diff --git a/README.md b/README.md
index d0251ecf..2a2ea553 100644
--- a/README.md
+++ b/README.md
@@ -18,10 +18,11 @@ ip2region - 最自由的ip地址查询库,ip到地区的映射库,提供Bina
### 4. 多查询客户端的支持,0.0x毫秒级别的查询
-已经集成的客户端有:java, php, c, python,nodejs,php扩展(支持linux, php5, php7版本已支持),golang。
+已经集成的客户端有:java, C#, php, c, python,nodejs,php扩展(支持linux, php5, php7版本已支持),golang。
提供了两种查询算法,响应时间如下:
客户端/binary算法/b-tree算法/Memory算法:
+ c#/0.x毫秒/0.x毫秒/0.x毫秒
java/0.x毫秒/0.x毫秒/0.1x毫秒 (使用RandomAccessFile)
php/0.x毫秒/0.1x毫秒/0.1x毫秒
c/0.0x毫秒/0.0x毫秒/0.00x毫秒(b-tree算法基本稳定在0.02x毫秒级别)
@@ -37,8 +38,21 @@ maven仓库地址:
1.4
```
+nuget安装命令
+```shell
+Install-Package IP2Region
+```
### 5. 测试程序:
+c#:
+```shell
+cd IP2Region_ConsoleTest
+dotnet run
+example result:
+请输入IP地址:
+36.149.160.55
+```
+
java:
```shell
cd binding/java
diff --git a/binding/c#/IP2Region/DBSearcher.cs b/binding/c#/IP2Region/DBSearcher.cs
new file mode 100644
index 00000000..ca5cf5a1
--- /dev/null
+++ b/binding/c#/IP2Region/DBSearcher.cs
@@ -0,0 +1,436 @@
+//*******************************
+// Create By Rocher Kong
+// Github https://github.com/RocherKong
+// Date 2018.02.09
+//*******************************
+using System;
+using System.IO;
+using System.Text;
+
+namespace IP2Region
+{
+ public class DbSearcher
+ {
+ public static int BTREE_ALGORITHM = 1;
+ public static int BINARY_ALGORITHM = 2;
+ public static int MEMORY_ALGORITYM = 3;
+
+ /**
+ * db config
+ */
+ private DbConfig dbConfig = null;
+
+ /**
+ * db file access handler
+ */
+ private FileStream raf = null;
+
+ /**
+ * header blocks buffer
+ */
+ 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;
+
+ /**
+ * 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)
+ {
+ this.dbConfig = dbConfig;
+ raf = new FileStream(dbFile, FileMode.Open, FileAccess.Read);
+ }
+
+ /**
+ * get the region with a int ip address with memory binary search algorithm
+ *
+ * @param ip
+ * @throws IOException
+ */
+ public DataBlock MemorySearch(long ip)
+ {
+ int blen = IndexBlock.GetIndexBlockLength();
+ if (dbBinStr == null)
+ {
+ 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;
+ }
+
+ //search the index blocks to define the data
+ int l = 0, h = totalIndexBlocks;
+ long sip, eip, dataptr = 0;
+ while (l <= h)
+ {
+ int m = (l + h) >> 1;
+ int p = (int)(firstIndexPtr + m * blen);
+
+ sip = Util.getIntLong(dbBinStr, p);
+ if (ip < sip)
+ {
+ h = m - 1;
+ }
+ else
+ {
+ eip = Util.getIntLong(dbBinStr, p + 4);
+ if (ip > eip)
+ {
+ l = m + 1;
+ }
+ else
+ {
+ dataptr = Util.getIntLong(dbBinStr, p + 8);
+ break;
+ }
+ }
+ }
+
+ //not matched
+ if (dataptr == 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);
+
+ 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)
+ {
+ return MemorySearch(Util.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)
+ {
+ //check and load the header
+ if (HeaderSip == null)
+ {
+ 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);
+
+ //fill the header
+ int len = b.Length >> 3, idx = 0; //b.lenght / 8
+ 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);
+ if (dataPtrTemp == 0) break;
+
+ HeaderSip[idx] = startIp;
+ HeaderPtr[idx] = (int)dataPtrTemp;
+ idx++;
+ }
+
+ headerLength = idx;
+ }
+
+ //1. define the index block with the binary search
+ if (ip == HeaderSip[0])
+ {
+ return GetByIndexPtr(HeaderPtr[0]);
+ }
+ else if (ip == HeaderSip[headerLength - 1])
+ {
+ return GetByIndexPtr(HeaderPtr[headerLength - 1]);
+ }
+
+ int l = 0, h = headerLength, sptr = 0, eptr = 0;
+ while (l <= h)
+ {
+ int m = (l + h) >> 1;
+
+ //perfetc matched, just return it
+ if (ip == HeaderSip[m])
+ {
+ if (m > 0)
+ {
+ sptr = HeaderPtr[m - 1];
+ eptr = HeaderPtr[m];
+ }
+ else
+ {
+ sptr = HeaderPtr[m];
+ eptr = HeaderPtr[m + 1];
+ }
+
+ break;
+ }
+
+ //less then the middle value
+ if (ip < HeaderSip[m])
+ {
+ if (m == 0)
+ {
+ sptr = HeaderPtr[m];
+ eptr = HeaderPtr[m + 1];
+ break;
+ }
+ else if (ip > HeaderSip[m - 1])
+ {
+ sptr = HeaderPtr[m - 1];
+ eptr = HeaderPtr[m];
+ break;
+ }
+ h = m - 1;
+ }
+ else
+ {
+ if (m == headerLength - 1)
+ {
+ sptr = HeaderPtr[m - 1];
+ eptr = HeaderPtr[m];
+ break;
+ }
+ else if (ip <= HeaderSip[m + 1])
+ {
+ sptr = HeaderPtr[m];
+ eptr = HeaderPtr[m + 1];
+ break;
+ }
+ l = m + 1;
+ }
+ }
+
+ //match nothing just stop it
+ 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);
+
+ l = 0; h = blockLen / blen;
+ long sip, eip, dataptr = 0;
+ while (l <= h)
+ {
+ int m = (l + h) >> 1;
+ int p = m * blen;
+ sip = Util.getIntLong(iBuffer, p);
+ if (ip < sip)
+ {
+ h = m - 1;
+ }
+ else
+ {
+ eip = Util.getIntLong(iBuffer, p + 4);
+ if (ip > eip)
+ {
+ l = m + 1;
+ }
+ else
+ {
+ dataptr = Util.getIntLong(iBuffer, p + 8);
+ break;
+ }
+ }
+ }
+
+ //not matched
+ if (dataptr == 0) return null;
+
+ //3. get the data
+ int dataLen = (int)((dataptr >> 24) & 0xFF);
+ int dataPtr = (int)((dataptr & 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 throught the ip address with b-tree search algorithm
+ *
+ * @param ip
+ * @return DataBlock
+ * @throws IOException
+ */
+ public DataBlock BtreeSearch(String ip)
+ {
+ return BtreeSearch(Util.ip2long(ip));
+ }
+
+ /**
+ * get the region with a int ip address with binary search algorithm
+ *
+ * @param ip
+ * @throws IOException
+*/
+ public DataBlock BinarySearch(long ip)
+ {
+ int blen = IndexBlock.GetIndexBlockLength();
+ if (totalIndexBlocks == 0)
+ {
+ raf.Seek(0L, SeekOrigin.Begin);
+ byte[] superBytes = new byte[8];
+ 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;
+ }
+
+ //search the index blocks to define the data
+ int l = 0, h = totalIndexBlocks;
+ byte[]
+ buffer = new byte[blen];
+ long sip, eip, dataptr = 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);
+ if (ip < sip)
+ {
+ h = m - 1;
+ }
+ else
+ {
+ eip = Util.getIntLong(buffer, 4);
+ if (ip > eip)
+ {
+ l = m + 1;
+ }
+ else
+ {
+ dataptr = Util.getIntLong(buffer, 8);
+ break;
+ }
+ }
+ }
+
+ //not matched
+ if (dataptr == 0) return null;
+
+ //get the data
+ int dataLen = (int)((dataptr >> 24) & 0xFF);
+ int dataPtr = (int)((dataptr & 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 throught the ip address with binary search algorithm
+ *
+ * @param ip
+ * @return DataBlock
+ * @throws IOException
+ */
+ public DataBlock BinarySearch(String ip)
+ {
+ return BinarySearch(Util.ip2long(ip));
+ }
+
+ /**
+ * get the db config
+ *
+ * @return DbConfig
+*/
+ public DbConfig GetDbConfig()
+ {
+ return dbConfig;
+ }
+
+ /**
+ * close the db
+ *
+ * @throws IOException
+*/
+ public void Close()
+ {
+ HeaderSip = null; //let gc do its work
+ HeaderPtr = null;
+ dbBinStr = null;
+ raf.Close();
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/binding/c#/IP2Region/DataBlock.cs b/binding/c#/IP2Region/DataBlock.cs
new file mode 100644
index 00000000..99cc4ad3
--- /dev/null
+++ b/binding/c#/IP2Region/DataBlock.cs
@@ -0,0 +1,92 @@
+//*******************************
+// 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
new file mode 100644
index 00000000..830b4fd5
--- /dev/null
+++ b/binding/c#/IP2Region/DbConfig.cs
@@ -0,0 +1,69 @@
+//*******************************
+// 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
new file mode 100644
index 00000000..9c25ac2c
--- /dev/null
+++ b/binding/c#/IP2Region/DbMakerConfigException.cs
@@ -0,0 +1,16 @@
+//*******************************
+// 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
new file mode 100644
index 00000000..095014b9
--- /dev/null
+++ b/binding/c#/IP2Region/HeaderBlock.cs
@@ -0,0 +1,73 @@
+//*******************************
+// 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
new file mode 100644
index 00000000..00fa2b27
--- /dev/null
+++ b/binding/c#/IP2Region/IP2Region.csproj
@@ -0,0 +1,22 @@
+
+
+
+ netstandard2.0;net46
+ true
+ Rocher
+ Rocher
+ 准确率99.9%的ip地址定位库,0.0x毫秒级查询, 支持Binary,B树,内存三种查询算法,妈妈再也不用担心我的ip地址定位!
+ ip2region - 最自由的ip地址查询库,ip到地区的映射库,提供Binary,B树和纯内存三种查询算法,妈妈再也不用担心我的ip地址定位。
+ Rocher
+ ip2region c# IP
+ https://gitee.com/rocherkong/IP2Region
+ 1.1.0
+
+
+
+
+ Always
+
+
+
+
diff --git a/binding/c#/IP2Region/IndexBlock.cs b/binding/c#/IP2Region/IndexBlock.cs
new file mode 100644
index 00000000..7b5319bc
--- /dev/null
+++ b/binding/c#/IP2Region/IndexBlock.cs
@@ -0,0 +1,117 @@
+//*******************************
+// 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/Util.cs b/binding/c#/IP2Region/Util.cs
new file mode 100644
index 00000000..7d526d49
--- /dev/null
+++ b/binding/c#/IP2Region/Util.cs
@@ -0,0 +1,162 @@
+//*******************************
+// Create By Rocher Kong
+// Github https://github.com/RocherKong
+// Date 2018.02.09
+//*******************************
+using System;
+using System.Globalization;
+using System.Text;
+
+namespace IP2Region
+{
+
+ public class Util
+ {
+ /**
+ * 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)
+ {
+ 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
+ */
+ 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
+ */
+ public static long getIntLong(byte[] b, int offset)
+ {
+ return (
+ ((b[offset++] & 0x000000FFL)) |
+ ((b[offset++] << 8) & 0x0000FF00L) |
+ ((b[offset++] << 16) & 0x00FF0000L) |
+ ((b[offset] << 24) & 0xFF000000L)
+ );
+ }
+
+ /**
+ * get a int from a byte array start from the specifield offset
+ *
+ * @param b
+ * @param offset
+ */
+ public static int getInt3(byte[] b, int offset)
+ {
+ return (
+ (b[offset++] & 0x000000FF) |
+ (b[offset++] & 0x0000FF00) |
+ (b[offset] & 0x00FF0000)
+ );
+ }
+
+ public static int getInt2(byte[] b, int offset)
+ {
+ return (
+ (b[offset++] & 0x000000FF) |
+ (b[offset] & 0x0000FF00)
+ );
+ }
+
+ public static int getInt1(byte[] b, int offset)
+ {
+ return (
+ (b[offset] & 0x000000FF)
+ );
+ }
+
+ /**
+ * string ip to long ip
+ *
+ * @param ip
+ * @return long
+ */
+ public static long ip2long(string ip)
+ {
+ string[] p = ip.Split('.');
+ if (p.Length != 4) return 0;
+ 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);
+ var bip4 = long.TryParse(p[3], out long ip4);
+
+ if (!bip1 || !bip2 || !bip3 || !bip4
+ || ip4 > 255 || ip1 > 255 || ip2 > 255 || ip3 > 255
+ || ip4 < 1 || ip1 < 1 || ip2 < 1 || ip3 < 1)
+ {
+ throw new Exception("IP Illegal.");
+ }
+
+ 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
+ */
+ 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;
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/binding/c#/IP2Region_ConsoleTest/DBFile/ip2region.db b/binding/c#/IP2Region_ConsoleTest/DBFile/ip2region.db
new file mode 100644
index 00000000..0bb25f6e
Binary files /dev/null and b/binding/c#/IP2Region_ConsoleTest/DBFile/ip2region.db differ
diff --git a/binding/c#/IP2Region_ConsoleTest/IP2Region_ConsoleTest.csproj b/binding/c#/IP2Region_ConsoleTest/IP2Region_ConsoleTest.csproj
new file mode 100644
index 00000000..cef353f5
--- /dev/null
+++ b/binding/c#/IP2Region_ConsoleTest/IP2Region_ConsoleTest.csproj
@@ -0,0 +1,24 @@
+
+
+
+ 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
new file mode 100644
index 00000000..722815c3
--- /dev/null
+++ b/binding/c#/IP2Region_ConsoleTest/Program.cs
@@ -0,0 +1,50 @@
+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
new file mode 100644
index 00000000..548b9afb
--- /dev/null
+++ b/binding/c#/IP2Region_ConsoleTest/Properties/PublishProfiles/FolderProfile.pubxml
@@ -0,0 +1,13 @@
+
+
+
+
+ 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
new file mode 100644
index 00000000..4c24a800
--- /dev/null
+++ b/binding/c#/IP2Region_ConsoleTest/Properties/PublishProfiles/FolderProfile.pubxml.user
@@ -0,0 +1,7 @@
+
+
+
+
\ No newline at end of file