Skip to content

Commit e5f0a23

Browse files
committed
Encapsulate unsafe code in a raw module
1 parent 291ebbc commit e5f0a23

File tree

4 files changed

+287
-264
lines changed

4 files changed

+287
-264
lines changed

src/lib.rs

+1-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// We *mostly* avoid unsafe code, but `mod map_core` allows it to use `RawTable`.
1+
// We *mostly* avoid unsafe code, but `map::core::raw` allows it to use `RawTable` buckets.
22
#![deny(unsafe_code)]
33
#![doc(html_root_url = "https://docs.rs/indexmap/1/")]
44
#![cfg_attr(not(has_std), no_std)]
@@ -80,8 +80,6 @@ mod mutable_keys;
8080
mod serde;
8181
mod util;
8282

83-
mod map_core;
84-
8583
pub mod map;
8684
pub mod set;
8785

src/map.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
//! `IndexMap` is a hash table where the iteration order of the key-value
22
//! pairs is independent of the hash values of the keys.
33
4+
mod core;
5+
46
#[cfg(not(has_std))]
57
use std::vec::Vec;
68

@@ -21,12 +23,12 @@ use std::collections::hash_map::RandomState;
2123
use std::cmp::Ordering;
2224
use std::fmt;
2325

26+
use self::core::IndexMapCore;
2427
use equivalent::Equivalent;
25-
use map_core::IndexMapCore;
2628
use util::third;
2729
use {Bucket, Entries, HashValue};
2830

29-
pub use map_core::{Entry, OccupiedEntry, VacantEntry};
31+
pub use self::core::{Entry, OccupiedEntry, VacantEntry};
3032

3133
/// A hash table where the iteration order of the key-value pairs is independent
3234
/// of the hash values of the keys.

src/map_core.rs renamed to src/map/core.rs

+6-259
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
#![allow(unsafe_code)]
21
//! This is the core implementation that doesn't depend on the hasher at all.
32
//!
43
//! The methods of `IndexMapCore` don't use any Hash properties of K.
@@ -8,6 +7,8 @@
87
//!
98
//! However, we should probably not let this show in the public API or docs.
109
10+
mod raw;
11+
1112
#[cfg(not(has_std))]
1213
use std::vec::Vec;
1314

@@ -23,8 +24,6 @@ use equivalent::Equivalent;
2324
use util::enumerate;
2425
use {Bucket, Entries, HashValue};
2526

26-
type RawBucket = hashbrown::raw::Bucket<usize>;
27-
2827
/// Core of the map that does not depend on S
2928
pub(crate) struct IndexMapCore<K, V> {
3029
/// indices mapping from the entry hash to its index.
@@ -67,16 +66,8 @@ where
6766
V: fmt::Debug,
6867
{
6968
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
70-
struct DebugIndices<'a>(&'a RawTable<usize>);
71-
impl fmt::Debug for DebugIndices<'_> {
72-
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
73-
let indices = unsafe { self.0.iter().map(|raw_bucket| raw_bucket.read()) };
74-
f.debug_list().entries(indices).finish()
75-
}
76-
}
77-
7869
f.debug_struct("IndexMapCore")
79-
.field("indices", &DebugIndices(&self.indices))
70+
.field("indices", &raw::DebugIndices(&self.indices))
8071
.field("entries", &self.entries)
8172
.finish()
8273
}
@@ -168,8 +159,7 @@ impl<K, V> IndexMapCore<K, V> {
168159
pub(crate) fn pop(&mut self) -> Option<(K, V)> {
169160
if let Some(entry) = self.entries.pop() {
170161
let last = self.entries.len();
171-
let raw_bucket = self.find_index(entry.hash, last).unwrap();
172-
unsafe { self.indices.erase_no_drop(&raw_bucket) };
162+
self.erase_index(entry.hash, last);
173163
Some((entry.key, entry.value))
174164
} else {
175165
None
@@ -190,17 +180,6 @@ impl<K, V> IndexMapCore<K, V> {
190180
i
191181
}
192182

193-
/// Return the index in `entries` where an equivalent key can be found
194-
pub(crate) fn get_index_of<Q>(&self, hash: HashValue, key: &Q) -> Option<usize>
195-
where
196-
Q: ?Sized + Equivalent<K>,
197-
{
198-
match self.find_equivalent(hash, key) {
199-
Some(raw_bucket) => Some(unsafe { raw_bucket.read() }),
200-
None => None,
201-
}
202-
}
203-
204183
pub(crate) fn insert_full(&mut self, hash: HashValue, key: K, value: V) -> (usize, Option<V>)
205184
where
206185
K: Eq,
@@ -211,150 +190,6 @@ impl<K, V> IndexMapCore<K, V> {
211190
}
212191
}
213192

214-
pub(crate) fn entry(&mut self, hash: HashValue, key: K) -> Entry<K, V>
215-
where
216-
K: Eq,
217-
{
218-
match self.find_equivalent(hash, &key) {
219-
// Safety: The entry is created with a live raw bucket, at the same time we have a &mut
220-
// reference to the map, so it can not be modified further.
221-
Some(raw_bucket) => Entry::Occupied(OccupiedEntry {
222-
map: self,
223-
raw_bucket,
224-
key,
225-
}),
226-
None => Entry::Vacant(VacantEntry {
227-
map: self,
228-
hash,
229-
key,
230-
}),
231-
}
232-
}
233-
234-
/// Return the raw bucket with an equivalent key
235-
fn find_equivalent<Q>(&self, hash: HashValue, key: &Q) -> Option<RawBucket>
236-
where
237-
Q: ?Sized + Equivalent<K>,
238-
{
239-
self.indices.find(hash.get(), {
240-
|&i| Q::equivalent(key, &self.entries[i].key)
241-
})
242-
}
243-
244-
/// Return the raw bucket for the given index
245-
fn find_index(&self, hash: HashValue, index: usize) -> Option<RawBucket> {
246-
self.indices.find(hash.get(), |&i| i == index)
247-
}
248-
249-
/// Remove an entry by shifting all entries that follow it
250-
pub(crate) fn shift_remove_full<Q>(&mut self, hash: HashValue, key: &Q) -> Option<(usize, K, V)>
251-
where
252-
Q: ?Sized + Equivalent<K>,
253-
{
254-
match self.find_equivalent(hash, key) {
255-
Some(raw_bucket) => unsafe { Some(self.shift_remove_bucket(raw_bucket)) },
256-
None => None,
257-
}
258-
}
259-
260-
/// Remove an entry by shifting all entries that follow it
261-
pub(crate) fn shift_remove_index(&mut self, index: usize) -> Option<(K, V)> {
262-
let raw_bucket = match self.entries.get(index) {
263-
Some(entry) => self.find_index(entry.hash, index).unwrap(),
264-
None => return None,
265-
};
266-
unsafe {
267-
let (_, key, value) = self.shift_remove_bucket(raw_bucket);
268-
Some((key, value))
269-
}
270-
}
271-
272-
/// Remove an entry by shifting all entries that follow it
273-
///
274-
/// Safety: The caller must pass a live `raw_bucket`.
275-
#[allow(unused_unsafe)]
276-
unsafe fn shift_remove_bucket(&mut self, raw_bucket: RawBucket) -> (usize, K, V) {
277-
// use Vec::remove, but then we need to update the indices that point
278-
// to all of the other entries that have to move
279-
let index = unsafe {
280-
self.indices.erase_no_drop(&raw_bucket);
281-
raw_bucket.read()
282-
};
283-
let entry = self.entries.remove(index);
284-
285-
// correct indices that point to the entries that followed the removed entry.
286-
// use a heuristic between a full sweep vs. a `find()` for every shifted item.
287-
let raw_capacity = self.indices.buckets();
288-
let shifted_entries = &self.entries[index..];
289-
if shifted_entries.len() > raw_capacity / 2 {
290-
// shift all indices greater than `index`
291-
unsafe {
292-
for bucket in self.indices.iter() {
293-
let i = bucket.read();
294-
if i > index {
295-
bucket.write(i - 1);
296-
}
297-
}
298-
}
299-
} else {
300-
// find each following entry to shift its index
301-
for (i, entry) in (index + 1..).zip(shifted_entries) {
302-
let shifted_bucket = self.find_index(entry.hash, i).unwrap();
303-
unsafe { shifted_bucket.write(i - 1) };
304-
}
305-
}
306-
307-
(index, entry.key, entry.value)
308-
}
309-
310-
/// Remove an entry by swapping it with the last
311-
pub(crate) fn swap_remove_full<Q>(&mut self, hash: HashValue, key: &Q) -> Option<(usize, K, V)>
312-
where
313-
Q: ?Sized + Equivalent<K>,
314-
{
315-
match self.find_equivalent(hash, key) {
316-
Some(raw_bucket) => unsafe { Some(self.swap_remove_bucket(raw_bucket)) },
317-
None => None,
318-
}
319-
}
320-
321-
/// Remove an entry by swapping it with the last
322-
pub(crate) fn swap_remove_index(&mut self, index: usize) -> Option<(K, V)> {
323-
let raw_bucket = match self.entries.get(index) {
324-
Some(entry) => self.find_index(entry.hash, index).unwrap(),
325-
None => return None,
326-
};
327-
unsafe {
328-
let (_, key, value) = self.swap_remove_bucket(raw_bucket);
329-
Some((key, value))
330-
}
331-
}
332-
333-
/// Remove an entry by swapping it with the last
334-
///
335-
/// Safety: The caller must pass a live `raw_bucket`.
336-
#[allow(unused_unsafe)]
337-
unsafe fn swap_remove_bucket(&mut self, raw_bucket: RawBucket) -> (usize, K, V) {
338-
// use swap_remove, but then we need to update the index that points
339-
// to the other entry that has to move
340-
let index = unsafe {
341-
self.indices.erase_no_drop(&raw_bucket);
342-
raw_bucket.read()
343-
};
344-
let entry = self.entries.swap_remove(index);
345-
346-
// correct index that points to the entry that had to swap places
347-
if let Some(entry) = self.entries.get(index) {
348-
// was not last element
349-
// examine new element in `index` and find it in indices
350-
let last = self.entries.len();
351-
let swapped_bucket = self.find_index(entry.hash, last).unwrap();
352-
unsafe { swapped_bucket.write(index) };
353-
}
354-
355-
(index, entry.key, entry.value)
356-
}
357-
358193
pub(crate) fn retain_in_order<F>(&mut self, mut keep: F)
359194
where
360195
F: FnMut(&mut K, &mut V) -> bool,
@@ -381,20 +216,6 @@ impl<K, V> IndexMapCore<K, V> {
381216
}
382217
}
383218

384-
pub(crate) fn reverse(&mut self) {
385-
self.entries.reverse();
386-
387-
// No need to save hash indices, can easily calculate what they should
388-
// be, given that this is an in-place reversal.
389-
let len = self.entries.len();
390-
unsafe {
391-
for raw_bucket in self.indices.iter() {
392-
let i = raw_bucket.read();
393-
raw_bucket.write(len - i - 1);
394-
}
395-
}
396-
}
397-
398219
fn rebuild_hash_table(&mut self) {
399220
self.indices.clear_no_drop();
400221
debug_assert!(self.indices.capacity() >= self.entries.len());
@@ -487,52 +308,10 @@ impl<'a, K: 'a + fmt::Debug, V: 'a + fmt::Debug> fmt::Debug for Entry<'a, K, V>
487308
}
488309
}
489310

490-
/// A view into an occupied entry in a `IndexMap`.
491-
/// It is part of the [`Entry`] enum.
492-
///
493-
/// [`Entry`]: enum.Entry.html
494-
pub struct OccupiedEntry<'a, K: 'a, V: 'a> {
495-
map: &'a mut IndexMapCore<K, V>,
496-
raw_bucket: RawBucket,
497-
key: K,
498-
}
499-
500-
// `hashbrown::raw::Bucket` is only `Send`, not `Sync`.
501-
// SAFETY: `&self` only accesses the bucket to read it.
502-
unsafe impl<K: Sync, V: Sync> Sync for OccupiedEntry<'_, K, V> {}
311+
pub use self::raw::OccupiedEntry;
503312

313+
// Extra methods that don't threaten the unsafe encapsulation.
504314
impl<'a, K, V> OccupiedEntry<'a, K, V> {
505-
pub fn key(&self) -> &K {
506-
&self.key
507-
}
508-
509-
pub fn get(&self) -> &V {
510-
&self.map.entries[self.index()].value
511-
}
512-
513-
pub fn get_mut(&mut self) -> &mut V {
514-
let index = self.index();
515-
&mut self.map.entries[index].value
516-
}
517-
518-
/// Put the new key in the occupied entry's key slot
519-
pub(crate) fn replace_key(self) -> K {
520-
let index = self.index();
521-
let old_key = &mut self.map.entries[index].key;
522-
replace(old_key, self.key)
523-
}
524-
525-
/// Return the index of the key-value pair
526-
#[inline]
527-
pub fn index(&self) -> usize {
528-
unsafe { self.raw_bucket.read() }
529-
}
530-
531-
pub fn into_mut(self) -> &'a mut V {
532-
let index = self.index();
533-
&mut self.map.entries[index].value
534-
}
535-
536315
/// Sets the value of the entry to `value`, and returns the entry's old value.
537316
pub fn insert(&mut self, value: V) -> V {
538317
replace(self.get_mut(), value)
@@ -573,38 +352,6 @@ impl<'a, K, V> OccupiedEntry<'a, K, V> {
573352
pub fn remove_entry(self) -> (K, V) {
574353
self.swap_remove_entry()
575354
}
576-
577-
/// Remove and return the key, value pair stored in the map for this entry
578-
///
579-
/// Like `Vec::swap_remove`, the pair is removed by swapping it with the
580-
/// last element of the map and popping it off. **This perturbs
581-
/// the postion of what used to be the last element!**
582-
///
583-
/// Computes in **O(1)** time (average).
584-
pub fn swap_remove_entry(self) -> (K, V) {
585-
// This is safe because it can only happen once (self is consumed)
586-
// and map.indices have not been modified since entry construction
587-
unsafe {
588-
let (_, key, value) = self.map.swap_remove_bucket(self.raw_bucket);
589-
(key, value)
590-
}
591-
}
592-
593-
/// Remove and return the key, value pair stored in the map for this entry
594-
///
595-
/// Like `Vec::remove`, the pair is removed by shifting all of the
596-
/// elements that follow it, preserving their relative order.
597-
/// **This perturbs the index of all of those elements!**
598-
///
599-
/// Computes in **O(n)** time (average).
600-
pub fn shift_remove_entry(self) -> (K, V) {
601-
// This is safe because it can only happen once (self is consumed)
602-
// and map.indices have not been modified since entry construction
603-
unsafe {
604-
let (_, key, value) = self.map.shift_remove_bucket(self.raw_bucket);
605-
(key, value)
606-
}
607-
}
608355
}
609356

610357
impl<'a, K: 'a + fmt::Debug, V: 'a + fmt::Debug> fmt::Debug for OccupiedEntry<'a, K, V> {

0 commit comments

Comments
 (0)