summaryrefslogtreecommitdiffstats
path: root/third_party/rust/phf_macros/src/lib.rs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /third_party/rust/phf_macros/src/lib.rs
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/phf_macros/src/lib.rs')
-rw-r--r--third_party/rust/phf_macros/src/lib.rs331
1 files changed, 331 insertions, 0 deletions
diff --git a/third_party/rust/phf_macros/src/lib.rs b/third_party/rust/phf_macros/src/lib.rs
new file mode 100644
index 0000000000..a033036e6b
--- /dev/null
+++ b/third_party/rust/phf_macros/src/lib.rs
@@ -0,0 +1,331 @@
+//! 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<u8>),
+ 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<String>),
+}
+
+impl PhfHash for ParsedKey {
+ fn phf_hash<H>(&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<ParsedKey> {
+ 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::<u8>().unwrap() as i8)),
+ "i16" => Some(ParsedKey::I16(s.base10_parse::<u16>().unwrap() as i16)),
+ "i32" => Some(ParsedKey::I32(s.base10_parse::<u32>().unwrap() as i32)),
+ "i64" => Some(ParsedKey::I64(s.base10_parse::<u64>().unwrap() as i64)),
+ "i128" => Some(ParsedKey::I128(s.base10_parse::<u128>().unwrap() as i128)),
+ "u8" => Some(ParsedKey::U8(s.base10_parse::<u8>().unwrap())),
+ "u16" => Some(ParsedKey::U16(s.base10_parse::<u16>().unwrap())),
+ "u32" => Some(ParsedKey::U32(s.base10_parse::<u32>().unwrap())),
+ "u64" => Some(ParsedKey::U64(s.base10_parse::<u64>().unwrap())),
+ "u128" => Some(ParsedKey::U128(s.base10_parse::<u128>().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::<u8>().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<H>(&self, state: &mut H)
+ where
+ H: Hasher,
+ {
+ self.parsed.phf_hash(state)
+ }
+}
+
+impl Parse for Key {
+ fn parse(input: ParseStream<'_>) -> parse::Result<Key> {
+ 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<H>(&self, state: &mut H)
+ where
+ H: Hasher,
+ {
+ self.key.phf_hash(state)
+ }
+}
+
+impl Parse for Entry {
+ fn parse(input: ParseStream<'_>) -> parse::Result<Entry> {
+ let key = input.parse()?;
+ input.parse::<Token![=>]>()?;
+ let value = input.parse()?;
+ Ok(Entry { key, value })
+ }
+}
+
+struct Map(Vec<Entry>);
+
+impl Parse for Map {
+ fn parse(input: ParseStream<'_>) -> parse::Result<Map> {
+ let parsed = Punctuated::<Entry, Token![,]>::parse_terminated(input)?;
+ let map = parsed.into_iter().collect::<Vec<_>>();
+ check_duplicates(&map)?;
+ Ok(Map(map))
+ }
+}
+
+struct Set(Vec<Entry>);
+
+impl Parse for Set {
+ fn parse(input: ParseStream<'_>) -> parse::Result<Set> {
+ let parsed = Punctuated::<Key, Token![,]>::parse_terminated(input)?;
+ let set = parsed
+ .into_iter()
+ .map(|key| Entry {
+ key,
+ value: syn::parse_str("()").unwrap(),
+ })
+ .collect::<Vec<_>>();
+ 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()
+}