binius_core/constraint_system/
value_vec.rs1use std::ops::{Deref, DerefMut, Index, IndexMut};
4
5use bytemuck::{Pod, Zeroable};
6
7use super::{ShiftedValueIndex, ValueIndex, ValueVecLayout};
8use crate::{error::ConstraintSystemError, word::Word};
9
10#[derive(Clone, Copy, Debug)]
15#[repr(C, align(16))]
16struct WordPair([Word; 2]);
17
18unsafe impl Zeroable for WordPair {}
22unsafe impl Pod for WordPair {}
23
24#[derive(Clone, Debug)]
38struct AlignedWords {
39 blocks: Vec<WordPair>,
41 len: usize,
43}
44
45impl AlignedWords {
46 fn zeroed(len: usize) -> Self {
48 Self {
49 blocks: vec![WordPair([Word::ZERO; 2]); len.div_ceil(2)],
51 len,
52 }
53 }
54}
55
56impl Deref for AlignedWords {
57 type Target = [Word];
58
59 fn deref(&self) -> &[Word] {
60 &bytemuck::cast_slice(&self.blocks)[..self.len]
63 }
64}
65
66impl DerefMut for AlignedWords {
67 fn deref_mut(&mut self) -> &mut [Word] {
68 &mut bytemuck::cast_slice_mut(&mut self.blocks)[..self.len]
70 }
71}
72
73#[derive(Clone, Debug)]
85pub struct ValueVec {
86 layout: ValueVecLayout,
88 data: AlignedWords,
90}
91
92impl ValueVec {
93 pub fn new(layout: ValueVecLayout) -> ValueVec {
97 let size = layout.combined_len() + layout.n_scratch;
98 ValueVec {
99 layout,
100 data: AlignedWords::zeroed(size),
101 }
102 }
103
104 pub fn new_from_data(
108 layout: ValueVecLayout,
109 public: Vec<Word>,
110 private: Vec<Word>,
111 ) -> Result<ValueVec, ConstraintSystemError> {
112 if public.len() != layout.n_public_words() {
113 return Err(ConstraintSystemError::ValueVecLenMismatch {
114 expected: layout.n_public_words(),
115 actual: public.len(),
116 });
117 }
118 if private.len() != layout.n_hidden_words {
119 return Err(ConstraintSystemError::ValueVecLenMismatch {
120 expected: layout.n_hidden_words,
121 actual: private.len(),
122 });
123 }
124
125 let full_len = layout.combined_len() + layout.n_scratch;
127 let mut data = AlignedWords::zeroed(full_len);
129 data[..public.len()].copy_from_slice(&public);
131 data[public.len()..public.len() + private.len()].copy_from_slice(&private);
132
133 Ok(ValueVec { layout, data })
134 }
135
136 pub const fn size(&self) -> usize {
138 self.layout.combined_len()
139 }
140
141 pub fn public(&self) -> &[Word] {
143 &self.data[..self.layout.offset_witness]
144 }
145
146 pub fn non_public(&self) -> &[Word] {
148 &self.data[self.layout.offset_witness..self.layout.combined_len()]
149 }
150
151 pub fn witness(&self) -> &[Word] {
153 let start = self.layout.offset_witness;
154 let end = start + self.layout.n_witness;
155 &self.data[start..end]
156 }
157
158 pub fn combined_witness(&self) -> &[Word] {
160 &self.data[..self.layout.combined_len()]
161 }
162
163 #[inline]
168 pub fn eval_operand(&self, operand: &[ShiftedValueIndex]) -> Word {
169 operand
171 .iter()
172 .fold(Word::ZERO, |acc, term| acc ^ term.eval(self))
173 }
174}
175
176impl Index<ValueIndex> for ValueVec {
177 type Output = Word;
178
179 fn index(&self, index: ValueIndex) -> &Self::Output {
180 &self.data[index.0 as usize]
181 }
182}
183
184impl IndexMut<ValueIndex> for ValueVec {
185 fn index_mut(&mut self, index: ValueIndex) -> &mut Self::Output {
186 &mut self.data[index.0 as usize]
187 }
188}
189
190#[cfg(test)]
191mod tests {
192 use proptest::{collection, prelude::any, prop_assert_eq, proptest};
193
194 use super::*;
195
196 #[test]
197 fn split_values_vec_and_combine() {
198 let values = ValueVec::new(ValueVecLayout {
199 n_const: 2,
200 n_inout: 2,
201 n_witness: 2,
202 n_internal: 2,
203 offset_inout: 2,
204 offset_witness: 4,
205 n_hidden_words: 4,
206 n_scratch: 0,
207 });
208
209 let public = values.public();
210 let non_public = values.non_public();
211 let combined =
212 ValueVec::new_from_data(values.layout.clone(), public.to_vec(), non_public.to_vec())
213 .unwrap();
214 assert_eq!(combined.combined_witness(), values.combined_witness());
215 }
216
217 fn assert_16_byte_aligned(words: &[Word]) {
219 assert_eq!(words.as_ptr() as usize % 16, 0);
220 }
221
222 #[test]
223 fn zeroed_is_aligned_zero_filled_and_correct_length() {
224 for len in [0, 1, 2, 3, 16, 17] {
230 let words = AlignedWords::zeroed(len);
231 assert_eq!(words.len(), len);
233 assert_16_byte_aligned(&words);
235 assert!(words.iter().all(|&w| w == Word::ZERO));
237 }
238 }
239
240 #[test]
241 fn deref_mut_writes_are_visible_through_deref() {
242 let mut words = AlignedWords::zeroed(5);
244 for (i, w) in words.iter_mut().enumerate() {
246 *w = Word::from_u64(i as u64 + 1);
247 }
248 assert_eq!(
250 &*words,
251 &[
252 Word::from_u64(1),
253 Word::from_u64(2),
254 Word::from_u64(3),
255 Word::from_u64(4),
256 Word::from_u64(5),
257 ]
258 );
259 }
260
261 proptest! {
262 #[test]
263 fn value_vec_preserves_words_and_alignment(
264 public in collection::vec(any::<u64>(), 4..32usize),
265 n_witness in 0..32usize,
266 n_scratch in 0..16usize,
267 ) {
268 let public: Vec<Word> = public.into_iter().map(Word).collect();
270 let private: Vec<Word> = (0..n_witness).map(|i| Word::from_u64(0xdead_0000 + i as u64)).collect();
271
272 let offset_witness = public.len().next_power_of_two();
279 let n_hidden_words = private.len();
280 let layout = ValueVecLayout {
281 n_const: 0,
282 n_inout: public.len(),
283 n_witness: private.len(),
284 n_internal: 0,
285 offset_inout: 0,
286 offset_witness,
287 n_hidden_words,
288 n_scratch,
289 };
290
291 let mut public_padded = public;
293 public_padded.resize(offset_witness, Word::ZERO);
294
295 let layout_combined_len = layout.combined_len();
296 let vv = ValueVec::new_from_data(layout, public_padded.clone(), private.clone()).unwrap();
297
298 assert_16_byte_aligned(vv.combined_witness());
300 prop_assert_eq!(vv.public(), &public_padded[..]);
302 prop_assert_eq!(vv.witness(), &private[..]);
303
304 let combined_len = layout_combined_len;
306 for i in combined_len..combined_len + n_scratch {
307 prop_assert_eq!(vv[ValueIndex(i as u32)], Word::ZERO);
308 }
309 }
310 }
311}