Skip to content

Commit

Permalink
More
Browse files Browse the repository at this point in the history
  • Loading branch information
Martin Taillefer committed Dec 4, 2024
1 parent d3b6f50 commit 77986b2
Show file tree
Hide file tree
Showing 6 changed files with 274 additions and 95 deletions.
6 changes: 0 additions & 6 deletions TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,10 @@

- Add support for empty literal collections in the macros.

- Add support for non-literal values when using the macros. This will end up using a facade
type as implementation.

- Consider adding support for case-insensitive strings.

- Extend the Scalar derive macro to support more varieties of enum types.

- In the generator, even when using fz_hash_map and fz_ordered_map, detect if the
keys are all integer or string literals and generate better code accordingly.

## Type System Nightmares

These are some things which I haven't done yet since I can't figure out how to express these things in the
Expand Down
248 changes: 181 additions & 67 deletions frozen-collections-core/src/macros/generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use quote::{format_ident, quote, ToTokens};
use std::fmt::Display;
use std::ops::Range;
use std::str::FromStr;
use syn::{parse2, Expr, ExprLit, Lit, LitInt, LitStr};
use syn::{parse2, Expr, Lit, LitInt, LitStr};

struct ProcessedEntry<K> {
base: Entry,
Expand Down Expand Up @@ -46,13 +46,35 @@ impl Output {
}

#[derive(Clone, Copy)]
pub enum KeyKind {
pub enum AssertedKeyKind {
Scalar,
String,
Hashed,
Ordered,
}

enum ScalarType {
I8,
I16,
I32,
I64,
ISize,
U8,
U16,
U32,
U64,
USize,
}

enum EffectiveKeyKind {
AllLiteralScalars(Option<ScalarType>),
LiteralAndExpressionScalars,
AllLiteralStrings,
LiteralAndExpressionStrings,
Hashed,
Ordered,
}

struct Generator {
entries: Vec<Entry>,
seeds: (u64, u64, u64, u64),
Expand All @@ -67,7 +89,7 @@ pub fn generate(
as_set: bool,
key_type: TokenStream,
value_type: TokenStream,
key_kind: KeyKind,
asserted_key_kind: AssertedKeyKind,
) -> syn::Result<Output> {
let mut gen = Generator {
entries: vec![],
Expand All @@ -88,77 +110,139 @@ pub fn generate(
));
}

match key_kind {
KeyKind::Scalar => match &gen.entries.clone()[0].key {
Expr::Lit(expr) => gen.decode_scalar_expr(expr),
Expr::Group(group) => match (*group.expr).clone() {
Expr::Lit(expr) => gen.decode_scalar_expr(&expr),
_ => gen.handle_non_literal_scalar_keys(),
},
_ => gen.handle_non_literal_scalar_keys(),
},

KeyKind::String => match &gen.entries[0].key {
Expr::Lit(expr) => match &expr.lit {
Lit::Str(_) => gen.handle_literal_string_keys(),
_ => Err(syn::Error::new_spanned(
expr,
"invalid literal, expecting a string value",
)),
},
Expr::Group(group) => match (*group.expr).clone() {
Expr::Lit(expr) => match &expr.lit {
Lit::Str(_) => gen.handle_literal_string_keys(),
_ => Err(syn::Error::new_spanned(
expr,
"invalid literal, expecting a string value",
)),
},
_ => gen.handle_non_literal_hashed_keys(),
},
_ => gen.handle_non_literal_hashed_keys(),
},
KeyKind::Hashed => gen.handle_non_literal_hashed_keys(),
KeyKind::Ordered => gen.handle_non_literal_ordered_keys(),
match gen.assess_keys(asserted_key_kind) {
EffectiveKeyKind::AllLiteralScalars(Some(ScalarType::I8)) => {
gen.handle_literal_scalar_keys::<i8>("")
}
EffectiveKeyKind::AllLiteralScalars(Some(ScalarType::I16)) => {
gen.handle_literal_scalar_keys::<i16>("")
}
EffectiveKeyKind::AllLiteralScalars(Some(ScalarType::I32)) => {
gen.handle_literal_scalar_keys::<i32>("")
}
EffectiveKeyKind::AllLiteralScalars(Some(ScalarType::I64)) => {
gen.handle_literal_scalar_keys::<i64>("")
}
EffectiveKeyKind::AllLiteralScalars(Some(ScalarType::ISize)) => {
gen.handle_literal_scalar_keys::<isize>("")
}
EffectiveKeyKind::AllLiteralScalars(Some(ScalarType::U8)) => {
gen.handle_literal_scalar_keys::<u8>("")
}
EffectiveKeyKind::AllLiteralScalars(Some(ScalarType::U16)) => {
gen.handle_literal_scalar_keys::<u16>("")
}
EffectiveKeyKind::AllLiteralScalars(Some(ScalarType::U32)) => {
gen.handle_literal_scalar_keys::<u32>("")
}
EffectiveKeyKind::AllLiteralScalars(Some(ScalarType::U64)) => {
gen.handle_literal_scalar_keys::<u64>("")
}
EffectiveKeyKind::AllLiteralScalars(Some(ScalarType::USize)) => {
gen.handle_literal_scalar_keys::<usize>("")
}
EffectiveKeyKind::AllLiteralScalars(None) => {
gen.handle_literal_scalar_keys::<i32>("i32")
}
EffectiveKeyKind::LiteralAndExpressionScalars => {
gen.handle_non_literal_scalar_keys()
}
EffectiveKeyKind::AllLiteralStrings => gen.handle_literal_string_keys(),
EffectiveKeyKind::LiteralAndExpressionStrings => {
gen.handle_non_literal_string_keys()
}
EffectiveKeyKind::Hashed => gen.handle_hashed_keys(),
EffectiveKeyKind::Ordered => gen.handle_ordered_keys(),
}
}

Payload::Vector(expr) => match key_kind {
KeyKind::Scalar => Ok(gen.emit_facade(&expr, "Scalar")),
KeyKind::String => Ok(gen.emit_facade(&expr, "String")),
KeyKind::Hashed => Ok(gen.emit_facade(&expr, "Hash")),
KeyKind::Ordered => Ok(gen.emit_facade(&expr, "Ordered")),
Payload::Vector(expr) => match asserted_key_kind {
AssertedKeyKind::Scalar => Ok(gen.emit_facade(&expr, "Scalar")),
AssertedKeyKind::String => Ok(gen.emit_facade(&expr, "String")),
AssertedKeyKind::Hashed => Ok(gen.emit_facade(&expr, "Hash")),
AssertedKeyKind::Ordered => Ok(gen.emit_facade(&expr, "Ordered")),
},
}
}

impl Generator {
fn decode_scalar_expr(self, expr: &ExprLit) -> syn::Result<Output> {
match &expr.lit {
Lit::Int(expr) => match expr.suffix() {
"i8" => self.handle_literal_scalar_keys::<i8>(),
"i16" => self.handle_literal_scalar_keys::<i16>(),
"i32" | "" => self.handle_literal_scalar_keys::<i32>(),
"i64" => self.handle_literal_scalar_keys::<i64>(),
"isize" => self.handle_literal_scalar_keys::<isize>(),
"u8" => self.handle_literal_scalar_keys::<u8>(),
"u16" => self.handle_literal_scalar_keys::<u16>(),
"u32" => self.handle_literal_scalar_keys::<u32>(),
"u64" => self.handle_literal_scalar_keys::<u64>(),
"usize" => self.handle_literal_scalar_keys::<usize>(),
_ => Err(syn::Error::new_spanned(
expr,
format!("unknown suffix {} for scalar value", expr.suffix()),
)),
},
_ => Err(syn::Error::new_spanned(
expr,
"invalid literal, expecting an integer value",
)),
fn assess_keys(&self, asserted_key_kind: AssertedKeyKind) -> EffectiveKeyKind {
let mut num_strings = 0;
let mut num_scalars = 0;
let mut scalar_type: Option<ScalarType> = None;

for entry in &self.entries {
match &entry.key {
Expr::Lit(expr) => match &expr.lit {
Lit::Str(_) => num_strings += 1,
Lit::Int(expr) => {
num_scalars += 1;

match expr.suffix() {
"i8" => scalar_type = Some(ScalarType::I8),
"i16" => scalar_type = Some(ScalarType::I16),
"i32" => scalar_type = Some(ScalarType::I32),
"i64" => scalar_type = Some(ScalarType::I64),
"isize" => scalar_type = Some(ScalarType::ISize),
"u8" => scalar_type = Some(ScalarType::U8),
"u16" => scalar_type = Some(ScalarType::U16),
"u32" => scalar_type = Some(ScalarType::U32),
"u64" => scalar_type = Some(ScalarType::U64),
"usize" => scalar_type = Some(ScalarType::USize),
_ => {}
}
}
_ => {}
},

Expr::Group(group) => match &*group.expr {
Expr::Lit(expr) => match &expr.lit {
Lit::Str(_) => num_strings += 1,
Lit::Int(expr) => {
num_scalars += 1;

match expr.suffix() {
"i8" => scalar_type = Some(ScalarType::I8),
"i16" => scalar_type = Some(ScalarType::I16),
"i32" => scalar_type = Some(ScalarType::I32),
"i64" => scalar_type = Some(ScalarType::I64),
"isize" => scalar_type = Some(ScalarType::ISize),
"u8" => scalar_type = Some(ScalarType::U8),
"u16" => scalar_type = Some(ScalarType::U16),
"u32" => scalar_type = Some(ScalarType::U32),
"u64" => scalar_type = Some(ScalarType::U64),
"usize" => scalar_type = Some(ScalarType::USize),
_ => {}
}
}
_ => {}
},
_ => {}
},

_ => {}
}
}

if num_scalars == self.entries.len() {
EffectiveKeyKind::AllLiteralScalars(scalar_type)
} else if num_scalars > 0 && num_strings == 0 {
EffectiveKeyKind::LiteralAndExpressionScalars
} else if num_strings == self.entries.len() {
EffectiveKeyKind::AllLiteralStrings
} else if num_strings > 0 {
EffectiveKeyKind::LiteralAndExpressionStrings
} else {
match asserted_key_kind {
AssertedKeyKind::Scalar => EffectiveKeyKind::LiteralAndExpressionScalars,
AssertedKeyKind::String => EffectiveKeyKind::LiteralAndExpressionStrings,
AssertedKeyKind::Hashed => EffectiveKeyKind::Hashed,
AssertedKeyKind::Ordered => EffectiveKeyKind::Ordered,
}
}
}

fn handle_literal_scalar_keys<K>(self) -> syn::Result<Output>
fn handle_literal_scalar_keys<K>(self, suffix: &str) -> syn::Result<Output>
where
K: Scalar + Ord + FromStr,
K::Err: Display,
Expand All @@ -167,9 +251,19 @@ impl Generator {

let hasher = PassthroughHasher::new();
for entry in &self.entries {
let k = parse2::<LitInt>(entry.key.to_token_stream())?.base10_parse::<K>()?;
let lit = parse2::<LitInt>(entry.key.to_token_stream())?;
let k = lit.base10_parse::<K>()?;

let mut e = entry.clone();
if suffix != "" {
e = Entry {
key: syn::parse_str::<Expr>(&format!("{}{}", lit.to_string(), suffix))?,
value: e.value,
}
}

processed_entries.push(ProcessedEntry {
base: entry.clone(),
base: e,
parsed_key: k,
hash_code: hasher.hash(&k),
});
Expand Down Expand Up @@ -283,7 +377,27 @@ impl Generator {
}
}

fn handle_non_literal_hashed_keys(mut self) -> syn::Result<Output> {
fn handle_non_literal_string_keys(mut self) -> syn::Result<Output> {
slow_dedup_by_keep_last(&mut self.entries, |x, y| x.key == y.key);

let mut processed_entries = Vec::with_capacity(self.entries.len());

for entry in &self.entries {
processed_entries.push(ProcessedEntry {
base: entry.clone(),
parsed_key: (),
hash_code: 0,
});
}

if processed_entries.len() < 3 {
Ok(self.emit_inline_scan(&processed_entries))
} else {
Ok(self.emit_hash_with_bridge(&processed_entries))
}
}

fn handle_hashed_keys(mut self) -> syn::Result<Output> {
slow_dedup_by_keep_last(&mut self.entries, |x, y| x.key == y.key);

let mut processed_entries = Vec::with_capacity(self.entries.len());
Expand All @@ -303,7 +417,7 @@ impl Generator {
}
}

fn handle_non_literal_ordered_keys(mut self) -> syn::Result<Output> {
fn handle_ordered_keys(mut self) -> syn::Result<Output> {
slow_dedup_by_keep_last(&mut self.entries, |x, y| x.key == y.key);

let mut processed_entries = Vec::with_capacity(self.entries.len());
Expand Down
Loading

0 comments on commit 77986b2

Please sign in to comment.