use std::borrow::Cow;
use proc_macro2::TokenStream;
use quote::{quote, ToTokens};
use syn::{Expr, ExprArray, ExprTuple, ExprUnary, Lit, UnOp};
use crate::utils::xor;
use crate::KEY;
const GLOBAL_ERROR: &str = r#"expected one literal parameter (like `true`, `1`, `1.14`, `'d'`, `b'e'`, `"foo"`, `b"bar"`, `c"baz"`, `["1", "2", "3"]`, `(99, "20", [3.3, 4.5])`)"#;
pub enum LiteralBytes {
Str(Vec<u8>),
ByteStr(Vec<u8>),
CStr(Vec<u8>),
Byte(Vec<u8>),
Int(Vec<u8>),
Float(Vec<u8>),
Char(Vec<u8>),
Bool(Vec<u8>),
Sequence {
inner: Vec<Self>,
delimiter: Delimiter,
},
}
pub enum Delimiter {
Bracket,
Parenthesis,
}
impl LiteralBytes {
pub fn parse(tokens: TokenStream) -> Result<Self, &'static str> {
let Ok(expr) = syn::parse2::<Expr>(tokens) else {
return Err(GLOBAL_ERROR);
};
if let Expr::Array(ExprArray { elems, .. }) | Expr::Tuple(ExprTuple { elems, .. }) = &expr {
let inner = elems
.iter()
.map(Expr::to_token_stream)
.map(Self::parse)
.collect::<Result<Vec<_>, _>>()?;
return Ok(Self::Sequence {
inner,
delimiter: match &expr {
Expr::Array(_) => Delimiter::Bracket,
Expr::Tuple(_) => Delimiter::Parenthesis,
_ => unreachable!(),
},
});
}
let (lit, negative) = match &expr {
Expr::Lit(lit) => (lit, false),
Expr::Unary(ExprUnary {
expr,
op: UnOp::Neg(_),
..
}) => {
let Expr::Lit(lit) = expr.as_ref() else {
return Err(GLOBAL_ERROR);
};
if !matches!(lit.lit, Lit::Int(_) | Lit::Float(_)) {
return Err(GLOBAL_ERROR);
}
(lit, true)
}
_ => return Err(GLOBAL_ERROR),
};
let literal = match &lit.lit {
Lit::Str(s) => Self::Str(s.value().into_bytes()),
Lit::ByteStr(s) => Self::ByteStr(s.value()),
Lit::CStr(s) => Self::CStr(s.value().into_bytes_with_nul()),
Lit::Byte(b) => Self::Byte(vec![b.value()]),
Lit::Char(c) => Self::Char((c.value() as u32).to_ne_bytes().to_vec()),
Lit::Int(i) => {
const ERROR: &str = "only `isize` integer literals supported";
let int = if negative {
let Ok(int) = i.base10_parse::<usize>() else {
return Err(ERROR);
};
if int > isize::MIN as usize {
return Err(ERROR);
}
let signed = (!int).wrapping_add(1) as isize;
signed
} else if let Ok(int) = i.base10_parse::<isize>() {
int
} else {
return Err(ERROR);
};
Self::Int(int.to_ne_bytes().to_vec())
}
Lit::Float(f) => {
let Ok(mut float) = f.base10_parse::<f64>() else {
return Err("only `f64` float literals supported");
};
if negative {
float = -float;
}
Self::Float(float.to_ne_bytes().to_vec())
}
Lit::Bool(b) => Self::Bool(vec![b.value as u8]),
_ => return Err(GLOBAL_ERROR),
};
Ok(literal)
}
pub fn len(&self) -> usize {
match self {
Self::Str(v)
| Self::ByteStr(v)
| Self::CStr(v)
| Self::Byte(v)
| Self::Int(v)
| Self::Float(v)
| Self::Char(v)
| Self::Bool(v) => v.len(),
Self::Sequence { inner, .. } => inner.iter().map(Self::len).sum(),
}
}
pub fn as_bytes(&self) -> Cow<'_, [u8]> {
match self {
Self::Str(v)
| Self::ByteStr(v)
| Self::CStr(v)
| Self::Byte(v)
| Self::Int(v)
| Self::Float(v)
| Self::Char(v)
| Self::Bool(v) => v.into(),
Self::Sequence { inner, .. } => inner
.iter()
.map(Self::as_bytes)
.flat_map(|b| b.to_vec())
.collect::<Vec<_>>()
.into(),
}
}
}
impl LiteralBytes {
pub fn encrypt(self) -> TokenStream {
let mut bytes = self.as_bytes().to_vec();
xor(&mut bytes, &KEY);
let decrypted = quote! {
let mut bytes = {
let mut bytes = [#(#bytes),*];
crate::__boo::xor(&mut bytes, crate::BOO_KEY);
bytes
};
};
unsafe { Self::decrypt(self, decrypted, bytes.len()) }
}
unsafe fn decrypt(
literal: LiteralBytes,
decrypted: TokenStream,
mut decrypted_len: usize,
) -> TokenStream {
match literal {
LiteralBytes::Str(_) => quote! {
unsafe {
#decrypted
::alloc::string::String::from_utf8_unchecked(bytes.to_vec())
}
},
LiteralBytes::ByteStr(_) => quote! {{
#decrypted
bytes
}},
LiteralBytes::CStr(_) => quote! {
unsafe {
#decrypted
::alloc::ffi::CString::from_vec_with_nul_unchecked(bytes.to_vec())
}
},
LiteralBytes::Byte(_) => quote! {{
#decrypted
(bytes as [u8; 1])[0]
}},
LiteralBytes::Int(_) => quote! {{
#decrypted
isize::from_ne_bytes(bytes)
}},
LiteralBytes::Float(_) => quote! {{
#decrypted
f64::from_ne_bytes(bytes)
}},
LiteralBytes::Char(_) => quote! {
unsafe {
#decrypted
::core::char::from_u32_unchecked(u32::from_ne_bytes(bytes))
}
},
LiteralBytes::Bool(_) => quote! {{
#decrypted
(bytes as [u8; 1])[0] != 0
}},
LiteralBytes::Sequence { inner, delimiter } => {
let (bytes, items): (Vec<_>, Vec<_>) = inner
.into_iter()
.enumerate()
.map(|(i, literal)| {
let item_var = format!("item_{i}").parse::<TokenStream>().unwrap();
let item_len = literal.len();
let remaining_len = decrypted_len
.checked_sub(item_len)
.expect("Sequence item exceeds remaining bytes");
let bytes = quote! {
let (#item_var, bytes) = crate::__boo::split_array::<
#decrypted_len,
#item_len,
#remaining_len
>(bytes);
};
let item_bytes = quote! { let mut bytes = #item_var; };
let decrypted_item =
unsafe { Self::decrypt(literal, item_bytes, item_len) };
decrypted_len = remaining_len;
(bytes, decrypted_item)
})
.unzip();
let sequence = match delimiter {
Delimiter::Bracket => quote! { [#(#items),*] },
Delimiter::Parenthesis => quote! { (#(#items),*) },
};
quote! {{
#decrypted
#(#bytes)*
#sequence
}}
}
}
}
}