From b52555c5ecc5c0c90b8e1f1b97874155acc6d379 Mon Sep 17 00:00:00 2001 From: lionsoul2014 Date: Fri, 12 Dec 2025 11:49:10 +0800 Subject: [PATCH 01/10] xdbPath to xdbFile and with path and File create supports --- .../org/lionsoul/ip2region/xdb/Searcher.java | 28 +++++++++++++------ 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/binding/java/src/main/java/org/lionsoul/ip2region/xdb/Searcher.java b/binding/java/src/main/java/org/lionsoul/ip2region/xdb/Searcher.java index 5dd22c4..8111c8e 100644 --- a/binding/java/src/main/java/org/lionsoul/ip2region/xdb/Searcher.java +++ b/binding/java/src/main/java/org/lionsoul/ip2region/xdb/Searcher.java @@ -4,6 +4,8 @@ package org.lionsoul.ip2region.xdb; +import java.io.File; + // xdb searcher (Not thread safe implementation) // @Author Lion // @Date 2022/06/23 @@ -29,7 +31,7 @@ public class Searcher { private final Version version; // random access file handle for file-based search - private final String xdbPath; + private final File xdbFile; private final RandomAccessFile handle; private int ioCount = 0; @@ -46,12 +48,20 @@ public class Searcher { // --- static method to create searchers - public static Searcher newWithFileOnly(Version version, String dbPath) throws IOException { - return new Searcher(version, dbPath, null, null); + public static Searcher newWithFileOnly(Version version, String xdbPath) throws IOException { + return new Searcher(version, new File(xdbPath), null, null); } - public static Searcher newWithVectorIndex(Version version, String dbPath, byte[] vectorIndex) throws IOException { - return new Searcher(version, dbPath, vectorIndex, null); + public static Searcher newWithFileOnly(Version version, File xdbFile) throws IOException { + return new Searcher(version, xdbFile, null, null); + } + + public static Searcher newWithVectorIndex(Version version, String xdbPath, byte[] vectorIndex) throws IOException { + return new Searcher(version, new File(xdbPath), vectorIndex, null); + } + + public static Searcher newWithVectorIndex(Version version, File xdbFile, byte[] vectorIndex) throws IOException { + return new Searcher(version, xdbFile, vectorIndex, null); } public static Searcher newWithBuffer(Version version, LongByteArray cBuff) throws IOException { @@ -60,15 +70,15 @@ public class Searcher { // --- End of creator - public Searcher(Version version, String dbFile, byte[] vectorIndex, LongByteArray cBuff) throws IOException { + public Searcher(Version version, File xdbFile, byte[] vectorIndex, LongByteArray cBuff) throws IOException { this.version = version; - this.xdbPath = dbFile; + this.xdbFile = xdbFile; if (cBuff != null) { this.handle = null; this.vectorIndex = null; this.contentBuff = cBuff; } else { - this.handle = new RandomAccessFile(dbFile, "r"); + this.handle = new RandomAccessFile(xdbFile, "r"); this.vectorIndex = vectorIndex; this.contentBuff = null; } @@ -179,7 +189,7 @@ public class Searcher { return String.format( "%s->{version:%s, xdb:%s, vIndex:%s, cBuffer:%s}", super.toString(), - version.name, xdbPath, + version.name, xdbFile.getAbsolutePath(), vectorIndex == null ? "null" : String.valueOf(vectorIndex.length), contentBuff == null ? "null" : String.valueOf(contentBuff.length()) ); From c2a0a550057b5edafbb5736a1a3e3fd2ac304129 Mon Sep 17 00:00:00 2001 From: lionsoul2014 Date: Fri, 12 Dec 2025 11:50:05 +0800 Subject: [PATCH 02/10] config build from xdb path and File supports --- .../org/lionsoul/ip2region/SearcherTest.java | 6 +-- .../lionsoul/ip2region/service/Config.java | 19 ++++---- .../ip2region/service/ConfigBuilder.java | 48 ++++++++++++++++--- .../service/InvalidCachePolicyException.java | 11 ----- .../service/InvalidConfigException.java | 7 +++ .../lionsoul/ip2region/service/Ip2Region.java | 20 +++++--- .../ip2region/service/SearcherPool.java | 2 +- .../ip2region/service/ConfigTest.java | 4 +- .../ip2region/service/Ip2RegionTest.java | 10 ++-- 9 files changed, 83 insertions(+), 44 deletions(-) delete mode 100644 binding/java/src/main/java/org/lionsoul/ip2region/service/InvalidCachePolicyException.java create mode 100644 binding/java/src/main/java/org/lionsoul/ip2region/service/InvalidConfigException.java diff --git a/binding/java/src/main/java/org/lionsoul/ip2region/SearcherTest.java b/binding/java/src/main/java/org/lionsoul/ip2region/SearcherTest.java index 279160f..33506f9 100644 --- a/binding/java/src/main/java/org/lionsoul/ip2region/SearcherTest.java +++ b/binding/java/src/main/java/org/lionsoul/ip2region/SearcherTest.java @@ -7,7 +7,7 @@ package org.lionsoul.ip2region; import org.lionsoul.ip2region.service.Config; -import org.lionsoul.ip2region.service.InvalidCachePolicyException; +import org.lionsoul.ip2region.service.InvalidConfigException; import org.lionsoul.ip2region.service.Ip2Region; import org.lionsoul.ip2region.xdb.InetAddressException; import org.lionsoul.ip2region.xdb.XdbException; @@ -49,7 +49,7 @@ public class SearcherTest { } public static final Ip2Region createService( - String v4XdbPath, String v4CachePolicy, String v6XdbPath, String v6CachePolicy) throws IOException, XdbException, InvalidCachePolicyException { + String v4XdbPath, String v4CachePolicy, String v6XdbPath, String v6CachePolicy) throws IOException, XdbException, InvalidConfigException { final Config v4Config = Config.custom() .setCachePolicy(Config.cachePolicyFromName(v4CachePolicy)) .setSearchers(1) @@ -89,7 +89,7 @@ public class SearcherTest { } } - public static void searchTest(String[] args) throws IOException, XdbException, InvalidCachePolicyException, InterruptedException { + public static void searchTest(String[] args) throws IOException, XdbException, InterruptedException, InvalidConfigException { String help = ""; String v4DbPath = "", v4CachePolicy = "vectorIndex"; String v6DbPath = "", v6CachePolicy = "vectorIndex"; diff --git a/binding/java/src/main/java/org/lionsoul/ip2region/service/Config.java b/binding/java/src/main/java/org/lionsoul/ip2region/service/Config.java index 8e8388b..1e4fbb3 100644 --- a/binding/java/src/main/java/org/lionsoul/ip2region/service/Config.java +++ b/binding/java/src/main/java/org/lionsoul/ip2region/service/Config.java @@ -4,6 +4,7 @@ package org.lionsoul.ip2region.service; +import java.io.File; import java.io.IOException; import org.lionsoul.ip2region.xdb.Header; @@ -27,7 +28,7 @@ public class Config { public final Version ipVersion; // xdb file path - public final String xdbPath; + public final File xdbFile; public final Header header; public final byte[] vIndex; @@ -40,21 +41,21 @@ public class Config { return new ConfigBuilder(); } - protected Config(int cachePolicy, Version ipVersion, String xdbPath, + protected Config(int cachePolicy, Version ipVersion, File xdbFile, Header header, byte[] vIndex, LongByteArray cBuffer, int searchers) throws IOException, XdbException { this.cachePolicy = cachePolicy; this.ipVersion = ipVersion; - this.xdbPath = xdbPath; - this.header = header; - this.vIndex = vIndex; + this.xdbFile = xdbFile; + this.header = header; + this.vIndex = vIndex; this.cBuffer = cBuffer; final Version xVersion = Version.fromHeader(header); // verify the ip version (ipVersion and the version of the xdb file should be the same) if (header.ipVersion != ipVersion.id) { throw new XdbException("ip verison not match: xdb file " - + xdbPath + " (" + xVersion.name + "), as " + ipVersion.name + " expected"); + + xdbFile.getAbsolutePath() + " (" + xVersion.name + "), as " + ipVersion.name + " expected"); } this.searchers = searchers; @@ -65,7 +66,7 @@ public class Config { sb.append('{'); sb.append("cache_policy:").append(cachePolicy).append(','); sb.append("version:").append(ipVersion.toString()).append(','); - sb.append("xdb_path:").append(xdbPath).append(','); + sb.append("xdb_path:").append(xdbFile.getAbsolutePath()).append(','); sb.append("header:").append(header.toString()).append(','); if (vIndex == null) { sb.append("v_index: null, "); @@ -82,7 +83,7 @@ public class Config { return sb.toString(); } - public static final int cachePolicyFromName(String name) throws InvalidCachePolicyException { + public static final int cachePolicyFromName(String name) throws InvalidConfigException { final String lName = name.toLowerCase(); if (lName.equals("file") || lName.equals("nocache")) { return NoCache; @@ -91,7 +92,7 @@ public class Config { } else if (lName.equals("content") || lName.equals("buffercache")) { return BufferCache; } else { - throw new InvalidCachePolicyException("invalid cache policy `" + name + "`"); + throw new InvalidConfigException("invalid cache policy `" + name + "`"); } } } \ No newline at end of file diff --git a/binding/java/src/main/java/org/lionsoul/ip2region/service/ConfigBuilder.java b/binding/java/src/main/java/org/lionsoul/ip2region/service/ConfigBuilder.java index dd838bd..7ffbd53 100644 --- a/binding/java/src/main/java/org/lionsoul/ip2region/service/ConfigBuilder.java +++ b/binding/java/src/main/java/org/lionsoul/ip2region/service/ConfigBuilder.java @@ -4,7 +4,9 @@ package org.lionsoul.ip2region.service; +import java.io.File; import java.io.IOException; +import java.io.InputStream; import java.io.RandomAccessFile; import org.lionsoul.ip2region.xdb.Header; @@ -23,8 +25,11 @@ public class ConfigBuilder { // cache policy private int cachePolicy = Config.VIndexCache; - // xdb file path + // xdb file path / Object / InputStream. + // Priority: InputStream -> File Object -> String path private String xdbPath = null; + private File xdbFile = null; + private InputStream xdbInputStream = null; // searchers private int searchers = 20; @@ -45,14 +50,45 @@ public class ConfigBuilder { return this; } + public ConfigBuilder setXdbFile(File xdbFile) { + this.xdbFile = xdbFile; + return this; + } + + public ConfigBuilder setXdbInputStream(InputStream xdbInputStream) { + this.xdbInputStream = xdbInputStream; + return this; + } + public ConfigBuilder setSearchers(int searchers) { this.searchers = searchers; return this; } - private Config build(Version ipVersion) throws IOException, XdbException { + private Config build(Version ipVersion) throws IOException, XdbException, InvalidConfigException { + if (xdbInputStream == null) { + // everyting is fine + } else if (cachePolicy != Config.BufferCache) { + // @Note: we can't directly rewrite the cachePolicy to Config.BufferCache. + // you must know what you are doing. + throw new InvalidConfigException("SetXdbInputStream could ONLY be used with cachePolicy = Config.BufferCache"); + } else { + + // return new Config(cachePolicy, ipVersion, null, header, null, cBuffer, searchers); + return null; + } + // load the header and the cache buffer - final RandomAccessFile raf = new RandomAccessFile(xdbPath, "r"); + final File xdbFile; + if (this.xdbFile != null) { + xdbFile = this.xdbFile; + } else if (this.xdbPath != null) { + xdbFile = new File(this.xdbPath); + } else { + throw new InvalidConfigException("Both xdbFile and xdbPath is null"); + } + + final RandomAccessFile raf = new RandomAccessFile(xdbFile, "r"); // 1, verify the xdb Searcher.verify(raf); @@ -67,16 +103,16 @@ public class ConfigBuilder { final LongByteArray cBuffer = cachePolicy == Config.BufferCache ? Searcher.loadContent(raf) : null; raf.close(); - return new Config(cachePolicy, ipVersion, xdbPath, header, vIndex, cBuffer, searchers); + return new Config(cachePolicy, ipVersion, xdbFile, header, vIndex, cBuffer, searchers); } // build the final #Config instance for IPv4 - public Config asV4() throws IOException, XdbException { + public Config asV4() throws IOException, XdbException, InvalidConfigException { return build(Version.IPv4); } // build the final #Config instance for IPv6 - public Config asV6() throws IOException, XdbException { + public Config asV6() throws IOException, XdbException, InvalidConfigException { return build(Version.IPv6); } diff --git a/binding/java/src/main/java/org/lionsoul/ip2region/service/InvalidCachePolicyException.java b/binding/java/src/main/java/org/lionsoul/ip2region/service/InvalidCachePolicyException.java deleted file mode 100644 index 348e58f..0000000 --- a/binding/java/src/main/java/org/lionsoul/ip2region/service/InvalidCachePolicyException.java +++ /dev/null @@ -1,11 +0,0 @@ -// 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. - -package org.lionsoul.ip2region.service; - -public class InvalidCachePolicyException extends Exception { - public InvalidCachePolicyException(String str) { - super(str); - } -} diff --git a/binding/java/src/main/java/org/lionsoul/ip2region/service/InvalidConfigException.java b/binding/java/src/main/java/org/lionsoul/ip2region/service/InvalidConfigException.java new file mode 100644 index 0000000..a46a00c --- /dev/null +++ b/binding/java/src/main/java/org/lionsoul/ip2region/service/InvalidConfigException.java @@ -0,0 +1,7 @@ +package org.lionsoul.ip2region.service; + +public class InvalidConfigException extends Exception { + public InvalidConfigException(String str) { + super(str); + } +} diff --git a/binding/java/src/main/java/org/lionsoul/ip2region/service/Ip2Region.java b/binding/java/src/main/java/org/lionsoul/ip2region/service/Ip2Region.java index a6fc03a..d55073b 100644 --- a/binding/java/src/main/java/org/lionsoul/ip2region/service/Ip2Region.java +++ b/binding/java/src/main/java/org/lionsoul/ip2region/service/Ip2Region.java @@ -4,6 +4,7 @@ package org.lionsoul.ip2region.service; +import java.io.File; import java.io.IOException; import org.lionsoul.ip2region.xdb.InetAddressException; @@ -37,23 +38,28 @@ public class Ip2Region { return new Ip2Region(v4Config, v6Config).init(); } - public static final Ip2Region create(final String v4XdbPath, final String v6XdbPath) throws IOException, XdbException { - return new Ip2Region(v4XdbPath, v6XdbPath).init(); + public static final Ip2Region create(final String v4XdbPath, final String v6XdbPath) throws IOException, XdbException, InvalidConfigException { + return new Ip2Region(new File(v4XdbPath), new File(v6XdbPath)).init(); + } + + public static final Ip2Region create(final File v4XdbFile, final File v6XdbFile) throws IOException, XdbException, InvalidConfigException { + return new Ip2Region(v4XdbFile, v6XdbFile).init(); } /** * init the ip2reigon with two xdb file path and default cachePolicy vIndex. * set it to null to disabled the search for specified version * - * @param v4XdbPath - * @param v6XdbPath + * @param v4XdbFile + * @param v6XdbFile * @throws XdbException * @throws IOException + * @throws InvalidConfigException */ - protected Ip2Region(String v4XdbPath, String v6XdbPath) throws IOException, XdbException { + protected Ip2Region(File v4XdbFile, File v6XdbFile) throws IOException, XdbException, InvalidConfigException { this( - v4XdbPath == null ? null : Config.custom().setXdbPath(v4XdbPath).asV4(), - v6XdbPath == null ? null : Config.custom().setXdbPath(v6XdbPath).asV6() + v4XdbFile == null ? null : Config.custom().setXdbFile(v4XdbFile).asV4(), + v6XdbFile == null ? null : Config.custom().setXdbFile(v6XdbFile).asV6() ); } diff --git a/binding/java/src/main/java/org/lionsoul/ip2region/service/SearcherPool.java b/binding/java/src/main/java/org/lionsoul/ip2region/service/SearcherPool.java index 48c7473..aa97e00 100644 --- a/binding/java/src/main/java/org/lionsoul/ip2region/service/SearcherPool.java +++ b/binding/java/src/main/java/org/lionsoul/ip2region/service/SearcherPool.java @@ -60,7 +60,7 @@ public class SearcherPool { protected SearcherPool init() throws IOException { // create the searchers for (int i = pool.size(); i < config.searchers; i++) { - final Searcher searcher = new Searcher(config.ipVersion, config.xdbPath, config.vIndex, config.cBuffer); + final Searcher searcher = new Searcher(config.ipVersion, config.xdbFile, config.vIndex, config.cBuffer); pool.add(searcher); } diff --git a/binding/java/src/test/java/org/lionsoul/ip2region/service/ConfigTest.java b/binding/java/src/test/java/org/lionsoul/ip2region/service/ConfigTest.java index 56a5b66..c716139 100644 --- a/binding/java/src/test/java/org/lionsoul/ip2region/service/ConfigTest.java +++ b/binding/java/src/test/java/org/lionsoul/ip2region/service/ConfigTest.java @@ -21,7 +21,7 @@ public class ConfigTest { } @Test - public void testBuildV4Config() throws IOException, XdbException { + public void testBuildV4Config() throws IOException, XdbException, InvalidConfigException { final Config v4Config = Config.custom() .setCachePolicy(Config.BufferCache) .setXdbPath(getDataPath("ip2region_v4.xdb")) @@ -31,7 +31,7 @@ public class ConfigTest { } @Test - public void testBuildV6Config() throws IOException, XdbException { + public void testBuildV6Config() throws IOException, XdbException, InvalidConfigException { final Config v4Config = Config.custom() .setCachePolicy(Config.VIndexCache) .setXdbPath(getDataPath("ip2region_v6.xdb")) diff --git a/binding/java/src/test/java/org/lionsoul/ip2region/service/Ip2RegionTest.java b/binding/java/src/test/java/org/lionsoul/ip2region/service/Ip2RegionTest.java index d0fcab7..d78829d 100644 --- a/binding/java/src/test/java/org/lionsoul/ip2region/service/Ip2RegionTest.java +++ b/binding/java/src/test/java/org/lionsoul/ip2region/service/Ip2RegionTest.java @@ -17,7 +17,7 @@ public class Ip2RegionTest { private static final Log log = Log.getLogger(Ip2RegionTest.class).setLevel(Log.DEBUG); @Test - public void TestConfigCreate() throws IOException, XdbException, InetAddressException, InterruptedException { + public void TestConfigCreate() throws IOException, XdbException, InetAddressException, InterruptedException, InvalidConfigException { final Config v4Config = Config.custom() .setCachePolicy(Config.NoCache) .setSearchers(10) @@ -46,7 +46,7 @@ public class Ip2RegionTest { } @Test - public void TestPathCreate() throws InetAddressException, IOException, XdbException, InterruptedException { + public void TestPathCreate() throws InetAddressException, IOException, XdbException, InterruptedException, InvalidConfigException { byte[] v4Bytes = Util.parseIP("113.92.157.29"); byte[] v6Bytes = Util.parseIP("240e:3b7:3272:d8d0:db09:c067:8d59:539e"); final Ip2Region ip2Region = Ip2Region.create(ConfigTest.getDataPath("ip2region_v4.xdb"), ConfigTest.getDataPath("ip2region_v6.xdb")); @@ -63,7 +63,7 @@ public class Ip2RegionTest { } @Test - public void TestInMemSearch() throws IOException, XdbException, InetAddressException, InterruptedException { + public void TestInMemSearch() throws IOException, XdbException, InetAddressException, InterruptedException, InvalidConfigException { final Config v4Config = Config.custom() .setCachePolicy(Config.BufferCache) .setXdbPath(ConfigTest.getDataPath("ip2region_v4.xdb")) @@ -90,7 +90,7 @@ public class Ip2RegionTest { } @Test - public void TestConcurrentCall() throws IOException, XdbException, InetAddressException, InterruptedException { + public void TestConcurrentCall() throws IOException, XdbException, InetAddressException, InterruptedException, InvalidConfigException { final Config v4Config = Config.custom() .setCachePolicy(Config.VIndexCache) .setSearchers(15) @@ -144,7 +144,7 @@ public class Ip2RegionTest { } @Test - public void TestV4Only() throws IOException, XdbException, InetAddressException, InterruptedException { + public void TestV4Only() throws IOException, XdbException, InetAddressException, InterruptedException, InvalidConfigException { final Config v4Config = Config.custom() .setCachePolicy(Config.NoCache) .setXdbPath(ConfigTest.getDataPath("ip2region_v4.xdb")) From c68cc0097c8fd957f5aa01701df9a761c54eb142 Mon Sep 17 00:00:00 2001 From: lionsoul2014 Date: Fri, 12 Dec 2025 11:58:03 +0800 Subject: [PATCH 03/10] xdbFile is null check --- .../src/main/java/org/lionsoul/ip2region/service/Config.java | 2 +- .../java/src/main/java/org/lionsoul/ip2region/xdb/Searcher.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/binding/java/src/main/java/org/lionsoul/ip2region/service/Config.java b/binding/java/src/main/java/org/lionsoul/ip2region/service/Config.java index 1e4fbb3..c916980 100644 --- a/binding/java/src/main/java/org/lionsoul/ip2region/service/Config.java +++ b/binding/java/src/main/java/org/lionsoul/ip2region/service/Config.java @@ -66,7 +66,7 @@ public class Config { sb.append('{'); sb.append("cache_policy:").append(cachePolicy).append(','); sb.append("version:").append(ipVersion.toString()).append(','); - sb.append("xdb_path:").append(xdbFile.getAbsolutePath()).append(','); + sb.append("xdb_path:").append(xdbFile == null ? "null" : xdbFile.getAbsolutePath()).append(','); sb.append("header:").append(header.toString()).append(','); if (vIndex == null) { sb.append("v_index: null, "); diff --git a/binding/java/src/main/java/org/lionsoul/ip2region/xdb/Searcher.java b/binding/java/src/main/java/org/lionsoul/ip2region/xdb/Searcher.java index 8111c8e..98b8475 100644 --- a/binding/java/src/main/java/org/lionsoul/ip2region/xdb/Searcher.java +++ b/binding/java/src/main/java/org/lionsoul/ip2region/xdb/Searcher.java @@ -189,7 +189,7 @@ public class Searcher { return String.format( "%s->{version:%s, xdb:%s, vIndex:%s, cBuffer:%s}", super.toString(), - version.name, xdbFile.getAbsolutePath(), + version.name, xdbFile == null ? "null" : xdbFile.getAbsolutePath(), vectorIndex == null ? "null" : String.valueOf(vectorIndex.length), contentBuff == null ? "null" : String.valueOf(contentBuff.length()) ); From ea297041ddb9317a1273fe242200d17721c88764 Mon Sep 17 00:00:00 2001 From: lionsoul2014 Date: Fri, 12 Dec 2025 12:15:21 +0800 Subject: [PATCH 04/10] load from xdb File and xdb Path --- .../lionsoul/ip2region/service/Config.java | 3 +++ .../org/lionsoul/ip2region/xdb/Searcher.java | 24 ++++++++++++++----- .../ip2region/service/SearcherPoolTest.java | 24 +++++++++++++++++++ 3 files changed, 45 insertions(+), 6 deletions(-) diff --git a/binding/java/src/main/java/org/lionsoul/ip2region/service/Config.java b/binding/java/src/main/java/org/lionsoul/ip2region/service/Config.java index c916980..c6f1832 100644 --- a/binding/java/src/main/java/org/lionsoul/ip2region/service/Config.java +++ b/binding/java/src/main/java/org/lionsoul/ip2region/service/Config.java @@ -23,6 +23,9 @@ public class Config { public static final int VIndexCache = 1; public static final int BufferCache = 2; + // alias of BufferCache but easier to understand + public static final int FullCache = 2; + // search cache policy public final int cachePolicy; public final Version ipVersion; diff --git a/binding/java/src/main/java/org/lionsoul/ip2region/xdb/Searcher.java b/binding/java/src/main/java/org/lionsoul/ip2region/xdb/Searcher.java index 98b8475..a2c3ddf 100644 --- a/binding/java/src/main/java/org/lionsoul/ip2region/xdb/Searcher.java +++ b/binding/java/src/main/java/org/lionsoul/ip2region/xdb/Searcher.java @@ -204,13 +204,17 @@ public class Searcher { return new Header(buff); } - public static Header loadHeaderFromFile(String dbPath) throws IOException { - final RandomAccessFile handle = new RandomAccessFile(dbPath, "r"); + public static Header loadHeaderFromFile(File xdbFile) throws IOException { + final RandomAccessFile handle = new RandomAccessFile(xdbFile, "r"); final Header header = loadHeader(handle); handle.close(); return header; } + public static Header loadHeaderFromFile(String xdbPath) throws IOException { + return loadHeaderFromFile(new File(xdbPath)); + } + public static byte[] loadVectorIndex(RandomAccessFile handle) throws IOException { handle.seek(HeaderInfoLength); int len = VectorIndexRows * VectorIndexCols * VectorIndexSize; @@ -223,13 +227,17 @@ public class Searcher { return buff; } - public static byte[] loadVectorIndexFromFile(String dbPath) throws IOException { - final RandomAccessFile handle = new RandomAccessFile(dbPath, "r"); + public static byte[] loadVectorIndexFromFile(File xdbFile) throws IOException { + final RandomAccessFile handle = new RandomAccessFile(xdbFile, "r"); final byte[] vIndex = loadVectorIndex(handle); handle.close(); return vIndex; } + public static byte[] loadVectorIndexFromFile(String xdbPath) throws IOException { + return loadVectorIndexFromFile(new File(xdbPath)); + } + public static LongByteArray loadContent(RandomAccessFile handle) throws IOException { handle.seek(0); // check the length and do the buff load @@ -249,13 +257,17 @@ public class Searcher { return byteArray; } - public static LongByteArray loadContentFromFile(String dbPath) throws IOException { - final RandomAccessFile handle = new RandomAccessFile(dbPath, "r"); + public static LongByteArray loadContentFromFile(File xdbFile) throws IOException { + final RandomAccessFile handle = new RandomAccessFile(xdbFile, "r"); final LongByteArray content = loadContent(handle); handle.close(); return content; } + public static LongByteArray loadContentFromFile(String xdbPath) throws IOException { + return loadContentFromFile(new File(xdbPath)); + } + // --- verify util function // Verify if the current Searcher could be used to search the specified xdb file. diff --git a/binding/java/src/test/java/org/lionsoul/ip2region/service/SearcherPoolTest.java b/binding/java/src/test/java/org/lionsoul/ip2region/service/SearcherPoolTest.java index 5d07761..5c32f63 100644 --- a/binding/java/src/test/java/org/lionsoul/ip2region/service/SearcherPoolTest.java +++ b/binding/java/src/test/java/org/lionsoul/ip2region/service/SearcherPoolTest.java @@ -32,6 +32,30 @@ public class SearcherPoolTest { log.debugf("v4 searcher pool closed gracefully"); } + @Test + public void testInMemV4SearcherPool() throws Exception { + final Config v4Config = Config.custom() + .setCachePolicy(Config.FullCache) + .setSearchers(5) + .setXdbPath(ConfigTest.getDataPath("ip2region_v4.xdb")) + .asV4(); + + + final String ipStr = "58.250.36.41"; + final SearcherPool v4Pool = SearcherPool.create(v4Config); + for (int i = 0; i < 20; i++) { + final Searcher searcher = v4Pool.borrowSearcher(); + log.debugf("borrowed searcher %d: %s", i, searcher.toString()); + final String region = searcher.search(ipStr); + log.debugf("search(%s)=%s", ipStr, region); + v4Pool.returnSearcher(searcher); + log.debugf("return searcher %d", i); + } + + v4Pool.close(); + log.debugf("v4 searcher pool closed gracefully"); + } + @Test public void testV6SearcherPool() throws Exception { final Config v6Config = Config.custom() From ff75ddbcfd14f07700b9108cf6595f584628e862 Mon Sep 17 00:00:00 2001 From: lionsoul2014 Date: Fri, 12 Dec 2025 12:16:59 +0800 Subject: [PATCH 05/10] verify from xdb File/Path --- .../main/java/org/lionsoul/ip2region/xdb/Searcher.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/binding/java/src/main/java/org/lionsoul/ip2region/xdb/Searcher.java b/binding/java/src/main/java/org/lionsoul/ip2region/xdb/Searcher.java index a2c3ddf..f7ce54f 100644 --- a/binding/java/src/main/java/org/lionsoul/ip2region/xdb/Searcher.java +++ b/binding/java/src/main/java/org/lionsoul/ip2region/xdb/Searcher.java @@ -297,10 +297,14 @@ public class Searcher { } } - public static void verifyFromFile(String dbFile) throws IOException, XdbException { - final RandomAccessFile handle = new RandomAccessFile(dbFile, "r"); + public static void verifyFromFile(File xdbFile) throws IOException, XdbException { + final RandomAccessFile handle = new RandomAccessFile(xdbFile, "r"); verify(handle); handle.close(); } + public static void verifyFromFile(String xdbPath) throws IOException, XdbException { + verifyFromFile(new File(xdbPath)); + } + } \ No newline at end of file From e11a390afc4a0a57c32164ea39072b6026adaff1 Mon Sep 17 00:00:00 2001 From: lionsoul2014 Date: Fri, 12 Dec 2025 14:28:50 +0800 Subject: [PATCH 06/10] content/vectorIndex/verify from Buffer --- .../org/lionsoul/ip2region/xdb/Searcher.java | 65 +++++++++++++++++-- 1 file changed, 61 insertions(+), 4 deletions(-) diff --git a/binding/java/src/main/java/org/lionsoul/ip2region/xdb/Searcher.java b/binding/java/src/main/java/org/lionsoul/ip2region/xdb/Searcher.java index f7ce54f..6152cdd 100644 --- a/binding/java/src/main/java/org/lionsoul/ip2region/xdb/Searcher.java +++ b/binding/java/src/main/java/org/lionsoul/ip2region/xdb/Searcher.java @@ -11,6 +11,7 @@ import java.io.File; // @Date 2022/06/23 import java.io.IOException; +import java.io.InputStream; import java.io.RandomAccessFile; public class Searcher { @@ -195,7 +196,9 @@ public class Searcher { ); } + // --- // --- static util function + // --- read xdb header public static Header loadHeader(RandomAccessFile handle) throws IOException { handle.seek(0); @@ -215,6 +218,12 @@ public class Searcher { return loadHeaderFromFile(new File(xdbPath)); } + public static Header loadHeaderFromBuffer(LongByteArray cBuffer) throws IOException { + return new Header(cBuffer.slice(0, HeaderInfoLength)); + } + + // --- read xdb vector index + public static byte[] loadVectorIndex(RandomAccessFile handle) throws IOException { handle.seek(HeaderInfoLength); int len = VectorIndexRows * VectorIndexCols * VectorIndexSize; @@ -238,6 +247,13 @@ public class Searcher { return loadVectorIndexFromFile(new File(xdbPath)); } + public static byte[] loadVectorIndexFromBuffer(LongByteArray cBuffer) throws IOException { + final int len = VectorIndexRows * VectorIndexCols * VectorIndexSize; + return cBuffer.slice(HeaderInfoLength, len); + } + + // --- read xdb content + public static LongByteArray loadContent(RandomAccessFile handle) throws IOException { handle.seek(0); // check the length and do the buff load @@ -268,6 +284,45 @@ public class Searcher { return loadContentFromFile(new File(xdbPath)); } + public static LongByteArray loadContentFromInputStream(InputStream is) throws IOException { + final LongByteArray byteArray = new LongByteArray(); + while (true) { + boolean done = false; + + // read at most MAX_WRITE_BYTES bytes + int rLen, tBytes = 0; + final byte[] buff = new byte[MAX_WRITE_BYTES]; + while (true) { + rLen = is.read(buff, tBytes, buff.length - tBytes); + if (rLen == -1) { + // reach the end of the stream + done = true; + break; + } else if (rLen == 0) { + // the entire buff was filled + break; + } + + tBytes += rLen; + } + + // check and copy the buffer with its actual filled bytes + if (tBytes == buff.length) { + byteArray.append(buff); + } else { + final byte[] nBuff = new byte[tBytes]; + System.arraycopy(buff, 0, nBuff, 0, tBytes); + byteArray.append(nBuff); + } + + if (done) { + break; + } + } + + return byteArray; + } + // --- verify util function // Verify if the current Searcher could be used to search the specified xdb file. @@ -276,9 +331,7 @@ public class Searcher { // // @Note: You Just need to check this ONCE when the service starts // Or use another process (eg, A command) to check once Just to confirm the suitability. - public static void verify(RandomAccessFile handle) throws IOException, XdbException { - final Header header = loadHeader(handle); - + public static void verify(Header header, long fileBytes) throws IOException, XdbException { // get the runtime ptr bytes int runtimePtrBytes = 0; if (header.version == STRUCTURE_20) { @@ -292,11 +345,15 @@ public class Searcher { // 1, confirm the xdb file size // to ensure that the maximum file pointer does not overflow final long maxFilePtr = (1L << (runtimePtrBytes * 8)) - 1; - if (handle.length() > maxFilePtr) { + if (fileBytes > maxFilePtr) { throw new XdbException("xdb file exceeds the maximum supported bytes: "+maxFilePtr+""); } } + public static void verify(RandomAccessFile handle) throws IOException, XdbException { + verify(loadHeader(handle), handle.length()); + } + public static void verifyFromFile(File xdbFile) throws IOException, XdbException { final RandomAccessFile handle = new RandomAccessFile(xdbFile, "r"); verify(handle); From 6059d63504255a1e1a0edfc1c73e396287fe8442 Mon Sep 17 00:00:00 2001 From: lionsoul2014 Date: Fri, 12 Dec 2025 14:29:09 +0800 Subject: [PATCH 07/10] build from InputStream supports --- .../lionsoul/ip2region/service/ConfigBuilder.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/binding/java/src/main/java/org/lionsoul/ip2region/service/ConfigBuilder.java b/binding/java/src/main/java/org/lionsoul/ip2region/service/ConfigBuilder.java index 7ffbd53..06dd6d6 100644 --- a/binding/java/src/main/java/org/lionsoul/ip2region/service/ConfigBuilder.java +++ b/binding/java/src/main/java/org/lionsoul/ip2region/service/ConfigBuilder.java @@ -73,9 +73,17 @@ public class ConfigBuilder { // you must know what you are doing. throw new InvalidConfigException("SetXdbInputStream could ONLY be used with cachePolicy = Config.BufferCache"); } else { + // 1, load the content buffer + final LongByteArray cBuffer = Searcher.loadContentFromInputStream(xdbInputStream); - // return new Config(cachePolicy, ipVersion, null, header, null, cBuffer, searchers); - return null; + // 2, verify the xdb from the buffer + Searcher.verify(Searcher.loadHeaderFromBuffer(cBuffer), cBuffer.length()); + + // 3, load the header + final Header header = Searcher.loadHeaderFromBuffer(cBuffer); + + // create the config without xdbFile and vIndex + return new Config(cachePolicy, ipVersion, null, header, null, cBuffer, searchers); } // load the header and the cache buffer From 9b0bedc045811721d957e86d7d4a14c7fd6e914b Mon Sep 17 00:00:00 2001 From: lionsoul2014 Date: Fri, 12 Dec 2025 14:48:20 +0800 Subject: [PATCH 08/10] remove the space --- .../java/org/lionsoul/ip2region/xdb/Header.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/binding/java/src/main/java/org/lionsoul/ip2region/xdb/Header.java b/binding/java/src/main/java/org/lionsoul/ip2region/xdb/Header.java index 4b73150..2a1bbc4 100644 --- a/binding/java/src/main/java/org/lionsoul/ip2region/xdb/Header.java +++ b/binding/java/src/main/java/org/lionsoul/ip2region/xdb/Header.java @@ -33,13 +33,13 @@ public class Header { @Override public String toString() { return "{" + - "Version: " + version + ',' + - "IndexPolicy: " + indexPolicy + ',' + - "CreatedAt: " + createdAt + ',' + - "StartIndexPtr: " + startIndexPtr + ',' + - "EndIndexPtr: " + endIndexPtr + ',' + - "IPVersion: " + ipVersion + ',' + - "RuntimePtrBytes: " + runtimePtrBytes + + "Version:" + version + ',' + + "IndexPolicy:" + indexPolicy + ',' + + "CreatedAt:" + createdAt + ',' + + "StartIndexPtr:" + startIndexPtr + ',' + + "EndIndexPtr:" + endIndexPtr + ',' + + "IPVersion:" + ipVersion + ',' + + "RuntimePtrBytes:" + runtimePtrBytes + '}'; } } From 450fce589c7fd2d285ae0661c7a8712baeffb9c4 Mon Sep 17 00:00:00 2001 From: lionsoul2014 Date: Fri, 12 Dec 2025 14:49:05 +0800 Subject: [PATCH 09/10] Config build tests from XdbFile and XdbInputStream --- .../lionsoul/ip2region/service/Config.java | 2 +- .../ip2region/service/ConfigTest.java | 47 ++++++++++++++++++- 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/binding/java/src/main/java/org/lionsoul/ip2region/service/Config.java b/binding/java/src/main/java/org/lionsoul/ip2region/service/Config.java index c6f1832..0cb938c 100644 --- a/binding/java/src/main/java/org/lionsoul/ip2region/service/Config.java +++ b/binding/java/src/main/java/org/lionsoul/ip2region/service/Config.java @@ -23,7 +23,7 @@ public class Config { public static final int VIndexCache = 1; public static final int BufferCache = 2; - // alias of BufferCache but easier to understand + // alias of BufferCache but easier to understand or Remember public static final int FullCache = 2; // search cache policy diff --git a/binding/java/src/test/java/org/lionsoul/ip2region/service/ConfigTest.java b/binding/java/src/test/java/org/lionsoul/ip2region/service/ConfigTest.java index c716139..ef26581 100644 --- a/binding/java/src/test/java/org/lionsoul/ip2region/service/ConfigTest.java +++ b/binding/java/src/test/java/org/lionsoul/ip2region/service/ConfigTest.java @@ -1,4 +1,6 @@ package org.lionsoul.ip2region.service; +import java.io.File; +import java.io.FileInputStream; import java.io.IOException; import java.security.CodeSource; @@ -23,13 +25,35 @@ public class ConfigTest { @Test public void testBuildV4Config() throws IOException, XdbException, InvalidConfigException { final Config v4Config = Config.custom() - .setCachePolicy(Config.BufferCache) + .setCachePolicy(Config.VIndexCache) .setXdbPath(getDataPath("ip2region_v4.xdb")) .setSearchers(20) .asV4(); log.debugf("builded config: %s", v4Config); } + @Test + public void testBuildV4ConfigFromFile() throws IOException, XdbException, InvalidConfigException { + final Config v4Config = Config.custom() + .setCachePolicy(Config.BufferCache) + .setXdbFile(new File(getDataPath("ip2region_v4.xdb"))) + .setSearchers(20) + .asV4(); + log.debugf("builded config: %s", v4Config); + } + + @Test + public void testBuildV4ConfigFromInputStream() throws IOException, XdbException, InvalidConfigException { + final Config v4Config = Config.custom() + .setCachePolicy(Config.BufferCache) + .setXdbInputStream(new FileInputStream(getDataPath("ip2region_v4.xdb"))) + .setSearchers(20) + .asV4(); + log.debugf("builded config: %s", v4Config); + } + + // --- IPv6 + @Test public void testBuildV6Config() throws IOException, XdbException, InvalidConfigException { final Config v4Config = Config.custom() @@ -39,4 +63,25 @@ public class ConfigTest { .asV6(); log.debugf("builded config: %s", v4Config); } + + @Test + public void testBuildV6ConfigFromFile() throws IOException, XdbException, InvalidConfigException { + final Config v4Config = Config.custom() + .setCachePolicy(Config.BufferCache) + .setXdbFile(new File(getDataPath("ip2region_v6.xdb"))) + .setSearchers(20) + .asV6(); + log.debugf("builded config: %s", v4Config); + } + + @Test + public void testBuildV6ConfigFromInputStream() throws IOException, XdbException, InvalidConfigException { + final Config v4Config = Config.custom() + .setCachePolicy(Config.BufferCache) + .setXdbInputStream(new FileInputStream(getDataPath("ip2region_v6.xdb"))) + .setSearchers(20) + .asV6(); + log.debugf("builded config: %s", v4Config); + } + } From e84ea900d46bd4211db059cd643de7e5a1f90094 Mon Sep 17 00:00:00 2001 From: lionsoul2014 Date: Fri, 12 Dec 2025 14:49:26 +0800 Subject: [PATCH 10/10] update pom version to 3.3.1 --- binding/java/ReadMe.md | 8 +++++++- binding/java/pom.xml | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/binding/java/ReadMe.md b/binding/java/ReadMe.md index 363fbe0..7471578 100644 --- a/binding/java/ReadMe.md +++ b/binding/java/ReadMe.md @@ -7,7 +7,7 @@ org.lionsoul ip2region - 3.3.0 + 3.3.1 ``` @@ -21,6 +21,8 @@ import org.lionsoul.ip2region.service.Ip2Region; final Config v4Config = Config.custom() .setCachePolicy(Config.VIndexCache) // 指定缓存策略: NoCache / VIndexCache / BufferCache .setSearchers(15) // 设置初始化的查询器数量 + // .setXdbInputStream(InputStream) // 设置 v4 xdb 文件的 inputstream 对象 + // .setXdbFile(File) // 设置 v4 xdb File 对象 .setXdbPath("ip2region v4 xdb path") // 设置 v4 xdb 文件的路径 .asV4(); // 指定为 v4 配置 @@ -28,9 +30,13 @@ final Config v4Config = Config.custom() final Config v6Config = Config.custom() .setCachePolicy(Config.VIndexCache) // 指定缓存策略: NoCache / VIndexCache / BufferCache .setSearchers(15) // 设置初始化的查询器数量 + // .setXdbInputStream(InputStream) // 设置 v6 xdb 文件的 inputstream 对象 + // .setXdbFile(File) // 设置 v6 xdb File 对象 .setXdbPath("ip2region v6 xdb path") // 设置 v6 xdb 文件的路径 .asV6(); // 指定为 v6 配置 +// 备注:Xdb 三种初始化输入的优先级:XdbInputStream -> XdbFile -> XdbPath + // 3,通过上述配置创建 Ip2Region 查询服务 final Ip2Region ip2Region = Ip2Region.create(v4Config, v6Config); diff --git a/binding/java/pom.xml b/binding/java/pom.xml index ce1b62d..248795b 100644 --- a/binding/java/pom.xml +++ b/binding/java/pom.xml @@ -4,7 +4,7 @@ org.lionsoul ip2region - 3.3.0 + 3.3.1 jar ip2region