acir/circuit/opcodes/
memory_operation.rs

1use std::marker::PhantomData;
2
3use crate::native_types::{Expression, Witness};
4use acir_field::AcirField;
5use serde::{Deserialize, Serialize};
6
7/// Identifier for a block of memory
8#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Hash, Copy, Default)]
9#[cfg_attr(feature = "arb", derive(proptest_derive::Arbitrary))]
10pub struct BlockId(pub u32);
11
12impl std::fmt::Display for BlockId {
13    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
14        write!(f, "b{}", self.0)
15    }
16}
17
18/// Whether a memory operation reads from or writes to memory
19#[derive(Clone, PartialEq, Eq, Debug, Hash, Copy)]
20#[cfg_attr(feature = "arb", derive(proptest_derive::Arbitrary))]
21pub enum MemOpKind {
22    Read,
23    Write,
24}
25
26/// Operation on a block of memory
27/// We can either write or read at an index in memory
28#[derive(Clone, PartialEq, Eq, Debug, Hash)]
29#[cfg_attr(feature = "arb", derive(proptest_derive::Arbitrary))]
30pub struct MemOp<F> {
31    pub operation: MemOpKind,
32    /// array index, it must be less than the array length
33    pub index: Witness,
34    /// the witness we are reading into (read), or the witness whose value is written (write)
35    pub value: Witness,
36    #[cfg_attr(feature = "arb", proptest(value = "PhantomData"))]
37    _phantom: PhantomData<F>,
38}
39
40/// Wire format for `MemOp` — preserves backwards-compatible serialization where all three
41/// fields are `Expression<F>`. The `serde(rename)` ensures this type registers under the
42/// same name ("MemOp") as the public type so that `serde_reflection` traces it correctly.
43#[derive(Serialize, Deserialize)]
44#[serde(rename = "MemOp")]
45struct MemOpWire<F> {
46    operation: Expression<F>,
47    index: Expression<F>,
48    value: Expression<F>,
49}
50
51impl<F: AcirField + Serialize> Serialize for MemOp<F> {
52    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
53        MemOpWire {
54            operation: match self.operation {
55                MemOpKind::Read => Expression::<F>::zero(),
56                MemOpKind::Write => Expression::<F>::one(),
57            },
58            index: Expression::<F>::from(self.index),
59            value: Expression::<F>::from(self.value),
60        }
61        .serialize(serializer)
62    }
63}
64
65impl<'de, F: AcirField + Deserialize<'de>> Deserialize<'de> for MemOp<F> {
66    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
67        let wire = MemOpWire::<F>::deserialize(deserializer)?;
68
69        let operation = if wire.operation.is_zero() {
70            MemOpKind::Read
71        } else if wire.operation.is_one() {
72            MemOpKind::Write
73        } else {
74            return Err(serde::de::Error::custom(
75                "MemOp operation must be either 0 (Read) or 1 (Write)",
76            ));
77        };
78        let index = wire
79            .index
80            .to_witness()
81            .ok_or_else(|| serde::de::Error::custom("MemOp index must be a single witness"))?;
82        let value = wire
83            .value
84            .to_witness()
85            .ok_or_else(|| serde::de::Error::custom("MemOp value must be a single witness"))?;
86
87        Ok(MemOp { operation, index, value, _phantom: PhantomData })
88    }
89}
90
91impl<F> MemOp<F> {
92    /// Creates a `MemOp` which reads from memory at `index` and inserts the read value
93    /// into the [`WitnessMap`][crate::native_types::WitnessMap] at `value`.
94    pub fn read_at_mem_index(index: Witness, value: Witness) -> Self {
95        MemOp { operation: MemOpKind::Read, index, value, _phantom: PhantomData }
96    }
97
98    /// Creates a `MemOp` which writes `value` into memory at `index`.
99    pub fn write_to_mem_index(index: Witness, value: Witness) -> Self {
100        MemOp { operation: MemOpKind::Write, index, value, _phantom: PhantomData }
101    }
102}