using System; using System.IO; using System.Text; using System.Threading.Tasks; using TEAMModelOS.SDK.IP2Region; using TEAMModelOS.SDK.Models; namespace TEAMModelOS.SDK { public class IPSearcher : IDisposable { const int BTREE_ALGORITHM = 1; const int BINARY_ALGORITHM = 2; const int MEMORY_ALGORITYM = 3; private IPConfig _dbConfig = null; /// /// db file access handler /// private Stream _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; /// /// Get by index ptr. /// 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 IPSearcher(IPConfig dbConfig, string dbFile) { if (_dbConfig == null) { _dbConfig = dbConfig; } try { _raf = new FileStream(dbFile, FileMode.Open, FileAccess.Read, FileShare.Read); } catch { } } public IPSearcher(string dbFile) : this(null, dbFile) { } public IPSearcher(IPConfig dbConfig, Stream dbFileStream) { if (_dbConfig == null) { _dbConfig = dbConfig; } _raf = dbFileStream; } public IPSearcher(Stream dbFileStream) : this(null, dbFileStream) { } #region Sync Methods /// /// Get the region with a int ip address with memory binary search algorithm. /// private DataBlock MemorySearch(long ip) { int blen = IndexBlock.LENGTH; if (_dbBinStr == null) { if (_raf == null) { return null; } try { _dbBinStr = new byte[(int)_raf.Length]; } catch (Exception) { return null; } _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); } /// /// Get the region throught the ip address with memory binary search algorithm. /// public DataBlock MemorySearch(string ip) { return MemorySearch(Utils.Ip2long(ip)); } /// /// Get the region with a int ip address with b-tree algorithm. /// private DataBlock BtreeSearch(long ip) { //check and load the header if (_headerSip == null) { _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); } /// /// Get the region throught the ip address with b-tree search algorithm. /// public DataBlock BtreeSearch(string ip) { return BtreeSearch(Utils.Ip2long(ip)); } /// /// Get the region with a int ip address with binary search algorithm. /// 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); } /// /// Get the region throught the ip address with binary search algorithm. /// public DataBlock BinarySearch(String ip) { return BinarySearch(Utils.Ip2long(ip)); } #endregion #region Async Methods /// /// Get the region throught the ip address with memory binary search algorithm. /// public Task MemorySearchAsync(string ip) { return Task.FromResult(MemorySearch(ip)); } public async Task SearchIpAsync( string ip) { if (ip.Contains("::")) { ip = "127.0.0.1"; } try { DataBlock block = await MemorySearchAsync(ip); if (block != null) { string region = block.Region.Replace("0|0|0|0|", "").Replace("0|0|0|", "").Replace("|0|0|0|0", "").Replace("|0|0|0|", "").Replace("|0|0|0", "").Replace("|0|0|", "").Replace("|0|0", "").Replace("|0|", "·").Replace("|0", "").Replace("|", "·"); //if (!string.IsNullOrWhiteSpace(region)) // { // region = region.Replace("中国·", "").Replace("中国", "").Replace("台湾省", "台湾"); // } return region; } else { return null; } } catch (IPInValidException) { return "IP Illigel."; } catch (Exception) { return null; } } /// /// Get the region throught the ip address with b-tree search algorithm. /// public Task BtreeSearchAsync(string ip) { return Task.FromResult(BtreeSearch(ip)); } /// /// Get the region throught the ip address with binary search algorithm. /// public Task BinarySearchAsync(string ip) { return Task.FromResult(BinarySearch(ip)); } #endregion /// /// Close the db. /// public void Close() { _headerSip = null; _headerPtr = null; _dbBinStr = null; _raf.Close(); } public void Dispose() { Close(); } } }