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/Cargo.lock | 46 +++++++++++++++ byte_encode_derive/Cargo.toml | 12 ++++ byte_encode_derive/src/lib.rs | 128 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 186 insertions(+) create mode 100644 byte_encode_derive/Cargo.lock create mode 100644 byte_encode_derive/Cargo.toml create mode 100644 byte_encode_derive/src/lib.rs (limited to 'byte_encode_derive') diff --git a/byte_encode_derive/Cargo.lock b/byte_encode_derive/Cargo.lock new file mode 100644 index 0000000..2616932 --- /dev/null +++ b/byte_encode_derive/Cargo.lock @@ -0,0 +1,46 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "byte_encode_derive" +version = "0.1.0" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "syn" +version = "2.0.118" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" diff --git a/byte_encode_derive/Cargo.toml b/byte_encode_derive/Cargo.toml new file mode 100644 index 0000000..832dadd --- /dev/null +++ b/byte_encode_derive/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "byte_encode_derive" +version = "0.1.0" +edition = "2024" + +[lib] +proc-macro = true + +[dependencies] + +syn = { version = "2", features = ["full", "parsing"] } +quote = "1" \ No newline at end of file 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