From 371f36fb76a871228eb75ba59dbad45df55a8a44 Mon Sep 17 00:00:00 2001 From: Alan Bernstein Date: Thu, 15 Jun 2017 11:43:28 -0500 Subject: [PATCH] Add fast check for empty conversions --- roaring/roaring.go | 53 ++++++++++++++++++++++++++++-- roaring/roaring_internal_test.go | 56 ++++++++++++++++++++++++++++++++ 2 files changed, 106 insertions(+), 3 deletions(-) diff --git a/roaring/roaring.go b/roaring/roaring.go index 1f5c9be58..39e29de57 100644 --- a/roaring/roaring.go +++ b/roaring/roaring.go @@ -1469,6 +1469,14 @@ func (c *container) runMax() uint32 { // bitmapToArray converts from bitmap format to array format. func (c *container) bitmapToArray() { c.array = make([]uint32, 0, c.n) + + // return early if empty + if c.n == 0 { + c.bitmap = nil + c.mapped = false + return + } + for i, bitmap := range c.bitmap { for bitmap != 0 { t := bitmap & -bitmap @@ -1483,6 +1491,14 @@ func (c *container) bitmapToArray() { // arrayToBitmap converts from array format to bitmap format. func (c *container) arrayToBitmap() { c.bitmap = make([]uint64, bitmapN) + + // return early if empty + if c.n == 0 { + c.array = nil + c.mapped = false + return + } + for _, v := range c.array { c.bitmap[int(v)/64] |= (uint64(1) << uint(v%64)) } @@ -1493,6 +1509,14 @@ func (c *container) arrayToBitmap() { // runToBitmap converts from RLE format to bitmap format. func (c *container) runToBitmap() { c.bitmap = make([]uint64, bitmapN) + + // return early if empty + if c.n == 0 { + c.runs = nil + c.mapped = false + return + } + for _, r := range c.runs { // TODO is there a faster way ?!?!! for v := r.start; v <= r.last; v++ { @@ -1505,9 +1529,16 @@ func (c *container) runToBitmap() { // bitmapToRun converts from bitmap format to RLE format. func (c *container) bitmapToRun() { - numRuns := c.bitmapCountRuns() // TODO test + // return early if empty + if c.n == 0 { + c.runs = make([]interval32, 0) + c.bitmap = nil + c.mapped = false + return + } + + numRuns := c.bitmapCountRuns() c.runs = make([]interval32, 0, numRuns) - // TODO return early if no runs current := c.bitmap[0] var i, start, last uint32 @@ -1553,7 +1584,15 @@ func (c *container) bitmapToRun() { // arrayToRun converts from array format to RLE format. func (c *container) arrayToRun() { - numRuns := c.arrayCountRuns() // TODO test + // return early if empty + if c.n == 0 { + c.runs = make([]interval32, 0) + c.array = nil + c.mapped = false + return + } + + numRuns := c.arrayCountRuns() c.runs = make([]interval32, 0, numRuns) start := c.array[0] for i, v := range c.array[1:] { @@ -1572,6 +1611,14 @@ func (c *container) arrayToRun() { // runToArray converts from RLE format to array format. func (c *container) runToArray() { c.array = make([]uint32, 0, c.n) + + // return early if empty + if c.n == 0 { + c.runs = nil + c.mapped = false + return + } + for _, r := range c.runs { for v := r.start; v <= r.last; v++ { c.array = append(c.array, v) diff --git a/roaring/roaring_internal_test.go b/roaring/roaring_internal_test.go index 8e083c9d0..62aacf467 100644 --- a/roaring/roaring_internal_test.go +++ b/roaring/roaring_internal_test.go @@ -748,6 +748,10 @@ func TestArrayToBitmap(t *testing.T) { array []uint32 exp []uint64 }{ + { + array: []uint32{}, + exp: []uint64{}, + }, { array: []uint32{0, 1, 2, 3}, exp: []uint64{0xF}, @@ -761,6 +765,7 @@ func TestArrayToBitmap(t *testing.T) { } a.array = test.array + a.n = len(test.array) a.arrayToBitmap() if !reflect.DeepEqual(a.bitmap, exp) { t.Fatalf("test #%v expected %v, but got %v", i, exp, a.bitmap) @@ -768,12 +773,47 @@ func TestArrayToBitmap(t *testing.T) { } } +func TestBitmapToArray(t *testing.T) { + a := &container{} + tests := []struct { + bitmap []uint64 + exp []uint32 + }{ + { + bitmap: []uint64{}, + exp: []uint32{}, + }, + { + bitmap: []uint64{0xF}, + exp: []uint32{0, 1, 2, 3}, + }, + } + for i, test := range tests { + a.bitmap = make([]uint64, bitmapN) + n := 0 + for i, v := range test.bitmap { + a.bitmap[i] = v + n += int(popcount(v)) + } + a.n = n + + a.bitmapToArray() + if !reflect.DeepEqual(a.array, test.exp) { + t.Fatalf("test #%v expected %v, but got %v", i, test.exp, a.array) + } + } +} + func TestRunToBitmap(t *testing.T) { a := &container{} tests := []struct { runs []interval32 exp []uint64 }{ + { + runs: []interval32{}, + exp: []uint64{}, + }, { runs: []interval32{{start: 0, last: 0}}, exp: []uint64{1}, @@ -794,11 +834,14 @@ func TestRunToBitmap(t *testing.T) { for i, test := range tests { exp := make([]uint64, bitmapN) + n := 0 for i, v := range test.exp { exp[i] = v + n += int(popcount(v)) } a.runs = test.runs + a.n = n a.runToBitmap() if !reflect.DeepEqual(a.bitmap, exp) { t.Fatalf("test #%v expected %v, but got %v", i, exp, a.bitmap) @@ -812,6 +855,11 @@ func TestBitmapToRun(t *testing.T) { bitmap []uint64 exp []interval32 }{ + { + // empty run + bitmap: []uint64{}, + exp: []interval32{}, + }, { // single-bit run bitmap: []uint64{1}, @@ -870,6 +918,10 @@ func TestArrayToRun(t *testing.T) { array []uint32 exp []interval32 }{ + { + array: []uint32{}, + exp: []interval32{}, + }, { array: []uint32{0}, exp: []interval32{{start: 0, last: 0}}, @@ -900,6 +952,10 @@ func TestRunToArray(t *testing.T) { runs []interval32 exp []uint32 }{ + { + runs: []interval32{}, + exp: []uint32{}, + }, { runs: []interval32{{start: 0, last: 0}}, exp: []uint32{0},