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. C语言中如何写一个简单可移植而又足够随机的随机数生成器

    在C语言中标准库中的随机数产生函数的返回可能不是最优的,因为有些随机数生成器的低位并不随机,而另一些返回随机数的函数实现上又太复杂鸟.所以rand()%N并不是一个好方法,牛人给出的建议是使用: ra ...

  2. 学习MQ(一) 感知

    声明:我的文字里出现的MQ,如没有特殊指明,就是指的IBM的websphere MQ 以前对MQ一无所知! MQ是IBM websphere的系列产品之一,是很好的一个中间件产品.其实我对这列产品并不 ...

  3. 没人看系列-----html随笔

    <!DOCTYPE> 目录 没人看系列-----html/css详解 前言 不多说这段时间写了好多好多前端的东西,以至于自己重新返回看了一遍前端的所有技术.故此做个总结,准备学东西的请绕行 ...

  4. 基于JS的WEB会议室预订拖拽式图形界面的实现

    06年的一篇blog,转到这个博客上: 很早之前写的,后来由于这个功能模块取消,最终没有上线,所以与Server交互的那部分还没有写,不过那部分方案我也已经出来了,而且现在客户端这一部分已经通过了比较 ...

  5. Roundcube Webmail信息泄露漏洞(CVE-2015-5383)

    Preface Software: https://roundcube.net/Versions: 1.1.x<1.1.2(亲测1.1.5也有效)CVE: CVE-2015-5383Author ...

  6. DDGScreenShot—截取图片的任意部分

    写在前面 DDGScreenShot 库提供了截取任意图片的功能, 支持手势截图,当然,输入任意的区域也可以,下面看看具体的代码 代码如下: 方法封装 /** ** 用手势截图(截取图片的任意部分) ...

  7. require.js配置路径的用法和css的引入

    前端开发在近一两年发展的非常快,JavaScript作为主流的开发语言得到了前所未有的热捧.大量的前端框架出现了,这些框架都在尝试着解决一 些前端开发中的共性问题,但是实现又不尽相同.通常一般的前端加 ...

  8. UE4笔记:利用Widget设计一个切换材质功能

    UE4引擎中的Widget蓝图是一个重要的工具,可用于场景中的页面叠加,镜头绑定,场景切换等多处地方,在这里笔者介绍一种利用控件蓝图和场景中物体进行信息交互的方法,直观的体现就是进行物体的材质切换. ...

  9. zlib 压缩输出缓冲区 overflow 问题

    [TOC] 问题 后台服务传包太大时,我们框架可以使用 zlib 库对响应进行压缩:在这次服务调试过程中,使用 zlib compress2 以 Z_BEST_COMPRESSION 模式进行压缩时, ...

  10. Tomcat和JavaWeb目录和流程

    Tomcat主要目录结构 bin 二进制可执行文件,包含启动和关闭tomcat文件  conf 配置文件,其中包含了server.xml.context.xml.web.xml等  webapps 存 ...