diff --git a/binding/c#/BenchmarkTest/App.config b/binding/c#/BenchmarkTest/App.config
deleted file mode 100644
index 1ab39519..00000000
--- a/binding/c#/BenchmarkTest/App.config
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/binding/c#/BenchmarkTest/BenchTests/BenchTestBase.cs b/binding/c#/BenchmarkTest/BenchTests/BenchTestBase.cs
deleted file mode 100644
index 835aed6d..00000000
--- a/binding/c#/BenchmarkTest/BenchTests/BenchTestBase.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-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
deleted file mode 100644
index f3e4fec6..00000000
--- a/binding/c#/BenchmarkTest/BenchTests/NormalBenchmarkTests.cs
+++ /dev/null
@@ -1,46 +0,0 @@
-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.MemorySearchAsync(validIp);
- }
-
- [Benchmark]
- public void TestSyncSpeedForBinarySearch()
- {
- _dbSearcher.BinarySearch(validIp);
- }
- [Benchmark]
- public async Task TestAsyncBinarySearch()
- {
- await _dbSearcher.BinarySearchAsync(validIp);
- }
-
- [Benchmark]
- public void TestSyncSpeedForBTreeSearch()
- {
- _dbSearcher.BtreeSearch(validIp);
- }
- [Benchmark]
- public async Task TestAsyncSpeedForBTreeSearch()
- {
- await _dbSearcher.BtreeSearchAsync(validIp);
- }
- }
-}
diff --git a/binding/c#/BenchmarkTest/IP2Region.Test.NetFxBenchmark.csproj b/binding/c#/BenchmarkTest/IP2Region.Test.NetFxBenchmark.csproj
deleted file mode 100644
index 3145a232..00000000
--- a/binding/c#/BenchmarkTest/IP2Region.Test.NetFxBenchmark.csproj
+++ /dev/null
@@ -1,154 +0,0 @@
-
-
-
-
- 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
deleted file mode 100644
index 44da3d15..00000000
--- a/binding/c#/BenchmarkTest/Program.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-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
deleted file mode 100644
index 6f7bdff7..00000000
--- a/binding/c#/BenchmarkTest/Properties/AssemblyInfo.cs
+++ /dev/null
@@ -1,35 +0,0 @@
-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
deleted file mode 100644
index 165b5a30..00000000
--- a/binding/c#/BenchmarkTest/packages.config
+++ /dev/null
@@ -1,54 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/binding/c#/IP2Region.Test.Benchmark/DbSearch_Test.cs b/binding/c#/IP2Region.Test.Benchmark/DbSearch_Test.cs
index 5c387607..f43ad229 100644
--- a/binding/c#/IP2Region.Test.Benchmark/DbSearch_Test.cs
+++ b/binding/c#/IP2Region.Test.Benchmark/DbSearch_Test.cs
@@ -1,9 +1,5 @@
using BenchmarkDotNet.Attributes;
-using BenchmarkDotNet.Order;
using IP2Region.Models;
-using System;
-using System.Collections.Generic;
-using System.Text;
using System.Threading.Tasks;
namespace IP2Region.Test.Benchmark
@@ -17,7 +13,6 @@ public class DbSearch_Test : TestBase
public DataBlock MemorySearch()
{
RandomIP = GetRandomIP();
- //Console.WriteLine(RandomIP);
return _search.MemorySearch(RandomIP);
}
diff --git a/binding/c#/IP2Region.Test.Benchmark/Program.cs b/binding/c#/IP2Region.Test.Benchmark/Program.cs
index 3c9d246a..7206d9cc 100644
--- a/binding/c#/IP2Region.Test.Benchmark/Program.cs
+++ b/binding/c#/IP2Region.Test.Benchmark/Program.cs
@@ -7,7 +7,7 @@ class Program
{
static void Main(string[] args)
{
- Console.WriteLine("Welcome To IP2Regin!");
+ Console.WriteLine("Now starting benchmark test, please wait......");
var summary = BenchmarkRunner.Run();
Console.ReadLine();
}
diff --git a/binding/c#/IP2Region.Test.Benchmark/TestBase.cs b/binding/c#/IP2Region.Test.Benchmark/TestBase.cs
index 233a33a9..369391d7 100644
--- a/binding/c#/IP2Region.Test.Benchmark/TestBase.cs
+++ b/binding/c#/IP2Region.Test.Benchmark/TestBase.cs
@@ -1,7 +1,5 @@
using BenchmarkDotNet.Attributes;
using System;
-using System.Collections.Generic;
-using System.Text;
namespace IP2Region.Test.Benchmark
{
@@ -17,7 +15,7 @@ public TestBase()
public TestBase(String DBFilePath)
{
- this._dBFilePath = DBFilePath;
+ _dBFilePath = DBFilePath;
}
[GlobalSetup]
@@ -37,7 +35,7 @@ public void Init()
[GlobalCleanup]
public void Dispose()
{
- _search?.Dispose();
+ _search.Dispose();
}
public String GetRandomIP()
diff --git a/binding/c#/IP2Region.Test.xUnit/SearchTest.cs b/binding/c#/IP2Region.Test.xUnit/SearchTest.cs
new file mode 100644
index 00000000..d4f1ec43
--- /dev/null
+++ b/binding/c#/IP2Region.Test.xUnit/SearchTest.cs
@@ -0,0 +1,51 @@
+using System;
+using System.Threading.Tasks;
+using Xunit;
+
+namespace IP2Region.Test.xUnit
+{
+ public class SearchTest : IDisposable
+ {
+ private readonly DbSearcher _search;
+
+ public SearchTest()
+ {
+ _search = new DbSearcher(Environment.CurrentDirectory + @"\DB\ip2region.db");
+ }
+ [Fact]
+ public void Search_Test()
+ {
+ string memResult = _search.MemorySearch("183.192.62.65").Region;
+ string binarySearchResult = _search.BinarySearch("183.192.62.65").Region;
+ string binaryTreeSearchResult = _search.BtreeSearch("183.192.62.65").Region;
+
+ Assert.NotNull(memResult);
+ Assert.NotNull(binarySearchResult);
+ Assert.NotNull(binaryTreeSearchResult);
+
+ Assert.Equal(memResult, binarySearchResult);
+ Assert.Equal(binaryTreeSearchResult, memResult);
+ }
+
+ [Fact]
+ public async Task SearchAsync_Test()
+ {
+ // We don't need the synchronizeContext, so just set to false
+ var memResult = await _search.MemorySearchAsync("183.192.62.65").ConfigureAwait(false);
+ var binarySearchResult = await _search.BinarySearchAsync("183.192.62.65").ConfigureAwait(false);
+ var bTreeSearchResult = await _search.BtreeSearchAsync("183.192.62.65").ConfigureAwait(false);
+
+ Assert.NotNull(memResult.Region);
+ Assert.NotNull(binarySearchResult.Region);
+ Assert.NotNull(bTreeSearchResult.Region);
+
+ Assert.Equal(memResult.Region, binarySearchResult.Region);
+ Assert.Equal(bTreeSearchResult.Region, memResult.Region);
+ }
+
+ public void Dispose()
+ {
+ _search.Dispose();
+ }
+ }
+}
diff --git a/binding/c#/IP2Region.Test.xUnit/UnitTest1.cs b/binding/c#/IP2Region.Test.xUnit/UnitTest1.cs
deleted file mode 100644
index 09a6f9ba..00000000
--- a/binding/c#/IP2Region.Test.xUnit/UnitTest1.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-using System;
-using System.Threading.Tasks;
-using Xunit;
-
-namespace IP2Region.Test.xUnit
-{
- public class UnitTest1
- {
- private readonly DbSearcher _search;
- public UnitTest1()
- {
- _search = new DbSearcher(Environment.CurrentDirectory + @"\DB\ip2region.db");
- }
- [Fact]
- public void Search_Test()
- {
-
- Assert.NotNull(_search.MemorySearch("183.192.62.65").Region);
- Assert.NotNull(_search.MemorySearchAsync("183.192.62.65").Result.Region);
- Assert.NotNull(_search.BinarySearch("183.192.62.65").Region);
- Assert.NotNull(_search.BinarySearchAsync("183.192.62.65").Result.Region);
- Assert.NotNull(_search.BtreeSearch("183.192.62.65").Region);
- Assert.NotNull(_search.BtreeSearchAsync("183.192.62.65").Result.Region);
-
- }
-
- [Fact]
- public async Task SearchAsync_Test()
- {
- var result = await _search.MemorySearchAsync("183.192.62.65");
- Assert.NotNull(result.Region);
- }
-
-
- }
-}
diff --git a/binding/c#/IP2Region/DBSearcher.cs b/binding/c#/IP2Region/DBSearcher.cs
index 80756b3d..b99dba86 100644
--- a/binding/c#/IP2Region/DBSearcher.cs
+++ b/binding/c#/IP2Region/DBSearcher.cs
@@ -360,23 +360,23 @@ public DataBlock BinarySearch(String ip)
///
/// Get the region throught the ip address with memory binary search algorithm.
///
- public async Task MemorySearchAsync(string ip)
+ public Task MemorySearchAsync(string ip)
{
- return await Task.FromResult(MemorySearch(ip));
+ return Task.FromResult(MemorySearch(ip));
}
///
/// Get the region throught the ip address with b-tree search algorithm.
///
- public async Task BtreeSearchAsync(string ip)
+ public Task BtreeSearchAsync(string ip)
{
- return await Task.FromResult(BtreeSearch(ip));
+ return Task.FromResult(BtreeSearch(ip));
}
///
/// Get the region throught the ip address with binary search algorithm.
///
- public async Task BinarySearchAsync(string ip)
+ public Task BinarySearchAsync(string ip)
{
- return await Task.FromResult(BinarySearch(ip));
+ return Task.FromResult(BinarySearch(ip));
}
#endregion
@@ -397,4 +397,4 @@ public void Dispose()
}
}
-}
\ No newline at end of file
+}
diff --git a/binding/c#/IP2Region_NetFx_Test/Program.cs b/binding/c#/IP2Region_NetFx_Test/Program.cs
index f0de3e1d..2b968009 100644
--- a/binding/c#/IP2Region_NetFx_Test/Program.cs
+++ b/binding/c#/IP2Region_NetFx_Test/Program.cs
@@ -1,9 +1,5 @@
using IP2Region;
using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
namespace IP2Region_NetFx_Test
{
@@ -11,9 +7,11 @@ class Program
{
static void Main(string[] args)
{
- DbSearcher _search = new DbSearcher(Environment.CurrentDirectory + @"\DB\ip2region.db");
- Console.WriteLine(_search.MemorySearch("183.192.62.65").Region);
- Console.Read();
+ using (var _search = new DbSearcher(Environment.CurrentDirectory + @"\DB\ip2region.db"))
+ {
+ Console.WriteLine(_search.MemorySearch("183.192.62.65").Region);
+ Console.Read();
+ }
}
}
}
diff --git a/binding/c#/IP2Region_NetFx_Test/Properties/AssemblyInfo.cs b/binding/c#/IP2Region_NetFx_Test/Properties/AssemblyInfo.cs
index 92c2b2d8..04e8005a 100644
--- a/binding/c#/IP2Region_NetFx_Test/Properties/AssemblyInfo.cs
+++ b/binding/c#/IP2Region_NetFx_Test/Properties/AssemblyInfo.cs
@@ -1,5 +1,4 @@
using System.Reflection;
-using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// 有关程序集的一般信息由以下
diff --git a/binding/c#/README.md b/binding/c#/README.md
index f7f22ab4..a3fb0e14 100644
--- a/binding/c#/README.md
+++ b/binding/c#/README.md
@@ -1,22 +1,30 @@
# IP2Region C# Client
## How To Use
-### 1.Install from Nuget. Support .Net Framework 4.5+ And netstandard 2.0(.net core)
+### 1.Install from Nuget. Support .Net Framework (>=4.5) And netstandard 2.0(.net core)
```powershell
Install-Package IP2Region
```
-### 2.Init DbSearcher with Newest DBFile Downloaded into your project.(https://github.com/lionsoul2014/ip2region/blob/master/data/ip2region.db)
-```c#
-DbSearcher _search=new DbSearcher(Environment.CurrentDirectory + @"\DB\ip2region.db");
-```
-### 3.Invoke Search Method.(MemorySearch,BtreeSearch,BinarySearch)
-```c#
+### 2.Init DbSearcher with [the newest DBFile](https://github.com/lionsoul2014/ip2region/blob/master/data/ip2region.db) Downloaded into your project, and invoke your search methods as below:
+```csharp
+using (var _search = new DbSearcher(Environment.CurrentDirectory + @"\DB\ip2region.db"))
+{
_search.MemorySearch("183.192.62.65").Region;
- _search.MemorySearchAsync("183.192.62.65").Result.Region;
_search.BinarySearch("183.192.62.65").Region;
- _search.BinarySearchAsync("183.192.62.65").Result.Region;
_search.BtreeSearch("183.192.62.65").Region;
- _search.BtreeSearchAsync("183.192.62.65").Result.Region;
+}
+```
+For async methods (Methods with the suffix "Async"), just put them with `async` and `await`.
+If you don't need the `SynchronizedContext`, please use `ConfigureAwait(false)`.
+For more about this, please read https://msdn.microsoft.com/en-us/magazine/jj991977.aspx.
+
+```csharp
+ public async Task SearchAsync_Test()
+ {
+ // We don't need the synchronizeContext, so just set to false.
+ // So as for BinarySearchAsync and BtreeSearchAsync
+ var memResult = await _search.MemorySearchAsync("183.192.62.65").ConfigureAwait(false);
+ }
```
## Test Result(From /IP2Region.Test.Benchmark)
@@ -28,11 +36,3 @@ BinarySearch | 52.22 us | 0.6403 us | 0.5347 us | 5 |
BinarySearch_Async | 53.03 us | 1.0271 us | 0.9608 us | 6 |
BtreeSearch | 19.05 us | 0.2464 us | 0.2305 us | 3 |
BtreeSearch_Async | 19.40 us | 0.3820 us | 0.6690 us | 4 |
-
-## Contribute History
-| Name | Github | Responsibility | Date | Remark |
-| ------ | ------ | ------ | ------ | ------ |
-| RocherKong | https://github.com/RocherKong | Creator | 20180209|
-| Dongwei | https://github.com/Maledong | Contributor | 20180708 | 1.Async 2.NetFxBenchmark 3.Rename of some Methods
-| RocherKong | https://github.com/RocherKong | Creator | 20180209| 1.CodeStandardized 2.Support Netfx4.5 3.TestStandardized
-
diff --git a/binding/c#/UnitTests/ExceptionTest.cs b/binding/c#/UnitTests/ExceptionTest.cs
deleted file mode 100644
index 1eb9552f..00000000
--- a/binding/c#/UnitTests/ExceptionTest.cs
+++ /dev/null
@@ -1,50 +0,0 @@
-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
deleted file mode 100644
index 4155d06e..00000000
--- a/binding/c#/UnitTests/NormalAsyncTest.cs
+++ /dev/null
@@ -1,148 +0,0 @@
-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
deleted file mode 100644
index 628e585e..00000000
--- a/binding/c#/UnitTests/NormalSyncTest.cs
+++ /dev/null
@@ -1,146 +0,0 @@
-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
deleted file mode 100644
index 4ecebe18..00000000
--- a/binding/c#/UnitTests/Properties/AssemblyInfo.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-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
deleted file mode 100644
index 45271428..00000000
--- a/binding/c#/UnitTests/UnitTests.csproj
+++ /dev/null
@@ -1,76 +0,0 @@
-
-
-
-
- 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
deleted file mode 100644
index 31240c15..00000000
--- a/binding/c#/UnitTests/packages.config
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/binding/nodejs/README.md b/binding/nodejs/README.md
index da52e32b..e6098f7a 100644
--- a/binding/nodejs/README.md
+++ b/binding/nodejs/README.md
@@ -1,3 +1,56 @@
# nodejs 客户端
-现已实现同步查询,异步查询未实现,用法参考 ip2region.spec.js
+## 实现情况:
+
+现已实现同步和异步查询,具体使用方法可以参考 `nodejs\tests\constructorTest.spec.js` 和`nodejs\tests\createTest.spec.js`。
+
+## 如何贡献?
+
+你可以任意修改代码,但必须确保通过全部的单元测试。要保证通过全部的单元测试,请在 Nodejs 控制台下切换到 nodejs 目录:
+
+1)在此之前,请先运行 `npm i` 确保你已经安装了各类初始化第三方工具。
+2)然后运行 `npm run coverage` 确保你的代码可以通过全部测试(必要时可以添加测试),同时保证代码覆盖率都是绿色(80%以上)。
+
+```bash
+D:\Projects\ip2region\binding\nodejs>npm run coverage
+
+> ip2region@0.0.1 coverage D:\Projects\ip2region\binding\nodejs
+> npm run test && jest --coverage
+
+
+> ip2region@0.0.1 test D:\Projects\ip2region\binding\nodejs
+> jest
+
+ PASS tests\constructorTest.spec.js
+ PASS tests\createTest.spec.js
+ PASS tests\exceptionTest.spec.js
+
+Snapshot Summary
+ › 168 snapshots written in 2 test suites.
+
+Test Suites: 3 passed, 3 total
+Tests: 14 passed, 14 total
+Snapshots: 168 added, 168 total
+Time: 1.645s
+Ran all test suites.
+ PASS tests\constructorTest.spec.js
+ PASS tests\createTest.spec.js
+ PASS tests\exceptionTest.spec.js
+----------------------|----------|----------|----------|----------|-------------------|
+File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
+----------------------|----------|----------|----------|----------|-------------------|
+All files | 92.34 | 80.77 | 96 | 93.83 | |
+ nodejs | 91.95 | 80.26 | 95.65 | 93.51 | |
+ ip2region.js | 91.95 | 80.26 | 95.65 | 93.51 |... 09,410,460,484 |
+ nodejs/tests/utils | 100 | 100 | 100 | 100 | |
+ asyncFor.js | 100 | 100 | 100 | 100 | |
+ fetchMainVersion.js | 100 | 100 | 100 | 100 | |
+ testData.js | 100 | 100 | 100 | 100 | |
+----------------------|----------|----------|----------|----------|-------------------|
+
+Test Suites: 3 passed, 3 total
+Tests: 14 passed, 14 total
+Snapshots: 168 passed, 168 total
+Time: 1.792s
+Ran all test suites.
+```
\ No newline at end of file
diff --git a/binding/nodejs/__snapshots__/ip2region.spec.js.snap b/binding/nodejs/__snapshots__/ip2region.spec.js.snap
deleted file mode 100644
index b738d52f..00000000
--- a/binding/nodejs/__snapshots__/ip2region.spec.js.snap
+++ /dev/null
@@ -1,29 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`ip2region binarySearch 1`] = `
-Object {
- "city": 2163,
- "region": "中国|0|广东省|深圳市|阿里云",
-}
-`;
-
-exports[`ip2region binarySearch 2`] = `
-Object {
- "city": 0,
- "region": "0|0|0|内网IP|内网IP",
-}
-`;
-
-exports[`ip2region should query 1`] = `
-Object {
- "city": 2163,
- "region": "中国|0|广东省|深圳市|阿里云",
-}
-`;
-
-exports[`ip2region should query 2`] = `
-Object {
- "city": 0,
- "region": "0|0|0|内网IP|内网IP",
-}
-`;
diff --git a/binding/nodejs/ip2region.js b/binding/nodejs/ip2region.js
index fd81a9bd..a704c534 100644
--- a/binding/nodejs/ip2region.js
+++ b/binding/nodejs/ip2region.js
@@ -5,20 +5,18 @@
*
* @author dongyado
* @author leeching
+ * @author dongwei
*/
const fs = require('fs');
-const IP_BASE = [16777216, 65536, 256, 1];
-const INDEX_BLOCK_LENGTH = 12;
-const TOTAL_HEADER_LENGTH = 8192;
-
+//#region Private Functions
/**
* Convert ip to long (xxx.xxx.xxx.xxx to a integer)
*
* @param {string} ip
* @return {number} long value
*/
-function ip2long(ip) {
+function _ip2long(ip) {
const arr = ip.split('.');
if (arr.length !== 4) {
throw new Error('invalid ip');
@@ -39,7 +37,7 @@ function ip2long(ip) {
* @param {number} offset
* @return {number} long value
*/
-function getLong(buffer, offset) {
+function _getLong(buffer, offset) {
const val =
(buffer[offset] & 0x000000ff) |
((buffer[offset + 1] << 8) & 0x0000ff00) |
@@ -48,82 +46,156 @@ function getLong(buffer, offset) {
return val < 0 ? val >>> 0 : val;
}
-/**
- * @typedef {Object} SearchResult
- * @property {number} city
- * @property {string} region
- */
+//#endregion
+
+//#region Private Variables
+
+// We don't wanna expose a private global settings to
+// the public for safety reason.
+const _globalInstances = new Map();
+
+const IP_BASE = [16777216, 65536, 256, 1];
+const INDEX_BLOCK_LENGTH = 12;
+const TOTAL_HEADER_LENGTH = 8192;
+
+// Private Message Symbols for functions
+const PrepareHeader = Symbol('#PrepareHeader');
+const CalTotalBlocks = Symbol('#CalsTotalBlocks');
+const ReadDataSync = Symbol('#ReadDataSync');
+const ReadData = Symbol('#ReadData');
+//#endregion
class IP2Region {
+
+ //#region Private Functions
+
+ [CalTotalBlocks]() {
+ const superBlock = new Buffer(8);
+ fs.readSync(this.dbFd, superBlock, 0, 8, 0);
+ this.firstIndexPtr = _getLong(superBlock, 0);
+ this.lastIndexPtr = _getLong(superBlock, 4);
+ this.totalBlocks =
+ (this.lastIndexPtr - this.firstIndexPtr) / INDEX_BLOCK_LENGTH + 1;
+ }
+
+ [PrepareHeader]() {
+ fs.readSync(
+ this.dbFd,
+ this.headerIndexBuffer,
+ 0,
+ TOTAL_HEADER_LENGTH,
+ 8
+ );
+
+ for (let i = 0; i < TOTAL_HEADER_LENGTH; i += 8) {
+ const startIp = _getLong(this.headerIndexBuffer, i);
+ const dataPtr = _getLong(this.headerIndexBuffer, i + 4);
+ if (dataPtr == 0) break;
+
+ this.headerSip.push(startIp);
+ this.headerPtr.push(dataPtr);
+ this.headerLen++; // header index size count
+ }
+ }
+
+ [ReadData](dataPos, callBack) {
+ if (dataPos == 0) return callBack(null,null);
+ const dataLen = (dataPos >> 24) & 0xff;
+ dataPos = dataPos & 0x00ffffff;
+ const dataBuffer = new Buffer(dataLen);
+
+ fs.read(this.dbFd, dataBuffer, 0, dataLen, dataPos, (err, result) => {
+ if (err) {
+ callBack(err, null);
+ }
+ else {
+ const city = _getLong(dataBuffer, 0);
+ const region = dataBuffer.toString('utf8', 4, dataLen);
+ callBack(null, { city, region });
+ }
+ });
+ }
+
+ [ReadDataSync](dataPos) {
+ if (dataPos == 0) return null;
+ const dataLen = (dataPos >> 24) & 0xff;
+ dataPos = dataPos & 0x00ffffff;
+ const dataBuffer = new Buffer(dataLen);
+
+ fs.readSync(this.dbFd, dataBuffer, 0, dataLen, dataPos);
+
+ const city = _getLong(dataBuffer, 0);
+ const region = dataBuffer.toString('utf8', 4, dataLen);
+
+ return { city, region };
+ }
+ //#endregion
+
+ //#region Static Functions
+
+ // Single Instance
static create(dbPath) {
- const oldInstance = IP2Region._instances.get(dbPath);
- if (oldInstance) {
- return oldInstance;
- } else {
- const instance = new IP2Region({ dbPath });
- IP2Region._instances.set(dbPath, instance);
- return instance;
+ let existInstance = _globalInstances.get(dbPath);
+ if (existInstance == null) {
+ existInstance = new IP2Region({ dbPath: dbPath });
}
+ return existInstance;
}
/**
* For backward compatibility
*/
static destroy() {
- IP2Region._instances.forEach(([key, instance]) => {
+ _globalInstances.forEach(([key, instance]) => {
instance.destroy();
});
}
+ //#endregion
+
constructor(options = {}) {
+
const { dbPath } = options;
- if (!dbPath || !fs.existsSync(dbPath)) {
- throw new Error(`[ip2region] db file not exists : ${dbPath}`);
- }
- try {
- this.dbFd = fs.openSync(dbPath, 'r');
- } catch (e) {
- throw new Error(
- `[ip2region] Can not open ip2region.db file , path: ${dbPath}`
- );
- }
+ this.dbFd = fs.openSync(dbPath, 'r');
- IP2Region._instances.set((this.dbPath = dbPath), this);
+ this.dbPath = dbPath;
+
+ _globalInstances.set(this.dbPath, this);
this.totalBlocks = this.firstIndexPtr = this.lastIndexPtr = 0;
- this.calcTotalBlocks();
+ this[CalTotalBlocks]();
this.headerIndexBuffer = new Buffer(TOTAL_HEADER_LENGTH);
this.headerSip = [];
this.headerPtr = [];
this.headerLen = 0;
- this.prepareHeader();
+ this[PrepareHeader]();
}
+ //#region Public Functions
/**
- * @public
+ * Destroy the current file by closing it.
*/
destroy() {
- fs.closeSync(ip2rObj.dbFd);
- IP2Region._instances.delete(this.dbPath);
+ fs.closeSync(this.dbFd);
+ _globalInstances.delete(this.dbPath);
}
/**
- * @public
- * @param {string} ip
- * @return {SearchResult}
+ * Sync of binarySearch.
+ * @param {string} ip The IP address to search for.
+ * @return {SearchResult} A result something like `{ city: 2163, region: '中国|0|广东省|深圳市|阿里云' }`
*/
binarySearchSync(ip) {
- ip = ip2long(ip);
+
+ ip = _ip2long(ip);
let low = 0;
let mid = 0;
let high = this.totalBlocks;
- let dataPos = 0;
let pos = 0;
let sip = 0;
- let eip = 0;
const indexBuffer = new Buffer(12);
// binary search
@@ -131,30 +203,89 @@ class IP2Region {
mid = (low + high) >> 1;
pos = this.firstIndexPtr + mid * INDEX_BLOCK_LENGTH;
fs.readSync(this.dbFd, indexBuffer, 0, INDEX_BLOCK_LENGTH, pos);
- sip = getLong(indexBuffer, 0);
+ sip = _getLong(indexBuffer, 0);
if (ip < sip) {
high = mid - 1;
} else {
- eip = getLong(indexBuffer, 4);
- if (ip > eip) {
+ sip = _getLong(indexBuffer, 4);
+ if (ip > sip) {
low = mid + 1;
} else {
- dataPos = getLong(indexBuffer, 8);
+ sip = _getLong(indexBuffer, 8);
break;
}
}
}
- return this.readData(dataPos);
+ return this[ReadDataSync](sip);
}
/**
- * @public
- * @param {string} ip
- * @return {SearchResult}
+ * Async of binarySearch.
+ * @param {string} ip The IP address to search for.
+ * @param {Function} callBack The callBack function with two parameters, if successful,
+ * err is null and result is `{ city: 2163, region: '中国|0|广东省|深圳市|阿里云' }`
+ */
+ binarySearch(ip, callBack) {
+
+ ip = _ip2long(ip);
+
+ let low = 0;
+ let mid = 0;
+ let high = this.totalBlocks;
+ let pos = 0;
+ let sip = 0;
+ const indexBuffer = new Buffer(12);
+ const _self = this;
+
+ // Because `while` is a sync method, we have to convert this to a recursive loop
+ // and in each loop we should continue calling `setImmediate` until we found the IP.
+ function _innerAsyncWhile() {
+ if (low <= high) {
+ mid = (low + high) >> 1;
+ pos = _self.firstIndexPtr + mid * INDEX_BLOCK_LENGTH;
+
+ // Now async read the file
+ fs.read(_self.dbFd, indexBuffer, 0, INDEX_BLOCK_LENGTH, pos, (err) => {
+
+ if (err) {
+ return callBack(err, null);
+ }
+
+ sip = _getLong(indexBuffer, 0);
+
+ if (ip < sip) {
+ high = mid - 1;
+ setImmediate(_innerAsyncWhile);
+ } else {
+ sip = _getLong(indexBuffer, 4);
+ if (ip > sip) {
+ low = mid + 1;
+ setImmediate(_innerAsyncWhile);
+ } else {
+ sip = _getLong(indexBuffer, 8);
+ _self[ReadData](sip, (err, result) => {
+ callBack(err, result);
+ });
+ }
+ }
+ });
+ }
+ }
+
+ // Call this immediately
+ _innerAsyncWhile();
+
+ }
+
+ /**
+ * Sync of btreeSearch.
+ * @param {string} ip The IP address to search for.
+ * @return {Function} A result something like `{ city: 2163, region: '中国|0|广东省|深圳市|阿里云' }`
*/
btreeSearchSync(ip) {
- ip = ip2long(ip);
+
+ ip = _ip2long(ip);
// first search (in header index)
let low = 0;
@@ -221,84 +352,143 @@ class IP2Region {
let p = 0;
let sip = 0;
- let eip = 0;
- let dataPtr = 0;
while (low <= high) {
mid = (low + high) >> 1;
p = mid * INDEX_BLOCK_LENGTH;
- sip = getLong(blockBuffer, p);
+ sip = _getLong(blockBuffer, p);
if (ip < sip) {
high = mid - 1;
} else {
- eip = getLong(blockBuffer, p + 4);
- if (ip > eip) {
+ sip = _getLong(blockBuffer, p + 4);
+ if (ip > sip) {
low = mid + 1;
} else {
- dataPtr = getLong(blockBuffer, p + 8);
+ sip = _getLong(blockBuffer, p + 8);
break;
}
}
}
- return this.readData(dataPtr);
+ return this[ReadDataSync](sip);
}
/**
- * @private
- */
- calcTotalBlocks() {
- const superBlock = new Buffer(8);
- fs.readSync(this.dbFd, superBlock, 0, 8, 0);
- this.firstIndexPtr = getLong(superBlock, 0);
- this.lastIndexPtr = getLong(superBlock, 4);
- this.totalBlocks =
- (this.lastIndexPtr - this.firstIndexPtr) / INDEX_BLOCK_LENGTH + 1;
- }
+ * Async of btreeSearch.
+ * @param {string} ip The IP address to search for.
+ * @param {Function} callBack The callBack function with two parameters, if successful,
+ * err is null and result is `{ city: 2163, region: '中国|0|广东省|深圳市|阿里云' }`
+ */
+ btreeSearch(ip, callBack) {
+ ip = _ip2long(ip);
- /**
- * @private
- */
- prepareHeader() {
- fs.readSync(
- this.dbFd,
- this.headerIndexBuffer,
- 0,
- TOTAL_HEADER_LENGTH,
- 8
- );
+ // first search (in header index)
+ let low = 0;
+ let mid = 0;
+ let high = this.headerLen;
+ let sptr = 0;
+ let eptr = 0;
- for (let i = 0; i < TOTAL_HEADER_LENGTH; i += 8) {
- const startIp = getLong(this.headerIndexBuffer, i);
- const dataPtr = getLong(this.headerIndexBuffer, i + 4);
- if (dataPtr == 0) break;
+ while (low <= high) {
+ mid = (low + high) >> 1;
- this.headerSip.push(startIp);
- this.headerPtr.push(dataPtr);
- this.headerLen++; // header index size count
+ if (ip == this.headerSip[mid]) {
+ if (mid > 0) {
+ sptr = this.headerPtr[mid - 1];
+ eptr = this.headerPtr[mid];
+ } else {
+ sptr = this.headerPtr[mid];
+ eptr = this.headerPtr[mid + 1];
+ }
+ break;
+ }
+
+ if (ip < this.headerSip[mid]) {
+ if (mid == 0) {
+ sptr = this.headerPtr[mid];
+ eptr = this.headerPtr[mid + 1];
+ break;
+ } else if (ip > this.headerSip[mid - 1]) {
+ sptr = this.headerPtr[mid - 1];
+ eptr = this.headerPtr[mid];
+ break;
+ }
+ high = mid - 1;
+ } else {
+ if (mid == this.headerLen - 1) {
+ sptr = this.headerPtr[mid - 1];
+ eptr = this.headerPtr[mid];
+ break;
+ } else if (ip <= this.headerSip[mid + 1]) {
+ sptr = this.headerPtr[mid];
+ eptr = this.headerPtr[mid + 1];
+ break;
+ }
+ low = mid + 1;
+ }
}
- }
- /**
- * @private
- * @param {number} dataPos
- * @return {SearchResult}
- */
- readData(dataPos) {
- if (dataPos == 0) return null;
- const dataLen = (dataPos >> 24) & 0xff;
- dataPos = dataPos & 0x00ffffff;
- const dataBuffer = new Buffer(dataLen);
+ // match nothing
+ if (sptr == 0) return callBack(null, null);
- fs.readSync(this.dbFd, dataBuffer, 0, dataLen, dataPos);
+ let p = 0;
+ let sip = 0;
- const city = getLong(dataBuffer, 0);
- const region = dataBuffer.toString('utf8', 4, dataLen);
+ // second search (in index)
+ const blockLen = eptr - sptr;
+ const blockBuffer = new Buffer(blockLen + INDEX_BLOCK_LENGTH);
+ low = 0;
+ high = blockLen / INDEX_BLOCK_LENGTH;
- return { city, region };
+ const _self = this;
+
+ function _innerAsyncWhile() {
+
+ if (low <= high) {
+
+ mid = (low + high) >> 1;
+ p = mid * INDEX_BLOCK_LENGTH;
+
+ // Use this to call the method itself as
+ // an asynchronize step
+ fs.read(_self.dbFd, blockBuffer,
+ 0,
+ blockLen + INDEX_BLOCK_LENGTH,
+ sptr, (err) => {
+
+ if (err) {
+ return callBack(err, null);
+ }
+
+ sip = _getLong(blockBuffer, p);
+
+ if (ip < sip) {
+ high = mid - 1;
+ setImmediate(_innerAsyncWhile);
+ } else {
+ sip = _getLong(blockBuffer, p + 4);
+ if (ip > sip) {
+ low = mid + 1;
+ setImmediate(_innerAsyncWhile);
+ } else {
+ sip = _getLong(blockBuffer, p + 8);
+ _self[ReadData](sip, (err, result) => {
+ callBack(err, result);
+ });
+ }
+ }
+ });
+ }
+ else {
+ // If we found nothing, return null
+ return callBack(null, null);
+ }
+ }
+
+ _innerAsyncWhile();
}
-}
-IP2Region._instances = new Map();
+ //#endregion
+}
module.exports = IP2Region;
diff --git a/binding/nodejs/ip2region.spec.js b/binding/nodejs/ip2region.spec.js
deleted file mode 100644
index 0982fd79..00000000
--- a/binding/nodejs/ip2region.spec.js
+++ /dev/null
@@ -1,24 +0,0 @@
-const path = require('path');
-const IP2Region = require('./ip2region');
-
-describe('ip2region', () => {
- let instance;
-
- beforeAll(() => {
- instance = IP2Region.create(path.join(__dirname, '../../data/ip2region.db'));
- });
-
- afterAll(() => {
- instance.destroy();
- });
-
- test('should query', () => {
- expect(instance.btreeSearchSync('120.24.78.68')).toMatchSnapshot();
- expect(instance.btreeSearchSync('10.10.10.10')).toMatchSnapshot();
- });
-
- test('binarySearch', () => {
- expect(instance.binarySearchSync('120.24.78.68')).toMatchSnapshot();
- expect(instance.binarySearchSync('10.10.10.10')).toMatchSnapshot();
- });
-});
diff --git a/binding/nodejs/package.json b/binding/nodejs/package.json
index 9accef06..e579b54b 100644
--- a/binding/nodejs/package.json
+++ b/binding/nodejs/package.json
@@ -4,7 +4,8 @@
"description": "ip database ",
"main": "ip2region.js",
"scripts": {
- "test": "jest"
+ "test": "jest",
+ "coverage":"npm run test && jest --coverage"
},
"repository": {
"type": "git",
diff --git a/binding/nodejs/tests/constructorTest.spec.js b/binding/nodejs/tests/constructorTest.spec.js
new file mode 100644
index 00000000..9e01bf3d
--- /dev/null
+++ b/binding/nodejs/tests/constructorTest.spec.js
@@ -0,0 +1,108 @@
+// This test is used for tesing of a static function `create` of IP2Region
+const IP2Region = require('../ip2region');
+const testIps = require('./utils/testData');
+const asyncFor = require('./utils/asyncFor');
+
+describe('Constructor Test', () => {
+ let instance;
+
+ beforeAll(() => {
+ instance = new IP2Region({ dbPath: '../../data/ip2region.db' });
+ });
+
+ afterAll(() => {
+ IP2Region.destroy();
+ });
+
+ test('btreeSearchSync query', () => {
+ for (const ip of testIps) {
+ expect(instance.btreeSearchSync(ip)).toMatchSnapshot();
+ }
+ });
+
+ test('binarySearchSync query', () => {
+ for (const ip of testIps) {
+ expect(instance.binarySearchSync(ip)).toMatchSnapshot();
+ }
+ });
+
+
+ //#region callBack
+ test('binarySearch query', (done) => {
+ asyncFor(testIps,
+ (value, continueCallBack) => {
+ instance.binarySearch(value, (err, result) => {
+ expect(err).toBe(null);
+ expect(result).toMatchSnapshot();
+ continueCallBack();
+ });
+ },
+ () => { done() });
+ });
+
+ test('btreeSearch query', (done) => {
+ asyncFor(testIps,
+ (value, continueCallBack) => {
+ instance.btreeSearch(value, (err, result) => {
+ expect(err).toBe(null);
+ expect(result).toMatchSnapshot();
+ continueCallBack();
+ });
+ },
+ () => { done() });
+ });
+
+ //#endregion
+
+ //#region Async Promisify test
+ const node_ver = require('./utils/fetchMainVersion');
+
+ // If we have Nodejs >= 8, we now support `async` and `await`
+ if (node_ver >= 8) {
+
+ const asyncBinarySearch = async (ip) => {
+
+ return new Promise((resolve, reject) => {
+ instance.binarySearch(ip, (err, result) => {
+ if (err) {
+ reject(err);
+ }
+ else {
+ resolve(result);
+ }
+ });
+ });
+
+ };
+
+ const asyncBtreeSearch = async (ip) => {
+
+ return new Promise((resolve, reject) => {
+ instance.btreeSearch(ip, (err, result) => {
+ if (err) {
+ reject(err);
+ }
+ else {
+ resolve(result);
+ }
+ });
+ });
+
+ };
+
+ test('async binarySearch query', async () => {
+ for (let i = 0; i < testIps.length; ++i) {
+ const result = await asyncBinarySearch(testIps[i]);
+ expect(result).toMatchSnapshot();
+ }
+ });
+
+ test('async btreeSearch query', async () => {
+ for (let i = 0; i < testIps.length; ++i) {
+ const result = await asyncBtreeSearch(testIps[i]);
+ expect(result).toMatchSnapshot();
+ }
+ });
+ }
+ //#endregion
+});
diff --git a/binding/nodejs/tests/createTest.spec.js b/binding/nodejs/tests/createTest.spec.js
new file mode 100644
index 00000000..ca4302ab
--- /dev/null
+++ b/binding/nodejs/tests/createTest.spec.js
@@ -0,0 +1,108 @@
+// This test is used for tesing of a static function `create` of IP2Region
+const IP2Region = require('../ip2region');
+const testIps = require('./utils/testData');
+const asyncFor = require('./utils/asyncFor');
+
+describe('Create Test', () => {
+ let instance;
+
+ beforeAll(() => {
+ instance = IP2Region.create('../../data/ip2region.db');
+ });
+
+ afterAll(() => {
+ IP2Region.destroy();
+ });
+
+ test('btreeSearchSync query', () => {
+ for (const ip of testIps) {
+ expect(instance.btreeSearchSync(ip)).toMatchSnapshot();
+ }
+ });
+
+ test('binarySearchSync query', () => {
+ for (const ip of testIps) {
+ expect(instance.binarySearchSync(ip)).toMatchSnapshot();
+ }
+ });
+
+
+ //#region callBack
+ test('binarySearch query', (done) => {
+ asyncFor(testIps,
+ (value, continueCallBack) => {
+ instance.binarySearch(value, (err, result) => {
+ expect(err).toBe(null);
+ expect(result).toMatchSnapshot();
+ continueCallBack();
+ });
+ },
+ () => { done() });
+ });
+
+ test('btreeSearch query', (done) => {
+ asyncFor(testIps,
+ (value, continueCallBack) => {
+ instance.btreeSearch(value, (err, result) => {
+ expect(err).toBe(null);
+ expect(result).toMatchSnapshot();
+ continueCallBack();
+ });
+ },
+ () => { done() });
+ });
+
+ //#endregion
+
+ //#region Async Promisify test
+ const node_ver = require('./utils/fetchMainVersion');
+
+ // If we have Nodejs >= 8, we now support `async` and `await`
+ if (node_ver >= 8) {
+
+ const asyncBinarySearch = async (ip) => {
+
+ return new Promise((resolve, reject) => {
+ instance.binarySearch(ip, (err, result) => {
+ if (err) {
+ reject(err);
+ }
+ else {
+ resolve(result);
+ }
+ });
+ });
+
+ };
+
+ const asyncBtreeSearch = async (ip) => {
+
+ return new Promise((resolve, reject) => {
+ instance.btreeSearch(ip, (err, result) => {
+ if (err) {
+ reject(err);
+ }
+ else {
+ resolve(result);
+ }
+ });
+ });
+
+ };
+
+ test('async binarySearch query', async () => {
+ for (let i = 0; i < testIps.length; ++i) {
+ const result = await asyncBinarySearch(testIps[i]);
+ expect(result).toMatchSnapshot();
+ }
+ });
+
+ test('async btreeSearch query', async () => {
+ for (let i = 0; i < testIps.length; ++i) {
+ const result = await asyncBtreeSearch(testIps[i]);
+ expect(result).toMatchSnapshot();
+ }
+ });
+ }
+ //#endregion
+});
diff --git a/binding/nodejs/tests/exceptionTest.spec.js b/binding/nodejs/tests/exceptionTest.spec.js
new file mode 100644
index 00000000..f3205330
--- /dev/null
+++ b/binding/nodejs/tests/exceptionTest.spec.js
@@ -0,0 +1,28 @@
+// This test is used for tesing of exceptions
+
+const IP2Region = require('../ip2region');
+
+describe('Constructor Test', () => {
+ let instance;
+
+ beforeAll(() => {
+ instance = new IP2Region({ dbPath: '../../data/ip2region.db' })
+ });
+
+ afterAll(() => {
+ instance.destroy();
+ });
+
+ test('IP invalid test', () => {
+ const invalidIps = ['255.234.233', '255.255.-1.255', null, undefined, '', 'x.255.y.200'];
+ for (const ip of invalidIps) {
+ expect(() => instance.btreeSearchSync(ip)).toThrow();
+ expect(() => instance.binarySearchSync(ip)).toThrow();
+ }
+ });
+
+ test('File Not Found test', () => {
+ expect(() => new IP2Region({ dbPath: 'A Bad File or Path Here' })).toThrow();
+ });
+
+});
diff --git a/binding/nodejs/tests/utils/asyncFor.js b/binding/nodejs/tests/utils/asyncFor.js
new file mode 100644
index 00000000..a574984d
--- /dev/null
+++ b/binding/nodejs/tests/utils/asyncFor.js
@@ -0,0 +1,23 @@
+/**
+ * Async For
+ * @param {Array} groupArray
+ * @param {Function} exeCallBack
+ * @param {Function} finalCallBack
+ */
+function asyncFor(groupArray, exeCallBack, finalCallBack) {
+
+ let i = 0;
+
+ function _innerAsyncLoop() {
+ if (i < groupArray.length) {
+ exeCallBack(groupArray[i++], _innerAsyncLoop);
+ }
+ else {
+ finalCallBack();
+ }
+ }
+
+ _innerAsyncLoop();
+}
+
+module.exports = asyncFor;
\ No newline at end of file
diff --git a/binding/nodejs/tests/utils/fetchMainVersion.js b/binding/nodejs/tests/utils/fetchMainVersion.js
new file mode 100644
index 00000000..ed79119c
--- /dev/null
+++ b/binding/nodejs/tests/utils/fetchMainVersion.js
@@ -0,0 +1,9 @@
+let node_ver = process.version
+// Because nodejs's version is something like `v8.11.3`. So we should ignore `v` first
+node_ver = node_ver.substr(1);
+// Splitted by `.`
+node_ver = node_ver.split('.');
+// Take the main version number
+node_ver = parseInt(node_ver[0]);
+
+module.exports = node_ver;
diff --git a/binding/nodejs/tests/utils/testData.js b/binding/nodejs/tests/utils/testData.js
new file mode 100644
index 00000000..315983c7
--- /dev/null
+++ b/binding/nodejs/tests/utils/testData.js
@@ -0,0 +1,16 @@
+module.exports = [
+ '0.0.0.0',
+ '10.10.10.10',
+ '210.109.255.230',
+ '192.168.0.1',
+ '255.255.255.255',
+ '77.49.66.88',
+ '210.248.255.231',
+ '35.193.251.120',
+ '197.84.60.202',
+ '183.196.233.159',
+ '20.108.91.101',
+ '120.196.148.137',
+ '249.255.250.200',
+ '112.65.1.130'
+]
\ No newline at end of file