static IEnumerable <object> ReadArray(StringView input) { #if SHARE_INTERNAL_METHODS input.TrimStart(); if (input.Length < 1 || input[0] != '[') { throw new Exception($"No Array at offset {input.offset}"); } #endif input.Move(1); input.TrimStart(); List <object> objects = new List <object>(); int startOffset = input.offset; if (input.Length != 0 && input[0] == ']') { input.Move(1); return(objects); } while (true) { input.TrimStart(); #if ALLOW_TRAILING_ARRAY_COMMAS if (input.Length == 0 || input[0] == ']') { break; } #endif object child = ReadElement(input); objects.Add(child); input.TrimStart(); if (input.Length == 0 || input[0] == ']') { break; } if (input[0] != ',') { throw new Exception($"Unexpected char at offset {input.offset}. Expected array separator ','"); } input.Move(1); } if (input.Length == 0) { throw new Exception($"Array at offset {startOffset} isn't closed"); } input.Move(1); return(objects); }
static (string, object) ReadObjectEntry(StringView input) { #if SHARE_INTERNAL_METHODS input.TrimStart(); #endif if (input.Length < 1 || input[0] != '"') { throw new Exception($"Unexpected value at offset {input.offset}. Expected object-key string at {input.offset}"); } string key = ReadString(input); input.TrimStart(); if (input.Length == 0 || input[0] != ':') { throw new Exception($"Unexpected value at offset {input.offset}. Expected object key-value separator ':'"); } input.Move(1); object value = ReadElement(input); return(key, value); }
static object ReadInt(StringView input) { int offset = -1; while (++offset < input.Length) { char c = input[offset]; if (c == '-' && offset == 0) { continue; } //ASCII 0 - 9 if (c >= 0x30 && c <= 0x39) { continue; } break; } var intString = input.SubString(0, offset); input.Move(offset); try { long longValue = Convert.ToInt64(intString); //Previous versions always returned int32, //and asint32 is the most common data type //for numbers we return an int32 if possible if (longValue >= int.MinValue && longValue <= int.MaxValue) { return((int)longValue); } return(longValue); } catch (OverflowException) { } try { return(Convert.ToUInt64(intString)); } catch (OverflowException) { } return(Convert.ToDouble(intString)); }
static object ReadNull(StringView input) { if (input.Length < 4) { throw new Exception($"Unexpected value at offset {input.offset}"); } string nullStr = input.SubString(0, 4); input.Move(4); if (nullStr == "null") { return(null); } throw new Exception($"Unexpected value at offset {input.offset}"); }
static bool ReadBool(StringView input) { if (input.Length < 4) { throw new Exception($"Unexpected value at offset {input.offset}"); } int offset = -1; while (++offset < input.Length) { char c = input[offset]; //ASCII a - z if (c >= 0x61 && c <= 0x7a) { continue; } break; } if (offset > 5) { throw new Exception($"Unexpected value at offset {input.offset}"); } string boolStr = input.SubString(0, offset); input.Move(boolStr.Length); if (boolStr == "true") { return(true); } if (boolStr == "false") { return(false); } throw new Exception($"Unexpected value at offset {input.offset}"); }
static double ReadDouble(StringView input) { int nonNumberChars = 0; int doubleDotOffset = -1; int eOffset = -1; int eSignOffset = -1; int offset = -1; while (++offset < input.Length) { char c = input[offset]; if (c == '-' && offset == 0) { ++nonNumberChars; continue; } if (c == '.' && doubleDotOffset == -1) { if (offset == nonNumberChars) { throw new Exception($"Invalid float value at offset {input.offset}. Floats mustn't start with a dot"); } doubleDotOffset = offset; ++nonNumberChars; continue; } if (c == 'e' && eOffset == -1) { if (offset == nonNumberChars) { throw new Exception($"Invalid float value at offset {input.offset}. Floats mustn't start with an 'e'"); } eOffset = offset; ++nonNumberChars; continue; } if ((c == '+' || c == '-') && eOffset != -1 && offset == eOffset + 1) { eSignOffset = offset; continue; } //ASCII 0 - 9 if (c >= 0x30 && c <= 0x39) { continue; } break; } if (doubleDotOffset == offset - 1) { throw new Exception($"Invalid float value at offset {input.offset}. Floats mustn't end with a dot"); } if (eOffset == offset - 1) { throw new Exception($"Invalid float value at offset {input.offset}. Floats mustn't end with an 'e'"); } if (eSignOffset == offset - 1) { throw new Exception($"Invalid float value at offset {input.offset}. Floats mustn't end with an 'e'-sign"); } var doubleString = input.SubString(0, offset); input.Move(offset); return(Convert.ToDouble(doubleString, CultureInfo.InvariantCulture)); }
static string ReadString(StringView input) { #if SHARE_INTERNAL_METHODS input.TrimStart(); if (input.Length < 1 || input[0] != '"') { throw new Exception($"No string at offset {input.offset}"); } #endif if (input.Length < 2) { throw new Exception($"Invalid string at offset {input.offset}"); } input.Move(1); var builder = new StringBuilder(); bool escape = false; bool stringClosed = false; while (input.Length != 0) { char c = input[0]; input.Move(1); if (escape) { switch (c) { case '\\': builder.Append('\\'); break; case 'b': builder.Append('\b'); break; case 'f': builder.Append('\f'); break; case 'r': builder.Append('\r'); break; case 'n': builder.Append('\n'); break; case 't': builder.Append('\t'); break; case '"': builder.Append('"'); break; case 'u': builder.Append(ReadEscapedUnicodeChar()); break; default: throw new Exception($"Invalid string escape sequence at offset {input.offset - 2}"); } escape = false; continue; } if (c == '\\') { escape = true; continue; } if (c == '"') { stringClosed = true; break; } builder.Append(c); } if (!stringClosed) { throw new Exception($"String not closed at offset {input.offset}"); } return(builder.ToString()); char ReadEscapedUnicodeChar() { if (input.Length < 4) { throw new Exception($"Invalid Unicode escape sequence at offset {input.offset}"); } string hexChars = input.SubString(0, 4); foreach (var c in hexChars) { if ((c >= 0x30 && c <= 0x39) || (c >= 0x41 && c <= 0x46) || (c >= 0x61 && c <= 0x66)) { continue; } throw new Exception($"Invalid Unicode escape sequence at offset {input.offset}"); } input.Move(4); int unicodePos = int.Parse(hexChars, NumberStyles.HexNumber); return((char)unicodePos); } }
static Dictionary <string, object> ReadObject(StringView input) { #if SHARE_INTERNAL_METHODS input.TrimStart(); if (input.Length < 1 || input[0] != '{') { throw new Exception($"No Object at offset {input.offset}"); } #endif input.Move(1); input.TrimStart(); Dictionary <string, object> childs = new Dictionary <string, object>(); int startOffset = input.offset; if (input.Length != 0 && input[0] == '}') { input.Move(1); return(childs); } while (true) { input.TrimStart(); #if ALLOW_TRAILING_OBJECT_COMMAS if (input.Length == 0 || input[0] == '}') { break; } #endif var(key, value) = ReadObjectEntry(input); if (childs.ContainsKey(key)) { throw new Exception($"Key '{key}' already exists in object at offset {startOffset}"); } childs[key] = value; input.TrimStart(); if (input.Length == 0 || input[0] == '}') { break; } if (input[0] != ',') { throw new Exception($"Unexpected char at offset {input.offset}. Expected object separator ','"); } input.Move(1); } input.TrimStart(); if (input.Length == 0) { throw new Exception($"Object at offset {startOffset} isn't closed"); } input.Move(1); return(childs); }