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