From bb850ddb5d9a159c55cc3b4d421b90a2c32ee52c Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Thu, 6 May 2021 17:53:54 +0200 Subject: [PATCH 1/9] PackAny(): pack []byte as string, not array of numbers --- pkg/icingadb/objectpacker/objectpacker.go | 43 +++++++++++++------ .../objectpacker/objectpacker_test.go | 10 +++++ 2 files changed, 41 insertions(+), 12 deletions(-) diff --git a/pkg/icingadb/objectpacker/objectpacker.go b/pkg/icingadb/objectpacker/objectpacker.go index dcd86ae8..be481ddf 100644 --- a/pkg/icingadb/objectpacker/objectpacker.go +++ b/pkg/icingadb/objectpacker/objectpacker.go @@ -19,7 +19,7 @@ func PackAny(in interface{}, out io.Writer) error { // packValue does the actual job of packAny and just exists for recursion w/o unneccessary reflect.ValueOf calls. func packValue(in reflect.Value, out io.Writer) error { - switch in.Kind() { + switch kind := in.Kind(); kind { case reflect.Invalid: // nil _, err := out.Write([]byte{0}) return err @@ -38,6 +38,21 @@ func packValue(in reflect.Value, out io.Writer) error { case reflect.Float32, reflect.Float64: return packFloat64(in.Float(), out) case reflect.Array, reflect.Slice: + if typ := in.Type(); typ.Elem().Kind() == reflect.Uint8 { + if kind == reflect.Array { + if !in.CanAddr() { + vNewElem := reflect.New(typ).Elem() + vNewElem.Set(in) + in = vNewElem + } + + in = in.Slice(0, in.Len()) + } + + // Pack []byte as string, not array of numbers. + return packString(in.Interface().([]uint8), out) + } + if _, err := out.Write([]byte{5}); err != nil { return err } @@ -107,17 +122,7 @@ func packValue(in reflect.Value, out io.Writer) error { return packValue(in.Elem(), out) } case reflect.String: - if _, err := out.Write([]byte{4}); err != nil { - return err - } - - b := []byte(in.String()) - if err := binary.Write(out, binary.BigEndian, uint64(len(b))); err != nil { - return err - } - - _, err := out.Write(b) - return err + return packString([]byte(in.String()), out) default: panic("bad type: " + in.Kind().String()) } @@ -131,3 +136,17 @@ func packFloat64(in float64, out io.Writer) error { return binary.Write(out, binary.BigEndian, in) } + +// packString deduplicates string packing of multiple locations in packValue. +func packString(in []byte, out io.Writer) error { + if _, err := out.Write([]byte{4}); err != nil { + return err + } + + if err := binary.Write(out, binary.BigEndian, uint64(len(in))); err != nil { + return err + } + + _, err := out.Write(in) + return err +} diff --git a/pkg/icingadb/objectpacker/objectpacker_test.go b/pkg/icingadb/objectpacker/objectpacker_test.go index 5d36cecb..7b15ccf1 100644 --- a/pkg/icingadb/objectpacker/objectpacker_test.go +++ b/pkg/icingadb/objectpacker/objectpacker_test.go @@ -121,6 +121,16 @@ func TestPackAny(t *testing.T) { assertPackAny(t, "a", []byte{4, 0, 0, 0, 0, 0, 0, 0, 1, 'a'}) assertPackAny(t, "ä", []byte{4, 0, 0, 0, 0, 0, 0, 0, 2, 0xc3, 0xa4}) + { + var binary [256]byte + for i := range binary { + binary[i] = byte(i) + } + + assertPackAny(t, binary, append([]byte{4, 0, 0, 0, 0, 0, 0, 1, 0}, binary[:]...)) + assertPackAny(t, binary[:], append([]byte{4, 0, 0, 0, 0, 0, 0, 1, 0}, binary[:]...)) + } + assertPackAnyPanic(t, complex64(0+0i), 0) assertPackAnyPanic(t, 0+0i, 0) assertPackAnyPanic(t, make(chan struct{}, 0), 0) From ab436735935c56566f679fb9d774cc3ba5d866a6 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Thu, 6 May 2021 18:00:47 +0200 Subject: [PATCH 2/9] PackAny(): support types.Binary --- pkg/icingadb/objectpacker/objectpacker.go | 5 ++++- pkg/icingadb/objectpacker/objectpacker_test.go | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/pkg/icingadb/objectpacker/objectpacker.go b/pkg/icingadb/objectpacker/objectpacker.go index be481ddf..85c0f6fb 100644 --- a/pkg/icingadb/objectpacker/objectpacker.go +++ b/pkg/icingadb/objectpacker/objectpacker.go @@ -17,6 +17,8 @@ func PackAny(in interface{}, out io.Writer) error { return packValue(reflect.ValueOf(in), out) } +var tBytes = reflect.TypeOf([]uint8(nil)) + // packValue does the actual job of packAny and just exists for recursion w/o unneccessary reflect.ValueOf calls. func packValue(in reflect.Value, out io.Writer) error { switch kind := in.Kind(); kind { @@ -50,7 +52,8 @@ func packValue(in reflect.Value, out io.Writer) error { } // Pack []byte as string, not array of numbers. - return packString(in.Interface().([]uint8), out) + return packString(in.Convert(tBytes). // Support types.Binary + Interface().([]uint8), out) } if _, err := out.Write([]byte{5}); err != nil { diff --git a/pkg/icingadb/objectpacker/objectpacker_test.go b/pkg/icingadb/objectpacker/objectpacker_test.go index 7b15ccf1..3f7f201c 100644 --- a/pkg/icingadb/objectpacker/objectpacker_test.go +++ b/pkg/icingadb/objectpacker/objectpacker_test.go @@ -2,6 +2,7 @@ package objectpacker import ( "bytes" + "github.com/icinga/icingadb/pkg/types" "io" "testing" "unsafe" @@ -129,6 +130,7 @@ func TestPackAny(t *testing.T) { assertPackAny(t, binary, append([]byte{4, 0, 0, 0, 0, 0, 0, 1, 0}, binary[:]...)) assertPackAny(t, binary[:], append([]byte{4, 0, 0, 0, 0, 0, 0, 1, 0}, binary[:]...)) + assertPackAny(t, types.Binary(binary[:]), append([]byte{4, 0, 0, 0, 0, 0, 0, 1, 0}, binary[:]...)) } assertPackAnyPanic(t, complex64(0+0i), 0) From a66cfca9c89f5bda4b249eff238fed4bbd8a6a1c Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Fri, 7 May 2021 12:16:09 +0200 Subject: [PATCH 3/9] PackAny(): support numbers only as float64 --- pkg/icingadb/objectpacker/objectpacker.go | 21 ++++---------- .../objectpacker/objectpacker_test.go | 28 +++++++++---------- 2 files changed, 20 insertions(+), 29 deletions(-) diff --git a/pkg/icingadb/objectpacker/objectpacker.go b/pkg/icingadb/objectpacker/objectpacker.go index 85c0f6fb..073909ff 100644 --- a/pkg/icingadb/objectpacker/objectpacker.go +++ b/pkg/icingadb/objectpacker/objectpacker.go @@ -33,12 +33,12 @@ func packValue(in reflect.Value, out io.Writer) error { _, err := out.Write([]byte{1}) return err } - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - return packFloat64(float64(in.Int()), out) - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - return packFloat64(float64(in.Uint()), out) - case reflect.Float32, reflect.Float64: - return packFloat64(in.Float(), out) + case reflect.Float64: + if _, err := out.Write([]byte{3}); err != nil { + return err + } + + return binary.Write(out, binary.BigEndian, in.Float()) case reflect.Array, reflect.Slice: if typ := in.Type(); typ.Elem().Kind() == reflect.Uint8 { if kind == reflect.Array { @@ -131,15 +131,6 @@ func packValue(in reflect.Value, out io.Writer) error { } } -// packFloat64 deduplicates float packing of multiple locations in packValue. -func packFloat64(in float64, out io.Writer) error { - if _, errWr := out.Write([]byte{3}); errWr != nil { - return errWr - } - - return binary.Write(out, binary.BigEndian, in) -} - // packString deduplicates string packing of multiple locations in packValue. func packString(in []byte, out io.Writer) error { if _, err := out.Write([]byte{4}); err != nil { diff --git a/pkg/icingadb/objectpacker/objectpacker_test.go b/pkg/icingadb/objectpacker/objectpacker_test.go index 3f7f201c..5d281073 100644 --- a/pkg/icingadb/objectpacker/objectpacker_test.go +++ b/pkg/icingadb/objectpacker/objectpacker_test.go @@ -62,20 +62,20 @@ func TestPackAny(t *testing.T) { assertPackAny(t, false, []byte{1}) assertPackAny(t, true, []byte{2}) - assertPackAny(t, -42, []byte{3, 0xc0, 0x45, 0, 0, 0, 0, 0, 0}) - assertPackAny(t, int8(-42), []byte{3, 0xc0, 0x45, 0, 0, 0, 0, 0, 0}) - assertPackAny(t, int16(-42), []byte{3, 0xc0, 0x45, 0, 0, 0, 0, 0, 0}) - assertPackAny(t, int32(-42), []byte{3, 0xc0, 0x45, 0, 0, 0, 0, 0, 0}) - assertPackAny(t, int64(-42), []byte{3, 0xc0, 0x45, 0, 0, 0, 0, 0, 0}) + assertPackAnyPanic(t, -42, 0) + assertPackAnyPanic(t, int8(-42), 0) + assertPackAnyPanic(t, int16(-42), 0) + assertPackAnyPanic(t, int32(-42), 0) + assertPackAnyPanic(t, int64(-42), 0) - assertPackAny(t, uint(42), []byte{3, 0x40, 0x45, 0, 0, 0, 0, 0, 0}) - assertPackAny(t, uint8(42), []byte{3, 0x40, 0x45, 0, 0, 0, 0, 0, 0}) - assertPackAny(t, uint16(42), []byte{3, 0x40, 0x45, 0, 0, 0, 0, 0, 0}) - assertPackAny(t, uint32(42), []byte{3, 0x40, 0x45, 0, 0, 0, 0, 0, 0}) - assertPackAny(t, uint64(42), []byte{3, 0x40, 0x45, 0, 0, 0, 0, 0, 0}) - assertPackAny(t, uintptr(42), []byte{3, 0x40, 0x45, 0, 0, 0, 0, 0, 0}) + assertPackAnyPanic(t, uint(42), 0) + assertPackAnyPanic(t, uint8(42), 0) + assertPackAnyPanic(t, uint16(42), 0) + assertPackAnyPanic(t, uint32(42), 0) + assertPackAnyPanic(t, uint64(42), 0) + assertPackAnyPanic(t, uintptr(42), 0) - assertPackAny(t, float32(-42.5), []byte{3, 0xc0, 0x45, 0x40, 0, 0, 0, 0, 0}) + assertPackAnyPanic(t, float32(-42.5), 0) assertPackAny(t, -42.5, []byte{3, 0xc0, 0x45, 0x40, 0, 0, 0, 0, 0}) assertPackAny(t, []struct{}(nil), []byte{5, 0, 0, 0, 0, 0, 0, 0, 0}) @@ -107,7 +107,7 @@ func TestPackAny(t *testing.T) { 4, 0, 0, 0, 0, 0, 0, 0, 0, }) - assertPackAny(t, map[string]uint8{"": 42}, []byte{ + assertPackAny(t, map[string]float64{"": 42}, []byte{ 6, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0x40, 0x45, 0, 0, 0, 0, 0, 0, @@ -116,7 +116,7 @@ func TestPackAny(t *testing.T) { assertPackAnyPanic(t, map[struct{}]struct{}{{}: {}}, 9) assertPackAny(t, (*int)(nil), []byte{0}) - assertPackAny(t, new(int), []byte{3, 0, 0, 0, 0, 0, 0, 0, 0}) + assertPackAny(t, new(float64), []byte{3, 0, 0, 0, 0, 0, 0, 0, 0}) assertPackAny(t, "", []byte{4, 0, 0, 0, 0, 0, 0, 0, 0}) assertPackAny(t, "a", []byte{4, 0, 0, 0, 0, 0, 0, 0, 1, 'a'}) From 23127e324571aa0dc91952fa2aabea53998f65ba Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Fri, 7 May 2021 12:38:18 +0200 Subject: [PATCH 4/9] PackAny(): disallow types recursively more strictly --- pkg/icingadb/objectpacker/objectpacker.go | 20 ++++++++++++++++++- .../objectpacker/objectpacker_test.go | 10 +++++----- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/pkg/icingadb/objectpacker/objectpacker.go b/pkg/icingadb/objectpacker/objectpacker.go index 073909ff..b9866932 100644 --- a/pkg/icingadb/objectpacker/objectpacker.go +++ b/pkg/icingadb/objectpacker/objectpacker.go @@ -71,6 +71,11 @@ func packValue(in reflect.Value, out io.Writer) error { } } + if l < 1 { + // Disallow (panic) some types in array/slice values (recursively), too - even if none present + _ = packValue(reflect.Zero(in.Type().Elem()), ioutil.Discard) + } + return nil case reflect.Interface: return packValue(in.Elem(), out) @@ -117,10 +122,23 @@ func packValue(in reflect.Value, out io.Writer) error { } } + if l < 1 { + typ := in.Type() + + // Disallow (panic) some types in map keys and values (recursively), too - even if none present + _ = packValue(reflect.Zero(typ.Key()), ioutil.Discard) + _ = packValue(reflect.Zero(typ.Elem()), ioutil.Discard) + } + return nil case reflect.Ptr: if in.IsNil() { - return packValue(reflect.Value{}, out) + err := packValue(reflect.Value{}, out) + + // Disallow (panic) some types in referenced value (recursively), too - even if none present + _ = packValue(reflect.Zero(in.Type().Elem()), ioutil.Discard) + + return err } else { return packValue(in.Elem(), out) } diff --git a/pkg/icingadb/objectpacker/objectpacker_test.go b/pkg/icingadb/objectpacker/objectpacker_test.go index 5d281073..039b449e 100644 --- a/pkg/icingadb/objectpacker/objectpacker_test.go +++ b/pkg/icingadb/objectpacker/objectpacker_test.go @@ -78,8 +78,8 @@ func TestPackAny(t *testing.T) { assertPackAnyPanic(t, float32(-42.5), 0) assertPackAny(t, -42.5, []byte{3, 0xc0, 0x45, 0x40, 0, 0, 0, 0, 0}) - assertPackAny(t, []struct{}(nil), []byte{5, 0, 0, 0, 0, 0, 0, 0, 0}) - assertPackAny(t, []struct{}{}, []byte{5, 0, 0, 0, 0, 0, 0, 0, 0}) + assertPackAnyPanic(t, []struct{}(nil), 9) + assertPackAnyPanic(t, []struct{}{}, 9) assertPackAny(t, []interface{}{nil, true, -42.5}, []byte{ 5, 0, 0, 0, 0, 0, 0, 0, 3, @@ -96,8 +96,8 @@ func TestPackAny(t *testing.T) { assertPackAnyPanic(t, []interface{}{0 + 0i}, 9) - assertPackAny(t, map[struct{}]struct{}(nil), []byte{6, 0, 0, 0, 0, 0, 0, 0, 0}) - assertPackAny(t, map[struct{}]struct{}{}, []byte{6, 0, 0, 0, 0, 0, 0, 0, 0}) + assertPackAnyPanic(t, map[struct{}]struct{}(nil), 9) + assertPackAnyPanic(t, map[struct{}]struct{}{}, 9) assertPackAny(t, map[interface{}]interface{}{true: "", "nil": -42.5}, []byte{ 6, 0, 0, 0, 0, 0, 0, 0, 2, @@ -115,7 +115,7 @@ func TestPackAny(t *testing.T) { assertPackAnyPanic(t, map[struct{}]struct{}{{}: {}}, 9) - assertPackAny(t, (*int)(nil), []byte{0}) + assertPackAnyPanic(t, (*int)(nil), 0) assertPackAny(t, new(float64), []byte{3, 0, 0, 0, 0, 0, 0, 0, 0}) assertPackAny(t, "", []byte{4, 0, 0, 0, 0, 0, 0, 0, 0}) From 9df2f9d7e62aa948593212d3afbda776e8ef5ea4 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Mon, 10 May 2021 12:13:24 +0200 Subject: [PATCH 5/9] PackAny(): panic() explicitly, not due to a programming error --- pkg/icingadb/objectpacker/objectpacker.go | 3 ++- pkg/icingadb/objectpacker/objectpacker_test.go | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/pkg/icingadb/objectpacker/objectpacker.go b/pkg/icingadb/objectpacker/objectpacker.go index b9866932..23abd2ca 100644 --- a/pkg/icingadb/objectpacker/objectpacker.go +++ b/pkg/icingadb/objectpacker/objectpacker.go @@ -17,6 +17,7 @@ func PackAny(in interface{}, out io.Writer) error { return packValue(reflect.ValueOf(in), out) } +var tByte = reflect.TypeOf(byte(0)) var tBytes = reflect.TypeOf([]uint8(nil)) // packValue does the actual job of packAny and just exists for recursion w/o unneccessary reflect.ValueOf calls. @@ -40,7 +41,7 @@ func packValue(in reflect.Value, out io.Writer) error { return binary.Write(out, binary.BigEndian, in.Float()) case reflect.Array, reflect.Slice: - if typ := in.Type(); typ.Elem().Kind() == reflect.Uint8 { + if typ := in.Type(); typ.Elem() == tByte { if kind == reflect.Array { if !in.CanAddr() { vNewElem := reflect.New(typ).Elem() diff --git a/pkg/icingadb/objectpacker/objectpacker_test.go b/pkg/icingadb/objectpacker/objectpacker_test.go index 039b449e..019c2a4d 100644 --- a/pkg/icingadb/objectpacker/objectpacker_test.go +++ b/pkg/icingadb/objectpacker/objectpacker_test.go @@ -133,6 +133,11 @@ func TestPackAny(t *testing.T) { assertPackAny(t, types.Binary(binary[:]), append([]byte{4, 0, 0, 0, 0, 0, 0, 1, 0}, binary[:]...)) } + { + type myByte byte + assertPackAnyPanic(t, []myByte(nil), 9) + } + assertPackAnyPanic(t, complex64(0+0i), 0) assertPackAnyPanic(t, 0+0i, 0) assertPackAnyPanic(t, make(chan struct{}, 0), 0) From 1b4b20746506e6f67f5b1bd98276aff88e37eff5 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Mon, 10 May 2021 12:35:23 +0200 Subject: [PATCH 6/9] PackAny(): enhance code docs --- pkg/icingadb/objectpacker/objectpacker.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/pkg/icingadb/objectpacker/objectpacker.go b/pkg/icingadb/objectpacker/objectpacker.go index 23abd2ca..9c810fff 100644 --- a/pkg/icingadb/objectpacker/objectpacker.go +++ b/pkg/icingadb/objectpacker/objectpacker.go @@ -72,8 +72,9 @@ func packValue(in reflect.Value, out io.Writer) error { } } + // If there aren't any values to pack, ... if l < 1 { - // Disallow (panic) some types in array/slice values (recursively), too - even if none present + // ... create one and pack it - panics on disallowed type. _ = packValue(reflect.Zero(in.Type().Elem()), ioutil.Discard) } @@ -100,7 +101,7 @@ func packValue(in reflect.Value, out io.Writer) error { { iter := in.MapRange() for iter.Next() { - // Disallow (panic) some types in map keys (recursively), too + // Not just stringify the key (below), but also pack it (here) - panics on disallowed type. _ = packValue(iter.Key(), ioutil.Discard) sorted = append(sorted, kv{[]byte(fmt.Sprint(iter.Key().Interface())), iter.Value()}) @@ -123,10 +124,11 @@ func packValue(in reflect.Value, out io.Writer) error { } } + // If there aren't any key-value pairs to pack, ... if l < 1 { typ := in.Type() - // Disallow (panic) some types in map keys and values (recursively), too - even if none present + // ... create one and pack it - panics on disallowed type. _ = packValue(reflect.Zero(typ.Key()), ioutil.Discard) _ = packValue(reflect.Zero(typ.Elem()), ioutil.Discard) } @@ -136,7 +138,7 @@ func packValue(in reflect.Value, out io.Writer) error { if in.IsNil() { err := packValue(reflect.Value{}, out) - // Disallow (panic) some types in referenced value (recursively), too - even if none present + // Create a fictive referenced value and pack it - panics on disallowed type. _ = packValue(reflect.Zero(in.Type().Elem()), ioutil.Discard) return err From c8fa629f3290305b2325915edc2d6b25fc0a8c08 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Mon, 10 May 2021 12:38:54 +0200 Subject: [PATCH 7/9] PackAny(): restore unit test case --- pkg/icingadb/objectpacker/objectpacker_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/icingadb/objectpacker/objectpacker_test.go b/pkg/icingadb/objectpacker/objectpacker_test.go index 019c2a4d..d495353a 100644 --- a/pkg/icingadb/objectpacker/objectpacker_test.go +++ b/pkg/icingadb/objectpacker/objectpacker_test.go @@ -115,6 +115,7 @@ func TestPackAny(t *testing.T) { assertPackAnyPanic(t, map[struct{}]struct{}{{}: {}}, 9) + assertPackAny(t, (*string)(nil), []byte{0}) assertPackAnyPanic(t, (*int)(nil), 0) assertPackAny(t, new(float64), []byte{3, 0, 0, 0, 0, 0, 0, 0, 0}) From a6da8ab90a86684fcb9908365d522261e502e46a Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Mon, 10 May 2021 13:15:34 +0200 Subject: [PATCH 8/9] PackAny(): pack [I]byte as string in map keys as well --- pkg/icingadb/objectpacker/objectpacker.go | 26 ++++++++++++++++--- .../objectpacker/objectpacker_test.go | 6 +++++ 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/pkg/icingadb/objectpacker/objectpacker.go b/pkg/icingadb/objectpacker/objectpacker.go index 9c810fff..1da7939a 100644 --- a/pkg/icingadb/objectpacker/objectpacker.go +++ b/pkg/icingadb/objectpacker/objectpacker.go @@ -101,10 +101,30 @@ func packValue(in reflect.Value, out io.Writer) error { { iter := in.MapRange() for iter.Next() { - // Not just stringify the key (below), but also pack it (here) - panics on disallowed type. - _ = packValue(iter.Key(), ioutil.Discard) + var packedKey []byte + if key := iter.Key(); key.Kind() == reflect.Array { + if typ := key.Type(); typ.Elem() == tByte { + if !key.CanAddr() { + vNewElem := reflect.New(typ).Elem() + vNewElem.Set(key) + key = vNewElem + } - sorted = append(sorted, kv{[]byte(fmt.Sprint(iter.Key().Interface())), iter.Value()}) + packedKey = key.Slice(0, key.Len()).Interface().([]byte) + } else { + // Not just stringify the key (below), but also pack it (here) - panics on disallowed type. + _ = packValue(iter.Key(), ioutil.Discard) + + packedKey = []byte(fmt.Sprint(key.Interface())) + } + } else { + // Not just stringify the key (below), but also pack it (here) - panics on disallowed type. + _ = packValue(iter.Key(), ioutil.Discard) + + packedKey = []byte(fmt.Sprint(key.Interface())) + } + + sorted = append(sorted, kv{packedKey, iter.Value()}) } } diff --git a/pkg/icingadb/objectpacker/objectpacker_test.go b/pkg/icingadb/objectpacker/objectpacker_test.go index d495353a..6afb5882 100644 --- a/pkg/icingadb/objectpacker/objectpacker_test.go +++ b/pkg/icingadb/objectpacker/objectpacker_test.go @@ -113,6 +113,12 @@ func TestPackAny(t *testing.T) { 3, 0x40, 0x45, 0, 0, 0, 0, 0, 0, }) + assertPackAny(t, map[[1]byte]bool{[1]byte{42}: true}, []byte{ + 6, 0, 0, 0, 0, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 1, 42, + 2, + }) + assertPackAnyPanic(t, map[struct{}]struct{}{{}: {}}, 9) assertPackAny(t, (*string)(nil), []byte{0}) From 40c18e3492ea1dd7b5e741ed5375379eca9a23d3 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Mon, 10 May 2021 14:01:52 +0200 Subject: [PATCH 9/9] PackAny(): Golangify spec doc --- pkg/icingadb/objectpacker/objectpacker.go | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/pkg/icingadb/objectpacker/objectpacker.go b/pkg/icingadb/objectpacker/objectpacker.go index 1da7939a..44c6b92b 100644 --- a/pkg/icingadb/objectpacker/objectpacker.go +++ b/pkg/icingadb/objectpacker/objectpacker.go @@ -12,7 +12,22 @@ import ( // PackAny packs any JSON-encodable value (ex. structs, also ignores interfaces like encoding.TextMarshaler) // to a BSON-similar format suitable for consistent hashing. Spec: -// https://github.com/Icinga/icinga2/blob/2cb995e/lib/base/object-packer.cpp#L222-L231 +// +// PackAny(nil) => 0x0 +// PackAny(false) => 0x1 +// PackAny(true) => 0x2 +// PackAny(float64(42)) => 0x3 ieee754_binary64_bigendian(42) +// PackAny("exämple") => 0x4 uint64_bigendian(len([]byte("exämple"))) []byte("exämple") +// PackAny([]uint8{0x42}) => 0x4 uint64_bigendian(len([]uint8{0x42})) []uint8{0x42} +// PackAny([1]uint8{0x42}) => 0x4 uint64_bigendian(len([1]uint8{0x42})) [1]uint8{0x42} +// PackAny([]T{x,y}) => 0x5 uint64_bigendian(len([]T{x,y})) PackAny(x) PackAny(y) +// PackAny(map[K]V{x:y}) => 0x6 uint64_bigendian(len(map[K]V{x:y})) len(map_key(x)) map_key(x) PackAny(y) +// PackAny((*T)(nil)) => 0x0 +// PackAny((*T)(0x42)) => PackAny(*(*T)(0x42)) +// PackAny(x) => panic() +// +// map_key([1]uint8{0x42}) => [1]uint8{0x42} +// map_key(x) => []byte(fmt.Sprint(x)) func PackAny(in interface{}, out io.Writer) error { return packValue(reflect.ValueOf(in), out) }