Skip to content
Early preview. Maxon is under active development — incomplete in places, with breaking changes expected before a 1.0 release.

Data & Hashing

An RFC 8259 JSON parser and serializer. A JsonDoc owns a flat arena of JsonNodes; an array or object refers to its children by JsonNodeId. Walk a document through the JsonDoc accessors.

Method Returns Throws Description
Json.parse(text String) JsonDoc JsonError Parse a document; doc.root is the top-level value.
Json.stringify(doc JsonDoc) String — Compact output of the tree at doc.root. NaN and infinities are written as null.
Json.stringifyPrettyNode(doc JsonDoc, root JsonNodeId) String — Indented output (two spaces per level) of the subtree at root.
Json.quote(text String) String — text as one quoted JSON string value, with ", \ and control characters escaped.
Member Returns Throws Description
JsonDoc.create() JsonDoc — An empty document.
nodes field, Array with JsonNode — The arena.
root field, JsonNodeId — The top-level node’s id, set by a parse. For an array or object it is the last node added, because children are added before their parent. Assign it when building.
add(node JsonNode) JsonNodeId — Append a node and return its id.
get(id JsonNodeId) JsonNode — The node; an id outside the arena panics.
rootKind() JsonKind — The kind of the root node.
getChild(parent JsonNodeId, key String) JsonNodeId JsonAccessError An object member’s id: notObject or missingKey.
getString(parent, key:) String JsonAccessError A string member; wrongType for another kind.
getInt(parent, key:) int(i64.min to i64.max) JsonAccessError A number member, truncated toward zero.
getBool(parent, key:) bool JsonAccessError A boolean member.
arrayLength(id JsonNodeId) int(0 to u64.max) JsonAccessError notArray for another kind.
arrayAt(id JsonNodeId, index) JsonNodeId JsonAccessError outOfBounds past the end.
Member Description
kind JsonKind
boolValue Set for jsonBool
numberValue Set for jsonNumber (a float)
stringValue Set for jsonString
children JsonNodeIdArray, for jsonArray and jsonObject
keys StringArray, parallel to children, for jsonObject
JsonNode.nullNode() A null
JsonNode.boolNode(value bool) A boolean
JsonNode.numberNode(value float) A number
JsonNode.stringNode(value String) A string
JsonNode.arrayNode(children JsonNodeIdArray) An array of already-added nodes
JsonNode.objectNode(keys StringArray, children JsonNodeIdArray) An object; keys[i] names children[i]
Enum Cases
JsonKind jsonNull, jsonBool, jsonNumber, jsonString, jsonArray, jsonObject
JsonError (from parse) unexpectedChar, unexpectedEof, invalidEscape, invalidNumber, invalidSurrogate, trailingContent
JsonAccessError (from accessors) notObject, notArray, missingKey, wrongType, outOfBounds

JsonNodeId is int(0 to u64.max) and JsonNodeIdArray is Array with JsonNodeId.

function main() returns ExitCode
let doc = try Json.parse("\{\"name\": \"maxon\", \"stars\": 42, \"tags\": [\"a\", \"b\"]\}") otherwise (e) 'bad'
print("invalid JSON: {e}\n")
return 1
end 'bad'
let name = try doc.getString(doc.root, key: "name") otherwise "?"
let stars = try doc.getInt(doc.root, key: "stars") otherwise 0
let tags = try doc.getChild(doc.root, key: "tags") otherwise 0
let second = try doc.arrayAt(tags, index: 1) otherwise 0
print("{doc.rootKind()} {name} {stars} {doc.get(second).stringValue}\n")
var built = JsonDoc.create()
var keys = StringArray.create()
var children = JsonNodeIdArray.create()
keys.push("ok")
children.push(built.add(JsonNode.boolNode(true)))
keys.push("score")
children.push(built.add(JsonNode.numberNode(1.5)))
built.root = built.add(JsonNode.objectNode(keys, children: children))
print("{Json.stringify(built)}\n")
return 0
end 'main'

Output: jsonObject maxon 42 b and {"ok":true,"score":1.5}.

Function Returns Description
sha256(data ByteArray) ByteArray The 32-byte SHA-256 digest (FIPS 180-4).
function main() returns ExitCode
let digest = sha256("abc".toByteArray())
var hex = ""
for b in digest 'each'
hex.append("{b:02x}")
end 'each'
print("{digest.count()} {hex}\n")
return 0
end 'main'

Output: 32 ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad.

Hasher is an incremental FNV-1a 64-bit hash, for content keys and cache digests. It is not cryptographic. Its result is a HashDigest, bits(64).

Member Returns Description
Hasher.create() Hasher A hasher at the FNV-1a offset basis.
Hasher.resume(state HashDigest) Hasher Continue from a value finalize() returned.
combine(value HashDigest) — Fold in one value.
combine(bytes ByteArray) — Fold in each byte.
finalize() HashDigest The running state, not post-processed, so resume(finalize()) continues exactly.
Hasher.empty() HashDigest The state of an empty fold.
Hasher.combined(state HashDigest, value HashDigest) HashDigest One step, as an expression.
Hasher.combined(state HashDigest, bytes ByteArray) HashDigest Each byte, as an expression.

FNV-1a folds a flat byte sequence and records no lengths, so "ab" then "c" hashes the same as "a" then "bc". When hashing a sequence of parts, combine each part’s length too.

function main() returns ExitCode
var hasher = Hasher.create()
hasher.combine("ab".toByteArray())
hasher.combine(7)
let direct = Hasher.combined(Hasher.combined(Hasher.empty(), bytes: "ab".toByteArray()), value: 7)
print("{hasher.finalize() == direct}\n")
return 0
end 'main'

Output: true.