mirror of
https://github.com/netfun2000/ip2region.git
synced 2026-02-27 09:44:31 +08:00
feat:support for concurrent file-based queries
This commit is contained in:
@@ -3,15 +3,15 @@ namespace IP2Region.Net.XDB;
|
||||
public enum CachePolicy
|
||||
{
|
||||
/// <summary>
|
||||
/// no cache , not thread safe!
|
||||
/// no cache
|
||||
/// </summary>
|
||||
File,
|
||||
/// <summary>
|
||||
/// cache vector index , reduce the number of IO operations , not thread safe!
|
||||
/// cache vector index , reduce the number of IO operations
|
||||
/// </summary>
|
||||
VectorIndex,
|
||||
/// <summary>
|
||||
/// default cache policy , cache whole xdb file , thread safe
|
||||
/// default cache policy , cache whole xdb file
|
||||
/// </summary>
|
||||
Content
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
using System.Net;
|
||||
|
||||
namespace IP2Region.Net.XDB;
|
||||
|
||||
public interface ISearcher
|
||||
{
|
||||
string? Search(string ipStr);
|
||||
|
||||
string? Search(IPAddress ipAddress);
|
||||
|
||||
string? Search(uint ipAddress);
|
||||
|
||||
int IoCount { get; }
|
||||
}
|
||||
@@ -1,60 +1,29 @@
|
||||
// Copyright 2022 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 Lee <lzh.shap@gmail.com>
|
||||
// @Date 2022/09/06
|
||||
// @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;
|
||||
|
||||
namespace IP2Region.Net.XDB;
|
||||
|
||||
public class Searcher : ISearcher
|
||||
{
|
||||
const int HeaderInfoLength = 256;
|
||||
const int VectorIndexRows = 256;
|
||||
const int VectorIndexCols = 256;
|
||||
const int VectorIndexSize = 8;
|
||||
const int SegmentIndexSize = 14;
|
||||
|
||||
private readonly byte[]? _vectorIndex;
|
||||
private readonly byte[]? _contentBuff;
|
||||
private readonly FileStream _contentStream;
|
||||
private readonly CachePolicy _cachePolicy;
|
||||
public int IoCount { get; private set; }
|
||||
private readonly AbstractCacheStrategy _cacheStrategy;
|
||||
public int IoCount => _cacheStrategy.IoCount;
|
||||
|
||||
public Searcher(CachePolicy cachePolicy = CachePolicy.Content, string? dbPath = null)
|
||||
public Searcher(CachePolicy cachePolicy, string dbPath)
|
||||
{
|
||||
if (string.IsNullOrEmpty(dbPath))
|
||||
{
|
||||
dbPath = Path.Combine(AppContext.BaseDirectory, "Data", "ip2region.xdb");
|
||||
}
|
||||
|
||||
_contentStream = File.OpenRead(dbPath);
|
||||
_cachePolicy = cachePolicy;
|
||||
|
||||
switch (_cachePolicy)
|
||||
{
|
||||
case CachePolicy.Content:
|
||||
using (var stream = new MemoryStream())
|
||||
{
|
||||
_contentStream.CopyTo(stream);
|
||||
_contentBuff = stream.ToArray();
|
||||
}
|
||||
|
||||
break;
|
||||
case CachePolicy.VectorIndex:
|
||||
var vectorLength = VectorIndexRows * VectorIndexCols * VectorIndexSize;
|
||||
_vectorIndex = new byte[vectorLength];
|
||||
Read(HeaderInfoLength, _vectorIndex);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
~Searcher()
|
||||
{
|
||||
_contentStream.Close();
|
||||
_contentStream.Dispose();
|
||||
var factory = new CacheStrategyFactory(dbPath);
|
||||
_cacheStrategy = factory.CreateCacheStrategy(cachePolicy);
|
||||
}
|
||||
|
||||
public string? Search(string ipStr)
|
||||
@@ -71,62 +40,37 @@ public class Searcher : ISearcher
|
||||
|
||||
public string? Search(uint ip)
|
||||
{
|
||||
var il0 = ip >> 24 & 0xFF;
|
||||
var il1 = ip >> 16 & 0xFF;
|
||||
var idx = il0 * VectorIndexCols * VectorIndexSize + il1 * VectorIndexSize;
|
||||
|
||||
uint sPtr = 0, ePtr = 0;
|
||||
|
||||
switch (_cachePolicy)
|
||||
{
|
||||
case CachePolicy.VectorIndex:
|
||||
sPtr = BitConverter.ToUInt32(_vectorIndex.AsSpan()[(int)idx..]);
|
||||
ePtr = BitConverter.ToUInt32(_vectorIndex.AsSpan()[((int)idx + 4)..]);
|
||||
break;
|
||||
case CachePolicy.Content:
|
||||
sPtr = BitConverter.ToUInt32(_contentBuff.AsSpan()[(HeaderInfoLength + (int)idx)..]);
|
||||
ePtr = BitConverter.ToUInt32(_contentBuff.AsSpan()[(HeaderInfoLength + (int)idx + 4)..]);
|
||||
break;
|
||||
case CachePolicy.File:
|
||||
var buff = new byte[VectorIndexSize];
|
||||
Read((int)(idx + HeaderInfoLength), buff);
|
||||
sPtr = BitConverter.ToUInt32(buff);
|
||||
ePtr = BitConverter.ToUInt32(buff.AsSpan()[4..]);
|
||||
break;
|
||||
}
|
||||
|
||||
var index = _cacheStrategy.GetVectorIndex(ip);
|
||||
uint sPtr = MemoryMarshal.Read<uint>(index.Span);
|
||||
uint ePtr = MemoryMarshal.Read<uint>(index.Span[4..]);
|
||||
|
||||
var dataLen = 0;
|
||||
uint dataPtr = 0;
|
||||
var l = 0;
|
||||
var h = (int)((ePtr - sPtr) / SegmentIndexSize);
|
||||
var buffer = new byte[SegmentIndexSize];
|
||||
uint l = 0;
|
||||
uint h = (ePtr -sPtr) / SegmentIndexSize;
|
||||
|
||||
while (l <= h)
|
||||
{
|
||||
var mid = Util.GetMidIp(l, h);
|
||||
var pos = sPtr + mid * SegmentIndexSize;
|
||||
|
||||
Read((int)pos, buffer);
|
||||
var sip = BitConverter.ToUInt32(buffer);
|
||||
var buffer = _cacheStrategy.GetData((int)pos, SegmentIndexSize);
|
||||
uint sip = MemoryMarshal.Read<uint>(buffer.Span);
|
||||
uint eip = MemoryMarshal.Read<uint>(buffer.Span[4..]);
|
||||
|
||||
if (ip < sip)
|
||||
{
|
||||
h = mid - 1;
|
||||
}
|
||||
else if (ip > eip)
|
||||
{
|
||||
l = mid + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
var eip = BitConverter.ToUInt32(buffer.AsSpan()[4..]);
|
||||
if (ip > eip)
|
||||
{
|
||||
l = mid + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
dataLen = BitConverter.ToUInt16(buffer.AsSpan()[8..]);
|
||||
dataPtr = BitConverter.ToUInt32(buffer.AsSpan()[10..]);
|
||||
break;
|
||||
}
|
||||
dataLen = MemoryMarshal.Read<ushort>(buffer.Span[8..]);
|
||||
dataPtr = MemoryMarshal.Read<uint>(buffer.Span[10..]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,29 +79,7 @@ public class Searcher : ISearcher
|
||||
return default;
|
||||
}
|
||||
|
||||
var regionBuff = new byte[dataLen];
|
||||
Read((int)dataPtr, regionBuff);
|
||||
return Encoding.UTF8.GetString(regionBuff);
|
||||
}
|
||||
|
||||
private void Read(int offset, byte[] buff)
|
||||
{
|
||||
switch (_cachePolicy)
|
||||
{
|
||||
case CachePolicy.Content:
|
||||
_contentBuff.AsSpan()[offset..(offset + buff.Length)].CopyTo(buff);
|
||||
break;
|
||||
default:
|
||||
_contentStream.Seek(offset, SeekOrigin.Begin);
|
||||
IoCount++;
|
||||
|
||||
var rLen = _contentStream.Read(buff);
|
||||
if (rLen != buff.Length)
|
||||
{
|
||||
throw new IOException($"incomplete read: readed bytes should be {buff.Length}");
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
var regionBuff = _cacheStrategy.GetData((int)dataPtr,dataLen);
|
||||
return Encoding.UTF8.GetString(regionBuff.Span);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Net;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace IP2Region.Net.XDB;
|
||||
|
||||
@@ -14,12 +15,9 @@ public static class Util
|
||||
{
|
||||
byte[] bytes = ipAddress.GetAddressBytes();
|
||||
Array.Reverse(bytes);
|
||||
return BitConverter.ToUInt32(bytes, 0);
|
||||
return MemoryMarshal.Read<uint>(bytes);
|
||||
}
|
||||
|
||||
public static uint GetMidIp(uint x, uint y)
|
||||
=> (x & y) + ((x ^ y) >> 1);
|
||||
|
||||
public static int GetMidIp(int x, int y)
|
||||
=> (x & y) + ((x ^ y) >> 1);
|
||||
}
|
||||
Reference in New Issue
Block a user