phf_macros-0.11.2/.cargo_vcs_info.json0000644000000001500000000000100132320ustar { "git": { "sha1": "c9c35fd8ba3f1bc228388b0cef6e3814a02a72c0" }, "path_in_vcs": "phf_macros" }phf_macros-0.11.2/Cargo.toml0000644000000023420000000000100112350ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" rust-version = "1.60" name = "phf_macros" version = "0.11.2" authors = ["Steven Fackler "] include = ["src/lib.rs"] description = "Macros to generate types in the phf crate" readme = "README.md" categories = ["data-structures"] license = "MIT" repository = "https://github.com/rust-phf/rust-phf" resolver = "1" [lib] proc-macro = true [dependencies.phf_generator] version = "0.11.1" [dependencies.phf_shared] version = "^0.11.2" default-features = false [dependencies.proc-macro2] version = "1" [dependencies.quote] version = "1" [dependencies.syn] version = "2" features = ["full"] [dependencies.unicase_] version = "2.4.0" optional = true package = "unicase" [features] unicase = [ "unicase_", "phf_shared/unicase", ] phf_macros-0.11.2/Cargo.toml.orig000064400000000000000000000012471046102023000147210ustar 00000000000000[package] name = "phf_macros" version = "0.11.2" authors = ["Steven Fackler "] edition = "2021" license = "MIT" description = "Macros to generate types in the phf crate" repository = "https://github.com/rust-phf/rust-phf" readme = "../README.md" include = ["src/lib.rs"] rust-version = "1.60" categories = ["data-structures"] [lib] proc-macro = true [features] unicase = ["unicase_", "phf_shared/unicase"] [dependencies] syn = { version = "2", features = ["full"] } quote = "1" proc-macro2 = "1" unicase_ = { package = "unicase", version = "2.4.0", optional = true } phf_generator = "0.11.1" phf_shared = { version = "^0.11.2", default-features = false } phf_macros-0.11.2/README.md000064400000000000000000000060271046102023000133120ustar 00000000000000# Rust-PHF [![CI](https://github.com/rust-phf/rust-phf/actions/workflows/ci.yml/badge.svg)](https://github.com/rust-phf/rust-phf/actions/workflows/ci.yml) [![Latest Version](https://img.shields.io/crates/v/phf.svg)](https://crates.io/crates/phf) [Documentation](https://docs.rs/phf) Rust-PHF is a library to generate efficient lookup tables at compile time using [perfect hash functions](http://en.wikipedia.org/wiki/Perfect_hash_function). It currently uses the [CHD algorithm](http://cmph.sourceforge.net/papers/esa09.pdf) and can generate a 100,000 entry map in roughly .4 seconds. MSRV (minimum supported rust version) is Rust 1.60. ## Usage PHF data structures can be constructed via either the procedural macros in the `phf_macros` crate or code generation supported by the `phf_codegen` crate. To compile the `phf` crate with a dependency on libcore instead of libstd, enabling use in environments where libstd will not work, set `default-features = false` for the dependency: ```toml [dependencies] # to use `phf` in `no_std` environments phf = { version = "0.11", default-features = false } ``` ### phf_macros ```rust use phf::phf_map; #[derive(Clone)] pub enum Keyword { Loop, Continue, Break, Fn, Extern, } static KEYWORDS: phf::Map<&'static str, Keyword> = phf_map! { "loop" => Keyword::Loop, "continue" => Keyword::Continue, "break" => Keyword::Break, "fn" => Keyword::Fn, "extern" => Keyword::Extern, }; pub fn parse_keyword(keyword: &str) -> Option { KEYWORDS.get(keyword).cloned() } ``` ```toml [dependencies] phf = { version = "0.11", features = ["macros"] } ``` #### Note Currently, the macro syntax has some limitations and may not work as you want. See [#183] or [#196] for example. [#183]: https://github.com/rust-phf/rust-phf/issues/183 [#196]: https://github.com/rust-phf/rust-phf/issues/196 ### phf_codegen To use `phf_codegen` on build.rs, you have to add dependencies under `[build-dependencies]`: ```toml [build-dependencies] phf = { version = "0.11.1", default-features = false } phf_codegen = "0.11.1" ``` Then put code on build.rs: ```rust use std::env; use std::fs::File; use std::io::{BufWriter, Write}; use std::path::Path; fn main() { let path = Path::new(&env::var("OUT_DIR").unwrap()).join("codegen.rs"); let mut file = BufWriter::new(File::create(&path).unwrap()); write!( &mut file, "static KEYWORDS: phf::Map<&'static str, Keyword> = {}", phf_codegen::Map::new() .entry("loop", "Keyword::Loop") .entry("continue", "Keyword::Continue") .entry("break", "Keyword::Break") .entry("fn", "Keyword::Fn") .entry("extern", "Keyword::Extern") .build() ) .unwrap(); write!(&mut file, ";\n").unwrap(); } ``` and lib.rs: ```rust #[derive(Clone)] enum Keyword { Loop, Continue, Break, Fn, Extern, } include!(concat!(env!("OUT_DIR"), "/codegen.rs")); pub fn parse_keyword(keyword: &str) -> Option { KEYWORDS.get(keyword).cloned() } ``` phf_macros-0.11.2/src/lib.rs000064400000000000000000000260671046102023000137440ustar 00000000000000//! A set of macros to generate Rust source for PHF data structures at compile time. //! See [the `phf` crate's documentation][phf] for details. //! //! [phf]: https://docs.rs/phf use phf_generator::HashState; use phf_shared::PhfHash; use proc_macro::TokenStream; use quote::quote; use std::collections::HashSet; use std::hash::Hasher; use syn::parse::{self, Parse, ParseStream}; use syn::punctuated::Punctuated; use syn::{parse_macro_input, Error, Expr, ExprLit, Lit, Token, UnOp}; #[cfg(feature = "unicase")] use unicase_::UniCase; #[derive(Hash, PartialEq, Eq, Clone)] enum ParsedKey { Str(String), Binary(Vec), Char(char), I8(i8), I16(i16), I32(i32), I64(i64), I128(i128), U8(u8), U16(u16), U32(u32), U64(u64), U128(u128), Bool(bool), #[cfg(feature = "unicase")] UniCase(UniCase), } impl PhfHash for ParsedKey { fn phf_hash(&self, state: &mut H) where H: Hasher, { match self { ParsedKey::Str(s) => s.phf_hash(state), ParsedKey::Binary(s) => s.phf_hash(state), ParsedKey::Char(s) => s.phf_hash(state), ParsedKey::I8(s) => s.phf_hash(state), ParsedKey::I16(s) => s.phf_hash(state), ParsedKey::I32(s) => s.phf_hash(state), ParsedKey::I64(s) => s.phf_hash(state), ParsedKey::I128(s) => s.phf_hash(state), ParsedKey::U8(s) => s.phf_hash(state), ParsedKey::U16(s) => s.phf_hash(state), ParsedKey::U32(s) => s.phf_hash(state), ParsedKey::U64(s) => s.phf_hash(state), ParsedKey::U128(s) => s.phf_hash(state), ParsedKey::Bool(s) => s.phf_hash(state), #[cfg(feature = "unicase")] ParsedKey::UniCase(s) => s.phf_hash(state), } } } impl ParsedKey { fn from_expr(expr: &Expr) -> Option { match expr { Expr::Lit(lit) => match &lit.lit { Lit::Str(s) => Some(ParsedKey::Str(s.value())), Lit::ByteStr(s) => Some(ParsedKey::Binary(s.value())), Lit::Byte(s) => Some(ParsedKey::U8(s.value())), Lit::Char(s) => Some(ParsedKey::Char(s.value())), Lit::Int(s) => match s.suffix() { // we've lost the sign at this point, so `-128i8` looks like `128i8`, // which doesn't fit in an `i8`; parse it as a `u8` and cast (to `0i8`), // which is handled below, by `Unary` "i8" => Some(ParsedKey::I8(s.base10_parse::().unwrap() as i8)), "i16" => Some(ParsedKey::I16(s.base10_parse::().unwrap() as i16)), "i32" => Some(ParsedKey::I32(s.base10_parse::().unwrap() as i32)), "i64" => Some(ParsedKey::I64(s.base10_parse::().unwrap() as i64)), "i128" => Some(ParsedKey::I128(s.base10_parse::().unwrap() as i128)), "u8" => Some(ParsedKey::U8(s.base10_parse::().unwrap())), "u16" => Some(ParsedKey::U16(s.base10_parse::().unwrap())), "u32" => Some(ParsedKey::U32(s.base10_parse::().unwrap())), "u64" => Some(ParsedKey::U64(s.base10_parse::().unwrap())), "u128" => Some(ParsedKey::U128(s.base10_parse::().unwrap())), _ => None, }, Lit::Bool(s) => Some(ParsedKey::Bool(s.value)), _ => None, }, Expr::Array(array) => { let mut buf = vec![]; for expr in &array.elems { match expr { Expr::Lit(lit) => match &lit.lit { Lit::Int(s) => match s.suffix() { "u8" | "" => buf.push(s.base10_parse::().unwrap()), _ => return None, }, _ => return None, }, _ => return None, } } Some(ParsedKey::Binary(buf)) } Expr::Unary(unary) => { // if we received an integer literal (always unsigned) greater than i__::max_value() // then casting it to a signed integer type of the same width will negate it to // the same absolute value so we don't need to negate it here macro_rules! try_negate ( ($val:expr) => {if $val < 0 { $val } else { -$val }} ); match unary.op { UnOp::Neg(_) => match ParsedKey::from_expr(&unary.expr)? { ParsedKey::I8(v) => Some(ParsedKey::I8(try_negate!(v))), ParsedKey::I16(v) => Some(ParsedKey::I16(try_negate!(v))), ParsedKey::I32(v) => Some(ParsedKey::I32(try_negate!(v))), ParsedKey::I64(v) => Some(ParsedKey::I64(try_negate!(v))), ParsedKey::I128(v) => Some(ParsedKey::I128(try_negate!(v))), _ => None, }, UnOp::Deref(_) => { let mut expr = &*unary.expr; while let Expr::Group(group) = expr { expr = &*group.expr; } match expr { Expr::Lit(ExprLit { lit: Lit::ByteStr(s), .. }) => Some(ParsedKey::Binary(s.value())), _ => None, } } _ => None, } } Expr::Group(group) => ParsedKey::from_expr(&group.expr), #[cfg(feature = "unicase")] Expr::Call(call) => { if let Expr::Path(ep) = call.func.as_ref() { let segments = &mut ep.path.segments.iter().rev(); let last = &segments.next()?.ident; let last_ahead = &segments.next()?.ident; let is_unicode = last_ahead == "UniCase" && last == "unicode"; let is_ascii = last_ahead == "UniCase" && last == "ascii"; if call.args.len() == 1 && (is_unicode || is_ascii) { if let Some(Expr::Lit(ExprLit { attrs: _, lit: Lit::Str(s), })) = call.args.first() { let v = if is_unicode { UniCase::unicode(s.value()) } else { UniCase::ascii(s.value()) }; Some(ParsedKey::UniCase(v)) } else { None } } else { None } } else { None } } _ => None, } } } struct Key { parsed: ParsedKey, expr: Expr, } impl PhfHash for Key { fn phf_hash(&self, state: &mut H) where H: Hasher, { self.parsed.phf_hash(state) } } impl Parse for Key { fn parse(input: ParseStream<'_>) -> parse::Result { let expr = input.parse()?; let parsed = ParsedKey::from_expr(&expr) .ok_or_else(|| Error::new_spanned(&expr, "unsupported key expression"))?; Ok(Key { parsed, expr }) } } struct Entry { key: Key, value: Expr, } impl PhfHash for Entry { fn phf_hash(&self, state: &mut H) where H: Hasher, { self.key.phf_hash(state) } } impl Parse for Entry { fn parse(input: ParseStream<'_>) -> parse::Result { let key = input.parse()?; input.parse::]>()?; let value = input.parse()?; Ok(Entry { key, value }) } } struct Map(Vec); impl Parse for Map { fn parse(input: ParseStream<'_>) -> parse::Result { let parsed = Punctuated::::parse_terminated(input)?; let map = parsed.into_iter().collect::>(); check_duplicates(&map)?; Ok(Map(map)) } } struct Set(Vec); impl Parse for Set { fn parse(input: ParseStream<'_>) -> parse::Result { let parsed = Punctuated::::parse_terminated(input)?; let set = parsed .into_iter() .map(|key| Entry { key, value: syn::parse_str("()").unwrap(), }) .collect::>(); check_duplicates(&set)?; Ok(Set(set)) } } fn check_duplicates(entries: &[Entry]) -> parse::Result<()> { let mut keys = HashSet::new(); for entry in entries { if !keys.insert(&entry.key.parsed) { return Err(Error::new_spanned(&entry.key.expr, "duplicate key")); } } Ok(()) } fn build_map(entries: &[Entry], state: HashState) -> proc_macro2::TokenStream { let key = state.key; let disps = state.disps.iter().map(|&(d1, d2)| quote!((#d1, #d2))); let entries = state.map.iter().map(|&idx| { let key = &entries[idx].key.expr; let value = &entries[idx].value; quote!((#key, #value)) }); quote! { phf::Map { key: #key, disps: &[#(#disps),*], entries: &[#(#entries),*], } } } fn build_ordered_map(entries: &[Entry], state: HashState) -> proc_macro2::TokenStream { let key = state.key; let disps = state.disps.iter().map(|&(d1, d2)| quote!((#d1, #d2))); let idxs = state.map.iter().map(|idx| quote!(#idx)); let entries = entries.iter().map(|entry| { let key = &entry.key.expr; let value = &entry.value; quote!((#key, #value)) }); quote! { phf::OrderedMap { key: #key, disps: &[#(#disps),*], idxs: &[#(#idxs),*], entries: &[#(#entries),*], } } } #[proc_macro] pub fn phf_map(input: TokenStream) -> TokenStream { let map = parse_macro_input!(input as Map); let state = phf_generator::generate_hash(&map.0); build_map(&map.0, state).into() } #[proc_macro] pub fn phf_set(input: TokenStream) -> TokenStream { let set = parse_macro_input!(input as Set); let state = phf_generator::generate_hash(&set.0); let map = build_map(&set.0, state); quote!(phf::Set { map: #map }).into() } #[proc_macro] pub fn phf_ordered_map(input: TokenStream) -> TokenStream { let map = parse_macro_input!(input as Map); let state = phf_generator::generate_hash(&map.0); build_ordered_map(&map.0, state).into() } #[proc_macro] pub fn phf_ordered_set(input: TokenStream) -> TokenStream { let set = parse_macro_input!(input as Set); let state = phf_generator::generate_hash(&set.0); let map = build_ordered_map(&set.0, state); quote!(phf::OrderedSet { map: #map }).into() }