Skip to content
This repository was archived by the owner on Dec 24, 2022. It is now read-only.

Commit b20eaad

Browse files
committed
Add ParseGuid for StringSegment
1 parent 110a9c8 commit b20eaad

5 files changed

Lines changed: 190 additions & 8 deletions

File tree

benchmarks/ServiceStack.Text.Benchmarks/ParseBuiltinsBenchmarks.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,15 @@ public class ParseBuiltinBenchmarks
1717
const string decimal_2 = "-1234.5678";
1818
const string decimal_3 = "1234.5678901234567890";
1919
const string decimal_4 = "-1234.5678901234567890";
20+
const string guid_1 = "{b6170a18-3dd7-4a9b-b5d6-21033b5ad162}";
2021

2122
readonly StringSegment ss_int32_1 = new StringSegment(int32_1);
2223
readonly StringSegment ss_int32_2 = new StringSegment(int32_2);
2324
readonly StringSegment ss_decimal_1 = new StringSegment(decimal_1);
2425
readonly StringSegment ss_decimal_2 = new StringSegment(decimal_2);
2526
readonly StringSegment ss_decimal_3 = new StringSegment(decimal_3);
2627
readonly StringSegment ss_decimal_4 = new StringSegment(decimal_4);
28+
readonly StringSegment ss_guid_1 = new StringSegment(guid_1);
2729

2830
[Benchmark]
2931
public void Int32Parse()
@@ -68,5 +70,16 @@ public void StringSegment_BigDecimalParse()
6870
var res2 = ss_decimal_4.ParseDecimal(true);
6971
}
7072

73+
[Benchmark]
74+
public void GuidParse()
75+
{
76+
var res1 = Guid.Parse(guid_1);
77+
}
78+
79+
[Benchmark]
80+
public void StringSegment_GuidParse()
81+
{
82+
var res1 = ss_guid_1.ParseGuid();
83+
}
7184
}
7285
}

src/ServiceStack.Text/Common/DeserializeBuiltin.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ private static ParseStringSegmentDelegate GetParseStringSegmentFn()
8383
}
8484

8585
if (typeof(T) == typeof(Guid))
86-
return value => new Guid(value.Value);
86+
return value => value.ParseGuid();
8787
if (typeof(T) == typeof(DateTimeOffset))
8888
return value => DateTimeSerializer.ParseDateTimeOffset(value.Value);
8989
if (typeof(T) == typeof(TimeSpan))
@@ -138,7 +138,7 @@ private static ParseStringSegmentDelegate GetParseStringSegmentFn()
138138
if (typeof(T) == typeof(TimeSpan?))
139139
return value => DateTimeSerializer.ParseNullableTimeSpan(value.Value);
140140
if (typeof(T) == typeof(Guid?))
141-
return value => value.IsNullOrEmpty() ? (Guid?)null : new Guid(value.Value);
141+
return value => value.IsNullOrEmpty() ? (Guid?)null : value.ParseGuid();
142142
if (typeof(T) == typeof(DateTimeOffset?))
143143
return value => DateTimeSerializer.ParseNullableDateTimeOffset(value.Value);
144144
}

src/ServiceStack.Text/Json/JsonTypeSerializer.cs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -705,14 +705,16 @@ public string EatValue(string value, ref int i)
705705

706706
public StringSegment EatValue(StringSegment value, ref int i)
707707
{
708+
var buf = value.Buffer;
708709
var valueLength = value.Length;
710+
var offset = value.Offset;
709711
if (i == valueLength) return default(StringSegment);
710712

711-
for (; i < value.Length; i++) { var c = value.GetChar(i); if (c >= WhiteSpaceFlags.Length || !WhiteSpaceFlags[c]) break; } //Whitespace inline
713+
for (; i < valueLength; i++) { var c = buf[offset + i]; if (c >= WhiteSpaceFlags.Length || !WhiteSpaceFlags[c]) break; } //Whitespace inline
712714
if (i == valueLength) return default(StringSegment);
713715

714716
var tokenStartPos = i;
715-
var valueChar = value.GetChar(i);
717+
var valueChar = buf[offset + i];
716718
var withinQuotes = false;
717719
var endsToEat = 1;
718720

@@ -731,7 +733,7 @@ public StringSegment EatValue(StringSegment value, ref int i)
731733
case JsWriter.MapStartChar:
732734
while (++i < valueLength && endsToEat > 0)
733735
{
734-
valueChar = value.GetChar(i);
736+
valueChar = buf[offset +i];
735737

736738
if (valueChar == JsonUtils.EscapeChar)
737739
{
@@ -757,7 +759,7 @@ public StringSegment EatValue(StringSegment value, ref int i)
757759
case JsWriter.ListStartChar:
758760
while (++i < valueLength && endsToEat > 0)
759761
{
760-
valueChar = value.GetChar(i);
762+
valueChar = buf[offset + i];
761763

762764
if (valueChar == JsonUtils.EscapeChar)
763765
{
@@ -783,7 +785,7 @@ public StringSegment EatValue(StringSegment value, ref int i)
783785
//Is Value
784786
while (++i < valueLength)
785787
{
786-
valueChar = value.GetChar(i);
788+
valueChar = buf[offset + i];
787789

788790
if (valueChar == JsWriter.ItemSeperator
789791
|| valueChar == JsWriter.MapEndChar

src/ServiceStack.Text/Support/StringSegmentExtensions.cs

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -501,6 +501,165 @@ public static decimal ParseDecimal(this StringSegment value, bool allowThousands
501501

502502
return result;
503503
}
504+
private static readonly byte[] lo16 = {
505+
255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
506+
255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
507+
255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
508+
255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
509+
255, 255, 255, 255, 255, 255, 255, 255, 0, 1,
510+
2, 3, 4, 5, 6, 7, 8, 9, 255, 255,
511+
255, 255, 255, 255, 255, 10, 11, 12, 13, 14,
512+
15, 255, 255, 255, 255, 255, 255, 255, 255, 255,
513+
255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
514+
255, 255, 255, 255, 255, 255, 255, 10, 11, 12,
515+
13, 14, 15
516+
};
517+
518+
private static readonly byte[] hi16 = {
519+
255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
520+
255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
521+
255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
522+
255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
523+
255, 255, 255, 255, 255, 255, 255, 255, 0, 16,
524+
32, 48, 64, 80, 96, 112, 128, 144, 255, 255,
525+
255, 255, 255, 255, 255, 160, 176, 192, 208, 224,
526+
240, 255, 255, 255, 255, 255, 255, 255, 255, 255,
527+
255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
528+
255, 255, 255, 255, 255, 255, 255, 160, 176, 192,
529+
208, 224, 240
530+
};
531+
532+
public static Guid ParseGuid(this StringSegment value)
533+
{
534+
//Guid can be in one of 3 forms:
535+
//1. General `{dddddddd-dddd-dddd-dddd-dddddddddddd}` or `(dddddddd-dddd-dddd-dddd-dddddddddddd)` 8-4-4-4-12 chars
536+
//2. Hex `{0xdddddddd,0xdddd,0xdddd,{0xdd,0xdd,0xdd,0xdd,0xdd,0xdd,0xdd,0xdd}}` 8-4-4-8x2 chars
537+
//3. No style `dddddddddddddddddddddddddddddddd` 32 chars
538+
539+
int i = value.Offset;
540+
int end = value.Offset + value.Length;
541+
while (Char.IsWhiteSpace(value.Buffer[i]) && i < end) i++;
542+
543+
if (i == end)
544+
throw new FormatException(BadFormat);
545+
546+
Guid result;
547+
548+
int guidLen;
549+
result = ParseGeneralStyleGuid(new StringSegment(value.Buffer, i, end - i), out guidLen);
550+
i += guidLen;
551+
552+
while (i < end && Char.IsWhiteSpace(value.Buffer[i])) i++;
553+
554+
if (i < end)
555+
throw new FormatException(BadFormat);
556+
557+
return result;
558+
}
559+
560+
private static Guid ParseGeneralStyleGuid(StringSegment value, out int len)
561+
{
562+
var buf = value.Buffer;
563+
var n = value.Offset;
564+
565+
int dash = 0;
566+
len = 32;
567+
bool hasParentesis = false;
568+
569+
if (value.Length < len)
570+
throw new FormatException(BadFormat);
571+
572+
var cs = value.GetChar(0);
573+
if (cs == '{' || cs == '(')
574+
{
575+
n++;
576+
len += 2;
577+
hasParentesis = true;
578+
}
504579

580+
if (buf[8 + n] == '-')
581+
{
582+
if (buf[13 + n] != '-'
583+
|| buf[18 + n] != '-'
584+
|| buf[23 + n] != '-')
585+
throw new FormatException(BadFormat);
586+
len += 4;
587+
dash = 1;
588+
}
589+
590+
if (value.Length < len)
591+
throw new FormatException(BadFormat);
592+
593+
if (hasParentesis)
594+
{
595+
var ce = buf[value.Offset + len - 1];
596+
597+
if ((cs != '{' || ce != '}') && (cs != '(' || ce != ')'))
598+
throw new FormatException(BadFormat);
599+
}
600+
601+
int a;
602+
short b, c;
603+
byte d, e, f, g, h, i, j, k;
604+
605+
byte a1 = ParseHexByte(buf[n], buf[n + 1]);
606+
n += 2;
607+
byte a2 = ParseHexByte(buf[n], buf[n + 1]);
608+
n += 2;
609+
byte a3 = ParseHexByte(buf[n], buf[n + 1]);
610+
n += 2;
611+
byte a4 = ParseHexByte(buf[n], buf[n + 1]);
612+
a = (a1 << 24) + (a2 << 16) + (a3 << 8) + a4;
613+
n += 2 + dash;
614+
615+
byte b1 = ParseHexByte(buf[n], buf[n + 1]);
616+
n += 2;
617+
byte b2 = ParseHexByte(buf[n], buf[n + 1]);
618+
b = (short)((b1 << 8) + b2);
619+
n += 2 + dash;
620+
621+
byte c1 = ParseHexByte(buf[n], buf[n + 1]);
622+
n += 2;
623+
byte c2 = ParseHexByte(buf[n], buf[n + 1]);
624+
c = (short)((c1 << 8) + c2);
625+
n += 2 + dash;
626+
627+
d = ParseHexByte(buf[n], buf[n + 1]);
628+
n += 2;
629+
e = ParseHexByte(buf[n], buf[n + 1]);
630+
n += 2 + dash;
631+
632+
f = ParseHexByte(buf[n], buf[n + 1]);
633+
n += 2;
634+
g = ParseHexByte(buf[n], buf[n + 1]);
635+
n += 2;
636+
h = ParseHexByte(buf[n], buf[n + 1]);
637+
n += 2;
638+
i = ParseHexByte(buf[n], buf[n + 1]);
639+
n += 2;
640+
j = ParseHexByte(buf[n], buf[n + 1]);
641+
n += 2;
642+
k = ParseHexByte(buf[n], buf[n + 1]);
643+
644+
return new Guid(a, b, c, d, e, f, g, h, i, j, k);
645+
}
646+
647+
private static byte ParseHexByte(char c1, char c2)
648+
{
649+
try
650+
{
651+
byte lo = lo16[c2];
652+
byte hi = hi16[c1];
653+
654+
if (lo == 255 || hi == 255)
655+
throw new FormatException(BadFormat);
656+
657+
return (byte)(hi + lo);
658+
}
659+
catch (IndexOutOfRangeException)
660+
{
661+
throw new FormatException(BadFormat);
662+
}
663+
}
505664
}
506665
}

tests/ServiceStack.Text.Tests/Support/StringSegmentParse.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,5 +70,13 @@ public void Can_parse_decimal()
7070
//Assert.Throws<FormatException>(() => new StringSegment(".e2").ParseDecimal());
7171

7272
}
73+
74+
[Test]
75+
public void Can_parse_guid()
76+
{
77+
Assert.That(new StringSegment("{b6170a18-3dd7-4a9b-b5d6-21033b5ad162}").ParseGuid(), Is.EqualTo(new Guid("{b6170a18-3dd7-4a9b-b5d6-21033b5ad162}")));
78+
Assert.That(new StringSegment("b6170a18-3dd7-4a9b-b5d6-21033b5ad162").ParseGuid(), Is.EqualTo(new Guid("{b6170a18-3dd7-4a9b-b5d6-21033b5ad162}")));
79+
Assert.That(new StringSegment("b6170a183dd74a9bb5d621033b5ad162").ParseGuid(), Is.EqualTo(new Guid("{b6170a18-3dd7-4a9b-b5d6-21033b5ad162}")));
80+
}
7381
}
74-
}
82+
}

0 commit comments

Comments
 (0)