...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package bitutils
18
19 import (
20 "encoding/binary"
21 "fmt"
22 "math/bits"
23 "unsafe"
24
25 "github.com/apache/arrow/go/v15/arrow"
26 "github.com/apache/arrow/go/v15/arrow/bitutil"
27 "github.com/apache/arrow/go/v15/internal/utils"
28 )
29
30
31
32 type BitRun struct {
33 Len int64
34 Set bool
35 }
36
37
38
39
40
41
42
43 type BitRunReader interface {
44 NextRun() BitRun
45 }
46
47 func (b BitRun) String() string {
48 return fmt.Sprintf("{Length: %d, set=%t}", b.Len, b.Set)
49 }
50
51 type bitRunReader struct {
52 bitmap []byte
53 pos int64
54 length int64
55 word uint64
56 curRunBitSet bool
57 }
58
59
60
61 func NewBitRunReader(bitmap []byte, offset int64, length int64) BitRunReader {
62 ret := &bitRunReader{
63 bitmap: bitmap[offset/8:],
64 pos: offset % 8,
65 length: (offset % 8) + length,
66 }
67
68 if length == 0 {
69 return ret
70 }
71
72 ret.curRunBitSet = bitutil.BitIsNotSet(bitmap, int(offset))
73 bitsRemaining := length + ret.pos
74 ret.loadWord(bitsRemaining)
75 ret.word = ret.word &^ LeastSignificantBitMask(ret.pos)
76 return ret
77 }
78
79
80
81 func (b *bitRunReader) NextRun() BitRun {
82 if b.pos >= b.length {
83 return BitRun{0, false}
84 }
85
86
87
88
89
90
91
92
93
94 b.curRunBitSet = !b.curRunBitSet
95
96 start := b.pos
97 startOffset := start & 63
98
99
100
101 b.word = ^b.word &^ LeastSignificantBitMask(startOffset)
102
103
104 newbits := int64(bits.TrailingZeros64(b.word)) - startOffset
105 b.pos += newbits
106
107 if IsMultipleOf64(b.pos) && b.pos < b.length {
108 b.advanceUntilChange()
109 }
110 return BitRun{b.pos - start, b.curRunBitSet}
111 }
112
113 func (b *bitRunReader) advanceUntilChange() {
114 newbits := int64(0)
115 for {
116 b.bitmap = b.bitmap[arrow.Uint64SizeBytes:]
117 b.loadNextWord()
118 newbits = int64(bits.TrailingZeros64(b.word))
119 b.pos += newbits
120 if !IsMultipleOf64(b.pos) || b.pos >= b.length || newbits <= 0 {
121 break
122 }
123 }
124 }
125
126 func (b *bitRunReader) loadNextWord() {
127 b.loadWord(b.length - b.pos)
128 }
129
130 func (b *bitRunReader) loadWord(bitsRemaining int64) {
131 b.word = 0
132 if bitsRemaining >= 64 {
133 b.word = binary.LittleEndian.Uint64(b.bitmap)
134 } else {
135 nbytes := bitutil.BytesForBits(bitsRemaining)
136 wordptr := (*(*[8]byte)(unsafe.Pointer(&b.word)))[:]
137 copy(wordptr, b.bitmap[:nbytes])
138
139 bitutil.SetBitTo(wordptr, int(bitsRemaining), bitutil.BitIsNotSet(wordptr, int(bitsRemaining-1)))
140
141 b.word = utils.ToLEUint64(b.word)
142 }
143
144
145
146
147
148 if b.curRunBitSet {
149 b.word = ^b.word
150 }
151 }
152
View as plain text