1use std::collections::HashMap;
4use std::rc::Rc;
5
6use num_bigint::BigInt;
7use num_traits::ToPrimitive;
8use sha2::{Digest, Sha256};
9
10use super::opcode::Instruction;
11use super::program::{CatchEntry, FunctionPrototype, Program, TryEntry};
12use super::source_map::SourceMap;
13#[cfg(test)]
14use crate::core::Value;
15use crate::kernel::{FunctionSchema, Position, SchemaField, SchemaType};
16use crate::lang::data::{Keyword, Metadata, MetadataValue, Symbol};
17
18const MAGIC: &[u8; 4] = b"HBC0";
19
20pub fn encode_program(program: &Program) -> Result<Vec<u8>, String> {
23 super::validate::validate(program).map_err(|error| error.to_string())?;
24 let mut payload = Writer::default();
25 payload.u16(program.entry);
26 payload.option_string(program.namespace.as_deref())?;
27 payload.len(program.constants.len())?;
28 for value in &program.constants {
29 payload.bytes(&crate::hta::encode(value)?)?;
30 }
31 payload.len(program.var_metadata.len())?;
32 for metadata in &program.var_metadata {
33 write_metadata(&mut payload, metadata)?;
34 }
35 write_schema_map(&mut payload, &program.schema_types)?;
36 write_schema_map(&mut payload, &program.function_types)?;
37 write_schema_map(&mut payload, &program.inferred_function_types)?;
38 payload.len(program.functions.len())?;
39 for function in &program.functions {
40 write_function(&mut payload, function)?;
41 }
42 let digest = Sha256::digest(&payload.bytes);
43 let mut output = MAGIC.to_vec();
44 output.extend_from_slice(
45 &u32::try_from(payload.bytes.len())
46 .map_err(|_| "bytecode artifact is too large")?
47 .to_be_bytes(),
48 );
49 output.extend_from_slice(&payload.bytes);
50 output.extend_from_slice(&digest);
51 Ok(output)
52}
53
54pub fn decode_program(bytes: &[u8]) -> Result<Program, String> {
56 if !bytes.starts_with(MAGIC) {
57 return Err("bytecode artifact has invalid magic".into());
58 }
59 if bytes.len() < 8 + 32 {
60 return Err("bytecode artifact is truncated".into());
61 }
62 let payload_len = u32::from_be_bytes(bytes[4..8].try_into().unwrap()) as usize;
63 let payload_end = 8usize
64 .checked_add(payload_len)
65 .ok_or("bytecode artifact length overflow")?;
66 if payload_end.checked_add(32) != Some(bytes.len()) {
67 return Err("bytecode artifact length mismatch".into());
68 }
69 let payload = &bytes[8..payload_end];
70 if Sha256::digest(payload)[..] != bytes[payload_end..] {
71 return Err("bytecode artifact checksum mismatch".into());
72 }
73 let mut reader = Reader::new(payload);
74 let entry = reader.u16()?;
75 let namespace = reader.option_string()?;
76 let constants = reader.many(|reader| crate::hta::decode_canonical(reader.bytes()?))?;
77 let var_metadata = reader.many(|reader| read_metadata(reader))?;
78 let schema_types = read_schema_map(&mut reader)?;
79 let function_types = read_schema_map(&mut reader)?;
80 let inferred_function_types = read_schema_map(&mut reader)?;
81 let functions = reader.many(|reader| read_function(reader, 5))?;
82 reader.finish()?;
83 let program = Program {
84 namespace,
85 constants,
86 var_metadata,
87 schema_types,
88 function_types,
89 inferred_function_types,
90 functions,
91 entry,
92 };
93 super::validate::validate(&program).map_err(|error| error.to_string())?;
94 Ok(program)
95}
96
97fn write_function(out: &mut Writer, function: &FunctionPrototype) -> Result<(), String> {
98 out.option_string(function.name.as_deref())?;
99 out.byte(u8::from(function.async_function));
100 out.u16(function.arity);
101 out.byte(u8::from(function.variadic));
102 out.u16(function.capture_count);
103 out.u16(function.local_count);
104 out.u16(function.max_stack);
105 out.len(function.code.len())?;
106 for instruction in &function.code {
107 write_instruction(out, instruction);
108 }
109 out.len(function.source_map.len())?;
110 for index in 0..function.source_map.len() {
111 match function.source_map.position(index) {
112 Some(position) => {
113 out.byte(1);
114 out.usize32(position.offset)?;
115 out.usize32(position.line)?;
116 out.usize32(position.column)?;
117 }
118 None => out.byte(0),
119 }
120 }
121 out.len(function.handlers.len())?;
122 for handler in &function.handlers {
123 out.u32(handler.start);
124 out.u32(handler.end);
125 out.u16(handler.depth);
126 out.len(handler.catches.len())?;
127 for catch in &handler.catches {
128 out.string(&catch.class)?;
129 out.u16(catch.binding);
130 out.u32(catch.target);
131 }
132 out.option_u32(handler.finally);
133 out.option_u16(handler.pending_value);
134 out.option_u16(handler.pending_error);
135 }
136 Ok(())
137}
138
139fn read_function(reader: &mut Reader<'_>, version: u8) -> Result<FunctionPrototype, String> {
140 let name = reader.option_string()?;
141 let async_function = version >= 2 && reader.boolean()?;
142 let arity = reader.u16()?;
143 let variadic = reader.boolean()?;
144 let capture_count = reader.u16()?;
145 let local_count = reader.u16()?;
146 let max_stack = reader.u16()?;
147 let code = reader.many(read_instruction)?;
148 let positions = reader.many(|reader| {
149 if reader.boolean()? {
150 Ok(Some(Position {
151 offset: reader.u32()? as usize,
152 line: reader.u32()? as usize,
153 column: reader.u32()? as usize,
154 }))
155 } else {
156 Ok(None)
157 }
158 })?;
159 let mut source_map = SourceMap::default();
160 for position in positions {
161 source_map.record(position);
162 }
163 let handlers = reader.many(|reader| {
164 let start = reader.u32()?;
165 let end = reader.u32()?;
166 let depth = reader.u16()?;
167 let catches = reader.many(|reader| {
168 Ok(CatchEntry {
169 class: reader.string()?,
170 binding: reader.u16()?,
171 target: reader.u32()?,
172 })
173 })?;
174 Ok(TryEntry {
175 start,
176 end,
177 depth,
178 catches,
179 finally: reader.option_u32()?,
180 pending_value: reader.option_u16()?,
181 pending_error: reader.option_u16()?,
182 })
183 })?;
184 Ok(FunctionPrototype {
185 name,
186 async_function,
187 arity,
188 variadic,
189 capture_count,
190 local_count,
191 max_stack,
192 code,
193 source_map,
194 handlers,
195 })
196}
197
198fn write_instruction(out: &mut Writer, instruction: &Instruction) {
199 use Instruction::*;
200 match instruction {
201 Constant(value) => {
202 out.byte(0);
203 out.u32(*value);
204 }
205 Nil => out.byte(1),
206 True => out.byte(2),
207 False => out.byte(3),
208 LoadLocal(value) => {
209 out.byte(4);
210 out.u16(*value);
211 }
212 StoreLocal(value) => {
213 out.byte(5);
214 out.u16(*value);
215 }
216 Pop => out.byte(6),
217 Dup => out.byte(28),
218 IntrinsicCall { target, argc } => {
219 out.byte(50);
220 out.u32(*target);
221 out.byte(*argc);
222 }
223 Jump(value) => {
224 out.byte(8);
225 out.u32(*value);
226 }
227 JumpIfFalse(value) => {
228 out.byte(9);
229 out.u32(*value);
230 }
231 Closure {
232 prototype,
233 captures,
234 } => {
235 out.byte(10);
236 out.u16(*prototype);
237 out.byte(*captures);
238 }
239 Call { argc } => {
240 out.byte(11);
241 out.byte(*argc);
242 }
243 CallStatic { prototype, argc } => {
244 out.byte(12);
245 out.u16(*prototype);
246 out.byte(*argc);
247 }
248 Throw => out.byte(13),
249 Rethrow => out.byte(14),
250 GetGlobal(value) => {
251 out.byte(15);
252 out.u32(*value);
253 }
254 DefGlobal { name, metadata } => {
255 out.byte(16);
256 out.u32(*name);
257 out.option_u16(*metadata);
258 }
259 SetGlobal(value) => {
260 out.byte(17);
261 out.u32(*value);
262 }
263 VarGlobal(value) => {
264 out.byte(18);
265 out.u32(*value);
266 }
267 DeclareGlobal(value) => {
268 out.byte(19);
269 out.u32(*value);
270 }
271 InstanceOf => out.byte(22),
272 MakeMultiArity { name, count } => {
273 out.byte(23);
274 out.u32(*name);
275 out.byte(*count);
276 }
277 Await => out.byte(26),
278 HostCall => out.byte(27),
279 DotCall { method, argc } => {
280 out.byte(45);
281 out.u32(*method);
282 out.byte(*argc);
283 }
284 BuildVector(count) => {
285 out.byte(29);
286 out.u16(*count);
287 }
288 BuildMap(count) => {
289 out.byte(30);
290 out.u16(*count);
291 }
292 BuildSet(count) => {
293 out.byte(31);
294 out.u16(*count);
295 }
296 DefMacro { name, metadata } => {
297 out.byte(32);
298 out.u32(*name);
299 out.option_u16(*metadata);
300 }
301 BuildList(count) => {
302 out.byte(33);
303 out.u16(*count);
304 }
305 ConcatList(count) => {
306 out.byte(34);
307 out.u16(*count);
308 }
309 ToVector => out.byte(35),
310 IntrinsicValue(target) => {
311 out.byte(51);
312 out.u32(*target);
313 }
314 ProtocolCall { target, argc } => {
315 out.byte(52);
316 out.u32(*target);
317 out.byte(*argc);
318 }
319 BuiltinValue(index) => {
320 out.byte(38);
321 out.u32(*index);
322 }
323 NamespaceValue(index) => {
324 out.byte(53);
325 out.u32(*index);
326 }
327 NamespaceOperation(index) => {
328 out.byte(54);
329 out.u32(*index);
330 }
331 DynamicBind(index) => {
332 out.byte(39);
333 out.u32(*index);
334 }
335 DynamicUnbind(index) => {
336 out.byte(40);
337 out.u32(*index);
338 }
339 Yield => out.byte(46),
340 MutableFieldGet(value) => {
341 out.byte(48);
342 out.u32(*value);
343 }
344 MutableFieldSet(value) => {
345 out.byte(49);
346 out.u32(*value);
347 }
348 Return => out.byte(24),
349 }
350}
351
352fn read_instruction(reader: &mut Reader<'_>) -> Result<Instruction, String> {
353 Ok(match reader.byte()? {
354 0 => Instruction::Constant(reader.u32()?),
355 1 => Instruction::Nil,
356 2 => Instruction::True,
357 3 => Instruction::False,
358 4 => Instruction::LoadLocal(reader.u16()?),
359 5 => Instruction::StoreLocal(reader.u16()?),
360 6 => Instruction::Pop,
361 7 => {
362 return Err(
363 "bytecode artifact uses retired Primitive opcode 7; rebuild required".into(),
364 )
365 }
366 8 => Instruction::Jump(reader.u32()?),
367 9 => Instruction::JumpIfFalse(reader.u32()?),
368 10 => Instruction::Closure {
369 prototype: reader.u16()?,
370 captures: reader.byte()?,
371 },
372 11 => Instruction::Call {
373 argc: reader.byte()?,
374 },
375 12 => Instruction::CallStatic {
376 prototype: reader.u16()?,
377 argc: reader.byte()?,
378 },
379 13 => Instruction::Throw,
380 14 => Instruction::Rethrow,
381 15 => Instruction::GetGlobal(reader.u32()?),
382 16 => Instruction::DefGlobal {
383 name: reader.u32()?,
384 metadata: reader.option_u16()?,
385 },
386 17 => Instruction::SetGlobal(reader.u32()?),
387 18 => Instruction::VarGlobal(reader.u32()?),
388 19 => Instruction::DeclareGlobal(reader.u32()?),
389 20 => {
390 return Err(
391 "bytecode artifact uses retired DefStruct opcode 20; rebuild required".into(),
392 )
393 }
394 21 => {
395 return Err(
396 "bytecode artifact uses retired StructField opcode 21; rebuild required".into(),
397 )
398 }
399 22 => Instruction::InstanceOf,
400 23 => Instruction::MakeMultiArity {
401 name: reader.u32()?,
402 count: reader.byte()?,
403 },
404 24 => Instruction::Return,
405 25 => {
406 return Err(
407 "bytecode artifact uses retired PrimitiveLocalConst opcode 25; rebuild required"
408 .into(),
409 )
410 }
411 26 => Instruction::Await,
412 27 => Instruction::HostCall,
413 28 => Instruction::Dup,
414 29 => Instruction::BuildVector(reader.u16()?),
415 30 => Instruction::BuildMap(reader.u16()?),
416 31 => Instruction::BuildSet(reader.u16()?),
417 32 => Instruction::DefMacro {
418 name: reader.u32()?,
419 metadata: reader.option_u16()?,
420 },
421 33 => Instruction::BuildList(reader.u16()?),
422 34 => Instruction::ConcatList(reader.u16()?),
423 35 => Instruction::ToVector,
424 37 => {
425 return Err(
426 "bytecode artifact uses retired PrimitiveValue opcode 37; rebuild required".into(),
427 )
428 }
429 38 => Instruction::BuiltinValue(reader.u32()?),
430 39 => Instruction::DynamicBind(reader.u32()?),
431 40 => Instruction::DynamicUnbind(reader.u32()?),
432 41 => {
433 return Err(
434 "bytecode artifact uses retired DefProtocol opcode 41; rebuild required".into(),
435 )
436 }
437 42 => {
438 return Err(
439 "bytecode artifact uses retired ExtendType opcode 42; rebuild required".into(),
440 )
441 }
442 43 => {
443 return Err(
444 "bytecode artifact uses retired DefMulti opcode 43; rebuild required".into(),
445 )
446 }
447 44 => {
448 return Err(
449 "bytecode artifact uses retired DefMethod opcode 44; rebuild required".into(),
450 )
451 }
452 45 => Instruction::DotCall {
453 method: reader.u32()?,
454 argc: reader.byte()?,
455 },
456 46 => Instruction::Yield,
457 47 => {
458 return Err(
459 "bytecode artifact uses retired DefMutable opcode 47; rebuild required".into(),
460 )
461 }
462 48 => Instruction::MutableFieldGet(reader.u32()?),
463 49 => Instruction::MutableFieldSet(reader.u32()?),
464 50 => Instruction::IntrinsicCall {
465 target: reader.u32()?,
466 argc: reader.byte()?,
467 },
468 51 => Instruction::IntrinsicValue(reader.u32()?),
469 52 => Instruction::ProtocolCall {
470 target: reader.u32()?,
471 argc: reader.byte()?,
472 },
473 53 => Instruction::NamespaceValue(reader.u32()?),
474 54 => Instruction::NamespaceOperation(reader.u32()?),
475 _ => return Err("bytecode artifact contains an unknown opcode".into()),
476 })
477}
478
479fn write_metadata(out: &mut Writer, metadata: &Metadata) -> Result<(), String> {
480 out.len(metadata.entries().len())?;
481 for (key, value) in metadata.entries() {
482 write_metadata_value(out, key)?;
483 write_metadata_value(out, value)?;
484 }
485 Ok(())
486}
487
488fn read_metadata(reader: &mut Reader<'_>) -> Result<Rc<Metadata>, String> {
489 let entries =
490 reader.many(|reader| Ok((read_metadata_value(reader)?, read_metadata_value(reader)?)))?;
491 Ok(Metadata::new(entries))
492}
493
494fn write_metadata_value(out: &mut Writer, value: &MetadataValue) -> Result<(), String> {
495 use MetadataValue::*;
496 match value {
497 Nil => out.byte(0),
498 Boolean(v) => {
499 out.byte(1);
500 out.byte(u8::from(*v));
501 }
502 Number(v) => {
503 out.byte(2);
504 out.i64(*v);
505 }
506 Float(v) => {
507 if !v.is_finite() {
508 return Err("non-finite number".into());
509 }
510 out.byte(3);
511 out.u64(v.to_bits());
512 }
513 BigInteger(v) => {
514 if let Some(value) = v.to_i64() {
515 out.byte(2);
516 out.i64(value);
517 } else {
518 out.byte(4);
519 out.string(&v.to_string())?;
520 }
521 }
522 Character(v) => {
523 out.byte(6);
524 out.u32(*v as u32);
525 }
526 Regex(v) => {
527 out.byte(7);
528 out.string(v)?;
529 }
530 Tagged(tag, value) => {
531 out.byte(8);
532 out.string(tag)?;
533 write_metadata_value(out, value)?;
534 }
535 String(v) => {
536 out.byte(9);
537 out.string(v)?;
538 }
539 Keyword(v) => {
540 out.byte(10);
541 out.string(v.as_str())?;
542 }
543 Symbol(v) => {
544 out.byte(11);
545 out.string(v.as_str())?;
546 }
547 Vector(values) => {
548 out.byte(12);
549 write_metadata_values(out, values)?;
550 }
551 List(values) => {
552 out.byte(13);
553 write_metadata_values(out, values)?;
554 }
555 Set(values) => {
556 out.byte(14);
557 write_metadata_values(out, values)?;
558 }
559 Map(values) => {
560 out.byte(15);
561 out.len(values.len())?;
562 for (k, v) in values {
563 write_metadata_value(out, k)?;
564 write_metadata_value(out, v)?;
565 }
566 }
567 }
568 Ok(())
569}
570
571fn write_metadata_values(out: &mut Writer, values: &[MetadataValue]) -> Result<(), String> {
572 out.len(values.len())?;
573 for value in values {
574 write_metadata_value(out, value)?;
575 }
576 Ok(())
577}
578
579fn read_metadata_value(reader: &mut Reader<'_>) -> Result<MetadataValue, String> {
580 Ok(match reader.byte()? {
581 0 => MetadataValue::Nil,
582 1 => MetadataValue::Boolean(reader.boolean()?),
583 2 => MetadataValue::Number(reader.i64()?),
584 3 => {
585 let value = f64::from_bits(reader.u64()?);
586 if !value.is_finite() {
587 return Err("non-finite number".into());
588 }
589 MetadataValue::Float(value)
590 }
591 4 => {
592 let value = BigInt::parse_bytes(reader.string()?.as_bytes(), 10)
593 .ok_or("invalid metadata big integer")?;
594 value
595 .to_i64()
596 .map(MetadataValue::Number)
597 .unwrap_or(MetadataValue::BigInteger(value))
598 }
599 5 => return Err("unsupported metadata tag: decimal".into()),
600 6 => MetadataValue::Character(
601 char::from_u32(reader.u32()?).ok_or("invalid metadata character")?,
602 ),
603 7 => MetadataValue::Regex(reader.string()?),
604 8 => MetadataValue::Tagged(reader.string()?, Box::new(read_metadata_value(reader)?)),
605 9 => MetadataValue::String(reader.string()?),
606 10 => MetadataValue::Keyword(Keyword::from(reader.string()?)),
607 11 => MetadataValue::Symbol(Symbol::from(reader.string()?)),
608 12 => MetadataValue::Vector(reader.many(read_metadata_value)?),
609 13 => MetadataValue::List(reader.many(read_metadata_value)?),
610 14 => MetadataValue::Set(reader.many(read_metadata_value)?),
611 15 => MetadataValue::Map(
612 reader.many(|r| Ok((read_metadata_value(r)?, read_metadata_value(r)?)))?,
613 ),
614 _ => return Err("bytecode artifact contains unknown metadata".into()),
615 })
616}
617
618fn write_schema_map(out: &mut Writer, schemas: &HashMap<String, SchemaType>) -> Result<(), String> {
619 let mut names = schemas.keys().collect::<Vec<_>>();
620 names.sort();
621 out.len(names.len())?;
622 for name in names {
623 out.string(name)?;
624 write_schema_type(out, &schemas[name])?;
625 }
626 Ok(())
627}
628
629fn read_schema_map(reader: &mut Reader<'_>) -> Result<HashMap<String, SchemaType>, String> {
630 let entries = reader.many(|reader| Ok((reader.string()?, read_schema_type(reader)?)))?;
631 let mut schemas = HashMap::with_capacity(entries.len());
632 for (name, schema) in entries {
633 if schemas.insert(name.clone(), schema).is_some() {
634 return Err(format!(
635 "bytecode artifact contains duplicate schema {name}"
636 ));
637 }
638 }
639 Ok(schemas)
640}
641
642fn write_schema_type(out: &mut Writer, schema: &SchemaType) -> Result<(), String> {
643 match schema {
644 SchemaType::Primitive(name) => {
645 out.byte(0);
646 out.string(name)?;
647 }
648 SchemaType::Reference(name) => {
649 out.byte(1);
650 out.string(name)?;
651 }
652 SchemaType::Union(types) => {
653 out.byte(2);
654 write_schema_types(out, types)?;
655 }
656 SchemaType::Vector(item) => {
657 out.byte(3);
658 write_schema_type(out, item)?;
659 }
660 SchemaType::Set(item) => {
661 out.byte(10);
662 write_schema_type(out, item)?;
663 }
664 SchemaType::Tuple(items) => {
665 out.byte(4);
666 write_schema_types(out, items)?;
667 }
668 SchemaType::Map(fields) => {
669 let property_aware = fields.iter().any(|field| field.properties.is_some());
672 out.byte(if property_aware { 12 } else { 5 });
673 out.len(fields.len())?;
674 for field in fields {
675 write_schema_form(out, &field.name)?;
676 if property_aware {
677 match &field.properties {
678 Some(properties) => {
679 out.byte(1);
680 write_schema_form(out, properties)?;
681 }
682 None => out.byte(0),
683 }
684 }
685 write_schema_type(out, &field.value_type)?;
686 }
687 }
688 SchemaType::Struct {
689 name,
690 mutable,
691 fields,
692 } => {
693 out.byte(13);
694 out.string(name)?;
695 out.byte(u8::from(*mutable));
696 let property_aware = fields.iter().any(|field| field.properties.is_some());
697 out.byte(u8::from(property_aware));
698 out.len(fields.len())?;
699 for field in fields {
700 write_schema_form(out, &field.name)?;
701 if property_aware {
702 match &field.properties {
703 Some(properties) => {
704 out.byte(1);
705 write_schema_form(out, properties)?;
706 }
707 None => out.byte(0),
708 }
709 }
710 write_schema_type(out, &field.value_type)?;
711 }
712 }
713 SchemaType::WithProperties { schema, properties } => {
714 out.byte(11);
715 write_schema_type(out, schema)?;
716 write_schema_form(out, properties)?;
717 }
718 SchemaType::Function(arities) => {
719 out.byte(6);
720 out.len(arities.len())?;
721 for arity in arities {
722 write_schema_types(out, &arity.fixed)?;
723 match &arity.rest {
724 Some(rest) => {
725 out.byte(1);
726 write_schema_type(out, rest)?;
727 }
728 None => out.byte(0),
729 }
730 write_schema_type(out, &arity.output)?;
731 }
732 }
733 SchemaType::Enum(values) => {
734 out.byte(7);
735 write_schema_forms(out, values)?;
736 }
737 SchemaType::Extension { head, arguments } => {
738 out.byte(8);
739 out.string(head)?;
740 write_schema_forms(out, arguments)?;
741 }
742 SchemaType::Unknown(surface) => {
743 out.byte(9);
744 write_schema_form(out, surface)?;
745 }
746 }
747 Ok(())
748}
749
750fn read_schema_type(reader: &mut Reader<'_>) -> Result<SchemaType, String> {
751 Ok(match reader.byte()? {
752 0 => SchemaType::Primitive(reader.string()?),
753 1 => SchemaType::Reference(reader.string()?),
754 2 => SchemaType::Union(reader.many(read_schema_type)?),
755 3 => SchemaType::Vector(Box::new(read_schema_type(reader)?)),
756 4 => SchemaType::Tuple(reader.many(read_schema_type)?),
757 5 => SchemaType::Map(reader.many(|reader| {
758 Ok(SchemaField {
759 name: read_schema_form(reader)?,
760 properties: None,
761 value_type: read_schema_type(reader)?,
762 })
763 })?),
764 6 => SchemaType::Function(reader.many(|reader| {
765 let fixed = reader.many(read_schema_type)?;
766 let rest = if reader.boolean()? {
767 Some(Box::new(read_schema_type(reader)?))
768 } else {
769 None
770 };
771 Ok(FunctionSchema {
772 fixed,
773 rest,
774 output: Box::new(read_schema_type(reader)?),
775 })
776 })?),
777 7 => SchemaType::Enum(read_schema_forms(reader)?),
778 8 => SchemaType::Extension {
779 head: reader.string()?,
780 arguments: read_schema_forms(reader)?,
781 },
782 9 => SchemaType::Unknown(read_schema_form(reader)?),
783 10 => SchemaType::Set(Box::new(read_schema_type(reader)?)),
784 11 => SchemaType::WithProperties {
785 schema: Box::new(read_schema_type(reader)?),
786 properties: read_schema_form(reader)?,
787 },
788 12 => SchemaType::Map(reader.many(|reader| {
789 let name = read_schema_form(reader)?;
790 let properties = if reader.boolean()? {
791 Some(read_schema_form(reader)?)
792 } else {
793 None
794 };
795 Ok(SchemaField {
796 name,
797 properties,
798 value_type: read_schema_type(reader)?,
799 })
800 })?),
801 13 => {
802 let name = reader.string()?;
803 let mutable = reader.boolean()?;
804 let property_aware = reader.boolean()?;
805 SchemaType::Struct {
806 name,
807 mutable,
808 fields: reader.many(|reader| {
809 let name = read_schema_form(reader)?;
810 let properties = if property_aware {
811 if reader.boolean()? {
812 Some(read_schema_form(reader)?)
813 } else {
814 None
815 }
816 } else {
817 None
818 };
819 Ok(SchemaField {
820 name,
821 properties,
822 value_type: read_schema_type(reader)?,
823 })
824 })?,
825 }
826 }
827 _ => return Err("bytecode artifact contains unknown schema type".into()),
828 })
829}
830
831fn write_schema_types(out: &mut Writer, types: &[SchemaType]) -> Result<(), String> {
832 out.len(types.len())?;
833 for schema in types {
834 write_schema_type(out, schema)?;
835 }
836 Ok(())
837}
838
839fn write_schema_forms(out: &mut Writer, forms: &[crate::kernel::Form]) -> Result<(), String> {
840 out.len(forms.len())?;
841 for form in forms {
842 write_schema_form(out, form)?;
843 }
844 Ok(())
845}
846
847fn read_schema_forms(reader: &mut Reader<'_>) -> Result<Vec<crate::kernel::Form>, String> {
848 reader.many(read_schema_form)
849}
850
851fn write_schema_form(out: &mut Writer, form: &crate::kernel::Form) -> Result<(), String> {
852 out.string(&form.to_string())
853}
854
855fn read_schema_form(reader: &mut Reader<'_>) -> Result<crate::kernel::Form, String> {
856 crate::kernel::parse(&reader.string()?)
857 .map_err(|error| format!("bytecode artifact contains invalid schema form: {error}"))
858}
859
860#[derive(Default)]
861struct Writer {
862 bytes: Vec<u8>,
863}
864impl Writer {
865 fn byte(&mut self, value: u8) {
866 self.bytes.push(value);
867 }
868 fn u16(&mut self, value: u16) {
869 self.bytes.extend_from_slice(&value.to_be_bytes());
870 }
871 fn u32(&mut self, value: u32) {
872 self.bytes.extend_from_slice(&value.to_be_bytes());
873 }
874 fn u64(&mut self, value: u64) {
875 self.bytes.extend_from_slice(&value.to_be_bytes());
876 }
877 fn i64(&mut self, value: i64) {
878 self.bytes.extend_from_slice(&value.to_be_bytes());
879 }
880 fn usize32(&mut self, value: usize) -> Result<(), String> {
881 self.u32(u32::try_from(value).map_err(|_| "bytecode field is too large")?);
882 Ok(())
883 }
884 fn len(&mut self, value: usize) -> Result<(), String> {
885 self.usize32(value)
886 }
887 fn bytes(&mut self, value: &[u8]) -> Result<(), String> {
888 self.len(value.len())?;
889 self.bytes.extend_from_slice(value);
890 Ok(())
891 }
892 fn string(&mut self, value: &str) -> Result<(), String> {
893 self.bytes(value.as_bytes())
894 }
895 fn option_string(&mut self, value: Option<&str>) -> Result<(), String> {
896 match value {
897 Some(v) => {
898 self.byte(1);
899 self.string(v)?;
900 }
901 None => self.byte(0),
902 };
903 Ok(())
904 }
905 fn option_u16(&mut self, value: Option<u16>) {
906 match value {
907 Some(v) => {
908 self.byte(1);
909 self.u16(v);
910 }
911 None => self.byte(0),
912 }
913 }
914 fn option_u32(&mut self, value: Option<u32>) {
915 match value {
916 Some(v) => {
917 self.byte(1);
918 self.u32(v);
919 }
920 None => self.byte(0),
921 }
922 }
923}
924
925struct Reader<'a> {
926 bytes: &'a [u8],
927 cursor: usize,
928}
929impl<'a> Reader<'a> {
930 fn new(bytes: &'a [u8]) -> Self {
931 Self { bytes, cursor: 0 }
932 }
933 fn take(&mut self, size: usize) -> Result<&'a [u8], String> {
934 let end = self
935 .cursor
936 .checked_add(size)
937 .ok_or("bytecode artifact length overflow")?;
938 if end > self.bytes.len() {
939 return Err("bytecode artifact is truncated".into());
940 }
941 let value = &self.bytes[self.cursor..end];
942 self.cursor = end;
943 Ok(value)
944 }
945 fn byte(&mut self) -> Result<u8, String> {
946 Ok(self.take(1)?[0])
947 }
948 fn boolean(&mut self) -> Result<bool, String> {
949 match self.byte()? {
950 0 => Ok(false),
951 1 => Ok(true),
952 _ => Err("bytecode artifact contains invalid boolean".into()),
953 }
954 }
955 fn u16(&mut self) -> Result<u16, String> {
956 Ok(u16::from_be_bytes(self.take(2)?.try_into().unwrap()))
957 }
958 fn u32(&mut self) -> Result<u32, String> {
959 Ok(u32::from_be_bytes(self.take(4)?.try_into().unwrap()))
960 }
961 fn u64(&mut self) -> Result<u64, String> {
962 Ok(u64::from_be_bytes(self.take(8)?.try_into().unwrap()))
963 }
964 fn i64(&mut self) -> Result<i64, String> {
965 Ok(i64::from_be_bytes(self.take(8)?.try_into().unwrap()))
966 }
967 fn bytes(&mut self) -> Result<&'a [u8], String> {
968 let size = self.u32()? as usize;
969 self.take(size)
970 }
971 fn string(&mut self) -> Result<String, String> {
972 String::from_utf8(self.bytes()?.to_vec())
973 .map_err(|_| "bytecode artifact contains invalid UTF-8".into())
974 }
975 fn option_string(&mut self) -> Result<Option<String>, String> {
976 if self.boolean()? {
977 Ok(Some(self.string()?))
978 } else {
979 Ok(None)
980 }
981 }
982 fn option_u16(&mut self) -> Result<Option<u16>, String> {
983 if self.boolean()? {
984 Ok(Some(self.u16()?))
985 } else {
986 Ok(None)
987 }
988 }
989 fn option_u32(&mut self) -> Result<Option<u32>, String> {
990 if self.boolean()? {
991 Ok(Some(self.u32()?))
992 } else {
993 Ok(None)
994 }
995 }
996 fn many<T>(
997 &mut self,
998 mut read: impl FnMut(&mut Reader<'a>) -> Result<T, String>,
999 ) -> Result<Vec<T>, String> {
1000 let size = self.u32()? as usize;
1001 let mut values = Vec::with_capacity(size.min(4096));
1002 for _ in 0..size {
1003 values.push(read(self)?);
1004 }
1005 Ok(values)
1006 }
1007 fn finish(&self) -> Result<(), String> {
1008 if self.cursor == self.bytes.len() {
1009 Ok(())
1010 } else {
1011 Err("bytecode artifact has trailing payload bytes".into())
1012 }
1013 }
1014}
1015
1016#[cfg(test)]
1017#[path = "artifact/tests.rs"]
1018mod tests;