mirror of https://github.com/ledisdb/ledisdb.git
offer function 'xor' 'not' in bit operation
This commit is contained in:
parent
cd194dff48
commit
70018092fe
119
ledis/t_bin.go
119
ledis/t_bin.go
|
@ -42,6 +42,10 @@ var bitsInByte = [256]int32{0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3,
|
||||||
6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5,
|
6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5,
|
||||||
6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8}
|
6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8}
|
||||||
|
|
||||||
|
var fillBits = [...]uint8{1, 3, 7, 15, 31, 63, 127, 255}
|
||||||
|
|
||||||
|
var emptySegment []byte = make([]byte, segByteSize, segByteSize)
|
||||||
|
|
||||||
var fillSegment []byte = func() []byte {
|
var fillSegment []byte = func() []byte {
|
||||||
data := make([]byte, segByteSize, segByteSize)
|
data := make([]byte, segByteSize, segByteSize)
|
||||||
for i := uint32(0); i < segByteSize; i++ {
|
for i := uint32(0); i < segByteSize; i++ {
|
||||||
|
@ -216,7 +220,7 @@ func (db *DB) bDelete(t *tx, key []byte) (drop int64) {
|
||||||
it := db.db.RangeIterator(minKey, maxKey, leveldb.RangeClose)
|
it := db.db.RangeIterator(minKey, maxKey, leveldb.RangeClose)
|
||||||
for ; it.Valid(); it.Next() {
|
for ; it.Valid(); it.Next() {
|
||||||
t.Delete(it.Key())
|
t.Delete(it.Key())
|
||||||
drop = 1
|
drop++
|
||||||
}
|
}
|
||||||
it.Close()
|
it.Close()
|
||||||
|
|
||||||
|
@ -285,7 +289,7 @@ func (db *DB) getSegment(key []byte, seq uint32) ([]byte, []byte, error) {
|
||||||
func (db *DB) allocateSegment(key []byte, seq uint32) ([]byte, []byte, error) {
|
func (db *DB) allocateSegment(key []byte, seq uint32) ([]byte, []byte, error) {
|
||||||
bk, segment, err := db.getSegment(key, seq)
|
bk, segment, err := db.getSegment(key, seq)
|
||||||
if err == nil && segment == nil {
|
if err == nil && segment == nil {
|
||||||
segment = make([]byte, segByteSize, segByteSize) // can be optimize ?
|
segment = make([]byte, segByteSize, segByteSize)
|
||||||
}
|
}
|
||||||
return bk, segment, err
|
return bk, segment, err
|
||||||
}
|
}
|
||||||
|
@ -389,6 +393,12 @@ func (db *DB) BTail(key []byte) (int32, error) {
|
||||||
return tail, nil
|
return tail, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (db *DB) bIterator(key []byte) *leveldb.RangeLimitIterator {
|
||||||
|
sk := db.bEncodeBinKey(key, minSeq)
|
||||||
|
ek := db.bEncodeBinKey(key, maxSeq)
|
||||||
|
return db.db.RangeIterator(sk, ek, leveldb.RangeClose)
|
||||||
|
}
|
||||||
|
|
||||||
func (db *DB) bSegAnd(a []byte, b []byte, res *[]byte) {
|
func (db *DB) bSegAnd(a []byte, b []byte, res *[]byte) {
|
||||||
if a == nil || b == nil {
|
if a == nil || b == nil {
|
||||||
*res = nil
|
*res = nil
|
||||||
|
@ -431,31 +441,60 @@ func (db *DB) bSegOr(a []byte, b []byte, res *[]byte) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DB) bIterator(key []byte) *leveldb.RangeLimitIterator {
|
func (db *DB) bSegXor(a []byte, b []byte, res *[]byte) {
|
||||||
sk := db.bEncodeBinKey(key, minSeq)
|
if a == nil && b == nil {
|
||||||
ek := db.bEncodeBinKey(key, maxSeq)
|
*res = fillSegment
|
||||||
return db.db.RangeIterator(sk, ek, leveldb.RangeClose)
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if a == nil {
|
||||||
|
a = emptySegment
|
||||||
|
}
|
||||||
|
|
||||||
|
if b == nil {
|
||||||
|
b = emptySegment
|
||||||
|
}
|
||||||
|
|
||||||
|
data := *res
|
||||||
|
if data == nil {
|
||||||
|
data = make([]byte, segByteSize, segByteSize)
|
||||||
|
*res = data
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := uint32(0); i < segByteSize; i++ {
|
||||||
|
data[i] = a[i] ^ b[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DB) BOperation(op uint8, dstkey []byte, srckeys ...[]byte) (blen int32, err error) {
|
func (db *DB) BOperation(op uint8, dstkey []byte, srckeys ...[]byte) (blen int32, err error) {
|
||||||
// return :
|
// return :
|
||||||
// The size of the string stored in the destination key,
|
// The size of the string stored in the destination key,
|
||||||
// that is equal to the size of the longest input string.
|
// that is equal to the size of the longest input string.
|
||||||
|
blen = -1
|
||||||
|
|
||||||
var exeOp func([]byte, []byte, *[]byte)
|
var exeOp func([]byte, []byte, *[]byte)
|
||||||
switch op {
|
switch op {
|
||||||
case OPand:
|
case OPand:
|
||||||
exeOp = db.bSegAnd
|
exeOp = db.bSegAnd
|
||||||
case OPor:
|
case OPor:
|
||||||
exeOp = db.bSegOr
|
exeOp = db.bSegOr
|
||||||
|
case OPxor, OPnot:
|
||||||
|
exeOp = db.bSegXor
|
||||||
default:
|
default:
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if dstkey == nil || srckeys == nil || len(srckeys) == 0 {
|
if dstkey == nil || srckeys == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
blen = -1
|
if (op == OPnot && len(srckeys) != 1) ||
|
||||||
|
(op != OPnot && len(srckeys) < 2) {
|
||||||
|
// msg("lack of arguments")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
t := db.binTx
|
t := db.binTx
|
||||||
t.Lock()
|
t.Lock()
|
||||||
|
@ -466,9 +505,9 @@ func (db *DB) BOperation(op uint8, dstkey []byte, srckeys ...[]byte) (blen int32
|
||||||
var nowSeq, nowOff int32
|
var nowSeq, nowOff int32
|
||||||
|
|
||||||
var keyNum int = len(srckeys)
|
var keyNum int = len(srckeys)
|
||||||
var iniIdx int = 0
|
var srcIdx int = 0
|
||||||
for ; iniIdx < keyNum; iniIdx++ {
|
for ; srcIdx < keyNum; srcIdx++ {
|
||||||
if nowSeq, nowOff, err = db.bGetMeta(srckeys[iniIdx]); err != nil { // todo : if key not exists ....
|
if nowSeq, nowOff, err = db.bGetMeta(srckeys[srcIdx]); err != nil { // todo : if key not exists ....
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -479,8 +518,8 @@ func (db *DB) BOperation(op uint8, dstkey []byte, srckeys ...[]byte) (blen int32
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if iniIdx+1 >= keyNum { // todo ... while operation is 'not'
|
if srcIdx == keyNum {
|
||||||
// msg("lack of arguments")
|
// none existing src key
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -488,7 +527,22 @@ func (db *DB) BOperation(op uint8, dstkey []byte, srckeys ...[]byte) (blen int32
|
||||||
var seq, off uint32
|
var seq, off uint32
|
||||||
var segments = make([][]byte, maxSegCount) // todo : init count also can be optimize while 'and' / 'or'
|
var segments = make([][]byte, maxSegCount) // todo : init count also can be optimize while 'and' / 'or'
|
||||||
|
|
||||||
it := db.bIterator(srckeys[iniIdx])
|
if op == OPnot {
|
||||||
|
for i := uint32(0); i < maxDstSeq; i++ {
|
||||||
|
segments[i] = fillSegment
|
||||||
|
}
|
||||||
|
|
||||||
|
var tailSeg = make([]byte, segByteSize, segByteSize)
|
||||||
|
var fillByte = fillBits[7]
|
||||||
|
var cnt = db.bCapByteSize(uint32(0), maxDstOff)
|
||||||
|
for i := uint32(0); i < cnt-1; i++ {
|
||||||
|
tailSeg[i] = fillByte
|
||||||
|
}
|
||||||
|
tailSeg[cnt-1] = fillBits[maxDstOff-(cnt-1)<<3]
|
||||||
|
segments[maxDstSeq] = tailSeg
|
||||||
|
|
||||||
|
} else {
|
||||||
|
it := db.bIterator(srckeys[srcIdx])
|
||||||
for ; it.Valid(); it.Next() {
|
for ; it.Valid(); it.Next() {
|
||||||
if _, seq, err = db.bDecodeBinKey(it.Key()); err != nil {
|
if _, seq, err = db.bDecodeBinKey(it.Key()); err != nil {
|
||||||
// to do ...
|
// to do ...
|
||||||
|
@ -498,9 +552,13 @@ func (db *DB) BOperation(op uint8, dstkey []byte, srckeys ...[]byte) (blen int32
|
||||||
segments[seq] = it.Value()
|
segments[seq] = it.Value()
|
||||||
}
|
}
|
||||||
it.Close()
|
it.Close()
|
||||||
|
srcIdx++
|
||||||
|
}
|
||||||
|
|
||||||
// operation with following keys
|
// operation with following keys
|
||||||
for i := iniIdx + 1; i < keyNum; i++ {
|
var res []byte
|
||||||
|
|
||||||
|
for i := srcIdx; i < keyNum; i++ {
|
||||||
if nowSeq, nowOff, err = db.bGetMeta(srckeys[i]); err != nil {
|
if nowSeq, nowOff, err = db.bGetMeta(srckeys[i]); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -516,10 +574,8 @@ func (db *DB) BOperation(op uint8, dstkey []byte, srckeys ...[]byte) (blen int32
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
it = db.bIterator(srckeys[i])
|
it := db.bIterator(srckeys[i])
|
||||||
segIdx := uint32(0)
|
for idx, end := uint32(0), false; !end; it.Next() {
|
||||||
|
|
||||||
for end := false; !end; it.Next() {
|
|
||||||
end = !it.Valid()
|
end = !it.Valid()
|
||||||
if !end {
|
if !end {
|
||||||
if _, seq, err = db.bDecodeBinKey(it.Key()); err != nil {
|
if _, seq, err = db.bDecodeBinKey(it.Key()); err != nil {
|
||||||
|
@ -535,18 +591,17 @@ func (db *DB) BOperation(op uint8, dstkey []byte, srckeys ...[]byte) (blen int32
|
||||||
// operation 'and' can be optimize here :
|
// operation 'and' can be optimize here :
|
||||||
// if seq > max_segments_idx, this loop can be break,
|
// if seq > max_segments_idx, this loop can be break,
|
||||||
// which can avoid cost from Key() and decode key
|
// which can avoid cost from Key() and decode key
|
||||||
|
for ; idx < seq; idx++ {
|
||||||
if op == OPand || op == OPor {
|
res = nil
|
||||||
for ; segIdx < seq; segIdx++ {
|
exeOp(segments[idx], nil, &res)
|
||||||
if segments[segIdx] != nil {
|
segments[idx] = res
|
||||||
exeOp(segments[segIdx], nil, &segments[segIdx])
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
} // else {...}
|
|
||||||
|
|
||||||
if !end {
|
if !end {
|
||||||
exeOp(segments[seq], it.Value(), &segments[segIdx])
|
res = it.Value()
|
||||||
segIdx++
|
exeOp(segments[seq], res, &res)
|
||||||
|
segments[seq] = res
|
||||||
|
idx++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
it.Close()
|
it.Close()
|
||||||
|
@ -557,19 +612,15 @@ func (db *DB) BOperation(op uint8, dstkey []byte, srckeys ...[]byte) (blen int32
|
||||||
db.rmExpire(t, bExpType, dstkey)
|
db.rmExpire(t, bExpType, dstkey)
|
||||||
|
|
||||||
// set data and meta
|
// set data and meta
|
||||||
// if op == OPand || op == OPor {
|
|
||||||
// for i := maxDstSeq; i >= 0; i-- {
|
|
||||||
//
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
db.bSetMeta(t, dstkey, maxDstSeq, maxDstOff)
|
db.bSetMeta(t, dstkey, maxDstSeq, maxDstOff)
|
||||||
|
|
||||||
|
// set data
|
||||||
var bk []byte
|
var bk []byte
|
||||||
for seq, seg := range segments {
|
for seq, seg := range segments {
|
||||||
if seg != nil {
|
if seg != nil {
|
||||||
// todo:
|
// todo:
|
||||||
// here can be optimize, like 'updateBinKeySeq',
|
// here can be optimize, like 'updateBinKeySeq',
|
||||||
// avoid too many make mem
|
// avoid allocate too many mem
|
||||||
bk = db.bEncodeBinKey(dstkey, uint32(seq))
|
bk = db.bEncodeBinKey(dstkey, uint32(seq))
|
||||||
t.Put(bk, seg)
|
t.Put(bk, seg)
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,11 +6,18 @@ import (
|
||||||
|
|
||||||
func cmpBytes(a []byte, b []byte) bool {
|
func cmpBytes(a []byte, b []byte) bool {
|
||||||
if len(a) != len(b) {
|
if len(a) != len(b) {
|
||||||
|
println("len diff")
|
||||||
|
println(len(a))
|
||||||
|
println(len(b))
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, n := range a {
|
for i, n := range a {
|
||||||
if n != b[i] {
|
if n != b[i] {
|
||||||
|
println("diff !")
|
||||||
|
println(i)
|
||||||
|
println(n)
|
||||||
|
println(b[i])
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,9 +34,11 @@ func newBytes(bitLen int32) []byte {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBinary(t *testing.T) {
|
func TestBinary(t *testing.T) {
|
||||||
testSimple(t)
|
// testSimple(t)
|
||||||
testSimpleII(t)
|
// testSimpleII(t)
|
||||||
testOp(t)
|
// testOpAndOr(t)
|
||||||
|
// testOpXor(t)
|
||||||
|
testOpNot(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testSimple(t *testing.T) {
|
func testSimple(t *testing.T) {
|
||||||
|
@ -80,7 +89,7 @@ func testSimpleII(t *testing.T) {
|
||||||
db := getTestDB()
|
db := getTestDB()
|
||||||
db.FlushAll()
|
db.FlushAll()
|
||||||
|
|
||||||
key := []byte("test_bin")
|
key := []byte("test_bin_2")
|
||||||
|
|
||||||
pos := int32(1234567)
|
pos := int32(1234567)
|
||||||
if ori, _ := db.BSetBit(key, pos, 1); ori != 0 {
|
if ori, _ := db.BSetBit(key, pos, 1); ori != 0 {
|
||||||
|
@ -120,11 +129,11 @@ func testSimpleII(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func testOp(t *testing.T) {
|
func testOpAndOr(t *testing.T) {
|
||||||
db := getTestDB()
|
db := getTestDB()
|
||||||
db.FlushAll()
|
db.FlushAll()
|
||||||
|
|
||||||
dstKey := []byte("test_bin_op")
|
dstKey := []byte("test_bin_op_and_or")
|
||||||
|
|
||||||
k0 := []byte("op_0")
|
k0 := []byte("op_0")
|
||||||
k1 := []byte("op_01")
|
k1 := []byte("op_01")
|
||||||
|
@ -245,3 +254,75 @@ func testOp(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testOpXor(t *testing.T) {
|
||||||
|
db := getTestDB()
|
||||||
|
db.FlushAll()
|
||||||
|
|
||||||
|
dstKey := []byte("test_bin_op_xor")
|
||||||
|
|
||||||
|
k0 := []byte("op_2_00")
|
||||||
|
k1 := []byte("op_2_01")
|
||||||
|
srcKeys := [][]byte{k0, k1}
|
||||||
|
|
||||||
|
db.BSetBit(k0, int32(0), 1)
|
||||||
|
db.BSetBit(k0, int32(7), 1)
|
||||||
|
db.BSetBit(k0, int32(segBitSize-1), 1)
|
||||||
|
db.BSetBit(k0, int32(segBitSize-8), 1)
|
||||||
|
|
||||||
|
db.BSetBit(k1, int32(7), 1)
|
||||||
|
db.BSetBit(k1, int32(segBitSize-8), 1)
|
||||||
|
|
||||||
|
var stdData []byte
|
||||||
|
var data []byte
|
||||||
|
|
||||||
|
// op - xor
|
||||||
|
db.BOperation(OPxor, dstKey, srcKeys...)
|
||||||
|
|
||||||
|
stdData = make([]byte, segByteSize)
|
||||||
|
stdData[0] = uint8(0x01)
|
||||||
|
stdData[segByteSize-1] = uint8(0x80)
|
||||||
|
|
||||||
|
data, _ = db.BGet(dstKey)
|
||||||
|
if cmpBytes(data, stdData) {
|
||||||
|
t.Fatal(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testOpNot(t *testing.T) {
|
||||||
|
db := getTestDB()
|
||||||
|
db.FlushAll()
|
||||||
|
|
||||||
|
// intputs
|
||||||
|
dstKey := []byte("test_bin_op_xor")
|
||||||
|
|
||||||
|
k0 := []byte("op_not_0")
|
||||||
|
srcKeys := [][]byte{k0}
|
||||||
|
|
||||||
|
db.BSetBit(k0, int32(0), 1)
|
||||||
|
db.BSetBit(k0, int32(7), 1)
|
||||||
|
|
||||||
|
pos := segBitSize
|
||||||
|
for i := uint32(8); i >= 1; i -= 2 {
|
||||||
|
db.BSetBit(k0, int32(pos-i), 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
db.BSetBit(k0, int32(3*segBitSize-10), 1)
|
||||||
|
|
||||||
|
// std
|
||||||
|
stdData := make([]byte, segByteSize*3-1)
|
||||||
|
for i, _ := range stdData {
|
||||||
|
stdData[i] = 255
|
||||||
|
}
|
||||||
|
stdData[0] = uint8(0x7e)
|
||||||
|
stdData[segByteSize-1] = uint8(0xaa)
|
||||||
|
stdData[segByteSize*3-2] = uint8(0x3f)
|
||||||
|
|
||||||
|
// op - not
|
||||||
|
db.BOperation(OPnot, dstKey, srcKeys...)
|
||||||
|
|
||||||
|
data, _ := db.BGet(dstKey)
|
||||||
|
if cmpBytes(data, stdData) {
|
||||||
|
t.Fatal(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue