diff --git a/fragment.go b/fragment.go index 03fc11b66..d6518ea60 100644 --- a/fragment.go +++ b/fragment.go @@ -26,6 +26,7 @@ import ( "io" "io/ioutil" "math" + "math/bits" "os" "sort" "strings" @@ -1353,10 +1354,24 @@ func (f *fragment) rangeLT(bitDepth uint, predicate int64, allowEquality bool) ( return f.rangeGTUnsigned(b.Intersect(f.row(bsiSignBit)), bitDepth, upredicate, allowEquality) } +// msb gives the 1-indexed position (counting from lsb) of the most +// significant bit. E.G. for 1 it would return 1, for 2 2, for 3 2, +// for 4 3, for 8 4, etc. +func msb(x uint64) uint { + lz := bits.LeadingZeros64(x) + return 64 - uint(lz) +} + // rangeLTUnsigned returns all bits LT/LTE the predicate without considering the sign bit. func (f *fragment) rangeLTUnsigned(filter *Row, bitDepth uint, predicate uint64, allowEquality bool) (*Row, error) { keep := NewRow() + // if the predicate is larger than all representable numbers given + // our bitDepth... then just return everything. + if msb(predicate) >= bitDepth { + return filter, nil + } + // Filter any bits that don't match the current bit value. leadingZeros := true for i := int(bitDepth - 1); i >= 0; i-- { diff --git a/fragment_internal_test.go b/fragment_internal_test.go index 1181d5e82..215a3bd7c 100644 --- a/fragment_internal_test.go +++ b/fragment_internal_test.go @@ -596,6 +596,21 @@ func TestFragment_Range(t *testing.T) { } }) + t.Run("LTRegression", func(t *testing.T) { + f := mustOpenFragment("i", "f", viewStandard, 0, "") + defer f.Clean(t) + + if _, err := f.setValue(1, 1, 1); err != nil { + t.Fatal(err) + } + + if b, err := f.rangeOp(pql.LT, 1, 2); err != nil { + t.Fatal(err) + } else if !reflect.DeepEqual(b.Columns(), []uint64{1}) { + t.Fatalf("unepxected coulmns: %+v", b.Columns()) + } + }) + t.Run("GT", func(t *testing.T) { f := mustOpenFragment("i", "f", viewStandard, 0, "") defer f.Clean(t)