1const 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
60pub 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 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 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 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 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 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}