Merge pull request #495 from nicolas-raoul/fix-for-issue2907-with-no-formatting-changes
[mono.git] / mcs / class / corlib / System / Guid.cs
1 //
2 // System.Guid.cs
3 //
4 // Authors:
5 //      Duco Fijma (duco@lorentz.xs4all.nl)
6 //      Sebastien Pouliot (sebastien@ximian.com)
7 //      Jb Evain (jbevain@novell.com)
8 //      Marek Safar (marek.safar@gmail.com)
9 //
10 // (C) 2002 Duco Fijma
11 // Copyright (C) 2004-2010 Novell, Inc (http://www.novell.com)
12 // Copyright 2012 Xamarin, Inc (http://www.xamarin.com)
13 //
14 // References
15 // 1.   UUIDs and GUIDs (DRAFT), Section 3.4
16 //      http://www.ics.uci.edu/~ejw/authoring/uuid-guid/draft-leach-uuids-guids-01.txt 
17 //
18 // Permission is hereby granted, free of charge, to any person obtaining
19 // a copy of this software and associated documentation files (the
20 // "Software"), to deal in the Software without restriction, including
21 // without limitation the rights to use, copy, modify, merge, publish,
22 // distribute, sublicense, and/or sell copies of the Software, and to
23 // permit persons to whom the Software is furnished to do so, subject to
24 // the following conditions:
25 // 
26 // The above copyright notice and this permission notice shall be
27 // included in all copies or substantial portions of the Software.
28 // 
29 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
30 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
31 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
32 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
33 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
34 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
35 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
36 //
37
38 using System.Runtime.InteropServices;
39 using System.Security.Cryptography;
40 using System.Text;
41
42 namespace System {
43
44         [Serializable]
45         [StructLayout (LayoutKind.Sequential)]
46         [ComVisible (true)]
47         public struct Guid : IFormattable, IComparable, IComparable<Guid>, IEquatable<Guid> {
48 #if MONOTOUCH
49                 static Guid () {
50                         if (MonoTouchAOTHelper.FalseFlag) {
51                                 var comparer = new System.Collections.Generic.GenericComparer <Guid> ();
52                                 var eqcomparer = new System.Collections.Generic.GenericEqualityComparer <Guid> ();
53                         }
54                 }
55 #endif
56                 private int _a; //_timeLow;
57                 private short _b; //_timeMid;
58                 private short _c; //_timeHighAndVersion;
59                 private byte _d; //_clockSeqHiAndReserved;
60                 private byte _e; //_clockSeqLow;
61                 private byte _f; //_node0;
62                 private byte _g; //_node1;
63                 private byte _h; //_node2;
64                 private byte _i; //_node3;
65                 private byte _j; //_node4;
66                 private byte _k; //_node5;
67
68                 enum Format {
69                         N, // 00000000000000000000000000000000
70                         D, // 00000000-0000-0000-0000-000000000000
71                         B, // {00000000-0000-0000-0000-000000000000}
72                         P, // (00000000-0000-0000-0000-000000000000)
73                         X, // {0x00000000,0x0000,0x0000,{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}}
74                 }
75
76                 class GuidParser {
77
78                         private string _src;
79                         private int _length;
80                         private int _cur;
81
82                         public GuidParser (string src)
83                         {
84                                 _src = src;
85                                 Reset ();
86                         }
87
88                         void Reset ()
89                         {
90                                 _cur = 0;
91                                 _length = _src.Length;
92                         }
93
94                         bool Eof {
95                                 get { return _cur >= _length; }
96                         }
97
98                         public static bool HasHyphen (Format format)
99                         {
100                                 switch (format) {
101                                 case Format.D:
102                                 case Format.B:
103                                 case Format.P:
104                                         return true;
105                                 default:
106                                         return false;
107                                 }
108                         }
109
110                         bool TryParseNDBP (Format format, out Guid guid)
111                         {
112                                 ulong a, b, c;
113                                 guid = new Guid ();
114
115                                 if (format == Format.B && !ParseChar ('{'))
116                                         return false;
117
118                                 if (format == Format.P && !ParseChar ('('))
119                                         return false;
120
121                                 if (!ParseHex (8, true, out a))
122                                         return false;
123
124                                 var has_hyphen = HasHyphen (format);
125
126                                 if (has_hyphen && !ParseChar ('-'))
127                                         return false;
128
129                                 if (!ParseHex (4, true, out b))
130                                         return false;
131
132                                 if (has_hyphen && !ParseChar ('-'))
133                                         return false;
134
135                                 if (!ParseHex (4, true, out c))
136                                         return false;
137
138                                 if (has_hyphen && !ParseChar ('-'))
139                                         return false;
140
141                                 var d = new byte [8];
142                                 for (int i = 0; i < d.Length; i++) {
143                                         ulong dd;
144                                         if (!ParseHex (2, true, out dd))
145                                                 return false;
146
147                                         if (i == 1 && has_hyphen && !ParseChar ('-'))
148                                                 return false;
149
150                                         d [i] = (byte) dd;
151                                 }
152
153                                 if (format == Format.B && !ParseChar ('}'))
154                                         return false;
155
156                                 if (format == Format.P && !ParseChar (')'))
157                                         return false;
158
159                                 if (!Eof)
160                                         return false;
161
162                                 guid = new Guid ((int) a, (short) b, (short) c, d);
163                                 return true;
164                         }
165
166                         bool TryParseX (out Guid guid)
167                         {
168                                 ulong a, b, c;
169                                 guid = new Guid ();
170
171                                 if (!(ParseChar ('{')
172                                         && ParseHexPrefix ()
173                                         && ParseHex (8, false, out a)
174                                         && ParseChar (',')
175                                         && ParseHexPrefix ()
176                                         && ParseHex (4, false, out b)
177                                         && ParseChar (',')
178                                         && ParseHexPrefix ()
179                                         && ParseHex (4, false, out c)
180                                         && ParseChar (',')
181                                         && ParseChar ('{'))) {
182
183                                         return false;
184                                 }
185
186                                 var d = new byte [8];
187                                 for (int i = 0; i < d.Length; ++i) {
188                                         ulong dd;
189
190                                         if (!(ParseHexPrefix () && ParseHex (2, false, out dd)))
191                                                 return false;
192
193                                         d [i] = (byte) dd;
194
195                                         if (i != 7 && !ParseChar (','))
196                                                 return false;
197                                 }
198
199                                 if (!(ParseChar ('}') && ParseChar ('}')))
200                                         return false;
201
202                                 if (!Eof)
203                                         return false;
204
205                                 guid = new Guid ((int) a, (short) b, (short) c, d);
206                                 return true;
207                         }
208
209                         bool ParseHexPrefix ()
210                         {
211                                 if (!ParseChar ('0'))
212                                         return false;
213
214                                 return ParseChar ('x');
215                         }
216
217                         bool ParseChar (char c)
218                         {
219                                 if (!Eof && _src [_cur] == c) {
220                                         _cur++;
221                                         return true;
222                                 }
223
224                                 return false;
225                         }
226
227                         bool ParseHex (int length, bool strict, out ulong res)
228                         {
229                                 res = 0;
230
231                                 for (int i = 0; i < length; i++) {
232                                         if (Eof)
233                                                 return !(strict && (i + 1 != length));
234
235                                         char c = Char.ToLowerInvariant (_src[_cur]);
236                                         if (Char.IsDigit (c)) {
237                                                 res = res * 16 + c - '0';
238                                                 _cur++;
239                                         } else if (c >= 'a' && c <= 'f') {
240                                                 res = res * 16 + c - 'a' + 10;
241                                                 _cur++;
242                                         } else {
243                                                 if (!strict)
244                                                         return true;
245
246                                                 return !(strict && (i + 1 != length));
247                                         }
248                                 }
249
250                                 return true;
251                         }
252
253                         public bool Parse (Format format, out Guid guid)
254                         {
255                                 if (format == Format.X)
256                                         return TryParseX (out guid);
257
258                                 return TryParseNDBP (format, out guid);
259                         }
260
261                         public bool Parse (out Guid guid)
262                         {
263                                 if (TryParseNDBP (Format.N, out guid))
264                                         return true;
265
266                                 Reset ();
267                                 if (TryParseNDBP (Format.D, out guid))
268                                         return true;
269
270                                 Reset ();
271                                 if (TryParseNDBP (Format.B, out guid))
272                                         return true;
273
274                                 Reset ();
275                                 if (TryParseNDBP (Format.P, out guid))
276                                         return true;
277
278                                 Reset ();
279                                 return TryParseX (out guid);
280                         }
281                 }
282
283                 private static void CheckNull (object o)
284                 {
285                         if (o == null) {
286                                 throw new ArgumentNullException (Locale.GetText ("Value cannot be null."));
287                         }
288                 }
289
290                 private static void CheckLength (byte[] o, int l)
291                 {
292                         if (o . Length != l) {
293                                 throw new ArgumentException (String.Format (Locale.GetText ("Array should be exactly {0} bytes long."), l));
294                         }
295                 }
296
297                 private static void CheckArray (byte[] o, int l)
298                 {
299                         CheckNull (o);
300                         CheckLength (o, l);
301                 }
302
303                 public Guid (byte[] b)
304                 {
305                         CheckArray (b, 16);
306                         _a = Mono.Security.BitConverterLE.ToInt32 (b, 0);
307                         _b = Mono.Security.BitConverterLE.ToInt16 (b, 4);
308                         _c = Mono.Security.BitConverterLE.ToInt16 (b, 6);
309                         _d = b [8];
310                         _e = b [9];
311                         _f = b [10];
312                         _g = b [11];
313                         _h = b [12];
314                         _i = b [13];
315                         _j = b [14];
316                         _k = b [15];
317                 }
318
319                 public Guid (string g)
320                 {
321                         CheckNull (g);
322                         g = g.Trim();
323                         var parser = new GuidParser (g);
324                         Guid guid;
325                         if (!parser.Parse (out guid))
326                                 throw CreateFormatException (g);
327
328                         this = guid;
329                 }
330
331                 static Exception CreateFormatException (string s)
332                 {
333                         return new FormatException (string.Format ("Invalid Guid format: {0}", s));
334                 }
335
336                 public Guid (int a, short b, short c, byte[] d)
337                 {
338                         CheckArray (d, 8);
339                         _a = (int) a;
340                         _b = (short) b;
341                         _c = (short) c;
342                         _d = d [0];
343                         _e = d [1];
344                         _f = d [2];
345                         _g = d [3];
346                         _h = d [4];
347                         _i = d [5];
348                         _j = d [6];
349                         _k = d [7];
350                 }
351
352                 public Guid (int a, short b, short c, byte d, byte e, byte f, byte g, byte h, byte i, byte j, byte k)
353                 {
354                         _a = a;
355                         _b = b;
356                         _c = c;
357                         _d = d;
358                         _e = e;
359                         _f = f;
360                         _g = g;
361                         _h = h;
362                         _i = i;
363                         _j = j;
364                         _k = k;
365                 }
366
367                 [CLSCompliant (false)]
368                 public Guid (uint a, ushort b, ushort c, byte d, byte e, byte f, byte g, byte h, byte i, byte j, byte k)
369                         : this((int) a, (short) b, (short) c, d, e, f, g, h, i, j, k)
370                 {
371                 }
372
373                 public static readonly Guid Empty = new Guid (0,0,0,0,0,0,0,0,0,0,0);
374
375                 private static int Compare (int x, int y)
376                 {
377                         return ((uint)x < (uint)y) ? -1 : 1;
378                 }
379
380                 public int CompareTo (object value)
381                 {
382                         if (value == null)
383                                 return 1;
384
385                         if (!(value is Guid)) {
386                                 throw new ArgumentException ("value", Locale.GetText (
387                                         "Argument of System.Guid.CompareTo should be a Guid."));
388                         }
389
390                         return CompareTo ((Guid)value);
391                 }
392
393                 public override bool Equals (object o)
394                 {
395                         if (o is Guid)
396                                 return CompareTo ((Guid)o) == 0;
397                         return false;
398                 }
399
400                 public int CompareTo (Guid value)
401                 {
402                         if (_a != value._a) {
403                                 return Compare (_a, value._a);
404                         }
405                         if (_b != value._b) {
406                                 return Compare (_b, value._b);
407                         }
408                         if (_c != value._c) {
409                                 return Compare (_c, value._c);
410                         }
411                         if (_d != value._d) {
412                                 return Compare (_d, value._d);
413                         }
414                         if (_e != value._e) {
415                                 return Compare (_e, value._e);
416                         }
417                         if (_f != value._f) {
418                                 return Compare (_f, value._f);
419                         }
420                         if (_g != value._g) {
421                                 return Compare (_g, value._g);
422                         }
423                         if (_h != value._h) {
424                                 return Compare (_h, value._h);
425                         }
426                         if (_i != value._i) {
427                                 return Compare (_i, value._i);
428                         }
429                         if (_j != value._j) {
430                                 return Compare (_j, value._j);
431                         }
432                         if (_k != value._k) {
433                                 return Compare (_k, value._k);
434                         }
435                         return 0;
436                 }
437
438                 public bool Equals (Guid g)
439                 {
440                         return CompareTo (g) == 0;
441                 }
442
443                 public override int GetHashCode ()
444                 {
445                         int res;
446         
447                         res = (int) _a; 
448                         res = res ^ ((int) _b << 16 | _c);
449                         res = res ^ ((int) _d << 24);
450                         res = res ^ ((int) _e << 16);
451                         res = res ^ ((int) _f << 8);
452                         res = res ^ ((int) _g);
453                         res = res ^ ((int) _h << 24);
454                         res = res ^ ((int) _i << 16);
455                         res = res ^ ((int) _j << 8);
456                         res = res ^ ((int) _k);
457
458                         return res;
459                 }
460
461                 private static char ToHex (int b)
462                 {
463                         return (char)((b<0xA)?('0' + b):('a' + b - 0xA));
464                 }
465
466                 private static object _rngAccess = new object ();
467 #if !FULL_AOT_RUNTIME
468                 private static RandomNumberGenerator _rng;
469                 private static RandomNumberGenerator _fastRng;
470 #else
471                 private static object _fastRng;
472 #endif
473
474                 // generated as per section 3.4 of the specification
475                 public static Guid NewGuid ()
476                 {
477 #if !FULL_AOT_RUNTIME
478                         byte[] b = new byte [16];
479
480                         // thread-safe access to the prng
481                         lock (_rngAccess) {
482                                 if (_rng == null)
483                                         _rng = RandomNumberGenerator.Create ();
484                                 _rng.GetBytes (b);
485                         }
486 #else
487                         byte[] b = FastNewGuidArray ();
488 #endif
489
490
491                         Guid res = new Guid (b);
492                         // Mask in Variant 1-0 in Bit[7..6]
493                         res._d = (byte) ((res._d & 0x3fu) | 0x80u);
494                         // Mask in Version 4 (random based Guid) in Bits[15..13]
495                         res._c = (short) ((res._c & 0x0fffu) | 0x4000u);
496
497                         return res;
498                 }
499
500                 // used in ModuleBuilder so mcs doesn't need to invoke 
501                 // CryptoConfig for simple assemblies.
502                 internal static byte[] FastNewGuidArray ()
503                 {
504                         byte[] guid = new byte [16];
505
506                         // thread-safe access to the prng
507                         lock (_rngAccess) {
508                                 // if known, use preferred RNG
509 #if FULL_AOT_RUNTIME
510                                 if (_fastRng == null)
511                                         _fastRng = new RNGCryptoServiceProvider ();
512                                 (_fastRng as RNGCryptoServiceProvider).GetBytes (guid);
513 #else
514                                 if (_rng != null)
515                                         _fastRng = _rng;
516                                 // else use hardcoded default RNG (bypassing CryptoConfig)
517                                 if (_fastRng == null)
518                                         _fastRng = new RNGCryptoServiceProvider ();
519                                 _fastRng.GetBytes (guid);
520 #endif
521                         }
522
523                         // Mask in Variant 1-0 in Bit[7..6]
524                         guid [8] = (byte) ((guid [8] & 0x3f) | 0x80);
525                         // Mask in Version 4 (random based Guid) in Bits[15..13]
526                         guid [7] = (byte) ((guid [7] & 0x0f) | 0x40);
527
528                         return guid;
529                 }
530
531                 public byte[] ToByteArray ()
532                 {
533                         byte[] res = new byte[16];
534                         byte[] tmp;
535                         int d = 0;
536                         int s;
537
538                         tmp = Mono.Security.BitConverterLE.GetBytes(_a);
539                         for (s=0; s<4; ++s) {
540                                 res[d++] = tmp[s];
541                         }
542
543                         tmp = Mono.Security.BitConverterLE.GetBytes(_b);
544                         for (s=0; s<2; ++s) {
545                                 res[d++] = tmp[s];
546                         }
547
548                         tmp = Mono.Security.BitConverterLE.GetBytes(_c);
549                         for (s=0; s<2; ++s) {
550                                 res[d++] = tmp[s];
551                         }
552
553                         res[8] = _d;
554                         res[9] = _e;
555                         res[10] = _f;
556                         res[11] = _g;
557                         res[12] = _h;
558                         res[13] = _i;
559                         res[14] = _j;
560                         res[15] = _k;
561
562                         return res;
563                 }
564
565                 static void AppendInt (StringBuilder builder, int value) {
566                         builder.Append (ToHex ((value >> 28) & 0xf));
567                         builder.Append (ToHex ((value >> 24) & 0xf));
568                         builder.Append (ToHex ((value >> 20) & 0xf));
569                         builder.Append (ToHex ((value >> 16) & 0xf));
570                         builder.Append (ToHex ((value >> 12) & 0xf));
571                         builder.Append (ToHex ((value >> 8) & 0xf));
572                         builder.Append (ToHex ((value >> 4) & 0xf));
573                         builder.Append (ToHex (value & 0xf));
574                 }
575
576                 static void AppendShort (StringBuilder builder, short value) {
577                         builder.Append (ToHex ((value >> 12) & 0xf));
578                         builder.Append (ToHex ((value >> 8) & 0xf));
579                         builder.Append (ToHex ((value >> 4) & 0xf));
580                         builder.Append (ToHex (value & 0xf));
581                 }
582
583                 static void AppendByte (StringBuilder builder, byte value) {
584                         builder.Append (ToHex ((value >> 4) & 0xf));
585                         builder.Append (ToHex (value & 0xf));
586                 }
587
588                 string ToString (Format format)
589                 {
590                         int length;
591                         switch (format) {
592                         case Format.B:
593                         case Format.P:
594                                 length = 38;
595                                 break;
596                         case Format.D:
597                                 length = 36;
598                                 break;
599                         case Format.N:
600                                 length = 32;
601                                 break;
602                         case Format.X:
603                                 length = 68;
604                                 break;          
605                         default:
606                                 throw new NotImplementedException (format.ToString ());
607                         }
608                         
609                         StringBuilder res = new StringBuilder (length);
610                         bool has_hyphen = GuidParser.HasHyphen (format);
611                         
612                         if (format == Format.P) {
613                                 res.Append ('(');
614                         } else if (format == Format.B) {
615                                 res.Append ('{');
616                         } else if (format == Format.X) {
617                                 res.Append ('{').Append ('0').Append ('x');
618                         }
619                 
620                         AppendInt (res, _a);
621                         if (has_hyphen) {
622                                 res.Append ('-');
623                         } else if (format == Format.X) {
624                                 res.Append (',').Append ('0').Append ('x');
625                         }
626                         
627                         AppendShort (res, _b);
628                         if (has_hyphen) {
629                                 res.Append ('-');
630                         } else if (format == Format.X) {
631                                 res.Append (',').Append ('0').Append ('x');
632                         }
633
634                         AppendShort (res, _c);
635                         if (has_hyphen) {
636                                 res.Append ('-');
637                         }
638         
639                         if (format == Format.X) {
640                                 res.Append (',').Append ('{').Append ('0').Append ('x');
641                                 AppendByte (res, _d);
642                                 res.Append (',').Append ('0').Append ('x');
643                                 AppendByte (res, _e);
644                                 res.Append (',').Append ('0').Append ('x');
645                                 AppendByte (res, _f);
646                                 res.Append (',').Append ('0').Append ('x');
647                                 AppendByte (res, _g);
648                                 res.Append (',').Append ('0').Append ('x');
649                                 AppendByte (res, _h);
650                                 res.Append (',').Append ('0').Append ('x');
651                                 AppendByte (res, _i);
652                                 res.Append (',').Append ('0').Append ('x');
653                                 AppendByte (res, _j);
654                                 res.Append (',').Append ('0').Append ('x');
655                                 AppendByte (res, _k);
656                                 res.Append ('}').Append ('}');;
657                         } else {
658                                 AppendByte (res, _d);
659                                 AppendByte (res, _e);
660         
661                                 if (has_hyphen) {
662                                         res.Append ('-');
663                                 }
664         
665                                 AppendByte (res, _f);
666                                 AppendByte (res, _g);
667                                 AppendByte (res, _h);
668                                 AppendByte (res, _i);
669                                 AppendByte (res, _j);
670                                 AppendByte (res, _k);
671         
672                                 if (format == Format.P) {
673                                         res.Append (')');
674                                 } else if (format == Format.B) {
675                                         res.Append ('}');
676                                 }
677                         }
678                 
679                         return res.ToString ();
680                 }
681         
682                 public override string ToString ()
683                 {
684                         return ToString (Format.D);
685                 }
686         
687                 public string ToString (string format)
688                 {
689                         return ToString (ParseFormat (format));
690                 }
691
692                 // provider value is never used
693                 public string ToString (string format, IFormatProvider provider)
694                 {
695                         return ToString (format);
696                 }
697
698                 public static bool operator == (Guid a, Guid b)
699                 {
700                         return a.Equals(b);
701                 }
702
703                 public static bool operator != (Guid a, Guid b)
704                 {
705                         return !( a.Equals (b) );
706                 }
707
708 #if NET_4_0 || MOONLIGHT || MOBILE
709                 public static Guid Parse (string input)
710                 {
711                         if (input == null)
712                                 throw new ArgumentNullException ("input");
713
714                         Guid guid;
715                         if (!TryParse (input, out guid))
716                                 throw CreateFormatException (input);
717
718                         return guid;
719                 }
720
721                 public static Guid ParseExact (string input, string format)
722                 {
723                         if (input == null)
724                                 throw new ArgumentNullException ("input");
725                         if (format == null)
726                                 throw new ArgumentNullException ("format");
727
728                         Guid guid;
729                         if (!TryParseExact (input, format, out guid))
730                                 throw CreateFormatException (input);
731
732                         return guid;
733                 }
734
735                 public static bool TryParse (string input, out Guid result)
736                 {
737                         if (input == null) {
738                                 result = Empty;
739                                 return false;
740                         }
741
742                         var parser = new GuidParser (input);
743                         return parser.Parse (out result);
744                 }
745
746                 public static bool TryParseExact (string input, string format, out Guid result)
747                 {
748                         if (input == null || format == null) {
749                                 result = Empty;
750                                 return false;
751                         }
752
753                         var parser = new GuidParser (input);
754                         return parser.Parse (ParseFormat (format), out result);
755                 }
756 #endif
757
758                 static Format ParseFormat (string format)
759                 {
760                         if (string.IsNullOrEmpty (format))
761                                 return Format.D;
762                         
763                         switch (format [0]) {
764                         case 'N':
765                         case 'n':
766                                 return Format.N;
767                         case 'D':
768                         case 'd':
769                                 return Format.D;
770                         case 'B':
771                         case 'b':
772                                 return Format.B;
773                         case 'P':
774                         case 'p':
775                                 return Format.P;
776 #if NET_4_0 || MOONLIGHT || MOBILE
777                         case 'X':
778                         case 'x':
779                                 return Format.X;
780 #endif
781                         }
782
783                         throw new FormatException (
784 #if NET_4_0 || MOONLIGHT || MOBILE
785                                 "Format String can be only one of \"D\", \"d\", \"N\", \"n\", \"P\", \"p\", \"B\", \"b\", \"X\" or \"x\""
786 #else
787                                 "Format String can be only one of \"D\", \"d\", \"N\", \"n\", \"P\", \"p\", \"B\" or \"b\""
788 #endif
789                                 );
790                 }
791         }
792 }