1
1
package rockredis
2
2
3
3
import (
4
+ "encoding/binary"
4
5
"errors"
6
+ "fmt"
7
+ "math/bits"
5
8
"time"
6
9
7
10
"github.com/youzan/ZanRedisDB/common"
@@ -10,10 +13,13 @@ import (
10
13
11
14
const (
12
15
tsLen = 8
16
+ // 2MB
17
+ MaxBitOffset = 2 * 8 * 1024 * 1024
13
18
)
14
19
15
20
var errKVKey = errors .New ("invalid encode kv key" )
16
21
var errInvalidDBValue = errors .New ("invalide db value" )
22
+ var errBitOverflow = errors .New ("bit offset overflowed" )
17
23
18
24
func convertRedisKeyToDBKVKey (key []byte ) ([]byte , []byte , error ) {
19
25
table , _ , _ := extractTableFromRedisKey (key )
@@ -518,6 +524,111 @@ func (db *RockDB) Append(ts int64, key []byte, value []byte) (int64, error) {
518
524
return int64 (len (oldValue ) - tsLen ), nil
519
525
}
520
526
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
+
521
632
func (db * RockDB ) Expire (key []byte , duration int64 ) (int64 , error ) {
522
633
if exists , err := db .KVExists (key ); err != nil || exists != 1 {
523
634
return 0 , err
0 commit comments