streamdal_gjson/
valid.rs

1// Copyright 2021 Joshua J Baker. All rights reserved.
2// Use of this source code is governed by an MIT-style
3// license that can be found in the LICENSE file.
4
5// Bit flags passed to the "info" parameter of the iter function which
6// provides additional information about the data
7
8const SPACE: u8 = 1 << 1;
9const STRING: u8 = 1 << 2;
10
11static TABLE: [u8; 256] = {
12    let mut table = [0; 256];
13    table[b'\t' as usize] |= SPACE;
14    table[b'\n' as usize] |= SPACE;
15    table[b'\r' as usize] |= SPACE;
16    table[b' ' as usize] |= SPACE;
17
18    table[0x00] |= STRING;
19    table[0x01] |= STRING;
20    table[0x02] |= STRING;
21    table[0x03] |= STRING;
22    table[0x04] |= STRING;
23    table[0x05] |= STRING;
24    table[0x06] |= STRING;
25    table[0x07] |= STRING;
26    table[0x08] |= STRING;
27    table[0x09] |= STRING;
28    table[0x0A] |= STRING;
29    table[0x0B] |= STRING;
30    table[0x0C] |= STRING;
31    table[0x0D] |= STRING;
32    table[0x0E] |= STRING;
33    table[0x0F] |= STRING;
34    table[0x10] |= STRING;
35    table[0x11] |= STRING;
36    table[0x12] |= STRING;
37    table[0x13] |= STRING;
38    table[0x14] |= STRING;
39    table[0x15] |= STRING;
40    table[0x16] |= STRING;
41    table[0x17] |= STRING;
42    table[0x18] |= STRING;
43    table[0x19] |= STRING;
44    table[0x1A] |= STRING;
45    table[0x1B] |= STRING;
46    table[0x1C] |= STRING;
47    table[0x1D] |= STRING;
48    table[0x1E] |= STRING;
49    table[0x1F] |= STRING;
50    table[b'"' as usize] |= STRING;
51    table[b'\\' as usize] |= STRING;
52
53    table
54};
55
56fn isspace(c: u8) -> bool {
57    TABLE[c as usize] & SPACE == SPACE
58}
59
60/// Returns true if the input is valid json.
61///
62/// ```
63/// if !gjson::valid(json) {
64/// 	return Err("invalid json");
65/// }
66/// let value = gjson::get(json, "name.last");
67/// ```
68pub fn valid(json: &str) -> bool {
69    let json = json.as_bytes();
70    let mut i = 0;
71    let (valid, next_i) = valid_any(json, i);
72    if !valid {
73        return false;
74    }
75    i = next_i;
76    while i < json.len() {
77        if !isspace(json[i]) {
78            return false;
79        }
80        i += 1;
81    }
82    true
83}
84
85fn valid_any(json: &[u8], mut i: usize) -> (bool, usize) {
86    while i < json.len() {
87        if isspace(json[i]) {
88            i += 1;
89            continue;
90        }
91        return match json[i] {
92            b'{' => valid_object(json, i),
93            b'[' => valid_array(json, i),
94            b'"' => valid_string(json, i),
95            b't' => valid_true(json, i),
96            b'f' => valid_false(json, i),
97            b'n' => valid_null(json, i),
98            _ => {
99                if json[i] == b'-' || (json[i] >= b'0' && json[i] <= b'9') {
100                    valid_number(json, i)
101                } else {
102                    break;
103                }
104            }
105        };
106    }
107    (false, i)
108}
109
110fn strip_ws(json: &[u8], mut i: usize) -> usize {
111    loop {
112        if i + 16 < json.len() {
113            for ch in &json[i..i + 16] {
114                if TABLE[*ch as usize] & SPACE != SPACE {
115                    return i;
116                }
117                i += 1;
118            }
119        }
120        while i < json.len() {
121            if TABLE[json[i] as usize] & SPACE != SPACE {
122                return i;
123            }
124            i += 1;
125        }
126        return i;
127    }
128}
129
130fn valid_object(json: &[u8], mut i: usize) -> (bool, usize) {
131    i = strip_ws(json, i + 1);
132    if i == json.len() {
133        return (false, i);
134    }
135    if json[i] == b'}' {
136        return (true, i + 1);
137    }
138    loop {
139        if json[i] != b'"' {
140            return (false, i);
141        }
142        let (valid, next_i) = valid_string(json, i);
143        if !valid {
144            return (false, i);
145        }
146        i = next_i;
147        i = strip_ws(json, i);
148        if i == json.len() {
149            return (false, i);
150        }
151        if json[i] != b':' {
152            return (false, i);
153        }
154        let (valid, next_i) = valid_any(json, i + 1);
155        if !valid {
156            return (false, i);
157        }
158        i = next_i;
159        i = strip_ws(json, i);
160        if i == json.len() {
161            return (false, i);
162        }
163        if json[i] == b'}' {
164            return (true, i + 1);
165        }
166        if json[i] != b',' {
167            return (false, i);
168        }
169        i = strip_ws(json, i + 1);
170        if i == json.len() {
171            return (false, i);
172        }
173    }
174}
175
176fn valid_array(json: &[u8], mut i: usize) -> (bool, usize) {
177    i = strip_ws(json, i + 1);
178    if i == json.len() {
179        return (false, i);
180    }
181    if json[i] == b']' {
182        return (true, i + 1);
183    }
184    loop {
185        let (valid, next_i) = valid_any(json, i);
186        if !valid {
187            return (false, i);
188        }
189        i = next_i;
190        i = strip_ws(json, i);
191        if i == json.len() {
192            return (false, i);
193        }
194        if json[i] == b']' {
195            return (true, i + 1);
196        }
197        if json[i] != b',' {
198            return (false, i);
199        }
200        i += 1;
201    }
202}
203
204fn ishexdigit(c: u8) -> bool {
205    (c >= b'0' && c <= b'9') || (c >= b'a' && c <= b'f') || (c >= b'A' && c <= b'F')
206}
207
208fn valid_string(json: &[u8], mut i: usize) -> (bool, usize) {
209    i += 1;
210    loop {
211        let mut ch: u8;
212        'tok: loop {
213            if i + 32 < json.len() {
214                for c in &json[i..i + 32] {
215                    ch = *c;
216                    if TABLE[ch as usize] & STRING == STRING {
217                        break 'tok;
218                    }
219                    i += 1;
220                }
221            }
222            while i < json.len() {
223                ch = json[i];
224                if TABLE[ch as usize] & STRING == STRING {
225                    break 'tok;
226                }
227                i += 1;
228            }
229            return (false, i);
230        }
231        if json[i] < b' ' {
232            return (false, i);
233        }
234        if json[i] == b'"' {
235            return (true, i + 1);
236        }
237        if json[i] == b'\\' {
238            i += 1;
239            if i == json.len() {
240                return (false, i);
241            }
242            match json[i] {
243                b'"' | b'\\' | b'/' | b'b' | b'f' | b'n' | b'r' | b't' => {}
244                b'u' => {
245                    for _ in 0..4 {
246                        i += 1;
247                        if i == json.len() {
248                            return (false, i);
249                        }
250                        if !ishexdigit(json[i]) {
251                            return (false, i);
252                        }
253                    }
254                }
255                _ => return (false, i),
256            }
257        }
258        i += 1;
259    }
260}
261
262fn valid_number(json: &[u8], mut i: usize) -> (bool, usize) {
263    // sign
264    if json[i] == b'-' {
265        i += 1;
266		if i == json.len() {
267			return (false,i);
268		}
269		if json[i] < b'0' || json[i] > b'9' {
270			return (false, i);
271		}
272    }
273    // int
274    if i == json.len() {
275        return (false, i);
276    }
277    if json[i] == b'0' {
278        i += 1;
279    } else {
280        while i < json.len() {
281            if json[i] >= b'0' && json[i] <= b'9' {
282                i += 1;
283                continue;
284            }
285            break;
286        }
287    }
288    // frac
289    if i == json.len() {
290        return (true, i);
291    }
292    if json[i] == b'.' {
293        i += 1;
294        if i == json.len() {
295            return (false, i);
296        }
297        if json[i] < b'0' || json[i] > b'9' {
298            return (false, i);
299        }
300        i += 1;
301        while i < json.len() {
302            if json[i] >= b'0' && json[i] <= b'9' {
303                i += 1;
304                continue;
305            }
306            break;
307        }
308    }
309    // exp
310    if i == json.len() {
311        return (true, i);
312    }
313    if json[i] == b'e' || json[i] == b'E' {
314        i += 1;
315        if i == json.len() {
316            return (false, i);
317        }
318        if json[i] == b'+' || json[i] == b'-' {
319            i += 1;
320        }
321        if i == json.len() {
322            return (false, i);
323        }
324        if json[i] < b'0' || json[i] > b'9' {
325            return (false, i);
326        }
327        i += 1;
328        while i < json.len() {
329            if json[i] >= b'0' && json[i] <= b'9' {
330                i += 1;
331                continue;
332            }
333            break;
334        }
335    }
336    return (true, i);
337}
338
339fn valid_true(json: &[u8], i: usize) -> (bool, usize) {
340    if i + 4 <= json.len() && json[i..i + 4].eq("true".as_bytes()) {
341        (true, i + 4)
342    } else {
343        (false, i)
344    }
345}
346
347fn valid_false(json: &[u8], i: usize) -> (bool, usize) {
348    if i + 5 <= json.len() && json[i..i + 5].eq("false".as_bytes()) {
349        (true, i + 5)
350    } else {
351        (false, i)
352    }
353}
354fn valid_null(json: &[u8], i: usize) -> (bool, usize) {
355    if i + 4 <= json.len() && json[i..i + 4].eq("null".as_bytes()) {
356        (true, i + 4)
357    } else {
358        (false, i)
359    }
360}
361
362#[cfg(test)]
363mod test {
364    use super::valid;
365
366    #[test]
367    fn basic() {
368        assert_eq!(valid("0"), true);
369        assert_eq!(valid("00"), false);
370        assert_eq!(valid("-00"), false);
371        assert_eq!(valid("-."), false);
372        assert_eq!(valid("-.123"), false);
373        assert_eq!(valid("0.0"), true);
374        assert_eq!(valid("10.0"), true);
375        assert_eq!(valid("10e1"), true);
376        assert_eq!(valid("10EE"), false);
377        assert_eq!(valid("10E-"), false);
378        assert_eq!(valid("10E+"), false);
379        assert_eq!(valid("10E123"), true);
380        assert_eq!(valid("10E-123"), true);
381        assert_eq!(valid("10E-0123"), true);
382        assert_eq!(valid(""), false);
383        assert_eq!(valid(" "), false);
384        assert_eq!(valid("{}"), true);
385        assert_eq!(valid("{"), false);
386        assert_eq!(valid("-"), false);
387        assert_eq!(valid("-1"), true);
388        assert_eq!(valid("-1."), false);
389        assert_eq!(valid("-1.0"), true);
390        assert_eq!(valid(" -1.0"), true);
391        assert_eq!(valid(" -1.0 "), true);
392        assert_eq!(valid("-1.0 "), true);
393        assert_eq!(valid("-1.0 i"), false);
394        assert_eq!(valid("-1.0 i"), false);
395        assert_eq!(valid("true"), true);
396        assert_eq!(valid(" true"), true);
397        assert_eq!(valid(" true "), true);
398        assert_eq!(valid(" True "), false);
399        assert_eq!(valid(" tru"), false);
400        assert_eq!(valid("false"), true);
401        assert_eq!(valid(" false"), true);
402        assert_eq!(valid(" false "), true);
403        assert_eq!(valid(" False "), false);
404        assert_eq!(valid(" fals"), false);
405        assert_eq!(valid("null"), true);
406        assert_eq!(valid(" null"), true);
407        assert_eq!(valid(" null "), true);
408        assert_eq!(valid(" Null "), false);
409        assert_eq!(valid(" nul"), false);
410        assert_eq!(valid(" []"), true);
411        assert_eq!(valid(" [true]"), true);
412        assert_eq!(valid(" [ true, null ]"), true);
413        assert_eq!(valid(" [ true,]"), false);
414        assert_eq!(valid(r#"{"hello":"world"}"#), true);
415        assert_eq!(valid(r#"{ "hello": "world" }"#), true);
416        assert_eq!(valid(r#"{ "hello": "world", }"#), false);
417        assert_eq!(valid(r#"{"a":"b",}"#), false);
418        assert_eq!(valid(r#"{"a":"b","a"}"#), false);
419        assert_eq!(valid(r#"{"a":"b","a":}"#), false);
420        assert_eq!(valid(r#"{"a":"b","a":1}"#), true);
421        assert_eq!(valid(r#"{"a":"b",2"1":2}"#), false);
422        assert_eq!(valid(r#"{"a":"b","a": 1, "c":{"hi":"there"} }"#), true);
423        assert_eq!(
424            valid(r#"{"a":"b","a": 1, "c":{"hi":"there", "easy":["going",{"mixed":"bag"}]} }"#),
425            true
426        );
427        assert_eq!(valid(r#""""#), true);
428        assert_eq!(valid(r#"""#), false);
429        assert_eq!(valid(r#""\n""#), true);
430        assert_eq!(valid(r#""\""#), false);
431        assert_eq!(valid(r#""\\""#), true);
432        assert_eq!(valid(r#""a\\b""#), true);
433        assert_eq!(valid(r#""a\\b\\\"a""#), true);
434        assert_eq!(valid(r#""a\\b\\\uFFAAa""#), true);
435        assert_eq!(valid(r#""a\\b\\\uFFAZa""#), false);
436        assert_eq!(valid(r#""a\\b\\\uFFA""#), false);
437        assert_eq!(valid(r#""a\\b\\\uFFAZa""#), false);
438        assert_eq!(valid(r#""#), false);
439        assert_eq!(valid("[-]"), false);
440        assert_eq!(valid("[-.123]"), false);
441    }
442
443    #[test]
444    fn xcover() {
445        // code coverage
446        assert_eq!(valid(r#"{"hel\lo":"world"}"#), false);
447        assert_eq!(valid(r#"{"hello"  "#), false);
448        assert_eq!(valid(r#"{"hello"  : true "#), false);
449        assert_eq!(valid(r#"{"hello"  : true x"#), false);
450        assert_eq!(valid(r#"{"hello"  : true , "#), false);
451        assert_eq!(valid(r#"[  "#), false);
452        assert_eq!(valid(r#"[ true "#), false);
453        assert_eq!(valid(r#"[ true x "#), false);
454        assert_eq!(valid(r#"[ true , "#), false);
455
456        assert_eq!(valid("[ \"hel\u{0}\" ]"), false);
457        assert_eq!(valid(r#"[ "hel\"#), false);
458        assert_eq!(valid(r#"[ "hel\u"#), false);
459
460        assert_eq!(valid(r#"[ 123.x ]"#), false);
461        assert_eq!(valid(r#"[ 123.0e"#), false);
462        assert_eq!(valid(r#"[ 123.0e1f"#), false);
463    }
464}