# A JSON text (RFC 8259) read into a tree of values, then walked breadth first for a digest of it: how many of each # kind, the numbers' sum, the strings' and the keys' bytes. Input that is no JSON text leaves valid false. variant Json { null bool(bool) num(number) str(text) arr(list of Json) obj(list of Member) } record Member { name: text value: Json } record Open { items: list of Json members: list of Member name: text object = false } state valid = false state nulls = 0 state trues = 0 state falses = 0 state numbers = 0 state strings = 0 state arrays = 0 state objects = 0 state sum = 0 state text_bytes = 0 state key_bytes = 0 input check = message { data: bytes } fn space(b) = b == 0x20 || b == 0x09 || b == 0x0a || b == 0x0d fn digit(b) = b >= 0x30 && b <= 0x39 fn hex(b) = digit(b) || (b >= 0x61 && b <= 0x66) || (b >= 0x41 && b <= 0x46) fn nibble(b) = if digit(b) { b - 0x30 } else if b >= 0x61 { b - 0x57 } else { b - 0x37 } on check { nulls, trues, falses, numbers, strings, arrays, objects, sum, text_bytes, key_bytes = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 let d = bytes(text(check.data)) let n, i, ok, mode = d.bytes, 0, true, 0 # mode 0: a value, 1: after one, 2: a key, 3: a key or }, 4: a value or ] let root: list of Json let open: list of Open let v: list of Json # the value just read, then placed let q: list of Json # the tree's values, breadth first while ok { while i < n && space(d[i]) { i = i + 1 } if i == n { ok = mode == 1 && open.count == 0 break } let b = d[i] if mode == 1 { if open.count == 0 { ok = false } else if b == 0x2c { mode = if open.last.object { 2 } else { 0 } } else if b == 0x5d && !open.last.object { v.append(Json.arr(open.last.items)) } else if b == 0x7d && open.last.object { v.append(Json.obj(open.last.members)) } else { ok = false } if v.count > 0 { open.remove(open.last) } i = i + 1 } else if mode == 3 && b == 0x7d || mode == 4 && b == 0x5d { if b == 0x7d { v.append(Json.obj(open.last.members)) } else { v.append(Json.arr(open.last.items)) } open.remove(open.last) i = i + 1 } else if mode == 2 || mode == 3 { ok = b == 0x22 mode = 5 # the key, then ':' } else if b == 0x5b || b == 0x7b { open.append(Open { object: b == 0x7b }) mode = if b == 0x5b { 4 } else { 3 } i = i + 1 } else if b == 0x22 || mode == 5 { i = i + 1 # a string: its bytes, escapes decoded let s = d[i .. i] while ok && i < n && d[i] != 0x22 { let start = i while i < n && d[i] != 0x22 && d[i] != 0x5c && d[i] >= 0x20 { i = i + 1 } s = s + d[start .. i] if i < n && d[i] < 0x20 { ok = false } else if i + 1 < n && d[i] == 0x5c { let e = d[i + 1] if e == 0x75 { ok = i + 5 < n && hex(d[i + 2]) && hex(d[i + 3]) && hex(d[i + 4]) && hex(d[i + 5]) let u = if ok { nibble(d[i + 2]) << 12 | nibble(d[i + 3]) << 8 | nibble(d[i + 4]) << 4 | nibble(d[i + 5]) } else { 0 } i = i + 6 if (u >= 0xd800 && u < 0xdc00 && i + 5 < n && d[i] == 0x5c && d[i + 1] == 0x75 && hex(d[i + 2]) && hex(d[i + 3]) && hex(d[i + 4]) && hex(d[i + 5])) { let low = nibble(d[i + 2]) << 12 | nibble(d[i + 3]) << 8 | nibble(d[i + 4]) << 4 | nibble(d[i + 5]) if low >= 0xdc00 && low < 0xe000 { u = 0x10000 + ((u - 0xd800) << 10 | low - 0xdc00) i = i + 6 } } if u < 0x80 { s = s + byte(u) } else if u < 0x800 { s = s + byte(0xc0 | u >> 6) + byte(0x80 | u & 0x3f) } else if u < 0x10000 { s = s + byte(0xe0 | u >> 12) + byte(0x80 | u >> 6 & 0x3f) + byte(0x80 | u & 0x3f) } else { s = s + byte(0xf0 | u >> 18) + byte(0x80 | u >> 12 & 0x3f) + byte(0x80 | u >> 6 & 0x3f) + byte(0x80 | u & 0x3f) } } else { let to = (if e == 0x62 { 0x08 } else if e == 0x66 { 0x0c } else if e == 0x6e { 0x0a } else if e == 0x72 { 0x0d } else if e == 0x74 { 0x09 } else { e }) ok = e == 0x22 || e == 0x5c || e == 0x2f || e == 0x62 || e == 0x66 || e == 0x6e || e == 0x72 || e == 0x74 s = s + byte(to) i = i + 2 } } else if i < n && d[i] == 0x5c { ok = false } } ok = ok && i < n i = i + 1 if mode == 5 { open[open.count - 1].name = text(s) while i < n && space(d[i]) { i = i + 1 } ok = ok && i < n && d[i] == 0x3a i = i + 1 mode = 0 } else { v.append(Json.str(text(s))) } } else if b == 0x2d || digit(b) { # -?(0|[1-9]\d*)(\.\d+)?([eE][+-]?\d+)?: its text read as a number let start = i if b == 0x2d { i = i + 1 } while i < n && (digit(d[i]) || d[i] == 0x2e || d[i] == 0x65 || d[i] == 0x45 || d[i] == 0x2b || d[i] == 0x2d) { i = i + 1 } v.append(Json.num(number(text(d[start .. i])))) } else { let word = if b == 0x74 { "true" } else if b == 0x66 { "false" } else { "null" } ok = i + word.bytes <= n && text(d[i .. i + word.bytes]) == word i = i + word.bytes if ok && b == 0x74 { v.append(Json.bool(true)) } else if ok && b == 0x66 { v.append(Json.bool(false)) } else if ok { v.append(Json.null) } } if ok && v.count > 0 { # a value read: placed in what is open, or the root if open.count == 0 { ok = root.count == 0 root.append(v.last) } else if open.last.object { open.last.members.append(Member { name: open.last.name, value: v.last }) } else { open.last.items.append(v.last) } v.remove(v.last) mode = 1 } } valid = ok if ok { q.append(root.first) let at = 0 while at < q.count { match q[at] { null => nulls = nulls + 1 bool(t) => if t { trues = trues + 1 } else { falses = falses + 1 } num(x) => { numbers = numbers + 1 sum = sum + x } str(t) => { strings = strings + 1 text_bytes = text_bytes + t.bytes } arr(items) => { arrays = arrays + 1 for y in items { q.append(y) } } obj(members) => { objects = objects + 1 for m in members { key_bytes = key_bytes + m.name.bytes q.append(m.value) } } } at = at + 1 } } }