use wirespec_codec::ir::*;
use crate::expr::{ExprContext, expr_to_c};
use crate::names::*;
use crate::type_map::*;
struct FieldParseCtx<'a> {
prefix: &'a str,
indent: &'a str,
struct_prefix: &'a str,
expr_ctx: &'a ExprContext,
}
fn needs_signed_cast(wt: &WireType) -> bool {
matches!(
wt,
WireType::I8 | WireType::I16 | WireType::I32 | WireType::I64
)
}
fn unsigned_c_type(wt: &WireType) -> &'static str {
match wt {
WireType::I8 => "uint8_t",
WireType::I16 => "uint16_t",
WireType::I32 => "uint32_t",
WireType::I64 => "uint64_t",
_ => unreachable!(),
}
}
pub fn emit_parse_items(
out: &mut String,
fields: &[CodecField],
items: &[CodecItem],
prefix: &str,
indent: &str,
struct_prefix: &str,
) {
emit_parse_items_with_ctx(
out,
fields,
items,
prefix,
indent,
struct_prefix,
&ExprContext::Parse,
);
}
pub fn emit_parse_items_with_ctx(
out: &mut String,
fields: &[CodecField],
items: &[CodecItem],
prefix: &str,
indent: &str,
struct_prefix: &str,
expr_ctx: &ExprContext,
) {
let mut emitted_bitgroups: Vec<u32> = Vec::new();
let ctx = FieldParseCtx {
prefix,
indent,
struct_prefix,
expr_ctx,
};
for item in items {
match item {
CodecItem::Field { field_id } => {
if let Some(f) = fields.iter().find(|f| &f.field_id == field_id) {
emit_field_parse_with_ctx(out, f, fields, &ctx, &mut emitted_bitgroups);
}
}
CodecItem::Derived(d) => {
let expr_str = expr_to_c(&d.expr, expr_ctx);
out.push_str(&format!(
"{indent}{struct_prefix}{} = {expr_str};\n",
d.name
));
}
CodecItem::Require(r) => {
let expr_str = expr_to_c(&r.expr, expr_ctx);
out.push_str(&format!(
"{indent}if (!({expr_str})) return WIRESPEC_ERR_CONSTRAINT;\n"
));
}
}
}
}
fn emit_field_parse(
out: &mut String,
f: &CodecField,
all_fields: &[CodecField],
prefix: &str,
indent: &str,
struct_prefix: &str,
emitted_bitgroups: &mut Vec<u32>,
) {
let ctx = FieldParseCtx {
prefix,
indent,
struct_prefix,
expr_ctx: &ExprContext::Parse,
};
emit_field_parse_with_ctx(out, f, all_fields, &ctx, emitted_bitgroups);
}
fn emit_field_parse_with_ctx(
out: &mut String,
f: &CodecField,
all_fields: &[CodecField],
ctx: &FieldParseCtx<'_>,
emitted_bitgroups: &mut Vec<u32>,
) {
let prefix = ctx.prefix;
let indent = ctx.indent;
let struct_prefix = ctx.struct_prefix;
let expr_ctx = ctx.expr_ctx;
match f.strategy {
FieldStrategy::Primitive | FieldStrategy::Checksum => {
let read_fn = cursor_read_fn(&f.wire_type, f.endianness);
if needs_signed_cast(&f.wire_type) {
let utype = unsigned_c_type(&f.wire_type);
out.push_str(&format!(
"{indent}r = {read_fn}(cur, ({utype} *)&{struct_prefix}{});\n",
f.name
));
} else {
out.push_str(&format!(
"{indent}r = {read_fn}(cur, &{struct_prefix}{});\n",
f.name
));
}
out.push_str(&format!("{indent}if (r != WIRESPEC_OK) return r;\n"));
}
FieldStrategy::BitGroup => {
if let Some(ref bg) = f.bitgroup_member
&& !emitted_bitgroups.contains(&bg.group_id)
{
emitted_bitgroups.push(bg.group_id);
emit_bitgroup_parse(out, f, all_fields, prefix, indent, struct_prefix, bg);
}
}
FieldStrategy::BytesFixed => {
if let Some(BytesSpec::Fixed { size }) = &f.bytes_spec {
out.push_str(&format!(
"{indent}r = wirespec_cursor_read_bytes(cur, {size}, &{struct_prefix}{});\n",
f.name
));
out.push_str(&format!("{indent}if (r != WIRESPEC_OK) return r;\n"));
}
}
FieldStrategy::BytesLength => {
if let Some(BytesSpec::Length { ref expr }) = f.bytes_spec {
let len_expr = expr_to_c(expr, expr_ctx);
out.push_str(&format!(
"{indent}r = wirespec_cursor_read_bytes(cur, (size_t){len_expr}, &{struct_prefix}{});\n",
f.name
));
out.push_str(&format!("{indent}if (r != WIRESPEC_OK) return r;\n"));
}
}
FieldStrategy::BytesRemaining => {
out.push_str(&format!(
"{indent}{struct_prefix}{}.ptr = cur->base + cur->pos;\n",
f.name
));
out.push_str(&format!(
"{indent}{struct_prefix}{}.len = wirespec_cursor_remaining(cur);\n",
f.name
));
out.push_str(&format!("{indent}cur->pos = cur->len;\n"));
}
FieldStrategy::BytesLor => {
if let Some(BytesSpec::LengthOrRemaining { ref expr }) = f.bytes_spec {
let len_expr = expr_to_c(expr, expr_ctx);
let value_id = get_value_ref_id(expr);
let field_name = crate::expr::extract_field_name(&value_id);
out.push_str(&format!(
"{indent}if ({struct_prefix}has_{field_name}) {{\n"
));
out.push_str(&format!(
"{indent} r = wirespec_cursor_read_bytes(cur, (size_t){len_expr}, &{struct_prefix}{});\n",
f.name
));
out.push_str(&format!("{indent} if (r != WIRESPEC_OK) return r;\n"));
out.push_str(&format!("{indent}}} else {{\n"));
out.push_str(&format!(
"{indent} {struct_prefix}{}.ptr = cur->base + cur->pos;\n",
f.name
));
out.push_str(&format!(
"{indent} {struct_prefix}{}.len = wirespec_cursor_remaining(cur);\n",
f.name
));
out.push_str(&format!("{indent} cur->pos = cur->len;\n"));
out.push_str(&format!("{indent}}}\n"));
}
}
FieldStrategy::Conditional => {
if let Some(ref cond) = f.condition {
let cond_str = expr_to_c(cond, expr_ctx);
out.push_str(&format!("{indent}{struct_prefix}has_{} = false;\n", f.name));
out.push_str(&format!("{indent}if ({cond_str}) {{\n"));
let inner_wt = f.inner_wire_type.as_ref().unwrap_or(&f.wire_type);
if let Some(ref ref_name) = f.ref_type_name
&& matches!(
inner_wt,
WireType::Struct(_) | WireType::VarInt | WireType::ContVarInt
)
{
let parse_fn = c_func_name(prefix, ref_name, "parse_cursor");
out.push_str(&format!(
"{indent} r = {parse_fn}(cur, &{struct_prefix}{});\n",
f.name
));
} else {
let read_fn = cursor_read_fn(inner_wt, f.endianness);
out.push_str(&format!(
"{indent} r = {read_fn}(cur, &{struct_prefix}{});\n",
f.name
));
}
out.push_str(&format!("{indent} if (r != WIRESPEC_OK) return r;\n"));
out.push_str(&format!(
"{indent} {struct_prefix}has_{} = true;\n",
f.name
));
out.push_str(&format!("{indent}}}\n"));
}
}
FieldStrategy::Array => {
if let Some(ref arr) = f.array_spec {
emit_array_parse_with_ctx(out, f, arr, prefix, indent, struct_prefix, expr_ctx);
}
}
FieldStrategy::Struct => {
if let Some(ref ref_name) = f.ref_type_name {
let parse_fn = c_func_name(prefix, ref_name, "parse_cursor");
out.push_str(&format!(
"{indent}r = {parse_fn}(cur, &{struct_prefix}{});\n",
f.name
));
out.push_str(&format!("{indent}if (r != WIRESPEC_OK) return r;\n"));
}
}
FieldStrategy::VarInt | FieldStrategy::ContVarInt => {
if let Some(ref ref_name) = f.ref_type_name {
let parse_fn = c_func_name(prefix, ref_name, "parse_cursor");
out.push_str(&format!(
"{indent}r = {parse_fn}(cur, &{struct_prefix}{});\n",
f.name
));
out.push_str(&format!("{indent}if (r != WIRESPEC_OK) return r;\n"));
}
}
}
}
fn emit_bitgroup_parse(
out: &mut String,
_first_field: &CodecField,
all_fields: &[CodecField],
_prefix: &str,
indent: &str,
struct_prefix: &str,
bg: &BitgroupMember,
) {
let group_id = bg.group_id;
let total_bits = bg.total_bits;
let container_type = bitgroup_c_type(total_bits);
let read_fn = bitgroup_read_fn(total_bits, bg.group_endianness);
let var_name = format!("_bg{group_id}");
out.push_str(&format!("{indent}{{\n"));
out.push_str(&format!("{indent} {container_type} {var_name};\n"));
out.push_str(&format!("{indent} r = {read_fn}(cur, &{var_name});\n"));
out.push_str(&format!("{indent} if (r != WIRESPEC_OK) return r;\n"));
for f in all_fields {
if let Some(ref mbg) = f.bitgroup_member
&& mbg.group_id == group_id
{
let mask = if mbg.member_width_bits >= 64 {
u64::MAX
} else {
(1u64 << mbg.member_width_bits) - 1
};
let shift = mbg.member_offset_bits;
let ctype = wire_type_to_c(&f.wire_type, "");
let cast_type = if ctype.contains("int") {
&ctype
} else {
"uint8_t"
};
out.push_str(&format!(
"{indent} {struct_prefix}{} = ({cast_type})(({var_name} >> {shift}) & 0x{mask:x});\n",
f.name
));
}
}
out.push_str(&format!("{indent}}}\n"));
}
fn emit_array_parse_with_ctx(
out: &mut String,
f: &CodecField,
arr: &ArraySpec,
prefix: &str,
indent: &str,
struct_prefix: &str,
expr_ctx: &ExprContext,
) {
if let Some(ref count_expr) = arr.count_expr {
let count_str = expr_to_c(count_expr, expr_ctx);
let max_elems = f.max_elements.unwrap_or(256);
out.push_str(&format!(
"{indent}{struct_prefix}{}_count = (uint32_t){count_str};\n",
f.name
));
out.push_str(&format!(
"{indent}if ({struct_prefix}{}_count > {max_elems}) return WIRESPEC_ERR_CAPACITY;\n",
f.name
));
out.push_str(&format!(
"{indent}for (uint32_t _i = 0; _i < {struct_prefix}{}_count; _i++) {{\n",
f.name
));
match arr.element_strategy {
FieldStrategy::Primitive => {
let read_fn = cursor_read_fn(&arr.element_wire_type, None);
out.push_str(&format!(
"{indent} r = {read_fn}(cur, &{struct_prefix}{}[_i]);\n",
f.name
));
out.push_str(&format!("{indent} if (r != WIRESPEC_OK) return r;\n"));
}
FieldStrategy::Struct => {
if let Some(ref ref_name) = arr.element_ref_type_name {
let parse_fn = c_func_name(prefix, ref_name, "parse_cursor");
out.push_str(&format!(
"{indent} r = {parse_fn}(cur, &{struct_prefix}{}[_i]);\n",
f.name
));
out.push_str(&format!("{indent} if (r != WIRESPEC_OK) return r;\n"));
}
}
FieldStrategy::VarInt | FieldStrategy::ContVarInt => {
if let Some(ref ref_name) = arr.element_ref_type_name {
let parse_fn = c_func_name(prefix, ref_name, "parse_cursor");
out.push_str(&format!(
"{indent} r = {parse_fn}(cur, &{struct_prefix}{}[_i]);\n",
f.name
));
out.push_str(&format!("{indent} if (r != WIRESPEC_OK) return r;\n"));
}
}
_ => unreachable!(
"unexpected array element strategy: {:?}",
arr.element_strategy
),
}
out.push_str(&format!("{indent}}}\n"));
} else {
let max_elems = f.max_elements.unwrap_or(256);
let cur_var = if let Some(ref within_expr) = arr.within_expr {
let within_c = expr_to_c(within_expr, expr_ctx);
out.push_str(&format!("{indent}{{\n"));
out.push_str(&format!("{indent} wirespec_cursor_t _arr_sub;\n"));
out.push_str(&format!(
"{indent} r = wirespec_cursor_sub(cur, (size_t){within_c}, &_arr_sub);\n"
));
out.push_str(&format!("{indent} if (r != WIRESPEC_OK) return r;\n"));
"&_arr_sub".to_string()
} else {
"cur".to_string()
};
out.push_str(&format!(
"{indent} {struct_prefix}{}_count = 0;\n",
f.name
));
out.push_str(&format!(
"{indent} while (wirespec_cursor_remaining({cur_var}) > 0) {{\n",
));
out.push_str(&format!(
"{indent} if ({struct_prefix}{}_count >= {max_elems}) return WIRESPEC_ERR_CAPACITY;\n",
f.name
));
match arr.element_strategy {
FieldStrategy::Primitive => {
let read_fn = cursor_read_fn(&arr.element_wire_type, None);
out.push_str(&format!(
"{indent} r = {read_fn}({cur_var}, &{struct_prefix}{}[{struct_prefix}{}_count]);\n",
f.name, f.name
));
out.push_str(&format!(
"{indent} if (r != WIRESPEC_OK) return r;\n"
));
}
FieldStrategy::Struct => {
if let Some(ref ref_name) = arr.element_ref_type_name {
let parse_fn = c_func_name(prefix, ref_name, "parse_cursor");
out.push_str(&format!(
"{indent} r = {parse_fn}({cur_var}, &{struct_prefix}{}[{struct_prefix}{}_count]);\n",
f.name, f.name
));
out.push_str(&format!(
"{indent} if (r != WIRESPEC_OK) return r;\n"
));
}
}
FieldStrategy::VarInt | FieldStrategy::ContVarInt => {
if let Some(ref ref_name) = arr.element_ref_type_name {
let parse_fn = c_func_name(prefix, ref_name, "parse_cursor");
out.push_str(&format!(
"{indent} r = {parse_fn}({cur_var}, &{struct_prefix}{}[{struct_prefix}{}_count]);\n",
f.name, f.name
));
out.push_str(&format!(
"{indent} if (r != WIRESPEC_OK) return r;\n"
));
}
}
_ => unreachable!(
"unexpected fill array element strategy: {:?}",
arr.element_strategy
),
}
out.push_str(&format!(
"{indent} {struct_prefix}{}_count++;\n",
f.name
));
out.push_str(&format!("{indent} }}\n"));
if arr.within_expr.is_some() {
out.push_str(&format!("{indent}}}\n"));
}
}
}
fn get_value_ref_id(expr: &CodecExpr) -> String {
match expr {
CodecExpr::ValueRef { reference } => reference.value_id.clone(),
_ => String::new(),
}
}
pub fn emit_frame_parse_body(out: &mut String, frame: &CodecFrame, prefix: &str, indent: &str) {
let struct_prefix = "out->";
match &frame.tag.wire_type {
WireType::VarInt | WireType::ContVarInt => {
if let Some(ref ref_name) = frame.tag.ref_type_name {
let parse_fn = c_func_name(prefix, ref_name, "parse_cursor");
out.push_str(&format!("{indent}uint64_t _tag_val;\n"));
out.push_str(&format!("{indent}r = {parse_fn}(cur, &_tag_val);\n"));
out.push_str(&format!("{indent}if (r != WIRESPEC_OK) return r;\n"));
} else {
out.push_str(&format!("{indent}uint64_t _tag_val;\n"));
out.push_str(&format!(
"{indent}r = wirespec_cursor_read_u8(cur, (uint8_t *)&_tag_val);\n"
));
out.push_str(&format!("{indent}if (r != WIRESPEC_OK) return r;\n"));
}
}
_ => {
let tag_read_fn = cursor_read_fn(&frame.tag.wire_type, frame.tag.endianness);
let tag_ctype = wire_type_to_c(&frame.tag.wire_type, prefix);
out.push_str(&format!("{indent}{tag_ctype} _tag_val;\n"));
out.push_str(&format!("{indent}r = {tag_read_fn}(cur, &_tag_val);\n"));
out.push_str(&format!("{indent}if (r != WIRESPEC_OK) return r;\n"));
}
}
out.push_str(&format!("{indent}{struct_prefix}frame_type = _tag_val;\n"));
let mut large_range_deferred: Vec<(i64, i64, String)> = Vec::new();
let mut wildcard_variant: Option<&CodecVariantScope> = None;
for variant in &frame.variants {
if matches!(variant.pattern, VariantPattern::Wildcard) {
wildcard_variant = Some(variant);
}
}
out.push_str(&format!("{indent}switch (_tag_val) {{\n"));
for variant in &frame.variants {
if matches!(variant.pattern, VariantPattern::Wildcard) {
continue;
}
if let Some(deferred) =
emit_variant_case(out, variant, frame, prefix, indent, struct_prefix)
{
large_range_deferred.push(deferred);
}
}
out.push_str(&format!("{indent} default:\n"));
let def_indent = format!("{indent} ");
for (lo, hi, body) in large_range_deferred {
out.push_str(&format!(
"{def_indent}if (_tag_val >= {lo} && _tag_val <= {hi}) {{\n"
));
out.push_str(&body);
out.push_str(&format!("{def_indent}}}\n"));
}
if let Some(wv) = wildcard_variant {
emit_variant_case_body(out, wv, frame, prefix, &def_indent, struct_prefix);
} else {
out.push_str(&format!("{def_indent}return WIRESPEC_ERR_INVALID_TAG;\n"));
}
out.push_str(&format!("{indent}}}\n"));
}
fn emit_variant_case_body(
out: &mut String,
variant: &CodecVariantScope,
frame: &CodecFrame,
prefix: &str,
inner_indent: &str,
struct_prefix: &str,
) {
let tag_val = c_frame_tag_value(prefix, &frame.name, &variant.name);
let vname = to_snake_case(&variant.name);
let variant_prefix = format!("{struct_prefix}value.{vname}.");
let header_field_names = vec![frame.tag.field_name.clone()];
let expr_ctx = ExprContext::CapsuleVariantParse {
variant_prefix: variant_prefix.clone(),
header_field_names,
};
out.push_str(&format!("{inner_indent}{struct_prefix}tag = {tag_val};\n"));
emit_parse_items_with_ctx(
out,
&variant.fields,
&variant.items,
prefix,
inner_indent,
&variant_prefix,
&expr_ctx,
);
out.push_str(&format!("{inner_indent}break;\n"));
}
fn emit_variant_case(
out: &mut String,
variant: &CodecVariantScope,
frame: &CodecFrame,
prefix: &str,
indent: &str,
struct_prefix: &str,
) -> Option<(i64, i64, String)> {
let inner_indent = format!("{indent} ");
match &variant.pattern {
VariantPattern::Exact { value } => {
out.push_str(&format!("{indent} case {value}:\n"));
emit_variant_case_body(out, variant, frame, prefix, &inner_indent, struct_prefix);
None
}
VariantPattern::RangeInclusive { start, end } => {
let count = end.saturating_sub(*start).saturating_add(1);
if count > 4096 {
let mut body = String::new();
emit_variant_case_body(
&mut body,
variant,
frame,
prefix,
&inner_indent,
struct_prefix,
);
Some((*start, *end, body))
} else {
for v in *start..=*end {
out.push_str(&format!("{indent} case {v}:\n"));
}
emit_variant_case_body(out, variant, frame, prefix, &inner_indent, struct_prefix);
None
}
}
VariantPattern::Wildcard => {
None
}
}
}
pub fn emit_capsule_parse_body(
out: &mut String,
capsule: &CodecCapsule,
prefix: &str,
indent: &str,
) {
let struct_prefix = "out->";
let mut emitted_bitgroups: Vec<u32> = Vec::new();
for item in &capsule.header_items {
match item {
CodecItem::Field { field_id } => {
if let Some(f) = capsule
.header_fields
.iter()
.find(|f| &f.field_id == field_id)
{
emit_field_parse(
out,
f,
&capsule.header_fields,
prefix,
indent,
struct_prefix,
&mut emitted_bitgroups,
);
}
}
CodecItem::Derived(d) => {
let expr_str = expr_to_c(&d.expr, &ExprContext::Parse);
out.push_str(&format!(
"{indent}{struct_prefix}{} = {expr_str};\n",
d.name
));
}
CodecItem::Require(r) => {
let expr_str = expr_to_c(&r.expr, &ExprContext::Parse);
out.push_str(&format!(
"{indent}if (!({expr_str})) return WIRESPEC_ERR_CONSTRAINT;\n"
));
}
}
}
let within_expr = format!("(size_t)({struct_prefix}{})", capsule.within_field);
out.push_str(&format!("{indent}wirespec_cursor_t sub;\n"));
out.push_str(&format!(
"{indent}r = wirespec_cursor_sub(cur, {within_expr}, &sub);\n"
));
out.push_str(&format!("{indent}if (r != WIRESPEC_OK) return r;\n"));
let tag_switch_expr = if let Some(ref expr) = capsule.tag_expr {
expr_to_c(expr, &ExprContext::Parse)
} else {
format!("{struct_prefix}{}", capsule.tag.field_name)
};
let mut large_range_deferred: Vec<(i64, i64, String)> = Vec::new();
let mut wildcard_variant: Option<&CodecVariantScope> = None;
for variant in &capsule.variants {
if matches!(variant.pattern, VariantPattern::Wildcard) {
wildcard_variant = Some(variant);
}
}
out.push_str(&format!("{indent}switch ({tag_switch_expr}) {{\n"));
for variant in &capsule.variants {
if matches!(variant.pattern, VariantPattern::Wildcard) {
continue;
}
if let Some(deferred) =
emit_capsule_variant_case(out, variant, capsule, prefix, indent, struct_prefix)
{
large_range_deferred.push(deferred);
}
}
out.push_str(&format!("{indent} default:\n"));
let def_indent = format!("{indent} ");
for (lo, hi, body) in large_range_deferred {
out.push_str(&format!(
"{def_indent}if ({tag_switch_expr} >= {lo} && {tag_switch_expr} <= {hi}) {{\n"
));
out.push_str(&body);
out.push_str(&format!("{def_indent}}}\n"));
}
if let Some(wv) = wildcard_variant {
emit_capsule_variant_case_body(out, wv, capsule, prefix, &def_indent, struct_prefix);
} else {
out.push_str(&format!("{def_indent}return WIRESPEC_ERR_INVALID_TAG;\n"));
}
out.push_str(&format!("{indent}}}\n"));
out.push_str(&format!(
"{indent}if (wirespec_cursor_remaining(&sub) != 0) return WIRESPEC_ERR_TRAILING_DATA;\n"
));
}
fn emit_capsule_variant_case_body(
out: &mut String,
variant: &CodecVariantScope,
capsule: &CodecCapsule,
prefix: &str,
inner_indent: &str,
struct_prefix: &str,
) {
let tag_val = c_frame_tag_value(prefix, &capsule.name, &variant.name);
let vname = to_snake_case(&variant.name);
let variant_prefix = format!("{struct_prefix}value.{vname}.");
let header_field_names: Vec<String> = capsule
.header_fields
.iter()
.map(|f| f.name.clone())
.collect();
let expr_ctx = ExprContext::CapsuleVariantParse {
variant_prefix: variant_prefix.clone(),
header_field_names,
};
out.push_str(&format!("{inner_indent}{struct_prefix}tag = {tag_val};\n"));
for item in &variant.items {
match item {
CodecItem::Field { field_id } => {
if let Some(f) = variant.fields.iter().find(|f| &f.field_id == field_id) {
emit_capsule_field_parse(
out,
f,
&variant.fields,
prefix,
inner_indent,
&variant_prefix,
&expr_ctx,
);
}
}
CodecItem::Derived(d) => {
let expr_str = expr_to_c(&d.expr, &expr_ctx);
out.push_str(&format!(
"{inner_indent}{variant_prefix}{} = {expr_str};\n",
d.name
));
}
CodecItem::Require(r) => {
let expr_str = expr_to_c(&r.expr, &expr_ctx);
out.push_str(&format!(
"{inner_indent}if (!({expr_str})) return WIRESPEC_ERR_CONSTRAINT;\n"
));
}
}
}
out.push_str(&format!("{inner_indent}break;\n"));
}
fn emit_capsule_variant_case(
out: &mut String,
variant: &CodecVariantScope,
capsule: &CodecCapsule,
prefix: &str,
indent: &str,
struct_prefix: &str,
) -> Option<(i64, i64, String)> {
let inner_indent = format!("{indent} ");
match &variant.pattern {
VariantPattern::Exact { value } => {
out.push_str(&format!("{indent} case {value}:\n"));
emit_capsule_variant_case_body(
out,
variant,
capsule,
prefix,
&inner_indent,
struct_prefix,
);
None
}
VariantPattern::RangeInclusive { start, end } => {
let count = end.saturating_sub(*start).saturating_add(1);
if count > 4096 {
let mut body = String::new();
emit_capsule_variant_case_body(
&mut body,
variant,
capsule,
prefix,
&inner_indent,
struct_prefix,
);
Some((*start, *end, body))
} else {
for v in *start..=*end {
out.push_str(&format!("{indent} case {v}:\n"));
}
emit_capsule_variant_case_body(
out,
variant,
capsule,
prefix,
&inner_indent,
struct_prefix,
);
None
}
}
VariantPattern::Wildcard => {
None
}
}
}
fn emit_capsule_field_parse(
out: &mut String,
f: &CodecField,
all_fields: &[CodecField],
prefix: &str,
indent: &str,
struct_prefix: &str,
expr_ctx: &ExprContext,
) {
match f.strategy {
FieldStrategy::Primitive | FieldStrategy::Checksum => {
let read_fn = cursor_read_fn(&f.wire_type, f.endianness);
if needs_signed_cast(&f.wire_type) {
let utype = unsigned_c_type(&f.wire_type);
out.push_str(&format!(
"{indent}r = {read_fn}(&sub, ({utype} *)&{struct_prefix}{});\n",
f.name
));
} else {
out.push_str(&format!(
"{indent}r = {read_fn}(&sub, &{struct_prefix}{});\n",
f.name
));
}
out.push_str(&format!("{indent}if (r != WIRESPEC_OK) return r;\n"));
}
FieldStrategy::Struct => {
if let Some(ref ref_name) = f.ref_type_name {
let parse_fn = c_func_name(prefix, ref_name, "parse_cursor");
out.push_str(&format!(
"{indent}r = {parse_fn}(&sub, &{struct_prefix}{});\n",
f.name
));
out.push_str(&format!("{indent}if (r != WIRESPEC_OK) return r;\n"));
}
}
FieldStrategy::VarInt | FieldStrategy::ContVarInt => {
if let Some(ref ref_name) = f.ref_type_name {
let parse_fn = c_func_name(prefix, ref_name, "parse_cursor");
out.push_str(&format!(
"{indent}r = {parse_fn}(&sub, &{struct_prefix}{});\n",
f.name
));
out.push_str(&format!("{indent}if (r != WIRESPEC_OK) return r;\n"));
}
}
FieldStrategy::Conditional => {
if let Some(ref cond) = f.condition {
let cond_str = expr_to_c(cond, expr_ctx);
out.push_str(&format!("{indent}{struct_prefix}has_{} = false;\n", f.name));
out.push_str(&format!("{indent}if ({cond_str}) {{\n"));
let inner_wt = f.inner_wire_type.as_ref().unwrap_or(&f.wire_type);
if let Some(ref ref_name) = f.ref_type_name
&& matches!(inner_wt, WireType::Struct(_))
{
let parse_fn = c_func_name(prefix, ref_name, "parse_cursor");
out.push_str(&format!(
"{indent} r = {parse_fn}(&sub, &{struct_prefix}{});\n",
f.name
));
} else {
let read_fn = cursor_read_fn(inner_wt, f.endianness);
out.push_str(&format!(
"{indent} r = {read_fn}(&sub, &{struct_prefix}{});\n",
f.name
));
}
out.push_str(&format!("{indent} if (r != WIRESPEC_OK) return r;\n"));
out.push_str(&format!(
"{indent} {struct_prefix}has_{} = true;\n",
f.name
));
out.push_str(&format!("{indent}}}\n"));
}
}
FieldStrategy::BytesRemaining => {
out.push_str(&format!(
"{indent}{struct_prefix}{}.ptr = sub.base + sub.pos;\n",
f.name
));
out.push_str(&format!(
"{indent}{struct_prefix}{}.len = wirespec_cursor_remaining(&sub);\n",
f.name
));
out.push_str(&format!("{indent}sub.pos = sub.len;\n"));
}
FieldStrategy::BytesFixed => {
if let Some(BytesSpec::Fixed { size }) = &f.bytes_spec {
out.push_str(&format!(
"{indent}r = wirespec_cursor_read_bytes(&sub, {size}, &{struct_prefix}{});\n",
f.name
));
out.push_str(&format!("{indent}if (r != WIRESPEC_OK) return r;\n"));
}
}
FieldStrategy::BytesLength => {
if let Some(BytesSpec::Length { ref expr }) = f.bytes_spec {
let len_expr = expr_to_c(expr, expr_ctx);
out.push_str(&format!(
"{indent}r = wirespec_cursor_read_bytes(&sub, (size_t){len_expr}, &{struct_prefix}{});\n",
f.name
));
out.push_str(&format!("{indent}if (r != WIRESPEC_OK) return r;\n"));
}
}
_ => {
let mut tmp = String::new();
let mut dummy_bg = Vec::new();
let ctx = FieldParseCtx {
prefix,
indent,
struct_prefix,
expr_ctx,
};
emit_field_parse_with_ctx(&mut tmp, f, all_fields, &ctx, &mut dummy_bg);
let tmp = tmp.replace("(cur, ", "(&sub, ");
let tmp = tmp.replace("cur->", "sub.");
out.push_str(&tmp);
}
}
}