SpanHelpers.Byte.cs 47 KB

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