binius_core/constraint_system/
value_vec.rs1use std::ops::{Deref, DerefMut, Index, IndexMut};
3
4use bytemuck::{Pod, Zeroable};
5
6use super::{ShiftedValueIndex, ValueIndex, ValueVecLayout};
7use crate::{error::ConstraintSystemError, word::Word};
8
9#[derive(Clone, Copy, Debug)]
14#[repr(C, align(16))]
15struct WordPair([Word; 2]);
16
17unsafe impl Zeroable for WordPair {}
21unsafe impl Pod for WordPair {}
22
23#[derive(Clone, Debug)]
37struct AlignedWords {
38 blocks: Vec<WordPair>,
40 len: usize,
42}
43
44impl AlignedWords {
45 fn zeroed(len: usize) -> Self {
47 Self {
48 blocks: vec![WordPair([Word::ZERO; 2]); len.div_ceil(2)],
50 len,
51 }
52 }
53}
54
55impl Deref for AlignedWords {
56 type Target = [Word];
57
58 fn deref(&self) -> &[Word] {
59 &bytemuck::cast_slice(&self.blocks)[..self.len]
62 }
63}
64
65impl DerefMut for AlignedWords {
66 fn deref_mut(&mut self) -> &mut [Word] {
67 &mut bytemuck::cast_slice_mut(&mut self.blocks)[..self.len]
69 }
70}
71
72#[derive(Clone, Debug)]
84pub struct ValueVec {
85 layout: ValueVecLayout,
87 data: AlignedWords,
89}
90
91impl ValueVec {
92 pub fn new(layout: ValueVecLayout) -> ValueVec {
96 let size = layout.committed_total_len + layout.n_scratch;
97 ValueVec {
98 layout,
99 data: AlignedWords::zeroed(size),
100 }
101 }
102
103 pub fn new_from_data(
107 layout: ValueVecLayout,
108 public: Vec<Word>,
109 private: Vec<Word>,
110 ) -> Result<ValueVec, ConstraintSystemError> {
111 let committed_len = public.len() + private.len();
112 if committed_len != layout.committed_total_len {
113 return Err(ConstraintSystemError::ValueVecLenMismatch {
114 expected: layout.committed_total_len,
115 actual: committed_len,
116 });
117 }
118
119 let full_len = layout.committed_total_len + layout.n_scratch;
121 let mut data = AlignedWords::zeroed(full_len);
123 data[..public.len()].copy_from_slice(&public);
125 data[public.len()..committed_len].copy_from_slice(&private);
127
128 Ok(ValueVec { layout, data })
129 }
130
131 pub const fn size(&self) -> usize {
133 self.layout.committed_total_len
134 }
135
136 pub fn get(&self, index: usize) -> Word {
140 self.data[index]
141 }
142
143 pub fn set(&mut self, index: usize, value: Word) {
147 self.data[index] = value;
148 }
149
150 pub fn public(&self) -> &[Word] {
152 &self.data[..self.layout.offset_witness]
153 }
154
155 pub fn non_public(&self) -> &[Word] {
157 &self.data[self.layout.offset_witness..self.layout.committed_total_len]
158 }
159
160 pub fn witness(&self) -> &[Word] {
162 let start = self.layout.offset_witness;
163 let end = start + self.layout.n_witness;
164 &self.data[start..end]
165 }
166
167 pub fn combined_witness(&self) -> &[Word] {
169 let start = 0;
170 let end = self.layout.committed_total_len;
171 &self.data[start..end]
172 }
173
174 #[inline]
179 pub fn eval_operand(&self, operand: &[ShiftedValueIndex]) -> Word {
180 operand
182 .iter()
183 .fold(Word::ZERO, |acc, term| acc ^ term.eval(self))
184 }
185}
186
187impl Index<ValueIndex> for ValueVec {
188 type Output = Word;
189
190 fn index(&self, index: ValueIndex) -> &Self::Output {
191 &self.data[index.0 as usize]
192 }
193}
194
195impl IndexMut<ValueIndex> for ValueVec {
196 fn index_mut(&mut self, index: ValueIndex) -> &mut Self::Output {
197 &mut self.data[index.0 as usize]
198 }
199}
200
201#[cfg(test)]
202mod tests {
203 use proptest::{collection, prelude::any, prop_assert_eq, proptest};
204
205 use super::*;
206
207 #[test]
208 fn split_values_vec_and_combine() {
209 let values = ValueVec::new(ValueVecLayout {
210 n_const: 2,
211 n_inout: 2,
212 n_witness: 2,
213 n_internal: 2,
214 offset_inout: 2,
215 offset_witness: 4,
216 committed_total_len: 8,
217 n_scratch: 0,
218 });
219
220 let public = values.public();
221 let non_public = values.non_public();
222 let combined =
223 ValueVec::new_from_data(values.layout.clone(), public.to_vec(), non_public.to_vec())
224 .unwrap();
225 assert_eq!(combined.combined_witness(), values.combined_witness());
226 }
227
228 fn assert_16_byte_aligned(words: &[Word]) {
230 assert_eq!(words.as_ptr() as usize % 16, 0);
231 }
232
233 #[test]
234 fn zeroed_is_aligned_zero_filled_and_correct_length() {
235 for len in [0, 1, 2, 3, 16, 17] {
241 let words = AlignedWords::zeroed(len);
242 assert_eq!(words.len(), len);
244 assert_16_byte_aligned(&words);
246 assert!(words.iter().all(|&w| w == Word::ZERO));
248 }
249 }
250
251 #[test]
252 fn deref_mut_writes_are_visible_through_deref() {
253 let mut words = AlignedWords::zeroed(5);
255 for (i, w) in words.iter_mut().enumerate() {
257 *w = Word::from_u64(i as u64 + 1);
258 }
259 assert_eq!(
261 &*words,
262 &[
263 Word::from_u64(1),
264 Word::from_u64(2),
265 Word::from_u64(3),
266 Word::from_u64(4),
267 Word::from_u64(5),
268 ]
269 );
270 }
271
272 proptest! {
273 #[test]
274 fn value_vec_preserves_words_and_alignment(
275 public in collection::vec(any::<u64>(), 4..32usize),
276 n_witness in 0..32usize,
277 n_scratch in 0..16usize,
278 ) {
279 let public: Vec<Word> = public.into_iter().map(Word).collect();
281 let private: Vec<Word> = (0..n_witness).map(|i| Word::from_u64(0xdead_0000 + i as u64)).collect();
282
283 let offset_witness = public.len().next_power_of_two();
290 let committed_total_len = offset_witness + private.len();
291 let layout = ValueVecLayout {
292 n_const: 0,
293 n_inout: public.len(),
294 n_witness: private.len(),
295 n_internal: 0,
296 offset_inout: 0,
297 offset_witness,
298 committed_total_len,
299 n_scratch,
300 };
301
302 let mut public_padded = public;
304 public_padded.resize(offset_witness, Word::ZERO);
305
306 let vv = ValueVec::new_from_data(layout, public_padded.clone(), private.clone()).unwrap();
307
308 assert_16_byte_aligned(vv.combined_witness());
310 prop_assert_eq!(vv.public(), &public_padded[..]);
312 prop_assert_eq!(vv.witness(), &private[..]);
313
314 for i in committed_total_len..committed_total_len + n_scratch {
316 prop_assert_eq!(vv.get(i), Word::ZERO);
317 }
318 }
319 }
320}