use std::{
hash::Hash,
collections::HashMap,
fmt::Debug,
ops::Range,
};
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("undefined label {label:?} referenced at {pos:#X}")]
Label { pos: usize, label: Label },
#[error("error at {pos:#X}: {source}")]
Other { pos: usize, #[source] source: BoxError },
}
pub type Result<T, E=Error> = std::result::Result<T, E>;
impl Error {
pub fn pos(&self) -> usize {
match self {
Error::Label { pos, .. } => *pos,
Error::Other { pos, .. } => *pos,
}
}
pub fn pos_mut(&mut self) -> &mut usize {
match self {
Error::Label { pos, .. } => pos,
Error::Other { pos, .. } => pos,
}
}
}
#[derive(Clone, Debug, thiserror::Error)]
#[error("attempted to write {value:#X} as a u{size}")]
pub struct LabelSizeError {
pub value: usize,
pub size: usize,
}
type BoxError = Box<dyn std::error::Error + Send + Sync>;
type Delayed = Box<dyn FnOnce(&DelayContext, &mut [u8]) -> Result<(), BoxError>>;
#[cfg(doc)]
#[doc(hidden)]
pub type T = ();
#[derive(Default)]
#[must_use]
pub struct Writer {
data: Vec<u8>,
delays: Vec<(Range<usize>, Delayed)>,
labels: HashMap<Label, usize>,
}
pub struct DelayContext<'a> {
pos: usize,
labels: &'a HashMap<Label, usize>,
}
impl<'a> DelayContext<'a> {
#[inline(always)]
pub fn pos(&self) -> usize {
self.pos
}
#[inline(always)]
pub fn label(&self, label: Label) -> Result<usize> {
self.labels.get(&label).copied()
.ok_or(Error::Label { pos: self.pos(), label })
}
}
impl Writer {
#[inline(always)]
pub fn new() -> Self {
Self {
data: Vec::new(),
delays: Vec::new(),
labels: HashMap::new(),
}
}
pub fn finish(mut self) -> Result<Vec<u8>> {
for (range, cb) in self.delays {
let pos = range.start;
let res = cb(
&DelayContext { pos, labels: &self.labels},
&mut self.data[range],
);
match res {
Ok(()) => {},
Err(e) => return match e.downcast() {
Ok(e) => Err(*e),
Err(e) => Err(Error::Other { pos, source: e })
}
}
}
Ok(self.data)
}
#[must_use]
#[inline(always)]
pub fn len(&self) -> usize {
self.data.len()
}
#[must_use]
#[inline(always)]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
#[inline(always)]
pub fn reserve(&mut self, size: usize) {
self.data.reserve(size);
}
#[inline(always)]
pub fn capacity(&self) -> usize {
self.data.capacity()
}
#[cfg(doc)]
pub fn T(&mut self, val: T) {}
#[inline(always)]
pub fn slice(&mut self, data: &[u8]) {
self.data.extend_from_slice(data)
}
#[inline(always)]
pub fn array<const N: usize>(&mut self, data: [u8; N]) {
self.slice(&data)
}
#[inline(always)]
pub fn place(&mut self, label: Label) {
self.put_label(label, self.len());
}
#[inline(always)]
pub fn here(&mut self) -> Label {
let l = Label::new();
self.place(l);
l
}
#[cfg(doc)]
pub fn labelN(&mut self, l: Label) {}
#[cfg(doc)]
pub fn diffN(&mut self, start: Label, end: Label) {}
fn put_label(&mut self, label: Label, pos: usize) {
if let Some(p) = self.labels.insert(label, pos) {
panic!("label already defined at 0x{p:04X}")
}
}
#[inline(always)]
pub fn delay<const N: usize, F>(&mut self, cb: F) where
F: FnOnce(&DelayContext) -> Result<[u8; N], BoxError> + 'static,
{
let start = self.len();
self.array([0; N]);
let end = self.len();
self.delays.push((start..end, Box::new(move |ctx, slice| {
slice.copy_from_slice(&cb(ctx)?);
Ok(())
})));
}
#[cfg(doc)]
pub fn ptrN(&mut self) -> Writer {}
#[inline(always)]
pub fn align(&mut self, size: usize) {
self.slice(&vec![0;(size-(self.len()%size))%size]);
}
#[inline]
pub fn append(&mut self, mut other: Writer) {
let shift = self.len();
self.data.reserve(other.data.capacity());
self.data.append(&mut other.data);
for (range, cb) in other.delays {
let range = range.start+shift..range.end+shift;
self.delays.push((range, cb))
}
for (label, pos) in other.labels {
self.put_label(label, pos+shift);
}
}
}
mod seal { pub trait Sealed: Sized {} }
impl seal::Sealed for Writer {}
macro_rules! primitives {
(
$(#[$trait_attrs:meta])* trait $trait:ident;
$suf:ident, $conv:ident;
{ $($type:ident),* }
{ $($ptr:tt),* }
) => { paste::paste! {
impl Writer {
$(#[doc(hidden)] #[inline(always)] pub fn [<$type $suf>](&mut self, val: $type) {
self.array($type::$conv(val));
})*
$(#[doc(hidden)] #[inline(always)] pub fn [<label$ptr $suf>](&mut self, label: Label) {
self.delay(move |ctx| {
let value = ctx.label(label)?;
let value = [<u$ptr>]::try_from(value).map_err(|_| LabelSizeError { value, size: $ptr })?;
Ok([<u$ptr>]::$conv(value))
});
})*
$(#[doc(hidden)] #[inline(always)] pub fn [<diff$ptr $suf>](&mut self, start: Label, end: Label) {
self.delay(move |ctx| {
let start = ctx.label(start)?;
let end = ctx.label(end)?;
let value = end - start;
let value = [<u$ptr>]::try_from(value).map_err(|_| LabelSizeError { value, size: $ptr })?;
Ok([<u$ptr>]::$conv(value))
});
})*
$(#[doc(hidden)] #[inline(always)] pub fn [<ptr$ptr $suf>](&mut self) -> Self {
let mut g = Writer::new();
self.[<label$ptr $suf>](g.here());
g
})*
}
$(#[$trait_attrs])*
pub trait $trait: seal::Sealed {
$(#[doc(hidden)] fn $type(&mut self, val: $type);)*
$(#[doc(hidden)] fn [<label$ptr>](&mut self, label: Label);)*
$(#[doc(hidden)] fn [<diff$ptr>](&mut self, start: Label, end: Label);)*
$(#[doc(hidden)] fn [<ptr$ptr>](&mut self) -> Self;)*
}
impl $trait for Writer {
$(#[doc(hidden)] #[inline(always)] fn $type(&mut self, val: $type) {
self.[<$type $suf>](val)
})*
$(#[doc(hidden)] #[inline(always)] fn [<label$ptr>](&mut self, label: Label) {
self.[<label$ptr $suf>](label)
})*
$(#[doc(hidden)] #[inline(always)] fn [<diff$ptr>](&mut self, start: Label, end: Label) {
self.[<diff$ptr $suf>](start, end)
})*
$(#[doc(hidden)] #[inline(always)] fn [<ptr$ptr>](&mut self) -> Self {
self.[<ptr$ptr $suf>]()
})*
}
} }
}
primitives!(
trait Le;
_le, to_le_bytes;
{
u8, u16, u32, u64, u128,
i8, i16, i32, i64, i128,
f32, f64
}
{ 8, 16, 32, 64, 128 }
);
primitives!(
trait Be;
_be, to_be_bytes;
{
u8, u16, u32, u64, u128,
i8, i16, i32, i64, i128,
f32, f64
}
{ 8, 16, 32, 64, 128 }
);
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Label(u64);
impl Debug for Label {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Label({:#04X})", self.0)
}
}
impl Label {
pub fn new() -> Label {
use std::sync::atomic::{AtomicU64, Ordering};
static COUNT: AtomicU64 = AtomicU64::new(0);
let n = COUNT.fetch_add(1, Ordering::Relaxed);
Label(n)
}
}
impl Default for Label {
fn default() -> Self {
Self::new()
}
}