Skip to content

Commit

Permalink
Merge 16f51bb into 313a320
Browse files Browse the repository at this point in the history
  • Loading branch information
lo48576 committed Aug 24, 2019
2 parents 313a320 + 16f51bb commit f48b1fb
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 1 deletion.
24 changes: 23 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ pub type DefaultStringInterner = StringInterner<Sym>;

/// Caches strings efficiently, with minimal memory footprint and associates them with unique symbols.
/// These symbols allow constant time comparisons and look-ups to the underlying interned strings.
#[derive(Debug, Clone, Eq)]
#[derive(Debug, Eq)]
pub struct StringInterner<S, H = RandomState>
where
S: Symbol,
Expand All @@ -219,6 +219,28 @@ impl Default for StringInterner<Sym, RandomState> {
}
}

// Should be manually cloned.
// See <https://github.com/Robbepop/string-interner/issues/9>.
impl<S, H> Clone for StringInterner<S, H>
where
S: Symbol,
H: Clone + BuildHasher,
{
fn clone(&self) -> Self {
let values = self.values.clone();
let mut map = HashMap::with_capacity_and_hasher(values.len(), self.map.hasher().clone());
// Recreate `InternalStrRef` from the newly cloned `Box<str>`s.
// Use `extend()` to avoid `H: Default` trait bound required by `FromIterator for HashMap`.
map.extend(
values
.iter()
.enumerate()
.map(|(i, s)| (InternalStrRef::from_str(s), S::from_usize(i))),
);
Self { values, map }
}
}

// About `Send` and `Sync` impls for `StringInterner`
// --------------------------------------------------
//
Expand Down
42 changes: 42 additions & 0 deletions src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -373,3 +373,45 @@ mod from_iterator {
);
}
}

// See <https://github.com/Robbepop/string-interner/issues/9>.
mod clone_and_drop {
use super::*;

fn clone_and_drop() -> (DefaultStringInterner, Sym) {
let mut old = DefaultStringInterner::new();
let foo = old.get_or_intern("foo");

let new = old.clone();
let _bar = old.get_or_intern("bar");

(new, foo)
}

#[test]
fn no_use_after_free() {
let (mut new, foo) = clone_and_drop();

// This assert may fail if there are use after free bug.
// See <https://github.com/Robbepop/string-interner/issues/9> for detail.
assert_eq!(
new.get_or_intern("foo"),
foo,
"`foo` should represent the string \"foo\" so they should be equal"
);
}

#[test]
// Test for new (non-`derive`) `Clone` impl.
fn clone() {
let mut old = DefaultStringInterner::new();
let strings = &["foo", "bar", "baz", "qux", "quux", "corge"];
let syms = strings.iter().map(|&s| old.get_or_intern(s)).collect::<Vec<_>>();

let mut new = old.clone();
for (&s, &sym) in strings.iter().zip(&syms) {
assert_eq!(new.resolve(sym), Some(s));
assert_eq!(new.get_or_intern(s), sym);
}
}
}

0 comments on commit f48b1fb

Please sign in to comment.