From 7c640bfeaf2a0fbc8c3550b4c19790a800a7f6a2 Mon Sep 17 00:00:00 2001 From: Dylan Date: Wed, 26 Aug 2026 23:58:16 +1200 Subject: feat: Added a bunch of stuff Added derive macro for byte encode Added start for PNG reading Added BMP writing --- byte_encode_derive/src/lib.rs | 128 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 byte_encode_derive/src/lib.rs (limited to 'byte_encode_derive/src') diff --git a/byte_encode_derive/src/lib.rs b/byte_encode_derive/src/lib.rs new file mode 100644 index 0000000..5970c66 --- /dev/null +++ b/byte_encode_derive/src/lib.rs @@ -0,0 +1,128 @@ +// Code mostly made by gemini somehow + +use proc_macro::TokenStream; +use quote::quote; +use syn::{parse_macro_input, Data, DeriveInput, Fields}; + +#[proc_macro_derive(ByteEncode)] +pub fn derive_byte_encode(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as DeriveInput); + let name = input.ident; + + // We only support structs with named fields for this example + let fields = match input.data { + Data::Struct(data) => match data.fields { + Fields::Named(fields) => fields.named, + _ => panic!("Only named fields are supported"), + }, + _ => panic!("Only structs are supported"), + }; + + // --- 1. Generate from_le_bytes parsing logic --- + let mut current_offset = quote! { 0 }; + let from_le_bytes_initializers = fields.iter().map(|f| { + let field_name = &f.ident; + let field_type = &f.ty; + + // Calculate start and end bounds for the current field slice + let start = quote! { #current_offset }; + let end = quote! { #start + <#field_type>::SIZE }; + + // Update the offset tracking for the next iteration loop + current_offset = quote! { #end }; + + quote! { + #field_name: <#field_type as ByteEncode<{<#field_type>::SIZE}>>::from_le_bytes( + buffer[#start..#end].try_into().unwrap() + ) + } + }); + let mut current_offset = quote! { 0 }; + let from_be_bytes_initializers = fields.iter().map(|f| { + let field_name = &f.ident; + let field_type = &f.ty; + + // Calculate start and end bounds for the current field slice + let start = quote! { #current_offset }; + let end = quote! { #start + <#field_type>::SIZE }; + + // Update the offset tracking for the next iteration loop + current_offset = quote! { #end }; + + quote! { + #field_name: <#field_type as ByteEncode<{<#field_type>::SIZE}>>::from_be_bytes( + &mut buffer[#start..#end].try_into().unwrap() + ) + } + }); + + + // --- 2. Generate to_le_bytes serialization logic --- + let mut current_offset = quote! { 0 }; + let to_le_bytes_writers = fields.iter().map(|f| { + let field_name = &f.ident; + let field_type = &f.ty; + + let start = quote! { #current_offset }; + let end = quote! { #start + <#field_type>::SIZE }; + + current_offset = quote! { #end }; + + quote! { + res[#start..#end].copy_from_slice(&self.#field_name.to_le_bytes()); + } + }); + let mut current_offset = quote! { 0 }; + let to_be_bytes_writers = fields.iter().map(|f| { + let field_name = &f.ident; + let field_type = &f.ty; + + let start = quote! { #current_offset }; + let end = quote! { #start + <#field_type>::SIZE }; + + current_offset = quote! { #end }; + + quote! { + res[#start..#end].copy_from_slice(&self.#field_name.to_be_bytes()); + } + }); + + + // --- 3. Construct final token stream --- + // This dynamically sums up sizes like: 0 + Field1::SIZE + Field2::SIZE ... + let total_size_expr = fields.iter().fold(quote! { 0 }, |acc, f| { + let field_type = &f.ty; + quote! { #acc + <#field_type>::SIZE } + }); + + let expanded = quote! { + impl ByteEncode<{ #total_size_expr }> for #name { + fn from_le_bytes(buffer: &[u8; { #total_size_expr }]) -> Self { + Self { + #( #from_le_bytes_initializers, )* + } + } + + fn to_le_bytes(&self) -> [u8; { #total_size_expr }] { + let mut res = [0u8; { #total_size_expr }]; + #( #to_le_bytes_writers )* + res + } + + + fn from_be_bytes(buffer: &mut [u8; { #total_size_expr }]) -> Self { + Self { + #( #from_be_bytes_initializers, )* + } + } + + fn to_be_bytes(&self) -> [u8; { #total_size_expr }] { + let mut res = [0u8; { #total_size_expr }]; + #( #to_be_bytes_writers )* + res + } + } + }; + + TokenStream::from(expanded) +} -- cgit v1.3.1