#![cfg(feature = "std")]
use crate::{Context, SizeOf};
use core::{
cell::{Cell, UnsafeCell},
mem::size_of,
ptr::NonNull,
sync::atomic::AtomicBool,
};
use std::{
collections::hash_map::RandomState,
ffi::{OsStr, OsString},
net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6},
path::{Path, PathBuf},
sync::{Barrier, Condvar, Mutex, Once, RwLock},
thread::{Thread, ThreadId},
time::{Instant, SystemTime},
};
const PATH_ELEM_SIZE: usize = if cfg!(windows) {
size_of::<u16>()
} else {
size_of::<u8>()
};
impl_total_size_childless! {
Path,
OsStr,
Barrier,
Condvar,
Instant,
ThreadId,
SystemTime,
RandomState,
IpAddr,
Ipv4Addr,
Ipv6Addr,
SocketAddr,
SocketAddrV4,
SocketAddrV6,
}
impl SizeOf for OsString {
fn size_of_children(&self, context: &mut Context) {
if self.capacity() != 0 {
context
.add_vectorlike(self.len(), self.capacity(), PATH_ELEM_SIZE)
.add_distinct_allocation();
}
}
}
impl SizeOf for PathBuf {
fn size_of_children(&self, context: &mut Context) {
if self.capacity() != 0 {
context
.add_vectorlike(self.as_os_str().len(), self.capacity(), PATH_ELEM_SIZE)
.add_distinct_allocation();
}
}
}
impl<T> SizeOf for Mutex<T>
where
T: SizeOf,
{
fn size_of_children(&self, context: &mut Context) {
if cfg!(target_env = "sgx") {
context
.add(estimate_mutex_size_sgx::<T>())
.add_distinct_allocation();
}
if let Ok(contents) = self.lock() {
contents.size_of_children(context);
}
}
}
const fn estimate_mutex_size_sgx<T>() -> usize {
#[allow(dead_code)]
struct FakeSgxMutex<T> {
inner: FakeSpinMutex<FakeWaitVariable<bool>>,
value: UnsafeCell<T>,
}
#[allow(dead_code)]
struct FakeSpinMutex<T> {
value: UnsafeCell<T>,
lock: AtomicBool,
}
#[allow(dead_code)]
struct FakeWaitVariable<T> {
queue: FakeWaitQueue,
lock: T,
}
#[allow(dead_code)]
struct FakeWaitQueue {
inner: FakeUnsafeList<FakeSpinMutex<FakeWaitEntry>>,
}
#[allow(dead_code)]
struct FakeWaitEntry {
tcs: NonNull<u8>,
wake: bool,
}
#[allow(dead_code)]
struct FakeUnsafeList<T> {
head_tail: NonNull<FakeUnsafeListEntry<T>>,
head_tail_entry: Option<FakeUnsafeListEntry<T>>,
}
#[allow(dead_code)]
struct FakeUnsafeListEntry<T> {
next: NonNull<FakeUnsafeListEntry<T>>,
prev: NonNull<FakeUnsafeListEntry<T>>,
value: Option<T>,
}
size_of::<FakeSgxMutex<T>>()
}
impl<T> SizeOf for RwLock<T>
where
T: SizeOf,
{
fn size_of_children(&self, context: &mut Context) {
if let Ok(contents) = self.read() {
contents.size_of_children(context);
}
}
}
impl SizeOf for Once {
fn size_of_children(&self, context: &mut Context) {
#[allow(dead_code)]
struct FakeWaiter {
thread: Cell<Option<Thread>>,
signaled: AtomicBool,
next: *const FakeWaiter,
}
context
.add(size_of::<FakeWaiter>())
.add_distinct_allocation();
}
}
pub(crate) mod hashmap {
use crate::{Context, SizeOf};
use core::mem::{align_of, size_of};
use std::collections::{HashMap, HashSet};
#[inline]
const fn capacity_to_buckets(capacity: usize) -> usize {
if capacity == 0 {
0
} else if capacity < 4 {
4
} else if capacity < 8 {
8
} else {
(capacity * 8 / 7).next_power_of_two()
}
}
const GROUP_WIDTH: usize = if cfg!(any(
target_pointer_width = "64",
target_arch = "aarch64",
target_arch = "x86_64",
target_arch = "wasm32",
)) {
size_of::<u64>()
} else {
size_of::<u32>()
};
#[inline]
pub(crate) const fn calculate_layout_for<T>(buckets: usize) -> usize {
let align = if align_of::<T>() > GROUP_WIDTH {
align_of::<T>()
} else {
GROUP_WIDTH
};
let ctrl_offset = ((size_of::<T>() * buckets) + (align - 1)) & !(align - 1);
ctrl_offset + buckets + GROUP_WIDTH
}
#[inline]
pub(crate) const fn estimate_hashmap_size<K, V>(
length: usize,
capacity: usize,
) -> (usize, usize) {
if capacity == 0 {
(0, 0)
} else {
let buckets = capacity_to_buckets(capacity);
let table_layout = calculate_layout_for::<(K, V)>(buckets);
let used_layout = calculate_layout_for::<(K, V)>(length);
(table_layout, used_layout)
}
}
impl<K, S> SizeOf for HashSet<K, S>
where
K: SizeOf,
S: SizeOf,
{
fn size_of_children(&self, context: &mut Context) {
if self.capacity() != 0 {
let (total_bytes, used_bytes) =
estimate_hashmap_size::<K, ()>(self.len(), self.capacity());
context
.add(used_bytes)
.add_excess(total_bytes - used_bytes)
.add_distinct_allocation();
self.iter().for_each(|key| key.size_of_children(context));
}
self.hasher().size_of_children(context);
}
}
impl<K, V, S> SizeOf for HashMap<K, V, S>
where
K: SizeOf,
V: SizeOf,
S: SizeOf,
{
fn size_of_children(&self, context: &mut Context) {
if self.capacity() != 0 {
let (total_bytes, used_bytes) =
estimate_hashmap_size::<K, V>(self.len(), self.capacity());
context
.add(used_bytes)
.add_excess(total_bytes - used_bytes)
.add_distinct_allocation();
self.iter().for_each(|(key, value)| {
key.size_of_children(context);
value.size_of_children(context);
});
}
self.hasher().size_of_children(context);
}
}
}