Merge pull request #401 from ArgoZhang/dev-net
fix(NET): refactor GetData method
This commit is contained in:
commit
096d68ea77
|
|
@ -2,7 +2,10 @@
|
||||||
|
|
||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
## [Unreleased]
|
## [3.0.0] - 2025-11-22
|
||||||
|
- 支持 .NET 10.0
|
||||||
|
- 增加 IPv6 支持
|
||||||
|
- 修复若干 bug
|
||||||
|
|
||||||
## [2.0.1] - 2023-07-30
|
## [2.0.1] - 2023-07-30
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ using IP2Region.Net.XDB;
|
||||||
|
|
||||||
namespace IP2Region.Net.BenchMark;
|
namespace IP2Region.Net.BenchMark;
|
||||||
|
|
||||||
|
[MemoryDiagnoser]
|
||||||
public class Benchmarks
|
public class Benchmarks
|
||||||
{
|
{
|
||||||
private static readonly string XdbPathV4 = Path.Combine(AppContext.BaseDirectory, "IP2Region", "ip2region_v4.xdb");
|
private static readonly string XdbPathV4 = Path.Combine(AppContext.BaseDirectory, "IP2Region", "ip2region_v4.xdb");
|
||||||
|
|
@ -24,6 +25,17 @@ public class Benchmarks
|
||||||
private readonly string _testIPv4Address = "114.114.114.114";
|
private readonly string _testIPv4Address = "114.114.114.114";
|
||||||
private readonly string _testIPv6Address = "240e:3b7:3272:d8d0:db09:c067:8d59:539e";
|
private readonly string _testIPv6Address = "240e:3b7:3272:d8d0:db09:c067:8d59:539e";
|
||||||
|
|
||||||
|
public Benchmarks()
|
||||||
|
{
|
||||||
|
_contentV4Searcher.Search(_testIPv4Address);
|
||||||
|
_vectorV4Searcher.Search(_testIPv4Address);
|
||||||
|
_fileV4Searcher.Search(_testIPv4Address);
|
||||||
|
|
||||||
|
_contentV6Searcher.Search(_testIPv6Address);
|
||||||
|
_vectorV6Searcher.Search(_testIPv6Address);
|
||||||
|
_fileV6Searcher.Search(_testIPv6Address);
|
||||||
|
}
|
||||||
|
|
||||||
[Benchmark]
|
[Benchmark]
|
||||||
[BenchmarkCategory("IPv4")]
|
[BenchmarkCategory("IPv4")]
|
||||||
public void ContentIPv4() => _contentV4Searcher.Search(_testIPv4Address);
|
public void ContentIPv4() => _contentV4Searcher.Search(_testIPv4Address);
|
||||||
|
|
|
||||||
|
|
@ -78,6 +78,8 @@ public class SearcherTest
|
||||||
|
|
||||||
searcher.Search("58.251.27.201");
|
searcher.Search("58.251.27.201");
|
||||||
Assert.Equal(3, searcher.IoCount);
|
Assert.Equal(3, searcher.IoCount);
|
||||||
|
|
||||||
|
searcher.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -89,6 +91,8 @@ public class SearcherTest
|
||||||
|
|
||||||
searcher.Search("58.251.27.201");
|
searcher.Search("58.251.27.201");
|
||||||
Assert.Equal(2, searcher.IoCount);
|
Assert.Equal(2, searcher.IoCount);
|
||||||
|
|
||||||
|
searcher.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -100,6 +104,8 @@ public class SearcherTest
|
||||||
|
|
||||||
searcher.Search("58.251.27.201");
|
searcher.Search("58.251.27.201");
|
||||||
Assert.Equal(0, searcher.IoCount);
|
Assert.Equal(0, searcher.IoCount);
|
||||||
|
|
||||||
|
searcher.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
|
|
@ -110,10 +116,6 @@ public class SearcherTest
|
||||||
var ipAddress = IPAddress.Parse(ipStr);
|
var ipAddress = IPAddress.Parse(ipStr);
|
||||||
var region = fileSearcher.Search(ipAddress);
|
var region = fileSearcher.Search(ipAddress);
|
||||||
Assert.Equal(expected, region);
|
Assert.Equal(expected, region);
|
||||||
|
|
||||||
var ip = XDB.Util.IpAddressToUInt32(ipAddress);
|
|
||||||
region = fileSearcher.Search(ip);
|
|
||||||
Assert.Equal(expected, region);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ namespace IP2Region.Net.Abstractions;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// IP 转化为地理位置搜索器接口
|
/// IP 转化为地理位置搜索器接口
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface ISearcher
|
public interface ISearcher : IDisposable
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 搜索方法
|
/// 搜索方法
|
||||||
|
|
@ -30,6 +30,7 @@ public interface ISearcher
|
||||||
/// 搜索方法 仅限 IPv4 使用
|
/// 搜索方法 仅限 IPv4 使用
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="ipAddress">IPv4 地址字节数组小端读取 uint 数值</param>
|
/// <param name="ipAddress">IPv4 地址字节数组小端读取 uint 数值</param>
|
||||||
|
[Obsolete("已弃用,请改用其他方法;Deprecated; please use Search(string) or Search(IPAddress) method.")]
|
||||||
string? Search(uint ipAddress);
|
string? Search(uint ipAddress);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,9 @@
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<id>IP2Region.Net</id>
|
<id>IP2Region.Net</id>
|
||||||
<version>2.1.1</version>
|
<version>3.0.0</version>
|
||||||
<title>IP2Region.Net</title>
|
<title>IP2Region.Net</title>
|
||||||
<authors>Alan Lee</authors>
|
<authors>Alan Lee;Argo Zhang(argo@live.ca)</authors>
|
||||||
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
|
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
|
||||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||||
<PackageProjectUrl>https://github.com/lionsoul2014/ip2region/tree/master/binding/csharp</PackageProjectUrl>
|
<PackageProjectUrl>https://github.com/lionsoul2014/ip2region/tree/master/binding/csharp</PackageProjectUrl>
|
||||||
|
|
|
||||||
|
|
@ -1,54 +0,0 @@
|
||||||
// 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 <lzh.shap@gmail.com>
|
|
||||||
// @Date 2023/07/25
|
|
||||||
// Updated by Argo Zhang <argo@live.ca> at 2025/11/21
|
|
||||||
|
|
||||||
using System.Buffers;
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual ReadOnlyMemory<byte> GetVectorIndex(int offset) => GetData(HeaderInfoLength + offset, VectorIndexSize);
|
|
||||||
|
|
||||||
public virtual ReadOnlyMemory<byte> GetData(long offset, int length)
|
|
||||||
{
|
|
||||||
byte[] buffer = ArrayPool<byte>.Shared.Rent(length);
|
|
||||||
int totalBytesRead = 0;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
XdbFileStream.Seek(offset, SeekOrigin.Begin);
|
|
||||||
|
|
||||||
int bytesRead;
|
|
||||||
while (totalBytesRead < length)
|
|
||||||
{
|
|
||||||
bytesRead = XdbFileStream.Read(buffer, totalBytesRead, length);
|
|
||||||
totalBytesRead += bytesRead;
|
|
||||||
|
|
||||||
IoCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
ArrayPool<byte>.Shared.Return(buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new ReadOnlyMemory<byte>(buffer, 0, totalBytesRead);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -5,14 +5,13 @@
|
||||||
// @Date 2023/07/25
|
// @Date 2023/07/25
|
||||||
// Updated by Argo Zhang <argo@live.ca> at 2025/11/21
|
// Updated by Argo Zhang <argo@live.ca> at 2025/11/21
|
||||||
|
|
||||||
using IP2Region.Net.Internal.Abstractions;
|
|
||||||
using IP2Region.Net.XDB;
|
using IP2Region.Net.XDB;
|
||||||
|
|
||||||
namespace IP2Region.Net.Internal;
|
namespace IP2Region.Net.Internal;
|
||||||
|
|
||||||
class CacheStrategyFactory(string xdbPath)
|
static class CacheStrategyFactory
|
||||||
{
|
{
|
||||||
public AbstractCacheStrategy CreateCacheStrategy(CachePolicy cachePolicy) => cachePolicy switch
|
public static ICacheStrategy CreateCacheStrategy(CachePolicy cachePolicy, string xdbPath) => cachePolicy switch
|
||||||
{
|
{
|
||||||
CachePolicy.Content => new ContentCacheStrategy(xdbPath),
|
CachePolicy.Content => new ContentCacheStrategy(xdbPath),
|
||||||
CachePolicy.VectorIndex => new VectorIndexCacheStrategy(xdbPath),
|
CachePolicy.VectorIndex => new VectorIndexCacheStrategy(xdbPath),
|
||||||
|
|
|
||||||
|
|
@ -5,11 +5,9 @@
|
||||||
// @Date 2023/07/25
|
// @Date 2023/07/25
|
||||||
// Updated by Argo Zhang <argo@live.ca> at 2025/11/21
|
// Updated by Argo Zhang <argo@live.ca> at 2025/11/21
|
||||||
|
|
||||||
using IP2Region.Net.Internal.Abstractions;
|
|
||||||
|
|
||||||
namespace IP2Region.Net.Internal;
|
namespace IP2Region.Net.Internal;
|
||||||
|
|
||||||
class ContentCacheStrategy : AbstractCacheStrategy
|
class ContentCacheStrategy : FileCacheStrategy
|
||||||
{
|
{
|
||||||
private readonly ReadOnlyMemory<byte> _cacheData;
|
private readonly ReadOnlyMemory<byte> _cacheData;
|
||||||
|
|
||||||
|
|
@ -20,5 +18,10 @@ class ContentCacheStrategy : AbstractCacheStrategy
|
||||||
XdbFileStream.Dispose();
|
XdbFileStream.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override ReadOnlyMemory<byte> GetData(long offset = 0, int length = 0) => _cacheData.Slice((int)offset, length);
|
public override ReadOnlyMemory<byte> GetData(long offset, int length) => _cacheData.Slice((int)offset, length);
|
||||||
|
|
||||||
|
protected override void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
base.Dispose(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,11 +5,81 @@
|
||||||
// @Date 2023/07/25
|
// @Date 2023/07/25
|
||||||
// Updated by Argo Zhang <argo@live.ca> at 2025/11/21
|
// Updated by Argo Zhang <argo@live.ca> at 2025/11/21
|
||||||
|
|
||||||
using IP2Region.Net.Internal.Abstractions;
|
using System.Buffers;
|
||||||
|
|
||||||
namespace IP2Region.Net.Internal;
|
namespace IP2Region.Net.Internal;
|
||||||
|
|
||||||
class FileCacheStrategy(string xdbPath) : AbstractCacheStrategy(xdbPath)
|
class FileCacheStrategy(string xdbPath) : ICacheStrategy
|
||||||
{
|
{
|
||||||
|
protected const int HeaderInfoLength = 256;
|
||||||
|
protected const int VectorIndexSize = 8;
|
||||||
|
|
||||||
|
protected const int BufferSize = 64 * 1024;
|
||||||
|
|
||||||
|
protected FileStream XdbFileStream = new(xdbPath, FileMode.Open, FileAccess.Read, FileShare.Read, BufferSize, FileOptions.RandomAccess);
|
||||||
|
|
||||||
|
public int IoCount { get; set; }
|
||||||
|
|
||||||
|
public void ResetIoCount()
|
||||||
|
{
|
||||||
|
IoCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual ReadOnlyMemory<byte> GetVectorIndex(int offset) => GetData(HeaderInfoLength + offset, VectorIndexSize);
|
||||||
|
|
||||||
|
public virtual ReadOnlyMemory<byte> GetData(long offset, int length)
|
||||||
|
{
|
||||||
|
var buffer = ArrayPool<byte>.Shared.Rent(length);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
int totalBytesRead = 0;
|
||||||
|
XdbFileStream.Seek(offset, SeekOrigin.Begin);
|
||||||
|
|
||||||
|
int bytesRead;
|
||||||
|
while (totalBytesRead < length)
|
||||||
|
{
|
||||||
|
bytesRead = XdbFileStream.Read(buffer, totalBytesRead, length - totalBytesRead);
|
||||||
|
if (bytesRead == 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
totalBytesRead += bytesRead;
|
||||||
|
IoCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
var ret = new byte[totalBytesRead];
|
||||||
|
if (totalBytesRead > 0)
|
||||||
|
{
|
||||||
|
Array.Copy(buffer, 0, ret, 0, totalBytesRead);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
ArrayPool<byte>.Shared.Return(buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 释放文件句柄
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="disposing"></param>
|
||||||
|
protected virtual void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (disposing)
|
||||||
|
{
|
||||||
|
XdbFileStream.Close();
|
||||||
|
XdbFileStream.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <inheritdoc/>
|
||||||
|
/// </summary>
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Dispose(true);
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
// 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 <lzh.shap@gmail.com>
|
||||||
|
// @Date 2023/07/25
|
||||||
|
// Updated by Argo Zhang <argo@live.ca> at 2025/11/21
|
||||||
|
|
||||||
|
namespace IP2Region.Net.Internal;
|
||||||
|
|
||||||
|
internal interface ICacheStrategy : IDisposable
|
||||||
|
{
|
||||||
|
int IoCount { get; }
|
||||||
|
|
||||||
|
void ResetIoCount();
|
||||||
|
|
||||||
|
ReadOnlyMemory<byte> GetVectorIndex(int offset);
|
||||||
|
|
||||||
|
ReadOnlyMemory<byte> GetData(long offset, int length);
|
||||||
|
}
|
||||||
|
|
@ -5,11 +5,9 @@
|
||||||
// @Date 2023/07/25
|
// @Date 2023/07/25
|
||||||
// Updated by Argo Zhang <argo@live.ca> at 2025/11/21
|
// Updated by Argo Zhang <argo@live.ca> at 2025/11/21
|
||||||
|
|
||||||
using IP2Region.Net.Internal.Abstractions;
|
|
||||||
|
|
||||||
namespace IP2Region.Net.Internal;
|
namespace IP2Region.Net.Internal;
|
||||||
|
|
||||||
class VectorIndexCacheStrategy : AbstractCacheStrategy
|
class VectorIndexCacheStrategy : FileCacheStrategy
|
||||||
{
|
{
|
||||||
private const int VectorIndexRows = 256;
|
private const int VectorIndexRows = 256;
|
||||||
private const int VectorIndexCols = 256;
|
private const int VectorIndexCols = 256;
|
||||||
|
|
@ -18,10 +16,7 @@ class VectorIndexCacheStrategy : AbstractCacheStrategy
|
||||||
|
|
||||||
public VectorIndexCacheStrategy(string xdbPath) : base(xdbPath)
|
public VectorIndexCacheStrategy(string xdbPath) : base(xdbPath)
|
||||||
{
|
{
|
||||||
XdbFileStream.Seek(HeaderInfoLength, SeekOrigin.Begin);
|
_vectorCache = GetData(HeaderInfoLength, VectorIndexRows * VectorIndexCols * VectorIndexSize);
|
||||||
var buffer = new byte[VectorIndexRows * VectorIndexCols * VectorIndexSize];
|
|
||||||
var length = XdbFileStream.Read(buffer, 0, buffer.Length);
|
|
||||||
_vectorCache = new ReadOnlyMemory<byte>(buffer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override ReadOnlyMemory<byte> GetVectorIndex(int offset) => _vectorCache.Slice(offset, VectorIndexSize);
|
public override ReadOnlyMemory<byte> GetVectorIndex(int offset) => _vectorCache.Slice(offset, VectorIndexSize);
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,8 @@
|
||||||
|
|
||||||
using IP2Region.Net.Abstractions;
|
using IP2Region.Net.Abstractions;
|
||||||
using IP2Region.Net.Internal;
|
using IP2Region.Net.Internal;
|
||||||
using IP2Region.Net.Internal.Abstractions;
|
|
||||||
using System.Buffers.Binary;
|
using System.Buffers.Binary;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
|
|
@ -17,24 +17,18 @@ namespace IP2Region.Net.XDB;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// <see cref="ISearcher"/> 实现类
|
/// <see cref="ISearcher"/> 实现类
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class Searcher : ISearcher
|
/// <remarks>
|
||||||
|
/// <inheritdoc/>
|
||||||
|
/// </remarks>
|
||||||
|
public class Searcher(CachePolicy cachePolicy, string xdbPath) : ISearcher
|
||||||
{
|
{
|
||||||
private readonly AbstractCacheStrategy _cacheStrategy;
|
private readonly ICacheStrategy _cacheStrategy = CacheStrategyFactory.CreateCacheStrategy(cachePolicy, xdbPath);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int IoCount => _cacheStrategy.IoCount;
|
public int IoCount => _cacheStrategy.IoCount;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// <inheritdoc/>
|
|
||||||
/// </summary>
|
|
||||||
public Searcher(CachePolicy cachePolicy, string dbPath)
|
|
||||||
{
|
|
||||||
var factory = new CacheStrategyFactory(dbPath);
|
|
||||||
_cacheStrategy = factory.CreateCacheStrategy(cachePolicy);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -47,14 +41,13 @@ public class Searcher : ISearcher
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string? Search(IPAddress ipAddress)
|
public string? Search(IPAddress ipAddress) => SearchCore(ipAddress.GetAddressBytes());
|
||||||
{
|
|
||||||
return SearchCore(ipAddress.GetAddressBytes());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[Obsolete("已弃用,请改用其他方法;Deprecated; please use Search(string) or Search(IPAddress) method.")]
|
||||||
|
[ExcludeFromCodeCoverage]
|
||||||
public string? Search(uint ipAddress)
|
public string? Search(uint ipAddress)
|
||||||
{
|
{
|
||||||
var bytes = BitConverter.GetBytes(ipAddress);
|
var bytes = BitConverter.GetBytes(ipAddress);
|
||||||
|
|
@ -93,7 +86,7 @@ public class Searcher : ISearcher
|
||||||
{
|
{
|
||||||
int m = (int)(l + h) >> 1;
|
int m = (int)(l + h) >> 1;
|
||||||
|
|
||||||
var p = (int)sPtr + m * indexSize;
|
var p = sPtr + m * indexSize;
|
||||||
var buff = _cacheStrategy.GetData(p, indexSize);
|
var buff = _cacheStrategy.GetData(p, indexSize);
|
||||||
|
|
||||||
var s = buff.Span.Slice(0, length);
|
var s = buff.Span.Slice(0, length);
|
||||||
|
|
@ -114,7 +107,7 @@ public class Searcher : ISearcher
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var regionBuff = _cacheStrategy.GetData((int)dataPtr, dataLen);
|
var regionBuff = _cacheStrategy.GetData(dataPtr, dataLen);
|
||||||
return Encoding.UTF8.GetString(regionBuff.Span.ToArray());
|
return Encoding.UTF8.GetString(regionBuff.Span.ToArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -154,4 +147,13 @@ public class Searcher : ISearcher
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <inheritdoc/>
|
||||||
|
/// </summary>
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_cacheStrategy.Dispose();
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ ISearcher searcher = new Searcher(CachePolicy , "your xdb file path");
|
||||||
| CachePolicy.Content | Cache the entire `xdb` data. | Yes |
|
| CachePolicy.Content | Cache the entire `xdb` data. | Yes |
|
||||||
| CachePolicy.VectorIndex | Cache `vecotorIndex` to speed up queries and reduce system io pressure by reducing one fixed IO operation. | Yes |
|
| CachePolicy.VectorIndex | Cache `vecotorIndex` to speed up queries and reduce system io pressure by reducing one fixed IO operation. | Yes |
|
||||||
| CachePolicy.File | Completely file-based queries | Yes |
|
| CachePolicy.File | Completely file-based queries | Yes |
|
||||||
|
|
||||||
### XDB File Description
|
### XDB File Description
|
||||||
Generate using [maker](https://github.com/lionsoul2014/ip2region/tree/master/maker/csharp), or [download](https://github.com/lionsoul2014/ip2region/blob/master/data/ip2region.xdb) pre-generated xdb files
|
Generate using [maker](https://github.com/lionsoul2014/ip2region/tree/master/maker/csharp), or [download](https://github.com/lionsoul2014/ip2region/blob/master/data/ip2region.xdb) pre-generated xdb files
|
||||||
|
|
||||||
|
|
@ -47,15 +48,47 @@ provider.GetRequiredKeyedService<ISearcher>("IP2Region.Net");
|
||||||
netstandard2.0;netstandard2.1;net6.0;net7.0;net8.0;net9.0;net10.0
|
netstandard2.0;netstandard2.1;net6.0;net7.0;net8.0;net9.0;net10.0
|
||||||
|
|
||||||
## Performance
|
## Performance
|
||||||
|
// * Summary *
|
||||||
|
|
||||||
| Method | Mean | Error | StdDev | Median |
|
BenchmarkDotNet v0.15.6, Windows 11 (10.0.26200.7171)
|
||||||
|------------ |------------:|----------:|------------:|------------:|
|
13th Gen Intel Core i7-13700 2.10GHz, 1 CPU, 24 logical and 16 physical cores
|
||||||
| ContentIPv4 | 101.5 ns | 2.04 ns | 4.57 ns | 103.1 ns |
|
.NET SDK 10.0.100
|
||||||
| VectorIPv4 | 7,488.1 ns | 222.91 ns | 657.26 ns | 7,819.2 ns |
|
[Host] : .NET 10.0.0 (10.0.0, 10.0.25.52411), X64 RyuJIT x86-64-v3
|
||||||
| FileIPv4 | 11,686.9 ns | 59.81 ns | 55.95 ns | 11,707.6 ns |
|
DefaultJob : .NET 10.0.0 (10.0.0, 10.0.25.52411), X64 RyuJIT x86-64-v3
|
||||||
| 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 |
|
| Method | Mean | Error | StdDev | Gen0 | Allocated |
|
||||||
|
|------------ |-------------:|-----------:|-----------:|-------:|----------:|
|
||||||
|
| ContentIPv4 | 53.70 ns | 0.296 ns | 0.277 ns | 0.0086 | 136 B |
|
||||||
|
| VectorIPv4 | 4,446.04 ns | 18.673 ns | 15.593 ns | 0.0076 | 232 B |
|
||||||
|
| FileIPv4 | 6,712.40 ns | 15.718 ns | 13.934 ns | 0.0153 | 264 B |
|
||||||
|
| ContentIPv6 | 145.53 ns | 0.331 ns | 0.277 ns | 0.0126 | 200 B |
|
||||||
|
| VectorIPv6 | 7,058.39 ns | 125.505 ns | 117.398 ns | 0.0381 | 712 B |
|
||||||
|
| FileIPv6 | 10,657.97 ns | 53.907 ns | 50.425 ns | 0.0458 | 744 B |
|
||||||
|
|
||||||
|
// * Hints *
|
||||||
|
Outliers
|
||||||
|
Benchmarks.VectorIPv4: Default -> 2 outliers were removed (4.55 us, 4.58 us)
|
||||||
|
Benchmarks.FileIPv4: Default -> 1 outlier was removed (6.79 us)
|
||||||
|
Benchmarks.ContentIPv6: Default -> 2 outliers were removed (148.08 ns, 152.27 ns)
|
||||||
|
|
||||||
|
// * Legends *
|
||||||
|
Mean : Arithmetic mean of all measurements
|
||||||
|
Error : Half of 99.9% confidence interval
|
||||||
|
StdDev : Standard deviation of all measurements
|
||||||
|
Gen0 : GC Generation 0 collects per 1000 operations
|
||||||
|
Allocated : Allocated memory per single operation (managed only, inclusive, 1KB = 1024B)
|
||||||
|
1 ns : 1 Nanosecond (0.000000001 sec)
|
||||||
|
|
||||||
|
// * Diagnostic Output - MemoryDiagnoser *
|
||||||
|
|
||||||
|
|
||||||
|
// ***** BenchmarkRunner: End *****
|
||||||
|
Run time: 00:02:06 (126.09 sec), executed benchmarks: 6
|
||||||
|
|
||||||
|
Global total time: 00:02:13 (133.47 sec), executed benchmarks: 6
|
||||||
|
// * Artifacts cleanup *
|
||||||
|
Artifacts cleanup is finished
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.
|
Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue