///|
/// Input offsets use UTF-16 code units, matching MoonBit string locations.
#derive(Debug)
pub(all) suberror ValueInputError {
InvalidJson(Int32, String)
DuplicateJsonKey(Int32)
JsonInputLimit(Int32, String)
}
///|
/// The UTF-8 length of `text`. A surrogate pair counts 4 bytes, and a lone
/// surrogate counts 3, the length of its replacement character.
#internal(internal, "a helper of the smithy-validation packages")
pub fn utf8_length(text : StringView) -> Int32 {
let mut bytes = 0
for c in text.iter() {
let n = c.to_int()
bytes += if n < 0x80 {
1
} else if n < 0x800 {
2
} else if n < 0x10000 {
3
} else {
4
}
}
bytes
}
///|
priv struct JsonContainer {
object_ : Bool
keys : Set[String]
mut expect_key : Bool
}
///|
/// Parse one bounded JSON value, rejecting duplicate decoded object names.
pub fn parse_json_value(text : String) -> Json raise ValueInputError {
if utf8_length(text) > 16777216 {
raise JsonInputLimit(0, "I expected at most 16 MiB of UTF-8 JSON input.")
}
let stack : Array[JsonContainer] = []
let mut i = 0
while i < text.length() {
let c = text.code_unit_at(i).to_int()
match c {
34 => {
let start = i
i += 1
let mut closed = false
while i < text.length() {
let part = text.code_unit_at(i).to_int()
if part == 34 {
i += 1
closed = true
break
}
if part == 92 {
i += 2
} else {
i += 1
}
}
guard closed else {
raise InvalidJson(
start, "I expected a closing quote for the JSON string.",
)
}
if stack.last() is Some(frame) && frame.object_ && frame.expect_key {
let value = @json.parse(text[start:i].to_owned()) catch {
_ =>
raise InvalidJson(
start, "I expected a valid JSON object member name.",
)
}
guard value is String(name) else {
raise InvalidJson(start, "I expected a string object member name.")
}
if frame.keys.contains(name) {
raise DuplicateJsonKey(start)
}
frame.keys.add(name)
frame.expect_key = false
}
continue
}
123 | 91 => {
if stack.length() >= 128 {
raise JsonInputLimit(
i, "I expected JSON nesting depth no greater than 128.",
)
}
stack.push({ object_: c == 123, keys: Set([]), expect_key: c == 123, })
}
125 | 93 => {
guard stack.pop() is Some(frame) && frame.object_ == (c == 125) else {
raise InvalidJson(
i, "I expected a matching JSON container delimiter.",
)
}
}
44 =>
if stack.last() is Some(frame) && frame.object_ {
frame.expect_key = true
}
_ => ()
}
i += 1
}
@syntax.parse_json_value(text) catch {
error => {
let offset = match error {
@json.InvalidChar(position, _)
| @json.InvalidNumber(position, _)
| @json.InvalidIdentEscape(position)
// The parse keeps the last value of a duplicate key, so it does not raise this.
| @json.DuplicateKey(position, _) => {
let mut line = 1
let mut start = 0
for at = 0; at < text.length(); at = at + 1 {
if line == position.line {
break
}
if text.code_unit_at(at).to_int() == 10 {
line += 1
start = at + 1
}
}
start + position.column
}
@json.InvalidEof => text.length()
@json.DepthLimitExceeded => 0 // Preflight already enforces a tighter limit.
}
raise InvalidJson(
offset,
"I expected one well-formed JSON value: " + error.to_string(),
)
}
}
}