1use std::ffi::CStr;
18
19use anyhow::{bail, Context, Result};
20use serde::Deserialize;
21use wasmtime::{AsContext, AsContextMut, Instance, Memory};
22
23#[derive(Debug, Deserialize, Clone)]
26#[serde(transparent)]
27pub struct EntrypointId(pub(crate) i32);
28
29#[derive(Debug, Deserialize, Clone)]
32#[serde(transparent)]
33pub struct BuiltinId(pub(crate) i32);
34
35#[derive(Debug)]
37pub struct Value(pub(crate) i32);
38
39#[derive(Debug)]
41pub struct Addr(pub(crate) i32);
42
43#[derive(Debug)]
45pub struct Heap {
46 pub(crate) ptr: i32,
48
49 pub(crate) len: i32,
51
52 pub(crate) freed: bool,
54}
55
56impl Heap {
57 pub const fn end(&self) -> i32 {
59 self.ptr + self.len
60 }
61
62 pub fn pages(&self) -> u64 {
64 let page_size = 64 * 1024;
65 let addr = self.end();
66 let page = addr / page_size;
67 #[allow(clippy::expect_used)]
70 if addr % page_size > 0 { page + 1 } else { page }
71 .try_into()
72 .expect("invalid heap address")
73 }
74}
75
76impl Drop for Heap {
77 fn drop(&mut self) {
78 if !self.freed {
79 tracing::warn!("forgot to free heap allocation");
80 self.freed = true;
81 }
82 }
83}
84
85#[derive(Debug)]
87pub struct NulStr(pub(crate) i32);
88
89impl NulStr {
90 pub fn read<'s, T: AsContext>(&self, store: &'s T, memory: &Memory) -> Result<&'s CStr> {
92 let mem = memory.data(store);
93 let start: usize = self.0.try_into().context("invalid address")?;
94 let mem = mem.get(start..).context("memory address out of bounds")?;
95 let nul = mem
96 .iter()
97 .position(|c| *c == 0)
98 .context("malformed string")?;
99 let mem = mem
100 .get(..=nul)
101 .context("issue while extracting nul-terminated string")?;
102 let res = CStr::from_bytes_with_nul(mem)?;
103 Ok(res)
104 }
105}
106
107#[derive(Debug)]
109pub struct Ctx(pub(crate) i32);
110
111#[derive(Debug, thiserror::Error)]
113pub enum OpaError {
114 #[error("Unrecoverable internal error")]
116 Internal,
117
118 #[error("Invalid value type was encountered")]
120 InvalidType,
121
122 #[error("Invalid object path reference")]
124 InvalidPath,
125
126 #[error("Unrecognized error code: {0}")]
128 Other(i32),
129}
130
131impl OpaError {
132 pub(crate) fn from_code(code: i32) -> Result<(), OpaError> {
134 match code {
135 0 => Ok(()),
136 1 => Err(Self::Internal),
137 2 => Err(Self::InvalidType),
138 3 => Err(Self::InvalidPath),
139 x => Err(Self::Other(x)),
140 }
141 }
142}
143
144#[derive(Debug, Clone, Copy)]
146pub enum AbiVersion {
147 V1_0,
149
150 V1_1,
152
153 V1_2,
155
156 V1_2Plus(i32),
158}
159
160impl AbiVersion {
161 pub(crate) fn from_instance<T: Send>(
167 mut store: impl AsContextMut<Data = T>,
168 instance: &Instance,
169 ) -> Result<Self> {
170 let abi_version = instance
171 .get_global(&mut store, "opa_wasm_abi_version")
172 .context("missing global opa_wasm_abi_version")?
173 .get(&mut store)
174 .i32()
175 .context("opa_wasm_abi_version is not an i32")?;
176
177 let abi_minor_version = instance
178 .get_global(&mut store, "opa_wasm_abi_minor_version")
179 .context("missing global opa_wasm_abi_minor_version")?
180 .get(&mut store)
181 .i32()
182 .context("opa_wasm_abi_minor_version is not an i32")?;
183
184 Self::new(abi_version, abi_minor_version)
185 }
186
187 fn new(major: i32, minor: i32) -> Result<Self> {
189 match (major, minor) {
190 (1, 0) => Ok(Self::V1_0),
191 (1, 1) => Ok(Self::V1_1),
192 (1, 2) => Ok(Self::V1_2),
193 (1, n @ 2..) => Ok(Self::V1_2Plus(n)),
194 (major, minor) => bail!("unsupported ABI version {}.{}", major, minor),
195 }
196 }
197
198 #[must_use]
200 pub(crate) const fn has_eval_fastpath(self) -> bool {
201 matches!(self, Self::V1_2 | Self::V1_2Plus(_))
202 }
203}
204
205impl std::fmt::Display for AbiVersion {
206 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
207 match self {
208 AbiVersion::V1_0 => write!(f, "1.0"),
209 AbiVersion::V1_1 => write!(f, "1.1"),
210 AbiVersion::V1_2 => write!(f, "1.2"),
211 AbiVersion::V1_2Plus(n) => write!(f, "1.{n} (1.2+ compatible)"),
212 }
213 }
214}