1use serde_json::{Map, Value};
8
9#[derive(Clone, Debug, PartialEq)]
10pub enum CField {
11 Int { bytes: usize, signed: bool },
12 Float { bytes: usize },
13 Pointer,
14 Array(Box<CField>, usize),
15 Struct(CStruct),
16}
17
18#[derive(Clone, Debug, PartialEq)]
19pub struct CStruct {
20 pub fields: Vec<(String, CField)>,
21 pub is_union: bool,
23}
24
25const POINTER_BYTES: usize = 8;
26const SCALARS: [(&str, CField); 11] = [
27 ("i8", CField::Int { bytes: 1, signed: true }),
28 ("u8", CField::Int { bytes: 1, signed: false }),
29 ("i16", CField::Int { bytes: 2, signed: true }),
30 ("u16", CField::Int { bytes: 2, signed: false }),
31 ("i32", CField::Int { bytes: 4, signed: true }),
32 ("u32", CField::Int { bytes: 4, signed: false }),
33 ("i64", CField::Int { bytes: 8, signed: true }),
34 ("u64", CField::Int { bytes: 8, signed: false }),
35 ("f32", CField::Float { bytes: 4 }),
36 ("f64", CField::Float { bytes: 8 }),
37 ("ptr", CField::Pointer),
38];
39
40impl CField {
41 pub fn size(&self) -> usize {
42 match self {
43 CField::Int { bytes, .. } | CField::Float { bytes } => *bytes,
44 CField::Pointer => POINTER_BYTES,
45 CField::Array(item, count) => item.size() * count,
46 CField::Struct(inner) => inner.size(),
47 }
48 }
49
50 pub fn align(&self) -> usize {
51 match self {
52 CField::Array(item, _) => item.align(),
53 CField::Struct(inner) => inner.align(),
54 scalar => scalar.size(),
55 }
56 }
57
58 pub fn text(&self) -> String {
60 match self {
61 CField::Array(item, count) => format!("[{};{count}]", item.text()),
62 CField::Struct(inner) => inner.text(),
63 scalar => SCALARS.iter().find(|(_, known)| known == scalar).map_or("?", |(name, _)| name).to_string(),
64 }
65 }
66
67 fn read(&self, bytes: &[u8], pointer_id: &mut dyn FnMut(usize) -> i32) -> Value {
68 let integer = |count: usize| {
69 let mut buffer = [0u8; 8];
70 buffer[..count].copy_from_slice(&bytes[..count]);
71 u64::from_le_bytes(buffer)
72 };
73 match self {
74 CField::Int { bytes: count, signed: true } => {
75 let shift = 64 - 8 * count;
76 Value::from(((integer(*count) << shift) as i64) >> shift)
77 }
78 CField::Int { bytes: count, signed: false } => Value::from(integer(*count)),
79 CField::Float { bytes: 4 } => Value::from(f32::from_bits(integer(4) as u32) as f64),
80 CField::Float { .. } => Value::from(f64::from_bits(integer(8))),
81 CField::Pointer => Value::from(pointer_id(integer(POINTER_BYTES) as usize)),
82 CField::Array(item, count) => {
83 Value::Array((0..*count).map(|index| item.read(&bytes[index * item.size()..], pointer_id)).collect())
84 }
85 CField::Struct(inner) => inner.read(bytes, pointer_id),
86 }
87 }
88
89 fn write(
90 &self,
91 value: &Value,
92 bytes: &mut [u8],
93 pointer_of: &mut dyn FnMut(i32) -> Option<usize>,
94 ) -> Result<(), String> {
95 let mut put = |number: u64, count: usize| bytes[..count].copy_from_slice(&number.to_le_bytes()[..count]);
96 match self {
97 CField::Int { bytes: count, .. } => put(number_of(value)? as i64 as u64, *count),
98 CField::Float { bytes: 4 } => put((number_of(value)? as f32).to_bits() as u64, 4),
99 CField::Float { .. } => put(number_of(value)?.to_bits(), 8),
100 CField::Pointer => {
101 let id = number_of(value)? as i32;
102 put(pointer_of(id).ok_or(format!("{id} is no C handle of this run"))? as u64, POINTER_BYTES)
103 }
104 CField::Array(item, count) => {
105 let items = value.as_array().ok_or(format!("a list for a C array, not {value}"))?;
106 for (index, item_value) in items.iter().take(*count).enumerate() {
107 item.write(item_value, &mut bytes[index * item.size()..], pointer_of)?;
108 }
109 }
110 CField::Struct(inner) => inner.write(value, bytes, pointer_of)?,
111 }
112 Ok(())
113 }
114}
115
116fn number_of(value: &Value) -> Result<f64, String> {
117 match value {
118 Value::Bool(truth) => Ok(f64::from(u8::from(*truth))),
119 Value::Number(number) => {
120 number.as_i64().map(|integer| integer as f64).or(number.as_f64()).ok_or(number.to_string())
121 }
122 other => Err(format!("a number for a C field, not {other}")),
123 }
124}
125
126impl CStruct {
127 fn placed(&self) -> Vec<(usize, &String, &CField)> {
129 let mut offset: usize = 0;
130 self.fields
131 .iter()
132 .map(|(name, field)| {
133 if self.is_union {
134 return (0, name, field);
135 }
136 offset = offset.next_multiple_of(field.align());
137 let placed = (offset, name, field);
138 offset += field.size();
139 placed
140 })
141 .collect()
142 }
143
144 pub fn align(&self) -> usize {
145 self.fields.iter().map(|(_, field)| field.align()).max().unwrap_or(1)
146 }
147
148 pub fn size(&self) -> usize {
149 let end = self.placed().iter().map(|(offset, _, field)| offset + field.size()).max().unwrap_or(0);
150 end.next_multiple_of(self.align())
151 }
152
153 pub fn text(&self) -> String {
154 let fields: Vec<String> = self.fields.iter().map(|(name, field)| format!("{name}:{}", field.text())).collect();
155 let (open, close) = if self.is_union { ('<', '>') } else { ('{', '}') };
156 format!("{open}{}{close}", fields.join(","))
157 }
158
159 pub fn of_text(text: &str) -> Option<CStruct> {
161 match parse_field(text)? {
162 (CField::Struct(layout), "") => Some(layout),
163 _ => None,
164 }
165 }
166
167 pub fn read(&self, bytes: &[u8], pointer_id: &mut dyn FnMut(usize) -> i32) -> Value {
169 let mut entries = Map::new();
170 for (offset, name, field) in self.placed() {
171 match field.read(&bytes[offset..], pointer_id) {
172 Value::Object(inner) if name.is_empty() => entries.extend(inner),
174 value => {
175 entries.insert(name.clone(), value);
176 }
177 }
178 }
179 Value::Object(entries)
180 }
181
182 pub fn write(
184 &self,
185 value: &Value,
186 bytes: &mut [u8],
187 pointer_of: &mut dyn FnMut(i32) -> Option<usize>,
188 ) -> Result<(), String> {
189 let entries = value.as_object().ok_or(format!("a map for a C struct, not {value}"))?;
190 for (offset, name, field) in self.placed() {
191 if name.is_empty() {
192 field.write(value, &mut bytes[offset..], pointer_of)?;
193 } else if let Some(field_value) = entries.get(name) {
194 field.write(field_value, &mut bytes[offset..], pointer_of)?;
195 }
196 }
197 Ok(())
198 }
199}
200
201fn parse_field(text: &str) -> Option<(CField, &str)> {
203 if let Some(rest) = text.strip_prefix('[') {
204 let (item, rest) = parse_field(rest)?;
205 let (count, rest) = rest.strip_prefix(';')?.split_once(']')?;
206 return Some((CField::Array(Box::new(item), count.parse().ok()?), rest));
207 }
208 let close = match text.chars().next()? {
209 '{' => '}',
210 '<' => '>',
211 _ => {
212 let end = text.find([',', '}', '>', ';', ']']).unwrap_or(text.len());
213 let (_, scalar) = SCALARS.iter().find(|(name, _)| *name == &text[..end])?;
214 return Some((scalar.clone(), &text[end..]));
215 }
216 };
217 let mut fields = vec![];
218 let mut rest = &text[1..];
219 while !rest.starts_with(close) {
220 let (name, after) = rest.split_once(':')?;
221 let (field, after) = parse_field(after)?;
222 fields.push((name.to_string(), field));
223 rest = after.strip_prefix(',').unwrap_or(after);
224 }
225 Some((CField::Struct(CStruct { fields, is_union: close == '>' }), &rest[1..]))
226}