From 5c1522fce4b1039d8f0e249159ec30d4990af5e9 Mon Sep 17 00:00:00 2001 From: lion Date: Wed, 15 Oct 2025 10:13:10 +0800 Subject: [PATCH] search test app is ready --- binding/javascript/index.d.ts | 1 + binding/javascript/searcher.js | 4 + binding/javascript/tests/search.app.js | 122 +++++++++++++++++++++++++ 3 files changed, 127 insertions(+) create mode 100644 binding/javascript/tests/search.app.js diff --git a/binding/javascript/index.d.ts b/binding/javascript/index.d.ts index 727fbe6..e55dd3d 100644 --- a/binding/javascript/index.d.ts +++ b/binding/javascript/index.d.ts @@ -40,6 +40,7 @@ export declare class Searcher { cBuffer: Buffer | null; constructor(version: Version, dbPath: string | null, vectorIndex: Buffer | null, cBuffer: Buffer | null); + getIPVersion(): Version; getIOCount(): number; search(ip: string | Buffer): string; read(offset: number, buff: Buffer, stats?: any): void; diff --git a/binding/javascript/searcher.js b/binding/javascript/searcher.js index 75db934..67be219 100644 --- a/binding/javascript/searcher.js +++ b/binding/javascript/searcher.js @@ -28,6 +28,10 @@ export class Searcher { } } + getIPVersion() { + return this.version; + } + getIOCount() { return this.ioCount; } diff --git a/binding/javascript/tests/search.app.js b/binding/javascript/tests/search.app.js new file mode 100644 index 0000000..9a9ebda --- /dev/null +++ b/binding/javascript/tests/search.app.js @@ -0,0 +1,122 @@ +// Copyright 2022 The Ip2Region Authors. All rights reserved. +// Use of this source code is governed by a Apache2.0-style +// license that can be found in the LICENSE file. + +// app to do the xdb search +// @Author Lion + +import * as xdb from '../index.js'; +import {ArgumentParser} from 'argparse'; +import fs from 'fs'; +import { parseIP } from '../util.js'; + + +const parser = new ArgumentParser({ + add_help: true, + description: 'ip2region search script', + prog: 'node tests/search.app.js', + usage: 'Usage %(prog)s [command options]' +}); + +parser.add_argument('--db', {help: 'ip2region binary xdb file path'}); +parser.add_argument('--cache-policy', {help: 'cache policy: file/vectorIndex/content, default: vectorIndex'}); + +const args = parser.parse_args(); +const dbPath = args.db || ''; +const cachePolicy = args.cache_policy || 'vectorIndex'; + +// create the searcher +const createSearcher = () => { + const handle = fs.openSync(dbPath, 'r'); + + // verify the xdb file + // @Note: do NOT call it every time you create a searcher since this will slow + // down the search response. + // @see the verify function for details. + xdb.verify(handle); + + // get the ip version from the header + const version = xdb.versionFromHeader(xdb.loadHeader(handle)); + + let searcher = null; + switch(cachePolicy) { + case 'file': + searcher = xdb.newWithFileOnly(version, dbPath); + break; + case 'vectorIndex': + const vIndex = xdb.loadVectorIndexFromFile(dbPath); + searcher = xdb.newWithVectorIndex(version, dbPath, vIndex); + break; + case 'content': + const cBuffer = xdb.loadContentFromFile(dbPath); + searcher = xdb.newWithBuffer(version, cBuffer); + break; + default: + fs.closeSync(handle); + throw new Error(`invalid cache-policy '${cachePolicy}'`); + } + + fs.closeSync(handle); + return searcher; +} + +const readlineSync = () => { + return new Promise((resolve, reject) => { + process.stdin.resume() + process.stdin.on('data', (buff) => { + process.stdin.pause(); + resolve(buff.toString('utf-8')) + }); + }); +} + +const main = async () => { + if (dbPath.length < 1) { + parser.print_help(); + return; + } + + const searcher = createSearcher(); + console.log(`ip2region xdb searcher test program +source xdb: ${dbPath} (${searcher.getIPVersion().name}, ${cachePolicy}) +type 'quit' to exit`); + + while (true) { + process.stdout.write('ip2region>> '); + + // get the input ip + const ipString = (await readlineSync()).trim(); + if (ipString.length == 0) { + continue; + } + + if (ipString == 'quit') { + break; + } + + // parse the ip address + const sTime = process.hrtime(); + let ipBytes = null; + try { + ipBytes = xdb.parseIP(ipString); + } catch (e) { + console.log(`failed to parse ip: ${e.message}`); + continue; + } + + // do the search + let region = null; + try { + region = await searcher.search(ipBytes); + } catch (e) { + console.log(`{err: ${e.message}, ioCount: ${searcher.getIOCount()}}`); + continue; + } + + const diff = process.hrtime(sTime); + const took = diff[0] * 1_000_000 + diff[1] / 1e6; + console.log(`{region: ${region}, ioCount: ${searcher.getIOCount()}, took: ${took} μs}`); + } +} + +main(); \ No newline at end of file