plotnik_lib/ir/slice.rs
1//! Relative range within a segment.
2//!
3//! `start_index` is an **element index**, not a byte offset. This naming
4//! distinguishes it from byte offsets like `StringRef.offset`.
5//!
6//! This struct is 8 bytes with 4-byte alignment for efficient access.
7//! Type safety is provided through generic methods, not stored PhantomData.
8
9use std::marker::PhantomData;
10
11/// Relative range within a compiled query segment.
12///
13/// Used for variable-length data (successors, effects, negated fields, type members).
14/// The slice references elements by index into the corresponding segment array.
15///
16/// Layout: 8 bytes (4 + 2 + 2), align 4.
17#[repr(C)]
18#[derive(Clone, Copy)]
19pub struct Slice<T> {
20 /// Element index into the segment array (NOT byte offset).
21 start_index: u32,
22 /// Number of elements. 65k elements per slice is sufficient.
23 len: u16,
24 _pad: u16,
25 _phantom: PhantomData<fn() -> T>,
26}
27
28// Compile-time size/alignment verification
29const _: () = assert!(size_of::<Slice<u8>>() == 8);
30const _: () = assert!(align_of::<Slice<u8>>() == 4);
31
32impl<T> Slice<T> {
33 /// Creates a new slice.
34 #[inline]
35 pub const fn new(start_index: u32, len: u16) -> Self {
36 Self {
37 start_index,
38 len,
39 _pad: 0,
40 _phantom: PhantomData,
41 }
42 }
43
44 /// Creates an empty slice.
45 #[inline]
46 pub const fn empty() -> Self {
47 Self::new(0, 0)
48 }
49
50 /// Returns the start index (element index, not byte offset).
51 #[inline]
52 pub fn start_index(&self) -> u32 {
53 self.start_index
54 }
55
56 /// Returns the number of elements.
57 #[inline]
58 pub fn len(&self) -> u16 {
59 self.len
60 }
61
62 /// Returns true if the slice is empty.
63 #[inline]
64 pub fn is_empty(&self) -> bool {
65 self.len == 0
66 }
67
68 /// Creates a slice encoding an inner type ID (for wrapper TypeDef).
69 /// The `start_index` stores the TypeId as u32, `len` is 0.
70 #[inline]
71 pub const fn from_inner_type(type_id: u16) -> Self {
72 Self::new(type_id as u32, 0)
73 }
74}
75
76impl<T> Default for Slice<T> {
77 fn default() -> Self {
78 Self::empty()
79 }
80}
81
82impl<T> PartialEq for Slice<T> {
83 fn eq(&self, other: &Self) -> bool {
84 self.start_index == other.start_index && self.len == other.len
85 }
86}
87
88impl<T> Eq for Slice<T> {}
89
90impl<T> std::fmt::Debug for Slice<T> {
91 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
92 f.debug_struct("Slice")
93 .field("start_index", &self.start_index)
94 .field("len", &self.len)
95 .finish()
96 }
97}