package bitutils
import (
"math"
"math/bits"
"unsafe"
"github.com/apache/arrow-go/v18/arrow/bitutil"
"github.com/apache/arrow-go/v18/internal/utils"
)
func loadWord(byt []byte ) uint64 {
return utils .ToLEUint64 (*(*uint64 )(unsafe .Pointer (&byt [0 ])))
}
func shiftWord(current , next uint64 , shift int64 ) uint64 {
if shift == 0 {
return current
}
return (current >> shift ) | (next << (64 - shift ))
}
type BitBlockCount struct {
Len int16
Popcnt int16
}
func (b BitBlockCount ) NoneSet () bool {
return b .Popcnt == 0
}
func (b BitBlockCount ) AllSet () bool {
return b .Len == b .Popcnt
}
type BitBlockCounter struct {
bitmap []byte
bitsRemaining int64
bitOffset int8
}
const (
wordBits int64 = 64
fourWordsBits int64 = wordBits * 4
)
func NewBitBlockCounter (bitmap []byte , startOffset , nbits int64 ) *BitBlockCounter {
return &BitBlockCounter {
bitmap : bitmap [startOffset /8 :],
bitsRemaining : nbits ,
bitOffset : int8 (startOffset % 8 ),
}
}
func (b *BitBlockCounter ) getBlockSlow (blockSize int64 ) BitBlockCount {
runlen := int16 (utils .Min (b .bitsRemaining , blockSize ))
popcnt := int16 (bitutil .CountSetBits (b .bitmap , int (b .bitOffset ), int (runlen )))
b .bitsRemaining -= int64 (runlen )
b .bitmap = b .bitmap [runlen /8 :]
return BitBlockCount {runlen , popcnt }
}
func (b *BitBlockCounter ) NextFourWords () BitBlockCount {
if b .bitsRemaining == 0 {
return BitBlockCount {0 , 0 }
}
totalPopcnt := 0
if b .bitOffset == 0 {
if b .bitsRemaining < fourWordsBits {
return b .getBlockSlow (fourWordsBits )
}
totalPopcnt += bits .OnesCount64 (loadWord (b .bitmap ))
totalPopcnt += bits .OnesCount64 (loadWord (b .bitmap [8 :]))
totalPopcnt += bits .OnesCount64 (loadWord (b .bitmap [16 :]))
totalPopcnt += bits .OnesCount64 (loadWord (b .bitmap [24 :]))
} else {
if b .bitsRemaining < 5 *fourWordsBits -int64 (b .bitOffset ) {
return b .getBlockSlow (fourWordsBits )
}
current := loadWord (b .bitmap )
next := loadWord (b .bitmap [8 :])
totalPopcnt += bits .OnesCount64 (shiftWord (current , next , int64 (b .bitOffset )))
current = next
next = loadWord (b .bitmap [16 :])
totalPopcnt += bits .OnesCount64 (shiftWord (current , next , int64 (b .bitOffset )))
current = next
next = loadWord (b .bitmap [24 :])
totalPopcnt += bits .OnesCount64 (shiftWord (current , next , int64 (b .bitOffset )))
current = next
next = loadWord (b .bitmap [32 :])
totalPopcnt += bits .OnesCount64 (shiftWord (current , next , int64 (b .bitOffset )))
}
b .bitmap = b .bitmap [bitutil .BytesForBits (fourWordsBits ):]
b .bitsRemaining -= fourWordsBits
return BitBlockCount {256 , int16 (totalPopcnt )}
}
func (b *BitBlockCounter ) NextWord () BitBlockCount {
if b .bitsRemaining == 0 {
return BitBlockCount {0 , 0 }
}
popcnt := 0
if b .bitOffset == 0 {
if b .bitsRemaining < wordBits {
return b .getBlockSlow (wordBits )
}
popcnt = bits .OnesCount64 (loadWord (b .bitmap ))
} else {
if b .bitsRemaining < (2 *wordBits - int64 (b .bitOffset )) {
return b .getBlockSlow (wordBits )
}
popcnt = bits .OnesCount64 (shiftWord (loadWord (b .bitmap ), loadWord (b .bitmap [8 :]), int64 (b .bitOffset )))
}
b .bitmap = b .bitmap [wordBits /8 :]
b .bitsRemaining -= wordBits
return BitBlockCount {64 , int16 (popcnt )}
}
type OptionalBitBlockCounter struct {
hasBitmap bool
pos int64
len int64
counter *BitBlockCounter
}
func NewOptionalBitBlockCounter (bitmap []byte , offset , length int64 ) *OptionalBitBlockCounter {
var counter *BitBlockCounter
if bitmap != nil {
counter = NewBitBlockCounter (bitmap , offset , length )
}
return &OptionalBitBlockCounter {
hasBitmap : bitmap != nil ,
pos : 0 ,
len : length ,
counter : counter ,
}
}
func (obc *OptionalBitBlockCounter ) NextBlock () BitBlockCount {
const maxBlockSize = math .MaxInt16
if obc .hasBitmap {
block := obc .counter .NextWord ()
obc .pos += int64 (block .Len )
return block
}
blockSize := int16 (utils .Min (maxBlockSize , obc .len -obc .pos ))
obc .pos += int64 (blockSize )
return BitBlockCount {blockSize , blockSize }
}
func (obc *OptionalBitBlockCounter ) NextWord () BitBlockCount {
const wordsize = 64
if obc .hasBitmap {
block := obc .counter .NextWord ()
obc .pos += int64 (block .Len )
return block
}
blockSize := int16 (utils .Min (wordsize , obc .len -obc .pos ))
obc .pos += int64 (blockSize )
return BitBlockCount {blockSize , blockSize }
}
func VisitBitBlocks (bitmap []byte , offset , length int64 , visitValid func (pos int64 ), visitInvalid func ()) {
counter := NewOptionalBitBlockCounter (bitmap , offset , length )
pos := int64 (0 )
for pos < length {
block := counter .NextBlock ()
if block .AllSet () {
for i := 0 ; i < int (block .Len ); i , pos = i +1 , pos +1 {
visitValid (pos )
}
} else if block .NoneSet () {
for i := 0 ; i < int (block .Len ); i , pos = i +1 , pos +1 {
visitInvalid ()
}
} else {
for i := 0 ; i < int (block .Len ); i , pos = i +1 , pos +1 {
if bitutil .BitIsSet (bitmap , int (offset +pos )) {
visitValid (pos )
} else {
visitInvalid ()
}
}
}
}
}
func VisitBitBlocksShort (bitmap []byte , offset , length int64 , visitValid func (pos int64 ) error , visitInvalid func () error ) error {
counter := NewOptionalBitBlockCounter (bitmap , offset , length )
pos := int64 (0 )
for pos < length {
block := counter .NextBlock ()
if block .AllSet () {
for i := 0 ; i < int (block .Len ); i , pos = i +1 , pos +1 {
if err := visitValid (pos ); err != nil {
return err
}
}
} else if block .NoneSet () {
for i := 0 ; i < int (block .Len ); i , pos = i +1 , pos +1 {
if err := visitInvalid (); err != nil {
return err
}
}
} else {
for i := 0 ; i < int (block .Len ); i , pos = i +1 , pos +1 {
if bitutil .BitIsSet (bitmap , int (offset +pos )) {
if err := visitValid (pos ); err != nil {
return err
}
} else {
if err := visitInvalid (); err != nil {
return err
}
}
}
}
}
return nil
}
func VisitTwoBitBlocks (leftBitmap , rightBitmap []byte , leftOffset , rightOffset int64 , len int64 , visitValid func (pos int64 ), visitNull func ()) {
if leftBitmap == nil || rightBitmap == nil {
if leftBitmap == nil {
VisitBitBlocks (rightBitmap , rightOffset , len , visitValid , visitNull )
} else {
VisitBitBlocks (leftBitmap , leftOffset , len , visitValid , visitNull )
}
return
}
bitCounter := NewBinaryBitBlockCounter (leftBitmap , rightBitmap , leftOffset , rightOffset , len )
var pos int64
for pos < len {
block := bitCounter .NextAndWord ()
if block .AllSet () {
for i := 0 ; i < int (block .Len ); i , pos = i +1 , pos +1 {
visitValid (pos )
}
} else if block .NoneSet () {
for i := 0 ; i < int (block .Len ); i , pos = i +1 , pos +1 {
visitNull ()
}
} else {
for i := 0 ; i < int (block .Len ); i , pos = i +1 , pos +1 {
if bitutil .BitIsSet (leftBitmap , int (leftOffset +pos )) && bitutil .BitIsSet (rightBitmap , int (rightOffset +pos )) {
visitValid (pos )
} else {
visitNull ()
}
}
}
}
}
type bitOp struct {
bit func (bool , bool ) bool
word func (uint64 , uint64 ) uint64
}
var (
bitBlockAnd = bitOp {
bit : func (a , b bool ) bool { return a && b },
word : func (a , b uint64 ) uint64 { return a & b },
}
bitBlockAndNot = bitOp {
bit : func (a , b bool ) bool { return a && !b },
word : func (a , b uint64 ) uint64 { return a &^ b },
}
bitBlockOr = bitOp {
bit : func (a , b bool ) bool { return a || b },
word : func (a , b uint64 ) uint64 { return a | b },
}
bitBlockOrNot = bitOp {
bit : func (a , b bool ) bool { return a || !b },
word : func (a , b uint64 ) uint64 { return a | ^b },
}
)
type BinaryBitBlockCounter struct {
left []byte
right []byte
bitsRemaining int64
leftOffset, rightOffset int64
bitsRequiredForWords int64
}
func NewBinaryBitBlockCounter (left , right []byte , leftOffset , rightOffset int64 , length int64 ) *BinaryBitBlockCounter {
ret := &BinaryBitBlockCounter {
left : left [leftOffset /8 :],
right : right [rightOffset /8 :],
leftOffset : leftOffset % 8 ,
rightOffset : rightOffset % 8 ,
bitsRemaining : length ,
}
leftBitsReq := int64 (64 )
if ret .leftOffset != 0 {
leftBitsReq = 64 + (64 - ret .leftOffset )
}
rightBitsReq := int64 (64 )
if ret .rightOffset != 0 {
rightBitsReq = 64 + (64 - ret .rightOffset )
}
if leftBitsReq > rightBitsReq {
ret .bitsRequiredForWords = leftBitsReq
} else {
ret .bitsRequiredForWords = rightBitsReq
}
return ret
}
func (b *BinaryBitBlockCounter ) NextAndWord () BitBlockCount { return b .nextWord (bitBlockAnd ) }
func (b *BinaryBitBlockCounter ) NextAndNotWord () BitBlockCount { return b .nextWord (bitBlockAndNot ) }
func (b *BinaryBitBlockCounter ) NextOrWord () BitBlockCount { return b .nextWord (bitBlockOr ) }
func (b *BinaryBitBlockCounter ) NextOrNotWord () BitBlockCount { return b .nextWord (bitBlockOrNot ) }
func (b *BinaryBitBlockCounter ) nextWord (op bitOp ) BitBlockCount {
if b .bitsRemaining == 0 {
return BitBlockCount {}
}
if b .bitsRemaining < b .bitsRequiredForWords {
runLength := int16 (b .bitsRemaining )
if runLength > int16 (wordBits ) {
runLength = int16 (wordBits )
}
var popcount int16
for i := int16 (0 ); i < runLength ; i ++ {
if op .bit (bitutil .BitIsSet (b .left , int (b .leftOffset )+int (i )),
bitutil .BitIsSet (b .right , int (b .rightOffset )+int (i ))) {
popcount ++
}
}
b .left = b .left [runLength /8 :]
b .right = b .right [runLength /8 :]
b .bitsRemaining -= int64 (runLength )
return BitBlockCount {Len : runLength , Popcnt : popcount }
}
var popcount int
if b .leftOffset == 0 && b .rightOffset == 0 {
popcount = bits .OnesCount64 (op .word (loadWord (b .left ), loadWord (b .right )))
} else {
leftWord := shiftWord (loadWord (b .left ), loadWord (b .left [8 :]), b .leftOffset )
rightWord := shiftWord (loadWord (b .right ), loadWord (b .right [8 :]), b .rightOffset )
popcount = bits .OnesCount64 (op .word (leftWord , rightWord ))
}
b .left = b .left [wordBits /8 :]
b .right = b .right [wordBits /8 :]
b .bitsRemaining -= wordBits
return BitBlockCount {Len : int16 (wordBits ), Popcnt : int16 (popcount )}
}
The pages are generated with Golds v0.8.2 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .