1use std::cell::RefCell;
11use std::collections::HashMap;
12
13use bumpalo::Bump;
14use hermes_atom_table::{AtomBytes, AtomTable};
15
16use super::{JSONHiddenClass, JSONValue};
17
18pub type Prop<'a> = (&'a JSONValue<'a>, &'a JSONValue<'a>);
20
21pub struct JSONFactory<'a> {
27 arena: &'a Bump,
28 atoms: &'a AtomTable,
29 strings: RefCell<HashMap<AtomBytes, &'a JSONValue<'a>>>,
30 numbers: RefCell<HashMap<u64, &'a JSONValue<'a>>>,
31 classes: RefCell<HashMap<Box<[AtomBytes]>, &'a JSONHiddenClass<'a>>>,
32 null_v: &'a JSONValue<'a>,
33 true_v: &'a JSONValue<'a>,
34 false_v: &'a JSONValue<'a>,
35}
36
37impl<'a> JSONFactory<'a> {
38 pub fn new(arena: &'a Bump, atoms: &'a AtomTable) -> JSONFactory<'a> {
40 JSONFactory {
41 arena,
42 atoms,
43 strings: RefCell::new(HashMap::new()),
44 numbers: RefCell::new(HashMap::new()),
45 classes: RefCell::new(HashMap::new()),
46 null_v: arena.alloc(JSONValue::Null),
47 true_v: arena.alloc(JSONValue::Boolean(true)),
48 false_v: arena.alloc(JSONValue::Boolean(false)),
49 }
50 }
51
52 pub fn arena(&self) -> &'a Bump {
54 self.arena
55 }
56
57 pub fn atoms(&self) -> &'a AtomTable {
59 self.atoms
60 }
61
62 pub fn get_null(&self) -> &'a JSONValue<'a> {
64 self.null_v
65 }
66
67 pub fn get_boolean(&self, v: bool) -> &'a JSONValue<'a> {
69 if v {
70 self.true_v
71 } else {
72 self.false_v
73 }
74 }
75
76 pub fn get_string(&self, lit: AtomBytes) -> &'a JSONValue<'a> {
78 if let Some(found) = self.strings.borrow().get(&lit) {
79 return found;
80 }
81 let node: &'a JSONValue<'a> = self.arena.alloc(JSONValue::String(lit));
82 self.strings.borrow_mut().insert(lit, node);
83 node
84 }
85
86 pub fn get_string_str(&self, s: &str) -> &'a JSONValue<'a> {
88 self.get_string(self.atoms.atom_bytes(s))
89 }
90
91 pub fn get_number(&self, value: f64) -> &'a JSONValue<'a> {
94 let bits = value.to_bits();
95 if let Some(found) = self.numbers.borrow().get(&bits) {
96 return found;
97 }
98 let node: &'a JSONValue<'a> = self.arena.alloc(JSONValue::Number(value));
99 self.numbers.borrow_mut().insert(bits, node);
100 node
101 }
102
103 fn key_bytes(&self, key: &'a JSONValue<'a>) -> AtomBytes {
104 key.as_string().expect("object key must be a JSON string")
105 }
106
107 pub fn sort_props(&self, props: &mut [Prop<'a>]) -> Option<AtomBytes> {
110 props.sort_by(|a, b| {
111 self.atoms
112 .bytes(self.key_bytes(a.0))
113 .cmp(self.atoms.bytes(self.key_bytes(b.0)))
114 });
115 let mut last: Option<AtomBytes> = None;
116 for p in props.iter() {
117 let kb = self.key_bytes(p.0);
118 if last == Some(kb) {
119 return Some(kb);
120 }
121 last = Some(kb);
122 }
123 None
124 }
125
126 pub fn get_hidden_class(&self, keys: &[AtomBytes]) -> &'a JSONHiddenClass<'a> {
129 if let Some(found) = self.classes.borrow().get(keys) {
130 return found;
131 }
132 let arena_keys: &'a [AtomBytes] = self.arena.alloc_slice_copy(keys);
133 let cls: &'a JSONHiddenClass<'a> =
134 self.arena.alloc(JSONHiddenClass { keys: arena_keys });
135 self.classes.borrow_mut().insert(keys.into(), cls);
136 cls
137 }
138
139 pub fn new_object(&self, props: &mut [Prop<'a>]) -> Option<&'a JSONValue<'a>> {
142 if self.sort_props(props).is_some() {
143 return None;
144 }
145 let keys: Vec<AtomBytes> = props.iter().map(|p| self.key_bytes(p.0)).collect();
146 let cls = self.get_hidden_class(&keys);
147 let values: Vec<&'a JSONValue<'a>> = props.iter().map(|p| p.1).collect();
148 let values: &'a [&'a JSONValue<'a>] = self.arena.alloc_slice_copy(&values);
149 Some(self.arena.alloc(JSONValue::Object(cls, values)))
150 }
151
152 pub fn new_object_sorted(&self, props: &[Prop<'a>]) -> Option<&'a JSONValue<'a>> {
155 let keys: Vec<AtomBytes> = props.iter().map(|p| self.key_bytes(p.0)).collect();
156 let cls = self.get_hidden_class(&keys);
157 let values: Vec<&'a JSONValue<'a>> = props.iter().map(|p| p.1).collect();
158 let values: &'a [&'a JSONValue<'a>] = self.arena.alloc_slice_copy(&values);
159 Some(self.arena.alloc(JSONValue::Object(cls, values)))
160 }
161
162 pub fn new_array(&self, values: &[&'a JSONValue<'a>]) -> &'a JSONValue<'a> {
164 let values: &'a [&'a JSONValue<'a>] = self.arena.alloc_slice_copy(values);
165 self.arena.alloc(JSONValue::Array(values))
166 }
167}
168
169#[cfg(test)]
170mod factory_tests {
171 use super::super::*;
172 use bumpalo::Bump;
173 use hermes_atom_table::AtomTable;
174
175 #[test]
176 fn objects_arrays_and_hidden_class_sharing() {
177 use super::super::JSONFactory;
178 let arena = Bump::new();
179 let atoms = AtomTable::new();
180 let f = JSONFactory::new(&arena, &atoms);
181
182 fn mk<'b>(f: &'b JSONFactory<'b>, k1: f64, k2: f64) -> &'b super::super::JSONValue<'b> {
183 let p1 = (f.get_string_str("key1"), f.get_number(k1));
185 let p2 = (f.get_string_str("key2"), f.get_number(k2));
186 f.new_object(&mut [p2, p1]).unwrap() }
188 let o1 = mk(&f, 1.0, 2.0);
189 let o3 = mk(&f, 20.0, 10.0);
190
191 let v1 = o1.as_object().unwrap();
192 assert_eq!(v1.size(), 2);
193 assert!(std::ptr::eq(
195 v1.get_hidden_class(),
196 o3.as_object().unwrap().get_hidden_class()
197 ));
198 assert_eq!(v1.count("key1", &atoms), 1);
200 assert_eq!(v1.count("zzz", &atoms), 0);
201 assert_eq!(v1.get("key1", &atoms).and_then(|v| v.as_number()), Some(1.0));
202 let dup = (f.get_string_str("k"), f.get_number(1.0));
204 assert!(f.new_object(&mut [dup, dup]).is_none());
205
206 let a = f.new_array(&[f.get_number(5.0), f.get_null()]);
208 let av = a.as_array().unwrap();
209 assert_eq!(av.len(), 2);
210 assert_eq!(av.at(0).as_number(), Some(5.0));
211 }
212
213 #[test]
214 fn object_positional_accessors_and_iter_sorted_order() {
215 use super::super::JSONFactory;
216 let arena = Bump::new();
217 let atoms = AtomTable::new();
218 let f = JSONFactory::new(&arena, &atoms);
219 let obj = f
221 .new_object(&mut [
222 (f.get_string_str("b"), f.get_number(2.0)),
223 (f.get_string_str("a"), f.get_number(1.0)),
224 (f.get_string_str("c"), f.get_number(3.0)),
225 ])
226 .unwrap();
227 let o = obj.as_object().unwrap();
228 assert_eq!(o.size(), 3);
229 assert_eq!(atoms.bytes(o.key_at(0)), b"a");
231 assert_eq!(o.value_at(0).as_number(), Some(1.0));
232 assert_eq!(atoms.bytes(o.key_at(2)), b"c");
233 assert_eq!(o.value_at(2).as_number(), Some(3.0));
234 let collected: Vec<(Vec<u8>, f64)> = o
236 .iter()
237 .map(|(k, v)| (atoms.bytes(k).to_vec(), v.as_number().unwrap()))
238 .collect();
239 assert_eq!(
240 collected,
241 vec![(b"a".to_vec(), 1.0), (b"b".to_vec(), 2.0), (b"c".to_vec(), 3.0)]
242 );
243 }
244
245 #[test]
246 fn uniquing_and_singletons() {
247 let arena = Bump::new();
248 let atoms = AtomTable::new();
249 let f = JSONFactory::new(&arena, &atoms);
250
251 let a = f.get_string_str("key2");
253 let b = f.get_string_str("key2");
254 assert!(std::ptr::eq(a, b));
255 assert_eq!(a.as_string().map(|h| atoms.bytes(h).to_vec()), Some(b"key2".to_vec()));
256
257 assert!(std::ptr::eq(f.get_number(1.0), f.get_number(1.0)));
259 assert!(!std::ptr::eq(f.get_number(0.0), f.get_number(-0.0)));
260
261 assert!(std::ptr::eq(f.get_null(), f.get_null()));
263 assert!(std::ptr::eq(f.get_boolean(true), f.get_boolean(true)));
264 assert!(!std::ptr::eq(f.get_boolean(true), f.get_boolean(false)));
265 }
266}