1use alloc::string::String;
4use alloc::vec::Vec;
5
6use crate::codec::{
7 crc32, write_def_id, write_i32, write_str, write_u8, write_u16, write_u32, write_u64,
8 write_varint,
9};
10use crate::definition::{
11 AddressDef, AddressPath, AliasEntry, CallAtom, CapabilityParam, ContainerDef, DebugInfoSection,
12 DirectEffects, EffectRowEntry, ExternalFnDef, FrameShapeDef, GlobalVarDef, LineEntry,
13 LineVariantGroup, ListDef, ListItemDef, ScopeLineTable, StructShapeDef,
14};
15use crate::id::DefinitionId;
16use crate::line::{LineContent, LinePart, PluralCategory, SelectKey};
17use crate::story::StoryData;
18use crate::value::{ListValue, MapKey, ProjSegment, Value, ValueType};
19
20use super::{
21 CAP_PARAM_ANY, CAT_FEW, CAT_MANY, CAT_ONE, CAT_OTHER, CAT_TWO, CAT_ZERO, HANDLE_PARAM_NONE,
22 HEADER_PREAMBLE, KEY_CARDINAL, KEY_EXACT, KEY_KEYWORD, KEY_ORDINAL, LINE_PLAIN, LINE_TEMPLATE,
23 MAGIC, PART_LITERAL, PART_SELECT, PART_SLOT, PART_SPAN, PROJ_SEG_INDEX, PROJ_SEG_KEY,
24 SECTION_COUNT, SECTION_ENTRY_SIZE, SectionKind, VAL_ARRAY, VAL_BOOL, VAL_CLOSURE,
25 VAL_DIVERT_TARGET, VAL_FLOAT, VAL_FN_REF, VAL_FRAGMENT_REF, VAL_HANDLE, VAL_INT, VAL_LIST,
26 VAL_MAP, VAL_MAT2, VAL_MAT3, VAL_MAT4, VAL_NULL, VAL_OPTION, VAL_PROJECTION, VAL_QUAT,
27 VAL_RANGE, VAL_RECORD, VAL_STRING, VAL_VAR_POINTER, VAL_VEC2, VAL_VEC3, VAL_VEC4, VAL_WEIGHTED,
28 VERSION,
29};
30
31#[expect(clippy::cast_possible_truncation, clippy::too_many_lines)]
35pub fn write_inkb(story: &StoryData, buf: &mut Vec<u8>) {
36 let base = buf.len();
37
38 let has_visibility = !story.private_defs.is_empty();
45 let has_frame_shapes = !story.frame_shapes.is_empty();
50 let has_debug_info = story.debug_info.is_some();
58 let has_line_variant_groups = !story.line_variant_groups.is_empty();
63 let section_count = SECTION_COUNT as usize
64 + usize::from(has_visibility)
65 + usize::from(has_frame_shapes)
66 + usize::from(has_debug_info)
67 + usize::from(has_line_variant_groups);
68 let header_size = HEADER_PREAMBLE + section_count * SECTION_ENTRY_SIZE;
69
70 buf.resize(base + header_size, 0);
72
73 let mut sections: Vec<(SectionKind, u32)> = Vec::with_capacity(section_count);
77
78 macro_rules! section {
79 ($kind:expr, $write:expr) => {{
80 let offset = (buf.len() - base) as u32;
81 $write;
82 sections.push(($kind, offset));
83 }};
84 }
85
86 section!(
87 SectionKind::NameTable,
88 write_section_name_table(&story.name_table, buf)
89 );
90 section!(
91 SectionKind::Variables,
92 write_section_variables(&story.variables, buf)
93 );
94 section!(
95 SectionKind::ListDefs,
96 write_section_list_defs(&story.list_defs, buf)
97 );
98 section!(
99 SectionKind::ListItems,
100 write_section_list_items(&story.list_items, buf)
101 );
102 section!(
103 SectionKind::Externals,
104 write_section_externals(&story.externals, buf)
105 );
106 section!(
107 SectionKind::Containers,
108 write_section_containers(&story.containers, buf)
109 );
110 section!(
111 SectionKind::LineTables,
112 write_section_line_tables(&story.line_tables, buf)
113 );
114 section!(
115 SectionKind::Labels,
116 write_section_addresses(&story.addresses, buf)
117 );
118 section!(
119 SectionKind::ListLiterals,
120 write_section_list_literals(&story.list_literals, buf)
121 );
122 section!(
123 SectionKind::AddressPaths,
124 write_section_address_paths(&story.address_paths, buf)
125 );
126 section!(
127 SectionKind::LiteralPool,
128 write_section_literal_pool(&story.literal_pool, buf)
129 );
130 section!(
131 SectionKind::StructShapes,
132 write_section_struct_shapes(&story.struct_shapes, buf)
133 );
134 section!(
138 SectionKind::EffectRows,
139 write_section_effect_rows(&story.effect_rows, buf)
140 );
141 if has_visibility {
142 section!(
143 SectionKind::Visibility,
144 write_section_visibility(&story.private_defs, buf)
145 );
146 }
147 section!(
150 SectionKind::AliasTable,
151 write_section_alias_table(&story.alias_table, buf)
152 );
153 if has_frame_shapes {
157 section!(
158 SectionKind::FrameShapes,
159 write_section_frame_shapes(&story.frame_shapes, buf)
160 );
161 }
162 if let Some(debug_info) = &story.debug_info {
167 section!(
168 SectionKind::DebugInfo,
169 write_section_debug_info(debug_info, buf)
170 );
171 }
172 if has_line_variant_groups {
176 section!(
177 SectionKind::LineVariantGroups,
178 write_section_line_variant_groups(&story.line_variant_groups, buf)
179 );
180 }
181
182 let file_size = (buf.len() - base) as u32;
183 let checksum = crc32(&buf[base + header_size..]);
184
185 let h = &mut buf[base..];
187 h[0..4].copy_from_slice(MAGIC);
188 h[4..6].copy_from_slice(&VERSION.to_le_bytes());
189 h[6] = section_count as u8;
190 h[7] = 0; h[8..12].copy_from_slice(&file_size.to_le_bytes());
192 h[12..16].copy_from_slice(&checksum.to_le_bytes());
193
194 for (i, (kind, offset)) in sections.iter().enumerate() {
195 let entry_base = HEADER_PREAMBLE + i * SECTION_ENTRY_SIZE;
196 h[entry_base] = *kind as u8;
197 h[entry_base + 1] = 0; h[entry_base + 2] = 0;
199 h[entry_base + 3] = 0;
200 h[entry_base + 4..entry_base + 8].copy_from_slice(&offset.to_le_bytes());
201 }
202}
203
204#[expect(clippy::cast_possible_truncation)]
211pub fn assemble_inkb(sections: &[(SectionKind, &[u8])], out: &mut Vec<u8>) {
212 let base = out.len();
213 let section_count = sections.len() as u8;
214 let header_size = HEADER_PREAMBLE + sections.len() * SECTION_ENTRY_SIZE;
215
216 out.resize(base + header_size, 0);
218
219 let mut entries: Vec<(SectionKind, u32)> = Vec::with_capacity(sections.len());
221 for (kind, data) in sections {
222 let offset = (out.len() - base) as u32;
223 entries.push((*kind, offset));
224 out.extend_from_slice(data);
225 }
226
227 let file_size = (out.len() - base) as u32;
228 let checksum = crc32(&out[base + header_size..]);
229
230 let h = &mut out[base..];
232 h[0..4].copy_from_slice(MAGIC);
233 h[4..6].copy_from_slice(&VERSION.to_le_bytes());
234 h[6] = section_count;
235 h[7] = 0;
236 h[8..12].copy_from_slice(&file_size.to_le_bytes());
237 h[12..16].copy_from_slice(&checksum.to_le_bytes());
238
239 for (i, (kind, offset)) in entries.iter().enumerate() {
240 let entry_base = HEADER_PREAMBLE + i * SECTION_ENTRY_SIZE;
241 h[entry_base] = *kind as u8;
242 h[entry_base + 1] = 0;
243 h[entry_base + 2] = 0;
244 h[entry_base + 3] = 0;
245 h[entry_base + 4..entry_base + 8].copy_from_slice(&offset.to_le_bytes());
246 }
247}
248
249#[expect(clippy::cast_possible_truncation)]
253pub fn write_section_name_table(names: &[String], buf: &mut Vec<u8>) {
254 write_u32(buf, names.len() as u32);
255 for name in names {
256 write_str(buf, name);
257 }
258}
259
260#[expect(clippy::cast_possible_truncation)]
262pub fn write_section_variables(variables: &[GlobalVarDef], buf: &mut Vec<u8>) {
263 write_u32(buf, variables.len() as u32);
264 for var in variables {
265 encode_global_var(var, buf);
266 }
267}
268
269#[expect(clippy::cast_possible_truncation)]
271pub fn write_section_list_defs(list_defs: &[ListDef], buf: &mut Vec<u8>) {
272 write_u32(buf, list_defs.len() as u32);
273 for ld in list_defs {
274 encode_list_def(ld, buf);
275 }
276}
277
278#[expect(clippy::cast_possible_truncation)]
280pub fn write_section_list_items(list_items: &[ListItemDef], buf: &mut Vec<u8>) {
281 write_u32(buf, list_items.len() as u32);
282 for li in list_items {
283 encode_list_item(li, buf);
284 }
285}
286
287#[expect(clippy::cast_possible_truncation)]
289pub fn write_section_externals(externals: &[ExternalFnDef], buf: &mut Vec<u8>) {
290 write_u32(buf, externals.len() as u32);
291 for ext in externals {
292 encode_external(ext, buf);
293 }
294}
295
296#[expect(clippy::cast_possible_truncation)]
298pub fn write_section_containers(containers: &[ContainerDef], buf: &mut Vec<u8>) {
299 write_u32(buf, containers.len() as u32);
300 for c in containers {
301 encode_container(c, buf);
302 }
303}
304
305#[expect(clippy::cast_possible_truncation)]
307pub fn write_section_addresses(addresses: &[AddressDef], buf: &mut Vec<u8>) {
308 write_u32(buf, addresses.len() as u32);
309 for addr in addresses {
310 write_def_id(buf, addr.id);
311 write_def_id(buf, addr.container_id);
312 write_u32(buf, addr.byte_offset);
313 }
314}
315
316#[expect(clippy::cast_possible_truncation)]
318pub fn write_section_address_paths(address_paths: &[AddressPath], buf: &mut Vec<u8>) {
319 write_u32(buf, address_paths.len() as u32);
320 for ap in address_paths {
321 write_u16(buf, ap.path.0);
322 write_def_id(buf, ap.target);
323 }
324}
325
326#[expect(clippy::cast_possible_truncation)]
330pub fn write_section_visibility(private_defs: &[DefinitionId], buf: &mut Vec<u8>) {
331 write_u32(buf, private_defs.len() as u32);
332 for id in private_defs {
333 write_def_id(buf, *id);
334 }
335}
336
337fn encode_global_var(v: &GlobalVarDef, buf: &mut Vec<u8>) {
340 write_def_id(buf, v.id);
341 write_u16(buf, v.name.0);
342 encode_value_type(v.value_type, buf);
343 encode_value(&v.default_value, buf);
344 write_u8(buf, u8::from(v.mutable));
345 write_u8(buf, u8::from(v.local));
346}
347
348fn encode_value_type(vt: ValueType, buf: &mut Vec<u8>) {
349 let tag = match vt {
350 ValueType::Int => VAL_INT,
351 ValueType::Float => VAL_FLOAT,
352 ValueType::Bool => VAL_BOOL,
353 ValueType::String => VAL_STRING,
354 ValueType::List => VAL_LIST,
355 ValueType::DivertTarget => VAL_DIVERT_TARGET,
356 ValueType::VariablePointer => VAL_VAR_POINTER,
357 ValueType::FragmentRef => VAL_FRAGMENT_REF,
359 ValueType::TempPointer | ValueType::Null => VAL_NULL,
360 ValueType::Array => VAL_ARRAY,
362 ValueType::Map => VAL_MAP,
363 ValueType::Record => VAL_RECORD,
365 ValueType::FnRef => VAL_FN_REF,
367 ValueType::Closure => VAL_CLOSURE,
368 ValueType::Handle => VAL_HANDLE,
370 ValueType::Projection => VAL_PROJECTION,
372 ValueType::Option => VAL_OPTION,
374 ValueType::Range => VAL_RANGE,
376 ValueType::Vec2 => VAL_VEC2,
378 ValueType::Vec3 => VAL_VEC3,
379 ValueType::Vec4 => VAL_VEC4,
380 ValueType::Quat => VAL_QUAT,
381 ValueType::Mat2 => VAL_MAT2,
382 ValueType::Mat3 => VAL_MAT3,
383 ValueType::Mat4 => VAL_MAT4,
384 ValueType::Weighted => VAL_WEIGHTED,
386 };
387 write_u8(buf, tag);
388}
389
390fn write_f32_lanes(buf: &mut Vec<u8>, lanes: &[f32]) {
396 for lane in lanes {
397 buf.extend_from_slice(&lane.to_le_bytes());
398 }
399}
400
401#[expect(clippy::cast_possible_truncation)]
402#[expect(
403 clippy::too_many_lines,
404 reason = "one match arm per value variant — the NS-A1 Option arm pushed this past 100"
405)]
406fn encode_value(v: &Value, buf: &mut Vec<u8>) {
407 match v {
408 Value::Int(n) => {
409 write_u8(buf, VAL_INT);
410 write_i32(buf, *n);
411 }
412 Value::Float(n) => {
413 write_u8(buf, VAL_FLOAT);
414 buf.extend_from_slice(&n.to_le_bytes());
415 }
416 Value::Bool(b) => {
417 write_u8(buf, VAL_BOOL);
418 write_u8(buf, u8::from(*b));
419 }
420 Value::String(s) => {
421 write_u8(buf, VAL_STRING);
422 write_str(buf, s);
423 }
424 Value::List(lv) => {
425 write_u8(buf, VAL_LIST);
426 write_u32(buf, lv.items.len() as u32);
427 for item in &lv.items {
428 write_def_id(buf, *item);
429 }
430 write_u32(buf, lv.origins.len() as u32);
431 for origin in &lv.origins {
432 write_def_id(buf, *origin);
433 }
434 }
435 Value::DivertTarget(id) => {
436 write_u8(buf, VAL_DIVERT_TARGET);
437 write_def_id(buf, *id);
438 }
439 Value::VariablePointer(id) => {
440 write_u8(buf, VAL_VAR_POINTER);
441 write_def_id(buf, *id);
442 }
443 Value::FragmentRef(idx) => {
444 write_u8(buf, VAL_FRAGMENT_REF);
445 write_u32(buf, *idx);
446 }
447 Value::TempPointer { .. } | Value::Null => {
449 write_u8(buf, VAL_NULL);
450 }
451 Value::Array(items) => {
455 write_u8(buf, VAL_ARRAY);
456 write_u32(buf, items.len() as u32);
457 for item in items.iter() {
458 encode_value(item, buf);
459 }
460 }
461 Value::Map(map) => {
462 write_u8(buf, VAL_MAP);
463 write_u32(buf, map.len() as u32);
464 for (key, val) in map.iter() {
466 encode_map_key(key, buf);
467 encode_value(val, buf);
468 }
469 }
470 Value::Record { shape, fields } => {
474 write_u8(buf, VAL_RECORD);
475 write_u32(buf, shape.0);
476 write_u32(buf, fields.len() as u32);
477 for field in fields.iter() {
478 encode_value(field, buf);
479 }
480 }
481 Value::FnRef(target) => {
486 write_u8(buf, VAL_FN_REF);
487 write_def_id(buf, *target);
488 }
489 Value::Closure(c) => {
490 write_u8(buf, VAL_CLOSURE);
491 write_def_id(buf, c.target);
492 write_u16(buf, c.env.len() as u16);
493 for entry in &c.env {
494 write_u16(buf, entry.name.0);
495 write_u8(buf, u8::from(entry.is_ref));
496 encode_value(&entry.payload, buf);
497 }
498 }
499 Value::Handle { kind, id } => {
505 write_u8(buf, VAL_HANDLE);
506 write_u16(buf, kind.0);
507 write_u64(buf, *id);
508 }
509 Value::Projection(p) => {
514 write_u8(buf, VAL_PROJECTION);
515 write_def_id(buf, p.cell);
516 write_u8(buf, p.segments.len() as u8);
517 for seg in &p.segments {
518 encode_proj_segment(seg, buf);
519 }
520 }
521 Value::OptionVal(inner) => {
529 write_u8(buf, VAL_OPTION);
530 match inner {
531 None => write_u8(buf, 0),
532 Some(v) => {
533 write_u8(buf, 1);
534 encode_value(v, buf);
535 }
536 }
537 }
538 Value::Range {
543 start,
544 end,
545 inclusive,
546 } => {
547 write_u8(buf, VAL_RANGE);
548 write_i32(buf, *start);
549 write_i32(buf, *end);
550 write_u8(buf, u8::from(*inclusive));
551 }
552 Value::Vec2(v) => {
561 write_u8(buf, VAL_VEC2);
562 write_f32_lanes(buf, &v.to_array());
563 }
564 Value::Vec3(v) => {
565 write_u8(buf, VAL_VEC3);
566 write_f32_lanes(buf, &v.to_array());
567 }
568 Value::Vec4(v) => {
569 write_u8(buf, VAL_VEC4);
570 write_f32_lanes(buf, &v.to_array());
571 }
572 Value::Quat(q) => {
573 write_u8(buf, VAL_QUAT);
574 write_f32_lanes(buf, &q.to_array());
575 }
576 Value::Mat2(m) => {
577 write_u8(buf, VAL_MAT2);
578 write_f32_lanes(buf, &m.to_cols_array());
579 }
580 Value::Mat3(m) => {
581 write_u8(buf, VAL_MAT3);
582 write_f32_lanes(buf, &m.to_cols_array());
583 }
584 Value::Mat4(m) => {
585 write_u8(buf, VAL_MAT4);
586 write_f32_lanes(buf, &m.to_cols_array());
587 }
588 Value::Weighted(w) => {
596 write_u8(buf, VAL_WEIGHTED);
597 write_u32(buf, w.entries.len() as u32);
598 for (weight, value) in &w.entries {
599 write_i32(buf, *weight);
600 encode_value(value, buf);
601 }
602 }
603 }
604}
605
606fn encode_proj_segment(seg: &ProjSegment, buf: &mut Vec<u8>) {
609 match seg {
610 ProjSegment::Index(n) => {
611 write_u8(buf, PROJ_SEG_INDEX);
612 write_i32(buf, *n);
613 }
614 ProjSegment::Key(v) => {
615 write_u8(buf, PROJ_SEG_KEY);
616 encode_value(v, buf);
617 }
618 }
619}
620
621fn encode_map_key(key: &MapKey, buf: &mut Vec<u8>) {
625 match key {
626 MapKey::Int(n) => {
627 write_u8(buf, VAL_INT);
628 write_i32(buf, *n);
629 }
630 MapKey::Str(s) => {
631 write_u8(buf, VAL_STRING);
632 write_str(buf, s);
633 }
634 MapKey::Bool(b) => {
635 write_u8(buf, VAL_BOOL);
636 write_u8(buf, u8::from(*b));
637 }
638 }
639}
640
641#[expect(clippy::cast_possible_truncation)]
642fn encode_list_def(ld: &ListDef, buf: &mut Vec<u8>) {
643 write_def_id(buf, ld.id);
644 write_u16(buf, ld.name.0);
645 write_u32(buf, ld.items.len() as u32);
646 for (name_id, ordinal) in &ld.items {
647 write_u16(buf, name_id.0);
648 write_i32(buf, *ordinal);
649 }
650}
651
652fn encode_list_item(li: &ListItemDef, buf: &mut Vec<u8>) {
653 write_def_id(buf, li.id);
654 write_def_id(buf, li.origin);
655 write_i32(buf, li.ordinal);
656 write_u16(buf, li.name.0);
657}
658
659#[expect(clippy::cast_possible_truncation)]
661pub fn write_section_list_literals(list_literals: &[ListValue], buf: &mut Vec<u8>) {
662 write_u32(buf, list_literals.len() as u32);
663 for lv in list_literals {
664 write_u32(buf, lv.items.len() as u32);
665 for item in &lv.items {
666 write_def_id(buf, *item);
667 }
668 write_u32(buf, lv.origins.len() as u32);
669 for origin in &lv.origins {
670 write_def_id(buf, *origin);
671 }
672 }
673}
674
675#[expect(clippy::cast_possible_truncation)]
681pub fn write_section_literal_pool(literal_pool: &[Value], buf: &mut Vec<u8>) {
682 write_u32(buf, literal_pool.len() as u32);
683 for v in literal_pool {
684 encode_value(v, buf);
685 }
686}
687
688#[expect(clippy::cast_possible_truncation)]
693pub fn write_section_struct_shapes(struct_shapes: &[StructShapeDef], buf: &mut Vec<u8>) {
694 write_u32(buf, struct_shapes.len() as u32);
695 for shape in struct_shapes {
696 write_u32(buf, shape.id.0);
697 write_u16(buf, shape.name.0);
698 write_u16(buf, shape.fields.len() as u16);
699 for field in &shape.fields {
700 write_u16(buf, field.0);
701 }
702 }
703}
704
705pub(crate) const ALIAS_TABLE_SECTION_VERSION: u8 = 1;
709
710#[expect(clippy::cast_possible_truncation)]
715pub fn write_section_alias_table(entries: &[AliasEntry], buf: &mut Vec<u8>) {
716 write_u8(buf, ALIAS_TABLE_SECTION_VERSION);
717 write_u32(buf, entries.len() as u32);
718 for entry in entries {
719 write_def_id(buf, entry.old);
720 write_def_id(buf, entry.new);
721 }
722}
723
724pub(crate) const FRAME_SHAPES_SECTION_VERSION: u8 = 1;
729
730#[expect(clippy::cast_possible_truncation)]
737pub fn write_section_frame_shapes(shapes: &[FrameShapeDef], buf: &mut Vec<u8>) {
738 write_u8(buf, FRAME_SHAPES_SECTION_VERSION);
739 write_u32(buf, shapes.len() as u32);
740 for shape in shapes {
741 write_def_id(buf, shape.site);
742 write_u32(buf, shape.slots.len() as u32);
743 for slot in &shape.slots {
744 write_u16(buf, slot.0);
745 }
746 }
747}
748
749pub(crate) const DEBUG_INFO_SECTION_VERSION: u8 = 2;
754
755pub(crate) const LOCAL_FLAG_HAS_RANGE: u8 = 0b01;
761pub(crate) const LOCAL_FLAG_SYNTHETIC: u8 = 0b10;
762pub(crate) const LOCAL_FLAGS_KNOWN: u8 = LOCAL_FLAG_HAS_RANGE | LOCAL_FLAG_SYNTHETIC;
763
764pub(crate) const LINE_VARIANT_GROUPS_SECTION_VERSION: u8 = 1;
767
768#[expect(
788 clippy::cast_possible_truncation,
789 reason = "counts capped at recognition"
790)]
791pub fn write_section_line_variant_groups(groups: &[LineVariantGroup], buf: &mut Vec<u8>) {
792 write_u8(buf, LINE_VARIANT_GROUPS_SECTION_VERSION);
793 write_u32(buf, groups.len() as u32);
794 for group in groups {
795 write_def_id(buf, group.scope_id);
796 write_u32(buf, group.base);
797 write_u8(buf, group.dims.len() as u8);
798 for dim in &group.dims {
799 write_u16(buf, *dim);
800 }
801 }
802}
803
804#[expect(clippy::cast_possible_truncation)]
805pub fn write_section_debug_info(section: &DebugInfoSection, buf: &mut Vec<u8>) {
806 write_u8(buf, DEBUG_INFO_SECTION_VERSION);
807
808 write_u32(buf, section.files.len() as u32);
809 for file in §ion.files {
810 write_u8(buf, file.surface as u8);
811 write_str(buf, &file.path);
812 write_u64(buf, file.source_hash);
815 write_varint(buf, file.line_starts.len() as u64);
816 let mut prev: u32 = 0;
822 for start in &file.line_starts {
823 write_varint(buf, u64::from(start.saturating_sub(prev)));
824 prev = *start;
825 }
826 }
827
828 write_u32(buf, section.containers.len() as u32);
829 for table in §ion.containers {
830 write_varint(buf, table.entries.len() as u64);
831 let mut prev_offset: u32 = 0;
832 for entry in &table.entries {
833 write_varint(
840 buf,
841 u64::from(entry.bytecode_offset.saturating_sub(prev_offset)),
842 );
843 prev_offset = entry.bytecode_offset;
844 write_varint(buf, u64::from(entry.file_idx));
845 write_varint(buf, u64::from(entry.range_start));
846 write_varint(buf, u64::from(entry.range_len));
847 write_u32(buf, entry.kind_token);
848 write_u8(buf, entry.flags);
849 }
850
851 write_varint(buf, table.locals.len() as u64);
852 for local in &table.locals {
853 write_u16(buf, local.slot);
854 write_str(buf, &local.name);
855 let mut flags = 0u8;
860 if local.declaring_range.is_some() {
861 flags |= LOCAL_FLAG_HAS_RANGE;
862 }
863 if local.synthetic {
864 flags |= LOCAL_FLAG_SYNTHETIC;
865 }
866 write_u8(buf, flags);
867 if let Some((file_idx, range_start, range_len)) = local.declaring_range {
868 write_varint(buf, u64::from(file_idx));
869 write_varint(buf, u64::from(range_start));
870 write_varint(buf, u64::from(range_len));
871 }
872 }
873 }
874}
875
876pub(crate) const EFFECT_ROWS_SECTION_VERSION: u8 = 3;
889
890#[expect(clippy::cast_possible_truncation)]
896pub fn write_section_effect_rows(rows: &[EffectRowEntry], buf: &mut Vec<u8>) {
897 write_u8(buf, EFFECT_ROWS_SECTION_VERSION);
898 write_u32(buf, rows.len() as u32);
899 for row in rows {
900 write_def_id(buf, row.def);
901 write_u8(buf, u8::from(row.is_entry));
905 encode_direct_effects(&row.direct, buf);
906 write_u32(buf, row.dispatches.len() as u32);
909 for d in &row.dispatches {
910 write_def_id(buf, d.cell);
911 write_u8(buf, u8::from(d.narrowable));
912 encode_direct_effects(&d.fallback, buf);
913 }
914 }
915}
916
917#[expect(clippy::cast_possible_truncation)]
919fn encode_direct_effects(direct: &DirectEffects, buf: &mut Vec<u8>) {
920 write_u32(buf, direct.reads.len() as u32);
921 for id in &direct.reads {
922 write_def_id(buf, *id);
923 }
924 write_u32(buf, direct.writes.len() as u32);
925 for id in &direct.writes {
926 write_def_id(buf, *id);
927 }
928 write_u32(buf, direct.calls.len() as u32);
929 for atom in &direct.calls {
930 encode_call_atom(atom, buf);
931 }
932 write_u8(buf, u8::from(direct.opaque));
933 let mut dims = 0u8;
935 if direct.emits {
936 dims |= super::EFFECT_DIM_EMITS;
937 }
938 if direct.tags {
939 dims |= super::EFFECT_DIM_TAGS;
940 }
941 if direct.faults {
942 dims |= super::EFFECT_DIM_FAULTS;
943 }
944 write_u8(buf, dims);
945}
946
947fn encode_call_atom(atom: &CallAtom, buf: &mut Vec<u8>) {
952 write_u16(buf, atom.name.0);
953 let cap_tag = match atom.capability {
954 CapabilityParam::Any => CAP_PARAM_ANY,
955 };
956 write_u8(buf, cap_tag);
957 write_u8(buf, atom.handle_param.unwrap_or(HANDLE_PARAM_NONE));
960}
961
962fn encode_external(ext: &ExternalFnDef, buf: &mut Vec<u8>) {
963 write_def_id(buf, ext.id);
964 write_u16(buf, ext.name.0);
965 write_u8(buf, ext.arg_count);
966 match ext.fallback {
967 Some(fb) => {
968 write_u8(buf, 1);
969 write_def_id(buf, fb);
970 }
971 None => {
972 write_u8(buf, 0);
973 }
974 }
975}
976
977#[expect(clippy::cast_possible_truncation)]
978fn encode_container(c: &ContainerDef, buf: &mut Vec<u8>) {
979 write_def_id(buf, c.id);
980 write_def_id(buf, c.scope_id);
981 match c.name {
982 Some(name_id) => {
983 write_u8(buf, 1);
984 write_u16(buf, name_id.0);
985 }
986 None => {
987 write_u8(buf, 0);
988 }
989 }
990 write_u8(buf, c.counting_flags.bits());
991 write_i32(buf, c.path_hash);
992 write_u8(buf, c.param_count);
993 write_u8(buf, u8::from(c.local));
994 write_u16(buf, c.params.len() as u16);
997 for p in &c.params {
998 write_u16(buf, p.name.0);
999 write_u8(buf, u8::from(p.is_ref));
1000 write_u16(buf, p.slot);
1001 }
1002 write_u32(buf, c.bytecode.len() as u32);
1003 buf.extend_from_slice(&c.bytecode);
1004}
1005
1006#[expect(clippy::cast_possible_truncation)]
1008pub fn write_section_line_tables(line_tables: &[ScopeLineTable], buf: &mut Vec<u8>) {
1009 write_u32(buf, line_tables.len() as u32);
1010 for lt in line_tables {
1011 encode_scope_line_table(lt, buf);
1012 }
1013}
1014
1015#[expect(clippy::cast_possible_truncation)]
1016fn encode_scope_line_table(lt: &ScopeLineTable, buf: &mut Vec<u8>) {
1017 write_def_id(buf, lt.scope_id);
1018 write_u32(buf, lt.lines.len() as u32);
1019 for entry in <.lines {
1020 encode_line_entry(entry, buf);
1021 }
1022}
1023
1024fn encode_line_entry(entry: &LineEntry, buf: &mut Vec<u8>) {
1025 encode_line_content(&entry.content, buf);
1026 write_u64(buf, entry.source_hash);
1027 match &entry.audio_ref {
1028 Some(audio) => {
1029 write_u8(buf, 1);
1030 write_str(buf, audio);
1031 }
1032 None => {
1033 write_u8(buf, 0);
1034 }
1035 }
1036
1037 #[expect(clippy::cast_possible_truncation)]
1039 write_u8(buf, entry.slot_info.len() as u8);
1040 for slot in &entry.slot_info {
1041 write_u8(buf, slot.index);
1042 write_str(buf, &slot.name);
1043 }
1044
1045 match &entry.source_location {
1047 Some(loc) => {
1048 write_u8(buf, 1);
1049 write_str(buf, &loc.file);
1050 write_u32(buf, loc.range_start);
1051 write_u32(buf, loc.range_end);
1052 }
1053 None => {
1054 write_u8(buf, 0);
1055 }
1056 }
1057}
1058
1059#[expect(clippy::cast_possible_truncation)]
1060pub(crate) fn encode_line_content(content: &LineContent, buf: &mut Vec<u8>) {
1061 match content {
1062 LineContent::Plain(s) => {
1063 write_u8(buf, LINE_PLAIN);
1064 write_str(buf, s);
1065 }
1066 LineContent::Template(parts) => {
1067 write_u8(buf, LINE_TEMPLATE);
1068 write_u32(buf, parts.len() as u32);
1069 for part in parts {
1070 encode_line_part(part, buf);
1071 }
1072 }
1073 }
1074}
1075
1076#[expect(clippy::cast_possible_truncation)]
1077fn encode_line_part(part: &LinePart, buf: &mut Vec<u8>) {
1078 match part {
1079 LinePart::Literal(s) => {
1080 write_u8(buf, PART_LITERAL);
1081 write_str(buf, s);
1082 }
1083 LinePart::Slot(idx) => {
1084 write_u8(buf, PART_SLOT);
1085 write_u8(buf, *idx);
1086 }
1087 LinePart::Select {
1088 slot,
1089 variants,
1090 default,
1091 } => {
1092 write_u8(buf, PART_SELECT);
1093 write_u8(buf, *slot);
1094 write_u32(buf, variants.len() as u32);
1095 for (key, text) in variants {
1096 encode_select_key(key, buf);
1097 write_str(buf, text);
1098 }
1099 write_str(buf, default);
1100 }
1101 LinePart::Span {
1102 name,
1103 attrs,
1104 children,
1105 } => {
1106 write_u8(buf, PART_SPAN);
1107 write_str(buf, name);
1108 write_u32(buf, attrs.len() as u32);
1109 for (k, v) in attrs {
1110 write_str(buf, k);
1111 write_str(buf, v);
1112 }
1113 write_u32(buf, children.len() as u32);
1114 for child in children {
1115 encode_line_part(child, buf);
1116 }
1117 }
1118 }
1119}
1120
1121fn encode_select_key(key: &SelectKey, buf: &mut Vec<u8>) {
1122 match key {
1123 SelectKey::Cardinal(cat) => {
1124 write_u8(buf, KEY_CARDINAL);
1125 encode_plural_category(*cat, buf);
1126 }
1127 SelectKey::Ordinal(cat) => {
1128 write_u8(buf, KEY_ORDINAL);
1129 encode_plural_category(*cat, buf);
1130 }
1131 SelectKey::Exact(n) => {
1132 write_u8(buf, KEY_EXACT);
1133 write_i32(buf, *n);
1134 }
1135 SelectKey::Keyword(k) => {
1136 write_u8(buf, KEY_KEYWORD);
1137 write_str(buf, k);
1138 }
1139 }
1140}
1141
1142fn encode_plural_category(cat: PluralCategory, buf: &mut Vec<u8>) {
1143 let tag = match cat {
1144 PluralCategory::Zero => CAT_ZERO,
1145 PluralCategory::One => CAT_ONE,
1146 PluralCategory::Two => CAT_TWO,
1147 PluralCategory::Few => CAT_FEW,
1148 PluralCategory::Many => CAT_MANY,
1149 PluralCategory::Other => CAT_OTHER,
1150 };
1151 write_u8(buf, tag);
1152}