-
Notifications
You must be signed in to change notification settings - Fork 101
/
result.go
145 lines (132 loc) · 4.59 KB
/
result.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
package maxminddb
import (
"errors"
"math"
"net/netip"
"reflect"
)
const notFound uint = math.MaxUint
type Result struct {
ip netip.Addr
err error
decoder decoder
offset uint
prefixLen uint8
}
// Decode unmarshals the data from the data section into the value pointed to
// by v. If v is nil or not a pointer, an error is returned. If the data in
// the database record cannot be stored in v because of type differences, an
// UnmarshalTypeError is returned. If the database is invalid or otherwise
// cannot be read, an InvalidDatabaseError is returned.
//
// An error will also be returned if there was an error during the
// Reader.Lookup call.
//
// If the Reader.Lookup call did not find a value for the IP address, no error
// will be returned and v will be unchanged.
func (r Result) Decode(v any) error {
if r.err != nil {
return r.err
}
if r.offset == notFound {
return nil
}
rv := reflect.ValueOf(v)
if rv.Kind() != reflect.Ptr || rv.IsNil() {
return errors.New("result param must be a pointer")
}
if dser, ok := v.(deserializer); ok {
_, err := r.decoder.decodeToDeserializer(r.offset, dser, 0, false)
return err
}
_, err := r.decoder.decode(r.offset, rv, 0)
return err
}
// DecodePath unmarshals a value from data section into v, following the
// specified path.
//
// The v parameter should be a pointer to the value where the decoded data
// will be stored. If v is nil or not a pointer, an error is returned. If the
// data in the database record cannot be stored in v because of type
// differences, an UnmarshalTypeError is returned.
//
// The path is a variadic list of keys (strings) and/or indices (ints) that
// describe the nested structure to traverse in the data to reach the desired
// value.
//
// For maps, string path elements are used as keys.
// For arrays, int path elements are used as indices. A negative offset will
// return values from the end of the array, e.g., -1 will return the last
// element.
//
// If the path is empty, the entire data structure is decoded into v.
//
// Returns an error if:
// - the path is invalid
// - the data cannot be decoded into the type of v
// - v is not a pointer or the database record cannot be stored in v due to
// type mismatch
// - the Result does not contain valid data
//
// Example usage:
//
// var city string
// err := result.DecodePath(&city, "location", "city", "names", "en")
//
// var geonameID int
// err := result.DecodePath(&geonameID, "subdivisions", 0, "geoname_id")
func (r Result) DecodePath(v any, path ...any) error {
if r.err != nil {
return r.err
}
if r.offset == notFound {
return nil
}
rv := reflect.ValueOf(v)
if rv.Kind() != reflect.Ptr || rv.IsNil() {
return errors.New("result param must be a pointer")
}
return r.decoder.decodePath(r.offset, path, rv)
}
// Err provides a way to check whether there was an error during the lookup
// without calling Result.Decode. If there was an error, it will also be
// returned from Result.Decode.
func (r Result) Err() error {
return r.err
}
// Found will return true if the IP was found in the search tree. It will
// return false if the IP was not found or if there was an error.
func (r Result) Found() bool {
return r.err == nil && r.offset != notFound
}
// Offset returns the offset of the record in the database. This can be
// passed to (*Reader).LookupOffset. It can also be used as a unique
// identifier for the data record in the particular database to cache the data
// record across lookups. Note that while the offset uniquely identifies the
// data record, other data in Result may differ between lookups. The offset
// is only valid for the current database version. If you update the database
// file, you must invalidate any cache associated with the previous version.
func (r Result) Offset() uintptr {
return uintptr(r.offset)
}
// Prefix returns the netip.Prefix representing the network associated with
// the data record in the database.
func (r Result) Prefix() netip.Prefix {
ip := r.ip
prefixLen := int(r.prefixLen)
if ip.Is4() {
// This is necessary as the node that the IPv4 start is at may
// be at a bit depth that is less that 96, i.e., ipv4Start points
// to a leaf node. For instance, if a record was inserted at ::/8,
// the ipv4Start would point directly at the leaf node for the
// record and would have a bit depth of 8. This would not happen
// with databases currently distributed by MaxMind as all of them
// have an IPv4 subtree that is greater than a single node.
if prefixLen < 96 {
return netip.PrefixFrom(zeroIP, prefixLen)
}
prefixLen -= 96
}
prefix, _ := ip.Prefix(prefixLen)
return prefix
}