1use crate::core::{ResultValue, Value};
2use crate::lang::data::MapEntry as PMapEntry;
3#[cfg(test)]
4use crate::lang::data::{Tuple as PTuple, Vector as PVector};
5use crate::lang::protocol::INamespaced;
6use num_bigint::BigInt;
7use num_traits::ToPrimitive;
8
9const MAGIC: &[u8; 4] = b"HTA0";
10pub const MAX_FRAME_BYTES: usize = 64 * 1024 * 1024;
11pub const MAX_NESTING_DEPTH: usize = 256;
12const NIL: u8 = 0;
13const FALSE: u8 = 1;
14const TRUE: u8 = 2;
15const I64: u8 = 3;
16const STRING: u8 = 4;
17const BYTES: u8 = 5;
18const KEYWORD: u8 = 6;
19const SYMBOL: u8 = 7;
20const LIST: u8 = 8;
21const VECTOR: u8 = 9;
22const SET: u8 = 10;
23const MAP: u8 = 11;
24const HANDLE: u8 = 12;
25const NAMESPACE: u8 = 13;
26const VAR: u8 = 14;
27const F64: u8 = 15;
28const ATOM: u8 = 16;
29const ARRAY: u8 = 17;
30const OBJECT: u8 = 18;
31const CHARACTER: u8 = 19;
32const BIG_INTEGER: u8 = 20;
33const REGEX: u8 = 22;
34const TUPLE: u8 = 23;
35const CONS: u8 = 24;
36const QUEUE: u8 = 25;
37const ORDERED_MAP: u8 = 26;
38const SORTED_MAP: u8 = 27;
39const TRIE: u8 = 28;
40const ORDERED_SET: u8 = 29;
41const SORTED_SET: u8 = 30;
42const TAGGED: u8 = 31;
43const EXCEPTION_INFO: u8 = 32;
44const STRUCT: u8 = 33;
45const POINTER: u8 = 34;
46const VAR_REF: u8 = 35;
47const DEQUE: u8 = 36;
48const PRIORITY_MAP: u8 = 37;
49const MAP_ENTRY: u8 = 38;
50const RESULT_STRUCT_NAME: &str = "std.native/Result";
51const RESULT_STRUCT_FIELDS: [&str; 4] = ["status", "data", "error", "context"];
52
53pub const HTA0_TAG_INVENTORY: &[(u8, &str)] = &[
56 (NIL, "nil"),
57 (FALSE, "false"),
58 (TRUE, "true"),
59 (I64, "i64"),
60 (STRING, "string"),
61 (BYTES, "bytes"),
62 (KEYWORD, "keyword"),
63 (SYMBOL, "symbol"),
64 (LIST, "list"),
65 (VECTOR, "vector"),
66 (SET, "set"),
67 (MAP, "map"),
68 (HANDLE, "handle"),
69 (NAMESPACE, "namespace"),
70 (VAR, "legacy-var"),
71 (F64, "f64"),
72 (ATOM, "atom"),
73 (ARRAY, "array"),
74 (OBJECT, "object"),
75 (CHARACTER, "character"),
76 (BIG_INTEGER, "big-integer"),
77 (REGEX, "regex"),
78 (TUPLE, "tuple"),
79 (CONS, "cons"),
80 (QUEUE, "queue"),
81 (ORDERED_MAP, "ordered-map"),
82 (SORTED_MAP, "sorted-map"),
83 (TRIE, "trie"),
84 (ORDERED_SET, "ordered-set"),
85 (SORTED_SET, "sorted-set"),
86 (TAGGED, "tagged"),
87 (EXCEPTION_INFO, "exception-info"),
88 (STRUCT, "struct"),
89 (POINTER, "pointer"),
90 (VAR_REF, "var-ref"),
91 (DEQUE, "deque"),
92 (PRIORITY_MAP, "priority-map"),
93 (MAP_ENTRY, "map-entry"),
94];
95
96fn decode_exception_provenance(
97 value: Value,
98) -> Result<
99 (
100 Option<crate::core::ExceptionSite>,
101 Vec<crate::core::ExceptionSite>,
102 ),
103 String,
104> {
105 let entries = crate::core::map_entries(&value)
106 .ok_or_else(|| "hta/value-malformed: invalid exception provenance".to_string())?;
107 if entries.len() != 2 {
108 return Err("hta/value-malformed: invalid exception provenance fields".into());
109 }
110 let created = entries
111 .iter()
112 .find_map(|(key, value)| field_name(key, "ex/created-at").then_some(value))
113 .ok_or_else(|| "hta/value-malformed: missing exception creation provenance".to_string())?;
114 let throws = entries
115 .iter()
116 .find_map(|(key, value)| field_name(key, "ex/throws").then_some(value))
117 .ok_or_else(|| "hta/value-malformed: missing exception throw provenance".to_string())?;
118 let created = match created {
119 Value::Nil => None,
120 value => Some(decode_exception_site(value)?),
121 };
122 let Value::Vector(throws) = throws else {
123 return Err("hta/value-malformed: invalid exception throws provenance".into());
124 };
125 let throws = throws
126 .iter()
127 .map(decode_exception_site)
128 .collect::<Result<Vec<_>, _>>()?;
129 Ok((created, throws))
130}
131
132fn decode_exception_site(value: &Value) -> Result<crate::core::ExceptionSite, String> {
133 let entries = crate::core::map_entries(value)
134 .ok_or_else(|| "hta/value-malformed: invalid exception provenance site".to_string())?;
135 if entries.len() != 4 {
136 return Err("hta/value-malformed: invalid exception provenance site".into());
137 }
138 let get = |name: &str| {
139 entries
140 .iter()
141 .find_map(|(key, value)| field_name(key, name).then_some(value))
142 };
143 let namespace = match get("namespace") {
144 Some(Value::Nil) => None,
145 Some(Value::String(value)) => Some(value.clone()),
146 _ => return Err("hta/value-malformed: invalid exception provenance namespace".into()),
147 };
148 let resource = match get("resource") {
149 Some(Value::Nil) => None,
150 Some(Value::String(value)) => Some(value.clone()),
151 _ => return Err("hta/value-malformed: invalid exception provenance resource".into()),
152 };
153 let line = match get("line") {
154 Some(Value::Number(value)) if *value >= 0 => *value as usize,
155 _ => return Err("hta/value-malformed: invalid exception provenance line".into()),
156 };
157 let column = match get("column") {
158 Some(Value::Number(value)) if *value >= 0 => *value as usize,
159 _ => return Err("hta/value-malformed: invalid exception provenance column".into()),
160 };
161 Ok(crate::core::ExceptionSite {
162 namespace,
163 resource,
164 line,
165 column,
166 })
167}
168
169fn field_name(value: &Value, expected: &str) -> bool {
170 match value {
171 Value::Keyword(value) => value.as_str() == expected,
172 Value::String(value) => value == expected,
173 _ => false,
174 }
175}
176
177pub fn encode(value: &Value) -> Result<Vec<u8>, String> {
178 let mut output = MAGIC.to_vec();
179 encode_bare(value, &mut output, 0)?;
180 if output.len() > MAX_FRAME_BYTES {
181 return Err("hta/value-too-large: frame exceeds 64 MiB".into());
182 }
183 Ok(output)
184}
185
186pub fn decode(bytes: &[u8]) -> Result<Value, String> {
187 if bytes.len() > MAX_FRAME_BYTES {
188 return Err("hta/value-too-large: frame exceeds 64 MiB".into());
189 }
190 if !bytes.starts_with(MAGIC) {
191 return Err("hta/value-malformed: invalid HTA0 header".into());
192 }
193 let mut reader = Reader {
194 bytes,
195 cursor: MAGIC.len(),
196 };
197 let value = reader.value(0)?;
198 if reader.cursor != bytes.len() {
199 return Err("hta/value-malformed: trailing bytes".into());
200 }
201 Ok(value)
202}
203
204pub fn decode_canonical(bytes: &[u8]) -> Result<Value, String> {
209 let value = decode(bytes)?;
210 let canonical = encode(&value)?;
211 if canonical != bytes {
212 return Err("hta/value-noncanonical: frame bytes are not canonical".into());
213 }
214 Ok(value)
215}
216
217fn encode_bare(value: &Value, output: &mut Vec<u8>, depth: usize) -> Result<(), String> {
218 if depth > MAX_NESTING_DEPTH {
219 return Err("hta/value-too-deep: nesting exceeds 256".into());
220 }
221 match value {
222 Value::Nil => output.push(NIL),
223 Value::Bool(false) => output.push(FALSE),
224 Value::Bool(true) => output.push(TRUE),
225 Value::Number(value) => {
226 output.push(I64);
227 output.extend_from_slice(&value.to_be_bytes());
228 }
229 Value::Float(value) => {
230 if !value.is_finite() {
231 return Err("hta/non-finite number".into());
232 }
233 output.push(F64);
234 output.extend_from_slice(&value.to_bits().to_be_bytes());
235 }
236 Value::Character(value) => {
237 output.push(CHARACTER);
238 output.extend_from_slice(&u32::from(*value).to_be_bytes());
239 }
240 Value::BigInteger(value) => {
241 if let Some(value) = value.to_i64() {
242 output.push(I64);
243 output.extend_from_slice(&value.to_be_bytes());
244 } else {
245 output.push(BIG_INTEGER);
246 encode_bytes(value.to_string().as_bytes(), output)?;
247 }
248 }
249 Value::Regex(value) => {
250 output.push(REGEX);
251 encode_bytes(value.as_bytes(), output)?;
252 }
253 Value::String(value) => {
254 output.push(STRING);
255 encode_bytes(value.as_str().as_bytes(), output)?;
256 }
257 Value::Bytes(value) => {
258 output.push(BYTES);
259 encode_bytes(value, output)?;
260 }
261 Value::ByteBuffer(value) => {
262 output.push(BYTES);
263 encode_bytes(&value.borrow(), output)?;
264 }
265 Value::Keyword(value) => {
266 output.push(KEYWORD);
267 encode_bytes(value.as_str().as_bytes(), output)?;
268 }
269 Value::Symbol(value) => {
270 output.push(SYMBOL);
271 encode_bytes(value.as_str().as_bytes(), output)?;
272 }
273 Value::List(values) => encode_sequence(LIST, values.iter(), output, depth)?,
274 Value::Tuple(values) => encode_sequence(VECTOR, values.iter(), output, depth)?,
275 Value::MapEntry(entry) => encode_sequence(MAP_ENTRY, entry.iter(), output, depth)?,
276 Value::Vector(values) => encode_sequence(VECTOR, values.iter(), output, depth)?,
277 Value::Cons(values) => encode_sequence(
278 CONS,
279 values.iter().collect::<Vec<_>>().iter(),
280 output,
281 depth,
282 )?,
283 Value::Queue(values) => encode_sequence(QUEUE, values.iter(), output, depth)?,
284 Value::Deque(values) => encode_sequence(DEQUE, values.iter(), output, depth)?,
285 Value::Set(values) => {
286 let mut encoded = values
287 .iter()
288 .map(|value| bare(value, depth + 1))
289 .collect::<Result<Vec<_>, _>>()?;
290 encoded.sort();
291 output.push(SET);
292 encode_len(encoded.len(), output)?;
293 for value in encoded {
294 output.extend_from_slice(&value);
295 }
296 }
297 Value::OrderedSet(values) => encode_sequence(ORDERED_SET, values.iter(), output, depth)?,
298 Value::SortedSet(values) => {
299 let mut encoded = values
300 .iter()
301 .map(|value| bare(value, depth + 1))
302 .collect::<Result<Vec<_>, _>>()?;
303 encoded.sort();
304 output.push(SORTED_SET);
305 encode_len(encoded.len(), output)?;
306 for value in encoded {
307 output.extend_from_slice(&value);
308 }
309 }
310 Value::OrderedMap(values) => encode_map(
311 ORDERED_MAP,
312 values.iter().map(|pair| (&pair.0, &pair.1)),
313 output,
314 depth,
315 )?,
316 Value::SortedMap(values) => encode_map(SORTED_MAP, values.iter(), output, depth)?,
317 Value::PriorityMap(values) => {
318 let entries = values.iter().collect::<Vec<_>>();
319 encode_map(
320 PRIORITY_MAP,
321 entries.iter().map(|pair| (&pair.0, &pair.1)),
322 output,
323 depth,
324 )?;
325 }
326 Value::Trie(values) => {
327 let entries = values
328 .iter()
329 .map(|key| {
330 (
331 Value::String(key.clone()),
332 values.get(&key).unwrap().clone(),
333 )
334 })
335 .collect::<Vec<_>>();
336 encode_map(
337 TRIE,
338 entries.iter().map(|pair| (&pair.0, &pair.1)),
339 output,
340 depth,
341 )?;
342 }
343 Value::Map(values) => {
344 let mut encoded = values
345 .iter()
346 .map(|(key, value)| Ok((bare(key, depth + 1)?, bare(value, depth + 1)?)))
347 .collect::<Result<Vec<_>, String>>()?;
348 encoded.sort_by(|left, right| left.0.cmp(&right.0));
349 output.push(MAP);
350 encode_len(encoded.len(), output)?;
351 for (key, value) in encoded {
352 output.extend_from_slice(&key);
353 output.extend_from_slice(&value);
354 }
355 }
356 Value::Namespace(value) => {
357 output.push(NAMESPACE);
358 encode_bytes(value.name().as_str().as_bytes(), output)?;
359 }
360 Value::Var(value) => {
361 output.push(VAR_REF);
362 encode_bare(&Value::Symbol(value.symbol().clone()), output, depth + 1)?;
363 }
364 Value::Atom(value) => {
365 output.push(ATOM);
366 encode_bare(&value.deref_value(), output, depth + 1)?;
367 }
368 Value::Array(values) => encode_sequence(ARRAY, values.borrow().iter(), output, depth)?,
369 Value::Object(values) => {
370 let values = values.borrow();
371 output.push(OBJECT);
372 encode_len(values.len(), output)?;
373 for (key, value) in values.iter() {
374 encode_bare(&Value::String(key.clone()), output, depth + 1)?;
375 encode_bare(value, output, depth + 1)?;
376 }
377 }
378 Value::Extension(value) => {
379 output.push(HANDLE);
380 encode_bytes(value.provider.as_bytes(), output)?;
381 encode_bytes(value.type_name.as_bytes(), output)?;
382 output.extend_from_slice(&value.handle.to_be_bytes());
383 }
384 Value::Tagged(value) => {
385 output.push(TAGGED);
386 encode_bare(&Value::Symbol(value.tag().clone()), output, depth + 1)?;
387 encode_bare(value.form(), output, depth + 1)?;
388 }
389 Value::ExceptionInfo(value) => {
390 if crate::core::map_entries(&value.data).is_none() {
391 return Err("hta/value-invalid: exception data must be a map".into());
392 }
393 if value
394 .cause
395 .as_deref()
396 .is_some_and(|cause| !matches!(cause, Value::ExceptionInfo(_)))
397 {
398 return Err("hta/value-invalid: exception cause must be an Exception".into());
399 }
400 output.push(EXCEPTION_INFO);
401 encode_bare(&Value::String(value.message.clone()), output, depth + 1)?;
402 encode_bare(&value.data, output, depth + 1)?;
403 encode_bare(
404 value.cause.as_deref().unwrap_or(&Value::Nil),
405 output,
406 depth + 1,
407 )?;
408 encode_bare(
409 &crate::core::exception_provenance_value(value),
410 output,
411 depth + 1,
412 )?;
413 }
414 Value::Result(value) => {
415 output.push(STRUCT);
416 encode_bare(&Value::String(RESULT_STRUCT_NAME.into()), output, depth + 1)?;
417 let fields = RESULT_STRUCT_FIELDS
418 .iter()
419 .map(|field| Value::String((*field).into()))
420 .collect::<Vec<_>>();
421 encode_sequence(VECTOR, fields.iter(), output, depth)?;
422 let values = [
423 value.status_value(),
424 value.data.clone(),
425 value.error_value(),
426 value.transport_context(),
427 ];
428 encode_sequence(VECTOR, values.iter(), output, depth)?;
429 }
430 Value::Struct(value) => {
431 output.push(STRUCT);
432 encode_bare(&Value::String(value.ty.name.clone()), output, depth + 1)?;
433 let fields = value
434 .ty
435 .fields
436 .iter()
437 .cloned()
438 .map(Value::String)
439 .collect::<Vec<_>>();
440 encode_sequence(VECTOR, fields.iter(), output, depth)?;
441 let values = value.ordered_values();
442 encode_sequence(VECTOR, values.into_iter(), output, depth)?;
443 }
444 Value::Pointer(value) => {
445 output.push(POINTER);
446 encode_bare(&Value::Keyword(value.context().clone()), output, depth + 1)?;
447 encode_bare(&Value::Map(value.fields().clone()), output, depth + 1)?;
448 }
449 Value::Mutable(_) | Value::MutableType(_) => {
450 return Err(
451 "hta/value-unsupported: mutable values are not serializable; use (into {} value)"
452 .into(),
453 )
454 }
455 _ => return Err(format!("hta/value-unsupported: {}", value.display())),
456 }
457 Ok(())
458}
459
460fn encode_map<'a>(
461 tag: u8,
462 values: impl Iterator<Item = (&'a Value, &'a Value)>,
463 output: &mut Vec<u8>,
464 depth: usize,
465) -> Result<(), String> {
466 let values = values.collect::<Vec<_>>();
467 output.push(tag);
468 encode_len(values.len(), output)?;
469 for (key, value) in values {
470 encode_bare(key, output, depth + 1)?;
471 encode_bare(value, output, depth + 1)?;
472 }
473 Ok(())
474}
475
476fn bare(value: &Value, depth: usize) -> Result<Vec<u8>, String> {
477 let mut output = Vec::new();
478 encode_bare(value, &mut output, depth)?;
479 Ok(output)
480}
481
482fn encode_sequence<'a>(
483 tag: u8,
484 values: impl Iterator<Item = &'a Value>,
485 output: &mut Vec<u8>,
486 depth: usize,
487) -> Result<(), String> {
488 let values = values.collect::<Vec<_>>();
489 output.push(tag);
490 encode_len(values.len(), output)?;
491 for value in values {
492 encode_bare(value, output, depth + 1)?;
493 }
494 Ok(())
495}
496
497fn encode_bytes(value: &[u8], output: &mut Vec<u8>) -> Result<(), String> {
498 encode_len(value.len(), output)?;
499 output.extend_from_slice(value);
500 Ok(())
501}
502fn encode_len(value: usize, output: &mut Vec<u8>) -> Result<(), String> {
503 let value = u32::try_from(value).map_err(|_| "hta/value-too-large")?;
504 output.extend_from_slice(&value.to_be_bytes());
505 Ok(())
506}
507
508fn decode_result_struct(
509 name: &str,
510 fields: &[String],
511 values: &[Value],
512) -> Result<Option<Value>, String> {
513 let exact_fields = fields.len() == RESULT_STRUCT_FIELDS.len()
514 && fields
515 .iter()
516 .zip(RESULT_STRUCT_FIELDS.iter())
517 .all(|(field, expected)| field == expected);
518 if name != RESULT_STRUCT_NAME || !exact_fields {
519 return Ok(None);
520 }
521 let [status, data, error, context] = values else {
522 return Err("hta/value-malformed: Result arity mismatch".into());
523 };
524 let result = match status {
525 Value::Keyword(status) if status.as_str() == "success" => {
526 if !matches!(error, Value::Nil) {
527 return Err("hta/value-malformed: success Result contains an error".into());
528 }
529 ResultValue::success(data.clone(), context.clone())
530 }
531 Value::Keyword(status) if status.as_str() == "error" => {
532 if !matches!(data, Value::Nil) {
533 return Err("hta/value-malformed: error Result contains success data".into());
534 }
535 if !matches!(error, Value::ExceptionInfo(_)) {
536 return Err("hta/value-malformed: error Result lacks a native Error".into());
537 }
538 ResultValue::error(error.clone(), context.clone())
539 }
540 _ => return Err("hta/value-malformed: invalid Result status".into()),
541 }
542 .map_err(|error| format!("hta/value-malformed: invalid Result: {error}"))?;
543 Ok(Some(Value::Result(std::rc::Rc::new(result))))
544}
545
546struct Reader<'a> {
547 bytes: &'a [u8],
548 cursor: usize,
549}
550impl Reader<'_> {
551 fn value(&mut self, depth: usize) -> Result<Value, String> {
552 if depth > MAX_NESTING_DEPTH {
553 return Err("hta/value-too-deep: nesting exceeds 256".into());
554 }
555 let tag = self.byte()?;
556 match tag {
557 NIL => Ok(Value::Nil),
558 FALSE => Ok(Value::Bool(false)),
559 TRUE => Ok(Value::Bool(true)),
560 I64 => {
561 let bytes = self.take(8)?;
562 Ok(Value::Number(i64::from_be_bytes(bytes.try_into().unwrap())))
563 }
564 F64 => {
565 let bytes = self.take(8)?;
566 let value = f64::from_bits(u64::from_be_bytes(bytes.try_into().unwrap()));
567 if !value.is_finite() {
568 return Err("hta/non-finite number".into());
569 }
570 Ok(Value::Float(value))
571 }
572 CHARACTER => {
573 let codepoint = u32::from_be_bytes(self.take(4)?.try_into().unwrap());
574 char::from_u32(codepoint)
575 .map(Value::Character)
576 .ok_or_else(|| "hta/value-malformed: invalid character scalar".into())
577 }
578 BIG_INTEGER => {
579 let text = String::from_utf8(self.data()?.to_vec())
580 .map_err(|_| "hta/value-malformed: invalid big integer")?;
581 let value = BigInt::parse_bytes(text.as_bytes(), 10)
582 .ok_or_else(|| "hta/value-malformed: invalid big integer".to_string())?;
583 Ok(crate::numeric::compact_integer(value))
584 }
585 REGEX => Ok(Value::Regex(
586 String::from_utf8(self.data()?.to_vec())
587 .map_err(|_| "hta/value-malformed: invalid regex")?,
588 )),
589 STRING => Ok(Value::String(
590 String::from_utf8(self.data()?.to_vec())
591 .map_err(|_| "hta/value-malformed: invalid UTF-8")?,
592 )),
593 BYTES => Ok(Value::Bytes(self.data()?.to_vec())),
594 KEYWORD => Ok(Value::Keyword(
595 String::from_utf8(self.data()?.to_vec())
596 .map_err(|_| "hta/value-malformed: invalid UTF-8")?
597 .into(),
598 )),
599 SYMBOL => Ok(Value::Symbol(
600 String::from_utf8(self.data()?.to_vec())
601 .map_err(|_| "hta/value-malformed: invalid UTF-8")?
602 .into(),
603 )),
604 LIST => Ok(Value::List(self.sequence(depth)?.into())),
605 TUPLE => Ok(Value::Tuple(Box::new(
606 crate::lang::data::Tuple::from_values(self.sequence(depth)?)?,
607 ))),
608 MAP_ENTRY => {
609 let values = self.sequence(depth)?;
610 let [key, value] = values.as_slice() else {
611 return Err("hta/value-malformed: map entry must contain two values".into());
612 };
613 Ok(Value::MapEntry(Box::new(PMapEntry::new(
614 key.clone(),
615 value.clone(),
616 ))))
617 }
618 VECTOR => Ok(Value::Vector(self.sequence(depth)?.into())),
619 CONS => {
620 let mut values = self.sequence(depth)?;
621 if values.is_empty() {
622 return Err("hta/value-malformed: empty cons".into());
623 }
624 let first = values.remove(0);
625 Ok(Value::Cons(Box::new(crate::lang::data::Cons::new(
626 first,
627 values.into_iter().collect(),
628 ))))
629 }
630 QUEUE => Ok(Value::Queue(Box::new(
631 self.sequence(depth)?.into_iter().collect(),
632 ))),
633 DEQUE => Ok(Value::Deque(Box::new(
634 self.sequence(depth)?.into_iter().collect(),
635 ))),
636 SET => Ok(Value::Set(self.sequence(depth)?.into())),
637 ORDERED_SET => Ok(Value::OrderedSet(Box::new(
638 self.sequence(depth)?.into_iter().collect(),
639 ))),
640 SORTED_SET => Ok(Value::SortedSet(Box::new(
641 self.sequence(depth)?.into_iter().collect(),
642 ))),
643 MAP => {
644 let size = self.len()?;
645 if size > self.bytes.len().saturating_sub(self.cursor) / 2 {
646 return Err("hta/value-malformed: impossible map length".into());
647 }
648 let mut values = Vec::with_capacity(size);
649 for _ in 0..size {
650 values.push((self.value(depth + 1)?, self.value(depth + 1)?));
651 }
652 Ok(Value::Map(values.into_iter().collect()))
653 }
654 ORDERED_MAP => Ok(Value::OrderedMap(Box::new(
655 self.entries(depth)?.into_iter().collect(),
656 ))),
657 SORTED_MAP => Ok(Value::SortedMap(Box::new(
658 self.entries(depth)?.into_iter().collect(),
659 ))),
660 PRIORITY_MAP => Ok(Value::PriorityMap(Box::new(
661 self.entries(depth)?.into_iter().collect(),
662 ))),
663 TRIE => {
664 let mut trie = crate::lang::data::Trie::new();
665 for (key, value) in self.entries(depth)? {
666 let Value::String(key) = key else {
667 return Err("hta/value-malformed: invalid trie key".into());
668 };
669 trie = trie.assoc_value(key, value);
670 }
671 Ok(Value::Trie(Box::new(trie)))
672 }
673 NAMESPACE => {
674 let name = String::from_utf8(self.data()?.to_vec())
675 .map_err(|_| "hta/value-malformed: invalid namespace name")?;
676 Ok(Value::Namespace(std::rc::Rc::new(
677 crate::kernel::Namespace::new(name),
678 )))
679 }
680 VAR => Err("hta/value-malformed: legacy var tag is not supported; use var-ref".into()),
681 VAR_REF => {
682 let symbol = match self.value(depth + 1)? {
683 Value::Symbol(symbol) if symbol.get_namespace().is_some() => symbol,
684 _ => return Err("hta/value-malformed: invalid Var reference".into()),
685 };
686 Ok(Value::Var(crate::kernel::Var::new(
687 symbol.as_str(),
688 Value::Nil,
689 )))
690 }
691 ATOM => Ok(Value::Atom(Box::new(crate::core::RuntimeAtom::new(
692 self.value(depth + 1)?,
693 true,
694 )))),
695 ARRAY => Ok(Value::Array(std::rc::Rc::new(std::cell::RefCell::new(
696 self.sequence(depth)?,
697 )))),
698 OBJECT => {
699 let size = self.len()?;
700 if size > self.bytes.len().saturating_sub(self.cursor) / 2 {
701 return Err("hta/value-malformed: impossible object length".into());
702 }
703 let mut values = Vec::with_capacity(size);
704 for _ in 0..size {
705 let Value::String(key) = self.value(depth + 1)? else {
706 return Err("hta/value-malformed: invalid object key".into());
707 };
708 values.push((key, self.value(depth + 1)?));
709 }
710 Ok(Value::Object(std::rc::Rc::new(std::cell::RefCell::new(
711 values,
712 ))))
713 }
714 HANDLE => {
715 let provider = String::from_utf8(self.data()?.to_vec())
716 .map_err(|_| "hta/value-malformed: invalid handle owner")?;
717 let type_name = String::from_utf8(self.data()?.to_vec())
718 .map_err(|_| "hta/value-malformed: invalid handle type")?;
719 let bytes = self.take(8)?;
720 Ok(Value::Extension(crate::core::ExtensionValue {
721 provider,
722 type_name,
723 handle: u64::from_be_bytes(bytes.try_into().unwrap()),
724 }))
725 }
726 TAGGED => {
727 let Value::Symbol(tag) = self.value(depth + 1)? else {
728 return Err("hta/value-malformed: invalid tagged literal tag".into());
729 };
730 Ok(Value::Tagged(Box::new(
731 crate::lang::data::TaggedLiteral::new(tag, self.value(depth + 1)?),
732 )))
733 }
734 EXCEPTION_INFO => {
735 let Value::String(message) = self.value(depth + 1)? else {
736 return Err("hta/value-malformed: invalid exception message".into());
737 };
738 let data = self.value(depth + 1)?;
739 let cause = match self.value(depth + 1)? {
740 Value::Nil => None,
741 value @ Value::ExceptionInfo(_) => Some(Box::new(value)),
742 _ => {
743 return Err("hta/value-malformed: invalid exception cause".into());
744 }
745 };
746 if crate::core::map_entries(&data).is_none() {
747 return Err("hta/value-malformed: invalid exception data".into());
748 }
749 let provenance = self.value(depth + 1)?;
750 let (created_at, throws) = decode_exception_provenance(provenance)?;
751 Ok(Value::ExceptionInfo(std::rc::Rc::new(
752 crate::core::ExceptionInfo {
753 message,
754 data: Box::new(data),
755 cause,
756 provenance: std::rc::Rc::new(std::cell::RefCell::new(
757 crate::core::ExceptionProvenance { created_at, throws },
758 )),
759 },
760 )))
761 }
762 STRUCT => {
763 let Value::String(name) = self.value(depth + 1)? else {
764 return Err("hta/value-malformed: invalid struct name".into());
765 };
766 let fields = match self.value(depth + 1)? {
767 Value::Vector(values) => values
768 .iter()
769 .map(|value| match value {
770 Value::String(field) => Ok(field.clone()),
771 _ => Err("hta/value-malformed: invalid struct field".into()),
772 })
773 .collect::<Result<Vec<_>, String>>()?,
774 _ => return Err("hta/value-malformed: invalid struct fields".into()),
775 };
776 let values: Vec<Value> = match self.value(depth + 1)? {
777 Value::Vector(values) => values.iter().cloned().collect(),
778 _ => return Err("hta/value-malformed: invalid struct values".into()),
779 };
780 if fields.len() != values.len() {
781 return Err("hta/value-malformed: struct arity mismatch".into());
782 }
783 if let Some(result) = decode_result_struct(&name, &fields, &values)? {
784 return Ok(result);
785 }
786 Ok(Value::Struct(std::rc::Rc::new(
787 crate::core::StructValue::from_values(
788 std::rc::Rc::new(crate::core::StructType::detached(name, fields)),
789 values,
790 None,
791 )?,
792 )))
793 }
794 POINTER => {
795 let Value::Keyword(context) = self.value(depth + 1)? else {
796 return Err("hta/value-malformed: invalid pointer context".into());
797 };
798 let Value::Map(fields) = self.value(depth + 1)? else {
799 return Err("hta/value-malformed: invalid pointer fields".into());
800 };
801 Ok(Value::Pointer(crate::lang::data::Pointer::new(
802 context, fields,
803 )))
804 }
805 _ => Err(format!("hta/value-malformed: unknown value tag {tag}")),
806 }
807 }
808 fn sequence(&mut self, depth: usize) -> Result<Vec<Value>, String> {
809 let size = self.len()?;
810 if size > self.bytes.len().saturating_sub(self.cursor) {
811 return Err("hta/value-malformed: impossible sequence length".into());
812 }
813 (0..size).map(|_| self.value(depth + 1)).collect()
814 }
815 fn entries(&mut self, depth: usize) -> Result<Vec<(Value, Value)>, String> {
816 let size = self.len()?;
817 if size > self.bytes.len().saturating_sub(self.cursor) / 2 {
818 return Err("hta/value-malformed: impossible map length".into());
819 }
820 (0..size)
821 .map(|_| Ok((self.value(depth + 1)?, self.value(depth + 1)?)))
822 .collect()
823 }
824 fn data(&mut self) -> Result<&[u8], String> {
825 let size = self.len()?;
826 self.take(size)
827 }
828 fn len(&mut self) -> Result<usize, String> {
829 let bytes = self.take(4)?;
830 Ok(u32::from_be_bytes(bytes.try_into().unwrap()) as usize)
831 }
832 fn byte(&mut self) -> Result<u8, String> {
833 Ok(self.take(1)?[0])
834 }
835 fn take(&mut self, size: usize) -> Result<&[u8], String> {
836 let end = self
837 .cursor
838 .checked_add(size)
839 .ok_or("hta/value-malformed: length overflow")?;
840 if end > self.bytes.len() {
841 return Err("hta/value-malformed: truncated value".into());
842 }
843 let output = &self.bytes[self.cursor..end];
844 self.cursor = end;
845 Ok(output)
846 }
847}
848
849#[cfg(test)]
850mod tests {
851 use super::*;
852 #[test]
853 fn canonical_round_trip() {
854 let value = Value::Map(
855 vec![
856 (Value::Keyword("b".into()), Value::Number(2)),
857 (
858 Value::Keyword("a".into()),
859 Value::Vector(PVector::from(vec![Value::Bool(true), Value::Nil])),
860 ),
861 ]
862 .into_iter()
863 .collect(),
864 );
865 let encoded = encode(&value).unwrap();
866 assert_eq!(encode(&decode(&encoded).unwrap()).unwrap(), encoded);
867 }
868 #[test]
869 fn compact_vectors_and_legacy_tuples_have_distinct_wire_boundaries() {
870 let tuple = Value::Tuple(Box::new(
871 PTuple::from_values(vec![Value::Number(1), Value::Number(2)]).unwrap(),
872 ));
873 let encoded = encode(&tuple).unwrap();
874 assert_eq!(encoded[4], VECTOR);
875 assert!(matches!(decode(&encoded).unwrap(), Value::Vector(_)));
876
877 let mut legacy = encoded;
878 legacy[4] = TUPLE;
879 assert!(matches!(decode(&legacy).unwrap(), Value::Tuple(_)));
880
881 let entry = Value::MapEntry(Box::new(PMapEntry::new(
882 Value::Keyword("key".into()),
883 Value::Number(42),
884 )));
885 let encoded_entry = encode(&entry).unwrap();
886 assert_eq!(encoded_entry[4], MAP_ENTRY);
887 assert_eq!(decode(&encoded_entry).unwrap(), entry);
888 }
889
890 #[test]
891 fn pointers_round_trip_as_descriptors() {
892 let fields = vec![(Value::Keyword("id".into()), Value::String("ROOT".into()))]
893 .into_iter()
894 .collect();
895 let pointer = Value::Pointer(crate::lang::data::Pointer::new(
896 crate::lang::data::Keyword::from("kernel"),
897 fields,
898 ));
899 assert_eq!(decode(&encode(&pointer).unwrap()).unwrap(), pointer);
900 }
901
902 #[test]
903 fn immutable_v3_values_round_trip_without_collection_normalization() {
904 let queue = Value::Queue(Box::new(
905 vec![Value::Number(1), Value::Number(2)]
906 .into_iter()
907 .collect(),
908 ));
909 assert!(matches!(
910 decode(&encode(&queue).unwrap()).unwrap(),
911 Value::Queue(_)
912 ));
913 let deque = Value::Deque(Box::new(
914 vec![Value::Number(1), Value::Number(2)]
915 .into_iter()
916 .collect(),
917 ));
918 assert!(matches!(
919 decode(&encode(&deque).unwrap()).unwrap(),
920 Value::Deque(_)
921 ));
922 let priority_map = Value::PriorityMap(Box::new(
923 vec![
924 (Value::Keyword("a".into()), Value::Number(2)),
925 (Value::Keyword("b".into()), Value::Number(1)),
926 ]
927 .into_iter()
928 .collect(),
929 ));
930 let decoded = decode(&encode(&priority_map).unwrap()).unwrap();
931 assert!(matches!(decoded, Value::PriorityMap(_)));
932 assert_eq!(
933 crate::core::map_entries(&decoded).unwrap()[0].0,
934 Value::Keyword("b".into())
935 );
936 let tagged = Value::Tagged(Box::new(crate::lang::data::TaggedLiteral::new(
937 crate::lang::data::Symbol::parse("demo/tag"),
938 Value::Number(42),
939 )));
940 assert!(matches!(
941 decode(&encode(&tagged).unwrap()).unwrap(),
942 Value::Tagged(_)
943 ));
944 }
945 #[test]
946 fn floats_round_trip_with_ieee_754_bits() {
947 for value in [0.28, -0.0] {
948 let decoded = decode(&encode(&Value::Float(value)).unwrap()).unwrap();
949 let Value::Float(decoded) = decoded else {
950 panic!("float value")
951 };
952 assert_eq!(decoded.to_bits(), value.to_bits());
953 }
954 for value in [f64::NAN, f64::INFINITY, f64::NEG_INFINITY] {
955 assert!(encode(&Value::Float(value)).is_err());
956 }
957 }
958
959 #[test]
960 fn big_integer_wire_widths_are_canonicalized() {
961 for (value, tag) in [
962 (BigInt::from(i64::MIN), I64),
963 (BigInt::from(42_i64), I64),
964 (BigInt::from(i64::MAX), I64),
965 (BigInt::from(i64::MAX) + 1, BIG_INTEGER),
966 ] {
967 let encoded = encode(&Value::BigInteger(value)).unwrap();
968 assert_eq!(encoded[4], tag);
969 assert_eq!(
970 decode_canonical(&encoded).unwrap(),
971 decode(&encoded).unwrap()
972 );
973 }
974 }
975
976 #[test]
977 fn canonical_decoder_rejects_noncanonical_big_integer_and_map_frames() {
978 let noncanonical_integer = b"HTA0\x14\0\0\0\x02\x34\x32";
979 assert!(decode_canonical(noncanonical_integer)
980 .unwrap_err()
981 .starts_with("hta/value-noncanonical:"));
982
983 let mut noncanonical_map = b"HTA0\x0b\0\0\0\x02".to_vec();
984 noncanonical_map.extend(bare(&Value::String("b".into()), 0).unwrap());
985 noncanonical_map.extend(bare(&Value::Number(2), 0).unwrap());
986 noncanonical_map.extend(bare(&Value::String("a".into()), 0).unwrap());
987 noncanonical_map.extend(bare(&Value::Number(1), 0).unwrap());
988 assert!(decode_canonical(&noncanonical_map)
989 .unwrap_err()
990 .starts_with("hta/value-noncanonical:"));
991 }
992
993 #[test]
994 fn portable_language_scalars_round_trip() {
995 for value in [
996 Value::Character('雪'),
997 Value::BigInteger(BigInt::parse_bytes(b"123456789012345678901234567890", 10).unwrap()),
998 Value::Float(1.25),
999 Value::Regex("^[a-z]+$".into()),
1000 ] {
1001 assert_eq!(decode(&encode(&value).unwrap()).unwrap(), value);
1002 }
1003 }
1004
1005 #[test]
1006 fn scalar_regex_and_pointer_tags_match_the_portable_golden_vectors() {
1007 assert_eq!(
1008 encode(&Value::Character('λ')).unwrap(),
1009 b"HTA0\x13\0\0\x03\xbb"
1010 );
1011 assert_eq!(
1012 encode(&Value::Regex("a+".into())).unwrap(),
1013 b"HTA0\x16\0\0\0\x02a+"
1014 );
1015 let fields = vec![(Value::Keyword("id".into()), Value::String("ROOT".into()))]
1016 .into_iter()
1017 .collect();
1018 let pointer = Value::Pointer(crate::lang::data::Pointer::new(
1019 crate::lang::data::Keyword::from("kernel"),
1020 fields,
1021 ));
1022 assert_eq!(
1023 encode(&pointer).unwrap(),
1024 b"HTA0\x22\x06\0\0\0\x06kernel\x0b\0\0\0\x01\x06\0\0\0\x02id\x04\0\0\0\x04ROOT"
1025 );
1026 }
1027
1028 #[test]
1029 fn tag_inventory_keeps_legacy_var_distinct_from_var_reference() {
1030 assert_eq!(
1031 HTA0_TAG_INVENTORY
1032 .iter()
1033 .find(|(_, name)| *name == "character"),
1034 Some(&(19, "character"))
1035 );
1036 assert_eq!(
1037 HTA0_TAG_INVENTORY
1038 .iter()
1039 .find(|(_, name)| *name == "pointer"),
1040 Some(&(34, "pointer"))
1041 );
1042 assert_eq!(
1043 HTA0_TAG_INVENTORY
1044 .iter()
1045 .find(|(_, name)| *name == "var-ref"),
1046 Some(&(35, "var-ref"))
1047 );
1048 assert!(decode(b"HTA0\x0e\x07\0\0\0\x04rank\0").is_err());
1049 }
1050
1051 #[test]
1052 fn native_result_round_trips_through_the_canonical_struct_shape() {
1053 let context = Value::Map(
1054 vec![(Value::Keyword("source".into()), Value::String("hta".into()))]
1055 .into_iter()
1056 .collect(),
1057 );
1058 let value = Value::Result(std::rc::Rc::new(
1059 ResultValue::success(Value::Number(42), context).unwrap(),
1060 ));
1061
1062 let encoded = encode(&value).unwrap();
1063 let decoded = decode(&encoded).unwrap();
1064
1065 assert_eq!(decoded, value);
1066 assert!(matches!(decoded, Value::Result(_)));
1067 assert!(encoded
1068 .windows(17)
1069 .any(|bytes| bytes == b"std.native/Result"));
1070 }
1071
1072 #[test]
1073 fn canonical_maps_ignore_insertion_order() {
1074 let a = Value::Map(
1075 vec![
1076 (Value::String("b".into()), Value::Number(2)),
1077 (Value::String("a".into()), Value::Number(1)),
1078 ]
1079 .into_iter()
1080 .collect(),
1081 );
1082 let b = Value::Map(
1083 vec![
1084 (Value::String("a".into()), Value::Number(1)),
1085 (Value::String("b".into()), Value::Number(2)),
1086 ]
1087 .into_iter()
1088 .collect(),
1089 );
1090 assert_eq!(encode(&a).unwrap(), encode(&b).unwrap());
1091 }
1092 #[test]
1093 fn namespaces_and_vars_use_snapshot_and_reference_contracts() {
1094 let namespace = crate::kernel::Namespace::new("example.lib");
1095 let var = namespace.intern("answer", Value::Number(42));
1096 let value = Value::Map(
1097 vec![
1098 (
1099 Value::Keyword("namespace".into()),
1100 Value::Namespace(std::rc::Rc::new(namespace)),
1101 ),
1102 (Value::Keyword("var".into()), Value::Var(var)),
1103 ]
1104 .into_iter()
1105 .collect(),
1106 );
1107 let decoded = decode(&encode(&value).unwrap()).unwrap();
1108 let Value::Map(decoded) = decoded else {
1109 panic!("map snapshot")
1110 };
1111 let Value::Namespace(namespace) = decoded.get(&Value::Keyword("namespace".into())).unwrap()
1112 else {
1113 panic!("namespace snapshot")
1114 };
1115 assert_eq!(namespace.name().as_str(), "example.lib");
1116 let Value::Var(var) = decoded.get(&Value::Keyword("var".into())).unwrap() else {
1117 panic!("var snapshot")
1118 };
1119 assert_eq!(var.symbol().as_str(), "example.lib/answer");
1120 assert_eq!(var.deref_value(), Value::Nil);
1121 let encoded = encode(&Value::Var(var.clone())).unwrap();
1122 assert_eq!(encoded[4], VAR_REF);
1123 assert_eq!(encoded, b"HTA0\x23\x07\x00\x00\x00\x12example.lib/answer");
1124 }
1125
1126 #[test]
1127 fn opaque_handles_round_trip() {
1128 let value = Value::Extension(crate::core::ExtensionValue {
1129 provider: "runtime".into(),
1130 type_name: "cursor".into(),
1131 handle: 42,
1132 });
1133 assert_eq!(decode(&encode(&value).unwrap()).unwrap(), value);
1134 }
1135
1136 #[test]
1137 fn structs_preserve_wire_shape_and_mutables_are_rejected() {
1138 let ty = std::rc::Rc::new(crate::core::StructType::detached(
1139 "demo/Point".into(),
1140 vec!["x".into(), "y".into()],
1141 ));
1142 let value = Value::Struct(std::rc::Rc::new(
1143 crate::core::StructValue::from_values(
1144 ty,
1145 vec![Value::Number(1), Value::Number(2)],
1146 None,
1147 )
1148 .unwrap(),
1149 ));
1150 let decoded = decode(&encode(&value).unwrap()).unwrap();
1151 assert_eq!(
1152 crate::core::call_value(Value::Keyword("x".into()), vec![decoded.clone()]).unwrap(),
1153 Value::Number(1)
1154 );
1155 assert_eq!(
1156 crate::core::call_value(
1157 Value::Keyword("missing".into()),
1158 vec![decoded.clone(), Value::Number(7)],
1159 )
1160 .unwrap(),
1161 Value::Number(7)
1162 );
1163 let Value::Struct(decoded) = decoded else {
1164 panic!("struct value")
1165 };
1166 assert_eq!(decoded.ty.name, "demo/Point");
1167 assert_eq!(decoded.ty.fields, vec!["x", "y"]);
1168 assert_eq!(
1169 decoded
1170 .ordered_values()
1171 .into_iter()
1172 .cloned()
1173 .collect::<Vec<_>>(),
1174 vec![Value::Number(1), Value::Number(2)]
1175 );
1176
1177 let mutable = Value::Mutable(std::rc::Rc::new(
1178 crate::core::MutableValue::from_values(
1179 std::rc::Rc::new(crate::core::MutableType::detached(
1180 "demo/Cursor".into(),
1181 vec!["x".into()],
1182 )),
1183 vec![Value::Number(1)],
1184 None,
1185 )
1186 .unwrap(),
1187 ));
1188 assert_eq!(
1189 encode(&mutable).unwrap_err(),
1190 "hta/value-unsupported: mutable values are not serializable; use (into {} value)"
1191 );
1192 }
1193
1194 #[test]
1195 fn nesting_depth_is_bounded_on_encode_and_decode() {
1196 let mut value = Value::Nil;
1197 for _ in 0..=MAX_NESTING_DEPTH {
1198 value = Value::Vector(PVector::from(vec![value]));
1199 }
1200 assert!(encode(&value).unwrap_err().contains("value-too-deep"));
1201
1202 let mut bytes = MAGIC.to_vec();
1203 for _ in 0..=MAX_NESTING_DEPTH {
1204 bytes.extend_from_slice(&[VECTOR, 0, 0, 0, 1]);
1205 }
1206 bytes.push(NIL);
1207 assert!(decode(&bytes).unwrap_err().contains("value-too-deep"));
1208 }
1209
1210 #[test]
1211 fn impossible_container_lengths_fail_before_allocating() {
1212 let mut bytes = MAGIC.to_vec();
1213 bytes.extend_from_slice(&[VECTOR, 0xff, 0xff, 0xff, 0xff]);
1214 assert!(decode(&bytes)
1215 .unwrap_err()
1216 .contains("impossible sequence length"));
1217 }
1218}