use alloc::{boxed::Box, vec::Vec};
use core::fmt;
use syntax_lang::Span;
use crate::{Language, kind::Kind};
pub const IMAGE_FORMAT: u16 = 1;
const MAGIC: &[u8; 4] = b"LSL\0";
const HEADER: usize = 4 + 2 + 2 + 8 + 8;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum ImageError {
NotAnImage,
Format(u16),
Corrupt,
Invalid,
}
impl fmt::Display for ImageError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::NotAnImage => f.write_str("not a lang-forge language image"),
Self::Format(v) => write!(
f,
"language image format {v} is not {IMAGE_FORMAT}, the format this lang-forge reads; forge the sketch again"
),
Self::Corrupt => f.write_str("the language image is truncated or damaged"),
Self::Invalid => {
f.write_str("the language image holds tables lang-forge does not build")
}
}
}
}
impl core::error::Error for ImageError {}
pub(crate) fn fnv1a(bytes: &[u8]) -> u64 {
let mut h: u64 = 0xcbf2_9ce4_8422_2325;
for &b in bytes {
h ^= u64::from(b);
h = h.wrapping_mul(0x0000_0100_0000_01b3);
}
h
}
#[derive(Default)]
pub(crate) struct Writer {
pub(crate) out: Vec<u8>,
}
pub(crate) struct Reader<'a> {
bytes: &'a [u8],
pos: usize,
}
pub(crate) type Res<T> = Result<T, ImageError>;
impl<'a> Reader<'a> {
pub(crate) fn new(bytes: &'a [u8]) -> Self {
Self { bytes, pos: 0 }
}
fn take(&mut self, n: usize) -> Res<&'a [u8]> {
let end = self.pos.checked_add(n).ok_or(ImageError::Invalid)?;
let slice = self.bytes.get(self.pos..end).ok_or(ImageError::Invalid)?;
self.pos = end;
Ok(slice)
}
pub(crate) fn left(&self) -> usize {
self.bytes.len() - self.pos
}
pub(crate) fn len(&mut self, min: usize) -> Res<usize> {
let n = u32::get(self)? as usize;
if n.saturating_mul(min.max(1)) > self.left() {
return Err(ImageError::Invalid);
}
Ok(n)
}
pub(crate) fn done(&self) -> bool {
self.pos == self.bytes.len()
}
}
pub(crate) trait Image: Sized {
const MIN: usize = 1;
fn put(&self, w: &mut Writer);
fn get(r: &mut Reader<'_>) -> Res<Self>;
}
macro_rules! int_image {
($($t:ty),*) => {$(
impl Image for $t {
const MIN: usize = core::mem::size_of::<$t>();
fn put(&self, w: &mut Writer) {
w.out.extend_from_slice(&self.to_le_bytes());
}
fn get(r: &mut Reader<'_>) -> Res<Self> {
let bytes = r.take(core::mem::size_of::<$t>())?;
let mut buf = [0u8; core::mem::size_of::<$t>()];
buf.copy_from_slice(bytes);
Ok(<$t>::from_le_bytes(buf))
}
}
)*};
}
int_image!(u8, u16, u32, u64, i8);
impl Image for bool {
fn put(&self, w: &mut Writer) {
w.out.push(u8::from(*self));
}
fn get(r: &mut Reader<'_>) -> Res<Self> {
match u8::get(r)? {
0 => Ok(false),
1 => Ok(true),
_ => Err(ImageError::Invalid),
}
}
}
impl Image for char {
const MIN: usize = 4;
fn put(&self, w: &mut Writer) {
(*self as u32).put(w);
}
fn get(r: &mut Reader<'_>) -> Res<Self> {
char::from_u32(u32::get(r)?).ok_or(ImageError::Invalid)
}
}
impl Image for Kind {
const MIN: usize = 4;
fn put(&self, w: &mut Writer) {
self.bits().put(w);
}
fn get(r: &mut Reader<'_>) -> Res<Self> {
Ok(Kind::from_bits(u32::get(r)?))
}
}
impl Image for Span {
const MIN: usize = 8;
fn put(&self, w: &mut Writer) {
self.start().to_u32().put(w);
self.end().to_u32().put(w);
}
fn get(r: &mut Reader<'_>) -> Res<Self> {
let (start, end) = (u32::get(r)?, u32::get(r)?);
if start > end {
return Err(ImageError::Invalid);
}
Ok(Span::new(start, end))
}
}
impl Image for Box<str> {
const MIN: usize = 4;
fn put(&self, w: &mut Writer) {
(self.len() as u32).put(w);
w.out.extend_from_slice(self.as_bytes());
}
fn get(r: &mut Reader<'_>) -> Res<Self> {
let n = r.len(1)?;
let bytes = r.take(n)?;
core::str::from_utf8(bytes)
.map(Box::from)
.map_err(|_| ImageError::Invalid)
}
}
impl<T: Image> Image for Box<[T]> {
const MIN: usize = 4;
fn put(&self, w: &mut Writer) {
(self.len() as u32).put(w);
for item in self.iter() {
item.put(w);
}
}
fn get(r: &mut Reader<'_>) -> Res<Self> {
Ok(Vec::<T>::get(r)?.into())
}
}
impl<T: Image> Image for Vec<T> {
const MIN: usize = 4;
fn put(&self, w: &mut Writer) {
(self.len() as u32).put(w);
for item in self {
item.put(w);
}
}
fn get(r: &mut Reader<'_>) -> Res<Self> {
let n = r.len(T::MIN)?;
let mut out = Vec::with_capacity(n);
for _ in 0..n {
out.push(T::get(r)?);
}
Ok(out)
}
}
impl<T: Image> Image for Option<T> {
fn put(&self, w: &mut Writer) {
match self {
None => 0u8.put(w),
Some(v) => {
1u8.put(w);
v.put(w);
}
}
}
fn get(r: &mut Reader<'_>) -> Res<Self> {
match u8::get(r)? {
0 => Ok(None),
1 => Ok(Some(T::get(r)?)),
_ => Err(ImageError::Invalid),
}
}
}
impl<A: Image, B: Image> Image for (A, B) {
const MIN: usize = A::MIN + B::MIN;
fn put(&self, w: &mut Writer) {
self.0.put(w);
self.1.put(w);
}
fn get(r: &mut Reader<'_>) -> Res<Self> {
Ok((A::get(r)?, B::get(r)?))
}
}
impl<A: Image, B: Image, C: Image> Image for (A, B, C) {
const MIN: usize = A::MIN + B::MIN + C::MIN;
fn put(&self, w: &mut Writer) {
self.0.put(w);
self.1.put(w);
self.2.put(w);
}
fn get(r: &mut Reader<'_>) -> Res<Self> {
Ok((A::get(r)?, B::get(r)?, C::get(r)?))
}
}
impl<T: Image + Copy + Default, const N: usize> Image for [T; N] {
const MIN: usize = T::MIN * N;
fn put(&self, w: &mut Writer) {
for item in self {
item.put(w);
}
}
fn get(r: &mut Reader<'_>) -> Res<Self> {
let mut out = [T::default(); N];
for slot in &mut out {
*slot = T::get(r)?;
}
Ok(out)
}
}
impl<T: Image> Image for Box<T> {
const MIN: usize = T::MIN;
fn put(&self, w: &mut Writer) {
(**self).put(w);
}
fn get(r: &mut Reader<'_>) -> Res<Self> {
Ok(Box::new(T::get(r)?))
}
}
impl Language {
#[must_use]
pub fn to_image(&self) -> Vec<u8> {
let mut body = Writer::default();
self.tables().put(&mut body);
let body = body.out;
let mut out = Vec::with_capacity(HEADER + body.len());
out.extend_from_slice(MAGIC);
out.extend_from_slice(&IMAGE_FORMAT.to_le_bytes());
out.extend_from_slice(&0u16.to_le_bytes());
out.extend_from_slice(&(body.len() as u64).to_le_bytes());
out.extend_from_slice(&fnv1a(&body).to_le_bytes());
out.extend_from_slice(&body);
out
}
pub fn from_image(bytes: &[u8]) -> Result<Language, ImageError> {
if bytes.len() < HEADER || &bytes[..4] != MAGIC {
return Err(ImageError::NotAnImage);
}
let format = u16::from_le_bytes([bytes[4], bytes[5]]);
if format != IMAGE_FORMAT {
return Err(ImageError::Format(format));
}
let mut word = [0u8; 8];
word.copy_from_slice(&bytes[8..16]);
let length = u64::from_le_bytes(word);
word.copy_from_slice(&bytes[16..24]);
let hash = u64::from_le_bytes(word);
let body = &bytes[HEADER..];
if bytes[6..8] != [0, 0] || body.len() as u64 != length || fnv1a(body) != hash {
return Err(ImageError::Corrupt);
}
let mut r = Reader::new(body);
let grammar = crate::grammar::Grammar::get(&mut r)?;
if !r.done() {
return Err(ImageError::Invalid);
}
grammar.validate()?;
Ok(Language::from_grammar(grammar))
}
}
pub(crate) fn check(ok: bool) -> Res<()> {
if ok { Ok(()) } else { Err(ImageError::Invalid) }
}
impl Image for usize {
const MIN: usize = 8;
fn put(&self, w: &mut Writer) {
(*self as u64).put(w);
}
fn get(r: &mut Reader<'_>) -> Res<Self> {
usize::try_from(u64::get(r)?).map_err(|_| ImageError::Invalid)
}
}
macro_rules! image_struct {
($t:ident { $($f:ident),* $(,)? }) => {
impl crate::image::Image for $t {
fn put(&self, w: &mut crate::image::Writer) {
$( crate::image::Image::put(&self.$f, w); )*
}
fn get(r: &mut crate::image::Reader<'_>) -> crate::image::Res<Self> {
Ok(Self { $( $f: crate::image::Image::get(r)?, )* })
}
}
};
}
pub(crate) use image_struct;
macro_rules! image_enum {
($t:ident { $($v:ident = $n:literal),* $(,)? }) => {
impl crate::image::Image for $t {
fn put(&self, w: &mut crate::image::Writer) {
let tag: u8 = match self { $( $t::$v => $n, )* };
crate::image::Image::put(&tag, w);
}
fn get(r: &mut crate::image::Reader<'_>) -> crate::image::Res<Self> {
match <u8 as crate::image::Image>::get(r)? {
$( $n => Ok($t::$v), )*
_ => Err(crate::image::ImageError::Invalid),
}
}
}
};
}
pub(crate) use image_enum;
#[cfg(test)]
mod tests {
#![allow(clippy::unwrap_used, clippy::expect_used)]
use super::*;
#[test]
fn test_primitives_round_trip_and_bounds() {
let mut w = Writer::default();
(7u8, 300u16, 70_000u32).put(&mut w);
Some(Box::<str>::from("é")).put(&mut w);
Vec::from([1u16, 2, 3]).put(&mut w);
true.put(&mut w);
let mut r = Reader::new(&w.out);
assert_eq!(<(u8, u16, u32)>::get(&mut r), Ok((7, 300, 70_000)));
assert_eq!(Option::<Box<str>>::get(&mut r), Ok(Some(Box::from("é"))));
assert_eq!(Vec::<u16>::get(&mut r), Ok(Vec::from([1, 2, 3])));
assert_eq!(bool::get(&mut r), Ok(true));
assert!(r.done());
let mut r = Reader::new(&[0xFF, 0xFF, 0xFF, 0x7F]);
assert_eq!(Vec::<u64>::get(&mut r), Err(ImageError::Invalid));
assert_eq!(bool::get(&mut Reader::new(&[2])), Err(ImageError::Invalid));
assert_eq!(
Box::<str>::get(&mut Reader::new(&[1, 0, 0, 0, 0xFF])),
Err(ImageError::Invalid)
);
assert_eq!(
char::get(&mut Reader::new(&[0, 0xD8, 0, 0])),
Err(ImageError::Invalid)
);
assert_eq!(fnv1a(b""), 0xcbf2_9ce4_8422_2325);
}
}