FileSearcher.cs 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. using IP2Region.Models;
  2. using System;
  3. using System.IO;
  4. using System.Runtime.CompilerServices;
  5. using System.Runtime.InteropServices;
  6. using System.Text;
  7. namespace IP2Region
  8. {
  9. /// <summary>
  10. /// 使用文件流的形式进行IP检索
  11. /// </summary>
  12. public class FileSearcher : IDisposable
  13. {
  14. /// <summary>
  15. /// 文件流
  16. /// </summary>
  17. private readonly Stream _Stream;
  18. /// <summary>
  19. /// 头部索引区块缓存
  20. /// </summary>
  21. private readonly byte[] _HeaderBuffer;
  22. /// <summary>
  23. /// IP数据块索引描述
  24. /// </summary>
  25. private ref SuperBlock SuperBlock => ref Unsafe.As<byte, SuperBlock>(ref _HeaderBuffer[0]);
  26. /// <summary>
  27. /// HaderIndex总数量
  28. /// </summary>
  29. private readonly int _HeaderMaxIndex;
  30. /// <summary>
  31. /// IndexBlock总数量
  32. /// </summary>
  33. private readonly int _IndexBlockCcount;
  34. /// <summary>
  35. /// 使用文件流的形式进行IP检索
  36. /// </summary>
  37. /// <param name="dbPath">数据库文件路径</param>
  38. public FileSearcher(string dbPath)
  39. {
  40. _Stream = new FileStream(dbPath, FileMode.Open, FileAccess.Read);
  41. _HeaderBuffer = new byte[BlockSize.SuperBlockSize + 8192];
  42. _Stream.Read(_HeaderBuffer, 0, _HeaderBuffer.Length);
  43. ref SuperBlock superBlock = ref SuperBlock;
  44. _IndexBlockCcount = (superBlock.LastIndexPtr - superBlock.FirstIndexPtr) / BlockSize.IndexBlockSize + 1;
  45. int maxIndex = 0;
  46. ref HeaderBlock headerCurrent = ref Unsafe.As<byte, HeaderBlock>(ref _HeaderBuffer[8]);
  47. do
  48. {
  49. if (headerCurrent.IPIndexPtr == 0)
  50. {
  51. break;
  52. }
  53. maxIndex++;
  54. if (maxIndex < 1024)
  55. {
  56. headerCurrent = ref Unsafe.Add(ref headerCurrent, 1);
  57. }
  58. else
  59. {
  60. break;
  61. }
  62. } while (true);
  63. _HeaderMaxIndex = maxIndex - 1;
  64. }
  65. /// <summary>
  66. /// 使用Btree进行IP检索
  67. /// </summary>
  68. /// <param name="ipStr">IP字符串</param>
  69. /// <returns></returns>
  70. public DataBlock BtreeSearch(string ipStr)
  71. {
  72. return BtreeSearch(Utils.String2NetworkIP(ipStr));
  73. }
  74. /// <summary>
  75. /// 使用Btree进行IP检索
  76. /// </summary>
  77. /// <param name="ipStr">网络字节序</param>
  78. /// <returns></returns>
  79. public DataBlock BtreeSearch(uint networkIP)
  80. {
  81. int index = _HeaderMaxIndex / 2, lower = 0, heighter = _HeaderMaxIndex;
  82. ref HeaderBlock headerStart = ref Unsafe.As<byte, HeaderBlock>(ref _HeaderBuffer[BlockSize.SuperBlockSize]);
  83. ref HeaderBlock headerCurrent = ref Unsafe.Add(ref headerStart, index);
  84. //二分法检索ipVal在HeaderIndex的命中的二级索引
  85. do
  86. {
  87. //Console.WriteLine("headerIndex:"+index);
  88. //IP与HeaderIndex的StartIP相同,直接命中
  89. if (headerCurrent.StartIP == networkIP)
  90. {
  91. break;
  92. }
  93. //HeaderIndex的StartIP小于目标IP
  94. if (headerCurrent.StartIP < networkIP)
  95. {
  96. //如果下一个HeaderIndex的StartIP大于等于目标IP,或者当前已是最后一个视为命中
  97. //example 1: ipValue = 100.23.1.23,[100].StartIP = 100.0.0.0,[101].StartIP = 121.0.0.0 target!
  98. //example 1: ipValue = 254.23.1.23,[1023].StartIP = 254.0.0.0,1023是最后一个索引 target!
  99. if (index == _HeaderMaxIndex || (Unsafe.Add(ref headerCurrent, 1)).StartIP >= networkIP)
  100. {
  101. break;
  102. }
  103. lower = index + 1;
  104. }
  105. //HeaderIndex的StartIP大于目标IP
  106. else
  107. {
  108. if (index == 0)
  109. {
  110. break;
  111. }
  112. heighter = index - 1;
  113. }
  114. index = (lower + heighter) >> 1;
  115. headerCurrent = ref Unsafe.Add(ref headerStart, index);
  116. } while (true);
  117. //计算2个HeaderIndex IPIndexPtr之间的IndexBlock的数量
  118. int partLen = index < _HeaderMaxIndex
  119. ? Unsafe.Add(ref headerCurrent, 1).IPIndexPtr - headerCurrent.IPIndexPtr
  120. : BlockSize.IndexBlockSize;
  121. int partCount = partLen / BlockSize.IndexBlockSize;
  122. index = partCount >> 1;
  123. heighter = partCount;
  124. lower = 0;
  125. //一次性加载整个HeaderIndex指向的IndexBlock块
  126. byte[] buffer = new byte[partLen];
  127. _Stream.Seek(headerCurrent.IPIndexPtr, SeekOrigin.Begin);
  128. _Stream.Read(buffer, 0, buffer.Length);
  129. ref IndexBlock blockStart = ref Unsafe.As<byte, IndexBlock>(ref buffer[0]);
  130. ref IndexBlock blockCurrent = ref Unsafe.Add(ref blockStart, index);
  131. //二分法检索ipVal在HeaderIndex的命中的二级索引
  132. do
  133. {
  134. //IP与IndexBlock的StartIP和EndIp区间,直接命中
  135. if (blockCurrent.StartIP <= networkIP && blockCurrent.EndIP >= networkIP)
  136. {
  137. break;
  138. }
  139. //IndexBlock的EndIp小于目标IP
  140. if (blockCurrent.StartIP < networkIP)
  141. {
  142. lower = index + 1;
  143. }
  144. //HeaderIndex的StartIP大于目标IP
  145. else
  146. {
  147. heighter = index - 1;
  148. }
  149. index = (lower + heighter) >> 1;
  150. blockCurrent = ref Unsafe.Add(ref blockStart, index);
  151. } while (true);
  152. buffer = new byte[blockCurrent.DataLen];
  153. int ptr = blockCurrent.DataPtr;
  154. _Stream.Seek(ptr, SeekOrigin.Begin);
  155. _Stream.Read(buffer, 0, buffer.Length);
  156. int cityId = Unsafe.As<byte, int>(ref buffer[0]);
  157. string region = Encoding.UTF8.GetString(buffer, 4, buffer.Length - 4);
  158. return new DataBlock(cityId, region, ptr);
  159. }
  160. /// <summary>
  161. /// 使用Binary进行IP检索
  162. /// </summary>
  163. /// <param name="ipStr">IP字符串</param>
  164. /// <returns></returns>
  165. public DataBlock BinarySearch(string ipStr)
  166. {
  167. return BinarySearch(Utils.String2NetworkIP(ipStr));
  168. }
  169. /// <summary>
  170. /// 使用Binary进行IP检索
  171. /// </summary>
  172. /// <param name="networkIP">网络字节序</param>
  173. /// <returns></returns>
  174. public DataBlock BinarySearch(uint networkIP)
  175. {
  176. int index = _IndexBlockCcount >> 1,
  177. lower = 0,
  178. heighter = _IndexBlockCcount,
  179. ptr = SuperBlock.FirstIndexPtr,
  180. indexLen = BlockSize.IndexBlockSize;
  181. //一次性加载整个HeaderIndex指向的IndexBlock块
  182. byte[] buffer = new byte[indexLen];
  183. _Stream.Seek(ptr + index * indexLen, SeekOrigin.Begin);
  184. _Stream.Read(buffer, 0, buffer.Length);
  185. ref IndexBlock blockCurrent = ref Unsafe.As<byte, IndexBlock>(ref buffer[0]);
  186. //二分法检索ipVal在HeaderIndex的命中的二级索引
  187. do
  188. {
  189. //Console.WriteLine("File BinarySearch" + index);
  190. //IP与IndexBlock的StartIP和EndIp区间,直接命中
  191. if (blockCurrent.StartIP <= networkIP && blockCurrent.EndIP >= networkIP)
  192. {
  193. break;
  194. }
  195. //IndexBlock的EndIp小于目标IP
  196. if (blockCurrent.StartIP < networkIP)
  197. {
  198. lower = index + 1;
  199. }
  200. //HeaderIndex的StartIP大于目标IP
  201. else
  202. {
  203. heighter = index - 1;
  204. }
  205. index = (lower + heighter) >> 1;
  206. _Stream.Seek(ptr + index * indexLen, SeekOrigin.Begin);
  207. _Stream.Read(buffer, 0, buffer.Length);
  208. blockCurrent = ref Unsafe.As<byte, IndexBlock>(ref buffer[0]);
  209. } while (true);
  210. buffer = new byte[blockCurrent.DataLen];
  211. ptr = blockCurrent.DataPtr;
  212. _Stream.Seek(ptr, SeekOrigin.Begin);
  213. _Stream.Read(buffer, 0, buffer.Length);
  214. int cityId = Unsafe.As<byte, int>(ref buffer[0]);
  215. string region = Encoding.UTF8.GetString(buffer, 4, buffer.Length - 4);
  216. return new DataBlock(cityId, region, ptr);
  217. }
  218. public void Dispose()
  219. {
  220. _Stream.Dispose();
  221. }
  222. }
  223. }