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