1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
use crate::;
use HasSegmentPool;
use AllocPolicy;
use ;
/// Returns the actual usable byte count of the allocation at `ptr`.
///
/// For small allocations this returns the size-class block size (which
/// may exceed the original allocation request because Mnemosyne rounds
/// up to the next size class). For large/huge allocations it returns
/// the distance from `ptr` to the end of the recorded payload mapping.
/// Returns `0` for a null pointer.
///
/// Mirrors `mi_usable_size` (mimalloc) and `malloc_usable_size`
/// (glibc/jemalloc): the value is the maximum number of bytes the
/// caller may dereference through `ptr` without overflowing the
/// allocation. Useful for Rust `Vec<T>` capacity-rounding and for any
/// caller that wants to know the allocator's actual reservation
/// without doing a follow-up `realloc`.
///
/// # Safety
///
/// `ptr` must either be null or be a pointer previously returned by a
/// Mnemosyne allocation entry point. Calling this with a pointer that
/// originated from a different allocator is undefined behavior; the
/// function uses the same segment-rounding classification as
/// `thread_free` and dereferences the resulting segment header.
/// # Examples
///
/// ```
/// use mnemosyne_local::{thread_alloc, thread_free, usable_size};
/// use mnemosyne_core::StandardPolicy;
/// use mnemosyne_backend::MemoryBackendWrapper as Backend;
///
/// // SAFETY: `p` is a live block from this allocator, which is exactly what
/// // `usable_size` requires; passing a foreign pointer is undefined behaviour.
/// unsafe {
/// let p = thread_alloc::<StandardPolicy, Backend>(100, 8);
/// assert!(!p.is_null());
///
/// // The answer is what the caller may dereference, not what it asked
/// // for: requests round up to a size class, so this is >= 100 and the
/// // whole reported span is writable.
/// let usable = usable_size(p);
/// assert!(usable >= 100);
/// p.write_bytes(0xFF, usable);
///
/// thread_free::<StandardPolicy, Backend>(p);
/// }
///
/// // Null reports zero rather than faulting.
/// // SAFETY: null is explicitly accepted by the contract.
/// assert_eq!(unsafe { usable_size(core::ptr::null_mut()) }, 0);
/// ```
pub unsafe
/// Returns the usable byte size of a large/huge allocation from its metadata
/// slot.
///
/// `pages[0].alloc_count` stores the requested payload size, not the allocator's
/// reservation. Large and huge mappings may include alignment or prefix slack,
/// so the actual usable span is the distance from `ptr` to the end of the
/// recorded raw mapping (`huge_mapping_suffix_from`), not the requested payload
/// length. This is the single authoritative size recovery shared by [`usable_size`]
/// and the free-profiling path.
///
/// # Safety
///
/// `ptr` must be a non-null user pointer from a Mnemosyne *large/huge*
/// allocation, so the pointer slot immediately preceding it holds a valid
/// segment header (`(ptr as *mut *mut Segment).sub(1)`).
pub unsafe
/// Returns a statistics snapshot for the current thread's allocator under
/// policy `P`.
///
/// `P` selects which allocator is reported, and is not decoration. ADR 0001
/// gives every `(backend, encryption mode)` pair its own `ThreadAllocator`
/// cache, so a process allocating through `HardenedPolicy` and a process
/// allocating through `StandardPolicy` have separate counters. Passing the
/// policy the caller actually allocates with is what makes the snapshot
/// describe their allocator rather than a neighbouring one (ADR 0008).
/// # Examples
///
/// ```
/// use mnemosyne_local::{thread_alloc, thread_allocator_stats, thread_free};
/// use mnemosyne_core::{StandardPolicy, policy::HardenedPolicy};
/// use mnemosyne_backend::MemoryBackendWrapper as Backend;
///
/// let before = thread_allocator_stats::<StandardPolicy, Backend>();
///
/// // SAFETY: allocated and freed once, under the policy named below.
/// let p = unsafe { thread_alloc::<StandardPolicy, Backend>(48, 8) };
/// assert!(!p.is_null());
///
/// let during = thread_allocator_stats::<StandardPolicy, Backend>();
/// assert_eq!(
/// during.current_thread_live_allocations,
/// before.current_thread_live_allocations + 1,
/// );
///
/// // `P` selects which allocator is reported. Each (backend, encryption mode)
/// // pair owns a separate cache, so asking under a policy you did not
/// // allocate with describes a different allocator — not this one.
/// let other = thread_allocator_stats::<HardenedPolicy, Backend>();
/// assert_eq!(other.current_thread_live_allocations, 0);
///
/// // SAFETY: `p` came from the allocation above and is freed exactly once.
/// unsafe { thread_free::<StandardPolicy, Backend>(p) };
/// ```