Guid.cs 42 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232
  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.Globalization;
  6. using System.Runtime.CompilerServices;
  7. using System.Runtime.InteropServices;
  8. using Internal.Runtime.CompilerServices;
  9. namespace System
  10. {
  11. // Represents a Globally Unique Identifier.
  12. [StructLayout(LayoutKind.Sequential)]
  13. [Serializable]
  14. [Runtime.Versioning.NonVersionable] // This only applies to field layout
  15. [TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
  16. public partial struct Guid : IFormattable, IComparable, IComparable<Guid>, IEquatable<Guid>, ISpanFormattable
  17. {
  18. public static readonly Guid Empty = new Guid();
  19. ////////////////////////////////////////////////////////////////////////////////
  20. // Member variables
  21. ////////////////////////////////////////////////////////////////////////////////
  22. private int _a; // Do not rename (binary serialization)
  23. private short _b; // Do not rename (binary serialization)
  24. private short _c; // Do not rename (binary serialization)
  25. private byte _d; // Do not rename (binary serialization)
  26. private byte _e; // Do not rename (binary serialization)
  27. private byte _f; // Do not rename (binary serialization)
  28. private byte _g; // Do not rename (binary serialization)
  29. private byte _h; // Do not rename (binary serialization)
  30. private byte _i; // Do not rename (binary serialization)
  31. private byte _j; // Do not rename (binary serialization)
  32. private byte _k; // Do not rename (binary serialization)
  33. ////////////////////////////////////////////////////////////////////////////////
  34. // Constructors
  35. ////////////////////////////////////////////////////////////////////////////////
  36. // Creates a new guid from an array of bytes.
  37. public Guid(byte[] b) :
  38. this(new ReadOnlySpan<byte>(b ?? throw new ArgumentNullException(nameof(b))))
  39. {
  40. }
  41. // Creates a new guid from a read-only span.
  42. public Guid(ReadOnlySpan<byte> b)
  43. {
  44. if ((uint)b.Length != 16)
  45. throw new ArgumentException(SR.Format(SR.Arg_GuidArrayCtor, "16"), nameof(b));
  46. if (BitConverter.IsLittleEndian)
  47. {
  48. this = MemoryMarshal.Read<Guid>(b);
  49. return;
  50. }
  51. // slower path for BigEndian:
  52. _k = b[15]; // hoist bounds checks
  53. _a = b[3] << 24 | b[2] << 16 | b[1] << 8 | b[0];
  54. _b = (short)(b[5] << 8 | b[4]);
  55. _c = (short)(b[7] << 8 | b[6]);
  56. _d = b[8];
  57. _e = b[9];
  58. _f = b[10];
  59. _g = b[11];
  60. _h = b[12];
  61. _i = b[13];
  62. _j = b[14];
  63. }
  64. [CLSCompliant(false)]
  65. public Guid(uint a, ushort b, ushort c, byte d, byte e, byte f, byte g, byte h, byte i, byte j, byte k)
  66. {
  67. _a = (int)a;
  68. _b = (short)b;
  69. _c = (short)c;
  70. _d = d;
  71. _e = e;
  72. _f = f;
  73. _g = g;
  74. _h = h;
  75. _i = i;
  76. _j = j;
  77. _k = k;
  78. }
  79. // Creates a new GUID initialized to the value represented by the arguments.
  80. //
  81. public Guid(int a, short b, short c, byte[] d)
  82. {
  83. if (d == null)
  84. throw new ArgumentNullException(nameof(d));
  85. // Check that array is not too big
  86. if (d.Length != 8)
  87. throw new ArgumentException(SR.Format(SR.Arg_GuidArrayCtor, "8"), nameof(d));
  88. _a = a;
  89. _b = b;
  90. _c = c;
  91. _k = d[7]; // hoist bounds checks
  92. _d = d[0];
  93. _e = d[1];
  94. _f = d[2];
  95. _g = d[3];
  96. _h = d[4];
  97. _i = d[5];
  98. _j = d[6];
  99. }
  100. // Creates a new GUID initialized to the value represented by the
  101. // arguments. The bytes are specified like this to avoid endianness issues.
  102. public Guid(int a, short b, short c, byte d, byte e, byte f, byte g, byte h, byte i, byte j, byte k)
  103. {
  104. _a = a;
  105. _b = b;
  106. _c = c;
  107. _d = d;
  108. _e = e;
  109. _f = f;
  110. _g = g;
  111. _h = h;
  112. _i = i;
  113. _j = j;
  114. _k = k;
  115. }
  116. private enum GuidParseThrowStyle : byte
  117. {
  118. None = 0,
  119. All = 1,
  120. AllButOverflow = 2
  121. }
  122. // This will store the result of the parsing. And it will eventually be used to construct a Guid instance.
  123. private struct GuidResult
  124. {
  125. internal readonly GuidParseThrowStyle _throwStyle;
  126. internal Guid _parsedGuid;
  127. private bool _overflow;
  128. private string _failureMessageID;
  129. private object _failureMessageFormatArgument;
  130. internal GuidResult(GuidParseThrowStyle canThrow) : this()
  131. {
  132. _throwStyle = canThrow;
  133. }
  134. internal void SetFailure(bool overflow, string failureMessageID)
  135. {
  136. _overflow = overflow;
  137. _failureMessageID = failureMessageID;
  138. if (_throwStyle != GuidParseThrowStyle.None)
  139. {
  140. throw CreateGuidParseException();
  141. }
  142. }
  143. internal void SetFailure(bool overflow, string failureMessageID, object failureMessageFormatArgument)
  144. {
  145. _failureMessageFormatArgument = failureMessageFormatArgument;
  146. SetFailure(overflow, failureMessageID);
  147. }
  148. internal Exception CreateGuidParseException()
  149. {
  150. if (_overflow)
  151. {
  152. return _throwStyle == GuidParseThrowStyle.All ?
  153. (Exception)new OverflowException(SR.GetResourceString(_failureMessageID)) :
  154. new FormatException(SR.Format_GuidUnrecognized);
  155. }
  156. else
  157. {
  158. return new FormatException(SR.GetResourceString(_failureMessageID));
  159. }
  160. }
  161. }
  162. // Creates a new guid based on the value in the string. The value is made up
  163. // of hex digits speared by the dash ("-"). The string may begin and end with
  164. // brackets ("{", "}").
  165. //
  166. // The string must be of the form dddddddd-dddd-dddd-dddd-dddddddddddd. where
  167. // d is a hex digit. (That is 8 hex digits, followed by 4, then 4, then 4,
  168. // then 12) such as: "CA761232-ED42-11CE-BACD-00AA0057B223"
  169. //
  170. public Guid(string g)
  171. {
  172. if (g == null)
  173. {
  174. throw new ArgumentNullException(nameof(g));
  175. }
  176. var result = new GuidResult(GuidParseThrowStyle.All);
  177. if (!TryParseGuid(g, ref result))
  178. {
  179. throw result.CreateGuidParseException();
  180. }
  181. this = result._parsedGuid;
  182. }
  183. public static Guid Parse(string input) =>
  184. Parse(input != null ? (ReadOnlySpan<char>)input : throw new ArgumentNullException(nameof(input)));
  185. public static Guid Parse(ReadOnlySpan<char> input)
  186. {
  187. var result = new GuidResult(GuidParseThrowStyle.AllButOverflow);
  188. if (!TryParseGuid(input, ref result))
  189. {
  190. throw result.CreateGuidParseException();
  191. }
  192. return result._parsedGuid;
  193. }
  194. public static bool TryParse(string input, out Guid result)
  195. {
  196. if (input == null)
  197. {
  198. result = default;
  199. return false;
  200. }
  201. return TryParse((ReadOnlySpan<char>)input, out result);
  202. }
  203. public static bool TryParse(ReadOnlySpan<char> input, out Guid result)
  204. {
  205. var parseResult = new GuidResult(GuidParseThrowStyle.None);
  206. if (TryParseGuid(input, ref parseResult))
  207. {
  208. result = parseResult._parsedGuid;
  209. return true;
  210. }
  211. else
  212. {
  213. result = default;
  214. return false;
  215. }
  216. }
  217. public static Guid ParseExact(string input, string format) =>
  218. ParseExact(
  219. input != null ? (ReadOnlySpan<char>)input : throw new ArgumentNullException(nameof(input)),
  220. format != null ? (ReadOnlySpan<char>)format : throw new ArgumentNullException(nameof(format)));
  221. public static Guid ParseExact(ReadOnlySpan<char> input, ReadOnlySpan<char> format)
  222. {
  223. if (format.Length != 1)
  224. {
  225. // all acceptable format strings are of length 1
  226. throw new FormatException(SR.Format_InvalidGuidFormatSpecification);
  227. }
  228. input = input.Trim();
  229. var result = new GuidResult(GuidParseThrowStyle.AllButOverflow);
  230. bool success;
  231. switch ((char)(format[0] | 0x20))
  232. {
  233. case 'd':
  234. success = TryParseExactD(input, ref result);
  235. break;
  236. case 'n':
  237. success = TryParseExactN(input, ref result);
  238. break;
  239. case 'b':
  240. success = TryParseExactB(input, ref result);
  241. break;
  242. case 'p':
  243. success = TryParseExactP(input, ref result);
  244. break;
  245. case 'x':
  246. success = TryParseExactX(input, ref result);
  247. break;
  248. default:
  249. throw new FormatException(SR.Format_InvalidGuidFormatSpecification);
  250. }
  251. if (!success)
  252. {
  253. throw result.CreateGuidParseException();
  254. }
  255. return result._parsedGuid;
  256. }
  257. public static bool TryParseExact(string input, string format, out Guid result)
  258. {
  259. if (input == null)
  260. {
  261. result = default;
  262. return false;
  263. }
  264. return TryParseExact((ReadOnlySpan<char>)input, format, out result);
  265. }
  266. public static bool TryParseExact(ReadOnlySpan<char> input, ReadOnlySpan<char> format, out Guid result)
  267. {
  268. if (format.Length != 1)
  269. {
  270. result = default;
  271. return false;
  272. }
  273. input = input.Trim();
  274. var parseResult = new GuidResult(GuidParseThrowStyle.None);
  275. bool success = false;
  276. switch ((char)(format[0] | 0x20))
  277. {
  278. case 'd':
  279. success = TryParseExactD(input, ref parseResult);
  280. break;
  281. case 'n':
  282. success = TryParseExactN(input, ref parseResult);
  283. break;
  284. case 'b':
  285. success = TryParseExactB(input, ref parseResult);
  286. break;
  287. case 'p':
  288. success = TryParseExactP(input, ref parseResult);
  289. break;
  290. case 'x':
  291. success = TryParseExactX(input, ref parseResult);
  292. break;
  293. }
  294. if (success)
  295. {
  296. result = parseResult._parsedGuid;
  297. return true;
  298. }
  299. else
  300. {
  301. result = default;
  302. return false;
  303. }
  304. }
  305. private static bool TryParseGuid(ReadOnlySpan<char> guidString, ref GuidResult result)
  306. {
  307. guidString = guidString.Trim(); // Remove whitespace from beginning and end
  308. if (guidString.Length == 0)
  309. {
  310. result.SetFailure(overflow: false, nameof(SR.Format_GuidUnrecognized));
  311. return false;
  312. }
  313. switch (guidString[0])
  314. {
  315. case '(':
  316. return TryParseExactP(guidString, ref result);
  317. case '{':
  318. return guidString.Contains('-') ?
  319. TryParseExactB(guidString, ref result) :
  320. TryParseExactX(guidString, ref result);
  321. default:
  322. return guidString.Contains('-') ?
  323. TryParseExactD(guidString, ref result) :
  324. TryParseExactN(guidString, ref result);
  325. }
  326. }
  327. // Two helpers used for parsing components:
  328. // - uint.TryParse(..., NumberStyles.AllowHexSpecifier, ...)
  329. // Used when we expect the entire provided span to be filled with and only with hex digits and no overflow is possible
  330. // - TryParseHex
  331. // Used when the component may have an optional '+' and "0x" prefix, when it may overflow, etc.
  332. private static bool TryParseExactB(ReadOnlySpan<char> guidString, ref GuidResult result)
  333. {
  334. // e.g. "{d85b1407-351d-4694-9392-03acc5870eb1}"
  335. if ((uint)guidString.Length != 38 || guidString[0] != '{' || guidString[37] != '}')
  336. {
  337. result.SetFailure(overflow: false, nameof(SR.Format_GuidInvLen));
  338. return false;
  339. }
  340. return TryParseExactD(guidString.Slice(1, 36), ref result);
  341. }
  342. private static bool TryParseExactD(ReadOnlySpan<char> guidString, ref GuidResult result)
  343. {
  344. // e.g. "d85b1407-351d-4694-9392-03acc5870eb1"
  345. // Compat notes due to the previous implementation's implementation details.
  346. // - Components may begin with "0x" or "0x+", but the expected length of each component
  347. // needs to include those prefixes, e.g. a four digit component could be "1234" or
  348. // "0x34" or "+0x4" or "+234", but not "0x1234" nor "+1234" nor "+0x1234".
  349. // - "0X" is valid instead of "0x"
  350. if ((uint)guidString.Length != 36)
  351. {
  352. result.SetFailure(overflow: false, nameof(SR.Format_GuidInvLen));
  353. return false;
  354. }
  355. if (guidString[8] != '-' || guidString[13] != '-' || guidString[18] != '-' || guidString[23] != '-')
  356. {
  357. result.SetFailure(overflow: false, nameof(SR.Format_GuidDashes));
  358. return false;
  359. }
  360. ref Guid g = ref result._parsedGuid;
  361. uint uintTmp;
  362. if (TryParseHex(guidString.Slice(0, 8), out Unsafe.As<int, uint>(ref g._a)) && // _a
  363. TryParseHex(guidString.Slice(9, 4), out uintTmp)) // _b
  364. {
  365. g._b = (short)uintTmp;
  366. if (TryParseHex(guidString.Slice(14, 4), out uintTmp)) // _c
  367. {
  368. g._c = (short)uintTmp;
  369. if (TryParseHex(guidString.Slice(19, 4), out uintTmp)) // _d, _e
  370. {
  371. g._d = (byte)(uintTmp >> 8);
  372. g._e = (byte)uintTmp;
  373. if (TryParseHex(guidString.Slice(24, 4), out uintTmp)) // _f, _g
  374. {
  375. g._f = (byte)(uintTmp >> 8);
  376. g._g = (byte)uintTmp;
  377. if (uint.TryParse(guidString.Slice(28, 8), NumberStyles.AllowHexSpecifier, null, out uintTmp)) // _h, _i, _j, _k
  378. {
  379. g._h = (byte)(uintTmp >> 24);
  380. g._i = (byte)(uintTmp >> 16);
  381. g._j = (byte)(uintTmp >> 8);
  382. g._k = (byte)uintTmp;
  383. return true;
  384. }
  385. }
  386. }
  387. }
  388. }
  389. result.SetFailure(overflow: false, nameof(SR.Format_GuidInvalidChar));
  390. return false;
  391. }
  392. private static bool TryParseExactN(ReadOnlySpan<char> guidString, ref GuidResult result)
  393. {
  394. // e.g. "d85b1407351d4694939203acc5870eb1"
  395. if ((uint)guidString.Length != 32)
  396. {
  397. result.SetFailure(overflow: false, nameof(SR.Format_GuidInvLen));
  398. return false;
  399. }
  400. ref Guid g = ref result._parsedGuid;
  401. uint uintTmp;
  402. if (uint.TryParse(guidString.Slice(0, 8), NumberStyles.AllowHexSpecifier, null, out Unsafe.As<int, uint>(ref g._a)) && // _a
  403. uint.TryParse(guidString.Slice(8, 8), NumberStyles.AllowHexSpecifier, null, out uintTmp)) // _b, _c
  404. {
  405. g._b = (short)(uintTmp >> 16);
  406. g._c = (short)uintTmp;
  407. if (uint.TryParse(guidString.Slice(16, 8), NumberStyles.AllowHexSpecifier, null, out uintTmp)) // _d, _e, _f, _g
  408. {
  409. g._d = (byte)(uintTmp >> 24);
  410. g._e = (byte)(uintTmp >> 16);
  411. g._f = (byte)(uintTmp >> 8);
  412. g._g = (byte)uintTmp;
  413. if (uint.TryParse(guidString.Slice(24, 8), NumberStyles.AllowHexSpecifier, null, out uintTmp)) // _h, _i, _j, _k
  414. {
  415. g._h = (byte)(uintTmp >> 24);
  416. g._i = (byte)(uintTmp >> 16);
  417. g._j = (byte)(uintTmp >> 8);
  418. g._k = (byte)uintTmp;
  419. return true;
  420. }
  421. }
  422. }
  423. result.SetFailure(overflow: false, nameof(SR.Format_GuidInvalidChar));
  424. return false;
  425. }
  426. private static bool TryParseExactP(ReadOnlySpan<char> guidString, ref GuidResult result)
  427. {
  428. // e.g. "(d85b1407-351d-4694-9392-03acc5870eb1)"
  429. if ((uint)guidString.Length != 38 || guidString[0] != '(' || guidString[37] != ')')
  430. {
  431. result.SetFailure(overflow: false, nameof(SR.Format_GuidInvLen));
  432. return false;
  433. }
  434. return TryParseExactD(guidString.Slice(1, 36), ref result);
  435. }
  436. private static bool TryParseExactX(ReadOnlySpan<char> guidString, ref GuidResult result)
  437. {
  438. // e.g. "{0xd85b1407,0x351d,0x4694,{0x93,0x92,0x03,0xac,0xc5,0x87,0x0e,0xb1}}"
  439. // Compat notes due to the previous implementation's implementation details.
  440. // - Each component need not be the full expected number of digits.
  441. // - Each component may contain any number of leading 0s
  442. // - The "short" components are parsed as 32-bits and only considered to overflow if they'd overflow 32 bits.
  443. // - The "byte" components are parsed as 32-bits and are considered to overflow if they'd overflow 8 bits,
  444. // but for the Guid ctor, whether they overflow 8 bits or 32 bits results in differing exceptions.
  445. // - Components may begin with "0x", "0x+", even "0x+0x".
  446. // - "0X" is valid instead of "0x"
  447. // Eat all of the whitespace. Unlike the other forms, X allows for any amount of whitespace
  448. // anywhere, not just at the beginning and end.
  449. guidString = EatAllWhitespace(guidString);
  450. // Check for leading '{'
  451. if ((uint)guidString.Length == 0 || guidString[0] != '{')
  452. {
  453. result.SetFailure(overflow: false, nameof(SR.Format_GuidBrace));
  454. return false;
  455. }
  456. // Check for '0x'
  457. if (!IsHexPrefix(guidString, 1))
  458. {
  459. result.SetFailure(overflow: false, nameof(SR.Format_GuidHexPrefix), "{0xdddddddd, etc}");
  460. return false;
  461. }
  462. // Find the end of this hex number (since it is not fixed length)
  463. int numStart = 3;
  464. int numLen = guidString.Slice(numStart).IndexOf(',');
  465. if (numLen <= 0)
  466. {
  467. result.SetFailure(overflow: false, nameof(SR.Format_GuidComma));
  468. return false;
  469. }
  470. bool overflow = false;
  471. if (!TryParseHex(guidString.Slice(numStart, numLen), out Unsafe.As<int, uint>(ref result._parsedGuid._a), ref overflow) || overflow)
  472. {
  473. result.SetFailure(overflow, overflow ? nameof(SR.Overflow_UInt32) : nameof(SR.Format_GuidInvalidChar));
  474. return false;
  475. }
  476. // Check for '0x'
  477. if (!IsHexPrefix(guidString, numStart + numLen + 1))
  478. {
  479. result.SetFailure(overflow: false, nameof(SR.Format_GuidHexPrefix), "{0xdddddddd, 0xdddd, etc}");
  480. return false;
  481. }
  482. // +3 to get by ',0x'
  483. numStart = numStart + numLen + 3;
  484. numLen = guidString.Slice(numStart).IndexOf(',');
  485. if (numLen <= 0)
  486. {
  487. result.SetFailure(overflow: false, nameof(SR.Format_GuidComma));
  488. return false;
  489. }
  490. // Read in the number
  491. if (!TryParseHex(guidString.Slice(numStart, numLen), out result._parsedGuid._b, ref overflow) || overflow)
  492. {
  493. result.SetFailure(overflow, overflow ? nameof(SR.Overflow_UInt32) : nameof(SR.Format_GuidInvalidChar));
  494. return false;
  495. }
  496. // Check for '0x'
  497. if (!IsHexPrefix(guidString, numStart + numLen + 1))
  498. {
  499. result.SetFailure(overflow: false, nameof(SR.Format_GuidHexPrefix), "{0xdddddddd, 0xdddd, 0xdddd, etc}");
  500. return false;
  501. }
  502. // +3 to get by ',0x'
  503. numStart = numStart + numLen + 3;
  504. numLen = guidString.Slice(numStart).IndexOf(',');
  505. if (numLen <= 0)
  506. {
  507. result.SetFailure(overflow: false, nameof(SR.Format_GuidComma));
  508. return false;
  509. }
  510. // Read in the number
  511. if (!TryParseHex(guidString.Slice(numStart, numLen), out result._parsedGuid._c, ref overflow) || overflow)
  512. {
  513. result.SetFailure(overflow, overflow ? nameof(SR.Overflow_UInt32) : nameof(SR.Format_GuidInvalidChar));
  514. return false;
  515. }
  516. // Check for '{'
  517. if ((uint)guidString.Length <= (uint)(numStart + numLen + 1) || guidString[numStart + numLen + 1] != '{')
  518. {
  519. result.SetFailure(overflow: false, nameof(SR.Format_GuidBrace));
  520. return false;
  521. }
  522. // Prepare for loop
  523. numLen++;
  524. for (int i = 0; i < 8; i++)
  525. {
  526. // Check for '0x'
  527. if (!IsHexPrefix(guidString, numStart + numLen + 1))
  528. {
  529. result.SetFailure(overflow: false, nameof(SR.Format_GuidHexPrefix), "{... { ... 0xdd, ...}}");
  530. return false;
  531. }
  532. // +3 to get by ',0x' or '{0x' for first case
  533. numStart = numStart + numLen + 3;
  534. // Calculate number length
  535. if (i < 7) // first 7 cases
  536. {
  537. numLen = guidString.Slice(numStart).IndexOf(',');
  538. if (numLen <= 0)
  539. {
  540. result.SetFailure(overflow: false, nameof(SR.Format_GuidComma));
  541. return false;
  542. }
  543. }
  544. else // last case ends with '}', not ','
  545. {
  546. numLen = guidString.Slice(numStart).IndexOf('}');
  547. if (numLen <= 0)
  548. {
  549. result.SetFailure(overflow: false, nameof(SR.Format_GuidBraceAfterLastNumber));
  550. return false;
  551. }
  552. }
  553. // Read in the number
  554. uint byteVal;
  555. if (!TryParseHex(guidString.Slice(numStart, numLen), out byteVal, ref overflow) || overflow || byteVal > byte.MaxValue)
  556. {
  557. // The previous implementation had some odd inconsistencies, which are carried forward here.
  558. // The byte values in the X format are treated as integers with regards to overflow, so
  559. // a "byte" value like 0xddd in Guid's ctor results in a FormatException but 0xddddddddd results
  560. // in OverflowException.
  561. result.SetFailure(overflow,
  562. overflow ? nameof(SR.Overflow_UInt32) :
  563. byteVal > byte.MaxValue ? nameof(SR.Overflow_Byte) :
  564. nameof(SR.Format_GuidInvalidChar));
  565. return false;
  566. }
  567. Unsafe.Add(ref result._parsedGuid._d, i) = (byte)byteVal;
  568. }
  569. // Check for last '}'
  570. if (numStart + numLen + 1 >= guidString.Length || guidString[numStart + numLen + 1] != '}')
  571. {
  572. result.SetFailure(overflow: false, nameof(SR.Format_GuidEndBrace));
  573. return false;
  574. }
  575. // Check if we have extra characters at the end
  576. if (numStart + numLen + 1 != guidString.Length - 1)
  577. {
  578. result.SetFailure(overflow: false, nameof(SR.Format_ExtraJunkAtEnd));
  579. return false;
  580. }
  581. return true;
  582. }
  583. private static bool TryParseHex(ReadOnlySpan<char> guidString, out short result, ref bool overflow)
  584. {
  585. uint tmp;
  586. bool success = TryParseHex(guidString, out tmp, ref overflow);
  587. result = (short)tmp;
  588. return success;
  589. }
  590. private static bool TryParseHex(ReadOnlySpan<char> guidString, out uint result)
  591. {
  592. bool overflowIgnored = false;
  593. return TryParseHex(guidString, out result, ref overflowIgnored);
  594. }
  595. private static bool TryParseHex(ReadOnlySpan<char> guidString, out uint result, ref bool overflow)
  596. {
  597. if ((uint)guidString.Length > 0)
  598. {
  599. if (guidString[0] == '+')
  600. {
  601. guidString = guidString.Slice(1);
  602. }
  603. if ((uint)guidString.Length > 1 && guidString[0] == '0' && (guidString[1] | 0x20) == 'x')
  604. {
  605. guidString = guidString.Slice(2);
  606. }
  607. }
  608. // Skip past leading 0s.
  609. int i = 0;
  610. for (; i < guidString.Length && guidString[i] == '0'; i++);
  611. int processedDigits = 0;
  612. ReadOnlySpan<byte> charToHexLookup = Number.CharToHexLookup;
  613. uint tmp = 0;
  614. for (; i < guidString.Length; i++)
  615. {
  616. int numValue;
  617. char c = guidString[i];
  618. if (c >= (uint)charToHexLookup.Length || (numValue = charToHexLookup[c]) == 0xFF)
  619. {
  620. if (processedDigits > 8) overflow = true;
  621. result = 0;
  622. return false;
  623. }
  624. tmp = (tmp * 16) + (uint)numValue;
  625. processedDigits++;
  626. }
  627. if (processedDigits > 8) overflow = true;
  628. result = tmp;
  629. return true;
  630. }
  631. private static ReadOnlySpan<char> EatAllWhitespace(ReadOnlySpan<char> str)
  632. {
  633. // Find the first whitespace character. If there is none, just return the input.
  634. int i;
  635. for (i = 0; i < str.Length && !char.IsWhiteSpace(str[i]); i++) ;
  636. if (i == str.Length)
  637. {
  638. return str;
  639. }
  640. // There was at least one whitespace. Copy over everything prior to it to a new array.
  641. var chArr = new char[str.Length];
  642. int newLength = 0;
  643. if (i > 0)
  644. {
  645. newLength = i;
  646. str.Slice(0, i).CopyTo(chArr);
  647. }
  648. // Loop through the remaining chars, copying over non-whitespace.
  649. for (; i < str.Length; i++)
  650. {
  651. char c = str[i];
  652. if (!char.IsWhiteSpace(c))
  653. {
  654. chArr[newLength++] = c;
  655. }
  656. }
  657. // Return the string with the whitespace removed.
  658. return new ReadOnlySpan<char>(chArr, 0, newLength);
  659. }
  660. private static bool IsHexPrefix(ReadOnlySpan<char> str, int i) =>
  661. i + 1 < str.Length &&
  662. str[i] == '0' &&
  663. (str[i + 1] | 0x20) == 'x';
  664. // Returns an unsigned byte array containing the GUID.
  665. public byte[] ToByteArray()
  666. {
  667. var g = new byte[16];
  668. if (BitConverter.IsLittleEndian)
  669. {
  670. MemoryMarshal.TryWrite<Guid>(g, ref this);
  671. }
  672. else
  673. {
  674. TryWriteBytes(g);
  675. }
  676. return g;
  677. }
  678. // Returns whether bytes are sucessfully written to given span.
  679. public bool TryWriteBytes(Span<byte> destination)
  680. {
  681. if (BitConverter.IsLittleEndian)
  682. {
  683. return MemoryMarshal.TryWrite(destination, ref this);
  684. }
  685. // slower path for BigEndian
  686. if (destination.Length < 16)
  687. return false;
  688. destination[15] = _k; // hoist bounds checks
  689. destination[0] = (byte)(_a);
  690. destination[1] = (byte)(_a >> 8);
  691. destination[2] = (byte)(_a >> 16);
  692. destination[3] = (byte)(_a >> 24);
  693. destination[4] = (byte)(_b);
  694. destination[5] = (byte)(_b >> 8);
  695. destination[6] = (byte)(_c);
  696. destination[7] = (byte)(_c >> 8);
  697. destination[8] = _d;
  698. destination[9] = _e;
  699. destination[10] = _f;
  700. destination[11] = _g;
  701. destination[12] = _h;
  702. destination[13] = _i;
  703. destination[14] = _j;
  704. return true;
  705. }
  706. // Returns the guid in "registry" format.
  707. public override string ToString()
  708. {
  709. return ToString("D", null);
  710. }
  711. public override int GetHashCode()
  712. {
  713. // Simply XOR all the bits of the GUID 32 bits at a time.
  714. return _a ^ Unsafe.Add(ref _a, 1) ^ Unsafe.Add(ref _a, 2) ^ Unsafe.Add(ref _a, 3);
  715. }
  716. // Returns true if and only if the guid represented
  717. // by o is the same as this instance.
  718. public override bool Equals(object o)
  719. {
  720. Guid g;
  721. // Check that o is a Guid first
  722. if (o == null || !(o is Guid))
  723. return false;
  724. else g = (Guid)o;
  725. // Now compare each of the elements
  726. return g._a == _a &&
  727. Unsafe.Add(ref g._a, 1) == Unsafe.Add(ref _a, 1) &&
  728. Unsafe.Add(ref g._a, 2) == Unsafe.Add(ref _a, 2) &&
  729. Unsafe.Add(ref g._a, 3) == Unsafe.Add(ref _a, 3);
  730. }
  731. public bool Equals(Guid g)
  732. {
  733. // Now compare each of the elements
  734. return g._a == _a &&
  735. Unsafe.Add(ref g._a, 1) == Unsafe.Add(ref _a, 1) &&
  736. Unsafe.Add(ref g._a, 2) == Unsafe.Add(ref _a, 2) &&
  737. Unsafe.Add(ref g._a, 3) == Unsafe.Add(ref _a, 3);
  738. }
  739. private int GetResult(uint me, uint them)
  740. {
  741. if (me < them)
  742. {
  743. return -1;
  744. }
  745. return 1;
  746. }
  747. public int CompareTo(object value)
  748. {
  749. if (value == null)
  750. {
  751. return 1;
  752. }
  753. if (!(value is Guid))
  754. {
  755. throw new ArgumentException(SR.Arg_MustBeGuid, nameof(value));
  756. }
  757. Guid g = (Guid)value;
  758. if (g._a != _a)
  759. {
  760. return GetResult((uint)_a, (uint)g._a);
  761. }
  762. if (g._b != _b)
  763. {
  764. return GetResult((uint)_b, (uint)g._b);
  765. }
  766. if (g._c != _c)
  767. {
  768. return GetResult((uint)_c, (uint)g._c);
  769. }
  770. if (g._d != _d)
  771. {
  772. return GetResult(_d, g._d);
  773. }
  774. if (g._e != _e)
  775. {
  776. return GetResult(_e, g._e);
  777. }
  778. if (g._f != _f)
  779. {
  780. return GetResult(_f, g._f);
  781. }
  782. if (g._g != _g)
  783. {
  784. return GetResult(_g, g._g);
  785. }
  786. if (g._h != _h)
  787. {
  788. return GetResult(_h, g._h);
  789. }
  790. if (g._i != _i)
  791. {
  792. return GetResult(_i, g._i);
  793. }
  794. if (g._j != _j)
  795. {
  796. return GetResult(_j, g._j);
  797. }
  798. if (g._k != _k)
  799. {
  800. return GetResult(_k, g._k);
  801. }
  802. return 0;
  803. }
  804. public int CompareTo(Guid value)
  805. {
  806. if (value._a != _a)
  807. {
  808. return GetResult((uint)_a, (uint)value._a);
  809. }
  810. if (value._b != _b)
  811. {
  812. return GetResult((uint)_b, (uint)value._b);
  813. }
  814. if (value._c != _c)
  815. {
  816. return GetResult((uint)_c, (uint)value._c);
  817. }
  818. if (value._d != _d)
  819. {
  820. return GetResult(_d, value._d);
  821. }
  822. if (value._e != _e)
  823. {
  824. return GetResult(_e, value._e);
  825. }
  826. if (value._f != _f)
  827. {
  828. return GetResult(_f, value._f);
  829. }
  830. if (value._g != _g)
  831. {
  832. return GetResult(_g, value._g);
  833. }
  834. if (value._h != _h)
  835. {
  836. return GetResult(_h, value._h);
  837. }
  838. if (value._i != _i)
  839. {
  840. return GetResult(_i, value._i);
  841. }
  842. if (value._j != _j)
  843. {
  844. return GetResult(_j, value._j);
  845. }
  846. if (value._k != _k)
  847. {
  848. return GetResult(_k, value._k);
  849. }
  850. return 0;
  851. }
  852. public static bool operator ==(Guid a, Guid b)
  853. {
  854. // Now compare each of the elements
  855. return a._a == b._a &&
  856. Unsafe.Add(ref a._a, 1) == Unsafe.Add(ref b._a, 1) &&
  857. Unsafe.Add(ref a._a, 2) == Unsafe.Add(ref b._a, 2) &&
  858. Unsafe.Add(ref a._a, 3) == Unsafe.Add(ref b._a, 3);
  859. }
  860. public static bool operator !=(Guid a, Guid b)
  861. {
  862. // Now compare each of the elements
  863. return a._a != b._a ||
  864. Unsafe.Add(ref a._a, 1) != Unsafe.Add(ref b._a, 1) ||
  865. Unsafe.Add(ref a._a, 2) != Unsafe.Add(ref b._a, 2) ||
  866. Unsafe.Add(ref a._a, 3) != Unsafe.Add(ref b._a, 3);
  867. }
  868. public string ToString(string format)
  869. {
  870. return ToString(format, null);
  871. }
  872. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  873. private static char HexToChar(int a)
  874. {
  875. a = a & 0xf;
  876. return (char)((a > 9) ? a - 10 + 0x61 : a + 0x30);
  877. }
  878. private static unsafe int HexsToChars(char* guidChars, int a, int b)
  879. {
  880. guidChars[0] = HexToChar(a >> 4);
  881. guidChars[1] = HexToChar(a);
  882. guidChars[2] = HexToChar(b >> 4);
  883. guidChars[3] = HexToChar(b);
  884. return 4;
  885. }
  886. private static unsafe int HexsToCharsHexOutput(char* guidChars, int a, int b)
  887. {
  888. guidChars[0] = '0';
  889. guidChars[1] = 'x';
  890. guidChars[2] = HexToChar(a >> 4);
  891. guidChars[3] = HexToChar(a);
  892. guidChars[4] = ',';
  893. guidChars[5] = '0';
  894. guidChars[6] = 'x';
  895. guidChars[7] = HexToChar(b >> 4);
  896. guidChars[8] = HexToChar(b);
  897. return 9;
  898. }
  899. // IFormattable interface
  900. // We currently ignore provider
  901. public string ToString(string format, IFormatProvider provider)
  902. {
  903. if (format == null || format.Length == 0)
  904. format = "D";
  905. // all acceptable format strings are of length 1
  906. if (format.Length != 1)
  907. throw new FormatException(SR.Format_InvalidGuidFormatSpecification);
  908. int guidSize;
  909. switch (format[0])
  910. {
  911. case 'D':
  912. case 'd':
  913. guidSize = 36;
  914. break;
  915. case 'N':
  916. case 'n':
  917. guidSize = 32;
  918. break;
  919. case 'B':
  920. case 'b':
  921. case 'P':
  922. case 'p':
  923. guidSize = 38;
  924. break;
  925. case 'X':
  926. case 'x':
  927. guidSize = 68;
  928. break;
  929. default:
  930. throw new FormatException(SR.Format_InvalidGuidFormatSpecification);
  931. }
  932. string guidString = string.FastAllocateString(guidSize);
  933. int bytesWritten;
  934. bool result = TryFormat(new Span<char>(ref guidString.GetRawStringData(), guidString.Length), out bytesWritten, format);
  935. Debug.Assert(result && bytesWritten == guidString.Length, "Formatting guid should have succeeded.");
  936. return guidString;
  937. }
  938. // Returns whether the guid is successfully formatted as a span.
  939. public bool TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan<char> format = default)
  940. {
  941. if (format.Length == 0)
  942. format = "D";
  943. // all acceptable format strings are of length 1
  944. if (format.Length != 1)
  945. throw new FormatException(SR.Format_InvalidGuidFormatSpecification);
  946. bool dash = true;
  947. bool hex = false;
  948. int braces = 0;
  949. int guidSize;
  950. switch (format[0])
  951. {
  952. case 'D':
  953. case 'd':
  954. guidSize = 36;
  955. break;
  956. case 'N':
  957. case 'n':
  958. dash = false;
  959. guidSize = 32;
  960. break;
  961. case 'B':
  962. case 'b':
  963. braces = '{' + ('}' << 16);
  964. guidSize = 38;
  965. break;
  966. case 'P':
  967. case 'p':
  968. braces = '(' + (')' << 16);
  969. guidSize = 38;
  970. break;
  971. case 'X':
  972. case 'x':
  973. braces = '{' + ('}' << 16);
  974. dash = false;
  975. hex = true;
  976. guidSize = 68;
  977. break;
  978. default:
  979. throw new FormatException(SR.Format_InvalidGuidFormatSpecification);
  980. }
  981. if (destination.Length < guidSize)
  982. {
  983. charsWritten = 0;
  984. return false;
  985. }
  986. unsafe
  987. {
  988. fixed (char* guidChars = &MemoryMarshal.GetReference(destination))
  989. {
  990. char * p = guidChars;
  991. if (braces != 0)
  992. *p++ = (char)braces;
  993. if (hex)
  994. {
  995. // {0xdddddddd,0xdddd,0xdddd,{0xdd,0xdd,0xdd,0xdd,0xdd,0xdd,0xdd,0xdd}}
  996. *p++ = '0';
  997. *p++ = 'x';
  998. p += HexsToChars(p, _a >> 24, _a >> 16);
  999. p += HexsToChars(p, _a >> 8, _a);
  1000. *p++ = ',';
  1001. *p++ = '0';
  1002. *p++ = 'x';
  1003. p += HexsToChars(p, _b >> 8, _b);
  1004. *p++ = ',';
  1005. *p++ = '0';
  1006. *p++ = 'x';
  1007. p += HexsToChars(p, _c >> 8, _c);
  1008. *p++ = ',';
  1009. *p++ = '{';
  1010. p += HexsToCharsHexOutput(p, _d, _e);
  1011. *p++ = ',';
  1012. p += HexsToCharsHexOutput(p, _f, _g);
  1013. *p++ = ',';
  1014. p += HexsToCharsHexOutput(p, _h, _i);
  1015. *p++ = ',';
  1016. p += HexsToCharsHexOutput(p, _j, _k);
  1017. *p++ = '}';
  1018. }
  1019. else
  1020. {
  1021. // [{|(]dddddddd[-]dddd[-]dddd[-]dddd[-]dddddddddddd[}|)]
  1022. p += HexsToChars(p, _a >> 24, _a >> 16);
  1023. p += HexsToChars(p, _a >> 8, _a);
  1024. if (dash)
  1025. *p++ = '-';
  1026. p += HexsToChars(p, _b >> 8, _b);
  1027. if (dash)
  1028. *p++ = '-';
  1029. p += HexsToChars(p, _c >> 8, _c);
  1030. if (dash)
  1031. *p++ = '-';
  1032. p += HexsToChars(p, _d, _e);
  1033. if (dash)
  1034. *p++ = '-';
  1035. p += HexsToChars(p, _f, _g);
  1036. p += HexsToChars(p, _h, _i);
  1037. p += HexsToChars(p, _j, _k);
  1038. }
  1039. if (braces != 0)
  1040. *p++ = (char)(braces >> 16);
  1041. Debug.Assert(p - guidChars == guidSize);
  1042. }
  1043. }
  1044. charsWritten = guidSize;
  1045. return true;
  1046. }
  1047. bool ISpanFormattable.TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan<char> format, IFormatProvider provider)
  1048. {
  1049. // Like with the IFormattable implementation, provider is ignored.
  1050. return TryFormat(destination, out charsWritten, format);
  1051. }
  1052. }
  1053. }