CrazyIter_Bin %!s(int64=3) %!d(string=hai) anos
pai
achega
a6c5ae8193

+ 284 - 0
TEAMModelOS.SDK/Helper/Network/IP2Region/FileSearcher.cs

@@ -0,0 +1,284 @@
+using IP2Region.Models;
+using System;
+using System.IO;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Text;
+
+namespace IP2Region
+{
+    /// <summary>
+    /// 使用文件流的形式进行IP检索
+    /// </summary>
+    public class FileSearcher : IDisposable
+    {
+        /// <summary>
+        ///  文件流
+        /// </summary>
+        private readonly Stream _Stream;
+
+        /// <summary>
+        /// 头部索引区块缓存
+        /// </summary>
+        private readonly byte[] _HeaderBuffer;
+
+        /// <summary>
+        /// IP数据块索引描述
+        /// </summary>
+        private ref SuperBlock SuperBlock => ref Unsafe.As<byte, SuperBlock>(ref _HeaderBuffer[0]);
+
+        /// <summary>
+        /// HaderIndex总数量
+        /// </summary>
+        private readonly int _HeaderMaxIndex;
+
+        /// <summary>
+        /// IndexBlock总数量
+        /// </summary>
+        private readonly int _IndexBlockCcount;
+
+        /// <summary>
+        /// 使用文件流的形式进行IP检索
+        /// </summary>
+        /// <param name="dbPath">数据库文件路径</param>
+        public FileSearcher(string dbPath)
+        {
+            _Stream = new FileStream(dbPath, FileMode.Open, FileAccess.Read);
+
+            _HeaderBuffer = new byte[BlockSize.SuperBlockSize + 8192];
+            _Stream.Read(_HeaderBuffer, 0, _HeaderBuffer.Length);
+
+            ref SuperBlock superBlock = ref SuperBlock;
+            _IndexBlockCcount = (superBlock.LastIndexPtr - superBlock.FirstIndexPtr) / BlockSize.IndexBlockSize + 1;
+
+            int maxIndex = 0;
+            ref HeaderBlock headerCurrent = ref Unsafe.As<byte, HeaderBlock>(ref _HeaderBuffer[8]);
+            do
+            {
+                if (headerCurrent.IPIndexPtr == 0)
+                {
+                    break;
+                }
+
+                maxIndex++;
+                if (maxIndex < 1024)
+                {
+                    headerCurrent = ref Unsafe.Add(ref headerCurrent, 1);
+                }
+                else
+                {
+                    break;
+                }
+
+            } while (true);
+            _HeaderMaxIndex = maxIndex - 1;
+        }
+
+        /// <summary>
+        /// 使用Btree进行IP检索
+        /// </summary>
+        /// <param name="ipStr">IP字符串</param>
+        /// <returns></returns>
+        public DataBlock BtreeSearch(string ipStr)
+        {
+            return BtreeSearch(Utils.String2NetworkIP(ipStr));
+        }
+
+        /// <summary>
+        /// 使用Btree进行IP检索
+        /// </summary>
+        /// <param name="ipStr">网络字节序</param>
+        /// <returns></returns>
+        public DataBlock BtreeSearch(uint networkIP)
+        {
+            int index = _HeaderMaxIndex / 2, lower = 0, heighter = _HeaderMaxIndex;
+
+            ref HeaderBlock headerStart = ref Unsafe.As<byte, HeaderBlock>(ref _HeaderBuffer[BlockSize.SuperBlockSize]);
+
+            ref HeaderBlock headerCurrent = ref Unsafe.Add(ref headerStart, index);
+
+            //二分法检索ipVal在HeaderIndex的命中的二级索引
+            do
+            {
+                //Console.WriteLine("headerIndex:"+index);
+                //IP与HeaderIndex的StartIP相同,直接命中
+                if (headerCurrent.StartIP == networkIP)
+                {
+                    break;
+                }
+
+                //HeaderIndex的StartIP小于目标IP
+                if (headerCurrent.StartIP < networkIP)
+                {
+                    //如果下一个HeaderIndex的StartIP大于等于目标IP,或者当前已是最后一个视为命中
+                    //example 1: ipValue = 100.23.1.23,[100].StartIP = 100.0.0.0,[101].StartIP = 121.0.0.0 target!
+                    //example 1: ipValue = 254.23.1.23,[1023].StartIP = 254.0.0.0,1023是最后一个索引 target!
+                    if (index == _HeaderMaxIndex || (Unsafe.Add(ref headerCurrent, 1)).StartIP >= networkIP)
+                    {
+                        break;
+                    }
+                    lower = index + 1;
+                }
+                //HeaderIndex的StartIP大于目标IP
+                else
+                {
+                    if (index == 0)
+                    {
+                        break;
+                    }
+                    heighter = index - 1;
+                }
+
+                index = (lower + heighter) >> 1;
+                headerCurrent = ref Unsafe.Add(ref headerStart, index);
+            } while (true);
+
+
+            //计算2个HeaderIndex IPIndexPtr之间的IndexBlock的数量
+            int partLen = index < _HeaderMaxIndex
+                ? Unsafe.Add(ref headerCurrent, 1).IPIndexPtr - headerCurrent.IPIndexPtr
+                : BlockSize.IndexBlockSize;
+            int partCount = partLen / BlockSize.IndexBlockSize;
+
+            index = partCount >> 1;
+
+            heighter = partCount;
+
+            lower = 0;
+
+            //一次性加载整个HeaderIndex指向的IndexBlock块
+            byte[] buffer = new byte[partLen];
+
+            _Stream.Seek(headerCurrent.IPIndexPtr, SeekOrigin.Begin);
+            _Stream.Read(buffer, 0, buffer.Length);
+
+            ref IndexBlock blockStart = ref Unsafe.As<byte, IndexBlock>(ref buffer[0]);
+            ref IndexBlock blockCurrent = ref Unsafe.Add(ref blockStart, index);
+
+            //二分法检索ipVal在HeaderIndex的命中的二级索引
+            do
+            {
+
+                //IP与IndexBlock的StartIP和EndIp区间,直接命中
+                if (blockCurrent.StartIP <= networkIP && blockCurrent.EndIP >= networkIP)
+                {
+                    break;
+                }
+
+                //IndexBlock的EndIp小于目标IP
+                if (blockCurrent.StartIP < networkIP)
+                {
+                    lower = index + 1;
+                }
+                //HeaderIndex的StartIP大于目标IP
+                else
+                {
+                    heighter = index - 1;
+                }
+
+                index = (lower + heighter) >> 1;
+
+                blockCurrent = ref Unsafe.Add(ref blockStart, index);
+
+            } while (true);
+
+            buffer = new byte[blockCurrent.DataLen];
+
+            int ptr = blockCurrent.DataPtr;
+
+            _Stream.Seek(ptr, SeekOrigin.Begin);
+
+            _Stream.Read(buffer, 0, buffer.Length);
+
+            int cityId = Unsafe.As<byte, int>(ref buffer[0]);
+
+            string region = Encoding.UTF8.GetString(buffer, 4, buffer.Length - 4);
+
+            return new DataBlock(cityId, region, ptr);
+        }
+
+        /// <summary>
+        /// 使用Binary进行IP检索
+        /// </summary>
+        /// <param name="ipStr">IP字符串</param>
+        /// <returns></returns>
+        public DataBlock BinarySearch(string ipStr)
+        {
+            return BinarySearch(Utils.String2NetworkIP(ipStr));
+        }
+
+        /// <summary>
+        /// 使用Binary进行IP检索
+        /// </summary>
+        /// <param name="networkIP">网络字节序</param>
+        /// <returns></returns>
+        public DataBlock BinarySearch(uint networkIP)
+        {
+            int index = _IndexBlockCcount >> 1,
+                lower = 0,
+                heighter = _IndexBlockCcount,
+                ptr = SuperBlock.FirstIndexPtr,
+                indexLen = BlockSize.IndexBlockSize;
+
+
+            //一次性加载整个HeaderIndex指向的IndexBlock块
+            byte[] buffer = new byte[indexLen];
+
+            _Stream.Seek(ptr + index * indexLen, SeekOrigin.Begin);
+
+            _Stream.Read(buffer, 0, buffer.Length);
+
+            ref IndexBlock blockCurrent = ref Unsafe.As<byte, IndexBlock>(ref buffer[0]);
+
+            //二分法检索ipVal在HeaderIndex的命中的二级索引
+            do
+            {
+                //Console.WriteLine("File BinarySearch" + index);
+                //IP与IndexBlock的StartIP和EndIp区间,直接命中
+                if (blockCurrent.StartIP <= networkIP && blockCurrent.EndIP >= networkIP)
+                {
+                    break;
+                }
+
+                //IndexBlock的EndIp小于目标IP
+                if (blockCurrent.StartIP < networkIP)
+                {
+                    lower = index + 1;
+                }
+                //HeaderIndex的StartIP大于目标IP
+                else
+                {
+                    heighter = index - 1;
+                }
+
+                index = (lower + heighter) >> 1;
+
+                _Stream.Seek(ptr + index * indexLen, SeekOrigin.Begin);
+
+                _Stream.Read(buffer, 0, buffer.Length);
+
+                blockCurrent = ref Unsafe.As<byte, IndexBlock>(ref buffer[0]);
+
+            } while (true);
+
+            buffer = new byte[blockCurrent.DataLen];
+
+            ptr = blockCurrent.DataPtr;
+
+            _Stream.Seek(ptr, SeekOrigin.Begin);
+
+            _Stream.Read(buffer, 0, buffer.Length);
+
+            int cityId = Unsafe.As<byte, int>(ref buffer[0]);
+
+            string region = Encoding.UTF8.GetString(buffer, 4, buffer.Length - 4);
+
+            return new DataBlock(cityId, region, ptr);
+        }
+
+        public void Dispose()
+        {
+            _Stream.Dispose();
+        }
+    }
+}

+ 0 - 23
TEAMModelOS.SDK/Helper/Network/IP2Region/IP2RegionExtensions.cs

@@ -1,23 +0,0 @@
-using IP2Region;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.DependencyInjection.Extensions;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using TEAMModelOS.SDK.DI;
-
-namespace TEAMModelOS.SDK
-{
-    public static class IP2RegionExtensions
-    {
-        public static IServiceCollection AddIP2Region(this IServiceCollection services, string configPath)
-        {
-            if (services == null) throw new ArgumentNullException(nameof(services));
-            if (configPath == null) throw new ArgumentNullException(nameof(configPath));
-            services.TryAddSingleton(new IP2RegionFileSearcher($"{configPath}/ip2region.db"));
-            return services;
-        }
-    }
-}

+ 0 - 411
TEAMModelOS.SDK/Helper/Network/IP2Region/IP2RegionFileSearcher.cs

@@ -1,411 +0,0 @@
-using IP2Region;
-using IP2Region.Models;
-using System;
-using System.IO;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace TEAMModelOS.SDK.DI
-{
-    /// <summary>
-    /// 使用文件流的形式进行IP检索
-    /// </summary>
-    public class IP2RegionFileSearcher : IDisposable
-    {
-        const int BTREE_ALGORITHM = 1;
-        const int BINARY_ALGORITHM = 2;
-        const int MEMORY_ALGORITYM = 3;
-
-        private DbConfig _dbConfig = null;
-
-        /// <summary>
-        /// db file access handler
-        /// </summary>
-        private Stream _raf = null;
-
-        /// <summary>
-        /// header blocks buffer
-        /// </summary>
-        private long[] _headerSip = null;
-        private int[] _headerPtr = null;
-        private int _headerLength;
-
-        /// <summary>
-        /// super blocks info 
-        /// </summary>
-        private long _firstIndexPtr = 0;
-        private long _lastIndexPtr = 0;
-        private int _totalIndexBlocks = 0;
-
-        /// <summary>
-        /// for memory mode
-        /// the original db binary string
-        /// </summary>
-        private byte[] _dbBinStr = null;
-
-        /// <summary>
-        /// Get by index ptr.
-        /// </summary>
-        private DataBlock GetByIndexPtr(long ptr)
-        {
-            _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);
-        }
-
-        public IP2RegionFileSearcher(DbConfig dbConfig, string dbFile)
-        {
-            if (_dbConfig == null)
-            {
-                _dbConfig = dbConfig;
-            }
-            _raf = new FileStream(dbFile, FileMode.Open, FileAccess.Read, FileShare.Read);
-        }
-
-        public IP2RegionFileSearcher(string dbFile) : this(null, dbFile) { }
-
-        public IP2RegionFileSearcher(DbConfig dbConfig, Stream dbFileStream)
-        {
-            if (_dbConfig == null)
-            {
-                _dbConfig = dbConfig;
-            }
-
-            _raf = dbFileStream;
-        }
-
-        public IP2RegionFileSearcher(Stream dbFileStream) : this(null, dbFileStream) { }
-
-        #region Sync Methods
-        /// <summary>
-        /// Get the region with a int ip address with memory binary search algorithm.
-        /// </summary>
-        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);
-
-                //initialize the global vars
-                _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 = 0;
-
-            while (l <= h)
-            {
-                int m = (l + h) >> 1;
-                int p = (int)(_firstIndexPtr + m * blen);
-
-                sip = Utils.GetIntLong(_dbBinStr, p);
-
-                if (ip < sip)
-                {
-                    h = m - 1;
-                }
-                else
-                {
-                    sip = Utils.GetIntLong(_dbBinStr, p + 4);
-                    if (ip > sip)
-                    {
-                        l = m + 1;
-                    }
-                    else
-                    {
-                        sip = Utils.GetIntLong(_dbBinStr, p + 8);
-                        break;
-                    }
-                }
-            }
-
-            //not matched
-            if (sip == 0) return null;
-
-            //get the data
-            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);
-        }
-
-        /// <summary>
-        /// Get the region throught the ip address with memory binary search algorithm.
-        /// </summary>
-        public DataBlock MemorySearch(string ip)
-        {
-            return MemorySearch(Utils.Ip2long(ip));
-        }
-
-        /// <summary>
-        /// Get the region with a int ip address with b-tree algorithm.
-        /// </summary>
-        private 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[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 = Utils.GetIntLong(b, i);
-                    dataPtrTemp = Utils.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 == _headerPtr[_headerLength - 1])
-            {
-                return GetByIndexPtr(_headerPtr[_headerLength - 1]);
-            }
-            int l = 0, h = _headerLength, sptr = 0, eptr = 0;
-            int m = 0;
-            while (l <= h)
-            {
-                m = (l + h) >> 1;
-                //perfectly 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];
-                    }
-                }
-                //less then the middle value
-                else 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.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 = 0;
-            int p = 0;
-            while (l <= h)
-            {
-                m = (l + h) >> 1;
-                p = m * blen;
-                sip = Utils.GetIntLong(iBuffer, p);
-                if (ip < sip)
-                {
-                    h = m - 1;
-                }
-                else
-                {
-                    sip = Utils.GetIntLong(iBuffer, p + 4);
-                    if (ip > sip)
-                    {
-                        l = m + 1;
-                    }
-                    else
-                    {
-                        sip = Utils.GetIntLong(iBuffer, p + 8);
-                        break;
-                    }
-                }
-            }
-            //not matched
-            if (sip == 0) return null;
-            //3. get the data
-            int dataLen = (int)((sip >> 24) & 0xFF);
-            int dataPtr = (int)((sip & 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);// new String(data, 4, data.Length - 4, "UTF-8");
-            return new DataBlock(city_id, region, dataPtr);
-        }
-
-        /// <summary>
-        /// Get the region throught the ip address with b-tree search algorithm.
-        /// </summary>
-        public DataBlock BtreeSearch(string ip)
-        {
-            return BtreeSearch(Utils.Ip2long(ip));
-        }
-
-        /// <summary>
-        /// Get the region with a int ip address with binary search algorithm.
-        /// </summary>
-        private DataBlock BinarySearch(long ip)
-        {
-            int blen = IndexBlock.LENGTH;
-            if (_totalIndexBlocks == 0)
-            {
-                _raf.Seek(0L, SeekOrigin.Begin);
-                byte[] superBytes = new byte[8];
-                _raf.Read(superBytes, 0, superBytes.Length);
-                //initialize the global vars
-                _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 = 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 = Utils.GetIntLong(buffer, 0);
-                if (ip < sip)
-                {
-                    h = m - 1;
-                }
-                else
-                {
-                    sip = Utils.GetIntLong(buffer, 4);
-                    if (ip > sip)
-                    {
-                        l = m + 1;
-                    }
-                    else
-                    {
-                        sip = Utils.GetIntLong(buffer, 8);
-                        break;
-                    }
-                }
-            }
-            //not matched
-            if (sip == 0) return null;
-            //get the data
-            int dataLen = (int)((sip >> 24) & 0xFF);
-            int dataPtr = (int)((sip & 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);//new String(data, 4, data.Length - 4, "UTF-8");
-            return new DataBlock(city_id, region, dataPtr);
-        }
-
-        /// <summary>
-        /// Get the region throught the ip address with binary search algorithm.
-        /// </summary>
-        public DataBlock BinarySearch(String ip)
-        {
-            return BinarySearch(Utils.Ip2long(ip));
-        }
-        #endregion
-
-        #region Async Methods
-        /// <summary>
-        /// Get the region throught the ip address with memory binary search algorithm.
-        /// </summary>
-        public Task<DataBlock> MemorySearchAsync(string ip)
-        {
-            return Task.FromResult(MemorySearch(ip));
-        }
-        /// <summary>
-        /// Get the region throught the ip address with b-tree search algorithm.
-        /// </summary>
-        public Task<DataBlock> BtreeSearchAsync(string ip)
-        {
-            return Task.FromResult(BtreeSearch(ip));
-        }
-        /// <summary>
-        /// Get the region throught the ip address with binary search algorithm.
-        /// </summary>
-        public Task<DataBlock> BinarySearchAsync(string ip)
-        {
-            return Task.FromResult(BinarySearch(ip));
-        }
-        #endregion
-
-        /// <summary>
-        /// Close the db.
-        /// </summary>
-        public void Close()
-        {
-            _headerSip = null;
-            _headerPtr = null;
-            _dbBinStr = null;
-            _raf.Close();
-        }
-
-        public void Dispose()
-        {
-            Close();
-        }
-    }
-}

+ 10 - 0
TEAMModelOS.SDK/Helper/Network/IP2Region/IPInValidException.cs

@@ -0,0 +1,10 @@
+using System;
+
+namespace IP2Region
+{
+    public class IPInValidException : Exception
+    {
+        const string ERROR_MSG = "IP Illigel. Please input a valid IP.";
+        public IPInValidException() : base(ERROR_MSG) { }
+    }
+}

+ 231 - 0
TEAMModelOS.SDK/Helper/Network/IP2Region/MemorySearcher.cs

@@ -0,0 +1,231 @@
+using IP2Region.Models;
+using System;
+using System.IO;
+using System.Runtime.CompilerServices;
+using System.Text;
+
+namespace IP2Region
+{
+    /// <summary>
+    /// 一次性加载整块内存,然后进行搜索
+    /// </summary>
+    public class MemorySearcher
+    {
+        /// <summary>
+        /// 数据缓存
+        /// </summary>
+        private readonly byte[] _Buffer;
+
+        /// <summary>
+        /// IP数据块索引描述
+        /// </summary>
+        private ref SuperBlock SuperBlock => ref Unsafe.As<byte, SuperBlock>(ref _Buffer[0]);
+
+        /// <summary>
+        /// HaderIndex总数量
+        /// </summary>
+        private readonly int _HeaderMaxIndex ;
+
+        /// <summary>
+        /// IndexBlock总数量
+        /// </summary>
+        private readonly int _IndexBlockCcount;
+
+
+
+        /// <summary>
+        /// 构造MemorySearcher,加载数据至内存中
+        /// </summary>
+        /// <param name="dbPath"></param>
+        public MemorySearcher(string dbPath)
+        {
+            _Buffer = LoadToMemory(dbPath);
+            ref SuperBlock superBlock = ref SuperBlock;
+            _IndexBlockCcount = (superBlock.LastIndexPtr - superBlock.FirstIndexPtr) / BlockSize.IndexBlockSize + 1;
+
+            int maxIndex = 0;
+            ref HeaderBlock headerCurrent = ref Unsafe.As<byte, HeaderBlock>(ref _Buffer[8]);
+            do
+            {
+                if (headerCurrent.IPIndexPtr == 0)
+                {
+                    break;
+                }
+
+                maxIndex++;
+                if (maxIndex < 1024)
+                {
+                    headerCurrent = ref Unsafe.Add(ref headerCurrent, 1);
+                }
+                else
+                {
+                    break;
+                }
+
+            } while (true);
+            _HeaderMaxIndex = maxIndex - 1;
+        }
+
+        /// <summary>
+        /// 加载数据到内存
+        /// </summary>
+        /// <param name="dbPath"></param>
+        /// <returns></returns>
+        private byte[] LoadToMemory(string dbPath)
+        {
+            using (FileStream stream = new FileStream(dbPath, FileMode.Open, FileAccess.Read))
+            {
+                byte[] buffer = new byte[stream.Length];
+                stream.Read(buffer, 0, buffer.Length);
+                return buffer;
+            }
+        }
+
+        /// <summary>
+        /// 使用Btree进行IP检索
+        /// </summary>
+        /// <param name="ipStr">IP字符串</param>
+        /// <returns></returns>
+        public DataBlock BtreeSearch(string ipStr)
+        {
+            return BtreeSearch(Utils.String2NetworkIP(ipStr));
+        }
+
+        /// <summary>
+        /// 使用Btree进行IP检索
+        /// </summary>
+        /// <param name="ipStr">网络字节序</param>
+        /// <returns></returns>
+        public DataBlock BtreeSearch(uint networkIP)
+        {
+            int index, lower = 0, heighter = _HeaderMaxIndex;
+
+            ref HeaderBlock headerStart = ref Unsafe.As<byte, HeaderBlock>(ref _Buffer[BlockSize.SuperBlockSize]);
+
+            ref HeaderBlock headerCurrent = ref headerStart;
+
+            //二分法检索ipVal在HeaderIndex的命中的二级索引
+            do
+            {
+                index = (heighter + lower) >> 1;
+
+                headerCurrent = ref Unsafe.Add(ref headerStart, index);
+
+                //IP与HeaderIndex的StartIP相同,直接命中
+                if (headerCurrent.StartIP == networkIP)
+                {
+                    break;
+                }
+
+                //HeaderIndex的StartIP小于目标IP
+                if (headerCurrent.StartIP < networkIP)
+                {
+                    //如果下一个HeaderIndex的StartIP大于等于目标IP,或者当前已是最后一个视为命中
+                    //example 1: ipValue = 100.23.1.23,[100].StartIP = 100.0.0.0,[101].StartIP = 121.0.0.0 target!
+                    //example 1: ipValue = 254.23.1.23,[1023].StartIP = 254.0.0.0,1023是最后一个索引 target!
+                    if (index == _HeaderMaxIndex || (Unsafe.Add(ref headerCurrent, 1)).StartIP >= networkIP)
+                    {
+                        break;
+                    }
+                    lower = index + 1;
+                }
+                //HeaderIndex的StartIP大于目标IP
+                else
+                {
+                    if (index == 0)
+                    {
+                        break;
+                    }
+                    heighter = index - 1;
+                }
+               
+            } while (true);
+
+            //计算2个HeaderIndex IPIndexPtr之间的IndexBlock的数量
+            int partLen = index < _HeaderMaxIndex
+                ? Unsafe.Add(ref headerCurrent, 1).IPIndexPtr - headerCurrent.IPIndexPtr
+                : BlockSize.IndexBlockSize;
+            int partCount = partLen / BlockSize.IndexBlockSize;
+
+            ref IndexBlock blockStart = ref Unsafe.As<byte, IndexBlock>(ref _Buffer[headerCurrent.IPIndexPtr]);
+
+            return DichotomySearch(networkIP, ref blockStart, partCount);
+        }
+
+        /// <summary>
+        /// 使用Binary进行IP检索
+        /// </summary>
+        /// <param name="ipStr">IP字符串</param>
+        /// <returns></returns>
+        public DataBlock BinarySearch(string ipStr)
+        {
+            return BinarySearch(Utils.String2NetworkIP(ipStr));
+        }
+
+        /// <summary>
+        /// 使用Binary进行IP检索
+        /// </summary>
+        /// <param name="networkIP">网络字节序</param>
+        /// <returns></returns>
+        public DataBlock BinarySearch(uint networkIP)
+        {
+            ref SuperBlock superBlock = ref SuperBlock;
+            ref IndexBlock blockStart = ref Unsafe.As<byte, IndexBlock>(ref _Buffer[superBlock.FirstIndexPtr]);
+            return DichotomySearch(networkIP, ref blockStart, _IndexBlockCcount);
+        }
+
+        /// <summary>
+        /// 二分法检索ipVal在HeaderIndex的命中的二级索引
+        /// </summary>
+        /// <param name="networkIP"></param>
+        /// <param name="blockStart"></param>
+        /// <param name="count"></param>
+        /// <returns></returns>
+        private DataBlock DichotomySearch(uint networkIP, ref IndexBlock blockStart, int count)
+        {
+            int index = count >> 1,
+                lower = 0,
+                heighter = count;
+
+            ref IndexBlock blockCurrent = ref Unsafe.Add(ref blockStart, index);
+
+            do
+            {
+                //Console.WriteLine("Memory BinarySearch" + index);
+                //IP与IndexBlock的StartIP和EndIp区间,直接命中
+                if (blockCurrent.StartIP <= networkIP && blockCurrent.EndIP >= networkIP)
+                {
+                    break;
+                }
+
+                //IndexBlock的EndIp小于目标IP
+                if (blockCurrent.StartIP < networkIP)
+                {
+                    //lower = index ;
+                    //index += Math.Max((heighter - lower) / 2, 1);
+                    lower = index + 1;
+                }
+                //HeaderIndex的StartIP大于目标IP
+                else
+                {
+                    //heighter = index;
+                    //index -= Math.Max((heighter - lower) / 2, 1);
+                    count = index - 1;
+                }
+
+                index = (lower + count) >> 1;
+                blockCurrent = ref Unsafe.Add(ref blockStart, index);
+
+            } while (true);
+
+
+            int dataPtr = blockCurrent.DataPtr;
+
+            int cityId = Unsafe.As<byte, int>(ref _Buffer[dataPtr]);
+
+            string region = Encoding.UTF8.GetString(_Buffer, blockCurrent.DataPtr + 4, blockCurrent.DataLen - 4);
+
+            return new DataBlock(cityId, region, dataPtr);
+        }
+    }
+}

+ 3 - 8
TEAMModelOS.SDK/Helper/Network/IP2Region/Models/DataBlock.cs

@@ -1,9 +1,4 @@
-//*******************************
-// Created By Rocher Kong 
-// Github https://github.com/RocherKong
-// Date 2018.02.09
-//*******************************
-namespace IP2Region.Models
+namespace IP2Region.Models
 {
     public class DataBlock
     {
@@ -35,7 +30,7 @@ namespace IP2Region.Models
             DataPtr = dataPtr;
         }
 
-        public DataBlock(int city_id, string region):this(city_id,region,0)
+        public DataBlock(int city_id, string region) : this(city_id, region, 0)
         {
         }
         #endregion
@@ -47,4 +42,4 @@ namespace IP2Region.Models
 
     }
 
-}
+}

+ 38 - 43
TEAMModelOS.SDK/Helper/Network/IP2Region/Models/DbConfig.cs

@@ -1,49 +1,44 @@
-//*******************************
-// Created By Rocher Kong 
-// Github https://github.com/RocherKong
-// Date 2018.02.09
-//*******************************
-using System;
+//using System;
 
-namespace IP2Region.Models
-{
-    public class DbMakerConfigException : Exception
-    {
-        public string ErrMsg { get; private set; }
-        public DbMakerConfigException(string errMsg)
-        {
-            ErrMsg = errMsg;
-        }
-    }
+//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 class DbConfig
+//    {
+//        public int TotalHeaderSize
+//        {
+//            get;
+//            private set;
+//        }
 
-        public int indexBlockSize
-        {
-            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(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)
-        {
-        }
-    }
+//        public DbConfig() : this(8 * 2048)
+//        {
+//        }
+//    }
 
-}
+//}

+ 7 - 40
TEAMModelOS.SDK/Helper/Network/IP2Region/Models/HeaderBlock.cs

@@ -1,49 +1,16 @@
-//*******************************
-// Created By Rocher Kong 
-// Github https://github.com/RocherKong
-// Date 2018.02.09
-//*******************************
-
-namespace IP2Region.Models
+namespace IP2Region.Models
 {
-    internal class HeaderBlock
+    internal struct HeaderBlock
     {
-        public long IndexStartIp
-        {
-            get;
-            private set;
-        }
 
-        public int IndexPtr
-        {
-            get;
-            private set;
-        }
+        internal uint StartIP;
 
-        public HeaderBlock(long indexStartIp, int indexPtr)
-        {
-            IndexStartIp = indexStartIp;
-            IndexPtr = indexPtr;
-        }
+        internal int IPIndexPtr;
 
-        /// <summary>
-        /// Get the bytes for total storage
-        /// </summary>
-        /// <returns>
-        /// Bytes gotten.
-        /// </returns>
-        public byte[] GetBytes()
+        public HeaderBlock(uint indexStartIp, int indexPtr)
         {
-            /*
-             * +------------+-----------+
-             * | 4bytes     | 4bytes    |
-             * +------------+-----------+
-             *  start ip      index ptr
-            */
-            byte[] b = new byte[8];
-            Utils.WriteIntLong(b, 0, IndexStartIp);
-            Utils.WriteIntLong(b, 4, IndexPtr);
-            return b;
+            StartIP = indexStartIp;
+            IPIndexPtr = indexPtr;
         }
     }
 }

+ 12 - 56
TEAMModelOS.SDK/Helper/Network/IP2Region/Models/IndexBlock.cs

@@ -1,65 +1,21 @@
-//*******************************
-// Created By Rocher Kong 
-// Github https://github.com/RocherKong
-// Date 2018.02.09
-//*******************************
-
-namespace IP2Region
+namespace IP2Region.Models
 {
-    internal class IndexBlock
+    /*
+     * +------------+-----------+-----------+-----------+
+     * | 4bytes     | 4bytes    | 3bytes    |  1bytes  |
+     * +------------+-----------+-----------+-----------+
+     *  start ip      end ip      data ptr     data len
+    */
+    internal struct 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];
+        public uint StartIP;
 
-            Utils.WriteIntLong(b, 0, StartIP);    //start ip
-            Utils.WriteIntLong(b, 4, EndIp);        //end ip
+        public uint EndIP;
 
-            //write the data ptr and the length
-            long mix = DataPtr | ((DataLen << 24) & 0xFF000000L);
-            Utils.WriteIntLong(b, 8, mix);
+        public Int24 DataPtr;
 
-            return b;
-        }
+        public byte DataLen;
     }
 }

+ 54 - 74
TEAMModelOS.SDK/Helper/Network/IP2Region/Utils.cs

@@ -1,122 +1,102 @@
-//*******************************
-// Created By Rocher Kong 
-// Github https://github.com/RocherKong
-// Date 2018.02.09
-//*******************************
-
+using IP2Region.Models;
 using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Text;
 
 namespace IP2Region
 {
-    public class IPInValidException : Exception
-    {
-        const string ERROR_MSG = "IP Illigel. Please input a valid IP.";
-        public IPInValidException() : base(ERROR_MSG) { }
-    }
-    internal static class Utils
+    public static class Utils
     {
-        /// <summary>
-        /// Write specfield bytes to a byte array start from offset.
-        /// </summary>
-        public static void Write(byte[] b, int offset, ulong v, int bytes)
+        public static void Write<T>(byte[] b, int offset, T value)
+            where T : unmanaged
         {
-            for (int i = 0; i < bytes; i++)
-            {
-                b[offset++] = (byte)((v >> (8 * i)) & 0xFF);
-            }
+            ref byte buffer = ref Unsafe.As<T, byte>(ref value);
+            Marshal.Copy((IntPtr)buffer, b, offset, Unsafe.SizeOf<T>());
         }
 
         /// <summary>
-        /// Write a int to a byte array.
+        /// Get a int from a byte array start from the specifiled offset.
         /// </summary>
-        public static void WriteIntLong(byte[] b, int offset, long v)
+        public static uint GetUint(byte[] b, int offset)
         {
-            b[offset++] = (byte)((v >> 0) & 0xFF);
-            b[offset++] = (byte)((v >> 8) & 0xFF);
-            b[offset++] = (byte)((v >> 16) & 0xFF);
-            b[offset] = (byte)((v >> 24) & 0xFF);
+            return Unsafe.As<byte, uint>(ref b[offset]);
         }
 
-        /// <summary>
-        /// Get a int from a byte array start from the specifiled offset.
-        /// </summary>
-        public static long GetIntLong(byte[] b, int offset)
+        public static int GetInt(byte[] b, int offset)
         {
-            return (
-                ((b[offset++] & 0x000000FFL)) |
-                ((b[offset++] << 8) & 0x0000FF00L) |
-                ((b[offset++] << 16) & 0x00FF0000L) |
-                ((b[offset] << 24) & 0xFF000000L)
-            );
+            return Unsafe.As<byte, int>(ref b[offset]);
         }
 
         /// <summary>
         /// Get a int from a byte array start from the specifield offset.
         /// </summary>
-        public static int GetInt3(byte[] b, int offset)
+        public static int GetInt24(byte[] b, int offset)
         {
-            return (
-                (b[offset++] & 0x000000FF) |
-                (b[offset++] & 0x0000FF00) |
-                (b[offset] & 0x00FF0000)
-            );
+            return Unsafe.As<byte, Int24>(ref b[offset]);
         }
 
-        public static int GetInt2(byte[] b, int offset)
+
+        public static ushort GetUshort(byte[] b, int offset)
         {
-            return (
-                (b[offset++] & 0x000000FF) |
-                (b[offset] & 0x0000FF00)
-            );
+            return Unsafe.As<byte, ushort>(ref b[offset]);
         }
 
-        public static int GetInt1(byte[] b, int offset)
+        public static byte GetByte(byte[] b, int offset)
         {
-            return (
-                (b[offset] & 0x000000FF)
-            );
+            return b[offset];
         }
+
         /// <summary>
         /// String ip to long ip.
         /// </summary>
-        public static long Ip2long(string ip)
+        public static uint String2NetworkIP(string ip)
         {
             string[] p = ip.Split('.');
+
             if (p.Length != 4) throw new IPInValidException();
 
-            foreach (string pp in p)
+            uint ipVal = 0;
+
+            ref byte ipRef = ref Unsafe.As<uint, byte>(ref ipVal);
+
+            for (int i = 3; i > -1; i--)
             {
-                if (pp.Length > 3) throw new IPInValidException();
-                if (!int.TryParse(pp, out int value) || value > 255)
+                if (byte.TryParse(p[i], out byte part))
+                {
+                    ref byte byteRef = ref Unsafe.Add(ref ipRef, i);
+                    byteRef = part;
+                }
+                else
                 {
                     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);
-            var bip4 = long.TryParse(p[3], out long ip4);
 
-            if (!bip1 || !bip2 || !bip3 || !bip4
-                || ip4 > 255 || ip1 > 255 || ip2 > 255 || ip3 > 255
-                || ip4 < 0 || ip1 < 0 || ip2 < 0 || ip3 < 0)
-            {
-                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);
+            return ipVal;
         }
 
         /// <summary>
         /// Int to ip string.
         /// </summary>
-        public static string Long2ip(long ip)
+        public static string IP2String(uint ipVal)
         {
-            return $"{(ip >> 24) & 0xFF}.{(ip >> 16) & 0xFF}.{(ip >> 8) & 0xFF}.{ip & 0xFF}";
+            ref byte ipRef = ref Unsafe.As<uint, byte>(ref ipVal);
+            StringBuilder ipString = new StringBuilder(15);
+            for(int i = 0; i < 4; i++)
+            {
+                if (i > 0)
+                {
+                    ipString.Append('.');
+                    ipString.Append(Unsafe.Add(ref ipRef, i));
+                }
+                else
+                {
+                    ipString.Append(ipRef);
+                }
+            }
+            return ipString.ToString();
         }
-    }
 
-}
+    }
+}

+ 13 - 12
TEAMModelOS/Controllers/Client/HiScanController.cs

@@ -376,7 +376,7 @@ namespace TEAMModelOS.Controllers.Core
                 if (!request.TryGetProperty("id", out JsonElement id)) return BadRequest();
                 if (!request.TryGetProperty("schoolId", out JsonElement _schoolId)) return BadRequest();
                 if (!request.TryGetProperty("sheetNo", out JsonElement _sheetNo)) return BadRequest();
-                if (!request.TryGetProperty("scope", out JsonElement _scope)) return BadRequest();
+                //if (!request.TryGetProperty("scope", out JsonElement _scope)) return BadRequest();
                 if (!request.TryGetProperty("owner", out JsonElement _owner)) return BadRequest();
                 var client = _azureCosmos.GetCosmosClient();
                 School school = null;
@@ -393,18 +393,19 @@ namespace TEAMModelOS.Controllers.Core
                 }
                 var response = await client.GetContainer(Constant.TEAMModelOS, "Teacher").ReadItemStreamAsync($"{id}", new PartitionKey("Base"));
                
-                if ($"{_scope}".Equals("school", StringComparison.OrdinalIgnoreCase))
+                if ($"{_owner}".Equals("school", StringComparison.OrdinalIgnoreCase))
                 {
-                    examData = await GetExamBySheet("school", $"{_schoolId}", $"{_schoolId}", client,  classesSet, $"{_sheetNo}", $"{_owner}");
-                    if (response.Status == 200 && examData == null)
-                    {
-                        //获取scope=school
-                        examData = await GetExamBySheet("school", $"{_schoolId}", $"{id}", client,  classesSet, $"{_sheetNo}", $"{_owner}");
-                    }
+                    examData = await GetExamBySheet($"{_owner}", $"{_schoolId}", $"{_schoolId}", client,  classesSet, $"{_sheetNo}");
+                  
                 }
                 else {
                     //获取scope=private
-                    examData = await GetExamBySheet("private", null, $"{id}", client,  classesSet, $"{_sheetNo}", $"{_owner}");
+                    examData = await GetExamBySheet($"{_owner}", null, $"{id}", client,  classesSet, $"{_sheetNo}");
+                    if (response.Status == 200 && examData == null && string.IsNullOrEmpty($"{_schoolId}")) {
+                        //获取scope=school
+                        examData = await GetExamBySheet($"{_owner}", $"{_schoolId}", $"{id}", client, classesSet, $"{_sheetNo}");
+                    }
+                    
                 }
 
                 (List<RMember> tmdIds, List<RGroupList> classInfo) = await GroupListService.GetStutmdidListids(client, _dingDing, classesSet.ToList(), $"{_schoolId}");
@@ -452,7 +453,7 @@ namespace TEAMModelOS.Controllers.Core
                 string blobSas = null;
                 string blobUrl = null;
                 ExamData exam = null;
-                if ($"{_scope}".Equals("school", StringComparison.OrdinalIgnoreCase))
+                if ($"{_owner}".Equals("school", StringComparison.OrdinalIgnoreCase))
                 {
                     if (examData != null)
                     {
@@ -481,11 +482,11 @@ namespace TEAMModelOS.Controllers.Core
             }
         }
 
-        private async Task<ExamData> GetExamBySheet(string scope, string school, string code, CosmosClient client,HashSet<string> classesSet, string sheetNo,string owner)
+        private async Task<ExamData> GetExamBySheet(string owner, string school, string code, CosmosClient client,HashSet<string> classesSet, string sheetNo)
         {
             ExamData examRcds = null;
     
-            StringBuilder sql = new StringBuilder($"SELECT  value(c) FROM c  join papers in c.papers where  c.owner='{owner}' and  c.scope='{scope}' and papers.sheetNo='{sheetNo}' ");
+            StringBuilder sql = new StringBuilder($"SELECT  value(c) FROM c  join papers in c.papers where  c.owner='{owner}'  and papers.sheetNo='{sheetNo}' ");
             if (!string.IsNullOrEmpty(school))
             {
                 sql.Append($"  and c.school='{school}' ");

+ 19 - 0
TEAMModelOS/Controllers/XTest/TestController.cs

@@ -3,6 +3,7 @@ using Azure.Messaging.ServiceBus;
 using Azure.Storage.Blobs.Models;
 using HTEXLib.COMM.Helpers;
 using HTEXLib.Helpers.ShapeHelpers;
+using IP2Region;
 using Microsoft.AspNetCore.Hosting;
 using Microsoft.AspNetCore.Http;
 using Microsoft.AspNetCore.Mvc;
@@ -602,6 +603,8 @@ namespace TEAMModelOS.Controllers
             }
             return Ok(new { itemBlob });
         }
+
+
         /// <summary>
         ///  
         /// </summary>
@@ -637,6 +640,22 @@ namespace TEAMModelOS.Controllers
             _=  _azureStorage.SaveLog("find-school", school.ToJsonString(),httpContext:HttpContext,dingDing:_dingDing,scope:"school");
             return Ok(new { doc, school,  });
         }
+
+        /// <summary>
+        ///  
+        /// </summary>
+        /// <param name="request"></param>
+        /// <returns></returns>
+        [ProducesDefaultResponseType]
+        //[AuthToken(Roles = "teacher")]
+        [HttpPost("get-ip")]
+        public async Task<IActionResult> GetIp(JsonElement request)
+        {
+            string path = $"{ _environment.ContentRootPath}/JsonFile/Core/ip2region.db";
+            FileSearcher iP2RegionFileSearcher = new FileSearcher(path);
+            var ip =   iP2RegionFileSearcher.BtreeSearch($"{request.GetProperty("ip")}");
+            return Ok(ip);
+        }
     }
 
 }

BIN=BIN
TEAMModelOS/JsonFile/Core/ip2region.db