Skip to content

Commit 20e9cfc

Browse files
committed
Add experimental Deserializer interface
1 parent a1069d8 commit 20e9cfc

File tree

3 files changed

+162
-0
lines changed

3 files changed

+162
-0
lines changed

decoder.go

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,18 @@ func (d *decoder) decode(offset uint, result reflect.Value, depth int) (uint, er
5656
return d.decodeFromType(typeNum, size, newOffset, result, depth+1)
5757
}
5858

59+
func (d *decoder) decodeToDeserializer(offset uint, dser Deserializer, depth int) (uint, error) {
60+
if depth > maximumDataStructureDepth {
61+
return 0, newInvalidDatabaseError("exceeded maximum data structure depth; database is likely corrupt")
62+
}
63+
typeNum, size, newOffset, err := d.decodeCtrlData(offset)
64+
if err != nil {
65+
return 0, err
66+
}
67+
68+
return d.decodeFromTypeToDeserializer(typeNum, size, newOffset, dser, depth+1)
69+
}
70+
5971
func (d *decoder) decodeCtrlData(offset uint) (dataType, uint, uint, error) {
6072
newOffset := offset + 1
6173
if offset >= uint(len(d.buffer)) {
@@ -157,6 +169,68 @@ func (d *decoder) decodeFromType(
157169
}
158170
}
159171

172+
func (d *decoder) decodeFromTypeToDeserializer(
173+
dtype dataType,
174+
size uint,
175+
offset uint,
176+
dser Deserializer,
177+
depth int,
178+
) (uint, error) {
179+
// For these types, size has a special meaning
180+
switch dtype {
181+
case _Bool:
182+
v, offset := d.decodeBool(size, offset)
183+
return offset, dser.Bool(v)
184+
case _Map:
185+
return d.decodeMapToDeserializer(size, offset, dser, depth)
186+
case _Pointer:
187+
pointer, newOffset, err := d.decodePointer(size, offset)
188+
if err != nil {
189+
return 0, err
190+
}
191+
_, err = d.decodeToDeserializer(pointer, dser, depth)
192+
return newOffset, err
193+
case _Slice:
194+
return d.decodeSliceToDeserializer(size, offset, dser, depth)
195+
}
196+
197+
// For the remaining types, size is the byte size
198+
if offset+size > uint(len(d.buffer)) {
199+
return 0, newOffsetError()
200+
}
201+
switch dtype {
202+
case _Bytes:
203+
v, offset := d.decodeBytes(size, offset)
204+
return offset, dser.Bytes(v)
205+
case _Float32:
206+
v, offset := d.decodeFloat32(size, offset)
207+
return offset, dser.Float32(v)
208+
case _Float64:
209+
v, offset := d.decodeFloat64(size, offset)
210+
return offset, dser.Float64(v)
211+
case _Int32:
212+
v, offset := d.decodeInt(size, offset)
213+
return offset, dser.Int32(int32(v))
214+
case _String:
215+
v, offset := d.decodeString(size, offset)
216+
return offset, dser.String(v)
217+
case _Uint16:
218+
v, offset := d.decodeUint(size, offset)
219+
return offset, dser.Uint16(uint16(v))
220+
case _Uint32:
221+
v, offset := d.decodeUint(size, offset)
222+
return offset, dser.Uint32(uint32(v))
223+
case _Uint64:
224+
v, offset := d.decodeUint(size, offset)
225+
return offset, dser.Uint64(uint64(v))
226+
case _Uint128:
227+
v, offset := d.decodeUint128(size, offset)
228+
return offset, dser.Uint128(v)
229+
default:
230+
return 0, newInvalidDatabaseError("unknown type: %d", dtype)
231+
}
232+
}
233+
160234
func (d *decoder) unmarshalBool(size, offset uint, result reflect.Value) (uint, error) {
161235
if size > 1 {
162236
return 0, newInvalidDatabaseError("the MaxMind DB file's data section contains bad data (bool size of %v)", size)
@@ -199,6 +273,7 @@ func (d *decoder) indirect(result reflect.Value) reflect.Value {
199273
if result.IsNil() {
200274
result.Set(reflect.New(result.Type().Elem()))
201275
}
276+
202277
result = result.Elem()
203278
}
204279
return result
@@ -486,6 +561,35 @@ func (d *decoder) decodeMap(
486561
return offset, nil
487562
}
488563

564+
func (d *decoder) decodeMapToDeserializer(
565+
size uint,
566+
offset uint,
567+
dser Deserializer,
568+
depth int,
569+
) (uint, error) {
570+
err := dser.StartMap(size)
571+
if err != nil {
572+
return 0, err
573+
}
574+
for i := uint(0); i < size; i++ {
575+
// TODO - implement key/value skipping?
576+
offset, err = d.decodeToDeserializer(offset, dser, depth)
577+
if err != nil {
578+
return 0, err
579+
}
580+
581+
offset, err = d.decodeToDeserializer(offset, dser, depth)
582+
if err != nil {
583+
return 0, err
584+
}
585+
}
586+
err = dser.End()
587+
if err != nil {
588+
return 0, err
589+
}
590+
return offset, nil
591+
}
592+
489593
func (d *decoder) decodePointer(
490594
size uint,
491595
offset uint,
@@ -538,6 +642,29 @@ func (d *decoder) decodeSlice(
538642
return offset, nil
539643
}
540644

645+
func (d *decoder) decodeSliceToDeserializer(
646+
size uint,
647+
offset uint,
648+
dser Deserializer,
649+
depth int,
650+
) (uint, error) {
651+
err := dser.StartSlice(size)
652+
if err != nil {
653+
return 0, err
654+
}
655+
for i := uint(0); i < size; i++ {
656+
offset, err = d.decodeToDeserializer(offset, dser, depth)
657+
if err != nil {
658+
return 0, err
659+
}
660+
}
661+
err = dser.End()
662+
if err != nil {
663+
return 0, err
664+
}
665+
return offset, nil
666+
}
667+
541668
func (d *decoder) decodeString(size, offset uint) (string, uint) {
542669
newOffset := offset + size
543670
return string(d.buffer[offset:newOffset]), newOffset

deserializer.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package maxminddb
2+
3+
import "math/big"
4+
5+
// Deserializer is an interface for a type that deserializes an MaxMind DB
6+
// data record to some other type. This exists as an alternative to the
7+
// standard reflection API.
8+
//
9+
// This is fundamentally different than the Unmarshaler interface that
10+
// several packages provide. A Deserializer will generally create the
11+
// final struct or value rather than unmarshaling to itself.
12+
//
13+
// This interface and the associated unmarshaling code is EXPERIMENTAL!
14+
// It is not currently covered by any Semantic Versioning guarantees.
15+
// Use at your own risk.
16+
type Deserializer interface {
17+
StartSlice(uint) error
18+
StartMap(uint) error
19+
End() error
20+
String(string) error
21+
Float64(float64) error
22+
Bytes([]byte) error
23+
Uint16(uint16) error
24+
Uint32(uint32) error
25+
Int32(int32) error
26+
Uint64(uint64) error
27+
Uint128(*big.Int) error
28+
Bool(bool) error
29+
Float32(float32) error
30+
}

reader.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,11 @@ func (r *Reader) decode(offset uintptr, result interface{}) error {
227227
return errors.New("result param must be a pointer")
228228
}
229229

230+
if dser, ok := result.(Deserializer); ok {
231+
_, err := r.decoder.decodeToDeserializer(uint(offset), dser, 0)
232+
return err
233+
}
234+
230235
_, err := r.decoder.decode(uint(offset), rv, 0)
231236
return err
232237
}

0 commit comments

Comments
 (0)