collections: allow HashMap
to work with generic hashers
This commit is contained in:
parent
f01a9a8d02
commit
72b5e30f6c
6 changed files with 181 additions and 110 deletions
|
@ -55,7 +55,8 @@
|
||||||
use std::cmp::max;
|
use std::cmp::max;
|
||||||
use std::default::Default;
|
use std::default::Default;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::hash::{Hash, Hasher, sip};
|
use std::hash::{Hash, Hasher};
|
||||||
|
use std::hash::sip::{SipState, SipHasher};
|
||||||
use std::iter::{FilterMap, Chain, Repeat, Zip};
|
use std::iter::{FilterMap, Chain, Repeat, Zip};
|
||||||
use std::iter;
|
use std::iter;
|
||||||
use std::mem::replace;
|
use std::mem::replace;
|
||||||
|
@ -78,10 +79,10 @@ struct Bucket<K,V> {
|
||||||
/// hash function for internal state. This means that the order of all hash maps
|
/// hash function for internal state. This means that the order of all hash maps
|
||||||
/// is randomized by keying each hash map randomly on creation.
|
/// is randomized by keying each hash map randomly on creation.
|
||||||
///
|
///
|
||||||
/// It is required that the keys implement the `Eq` and `Hash` traits.
|
/// It is required that the keys implement the `Eq` and `Hash` traits, although
|
||||||
pub struct HashMap<K,V> {
|
/// this can frequently be achieved by using `#[deriving(Eq, Hash)]`.
|
||||||
priv k0: u64,
|
pub struct HashMap<K, V, H = SipHasher> {
|
||||||
priv k1: u64,
|
priv hasher: H,
|
||||||
priv resize_at: uint,
|
priv resize_at: uint,
|
||||||
priv size: uint,
|
priv size: uint,
|
||||||
priv buckets: Vec<Option<Bucket<K, V>>>
|
priv buckets: Vec<Option<Bucket<K, V>>>
|
||||||
|
@ -98,7 +99,7 @@ fn resize_at(capacity: uint) -> uint {
|
||||||
(capacity * 3) / 4
|
(capacity * 3) / 4
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<K:Hash + Eq, V> HashMap<K, V> {
|
impl<K: Hash<S> + Eq, V, S, H: Hasher<S>> HashMap<K, V, H> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn to_bucket(&self, h: uint) -> uint {
|
fn to_bucket(&self, h: uint) -> uint {
|
||||||
// A good hash function with entropy spread over all of the
|
// A good hash function with entropy spread over all of the
|
||||||
|
@ -127,14 +128,13 @@ impl<K:Hash + Eq, V> HashMap<K, V> {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn bucket_for_key(&self, k: &K) -> SearchResult {
|
fn bucket_for_key(&self, k: &K) -> SearchResult {
|
||||||
let hash = sip::hash_with_keys(self.k0, self.k1, k) as uint;
|
let hash = self.hasher.hash(k) as uint;
|
||||||
self.bucket_for_key_with_hash(hash, k)
|
self.bucket_for_key_with_hash(hash, k)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn bucket_for_key_equiv<Q:Hash + Equiv<K>>(&self, k: &Q)
|
fn bucket_for_key_equiv<Q:Hash<S> + Equiv<K>>(&self, k: &Q) -> SearchResult {
|
||||||
-> SearchResult {
|
let hash = self.hasher.hash(k) as uint;
|
||||||
let hash = sip::hash_with_keys(self.k0, self.k1, k) as uint;
|
|
||||||
self.bucket_for_key_with_hash_equiv(hash, k)
|
self.bucket_for_key_with_hash_equiv(hash, k)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -285,12 +285,12 @@ impl<K:Hash + Eq, V> HashMap<K, V> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<K:Hash + Eq,V> Container for HashMap<K, V> {
|
impl<K: Hash<S> + Eq, V, S, H: Hasher<S>> Container for HashMap<K, V, H> {
|
||||||
/// Return the number of elements in the map
|
/// Return the number of elements in the map
|
||||||
fn len(&self) -> uint { self.size }
|
fn len(&self) -> uint { self.size }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<K:Hash + Eq,V> Mutable for HashMap<K, V> {
|
impl<K: Hash<S> + Eq, V, S, H: Hasher<S>> Mutable for HashMap<K, V, H> {
|
||||||
/// Clear the map, removing all key-value pairs.
|
/// Clear the map, removing all key-value pairs.
|
||||||
fn clear(&mut self) {
|
fn clear(&mut self) {
|
||||||
for bkt in self.buckets.as_mut_slice().mut_iter() {
|
for bkt in self.buckets.as_mut_slice().mut_iter() {
|
||||||
|
@ -300,7 +300,7 @@ impl<K:Hash + Eq,V> Mutable for HashMap<K, V> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<K:Hash + Eq,V> Map<K, V> for HashMap<K, V> {
|
impl<K: Hash<S> + Eq, V, S, H: Hasher<S>> Map<K, V> for HashMap<K, V, H> {
|
||||||
/// Return a reference to the value corresponding to the key
|
/// Return a reference to the value corresponding to the key
|
||||||
fn find<'a>(&'a self, k: &K) -> Option<&'a V> {
|
fn find<'a>(&'a self, k: &K) -> Option<&'a V> {
|
||||||
match self.bucket_for_key(k) {
|
match self.bucket_for_key(k) {
|
||||||
|
@ -310,7 +310,7 @@ impl<K:Hash + Eq,V> Map<K, V> for HashMap<K, V> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<K:Hash + Eq,V> MutableMap<K, V> for HashMap<K, V> {
|
impl<K: Hash<S> + Eq, V, S, H: Hasher<S>> MutableMap<K, V> for HashMap<K, V, H> {
|
||||||
/// Return a mutable reference to the value corresponding to the key
|
/// Return a mutable reference to the value corresponding to the key
|
||||||
fn find_mut<'a>(&'a mut self, k: &K) -> Option<&'a mut V> {
|
fn find_mut<'a>(&'a mut self, k: &K) -> Option<&'a mut V> {
|
||||||
let idx = match self.bucket_for_key(k) {
|
let idx = match self.bucket_for_key(k) {
|
||||||
|
@ -335,21 +335,21 @@ impl<K:Hash + Eq,V> MutableMap<K, V> for HashMap<K, V> {
|
||||||
self.expand();
|
self.expand();
|
||||||
}
|
}
|
||||||
|
|
||||||
let hash = sip::hash_with_keys(self.k0, self.k1, &k) as uint;
|
let hash = self.hasher.hash(&k) as uint;
|
||||||
self.insert_internal(hash, k, v)
|
self.insert_internal(hash, k, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Removes a key from the map, returning the value at the key if the key
|
/// Removes a key from the map, returning the value at the key if the key
|
||||||
/// was previously in the map.
|
/// was previously in the map.
|
||||||
fn pop(&mut self, k: &K) -> Option<V> {
|
fn pop(&mut self, k: &K) -> Option<V> {
|
||||||
let hash = sip::hash_with_keys(self.k0, self.k1, k) as uint;
|
let hash = self.hasher.hash(k) as uint;
|
||||||
self.pop_internal(hash, k)
|
self.pop_internal(hash, k)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<K: Hash + Eq, V> HashMap<K, V> {
|
impl<K: Hash + Eq, V> HashMap<K, V> {
|
||||||
/// Create an empty HashMap
|
/// Create an empty HashMap
|
||||||
pub fn new() -> HashMap<K, V> {
|
pub fn new() -> HashMap<K, V, SipHasher> {
|
||||||
HashMap::with_capacity(INITIAL_CAPACITY)
|
HashMap::with_capacity(INITIAL_CAPACITY)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -357,20 +357,27 @@ impl<K: Hash + Eq, V> HashMap<K, V> {
|
||||||
/// elements in the hash table.
|
/// elements in the hash table.
|
||||||
pub fn with_capacity(capacity: uint) -> HashMap<K, V> {
|
pub fn with_capacity(capacity: uint) -> HashMap<K, V> {
|
||||||
let mut r = rand::task_rng();
|
let mut r = rand::task_rng();
|
||||||
HashMap::with_capacity_and_keys(r.gen(), r.gen(), capacity)
|
let hasher = SipHasher::new_with_keys(r.gen(), r.gen());
|
||||||
|
HashMap::with_capacity_and_hasher(hasher, capacity)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<K: Hash<S> + Eq, V, S, H: Hasher<S>> HashMap<K, V, H> {
|
||||||
|
pub fn with_hasher(hasher: H) -> HashMap<K, V, H> {
|
||||||
|
HashMap::with_capacity_and_hasher(hasher, INITIAL_CAPACITY)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create an empty HashMap with space for at least `capacity`
|
/// Create an empty HashMap with space for at least `capacity`
|
||||||
/// elements, using `k0` and `k1` as the keys.
|
/// elements, using `hasher` to hash the keys.
|
||||||
///
|
///
|
||||||
/// Warning: `k0` and `k1` are normally randomly generated, and
|
/// Warning: `hasher` is normally randomly generated, and
|
||||||
/// are designed to allow HashMaps to be resistant to attacks that
|
/// is designed to allow HashMaps to be resistant to attacks that
|
||||||
/// cause many collisions and very poor performance. Setting them
|
/// cause many collisions and very poor performance. Setting it
|
||||||
/// manually using this function can expose a DoS attack vector.
|
/// manually using this function can expose a DoS attack vector.
|
||||||
pub fn with_capacity_and_keys(k0: u64, k1: u64, capacity: uint) -> HashMap<K, V> {
|
pub fn with_capacity_and_hasher(hasher: H, capacity: uint) -> HashMap<K, V, H> {
|
||||||
let cap = max(INITIAL_CAPACITY, capacity);
|
let cap = max(INITIAL_CAPACITY, capacity);
|
||||||
HashMap {
|
HashMap {
|
||||||
k0: k0, k1: k1,
|
hasher: hasher,
|
||||||
resize_at: resize_at(cap),
|
resize_at: resize_at(cap),
|
||||||
size: 0,
|
size: 0,
|
||||||
buckets: Vec::from_fn(cap, |_| None)
|
buckets: Vec::from_fn(cap, |_| None)
|
||||||
|
@ -442,7 +449,7 @@ impl<K: Hash + Eq, V> HashMap<K, V> {
|
||||||
self.expand();
|
self.expand();
|
||||||
}
|
}
|
||||||
|
|
||||||
let hash = sip::hash_with_keys(self.k0, self.k1, &k) as uint;
|
let hash = self.hasher.hash(&k) as uint;
|
||||||
let idx = match self.bucket_for_key_with_hash(hash, &k) {
|
let idx = match self.bucket_for_key_with_hash(hash, &k) {
|
||||||
TableFull => fail!("Internal logic error"),
|
TableFull => fail!("Internal logic error"),
|
||||||
FoundEntry(idx) => { found(&k, self.mut_value_for_bucket(idx), a); idx }
|
FoundEntry(idx) => { found(&k, self.mut_value_for_bucket(idx), a); idx }
|
||||||
|
@ -502,7 +509,7 @@ impl<K: Hash + Eq, V> HashMap<K, V> {
|
||||||
|
|
||||||
/// Return true if the map contains a value for the specified key,
|
/// Return true if the map contains a value for the specified key,
|
||||||
/// using equivalence
|
/// using equivalence
|
||||||
pub fn contains_key_equiv<Q:Hash + Equiv<K>>(&self, key: &Q) -> bool {
|
pub fn contains_key_equiv<Q:Hash<S> + Equiv<K>>(&self, key: &Q) -> bool {
|
||||||
match self.bucket_for_key_equiv(key) {
|
match self.bucket_for_key_equiv(key) {
|
||||||
FoundEntry(_) => {true}
|
FoundEntry(_) => {true}
|
||||||
TableFull | FoundHole(_) => {false}
|
TableFull | FoundHole(_) => {false}
|
||||||
|
@ -511,8 +518,7 @@ impl<K: Hash + Eq, V> HashMap<K, V> {
|
||||||
|
|
||||||
/// Return the value corresponding to the key in the map, using
|
/// Return the value corresponding to the key in the map, using
|
||||||
/// equivalence
|
/// equivalence
|
||||||
pub fn find_equiv<'a, Q:Hash + Equiv<K>>(&'a self, k: &Q)
|
pub fn find_equiv<'a, Q:Hash<S> + Equiv<K>>(&'a self, k: &Q) -> Option<&'a V> {
|
||||||
-> Option<&'a V> {
|
|
||||||
match self.bucket_for_key_equiv(k) {
|
match self.bucket_for_key_equiv(k) {
|
||||||
FoundEntry(idx) => Some(self.value_for_bucket(idx)),
|
FoundEntry(idx) => Some(self.value_for_bucket(idx)),
|
||||||
TableFull | FoundHole(_) => None,
|
TableFull | FoundHole(_) => None,
|
||||||
|
@ -552,7 +558,7 @@ impl<K: Hash + Eq, V> HashMap<K, V> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<K: Hash + Eq, V: Clone> HashMap<K, V> {
|
impl<K: Hash<S> + Eq, V: Clone, S, H: Hasher<S>> HashMap<K, V, H> {
|
||||||
/// Like `find`, but returns a copy of the value.
|
/// Like `find`, but returns a copy of the value.
|
||||||
pub fn find_copy(&self, k: &K) -> Option<V> {
|
pub fn find_copy(&self, k: &K) -> Option<V> {
|
||||||
self.find(k).map(|v| (*v).clone())
|
self.find(k).map(|v| (*v).clone())
|
||||||
|
@ -564,8 +570,8 @@ impl<K: Hash + Eq, V: Clone> HashMap<K, V> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<K:Hash + Eq,V:Eq> Eq for HashMap<K, V> {
|
impl<K: Hash<S> + Eq, V: Eq, S, H: Hasher<S>> Eq for HashMap<K, V, H> {
|
||||||
fn eq(&self, other: &HashMap<K, V>) -> bool {
|
fn eq(&self, other: &HashMap<K, V, H>) -> bool {
|
||||||
if self.len() != other.len() { return false; }
|
if self.len() != other.len() { return false; }
|
||||||
|
|
||||||
self.iter().all(|(key, value)| {
|
self.iter().all(|(key, value)| {
|
||||||
|
@ -576,12 +582,12 @@ impl<K:Hash + Eq,V:Eq> Eq for HashMap<K, V> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ne(&self, other: &HashMap<K, V>) -> bool { !self.eq(other) }
|
fn ne(&self, other: &HashMap<K, V, H>) -> bool { !self.eq(other) }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<K:Hash + Eq + Clone,V:Clone> Clone for HashMap<K,V> {
|
impl<K: Hash<S> + Eq + Clone, V:Clone, S, H: Hasher<S> + Clone> Clone for HashMap<K, V, H> {
|
||||||
fn clone(&self) -> HashMap<K,V> {
|
fn clone(&self) -> HashMap<K, V, H> {
|
||||||
let mut new_map = HashMap::with_capacity(self.len());
|
let mut new_map = HashMap::with_capacity_and_hasher(self.hasher.clone(), self.len());
|
||||||
for (key, value) in self.iter() {
|
for (key, value) in self.iter() {
|
||||||
new_map.insert((*key).clone(), (*value).clone());
|
new_map.insert((*key).clone(), (*value).clone());
|
||||||
}
|
}
|
||||||
|
@ -589,7 +595,7 @@ impl<K:Hash + Eq + Clone,V:Clone> Clone for HashMap<K,V> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<A: fmt::Show + Hash + Eq, B: fmt::Show> fmt::Show for HashMap<A, B> {
|
impl<K: fmt::Show + Hash<S> + Eq, V: fmt::Show, S, H: Hasher<S>> fmt::Show for HashMap<K, V, H> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
try!(write!(f.buf, r"\{"))
|
try!(write!(f.buf, r"\{"))
|
||||||
let mut first = true;
|
let mut first = true;
|
||||||
|
@ -705,16 +711,16 @@ impl<K> Iterator<K> for SetMoveItems<K> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<K: Eq + Hash, V> FromIterator<(K, V)> for HashMap<K, V> {
|
impl<K: Hash<S> + Eq, V, S, H: Hasher<S> + Default> FromIterator<(K, V)> for HashMap<K, V, H> {
|
||||||
fn from_iterator<T: Iterator<(K, V)>>(iter: &mut T) -> HashMap<K, V> {
|
fn from_iterator<T: Iterator<(K, V)>>(iter: &mut T) -> HashMap<K, V, H> {
|
||||||
let (lower, _) = iter.size_hint();
|
let (lower, _) = iter.size_hint();
|
||||||
let mut map = HashMap::with_capacity(lower);
|
let mut map = HashMap::with_capacity_and_hasher(Default::default(), lower);
|
||||||
map.extend(iter);
|
map.extend(iter);
|
||||||
map
|
map
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<K: Eq + Hash, V> Extendable<(K, V)> for HashMap<K, V> {
|
impl<K: Hash<S> + Eq, V, S, H: Hasher<S> + Default> Extendable<(K, V)> for HashMap<K, V, H> {
|
||||||
fn extend<T: Iterator<(K, V)>>(&mut self, iter: &mut T) {
|
fn extend<T: Iterator<(K, V)>>(&mut self, iter: &mut T) {
|
||||||
for (k, v) in *iter {
|
for (k, v) in *iter {
|
||||||
self.insert(k, v);
|
self.insert(k, v);
|
||||||
|
@ -722,54 +728,56 @@ impl<K: Eq + Hash, V> Extendable<(K, V)> for HashMap<K, V> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<K: Eq + Hash, V> Default for HashMap<K, V> {
|
impl<K: Hash<S> + Eq, V, S, H: Hasher<S> + Default> Default for HashMap<K, V, H> {
|
||||||
fn default() -> HashMap<K, V> { HashMap::new() }
|
fn default() -> HashMap<K, V, H> {
|
||||||
|
HashMap::with_capacity_and_hasher(Default::default(), INITIAL_CAPACITY)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An implementation of a hash set using the underlying representation of a
|
/// An implementation of a hash set using the underlying representation of a
|
||||||
/// HashMap where the value is (). As with the `HashMap` type, a `HashSet`
|
/// HashMap where the value is (). As with the `HashMap` type, a `HashSet`
|
||||||
/// requires that the elements implement the `Eq` and `Hash` traits.
|
/// requires that the elements implement the `Eq` and `Hash` traits.
|
||||||
pub struct HashSet<T> {
|
pub struct HashSet<T, H = SipHasher> {
|
||||||
priv map: HashMap<T, ()>
|
priv map: HashMap<T, (), H>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T:Hash + Eq> Eq for HashSet<T> {
|
impl<T: Hash<S> + Eq, S, H: Hasher<S>> Eq for HashSet<T, H> {
|
||||||
fn eq(&self, other: &HashSet<T>) -> bool { self.map == other.map }
|
fn eq(&self, other: &HashSet<T, H>) -> bool { self.map == other.map }
|
||||||
fn ne(&self, other: &HashSet<T>) -> bool { self.map != other.map }
|
fn ne(&self, other: &HashSet<T, H>) -> bool { self.map != other.map }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T:Hash + Eq> Container for HashSet<T> {
|
impl<T: Hash<S> + Eq, S, H: Hasher<S>> Container for HashSet<T, H> {
|
||||||
/// Return the number of elements in the set
|
/// Return the number of elements in the set
|
||||||
fn len(&self) -> uint { self.map.len() }
|
fn len(&self) -> uint { self.map.len() }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T:Hash + Eq> Mutable for HashSet<T> {
|
impl<T: Hash<S> + Eq, S, H: Hasher<S>> Mutable for HashSet<T, H> {
|
||||||
/// Clear the set, removing all values.
|
/// Clear the set, removing all values.
|
||||||
fn clear(&mut self) { self.map.clear() }
|
fn clear(&mut self) { self.map.clear() }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T:Hash + Eq> Set<T> for HashSet<T> {
|
impl<T: Hash<S> + Eq, S, H: Hasher<S>> Set<T> for HashSet<T, H> {
|
||||||
/// Return true if the set contains a value
|
/// Return true if the set contains a value
|
||||||
fn contains(&self, value: &T) -> bool { self.map.contains_key(value) }
|
fn contains(&self, value: &T) -> bool { self.map.contains_key(value) }
|
||||||
|
|
||||||
/// Return true if the set has no elements in common with `other`.
|
/// Return true if the set has no elements in common with `other`.
|
||||||
/// This is equivalent to checking for an empty intersection.
|
/// This is equivalent to checking for an empty intersection.
|
||||||
fn is_disjoint(&self, other: &HashSet<T>) -> bool {
|
fn is_disjoint(&self, other: &HashSet<T, H>) -> bool {
|
||||||
self.iter().all(|v| !other.contains(v))
|
self.iter().all(|v| !other.contains(v))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return true if the set is a subset of another
|
/// Return true if the set is a subset of another
|
||||||
fn is_subset(&self, other: &HashSet<T>) -> bool {
|
fn is_subset(&self, other: &HashSet<T, H>) -> bool {
|
||||||
self.iter().all(|v| other.contains(v))
|
self.iter().all(|v| other.contains(v))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return true if the set is a superset of another
|
/// Return true if the set is a superset of another
|
||||||
fn is_superset(&self, other: &HashSet<T>) -> bool {
|
fn is_superset(&self, other: &HashSet<T, H>) -> bool {
|
||||||
other.is_subset(self)
|
other.is_subset(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T:Hash + Eq> MutableSet<T> for HashSet<T> {
|
impl<T: Hash<S> + Eq, S, H: Hasher<S>> MutableSet<T> for HashSet<T, H> {
|
||||||
/// Add a value to the set. Return true if the value was not already
|
/// Add a value to the set. Return true if the value was not already
|
||||||
/// present in the set.
|
/// present in the set.
|
||||||
fn insert(&mut self, value: T) -> bool { self.map.insert(value, ()) }
|
fn insert(&mut self, value: T) -> bool { self.map.insert(value, ()) }
|
||||||
|
@ -779,27 +787,35 @@ impl<T:Hash + Eq> MutableSet<T> for HashSet<T> {
|
||||||
fn remove(&mut self, value: &T) -> bool { self.map.remove(value) }
|
fn remove(&mut self, value: &T) -> bool { self.map.remove(value) }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T:Hash + Eq> HashSet<T> {
|
impl<T: Hash<SipState> + Eq> HashSet<T, SipHasher> {
|
||||||
/// Create an empty HashSet
|
/// Create an empty HashSet
|
||||||
pub fn new() -> HashSet<T> {
|
pub fn new() -> HashSet<T, SipHasher> {
|
||||||
HashSet::with_capacity(INITIAL_CAPACITY)
|
HashSet::with_capacity(INITIAL_CAPACITY)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create an empty HashSet with space for at least `n` elements in
|
/// Create an empty HashSet with space for at least `n` elements in
|
||||||
/// the hash table.
|
/// the hash table.
|
||||||
pub fn with_capacity(capacity: uint) -> HashSet<T> {
|
pub fn with_capacity(capacity: uint) -> HashSet<T, SipHasher> {
|
||||||
HashSet { map: HashMap::with_capacity(capacity) }
|
HashSet { map: HashMap::with_capacity(capacity) }
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Hash<S> + Eq, S, H: Hasher<S>> HashSet<T, H> {
|
||||||
|
pub fn with_hasher(hasher: H) -> HashSet<T, H> {
|
||||||
|
HashSet::with_capacity_and_hasher(hasher, INITIAL_CAPACITY)
|
||||||
|
}
|
||||||
|
|
||||||
/// Create an empty HashSet with space for at least `capacity`
|
/// Create an empty HashSet with space for at least `capacity`
|
||||||
/// elements in the hash table, using `k0` and `k1` as the keys.
|
/// elements in the hash table, using `k0` and `k1` as the keys.
|
||||||
///
|
///
|
||||||
/// Warning: `k0` and `k1` are normally randomly generated, and
|
/// Warning: `hasher` is normally randomly generated, and
|
||||||
/// are designed to allow HashSets to be resistant to attacks that
|
/// are designed to allow HashSets to be resistant to attacks that
|
||||||
/// cause many collisions and very poor performance. Setting them
|
/// cause many collisions and very poor performance. Setting them
|
||||||
/// manually using this function can expose a DoS attack vector.
|
/// manually using this function can expose a DoS attack vector.
|
||||||
pub fn with_capacity_and_keys(k0: u64, k1: u64, capacity: uint) -> HashSet<T> {
|
pub fn with_capacity_and_hasher(hasher: H, capacity: uint) -> HashSet<T, H> {
|
||||||
HashSet { map: HashMap::with_capacity_and_keys(k0, k1, capacity) }
|
HashSet {
|
||||||
|
map: HashMap::with_capacity_and_hasher(hasher, capacity)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reserve space for at least `n` elements in the hash table.
|
/// Reserve space for at least `n` elements in the hash table.
|
||||||
|
@ -809,7 +825,7 @@ impl<T:Hash + Eq> HashSet<T> {
|
||||||
|
|
||||||
/// Returns true if the hash set contains a value equivalent to the
|
/// Returns true if the hash set contains a value equivalent to the
|
||||||
/// given query value.
|
/// given query value.
|
||||||
pub fn contains_equiv<Q:Hash + Equiv<T>>(&self, value: &Q) -> bool {
|
pub fn contains_equiv<Q: Hash<S> + Equiv<T>>(&self, value: &Q) -> bool {
|
||||||
self.map.contains_key_equiv(value)
|
self.map.contains_key_equiv(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -827,7 +843,7 @@ impl<T:Hash + Eq> HashSet<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Visit the values representing the difference
|
/// Visit the values representing the difference
|
||||||
pub fn difference<'a>(&'a self, other: &'a HashSet<T>) -> SetAlgebraItems<'a, T> {
|
pub fn difference<'a>(&'a self, other: &'a HashSet<T, H>) -> SetAlgebraItems<'a, T, H> {
|
||||||
Repeat::new(other)
|
Repeat::new(other)
|
||||||
.zip(self.iter())
|
.zip(self.iter())
|
||||||
.filter_map(|(other, elt)| {
|
.filter_map(|(other, elt)| {
|
||||||
|
@ -836,14 +852,14 @@ impl<T:Hash + Eq> HashSet<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Visit the values representing the symmetric difference
|
/// Visit the values representing the symmetric difference
|
||||||
pub fn symmetric_difference<'a>(&'a self, other: &'a HashSet<T>)
|
pub fn symmetric_difference<'a>(&'a self, other: &'a HashSet<T, H>)
|
||||||
-> Chain<SetAlgebraItems<'a, T>, SetAlgebraItems<'a, T>> {
|
-> Chain<SetAlgebraItems<'a, T, H>, SetAlgebraItems<'a, T, H>> {
|
||||||
self.difference(other).chain(other.difference(self))
|
self.difference(other).chain(other.difference(self))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Visit the values representing the intersection
|
/// Visit the values representing the intersection
|
||||||
pub fn intersection<'a>(&'a self, other: &'a HashSet<T>)
|
pub fn intersection<'a>(&'a self, other: &'a HashSet<T, H>)
|
||||||
-> SetAlgebraItems<'a, T> {
|
-> SetAlgebraItems<'a, T, H> {
|
||||||
Repeat::new(other)
|
Repeat::new(other)
|
||||||
.zip(self.iter())
|
.zip(self.iter())
|
||||||
.filter_map(|(other, elt)| {
|
.filter_map(|(other, elt)| {
|
||||||
|
@ -852,22 +868,22 @@ impl<T:Hash + Eq> HashSet<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Visit the values representing the union
|
/// Visit the values representing the union
|
||||||
pub fn union<'a>(&'a self, other: &'a HashSet<T>)
|
pub fn union<'a>(&'a self, other: &'a HashSet<T, H>)
|
||||||
-> Chain<SetItems<'a, T>, SetAlgebraItems<'a, T>> {
|
-> Chain<SetItems<'a, T>, SetAlgebraItems<'a, T, H>> {
|
||||||
self.iter().chain(other.difference(self))
|
self.iter().chain(other.difference(self))
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T:Hash + Eq + Clone> Clone for HashSet<T> {
|
impl<T: Hash<S> + Eq + Clone, S, H: Hasher<S> + Clone> Clone for HashSet<T, H> {
|
||||||
fn clone(&self) -> HashSet<T> {
|
fn clone(&self) -> HashSet<T, H> {
|
||||||
HashSet {
|
HashSet {
|
||||||
map: self.map.clone()
|
map: self.map.clone()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<A: fmt::Show + Hash + Eq> fmt::Show for HashSet<A> {
|
impl<T: fmt::Show + Hash<S> + Eq, S, H: Hasher<S>> fmt::Show for HashSet<T, H> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
try!(write!(f.buf, r"\{"))
|
try!(write!(f.buf, r"\{"))
|
||||||
let mut first = true;
|
let mut first = true;
|
||||||
|
@ -883,33 +899,37 @@ impl<A: fmt::Show + Hash + Eq> fmt::Show for HashSet<A> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<K: Eq + Hash> FromIterator<K> for HashSet<K> {
|
impl<T: Hash<S> + Eq, S, H: Hasher<S> + Default> FromIterator<T> for HashSet<T, H> {
|
||||||
fn from_iterator<T: Iterator<K>>(iter: &mut T) -> HashSet<K> {
|
fn from_iterator<Iter: Iterator<T>>(iter: &mut Iter) -> HashSet<T, H> {
|
||||||
let (lower, _) = iter.size_hint();
|
let (lower, _) = iter.size_hint();
|
||||||
let mut set = HashSet::with_capacity(lower);
|
let mut set = HashSet::with_capacity_and_hasher(Default::default(), lower);
|
||||||
set.extend(iter);
|
set.extend(iter);
|
||||||
set
|
set
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<K: Eq + Hash> Extendable<K> for HashSet<K> {
|
impl<T: Hash<S> + Eq, S, H: Hasher<S> + Default> Extendable<T> for HashSet<T, H> {
|
||||||
fn extend<T: Iterator<K>>(&mut self, iter: &mut T) {
|
fn extend<Iter: Iterator<T>>(&mut self, iter: &mut Iter) {
|
||||||
for k in *iter {
|
for k in *iter {
|
||||||
self.insert(k);
|
self.insert(k);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<K: Eq + Hash> Default for HashSet<K> {
|
impl<T: Hash<S> + Eq, S, H: Hasher<S> + Default> Default for HashSet<T, H> {
|
||||||
fn default() -> HashSet<K> { HashSet::new() }
|
fn default() -> HashSet<T, H> {
|
||||||
|
HashSet {
|
||||||
|
map: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// `Repeat` is used to feed the filter closure an explicit capture
|
// `Repeat` is used to feed the filter closure an explicit capture
|
||||||
// of a reference to the other set
|
// of a reference to the other set
|
||||||
/// Set operations iterator
|
/// Set operations iterator
|
||||||
pub type SetAlgebraItems<'a, T> =
|
pub type SetAlgebraItems<'a, T, H> =
|
||||||
FilterMap<'static,(&'a HashSet<T>, &'a T), &'a T,
|
FilterMap<'static,(&'a HashSet<T, H>, &'a T), &'a T,
|
||||||
Zip<Repeat<&'a HashSet<T>>,SetItems<'a,T>>>;
|
Zip<Repeat<&'a HashSet<T, H>>, SetItems<'a, T>>>;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test_map {
|
mod test_map {
|
||||||
|
|
|
@ -17,7 +17,11 @@
|
||||||
#[crate_type = "dylib"];
|
#[crate_type = "dylib"];
|
||||||
#[license = "MIT/ASL2"];
|
#[license = "MIT/ASL2"];
|
||||||
|
|
||||||
#[feature(macro_rules, managed_boxes)];
|
#[feature(macro_rules, managed_boxes, default_type_params)];
|
||||||
|
|
||||||
|
// NOTE remove the following two attributes after the next snapshot.
|
||||||
|
#[allow(unrecognized_lint)];
|
||||||
|
#[allow(default_type_param_usage)];
|
||||||
|
|
||||||
#[cfg(test)] extern crate test;
|
#[cfg(test)] extern crate test;
|
||||||
|
|
||||||
|
|
|
@ -4926,7 +4926,7 @@ pub fn trait_method_of_method(tcx: ctxt,
|
||||||
/// Creates a hash of the type `t` which will be the same no matter what crate
|
/// Creates a hash of the type `t` which will be the same no matter what crate
|
||||||
/// context it's calculated within. This is used by the `type_id` intrinsic.
|
/// context it's calculated within. This is used by the `type_id` intrinsic.
|
||||||
pub fn hash_crate_independent(tcx: ctxt, t: t, local_hash: ~str) -> u64 {
|
pub fn hash_crate_independent(tcx: ctxt, t: t, local_hash: ~str) -> u64 {
|
||||||
let mut state = sip::SipState::new(0, 0);
|
let mut state = sip::SipState::new();
|
||||||
macro_rules! byte( ($b:expr) => { ($b as u8).hash(&mut state) } );
|
macro_rules! byte( ($b:expr) => { ($b as u8).hash(&mut state) } );
|
||||||
macro_rules! hash( ($e:expr) => { $e.hash(&mut state) } );
|
macro_rules! hash( ($e:expr) => { $e.hash(&mut state) } );
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,8 @@
|
||||||
//! Implementations of serialization for structures found in libcollections
|
//! Implementations of serialization for structures found in libcollections
|
||||||
|
|
||||||
use std::uint;
|
use std::uint;
|
||||||
use std::hash::Hash;
|
use std::default::Default;
|
||||||
|
use std::hash::{Hash, Hasher};
|
||||||
|
|
||||||
use {Decodable, Encodable, Decoder, Encoder};
|
use {Decodable, Encodable, Decoder, Encoder};
|
||||||
use collections::{DList, RingBuf, TreeMap, TreeSet, Deque, HashMap, HashSet,
|
use collections::{DList, RingBuf, TreeMap, TreeSet, Deque, HashMap, HashSet,
|
||||||
|
@ -164,9 +165,11 @@ impl<
|
||||||
|
|
||||||
impl<
|
impl<
|
||||||
E: Encoder,
|
E: Encoder,
|
||||||
K: Encodable<E> + Hash + Eq,
|
K: Encodable<E> + Hash<S> + Eq,
|
||||||
V: Encodable<E>
|
V: Encodable<E>,
|
||||||
> Encodable<E> for HashMap<K, V> {
|
S,
|
||||||
|
H: Hasher<S>
|
||||||
|
> Encodable<E> for HashMap<K, V, H> {
|
||||||
fn encode(&self, e: &mut E) {
|
fn encode(&self, e: &mut E) {
|
||||||
e.emit_map(self.len(), |e| {
|
e.emit_map(self.len(), |e| {
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
|
@ -181,12 +184,15 @@ impl<
|
||||||
|
|
||||||
impl<
|
impl<
|
||||||
D: Decoder,
|
D: Decoder,
|
||||||
K: Decodable<D> + Hash + Eq,
|
K: Decodable<D> + Hash<S> + Eq,
|
||||||
V: Decodable<D>
|
V: Decodable<D>,
|
||||||
> Decodable<D> for HashMap<K, V> {
|
S,
|
||||||
fn decode(d: &mut D) -> HashMap<K, V> {
|
H: Hasher<S> + Default
|
||||||
|
> Decodable<D> for HashMap<K, V, H> {
|
||||||
|
fn decode(d: &mut D) -> HashMap<K, V, H> {
|
||||||
d.read_map(|d, len| {
|
d.read_map(|d, len| {
|
||||||
let mut map = HashMap::with_capacity(len);
|
let hasher = Default::default();
|
||||||
|
let mut map = HashMap::with_capacity_and_hasher(hasher, len);
|
||||||
for i in range(0u, len) {
|
for i in range(0u, len) {
|
||||||
let key = d.read_map_elt_key(i, |d| Decodable::decode(d));
|
let key = d.read_map_elt_key(i, |d| Decodable::decode(d));
|
||||||
let val = d.read_map_elt_val(i, |d| Decodable::decode(d));
|
let val = d.read_map_elt_val(i, |d| Decodable::decode(d));
|
||||||
|
@ -198,10 +204,12 @@ impl<
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<
|
impl<
|
||||||
S: Encoder,
|
E: Encoder,
|
||||||
T: Encodable<S> + Hash + Eq
|
T: Encodable<E> + Hash<S> + Eq,
|
||||||
> Encodable<S> for HashSet<T> {
|
S,
|
||||||
fn encode(&self, s: &mut S) {
|
H: Hasher<S>
|
||||||
|
> Encodable<E> for HashSet<T, H> {
|
||||||
|
fn encode(&self, s: &mut E) {
|
||||||
s.emit_seq(self.len(), |s| {
|
s.emit_seq(self.len(), |s| {
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
for e in self.iter() {
|
for e in self.iter() {
|
||||||
|
@ -214,11 +222,13 @@ impl<
|
||||||
|
|
||||||
impl<
|
impl<
|
||||||
D: Decoder,
|
D: Decoder,
|
||||||
T: Decodable<D> + Hash + Eq
|
T: Decodable<D> + Hash<S> + Eq,
|
||||||
> Decodable<D> for HashSet<T> {
|
S,
|
||||||
fn decode(d: &mut D) -> HashSet<T> {
|
H: Hasher<S> + Default
|
||||||
|
> Decodable<D> for HashSet<T, H> {
|
||||||
|
fn decode(d: &mut D) -> HashSet<T, H> {
|
||||||
d.read_seq(|d, len| {
|
d.read_seq(|d, len| {
|
||||||
let mut set = HashSet::with_capacity(len);
|
let mut set = HashSet::with_capacity_and_hasher(Default::default(), len);
|
||||||
for i in range(0u, len) {
|
for i in range(0u, len) {
|
||||||
set.insert(d.read_seq_elt(i, |d| Decodable::decode(d)));
|
set.insert(d.read_seq_elt(i, |d| Decodable::decode(d)));
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,11 @@ Core encoding and decoding interfaces.
|
||||||
#[license = "MIT/ASL2"];
|
#[license = "MIT/ASL2"];
|
||||||
#[allow(missing_doc)];
|
#[allow(missing_doc)];
|
||||||
#[forbid(non_camel_case_types)];
|
#[forbid(non_camel_case_types)];
|
||||||
#[feature(macro_rules,managed_boxes)];
|
#[feature(macro_rules, managed_boxes, default_type_params)];
|
||||||
|
|
||||||
|
// NOTE remove the following two attributes after the next snapshot.
|
||||||
|
#[allow(unrecognized_lint)];
|
||||||
|
#[allow(default_type_param_usage)];
|
||||||
|
|
||||||
// test harness access
|
// test harness access
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -24,7 +24,9 @@
|
||||||
* discouraged.
|
* discouraged.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use clone::Clone;
|
||||||
use container::Container;
|
use container::Container;
|
||||||
|
use default::Default;
|
||||||
use io::{IoResult, Writer};
|
use io::{IoResult, Writer};
|
||||||
use iter::Iterator;
|
use iter::Iterator;
|
||||||
use result::Ok;
|
use result::Ok;
|
||||||
|
@ -81,7 +83,13 @@ macro_rules! compress (
|
||||||
impl SipState {
|
impl SipState {
|
||||||
/// Create a `SipState` that is keyed off the provided keys.
|
/// Create a `SipState` that is keyed off the provided keys.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new(key0: u64, key1: u64) -> SipState {
|
pub fn new() -> SipState {
|
||||||
|
SipState::new_with_keys(0, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a `SipState` that is keyed off the provided keys.
|
||||||
|
#[inline]
|
||||||
|
pub fn new_with_keys(key0: u64, key1: u64) -> SipState {
|
||||||
let mut state = SipState {
|
let mut state = SipState {
|
||||||
k0: key0,
|
k0: key0,
|
||||||
k1: key1,
|
k1: key1,
|
||||||
|
@ -206,7 +214,22 @@ impl Writer for SipState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `Sip` computes the SipHash algorithm from a stream of bytes.
|
impl Clone for SipState {
|
||||||
|
#[inline]
|
||||||
|
fn clone(&self) -> SipState {
|
||||||
|
*self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for SipState {
|
||||||
|
#[inline]
|
||||||
|
fn default() -> SipState {
|
||||||
|
SipState::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `SipHasher` computes the SipHash algorithm from a stream of bytes.
|
||||||
|
#[deriving(Clone)]
|
||||||
pub struct SipHasher {
|
pub struct SipHasher {
|
||||||
priv state: SipState,
|
priv state: SipState,
|
||||||
}
|
}
|
||||||
|
@ -215,14 +238,16 @@ impl SipHasher {
|
||||||
/// Create a `Sip`.
|
/// Create a `Sip`.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new() -> SipHasher {
|
pub fn new() -> SipHasher {
|
||||||
SipHasher::new_with_keys(0, 0)
|
SipHasher {
|
||||||
|
state: SipState::new(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a `Sip` that is keyed off the provided keys.
|
/// Create a `Sip` that is keyed off the provided keys.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new_with_keys(key0: u64, key1: u64) -> SipHasher {
|
pub fn new_with_keys(key0: u64, key1: u64) -> SipHasher {
|
||||||
SipHasher {
|
SipHasher {
|
||||||
state: SipState::new(key0, key1),
|
state: SipState::new_with_keys(key0, key1),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -230,23 +255,31 @@ impl SipHasher {
|
||||||
impl Hasher<SipState> for SipHasher {
|
impl Hasher<SipState> for SipHasher {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn hash<T: Hash<SipState>>(&self, value: &T) -> u64 {
|
fn hash<T: Hash<SipState>>(&self, value: &T) -> u64 {
|
||||||
let mut state = self.state; // implicitly copy the state.
|
let mut state = self.state.clone();
|
||||||
value.hash(&mut state);
|
value.hash(&mut state);
|
||||||
state.result()
|
state.result()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for SipHasher {
|
||||||
|
#[inline]
|
||||||
|
fn default() -> SipHasher {
|
||||||
|
SipHasher::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Hash a value using the SipHash algorithm.
|
/// Hash a value using the SipHash algorithm.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn hash<T: Hash<SipState>>(value: &T) -> u64 {
|
pub fn hash<T: Hash<SipState>>(value: &T) -> u64 {
|
||||||
hash_with_keys(0, 0, value)
|
let mut state = SipState::new();
|
||||||
|
value.hash(&mut state);
|
||||||
|
state.result()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Hash a value with the SipHash algorithm with the provided keys.
|
/// Hash a value with the SipHash algorithm with the provided keys.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn hash_with_keys<T: Hash<SipState>>(k0: u64, k1: u64, value: &T) -> u64 {
|
pub fn hash_with_keys<T: Hash<SipState>>(k0: u64, k1: u64, value: &T) -> u64 {
|
||||||
let mut state = SipState::new(k0, k1);
|
let mut state = SipState::new_with_keys(k0, k1);
|
||||||
value.hash(&mut state);
|
value.hash(&mut state);
|
||||||
state.result()
|
state.result()
|
||||||
}
|
}
|
||||||
|
@ -350,8 +383,8 @@ mod tests {
|
||||||
let k1 = 0x_0f_0e_0d_0c_0b_0a_09_08_u64;
|
let k1 = 0x_0f_0e_0d_0c_0b_0a_09_08_u64;
|
||||||
let mut buf : ~[u8] = ~[];
|
let mut buf : ~[u8] = ~[];
|
||||||
let mut t = 0;
|
let mut t = 0;
|
||||||
let mut state_inc = SipState::new(k0, k1);
|
let mut state_inc = SipState::new_with_keys(k0, k1);
|
||||||
let mut state_full = SipState::new(k0, k1);
|
let mut state_full = SipState::new_with_keys(k0, k1);
|
||||||
|
|
||||||
fn to_hex_str(r: &[u8, ..8]) -> ~str {
|
fn to_hex_str(r: &[u8, ..8]) -> ~str {
|
||||||
let mut s = ~"";
|
let mut s = ~"";
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue