port-rebase fixes

Signed-off-by: Artem Fetishev <rtm@victoriametrics.com>
This commit is contained in:
Artem Fetishev
2026-02-02 12:33:58 +01:00
parent 217d116c2c
commit 1f1c619abb
12 changed files with 344 additions and 91 deletions

2
go.mod
View File

@@ -7,7 +7,7 @@ require (
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.0
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.4
github.com/RoaringBitmap/roaring/v2 v2.14.0
github.com/RoaringBitmap/roaring/v2 v2.14.4
github.com/VictoriaMetrics/VictoriaLogs v0.0.0-20260218111324-95b48d57d032
github.com/VictoriaMetrics/easyproto v1.2.0
github.com/VictoriaMetrics/fastcache v1.13.3

4
go.sum
View File

@@ -52,8 +52,8 @@ github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapp
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.55.0/go.mod h1:Mf6O40IAyB9zR/1J8nGDDPirZQQPbYJni8Yisy7NTMc=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/RoaringBitmap/roaring/v2 v2.14.0 h1:NjlfrI3SmA9Zm5yM1FV+IR096NyVt2R8wRp56y6I8zU=
github.com/RoaringBitmap/roaring/v2 v2.14.0/go.mod h1:oMvV6omPWr+2ifRdeZvVJyaz+aoEUopyv5iH0u/+wbY=
github.com/RoaringBitmap/roaring/v2 v2.14.4 h1:4aKySrrg9G/5oRtJ3TrZLObVqxgQ9f1znCRBwEwjuVw=
github.com/RoaringBitmap/roaring/v2 v2.14.4/go.mod h1:oMvV6omPWr+2ifRdeZvVJyaz+aoEUopyv5iH0u/+wbY=
github.com/VictoriaMetrics/VictoriaLogs v0.0.0-20260218111324-95b48d57d032 h1:kKVeXC+HAcMeMLefoKCWf934y9MoLU8V3Da7k6WP4K8=
github.com/VictoriaMetrics/VictoriaLogs v0.0.0-20260218111324-95b48d57d032/go.mod h1:WQ8hGgfKx1lXCCcS1SJSOklN9fToSbshtvKHp3xsv4w=
github.com/VictoriaMetrics/easyproto v1.2.0 h1:FJT9uNXA2isppFuJErbLqD306KoFlehl7Wn2dg/6oIE=

View File

@@ -62,6 +62,10 @@ func (ac *arrayContainer) getManyIterator() manyIterable {
return &shortIterator{ac.content, 0}
}
func (ac *arrayContainer) getUnsetIterator() shortPeekable {
return newArrayContainerUnsetIterator(ac.content)
}
func (ac *arrayContainer) minimum() uint16 {
return ac.content[0] // assume not empty
}

View File

@@ -262,6 +262,39 @@ func (bc *bitmapContainer) getManyIterator() manyIterable {
return newBitmapContainerManyIterator(bc)
}
type bitmapContainerUnsetIterator struct {
ptr *bitmapContainer
i int
}
func (bcui *bitmapContainerUnsetIterator) next() uint16 {
j := bcui.i
bcui.i = bcui.ptr.NextUnsetBit(uint(bcui.i) + 1)
return uint16(j)
}
func (bcui *bitmapContainerUnsetIterator) hasNext() bool {
return bcui.i >= 0 && bcui.i < 65536
}
func (bcui *bitmapContainerUnsetIterator) peekNext() uint16 {
return uint16(bcui.i)
}
func (bcui *bitmapContainerUnsetIterator) advanceIfNeeded(minval uint16) {
if bcui.hasNext() && bcui.peekNext() < minval {
bcui.i = bcui.ptr.NextUnsetBit(uint(minval))
}
}
func newBitmapContainerUnsetIterator(a *bitmapContainer) *bitmapContainerUnsetIterator {
return &bitmapContainerUnsetIterator{a, a.NextUnsetBit(0)}
}
func (bc *bitmapContainer) getUnsetIterator() shortPeekable {
return newBitmapContainerUnsetIterator(bc)
}
func (bc *bitmapContainer) getSizeInBytes() int {
return len(bc.bitmap) * 8
}
@@ -1113,6 +1146,29 @@ func (bc *bitmapContainer) NextSetBit(i uint) int {
return -1
}
func (bc *bitmapContainer) NextUnsetBit(i uint) int {
var (
x = i / 64
length = uint(len(bc.bitmap))
)
if x >= length {
return int(i)
}
w := bc.bitmap[x]
w = w >> uint(i%64)
w = ^w
if w != 0 {
return int(i) + countTrailingZeros(w)
}
x++
for ; x < length; x++ {
if bc.bitmap[x] != 0xFFFFFFFFFFFFFFFF {
return int(x*64) + countTrailingZeros(^bc.bitmap[x])
}
}
return int(length * 64)
}
// PrevSetBit returns the previous set bit e.g the previous int packed into the bitmaparray
func (bc *bitmapContainer) PrevSetBit(i int) int {
if i < 0 {

View File

@@ -1,9 +1,11 @@
package roaring
import "iter"
// Values returns an iterator that yields the elements of the bitmap in
// increasing order. Starting with Go 1.23, users can use a for loop to iterate
// over it.
func Values(b *Bitmap) func(func(uint32) bool) {
func Values(b *Bitmap) iter.Seq[uint32] {
return func(yield func(uint32) bool) {
it := b.Iterator()
for it.HasNext() {
@@ -17,7 +19,7 @@ func Values(b *Bitmap) func(func(uint32) bool) {
// Backward returns an iterator that yields the elements of the bitmap in
// decreasing order. Starting with Go 1.23, users can use a for loop to iterate
// over it.
func Backward(b *Bitmap) func(func(uint32) bool) {
func Backward(b *Bitmap) iter.Seq[uint32] {
return func(yield func(uint32) bool) {
it := b.ReverseIterator()
for it.HasNext() {
@@ -30,9 +32,9 @@ func Backward(b *Bitmap) func(func(uint32) bool) {
// Unset creates an iterator that yields values in the range [min, max] that are NOT contained in the bitmap.
// The iterator becomes invalid if the bitmap is modified (e.g., with Add or Remove).
func Unset(b *Bitmap, min, max uint32) func(func(uint32) bool) {
func Unset(b *Bitmap, min, max uint32) iter.Seq[uint32] {
return func(yield func(uint32) bool) {
it := b.UnsetIterator(min, max)
it := b.UnsetIterator(uint64(min), uint64(max)+1)
for it.HasNext() {
if !yield(it.Next()) {
return

View File

@@ -743,94 +743,179 @@ func (ii *manyIntIterator) Initialize(a *Bitmap) {
}
type unsetIterator struct {
min, max uint32
current uint64 // use uint64 to avoid overflow
it IntPeekable
hasNext bool
containerIndex int
nextKey int
hs uint32
iter shortPeekable
highlowcontainer *roaringArray
arrayUnsetIter arrayContainerUnsetIterator
runUnsetIter runUnsetIterator16
bitmapUnsetIter bitmapContainerUnsetIterator
emptyContainerVal uint16
start, end uint64
}
// Initialize configures the unset iterator to iterate over values in [min, max] that are not in the bitmap
func (ui *unsetIterator) Initialize(b *Bitmap, min, max uint32) {
ui.min = min
ui.max = max
ui.current = uint64(min)
ui.it = b.Iterator()
// Advance to first value >= min
ui.it.AdvanceIfNeeded(min)
ui.updateHasNext()
}
func (ui *unsetIterator) HasNext() bool {
return ui.hasNext
}
func (ui *unsetIterator) Next() uint32 {
if !ui.hasNext {
panic("Next() called when HasNext() returns false")
}
result := ui.current
ui.current++
ui.updateHasNext()
return uint32(result)
}
func (ui *unsetIterator) updateHasNext() {
for ui.current <= uint64(ui.max) {
if !ui.it.HasNext() {
// No more set bits, we have values to yield
ui.hasNext = true
return
// HasNext returns true if there are more integers to iterate over
func (iui *unsetIterator) HasNext() bool {
// Skip containers that have no unset bits in our range
for iui.nextKey < 65536 && uint64(iui.nextKey)<<16 < iui.end {
if iui.iter == nil {
// We're in an empty container gap, which has unset bits
if uint64(iui.nextKey)<<16|uint64(iui.emptyContainerVal) < iui.end {
return true
}
// Move to next container
iui.nextKey++
iui.containerIndex++
iui.init()
continue
}
nextSet := ui.it.PeekNext()
if nextSet > ui.max {
// Next set bit is beyond our range, we have values to yield
ui.hasNext = true
return
if iui.iter.hasNext() {
// Check if next value is within range
nextVal := (uint64(iui.nextKey) << 16) | uint64(iui.iter.peekNext())
if nextVal < iui.end {
return true
}
}
if ui.current < uint64(nextSet) {
// We have unset values before the next set bit
ui.hasNext = true
return
}
// Skip the set bit
ui.it.Next()
ui.current = uint64(nextSet) + 1
// Current container has no more unset bits in range, move to next
iui.nextKey++
iui.containerIndex++
iui.init()
}
ui.hasNext = false
return false
}
// PeekNext returns the next value without advancing the iterator
func (ui *unsetIterator) PeekNext() uint32 {
if !ui.hasNext {
panic("PeekNext() called when HasNext() returns false")
}
return uint32(ui.current)
}
// AdvanceIfNeeded advances the iterator so that the next value is at least minval
func (ui *unsetIterator) AdvanceIfNeeded(minval uint32) {
if minval <= ui.min {
return // Already at or before the start of our range
}
if minval > ui.max {
// Beyond our range, no more values
ui.hasNext = false
func (iui *unsetIterator) init() {
// Check if we've gone past the end range
if uint64(iui.nextKey)<<16 >= iui.end {
iui.iter = nil
return
}
// Set current to minval, but make sure we skip any set bits
ui.current = uint64(minval)
// Check if we're in an empty container gap
if iui.containerIndex >= iui.highlowcontainer.size() ||
iui.highlowcontainer.getKeyAtIndex(iui.containerIndex) > uint16(iui.nextKey) {
// We're in a gap - iterate through empty container
iui.emptyContainerVal = 0
// If this container overlaps with start, advance to start
if uint64(iui.nextKey)<<16 < iui.start && iui.start < uint64(iui.nextKey+1)<<16 {
iui.emptyContainerVal = uint16(iui.start)
}
iui.iter = nil
return
}
// Advance the internal iterator to be at or beyond minval
ui.it.AdvanceIfNeeded(minval)
// We're in an actual container
iui.hs = uint32(iui.nextKey) << 16
c := iui.highlowcontainer.getContainerAtIndex(iui.containerIndex)
switch t := c.(type) {
case *arrayContainer:
iui.arrayUnsetIter = *newArrayContainerUnsetIterator(t.content)
iui.iter = &iui.arrayUnsetIter
case *runContainer16:
iui.runUnsetIter = *t.newRunUnsetIterator16()
iui.iter = &iui.runUnsetIter
case *bitmapContainer:
iui.bitmapUnsetIter = *newBitmapContainerUnsetIterator(t)
iui.iter = &iui.bitmapUnsetIter
}
ui.updateHasNext()
// If this container overlaps with start, advance to the low bits of start
if uint64(iui.nextKey)<<16 < iui.start && iui.start < uint64(iui.nextKey+1)<<16 {
iui.iter.advanceIfNeeded(uint16(iui.start))
}
}
// Next returns the next integer
func (iui *unsetIterator) Next() uint32 {
if iui.iter == nil {
// We're in an empty container gap
x := (uint32(iui.nextKey) << 16) | uint32(iui.emptyContainerVal)
iui.emptyContainerVal++
if iui.emptyContainerVal == 0 || uint64(iui.nextKey)<<16|uint64(iui.emptyContainerVal) >= iui.end {
// Wrapped around or reached end, move to next container
iui.nextKey++
iui.init()
}
return x
}
x := uint32(iui.iter.next()) | iui.hs
if !iui.iter.hasNext() || uint64(iui.nextKey)<<16|uint64(iui.iter.peekNext()) >= iui.end {
iui.nextKey++
iui.containerIndex++
iui.init()
}
return x
}
// PeekNext peeks the next value without advancing the iterator
func (iui *unsetIterator) PeekNext() uint32 {
if !iui.HasNext() {
panic("PeekNext() called when HasNext() returns false")
}
if iui.iter == nil {
return (uint32(iui.nextKey) << 16) | uint32(iui.emptyContainerVal)
}
return uint32(iui.iter.peekNext()&maxLowBit) | iui.hs
}
// AdvanceIfNeeded advances as long as the next value is smaller than minval
func (iui *unsetIterator) AdvanceIfNeeded(minval uint32) {
targetKey := int(minval >> 16)
for iui.HasNext() && iui.nextKey < targetKey {
iui.nextKey++
// Find the next container that matches or exceeds nextKey
for iui.containerIndex < iui.highlowcontainer.size() &&
int(iui.highlowcontainer.getKeyAtIndex(iui.containerIndex)) < iui.nextKey {
iui.containerIndex++
}
iui.init()
}
if iui.HasNext() && iui.nextKey == targetKey {
if iui.iter != nil {
iui.iter.advanceIfNeeded(lowbits(minval))
if !iui.iter.hasNext() || uint64(iui.nextKey)<<16|uint64(iui.iter.peekNext()) >= iui.end {
iui.nextKey++
iui.containerIndex++
iui.init()
}
} else {
lowVal := lowbits(minval)
if iui.emptyContainerVal < lowVal {
iui.emptyContainerVal = lowVal
}
if uint64(iui.nextKey)<<16|uint64(iui.emptyContainerVal) >= iui.end {
iui.nextKey++
iui.containerIndex++
iui.init()
}
}
}
}
// Initialize configures the unset iterator to iterate over values in [start, end) that are not in the bitmap
func (iui *unsetIterator) Initialize(a *Bitmap, start, end uint64) {
if end > 0x100000000 {
panic("end > 0x100000000")
}
iui.start = start
iui.end = end
iui.containerIndex = 0
iui.nextKey = int(start >> 16)
iui.highlowcontainer = &a.highlowcontainer
// Find the first container that matches or exceeds the start key
for iui.containerIndex < iui.highlowcontainer.size() &&
int(iui.highlowcontainer.getKeyAtIndex(iui.containerIndex)) < iui.nextKey {
iui.containerIndex++
}
iui.init()
}
// String creates a string representation of the Bitmap
@@ -915,11 +1000,11 @@ func (rb *Bitmap) ManyIterator() ManyIntIterable {
return p
}
// UnsetIterator creates a new IntPeekable to iterate over values in the range [min, max] that are NOT contained in the bitmap.
// UnsetIterator creates a new IntPeekable to iterate over values in the range [start, end) that are NOT contained in the bitmap.
// The iterator becomes invalid if the bitmap is modified (e.g., with Add or Remove).
func (rb *Bitmap) UnsetIterator(min, max uint32) IntPeekable {
func (rb *Bitmap) UnsetIterator(start, end uint64) IntPeekable {
p := new(unsetIterator)
p.Initialize(rb, min, max)
p.Initialize(rb, start, end)
return p
}

View File

@@ -1,9 +1,11 @@
package roaring64
import "iter"
// Values returns an iterator that yields the elements of the bitmap in
// increasing order. Starting with Go 1.23, users can use a for loop to iterate
// over it.
func Values(b *Bitmap) func(func(uint64) bool) {
func Values(b *Bitmap) iter.Seq[uint64] {
return func(yield func(uint64) bool) {
it := b.Iterator()
for it.HasNext() {
@@ -17,7 +19,7 @@ func Values(b *Bitmap) func(func(uint64) bool) {
// Backward returns an iterator that yields the elements of the bitmap in
// decreasing order. Starting with Go 1.23, users can use a for loop to iterate
// over it.
func Backward(b *Bitmap) func(func(uint64) bool) {
func Backward(b *Bitmap) iter.Seq[uint64] {
return func(yield func(uint64) bool) {
it := b.ReverseIterator()
for it.HasNext() {

View File

@@ -40,6 +40,7 @@ type container interface {
inot(firstOfRange, endx int) container // i stands for inplace, range is [firstOfRange,endx)
xor(r container) container
getShortIterator() shortPeekable
getUnsetIterator() shortPeekable
iterate(cb func(x uint16) bool) bool
getReverseIterator() shortIterable
getManyIterator() manyIterable

View File

@@ -1980,6 +1980,61 @@ func (rc *runContainer16) getManyIterator() manyIterable {
return rc.newManyRunIterator16()
}
type runUnsetIterator16 struct {
rc *runContainer16
curIndex int
nextVal int
}
func (rc *runContainer16) newRunUnsetIterator16() *runUnsetIterator16 {
rui := &runUnsetIterator16{rc: rc, curIndex: 0, nextVal: 0}
if len(rc.iv) > 0 && rc.iv[0].start == 0 {
rui.nextVal = int(rc.iv[0].start) + int(rc.iv[0].length) + 1
rui.curIndex = 1
}
return rui
}
func (rui *runUnsetIterator16) hasNext() bool {
return rui.nextVal < 65536
}
func (rui *runUnsetIterator16) next() uint16 {
val := rui.nextVal
rui.nextVal++
if rui.curIndex < len(rui.rc.iv) && uint16(rui.nextVal) >= rui.rc.iv[rui.curIndex].start {
rui.nextVal = int(rui.rc.iv[rui.curIndex].start) + int(rui.rc.iv[rui.curIndex].length) + 1
rui.curIndex++
}
return uint16(val)
}
func (rui *runUnsetIterator16) peekNext() uint16 {
return uint16(rui.nextVal)
}
func (rui *runUnsetIterator16) advanceIfNeeded(minval uint16) {
if !rui.hasNext() || rui.peekNext() >= minval {
return
}
rui.nextVal = int(minval)
for rui.curIndex < len(rui.rc.iv) {
if rui.rc.iv[rui.curIndex].start+rui.rc.iv[rui.curIndex].length < minval {
rui.curIndex++
} else if rui.rc.iv[rui.curIndex].start <= minval {
rui.nextVal = int(rui.rc.iv[rui.curIndex].start) + int(rui.rc.iv[rui.curIndex].length) + 1
rui.curIndex++
break
} else {
break
}
}
}
func (rc *runContainer16) getUnsetIterator() shortPeekable {
return rc.newRunUnsetIterator16()
}
// add the values in the range [firstOfRange, endx). endx
// is still abe to express 2^16 because it is an int not an uint16.
func (rc *runContainer16) iaddRange(firstOfRange, endx int) container {

View File

@@ -50,3 +50,53 @@ func (si *reverseIterator) next() uint16 {
si.loc--
return a
}
type arrayContainerUnsetIterator struct {
content []uint16
// pos is the index of the next set bit that is >= nextVal.
// When nextVal reaches content[pos], pos is incremented.
pos int
nextVal int
}
func (acui *arrayContainerUnsetIterator) next() uint16 {
val := acui.nextVal
acui.nextVal++
for acui.pos < len(acui.content) && uint16(acui.nextVal) >= acui.content[acui.pos] {
acui.nextVal++
acui.pos++
}
return uint16(val)
}
func (acui *arrayContainerUnsetIterator) hasNext() bool {
return acui.nextVal < 65536
}
func (acui *arrayContainerUnsetIterator) peekNext() uint16 {
return uint16(acui.nextVal)
}
func (acui *arrayContainerUnsetIterator) advanceIfNeeded(minval uint16) {
if !acui.hasNext() || acui.peekNext() >= minval {
return
}
acui.nextVal = int(minval)
acui.pos = binarySearch(acui.content, minval)
if acui.pos < 0 {
acui.pos = -acui.pos - 1
}
for acui.pos < len(acui.content) && uint16(acui.nextVal) >= acui.content[acui.pos] {
acui.nextVal++
acui.pos++
}
}
func newArrayContainerUnsetIterator(content []uint16) *arrayContainerUnsetIterator {
acui := &arrayContainerUnsetIterator{content: content, pos: 0, nextVal: 0}
for acui.pos < len(acui.content) && uint16(acui.nextVal) >= acui.content[acui.pos] {
acui.nextVal++
acui.pos++
}
return acui
}

View File

@@ -78,8 +78,6 @@ type actionRecord struct {
PairSnapshots []string // base64-encoded MarshalBinary of each pair's Bitmap
}
var ()
type smatPair struct {
bm *Bitmap
bs *bitset.BitSet

2
vendor/modules.txt vendored
View File

@@ -132,7 +132,7 @@ github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric
# github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.55.0
## explicit; go 1.24.0
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping
# github.com/RoaringBitmap/roaring/v2 v2.14.0
# github.com/RoaringBitmap/roaring/v2 v2.14.4
## explicit; go 1.24.0
github.com/RoaringBitmap/roaring/v2
github.com/RoaringBitmap/roaring/v2/internal