Skip to content

Commit 5a39e6c

Browse files
committed
Handle nil pointers when unmarshaling to struct
1 parent 03a3fea commit 5a39e6c

File tree

3 files changed

+67
-11
lines changed

3 files changed

+67
-11
lines changed

decoder.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,8 @@ func (d *decoder) decode(offset uint, result reflect.Value) (uint, error) {
3939
if typeNum != _Pointer && result.Kind() == reflect.Uintptr {
4040
result.Set(reflect.ValueOf(uintptr(offset)))
4141
return d.nextValueOffset(offset, 1), nil
42-
} else {
43-
return d.decodeFromType(typeNum, size, newOffset, result)
4442
}
43+
return d.decodeFromType(typeNum, size, newOffset, result)
4544
}
4645

4746
func (d *decoder) decodeCtrlData(offset uint) (dataType, uint, uint) {
@@ -86,7 +85,10 @@ func (d *decoder) sizeFromCtrlByte(ctrlByte byte, offset uint, typeNum dataType)
8685

8786
func (d *decoder) decodeFromType(dtype dataType, size uint, offset uint, result reflect.Value) (uint, error) {
8887
if result.Kind() == reflect.Ptr {
89-
result = reflect.Indirect(result)
88+
if result.IsNil() {
89+
result.Set(reflect.New(result.Type().Elem()))
90+
}
91+
result = result.Elem()
9092
}
9193

9294
switch dtype {

reader.go

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ import (
99
)
1010

1111
const (
12-
// Returned by LookupOffset when a matched root record offset cannot be found.
12+
// NotFound is returned by LookupOffset when a matched root record offset
13+
// cannot be found.
1314
NotFound = ^uintptr(0)
1415

1516
dataSectionSeparatorSize = 16
@@ -106,11 +107,11 @@ func (r *Reader) startNode() (uint, error) {
106107
// Lookup takes an IP address as a net.IP structure and a pointer to the
107108
// result value to Decode into.
108109
func (r *Reader) Lookup(ipAddress net.IP, result interface{}) error {
109-
if pointer, err := r.lookupPointer(ipAddress); pointer == 0 {
110+
pointer, err := r.lookupPointer(ipAddress)
111+
if pointer == 0 || err != nil {
110112
return err
111-
} else {
112-
return r.retrieveData(pointer, result)
113113
}
114+
return r.retrieveData(pointer, result)
114115
}
115116

116117
// LookupOffset maps an argument net.IP to a corresponding record offset in the
@@ -119,14 +120,14 @@ func (r *Reader) Lookup(ipAddress net.IP, result interface{}) error {
119120
// is an advanced API, which exists to provide clients with a means to cache
120121
// previously-decoded records.
121122
func (r *Reader) LookupOffset(ipAddress net.IP) (uintptr, error) {
122-
if pointer, err := r.lookupPointer(ipAddress); pointer == 0 {
123+
pointer, err := r.lookupPointer(ipAddress)
124+
if pointer == 0 || err != nil {
123125
return NotFound, err
124-
} else {
125-
return r.resolveDataPointer(pointer)
126126
}
127+
return r.resolveDataPointer(pointer)
127128
}
128129

129-
// Decodes the record at |offset| into |result|. The result value pointed to
130+
// Decode the record at |offset| into |result|. The result value pointed to
130131
// must be a data value that corresponds to a record in the database. This may
131132
// include a struct representation of the data, a map capable of holding the
132133
// data or an empty interface{} value.

reader_test.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,59 @@ func (s *MySuite) TestDecoder(c *C) {
162162
c.Assert(reader.Close(), IsNil)
163163
}
164164

165+
type PointerMap struct {
166+
MapX map[string]interface{} `maxminddb:"mapX"`
167+
}
168+
169+
type TestPointerType struct {
170+
Array *[]uint `maxminddb:"array"`
171+
Boolean *bool `maxminddb:"boolean"`
172+
Bytes *[]byte `maxminddb:"bytes"`
173+
Double *float64 `maxminddb:"double"`
174+
Float *float32 `maxminddb:"float"`
175+
Int32 *int32 `maxminddb:"int32"`
176+
Map *PointerMap `maxminddb:"map"`
177+
Uint16 *uint16 `maxminddb:"uint16"`
178+
Uint32 *uint32 `maxminddb:"uint32"`
179+
Uint64 *uint64 `maxminddb:"uint64"`
180+
Uint128 *big.Int `maxminddb:"uint128"`
181+
Utf8String *string `maxminddb:"utf8_string"`
182+
}
183+
184+
func (s *MySuite) TestStructWithPointer(c *C) {
185+
reader, err := Open("test-data/test-data/MaxMind-DB-test-decoder.mmdb")
186+
c.Assert(err, IsNil)
187+
188+
var result TestPointerType
189+
190+
c.Log("Before")
191+
reader.Lookup(net.ParseIP("::1.1.1.0"), &result)
192+
c.Assert(err, IsNil)
193+
194+
c.Assert(*result.Array, DeepEquals, []uint{uint(1), uint(2), uint(3)})
195+
c.Assert(*result.Boolean, Equals, true)
196+
c.Assert(*result.Bytes, DeepEquals, []byte{0x00, 0x00, 0x00, 0x2a})
197+
c.Assert(*result.Double, Equals, 42.123456)
198+
c.Assert(*result.Float, Equals, float32(1.1))
199+
c.Assert(*result.Int32, Equals, int32(-268435456))
200+
201+
c.Assert(result.Map.MapX, DeepEquals,
202+
map[string]interface{}{
203+
"arrayX": []interface{}{uint64(7), uint64(8), uint64(9)},
204+
"utf8_stringX": "hello",
205+
})
206+
207+
c.Assert(*result.Uint16, Equals, uint16(100))
208+
c.Assert(*result.Uint32, Equals, uint32(268435456))
209+
c.Assert(*result.Uint64, Equals, uint64(1152921504606846976))
210+
c.Assert(*result.Utf8String, Equals, "unicode! ☯ - ♫")
211+
bigInt := new(big.Int)
212+
bigInt.SetString("1329227995784915872903807060280344576", 10)
213+
c.Assert(result.Uint128, DeepEquals, bigInt)
214+
215+
c.Assert(reader.Close(), IsNil)
216+
}
217+
165218
func (s *MySuite) TestNestedOffsetDecode(c *C) {
166219
db, err := Open("test-data/test-data/GeoIP2-City-Test.mmdb")
167220
c.Assert(err, IsNil)

0 commit comments

Comments
 (0)