Skip to content

Commit 38a5808

Browse files
committed
Reduced file I/O
1 parent cd382e7 commit 38a5808

File tree

3 files changed

+94
-49
lines changed

3 files changed

+94
-49
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "ip2location-nodejs",
3-
"version": "9.2.0",
3+
"version": "9.3.0",
44
"description": "IP2Location geolocation component",
55
"keywords": [
66
"ip2location",

src/ip2location.d.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@ export class IP2Location {
22
readRow(readBytes: any, position: any): any;
33
readBin(readBytes: any, position: any, readType: any, isBigInt: any): any;
44
read8(position: any): any;
5+
read8Row(position: any, buffer: any): any;
56
read32(position: any, isBigInt: any): any;
67
read32Row(position: any, buffer: any): any;
8+
read128Row(position: any, buffer: any): any;
9+
read32Or128Row(position: any, buffer: any, len: any): any;
710
read32Or128(position: any, ipType: any): any;
811
read128(position: any): any;
912
readFloatRow(position: any, buffer: any): any;
@@ -113,4 +116,4 @@ export class IPTools {
113116
ipV6ToCIDR(ipFrom: any, ipTo: any): string[];
114117
cidrToIPV4(cidr: any): string[];
115118
cidrToIPV6(cidr: any): any[];
116-
}
119+
}

src/ip2location.js

Lines changed: 89 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ var bigInt = require("big-integer");
44
var https = require("https");
55

66
// For BIN queries
7-
const VERSION = "9.2.0";
7+
const VERSION = "9.3.0";
88
const MAX_INDEX = 65536;
99
const COUNTRY_POSITION = [
1010
0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
@@ -269,6 +269,11 @@ class IP2Location {
269269
return this.readBin(readBytes, position - 1, "int8");
270270
}
271271

272+
// Read 8 bits integer in the buffer
273+
read8Row(position, buffer) {
274+
return buffer.readUInt8(position);
275+
}
276+
272277
// Read 32 bits integer in the database
273278
read32(position, isBigInt) {
274279
let readBytes = 4;
@@ -280,6 +285,29 @@ class IP2Location {
280285
return buffer.readUInt32LE(position);
281286
}
282287

288+
// Read 128 bits integer in the buffer
289+
read128Row(position, buffer) {
290+
let myBig = bigInt(); // zero
291+
let bitShift = 8;
292+
for (let x = 0; x < 16; x++) {
293+
let pos = position + x;
294+
myBig = myBig.add(
295+
bigInt(this.read8Row(pos, buffer)).shiftLeft(bitShift * x)
296+
);
297+
}
298+
return myBig;
299+
}
300+
301+
read32Or128Row(position, buffer, len) {
302+
if (len == 4) {
303+
return this.read32Row(position, buffer);
304+
} else if (len == 16) {
305+
return this.read128Row(position, buffer);
306+
} else {
307+
return 0;
308+
}
309+
}
310+
283311
read32Or128(position, ipType) {
284312
if (ipType == 4) {
285313
return this.read32(position, true);
@@ -303,12 +331,10 @@ class IP2Location {
303331

304332
// Read strings in the database
305333
readStr(position) {
306-
let readBytes = 1;
307-
return this.readBin(
308-
this.readBin(readBytes, position, "int8"),
309-
position + 1,
310-
"str"
311-
);
334+
let readBytes = 256; // max size of string field + 1 byte for the length
335+
let row = this.readRow(readBytes, position + 1);
336+
let len = this.read8Row(0, row);
337+
return row.toString("utf8", 1, len + 1);
312338
}
313339

314340
// Read metadata and indexes
@@ -319,21 +345,24 @@ class IP2Location {
319345
if (this.#binFile && this.#binFile != "") {
320346
this.#fd = fs.openSync(this.#binFile, "r");
321347

322-
this.#myDB.dbType = this.read8(1);
323-
this.#myDB.dbColumn = this.read8(2);
324-
this.#myDB.dbYear = this.read8(3);
325-
this.#myDB.dbMonth = this.read8(4);
326-
this.#myDB.dbDay = this.read8(5);
327-
this.#myDB.dbCount = this.read32(6);
328-
this.#myDB.baseAddress = this.read32(10);
329-
this.#myDB.dbCountIPV6 = this.read32(14);
330-
this.#myDB.baseAddressIPV6 = this.read32(18);
331-
this.#myDB.indexBaseAddress = this.read32(22);
332-
this.#myDB.indexBaseAddressIPV6 = this.read32(26);
333-
this.#myDB.productCode = this.read8(30);
348+
let len = 64; // 64-byte header
349+
let row = this.readRow(len, 1);
350+
351+
this.#myDB.dbType = this.read8Row(0, row);
352+
this.#myDB.dbColumn = this.read8Row(1, row);
353+
this.#myDB.dbYear = this.read8Row(2, row);
354+
this.#myDB.dbMonth = this.read8Row(3, row);
355+
this.#myDB.dbDay = this.read8Row(4, row);
356+
this.#myDB.dbCount = this.read32Row(5, row);
357+
this.#myDB.baseAddress = this.read32Row(9, row);
358+
this.#myDB.dbCountIPV6 = this.read32Row(13, row);
359+
this.#myDB.baseAddressIPV6 = this.read32Row(17, row);
360+
this.#myDB.indexBaseAddress = this.read32Row(21, row);
361+
this.#myDB.indexBaseAddressIPV6 = this.read32Row(25, row);
362+
this.#myDB.productCode = this.read8Row(29, row);
334363
// below 2 fields just read for now, not being used yet
335-
this.#myDB.productType = this.read8(31);
336-
this.#myDB.fileSize = this.read32(32);
364+
this.#myDB.productType = this.read8Row(30, row);
365+
this.#myDB.fileSize = this.read32Row(31, row);
337366

338367
// check if is correct BIN (should be 1 for IP2Location BIN file), also checking for zipped file (PK being the first 2 chars)
339368
if (
@@ -434,20 +463,28 @@ class IP2Location {
434463
this.#categoryEnabled = CATEGORY_POSITION[dbt] != 0 ? 1 : 0;
435464

436465
if (this.#myDB.indexed == 1) {
437-
let pointer = this.#myDB.indexBaseAddress;
466+
len = MAX_INDEX;
467+
if (this.#myDB.indexedIPV6 == 1) {
468+
len += MAX_INDEX;
469+
}
470+
len *= 8; // 4 bytes for both From/To
471+
472+
row = this.readRow(len, this.#myDB.indexBaseAddress);
473+
474+
let pointer = 0;
438475

439476
for (let x = 0; x < MAX_INDEX; x++) {
440477
this.#indexArrayIPV4[x] = Array(2);
441-
this.#indexArrayIPV4[x][0] = this.read32(pointer);
442-
this.#indexArrayIPV4[x][1] = this.read32(pointer + 4);
478+
this.#indexArrayIPV4[x][0] = this.read32Row(pointer, row);
479+
this.#indexArrayIPV4[x][1] = this.read32Row(pointer + 4, row);
443480
pointer += 8;
444481
}
445482

446-
if (this.#myDB.indexBaseAddressIPV6 > 0) {
483+
if (this.#myDB.indexedIPV6 == 1) {
447484
for (let x = 0; x < MAX_INDEX; x++) {
448485
this.#indexArrayIPV6[x] = Array(2);
449-
this.#indexArrayIPV6[x][0] = this.read32(pointer);
450-
this.#indexArrayIPV6[x][1] = this.read32(pointer + 4);
486+
this.#indexArrayIPV6[x][0] = this.read32Row(pointer, row);
487+
this.#indexArrayIPV6[x][1] = this.read32Row(pointer + 4, row);
451488
pointer += 8;
452489
}
453490
}
@@ -509,8 +546,9 @@ class IP2Location {
509546
let rowOffset2;
510547
let ipFrom;
511548
let ipTo;
512-
let firstCol;
549+
let firstCol = 4; // IP From is 4 bytes
513550
let row;
551+
let fullRow;
514552

515553
if (ipType == 4) {
516554
MAX_IP_RANGE = MAX_IPV4_RANGE;
@@ -519,9 +557,11 @@ class IP2Location {
519557
columnSize = this.#ipV4ColumnSize;
520558
ipNumber = dot2Num(myIP);
521559

522-
indexAddress = ipNumber >>> 16;
523-
low = this.#indexArrayIPV4[indexAddress][0];
524-
high = this.#indexArrayIPV4[indexAddress][1];
560+
if (this.#myDB.indexed == 1) {
561+
indexAddress = ipNumber >>> 16;
562+
low = this.#indexArrayIPV4[indexAddress][0];
563+
high = this.#indexArrayIPV4[indexAddress][1];
564+
}
525565
} else if (ipType == 6) {
526566
MAX_IP_RANGE = MAX_IPV6_RANGE;
527567
high = this.#myDB.dbCountIPV6;
@@ -544,16 +584,20 @@ class IP2Location {
544584
} else {
545585
ipNumber = ipNumber.not().and(LAST_32_BITS).toJSNumber();
546586
}
547-
indexAddress = ipNumber >>> 16;
548-
low = this.#indexArrayIPV4[indexAddress][0];
549-
high = this.#indexArrayIPV4[indexAddress][1];
587+
if (this.#myDB.indexed == 1) {
588+
indexAddress = ipNumber >>> 16;
589+
low = this.#indexArrayIPV4[indexAddress][0];
590+
high = this.#indexArrayIPV4[indexAddress][1];
591+
}
550592
} else {
551-
indexAddress = ipNumber.shiftRight(112).toJSNumber();
552-
low = this.#indexArrayIPV6[indexAddress][0];
553-
high = this.#indexArrayIPV6[indexAddress][1];
593+
firstCol = 16; // IPv6 is 16 bytes
594+
if (this.#myDB.indexedIPV6 == 1) {
595+
indexAddress = ipNumber.shiftRight(112).toJSNumber();
596+
low = this.#indexArrayIPV6[indexAddress][0];
597+
high = this.#indexArrayIPV6[indexAddress][1];
598+
}
554599
}
555600
}
556-
557601
data.ip = myIP;
558602
ipNumber = bigInt(ipNumber);
559603

@@ -564,25 +608,23 @@ class IP2Location {
564608
data.ipNo = ipNumber.toString();
565609

566610
while (low <= high) {
567-
mid = parseInt((low + high) / 2);
611+
mid = Math.trunc((low + high) / 2);
568612
rowOffset = baseAddress + mid * columnSize;
569613
rowOffset2 = rowOffset + columnSize;
570614

571-
ipFrom = this.read32Or128(rowOffset, ipType);
572-
ipTo = this.read32Or128(rowOffset2, ipType);
615+
// reading IP From + whole row + next IP From
616+
fullRow = this.readRow(columnSize + firstCol, rowOffset);
617+
ipFrom = this.read32Or128Row(0, fullRow, firstCol);
618+
ipTo = this.read32Or128Row(columnSize, fullRow, firstCol);
573619

574620
ipFrom = bigInt(ipFrom);
575621
ipTo = bigInt(ipTo);
576622

577623
if (ipFrom.leq(ipNumber) && ipTo.gt(ipNumber)) {
578624
loadMesg(data, MSG_NOT_SUPPORTED); // load default message
579625

580-
firstCol = 4;
581-
if (ipType == 6) {
582-
firstCol = 16;
583-
}
584-
585-
row = this.readRow(columnSize - firstCol, rowOffset + firstCol);
626+
let rowLen = columnSize - firstCol;
627+
row = fullRow.subarray(firstCol, firstCol + rowLen); // extract the actual row data
586628

587629
if (this.#countryEnabled) {
588630
if (

0 commit comments

Comments
 (0)