diff --git a/.gitignore b/.gitignore index 9bb1040..fe4f008 100644 --- a/.gitignore +++ b/.gitignore @@ -31,5 +31,9 @@ Cargo.lock target - - +# VS ignore cases +/**/*.sln +/binding/c#/**/.vs/ +/binding/c#/**/packages +/binding/c#/**/bin +/binding/c#/**/obj diff --git a/README.md b/README.md index 66fdc63..d4f314a 100644 --- a/README.md +++ b/README.md @@ -44,22 +44,7 @@ Install-Package IP2Region ``` ### 5. 测试程序: -c#: -```shell -cd IP2Region_ConsoleTest -dotnet run -example result: -请输入IP地址: -36.149.160.55 -``` - -node: -``` -> let ip2region = require('./ip2region.js'); -> let dbService = ip2region.create('./ip2region.db'); -> dbService.binarySearchSync(' 101.105.35.57') -{ city: 0, region: '中国|0|广东|深圳|鹏博士' } -``` +C# 和 Node.js,请具体参考文件夹中README.md 说明。 java: ```shell diff --git a/binding/c#/BenchmarkTest/App.config b/binding/c#/BenchmarkTest/App.config new file mode 100644 index 0000000..1ab3951 --- /dev/null +++ b/binding/c#/BenchmarkTest/App.config @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/binding/c#/BenchmarkTest/BenchTests/BenchTestBase.cs b/binding/c#/BenchmarkTest/BenchTests/BenchTestBase.cs new file mode 100644 index 0000000..835aed6 --- /dev/null +++ b/binding/c#/BenchmarkTest/BenchTests/BenchTestBase.cs @@ -0,0 +1,21 @@ +using BenchmarkDotNet.Attributes; +using IP2Region; + +namespace BenchmarkTest.BenchTests +{ + public class BenchTestBase + { + protected DbSearcher _dbSearcher = null; + + [GlobalSetup] + public void Init() + { + _dbSearcher = new DbSearcher("../../../../../data/ip2region.db"); + } + [GlobalCleanup] + public void Clearup() + { + _dbSearcher.Dispose(); + } + } +} diff --git a/binding/c#/BenchmarkTest/BenchTests/NormalBenchmarkTests.cs b/binding/c#/BenchmarkTest/BenchTests/NormalBenchmarkTests.cs new file mode 100644 index 0000000..40a4175 --- /dev/null +++ b/binding/c#/BenchmarkTest/BenchTests/NormalBenchmarkTests.cs @@ -0,0 +1,46 @@ +using BenchmarkDotNet.Attributes; +using System.Threading.Tasks; + +namespace BenchmarkTest.BenchTests +{ + public class NormalBenchmarkTests : BenchTestBase + { + [Params("0.0.0.0", "210.109.255.230", "192.168.0.1", "255.255.255.255", "183.196.233.159", + "77.49.66.88", "210.248.255.231", "10.10.10.10", "197.84.60.202", "35.193.251.120", + "20.108.91.101", "120.196.148.137", "249.255.250.200", "112.65.1.130")] + public string validIp = null; + + [Benchmark] + public void TestSyncSpeedForMemorySearch() + { + _dbSearcher.MemorySearch(validIp); + } + [Benchmark] + public async Task TestAsyncSpeedForMemorySearch() + { + await _dbSearcher.AsyncMemorySearch(validIp); + } + + [Benchmark] + public void TestSyncSpeedForBinarySearch() + { + _dbSearcher.BinarySearch(validIp); + } + [Benchmark] + public async Task TestAsyncBinarySearch() + { + await _dbSearcher.AsyncBinarySearch(validIp); + } + + [Benchmark] + public void TestSyncSpeedForBTreeSearch() + { + _dbSearcher.BtreeSearch(validIp); + } + [Benchmark] + public async Task TestAsyncSpeedForBTreeSearch() + { + await _dbSearcher.AsyncBtreeSearch(validIp); + } + } +} diff --git a/binding/c#/BenchmarkTest/BenchmarkTest.csproj b/binding/c#/BenchmarkTest/BenchmarkTest.csproj new file mode 100644 index 0000000..c83403a --- /dev/null +++ b/binding/c#/BenchmarkTest/BenchmarkTest.csproj @@ -0,0 +1,154 @@ + + + + + Debug + AnyCPU + {053EA51E-2246-4FCB-A106-9B53E70FDC3F} + Exe + BenchmarkTest + BenchmarkTest + v4.6 + 512 + + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\BenchmarkDotNet.0.10.14\lib\net46\BenchmarkDotNet.dll + + + ..\packages\BenchmarkDotNet.Core.0.10.14\lib\net46\BenchmarkDotNet.Core.dll + + + ..\packages\BenchmarkDotNet.Toolchains.Roslyn.0.10.14\lib\net46\BenchmarkDotNet.Toolchains.Roslyn.dll + + + ..\packages\Microsoft.CodeAnalysis.Common.2.6.1\lib\netstandard1.3\Microsoft.CodeAnalysis.dll + + + ..\packages\Microsoft.CodeAnalysis.CSharp.2.6.1\lib\netstandard1.3\Microsoft.CodeAnalysis.CSharp.dll + + + ..\packages\Microsoft.DotNet.InternalAbstractions.1.0.0\lib\net451\Microsoft.DotNet.InternalAbstractions.dll + + + ..\packages\Microsoft.DotNet.PlatformAbstractions.1.1.1\lib\net451\Microsoft.DotNet.PlatformAbstractions.dll + + + ..\packages\Microsoft.Win32.Registry.4.3.0\lib\net46\Microsoft.Win32.Registry.dll + + + + ..\packages\System.AppContext.4.3.0\lib\net46\System.AppContext.dll + + + ..\packages\System.Collections.Immutable.1.3.1\lib\portable-net45+win8+wp8+wpa81\System.Collections.Immutable.dll + + + + ..\packages\System.Console.4.3.0\lib\net46\System.Console.dll + + + + + + ..\packages\System.Diagnostics.FileVersionInfo.4.3.0\lib\net46\System.Diagnostics.FileVersionInfo.dll + + + ..\packages\System.Diagnostics.StackTrace.4.3.0\lib\net46\System.Diagnostics.StackTrace.dll + + + ..\packages\System.IO.Compression.4.3.0\lib\net46\System.IO.Compression.dll + + + ..\packages\System.IO.FileSystem.4.3.0\lib\net46\System.IO.FileSystem.dll + + + ..\packages\System.IO.FileSystem.Primitives.4.3.0\lib\net46\System.IO.FileSystem.Primitives.dll + + + + + + ..\packages\System.Reflection.Metadata.1.4.2\lib\portable-net45+win8\System.Reflection.Metadata.dll + + + ..\packages\System.Security.Cryptography.Algorithms.4.3.0\lib\net46\System.Security.Cryptography.Algorithms.dll + + + ..\packages\System.Security.Cryptography.Encoding.4.3.0\lib\net46\System.Security.Cryptography.Encoding.dll + + + ..\packages\System.Security.Cryptography.Primitives.4.3.0\lib\net46\System.Security.Cryptography.Primitives.dll + + + ..\packages\System.Security.Cryptography.X509Certificates.4.3.0\lib\net46\System.Security.Cryptography.X509Certificates.dll + + + ..\packages\System.Text.Encoding.CodePages.4.3.0\lib\net46\System.Text.Encoding.CodePages.dll + + + ..\packages\System.Threading.Tasks.Extensions.4.3.0\lib\portable-net45+win8+wp8+wpa81\System.Threading.Tasks.Extensions.dll + + + ..\packages\System.Threading.Thread.4.3.0\lib\net46\System.Threading.Thread.dll + + + ..\packages\System.ValueTuple.4.3.0\lib\netstandard1.0\System.ValueTuple.dll + + + + + ..\packages\System.Xml.ReaderWriter.4.3.0\lib\net46\System.Xml.ReaderWriter.dll + + + ..\packages\System.Xml.XmlDocument.4.3.0\lib\net46\System.Xml.XmlDocument.dll + + + ..\packages\System.Xml.XPath.4.3.0\lib\net46\System.Xml.XPath.dll + + + ..\packages\System.Xml.XPath.XDocument.4.3.0\lib\net46\System.Xml.XPath.XDocument.dll + + + + + + + + + + + + + + + + + + + {4c6032ee-43a5-4d5c-88f8-c411069e49e0} + IP2Region + + + + \ No newline at end of file diff --git a/binding/c#/BenchmarkTest/Program.cs b/binding/c#/BenchmarkTest/Program.cs new file mode 100644 index 0000000..44da3d1 --- /dev/null +++ b/binding/c#/BenchmarkTest/Program.cs @@ -0,0 +1,13 @@ +using BenchmarkDotNet.Running; +using BenchmarkTest.BenchTests; + +namespace BenchmarkTest +{ + public class Program + { + public static void Main(string[] args) + { + BenchmarkRunner.Run(); + } + } +} diff --git a/binding/c#/BenchmarkTest/Properties/AssemblyInfo.cs b/binding/c#/BenchmarkTest/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..6f7bdff --- /dev/null +++ b/binding/c#/BenchmarkTest/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +// 有关程序集的一般信息由以下 +// 控制。更改这些特性值可修改 +// 与程序集关联的信息。 +[assembly: AssemblyTitle("BenchmarkTest")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("BenchmarkTest")] +[assembly: AssemblyCopyright("Copyright © 2018")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// 将 ComVisible 设置为 false 会使此程序集中的类型 +//对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型 +//请将此类型的 ComVisible 特性设置为 true。 +[assembly: ComVisible(false)] + +// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID +[assembly: Guid("053ea51e-2246-4fcb-a106-9b53e70fdc3f")] + +// 程序集的版本信息由下列四个值组成: +// +// 主版本 +// 次版本 +// 生成号 +// 修订号 +// +// 可以指定所有值,也可以使用以下所示的 "*" 预置版本号和修订号 +// 方法是按如下所示使用“*”: : +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/binding/c#/BenchmarkTest/packages.config b/binding/c#/BenchmarkTest/packages.config new file mode 100644 index 0000000..165b5a3 --- /dev/null +++ b/binding/c#/BenchmarkTest/packages.config @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/binding/c#/IP2Region/DBSearcher.cs b/binding/c#/IP2Region/DBSearcher.cs index ca5cf5a..1c1f6d2 100644 --- a/binding/c#/IP2Region/DBSearcher.cs +++ b/binding/c#/IP2Region/DBSearcher.cs @@ -3,261 +3,244 @@ // Github https://github.com/RocherKong // Date 2018.02.09 //******************************* +using IP2Region.Models; using System; using System.IO; using System.Text; - +using System.Threading.Tasks; + namespace IP2Region { - public class DbSearcher + public class DbSearcher : IDisposable { - public static int BTREE_ALGORITHM = 1; - public static int BINARY_ALGORITHM = 2; - public static int MEMORY_ALGORITYM = 3; + const int BTREE_ALGORITHM = 1; + const int BINARY_ALGORITHM = 2; + const int MEMORY_ALGORITYM = 3; - /** - * db config - */ - private DbConfig dbConfig = null; + private DbConfig _dbConfig = null; /** * db file access handler */ - private FileStream raf = null; + private FileStream _raf = null; /** * header blocks buffer */ - private long[] HeaderSip = null; - private int[] HeaderPtr = null; - private int headerLength; + private long[] _headerSip = null; + private int[] _headerPtr = null; + private int _headerLength; /** * super blocks info */ - private long firstIndexPtr = 0; - private long lastIndexPtr = 0; - private int totalIndexBlocks = 0; + private long _firstIndexPtr = 0; + private long _lastIndexPtr = 0; + private int _totalIndexBlocks = 0; /** * for memory mode * the original db binary string */ - private byte[] dbBinStr = null; - - /** - * construct class - * - * @param bdConfig - * @param dbFile - * @throws FileNotFoundException - */ - public DbSearcher(DbConfig dbConfig, String dbFile) + private byte[] _dbBinStr = null; + + /// + /// Get by index ptr. + /// + private DataBlock GetByIndexPtr(long ptr) { - this.dbConfig = dbConfig; - raf = new FileStream(dbFile, FileMode.Open, FileAccess.Read); + _raf.Seek(ptr, SeekOrigin.Begin); + byte[] + buffer = new byte[12]; + _raf.Read(buffer, 0, buffer.Length); + + long extra = Utils.getIntLong(buffer, 8); + + int dataLen = (int)((extra >> 24) & 0xFF); + int dataPtr = (int)((extra & 0x00FFFFFF)); + + _raf.Seek(dataPtr, SeekOrigin.Begin); + byte[] data = new byte[dataLen]; + _raf.Read(data, 0, data.Length); + + int city_id = (int)Utils.getIntLong(data, 0); + string region = Encoding.UTF8.GetString(data, 4, data.Length - 4); + + return new DataBlock(city_id, region, dataPtr); } - /** - * get the region with a int ip address with memory binary search algorithm - * - * @param ip - * @throws IOException - */ - public DataBlock MemorySearch(long ip) + public DbSearcher(DbConfig dbConfig, string dbFile) { - int blen = IndexBlock.GetIndexBlockLength(); - if (dbBinStr == null) + if (_dbConfig == null) + { + _dbConfig = dbConfig; + } + _raf = new FileStream(dbFile, FileMode.Open, FileAccess.Read, FileShare.Read); + } + + public DbSearcher(string dbFile) : this(null, dbFile) { } + + #region Sync Methods + /// + /// Get the region with a int ip address with memory binary search algorithm. + /// + private DataBlock MemorySearch(long ip) + { + int blen = IndexBlock.LENGTH; + if (_dbBinStr == null) { - dbBinStr = new byte[(int)raf.Length]; - raf.Seek(0L, SeekOrigin.Begin); - raf.Read(dbBinStr, 0, dbBinStr.Length); + _dbBinStr = new byte[(int)_raf.Length]; + _raf.Seek(0L, SeekOrigin.Begin); + _raf.Read(_dbBinStr, 0, _dbBinStr.Length); //initialize the global vars - firstIndexPtr = Util.getIntLong(dbBinStr, 0); - lastIndexPtr = Util.getIntLong(dbBinStr, 4); - totalIndexBlocks = (int)((lastIndexPtr - firstIndexPtr) / blen) + 1; + _firstIndexPtr = Utils.getIntLong(_dbBinStr, 0); + _lastIndexPtr = Utils.getIntLong(_dbBinStr, 4); + _totalIndexBlocks = (int)((_lastIndexPtr - _firstIndexPtr) / blen) + 1; } //search the index blocks to define the data - int l = 0, h = totalIndexBlocks; - long sip, eip, dataptr = 0; + int l = 0, h = _totalIndexBlocks; + long sip = 0; + while (l <= h) { int m = (l + h) >> 1; - int p = (int)(firstIndexPtr + m * blen); + int p = (int)(_firstIndexPtr + m * blen); + + sip = Utils.getIntLong(_dbBinStr, p); - sip = Util.getIntLong(dbBinStr, p); if (ip < sip) { h = m - 1; } else { - eip = Util.getIntLong(dbBinStr, p + 4); - if (ip > eip) + sip = Utils.getIntLong(_dbBinStr, p + 4); + if (ip > sip) { l = m + 1; } else { - dataptr = Util.getIntLong(dbBinStr, p + 8); + sip = Utils.getIntLong(_dbBinStr, p + 8); break; } } } //not matched - if (dataptr == 0) return null; + if (sip == 0) return null; //get the data - int dataLen = (int)((dataptr >> 24) & 0xFF); - int dataPtr = (int)((dataptr & 0x00FFFFFF)); - int city_id = (int)Util.getIntLong(dbBinStr, dataPtr); - String region = System.Text.Encoding.UTF8.GetString(dbBinStr, dataPtr + 4, dataLen - 4);//new String(dbBinStr, dataPtr + 4, dataLen - 4, Encoding.UTF8); + int dataLen = (int)((sip >> 24) & 0xFF); + int dataPtr = (int)((sip & 0x00FFFFFF)); + int city_id = (int)Utils.getIntLong(_dbBinStr, dataPtr); + string region = Encoding.UTF8.GetString(_dbBinStr, dataPtr + 4, dataLen - 4);//new String(dbBinStr, dataPtr + 4, dataLen - 4, Encoding.UTF8); return new DataBlock(city_id, region, dataPtr); } - /** - * get the region throught the ip address with memory binary search algorithm - * - * @param ip - * @return DataBlock - * @throws IOException -*/ - public DataBlock MemorySearch(String ip) + /// + /// Get the region throught the ip address with memory binary search algorithm. + /// + public DataBlock MemorySearch(string ip) { - return MemorySearch(Util.ip2long(ip)); + return MemorySearch(Utils.ip2long(ip)); } - - /** - * get by index ptr - * - * @param indexPtr - * @throws IOException -*/ - public DataBlock GetByIndexPtr(long ptr) - { - raf.Seek(ptr, SeekOrigin.Begin); - byte[] - buffer = new byte[12]; - raf.Read(buffer, 0, buffer.Length); - //long startIp = Util.getIntLong(buffer, 0); - //long endIp = Util.getIntLong(buffer, 4); - long extra = Util.getIntLong(buffer, 8); - - int dataLen = (int)((extra >> 24) & 0xFF); - int dataPtr = (int)((extra & 0x00FFFFFF)); - - raf.Seek(dataPtr, SeekOrigin.Begin); - byte[] data = new byte[dataLen]; - raf.Read(data, 0, data.Length); - - int city_id = (int)Util.getIntLong(data, 0); - String region = Encoding.UTF8.GetString(data, 4, data.Length - 4); - //new String(data, 4, data.Length - 4, "UTF-8"); - - return new DataBlock(city_id, region, dataPtr); - } - - /** - * get the region with a int ip address with b-tree algorithm - * - * @param ip - * @throws IOException -*/ - public DataBlock BtreeSearch(long ip) + /// + /// Get the region with a int ip address with b-tree algorithm. + /// + private DataBlock BtreeSearch(long ip) { //check and load the header - if (HeaderSip == null) + if (_headerSip == null) { - raf.Seek(8L, SeekOrigin.Begin); //pass the super block - //byte[] b = new byte[dbConfig.getTotalHeaderSize()]; + _raf.Seek(8L, SeekOrigin.Begin); //pass the super block + //byte[] b = new byte[dbConfig.getTotalHeaderSize()]; byte[] b = new byte[4096]; - raf.Read(b, 0, b.Length); + _raf.Read(b, 0, b.Length); //fill the header int len = b.Length >> 3, idx = 0; //b.lenght / 8 - HeaderSip = new long[len]; - HeaderPtr = new int[len]; + _headerSip = new long[len]; + _headerPtr = new int[len]; long startIp, dataPtrTemp; for (int i = 0; i < b.Length; i += 8) { - startIp = Util.getIntLong(b, i); - dataPtrTemp = Util.getIntLong(b, i + 4); + startIp = Utils.getIntLong(b, i); + dataPtrTemp = Utils.getIntLong(b, i + 4); if (dataPtrTemp == 0) break; - HeaderSip[idx] = startIp; - HeaderPtr[idx] = (int)dataPtrTemp; + _headerSip[idx] = startIp; + _headerPtr[idx] = (int)dataPtrTemp; idx++; } - headerLength = idx; + _headerLength = idx; } //1. define the index block with the binary search - if (ip == HeaderSip[0]) + if (ip == _headerSip[0]) { - return GetByIndexPtr(HeaderPtr[0]); + return GetByIndexPtr(_headerPtr[0]); } - else if (ip == HeaderSip[headerLength - 1]) + else if (ip == _headerPtr[_headerLength - 1]) { - return GetByIndexPtr(HeaderPtr[headerLength - 1]); + return GetByIndexPtr(_headerPtr[_headerLength - 1]); } - int l = 0, h = headerLength, sptr = 0, eptr = 0; + int l = 0, h = _headerLength, sptr = 0, eptr = 0; + int m = 0; + while (l <= h) { - int m = (l + h) >> 1; + m = (l + h) >> 1; - //perfetc matched, just return it - if (ip == HeaderSip[m]) + //perfectly matched, just return it + if (ip == _headerSip[m]) { if (m > 0) { - sptr = HeaderPtr[m - 1]; - eptr = HeaderPtr[m]; + sptr = _headerPtr[m - 1]; + eptr = _headerPtr[m]; } else { - sptr = HeaderPtr[m]; - eptr = HeaderPtr[m + 1]; + sptr = _headerPtr[m]; + eptr = _headerPtr[m + 1]; } - - break; } - //less then the middle value - if (ip < HeaderSip[m]) + else if (ip < _headerSip[m]) { if (m == 0) { - sptr = HeaderPtr[m]; - eptr = HeaderPtr[m + 1]; + sptr = _headerPtr[m]; + eptr = _headerPtr[m + 1]; break; } - else if (ip > HeaderSip[m - 1]) + else if (ip > _headerSip[m - 1]) { - sptr = HeaderPtr[m - 1]; - eptr = HeaderPtr[m]; + sptr = _headerPtr[m - 1]; + eptr = _headerPtr[m]; break; } h = m - 1; } else { - if (m == headerLength - 1) + if (m == _headerLength - 1) { - sptr = HeaderPtr[m - 1]; - eptr = HeaderPtr[m]; + sptr = _headerPtr[m - 1]; + eptr = _headerPtr[m]; break; } - else if (ip <= HeaderSip[m + 1]) + else if (ip <= _headerSip[m + 1]) { - sptr = HeaderPtr[m]; - eptr = HeaderPtr[m + 1]; + sptr = _headerPtr[m]; + eptr = _headerPtr[m + 1]; break; } l = m + 1; @@ -268,169 +251,179 @@ namespace IP2Region if (sptr == 0) return null; //2. search the index blocks to define the data - int blockLen = eptr - sptr, blen = IndexBlock.GetIndexBlockLength(); - byte[] - iBuffer = new byte[blockLen + blen]; //include the right border block - raf.Seek(sptr, SeekOrigin.Begin); - raf.Read(iBuffer, 0, iBuffer.Length); + int blockLen = eptr - sptr, blen = IndexBlock.LENGTH; + byte[] iBuffer = new byte[blockLen + blen]; //include the right border block + _raf.Seek(sptr, SeekOrigin.Begin); + _raf.Read(iBuffer, 0, iBuffer.Length); l = 0; h = blockLen / blen; - long sip, eip, dataptr = 0; + long sip = 0; + int p = 0; + while (l <= h) { - int m = (l + h) >> 1; - int p = m * blen; - sip = Util.getIntLong(iBuffer, p); + m = (l + h) >> 1; + p = m * blen; + sip = Utils.getIntLong(iBuffer, p); if (ip < sip) { h = m - 1; } else { - eip = Util.getIntLong(iBuffer, p + 4); - if (ip > eip) + sip = Utils.getIntLong(iBuffer, p + 4); + if (ip > sip) { l = m + 1; } else { - dataptr = Util.getIntLong(iBuffer, p + 8); + sip = Utils.getIntLong(iBuffer, p + 8); break; } } } //not matched - if (dataptr == 0) return null; + if (sip == 0) return null; //3. get the data - int dataLen = (int)((dataptr >> 24) & 0xFF); - int dataPtr = (int)((dataptr & 0x00FFFFFF)); + int dataLen = (int)((sip >> 24) & 0xFF); + int dataPtr = (int)((sip & 0x00FFFFFF)); - raf.Seek(dataPtr, SeekOrigin.Begin); + _raf.Seek(dataPtr, SeekOrigin.Begin); byte[] data = new byte[dataLen]; - raf.Read(data, 0, data.Length); + _raf.Read(data, 0, data.Length); - int city_id = (int)Util.getIntLong(data, 0); + int city_id = (int)Utils.getIntLong(data, 0); String region = Encoding.UTF8.GetString(data, 4, data.Length - 4);// new String(data, 4, data.Length - 4, "UTF-8"); return new DataBlock(city_id, region, dataPtr); } - /** - * get the region throught the ip address with b-tree search algorithm - * - * @param ip - * @return DataBlock - * @throws IOException - */ - public DataBlock BtreeSearch(String ip) + /// + /// Get the region throught the ip address with b-tree search algorithm. + /// + public DataBlock BtreeSearch(string ip) { - return BtreeSearch(Util.ip2long(ip)); + return BtreeSearch(Utils.ip2long(ip)); } - /** - * get the region with a int ip address with binary search algorithm - * - * @param ip - * @throws IOException -*/ - public DataBlock BinarySearch(long ip) + /// + /// Get the region with a int ip address with binary search algorithm. + /// + private DataBlock BinarySearch(long ip) { - int blen = IndexBlock.GetIndexBlockLength(); - if (totalIndexBlocks == 0) + int blen = IndexBlock.LENGTH; + if (_totalIndexBlocks == 0) { - raf.Seek(0L, SeekOrigin.Begin); + _raf.Seek(0L, SeekOrigin.Begin); byte[] superBytes = new byte[8]; - raf.Read(superBytes, 0, superBytes.Length); + _raf.Read(superBytes, 0, superBytes.Length); //initialize the global vars - firstIndexPtr = Util.getIntLong(superBytes, 0); - lastIndexPtr = Util.getIntLong(superBytes, 4); - totalIndexBlocks = (int)((lastIndexPtr - firstIndexPtr) / blen) + 1; + _firstIndexPtr = Utils.getIntLong(superBytes, 0); + _lastIndexPtr = Utils.getIntLong(superBytes, 4); + _totalIndexBlocks = (int)((_lastIndexPtr - _firstIndexPtr) / blen) + 1; } //search the index blocks to define the data - int l = 0, h = totalIndexBlocks; - byte[] - buffer = new byte[blen]; - long sip, eip, dataptr = 0; + int l = 0, h = _totalIndexBlocks; + byte[] buffer = new byte[blen]; + long sip = 0; + while (l <= h) { int m = (l + h) >> 1; - raf.Seek(firstIndexPtr + m * blen, SeekOrigin.Begin); //set the file pointer - raf.Read(buffer, 0, buffer.Length); - sip = Util.getIntLong(buffer, 0); + _raf.Seek(_firstIndexPtr + m * blen, SeekOrigin.Begin); //set the file pointer + _raf.Read(buffer, 0, buffer.Length); + sip = Utils.getIntLong(buffer, 0); if (ip < sip) { h = m - 1; } else { - eip = Util.getIntLong(buffer, 4); - if (ip > eip) + sip = Utils.getIntLong(buffer, 4); + if (ip > sip) { l = m + 1; } else { - dataptr = Util.getIntLong(buffer, 8); + sip = Utils.getIntLong(buffer, 8); break; } } } //not matched - if (dataptr == 0) return null; + if (sip == 0) return null; //get the data - int dataLen = (int)((dataptr >> 24) & 0xFF); - int dataPtr = (int)((dataptr & 0x00FFFFFF)); + int dataLen = (int)((sip >> 24) & 0xFF); + int dataPtr = (int)((sip & 0x00FFFFFF)); - raf.Seek(dataPtr, SeekOrigin.Begin); + _raf.Seek(dataPtr, SeekOrigin.Begin); byte[] data = new byte[dataLen]; - raf.Read(data, 0, data.Length); - - int city_id = (int)Util.getIntLong(data, 0); + _raf.Read(data, 0, data.Length); + + int city_id = (int)Utils.getIntLong(data, 0); String region = Encoding.UTF8.GetString(data, 4, data.Length - 4);//new String(data, 4, data.Length - 4, "UTF-8"); return new DataBlock(city_id, region, dataPtr); - } - - /** - * get the region throught the ip address with binary search algorithm - * - * @param ip - * @return DataBlock - * @throws IOException - */ + } + + /// + /// Get the region throught the ip address with binary search algorithm. + /// public DataBlock BinarySearch(String ip) { - return BinarySearch(Util.ip2long(ip)); - } + return BinarySearch(Utils.ip2long(ip)); + } + + #endregion + + #region Async Methods - /** - * get the db config - * - * @return DbConfig -*/ - public DbConfig GetDbConfig() + /// + /// Get the region throught the ip address with memory binary search algorithm. + /// + public async Task AsyncMemorySearch(string ip) { - return dbConfig; + return await Task.FromResult(MemorySearch(ip)); } - - /** - * close the db - * - * @throws IOException -*/ + /// + /// Get the region throught the ip address with b-tree search algorithm. + /// + public async Task AsyncBtreeSearch(string ip) + { + return await Task.FromResult(BtreeSearch(ip)); + } + + /// + /// Get the region throught the ip address with binary search algorithm. + /// + public async Task AsyncBinarySearch(string ip) + { + return await Task.FromResult(BinarySearch(ip)); + } + + #endregion + /// + /// Close the db. + /// public void Close() { - HeaderSip = null; //let gc do its work - HeaderPtr = null; - dbBinStr = null; - raf.Close(); + _headerSip = null; //let gc do its work + _headerPtr = null; + _dbBinStr = null; + _raf.Close(); + } + + public void Dispose() + { + Close(); } - } } \ No newline at end of file diff --git a/binding/c#/IP2Region/DataBlock.cs b/binding/c#/IP2Region/DataBlock.cs deleted file mode 100644 index 99cc4ad..0000000 --- a/binding/c#/IP2Region/DataBlock.cs +++ /dev/null @@ -1,92 +0,0 @@ -//******************************* -// Create By Rocher Kong -// Github https://github.com/RocherKong -// Date 2018.02.09 -//******************************* -using System; -using System.Text; - -namespace IP2Region -{ - public class DataBlock - { - /** - * city id - */ - private int city_id; - - /** - * region address - */ - private String region; - - /** - * region ptr in the db file - */ - private int dataPtr; - - /** - * construct method - * - * @param city_id - * @param region region string - * @param ptr data ptr - */ - public DataBlock(int city_id, String region, int dataPtr) - { - this.city_id = city_id; - this.region = region; - this.dataPtr = dataPtr; - } - - public DataBlock(int city_id, String region) - { - this.city_id = city_id; - this.region = region; - this.dataPtr = 0; - } - - public int GetCityId() - { - return city_id; - } - - public DataBlock SetCityId(int city_id) - { - this.city_id = city_id; - return this; - } - - public String GetRegion() - { - return region; - } - - public DataBlock SetRegion(String region) - { - this.region = region; - return this; - } - - public int GetDataPtr() - { - return dataPtr; - } - - public DataBlock SetDataPtr(int dataPtr) - { - this.dataPtr = dataPtr; - return this; - } - - public override String ToString() - { - StringBuilder sb = new StringBuilder(); - - sb.Append(city_id).Append('|').Append(region).Append('|').Append(dataPtr); - return sb.ToString(); - } - - } - -} diff --git a/binding/c#/IP2Region/DbConfig.cs b/binding/c#/IP2Region/DbConfig.cs deleted file mode 100644 index 830b4fd..0000000 --- a/binding/c#/IP2Region/DbConfig.cs +++ /dev/null @@ -1,69 +0,0 @@ -//******************************* -// Create By Rocher Kong -// Github https://github.com/RocherKong -// Date 2018.02.09 -//******************************* -using System; - -namespace IP2Region -{ - public class DbConfig - { - /** - * total header data block size - */ - private int totalHeaderSize; - - /** - * max index data block size - * u should always choice the fastest read block size - */ - private int indexBlockSize; - - /** - * construct method - * - * @param totalHeaderSize - * @param dataBlockSize - * @throws DbMakerConfigException - */ - public DbConfig(int totalHeaderSize) - { - if ((totalHeaderSize % 8) != 0) - { - throw new DbMakerConfigException("totalHeaderSize must be times of 8"); - } - this.totalHeaderSize = totalHeaderSize; - this.indexBlockSize = 8192; //4 * 2048 - } - - public DbConfig() - { - this.totalHeaderSize = 8 * 2048; - this.indexBlockSize = 8192; - } - - public int GetTotalHeaderSize() - { - return totalHeaderSize; - } - - public DbConfig SetTotalHeaderSize(int totalHeaderSize) - { - this.totalHeaderSize = totalHeaderSize; - return this; - } - - public int GetIndexBlockSize() - { - return indexBlockSize; - } - - public DbConfig SetIndexBlockSize(int dataBlockSize) - { - this.indexBlockSize = dataBlockSize; - return this; - } - } - -} \ No newline at end of file diff --git a/binding/c#/IP2Region/DbMakerConfigException.cs b/binding/c#/IP2Region/DbMakerConfigException.cs deleted file mode 100644 index 9c25ac2..0000000 --- a/binding/c#/IP2Region/DbMakerConfigException.cs +++ /dev/null @@ -1,16 +0,0 @@ -//******************************* -// Create By Rocher Kong -// Github https://github.com/RocherKong -// Date 2018.02.09 -//******************************* -namespace IP2Region -{ - public class DbMakerConfigException : System.Exception - { - public string ErrorCode { get; set; } - public DbMakerConfigException(string ErrorCode) - { - this.ErrorCode=ErrorCode; - } - } -} \ No newline at end of file diff --git a/binding/c#/IP2Region/HeaderBlock.cs b/binding/c#/IP2Region/HeaderBlock.cs deleted file mode 100644 index 095014b..0000000 --- a/binding/c#/IP2Region/HeaderBlock.cs +++ /dev/null @@ -1,73 +0,0 @@ -//******************************* -// Create By Rocher Kong -// Github https://github.com/RocherKong -// Date 2018.02.09 -//******************************* - -using System; - -namespace IP2Region -{ - - public class HeaderBlock - { - /** - * index block start ip address - */ - private long indexStartIp; - - /** - * ip address - */ - private int indexPtr; - - public HeaderBlock(long indexStartIp, int indexPtr) - { - this.indexStartIp = indexStartIp; - this.indexPtr = indexPtr; - } - - public long GetIndexStartIp() - { - return indexStartIp; - } - - public HeaderBlock SetIndexStartIp(long indexStartIp) - { - this.indexStartIp = indexStartIp; - return this; - } - - public int GetIndexPtr() - { - return indexPtr; - } - - public HeaderBlock SetIndexPtr(int indexPtr) - { - this.indexPtr = indexPtr; - return this; - } - - /** - * get the bytes for db storage - * - * @return byte[] - */ - public byte[] GetBytes() - { - /* - * +------------+-----------+ - * | 4bytes | 4bytes | - * +------------+-----------+ - * start ip index ptr - */ - byte[] b = new byte[8]; - - Util.writeIntLong(b, 0, indexStartIp); - Util.writeIntLong(b, 4, indexPtr); - - return b; - } - } -} \ No newline at end of file diff --git a/binding/c#/IP2Region/IP2Region.csproj b/binding/c#/IP2Region/IP2Region.csproj index 00fa2b2..7219992 100644 --- a/binding/c#/IP2Region/IP2Region.csproj +++ b/binding/c#/IP2Region/IP2Region.csproj @@ -13,6 +13,10 @@ 1.1.0 + + 7 + + Always diff --git a/binding/c#/IP2Region/IndexBlock.cs b/binding/c#/IP2Region/IndexBlock.cs deleted file mode 100644 index 7b5319b..0000000 --- a/binding/c#/IP2Region/IndexBlock.cs +++ /dev/null @@ -1,117 +0,0 @@ -//******************************* -// Create By Rocher Kong -// Github https://github.com/RocherKong -// Date 2018.02.09 -//******************************* -using System; - -namespace IP2Region -{ - - public class IndexBlock - { - private static int LENGTH = 12; - - /** - * start ip address - */ - private long startIp; - - /** - * end ip address - */ - private long endIp; - - /** - * data ptr and data length - */ - private uint dataPtr; - - /** - * data length - */ - private int dataLen; - - public IndexBlock(long startIp, long endIp, uint dataPtr, int dataLen) - { - this.startIp = startIp; - this.endIp = endIp; - this.dataPtr = dataPtr; - this.dataLen = dataLen; - } - - public long GetStartIp() - { - return startIp; - } - - public IndexBlock SetStartIp(long startIp) - { - this.startIp = startIp; - return this; - } - - public long GetEndIp() - { - return endIp; - } - - public IndexBlock SetEndIp(long endIp) - { - this.endIp = endIp; - return this; - } - - public uint GetDataPtr() - { - return dataPtr; - } - - public IndexBlock SetDataPtr(uint dataPtr) - { - this.dataPtr = dataPtr; - return this; - } - - public int GetDataLen() - { - return dataLen; - } - - public IndexBlock SetDataLen(int dataLen) - { - this.dataLen = dataLen; - return this; - } - - public static int GetIndexBlockLength() - { - return LENGTH; - } - - /** - * get the bytes for storage - * - * @return byte[] - */ - public byte[] GetBytes() - { - /* - * +------------+-----------+-----------+ - * | 4bytes | 4bytes | 4bytes | - * +------------+-----------+-----------+ - * start ip end ip data ptr + len - */ - byte[] b = new byte[12]; - - Util.writeIntLong(b, 0, startIp); //start ip - Util.writeIntLong(b, 4, endIp); //end ip - - //write the data ptr and the length - long mix = dataPtr | ((dataLen << 24) & 0xFF000000L); - Util.writeIntLong(b, 8, mix); - - return b; - } - } -} \ No newline at end of file diff --git a/binding/c#/IP2Region/Models/DataBlock.cs b/binding/c#/IP2Region/Models/DataBlock.cs new file mode 100644 index 0000000..db6b19a --- /dev/null +++ b/binding/c#/IP2Region/Models/DataBlock.cs @@ -0,0 +1,55 @@ +//******************************* +// Create By Rocher Kong +// Github https://github.com/RocherKong +// Date 2018.02.09 +// +// Modified By Dongwei +// Date 2018.07.18 +// GitHub https://github.com/Maledong +//******************************* + +namespace IP2Region.Models +{ + public class DataBlock + { + #region Private Properties + public int CityID + { + get; + private set; + } + + public string Region + { + get; + private set; + } + + public int DataPtr + { + get; + private set; + } + #endregion + + #region Constructor + public DataBlock(int city_id, string region, int dataPtr = 0) + { + CityID = city_id; + Region = region; + DataPtr = dataPtr; + } + + public DataBlock(int city_id, string region):this(city_id,region,0) + { + } + #endregion + + public override string ToString() + { + return $"{CityID}|{Region}|{DataPtr}"; + } + + } + +} diff --git a/binding/c#/IP2Region/Models/DbConfig.cs b/binding/c#/IP2Region/Models/DbConfig.cs new file mode 100644 index 0000000..d823d34 --- /dev/null +++ b/binding/c#/IP2Region/Models/DbConfig.cs @@ -0,0 +1,54 @@ +//******************************* +// Create By Rocher Kong +// Github https://github.com/RocherKong +// Date 2018.02.09 +// +// Modified By Dongwei +// Date 2018.07.18 +// GitHub https://github.com/Maledong +//******************************* + +using System; + +namespace IP2Region.Models +{ + public class DbMakerConfigException : Exception + { + public string ErrMsg { get; private set; } + public DbMakerConfigException(string errMsg) + { + ErrMsg = errMsg; + } + } + + public class DbConfig + { + public int TotalHeaderSize + { + get; + private set; + } + + public int indexBlockSize + { + get; + private set; + } + + public DbConfig(int totalHeaderSize) + { + if ((totalHeaderSize % 8) != 0) + { + throw new DbMakerConfigException("totalHeaderSize must be times of 8"); + } + TotalHeaderSize = totalHeaderSize; + //4 * 2048 + indexBlockSize = 8192; + } + + public DbConfig():this(8 * 2048) + { + } + } + +} \ No newline at end of file diff --git a/binding/c#/IP2Region/Models/HeaderBlock.cs b/binding/c#/IP2Region/Models/HeaderBlock.cs new file mode 100644 index 0000000..24de495 --- /dev/null +++ b/binding/c#/IP2Region/Models/HeaderBlock.cs @@ -0,0 +1,56 @@ +//******************************* +// Create By Rocher Kong +// Github https://github.com/RocherKong +// Date 2018.02.09 +// +// Modified By Dongwei +// Date 2018.07.18 +// GitHub https://github.com/Maledong +//******************************* + + +namespace IP2Region.Models +{ + internal class HeaderBlock + { + public long IndexStartIp + { + get; + private set; + } + + public int IndexPtr + { + get; + private set; + } + + public HeaderBlock(long indexStartIp, int indexPtr) + { + IndexStartIp = indexStartIp; + IndexPtr = indexPtr; + } + + /// + /// Get the bytes for total storage + /// + /// + /// Bytes gotten. + /// + public byte[] GetBytes() + { + /* + * +------------+-----------+ + * | 4bytes | 4bytes | + * +------------+-----------+ + * start ip index ptr + */ + byte[] b = new byte[8]; + + Utils.writeIntLong(b, 0, IndexStartIp); + Utils.writeIntLong(b, 4, IndexPtr); + + return b; + } + } +} \ No newline at end of file diff --git a/binding/c#/IP2Region/Models/IndexBlock.cs b/binding/c#/IP2Region/Models/IndexBlock.cs new file mode 100644 index 0000000..ce0679e --- /dev/null +++ b/binding/c#/IP2Region/Models/IndexBlock.cs @@ -0,0 +1,69 @@ +//******************************* +// Create By Rocher Kong +// Github https://github.com/RocherKong +// Date 2018.02.09 +// +// Modified By Dongwei +// Date 2018.07.18 +// GitHub https://github.com/Maledong +//******************************* + +namespace IP2Region +{ + internal class IndexBlock + { + public const int LENGTH = 12; + + public long StartIP + { + get; + private set; + } + + public long EndIp + { + get; + private set; + } + + public uint DataPtr + { + get; + private set; + } + + public int DataLen + { + get; + private set; + } + + public IndexBlock(long startIp, long endIp, uint dataPtr, int dataLen) + { + StartIP = startIp; + EndIp = endIp; + DataPtr = dataPtr; + DataLen = dataLen; + } + + public byte[] GetBytes() + { + /* + * +------------+-----------+-----------+ + * | 4bytes | 4bytes | 4bytes | + * +------------+-----------+-----------+ + * start ip end ip data ptr + len + */ + byte[] b = new byte[12]; + + Utils.writeIntLong(b, 0, StartIP); //start ip + Utils.writeIntLong(b, 4, EndIp); //end ip + + //write the data ptr and the length + long mix = DataPtr | ((DataLen << 24) & 0xFF000000L); + Utils.writeIntLong(b, 8, mix); + + return b; + } + } +} \ No newline at end of file diff --git a/binding/c#/IP2Region/Util.cs b/binding/c#/IP2Region/Utils.cs similarity index 55% rename from binding/c#/IP2Region/Util.cs rename to binding/c#/IP2Region/Utils.cs index 7d526d4..9d6d881 100644 --- a/binding/c#/IP2Region/Util.cs +++ b/binding/c#/IP2Region/Utils.cs @@ -2,53 +2,48 @@ // Create By Rocher Kong // Github https://github.com/RocherKong // Date 2018.02.09 +// +// Modified By Dongwei +// Date 2018.07.18 +// GitHub https://github.com/Maledong //******************************* using System; -using System.Globalization; -using System.Text; namespace IP2Region { - - public class Util + public class IPInValidException : Exception + { + const string ERROR_MSG = "IP Illigel. Please input a valid IP."; + + public IPInValidException() : base(ERROR_MSG) { } + } + internal static class Utils { - /** - * write specfield bytes to a byte array start from offset - * - * @param b - * @param offset - * @param v - * @param bytes - */ - public static void write(byte[] b, int offset, ulong v, int bytes) + /// + /// Write specfield bytes to a byte array start from offset. + /// + public static void Write(byte[] b, int offset, ulong v, int bytes) { for (int i = 0; i < bytes; i++) { b[offset++] = (byte)((v >> (8 * i)) & 0xFF); } - } - - /** - * write a int to a byte array - * - * @param b - * @param offet - * @param v - */ + } + + /// + /// Write a int to a byte array. + /// public static void writeIntLong(byte[] b, int offset, long v) { b[offset++] = (byte)((v >> 0) & 0xFF); b[offset++] = (byte)((v >> 8) & 0xFF); b[offset++] = (byte)((v >> 16) & 0xFF); b[offset] = (byte)((v >> 24) & 0xFF); - } - - /** - * get a int from a byte array start from the specifiled offset - * - * @param b - * @param offset - */ + } + + /// + /// Get a int from a byte array start from the specifiled offset. + /// public static long getIntLong(byte[] b, int offset) { return ( @@ -59,12 +54,9 @@ namespace IP2Region ); } - /** - * get a int from a byte array start from the specifield offset - * - * @param b - * @param offset - */ + /// + /// Get a int from a byte array start from the specifield offset. + /// public static int getInt3(byte[] b, int offset) { return ( @@ -89,16 +81,23 @@ namespace IP2Region ); } - /** - * string ip to long ip - * - * @param ip - * @return long - */ + /// + /// String ip to long ip. + /// public static long ip2long(string ip) - { + { string[] p = ip.Split('.'); - if (p.Length != 4) return 0; + if (p.Length != 4) throw new IPInValidException(); + + foreach (string pp in p) + { + if (pp.Length > 3) throw new IPInValidException(); + if (!int.TryParse(pp, out int value) || value > 255) + { + throw new IPInValidException(); + } + } + var bip1 = long.TryParse(p[0], out long ip1); var bip2 = long.TryParse(p[1], out long ip2); var bip3 = long.TryParse(p[2], out long ip3); @@ -106,56 +105,25 @@ namespace IP2Region if (!bip1 || !bip2 || !bip3 || !bip4 || ip4 > 255 || ip1 > 255 || ip2 > 255 || ip3 > 255 - || ip4 < 1 || ip1 < 1 || ip2 < 1 || ip3 < 1) + || ip4 < 0 || ip1 < 0 || ip2 < 0 || ip3 < 0) { - throw new Exception("IP Illegal."); - } - + throw new IPInValidException(); + } + + long p1 = ((ip1 << 24) & 0xFF000000); long p2 = ((ip2 << 16) & 0x00FF0000); long p3 = ((ip3 << 8) & 0x0000FF00); long p4 = ((ip4 << 0) & 0x000000FF); return ((p1 | p2 | p3 | p4) & 0xFFFFFFFFL); - } - - /** - * int to ip string - * - * @param ip - * @return string - */ + } + + /// + /// Int to ip string. + /// public static string long2ip(long ip) { - StringBuilder sb = new StringBuilder(); - - sb - .Append((ip >> 24) & 0xFF).Append('.') - .Append((ip >> 16) & 0xFF).Append('.') - .Append((ip >> 8) & 0xFF).Append('.') - .Append((ip >> 0) & 0xFF); - - return sb.ToString(); - } - - /** - * check the validate of the specifeld ip address - * - * @param ip - * @return boolean - */ - public static Boolean isIpAddress(string ip) - { - string[] p = ip.Split('.'); - if (p.Length != 4) return false; - - foreach (string pp in p) - { - if (pp.Length > 3) return false; - int val = int.Parse(pp); - if (val > 255) return false; - } - - return true; + return $"{(ip >> 24) & 0xFF}.{(ip >> 16) & 0xFF}.{(ip >> 8) & 0xFF}.{ip & 0xFF}"; } } diff --git a/binding/c#/IP2Region_ConsoleTest/DBFile/ip2region.db b/binding/c#/IP2Region_ConsoleTest/DBFile/ip2region.db deleted file mode 100644 index 0bb25f6..0000000 Binary files a/binding/c#/IP2Region_ConsoleTest/DBFile/ip2region.db and /dev/null differ diff --git a/binding/c#/IP2Region_ConsoleTest/IP2Region_ConsoleTest.csproj b/binding/c#/IP2Region_ConsoleTest/IP2Region_ConsoleTest.csproj deleted file mode 100644 index cef353f..0000000 --- a/binding/c#/IP2Region_ConsoleTest/IP2Region_ConsoleTest.csproj +++ /dev/null @@ -1,24 +0,0 @@ - - - - Exe - netcoreapp2.0 - - - - - - - - - C:\Program Files (x86)\Microsoft SDKs\NuGetPackagesFallback\netstandard.library\2.0.1\build\netstandard2.0\ref\netstandard.dll - - - - - - Always - - - - diff --git a/binding/c#/IP2Region_ConsoleTest/Program.cs b/binding/c#/IP2Region_ConsoleTest/Program.cs deleted file mode 100644 index 722815c..0000000 --- a/binding/c#/IP2Region_ConsoleTest/Program.cs +++ /dev/null @@ -1,50 +0,0 @@ -using IP2Region; -using System; -using System.Diagnostics; - -namespace IP2Region_ConsoleTest -{ - class Program - { - static void Main(string[] args) - { - DbSearcher dbSearcher = new DbSearcher(new DbConfig - { - - }, AppContext.BaseDirectory + @"/DBFile/ip2region.db"); - string ipAddress = ""; - DataBlock result = null, result2 = null,result3=null; - Console.WriteLine("请输入IP地址:"); - ipAddress = Console.ReadLine(); - Stopwatch sw = new Stopwatch(); - do - { - sw.Start(); - try - { - result = dbSearcher.BtreeSearch(ipAddress); - sw.Stop(); - Console.WriteLine("[btree]你的IP所属区域为:" + result.GetRegion() + ";耗时:" + sw.Elapsed.TotalMilliseconds + "ms"); - sw.Start(); - result2 = dbSearcher.BinarySearch(ipAddress); - sw.Stop(); - Console.WriteLine("[binarySearch]你的IP所属区域为:" + result2.GetRegion() + ";耗时:" + sw.Elapsed.TotalMilliseconds + "ms"); - sw.Start(); - result3 = dbSearcher.MemorySearch(ipAddress); - sw.Stop(); - Console.WriteLine("[MemorySearch]你的IP所属区域为:" + result3.GetRegion() + ";耗时:" + sw.Elapsed.TotalMilliseconds + "ms"); - Console.WriteLine("请输入IP地址:"); - } - catch (Exception ex) - { - - Console.WriteLine(ex.Message); - sw.Stop(); - } - - //Console.WriteLine("结束请输入bye"); - - } while ((ipAddress = Console.ReadLine()) != "bye"); - } - } -} diff --git a/binding/c#/IP2Region_ConsoleTest/Properties/PublishProfiles/FolderProfile.pubxml b/binding/c#/IP2Region_ConsoleTest/Properties/PublishProfiles/FolderProfile.pubxml deleted file mode 100644 index 548b9af..0000000 --- a/binding/c#/IP2Region_ConsoleTest/Properties/PublishProfiles/FolderProfile.pubxml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - FileSystem - Release - netcoreapp2.0 - bin\Release\PublishOutput - - \ No newline at end of file diff --git a/binding/c#/IP2Region_ConsoleTest/Properties/PublishProfiles/FolderProfile.pubxml.user b/binding/c#/IP2Region_ConsoleTest/Properties/PublishProfiles/FolderProfile.pubxml.user deleted file mode 100644 index 4c24a80..0000000 --- a/binding/c#/IP2Region_ConsoleTest/Properties/PublishProfiles/FolderProfile.pubxml.user +++ /dev/null @@ -1,7 +0,0 @@ - - - - \ No newline at end of file diff --git a/binding/c#/README.md b/binding/c#/README.md new file mode 100644 index 0000000..e455346 --- /dev/null +++ b/binding/c#/README.md @@ -0,0 +1,18 @@ +# C# 客户端 + +## 实现情况: + +现已实现同步和异步查询,具体使用方法可以参考 UnitTests 中的使用方法。 + +## 如何贡献? + +你可以任意修改代码,但必须确保通过全部的单元测试。 +在此之前,请保证你已经安装了 Net4.6 和 Net Core 2.0 版本。 + +操作步骤: +1)使用 VS2017(推荐,可以到 https://visualstudio.microsoft.com/zh-hans/downloads/ 下载。),或者其它相关版本打开 IP2Region 项目。 +2)使用 VS 打开IP2Region 项目。 +3)保存 sln 文件到 c# 文件夹下(该文件应该自动被忽略)。 +4)右键点击“解决方案IP2Region”,添加 UnitTests 和 BenchmarkTest 项目。 +5)请右键点击整个解决方案,选择“重新生成解决方案”。 +6)菜单栏上“测试”=>“运行”=>“所有测试”,检测是否通过全部测试即可(你可以根据需要自行增加特定的单元测试)。 \ No newline at end of file diff --git a/binding/c#/UnitTests/ExceptionTest.cs b/binding/c#/UnitTests/ExceptionTest.cs new file mode 100644 index 0000000..1eb9552 --- /dev/null +++ b/binding/c#/UnitTests/ExceptionTest.cs @@ -0,0 +1,50 @@ +using IP2Region; +using IP2Region.Models; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace UnitTests +{ + /// + /// This test class is mainly used to test your exception types. + /// It should throw exceptions of differnt kinds of types on certain occations. + /// + [TestClass] + public class ExceptionTest + { + /// + /// Path error should throw "DbMakerConfigException" + /// + [TestMethod] + [ExpectedException(typeof(DbMakerConfigException))] + public void TestDbMakerConfigException() + { + using (var _dbSearcher = new DbSearcher(new DbConfig(255), "../../../../../data/ip2region.db")) { } + } + /// + /// Invalid IP should throw "IPInValidException" + /// + [TestMethod] + public void TestInvalidIP() + { + using (var _dbSearcher = new DbSearcher("../../../../../data/ip2region.db")) + { + var invalidIps = new string[] { "256.255.1.1", "-1.0.0.0", "192.168.4", "x.y.z" }; + var counter = 0; + + foreach (var item in invalidIps) + { + try + { + _dbSearcher.MemorySearch(item); + } + catch (IPInValidException) + { + counter++; + } + } + + Assert.AreEqual(counter, 4); + } + } + } +} diff --git a/binding/c#/UnitTests/NormalAsyncTest.cs b/binding/c#/UnitTests/NormalAsyncTest.cs new file mode 100644 index 0000000..4155d06 --- /dev/null +++ b/binding/c#/UnitTests/NormalAsyncTest.cs @@ -0,0 +1,148 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using IP2Region; +using System.Threading.Tasks; + +namespace UnitTests +{ + /// + /// This class is used to test with normal IPs (Chinese, Foreign and Special). + /// Use async functions for testing. + /// + [TestClass] + public class NormalAsyncTest + { + private static DbSearcher _dbSearcher = null; + + [ClassInitialize] + public static void Init(TestContext context) + { + _dbSearcher = new DbSearcher("../../../../../data/ip2region.db"); + } + + [ClassCleanup] + public static void ClearUp() + { + _dbSearcher.Dispose(); + } + + #region Normal Valid Chinese IP Test Cases + [TestMethod] + [TestCategory("Async Chinese Ip Test")] + public async Task AsyncBinarySearchTestForChinese() + { + const int cityId = 2163; + const string region = "中国|0|广东省|深圳市|鹏博士"; + + var result = await _dbSearcher.AsyncBinarySearch("101.105.35.57"); + Assert.AreEqual(result.CityID, cityId); + Assert.AreEqual(result.Region, region); + } + [TestMethod] + [TestCategory("Async Chinese Ip Test")] + public async Task AsyncBtreeSearchTestForChinese() + { + const int cityId = 2163; + const string region = "中国|0|广东省|深圳市|鹏博士"; + + var result = await _dbSearcher.AsyncBtreeSearch("101.105.35.57"); + Assert.AreEqual(result.CityID, cityId); + Assert.AreEqual(result.Region, region); + } + [TestMethod] + [TestCategory("Async Chinese Ip Test")] + public async Task AsyncMemorySearchTestForChinese() + { + const int cityId = 2163; + const string region = "中国|0|广东省|深圳市|鹏博士"; + + var result = await _dbSearcher.AsyncMemorySearch("101.105.35.57"); + Assert.AreEqual(result.CityID, cityId); + Assert.AreEqual(result.Region, region); + } + #endregion + + #region Normal Valid Foreign IP Test Cases + [TestMethod] + [TestCategory("Async Foreign Ip Test")] + public async Task AsyncBinarySearchTestForForeign() + { + const int cityId = 71; + const string region = "荷兰|0|0|0|0"; + + var result = await _dbSearcher.AsyncBinarySearch("86.84.21.60"); + Assert.AreEqual(result.CityID, cityId); + Assert.AreEqual(result.Region, region); + } + [TestMethod] + [TestCategory("Async Foreign Ip Test")] + public async Task AsyncBtreeSearchTestForForeign() + { + const int cityId = 71; + const string region = "荷兰|0|0|0|0"; + + var result = await _dbSearcher.AsyncBtreeSearch("86.84.21.60"); + Assert.AreEqual(result.CityID, cityId); + Assert.AreEqual(result.Region, region); + } + [TestMethod] + [TestCategory("Async Foreign Ip Test")] + public async Task AsyncMemorySearchTestForForeign() + { + const int cityId = 71; + const string region = "荷兰|0|0|0|0"; + + var result = await _dbSearcher.AsyncMemorySearch("86.84.21.60"); + Assert.AreEqual(result.CityID, cityId); + Assert.AreEqual(result.Region, region); + } + #endregion + + #region Normal Valid Special Ip Test Cases + [TestMethod] + [TestCategory("Async Special Ip Test")] + public async Task AsyncBinarySearchTestForSpecial() + { + const int cityId = 0; + const string region = "0|0|0|内网IP|内网IP"; + string[] specialIps = new string[] { "255.255.255.255", "0.0.0.0" }; + + foreach (var item in specialIps) + { + var result = await _dbSearcher.AsyncBinarySearch(item); + Assert.AreEqual(result.CityID, cityId); + Assert.AreEqual(result.Region, region); + } + } + [TestMethod] + [TestCategory("Async Special Ip Test")] + public async Task AsyncBtreeSearchTestForSpecial() + { + const int cityId = 0; + const string region = "0|0|0|内网IP|内网IP"; + string[] specialIps = new string[] { "255.255.255.255", "0.0.0.0" }; + + foreach (var item in specialIps) + { + var result = await _dbSearcher.AsyncBtreeSearch(item); + Assert.AreEqual(result.CityID, cityId); + Assert.AreEqual(result.Region, region); + } + } + [TestMethod] + [TestCategory("Async Special Ip Test")] + public async Task AsyncMemorySearchTestForSpecial() + { + const int cityId = 0; + const string region = "0|0|0|内网IP|内网IP"; + string[] specialIps = new string[] { "255.255.255.255", "0.0.0.0" }; + + foreach (var item in specialIps) + { + var result = await _dbSearcher.AsyncMemorySearch(item); + Assert.AreEqual(result.CityID, cityId); + Assert.AreEqual(result.Region, region); + } + } + #endregion + } +} diff --git a/binding/c#/UnitTests/NormalSyncTest.cs b/binding/c#/UnitTests/NormalSyncTest.cs new file mode 100644 index 0000000..628e585 --- /dev/null +++ b/binding/c#/UnitTests/NormalSyncTest.cs @@ -0,0 +1,146 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using IP2Region; +namespace UnitTests +{ + /// + /// This class is used to test with normal IPs (Chinese, Foreign and Special). + /// Use sync functions for testing. + /// + [TestClass] + public class NormalSyncTest + { + private static DbSearcher _dbSearcher = null; + + [ClassInitialize] + public static void Init(TestContext context) + { + _dbSearcher = new DbSearcher("../../../../../data/ip2region.db"); + } + + [ClassCleanup] + public static void ClearUp() + { + _dbSearcher.Dispose(); + } + + #region Normal Valid Chinese IP Test Cases + [TestMethod] + [TestCategory("Chinese Ip Test")] + public void BinarySearchTestForChinese() + { + const int cityId = 2163; + const string region = "中国|0|广东省|深圳市|鹏博士"; + + var result = _dbSearcher.BinarySearch("101.105.35.57"); + Assert.AreEqual(result.CityID, cityId); + Assert.AreEqual(result.Region, region); + } + [TestMethod] + [TestCategory("Chinese Ip Test")] + public void BtreeSearchTestForChinese() + { + const int cityId = 2163; + const string region = "中国|0|广东省|深圳市|鹏博士"; + + var result = _dbSearcher.BtreeSearch("101.105.35.57"); + Assert.AreEqual(result.CityID, cityId); + Assert.AreEqual(result.Region, region); + } + [TestMethod] + [TestCategory("Chinese Ip Test")] + public void MemorySearchTestForChinese() + { + const int cityId = 2163; + const string region = "中国|0|广东省|深圳市|鹏博士"; + + var result = _dbSearcher.MemorySearch("101.105.35.57"); + Assert.AreEqual(result.CityID, cityId); + Assert.AreEqual(result.Region, region); + } + #endregion + + #region Normal Valid Foreign IP Test Cases + [TestMethod] + [TestCategory("Foreign Ip Test")] + public void BinarySearchTestForForeign() + { + const int cityId = 71; + const string region = "荷兰|0|0|0|0"; + + var result = _dbSearcher.BinarySearch("86.84.21.60"); + Assert.AreEqual(result.CityID, cityId); + Assert.AreEqual(result.Region, region); + } + [TestMethod] + [TestCategory("Foreign Ip Test")] + public void BtreeSearchTestForForeign() + { + const int cityId = 71; + const string region = "荷兰|0|0|0|0"; + + var result = _dbSearcher.BtreeSearch("86.84.21.60"); + Assert.AreEqual(result.CityID, cityId); + Assert.AreEqual(result.Region, region); + } + [TestMethod] + [TestCategory("Foreign Ip Test")] + public void MemorySearchTestForForeign() + { + const int cityId = 71; + const string region = "荷兰|0|0|0|0"; + + var result = _dbSearcher.MemorySearch("86.84.21.60"); + Assert.AreEqual(result.CityID, cityId); + Assert.AreEqual(result.Region, region); + } + #endregion + + #region Normal Valid Special Ip Test Cases + [TestMethod] + [TestCategory("Special Ip Test")] + public void BinarySearchTestForSpecial() + { + const int cityId = 0; + const string region = "0|0|0|内网IP|内网IP"; + string[] specialIps = new string[] { "255.255.255.255", "0.0.0.0" }; + + foreach (var item in specialIps) + { + var result = _dbSearcher.BinarySearch(item); + Assert.AreEqual(result.CityID, cityId); + Assert.AreEqual(result.Region, region); + } + } + [TestMethod] + [TestCategory("Special Ip Test")] + public void BtreeSearchTestForSpecial() + { + const int cityId = 0; + const string region = "0|0|0|内网IP|内网IP"; + string[] specialIps = new string[] { "255.255.255.255", "0.0.0.0" }; + + foreach (var item in specialIps) + { + var result = _dbSearcher.BtreeSearch(item); + Assert.AreEqual(result.CityID, cityId); + Assert.AreEqual(result.Region, region); + } + } + [TestMethod] + [TestCategory("Special Ip Test")] + public void MemorySearchTestForSpecial() + { + const int cityId = 0; + const string region = "0|0|0|内网IP|内网IP"; + string[] specialIps = new string[] { "255.255.255.255", "0.0.0.0" }; + + foreach (var item in specialIps) + { + var result = _dbSearcher.MemorySearch(item); + Assert.AreEqual(result.CityID, cityId); + Assert.AreEqual(result.Region, region); + } + } + #endregion + } +} diff --git a/binding/c#/UnitTests/Properties/AssemblyInfo.cs b/binding/c#/UnitTests/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..4ecebe1 --- /dev/null +++ b/binding/c#/UnitTests/Properties/AssemblyInfo.cs @@ -0,0 +1,19 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +[assembly: AssemblyTitle("UnitTests")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("UnitTests")] +[assembly: AssemblyCopyright("Copyright © 2018")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +[assembly: ComVisible(false)] + +[assembly: Guid("38abc99f-cc14-47e3-9eeb-06c67d5ac33f")] + +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/binding/c#/UnitTests/UnitTests.csproj b/binding/c#/UnitTests/UnitTests.csproj new file mode 100644 index 0000000..4527142 --- /dev/null +++ b/binding/c#/UnitTests/UnitTests.csproj @@ -0,0 +1,76 @@ + + + + + Debug + AnyCPU + {38ABC99F-CC14-47E3-9EEB-06C67D5AC33F} + Library + Properties + UnitTests + UnitTests + v4.6 + 512 + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 15.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages + False + UnitTest + + + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\MSTest.TestFramework.1.2.1\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.dll + + + ..\packages\MSTest.TestFramework.1.2.1\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll + + + + + + + + + + + + + + + + {4c6032ee-43a5-4d5c-88f8-c411069e49e0} + IP2Region + + + + + + + 这台计算机上缺少此项目引用的 NuGet 程序包。使用“NuGet 程序包还原”可下载这些程序包。有关更多信息,请参见 http://go.microsoft.com/fwlink/?LinkID=322105。缺少的文件是 {0}。 + + + + + + \ No newline at end of file diff --git a/binding/c#/UnitTests/packages.config b/binding/c#/UnitTests/packages.config new file mode 100644 index 0000000..31240c1 --- /dev/null +++ b/binding/c#/UnitTests/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file