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")]
559 FuncKeyKind::ComponentTrampoline
560 | FuncKeyKind::ResourceDropTrampoline
561 | FuncKeyKind::UnsafeIntrinsic => true,
562 }
563 }
564
565 fn has_src_locs(kind: FuncKeyKind) -> bool {
567 match kind {
568 FuncKeyKind::DefinedWasmFunction => true,
569 FuncKeyKind::ArrayToWasmTrampoline
570 | FuncKeyKind::WasmToArrayTrampoline
571 | FuncKeyKind::WasmToBuiltinTrampoline
572 | FuncKeyKind::PatchableToBuiltinTrampoline
573 | FuncKeyKind::PulleyHostCall
574 | FuncKeyKind::ModuleStartup => false,
575 #[cfg(feature = "component-model")]
576 FuncKeyKind::ComponentTrampoline
577 | FuncKeyKind::ResourceDropTrampoline
578 | FuncKeyKind::UnsafeIntrinsic => false,
579 }
580 }
581}
582
583#[derive(Serialize, Deserialize)]
588pub struct CompiledModuleInfo {
589 pub module: Module,
591
592 pub meta: Metadata,
594
595 pub func_names: Vec<FunctionName>,
597
598 pub checksum: WasmChecksum,
600}
601
602#[derive(Serialize, Deserialize)]
605pub struct FunctionName {
606 pub idx: FuncIndex,
608 pub offset: u32,
611 pub len: u32,
613}
614
615#[derive(Serialize, Deserialize)]
617pub struct Metadata {
618 pub has_unparsed_debuginfo: bool,
621
622 pub code_section_offset: u64,
624
625 pub has_wasm_debuginfo: bool,
631
632 pub dwarf: Vec<(u8, Range<u64>)>,
635}
636
637#[derive(Serialize, Deserialize, Hash, Eq, PartialEq, Debug)]
639pub enum FlagValue<'a> {
640 Enum(&'a str),
642 Num(u8),
644 Bool(bool),
646}
647
648impl fmt::Display for FlagValue<'_> {
649 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
650 match self {
651 Self::Enum(v) => v.fmt(f),
652 Self::Num(v) => v.fmt(f),
653 Self::Bool(v) => v.fmt(f),
654 }
655 }
656}
657
658pub enum ObjectKind {
660 Module,
662 Component,
664}
665
666#[cfg(test)]
667mod tests {
668 use super::*;
669 use crate::{DefinedFuncIndex, StaticModuleIndex};
670
671 fn func_loc(range: Range<u32>) -> FunctionLoc {
672 FunctionLoc {
673 start: range.start,
674 length: range.end - range.start,
675 }
676 }
677
678 fn def_func_key(m: u32, f: u32) -> FuncKey {
679 FuncKey::DefinedWasmFunction(
680 StaticModuleIndex::from_u32(m),
681 DefinedFuncIndex::from_u32(f),
682 )
683 }
684
685 fn array_to_wasm_tramp_key(m: u32, f: u32) -> FuncKey {
686 FuncKey::ArrayToWasmTrampoline(
687 StaticModuleIndex::from_u32(m),
688 DefinedFuncIndex::from_u32(f),
689 )
690 }
691
692 fn make_test_table() -> CompiledFunctionsTable {
693 let mut builder = CompiledFunctionsTableBuilder::new();
694
695 builder
696 .push_func(def_func_key(0, 0), func_loc(0..10), FilePos::new(111))
698 .push_func(def_func_key(0, 1), func_loc(10..20), FilePos::new(222))
699 .push_func(def_func_key(0, 2), func_loc(20..30), FilePos::none())
700 .push_func(def_func_key(0, 5), func_loc(30..40), FilePos::new(333))
702 .push_func(
704 array_to_wasm_tramp_key(0, 1),
705 func_loc(100..110),
706 FilePos::none(),
707 )
708 .push_func(
709 array_to_wasm_tramp_key(0, 2),
710 func_loc(110..120),
711 FilePos::none(),
712 )
713 .push_func(
714 array_to_wasm_tramp_key(0, 5),
715 func_loc(120..130),
716 FilePos::none(),
717 );
718
719 builder.finish()
720 }
721
722 #[test]
723 fn src_locs() {
724 let table = make_test_table();
725
726 for (key, expected) in [
727 (def_func_key(0, 0), Some(FilePos::new(111))),
728 (def_func_key(0, 1), Some(FilePos::new(222))),
729 (def_func_key(0, 2), None),
730 (def_func_key(0, 3), None),
731 (def_func_key(0, 4), None),
732 (def_func_key(0, 5), Some(FilePos::new(333))),
733 (array_to_wasm_tramp_key(0, 0), None),
734 (array_to_wasm_tramp_key(0, 1), None),
735 (array_to_wasm_tramp_key(0, 2), None),
736 (array_to_wasm_tramp_key(0, 3), None),
737 (array_to_wasm_tramp_key(0, 4), None),
738 (array_to_wasm_tramp_key(0, 5), None),
739 ] {
740 eprintln!("Checking key {key:?}");
741 let actual = table.src_loc(key);
742 assert_eq!(expected, actual);
743 }
744 }
745
746 #[test]
747 fn func_locs() {
748 let table = make_test_table();
749
750 for (key, expected) in [
751 (def_func_key(0, 0), Some(0)),
752 (def_func_key(0, 1), Some(10)),
753 (def_func_key(0, 2), Some(20)),
754 (def_func_key(0, 3), None),
755 (def_func_key(0, 4), None),
756 (def_func_key(0, 5), Some(30)),
757 (array_to_wasm_tramp_key(0, 0), None),
758 (array_to_wasm_tramp_key(0, 1), Some(100)),
759 (array_to_wasm_tramp_key(0, 2), Some(110)),
760 (array_to_wasm_tramp_key(0, 3), None),
761 (array_to_wasm_tramp_key(0, 4), None),
762 (array_to_wasm_tramp_key(0, 5), Some(120)),
763 ] {
764 let actual = table.func_loc(key);
765 match (expected, actual) {
766 (None, None) => {}
767 (Some(expected), Some(actual)) => assert_eq!(expected, actual.start),
768 (None, Some(actual)) => {
769 panic!("expected no function location for {key:?}, got {actual:?}")
770 }
771 (Some(_), None) => {
772 panic!("expected a function location for {key:?}, but got nothing")
773 }
774 }
775 }
776 }
777
778 #[test]
779 fn reverse_func_locs() {
780 let table = make_test_table();
781
782 for (range, expected) in [
783 (0..10, Some(def_func_key(0, 0))),
784 (10..20, Some(def_func_key(0, 1))),
785 (20..30, Some(def_func_key(0, 2))),
786 (30..40, Some(def_func_key(0, 5))),
787 (40..100, None),
788 (100..110, Some(array_to_wasm_tramp_key(0, 1))),
789 (110..120, Some(array_to_wasm_tramp_key(0, 2))),
790 (120..130, Some(array_to_wasm_tramp_key(0, 5))),
791 (140..150, None),
792 ] {
793 for i in range {
794 eprintln!("Checking offset {i}");
795 let actual = table.func_by_text_offset(i);
796 assert_eq!(expected, actual);
797 }
798 }
799 }
800
801 #[test]
802 fn reverse_lookups() {
803 use arbitrary::{Result, Unstructured};
804
805 arbtest::arbtest(|u| run(u)).budget_ms(1_000);
806
807 fn run(u: &mut Unstructured<'_>) -> Result<()> {
808 let mut funcs = Vec::new();
809
810 for _ in 0..u.int_in_range(1..=200)? {
812 #[cfg(feature = "component-model")]
813 let choice = u.int_in_range(0..=6)?;
814 #[cfg(not(feature = "component-model"))]
815 let choice = u.int_in_range(0..=4)?;
816
817 let key = match choice {
818 0 => FuncKey::DefinedWasmFunction(idx(u, 10)?, idx(u, 200)?),
819 1 => FuncKey::ArrayToWasmTrampoline(idx(u, 10)?, idx(u, 200)?),
820 2 => FuncKey::WasmToArrayTrampoline(idx(u, 100)?),
821 3 => FuncKey::WasmToBuiltinTrampoline(u.arbitrary()?),
822 4 => FuncKey::PulleyHostCall(u.arbitrary()?),
823 #[cfg(feature = "component-model")]
824 5 => FuncKey::ComponentTrampoline(u.arbitrary()?, idx(u, 50)?),
825 #[cfg(feature = "component-model")]
826 6 => FuncKey::ResourceDropTrampoline,
827 _ => unreachable!(),
828 };
829 funcs.push(key);
830 }
831
832 funcs.sort();
835 funcs.dedup();
836
837 let mut builder = CompiledFunctionsTableBuilder::new();
838 let mut size = 0;
839 let mut expected = Vec::new();
840 for key in funcs {
841 let length = u.int_in_range(1..=10)?;
842 for _ in 0..length {
843 expected.push(key);
844 }
845 builder.push_func(
847 key,
848 FunctionLoc {
849 start: size,
850 length,
851 },
852 FilePos::none(),
853 );
854 size += length;
855 }
856 let index = builder.finish();
857
858 let mut expected = expected.iter();
859 for i in 0..size {
860 let actual = index.func_by_text_offset(i).unwrap();
862 assert_eq!(Some(&actual), expected.next());
863 }
864
865 Ok(())
866 }
867
868 fn idx<T>(u: &mut Unstructured<'_>, max: usize) -> Result<T>
869 where
870 T: EntityRef,
871 {
872 Ok(T::new(u.int_in_range(0..=max - 1)?))
873 }
874 }
875}