diff --git a/Makefile b/Makefile index 2027cf2..d3b1941 100644 --- a/Makefile +++ b/Makefile @@ -33,3 +33,6 @@ test_store: test_rpl: $(GO) test --race -tags '$(GO_BUILD_TAGS)' ./rpl + +fmt: + go fmt ./... \ No newline at end of file diff --git a/cmd/ledis-cli/const.go b/cmd/ledis-cli/const.go index 54554c7..a3e0674 100644 --- a/cmd/ledis-cli/const.go +++ b/cmd/ledis-cli/const.go @@ -1,7 +1,8 @@ -//This file was generated by .tools/generate_commands.py on Tue Feb 03 2015 14:21:53 +0800 +//This file was generated by .tools/generate_commands.py on Fri Feb 06 2015 09:15:18 +0800 package main var helpCommands = [][]string{ + {"APPEND", "key value", "KV"}, {"BCOUNT", "key [start end]", "Bitmap"}, {"BDELETE", "key", "ZSet"}, {"BEGIN", "-", "Transaction"}, @@ -9,6 +10,9 @@ var helpCommands = [][]string{ {"BEXPIREAT", "key timestamp", "Bitmap"}, {"BGET", "key", "Bitmap"}, {"BGETBIT", "key offset", "Bitmap"}, + {"BITCOUNT", "key [start] [end]", "KV"}, + {"BITOP", "operation destkey key [key ...]", "KV"}, + {"BITPOS", "key bit [start] [end]", "KV"}, {"BLPOP", "key [key ...] timeout", "List"}, {"BMSETBIT", "key offset value [offset value ...]", "Bitmap"}, {"BOPT", "operation destkey key [key ...]", "Bitmap"}, @@ -34,6 +38,8 @@ var helpCommands = [][]string{ {"FLUSHDB", "-", "Server"}, {"FULLSYNC", "[NEW]", "Replication"}, {"GET", "key", "KV"}, + {"GETBIT", "key offset", "KV"}, + {"GETRANGE", "key start end", "KV"}, {"GETSET", " key value", "KV"}, {"HCLEAR", "key", "Hash"}, {"HDEL", "key field [field ...]", "Hash"}, @@ -92,8 +98,10 @@ var helpCommands = [][]string{ {"SDUMP", "key", "Set"}, {"SELECT", "index", "Server"}, {"SET", "key value", "KV"}, + {"SETBIT", "key offset value", "KV"}, {"SETEX", "key seconds value", "KV"}, {"SETNX", "key value", "KV"}, + {"SETRANGE", "key offset value", "KV"}, {"SEXPIRE", "key seconds", "Set"}, {"SEXPIREAT", "key timestamp", "Set"}, {"SINTER", "key [key ...]", "Set"}, @@ -104,6 +112,7 @@ var helpCommands = [][]string{ {"SMEMBERS", "key", "Set"}, {"SPERSIST", "key", "Set"}, {"SREM", "key member [member ...]", "Set"}, + {"STRLEN", "key", "KV"}, {"STTL", "key", "Set"}, {"SUNION", "key [key ...]", "Set"}, {"SUNIONSTORE", "destination key [key ...]", "Set"}, diff --git a/doc/commands.json b/doc/commands.json index abbd4f1..4b549db 100644 --- a/doc/commands.json +++ b/doc/commands.json @@ -794,5 +794,59 @@ "arguments" : "-", "group" : "Server", "readonly" : true + }, + + "APPEND": { + "arguments" : "key value", + "group" : "KV", + "readonly" : false + }, + + "GETRANGE": { + "arguments" : "key start end", + "group" : "KV", + "readonly" : true + }, + + "SETRANGE": { + "arguments" : "key offset value", + "group" : "KV", + "readonly" : false + }, + + "STRLEN": { + "arguments" : "key", + "group" : "KV", + "readonly" : true + }, + + "BITCOUNT": { + "arguments" : "key [start] [end]", + "group" : "KV", + "readonly" : true + }, + + "BITOP": { + "arguments" : "operation destkey key [key ...]", + "group" : "KV", + "readonly" : false + }, + + "BITPOS": { + "arguments" : "key bit [start] [end]", + "group" : "KV", + "readonly" : true + }, + + "GETBIT": { + "arguments" : "key offset", + "group" : "KV", + "readonly" : true + }, + + "SETBIT": { + "arguments" : "key offset value", + "group" : "KV", + "readonly" : false } } diff --git a/ledis/const.go b/ledis/const.go index a61cb90..5d4b587 100644 --- a/ledis/const.go +++ b/ledis/const.go @@ -98,3 +98,10 @@ const ( DBInTransaction uint8 = 0x1 DBInMulti uint8 = 0x2 ) + +const ( + BitAND = "and" + BitOR = "or" + BitXOR = "xor" + BitNot = "not" +) diff --git a/ledis/scan_test.go b/ledis/scan_test.go index 7a60da6..b2a0970 100644 --- a/ledis/scan_test.go +++ b/ledis/scan_test.go @@ -250,52 +250,52 @@ func TestDBLScan(t *testing.T) { } func TestDBBScan(t *testing.T) { - db := getTestDB() + // db := getTestDB() - db.bFlush() + // db.bFlush() - k1 := []byte("k1") - if _, err := db.BSetBit(k1, 1, 1); err != nil { - t.Fatal(err.Error()) - } + // k1 := []byte("k1") + // if _, err := db.BSetBit(k1, 1, 1); err != nil { + // t.Fatal(err.Error()) + // } - k2 := []byte("k2") - if _, err := db.BSetBit(k2, 1, 1); err != nil { - t.Fatal(err.Error()) - } - k3 := []byte("k3") + // k2 := []byte("k2") + // if _, err := db.BSetBit(k2, 1, 1); err != nil { + // t.Fatal(err.Error()) + // } + // k3 := []byte("k3") - if _, err := db.BSetBit(k3, 1, 0); err != nil { - t.Fatal(err.Error()) - } + // if _, err := db.BSetBit(k3, 1, 0); err != nil { + // t.Fatal(err.Error()) + // } - if v, err := db.BScan(nil, 1, true, ""); err != nil { - t.Fatal(err) - } else if len(v) != 1 { - t.Fatal("invalid length ", len(v)) - } else if string(v[0]) != "k1" { - t.Fatal("invalid value ", string(v[0])) - } + // if v, err := db.BScan(nil, 1, true, ""); err != nil { + // t.Fatal(err) + // } else if len(v) != 1 { + // t.Fatal("invalid length ", len(v)) + // } else if string(v[0]) != "k1" { + // t.Fatal("invalid value ", string(v[0])) + // } - if v, err := db.BScan(k1, 2, true, ""); err != nil { - t.Fatal(err) - } else if len(v) != 2 { - t.Fatal("invalid length ", len(v)) - } else if string(v[0]) != "k1" { - t.Fatal("invalid value ", string(v[0])) - } else if string(v[1]) != "k2" { - t.Fatal("invalid value ", string(v[1])) - } + // if v, err := db.BScan(k1, 2, true, ""); err != nil { + // t.Fatal(err) + // } else if len(v) != 2 { + // t.Fatal("invalid length ", len(v)) + // } else if string(v[0]) != "k1" { + // t.Fatal("invalid value ", string(v[0])) + // } else if string(v[1]) != "k2" { + // t.Fatal("invalid value ", string(v[1])) + // } - if v, err := db.BScan(k1, 2, false, ""); err != nil { - t.Fatal(err) - } else if len(v) != 2 { - t.Fatal("invalid length ", len(v)) - } else if string(v[0]) != "k2" { - t.Fatal("invalid value ", string(v[0])) - } else if string(v[1]) != "k3" { - t.Fatal("invalid value ", string(v[1])) - } + // if v, err := db.BScan(k1, 2, false, ""); err != nil { + // t.Fatal(err) + // } else if len(v) != 2 { + // t.Fatal("invalid length ", len(v)) + // } else if string(v[0]) != "k2" { + // t.Fatal("invalid value ", string(v[0])) + // } else if string(v[1]) != "k3" { + // t.Fatal("invalid value ", string(v[1])) + // } } diff --git a/ledis/t_bit.go b/ledis/t_bit.go index 501ea1b..cff51d6 100644 --- a/ledis/t_bit.go +++ b/ledis/t_bit.go @@ -3,6 +3,7 @@ package ledis import ( "encoding/binary" "errors" + "github.com/siddontang/go/log" "github.com/siddontang/go/num" "github.com/siddontang/ledisdb/store" "sort" @@ -50,19 +51,6 @@ const ( maxSeq uint32 = uint32((maxByteSize << 3) - 1) ) -var bitsInByte = [256]int32{0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, - 4, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 1, 2, 2, 3, 2, 3, - 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, - 5, 5, 6, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, - 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, - 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 1, 2, - 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, - 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, - 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 2, 3, 3, 4, 3, 4, 4, - 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, - 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} - var fillBits = [...]uint8{1, 3, 7, 15, 31, 63, 127, 255} var emptySegment []byte = make([]byte, segByteSize, segByteSize) @@ -442,6 +430,8 @@ func (db *DB) bCountSeg(key []byte, seq uint32, soff uint32, eoff uint32) (cnt i } func (db *DB) BGet(key []byte) (data []byte, err error) { + log.Error("bitmap type will be deprecated later, please use bit operations in kv type") + if err = checkKeySize(key); err != nil { return } @@ -476,6 +466,8 @@ func (db *DB) BGet(key []byte) (data []byte, err error) { } func (db *DB) BDelete(key []byte) (drop int64, err error) { + log.Error("bitmap type will be deprecated later, please use bit operations in kv type") + if err = checkKeySize(key); err != nil { return } @@ -492,6 +484,8 @@ func (db *DB) BDelete(key []byte) (drop int64, err error) { } func (db *DB) BSetBit(key []byte, offset int32, val uint8) (ori uint8, err error) { + log.Error("bitmap type will be deprecated later, please use bit operations in kv type") + if err = checkKeySize(key); err != nil { return } @@ -528,6 +522,8 @@ func (db *DB) BSetBit(key []byte, offset int32, val uint8) (ori uint8, err error } func (db *DB) BMSetBit(key []byte, args ...BitPair) (place int64, err error) { + log.Error("bitmap type will be deprecated later, please use bit operations in kv type") + if err = checkKeySize(key); err != nil { return } @@ -608,6 +604,8 @@ func (db *DB) BMSetBit(key []byte, args ...BitPair) (place int64, err error) { } func (db *DB) BGetBit(key []byte, offset int32) (uint8, error) { + log.Error("bitmap type will be deprecated later, please use bit operations in kv type") + if seq, off, err := db.bParseOffset(key, offset); err != nil { return 0, err } else { @@ -631,6 +629,8 @@ func (db *DB) BGetBit(key []byte, offset int32) (uint8, error) { // } func (db *DB) BCount(key []byte, start int32, end int32) (cnt int32, err error) { + log.Error("bitmap type will be deprecated later, please use bit operations in kv type") + var sseq, soff uint32 if sseq, soff, err = db.bParseOffset(key, start); err != nil { return @@ -686,6 +686,8 @@ func (db *DB) BCount(key []byte, start int32, end int32) (cnt int32, err error) } func (db *DB) BTail(key []byte) (int32, error) { + log.Error("bitmap type will be deprecated later, please use bit operations in kv type") + // effective length of data, the highest bit-pos set in history tailSeq, tailOff, err := db.bGetMeta(key) if err != nil { @@ -701,6 +703,8 @@ func (db *DB) BTail(key []byte) (int32, error) { } func (db *DB) BOperation(op uint8, dstkey []byte, srckeys ...[]byte) (blen int32, err error) { + log.Error("bitmap type will be deprecated later, please use bit operations in kv type") + // blen - // the total bit size of data stored in destination key, // that is equal to the size of the longest input string. @@ -865,6 +869,7 @@ func (db *DB) BOperation(op uint8, dstkey []byte, srckeys ...[]byte) (blen int32 } func (db *DB) BExpire(key []byte, duration int64) (int64, error) { + log.Error("bitmap type will be deprecated later, please use bit operations in kv type") if duration <= 0 { return 0, errExpireValue } @@ -877,6 +882,7 @@ func (db *DB) BExpire(key []byte, duration int64) (int64, error) { } func (db *DB) BExpireAt(key []byte, when int64) (int64, error) { + log.Error("bitmap type will be deprecated later, please use bit operations in kv type") if when <= time.Now().Unix() { return 0, errExpireValue } @@ -889,6 +895,7 @@ func (db *DB) BExpireAt(key []byte, when int64) (int64, error) { } func (db *DB) BTTL(key []byte) (int64, error) { + log.Error("bitmap type will be deprecated later, please use bit operations in kv type") if err := checkKeySize(key); err != nil { return -1, err } @@ -897,6 +904,7 @@ func (db *DB) BTTL(key []byte) (int64, error) { } func (db *DB) BPersist(key []byte) (int64, error) { + log.Error("bitmap type will be deprecated later, please use bit operations in kv type") if err := checkKeySize(key); err != nil { return 0, err } @@ -915,10 +923,12 @@ func (db *DB) BPersist(key []byte) (int64, error) { } func (db *DB) BScan(key []byte, count int, inclusive bool, match string) ([][]byte, error) { + log.Error("bitmap type will be deprecated later, please use bit operations in kv type") return db.scan(BitMetaType, key, count, inclusive, match) } func (db *DB) BRevScan(key []byte, count int, inclusive bool, match string) ([][]byte, error) { + log.Error("bitmap type will be deprecated later, please use bit operations in kv type") return db.revscan(BitMetaType, key, count, inclusive, match) } diff --git a/ledis/t_bit_test.go b/ledis/t_bit_test.go index c62b07a..5822fed 100644 --- a/ledis/t_bit_test.go +++ b/ledis/t_bit_test.go @@ -1,593 +1,593 @@ package ledis -import ( - "encoding/binary" - "testing" -) - -func cmpBytes(a []byte, b []byte) bool { - if len(a) != len(b) { - println("len diff") - println(len(a)) - println(len(b)) - return true - } - - for i, n := range a { - if n != b[i] { - println("diff !") - println(i) - println(n) - println(b[i]) - return true - } - } - return false -} - -func newBytes(bitLen int32) []byte { - bytes := bitLen / 8 - if bitLen%8 > 0 { - bytes++ - } - - return make([]byte, bytes, bytes) -} - -func TestBinary(t *testing.T) { - testSimple(t) - testSimpleII(t) - testCount(t) - testOpAndOr(t) - testOpXor(t) - testOpNot(t) - testMSetBit(t) - testBitExpire(t) - testBFlush(t) -} - -func testSimple(t *testing.T) { - db := getTestDB() - - key := []byte("test_bin") - - if v, _ := db.BGetBit(key, 100); v != 0 { - t.Error(v) - } - - if ori, _ := db.BSetBit(key, 50, 1); ori != 0 { - t.Error(ori) - } - - if v, _ := db.BGetBit(key, 50); v != 1 { - t.Error(v) - } - - if ori, _ := db.BSetBit(key, 50, 0); ori != 1 { - t.Error(ori) - } - - if v, _ := db.BGetBit(key, 50); v != 0 { - t.Error(v) - } - - db.BSetBit(key, 7, 1) - db.BSetBit(key, 8, 1) - db.BSetBit(key, 9, 1) - db.BSetBit(key, 10, 1) - - if sum, _ := db.BCount(key, 0, -1); sum != 4 { - t.Error(sum) - } - - data, _ := db.BGet(key) - if cmpBytes(data, []byte{0x80, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00}) { - t.Error(data) - } - - if tail, _ := db.BTail(key); tail != int32(50) { - t.Error(tail) - } -} - -func testSimpleII(t *testing.T) { - db := getTestDB() - db.FlushAll() - - key := []byte("test_bin_2") - - pos := int32(1234567) - if ori, _ := db.BSetBit(key, pos, 1); ori != 0 { - t.Error(ori) - } - - if v, _ := db.BGetBit(key, pos); v != 1 { - t.Error(v) - } - - if v, _ := db.BGetBit(key, pos-1); v != 0 { - t.Error(v) - } - - if v, _ := db.BGetBit(key, pos+1); v != 0 { - t.Error(v) - } - - if tail, _ := db.BTail(key); tail != pos { - t.Error(tail) - } - - data, _ := db.BGet(key) - stdData := newBytes(pos + 1) - stdData[pos/8] = uint8(1 << (uint(pos) % 8)) - - if cmpBytes(data, stdData) { - t.Error(len(data)) - } +// import ( +// "encoding/binary" +// "testing" +// ) + +// func cmpBytes(a []byte, b []byte) bool { +// if len(a) != len(b) { +// println("len diff") +// println(len(a)) +// println(len(b)) +// return true +// } + +// for i, n := range a { +// if n != b[i] { +// println("diff !") +// println(i) +// println(n) +// println(b[i]) +// return true +// } +// } +// return false +// } + +// func newBytes(bitLen int32) []byte { +// bytes := bitLen / 8 +// if bitLen%8 > 0 { +// bytes++ +// } + +// return make([]byte, bytes, bytes) +// } + +// func TestBinary(t *testing.T) { +// testSimple(t) +// testSimpleII(t) +// testCount(t) +// testOpAndOr(t) +// testOpXor(t) +// testOpNot(t) +// testMSetBit(t) +// testBitExpire(t) +// testBFlush(t) +// } + +// func testSimple(t *testing.T) { +// db := getTestDB() + +// key := []byte("test_bin") + +// if v, _ := db.BGetBit(key, 100); v != 0 { +// t.Error(v) +// } + +// if ori, _ := db.BSetBit(key, 50, 1); ori != 0 { +// t.Error(ori) +// } + +// if v, _ := db.BGetBit(key, 50); v != 1 { +// t.Error(v) +// } + +// if ori, _ := db.BSetBit(key, 50, 0); ori != 1 { +// t.Error(ori) +// } + +// if v, _ := db.BGetBit(key, 50); v != 0 { +// t.Error(v) +// } + +// db.BSetBit(key, 7, 1) +// db.BSetBit(key, 8, 1) +// db.BSetBit(key, 9, 1) +// db.BSetBit(key, 10, 1) + +// if sum, _ := db.BCount(key, 0, -1); sum != 4 { +// t.Error(sum) +// } + +// data, _ := db.BGet(key) +// if cmpBytes(data, []byte{0x80, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00}) { +// t.Error(data) +// } + +// if tail, _ := db.BTail(key); tail != int32(50) { +// t.Error(tail) +// } +// } + +// func testSimpleII(t *testing.T) { +// db := getTestDB() +// db.FlushAll() + +// key := []byte("test_bin_2") + +// pos := int32(1234567) +// if ori, _ := db.BSetBit(key, pos, 1); ori != 0 { +// t.Error(ori) +// } + +// if v, _ := db.BGetBit(key, pos); v != 1 { +// t.Error(v) +// } + +// if v, _ := db.BGetBit(key, pos-1); v != 0 { +// t.Error(v) +// } + +// if v, _ := db.BGetBit(key, pos+1); v != 0 { +// t.Error(v) +// } + +// if tail, _ := db.BTail(key); tail != pos { +// t.Error(tail) +// } + +// data, _ := db.BGet(key) +// stdData := newBytes(pos + 1) +// stdData[pos/8] = uint8(1 << (uint(pos) % 8)) + +// if cmpBytes(data, stdData) { +// t.Error(len(data)) +// } - if drop, _ := db.BDelete(key); drop != 1 { - t.Error(false) - } - - if data, _ := db.BGet(key); data != nil { - t.Error(data) - } -} +// if drop, _ := db.BDelete(key); drop != 1 { +// t.Error(false) +// } + +// if data, _ := db.BGet(key); data != nil { +// t.Error(data) +// } +// } -func testCount(t *testing.T) { - db := getTestDB() - db.FlushAll() +// func testCount(t *testing.T) { +// db := getTestDB() +// db.FlushAll() - key := []byte("test_bin_count") +// key := []byte("test_bin_count") - if ori, _ := db.BSetBit(key, 0, 1); ori != 0 { - t.Error(ori) - } +// if ori, _ := db.BSetBit(key, 0, 1); ori != 0 { +// t.Error(ori) +// } - if ori, _ := db.BSetBit(key, 10, 1); ori != 0 { - t.Error(ori) - } +// if ori, _ := db.BSetBit(key, 10, 1); ori != 0 { +// t.Error(ori) +// } - if ori, _ := db.BSetBit(key, 262140, 1); ori != 0 { - t.Error(ori) - } - - // count - - if sum, _ := db.BCount(key, 0, -1); sum != 3 { - t.Error(sum) - } - - if sum, _ := db.BCount(key, 0, 9); sum != 1 { - t.Error(sum) - } - - if sum, _ := db.BCount(key, 0, 10); sum != 2 { - t.Error(sum) - } - - if sum, _ := db.BCount(key, 0, 11); sum != 2 { - t.Error(sum) - } - - if sum, _ := db.BCount(key, 0, 262139); sum != 2 { - t.Error(sum) - } - - if sum, _ := db.BCount(key, 0, 262140); sum != 3 { - t.Error(sum) - } - - if sum, _ := db.BCount(key, 0, 262141); sum != 3 { - t.Error(sum) - } - - if sum, _ := db.BCount(key, 10, 262140); sum != 2 { - t.Error(sum) - } - - if sum, _ := db.BCount(key, 11, 262140); sum != 1 { - t.Error(sum) - } - - if sum, _ := db.BCount(key, 11, 262139); sum != 0 { - t.Error(sum) - } - - key = []byte("test_bin_count_2") - - db.BSetBit(key, 1, 1) - db.BSetBit(key, 2, 1) - db.BSetBit(key, 4, 1) - db.BSetBit(key, 6, 1) - - if sum, _ := db.BCount(key, 0, -1); sum != 4 { - t.Error(sum) - } - - if sum, _ := db.BCount(key, 1, 1); sum != 1 { - t.Error(sum) - } - - if sum, _ := db.BCount(key, 0, 7); sum != 4 { - t.Error(sum) - } - - if ori, _ := db.BSetBit(key, 8, 1); ori != 0 { - t.Error(ori) - } - - if ori, _ := db.BSetBit(key, 11, 1); ori != 0 { - t.Error(ori) - } - - if sum, _ := db.BCount(key, 0, -1); sum != 6 { - t.Error(sum) - } - - if sum, _ := db.BCount(key, 0, 16); sum != 6 { - t.Error(sum) - } -} - -func testOpAndOr(t *testing.T) { - db := getTestDB() - db.FlushAll() - - dstKey := []byte("test_bin_op_and_or") - - k0 := []byte("op_0") - k1 := []byte("op_01") - k2 := []byte("op_10") - k3 := []byte("op_11") - srcKeys := [][]byte{k2, k0, k1, k3} - - /* - - - ... - 0 - [10000000] ... [00000001] - 1 - nil - 2 - [00000000] ... [11111111] ... [00000000] - 3 - [01010101] ... [10000001] [10101010] - 4 - [10000000] ... [00000000] - 5 - [00000000] ... [00000011] [00000001] - ... - */ - // (k0 - seg:0) - db.BSetBit(k0, int32(0), 1) - db.BSetBit(k0, int32(segBitSize-1), 1) - // (k0 - seg:2) - pos := segBitSize*2 + segBitSize/2 - for i := uint32(0); i < 8; i++ { - db.BSetBit(k0, int32(pos+i), 1) - } - // (k0 - seg:3) - pos = segBitSize * 3 - db.BSetBit(k0, int32(pos+8), 1) - db.BSetBit(k0, int32(pos+15), 1) - for i := uint32(1); i < 8; i += 2 { - db.BSetBit(k0, int32(pos+i), 1) - } - pos = segBitSize*4 - 8 - for i := uint32(0); i < 8; i += 2 { - db.BSetBit(k0, int32(pos+i), 1) - } - // (k0 - seg:4) - db.BSetBit(k0, int32(segBitSize*5-1), 1) - // (k0 - seg:5) - db.BSetBit(k0, int32(segBitSize*5), 1) - db.BSetBit(k0, int32(segBitSize*5+8), 1) - db.BSetBit(k0, int32(segBitSize*5+9), 1) - - /* - - 0 - nil - 1 - [00000001] ... [10000000] - 2 - nil - 3 - [10101010] ... [10000001] [01010101] - ... - */ - // (k1 - seg:1) - db.BSetBit(k1, int32(segBitSize+7), 1) - db.BSetBit(k1, int32(segBitSize*2-8), 1) - // (k1 - seg:3) - pos = segBitSize * 3 - db.BSetBit(k1, int32(pos+8), 1) - db.BSetBit(k1, int32(pos+15), 1) - for i := uint32(0); i < 8; i += 2 { - db.BSetBit(k0, int32(pos+i), 1) - } - pos = segBitSize*4 - 8 - for i := uint32(1); i < 8; i += 2 { - db.BSetBit(k0, int32(pos+i), 1) - } - - var stdData []byte - var data []byte - var tmpKeys [][]byte - - // op - or - db.BOperation(OPor, dstKey, srcKeys...) - - stdData = make([]byte, 5*segByteSize+2) - stdData[0] = uint8(0x01) - stdData[segByteSize-1] = uint8(0x80) - stdData[segByteSize] = uint8(0x80) - stdData[segByteSize*2-1] = uint8(0x01) - stdData[segByteSize*2+segByteSize/2] = uint8(0xff) - stdData[segByteSize*3] = uint8(0xff) - stdData[segByteSize*3+1] = uint8(0x81) - stdData[segByteSize*4-1] = uint8(0xff) - stdData[segByteSize*5-1] = uint8(0x80) - stdData[segByteSize*5] = uint8(0x01) - stdData[segByteSize*5+1] = uint8(0x03) - - data, _ = db.BGet(dstKey) - if cmpBytes(data, stdData) { - t.Fatal(false) - } - - tmpKeys = [][]byte{k0, dstKey, k1} - db.BOperation(OPor, dstKey, tmpKeys...) - - data, _ = db.BGet(dstKey) - if cmpBytes(data, stdData) { - t.Fatal(false) - } - - // op - and - db.BOperation(OPand, dstKey, srcKeys...) - - stdData = make([]byte, 5*segByteSize+2) - stdData[segByteSize*3+1] = uint8(0x81) - - data, _ = db.BGet(dstKey) - if cmpBytes(data, stdData) { - t.Fatal(false) - } - - tmpKeys = [][]byte{k0, dstKey, k1} - db.BOperation(OPand, dstKey, tmpKeys...) - - data, _ = db.BGet(dstKey) - if cmpBytes(data, stdData) { - t.Fatal(false) - } - -} - -func testOpAnd(t *testing.T) { - db := getTestDB() - db.FlushAll() - - dstKey := []byte("test_bin_or") - - k0 := []byte("op_or_0") - k1 := []byte("op_or_01") - srcKeys := [][]byte{k0, k1} - - db.BSetBit(k0, 0, 1) - db.BSetBit(k0, 2, 1) - - db.BSetBit(k1, 1, 1) - - if blen, _ := db.BOperation(OPand, dstKey, srcKeys...); blen != 3 { - t.Fatal(blen) - } - - if cnt, _ := db.BCount(dstKey, 0, -1); cnt != 1 { - t.Fatal(1) - } -} - -func testOpXor(t *testing.T) { - db := getTestDB() - db.FlushAll() - - dstKey := []byte("test_bin_op_xor") - - k0 := []byte("op_xor_00") - k1 := []byte("op_xor_01") - srcKeys := [][]byte{k0, k1} - - reqs := make([]BitPair, 4) - reqs[0] = BitPair{0, 1} - reqs[1] = BitPair{7, 1} - reqs[2] = BitPair{int32(segBitSize - 1), 1} - reqs[3] = BitPair{int32(segBitSize - 8), 1} - db.BMSetBit(k0, reqs...) - - reqs = make([]BitPair, 2) - reqs[0] = BitPair{7, 1} - reqs[1] = BitPair{int32(segBitSize - 8), 1} - db.BMSetBit(k1, reqs...) - - 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) - } -} +// if ori, _ := db.BSetBit(key, 262140, 1); ori != 0 { +// t.Error(ori) +// } + +// // count + +// if sum, _ := db.BCount(key, 0, -1); sum != 3 { +// t.Error(sum) +// } + +// if sum, _ := db.BCount(key, 0, 9); sum != 1 { +// t.Error(sum) +// } + +// if sum, _ := db.BCount(key, 0, 10); sum != 2 { +// t.Error(sum) +// } + +// if sum, _ := db.BCount(key, 0, 11); sum != 2 { +// t.Error(sum) +// } + +// if sum, _ := db.BCount(key, 0, 262139); sum != 2 { +// t.Error(sum) +// } + +// if sum, _ := db.BCount(key, 0, 262140); sum != 3 { +// t.Error(sum) +// } + +// if sum, _ := db.BCount(key, 0, 262141); sum != 3 { +// t.Error(sum) +// } + +// if sum, _ := db.BCount(key, 10, 262140); sum != 2 { +// t.Error(sum) +// } + +// if sum, _ := db.BCount(key, 11, 262140); sum != 1 { +// t.Error(sum) +// } + +// if sum, _ := db.BCount(key, 11, 262139); sum != 0 { +// t.Error(sum) +// } + +// key = []byte("test_bin_count_2") + +// db.BSetBit(key, 1, 1) +// db.BSetBit(key, 2, 1) +// db.BSetBit(key, 4, 1) +// db.BSetBit(key, 6, 1) + +// if sum, _ := db.BCount(key, 0, -1); sum != 4 { +// t.Error(sum) +// } + +// if sum, _ := db.BCount(key, 1, 1); sum != 1 { +// t.Error(sum) +// } + +// if sum, _ := db.BCount(key, 0, 7); sum != 4 { +// t.Error(sum) +// } + +// if ori, _ := db.BSetBit(key, 8, 1); ori != 0 { +// t.Error(ori) +// } + +// if ori, _ := db.BSetBit(key, 11, 1); ori != 0 { +// t.Error(ori) +// } + +// if sum, _ := db.BCount(key, 0, -1); sum != 6 { +// t.Error(sum) +// } + +// if sum, _ := db.BCount(key, 0, 16); sum != 6 { +// t.Error(sum) +// } +// } + +// func testOpAndOr(t *testing.T) { +// db := getTestDB() +// db.FlushAll() + +// dstKey := []byte("test_bin_op_and_or") + +// k0 := []byte("op_0") +// k1 := []byte("op_01") +// k2 := []byte("op_10") +// k3 := []byte("op_11") +// srcKeys := [][]byte{k2, k0, k1, k3} + +// /* +// +// - ... +// 0 - [10000000] ... [00000001] +// 1 - nil +// 2 - [00000000] ... [11111111] ... [00000000] +// 3 - [01010101] ... [10000001] [10101010] +// 4 - [10000000] ... [00000000] +// 5 - [00000000] ... [00000011] [00000001] +// ... +// */ +// // (k0 - seg:0) +// db.BSetBit(k0, int32(0), 1) +// db.BSetBit(k0, int32(segBitSize-1), 1) +// // (k0 - seg:2) +// pos := segBitSize*2 + segBitSize/2 +// for i := uint32(0); i < 8; i++ { +// db.BSetBit(k0, int32(pos+i), 1) +// } +// // (k0 - seg:3) +// pos = segBitSize * 3 +// db.BSetBit(k0, int32(pos+8), 1) +// db.BSetBit(k0, int32(pos+15), 1) +// for i := uint32(1); i < 8; i += 2 { +// db.BSetBit(k0, int32(pos+i), 1) +// } +// pos = segBitSize*4 - 8 +// for i := uint32(0); i < 8; i += 2 { +// db.BSetBit(k0, int32(pos+i), 1) +// } +// // (k0 - seg:4) +// db.BSetBit(k0, int32(segBitSize*5-1), 1) +// // (k0 - seg:5) +// db.BSetBit(k0, int32(segBitSize*5), 1) +// db.BSetBit(k0, int32(segBitSize*5+8), 1) +// db.BSetBit(k0, int32(segBitSize*5+9), 1) + +// /* +// +// 0 - nil +// 1 - [00000001] ... [10000000] +// 2 - nil +// 3 - [10101010] ... [10000001] [01010101] +// ... +// */ +// // (k1 - seg:1) +// db.BSetBit(k1, int32(segBitSize+7), 1) +// db.BSetBit(k1, int32(segBitSize*2-8), 1) +// // (k1 - seg:3) +// pos = segBitSize * 3 +// db.BSetBit(k1, int32(pos+8), 1) +// db.BSetBit(k1, int32(pos+15), 1) +// for i := uint32(0); i < 8; i += 2 { +// db.BSetBit(k0, int32(pos+i), 1) +// } +// pos = segBitSize*4 - 8 +// for i := uint32(1); i < 8; i += 2 { +// db.BSetBit(k0, int32(pos+i), 1) +// } + +// var stdData []byte +// var data []byte +// var tmpKeys [][]byte + +// // op - or +// db.BOperation(OPor, dstKey, srcKeys...) + +// stdData = make([]byte, 5*segByteSize+2) +// stdData[0] = uint8(0x01) +// stdData[segByteSize-1] = uint8(0x80) +// stdData[segByteSize] = uint8(0x80) +// stdData[segByteSize*2-1] = uint8(0x01) +// stdData[segByteSize*2+segByteSize/2] = uint8(0xff) +// stdData[segByteSize*3] = uint8(0xff) +// stdData[segByteSize*3+1] = uint8(0x81) +// stdData[segByteSize*4-1] = uint8(0xff) +// stdData[segByteSize*5-1] = uint8(0x80) +// stdData[segByteSize*5] = uint8(0x01) +// stdData[segByteSize*5+1] = uint8(0x03) + +// data, _ = db.BGet(dstKey) +// if cmpBytes(data, stdData) { +// t.Fatal(false) +// } + +// tmpKeys = [][]byte{k0, dstKey, k1} +// db.BOperation(OPor, dstKey, tmpKeys...) + +// data, _ = db.BGet(dstKey) +// if cmpBytes(data, stdData) { +// t.Fatal(false) +// } + +// // op - and +// db.BOperation(OPand, dstKey, srcKeys...) + +// stdData = make([]byte, 5*segByteSize+2) +// stdData[segByteSize*3+1] = uint8(0x81) + +// data, _ = db.BGet(dstKey) +// if cmpBytes(data, stdData) { +// t.Fatal(false) +// } + +// tmpKeys = [][]byte{k0, dstKey, k1} +// db.BOperation(OPand, dstKey, tmpKeys...) + +// data, _ = db.BGet(dstKey) +// if cmpBytes(data, stdData) { +// t.Fatal(false) +// } + +// } + +// func testOpAnd(t *testing.T) { +// db := getTestDB() +// db.FlushAll() + +// dstKey := []byte("test_bin_or") + +// k0 := []byte("op_or_0") +// k1 := []byte("op_or_01") +// srcKeys := [][]byte{k0, k1} + +// db.BSetBit(k0, 0, 1) +// db.BSetBit(k0, 2, 1) + +// db.BSetBit(k1, 1, 1) + +// if blen, _ := db.BOperation(OPand, dstKey, srcKeys...); blen != 3 { +// t.Fatal(blen) +// } + +// if cnt, _ := db.BCount(dstKey, 0, -1); cnt != 1 { +// t.Fatal(1) +// } +// } + +// func testOpXor(t *testing.T) { +// db := getTestDB() +// db.FlushAll() + +// dstKey := []byte("test_bin_op_xor") + +// k0 := []byte("op_xor_00") +// k1 := []byte("op_xor_01") +// srcKeys := [][]byte{k0, k1} + +// reqs := make([]BitPair, 4) +// reqs[0] = BitPair{0, 1} +// reqs[1] = BitPair{7, 1} +// reqs[2] = BitPair{int32(segBitSize - 1), 1} +// reqs[3] = BitPair{int32(segBitSize - 8), 1} +// db.BMSetBit(k0, reqs...) + +// reqs = make([]BitPair, 2) +// reqs[0] = BitPair{7, 1} +// reqs[1] = BitPair{int32(segBitSize - 8), 1} +// db.BMSetBit(k1, reqs...) + +// 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() +// func testOpNot(t *testing.T) { +// db := getTestDB() +// db.FlushAll() - // intputs - dstKey := []byte("test_bin_op_not") +// // intputs +// dstKey := []byte("test_bin_op_not") - k0 := []byte("op_not_0") - srcKeys := [][]byte{k0} +// k0 := []byte("op_not_0") +// srcKeys := [][]byte{k0} - db.BSetBit(k0, int32(0), 1) - db.BSetBit(k0, int32(7), 1) +// 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) - } +// 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) +// 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) +// // 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...) +// // op - not +// db.BOperation(OPnot, dstKey, srcKeys...) - data, _ := db.BGet(dstKey) - if cmpBytes(data, stdData) { - t.Fatal(false) - } +// data, _ := db.BGet(dstKey) +// if cmpBytes(data, stdData) { +// t.Fatal(false) +// } - k1 := []byte("op_not_2") - srcKeys = [][]byte{k1} +// k1 := []byte("op_not_2") +// srcKeys = [][]byte{k1} - db.BSetBit(k1, 0, 1) - db.BSetBit(k1, 2, 1) - db.BSetBit(k1, 4, 1) - db.BSetBit(k1, 6, 1) - - if blen, _ := db.BOperation(OPnot, dstKey, srcKeys...); blen != 7 { - t.Fatal(blen) - } - - if cnt, _ := db.BCount(dstKey, 0, -1); cnt != 3 { - t.Fatal(cnt) - } -} - -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 -} - -func testBitExpire(t *testing.T) { - db := getTestDB() - db.FlushAll() - - key := []byte("test_b_ttl") - - db.BSetBit(key, 0, 1) - - if res, err := db.BExpire(key, 100); res != 1 || err != nil { - t.Fatal(false) - } - - if ttl, err := db.BTTL(key); ttl != 100 || err != nil { - t.Fatal(false) - } -} - -func testBFlush(t *testing.T) { - db := getTestDB() - db.FlushAll() - - for i := 0; i < 2000; i++ { - key := make([]byte, 4) - binary.LittleEndian.PutUint32(key, uint32(i)) - if _, err := db.BSetBit(key, 1, 1); err != nil { - t.Fatal(err.Error()) - } - } - - if v, err := db.BScan(nil, 3000, true, ""); err != nil { - t.Fatal(err.Error()) - } else if len(v) != 2000 { - t.Fatal("invalid value ", len(v)) - } - - for i := 0; i < 2000; i++ { - key := make([]byte, 4) - binary.LittleEndian.PutUint32(key, uint32(i)) - if v, err := db.BGetBit(key, 1); err != nil { - t.Fatal(err.Error()) - } else if v != 1 { - t.Fatal("invalid value ", v) - } - } - - if n, err := db.bFlush(); err != nil { - t.Fatal(err.Error()) - } else if n != 2000 { - t.Fatal("invalid value ", n) - } - - if v, err := db.BScan(nil, 3000, true, ""); err != nil { - t.Fatal(err.Error()) - } else if len(v) != 0 { - t.Fatal("invalid value length ", len(v)) - } - - for i := 0; i < 2000; i++ { - key := make([]byte, 4) - binary.LittleEndian.PutUint32(key, uint32(i)) - if v, err := db.BGet(key); err != nil { - t.Fatal(err.Error()) - } else if v != nil { - - t.Fatal("invalid value ", v) - } - } - -} +// db.BSetBit(k1, 0, 1) +// db.BSetBit(k1, 2, 1) +// db.BSetBit(k1, 4, 1) +// db.BSetBit(k1, 6, 1) + +// if blen, _ := db.BOperation(OPnot, dstKey, srcKeys...); blen != 7 { +// t.Fatal(blen) +// } + +// if cnt, _ := db.BCount(dstKey, 0, -1); cnt != 3 { +// t.Fatal(cnt) +// } +// } + +// 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 +// } + +// func testBitExpire(t *testing.T) { +// db := getTestDB() +// db.FlushAll() + +// key := []byte("test_b_ttl") + +// db.BSetBit(key, 0, 1) + +// if res, err := db.BExpire(key, 100); res != 1 || err != nil { +// t.Fatal(false) +// } + +// if ttl, err := db.BTTL(key); ttl != 100 || err != nil { +// t.Fatal(false) +// } +// } + +// func testBFlush(t *testing.T) { +// db := getTestDB() +// db.FlushAll() + +// for i := 0; i < 2000; i++ { +// key := make([]byte, 4) +// binary.LittleEndian.PutUint32(key, uint32(i)) +// if _, err := db.BSetBit(key, 1, 1); err != nil { +// t.Fatal(err.Error()) +// } +// } + +// if v, err := db.BScan(nil, 3000, true, ""); err != nil { +// t.Fatal(err.Error()) +// } else if len(v) != 2000 { +// t.Fatal("invalid value ", len(v)) +// } + +// for i := 0; i < 2000; i++ { +// key := make([]byte, 4) +// binary.LittleEndian.PutUint32(key, uint32(i)) +// if v, err := db.BGetBit(key, 1); err != nil { +// t.Fatal(err.Error()) +// } else if v != 1 { +// t.Fatal("invalid value ", v) +// } +// } + +// if n, err := db.bFlush(); err != nil { +// t.Fatal(err.Error()) +// } else if n != 2000 { +// t.Fatal("invalid value ", n) +// } + +// if v, err := db.BScan(nil, 3000, true, ""); err != nil { +// t.Fatal(err.Error()) +// } else if len(v) != 0 { +// t.Fatal("invalid value length ", len(v)) +// } + +// for i := 0; i < 2000; i++ { +// key := make([]byte, 4) +// binary.LittleEndian.PutUint32(key, uint32(i)) +// if v, err := db.BGet(key); err != nil { +// t.Fatal(err.Error()) +// } else if v != nil { + +// t.Fatal("invalid value ", v) +// } +// } + +// } diff --git a/ledis/t_kv.go b/ledis/t_kv.go index 37315c1..398d2a8 100644 --- a/ledis/t_kv.go +++ b/ledis/t_kv.go @@ -1,9 +1,12 @@ package ledis import ( + "encoding/binary" "errors" + "fmt" "github.com/siddontang/go/num" "github.com/siddontang/ledisdb/store" + "strings" "time" ) @@ -394,3 +397,375 @@ func (db *DB) Persist(key []byte) (int64, error) { err = t.Commit() return n, err } + +func (db *DB) SetRange(key []byte, offset int, value []byte) (int64, error) { + if len(value) == 0 { + return 0, nil + } + + if err := checkKeySize(key); err != nil { + return 0, err + } else if len(value)+offset > MaxValueSize { + return 0, errValueSize + } + + key = db.encodeKVKey(key) + + t := db.kvBatch + + t.Lock() + defer t.Unlock() + + oldValue, err := db.bucket.Get(key) + if err != nil { + return 0, err + } + + extra := offset + len(value) - len(oldValue) + if extra > 0 { + oldValue = append(oldValue, make([]byte, extra)...) + } + + copy(oldValue[offset:], value) + + t.Put(key, oldValue) + + if err := t.Commit(); err != nil { + return 0, err + } + + return int64(len(oldValue)), nil +} + +func getRange(start int, end int, valLen int) (int, int) { + if start < 0 { + start = valLen + start + } + + if end < 0 { + end = valLen + end + } + + if start < 0 { + start = 0 + } + + if end < 0 { + end = 0 + } + + if end >= valLen { + end = valLen - 1 + } + return start, end +} + +func (db *DB) GetRange(key []byte, start int, end int) ([]byte, error) { + if err := checkKeySize(key); err != nil { + return nil, err + } + key = db.encodeKVKey(key) + + value, err := db.bucket.Get(key) + if err != nil { + return nil, err + } + + valLen := len(value) + + start, end = getRange(start, end, valLen) + + if start > end { + return nil, nil + } + + return value[start : end+1], nil +} + +func (db *DB) StrLen(key []byte) (int64, error) { + s, err := db.GetSlice(key) + if err != nil { + return 0, err + } + + n := s.Size() + s.Free() + return int64(n), nil +} + +func (db *DB) Append(key []byte, value []byte) (int64, error) { + if len(value) == 0 { + return 0, nil + } + + if err := checkKeySize(key); err != nil { + return 0, err + } + key = db.encodeKVKey(key) + + t := db.kvBatch + + t.Lock() + defer t.Unlock() + + oldValue, err := db.bucket.Get(key) + if err != nil { + return 0, err + } + + if len(oldValue)+len(value) > MaxValueSize { + return 0, errValueSize + } + + oldValue = append(oldValue, value...) + + t.Put(key, oldValue) + + if err := t.Commit(); err != nil { + return 0, nil + } + + return int64(len(oldValue)), nil +} + +func (db *DB) BitOP(op string, destKey []byte, srcKeys ...[]byte) (int64, error) { + if err := checkKeySize(destKey); err != nil { + return 0, err + } + + op = strings.ToLower(op) + if len(srcKeys) == 0 { + return 0, nil + } else if op == BitNot && len(srcKeys) > 1 { + return 0, fmt.Errorf("BITOP NOT has only one srckey") + } else if len(srcKeys) < 2 { + return 0, nil + } + + key := db.encodeKVKey(srcKeys[0]) + + value, err := db.bucket.Get(key) + if err != nil { + return 0, err + } + + if op == BitNot { + for i := 0; i < len(value); i++ { + value[i] ^= value[i] + } + } else { + for j := 1; j < len(srcKeys); j++ { + if err := checkKeySize(srcKeys[j]); err != nil { + return 0, err + } + + key = db.encodeKVKey(srcKeys[j]) + ovalue, err := db.bucket.Get(key) + if err != nil { + return 0, err + } + + if len(value) < len(ovalue) { + value, ovalue = ovalue, value + } + + for i := 0; i < len(ovalue); i++ { + switch op { + case BitAND: + value[i] &= ovalue[i] + case BitOR: + value[i] |= ovalue[i] + case BitXOR: + value[i] ^= ovalue[i] + default: + return 0, fmt.Errorf("invalid op type: %s", op) + } + } + + for i := len(ovalue); i < len(value); i++ { + switch op { + case BitAND: + value[i] &= 0 + case BitOR: + value[i] |= 0 + case BitXOR: + value[i] ^= 0 + } + } + } + } + + key = db.encodeKVKey(destKey) + + t := db.kvBatch + + t.Lock() + defer t.Unlock() + + t.Put(key, value) + + if err := t.Commit(); err != nil { + return 0, err + } + + return int64(len(value)), nil +} + +var bitsInByte = [256]int32{0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, + 4, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 1, 2, 2, 3, 2, 3, + 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, + 5, 5, 6, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, + 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, + 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 1, 2, + 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, + 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 2, 3, 3, 4, 3, 4, 4, + 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, + 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} + +func numberBitCount(i uint32) uint32 { + i = i - ((i >> 1) & 0x55555555) + i = (i & 0x33333333) + ((i >> 2) & 0x33333333) + return (((i + (i >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24 +} + +func (db *DB) BitCount(key []byte, start int, end int) (int64, error) { + if err := checkKeySize(key); err != nil { + return 0, err + } + + key = db.encodeKVKey(key) + value, err := db.bucket.Get(key) + if err != nil { + return 0, err + } + + start, end = getRange(start, end, len(value)) + value = value[start : end+1] + + var n int64 = 0 + + pos := 0 + for ; pos+4 <= len(value); pos = pos + 4 { + n += int64(numberBitCount(binary.BigEndian.Uint32(value[pos : pos+4]))) + } + + for ; pos < len(value); pos++ { + n += int64(bitsInByte[value[pos]]) + } + + return n, nil +} + +func (db *DB) BitPos(key []byte, on int, start int, end int) (int64, error) { + if err := checkKeySize(key); err != nil { + return 0, err + } + + if (on & ^1) != 0 { + return 0, fmt.Errorf("bit must be 0 or 1, not %d", on) + } + + var skipValue uint8 = 0 + if on == 0 { + skipValue = 0xFF + } + + key = db.encodeKVKey(key) + value, err := db.bucket.Get(key) + if err != nil { + return 0, err + } + + start, end = getRange(start, end, len(value)) + value = value[start : end+1] + + for i, v := range value { + if uint8(v) != skipValue { + for j := 0; j < 8; j++ { + isNull := uint8(v)&(1<> 3) + extra := byteOffset + 1 - len(value) + if extra > 0 { + value = append(value, make([]byte, extra)...) + } + + byteVal := value[byteOffset] + bit := 7 - uint8(uint32(offset)&0x7) + bitVal := byteVal & (1 << bit) + + byteVal &= ^(1 << bit) + byteVal |= (uint8(on&0x1) << bit) + + value[byteOffset] = byteVal + + t.Put(key, value) + if err := t.Commit(); err != nil { + return 0, err + } + + if bitVal > 0 { + return 1, nil + } else { + return 0, nil + } +} + +func (db *DB) GetBit(key []byte, offset int) (int64, error) { + if err := checkKeySize(key); err != nil { + return 0, err + } + + key = db.encodeKVKey(key) + + value, err := db.bucket.Get(key) + if err != nil { + return 0, err + } + + byteOffset := uint32(offset) >> 3 + bit := 7 - uint8(uint32(offset)&0x7) + + if byteOffset >= uint32(len(value)) { + return 0, nil + } + + bitVal := value[byteOffset] & (1 << bit) + if bitVal > 0 { + return 1, nil + } else { + return 0, nil + } +} diff --git a/ledis/t_kv_test.go b/ledis/t_kv_test.go index 77ce706..63ea052 100644 --- a/ledis/t_kv_test.go +++ b/ledis/t_kv_test.go @@ -42,6 +42,211 @@ func TestDBKV(t *testing.T) { t.Fatal(string(v2)) } + key3 := []byte("testdb_kv_range") + + if n, err := db.Append(key3, []byte("Hello")); err != nil { + t.Fatal(err) + } else if n != 5 { + t.Fatal(n) + } + + if n, err := db.Append(key3, []byte(" World")); err != nil { + t.Fatal(err) + } else if n != 11 { + t.Fatal(n) + } + + if n, err := db.StrLen(key3); err != nil { + t.Fatal(err) + } else if n != 11 { + t.Fatal(n) + } + + if v, err := db.GetRange(key3, 0, 4); err != nil { + t.Fatal(err) + } else if string(v) != "Hello" { + t.Fatal(string(v)) + } + + if v, err := db.GetRange(key3, 0, -1); err != nil { + t.Fatal(err) + } else if string(v) != "Hello World" { + t.Fatal(string(v)) + } + + if v, err := db.GetRange(key3, -5, -1); err != nil { + t.Fatal(err) + } else if string(v) != "World" { + t.Fatal(string(v)) + } + + if n, err := db.SetRange(key3, 6, []byte("Redis")); err != nil { + t.Fatal(err) + } else if n != 11 { + t.Fatal(n) + } + + if v, err := db.Get(key3); err != nil { + t.Fatal(err) + } else if string(v) != "Hello Redis" { + t.Fatal(string(v)) + } + + key4 := []byte("testdb_kv_range_none") + if n, err := db.SetRange(key4, 6, []byte("Redis")); err != nil { + t.Fatal(err) + } else if n != 11 { + t.Fatal(n) + } + + key5 := []byte("testdb_kv_bit") + if n, err := db.SetBit(key5, 7, 1); err != nil { + t.Fatal(err) + } else if n != 0 { + t.Fatal(n) + } + + if n, err := db.GetBit(key5, 0); err != nil { + t.Fatal(err) + } else if n != 0 { + t.Fatal(n) + } + if n, err := db.GetBit(key5, 7); err != nil { + t.Fatal(err) + } else if n != 1 { + t.Fatal(n) + } + if n, err := db.GetBit(key5, 100); err != nil { + t.Fatal(err) + } else if n != 0 { + t.Fatal(n) + } + + if n, err := db.BitCount(key5, 0, -1); err != nil { + t.Fatal(err) + } else if n != 1 { + t.Fatal(n) + } + + if n, err := db.BitPos(key5, 1, 0, -1); err != nil { + t.Fatal(err) + } else if n != 7 { + t.Fatal(n) + } + + if n, err := db.SetBit(key5, 7, 0); err != nil { + t.Fatal(err) + } else if n != 1 { + t.Fatal(n) + } + + if err := db.Set(key5, []byte{0xff, 0xf0, 0x00}); err != nil { + t.Fatal(err) + } + + if n, err := db.BitPos(key5, 0, 0, -1); err != nil { + t.Fatal(err) + } else if n != 12 { + t.Fatal(n) + } + + if err := db.Set(key5, []byte{0x00, 0xff, 0xf0}); err != nil { + t.Fatal(err) + } + + if n, err := db.BitPos(key5, 1, 0, -1); err != nil { + t.Fatal(err) + } else if n != 8 { + t.Fatal(n) + } + + if n, err := db.BitPos(key5, 1, 2, -1); err != nil { + t.Fatal(err) + } else if n != 16 { + t.Fatal(n) + } + + if err := db.Set(key5, []byte{0x00, 0x00, 0x00}); err != nil { + t.Fatal(err) + } + + if n, err := db.BitPos(key5, 1, 0, -1); err != nil { + t.Fatal(err) + } else if n != -1 { + t.Fatal(n) + } + + if err := db.Set(key5, []byte("foobar")); err != nil { + t.Fatal(err) + } + + if n, err := db.BitCount(key5, 0, -1); err != nil { + t.Fatal(err) + } else if n != 26 { + t.Fatal(n) + } + + if n, err := db.BitCount(key5, 0, 0); err != nil { + t.Fatal(err) + } else if n != 4 { + t.Fatal(n) + } + + if n, err := db.BitCount(key5, 1, 1); err != nil { + t.Fatal(err) + } else if n != 6 { + t.Fatal(n) + } + + key6 := []byte("testdb_kv_bitop_desc") + + if err := db.Set(key4, []byte("foobar")); err != nil { + t.Fatal(err) + } + + if err := db.Set(key5, []byte("abcdef")); err != nil { + t.Fatal(err) + } + + if n, err := db.BitOP(BitAND, key6, key4, key5); err != nil { + t.Fatal(err) + } else if n != 6 { + t.Fatal(err) + } + + if v, err := db.Get(key6); err != nil { + t.Fatal(err) + } else if string(v) != "`bc`ab" { + t.Fatal(string(v)) + } + + if err := db.Set(key4, []byte("fooba")); err != nil { + t.Fatal(err) + } + + if n, err := db.BitOP(BitOR, key6, key4, key5); err != nil { + t.Fatal(err) + } else if n != 6 { + t.Fatal(err) + } + + if v, err := db.Get(key6); err != nil { + t.Fatal(err) + } else if string(v) != "goofef" { + t.Fatal(string(v)) + } + + if n, err := db.BitOP(BitAND, key6, key4, key5); err != nil { + t.Fatal(err) + } else if n != 6 { + t.Fatal(err) + } + + if v, err := db.Get(key6); err != nil { + t.Fatal(err) + } else if string(v) != "`bc`a\x00" { + t.Fatal(string(v)) + } } func TestKVPersist(t *testing.T) { diff --git a/ledis/t_ttl.go b/ledis/t_ttl.go index 2fd2e3f..70c8088 100644 --- a/ledis/t_ttl.go +++ b/ledis/t_ttl.go @@ -139,7 +139,7 @@ func (c *ttlChecker) setNextCheckTime(when int64, force bool) { c.Lock() if force { c.nc = when - } else if c.nc > when { + } else if c.nc > when { c.nc = when } c.Unlock() diff --git a/ledis/t_ttl_test.go b/ledis/t_ttl_test.go index b041e41..371a40d 100644 --- a/ledis/t_ttl_test.go +++ b/ledis/t_ttl_test.go @@ -195,50 +195,50 @@ func setAdaptor(db *DB) *adaptor { } -func bitAdaptor(db *DB) *adaptor { - adp := new(adaptor) - adp.showIdent = func() string { - return "bit-adaptor" - } +// func bitAdaptor(db *DB) *adaptor { +// adp := new(adaptor) +// adp.showIdent = func() string { +// return "bit-adaptor" +// } - adp.set = func(k []byte, v []byte) (int64, error) { - datas := make([]BitPair, 3) - datas[0] = BitPair{0, 1} - datas[1] = BitPair{2, 1} - datas[2] = BitPair{5, 1} +// adp.set = func(k []byte, v []byte) (int64, error) { +// datas := make([]BitPair, 3) +// datas[0] = BitPair{0, 1} +// datas[1] = BitPair{2, 1} +// datas[2] = BitPair{5, 1} - if _, err := db.BMSetBit(k, datas...); err != nil { - return 0, err - } else { - return int64(len(datas)), nil - } - } +// if _, err := db.BMSetBit(k, datas...); err != nil { +// return 0, err +// } else { +// return int64(len(datas)), nil +// } +// } - adp.exists = func(k []byte) (int64, error) { - var start, end int32 = 0, -1 - if blen, err := db.BCount(k, start, end); err != nil || blen <= 0 { - return 0, err - } else { - return 1, nil - } - } +// adp.exists = func(k []byte) (int64, error) { +// var start, end int32 = 0, -1 +// if blen, err := db.BCount(k, start, end); err != nil || blen <= 0 { +// return 0, err +// } else { +// return 1, nil +// } +// } - adp.del = db.BDelete - adp.expire = db.BExpire - adp.expireAt = db.BExpireAt - adp.ttl = db.BTTL +// adp.del = db.BDelete +// adp.expire = db.BExpire +// adp.expireAt = db.BExpireAt +// adp.ttl = db.BTTL - return adp -} +// return adp +// } func allAdaptors(db *DB) []*adaptor { - adps := make([]*adaptor, 6) + adps := make([]*adaptor, 5) adps[0] = kvAdaptor(db) adps[1] = listAdaptor(db) adps[2] = hashAdaptor(db) adps[3] = zsetAdaptor(db) adps[4] = setAdaptor(db) - adps[5] = bitAdaptor(db) + //adps[5] = bitAdaptor(db) return adps } diff --git a/server/cmd_bit_test.go b/server/cmd_bit_test.go index f19850b..a8f84fb 100644 --- a/server/cmd_bit_test.go +++ b/server/cmd_bit_test.go @@ -1,367 +1,367 @@ package server -import ( - "github.com/siddontang/ledisdb/client/go/ledis" - "testing" -) - -func TestBit(t *testing.T) { - testBitGetSet(t) - testBitMset(t) - testBitCount(t) - testBitOpt(t) -} - -func testBitGetSet(t *testing.T) { - c := getTestConn() - defer c.Close() - - key := []byte("test_cmd_bin_basic") - - // get / set - if v, err := ledis.Int(c.Do("bgetbit", key, 1024)); err != nil { - t.Fatal(err) - } else if v != 0 { - t.Fatal(v) - } - - if ori, err := ledis.Int(c.Do("bsetbit", key, 1024, 1)); err != nil { - t.Fatal(err) - } else if ori != 0 { - t.Fatal(ori) - } - - if v, err := ledis.Int(c.Do("bgetbit", key, 1024)); err != nil { - t.Fatal(err) - } else if v != 1 { - t.Fatal(v) - } - - // fetch from revert pos - c.Do("bsetbit", key, 1000, 1) - - if v, err := ledis.Int(c.Do("bgetbit", key, -1)); err != nil { - t.Fatal(err) - } else if v != 1 { - t.Fatal(v) - } - - if v, err := ledis.Int(c.Do("bgetbit", key, -25)); err != nil { - t.Fatal(err) - } else if v != 1 { - t.Fatal(v) - } - - // delete - if drop, err := ledis.Int(c.Do("bdelete", key)); err != nil { - t.Fatal(err) - } else if drop != 1 { - t.Fatal(drop) - } - - if drop, err := ledis.Int(c.Do("bdelete", key)); err != nil { - t.Fatal(err) - } else if drop != 0 { - t.Fatal(drop) - } -} - -func testBitMset(t *testing.T) { - c := getTestConn() - defer c.Close() - - key := []byte("test_cmd_bin_mset") - - if n, err := ledis.Int( - c.Do("bmsetbit", key, - 500, 0, - 100, 1, - 200, 1, - 1000, 1, - 900, 0, - 500000, 1, - 600, 0, - 300, 1, - 100000, 1)); err != nil { - t.Fatal(err) - } else if n != 9 { - t.Fatal(n) - } - - fillPos := []int{100, 200, 300, 1000, 100000, 500000} - for _, pos := range fillPos { - v, err := ledis.Int(c.Do("bgetbit", key, pos)) - if err != nil || v != 1 { - t.Fatal(pos) - } - } - - // err - if n, err := ledis.Int( - c.Do("bmsetbit", key, 3, 0, 2, 1, 3, 0, 1, 1)); err == nil || n != 0 { - t.Fatal(n) // duplication on pos - } -} - -func testBitCount(t *testing.T) { - c := getTestConn() - defer c.Close() - - key := []byte("test_cmd_bin_count") - sum := 0 - for pos := 1; pos < 1000000; pos += 10001 { - c.Do("bsetbit", key, pos, 1) - sum++ - } - - if n, err := ledis.Int(c.Do("bcount", key)); err != nil { - t.Fatal(err) - } else if n != sum { - t.Fatal(n) - } -} - -func testBitOpt(t *testing.T) { - c := getTestConn() - defer c.Close() - - dstk := []byte("bin_op_res") - kmiss := []byte("bin_op_miss") - - k0 := []byte("bin_op_0") - k1 := []byte("bin_op_1") - c.Do("bmsetbit", k0, 10, 1, 30, 1, 50, 1, 70, 1, 100, 1) - c.Do("bmsetbit", k1, 20, 1, 40, 1, 60, 1, 80, 1, 100, 1) - - // case - lack of args - // todo ... - - // case - 'not' on inexisting key - if blen, err := ledis.Int( - c.Do("bopt", "not", dstk, kmiss)); err != nil { - t.Fatal(err) - } else if blen != 0 { - t.Fatal(blen) - } - - if v, _ := ledis.String(c.Do("bget", dstk)); v != "" { - t.Fatal(v) - } - - // case - 'and', 'or', 'xor' with inexisting key - opts := []string{"and", "or", "xor"} - for _, op := range opts { - if blen, err := ledis.Int( - c.Do("bopt", op, dstk, kmiss, k0)); err != nil { - t.Fatal(err) - } else if blen != 0 { - t.Fatal(blen) - } - } - - // case - 'and' - if blen, err := ledis.Int( - c.Do("bopt", "and", dstk, k0, k1)); err != nil { - t.Fatal(err) - } else if blen != 101 { - t.Fatal(blen) - } - - if v, _ := ledis.Int(c.Do("bgetbit", dstk, 100)); v != 1 { - t.Fatal(v) - } - - if v, _ := ledis.Int(c.Do("bgetbit", dstk, 20)); v != 0 { - t.Fatal(v) - } - - if v, _ := ledis.Int(c.Do("bgetbit", dstk, 40)); v != 0 { - t.Fatal(v) - } - - // case - 'or' - if blen, err := ledis.Int( - c.Do("bopt", "or", dstk, k0, k1)); err != nil { - t.Fatal(err) - } else if blen != 101 { - t.Fatal(blen) - } - - if v, _ := ledis.Int(c.Do("bgetbit", dstk, 100)); v != 1 { - t.Fatal(v) - } - - if v, _ := ledis.Int(c.Do("bgetbit", dstk, 20)); v != 1 { - t.Fatal(v) - } - - if v, _ := ledis.Int(c.Do("bgetbit", dstk, 40)); v != 1 { - t.Fatal(v) - } - - // case - 'xor' - if blen, err := ledis.Int( - c.Do("bopt", "xor", dstk, k0, k1)); err != nil { - t.Fatal(err) - } else if blen != 101 { - t.Fatal(blen) - } - - if v, _ := ledis.Int(c.Do("bgetbit", dstk, 100)); v != 0 { - t.Fatal(v) - } - - if v, _ := ledis.Int(c.Do("bgetbit", dstk, 20)); v != 1 { - t.Fatal(v) - } - - if v, _ := ledis.Int(c.Do("bgetbit", dstk, 40)); v != 1 { - t.Fatal(v) - } - - return -} - -func TestBitErrorParams(t *testing.T) { - c := getTestConn() - defer c.Close() - - if _, err := c.Do("bget"); err == nil { - t.Fatal("invalid err of %v", err) - } - - if _, err := c.Do("bdelete"); err == nil { - t.Fatal("invalid err of %v", err) - } - - // bsetbit - if _, err := c.Do("bsetbit"); err == nil { - t.Fatal("invalid err of %v", err) - } - - if _, err := c.Do("bsetbit", "test_bsetbit"); err == nil { - t.Fatal("invalid err of %v", err) - } - - if _, err := c.Do("bsetbit", "test_bsetbit", "o", "v"); err == nil { - t.Fatal("invalid err of %v", err) - } - - if _, err := c.Do("bsetbit", "test_bsetbit", "o", 1); err == nil { - t.Fatal("invalid err of %v", err) - } - - // if _, err := c.Do("bsetbit", "test_bsetbit", -1, 1); err == nil { - // t.Fatal("invalid err of %v", err) - // } - - if _, err := c.Do("bsetbit", "test_bsetbit", 1, "v"); err == nil { - t.Fatal("invalid err of %v", err) - } - - if _, err := c.Do("bsetbit", "test_bsetbit", 1, 2); err == nil { - t.Fatal("invalid err of %v", err) - } - - //bgetbit - if _, err := c.Do("bgetbit", "test_bgetbit"); err == nil { - t.Fatal("invalid err of %v", err) - } - - if _, err := c.Do("bgetbit", "test_bgetbit", "o"); err == nil { - t.Fatal("invalid err of %v", err) - } - - // if _, err := c.Do("bgetbit", "test_bgetbit", -1); err == nil { - // t.Fatal("invalid err of %v", err) - // } - - //bmsetbit - if _, err := c.Do("bmsetbit", "test_bmsetbit"); err == nil { - t.Fatal("invalid err of %v", err) - } - - if _, err := c.Do("bmsetbit", "test_bmsetbit", 0, 1, 2); err == nil { - t.Fatal("invalid err of %v", err) - } - - if _, err := c.Do("bmsetbit", "test_bmsetbit", "o", "v"); err == nil { - t.Fatal("invalid err of %v", err) - } - - if _, err := c.Do("bmsetbit", "test_bmsetbit", "o", 1); err == nil { - t.Fatal("invalid err of %v", err) - } - - // if _, err := c.Do("bmsetbit", "test_bmsetbit", -1, 1); err == nil { - // t.Fatal("invalid err of %v", err) - // } - - if _, err := c.Do("bmsetbit", "test_bmsetbit", 1, "v"); err == nil { - t.Fatal("invalid err of %v", err) - } - - if _, err := c.Do("bmsetbit", "test_bmsetbit", 1, 2); err == nil { - t.Fatal("invalid err of %v", err) - } - - if _, err := c.Do("bmsetbit", "test_bmsetbit", 1, 0.1); err == nil { - t.Fatal("invalid err of %v", err) - } - - //bcount +// import ( +// "github.com/siddontang/ledisdb/client/go/ledis" +// "testing" +// ) + +// func TestBit(t *testing.T) { +// testBitGetSet(t) +// testBitMset(t) +// testBitCount(t) +// testBitOpt(t) +// } + +// func testBitGetSet(t *testing.T) { +// c := getTestConn() +// defer c.Close() + +// key := []byte("test_cmd_bin_basic") + +// // get / set +// if v, err := ledis.Int(c.Do("bgetbit", key, 1024)); err != nil { +// t.Fatal(err) +// } else if v != 0 { +// t.Fatal(v) +// } + +// if ori, err := ledis.Int(c.Do("bsetbit", key, 1024, 1)); err != nil { +// t.Fatal(err) +// } else if ori != 0 { +// t.Fatal(ori) +// } + +// if v, err := ledis.Int(c.Do("bgetbit", key, 1024)); err != nil { +// t.Fatal(err) +// } else if v != 1 { +// t.Fatal(v) +// } + +// // fetch from revert pos +// c.Do("bsetbit", key, 1000, 1) + +// if v, err := ledis.Int(c.Do("bgetbit", key, -1)); err != nil { +// t.Fatal(err) +// } else if v != 1 { +// t.Fatal(v) +// } + +// if v, err := ledis.Int(c.Do("bgetbit", key, -25)); err != nil { +// t.Fatal(err) +// } else if v != 1 { +// t.Fatal(v) +// } + +// // delete +// if drop, err := ledis.Int(c.Do("bdelete", key)); err != nil { +// t.Fatal(err) +// } else if drop != 1 { +// t.Fatal(drop) +// } + +// if drop, err := ledis.Int(c.Do("bdelete", key)); err != nil { +// t.Fatal(err) +// } else if drop != 0 { +// t.Fatal(drop) +// } +// } + +// func testBitMset(t *testing.T) { +// c := getTestConn() +// defer c.Close() + +// key := []byte("test_cmd_bin_mset") + +// if n, err := ledis.Int( +// c.Do("bmsetbit", key, +// 500, 0, +// 100, 1, +// 200, 1, +// 1000, 1, +// 900, 0, +// 500000, 1, +// 600, 0, +// 300, 1, +// 100000, 1)); err != nil { +// t.Fatal(err) +// } else if n != 9 { +// t.Fatal(n) +// } + +// fillPos := []int{100, 200, 300, 1000, 100000, 500000} +// for _, pos := range fillPos { +// v, err := ledis.Int(c.Do("bgetbit", key, pos)) +// if err != nil || v != 1 { +// t.Fatal(pos) +// } +// } + +// // err +// if n, err := ledis.Int( +// c.Do("bmsetbit", key, 3, 0, 2, 1, 3, 0, 1, 1)); err == nil || n != 0 { +// t.Fatal(n) // duplication on pos +// } +// } + +// func testBitCount(t *testing.T) { +// c := getTestConn() +// defer c.Close() + +// key := []byte("test_cmd_bin_count") +// sum := 0 +// for pos := 1; pos < 1000000; pos += 10001 { +// c.Do("bsetbit", key, pos, 1) +// sum++ +// } + +// if n, err := ledis.Int(c.Do("bcount", key)); err != nil { +// t.Fatal(err) +// } else if n != sum { +// t.Fatal(n) +// } +// } + +// func testBitOpt(t *testing.T) { +// c := getTestConn() +// defer c.Close() + +// dstk := []byte("bin_op_res") +// kmiss := []byte("bin_op_miss") + +// k0 := []byte("bin_op_0") +// k1 := []byte("bin_op_1") +// c.Do("bmsetbit", k0, 10, 1, 30, 1, 50, 1, 70, 1, 100, 1) +// c.Do("bmsetbit", k1, 20, 1, 40, 1, 60, 1, 80, 1, 100, 1) + +// // case - lack of args +// // todo ... + +// // case - 'not' on inexisting key +// if blen, err := ledis.Int( +// c.Do("bopt", "not", dstk, kmiss)); err != nil { +// t.Fatal(err) +// } else if blen != 0 { +// t.Fatal(blen) +// } + +// if v, _ := ledis.String(c.Do("bget", dstk)); v != "" { +// t.Fatal(v) +// } + +// // case - 'and', 'or', 'xor' with inexisting key +// opts := []string{"and", "or", "xor"} +// for _, op := range opts { +// if blen, err := ledis.Int( +// c.Do("bopt", op, dstk, kmiss, k0)); err != nil { +// t.Fatal(err) +// } else if blen != 0 { +// t.Fatal(blen) +// } +// } + +// // case - 'and' +// if blen, err := ledis.Int( +// c.Do("bopt", "and", dstk, k0, k1)); err != nil { +// t.Fatal(err) +// } else if blen != 101 { +// t.Fatal(blen) +// } + +// if v, _ := ledis.Int(c.Do("bgetbit", dstk, 100)); v != 1 { +// t.Fatal(v) +// } + +// if v, _ := ledis.Int(c.Do("bgetbit", dstk, 20)); v != 0 { +// t.Fatal(v) +// } + +// if v, _ := ledis.Int(c.Do("bgetbit", dstk, 40)); v != 0 { +// t.Fatal(v) +// } + +// // case - 'or' +// if blen, err := ledis.Int( +// c.Do("bopt", "or", dstk, k0, k1)); err != nil { +// t.Fatal(err) +// } else if blen != 101 { +// t.Fatal(blen) +// } + +// if v, _ := ledis.Int(c.Do("bgetbit", dstk, 100)); v != 1 { +// t.Fatal(v) +// } + +// if v, _ := ledis.Int(c.Do("bgetbit", dstk, 20)); v != 1 { +// t.Fatal(v) +// } + +// if v, _ := ledis.Int(c.Do("bgetbit", dstk, 40)); v != 1 { +// t.Fatal(v) +// } + +// // case - 'xor' +// if blen, err := ledis.Int( +// c.Do("bopt", "xor", dstk, k0, k1)); err != nil { +// t.Fatal(err) +// } else if blen != 101 { +// t.Fatal(blen) +// } + +// if v, _ := ledis.Int(c.Do("bgetbit", dstk, 100)); v != 0 { +// t.Fatal(v) +// } + +// if v, _ := ledis.Int(c.Do("bgetbit", dstk, 20)); v != 1 { +// t.Fatal(v) +// } + +// if v, _ := ledis.Int(c.Do("bgetbit", dstk, 40)); v != 1 { +// t.Fatal(v) +// } + +// return +// } + +// func TestBitErrorParams(t *testing.T) { +// c := getTestConn() +// defer c.Close() + +// if _, err := c.Do("bget"); err == nil { +// t.Fatal("invalid err of %v", err) +// } + +// if _, err := c.Do("bdelete"); err == nil { +// t.Fatal("invalid err of %v", err) +// } + +// // bsetbit +// if _, err := c.Do("bsetbit"); err == nil { +// t.Fatal("invalid err of %v", err) +// } + +// if _, err := c.Do("bsetbit", "test_bsetbit"); err == nil { +// t.Fatal("invalid err of %v", err) +// } + +// if _, err := c.Do("bsetbit", "test_bsetbit", "o", "v"); err == nil { +// t.Fatal("invalid err of %v", err) +// } + +// if _, err := c.Do("bsetbit", "test_bsetbit", "o", 1); err == nil { +// t.Fatal("invalid err of %v", err) +// } + +// // if _, err := c.Do("bsetbit", "test_bsetbit", -1, 1); err == nil { +// // t.Fatal("invalid err of %v", err) +// // } + +// if _, err := c.Do("bsetbit", "test_bsetbit", 1, "v"); err == nil { +// t.Fatal("invalid err of %v", err) +// } + +// if _, err := c.Do("bsetbit", "test_bsetbit", 1, 2); err == nil { +// t.Fatal("invalid err of %v", err) +// } + +// //bgetbit +// if _, err := c.Do("bgetbit", "test_bgetbit"); err == nil { +// t.Fatal("invalid err of %v", err) +// } + +// if _, err := c.Do("bgetbit", "test_bgetbit", "o"); err == nil { +// t.Fatal("invalid err of %v", err) +// } + +// // if _, err := c.Do("bgetbit", "test_bgetbit", -1); err == nil { +// // t.Fatal("invalid err of %v", err) +// // } + +// //bmsetbit +// if _, err := c.Do("bmsetbit", "test_bmsetbit"); err == nil { +// t.Fatal("invalid err of %v", err) +// } + +// if _, err := c.Do("bmsetbit", "test_bmsetbit", 0, 1, 2); err == nil { +// t.Fatal("invalid err of %v", err) +// } + +// if _, err := c.Do("bmsetbit", "test_bmsetbit", "o", "v"); err == nil { +// t.Fatal("invalid err of %v", err) +// } + +// if _, err := c.Do("bmsetbit", "test_bmsetbit", "o", 1); err == nil { +// t.Fatal("invalid err of %v", err) +// } + +// // if _, err := c.Do("bmsetbit", "test_bmsetbit", -1, 1); err == nil { +// // t.Fatal("invalid err of %v", err) +// // } + +// if _, err := c.Do("bmsetbit", "test_bmsetbit", 1, "v"); err == nil { +// t.Fatal("invalid err of %v", err) +// } + +// if _, err := c.Do("bmsetbit", "test_bmsetbit", 1, 2); err == nil { +// t.Fatal("invalid err of %v", err) +// } + +// if _, err := c.Do("bmsetbit", "test_bmsetbit", 1, 0.1); err == nil { +// t.Fatal("invalid err of %v", err) +// } + +// //bcount - if _, err := c.Do("bcount"); err == nil { - t.Fatal("invalid err of %v", err) - } +// if _, err := c.Do("bcount"); err == nil { +// t.Fatal("invalid err of %v", err) +// } - if _, err := c.Do("bcount", "a", "b", "c"); err == nil { - t.Fatal("invalid err of %v", err) - } +// if _, err := c.Do("bcount", "a", "b", "c"); err == nil { +// t.Fatal("invalid err of %v", err) +// } - if _, err := c.Do("bcount", 1, "a"); err == nil { - t.Fatal("invalid err of %v", err) - } - - // if _, err := c.Do("bcount", 1); err == nil { - // t.Fatal("invalid err of %v", err) - // } - - //bopt - if _, err := c.Do("bopt"); err == nil { - t.Fatal("invalid err of %v", err) - } - - if _, err := c.Do("bopt", "and", 1); err == nil { - t.Fatal("invalid err of %v", err) - } - - if _, err := c.Do("bopt", "x", 1); err == nil { - t.Fatal("invalid err of %v", err) - } - - if _, err := c.Do("bopt", ""); err == nil { - t.Fatal("invalid err of %v", err) - } - - //bexpire - if _, err := c.Do("bexpire", "test_bexpire"); err == nil { - t.Fatal("invalid err of %v", err) - } - - //bexpireat - if _, err := c.Do("bexpireat", "test_bexpireat"); err == nil { - t.Fatal("invalid err of %v", err) - } - - //bttl - if _, err := c.Do("bttl"); err == nil { - t.Fatal("invalid err of %v", err) - } +// if _, err := c.Do("bcount", 1, "a"); err == nil { +// t.Fatal("invalid err of %v", err) +// } + +// // if _, err := c.Do("bcount", 1); err == nil { +// // t.Fatal("invalid err of %v", err) +// // } + +// //bopt +// if _, err := c.Do("bopt"); err == nil { +// t.Fatal("invalid err of %v", err) +// } + +// if _, err := c.Do("bopt", "and", 1); err == nil { +// t.Fatal("invalid err of %v", err) +// } + +// if _, err := c.Do("bopt", "x", 1); err == nil { +// t.Fatal("invalid err of %v", err) +// } + +// if _, err := c.Do("bopt", ""); err == nil { +// t.Fatal("invalid err of %v", err) +// } + +// //bexpire +// if _, err := c.Do("bexpire", "test_bexpire"); err == nil { +// t.Fatal("invalid err of %v", err) +// } + +// //bexpireat +// if _, err := c.Do("bexpireat", "test_bexpireat"); err == nil { +// t.Fatal("invalid err of %v", err) +// } + +// //bttl +// if _, err := c.Do("bttl"); err == nil { +// t.Fatal("invalid err of %v", err) +// } - //bpersist - if _, err := c.Do("bpersist"); err == nil { - t.Fatal("invalid err of %v", err) - } - -} +// //bpersist +// if _, err := c.Do("bpersist"); err == nil { +// t.Fatal("invalid err of %v", err) +// } + +// } diff --git a/server/cmd_kv.go b/server/cmd_kv.go index 4e7d601..45a4b7a 100644 --- a/server/cmd_kv.go +++ b/server/cmd_kv.go @@ -391,20 +391,230 @@ func xrevscanCommand(c *client) error { return xscanGeneric(c, c.db.RevScan) } +func appendCommand(c *client) error { + args := c.args + if len(args) != 2 { + return ErrCmdParams + } + + if n, err := c.db.Append(args[0], args[1]); err != nil { + return err + } else { + c.resp.writeInteger(n) + } + return nil +} + +func getrangeCommand(c *client) error { + args := c.args + if len(args) != 3 { + return ErrCmdParams + } + + key := args[0] + start, err := strconv.Atoi(string(args[1])) + if err != nil { + return err + } + + end, err := strconv.Atoi(string(args[2])) + if err != nil { + return err + } + + if v, err := c.db.GetRange(key, start, end); err != nil { + return err + } else { + c.resp.writeBulk(v) + } + + return nil + +} + +func setrangeCommand(c *client) error { + args := c.args + if len(args) != 3 { + return ErrCmdParams + } + + key := args[0] + offset, err := strconv.Atoi(string(args[1])) + if err != nil { + return err + } + + value := args[2] + + if n, err := c.db.SetRange(key, offset, value); err != nil { + return err + } else { + c.resp.writeInteger(n) + } + return nil +} + +func strlenCommand(c *client) error { + if len(c.args) != 1 { + return ErrCmdParams + } + + if n, err := c.db.StrLen(c.args[0]); err != nil { + return err + } else { + c.resp.writeInteger(n) + } + return nil +} + +func parseBitRange(args [][]byte) (start int, end int, err error) { + start = 0 + end = -1 + if len(args) > 0 { + if start, err = strconv.Atoi(string(args[0])); err != nil { + return + } + } + + if len(args) == 2 { + if end, err = strconv.Atoi(string(args[1])); err != nil { + return + } + } + return +} + +func bitcountCommand(c *client) error { + args := c.args + if len(args) == 0 || len(args) > 3 { + return ErrCmdParams + } + + key := args[0] + start, end, err := parseBitRange(args[1:]) + if err != nil { + return err + } + + if n, err := c.db.BitCount(key, start, end); err != nil { + return err + } else { + c.resp.writeInteger(n) + } + return nil +} + +func bitopCommand(c *client) error { + args := c.args + if len(args) < 3 { + return ErrCmdParams + } + + op := string(args[0]) + destKey := args[1] + srcKeys := args[2:] + + if n, err := c.db.BitOP(op, destKey, srcKeys...); err != nil { + return err + } else { + c.resp.writeInteger(n) + } + + return nil +} + +func bitposCommand(c *client) error { + args := c.args + if len(args) < 2 { + return ErrCmdParams + } + + key := args[0] + bit, err := strconv.Atoi(string(args[1])) + if err != nil { + return err + } + start, end, err := parseBitRange(args[2:]) + if err != nil { + return err + } + + if n, err := c.db.BitPos(key, bit, start, end); err != nil { + return err + } else { + c.resp.writeInteger(n) + } + return nil +} + +func getbitCommand(c *client) error { + args := c.args + if len(args) != 2 { + return ErrCmdParams + } + + key := args[0] + offset, err := strconv.Atoi(string(args[1])) + if err != nil { + return err + } + + if n, err := c.db.GetBit(key, offset); err != nil { + return err + } else { + c.resp.writeInteger(n) + } + return nil +} + +func setbitCommand(c *client) error { + args := c.args + if len(args) != 3 { + return ErrCmdParams + } + + key := args[0] + offset, err := strconv.Atoi(string(args[1])) + if err != nil { + return err + } + + value, err := strconv.Atoi(string(args[2])) + if err != nil { + return err + } + + if n, err := c.db.SetBit(key, offset, value); err != nil { + return err + } else { + c.resp.writeInteger(n) + } + return nil +} + func init() { + register("append", appendCommand) + register("bitcount", bitcountCommand) + register("bitop", bitopCommand) + register("bitpos", bitposCommand) register("decr", decrCommand) register("decrby", decrbyCommand) register("del", delCommand) register("exists", existsCommand) register("get", getCommand) + register("getbit", getbitCommand) + register("getrange", getrangeCommand) register("getset", getsetCommand) register("incr", incrCommand) register("incrby", incrbyCommand) register("mget", mgetCommand) register("mset", msetCommand) register("set", setCommand) + register("setbit", setbitCommand) register("setnx", setnxCommand) register("setex", setexCommand) + register("setrange", setrangeCommand) + register("strlen", strlenCommand) register("expire", expireCommand) register("expireat", expireAtCommand) register("ttl", ttlCommand) diff --git a/server/cmd_kv_test.go b/server/cmd_kv_test.go index d3b0fe4..e02066f 100644 --- a/server/cmd_kv_test.go +++ b/server/cmd_kv_test.go @@ -78,6 +78,72 @@ func TestKV(t *testing.T) { } else if n != 0 { t.Fatal(n) } + + rangeKey := "range_key" + if n, err := ledis.Int(c.Do("append", rangeKey, "Hello ")); err != nil { + t.Fatal(err) + } else if n != 6 { + t.Fatal(n) + } + + if n, err := ledis.Int(c.Do("setrange", rangeKey, 6, "Redis")); err != nil { + t.Fatal(err) + } else if n != 11 { + t.Fatal(n) + } + + if n, err := ledis.Int(c.Do("strlen", rangeKey)); err != nil { + t.Fatal(err) + } else if n != 11 { + t.Fatal(n) + } + + if v, err := ledis.String(c.Do("getrange", rangeKey, 0, -1)); err != nil { + t.Fatal(err) + } else if v != "Hello Redis" { + t.Fatal(v) + } + + bitKey := "bit_key" + if n, err := ledis.Int(c.Do("setbit", bitKey, 7, 1)); err != nil { + t.Fatal(err) + } else if n != 0 { + t.Fatal(n) + } + + if n, err := ledis.Int(c.Do("getbit", bitKey, 7)); err != nil { + t.Fatal(err) + } else if n != 1 { + t.Fatal(n) + } + + if n, err := ledis.Int(c.Do("bitcount", bitKey)); err != nil { + t.Fatal(err) + } else if n != 1 { + t.Fatal(n) + } + + if n, err := ledis.Int(c.Do("bitpos", bitKey, 1)); err != nil { + t.Fatal(err) + } else if n != 7 { + t.Fatal(n) + } + + c.Do("set", "key1", "foobar") + c.Do("set", "key2", "abcdef") + + if n, err := ledis.Int(c.Do("bitop", "and", "bit_dest_key", "key1", "key2")); err != nil { + t.Fatal(err) + } else if n != 6 { + t.Fatal(n) + } + + if v, err := ledis.String(c.Do("get", "bit_dest_key")); err != nil { + t.Fatal(err) + } else if v != "`bc`ab" { + t.Fatal(v) + } + } func TestKVM(t *testing.T) { diff --git a/server/cmd_ttl_test.go b/server/cmd_ttl_test.go index d851b83..5be1347 100644 --- a/server/cmd_ttl_test.go +++ b/server/cmd_ttl_test.go @@ -13,7 +13,7 @@ func now() int64 { func TestExpire(t *testing.T) { // test for kv, list, hash, set, zset, bitmap in all - ttlType := []string{"k", "l", "h", "s", "z", "b"} + ttlType := []string{"k", "l", "h", "s", "z"} var ( expire string @@ -61,7 +61,6 @@ func TestExpire(t *testing.T) { key = "bitmap_ttl" c.Do("bsetbit", key, 0, 1) } - // expire + ttl exp := int64(10) if n, err := ledis.Int(c.Do(expire, key, exp)); err != nil { diff --git a/server/scan_test.go b/server/scan_test.go index d7652df..1778416 100644 --- a/server/scan_test.go +++ b/server/scan_test.go @@ -33,7 +33,6 @@ func TestScan(t *testing.T) { testListScan(t, c) testZSetScan(t, c) testSetScan(t, c) - testBitScan(t, c) } @@ -154,14 +153,3 @@ func testSetScan(t *testing.T, c *ledis.Client) { checkScan(t, c, "xsscan") checkRevScan(t, c, "xsrevscan") } - -func testBitScan(t *testing.T, c *ledis.Client) { - for i := 0; i < 10; i++ { - if _, err := c.Do("bsetbit", fmt.Sprintf("%d", i), 1024, 1); err != nil { - t.Fatal(err) - } - } - - checkScan(t, c, "xbscan") - checkRevScan(t, c, "xbrevscan") -}