SpanHelpers.Byte.cs 46 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139
  1. // Licensed to the .NET Foundation under one or more agreements.
  2. // The .NET Foundation licenses this file to you under the MIT license.
  3. // See the LICENSE file in the project root for more information.
  4. using System.Diagnostics;
  5. using System.Runtime.CompilerServices;
  6. using System.Numerics;
  7. using Internal.Runtime.CompilerServices;
  8. #if BIT64
  9. using nuint = System.UInt64;
  10. #else
  11. using nuint = System.UInt32;
  12. #endif // BIT64
  13. namespace System
  14. {
  15. internal static partial class SpanHelpers // .Byte
  16. {
  17. public static int IndexOf(ref byte searchSpace, int searchSpaceLength, ref byte value, int valueLength)
  18. {
  19. Debug.Assert(searchSpaceLength >= 0);
  20. Debug.Assert(valueLength >= 0);
  21. if (valueLength == 0)
  22. return 0; // A zero-length sequence is always treated as "found" at the start of the search space.
  23. byte valueHead = value;
  24. ref byte valueTail = ref Unsafe.Add(ref value, 1);
  25. int valueTailLength = valueLength - 1;
  26. int remainingSearchSpaceLength = searchSpaceLength - valueTailLength;
  27. int index = 0;
  28. while (remainingSearchSpaceLength > 0)
  29. {
  30. // Do a quick search for the first element of "value".
  31. int relativeIndex = IndexOf(ref Unsafe.Add(ref searchSpace, index), valueHead, remainingSearchSpaceLength);
  32. if (relativeIndex == -1)
  33. break;
  34. remainingSearchSpaceLength -= relativeIndex;
  35. index += relativeIndex;
  36. if (remainingSearchSpaceLength <= 0)
  37. break; // The unsearched portion is now shorter than the sequence we're looking for. So it can't be there.
  38. // Found the first element of "value". See if the tail matches.
  39. if (SequenceEqual(ref Unsafe.Add(ref searchSpace, index + 1), ref valueTail, valueTailLength))
  40. return index; // The tail matched. Return a successful find.
  41. remainingSearchSpaceLength--;
  42. index++;
  43. }
  44. return -1;
  45. }
  46. public static int IndexOfAny(ref byte searchSpace, int searchSpaceLength, ref byte value, int valueLength)
  47. {
  48. Debug.Assert(searchSpaceLength >= 0);
  49. Debug.Assert(valueLength >= 0);
  50. if (valueLength == 0)
  51. return 0; // A zero-length sequence is always treated as "found" at the start of the search space.
  52. int index = -1;
  53. for (int i = 0; i < valueLength; i++)
  54. {
  55. var tempIndex = IndexOf(ref searchSpace, Unsafe.Add(ref value, i), searchSpaceLength);
  56. if ((uint)tempIndex < (uint)index)
  57. {
  58. index = tempIndex;
  59. // Reduce space for search, cause we don't care if we find the search value after the index of a previously found value
  60. searchSpaceLength = tempIndex;
  61. if (index == 0)
  62. break;
  63. }
  64. }
  65. return index;
  66. }
  67. public static int LastIndexOfAny(ref byte searchSpace, int searchSpaceLength, ref byte value, int valueLength)
  68. {
  69. Debug.Assert(searchSpaceLength >= 0);
  70. Debug.Assert(valueLength >= 0);
  71. if (valueLength == 0)
  72. return 0; // A zero-length sequence is always treated as "found" at the start of the search space.
  73. int index = -1;
  74. for (int i = 0; i < valueLength; i++)
  75. {
  76. var tempIndex = LastIndexOf(ref searchSpace, Unsafe.Add(ref value, i), searchSpaceLength);
  77. if (tempIndex > index)
  78. index = tempIndex;
  79. }
  80. return index;
  81. }
  82. // Adapted from IndexOf(...)
  83. public static unsafe bool Contains(ref byte searchSpace, byte value, int length)
  84. {
  85. Debug.Assert(length >= 0);
  86. uint uValue = value; // Use uint for comparisons to avoid unnecessary 8->32 extensions
  87. IntPtr index = (IntPtr)0; // Use IntPtr for arithmetic to avoid unnecessary 64->32->64 truncations
  88. IntPtr nLength = (IntPtr)length;
  89. if (Vector.IsHardwareAccelerated && length >= Vector<byte>.Count * 2)
  90. {
  91. int unaligned = (int)Unsafe.AsPointer(ref searchSpace) & (Vector<byte>.Count - 1);
  92. nLength = (IntPtr)((Vector<byte>.Count - unaligned) & (Vector<byte>.Count - 1));
  93. }
  94. SequentialScan:
  95. while ((byte*)nLength >= (byte*)8)
  96. {
  97. nLength -= 8;
  98. if (uValue == Unsafe.AddByteOffset(ref searchSpace, index + 0) ||
  99. uValue == Unsafe.AddByteOffset(ref searchSpace, index + 1) ||
  100. uValue == Unsafe.AddByteOffset(ref searchSpace, index + 2) ||
  101. uValue == Unsafe.AddByteOffset(ref searchSpace, index + 3) ||
  102. uValue == Unsafe.AddByteOffset(ref searchSpace, index + 4) ||
  103. uValue == Unsafe.AddByteOffset(ref searchSpace, index + 5) ||
  104. uValue == Unsafe.AddByteOffset(ref searchSpace, index + 6) ||
  105. uValue == Unsafe.AddByteOffset(ref searchSpace, index + 7))
  106. {
  107. goto Found;
  108. }
  109. index += 8;
  110. }
  111. if ((byte*)nLength >= (byte*)4)
  112. {
  113. nLength -= 4;
  114. if (uValue == Unsafe.AddByteOffset(ref searchSpace, index + 0) ||
  115. uValue == Unsafe.AddByteOffset(ref searchSpace, index + 1) ||
  116. uValue == Unsafe.AddByteOffset(ref searchSpace, index + 2) ||
  117. uValue == Unsafe.AddByteOffset(ref searchSpace, index + 3))
  118. {
  119. goto Found;
  120. }
  121. index += 4;
  122. }
  123. while ((byte*)nLength > (byte*)0)
  124. {
  125. nLength -= 1;
  126. if (uValue == Unsafe.AddByteOffset(ref searchSpace, index))
  127. goto Found;
  128. index += 1;
  129. }
  130. if (Vector.IsHardwareAccelerated && ((int)(byte*)index < length))
  131. {
  132. nLength = (IntPtr)((length - (int)(byte*)index) & ~(Vector<byte>.Count - 1));
  133. // Get comparison Vector
  134. Vector<byte> vComparison = new Vector<byte>(value);
  135. while ((byte*)nLength > (byte*)index)
  136. {
  137. var vMatches = Vector.Equals(vComparison, Unsafe.ReadUnaligned<Vector<byte>>(ref Unsafe.AddByteOffset(ref searchSpace, index)));
  138. if (Vector<byte>.Zero.Equals(vMatches))
  139. {
  140. index += Vector<byte>.Count;
  141. continue;
  142. }
  143. goto Found;
  144. }
  145. if ((int)(byte*)index < length)
  146. {
  147. nLength = (IntPtr)(length - (int)(byte*)index);
  148. goto SequentialScan;
  149. }
  150. }
  151. return false;
  152. Found:
  153. return true;
  154. }
  155. public static unsafe int IndexOf(ref byte searchSpace, byte value, int length)
  156. {
  157. Debug.Assert(length >= 0);
  158. uint uValue = value; // Use uint for comparisons to avoid unnecessary 8->32 extensions
  159. IntPtr index = (IntPtr)0; // Use IntPtr for arithmetic to avoid unnecessary 64->32->64 truncations
  160. IntPtr nLength = (IntPtr)length;
  161. if (Vector.IsHardwareAccelerated && length >= Vector<byte>.Count * 2)
  162. {
  163. int unaligned = (int)Unsafe.AsPointer(ref searchSpace) & (Vector<byte>.Count - 1);
  164. nLength = (IntPtr)((Vector<byte>.Count - unaligned) & (Vector<byte>.Count - 1));
  165. }
  166. SequentialScan:
  167. while ((byte*)nLength >= (byte*)8)
  168. {
  169. nLength -= 8;
  170. if (uValue == Unsafe.AddByteOffset(ref searchSpace, index))
  171. goto Found;
  172. if (uValue == Unsafe.AddByteOffset(ref searchSpace, index + 1))
  173. goto Found1;
  174. if (uValue == Unsafe.AddByteOffset(ref searchSpace, index + 2))
  175. goto Found2;
  176. if (uValue == Unsafe.AddByteOffset(ref searchSpace, index + 3))
  177. goto Found3;
  178. if (uValue == Unsafe.AddByteOffset(ref searchSpace, index + 4))
  179. goto Found4;
  180. if (uValue == Unsafe.AddByteOffset(ref searchSpace, index + 5))
  181. goto Found5;
  182. if (uValue == Unsafe.AddByteOffset(ref searchSpace, index + 6))
  183. goto Found6;
  184. if (uValue == Unsafe.AddByteOffset(ref searchSpace, index + 7))
  185. goto Found7;
  186. index += 8;
  187. }
  188. if ((byte*)nLength >= (byte*)4)
  189. {
  190. nLength -= 4;
  191. if (uValue == Unsafe.AddByteOffset(ref searchSpace, index))
  192. goto Found;
  193. if (uValue == Unsafe.AddByteOffset(ref searchSpace, index + 1))
  194. goto Found1;
  195. if (uValue == Unsafe.AddByteOffset(ref searchSpace, index + 2))
  196. goto Found2;
  197. if (uValue == Unsafe.AddByteOffset(ref searchSpace, index + 3))
  198. goto Found3;
  199. index += 4;
  200. }
  201. while ((byte*)nLength > (byte*)0)
  202. {
  203. nLength -= 1;
  204. if (uValue == Unsafe.AddByteOffset(ref searchSpace, index))
  205. goto Found;
  206. index += 1;
  207. }
  208. if (Vector.IsHardwareAccelerated && ((int)(byte*)index < length))
  209. {
  210. nLength = (IntPtr)((length - (int)(byte*)index) & ~(Vector<byte>.Count - 1));
  211. // Get comparison Vector
  212. Vector<byte> vComparison = new Vector<byte>(value);
  213. while ((byte*)nLength > (byte*)index)
  214. {
  215. var vMatches = Vector.Equals(vComparison, Unsafe.ReadUnaligned<Vector<byte>>(ref Unsafe.AddByteOffset(ref searchSpace, index)));
  216. if (Vector<byte>.Zero.Equals(vMatches))
  217. {
  218. index += Vector<byte>.Count;
  219. continue;
  220. }
  221. // Find offset of first match
  222. return (int)(byte*)index + LocateFirstFoundByte(vMatches);
  223. }
  224. if ((int)(byte*)index < length)
  225. {
  226. nLength = (IntPtr)(length - (int)(byte*)index);
  227. goto SequentialScan;
  228. }
  229. }
  230. return -1;
  231. Found: // Workaround for https://github.com/dotnet/coreclr/issues/13549
  232. return (int)(byte*)index;
  233. Found1:
  234. return (int)(byte*)(index + 1);
  235. Found2:
  236. return (int)(byte*)(index + 2);
  237. Found3:
  238. return (int)(byte*)(index + 3);
  239. Found4:
  240. return (int)(byte*)(index + 4);
  241. Found5:
  242. return (int)(byte*)(index + 5);
  243. Found6:
  244. return (int)(byte*)(index + 6);
  245. Found7:
  246. return (int)(byte*)(index + 7);
  247. }
  248. public static int LastIndexOf(ref byte searchSpace, int searchSpaceLength, ref byte value, int valueLength)
  249. {
  250. Debug.Assert(searchSpaceLength >= 0);
  251. Debug.Assert(valueLength >= 0);
  252. if (valueLength == 0)
  253. return 0; // A zero-length sequence is always treated as "found" at the start of the search space.
  254. byte valueHead = value;
  255. ref byte valueTail = ref Unsafe.Add(ref value, 1);
  256. int valueTailLength = valueLength - 1;
  257. int index = 0;
  258. for (; ; )
  259. {
  260. Debug.Assert(0 <= index && index <= searchSpaceLength); // Ensures no deceptive underflows in the computation of "remainingSearchSpaceLength".
  261. int remainingSearchSpaceLength = searchSpaceLength - index - valueTailLength;
  262. if (remainingSearchSpaceLength <= 0)
  263. break; // The unsearched portion is now shorter than the sequence we're looking for. So it can't be there.
  264. // Do a quick search for the first element of "value".
  265. int relativeIndex = LastIndexOf(ref searchSpace, valueHead, remainingSearchSpaceLength);
  266. if (relativeIndex == -1)
  267. break;
  268. // Found the first element of "value". See if the tail matches.
  269. if (SequenceEqual(ref Unsafe.Add(ref searchSpace, relativeIndex + 1), ref valueTail, valueTailLength))
  270. return relativeIndex; // The tail matched. Return a successful find.
  271. index += remainingSearchSpaceLength - relativeIndex;
  272. }
  273. return -1;
  274. }
  275. public static unsafe int LastIndexOf(ref byte searchSpace, byte value, int length)
  276. {
  277. Debug.Assert(length >= 0);
  278. uint uValue = value; // Use uint for comparisons to avoid unnecessary 8->32 extensions
  279. IntPtr index = (IntPtr)length; // Use IntPtr for arithmetic to avoid unnecessary 64->32->64 truncations
  280. IntPtr nLength = (IntPtr)length;
  281. if (Vector.IsHardwareAccelerated && length >= Vector<byte>.Count * 2)
  282. {
  283. int unaligned = (int)Unsafe.AsPointer(ref searchSpace) & (Vector<byte>.Count - 1);
  284. nLength = (IntPtr)(((length & (Vector<byte>.Count - 1)) + unaligned) & (Vector<byte>.Count - 1));
  285. }
  286. SequentialScan:
  287. while ((byte*)nLength >= (byte*)8)
  288. {
  289. nLength -= 8;
  290. index -= 8;
  291. if (uValue == Unsafe.AddByteOffset(ref searchSpace, index + 7))
  292. goto Found7;
  293. if (uValue == Unsafe.AddByteOffset(ref searchSpace, index + 6))
  294. goto Found6;
  295. if (uValue == Unsafe.AddByteOffset(ref searchSpace, index + 5))
  296. goto Found5;
  297. if (uValue == Unsafe.AddByteOffset(ref searchSpace, index + 4))
  298. goto Found4;
  299. if (uValue == Unsafe.AddByteOffset(ref searchSpace, index + 3))
  300. goto Found3;
  301. if (uValue == Unsafe.AddByteOffset(ref searchSpace, index + 2))
  302. goto Found2;
  303. if (uValue == Unsafe.AddByteOffset(ref searchSpace, index + 1))
  304. goto Found1;
  305. if (uValue == Unsafe.AddByteOffset(ref searchSpace, index))
  306. goto Found;
  307. }
  308. if ((byte*)nLength >= (byte*)4)
  309. {
  310. nLength -= 4;
  311. index -= 4;
  312. if (uValue == Unsafe.AddByteOffset(ref searchSpace, index + 3))
  313. goto Found3;
  314. if (uValue == Unsafe.AddByteOffset(ref searchSpace, index + 2))
  315. goto Found2;
  316. if (uValue == Unsafe.AddByteOffset(ref searchSpace, index + 1))
  317. goto Found1;
  318. if (uValue == Unsafe.AddByteOffset(ref searchSpace, index))
  319. goto Found;
  320. }
  321. while ((byte*)nLength > (byte*)0)
  322. {
  323. nLength -= 1;
  324. index -= 1;
  325. if (uValue == Unsafe.AddByteOffset(ref searchSpace, index))
  326. goto Found;
  327. }
  328. if (Vector.IsHardwareAccelerated && ((byte*)index > (byte*)0))
  329. {
  330. nLength = (IntPtr)((int)(byte*)index & ~(Vector<byte>.Count - 1));
  331. // Get comparison Vector
  332. Vector<byte> vComparison = new Vector<byte>(value);
  333. while ((byte*)nLength > (byte*)(Vector<byte>.Count - 1))
  334. {
  335. var vMatches = Vector.Equals(vComparison, Unsafe.ReadUnaligned<Vector<byte>>(ref Unsafe.AddByteOffset(ref searchSpace, index - Vector<byte>.Count)));
  336. if (Vector<byte>.Zero.Equals(vMatches))
  337. {
  338. index -= Vector<byte>.Count;
  339. nLength -= Vector<byte>.Count;
  340. continue;
  341. }
  342. // Find offset of first match
  343. return (int)(index) - Vector<byte>.Count + LocateLastFoundByte(vMatches);
  344. }
  345. if ((byte*)index > (byte*)0)
  346. {
  347. nLength = index;
  348. goto SequentialScan;
  349. }
  350. }
  351. return -1;
  352. Found: // Workaround for https://github.com/dotnet/coreclr/issues/13549
  353. return (int)(byte*)index;
  354. Found1:
  355. return (int)(byte*)(index + 1);
  356. Found2:
  357. return (int)(byte*)(index + 2);
  358. Found3:
  359. return (int)(byte*)(index + 3);
  360. Found4:
  361. return (int)(byte*)(index + 4);
  362. Found5:
  363. return (int)(byte*)(index + 5);
  364. Found6:
  365. return (int)(byte*)(index + 6);
  366. Found7:
  367. return (int)(byte*)(index + 7);
  368. }
  369. public static unsafe int IndexOfAny(ref byte searchSpace, byte value0, byte value1, int length)
  370. {
  371. Debug.Assert(length >= 0);
  372. uint uValue0 = value0; // Use uint for comparisons to avoid unnecessary 8->32 extensions
  373. uint uValue1 = value1; // Use uint for comparisons to avoid unnecessary 8->32 extensions
  374. IntPtr index = (IntPtr)0; // Use IntPtr for arithmetic to avoid unnecessary 64->32->64 truncations
  375. IntPtr nLength = (IntPtr)length;
  376. if (Vector.IsHardwareAccelerated && length >= Vector<byte>.Count * 2)
  377. {
  378. int unaligned = (int)Unsafe.AsPointer(ref searchSpace) & (Vector<byte>.Count - 1);
  379. nLength = (IntPtr)((Vector<byte>.Count - unaligned) & (Vector<byte>.Count - 1));
  380. }
  381. SequentialScan:
  382. uint lookUp;
  383. while ((byte*)nLength >= (byte*)8)
  384. {
  385. nLength -= 8;
  386. lookUp = Unsafe.AddByteOffset(ref searchSpace, index);
  387. if (uValue0 == lookUp || uValue1 == lookUp)
  388. goto Found;
  389. lookUp = Unsafe.AddByteOffset(ref searchSpace, index + 1);
  390. if (uValue0 == lookUp || uValue1 == lookUp)
  391. goto Found1;
  392. lookUp = Unsafe.AddByteOffset(ref searchSpace, index + 2);
  393. if (uValue0 == lookUp || uValue1 == lookUp)
  394. goto Found2;
  395. lookUp = Unsafe.AddByteOffset(ref searchSpace, index + 3);
  396. if (uValue0 == lookUp || uValue1 == lookUp)
  397. goto Found3;
  398. lookUp = Unsafe.AddByteOffset(ref searchSpace, index + 4);
  399. if (uValue0 == lookUp || uValue1 == lookUp)
  400. goto Found4;
  401. lookUp = Unsafe.AddByteOffset(ref searchSpace, index + 5);
  402. if (uValue0 == lookUp || uValue1 == lookUp)
  403. goto Found5;
  404. lookUp = Unsafe.AddByteOffset(ref searchSpace, index + 6);
  405. if (uValue0 == lookUp || uValue1 == lookUp)
  406. goto Found6;
  407. lookUp = Unsafe.AddByteOffset(ref searchSpace, index + 7);
  408. if (uValue0 == lookUp || uValue1 == lookUp)
  409. goto Found7;
  410. index += 8;
  411. }
  412. if ((byte*)nLength >= (byte*)4)
  413. {
  414. nLength -= 4;
  415. lookUp = Unsafe.AddByteOffset(ref searchSpace, index);
  416. if (uValue0 == lookUp || uValue1 == lookUp)
  417. goto Found;
  418. lookUp = Unsafe.AddByteOffset(ref searchSpace, index + 1);
  419. if (uValue0 == lookUp || uValue1 == lookUp)
  420. goto Found1;
  421. lookUp = Unsafe.AddByteOffset(ref searchSpace, index + 2);
  422. if (uValue0 == lookUp || uValue1 == lookUp)
  423. goto Found2;
  424. lookUp = Unsafe.AddByteOffset(ref searchSpace, index + 3);
  425. if (uValue0 == lookUp || uValue1 == lookUp)
  426. goto Found3;
  427. index += 4;
  428. }
  429. while ((byte*)nLength > (byte*)0)
  430. {
  431. nLength -= 1;
  432. lookUp = Unsafe.AddByteOffset(ref searchSpace, index);
  433. if (uValue0 == lookUp || uValue1 == lookUp)
  434. goto Found;
  435. index += 1;
  436. }
  437. if (Vector.IsHardwareAccelerated && ((int)(byte*)index < length))
  438. {
  439. nLength = (IntPtr)((length - (int)(byte*)index) & ~(Vector<byte>.Count - 1));
  440. // Get comparison Vector
  441. Vector<byte> values0 = new Vector<byte>(value0);
  442. Vector<byte> values1 = new Vector<byte>(value1);
  443. while ((byte*)nLength > (byte*)index)
  444. {
  445. Vector<byte> vData = Unsafe.ReadUnaligned<Vector<byte>>(ref Unsafe.AddByteOffset(ref searchSpace, index));
  446. var vMatches = Vector.BitwiseOr(
  447. Vector.Equals(vData, values0),
  448. Vector.Equals(vData, values1));
  449. if (Vector<byte>.Zero.Equals(vMatches))
  450. {
  451. index += Vector<byte>.Count;
  452. continue;
  453. }
  454. // Find offset of first match
  455. return (int)(byte*)index + LocateFirstFoundByte(vMatches);
  456. }
  457. if ((int)(byte*)index < length)
  458. {
  459. nLength = (IntPtr)(length - (int)(byte*)index);
  460. goto SequentialScan;
  461. }
  462. }
  463. return -1;
  464. Found: // Workaround for https://github.com/dotnet/coreclr/issues/13549
  465. return (int)(byte*)index;
  466. Found1:
  467. return (int)(byte*)(index + 1);
  468. Found2:
  469. return (int)(byte*)(index + 2);
  470. Found3:
  471. return (int)(byte*)(index + 3);
  472. Found4:
  473. return (int)(byte*)(index + 4);
  474. Found5:
  475. return (int)(byte*)(index + 5);
  476. Found6:
  477. return (int)(byte*)(index + 6);
  478. Found7:
  479. return (int)(byte*)(index + 7);
  480. }
  481. public static unsafe int IndexOfAny(ref byte searchSpace, byte value0, byte value1, byte value2, int length)
  482. {
  483. Debug.Assert(length >= 0);
  484. uint uValue0 = value0; // Use uint for comparisons to avoid unnecessary 8->32 extensions
  485. uint uValue1 = value1; // Use uint for comparisons to avoid unnecessary 8->32 extensions
  486. uint uValue2 = value2; // Use uint for comparisons to avoid unnecessary 8->32 extensions
  487. IntPtr index = (IntPtr)0; // Use IntPtr for arithmetic to avoid unnecessary 64->32->64 truncations
  488. IntPtr nLength = (IntPtr)length;
  489. if (Vector.IsHardwareAccelerated && length >= Vector<byte>.Count * 2)
  490. {
  491. int unaligned = (int)Unsafe.AsPointer(ref searchSpace) & (Vector<byte>.Count - 1);
  492. nLength = (IntPtr)((Vector<byte>.Count - unaligned) & (Vector<byte>.Count - 1));
  493. }
  494. SequentialScan:
  495. uint lookUp;
  496. while ((byte*)nLength >= (byte*)8)
  497. {
  498. nLength -= 8;
  499. lookUp = Unsafe.AddByteOffset(ref searchSpace, index);
  500. if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp)
  501. goto Found;
  502. lookUp = Unsafe.AddByteOffset(ref searchSpace, index + 1);
  503. if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp)
  504. goto Found1;
  505. lookUp = Unsafe.AddByteOffset(ref searchSpace, index + 2);
  506. if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp)
  507. goto Found2;
  508. lookUp = Unsafe.AddByteOffset(ref searchSpace, index + 3);
  509. if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp)
  510. goto Found3;
  511. lookUp = Unsafe.AddByteOffset(ref searchSpace, index + 4);
  512. if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp)
  513. goto Found4;
  514. lookUp = Unsafe.AddByteOffset(ref searchSpace, index + 5);
  515. if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp)
  516. goto Found5;
  517. lookUp = Unsafe.AddByteOffset(ref searchSpace, index + 6);
  518. if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp)
  519. goto Found6;
  520. lookUp = Unsafe.AddByteOffset(ref searchSpace, index + 7);
  521. if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp)
  522. goto Found7;
  523. index += 8;
  524. }
  525. if ((byte*)nLength >= (byte*)4)
  526. {
  527. nLength -= 4;
  528. lookUp = Unsafe.AddByteOffset(ref searchSpace, index);
  529. if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp)
  530. goto Found;
  531. lookUp = Unsafe.AddByteOffset(ref searchSpace, index + 1);
  532. if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp)
  533. goto Found1;
  534. lookUp = Unsafe.AddByteOffset(ref searchSpace, index + 2);
  535. if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp)
  536. goto Found2;
  537. lookUp = Unsafe.AddByteOffset(ref searchSpace, index + 3);
  538. if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp)
  539. goto Found3;
  540. index += 4;
  541. }
  542. while ((byte*)nLength > (byte*)0)
  543. {
  544. nLength -= 1;
  545. lookUp = Unsafe.AddByteOffset(ref searchSpace, index);
  546. if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp)
  547. goto Found;
  548. index += 1;
  549. }
  550. if (Vector.IsHardwareAccelerated && ((int)(byte*)index < length))
  551. {
  552. nLength = (IntPtr)((length - (int)(byte*)index) & ~(Vector<byte>.Count - 1));
  553. // Get comparison Vector
  554. Vector<byte> values0 = new Vector<byte>(value0);
  555. Vector<byte> values1 = new Vector<byte>(value1);
  556. Vector<byte> values2 = new Vector<byte>(value2);
  557. while ((byte*)nLength > (byte*)index)
  558. {
  559. Vector<byte> vData = Unsafe.ReadUnaligned<Vector<byte>>(ref Unsafe.AddByteOffset(ref searchSpace, index));
  560. var vMatches = Vector.BitwiseOr(
  561. Vector.BitwiseOr(
  562. Vector.Equals(vData, values0),
  563. Vector.Equals(vData, values1)),
  564. Vector.Equals(vData, values2));
  565. if (Vector<byte>.Zero.Equals(vMatches))
  566. {
  567. index += Vector<byte>.Count;
  568. continue;
  569. }
  570. // Find offset of first match
  571. return (int)(byte*)index + LocateFirstFoundByte(vMatches);
  572. }
  573. if ((int)(byte*)index < length)
  574. {
  575. nLength = (IntPtr)(length - (int)(byte*)index);
  576. goto SequentialScan;
  577. }
  578. }
  579. return -1;
  580. Found: // Workaround for https://github.com/dotnet/coreclr/issues/13549
  581. return (int)(byte*)index;
  582. Found1:
  583. return (int)(byte*)(index + 1);
  584. Found2:
  585. return (int)(byte*)(index + 2);
  586. Found3:
  587. return (int)(byte*)(index + 3);
  588. Found4:
  589. return (int)(byte*)(index + 4);
  590. Found5:
  591. return (int)(byte*)(index + 5);
  592. Found6:
  593. return (int)(byte*)(index + 6);
  594. Found7:
  595. return (int)(byte*)(index + 7);
  596. }
  597. public static unsafe int LastIndexOfAny(ref byte searchSpace, byte value0, byte value1, int length)
  598. {
  599. Debug.Assert(length >= 0);
  600. uint uValue0 = value0; // Use uint for comparisons to avoid unnecessary 8->32 extensions
  601. uint uValue1 = value1; // Use uint for comparisons to avoid unnecessary 8->32 extensions
  602. IntPtr index = (IntPtr)length; // Use IntPtr for arithmetic to avoid unnecessary 64->32->64 truncations
  603. IntPtr nLength = (IntPtr)length;
  604. if (Vector.IsHardwareAccelerated && length >= Vector<byte>.Count * 2)
  605. {
  606. int unaligned = (int)Unsafe.AsPointer(ref searchSpace) & (Vector<byte>.Count - 1);
  607. nLength = (IntPtr)(((length & (Vector<byte>.Count - 1)) + unaligned) & (Vector<byte>.Count - 1));
  608. }
  609. SequentialScan:
  610. uint lookUp;
  611. while ((byte*)nLength >= (byte*)8)
  612. {
  613. nLength -= 8;
  614. index -= 8;
  615. lookUp = Unsafe.AddByteOffset(ref searchSpace, index + 7);
  616. if (uValue0 == lookUp || uValue1 == lookUp)
  617. goto Found7;
  618. lookUp = Unsafe.AddByteOffset(ref searchSpace, index + 6);
  619. if (uValue0 == lookUp || uValue1 == lookUp)
  620. goto Found6;
  621. lookUp = Unsafe.AddByteOffset(ref searchSpace, index + 5);
  622. if (uValue0 == lookUp || uValue1 == lookUp)
  623. goto Found5;
  624. lookUp = Unsafe.AddByteOffset(ref searchSpace, index + 4);
  625. if (uValue0 == lookUp || uValue1 == lookUp)
  626. goto Found4;
  627. lookUp = Unsafe.AddByteOffset(ref searchSpace, index + 3);
  628. if (uValue0 == lookUp || uValue1 == lookUp)
  629. goto Found3;
  630. lookUp = Unsafe.AddByteOffset(ref searchSpace, index + 2);
  631. if (uValue0 == lookUp || uValue1 == lookUp)
  632. goto Found2;
  633. lookUp = Unsafe.AddByteOffset(ref searchSpace, index + 1);
  634. if (uValue0 == lookUp || uValue1 == lookUp)
  635. goto Found1;
  636. lookUp = Unsafe.AddByteOffset(ref searchSpace, index);
  637. if (uValue0 == lookUp || uValue1 == lookUp)
  638. goto Found;
  639. }
  640. if ((byte*)nLength >= (byte*)4)
  641. {
  642. nLength -= 4;
  643. index -= 4;
  644. lookUp = Unsafe.AddByteOffset(ref searchSpace, index + 3);
  645. if (uValue0 == lookUp || uValue1 == lookUp)
  646. goto Found3;
  647. lookUp = Unsafe.AddByteOffset(ref searchSpace, index + 2);
  648. if (uValue0 == lookUp || uValue1 == lookUp)
  649. goto Found2;
  650. lookUp = Unsafe.AddByteOffset(ref searchSpace, index + 1);
  651. if (uValue0 == lookUp || uValue1 == lookUp)
  652. goto Found1;
  653. lookUp = Unsafe.AddByteOffset(ref searchSpace, index);
  654. if (uValue0 == lookUp || uValue1 == lookUp)
  655. goto Found;
  656. }
  657. while ((byte*)nLength > (byte*)0)
  658. {
  659. nLength -= 1;
  660. index -= 1;
  661. lookUp = Unsafe.AddByteOffset(ref searchSpace, index);
  662. if (uValue0 == lookUp || uValue1 == lookUp)
  663. goto Found;
  664. }
  665. if (Vector.IsHardwareAccelerated && ((byte*)index > (byte*)0))
  666. {
  667. nLength = (IntPtr)((int)(byte*)index & ~(Vector<byte>.Count - 1));
  668. // Get comparison Vector
  669. Vector<byte> values0 = new Vector<byte>(value0);
  670. Vector<byte> values1 = new Vector<byte>(value1);
  671. while ((byte*)nLength > (byte*)(Vector<byte>.Count - 1))
  672. {
  673. Vector<byte> vData = Unsafe.ReadUnaligned<Vector<byte>>(ref Unsafe.AddByteOffset(ref searchSpace, index - Vector<byte>.Count));
  674. var vMatches = Vector.BitwiseOr(
  675. Vector.Equals(vData, values0),
  676. Vector.Equals(vData, values1));
  677. if (Vector<byte>.Zero.Equals(vMatches))
  678. {
  679. index -= Vector<byte>.Count;
  680. nLength -= Vector<byte>.Count;
  681. continue;
  682. }
  683. // Find offset of first match
  684. return (int)(index) - Vector<byte>.Count + LocateLastFoundByte(vMatches);
  685. }
  686. if ((byte*)index > (byte*)0)
  687. {
  688. nLength = index;
  689. goto SequentialScan;
  690. }
  691. }
  692. return -1;
  693. Found: // Workaround for https://github.com/dotnet/coreclr/issues/13549
  694. return (int)(byte*)index;
  695. Found1:
  696. return (int)(byte*)(index + 1);
  697. Found2:
  698. return (int)(byte*)(index + 2);
  699. Found3:
  700. return (int)(byte*)(index + 3);
  701. Found4:
  702. return (int)(byte*)(index + 4);
  703. Found5:
  704. return (int)(byte*)(index + 5);
  705. Found6:
  706. return (int)(byte*)(index + 6);
  707. Found7:
  708. return (int)(byte*)(index + 7);
  709. }
  710. public static unsafe int LastIndexOfAny(ref byte searchSpace, byte value0, byte value1, byte value2, int length)
  711. {
  712. Debug.Assert(length >= 0);
  713. uint uValue0 = value0; // Use uint for comparisons to avoid unnecessary 8->32 extensions
  714. uint uValue1 = value1; // Use uint for comparisons to avoid unnecessary 8->32 extensions
  715. uint uValue2 = value2; // Use uint for comparisons to avoid unnecessary 8->32 extensions
  716. IntPtr index = (IntPtr)length; // Use IntPtr for arithmetic to avoid unnecessary 64->32->64 truncations
  717. IntPtr nLength = (IntPtr)length;
  718. if (Vector.IsHardwareAccelerated && length >= Vector<byte>.Count * 2)
  719. {
  720. int unaligned = (int)Unsafe.AsPointer(ref searchSpace) & (Vector<byte>.Count - 1);
  721. nLength = (IntPtr)(((length & (Vector<byte>.Count - 1)) + unaligned) & (Vector<byte>.Count - 1));
  722. }
  723. SequentialScan:
  724. uint lookUp;
  725. while ((byte*)nLength >= (byte*)8)
  726. {
  727. nLength -= 8;
  728. index -= 8;
  729. lookUp = Unsafe.AddByteOffset(ref searchSpace, index + 7);
  730. if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp)
  731. goto Found7;
  732. lookUp = Unsafe.AddByteOffset(ref searchSpace, index + 6);
  733. if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp)
  734. goto Found6;
  735. lookUp = Unsafe.AddByteOffset(ref searchSpace, index + 5);
  736. if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp)
  737. goto Found5;
  738. lookUp = Unsafe.AddByteOffset(ref searchSpace, index + 4);
  739. if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp)
  740. goto Found4;
  741. lookUp = Unsafe.AddByteOffset(ref searchSpace, index + 3);
  742. if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp)
  743. goto Found3;
  744. lookUp = Unsafe.AddByteOffset(ref searchSpace, index + 2);
  745. if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp)
  746. goto Found2;
  747. lookUp = Unsafe.AddByteOffset(ref searchSpace, index + 1);
  748. if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp)
  749. goto Found1;
  750. lookUp = Unsafe.AddByteOffset(ref searchSpace, index);
  751. if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp)
  752. goto Found;
  753. }
  754. if ((byte*)nLength >= (byte*)4)
  755. {
  756. nLength -= 4;
  757. index -= 4;
  758. lookUp = Unsafe.AddByteOffset(ref searchSpace, index + 3);
  759. if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp)
  760. goto Found3;
  761. lookUp = Unsafe.AddByteOffset(ref searchSpace, index + 2);
  762. if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp)
  763. goto Found2;
  764. lookUp = Unsafe.AddByteOffset(ref searchSpace, index + 1);
  765. if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp)
  766. goto Found1;
  767. lookUp = Unsafe.AddByteOffset(ref searchSpace, index);
  768. if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp)
  769. goto Found;
  770. }
  771. while ((byte*)nLength > (byte*)0)
  772. {
  773. nLength -= 1;
  774. index -= 1;
  775. lookUp = Unsafe.AddByteOffset(ref searchSpace, index);
  776. if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp)
  777. goto Found;
  778. }
  779. if (Vector.IsHardwareAccelerated && ((byte*)index > (byte*)0))
  780. {
  781. nLength = (IntPtr)((int)(byte*)index & ~(Vector<byte>.Count - 1));
  782. // Get comparison Vector
  783. Vector<byte> values0 = new Vector<byte>(value0);
  784. Vector<byte> values1 = new Vector<byte>(value1);
  785. Vector<byte> values2 = new Vector<byte>(value2);
  786. while ((byte*)nLength > (byte*)(Vector<byte>.Count - 1))
  787. {
  788. Vector<byte> vData = Unsafe.ReadUnaligned<Vector<byte>>(ref Unsafe.AddByteOffset(ref searchSpace, index - Vector<byte>.Count));
  789. var vMatches = Vector.BitwiseOr(
  790. Vector.BitwiseOr(
  791. Vector.Equals(vData, values0),
  792. Vector.Equals(vData, values1)),
  793. Vector.Equals(vData, values2));
  794. if (Vector<byte>.Zero.Equals(vMatches))
  795. {
  796. index -= Vector<byte>.Count;
  797. nLength -= Vector<byte>.Count;
  798. continue;
  799. }
  800. // Find offset of first match
  801. return (int)(index) - Vector<byte>.Count + LocateLastFoundByte(vMatches);
  802. }
  803. if ((byte*)index > (byte*)0)
  804. {
  805. nLength = index;
  806. goto SequentialScan;
  807. }
  808. }
  809. return -1;
  810. Found: // Workaround for https://github.com/dotnet/coreclr/issues/13549
  811. return (int)(byte*)index;
  812. Found1:
  813. return (int)(byte*)(index + 1);
  814. Found2:
  815. return (int)(byte*)(index + 2);
  816. Found3:
  817. return (int)(byte*)(index + 3);
  818. Found4:
  819. return (int)(byte*)(index + 4);
  820. Found5:
  821. return (int)(byte*)(index + 5);
  822. Found6:
  823. return (int)(byte*)(index + 6);
  824. Found7:
  825. return (int)(byte*)(index + 7);
  826. }
  827. // Optimized byte-based SequenceEquals. The "length" parameter for this one is declared a nuint rather than int as we also use it for types other than byte
  828. // where the length can exceed 2Gb once scaled by sizeof(T).
  829. public static unsafe bool SequenceEqual(ref byte first, ref byte second, nuint length)
  830. {
  831. if (Unsafe.AreSame(ref first, ref second))
  832. goto Equal;
  833. IntPtr i = (IntPtr)0; // Use IntPtr for arithmetic to avoid unnecessary 64->32->64 truncations
  834. IntPtr n = (IntPtr)(void*)length;
  835. if (Vector.IsHardwareAccelerated && (byte*)n >= (byte*)Vector<byte>.Count)
  836. {
  837. n -= Vector<byte>.Count;
  838. while ((byte*)n > (byte*)i)
  839. {
  840. if (Unsafe.ReadUnaligned<Vector<byte>>(ref Unsafe.AddByteOffset(ref first, i)) !=
  841. Unsafe.ReadUnaligned<Vector<byte>>(ref Unsafe.AddByteOffset(ref second, i)))
  842. {
  843. goto NotEqual;
  844. }
  845. i += Vector<byte>.Count;
  846. }
  847. return Unsafe.ReadUnaligned<Vector<byte>>(ref Unsafe.AddByteOffset(ref first, n)) ==
  848. Unsafe.ReadUnaligned<Vector<byte>>(ref Unsafe.AddByteOffset(ref second, n));
  849. }
  850. if ((byte*)n >= (byte*)sizeof(UIntPtr))
  851. {
  852. n -= sizeof(UIntPtr);
  853. while ((byte*)n > (byte*)i)
  854. {
  855. if (Unsafe.ReadUnaligned<UIntPtr>(ref Unsafe.AddByteOffset(ref first, i)) !=
  856. Unsafe.ReadUnaligned<UIntPtr>(ref Unsafe.AddByteOffset(ref second, i)))
  857. {
  858. goto NotEqual;
  859. }
  860. i += sizeof(UIntPtr);
  861. }
  862. return Unsafe.ReadUnaligned<UIntPtr>(ref Unsafe.AddByteOffset(ref first, n)) ==
  863. Unsafe.ReadUnaligned<UIntPtr>(ref Unsafe.AddByteOffset(ref second, n));
  864. }
  865. while ((byte*)n > (byte*)i)
  866. {
  867. if (Unsafe.AddByteOffset(ref first, i) != Unsafe.AddByteOffset(ref second, i))
  868. goto NotEqual;
  869. i += 1;
  870. }
  871. Equal:
  872. return true;
  873. NotEqual: // Workaround for https://github.com/dotnet/coreclr/issues/13549
  874. return false;
  875. }
  876. // Vector sub-search adapted from https://github.com/aspnet/KestrelHttpServer/pull/1138
  877. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  878. private static int LocateFirstFoundByte(Vector<byte> match)
  879. {
  880. var vector64 = Vector.AsVectorUInt64(match);
  881. ulong candidate = 0;
  882. int i = 0;
  883. // Pattern unrolled by jit https://github.com/dotnet/coreclr/pull/8001
  884. for (; i < Vector<ulong>.Count; i++)
  885. {
  886. candidate = vector64[i];
  887. if (candidate != 0)
  888. {
  889. break;
  890. }
  891. }
  892. // Single LEA instruction with jitted const (using function result)
  893. return i * 8 + LocateFirstFoundByte(candidate);
  894. }
  895. public static unsafe int SequenceCompareTo(ref byte first, int firstLength, ref byte second, int secondLength)
  896. {
  897. Debug.Assert(firstLength >= 0);
  898. Debug.Assert(secondLength >= 0);
  899. if (Unsafe.AreSame(ref first, ref second))
  900. goto Equal;
  901. IntPtr minLength = (IntPtr)((firstLength < secondLength) ? firstLength : secondLength);
  902. IntPtr i = (IntPtr)0; // Use IntPtr for arithmetic to avoid unnecessary 64->32->64 truncations
  903. IntPtr n = (IntPtr)(void*)minLength;
  904. if (Vector.IsHardwareAccelerated && (byte*)n > (byte*)Vector<byte>.Count)
  905. {
  906. n -= Vector<byte>.Count;
  907. while ((byte*)n > (byte*)i)
  908. {
  909. if (Unsafe.ReadUnaligned<Vector<byte>>(ref Unsafe.AddByteOffset(ref first, i)) !=
  910. Unsafe.ReadUnaligned<Vector<byte>>(ref Unsafe.AddByteOffset(ref second, i)))
  911. {
  912. goto NotEqual;
  913. }
  914. i += Vector<byte>.Count;
  915. }
  916. goto NotEqual;
  917. }
  918. if ((byte*)n > (byte*)sizeof(UIntPtr))
  919. {
  920. n -= sizeof(UIntPtr);
  921. while ((byte*)n > (byte*)i)
  922. {
  923. if (Unsafe.ReadUnaligned<UIntPtr>(ref Unsafe.AddByteOffset(ref first, i)) !=
  924. Unsafe.ReadUnaligned<UIntPtr>(ref Unsafe.AddByteOffset(ref second, i)))
  925. {
  926. goto NotEqual;
  927. }
  928. i += sizeof(UIntPtr);
  929. }
  930. }
  931. NotEqual: // Workaround for https://github.com/dotnet/coreclr/issues/13549
  932. while ((byte*)minLength > (byte*)i)
  933. {
  934. int result = Unsafe.AddByteOffset(ref first, i).CompareTo(Unsafe.AddByteOffset(ref second, i));
  935. if (result != 0)
  936. return result;
  937. i += 1;
  938. }
  939. Equal:
  940. return firstLength - secondLength;
  941. }
  942. // Vector sub-search adapted from https://github.com/aspnet/KestrelHttpServer/pull/1138
  943. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  944. private static int LocateLastFoundByte(Vector<byte> match)
  945. {
  946. var vector64 = Vector.AsVectorUInt64(match);
  947. ulong candidate = 0;
  948. int i = Vector<ulong>.Count - 1;
  949. // Pattern unrolled by jit https://github.com/dotnet/coreclr/pull/8001
  950. for (; i >= 0; i--)
  951. {
  952. candidate = vector64[i];
  953. if (candidate != 0)
  954. {
  955. break;
  956. }
  957. }
  958. // Single LEA instruction with jitted const (using function result)
  959. return i * 8 + LocateLastFoundByte(candidate);
  960. }
  961. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  962. private static int LocateFirstFoundByte(ulong match)
  963. {
  964. // Flag least significant power of two bit
  965. var powerOfTwoFlag = match ^ (match - 1);
  966. // Shift all powers of two into the high byte and extract
  967. return (int)((powerOfTwoFlag * XorPowerOfTwoToHighByte) >> 57);
  968. }
  969. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  970. private static int LocateLastFoundByte(ulong match)
  971. {
  972. // Find the most significant byte that has its highest bit set
  973. int index = 7;
  974. while ((long)match > 0)
  975. {
  976. match = match << 8;
  977. index--;
  978. }
  979. return index;
  980. }
  981. private const ulong XorPowerOfTwoToHighByte = (0x07ul |
  982. 0x06ul << 8 |
  983. 0x05ul << 16 |
  984. 0x04ul << 24 |
  985. 0x03ul << 32 |
  986. 0x02ul << 40 |
  987. 0x01ul << 48) + 1;
  988. }
  989. }