diff --git a/roaring/containers_test.go b/roaring/containers_test.go index e5d9fc2c4..5e170d4d1 100644 --- a/roaring/containers_test.go +++ b/roaring/containers_test.go @@ -176,6 +176,14 @@ func TestSliceContainers(t *testing.T) { }) } +func TestContainersFB1247(t *testing.T) { + bm := [bitmapN]uint64{0xF} + co := NewContainerBitmap(1, bm[:]) + co = co.bitmapToArray() + //should not panic + +} + func genRun(r *rand.Rand) Interval16 { gen: dat := r.Uint32() diff --git a/roaring/roaring.go b/roaring/roaring.go index 9f2ef5039..afbaa6b42 100644 --- a/roaring/roaring.go +++ b/roaring/roaring.go @@ -3567,21 +3567,40 @@ func (c *Container) bitmapToArray() *Container { return c } bitmap := c.bitmap() - n := int32(0) - array := make([]uint16, c.N()) - for i, word := range bitmap { - for word != 0 { - t := word & -word - if roaringParanoia { - if n >= c.N() { - panic("bitmap has more bits set than container.n") + // FB-1247 adding an extra check just in case c.N proves to be unreliable + // TODO prove this has to be reliable + makeArray := func(bm []uint64, ar []uint16) ([]uint16, bool, int32) { + n := int32(0) + for i, word := range bm { + for word != 0 { + t := word & -word + if roaringParanoia { + if n >= c.N() { + panic("bitmap has more bits set than container.n") + } } + if n == int32(len(ar)) { + return ar, true, n + } + ar[n] = uint16((i*64 + int(popcount(t-1)))) + n++ + word ^= t } - array[n] = uint16((i*64 + int(popcount(t-1)))) - n++ - word ^= t } + return ar, false, n + } + array, fail, n := makeArray(bitmap, make([]uint16, c.N())) + if fail { + // the onlyreason we are here is because N was incorrect + // so we force a recount of N and try again + c.bitmapRepair() // the onlyreason we are here is because N was incorrect + array, fail, n = makeArray(bitmap, make([]uint16, c.N())) + if fail { + //this should not be able to happen under any circumstance + panic("bitmapToArray failure") + } + } if roaringParanoia { if n != c.N() {