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