ip2region-ts
Version:
node-ip2region written in typescript with esm support, latest xdb
218 lines (216 loc) • 7.57 kB
JavaScript
;
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var index_exports = {};
__export(index_exports, {
defaultDbFile: () => defaultDbFile,
isValidIp: () => isValidIp,
loadContentFromFile: () => loadContentFromFile,
loadVectorIndexFromFile: () => loadVectorIndexFromFile,
newWithBuffer: () => newWithBuffer,
newWithFileOnly: () => newWithFileOnly,
newWithVectorIndex: () => newWithVectorIndex
});
module.exports = __toCommonJS(index_exports);
var import_node_buffer = require("buffer");
var import_node_fs = __toESM(require("fs"), 1);
var import_node_path = __toESM(require("path"), 1);
var import_node_process = __toESM(require("process"), 1);
var VectorIndexSize = 8;
var VectorIndexCols = 256;
var VectorIndexLength = 256 * 256 * (4 + 4);
var SegmentIndexSize = 14;
var IP_REGEX = /^(?:(?:\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])$/;
var getStartEndPtr = Symbol("#getStartEndPtr");
var getBuffer = Symbol("#getBuffer");
var openFilePromise = Symbol("#openFilePromise");
var defaultDbFile = import_node_path.default.resolve(__dirname, "../data/ip2region.xdb");
var Searcher = class {
_dbFile;
_vectorIndex;
_buffer;
constructor(dbFile, vectorIndex, buffer) {
this._dbFile = dbFile;
this._vectorIndex = vectorIndex;
this._buffer = buffer;
if (this._buffer) {
this._vectorIndex = this._buffer.subarray(256, 256 + VectorIndexLength);
}
}
async [getStartEndPtr](idx, fd, ioStatus) {
if (this._vectorIndex) {
const sPtr = this._vectorIndex.readUInt32LE(idx);
const ePtr = this._vectorIndex.readUInt32LE(idx + 4);
return { sPtr, ePtr };
} else {
const buf = await this[getBuffer](256 + idx, 8, fd, ioStatus);
const sPtr = buf.readUInt32LE();
const ePtr = buf.readUInt32LE(4);
return { sPtr, ePtr };
}
}
async [getBuffer](offset, length, fd, ioStatus) {
if (this._buffer) {
return this._buffer.subarray(offset, offset + length);
} else {
const buf = import_node_buffer.Buffer.alloc(length);
return await new Promise((resolve, reject) => {
ioStatus.ioCount += 1;
import_node_fs.default.read(fd, buf, 0, length, offset, (err) => {
if (err) {
reject(err);
} else {
resolve(buf);
}
});
});
}
}
[openFilePromise](fileName) {
return new Promise((resolve, reject) => {
import_node_fs.default.open(fileName, "r", (err, fd) => {
if (err) {
reject(err);
} else {
resolve(fd);
}
});
});
}
async search(ip) {
const startTime = import_node_process.default.hrtime();
const ioStatus = {
ioCount: 0
};
if (!isValidIp(ip)) {
throw new Error(`IP: ${ip} is invalid`);
}
let fd = 0;
if (!this._buffer && this._dbFile) {
fd = await this[openFilePromise](this._dbFile);
}
const ps = ip.split(".");
const i0 = Number.parseInt(ps[0]);
const i1 = Number.parseInt(ps[1]);
const i2 = Number.parseInt(ps[2]);
const i3 = Number.parseInt(ps[3]);
const ipInt = i0 * 256 * 256 * 256 + i1 * 256 * 256 + i2 * 256 + i3;
const idx = i0 * VectorIndexCols * VectorIndexSize + i1 * VectorIndexSize;
const { sPtr, ePtr } = await this[getStartEndPtr](idx, fd, ioStatus);
let l = 0;
let h = (ePtr - sPtr) / SegmentIndexSize;
let result = null;
while (l <= h) {
const m = l + h >> 1;
const p = sPtr + m * SegmentIndexSize;
const buff = await this[getBuffer](p, SegmentIndexSize, fd, ioStatus);
const sip = buff.readUInt32LE(0);
if (ipInt < sip) {
h = m - 1;
} else {
const eip = buff.readUInt32LE(4);
if (ipInt > eip) {
l = m + 1;
} else {
const dataLen = buff.readUInt16LE(8);
const dataPtr = buff.readUInt32LE(10);
const data = await this[getBuffer](dataPtr, dataLen, fd, ioStatus);
result = data.toString("utf8");
break;
}
}
}
if (fd) {
import_node_fs.default.close(fd, () => {
});
}
const diff = import_node_process.default.hrtime(startTime);
const took = (diff[0] * 1e9 + diff[1]) / 1e3;
return { region: result, ioCount: ioStatus.ioCount, took };
}
};
function _checkFile(dbPath) {
try {
import_node_fs.default.accessSync(dbPath, import_node_fs.default.constants.F_OK);
} catch (error) {
throw new Error(`${dbPath} ${error ? "does not exist" : "exists"}`);
}
try {
import_node_fs.default.accessSync(dbPath, import_node_fs.default.constants.R_OK);
} catch (error) {
throw new Error(`${dbPath} ${error ? "is not readable" : "is readable"}`);
}
}
function isValidIp(ip) {
return IP_REGEX.test(ip);
}
function newWithFileOnly(dbPath) {
_checkFile(dbPath);
return new Searcher(dbPath, null, null);
}
function newWithVectorIndex(dbPath, vectorIndex) {
_checkFile(dbPath);
if (!import_node_buffer.Buffer.isBuffer(vectorIndex)) {
throw new TypeError("vectorIndex is invalid");
}
return new Searcher(dbPath, vectorIndex, null);
}
function newWithBuffer(buffer) {
if (!import_node_buffer.Buffer.isBuffer(buffer)) {
throw new TypeError("buffer is invalid");
}
return new Searcher(null, null, buffer);
}
function loadVectorIndexFromFile(dbPath) {
const fd = import_node_fs.default.openSync(dbPath, "r");
const buffer = import_node_buffer.Buffer.alloc(VectorIndexLength);
import_node_fs.default.readSync(fd, buffer, 0, VectorIndexLength, 256);
import_node_fs.default.close(fd, () => {
});
return buffer;
}
function loadContentFromFile(dbPath) {
const stats = import_node_fs.default.statSync(dbPath);
const buffer = import_node_buffer.Buffer.alloc(stats.size);
const fd = import_node_fs.default.openSync(dbPath, "r");
import_node_fs.default.readSync(fd, buffer, 0, stats.size, 0);
import_node_fs.default.close(fd, () => {
});
return buffer;
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
defaultDbFile,
isValidIp,
loadContentFromFile,
loadVectorIndexFromFile,
newWithBuffer,
newWithFileOnly,
newWithVectorIndex
});