Fauna v10 .NET/C# Driver 1.0.1
 
Loading...
Searching...
No Matches
Utf8FaunaReader.cs
Go to the documentation of this file.
1using System.Buffers;
2using System.Globalization;
3using System.Text;
4using System.Text.Json;
6using Fauna.Types;
7
8namespace Fauna.Serialization;
9
13public ref struct Utf8FaunaReader
14{
15 private Utf8JsonReader _json;
16 private readonly Stack<object> _tokenStack = new();
17 private TokenType? _bufferedTokenType = null;
18 private object? _bufferedTokenValue = null;
19
20 private readonly HashSet<TokenType> _closers = new()
21 {
22 TokenType.EndObject,
23 TokenType.EndPage,
24 TokenType.EndDocument,
25 TokenType.EndRef,
26 TokenType.EndArray
27 };
28
29 private string? _taggedTokenValue = null;
30
34 public TokenType CurrentTokenType { get; private set; }
35
36 private enum TokenTypeInternal
37 {
39 StartEscapedObject,
40 StartPageUnmaterialized,
41 }
42
47 public Utf8FaunaReader(ReadOnlySequence<byte> bytes)
48 {
49 _json = new Utf8JsonReader(bytes);
51 }
52
57 public Utf8FaunaReader(string str)
58 {
59 var bytes = Encoding.UTF8.GetBytes(str);
60 var seq = new ReadOnlySequence<byte>(bytes);
61 _json = new Utf8JsonReader(seq);
63 }
64
68 public void Skip()
69 {
70 switch (CurrentTokenType)
71 {
72 case TokenType.StartObject:
73 case TokenType.StartArray:
74 case TokenType.StartPage:
75 case TokenType.StartRef:
76 case TokenType.StartDocument:
77 SkipInternal();
78 break;
79 }
80 }
81
82 private void SkipInternal()
83 {
84 var startCount = _tokenStack.Count;
85 while (Read())
86 {
87 if (_tokenStack.Count < startCount) break;
88 }
89 }
90
95 public bool Read()
96 {
97 _taggedTokenValue = null;
98
99 if (_bufferedTokenType != null)
100 {
101 CurrentTokenType = (TokenType)_bufferedTokenType;
102 _bufferedTokenType = null;
103 if (_closers.Contains(CurrentTokenType))
104 {
105 _tokenStack.Pop();
106 }
107 return true;
108 }
109
110 // At this point we're passed a buffered token type so this read should unset this no matter what
111 _bufferedTokenValue = null;
112
113 if (!Advance())
114 {
115 return false;
116 }
117
118 switch (_json.TokenType)
119 {
120 case JsonTokenType.PropertyName:
121 CurrentTokenType = TokenType.FieldName;
122 break;
123 case JsonTokenType.None:
124 break;
125 case JsonTokenType.StartObject:
126 HandleStartObject();
127 break;
128 case JsonTokenType.EndObject:
129 HandleEndObject();
130 break;
131 case JsonTokenType.StartArray:
132 _tokenStack.Push(TokenType.StartArray);
133 CurrentTokenType = TokenType.StartArray;
134 break;
135 case JsonTokenType.EndArray:
136 _tokenStack.Pop();
137 CurrentTokenType = TokenType.EndArray;
138 break;
139 case JsonTokenType.String:
141 break;
142 case JsonTokenType.True:
144 break;
145 case JsonTokenType.False:
147 break;
148 case JsonTokenType.Null:
150 break;
151 case JsonTokenType.Comment:
152 case JsonTokenType.Number:
153 default:
154 throw new SerializationException($"Unhandled JSON token type {_json.TokenType}.");
155 }
156
157 return true;
158 }
159
165 public object? GetValue()
166 {
167 return CurrentTokenType switch
168 {
169 TokenType.FieldName or TokenType.String => GetString(),
170 TokenType.Int => GetInt(),
171 TokenType.Long => GetLong(),
172 TokenType.Double => GetDouble(),
173 TokenType.Date => GetDate(),
174 TokenType.Time => GetTime(),
175 TokenType.True or TokenType.False => GetBoolean(),
176 TokenType.Module => GetModule(),
177 TokenType.Bytes => GetBytes(),
178 _ => throw new SerializationException($"{CurrentTokenType} does not have an associated value")
179 };
180 }
181
186 public string? GetString()
187 {
188 if (CurrentTokenType != TokenType.String && CurrentTokenType != TokenType.FieldName)
189 {
190 throw new InvalidOperationException($"Fauna token value isn't a {TokenType.String.ToString()} or a {TokenType.FieldName.ToString()}.");
191 }
192
193 try
194 {
195 if (_bufferedTokenValue != null) return (string)_bufferedTokenValue;
196 return _json.GetString();
197 }
198 catch (Exception e)
199 {
200 throw new SerializationException("Failed to get string", e);
201 }
202 }
203
208 public bool GetBoolean()
209 {
210 try
211 {
212 return _json.GetBoolean();
213 }
214 catch (Exception e)
215 {
216 throw new SerializationException("Failed to get boolean", e);
217 }
218 }
219
224 public DateOnly GetDate()
225 {
226 ValidateTaggedType(TokenType.Date);
227
228 try
229 {
230 return DateOnly.Parse(_taggedTokenValue!);
231 }
232 catch (Exception e)
233 {
234 throw new SerializationException($"Failed to get date from {_taggedTokenValue}", e);
235 }
236 }
237
242 public float GetFloat()
243 {
244 ValidateTaggedTypes(TokenType.Int, TokenType.Long, TokenType.Double);
245
246 try
247 {
248 return float.Parse(_taggedTokenValue!, CultureInfo.InvariantCulture);
249 }
250 catch (Exception e)
251 {
252 throw new SerializationException($"Failed to get float from {_taggedTokenValue}", e);
253 }
254 }
255
260 public double GetDouble()
261 {
262 ValidateTaggedTypes(TokenType.Int, TokenType.Long, TokenType.Double);
263
264 try
265 {
266 return double.Parse(_taggedTokenValue!, CultureInfo.InvariantCulture);
267 }
268 catch (Exception e)
269 {
270 throw new SerializationException($"Failed to get double from {_taggedTokenValue}", e);
271 }
272 }
273
278 public decimal GetDoubleAsDecimal()
279 {
280 ValidateTaggedType(TokenType.Double);
281
282 try
283 {
284 return decimal.Parse(_taggedTokenValue!, CultureInfo.InvariantCulture);
285 }
286 catch (Exception e)
287 {
288 throw new SerializationException($"Failed to get decimal from {_taggedTokenValue}", e);
289 }
290 }
291
296 public byte GetByte()
297 {
298 ValidateTaggedTypes(TokenType.Int);
299
300 try
301 {
302 return byte.Parse(_taggedTokenValue!);
303 }
304 catch (Exception e)
305 {
306 throw new SerializationException($"Failed to get byte from {_taggedTokenValue}", e);
307 }
308 }
309
314 public byte[] GetBytes()
315 {
316 ValidateTaggedTypes(TokenType.Bytes);
317
318 try
319 {
320 return Convert.FromBase64String(_taggedTokenValue!);
321 }
322 catch (Exception e)
323 {
324 throw new SerializationException($"Failed to get byte array from {_taggedTokenValue}", e);
325 }
326 }
327
332 public sbyte GetUnsignedByte()
333 {
334 ValidateTaggedTypes(TokenType.Int);
335
336 try
337 {
338 return sbyte.Parse(_taggedTokenValue!);
339 }
340 catch (Exception e)
341 {
342 throw new SerializationException($"Failed to get sbyte from {_taggedTokenValue}", e);
343 }
344 }
345
350 public int GetInt()
351 {
352 ValidateTaggedTypes(TokenType.Int, TokenType.Long);
353
354 try
355 {
356 return int.Parse(_taggedTokenValue!);
357 }
358 catch (Exception e)
359 {
360 throw new SerializationException($"Failed to get int from {_taggedTokenValue}", e);
361 }
362 }
363
368 public uint GetUnsignedInt()
369 {
370 ValidateTaggedTypes(TokenType.Int, TokenType.Long);
371
372 try
373 {
374 return uint.Parse(_taggedTokenValue!);
375 }
376 catch (Exception e)
377 {
378 throw new SerializationException($"Failed to get uint from {_taggedTokenValue}", e);
379 }
380 }
381
386 public short GetShort()
387 {
388 ValidateTaggedTypes(TokenType.Int, TokenType.Long);
389 try
390 {
391 return short.Parse(_taggedTokenValue!);
392 }
393 catch (Exception e)
394 {
395 throw new SerializationException($"Failed to get short from {_taggedTokenValue}", e);
396 }
397 }
398
403 public ushort GetUnsignedShort()
404 {
405 ValidateTaggedTypes(TokenType.Int, TokenType.Long);
406 try
407 {
408 return ushort.Parse(_taggedTokenValue!);
409 }
410 catch (Exception e)
411 {
412 throw new SerializationException($"Failed to get ushort from {_taggedTokenValue}", e);
413 }
414 }
415
420 public long GetLong()
421 {
422 ValidateTaggedTypes(TokenType.Int, TokenType.Long);
423
424 try
425 {
426 return long.Parse(_taggedTokenValue!);
427 }
428 catch (Exception e)
429 {
430 throw new SerializationException($"Failed to get long from {_taggedTokenValue}", e);
431 }
432 }
433
439 {
440 ValidateTaggedType(TokenType.Module);
441
442 return new Module(_taggedTokenValue!);
443 }
444
450 {
451 ValidateTaggedType(TokenType.EventSource);
452
453 return new EventSource(_taggedTokenValue!);
454 }
455
460 public DateTime GetTime()
461 {
462 ValidateTaggedType(TokenType.Time);
463
464 try
465 {
466 return DateTime.Parse(_taggedTokenValue!);
467 }
468 catch (Exception e)
469 {
470 throw new SerializationException($"Failed to get time from {_taggedTokenValue}", e);
471 }
472 }
473
479 public string TryGetString(out string value)
480 {
481 throw new NotImplementedException();
482 }
483
489 public bool TryGetBoolean(out bool value)
490 {
491 throw new NotImplementedException();
492 }
493
499 public DateTime TryGetDateTime(out DateTime value)
500 {
501 throw new NotImplementedException();
502 }
503
509 public double TryGetDouble(out double value)
510 {
511 throw new NotImplementedException();
512 }
513
519 public int TryGetInt(out int value)
520 {
521 throw new NotImplementedException();
522 }
523
529 public long TryGetLong(out long value)
530 {
531 throw new NotImplementedException();
532 }
533
539 public Module TryGetModule(out Module value)
540 {
541 throw new NotImplementedException();
542 }
543
544 private void ValidateTaggedType(TokenType type)
545 {
546 if (CurrentTokenType != type || _taggedTokenValue == null || _taggedTokenValue.GetType() != typeof(string))
547 {
548 throw new InvalidOperationException($"CurrentTokenType is a {CurrentTokenType.ToString()}, not a {type.ToString()}.");
549 }
550 }
551
552 private void ValidateTaggedTypes(params TokenType[] types)
553 {
554 if (!types.Contains(CurrentTokenType) || _taggedTokenValue == null || _taggedTokenValue.GetType() != typeof(string))
555 {
556 throw new InvalidOperationException($"CurrentTokenType is a {CurrentTokenType.ToString()}, not in {types}.");
557 }
558 }
559
560
561 private void HandleStartObject()
562 {
563 AdvanceTrue();
564
565 switch (_json.TokenType)
566 {
567 case JsonTokenType.PropertyName:
568 switch (_json.GetString())
569 {
570 case "@date":
571 HandleTaggedString(TokenType.Date);
572 break;
573 case "@doc":
574 AdvanceTrue();
575 CurrentTokenType = TokenType.StartDocument;
576 _tokenStack.Push(TokenType.StartDocument);
577 break;
578 case "@double":
579 HandleTaggedString(TokenType.Double);
580 break;
581 case "@int":
582 HandleTaggedString(TokenType.Int);
583 break;
584 case "@long":
585 HandleTaggedString(TokenType.Long);
586 break;
587 case "@mod":
588 HandleTaggedString(TokenType.Module);
589 break;
590 case "@stream":
591 HandleTaggedString(TokenType.EventSource);
592 break;
593 case "@object":
594 AdvanceTrue();
595 CurrentTokenType = TokenType.StartObject;
596 _tokenStack.Push(TokenTypeInternal.StartEscapedObject);
597 break;
598 case "@ref":
599 AdvanceTrue();
600 CurrentTokenType = TokenType.StartRef;
601 _tokenStack.Push(TokenType.StartRef);
602 break;
603 case "@set":
604 AdvanceTrue();
605 CurrentTokenType = TokenType.StartPage;
606 if (_json.TokenType == JsonTokenType.String)
607 {
608 _bufferedTokenValue = _json.GetString();
609 _bufferedTokenType = TokenType.String;
610 _tokenStack.Push(TokenTypeInternal.StartPageUnmaterialized);
611 }
612 else
613 {
614 _tokenStack.Push(TokenType.StartPage);
615 }
616 break;
617 case "@time":
618 HandleTaggedString(TokenType.Time);
619 break;
620 case "@bytes":
621 HandleTaggedString(TokenType.Bytes);
622 break;
623 default:
624 _bufferedTokenType = TokenType.FieldName;
625 _tokenStack.Push(TokenType.StartObject);
626 CurrentTokenType = TokenType.StartObject;
627 break;
628 }
629 break;
630 case JsonTokenType.EndObject:
631 _bufferedTokenType = TokenType.EndObject;
632 _tokenStack.Push(TokenType.StartObject);
633 CurrentTokenType = TokenType.StartObject;
634 break;
635 default:
636 throw new SerializationException($"Unexpected token following StartObject: {_json.TokenType}");
637 }
638 }
639
640 private void HandleEndObject()
641 {
642 var startToken = _tokenStack.Pop();
643 switch (startToken)
644 {
645 case TokenType.StartDocument:
646 CurrentTokenType = TokenType.EndDocument;
647 AdvanceTrue();
648 break;
649 case TokenType.StartPage:
650 CurrentTokenType = TokenType.EndPage;
651 AdvanceTrue();
652 break;
653 case TokenTypeInternal.StartPageUnmaterialized:
654 CurrentTokenType = TokenType.EndPage;
655 break;
656 case TokenType.StartRef:
658 AdvanceTrue();
659 break;
660 case TokenTypeInternal.StartEscapedObject:
661 CurrentTokenType = TokenType.EndObject;
662 AdvanceTrue();
663 break;
664 case TokenType.StartObject:
665 CurrentTokenType = TokenType.EndObject;
666 break;
667 default:
668 throw new SerializationException($"Unexpected token {startToken}. This might be a bug.");
669 }
670 }
671
683 private void HandleTaggedString(TokenType token)
684 {
685 AdvanceTrue();
686 CurrentTokenType = token;
687 _taggedTokenValue = _json.GetString();
688 AdvanceTrue();
689 }
690
691 private bool Advance()
692 {
693 try
694 {
695 return _json.Read();
696 }
697 catch (Exception e)
698 {
699 throw new SerializationException("Failed to advance underlying JSON reader.", e);
700 }
701 }
702
703 private void AdvanceTrue()
704 {
705 if (!Advance())
706 {
707 throw new SerializationException("Unexpected end of underlying JSON reader.");
708 }
709 }
710}
Represents error that occur during serialization and deserialization of Fauna data.
Represents a Fauna EventSource for initializing Streams and Feeds.
Definition EventSource.cs:9
Represents a module, a singleton object grouping related functionalities. Modules are serialized as @...
Definition Module.cs:8
TokenType
Enumerates the types of tokens used in Fauna serialization.
Definition TokenType.cs:7
@ EventSource
The token type is the Fauna event source token.
Represents a reader that provides fast, non-cached, forward-only access to serialized data.
Utf8FaunaReader(ReadOnlySequence< byte > bytes)
Initializes a new Utf8FaunaReader to read from a ReadOnlySequence of bytes.
decimal GetDoubleAsDecimal()
Retrieves a decimal value from the current token.
DateOnly GetDate()
Retrieves a DateOnly value from the current token.
void Skip()
Skips the value of the current token.
DateTime TryGetDateTime(out DateTime value)
Tries to retrieve a DateTime value from the current token.
byte GetByte()
Retrieves an byte value from the current token.
Module TryGetModule(out Module value)
Tries to retrieve a Module object from the current token.
short GetShort()
Retrieves an short value from the current token.
long TryGetLong(out long value)
Tries to retrieve a long value from the current token.
double GetDouble()
Retrieves a double value from the current token.
byte[] GetBytes()
Retrieves a byte array value from the current token.
Utf8FaunaReader(string str)
Initializes a new Utf8FaunaReader to read from a string.
string TryGetString(out string value)
Tries to retrieve a string value from the current token.
bool TryGetBoolean(out bool value)
Tries to retrieve a boolean value from the current token.
object? GetValue()
Gets the value of the current token.
TokenType CurrentTokenType
Gets the type of the current token.
EventSource GetEventSource()
Retrieves an EventSource token string from the current token.
int TryGetInt(out int value)
Tries to retrieve an integer value from the current token.
uint GetUnsignedInt()
Retrieves an unsigned integer value from the current token.
ushort GetUnsignedShort()
Retrieves an unsigned short value from the current token.
long GetLong()
Retrieves a long value from the current token.
bool GetBoolean()
Retrieves a boolean value from the current JSON token.
float GetFloat()
Retrieves a float value from the current token.
string? GetString()
Retrieves a string value from the current token.
bool Read()
Reads the next token from the source.
DateTime GetTime()
Retrieves a DateTime value from the current token.
double TryGetDouble(out double value)
Tries to retrieve a double value from the current token.
sbyte GetUnsignedByte()
Retrieves an unsigned byte value from the current token.
int GetInt()
Retrieves an integer value from the current token.
Module GetModule()
Retrieves a Module object from the current token.