Skip to content

Commit fcf5d84

Browse files
authored
Fix Int indexes to make them sortable. (#114)
1 parent 422653b commit fcf5d84

File tree

4 files changed

+116
-61
lines changed

4 files changed

+116
-61
lines changed

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module github.com/hashicorp/go-memdb
22

3-
go 1.12
3+
go 1.13
44

55
require (
66
github.com/hashicorp/go-immutable-radix v1.3.0

go.sum

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ github.com/hashicorp/go-immutable-radix v1.3.0 h1:8exGP7ego3OmkfksihtSouGMZ+hQrh
22
github.com/hashicorp/go-immutable-radix v1.3.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
33
github.com/hashicorp/go-uuid v1.0.0 h1:RS8zrF7PhGwyNPOtxSClXXj9HA8feRnJzgnI1RJCSnM=
44
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
5-
github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo=
65
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
76
github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
87
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=

index.go

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ import (
55
"encoding/hex"
66
"errors"
77
"fmt"
8-
"math/bits"
98
"reflect"
9+
"strconv"
1010
"strings"
1111
)
1212

@@ -308,8 +308,7 @@ func (i *IntFieldIndex) FromObject(obj interface{}) (bool, []byte, error) {
308308

309309
// Get the value and encode it
310310
val := fv.Int()
311-
buf := make([]byte, size)
312-
binary.PutVarint(buf, val)
311+
buf := encodeInt(val, size)
313312

314313
return true, buf, nil
315314
}
@@ -331,26 +330,50 @@ func (i *IntFieldIndex) FromArgs(args ...interface{}) ([]byte, error) {
331330
}
332331

333332
val := v.Int()
334-
buf := make([]byte, size)
335-
binary.PutVarint(buf, val)
333+
buf := encodeInt(val, size)
336334

337335
return buf, nil
338336
}
339337

338+
func encodeInt(val int64, size int) []byte {
339+
buf := make([]byte, size)
340+
341+
// This bit flips the sign bit on any sized signed twos-complement integer,
342+
// which when truncated to a uint of the same size will bias the value such
343+
// that the maximum negative int becomes 0, and the maximum positive int
344+
// becomes the maximum positive uint.
345+
scaled := val ^ int64(-1<<(size*8-1))
346+
347+
switch size {
348+
case 1:
349+
buf[0] = uint8(scaled)
350+
case 2:
351+
binary.BigEndian.PutUint16(buf, uint16(scaled))
352+
case 4:
353+
binary.BigEndian.PutUint32(buf, uint32(scaled))
354+
case 8:
355+
binary.BigEndian.PutUint64(buf, uint64(scaled))
356+
default:
357+
panic(fmt.Sprintf("unsupported int size parameter: %d", size))
358+
}
359+
360+
return buf
361+
}
362+
340363
// IsIntType returns whether the passed type is a type of int and the number
341364
// of bytes needed to encode the type.
342365
func IsIntType(k reflect.Kind) (size int, okay bool) {
343366
switch k {
344367
case reflect.Int:
345-
return binary.MaxVarintLen64, true
368+
return strconv.IntSize / 8, true
346369
case reflect.Int8:
347-
return 2, true
370+
return 1, true
348371
case reflect.Int16:
349-
return binary.MaxVarintLen16, true
372+
return 2, true
350373
case reflect.Int32:
351-
return binary.MaxVarintLen32, true
374+
return 4, true
352375
case reflect.Int64:
353-
return binary.MaxVarintLen64, true
376+
return 8, true
354377
default:
355378
return 0, false
356379
}
@@ -420,6 +443,8 @@ func encodeUInt(val uint64, size int) []byte {
420443
binary.BigEndian.PutUint32(buf, uint32(val))
421444
case 8:
422445
binary.BigEndian.PutUint64(buf, val)
446+
default:
447+
panic(fmt.Sprintf("unsupported uint size parameter: %d", size))
423448
}
424449

425450
return buf
@@ -430,7 +455,7 @@ func encodeUInt(val uint64, size int) []byte {
430455
func IsUintType(k reflect.Kind) (size int, okay bool) {
431456
switch k {
432457
case reflect.Uint:
433-
return bits.UintSize / 8, true
458+
return strconv.IntSize / 8, true
434459
case reflect.Uint8:
435460
return 1, true
436461
case reflect.Uint16:

index_test.go

Lines changed: 79 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -578,7 +578,6 @@ func TestUUIDFieldIndex_PrefixFromArgs(t *testing.T) {
578578
if !bytes.Equal(uuidBuf[:9], val) {
579579
t.Fatalf("foo")
580580
}
581-
582581
}
583582

584583
func BenchmarkUUIDFieldIndex_parseString(b *testing.B) {
@@ -609,16 +608,16 @@ func generateUUID() ([]byte, string) {
609608
func TestIntFieldIndex_FromObject(t *testing.T) {
610609
obj := testObj()
611610

612-
eint := make([]byte, 10)
613-
eint8 := make([]byte, 2)
614-
eint16 := make([]byte, 3)
615-
eint32 := make([]byte, 5)
616-
eint64 := make([]byte, 10)
617-
binary.PutVarint(eint, int64(obj.Int))
618-
binary.PutVarint(eint8, int64(obj.Int8))
619-
binary.PutVarint(eint16, int64(obj.Int16))
620-
binary.PutVarint(eint32, int64(obj.Int32))
621-
binary.PutVarint(eint64, obj.Int64)
611+
eint := make([]byte, 8)
612+
eint8 := make([]byte, 1)
613+
eint16 := make([]byte, 2)
614+
eint32 := make([]byte, 4)
615+
eint64 := make([]byte, 8)
616+
binary.BigEndian.PutUint64(eint, 1<<63+1)
617+
eint8[0] = 0
618+
binary.BigEndian.PutUint16(eint16, 0)
619+
binary.BigEndian.PutUint32(eint32, 0)
620+
binary.BigEndian.PutUint64(eint64, 0)
622621

623622
cases := []struct {
624623
Field string
@@ -659,7 +658,6 @@ func TestIntFieldIndex_FromObject(t *testing.T) {
659658
t.Run(c.Field, func(t *testing.T) {
660659
indexer := IntFieldIndex{c.Field}
661660
ok, val, err := indexer.FromObject(obj)
662-
663661
if err != nil {
664662
if ok {
665663
t.Fatalf("okay and error")
@@ -679,7 +677,6 @@ func TestIntFieldIndex_FromObject(t *testing.T) {
679677
if !bytes.Equal(val, c.Expected) {
680678
t.Fatalf("bad: %#v %#v", val, c.Expected)
681679
}
682-
683680
})
684681
}
685682
}
@@ -702,16 +699,16 @@ func TestIntFieldIndex_FromArgs(t *testing.T) {
702699
}
703700

704701
obj := testObj()
705-
eint := make([]byte, 10)
706-
eint8 := make([]byte, 2)
707-
eint16 := make([]byte, 3)
708-
eint32 := make([]byte, 5)
709-
eint64 := make([]byte, 10)
710-
binary.PutVarint(eint, int64(obj.Int))
711-
binary.PutVarint(eint8, int64(obj.Int8))
712-
binary.PutVarint(eint16, int64(obj.Int16))
713-
binary.PutVarint(eint32, int64(obj.Int32))
714-
binary.PutVarint(eint64, obj.Int64)
702+
eint := make([]byte, 8)
703+
eint8 := make([]byte, 1)
704+
eint16 := make([]byte, 2)
705+
eint32 := make([]byte, 4)
706+
eint64 := make([]byte, 8)
707+
binary.BigEndian.PutUint64(eint, 1<<63+1)
708+
eint8[0] = 0
709+
binary.BigEndian.PutUint16(eint16, 0)
710+
binary.BigEndian.PutUint32(eint32, 0)
711+
binary.BigEndian.PutUint64(eint64, 0)
715712

716713
val, err := indexer.FromArgs(obj.Int)
717714
if err != nil {
@@ -754,6 +751,46 @@ func TestIntFieldIndex_FromArgs(t *testing.T) {
754751
}
755752
}
756753

754+
func TestIntFieldIndexSortability(t *testing.T) {
755+
testCases := []struct {
756+
i8l int8
757+
i8r int8
758+
i16l int16
759+
i16r int16
760+
i32l int32
761+
i32r int32
762+
i64l int64
763+
i64r int64
764+
il int
765+
ir int
766+
expected int
767+
name string
768+
}{
769+
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "zero"},
770+
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, "small eq"},
771+
{0, 1, 0, 1, 0, 1, 0, 1, 0, 1, -1, "small lt"},
772+
{2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 1, "small gt"},
773+
{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, "small neg eq"},
774+
{-2, -1, -2, -1, -2, -1, -2, -1, -2, -1, -1, "small neg lt"},
775+
{-1, -2, -1, -2, -1, -2, -1, -2, -1, -2, 1, "small neg gt"},
776+
{-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, "neg vs pos"},
777+
{-128, 127, -32768, 32767, -2147483648, 2147483647, -9223372036854775808, 9223372036854775807, -9223372036854775808, 9223372036854775807, -1, "max conditions"},
778+
{100, 127, 1000, 2000, 1000000000, 2000000000, 10000000000, 20000000000, 1000000000, 2000000000, -1, "large lt"},
779+
{100, 99, 1000, 999, 1000000000, 999999999, 10000000000, 9999999999, 1000000000, 999999999, 1, "large gt"},
780+
{126, 127, 255, 256, 65535, 65536, 4294967295, 4294967296, 65535, 65536, -1, "edge conditions"},
781+
}
782+
783+
for _, tc := range testCases {
784+
t.Run(tc.name, func(t *testing.T) {
785+
compareEncoded(t, &IntFieldIndex{"Foo"}, tc.i8l, tc.i8r, tc.expected)
786+
compareEncoded(t, &IntFieldIndex{"Foo"}, tc.i16l, tc.i16r, tc.expected)
787+
compareEncoded(t, &IntFieldIndex{"Foo"}, tc.i32l, tc.i32r, tc.expected)
788+
compareEncoded(t, &IntFieldIndex{"Foo"}, tc.i64l, tc.i64r, tc.expected)
789+
compareEncoded(t, &IntFieldIndex{"Foo"}, tc.il, tc.ir, tc.expected)
790+
})
791+
}
792+
}
793+
757794
func TestUintFieldIndex_FromObject(t *testing.T) {
758795
obj := testObj()
759796

@@ -807,7 +844,6 @@ func TestUintFieldIndex_FromObject(t *testing.T) {
807844
t.Run(c.Field, func(t *testing.T) {
808845
indexer := UintFieldIndex{c.Field}
809846
ok, val, err := indexer.FromObject(obj)
810-
811847
if err != nil {
812848
if ok {
813849
t.Fatalf("okay and error")
@@ -827,7 +863,6 @@ func TestUintFieldIndex_FromObject(t *testing.T) {
827863
if !bytes.Equal(val, c.Expected) {
828864
t.Fatalf("bad: %#v %#v", val, c.Expected)
829865
}
830-
831866
})
832867
}
833868
}
@@ -928,25 +963,23 @@ func TestUIntFieldIndexSortability(t *testing.T) {
928963

929964
for _, tc := range testCases {
930965
t.Run(tc.name, func(t *testing.T) {
931-
compareEncoded(t, tc.u8l, tc.u8r, tc.expected)
932-
compareEncoded(t, tc.u16l, tc.u16r, tc.expected)
933-
compareEncoded(t, tc.u32l, tc.u32r, tc.expected)
934-
compareEncoded(t, tc.u64l, tc.u64r, tc.expected)
935-
compareEncoded(t, tc.ul, tc.ur, tc.expected)
966+
compareEncoded(t, &UintFieldIndex{"Foo"}, tc.u8l, tc.u8r, tc.expected)
967+
compareEncoded(t, &UintFieldIndex{"Foo"}, tc.u16l, tc.u16r, tc.expected)
968+
compareEncoded(t, &UintFieldIndex{"Foo"}, tc.u32l, tc.u32r, tc.expected)
969+
compareEncoded(t, &UintFieldIndex{"Foo"}, tc.u64l, tc.u64r, tc.expected)
970+
compareEncoded(t, &UintFieldIndex{"Foo"}, tc.ul, tc.ur, tc.expected)
936971
})
937972
}
938973
}
939974

940-
func compareEncoded(t *testing.T, l interface{}, r interface{}, expected int) {
941-
indexer := UintFieldIndex{"Foo"}
942-
975+
func compareEncoded(t *testing.T, indexer Indexer, l interface{}, r interface{}, expected int) {
943976
lBytes, err := indexer.FromArgs(l)
944977
if err != nil {
945-
t.Fatalf("unable to encode: %d", l)
978+
t.Fatalf("unable to encode %d: %s", l, err)
946979
}
947980
rBytes, err := indexer.FromArgs(r)
948981
if err != nil {
949-
t.Fatalf("unable to encode: %d", r)
982+
t.Fatalf("unable to encode %d: %s", r, err)
950983
}
951984

952985
if bytes.Compare(lBytes, rBytes) != expected {
@@ -1113,21 +1146,19 @@ func TestFieldSetIndex_FromArgs(t *testing.T) {
11131146
}
11141147
}
11151148

1116-
var (
1117-
// A conditional that checks if TestObject.Bar == 42
1118-
conditional = func(obj interface{}) (bool, error) {
1119-
test, ok := obj.(*TestObject)
1120-
if !ok {
1121-
return false, fmt.Errorf("Expect only TestObj types")
1122-
}
1123-
1124-
if test.Bar != 42 {
1125-
return false, nil
1126-
}
1149+
// A conditional that checks if TestObject.Bar == 42
1150+
var conditional = func(obj interface{}) (bool, error) {
1151+
test, ok := obj.(*TestObject)
1152+
if !ok {
1153+
return false, fmt.Errorf("Expect only TestObj types")
1154+
}
11271155

1128-
return true, nil
1156+
if test.Bar != 42 {
1157+
return false, nil
11291158
}
1130-
)
1159+
1160+
return true, nil
1161+
}
11311162

11321163
func TestConditionalIndex_FromObject(t *testing.T) {
11331164
obj := testObj()

0 commit comments

Comments
 (0)