1use std::sync::Arc;
4
5#[non_exhaustive]
7#[derive(Debug, Clone)]
8pub enum Value {
9 U32(u32),
11 I32(i32),
13 U64(u64),
15 Bool(bool),
17 Bytes(Arc<[u8]>),
19 Float(f64),
21 Array(Vec<Value>),
23}
24
25impl PartialEq for Value {
26 fn eq(&self, other: &Self) -> bool {
27 match (self, other) {
28 (Self::U32(a), Self::U32(b)) => a == b,
29 (Self::I32(a), Self::I32(b)) => a == b,
30 (Self::U64(a), Self::U64(b)) => a == b,
31 (Self::Bool(a), Self::Bool(b)) => a == b,
32 (Self::Bytes(a), Self::Bytes(b)) => a == b,
33 (Self::Float(a), Self::Float(b)) => a.to_bits() == b.to_bits(),
34 (Self::Array(a), Self::Array(b)) => a == b,
35 _ => false,
36 }
37 }
38}
39
40impl Eq for Value {}
41
42impl Value {
43 #[must_use]
45 pub fn truthy(&self) -> bool {
46 match self {
47 Self::Array(values) => !values.is_empty(),
48 Self::Float(value) => *value != 0.0,
49 _ => self.try_as_u32().unwrap_or(1) != 0,
50 }
51 }
52
53 #[must_use]
55 pub fn to_bytes(&self) -> Vec<u8> {
56 match self {
57 Self::U32(value) => value.to_le_bytes().to_vec(),
58 Self::I32(value) => value.to_le_bytes().to_vec(),
59 Self::U64(value) => value.to_le_bytes().to_vec(),
60 Self::Bool(value) => u32::from(*value).to_le_bytes().to_vec(),
61 Self::Bytes(bytes) => bytes.to_vec(),
62 Self::Float(value) => value.to_le_bytes().to_vec(),
63 Self::Array(values) => values.iter().flat_map(Self::to_bytes).collect(),
64 }
65 }
66
67 #[must_use]
69 pub fn to_bytes_width(&self, declared_width: usize) -> Vec<u8> {
70 let mut bytes = self.to_bytes();
71 if declared_width == 0 {
72 return bytes;
73 }
74 bytes.resize(declared_width, 0);
75 bytes.truncate(declared_width);
76 bytes
77 }
78
79 pub fn extend_bytes_width(
86 &self,
87 declared_width: usize,
88 out: &mut Vec<u8>,
89 ) -> Result<(), vyre::Error> {
90 let start_len = out.len();
91 let fixed_next_len = if declared_width == 0 {
92 None
93 } else {
94 Some(start_len.checked_add(declared_width).ok_or_else(|| {
95 vyre::Error::interp(
96 "encoded value byte size overflows usize. Fix: reduce the argument count or byte payload size.",
97 )
98 })?)
99 };
100 match self {
101 Self::U32(value) => extend_fixed_width(&value.to_le_bytes(), declared_width, out),
102 Self::I32(value) => extend_fixed_width(&value.to_le_bytes(), declared_width, out),
103 Self::U64(value) => extend_fixed_width(&value.to_le_bytes(), declared_width, out),
104 Self::Bool(value) => {
105 extend_fixed_width(&u32::from(*value).to_le_bytes(), declared_width, out);
106 }
107 Self::Bytes(bytes) => extend_fixed_width(bytes, declared_width, out),
108 Self::Float(value) => extend_fixed_width(&value.to_le_bytes(), declared_width, out),
109 Self::Array(values) => {
110 for value in values {
111 value.extend_bytes_width(0, out)?;
112 }
113 if let Some(next_len) = fixed_next_len {
114 out.truncate(start_len + declared_width.min(out.len() - start_len));
115 out.resize(next_len, 0);
116 }
117 }
118 }
119 if let Some(next_len) = fixed_next_len {
120 debug_assert_eq!(out.len(), next_len);
121 }
122 Ok(())
123 }
124
125 #[must_use]
127 pub fn try_as_u32(&self) -> Option<u32> {
128 match self {
129 Self::U32(value) => Some(*value),
130 Self::I32(value) => u32::try_from(*value).ok(),
131 Self::U64(value) => u32::try_from(*value).ok(),
132 Self::Bool(value) => Some(u32::from(*value)),
133 Self::Bytes(bytes) => (bytes.len() <= 4).then(|| read_u32_prefix(bytes)),
134 Self::Float(value) => Some(*value as u32),
135 Self::Array(_) => None,
136 }
137 }
138
139 #[must_use]
141 pub fn as_u32(&self) -> u32 {
142 self.try_as_u32().unwrap_or(0)
143 }
144
145 #[must_use]
147 pub fn try_as_u64(&self) -> Option<u64> {
148 match self {
149 Self::U32(value) => Some(u64::from(*value)),
150 Self::I32(value) => u64::try_from(*value).ok(),
151 Self::U64(value) => Some(*value),
152 Self::Bool(value) => Some(u64::from(*value)),
153 Self::Bytes(bytes) => (bytes.len() <= 8).then(|| read_u64_prefix(bytes)),
154 Self::Float(value) => Some(*value as u64),
155 Self::Array(_) => None,
156 }
157 }
158
159 #[must_use]
161 pub fn as_u64(&self) -> u64 {
162 self.try_as_u64().unwrap_or(0)
163 }
164
165 #[must_use]
167 pub fn try_as_f32(&self) -> Option<f32> {
168 match self {
169 Self::Float(value) => Some(*value as f32),
170 Self::U32(value) => Some(f32::from_bits(*value)),
171 _ => None,
172 }
173 }
174
175 #[must_use]
177 pub fn wide_bytes(&self) -> Vec<u8> {
178 self.to_bytes()
179 }
180
181 #[must_use]
183 pub fn zero_for(ty: vyre::ir::DataType) -> Self {
184 Self::try_zero_for(ty).unwrap_or_else(|| Self::Bytes(Arc::from([])))
185 }
186
187 #[must_use]
189 pub fn try_zero_for(ty: vyre::ir::DataType) -> Option<Self> {
190 match ty {
191 vyre::ir::DataType::U32 => Some(Self::U32(0)),
192 vyre::ir::DataType::I32 => Some(Self::I32(0)),
193 vyre::ir::DataType::U64 => Some(Self::U64(0)),
194 vyre::ir::DataType::Bool => Some(Self::Bool(false)),
195 vyre::ir::DataType::Bytes => Some(Self::Bytes(Arc::from([]))),
196 vyre::ir::DataType::F32 => Some(Self::Float(0.0)),
197 vyre::ir::DataType::Vec2U32 => Some(Self::Bytes(Arc::from(vec![0; 8]))),
198 vyre::ir::DataType::Vec4U32 => Some(Self::Bytes(Arc::from(vec![0; 16]))),
199 _ => None,
200 }
201 }
202
203 pub fn from_element_bytes(ty: vyre::ir::DataType, bytes: &[u8]) -> Result<Self, String> {
209 match ty {
210 vyre::ir::DataType::U32 => {
211 if bytes.len() < 4 {
212 return Err("u32 requires 4 bytes".to_string());
213 }
214 Ok(Self::U32(u32::from_le_bytes([
215 bytes[0], bytes[1], bytes[2], bytes[3],
216 ])))
217 }
218 vyre::ir::DataType::I32 => {
219 if bytes.len() < 4 {
220 return Err("i32 requires 4 bytes".to_string());
221 }
222 Ok(Self::I32(i32::from_le_bytes([
223 bytes[0], bytes[1], bytes[2], bytes[3],
224 ])))
225 }
226 vyre::ir::DataType::U64 => {
227 if bytes.len() < 8 {
228 return Err("u64 requires 8 bytes".to_string());
229 }
230 Ok(Self::U64(u64::from_le_bytes([
231 bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
232 ])))
233 }
234 vyre::ir::DataType::Bool => {
235 if bytes.len() < 4 {
236 return Err("bool requires 4 bytes".to_string());
237 }
238 Ok(Self::Bool(
239 u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]) != 0,
240 ))
241 }
242 vyre::ir::DataType::Vec2U32 => {
243 if bytes.len() < 8 {
244 return Err("vec2u32 requires 8 bytes".to_string());
245 }
246 Ok(Self::Bytes(Arc::from(&bytes[..8])))
247 }
248 vyre::ir::DataType::Vec4U32 => {
249 if bytes.len() < 16 {
250 return Err("vec4u32 requires 16 bytes".to_string());
251 }
252 Ok(Self::Bytes(Arc::from(&bytes[..16])))
253 }
254 vyre::ir::DataType::F32 => {
255 if bytes.len() < 4 {
256 return Err("f32 requires 4 bytes".to_string());
257 }
258 Ok(Self::Float(f64::from(f32::from_le_bytes([
259 bytes[0], bytes[1], bytes[2], bytes[3],
260 ]))))
261 }
262 vyre::ir::DataType::Bytes => Ok(Self::Bytes(Arc::from(bytes))),
263 _ => Ok(Self::Bytes(Arc::from(bytes))),
264 }
265 }
266}
267
268fn extend_fixed_width(bytes: &[u8], declared_width: usize, out: &mut Vec<u8>) {
269 if declared_width == 0 {
270 out.extend_from_slice(bytes);
271 return;
272 }
273 let copied = bytes.len().min(declared_width);
274 out.extend_from_slice(&bytes[..copied]);
275 out.resize(out.len() + (declared_width - copied), 0);
276}
277
278impl From<Vec<u8>> for Value {
279 fn from(bytes: Vec<u8>) -> Self {
280 Self::Bytes(Arc::from(bytes))
281 }
282}
283
284impl From<&[u8]> for Value {
285 fn from(bytes: &[u8]) -> Self {
286 Self::Bytes(Arc::from(bytes))
287 }
288}
289
290fn read_u32_prefix(bytes: &[u8]) -> u32 {
291 let mut padded = [0u8; 4];
292 let len = bytes.len().min(4);
293 padded[..len].copy_from_slice(&bytes[..len]);
294 u32::from_le_bytes(padded)
295}
296
297fn read_u64_prefix(bytes: &[u8]) -> u64 {
298 let mut padded = [0u8; 8];
299 let len = bytes.len().min(8);
300 padded[..len].copy_from_slice(&bytes[..len]);
301 u64::from_le_bytes(padded)
302}
303
304#[cfg(test)]
305mod tests {
306 use super::*;
307 use proptest::prelude::*;
308
309 #[test]
310 fn neg_zero_truthiness_is_false() {
311 assert!(!Value::Float(-0.0).truthy());
312 }
313
314 #[test]
315 fn pos_zero_truthiness_is_false() {
316 assert!(!Value::Float(0.0).truthy());
317 }
318
319 #[test]
320 fn nonzero_float_truthiness_is_true() {
321 assert!(Value::Float(1.0).truthy());
322 assert!(Value::Float(-1.0).truthy());
323 assert!(Value::Float(f64::INFINITY).truthy());
324 assert!(Value::Float(f64::NEG_INFINITY).truthy());
325 }
326
327 proptest! {
328 #[test]
329 fn neg_zero_select_branches_to_false(
330 positive_sign in proptest::bool::ANY,
331 ) {
332 let zero = if positive_sign { 0.0_f64 } else { -0.0_f64 };
333 prop_assert!(!Value::Float(zero).truthy(),
334 "Value::Float({zero}).truthy() must be false to match backend bool(0.0)/bool(-0.0) semantics");
335 }
336 }
337}