From ae44478b30d890fe0fb04022f44d474dcdcc3f9d Mon Sep 17 00:00:00 2001 From: Lassi Pulkkinen Date: Thu, 31 Oct 2024 03:11:21 +0200 Subject: Initial commit (import old repo) --- encoding/json/load.ha | 148 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 148 insertions(+) create mode 100644 encoding/json/load.ha (limited to 'encoding/json/load.ha') diff --git a/encoding/json/load.ha b/encoding/json/load.ha new file mode 100644 index 0000000..8dc2b56 --- /dev/null +++ b/encoding/json/load.ha @@ -0,0 +1,148 @@ +use memio; +use io; +use strings; +use types; + +// Options for [[load]]. +export type load_option = nestlimit; + +// The maximum number of nested objects or arrays that can be entered before +// erroring out. +export type nestlimit = uint; + +// Parses a JSON value from the given [[io::handle]], returning the value or an +// error. The return value is allocated on the heap; use [[finish]] to free it +// up when you're done using it. +// +// By default, this function assumes non-antagonistic inputs, and does not limit +// recursion depth or memory usage. You may want to set a custom [[nestlimit]], +// or incorporate an [[io::limitreader]] or similar. Alternatively, you can use +// the JSON lexer ([[lex]]) directly if dealing with potentially malicious +// inputs. +export fn load(src: io::handle, opts: load_option...) (value | error) = { + let limit = types::UINT_MAX; + for (let i = 0z; i < len(opts); i += 1) { + limit = opts[i]: nestlimit: uint; + }; + const lex = newlexer(src); + defer close(&lex); + return _load(&lex, 0, limit); +}; + +// Parses a JSON value from the given string, returning the value or an error. +// The return value is allocated on the heap; use [[finish]] to free it up when +// you're done using it. +// +// See the documentation for [[load]] for information on dealing with +// potentially malicious inputs. +export fn loadstr(input: str, opts: load_option...) (value | error) = { + let src = memio::fixed(strings::toutf8(input)); + return load(&src, opts...); +}; + +fn _load(lexer: *lexer, level: uint, limit: uint) (value | error) = { + const tok = mustscan(lexer)?; + match (tok) { + case _null => + return _null; + case let b: bool => + return b; + case let f: f64 => + return f; + case let s: str => + return strings::dup(s); + case arraystart => + if (level == limit) { + return limitreached; + }; + return _load_array(lexer, level + 1, limit); + case objstart => + if (level == limit) { + return limitreached; + }; + return _load_obj(lexer, level + 1, limit); + case (arrayend | objend | colon | comma) => + return lexer.loc: invalid; + }; +}; + +fn _load_array(lexer: *lexer, level: uint, limit: uint) (value | error) = { + let success = false; + let array: []value = []; + defer if (!success) finish(array); + let tok = mustscan(lexer)?; + match (tok) { + case arrayend => + success = true; + return array; + case => + unlex(lexer, tok); + }; + + for (true) { + append(array, _load(lexer, level, limit)?); + + tok = mustscan(lexer)?; + match (tok) { + case comma => void; + case arrayend => break; + case => + return lexer.loc: invalid; + }; + }; + success = true; + return array; +}; + +fn _load_obj(lexer: *lexer, level: uint, limit: uint) (value | error) = { + let success = false; + let obj = newobject(); + defer if (!success) finish(obj); + let tok = mustscan(lexer)?; + match (tok) { + case objend => + success = true; + return obj; + case => + unlex(lexer, tok); + }; + + for (true) { + let tok = mustscan(lexer)?; + const key = match (tok) { + case let s: str => + yield strings::dup(s); + case => + return lexer.loc: invalid; + }; + defer free(key); + + tok = mustscan(lexer)?; + if (!(tok is colon)) { + return lexer.loc: invalid; + }; + + put(&obj, key, _load(lexer, level, limit)?); + + tok = mustscan(lexer)?; + match (tok) { + case comma => void; + case objend => break; + case => + return lexer.loc: invalid; + }; + }; + + success = true; + return obj; +}; + +fn mustscan(lexer: *lexer) (token | error) = { + match (lex(lexer)?) { + case io::EOF => + lexer.loc.1 += 1; + return lexer.loc: invalid; + case let tok: token => + return tok; + }; +}; -- cgit v1.2.3