1use alloc::string::String;
4use alloc::vec::Vec;
5
6use crate::codec::{
7 crc32, read_def_id, read_i32, read_str, read_u8, read_u16, read_u32, read_u64, read_varint,
8};
9use crate::counting::CountingFlags;
10use crate::definition::{
11 AddressDef, AddressPath, AliasEntry, CallAtom, CapabilityParam, ContainerDef,
12 DebugContainerTable, DebugEntry, DebugFileEntry, DebugInfoSection, DebugLocalEntry,
13 DirectEffects, DispatchEntry, EffectRowEntry, ExternalFnDef, FileSurface, FrameShapeDef,
14 GlobalVarDef, LineEntry, ListDef, ListItemDef, ParamMeta, ScopeLineTable, SlotInfo,
15 SourceLocation, StructShapeDef,
16};
17use crate::id::{DefinitionId, NameId};
18use crate::line::{LineContent, LinePart, PluralCategory, SelectKey};
19use crate::opcode::DecodeError;
20use crate::story::StoryData;
21use crate::value::{
22 ClosureEnvEntry, ListValue, MAX_DECODE_DEPTH, MapKey, OrderedMap, ShapeId, Value, ValueType,
23};
24
25use super::write::{
26 ALIAS_TABLE_SECTION_VERSION, DEBUG_INFO_SECTION_VERSION, EFFECT_ROWS_SECTION_VERSION,
27 FRAME_SHAPES_SECTION_VERSION, LINE_VARIANT_GROUPS_SECTION_VERSION, LOCAL_FLAG_HAS_RANGE,
28 LOCAL_FLAG_SYNTHETIC, LOCAL_FLAGS_KNOWN,
29};
30use super::{
31 CAP_PARAM_ANY, CAT_FEW, CAT_MANY, CAT_ONE, CAT_OTHER, CAT_TWO, CAT_ZERO, HANDLE_PARAM_NONE,
32 HEADER_PREAMBLE, InkbIndex, KEY_CARDINAL, KEY_EXACT, KEY_KEYWORD, KEY_ORDINAL, LINE_PLAIN,
33 LINE_TEMPLATE, MAGIC, PART_LITERAL, PART_SELECT, PART_SLOT, PART_SPAN, PROJ_SEG_INDEX,
34 PROJ_SEG_KEY, SECTION_ENTRY_SIZE, SectionEntry, SectionKind, VAL_ARRAY, VAL_BOOL, VAL_CLOSURE,
35 VAL_DIVERT_TARGET, VAL_FLOAT, VAL_FN_REF, VAL_FRAGMENT_REF, VAL_HANDLE, VAL_INT, VAL_LIST,
36 VAL_MAP, VAL_MAT2, VAL_MAT3, VAL_MAT4, VAL_NULL, VAL_OPTION, VAL_PROJECTION, VAL_QUAT,
37 VAL_RANGE, VAL_RECORD, VAL_STRING, VAL_VAR_POINTER, VAL_VEC2, VAL_VEC3, VAL_VEC4, VAL_WEIGHTED,
38 VERSION, safe_capacity,
39};
40
41pub fn read_inkb(buf: &[u8]) -> Result<StoryData, DecodeError> {
45 let index = read_inkb_index(buf)?;
46
47 let header_size = index.header_size();
49 let computed = crc32(&buf[header_size..]);
50 if computed != index.checksum {
51 return Err(DecodeError::ChecksumMismatch {
52 expected: index.checksum,
53 actual: computed,
54 });
55 }
56
57 let name_table = read_section_name_table(buf, &index)?;
58 let variables = read_section_variables(buf, &index)?;
59 let list_defs = read_section_list_defs(buf, &index)?;
60 let list_items = read_section_list_items(buf, &index)?;
61 let externals = read_section_externals(buf, &index)?;
62 let containers = read_section_containers(buf, &index)?;
63 let line_tables = read_section_line_tables(buf, &index)?;
64 let addresses = read_section_addresses(buf, &index)?;
65 let list_literals = read_section_list_literals(buf, &index)?;
66 let address_paths = read_section_address_paths(buf, &index)?;
67 let literal_pool = read_section_literal_pool(buf, &index)?;
68 let struct_shapes = read_section_struct_shapes(buf, &index)?;
69 let private_defs = read_section_visibility(buf, &index)?;
70 let alias_table = read_section_alias_table(buf, &index)?;
71 let effect_rows = read_section_effect_rows(buf, &index)?;
72 let frame_shapes = read_section_frame_shapes(buf, &index)?;
73 let debug_info = read_section_debug_info(buf, &index)?;
74 let line_variant_groups = read_section_line_variant_groups(buf, &index)?;
75
76 Ok(StoryData {
77 containers,
78 line_tables,
79 variables,
80 list_defs,
81 list_items,
82 externals,
83 addresses,
84 address_paths,
85 name_table,
86 list_literals,
87 literal_pool,
88 struct_shapes,
89 private_defs,
90 alias_table,
91 effect_rows,
92 frame_shapes,
93 debug_info,
94 line_variant_groups,
95 source_checksum: index.checksum,
96 })
97}
98
99pub fn read_inkb_index(buf: &[u8]) -> Result<InkbIndex, DecodeError> {
103 if buf.len() < HEADER_PREAMBLE {
104 return Err(DecodeError::UnexpectedEof);
105 }
106
107 let magic: [u8; 4] = [buf[0], buf[1], buf[2], buf[3]];
108 if &magic != MAGIC {
109 return Err(DecodeError::BadMagic(magic));
110 }
111
112 let mut off = 4;
113 let version = read_u16(buf, &mut off)?;
114 if version != VERSION {
115 return Err(DecodeError::UnsupportedVersion(version));
116 }
117
118 let section_count = read_u8(buf, &mut off)?;
119 let _reserved = read_u8(buf, &mut off)?;
120 let file_size = read_u32(buf, &mut off)?;
121 let checksum = read_u32(buf, &mut off)?;
122
123 if file_size as usize != buf.len() {
125 return Err(DecodeError::FileSizeMismatch {
126 expected: file_size,
127 actual: buf.len(),
128 });
129 }
130
131 let total_header = HEADER_PREAMBLE + section_count as usize * SECTION_ENTRY_SIZE;
132 if buf.len() < total_header {
133 return Err(DecodeError::UnexpectedEof);
134 }
135
136 let mut sections = Vec::with_capacity(section_count as usize);
137 for _ in 0..section_count {
138 let kind_tag = read_u8(buf, &mut off)?;
139 let kind = SectionKind::from_u8(kind_tag)?;
140 let _reserved0 = read_u8(buf, &mut off)?;
141 let _reserved1 = read_u8(buf, &mut off)?;
142 let _reserved2 = read_u8(buf, &mut off)?;
143 let offset = read_u32(buf, &mut off)?;
144 sections.push(SectionEntry { kind, offset });
145 }
146
147 #[expect(clippy::cast_possible_truncation)]
153 let header_size = total_header as u32;
154 let mut prev_offset = header_size;
155 for entry in §ions {
156 if entry.offset < header_size || entry.offset > file_size || entry.offset < prev_offset {
157 return Err(DecodeError::InvalidSectionOffset {
158 kind: entry.kind as u8,
159 offset: entry.offset,
160 });
161 }
162 prev_offset = entry.offset;
163 }
164
165 Ok(InkbIndex {
166 version,
167 file_size,
168 checksum,
169 sections,
170 })
171}
172
173pub fn read_section_name_table(buf: &[u8], index: &InkbIndex) -> Result<Vec<String>, DecodeError> {
177 let range =
178 index
179 .section_range(SectionKind::NameTable)
180 .ok_or(DecodeError::MissingSectionKind(
181 SectionKind::NameTable as u8,
182 ))?;
183 let mut off = range.start;
184 let count = read_u32(buf, &mut off)? as usize;
185 let mut names = Vec::with_capacity(safe_capacity(count, buf.len(), off, 4));
186 for _ in 0..count {
187 names.push(read_str(buf, &mut off)?);
188 }
189 Ok(names)
190}
191
192pub fn read_section_variables(
194 buf: &[u8],
195 index: &InkbIndex,
196) -> Result<Vec<GlobalVarDef>, DecodeError> {
197 let range =
198 index
199 .section_range(SectionKind::Variables)
200 .ok_or(DecodeError::MissingSectionKind(
201 SectionKind::Variables as u8,
202 ))?;
203 let mut off = range.start;
204 let count = read_u32(buf, &mut off)? as usize;
205 let mut vars = Vec::with_capacity(safe_capacity(count, buf.len(), off, 12));
206 for _ in 0..count {
207 vars.push(decode_global_var(buf, &mut off)?);
208 }
209 Ok(vars)
210}
211
212pub fn read_section_list_defs(buf: &[u8], index: &InkbIndex) -> Result<Vec<ListDef>, DecodeError> {
214 let range = index
215 .section_range(SectionKind::ListDefs)
216 .ok_or(DecodeError::MissingSectionKind(SectionKind::ListDefs as u8))?;
217 let mut off = range.start;
218 let count = read_u32(buf, &mut off)? as usize;
219 let mut defs = Vec::with_capacity(safe_capacity(count, buf.len(), off, 14));
220 for _ in 0..count {
221 defs.push(decode_list_def(buf, &mut off)?);
222 }
223 Ok(defs)
224}
225
226pub fn read_section_list_items(
228 buf: &[u8],
229 index: &InkbIndex,
230) -> Result<Vec<ListItemDef>, DecodeError> {
231 let range =
232 index
233 .section_range(SectionKind::ListItems)
234 .ok_or(DecodeError::MissingSectionKind(
235 SectionKind::ListItems as u8,
236 ))?;
237 let mut off = range.start;
238 let count = read_u32(buf, &mut off)? as usize;
239 let mut items = Vec::with_capacity(safe_capacity(count, buf.len(), off, 20));
240 for _ in 0..count {
241 items.push(decode_list_item(buf, &mut off)?);
242 }
243 Ok(items)
244}
245
246pub fn read_section_externals(
248 buf: &[u8],
249 index: &InkbIndex,
250) -> Result<Vec<ExternalFnDef>, DecodeError> {
251 let range =
252 index
253 .section_range(SectionKind::Externals)
254 .ok_or(DecodeError::MissingSectionKind(
255 SectionKind::Externals as u8,
256 ))?;
257 let mut off = range.start;
258 let count = read_u32(buf, &mut off)? as usize;
259 let mut exts = Vec::with_capacity(safe_capacity(count, buf.len(), off, 12));
260 for _ in 0..count {
261 exts.push(decode_external(buf, &mut off)?);
262 }
263 Ok(exts)
264}
265
266pub fn read_section_containers(
268 buf: &[u8],
269 index: &InkbIndex,
270) -> Result<Vec<ContainerDef>, DecodeError> {
271 let range =
272 index
273 .section_range(SectionKind::Containers)
274 .ok_or(DecodeError::MissingSectionKind(
275 SectionKind::Containers as u8,
276 ))?;
277 let mut off = range.start;
278 let count = read_u32(buf, &mut off)? as usize;
279 let mut containers = Vec::with_capacity(safe_capacity(count, buf.len(), off, 21));
280 for _ in 0..count {
281 containers.push(decode_container(buf, &mut off)?);
282 }
283 Ok(containers)
284}
285
286pub fn read_section_addresses(
288 buf: &[u8],
289 index: &InkbIndex,
290) -> Result<Vec<AddressDef>, DecodeError> {
291 let Some(range) = index.section_range(SectionKind::Labels) else {
292 return Ok(Vec::new());
294 };
295 let mut off = range.start;
296 let count = read_u32(buf, &mut off)? as usize;
297 let mut addresses = Vec::with_capacity(safe_capacity(count, buf.len(), off, 20));
299 for _ in 0..count {
300 let id = read_def_id(buf, &mut off)?;
301 let container_id = read_def_id(buf, &mut off)?;
302 let byte_offset = read_u32(buf, &mut off)?;
303 addresses.push(AddressDef {
304 id,
305 container_id,
306 byte_offset,
307 });
308 }
309 Ok(addresses)
310}
311
312pub fn read_section_address_paths(
314 buf: &[u8],
315 index: &InkbIndex,
316) -> Result<Vec<AddressPath>, DecodeError> {
317 let Some(range) = index.section_range(SectionKind::AddressPaths) else {
318 return Ok(Vec::new());
321 };
322 let mut off = range.start;
323 let count = read_u32(buf, &mut off)? as usize;
324 let mut paths = Vec::with_capacity(safe_capacity(count, buf.len(), off, 10));
326 for _ in 0..count {
327 let path = NameId(read_u16(buf, &mut off)?);
328 let target = read_def_id(buf, &mut off)?;
329 paths.push(AddressPath { path, target });
330 }
331 Ok(paths)
332}
333
334fn decode_global_var(buf: &[u8], off: &mut usize) -> Result<GlobalVarDef, DecodeError> {
337 let id = read_def_id(buf, off)?;
338 let name = NameId(read_u16(buf, off)?);
339 let value_type = decode_value_type(buf, off)?;
340 let default_value = decode_value(buf, off, 0)?;
341 let mutable = read_u8(buf, off)? != 0;
342 let local = read_u8(buf, off)? != 0;
343 Ok(GlobalVarDef {
344 id,
345 name,
346 value_type,
347 default_value,
348 mutable,
349 local,
350 })
351}
352
353fn decode_value_type(buf: &[u8], off: &mut usize) -> Result<ValueType, DecodeError> {
354 let tag = read_u8(buf, off)?;
355 match tag {
356 VAL_INT => Ok(ValueType::Int),
357 VAL_FLOAT => Ok(ValueType::Float),
358 VAL_BOOL => Ok(ValueType::Bool),
359 VAL_STRING => Ok(ValueType::String),
360 VAL_LIST => Ok(ValueType::List),
361 VAL_DIVERT_TARGET => Ok(ValueType::DivertTarget),
362 VAL_VAR_POINTER => Ok(ValueType::VariablePointer),
363 VAL_FRAGMENT_REF => Ok(ValueType::FragmentRef),
364 VAL_NULL => Ok(ValueType::Null),
365 VAL_ARRAY => Ok(ValueType::Array),
366 VAL_MAP => Ok(ValueType::Map),
367 VAL_RECORD => Ok(ValueType::Record),
368 VAL_FN_REF => Ok(ValueType::FnRef),
369 VAL_CLOSURE => Ok(ValueType::Closure),
370 VAL_HANDLE => Ok(ValueType::Handle),
371 VAL_PROJECTION => Ok(ValueType::Projection),
372 VAL_OPTION => Ok(ValueType::Option),
373 VAL_RANGE => Ok(ValueType::Range),
374 VAL_VEC2 => Ok(ValueType::Vec2),
375 VAL_VEC3 => Ok(ValueType::Vec3),
376 VAL_VEC4 => Ok(ValueType::Vec4),
377 VAL_QUAT => Ok(ValueType::Quat),
378 VAL_MAT2 => Ok(ValueType::Mat2),
379 VAL_MAT3 => Ok(ValueType::Mat3),
380 VAL_MAT4 => Ok(ValueType::Mat4),
381 VAL_WEIGHTED => Ok(ValueType::Weighted),
382 _ => Err(DecodeError::InvalidValueType(tag)),
383 }
384}
385
386fn read_f32_lanes<const N: usize>(buf: &[u8], off: &mut usize) -> Result<[f32; N], DecodeError> {
392 if *off + 4 * N > buf.len() {
393 return Err(DecodeError::UnexpectedEof);
394 }
395 let mut lanes = [0.0f32; N];
396 for lane in &mut lanes {
397 *lane = f32::from_le_bytes([buf[*off], buf[*off + 1], buf[*off + 2], buf[*off + 3]]);
398 *off += 4;
399 }
400 Ok(lanes)
401}
402
403#[expect(
404 clippy::too_many_lines,
405 reason = "one match arm per value tag — the NS-A1 VAL_OPTION arm pushed this past 100"
406)]
407fn decode_value(buf: &[u8], off: &mut usize, depth: usize) -> Result<Value, DecodeError> {
408 if depth > MAX_DECODE_DEPTH {
409 return Err(DecodeError::MaxDepthExceeded(MAX_DECODE_DEPTH));
410 }
411 let tag = read_u8(buf, off)?;
412 match tag {
413 VAL_INT => Ok(Value::Int(read_i32(buf, off)?)),
414 VAL_FLOAT => {
415 if *off + 4 > buf.len() {
416 return Err(DecodeError::UnexpectedEof);
417 }
418 let v = f32::from_le_bytes([buf[*off], buf[*off + 1], buf[*off + 2], buf[*off + 3]]);
419 *off += 4;
420 Ok(Value::Float(v))
421 }
422 VAL_BOOL => Ok(Value::Bool(read_u8(buf, off)? != 0)),
423 VAL_STRING => Ok(Value::String(read_str(buf, off)?.into())),
424 VAL_LIST => {
425 let item_count = read_u32(buf, off)? as usize;
426 let mut items = Vec::with_capacity(safe_capacity(item_count, buf.len(), *off, 8));
427 for _ in 0..item_count {
428 items.push(read_def_id(buf, off)?);
429 }
430 let origin_count = read_u32(buf, off)? as usize;
431 let mut origins = Vec::with_capacity(safe_capacity(origin_count, buf.len(), *off, 8));
432 for _ in 0..origin_count {
433 origins.push(read_def_id(buf, off)?);
434 }
435 Ok(Value::List(ListValue { items, origins }.into()))
436 }
437 VAL_DIVERT_TARGET => Ok(Value::DivertTarget(read_def_id(buf, off)?)),
438 VAL_VAR_POINTER => Ok(Value::VariablePointer(read_def_id(buf, off)?)),
439 VAL_FRAGMENT_REF => Ok(Value::FragmentRef(read_u32(buf, off)?)),
440 VAL_NULL => Ok(Value::Null),
441 VAL_ARRAY => {
442 let len = read_u32(buf, off)? as usize;
443 let mut items = Vec::with_capacity(safe_capacity(len, buf.len(), *off, 1));
446 for _ in 0..len {
447 items.push(decode_value(buf, off, depth + 1)?);
448 }
449 Ok(Value::array(items))
450 }
451 VAL_MAP => {
452 let len = read_u32(buf, off)? as usize;
453 let mut map = OrderedMap::with_capacity(safe_capacity(len, buf.len(), *off, 2));
454 for _ in 0..len {
455 let key = decode_map_key(buf, off)?;
456 let val = decode_value(buf, off, depth + 1)?;
457 if map.contains_key(&key) {
461 return Err(DecodeError::DuplicateMapKey);
462 }
463 map.insert(key, val);
464 }
465 Ok(Value::map(map))
466 }
467 VAL_RECORD => {
468 let shape = ShapeId(read_u32(buf, off)?);
469 let len = read_u32(buf, off)? as usize;
470 let mut fields = Vec::with_capacity(safe_capacity(len, buf.len(), *off, 1));
471 for _ in 0..len {
472 fields.push(decode_value(buf, off, depth + 1)?);
473 }
474 Ok(Value::record(shape, fields))
475 }
476 VAL_FN_REF => Ok(Value::FnRef(read_def_id(buf, off)?)),
478 VAL_CLOSURE => {
479 let target = read_def_id(buf, off)?;
480 let count = read_u16(buf, off)? as usize;
481 let mut env = Vec::with_capacity(safe_capacity(count, buf.len(), *off, 4));
482 for _ in 0..count {
483 let name = NameId(read_u16(buf, off)?);
484 let is_ref = read_u8(buf, off)? != 0;
485 let payload = decode_value(buf, off, depth + 1)?;
486 env.push(ClosureEnvEntry {
487 name,
488 is_ref,
489 payload,
490 });
491 }
492 Ok(Value::closure(target, env))
493 }
494 VAL_HANDLE => {
496 let kind = NameId(read_u16(buf, off)?);
497 let id = read_u64(buf, off)?;
498 Ok(Value::handle(kind, id))
499 }
500 VAL_PROJECTION => {
504 let cell = read_def_id(buf, off)?;
505 let count = read_u8(buf, off)? as usize;
506 let mut segments = Vec::with_capacity(safe_capacity(count, buf.len(), *off, 1));
507 for _ in 0..count {
508 segments.push(decode_proj_segment(buf, off, depth + 1)?);
509 }
510 Ok(Value::projection(cell, segments))
511 }
512 VAL_OPTION => match read_u8(buf, off)? {
518 0 => Ok(Value::none()),
519 1 => Ok(Value::some(decode_value(buf, off, depth + 1)?)),
520 other => Err(DecodeError::InvalidValueType(other)),
521 },
522 VAL_RANGE => {
527 let start = read_i32(buf, off)?;
528 let end = read_i32(buf, off)?;
529 let inclusive = match read_u8(buf, off)? {
530 0 => false,
531 1 => true,
532 other => return Err(DecodeError::InvalidValueType(other)),
533 };
534 Ok(Value::range(start, end, inclusive))
535 }
536 VAL_VEC2 => Ok(Value::Vec2(glam::Vec2::from_array(read_f32_lanes::<2>(
542 buf, off,
543 )?))),
544 VAL_VEC3 => Ok(Value::Vec3(glam::Vec3::from_array(read_f32_lanes::<3>(
545 buf, off,
546 )?))),
547 VAL_VEC4 => Ok(Value::Vec4(glam::Vec4::from_array(read_f32_lanes::<4>(
548 buf, off,
549 )?))),
550 VAL_QUAT => Ok(Value::Quat(glam::Quat::from_array(read_f32_lanes::<4>(
551 buf, off,
552 )?))),
553 VAL_MAT2 => Ok(Value::Mat2(glam::Mat2::from_cols_array(&read_f32_lanes::<
554 4,
555 >(
556 buf, off
557 )?))),
558 VAL_MAT3 => Ok(Value::Mat3(glam::Mat3::from_cols_array(&read_f32_lanes::<
559 9,
560 >(
561 buf, off
562 )?))),
563 VAL_MAT4 => Ok(Value::Mat4(glam::Mat4::from_cols_array(&read_f32_lanes::<
564 16,
565 >(
566 buf, off
567 )?))),
568 VAL_WEIGHTED => {
576 let count = read_u32(buf, off)?;
577 if count == 0 {
578 return Err(DecodeError::InvalidValueType(VAL_WEIGHTED));
579 }
580 let mut entries = Vec::with_capacity(safe_capacity(count as usize, buf.len(), *off, 5));
581 for _ in 0..count {
582 let weight = read_i32(buf, off)?;
583 if weight < 1 {
584 return Err(DecodeError::InvalidValueType(VAL_WEIGHTED));
585 }
586 let value = decode_value(buf, off, depth + 1)?;
587 entries.push((weight, value));
588 }
589 Ok(Value::weighted(entries))
590 }
591 _ => Err(DecodeError::InvalidValueType(tag)),
592 }
593}
594
595fn decode_proj_segment(
597 buf: &[u8],
598 off: &mut usize,
599 depth: usize,
600) -> Result<crate::ProjSegment, DecodeError> {
601 let kind = read_u8(buf, off)?;
602 match kind {
603 PROJ_SEG_INDEX => Ok(crate::ProjSegment::Index(read_i32(buf, off)?)),
604 PROJ_SEG_KEY => Ok(crate::ProjSegment::Key(decode_value(buf, off, depth)?)),
605 other => Err(DecodeError::InvalidProjSegmentKind(other)),
606 }
607}
608
609fn decode_map_key(buf: &[u8], off: &mut usize) -> Result<MapKey, DecodeError> {
613 let tag = read_u8(buf, off)?;
614 match tag {
615 VAL_INT => Ok(MapKey::Int(read_i32(buf, off)?)),
616 VAL_STRING => Ok(MapKey::Str(read_str(buf, off)?.into())),
617 VAL_BOOL => Ok(MapKey::Bool(read_u8(buf, off)? != 0)),
618 _ => Err(DecodeError::InvalidValueType(tag)),
619 }
620}
621
622fn decode_list_def(buf: &[u8], off: &mut usize) -> Result<ListDef, DecodeError> {
623 let id = read_def_id(buf, off)?;
624 let name = NameId(read_u16(buf, off)?);
625 let item_count = read_u32(buf, off)? as usize;
626 let mut items = Vec::with_capacity(safe_capacity(item_count, buf.len(), *off, 6));
627 for _ in 0..item_count {
628 let name_id = NameId(read_u16(buf, off)?);
629 let ordinal = read_i32(buf, off)?;
630 items.push((name_id, ordinal));
631 }
632 Ok(ListDef { id, name, items })
633}
634
635fn decode_list_item(buf: &[u8], off: &mut usize) -> Result<ListItemDef, DecodeError> {
636 let id = read_def_id(buf, off)?;
637 let origin = read_def_id(buf, off)?;
638 let ordinal = read_i32(buf, off)?;
639 let name = NameId(read_u16(buf, off)?);
640 Ok(ListItemDef {
641 id,
642 origin,
643 ordinal,
644 name,
645 })
646}
647
648pub fn read_section_list_literals(
650 buf: &[u8],
651 index: &InkbIndex,
652) -> Result<Vec<ListValue>, DecodeError> {
653 let Some(range) = index.section_range(SectionKind::ListLiterals) else {
654 return Ok(Vec::new());
655 };
656 let mut off = range.start;
657 let count = read_u32(buf, &mut off)? as usize;
658 let mut literals = Vec::with_capacity(safe_capacity(count, buf.len(), off, 8));
659 for _ in 0..count {
660 let item_count = read_u32(buf, &mut off)? as usize;
661 let mut items = Vec::with_capacity(safe_capacity(item_count, buf.len(), off, 8));
662 for _ in 0..item_count {
663 items.push(read_def_id(buf, &mut off)?);
664 }
665 let origin_count = read_u32(buf, &mut off)? as usize;
666 let mut origins = Vec::with_capacity(safe_capacity(origin_count, buf.len(), off, 8));
667 for _ in 0..origin_count {
668 origins.push(read_def_id(buf, &mut off)?);
669 }
670 literals.push(ListValue { items, origins });
671 }
672 Ok(literals)
673}
674
675pub fn read_section_literal_pool(buf: &[u8], index: &InkbIndex) -> Result<Vec<Value>, DecodeError> {
679 let Some(range) = index.section_range(SectionKind::LiteralPool) else {
680 return Ok(Vec::new());
681 };
682 let mut off = range.start;
683 let count = read_u32(buf, &mut off)? as usize;
684 let mut pool = Vec::with_capacity(safe_capacity(count, buf.len(), off, 1));
685 for _ in 0..count {
686 pool.push(decode_value(buf, &mut off, 0)?);
687 }
688 Ok(pool)
689}
690
691pub fn read_section_struct_shapes(
695 buf: &[u8],
696 index: &InkbIndex,
697) -> Result<Vec<StructShapeDef>, DecodeError> {
698 let Some(range) = index.section_range(SectionKind::StructShapes) else {
699 return Ok(Vec::new());
700 };
701 let mut off = range.start;
702 let count = read_u32(buf, &mut off)? as usize;
703 let mut shapes = Vec::with_capacity(safe_capacity(count, buf.len(), off, 8));
704 for _ in 0..count {
705 let id = ShapeId(read_u32(buf, &mut off)?);
706 let name = NameId(read_u16(buf, &mut off)?);
707 let field_count = read_u16(buf, &mut off)? as usize;
708 let mut fields = Vec::with_capacity(safe_capacity(field_count, buf.len(), off, 2));
709 for _ in 0..field_count {
710 fields.push(NameId(read_u16(buf, &mut off)?));
711 }
712 shapes.push(StructShapeDef { id, name, fields });
713 }
714 Ok(shapes)
715}
716
717pub fn read_section_visibility(
722 buf: &[u8],
723 index: &InkbIndex,
724) -> Result<Vec<DefinitionId>, DecodeError> {
725 let Some(range) = index.section_range(SectionKind::Visibility) else {
726 return Ok(Vec::new());
727 };
728 let mut off = range.start;
729 let count = read_u32(buf, &mut off)? as usize;
730 let mut ids = Vec::with_capacity(safe_capacity(count, buf.len(), off, 4));
731 for _ in 0..count {
732 ids.push(read_def_id(buf, &mut off)?);
733 }
734 Ok(ids)
735}
736
737pub fn read_section_alias_table(
743 buf: &[u8],
744 index: &InkbIndex,
745) -> Result<Vec<AliasEntry>, DecodeError> {
746 let Some(range) = index.section_range(SectionKind::AliasTable) else {
747 return Ok(Vec::new());
748 };
749 let mut off = range.start;
750 let section_version = read_u8(buf, &mut off)?;
751 if section_version != ALIAS_TABLE_SECTION_VERSION {
752 return Err(DecodeError::UnsupportedSectionVersion {
753 section: SectionKind::AliasTable as u8,
754 version: section_version,
755 });
756 }
757 let count = read_u32(buf, &mut off)? as usize;
758 let mut entries = Vec::with_capacity(safe_capacity(count, buf.len(), off, 16));
759 for _ in 0..count {
760 let old = read_def_id(buf, &mut off)?;
761 let new = read_def_id(buf, &mut off)?;
762 entries.push(AliasEntry { old, new });
763 }
764 Ok(entries)
765}
766
767pub fn read_section_effect_rows(
773 buf: &[u8],
774 index: &InkbIndex,
775) -> Result<Vec<EffectRowEntry>, DecodeError> {
776 let Some(range) = index.section_range(SectionKind::EffectRows) else {
777 return Ok(Vec::new());
778 };
779 let mut off = range.start;
780 let section_version = read_u8(buf, &mut off)?;
781 if section_version != EFFECT_ROWS_SECTION_VERSION {
782 return Err(DecodeError::UnsupportedSectionVersion {
783 section: SectionKind::EffectRows as u8,
784 version: section_version,
785 });
786 }
787 let count = read_u32(buf, &mut off)? as usize;
788 let mut rows = Vec::with_capacity(safe_capacity(count, buf.len(), off, 27));
792 for _ in 0..count {
793 let def = read_def_id(buf, &mut off)?;
794 let is_entry = read_u8(buf, &mut off)? != 0;
796 let direct = decode_direct_effects(buf, &mut off)?;
797 let dispatch_count = read_u32(buf, &mut off)? as usize;
798 let mut dispatches = Vec::with_capacity(safe_capacity(dispatch_count, buf.len(), off, 13));
799 for _ in 0..dispatch_count {
800 let cell = read_def_id(buf, &mut off)?;
801 let narrowable = read_u8(buf, &mut off)? != 0;
802 let fallback = decode_direct_effects(buf, &mut off)?;
803 dispatches.push(DispatchEntry {
804 cell,
805 narrowable,
806 fallback,
807 });
808 }
809 rows.push(EffectRowEntry {
810 def,
811 is_entry,
812 direct,
813 dispatches,
814 });
815 }
816 Ok(rows)
817}
818
819pub fn read_section_frame_shapes(
826 buf: &[u8],
827 index: &InkbIndex,
828) -> Result<Vec<FrameShapeDef>, DecodeError> {
829 let Some(range) = index.section_range(SectionKind::FrameShapes) else {
830 return Ok(Vec::new());
831 };
832 let mut off = range.start;
833 let section_version = read_u8(buf, &mut off)?;
834 if section_version != FRAME_SHAPES_SECTION_VERSION {
835 return Err(DecodeError::UnsupportedSectionVersion {
836 section: SectionKind::FrameShapes as u8,
837 version: section_version,
838 });
839 }
840 let count = read_u32(buf, &mut off)? as usize;
841 let mut shapes = Vec::with_capacity(safe_capacity(count, buf.len(), off, 12));
843 for _ in 0..count {
844 let site = read_def_id(buf, &mut off)?;
845 let slot_count = read_u32(buf, &mut off)? as usize;
846 let mut slots = Vec::with_capacity(safe_capacity(slot_count, buf.len(), off, 2));
847 for _ in 0..slot_count {
848 slots.push(NameId(read_u16(buf, &mut off)?));
849 }
850 shapes.push(FrameShapeDef { site, slots });
851 }
852 Ok(shapes)
853}
854
855pub fn read_section_line_variant_groups(
863 buf: &[u8],
864 index: &InkbIndex,
865) -> Result<Vec<crate::definition::LineVariantGroup>, DecodeError> {
866 let Some(range) = index.section_range(SectionKind::LineVariantGroups) else {
867 return Ok(Vec::new());
868 };
869 let mut off = range.start;
870 let section_version = read_u8(buf, &mut off)?;
871 if section_version != LINE_VARIANT_GROUPS_SECTION_VERSION {
872 return Err(DecodeError::UnsupportedSectionVersion {
873 section: SectionKind::LineVariantGroups as u8,
874 version: section_version,
875 });
876 }
877 let count = read_u32(buf, &mut off)? as usize;
878 let mut groups = Vec::with_capacity(safe_capacity(count, buf.len(), off, 15));
881 for _ in 0..count {
882 let scope_id = read_def_id(buf, &mut off)?;
883 let base = read_u32(buf, &mut off)?;
884 let dim_count = read_u8(buf, &mut off)? as usize;
885 let mut dims = Vec::with_capacity(safe_capacity(dim_count, buf.len(), off, 2));
886 for _ in 0..dim_count {
887 dims.push(read_u16(buf, &mut off)?);
888 }
889 groups.push(crate::definition::LineVariantGroup {
890 scope_id,
891 base,
892 dims,
893 });
894 }
895 Ok(groups)
896}
897
898pub fn read_section_debug_info(
908 buf: &[u8],
909 index: &InkbIndex,
910) -> Result<Option<DebugInfoSection>, DecodeError> {
911 let Some(range) = index.section_range(SectionKind::DebugInfo) else {
912 return Ok(None);
913 };
914 let mut off = range.start;
915 let section_version = read_u8(buf, &mut off)?;
916 if section_version != DEBUG_INFO_SECTION_VERSION {
917 return Err(DecodeError::UnsupportedSectionVersion {
918 section: SectionKind::DebugInfo as u8,
919 version: section_version,
920 });
921 }
922
923 let file_count = read_u32(buf, &mut off)? as usize;
924 let mut files = Vec::with_capacity(safe_capacity(file_count, buf.len(), off, 14));
927 for _ in 0..file_count {
928 let surface = FileSurface::from_u8(read_u8(buf, &mut off)?)?;
929 let path = read_str(buf, &mut off)?;
930 let source_hash = read_u64(buf, &mut off)?;
931 let line_count =
934 usize::try_from(read_varint(buf, &mut off)?).map_err(|_| DecodeError::UnexpectedEof)?;
935 let mut line_starts = Vec::with_capacity(safe_capacity(line_count, buf.len(), off, 1));
937 let mut prev: u32 = 0;
938 for _ in 0..line_count {
939 let delta = u32::try_from(read_varint(buf, &mut off)?)
940 .map_err(|_| DecodeError::UnexpectedEof)?;
941 prev = prev.wrapping_add(delta);
944 line_starts.push(prev);
945 }
946 files.push(DebugFileEntry {
947 surface,
948 path,
949 source_hash,
950 line_starts,
951 });
952 }
953
954 let container_count = read_u32(buf, &mut off)? as usize;
955 let mut containers = Vec::with_capacity(safe_capacity(container_count, buf.len(), off, 2));
958 for _ in 0..container_count {
959 let entry_count =
963 usize::try_from(read_varint(buf, &mut off)?).map_err(|_| DecodeError::UnexpectedEof)?;
964 let mut entries = Vec::with_capacity(safe_capacity(entry_count, buf.len(), off, 9));
967 let mut prev_offset: u32 = 0;
968 for _ in 0..entry_count {
969 #[expect(
970 clippy::cast_possible_truncation,
971 reason = "wire fields are u32-domain values re-widened to u64 for varint transport"
972 )]
973 let delta = read_varint(buf, &mut off)? as u32;
974 let bytecode_offset = prev_offset.wrapping_add(delta);
975 prev_offset = bytecode_offset;
976 #[expect(clippy::cast_possible_truncation)]
977 let file_idx = read_varint(buf, &mut off)? as u32;
978 #[expect(clippy::cast_possible_truncation)]
979 let range_start = read_varint(buf, &mut off)? as u32;
980 #[expect(clippy::cast_possible_truncation)]
981 let range_len = read_varint(buf, &mut off)? as u32;
982 let kind_token = read_u32(buf, &mut off)?;
983 let flags = read_u8(buf, &mut off)?;
986 entries.push(DebugEntry {
987 bytecode_offset,
988 file_idx,
989 range_start,
990 range_len,
991 kind_token,
992 flags,
993 });
994 }
995
996 let local_count =
997 usize::try_from(read_varint(buf, &mut off)?).map_err(|_| DecodeError::UnexpectedEof)?;
998 let mut locals = Vec::with_capacity(safe_capacity(local_count, buf.len(), off, 7));
1001 for _ in 0..local_count {
1002 let slot = read_u16(buf, &mut off)?;
1003 let name = read_str(buf, &mut off)?;
1004 let flags = read_u8(buf, &mut off)?;
1008 if flags & !LOCAL_FLAGS_KNOWN != 0 {
1009 return Err(DecodeError::InvalidDebugLocalFlags(flags));
1010 }
1011 let synthetic = flags & LOCAL_FLAG_SYNTHETIC != 0;
1012 let declaring_range = if flags & LOCAL_FLAG_HAS_RANGE == 0 {
1013 None
1014 } else {
1015 #[expect(clippy::cast_possible_truncation)]
1016 let file_idx = read_varint(buf, &mut off)? as u32;
1017 #[expect(clippy::cast_possible_truncation)]
1018 let range_start = read_varint(buf, &mut off)? as u32;
1019 #[expect(clippy::cast_possible_truncation)]
1020 let range_len = read_varint(buf, &mut off)? as u32;
1021 Some((file_idx, range_start, range_len))
1022 };
1023 locals.push(DebugLocalEntry {
1024 slot,
1025 name,
1026 declaring_range,
1027 synthetic,
1028 });
1029 }
1030
1031 containers.push(DebugContainerTable { entries, locals });
1032 }
1033
1034 Ok(Some(DebugInfoSection { files, containers }))
1035}
1036
1037fn decode_direct_effects(buf: &[u8], off: &mut usize) -> Result<DirectEffects, DecodeError> {
1039 let read_count = read_u32(buf, off)? as usize;
1040 let mut reads = Vec::with_capacity(safe_capacity(read_count, buf.len(), *off, 8));
1041 for _ in 0..read_count {
1042 reads.push(read_def_id(buf, off)?);
1043 }
1044 let write_count = read_u32(buf, off)? as usize;
1045 let mut writes = Vec::with_capacity(safe_capacity(write_count, buf.len(), *off, 8));
1046 for _ in 0..write_count {
1047 writes.push(read_def_id(buf, off)?);
1048 }
1049 let call_count = read_u32(buf, off)? as usize;
1050 let mut calls = Vec::with_capacity(safe_capacity(call_count, buf.len(), *off, 4));
1051 for _ in 0..call_count {
1052 calls.push(decode_call_atom(buf, off)?);
1053 }
1054 let opaque = read_u8(buf, off)? != 0;
1055 let dims = read_u8(buf, off)?;
1059 if dims & !super::EFFECT_DIM_KNOWN_MASK != 0 {
1060 return Err(DecodeError::InvalidEffectDimensions(dims));
1061 }
1062 Ok(DirectEffects {
1063 reads,
1064 writes,
1065 calls,
1066 opaque,
1067 emits: dims & super::EFFECT_DIM_EMITS != 0,
1068 tags: dims & super::EFFECT_DIM_TAGS != 0,
1069 faults: dims & super::EFFECT_DIM_FAULTS != 0,
1070 })
1071}
1072
1073fn decode_call_atom(buf: &[u8], off: &mut usize) -> Result<CallAtom, DecodeError> {
1078 let name = NameId(read_u16(buf, off)?);
1079 let cap_tag = read_u8(buf, off)?;
1080 let capability = match cap_tag {
1081 CAP_PARAM_ANY => CapabilityParam::Any,
1082 other => return Err(DecodeError::InvalidEffectCapParam(other)),
1083 };
1084 let handle_tag = read_u8(buf, off)?;
1085 if handle_tag != HANDLE_PARAM_NONE {
1086 return Err(DecodeError::InvalidEffectHandleParam(handle_tag));
1087 }
1088 Ok(CallAtom {
1089 name,
1090 capability,
1091 handle_param: None,
1092 })
1093}
1094
1095fn decode_external(buf: &[u8], off: &mut usize) -> Result<ExternalFnDef, DecodeError> {
1096 let id = read_def_id(buf, off)?;
1097 let name = NameId(read_u16(buf, off)?);
1098 let arg_count = read_u8(buf, off)?;
1099 let has_fallback = read_u8(buf, off)? != 0;
1100 let fallback = if has_fallback {
1101 Some(read_def_id(buf, off)?)
1102 } else {
1103 None
1104 };
1105 Ok(ExternalFnDef {
1106 id,
1107 name,
1108 arg_count,
1109 fallback,
1110 })
1111}
1112
1113fn decode_container(buf: &[u8], off: &mut usize) -> Result<ContainerDef, DecodeError> {
1114 let id = read_def_id(buf, off)?;
1115 let scope_id = read_def_id(buf, off)?;
1116 let has_name = read_u8(buf, off)? != 0;
1117 let name = if has_name {
1118 Some(NameId(read_u16(buf, off)?))
1119 } else {
1120 None
1121 };
1122 let counting_bits = read_u8(buf, off)?;
1123 let counting_flags = CountingFlags::from_bits(counting_bits).unwrap_or(CountingFlags::empty());
1124 let path_hash = read_i32(buf, off)?;
1125 let param_count = read_u8(buf, off)?;
1126 let local = read_u8(buf, off)? != 0;
1127 let param_meta_count = read_u16(buf, off)? as usize;
1129 let mut params = Vec::with_capacity(safe_capacity(param_meta_count, buf.len(), *off, 3));
1130 for _ in 0..param_meta_count {
1131 let name = NameId(read_u16(buf, off)?);
1132 let is_ref = read_u8(buf, off)? != 0;
1133 let slot = read_u16(buf, off)?;
1134 params.push(ParamMeta { name, is_ref, slot });
1135 }
1136 if !params.is_empty() && params.len() != usize::from(param_count) {
1144 return Err(DecodeError::ParamCountMismatch {
1145 declared: param_count,
1146 actual: params.len(),
1147 });
1148 }
1149
1150 let bytecode_len = read_u32(buf, off)? as usize;
1151 if *off + bytecode_len > buf.len() {
1152 return Err(DecodeError::UnexpectedEof);
1153 }
1154 let bytecode = buf[*off..*off + bytecode_len].to_vec();
1155 *off += bytecode_len;
1156
1157 Ok(ContainerDef {
1158 id,
1159 scope_id,
1160 name,
1161 bytecode,
1162 counting_flags,
1163 path_hash,
1164 param_count,
1165 params,
1166 local,
1167 })
1168}
1169
1170pub fn read_section_line_tables(
1172 buf: &[u8],
1173 index: &InkbIndex,
1174) -> Result<Vec<ScopeLineTable>, DecodeError> {
1175 let range =
1176 index
1177 .section_range(SectionKind::LineTables)
1178 .ok_or(DecodeError::MissingSectionKind(
1179 SectionKind::LineTables as u8,
1180 ))?;
1181 let mut off = range.start;
1182 let count = read_u32(buf, &mut off)? as usize;
1183 let mut tables = Vec::with_capacity(safe_capacity(count, buf.len(), off, 12));
1184 for _ in 0..count {
1185 tables.push(decode_scope_line_table(buf, &mut off)?);
1186 }
1187 Ok(tables)
1188}
1189
1190fn decode_scope_line_table(buf: &[u8], off: &mut usize) -> Result<ScopeLineTable, DecodeError> {
1191 let scope_id = read_def_id(buf, off)?;
1192 let line_count = read_u32(buf, off)? as usize;
1193 let mut lines = Vec::with_capacity(safe_capacity(line_count, buf.len(), *off, 9));
1194 for _ in 0..line_count {
1195 lines.push(decode_line_entry(buf, off)?);
1196 }
1197 Ok(ScopeLineTable { scope_id, lines })
1198}
1199
1200fn decode_line_entry(buf: &[u8], off: &mut usize) -> Result<LineEntry, DecodeError> {
1201 let content = decode_line_content(buf, off)?;
1202 let source_hash = read_u64(buf, off)?;
1203 let has_audio = read_u8(buf, off)? != 0;
1204 let audio_ref = if has_audio {
1205 Some(read_str(buf, off)?)
1206 } else {
1207 None
1208 };
1209 let slot_count = read_u8(buf, off)? as usize;
1211 let mut slot_info = Vec::with_capacity(slot_count);
1212 for _ in 0..slot_count {
1213 let index = read_u8(buf, off)?;
1214 let name = read_str(buf, off)?;
1215 slot_info.push(SlotInfo { index, name });
1216 }
1217
1218 let has_source_loc = read_u8(buf, off)? != 0;
1220 let source_location = if has_source_loc {
1221 let file = read_str(buf, off)?;
1222 let range_start = read_u32(buf, off)?;
1223 let range_end = read_u32(buf, off)?;
1224 Some(SourceLocation {
1225 file,
1226 range_start,
1227 range_end,
1228 })
1229 } else {
1230 None
1231 };
1232
1233 let flags = crate::LineFlags::from_content(&content);
1234 Ok(LineEntry {
1235 content,
1236 flags,
1237 source_hash,
1238 audio_ref,
1239 slot_info,
1240 source_location,
1241 })
1242}
1243
1244pub(crate) fn decode_line_content(buf: &[u8], off: &mut usize) -> Result<LineContent, DecodeError> {
1245 let tag = read_u8(buf, off)?;
1246 match tag {
1247 LINE_PLAIN => Ok(LineContent::Plain(read_str(buf, off)?)),
1248 LINE_TEMPLATE => {
1249 let part_count = read_u32(buf, off)? as usize;
1250 let mut parts = Vec::with_capacity(safe_capacity(part_count, buf.len(), *off, 2));
1251 for _ in 0..part_count {
1252 parts.push(decode_line_part(buf, off, 0)?);
1253 }
1254 Ok(LineContent::Template(parts))
1255 }
1256 _ => Err(DecodeError::InvalidLineContent(tag)),
1257 }
1258}
1259
1260fn decode_line_part(buf: &[u8], off: &mut usize, depth: usize) -> Result<LinePart, DecodeError> {
1265 if depth > MAX_DECODE_DEPTH {
1266 return Err(DecodeError::MaxDepthExceeded(MAX_DECODE_DEPTH));
1267 }
1268 let tag = read_u8(buf, off)?;
1269 match tag {
1270 PART_LITERAL => Ok(LinePart::Literal(read_str(buf, off)?)),
1271 PART_SLOT => Ok(LinePart::Slot(read_u8(buf, off)?)),
1272 PART_SELECT => {
1273 let slot = read_u8(buf, off)?;
1274 let variant_count = read_u32(buf, off)? as usize;
1275 let mut variants = Vec::with_capacity(safe_capacity(variant_count, buf.len(), *off, 6));
1276 for _ in 0..variant_count {
1277 let key = decode_select_key(buf, off)?;
1278 let text = read_str(buf, off)?;
1279 variants.push((key, text));
1280 }
1281 let default = read_str(buf, off)?;
1282 Ok(LinePart::Select {
1283 slot,
1284 variants,
1285 default,
1286 })
1287 }
1288 PART_SPAN => {
1289 let name = read_str(buf, off)?;
1290 let attr_count = read_u32(buf, off)? as usize;
1291 let attrs_cap = safe_capacity(attr_count, buf.len(), *off, 8);
1294 let mut attrs = Vec::with_capacity(attrs_cap);
1295 for _ in 0..attr_count {
1296 let k = read_str(buf, off)?;
1297 let v = read_str(buf, off)?;
1298 attrs.push((k, v));
1299 }
1300 let child_count = read_u32(buf, off)? as usize;
1301 let mut children = Vec::with_capacity(safe_capacity(child_count, buf.len(), *off, 2));
1302 for _ in 0..child_count {
1303 children.push(decode_line_part(buf, off, depth + 1)?);
1304 }
1305 Ok(LinePart::Span {
1306 name,
1307 attrs,
1308 children,
1309 })
1310 }
1311 _ => Err(DecodeError::InvalidLinePart(tag)),
1312 }
1313}
1314
1315fn decode_select_key(buf: &[u8], off: &mut usize) -> Result<SelectKey, DecodeError> {
1316 let tag = read_u8(buf, off)?;
1317 match tag {
1318 KEY_CARDINAL => Ok(SelectKey::Cardinal(decode_plural_category(buf, off)?)),
1319 KEY_ORDINAL => Ok(SelectKey::Ordinal(decode_plural_category(buf, off)?)),
1320 KEY_EXACT => Ok(SelectKey::Exact(read_i32(buf, off)?)),
1321 KEY_KEYWORD => Ok(SelectKey::Keyword(read_str(buf, off)?)),
1322 _ => Err(DecodeError::InvalidSelectKey(tag)),
1323 }
1324}
1325
1326fn decode_plural_category(buf: &[u8], off: &mut usize) -> Result<PluralCategory, DecodeError> {
1327 let tag = read_u8(buf, off)?;
1328 match tag {
1329 CAT_ZERO => Ok(PluralCategory::Zero),
1330 CAT_ONE => Ok(PluralCategory::One),
1331 CAT_TWO => Ok(PluralCategory::Two),
1332 CAT_FEW => Ok(PluralCategory::Few),
1333 CAT_MANY => Ok(PluralCategory::Many),
1334 CAT_OTHER => Ok(PluralCategory::Other),
1335 _ => Err(DecodeError::InvalidPluralCategory(tag)),
1336 }
1337}
1338
1339#[cfg(test)]
1340mod tests {
1341 use super::*;
1342 use crate::codec::{write_u8, write_u32};
1343
1344 fn duplicate_int_key_map_bytes() -> Vec<u8> {
1351 let mut buf = Vec::new();
1352 write_u8(&mut buf, VAL_MAP);
1353 write_u32(&mut buf, 2); write_u8(&mut buf, VAL_INT);
1356 buf.extend_from_slice(&0i32.to_le_bytes());
1357 write_u8(&mut buf, VAL_INT);
1358 buf.extend_from_slice(&1i32.to_le_bytes());
1359 write_u8(&mut buf, VAL_INT);
1361 buf.extend_from_slice(&0i32.to_le_bytes());
1362 write_u8(&mut buf, VAL_INT);
1363 buf.extend_from_slice(&2i32.to_le_bytes());
1364 buf
1365 }
1366
1367 #[test]
1368 fn decode_value_rejects_duplicate_map_key() {
1369 let buf = duplicate_int_key_map_bytes();
1370 let mut off = 0;
1371 assert_eq!(
1372 decode_value(&buf, &mut off, 0),
1373 Err(DecodeError::DuplicateMapKey)
1374 );
1375 }
1376
1377 #[test]
1378 fn decode_value_accepts_distinct_map_keys() {
1379 let mut buf = Vec::new();
1380 write_u8(&mut buf, VAL_MAP);
1381 write_u32(&mut buf, 2);
1382 write_u8(&mut buf, VAL_INT);
1383 buf.extend_from_slice(&0i32.to_le_bytes());
1384 write_u8(&mut buf, VAL_INT);
1385 buf.extend_from_slice(&1i32.to_le_bytes());
1386 write_u8(&mut buf, VAL_INT);
1387 buf.extend_from_slice(&5i32.to_le_bytes());
1388 write_u8(&mut buf, VAL_INT);
1389 buf.extend_from_slice(&2i32.to_le_bytes());
1390
1391 let mut off = 0;
1392 let value = decode_value(&buf, &mut off, 0).expect("distinct keys decode cleanly");
1393 let Value::Map(map) = value else {
1394 unreachable!("expected a map value");
1395 };
1396 assert_eq!(map.len(), 2);
1397 }
1398}