Skip to main content

hara_native/
hta.rs

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
51/// The canonical HTA0 tag inventory. Every host codec must preserve these
52/// numeric assignments; omitted values are reserved for future revisions.
53pub 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
200/// Decodes an HTA0 frame and verifies that its bytes are canonical.
201///
202/// The ordinary decoder is intentionally permissive for trusted legacy state;
203/// transport and artifact boundaries should use this entry point instead.
204pub 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}