From d223dd1e57cc412aa2eff28e6604f86b9f013083 Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Fri, 21 Feb 2014 21:33:23 -0800 Subject: [PATCH] std: rewrite Hash to make it more generic This patch merges IterBytes and Hash traits, which clears up the confusion of using `#[deriving(IterBytes)]` to support hashing. Instead, it now is much easier to use the new `#[deriving(Hash)]` for making a type hashable with a stream hash. Furthermore, it supports custom non-stream-based hashers, such as if a value's hash was cached in a database. This does not yet replace the old IterBytes-hash with this new version. --- src/etc/generate-deriving-span-tests.py | 3 +- src/libextra/stats.rs | 1 + src/librustc/metadata/decoder.rs | 1 + src/librustc/metadata/encoder.rs | 1 + src/librustc/middle/ty.rs | 2 +- src/libserialize/serialize.rs | 1 + src/libstd/hash/mod.rs | 369 +++++++++++++ src/libstd/hash/sip.rs | 519 ++++++++++++++++++ src/libstd/{hash.rs => hash_old.rs} | 12 +- src/libstd/hashmap.rs | 2 +- src/libstd/lib.rs | 6 + src/libstd/prelude.rs | 1 - src/libstd/to_str.rs | 2 +- src/libsyntax/ext/deriving/generic.rs | 12 +- src/libsyntax/ext/deriving/hash.rs | 97 ++++ src/libsyntax/ext/deriving/mod.rs | 2 + src/libsyntax/util/interner.rs | 1 + .../deriving-span-Hash-enum-struct-variant.rs | 27 + .../compile-fail/deriving-span-Hash-enum.rs | 27 + .../compile-fail/deriving-span-Hash-struct.rs | 25 + .../deriving-span-Hash-tuple-struct.rs | 25 + src/test/run-pass/deriving-hash.rs | 28 + src/test/run-pass/deriving-meta-multiple.rs | 2 + src/test/run-pass/deriving-meta.rs | 2 + src/test/run-pass/typeid-intrinsic.rs | 1 + 25 files changed, 1154 insertions(+), 15 deletions(-) create mode 100644 src/libstd/hash/mod.rs create mode 100644 src/libstd/hash/sip.rs rename src/libstd/{hash.rs => hash_old.rs} (98%) create mode 100644 src/libsyntax/ext/deriving/hash.rs create mode 100644 src/test/compile-fail/deriving-span-Hash-enum-struct-variant.rs create mode 100644 src/test/compile-fail/deriving-span-Hash-enum.rs create mode 100644 src/test/compile-fail/deriving-span-Hash-struct.rs create mode 100644 src/test/compile-fail/deriving-span-Hash-tuple-struct.rs create mode 100644 src/test/run-pass/deriving-hash.rs diff --git a/src/etc/generate-deriving-span-tests.py b/src/etc/generate-deriving-span-tests.py index ecf58d55576cf..cf895d2b6dee5 100755 --- a/src/etc/generate-deriving-span-tests.py +++ b/src/etc/generate-deriving-span-tests.py @@ -119,7 +119,8 @@ def write_file(name, string): ('Clone', [], 1), ('DeepClone', ['Clone'], 1), ('Eq', [], 2), ('Ord', [], 8), ('TotalEq', [], 1), ('TotalOrd', ['TotalEq'], 1), - ('Show', [], 1)]: + ('Show', [], 1), + ('Hash', [], 1)]: traits[trait] = (ALL, supers, errs) for (trait, (types, super_traits, error_count)) in traits.items(): diff --git a/src/libextra/stats.rs b/src/libextra/stats.rs index 181ea6766c575..6738275c4c1f1 100644 --- a/src/libextra/stats.rs +++ b/src/libextra/stats.rs @@ -11,6 +11,7 @@ #[allow(missing_doc)]; use std::cmp; +use std::hash_old::Hash; use std::hashmap; use std::io; use std::mem; diff --git a/src/librustc/metadata/decoder.rs b/src/librustc/metadata/decoder.rs index 47fcc4534897c..88aff133fb069 100644 --- a/src/librustc/metadata/decoder.rs +++ b/src/librustc/metadata/decoder.rs @@ -27,6 +27,7 @@ use middle::typeck; use middle::astencode::vtable_decoder_helpers; use std::u64; +use std::hash_old::Hash; use std::io; use std::io::extensions::u64_from_be_bytes; use std::option; diff --git a/src/librustc/metadata/encoder.rs b/src/librustc/metadata/encoder.rs index d2b843cdcf7e9..7b7f45b2d2d49 100644 --- a/src/librustc/metadata/encoder.rs +++ b/src/librustc/metadata/encoder.rs @@ -26,6 +26,7 @@ use middle; use serialize::Encodable; use std::cast; use std::cell::{Cell, RefCell}; +use std::hash_old::Hash; use std::hashmap::{HashMap, HashSet}; use std::io::MemWriter; use std::str; diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 09c21d54c87a0..f47c2f84e99c0 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -4916,7 +4916,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 { - use std::hash::{SipState, Streaming}; + use std::hash_old::{SipState, Streaming}; let mut hash = SipState::new(0, 0); let region = |_hash: &mut SipState, r: Region| { diff --git a/src/libserialize/serialize.rs b/src/libserialize/serialize.rs index e57e7adcf8ddb..f350bb0761f7f 100644 --- a/src/libserialize/serialize.rs +++ b/src/libserialize/serialize.rs @@ -14,6 +14,7 @@ Core encoding and decoding interfaces. */ +use std::hash_old::Hash; use std::hashmap::{HashMap, HashSet}; use std::rc::Rc; use std::trie::{TrieMap, TrieSet}; diff --git a/src/libstd/hash/mod.rs b/src/libstd/hash/mod.rs new file mode 100644 index 0000000000000..ac079cb3fb335 --- /dev/null +++ b/src/libstd/hash/mod.rs @@ -0,0 +1,369 @@ +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +/*! + * Generic hashing support. + * + * This module provides a generic way to compute the hash of a value. The + * simplest way to make a type hashable is to use `#[deriving(Hash)]`: + * + * # Example + * + * ```rust + * use std::hash; + * use std::hash::Hash; + * + * #[deriving(Hash)] + * struct Person { + * id: uint, + * name: ~str, + * phone: uint, + * } + * + * let person1 = Person { id: 5, name: ~"Janet", phone: 555_666_7777 }; + * let person2 = Person { id: 5, name: ~"Bob", phone: 555_666_7777 }; + * + * assert!(hash::hash(&person1) != hash::hash(&person2)); + * ``` + * + * If you need more control over how a value is hashed, you need to implement + * the trait `Hash`: + * + * ```rust + * use std::hash; + * use std::hash::Hash; + * use std::hash::sip::SipState; + * + * struct Person { + * id: uint, + * name: ~str, + * phone: uint, + * } + * + * impl Hash for Person { + * fn hash(&self, state: &mut SipState) { + * self.id.hash(state); + * self.phone.hash(state); + * } + * } + * + * let person1 = Person { id: 5, name: ~"Janet", phone: 555_666_7777 }; + * let person2 = Person { id: 5, name: ~"Bob", phone: 555_666_7777 }; + * + * assert!(hash::hash(&person1) == hash::hash(&person2)); + * ``` + */ + +#[allow(unused_must_use)]; + +use container::Container; +use io::Writer; +use iter::Iterator; +use option::{Option, Some, None}; +use rc::Rc; +use str::{Str, StrSlice}; +use vec::{Vector, ImmutableVector}; + +/// Reexport the `sip::hash` function as our default hasher. +pub use hash = self::sip::hash; + +pub mod sip; + +/// A trait that represents a hashable type. The `S` type parameter is an +/// abstract hash state that is used by the `Hash` to compute the hash. +/// It defaults to `std::hash::sip::SipState`. +pub trait Hash { + /// Compute a hash of the value. + fn hash(&self, state: &mut S); +} + +/// A trait that computes a hash for a value. The main users of this trait are +/// containers like `HashMap`, which need a generic way hash multiple types. +pub trait Hasher { + /// Compute a hash of the value. + fn hash>(&self, value: &T) -> u64; +} + +////////////////////////////////////////////////////////////////////////////// + +macro_rules! impl_hash( + ( $( $ty:ty => $method:ident;)* ) => ( + $( + impl Hash for $ty { + #[inline] + fn hash(&self, state: &mut S) { + state.$method(*self); + } + } + )* + ) +) + +impl_hash!( + u8 => write_u8; + u16 => write_le_u16; + u32 => write_le_u32; + u64 => write_le_u64; + uint => write_le_uint; + i8 => write_i8; + i16 => write_le_i16; + i32 => write_le_i32; + i64 => write_le_i64; + int => write_le_int; +) + +impl Hash for bool { + #[inline] + fn hash(&self, state: &mut S) { + (*self as u8).hash(state); + } +} + +impl Hash for char { + #[inline] + fn hash(&self, state: &mut S) { + (*self as u32).hash(state); + } +} + +impl<'a, S: Writer> Hash for &'a str { + #[inline] + fn hash(&self, state: &mut S) { + state.write(self.as_bytes()); + state.write_u8(0xFF); + } +} + +impl Hash for ~str { + #[inline] + fn hash(&self, state: &mut S) { + self.as_slice().hash(state); + } +} + +macro_rules! impl_hash_tuple( + () => ( + impl Hash for () { + #[inline] + fn hash(&self, state: &mut S) { + state.write([]); + } + } + ); + + ($A:ident $($B:ident)*) => ( + impl< + S: Writer, + $A: Hash $(, $B: Hash)* + > Hash for ($A, $($B),*) { + #[inline] + fn hash(&self, state: &mut S) { + match *self { + (ref $A, $(ref $B),*) => { + $A.hash(state); + $( + $B.hash(state); + )* + } + } + } + } + + impl_hash_tuple!($($B)*) + ); +) + +impl_hash_tuple!(A0 A1 A2 A3 A4 A5 A6 A7) + +impl<'a, S: Writer, T: Hash> Hash for &'a [T] { + #[inline] + fn hash(&self, state: &mut S) { + self.len().hash(state); + for elt in self.iter() { + elt.hash(state); + } + } +} + + +impl<'a, S: Writer, T: Hash> Hash for &'a mut [T] { + #[inline] + fn hash(&self, state: &mut S) { + self.as_slice().hash(state); + } +} + +impl> Hash for ~[T] { + #[inline] + fn hash(&self, state: &mut S) { + self.as_slice().hash(state); + } +} + +impl<'a, S: Writer, T: Hash> Hash for &'a T { + #[inline] + fn hash(&self, state: &mut S) { + (**self).hash(state); + } +} + +impl<'a, S: Writer, T: Hash> Hash for &'a mut T { + #[inline] + fn hash(&self, state: &mut S) { + (**self).hash(state); + } +} + +impl> Hash for ~T { + #[inline] + fn hash(&self, state: &mut S) { + (**self).hash(state); + } +} + +impl> Hash for @T { + #[inline] + fn hash(&self, state: &mut S) { + (**self).hash(state); + } +} + +impl> Hash for Rc { + #[inline] + fn hash(&self, state: &mut S) { + self.borrow().hash(state); + } +} + +impl> Hash for Option { + #[inline] + fn hash(&self, state: &mut S) { + match *self { + Some(ref x) => { + 0u8.hash(state); + x.hash(state); + } + None => { + 1u8.hash(state); + } + } + } +} + +impl Hash for *T { + #[inline] + fn hash(&self, state: &mut S) { + // NB: raw-pointer Hash does _not_ dereference + // to the target; it just gives you the pointer-bytes. + (*self as uint).hash(state); + } +} + +impl Hash for *mut T { + #[inline] + fn hash(&self, state: &mut S) { + // NB: raw-pointer Hash does _not_ dereference + // to the target; it just gives you the pointer-bytes. + (*self as uint).hash(state); + } +} + +////////////////////////////////////////////////////////////////////////////// + +#[cfg(test)] +mod tests { + use cast; + use io::{IoResult, Writer}; + use iter::{Iterator}; + use option::{Some, None}; + use result::Ok; + use vec::ImmutableVector; + + use super::{Hash, Hasher}; + + struct MyWriterHasher; + + impl Hasher for MyWriterHasher { + fn hash>(&self, value: &T) -> u64 { + let mut state = MyWriter { hash: 0 }; + value.hash(&mut state); + state.hash + } + } + + struct MyWriter { + hash: u64, + } + + impl Writer for MyWriter { + // Most things we'll just add up the bytes. + fn write(&mut self, buf: &[u8]) -> IoResult<()> { + for byte in buf.iter() { + self.hash += *byte as u64; + } + Ok(()) + } + } + + #[test] + fn test_writer_hasher() { + let hasher = MyWriterHasher; + + assert_eq!(hasher.hash(&()), 0); + + assert_eq!(hasher.hash(&5u8), 5); + assert_eq!(hasher.hash(&5u16), 5); + assert_eq!(hasher.hash(&5u32), 5); + assert_eq!(hasher.hash(&5u64), 5); + assert_eq!(hasher.hash(&5u), 5); + + assert_eq!(hasher.hash(&5i8), 5); + assert_eq!(hasher.hash(&5i16), 5); + assert_eq!(hasher.hash(&5i32), 5); + assert_eq!(hasher.hash(&5i64), 5); + assert_eq!(hasher.hash(&5i), 5); + + assert_eq!(hasher.hash(&false), 0); + assert_eq!(hasher.hash(&true), 1); + + assert_eq!(hasher.hash(&'a'), 97); + + assert_eq!(hasher.hash(& &"a"), 97 + 0xFF); + assert_eq!(hasher.hash(& &[1u8, 2u8, 3u8]), 9); + + unsafe { + let ptr: *int = cast::transmute(5); + assert_eq!(hasher.hash(&ptr), 5); + } + + unsafe { + let ptr: *mut int = cast::transmute(5); + assert_eq!(hasher.hash(&ptr), 5); + } + } + + struct Custom { + hash: u64 + } + + impl Hash for Custom { + fn hash(&self, state: &mut u64) { + *state = self.hash; + } + } + + #[test] + fn test_custom_state() { + let custom = Custom { hash: 5 }; + let mut state = 0; + custom.hash(&mut state); + assert_eq!(state, 5); + } +} diff --git a/src/libstd/hash/sip.rs b/src/libstd/hash/sip.rs new file mode 100644 index 0000000000000..ce5e0d6b2edd2 --- /dev/null +++ b/src/libstd/hash/sip.rs @@ -0,0 +1,519 @@ +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +/*! + * Implementation of SipHash 2-4 + * + * See: http://131002.net/siphash/ + * + * Consider this as a main "general-purpose" hash for all hashtables: it + * runs at good speed (competitive with spooky and city) and permits + * strong _keyed_ hashing. Key your hashtables from a strong RNG, + * such as `rand::Rng`. + * + * Although the SipHash algorithm is considered to be cryptographically + * strong, this implementation has not been reviewed for such purposes. + * As such, all cryptographic uses of this implementation are strongly + * discouraged. + */ + +use container::Container; +use io::{IoResult, Writer}; +use iter::Iterator; +use result::Ok; +use vec::ImmutableVector; + +use super::{Hash, Hasher}; + +/// `SipState` computes a SipHash 2-4 hash over a stream of bytes. +pub struct SipState { + priv k0: u64, + priv k1: u64, + priv length: uint, // how many bytes we've processed + priv v0: u64, // hash state + priv v1: u64, + priv v2: u64, + priv v3: u64, + priv tail: [u8, ..8], // unprocessed bytes + priv ntail: uint, // how many bytes in tail are valid +} + +// sadly, these macro definitions can't appear later, +// because they're needed in the following defs; +// this design could be improved. + +macro_rules! u8to64_le ( + ($buf:expr, $i:expr) => + ($buf[0+$i] as u64 | + $buf[1+$i] as u64 << 8 | + $buf[2+$i] as u64 << 16 | + $buf[3+$i] as u64 << 24 | + $buf[4+$i] as u64 << 32 | + $buf[5+$i] as u64 << 40 | + $buf[6+$i] as u64 << 48 | + $buf[7+$i] as u64 << 56) +) + +macro_rules! rotl ( + ($x:expr, $b:expr) => + (($x << $b) | ($x >> (64 - $b))) +) + +macro_rules! compress ( + ($v0:expr, $v1:expr, $v2:expr, $v3:expr) => + ({ + $v0 += $v1; $v1 = rotl!($v1, 13); $v1 ^= $v0; + $v0 = rotl!($v0, 32); + $v2 += $v3; $v3 = rotl!($v3, 16); $v3 ^= $v2; + $v0 += $v3; $v3 = rotl!($v3, 21); $v3 ^= $v0; + $v2 += $v1; $v1 = rotl!($v1, 17); $v1 ^= $v2; + $v2 = rotl!($v2, 32); + }) +) + +impl SipState { + /// Create a `SipState` that is keyed off the provided keys. + #[inline] + pub fn new(key0: u64, key1: u64) -> SipState { + let mut state = SipState { + k0: key0, + k1: key1, + length: 0, + v0: 0, + v1: 0, + v2: 0, + v3: 0, + tail: [ 0, 0, 0, 0, 0, 0, 0, 0 ], + ntail: 0, + }; + state.reset(); + state + } + + /// Reset the state back to it's initial state. + #[inline] + pub fn reset(&mut self) { + self.length = 0; + self.v0 = self.k0 ^ 0x736f6d6570736575; + self.v1 = self.k1 ^ 0x646f72616e646f6d; + self.v2 = self.k0 ^ 0x6c7967656e657261; + self.v3 = self.k1 ^ 0x7465646279746573; + self.ntail = 0; + } + + /// Return the computed hash. + #[inline] + pub fn result(&self) -> u64 { + let mut v0 = self.v0; + let mut v1 = self.v1; + let mut v2 = self.v2; + let mut v3 = self.v3; + + let mut b : u64 = (self.length as u64 & 0xff) << 56; + + if self.ntail > 0 { b |= self.tail[0] as u64 << 0; } + if self.ntail > 1 { b |= self.tail[1] as u64 << 8; } + if self.ntail > 2 { b |= self.tail[2] as u64 << 16; } + if self.ntail > 3 { b |= self.tail[3] as u64 << 24; } + if self.ntail > 4 { b |= self.tail[4] as u64 << 32; } + if self.ntail > 5 { b |= self.tail[5] as u64 << 40; } + if self.ntail > 6 { b |= self.tail[6] as u64 << 48; } + + v3 ^= b; + compress!(v0, v1, v2, v3); + compress!(v0, v1, v2, v3); + v0 ^= b; + + v2 ^= 0xff; + compress!(v0, v1, v2, v3); + compress!(v0, v1, v2, v3); + compress!(v0, v1, v2, v3); + compress!(v0, v1, v2, v3); + + v0 ^ v1 ^ v2 ^ v3 + } +} + +impl Writer for SipState { + #[inline] + fn write(&mut self, msg: &[u8]) -> IoResult<()> { + let length = msg.len(); + self.length += length; + + let mut needed = 0u; + + if self.ntail != 0 { + needed = 8 - self.ntail; + + if length < needed { + let mut t = 0; + while t < length { + self.tail[self.ntail+t] = msg[t]; + t += 1; + } + self.ntail += length; + return Ok(()); + } + + let mut t = 0; + while t < needed { + self.tail[self.ntail+t] = msg[t]; + t += 1; + } + + let m = u8to64_le!(self.tail, 0); + + self.v3 ^= m; + compress!(self.v0, self.v1, self.v2, self.v3); + compress!(self.v0, self.v1, self.v2, self.v3); + self.v0 ^= m; + + self.ntail = 0; + } + + // Buffered tail is now flushed, process new input. + let len = length - needed; + let end = len & (!0x7); + let left = len & 0x7; + + let mut i = needed; + while i < end { + let mi = u8to64_le!(msg, i); + + self.v3 ^= mi; + compress!(self.v0, self.v1, self.v2, self.v3); + compress!(self.v0, self.v1, self.v2, self.v3); + self.v0 ^= mi; + + i += 8; + } + + let mut t = 0u; + while t < left { + self.tail[t] = msg[i+t]; + t += 1 + } + self.ntail = left; + + Ok(()) + } +} + +/// `Sip` computes the SipHash algorithm from a stream of bytes. +pub struct SipHasher { + priv state: SipState, +} + +impl SipHasher { + /// Create a `Sip`. + #[inline] + pub fn new() -> SipHasher { + SipHasher::new_with_keys(0, 0) + } + + /// Create a `Sip` that is keyed off the provided keys. + #[inline] + pub fn new_with_keys(key0: u64, key1: u64) -> SipHasher { + SipHasher { + state: SipState::new(key0, key1), + } + } +} + +impl Hasher for SipHasher { + #[inline] + fn hash>(&self, value: &T) -> u64 { + let mut state = self.state; // implicitly copy the state. + value.hash(&mut state); + state.result() + } +} + + +/// Hash a value using the SipHash algorithm. +#[inline] +pub fn hash>(value: &T) -> u64 { + hash_with_keys(0, 0, value) +} + +/// 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); + value.hash(&mut state); + state.result() +} + +#[cfg(test)] +mod tests { + extern crate test; + use io::Writer; + use iter::Iterator; + use num::ToStrRadix; + use option::{Some, None}; + use str::{Str, OwnedStr}; + use vec::{Vector, ImmutableVector, OwnedVector}; + use self::test::BenchHarness; + + use super::super::Hash; + use super::{SipState, hash, hash_with_keys}; + + // Hash just the bytes of the slice, without length prefix + struct Bytes<'a>(&'a [u8]); + + impl<'a, S: Writer> Hash for Bytes<'a> { + #[allow(unused_must_use)] + fn hash(&self, state: &mut S) { + let Bytes(v) = *self; + state.write(v); + } + } + + #[test] + #[allow(unused_must_use)] + fn test_siphash() { + let vecs : [[u8, ..8], ..64] = [ + [ 0x31, 0x0e, 0x0e, 0xdd, 0x47, 0xdb, 0x6f, 0x72, ], + [ 0xfd, 0x67, 0xdc, 0x93, 0xc5, 0x39, 0xf8, 0x74, ], + [ 0x5a, 0x4f, 0xa9, 0xd9, 0x09, 0x80, 0x6c, 0x0d, ], + [ 0x2d, 0x7e, 0xfb, 0xd7, 0x96, 0x66, 0x67, 0x85, ], + [ 0xb7, 0x87, 0x71, 0x27, 0xe0, 0x94, 0x27, 0xcf, ], + [ 0x8d, 0xa6, 0x99, 0xcd, 0x64, 0x55, 0x76, 0x18, ], + [ 0xce, 0xe3, 0xfe, 0x58, 0x6e, 0x46, 0xc9, 0xcb, ], + [ 0x37, 0xd1, 0x01, 0x8b, 0xf5, 0x00, 0x02, 0xab, ], + [ 0x62, 0x24, 0x93, 0x9a, 0x79, 0xf5, 0xf5, 0x93, ], + [ 0xb0, 0xe4, 0xa9, 0x0b, 0xdf, 0x82, 0x00, 0x9e, ], + [ 0xf3, 0xb9, 0xdd, 0x94, 0xc5, 0xbb, 0x5d, 0x7a, ], + [ 0xa7, 0xad, 0x6b, 0x22, 0x46, 0x2f, 0xb3, 0xf4, ], + [ 0xfb, 0xe5, 0x0e, 0x86, 0xbc, 0x8f, 0x1e, 0x75, ], + [ 0x90, 0x3d, 0x84, 0xc0, 0x27, 0x56, 0xea, 0x14, ], + [ 0xee, 0xf2, 0x7a, 0x8e, 0x90, 0xca, 0x23, 0xf7, ], + [ 0xe5, 0x45, 0xbe, 0x49, 0x61, 0xca, 0x29, 0xa1, ], + [ 0xdb, 0x9b, 0xc2, 0x57, 0x7f, 0xcc, 0x2a, 0x3f, ], + [ 0x94, 0x47, 0xbe, 0x2c, 0xf5, 0xe9, 0x9a, 0x69, ], + [ 0x9c, 0xd3, 0x8d, 0x96, 0xf0, 0xb3, 0xc1, 0x4b, ], + [ 0xbd, 0x61, 0x79, 0xa7, 0x1d, 0xc9, 0x6d, 0xbb, ], + [ 0x98, 0xee, 0xa2, 0x1a, 0xf2, 0x5c, 0xd6, 0xbe, ], + [ 0xc7, 0x67, 0x3b, 0x2e, 0xb0, 0xcb, 0xf2, 0xd0, ], + [ 0x88, 0x3e, 0xa3, 0xe3, 0x95, 0x67, 0x53, 0x93, ], + [ 0xc8, 0xce, 0x5c, 0xcd, 0x8c, 0x03, 0x0c, 0xa8, ], + [ 0x94, 0xaf, 0x49, 0xf6, 0xc6, 0x50, 0xad, 0xb8, ], + [ 0xea, 0xb8, 0x85, 0x8a, 0xde, 0x92, 0xe1, 0xbc, ], + [ 0xf3, 0x15, 0xbb, 0x5b, 0xb8, 0x35, 0xd8, 0x17, ], + [ 0xad, 0xcf, 0x6b, 0x07, 0x63, 0x61, 0x2e, 0x2f, ], + [ 0xa5, 0xc9, 0x1d, 0xa7, 0xac, 0xaa, 0x4d, 0xde, ], + [ 0x71, 0x65, 0x95, 0x87, 0x66, 0x50, 0xa2, 0xa6, ], + [ 0x28, 0xef, 0x49, 0x5c, 0x53, 0xa3, 0x87, 0xad, ], + [ 0x42, 0xc3, 0x41, 0xd8, 0xfa, 0x92, 0xd8, 0x32, ], + [ 0xce, 0x7c, 0xf2, 0x72, 0x2f, 0x51, 0x27, 0x71, ], + [ 0xe3, 0x78, 0x59, 0xf9, 0x46, 0x23, 0xf3, 0xa7, ], + [ 0x38, 0x12, 0x05, 0xbb, 0x1a, 0xb0, 0xe0, 0x12, ], + [ 0xae, 0x97, 0xa1, 0x0f, 0xd4, 0x34, 0xe0, 0x15, ], + [ 0xb4, 0xa3, 0x15, 0x08, 0xbe, 0xff, 0x4d, 0x31, ], + [ 0x81, 0x39, 0x62, 0x29, 0xf0, 0x90, 0x79, 0x02, ], + [ 0x4d, 0x0c, 0xf4, 0x9e, 0xe5, 0xd4, 0xdc, 0xca, ], + [ 0x5c, 0x73, 0x33, 0x6a, 0x76, 0xd8, 0xbf, 0x9a, ], + [ 0xd0, 0xa7, 0x04, 0x53, 0x6b, 0xa9, 0x3e, 0x0e, ], + [ 0x92, 0x59, 0x58, 0xfc, 0xd6, 0x42, 0x0c, 0xad, ], + [ 0xa9, 0x15, 0xc2, 0x9b, 0xc8, 0x06, 0x73, 0x18, ], + [ 0x95, 0x2b, 0x79, 0xf3, 0xbc, 0x0a, 0xa6, 0xd4, ], + [ 0xf2, 0x1d, 0xf2, 0xe4, 0x1d, 0x45, 0x35, 0xf9, ], + [ 0x87, 0x57, 0x75, 0x19, 0x04, 0x8f, 0x53, 0xa9, ], + [ 0x10, 0xa5, 0x6c, 0xf5, 0xdf, 0xcd, 0x9a, 0xdb, ], + [ 0xeb, 0x75, 0x09, 0x5c, 0xcd, 0x98, 0x6c, 0xd0, ], + [ 0x51, 0xa9, 0xcb, 0x9e, 0xcb, 0xa3, 0x12, 0xe6, ], + [ 0x96, 0xaf, 0xad, 0xfc, 0x2c, 0xe6, 0x66, 0xc7, ], + [ 0x72, 0xfe, 0x52, 0x97, 0x5a, 0x43, 0x64, 0xee, ], + [ 0x5a, 0x16, 0x45, 0xb2, 0x76, 0xd5, 0x92, 0xa1, ], + [ 0xb2, 0x74, 0xcb, 0x8e, 0xbf, 0x87, 0x87, 0x0a, ], + [ 0x6f, 0x9b, 0xb4, 0x20, 0x3d, 0xe7, 0xb3, 0x81, ], + [ 0xea, 0xec, 0xb2, 0xa3, 0x0b, 0x22, 0xa8, 0x7f, ], + [ 0x99, 0x24, 0xa4, 0x3c, 0xc1, 0x31, 0x57, 0x24, ], + [ 0xbd, 0x83, 0x8d, 0x3a, 0xaf, 0xbf, 0x8d, 0xb7, ], + [ 0x0b, 0x1a, 0x2a, 0x32, 0x65, 0xd5, 0x1a, 0xea, ], + [ 0x13, 0x50, 0x79, 0xa3, 0x23, 0x1c, 0xe6, 0x60, ], + [ 0x93, 0x2b, 0x28, 0x46, 0xe4, 0xd7, 0x06, 0x66, ], + [ 0xe1, 0x91, 0x5f, 0x5c, 0xb1, 0xec, 0xa4, 0x6c, ], + [ 0xf3, 0x25, 0x96, 0x5c, 0xa1, 0x6d, 0x62, 0x9f, ], + [ 0x57, 0x5f, 0xf2, 0x8e, 0x60, 0x38, 0x1b, 0xe5, ], + [ 0x72, 0x45, 0x06, 0xeb, 0x4c, 0x32, 0x8a, 0x95, ] + ]; + + let k0 = 0x_07_06_05_04_03_02_01_00_u64; + 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); + + fn to_hex_str(r: &[u8, ..8]) -> ~str { + let mut s = ~""; + for b in r.iter() { + s.push_str((*b as uint).to_str_radix(16u)); + } + s + } + + fn result_bytes(h: u64) -> ~[u8] { + ~[(h >> 0) as u8, + (h >> 8) as u8, + (h >> 16) as u8, + (h >> 24) as u8, + (h >> 32) as u8, + (h >> 40) as u8, + (h >> 48) as u8, + (h >> 56) as u8, + ] + } + + fn result_str(h: u64) -> ~str { + let r = result_bytes(h); + let mut s = ~""; + for b in r.iter() { + s.push_str((*b as uint).to_str_radix(16u)); + } + s + } + + while t < 64 { + debug!("siphash test {}", t); + let vec = u8to64_le!(vecs[t], 0); + let out = hash_with_keys(k0, k1, &Bytes(buf.as_slice())); + debug!("got {:?}, expected {:?}", out, vec); + assert_eq!(vec, out); + + state_full.reset(); + state_full.write(buf); + let f = result_str(state_full.result()); + let i = result_str(state_inc.result()); + let v = to_hex_str(&vecs[t]); + debug!("{}: ({}) => inc={} full={}", t, v, i, f); + + assert!(f == i && f == v); + + buf.push(t as u8); + state_inc.write_u8(t); + + t += 1; + } + } + + #[test] #[cfg(target_arch = "arm")] + fn test_hash_uint() { + let val = 0xdeadbeef_deadbeef_u64; + assert!(hash(&(val as u64)) != hash(&(val as uint))); + assert_eq!(hash(&(val as u32)), hash(&(val as uint))); + } + #[test] #[cfg(target_arch = "x86_64")] + fn test_hash_uint() { + let val = 0xdeadbeef_deadbeef_u64; + assert_eq!(hash(&(val as u64)), hash(&(val as uint))); + assert!(hash(&(val as u32)) != hash(&(val as uint))); + } + #[test] #[cfg(target_arch = "x86")] + fn test_hash_uint() { + let val = 0xdeadbeef_deadbeef_u64; + assert!(hash(&(val as u64)) != hash(&(val as uint))); + assert_eq!(hash(&(val as u32)), hash(&(val as uint))); + } + + #[test] + fn test_hash_idempotent() { + let val64 = 0xdeadbeef_deadbeef_u64; + assert_eq!(hash(&val64), hash(&val64)); + let val32 = 0xdeadbeef_u32; + assert_eq!(hash(&val32), hash(&val32)); + } + + #[test] + fn test_hash_no_bytes_dropped_64() { + let val = 0xdeadbeef_deadbeef_u64; + + assert!(hash(&val) != hash(&zero_byte(val, 0))); + assert!(hash(&val) != hash(&zero_byte(val, 1))); + assert!(hash(&val) != hash(&zero_byte(val, 2))); + assert!(hash(&val) != hash(&zero_byte(val, 3))); + assert!(hash(&val) != hash(&zero_byte(val, 4))); + assert!(hash(&val) != hash(&zero_byte(val, 5))); + assert!(hash(&val) != hash(&zero_byte(val, 6))); + assert!(hash(&val) != hash(&zero_byte(val, 7))); + + fn zero_byte(val: u64, byte: uint) -> u64 { + assert!(byte < 8); + val & !(0xff << (byte * 8)) + } + } + + #[test] + fn test_hash_no_bytes_dropped_32() { + let val = 0xdeadbeef_u32; + + assert!(hash(&val) != hash(&zero_byte(val, 0))); + assert!(hash(&val) != hash(&zero_byte(val, 1))); + assert!(hash(&val) != hash(&zero_byte(val, 2))); + assert!(hash(&val) != hash(&zero_byte(val, 3))); + + fn zero_byte(val: u32, byte: uint) -> u32 { + assert!(byte < 4); + val & !(0xff << (byte * 8)) + } + } + + #[test] + fn test_hash_no_concat_alias() { + let s = ("aa", "bb"); + let t = ("aabb", ""); + let u = ("a", "abb"); + + assert!(s != t && t != u); + assert!(hash(&s) != hash(&t) && hash(&s) != hash(&u)); + + let v = (&[1u8], &[0u8, 0], &[0u8]); + let w = (&[1u8, 0, 0, 0], &[], &[]); + + assert!(v != w); + assert!(hash(&v) != hash(&w)); + } + + #[bench] + fn bench_str(bh: &mut BenchHarness) { + let s = "foo"; + bh.iter(|| { + assert_eq!(hash(&s), 16262950014981195938); + }) + } + + struct Compound { + x: u8, + y: u16, + z: ~str, + } + + impl Hash for Compound { + #[inline] + fn hash(&self, state: &mut S) { + self.x.hash(state); + self.y.hash(state); + self.z.hash(state); + } + } + + #[bench] + fn bench_compound_1(bh: &mut BenchHarness) { + let compound = Compound { + x: 1, + y: 2, + z: ~"foobarbaz", + }; + bh.iter(|| { + assert_eq!(hash(&compound), 3581836382593270478); + }) + } +} diff --git a/src/libstd/hash.rs b/src/libstd/hash_old.rs similarity index 98% rename from src/libstd/hash.rs rename to src/libstd/hash_old.rs index 432e27257f355..07b16db95f867 100644 --- a/src/libstd/hash.rs +++ b/src/libstd/hash_old.rs @@ -36,7 +36,7 @@ use to_bytes::IterBytes; use vec::ImmutableVector; // Alias `SipState` to `State`. -pub use State = hash::SipState; +pub use State = hash_old::SipState; /** * Types that can meaningfully be hashed should implement this. @@ -298,9 +298,13 @@ impl Streaming for SipState { #[cfg(test)] mod tests { - use super::*; - use prelude::*; - use super::SipState; + use super::{Hash, Streaming, SipState}; + use iter::Iterator; + use num::ToStrRadix; + use option::Some; + use str::OwnedStr; + use to_bytes::IterBytes; + use vec::{Vector, OwnedVector, ImmutableVector}; // Hash just the bytes of the slice, without length prefix struct Bytes<'a>(&'a [u8]); diff --git a/src/libstd/hashmap.rs b/src/libstd/hashmap.rs index 5d4db10672844..f3783c27b5472 100644 --- a/src/libstd/hashmap.rs +++ b/src/libstd/hashmap.rs @@ -57,7 +57,7 @@ use clone::Clone; use cmp::{Eq, Equiv, max}; use default::Default; use fmt; -use hash::Hash; +use hash_old::Hash; use iter; use iter::{Iterator, FromIterator, Extendable}; use iter::{FilterMap, Chain, Repeat, Zip}; diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index d3ddd9ae78380..6996cba42b40e 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -54,6 +54,10 @@ #[feature(macro_rules, globs, asm, managed_boxes, thread_local, link_args, simd)]; +// Turn on default type parameters. +#[feature(default_type_params)]; +#[allow(default_type_param_usage)]; + // Don't link to std. We are std. #[no_std]; @@ -141,6 +145,7 @@ pub mod iter; pub mod to_str; pub mod to_bytes; pub mod clone; +pub mod hash_old; pub mod hash; pub mod container; pub mod default; @@ -213,6 +218,7 @@ mod std { pub use cmp; pub use comm; pub use fmt; + pub use hash; pub use io; pub use kinds; pub use local_data; diff --git a/src/libstd/prelude.rs b/src/libstd/prelude.rs index d322650cec925..83ef85fc35b4a 100644 --- a/src/libstd/prelude.rs +++ b/src/libstd/prelude.rs @@ -45,7 +45,6 @@ pub use cmp::{Eq, Ord, TotalEq, TotalOrd, Ordering, Less, Equal, Greater, Equiv} pub use container::{Container, Mutable, Map, MutableMap, Set, MutableSet}; pub use default::Default; pub use from_str::FromStr; -pub use hash::Hash; pub use iter::{FromIterator, Extendable}; pub use iter::{Iterator, DoubleEndedIterator, RandomAccessIterator, CloneableIterator}; pub use iter::{OrdIterator, MutableDoubleEndedIterator, ExactSize}; diff --git a/src/libstd/to_str.rs b/src/libstd/to_str.rs index ab14e9f566778..0e5627fa06669 100644 --- a/src/libstd/to_str.rs +++ b/src/libstd/to_str.rs @@ -18,7 +18,7 @@ use option::{Some, None}; use str::OwnedStr; use hashmap::HashMap; use hashmap::HashSet; -use hash::Hash; +use hash_old::Hash; use iter::Iterator; use cmp::Eq; use vec::ImmutableVector; diff --git a/src/libsyntax/ext/deriving/generic.rs b/src/libsyntax/ext/deriving/generic.rs index a3bef1b5eddcb..99ac07ba4d7b0 100644 --- a/src/libsyntax/ext/deriving/generic.rs +++ b/src/libsyntax/ext/deriving/generic.rs @@ -416,11 +416,11 @@ impl<'a> TraitDef<'a> { self_type, methods.map(|x| *x))) } - pub fn expand_struct_def(&self, - cx: &mut ExtCtxt, - struct_def: &StructDef, - type_ident: Ident, - generics: &Generics) -> @ast::Item { + fn expand_struct_def(&self, + cx: &mut ExtCtxt, + struct_def: &StructDef, + type_ident: Ident, + generics: &Generics) -> @ast::Item { let methods = self.methods.map(|method_def| { let (explicit_self, self_args, nonself_args, tys) = method_def.split_self_nonself_args( @@ -450,7 +450,7 @@ impl<'a> TraitDef<'a> { self.create_derived_impl(cx, type_ident, generics, methods) } - pub fn expand_enum_def(&self, + fn expand_enum_def(&self, cx: &mut ExtCtxt, enum_def: &EnumDef, type_ident: Ident, diff --git a/src/libsyntax/ext/deriving/hash.rs b/src/libsyntax/ext/deriving/hash.rs new file mode 100644 index 0000000000000..0b9158547ed83 --- /dev/null +++ b/src/libsyntax/ext/deriving/hash.rs @@ -0,0 +1,97 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use ast::{MetaItem, Item, Expr, MutMutable}; +use codemap::Span; +use ext::base::ExtCtxt; +use ext::build::AstBuilder; +use ext::deriving::generic::*; +use parse::token::InternedString; + +pub fn expand_deriving_hash(cx: &mut ExtCtxt, + span: Span, + mitem: @MetaItem, + item: @Item, + push: |@Item|) { + + let allow_default_type_param_usage = cx.attribute( + span, + cx.meta_list( + span, + InternedString::new("allow"), + ~[cx.meta_word(span, InternedString::new("default_type_param_usage"))])); + + let hash_trait_def = TraitDef { + span: span, + attributes: ~[allow_default_type_param_usage], + path: Path::new_(~["std", "hash", "Hash"], None, + ~[~Literal(Path::new_local("__H"))], true), + additional_bounds: ~[], + generics: LifetimeBounds { + lifetimes: ~[], + bounds: ~[("__H", ~[Path::new(~["std", "io", "Writer"])])], + }, + methods: ~[ + MethodDef { + name: "hash", + generics: LifetimeBounds::empty(), + explicit_self: borrowed_explicit_self(), + args: ~[Ptr(~Literal(Path::new_local("__H")), + Borrowed(None, MutMutable))], + ret_ty: nil_ty(), + inline: true, + const_nonmatching: false, + combine_substructure: hash_substructure + } + ] + }; + + hash_trait_def.expand(cx, mitem, item, push); +} + +fn hash_substructure(cx: &mut ExtCtxt, trait_span: Span, substr: &Substructure) -> @Expr { + let state_expr = match substr.nonself_args { + [state_expr] => state_expr, + _ => cx.span_bug(trait_span, "incorrect number of arguments in `deriving(Hash)`") + }; + let hash_ident = substr.method_ident; + let call_hash = |span, thing_expr| { + let expr = cx.expr_method_call(span, thing_expr, hash_ident, ~[state_expr]); + cx.stmt_expr(expr) + }; + let mut stmts = ~[]; + + let fields = match *substr.fields { + Struct(ref fs) => fs, + EnumMatching(index, variant, ref fs) => { + // Determine the discriminant. We will feed this value to the byte + // iteration function. + let discriminant = match variant.node.disr_expr { + Some(d) => d, + None => cx.expr_uint(trait_span, index) + }; + + stmts.push(call_hash(trait_span, discriminant)); + + fs + } + _ => cx.span_bug(trait_span, "impossible substructure in `deriving(Hash)`") + }; + + for &FieldInfo { self_, span, .. } in fields.iter() { + stmts.push(call_hash(span, self_)); + } + + if stmts.len() == 0 { + cx.span_bug(trait_span, "#[deriving(Hash)] needs at least one field"); + } + + cx.expr_block(cx.block(trait_span, stmts, None)) +} diff --git a/src/libsyntax/ext/deriving/mod.rs b/src/libsyntax/ext/deriving/mod.rs index 7c686e5cd6708..31ee99fbd260e 100644 --- a/src/libsyntax/ext/deriving/mod.rs +++ b/src/libsyntax/ext/deriving/mod.rs @@ -26,6 +26,7 @@ pub mod clone; pub mod iter_bytes; pub mod encodable; pub mod decodable; +pub mod hash; pub mod rand; pub mod to_str; pub mod show; @@ -74,6 +75,7 @@ pub fn expand_meta_deriving(cx: &mut ExtCtxt, "DeepClone" => expand!(clone::expand_deriving_deep_clone), "IterBytes" => expand!(iter_bytes::expand_deriving_iter_bytes), + "Hash" => expand!(hash::expand_deriving_hash), "Encodable" => expand!(encodable::expand_deriving_encodable), "Decodable" => expand!(decodable::expand_deriving_decodable), diff --git a/src/libsyntax/util/interner.rs b/src/libsyntax/util/interner.rs index aedff2d785492..1c801f7d97057 100644 --- a/src/libsyntax/util/interner.rs +++ b/src/libsyntax/util/interner.rs @@ -17,6 +17,7 @@ use ast::Name; use std::cast; use std::cell::RefCell; use std::cmp::Equiv; +use std::hash_old::Hash; use std::hashmap::HashMap; use std::rc::Rc; diff --git a/src/test/compile-fail/deriving-span-Hash-enum-struct-variant.rs b/src/test/compile-fail/deriving-span-Hash-enum-struct-variant.rs new file mode 100644 index 0000000000000..182c669cbea1c --- /dev/null +++ b/src/test/compile-fail/deriving-span-Hash-enum-struct-variant.rs @@ -0,0 +1,27 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// This file was auto-generated using 'src/etc/generate-keyword-span-tests.py' + +#[feature(struct_variant)]; +extern crate extra; + +use std::hash::Hash; + +struct Error; + +#[deriving(Hash)] +enum Enum { + A { + x: Error //~ ERROR + } +} + +fn main() {} diff --git a/src/test/compile-fail/deriving-span-Hash-enum.rs b/src/test/compile-fail/deriving-span-Hash-enum.rs new file mode 100644 index 0000000000000..7617e0a33c385 --- /dev/null +++ b/src/test/compile-fail/deriving-span-Hash-enum.rs @@ -0,0 +1,27 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// This file was auto-generated using 'src/etc/generate-keyword-span-tests.py' + +#[feature(struct_variant)]; +extern crate extra; + +use std::hash::Hash; + +struct Error; + +#[deriving(Hash)] +enum Enum { + A( + Error //~ ERROR + ) +} + +fn main() {} diff --git a/src/test/compile-fail/deriving-span-Hash-struct.rs b/src/test/compile-fail/deriving-span-Hash-struct.rs new file mode 100644 index 0000000000000..f20da9a9d16a7 --- /dev/null +++ b/src/test/compile-fail/deriving-span-Hash-struct.rs @@ -0,0 +1,25 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// This file was auto-generated using 'src/etc/generate-keyword-span-tests.py' + +#[feature(struct_variant)]; +extern crate extra; + +use std::hash::Hash; + +struct Error; + +#[deriving(Hash)] +struct Struct { + x: Error //~ ERROR +} + +fn main() {} diff --git a/src/test/compile-fail/deriving-span-Hash-tuple-struct.rs b/src/test/compile-fail/deriving-span-Hash-tuple-struct.rs new file mode 100644 index 0000000000000..9b7ae50b738b8 --- /dev/null +++ b/src/test/compile-fail/deriving-span-Hash-tuple-struct.rs @@ -0,0 +1,25 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// This file was auto-generated using 'src/etc/generate-keyword-span-tests.py' + +#[feature(struct_variant)]; +extern crate extra; + +use std::hash::Hash; + +struct Error; + +#[deriving(Hash)] +struct Struct( + Error //~ ERROR +); + +fn main() {} diff --git a/src/test/run-pass/deriving-hash.rs b/src/test/run-pass/deriving-hash.rs new file mode 100644 index 0000000000000..087b7ce56ab47 --- /dev/null +++ b/src/test/run-pass/deriving-hash.rs @@ -0,0 +1,28 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-fast + +use std::hash; +use std::hash::Hash; + +#[deriving(Hash)] +struct Person { + id: uint, + name: ~str, + phone: uint, +} + +fn main() { + let person1 = Person { id: 5, name: ~"Janet", phone: 555_666_7777 }; + let person2 = Person { id: 5, name: ~"Bob", phone: 555_666_7777 }; + assert!(hash::hash(&person1) == hash::hash(&person1)); + assert!(hash::hash(&person1) != hash::hash(&person2)); +} diff --git a/src/test/run-pass/deriving-meta-multiple.rs b/src/test/run-pass/deriving-meta-multiple.rs index c65cae6d3b954..49bf101954b71 100644 --- a/src/test/run-pass/deriving-meta-multiple.rs +++ b/src/test/run-pass/deriving-meta-multiple.rs @@ -10,6 +10,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use std::hash_old::Hash; + #[deriving(Eq)] #[deriving(Clone)] #[deriving(IterBytes)] diff --git a/src/test/run-pass/deriving-meta.rs b/src/test/run-pass/deriving-meta.rs index cbe54790404e9..93193fc9d65e0 100644 --- a/src/test/run-pass/deriving-meta.rs +++ b/src/test/run-pass/deriving-meta.rs @@ -10,6 +10,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use std::hash_old::Hash; + #[deriving(Eq, Clone, IterBytes)] struct Foo { bar: uint, diff --git a/src/test/run-pass/typeid-intrinsic.rs b/src/test/run-pass/typeid-intrinsic.rs index 71e19b51c7266..2d5c1d100be8b 100644 --- a/src/test/run-pass/typeid-intrinsic.rs +++ b/src/test/run-pass/typeid-intrinsic.rs @@ -15,6 +15,7 @@ extern crate other1 = "typeid-intrinsic"; extern crate other2 = "typeid-intrinsic2"; +use std::hash_old::Hash; use std::unstable::intrinsics; use std::unstable::intrinsics::TypeId;