Skip to content

Commit 9177964

Browse files
committed
Reduced file I/O
1 parent 7cee262 commit 9177964

File tree

3 files changed

+96
-41
lines changed

3 files changed

+96
-41
lines changed

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
MIT License
22

3-
Copyright (c) 2021 ip2location
3+
Copyright (c) 2022 ip2location
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
package = "ip2location"
2-
version = "8.5.0-1"
2+
version = "8.6.0-1"
33
source = {
44
url = "git://github.com/ip2location/ip2location-lua.git"
55
}

ip2location.lua

Lines changed: 94 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,15 @@ ip2location = {
1111
ipv4databaseaddr = 0,
1212
ipv6databasecount = 0,
1313
ipv6databaseaddr = 0,
14+
ipv4indexed = false,
15+
ipv6indexed = false,
1416
ipv4indexbaseaddr = 0,
1517
ipv6indexbaseaddr = 0,
1618
ipv4columnsize = 0,
1719
ipv6columnsize = 0,
1820
productcode = 0,
1921
producttype = 0,
2022
filesize = 0,
21-
columnsize_without_ip = 0,
2223
country_position_offset = 0,
2324
region_position_offset = 0,
2425
city_position_offset = 0,
@@ -121,7 +122,7 @@ local usagetype_position = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
121122
local addresstype_position = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21}
122123
local category_position = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 22}
123124

124-
local api_version = "8.5.0"
125+
local api_version = "8.6.0"
125126

126127
local modes = {
127128
countryshort = 0x000001,
@@ -161,6 +162,13 @@ local function printme(stuff)
161162
print(inspect(stuff))
162163
end
163164

165+
-- read row
166+
local function readrow(myfile, pos, len)
167+
myfile:seek("set", pos - 1)
168+
local bytestr = myfile:read(len)
169+
return bytestr
170+
end
171+
164172
-- read byte
165173
local function readuint8(pos, myfile)
166174
myfile:seek("set", pos - 1)
@@ -172,6 +180,17 @@ local function readuint8(pos, myfile)
172180
return value
173181
end
174182

183+
-- read byte
184+
local function readuint8header(pos, row)
185+
local pos2 = pos + 1 -- due to index starting with 1
186+
local bytestr = string.sub(row, pos2, pos2) -- strip 1 byte
187+
local value = 0 -- no need BigNum
188+
if bytestr ~= nil then
189+
value = string.byte(bytestr)
190+
end
191+
return value
192+
end
193+
175194
-- read unsigned 32-bit integer
176195
local function readuint32(pos, myfile)
177196
myfile:seek("set", pos - 1)
@@ -183,6 +202,17 @@ local function readuint32(pos, myfile)
183202
return value
184203
end
185204

205+
-- read unsigned 32-bit integer
206+
local function readuint32header(pos, row)
207+
local pos2 = pos + 1 -- due to index starting with 1
208+
local bytestr = string.sub(row, pos2, pos2 + 3) -- strip 4 bytes
209+
local value = 0 -- no need BigNum
210+
if bytestr ~= nil then
211+
value = string.unpack("<I4", bytestr)
212+
end
213+
return value
214+
end
215+
186216
-- read unsigned 32-bit integer
187217
local function readuint32row(pos, row)
188218
local pos2 = pos + 1 -- due to index starting with 1
@@ -205,16 +235,27 @@ local function readuint128(pos, myfile)
205235
return value
206236
end
207237

238+
-- read unsigned 128-bit integer
239+
local function readuint128row(pos, row)
240+
local pos2 = pos + 1 -- due to index starting with 1
241+
local bytestr = string.sub(row, pos2, pos2 + 15) -- strip 16 bytes
242+
local value = bn.ZERO
243+
if bytestr ~= nil then
244+
value = bn(string.unpack("<I8", bytestr)) + (bn(string.unpack("<I8", bytestr, 9)) << 64) -- cannot read 16 bytes at once so split into 2
245+
end
246+
return value
247+
end
248+
208249
-- read string
209250
local function readstr(pos, myfile)
210251
myfile:seek("set", pos)
211-
local len = myfile:read(1)
252+
local data = myfile:read(256) -- max size of string field + 1 byte for length
212253
local strlen = 0
254+
local len = string.sub(data, 1, 1)
213255
if len ~= nil then
214256
strlen = string.byte(len)
215257
end
216-
myfile:seek("set", pos + 1)
217-
local bytestr = myfile:read(strlen)
258+
local bytestr = string.sub(data, 2, (strlen + 1))
218259
local value = ''
219260
if bytestr ~= nil then
220261
value = bytestr
@@ -246,30 +287,40 @@ function ip2location:new(dbpath)
246287
else
247288
x.f = file
248289
end
249-
x.databasetype = readuint8(1, x.f)
250-
x.databasecolumn = readuint8(2, x.f)
251-
x.databaseyear = readuint8(3, x.f)
252-
x.databasemonth = readuint8(4, x.f)
253-
x.databaseday = readuint8(5, x.f)
254-
255-
x.ipv4databasecount = readuint32(6, x.f):asnumber()
256-
x.ipv4databaseaddr = readuint32(10, x.f):asnumber()
257-
x.ipv6databasecount = readuint32(14, x.f):asnumber()
258-
x.ipv6databaseaddr = readuint32(18, x.f):asnumber()
259-
x.ipv4indexbaseaddr = readuint32(22, x.f):asnumber()
260-
x.ipv6indexbaseaddr = readuint32(26, x.f):asnumber()
261-
x.productcode = readuint8(30, x.f)
262-
x.producttype = readuint8(31, x.f)
263-
x.filesize = readuint32(32, x.f):asnumber()
290+
291+
local row = readrow(x.f, 1, 64) -- 64-byte header
292+
293+
x.databasetype = readuint8header(0, row)
294+
x.databasecolumn = readuint8header(1, row)
295+
x.databaseyear = readuint8header(2, row)
296+
x.databasemonth = readuint8header(3, row)
297+
x.databaseday = readuint8header(4, row)
298+
299+
x.ipv4databasecount = readuint32header(5, row)
300+
x.ipv4databaseaddr = readuint32header(9, row)
301+
x.ipv6databasecount = readuint32header(13, row)
302+
x.ipv6databaseaddr = readuint32header(17, row)
303+
x.ipv4indexbaseaddr = readuint32header(21, row)
304+
x.ipv6indexbaseaddr = readuint32header(25, row)
305+
x.productcode = readuint8header(29, row)
306+
x.producttype = readuint8header(30, row)
307+
x.filesize = readuint32header(31, row)
264308

265309
-- check if is correct BIN (should be 1 for IP2Location BIN file), also checking for zipped file (PK being the first 2 chars)
266310
if (x.productcode ~= 1 and x.databaseyear >= 21) or (x.databasetype == 80 and x.databasecolumn == 75) then -- only BINs from Jan 2021 onwards have this byte set
267311
error(invalid_bin)
268312
end
269313

314+
if x.ipv4indexbaseaddr > 0 then
315+
x.ipv4indexed = true
316+
end
317+
318+
if x.ipv6databasecount > 0 and x.ipv6indexbaseaddr > 0 then
319+
x.ipv6indexed = true
320+
end
321+
270322
x.ipv4columnsize = x.databasecolumn * 4 -- 4 bytes each column
271323
x.ipv6columnsize = 16 + ((x.databasecolumn - 1) * 4) -- 4 bytes each column, except IPFrom column which is 16 bytes
272-
x.columnsize_without_ip = (x.databasecolumn - 1) * 4 -- 4 bytes each column, minus the IPFrom column
273324

274325
local dbt = x.databasetype
275326

@@ -387,7 +438,7 @@ function ip2location:checkip(ip)
387438
end
388439

389440
local ipindex = 0;
390-
if self.ipv4indexbaseaddr > 0 then
441+
if self.ipv4indexed then
391442
ipindex = ((ipnum >> 16) << 3):asnumber() + self.ipv4indexbaseaddr
392443
end
393444
return R.IPV4, ipnum, ipindex
@@ -405,7 +456,7 @@ function ip2location:checkip(ip)
405456
end
406457

407458
local ipindex = 0;
408-
if self.ipv4indexbaseaddr > 0 then
459+
if self.ipv4indexed then
409460
ipindex = ((ipnum >> 16) << 3):asnumber() + self.ipv4indexbaseaddr
410461
end
411462
return R.IPV4, ipnum, ipindex
@@ -470,12 +521,12 @@ function ip2location:checkip(ip)
470521

471522
local ipindex = 0;
472523
if override == 1 then
473-
if self.ipv4indexbaseaddr > 0 then
524+
if self.ipv4indexed then
474525
ipindex = ((ipnum >> 16) << 3):asnumber() + self.ipv4indexbaseaddr
475526
end
476527
return R.IPV4, ipnum, ipindex
477528
else
478-
if self.ipv6indexbaseaddr > 0 then
529+
if self.ipv6indexed then
479530
ipindex = ((ipnum >> 112) << 3):asnumber() + self.ipv6indexbaseaddr
480531
end
481532
return R.IPV6, ipnum, ipindex
@@ -555,7 +606,10 @@ function ip2location:query(ipaddress, mode)
555606
local ipfrom = bn.ZERO
556607
local ipto = bn.ZERO
557608
local maxip = bn.ZERO
558-
local firstcol = 4
609+
local firstcol = 4 -- 4 bytes for IP From
610+
local fullrow
611+
local row
612+
local readlen = 0
559613

560614
-- printme(self)
561615

@@ -565,6 +619,7 @@ function ip2location:query(ipaddress, mode)
565619
maxip = max_ipv4_range
566620
colsize = self.ipv4columnsize
567621
else
622+
firstcol = 16 -- 16 bytes for IP From
568623
baseaddr = self.ipv6databaseaddr
569624
high = self.ipv6databasecount
570625
maxip = max_ipv6_range
@@ -573,8 +628,9 @@ function ip2location:query(ipaddress, mode)
573628

574629
-- reading index
575630
if ipindex > 0 then
576-
low = readuint32(ipindex, self.f):asnumber()
577-
high = readuint32(ipindex + 4, self.f):asnumber()
631+
row = readrow(self.f, ipindex, 8) -- 4 bytes for each IP From/To
632+
low = readuint32row(0, row):asnumber()
633+
high = readuint32row(4, row):asnumber()
578634
end
579635

580636
if ipno >= maxip then
@@ -584,23 +640,22 @@ function ip2location:query(ipaddress, mode)
584640
while low <= high do
585641
mid = round((low + high) / 2)
586642
rowoffset = baseaddr + (mid * colsize)
587-
rowoffset2 = rowoffset + colsize
643+
644+
-- reading IP From + whole row + next IP From
645+
readlen = colsize + firstcol
646+
fullrow = readrow(self.f, rowoffset, readlen)
588647

589648
if iptype == 4 then
590-
ipfrom = readuint32(rowoffset, self.f)
591-
ipto = readuint32(rowoffset2, self.f)
649+
ipfrom = readuint32row(0, fullrow)
650+
ipto = readuint32row(colsize, fullrow)
592651
else
593-
ipfrom = readuint128(rowoffset, self.f)
594-
ipto = readuint128(rowoffset2, self.f)
652+
ipfrom = readuint128row(0, fullrow)
653+
ipto = readuint128row(colsize, fullrow)
595654
end
596655

597656
if (ipno >= ipfrom) and (ipno < ipto) then
598-
if iptype == 6 then
599-
firstcol = 16
600-
end
601-
602-
self.f:seek("set", rowoffset + firstcol - 1)
603-
local row = self.f:read(self.columnsize_without_ip)
657+
rowlen = colsize - firstcol
658+
row = string.sub(fullrow, firstcol + 1, (firstcol + rowlen + 1)) -- extract the actual row data
604659

605660
if (mode&modes.countryshort == 1) and (self.country_enabled == true) then
606661
result.country_short = readstr(readuint32row(self.country_position_offset, row):asnumber(), self.f)

0 commit comments

Comments
 (0)