use core::marker::PhantomData;
use crate::{Flat, Near, NearList, Patch, emitter::Pos, list::Segment};
pub unsafe trait Emit<T>: Sized {
fn emit(self, p: &mut impl Patch) -> Pos
where
T: Flat,
{
let at = p.alloc::<T>();
unsafe { self.write_at(p, at) };
at
}
unsafe fn write_at(self, p: &mut impl Patch, at: Pos);
}
unsafe impl<T: Flat> Emit<T> for T {
unsafe fn write_at(self, p: &mut impl Patch, at: Pos) {
unsafe { self.deep_copy(p, at) };
}
}
unsafe impl<T: Flat> Emit<T> for &T {
unsafe fn write_at(self, p: &mut impl Patch, at: Pos) {
unsafe { self.deep_copy(p, at) };
}
}
pub fn near<T: Flat>(builder: impl Emit<T>) -> impl Emit<Near<T>> {
struct W<B>(B);
unsafe impl<T: Flat, B: Emit<T>> Emit<Near<T>> for W<B> {
unsafe fn write_at(self, p: &mut impl Patch, at: Pos) {
let target = Emit::<T>::emit(self.0, p);
unsafe { p.patch_near::<T>(at, target) };
}
}
W(builder)
}
pub fn list<T: Flat>(
iter: impl IntoIterator<IntoIter: ExactSizeIterator, Item: Emit<T>>,
) -> impl Emit<NearList<T>> {
struct W<I>(I);
unsafe impl<T: Flat, I> Emit<NearList<T>> for W<I>
where
I: IntoIterator,
I::IntoIter: ExactSizeIterator,
I::Item: Emit<T>,
{
unsafe fn write_at(self, p: &mut impl Patch, at: Pos) {
let mut iter = self.0.into_iter();
let len = iter.len() as u32;
if len == 0 {
unsafe { p.patch_list_header::<T>(at, Pos::ZERO, 0) };
} else {
let seg_pos = p.alloc_segment::<T>(len);
let values_offset = size_of::<Segment<T>>();
for i in 0..len as usize {
let item = iter.next().expect("ExactSizeIterator lied");
unsafe {
item.write_at(p, seg_pos.offset(values_offset + i * size_of::<T>()));
}
}
unsafe { p.patch_list_header::<T>(at, seg_pos, len) };
}
}
}
W(iter)
}
pub fn maybe<T: Flat>(opt: Option<impl Emit<T>>) -> impl Emit<Option<Near<T>>> {
struct W<B>(Option<B>);
unsafe impl<T: Flat, B: Emit<T>> Emit<Option<Near<T>>> for W<B> {
unsafe fn write_at(self, p: &mut impl Patch, at: Pos) {
match self.0 {
Some(builder) => {
let target = Emit::<T>::emit(builder, p);
unsafe { p.patch_near::<T>(at, target) };
}
None => unsafe { p.write_flat::<i32>(at, 0) },
}
}
}
W(opt)
}
#[must_use]
pub fn none<T: Flat>() -> impl Emit<Option<Near<T>>> {
struct W<T>(PhantomData<T>);
unsafe impl<T: Flat> Emit<Option<Near<T>>> for W<T> {
unsafe fn write_at(self, p: &mut impl Patch, at: Pos) {
unsafe { p.write_flat::<i32>(at, 0) };
}
}
W(PhantomData)
}
pub fn array<T: Flat, B: Emit<T>, const N: usize>(builders: [B; N]) -> impl Emit<[T; N]> {
struct W<B, const N: usize>([B; N]);
unsafe impl<T: Flat, B: Emit<T>, const N: usize> Emit<[T; N]> for W<B, N> {
unsafe fn write_at(self, p: &mut impl Patch, at: Pos) {
for (i, builder) in self.0.into_iter().enumerate() {
unsafe { builder.write_at(p, at.offset(i * size_of::<T>())) };
}
}
}
W(builders)
}
#[must_use]
pub fn empty<T: Flat>() -> impl Emit<NearList<T>> {
list(core::iter::empty::<T>())
}