package volume

import (
    "github.com/syndtr/goleveldb/leveldb"
    "sync"
    "encoding/binary"
    "github.com/syndtr/goleveldb/leveldb/util"
    "errors"
    "path/filepath"
    "strconv"
    "fmt"
)

const (
    ReversedsizeOffsetPrefix = '\x11' //key= "\x01"+Reversesize(8 byte)+offset(8 byte) value=[]
    OffsetSizePrefix = '\x22' //key= "\x02"+offset(8 byte)+size(8 byte) value=[]
)

/*
status主要存储空闲块的offset size
 */
type Status struct {
    path       string
    db         *leveldb.DB
    spaceMutex sync.Mutex
}
//创建空闲文件  元数据
func NewStatus(dir string, vid uint64) (status *Status, err error) {
    path := filepath.Join(dir, strconv.FormatUint(vid, 10) + ".status")
    status = new(Status)
    status.path = path
    status.db, err = leveldb.OpenFile(path, nil)
    return status, err
}
//分配空间
func (s *Status)newSpace(size uint64) (offset uint64, err error) {
    s.spaceMutex.Lock()
    defer s.spaceMutex.Unlock()

    //这里根据size倒序存储,使最大的空间最先被获取
    iter := s.db.NewIterator(util.BytesPrefix([]byte{ReversedsizeOffsetPrefix}), nil)
    defer iter.Release()
    iter.Next()
    key := iter.Key()
    if len(key) == 0 {
        return 0, errors.New("can't get free space")
    }

    freeSize := binary.BigEndian.Uint64(key[1:9]) ^ (^uint64(0))
    if freeSize < size {
        return 0, errors.New("can't get free space")
    }
    offset = binary.BigEndian.Uint64(key[9:])

    transaction, err := s.db.OpenTransaction()
    if err != nil {
        return 0, err
    }

    transaction.Delete(key, nil)

    key = getOffsetSizeKey(offset, freeSize)
    transaction.Delete(key, nil)

    //如果空闲块大小大于所请求大小,则将剩下空闲块,重新记录
    if freeSize > size {
        key = getReversedsizeOffset(offset + size, freeSize - size)
        transaction.Put(key, nil, nil)

        key = getOffsetSizeKey(offset + size, freeSize - size)
        transaction.Put(key, nil, nil)
    }

    err = transaction.Commit()
    return offset, err
}
//  释放文件空间
func (s *Status)freeSpace(offset uint64, size uint64) error {
    s.spaceMutex.Lock()
    defer s.spaceMutex.Unlock()
       //这里根据size倒序存储,使最大的空间最先被获取
    iter := s.db.NewIterator(util.BytesPrefix([]byte{OffsetSizePrefix}), nil)
    defer iter.Release()
    key := getOffsetSizeKey(offset, 0)
    iter.Seek(key)
    transaction, err := s.db.OpenTransaction()
    if err != nil {
        return err
    }
    //如果与下一块空闲块相邻,则合成一块空闲块
    key = iter.Key()
    if len(key) != 0 {
        nOffset := binary.BigEndian.Uint64(key[1:9])
        nSize := binary.BigEndian.Uint64(key[9:])
        if nOffset < offset + size {
            panic(fmt.Errorf("nOffset: %d < offset: %d + size: %d", nOffset, offset, size))
        } else if nOffset == offset + size {
            transaction.Delete(key, nil)
            size += nSize
            key = getReversedsizeOffset(nOffset, nSize)
            transaction.Delete(key, nil)
        }
    }
    //如果与上一块空闲块相邻,则合成一块空闲块
    iter.Prev()
    key = iter.Key()
    if len(key) != 0 {
        pOffset := binary.BigEndian.Uint64(key[1:9])
        pSize := binary.BigEndian.Uint64(key[9:])
        if pOffset + pSize > offset {
            panic(fmt.Errorf("pOffset: %d + pSize: %d > offset: %d", pOffset, pSize, offset))
        } else if pOffset + pSize == offset {
            transaction.Delete(key, nil)
            offset = pOffset
            size += pSize
            key = getReversedsizeOffset(pOffset, pSize)
            transaction.Delete(key, nil)
        }
    }
    key = getOffsetSizeKey(offset, size)
    transaction.Put(key, nil, nil)
    key = getReversedsizeOffset(offset, size)
    transaction.Put(key, nil, nil)
    return transaction.Commit()
}
//
func (s *Status)getMaxFreeSpace() uint64 {
    iter := s.db.NewIterator(util.BytesPrefix([]byte{ReversedsizeOffsetPrefix}), nil)
    defer iter.Release()
    iter.Next()
    key := iter.Key()
    if len(key) == 0 {
        return 0
    }
    freeSize := binary.BigEndian.Uint64(key[1:9]) ^ (^uint64(0))
    return freeSize
}
//获取偏移量 文件key
func getOffsetSizeKey(offset, size uint64) []byte {
    key := make([]byte, 1 + 16)
    key[0] = OffsetSizePrefix
    binary.BigEndian.PutUint64(key[1:9], offset)
    binary.BigEndian.PutUint64(key[9:], size)
    return key
}
//
func getReversedsizeOffset(offset, size uint64) []byte {
    key := make([]byte, 1 + 16)
    key[0] = ReversedsizeOffsetPrefix
    binary.BigEndian.PutUint64(key[9:], offset)
    binary.BigEndian.PutUint64(key[1:9], size ^ (^uint64(0)))
    return key
}

status.go的更多相关文章

  1. http status code

    属于转载 http status code:200:成功,服务器已成功处理了请求,通常这表示服务器提供了请求的网页 404:未找到,服务器未找到 201-206都表示服务器成功处理了请求的状态代码,说 ...

  2. ORACLE中STATUS为INACTIVE但是SERVER为SHARED状态的会话浅析

    我们知道当ORACLE数据库启用共享服务器模式时,通过共享服务器模式连接到数据库的会话是有一些特征的.在v$session里面,其SERVER的状态一般为SHARED和NONE, 为SHARED时,表 ...

  3. 重定向Http status code 303 和 302

    http 302 http 303 Http 302 302是一个普通的重定向代码.直观的看来是,请求者(浏览器或者模拟http请求)发起一个请求,然后服务端重定向到另一个地址.而事实上,服务端仅仅是 ...

  4. C#、JAVA操作Hadoop(HDFS、Map/Reduce)真实过程概述。组件、源码下载。无法解决:Response status code does not indicate success: 500。

    一.Hadoop环境配置概述 三台虚拟机,操作系统为:Ubuntu 16.04. Hadoop版本:2.7.2 NameNode:192.168.72.132 DataNode:192.168.72. ...

  5. 关于XHR对象中status范围的记录

    if(xhr.status >= 200 && xhr.status < 300 || xhr.status == 304){ // 成功执行区域 // 2XX表示有效响应 ...

  6. Job for httpd.service failed because the control process exited with error code. See "systemctl status httpd.service" and "journalctl -xe" for details

    thinkphp 在Apache上配置启用伪静态,重启Apache1 restart 竟然失败了,报错 Job for httpd.service failed because the control ...

  7. ajax response status list [转载]

    比较理想的解释方法应该以"状态:任务(目标)+过程+表现(或特征)"的表达模式来对这几个状态进行定义  [全文]  在<Pragmatic Ajax A Web 2.0 Pr ...

  8. Zabbix监控redis status

    概述 zabbix采用Trapper方式监控redis status 原理 redis-cli info命令得到redis服务器的统计信息,脚本对信息分两部分处理: (1)# Keyspace部分为Z ...

  9. Zabbix监控nginx-rtmp status(json版)

    与前面的文章 zabbix监控nginx-rtmp status(html版)区别只在于取值的页面不一样 http://127.0.0.1:81/control/get/all_streams sta ...

  10. 查看Mysql版本号 (最简单的是status )

    一.使用命令行模式进入mysql会看到最开始的提示符;二.命令行中使用status可以看到;三.使用系统函数等等,   查看版本信息 #1使用命令行模式进入mysql会看到最开始的提示符 Your M ...

随机推荐

  1. obj-c编程10:Foundation库中类的使用(1)[数字,字符串]

    我们知道在mac或iphone上编程最终逃不开os x平台,你无法在windows或linux上开发纯正的apple程序.(so不要舍不得银子买mac啦)虽说linux和windows上有移植的obj ...

  2. 工作中常用Git指令操作

    常用Git指令总结 前阵子有几天好不顺,可谓是喝水都呛着,更何况被Git给呛着了,还不轻,哈哈.所以打算总结一下自己在工作使用到Git相关的东西以及和大家探讨使用GIt的心得体会.于是,关于Git的的 ...

  3. box-sizing属性(指定针对元素的宽度与高度的计算方法)

    在css中,使用width属性与height属性来指定元素的宽度与高度.使用box-sizing属性,可以指定用width属性与height属性分别指定的宽度值与高度值是否包含元素的内部补白区域与边框 ...

  4. miniui几个常用知识点汇总

    1.在表格中去除系统自带的序列号,请看代码: function allAndBrief(id) { if(id==1){ grid.set({ columns: [ { type: "ind ...

  5. 学习MQ(二)基本概念

    学习MQ(二)基本概念 这次简单罗列一下MQ的基本概念,还有我对它们的理解 1.queue manager 队列管理器,这是MQ系统中最上层的一个概念.每一个queue manager都有一个侦听器, ...

  6. java并发容器(Map、List、BlockingQueue)

    转发: 大海巨浪 Java库本身就有多种线程安全的容器和同步工具,其中同步容器包括两部分:一个是Vector和Hashtable.另外还有JDK1.2中加入的同步包装类,这些类都是由Collectio ...

  7. 面向对象,更适合JavaScript

    面向对象程序设计是软件开发中一个很庞大很复杂的话题,它并不是仅仅学会类.继承.封装.多态这些面向对象编程语法元素就表示掌握的,这些语法元素只是实现面向对象程序的工具, 就像砖块.水泥能搭建小屋,也能造 ...

  8. Kali Linux 工具使用中文说明书

    From: https://www.hackfun.org/kali-tools/kali-tools-zh.html 英文版地址:http://tools.kali.org/ 信息收集 accche ...

  9. Nginx SSL功能支持的一些资料。

    http://wiki.nginx.org/HttpSslModulehttp://zou.lu/nginx-https-ssl-module/http://www.21andy.com/blog/2 ...

  10. php coding中的一些小问题

    最近在SAE上写微博应用,碰到一些小问题,记下来,以供参考: 1.出错提示: Fatal error: Can't use function return value in write context ...