IP2RegionFileSearcher.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411
  1. using IP2Region;
  2. using IP2Region.Models;
  3. using System;
  4. using System.IO;
  5. using System.Runtime.CompilerServices;
  6. using System.Runtime.InteropServices;
  7. using System.Text;
  8. using System.Threading.Tasks;
  9. namespace TEAMModelOS.SDK.DI
  10. {
  11. /// <summary>
  12. /// 使用文件流的形式进行IP检索
  13. /// </summary>
  14. public class IP2RegionFileSearcher : IDisposable
  15. {
  16. const int BTREE_ALGORITHM = 1;
  17. const int BINARY_ALGORITHM = 2;
  18. const int MEMORY_ALGORITYM = 3;
  19. private DbConfig _dbConfig = null;
  20. /// <summary>
  21. /// db file access handler
  22. /// </summary>
  23. private Stream _raf = null;
  24. /// <summary>
  25. /// header blocks buffer
  26. /// </summary>
  27. private long[] _headerSip = null;
  28. private int[] _headerPtr = null;
  29. private int _headerLength;
  30. /// <summary>
  31. /// super blocks info
  32. /// </summary>
  33. private long _firstIndexPtr = 0;
  34. private long _lastIndexPtr = 0;
  35. private int _totalIndexBlocks = 0;
  36. /// <summary>
  37. /// for memory mode
  38. /// the original db binary string
  39. /// </summary>
  40. private byte[] _dbBinStr = null;
  41. /// <summary>
  42. /// Get by index ptr.
  43. /// </summary>
  44. private DataBlock GetByIndexPtr(long ptr)
  45. {
  46. _raf.Seek(ptr, SeekOrigin.Begin);
  47. byte[] buffer = new byte[12];
  48. _raf.Read(buffer, 0, buffer.Length);
  49. long extra = Utils.GetIntLong(buffer, 8);
  50. int dataLen = (int)((extra >> 24) & 0xFF);
  51. int dataPtr = (int)((extra & 0x00FFFFFF));
  52. _raf.Seek(dataPtr, SeekOrigin.Begin);
  53. byte[] data = new byte[dataLen];
  54. _raf.Read(data, 0, data.Length);
  55. int city_id = (int)Utils.GetIntLong(data, 0);
  56. string region = Encoding.UTF8.GetString(data, 4, data.Length - 4);
  57. return new DataBlock(city_id, region, dataPtr);
  58. }
  59. public IP2RegionFileSearcher(DbConfig dbConfig, string dbFile)
  60. {
  61. if (_dbConfig == null)
  62. {
  63. _dbConfig = dbConfig;
  64. }
  65. _raf = new FileStream(dbFile, FileMode.Open, FileAccess.Read, FileShare.Read);
  66. }
  67. public IP2RegionFileSearcher(string dbFile) : this(null, dbFile) { }
  68. public IP2RegionFileSearcher(DbConfig dbConfig, Stream dbFileStream)
  69. {
  70. if (_dbConfig == null)
  71. {
  72. _dbConfig = dbConfig;
  73. }
  74. _raf = dbFileStream;
  75. }
  76. public IP2RegionFileSearcher(Stream dbFileStream) : this(null, dbFileStream) { }
  77. #region Sync Methods
  78. /// <summary>
  79. /// Get the region with a int ip address with memory binary search algorithm.
  80. /// </summary>
  81. private DataBlock MemorySearch(long ip)
  82. {
  83. int blen = IndexBlock.LENGTH;
  84. if (_dbBinStr == null)
  85. {
  86. _dbBinStr = new byte[(int)_raf.Length];
  87. _raf.Seek(0L, SeekOrigin.Begin);
  88. _raf.Read(_dbBinStr, 0, _dbBinStr.Length);
  89. //initialize the global vars
  90. _firstIndexPtr = Utils.GetIntLong(_dbBinStr, 0);
  91. _lastIndexPtr = Utils.GetIntLong(_dbBinStr, 4);
  92. _totalIndexBlocks = (int)((_lastIndexPtr - _firstIndexPtr) / blen) + 1;
  93. }
  94. //search the index blocks to define the data
  95. int l = 0, h = _totalIndexBlocks;
  96. long sip = 0;
  97. while (l <= h)
  98. {
  99. int m = (l + h) >> 1;
  100. int p = (int)(_firstIndexPtr + m * blen);
  101. sip = Utils.GetIntLong(_dbBinStr, p);
  102. if (ip < sip)
  103. {
  104. h = m - 1;
  105. }
  106. else
  107. {
  108. sip = Utils.GetIntLong(_dbBinStr, p + 4);
  109. if (ip > sip)
  110. {
  111. l = m + 1;
  112. }
  113. else
  114. {
  115. sip = Utils.GetIntLong(_dbBinStr, p + 8);
  116. break;
  117. }
  118. }
  119. }
  120. //not matched
  121. if (sip == 0) return null;
  122. //get the data
  123. int dataLen = (int)((sip >> 24) & 0xFF);
  124. int dataPtr = (int)((sip & 0x00FFFFFF));
  125. int city_id = (int)Utils.GetIntLong(_dbBinStr, dataPtr);
  126. string region = Encoding.UTF8.GetString(_dbBinStr, dataPtr + 4, dataLen - 4);//new String(dbBinStr, dataPtr + 4, dataLen - 4, Encoding.UTF8);
  127. return new DataBlock(city_id, region, dataPtr);
  128. }
  129. /// <summary>
  130. /// Get the region throught the ip address with memory binary search algorithm.
  131. /// </summary>
  132. public DataBlock MemorySearch(string ip)
  133. {
  134. return MemorySearch(Utils.Ip2long(ip));
  135. }
  136. /// <summary>
  137. /// Get the region with a int ip address with b-tree algorithm.
  138. /// </summary>
  139. private DataBlock BtreeSearch(long ip)
  140. {
  141. //check and load the header
  142. if (_headerSip == null)
  143. {
  144. _raf.Seek(8L, SeekOrigin.Begin); //pass the super block
  145. byte[] b = new byte[4096];
  146. _raf.Read(b, 0, b.Length);
  147. //fill the header
  148. int len = b.Length >> 3, idx = 0; //b.lenght / 8
  149. _headerSip = new long[len];
  150. _headerPtr = new int[len];
  151. long startIp, dataPtrTemp;
  152. for (int i = 0; i < b.Length; i += 8)
  153. {
  154. startIp = Utils.GetIntLong(b, i);
  155. dataPtrTemp = Utils.GetIntLong(b, i + 4);
  156. if (dataPtrTemp == 0) break;
  157. _headerSip[idx] = startIp;
  158. _headerPtr[idx] = (int)dataPtrTemp;
  159. idx++;
  160. }
  161. _headerLength = idx;
  162. }
  163. //1. define the index block with the binary search
  164. if (ip == _headerSip[0])
  165. {
  166. return GetByIndexPtr(_headerPtr[0]);
  167. }
  168. else if (ip == _headerPtr[_headerLength - 1])
  169. {
  170. return GetByIndexPtr(_headerPtr[_headerLength - 1]);
  171. }
  172. int l = 0, h = _headerLength, sptr = 0, eptr = 0;
  173. int m = 0;
  174. while (l <= h)
  175. {
  176. m = (l + h) >> 1;
  177. //perfectly matched, just return it
  178. if (ip == _headerSip[m])
  179. {
  180. if (m > 0)
  181. {
  182. sptr = _headerPtr[m - 1];
  183. eptr = _headerPtr[m];
  184. }
  185. else
  186. {
  187. sptr = _headerPtr[m];
  188. eptr = _headerPtr[m + 1];
  189. }
  190. }
  191. //less then the middle value
  192. else if (ip < _headerSip[m])
  193. {
  194. if (m == 0)
  195. {
  196. sptr = _headerPtr[m];
  197. eptr = _headerPtr[m + 1];
  198. break;
  199. }
  200. else if (ip > _headerSip[m - 1])
  201. {
  202. sptr = _headerPtr[m - 1];
  203. eptr = _headerPtr[m];
  204. break;
  205. }
  206. h = m - 1;
  207. }
  208. else
  209. {
  210. if (m == _headerLength - 1)
  211. {
  212. sptr = _headerPtr[m - 1];
  213. eptr = _headerPtr[m];
  214. break;
  215. }
  216. else if (ip <= _headerSip[m + 1])
  217. {
  218. sptr = _headerPtr[m];
  219. eptr = _headerPtr[m + 1];
  220. break;
  221. }
  222. l = m + 1;
  223. }
  224. }
  225. //match nothing just stop it
  226. if (sptr == 0) return null;
  227. //2. search the index blocks to define the data
  228. int blockLen = eptr - sptr, blen = IndexBlock.LENGTH;
  229. byte[] iBuffer = new byte[blockLen + blen]; //include the right border block
  230. _raf.Seek(sptr, SeekOrigin.Begin);
  231. _raf.Read(iBuffer, 0, iBuffer.Length);
  232. l = 0; h = blockLen / blen;
  233. long sip = 0;
  234. int p = 0;
  235. while (l <= h)
  236. {
  237. m = (l + h) >> 1;
  238. p = m * blen;
  239. sip = Utils.GetIntLong(iBuffer, p);
  240. if (ip < sip)
  241. {
  242. h = m - 1;
  243. }
  244. else
  245. {
  246. sip = Utils.GetIntLong(iBuffer, p + 4);
  247. if (ip > sip)
  248. {
  249. l = m + 1;
  250. }
  251. else
  252. {
  253. sip = Utils.GetIntLong(iBuffer, p + 8);
  254. break;
  255. }
  256. }
  257. }
  258. //not matched
  259. if (sip == 0) return null;
  260. //3. get the data
  261. int dataLen = (int)((sip >> 24) & 0xFF);
  262. int dataPtr = (int)((sip & 0x00FFFFFF));
  263. _raf.Seek(dataPtr, SeekOrigin.Begin);
  264. byte[] data = new byte[dataLen];
  265. _raf.Read(data, 0, data.Length);
  266. int city_id = (int)Utils.GetIntLong(data, 0);
  267. String region = Encoding.UTF8.GetString(data, 4, data.Length - 4);// new String(data, 4, data.Length - 4, "UTF-8");
  268. return new DataBlock(city_id, region, dataPtr);
  269. }
  270. /// <summary>
  271. /// Get the region throught the ip address with b-tree search algorithm.
  272. /// </summary>
  273. public DataBlock BtreeSearch(string ip)
  274. {
  275. return BtreeSearch(Utils.Ip2long(ip));
  276. }
  277. /// <summary>
  278. /// Get the region with a int ip address with binary search algorithm.
  279. /// </summary>
  280. private DataBlock BinarySearch(long ip)
  281. {
  282. int blen = IndexBlock.LENGTH;
  283. if (_totalIndexBlocks == 0)
  284. {
  285. _raf.Seek(0L, SeekOrigin.Begin);
  286. byte[] superBytes = new byte[8];
  287. _raf.Read(superBytes, 0, superBytes.Length);
  288. //initialize the global vars
  289. _firstIndexPtr = Utils.GetIntLong(superBytes, 0);
  290. _lastIndexPtr = Utils.GetIntLong(superBytes, 4);
  291. _totalIndexBlocks = (int)((_lastIndexPtr - _firstIndexPtr) / blen) + 1;
  292. }
  293. //search the index blocks to define the data
  294. int l = 0, h = _totalIndexBlocks;
  295. byte[] buffer = new byte[blen];
  296. long sip = 0;
  297. while (l <= h)
  298. {
  299. int m = (l + h) >> 1;
  300. _raf.Seek(_firstIndexPtr + m * blen, SeekOrigin.Begin); //set the file pointer
  301. _raf.Read(buffer, 0, buffer.Length);
  302. sip = Utils.GetIntLong(buffer, 0);
  303. if (ip < sip)
  304. {
  305. h = m - 1;
  306. }
  307. else
  308. {
  309. sip = Utils.GetIntLong(buffer, 4);
  310. if (ip > sip)
  311. {
  312. l = m + 1;
  313. }
  314. else
  315. {
  316. sip = Utils.GetIntLong(buffer, 8);
  317. break;
  318. }
  319. }
  320. }
  321. //not matched
  322. if (sip == 0) return null;
  323. //get the data
  324. int dataLen = (int)((sip >> 24) & 0xFF);
  325. int dataPtr = (int)((sip & 0x00FFFFFF));
  326. _raf.Seek(dataPtr, SeekOrigin.Begin);
  327. byte[] data = new byte[dataLen];
  328. _raf.Read(data, 0, data.Length);
  329. int city_id = (int)Utils.GetIntLong(data, 0);
  330. String region = Encoding.UTF8.GetString(data, 4, data.Length - 4);//new String(data, 4, data.Length - 4, "UTF-8");
  331. return new DataBlock(city_id, region, dataPtr);
  332. }
  333. /// <summary>
  334. /// Get the region throught the ip address with binary search algorithm.
  335. /// </summary>
  336. public DataBlock BinarySearch(String ip)
  337. {
  338. return BinarySearch(Utils.Ip2long(ip));
  339. }
  340. #endregion
  341. #region Async Methods
  342. /// <summary>
  343. /// Get the region throught the ip address with memory binary search algorithm.
  344. /// </summary>
  345. public Task<DataBlock> MemorySearchAsync(string ip)
  346. {
  347. return Task.FromResult(MemorySearch(ip));
  348. }
  349. /// <summary>
  350. /// Get the region throught the ip address with b-tree search algorithm.
  351. /// </summary>
  352. public Task<DataBlock> BtreeSearchAsync(string ip)
  353. {
  354. return Task.FromResult(BtreeSearch(ip));
  355. }
  356. /// <summary>
  357. /// Get the region throught the ip address with binary search algorithm.
  358. /// </summary>
  359. public Task<DataBlock> BinarySearchAsync(string ip)
  360. {
  361. return Task.FromResult(BinarySearch(ip));
  362. }
  363. #endregion
  364. /// <summary>
  365. /// Close the db.
  366. /// </summary>
  367. public void Close()
  368. {
  369. _headerSip = null;
  370. _headerPtr = null;
  371. _dbBinStr = null;
  372. _raf.Close();
  373. }
  374. public void Dispose()
  375. {
  376. Close();
  377. }
  378. }
  379. }