WIP bug fix, cleanup, additional tests

This commit is contained in:
Alan Bernstein 2017-06-12 11:51:24 -05:00 committed by Matt Jaffee
parent f4f07c0aa2
commit bf7a1bc08a
3 changed files with 159 additions and 54 deletions

View file

@ -17,6 +17,7 @@ package pilosa_test
import (
"bytes"
"flag"
"fmt"
"io/ioutil"
"math"
"os"
@ -248,6 +249,7 @@ func TestFragment_TopN_Intersect_Large(t *testing.T) {
// Set bits on rows 0 - 999. Higher rows have higher bit counts.
for i := uint64(0); i < 1000; i++ {
for j := uint64(0); j < i; j++ {
//fmt.Printf("testfragment: setting %d, %d\n", i, j)
f.MustSetBits(i, j)
}
}
@ -272,6 +274,45 @@ func TestFragment_TopN_Intersect_Large(t *testing.T) {
}
}
// temporary test for catching op log bug
func TestFragment_TopN_Intersect_Moderate(t *testing.T) {
f := MustOpenFragment("i", "f", pilosa.ViewStandard, 0, pilosa.CacheTypeRanked)
defer f.Close()
// Create an intersecting input row.
src := pilosa.NewBitmap(
80, 81, 82, 83, 84, 85, 86, 87, 88, 89,
90, 91, 92, 93, 94, 95, 96, 97, 98, 99,
)
// Set bits on rows 0 - 99. Higher rows have higher bit counts.
for i := uint64(0); i < 100; i++ {
for j := uint64(0); j < i; j++ {
//fmt.Printf("testfragment: setting %d, %d\n", i, j)
f.MustSetBits(i, j)
}
}
f.RecalculateCache()
// Retrieve top rows.
if pairs, err := f.Top(pilosa.TopOptions{N: 10, Src: src}); err != nil {
t.Fatal(err)
} else if !reflect.DeepEqual(pairs, []pilosa.Pair{
{ID: 99, Count: 19},
{ID: 98, Count: 18},
{ID: 97, Count: 17},
{ID: 96, Count: 16},
{ID: 95, Count: 15},
{ID: 94, Count: 14},
{ID: 93, Count: 13},
{ID: 92, Count: 12},
{ID: 91, Count: 11},
{ID: 90, Count: 10},
}) {
t.Fatalf("unexpected pairs: %s", spew.Sdump(pairs))
}
}
// Ensure a fragment can return top rows when specified by ID.
func TestFragment_TopN_IDs(t *testing.T) {
f := MustOpenFragment("i", "f", pilosa.ViewStandard, 0, pilosa.CacheTypeRanked)
@ -789,3 +830,34 @@ func TestFragment_Zero_Tanimoto(t *testing.T) {
t.Fatalf("unexpected pair(1): %v", pairs[2])
}
}
func TestFragment_Snapshot_Run(t *testing.T) {
fmt.Printf("mustopenfragment\n")
f := MustOpenFragment("i", "f", pilosa.ViewStandard, 0, "")
defer f.Close()
fmt.Printf("\n---\nset bits\n")
// Set bits on the fragment.
for i := uint64(1); i < 3; i++ {
if _, err := f.SetBit(1000, i); err != nil {
t.Fatal(err)
}
}
fmt.Printf("\n---\nsnapshot\n")
// Snapshot bitmap and verify data.
if err := f.Snapshot(); err != nil {
t.Fatal(err)
} else if n := f.Row(1000).Count(); n != 2 {
t.Fatalf("unexpected count: %d", n)
}
fmt.Printf("\n---\nreopen\n")
// Close and reopen the fragment & verify the data.
if err := f.Reopen(); err != nil {
t.Fatal(err)
} else if n := f.Row(1000).Count(); n != 2 {
t.Fatalf("unexpected count (reopen): %d", n)
}
}

View file

@ -26,9 +26,17 @@ import (
)
const (
// cookie is the first four bytes in a roaring bitmap file.
cookieNoRuns = uint32(12346)
cookie = uint32(12347)
// magicNumber is an identifier, in bytes 0-1 of the file.
magicNumberNoRuns = uint32(12346)
magicNumber = uint32(12347)
// storageVersion indicates the storage version, in bytes 2-3.
storageVersion = uint32(0)
// cookie is the first four bytes in a roaring bitmap file,
// formed by joining magicNumber and storageVersion
cookieNoRuns = magicNumberNoRuns<<16 + storageVersion
cookie = magicNumber<<16 + storageVersion
// headerBaseSize is the size of the cookie and key count at the beginning of a file.
// Headers in files with runs also include runFlagBitset, of length (numContainers+7)/8.
@ -661,7 +669,7 @@ func (b *Bitmap) UnmarshalBinary(data []byte) error {
// Map byte slice directly to the container data.
c := b.containers[i]
if c.n <= ArrayMaxSize {
if containsRuns && (runFlagBitset[uint8(i)/8]&(1<<uint8(i)%8)) != 0 {
if containsRuns && (runFlagBitset[uint8(i)/8]&(1<<uint8(i%8))) != 0 {
// Read runs.
runCount := binary.LittleEndian.Uint16(data[offset : offset+2])
c.runs = (*[0xFFFFFFF]interval32)(unsafe.Pointer(&data[offset+2]))[:runCount] // TODO verify
@ -680,7 +688,6 @@ func (b *Bitmap) UnmarshalBinary(data []byte) error {
c.bitmap = (*[0xFFFFFFF]uint64)(unsafe.Pointer(&data[offset]))[:bitmapN]
opsOffset = int(offset) + len(c.bitmap)*8
}
// Verify container count on load.
// TODO: instead of commenting this out, we need to make it a configuration option
//count := c.count()
@ -809,8 +816,8 @@ type BitmapInfo struct {
// Iterator represents an iterator over a Bitmap.
type Iterator struct {
bitmap *Bitmap
i, j int
bitmap *Bitmap
i, j, k int // i: container; j: array index, bit index, or run index; k:
}
// eof returns true if the iterator is at the end of the bitmap.
@ -829,7 +836,8 @@ func (itr *Iterator) Seek(seek uint64) {
// Move to the correct value index inside the array container.
lb := lowbits(seek)
if c := itr.bitmap.containers[itr.i]; c.isArray() {
c := itr.bitmap.containers[itr.i]
if c.isArray() {
// Find index in the container.
itr.j = search32(c.array, lb)
if itr.j < 0 {
@ -845,6 +853,15 @@ func (itr *Iterator) Seek(seek uint64) {
return
}
if c.isRun() {
// TODO work for seek!=0
itr.i, itr.j, itr.k = 0, 0, -1
if seek != 0 {
panic("cant seeek nonzero")
}
return
}
// If it's a bitmap container then move to index before the value and call next().
itr.j = int(lb) - 1
}
@ -868,6 +885,35 @@ func (itr *Iterator) Next() (v uint64, eof bool) {
itr.j++
return itr.peek(), false
}
if c.isRun() {
if itr.j >= len(c.runs)-1 {
r := c.runs[itr.j]
runLength := int(r.last - r.start)
if itr.k >= runLength {
itr.i++
itr.j = -1
continue
} else {
itr.k++
return itr.peek(), false
}
}
r := c.runs[itr.j]
runLength := int(r.last - r.start)
if itr.k >= runLength {
itr.k = 0
itr.j++
} else {
itr.k++
}
return itr.peek(), false
}
// Move to the next possible index in the bitmap container.
itr.j++
@ -904,6 +950,9 @@ func (itr *Iterator) peek() uint64 {
if c.isArray() {
return uint64(key)<<16 | uint64(c.array[itr.j])
}
if c.isRun() {
return uint64(key)<<16 | uint64(c.runs[itr.j].start+uint32(itr.k))
}
return uint64(key)<<16 | uint64(itr.j)
}
@ -1002,10 +1051,12 @@ func (c *container) isArray() bool {
return c.bitmap == nil && c.runs == nil
}
// isBitmap returns true if the container is a bitmap container
func (c *container) isBitmap() bool {
return c.array == nil && c.runs == nil
}
// isRun returns true if the container is a run-length-encoded container
func (c *container) isRun() bool {
return c.array == nil && c.bitmap == nil
}
@ -1582,7 +1633,7 @@ func (c *container) size() int {
if c.isArray() {
return len(c.array) * 4
} else if c.isRun() {
return len(c.runs) * 8
return len(c.runs)*8 + 2
} else {
return len(c.bitmap) * 8
}
@ -1597,7 +1648,7 @@ func (c *container) info() ContainerInfo {
info.Alloc = len(c.array) * 4
} else if c.isRun() {
info.Type = "run"
info.Alloc = len(c.runs) * 8
info.Alloc = len(c.runs)*8 + 2
} else {
info.Type = "bitmap"
info.Alloc = len(c.bitmap) * 8
@ -2323,26 +2374,28 @@ func differenceArrayArray(a, b *container) *container {
func differenceArrayRun(a, b *container) *container {
// func (ac *arrayContainer) iandNotRun16(rc *runContainer16) container {
if b.n == 0 {
if a.n == 0 || b.n == 0 {
return a.clone()
}
output := &container{array: make([]uint32, 0, a.n)}
// cardinality upper bound: card(A)
i := 0 // array index
j := 0 // run index
i := 0 // array index
j := 0 // run index
// keep all array elements before beginning of runs
for ; i < b.runs[j].start; i++ {
for ; i < int(b.runs[j].start); i++ {
output.array = append(output.array, a.array[i])
}
// handle overlap
for ; i < a.n; i++ {
// if array element in run, keep
if !(a.array[i] >= b.runs[j].start && a.array[i] <= b.runs[j].last) {
output.array = append(output.array, a.array[i])
}
// update current run
if i >= int(b.runs[j].last) {
j++
if j == len(b.runs) {
@ -2354,7 +2407,7 @@ func differenceArrayRun(a, b *container) *container {
// keep all array elements after end of runs
output.array = append(output.array, a.array[i:]...)
return output
}
@ -2372,27 +2425,33 @@ func differenceBitmapRun(a, b *container) *container {
func differenceRunArray(a, b *container) *container {
// TODO
if a.n == 0 || b.n == 0 {
return a.clone()
}
output := &container{runs: make([]interval32, 0, a.n)}
return output
}
func differenceRunBitmap(a, b *container) *container {
// TODO
if a.n == 0 || b.n == 0 {
return a.clone()
}
output := &container{runs: make([]interval32, 0, a.n)}
itr := newBufIterator(newBitmapIterator(b.bitmap))
fmt.Printf("\ndifferenceRunBitmap\n")
for i := 0; ; {
vb, eof := itr.next()
if eof || {
if eof {
break
}
fmt.Println(i, vb, eof)
fmt.Println(i, vb)
i++
}
return output
}
@ -2403,8 +2462,8 @@ func differenceRunRun(a, b *container) *container {
return a.clone()
}
apos := 0 // current a-run index
bpos := 0 // current b-run index
apos := 0 // current a-run index
bpos := 0 // current b-run index
astart := a.runs[apos].start
alast := a.runs[apos].last
bstart := b.runs[bpos].start
@ -2412,7 +2471,7 @@ func differenceRunRun(a, b *container) *container {
alen := len(a.runs)
blen := len(b.runs)
output := &container{runs: make([]interval32, 0, alen+blen)} // TODO allocate max then truncate? or something else
output := &container{runs: make([]interval32, 0, alen+blen)} // TODO allocate max then truncate? or something else
// cardinality upper bound: sum of number of runs
// each B-run could split an A-run in two, up to len(b.runs) times

View file

@ -996,36 +996,6 @@ func TestUnionBitmapRun(t *testing.T) {
}
}
func TestRunToArray(t *testing.T) {
a := &container{}
tests := []struct {
runs []interval32
exp []uint32
}{
{
runs: []interval32{{start: 0, last: 0}},
exp: []uint32{0},
},
{
runs: []interval32{{start: 0, last: 4}},
exp: []uint32{0, 1, 2, 3, 4},
},
{
runs: []interval32{{start: 2, last: 2}, {start: 5, last: 7}, {start: 13, last: 14}, {start: 17, last: 17}},
exp: []uint32{2, 5, 6, 7, 13, 14, 17},
},
}
for i, test := range tests {
a.runs = test.runs
a.n = len(test.exp)
a.runToArray()
if !reflect.DeepEqual(a.array, test.exp) {
t.Fatalf("test #%v expected %v, but got %v", i, test.exp, a.array)
}
}
}
func TestBitmapCountRuns(t *testing.T) {
c := &container{bitmap: make([]uint64, bitmapN)}
tests := []struct {
@ -1133,6 +1103,7 @@ func TestDifferenceArrayRun(t *testing.T) {
}
for i, test := range tests {
a.array = test.array
a.n = len(a.array)
b.runs = test.runs
b.n = b.runCountRange(0, 100)
ret := differenceArrayRun(a, b)
@ -1160,6 +1131,7 @@ func TestDifferenceRunArray(t *testing.T) {
a.runs = test.runs
a.n = a.runCountRange(0, 100)
b.array = test.array
b.n = len(b.array)
ret := differenceRunArray(a, b)
if !reflect.DeepEqual(ret.array, test.exp) {
t.Fatalf("test #%v expected %v, but got %v", i, test.exp, ret.array)
@ -1187,6 +1159,7 @@ func TestDifferenceRunBitmap(t *testing.T) {
for i, v := range test.bitmap {
b.bitmap[i] = v
}
b.n = b.bitmapCountRange(0, 100)
ret := differenceRunBitmap(a, b)
if !reflect.DeepEqual(ret.runs, test.exp) {
t.Fatalf("test #%v expected %v, but got %v", i, test.exp, ret.runs)
@ -1212,11 +1185,12 @@ func TestDifferenceBitmapRun(t *testing.T) {
for i, v := range test.bitmap {
a.bitmap[i] = v
}
a.n = a.bitmapCountRange(0, 100)
b.runs = test.runs
b.n = b.runCountRange(0, 100)
ret := differenceBitmapRun(a, b)
if !reflect.DeepEqual(ret.bitmap[:len(test.exp)], test.exp) {
t.Fatalf("test #%v expected %v, but got %v", i, test.exp, ret.bitmap[:len(test.exp)])
t.Fatalf("test #%v expected \n%X, but got \n%X", i, test.exp, ret.bitmap[:len(test.exp)])
}
}
}