1use crate::schema::{FieldPath, FieldType, Schema};
39use std::{cmp::Ordering, collections::HashMap, fmt, ops::Range, sync::Arc};
40
41pub type ChunkKey = (i32, i64, i64);
44
45#[non_exhaustive]
52#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
53pub enum Target<'a> {
54 Definition { kind: &'a str, id: &'a str },
56 DefinitionType(&'a str),
58 Plane(&'a str),
60 Anchor { plane: &'a str, id: &'a str },
62 Zone { plane: &'a str, id: &'a str },
64 RenderLayer(&'a str),
66 GroundLayer(&'a str),
68 Terrain(&'a str),
70 File(&'a str),
73}
74
75#[non_exhaustive]
77#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
78pub enum Role<'a> {
79 ManifestProfile,
81 ManifestPlane,
83 ProfileGroundLayer,
85 ProfileSchema,
88 ProfileTransitions,
90 SchemaTarget { field: &'a str },
94 DefinitionType,
97 Field { field: &'a str },
102 ZoneParent,
104 DefaultZone,
106 GroundPalette { layer: &'a str },
109 ZonePalette,
111 ChunkPlane,
113 Proto,
115 Layer,
117 PortalPlane,
119 PortalAnchor { implicit_plane: bool },
123 StairwellStep,
125 TerrainLayer,
127 TerrainProto,
129 TransitionLayer,
131 TransitionBlend,
133 TransitionTile,
135}
136
137#[non_exhaustive]
139#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
140pub enum Holder<'a> {
141 Manifest,
143 Profile,
145 Schema { path: &'a str },
147 Transitions,
149 Definition { kind: &'a str, id: &'a str },
151 Plane { plane: &'a str },
153 Zone { plane: &'a str, zone: &'a str },
155 Stairwell { plane: &'a str, stairwell: &'a str },
157 Chunk { plane: &'a str, chunk: ChunkKey },
159 Object {
163 plane: &'a str,
164 chunk: ChunkKey,
165 ordinal: usize,
166 },
167}
168
169#[derive(Clone, Default, Eq, PartialEq)]
179pub struct ReferenceIndex {
180 names: Vec<Box<str>>,
183 holders: Vec<HolderEntry>,
185 records: Vec<Record>,
187 by_holder: Vec<u32>,
189}
190
191#[derive(Clone, Copy)]
193pub struct Reference<'i> {
194 index: &'i ReferenceIndex,
195 record: &'i Record,
196}
197
198impl<'i> Reference<'i> {
199 #[must_use]
201 pub fn target(&self) -> Target<'i> {
202 self.index.target(&self.record.target())
203 }
204
205 #[must_use]
207 pub fn role(&self) -> Role<'i> {
208 self.index.role(self.record)
209 }
210
211 #[must_use]
213 pub fn holder(&self) -> Holder<'i> {
214 self.index.holder(self.record.holder, self.record.ordinal)
215 }
216
217 #[must_use]
219 pub fn file(&self) -> &'i str {
220 self.index.holder_file(self.record.holder)
221 }
222
223 #[must_use]
227 pub fn span(&self) -> Range<usize> {
228 widen(self.record.start)..widen(self.record.end)
229 }
230}
231
232impl fmt::Debug for Reference<'_> {
233 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
234 formatter
235 .debug_struct("Reference")
236 .field("target", &self.target())
237 .field("role", &self.role())
238 .field("holder", &self.holder())
239 .field("file", &self.file())
240 .field("span", &self.span())
241 .finish()
242 }
243}
244
245impl fmt::Debug for ReferenceIndex {
246 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
247 formatter.debug_list().entries(self.iter()).finish()
248 }
249}
250
251impl ReferenceIndex {
252 #[must_use]
254 pub fn len(&self) -> usize {
255 self.records.len()
256 }
257
258 #[must_use]
260 pub fn is_empty(&self) -> bool {
261 self.records.is_empty()
262 }
263
264 pub fn iter(&self) -> impl ExactSizeIterator<Item = Reference<'_>> + '_ {
266 self.records.iter().map(|record| Reference {
267 index: self,
268 record,
269 })
270 }
271
272 pub fn to(&self, target: Target<'_>) -> impl ExactSizeIterator<Item = Reference<'_>> + '_ {
275 let found = self.target_key(target).map_or(&[][..], |key| {
276 let start = self.records.partition_point(|record| record.target() < key);
277 let length = self.records[start..].partition_point(|record| record.target() == key);
278 &self.records[start..start + length]
279 });
280 found.iter().map(|record| Reference {
281 index: self,
282 record,
283 })
284 }
285
286 pub fn from(&self, holder: Holder<'_>) -> impl ExactSizeIterator<Item = Reference<'_>> + '_ {
290 let found = self
291 .holder_key(holder)
292 .map_or(&[][..], |(holder, ordinal)| {
293 let key = (holder, ordinal);
294 let at = |position: &u32| {
295 let record = &self.records[widen(*position)];
296 (record.holder, record.ordinal)
297 };
298 let start = self
299 .by_holder
300 .partition_point(|position| at(position) < key);
301 let length =
302 self.by_holder[start..].partition_point(|position| at(position) == key);
303 &self.by_holder[start..start + length]
304 });
305 found.iter().map(|position| Reference {
306 index: self,
307 record: &self.records[widen(*position)],
308 })
309 }
310
311 #[cfg(test)]
314 pub(crate) fn heap_bytes(&self) -> usize {
315 self.names.capacity() * size_of::<Box<str>>()
316 + self.names.iter().map(|name| name.len()).sum::<usize>()
317 + self.holders.capacity() * size_of::<HolderEntry>()
318 + self.records.capacity() * size_of::<Record>()
319 + self.by_holder.capacity() * size_of::<u32>()
320 }
321
322 fn name(&self, symbol: u32) -> &str {
323 self.names.get(widen(symbol)).map_or("", |name| name)
324 }
325
326 fn symbol(&self, text: &str) -> Option<u32> {
327 let position = self
328 .names
329 .binary_search_by(|name| (**name).cmp(text))
330 .ok()?;
331 u32::try_from(position).ok()
332 }
333
334 fn target(&self, key: &TargetKey) -> Target<'_> {
335 let (a, b) = (self.name(key.a), self.name(key.b));
336 match key.kind {
337 TargetKind::Definition => Target::Definition { kind: a, id: b },
338 TargetKind::DefinitionType => Target::DefinitionType(a),
339 TargetKind::Plane => Target::Plane(a),
340 TargetKind::Anchor => Target::Anchor { plane: a, id: b },
341 TargetKind::Zone => Target::Zone { plane: a, id: b },
342 TargetKind::RenderLayer => Target::RenderLayer(a),
343 TargetKind::GroundLayer => Target::GroundLayer(a),
344 TargetKind::Terrain => Target::Terrain(a),
345 TargetKind::File => Target::File(a),
346 }
347 }
348
349 fn target_key(&self, target: Target<'_>) -> Option<TargetKey> {
350 let (kind, a, b) = split_target(target);
351 Some(TargetKey {
352 kind,
353 a: self.symbol(a)?,
354 b: if b.is_empty() { 0 } else { self.symbol(b)? },
355 })
356 }
357
358 fn role(&self, record: &Record) -> Role<'_> {
359 let argument = self.name(record.argument);
360 match record.role {
361 RoleKind::ManifestProfile => Role::ManifestProfile,
362 RoleKind::ManifestPlane => Role::ManifestPlane,
363 RoleKind::ProfileGroundLayer => Role::ProfileGroundLayer,
364 RoleKind::ProfileSchema => Role::ProfileSchema,
365 RoleKind::ProfileTransitions => Role::ProfileTransitions,
366 RoleKind::SchemaTarget => Role::SchemaTarget { field: argument },
367 RoleKind::DefinitionType => Role::DefinitionType,
368 RoleKind::Field => Role::Field { field: argument },
369 RoleKind::ZoneParent => Role::ZoneParent,
370 RoleKind::DefaultZone => Role::DefaultZone,
371 RoleKind::GroundPalette => Role::GroundPalette { layer: argument },
372 RoleKind::ZonePalette => Role::ZonePalette,
373 RoleKind::ChunkPlane => Role::ChunkPlane,
374 RoleKind::Proto => Role::Proto,
375 RoleKind::Layer => Role::Layer,
376 RoleKind::PortalPlane => Role::PortalPlane,
377 RoleKind::PortalAnchor => Role::PortalAnchor {
378 implicit_plane: record.argument != 0,
379 },
380 RoleKind::StairwellStep => Role::StairwellStep,
381 RoleKind::TerrainLayer => Role::TerrainLayer,
382 RoleKind::TerrainProto => Role::TerrainProto,
383 RoleKind::TransitionLayer => Role::TransitionLayer,
384 RoleKind::TransitionBlend => Role::TransitionBlend,
385 RoleKind::TransitionTile => Role::TransitionTile,
386 }
387 }
388
389 fn holder_entry(&self, holder: u32) -> Option<&HolderEntry> {
390 self.holders.get(widen(holder))
391 }
392
393 fn holder_file(&self, holder: u32) -> &str {
394 self.holder_entry(holder)
395 .map_or("", |entry| self.name(entry.file))
396 }
397
398 fn holder(&self, holder: u32, ordinal: u32) -> Holder<'_> {
399 let Some(entry) = self.holder_entry(holder) else {
400 return Holder::Manifest;
401 };
402 let (a, b) = (self.name(entry.key.a), self.name(entry.key.b));
403 match entry.key.kind {
404 HolderKind::Manifest => Holder::Manifest,
405 HolderKind::Profile => Holder::Profile,
406 HolderKind::Schema => Holder::Schema { path: a },
407 HolderKind::Transitions => Holder::Transitions,
408 HolderKind::Definition => Holder::Definition { kind: a, id: b },
409 HolderKind::Plane => Holder::Plane { plane: a },
410 HolderKind::Zone => Holder::Zone { plane: a, zone: b },
411 HolderKind::Stairwell => Holder::Stairwell {
412 plane: a,
413 stairwell: b,
414 },
415 HolderKind::Chunk if ordinal == NO_ORDINAL => Holder::Chunk {
416 plane: a,
417 chunk: entry.key.chunk,
418 },
419 HolderKind::Chunk => Holder::Object {
420 plane: a,
421 chunk: entry.key.chunk,
422 ordinal: widen(ordinal),
423 },
424 }
425 }
426
427 fn holder_key(&self, holder: Holder<'_>) -> Option<(u32, u32)> {
430 let (kind, a, b, chunk, ordinal) = split_holder(holder);
431 let ordinal = match ordinal {
432 Some(ordinal) => u32::try_from(ordinal).ok().filter(|o| *o != NO_ORDINAL)?,
433 None => NO_ORDINAL,
434 };
435 let key = HolderKey {
436 kind,
437 a: if a.is_empty() { 0 } else { self.symbol(a)? },
438 b: if b.is_empty() { 0 } else { self.symbol(b)? },
439 chunk,
440 };
441 let position = self
442 .holders
443 .binary_search_by(|entry| entry.key.cmp(&key))
444 .ok()?;
445 Some((u32::try_from(position).ok()?, ordinal))
446 }
447}
448
449fn widen(value: u32) -> usize {
451 usize::try_from(value).unwrap_or(usize::MAX)
452}
453
454const NO_ORDINAL: u32 = u32::MAX;
457
458#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
459enum TargetKind {
460 Definition,
461 DefinitionType,
462 Plane,
463 Anchor,
464 Zone,
465 RenderLayer,
466 GroundLayer,
467 Terrain,
468 File,
469}
470
471fn split_target(target: Target<'_>) -> (TargetKind, &str, &str) {
474 match target {
475 Target::Definition { kind, id } => (TargetKind::Definition, kind, id),
476 Target::DefinitionType(kind) => (TargetKind::DefinitionType, kind, ""),
477 Target::Plane(plane) => (TargetKind::Plane, plane, ""),
478 Target::Anchor { plane, id } => (TargetKind::Anchor, plane, id),
479 Target::Zone { plane, id } => (TargetKind::Zone, plane, id),
480 Target::RenderLayer(layer) => (TargetKind::RenderLayer, layer, ""),
481 Target::GroundLayer(layer) => (TargetKind::GroundLayer, layer, ""),
482 Target::Terrain(terrain) => (TargetKind::Terrain, terrain, ""),
483 Target::File(path) => (TargetKind::File, path, ""),
484 }
485}
486
487#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
488enum RoleKind {
489 ManifestProfile,
490 ManifestPlane,
491 ProfileGroundLayer,
492 ProfileSchema,
493 ProfileTransitions,
494 SchemaTarget,
495 DefinitionType,
496 Field,
497 ZoneParent,
498 DefaultZone,
499 GroundPalette,
500 ZonePalette,
501 ChunkPlane,
502 Proto,
503 Layer,
504 PortalPlane,
505 PortalAnchor,
506 StairwellStep,
507 TerrainLayer,
508 TerrainProto,
509 TransitionLayer,
510 TransitionBlend,
511 TransitionTile,
512}
513
514enum Argument<'a> {
516 None,
517 Text(&'a str),
518 Flag(bool),
519}
520
521fn split_role(role: Role<'_>) -> (RoleKind, Argument<'_>) {
522 match role {
523 Role::ManifestProfile => (RoleKind::ManifestProfile, Argument::None),
524 Role::ManifestPlane => (RoleKind::ManifestPlane, Argument::None),
525 Role::ProfileGroundLayer => (RoleKind::ProfileGroundLayer, Argument::None),
526 Role::ProfileSchema => (RoleKind::ProfileSchema, Argument::None),
527 Role::ProfileTransitions => (RoleKind::ProfileTransitions, Argument::None),
528 Role::SchemaTarget { field } => (RoleKind::SchemaTarget, Argument::Text(field)),
529 Role::DefinitionType => (RoleKind::DefinitionType, Argument::None),
530 Role::Field { field } => (RoleKind::Field, Argument::Text(field)),
531 Role::ZoneParent => (RoleKind::ZoneParent, Argument::None),
532 Role::DefaultZone => (RoleKind::DefaultZone, Argument::None),
533 Role::GroundPalette { layer } => (RoleKind::GroundPalette, Argument::Text(layer)),
534 Role::ZonePalette => (RoleKind::ZonePalette, Argument::None),
535 Role::ChunkPlane => (RoleKind::ChunkPlane, Argument::None),
536 Role::Proto => (RoleKind::Proto, Argument::None),
537 Role::Layer => (RoleKind::Layer, Argument::None),
538 Role::PortalPlane => (RoleKind::PortalPlane, Argument::None),
539 Role::PortalAnchor { implicit_plane } => {
540 (RoleKind::PortalAnchor, Argument::Flag(implicit_plane))
541 }
542 Role::StairwellStep => (RoleKind::StairwellStep, Argument::None),
543 Role::TerrainLayer => (RoleKind::TerrainLayer, Argument::None),
544 Role::TerrainProto => (RoleKind::TerrainProto, Argument::None),
545 Role::TransitionLayer => (RoleKind::TransitionLayer, Argument::None),
546 Role::TransitionBlend => (RoleKind::TransitionBlend, Argument::None),
547 Role::TransitionTile => (RoleKind::TransitionTile, Argument::None),
548 }
549}
550
551const fn argument_is_symbol(role: RoleKind) -> bool {
553 matches!(
554 role,
555 RoleKind::SchemaTarget | RoleKind::Field | RoleKind::GroundPalette
556 )
557}
558
559#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
560enum HolderKind {
561 Manifest,
562 Profile,
563 Schema,
564 Transitions,
565 Definition,
566 Plane,
567 Zone,
568 Stairwell,
569 Chunk,
571}
572
573fn split_holder(holder: Holder<'_>) -> (HolderKind, &str, &str, ChunkKey, Option<usize>) {
576 match holder {
577 Holder::Manifest => (HolderKind::Manifest, "", "", (0, 0, 0), None),
578 Holder::Profile => (HolderKind::Profile, "", "", (0, 0, 0), None),
579 Holder::Schema { path } => (HolderKind::Schema, path, "", (0, 0, 0), None),
580 Holder::Transitions => (HolderKind::Transitions, "", "", (0, 0, 0), None),
581 Holder::Definition { kind, id } => (HolderKind::Definition, kind, id, (0, 0, 0), None),
582 Holder::Plane { plane } => (HolderKind::Plane, plane, "", (0, 0, 0), None),
583 Holder::Zone { plane, zone } => (HolderKind::Zone, plane, zone, (0, 0, 0), None),
584 Holder::Stairwell { plane, stairwell } => {
585 (HolderKind::Stairwell, plane, stairwell, (0, 0, 0), None)
586 }
587 Holder::Chunk { plane, chunk } => (HolderKind::Chunk, plane, "", chunk, None),
588 Holder::Object {
589 plane,
590 chunk,
591 ordinal,
592 } => (HolderKind::Chunk, plane, "", chunk, Some(ordinal)),
593 }
594}
595
596#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
598struct TargetKey {
599 kind: TargetKind,
600 a: u32,
601 b: u32,
602}
603
604#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
605struct HolderKey {
606 kind: HolderKind,
607 a: u32,
608 b: u32,
609 chunk: ChunkKey,
610}
611
612#[derive(Clone, Copy, Debug, Eq, PartialEq)]
613struct HolderEntry {
614 key: HolderKey,
615 file: u32,
617}
618
619#[derive(Clone, Copy, Debug, Eq, PartialEq)]
622struct Record {
623 kind: TargetKind,
624 role: RoleKind,
625 a: u32,
626 b: u32,
627 argument: u32,
629 holder: u32,
631 ordinal: u32,
633 start: u32,
634 end: u32,
635}
636
637impl Record {
638 const fn target(&self) -> TargetKey {
639 TargetKey {
640 kind: self.kind,
641 a: self.a,
642 b: self.b,
643 }
644 }
645}
646
647#[derive(Clone, Copy, Debug)]
649pub(crate) struct Site<'a> {
650 pub target: Target<'a>,
651 pub role: Role<'a>,
652 pub holder: Holder<'a>,
653 pub file: &'a str,
655 pub start: usize,
656 pub end: usize,
657}
658
659const MAXIMUM_COUNT: u32 = u32::MAX - 1;
662
663#[cfg(test)]
664thread_local! {
665 pub(crate) static RECORDED: std::cell::Cell<usize> = const { std::cell::Cell::new(0) };
668 pub(crate) static TEST_MAXIMUM_COUNT: std::cell::Cell<Option<u32>> =
670 const { std::cell::Cell::new(None) };
671}
672
673pub(crate) struct Recorder {
678 symbols: HashMap<Box<str>, u32>,
679 names: Vec<Box<str>>,
680 holders: HashMap<HolderKey, u32>,
681 holder_entries: Vec<HolderEntry>,
682 records: Vec<Record>,
683 fields: HashMap<(usize, usize), (Arc<Schema>, u32)>,
689 maximum: u32,
691 overflowed: bool,
693}
694
695impl Default for Recorder {
696 fn default() -> Self {
697 #[cfg(test)]
698 let maximum = TEST_MAXIMUM_COUNT.get().unwrap_or(MAXIMUM_COUNT);
699 #[cfg(not(test))]
700 let maximum = MAXIMUM_COUNT;
701 Self {
704 symbols: HashMap::from([(Box::from(""), 0)]),
705 names: vec![Box::from("")],
706 holders: HashMap::new(),
707 holder_entries: Vec::new(),
708 records: Vec::new(),
709 fields: HashMap::new(),
710 maximum,
711 overflowed: false,
712 }
713 }
714}
715
716pub(crate) fn packed(value: usize) -> Option<u32> {
719 u32::try_from(value)
720 .ok()
721 .filter(|value| *value <= MAXIMUM_COUNT)
722}
723
724fn narrow(value: usize) -> u32 {
726 packed(value).unwrap_or(MAXIMUM_COUNT)
727}
728
729impl Recorder {
730 pub const fn overflowed(&self) -> bool {
732 self.overflowed
733 }
734
735 fn fit(&mut self, value: Option<u32>) -> Option<u32> {
737 if value.is_none() {
738 self.overflowed = true;
739 }
740 value
741 }
742
743 fn next(&mut self, length: usize) -> Option<u32> {
745 let maximum = self.maximum;
746 self.fit(packed(length).filter(|next| *next < maximum))
747 }
748
749 fn intern(&mut self, text: &str) -> Option<u32> {
750 if let Some(symbol) = self.symbols.get(text) {
751 return Some(*symbol);
752 }
753 let symbol = self.next(self.names.len())?;
754 self.names.push(text.into());
755 self.symbols.insert(text.into(), symbol);
756 Some(symbol)
757 }
758
759 fn intern_part(&mut self, text: &str) -> Option<u32> {
762 if text.is_empty() {
763 Some(0)
764 } else {
765 self.intern(text)
766 }
767 }
768
769 pub fn record(&mut self, site: &Site<'_>) {
770 let _ = self.try_record(site, None);
771 }
772
773 pub fn record_field(
778 &mut self,
779 site: &Site<'_>,
780 (schema, field_type): (&Arc<Schema>, &FieldType),
781 path: &FieldPath<'_>,
782 ) {
783 if self.overflowed {
784 return;
785 }
786 let key = (
787 Arc::as_ptr(schema).addr(),
788 std::ptr::from_ref(field_type).addr(),
789 );
790 let symbol = if let Some((_, symbol)) = self.fields.get(&key) {
791 *symbol
792 } else {
793 let Some(symbol) = self.intern(&path.render()) else {
794 return;
795 };
796 self.fields.insert(key, (Arc::clone(schema), symbol));
797 symbol
798 };
799 let _ = self.try_record(site, Some(symbol));
800 }
801
802 fn try_record(&mut self, site: &Site<'_>, argument: Option<u32>) -> Option<()> {
805 if self.overflowed {
806 return None;
807 }
808 let (kind, a, b) = split_target(site.target);
809 let target = TargetKey {
810 kind,
811 a: self.intern(a)?,
812 b: self.intern_part(b)?,
813 };
814 let (role, text) = split_role(site.role);
815 let argument = match (argument, text) {
816 (Some(symbol), _) => symbol,
817 (None, Argument::None) => 0,
818 (None, Argument::Text(text)) => self.intern(text)?,
819 (None, Argument::Flag(flag)) => u32::from(flag),
820 };
821 let (holder_kind, holder_a, holder_b, chunk, ordinal) = split_holder(site.holder);
822 let key = HolderKey {
823 kind: holder_kind,
824 a: self.intern_part(holder_a)?,
825 b: self.intern_part(holder_b)?,
826 chunk,
827 };
828 let file = self.intern(site.file)?;
829 let holder = if let Some(holder) = self.holders.get(&key) {
830 *holder
831 } else {
832 let position = self.next(self.holder_entries.len())?;
833 self.holder_entries.push(HolderEntry { key, file });
834 self.holders.insert(key, position);
835 position
836 };
837 let ordinal = match ordinal {
838 None => NO_ORDINAL,
839 Some(ordinal) => self.fit(packed(ordinal).filter(|o| *o < NO_ORDINAL))?,
841 };
842 let (start, end) = (packed(site.start), packed(site.end));
843 let (start, end) = (self.fit(start)?, self.fit(end)?);
844 self.next(self.records.len())?;
845 self.records.push(Record {
846 kind: target.kind,
847 a: target.a,
848 b: target.b,
849 role,
850 argument,
851 holder,
852 ordinal,
853 start,
854 end,
855 });
856 #[cfg(test)]
857 RECORDED.with(|recorded| recorded.set(recorded.get() + 1));
858 Some(())
859 }
860
861 pub fn finish(self) -> ReferenceIndex {
864 let Self {
865 names,
866 holder_entries,
867 records,
868 ..
869 } = self;
870 let (names, renumbered) = sorted(names);
871 let symbol = |old: u32| renumbered.get(widen(old)).copied().unwrap_or(0);
872
873 let renumber_holder = |key: HolderKey| HolderKey {
874 a: symbol(key.a),
875 b: symbol(key.b),
876 ..key
877 };
878 let mut holders: Vec<(u32, HolderEntry)> = holder_entries
879 .into_iter()
880 .enumerate()
881 .map(|(old, entry)| {
882 (
883 narrow(old),
884 HolderEntry {
885 key: renumber_holder(entry.key),
886 file: symbol(entry.file),
887 },
888 )
889 })
890 .collect();
891 holders.sort_by_key(|(_, entry)| entry.key);
892 let mut holder_position = vec![0; holders.len()];
893 for (new, (old, _)) in holders.iter().enumerate() {
894 holder_position[widen(*old)] = narrow(new);
895 }
896 let holders: Vec<HolderEntry> = holders.into_iter().map(|(_, entry)| entry).collect();
897
898 let mut records: Vec<Record> = records
899 .into_iter()
900 .map(|record| Record {
901 a: symbol(record.a),
902 b: symbol(record.b),
903 argument: if argument_is_symbol(record.role) {
904 symbol(record.argument)
905 } else {
906 record.argument
907 },
908 holder: holder_position
909 .get(widen(record.holder))
910 .copied()
911 .unwrap_or(0),
912 ..record
913 })
914 .collect();
915 let file = |record: &Record| {
916 holders
917 .get(widen(record.holder))
918 .map_or(0, |entry| entry.file)
919 };
920 let source_order = |a: &Record, b: &Record| -> Ordering {
923 (
924 file(a),
925 a.start,
926 a.end,
927 a.role,
928 a.argument,
929 a.holder,
930 a.ordinal,
931 )
932 .cmp(&(
933 file(b),
934 b.start,
935 b.end,
936 b.role,
937 b.argument,
938 b.holder,
939 b.ordinal,
940 ))
941 };
942 records.sort_by(|a, b| a.target().cmp(&b.target()).then_with(|| source_order(a, b)));
943 let mut by_holder: Vec<u32> = (0..narrow(records.len())).collect();
944 by_holder.sort_by(|a, b| {
945 let (a, b) = (&records[widen(*a)], &records[widen(*b)]);
946 (a.holder, a.ordinal)
947 .cmp(&(b.holder, b.ordinal))
948 .then_with(|| source_order(a, b))
949 .then_with(|| a.target().cmp(&b.target()))
950 });
951 records.shrink_to_fit();
954 ReferenceIndex {
955 names,
956 holders,
957 records,
958 by_holder,
959 }
960 }
961}
962
963fn sorted(names: Vec<Box<str>>) -> (Vec<Box<str>>, Vec<u32>) {
965 let mut order: Vec<u32> = (0..narrow(names.len())).collect();
966 order.sort_by(|a, b| names[widen(*a)].cmp(&names[widen(*b)]));
967 let mut renumbered = vec![0; names.len()];
968 for (new, old) in order.iter().enumerate() {
969 renumbered[widen(*old)] = narrow(new);
970 }
971 let mut slots: Vec<Option<Box<str>>> = names.into_iter().map(Some).collect();
972 let mut names: Vec<Box<str>> = order
973 .iter()
974 .map(|old| slots[widen(*old)].take().unwrap_or_default())
975 .collect();
976 names.shrink_to_fit();
977 (names, renumbered)
978}
979
980pub(crate) fn message(site: &Site<'_>) -> String {
983 match (site.role, site.target) {
984 (Role::ZonePalette, Target::Zone { id, .. }) => {
985 format!("zone \"{id}\" is not in this plane's zones.jsonc")
986 }
987 (Role::ZoneParent, Target::Zone { id: parent, .. }) => {
988 let zone = match site.holder {
989 Holder::Zone { zone, .. } => zone,
990 _ => "",
991 };
992 format!("zone \"{zone}\" has unknown parent \"{parent}\"")
993 }
994 (Role::DefaultZone, Target::Zone { id, .. }) => {
995 format!("default zone \"{id}\" is not in zones.jsonc")
996 }
997 (Role::ProfileGroundLayer, Target::RenderLayer(layer)) => {
998 format!("ground layer \"{layer}\" must be a render layer other than height or zone")
999 }
1000 (Role::SchemaTarget { field }, Target::DefinitionType(kind)) => {
1001 format!("field \"{field}\" references unknown definition type \"{kind}\"")
1002 }
1003 (_, Target::Definition { kind, id }) => format!("no {kind} definition \"{id}\""),
1004 (_, Target::Plane(plane)) => format!("no plane \"{plane}\""),
1005 (_, Target::Anchor { plane, id }) => format!("no anchor \"{id}\" in plane \"{plane}\""),
1006 (_, Target::RenderLayer(layer)) => {
1007 format!("\"{layer}\" is not a render layer of the profile")
1008 }
1009 (_, Target::GroundLayer(layer)) => {
1010 format!("\"{layer}\" is not a ground layer of the profile")
1011 }
1012 (_, Target::Terrain(terrain)) => format!("no terrain \"{terrain}\""),
1013 (_, Target::Zone { plane, id }) => format!("no zone \"{id}\" in plane \"{plane}\""),
1014 (_, Target::DefinitionType(kind)) => format!("no definition type \"{kind}\""),
1015 (_, Target::File(path)) => format!("no file {path}"),
1016 }
1017}
1018
1019#[cfg(test)]
1020mod tests {
1021 use super::{Holder, Record, Recorder, Role, Site, Target};
1022
1023 fn site<'a>(target: Target<'a>, holder: Holder<'a>, file: &'a str, start: usize) -> Site<'a> {
1024 Site {
1025 target,
1026 role: Role::Proto,
1027 holder,
1028 file,
1029 start,
1030 end: start + 3,
1031 }
1032 }
1033
1034 #[test]
1035 fn a_value_is_packed_only_below_the_marker() {
1036 use super::packed;
1037 assert_eq!(packed(0), Some(0));
1038 let largest = usize::try_from(u32::MAX - 1).unwrap();
1039 assert_eq!(packed(largest), Some(u32::MAX - 1));
1040 assert_eq!(packed(largest + 1), None);
1041 assert_eq!(packed(usize::MAX), None);
1042 }
1043
1044 #[test]
1045 fn a_record_that_does_not_fit_refuses_the_index() {
1046 let mut recorder = Recorder::default();
1047 recorder.record(&Site {
1048 start: usize::MAX,
1049 ..site(Target::Plane("p"), Holder::Manifest, "world.jsonc", 0)
1050 });
1051 assert!(recorder.overflowed());
1052 recorder.record(&site(
1054 Target::Plane("p"),
1055 Holder::Manifest,
1056 "world.jsonc",
1057 0,
1058 ));
1059 assert!(recorder.overflowed());
1060 assert_eq!(recorder.finish().len(), 0);
1061 let mut ordinal = Recorder::default();
1062 ordinal.record(&site(
1063 Target::Plane("p"),
1064 Holder::Object {
1065 plane: "p",
1066 chunk: (0, 0, 0),
1067 ordinal: usize::try_from(u32::MAX).unwrap(),
1068 },
1069 "c.jsonc",
1070 0,
1071 ));
1072 assert!(ordinal.overflowed());
1073 }
1074
1075 #[test]
1078 fn a_field_recorded_first_keeps_symbol_zero_empty() {
1079 use crate::schema::{FieldPath, FieldType, Schema};
1080 use std::sync::Arc;
1081 let schema = Arc::new(Schema::new("s.jsonc".into()));
1082 let field_type = FieldType::Reference {
1083 target: "proto".into(),
1084 };
1085 let root = FieldPath::Root;
1086 let path = FieldPath::Field(&root, "f");
1087 let holder = Holder::Object {
1088 plane: "p",
1089 chunk: (0, 0, 0),
1090 ordinal: 0,
1091 };
1092 let mut recorder = Recorder::default();
1093 recorder.record_field(
1094 &Site {
1095 target: Target::Definition {
1096 kind: "proto",
1097 id: "x",
1098 },
1099 role: Role::Field { field: "" },
1100 holder,
1101 file: "c.jsonc",
1102 start: 1,
1103 end: 4,
1104 },
1105 (&schema, &field_type),
1106 &path,
1107 );
1108 recorder.record(&Site {
1109 target: Target::Plane("p"),
1110 role: Role::ChunkPlane,
1111 holder: Holder::Chunk {
1112 plane: "p",
1113 chunk: (0, 0, 0),
1114 },
1115 file: "c.jsonc",
1116 start: 10,
1117 end: 13,
1118 });
1119 let index = recorder.finish();
1120 let fields: Vec<Role<'_>> = index.from(holder).map(|found| found.role()).collect();
1121 assert_eq!(fields, [Role::Field { field: "f" }]);
1122 assert_eq!(index.to(Target::Plane("p")).count(), 1);
1123 }
1124
1125 #[test]
1126 fn a_record_is_32_bytes() {
1127 assert_eq!(size_of::<Record>(), 32);
1128 }
1129
1130 #[test]
1131 fn interning_sorts_strings_and_renumbers_every_symbol() {
1132 let mut recorder = Recorder::default();
1133 let chunk = |ordinal| Holder::Object {
1134 plane: "p",
1135 chunk: (0, 0, 0),
1136 ordinal,
1137 };
1138 recorder.record(&site(
1140 Target::Definition {
1141 kind: "proto",
1142 id: "zeta",
1143 },
1144 chunk(1),
1145 "b.jsonc",
1146 40,
1147 ));
1148 recorder.record(&site(
1149 Target::Definition {
1150 kind: "proto",
1151 id: "alpha",
1152 },
1153 chunk(0),
1154 "b.jsonc",
1155 10,
1156 ));
1157 recorder.record(&site(
1158 Target::Definition {
1159 kind: "proto",
1160 id: "zeta",
1161 },
1162 Holder::Stairwell {
1163 plane: "p",
1164 stairwell: "s",
1165 },
1166 "a.jsonc",
1167 90,
1168 ));
1169 let index = recorder.finish();
1170 assert_eq!(index.len(), 3);
1171 let order: Vec<(Target<'_>, &str, usize)> = index
1172 .iter()
1173 .map(|reference| (reference.target(), reference.file(), reference.span().start))
1174 .collect();
1175 assert_eq!(
1176 order,
1177 [
1178 (
1179 Target::Definition {
1180 kind: "proto",
1181 id: "alpha"
1182 },
1183 "b.jsonc",
1184 10
1185 ),
1186 (
1187 Target::Definition {
1188 kind: "proto",
1189 id: "zeta"
1190 },
1191 "a.jsonc",
1192 90
1193 ),
1194 (
1195 Target::Definition {
1196 kind: "proto",
1197 id: "zeta"
1198 },
1199 "b.jsonc",
1200 40
1201 ),
1202 ]
1203 );
1204 let from: Vec<usize> = index
1205 .from(chunk(1))
1206 .map(|reference| reference.span().start)
1207 .collect();
1208 assert_eq!(from, [40]);
1209 assert_eq!(index.from(chunk(2)).len(), 0);
1210 assert_eq!(
1211 index
1212 .from(Holder::Chunk {
1213 plane: "p",
1214 chunk: (0, 0, 0)
1215 })
1216 .len(),
1217 0
1218 );
1219 assert_eq!(
1220 index
1221 .to(Target::Definition {
1222 kind: "proto",
1223 id: "missing"
1224 })
1225 .len(),
1226 0
1227 );
1228 assert_eq!(index.to(Target::Plane("proto")).len(), 0);
1230 }
1231}