use crate::atom::Atom;
use crate::loader::decode::{ExportEntry, ImportEntry, LambdaEntry, LineInfo};
use super::compact::{AtomEncoder, push_signed, push_unsigned};
use super::container::EncodeError;
pub(crate) fn encode_atom_chunk(
atoms: &[Atom],
encoder: &AtomEncoder<'_>,
) -> Result<Vec<u8>, EncodeError> {
let names = atoms
.iter()
.map(|atom| encoder.resolve(*atom))
.collect::<Result<Vec<_>, _>>()?;
let count = i32::try_from(names.len()).map_err(|_| EncodeError::ValueOutOfRange)?;
let compact_lengths = names.iter().any(|name| name.len() > u8::MAX as usize);
let mut out = Vec::new();
if compact_lengths {
out.extend_from_slice(
&count
.checked_neg()
.ok_or(EncodeError::ValueOutOfRange)?
.to_be_bytes(),
);
} else {
out.extend_from_slice(&count.to_be_bytes());
}
for name in names {
let bytes = name.as_bytes();
if compact_lengths {
push_unsigned(
&mut out,
0,
u64::try_from(bytes.len()).map_err(|_| EncodeError::ValueOutOfRange)?,
);
} else {
out.push(bytes.len() as u8);
}
out.extend_from_slice(bytes);
}
Ok(out)
}
pub(crate) fn encode_import_chunk(
imports: &[ImportEntry],
encoder: &AtomEncoder<'_>,
) -> Result<Vec<u8>, EncodeError> {
let mut out = Vec::new();
out.extend_from_slice(&count_u32(imports.len())?.to_be_bytes());
for import in imports {
out.extend_from_slice(&encoder.index_of(import.module)?.to_be_bytes());
out.extend_from_slice(&encoder.index_of(import.function)?.to_be_bytes());
out.extend_from_slice(&u32::from(import.arity).to_be_bytes());
}
Ok(out)
}
pub(crate) fn encode_export_chunk(
exports: &[ExportEntry],
encoder: &AtomEncoder<'_>,
) -> Result<Vec<u8>, EncodeError> {
let mut out = Vec::new();
out.extend_from_slice(&count_u32(exports.len())?.to_be_bytes());
for export in exports {
out.extend_from_slice(&encoder.index_of(export.function)?.to_be_bytes());
out.extend_from_slice(&u32::from(export.arity).to_be_bytes());
out.extend_from_slice(&export.label.to_be_bytes());
}
Ok(out)
}
pub(crate) fn encode_lambda_chunk(
lambdas: &[LambdaEntry],
encoder: &AtomEncoder<'_>,
) -> Result<Vec<u8>, EncodeError> {
let mut out = Vec::new();
out.extend_from_slice(&count_u32(lambdas.len())?.to_be_bytes());
for (position, lambda) in lambdas.iter().enumerate() {
out.extend_from_slice(&encoder.index_of(lambda.function)?.to_be_bytes());
out.extend_from_slice(&u32::from(lambda.arity).to_be_bytes());
out.extend_from_slice(&lambda.label.to_be_bytes());
out.extend_from_slice(&count_u32(position)?.to_be_bytes());
out.extend_from_slice(&lambda.num_free.to_be_bytes());
out.extend_from_slice(&0_u32.to_be_bytes());
}
Ok(out)
}
pub(crate) fn encode_string_chunk(string_table: &[u8]) -> Vec<u8> {
string_table.to_vec()
}
pub(crate) fn encode_line_chunk(line_info: &[LineInfo]) -> Result<Vec<u8>, EncodeError> {
let mut out = Vec::new();
out.extend_from_slice(&0_u32.to_be_bytes()); out.extend_from_slice(&0_u32.to_be_bytes()); out.extend_from_slice(&count_u32(line_info.len())?.to_be_bytes()); out.extend_from_slice(&count_u32(line_info.len())?.to_be_bytes()); out.extend_from_slice(&0_u32.to_be_bytes());
let mut current_file = 0_u32;
for info in line_info {
if info.file != current_file {
push_unsigned(&mut out, 2, u64::from(info.file));
current_file = info.file;
}
push_signed(&mut out, 1, i64::from(info.line));
}
Ok(out)
}
fn count_u32(value: usize) -> Result<u32, EncodeError> {
u32::try_from(value).map_err(|_| EncodeError::ValueOutOfRange)
}