mirror of
https://github.com/featurebasedb/featurebase.git
synced 2026-08-28 10:54:59 +00:00
The ioutil package is deprecated, with all of its functions having moved into os or io. Do the replacements so we stop having this impending.
310 lines
10 KiB
Go
310 lines
10 KiB
Go
// Copyright 2021 Molecula Corp. All rights reserved.
|
|
//go:build gofuzz
|
|
// +build gofuzz
|
|
|
|
package roaring
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"fmt"
|
|
"reflect"
|
|
)
|
|
|
|
// FuzzBitmapUnmarshalBinary fuzz tests the unmarshaling of binary
|
|
// to both Pilosa and official roaring formats.
|
|
func FuzzBitmapUnmarshalBinary(data []byte) int {
|
|
b := NewBitmap()
|
|
err := b.UnmarshalBinary(data)
|
|
if err != nil {
|
|
return 0
|
|
}
|
|
return 1
|
|
}
|
|
|
|
// FuzzRoaringOps fuzz tests different operations on roaring bitmaps,
|
|
// comparing the results to a naive implementation of the operations
|
|
// on uint64 slices.
|
|
func FuzzRoaringOps(data []byte) int {
|
|
// number of uint64s not to include
|
|
const reserved = 4
|
|
// flipping is too inefficient for large values of end - start
|
|
// and will cause go-fuzz to hang if not controlled.
|
|
const maxFlips = 1000000
|
|
|
|
arr := bytesToUint64s(data)
|
|
if len(arr) <= reserved {
|
|
return 0
|
|
}
|
|
|
|
// start > end is possible. This will test correctness in unexpected conditions.
|
|
start, end, split, rand := arr[0], arr[1], int(arr[2]), arr[3]
|
|
|
|
// don't include start, end, split, rand in the slices
|
|
if split < reserved {
|
|
split = reserved
|
|
}
|
|
// ensure slice in bounds
|
|
if split > len(arr) {
|
|
split = len(arr)
|
|
}
|
|
// using removeSliceDuplicates guarantees that the slice inputs of the
|
|
// following functions do not have duplicates and are sorted, just as
|
|
// the Roaring Bitmap implementations are.
|
|
s1 := removeSliceDuplicates(arr[reserved:split])
|
|
s2 := removeSliceDuplicates(arr[split:])
|
|
if len(s1) == 0 {
|
|
s1 = nil
|
|
}
|
|
if len(s2) == 0 {
|
|
s2 = nil
|
|
}
|
|
bm1 := NewBitmap(arr[reserved:split]...)
|
|
bm2 := NewBitmap(arr[split:]...)
|
|
|
|
expected := s1
|
|
actual := bm1.Slice()
|
|
if !reflect.DeepEqual(expected, actual) {
|
|
panic(fmt.Sprintf("first slice:\n expected: %v\n got: %v", expected, actual))
|
|
}
|
|
|
|
expected = s2
|
|
actual = bm2.Slice()
|
|
if !reflect.DeepEqual(expected, actual) {
|
|
panic(fmt.Sprintf("second slice:\n expected: %v\n got: %v", expected, actual))
|
|
}
|
|
// Pure functions
|
|
|
|
expected = []uint64{maxInSlice(s1), maxInSlice(s2)}
|
|
actual = []uint64{bm1.Max(), bm2.Max()}
|
|
if !reflect.DeepEqual(expected, actual) {
|
|
panic(fmt.Sprintf("max values:\n expected: %v\n got: %v", expected, actual))
|
|
}
|
|
|
|
expected = intersectSlice(s1, s2)
|
|
actual = bm1.Intersect(bm2).Slice()
|
|
if !reflect.DeepEqual(expected, actual) {
|
|
panic(fmt.Sprintf("intersection:\n expected: %v\n got: %v", expected, actual))
|
|
}
|
|
|
|
expected = unionSlice(s1, s2)
|
|
actual = bm1.Union(bm2).Slice()
|
|
if !reflect.DeepEqual(expected, actual) {
|
|
panic(fmt.Sprintf("union:\n expected: %v\n got: %v", expected, actual))
|
|
}
|
|
|
|
expected = differenceSlice(s1, s2)
|
|
actual = bm1.Difference(bm2).Slice()
|
|
if !reflect.DeepEqual(expected, actual) {
|
|
panic(fmt.Sprintf("difference:\n expected: %v\n got: %v", expected, actual))
|
|
}
|
|
|
|
expected = xorSlice(s1, s2)
|
|
actual = bm1.Xor(bm2).Slice()
|
|
if !reflect.DeepEqual(expected, actual) {
|
|
panic(fmt.Sprintf("XOR:\n expected: %v\n got: %v", expected, actual))
|
|
}
|
|
|
|
if (len(s1) > 0) != bm1.Any() {
|
|
panic(fmt.Sprintf("any:\n %v has %v values but got %v which has %v values", s1, len(s1), bm1.Slice(), bm1.Any()))
|
|
}
|
|
if (len(s2) > 0) != bm2.Any() {
|
|
panic(fmt.Sprintf("any:\n %v has %v values but got %v which has %v values", s2, len(s2), bm2.Slice(), bm2.Any()))
|
|
}
|
|
|
|
expected = []uint64{uint64(len(s1)), uint64(len(s2))}
|
|
actual = []uint64{bm1.Count(), bm2.Count()}
|
|
if !reflect.DeepEqual(expected, actual) {
|
|
panic(fmt.Sprintf("count:\n expected: %v\n got: %v", expected, actual))
|
|
}
|
|
|
|
expect := countRangeSlice(s1, start, end)
|
|
got := bm1.CountRange(start, end)
|
|
if expect != got {
|
|
panic(fmt.Sprintf("count range:\n count from %v to %v in slice %v and bitmap %v:\n expected %v got %v",
|
|
start, end, s1, bm1.Slice(), expect, got))
|
|
}
|
|
expect = countRangeSlice(s2, start, end)
|
|
got = bm2.CountRange(start, end)
|
|
if expect != got {
|
|
panic(fmt.Sprintf("count range:\n count from %v to %v in slice %v and bitmap %v:\n expected %v got %v",
|
|
start, end, s2, bm2.Slice(), expect, got))
|
|
}
|
|
|
|
expected = rangeSlice(s1, start, end)
|
|
actual = bm1.SliceRange(start, end)
|
|
if !reflect.DeepEqual(expected, actual) {
|
|
panic(fmt.Sprintf("slice range:\n from %v to %v in slice %v and bitmap %v:\n expected %v\n got %v",
|
|
start, end, s1, bm1.Slice(), expected, actual))
|
|
}
|
|
expected = rangeSlice(s2, start, end)
|
|
actual = bm2.SliceRange(start, end)
|
|
if !reflect.DeepEqual(expected, actual) {
|
|
panic(fmt.Sprintf("slice range:\n from %v to %v in slice %v and bitmap %v:\n expected %v\n got %v",
|
|
start, end, s2, bm2.Slice(), expected, actual))
|
|
}
|
|
|
|
expect = uint64(len(intersectSlice(s1, s2)))
|
|
got = bm1.IntersectionCount(bm2)
|
|
if expect != got {
|
|
panic(fmt.Sprintf("intersection count:\n expected %v got %v", expect, got))
|
|
}
|
|
|
|
_, found := containedInSlice(s1, rand)
|
|
if found != bm1.Contains(rand) {
|
|
panic(fmt.Sprintf("contains:\n %v contains %v: %v\n %v contains %v: %v", s1, rand, found,
|
|
bm1.Slice(), rand, bm1.Contains(rand)))
|
|
}
|
|
_, found = containedInSlice(s2, rand)
|
|
if found != bm2.Contains(rand) {
|
|
panic(fmt.Sprintf("contains:\n %v contains %v: %v\n %v contains %v: %v", s2, rand, found,
|
|
bm2.Slice(), rand, bm2.Contains(rand)))
|
|
}
|
|
|
|
if end-start < maxFlips {
|
|
expected = flipSlice(s1, start, end)
|
|
actual = bm1.Flip(start, end).Slice()
|
|
if !reflect.DeepEqual(expected, actual) {
|
|
panic(fmt.Sprintf("flip:\n from %v to %v in slice %v and bitmap %v\n expected %v\n got %v",
|
|
start, end, s1, bm1.Slice(), expected, actual))
|
|
}
|
|
expected = flipSlice(s2, start, end)
|
|
actual = bm2.Flip(start, end).Slice()
|
|
if !reflect.DeepEqual(expected, actual) {
|
|
panic(fmt.Sprintf("flip:\n from %v to %v in slice %v and bitmap %v\n expected %v\n got %v",
|
|
start, end, s2, bm2.Slice(), expected, actual))
|
|
}
|
|
}
|
|
|
|
expected = make([]uint64, 0)
|
|
actual = make([]uint64, 0)
|
|
forEachInSlice(s1, func(v uint64) { expected = append(expected, v) })
|
|
bm1.ForEach(func(v uint64) error { actual = append(actual, v); return nil })
|
|
if !reflect.DeepEqual(expected, actual) {
|
|
panic(fmt.Sprintf("for each:\n expected %v\n got %v", expected, actual))
|
|
}
|
|
expected = make([]uint64, 0)
|
|
actual = make([]uint64, 0)
|
|
forEachInSlice(s2, func(v uint64) { expected = append(expected, v) })
|
|
bm2.ForEach(func(v uint64) error { actual = append(actual, v); return nil })
|
|
if !reflect.DeepEqual(expected, actual) {
|
|
panic(fmt.Sprintf("for each:\n expected %v\n got %v", expected, actual))
|
|
}
|
|
|
|
expected = make([]uint64, 0)
|
|
actual = make([]uint64, 0)
|
|
forEachInRangeSlice(s1, start, end, func(v uint64) { expected = append(expected, v) })
|
|
bm1.ForEachRange(start, end, func(v uint64) error { actual = append(actual, v); return nil })
|
|
if !reflect.DeepEqual(expected, actual) {
|
|
panic(fmt.Sprintf("for each in range:\n expected %v\n got %v", expected, actual))
|
|
}
|
|
expected = make([]uint64, 0)
|
|
actual = make([]uint64, 0)
|
|
forEachInRangeSlice(s2, start, end, func(v uint64) { expected = append(expected, v) })
|
|
bm2.ForEachRange(start, end, func(v uint64) error { actual = append(actual, v); return nil })
|
|
if !reflect.DeepEqual(expected, actual) {
|
|
panic(fmt.Sprintf("for each in range:\n expected %v\n got %v", expected, actual))
|
|
}
|
|
|
|
// Impure functions
|
|
// The following tests operations that mutate bitmaps.
|
|
|
|
nbm1, nbm2 := bm1.Clone(), bm2.Clone()
|
|
expected = shiftSlice(s1, 1)
|
|
tempBM, _ := nbm1.Shift(1)
|
|
actual = tempBM.Slice()
|
|
if !reflect.DeepEqual(expected, actual) {
|
|
panic(fmt.Sprintf("shift:\n in slice %v and bitmap %v \n expected %v\n got %v",
|
|
s1, bm1.Slice(), expected, actual))
|
|
}
|
|
expected = shiftSlice(s2, 1)
|
|
tempBM, _ = nbm2.Shift(1)
|
|
actual = tempBM.Slice()
|
|
if !reflect.DeepEqual(expected, actual) {
|
|
panic(fmt.Sprintf("shift:\n in slice %v and bitmap %v \n expected %v\n got %v",
|
|
s2, bm2.Slice(), expected, actual))
|
|
}
|
|
|
|
// reuse start and end as random values
|
|
rand2 := start
|
|
rand3 := end
|
|
|
|
nbm1 = bm1.Clone()
|
|
expected, echanged := addNToSlice(s1, rand)
|
|
achanged := nbm1.DirectAddN(rand)
|
|
actual = nbm1.Slice()
|
|
if echanged != achanged || !reflect.DeepEqual(expected, actual) {
|
|
panic(fmt.Sprintf("directAddN:\n adding %v in slice %v and bitmap %v \n expected %v and %v changed \n got %v and %v changed",
|
|
rand, s1, bm1.Slice(), expected, echanged, actual, achanged))
|
|
}
|
|
nbm2 = bm2.Clone()
|
|
expected, echanged = addNToSlice(s2, rand2, rand3)
|
|
achanged = nbm2.DirectAddN(rand2, rand3)
|
|
actual = nbm2.Slice()
|
|
if echanged != achanged || !reflect.DeepEqual(expected, actual) {
|
|
panic(fmt.Sprintf("directAddN:\n adding %v and %v in slice %v and bitmap %v \n expected %v and %v changed \n got %v and %v changed",
|
|
rand2, rand3, s2, bm2.Slice(), expected, echanged, actual, achanged))
|
|
}
|
|
|
|
nbm1 = bm1.Clone()
|
|
expected, echanged = removeNFromSlice(s1, rand2, rand3)
|
|
achanged = nbm1.DirectRemoveN(rand2, rand3)
|
|
actual = nbm1.Slice()
|
|
if echanged != achanged || !reflect.DeepEqual(expected, actual) {
|
|
panic(fmt.Sprintf("directRemoveN\n removing %v and %v in slice %v and bitmap %v \n expected %v and %v changed \n got %v and %v changed",
|
|
rand2, rand3, s1, bm1.Slice(), expected, echanged, actual, achanged))
|
|
}
|
|
nbm2 = bm2.Clone()
|
|
expected, echanged = removeNFromSlice(s2, rand)
|
|
achanged = nbm2.DirectRemoveN(rand)
|
|
actual = nbm2.Slice()
|
|
if echanged != achanged || !reflect.DeepEqual(expected, actual) {
|
|
panic(fmt.Sprintf("directRemoveN:\n removing %v in slice %v and bitmap %v \n expected %v and %v changed \n got %v and %v changed",
|
|
rand, s2, bm2.Slice(), expected, echanged, actual, achanged))
|
|
}
|
|
|
|
nbm1, nbm2 = bm1.Clone(), bm2.Clone()
|
|
expected = unionSlice(s1, s2)
|
|
nbm1.UnionInPlace(nbm2)
|
|
actual = nbm1.Slice()
|
|
if !reflect.DeepEqual(expected, actual) {
|
|
panic(fmt.Sprintf("union in place:\n expected %v\n got %v", expected, actual))
|
|
}
|
|
|
|
return 1
|
|
}
|
|
|
|
func bytesToUint64s(data []byte) []uint64 {
|
|
const uint64Size = 8
|
|
size := len(data) / uint64Size
|
|
|
|
slice := make([]uint64, 0)
|
|
for i := 0; i < size; i++ {
|
|
offset := i * uint64Size
|
|
num := binary.LittleEndian.Uint64(data[offset : offset+uint64Size])
|
|
slice = append(slice, num)
|
|
}
|
|
return slice
|
|
}
|
|
|
|
// copy and paste the following to a main file to run.
|
|
// path should be the absolute path to the corpus.
|
|
// make sure filename is not already in the corpus.
|
|
func addSliceToCorpus(slice []uint64, filename, path string) {
|
|
data := uint64sToBytes(slice)
|
|
err := os.WriteFile(path+"/"+filename, data, 0750)
|
|
if err != nil {
|
|
fmt.Printf("could not write to file: %v\n", err)
|
|
}
|
|
}
|
|
|
|
func uint64sToBytes(slice []uint64) []byte {
|
|
const uint64Size = 8
|
|
size := len(slice) * uint64Size
|
|
|
|
data := make([]byte, size)
|
|
for i := 0; i < len(slice); i++ {
|
|
offset := i * uint64Size
|
|
binary.LittleEndian.PutUint64(data[offset:offset+uint64Size], slice[i])
|
|
}
|
|
return data
|
|
}
|