xsd_schema/namespace/
table.rs1use crate::ids::NameId;
21use std::cell::RefCell;
22use std::collections::hash_map::DefaultHasher;
23use std::hash::{Hash, Hasher};
24
25#[derive(Debug)]
34struct Entry {
35 hash: u64,
37 next: i32,
39 text: Box<str>,
41}
42
43#[derive(Debug)]
62pub struct NameTable {
63 entries: RefCell<Vec<Entry>>,
65 buckets: RefCell<Vec<i32>>,
67}
68
69impl NameTable {
70 const INITIAL_BUCKETS: usize = 256;
72
73 pub fn new() -> Self {
75 let table = Self {
76 entries: RefCell::new(Vec::with_capacity(Self::INITIAL_BUCKETS)),
77 buckets: RefCell::new(vec![-1; Self::INITIAL_BUCKETS]),
78 };
79
80 table.add("");
83
84 table.add(XS_NAMESPACE);
86 table.add(XSI_NAMESPACE);
87 table.add(XML_NAMESPACE);
88 table.add(XMLNS_NAMESPACE);
89
90 table.add("xs");
92 table.add("xsd");
93 table.add("xsi");
94 table.add("xml");
95 table.add("xmlns");
96
97 table.add("type");
99 table.add("nil");
100 table.add("schemaLocation");
101 table.add("noNamespaceSchemaLocation");
102
103 table
104 }
105
106 pub fn add(&self, value: &str) -> NameId {
113 let hash = Self::hash_str(value);
114
115 if let Some(id) = self.find(value, hash) {
117 return id;
118 }
119
120 self.insert(value, hash)
122 }
123
124 pub fn get(&self, value: &str) -> Option<NameId> {
126 let hash = Self::hash_str(value);
127 self.find(value, hash)
128 }
129
130 pub fn resolve_ref(&self, id: NameId) -> &str {
149 unsafe { &(&(*self.entries.as_ptr()))[id.0 as usize].text }
150 }
151
152 pub fn try_resolve_ref(&self, id: NameId) -> Option<&str> {
156 unsafe {
157 (&(*self.entries.as_ptr()))
158 .get(id.0 as usize)
159 .map(|e| e.text.as_ref())
160 }
161 }
162
163 pub fn resolve(&self, id: NameId) -> String {
171 self.resolve_ref(id).to_string()
172 }
173
174 pub fn try_resolve(&self, id: NameId) -> Option<String> {
178 self.try_resolve_ref(id).map(|s| s.to_string())
179 }
180
181 pub fn len(&self) -> usize {
183 self.entries.borrow().len()
184 }
185
186 pub fn is_empty(&self) -> bool {
188 self.entries.borrow().is_empty()
189 }
190
191 fn hash_str(value: &str) -> u64 {
193 let mut hasher = DefaultHasher::new();
194 value.hash(&mut hasher);
195 hasher.finish()
196 }
197
198 fn find(&self, value: &str, hash: u64) -> Option<NameId> {
200 let entries = self.entries.borrow();
201 let buckets = self.buckets.borrow();
202
203 let bucket_idx = (hash as usize) % buckets.len();
204 let mut entry_idx = buckets[bucket_idx];
205
206 while entry_idx >= 0 {
207 let entry = &entries[entry_idx as usize];
208 if entry.hash == hash && entry.text.as_ref() == value {
209 return Some(NameId(entry_idx as u32));
210 }
211 entry_idx = entry.next;
212 }
213
214 None
215 }
216
217 fn insert(&self, value: &str, hash: u64) -> NameId {
219 let mut entries = self.entries.borrow_mut();
220 let mut buckets = self.buckets.borrow_mut();
221
222 if entries.len() >= buckets.len() {
224 Self::rehash_internal(&mut entries, &mut buckets);
225 }
226
227 let id = NameId(entries.len() as u32);
228 let bucket_idx = (hash as usize) % buckets.len();
229
230 let next = buckets[bucket_idx];
232 entries.push(Entry {
233 hash,
234 next,
235 text: value.into(),
236 });
237 buckets[bucket_idx] = id.0 as i32;
238
239 id
240 }
241
242 #[allow(clippy::ptr_arg)]
244 fn rehash_internal(entries: &mut Vec<Entry>, buckets: &mut Vec<i32>) {
245 let new_size = buckets.len() * 2;
246 *buckets = vec![-1; new_size];
247
248 for (idx, entry) in entries.iter_mut().enumerate() {
250 let bucket_idx = (entry.hash as usize) % new_size;
251 entry.next = buckets[bucket_idx];
252 buckets[bucket_idx] = idx as i32;
253 }
254 }
255}
256
257impl Default for NameTable {
258 fn default() -> Self {
259 Self::new()
260 }
261}
262
263pub const XS_NAMESPACE: &str = "http://www.w3.org/2001/XMLSchema";
265pub const XSI_NAMESPACE: &str = "http://www.w3.org/2001/XMLSchema-instance";
266pub const XML_NAMESPACE: &str = "http://www.w3.org/XML/1998/namespace";
267pub const XMLNS_NAMESPACE: &str = "http://www.w3.org/2000/xmlns/";
268
269pub mod well_known {
271 use crate::ids::NameId;
272
273 pub const EMPTY: NameId = NameId(0);
275
276 pub const XS_NAMESPACE: NameId = NameId(1);
278
279 pub const XSI_NAMESPACE: NameId = NameId(2);
281
282 pub const XML_NAMESPACE: NameId = NameId(3);
284
285 pub const XMLNS_NAMESPACE: NameId = NameId(4);
287
288 pub const XS_PREFIX: NameId = NameId(5);
290
291 pub const XSD_PREFIX: NameId = NameId(6);
293
294 pub const XSI_PREFIX: NameId = NameId(7);
296
297 pub const XML_PREFIX: NameId = NameId(8);
299
300 pub const XMLNS_PREFIX: NameId = NameId(9);
302
303 pub const XSI_TYPE: NameId = NameId(10);
305
306 pub const XSI_NIL: NameId = NameId(11);
308
309 pub const XSI_SCHEMA_LOCATION: NameId = NameId(12);
311
312 pub const XSI_NO_NAMESPACE_SCHEMA_LOCATION: NameId = NameId(13);
314}
315
316#[cfg(test)]
317mod tests {
318 use super::*;
319
320 #[test]
321 fn test_empty_string_is_id_zero() {
322 let table = NameTable::new();
323 assert_eq!(table.resolve(NameId(0)), "");
324 }
325
326 #[test]
327 fn test_add_and_resolve() {
328 let table = NameTable::new();
329 let id = table.add("hello");
330 assert_eq!(table.resolve(id), "hello");
331 }
332
333 #[test]
334 fn test_deduplication() {
335 let table = NameTable::new();
336 let id1 = table.add("hello");
337 let id2 = table.add("hello");
338 assert_eq!(id1, id2);
339 }
340
341 #[test]
342 fn test_different_strings_different_ids() {
343 let table = NameTable::new();
344 let id1 = table.add("hello");
345 let id2 = table.add("world");
346 assert_ne!(id1, id2);
347 }
348
349 #[test]
350 fn test_get_existing() {
351 let table = NameTable::new();
352 let id = table.add("test");
353 assert_eq!(table.get("test"), Some(id));
354 }
355
356 #[test]
357 fn test_get_nonexistent() {
358 let table = NameTable::new();
359 assert_eq!(table.get("nonexistent"), None);
360 }
361
362 #[test]
363 fn test_well_known_namespaces() {
364 let table = NameTable::new();
365 assert_eq!(table.resolve(well_known::XS_NAMESPACE), XS_NAMESPACE);
366 assert_eq!(table.resolve(well_known::XSI_NAMESPACE), XSI_NAMESPACE);
367 assert_eq!(table.resolve(well_known::XML_NAMESPACE), XML_NAMESPACE);
368 assert_eq!(table.resolve(well_known::XMLNS_NAMESPACE), XMLNS_NAMESPACE);
369 }
370
371 #[test]
372 fn test_well_known_prefixes() {
373 let table = NameTable::new();
374 assert_eq!(table.resolve(well_known::XS_PREFIX), "xs");
375 assert_eq!(table.resolve(well_known::XSD_PREFIX), "xsd");
376 assert_eq!(table.resolve(well_known::XSI_PREFIX), "xsi");
377 assert_eq!(table.resolve(well_known::XML_PREFIX), "xml");
378 }
379
380 #[test]
381 fn test_well_known_xsi_local_names() {
382 let table = NameTable::new();
383 assert_eq!(table.resolve(well_known::XSI_TYPE), "type");
384 assert_eq!(table.resolve(well_known::XSI_NIL), "nil");
385 assert_eq!(
386 table.resolve(well_known::XSI_SCHEMA_LOCATION),
387 "schemaLocation"
388 );
389 assert_eq!(
390 table.resolve(well_known::XSI_NO_NAMESPACE_SCHEMA_LOCATION),
391 "noNamespaceSchemaLocation"
392 );
393 }
394
395 #[test]
396 fn test_rehashing() {
397 let table = NameTable::new();
398 for i in 0..1000 {
400 let s = format!("string_{}", i);
401 table.add(&s);
402 }
403 for i in 0..1000 {
405 let s = format!("string_{}", i);
406 assert!(table.get(&s).is_some(), "Failed to find: {}", s);
407 }
408 }
409
410 #[test]
411 fn test_unicode_strings() {
412 let table = NameTable::new();
413 let id1 = table.add("日本語");
414 let id2 = table.add("日本語");
415 assert_eq!(id1, id2);
416 assert_eq!(table.resolve(id1), "日本語");
417 }
418
419 #[test]
420 fn test_interior_mutability() {
421 let table = NameTable::new();
423 let table_ref = &table;
424 let id1 = table_ref.add("through_ref");
425 let id2 = table_ref.add("through_ref");
426 assert_eq!(id1, id2);
427 assert_eq!(table.resolve(id1), "through_ref");
428 }
429
430 #[test]
431 fn test_resolve_ref_basic() {
432 let table = NameTable::new();
433 let id = table.add("hello");
434 assert_eq!(table.resolve_ref(id), "hello");
435 assert_eq!(table.resolve_ref(NameId(0)), "");
436 }
437
438 #[test]
439 fn test_try_resolve_ref_basic() {
440 let table = NameTable::new();
441 let id = table.add("test");
442 assert_eq!(table.try_resolve_ref(id), Some("test"));
443 assert_eq!(table.try_resolve_ref(NameId(9999)), None);
444 }
445
446 #[test]
447 fn test_resolve_ref_stability_across_add() {
448 let table = NameTable::new();
449 let id = table.add("stable_string");
450 let resolved: &str = table.resolve_ref(id);
451 for i in 0..500 {
453 table.add(&format!("extra_{i}"));
454 }
455 assert_eq!(resolved, "stable_string");
456 }
457
458 #[test]
459 fn test_try_resolve_ref_stability_across_add() {
460 let table = NameTable::new();
461 let id = table.add("test_str");
462 let resolved: Option<&str> = table.try_resolve_ref(id);
463 for i in 0..500 {
465 table.add(&format!("filler_{i}"));
466 }
467 assert_eq!(resolved, Some("test_str"));
468 }
469}