1use crate::prelude::*;
5use crate::{
6 EntityRef, FilePos, FuncIndex, FuncKey, FuncKeyIndex, FuncKeyKind, FuncKeyNamespace, Module,
7 PanicOnOom as _,
8};
9use core::ops::Range;
10use core::{fmt, u32};
11use core::{iter, str};
12use serde_derive::{Deserialize, Serialize};
13#[cfg(feature = "rr")]
14use sha2::{Digest, Sha256};
15
16#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
19pub struct FunctionLoc {
20 pub start: u32,
23 pub length: u32,
25}
26
27impl FunctionLoc {
28 #[inline]
30 pub fn is_empty(&self) -> bool {
31 self.length == 0
32 }
33}
34
35#[derive(Copy, Clone, Default, PartialEq, Eq, Ord, PartialOrd, Debug, Serialize, Deserialize)]
40pub struct WasmChecksum([u8; 32]);
41
42impl WasmChecksum {
43 #[cfg(feature = "rr")]
47 pub fn from_binary(bin: &[u8], recording: bool) -> WasmChecksum {
48 if recording {
49 WasmChecksum(Sha256::digest(bin).into())
50 } else {
51 WasmChecksum::default()
52 }
53 }
54
55 #[cfg(not(feature = "rr"))]
58 pub fn from_binary(_: &[u8], _: bool) -> WasmChecksum {
59 WasmChecksum::default()
60 }
61}
62
63impl core::ops::Deref for WasmChecksum {
64 type Target = [u8; 32];
65
66 fn deref(&self) -> &Self::Target {
67 &self.0
68 }
69}
70
71pub struct CompiledFunctionsTableBuilder {
73 inner: CompiledFunctionsTable,
74}
75
76impl CompiledFunctionsTableBuilder {
77 pub fn new() -> Self {
79 Self {
80 inner: CompiledFunctionsTable {
81 namespaces: TryPrimaryMap::new(),
82 func_loc_starts: TryPrimaryMap::new(),
83 sparse_starts: TryPrimaryMap::new(),
84 src_loc_starts: TryPrimaryMap::new(),
85 sparse_indices: TryPrimaryMap::new(),
86 func_locs: TryPrimaryMap::new(),
87 src_locs: TryPrimaryMap::new(),
88 },
89 }
90 }
91
92 fn last_namespace(&self) -> Option<FuncKeyNamespace> {
93 let (_, &ns) = self.inner.namespaces.last()?;
94 Some(ns)
95 }
96
97 fn last_key_index(&self) -> Option<FuncKeyIndex> {
98 let (ns_idx, ns) = self.inner.namespaces.last()?;
99 let start = self.inner.func_loc_starts[ns_idx];
100 if CompiledFunctionsTable::is_dense(ns.kind()) {
101 let len = self.inner.func_locs.len();
102 let len = u32::try_from(len).unwrap();
103 let key_index = len - start.as_u32();
104 let key_index = FuncKeyIndex::from_raw(key_index);
105 Some(key_index)
106 } else {
107 let sparse_start = self.inner.sparse_starts[ns_idx];
108 if self.inner.sparse_indices.len() > sparse_start.index() {
109 let (_, &key_index) = self.inner.sparse_indices.last().unwrap();
110 Some(key_index)
111 } else {
112 None
113 }
114 }
115 }
116
117 fn last_func_loc(&self) -> Option<FunctionLoc> {
118 let (_, &loc) = self.inner.func_locs.last()?;
119 Some(loc)
120 }
121
122 pub fn push_func(
126 &mut self,
127 key: FuncKey,
128 func_loc: FunctionLoc,
129 src_loc: FilePos,
130 ) -> &mut Self {
131 let (key_ns, key_index) = key.into_parts();
132
133 assert!(
134 self.last_namespace().is_none_or(|ns| ns <= key_ns),
135 "`FuncKey`s pushed out of order"
136 );
137 assert!(
138 self.last_key_index().is_none_or(
139 |i| i <= key_index || self.last_namespace().is_some_and(|ns| ns != key_ns)
140 ),
141 "`FuncKey`s pushed out of order"
142 );
143 assert!(
144 self.last_func_loc()
145 .is_none_or(|l| l.start + l.length <= func_loc.start),
146 "`FunctionLoc`s pushed out of order"
147 );
148
149 let kind_start_index = self
151 .inner
152 .namespaces
153 .last()
154 .and_then(|(ns_idx, ns)| {
155 if *ns == key_ns {
156 Some(self.inner.func_loc_starts[ns_idx])
157 } else {
158 None
159 }
160 })
161 .unwrap_or_else(|| {
162 let start = self.inner.func_locs.next_key();
163 let ns_idx = self.inner.namespaces.push(key_ns).panic_on_oom();
164 let ns_idx2 = self.inner.func_loc_starts.push(start).panic_on_oom();
165 let ns_idx3 = self
166 .inner
167 .sparse_starts
168 .push(self.inner.sparse_indices.next_key())
169 .panic_on_oom();
170 let ns_idx4 = self
171 .inner
172 .src_loc_starts
173 .push(self.inner.src_locs.next_key())
174 .panic_on_oom();
175 debug_assert_eq!(ns_idx, ns_idx2);
176 debug_assert_eq!(ns_idx, ns_idx3);
177 debug_assert_eq!(ns_idx, ns_idx4);
178 start
179 });
180
181 if CompiledFunctionsTable::is_dense(key.kind()) {
182 let index = kind_start_index.as_u32() + key_index.into_raw();
184 let index = FuncLocIndex::from_u32(index);
185 debug_assert!(self.inner.func_locs.get(index).is_none());
186
187 let null_func_loc = FunctionLoc {
195 start: self
196 .last_func_loc()
197 .map(|l| l.start + l.length)
198 .unwrap_or_default(),
199 length: 0,
200 };
201 let gap = index.index() - self.inner.func_locs.len();
202 self.inner
203 .func_locs
204 .try_extend(iter::repeat(null_func_loc).take(gap))
205 .panic_on_oom();
206 debug_assert_eq!(index, self.inner.func_locs.next_key());
207
208 if CompiledFunctionsTable::has_src_locs(key_ns.kind()) {
209 self.inner
210 .src_locs
211 .try_extend(iter::repeat(FilePos::none()).take(gap))
212 .panic_on_oom();
213 }
214 } else {
215 debug_assert!(
216 src_loc.is_none(),
217 "sparse keys do not have source locations"
218 );
219 self.inner.sparse_indices.push(key_index).panic_on_oom();
220 }
221
222 self.inner.func_locs.push(func_loc).panic_on_oom();
224 if CompiledFunctionsTable::has_src_locs(key_ns.kind()) {
225 self.inner.src_locs.push(src_loc).panic_on_oom();
226 } else {
227 debug_assert!(src_loc.is_none());
228 }
229
230 self
231 }
232
233 pub fn finish(self) -> CompiledFunctionsTable {
235 self.inner
236 }
237}
238
239#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
240struct NamespaceIndex(u32);
241cranelift_entity::entity_impl!(NamespaceIndex);
242
243#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
244struct FuncLocIndex(u32);
245cranelift_entity::entity_impl!(FuncLocIndex);
246
247#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
248struct SparseIndex(u32);
249cranelift_entity::entity_impl!(SparseIndex);
250
251#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
252struct SrcLocIndex(u32);
253cranelift_entity::entity_impl!(SrcLocIndex);
254
255#[derive(Debug, Serialize, Deserialize)]
287pub struct CompiledFunctionsTable {
288 namespaces: TryPrimaryMap<NamespaceIndex, FuncKeyNamespace>,
293
294 func_loc_starts: TryPrimaryMap<NamespaceIndex, FuncLocIndex>,
301
302 sparse_starts: TryPrimaryMap<NamespaceIndex, SparseIndex>,
311
312 src_loc_starts: TryPrimaryMap<NamespaceIndex, SrcLocIndex>,
322
323 sparse_indices: TryPrimaryMap<SparseIndex, FuncKeyIndex>,
331
332 func_locs: TryPrimaryMap<FuncLocIndex, FunctionLoc>,
342
343 src_locs: TryPrimaryMap<SrcLocIndex, FilePos>,
349}
350
351impl CompiledFunctionsTable {
352 #[inline]
353 fn namespace_index(&self, namespace: FuncKeyNamespace) -> Option<NamespaceIndex> {
354 const LINEAR_SEARCH_LIMIT: usize = 32;
355 if self.namespaces.len() <= LINEAR_SEARCH_LIMIT {
356 self.namespaces
357 .iter()
358 .find_map(|(idx, ns)| if *ns == namespace { Some(idx) } else { None })
359 } else {
360 self.namespaces
361 .binary_search_values_by_key(&namespace, |ns| *ns)
362 .ok()
363 }
364 }
365
366 #[inline]
367 fn func_loc_range(&self, ns_idx: NamespaceIndex) -> Range<FuncLocIndex> {
368 let start = self.func_loc_starts[ns_idx];
369 let next_ns_idx = NamespaceIndex::from_u32(ns_idx.as_u32() + 1);
370 let end = self
371 .func_loc_starts
372 .get(next_ns_idx)
373 .copied()
374 .unwrap_or_else(|| self.func_locs.next_key());
375 start..end
376 }
377
378 fn sparse_range(&self, ns_idx: NamespaceIndex) -> Range<SparseIndex> {
379 debug_assert!(!Self::is_dense(self.namespaces[ns_idx].kind()));
380 let start = self.sparse_starts[ns_idx];
381 let next_ns_idx = NamespaceIndex::from_u32(ns_idx.as_u32() + 1);
382 let end = self
383 .sparse_starts
384 .get(next_ns_idx)
385 .copied()
386 .unwrap_or_else(|| self.sparse_indices.next_key());
387 start..end
388 }
389
390 fn src_loc_range(&self, ns_idx: NamespaceIndex) -> Range<SrcLocIndex> {
391 debug_assert!(Self::has_src_locs(self.namespaces[ns_idx].kind()));
392 let start = self.src_loc_starts[ns_idx];
393 let next_ns_idx = NamespaceIndex::from_u32(ns_idx.as_u32() + 1);
394 let end = self
395 .src_loc_starts
396 .get(next_ns_idx)
397 .copied()
398 .unwrap_or_else(|| self.src_locs.next_key());
399 start..end
400 }
401
402 #[inline]
405 fn func_loc_index(&self, key: FuncKey) -> Option<FuncLocIndex> {
406 let (key_ns, key_index) = key.into_parts();
407 let ns_idx = self.namespace_index(key_ns)?;
408 let Range { start, end } = self.func_loc_range(ns_idx);
409
410 let index = if Self::is_dense(key.kind()) {
411 let index = start.as_u32().checked_add(key_index.into_raw())?;
412 FuncLocIndex::from_u32(index)
413 } else {
414 let sparse_range = self.sparse_range(ns_idx);
415 let sparse_subslice = self.sparse_indices.get_range(sparse_range).unwrap();
416 match sparse_subslice.binary_search(&key_index) {
417 Ok(i) => FuncLocIndex::new(start.index() + i),
418 Err(_) => return None,
419 }
420 };
421
422 if index < end { Some(index) } else { None }
423 }
424
425 #[inline]
428 pub fn func_loc(&self, key: FuncKey) -> Option<&FunctionLoc> {
429 let index = self.func_loc_index(key)?;
430 let loc = &self.func_locs[index];
431 if loc.is_empty() { None } else { Some(loc) }
432 }
433
434 fn src_loc_index(&self, key: FuncKey) -> Option<SrcLocIndex> {
435 let (key_ns, key_index) = key.into_parts();
436 if !Self::has_src_locs(key_ns.kind()) {
437 return None;
438 }
439
440 let ns_idx = self.namespace_index(key_ns)?;
441 let Range { start, end } = self.src_loc_range(ns_idx);
442
443 debug_assert!(Self::is_dense(key_ns.kind()));
444 let index = start.as_u32().checked_add(key_index.into_raw())?;
445 let index = SrcLocIndex::from_u32(index);
446 if index >= end {
447 return None;
448 }
449
450 Some(index)
451 }
452
453 pub fn src_loc(&self, key: FuncKey) -> Option<FilePos> {
456 let index = self.src_loc_index(key)?;
457 let loc = self.src_locs[index];
458 if loc.is_none() { None } else { Some(loc) }
459 }
460
461 pub fn func_by_text_offset(&self, text_offset: u32) -> Option<FuncKey> {
464 let index = match self.func_locs.as_values_slice().binary_search_by(|loc| {
465 if loc.is_empty() {
466 loc.start
467 .cmp(&text_offset)
468 .then_with(|| core::cmp::Ordering::Less)
469 } else {
470 if loc.start > text_offset {
471 core::cmp::Ordering::Greater
472 } else if loc.start + loc.length <= text_offset {
473 core::cmp::Ordering::Less
474 } else {
475 debug_assert!(loc.start <= text_offset);
476 debug_assert!(text_offset < loc.start + loc.length);
477 core::cmp::Ordering::Equal
478 }
479 }
480 }) {
481 Ok(k) => k,
483 Err(k) => k,
488 };
489 let index = FuncLocIndex::new(index);
490
491 let loc = self.func_locs.get(index)?;
497 let start = loc.start;
498 let end = start + loc.length;
499 if text_offset < start || end < text_offset {
500 return None;
501 }
502
503 let ns_idx = match self
504 .func_loc_starts
505 .binary_search_values_by_key(&index, |s| *s)
506 {
507 Ok(i) => i,
509 Err(i) => {
513 let i = i.as_u32();
514 assert_ne!(i, 0);
515 NamespaceIndex::from_u32(i - 1)
516 }
517 };
518 let key_ns = self.namespaces[ns_idx];
519 let start = self.func_loc_starts[ns_idx];
520
521 let key_index = if Self::is_dense(key_ns.kind()) {
522 let key_index = index.as_u32() - start.as_u32();
523 FuncKeyIndex::from_raw(key_index)
524 } else {
525 let sparse_offset = index.as_u32() - start.as_u32();
526 let sparse_start = self.sparse_starts[ns_idx];
527 let sparse_index = SparseIndex::from_u32(sparse_start.as_u32() + sparse_offset);
528 debug_assert!(
529 {
530 let range = self.sparse_range(ns_idx);
531 range.start <= sparse_index && sparse_index < range.end
532 },
533 "{sparse_index:?} is not within {:?}",
534 self.sparse_range(ns_idx)
535 );
536 self.sparse_indices[sparse_index]
537 };
538 let key = FuncKey::from_parts(key_ns, key_index);
539
540 Some(key)
541 }
542
543 fn is_dense(kind: FuncKeyKind) -> bool {
548 match kind {
549 FuncKeyKind::DefinedWasmFunction
550 | FuncKeyKind::WasmToArrayTrampoline
551 | FuncKeyKind::PulleyHostCall => true,
552
553 FuncKeyKind::ArrayToWasmTrampoline
554 | FuncKeyKind::WasmToBuiltinTrampoline
555 | FuncKeyKind::PatchableToBuiltinTrampoline
556 | FuncKeyKind::ModuleStartup => false,
557
558 #[cfg(feature = "component-model")]
561 FuncKeyKind::ComponentTrampoline
562 | FuncKeyKind::ResourceDropTrampoline
563 | FuncKeyKind::UnsafeIntrinsic => true,
564 }
565 }
566
567 fn has_src_locs(kind: FuncKeyKind) -> bool {
569 match kind {
570 FuncKeyKind::DefinedWasmFunction => true,
571 FuncKeyKind::ArrayToWasmTrampoline
572 | FuncKeyKind::WasmToArrayTrampoline
573 | FuncKeyKind::WasmToBuiltinTrampoline
574 | FuncKeyKind::PatchableToBuiltinTrampoline
575 | FuncKeyKind::PulleyHostCall
576 | FuncKeyKind::ModuleStartup => false,
577 #[cfg(feature = "component-model")]
578 FuncKeyKind::ComponentTrampoline
579 | FuncKeyKind::ResourceDropTrampoline
580 | FuncKeyKind::UnsafeIntrinsic => false,
581 }
582 }
583}
584
585#[derive(Serialize, Deserialize)]
590pub struct CompiledModuleInfo {
591 pub module: Module,
593
594 pub meta: Metadata,
596
597 pub func_names: Vec<FunctionName>,
599
600 pub checksum: WasmChecksum,
602}
603
604#[derive(Serialize, Deserialize)]
607pub struct FunctionName {
608 pub idx: FuncIndex,
610 pub offset: u32,
613 pub len: u32,
615}
616
617#[derive(Serialize, Deserialize)]
619pub struct Metadata {
620 pub has_unparsed_debuginfo: bool,
623
624 pub code_section_offset: u64,
626
627 pub has_wasm_debuginfo: bool,
633
634 pub dwarf: Vec<(u8, Range<u64>)>,
637}
638
639#[derive(Serialize, Deserialize, Hash, Eq, PartialEq, Debug)]
641pub enum FlagValue<'a> {
642 Enum(&'a str),
644 Num(u8),
646 Bool(bool),
648}
649
650impl fmt::Display for FlagValue<'_> {
651 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
652 match self {
653 Self::Enum(v) => v.fmt(f),
654 Self::Num(v) => v.fmt(f),
655 Self::Bool(v) => v.fmt(f),
656 }
657 }
658}
659
660pub enum ObjectKind {
662 Module,
664 Component,
666}
667
668#[cfg(test)]
669mod tests {
670 use super::*;
671 use crate::{DefinedFuncIndex, StaticModuleIndex};
672
673 fn func_loc(range: Range<u32>) -> FunctionLoc {
674 FunctionLoc {
675 start: range.start,
676 length: range.end - range.start,
677 }
678 }
679
680 fn def_func_key(m: u32, f: u32) -> FuncKey {
681 FuncKey::DefinedWasmFunction(
682 StaticModuleIndex::from_u32(m),
683 DefinedFuncIndex::from_u32(f),
684 )
685 }
686
687 fn array_to_wasm_tramp_key(m: u32, f: u32) -> FuncKey {
688 FuncKey::ArrayToWasmTrampoline(
689 StaticModuleIndex::from_u32(m),
690 DefinedFuncIndex::from_u32(f),
691 )
692 }
693
694 fn make_test_table() -> CompiledFunctionsTable {
695 let mut builder = CompiledFunctionsTableBuilder::new();
696
697 builder
698 .push_func(def_func_key(0, 0), func_loc(0..10), FilePos::new(111))
700 .push_func(def_func_key(0, 1), func_loc(10..20), FilePos::new(222))
701 .push_func(def_func_key(0, 2), func_loc(20..30), FilePos::none())
702 .push_func(def_func_key(0, 5), func_loc(30..40), FilePos::new(333))
704 .push_func(
706 array_to_wasm_tramp_key(0, 1),
707 func_loc(100..110),
708 FilePos::none(),
709 )
710 .push_func(
711 array_to_wasm_tramp_key(0, 2),
712 func_loc(110..120),
713 FilePos::none(),
714 )
715 .push_func(
716 array_to_wasm_tramp_key(0, 5),
717 func_loc(120..130),
718 FilePos::none(),
719 );
720
721 builder.finish()
722 }
723
724 #[test]
725 fn src_locs() {
726 let table = make_test_table();
727
728 for (key, expected) in [
729 (def_func_key(0, 0), Some(FilePos::new(111))),
730 (def_func_key(0, 1), Some(FilePos::new(222))),
731 (def_func_key(0, 2), None),
732 (def_func_key(0, 3), None),
733 (def_func_key(0, 4), None),
734 (def_func_key(0, 5), Some(FilePos::new(333))),
735 (array_to_wasm_tramp_key(0, 0), None),
736 (array_to_wasm_tramp_key(0, 1), None),
737 (array_to_wasm_tramp_key(0, 2), None),
738 (array_to_wasm_tramp_key(0, 3), None),
739 (array_to_wasm_tramp_key(0, 4), None),
740 (array_to_wasm_tramp_key(0, 5), None),
741 ] {
742 eprintln!("Checking key {key:?}");
743 let actual = table.src_loc(key);
744 assert_eq!(expected, actual);
745 }
746 }
747
748 #[test]
749 fn func_locs() {
750 let table = make_test_table();
751
752 for (key, expected) in [
753 (def_func_key(0, 0), Some(0)),
754 (def_func_key(0, 1), Some(10)),
755 (def_func_key(0, 2), Some(20)),
756 (def_func_key(0, 3), None),
757 (def_func_key(0, 4), None),
758 (def_func_key(0, 5), Some(30)),
759 (array_to_wasm_tramp_key(0, 0), None),
760 (array_to_wasm_tramp_key(0, 1), Some(100)),
761 (array_to_wasm_tramp_key(0, 2), Some(110)),
762 (array_to_wasm_tramp_key(0, 3), None),
763 (array_to_wasm_tramp_key(0, 4), None),
764 (array_to_wasm_tramp_key(0, 5), Some(120)),
765 ] {
766 let actual = table.func_loc(key);
767 match (expected, actual) {
768 (None, None) => {}
769 (Some(expected), Some(actual)) => assert_eq!(expected, actual.start),
770 (None, Some(actual)) => {
771 panic!("expected no function location for {key:?}, got {actual:?}")
772 }
773 (Some(_), None) => {
774 panic!("expected a function location for {key:?}, but got nothing")
775 }
776 }
777 }
778 }
779
780 #[test]
781 fn reverse_func_locs() {
782 let table = make_test_table();
783
784 for (range, expected) in [
785 (0..10, Some(def_func_key(0, 0))),
786 (10..20, Some(def_func_key(0, 1))),
787 (20..30, Some(def_func_key(0, 2))),
788 (30..40, Some(def_func_key(0, 5))),
789 (40..100, None),
790 (100..110, Some(array_to_wasm_tramp_key(0, 1))),
791 (110..120, Some(array_to_wasm_tramp_key(0, 2))),
792 (120..130, Some(array_to_wasm_tramp_key(0, 5))),
793 (140..150, None),
794 ] {
795 for i in range {
796 eprintln!("Checking offset {i}");
797 let actual = table.func_by_text_offset(i);
798 assert_eq!(expected, actual);
799 }
800 }
801 }
802
803 #[test]
804 fn reverse_lookups() {
805 use arbitrary::{Result, Unstructured};
806
807 arbtest::arbtest(|u| run(u)).budget_ms(1_000);
808
809 fn run(u: &mut Unstructured<'_>) -> Result<()> {
810 let mut funcs = Vec::new();
811
812 for _ in 0..u.int_in_range(1..=200)? {
814 #[cfg(feature = "component-model")]
815 let choice = u.int_in_range(0..=6)?;
816 #[cfg(not(feature = "component-model"))]
817 let choice = u.int_in_range(0..=4)?;
818
819 let key = match choice {
820 0 => FuncKey::DefinedWasmFunction(idx(u, 10)?, idx(u, 200)?),
821 1 => FuncKey::ArrayToWasmTrampoline(idx(u, 10)?, idx(u, 200)?),
822 2 => FuncKey::WasmToArrayTrampoline(idx(u, 100)?),
823 3 => FuncKey::WasmToBuiltinTrampoline(u.arbitrary()?),
824 4 => FuncKey::PulleyHostCall(u.arbitrary()?),
825 #[cfg(feature = "component-model")]
826 5 => FuncKey::ComponentTrampoline(u.arbitrary()?, idx(u, 50)?),
827 #[cfg(feature = "component-model")]
828 6 => FuncKey::ResourceDropTrampoline,
829 _ => unreachable!(),
830 };
831 funcs.push(key);
832 }
833
834 funcs.sort();
837 funcs.dedup();
838
839 let mut builder = CompiledFunctionsTableBuilder::new();
840 let mut size = 0;
841 let mut expected = Vec::new();
842 for key in funcs {
843 let length = u.int_in_range(1..=10)?;
844 for _ in 0..length {
845 expected.push(key);
846 }
847 builder.push_func(
849 key,
850 FunctionLoc {
851 start: size,
852 length,
853 },
854 FilePos::none(),
855 );
856 size += length;
857 }
858 let index = builder.finish();
859
860 let mut expected = expected.iter();
861 for i in 0..size {
862 let actual = index.func_by_text_offset(i).unwrap();
864 assert_eq!(Some(&actual), expected.next());
865 }
866
867 Ok(())
868 }
869
870 fn idx<T>(u: &mut Unstructured<'_>, max: usize) -> Result<T>
871 where
872 T: EntityRef,
873 {
874 Ok(T::new(u.int_in_range(0..=max - 1)?))
875 }
876 }
877}