diff --git a/maker/java/src/main/java/org/lionsoul/ip2region/xdb/IPv4.java b/maker/java/src/main/java/org/lionsoul/ip2region/xdb/IPv4.java new file mode 100644 index 0000000..c05fae8 --- /dev/null +++ b/maker/java/src/main/java/org/lionsoul/ip2region/xdb/IPv4.java @@ -0,0 +1,26 @@ +// 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.xdb; + +// IPv4 version implementation +// @Author Lion +// @Date 2025/09/10 + +public class IPv4 extends Version { + public IPv4() { + // segmentIndex: 4 + 4 + 2 + 4 + super(4, "IPv4", 4, 14); + } + + @Override + public int putBytes(byte[] buff, int offset, byte[] ip) { + // use the Little endian byte order to compatible with the old searcher implementation + buff[offset++] = ip[3]; + buff[offset++] = ip[2]; + buff[offset++] = ip[1]; + buff[offset ] = ip[0]; + return ip.length; + } +} diff --git a/maker/java/src/main/java/org/lionsoul/ip2region/xdb/IPv6.java b/maker/java/src/main/java/org/lionsoul/ip2region/xdb/IPv6.java new file mode 100644 index 0000000..37e18ce --- /dev/null +++ b/maker/java/src/main/java/org/lionsoul/ip2region/xdb/IPv6.java @@ -0,0 +1,24 @@ +// 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.xdb; + +// IPv4 version implementation +// @Author Lion +// @Date 2025/09/10 + +public class IPv6 extends Version { + + public IPv6() { + // segmentIndex: 16 + 16 + 2 + 4 + super(6, "IPv6", 16, 38); + } + + @Override + public int putBytes(byte[] buff, int offset, byte[] ip) { + System.arraycopy(ip, 0, buff, offset, ip.length); + return ip.length; + } + +} diff --git a/maker/java/src/main/java/org/lionsoul/ip2region/xdb/LittleEndian.java b/maker/java/src/main/java/org/lionsoul/ip2region/xdb/LittleEndian.java new file mode 100644 index 0000000..2a8b7b0 --- /dev/null +++ b/maker/java/src/main/java/org/lionsoul/ip2region/xdb/LittleEndian.java @@ -0,0 +1,58 @@ +// 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.xdb; + +// Little Endian basic data type decode and encode. +// @Author Lion +// @Date 2025/09/10 + +public class LittleEndian { + + public final static int[] shiftIndex = {0, 8, 16, 24, 32, 40, 48, 56}; + + // put specified bytes to the buffer started from the offset + public static void put(final byte[] buff, int offset, long value, int bytes) { + if (bytes > 8) { + throw new IndexOutOfBoundsException("bytes should be <= 8"); + } + + for (int i = 0; i < bytes; i++) { + buff[offset++] = (byte)((value >>> shiftIndex[i]) & 0xFF); + } + } + + // put an uint32 (4 bytes long) to the buffer from the offset + public static void putUint32(final byte[] buff, int offset, long value) { + buff[offset++] = (byte) (value & 0xFF); + buff[offset++] = (byte) ((value >> 8) & 0xFF); + buff[offset++] = (byte) ((value >> 16) & 0xFF); + buff[offset ] = (byte) ((value >> 24) & 0xFF); + } + + // put a 2-bytes int to the buffer from the specified offset + public static void putInt2(final byte[] buff, int offset, int value) { + buff[offset++] = (byte) (value & 0xFF); + buff[offset ] = (byte) ((value >> 8) & 0xFF); + } + + // get an uint32 from a byte array from the specified offset + public static long getUint32(final byte[] buff, int offset) { + return ( + ((buff[offset++] & 0x000000FFL)) | + ((buff[offset++] << 8) & 0x0000FF00L) | + ((buff[offset++] << 16) & 0x00FF0000L) | + ((buff[offset ] << 24) & 0xFF000000L) + ); + } + + // get an 2 bytes int from a byte array from the specified offset + public static int getInt2(final byte[] buff, int offset) { + return ( + ((buff[offset++]) & 0x000000FF) | + ((buff[offset ] << 8) & 0x0000FF00) + ); + } + +} diff --git a/maker/java/src/main/java/org/lionsoul/ip2region/xdb/Util.java b/maker/java/src/main/java/org/lionsoul/ip2region/xdb/Util.java index 9905ddf..5ebbb22 100644 --- a/maker/java/src/main/java/org/lionsoul/ip2region/xdb/Util.java +++ b/maker/java/src/main/java/org/lionsoul/ip2region/xdb/Util.java @@ -9,6 +9,51 @@ package org.lionsoul.ip2region.xdb; public class Util { + + // compare two byte ip + // Returns: -1 if ip1 < ip2, 0 if ip1 == ip2, 1 if ip1 > ip2 + public static int ipCompare(byte[] ip1, byte[] ip2) { + for (int i = 0; i < ip1.length; i++) { + if (ip1[i] < ip2[i]) { + return -1; + } + + if (ip1[i] > ip2[i]) { + return 1; + } + } + + return 0; + } + + public static byte[] ipAddOne(byte[] ip) { + final byte[] r = new byte[ip.length]; + System.arraycopy(ip, 0, r, 0, ip.length); + for (int i = ip.length - 1; i >= 0; i--) { + r[i]++; + if (r[i] != 0) { // No overflow + break; + } + } + + return r; + } + + public static byte[] ipSubOne(byte[] ip) { + final byte[] r = new byte[ip.length]; + System.arraycopy(ip, 0, r, 0, ip.length); + for (int i = ip.length - 1; i >= 0; i--) { + if (r[i] != 0) { // No borrow needed + r[i]--; + break; + } + r[i] = (byte) 0xFF; // borrow from the next byte + } + + return r; + } + + // write specified bytes into a byte array start from offset public static void write( byte[] b, int offset, long v, int bytes) { for ( int i = 0; i < bytes; i++ ) { diff --git a/maker/java/src/main/java/org/lionsoul/ip2region/xdb/Version.java b/maker/java/src/main/java/org/lionsoul/ip2region/xdb/Version.java new file mode 100644 index 0000000..a1a1c71 --- /dev/null +++ b/maker/java/src/main/java/org/lionsoul/ip2region/xdb/Version.java @@ -0,0 +1,59 @@ +// Copyright 2025 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.xdb; + +// IP version abstract manager (IPv4 & IPv6) +// @Author Lion +// @Date 2025/09/10 + +public abstract class Version { + public static final int IPv4VersionNo = 4; + public static final int IPv6VersionNo = 6; + + public static final IPv4 IPv4 = new IPv4(); + public static final IPv6 IPv6 = new IPv6(); + + // version id and name + public final int id; + public final String name; + + // the numbers of bytes for one IP + public final int bytes; + + // segment index size (bytes) + public final int segmentIndexSize; + + public Version(int id, String name, int bytes, int segmentIndexSize) { + this.id = id; + this.name = name; + this.bytes = bytes; + this.segmentIndexSize = segmentIndexSize; + } + + // encode the specified IP bytes to the specified buffer + public abstract int putBytes(byte[] buff, int offset, byte[] ip); + + // compare the two IPs with the current version. + // Returns: -1 if ip1 < ip2, 0 if ip1 == ip2, 1 if ip1 > ip2 + public int ipCompare(byte[] ip1, byte[] ip2) { + return Util.ipCompare(ip1, ip2); + } + + // parse the version from an name + public static final Version fromName(String name) throws Exception { + final String n = name.toUpperCase(); + if (n.equals("V4") || n.equals("IPV4")) { + return IPv4; + } else if (n.equals("V6") || n.equals("IPV6")) { + return IPv6; + } else { + throw new Exception("invalid version name `"+name+"`"); + } + } + + @Override public String toString() { + return String.format("{Id:%d, Name:%s, Bytes:%d, IndexSize: %d}", id, name, bytes, segmentIndexSize); + } +} \ No newline at end of file diff --git a/maker/java/src/test/java/org/lionsoul/ip2region/xdb/LittleEndianTest.java b/maker/java/src/test/java/org/lionsoul/ip2region/xdb/LittleEndianTest.java new file mode 100644 index 0000000..e0196d3 --- /dev/null +++ b/maker/java/src/test/java/org/lionsoul/ip2region/xdb/LittleEndianTest.java @@ -0,0 +1,34 @@ +package org.lionsoul.ip2region.xdb; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +public class LittleEndianTest { + + @Test + public void testAll() { + final byte[] buff = new byte[14]; + + // encode + // do the put + LittleEndian.put(buff, 0, 1L, 4); + LittleEndian.put(buff, 4, 2L, 4); + + // putUint32 + LittleEndian.putInt2(buff, 8, 24); + LittleEndian.putUint32(buff, 10, 1024L); + + // decode + assertEquals(LittleEndian.getUint32(buff, 0), 1); + assertEquals(LittleEndian.getUint32(buff, 4), 2); + assertEquals(LittleEndian.getInt2(buff, 8), 24); + assertEquals(LittleEndian.getUint32(buff, 10), 1024); + + System.out.printf("uint32(buff, 0): %d\n", LittleEndian.getUint32(buff, 0)); + System.out.printf("uint32(buff, 4): %d\n", LittleEndian.getUint32(buff, 4)); + System.out.printf("int2(buff, 8): %d\n", LittleEndian.getInt2(buff, 8)); + System.out.printf("uint32(buff, 10): %d\n", LittleEndian.getUint32(buff, 10)); + } + +} diff --git a/maker/java/src/test/java/org/lionsoul/ip2region/xdb/VersionTest.java b/maker/java/src/test/java/org/lionsoul/ip2region/xdb/VersionTest.java new file mode 100644 index 0000000..c38e41b --- /dev/null +++ b/maker/java/src/test/java/org/lionsoul/ip2region/xdb/VersionTest.java @@ -0,0 +1,19 @@ +package org.lionsoul.ip2region.xdb; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +public class VersionTest { + @Test + public void testFromName() throws Exception { + final String[] vers = new String[]{"IPv4", "IPv6"}; + final Version v4 = Version.fromName(vers[0]); + assertEquals(v4.name, vers[0]); + final Version v6 = Version.fromName(vers[1]); + assertEquals(v6.name, vers[1]); + + System.out.printf("v4: %s\n", v4); + System.out.printf("v6: %s\n", v6); + } +}