1use std::ops::{Range, RangeInclusive};
5
6use binius_core::word::Word;
7use binius_frontend::{CircuitBuilder, Wire, WitnessFiller};
8
9#[derive(Clone)]
46pub struct ByteVec {
47 pub len_bytes: Wire,
49 pub data: Vec<Wire>,
52 pub len_range: RangeInclusive<usize>,
55}
56
57impl ByteVec {
58 pub fn new(data: Vec<Wire>, len_bytes: Wire) -> Self {
63 let capacity = data.len() * Word::BYTES;
64 Self::new_with_len_range(data, len_bytes, 0..=capacity)
65 }
66
67 pub fn new_with_len_range(
76 data: Vec<Wire>,
77 len_bytes: Wire,
78 len_range: RangeInclusive<usize>,
79 ) -> Self {
80 let capacity = data.len() * Word::BYTES;
81 assert!(len_range.start() <= len_range.end(), "invalid len_range: start > end");
82 assert!(
83 *len_range.end() <= capacity,
84 "len_range.end {} exceeds capacity {capacity}",
85 len_range.end()
86 );
87 Self {
88 len_bytes,
89 data,
90 len_range,
91 }
92 }
93
94 pub fn new_const_len(b: &CircuitBuilder, data: Vec<Wire>, len: usize) -> Self {
100 let len_bytes = b.add_constant_64(len as u64);
101 Self::new_with_len_range(data, len_bytes, len..=len)
102 }
103
104 pub fn new_inout(b: &CircuitBuilder, max_len: usize) -> Self {
106 let len_bytes = b.add_inout();
107 let data = (0..max_len).map(|_| b.add_inout()).collect();
108 Self::new(data, len_bytes)
109 }
110
111 pub fn new_witness(b: &CircuitBuilder, max_len: usize) -> Self {
113 let len_bytes = b.add_inout();
114 let data = (0..max_len).map(|_| b.add_witness()).collect();
115 Self::new(data, len_bytes)
116 }
117
118 pub fn populate_len_bytes(&self, w: &mut WitnessFiller<'_>, len_bytes: usize) {
123 self.assert_len_in_range(len_bytes);
124 w[self.len_bytes] = Word(len_bytes as u64);
125 }
126
127 fn assert_len_in_range(&self, len_bytes: usize) {
129 assert!(
130 self.len_range.contains(&len_bytes),
131 "len_bytes {len_bytes} outside len_range {:?}",
132 self.len_range
133 );
134 }
135
136 pub fn populate_bytes_le(&self, w: &mut WitnessFiller<'_>, bytes: &[u8]) {
143 self.assert_len_in_range(bytes.len());
144 w.pack_bytes_le(&self.data, bytes);
145 w[self.len_bytes] = Word(bytes.len() as u64);
146 }
147
148 pub fn populate_data(&self, w: &mut WitnessFiller<'_>, data_bytes: &[u8]) {
156 assert!(
157 data_bytes.len() <= self.max_len_bytes(),
158 "vector data length {} exceeds maximum {}",
159 data_bytes.len(),
160 self.max_len_bytes()
161 );
162
163 for (i, chunk) in data_bytes.chunks(8).enumerate() {
165 if i < self.data.len() {
166 let mut word = 0u64;
167 for (j, &byte) in chunk.iter().enumerate() {
168 word |= (byte as u64) << (j * 8);
169 }
170 w[self.data[i]] = Word(word);
171 }
172 }
173
174 for i in data_bytes.len().div_ceil(8)..self.data.len() {
176 w[self.data[i]] = Word::ZERO;
177 }
178 }
179
180 pub const fn max_len_bytes(&self) -> usize {
182 self.data.len() * 8
183 }
184
185 pub fn truncate(&self, b: &CircuitBuilder, num_wires: usize) -> ByteVec {
190 assert!(num_wires <= self.data.len(), "num_wires must be less than self.data.len()");
191
192 let trimmed_wires = self.data[0..num_wires].to_vec();
193 ByteVec::new_const_len(b, trimmed_wires, num_wires << 3)
194 }
195
196 pub fn slice_const_range(&self, b: &CircuitBuilder, range: Range<usize>) -> ByteVec {
229 assert!(range.start <= range.end, "Invalid range: start > end");
230 assert!(
231 range.end <= *self.len_range.end(),
232 "Range end {} exceeds length bound {}",
233 range.end,
234 self.len_range.end()
235 );
236
237 let slice_len = range.len();
238
239 if slice_len == 0 {
241 return ByteVec::new_const_len(b, Vec::new(), 0);
242 }
243
244 if self.len_range.start() != self.len_range.end() {
249 let range_end_const = b.add_constant_64(range.end as u64);
250 let valid = b.icmp_ule(range_end_const, self.len_bytes);
251 b.assert_true("slice_range_check", valid);
252 }
253
254 let output_words = extract_const_range(b, &self.data, range);
255 ByteVec::new_const_len(b, output_words, slice_len)
256 }
257}
258
259pub(crate) fn extract_const_range(
274 b: &CircuitBuilder,
275 data: &[Wire],
276 range: Range<usize>,
277) -> Vec<Wire> {
278 assert!(range.start <= range.end, "invalid range: start > end");
279 assert!(
280 range.end <= data.len() * Word::BYTES,
281 "range.end {} exceeds capacity {}",
282 range.end,
283 data.len() * Word::BYTES
284 );
285
286 let slice_len = range.len();
287 if slice_len == 0 {
288 return Vec::new();
289 }
290
291 let start_word_idx = range.start / Word::BYTES;
292 let last_word_index = (range.end - 1) / Word::BYTES;
294 let byte_offset = range.start % Word::BYTES;
295 let num_output_words = slice_len.div_ceil(Word::BYTES);
296
297 if byte_offset == 0 {
300 data[start_word_idx..start_word_idx + num_output_words].to_vec()
302 } else {
303 (0..num_output_words)
305 .map(|i| {
306 let source_idx = start_word_idx + i;
307
308 let current_word = data[source_idx];
309 let shifted_current = b.shr(current_word, (byte_offset * 8) as u32);
311
312 if source_idx < last_word_index {
313 let next_word = data[source_idx + 1];
314 let shifted_next = b.shl(next_word, ((Word::BYTES - byte_offset) * 8) as u32);
316 b.bxor(shifted_current, shifted_next)
318 } else {
319 shifted_current
320 }
321 })
322 .collect()
323 }
324}
325
326#[cfg(test)]
327mod tests {
328
329 use super::{ByteVec, CircuitBuilder, Word};
330
331 #[test]
332 fn test_slice_const_range_aligned() {
333 let b = CircuitBuilder::new();
334
335 let byte_vec = ByteVec::new_witness(&b, 4);
337
338 let slice = byte_vec.slice_const_range(&b, 8..16);
340
341 assert_eq!(slice.data.len(), 1, "Slice should have 1 word");
342
343 let circuit = b.build();
344 let mut filler = circuit.new_witness_filler();
345
346 let input_data: Vec<u8> = (0..32).map(|i| i as u8).collect();
348 byte_vec.populate_bytes_le(&mut filler, &input_data);
349
350 let expected_word = 0x0f0e0d0c0b0a0908u64;
352 filler[slice.data[0]] = Word(expected_word);
353
354 circuit.populate_wire_witness(&mut filler).unwrap();
355
356 let cs = circuit.constraint_system();
358 cs.verify(&filler.into_value_vec()).unwrap();
359 }
360
361 #[test]
362 fn test_slice_const_range_unaligned() {
363 let b = CircuitBuilder::new();
364
365 let byte_vec = ByteVec::new_witness(&b, 4);
367
368 let slice = byte_vec.slice_const_range(&b, 3..11);
370
371 assert_eq!(slice.data.len(), 1, "Slice should have 1 word");
372 b.force_commit(slice.data[0]);
374
375 let circuit = b.build();
376 let mut filler = circuit.new_witness_filler();
377
378 let input_data: Vec<u8> = (0..32).map(|i| i as u8).collect();
380 byte_vec.populate_bytes_le(&mut filler, &input_data);
381
382 let expected_word = 0x0a09080706050403u64;
385 filler[slice.data[0]] = Word(expected_word);
386
387 circuit.populate_wire_witness(&mut filler).unwrap();
388
389 let cs = circuit.constraint_system();
391 cs.verify(&filler.into_value_vec()).unwrap();
392 }
393
394 #[test]
395 fn test_slice_const_range_partial_word() {
396 let b = CircuitBuilder::new();
397
398 let byte_vec = ByteVec::new_witness(&b, 3);
400
401 let slice = byte_vec.slice_const_range(&b, 0..5);
403
404 assert_eq!(slice.data.len(), 1, "Slice should have 1 word (rounded up)");
405
406 let circuit = b.build();
407 let mut filler = circuit.new_witness_filler();
408
409 let input_data: Vec<u8> = (0..24).map(|i| i as u8).collect();
411 byte_vec.populate_bytes_le(&mut filler, &input_data);
412
413 let expected_word = 0x0706050403020100u64; filler[slice.data[0]] = Word(expected_word);
418
419 circuit.populate_wire_witness(&mut filler).unwrap();
420
421 let cs = circuit.constraint_system();
423 cs.verify(&filler.into_value_vec()).unwrap();
424 }
425
426 #[test]
427 fn test_slice_const_range_unaligned_partial() {
428 let b = CircuitBuilder::new();
429
430 let byte_vec = ByteVec::new_witness(&b, 3);
432
433 let slice = byte_vec.slice_const_range(&b, 3..8);
435
436 assert_eq!(slice.data.len(), 1, "Slice should have 1 word");
437 b.force_commit(slice.data[0]);
439
440 let circuit = b.build();
441 let mut filler = circuit.new_witness_filler();
442
443 let input_data: Vec<u8> = (0..24).map(|i| i as u8).collect();
445 byte_vec.populate_bytes_le(&mut filler, &input_data);
446
447 let expected_word = 0x0000000706050403u64; filler[slice.data[0]] = Word(expected_word);
452
453 circuit.populate_wire_witness(&mut filler).unwrap();
454
455 let cs = circuit.constraint_system();
457 cs.verify(&filler.into_value_vec()).unwrap();
458 }
459
460 #[test]
461 fn test_slice_const_range_empty() {
462 let b = CircuitBuilder::new();
463
464 let byte_vec = ByteVec::new_witness(&b, 2);
466
467 let slice = byte_vec.slice_const_range(&b, 5..5);
469
470 assert_eq!(slice.data.len(), 0, "Empty slice should have 0 words");
471
472 let circuit = b.build();
473 let mut filler = circuit.new_witness_filler();
474
475 let input_data: Vec<u8> = (0..16).map(|i| i as u8).collect();
477 byte_vec.populate_bytes_le(&mut filler, &input_data);
478
479 circuit.populate_wire_witness(&mut filler).unwrap();
480
481 let cs = circuit.constraint_system();
483 cs.verify(&filler.into_value_vec()).unwrap();
484 }
485
486 #[test]
487 fn test_slice_const_range_bounds_check_valid() {
488 let b = CircuitBuilder::new();
489
490 let byte_vec = ByteVec::new_witness(&b, 2);
492
493 let slice = byte_vec.slice_const_range(&b, 0..16);
495
496 let circuit = b.build();
497 let mut filler = circuit.new_witness_filler();
498
499 let input_data: Vec<u8> = (0..16).map(|i| i as u8).collect();
501 byte_vec.populate_bytes_le(&mut filler, &input_data);
502
503 for (i, chunk) in input_data.chunks(8).enumerate() {
505 let mut word = 0u64;
506 for (j, &byte) in chunk.iter().enumerate() {
507 word |= (byte as u64) << (j * 8);
508 }
509 filler[slice.data[i]] = Word(word);
510 }
511
512 circuit.populate_wire_witness(&mut filler).unwrap();
514
515 let cs = circuit.constraint_system();
517 cs.verify(&filler.into_value_vec()).unwrap();
518 }
519
520 #[test]
521 fn test_slice_const_range_bounds_check_fail() {
522 let b = CircuitBuilder::new();
523
524 let byte_vec = ByteVec::new_witness(&b, 2);
526
527 let slice = byte_vec.slice_const_range(&b, 0..16);
529
530 let circuit = b.build();
531 let mut filler = circuit.new_witness_filler();
532
533 let input_data: Vec<u8> = (0..10).map(|i| i as u8).collect();
535 byte_vec.populate_bytes_le(&mut filler, &input_data);
536
537 for i in 0..2 {
539 let start = i * 8;
540 let end = (start + 8).min(input_data.len());
541 let mut word = 0u64;
542 if start < input_data.len() {
543 for (j, &byte) in input_data[start..end].iter().enumerate() {
544 word |= (byte as u64) << (j * 8);
545 }
546 }
547 filler[slice.data[i]] = Word(word);
548 }
549
550 let result = circuit.populate_wire_witness(&mut filler);
552 assert!(result.is_err(), "Should fail bounds check when range.end > len_bytes");
553 }
554
555 #[test]
556 fn test_slice_const_range_multi_word() {
557 let b = CircuitBuilder::new();
558
559 let byte_vec = ByteVec::new_witness(&b, 4);
561
562 let slice = byte_vec.slice_const_range(&b, 5..21);
564
565 assert_eq!(slice.data.len(), 2, "Slice should have 2 words");
566 for &wire in &slice.data {
568 b.force_commit(wire);
569 }
570
571 let circuit = b.build();
572 let mut filler = circuit.new_witness_filler();
573
574 let input_data: Vec<u8> = (0..32).map(|i| i as u8).collect();
576 byte_vec.populate_bytes_le(&mut filler, &input_data);
577
578 let slice_bytes: Vec<u8> = input_data[5..21].to_vec();
580
581 for (i, chunk) in slice_bytes.chunks(8).enumerate() {
583 let mut word = 0u64;
584 for (j, &byte) in chunk.iter().enumerate() {
585 word |= (byte as u64) << (j * 8);
586 }
587 filler[slice.data[i]] = Word(word);
588 }
589
590 circuit.populate_wire_witness(&mut filler).unwrap();
591
592 let cs = circuit.constraint_system();
594 cs.verify(&filler.into_value_vec()).unwrap();
595 }
596
597 #[test]
598 #[should_panic(expected = "Invalid range")]
599 #[allow(clippy::reversed_empty_ranges)]
600 fn test_slice_const_range_invalid_range() {
601 let b = CircuitBuilder::new();
602 let byte_vec = ByteVec::new_witness(&b, 4);
603
604 byte_vec.slice_const_range(&b, 10..5);
606 }
607
608 #[test]
609 #[should_panic(expected = "exceeds length bound")]
610 fn test_slice_const_range_exceeds_capacity() {
611 let b = CircuitBuilder::new();
612 let byte_vec = ByteVec::new_witness(&b, 2); byte_vec.slice_const_range(&b, 0..20);
616 }
617
618 #[test]
619 fn test_slice_const_range_unaligned_at_capacity_boundary() {
620 let b = CircuitBuilder::new();
623
624 let byte_vec = ByteVec::new_witness(&b, 2);
626
627 let slice = byte_vec.slice_const_range(&b, 5..16);
630
631 assert_eq!(slice.data.len(), 2, "Slice should have 2 words");
632 for &wire in &slice.data {
634 b.force_commit(wire);
635 }
636
637 let circuit = b.build();
638 let mut filler = circuit.new_witness_filler();
639
640 let input_data: Vec<u8> = (0..16).map(|i| i as u8).collect();
642 byte_vec.populate_bytes_le(&mut filler, &input_data);
643
644 let slice_bytes: Vec<u8> = input_data[5..16].to_vec();
646
647 for (i, chunk) in slice_bytes.chunks(8).enumerate() {
649 let mut word = 0u64;
650 for (j, &byte) in chunk.iter().enumerate() {
651 word |= (byte as u64) << (j * 8);
652 }
653 filler[slice.data[i]] = Word(word);
654 }
655
656 circuit.populate_wire_witness(&mut filler).unwrap();
657
658 let cs = circuit.constraint_system();
660 cs.verify(&filler.into_value_vec()).unwrap();
661 }
662
663 #[test]
664 fn test_new_defaults_to_full_len_range() {
665 let b = CircuitBuilder::new();
666 let v = ByteVec::new_witness(&b, 4); assert_eq!(v.len_range, 0..=32);
668 }
669
670 #[test]
671 fn test_new_const_len_sets_point_range() {
672 let b = CircuitBuilder::new();
673 let data = vec![b.add_witness(); 4]; let v = ByteVec::new_const_len(&b, data, 18);
675 assert_eq!(v.len_range, 18..=18);
676 }
677
678 #[test]
679 #[should_panic(expected = "exceeds capacity")]
680 fn test_new_const_len_exceeds_capacity_panics() {
681 let b = CircuitBuilder::new();
682 let data = vec![b.add_witness(); 2]; ByteVec::new_const_len(&b, data, 17);
684 }
685
686 #[test]
687 #[should_panic(expected = "exceeds capacity")]
688 fn test_new_with_len_range_end_exceeds_capacity_panics() {
689 let b = CircuitBuilder::new();
690 let data = vec![b.add_witness(); 2]; let len_bytes = b.add_inout();
692 ByteVec::new_with_len_range(data, len_bytes, 0..=17);
693 }
694
695 #[test]
696 #[should_panic(expected = "outside len_range")]
697 fn test_populate_len_bytes_out_of_range_panics() {
698 let b = CircuitBuilder::new();
699 let v = ByteVec::new_const_len(&b, vec![b.add_witness(); 2], 8);
701 let circuit = b.build();
702 let mut filler = circuit.new_witness_filler();
703 v.populate_len_bytes(&mut filler, 9);
704 }
705
706 #[test]
707 fn test_slice_const_range_const_len_skips_runtime_check() {
708 let b = CircuitBuilder::new();
711 let data: Vec<_> = (0..2).map(|_| b.add_witness()).collect(); let v = ByteVec::new_const_len(&b, data, 16);
713 let slice = v.slice_const_range(&b, 0..11);
714 assert_eq!(slice.data.len(), 2);
715
716 let circuit = b.build();
717 let mut filler = circuit.new_witness_filler();
718 v.populate_data(&mut filler, &(0..16).map(|i| i as u8).collect::<Vec<_>>());
719
720 circuit.populate_wire_witness(&mut filler).unwrap();
721 let cs = circuit.constraint_system();
722 cs.verify(&filler.into_value_vec()).unwrap();
723 }
724
725 #[test]
726 fn test_slice_const_range_propagates_point_len_range() {
727 let b = CircuitBuilder::new();
728 let v = ByteVec::new_witness(&b, 4);
729 let s = v.slice_const_range(&b, 3..11); assert_eq!(s.len_range, 8..=8);
731 }
732}