forked from mirror/ledisdb
BMSetBit - offer a way to set multi bit during an api call; also fix offset parse bug.
This commit is contained in:
parent
2dbf643b16
commit
1a48d08d66
129
ledis/t_bin.go
129
ledis/t_bin.go
|
@ -4,6 +4,7 @@ import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"github.com/siddontang/ledisdb/leveldb"
|
"github.com/siddontang/ledisdb/leveldb"
|
||||||
|
"sort"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -19,6 +20,14 @@ type BitPair struct {
|
||||||
Val uint8
|
Val uint8
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type segBitInfo struct {
|
||||||
|
Seq uint32
|
||||||
|
Off uint32
|
||||||
|
Val uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
type segBitInfoArray []segBitInfo
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// byte
|
// byte
|
||||||
segByteWidth uint32 = 9
|
segByteWidth uint32 = 9
|
||||||
|
@ -62,6 +71,7 @@ var fillSegment []byte = func() []byte {
|
||||||
|
|
||||||
var errBinKey = errors.New("invalid bin key")
|
var errBinKey = errors.New("invalid bin key")
|
||||||
var errOffset = errors.New("invalid offset")
|
var errOffset = errors.New("invalid offset")
|
||||||
|
var errDuplicatePos = errors.New("duplicate bit pos")
|
||||||
|
|
||||||
func getBit(sz []byte, offset uint32) uint8 {
|
func getBit(sz []byte, offset uint32) uint8 {
|
||||||
index := offset >> 3
|
index := offset >> 3
|
||||||
|
@ -90,6 +100,22 @@ func setBit(sz []byte, offset uint32, val uint8) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (datas *segBitInfoArray) Len() int {
|
||||||
|
return len(*datas)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (datas *segBitInfoArray) Less(i, j int) bool {
|
||||||
|
res := (*datas)[i].Seq < (*datas)[j].Seq
|
||||||
|
if !res && (*datas)[i].Seq == (*datas)[j].Seq {
|
||||||
|
res = (*datas)[i].Off < (*datas)[j].Off
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func (datas *segBitInfoArray) Swap(i, j int) {
|
||||||
|
(*datas)[i], (*datas)[j] = (*datas)[j], (*datas)[i]
|
||||||
|
}
|
||||||
|
|
||||||
func (db *DB) bEncodeMetaKey(key []byte) []byte {
|
func (db *DB) bEncodeMetaKey(key []byte) []byte {
|
||||||
mk := make([]byte, len(key)+2)
|
mk := make([]byte, len(key)+2)
|
||||||
mk[0] = db.index
|
mk[0] = db.index
|
||||||
|
@ -151,7 +177,7 @@ func (db *DB) bParseOffset(key []byte, offset int32) (seq uint32, off uint32, er
|
||||||
err = e
|
err = e
|
||||||
return
|
return
|
||||||
} else if tailSeq >= 0 {
|
} else if tailSeq >= 0 {
|
||||||
offset += int32(uint32(tailSeq)<<segBitWidth | uint32(tailOff))
|
offset += int32((uint32(tailSeq)<<segBitWidth | uint32(tailOff)) + 1)
|
||||||
if offset < 0 {
|
if offset < 0 {
|
||||||
err = errOffset
|
err = errOffset
|
||||||
return
|
return
|
||||||
|
@ -409,48 +435,105 @@ func (db *DB) BSetBit(key []byte, offset int32, val uint8) (ori uint8, err error
|
||||||
|
|
||||||
if segment != nil {
|
if segment != nil {
|
||||||
ori = getBit(segment, off)
|
ori = getBit(segment, off)
|
||||||
setBit(segment, off, val)
|
if setBit(segment, off, val) {
|
||||||
|
|
||||||
t := db.binTx
|
t := db.binTx
|
||||||
t.Lock()
|
t.Lock()
|
||||||
|
|
||||||
t.Put(bk, segment)
|
t.Put(bk, segment)
|
||||||
if _, _, e := db.bUpdateMeta(t, key, seq, off); e != nil {
|
if _, _, e := db.bUpdateMeta(t, key, seq, off); e != nil {
|
||||||
err = e
|
err = e
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = t.Commit()
|
err = t.Commit()
|
||||||
t.Unlock()
|
t.Unlock()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// func (db *DB) BMSetBit(key []byte, args ...BitPair) (err error) {
|
func (db *DB) BMSetBit(key []byte, args ...BitPair) (place int32, err error) {
|
||||||
// if err = checkKeySize(key); err != nil {
|
if err = checkKeySize(key); err != nil {
|
||||||
// return
|
return
|
||||||
// }
|
}
|
||||||
|
|
||||||
// // todo ... optimize by sort the args by 'pos', and merge it ...
|
// (ps : so as to aviod wasting the mem copy while calling db.Get() and batch.Put(),
|
||||||
|
// here we sequence the request pos in order, so that we can execute diff pos
|
||||||
|
// set on the same segment by justing calling db.Get() and batch.Put() one time
|
||||||
|
// either.)
|
||||||
|
|
||||||
// t := db.binTx
|
// sort the requesting data by set pos
|
||||||
// t.Lock()
|
var argCnt = len(args)
|
||||||
// defer t.Unlock()
|
var bInfos segBitInfoArray = make(segBitInfoArray, argCnt)
|
||||||
|
var seq, off uint32
|
||||||
|
|
||||||
// var bk, segment []byte
|
for i, info := range args {
|
||||||
// var seq, off uint32
|
if seq, off, err = db.bParseOffset(key, info.Pos); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// for _, bitInfo := range args {
|
bInfos[i].Seq = seq
|
||||||
// if seq, off, err = db.bParseOffset(key, bitInfo.Pos); err != nil {
|
bInfos[i].Off = off
|
||||||
// return
|
bInfos[i].Val = info.Val
|
||||||
// }
|
}
|
||||||
|
|
||||||
// if bk, segment, err = db.bAllocateSegment(key, seq); err != nil {
|
sort.Sort(&bInfos)
|
||||||
// return
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return t.Commit()
|
for i := 1; i < argCnt; i++ {
|
||||||
// }
|
if bInfos[i].Seq == bInfos[i-1].Seq && bInfos[i].Off == bInfos[i-1].Off {
|
||||||
|
return 0, errDuplicatePos
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// set data
|
||||||
|
t := db.binTx
|
||||||
|
t.Lock()
|
||||||
|
defer t.Unlock()
|
||||||
|
|
||||||
|
var bk, segment []byte
|
||||||
|
var lastSeq, maxSeq, maxOff uint32
|
||||||
|
|
||||||
|
for _, info := range bInfos {
|
||||||
|
if segment != nil && info.Seq != lastSeq {
|
||||||
|
t.Put(bk, segment)
|
||||||
|
segment = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if segment == nil {
|
||||||
|
if bk, segment, err = db.bAllocateSegment(key, info.Seq); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if segment == nil {
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
lastSeq = info.Seq
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if setBit(segment, info.Off, info.Val) {
|
||||||
|
maxSeq = info.Seq
|
||||||
|
maxOff = info.Off
|
||||||
|
place++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if segment != nil {
|
||||||
|
t.Put(bk, segment)
|
||||||
|
}
|
||||||
|
|
||||||
|
// finally, update meta
|
||||||
|
if place > 0 {
|
||||||
|
if _, _, err = db.bUpdateMeta(t, key, maxSeq, maxOff); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = t.Commit()
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func (db *DB) BGetBit(key []byte, offset int32) (uint8, error) {
|
func (db *DB) BGetBit(key []byte, offset int32) (uint8, error) {
|
||||||
if seq, off, err := db.bParseOffset(key, offset); err != nil {
|
if seq, off, err := db.bParseOffset(key, offset); err != nil {
|
||||||
|
|
|
@ -34,11 +34,12 @@ func newBytes(bitLen int32) []byte {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBinary(t *testing.T) {
|
func TestBinary(t *testing.T) {
|
||||||
// testSimple(t)
|
testSimple(t)
|
||||||
// testSimpleII(t)
|
testSimpleII(t)
|
||||||
// testOpAndOr(t)
|
testOpAndOr(t)
|
||||||
// testOpXor(t)
|
testOpXor(t)
|
||||||
testOpNot(t)
|
testOpNot(t)
|
||||||
|
testMSetBit(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testSimple(t *testing.T) {
|
func testSimple(t *testing.T) {
|
||||||
|
@ -326,3 +327,53 @@ func testOpNot(t *testing.T) {
|
||||||
t.Fatal(false)
|
t.Fatal(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testMSetBit(t *testing.T) {
|
||||||
|
db := getTestDB()
|
||||||
|
db.FlushAll()
|
||||||
|
|
||||||
|
key := []byte("test_mset")
|
||||||
|
|
||||||
|
var datas = make([]BitPair, 8)
|
||||||
|
|
||||||
|
// 1st
|
||||||
|
datas[0] = BitPair{1000, 1}
|
||||||
|
datas[1] = BitPair{11, 1}
|
||||||
|
datas[2] = BitPair{10, 1}
|
||||||
|
datas[3] = BitPair{2, 1}
|
||||||
|
datas[4] = BitPair{int32(segBitSize - 1), 1}
|
||||||
|
datas[5] = BitPair{int32(segBitSize), 1}
|
||||||
|
datas[6] = BitPair{int32(segBitSize + 1), 1}
|
||||||
|
datas[7] = BitPair{int32(segBitSize) + 10, 0}
|
||||||
|
|
||||||
|
db.BMSetBit(key, datas...)
|
||||||
|
|
||||||
|
if sum, _ := db.BCount(key, 0, -1); sum != 7 {
|
||||||
|
t.Error(sum)
|
||||||
|
}
|
||||||
|
|
||||||
|
if tail, _ := db.BTail(key); tail != int32(segBitSize+10) {
|
||||||
|
t.Error(tail)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2nd
|
||||||
|
datas = make([]BitPair, 5)
|
||||||
|
|
||||||
|
datas[0] = BitPair{1000, 0}
|
||||||
|
datas[1] = BitPair{int32(segBitSize + 1), 0}
|
||||||
|
datas[2] = BitPair{int32(segBitSize * 10), 1}
|
||||||
|
datas[3] = BitPair{10, 0}
|
||||||
|
datas[4] = BitPair{99, 0}
|
||||||
|
|
||||||
|
db.BMSetBit(key, datas...)
|
||||||
|
|
||||||
|
if sum, _ := db.BCount(key, 0, -1); sum != 7-3+1 {
|
||||||
|
t.Error(sum)
|
||||||
|
}
|
||||||
|
|
||||||
|
if tail, _ := db.BTail(key); tail != int32(segBitSize*10) {
|
||||||
|
t.Error(tail)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue