Skip to main content

bugstalker/debugger/variable/value/
serialize.rs

1use crate::debugger::debugee::dwarf::r#type::{
2    ArrayType, ComplexType, MemberLocation, StructureMember, TypeDeclaration, TypeId,
3};
4use crate::debugger::variable::value::Value;
5use indexmap::IndexMap;
6use serde_json::Value as JsonValue;
7
8#[derive(Debug, thiserror::Error)]
9pub enum SerializeError {
10    #[error("unsupported input: {0}")]
11    UnsupportedInput(String),
12    #[error("missing required field: {0}")]
13    MissingField(String),
14    #[error("unexpected input for type: {0}")]
15    UnexpectedInput(String),
16    #[error("unknown type id")]
17    UnknownType,
18    #[error("unknown type size for type id {0:?}")]
19    UnknownTypeSize(TypeId),
20    #[error("enum variant '{0}' not found")]
21    EnumVariantNotFound(String),
22    #[error("enum discriminant type missing")]
23    EnumDiscriminantMissing,
24    #[error("member offset for '{0}' not available")]
25    MemberOffsetMissing(String),
26    #[error("array element type missing")]
27    ArrayElementTypeMissing,
28    #[error("array length mismatch: expected {expected}, got {actual}")]
29    ArrayLengthMismatch { expected: usize, actual: usize },
30    #[error("value size mismatch: expected {expected}, got {actual}")]
31    SizeMismatch { expected: usize, actual: usize },
32    #[error("scalar parse failed: {0}")]
33    ScalarParse(String),
34}
35
36#[derive(Clone, Debug)]
37pub struct WriteOffset {
38    pub path: String,
39    pub offset: usize,
40    pub size: usize,
41}
42
43#[derive(Clone, Debug)]
44pub struct SerializedValue {
45    pub bytes: Vec<u8>,
46    pub size: usize,
47    pub offsets: Vec<WriteOffset>,
48}
49
50#[derive(Clone, Debug)]
51enum InputValue {
52    Scalar(String),
53    Array(Vec<InputValue>),
54    Object(IndexMap<String, InputValue>),
55    EnumVariant {
56        name: String,
57        payload: Option<Box<InputValue>>,
58    },
59}
60
61pub fn serialize_dap_value(
62    input: &str,
63    type_graph: &ComplexType,
64    type_id: TypeId,
65    runtime_value: Option<&Value>,
66) -> Result<SerializedValue, SerializeError> {
67    let input = parse_input_value(input)?;
68    let mut offsets = Vec::new();
69    let bytes =
70        serialize_value_inner(&input, type_graph, type_id, runtime_value, "", &mut offsets)?;
71    let size = bytes.len();
72    Ok(SerializedValue {
73        bytes,
74        size,
75        offsets,
76    })
77}
78
79fn parse_input_value(input: &str) -> Result<InputValue, SerializeError> {
80    let trimmed = input.trim();
81    if trimmed.is_empty() {
82        return Err(SerializeError::UnsupportedInput("empty input".to_string()));
83    }
84
85    if let Ok(json) = serde_json::from_str::<JsonValue>(trimmed) {
86        return Ok(input_from_json(&json));
87    }
88
89    if let Some(enum_value) = parse_enum_syntax(trimmed)? {
90        return Ok(enum_value);
91    }
92
93    if let Some(tuple) = parse_tuple_syntax(trimmed)? {
94        return Ok(tuple);
95    }
96
97    if let Some(obj) = parse_object_syntax(trimmed)? {
98        return Ok(obj);
99    }
100
101    Ok(InputValue::Scalar(trimmed.to_string()))
102}
103
104fn parse_enum_syntax(input: &str) -> Result<Option<InputValue>, SerializeError> {
105    let mut chars = input.chars();
106    let first = chars.next();
107    let Some(first) = first else {
108        return Ok(None);
109    };
110    if !first.is_ascii_alphabetic() && first != '_' {
111        return Ok(None);
112    }
113
114    let mut ident = String::new();
115    ident.push(first);
116    let mut rest_start = first.len_utf8();
117    for ch in input[rest_start..].chars() {
118        if ch.is_ascii_alphanumeric() || ch == '_' {
119            ident.push(ch);
120            rest_start += ch.len_utf8();
121        } else {
122            break;
123        }
124    }
125
126    let rest = input[rest_start..].trim_start();
127    if rest.is_empty() {
128        return Ok(None);
129    }
130
131    let payload = if rest.starts_with('(') && rest.ends_with(')') {
132        let inner = &rest[1..rest.len() - 1];
133        Some(Box::new(parse_input_value(inner)?))
134    } else if rest.starts_with('{') && rest.ends_with('}') {
135        let inner = &rest[1..rest.len() - 1];
136        Some(Box::new(parse_object_body(inner)?))
137    } else {
138        return Ok(None);
139    };
140
141    Ok(Some(InputValue::EnumVariant {
142        name: ident,
143        payload,
144    }))
145}
146
147fn parse_tuple_syntax(input: &str) -> Result<Option<InputValue>, SerializeError> {
148    let trimmed = input.trim();
149    if !(trimmed.starts_with('(') && trimmed.ends_with(')')) {
150        return Ok(None);
151    }
152    let inner = &trimmed[1..trimmed.len() - 1];
153    let items = split_top_level(inner)
154        .into_iter()
155        .filter(|part| !part.trim().is_empty())
156        .map(|part| parse_input_value(part.trim()))
157        .collect::<Result<Vec<_>, _>>()?;
158    Ok(Some(InputValue::Array(items)))
159}
160
161fn parse_object_syntax(input: &str) -> Result<Option<InputValue>, SerializeError> {
162    let trimmed = input.trim();
163    if !(trimmed.starts_with('{') && trimmed.ends_with('}')) {
164        return Ok(None);
165    }
166    let inner = &trimmed[1..trimmed.len() - 1];
167    Ok(Some(parse_object_body(inner)?))
168}
169
170fn parse_object_body(body: &str) -> Result<InputValue, SerializeError> {
171    let mut out = IndexMap::new();
172    for part in split_top_level(body) {
173        let part = part.trim();
174        if part.is_empty() {
175            continue;
176        }
177        let (key, value) = split_key_value(part)?;
178        let key = key.trim().trim_matches('"').trim_matches('\'');
179        let value = parse_input_value(value.trim())?;
180        out.insert(key.to_string(), value);
181    }
182    Ok(InputValue::Object(out))
183}
184
185fn split_key_value(input: &str) -> Result<(&str, &str), SerializeError> {
186    let mut depth = 0usize;
187    let mut in_str = None;
188    for (idx, ch) in input.char_indices() {
189        match ch {
190            '"' | '\'' => {
191                if in_str == Some(ch) {
192                    in_str = None;
193                } else if in_str.is_none() {
194                    in_str = Some(ch);
195                }
196            }
197            '{' | '[' | '(' if in_str.is_none() => depth += 1,
198            '}' | ']' | ')' if in_str.is_none() => depth = depth.saturating_sub(1),
199            ':' if in_str.is_none() && depth == 0 => {
200                let (k, v) = input.split_at(idx);
201                return Ok((k, &v[1..]));
202            }
203            _ => {}
204        }
205    }
206    Err(SerializeError::UnsupportedInput(format!(
207        "expected key:value in '{input}'"
208    )))
209}
210
211fn split_top_level(input: &str) -> Vec<&str> {
212    let mut parts = Vec::new();
213    let mut depth = 0usize;
214    let mut in_str: Option<char> = None;
215    let mut start = 0usize;
216    let mut prev_char = '\0';
217
218    for (idx, ch) in input.char_indices() {
219        if let Some(quote) = in_str {
220            if ch == quote && prev_char != '\\' {
221                in_str = None;
222            }
223            prev_char = ch;
224            continue;
225        }
226
227        match ch {
228            '\'' | '"' => in_str = Some(ch),
229            '{' | '[' | '(' => depth += 1,
230            '}' | ']' | ')' => depth = depth.saturating_sub(1),
231            ',' if depth == 0 => {
232                parts.push(&input[start..idx]);
233                start = idx + ch.len_utf8();
234            }
235            _ => {}
236        }
237        prev_char = ch;
238    }
239
240    if start <= input.len() {
241        parts.push(&input[start..]);
242    }
243
244    parts
245}
246
247fn input_from_json(value: &JsonValue) -> InputValue {
248    match value {
249        JsonValue::Null => InputValue::Scalar("null".to_string()),
250        JsonValue::Bool(b) => InputValue::Scalar(b.to_string()),
251        JsonValue::Number(num) => InputValue::Scalar(num.to_string()),
252        JsonValue::String(s) => InputValue::Scalar(s.clone()),
253        JsonValue::Array(items) => InputValue::Array(items.iter().map(input_from_json).collect()),
254        JsonValue::Object(map) => InputValue::Object(
255            map.iter()
256                .map(|(k, v)| (k.clone(), input_from_json(v)))
257                .collect(),
258        ),
259    }
260}
261
262fn serialize_value_inner(
263    input: &InputValue,
264    type_graph: &ComplexType,
265    type_id: TypeId,
266    runtime_value: Option<&Value>,
267    path: &str,
268    offsets: &mut Vec<WriteOffset>,
269) -> Result<Vec<u8>, SerializeError> {
270    let mut ctx = SerializeContext {
271        input,
272        type_graph,
273        type_id,
274        runtime_value,
275        path,
276        offsets,
277    };
278    let decl = type_graph
279        .types
280        .get(&type_id)
281        .ok_or(SerializeError::UnknownType)?;
282    match decl {
283        TypeDeclaration::Scalar(scalar) => serialize_scalar_value(input, scalar),
284        TypeDeclaration::Pointer { .. } => serialize_pointer_value(input),
285        TypeDeclaration::Array(array) => serialize_array_value(&mut ctx, array),
286        TypeDeclaration::Structure {
287            byte_size, members, ..
288        } => serialize_struct_value(&mut ctx, *byte_size, members),
289        TypeDeclaration::Union {
290            byte_size, members, ..
291        } => serialize_union_value(&mut ctx, *byte_size, members),
292        TypeDeclaration::CStyleEnum {
293            byte_size,
294            discr_type,
295            enumerators,
296            ..
297        } => serialize_c_enum_value(input, type_graph, *byte_size, *discr_type, enumerators),
298        TypeDeclaration::RustEnum {
299            byte_size,
300            discr_type,
301            enumerators,
302            ..
303        } => serialize_rust_enum_value(&mut ctx, *byte_size, discr_type.as_deref(), enumerators),
304        TypeDeclaration::Subroutine { .. } => serialize_pointer_value(input),
305        TypeDeclaration::ModifiedType { inner, .. } => {
306            let inner = inner.ok_or(SerializeError::UnknownType)?;
307            serialize_value_inner(input, type_graph, inner, runtime_value, path, offsets)
308        }
309    }
310}
311
312struct SerializeContext<'a> {
313    input: &'a InputValue,
314    type_graph: &'a ComplexType,
315    type_id: TypeId,
316    runtime_value: Option<&'a Value>,
317    path: &'a str,
318    offsets: &'a mut Vec<WriteOffset>,
319}
320
321fn serialize_scalar_value(
322    input: &InputValue,
323    scalar: &crate::debugger::debugee::dwarf::r#type::ScalarType,
324) -> Result<Vec<u8>, SerializeError> {
325    let input = match input {
326        InputValue::Scalar(s) => s.as_str(),
327        _ => {
328            return Err(SerializeError::UnexpectedInput(
329                "expected scalar".to_string(),
330            ));
331        }
332    };
333    let byte_size = scalar.byte_size.unwrap_or(0) as usize;
334    let encoding = scalar.encoding;
335
336    fn parse_i128(input: &str) -> Result<i128, SerializeError> {
337        if let Some(hex) = input
338            .strip_prefix("0x")
339            .or_else(|| input.strip_prefix("0X"))
340        {
341            i128::from_str_radix(hex, 16).map_err(|e| SerializeError::ScalarParse(e.to_string()))
342        } else {
343            input
344                .parse::<i128>()
345                .map_err(|e| SerializeError::ScalarParse(e.to_string()))
346        }
347    }
348
349    fn parse_u128(input: &str) -> Result<u128, SerializeError> {
350        if let Some(hex) = input
351            .strip_prefix("0x")
352            .or_else(|| input.strip_prefix("0X"))
353        {
354            u128::from_str_radix(hex, 16).map_err(|e| SerializeError::ScalarParse(e.to_string()))
355        } else {
356            input
357                .parse::<u128>()
358                .map_err(|e| SerializeError::ScalarParse(e.to_string()))
359        }
360    }
361
362    let bytes = match encoding {
363        Some(gimli::DW_ATE_float) => match byte_size {
364            4 => Ok(input
365                .parse::<f32>()
366                .map_err(|e| SerializeError::ScalarParse(e.to_string()))?
367                .to_le_bytes()
368                .to_vec()),
369            8 => Ok(input
370                .parse::<f64>()
371                .map_err(|e| SerializeError::ScalarParse(e.to_string()))?
372                .to_le_bytes()
373                .to_vec()),
374            _ => Err(SerializeError::ScalarParse(format!(
375                "unsupported float size {byte_size}"
376            ))),
377        },
378        Some(gimli::DW_ATE_boolean) => {
379            let value = match input {
380                "true" | "True" | "TRUE" | "1" => true,
381                "false" | "False" | "FALSE" | "0" => false,
382                _ => {
383                    return Err(SerializeError::ScalarParse(format!(
384                        "expected bool, got '{input}'"
385                    )));
386                }
387            };
388            Ok(vec![if value { 1 } else { 0 }])
389        }
390        Some(gimli::DW_ATE_UTF) | Some(gimli::DW_ATE_ASCII) => {
391            let ch =
392                if let Some(stripped) = input.strip_prefix('\'').and_then(|s| s.strip_suffix('\''))
393                {
394                    stripped.chars().next().ok_or_else(|| {
395                        SerializeError::ScalarParse("empty char literal".to_string())
396                    })?
397                } else if input.chars().count() == 1 {
398                    input.chars().next().unwrap()
399                } else {
400                    return Err(SerializeError::ScalarParse(
401                        "expected char literal".to_string(),
402                    ));
403                };
404            Ok((ch as u32).to_le_bytes().to_vec())
405        }
406        Some(gimli::DW_ATE_signed) | Some(gimli::DW_ATE_signed_char) => {
407            let value = parse_i128(input)?;
408            int_to_bytes(value as i128, byte_size)
409        }
410        Some(gimli::DW_ATE_unsigned)
411        | Some(gimli::DW_ATE_unsigned_char)
412        | Some(gimli::DW_ATE_address) => {
413            let value = parse_u128(input)?;
414            uint_to_bytes(value, byte_size)
415        }
416        _ => {
417            let value = parse_i128(input)?;
418            int_to_bytes(value, byte_size)
419        }
420    }?;
421
422    Ok(bytes)
423}
424
425fn serialize_pointer_value(input: &InputValue) -> Result<Vec<u8>, SerializeError> {
426    let input = match input {
427        InputValue::Scalar(s) => s.as_str(),
428        _ => {
429            return Err(SerializeError::UnexpectedInput(
430                "expected pointer scalar".to_string(),
431            ));
432        }
433    };
434    let addr = if let Some(hex) = input
435        .strip_prefix("0x")
436        .or_else(|| input.strip_prefix("0X"))
437    {
438        usize::from_str_radix(hex, 16).map_err(|e| SerializeError::ScalarParse(e.to_string()))?
439    } else {
440        input
441            .parse::<usize>()
442            .map_err(|e| SerializeError::ScalarParse(e.to_string()))?
443    };
444    Ok(addr.to_le_bytes().to_vec())
445}
446
447fn serialize_struct_value(
448    ctx: &mut SerializeContext<'_>,
449    byte_size: Option<u64>,
450    members: &[StructureMember],
451) -> Result<Vec<u8>, SerializeError> {
452    let size = byte_size.ok_or(SerializeError::UnknownTypeSize(ctx.type_id))?;
453    let mut buffer = vec![0u8; size as usize];
454
455    let input_map = match ctx.input {
456        InputValue::Object(map) => Some(map),
457        _ => None,
458    };
459    let input_array = match ctx.input {
460        InputValue::Array(items) => Some(items),
461        _ => None,
462    };
463
464    for (index, member) in members.iter().enumerate() {
465        let member_input = if let Some(name) = member.name.as_deref() {
466            input_map
467                .and_then(|map| map.get(name))
468                .ok_or_else(|| SerializeError::MissingField(name.to_string()))?
469        } else {
470            input_array
471                .and_then(|items| items.get(index))
472                .ok_or_else(|| SerializeError::MissingField(format!("[{index}]")))?
473        };
474
475        let member_type = member.type_ref.ok_or(SerializeError::UnknownType)?;
476        let member_runtime = runtime_member(ctx.runtime_value, member.name.as_deref(), index);
477        let member_bytes = serialize_value_inner(
478            member_input,
479            ctx.type_graph,
480            member_type,
481            member_runtime,
482            &format!("{}{}", ctx.path, member_path_suffix(member, index)),
483            ctx.offsets,
484        )?;
485        let offset = member_offset(member, ctx.runtime_value, index)
486            .ok_or_else(|| SerializeError::MemberOffsetMissing(member_name(member, index)))?;
487        let end = offset + member_bytes.len();
488        if end > buffer.len() {
489            return Err(SerializeError::SizeMismatch {
490                expected: buffer.len(),
491                actual: end,
492            });
493        }
494        buffer[offset..end].copy_from_slice(&member_bytes);
495        ctx.offsets.push(WriteOffset {
496            path: format!("{}{}", ctx.path, member_path_suffix(member, index)),
497            offset,
498            size: member_bytes.len(),
499        });
500    }
501
502    Ok(buffer)
503}
504
505fn serialize_union_value(
506    ctx: &mut SerializeContext<'_>,
507    byte_size: Option<u64>,
508    members: &[StructureMember],
509) -> Result<Vec<u8>, SerializeError> {
510    let size = byte_size.ok_or(SerializeError::UnknownTypeSize(ctx.type_id))?;
511    let mut buffer = vec![0u8; size as usize];
512    let (member_idx, member_input) = match ctx.input {
513        InputValue::Object(map) => {
514            let (name, value) = map
515                .iter()
516                .next()
517                .ok_or_else(|| SerializeError::UnsupportedInput("empty union".to_string()))?;
518            let idx = members
519                .iter()
520                .position(|m| m.name.as_deref() == Some(name))
521                .ok_or_else(|| SerializeError::MissingField(name.to_string()))?;
522            (idx, value)
523        }
524        InputValue::Array(items) => {
525            let value = items
526                .first()
527                .ok_or_else(|| SerializeError::UnsupportedInput("empty union".to_string()))?;
528            (0, value)
529        }
530        _ => {
531            return Err(SerializeError::UnexpectedInput(
532                "union expects object with single field".to_string(),
533            ));
534        }
535    };
536    let member = &members[member_idx];
537    let member_type = member.type_ref.ok_or(SerializeError::UnknownType)?;
538    let member_runtime = runtime_member(ctx.runtime_value, member.name.as_deref(), member_idx);
539    let member_bytes = serialize_value_inner(
540        member_input,
541        ctx.type_graph,
542        member_type,
543        member_runtime,
544        &format!("{}{}", ctx.path, member_path_suffix(member, member_idx)),
545        ctx.offsets,
546    )?;
547    let offset = member_offset(member, ctx.runtime_value, member_idx)
548        .ok_or_else(|| SerializeError::MemberOffsetMissing(member_name(member, member_idx)))?;
549    let end = offset + member_bytes.len();
550    if end > buffer.len() {
551        return Err(SerializeError::SizeMismatch {
552            expected: buffer.len(),
553            actual: end,
554        });
555    }
556    buffer[offset..end].copy_from_slice(&member_bytes);
557    ctx.offsets.push(WriteOffset {
558        path: format!("{}{}", ctx.path, member_path_suffix(member, member_idx)),
559        offset,
560        size: member_bytes.len(),
561    });
562    Ok(buffer)
563}
564
565fn serialize_array_value(
566    ctx: &mut SerializeContext<'_>,
567    array: &ArrayType,
568) -> Result<Vec<u8>, SerializeError> {
569    let items = match ctx.input {
570        InputValue::Array(items) => items,
571        _ => {
572            return Err(SerializeError::UnexpectedInput(
573                "array expects list".to_string(),
574            ));
575        }
576    };
577    let element_type = array
578        .element_type()
579        .ok_or(SerializeError::ArrayElementTypeMissing)?;
580    let mut buf = Vec::new();
581    let mut element_size = None;
582    for (idx, item) in items.iter().enumerate() {
583        let runtime_item = runtime_array_item(ctx.runtime_value, idx);
584        let bytes = serialize_value_inner(
585            item,
586            ctx.type_graph,
587            element_type,
588            runtime_item,
589            &format!("{}[{idx}]", ctx.path),
590            ctx.offsets,
591        )?;
592        let size = bytes.len();
593        if let Some(existing) = element_size {
594            if existing != size {
595                return Err(SerializeError::SizeMismatch {
596                    expected: existing,
597                    actual: size,
598                });
599            }
600        } else {
601            element_size = Some(size);
602        }
603        buf.extend_from_slice(&bytes);
604        ctx.offsets.push(WriteOffset {
605            path: format!("{}[{idx}]", ctx.path),
606            offset: idx * size,
607            size,
608        });
609    }
610    if let Some(expected_size) = array.byte_size_hint() {
611        let expected_size = expected_size as usize;
612        if expected_size != buf.len() {
613            let element_size = element_size.unwrap_or(0);
614            let expected_len = if element_size > 0 {
615                expected_size / element_size
616            } else {
617                0
618            };
619            return Err(SerializeError::ArrayLengthMismatch {
620                expected: expected_len,
621                actual: items.len(),
622            });
623        }
624    }
625    Ok(buf)
626}
627
628fn serialize_c_enum_value(
629    input: &InputValue,
630    type_graph: &ComplexType,
631    byte_size: Option<u64>,
632    discr_type: Option<TypeId>,
633    enumerators: &std::collections::HashMap<i64, String>,
634) -> Result<Vec<u8>, SerializeError> {
635    let name_or_value = match input {
636        InputValue::Scalar(s) => s.clone(),
637        InputValue::EnumVariant { name, .. } => name.clone(),
638        _ => {
639            return Err(SerializeError::UnexpectedInput(
640                "enum expects scalar".to_string(),
641            ));
642        }
643    };
644
645    let discr_value = if let Ok(num) = name_or_value.parse::<i64>() {
646        num
647    } else {
648        let (value, _) = enumerators
649            .iter()
650            .find(|(_, name)| *name == &name_or_value)
651            .ok_or_else(|| SerializeError::EnumVariantNotFound(name_or_value.clone()))?;
652        *value
653    };
654
655    if let Some(type_id) = discr_type {
656        let input = InputValue::Scalar(discr_value.to_string());
657        let mut offsets = Vec::new();
658        let bytes = serialize_value_inner(&input, type_graph, type_id, None, "", &mut offsets)?;
659        if let Some(byte_size) = byte_size {
660            let expected = byte_size as usize;
661            if bytes.len() != expected {
662                return Err(SerializeError::SizeMismatch {
663                    expected,
664                    actual: bytes.len(),
665                });
666            }
667        }
668        return Ok(bytes);
669    }
670
671    let size = byte_size.unwrap_or(0) as usize;
672    if size == 0 {
673        return Err(SerializeError::EnumDiscriminantMissing);
674    }
675    int_to_bytes(discr_value as i128, size)
676}
677
678fn serialize_rust_enum_value(
679    ctx: &mut SerializeContext<'_>,
680    byte_size: Option<u64>,
681    discr_member: Option<&StructureMember>,
682    enumerators: &std::collections::HashMap<Option<i64>, StructureMember>,
683) -> Result<Vec<u8>, SerializeError> {
684    let size = byte_size.ok_or(SerializeError::UnknownTypeSize(ctx.type_id))?;
685    let mut buffer = vec![0u8; size as usize];
686
687    let (variant_name, payload) = match ctx.input {
688        InputValue::EnumVariant { name, payload } => (name.clone(), payload.as_deref()),
689        InputValue::Scalar(name) => (name.clone(), None),
690        _ => {
691            return Err(SerializeError::UnexpectedInput(
692                "enum expects variant".to_string(),
693            ));
694        }
695    };
696
697    let (discr_value, variant_member) = enumerators
698        .iter()
699        .find(|(_, member)| member.name.as_deref() == Some(variant_name.as_str()))
700        .map(|(discr, member)| (discr.unwrap_or_default(), member))
701        .ok_or_else(|| SerializeError::EnumVariantNotFound(variant_name.clone()))?;
702
703    if let Some(discr_member) = discr_member {
704        let discr_type = discr_member
705            .type_ref
706            .ok_or(SerializeError::EnumDiscriminantMissing)?;
707        let discr_bytes = serialize_value_inner(
708            &InputValue::Scalar(discr_value.to_string()),
709            ctx.type_graph,
710            discr_type,
711            None,
712            ctx.path,
713            ctx.offsets,
714        )?;
715        let offset = member_offset(discr_member, ctx.runtime_value, 0)
716            .ok_or_else(|| SerializeError::MemberOffsetMissing("discriminant".to_string()))?;
717        let end = offset + discr_bytes.len();
718        if end > buffer.len() {
719            return Err(SerializeError::SizeMismatch {
720                expected: buffer.len(),
721                actual: end,
722            });
723        }
724        buffer[offset..end].copy_from_slice(&discr_bytes);
725        ctx.offsets.push(WriteOffset {
726            path: format!("{}.discriminant", ctx.path),
727            offset,
728            size: discr_bytes.len(),
729        });
730    }
731
732    if let Some(variant_type) = variant_member.type_ref {
733        let payload = payload.ok_or_else(|| SerializeError::MissingField(variant_name.clone()))?;
734        let payload_bytes = serialize_value_inner(
735            payload,
736            ctx.type_graph,
737            variant_type,
738            runtime_member(ctx.runtime_value, variant_member.name.as_deref(), 0),
739            &format!("{}.{}", ctx.path, variant_name),
740            ctx.offsets,
741        )?;
742        let offset = member_offset(variant_member, ctx.runtime_value, 0)
743            .ok_or_else(|| SerializeError::MemberOffsetMissing(format!("enum::{variant_name}")))?;
744        let end = offset + payload_bytes.len();
745        if end > buffer.len() {
746            return Err(SerializeError::SizeMismatch {
747                expected: buffer.len(),
748                actual: end,
749            });
750        }
751        buffer[offset..end].copy_from_slice(&payload_bytes);
752        ctx.offsets.push(WriteOffset {
753            path: format!("{}.{}", ctx.path, variant_name),
754            offset,
755            size: payload_bytes.len(),
756        });
757    }
758
759    Ok(buffer)
760}
761
762fn member_path_suffix(member: &StructureMember, index: usize) -> String {
763    member
764        .name
765        .as_deref()
766        .map(|name| format!(".{name}"))
767        .unwrap_or_else(|| format!("[{index}]"))
768}
769
770fn member_name(member: &StructureMember, index: usize) -> String {
771    member
772        .name
773        .as_deref()
774        .map(|name| name.to_string())
775        .unwrap_or_else(|| format!("[{index}]"))
776}
777
778fn member_offset(
779    member: &StructureMember,
780    runtime_value: Option<&Value>,
781    index: usize,
782) -> Option<usize> {
783    match member.in_struct_location.as_ref()? {
784        MemberLocation::Offset(offset) => (*offset >= 0).then_some(*offset as usize),
785        MemberLocation::Expr(_) => {
786            let base = runtime_value?.in_memory_location()? as isize;
787            let member_value = runtime_member(runtime_value, member.name.as_deref(), index)?;
788            let member_addr = member_value.in_memory_location()? as isize;
789            let offset = member_addr - base;
790            (offset >= 0).then_some(offset as usize)
791        }
792    }
793}
794
795fn runtime_member<'a>(
796    runtime_value: Option<&'a Value>,
797    name: Option<&str>,
798    index: usize,
799) -> Option<&'a Value> {
800    let Value::Struct(structure) = runtime_value? else {
801        return None;
802    };
803    if let Some(name) = name {
804        structure
805            .members
806            .iter()
807            .find(|member| member.field_name.as_deref() == Some(name))
808            .map(|member| &member.value)
809    } else {
810        structure.members.get(index).map(|member| &member.value)
811    }
812}
813
814fn runtime_array_item(runtime_value: Option<&Value>, index: usize) -> Option<&Value> {
815    let Value::Array(array) = runtime_value? else {
816        return None;
817    };
818    array.items.as_ref()?.get(index).map(|item| &item.value)
819}
820
821fn int_to_bytes(value: i128, size: usize) -> Result<Vec<u8>, SerializeError> {
822    let bytes = match size {
823        1 => (value as i8).to_le_bytes().to_vec(),
824        2 => (value as i16).to_le_bytes().to_vec(),
825        4 => (value as i32).to_le_bytes().to_vec(),
826        8 => (value as i64).to_le_bytes().to_vec(),
827        16 => value.to_le_bytes().to_vec(),
828        0 => Vec::new(),
829        _ => {
830            return Err(SerializeError::ScalarParse(format!(
831                "unsupported int size {size}"
832            )));
833        }
834    };
835    Ok(bytes)
836}
837
838fn uint_to_bytes(value: u128, size: usize) -> Result<Vec<u8>, SerializeError> {
839    let bytes = match size {
840        1 => (value as u8).to_le_bytes().to_vec(),
841        2 => (value as u16).to_le_bytes().to_vec(),
842        4 => (value as u32).to_le_bytes().to_vec(),
843        8 => (value as u64).to_le_bytes().to_vec(),
844        16 => value.to_le_bytes().to_vec(),
845        0 => Vec::new(),
846        _ => {
847            return Err(SerializeError::ScalarParse(format!(
848                "unsupported uint size {size}"
849            )));
850        }
851    };
852    Ok(bytes)
853}