binius_core/constraint_system/
value_index.rs1use binius_utils::serialization::{DeserializeBytes, SerializationError, SerializeBytes};
4use bytes::{Buf, BufMut};
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
15#[repr(u8)]
16pub enum ValueSegment {
17 Constant = 0,
19 InOut = 1,
21 Private = 2,
23 Scratch = 3,
30}
31
32impl ValueSegment {
33 pub const ALL: [ValueSegment; 4] = [
35 ValueSegment::Constant,
36 ValueSegment::InOut,
37 ValueSegment::Private,
38 ValueSegment::Scratch,
39 ];
40
41 pub const fn is_referenceable(self) -> bool {
43 !matches!(self, ValueSegment::Scratch)
44 }
45
46 const fn from_tag(tag: u32) -> Self {
48 match tag {
49 0 => ValueSegment::Constant,
50 1 => ValueSegment::InOut,
51 2 => ValueSegment::Private,
52 3 => ValueSegment::Scratch,
53 _ => panic!("tag is masked to two bits"),
54 }
55 }
56}
57
58#[repr(transparent)]
71#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
72pub struct ValueIndex(u32);
73
74impl ValueIndex {
75 pub const INDEX_BITS: u32 = 30;
77
78 pub const SEGMENT_CAPACITY: u32 = 1 << Self::INDEX_BITS;
80
81 const INDEX_MASK: u32 = Self::SEGMENT_CAPACITY - 1;
83
84 pub const fn new(segment: ValueSegment, index: u32) -> Self {
90 assert!(index < Self::SEGMENT_CAPACITY, "value index out of range");
91 Self(((segment as u32) << Self::INDEX_BITS) | index)
92 }
93
94 pub const fn constant(index: u32) -> Self {
96 Self::new(ValueSegment::Constant, index)
97 }
98
99 pub const fn inout(index: u32) -> Self {
101 Self::new(ValueSegment::InOut, index)
102 }
103
104 pub const fn private(index: u32) -> Self {
106 Self::new(ValueSegment::Private, index)
107 }
108
109 pub const fn scratch(index: u32) -> Self {
111 Self::new(ValueSegment::Scratch, index)
112 }
113
114 pub const fn segment(self) -> ValueSegment {
116 ValueSegment::from_tag(self.0 >> Self::INDEX_BITS)
117 }
118
119 pub const fn index(self) -> u32 {
121 self.0 & Self::INDEX_MASK
122 }
123}
124
125impl std::fmt::Debug for ValueIndex {
127 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
128 write!(f, "ValueIndex({:?}, {})", self.segment(), self.index())
129 }
130}
131
132impl SerializeBytes for ValueIndex {
133 fn serialize(&self, write_buf: impl BufMut) -> Result<(), SerializationError> {
134 self.0.serialize(write_buf)
135 }
136}
137
138impl DeserializeBytes for ValueIndex {
139 fn deserialize(read_buf: impl Buf) -> Result<Self, SerializationError>
140 where
141 Self: Sized,
142 {
143 Ok(ValueIndex(u32::deserialize(read_buf)?))
144 }
145}
146
147#[cfg(test)]
148mod tests {
149 use super::*;
150
151 #[test]
152 fn round_trips_every_segment_through_the_packing() {
153 for segment in ValueSegment::ALL {
154 for index in [0, 1, 12345, ValueIndex::SEGMENT_CAPACITY - 1] {
155 let value_index = ValueIndex::new(segment, index);
156 assert_eq!(value_index.segment(), segment);
157 assert_eq!(value_index.index(), index);
158 }
159 }
160 }
161
162 #[test]
163 fn orders_words_by_segment_then_index() {
164 let ascending = [
166 ValueIndex::constant(0),
167 ValueIndex::constant(1),
168 ValueIndex::inout(0),
169 ValueIndex::private(0),
170 ValueIndex::private(7),
171 ValueIndex::scratch(0),
172 ];
173 assert!(ascending.is_sorted());
174 }
175
176 #[test]
177 #[should_panic(expected = "value index out of range")]
178 fn rejects_an_index_past_the_segment_capacity() {
179 ValueIndex::scratch(ValueIndex::SEGMENT_CAPACITY);
180 }
181
182 #[test]
183 fn only_scratch_is_unreferenceable() {
184 for segment in ValueSegment::ALL {
185 assert_eq!(segment.is_referenceable(), segment != ValueSegment::Scratch);
186 }
187 }
188
189 #[test]
190 fn test_value_index_serialization_round_trip() {
191 let value_index = ValueIndex::private(12345);
192
193 let mut buf = Vec::new();
194 value_index.serialize(&mut buf).unwrap();
195
196 let deserialized = ValueIndex::deserialize(&mut buf.as_slice()).unwrap();
197
198 assert_eq!(value_index, deserialized);
199 }
200}