1use binius_utils::serialization::{DeserializeBytes, SerializationError, SerializeBytes};
4use bytes::{Buf, BufMut};
5
6use super::ValueIndex;
7use crate::{consts, error::ConstraintSystemError};
8
9#[derive(Clone, Debug, PartialEq, Eq)]
11pub struct ValueVecLayout {
12 pub n_const: usize,
14 pub n_inout: usize,
16 pub n_witness: usize,
18 pub n_internal: usize,
22
23 pub offset_inout: usize,
25 pub offset_witness: usize,
30 pub committed_total_len: usize,
33 pub n_scratch: usize,
35}
36
37impl ValueVecLayout {
38 pub const fn validate(&self) -> Result<(), ConstraintSystemError> {
45 if !self.offset_witness.is_power_of_two() {
46 return Err(ConstraintSystemError::PublicInputPowerOfTwo);
47 }
48
49 let pub_input_size = self.offset_witness;
50 if pub_input_size < consts::MIN_WORDS_PER_SEGMENT {
51 return Err(ConstraintSystemError::PublicInputTooShort { pub_input_size });
52 }
53
54 Ok(())
55 }
56
57 pub const fn n_public_words(&self) -> usize {
60 self.offset_witness
61 }
62
63 pub const fn log_public_words(&self) -> usize {
67 self.offset_witness.trailing_zeros() as usize
68 }
69
70 pub const fn n_non_public_words(&self) -> usize {
73 self.committed_total_len - self.offset_witness
74 }
75
76 pub(super) const fn is_padding(&self, index: ValueIndex) -> bool {
78 let idx = index.0 as usize;
79
80 if idx >= self.n_const && idx < self.offset_inout {
82 return true;
83 }
84
85 let end_of_inout = self.offset_inout + self.n_inout;
87 if idx >= end_of_inout && idx < self.offset_witness {
88 return true;
89 }
90
91 let end_of_internal = self.offset_witness + self.n_witness + self.n_internal;
93 if idx >= end_of_internal && idx < self.committed_total_len {
94 return true;
95 }
96
97 false
98 }
99
100 pub(super) const fn is_committed_oob(&self, index: ValueIndex) -> bool {
102 index.0 as usize >= self.committed_total_len
103 }
104}
105
106impl SerializeBytes for ValueVecLayout {
107 fn serialize(&self, mut write_buf: impl BufMut) -> Result<(), SerializationError> {
108 self.n_const.serialize(&mut write_buf)?;
109 self.n_inout.serialize(&mut write_buf)?;
110 self.n_witness.serialize(&mut write_buf)?;
111 self.n_internal.serialize(&mut write_buf)?;
112 self.offset_inout.serialize(&mut write_buf)?;
113 self.offset_witness.serialize(&mut write_buf)?;
114 self.committed_total_len.serialize(&mut write_buf)?;
115 self.n_scratch.serialize(write_buf)
116 }
117}
118
119impl DeserializeBytes for ValueVecLayout {
120 fn deserialize(mut read_buf: impl Buf) -> Result<Self, SerializationError>
121 where
122 Self: Sized,
123 {
124 let n_const = usize::deserialize(&mut read_buf)?;
125 let n_inout = usize::deserialize(&mut read_buf)?;
126 let n_witness = usize::deserialize(&mut read_buf)?;
127 let n_internal = usize::deserialize(&mut read_buf)?;
128 let offset_inout = usize::deserialize(&mut read_buf)?;
129 let offset_witness = usize::deserialize(&mut read_buf)?;
130 let committed_total_len = usize::deserialize(&mut read_buf)?;
131 let n_scratch = usize::deserialize(read_buf)?;
132
133 Ok(ValueVecLayout {
134 n_const,
135 n_inout,
136 n_witness,
137 n_internal,
138 offset_inout,
139 offset_witness,
140 committed_total_len,
141 n_scratch,
142 })
143 }
144}
145
146#[cfg(test)]
147mod tests {
148 use super::*;
149
150 #[test]
151 fn test_value_vec_layout_serialization_round_trip() {
152 let layout = ValueVecLayout {
153 n_const: 5,
154 n_inout: 3,
155 n_witness: 12,
156 n_internal: 7,
157 offset_inout: 8,
158 offset_witness: 16,
159 committed_total_len: 32,
160 n_scratch: 0,
161 };
162
163 let mut buf = Vec::new();
164 layout.serialize(&mut buf).unwrap();
165
166 let deserialized = ValueVecLayout::deserialize(&mut buf.as_slice()).unwrap();
167 assert_eq!(layout, deserialized);
168 }
169
170 #[test]
171 fn test_is_padding_comprehensive() {
172 let layout = ValueVecLayout {
174 n_const: 2, n_inout: 3, n_witness: 5, n_internal: 10, offset_inout: 4, offset_witness: 16, committed_total_len: 64, n_scratch: 0,
182 };
183
184 assert!(!layout.is_padding(ValueIndex(0)), "index 0 should be constant");
186 assert!(!layout.is_padding(ValueIndex(1)), "index 1 should be constant");
187
188 assert!(
190 layout.is_padding(ValueIndex(2)),
191 "index 2 should be padding between const and inout"
192 );
193 assert!(
194 layout.is_padding(ValueIndex(3)),
195 "index 3 should be padding between const and inout"
196 );
197
198 assert!(!layout.is_padding(ValueIndex(4)), "index 4 should be inout");
200 assert!(!layout.is_padding(ValueIndex(5)), "index 5 should be inout");
201 assert!(!layout.is_padding(ValueIndex(6)), "index 6 should be inout");
202
203 for i in 7..16 {
205 assert!(
206 layout.is_padding(ValueIndex(i)),
207 "index {} should be padding between inout and witness",
208 i
209 );
210 }
211
212 for i in 16..21 {
214 assert!(!layout.is_padding(ValueIndex(i)), "index {} should be witness", i);
215 }
216
217 for i in 21..31 {
219 assert!(!layout.is_padding(ValueIndex(i)), "index {} should be internal", i);
220 }
221
222 for i in 31..64 {
224 assert!(
225 layout.is_padding(ValueIndex(i)),
226 "index {} should be padding after internal",
227 i
228 );
229 }
230 }
231
232 #[test]
233 fn test_is_padding_minimal_layout() {
234 let layout = ValueVecLayout {
236 n_const: 4, n_inout: 4, n_witness: 4, n_internal: 4, offset_inout: 4, offset_witness: 8, committed_total_len: 16, n_scratch: 0,
244 };
245
246 for i in 0..16 {
248 assert!(
249 !layout.is_padding(ValueIndex(i)),
250 "index {} should not be padding in minimal layout",
251 i
252 );
253 }
254 }
255
256 #[test]
257 fn test_is_padding_public_section_min_size() {
258 let layout = ValueVecLayout {
260 n_const: 1, n_inout: 1, n_witness: 2, n_internal: 2, offset_inout: 4, offset_witness: 8, committed_total_len: 16, n_scratch: 0,
268 };
269
270 assert!(!layout.is_padding(ValueIndex(0)), "index 0 should be constant");
272
273 assert!(layout.is_padding(ValueIndex(1)), "index 1 should be padding");
275 assert!(layout.is_padding(ValueIndex(2)), "index 2 should be padding");
276 assert!(layout.is_padding(ValueIndex(3)), "index 3 should be padding");
277
278 assert!(!layout.is_padding(ValueIndex(4)), "index 4 should be inout");
280
281 assert!(layout.is_padding(ValueIndex(5)), "index 5 should be padding");
283 assert!(layout.is_padding(ValueIndex(6)), "index 6 should be padding");
284 assert!(layout.is_padding(ValueIndex(7)), "index 7 should be padding");
285
286 assert!(!layout.is_padding(ValueIndex(8)), "index 8 should be witness");
288 assert!(!layout.is_padding(ValueIndex(9)), "index 9 should be witness");
289
290 assert!(!layout.is_padding(ValueIndex(10)), "index 10 should be internal");
292 assert!(!layout.is_padding(ValueIndex(11)), "index 11 should be internal");
293
294 for i in 12..16 {
296 assert!(layout.is_padding(ValueIndex(i)), "index {} should be end padding", i);
297 }
298 }
299
300 #[test]
301 fn test_is_padding_boundary_conditions() {
302 let layout = ValueVecLayout {
303 n_const: 2,
304 n_inout: 2,
305 n_witness: 4,
306 n_internal: 4,
307 offset_inout: 4,
308 offset_witness: 8,
309 committed_total_len: 16,
310 n_scratch: 0,
311 };
312
313 assert!(!layout.is_padding(ValueIndex(1)), "last constant should not be padding");
315 assert!(layout.is_padding(ValueIndex(2)), "first padding after const should be padding");
316
317 assert!(layout.is_padding(ValueIndex(3)), "last padding before inout should be padding");
318 assert!(!layout.is_padding(ValueIndex(4)), "first inout should not be padding");
319
320 assert!(!layout.is_padding(ValueIndex(5)), "last inout should not be padding");
321 assert!(layout.is_padding(ValueIndex(6)), "first padding after inout should be padding");
322
323 assert!(layout.is_padding(ValueIndex(7)), "last padding before witness should be padding");
324 assert!(!layout.is_padding(ValueIndex(8)), "first witness should not be padding");
325
326 assert!(!layout.is_padding(ValueIndex(11)), "last witness should not be padding");
327 assert!(!layout.is_padding(ValueIndex(12)), "first internal should not be padding");
328
329 assert!(!layout.is_padding(ValueIndex(15)), "last internal should not be padding");
330 }
332
333 #[test]
334 fn test_is_padding_matches_compiler_requirements() {
335 let layout1 = ValueVecLayout {
343 n_const: 1,
344 n_inout: 1,
345 n_witness: 4,
346 n_internal: 4,
347 offset_inout: 1, offset_witness: 8, committed_total_len: 16,
350 n_scratch: 0,
351 };
352
353 assert!(!layout1.is_padding(ValueIndex(0)), "const should not be padding");
355 assert!(!layout1.is_padding(ValueIndex(1)), "inout should not be padding");
356 for i in 2..8 {
357 assert!(
358 layout1.is_padding(ValueIndex(i)),
359 "index {} should be padding to meet MIN_WORDS_PER_SEGMENT",
360 i
361 );
362 }
363
364 let layout2 = ValueVecLayout {
366 n_const: 4,
367 n_inout: 4,
368 n_witness: 8,
369 n_internal: 0,
370 offset_inout: 4,
371 offset_witness: 8, committed_total_len: 16,
373 n_scratch: 0,
374 };
375
376 for i in 0..8 {
378 assert!(!layout2.is_padding(ValueIndex(i)), "index {} should not be padding", i);
379 }
380
381 let layout3 = ValueVecLayout {
384 n_const: 5,
385 n_inout: 5,
386 n_witness: 16,
387 n_internal: 0,
388 offset_inout: 5,
389 offset_witness: 16, committed_total_len: 32,
391 n_scratch: 0,
392 };
393
394 for i in 0..5 {
396 assert!(!layout3.is_padding(ValueIndex(i)), "const {} should not be padding", i);
397 }
398 for i in 5..10 {
399 assert!(!layout3.is_padding(ValueIndex(i)), "inout {} should not be padding", i);
400 }
401 for i in 10..16 {
402 assert!(
403 layout3.is_padding(ValueIndex(i)),
404 "index {} should be padding for power-of-2 alignment",
405 i
406 );
407 }
408
409 let layout4 = ValueVecLayout {
411 n_const: 2, n_inout: 2, n_witness: 4, n_internal: 4, offset_inout: 8, offset_witness: 16, committed_total_len: 32, n_scratch: 0,
419 };
420
421 assert!(!layout4.is_padding(ValueIndex(0)));
423 assert!(!layout4.is_padding(ValueIndex(1)));
424
425 for i in 2..8 {
427 assert!(layout4.is_padding(ValueIndex(i)), "padding between const and inout at {}", i);
428 }
429
430 assert!(!layout4.is_padding(ValueIndex(8)));
432 assert!(!layout4.is_padding(ValueIndex(9)));
433
434 for i in 10..16 {
436 assert!(
437 layout4.is_padding(ValueIndex(i)),
438 "padding between inout and witness at {}",
439 i
440 );
441 }
442
443 for i in 16..20 {
445 assert!(!layout4.is_padding(ValueIndex(i)), "witness at {}", i);
446 }
447
448 for i in 20..24 {
450 assert!(!layout4.is_padding(ValueIndex(i)), "internal at {}", i);
451 }
452
453 for i in 24..32 {
455 assert!(layout4.is_padding(ValueIndex(i)), "padding after internal at {}", i);
456 }
457 }
458}