Skip to content

Commit 860b00e

Browse files
authored
Merge pull request #21 from kz-sher/optimise-performance
Optimise Performance
2 parents b2a5c6c + fd49350 commit 860b00e

File tree

3 files changed

+71
-44
lines changed

3 files changed

+71
-44
lines changed

go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
module github.com/ip2location/ip2location-go/v9
22

33
go 1.14
4+
5+
require lukechampine.com/uint128 v1.2.0 // indirect

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI=
2+
lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=

ip2location.go

Lines changed: 67 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,13 @@ import (
99
"errors"
1010
"fmt"
1111
"io"
12+
"lukechampine.com/uint128"
1213
"math"
1314
"math/big"
1415
"net"
1516
"os"
1617
"strconv"
18+
"unsafe"
1719
)
1820

1921
type DBReader interface {
@@ -146,15 +148,15 @@ var category_position = [26]uint8{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
146148

147149
const api_version string = "9.5.0"
148150

149-
var max_ipv4_range = big.NewInt(4294967295)
150-
var max_ipv6_range = big.NewInt(0)
151-
var from_v4mapped = big.NewInt(281470681743360)
152-
var to_v4mapped = big.NewInt(281474976710655)
153-
var from_6to4 = big.NewInt(0)
154-
var to_6to4 = big.NewInt(0)
155-
var from_teredo = big.NewInt(0)
156-
var to_teredo = big.NewInt(0)
157-
var last_32bits = big.NewInt(4294967295)
151+
var max_ipv4_range = uint128.From64(4294967295)
152+
var max_ipv6_range = uint128.From64(0)
153+
var from_v4mapped = uint128.From64(281470681743360)
154+
var to_v4mapped = uint128.From64(281474976710655)
155+
var from_6to4 = uint128.From64(0)
156+
var to_6to4 = uint128.From64(0)
157+
var from_teredo = uint128.From64(0)
158+
var to_teredo = uint128.From64(0)
159+
var last_32bits = uint128.From64(4294967295)
158160

159161
const countryshort uint32 = 0x000001
160162
const countrylong uint32 = 0x000002
@@ -187,10 +189,10 @@ const not_supported string = "This parameter is unavailable for selected data fi
187189
const invalid_bin string = "Incorrect IP2Location BIN file format. Please make sure that you are using the latest IP2Location BIN file."
188190

189191
// get IP type and calculate IP number; calculates index too if exists
190-
func (d *DB) checkip(ip string) (iptype uint32, ipnum *big.Int, ipindex uint32) {
192+
func (d *DB) checkip(ip string) (iptype uint32, ipnum uint128.Uint128, ipindex uint32) {
191193
iptype = 0
192-
ipnum = big.NewInt(0)
193-
ipnumtmp := big.NewInt(0)
194+
ipnum = uint128.From64(0)
195+
ipnumtmp := uint128.From64(0)
194196
ipindex = 0
195197
ipaddress := net.ParseIP(ip)
196198

@@ -199,43 +201,43 @@ func (d *DB) checkip(ip string) (iptype uint32, ipnum *big.Int, ipindex uint32)
199201

200202
if v4 != nil {
201203
iptype = 4
202-
ipnum.SetBytes(v4)
204+
ipnum = uint128.From64(uint64(binary.BigEndian.Uint32(v4)))
203205
} else {
204206
v6 := ipaddress.To16()
205207

206208
if v6 != nil {
207209
iptype = 6
208-
ipnum.SetBytes(v6)
210+
ipnum.PutBytes(v6)
209211

210212
if ipnum.Cmp(from_v4mapped) >= 0 && ipnum.Cmp(to_v4mapped) <= 0 {
211213
// ipv4-mapped ipv6 should treat as ipv4 and read ipv4 data section
212214
iptype = 4
213-
ipnum.Sub(ipnum, from_v4mapped)
215+
ipnum = ipnum.Sub(from_v4mapped)
214216
} else if ipnum.Cmp(from_6to4) >= 0 && ipnum.Cmp(to_6to4) <= 0 {
215217
// 6to4 so need to remap to ipv4
216218
iptype = 4
217-
ipnum.Rsh(ipnum, 80)
218-
ipnum.And(ipnum, last_32bits)
219+
ipnum = ipnum.Rsh(80)
220+
ipnum = ipnum.And(last_32bits)
219221
} else if ipnum.Cmp(from_teredo) >= 0 && ipnum.Cmp(to_teredo) <= 0 {
220222
// Teredo so need to remap to ipv4
221223
iptype = 4
222-
ipnum.Not(ipnum)
223-
ipnum.And(ipnum, last_32bits)
224+
ipnum = uint128.Uint128{^ipnum.Lo, ^ipnum.Hi}
225+
ipnum = ipnum.And(last_32bits)
224226
}
225227
}
226228
}
227229
}
228230
if iptype == 4 {
229231
if d.meta.ipv4indexed {
230-
ipnumtmp.Rsh(ipnum, 16)
231-
ipnumtmp.Lsh(ipnumtmp, 3)
232-
ipindex = uint32(ipnumtmp.Add(ipnumtmp, big.NewInt(int64(d.meta.ipv4indexbaseaddr))).Uint64())
232+
ipnumtmp = ipnum.Rsh(16)
233+
ipnumtmp = ipnumtmp.Lsh(3)
234+
ipindex = uint32(ipnumtmp.Add(uint128.From64(uint64(d.meta.ipv4indexbaseaddr))).Lo)
233235
}
234236
} else if iptype == 6 {
235237
if d.meta.ipv6indexed {
236-
ipnumtmp.Rsh(ipnum, 112)
237-
ipnumtmp.Lsh(ipnumtmp, 3)
238-
ipindex = uint32(ipnumtmp.Add(ipnumtmp, big.NewInt(int64(d.meta.ipv6indexbaseaddr))).Uint64())
238+
ipnumtmp = ipnum.Rsh(112)
239+
ipnumtmp = ipnumtmp.Lsh(3)
240+
ipindex = uint32(ipnumtmp.Add(uint128.From64(uint64(d.meta.ipv6indexbaseaddr))).Lo)
239241
}
240242
}
241243
return
@@ -290,33 +292,33 @@ func (d *DB) readuint32(pos uint32) (uint32, error) {
290292
}
291293

292294
// read unsigned 128-bit integer from slices
293-
func (d *DB) readuint128_row(row []byte, pos uint32) *big.Int {
294-
retval := big.NewInt(0)
295+
func (d *DB) readuint128_row(row []byte, pos uint32) uint128.Uint128 {
296+
retval := uint128.From64(0)
295297
data := row[pos : pos+16]
296298

297299
// little endian to big endian
298300
for i, j := 0, len(data)-1; i < j; i, j = i+1, j-1 {
299301
data[i], data[j] = data[j], data[i]
300302
}
301-
retval.SetBytes(data)
303+
retval.PutBytes(data)
302304
return retval
303305
}
304306

305307
// read unsigned 128-bit integer
306-
func (d *DB) readuint128(pos uint32) (*big.Int, error) {
308+
func (d *DB) readuint128(pos uint32) (uint128.Uint128, error) {
307309
pos2 := int64(pos)
308-
retval := big.NewInt(0)
310+
retval := uint128.From64(0)
309311
data := make([]byte, 16)
310312
_, err := d.f.ReadAt(data, pos2-1)
311313
if err != nil {
312-
return nil, err
314+
return uint128.From64(0), err
313315
}
314316

315317
// little endian to big endian
316318
for i, j := 0, len(data)-1; i < j; i, j = i+1, j-1 {
317319
data[i], data[j] = data[j], data[i]
318320
}
319-
retval.SetBytes(data)
321+
retval.PutBytes(data)
320322
return retval, nil
321323
}
322324

@@ -331,7 +333,7 @@ func (d *DB) readstr(pos uint32) (string, error) {
331333
return "", err
332334
}
333335
strlen := data[0]
334-
retval = string(data[1:(strlen + 1)])
336+
retval = convertBytesToString(data[1:(strlen + 1)])
335337
return retval, nil
336338
}
337339

@@ -365,11 +367,25 @@ func OpenDB(dbpath string) (*DB, error) {
365367
func OpenDBWithReader(reader DBReader) (*DB, error) {
366368
var db = &DB{}
367369

368-
max_ipv6_range.SetString("340282366920938463463374607431768211455", 10)
369-
from_6to4.SetString("42545680458834377588178886921629466624", 10)
370-
to_6to4.SetString("42550872755692912415807417417958686719", 10)
371-
from_teredo.SetString("42540488161975842760550356425300246528", 10)
372-
to_teredo.SetString("42540488241204005274814694018844196863", 10)
370+
_max_ipv6_range := big.NewInt(0)
371+
_max_ipv6_range.SetString("340282366920938463463374607431768211455", 10)
372+
max_ipv6_range = uint128.FromBig(_max_ipv6_range)
373+
374+
_from_6to4 := big.NewInt(0)
375+
_from_6to4.SetString("42545680458834377588178886921629466624", 10)
376+
from_6to4 = uint128.FromBig(_from_6to4)
377+
378+
_to_6to4 := big.NewInt(0)
379+
_to_6to4.SetString("42550872755692912415807417417958686719", 10)
380+
to_6to4 = uint128.FromBig(_to_6to4)
381+
382+
_from_teredo := big.NewInt(0)
383+
_from_teredo.SetString("42540488161975842760550356425300246528", 10)
384+
from_teredo = uint128.FromBig(_from_teredo)
385+
386+
_to_teredo := big.NewInt(0)
387+
_to_teredo.SetString("42540488241204005274814694018844196863", 10)
388+
to_teredo = uint128.FromBig(_to_teredo)
373389

374390
db.f = reader
375391

@@ -563,6 +579,13 @@ func handleError(rec IP2Locationrecord, err error) IP2Locationrecord {
563579
return rec
564580
}
565581

582+
// convertBytesToString provides a no-copy []byte to string conversion.
583+
// This implementation is adopted by official strings.Builder.
584+
// Reference: https://github.com/golang/go/issues/25484
585+
func convertBytesToString(b []byte) string {
586+
return *(*string)(unsafe.Pointer(&b))
587+
}
588+
566589
// Get_all will return all geolocation fields based on the queried IP address.
567590
//
568591
// Deprecated: No longer being updated.
@@ -854,9 +877,9 @@ func (d *DB) query(ipaddress string, mode uint32) (IP2Locationrecord, error) {
854877
var row []byte
855878
var fullrow []byte
856879
var readlen uint32
857-
ipfrom := big.NewInt(0)
858-
ipto := big.NewInt(0)
859-
maxip := big.NewInt(0)
880+
ipfrom := uint128.From64(0)
881+
ipto := uint128.From64(0)
882+
maxip := uint128.From64(0)
860883

861884
if iptype == 4 {
862885
baseaddr = d.meta.ipv4databaseaddr
@@ -882,7 +905,7 @@ func (d *DB) query(ipaddress string, mode uint32) (IP2Locationrecord, error) {
882905
}
883906

884907
if ipno.Cmp(maxip) >= 0 {
885-
ipno.Sub(ipno, big.NewInt(1))
908+
ipno = ipno.Sub(uint128.From64(1))
886909
}
887910

888911
for low <= high {
@@ -898,10 +921,10 @@ func (d *DB) query(ipaddress string, mode uint32) (IP2Locationrecord, error) {
898921

899922
if iptype == 4 {
900923
ipfrom32 := d.readuint32_row(fullrow, 0)
901-
ipfrom = big.NewInt(int64(ipfrom32))
924+
ipfrom = uint128.From64(uint64(ipfrom32))
902925

903926
ipto32 := d.readuint32_row(fullrow, colsize)
904-
ipto = big.NewInt(int64(ipto32))
927+
ipto = uint128.From64(uint64(ipto32))
905928
} else {
906929
ipfrom = d.readuint128_row(fullrow, 0)
907930

0 commit comments

Comments
 (0)