mirror of
https://github.com/lionsoul2014/ip2region.git
synced 2025-12-08 19:25:22 +00:00
160 lines
4.4 KiB
C#
160 lines
4.4 KiB
C#
// 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 IP2Region.Net.Abstractions;
|
||
using IP2Region.Net.Internal;
|
||
using System.Buffers.Binary;
|
||
using System.Diagnostics.CodeAnalysis;
|
||
using System.Net;
|
||
using System.Text;
|
||
|
||
namespace IP2Region.Net.XDB;
|
||
|
||
/// <summary>
|
||
/// <see cref="ISearcher"/> 实现类
|
||
/// </summary>
|
||
/// <remarks>
|
||
/// <inheritdoc/>
|
||
/// </remarks>
|
||
public class Searcher(CachePolicy cachePolicy, string xdbPath) : ISearcher
|
||
{
|
||
private readonly ICacheStrategy _cacheStrategy = CacheStrategyFactory.CreateCacheStrategy(cachePolicy, xdbPath);
|
||
|
||
/// <summary>
|
||
/// <inheritdoc/>
|
||
/// </summary>
|
||
public int IoCount => _cacheStrategy.IoCount;
|
||
|
||
/// <summary>
|
||
/// <inheritdoc/>
|
||
/// </summary>
|
||
public string? Search(string ipStr)
|
||
{
|
||
var ipAddress = IPAddress.Parse(ipStr);
|
||
return SearchCore(ipAddress.GetAddressBytes());
|
||
}
|
||
|
||
/// <summary>
|
||
/// <inheritdoc/>
|
||
/// </summary>
|
||
public string? Search(IPAddress ipAddress) => SearchCore(ipAddress.GetAddressBytes());
|
||
|
||
/// <summary>
|
||
/// <inheritdoc/>
|
||
/// </summary>
|
||
[Obsolete("已弃用,请改用其他方法;Deprecated; please use Search(string) or Search(IPAddress) method.")]
|
||
[ExcludeFromCodeCoverage]
|
||
public string? Search(uint ipAddress)
|
||
{
|
||
var bytes = BitConverter.GetBytes(ipAddress);
|
||
Array.Reverse(bytes);
|
||
return SearchCore(bytes);
|
||
}
|
||
|
||
string? SearchCore(byte[] ipBytes)
|
||
{
|
||
// 重置 IO 计数器
|
||
_cacheStrategy.ResetIoCount();
|
||
|
||
// 每个 vector 索引项的字节数
|
||
var vectorIndexSize = 8;
|
||
|
||
// vector 索引的列数
|
||
var vectorIndexCols = 256;
|
||
|
||
// 计算得到 vector 索引项的开始地址。
|
||
var il0 = ipBytes[0];
|
||
var il1 = ipBytes[1];
|
||
var idx = il0 * vectorIndexCols * vectorIndexSize + il1 * vectorIndexSize;
|
||
|
||
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;
|
||
var l = 0;
|
||
var h = (ePtr - sPtr) / indexSize;
|
||
var dataLen = 0;
|
||
long dataPtr = 0;
|
||
|
||
while (l <= h)
|
||
{
|
||
int m = (int)(l + h) >> 1;
|
||
|
||
var p = sPtr + m * indexSize;
|
||
var buff = _cacheStrategy.GetData(p, indexSize);
|
||
|
||
var s = buff.Span.Slice(0, length);
|
||
var e = buff.Span.Slice(length, length);
|
||
if (ByteCompare(ipBytes, s) < 0)
|
||
{
|
||
h = m - 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, 4));
|
||
break;
|
||
}
|
||
}
|
||
|
||
var regionBuff = _cacheStrategy.GetData(dataPtr, dataLen);
|
||
return Encoding.UTF8.GetString(regionBuff.Span.ToArray());
|
||
}
|
||
|
||
static int ByteCompare(byte[] ip1, ReadOnlySpan<byte> ip2) => ip1.Length == 4 ? IPv4Compare(ip1, ip2) : IPv6Compare(ip1, ip2);
|
||
|
||
static int IPv4Compare(byte[] ip1, ReadOnlySpan<byte> ip2)
|
||
{
|
||
var ret = 0;
|
||
for (int i = 0; i < ip1.Length; i++)
|
||
{
|
||
var ip2Index = ip1.Length - 1 - i;
|
||
if (ip1[i] < ip2[ip2Index])
|
||
{
|
||
return -1;
|
||
}
|
||
else if (ip1[i] > ip2[ip2Index])
|
||
{
|
||
return 1;
|
||
}
|
||
}
|
||
return ret;
|
||
}
|
||
|
||
static int IPv6Compare(byte[] ip1, ReadOnlySpan<byte> 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;
|
||
}
|
||
|
||
/// <summary>
|
||
/// <inheritdoc/>
|
||
/// </summary>
|
||
public void Dispose()
|
||
{
|
||
_cacheStrategy.Dispose();
|
||
GC.SuppressFinalize(this);
|
||
}
|
||
}
|