1use sim_kernel::{Cx, Lib, LibLoader, LibSource, Result};
2
3use crate::loaders::{
4 reexport::{ReexportKind, ReexportLib, ReexportSpec},
5 shared::{expr_kind, parse_symbol_text},
6};
7
8const BINARY_PACK_MAGIC: &[u8; 4] = b"L8PK";
9const BINARY_PACK_VERSION: u32 = 1;
10
11pub struct BinaryPackLoader;
13
14impl Default for BinaryPackLoader {
15 fn default() -> Self {
16 Self
17 }
18}
19
20impl LibLoader for BinaryPackLoader {
21 fn can_load(&self, source: &LibSource) -> bool {
22 if let Ok(Some(path)) = sim_run_loaders::path_from_source(source) {
23 return path.extension().is_some_and(|ext| ext == "l8b");
24 }
25 if let Ok(Some(bytes)) = sim_run_loaders::bytes_from_source(source) {
26 return has_binary_pack_magic(&bytes);
27 }
28 false
29 }
30
31 fn load(&self, _cx: &mut Cx, source: LibSource) -> Result<Box<dyn Lib>> {
32 let bytes = read_pack_source(source)?;
33 let pack = decode_binary_lib_pack(&bytes)?;
34 Ok(Box::new(ReexportLib::new(pack.manifest, pack.exports)))
35 }
36
37 fn inspect_manifest(
38 &self,
39 _cx: &mut Cx,
40 source: &LibSource,
41 ) -> Result<Option<sim_kernel::LibManifest>> {
42 let bytes = if let Some(path) = sim_run_loaders::path_from_source(source)? {
43 std::fs::read(&path).map_err(|err| {
44 sim_kernel::Error::HostError(format!(
45 "failed to read binary lib pack {}: {err}",
46 path.display()
47 ))
48 })?
49 } else if let Some(bytes) = sim_run_loaders::bytes_from_source(source)? {
50 bytes
51 } else if let Some(url) = sim_run_loaders::url_from_source(source)? {
52 return Err(sim_kernel::Error::HostError(format!(
53 "url inspection is not implemented for binary lib pack {url}"
54 )));
55 } else {
56 return Ok(None);
57 };
58 Ok(Some(decode_binary_lib_pack(&bytes)?.manifest))
59 }
60}
61
62#[derive(Clone, Debug, PartialEq, Eq)]
64pub struct BinaryLibPack {
65 pub manifest: sim_kernel::LibManifest,
67 pub exports: Vec<ReexportSpec>,
69}
70
71pub fn encode_binary_lib_pack(pack: &BinaryLibPack) -> Result<Vec<u8>> {
73 let manifest_expr = manifest_to_expr(&pack.manifest);
74 let exports_expr = reexports_to_expr(&pack.exports);
75 let manifest = sim_codec_binary::encode_frame(&manifest_expr)?.0;
76 let exports = sim_codec_binary::encode_frame(&exports_expr)?.0;
77
78 let mut bytes = Vec::with_capacity(16 + manifest.len() + exports.len());
79 bytes.extend_from_slice(BINARY_PACK_MAGIC);
80 bytes.extend_from_slice(&BINARY_PACK_VERSION.to_le_bytes());
81 bytes.extend_from_slice(
82 &u32::try_from(manifest.len())
83 .map_err(|_| sim_kernel::Error::HostError("manifest frame too large".to_owned()))?
84 .to_le_bytes(),
85 );
86 bytes.extend_from_slice(
87 &u32::try_from(exports.len())
88 .map_err(|_| sim_kernel::Error::HostError("export frame too large".to_owned()))?
89 .to_le_bytes(),
90 );
91 bytes.extend_from_slice(&manifest);
92 bytes.extend_from_slice(&exports);
93 Ok(bytes)
94}
95
96pub fn decode_binary_lib_pack(bytes: &[u8]) -> Result<BinaryLibPack> {
99 if !has_binary_pack_magic(bytes) {
100 return Err(sim_kernel::Error::HostError(
101 "invalid binary lib pack magic".to_owned(),
102 ));
103 }
104 if bytes.len() < 16 {
105 return Err(sim_kernel::Error::HostError(
106 "binary lib pack header is truncated".to_owned(),
107 ));
108 }
109 let version = read_u32(bytes, 4)?;
110 if version != BINARY_PACK_VERSION {
111 return Err(sim_kernel::Error::HostError(format!(
112 "unsupported binary lib pack version {version}"
113 )));
114 }
115 let manifest_len = read_u32(bytes, 8)? as usize;
116 let exports_len = read_u32(bytes, 12)? as usize;
117 let manifest_start: usize = 16;
118 let manifest_end = manifest_start
119 .checked_add(manifest_len)
120 .ok_or_else(|| sim_kernel::Error::HostError("manifest frame length overflow".to_owned()))?;
121 let exports_end = manifest_end
122 .checked_add(exports_len)
123 .ok_or_else(|| sim_kernel::Error::HostError("export frame length overflow".to_owned()))?;
124 if exports_end != bytes.len() {
125 return Err(sim_kernel::Error::HostError(
126 "binary lib pack length does not match header".to_owned(),
127 ));
128 }
129 let (_, manifest_expr) = sim_codec_binary::decode_frame(
130 sim_kernel::CodecId(0),
131 &bytes[manifest_start..manifest_end],
132 )?;
133 let (_, exports_expr) =
134 sim_codec_binary::decode_frame(sim_kernel::CodecId(0), &bytes[manifest_end..exports_end])?;
135 Ok(BinaryLibPack {
136 manifest: expr_to_manifest(manifest_expr)?,
137 exports: expr_to_reexports(exports_expr)?,
138 })
139}
140
141fn read_pack_source(source: LibSource) -> Result<Vec<u8>> {
142 if let Some(path) = sim_run_loaders::path_from_source(&source)? {
143 return std::fs::read(&path).map_err(|err| {
144 sim_kernel::Error::HostError(format!(
145 "failed to read binary lib pack {}: {err}",
146 path.display()
147 ))
148 });
149 }
150 if let Some(bytes) = sim_run_loaders::bytes_from_source(&source)? {
151 return Ok(bytes);
152 }
153 if let Some(url) = sim_run_loaders::url_from_source(&source)? {
154 return Err(sim_kernel::Error::HostError(format!(
155 "url loading is not implemented for binary lib pack {url}"
156 )));
157 }
158 Err(sim_kernel::Error::HostError(
159 "binary pack loader received unsupported source".to_owned(),
160 ))
161}
162
163pub(super) fn manifest_to_expr(manifest: &sim_kernel::LibManifest) -> sim_kernel::Expr {
164 sim_kernel::Expr::Map(vec![
165 symbol_entry("id", sim_kernel::Expr::Symbol(manifest.id.clone())),
166 symbol_entry(
167 "version",
168 sim_kernel::Expr::String(manifest.version.0.clone()),
169 ),
170 symbol_entry("abi-major", number_expr(manifest.abi.major)),
171 symbol_entry("abi-minor", number_expr(manifest.abi.minor)),
172 symbol_entry(
173 "target",
174 sim_kernel::Expr::String(lib_target_name(&manifest.target)),
175 ),
176 symbol_entry(
177 "requires",
178 sim_kernel::Expr::List(
179 manifest
180 .requires
181 .iter()
182 .map(|dependency| {
183 sim_kernel::Expr::Map(vec![
184 symbol_entry("id", sim_kernel::Expr::Symbol(dependency.id.clone())),
185 symbol_entry(
186 "minimum-version",
187 dependency
188 .minimum_version
189 .as_ref()
190 .map(|version| sim_kernel::Expr::String(version.0.clone()))
191 .unwrap_or(sim_kernel::Expr::Nil),
192 ),
193 ])
194 })
195 .collect(),
196 ),
197 ),
198 symbol_entry(
199 "capabilities",
200 sim_kernel::Expr::List(
201 manifest
202 .capabilities
203 .iter()
204 .map(|capability| sim_kernel::Expr::String(capability.as_str().to_owned()))
205 .collect(),
206 ),
207 ),
208 symbol_entry(
209 "exports",
210 sim_kernel::Expr::List(
211 manifest
212 .exports
213 .iter()
214 .map(|export| {
215 sim_kernel::Expr::Map(vec![
216 symbol_entry(
217 "kind",
218 sim_kernel::Expr::String(
219 export.kind_symbol().symbol().as_qualified_str(),
220 ),
221 ),
222 symbol_entry(
223 "symbol",
224 sim_kernel::Expr::Symbol(export.symbol().clone()),
225 ),
226 ])
227 })
228 .collect(),
229 ),
230 ),
231 ])
232}
233
234pub(super) fn expr_to_manifest(expr: sim_kernel::Expr) -> Result<sim_kernel::LibManifest> {
235 Ok(sim_kernel::LibManifest {
236 id: expect_symbol_field(&expr, "id")?,
237 version: sim_kernel::Version(expect_string_field(&expr, "version")?),
238 abi: sim_kernel::AbiVersion {
239 major: expect_u16_field(&expr, "abi-major")?,
240 minor: expect_u16_field(&expr, "abi-minor")?,
241 },
242 target: parse_lib_target(&expect_string_field(&expr, "target")?)?,
243 requires: expect_list_field(&expr, "requires")?
244 .into_iter()
245 .map(|entry| {
246 Ok(sim_kernel::Dependency {
247 id: expect_symbol_field(&entry, "id")?,
248 minimum_version: expect_optional_string_field(&entry, "minimum-version")?
249 .map(sim_kernel::Version),
250 })
251 })
252 .collect::<Result<Vec<_>>>()?,
253 capabilities: expect_list_field(&expr, "capabilities")?
254 .into_iter()
255 .map(|entry| match entry {
256 sim_kernel::Expr::String(capability) => {
257 Ok(sim_kernel::CapabilityName::new(capability))
258 }
259 other => Err(sim_kernel::Error::Lib(format!(
260 "expected capability string, found {:?}",
261 expr_kind(&other)
262 ))),
263 })
264 .collect::<Result<Vec<_>>>()?,
265 exports: expect_list_field(&expr, "exports")?
266 .into_iter()
267 .map(expr_to_manifest_export)
268 .collect::<Result<Vec<_>>>()?,
269 })
270}
271
272fn reexports_to_expr(exports: &[ReexportSpec]) -> sim_kernel::Expr {
273 sim_kernel::Expr::List(
274 exports
275 .iter()
276 .map(|export| {
277 sim_kernel::Expr::Map(vec![
278 symbol_entry(
279 "kind",
280 sim_kernel::Expr::String(
281 match export.kind {
282 ReexportKind::Class => "class",
283 ReexportKind::Function => "function",
284 ReexportKind::Macro => "macro",
285 ReexportKind::Shape => "shape",
286 ReexportKind::Codec => "codec",
287 ReexportKind::NumberDomain => "number-domain",
288 ReexportKind::Value => "value",
289 }
290 .to_owned(),
291 ),
292 ),
293 symbol_entry("export", sim_kernel::Expr::Symbol(export.export.clone())),
294 symbol_entry("target", sim_kernel::Expr::Symbol(export.target.clone())),
295 ])
296 })
297 .collect(),
298 )
299}
300
301fn expr_to_reexports(expr: sim_kernel::Expr) -> Result<Vec<ReexportSpec>> {
302 let sim_kernel::Expr::List(entries) = expr else {
303 return Err(sim_kernel::Error::Lib(
304 "expected binary lib pack reexports list".to_owned(),
305 ));
306 };
307 entries
308 .into_iter()
309 .map(|entry| {
310 let kind = match expect_string_field(&entry, "kind")?.as_str() {
311 "class" => ReexportKind::Class,
312 "function" => ReexportKind::Function,
313 "macro" => ReexportKind::Macro,
314 "shape" => ReexportKind::Shape,
315 "codec" => ReexportKind::Codec,
316 "number-domain" => ReexportKind::NumberDomain,
317 "value" => ReexportKind::Value,
318 other => {
319 return Err(sim_kernel::Error::Lib(format!(
320 "unknown reexport kind {other}"
321 )));
322 }
323 };
324 Ok(ReexportSpec {
325 kind,
326 export: expect_symbol_field(&entry, "export")?,
327 target: expect_symbol_field(&entry, "target")?,
328 })
329 })
330 .collect()
331}
332
333fn expr_to_manifest_export(expr: sim_kernel::Expr) -> Result<sim_kernel::Export> {
334 let kind = expect_string_field(&expr, "kind")?;
335 let symbol = expect_symbol_field(&expr, "symbol")?;
336 match kind.as_str() {
337 "class" => Ok(sim_kernel::Export::Class {
338 symbol,
339 class_id: None,
340 }),
341 "function" => Ok(sim_kernel::Export::Function {
342 symbol,
343 function_id: None,
344 }),
345 "macro" => Ok(sim_kernel::Export::Macro {
346 symbol,
347 macro_id: None,
348 }),
349 "shape" => Ok(sim_kernel::Export::Shape {
350 symbol,
351 shape_id: None,
352 }),
353 "codec" => Ok(sim_kernel::Export::Codec {
354 symbol,
355 codec_id: None,
356 }),
357 "number-domain" => Ok(sim_kernel::Export::NumberDomain {
358 symbol,
359 number_domain_id: None,
360 }),
361 "value" => Ok(sim_kernel::Export::Value { symbol }),
362 other => Err(sim_kernel::Error::Lib(format!(
363 "unknown manifest export kind {other}"
364 ))),
365 }
366}
367
368fn symbol_entry(key: &str, value: sim_kernel::Expr) -> (sim_kernel::Expr, sim_kernel::Expr) {
369 (
370 sim_kernel::Expr::Symbol(sim_kernel::Symbol::new(key)),
371 value,
372 )
373}
374
375fn number_expr(value: impl ToString) -> sim_kernel::Expr {
376 sim_kernel::Expr::Number(sim_kernel::NumberLiteral {
377 domain: sim_kernel::Symbol::qualified("numbers", "f64"),
378 canonical: value.to_string(),
379 })
380}
381
382fn lib_target_name(target: &sim_kernel::LibTarget) -> String {
383 target.to_symbol().as_qualified_str()
384}
385
386fn parse_lib_target(name: &str) -> Result<sim_kernel::LibTarget> {
387 Ok(sim_kernel::LibTarget::from_symbol(&parse_symbol_text(name)))
388}
389
390fn expect_map_field<'a>(expr: &'a sim_kernel::Expr, field: &str) -> Result<&'a sim_kernel::Expr> {
391 let sim_kernel::Expr::Map(entries) = expr else {
392 return Err(sim_kernel::Error::Lib(format!(
393 "expected map expr for field lookup, found {:?}",
394 expr_kind(expr)
395 )));
396 };
397 entries
398 .iter()
399 .find_map(|(key, value)| match key {
400 sim_kernel::Expr::Symbol(symbol)
401 if symbol.name.as_ref() == field && symbol.namespace.is_none() =>
402 {
403 Some(value)
404 }
405 _ => None,
406 })
407 .ok_or_else(|| sim_kernel::Error::Lib(format!("missing field {field}")))
408}
409
410fn expect_list_field(expr: &sim_kernel::Expr, field: &str) -> Result<Vec<sim_kernel::Expr>> {
411 match expect_map_field(expr, field)? {
412 sim_kernel::Expr::List(items) => Ok(items.clone()),
413 other => Err(sim_kernel::Error::Lib(format!(
414 "expected list field {field}, found {:?}",
415 expr_kind(other)
416 ))),
417 }
418}
419
420fn expect_symbol_field(expr: &sim_kernel::Expr, field: &str) -> Result<sim_kernel::Symbol> {
421 match expect_map_field(expr, field)? {
422 sim_kernel::Expr::Symbol(symbol) => Ok(symbol.clone()),
423 sim_kernel::Expr::String(value) => Ok(parse_symbol_text(value)),
424 other => Err(sim_kernel::Error::Lib(format!(
425 "expected symbol field {field}, found {:?}",
426 expr_kind(other)
427 ))),
428 }
429}
430
431fn expect_string_field(expr: &sim_kernel::Expr, field: &str) -> Result<String> {
432 match expect_map_field(expr, field)? {
433 sim_kernel::Expr::String(value) => Ok(value.clone()),
434 other => Err(sim_kernel::Error::Lib(format!(
435 "expected string field {field}, found {:?}",
436 expr_kind(other)
437 ))),
438 }
439}
440
441fn expect_optional_string_field(expr: &sim_kernel::Expr, field: &str) -> Result<Option<String>> {
442 match expect_map_field(expr, field)? {
443 sim_kernel::Expr::Nil => Ok(None),
444 sim_kernel::Expr::String(value) => Ok(Some(value.clone())),
445 other => Err(sim_kernel::Error::Lib(format!(
446 "expected optional string field {field}, found {:?}",
447 expr_kind(other)
448 ))),
449 }
450}
451
452fn expect_u16_field(expr: &sim_kernel::Expr, field: &str) -> Result<u16> {
453 match expect_map_field(expr, field)? {
454 sim_kernel::Expr::Number(number) => number
455 .canonical
456 .parse::<u16>()
457 .map_err(|err| sim_kernel::Error::Lib(format!("invalid {field} number: {err}"))),
458 other => Err(sim_kernel::Error::Lib(format!(
459 "expected numeric field {field}, found {:?}",
460 expr_kind(other)
461 ))),
462 }
463}
464
465fn has_binary_pack_magic(bytes: &[u8]) -> bool {
466 bytes.get(..4) == Some(BINARY_PACK_MAGIC.as_slice())
467}
468
469fn read_u32(bytes: &[u8], offset: usize) -> Result<u32> {
470 let raw = bytes.get(offset..offset + 4).ok_or_else(|| {
471 sim_kernel::Error::HostError("binary lib pack header is truncated".to_owned())
472 })?;
473 let mut buf = [0u8; 4];
474 buf.copy_from_slice(raw);
475 Ok(u32::from_le_bytes(buf))
476}