Skip to content

Commit 35157ea

Browse files
committed
Add a facility allowing manual control of the decoding
1 parent 63332e3 commit 35157ea

File tree

2 files changed

+530
-0
lines changed

2 files changed

+530
-0
lines changed

decoder_raw.go

+394
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,394 @@
1+
package maxminddb
2+
3+
import (
4+
"fmt"
5+
)
6+
7+
// Decoder allows decoding of a single value stored at a specific offset
8+
// in the database.
9+
type Decoder struct {
10+
d decoder
11+
offset uint
12+
13+
hasNextOffset bool
14+
nextOffset uint
15+
}
16+
17+
// Decoder returns a decoder for a single value stored at offset.
18+
func (r *Reader) Decoder(offset uintptr) *Decoder {
19+
return &Decoder{
20+
d: r.decoder,
21+
offset: uint(offset),
22+
}
23+
}
24+
25+
func (d *Decoder) reset(offset uint) {
26+
d.offset = offset
27+
d.hasNextOffset = false
28+
d.nextOffset = 0
29+
}
30+
31+
func (d *Decoder) next(numberToSkip uint) error {
32+
if numberToSkip > 1 || !d.hasNextOffset {
33+
offset, err := d.d.nextValueOffset(d.offset, numberToSkip)
34+
if err != nil {
35+
return err
36+
}
37+
38+
d.reset(offset)
39+
return nil
40+
}
41+
42+
d.reset(d.nextOffset)
43+
return nil
44+
}
45+
46+
func (d *Decoder) setNextOffset(offset uint) {
47+
if !d.hasNextOffset {
48+
d.hasNextOffset = true
49+
d.nextOffset = offset
50+
}
51+
}
52+
53+
func (d *Decoder) new(offset uint) *Decoder {
54+
return &Decoder{
55+
d: d.d,
56+
offset: offset,
57+
}
58+
}
59+
60+
func unexpectedTypeErr(expectedType, actualType dataType) error {
61+
return fmt.Errorf("unexpected type %d, expected %d", actualType, expectedType)
62+
}
63+
64+
func (d *Decoder) decodeCtrlDataAndFollow(expectedType dataType) (uint, uint, error) {
65+
dataOffset := d.offset
66+
for {
67+
var typeNum dataType
68+
var size uint
69+
var err error
70+
typeNum, size, dataOffset, err = d.d.decodeCtrlData(dataOffset)
71+
if err != nil {
72+
return 0, 0, err
73+
}
74+
75+
if typeNum == _Pointer {
76+
var nextOffset uint
77+
dataOffset, nextOffset, err = d.d.decodePointer(size, dataOffset)
78+
if err != nil {
79+
return 0, 0, err
80+
}
81+
d.setNextOffset(nextOffset)
82+
continue
83+
}
84+
85+
if typeNum != expectedType {
86+
return 0, 0, unexpectedTypeErr(expectedType, typeNum)
87+
}
88+
89+
return size, dataOffset, nil
90+
}
91+
}
92+
93+
// DecodeBool decodes the value pointed by the decoder as a bool.
94+
//
95+
// Returns an error if the database is malformed or if the pointed value is not a bool.
96+
func (d *Decoder) DecodeBool() (bool, error) {
97+
size, offset, err := d.decodeCtrlDataAndFollow(_Bool)
98+
if err != nil {
99+
return false, err
100+
}
101+
102+
if size > 1 {
103+
return false, newInvalidDatabaseError(
104+
"the MaxMind DB file's data section contains bad data (bool size of %v)",
105+
size,
106+
)
107+
}
108+
109+
var value bool
110+
value, _ = d.d.decodeBool(size, offset)
111+
d.setNextOffset(offset)
112+
return value, nil
113+
}
114+
115+
func (d *Decoder) decodeBytes(typ dataType) ([]byte, error) {
116+
size, offset, err := d.decodeCtrlDataAndFollow(typ)
117+
if err != nil {
118+
return nil, err
119+
}
120+
d.setNextOffset(offset + size)
121+
return d.d.buffer[offset : offset+size], nil
122+
}
123+
124+
// DecodeString decodes the value pointed by the decoder as a string.
125+
//
126+
// Returns an error if the database is malformed or if the pointed value is not a string.
127+
func (d *Decoder) DecodeString() (string, error) {
128+
val, err := d.decodeBytes(_String)
129+
if err != nil {
130+
return "", err
131+
}
132+
return string(val), err
133+
}
134+
135+
// DecodeBytes decodes the value pointed by the decoder as bytes.
136+
//
137+
// Returns an error if the database is malformed or if the pointed value is not bytes.
138+
func (d *Decoder) DecodeBytes() ([]byte, error) {
139+
return d.decodeBytes(_Bytes)
140+
}
141+
142+
// DecodeFloat32 decodes the value pointed by the decoder as a float32.
143+
//
144+
// Returns an error if the database is malformed or if the pointed value is not a float.
145+
func (d *Decoder) DecodeFloat32() (float32, error) {
146+
size, offset, err := d.decodeCtrlDataAndFollow(_Float32)
147+
if err != nil {
148+
return 0, err
149+
}
150+
151+
if size != 4 {
152+
return 0, newInvalidDatabaseError(
153+
"the MaxMind DB file's data section contains bad data (float32 size of %v)",
154+
size,
155+
)
156+
}
157+
158+
value, nextOffset := d.d.decodeFloat32(size, offset)
159+
d.setNextOffset(nextOffset)
160+
return value, nil
161+
}
162+
163+
// DecodeFloat64 decodes the value pointed by the decoder as a float64.
164+
//
165+
// Returns an error if the database is malformed or if the pointed value is not a double.
166+
func (d *Decoder) DecodeFloat64() (float64, error) {
167+
size, offset, err := d.decodeCtrlDataAndFollow(_Float64)
168+
if err != nil {
169+
return 0, err
170+
}
171+
172+
if size != 8 {
173+
return 0, newInvalidDatabaseError(
174+
"the MaxMind DB file's data section contains bad data (float64 size of %v)",
175+
size,
176+
)
177+
}
178+
179+
value, nextOffset := d.d.decodeFloat64(size, offset)
180+
d.setNextOffset(nextOffset)
181+
return value, nil
182+
}
183+
184+
// DecodeInt32 decodes the value pointed by the decoder as a int32.
185+
//
186+
// Returns an error if the database is malformed or if the pointed value is not an int32.
187+
func (d *Decoder) DecodeInt32() (int32, error) {
188+
size, offset, err := d.decodeCtrlDataAndFollow(_Int32)
189+
if err != nil {
190+
return 0, err
191+
}
192+
193+
if size > 4 {
194+
return 0, newInvalidDatabaseError(
195+
"the MaxMind DB file's data section contains bad data (int32 size of %v)",
196+
size,
197+
)
198+
}
199+
200+
var val int32
201+
for _, b := range d.d.buffer[offset : offset+size] {
202+
val = (val << 8) | int32(b)
203+
}
204+
d.setNextOffset(offset + size)
205+
return val, nil
206+
}
207+
208+
// DecodeUInt16 decodes the value pointed by the decoder as a uint16.
209+
//
210+
// Returns an error if the database is malformed or if the pointed value is not an uint16.
211+
func (d *Decoder) DecodeUInt16() (uint16, error) {
212+
size, offset, err := d.decodeCtrlDataAndFollow(_Uint16)
213+
if err != nil {
214+
return 0, err
215+
}
216+
217+
if size > 2 {
218+
return 0, newInvalidDatabaseError(
219+
"the MaxMind DB file's data section contains bad data (uint16 size of %v)",
220+
size,
221+
)
222+
}
223+
224+
var val uint16
225+
for _, b := range d.d.buffer[offset : offset+size] {
226+
val = (val << 8) | uint16(b)
227+
}
228+
d.setNextOffset(offset + size)
229+
return val, nil
230+
}
231+
232+
// DecodeUInt32 decodes the value pointed by the decoder as a uint32.
233+
//
234+
// Returns an error if the database is malformed or if the pointed value is not an uint32.
235+
func (d *Decoder) DecodeUInt32() (uint32, error) {
236+
size, offset, err := d.decodeCtrlDataAndFollow(_Uint32)
237+
if err != nil {
238+
return 0, err
239+
}
240+
241+
if size > 4 {
242+
return 0, newInvalidDatabaseError(
243+
"the MaxMind DB file's data section contains bad data (uint32 size of %v)",
244+
size,
245+
)
246+
}
247+
248+
var val uint32
249+
for _, b := range d.d.buffer[offset : offset+size] {
250+
val = (val << 8) | uint32(b)
251+
}
252+
d.setNextOffset(offset + size)
253+
return val, nil
254+
}
255+
256+
// DecodeUInt64 decodes the value pointed by the decoder as a uint64.
257+
//
258+
// Returns an error if the database is malformed or if the pointed value is not an uint64.
259+
func (d *Decoder) DecodeUInt64() (uint64, error) {
260+
size, offset, err := d.decodeCtrlDataAndFollow(_Uint64)
261+
if err != nil {
262+
return 0, err
263+
}
264+
265+
if size > 8 {
266+
return 0, newInvalidDatabaseError(
267+
"the MaxMind DB file's data section contains bad data (uint64 size of %v)",
268+
size,
269+
)
270+
}
271+
272+
var val uint64
273+
for _, b := range d.d.buffer[offset : offset+size] {
274+
val, _ = append64(val, b)
275+
}
276+
d.setNextOffset(offset + size)
277+
return val, nil
278+
}
279+
280+
// DecodeUInt128 decodes the value pointed by the decoder as a uint128.
281+
//
282+
// Returns an error if the database is malformed or if the pointed value is not an uint128.
283+
func (d *Decoder) DecodeUInt128() (hi, lo uint64, err error) {
284+
size, offset, err := d.decodeCtrlDataAndFollow(_Uint128)
285+
if err != nil {
286+
return 0, 0, err
287+
}
288+
289+
if size > 16 {
290+
return 0, 0, newInvalidDatabaseError(
291+
"the MaxMind DB file's data section contains bad data (uint128 size of %v)",
292+
size,
293+
)
294+
}
295+
296+
for _, b := range d.d.buffer[offset : offset+size] {
297+
var carry byte
298+
lo, carry = append64(lo, b)
299+
hi, _ = append64(hi, carry)
300+
}
301+
302+
d.setNextOffset(offset + size)
303+
304+
return hi, lo, nil
305+
}
306+
307+
func append64(val uint64, b byte) (uint64, byte) {
308+
return (val << 8) | uint64(b), byte(val >> 56)
309+
}
310+
311+
// DecodeMap decodes the value pointed by the decoder as a map.
312+
//
313+
// If the callback returns false, the iteration stops immediately, the remaining
314+
// elements are skipped and DecodeMap returns nil. If any other error is returned,
315+
// the iteration stops immediately and DecodeMap returns that error.
316+
//
317+
// Returns an error if the database is malformed or if the pointed value is not a map.
318+
func (d *Decoder) DecodeMap(cb func(key string, value *Decoder) (bool, error)) error {
319+
size, offset, err := d.decodeCtrlDataAndFollow(_Map)
320+
if err != nil {
321+
return err
322+
}
323+
324+
dec := d.new(offset)
325+
326+
for i := uint(0); i < size; i++ {
327+
var key string
328+
key, err = dec.DecodeString()
329+
if err != nil {
330+
return err
331+
}
332+
333+
err = dec.next(1)
334+
if err != nil {
335+
return err
336+
}
337+
338+
ok, cbErr := cb(key, dec)
339+
340+
err = dec.next(1)
341+
if err != nil {
342+
return err
343+
}
344+
345+
if cbErr != nil {
346+
return cbErr
347+
}
348+
if !ok {
349+
// Skip the unvisited elements:
350+
return dec.next((size - i - 1) * 2)
351+
}
352+
}
353+
354+
d.setNextOffset(dec.offset)
355+
356+
return nil
357+
}
358+
359+
// DecodeSlice decodes the value pointed by the decoder as a slice.
360+
//
361+
// If the callback returns false, the iteration stops immediately, the remaining
362+
// elements are skipped and DecodeSlice returns nil. If an error is returned,
363+
// the iteration stops immediately and DecodeSlice returns that error.
364+
//
365+
// Returns an error if the database is malformed or if the pointed value is not a slice.
366+
func (d *Decoder) DecodeSlice(cb func(value *Decoder) (ok bool, err error)) error {
367+
size, offset, err := d.decodeCtrlDataAndFollow(_Slice)
368+
if err != nil {
369+
return err
370+
}
371+
372+
dec := d.new(offset)
373+
374+
for i := uint(0); i < size; i++ {
375+
ok, cbErr := cb(dec)
376+
377+
err := dec.next(1)
378+
if err != nil {
379+
return err
380+
}
381+
382+
if cbErr != nil {
383+
return cbErr
384+
}
385+
if !ok {
386+
// Skip the unvisited elements:
387+
return dec.next((size - i - 1))
388+
}
389+
}
390+
391+
d.setNextOffset(dec.offset)
392+
393+
return nil
394+
}

0 commit comments

Comments
 (0)