Skip to main content

df_hyperliquid_core/utils/json/
parse.rs

1//! Reading JSON text: keys kept in the order written, numbers kept as written, and a mistake placed
2//! by line and column.
3
4use crate::utils::json::Json;
5
6/// Where the text stopped being JSON, counted from one as an editor counts.
7#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
8#[error("line {line}, column {column}: {message}")]
9pub struct BadJson {
10    pub line: usize,
11    pub column: usize,
12    pub message: String,
13}
14
15impl Json {
16    pub fn parse(text: &str) -> Result<Json, BadJson> {
17        let mut reader = Reader { text: text.as_bytes(), at: 0, source: text };
18        reader.space();
19        let value = reader.value(0)?;
20        reader.space();
21        if reader.at < reader.text.len() {
22            return Err(reader.fail("more after the value ends"));
23        }
24        Ok(value)
25    }
26}
27
28/// Nesting deeper than this is refused rather than followed off the stack.
29const DEEPEST: usize = 128;
30
31struct Reader<'a> {
32    text: &'a [u8],
33    at: usize,
34    source: &'a str,
35}
36
37impl Reader<'_> {
38    fn fail(&self, message: &str) -> BadJson {
39        let before = self.source.get(..self.at).unwrap_or(self.source);
40        let line = before.matches('\n').count() + 1;
41        let column = before.rsplit('\n').next().map_or(0, |tail| tail.chars().count()) + 1;
42        BadJson { line, column, message: message.to_owned() }
43    }
44
45    fn peek(&self) -> Option<u8> {
46        self.text.get(self.at).copied()
47    }
48
49    fn space(&mut self) {
50        while matches!(self.peek(), Some(b' ' | b'\n' | b'\r' | b'\t')) {
51            self.at += 1;
52        }
53    }
54
55    fn expect(&mut self, word: &str) -> Result<(), BadJson> {
56        match self.text[self.at..].starts_with(word.as_bytes()) {
57            true => {
58                self.at += word.len();
59                Ok(())
60            }
61            false => Err(self.fail(&format!("expected {word}"))),
62        }
63    }
64
65    fn value(&mut self, depth: usize) -> Result<Json, BadJson> {
66        if depth > DEEPEST {
67            return Err(self.fail("nested too deep"));
68        }
69        match self.peek() {
70            None => Err(self.fail("the text ends where a value should be")),
71            Some(b'n') => self.expect("null").map(|()| Json::Null),
72            Some(b't') => self.expect("true").map(|()| Json::Bool(true)),
73            Some(b'f') => self.expect("false").map(|()| Json::Bool(false)),
74            Some(b'"') => self.string().map(Json::String),
75            Some(b'[') => self.array(depth),
76            Some(b'{') => self.object(depth),
77            Some(b'-' | b'0'..=b'9') => self.number(),
78            Some(_) => Err(self.fail("not the start of a value")),
79        }
80    }
81
82    fn array(&mut self, depth: usize) -> Result<Json, BadJson> {
83        self.at += 1;
84        let mut items = Vec::new();
85        self.space();
86        if self.peek() == Some(b']') {
87            self.at += 1;
88            return Ok(Json::Array(items));
89        }
90        loop {
91            self.space();
92            items.push(self.value(depth + 1)?);
93            self.space();
94            match self.peek() {
95                Some(b',') => self.at += 1,
96                Some(b']') => {
97                    self.at += 1;
98                    return Ok(Json::Array(items));
99                }
100                _ => return Err(self.fail("expected , or ] in an array")),
101            }
102        }
103    }
104
105    fn object(&mut self, depth: usize) -> Result<Json, BadJson> {
106        self.at += 1;
107        let mut entries: Vec<(String, Json)> = Vec::new();
108        self.space();
109        if self.peek() == Some(b'}') {
110            self.at += 1;
111            return Ok(Json::Object(entries));
112        }
113        loop {
114            self.space();
115            if self.peek() != Some(b'"') {
116                return Err(self.fail("expected a key in quotes"));
117            }
118            let key = self.string()?;
119            if entries.iter().any(|(seen, _)| *seen == key) {
120                return Err(self.fail(&format!("the key {key:?} is written twice")));
121            }
122            self.space();
123            if self.peek() != Some(b':') {
124                return Err(self.fail("expected : after a key"));
125            }
126            self.at += 1;
127            self.space();
128            let value = self.value(depth + 1)?;
129            entries.push((key, value));
130            self.space();
131            match self.peek() {
132                Some(b',') => self.at += 1,
133                Some(b'}') => {
134                    self.at += 1;
135                    return Ok(Json::Object(entries));
136                }
137                _ => return Err(self.fail("expected , or } in an object")),
138            }
139        }
140    }
141
142    fn number(&mut self) -> Result<Json, BadJson> {
143        let start = self.at;
144        if self.peek() == Some(b'-') {
145            self.at += 1;
146        }
147        let digits = |reader: &mut Self| {
148            let from = reader.at;
149            while matches!(reader.peek(), Some(b'0'..=b'9')) {
150                reader.at += 1;
151            }
152            reader.at - from
153        };
154        let whole = self.at;
155        if digits(self) == 0 {
156            return Err(self.fail("a number needs digits"));
157        }
158        if self.text[whole] == b'0' && self.at - whole > 1 {
159            return Err(self.fail("a number does not start with 0"));
160        }
161        if self.peek() == Some(b'.') {
162            self.at += 1;
163            if digits(self) == 0 {
164                return Err(self.fail("digits must follow the point"));
165            }
166        }
167        if matches!(self.peek(), Some(b'e' | b'E')) {
168            self.at += 1;
169            if matches!(self.peek(), Some(b'+' | b'-')) {
170                self.at += 1;
171            }
172            if digits(self) == 0 {
173                return Err(self.fail("digits must follow the exponent"));
174            }
175        }
176        Ok(Json::Number(self.source[start..self.at].to_owned()))
177    }
178
179    fn string(&mut self) -> Result<String, BadJson> {
180        self.at += 1;
181        let mut out = String::new();
182        loop {
183            let start = self.at;
184            while !matches!(self.peek(), None | Some(b'"' | b'\\')) {
185                if self.text[self.at] < 0x20 {
186                    return Err(self.fail("a raw control character inside a string"));
187                }
188                self.at += 1;
189            }
190            out.push_str(&self.source[start..self.at]);
191            match self.peek() {
192                None => return Err(self.fail("a string that never closes")),
193                Some(b'"') => {
194                    self.at += 1;
195                    return Ok(out);
196                }
197                _ => {
198                    self.at += 1;
199                    let escaped = self.peek().ok_or_else(|| self.fail("a string that never closes"))?;
200                    self.at += 1;
201                    match escaped {
202                        b'"' => out.push('"'),
203                        b'\\' => out.push('\\'),
204                        b'/' => out.push('/'),
205                        b'b' => out.push('\u{08}'),
206                        b'f' => out.push('\u{0c}'),
207                        b'n' => out.push('\n'),
208                        b'r' => out.push('\r'),
209                        b't' => out.push('\t'),
210                        b'u' => out.push(self.unicode()?),
211                        _ => return Err(self.fail("not an escape JSON knows")),
212                    }
213                }
214            }
215        }
216    }
217
218    fn unicode(&mut self) -> Result<char, BadJson> {
219        let first = self.four()?;
220        if (0xd800..0xdc00).contains(&first) {
221            if !self.text[self.at..].starts_with(b"\\u") {
222                return Err(self.fail("half of a surrogate pair"));
223            }
224            self.at += 2;
225            let second = self.four()?;
226            if !(0xdc00..0xe000).contains(&second) {
227                return Err(self.fail("half of a surrogate pair"));
228            }
229            let joined = 0x10000 + ((first - 0xd800) << 10) + (second - 0xdc00);
230            return char::from_u32(joined).ok_or_else(|| self.fail("not a character"));
231        }
232        char::from_u32(first).ok_or_else(|| self.fail("half of a surrogate pair"))
233    }
234
235    fn four(&mut self) -> Result<u32, BadJson> {
236        let digits = self.source.get(self.at..self.at + 4).ok_or_else(|| self.fail("\\u needs four hex digits"))?;
237        let value = u32::from_str_radix(digits, 16).map_err(|_| self.fail("\\u needs four hex digits"))?;
238        self.at += 4;
239        Ok(value)
240    }
241}
242
243#[cfg(test)]
244#[path = "tests/parse.rs"]
245mod tests;