sark-gen 0.21.0

Sark proc-macro generators
Documentation
use crate::macros::tokens::local;

#[derive(Clone, Copy)]
pub(in crate::request) enum Case {
    Exact,
    Folded,
}

impl Case {
    pub(in crate::request) fn emit(
        self,
        name: &proc_macro2::Ident,
        bytes: &[u8],
    ) -> proc_macro2::TokenStream {
        let folded = matches!(self, Self::Folded);
        let chunk = local("__c");
        let mut checks = Vec::new();
        if folded && bytes.len() > 8 {
            let mut offset = 0usize;
            while offset + 8 < bytes.len() {
                checks.push(self.chunk_check::<8>(&chunk, bytes, offset));
                offset += 8;
            }
            let tail = bytes.len() - 8;
            checks.push(self.chunk_check::<8>(&chunk, bytes, tail));
            return Self::wrap(&chunk, name, bytes.len(), checks);
        }
        let mut offset = 0usize;
        while offset + 8 <= bytes.len() {
            checks.push(self.chunk_check::<8>(&chunk, bytes, offset));
            offset += 8;
        }
        if offset + 4 <= bytes.len() {
            checks.push(self.chunk_check::<4>(&chunk, bytes, offset));
            offset += 4;
        }
        if offset + 2 <= bytes.len() {
            checks.push(self.chunk_check::<2>(&chunk, bytes, offset));
            offset += 2;
        }
        if offset < bytes.len() {
            let byte = bytes[offset];
            if folded && byte.is_ascii_lowercase() {
                checks.push(quote::quote! { ((#chunk[#offset] | 0x20) == #byte) });
            } else {
                checks.push(quote::quote! { #chunk[#offset] == #byte });
            }
        }
        Self::wrap(&chunk, name, bytes.len(), checks)
    }

    fn chunk_check<const N: usize>(
        self,
        chunk: &proc_macro2::Ident,
        bytes: &[u8],
        offset: usize,
    ) -> proc_macro2::TokenStream {
        const { assert!(N == 2 || N == 4 || N == 8) };
        let c = &bytes[offset..offset + N];
        let indices = offset..offset + N;
        let read = if N == 8 {
            quote::quote! { ::core::primitive::u64::from_le_bytes([#(#chunk[#indices]),*]) }
        } else if N == 4 {
            quote::quote! { ::core::primitive::u32::from_le_bytes([#(#chunk[#indices]),*]) }
        } else {
            quote::quote! { ::core::primitive::u16::from_le_bytes([#(#chunk[#indices]),*]) }
        };
        let word = Self::word(c);
        if matches!(self, Self::Folded) {
            let mask = c.iter().enumerate().fold(0u64, |mask, (idx, byte)| {
                if byte.is_ascii_lowercase() {
                    mask | (0x20u64 << (idx * 8))
                } else {
                    mask
                }
            });
            quote::quote! { ((#read as ::core::primitive::u64) | #mask) == #word }
        } else {
            quote::quote! { (#read as ::core::primitive::u64) == #word }
        }
    }

    fn word(bytes: &[u8]) -> u64 {
        bytes.iter().enumerate().fold(0u64, |word, (idx, byte)| {
            word | (u64::from(*byte) << (idx * 8))
        })
    }

    fn wrap(
        chunk: &proc_macro2::Ident,
        name: &proc_macro2::Ident,
        len: usize,
        checks: Vec<proc_macro2::TokenStream>,
    ) -> proc_macro2::TokenStream {
        use proc_macro2::Literal;
        let len = Literal::usize_unsuffixed(len);
        quote::quote! {
            #name.len() == #len
                && match #name.first_chunk::<#len>() {
                    ::core::option::Option::Some(#chunk) => true #( && #checks )*,
                    ::core::option::Option::None => false,
                }
        }
    }
}