Files
opencloud/vendor/github.com/RoaringBitmap/roaring/v2/smat.go
dependabot[bot] 2b0d61acf5 build(deps): bump github.com/blevesearch/bleve/v2 from 2.5.7 to 2.6.0
Bumps [github.com/blevesearch/bleve/v2](https://github.com/blevesearch/bleve) from 2.5.7 to 2.6.0.
- [Release notes](https://github.com/blevesearch/bleve/releases)
- [Commits](https://github.com/blevesearch/bleve/compare/v2.5.7...v2.6.0)

---
updated-dependencies:
- dependency-name: github.com/blevesearch/bleve/v2
  dependency-version: 2.6.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-05-20 00:37:30 +00:00

718 lines
22 KiB
Go

/*
# Instructions for smat testing for roaring
[smat](https://github.com/mschoch/smat) is a framework that provides
state machine assisted fuzz testing.
To run the smat tests for roaring...
## Prerequisites
Go 1.18 or later (for native fuzzing support).
## Steps
1. Generate initial smat corpus:
```
go test -tags=gofuzz -run=TestGenerateSmatCorpus
```
You should see a directory `workdir` created with initial corpus files.
2. Run the fuzz test:
```
go test -run='^$' -fuzz=FuzzSmat -fuzztime=300s -timeout=60s
```
Adjust `-fuzztime` as needed for longer or shorter runs. If crashes are found,
check the test output and the reproducer files in the `workdir` directory.
You may copy the reproducers to roaring_tests.go
*/
package roaring
import (
"encoding/base64"
"fmt"
"os"
"path/filepath"
"runtime/debug"
"slices"
"strings"
"time"
"github.com/bits-and-blooms/bitset"
"github.com/mschoch/smat"
)
// The native fuzz entry point lives in a _test.go file so the go test
// fuzz engine discovers it. See smat_fuzz_test.go for the fuzz wrapper.
var smatDebug = true
const max_value = 1048576
const max_pairs = 10
func smatLog(prefix, format string, args ...interface{}) {
if smatDebug {
fmt.Print(prefix)
fmt.Printf(format, args...)
}
}
type smatContext struct {
pairs []*smatPair
// Two registers, x & y.
x int
y int
actions int
// per-context last action for this fuzz worker
lastAction *actionRecord
}
// actionRecord stores a snapshot of the state just before an action runs.
type actionRecord struct {
Name string
X, Y int
PairSnapshots []string // base64-encoded MarshalBinary of each pair's Bitmap
}
type smatPair struct {
bm *Bitmap
bs *bitset.BitSet
// parent context (nil if unknown)
ctx *smatContext
}
// ------------------------------------------------------------------
var smatActionMap = smat.ActionMap{
smat.ActionID('X'): smatAction("x++", smatWrap(func(c *smatContext) { c.x = (c.x + 1) % max_value })),
smat.ActionID('x'): smatAction("x--", smatWrap(func(c *smatContext) { c.x = (c.x - 1 + max_value) % max_value })),
smat.ActionID('Y'): smatAction("y++", smatWrap(func(c *smatContext) { c.y = (c.y + 1) % max_value })),
smat.ActionID('y'): smatAction("y--", smatWrap(func(c *smatContext) { c.y = (c.y - 1 + max_value) % max_value })),
smat.ActionID('*'): smatAction("x*y", smatWrap(func(c *smatContext) { c.x = (c.x * c.y) % max_value })),
smat.ActionID('<'): smatAction("x<<", smatWrap(func(c *smatContext) { c.x = (c.x << 1) % max_value })),
smat.ActionID('^'): smatAction("swap", smatWrap(func(c *smatContext) { c.x, c.y = c.y, c.x })),
smat.ActionID('['): smatAction(" pushPair", smatWrap(smatPushPair)),
smat.ActionID(']'): smatAction(" popPair", smatWrap(smatPopPair)),
smat.ActionID('B'): smatAction(" setBit", smatWrap(smatSetBit)),
smat.ActionID('b'): smatAction(" removeBit", smatWrap(smatRemoveBit)),
smat.ActionID('o'): smatAction(" or", smatWrap(smatOr)),
smat.ActionID('a'): smatAction(" and", smatWrap(smatAnd)),
smat.ActionID('z'): smatAction(" xor", smatWrap(smatXor)),
smat.ActionID('#'): smatAction(" cardinality", smatWrap(smatCardinality)),
smat.ActionID('O'): smatAction(" orCardinality", smatWrap(smatOrCardinality)),
smat.ActionID('A'): smatAction(" andCardinality", smatWrap(smatAndCardinality)),
smat.ActionID('Z'): smatAction(" xorCardinality", smatWrap(smatXorCardinality)),
smat.ActionID('c'): smatAction(" clear", smatWrap(smatClear)),
smat.ActionID('r'): smatAction(" runOptimize", smatWrap(smatRunOptimize)),
smat.ActionID('e'): smatAction(" isEmpty", smatWrap(smatIsEmpty)),
smat.ActionID('i'): smatAction(" intersects", smatWrap(smatIntersects)),
smat.ActionID('f'): smatAction(" flip", smatWrap(smatFlip)),
smat.ActionID('-'): smatAction(" difference", smatWrap(smatDifference)),
}
var smatRunningPercentActions []smat.PercentAction
func init() {
var ids []int
for actionId := range smatActionMap {
ids = append(ids, int(actionId))
}
slices.Sort(ids)
pct := 100 / len(smatActionMap)
for _, actionId := range ids {
smatRunningPercentActions = append(smatRunningPercentActions,
smat.PercentAction{Percent: pct, Action: smat.ActionID(actionId)})
}
smatActionMap[smat.ActionID('S')] = smatAction("SETUP", smatSetupFunc)
smatActionMap[smat.ActionID('T')] = smatAction("TEARDOWN", smatTeardownFunc)
}
// We only have one smat state: running.
func smatRunning(next byte) smat.ActionID {
return smat.PercentExecute(next, smatRunningPercentActions...)
}
func smatAction(name string, f func(ctx smat.Context) (smat.State, error)) func(smat.Context) (smat.State, error) {
return func(ctx smat.Context) (smat.State, error) {
c := ctx.(*smatContext)
// Snapshot all pairs' bitmaps (base64 of MarshalBinary) before action
rec := actionRecord{Name: name, X: c.x, Y: c.y}
if len(c.pairs) > 0 {
rec.PairSnapshots = make([]string, 0, len(c.pairs))
for _, pair := range c.pairs {
if pair == nil || pair.bm == nil {
rec.PairSnapshots = append(rec.PairSnapshots, "<nil>")
continue
}
b, err := pair.bm.MarshalBinary()
if err != nil {
rec.PairSnapshots = append(rec.PairSnapshots, "<marshal-error:"+err.Error()+">")
} else {
rec.PairSnapshots = append(rec.PairSnapshots, base64.StdEncoding.EncodeToString(b))
}
}
}
// record per-context last action (no global mutex required)
if c != nil {
c.lastAction = &rec
}
// catch panics inside action to dump a repro and stack before re-panicking
defer func() {
if r := recover(); r != nil {
// best-effort: write quick repro with lastAction from context
var lastAction *actionRecord
if c != nil {
lastAction = c.lastAction
}
ts := time.Now().UnixNano()
repro := "// Reproducer generated by smat (panic)\n"
repro += "package roaring\n\n"
repro += "import (\n\t\"encoding/base64\"\n\t\"testing\"\n)\n\n"
repro += fmt.Sprintf("func TestFuzzerPanicRepro_%d(t *testing.T) {\n", ts)
// similar to checkEquals repro
if lastAction != nil && len(lastAction.PairSnapshots) > 0 {
pairIndex := lastAction.X % len(lastAction.PairSnapshots)
if pairIndex < len(lastAction.PairSnapshots) {
snapshot := lastAction.PairSnapshots[pairIndex]
if snapshot != "<nil>" && !strings.HasPrefix(snapshot, "<") {
repro += fmt.Sprintf("\tb, _ := base64.StdEncoding.DecodeString(\"%s\")\n", snapshot)
repro += "\tbm := NewBitmap()\n"
repro += "\tbm.UnmarshalBinary(b)\n"
// perform the action that caused panic
if strings.Contains(lastAction.Name, "setBit") {
repro += fmt.Sprintf("\tbm.AddInt(%d)\n", lastAction.Y)
} else if strings.Contains(lastAction.Name, "removeBit") {
repro += fmt.Sprintf("\tbm.Remove(%d)\n", lastAction.Y)
} else if strings.Contains(lastAction.Name, "flip") {
repro += fmt.Sprintf("\tbm.Flip(uint64(%d), uint64(%d)+1)\n", lastAction.Y, lastAction.Y)
} else if strings.Contains(lastAction.Name, "runOptimize") {
repro += "\tbm.RunOptimize()\n"
} else if strings.Contains(lastAction.Name, "clear") {
repro += "\tbm.Clear()\n"
} else if lastAction.Name == " or" {
pairIndexY := lastAction.Y % len(lastAction.PairSnapshots)
if pairIndexY < len(lastAction.PairSnapshots) {
snapshotY := lastAction.PairSnapshots[pairIndexY]
if snapshotY != "<nil>" && !strings.HasPrefix(snapshotY, "<") {
repro += fmt.Sprintf("\tb2, _ := base64.StdEncoding.DecodeString(\"%s\")\n", snapshotY)
repro += "\tbm2 := NewBitmap()\n"
repro += "\tbm2.UnmarshalBinary(b2)\n"
repro += "\tbm.Or(bm2)\n"
}
}
} else if lastAction.Name == " and" {
pairIndexY := lastAction.Y % len(lastAction.PairSnapshots)
if pairIndexY < len(lastAction.PairSnapshots) {
snapshotY := lastAction.PairSnapshots[pairIndexY]
if snapshotY != "<nil>" && !strings.HasPrefix(snapshotY, "<") {
repro += fmt.Sprintf("\tb2, _ := base64.StdEncoding.DecodeString(\"%s\")\n", snapshotY)
repro += "\tbm2 := NewBitmap()\n"
repro += "\tbm2.UnmarshalBinary(b2)\n"
repro += "\tbm.And(bm2)\n"
}
}
} else if lastAction.Name == " difference" {
pairIndexY := lastAction.Y % len(lastAction.PairSnapshots)
if pairIndexY < len(lastAction.PairSnapshots) {
snapshotY := lastAction.PairSnapshots[pairIndexY]
if snapshotY != "<nil>" && !strings.HasPrefix(snapshotY, "<") {
repro += fmt.Sprintf("\tb2, _ := base64.StdEncoding.DecodeString(\"%s\")\n", snapshotY)
repro += "\tbm2 := NewBitmap()\n"
repro += "\tbm2.UnmarshalBinary(b2)\n"
repro += "\tbm.AndNot(bm2)\n"
}
}
} else if lastAction.Name == " xor" {
pairIndexY := lastAction.Y % len(lastAction.PairSnapshots)
if pairIndexY < len(lastAction.PairSnapshots) {
snapshotY := lastAction.PairSnapshots[pairIndexY]
if snapshotY != "<nil>" && !strings.HasPrefix(snapshotY, "<") {
repro += fmt.Sprintf("\tb2, _ := base64.StdEncoding.DecodeString(\"%s\")\n", snapshotY)
repro += "\tbm2 := NewBitmap()\n"
repro += "\tbm2.UnmarshalBinary(b2)\n"
repro += "\tbm.Xor(bm2)\n"
}
}
} else {
repro += fmt.Sprintf("\t// Unhandled action: %s\n", lastAction.Name)
}
} else {
repro += "\t// invalid snapshot\n"
}
}
}
repro += "}\n"
if path, werr := saveReproFile("smat_panic_repro", ts, repro); werr == nil {
fmt.Printf("wrote panic repro to %s\n", path)
} else {
fmt.Printf("failed writing panic repro: %v\n", werr)
}
fmt.Printf("PANIC in action %s: %v\n", rec.Name, r)
fmt.Printf("stack:\n%s\n", debug.Stack())
panic(r)
}
}()
c.actions++
return f(ctx)
}
}
// saveReproFile writes the given repro content to workdir/<prefix>_<ts>_test.go
// or falls back to the OS temp dir. Returns full path or error.
func saveReproFile(prefix string, ts int64, content string) (string, error) {
// try workdir
if err := os.MkdirAll("workdir", 0o755); err == nil {
fname := fmt.Sprintf("workdir/%s_%d_test.go", prefix, ts)
if err := os.WriteFile(fname, []byte(content), 0o644); err == nil {
return fname, nil
}
}
// fallback to temp
tmp := os.TempDir()
fname := fmt.Sprintf("%s_%d_test.go", prefix, ts)
full := filepath.Join(tmp, fname)
if err := os.WriteFile(full, []byte(content), 0o644); err == nil {
return full, nil
} else {
return "", err
}
}
// Creates an smat action func based on a simple callback.
func smatWrap(cb func(c *smatContext)) func(smat.Context) (next smat.State, err error) {
return func(ctx smat.Context) (next smat.State, err error) {
c := ctx.(*smatContext)
cb(c)
return smatRunning, nil
}
}
// Invokes a callback function with the input v bounded to len(c.pairs).
func (c *smatContext) withPair(v int, cb func(*smatPair)) {
if len(c.pairs) > 0 {
if v < 0 {
v = -v
}
v = v % len(c.pairs)
cb(c.pairs[v])
}
}
// ------------------------------------------------------------------
func smatSetupFunc(ctx smat.Context) (next smat.State, err error) {
return smatRunning, nil
}
func smatTeardownFunc(ctx smat.Context) (next smat.State, err error) {
return nil, err
}
// ------------------------------------------------------------------
func smatPushPair(c *smatContext) {
if len(c.pairs) >= max_pairs {
return
}
p := &smatPair{
bm: NewBitmap(),
bs: bitset.New(100),
ctx: c,
}
c.pairs = append(c.pairs, p)
}
func smatPopPair(c *smatContext) {
if len(c.pairs) > 0 {
c.pairs = c.pairs[0 : len(c.pairs)-1]
}
}
func smatSetBit(c *smatContext) {
c.withPair(c.x, func(p *smatPair) {
p.Validate()
y := uint32(c.y)
p.bm.AddInt(int(y))
p.bs.Set(uint(y))
p.checkEquals()
})
}
func smatRemoveBit(c *smatContext) {
c.withPair(c.x, func(p *smatPair) {
p.Validate()
y := uint32(c.y)
p.bm.Remove(y)
p.bs.Clear(uint(y))
p.checkEquals()
})
}
func smatAnd(c *smatContext) {
c.withPair(c.x, func(px *smatPair) {
c.withPair(c.y, func(py *smatPair) {
px.Validate()
py.Validate()
px.bm.And(py.bm)
px.bs = px.bs.Intersection(py.bs)
px.checkEquals()
py.checkEquals()
})
})
}
func smatOr(c *smatContext) {
c.withPair(c.x, func(px *smatPair) {
c.withPair(c.y, func(py *smatPair) {
px.Validate()
py.Validate()
px.bm.Or(py.bm)
px.bs = px.bs.Union(py.bs)
px.checkEquals()
py.checkEquals()
})
})
}
func smatXor(c *smatContext) {
c.withPair(c.x, func(px *smatPair) {
c.withPair(c.y, func(py *smatPair) {
px.Validate()
py.Validate()
px.bm.Xor(py.bm)
px.bs = px.bs.SymmetricDifference(py.bs)
px.checkEquals()
py.checkEquals()
})
})
}
func smatAndCardinality(c *smatContext) {
c.withPair(c.x, func(px *smatPair) {
c.withPair(c.y, func(py *smatPair) {
px.Validate()
py.Validate()
c0 := px.bm.AndCardinality(py.bm)
c1 := px.bs.IntersectionCardinality(py.bs)
if c0 != uint64(c1) {
panic("expected same add cardinality")
}
px.checkEquals()
py.checkEquals()
})
})
}
func smatOrCardinality(c *smatContext) {
c.withPair(c.x, func(px *smatPair) {
c.withPair(c.y, func(py *smatPair) {
px.Validate()
py.Validate()
c0 := px.bm.OrCardinality(py.bm)
c1 := px.bs.UnionCardinality(py.bs)
if c0 != uint64(c1) {
panic("expected same or cardinality")
}
px.checkEquals()
py.checkEquals()
})
})
}
func smatXorCardinality(c *smatContext) {
c.withPair(c.x, func(px *smatPair) {
c.withPair(c.y, func(py *smatPair) {
px.Validate()
py.Validate()
c0 := px.bm.OrCardinality(py.bm) - px.bm.AndCardinality(py.bm)
c1 := px.bs.SymmetricDifferenceCardinality(py.bs)
if c0 != uint64(c1) {
panic("expected same xor cardinality")
}
px.checkEquals()
py.checkEquals()
})
})
}
func smatRunOptimize(c *smatContext) {
c.withPair(c.x, func(px *smatPair) {
px.Validate()
px.bm.RunOptimize()
px.checkEquals()
})
}
func smatClear(c *smatContext) {
c.withPair(c.x, func(px *smatPair) {
px.Validate()
px.bm.Clear()
px.bs = px.bs.ClearAll()
px.checkEquals()
})
}
func smatCardinality(c *smatContext) {
c.withPair(c.x, func(px *smatPair) {
c0 := px.bm.GetCardinality()
c1 := px.bs.Count()
if c0 != uint64(c1) {
panic("expected same cardinality")
}
})
}
func smatIsEmpty(c *smatContext) {
c.withPair(c.x, func(px *smatPair) {
c0 := px.bm.IsEmpty()
c1 := px.bs.None()
if c0 != c1 {
panic("expected same is empty")
}
})
}
func smatIntersects(c *smatContext) {
c.withPair(c.x, func(px *smatPair) {
c.withPair(c.y, func(py *smatPair) {
px.Validate()
py.Validate()
v0 := px.bm.Intersects(py.bm)
v1 := px.bs.IntersectionCardinality(py.bs) > 0
if v0 != v1 {
panic("intersects not equal")
}
px.checkEquals()
py.checkEquals()
})
})
}
func smatFlip(c *smatContext) {
c.withPair(c.x, func(p *smatPair) {
p.Validate()
y := uint32(c.y)
p.bm.Flip(uint64(y), uint64(y)+1)
p.bs = p.bs.Flip(uint(y))
p.checkEquals()
})
}
func smatDifference(c *smatContext) {
c.withPair(c.x, func(px *smatPair) {
c.withPair(c.y, func(py *smatPair) {
px.Validate()
py.Validate()
px.bm.AndNot(py.bm)
px.bs = px.bs.Difference(py.bs)
px.checkEquals()
py.checkEquals()
})
})
}
func (p *smatPair) checkEquals() {
valid := p.bm.Validate()
if valid != nil {
// marshal current bitmap
var curSnap string
if p != nil && p.bm != nil {
if b, err := p.bm.MarshalBinary(); err == nil {
curSnap = base64.StdEncoding.EncodeToString(b)
} else {
curSnap = "<marshal-error:" + err.Error() + ">"
}
} else {
curSnap = "<nil>"
}
// collect last action summary from context (per-worker)
last := "<none>"
if p != nil && p.ctx != nil {
c := p.ctx
if c.lastAction != nil {
last = fmt.Sprintf("action=%s x=%d y=%d pairs=%d", c.lastAction.Name, c.lastAction.X, c.lastAction.Y, len(c.lastAction.PairSnapshots))
}
}
// If debugging enabled, log extra info
smatLog("ERROR: ", "bitmap invalid: %v\n", valid)
// build a reproducible test snippet that reconstructs the bitmap and replays the failing action
ts := time.Now().UnixNano()
testName := fmt.Sprintf("TestFuzzerRepro_%d", ts)
repro := "// Reproducer generated by smat\n"
repro += "package roaring\n\n"
repro += "import (\n\t\"encoding/base64\"\n\t\"testing\"\n)\n\n"
repro += fmt.Sprintf("func %s(t *testing.T) {\n", testName)
var lastAction *actionRecord
if p != nil && p.ctx != nil {
lastAction = p.ctx.lastAction
}
// use the snapshot of the modified pair
if lastAction != nil && len(lastAction.PairSnapshots) > 0 {
// assume the modified pair is x % len(pairs), but since pairs are in order, and x is lastAction.X
pairIndex := lastAction.X % len(lastAction.PairSnapshots)
if pairIndex < len(lastAction.PairSnapshots) {
snapshot := lastAction.PairSnapshots[pairIndex]
if snapshot != "<nil>" && !strings.HasPrefix(snapshot, "<") {
repro += fmt.Sprintf("\tb, _ := base64.StdEncoding.DecodeString(\"%s\")\n", snapshot)
repro += "\tbm := NewBitmap()\n"
repro += "\tbm.UnmarshalBinary(b)\n"
repro += "\tif err := bm.Validate(); err != nil {\n"
repro += "\t\tt.Errorf(\"Initial Validate failed: %v\", err)\n"
repro += "\t}\n"
// perform the action
if strings.Contains(lastAction.Name, "setBit") {
repro += fmt.Sprintf("\tbm.AddInt(%d)\n", lastAction.Y)
} else if strings.Contains(lastAction.Name, "removeBit") {
repro += fmt.Sprintf("\tbm.Remove(%d)\n", lastAction.Y)
} else if strings.Contains(lastAction.Name, "flip") {
repro += fmt.Sprintf("\tbm.Flip(uint64(%d), uint64(%d)+1)\n", lastAction.Y, lastAction.Y)
} else if strings.Contains(lastAction.Name, "runOptimize") {
repro += "\tbm.RunOptimize()\n"
} else if strings.Contains(lastAction.Name, "clear") {
repro += "\tbm.Clear()\n"
} else if lastAction.Name == " or" {
pairIndexY := lastAction.Y % len(lastAction.PairSnapshots)
if pairIndexY < len(lastAction.PairSnapshots) {
snapshotY := lastAction.PairSnapshots[pairIndexY]
if snapshotY != "<nil>" && !strings.HasPrefix(snapshotY, "<") {
repro += fmt.Sprintf("\tb2, _ := base64.StdEncoding.DecodeString(\"%s\")\n", snapshotY)
repro += "\tbm2 := NewBitmap()\n"
repro += "\tbm2.UnmarshalBinary(b2)\n"
repro += "\tbm.Or(bm2)\n"
}
}
} else if lastAction.Name == " and" {
pairIndexY := lastAction.Y % len(lastAction.PairSnapshots)
if pairIndexY < len(lastAction.PairSnapshots) {
snapshotY := lastAction.PairSnapshots[pairIndexY]
if snapshotY != "<nil>" && !strings.HasPrefix(snapshotY, "<") {
repro += fmt.Sprintf("\tb2, _ := base64.StdEncoding.DecodeString(\"%s\")\n", snapshotY)
repro += "\tbm2 := NewBitmap()\n"
repro += "\tbm2.UnmarshalBinary(b2)\n"
repro += "\tbm.And(bm2)\n"
}
}
} else if lastAction.Name == " difference" {
pairIndexY := lastAction.Y % len(lastAction.PairSnapshots)
if pairIndexY < len(lastAction.PairSnapshots) {
snapshotY := lastAction.PairSnapshots[pairIndexY]
if snapshotY != "<nil>" && !strings.HasPrefix(snapshotY, "<") {
repro += fmt.Sprintf("\tb2, _ := base64.StdEncoding.DecodeString(\"%s\")\n", snapshotY)
repro += "\tbm2 := NewBitmap()\n"
repro += "\tbm2.UnmarshalBinary(b2)\n"
repro += "\tbm.AndNot(bm2)\n"
}
}
} else if lastAction.Name == " xor" {
pairIndexY := lastAction.Y % len(lastAction.PairSnapshots)
if pairIndexY < len(lastAction.PairSnapshots) {
snapshotY := lastAction.PairSnapshots[pairIndexY]
if snapshotY != "<nil>" && !strings.HasPrefix(snapshotY, "<") {
repro += fmt.Sprintf("\tb2, _ := base64.StdEncoding.DecodeString(\"%s\")\n", snapshotY)
repro += "\tbm2 := NewBitmap()\n"
repro += "\tbm2.UnmarshalBinary(b2)\n"
repro += "\tbm.Xor(bm2)\n"
}
}
} else {
repro += fmt.Sprintf("\t// Unhandled action: %s\n", lastAction.Name)
}
repro += "\tif err := bm.Validate(); err != nil {\n"
repro += "\t\tt.Errorf(\"Validate failed: %v\", err)\n"
repro += "\t} else {\n"
repro += "\t\tt.Logf(\"Validate succeeded\")\n"
repro += "\t}\n"
} else {
repro += "\t// invalid snapshot\n"
}
}
}
repro += "}\n"
// print the repro snippet for the developer
fmt.Println()
fmt.Println("=== SMAT REPRODUCER SNIPPET ===")
if len(repro) > 10000 {
fmt.Println("// Reproducer too large, skipping full print")
} else {
fmt.Println(repro)
}
// also write the repro snippet to a timestamped file in workdir/
if len(repro) > 10000 {
repro = "// Reproducer too large, skipping\n"
}
if err := os.MkdirAll("workdir", 0o755); err == nil {
fname := fmt.Sprintf("workdir/smat_repro_%d_test.go", ts)
if werr := os.WriteFile(fname, []byte(repro), 0o644); werr == nil {
fmt.Printf("Wrote repro to %s\n", fname)
} else {
fmt.Printf("Failed writing repro file: %v\n", werr)
}
} else {
fmt.Printf("Failed creating workdir: %v\n", err)
}
panic(fmt.Sprintf("[checkEquals] bitmap invalid: %v\ncurrentBase64:%s\nlastAction:%s\n", valid, curSnap, last))
}
if !p.equalsBitSet(p.bs, p.bm) {
panic("bitset mismatch")
}
}
func (p *smatPair) Validate() {
valid := p.bm.Validate()
if valid != nil {
panic(fmt.Sprintf("[Validate] bitmap invalid: %v", valid))
}
}
func (p *smatPair) equalsBitSet(a *bitset.BitSet, b *Bitmap) bool {
for i, e := a.NextSet(0); e; i, e = a.NextSet(i + 1) {
if !b.ContainsInt(int(i)) {
fmt.Printf("in a bitset, not b bitmap, i: %d\n", i)
fmt.Printf(" a bitset: %s\n b bitmap: %s\n",
a.String(), b.String())
return false
}
}
i := b.Iterator()
for i.HasNext() {
v := i.Next()
if !a.Test(uint(v)) {
fmt.Printf("in b bitmap, not a bitset, v: %d\n", v)
fmt.Printf(" a bitset: %s\n b bitmap: %s\n",
a.String(), b.String())
return false
}
}
return true
}