about summary refs log tree commit diff
path: root/src/libstd
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2017-03-09 08:26:17 +0000
committerbors <bors@rust-lang.org>2017-03-09 08:26:17 +0000
commit3087a1f39eaeac9d76c8b159dcc64de515bb2b83 (patch)
treebace37e5277d56098ee4d1bb0727d930d68b650c /src/libstd
parent5c9208faf1f180cd15cf93f74f1e57b24856d11e (diff)
parentf2886e8bda7d628ae0cc16e4fe579cbc2c6dc1b0 (diff)
downloadrust-3087a1f39eaeac9d76c8b159dcc64de515bb2b83.tar.gz
rust-3087a1f39eaeac9d76c8b159dcc64de515bb2b83.zip
Auto merge of #40368 - arielb1:rollup, r=arielb1
Rollup of 20 pull requests

- Successful merges: #40154, #40222, #40226, #40237, #40254, #40258, #40265, #40268, #40279, #40283, #40292, #40293, #40296, #40316, #40321, #40325, #40326, #40327, #40333, #40335
- Failed merges:
Diffstat (limited to 'src/libstd')
-rw-r--r--src/libstd/collections/hash/map.rs24
-rw-r--r--src/libstd/collections/hash/table.rs74
-rw-r--r--src/libstd/env.rs8
3 files changed, 82 insertions, 24 deletions
diff --git a/src/libstd/collections/hash/map.rs b/src/libstd/collections/hash/map.rs
index f0738fe9b70..f9b0ec479d7 100644
--- a/src/libstd/collections/hash/map.rs
+++ b/src/libstd/collections/hash/map.rs
@@ -396,8 +396,6 @@ pub struct HashMap<K, V, S = RandomState> {
     table: RawTable<K, V>,
 
     resize_policy: DefaultResizePolicy,
-
-    long_probes: bool,
 }
 
 /// Search for a pre-hashed key.
@@ -655,7 +653,6 @@ impl<K, V, S> HashMap<K, V, S>
             hash_builder: hash_builder,
             resize_policy: DefaultResizePolicy::new(),
             table: RawTable::new(0),
-            long_probes: false,
         }
     }
 
@@ -688,7 +685,6 @@ impl<K, V, S> HashMap<K, V, S>
             hash_builder: hash_builder,
             resize_policy: resize_policy,
             table: RawTable::new(raw_cap),
-            long_probes: false,
         }
     }
 
@@ -746,7 +742,7 @@ impl<K, V, S> HashMap<K, V, S>
             let min_cap = self.len().checked_add(additional).expect("reserve overflow");
             let raw_cap = self.resize_policy.raw_capacity(min_cap);
             self.resize(raw_cap);
-        } else if self.long_probes && remaining <= self.len() {
+        } else if self.table.tag() && remaining <= self.len() {
             // Probe sequence is too long and table is half full,
             // resize early to reduce probing length.
             let new_capacity = self.table.capacity() * 2;
@@ -763,7 +759,6 @@ impl<K, V, S> HashMap<K, V, S>
         assert!(self.table.size() <= new_raw_cap);
         assert!(new_raw_cap.is_power_of_two() || new_raw_cap == 0);
 
-        self.long_probes = false;
         let mut old_table = replace(&mut self.table, RawTable::new(new_raw_cap));
         let old_size = old_table.size();
 
@@ -844,8 +839,7 @@ impl<K, V, S> HashMap<K, V, S>
     /// If the key already exists, the hashtable will be returned untouched
     /// and a reference to the existing element will be returned.
     fn insert_hashed_nocheck(&mut self, hash: SafeHash, k: K, v: V) -> Option<V> {
-        let entry = search_hashed(&mut self.table, hash, |key| *key == k)
-            .into_entry(k, &mut self.long_probes);
+        let entry = search_hashed(&mut self.table, hash, |key| *key == k).into_entry(k);
         match entry {
             Some(Occupied(mut elem)) => Some(elem.insert(v)),
             Some(Vacant(elem)) => {
@@ -1002,7 +996,7 @@ impl<K, V, S> HashMap<K, V, S>
         self.reserve(1);
         let hash = self.make_hash(&key);
         search_hashed(&mut self.table, hash, |q| q.eq(&key))
-            .into_entry(key, &mut self.long_probes).expect("unreachable")
+            .into_entry(key).expect("unreachable")
     }
 
     /// Returns the number of elements in the map.
@@ -1456,7 +1450,7 @@ impl<K, V, M> InternalEntry<K, V, M> {
 
 impl<'a, K, V> InternalEntry<K, V, &'a mut RawTable<K, V>> {
     #[inline]
-    fn into_entry(self, key: K, long_probes: &'a mut bool) -> Option<Entry<'a, K, V>> {
+    fn into_entry(self, key: K) -> Option<Entry<'a, K, V>> {
         match self {
             InternalEntry::Occupied { elem } => {
                 Some(Occupied(OccupiedEntry {
@@ -1469,7 +1463,6 @@ impl<'a, K, V> InternalEntry<K, V, &'a mut RawTable<K, V>> {
                     hash: hash,
                     key: key,
                     elem: elem,
-                    long_probes: long_probes,
                 }))
             }
             InternalEntry::TableIsEmpty => None,
@@ -1542,7 +1535,6 @@ pub struct VacantEntry<'a, K: 'a, V: 'a> {
     hash: SafeHash,
     key: K,
     elem: VacantEntryState<K, V, &'a mut RawTable<K, V>>,
-    long_probes: &'a mut bool,
 }
 
 #[stable(feature= "debug_hash_map", since = "1.12.0")]
@@ -2117,15 +2109,15 @@ impl<'a, K: 'a, V: 'a> VacantEntry<'a, K, V> {
     #[stable(feature = "rust1", since = "1.0.0")]
     pub fn insert(self, value: V) -> &'a mut V {
         match self.elem {
-            NeqElem(bucket, disp) => {
+            NeqElem(mut bucket, disp) => {
                 if disp >= DISPLACEMENT_THRESHOLD {
-                    *self.long_probes = true;
+                    bucket.table_mut().set_tag(true);
                 }
                 robin_hood(bucket, disp, self.hash, self.key, value)
             },
-            NoElem(bucket, disp) => {
+            NoElem(mut bucket, disp) => {
                 if disp >= DISPLACEMENT_THRESHOLD {
-                    *self.long_probes = true;
+                    bucket.table_mut().set_tag(true);
                 }
                 bucket.put(self.hash, self.key, value).into_mut_refs().1
             },
diff --git a/src/libstd/collections/hash/table.rs b/src/libstd/collections/hash/table.rs
index 9e92b475014..0e225b2964f 100644
--- a/src/libstd/collections/hash/table.rs
+++ b/src/libstd/collections/hash/table.rs
@@ -34,6 +34,42 @@ type HashUint = usize;
 
 const EMPTY_BUCKET: HashUint = 0;
 
+/// Special `Unique<HashUint>` that uses the lower bit of the pointer
+/// to expose a boolean tag.
+/// Note: when the pointer is initialized to EMPTY `.ptr()` will return
+/// null and the tag functions shouldn't be used.
+struct TaggedHashUintPtr(Unique<HashUint>);
+
+impl TaggedHashUintPtr {
+    #[inline]
+    unsafe fn new(ptr: *mut HashUint) -> Self {
+        debug_assert!(ptr as usize & 1 == 0 || ptr as usize == EMPTY as usize);
+        TaggedHashUintPtr(Unique::new(ptr))
+    }
+
+    #[inline]
+    fn set_tag(&mut self, value: bool) {
+        let usize_ptr = &*self.0 as *const *mut HashUint as *mut usize;
+        unsafe {
+            if value {
+                *usize_ptr |= 1;
+            } else {
+                *usize_ptr &= !1;
+            }
+        }
+    }
+
+    #[inline]
+    fn tag(&self) -> bool {
+        (*self.0 as usize) & 1 == 1
+    }
+
+    #[inline]
+    fn ptr(&self) -> *mut HashUint {
+        (*self.0 as usize & !1) as *mut HashUint
+    }
+}
+
 /// The raw hashtable, providing safe-ish access to the unzipped and highly
 /// optimized arrays of hashes, and key-value pairs.
 ///
@@ -72,10 +108,14 @@ const EMPTY_BUCKET: HashUint = 0;
 /// around just the "table" part of the hashtable. It enforces some
 /// invariants at the type level and employs some performance trickery,
 /// but in general is just a tricked out `Vec<Option<(u64, K, V)>>`.
+///
+/// The hashtable also exposes a special boolean tag. The tag defaults to false
+/// when the RawTable is created and is accessible with the `tag` and `set_tag`
+/// functions.
 pub struct RawTable<K, V> {
     capacity: usize,
     size: usize,
-    hashes: Unique<HashUint>,
+    hashes: TaggedHashUintPtr,
 
     // Because K/V do not appear directly in any of the types in the struct,
     // inform rustc that in fact instances of K and V are reachable from here.
@@ -208,6 +248,10 @@ impl<K, V, M> FullBucket<K, V, M> {
     pub fn table(&self) -> &M {
         &self.table
     }
+    /// Borrow a mutable reference to the table.
+    pub fn table_mut(&mut self) -> &mut M {
+        &mut self.table
+    }
     /// Move out the reference to the table.
     pub fn into_table(self) -> M {
         self.table
@@ -227,6 +271,10 @@ impl<K, V, M> EmptyBucket<K, V, M> {
     pub fn table(&self) -> &M {
         &self.table
     }
+    /// Borrow a mutable reference to the table.
+    pub fn table_mut(&mut self) -> &mut M {
+        &mut self.table
+    }
 }
 
 impl<K, V, M> Bucket<K, V, M> {
@@ -687,7 +735,7 @@ impl<K, V> RawTable<K, V> {
             return RawTable {
                 size: 0,
                 capacity: 0,
-                hashes: Unique::new(EMPTY as *mut HashUint),
+                hashes: TaggedHashUintPtr::new(EMPTY as *mut HashUint),
                 marker: marker::PhantomData,
             };
         }
@@ -728,7 +776,7 @@ impl<K, V> RawTable<K, V> {
         RawTable {
             capacity: capacity,
             size: 0,
-            hashes: Unique::new(hashes),
+            hashes: TaggedHashUintPtr::new(hashes),
             marker: marker::PhantomData,
         }
     }
@@ -737,13 +785,13 @@ impl<K, V> RawTable<K, V> {
         let hashes_size = self.capacity * size_of::<HashUint>();
         let pairs_size = self.capacity * size_of::<(K, V)>();
 
-        let buffer = *self.hashes as *mut u8;
+        let buffer = self.hashes.ptr() as *mut u8;
         let (pairs_offset, _, oflo) =
             calculate_offsets(hashes_size, pairs_size, align_of::<(K, V)>());
         debug_assert!(!oflo, "capacity overflow");
         unsafe {
             RawBucket {
-                hash: *self.hashes,
+                hash: self.hashes.ptr(),
                 pair: buffer.offset(pairs_offset as isize) as *const _,
                 _marker: marker::PhantomData,
             }
@@ -755,7 +803,7 @@ impl<K, V> RawTable<K, V> {
     pub fn new(capacity: usize) -> RawTable<K, V> {
         unsafe {
             let ret = RawTable::new_uninitialized(capacity);
-            ptr::write_bytes(*ret.hashes, 0, capacity);
+            ptr::write_bytes(ret.hashes.ptr(), 0, capacity);
             ret
         }
     }
@@ -774,7 +822,7 @@ impl<K, V> RawTable<K, V> {
     fn raw_buckets(&self) -> RawBuckets<K, V> {
         RawBuckets {
             raw: self.first_bucket_raw(),
-            hashes_end: unsafe { self.hashes.offset(self.capacity as isize) },
+            hashes_end: unsafe { self.hashes.ptr().offset(self.capacity as isize) },
             marker: marker::PhantomData,
         }
     }
@@ -832,6 +880,16 @@ impl<K, V> RawTable<K, V> {
             marker: marker::PhantomData,
         }
     }
+
+    /// Set the table tag
+    pub fn set_tag(&mut self, value: bool) {
+        self.hashes.set_tag(value)
+    }
+
+    /// Get the table tag
+    pub fn tag(&self) -> bool {
+        self.hashes.tag()
+    }
 }
 
 /// A raw iterator. The basis for some other iterators in this module. Although
@@ -1156,7 +1214,7 @@ unsafe impl<#[may_dangle] K, #[may_dangle] V> Drop for RawTable<K, V> {
         debug_assert!(!oflo, "should be impossible");
 
         unsafe {
-            deallocate(*self.hashes as *mut u8, size, align);
+            deallocate(self.hashes.ptr() as *mut u8, size, align);
             // Remember how everything was allocated out of one buffer
             // during initialization? We only need one call to free here.
         }
diff --git a/src/libstd/env.rs b/src/libstd/env.rs
index dd4f1ff4f5e..64eb52e28bc 100644
--- a/src/libstd/env.rs
+++ b/src/libstd/env.rs
@@ -590,6 +590,10 @@ pub fn current_exe() -> io::Result<PathBuf> {
 ///
 /// This structure is created through the [`std::env::args`] function.
 ///
+/// The first element is traditionally the path of the executable, but it can be
+/// set to arbitrary text, and may not even exist. This means this property should
+/// not be relied upon for security purposes.
+///
 /// [`String`]: ../string/struct.String.html
 /// [`std::env::args`]: ./fn.args.html
 #[stable(feature = "env", since = "1.0.0")]
@@ -600,6 +604,10 @@ pub struct Args { inner: ArgsOs }
 ///
 /// This structure is created through the [`std::env::args_os`] function.
 ///
+/// The first element is traditionally the path of the executable, but it can be
+/// set to arbitrary text, and may not even exist. This means this property should
+/// not be relied upon for security purposes.
+///
 /// [`OsString`]: ../ffi/struct.OsString.html
 /// [`std::env::args_os`]: ./fn.args_os.html
 #[stable(feature = "env", since = "1.0.0")]