Skip to content

Commit 42aa407

Browse files
committed
refactor to use list instead of array
1 parent c0ba0ba commit 42aa407

3 files changed

Lines changed: 544 additions & 0 deletions

File tree

list.go

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
package bogo
2+
3+
import (
4+
"bytes"
5+
"fmt"
6+
"reflect"
7+
)
8+
9+
func encodeList(list any) ([]byte, error) {
10+
v := reflect.ValueOf(list)
11+
12+
if v.Kind() != reflect.Slice && v.Kind() != reflect.Array {
13+
return nil, wrapError(arrEncErr, "type is not a list type")
14+
}
15+
16+
buf := bytes.Buffer{}
17+
18+
for i := 0; i < v.Len(); i++ {
19+
elem := v.Index(i)
20+
data, err := encode(elem.Interface())
21+
if err != nil {
22+
return nil, wrapError(arrEncErr, "error encoding element in list", err.Error())
23+
}
24+
buf.Write(data)
25+
}
26+
data := buf.Bytes()
27+
dataLen := len(data)
28+
encodedLengthData, err := encodeUint(uint64(dataLen))
29+
if err != nil {
30+
return nil, wrapError(arrEncErr, "error encoding list length", err.Error())
31+
}
32+
// remove the type information
33+
encodedLengthData = encodedLengthData[1:]
34+
35+
buf2 := bytes.Buffer{}
36+
buf2.WriteByte(TypeUntypedList)
37+
buf2.Write(encodedLengthData)
38+
buf2.Write(data)
39+
return buf2.Bytes(), nil
40+
}
41+
42+
func decodeList(data []byte, v any) error {
43+
rv := reflect.ValueOf(v)
44+
if rv.Kind() != reflect.Pointer || rv.IsNil() {
45+
return wrapError(arrDecErr, fmt.Sprintf("invalid decoder destination, %q", reflect.TypeOf(v)))
46+
}
47+
if rv.Kind() == reflect.Array || rv.Kind() == reflect.Slice {
48+
return wrapError(arrDecErr, fmt.Sprintf("incompatible types %q", rv.Kind()))
49+
}
50+
elem := rv.Elem()
51+
52+
i := 0
53+
computeDataSize := func() (uint64, error) {
54+
i++
55+
sizeLen := uint64(data[i])
56+
i++
57+
sizeLenEndI := i + int(sizeLen)
58+
size, err := decodeUint(data[i:sizeLenEndI])
59+
if err != nil {
60+
return 0, wrapError(arrDecErr, err.Error())
61+
}
62+
i = sizeLenEndI
63+
return size, nil
64+
}
65+
66+
for i < len(data) {
67+
var entryVal reflect.Value
68+
entryType := Type(data[i])
69+
switch entryType {
70+
case TypeUntypedList:
71+
size, err := computeDataSize()
72+
if err != nil {
73+
return err
74+
}
75+
var zeroSlice []any
76+
err = decodeList(data[i:i+int(size)], &zeroSlice)
77+
if err != nil {
78+
return wrapError(arrDecErr, err.Error())
79+
}
80+
i += int(size)
81+
entryVal = reflect.ValueOf(zeroSlice)
82+
case TypeNull:
83+
entryVal = reflect.Zero(reflect.TypeOf((*any)(nil)).Elem())
84+
i++
85+
case TypeBoolTrue:
86+
entryVal = reflect.ValueOf(true)
87+
i++
88+
case TypeBoolFalse:
89+
entryVal = reflect.ValueOf(false)
90+
i++
91+
case TypeByte:
92+
entryVal = reflect.ValueOf(data[i])
93+
i++
94+
case TypeString:
95+
size, err := computeDataSize()
96+
if err != nil {
97+
return err
98+
}
99+
entryVal = reflect.ValueOf(string(data[i : i+int(size)]))
100+
i += int(size)
101+
case TypeInt:
102+
i++
103+
sizeLen := uint64(data[i])
104+
i++
105+
endI := i + int(sizeLen)
106+
n, err := decodeInt(data[i:endI])
107+
if err != nil {
108+
return wrapError(arrDecErr, err.Error())
109+
}
110+
entryVal = reflect.ValueOf(n)
111+
i = endI
112+
case TypeUint:
113+
i++
114+
sizeLen := uint64(data[i])
115+
i++
116+
endI := i + int(sizeLen)
117+
n, err := decodeUint(data[i:endI])
118+
if err != nil {
119+
return wrapError(arrDecErr, err.Error())
120+
}
121+
entryVal = reflect.ValueOf(n)
122+
i = endI
123+
case TypeFloat:
124+
i++
125+
sizeLen := uint64(data[i])
126+
i++
127+
endI := i + int(sizeLen)
128+
n, err := decodeFloat(data[i:endI])
129+
if err != nil {
130+
return wrapError(arrDecErr, err.Error())
131+
}
132+
entryVal = reflect.ValueOf(n)
133+
134+
i = endI
135+
case TypeObject:
136+
// Read object size
137+
size, err := computeDataSize()
138+
if err != nil {
139+
return err
140+
}
141+
142+
// Decode object
143+
objData := data[i : i+int(size)]
144+
obj, err := decodeObject(objData[2:]) // Skip type and size bytes
145+
if err != nil {
146+
return wrapError(arrDecErr, "failed to decode object", err.Error())
147+
}
148+
149+
entryVal = reflect.ValueOf(obj)
150+
i += int(size)
151+
case TypeBlob:
152+
// Read blob size
153+
size, err := computeDataSize()
154+
if err != nil {
155+
return err
156+
}
157+
158+
// Decode blob
159+
blobData := data[i : i+int(size)]
160+
blob, err := decodeBlob(blobData[2:]) // Skip type and size bytes
161+
if err != nil {
162+
return wrapError(arrDecErr, "failed to decode blob", err.Error())
163+
}
164+
165+
entryVal = reflect.ValueOf(blob)
166+
i += int(size)
167+
case TypeTimestamp:
168+
// Read timestamp (fixed 8 bytes)
169+
if i+9 > len(data) { // 1 type + 8 data bytes
170+
return wrapError(arrDecErr, "insufficient data for timestamp")
171+
}
172+
173+
timestamp, err := decodeTimestamp(data[i+1 : i+9])
174+
if err != nil {
175+
return wrapError(arrDecErr, "failed to decode timestamp", err.Error())
176+
}
177+
178+
entryVal = reflect.ValueOf(timestamp)
179+
i += 9
180+
default:
181+
return wrapError(arrDecErr, fmt.Sprintf("unsupported list element type: %d", entryType))
182+
}
183+
184+
if !entryVal.Type().AssignableTo(elem.Type().Elem()) {
185+
return wrapError(arrDecErr, "item type does not match slice element type")
186+
}
187+
elem.Set(reflect.Append(elem, entryVal))
188+
}
189+
190+
return nil
191+
}

list_test.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package bogo
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/assert"
7+
"github.com/stretchr/testify/require"
8+
)
9+
10+
func TestListEncoding(t *testing.T) {
11+
expected := []any{1, true, nil, "yes", 0.5, uint16(120), []any{7, 8, 9}, -98.0005}
12+
res, err := encodeList(expected)
13+
require.NoError(t, err)
14+
15+
assert.Equal(t, TypeUntypedList, int(res[0]))
16+
arrLen := uint64(res[1])
17+
size, err := decodeUint(res[2 : 2+arrLen])
18+
require.NoError(t, err)
19+
assert.Equal(t, size, uint64(len(res[2+arrLen:])))
20+
21+
var actual []any
22+
err = decodeList(res[3:], &actual)
23+
require.NoError(t, err)
24+
25+
assert.Equal(t, int64(expected[0].(int)), actual[0])
26+
assert.Equal(t, expected[1], actual[1])
27+
assert.Equal(t, expected[2], actual[2])
28+
assert.Equal(t, expected[3], actual[3])
29+
assert.Equal(t, expected[4], actual[4])
30+
assert.Equal(t, uint64(expected[5].(uint16)), actual[5])
31+
32+
expInner := expected[6].([]interface{})
33+
actInner := actual[6].([]interface{})
34+
for i := range expInner {
35+
assert.Equal(t, int64(expInner[i].(int)), actInner[i])
36+
}
37+
38+
assert.Equal(t, expected[7], actual[7])
39+
}

0 commit comments

Comments
 (0)