1pub mod factory;
13pub mod parser;
14
15pub use factory::{JSONFactory, Prop};
16pub use parser::JSONParser;
17
18use hermes_atom_table::AtomBytes;
19
20#[derive(Clone, Copy, PartialEq, Eq, Debug)]
22pub enum JSONKind {
23 Object,
25 Array,
27 String,
29 Number,
31 Boolean,
33 Null,
35}
36
37pub fn kind_to_string(kind: JSONKind) -> &'static str {
39 match kind {
40 JSONKind::Object => "Object",
41 JSONKind::Array => "Array",
42 JSONKind::String => "String",
43 JSONKind::Number => "Number",
44 JSONKind::Boolean => "Boolean",
45 JSONKind::Null => "Null",
46 }
47}
48
49pub struct JSONHiddenClass<'a> {
52 pub(crate) keys: &'a [AtomBytes],
53}
54
55impl<'a> JSONHiddenClass<'a> {
56 pub fn size(&self) -> usize {
58 self.keys.len()
59 }
60
61 pub fn keys(&self) -> &'a [AtomBytes] {
63 self.keys
64 }
65
66 pub fn find(&self, name: &[u8], atoms: &hermes_atom_table::AtomTable) -> Option<usize> {
69 self.keys
70 .binary_search_by(|k| atoms.bytes(*k).cmp(name))
71 .ok()
72 }
73}
74
75pub enum JSONValue<'a> {
79 Null,
81 Boolean(bool),
83 Number(f64),
86 String(AtomBytes),
88 Array(&'a [&'a JSONValue<'a>]),
90 Object(&'a JSONHiddenClass<'a>, &'a [&'a JSONValue<'a>]),
93}
94
95impl<'a> JSONValue<'a> {
96 pub fn kind(&self) -> JSONKind {
98 match self {
99 JSONValue::Null => JSONKind::Null,
100 JSONValue::Boolean(_) => JSONKind::Boolean,
101 JSONValue::Number(_) => JSONKind::Number,
102 JSONValue::String(_) => JSONKind::String,
103 JSONValue::Array(_) => JSONKind::Array,
104 JSONValue::Object(..) => JSONKind::Object,
105 }
106 }
107
108 pub fn as_number(&self) -> Option<f64> {
110 match self {
111 JSONValue::Number(n) => Some(*n),
112 _ => None,
113 }
114 }
115
116 pub fn as_boolean(&self) -> Option<bool> {
118 match self {
119 JSONValue::Boolean(b) => Some(*b),
120 _ => None,
121 }
122 }
123
124 pub fn as_string(&self) -> Option<AtomBytes> {
126 match self {
127 JSONValue::String(a) => Some(*a),
128 _ => None,
129 }
130 }
131
132 pub fn as_array(&self) -> Option<ArrayView<'a>> {
134 match self {
135 JSONValue::Array(v) => Some(ArrayView { values: v }),
136 _ => None,
137 }
138 }
139
140 pub fn as_object(&self) -> Option<ObjectView<'a>> {
142 match self {
143 JSONValue::Object(c, v) => Some(ObjectView { class: c, values: v }),
144 _ => None,
145 }
146 }
147
148 pub fn emit_into(
155 &self,
156 emitter: &mut hermes_support::json_emitter::JSONEmitter,
157 atoms: &hermes_atom_table::AtomTable,
158 ) {
159 match self {
160 JSONValue::Object(class, values) => {
161 emitter.open_dict();
162 for (k, v) in class.keys.iter().copied().zip(values.iter().copied()) {
163 let ku = crate::utf8::convert_utf8_with_surrogates_to_utf16(atoms.bytes(k));
164 emitter.emit_key_u16(&ku);
165 v.emit_into(emitter, atoms);
166 }
167 emitter.close_dict();
168 }
169 JSONValue::Array(values) => {
170 emitter.open_array();
171 for &v in values.iter() {
172 v.emit_into(emitter, atoms);
173 }
174 emitter.close_array();
175 }
176 JSONValue::String(a) => {
177 let vu = crate::utf8::convert_utf8_with_surrogates_to_utf16(atoms.bytes(*a));
178 emitter.emit_u16(&vu);
179 }
180 JSONValue::Number(n) => emitter.emit_f64(*n),
181 JSONValue::Boolean(b) => emitter.emit_bool(*b),
182 JSONValue::Null => emitter.emit_null_value(),
183 }
184 }
185}
186
187pub struct ArrayView<'a> {
189 values: &'a [&'a JSONValue<'a>],
190}
191
192impl<'a> ArrayView<'a> {
193 pub fn len(&self) -> usize {
195 self.values.len()
196 }
197
198 pub fn is_empty(&self) -> bool {
200 self.values.is_empty()
201 }
202
203 pub fn at(&self, pos: usize) -> &'a JSONValue<'a> {
205 self.values[pos]
206 }
207
208 pub fn iter(&self) -> impl Iterator<Item = &'a JSONValue<'a>> + '_ {
210 self.values.iter().copied()
211 }
212}
213
214pub struct ObjectView<'a> {
217 pub(crate) class: &'a JSONHiddenClass<'a>,
218 pub(crate) values: &'a [&'a JSONValue<'a>],
219}
220
221impl<'a> ObjectView<'a> {
222 pub fn size(&self) -> usize {
224 self.values.len()
225 }
226
227 pub fn get_hidden_class(&self) -> &'a JSONHiddenClass<'a> {
229 self.class
230 }
231
232 pub fn get(
234 &self,
235 name: &str,
236 atoms: &hermes_atom_table::AtomTable,
237 ) -> Option<&'a JSONValue<'a>> {
238 self.class.find(name.as_bytes(), atoms).map(|i| self.values[i])
239 }
240
241 pub fn at(&self, name: &str, atoms: &hermes_atom_table::AtomTable) -> &'a JSONValue<'a> {
243 self.get(name, atoms).expect("name not found")
244 }
245
246 pub fn count(&self, name: &str, atoms: &hermes_atom_table::AtomTable) -> usize {
248 if self.class.find(name.as_bytes(), atoms).is_some() {
249 1
250 } else {
251 0
252 }
253 }
254
255 pub fn value_at(&self, index: usize) -> &'a JSONValue<'a> {
257 self.values[index]
258 }
259
260 pub fn key_at(&self, index: usize) -> hermes_atom_table::AtomBytes {
262 self.class.keys[index]
263 }
264
265 pub fn find(&self, name: &str, atoms: &hermes_atom_table::AtomTable) -> Option<usize> {
269 self.class.find(name.as_bytes(), atoms)
270 }
271
272 pub fn iter(
274 &self,
275 ) -> impl Iterator<Item = (hermes_atom_table::AtomBytes, &'a JSONValue<'a>)> + '_ {
276 self.class.keys.iter().copied().zip(self.values.iter().copied())
277 }
278}
279
280pub struct JSONSharedValue {
290 value: *const JSONValue<'static>,
293 #[allow(dead_code)] allocator: std::rc::Rc<bumpalo::Bump>,
296}
297
298impl JSONSharedValue {
299 pub fn new(value: &JSONValue<'_>, allocator: std::rc::Rc<bumpalo::Bump>) -> JSONSharedValue {
302 let value: *const JSONValue<'static> =
306 (value as *const JSONValue<'_>).cast::<JSONValue<'static>>();
307 JSONSharedValue { value, allocator }
308 }
309
310 pub fn get(&self) -> &JSONValue<'_> {
312 #[allow(unsafe_code)] unsafe { &*self.value }
318 }
319}
320
321#[cfg(test)]
322mod model_tests {
323 use super::*;
324 use bumpalo::Bump;
325
326 #[test]
327 fn kinds_and_scalar_accessors() {
328 let arena = Bump::new();
329 let n: &JSONValue = arena.alloc(JSONValue::Number(1.5));
330 let b: &JSONValue = arena.alloc(JSONValue::Boolean(true));
331 assert_eq!(n.kind(), JSONKind::Number);
332 assert_eq!(b.kind(), JSONKind::Boolean);
333 assert_eq!(n.as_number(), Some(1.5));
334 assert_eq!(b.as_boolean(), Some(true));
335 assert_eq!(n.as_boolean(), None);
336 assert_eq!(JSONValue::Null.kind(), JSONKind::Null);
337 assert_eq!(kind_to_string(JSONKind::Array), "Array");
338 }
339
340 #[test]
341 fn array_accessors() {
342 let arena = Bump::new();
343 let a = arena.alloc(JSONValue::Number(10.0));
344 let b = arena.alloc(JSONValue::Number(20.0));
345 let elems: &[&JSONValue] = arena.alloc_slice_copy(&[&*a, &*b]);
346 let arr = arena.alloc(JSONValue::Array(elems));
347 let view = arr.as_array().unwrap();
348 assert_eq!(view.len(), 2);
349 assert_eq!(view.at(0).as_number(), Some(10.0));
350 assert_eq!(view.iter().count(), 2);
351 }
352
353 #[test]
354 fn kind_to_string_all_variants() {
355 use JSONKind::*;
356 let pairs = [
357 (Object, "Object"),
358 (Array, "Array"),
359 (String, "String"),
360 (Number, "Number"),
361 (Boolean, "Boolean"),
362 (Null, "Null"),
363 ];
364 for (k, s) in pairs {
365 assert_eq!(kind_to_string(k), s);
366 }
367 }
368
369 #[test]
370 fn emit_into_round_trip() {
371 use super::JSONFactory;
372 use bumpalo::Bump;
373 use hermes_atom_table::AtomTable;
374 use hermes_support::json_emitter::JSONEmitter;
375
376 let arena = Bump::new();
377 let atoms = AtomTable::new();
378 let f = JSONFactory::new(&arena, &atoms);
379
380 let nested = {
382 let p = (f.get_string_str("nested1"), f.get_boolean(true));
383 f.new_object(&mut [p]).unwrap()
384 };
385 let arr = f.new_array(&[f.get_boolean(false), f.get_null(), f.get_string_str("value2")]);
386 let obj = f.new_object(&mut [
387 (f.get_string_str("key1"), f.get_number(1.0)),
388 (f.get_string_str("key2"), f.get_string_str("value2")),
389 (f.get_string_str("key3"), nested),
390 (f.get_string_str("key4"), arr),
391 ]).unwrap();
392
393 let mut s = String::new();
394 {
395 let mut e = JSONEmitter::new(&mut s, false);
396 obj.emit_into(&mut e, &atoms);
397 }
398 assert_eq!(s, r#"{"key1":1,"key2":"value2","key3":{"nested1":true},"key4":[false,null,"value2"]}"#);
400 }
401
402 #[test]
403 fn emit_into_astral_string() {
404 use super::JSONFactory;
405 use bumpalo::Bump;
406 use hermes_atom_table::AtomTable;
407 use hermes_support::json_emitter::JSONEmitter;
408 let arena = Bump::new();
409 let atoms = AtomTable::new();
410 let f = JSONFactory::new(&arena, &atoms);
411 let s = f.get_string_str("\u{10000}");
416 let mut out = String::new();
417 { let mut e = JSONEmitter::new(&mut out, false); s.emit_into(&mut e, &atoms); }
418 assert_eq!(out, "\"\\ud800\\udc00\"");
419 }
420
421 #[test]
422 fn shared_value_outlives_parse() {
423 use std::rc::Rc;
424 use bumpalo::Bump;
425 let shared: JSONSharedValue = {
427 let arena = Rc::new(Bump::new());
428 let v: &JSONValue = arena.alloc(JSONValue::Number(3.5));
429 JSONSharedValue::new(v, arena.clone())
430 };
431 assert_eq!(shared.get().as_number(), Some(3.5));
432 }
433
434 #[test]
435 fn string_accessor_and_hidden_class_find() {
436 use hermes_atom_table::AtomTable;
437 let arena = Bump::new();
438 let atoms = AtomTable::new();
439 let a = atoms.atom_bytes("foo");
440 let s = arena.alloc(JSONValue::String(a));
441 assert_eq!(s.as_string(), Some(a));
442 assert_eq!(s.as_number(), None);
443
444 let ka = atoms.atom_bytes("a");
446 let kb = atoms.atom_bytes("b");
447 let kc = atoms.atom_bytes("c");
448 let keys: &[hermes_atom_table::AtomBytes] = arena.alloc_slice_copy(&[ka, kb, kc]);
449 let hc = JSONHiddenClass { keys };
450 assert_eq!(hc.find(b"a", &atoms), Some(0));
451 assert_eq!(hc.find(b"b", &atoms), Some(1));
452 assert_eq!(hc.find(b"c", &atoms), Some(2));
453 assert_eq!(hc.find(b"z", &atoms), None);
454 }
455
456 #[test]
457 fn object_find_index() {
458 use super::JSONFactory;
459 use bumpalo::Bump;
460 use hermes_atom_table::AtomTable;
461 let arena = Bump::new();
462 let atoms = AtomTable::new();
463 let f = JSONFactory::new(&arena, &atoms);
464 let obj = f
465 .new_object(&mut [
466 (f.get_string_str("b"), f.get_number(2.0)),
467 (f.get_string_str("a"), f.get_number(1.0)),
468 ])
469 .unwrap();
470 let o = obj.as_object().unwrap();
471 assert_eq!(o.find("a", &atoms), Some(0));
473 assert_eq!(o.find("b", &atoms), Some(1));
474 assert_eq!(o.find("zzz", &atoms), None);
475 assert_eq!(o.value_at(o.find("a", &atoms).unwrap()).as_number(), Some(1.0));
477 }
478}