From ef7ac3c088dee84d9eb7f64edfcad1f4a34dc78a Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Tue, 4 Nov 2025 14:58:04 +0800 Subject: [PATCH 01/37] =?UTF-8?q?refactor:=20=E6=9B=B4=E6=96=B0=E5=8D=95?= =?UTF-8?q?=E5=85=83=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../IP2Region.Net.Test.csproj | 71 ++++++++++--------- .../csharp/IP2Region.Net.Test/SearcherTest.cs | 60 ++++++++-------- binding/csharp/IP2Region.Net.Test/Usings.cs | 1 - binding/csharp/IP2Region.Net.Test/UtilTest.cs | 6 +- binding/csharp/IP2Region.Net.Test/XdbTest.cs | 44 ++++++++++++ 5 files changed, 118 insertions(+), 64 deletions(-) delete mode 100644 binding/csharp/IP2Region.Net.Test/Usings.cs create mode 100644 binding/csharp/IP2Region.Net.Test/XdbTest.cs diff --git a/binding/csharp/IP2Region.Net.Test/IP2Region.Net.Test.csproj b/binding/csharp/IP2Region.Net.Test/IP2Region.Net.Test.csproj index c2dd005..d9374f0 100644 --- a/binding/csharp/IP2Region.Net.Test/IP2Region.Net.Test.csproj +++ b/binding/csharp/IP2Region.Net.Test/IP2Region.Net.Test.csproj @@ -1,40 +1,47 @@  - - net9.0 - enable - enable + + net9.0 + enable + enable - false - + false + - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + - - - + + + - - - TestData/ipv4_source.txt - PreserveNewest - - - TestData/ip2region_v4.xdb - PreserveNewest - - + + + TestData/ipv4_source.txt + PreserveNewest + + + TestData/ip2region_v4.xdb + PreserveNewest + + + TestData/ipv6_source.txt + PreserveNewest + + + TestData/ip2region_v6.xdb + PreserveNewest + + diff --git a/binding/csharp/IP2Region.Net.Test/SearcherTest.cs b/binding/csharp/IP2Region.Net.Test/SearcherTest.cs index 21aa767..e673301 100644 --- a/binding/csharp/IP2Region.Net.Test/SearcherTest.cs +++ b/binding/csharp/IP2Region.Net.Test/SearcherTest.cs @@ -1,54 +1,58 @@ using IP2Region.Net.XDB; +using Xunit; namespace IP2Region.Net.Test; -[TestFixture] public class SearcherTest { private readonly string _xdbPath = Path.Combine(AppContext.BaseDirectory, "TestData", "ip2region_v4.xdb"); - - public static IEnumerable Ips() - { - yield return "114.114.114.114"; - yield return "119.29.29.29"; - yield return "223.5.5.5"; - yield return "180.76.76.76"; - yield return "8.8.8.8"; - } - [TestCaseSource(nameof(Ips))] - [Parallelizable(ParallelScope.All)] + [Theory] + [InlineData("114.114.114.114")] + [InlineData("119.29.29.29")] + [InlineData("223.5.5.5")] + [InlineData("180.76.76.76")] + [InlineData("8.8.8.8")] public void TestSearchCacheContent(string ip) { - var contentSearcher = new Searcher(CachePolicy.Content,_xdbPath); + var contentSearcher = new Searcher(CachePolicy.Content, _xdbPath); var region = contentSearcher.Search(ip); Console.WriteLine(region); } - [TestCaseSource(nameof(Ips))] - [Parallelizable(ParallelScope.All)] + [Theory] + [InlineData("114.114.114.114")] + [InlineData("119.29.29.29")] + [InlineData("223.5.5.5")] + [InlineData("180.76.76.76")] + [InlineData("8.8.8.8")] public void TestSearchCacheVector(string ip) { - var vectorSearcher = new Searcher(CachePolicy.VectorIndex,_xdbPath); + var vectorSearcher = new Searcher(CachePolicy.VectorIndex, _xdbPath); var region = vectorSearcher.Search(ip); Console.WriteLine(region); } - [TestCaseSource(nameof(Ips))] - [Parallelizable(ParallelScope.All)] + [Theory] + [InlineData("114.114.114.114")] + [InlineData("119.29.29.29")] + [InlineData("223.5.5.5")] + [InlineData("180.76.76.76")] + [InlineData("8.8.8.8")] public void TestSearchCacheFile(string ip) { - var fileSearcher = new Searcher(CachePolicy.File,_xdbPath); + var fileSearcher = new Searcher(CachePolicy.File, _xdbPath); var region = fileSearcher.Search(ip); Console.WriteLine(region); } - [TestCase(CachePolicy.Content)] - [TestCase(CachePolicy.VectorIndex)] - [TestCase(CachePolicy.File)] + [Theory] + [InlineData(CachePolicy.Content)] + [InlineData(CachePolicy.VectorIndex)] + [InlineData(CachePolicy.File)] public void TestBenchSearch(CachePolicy cachePolicy) { - Searcher searcher = new Searcher(cachePolicy,_xdbPath); + Searcher searcher = new Searcher(cachePolicy, _xdbPath); var srcPath = Path.Combine(AppContext.BaseDirectory, "TestData", "ipv4_source.txt"); foreach (var line in File.ReadLines(srcPath)) @@ -68,12 +72,12 @@ public class SearcherTest foreach (var ip in temp) { - var region = searcher.Search(ip); + //var region = searcher.Search(ip); - if (region != ps[2]) - { - throw new Exception($"failed search {ip} with ({region}!={ps[2]})"); - } + //if (region != ps[2]) + //{ + // throw new Exception($"failed search {ip} with ({region}!={ps[2]})"); + //} } } } diff --git a/binding/csharp/IP2Region.Net.Test/Usings.cs b/binding/csharp/IP2Region.Net.Test/Usings.cs deleted file mode 100644 index cefced4..0000000 --- a/binding/csharp/IP2Region.Net.Test/Usings.cs +++ /dev/null @@ -1 +0,0 @@ -global using NUnit.Framework; \ No newline at end of file diff --git a/binding/csharp/IP2Region.Net.Test/UtilTest.cs b/binding/csharp/IP2Region.Net.Test/UtilTest.cs index c25fe01..aad3a28 100644 --- a/binding/csharp/IP2Region.Net.Test/UtilTest.cs +++ b/binding/csharp/IP2Region.Net.Test/UtilTest.cs @@ -1,11 +1,11 @@ -using IP2Region.Net.XDB; +using Xunit; namespace IP2Region.Net.Test; -[TestFixture] public class UtilTest { - [TestCase("114.114.114.114")] + [Theory] + [InlineData("114.114.114.114")] public void TestIpAddressToUInt32(string value) { var uintIp = XDB.Util.IpAddressToUInt32(value); diff --git a/binding/csharp/IP2Region.Net.Test/XdbTest.cs b/binding/csharp/IP2Region.Net.Test/XdbTest.cs new file mode 100644 index 0000000..07d91bd --- /dev/null +++ b/binding/csharp/IP2Region.Net.Test/XdbTest.cs @@ -0,0 +1,44 @@ +using IP2Region.Net.XDB; +using System.Buffers; +using System.Buffers.Binary; +using System.Net; +using Xunit; + +namespace IP2Region.Net.Test; + +public class XdbTest +{ + [Theory] + [InlineData("v4")] + [InlineData("v6")] + public async Task Version_Ok(string version) + { + var db = Path.Combine(AppContext.BaseDirectory, "TestData", $"ip2region_{version}.xdb"); + using var reader = File.OpenRead(db); + + using var owner = MemoryPool.Shared.Rent(256); + var length = await reader.ReadAsync(owner.Memory[0..256]); + Assert.Equal(256, length); + + var ver = BinaryPrimitives.ReadUInt16LittleEndian(owner.Memory[..2].ToArray()); + var indexPolicy = BinaryPrimitives.ReadUInt16LittleEndian(owner.Memory.Slice(2, 2).ToArray()); + var createdAt = BinaryPrimitives.ReadUInt16LittleEndian(owner.Memory.Slice(4, 4).ToArray()); + var startIndexPtr = BinaryPrimitives.ReadUInt16LittleEndian(owner.Memory.Slice(8, 4).ToArray()); + var endIndexPtr = BinaryPrimitives.ReadUInt16LittleEndian(owner.Memory.Slice(12, 4).ToArray()); + + // since IPv6 supporting + var ipVersion = BinaryPrimitives.ReadUInt16LittleEndian(owner.Memory.Slice(16, 2).ToArray()); + var runtimePtrBytes = BinaryPrimitives.ReadUInt16LittleEndian(owner.Memory.Slice(18, 2).ToArray()); + } + + [Fact] + public void ParseIp() + { + //var ip = IPAddress.Parse("2409:895a:8b4:2de4:20e7:5a15:fe6f:431c"); + var ip = IPAddress.Parse("183.160.236.53"); + var ipNum = Util.IpAddressToUInt32(ip); + + var searcher = new Searcher(CachePolicy.Content, Path.Combine(AppContext.BaseDirectory, "TestData", "ip2region_v4.xdb")); + var result = searcher.Search(ip); + } +} From 8573f2f04e729a8de8e776abaae3470ad08ab9bc Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Tue, 4 Nov 2025 15:45:38 +0800 Subject: [PATCH 02/37] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=20Version=20?= =?UTF-8?q?=E9=80=82=E9=85=8D=20ipV6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Extensions/ServiceCollectionExtensions.cs | 8 ++++- binding/csharp/IP2Region.Net/XDB/Searcher.cs | 31 +++++++++---------- binding/csharp/IP2Region.Net/XDB/Version.cs | 20 ++++++++++++ 3 files changed, 41 insertions(+), 18 deletions(-) create mode 100644 binding/csharp/IP2Region.Net/XDB/Version.cs diff --git a/binding/csharp/IP2Region.Net/Extensions/ServiceCollectionExtensions.cs b/binding/csharp/IP2Region.Net/Extensions/ServiceCollectionExtensions.cs index 980243a..b12278d 100644 --- a/binding/csharp/IP2Region.Net/Extensions/ServiceCollectionExtensions.cs +++ b/binding/csharp/IP2Region.Net/Extensions/ServiceCollectionExtensions.cs @@ -1,4 +1,10 @@ -using IP2Region.Net.Abstractions; +// Copyright 2023 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. +// @Author Alan +// @Date 2023/07/25 + +using IP2Region.Net.Abstractions; using IP2Region.Net.XDB; using Microsoft.Extensions.DependencyInjection.Extensions; diff --git a/binding/csharp/IP2Region.Net/XDB/Searcher.cs b/binding/csharp/IP2Region.Net/XDB/Searcher.cs index de29dc9..d684387 100644 --- a/binding/csharp/IP2Region.Net/XDB/Searcher.cs +++ b/binding/csharp/IP2Region.Net/XDB/Searcher.cs @@ -4,19 +4,18 @@ // @Author Alan Lee // @Date 2023/07/23 -using System.Net; -using System.Runtime.InteropServices; -using System.Text; using IP2Region.Net.Abstractions; using IP2Region.Net.Internal; using IP2Region.Net.Internal.Abstractions; +using System.Buffers.Binary; +using System.Net; +using System.Runtime.InteropServices; +using System.Text; namespace IP2Region.Net.XDB; public class Searcher : ISearcher { - const int SegmentIndexSize = 14; - private readonly AbstractCacheStrategy _cacheStrategy; public int IoCount => _cacheStrategy.IoCount; @@ -40,36 +39,34 @@ public class Searcher : ISearcher public string? Search(uint ip) { + var version = new Version() { Length = 4, IndexSize = 14 }; var index = _cacheStrategy.GetVectorIndex(ip); uint sPtr = MemoryMarshal.Read(index.Span); uint ePtr = MemoryMarshal.Read(index.Span.Slice(4)); - var dataLen = 0; + uint dataLen = 0; uint dataPtr = 0; uint l = 0; - uint h = (ePtr -sPtr) / SegmentIndexSize; + uint h = (ePtr - sPtr) / (uint)version.IndexSize; while (l <= h) { var mid = Util.GetMidIp(l, h); - var pos = sPtr + mid * SegmentIndexSize; + var pos = sPtr + mid * version.IndexSize; + var buffer = _cacheStrategy.GetData((int)pos, version.IndexSize); - var buffer = _cacheStrategy.GetData((int)pos, SegmentIndexSize); - uint sip = MemoryMarshal.Read(buffer.Span); - uint eip = MemoryMarshal.Read(buffer.Span.Slice(4)); - - if (ip < sip) + if (ip < version.GetVectorIndexStartPos(buffer)) { h = mid - 1; } - else if (ip > eip) + else if (ip > version.GetVectorIndexEndPos(buffer)) { l = mid + 1; } else { - dataLen = MemoryMarshal.Read(buffer.Span.Slice(8)); - dataPtr = MemoryMarshal.Read(buffer.Span.Slice(10)); + dataLen = BinaryPrimitives.ReadUInt16LittleEndian(buffer.Span.Slice(8)); + dataPtr = BinaryPrimitives.ReadUInt32LittleEndian(buffer.Span.Slice(10)); break; } } @@ -79,7 +76,7 @@ public class Searcher : ISearcher return default; } - var regionBuff = _cacheStrategy.GetData((int)dataPtr,dataLen); + var regionBuff = _cacheStrategy.GetData((int)dataPtr, (int)dataLen); return Encoding.UTF8.GetString(regionBuff.Span.ToArray()); } } \ No newline at end of file diff --git a/binding/csharp/IP2Region.Net/XDB/Version.cs b/binding/csharp/IP2Region.Net/XDB/Version.cs new file mode 100644 index 0000000..8a4e014 --- /dev/null +++ b/binding/csharp/IP2Region.Net/XDB/Version.cs @@ -0,0 +1,20 @@ +using System.Buffers.Binary; + +namespace IP2Region.Net.XDB; + +public struct Version +{ + public int IndexSize { get; set; } + + public int Length { get; set; } + + public uint GetVectorIndexStartPos(ReadOnlyMemory buffer) + { + return BinaryPrimitives.ReadUInt32LittleEndian(buffer.Span); + } + + public uint GetVectorIndexEndPos(ReadOnlyMemory buffer) + { + return BinaryPrimitives.ReadUInt32LittleEndian(buffer.Slice(Length).Span); + } +} From 7fab19c46fa65af167074a09daa67792e50cdf92 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Wed, 19 Nov 2025 18:01:38 +0800 Subject: [PATCH 03/37] =?UTF-8?q?chore:=20=E6=9B=B4=E6=96=B0=E9=80=BB?= =?UTF-8?q?=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../csharp/IP2Region.Net.Test/SearcherTest.cs | 108 +++++++++++++++++- 1 file changed, 107 insertions(+), 1 deletion(-) diff --git a/binding/csharp/IP2Region.Net.Test/SearcherTest.cs b/binding/csharp/IP2Region.Net.Test/SearcherTest.cs index e673301..15a3364 100644 --- a/binding/csharp/IP2Region.Net.Test/SearcherTest.cs +++ b/binding/csharp/IP2Region.Net.Test/SearcherTest.cs @@ -1,11 +1,117 @@ using IP2Region.Net.XDB; +using System.Buffers; +using System.Buffers.Binary; +using System.Net; +using System.Text; using Xunit; namespace IP2Region.Net.Test; public class SearcherTest { - private readonly string _xdbPath = Path.Combine(AppContext.BaseDirectory, "TestData", "ip2region_v4.xdb"); + private readonly string _xdbPath = Path.Combine(AppContext.BaseDirectory, "TestData", "ip2region_v6.xdb"); + + [Fact] + public void Test() + { + // 常量定义 + // 每个 vector 索引项的字节数 + var VectorIndexSize = 8; + + // vector 索引的列数 + var VectorIndexCols = 256; + + // vector 索引段整个的字节数 + var VectorIndexLength = 512; + + //var ipAddress = IPAddress.Parse("240e:3b7:3272:d8d0:db09:c067:8d59:539e"); + var ipAddress = IPAddress.Parse("2c0f:fda8:21::"); + // [36,14,3,183,50,114,216,208,219,9,192,103,141,89,83,158] + //var ipAddress = IPAddress.Parse("58.251.27.201"); + byte[] ip_bytes = ipAddress.GetAddressBytes(); + //Array.Reverse(bytes); + + //var ip = MemoryMarshal.Read(bytes); + + var il0 = ip_bytes[0] & 0xFF; + var il1 = ip_bytes[1] & 0xFF; + + var idx = il0 * VectorIndexCols * VectorIndexSize + il1 * VectorIndexSize; + + var data = Read(256 + idx, VectorIndexSize); + var sPtr = BinaryPrimitives.ReadUInt32LittleEndian(data.Span); + var ePtr = BinaryPrimitives.ReadUInt32LittleEndian(data.Span.Slice(4)); + + var indexSize = 38; + var l = 0; + var h = (ePtr - sPtr) / indexSize; + var dataLen = 0; + var dataPtr = 0l; + + var bytes = ip_bytes.Length; + var dBytes = ip_bytes.Length << 1; + var buff = new byte[indexSize]; + + while (l < h) + { + // 得到中间的索引项 + int m = (int)(l + h) >> 1; + + var p = (int)sPtr + m * indexSize; + + buff = Read(p, buff.Length).ToArray(); + + var s1 = BinaryPrimitives.ReadUInt32BigEndian(ip_bytes); + var s = BinaryPrimitives.ReadUInt32BigEndian(buff.AsSpan().Slice(0, ip_bytes.Length).ToArray()); + var e = BinaryPrimitives.ReadUInt32BigEndian(buff.AsSpan().Slice(ip_bytes.Length).ToArray()); + if (s1 < s) + { + h = m - 1; + } + else if (s1 > e) + { + l = m + 1; + } + else + { + dataLen = BinaryPrimitives.ReadUInt16LittleEndian(buff.AsSpan().Slice(dBytes, 2)); + dataPtr = BinaryPrimitives.ReadUInt32LittleEndian(buff.AsSpan().Slice(dBytes + 2)); + break; + } + } + + var regionBuff = Read((int)dataPtr, (int)dataLen); + var address = Encoding.UTF8.GetString(regionBuff.Span.ToArray()); + } + + private ReadOnlyMemory Read(int offset, int length) + { + int BufferSize = 4096; + + var stream = new FileStream(_xdbPath, FileMode.Open, FileAccess.Read, FileShare.Read, BufferSize, useAsync: true); + + byte[] buffer = ArrayPool.Shared.Rent(length); + int totalBytesRead = 0; + try + { + stream.Seek(offset, SeekOrigin.Begin); + + int bytesRead; + do + { + int bytesToRead = Math.Min(BufferSize, length - totalBytesRead); + bytesRead = stream.Read(buffer, totalBytesRead, bytesToRead); + totalBytesRead += bytesRead; + + } while (bytesRead > 0 && totalBytesRead < length); + } + finally + { + ArrayPool.Shared.Return(buffer); + } + + return new ReadOnlyMemory(buffer, 0, totalBytesRead); + } [Theory] [InlineData("114.114.114.114")] From 4f39707bebc088580f43fe046392a2654655ebf5 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Thu, 20 Nov 2025 15:22:04 +0800 Subject: [PATCH 04/37] =?UTF-8?q?refactor:=20=E6=9B=B4=E6=94=B9=E4=B8=BA?= =?UTF-8?q?=20XdbVersion?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- binding/csharp/IP2Region.Net/XDB/Version.cs | 20 ---------- .../csharp/IP2Region.Net/XDB/XdbVersion.cs | 39 +++++++++++++++++++ 2 files changed, 39 insertions(+), 20 deletions(-) delete mode 100644 binding/csharp/IP2Region.Net/XDB/Version.cs create mode 100644 binding/csharp/IP2Region.Net/XDB/XdbVersion.cs diff --git a/binding/csharp/IP2Region.Net/XDB/Version.cs b/binding/csharp/IP2Region.Net/XDB/Version.cs deleted file mode 100644 index 8a4e014..0000000 --- a/binding/csharp/IP2Region.Net/XDB/Version.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System.Buffers.Binary; - -namespace IP2Region.Net.XDB; - -public struct Version -{ - public int IndexSize { get; set; } - - public int Length { get; set; } - - public uint GetVectorIndexStartPos(ReadOnlyMemory buffer) - { - return BinaryPrimitives.ReadUInt32LittleEndian(buffer.Span); - } - - public uint GetVectorIndexEndPos(ReadOnlyMemory buffer) - { - return BinaryPrimitives.ReadUInt32LittleEndian(buffer.Slice(Length).Span); - } -} diff --git a/binding/csharp/IP2Region.Net/XDB/XdbVersion.cs b/binding/csharp/IP2Region.Net/XDB/XdbVersion.cs new file mode 100644 index 0000000..2ac7398 --- /dev/null +++ b/binding/csharp/IP2Region.Net/XDB/XdbVersion.cs @@ -0,0 +1,39 @@ +namespace IP2Region.Net.XDB; + +public record struct XdbVersion +{ + /// + /// 获得/设置 版本号 + /// + public ushort Ver { get; set; } + + /// + /// 获得/设置 缓存策略 + /// + public ushort CachePolice { get; set; } + + /// + /// 获得/设置 文件生成时间 + /// + public DateTimeOffset CreatedTime { get; set; } + + /// + /// 获得/设置 索引起始地址 + /// + public uint StartIndex { get; set; } + + /// + /// 获得/设置 索引结束地址 + /// + public uint EndIndex { get; set; } + + /// + /// 获得/设置 IP版本 + /// + public ushort IPVer { get; set; } + + /// + /// 获得/设置 指针字节数 + /// + public ushort BytesCount { get; set; } +} From 499e09176148e7f88db38e56f1a1aed35bb5c3ef Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Thu, 20 Nov 2025 15:22:21 +0800 Subject: [PATCH 05/37] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=20IPv6=20?= =?UTF-8?q?=E7=AE=97=E6=B3=95=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- binding/csharp/IP2Region.Net/XDB/Searcher.cs | 116 ++++++++++++++----- 1 file changed, 84 insertions(+), 32 deletions(-) diff --git a/binding/csharp/IP2Region.Net/XDB/Searcher.cs b/binding/csharp/IP2Region.Net/XDB/Searcher.cs index d684387..bcf63d1 100644 --- a/binding/csharp/IP2Region.Net/XDB/Searcher.cs +++ b/binding/csharp/IP2Region.Net/XDB/Searcher.cs @@ -9,7 +9,6 @@ using IP2Region.Net.Internal; using IP2Region.Net.Internal.Abstractions; using System.Buffers.Binary; using System.Net; -using System.Runtime.InteropServices; using System.Text; namespace IP2Region.Net.XDB; @@ -17,6 +16,7 @@ namespace IP2Region.Net.XDB; public class Searcher : ISearcher { private readonly AbstractCacheStrategy _cacheStrategy; + public int IoCount => _cacheStrategy.IoCount; public Searcher(CachePolicy cachePolicy, string dbPath) @@ -27,56 +27,108 @@ public class Searcher : ISearcher public string? Search(string ipStr) { - var ip = Util.IpAddressToUInt32(ipStr); - return Search(ip); + var ipAddress = IPAddress.Parse(ipStr); + return Search(ipAddress); } public string? Search(IPAddress ipAddress) { - var ip = Util.IpAddressToUInt32(ipAddress); - return Search(ip); + return SearchCore(ipAddress.GetAddressBytes()); } - public string? Search(uint ip) + public string? Search(uint ipAddress) { - var version = new Version() { Length = 4, IndexSize = 14 }; - var index = _cacheStrategy.GetVectorIndex(ip); - uint sPtr = MemoryMarshal.Read(index.Span); - uint ePtr = MemoryMarshal.Read(index.Span.Slice(4)); + var bytes = BitConverter.GetBytes(ipAddress); + bytes.Reverse(); + return SearchCore(bytes); + } - uint dataLen = 0; - uint dataPtr = 0; - uint l = 0; - uint h = (ePtr - sPtr) / (uint)version.IndexSize; + string? SearchCore(byte[] ipBytes) + { + // 每个 vector 索引项的字节数 + var vectorIndexSize = 8; - while (l <= h) + // vector 索引的列数 + var vectorIndexCols = 256; + + // 计算得到 vector 索引项的开始地址。 + var il0 = ipBytes[0]; + var il1 = ipBytes[1]; + var idx = il0 * vectorIndexCols * vectorIndexSize + il1 * vectorIndexSize; + + var data = _cacheStrategy.GetData(256 + idx, vectorIndexSize); + var sPtr = BinaryPrimitives.ReadUInt32LittleEndian(data.Span); + var ePtr = BinaryPrimitives.ReadUInt32LittleEndian(data.Span[4..]); + + var length = ipBytes.Length; + var indexSize = length * 2 + 6; + var l = 0; + var h = (ePtr - sPtr) / indexSize; + var dataLen = 0; + long dataPtr = 0; + + while (l < h) { - var mid = Util.GetMidIp(l, h); - var pos = sPtr + mid * version.IndexSize; - var buffer = _cacheStrategy.GetData((int)pos, version.IndexSize); + int m = (int)(l + h) >> 1; - if (ip < version.GetVectorIndexStartPos(buffer)) + var p = (int)sPtr + m * indexSize; + var buff = _cacheStrategy.GetData(p, indexSize); + + var s = buff.Span[..length]; + var e = buff.Span.Slice(length, length); + if (ByteCompare(ipBytes, s) == -1) { - h = mid - 1; + h = m - 1; } - else if (ip > version.GetVectorIndexEndPos(buffer)) + else if (ByteCompare(ipBytes, e) == 1) { - l = mid + 1; + l = m + 1; } else { - dataLen = BinaryPrimitives.ReadUInt16LittleEndian(buffer.Span.Slice(8)); - dataPtr = BinaryPrimitives.ReadUInt32LittleEndian(buffer.Span.Slice(10)); + dataLen = BinaryPrimitives.ReadUInt16LittleEndian(buff.Span.Slice(length * 2, 2)); + dataPtr = BinaryPrimitives.ReadUInt32LittleEndian(buff.Span[(length * 2 + 2)..]); break; } } - if (dataLen == 0) - { - return default; - } - - var regionBuff = _cacheStrategy.GetData((int)dataPtr, (int)dataLen); - return Encoding.UTF8.GetString(regionBuff.Span.ToArray()); + var regionBuff = _cacheStrategy.GetData((int)dataPtr, dataLen); + return Encoding.UTF8.GetString(regionBuff.Span); } -} \ No newline at end of file + + static int ByteCompare(byte[] ip1, ReadOnlySpan ip2) => ip1.Length == 4 ? IPv4Compare(ip1, ip2) : IPv6Compare(ip1, ip2); + + static int IPv4Compare(byte[] ip1, ReadOnlySpan ip2) + { + var ret = 0; + for (int i = 0; i < ip1.Length; i++) + { + if (ip1[i] < ip2[ip1.Length - 1 - i]) + { + return -1; + } + else if (ip1[i] > ip2[ip1.Length - 1 - i]) + { + return 1; + } + } + return ret; + } + + static int IPv6Compare(byte[] ip1, ReadOnlySpan ip2) + { + var ret = 0; + for (int i = 0; i < ip1.Length; i++) + { + if (ip1[i] < ip2[i]) + { + return -1; + } + else if (ip1[i] > ip2[i]) + { + return 1; + } + } + return ret; + } +} From 31cd0784550295275b2f143aaf394e46944caddf Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Thu, 20 Nov 2025 15:26:12 +0800 Subject: [PATCH 06/37] =?UTF-8?q?refactor:=20=E5=85=BC=E5=AE=B9=20netstand?= =?UTF-8?q?er=20=E6=A1=86=E6=9E=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- binding/csharp/IP2Region.Net/XDB/Searcher.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/binding/csharp/IP2Region.Net/XDB/Searcher.cs b/binding/csharp/IP2Region.Net/XDB/Searcher.cs index bcf63d1..0f0858a 100644 --- a/binding/csharp/IP2Region.Net/XDB/Searcher.cs +++ b/binding/csharp/IP2Region.Net/XDB/Searcher.cs @@ -58,7 +58,7 @@ public class Searcher : ISearcher var data = _cacheStrategy.GetData(256 + idx, vectorIndexSize); var sPtr = BinaryPrimitives.ReadUInt32LittleEndian(data.Span); - var ePtr = BinaryPrimitives.ReadUInt32LittleEndian(data.Span[4..]); + var ePtr = BinaryPrimitives.ReadUInt32LittleEndian(data.Span.Slice(4)); var length = ipBytes.Length; var indexSize = length * 2 + 6; @@ -74,7 +74,7 @@ public class Searcher : ISearcher var p = (int)sPtr + m * indexSize; var buff = _cacheStrategy.GetData(p, indexSize); - var s = buff.Span[..length]; + var s = buff.Span.Slice(0, length); var e = buff.Span.Slice(length, length); if (ByteCompare(ipBytes, s) == -1) { @@ -87,13 +87,13 @@ public class Searcher : ISearcher else { dataLen = BinaryPrimitives.ReadUInt16LittleEndian(buff.Span.Slice(length * 2, 2)); - dataPtr = BinaryPrimitives.ReadUInt32LittleEndian(buff.Span[(length * 2 + 2)..]); + dataPtr = BinaryPrimitives.ReadUInt32LittleEndian(buff.Span.Slice(length * 2 + 2)); break; } } var regionBuff = _cacheStrategy.GetData((int)dataPtr, dataLen); - return Encoding.UTF8.GetString(regionBuff.Span); + return Encoding.UTF8.GetString(regionBuff.Span.ToArray()); } static int ByteCompare(byte[] ip1, ReadOnlySpan ip2) => ip1.Length == 4 ? IPv4Compare(ip1, ip2) : IPv6Compare(ip1, ip2); From d4d211c50bb9ed34e0a65566e972cb8d30508ad9 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Thu, 20 Nov 2025 15:26:30 +0800 Subject: [PATCH 07/37] =?UTF-8?q?test:=20=E6=9B=B4=E6=96=B0=E5=8D=95?= =?UTF-8?q?=E5=85=83=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../csharp/IP2Region.Net.Test/SearcherTest.cs | 242 +++++------------- binding/csharp/IP2Region.Net.Test/XdbTest.cs | 74 ++++-- 2 files changed, 124 insertions(+), 192 deletions(-) diff --git a/binding/csharp/IP2Region.Net.Test/SearcherTest.cs b/binding/csharp/IP2Region.Net.Test/SearcherTest.cs index 15a3364..bed1ccf 100644 --- a/binding/csharp/IP2Region.Net.Test/SearcherTest.cs +++ b/binding/csharp/IP2Region.Net.Test/SearcherTest.cs @@ -1,190 +1,90 @@ using IP2Region.Net.XDB; -using System.Buffers; -using System.Buffers.Binary; -using System.Net; -using System.Text; using Xunit; namespace IP2Region.Net.Test; public class SearcherTest { - private readonly string _xdbPath = Path.Combine(AppContext.BaseDirectory, "TestData", "ip2region_v6.xdb"); + private readonly string _xdbPathV4 = Path.Combine(AppContext.BaseDirectory, "TestData", "ip2region_v4.xdb"); + private readonly string _xdbPathV6 = Path.Combine(AppContext.BaseDirectory, "TestData", "ip2region_v6.xdb"); + + [Theory] + [InlineData("58.251.27.201", "中国|广东省|深圳市|联通", "v4")] + [InlineData("114.114.114.114", "中国|江苏省|南京市|0", "v4")] + [InlineData("119.29.29.29", "中国|北京|北京市|腾讯", "v4")] + [InlineData("223.5.5.5", "中国|浙江省|杭州市|阿里云", "v4")] + [InlineData("180.76.76.76", "中国|北京|北京市|百度", "v4")] + [InlineData("8.8.8.8", "", "v4")] + [InlineData("240e:3b7:3272:d8d0:db09:c067:8d59:539e", "中国|广东省|深圳市|家庭宽带", "v6")] + public void TestSearchCacheContent(string ip, string expected, string version) + { + var _xdbPath = version == "v4" ? _xdbPathV4 : _xdbPathV6; + var contentSearcher = new Searcher(CachePolicy.Content, _xdbPath); + var region = contentSearcher.Search(ip); + Assert.Equal(expected, region); + } + + [Theory] + [InlineData("58.251.27.201", "中国|广东省|深圳市|联通", "v4")] + [InlineData("114.114.114.114", "中国|江苏省|南京市|0", "v4")] + [InlineData("119.29.29.29", "中国|北京|北京市|腾讯", "v4")] + [InlineData("223.5.5.5", "中国|浙江省|杭州市|阿里云", "v4")] + [InlineData("180.76.76.76", "中国|北京|北京市|百度", "v4")] + [InlineData("8.8.8.8", "", "v4")] + [InlineData("240e:3b7:3272:d8d0:db09:c067:8d59:539e", "中国|广东省|深圳市|家庭宽带", "v6")] + public void TestSearchCacheVector(string ip, string expected, string version) + { + var _xdbPath = version == "v4" ? _xdbPathV4 : _xdbPathV6; + var vectorSearcher = new Searcher(CachePolicy.VectorIndex, _xdbPath); + var region = vectorSearcher.Search(ip); + Assert.Equal(expected, region); + } + + [Theory] + [InlineData("58.251.27.201", "中国|广东省|深圳市|联通", "v4")] + [InlineData("114.114.114.114", "中国|江苏省|南京市|0", "v4")] + [InlineData("119.29.29.29", "中国|北京|北京市|腾讯", "v4")] + [InlineData("223.5.5.5", "中国|浙江省|杭州市|阿里云", "v4")] + [InlineData("180.76.76.76", "中国|北京|北京市|百度", "v4")] + [InlineData("8.8.8.8", "", "v4")] + [InlineData("240e:3b7:3272:d8d0:db09:c067:8d59:539e", "中国|广东省|深圳市|家庭宽带", "v6")] + public void TestSearchCacheFile(string ip, string expected, string version) + { + var _xdbPath = version == "v4" ? _xdbPathV4 : _xdbPathV6; + var fileSearcher = new Searcher(CachePolicy.File, _xdbPath); + var region = fileSearcher.Search(ip); + Assert.Equal(expected, region); + } [Fact] public void Test() { - // 常量定义 - // 每个 vector 索引项的字节数 - var VectorIndexSize = 8; - - // vector 索引的列数 - var VectorIndexCols = 256; - - // vector 索引段整个的字节数 - var VectorIndexLength = 512; - - //var ipAddress = IPAddress.Parse("240e:3b7:3272:d8d0:db09:c067:8d59:539e"); - var ipAddress = IPAddress.Parse("2c0f:fda8:21::"); - // [36,14,3,183,50,114,216,208,219,9,192,103,141,89,83,158] - //var ipAddress = IPAddress.Parse("58.251.27.201"); - byte[] ip_bytes = ipAddress.GetAddressBytes(); - //Array.Reverse(bytes); - - //var ip = MemoryMarshal.Read(bytes); - - var il0 = ip_bytes[0] & 0xFF; - var il1 = ip_bytes[1] & 0xFF; - - var idx = il0 * VectorIndexCols * VectorIndexSize + il1 * VectorIndexSize; - - var data = Read(256 + idx, VectorIndexSize); - var sPtr = BinaryPrimitives.ReadUInt32LittleEndian(data.Span); - var ePtr = BinaryPrimitives.ReadUInt32LittleEndian(data.Span.Slice(4)); - - var indexSize = 38; - var l = 0; - var h = (ePtr - sPtr) / indexSize; - var dataLen = 0; - var dataPtr = 0l; - - var bytes = ip_bytes.Length; - var dBytes = ip_bytes.Length << 1; - var buff = new byte[indexSize]; - - while (l < h) - { - // 得到中间的索引项 - int m = (int)(l + h) >> 1; - - var p = (int)sPtr + m * indexSize; - - buff = Read(p, buff.Length).ToArray(); - - var s1 = BinaryPrimitives.ReadUInt32BigEndian(ip_bytes); - var s = BinaryPrimitives.ReadUInt32BigEndian(buff.AsSpan().Slice(0, ip_bytes.Length).ToArray()); - var e = BinaryPrimitives.ReadUInt32BigEndian(buff.AsSpan().Slice(ip_bytes.Length).ToArray()); - if (s1 < s) - { - h = m - 1; - } - else if (s1 > e) - { - l = m + 1; - } - else - { - dataLen = BinaryPrimitives.ReadUInt16LittleEndian(buff.AsSpan().Slice(dBytes, 2)); - dataPtr = BinaryPrimitives.ReadUInt32LittleEndian(buff.AsSpan().Slice(dBytes + 2)); - break; - } - } - - var regionBuff = Read((int)dataPtr, (int)dataLen); - var address = Encoding.UTF8.GetString(regionBuff.Span.ToArray()); - } - - private ReadOnlyMemory Read(int offset, int length) - { - int BufferSize = 4096; - - var stream = new FileStream(_xdbPath, FileMode.Open, FileAccess.Read, FileShare.Read, BufferSize, useAsync: true); - - byte[] buffer = ArrayPool.Shared.Rent(length); - int totalBytesRead = 0; - try - { - stream.Seek(offset, SeekOrigin.Begin); - - int bytesRead; - do - { - int bytesToRead = Math.Min(BufferSize, length - totalBytesRead); - bytesRead = stream.Read(buffer, totalBytesRead, bytesToRead); - totalBytesRead += bytesRead; - - } while (bytesRead > 0 && totalBytesRead < length); - } - finally - { - ArrayPool.Shared.Return(buffer); - } - - return new ReadOnlyMemory(buffer, 0, totalBytesRead); + var ip = XDB.Util.IpAddressToUInt32("58.251.27.201"); + var searcher = new Searcher(CachePolicy.File, _xdbPathV4); + var region = searcher.Search(ip); + Assert.Equal("中国|广东省|深圳市|联通", region); } [Theory] - [InlineData("114.114.114.114")] - [InlineData("119.29.29.29")] - [InlineData("223.5.5.5")] - [InlineData("180.76.76.76")] - [InlineData("8.8.8.8")] - public void TestSearchCacheContent(string ip) + [InlineData(CachePolicy.Content, "v4")] + [InlineData(CachePolicy.VectorIndex, "v4")] + [InlineData(CachePolicy.File, "v4")] + public void TestBenchSearch(CachePolicy cachePolicy, string version) { - var contentSearcher = new Searcher(CachePolicy.Content, _xdbPath); - var region = contentSearcher.Search(ip); - Console.WriteLine(region); - } + var _xdbPath = version == "v4" ? _xdbPathV4 : _xdbPathV6; + var searcher = new Searcher(cachePolicy, _xdbPath); + var srcPath = Path.Combine(AppContext.BaseDirectory, "TestData", $"ip{version}_source.txt"); - [Theory] - [InlineData("114.114.114.114")] - [InlineData("119.29.29.29")] - [InlineData("223.5.5.5")] - [InlineData("180.76.76.76")] - [InlineData("8.8.8.8")] - public void TestSearchCacheVector(string ip) - { - var vectorSearcher = new Searcher(CachePolicy.VectorIndex, _xdbPath); - var region = vectorSearcher.Search(ip); - Console.WriteLine(region); - } + //foreach (var line in File.ReadLines(srcPath)) + //{ + // var ps = line.Trim().Split("|", 3); + // var sip = ps[0]; + // var eip = ps[1]; - [Theory] - [InlineData("114.114.114.114")] - [InlineData("119.29.29.29")] - [InlineData("223.5.5.5")] - [InlineData("180.76.76.76")] - [InlineData("8.8.8.8")] - public void TestSearchCacheFile(string ip) - { - var fileSearcher = new Searcher(CachePolicy.File, _xdbPath); - var region = fileSearcher.Search(ip); - Console.WriteLine(region); - } - - [Theory] - [InlineData(CachePolicy.Content)] - [InlineData(CachePolicy.VectorIndex)] - [InlineData(CachePolicy.File)] - public void TestBenchSearch(CachePolicy cachePolicy) - { - Searcher searcher = new Searcher(cachePolicy, _xdbPath); - var srcPath = Path.Combine(AppContext.BaseDirectory, "TestData", "ipv4_source.txt"); - - foreach (var line in File.ReadLines(srcPath)) - { - var ps = line.Trim().Split("|", 3); - - if (ps.Length != 3) - { - throw new ArgumentException($"invalid ip segment line {line}", nameof(line)); - } - - var sip = Util.IpAddressToUInt32(ps[0]); - var eip = Util.IpAddressToUInt32(ps[1]); - var mip = Util.GetMidIp(sip, eip); - - uint[] temp = { sip, Util.GetMidIp(sip, mip), mip, Util.GetMidIp(mip, eip), eip }; - - foreach (var ip in temp) - { - //var region = searcher.Search(ip); - - //if (region != ps[2]) - //{ - // throw new Exception($"failed search {ip} with ({region}!={ps[2]})"); - //} - } - } + // var s1 = searcher.Search(sip); + // var s2 = searcher.Search(eip); + // //Assert.Equal(s1, ps[2]); + // //Assert.Equal(s2, ps[2]); + //} } } \ No newline at end of file diff --git a/binding/csharp/IP2Region.Net.Test/XdbTest.cs b/binding/csharp/IP2Region.Net.Test/XdbTest.cs index 07d91bd..b61c911 100644 --- a/binding/csharp/IP2Region.Net.Test/XdbTest.cs +++ b/binding/csharp/IP2Region.Net.Test/XdbTest.cs @@ -1,44 +1,76 @@ -using IP2Region.Net.XDB; -using System.Buffers; +using System.Buffers; using System.Buffers.Binary; -using System.Net; using Xunit; namespace IP2Region.Net.Test; public class XdbTest { - [Theory] - [InlineData("v4")] - [InlineData("v6")] - public async Task Version_Ok(string version) + [Fact] + public async Task VersionIPV4_Ok() { - var db = Path.Combine(AppContext.BaseDirectory, "TestData", $"ip2region_{version}.xdb"); + var db = Path.Combine(AppContext.BaseDirectory, "TestData", $"ip2region_v4.xdb"); using var reader = File.OpenRead(db); using var owner = MemoryPool.Shared.Rent(256); var length = await reader.ReadAsync(owner.Memory[0..256]); Assert.Equal(256, length); - var ver = BinaryPrimitives.ReadUInt16LittleEndian(owner.Memory[..2].ToArray()); - var indexPolicy = BinaryPrimitives.ReadUInt16LittleEndian(owner.Memory.Slice(2, 2).ToArray()); - var createdAt = BinaryPrimitives.ReadUInt16LittleEndian(owner.Memory.Slice(4, 4).ToArray()); - var startIndexPtr = BinaryPrimitives.ReadUInt16LittleEndian(owner.Memory.Slice(8, 4).ToArray()); - var endIndexPtr = BinaryPrimitives.ReadUInt16LittleEndian(owner.Memory.Slice(12, 4).ToArray()); + var ver = BinaryPrimitives.ReadUInt16LittleEndian(owner.Memory.Span[..2]); + Assert.Equal(3, ver); + + var indexPolicy = BinaryPrimitives.ReadUInt16LittleEndian(owner.Memory.Span.Slice(2, 2)); + Assert.Equal(1, indexPolicy); + + var createdAt = BinaryPrimitives.ReadUInt32LittleEndian(owner.Memory.Span.Slice(4, 4)); + var dtm = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); + dtm = dtm.AddSeconds(createdAt); + Assert.Equal("2025-09-06 02:24:16", dtm.ToString("yyyy-MM-dd HH:mm:ss")); + + var startIndexPtr = BinaryPrimitives.ReadUInt32LittleEndian(owner.Memory.Span.Slice(8, 4)); + Assert.Equal((uint)955933, startIndexPtr); + + var endIndexPtr = BinaryPrimitives.ReadUInt32LittleEndian(owner.Memory.Span.Slice(12, 4)); + Assert.Equal((uint)11042415, endIndexPtr); // since IPv6 supporting - var ipVersion = BinaryPrimitives.ReadUInt16LittleEndian(owner.Memory.Slice(16, 2).ToArray()); - var runtimePtrBytes = BinaryPrimitives.ReadUInt16LittleEndian(owner.Memory.Slice(18, 2).ToArray()); + var ipVersion = BinaryPrimitives.ReadUInt16LittleEndian(owner.Memory.Span.Slice(16, 2)); + Assert.Equal(4, ipVersion); + + var runtimePtrBytes = BinaryPrimitives.ReadUInt16LittleEndian(owner.Memory.Span.Slice(18, 2)); + Assert.Equal(4, runtimePtrBytes); } [Fact] - public void ParseIp() + public async Task VersionIPV6_Ok() { - //var ip = IPAddress.Parse("2409:895a:8b4:2de4:20e7:5a15:fe6f:431c"); - var ip = IPAddress.Parse("183.160.236.53"); - var ipNum = Util.IpAddressToUInt32(ip); + var db = Path.Combine(AppContext.BaseDirectory, "TestData", $"ip2region_v6.xdb"); + using var reader = File.OpenRead(db); - var searcher = new Searcher(CachePolicy.Content, Path.Combine(AppContext.BaseDirectory, "TestData", "ip2region_v4.xdb")); - var result = searcher.Search(ip); + using var owner = MemoryPool.Shared.Rent(256); + var length = await reader.ReadAsync(owner.Memory[0..256]); + Assert.Equal(256, length); + + var ver = BinaryPrimitives.ReadUInt16LittleEndian(owner.Memory.Span[..2]); + Assert.Equal(3, ver); + + var indexPolicy = BinaryPrimitives.ReadUInt16LittleEndian(owner.Memory.Span.Slice(2, 2)); + var createdAt = BinaryPrimitives.ReadUInt32LittleEndian(owner.Memory.Span.Slice(4, 4)); + var dtm = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); + dtm = dtm.AddSeconds(createdAt); + Assert.Equal("2025-10-17 04:41:04", dtm.ToString("yyyy-MM-dd HH:mm:ss")); + + var startIndexPtr = BinaryPrimitives.ReadUInt32LittleEndian(owner.Memory.Span.Slice(8, 4)); + Assert.Equal((uint)3094259, startIndexPtr); + + var endIndexPtr = BinaryPrimitives.ReadUInt32LittleEndian(owner.Memory.Span.Slice(12, 4)); + Assert.Equal((uint)36258303, endIndexPtr); + + // since IPv6 supporting + var ipVersion = BinaryPrimitives.ReadUInt16LittleEndian(owner.Memory.Span.Slice(16, 2)); + Assert.Equal(6, ipVersion); + + var runtimePtrBytes = BinaryPrimitives.ReadUInt16LittleEndian(owner.Memory.Span.Slice(18, 2)); + Assert.Equal(4, runtimePtrBytes); } } From af82b7e18e69994ac833a7e438563aadf36c00de Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Thu, 20 Nov 2025 15:45:39 +0800 Subject: [PATCH 08/37] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=20XdbVersion?= =?UTF-8?q?=20=E7=BB=93=E6=9E=84=E4=BD=93=E4=B8=8E=E8=AF=BB=E5=8F=96?= =?UTF-8?q?=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- binding/csharp/IP2Region.Net.Test/XdbTest.cs | 75 +++++-------------- binding/csharp/IP2Region.Net/XDB/Util.cs | 55 +++++++++++++- .../csharp/IP2Region.Net/XDB/XdbVersion.cs | 2 +- 3 files changed, 72 insertions(+), 60 deletions(-) diff --git a/binding/csharp/IP2Region.Net.Test/XdbTest.cs b/binding/csharp/IP2Region.Net.Test/XdbTest.cs index b61c911..ff376a1 100644 --- a/binding/csharp/IP2Region.Net.Test/XdbTest.cs +++ b/binding/csharp/IP2Region.Net.Test/XdbTest.cs @@ -1,6 +1,4 @@ -using System.Buffers; -using System.Buffers.Binary; -using Xunit; +using Xunit; namespace IP2Region.Net.Test; @@ -10,67 +8,28 @@ public class XdbTest public async Task VersionIPV4_Ok() { var db = Path.Combine(AppContext.BaseDirectory, "TestData", $"ip2region_v4.xdb"); - using var reader = File.OpenRead(db); - using var owner = MemoryPool.Shared.Rent(256); - var length = await reader.ReadAsync(owner.Memory[0..256]); - Assert.Equal(256, length); - - var ver = BinaryPrimitives.ReadUInt16LittleEndian(owner.Memory.Span[..2]); - Assert.Equal(3, ver); - - var indexPolicy = BinaryPrimitives.ReadUInt16LittleEndian(owner.Memory.Span.Slice(2, 2)); - Assert.Equal(1, indexPolicy); - - var createdAt = BinaryPrimitives.ReadUInt32LittleEndian(owner.Memory.Span.Slice(4, 4)); - var dtm = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); - dtm = dtm.AddSeconds(createdAt); - Assert.Equal("2025-09-06 02:24:16", dtm.ToString("yyyy-MM-dd HH:mm:ss")); - - var startIndexPtr = BinaryPrimitives.ReadUInt32LittleEndian(owner.Memory.Span.Slice(8, 4)); - Assert.Equal((uint)955933, startIndexPtr); - - var endIndexPtr = BinaryPrimitives.ReadUInt32LittleEndian(owner.Memory.Span.Slice(12, 4)); - Assert.Equal((uint)11042415, endIndexPtr); - - // since IPv6 supporting - var ipVersion = BinaryPrimitives.ReadUInt16LittleEndian(owner.Memory.Span.Slice(16, 2)); - Assert.Equal(4, ipVersion); - - var runtimePtrBytes = BinaryPrimitives.ReadUInt16LittleEndian(owner.Memory.Span.Slice(18, 2)); - Assert.Equal(4, runtimePtrBytes); + var version = await XDB.Util.GetVersionAsync(db); + Assert.Equal(3, version.Ver); + Assert.Equal(1, version.CachePolice); + Assert.Equal("2025-09-06 02:24:16", version.CreatedTime.ToString("yyyy-MM-dd HH:mm:ss")); + Assert.Equal((uint)955933, version.StartIndex); + Assert.Equal((uint)11042415, version.EndIndex); + Assert.Equal(4, version.IPVer); + Assert.Equal(4, version.BytesCount); } [Fact] public async Task VersionIPV6_Ok() { var db = Path.Combine(AppContext.BaseDirectory, "TestData", $"ip2region_v6.xdb"); - using var reader = File.OpenRead(db); - - using var owner = MemoryPool.Shared.Rent(256); - var length = await reader.ReadAsync(owner.Memory[0..256]); - Assert.Equal(256, length); - - var ver = BinaryPrimitives.ReadUInt16LittleEndian(owner.Memory.Span[..2]); - Assert.Equal(3, ver); - - var indexPolicy = BinaryPrimitives.ReadUInt16LittleEndian(owner.Memory.Span.Slice(2, 2)); - var createdAt = BinaryPrimitives.ReadUInt32LittleEndian(owner.Memory.Span.Slice(4, 4)); - var dtm = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); - dtm = dtm.AddSeconds(createdAt); - Assert.Equal("2025-10-17 04:41:04", dtm.ToString("yyyy-MM-dd HH:mm:ss")); - - var startIndexPtr = BinaryPrimitives.ReadUInt32LittleEndian(owner.Memory.Span.Slice(8, 4)); - Assert.Equal((uint)3094259, startIndexPtr); - - var endIndexPtr = BinaryPrimitives.ReadUInt32LittleEndian(owner.Memory.Span.Slice(12, 4)); - Assert.Equal((uint)36258303, endIndexPtr); - - // since IPv6 supporting - var ipVersion = BinaryPrimitives.ReadUInt16LittleEndian(owner.Memory.Span.Slice(16, 2)); - Assert.Equal(6, ipVersion); - - var runtimePtrBytes = BinaryPrimitives.ReadUInt16LittleEndian(owner.Memory.Span.Slice(18, 2)); - Assert.Equal(4, runtimePtrBytes); + var version = await XDB.Util.GetVersionAsync(db); + Assert.Equal(3, version.Ver); + Assert.Equal(1, version.CachePolice); + Assert.Equal("2025-10-17 04:41:04", version.CreatedTime.ToString("yyyy-MM-dd HH:mm:ss")); + Assert.Equal((uint)3094259, version.StartIndex); + Assert.Equal((uint)36258303, version.EndIndex); + Assert.Equal(6, version.IPVer); + Assert.Equal(4, version.BytesCount); } } diff --git a/binding/csharp/IP2Region.Net/XDB/Util.cs b/binding/csharp/IP2Region.Net/XDB/Util.cs index 1075377..d92a613 100644 --- a/binding/csharp/IP2Region.Net/XDB/Util.cs +++ b/binding/csharp/IP2Region.Net/XDB/Util.cs @@ -1,3 +1,5 @@ +using System.Buffers; +using System.Buffers.Binary; using System.Net; using System.Runtime.InteropServices; @@ -10,7 +12,7 @@ public static class Util var address = IPAddress.Parse(ipAddress); return IpAddressToUInt32(address); } - + public static uint IpAddressToUInt32(IPAddress ipAddress) { byte[] bytes = ipAddress.GetAddressBytes(); @@ -20,4 +22,55 @@ public static class Util public static uint GetMidIp(uint x, uint y) => (x & y) + ((x ^ y) >> 1); + + public static async Task GetVersionAsync(string dbPath, CancellationToken token = default) + { + if (string.IsNullOrEmpty(dbPath)) + { + throw new ArgumentNullException(nameof(dbPath)); + } + + if (!File.Exists(dbPath)) + { + throw new FileNotFoundException("xdb file not fould.", dbPath); + } + + XdbVersion ret = default; + using var reader = File.OpenRead(dbPath); + var buffer = ArrayPool.Shared.Rent(256); + + try + { + var length = await reader.ReadAsync(buffer, 0, 256, token); + if (length == 256) + { + ret = Parse(buffer); + } + } + finally + { + ArrayPool.Shared.Return(buffer); + } + + return ret; + } + + private static XdbVersion Parse(ReadOnlySpan buffer) + { + var ret = new XdbVersion + { + Ver = BinaryPrimitives.ReadUInt16LittleEndian(buffer.Slice(0, 2)), + CachePolice = BinaryPrimitives.ReadUInt16LittleEndian(buffer.Slice(2, 2)), + StartIndex = BinaryPrimitives.ReadUInt32LittleEndian(buffer.Slice(8, 4)), + EndIndex = BinaryPrimitives.ReadUInt32LittleEndian(buffer.Slice(12, 4)), + IPVer = BinaryPrimitives.ReadUInt16LittleEndian(buffer.Slice(16, 2)), + BytesCount = BinaryPrimitives.ReadUInt16LittleEndian(buffer.Slice(18, 2)) + }; + + var createdAt = BinaryPrimitives.ReadUInt32LittleEndian(buffer.Slice(4, 4)); + var dtm = new DateTimeOffset(1970, 1, 1, 0, 0, 0, TimeSpan.FromHours(8)); + ret.CreatedTime = dtm.AddSeconds(createdAt); + + return ret; + } } \ No newline at end of file diff --git a/binding/csharp/IP2Region.Net/XDB/XdbVersion.cs b/binding/csharp/IP2Region.Net/XDB/XdbVersion.cs index 2ac7398..1fdb743 100644 --- a/binding/csharp/IP2Region.Net/XDB/XdbVersion.cs +++ b/binding/csharp/IP2Region.Net/XDB/XdbVersion.cs @@ -1,6 +1,6 @@ namespace IP2Region.Net.XDB; -public record struct XdbVersion +public struct XdbVersion { /// /// 获得/设置 版本号 From a2778f9b9808953c0813e9eb446caa3b4bb4f880 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Thu, 20 Nov 2025 16:25:25 +0800 Subject: [PATCH 09/37] =?UTF-8?q?refactor:=20=E5=A2=9E=E5=8A=A0=E8=BE=B9?= =?UTF-8?q?=E7=95=8C=E6=95=B0=E6=8D=AE=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- binding/csharp/IP2Region.Net/XDB/Searcher.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/binding/csharp/IP2Region.Net/XDB/Searcher.cs b/binding/csharp/IP2Region.Net/XDB/Searcher.cs index 0f0858a..60690c8 100644 --- a/binding/csharp/IP2Region.Net/XDB/Searcher.cs +++ b/binding/csharp/IP2Region.Net/XDB/Searcher.cs @@ -67,7 +67,7 @@ public class Searcher : ISearcher var dataLen = 0; long dataPtr = 0; - while (l < h) + while (l <= h) { int m = (int)(l + h) >> 1; @@ -76,18 +76,18 @@ public class Searcher : ISearcher var s = buff.Span.Slice(0, length); var e = buff.Span.Slice(length, length); - if (ByteCompare(ipBytes, s) == -1) + if (ByteCompare(ipBytes, s) < 0) { h = m - 1; } - else if (ByteCompare(ipBytes, e) == 1) + else if (ByteCompare(ipBytes, e) > 0) { l = m + 1; } else { dataLen = BinaryPrimitives.ReadUInt16LittleEndian(buff.Span.Slice(length * 2, 2)); - dataPtr = BinaryPrimitives.ReadUInt32LittleEndian(buff.Span.Slice(length * 2 + 2)); + dataPtr = BinaryPrimitives.ReadUInt32LittleEndian(buff.Span.Slice(length * 2 + 2, 4)); break; } } From 87666aa353d85c3921a50bdcdfd2da395e493a30 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Thu, 20 Nov 2025 16:25:38 +0800 Subject: [PATCH 10/37] =?UTF-8?q?test:=20=E6=9B=B4=E6=96=B0=E5=8D=95?= =?UTF-8?q?=E5=85=83=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../csharp/IP2Region.Net.Test/SearcherTest.cs | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/binding/csharp/IP2Region.Net.Test/SearcherTest.cs b/binding/csharp/IP2Region.Net.Test/SearcherTest.cs index bed1ccf..ed8078b 100644 --- a/binding/csharp/IP2Region.Net.Test/SearcherTest.cs +++ b/binding/csharp/IP2Region.Net.Test/SearcherTest.cs @@ -69,22 +69,25 @@ public class SearcherTest [InlineData(CachePolicy.Content, "v4")] [InlineData(CachePolicy.VectorIndex, "v4")] [InlineData(CachePolicy.File, "v4")] + [InlineData(CachePolicy.Content, "v6")] + [InlineData(CachePolicy.VectorIndex, "v6")] + [InlineData(CachePolicy.File, "v6")] public void TestBenchSearch(CachePolicy cachePolicy, string version) { var _xdbPath = version == "v4" ? _xdbPathV4 : _xdbPathV6; var searcher = new Searcher(cachePolicy, _xdbPath); var srcPath = Path.Combine(AppContext.BaseDirectory, "TestData", $"ip{version}_source.txt"); - //foreach (var line in File.ReadLines(srcPath)) - //{ - // var ps = line.Trim().Split("|", 3); - // var sip = ps[0]; - // var eip = ps[1]; + foreach (var line in File.ReadLines(srcPath)) + { + var ps = line.Trim().Split("|", 3); + var sip = ps[0]; + var eip = ps[1]; - // var s1 = searcher.Search(sip); - // var s2 = searcher.Search(eip); - // //Assert.Equal(s1, ps[2]); - // //Assert.Equal(s2, ps[2]); - //} + var s1 = searcher.Search(sip); + var s2 = searcher.Search(eip); + Assert.Equal(s1, ps[2]); + Assert.Equal(s2, ps[2]); + } } } \ No newline at end of file From 62ab1605722df915a6b69b9c197712a80b7b72dd Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Thu, 20 Nov 2025 17:30:57 +0800 Subject: [PATCH 11/37] =?UTF-8?q?refactor:=20=E7=B2=BE=E7=AE=80=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Abstractions/AbstractCacheStrategy.cs | 16 +++---------- .../Internal/CacheStrategyFactory.cs | 23 +++++-------------- .../Internal/ContentCacheStrategy.cs | 6 ----- .../Internal/FileCacheStrategy.cs | 11 +-------- .../Internal/VectorIndexCacheStrategy.cs | 6 ----- 5 files changed, 10 insertions(+), 52 deletions(-) diff --git a/binding/csharp/IP2Region.Net/Internal/Abstractions/AbstractCacheStrategy.cs b/binding/csharp/IP2Region.Net/Internal/Abstractions/AbstractCacheStrategy.cs index 13d19e5..8d5f06c 100644 --- a/binding/csharp/IP2Region.Net/Internal/Abstractions/AbstractCacheStrategy.cs +++ b/binding/csharp/IP2Region.Net/Internal/Abstractions/AbstractCacheStrategy.cs @@ -26,16 +26,6 @@ internal abstract class AbstractCacheStrategy useAsync: true); } - protected int GetVectorIndexStartPos(uint ip) - { - var il0 = ip >> 24 & 0xFF; - var il1 = ip >> 16 & 0xFF; - var idx = il0 * VectorIndexCols * VectorIndexSize + il1 * VectorIndexSize; - return (int)idx; - } - - internal abstract ReadOnlyMemory GetVectorIndex(uint ip); - internal virtual ReadOnlyMemory GetData(int offset, int length) { byte[] buffer = ArrayPool.Shared.Rent(length); @@ -45,14 +35,14 @@ internal abstract class AbstractCacheStrategy XdbFileStream.Seek(offset, SeekOrigin.Begin); int bytesRead; - do + while (totalBytesRead < length) { int bytesToRead = Math.Min(BufferSize, length - totalBytesRead); bytesRead = XdbFileStream.Read(buffer, totalBytesRead, bytesToRead); totalBytesRead += bytesRead; - + IoCount++; - } while (bytesRead > 0 && totalBytesRead < length); + } } finally { diff --git a/binding/csharp/IP2Region.Net/Internal/CacheStrategyFactory.cs b/binding/csharp/IP2Region.Net/Internal/CacheStrategyFactory.cs index 14e8eb0..4d9b3d5 100644 --- a/binding/csharp/IP2Region.Net/Internal/CacheStrategyFactory.cs +++ b/binding/csharp/IP2Region.Net/Internal/CacheStrategyFactory.cs @@ -9,23 +9,12 @@ using IP2Region.Net.XDB; namespace IP2Region.Net.Internal; -internal class CacheStrategyFactory +internal class CacheStrategyFactory(string xdbPath) { - private readonly string _xdbPath; - - public CacheStrategyFactory(string xdbPath) + public AbstractCacheStrategy CreateCacheStrategy(CachePolicy cachePolicy) => cachePolicy switch { - _xdbPath = xdbPath; - } - - public AbstractCacheStrategy CreateCacheStrategy(CachePolicy cachePolicy) - { - return cachePolicy switch - { - CachePolicy.Content => new ContentCacheStrategy(_xdbPath), - CachePolicy.VectorIndex => new VectorIndexCacheStrategy(_xdbPath), - CachePolicy.File => new FileCacheStrategy(_xdbPath), - _ => throw new ArgumentException(nameof(cachePolicy)) - }; - } + CachePolicy.Content => new ContentCacheStrategy(xdbPath), + CachePolicy.VectorIndex => new VectorIndexCacheStrategy(xdbPath), + _ => new FileCacheStrategy(xdbPath), + }; } \ No newline at end of file diff --git a/binding/csharp/IP2Region.Net/Internal/ContentCacheStrategy.cs b/binding/csharp/IP2Region.Net/Internal/ContentCacheStrategy.cs index e3094e1..614495a 100644 --- a/binding/csharp/IP2Region.Net/Internal/ContentCacheStrategy.cs +++ b/binding/csharp/IP2Region.Net/Internal/ContentCacheStrategy.cs @@ -19,12 +19,6 @@ internal class ContentCacheStrategy : AbstractCacheStrategy XdbFileStream.Dispose(); } - internal override ReadOnlyMemory GetVectorIndex(uint ip) - { - int idx = GetVectorIndexStartPos(ip); - return _cacheData.Slice(HeaderInfoLength + idx, VectorIndexSize); - } - internal override ReadOnlyMemory GetData(int offset, int length) { return _cacheData.Slice(offset, length); diff --git a/binding/csharp/IP2Region.Net/Internal/FileCacheStrategy.cs b/binding/csharp/IP2Region.Net/Internal/FileCacheStrategy.cs index 31f90f3..0f98cfe 100644 --- a/binding/csharp/IP2Region.Net/Internal/FileCacheStrategy.cs +++ b/binding/csharp/IP2Region.Net/Internal/FileCacheStrategy.cs @@ -8,15 +8,6 @@ using IP2Region.Net.Internal.Abstractions; namespace IP2Region.Net.Internal; -internal class FileCacheStrategy : AbstractCacheStrategy +internal class FileCacheStrategy(string xdbPath) : AbstractCacheStrategy(xdbPath) { - public FileCacheStrategy(string xdbPath) : base(xdbPath) - { - } - - internal override ReadOnlyMemory GetVectorIndex(uint ip) - { - var idx = GetVectorIndexStartPos(ip); - return GetData(HeaderInfoLength + idx, VectorIndexSize); - } } \ No newline at end of file diff --git a/binding/csharp/IP2Region.Net/Internal/VectorIndexCacheStrategy.cs b/binding/csharp/IP2Region.Net/Internal/VectorIndexCacheStrategy.cs index f1d8ae6..e9f5fac 100644 --- a/binding/csharp/IP2Region.Net/Internal/VectorIndexCacheStrategy.cs +++ b/binding/csharp/IP2Region.Net/Internal/VectorIndexCacheStrategy.cs @@ -17,10 +17,4 @@ internal class VectorIndexCacheStrategy : AbstractCacheStrategy var vectorLength = VectorIndexRows * VectorIndexCols * VectorIndexSize; _vectorIndex = base.GetData(HeaderInfoLength, vectorLength); } - - internal override ReadOnlyMemory GetVectorIndex(uint ip) - { - var idx = GetVectorIndexStartPos(ip); - return _vectorIndex.Slice(idx, VectorIndexSize); - } } \ No newline at end of file From 9ef1fc34ac6fdb220f65eb3f4ee4070c46276e74 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Thu, 20 Nov 2025 17:31:13 +0800 Subject: [PATCH 12/37] =?UTF-8?q?test:=20=E5=A2=9E=E5=8A=A0=E5=8D=95?= =?UTF-8?q?=E5=85=83=E6=B5=8B=E8=AF=95=E6=8F=90=E9=AB=98=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E8=A6=86=E7=9B=96=E7=8E=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- binding/csharp/IP2Region.Net.Test/UtilTest.cs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/binding/csharp/IP2Region.Net.Test/UtilTest.cs b/binding/csharp/IP2Region.Net.Test/UtilTest.cs index aad3a28..8d6eb3d 100644 --- a/binding/csharp/IP2Region.Net.Test/UtilTest.cs +++ b/binding/csharp/IP2Region.Net.Test/UtilTest.cs @@ -4,11 +4,17 @@ namespace IP2Region.Net.Test; public class UtilTest { - [Theory] - [InlineData("114.114.114.114")] - public void TestIpAddressToUInt32(string value) + [Fact] + public void IpAddressToUInt32_Ok() { - var uintIp = XDB.Util.IpAddressToUInt32(value); - Console.WriteLine(uintIp); + var uintIp = XDB.Util.IpAddressToUInt32("114.114.114.114"); + Assert.Equal((uint)1920103026, uintIp); + } + + [Fact] + public void GetMidIp_Ok() + { + var uintIp = XDB.Util.GetMidIp(1, 10); + Assert.Equal((uint)5, uintIp); } } \ No newline at end of file From 3aa732d1ced11e351d634f3b99b110fc288b028a Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Thu, 20 Nov 2025 17:31:21 +0800 Subject: [PATCH 13/37] =?UTF-8?q?test:=20=E5=A2=9E=E5=8A=A0=E5=8D=95?= =?UTF-8?q?=E5=85=83=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- binding/csharp/IP2Region.Net.Test/XdbTest.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/binding/csharp/IP2Region.Net.Test/XdbTest.cs b/binding/csharp/IP2Region.Net.Test/XdbTest.cs index ff376a1..e7bec8d 100644 --- a/binding/csharp/IP2Region.Net.Test/XdbTest.cs +++ b/binding/csharp/IP2Region.Net.Test/XdbTest.cs @@ -32,4 +32,11 @@ public class XdbTest Assert.Equal(6, version.IPVer); Assert.Equal(4, version.BytesCount); } + + [Fact] + public async Task GetVersionAsync_Error() + { + await Assert.ThrowsAsync(async () => await XDB.Util.GetVersionAsync(null)); + await Assert.ThrowsAsync(async () => await XDB.Util.GetVersionAsync(Path.Combine(AppContext.BaseDirectory, "test.xdb"))); + } } From cbf8f9bfa87eb3f3c17f9751dd1fcba2b8e04038 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Thu, 20 Nov 2025 17:49:57 +0800 Subject: [PATCH 14/37] =?UTF-8?q?test:=20=E5=A2=9E=E5=8A=A0=E5=8D=95?= =?UTF-8?q?=E5=85=83=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../IP2Region.Net.Test.csproj | 4 + .../csharp/IP2Region.Net.Test/SearcherTest.cs | 92 +++++++++++++------ 2 files changed, 67 insertions(+), 29 deletions(-) diff --git a/binding/csharp/IP2Region.Net.Test/IP2Region.Net.Test.csproj b/binding/csharp/IP2Region.Net.Test/IP2Region.Net.Test.csproj index d9374f0..09afb18 100644 --- a/binding/csharp/IP2Region.Net.Test/IP2Region.Net.Test.csproj +++ b/binding/csharp/IP2Region.Net.Test/IP2Region.Net.Test.csproj @@ -21,6 +21,10 @@ + + + + diff --git a/binding/csharp/IP2Region.Net.Test/SearcherTest.cs b/binding/csharp/IP2Region.Net.Test/SearcherTest.cs index ed8078b..36716f1 100644 --- a/binding/csharp/IP2Region.Net.Test/SearcherTest.cs +++ b/binding/csharp/IP2Region.Net.Test/SearcherTest.cs @@ -1,4 +1,7 @@ +using IP2Region.Net.Abstractions; using IP2Region.Net.XDB; +using Microsoft.Extensions.DependencyInjection; +using System.Net; using Xunit; namespace IP2Region.Net.Test; @@ -14,7 +17,7 @@ public class SearcherTest [InlineData("119.29.29.29", "中国|北京|北京市|腾讯", "v4")] [InlineData("223.5.5.5", "中国|浙江省|杭州市|阿里云", "v4")] [InlineData("180.76.76.76", "中国|北京|北京市|百度", "v4")] - [InlineData("8.8.8.8", "", "v4")] + [InlineData("8.8.8.8", "美国|0|0|Level3", "v4")] [InlineData("240e:3b7:3272:d8d0:db09:c067:8d59:539e", "中国|广东省|深圳市|家庭宽带", "v6")] public void TestSearchCacheContent(string ip, string expected, string version) { @@ -30,7 +33,7 @@ public class SearcherTest [InlineData("119.29.29.29", "中国|北京|北京市|腾讯", "v4")] [InlineData("223.5.5.5", "中国|浙江省|杭州市|阿里云", "v4")] [InlineData("180.76.76.76", "中国|北京|北京市|百度", "v4")] - [InlineData("8.8.8.8", "", "v4")] + [InlineData("8.8.8.8", "美国|0|0|Level3", "v4")] [InlineData("240e:3b7:3272:d8d0:db09:c067:8d59:539e", "中国|广东省|深圳市|家庭宽带", "v6")] public void TestSearchCacheVector(string ip, string expected, string version) { @@ -41,13 +44,16 @@ public class SearcherTest } [Theory] + [InlineData("58.251.0.0", "中国|广东省|深圳市|联通", "v4")] + [InlineData("58.251.255.255", "中国|广东省|深圳市|联通", "v4")] [InlineData("58.251.27.201", "中国|广东省|深圳市|联通", "v4")] [InlineData("114.114.114.114", "中国|江苏省|南京市|0", "v4")] [InlineData("119.29.29.29", "中国|北京|北京市|腾讯", "v4")] [InlineData("223.5.5.5", "中国|浙江省|杭州市|阿里云", "v4")] [InlineData("180.76.76.76", "中国|北京|北京市|百度", "v4")] - [InlineData("8.8.8.8", "", "v4")] + [InlineData("8.8.8.8", "美国|0|0|Level3", "v4")] [InlineData("240e:3b7:3272:d8d0:db09:c067:8d59:539e", "中国|广东省|深圳市|家庭宽带", "v6")] + [InlineData("240e:044d:2d00:0000:0000:0000:0000:0000", "美国|加利福尼亚州|洛杉矶|移动网络", "v6")] public void TestSearchCacheFile(string ip, string expected, string version) { var _xdbPath = version == "v4" ? _xdbPathV4 : _xdbPathV6; @@ -57,37 +63,65 @@ public class SearcherTest } [Fact] - public void Test() + public void IoCount_Ok() { - var ip = XDB.Util.IpAddressToUInt32("58.251.27.201"); var searcher = new Searcher(CachePolicy.File, _xdbPathV4); - var region = searcher.Search(ip); - Assert.Equal("中国|广东省|深圳市|联通", region); + searcher.Search("58.251.27.201"); + + Assert.True(searcher.IoCount > 0); } [Theory] - [InlineData(CachePolicy.Content, "v4")] - [InlineData(CachePolicy.VectorIndex, "v4")] - [InlineData(CachePolicy.File, "v4")] - [InlineData(CachePolicy.Content, "v6")] - [InlineData(CachePolicy.VectorIndex, "v6")] - [InlineData(CachePolicy.File, "v6")] - public void TestBenchSearch(CachePolicy cachePolicy, string version) + [InlineData("58.251.255.255", "中国|广东省|深圳市|联通")] + public void Search_UintIp_Ok(string ipStr, string expected) { - var _xdbPath = version == "v4" ? _xdbPathV4 : _xdbPathV6; - var searcher = new Searcher(cachePolicy, _xdbPath); - var srcPath = Path.Combine(AppContext.BaseDirectory, "TestData", $"ip{version}_source.txt"); - - foreach (var line in File.ReadLines(srcPath)) - { - var ps = line.Trim().Split("|", 3); - var sip = ps[0]; - var eip = ps[1]; - - var s1 = searcher.Search(sip); - var s2 = searcher.Search(eip); - Assert.Equal(s1, ps[2]); - Assert.Equal(s2, ps[2]); - } + var fileSearcher = new Searcher(CachePolicy.File, _xdbPathV4); + var ipAddress = IPAddress.Parse(ipStr); + var ip = XDB.Util.IpAddressToUInt32(ipAddress); + var region = fileSearcher.Search(ip); + Assert.Equal(expected, region); } + + [Theory] + [InlineData("58.251.255.255", "中国|广东省|深圳市|联通")] + public void AddIP2RegionService_Ok(string ipStr, string expected) + { + var services = new ServiceCollection(); + services.AddIP2RegionService(_xdbPathV4, CachePolicy.File); + + var provider = services.BuildServiceProvider(); + var searcher = provider.GetRequiredService(); + var region = searcher.Search(ipStr); + Assert.Equal(expected, region); + + searcher = provider.GetRequiredKeyedService("IP2Region.Net"); + region = searcher.Search(ipStr); + Assert.Equal(expected, region); + } + + //[Theory] + //[InlineData(CachePolicy.Content, "v4")] + //[InlineData(CachePolicy.VectorIndex, "v4")] + //[InlineData(CachePolicy.File, "v4")] + //[InlineData(CachePolicy.Content, "v6")] + //[InlineData(CachePolicy.VectorIndex, "v6")] + //[InlineData(CachePolicy.File, "v6")] + //public void TestBenchSearch(CachePolicy cachePolicy, string version) + //{ + // var _xdbPath = version == "v4" ? _xdbPathV4 : _xdbPathV6; + // var searcher = new Searcher(cachePolicy, _xdbPath); + // var srcPath = Path.Combine(AppContext.BaseDirectory, "TestData", $"ip{version}_source.txt"); + + // foreach (var line in File.ReadLines(srcPath)) + // { + // var ps = line.Trim().Split("|", 3); + // var sip = ps[0]; + // var eip = ps[1]; + + // var s1 = searcher.Search(sip); + // var s2 = searcher.Search(eip); + // Assert.Equal(s1, ps[2]); + // Assert.Equal(s2, ps[2]); + // } + //} } \ No newline at end of file From f03861162f8fca5cf4072ec91e12b13fb46bfe71 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Thu, 20 Nov 2025 17:50:33 +0800 Subject: [PATCH 15/37] =?UTF-8?q?test:=20=E5=A2=9E=E5=8A=A0=E5=85=A8?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E9=AA=8C=E8=AF=81=E5=8D=95=E5=85=83=E6=B5=8B?= =?UTF-8?q?=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../csharp/IP2Region.Net.Test/SearcherTest.cs | 46 +++++++++---------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/binding/csharp/IP2Region.Net.Test/SearcherTest.cs b/binding/csharp/IP2Region.Net.Test/SearcherTest.cs index 36716f1..1c88d8d 100644 --- a/binding/csharp/IP2Region.Net.Test/SearcherTest.cs +++ b/binding/csharp/IP2Region.Net.Test/SearcherTest.cs @@ -99,29 +99,29 @@ public class SearcherTest Assert.Equal(expected, region); } - //[Theory] - //[InlineData(CachePolicy.Content, "v4")] - //[InlineData(CachePolicy.VectorIndex, "v4")] - //[InlineData(CachePolicy.File, "v4")] - //[InlineData(CachePolicy.Content, "v6")] - //[InlineData(CachePolicy.VectorIndex, "v6")] - //[InlineData(CachePolicy.File, "v6")] - //public void TestBenchSearch(CachePolicy cachePolicy, string version) - //{ - // var _xdbPath = version == "v4" ? _xdbPathV4 : _xdbPathV6; - // var searcher = new Searcher(cachePolicy, _xdbPath); - // var srcPath = Path.Combine(AppContext.BaseDirectory, "TestData", $"ip{version}_source.txt"); + [Theory] + [InlineData(CachePolicy.Content, "v4")] + [InlineData(CachePolicy.VectorIndex, "v4")] + [InlineData(CachePolicy.File, "v4")] + [InlineData(CachePolicy.Content, "v6")] + [InlineData(CachePolicy.VectorIndex, "v6")] + [InlineData(CachePolicy.File, "v6")] + public void TestBenchSearch(CachePolicy cachePolicy, string version) + { + var _xdbPath = version == "v4" ? _xdbPathV4 : _xdbPathV6; + var searcher = new Searcher(cachePolicy, _xdbPath); + var srcPath = Path.Combine(AppContext.BaseDirectory, "TestData", $"ip{version}_source.txt"); - // foreach (var line in File.ReadLines(srcPath)) - // { - // var ps = line.Trim().Split("|", 3); - // var sip = ps[0]; - // var eip = ps[1]; + foreach (var line in File.ReadLines(srcPath)) + { + var ps = line.Trim().Split("|", 3); + var sip = ps[0]; + var eip = ps[1]; - // var s1 = searcher.Search(sip); - // var s2 = searcher.Search(eip); - // Assert.Equal(s1, ps[2]); - // Assert.Equal(s2, ps[2]); - // } - //} + var s1 = searcher.Search(sip); + var s2 = searcher.Search(eip); + Assert.Equal(s1, ps[2]); + Assert.Equal(s2, ps[2]); + } + } } \ No newline at end of file From 55a493fd8e472e5d3764cdab2bac936b18be48df Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Thu, 20 Nov 2025 17:51:13 +0800 Subject: [PATCH 16/37] chore: bump version 3.0.0 --- binding/csharp/IP2Region.Net/IP2Region.Net.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/binding/csharp/IP2Region.Net/IP2Region.Net.csproj b/binding/csharp/IP2Region.Net/IP2Region.Net.csproj index badc071..380ec52 100644 --- a/binding/csharp/IP2Region.Net/IP2Region.Net.csproj +++ b/binding/csharp/IP2Region.Net/IP2Region.Net.csproj @@ -2,7 +2,7 @@ IP2Region.Net - 2.1.0 + 3.0.0 IP2Region.Net Alan Lee Apache-2.0 From d7d06e21858b66a83fc39afb3f9d2fcd43a7de15 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Thu, 20 Nov 2025 17:52:48 +0800 Subject: [PATCH 17/37] =?UTF-8?q?chore:=20=E5=8D=95=E5=85=83=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E6=A1=86=E6=9E=B6=E6=9B=B4=E6=94=B9=E4=B8=BA=20net10?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- binding/csharp/IP2Region.Net.Test/IP2Region.Net.Test.csproj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/binding/csharp/IP2Region.Net.Test/IP2Region.Net.Test.csproj b/binding/csharp/IP2Region.Net.Test/IP2Region.Net.Test.csproj index 09afb18..ac1ed80 100644 --- a/binding/csharp/IP2Region.Net.Test/IP2Region.Net.Test.csproj +++ b/binding/csharp/IP2Region.Net.Test/IP2Region.Net.Test.csproj @@ -1,7 +1,7 @@  - net9.0 + net10.0 enable enable @@ -9,7 +9,7 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive @@ -22,7 +22,7 @@ - + From f1d0e1bee02122ec2a4f7cd5481fdd937bc19d8e Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Fri, 21 Nov 2025 08:15:23 +0800 Subject: [PATCH 18/37] =?UTF-8?q?chore:=20=E6=9B=B4=E6=96=B0=20BenchMark?= =?UTF-8?q?=20=E5=B7=A5=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../IP2Region.Net.BenchMark.csproj | 12 ++++--- .../csharp/IP2Region.Net.BenchMark/Program.cs | 34 ++++++++++++++----- 2 files changed, 34 insertions(+), 12 deletions(-) diff --git a/binding/csharp/IP2Region.Net.BenchMark/IP2Region.Net.BenchMark.csproj b/binding/csharp/IP2Region.Net.BenchMark/IP2Region.Net.BenchMark.csproj index 8d4f436..4060806 100644 --- a/binding/csharp/IP2Region.Net.BenchMark/IP2Region.Net.BenchMark.csproj +++ b/binding/csharp/IP2Region.Net.BenchMark/IP2Region.Net.BenchMark.csproj @@ -2,14 +2,14 @@ Exe - net9.0 + net10.0 enable enable - - + + @@ -21,6 +21,10 @@ IP2Region/ip2region_v4.xdb PreserveNewest - + + IP2Region/ip2region_v6.xdb + PreserveNewest + + diff --git a/binding/csharp/IP2Region.Net.BenchMark/Program.cs b/binding/csharp/IP2Region.Net.BenchMark/Program.cs index b079b8a..8cccb07 100644 --- a/binding/csharp/IP2Region.Net.BenchMark/Program.cs +++ b/binding/csharp/IP2Region.Net.BenchMark/Program.cs @@ -7,23 +7,41 @@ BenchmarkRunner.Run(typeof(Program).Assembly); public class CachePolicyCompare { - private static readonly string XdbPath = Path.Combine(AppContext.BaseDirectory, "IP2Region", "ip2region_v4.xdb"); - private readonly ISearcher _contentSearcher = new Searcher(CachePolicy.Content, XdbPath); - private readonly ISearcher _vectorSearcher = new Searcher(CachePolicy.VectorIndex,XdbPath); - private readonly ISearcher _fileSearcher = new Searcher(CachePolicy.File,XdbPath); + private static readonly string XdbPathV4 = Path.Combine(AppContext.BaseDirectory, "IP2Region", "ip2region_v4.xdb"); + private static readonly string XdbPathV6 = Path.Combine(AppContext.BaseDirectory, "IP2Region", "ip2region_v6.xdb"); + private readonly ISearcher _contentV4Searcher = new Searcher(CachePolicy.Content, XdbPathV4); + private readonly ISearcher _vectorV4Searcher = new Searcher(CachePolicy.VectorIndex, XdbPathV4); + private readonly ISearcher _fileV4Searcher = new Searcher(CachePolicy.File, XdbPathV4); + private readonly ISearcher _contentV6Searcher = new Searcher(CachePolicy.Content, XdbPathV6); + private readonly ISearcher _vectorV6Searcher = new Searcher(CachePolicy.VectorIndex, XdbPathV6); + private readonly ISearcher _fileV6Searcher = new Searcher(CachePolicy.File, XdbPathV6); - private readonly string _testIpAddress = "114.114.114.114"; + private readonly string _testIpV4Address = "114.114.114.114"; + private readonly string _testIpV6Address = "240e:3b7:3272:d8d0:db09:c067:8d59:539e"; [Benchmark] [BenchmarkCategory(nameof(CachePolicy.Content))] - public void CachePolicy_Content() => _contentSearcher.Search(_testIpAddress); + public void ContentIpV4() => _contentV4Searcher.Search(_testIpV4Address); [Benchmark] [BenchmarkCategory(nameof(CachePolicy.VectorIndex))] - public void CachePolicy_VectorIndex() => _vectorSearcher.Search(_testIpAddress); + public void VectorIndexIpV4() => _vectorV4Searcher.Search(_testIpV4Address); [Benchmark] [BenchmarkCategory(nameof(CachePolicy.File))] - public void CachePolicy_File() => _fileSearcher.Search(_testIpAddress); + public void FileIpV4() => _fileV4Searcher.Search(_testIpV4Address); + + [Benchmark] + [BenchmarkCategory(nameof(CachePolicy.Content))] + public void ContentIpV6() => _contentV6Searcher.Search(_testIpV6Address); + + [Benchmark] + [BenchmarkCategory(nameof(CachePolicy.VectorIndex))] + public void VectorIndexIpV6() => _vectorV6Searcher.Search(_testIpV6Address); + + + [Benchmark] + [BenchmarkCategory(nameof(CachePolicy.File))] + public void FileIpV6() => _fileV6Searcher.Search(_testIpV6Address); } \ No newline at end of file From 92cc3fd7cba939bfc53e7ac89ed1597a5f76b6ac Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Fri, 21 Nov 2025 08:52:02 +0800 Subject: [PATCH 19/37] =?UTF-8?q?chore:=20=E5=A2=9E=E5=8A=A0=20editorconfi?= =?UTF-8?q?g=20=E9=85=8D=E7=BD=AE=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- binding/csharp/.editorconfig | 26 ++++++++++++++++++++++++++ binding/csharp/IP2Region.Net.slnx | 1 + 2 files changed, 27 insertions(+) create mode 100644 binding/csharp/.editorconfig diff --git a/binding/csharp/.editorconfig b/binding/csharp/.editorconfig new file mode 100644 index 0000000..b53f6cb --- /dev/null +++ b/binding/csharp/.editorconfig @@ -0,0 +1,26 @@ +root = true + +# To learn more about .editorconfig see https://aka.ms/editorconfigdocs +[*] +charset = utf-8 +indent_style = space +trim_trailing_whitespace = true +insert_final_newline = true +spelling_exclusion_path = .\exclusion.dic + +[*.csproj] +charset = utf-8 +insert_final_newline = true + +[*.{xml,config,csproj,nuspec,props,resx,targets,yml,tasks,json}] +indent_size = 2 + +[*.sh] +end_of_line = lf + +[*.cs] +dotnet_analyzer_diagnostic.category-Style.severity = none +csharp_style_namespace_declarations = file_scoped:silent + +[*.cs] +file_header_template = Copyright 2023 The Ip2Region Authors. All rights reserved.\nUse of this source code is governed by a Apache2.0-style\nlicense that can be found in the LICENSE file.\n@Author Alan \n@Date 2023/07/25\nUpdated by Argo Zhang at 2025/11/21 diff --git a/binding/csharp/IP2Region.Net.slnx b/binding/csharp/IP2Region.Net.slnx index 559f5de..1dc947d 100644 --- a/binding/csharp/IP2Region.Net.slnx +++ b/binding/csharp/IP2Region.Net.slnx @@ -1,5 +1,6 @@ + From 75d944eaf774184bf385ca84fa604299f26b2863 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Fri, 21 Nov 2025 08:52:17 +0800 Subject: [PATCH 20/37] =?UTF-8?q?doc:=20=E5=A2=9E=E5=8A=A0=E7=B1=BB?= =?UTF-8?q?=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../IP2Region.Net/Abstractions/ISearcher.cs | 20 ++++++++++++++++++- binding/csharp/IP2Region.Net/XDB/Util.cs | 2 +- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/binding/csharp/IP2Region.Net/Abstractions/ISearcher.cs b/binding/csharp/IP2Region.Net/Abstractions/ISearcher.cs index ffd64b9..d270e4e 100644 --- a/binding/csharp/IP2Region.Net/Abstractions/ISearcher.cs +++ b/binding/csharp/IP2Region.Net/Abstractions/ISearcher.cs @@ -8,13 +8,31 @@ using System.Net; namespace IP2Region.Net.Abstractions; +/// +/// IP 转化为地理位置搜索器接口 +/// public interface ISearcher { + /// + /// 搜索方法 + /// + /// IP 地址字符串 如 192.168.0.1 + /// string? Search(string ipStr); + /// + /// 搜索方法 + /// string? Search(IPAddress ipAddress); + /// + /// 搜索方法 仅限 IPv4 使用 + /// + /// IPv4 地址字节数组小端读取 uint 数值 string? Search(uint ipAddress); + /// + /// 获得 内部 IO 访问次数 + /// int IoCount { get; } -} \ No newline at end of file +} diff --git a/binding/csharp/IP2Region.Net/XDB/Util.cs b/binding/csharp/IP2Region.Net/XDB/Util.cs index d92a613..b151df2 100644 --- a/binding/csharp/IP2Region.Net/XDB/Util.cs +++ b/binding/csharp/IP2Region.Net/XDB/Util.cs @@ -73,4 +73,4 @@ public static class Util return ret; } -} \ No newline at end of file +} From 002c482db3787318fb5b4393d61d5ae60b95da36 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Fri, 21 Nov 2025 09:03:37 +0800 Subject: [PATCH 21/37] =?UTF-8?q?doc:=20=E6=96=87=E6=A1=A3=E7=BB=9F?= =?UTF-8?q?=E4=B8=80=E6=A0=BC=E5=BC=8F=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Extensions/ServiceCollectionExtensions.cs | 2 +- .../Abstractions/AbstractCacheStrategy.cs | 7 +------ .../Internal/CacheStrategyFactory.cs | 2 +- .../Internal/ContentCacheStrategy.cs | 6 +++--- .../Internal/FileCacheStrategy.cs | 5 +++-- .../Internal/VectorIndexCacheStrategy.cs | 9 +++++++-- .../csharp/IP2Region.Net/XDB/CachePolicy.cs | 7 ++++++- binding/csharp/IP2Region.Net/XDB/Searcher.cs | 18 ++++++++++++++++++ binding/csharp/IP2Region.Net/XDB/Util.cs | 3 +++ binding/csharp/IP2Region.Net/XDB/XdbVersion.cs | 5 ++++- 10 files changed, 47 insertions(+), 17 deletions(-) diff --git a/binding/csharp/IP2Region.Net/Extensions/ServiceCollectionExtensions.cs b/binding/csharp/IP2Region.Net/Extensions/ServiceCollectionExtensions.cs index b12278d..ace55ad 100644 --- a/binding/csharp/IP2Region.Net/Extensions/ServiceCollectionExtensions.cs +++ b/binding/csharp/IP2Region.Net/Extensions/ServiceCollectionExtensions.cs @@ -1,4 +1,4 @@ -// Copyright 2023 The Ip2Region Authors. All rights reserved. +// Copyright 2023 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. // @Author Alan diff --git a/binding/csharp/IP2Region.Net/Internal/Abstractions/AbstractCacheStrategy.cs b/binding/csharp/IP2Region.Net/Internal/Abstractions/AbstractCacheStrategy.cs index 8d5f06c..3509187 100644 --- a/binding/csharp/IP2Region.Net/Internal/Abstractions/AbstractCacheStrategy.cs +++ b/binding/csharp/IP2Region.Net/Internal/Abstractions/AbstractCacheStrategy.cs @@ -10,11 +10,6 @@ namespace IP2Region.Net.Internal.Abstractions; internal abstract class AbstractCacheStrategy { - protected const int HeaderInfoLength = 256; - protected const int VectorIndexRows = 256; - protected const int VectorIndexCols = 256; - protected const int VectorIndexSize = 8; - protected readonly FileStream XdbFileStream; private const int BufferSize = 4096; @@ -51,4 +46,4 @@ internal abstract class AbstractCacheStrategy return new ReadOnlyMemory(buffer, 0, totalBytesRead); } -} \ No newline at end of file +} diff --git a/binding/csharp/IP2Region.Net/Internal/CacheStrategyFactory.cs b/binding/csharp/IP2Region.Net/Internal/CacheStrategyFactory.cs index 4d9b3d5..0deb198 100644 --- a/binding/csharp/IP2Region.Net/Internal/CacheStrategyFactory.cs +++ b/binding/csharp/IP2Region.Net/Internal/CacheStrategyFactory.cs @@ -17,4 +17,4 @@ internal class CacheStrategyFactory(string xdbPath) CachePolicy.VectorIndex => new VectorIndexCacheStrategy(xdbPath), _ => new FileCacheStrategy(xdbPath), }; -} \ No newline at end of file +} diff --git a/binding/csharp/IP2Region.Net/Internal/ContentCacheStrategy.cs b/binding/csharp/IP2Region.Net/Internal/ContentCacheStrategy.cs index 614495a..c455521 100644 --- a/binding/csharp/IP2Region.Net/Internal/ContentCacheStrategy.cs +++ b/binding/csharp/IP2Region.Net/Internal/ContentCacheStrategy.cs @@ -8,9 +8,9 @@ using IP2Region.Net.Internal.Abstractions; namespace IP2Region.Net.Internal; -internal class ContentCacheStrategy : AbstractCacheStrategy +class ContentCacheStrategy : AbstractCacheStrategy { - private readonly ReadOnlyMemory _cacheData; + readonly ReadOnlyMemory _cacheData; public ContentCacheStrategy(string xdbPath) : base(xdbPath) { @@ -23,4 +23,4 @@ internal class ContentCacheStrategy : AbstractCacheStrategy { return _cacheData.Slice(offset, length); } -} \ No newline at end of file +} diff --git a/binding/csharp/IP2Region.Net/Internal/FileCacheStrategy.cs b/binding/csharp/IP2Region.Net/Internal/FileCacheStrategy.cs index 0f98cfe..3a597b0 100644 --- a/binding/csharp/IP2Region.Net/Internal/FileCacheStrategy.cs +++ b/binding/csharp/IP2Region.Net/Internal/FileCacheStrategy.cs @@ -8,6 +8,7 @@ using IP2Region.Net.Internal.Abstractions; namespace IP2Region.Net.Internal; -internal class FileCacheStrategy(string xdbPath) : AbstractCacheStrategy(xdbPath) +class FileCacheStrategy(string xdbPath) : AbstractCacheStrategy(xdbPath) { -} \ No newline at end of file + +} diff --git a/binding/csharp/IP2Region.Net/Internal/VectorIndexCacheStrategy.cs b/binding/csharp/IP2Region.Net/Internal/VectorIndexCacheStrategy.cs index e9f5fac..e9eb40a 100644 --- a/binding/csharp/IP2Region.Net/Internal/VectorIndexCacheStrategy.cs +++ b/binding/csharp/IP2Region.Net/Internal/VectorIndexCacheStrategy.cs @@ -10,11 +10,16 @@ namespace IP2Region.Net.Internal; internal class VectorIndexCacheStrategy : AbstractCacheStrategy { - private readonly ReadOnlyMemory _vectorIndex; + const int HeaderInfoLength = 256; + const int VectorIndexRows = 256; + const int VectorIndexCols = 256; + const int VectorIndexSize = 8; + + readonly ReadOnlyMemory _vectorIndex; public VectorIndexCacheStrategy(string xdbPath) : base(xdbPath) { var vectorLength = VectorIndexRows * VectorIndexCols * VectorIndexSize; _vectorIndex = base.GetData(HeaderInfoLength, vectorLength); } -} \ No newline at end of file +} diff --git a/binding/csharp/IP2Region.Net/XDB/CachePolicy.cs b/binding/csharp/IP2Region.Net/XDB/CachePolicy.cs index 0f4d035..9397ee0 100644 --- a/binding/csharp/IP2Region.Net/XDB/CachePolicy.cs +++ b/binding/csharp/IP2Region.Net/XDB/CachePolicy.cs @@ -1,17 +1,22 @@ namespace IP2Region.Net.XDB; +/// +/// 缓存策略枚举 +/// public enum CachePolicy { /// /// no cache /// File, + /// /// cache vector index , reduce the number of IO operations /// VectorIndex, + /// /// default cache policy , cache whole xdb file /// Content -} \ No newline at end of file +} diff --git a/binding/csharp/IP2Region.Net/XDB/Searcher.cs b/binding/csharp/IP2Region.Net/XDB/Searcher.cs index 60690c8..5fcbf30 100644 --- a/binding/csharp/IP2Region.Net/XDB/Searcher.cs +++ b/binding/csharp/IP2Region.Net/XDB/Searcher.cs @@ -13,29 +13,47 @@ using System.Text; namespace IP2Region.Net.XDB; +/// +/// 实现类 +/// public class Searcher : ISearcher { private readonly AbstractCacheStrategy _cacheStrategy; + /// + /// + /// public int IoCount => _cacheStrategy.IoCount; + /// + /// + /// public Searcher(CachePolicy cachePolicy, string dbPath) { var factory = new CacheStrategyFactory(dbPath); _cacheStrategy = factory.CreateCacheStrategy(cachePolicy); } + /// + /// + /// public string? Search(string ipStr) { var ipAddress = IPAddress.Parse(ipStr); return Search(ipAddress); } + /// + /// + /// public string? Search(IPAddress ipAddress) { return SearchCore(ipAddress.GetAddressBytes()); } + /// + /// + /// public string? Search(uint ipAddress) { var bytes = BitConverter.GetBytes(ipAddress); diff --git a/binding/csharp/IP2Region.Net/XDB/Util.cs b/binding/csharp/IP2Region.Net/XDB/Util.cs index b151df2..f593a9b 100644 --- a/binding/csharp/IP2Region.Net/XDB/Util.cs +++ b/binding/csharp/IP2Region.Net/XDB/Util.cs @@ -5,6 +5,9 @@ using System.Runtime.InteropServices; namespace IP2Region.Net.XDB; +/// +/// 工具类 +/// public static class Util { public static uint IpAddressToUInt32(string ipAddress) diff --git a/binding/csharp/IP2Region.Net/XDB/XdbVersion.cs b/binding/csharp/IP2Region.Net/XDB/XdbVersion.cs index 1fdb743..e93e9db 100644 --- a/binding/csharp/IP2Region.Net/XDB/XdbVersion.cs +++ b/binding/csharp/IP2Region.Net/XDB/XdbVersion.cs @@ -1,5 +1,8 @@ -namespace IP2Region.Net.XDB; +namespace IP2Region.Net.XDB; +/// +/// XdbVersion 结构体 +/// public struct XdbVersion { /// From 87b69e6b5fc931e1e494c84c239db5d727ad0bc8 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Fri, 21 Nov 2025 09:42:59 +0800 Subject: [PATCH 22/37] =?UTF-8?q?perf:=20=E6=8F=90=E9=AB=98=20Content=20?= =?UTF-8?q?=E7=BC=93=E5=AD=98=E6=A8=A1=E5=BC=8F=E6=80=A7=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Abstractions/AbstractCacheStrategy.cs | 25 +++++++++++-------- .../Internal/ContentCacheStrategy.cs | 17 ++++++------- binding/csharp/IP2Region.Net/XDB/Util.cs | 7 +++++- 3 files changed, 29 insertions(+), 20 deletions(-) diff --git a/binding/csharp/IP2Region.Net/Internal/Abstractions/AbstractCacheStrategy.cs b/binding/csharp/IP2Region.Net/Internal/Abstractions/AbstractCacheStrategy.cs index 3509187..9ba96af 100644 --- a/binding/csharp/IP2Region.Net/Internal/Abstractions/AbstractCacheStrategy.cs +++ b/binding/csharp/IP2Region.Net/Internal/Abstractions/AbstractCacheStrategy.cs @@ -8,32 +8,26 @@ using System.Buffers; namespace IP2Region.Net.Internal.Abstractions; -internal abstract class AbstractCacheStrategy +internal abstract class AbstractCacheStrategy(string xdbPath) { - protected readonly FileStream XdbFileStream; private const int BufferSize = 4096; internal int IoCount { get; private set; } - protected AbstractCacheStrategy(string xdbPath) - { - XdbFileStream = new FileStream(xdbPath, FileMode.Open, FileAccess.Read, FileShare.Read, BufferSize, - useAsync: true); - } - internal virtual ReadOnlyMemory GetData(int offset, int length) { byte[] buffer = ArrayPool.Shared.Rent(length); int totalBytesRead = 0; try { - XdbFileStream.Seek(offset, SeekOrigin.Begin); + var stream = GetXdbFileStream(); + stream.Seek(offset, SeekOrigin.Begin); int bytesRead; while (totalBytesRead < length) { int bytesToRead = Math.Min(BufferSize, length - totalBytesRead); - bytesRead = XdbFileStream.Read(buffer, totalBytesRead, bytesToRead); + bytesRead = stream.Read(buffer, totalBytesRead, bytesToRead); totalBytesRead += bytesRead; IoCount++; @@ -46,4 +40,15 @@ internal abstract class AbstractCacheStrategy return new ReadOnlyMemory(buffer, 0, totalBytesRead); } + + FileStream? _xdbFileStream; + + protected FileStream GetXdbFileStream() + { + if (_xdbFileStream == null) + { + _xdbFileStream = new FileStream(xdbPath, FileMode.Open, FileAccess.Read, FileShare.Read, BufferSize, FileOptions.RandomAccess); + } + return _xdbFileStream; + } } diff --git a/binding/csharp/IP2Region.Net/Internal/ContentCacheStrategy.cs b/binding/csharp/IP2Region.Net/Internal/ContentCacheStrategy.cs index c455521..0ef7251 100644 --- a/binding/csharp/IP2Region.Net/Internal/ContentCacheStrategy.cs +++ b/binding/csharp/IP2Region.Net/Internal/ContentCacheStrategy.cs @@ -8,19 +8,18 @@ using IP2Region.Net.Internal.Abstractions; namespace IP2Region.Net.Internal; -class ContentCacheStrategy : AbstractCacheStrategy +class ContentCacheStrategy(string xdbPath) : AbstractCacheStrategy(xdbPath) { - readonly ReadOnlyMemory _cacheData; - - public ContentCacheStrategy(string xdbPath) : base(xdbPath) - { - _cacheData = base.GetData(0, (int)XdbFileStream.Length); - XdbFileStream.Close(); - XdbFileStream.Dispose(); - } + ReadOnlyMemory _cacheData = ReadOnlyMemory.Empty; internal override ReadOnlyMemory GetData(int offset, int length) { + if (_cacheData.IsEmpty) + { + using var reader = base.GetXdbFileStream(); + _cacheData = base.GetData(0, (int)reader.Length); + } + return _cacheData.Slice(offset, length); } } diff --git a/binding/csharp/IP2Region.Net/XDB/Util.cs b/binding/csharp/IP2Region.Net/XDB/Util.cs index f593a9b..5f8673c 100644 --- a/binding/csharp/IP2Region.Net/XDB/Util.cs +++ b/binding/csharp/IP2Region.Net/XDB/Util.cs @@ -38,8 +38,13 @@ public static class Util throw new FileNotFoundException("xdb file not fould.", dbPath); } - XdbVersion ret = default; using var reader = File.OpenRead(dbPath); + return await GetVersionAsync(reader, token); + } + + internal static async Task GetVersionAsync(FileStream reader, CancellationToken token = default) + { + XdbVersion ret = default; var buffer = ArrayPool.Shared.Rent(256); try From 5cfd5f824223c9c371a97f9d405839643f0e35c6 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Fri, 21 Nov 2025 11:15:31 +0800 Subject: [PATCH 23/37] =?UTF-8?q?perf:=20=E5=A2=9E=E5=8A=A0=E5=90=91?= =?UTF-8?q?=E9=87=8F=E7=BC=93=E5=AD=98=E9=80=BB=E8=BE=91=E6=8F=90=E9=AB=98?= =?UTF-8?q?=E6=80=A7=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Abstractions/AbstractCacheStrategy.cs | 16 +++++++++++++--- .../Internal/ContentCacheStrategy.cs | 4 ++-- .../Internal/VectorIndexCacheStrategy.cs | 18 ++++++++---------- binding/csharp/IP2Region.Net/XDB/Searcher.cs | 7 +++++-- 4 files changed, 28 insertions(+), 17 deletions(-) diff --git a/binding/csharp/IP2Region.Net/Internal/Abstractions/AbstractCacheStrategy.cs b/binding/csharp/IP2Region.Net/Internal/Abstractions/AbstractCacheStrategy.cs index 9ba96af..e277dde 100644 --- a/binding/csharp/IP2Region.Net/Internal/Abstractions/AbstractCacheStrategy.cs +++ b/binding/csharp/IP2Region.Net/Internal/Abstractions/AbstractCacheStrategy.cs @@ -10,11 +10,21 @@ namespace IP2Region.Net.Internal.Abstractions; internal abstract class AbstractCacheStrategy(string xdbPath) { - private const int BufferSize = 4096; + private const int BufferSize = 64 * 1024; - internal int IoCount { get; private set; } + public int IoCount { get; private set; } - internal virtual ReadOnlyMemory GetData(int offset, int length) + public void ResetIoCount() + { + IoCount = 0; + } + + public virtual ReadOnlyMemory GetVectorIndexStartPos(int offset) + { + return GetData(256 + offset, 8); + } + + public virtual ReadOnlyMemory GetData(int offset, int length) { byte[] buffer = ArrayPool.Shared.Rent(length); int totalBytesRead = 0; diff --git a/binding/csharp/IP2Region.Net/Internal/ContentCacheStrategy.cs b/binding/csharp/IP2Region.Net/Internal/ContentCacheStrategy.cs index 0ef7251..94e7f51 100644 --- a/binding/csharp/IP2Region.Net/Internal/ContentCacheStrategy.cs +++ b/binding/csharp/IP2Region.Net/Internal/ContentCacheStrategy.cs @@ -10,9 +10,9 @@ namespace IP2Region.Net.Internal; class ContentCacheStrategy(string xdbPath) : AbstractCacheStrategy(xdbPath) { - ReadOnlyMemory _cacheData = ReadOnlyMemory.Empty; + ReadOnlyMemory _cacheData = default; - internal override ReadOnlyMemory GetData(int offset, int length) + public override ReadOnlyMemory GetData(int offset, int length) { if (_cacheData.IsEmpty) { diff --git a/binding/csharp/IP2Region.Net/Internal/VectorIndexCacheStrategy.cs b/binding/csharp/IP2Region.Net/Internal/VectorIndexCacheStrategy.cs index e9eb40a..4dea3fe 100644 --- a/binding/csharp/IP2Region.Net/Internal/VectorIndexCacheStrategy.cs +++ b/binding/csharp/IP2Region.Net/Internal/VectorIndexCacheStrategy.cs @@ -8,18 +8,16 @@ using IP2Region.Net.Internal.Abstractions; namespace IP2Region.Net.Internal; -internal class VectorIndexCacheStrategy : AbstractCacheStrategy +internal class VectorIndexCacheStrategy(string xdbPath) : AbstractCacheStrategy(xdbPath) { - const int HeaderInfoLength = 256; - const int VectorIndexRows = 256; - const int VectorIndexCols = 256; - const int VectorIndexSize = 8; + ReadOnlyMemory _vectorCahe = default; - readonly ReadOnlyMemory _vectorIndex; - - public VectorIndexCacheStrategy(string xdbPath) : base(xdbPath) + public override ReadOnlyMemory GetVectorIndexStartPos(int offset) { - var vectorLength = VectorIndexRows * VectorIndexCols * VectorIndexSize; - _vectorIndex = base.GetData(HeaderInfoLength, vectorLength); + if (_vectorCahe.IsEmpty) + { + _vectorCahe = base.GetData(256, 256 * 256 * 8); + } + return _vectorCahe.Slice(offset, 8); } } diff --git a/binding/csharp/IP2Region.Net/XDB/Searcher.cs b/binding/csharp/IP2Region.Net/XDB/Searcher.cs index 5fcbf30..1aa7fc6 100644 --- a/binding/csharp/IP2Region.Net/XDB/Searcher.cs +++ b/binding/csharp/IP2Region.Net/XDB/Searcher.cs @@ -40,7 +40,7 @@ public class Searcher : ISearcher public string? Search(string ipStr) { var ipAddress = IPAddress.Parse(ipStr); - return Search(ipAddress); + return SearchCore(ipAddress.GetAddressBytes()); } /// @@ -63,6 +63,9 @@ public class Searcher : ISearcher string? SearchCore(byte[] ipBytes) { + // 重置 IO 计数器 + _cacheStrategy.ResetIoCount(); + // 每个 vector 索引项的字节数 var vectorIndexSize = 8; @@ -74,7 +77,7 @@ public class Searcher : ISearcher var il1 = ipBytes[1]; var idx = il0 * vectorIndexCols * vectorIndexSize + il1 * vectorIndexSize; - var data = _cacheStrategy.GetData(256 + idx, vectorIndexSize); + var data = _cacheStrategy.GetVectorIndexStartPos(idx); var sPtr = BinaryPrimitives.ReadUInt32LittleEndian(data.Span); var ePtr = BinaryPrimitives.ReadUInt32LittleEndian(data.Span.Slice(4)); From db50f7b0dacc2f1816cf49ed27312689ad555efe Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Fri, 21 Nov 2025 11:15:46 +0800 Subject: [PATCH 24/37] =?UTF-8?q?test:=20=E5=A2=9E=E5=8A=A0=E5=8D=95?= =?UTF-8?q?=E5=85=83=E6=B5=8B=E8=AF=95=E8=A6=86=E7=9B=96=E5=90=91=E9=87=8F?= =?UTF-8?q?=E7=BC=93=E5=AD=98=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../csharp/IP2Region.Net.Test/SearcherTest.cs | 30 +++++++++++++++++-- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/binding/csharp/IP2Region.Net.Test/SearcherTest.cs b/binding/csharp/IP2Region.Net.Test/SearcherTest.cs index 1c88d8d..ee8b7be 100644 --- a/binding/csharp/IP2Region.Net.Test/SearcherTest.cs +++ b/binding/csharp/IP2Region.Net.Test/SearcherTest.cs @@ -63,12 +63,36 @@ public class SearcherTest } [Fact] - public void IoCount_Ok() + public void IoCount_File_Ok() { var searcher = new Searcher(CachePolicy.File, _xdbPathV4); searcher.Search("58.251.27.201"); + Assert.Equal(3, searcher.IoCount); - Assert.True(searcher.IoCount > 0); + searcher.Search("58.251.27.201"); + Assert.Equal(3, searcher.IoCount); + } + + [Fact] + public void IoCount_Content_Ok() + { + var searcher = new Searcher(CachePolicy.Content, _xdbPathV4); + searcher.Search("58.251.27.201"); + Assert.Equal(169, searcher.IoCount); + + searcher.Search("58.251.27.201"); + Assert.Equal(0, searcher.IoCount); + } + + [Fact] + public void IoCount_Vector_Ok() + { + var searcher = new Searcher(CachePolicy.VectorIndex, _xdbPathV4); + searcher.Search("58.251.27.201"); + Assert.Equal(10, searcher.IoCount); + + searcher.Search("58.251.27.201"); + Assert.Equal(2, searcher.IoCount); } [Theory] @@ -124,4 +148,4 @@ public class SearcherTest Assert.Equal(s2, ps[2]); } } -} \ No newline at end of file +} From ca7fc939532399b05d21825f47aaae2da9d11f61 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Fri, 21 Nov 2025 11:18:37 +0800 Subject: [PATCH 25/37] =?UTF-8?q?test:=20=E5=A2=9E=E5=8A=A0=E5=8D=95?= =?UTF-8?q?=E5=85=83=E6=B5=8B=E8=AF=95=E6=8F=90=E9=AB=98=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E8=A6=86=E7=9B=96=E7=8E=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- binding/csharp/IP2Region.Net.Test/SearcherTest.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/binding/csharp/IP2Region.Net.Test/SearcherTest.cs b/binding/csharp/IP2Region.Net.Test/SearcherTest.cs index ee8b7be..80a3358 100644 --- a/binding/csharp/IP2Region.Net.Test/SearcherTest.cs +++ b/binding/csharp/IP2Region.Net.Test/SearcherTest.cs @@ -97,12 +97,15 @@ public class SearcherTest [Theory] [InlineData("58.251.255.255", "中国|广东省|深圳市|联通")] - public void Search_UintIp_Ok(string ipStr, string expected) + public void Search_Ip_Ok(string ipStr, string expected) { var fileSearcher = new Searcher(CachePolicy.File, _xdbPathV4); var ipAddress = IPAddress.Parse(ipStr); + var region = fileSearcher.Search(ipAddress); + Assert.Equal(expected, region); + var ip = XDB.Util.IpAddressToUInt32(ipAddress); - var region = fileSearcher.Search(ip); + region = fileSearcher.Search(ip); Assert.Equal(expected, region); } From 30fa54a83fc94563f6850e430158c5c5abcd4913 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Fri, 21 Nov 2025 11:27:51 +0800 Subject: [PATCH 26/37] =?UTF-8?q?perf:=20=E6=9B=B4=E6=96=B0=E5=9F=BA?= =?UTF-8?q?=E5=87=86=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../csharp/IP2Region.Net.BenchMark/Program.cs | 27 ++++++++++++------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/binding/csharp/IP2Region.Net.BenchMark/Program.cs b/binding/csharp/IP2Region.Net.BenchMark/Program.cs index 8cccb07..9f7d0b0 100644 --- a/binding/csharp/IP2Region.Net.BenchMark/Program.cs +++ b/binding/csharp/IP2Region.Net.BenchMark/Program.cs @@ -1,6 +1,5 @@ -using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Running; -using IP2Region.Net.Abstractions; using IP2Region.Net.XDB; BenchmarkRunner.Run(typeof(Program).Assembly); @@ -9,16 +8,26 @@ public class CachePolicyCompare { private static readonly string XdbPathV4 = Path.Combine(AppContext.BaseDirectory, "IP2Region", "ip2region_v4.xdb"); private static readonly string XdbPathV6 = Path.Combine(AppContext.BaseDirectory, "IP2Region", "ip2region_v6.xdb"); - private readonly ISearcher _contentV4Searcher = new Searcher(CachePolicy.Content, XdbPathV4); - private readonly ISearcher _vectorV4Searcher = new Searcher(CachePolicy.VectorIndex, XdbPathV4); - private readonly ISearcher _fileV4Searcher = new Searcher(CachePolicy.File, XdbPathV4); - private readonly ISearcher _contentV6Searcher = new Searcher(CachePolicy.Content, XdbPathV6); - private readonly ISearcher _vectorV6Searcher = new Searcher(CachePolicy.VectorIndex, XdbPathV6); - private readonly ISearcher _fileV6Searcher = new Searcher(CachePolicy.File, XdbPathV6); + private readonly Searcher _contentV4Searcher = new Searcher(CachePolicy.Content, XdbPathV4); + private readonly Searcher _vectorV4Searcher = new Searcher(CachePolicy.VectorIndex, XdbPathV4); + private readonly Searcher _fileV4Searcher = new Searcher(CachePolicy.File, XdbPathV4); + private readonly Searcher _contentV6Searcher = new Searcher(CachePolicy.Content, XdbPathV6); + private readonly Searcher _vectorV6Searcher = new Searcher(CachePolicy.VectorIndex, XdbPathV6); + private readonly Searcher _fileV6Searcher = new Searcher(CachePolicy.File, XdbPathV6); private readonly string _testIpV4Address = "114.114.114.114"; private readonly string _testIpV6Address = "240e:3b7:3272:d8d0:db09:c067:8d59:539e"; + public CachePolicyCompare() + { + _contentV4Searcher.Search(_testIpV4Address); + _vectorV4Searcher.Search(_testIpV4Address); + _fileV4Searcher.Search(_testIpV4Address); + _contentV6Searcher.Search(_testIpV6Address); + _vectorV6Searcher.Search(_testIpV6Address); + _fileV6Searcher.Search(_testIpV6Address); + } + [Benchmark] [BenchmarkCategory(nameof(CachePolicy.Content))] public void ContentIpV4() => _contentV4Searcher.Search(_testIpV4Address); @@ -44,4 +53,4 @@ public class CachePolicyCompare [Benchmark] [BenchmarkCategory(nameof(CachePolicy.File))] public void FileIpV6() => _fileV6Searcher.Search(_testIpV6Address); -} \ No newline at end of file +} From 6684d0a928dfe0e7f29f02aa9760d3bda772f688 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Fri, 21 Nov 2025 12:24:32 +0800 Subject: [PATCH 27/37] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96=E7=BC=93?= =?UTF-8?q?=E5=AD=98=E7=AD=96=E7=95=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../IP2Region.Net/Internal/ContentCacheStrategy.cs | 14 +++++++------- .../Internal/VectorIndexCacheStrategy.cs | 12 +++++------- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/binding/csharp/IP2Region.Net/Internal/ContentCacheStrategy.cs b/binding/csharp/IP2Region.Net/Internal/ContentCacheStrategy.cs index 94e7f51..ad3e2ba 100644 --- a/binding/csharp/IP2Region.Net/Internal/ContentCacheStrategy.cs +++ b/binding/csharp/IP2Region.Net/Internal/ContentCacheStrategy.cs @@ -8,18 +8,18 @@ using IP2Region.Net.Internal.Abstractions; namespace IP2Region.Net.Internal; -class ContentCacheStrategy(string xdbPath) : AbstractCacheStrategy(xdbPath) +class ContentCacheStrategy : AbstractCacheStrategy { ReadOnlyMemory _cacheData = default; + public ContentCacheStrategy(string xdbPath) : base(xdbPath) + { + using var reader = base.GetXdbFileStream(); + _cacheData = base.GetData(0, (int)reader.Length); + } + public override ReadOnlyMemory GetData(int offset, int length) { - if (_cacheData.IsEmpty) - { - using var reader = base.GetXdbFileStream(); - _cacheData = base.GetData(0, (int)reader.Length); - } - return _cacheData.Slice(offset, length); } } diff --git a/binding/csharp/IP2Region.Net/Internal/VectorIndexCacheStrategy.cs b/binding/csharp/IP2Region.Net/Internal/VectorIndexCacheStrategy.cs index 4dea3fe..f34b1b2 100644 --- a/binding/csharp/IP2Region.Net/Internal/VectorIndexCacheStrategy.cs +++ b/binding/csharp/IP2Region.Net/Internal/VectorIndexCacheStrategy.cs @@ -8,16 +8,14 @@ using IP2Region.Net.Internal.Abstractions; namespace IP2Region.Net.Internal; -internal class VectorIndexCacheStrategy(string xdbPath) : AbstractCacheStrategy(xdbPath) +internal class VectorIndexCacheStrategy : AbstractCacheStrategy { ReadOnlyMemory _vectorCahe = default; - public override ReadOnlyMemory GetVectorIndexStartPos(int offset) + public VectorIndexCacheStrategy(string xdbPath) : base(xdbPath) { - if (_vectorCahe.IsEmpty) - { - _vectorCahe = base.GetData(256, 256 * 256 * 8); - } - return _vectorCahe.Slice(offset, 8); + _vectorCahe = base.GetData(256, 256 * 256 * 8); } + + public override ReadOnlyMemory GetVectorIndexStartPos(int offset) => _vectorCahe.Slice(offset, 8); } From 7bd81fecbf76eea37ed781c64b0262fee6bdf336 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Fri, 21 Nov 2025 12:24:49 +0800 Subject: [PATCH 28/37] =?UTF-8?q?chore:=20=E5=A2=9E=E5=8A=A0=20net10=20?= =?UTF-8?q?=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../csharp/IP2Region.Net/IP2Region.Net.csproj | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/binding/csharp/IP2Region.Net/IP2Region.Net.csproj b/binding/csharp/IP2Region.Net/IP2Region.Net.csproj index 380ec52..dc0e1ab 100644 --- a/binding/csharp/IP2Region.Net/IP2Region.Net.csproj +++ b/binding/csharp/IP2Region.Net/IP2Region.Net.csproj @@ -15,7 +15,7 @@ git enable enable - netstandard2.0;netstandard2.1;net6.0;net7.0;net8.0;net9.0 + netstandard2.0;netstandard2.1;net6.0;net7.0;net8.0;net9.0;net10.0 latest c2f07fe1-a300-4de3-8200-1278ed8cb5b7 @@ -30,30 +30,34 @@ - + - + - + - + - + - + - - + + + + + + CHANGELOG.md From edc2559489f3f5afb7ceab1842fcde3835f2fbf6 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Fri, 21 Nov 2025 12:25:13 +0800 Subject: [PATCH 29/37] =?UTF-8?q?test:=20=E6=9B=B4=E6=96=B0=E5=8D=95?= =?UTF-8?q?=E5=85=83=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- binding/csharp/IP2Region.Net.Test/SearcherTest.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/binding/csharp/IP2Region.Net.Test/SearcherTest.cs b/binding/csharp/IP2Region.Net.Test/SearcherTest.cs index 80a3358..b9b4e22 100644 --- a/binding/csharp/IP2Region.Net.Test/SearcherTest.cs +++ b/binding/csharp/IP2Region.Net.Test/SearcherTest.cs @@ -78,7 +78,7 @@ public class SearcherTest { var searcher = new Searcher(CachePolicy.Content, _xdbPathV4); searcher.Search("58.251.27.201"); - Assert.Equal(169, searcher.IoCount); + Assert.Equal(0, searcher.IoCount); searcher.Search("58.251.27.201"); Assert.Equal(0, searcher.IoCount); @@ -89,7 +89,7 @@ public class SearcherTest { var searcher = new Searcher(CachePolicy.VectorIndex, _xdbPathV4); searcher.Search("58.251.27.201"); - Assert.Equal(10, searcher.IoCount); + Assert.Equal(2, searcher.IoCount); searcher.Search("58.251.27.201"); Assert.Equal(2, searcher.IoCount); From 4ea2e7360da18a7f2d0ca79f56c152c7a48d2720 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Fri, 21 Nov 2025 12:25:24 +0800 Subject: [PATCH 30/37] =?UTF-8?q?chore:=20=E5=A2=9E=E5=8A=A0=E5=8E=8B?= =?UTF-8?q?=E5=8A=9B=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../IP2Region.Net.BenchMark/Benmarks.cs | 50 +++++++++++++++++ .../csharp/IP2Region.Net.BenchMark/Program.cs | 56 +------------------ 2 files changed, 52 insertions(+), 54 deletions(-) create mode 100644 binding/csharp/IP2Region.Net.BenchMark/Benmarks.cs diff --git a/binding/csharp/IP2Region.Net.BenchMark/Benmarks.cs b/binding/csharp/IP2Region.Net.BenchMark/Benmarks.cs new file mode 100644 index 0000000..66dcb85 --- /dev/null +++ b/binding/csharp/IP2Region.Net.BenchMark/Benmarks.cs @@ -0,0 +1,50 @@ +// Copyright 2023 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. +// @Author Alan +// @Date 2023/07/25 +// Updated by Argo Zhang at 2025/11/21 + +using BenchmarkDotNet.Attributes; +using IP2Region.Net.XDB; + +namespace IP2Region.Net.BenchMark; + +public class Benchmarks +{ + private static readonly string XdbPathV4 = Path.Combine(AppContext.BaseDirectory, "IP2Region", "ip2region_v4.xdb"); + private static readonly string XdbPathV6 = Path.Combine(AppContext.BaseDirectory, "IP2Region", "ip2region_v6.xdb"); + private static readonly Searcher _contentV4Searcher = new Searcher(CachePolicy.Content, XdbPathV4); + private static readonly Searcher _vectorV4Searcher = new Searcher(CachePolicy.VectorIndex, XdbPathV4); + private static readonly Searcher _fileV4Searcher = new Searcher(CachePolicy.File, XdbPathV4); + private static readonly Searcher _contentV6Searcher = new Searcher(CachePolicy.Content, XdbPathV6); + private static readonly Searcher _vectorV6Searcher = new Searcher(CachePolicy.VectorIndex, XdbPathV6); + private static readonly Searcher _fileV6Searcher = new Searcher(CachePolicy.File, XdbPathV6); + + private readonly string _testIpV4Address = "114.114.114.114"; + private readonly string _testIpV6Address = "240e:3b7:3272:d8d0:db09:c067:8d59:539e"; + + [Benchmark] + [BenchmarkCategory(nameof(CachePolicy.Content))] + public void ContentIpV4() => _contentV4Searcher.Search(_testIpV4Address); + + //[Benchmark] + [BenchmarkCategory(nameof(CachePolicy.VectorIndex))] + public void VectorIndexIpV4() => _vectorV4Searcher.Search(_testIpV4Address); + + [Benchmark] + [BenchmarkCategory(nameof(CachePolicy.File))] + public void FileIpV4() => _fileV4Searcher.Search(_testIpV4Address); + + [Benchmark] + [BenchmarkCategory(nameof(CachePolicy.Content))] + public void ContentIpV6() => _contentV6Searcher.Search(_testIpV6Address); + + [Benchmark] + [BenchmarkCategory(nameof(CachePolicy.VectorIndex))] + public void VectorIndexIpV6() => _vectorV6Searcher.Search(_testIpV6Address); + + [Benchmark] + [BenchmarkCategory(nameof(CachePolicy.File))] + public void FileIpV6() => _fileV6Searcher.Search(_testIpV6Address); +} diff --git a/binding/csharp/IP2Region.Net.BenchMark/Program.cs b/binding/csharp/IP2Region.Net.BenchMark/Program.cs index 9f7d0b0..5ca00ae 100644 --- a/binding/csharp/IP2Region.Net.BenchMark/Program.cs +++ b/binding/csharp/IP2Region.Net.BenchMark/Program.cs @@ -1,56 +1,4 @@ -using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Running; -using IP2Region.Net.XDB; +using IP2Region.Net.BenchMark; -BenchmarkRunner.Run(typeof(Program).Assembly); - -public class CachePolicyCompare -{ - private static readonly string XdbPathV4 = Path.Combine(AppContext.BaseDirectory, "IP2Region", "ip2region_v4.xdb"); - private static readonly string XdbPathV6 = Path.Combine(AppContext.BaseDirectory, "IP2Region", "ip2region_v6.xdb"); - private readonly Searcher _contentV4Searcher = new Searcher(CachePolicy.Content, XdbPathV4); - private readonly Searcher _vectorV4Searcher = new Searcher(CachePolicy.VectorIndex, XdbPathV4); - private readonly Searcher _fileV4Searcher = new Searcher(CachePolicy.File, XdbPathV4); - private readonly Searcher _contentV6Searcher = new Searcher(CachePolicy.Content, XdbPathV6); - private readonly Searcher _vectorV6Searcher = new Searcher(CachePolicy.VectorIndex, XdbPathV6); - private readonly Searcher _fileV6Searcher = new Searcher(CachePolicy.File, XdbPathV6); - - private readonly string _testIpV4Address = "114.114.114.114"; - private readonly string _testIpV6Address = "240e:3b7:3272:d8d0:db09:c067:8d59:539e"; - - public CachePolicyCompare() - { - _contentV4Searcher.Search(_testIpV4Address); - _vectorV4Searcher.Search(_testIpV4Address); - _fileV4Searcher.Search(_testIpV4Address); - _contentV6Searcher.Search(_testIpV6Address); - _vectorV6Searcher.Search(_testIpV6Address); - _fileV6Searcher.Search(_testIpV6Address); - } - - [Benchmark] - [BenchmarkCategory(nameof(CachePolicy.Content))] - public void ContentIpV4() => _contentV4Searcher.Search(_testIpV4Address); - - [Benchmark] - [BenchmarkCategory(nameof(CachePolicy.VectorIndex))] - public void VectorIndexIpV4() => _vectorV4Searcher.Search(_testIpV4Address); - - - [Benchmark] - [BenchmarkCategory(nameof(CachePolicy.File))] - public void FileIpV4() => _fileV4Searcher.Search(_testIpV4Address); - - [Benchmark] - [BenchmarkCategory(nameof(CachePolicy.Content))] - public void ContentIpV6() => _contentV6Searcher.Search(_testIpV6Address); - - [Benchmark] - [BenchmarkCategory(nameof(CachePolicy.VectorIndex))] - public void VectorIndexIpV6() => _vectorV6Searcher.Search(_testIpV6Address); - - - [Benchmark] - [BenchmarkCategory(nameof(CachePolicy.File))] - public void FileIpV6() => _fileV6Searcher.Search(_testIpV6Address); -} +BenchmarkRunner.Run(); From 9ecca28fc2acb796be4b0892c02fd38910d49d64 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Fri, 21 Nov 2025 12:37:47 +0800 Subject: [PATCH 31/37] =?UTF-8?q?refactor:=20=E6=9B=B4=E6=96=B0=E6=97=B6?= =?UTF-8?q?=E5=B7=AE=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- binding/csharp/IP2Region.Net/XDB/Util.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/binding/csharp/IP2Region.Net/XDB/Util.cs b/binding/csharp/IP2Region.Net/XDB/Util.cs index 5f8673c..0510f40 100644 --- a/binding/csharp/IP2Region.Net/XDB/Util.cs +++ b/binding/csharp/IP2Region.Net/XDB/Util.cs @@ -76,7 +76,7 @@ public static class Util }; var createdAt = BinaryPrimitives.ReadUInt32LittleEndian(buffer.Slice(4, 4)); - var dtm = new DateTimeOffset(1970, 1, 1, 0, 0, 0, TimeSpan.FromHours(8)); + var dtm = new DateTimeOffset(1970, 1, 1, 0, 0, 0, TimeSpan.FromHours(0)); ret.CreatedTime = dtm.AddSeconds(createdAt); return ret; From ad84da7d3cf75dce7e285d7f95d1f017dede263d Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Fri, 21 Nov 2025 12:38:10 +0800 Subject: [PATCH 32/37] =?UTF-8?q?test:=20=E6=9B=B4=E6=96=B0=E5=8D=95?= =?UTF-8?q?=E5=85=83=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- binding/csharp/IP2Region.Net.Test/XdbTest.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/binding/csharp/IP2Region.Net.Test/XdbTest.cs b/binding/csharp/IP2Region.Net.Test/XdbTest.cs index e7bec8d..2e4a75f 100644 --- a/binding/csharp/IP2Region.Net.Test/XdbTest.cs +++ b/binding/csharp/IP2Region.Net.Test/XdbTest.cs @@ -1,4 +1,4 @@ -using Xunit; +using Xunit; namespace IP2Region.Net.Test; @@ -36,7 +36,7 @@ public class XdbTest [Fact] public async Task GetVersionAsync_Error() { - await Assert.ThrowsAsync(async () => await XDB.Util.GetVersionAsync(null)); + await Assert.ThrowsAsync(async () => await XDB.Util.GetVersionAsync(null!)); await Assert.ThrowsAsync(async () => await XDB.Util.GetVersionAsync(Path.Combine(AppContext.BaseDirectory, "test.xdb"))); } } From 14d23324832437fe2a3990bf1312079425a198fb Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Fri, 21 Nov 2025 13:14:42 +0800 Subject: [PATCH 33/37] =?UTF-8?q?doc:=20=E6=9B=B4=E6=96=B0=E5=A4=B4?= =?UTF-8?q?=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- binding/csharp/.editorconfig | 3 +-- binding/csharp/IP2Region.Net.BenchMark/Benmarks.cs | 2 +- binding/csharp/IP2Region.Net.BenchMark/Program.cs | 7 +++++++ binding/csharp/IP2Region.Net.Test/SearcherTest.cs | 7 +++++++ binding/csharp/IP2Region.Net.Test/UtilTest.cs | 7 +++++++ binding/csharp/IP2Region.Net.Test/XdbTest.cs | 7 +++++++ binding/csharp/IP2Region.Net/Abstractions/ISearcher.cs | 3 ++- .../Extensions/ServiceCollectionExtensions.cs | 3 ++- .../Internal/Abstractions/AbstractCacheStrategy.cs | 3 ++- .../IP2Region.Net/Internal/CacheStrategyFactory.cs | 3 ++- .../IP2Region.Net/Internal/ContentCacheStrategy.cs | 5 +++-- .../csharp/IP2Region.Net/Internal/FileCacheStrategy.cs | 3 ++- .../IP2Region.Net/Internal/VectorIndexCacheStrategy.cs | 9 +++++---- binding/csharp/IP2Region.Net/XDB/CachePolicy.cs | 7 +++++++ binding/csharp/IP2Region.Net/XDB/Searcher.cs | 9 +++++---- binding/csharp/IP2Region.Net/XDB/Util.cs | 9 ++++++++- binding/csharp/IP2Region.Net/XDB/XdbVersion.cs | 7 +++++++ 17 files changed, 75 insertions(+), 19 deletions(-) diff --git a/binding/csharp/.editorconfig b/binding/csharp/.editorconfig index b53f6cb..9ff4bca 100644 --- a/binding/csharp/.editorconfig +++ b/binding/csharp/.editorconfig @@ -19,8 +19,7 @@ indent_size = 2 end_of_line = lf [*.cs] -dotnet_analyzer_diagnostic.category-Style.severity = none csharp_style_namespace_declarations = file_scoped:silent [*.cs] -file_header_template = Copyright 2023 The Ip2Region Authors. All rights reserved.\nUse of this source code is governed by a Apache2.0-style\nlicense that can be found in the LICENSE file.\n@Author Alan \n@Date 2023/07/25\nUpdated by Argo Zhang at 2025/11/21 +file_header_template = Copyright 2025 The Ip2Region Authors. All rights reserved.\nUse of this source code is governed by a Apache2.0-style\nlicense that can be found in the LICENSE file.\n@Author Alan \n@Date 2023/07/25\nUpdated by Argo Zhang at 2025/11/21 diff --git a/binding/csharp/IP2Region.Net.BenchMark/Benmarks.cs b/binding/csharp/IP2Region.Net.BenchMark/Benmarks.cs index 66dcb85..f5eb2fd 100644 --- a/binding/csharp/IP2Region.Net.BenchMark/Benmarks.cs +++ b/binding/csharp/IP2Region.Net.BenchMark/Benmarks.cs @@ -1,4 +1,4 @@ -// Copyright 2023 The Ip2Region Authors. All rights reserved. +// 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. // @Author Alan diff --git a/binding/csharp/IP2Region.Net.BenchMark/Program.cs b/binding/csharp/IP2Region.Net.BenchMark/Program.cs index 5ca00ae..49bae6f 100644 --- a/binding/csharp/IP2Region.Net.BenchMark/Program.cs +++ b/binding/csharp/IP2Region.Net.BenchMark/Program.cs @@ -1,3 +1,10 @@ +// 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. +// @Author Alan +// @Date 2023/07/25 +// Updated by Argo Zhang at 2025/11/21 + using BenchmarkDotNet.Running; using IP2Region.Net.BenchMark; diff --git a/binding/csharp/IP2Region.Net.Test/SearcherTest.cs b/binding/csharp/IP2Region.Net.Test/SearcherTest.cs index b9b4e22..ce10a11 100644 --- a/binding/csharp/IP2Region.Net.Test/SearcherTest.cs +++ b/binding/csharp/IP2Region.Net.Test/SearcherTest.cs @@ -1,3 +1,10 @@ +// 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. +// @Author Alan +// @Date 2023/07/25 +// Updated by Argo Zhang at 2025/11/21 + using IP2Region.Net.Abstractions; using IP2Region.Net.XDB; using Microsoft.Extensions.DependencyInjection; diff --git a/binding/csharp/IP2Region.Net.Test/UtilTest.cs b/binding/csharp/IP2Region.Net.Test/UtilTest.cs index 8d6eb3d..8a4d38b 100644 --- a/binding/csharp/IP2Region.Net.Test/UtilTest.cs +++ b/binding/csharp/IP2Region.Net.Test/UtilTest.cs @@ -1,3 +1,10 @@ +// 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. +// @Author Alan +// @Date 2023/07/25 +// Updated by Argo Zhang at 2025/11/21 + using Xunit; namespace IP2Region.Net.Test; diff --git a/binding/csharp/IP2Region.Net.Test/XdbTest.cs b/binding/csharp/IP2Region.Net.Test/XdbTest.cs index 2e4a75f..8b92da8 100644 --- a/binding/csharp/IP2Region.Net.Test/XdbTest.cs +++ b/binding/csharp/IP2Region.Net.Test/XdbTest.cs @@ -1,3 +1,10 @@ +// 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. +// @Author Alan +// @Date 2023/07/25 +// Updated by Argo Zhang at 2025/11/21 + using Xunit; namespace IP2Region.Net.Test; diff --git a/binding/csharp/IP2Region.Net/Abstractions/ISearcher.cs b/binding/csharp/IP2Region.Net/Abstractions/ISearcher.cs index d270e4e..89cd5e5 100644 --- a/binding/csharp/IP2Region.Net/Abstractions/ISearcher.cs +++ b/binding/csharp/IP2Region.Net/Abstractions/ISearcher.cs @@ -1,8 +1,9 @@ -// Copyright 2023 The Ip2Region Authors. All rights reserved. +// 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. // @Author Alan // @Date 2023/07/25 +// Updated by Argo Zhang at 2025/11/21 using System.Net; diff --git a/binding/csharp/IP2Region.Net/Extensions/ServiceCollectionExtensions.cs b/binding/csharp/IP2Region.Net/Extensions/ServiceCollectionExtensions.cs index ace55ad..16fbf05 100644 --- a/binding/csharp/IP2Region.Net/Extensions/ServiceCollectionExtensions.cs +++ b/binding/csharp/IP2Region.Net/Extensions/ServiceCollectionExtensions.cs @@ -1,8 +1,9 @@ -// Copyright 2023 The Ip2Region Authors. All rights reserved. +// 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. // @Author Alan // @Date 2023/07/25 +// Updated by Argo Zhang at 2025/11/21 using IP2Region.Net.Abstractions; using IP2Region.Net.XDB; diff --git a/binding/csharp/IP2Region.Net/Internal/Abstractions/AbstractCacheStrategy.cs b/binding/csharp/IP2Region.Net/Internal/Abstractions/AbstractCacheStrategy.cs index e277dde..a716382 100644 --- a/binding/csharp/IP2Region.Net/Internal/Abstractions/AbstractCacheStrategy.cs +++ b/binding/csharp/IP2Region.Net/Internal/Abstractions/AbstractCacheStrategy.cs @@ -1,8 +1,9 @@ -// Copyright 2023 The Ip2Region Authors. All rights reserved. +// 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. // @Author Alan // @Date 2023/07/25 +// Updated by Argo Zhang at 2025/11/21 using System.Buffers; diff --git a/binding/csharp/IP2Region.Net/Internal/CacheStrategyFactory.cs b/binding/csharp/IP2Region.Net/Internal/CacheStrategyFactory.cs index 0deb198..e71f5bd 100644 --- a/binding/csharp/IP2Region.Net/Internal/CacheStrategyFactory.cs +++ b/binding/csharp/IP2Region.Net/Internal/CacheStrategyFactory.cs @@ -1,8 +1,9 @@ -// Copyright 2023 The Ip2Region Authors. All rights reserved. +// 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. // @Author Alan // @Date 2023/07/25 +// Updated by Argo Zhang at 2025/11/21 using IP2Region.Net.Internal.Abstractions; using IP2Region.Net.XDB; diff --git a/binding/csharp/IP2Region.Net/Internal/ContentCacheStrategy.cs b/binding/csharp/IP2Region.Net/Internal/ContentCacheStrategy.cs index ad3e2ba..fad002b 100644 --- a/binding/csharp/IP2Region.Net/Internal/ContentCacheStrategy.cs +++ b/binding/csharp/IP2Region.Net/Internal/ContentCacheStrategy.cs @@ -1,8 +1,9 @@ -// Copyright 2023 The Ip2Region Authors. All rights reserved. +// 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. // @Author Alan // @Date 2023/07/25 +// Updated by Argo Zhang at 2025/11/21 using IP2Region.Net.Internal.Abstractions; @@ -10,7 +11,7 @@ namespace IP2Region.Net.Internal; class ContentCacheStrategy : AbstractCacheStrategy { - ReadOnlyMemory _cacheData = default; + readonly ReadOnlyMemory _cacheData = default; public ContentCacheStrategy(string xdbPath) : base(xdbPath) { diff --git a/binding/csharp/IP2Region.Net/Internal/FileCacheStrategy.cs b/binding/csharp/IP2Region.Net/Internal/FileCacheStrategy.cs index 3a597b0..2a69364 100644 --- a/binding/csharp/IP2Region.Net/Internal/FileCacheStrategy.cs +++ b/binding/csharp/IP2Region.Net/Internal/FileCacheStrategy.cs @@ -1,8 +1,9 @@ -// Copyright 2023 The Ip2Region Authors. All rights reserved. +// 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. // @Author Alan // @Date 2023/07/25 +// Updated by Argo Zhang at 2025/11/21 using IP2Region.Net.Internal.Abstractions; diff --git a/binding/csharp/IP2Region.Net/Internal/VectorIndexCacheStrategy.cs b/binding/csharp/IP2Region.Net/Internal/VectorIndexCacheStrategy.cs index f34b1b2..89ff83e 100644 --- a/binding/csharp/IP2Region.Net/Internal/VectorIndexCacheStrategy.cs +++ b/binding/csharp/IP2Region.Net/Internal/VectorIndexCacheStrategy.cs @@ -1,8 +1,9 @@ -// Copyright 2023 The Ip2Region Authors. All rights reserved. +// 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. // @Author Alan // @Date 2023/07/25 +// Updated by Argo Zhang at 2025/11/21 using IP2Region.Net.Internal.Abstractions; @@ -10,12 +11,12 @@ namespace IP2Region.Net.Internal; internal class VectorIndexCacheStrategy : AbstractCacheStrategy { - ReadOnlyMemory _vectorCahe = default; + readonly ReadOnlyMemory _vectorCache = default; public VectorIndexCacheStrategy(string xdbPath) : base(xdbPath) { - _vectorCahe = base.GetData(256, 256 * 256 * 8); + _vectorCache = base.GetData(256, 256 * 256 * 8); } - public override ReadOnlyMemory GetVectorIndexStartPos(int offset) => _vectorCahe.Slice(offset, 8); + public override ReadOnlyMemory GetVectorIndexStartPos(int offset) => _vectorCache.Slice(offset, 8); } diff --git a/binding/csharp/IP2Region.Net/XDB/CachePolicy.cs b/binding/csharp/IP2Region.Net/XDB/CachePolicy.cs index 9397ee0..59b6b29 100644 --- a/binding/csharp/IP2Region.Net/XDB/CachePolicy.cs +++ b/binding/csharp/IP2Region.Net/XDB/CachePolicy.cs @@ -1,3 +1,10 @@ +// 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. +// @Author Alan +// @Date 2023/07/25 +// Updated by Argo Zhang at 2025/11/21 + namespace IP2Region.Net.XDB; /// diff --git a/binding/csharp/IP2Region.Net/XDB/Searcher.cs b/binding/csharp/IP2Region.Net/XDB/Searcher.cs index 1aa7fc6..adb30d6 100644 --- a/binding/csharp/IP2Region.Net/XDB/Searcher.cs +++ b/binding/csharp/IP2Region.Net/XDB/Searcher.cs @@ -1,8 +1,9 @@ -// Copyright 2023 The Ip2Region Authors. All rights reserved. +// 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. -// @Author Alan Lee -// @Date 2023/07/23 +// @Author Alan +// @Date 2023/07/25 +// Updated by Argo Zhang at 2025/11/21 using IP2Region.Net.Abstractions; using IP2Region.Net.Internal; @@ -57,7 +58,7 @@ public class Searcher : ISearcher public string? Search(uint ipAddress) { var bytes = BitConverter.GetBytes(ipAddress); - bytes.Reverse(); + Array.Reverse(bytes); return SearchCore(bytes); } diff --git a/binding/csharp/IP2Region.Net/XDB/Util.cs b/binding/csharp/IP2Region.Net/XDB/Util.cs index 0510f40..54afc7a 100644 --- a/binding/csharp/IP2Region.Net/XDB/Util.cs +++ b/binding/csharp/IP2Region.Net/XDB/Util.cs @@ -1,3 +1,10 @@ +// 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. +// @Author Alan +// @Date 2023/07/25 +// Updated by Argo Zhang at 2025/11/21 + using System.Buffers; using System.Buffers.Binary; using System.Net; @@ -35,7 +42,7 @@ public static class Util if (!File.Exists(dbPath)) { - throw new FileNotFoundException("xdb file not fould.", dbPath); + throw new FileNotFoundException("xdb file not found.", dbPath); } using var reader = File.OpenRead(dbPath); diff --git a/binding/csharp/IP2Region.Net/XDB/XdbVersion.cs b/binding/csharp/IP2Region.Net/XDB/XdbVersion.cs index e93e9db..5630cf8 100644 --- a/binding/csharp/IP2Region.Net/XDB/XdbVersion.cs +++ b/binding/csharp/IP2Region.Net/XDB/XdbVersion.cs @@ -1,3 +1,10 @@ +// 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. +// @Author Alan +// @Date 2023/07/25 +// Updated by Argo Zhang at 2025/11/21 + namespace IP2Region.Net.XDB; /// From 426038b3fea89c028e86798ef2826d2873ff35e2 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Fri, 21 Nov 2025 15:46:09 +0800 Subject: [PATCH 34/37] =?UTF-8?q?refactor:=20=E9=87=8D=E6=9E=84=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../IP2Region.Net.BenchMark/Benmarks.cs | 44 +++++++++---------- .../csharp/IP2Region.Net.Test/SearcherTest.cs | 22 +++++----- .../Abstractions/AbstractCacheStrategy.cs | 32 ++++++-------- .../Internal/CacheStrategyFactory.cs | 2 +- .../Internal/ContentCacheStrategy.cs | 10 ++--- .../Internal/VectorIndexCacheStrategy.cs | 11 +++-- binding/csharp/IP2Region.Net/XDB/Searcher.cs | 11 ++--- 7 files changed, 63 insertions(+), 69 deletions(-) diff --git a/binding/csharp/IP2Region.Net.BenchMark/Benmarks.cs b/binding/csharp/IP2Region.Net.BenchMark/Benmarks.cs index f5eb2fd..eabf368 100644 --- a/binding/csharp/IP2Region.Net.BenchMark/Benmarks.cs +++ b/binding/csharp/IP2Region.Net.BenchMark/Benmarks.cs @@ -14,37 +14,37 @@ public class Benchmarks { private static readonly string XdbPathV4 = Path.Combine(AppContext.BaseDirectory, "IP2Region", "ip2region_v4.xdb"); private static readonly string XdbPathV6 = Path.Combine(AppContext.BaseDirectory, "IP2Region", "ip2region_v6.xdb"); - private static readonly Searcher _contentV4Searcher = new Searcher(CachePolicy.Content, XdbPathV4); - private static readonly Searcher _vectorV4Searcher = new Searcher(CachePolicy.VectorIndex, XdbPathV4); - private static readonly Searcher _fileV4Searcher = new Searcher(CachePolicy.File, XdbPathV4); - private static readonly Searcher _contentV6Searcher = new Searcher(CachePolicy.Content, XdbPathV6); - private static readonly Searcher _vectorV6Searcher = new Searcher(CachePolicy.VectorIndex, XdbPathV6); - private static readonly Searcher _fileV6Searcher = new Searcher(CachePolicy.File, XdbPathV6); + private static readonly Searcher _contentV4Searcher = new(CachePolicy.Content, XdbPathV4); + private static readonly Searcher _vectorV4Searcher = new(CachePolicy.VectorIndex, XdbPathV4); + private static readonly Searcher _fileV4Searcher = new(CachePolicy.File, XdbPathV4); + private static readonly Searcher _contentV6Searcher = new(CachePolicy.Content, XdbPathV6); + private static readonly Searcher _vectorV6Searcher = new(CachePolicy.VectorIndex, XdbPathV6); + private static readonly Searcher _fileV6Searcher = new(CachePolicy.File, XdbPathV6); - private readonly string _testIpV4Address = "114.114.114.114"; - private readonly string _testIpV6Address = "240e:3b7:3272:d8d0:db09:c067:8d59:539e"; + private readonly string _testIPv4Address = "114.114.114.114"; + private readonly string _testIPv6Address = "240e:3b7:3272:d8d0:db09:c067:8d59:539e"; [Benchmark] - [BenchmarkCategory(nameof(CachePolicy.Content))] - public void ContentIpV4() => _contentV4Searcher.Search(_testIpV4Address); - - //[Benchmark] - [BenchmarkCategory(nameof(CachePolicy.VectorIndex))] - public void VectorIndexIpV4() => _vectorV4Searcher.Search(_testIpV4Address); + [BenchmarkCategory("IPv4")] + public void ContentIPv4() => _contentV4Searcher.Search(_testIPv4Address); [Benchmark] - [BenchmarkCategory(nameof(CachePolicy.File))] - public void FileIpV4() => _fileV4Searcher.Search(_testIpV4Address); + [BenchmarkCategory("IPv4")] + public void VectorIPv4() => _vectorV4Searcher.Search(_testIPv4Address); [Benchmark] - [BenchmarkCategory(nameof(CachePolicy.Content))] - public void ContentIpV6() => _contentV6Searcher.Search(_testIpV6Address); + [BenchmarkCategory("IPv4")] + public void FileIPv4() => _fileV4Searcher.Search(_testIPv4Address); [Benchmark] - [BenchmarkCategory(nameof(CachePolicy.VectorIndex))] - public void VectorIndexIpV6() => _vectorV6Searcher.Search(_testIpV6Address); + [BenchmarkCategory("IPv6")] + public void ContentIPv6() => _contentV6Searcher.Search(_testIPv6Address); [Benchmark] - [BenchmarkCategory(nameof(CachePolicy.File))] - public void FileIpV6() => _fileV6Searcher.Search(_testIpV6Address); + [BenchmarkCategory("IPv6")] + public void VectorIPv6() => _vectorV6Searcher.Search(_testIPv6Address); + + [Benchmark] + [BenchmarkCategory("IPv6")] + public void FileIPv6() => _fileV6Searcher.Search(_testIPv6Address); } diff --git a/binding/csharp/IP2Region.Net.Test/SearcherTest.cs b/binding/csharp/IP2Region.Net.Test/SearcherTest.cs index ce10a11..da9f840 100644 --- a/binding/csharp/IP2Region.Net.Test/SearcherTest.cs +++ b/binding/csharp/IP2Region.Net.Test/SearcherTest.cs @@ -80,17 +80,6 @@ public class SearcherTest Assert.Equal(3, searcher.IoCount); } - [Fact] - public void IoCount_Content_Ok() - { - var searcher = new Searcher(CachePolicy.Content, _xdbPathV4); - searcher.Search("58.251.27.201"); - Assert.Equal(0, searcher.IoCount); - - searcher.Search("58.251.27.201"); - Assert.Equal(0, searcher.IoCount); - } - [Fact] public void IoCount_Vector_Ok() { @@ -102,6 +91,17 @@ public class SearcherTest Assert.Equal(2, searcher.IoCount); } + [Fact] + public void IoCount_Content_Ok() + { + var searcher = new Searcher(CachePolicy.Content, _xdbPathV4); + searcher.Search("58.251.27.201"); + Assert.Equal(0, searcher.IoCount); + + searcher.Search("58.251.27.201"); + Assert.Equal(0, searcher.IoCount); + } + [Theory] [InlineData("58.251.255.255", "中国|广东省|深圳市|联通")] public void Search_Ip_Ok(string ipStr, string expected) diff --git a/binding/csharp/IP2Region.Net/Internal/Abstractions/AbstractCacheStrategy.cs b/binding/csharp/IP2Region.Net/Internal/Abstractions/AbstractCacheStrategy.cs index a716382..d94524b 100644 --- a/binding/csharp/IP2Region.Net/Internal/Abstractions/AbstractCacheStrategy.cs +++ b/binding/csharp/IP2Region.Net/Internal/Abstractions/AbstractCacheStrategy.cs @@ -11,7 +11,11 @@ namespace IP2Region.Net.Internal.Abstractions; internal abstract class AbstractCacheStrategy(string xdbPath) { + protected const int HeaderInfoLength = 256; + protected const int VectorIndexSize = 8; + private const int BufferSize = 64 * 1024; + private readonly FileStream _xdbFileStream = new(xdbPath, FileMode.Open, FileAccess.Read, FileShare.Read, BufferSize, FileOptions.RandomAccess); public int IoCount { get; private set; } @@ -20,25 +24,26 @@ internal abstract class AbstractCacheStrategy(string xdbPath) IoCount = 0; } - public virtual ReadOnlyMemory GetVectorIndexStartPos(int offset) - { - return GetData(256 + offset, 8); - } + public virtual ReadOnlyMemory GetVectorIndex(int offset) => GetData(HeaderInfoLength + offset, VectorIndexSize); - public virtual ReadOnlyMemory GetData(int offset, int length) + public virtual ReadOnlyMemory GetData(int offset = 0, int length = 0) { + if (length == 0) + { + length = (int)_xdbFileStream.Length; + } + byte[] buffer = ArrayPool.Shared.Rent(length); int totalBytesRead = 0; try { - var stream = GetXdbFileStream(); - stream.Seek(offset, SeekOrigin.Begin); + _xdbFileStream.Seek(offset, SeekOrigin.Begin); int bytesRead; while (totalBytesRead < length) { int bytesToRead = Math.Min(BufferSize, length - totalBytesRead); - bytesRead = stream.Read(buffer, totalBytesRead, bytesToRead); + bytesRead = _xdbFileStream.Read(buffer, totalBytesRead, bytesToRead); totalBytesRead += bytesRead; IoCount++; @@ -51,15 +56,4 @@ internal abstract class AbstractCacheStrategy(string xdbPath) return new ReadOnlyMemory(buffer, 0, totalBytesRead); } - - FileStream? _xdbFileStream; - - protected FileStream GetXdbFileStream() - { - if (_xdbFileStream == null) - { - _xdbFileStream = new FileStream(xdbPath, FileMode.Open, FileAccess.Read, FileShare.Read, BufferSize, FileOptions.RandomAccess); - } - return _xdbFileStream; - } } diff --git a/binding/csharp/IP2Region.Net/Internal/CacheStrategyFactory.cs b/binding/csharp/IP2Region.Net/Internal/CacheStrategyFactory.cs index e71f5bd..084a3d0 100644 --- a/binding/csharp/IP2Region.Net/Internal/CacheStrategyFactory.cs +++ b/binding/csharp/IP2Region.Net/Internal/CacheStrategyFactory.cs @@ -10,7 +10,7 @@ using IP2Region.Net.XDB; namespace IP2Region.Net.Internal; -internal class CacheStrategyFactory(string xdbPath) +class CacheStrategyFactory(string xdbPath) { public AbstractCacheStrategy CreateCacheStrategy(CachePolicy cachePolicy) => cachePolicy switch { diff --git a/binding/csharp/IP2Region.Net/Internal/ContentCacheStrategy.cs b/binding/csharp/IP2Region.Net/Internal/ContentCacheStrategy.cs index fad002b..cc88359 100644 --- a/binding/csharp/IP2Region.Net/Internal/ContentCacheStrategy.cs +++ b/binding/csharp/IP2Region.Net/Internal/ContentCacheStrategy.cs @@ -11,16 +11,12 @@ namespace IP2Region.Net.Internal; class ContentCacheStrategy : AbstractCacheStrategy { - readonly ReadOnlyMemory _cacheData = default; + private readonly ReadOnlyMemory _cacheData; public ContentCacheStrategy(string xdbPath) : base(xdbPath) { - using var reader = base.GetXdbFileStream(); - _cacheData = base.GetData(0, (int)reader.Length); + _cacheData = base.GetData(); } - public override ReadOnlyMemory GetData(int offset, int length) - { - return _cacheData.Slice(offset, length); - } + public override ReadOnlyMemory GetData(int offset = 0, int length = 0) => _cacheData.Slice(offset, length); } diff --git a/binding/csharp/IP2Region.Net/Internal/VectorIndexCacheStrategy.cs b/binding/csharp/IP2Region.Net/Internal/VectorIndexCacheStrategy.cs index 89ff83e..8ea0b01 100644 --- a/binding/csharp/IP2Region.Net/Internal/VectorIndexCacheStrategy.cs +++ b/binding/csharp/IP2Region.Net/Internal/VectorIndexCacheStrategy.cs @@ -9,14 +9,17 @@ using IP2Region.Net.Internal.Abstractions; namespace IP2Region.Net.Internal; -internal class VectorIndexCacheStrategy : AbstractCacheStrategy +class VectorIndexCacheStrategy : AbstractCacheStrategy { - readonly ReadOnlyMemory _vectorCache = default; + private const int VectorIndexRows = 256; + private const int VectorIndexCols = 256; + + private readonly ReadOnlyMemory _vectorCache; public VectorIndexCacheStrategy(string xdbPath) : base(xdbPath) { - _vectorCache = base.GetData(256, 256 * 256 * 8); + _vectorCache = GetData(HeaderInfoLength, VectorIndexRows * VectorIndexCols * VectorIndexSize); } - public override ReadOnlyMemory GetVectorIndexStartPos(int offset) => _vectorCache.Slice(offset, 8); + public override ReadOnlyMemory GetVectorIndex(int offset) => _vectorCache.Slice(offset, VectorIndexSize); } diff --git a/binding/csharp/IP2Region.Net/XDB/Searcher.cs b/binding/csharp/IP2Region.Net/XDB/Searcher.cs index adb30d6..2f0eadb 100644 --- a/binding/csharp/IP2Region.Net/XDB/Searcher.cs +++ b/binding/csharp/IP2Region.Net/XDB/Searcher.cs @@ -78,9 +78,9 @@ public class Searcher : ISearcher var il1 = ipBytes[1]; var idx = il0 * vectorIndexCols * vectorIndexSize + il1 * vectorIndexSize; - var data = _cacheStrategy.GetVectorIndexStartPos(idx); - var sPtr = BinaryPrimitives.ReadUInt32LittleEndian(data.Span); - var ePtr = BinaryPrimitives.ReadUInt32LittleEndian(data.Span.Slice(4)); + var vector = _cacheStrategy.GetVectorIndex(idx); + var sPtr = BinaryPrimitives.ReadUInt32LittleEndian(vector.Span); + var ePtr = BinaryPrimitives.ReadUInt32LittleEndian(vector.Span.Slice(4)); var length = ipBytes.Length; var indexSize = length * 2 + 6; @@ -125,11 +125,12 @@ public class Searcher : ISearcher var ret = 0; for (int i = 0; i < ip1.Length; i++) { - if (ip1[i] < ip2[ip1.Length - 1 - i]) + var ip2Index = ip1.Length - 1 - i; + if (ip1[i] < ip2[ip2Index]) { return -1; } - else if (ip1[i] > ip2[ip1.Length - 1 - i]) + else if (ip1[i] > ip2[ip2Index]) { return 1; } From 85144e1c4030838d9c7e00cdbf40bd1b24f8ff53 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Fri, 21 Nov 2025 17:05:13 +0800 Subject: [PATCH 35/37] =?UTF-8?q?refactor:=20=E4=BB=A3=E7=A0=81=E9=87=8D?= =?UTF-8?q?=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- binding/csharp/IP2Region.Net/IP2Region.Net.csproj | 2 +- .../Abstractions/AbstractCacheStrategy.cs | 15 +++++---------- .../Internal/ContentCacheStrategy.cs | 6 ++++-- .../Internal/VectorIndexCacheStrategy.cs | 5 ++++- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/binding/csharp/IP2Region.Net/IP2Region.Net.csproj b/binding/csharp/IP2Region.Net/IP2Region.Net.csproj index dc0e1ab..83901fe 100644 --- a/binding/csharp/IP2Region.Net/IP2Region.Net.csproj +++ b/binding/csharp/IP2Region.Net/IP2Region.Net.csproj @@ -2,7 +2,7 @@ IP2Region.Net - 3.0.0 + 2.1.1 IP2Region.Net Alan Lee Apache-2.0 diff --git a/binding/csharp/IP2Region.Net/Internal/Abstractions/AbstractCacheStrategy.cs b/binding/csharp/IP2Region.Net/Internal/Abstractions/AbstractCacheStrategy.cs index d94524b..909f75c 100644 --- a/binding/csharp/IP2Region.Net/Internal/Abstractions/AbstractCacheStrategy.cs +++ b/binding/csharp/IP2Region.Net/Internal/Abstractions/AbstractCacheStrategy.cs @@ -15,10 +15,11 @@ internal abstract class AbstractCacheStrategy(string xdbPath) protected const int VectorIndexSize = 8; private const int BufferSize = 64 * 1024; - private readonly FileStream _xdbFileStream = new(xdbPath, FileMode.Open, FileAccess.Read, FileShare.Read, BufferSize, FileOptions.RandomAccess); public int IoCount { get; private set; } + protected FileStream XdbFileStream = new(xdbPath, FileMode.Open, FileAccess.Read, FileShare.Read, BufferSize, FileOptions.RandomAccess); + public void ResetIoCount() { IoCount = 0; @@ -26,24 +27,18 @@ internal abstract class AbstractCacheStrategy(string xdbPath) public virtual ReadOnlyMemory GetVectorIndex(int offset) => GetData(HeaderInfoLength + offset, VectorIndexSize); - public virtual ReadOnlyMemory GetData(int offset = 0, int length = 0) + public virtual ReadOnlyMemory GetData(long offset, int length) { - if (length == 0) - { - length = (int)_xdbFileStream.Length; - } - byte[] buffer = ArrayPool.Shared.Rent(length); int totalBytesRead = 0; try { - _xdbFileStream.Seek(offset, SeekOrigin.Begin); + XdbFileStream.Seek(offset, SeekOrigin.Begin); int bytesRead; while (totalBytesRead < length) { - int bytesToRead = Math.Min(BufferSize, length - totalBytesRead); - bytesRead = _xdbFileStream.Read(buffer, totalBytesRead, bytesToRead); + bytesRead = XdbFileStream.Read(buffer, totalBytesRead, length); totalBytesRead += bytesRead; IoCount++; diff --git a/binding/csharp/IP2Region.Net/Internal/ContentCacheStrategy.cs b/binding/csharp/IP2Region.Net/Internal/ContentCacheStrategy.cs index cc88359..135751d 100644 --- a/binding/csharp/IP2Region.Net/Internal/ContentCacheStrategy.cs +++ b/binding/csharp/IP2Region.Net/Internal/ContentCacheStrategy.cs @@ -15,8 +15,10 @@ class ContentCacheStrategy : AbstractCacheStrategy public ContentCacheStrategy(string xdbPath) : base(xdbPath) { - _cacheData = base.GetData(); + _cacheData = base.GetData(0, (int)XdbFileStream.Length); + XdbFileStream.Close(); + XdbFileStream.Dispose(); } - public override ReadOnlyMemory GetData(int offset = 0, int length = 0) => _cacheData.Slice(offset, length); + public override ReadOnlyMemory GetData(long offset = 0, int length = 0) => _cacheData.Slice((int)offset, length); } diff --git a/binding/csharp/IP2Region.Net/Internal/VectorIndexCacheStrategy.cs b/binding/csharp/IP2Region.Net/Internal/VectorIndexCacheStrategy.cs index 8ea0b01..b5abc17 100644 --- a/binding/csharp/IP2Region.Net/Internal/VectorIndexCacheStrategy.cs +++ b/binding/csharp/IP2Region.Net/Internal/VectorIndexCacheStrategy.cs @@ -18,7 +18,10 @@ class VectorIndexCacheStrategy : AbstractCacheStrategy public VectorIndexCacheStrategy(string xdbPath) : base(xdbPath) { - _vectorCache = GetData(HeaderInfoLength, VectorIndexRows * VectorIndexCols * VectorIndexSize); + XdbFileStream.Seek(HeaderInfoLength, SeekOrigin.Begin); + var buffer = new byte[VectorIndexRows * VectorIndexCols * VectorIndexSize]; + var length = XdbFileStream.Read(buffer, 0, buffer.Length); + _vectorCache = new ReadOnlyMemory(buffer); } public override ReadOnlyMemory GetVectorIndex(int offset) => _vectorCache.Slice(offset, VectorIndexSize); From 2c9ba9c56c105577f312fd3d376ada5359b8f92a Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Fri, 21 Nov 2025 17:05:22 +0800 Subject: [PATCH 36/37] =?UTF-8?q?doc:=20=E5=A2=9E=E5=8A=A0=E5=9F=BA?= =?UTF-8?q?=E5=87=86=E6=B5=8B=E8=AF=95=E7=BB=93=E6=9E=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- binding/csharp/README.md | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/binding/csharp/README.md b/binding/csharp/README.md index f203c57..797c2d6 100644 --- a/binding/csharp/README.md +++ b/binding/csharp/README.md @@ -45,18 +45,14 @@ provider.GetRequiredKeyedService("IP2Region.Net"); ## Performance -``` ini -BenchmarkDotNet=v0.13.2, OS=macOS 13.4.1 (c) (22F770820d) [Darwin 22.5.0] -Apple M1, 1 CPU, 8 logical and 8 physical cores -.NET SDK=7.0.306 - [Host] : .NET 6.0.20 (6.0.2023.32017), Arm64 RyuJIT AdvSIMD - DefaultJob : .NET 6.0.20 (6.0.2023.32017), Arm64 RyuJIT AdvSIMD -``` -| Method | Mean | Error | StdDev | -|------------------------ |-------------:|----------:|----------:| -| CachePolicy_Content | 58.32 ns | 0.182 ns | 0.170 ns | -| CachePolicy_File | 16,417.56 ns | 50.569 ns | 47.302 ns | -| CachePolicy_VectorIndex | 9,348.11 ns | 38.492 ns | 65.363 ns | +| Method | Mean | Error | StdDev | Median | +|------------ |------------:|----------:|------------:|------------:| +| ContentIPv4 | 101.5 ns | 2.04 ns | 4.57 ns | 103.1 ns | +| VectorIPv4 | 7,488.1 ns | 222.91 ns | 657.26 ns | 7,819.2 ns | +| FileIPv4 | 11,686.9 ns | 59.81 ns | 55.95 ns | 11,707.6 ns | +| ContentIPv6 | 296.1 ns | 1.84 ns | 1.72 ns | 296.2 ns | +| VectorIPv6 | 15,025.1 ns | 938.17 ns | 2,766.21 ns | 16,642.9 ns | +| FileIPv6 | 19,721.0 ns | 807.55 ns | 2,381.08 ns | 20,905.7 ns | ## Contributing Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change. @@ -64,4 +60,4 @@ Pull requests are welcome. For major changes, please open an issue first to disc Please make sure to update tests as appropriate. ## License -[Apache License 2.0](https://github.com/lionsoul2014/ip2region/blob/master/LICENSE.md) \ No newline at end of file +[Apache License 2.0](https://github.com/lionsoul2014/ip2region/blob/master/LICENSE.md) From d1151accdea066149686a9c22551f33b04779dc1 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Fri, 21 Nov 2025 17:09:31 +0800 Subject: [PATCH 37/37] =?UTF-8?q?doc:=20=E5=A2=9E=E5=8A=A0=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E6=A1=86=E6=9E=B6=E5=88=97=E8=A1=A8=E6=96=87=E6=A1=A3?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=20NET10=20=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- binding/csharp/README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/binding/csharp/README.md b/binding/csharp/README.md index 797c2d6..5f1db40 100644 --- a/binding/csharp/README.md +++ b/binding/csharp/README.md @@ -43,6 +43,9 @@ NET8+ support keyed service provider.GetRequiredKeyedService("IP2Region.Net"); ``` +## TargetFrameworks +netstandard2.0;netstandard2.1;net6.0;net7.0;net8.0;net9.0;net10.0 + ## Performance | Method | Mean | Error | StdDev | Median |