Skip to content

Commit b1bf2e5

Browse files
committed
add bit command support
1 parent 77774b7 commit b1bf2e5

File tree

3 files changed

+222
-106
lines changed

3 files changed

+222
-106
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,3 +131,7 @@ based on this golang sdk if you want use the redis client in other language.
131131

132132

133133
[client-sdk]: https://github.com/youzan/go-zanredisdb
134+
135+
## Thanks
136+
137+
Many thanks for these great projects which make this project possible: etcd, RocksDB, ledisdb, pika.

rockredis/t_kv.go

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
package rockredis
22

33
import (
4+
"encoding/binary"
45
"errors"
6+
"fmt"
7+
"math/bits"
58
"time"
69

710
"github.com/youzan/ZanRedisDB/common"
@@ -10,10 +13,13 @@ import (
1013

1114
const (
1215
tsLen = 8
16+
// 2MB
17+
MaxBitOffset = 2 * 8 * 1024 * 1024
1318
)
1419

1520
var errKVKey = errors.New("invalid encode kv key")
1621
var errInvalidDBValue = errors.New("invalide db value")
22+
var errBitOverflow = errors.New("bit offset overflowed")
1723

1824
func convertRedisKeyToDBKVKey(key []byte) ([]byte, []byte, error) {
1925
table, _, _ := extractTableFromRedisKey(key)
@@ -518,6 +524,111 @@ func (db *RockDB) Append(ts int64, key []byte, value []byte) (int64, error) {
518524
return int64(len(oldValue) - tsLen), nil
519525
}
520526

527+
func (db *RockDB) BitSet(ts int64, key []byte, offset int64, on int) (int64, error) {
528+
table, key, err := convertRedisKeyToDBKVKey(key)
529+
if err != nil {
530+
return 0, err
531+
}
532+
if offset > MaxBitOffset {
533+
return 0, errBitOverflow
534+
}
535+
536+
if (on & ^1) != 0 {
537+
return 0, fmt.Errorf("bit should be 0 or 1, got %d", on)
538+
}
539+
var v []byte
540+
if v, err = db.eng.GetBytesNoLock(db.defaultReadOpts, key); err != nil {
541+
return 0, err
542+
}
543+
db.wb.Clear()
544+
if v == nil {
545+
db.IncrTableKeyCount(table, 1, db.wb)
546+
} else if len(v) >= tsLen {
547+
v = v[:len(v)-tsLen]
548+
}
549+
550+
byteOffset := int(uint32(offset) >> 3)
551+
expandLen := byteOffset + 1 - len(v)
552+
if expandLen > 0 {
553+
if on == 0 {
554+
// not changed
555+
return 0, nil
556+
}
557+
v = append(v, make([]byte, expandLen)...)
558+
}
559+
byteVal := v[byteOffset]
560+
bit := 7 - uint8(uint32(offset)&0x7)
561+
oldBit := byteVal & (1 << bit)
562+
563+
byteVal &= ^(1 << bit)
564+
byteVal |= (uint8(on&0x1) << bit)
565+
v[byteOffset] = byteVal
566+
v = append(v, PutInt64(ts)...)
567+
db.wb.Put(key, v)
568+
err = db.eng.Write(db.defaultWriteOpts, db.wb)
569+
if err != nil {
570+
return 0, err
571+
}
572+
if oldBit > 0 {
573+
return 1, nil
574+
}
575+
return 0, nil
576+
}
577+
578+
func popcountBytes(s []byte) (count int64) {
579+
for i := 0; i+8 <= len(s); i += 8 {
580+
x := binary.LittleEndian.Uint64(s[i:])
581+
count += int64(bits.OnesCount64(x))
582+
}
583+
584+
s = s[len(s)&^7:]
585+
586+
if len(s) >= 4 {
587+
count += int64(bits.OnesCount32(binary.LittleEndian.Uint32(s)))
588+
s = s[4:]
589+
}
590+
591+
if len(s) >= 2 {
592+
count += int64(bits.OnesCount16(binary.LittleEndian.Uint16(s)))
593+
s = s[2:]
594+
}
595+
596+
if len(s) == 1 {
597+
count += int64(bits.OnesCount8(s[0]))
598+
}
599+
return
600+
}
601+
602+
func (db *RockDB) BitGet(key []byte, offset int64) (int64, error) {
603+
v, err := db.KVGet(key)
604+
if err != nil {
605+
return 0, err
606+
}
607+
608+
byteOffset := (uint32(offset) >> 3)
609+
if byteOffset >= uint32(len(v)) {
610+
return 0, nil
611+
}
612+
byteVal := v[byteOffset]
613+
bit := 7 - uint8(uint32(offset)&0x7)
614+
oldBit := byteVal & (1 << bit)
615+
if oldBit > 0 {
616+
return 1, nil
617+
}
618+
619+
return 0, nil
620+
}
621+
622+
func (db *RockDB) BitCount(key []byte, start, end int) (int64, error) {
623+
v, err := db.KVGet(key)
624+
if err != nil {
625+
return 0, err
626+
}
627+
start, end = getRange(start, end, len(v))
628+
v = v[start : end+1]
629+
return popcountBytes(v), nil
630+
}
631+
521632
func (db *RockDB) Expire(key []byte, duration int64) (int64, error) {
522633
if exists, err := db.KVExists(key); err != nil || exists != 1 {
523634
return 0, err

0 commit comments

Comments
 (0)