1use crate::{
2 black_box::BlackBoxOp,
3 lengths::{ElementsFlattenedLength, FlattenedLength, SemanticLength, SemiFlattenedLength},
4};
5use acir_field::AcirField;
6use itertools::Itertools;
7use msgpack_tagged::MsgpackTagged;
8use serde::{Deserialize, Serialize};
9
10pub type Label = usize;
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
16#[derive(Serialize, Deserialize, MsgpackTagged)]
17#[cfg_attr(feature = "arb", derive(proptest_derive::Arbitrary))]
18pub enum MemoryAddress {
19 #[tag(0)]
21 Direct(u32),
22 #[tag(1)]
29 Relative(u32),
30}
31
32impl MemoryAddress {
33 pub fn direct(address: u32) -> Self {
35 MemoryAddress::Direct(address)
36 }
37
38 pub fn relative(offset: u32) -> Self {
40 MemoryAddress::Relative(offset)
41 }
42
43 pub fn unwrap_direct(self) -> u32 {
47 match self {
48 MemoryAddress::Direct(address) => address,
49 MemoryAddress::Relative(_) => panic!("Expected direct memory address"),
50 }
51 }
52
53 pub fn unwrap_relative(self) -> u32 {
57 match self {
58 MemoryAddress::Direct(_) => panic!("Expected relative memory address"),
59 MemoryAddress::Relative(offset) => offset,
60 }
61 }
62
63 pub fn to_u32(self) -> u32 {
65 match self {
66 MemoryAddress::Direct(address) => address,
67 MemoryAddress::Relative(offset) => offset,
68 }
69 }
70
71 pub fn is_relative(&self) -> bool {
72 match self {
73 MemoryAddress::Relative(_) => true,
74 MemoryAddress::Direct(_) => false,
75 }
76 }
77
78 pub fn is_direct(&self) -> bool {
79 !self.is_relative()
80 }
81
82 pub fn offset(&self, amount: u32) -> Self {
86 let address = self.unwrap_direct();
88 MemoryAddress::direct(address.checked_add(amount).expect("memory offset overflow"))
89 }
90}
91
92impl std::fmt::Display for MemoryAddress {
93 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
94 match self {
95 MemoryAddress::Direct(address) => write!(f, "@{address}"),
96 MemoryAddress::Relative(offset) => write!(f, "sp[{offset}]"),
97 }
98 }
99}
100
101#[derive(Debug, Clone, Eq, PartialEq, Hash)]
103#[derive(Serialize, Deserialize, MsgpackTagged)]
104pub enum HeapValueType {
105 #[tag(0)]
107 Simple(BitSize),
108 #[tag(1)]
112 Array {
113 #[tag(0)]
114 value_types: Vec<HeapValueType>,
115 #[tag(1)]
116 size: SemanticLength,
117 },
118 #[tag(2)]
122 Vector {
123 #[tag(0)]
124 value_types: Vec<HeapValueType>,
125 },
126}
127
128impl HeapValueType {
129 pub fn all_simple(types: &[HeapValueType]) -> bool {
131 types.iter().all(|typ| matches!(typ, HeapValueType::Simple(_)))
132 }
133
134 pub fn field() -> HeapValueType {
136 HeapValueType::Simple(BitSize::Field)
137 }
138
139 pub fn flattened_size(&self) -> Option<FlattenedLength> {
143 match self {
144 HeapValueType::Simple(_) => Some(FlattenedLength(1)),
145 HeapValueType::Array { value_types, size } => {
146 let elements_flattened_size =
148 value_types.iter().map(|t| t.flattened_size()).sum::<Option<FlattenedLength>>();
149 elements_flattened_size.map(|elements_flattened_size| {
151 ElementsFlattenedLength::from(elements_flattened_size) * *size
152 })
153 }
154 HeapValueType::Vector { .. } => {
155 None
157 }
158 }
159 }
160}
161
162impl std::fmt::Display for HeapValueType {
163 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
164 let write_types =
165 |f: &mut std::fmt::Formatter<'_>, value_types: &[HeapValueType]| -> std::fmt::Result {
166 if value_types.len() == 1 {
167 write!(f, "{}", value_types[0])?;
168 } else {
169 write!(f, "(")?;
170 for (index, value_type) in value_types.iter().enumerate() {
171 if index > 0 {
172 write!(f, ", ")?;
173 }
174 write!(f, "{value_type}")?;
175 }
176 write!(f, ")")?;
177 }
178 Ok(())
179 };
180
181 match self {
182 HeapValueType::Simple(bit_size) => {
183 write!(f, "{bit_size}")
184 }
185 HeapValueType::Array { value_types, size } => {
186 write!(f, "[")?;
187 write_types(f, value_types)?;
188 write!(f, "; {size}")?;
189 write!(f, "]")
190 }
191 HeapValueType::Vector { value_types } => {
192 write!(f, "@[")?;
193 write_types(f, value_types)?;
194 write!(f, "]")
195 }
196 }
197 }
198}
199
200#[derive(Debug, Clone, PartialEq, Eq, Copy, Hash)]
202#[derive(Serialize, Deserialize, MsgpackTagged)]
203#[cfg_attr(feature = "arb", derive(proptest_derive::Arbitrary))]
204pub struct HeapArray {
205 #[tag(0)]
209 pub pointer: MemoryAddress,
210 #[tag(1)]
212 pub size: SemiFlattenedLength,
213}
214
215impl Default for HeapArray {
216 fn default() -> Self {
217 Self { pointer: MemoryAddress::direct(0), size: SemiFlattenedLength(0) }
218 }
219}
220
221impl std::fmt::Display for HeapArray {
222 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
223 write!(f, "[{}; {}]", self.pointer, self.size)
224 }
225}
226
227#[derive(Debug, Clone, PartialEq, Eq, Copy, Hash)]
229#[derive(Serialize, Deserialize, MsgpackTagged)]
230#[cfg_attr(feature = "arb", derive(proptest_derive::Arbitrary))]
231pub struct HeapVector {
232 #[tag(0)]
236 pub pointer: MemoryAddress,
237 #[tag(1)]
239 pub size: MemoryAddress,
240}
241
242impl std::fmt::Display for HeapVector {
243 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
244 write!(f, "@[{}; {}]", self.pointer, self.size)
245 }
246}
247
248#[derive(Debug, Clone, PartialEq, Eq, Copy, PartialOrd, Ord, Hash)]
252#[derive(Serialize, Deserialize, MsgpackTagged)]
253#[cfg_attr(feature = "arb", derive(proptest_derive::Arbitrary))]
254pub enum IntegerBitSize {
255 #[tag(0)]
256 U1,
257 #[tag(1)]
258 U8,
259 #[tag(2)]
260 U16,
261 #[tag(3)]
262 U32,
263 #[tag(4)]
264 U64,
265 #[tag(5)]
266 U128,
267}
268
269impl From<IntegerBitSize> for u32 {
270 fn from(bit_size: IntegerBitSize) -> u32 {
271 match bit_size {
272 IntegerBitSize::U1 => 1,
273 IntegerBitSize::U8 => 8,
274 IntegerBitSize::U16 => 16,
275 IntegerBitSize::U32 => 32,
276 IntegerBitSize::U64 => 64,
277 IntegerBitSize::U128 => 128,
278 }
279 }
280}
281
282impl TryFrom<u32> for IntegerBitSize {
283 type Error = &'static str;
284
285 fn try_from(value: u32) -> Result<Self, Self::Error> {
286 match value {
287 1 => Ok(IntegerBitSize::U1),
288 8 => Ok(IntegerBitSize::U8),
289 16 => Ok(IntegerBitSize::U16),
290 32 => Ok(IntegerBitSize::U32),
291 64 => Ok(IntegerBitSize::U64),
292 128 => Ok(IntegerBitSize::U128),
293 _ => Err("Invalid bit size"),
294 }
295 }
296}
297
298impl std::fmt::Display for IntegerBitSize {
299 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
300 match self {
301 IntegerBitSize::U1 => write!(f, "bool"),
302 IntegerBitSize::U8 => write!(f, "u8"),
303 IntegerBitSize::U16 => write!(f, "u16"),
304 IntegerBitSize::U32 => write!(f, "u32"),
305 IntegerBitSize::U64 => write!(f, "u64"),
306 IntegerBitSize::U128 => write!(f, "u128"),
307 }
308 }
309}
310
311#[derive(Debug, Clone, PartialEq, Eq, Copy, PartialOrd, Ord, Hash)]
316#[derive(Serialize, Deserialize, MsgpackTagged)]
317#[cfg_attr(feature = "arb", derive(proptest_derive::Arbitrary))]
318pub enum BitSize {
319 #[tag(0)]
320 Field,
321 #[tag(1)]
322 Integer(IntegerBitSize),
323}
324
325impl BitSize {
326 pub fn to_u32<F: AcirField>(self) -> u32 {
331 match self {
332 BitSize::Field => F::max_num_bits(),
333 BitSize::Integer(bit_size) => bit_size.into(),
334 }
335 }
336
337 pub fn try_from_u32<F: AcirField>(value: u32) -> Result<Self, &'static str> {
342 if value == F::max_num_bits() {
343 Ok(BitSize::Field)
344 } else {
345 Ok(BitSize::Integer(IntegerBitSize::try_from(value)?))
346 }
347 }
348}
349
350impl std::fmt::Display for BitSize {
351 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
352 match self {
353 BitSize::Field => write!(f, "field"),
354 BitSize::Integer(bit_size) => write!(f, "{bit_size}"),
355 }
356 }
357}
358
359#[derive(Debug, Clone, PartialEq, Eq, Copy, Hash)]
366#[derive(Serialize, Deserialize, MsgpackTagged)]
367#[cfg_attr(feature = "arb", derive(proptest_derive::Arbitrary))]
368pub enum ValueOrArray {
369 #[tag(0)]
374 MemoryAddress(MemoryAddress),
375 #[tag(1)]
379 HeapArray(HeapArray),
380 #[tag(2)]
384 HeapVector(HeapVector),
385}
386
387impl std::fmt::Display for ValueOrArray {
388 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
389 match self {
390 ValueOrArray::MemoryAddress(memory_address) => {
391 write!(f, "{memory_address}")
392 }
393 ValueOrArray::HeapArray(heap_array) => {
394 write!(f, "{heap_array}")
395 }
396 ValueOrArray::HeapVector(heap_vector) => {
397 write!(f, "{heap_vector}")
398 }
399 }
400 }
401}
402
403#[derive(Debug, Clone, PartialEq, Eq, Hash)]
404#[derive(Serialize, Deserialize, MsgpackTagged)]
405#[cfg_attr(feature = "arb", derive(proptest_derive::Arbitrary))]
406pub enum BrilligOpcode<F> {
407 #[tag(0)]
411 BinaryFieldOp {
412 #[tag(0)]
413 destination: MemoryAddress,
414 #[tag(1)]
415 op: BinaryFieldOp,
416 #[tag(2)]
417 lhs: MemoryAddress,
418 #[tag(3)]
419 rhs: MemoryAddress,
420 },
421 #[tag(1)]
425 BinaryIntOp {
426 #[tag(0)]
427 destination: MemoryAddress,
428 #[tag(1)]
429 op: BinaryIntOp,
430 #[tag(2)]
431 bit_size: IntegerBitSize,
432 #[tag(3)]
433 lhs: MemoryAddress,
434 #[tag(4)]
435 rhs: MemoryAddress,
436 },
437 #[tag(2)]
440 Not {
441 #[tag(0)]
442 destination: MemoryAddress,
443 #[tag(1)]
444 source: MemoryAddress,
445 #[tag(2)]
446 bit_size: IntegerBitSize,
447 },
448 #[tag(3)]
452 Cast {
453 #[tag(0)]
454 destination: MemoryAddress,
455 #[tag(1)]
456 source: MemoryAddress,
457 #[tag(2)]
458 bit_size: BitSize,
459 },
460 #[tag(4)]
463 JumpIf {
464 #[tag(0)]
465 condition: MemoryAddress,
466 #[tag(1)]
467 location: Label,
468 },
469 #[tag(5)]
471 Jump {
472 #[tag(0)]
473 location: Label,
474 },
475 #[tag(6)]
478 CalldataCopy {
479 #[tag(0)]
480 destination_address: MemoryAddress,
481 #[tag(1)]
482 size_address: MemoryAddress,
483 #[tag(2)]
484 offset_address: MemoryAddress,
485 },
486 #[tag(7)]
492 Call {
493 #[tag(0)]
494 location: Label,
495 },
496 #[tag(8)]
498 Const {
499 #[tag(0)]
500 destination: MemoryAddress,
501 #[tag(1)]
502 bit_size: BitSize,
503 #[tag(2)]
504 value: F,
505 },
506 #[tag(9)]
508 IndirectConst {
509 #[tag(0)]
510 destination_pointer: MemoryAddress,
511 #[tag(1)]
512 bit_size: BitSize,
513 #[tag(2)]
514 value: F,
515 },
516 #[tag(10)]
520 Return,
521 #[tag(11)]
526 ForeignCall {
527 #[tag(0)]
530 function: String,
531 #[tag(1)]
537 destinations: Vec<ValueOrArray>,
538 #[tag(2)]
540 destination_value_types: Vec<HeapValueType>,
541 #[tag(3)]
543 inputs: Vec<ValueOrArray>,
544 #[tag(4)]
547 input_value_types: Vec<HeapValueType>,
548 },
549 #[tag(12)]
551 Mov {
552 #[tag(0)]
553 destination: MemoryAddress,
554 #[tag(1)]
555 source: MemoryAddress,
556 },
557 #[tag(13)]
563 ConditionalMov {
564 #[tag(0)]
565 destination: MemoryAddress,
566 #[tag(1)]
567 source_a: MemoryAddress,
568 #[tag(2)]
569 source_b: MemoryAddress,
570 #[tag(3)]
571 condition: MemoryAddress,
572 },
573 #[tag(14)]
576 Load {
577 #[tag(0)]
578 destination: MemoryAddress,
579 #[tag(1)]
580 source_pointer: MemoryAddress,
581 },
582 #[tag(15)]
585 Store {
586 #[tag(0)]
587 destination_pointer: MemoryAddress,
588 #[tag(1)]
589 source: MemoryAddress,
590 },
591 #[tag(16)]
594 BlackBox(BlackBoxOp),
595 #[tag(17)]
597 Trap {
598 #[tag(0)]
599 revert_data: HeapVector,
600 },
601 #[tag(18)]
603 Stop {
604 #[tag(0)]
605 return_data: HeapVector,
606 },
607}
608
609impl<F: std::fmt::Display> std::fmt::Display for BrilligOpcode<F> {
610 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
611 match self {
612 BrilligOpcode::BinaryFieldOp { destination, op, lhs, rhs } => {
613 write!(f, "{destination} = field {op} {lhs}, {rhs}")
614 }
615 BrilligOpcode::BinaryIntOp { destination, op, bit_size, lhs, rhs } => {
616 write!(f, "{destination} = {bit_size} {op} {lhs}, {rhs}")
617 }
618 BrilligOpcode::Not { destination, source, bit_size } => {
619 write!(f, "{destination} = {bit_size} not {source}")
620 }
621 BrilligOpcode::Cast { destination, source, bit_size } => {
622 write!(f, "{destination} = cast {source} to {bit_size}")
623 }
624 BrilligOpcode::JumpIf { condition, location } => {
625 write!(f, "jump if {condition} to {location}")
626 }
627 BrilligOpcode::Jump { location } => {
628 write!(f, "jump to {location}")
629 }
630 BrilligOpcode::CalldataCopy { destination_address, size_address, offset_address } => {
631 write!(
632 f,
633 "{destination_address} = calldata copy [{offset_address}; {size_address}]"
634 )
635 }
636 BrilligOpcode::Call { location } => {
637 write!(f, "call {location}")
638 }
639 BrilligOpcode::Const { destination, bit_size, value } => {
640 write!(f, "{destination} = const {bit_size} {value}")
641 }
642 BrilligOpcode::IndirectConst { destination_pointer, bit_size, value } => {
643 write!(f, "{destination_pointer} = indirect const {bit_size} {value}")
644 }
645 BrilligOpcode::Return => {
646 write!(f, "return")
647 }
648 BrilligOpcode::ForeignCall {
649 function,
650 destinations,
651 destination_value_types,
652 inputs,
653 input_value_types,
654 } => {
655 if !destinations.is_empty() {
656 for (index, (destination, destination_value_type)) in
657 destinations.iter().zip_eq(destination_value_types).enumerate()
658 {
659 if index > 0 {
660 write!(f, ", ")?;
661 }
662 write!(f, "{destination}: {destination_value_type}")?;
663 }
664 write!(f, " = ")?;
665 }
666
667 write!(f, "foreign call {function}(")?;
668
669 for (index, (input, input_value_type)) in
670 inputs.iter().zip_eq(input_value_types).enumerate()
671 {
672 if index > 0 {
673 write!(f, ", ")?;
674 }
675 write!(f, "{input}: {input_value_type}")?;
676 }
677
678 write!(f, ")")?;
679 Ok(())
680 }
681 BrilligOpcode::Mov { destination, source } => {
682 write!(f, "{destination} = {source}")
683 }
684 BrilligOpcode::ConditionalMov { destination, source_a, source_b, condition } => {
685 write!(f, "{destination} = if {condition} then {source_a} else {source_b}")
686 }
687 BrilligOpcode::Load { destination, source_pointer } => {
688 write!(f, "{destination} = load {source_pointer}")
689 }
690 BrilligOpcode::Store { destination_pointer, source } => {
691 write!(f, "store {source} at {destination_pointer}")
692 }
693 BrilligOpcode::BlackBox(black_box_op) => {
694 write!(f, "{black_box_op}")
695 }
696 BrilligOpcode::Trap { revert_data } => {
697 write!(f, "trap {revert_data}")
698 }
699 BrilligOpcode::Stop { return_data } => {
700 write!(f, "stop {return_data}")
701 }
702 }
703 }
704}
705
706#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
712#[derive(Serialize, Deserialize, MsgpackTagged)]
713#[cfg_attr(feature = "arb", derive(proptest_derive::Arbitrary))]
714pub enum BinaryFieldOp {
715 #[tag(0)]
716 Add,
717 #[tag(1)]
718 Sub,
719 #[tag(2)]
720 Mul,
721 #[tag(3)]
723 Div,
724 #[tag(4)]
726 IntegerDiv,
727 #[tag(5)]
729 Equals,
730 #[tag(6)]
732 LessThan,
733 #[tag(7)]
735 LessThanEquals,
736}
737
738impl std::fmt::Display for BinaryFieldOp {
739 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
740 match self {
741 BinaryFieldOp::Add => write!(f, "add"),
742 BinaryFieldOp::Sub => write!(f, "sub"),
743 BinaryFieldOp::Mul => write!(f, "mul"),
744 BinaryFieldOp::Div => write!(f, "field_div"),
745 BinaryFieldOp::IntegerDiv => write!(f, "int_div"),
746 BinaryFieldOp::Equals => write!(f, "eq"),
747 BinaryFieldOp::LessThan => write!(f, "lt"),
748 BinaryFieldOp::LessThanEquals => write!(f, "lt_eq"),
749 }
750 }
751}
752
753#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
755#[derive(Serialize, Deserialize, MsgpackTagged)]
756#[cfg_attr(feature = "arb", derive(proptest_derive::Arbitrary))]
757pub enum BinaryIntOp {
758 #[tag(0)]
759 Add,
760 #[tag(1)]
761 Sub,
762 #[tag(2)]
763 Mul,
764 #[tag(3)]
765 Div,
766 #[tag(4)]
768 Equals,
769 #[tag(5)]
771 LessThan,
772 #[tag(6)]
774 LessThanEquals,
775 #[tag(7)]
777 And,
778 #[tag(8)]
780 Or,
781 #[tag(9)]
783 Xor,
784 #[tag(10)]
786 Shl,
787 #[tag(11)]
789 Shr,
790}
791
792impl std::fmt::Display for BinaryIntOp {
793 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
794 match self {
795 BinaryIntOp::Add => write!(f, "add"),
796 BinaryIntOp::Sub => write!(f, "sub"),
797 BinaryIntOp::Mul => write!(f, "mul"),
798 BinaryIntOp::Div => write!(f, "div"),
799 BinaryIntOp::Equals => write!(f, "eq"),
800 BinaryIntOp::LessThan => write!(f, "lt"),
801 BinaryIntOp::LessThanEquals => write!(f, "lt_eq"),
802 BinaryIntOp::And => write!(f, "and"),
803 BinaryIntOp::Or => write!(f, "or"),
804 BinaryIntOp::Xor => write!(f, "xor"),
805 BinaryIntOp::Shl => write!(f, "shl"),
806 BinaryIntOp::Shr => write!(f, "shr"),
807 }
808 }
809}
810
811#[cfg(test)]
812mod tests {
813 use crate::MemoryAddress;
814
815 use super::{BitSize, IntegerBitSize};
816 use acir_field::FieldElement;
817
818 #[test]
820 fn test_integer_bitsize_roundtrip() {
821 let integer_sizes = [
822 IntegerBitSize::U1,
823 IntegerBitSize::U8,
824 IntegerBitSize::U16,
825 IntegerBitSize::U32,
826 IntegerBitSize::U64,
827 IntegerBitSize::U128,
828 ];
829
830 for int_size in integer_sizes {
831 let as_u32: u32 = int_size.into();
833 let roundtrip = IntegerBitSize::try_from(as_u32)
835 .expect("Should successfully convert back from u32");
836 assert_eq!(
837 int_size, roundtrip,
838 "IntegerBitSize::{int_size} should roundtrip through From<IntegerBitSize> for u32 and TryFrom<u32>"
839 );
840 }
841 }
842
843 #[test]
844 fn test_integer_bitsize_values() {
845 assert_eq!(u32::from(IntegerBitSize::U1), 1);
847 assert_eq!(u32::from(IntegerBitSize::U8), 8);
848 assert_eq!(u32::from(IntegerBitSize::U16), 16);
849 assert_eq!(u32::from(IntegerBitSize::U32), 32);
850 assert_eq!(u32::from(IntegerBitSize::U64), 64);
851 assert_eq!(u32::from(IntegerBitSize::U128), 128);
852 }
853
854 #[test]
855 fn test_integer_bitsize_try_from_invalid() {
856 assert!(IntegerBitSize::try_from(0).is_err());
858 assert!(IntegerBitSize::try_from(2).is_err());
859 assert!(IntegerBitSize::try_from(7).is_err());
860 assert!(IntegerBitSize::try_from(15).is_err());
861 assert!(IntegerBitSize::try_from(31).is_err());
862 assert!(IntegerBitSize::try_from(63).is_err());
863 assert!(IntegerBitSize::try_from(127).is_err());
864 assert!(IntegerBitSize::try_from(129).is_err());
865 assert!(IntegerBitSize::try_from(256).is_err());
866 }
867
868 #[test]
870 fn test_bitsize_roundtrip() {
871 let integer_sizes = [
873 IntegerBitSize::U1,
874 IntegerBitSize::U8,
875 IntegerBitSize::U16,
876 IntegerBitSize::U32,
877 IntegerBitSize::U64,
878 IntegerBitSize::U128,
879 ];
880
881 for int_size in integer_sizes {
882 let bit_size = BitSize::Integer(int_size);
883 let as_u32 = bit_size.to_u32::<FieldElement>();
884 let roundtrip = BitSize::try_from_u32::<FieldElement>(as_u32)
885 .expect("Should successfully convert back from u32");
886 assert_eq!(
887 bit_size, roundtrip,
888 "BitSize::Integer({int_size}) should roundtrip through to_u32/try_from_u32"
889 );
890 }
891
892 let field_bit_size = BitSize::Field;
894 let as_u32 = field_bit_size.to_u32::<FieldElement>();
895 let roundtrip = BitSize::try_from_u32::<FieldElement>(as_u32)
896 .expect("Should successfully convert Field back from u32");
897 assert_eq!(
898 field_bit_size, roundtrip,
899 "BitSize::Field should roundtrip through to_u32/try_from_u32"
900 );
901 }
902
903 #[test]
904 fn test_bitsize_to_u32_values_integers() {
905 assert_eq!(BitSize::Integer(IntegerBitSize::U1).to_u32::<FieldElement>(), 1);
907 assert_eq!(BitSize::Integer(IntegerBitSize::U8).to_u32::<FieldElement>(), 8);
908 assert_eq!(BitSize::Integer(IntegerBitSize::U16).to_u32::<FieldElement>(), 16);
909 assert_eq!(BitSize::Integer(IntegerBitSize::U32).to_u32::<FieldElement>(), 32);
910 assert_eq!(BitSize::Integer(IntegerBitSize::U64).to_u32::<FieldElement>(), 64);
911 assert_eq!(BitSize::Integer(IntegerBitSize::U128).to_u32::<FieldElement>(), 128);
912 }
913
914 #[test]
915 #[cfg(feature = "bn254")]
916 fn test_bitsize_to_u32_field_bn254() {
917 assert_eq!(BitSize::Field.to_u32::<FieldElement>(), 254);
919 }
920
921 #[test]
922 #[cfg(feature = "bls12_381")]
923 fn test_bitsize_to_u32_field_bls12_381() {
924 assert_eq!(BitSize::Field.to_u32::<FieldElement>(), 255);
926 }
927
928 #[test]
929 fn test_bitsize_try_from_u32_invalid() {
930 assert!(BitSize::try_from_u32::<FieldElement>(2).is_err());
932 assert!(BitSize::try_from_u32::<FieldElement>(7).is_err());
933 assert!(BitSize::try_from_u32::<FieldElement>(0).is_err());
934 assert!(BitSize::try_from_u32::<FieldElement>(256).is_err());
935 }
936
937 #[test]
938 #[should_panic = "memory offset overflow"]
939 fn memory_offset_overflow() {
940 let addr = MemoryAddress::direct(u32::MAX);
941 let _ = addr.offset(1);
942 }
943}
944
945#[cfg(feature = "arb")]
946mod prop_tests {
947 use proptest::arbitrary::Arbitrary;
948 use proptest::prelude::*;
949
950 use crate::lengths::SemanticLength;
951
952 use super::{BitSize, HeapValueType};
953
954 impl Arbitrary for HeapValueType {
956 type Parameters = ();
957 type Strategy = BoxedStrategy<Self>;
958
959 fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
960 let leaf = any::<BitSize>().prop_map(HeapValueType::Simple);
961 leaf.prop_recursive(2, 3, 2, |inner| {
962 prop_oneof![
963 (prop::collection::vec(inner.clone(), 1..3), any::<u32>()).prop_map(
964 |(value_types, size)| {
965 HeapValueType::Array { value_types, size: SemanticLength(size) }
966 }
967 ),
968 (prop::collection::vec(inner, 1..3))
969 .prop_map(|value_types| { HeapValueType::Vector { value_types } }),
970 ]
971 })
972 .boxed()
973 }
974 }
975}