Skip to content

Commit

Permalink
Support binary literal keys!
Browse files Browse the repository at this point in the history
  • Loading branch information
sfackler committed Jul 10, 2014
1 parent 04b11a1 commit 6bfb12b
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 21 deletions.
8 changes: 8 additions & 0 deletions phf/src/lib.rs
Expand Up @@ -35,6 +35,8 @@ pub fn displace(f1: uint, f2: uint, d1: uint, d2: uint) -> uint {

/// An immutable map constructed at compile time.
///
/// Keys may be either string literals or binary string literals.
///
/// `PhfMap`s may be created with the `phf_map` macro:
///
/// ```rust
Expand Down Expand Up @@ -214,6 +216,8 @@ impl<'a, K, V> Iterator<&'a V> for PhfMapValues<'a, K, V> {

/// An immutable set constructed at compile time.
///
/// Values may be either string literals or binary string literals.
///
/// `PhfSet`s may be created with the `phf_set` macro:
///
/// ```rust
Expand Down Expand Up @@ -321,6 +325,8 @@ impl<'a, T> Iterator<&'a T> for PhfSetValues<'a, T> {

/// An order-preserving immutable map constructed at compile time.
///
/// Keys may be either string literals or binary string literals.
///
/// Unlike a `PhfMap`, the order of entries in a `PhfOrderedMap` is guaranteed
/// to be the order the entries were listed in.
///
Expand Down Expand Up @@ -542,6 +548,8 @@ impl<'a, K, V> ExactSize<&'a V> for PhfOrderedMapValues<'a, K, V> {}

/// An order-preserving immutable set constructed at compile time.
///
/// Values may be either string literals or binary string literals.
///
/// Unlike a `PhfSet`, the order of entries in a `PhfOrderedSet` is guaranteed
/// to be the order the entries were listed in.
///
Expand Down
10 changes: 10 additions & 0 deletions phf/src/test.rs
Expand Up @@ -114,6 +114,16 @@ mod map {
);
assert_eq!(Some(&0), map.find_equiv(&"a".to_string().as_slice()));
}

#[test]
fn test_binary_keys() {
static map: PhfMap<&'static [u8], int> = phf_map! {
b"hello" => 0,
b"world" => 1
};
assert_eq!(Some(&0), map.find(&b"hello"));
assert_eq!(Some(&1), map.find(&b"world"));
}
}

mod set {
Expand Down
39 changes: 18 additions & 21 deletions phf_mac/src/lib.rs
Expand Up @@ -13,20 +13,21 @@ extern crate phf;
extern crate rustc;

use std::collections::HashMap;
use std::fmt;
use std::gc::{Gc, GC};
use std::hash;
use std::hash::{Hash};
use std::os;
use std::rc::Rc;
use syntax::ast;
use syntax::ast::{TokenTree, LitStr, Expr, ExprVec, ExprLit};
use syntax::ast::{TokenTree, LitStr, LitBinary, Expr, ExprVec, ExprLit};
use syntax::codemap::Span;
use syntax::ext::base::{DummyResult,
ExtCtxt,
MacResult,
MacExpr};
use syntax::parse;
use syntax::parse::token::{InternedString, COMMA, EOF, FAT_ARROW};
use syntax::print::pprust;
use rand::{Rng, SeedableRng, XorShiftRng};
use rustc::plugin::Registry;

Expand All @@ -45,21 +46,15 @@ pub fn macro_registrar(reg: &mut Registry) {

#[deriving(PartialEq, Eq, Clone)]
enum Key {
StaticStr(InternedString),
KeyStr(InternedString),
KeyBinary(Rc<Vec<u8>>),
}

impl<S: hash::Writer> Hash<S> for Key {
fn hash(&self, state: &mut S) {
match *self {
StaticStr(ref s) => s.get().hash(state),
}
}
}

impl fmt::Show for Key {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
match *self {
StaticStr(ref s) => s.fmt(fmt),
KeyStr(ref s) => s.get().hash(state),
KeyBinary(ref b) => b.hash(state),
}
}
}
Expand Down Expand Up @@ -151,7 +146,7 @@ fn parse_map(cx: &mut ExtCtxt, tts: &[TokenTree]) -> Option<Vec<Entry>> {
let key = cx.expand_expr(parser.parse_expr());
let key_contents = parse_key(cx, key).unwrap_or_else(|| {
bad = true;
StaticStr(InternedString::new(""))
KeyStr(InternedString::new(""))
});

if !parser.eat(&FAT_ARROW) {
Expand Down Expand Up @@ -198,7 +193,7 @@ fn parse_set(cx: &mut ExtCtxt, tts: &[TokenTree]) -> Option<Vec<Entry>> {
let key = cx.expand_expr(parser.parse_expr());
let key_contents = parse_key(cx, key).unwrap_or_else(|| {
bad = true;
StaticStr(InternedString::new(""))
KeyStr(InternedString::new(""))
});

entries.push(Entry {
Expand Down Expand Up @@ -231,7 +226,8 @@ fn parse_key(cx: &mut ExtCtxt, e: &Expr) -> Option<Key> {
match e.node {
ExprLit(lit) => {
match lit.node {
LitStr(ref s, _) => Some(StaticStr(s.clone())),
LitStr(ref s, _) => Some(KeyStr(s.clone())),
LitBinary(ref b) => Some(KeyBinary(b.clone())),
_ => {
cx.span_err(e.span, "expected string literal");
None
Expand All @@ -249,18 +245,19 @@ fn has_duplicates(cx: &mut ExtCtxt, sp: Span, entries: &[Entry]) -> bool {
let mut dups = false;
let mut strings = HashMap::new();
for entry in entries.iter() {
let spans = strings.find_or_insert(entry.key_contents.clone(), vec![]);
let &(ref mut spans, _) = strings.find_or_insert(&entry.key_contents,
(vec![], &entry.key));
spans.push(entry.key.span);
}

for (key, spans) in strings.iter() {
for &(ref spans, key) in strings.values() {
if spans.len() == 1 {
continue;
}

dups = true;
cx.span_err(sp,
format!("duplicate key `{}`", key).as_slice());
cx.span_err(sp, format!("duplicate key {}",
pprust::expr_to_str(&**key)).as_slice());
for span in spans.iter() {
cx.span_note(*span, "one occurrence here");
}
Expand Down Expand Up @@ -333,15 +330,15 @@ fn try_generate_hash(entries: &[Entry], rng: &mut XorShiftRng)
let mut try_map = HashMap::new();
'buckets: for bucket in buckets.iter() {
for d1 in range(0, table_len) {
'disps_l: for d2 in range(0, table_len) {
'disps: for d2 in range(0, table_len) {
try_map.clear();
for &key in bucket.keys.iter() {
let idx = phf::displace(hashes.get(key).f1,
hashes.get(key).f2,
d1,
d2) % table_len;
if map.get(idx).is_some() || try_map.find(&idx).is_some() {
continue 'disps_l;
continue 'disps;
}
try_map.insert(idx, key);
}
Expand Down

0 comments on commit 6bfb12b

Please sign in to comment.