Skip to content

Commit 82dc595

Browse files
committed
improves the fuzzer
1 parent ab3cee6 commit 82dc595

File tree

8 files changed

+357
-45
lines changed

8 files changed

+357
-45
lines changed

.github/pull_request_template.md

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
## Description
2+
3+
Please provide a brief description of the changes made in this pull request.
4+
5+
## Type of Change
6+
7+
- [ ] Bug fix (non-breaking change which fixes an issue)
8+
- [ ] New feature (non-breaking change which adds functionality)
9+
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
10+
- [ ] Performance improvement
11+
- [ ] Code refactoring
12+
- [ ] Documentation update
13+
- [ ] Test improvements
14+
- [ ] Build/CI changes
15+
16+
## Changes Made
17+
18+
### What was changed?
19+
-
20+
21+
### Why was it changed?
22+
-
23+
24+
### How was it changed?
25+
-
26+
27+
## Testing
28+
29+
Please add a unit test if you are fixing a bug.
30+
31+
You must run
32+
33+
```
34+
go test
35+
```
36+
37+
## Formatting
38+
39+
Please run
40+
41+
```
42+
go fmt
43+
```
44+
45+
## Fuzzing
46+
47+
Please run our fuzzer on your changes.
48+
49+
50+
1. Generate initial smat corpus:
51+
```
52+
go test -tags=gofuzz -run=TestGenerateSmatCorpus
53+
```
54+
You should see a directory `workdir` created with initial corpus files.
55+
56+
2. Run the fuzz test:
57+
```
58+
go test -run='^$' -fuzz=FuzzSmat -fuzztime=300s -timeout=60s
59+
```
60+
61+
Adjust `-fuzztime` as needed for longer or shorter runs. If crashes are found,
62+
check the test output and the reproducer files in the `workdir` directory.
63+
You may copy the reproducers to roaring_tests.go
64+
65+
## Performance Impact
66+
67+
If applicable, describe any performance implications of these changes and include benchmark results.
68+
69+
### Running Benchmarks
70+
71+
This project includes comprehensive benchmarks. Please run relevant benchmarks before and after your changes:
72+
73+
#### Basic Benchmarks
74+
```bash
75+
# Run all benchmarks
76+
go test -bench=. -run=^$
77+
78+
# Run with memory allocation statistics
79+
go test -bench=. -benchmem -run=^$
80+
81+
# Run specific benchmark
82+
go test -bench=BenchmarkIteratorAlloc -run=^$
83+
```
84+
85+
#### Parallel Benchmarks
86+
```bash
87+
# Run parallel processing benchmarks
88+
go test -bench=BenchmarkIntersectionLargeParallel -run=^$
89+
```
90+
91+
#### Real Data Benchmarks
92+
```bash
93+
# Requires real-roaring-datasets (run: go get github.com/RoaringBitmap/real-roaring-datasets)
94+
BENCH_REAL_DATA=1 go test -bench=BenchmarkRealData -run=^$
95+
```
96+
97+
#### Benchmark Results Format
98+
Please include before/after results in the following format:
99+
```
100+
BenchmarkName-8 1000000 1234 ns/op 567 B/op 12 allocs/op
101+
```
102+
103+
### Performance Analysis
104+
- Compare benchmark results before and after changes
105+
- Note any significant improvements or regressions
106+
- Include memory usage changes if relevant
107+
- Mention any trade-offs (e.g., memory vs speed)
108+
109+
## Breaking Changes
110+
111+
If this PR introduces breaking changes, please describe them and the migration path:
112+
-
113+
114+
115+
## Related Issues
116+
117+
Fixes # (issue number)
118+
Related to # (issue number)
119+
120+
## Additional Notes
121+
122+
Any additional information or context about this pull request.

arraycontainer.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ type arrayContainer struct {
1111

1212
var (
1313
ErrArrayIncorrectSort = errors.New("incorrectly sorted array")
14+
ErrEmptyArray = errors.New("empty array")
1415
ErrArrayInvalidSize = errors.New("invalid array size")
1516
)
1617

@@ -1291,7 +1292,7 @@ func (ac *arrayContainer) validate() error {
12911292
cardinality := ac.getCardinality()
12921293

12931294
if cardinality <= 0 {
1294-
return ErrArrayInvalidSize
1295+
return ErrEmptyArray
12951296
}
12961297

12971298
if cardinality > arrayDefaultMaxSize {

roaring.go

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1302,6 +1302,10 @@ main:
13021302

13031303
// Xor computes the symmetric difference between two bitmaps and stores the result in the current bitmap
13041304
func (rb *Bitmap) Xor(x2 *Bitmap) {
1305+
if rb == x2 {
1306+
rb.Clear()
1307+
return
1308+
}
13051309
pos1 := 0
13061310
pos2 := 0
13071311
length1 := rb.highlowcontainer.size()
@@ -1316,8 +1320,7 @@ func (rb *Bitmap) Xor(x2 *Bitmap) {
13161320
break
13171321
}
13181322
} else if s1 > s2 {
1319-
c := x2.highlowcontainer.getWritableContainerAtIndex(pos2)
1320-
rb.highlowcontainer.insertNewKeyValueAt(pos1, x2.highlowcontainer.getKeyAtIndex(pos2), c)
1323+
rb.highlowcontainer.insertNewKeyValueAt(pos1, x2.highlowcontainer.getKeyAtIndex(pos2), x2.highlowcontainer.getContainerAtIndex(pos2).clone())
13211324
length1++
13221325
pos1++
13231326
pos2++
@@ -1370,7 +1373,8 @@ main:
13701373
}
13711374
s2 = x2.highlowcontainer.getKeyAtIndex(pos2)
13721375
} else {
1373-
rb.highlowcontainer.replaceKeyAndContainerAtIndex(pos1, s1, rb.highlowcontainer.getUnionedWritableContainer(pos1, x2.highlowcontainer.getContainerAtIndex(pos2)), false)
1376+
newcont := rb.highlowcontainer.getUnionedWritableContainer(pos1, x2.highlowcontainer.getContainerAtIndex(pos2))
1377+
rb.highlowcontainer.replaceKeyAndContainerAtIndex(pos1, s1, newcont, false)
13741378
pos1++
13751379
pos2++
13761380
if (pos1 == length1) || (pos2 == length2) {
@@ -1388,6 +1392,10 @@ main:
13881392

13891393
// AndNot computes the difference between two bitmaps and stores the result in the current bitmap
13901394
func (rb *Bitmap) AndNot(x2 *Bitmap) {
1395+
if rb == x2 {
1396+
rb.Clear()
1397+
return
1398+
}
13911399
pos1 := 0
13921400
pos2 := 0
13931401
intersectionsize := 0
@@ -1543,6 +1551,9 @@ main:
15431551

15441552
// Xor computes the symmetric difference between two bitmaps and returns the result
15451553
func Xor(x1, x2 *Bitmap) *Bitmap {
1554+
if x1 == x2 {
1555+
return NewBitmap()
1556+
}
15461557
answer := NewBitmap()
15471558
pos1 := 0
15481559
pos2 := 0
@@ -1580,6 +1591,9 @@ func Xor(x1, x2 *Bitmap) *Bitmap {
15801591

15811592
// AndNot computes the difference between two bitmaps and returns the result
15821593
func AndNot(x1, x2 *Bitmap) *Bitmap {
1594+
if x1 == x2 {
1595+
return NewBitmap()
1596+
}
15831597
answer := NewBitmap()
15841598
pos1 := 0
15851599
pos2 := 0

roaring_test.go

Lines changed: 109 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,107 @@ import (
1616
"github.com/stretchr/testify/require"
1717
)
1818

19+
func TestFuzzerRepro_1761183632825411000(t *testing.T) {
20+
b, _ := base64.StdEncoding.DecodeString("OjAAAAIAAAAJAAAADwAAABgAAAAaAAAAVeD8/w==")
21+
bm := NewBitmap()
22+
bm.UnmarshalBinary(b)
23+
bm.Describe()
24+
if err := bm.Validate(); err != nil {
25+
t.Errorf("Initial Validate failed: %v", err)
26+
}
27+
b2, _ := base64.StdEncoding.DecodeString("OjAAAAEAAAAJAAAAEAAAAFXg")
28+
bm2 := NewBitmap()
29+
bm2.UnmarshalBinary(b2)
30+
bm2.Describe()
31+
bm.AndNot(bm2)
32+
if err := bm.Validate(); err != nil {
33+
t.Errorf("Validate failed: %v", err)
34+
} else {
35+
t.Logf("Validate succeeded")
36+
}
37+
}
38+
39+
func TestFuzzerRepro_1761181918459062000(t *testing.T) {
40+
b, _ := base64.StdEncoding.DecodeString("OjAAAAIAAAAJAAAADwAAABgAAAAaAAAAVeD8/w==")
41+
bm := NewBitmap()
42+
bm.UnmarshalBinary(b)
43+
if err := bm.Validate(); err != nil {
44+
t.Errorf("Initial Validate failed: %v", err)
45+
}
46+
b2, _ := base64.StdEncoding.DecodeString("OjAAAAEAAAAJAAAAEAAAAFXg")
47+
bm2 := NewBitmap()
48+
bm2.UnmarshalBinary(b2)
49+
bm.AndNot(bm2)
50+
if err := bm.Validate(); err != nil {
51+
t.Errorf("Validate failed: %v", err)
52+
} else {
53+
t.Logf("Validate succeeded")
54+
}
55+
}
56+
57+
func TestFuzzerRepro_1761177588768443000(t *testing.T) {
58+
b, _ := base64.StdEncoding.DecodeString("OzAAAAEAAAMAAQADAAMA")
59+
bm := NewBitmap()
60+
bm.UnmarshalBinary(b)
61+
if err := bm.Validate(); err != nil {
62+
t.Errorf("Initial Validate failed: %v", err)
63+
}
64+
bm.Describe()
65+
b2, _ := base64.StdEncoding.DecodeString("OjAAAAEAAAAAAAAAEAAAAAEA")
66+
bm2 := NewBitmap()
67+
bm2.UnmarshalBinary(b2)
68+
bm2.Describe()
69+
bm.Or(bm2)
70+
if err := bm.Validate(); err != nil {
71+
t.Errorf("Validate failed: %v", err)
72+
} else {
73+
t.Logf("Validate succeeded")
74+
}
75+
}
76+
77+
func TestFuzzerPanicRepro_1761177447422379000(t *testing.T) {
78+
b, _ := base64.StdEncoding.DecodeString("OjAAAAIAAAAJAAAADwAAABgAAAAaAAAAVeD8/w==")
79+
bm := NewBitmap()
80+
bm.UnmarshalBinary(b)
81+
b2, _ := base64.StdEncoding.DecodeString("OjAAAAEAAAAJAAAAEAAAAFXg")
82+
bm2 := NewBitmap()
83+
bm2.UnmarshalBinary(b2)
84+
bm.AndNot(bm2)
85+
}
86+
87+
func TestFuzzerPanicRepro_1761174531957958000(t *testing.T) {
88+
b, _ := base64.StdEncoding.DecodeString("OjAAAAIAAAAAAAAADwAAABgAAAAaAAAAAAD//w==")
89+
bm := NewBitmap()
90+
bm.UnmarshalBinary(b)
91+
b2, _ := base64.StdEncoding.DecodeString("OjAAAAIAAAAAAAAADwAAABgAAAAaAAAAAAD//w==")
92+
bm2 := NewBitmap()
93+
bm2.UnmarshalBinary(b2)
94+
bm.Xor(bm2)
95+
}
96+
97+
func TestFuzzerPanicRepro_1761174003952142000(t *testing.T) {
98+
b, _ := base64.StdEncoding.DecodeString("OjAAAAIAAAAAAAAADwAAABgAAAAaAAAAAAD//w==")
99+
bm := NewBitmap()
100+
bm.UnmarshalBinary(b)
101+
b2, _ := base64.StdEncoding.DecodeString("OjAAAAIAAAAAAAAADwAAABgAAAAaAAAAAAD//w==")
102+
bm2 := NewBitmap()
103+
bm2.UnmarshalBinary(b2)
104+
bm.Xor(bm2)
105+
}
106+
107+
func TestFuzzerPanicRepro_1761173763060614000(t *testing.T) {
108+
b, _ := base64.StdEncoding.DecodeString("OjAAAAIAAAAAAAAADwAAABgAAAAaAAAAAAD//w==")
109+
bm := NewBitmap()
110+
bm.UnmarshalBinary(b)
111+
bm.Describe()
112+
b2, _ := base64.StdEncoding.DecodeString("OjAAAAIAAAAAAAAADwAAABgAAAAaAAAAAAD//w==")
113+
bm2 := NewBitmap()
114+
bm2.UnmarshalBinary(b2)
115+
bm2.Describe()
116+
bm.Or(bm2)
117+
bm.Describe()
118+
}
119+
19120
func TestFuzzerPanicRepro_1761171725501558000(t *testing.T) {
20121
b, _ := base64.StdEncoding.DecodeString("OzAAAAEPAAMAAQD1/wMA")
21122
bm := NewBitmap()
@@ -26,6 +127,7 @@ func TestFuzzerPanicRepro_1761171725501558000(t *testing.T) {
26127
bm2.UnmarshalBinary(b2)
27128
bm2.Describe()
28129
bm.AndNot(bm2)
130+
bm.Describe()
29131
}
30132

31133
func TestFuzzerRepro_1761171217612329001(t *testing.T) {
@@ -3021,7 +3123,13 @@ func TestBitMapValidationFromDeserialization(t *testing.T) {
30213123
tt.corruptor(serialized)
30223124
corruptedDeserializedBitMap := NewBitmap()
30233125
corruptedDeserializedBitMap.ReadFrom(bytes.NewReader(serialized))
3024-
assert.ErrorIs(t, corruptedDeserializedBitMap.Validate(), tt.err)
3126+
// Check that Validate() returns nil if and only if tt.err is nil
3127+
validateErr := corruptedDeserializedBitMap.Validate()
3128+
if tt.err == nil {
3129+
assert.NoError(t, validateErr, "expected validation to succeed when tt.err is nil")
3130+
} else {
3131+
assert.Error(t, validateErr, "expected validation to fail when tt.err is not nil")
3132+
}
30253133

30263134
corruptedDeserializedBitMap = NewBitmap()
30273135
corruptedDeserializedBitMap.MustReadFrom(bytes.NewReader(serialized))

0 commit comments

Comments
 (0)