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 5 using System.Diagnostics; 6 using System.Runtime.CompilerServices; 7 using System.Runtime.InteropServices; 8 9 using Internal.Runtime.CompilerServices; 10 11 namespace System 12 { 13 // Represents a Globally Unique Identifier. 14 [StructLayout(LayoutKind.Sequential)] 15 [Serializable] 16 [Runtime.Versioning.NonVersionable] // This only applies to field layout 17 [TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] 18 public partial struct Guid : IFormattable, IComparable, IComparable<Guid>, IEquatable<Guid>, ISpanFormattable 19 { 20 public static readonly Guid Empty = new Guid(); 21 22 //////////////////////////////////////////////////////////////////////////////// 23 // Member variables 24 //////////////////////////////////////////////////////////////////////////////// 25 private int _a; // Do not rename (binary serialization) 26 private short _b; // Do not rename (binary serialization) 27 private short _c; // Do not rename (binary serialization) 28 private byte _d; // Do not rename (binary serialization) 29 private byte _e; // Do not rename (binary serialization) 30 private byte _f; // Do not rename (binary serialization) 31 private byte _g; // Do not rename (binary serialization) 32 private byte _h; // Do not rename (binary serialization) 33 private byte _i; // Do not rename (binary serialization) 34 private byte _j; // Do not rename (binary serialization) 35 private byte _k; // Do not rename (binary serialization) 36 37 //////////////////////////////////////////////////////////////////////////////// 38 // Constructors 39 //////////////////////////////////////////////////////////////////////////////// 40 41 // Creates a new guid from an array of bytes. GuidSystem.Guid42 public Guid(byte[] b) : 43 this(new ReadOnlySpan<byte>(b ?? throw new ArgumentNullException(nameof(b)))) 44 { 45 } 46 47 // Creates a new guid from a read-only span. GuidSystem.Guid48 public Guid(ReadOnlySpan<byte> b) 49 { 50 if (b.Length != 16) 51 throw new ArgumentException(SR.Format(SR.Arg_GuidArrayCtor, "16"), nameof(b)); 52 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 _k = b[15]; 64 } 65 66 [CLSCompliant(false)] GuidSystem.Guid67 public Guid(uint a, ushort b, ushort c, byte d, byte e, byte f, byte g, byte h, byte i, byte j, byte k) 68 { 69 _a = (int)a; 70 _b = (short)b; 71 _c = (short)c; 72 _d = d; 73 _e = e; 74 _f = f; 75 _g = g; 76 _h = h; 77 _i = i; 78 _j = j; 79 _k = k; 80 } 81 82 // Creates a new GUID initialized to the value represented by the arguments. 83 // GuidSystem.Guid84 public Guid(int a, short b, short c, byte[] d) 85 { 86 if (d == null) 87 throw new ArgumentNullException(nameof(d)); 88 // Check that array is not too big 89 if (d.Length != 8) 90 throw new ArgumentException(SR.Format(SR.Arg_GuidArrayCtor, "8"), nameof(d)); 91 92 _a = a; 93 _b = b; 94 _c = c; 95 _d = d[0]; 96 _e = d[1]; 97 _f = d[2]; 98 _g = d[3]; 99 _h = d[4]; 100 _i = d[5]; 101 _j = d[6]; 102 _k = d[7]; 103 } 104 105 // Creates a new GUID initialized to the value represented by the 106 // arguments. The bytes are specified like this to avoid endianness issues. 107 // GuidSystem.Guid108 public Guid(int a, short b, short c, byte d, byte e, byte f, byte g, byte h, byte i, byte j, byte k) 109 { 110 _a = a; 111 _b = b; 112 _c = c; 113 _d = d; 114 _e = e; 115 _f = f; 116 _g = g; 117 _h = h; 118 _i = i; 119 _j = j; 120 _k = k; 121 } 122 123 [Flags] 124 private enum GuidStyles 125 { 126 None = 0x00000000, 127 AllowParenthesis = 0x00000001, //Allow the guid to be enclosed in parens 128 AllowBraces = 0x00000002, //Allow the guid to be enclosed in braces 129 AllowDashes = 0x00000004, //Allow the guid to contain dash group separators 130 AllowHexPrefix = 0x00000008, //Allow the guid to contain {0xdd,0xdd} 131 RequireParenthesis = 0x00000010, //Require the guid to be enclosed in parens 132 RequireBraces = 0x00000020, //Require the guid to be enclosed in braces 133 RequireDashes = 0x00000040, //Require the guid to contain dash group separators 134 RequireHexPrefix = 0x00000080, //Require the guid to contain {0xdd,0xdd} 135 136 HexFormat = RequireBraces | RequireHexPrefix, /* X */ 137 NumberFormat = None, /* N */ 138 DigitFormat = RequireDashes, /* D */ 139 BraceFormat = RequireBraces | RequireDashes, /* B */ 140 ParenthesisFormat = RequireParenthesis | RequireDashes, /* P */ 141 142 Any = AllowParenthesis | AllowBraces | AllowDashes | AllowHexPrefix, 143 } 144 private enum GuidParseThrowStyle 145 { 146 None = 0, 147 All = 1, 148 AllButOverflow = 2 149 } 150 private enum ParseFailureKind 151 { 152 None = 0, 153 ArgumentNull = 1, 154 Format = 2, 155 FormatWithParameter = 3, 156 NativeException = 4, 157 FormatWithInnerException = 5 158 } 159 160 // This will store the result of the parsing. And it will eventually be used to construct a Guid instance. 161 private struct GuidResult 162 { 163 internal Guid _parsedGuid; 164 internal GuidParseThrowStyle _throwStyle; 165 166 private ParseFailureKind _failure; 167 private string _failureMessageID; 168 private object _failureMessageFormatArgument; 169 private string _failureArgumentName; 170 private Exception _innerException; 171 InitSystem.Guid.GuidResult172 internal void Init(GuidParseThrowStyle canThrow) 173 { 174 _throwStyle = canThrow; 175 } 176 SetFailureSystem.Guid.GuidResult177 internal void SetFailure(Exception nativeException) 178 { 179 _failure = ParseFailureKind.NativeException; 180 _innerException = nativeException; 181 } 182 SetFailureSystem.Guid.GuidResult183 internal void SetFailure(ParseFailureKind failure, string failureMessageID) 184 { 185 SetFailure(failure, failureMessageID, null, null, null); 186 } 187 SetFailureSystem.Guid.GuidResult188 internal void SetFailure(ParseFailureKind failure, string failureMessageID, object failureMessageFormatArgument) 189 { 190 SetFailure(failure, failureMessageID, failureMessageFormatArgument, null, null); 191 } 192 SetFailureSystem.Guid.GuidResult193 internal void SetFailure(ParseFailureKind failure, string failureMessageID, object failureMessageFormatArgument, 194 string failureArgumentName, Exception innerException) 195 { 196 Debug.Assert(failure != ParseFailureKind.NativeException, "ParseFailureKind.NativeException should not be used with this overload"); 197 _failure = failure; 198 _failureMessageID = failureMessageID; 199 _failureMessageFormatArgument = failureMessageFormatArgument; 200 _failureArgumentName = failureArgumentName; 201 _innerException = innerException; 202 if (_throwStyle != GuidParseThrowStyle.None) 203 { 204 throw GetGuidParseException(); 205 } 206 } 207 GetGuidParseExceptionSystem.Guid.GuidResult208 internal Exception GetGuidParseException() 209 { 210 switch (_failure) 211 { 212 case ParseFailureKind.ArgumentNull: 213 return new ArgumentNullException(_failureArgumentName, SR.GetResourceString(_failureMessageID)); 214 215 case ParseFailureKind.FormatWithInnerException: 216 return new FormatException(SR.GetResourceString(_failureMessageID), _innerException); 217 218 case ParseFailureKind.FormatWithParameter: 219 return new FormatException(SR.Format(SR.GetResourceString(_failureMessageID), _failureMessageFormatArgument)); 220 221 case ParseFailureKind.Format: 222 return new FormatException(SR.GetResourceString(_failureMessageID)); 223 224 case ParseFailureKind.NativeException: 225 return _innerException; 226 227 default: 228 Debug.Fail("Unknown GuidParseFailure: " + _failure); 229 return new FormatException(SR.Format_GuidUnrecognized); 230 } 231 } 232 } 233 234 // Creates a new guid based on the value in the string. The value is made up 235 // of hex digits speared by the dash ("-"). The string may begin and end with 236 // brackets ("{", "}"). 237 // 238 // The string must be of the form dddddddd-dddd-dddd-dddd-dddddddddddd. where 239 // d is a hex digit. (That is 8 hex digits, followed by 4, then 4, then 4, 240 // then 12) such as: "CA761232-ED42-11CE-BACD-00AA0057B223" 241 // GuidSystem.Guid242 public Guid(string g) 243 { 244 if (g == null) 245 { 246 throw new ArgumentNullException(nameof(g)); 247 } 248 249 GuidResult result = new GuidResult(); 250 result.Init(GuidParseThrowStyle.All); 251 if (TryParseGuid(g, GuidStyles.Any, ref result)) 252 { 253 this = result._parsedGuid; 254 } 255 else 256 { 257 throw result.GetGuidParseException(); 258 } 259 } 260 ParseSystem.Guid261 public static Guid Parse(string input) => 262 Parse(input != null ? (ReadOnlySpan<char>)input : throw new ArgumentNullException(nameof(input))); 263 ParseSystem.Guid264 public static Guid Parse(ReadOnlySpan<char> input) 265 { 266 GuidResult result = new GuidResult(); 267 result.Init(GuidParseThrowStyle.AllButOverflow); 268 if (TryParseGuid(input, GuidStyles.Any, ref result)) 269 { 270 return result._parsedGuid; 271 } 272 else 273 { 274 throw result.GetGuidParseException(); 275 } 276 } 277 TryParseSystem.Guid278 public static bool TryParse(string input, out Guid result) 279 { 280 if (input == null) 281 { 282 result = default(Guid); 283 return false; 284 } 285 286 return TryParse((ReadOnlySpan<char>)input, out result); 287 } 288 TryParseSystem.Guid289 public static bool TryParse(ReadOnlySpan<char> input, out Guid result) 290 { 291 GuidResult parseResult = new GuidResult(); 292 parseResult.Init(GuidParseThrowStyle.None); 293 if (TryParseGuid(input, GuidStyles.Any, ref parseResult)) 294 { 295 result = parseResult._parsedGuid; 296 return true; 297 } 298 else 299 { 300 result = default(Guid); 301 return false; 302 } 303 } 304 ParseExactSystem.Guid305 public static Guid ParseExact(string input, string format) => 306 ParseExact( 307 input != null ? (ReadOnlySpan<char>)input : throw new ArgumentNullException(nameof(input)), 308 format != null ? (ReadOnlySpan<char>)format : throw new ArgumentNullException(nameof(format))); 309 ParseExactSystem.Guid310 public static Guid ParseExact(ReadOnlySpan<char> input, ReadOnlySpan<char> format) 311 { 312 if (format.Length != 1) 313 { 314 // all acceptable format strings are of length 1 315 throw new FormatException(SR.Format_InvalidGuidFormatSpecification); 316 } 317 318 GuidStyles style; 319 switch (format[0]) 320 { 321 case 'D': 322 case 'd': 323 style = GuidStyles.DigitFormat; 324 break; 325 case 'N': 326 case 'n': 327 style = GuidStyles.NumberFormat; 328 break; 329 case 'B': 330 case 'b': 331 style = GuidStyles.BraceFormat; 332 break; 333 case 'P': 334 case 'p': 335 style = GuidStyles.ParenthesisFormat; 336 break; 337 case 'X': 338 case 'x': 339 style = GuidStyles.HexFormat; 340 break; 341 default: 342 throw new FormatException(SR.Format_InvalidGuidFormatSpecification); 343 } 344 345 GuidResult result = new GuidResult(); 346 result.Init(GuidParseThrowStyle.AllButOverflow); 347 if (TryParseGuid(input, style, ref result)) 348 { 349 return result._parsedGuid; 350 } 351 else 352 { 353 throw result.GetGuidParseException(); 354 } 355 } 356 TryParseExactSystem.Guid357 public static bool TryParseExact(string input, string format, out Guid result) 358 { 359 if (input == null) 360 { 361 result = default(Guid); 362 return false; 363 } 364 365 return TryParseExact((ReadOnlySpan<char>)input, format, out result); 366 } 367 TryParseExactSystem.Guid368 public static bool TryParseExact(ReadOnlySpan<char> input, ReadOnlySpan<char> format, out Guid result) 369 { 370 if (format.Length != 1) 371 { 372 result = default(Guid); 373 return false; 374 } 375 376 GuidStyles style; 377 switch (format[0]) 378 { 379 case 'D': 380 case 'd': 381 style = GuidStyles.DigitFormat; 382 break; 383 case 'N': 384 case 'n': 385 style = GuidStyles.NumberFormat; 386 break; 387 case 'B': 388 case 'b': 389 style = GuidStyles.BraceFormat; 390 break; 391 case 'P': 392 case 'p': 393 style = GuidStyles.ParenthesisFormat; 394 break; 395 case 'X': 396 case 'x': 397 style = GuidStyles.HexFormat; 398 break; 399 default: 400 // invalid guid format specification 401 result = default(Guid); 402 return false; 403 } 404 405 GuidResult parseResult = new GuidResult(); 406 parseResult.Init(GuidParseThrowStyle.None); 407 if (TryParseGuid(input, style, ref parseResult)) 408 { 409 result = parseResult._parsedGuid; 410 return true; 411 } 412 else 413 { 414 result = default(Guid); 415 return false; 416 } 417 } 418 TryParseGuidSystem.Guid419 private static bool TryParseGuid(ReadOnlySpan<char> guidString, GuidStyles flags, ref GuidResult result) 420 { 421 guidString = guidString.Trim(); // Remove whitespace from beginning and end 422 423 if (guidString.Length == 0) 424 { 425 result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidUnrecognized)); 426 return false; 427 } 428 429 // Check for dashes 430 bool dashesExistInString = guidString.IndexOf('-') >= 0; 431 432 if (dashesExistInString) 433 { 434 if ((flags & (GuidStyles.AllowDashes | GuidStyles.RequireDashes)) == 0) 435 { 436 // dashes are not allowed 437 result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidUnrecognized)); 438 return false; 439 } 440 } 441 else 442 { 443 if ((flags & GuidStyles.RequireDashes) != 0) 444 { 445 // dashes are required 446 result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidUnrecognized)); 447 return false; 448 } 449 } 450 451 // Check for braces 452 bool bracesExistInString = (guidString.IndexOf('{', 0) >= 0); 453 454 if (bracesExistInString) 455 { 456 if ((flags & (GuidStyles.AllowBraces | GuidStyles.RequireBraces)) == 0) 457 { 458 // braces are not allowed 459 result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidUnrecognized)); 460 return false; 461 } 462 } 463 else 464 { 465 if ((flags & GuidStyles.RequireBraces) != 0) 466 { 467 // braces are required 468 result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidUnrecognized)); 469 return false; 470 } 471 } 472 473 // Check for parenthesis 474 bool parenthesisExistInString = (guidString.IndexOf('(', 0) >= 0); 475 476 if (parenthesisExistInString) 477 { 478 if ((flags & (GuidStyles.AllowParenthesis | GuidStyles.RequireParenthesis)) == 0) 479 { 480 // parenthesis are not allowed 481 result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidUnrecognized)); 482 return false; 483 } 484 } 485 else 486 { 487 if ((flags & GuidStyles.RequireParenthesis) != 0) 488 { 489 // parenthesis are required 490 result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidUnrecognized)); 491 return false; 492 } 493 } 494 495 try 496 { 497 // let's get on with the parsing 498 if (dashesExistInString) 499 { 500 // Check if it's of the form [{|(]dddddddd-dddd-dddd-dddd-dddddddddddd[}|)] 501 return TryParseGuidWithDashes(guidString, ref result); 502 } 503 else if (bracesExistInString) 504 { 505 // Check if it's of the form {0xdddddddd,0xdddd,0xdddd,{0xdd,0xdd,0xdd,0xdd,0xdd,0xdd,0xdd,0xdd}} 506 return TryParseGuidWithHexPrefix(guidString, ref result); 507 } 508 else 509 { 510 // Check if it's of the form dddddddddddddddddddddddddddddddd 511 return TryParseGuidWithNoStyle(guidString, ref result); 512 } 513 } 514 catch (IndexOutOfRangeException ex) 515 { 516 result.SetFailure(ParseFailureKind.FormatWithInnerException, nameof(SR.Format_GuidUnrecognized), null, null, ex); 517 return false; 518 } 519 catch (ArgumentException ex) 520 { 521 result.SetFailure(ParseFailureKind.FormatWithInnerException, nameof(SR.Format_GuidUnrecognized), null, null, ex); 522 return false; 523 } 524 } 525 526 // Check if it's of the form {0xdddddddd,0xdddd,0xdddd,{0xdd,0xdd,0xdd,0xdd,0xdd,0xdd,0xdd,0xdd}} TryParseGuidWithHexPrefixSystem.Guid527 private static bool TryParseGuidWithHexPrefix(ReadOnlySpan<char> guidString, ref GuidResult result) 528 { 529 int numStart = 0; 530 int numLen = 0; 531 532 // Eat all of the whitespace 533 guidString = EatAllWhitespace(guidString); 534 535 // Check for leading '{' 536 if (guidString.Length == 0 || guidString[0] != '{') 537 { 538 result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidBrace)); 539 return false; 540 } 541 542 // Check for '0x' 543 if (!IsHexPrefix(guidString, 1)) 544 { 545 result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidHexPrefix), "{0xdddddddd, etc}"); 546 return false; 547 } 548 549 // Find the end of this hex number (since it is not fixed length) 550 numStart = 3; 551 numLen = guidString.IndexOf(',', numStart) - numStart; 552 if (numLen <= 0) 553 { 554 result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidComma)); 555 return false; 556 } 557 558 if (!StringToInt(guidString.Slice(numStart, numLen) /*first DWORD*/, -1, ParseNumbers.IsTight, out result._parsedGuid._a, ref result)) 559 return false; 560 561 // Check for '0x' 562 if (!IsHexPrefix(guidString, numStart + numLen + 1)) 563 { 564 result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidHexPrefix), "{0xdddddddd, 0xdddd, etc}"); 565 return false; 566 } 567 // +3 to get by ',0x' 568 numStart = numStart + numLen + 3; 569 numLen = guidString.IndexOf(',', numStart) - numStart; 570 if (numLen <= 0) 571 { 572 result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidComma)); 573 return false; 574 } 575 576 // Read in the number 577 if (!StringToShort(guidString.Slice(numStart, numLen) /*first DWORD*/, -1, ParseNumbers.IsTight, out result._parsedGuid._b, ref result)) 578 return false; 579 // Check for '0x' 580 if (!IsHexPrefix(guidString, numStart + numLen + 1)) 581 { 582 result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidHexPrefix), "{0xdddddddd, 0xdddd, 0xdddd, etc}"); 583 return false; 584 } 585 // +3 to get by ',0x' 586 numStart = numStart + numLen + 3; 587 numLen = guidString.IndexOf(',', numStart) - numStart; 588 if (numLen <= 0) 589 { 590 result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidComma)); 591 return false; 592 } 593 594 // Read in the number 595 if (!StringToShort(guidString.Slice(numStart, numLen) /*first DWORD*/, -1, ParseNumbers.IsTight, out result._parsedGuid._c, ref result)) 596 return false; 597 598 // Check for '{' 599 if (guidString.Length <= numStart + numLen + 1 || guidString[numStart + numLen + 1] != '{') 600 { 601 result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidBrace)); 602 return false; 603 } 604 605 // Prepare for loop 606 numLen++; 607 Span<byte> bytes = stackalloc byte[8]; 608 609 for (int i = 0; i < bytes.Length; i++) 610 { 611 // Check for '0x' 612 if (!IsHexPrefix(guidString, numStart + numLen + 1)) 613 { 614 result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidHexPrefix), "{... { ... 0xdd, ...}}"); 615 return false; 616 } 617 618 // +3 to get by ',0x' or '{0x' for first case 619 numStart = numStart + numLen + 3; 620 621 // Calculate number length 622 if (i < 7) // first 7 cases 623 { 624 numLen = guidString.IndexOf(',', numStart) - numStart; 625 if (numLen <= 0) 626 { 627 result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidComma)); 628 return false; 629 } 630 } 631 else // last case ends with '}', not ',' 632 { 633 numLen = guidString.IndexOf('}', numStart) - numStart; 634 if (numLen <= 0) 635 { 636 result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidBraceAfterLastNumber)); 637 return false; 638 } 639 } 640 641 // Read in the number 642 int signedNumber; 643 if (!StringToInt(guidString.Slice(numStart, numLen), -1, ParseNumbers.IsTight, out signedNumber, ref result)) 644 { 645 return false; 646 } 647 uint number = (uint)signedNumber; 648 649 // check for overflow 650 if (number > 255) 651 { 652 result.SetFailure(ParseFailureKind.Format, nameof(SR.Overflow_Byte)); 653 return false; 654 } 655 bytes[i] = (byte)number; 656 } 657 658 result._parsedGuid._d = bytes[0]; 659 result._parsedGuid._e = bytes[1]; 660 result._parsedGuid._f = bytes[2]; 661 result._parsedGuid._g = bytes[3]; 662 result._parsedGuid._h = bytes[4]; 663 result._parsedGuid._i = bytes[5]; 664 result._parsedGuid._j = bytes[6]; 665 result._parsedGuid._k = bytes[7]; 666 667 // Check for last '}' 668 if (numStart + numLen + 1 >= guidString.Length || guidString[numStart + numLen + 1] != '}') 669 { 670 result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidEndBrace)); 671 return false; 672 } 673 674 // Check if we have extra characters at the end 675 if (numStart + numLen + 1 != guidString.Length - 1) 676 { 677 result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_ExtraJunkAtEnd)); 678 return false; 679 } 680 681 return true; 682 } 683 684 // Check if it's of the form dddddddddddddddddddddddddddddddd TryParseGuidWithNoStyleSystem.Guid685 private static bool TryParseGuidWithNoStyle(ReadOnlySpan<char> guidString, ref GuidResult result) 686 { 687 int startPos = 0; 688 int temp; 689 long templ; 690 int currentPos = 0; 691 692 if (guidString.Length != 32) 693 { 694 result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidInvLen)); 695 return false; 696 } 697 698 for (int i = 0; i < guidString.Length; i++) 699 { 700 char ch = guidString[i]; 701 if (ch >= '0' && ch <= '9') 702 { 703 continue; 704 } 705 else 706 { 707 char upperCaseCh = char.ToUpperInvariant(ch); 708 if (upperCaseCh >= 'A' && upperCaseCh <= 'F') 709 { 710 continue; 711 } 712 } 713 714 result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidInvalidChar)); 715 return false; 716 } 717 718 if (!StringToInt(guidString.Slice(startPos, 8) /*first DWORD*/, -1, ParseNumbers.IsTight, out result._parsedGuid._a, ref result)) 719 return false; 720 721 startPos += 8; 722 if (!StringToShort(guidString.Slice(startPos, 4), -1, ParseNumbers.IsTight, out result._parsedGuid._b, ref result)) 723 return false; 724 725 startPos += 4; 726 if (!StringToShort(guidString.Slice(startPos, 4), -1, ParseNumbers.IsTight, out result._parsedGuid._c, ref result)) 727 return false; 728 729 startPos += 4; 730 if (!StringToInt(guidString.Slice(startPos, 4), -1, ParseNumbers.IsTight, out temp, ref result)) 731 return false; 732 733 startPos += 4; 734 currentPos = startPos; 735 736 if (!StringToLong(guidString, ref currentPos, ParseNumbers.NoSpace, out templ, ref result)) 737 return false; 738 739 if (currentPos - startPos != 12) 740 { 741 result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidInvLen)); 742 return false; 743 } 744 745 result._parsedGuid._d = (byte)(temp >> 8); 746 result._parsedGuid._e = (byte)(temp); 747 temp = (int)(templ >> 32); 748 result._parsedGuid._f = (byte)(temp >> 8); 749 result._parsedGuid._g = (byte)(temp); 750 temp = (int)(templ); 751 result._parsedGuid._h = (byte)(temp >> 24); 752 result._parsedGuid._i = (byte)(temp >> 16); 753 result._parsedGuid._j = (byte)(temp >> 8); 754 result._parsedGuid._k = (byte)(temp); 755 756 return true; 757 } 758 759 // Check if it's of the form [{|(]dddddddd-dddd-dddd-dddd-dddddddddddd[}|)] TryParseGuidWithDashesSystem.Guid760 private static bool TryParseGuidWithDashes(ReadOnlySpan<char> guidString, ref GuidResult result) 761 { 762 int startPos = 0; 763 int temp; 764 long templ; 765 int currentPos = 0; 766 767 // check to see that it's the proper length 768 if (guidString[0] == '{') 769 { 770 if (guidString.Length != 38 || guidString[37] != '}') 771 { 772 result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidInvLen)); 773 return false; 774 } 775 startPos = 1; 776 } 777 else if (guidString[0] == '(') 778 { 779 if (guidString.Length != 38 || guidString[37] != ')') 780 { 781 result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidInvLen)); 782 return false; 783 } 784 startPos = 1; 785 } 786 else if (guidString.Length != 36) 787 { 788 result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidInvLen)); 789 return false; 790 } 791 792 if (guidString[8 + startPos] != '-' || 793 guidString[13 + startPos] != '-' || 794 guidString[18 + startPos] != '-' || 795 guidString[23 + startPos] != '-') 796 { 797 result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidDashes)); 798 return false; 799 } 800 801 currentPos = startPos; 802 if (!StringToInt(guidString, ref currentPos, 8, ParseNumbers.NoSpace, out temp, ref result)) 803 return false; 804 result._parsedGuid._a = temp; 805 ++currentPos; //Increment past the '-'; 806 807 if (!StringToInt(guidString, ref currentPos, 4, ParseNumbers.NoSpace, out temp, ref result)) 808 return false; 809 result._parsedGuid._b = (short)temp; 810 ++currentPos; //Increment past the '-'; 811 812 if (!StringToInt(guidString, ref currentPos, 4, ParseNumbers.NoSpace, out temp, ref result)) 813 return false; 814 result._parsedGuid._c = (short)temp; 815 ++currentPos; //Increment past the '-'; 816 817 if (!StringToInt(guidString, ref currentPos, 4, ParseNumbers.NoSpace, out temp, ref result)) 818 return false; 819 ++currentPos; //Increment past the '-'; 820 startPos = currentPos; 821 822 if (!StringToLong(guidString, ref currentPos, ParseNumbers.NoSpace, out templ, ref result)) 823 return false; 824 825 if (currentPos - startPos != 12) 826 { 827 result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidInvLen)); 828 return false; 829 } 830 result._parsedGuid._d = (byte)(temp >> 8); 831 result._parsedGuid._e = (byte)(temp); 832 temp = (int)(templ >> 32); 833 result._parsedGuid._f = (byte)(temp >> 8); 834 result._parsedGuid._g = (byte)(temp); 835 temp = (int)(templ); 836 result._parsedGuid._h = (byte)(temp >> 24); 837 result._parsedGuid._i = (byte)(temp >> 16); 838 result._parsedGuid._j = (byte)(temp >> 8); 839 result._parsedGuid._k = (byte)(temp); 840 841 return true; 842 } 843 StringToShortSystem.Guid844 private static bool StringToShort(ReadOnlySpan<char> str, int requiredLength, int flags, out short result, ref GuidResult parseResult) 845 { 846 int parsePos = 0; 847 return StringToShort(str, ref parsePos, requiredLength, flags, out result, ref parseResult); 848 } 849 StringToShortSystem.Guid850 private static bool StringToShort(ReadOnlySpan<char> str, ref int parsePos, int requiredLength, int flags, out short result, ref GuidResult parseResult) 851 { 852 result = 0; 853 int x; 854 bool retValue = StringToInt(str, ref parsePos, requiredLength, flags, out x, ref parseResult); 855 result = (short)x; 856 return retValue; 857 } 858 StringToIntSystem.Guid859 private static bool StringToInt(ReadOnlySpan<char> str, int requiredLength, int flags, out int result, ref GuidResult parseResult) 860 { 861 int parsePos = 0; 862 return StringToInt(str, ref parsePos, requiredLength, flags, out result, ref parseResult); 863 } 864 StringToIntSystem.Guid865 private static bool StringToInt(ReadOnlySpan<char> str, ref int parsePos, int requiredLength, int flags, out int result, ref GuidResult parseResult) 866 { 867 result = 0; 868 869 int currStart = parsePos; 870 try 871 { 872 result = ParseNumbers.StringToInt(str, 16, flags, ref parsePos); 873 } 874 catch (OverflowException ex) 875 { 876 if (parseResult._throwStyle == GuidParseThrowStyle.All) 877 { 878 throw; 879 } 880 else if (parseResult._throwStyle == GuidParseThrowStyle.AllButOverflow) 881 { 882 throw new FormatException(SR.Format_GuidUnrecognized, ex); 883 } 884 else 885 { 886 parseResult.SetFailure(ex); 887 return false; 888 } 889 } 890 catch (Exception ex) 891 { 892 if (parseResult._throwStyle == GuidParseThrowStyle.None) 893 { 894 parseResult.SetFailure(ex); 895 return false; 896 } 897 else 898 { 899 throw; 900 } 901 } 902 903 //If we didn't parse enough characters, there's clearly an error. 904 if (requiredLength != -1 && parsePos - currStart != requiredLength) 905 { 906 parseResult.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidInvalidChar)); 907 return false; 908 } 909 return true; 910 } 911 StringToLongSystem.Guid912 private static unsafe bool StringToLong(ReadOnlySpan<char> str, ref int parsePos, int flags, out long result, ref GuidResult parseResult) 913 { 914 result = 0; 915 916 try 917 { 918 result = ParseNumbers.StringToLong(str, 16, flags, ref parsePos); 919 } 920 catch (OverflowException ex) 921 { 922 if (parseResult._throwStyle == GuidParseThrowStyle.All) 923 { 924 throw; 925 } 926 else if (parseResult._throwStyle == GuidParseThrowStyle.AllButOverflow) 927 { 928 throw new FormatException(SR.Format_GuidUnrecognized, ex); 929 } 930 else 931 { 932 parseResult.SetFailure(ex); 933 return false; 934 } 935 } 936 catch (Exception ex) 937 { 938 if (parseResult._throwStyle == GuidParseThrowStyle.None) 939 { 940 parseResult.SetFailure(ex); 941 return false; 942 } 943 else 944 { 945 throw; 946 } 947 } 948 return true; 949 } 950 EatAllWhitespaceSystem.Guid951 private static ReadOnlySpan<char> EatAllWhitespace(ReadOnlySpan<char> str) 952 { 953 // Find the first whitespace character. If there is none, just return the input. 954 int i; 955 for (i = 0; i < str.Length && !char.IsWhiteSpace(str[i]); i++) ; 956 if (i == str.Length) 957 { 958 return str; 959 } 960 961 // There was at least one whitespace. Copy over everything prior to it to a new array. 962 var chArr = new char[str.Length]; 963 int newLength = 0; 964 if (i > 0) 965 { 966 newLength = i; 967 str.Slice(0, i).CopyTo(chArr); 968 } 969 970 // Loop through the remaining chars, copying over non-whitespace. 971 for (; i < str.Length; i++) 972 { 973 char c = str[i]; 974 if (!char.IsWhiteSpace(c)) 975 { 976 chArr[newLength++] = c; 977 } 978 } 979 980 // Return the string with the whitespace removed. 981 return new ReadOnlySpan<char>(chArr, 0, newLength); 982 } 983 IsHexPrefixSystem.Guid984 private static bool IsHexPrefix(ReadOnlySpan<char> str, int i) => 985 i + 1 < str.Length && 986 str[i] == '0' && 987 (str[i + 1] == 'x' || char.ToLowerInvariant(str[i + 1]) == 'x'); 988 989 [MethodImpl(MethodImplOptions.AggressiveInlining)] 990 private void WriteByteHelper(Span<byte> destination) 991 { 992 destination[0] = (byte)(_a); 993 destination[1] = (byte)(_a >> 8); 994 destination[2] = (byte)(_a >> 16); 995 destination[3] = (byte)(_a >> 24); 996 destination[4] = (byte)(_b); 997 destination[5] = (byte)(_b >> 8); 998 destination[6] = (byte)(_c); 999 destination[7] = (byte)(_c >> 8); 1000 destination[8] = _d; 1001 destination[9] = _e; 1002 destination[10] = _f; 1003 destination[11] = _g; 1004 destination[12] = _h; 1005 destination[13] = _i; 1006 destination[14] = _j; 1007 destination[15] = _k; 1008 } 1009 1010 // Returns an unsigned byte array containing the GUID. ToByteArray()1011 public byte[] ToByteArray() 1012 { 1013 var g = new byte[16]; 1014 WriteByteHelper(g); 1015 return g; 1016 } 1017 1018 // Returns whether bytes are sucessfully written to given span. TryWriteBytes(Span<byte> destination)1019 public bool TryWriteBytes(Span<byte> destination) 1020 { 1021 if (destination.Length < 16) 1022 return false; 1023 1024 WriteByteHelper(destination); 1025 return true; 1026 } 1027 1028 // Returns the guid in "registry" format. ToString()1029 public override string ToString() 1030 { 1031 return ToString("D", null); 1032 } 1033 GetHashCode()1034 public override int GetHashCode() 1035 { 1036 // Simply XOR all the bits of the GUID 32 bits at a time. 1037 return _a ^ Unsafe.Add(ref _a, 1) ^ Unsafe.Add(ref _a, 2) ^ Unsafe.Add(ref _a, 3); 1038 } 1039 1040 // Returns true if and only if the guid represented 1041 // by o is the same as this instance. Equals(object o)1042 public override bool Equals(object o) 1043 { 1044 Guid g; 1045 // Check that o is a Guid first 1046 if (o == null || !(o is Guid)) 1047 return false; 1048 else g = (Guid)o; 1049 1050 // Now compare each of the elements 1051 return g._a == _a && 1052 Unsafe.Add(ref g._a, 1) == Unsafe.Add(ref _a, 1) && 1053 Unsafe.Add(ref g._a, 2) == Unsafe.Add(ref _a, 2) && 1054 Unsafe.Add(ref g._a, 3) == Unsafe.Add(ref _a, 3); 1055 } 1056 Equals(Guid g)1057 public bool Equals(Guid g) 1058 { 1059 // Now compare each of the elements 1060 return g._a == _a && 1061 Unsafe.Add(ref g._a, 1) == Unsafe.Add(ref _a, 1) && 1062 Unsafe.Add(ref g._a, 2) == Unsafe.Add(ref _a, 2) && 1063 Unsafe.Add(ref g._a, 3) == Unsafe.Add(ref _a, 3); 1064 } 1065 GetResult(uint me, uint them)1066 private int GetResult(uint me, uint them) 1067 { 1068 if (me < them) 1069 { 1070 return -1; 1071 } 1072 return 1; 1073 } 1074 CompareTo(object value)1075 public int CompareTo(object value) 1076 { 1077 if (value == null) 1078 { 1079 return 1; 1080 } 1081 if (!(value is Guid)) 1082 { 1083 throw new ArgumentException(SR.Arg_MustBeGuid, nameof(value)); 1084 } 1085 Guid g = (Guid)value; 1086 1087 if (g._a != _a) 1088 { 1089 return GetResult((uint)_a, (uint)g._a); 1090 } 1091 1092 if (g._b != _b) 1093 { 1094 return GetResult((uint)_b, (uint)g._b); 1095 } 1096 1097 if (g._c != _c) 1098 { 1099 return GetResult((uint)_c, (uint)g._c); 1100 } 1101 1102 if (g._d != _d) 1103 { 1104 return GetResult(_d, g._d); 1105 } 1106 1107 if (g._e != _e) 1108 { 1109 return GetResult(_e, g._e); 1110 } 1111 1112 if (g._f != _f) 1113 { 1114 return GetResult(_f, g._f); 1115 } 1116 1117 if (g._g != _g) 1118 { 1119 return GetResult(_g, g._g); 1120 } 1121 1122 if (g._h != _h) 1123 { 1124 return GetResult(_h, g._h); 1125 } 1126 1127 if (g._i != _i) 1128 { 1129 return GetResult(_i, g._i); 1130 } 1131 1132 if (g._j != _j) 1133 { 1134 return GetResult(_j, g._j); 1135 } 1136 1137 if (g._k != _k) 1138 { 1139 return GetResult(_k, g._k); 1140 } 1141 1142 return 0; 1143 } 1144 CompareTo(Guid value)1145 public int CompareTo(Guid value) 1146 { 1147 if (value._a != _a) 1148 { 1149 return GetResult((uint)_a, (uint)value._a); 1150 } 1151 1152 if (value._b != _b) 1153 { 1154 return GetResult((uint)_b, (uint)value._b); 1155 } 1156 1157 if (value._c != _c) 1158 { 1159 return GetResult((uint)_c, (uint)value._c); 1160 } 1161 1162 if (value._d != _d) 1163 { 1164 return GetResult(_d, value._d); 1165 } 1166 1167 if (value._e != _e) 1168 { 1169 return GetResult(_e, value._e); 1170 } 1171 1172 if (value._f != _f) 1173 { 1174 return GetResult(_f, value._f); 1175 } 1176 1177 if (value._g != _g) 1178 { 1179 return GetResult(_g, value._g); 1180 } 1181 1182 if (value._h != _h) 1183 { 1184 return GetResult(_h, value._h); 1185 } 1186 1187 if (value._i != _i) 1188 { 1189 return GetResult(_i, value._i); 1190 } 1191 1192 if (value._j != _j) 1193 { 1194 return GetResult(_j, value._j); 1195 } 1196 1197 if (value._k != _k) 1198 { 1199 return GetResult(_k, value._k); 1200 } 1201 1202 return 0; 1203 } 1204 operator ==(Guid a, Guid b)1205 public static bool operator ==(Guid a, Guid b) 1206 { 1207 // Now compare each of the elements 1208 return a._a == b._a && 1209 Unsafe.Add(ref a._a, 1) == Unsafe.Add(ref b._a, 1) && 1210 Unsafe.Add(ref a._a, 2) == Unsafe.Add(ref b._a, 2) && 1211 Unsafe.Add(ref a._a, 3) == Unsafe.Add(ref b._a, 3); 1212 } 1213 operator !=(Guid a, Guid b)1214 public static bool operator !=(Guid a, Guid b) 1215 { 1216 // Now compare each of the elements 1217 return a._a != b._a || 1218 Unsafe.Add(ref a._a, 1) != Unsafe.Add(ref b._a, 1) || 1219 Unsafe.Add(ref a._a, 2) != Unsafe.Add(ref b._a, 2) || 1220 Unsafe.Add(ref a._a, 3) != Unsafe.Add(ref b._a, 3); 1221 } 1222 ToString(string format)1223 public string ToString(string format) 1224 { 1225 return ToString(format, null); 1226 } 1227 1228 [MethodImpl(MethodImplOptions.AggressiveInlining)] HexToChar(int a)1229 private static char HexToChar(int a) 1230 { 1231 a = a & 0xf; 1232 return (char)((a > 9) ? a - 10 + 0x61 : a + 0x30); 1233 } 1234 HexsToChars(char* guidChars, int a, int b)1235 unsafe private static int HexsToChars(char* guidChars, int a, int b) 1236 { 1237 guidChars[0] = HexToChar(a >> 4); 1238 guidChars[1] = HexToChar(a); 1239 1240 guidChars[2] = HexToChar(b >> 4); 1241 guidChars[3] = HexToChar(b); 1242 1243 return 4; 1244 } 1245 HexsToCharsHexOutput(char* guidChars, int a, int b)1246 unsafe private static int HexsToCharsHexOutput(char* guidChars, int a, int b) 1247 { 1248 guidChars[0] = '0'; 1249 guidChars[1] = 'x'; 1250 1251 guidChars[2] = HexToChar(a >> 4); 1252 guidChars[3] = HexToChar(a); 1253 1254 guidChars[4] = ','; 1255 guidChars[5] = '0'; 1256 guidChars[6] = 'x'; 1257 1258 guidChars[7] = HexToChar(b >> 4); 1259 guidChars[8] = HexToChar(b); 1260 1261 return 9; 1262 } 1263 1264 // IFormattable interface 1265 // We currently ignore provider ToString(string format, IFormatProvider provider)1266 public string ToString(string format, IFormatProvider provider) 1267 { 1268 if (format == null || format.Length == 0) 1269 format = "D"; 1270 1271 // all acceptable format strings are of length 1 1272 if (format.Length != 1) 1273 throw new FormatException(SR.Format_InvalidGuidFormatSpecification); 1274 1275 int guidSize; 1276 switch (format[0]) 1277 { 1278 case 'D': 1279 case 'd': 1280 guidSize = 36; 1281 break; 1282 case 'N': 1283 case 'n': 1284 guidSize = 32; 1285 break; 1286 case 'B': 1287 case 'b': 1288 case 'P': 1289 case 'p': 1290 guidSize = 38; 1291 break; 1292 case 'X': 1293 case 'x': 1294 guidSize = 68; 1295 break; 1296 default: 1297 throw new FormatException(SR.Format_InvalidGuidFormatSpecification); 1298 } 1299 1300 string guidString = string.FastAllocateString(guidSize); 1301 1302 int bytesWritten; 1303 bool result = TryFormat(new Span<char>(ref guidString.GetRawStringData(), guidString.Length), out bytesWritten, format); 1304 Debug.Assert(result && bytesWritten == guidString.Length, "Formatting guid should have succeeded."); 1305 1306 return guidString; 1307 } 1308 1309 // Returns whether the guid is successfully formatted as a span. TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan<char> format = default)1310 public bool TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan<char> format = default) 1311 { 1312 if (format.Length == 0) 1313 format = "D"; 1314 1315 // all acceptable format strings are of length 1 1316 if (format.Length != 1) 1317 throw new FormatException(SR.Format_InvalidGuidFormatSpecification); 1318 1319 bool dash = true; 1320 bool hex = false; 1321 int braces = 0; 1322 1323 int guidSize; 1324 1325 switch (format[0]) 1326 { 1327 case 'D': 1328 case 'd': 1329 guidSize = 36; 1330 break; 1331 case 'N': 1332 case 'n': 1333 dash = false; 1334 guidSize = 32; 1335 break; 1336 case 'B': 1337 case 'b': 1338 braces = '{' + ('}' << 16); 1339 guidSize = 38; 1340 break; 1341 case 'P': 1342 case 'p': 1343 braces = '(' + (')' << 16); 1344 guidSize = 38; 1345 break; 1346 case 'X': 1347 case 'x': 1348 braces = '{' + ('}' << 16); 1349 dash = false; 1350 hex = true; 1351 guidSize = 68; 1352 break; 1353 default: 1354 throw new FormatException(SR.Format_InvalidGuidFormatSpecification); 1355 } 1356 1357 if (destination.Length < guidSize) 1358 { 1359 charsWritten = 0; 1360 return false; 1361 } 1362 1363 unsafe 1364 { 1365 fixed (char* guidChars = &MemoryMarshal.GetReference(destination)) 1366 { 1367 char * p = guidChars; 1368 1369 if (braces != 0) 1370 *p++ = (char)braces; 1371 1372 if (hex) 1373 { 1374 // {0xdddddddd,0xdddd,0xdddd,{0xdd,0xdd,0xdd,0xdd,0xdd,0xdd,0xdd,0xdd}} 1375 *p++ = '0'; 1376 *p++ = 'x'; 1377 p += HexsToChars(p, _a >> 24, _a >> 16); 1378 p += HexsToChars(p, _a >> 8, _a); 1379 *p++ = ','; 1380 *p++ = '0'; 1381 *p++ = 'x'; 1382 p += HexsToChars(p, _b >> 8, _b); 1383 *p++ = ','; 1384 *p++ = '0'; 1385 *p++ = 'x'; 1386 p += HexsToChars(p, _c >> 8, _c); 1387 *p++ = ','; 1388 *p++ = '{'; 1389 p += HexsToCharsHexOutput(p, _d, _e); 1390 *p++ = ','; 1391 p += HexsToCharsHexOutput(p, _f, _g); 1392 *p++ = ','; 1393 p += HexsToCharsHexOutput(p, _h, _i); 1394 *p++ = ','; 1395 p += HexsToCharsHexOutput(p, _j, _k); 1396 *p++ = '}'; 1397 } 1398 else 1399 { 1400 // [{|(]dddddddd[-]dddd[-]dddd[-]dddd[-]dddddddddddd[}|)] 1401 p += HexsToChars(p, _a >> 24, _a >> 16); 1402 p += HexsToChars(p, _a >> 8, _a); 1403 if (dash) 1404 *p++ = '-'; 1405 p += HexsToChars(p, _b >> 8, _b); 1406 if (dash) 1407 *p++ = '-'; 1408 p += HexsToChars(p, _c >> 8, _c); 1409 if (dash) 1410 *p++ = '-'; 1411 p += HexsToChars(p, _d, _e); 1412 if (dash) 1413 *p++ = '-'; 1414 p += HexsToChars(p, _f, _g); 1415 p += HexsToChars(p, _h, _i); 1416 p += HexsToChars(p, _j, _k); 1417 } 1418 1419 if (braces != 0) 1420 *p++ = (char)(braces >> 16); 1421 1422 Debug.Assert(p - guidChars == guidSize); 1423 } 1424 } 1425 1426 charsWritten = guidSize; 1427 return true; 1428 } 1429 ISpanFormattable.TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan<char> format, IFormatProvider provider)1430 bool ISpanFormattable.TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan<char> format, IFormatProvider provider) 1431 { 1432 // Like with the IFormattable implementation, provider is ignored. 1433 return TryFormat(destination, out charsWritten, format); 1434 } 1435 } 1436 } 1437