Skip to content

Commit c87192e

Browse files
authored
Merge pull request #4 from kz-sher/optimise-performance
Optimise Performance
2 parents db15b0a + 87e8df5 commit c87192e

File tree

3 files changed

+79
-71
lines changed

3 files changed

+79
-71
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/ip2proxy-go/v3
22

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

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=

ip2proxy.go

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

1921
// Implement db reader interface
@@ -43,9 +45,9 @@ type ip2proxyMeta struct {
4345
fileSize uint32
4446
}
4547

46-
// The ip2proxyRecord struct stores all of the available
48+
// The IP2ProxyRecord struct stores all of the available
4749
// proxy info found in the IP2Proxy database.
48-
type ip2proxyRecord struct {
50+
type IP2ProxyRecord struct {
4951
countryShort string
5052
countryLong string
5153
region string
@@ -113,15 +115,15 @@ var providerPosition = [12]uint8{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13}
113115

114116
const moduleVersion string = "3.4.1"
115117

116-
var maxIPV4Range = big.NewInt(4294967295)
117-
var maxIPV6Range = big.NewInt(0)
118-
var fromV4Mapped = big.NewInt(281470681743360)
119-
var toV4Mapped = big.NewInt(281474976710655)
120-
var from6To4 = big.NewInt(0)
121-
var to6To4 = big.NewInt(0)
122-
var fromTeredo = big.NewInt(0)
123-
var toTeredo = big.NewInt(0)
124-
var last32Bits = big.NewInt(4294967295)
118+
var maxIPV4Range = uint128.From64(4294967295)
119+
var maxIPV6Range = uint128.From64(0)
120+
var fromV4Mapped = uint128.From64(281470681743360)
121+
var toV4Mapped = uint128.From64(281474976710655)
122+
var from6To4 = uint128.From64(0)
123+
var to6To4 = uint128.From64(0)
124+
var fromTeredo = uint128.From64(0)
125+
var toTeredo = uint128.From64(0)
126+
var last32Bits = uint128.From64(4294967295)
125127

126128
const countryShort uint32 = 0x00001
127129
const countryLong uint32 = 0x00002
@@ -147,10 +149,10 @@ const msgIPV6Unsupported string = "IPV6 ADDRESS MISSING IN IPV4 BIN"
147149
const msgInvalidBin string = "Incorrect IP2Proxy BIN file format. Please make sure that you are using the latest IP2Proxy BIN file."
148150

149151
// get IP type and calculate IP number; calculates index too if exists
150-
func (d *DB) checkIP(ip string) (ipType uint32, ipNum *big.Int, ipIndex uint32) {
152+
func (d *DB) checkIP(ip string) (ipType uint32, ipNum uint128.Uint128, ipIndex uint32) {
151153
ipType = 0
152-
ipNum = big.NewInt(0)
153-
ipNumTmp := big.NewInt(0)
154+
ipNum = uint128.From64(0)
155+
ipNumTmp := uint128.From64(0)
154156
ipIndex = 0
155157
ipAddress := net.ParseIP(ip)
156158

@@ -159,43 +161,43 @@ func (d *DB) checkIP(ip string) (ipType uint32, ipNum *big.Int, ipIndex uint32)
159161

160162
if v4 != nil {
161163
ipType = 4
162-
ipNum.SetBytes(v4)
164+
ipNum = uint128.From64(uint64(binary.BigEndian.Uint32(v4)))
163165
} else {
164166
v6 := ipAddress.To16()
165167

166168
if v6 != nil {
167169
ipType = 6
168-
ipNum.SetBytes(v6)
170+
ipNum.PutBytes(v6)
169171

170172
if ipNum.Cmp(fromV4Mapped) >= 0 && ipNum.Cmp(toV4Mapped) <= 0 {
171173
// ipv4-mapped ipv6 should treat as ipv4 and read ipv4 data section
172174
ipType = 4
173-
ipNum.Sub(ipNum, fromV4Mapped)
175+
ipNum = ipNum.Sub(fromV4Mapped)
174176
} else if ipNum.Cmp(from6To4) >= 0 && ipNum.Cmp(to6To4) <= 0 {
175177
// 6to4 so need to remap to ipv4
176178
ipType = 4
177-
ipNum.Rsh(ipNum, 80)
178-
ipNum.And(ipNum, last32Bits)
179+
ipNum = ipNum.Rsh(80)
180+
ipNum = ipNum.And(last32Bits)
179181
} else if ipNum.Cmp(fromTeredo) >= 0 && ipNum.Cmp(toTeredo) <= 0 {
180182
// Teredo so need to remap to ipv4
181183
ipType = 4
182-
ipNum.Not(ipNum)
183-
ipNum.And(ipNum, last32Bits)
184+
ipNum = uint128.Uint128{^ipNum.Lo, ^ipNum.Hi}
185+
ipNum = ipNum.And(last32Bits)
184186
}
185187
}
186188
}
187189
}
188190
if ipType == 4 {
189191
if d.meta.ipV4Indexed {
190-
ipNumTmp.Rsh(ipNum, 16)
191-
ipNumTmp.Lsh(ipNumTmp, 3)
192-
ipIndex = uint32(ipNumTmp.Add(ipNumTmp, big.NewInt(int64(d.meta.ipV4IndexBaseAddr))).Uint64())
192+
ipNumTmp = ipNum.Rsh(16)
193+
ipNumTmp = ipNumTmp.Lsh(3)
194+
ipIndex = uint32(ipNumTmp.Add(uint128.From64(uint64(d.meta.ipV4IndexBaseAddr))).Lo)
193195
}
194196
} else if ipType == 6 {
195197
if d.meta.ipV6Indexed {
196-
ipNumTmp.Rsh(ipNum, 112)
197-
ipNumTmp.Lsh(ipNumTmp, 3)
198-
ipIndex = uint32(ipNumTmp.Add(ipNumTmp, big.NewInt(int64(d.meta.ipV6IndexBaseAddr))).Uint64())
198+
ipNumTmp = ipNum.Rsh(112)
199+
ipNumTmp = ipNumTmp.Lsh(3)
200+
ipIndex = uint32(ipNumTmp.Add(uint128.From64(uint64(d.meta.ipV6IndexBaseAddr))).Lo)
199201
}
200202
}
201203
return
@@ -250,33 +252,33 @@ func (d *DB) readUint32(pos uint32) (uint32, error) {
250252
}
251253

252254
// read unsigned 128-bit integer from slices
253-
func (d *DB) readUint128Row(row []byte, pos uint32) *big.Int {
254-
retVal := big.NewInt(0)
255+
func (d *DB) readUint128Row(row []byte, pos uint32) uint128.Uint128 {
256+
retVal := uint128.From64(0)
255257
data := row[pos : pos+16]
256258

257259
// little endian to big endian
258260
for i, j := 0, len(data)-1; i < j; i, j = i+1, j-1 {
259261
data[i], data[j] = data[j], data[i]
260262
}
261-
retVal.SetBytes(data)
263+
retVal.PutBytes(data)
262264
return retVal
263265
}
264266

265267
// read unsigned 128-bit integer
266-
func (d *DB) readUint128(pos uint32) (*big.Int, error) {
268+
func (d *DB) readUint128(pos uint32) (uint128.Uint128, error) {
267269
pos2 := int64(pos)
268-
retVal := big.NewInt(0)
270+
retVal := uint128.From64(0)
269271
data := make([]byte, 16)
270272
_, err := d.f.ReadAt(data, pos2-1)
271273
if err != nil {
272-
return nil, err
274+
return uint128.From64(0), err
273275
}
274276

275277
// little endian to big endian
276278
for i, j := 0, len(data)-1; i < j; i, j = i+1, j-1 {
277279
data[i], data[j] = data[j], data[i]
278280
}
279-
retVal.SetBytes(data)
281+
retVal.PutBytes(data)
280282
return retVal, nil
281283
}
282284

@@ -291,7 +293,7 @@ func (d *DB) readStr(pos uint32) (string, error) {
291293
return "", err
292294
}
293295
strLen := data[0]
294-
retVal = string(data[1:(strLen + 1)])
296+
retVal = convertBytesToString(data[1:(strLen + 1)])
295297
return retVal, nil
296298
}
297299

@@ -316,11 +318,25 @@ func OpenDB(dbPath string) (*DB, error) {
316318
func OpenDBWithReader(reader dbReader) (*DB, error) {
317319
var db = &DB{}
318320

319-
maxIPV6Range.SetString("340282366920938463463374607431768211455", 10)
320-
from6To4.SetString("42545680458834377588178886921629466624", 10)
321-
to6To4.SetString("42550872755692912415807417417958686719", 10)
322-
fromTeredo.SetString("42540488161975842760550356425300246528", 10)
323-
toTeredo.SetString("42540488241204005274814694018844196863", 10)
321+
_maxIPV6Range := big.NewInt(0)
322+
_maxIPV6Range.SetString("340282366920938463463374607431768211455", 10)
323+
maxIPV6Range = uint128.FromBig(_maxIPV6Range)
324+
325+
_from6To4 := big.NewInt(0)
326+
_from6To4.SetString("42545680458834377588178886921629466624", 10)
327+
from6To4 = uint128.FromBig(_from6To4)
328+
329+
_to6To4 := big.NewInt(0)
330+
_to6To4.SetString("42550872755692912415807417417958686719", 10)
331+
to6To4 = uint128.FromBig(_to6To4)
332+
333+
_fromTeredo := big.NewInt(0)
334+
_fromTeredo.SetString("42540488161975842760550356425300246528", 10)
335+
fromTeredo = uint128.FromBig(_fromTeredo)
336+
337+
_toTeredo := big.NewInt(0)
338+
_toTeredo.SetString("42540488241204005274814694018844196863", 10)
339+
toTeredo = uint128.FromBig(_toTeredo)
324340

325341
db.f = reader
326342

@@ -509,8 +525,8 @@ func (d *DB) DatabaseVersion() string {
509525
}
510526

511527
// populate record with message
512-
func loadMessage(mesg string) ip2proxyRecord {
513-
var x ip2proxyRecord
528+
func loadMessage(mesg string) IP2ProxyRecord {
529+
var x IP2ProxyRecord
514530

515531
x.countryShort = mesg
516532
x.countryLong = mesg
@@ -530,13 +546,20 @@ func loadMessage(mesg string) ip2proxyRecord {
530546
return x
531547
}
532548

533-
func handleError(rec ip2proxyRecord, err error) ip2proxyRecord {
549+
func handleError(rec IP2ProxyRecord, err error) IP2ProxyRecord {
534550
if err != nil {
535551
fmt.Print(err)
536552
}
537553
return rec
538554
}
539555

556+
// convertBytesToString provides a no-copy []byte to string conversion.
557+
// This implementation is adopted by official strings.Builder.
558+
// Reference: https://github.com/golang/go/issues/25484
559+
func convertBytesToString(b []byte) string {
560+
return *(*string)(unsafe.Pointer(&b))
561+
}
562+
540563
// GetAll will return all proxy fields based on the queried IP address.
541564
//
542565
// Deprecated: No longer being updated.
@@ -658,27 +681,8 @@ func IsProxy(ipAddress string) int8 {
658681
}
659682

660683
// GetAll will return all proxy fields based on the queried IP address.
661-
func (d *DB) GetAll(ipAddress string) (map[string]string, error) {
662-
data, err := d.query(ipAddress, all)
663-
664-
var x = make(map[string]string)
665-
s := strconv.Itoa(int(data.isProxy))
666-
x["isProxy"] = s
667-
x["ProxyType"] = data.proxyType
668-
x["CountryShort"] = data.countryShort
669-
x["CountryLong"] = data.countryLong
670-
x["Region"] = data.region
671-
x["City"] = data.city
672-
x["ISP"] = data.isp
673-
x["Domain"] = data.domain
674-
x["UsageType"] = data.usageType
675-
x["ASN"] = data.asn
676-
x["AS"] = data.as
677-
x["LastSeen"] = data.lastSeen
678-
x["Threat"] = data.threat
679-
x["Provider"] = data.provider
680-
681-
return x, err
684+
func (d *DB) GetAll(ipAddress string) (IP2ProxyRecord, error) {
685+
return d.query(ipAddress, all)
682686
}
683687

684688
// GetCountryShort will return the ISO-3166 country code based on the queried IP address.
@@ -766,7 +770,7 @@ func (d *DB) IsProxy(ipAddress string) (int8, error) {
766770
}
767771

768772
// main query
769-
func (d *DB) query(ipAddress string, mode uint32) (ip2proxyRecord, error) {
773+
func (d *DB) query(ipAddress string, mode uint32) (IP2ProxyRecord, error) {
770774
x := loadMessage(msgNotSupported) // default message
771775

772776
// read metadata
@@ -795,9 +799,9 @@ func (d *DB) query(ipAddress string, mode uint32) (ip2proxyRecord, error) {
795799
var row []byte
796800
var fullRow []byte
797801
var readLen uint32
798-
ipFrom := big.NewInt(0)
799-
ipTo := big.NewInt(0)
800-
maxIP := big.NewInt(0)
802+
ipFrom := uint128.From64(0)
803+
ipTo := uint128.From64(0)
804+
maxIP := uint128.From64(0)
801805

802806
if ipType == 4 {
803807
baseAddr = d.meta.ipV4DatabaseAddr
@@ -827,7 +831,7 @@ func (d *DB) query(ipAddress string, mode uint32) (ip2proxyRecord, error) {
827831
}
828832

829833
if ipNo.Cmp(maxIP) >= 0 {
830-
ipNo.Sub(ipNo, big.NewInt(1))
834+
ipNo = ipNo.Sub(uint128.From64(1))
831835
}
832836

833837
for low <= high {
@@ -843,10 +847,10 @@ func (d *DB) query(ipAddress string, mode uint32) (ip2proxyRecord, error) {
843847

844848
if ipType == 4 {
845849
ipFrom32 := d.readUint32Row(fullRow, 0)
846-
ipFrom = big.NewInt(int64(ipFrom32))
850+
ipFrom = uint128.From64(uint64(ipFrom32))
847851

848852
ipTo32 := d.readUint32Row(fullRow, colSize)
849-
ipTo = big.NewInt(int64(ipTo32))
853+
ipTo = uint128.From64(uint64(ipTo32))
850854
} else {
851855
ipFrom = d.readUint128Row(fullRow, 0)
852856

0 commit comments

Comments
 (0)