Skip to content

Commit 3b350d7

Browse files
committed
Auto merge of #553 - Amanieu:allocation_size, r=Amanieu
Re-introduce a way to get the allocation size of a table This was previously removed from `RawTable` in #546. This is now added as a public API on `HashMap`, `HashSet` and `HashTable`. Fixes #238 Fixes #506
2 parents 4b0c778 + 1089b93 commit 3b350d7

File tree

4 files changed

+97
-0
lines changed

4 files changed

+97
-0
lines changed

Diff for: src/map.rs

+19
Original file line numberDiff line numberDiff line change
@@ -1929,6 +1929,16 @@ where
19291929
let hash = make_hash::<Q, S>(&self.hash_builder, k);
19301930
self.table.remove_entry(hash, equivalent_key(k))
19311931
}
1932+
1933+
/// Returns the total amount of memory allocated internally by the hash
1934+
/// set, in bytes.
1935+
///
1936+
/// The returned number is informational only. It is intended to be
1937+
/// primarily used for memory profiling.
1938+
#[inline]
1939+
pub fn allocation_size(&self) -> usize {
1940+
self.table.allocation_size()
1941+
}
19321942
}
19331943

19341944
impl<K, V, S, A> PartialEq for HashMap<K, V, S, A>
@@ -6416,4 +6426,13 @@ mod test_map {
64166426
// All allocator clones should already be dropped.
64176427
assert_eq!(dropped.load(Ordering::SeqCst), 0);
64186428
}
6429+
6430+
#[test]
6431+
fn test_allocation_info() {
6432+
assert_eq!(HashMap::<(), ()>::new().allocation_size(), 0);
6433+
assert_eq!(HashMap::<u32, u32>::new().allocation_size(), 0);
6434+
assert!(
6435+
HashMap::<u32, u32>::with_capacity(1).allocation_size() > core::mem::size_of::<u32>()
6436+
);
6437+
}
64196438
}

Diff for: src/raw/mod.rs

+39
Original file line numberDiff line numberDiff line change
@@ -771,6 +771,18 @@ impl<T, A: Allocator> RawTable<T, A> {
771771
NonNull::new_unchecked(self.data_end().as_ptr().wrapping_sub(self.buckets()))
772772
}
773773

774+
/// Returns the total amount of memory allocated internally by the hash
775+
/// table, in bytes.
776+
///
777+
/// The returned number is informational only. It is intended to be
778+
/// primarily used for memory profiling.
779+
#[inline]
780+
pub fn allocation_size(&self) -> usize {
781+
// SAFETY: We use the same `table_layout` that was used to allocate
782+
// this table.
783+
unsafe { self.table.allocation_size_or_zero(Self::TABLE_LAYOUT) }
784+
}
785+
774786
/// Returns the index of a bucket from a `Bucket`.
775787
#[inline]
776788
pub unsafe fn bucket_index(&self, bucket: &Bucket<T>) -> usize {
@@ -3056,6 +3068,33 @@ impl RawTableInner {
30563068
)
30573069
}
30583070

3071+
/// Returns the total amount of memory allocated internally by the hash
3072+
/// table, in bytes.
3073+
///
3074+
/// The returned number is informational only. It is intended to be
3075+
/// primarily used for memory profiling.
3076+
///
3077+
/// # Safety
3078+
///
3079+
/// The `table_layout` must be the same [`TableLayout`] as the `TableLayout`
3080+
/// that was used to allocate this table. Failure to comply with this condition
3081+
/// may result in [`undefined behavior`].
3082+
///
3083+
///
3084+
/// [`undefined behavior`]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html
3085+
#[inline]
3086+
unsafe fn allocation_size_or_zero(&self, table_layout: TableLayout) -> usize {
3087+
if self.is_empty_singleton() {
3088+
0
3089+
} else {
3090+
// SAFETY:
3091+
// 1. We have checked that our table is allocated.
3092+
// 2. The caller ensures that `table_layout` matches the [`TableLayout`]
3093+
// that was used to allocate this table.
3094+
unsafe { self.allocation_info(table_layout).1.size() }
3095+
}
3096+
}
3097+
30593098
/// Marks all table buckets as empty without dropping their contents.
30603099
#[inline]
30613100
fn clear_no_drop(&mut self) {

Diff for: src/set.rs

+17
Original file line numberDiff line numberDiff line change
@@ -1230,6 +1230,16 @@ where
12301230
None => None,
12311231
}
12321232
}
1233+
1234+
/// Returns the total amount of memory allocated internally by the hash
1235+
/// set, in bytes.
1236+
///
1237+
/// The returned number is informational only. It is intended to be
1238+
/// primarily used for memory profiling.
1239+
#[inline]
1240+
pub fn allocation_size(&self) -> usize {
1241+
self.map.allocation_size()
1242+
}
12331243
}
12341244

12351245
impl<T, S, A> PartialEq for HashSet<T, S, A>
@@ -3055,4 +3065,11 @@ mod test_set {
30553065
// (and without the `map`, it does not).
30563066
let mut _set: HashSet<_> = (0..3).map(|_| ()).collect();
30573067
}
3068+
3069+
#[test]
3070+
fn test_allocation_info() {
3071+
assert_eq!(HashSet::<()>::new().allocation_size(), 0);
3072+
assert_eq!(HashSet::<u32>::new().allocation_size(), 0);
3073+
assert!(HashSet::<u32>::with_capacity(1).allocation_size() > core::mem::size_of::<u32>());
3074+
}
30583075
}

Diff for: src/table.rs

+22
Original file line numberDiff line numberDiff line change
@@ -1088,6 +1088,16 @@ where
10881088
) -> Option<[&'_ mut T; N]> {
10891089
self.raw.get_many_unchecked_mut(hashes, eq)
10901090
}
1091+
1092+
/// Returns the total amount of memory allocated internally by the hash
1093+
/// table, in bytes.
1094+
///
1095+
/// The returned number is informational only. It is intended to be
1096+
/// primarily used for memory profiling.
1097+
#[inline]
1098+
pub fn allocation_size(&self) -> usize {
1099+
self.raw.allocation_size()
1100+
}
10911101
}
10921102

10931103
impl<T, A> IntoIterator for HashTable<T, A>
@@ -2208,3 +2218,15 @@ where
22082218
}
22092219

22102220
impl<T, F, A: Allocator> FusedIterator for ExtractIf<'_, T, F, A> where F: FnMut(&mut T) -> bool {}
2221+
2222+
#[cfg(test)]
2223+
mod tests {
2224+
use super::HashTable;
2225+
2226+
#[test]
2227+
fn test_allocation_info() {
2228+
assert_eq!(HashTable::<()>::new().allocation_size(), 0);
2229+
assert_eq!(HashTable::<u32>::new().allocation_size(), 0);
2230+
assert!(HashTable::<u32>::with_capacity(1).allocation_size() > core::mem::size_of::<u32>());
2231+
}
2232+
}

0 commit comments

Comments
 (0)