final class · System.Text

JsonReader

public final class JsonReader

Reads a value from UTF-8 units. Arrays and objects nest at most MaxDepth deep (a FormatError beyond).

Kind
final class
Namespace
System.Text
Assembly
System.Core
Access
public, final
Source file
Json.ant

Inheritance

Base classes
Any JsonReader

Constructors

new(u8[] units)
#

Properties

bool AtEnd { get; }
#

read-only

Methods

Fail(str what)
#
SkipSpace()
#
ReadValue() : JsonValue
#
ExpectWord(str word)
#
ReadNumber() : JsonValue
#

A number by the JSON grammar: -?, 0 or digits not starting with one, a fraction . digits, an exponent e/E, a sign, digits (no leading zeros, no 1., no .5).

Digits() : i32
#

Past the digits here; how many there were.

ReadText() : str
#
ReadKey() : str
#

An object's key: the string of an earlier key with the same units when there is one.

ReadHex4() : u32
#
ReadArray() : JsonValue
#
ReadObject() : JsonValue
#

Constants

const i32 MaxDepth = 256
#

Private and internal members (8)

u8[] p
#

private

i32 at
#

private

i32 depth
#

private

u8[] scratch
#

private

i32 used
#

private

str?[] keys
#

private

Enter()
#

private

Put(u32 unit)
#

private

Inherited members (5)

Source

JsonReader as Antril, decompiled from its Larva HIR.

System.Text.JsonReader.ant
// System.Text.JsonReader — System.Core (decompiled from Larva HIR)

import System;

into namespace System.Text
{
	/// Reads a value from UTF-8 units. Arrays and objects nest at most MaxDepth deep (a FormatError beyond).
	public final class JsonReader
	{
		u8[] p;
		i32 at;
		i32 depth;
		u8[] scratch;
		i32 used;
		str?[] keys;

		Enter()
		{
			depth++;
			if (depth > 256)
				Fail("nesting deeper than " + 256);
		}

		Put(u32 unit)
		{
			if (used == scratch.Length)
			{
				u8[] bigger = new u8[scratch.Length * 2];
				Array.Copy(scratch, 0, bigger, 0, used);
				scratch = bigger;
			}
			scratch[used] = (u8)unit;
			used++;
		}

		public:
			const i32 MaxDepth = 256;

			new(u8[] units)
			{
				p = units;
				scratch = new u8[64];
				keys = new str?[64];
			}

			Fail(str what)
			{
				throw new FormatError("JSON: " + what + " at " + at);
			}

			SkipSpace()
			{
				loop (at < p.Length && (p[at] == 32 || p[at] == 9 || p[at] == 10 || p[at] == 13))
					at++;
			}

			ReadValue() : JsonValue
			{
				SkipSpace();
				if (at >= p.Length)
					Fail("unexpected end");
				u32 c = p[at];
				if (c == 123)
					return ReadObject();
				if (c == 91)
					return ReadArray();
				if (c == 34)
					return JsonValue.Of(ReadText());
				if (c == 116)
				{
					ExpectWord("true");
					return JsonValue.Of(true);
				}
				if (c == 102)
				{
					ExpectWord("false");
					return JsonValue.Of(false);
				}
				if (c == 110)
				{
					ExpectWord("null");
					return JsonValue.Null();
				}
				if (c == 45 || c >= 48 && c <= 57)
					return ReadNumber();
				Fail("unexpected character");
				return JsonValue.Null();
			}

			ExpectWord(str word)
			{
				i32 n = word.Length;
				loop (i32 i = 0..n)
				{
					if (at + i >= p.Length || p[at + i] != word[i])
						Fail("expected " + word);
				}
				at += n;
			}

			/// A number by the JSON grammar: `-`?, `0` or digits not starting with one, a fraction `.` digits, an
			/// exponent `e`/`E`, a sign, digits (no leading zeros, no `1.`, no `.5`).
			ReadNumber() : JsonValue
			{
				i32 start = at;
				bool negative = p[at] == 45;
				if (negative)
					at++;
				if (at < p.Length && p[at] == 48)
					at++;
				else if (Digits() == 0)
					Fail("bad number");
				i32 whole = at - start - (negative ? 1 : 0);
				bool integer = !(at < p.Length && (p[at] == 46 || p[at] == 101 || p[at] == 69));
				if (integer && whole <= 15 && !(negative && whole == 1 && p[at - 1] == 48))
				{
					i64 value = 0;
					loop (i32 i = at - whole..at)
						value = value * 10 + ((i64)p[i] - 48);
					return JsonValue.Of(negative ? -value : value);
				}
				if (at < p.Length && p[at] == 46)
				{
					at++;
					if (Digits() == 0)
						Fail("bad number");
				}
				if (at < p.Length && (p[at] == 101 || p[at] == 69))
				{
					at++;
					if (at < p.Length && (p[at] == 43 || p[at] == 45))
						at++;
					if (Digits() == 0)
						Fail("bad number");
				}
				f64? parsed = Float64.TryParse(new str(p, start, at - start));
				if (parsed == null)
					Fail("bad number");
				return JsonValue.Of(parsed ?? 0.0);
			}

			/// Past the digits here; how many there were.
			Digits() : i32
			{
				i32 start = at;
				loop (at < p.Length && p[at] >= 48 && p[at] <= 57)
					at++;
				return at - start;
			}

			ReadText() : str
			{
				at++;
				i32 start = at;
				loop (at < p.Length && p[at] != 34 && p[at] != 92)
					at++;
				if (at >= p.Length)
					Fail("unterminated string");
				if (p[at] == 34)
				{
					at++;
					return new str(p, start, at - 1 - start);
				}
				used = 0;
				loop (i32 i = start..at)
					Put(p[i]);
				loop (true)
				{
					if (at >= p.Length)
						Fail("unterminated string");
					u32 c = p[at];
					at++;
					if (c == 34)
						break;
					if (c != 92)
					{
						Put(c);
						continue;
					}
					if (at >= p.Length)
						Fail("unterminated escape");
					u32 e = p[at];
					at++;
					if (e == 110)
						Put(10);
					else if (e == 116)
						Put(9);
					else if (e == 114)
						Put(13);
					else if (e == 98)
						Put(8);
					else if (e == 102)
						Put(12);
					else if (e == 34 || e == 92 || e == 47)
						Put(e);
					else if (e == 117)
					{
						u32 code = ReadHex4();
						if (code >= 55296 && code < 56320)
						{
							i32 before = at;
							u32 low = 0;
							if (at + 1 < p.Length && p[at] == 92 && p[at + 1] == 117)
							{
								at += 2;
								low = ReadHex4();
							}
							if (low >= 56320 && low < 57344)
								code = 65536 + (code - 55296 << 10) + (low - 56320);
							else
							{
								at = before;
								code = 65533;
							}
						}
						else if (code >= 56320 && code < 57344)
							code = 65533;
						if (code < 128)
							Put(code);
						else if (code < 2048)
						{
							Put(192 | code >> 6);
							Put(128 | code & 63);
						}
						else if (code < 65536)
						{
							Put(224 | code >> 12);
							Put(128 | code >> 6 & 63);
							Put(128 | code & 63);
						}
						else
						{
							Put(240 | code >> 18);
							Put(128 | code >> 12 & 63);
							Put(128 | code >> 6 & 63);
							Put(128 | code & 63);
						}
					}
					else
						Fail("bad escape");
				}
				return new str(scratch, 0, used);
			}

			/// An object's key: the string of an earlier key with the same units when there is one.
			ReadKey() : str
			{
				i32 start = at + 1;
				i32 end = start;
				u32 h = 0;
				loop (end < p.Length && p[end] != 34 && p[end] != 92)
				{
					h = h * 31 + (u32)p[end];
					end++;
				}
				if (end >= p.Length || p[end] != 34 || end - start > 32)
					return ReadText();
				i32 count = end - start;
				i32 slot = (i32)(h & 63);
				at = end + 1;
				if (keys[slot] is str known && known.Length == count)
				{
					bool same = true;
					loop (i32 i = 0..count)
					{
						if (known[i] != p[start + i])
						{
							same = false;
							break;
						}
					}
					if (same)
						return known;
				}
				str key = new str(p, start, count);
				keys[slot] = key;
				return key;
			}

			ReadHex4() : u32
			{
				u32 value = 0;
				loop (i32 i = 0..4)
				{
					if (at >= p.Length)
						Fail("bad \\u escape");
					u32 c = p[at];
					at++;
					u32 digit = c >= 48 && c <= 57 ? c - 48 : c >= 97 && c <= 102 ? c - 97 + 10 : c >= 65 && c <= 70 ? c - 65 + 10 : 16;
					if (digit > 15)
						Fail("bad \\u escape");
					value = value * 16 + digit;
				}
				return value;
			}

			ReadArray() : JsonValue
			{
				at++;
				Enter();
				JsonValue array = JsonValue.NewArray();
				SkipSpace();
				if (at < p.Length && p[at] == 93)
				{
					at++;
					depth--;
					return array;
				}
				loop (true)
				{
					array.Add(ReadValue());
					SkipSpace();
					if (at >= p.Length)
						Fail("unterminated array");
					u32 c = p[at];
					at++;
					if (c == 93)
						break;
					if (c != 44)
						Fail("expected , or ]");
				}
				depth--;
				return array;
			}

			ReadObject() : JsonValue
			{
				at++;
				Enter();
				JsonValue obj = JsonValue.NewObject();
				SkipSpace();
				if (at < p.Length && p[at] == 125)
				{
					at++;
					depth--;
					return obj;
				}
				loop (true)
				{
					SkipSpace();
					if (at >= p.Length || p[at] != 34)
						Fail("expected a key");
					str key = ReadKey();
					SkipSpace();
					if (at >= p.Length || p[at] != 58)
						Fail("expected :");
					at++;
					obj.Set(key, ReadValue());
					SkipSpace();
					if (at >= p.Length)
						Fail("unterminated obj");
					u32 c = p[at];
					at++;
					if (c == 125)
						break;
					if (c != 44)
						Fail("expected , or }");
				}
				depth--;
				return obj;
			}

			bool AtEnd
			{
				get => at >= p.Length;
			}
	}
}
Esc