@@ -52,7 +52,7 @@ fn layout_for_allocation(
5252 req_user_alignment : usize , // Alignment requested for the user pointer
5353) -> Result < ( Layout , usize ) , ( ) > {
5454 // The alignment for the total block must satisfy both metadata and user alignment.
55- let total_block_align = cmp:: max ( req_user_alignment, METADATA_ALIGN ) ;
55+ let total_align = cmp:: max ( req_user_alignment, METADATA_ALIGN ) ;
5656
5757 // Calculate the offset for the user data pointer.
5858 // The user pointer must start *after* the metadata AND satisfy `req_user_alignment`.
@@ -68,11 +68,10 @@ fn layout_for_allocation(
6868 } ;
6969
7070 // Calculate the total size needed for the allocation block.
71- let total_block_size = user_data_offset. checked_add ( req_user_size) . ok_or ( ( ) ) ?;
71+ let total_size = user_data_offset. checked_add ( req_user_size) . ok_or ( ( ) ) ?;
7272
7373 // Create the final layout for the allocator.
74- let total_layout =
75- Layout :: from_size_align ( total_block_size, total_block_align) . map_err ( |_| ( ) ) ?;
74+ let total_layout = Layout :: from_size_align ( total_size, total_align) . map_err ( |_| ( ) ) ?;
7675
7776 Ok ( ( total_layout, user_data_offset) )
7877}
@@ -86,12 +85,12 @@ fn layout_for_allocation(
8685/// `user_ptr` must point to the start of the user data area of a valid allocation
8786/// managed by this allocator. The memory layout must be as expected (Metadata immediately preceding).
8887#[ inline]
89- unsafe fn get_metadata_ptr_from_user_ptr ( user_ptr : NonNull < u8 > ) -> NonNull < Metadata > {
88+ unsafe fn get_metadata_ptr ( user_ptr : NonNull < u8 > ) -> NonNull < Metadata > {
9089 // Calculate the address directly before the user pointer.
91- let metadata_byte_ptr = user_ptr. as_ptr ( ) . sub ( METADATA_SIZE ) ;
90+ let metadata_ptr = user_ptr. as_ptr ( ) . sub ( METADATA_SIZE ) ;
9291 // Safety: Caller guarantees user_ptr points METADATA_SIZE bytes *after*
9392 // the start of a valid Metadata struct instance within the same allocated block.
94- NonNull :: new_unchecked ( metadata_byte_ptr . cast :: < Metadata > ( ) )
93+ NonNull :: new_unchecked ( metadata_ptr . cast :: < Metadata > ( ) )
9594}
9695
9796/// Recovers the original allocation pointer (as returned by `talc`) and the
@@ -101,27 +100,27 @@ unsafe fn get_metadata_ptr_from_user_ptr(user_ptr: NonNull<u8>) -> NonNull<Metad
101100/// `user_ptr` must be a non-null pointer returned by `malloc`, `aligned_alloc`, or `realloc`
102101/// from this allocator. The metadata preceding it must be intact.
103102#[ inline]
104- unsafe fn recover_alloc_ptr_and_layout ( user_ptr : NonNull < u8 > ) -> Result < ( NonNull < u8 > , Layout ) , ( ) > {
103+ unsafe fn recover_alloc_info ( user_ptr : NonNull < u8 > ) -> Result < ( NonNull < u8 > , Layout ) , ( ) > {
105104 // 1. Get pointer to metadata and read it to find original size and alignment.
106- let metadata_ptr = get_metadata_ptr_from_user_ptr ( user_ptr) ;
105+ let metadata_ptr = get_metadata_ptr ( user_ptr) ;
107106 // Safety: Pointer is assumed valid by caller.
108107 let metadata = metadata_ptr. as_ptr ( ) . read ( ) ;
109108
110109 // 2. Recalculate the layout and user_data_offset used for the original allocation.
111110 // Use the size and alignment stored *in the metadata*.
112- let ( original_total_layout , user_data_offset) =
111+ let ( original_layout , user_data_offset) =
113112 layout_for_allocation ( metadata. size , metadata. alignment ) ?;
114113
115114 // 3. Calculate the original allocation start pointer address.
116115 // user_ptr = alloc_ptr + user_data_offset
117116 // alloc_ptr = user_ptr - user_data_offset
118- let alloc_start_ptr_addr = user_ptr. as_ptr ( ) . wrapping_sub ( user_data_offset) ;
117+ let start_ptr_addr = user_ptr. as_ptr ( ) . wrapping_sub ( user_data_offset) ;
119118
120119 // Safety: We assume the subtraction is valid and yields the original non-null pointer
121120 // returned by talc.
122- let alloc_start_ptr = NonNull :: new_unchecked ( alloc_start_ptr_addr ) ;
121+ let start_ptr = NonNull :: new_unchecked ( start_ptr_addr ) ;
123122
124- Ok ( ( alloc_start_ptr , original_total_layout ) )
123+ Ok ( ( start_ptr , original_layout ) )
125124}
126125
127126// == C API Implementation ==
@@ -151,6 +150,33 @@ pub unsafe extern "C" fn heap_init(address: *mut u8, size: usize) -> bool {
151150 ALLOCATOR . lock ( ) . claim ( arena) . is_ok ( )
152151}
153152
153+ /// Returns the usable size of the memory block pointed to by `ptr`.
154+ /// This corresponds to the size originally requested during allocation (`malloc`, `aligned_alloc`, `realloc`).
155+ /// Returns 0 if `ptr` is null.
156+ ///
157+ /// # Safety
158+ /// - `ptr` must be null or a pointer previously returned by `malloc`, `realloc`,
159+ /// or `aligned_alloc` from this specific allocator instance and implementation.
160+ /// Passing any other pointer (including pointers offset from the original user pointer)
161+ /// leads to Undefined Behavior.
162+ /// - The behavior is undefined if the metadata preceding `ptr` has been corrupted.
163+ #[ no_mangle]
164+ pub unsafe extern "C" fn usable_size ( ptr : * mut c_void ) -> usize {
165+ // Standard behavior: usable_size(NULL) returns 0.
166+ let Some ( user_ptr) = NonNull :: new ( ptr. cast :: < u8 > ( ) ) else {
167+ return 0 ;
168+ } ;
169+
170+ // Retrieve the stored size from the metadata located just before the user pointer.
171+ // Safety: Assumes `ptr` is valid and points *after* our metadata header as designed.
172+ let metadata_ptr = get_metadata_ptr ( user_ptr) ;
173+ // Safety: Pointer is assumed valid by caller, points to readable Metadata.
174+ let metadata = metadata_ptr. as_ptr ( ) . read ( ) ;
175+
176+ // Return the user-requested size stored in the metadata.
177+ metadata. size
178+ }
179+
154180/// Internal allocation function implementing the core logic for `malloc` and `aligned_alloc`.
155181/// Handles layout calculation, allocation via `talc`, metadata writing, and returns the aligned user pointer.
156182///
@@ -171,20 +197,20 @@ unsafe fn allocate_internal(size: usize, alignment: usize) -> *mut c_void {
171197 // `alloc_ptr` points to the start of the talc allocation block.
172198 // Calculate the user pointer address based on the start and the offset.
173199 // Safety: alloc_ptr is valid and non-null, user_data_offset calculated correctly.
174- let user_ptr_addr = alloc_ptr. as_ptr ( ) . add ( user_data_offset) ;
200+ let user_ptr = alloc_ptr. as_ptr ( ) . add ( user_data_offset) ;
175201
176202 // Calculate the metadata pointer address (immediately before user_ptr).
177203 // Safety: user_ptr_addr is valid, and layout_for_allocation ensures
178204 // user_data_offset >= METADATA_SIZE.
179- let metadata_ptr_addr = user_ptr_addr . sub ( METADATA_SIZE ) ;
180- let metadata_ptr = metadata_ptr_addr . cast :: < Metadata > ( ) ;
205+ let metadata_ptr = user_ptr . sub ( METADATA_SIZE ) ;
206+ let metadata_ptr = metadata_ptr . cast :: < Metadata > ( ) ;
181207
182208 // Write the metadata (original requested size and alignment).
183209 // Safety: metadata_ptr points to valid, allocated space of METADATA_SIZE within the block.
184210 metadata_ptr. write ( Metadata { size, alignment } ) ;
185211
186212 // Return the user pointer (correctly aligned).
187- user_ptr_addr as * mut c_void
213+ user_ptr as * mut c_void
188214 }
189215 Err ( _) => ptr:: null_mut ( ) , // Allocation failed (OOM)
190216 }
@@ -205,8 +231,7 @@ pub unsafe extern "C" fn malloc(size: usize) -> *mut c_void {
205231 return ptr:: null_mut ( ) ;
206232 }
207233 // Use the natural alignment of usize as the default alignment for malloc.
208- let default_alignment = align_of :: < usize > ( ) ;
209- allocate_internal ( size, default_alignment)
234+ allocate_internal ( size, align_of :: < usize > ( ) )
210235}
211236
212237/// Allocates memory with specified alignment.
@@ -251,21 +276,18 @@ pub unsafe extern "C" fn free(ptr: *mut c_void) {
251276 } ;
252277
253278 // Recover the original allocation pointer and layout using the metadata stored before user_ptr.
254- match recover_alloc_ptr_and_layout ( user_ptr) {
255- Ok ( ( alloc_start_ptr , original_total_layout ) ) => {
256- // Safety: `alloc_start_ptr ` and `original_total_layout ` must correspond to a previous
279+ match recover_alloc_info ( user_ptr) {
280+ Ok ( ( start_ptr , original_layout ) ) => {
281+ // Safety: `start_ptr ` and `original_layout ` must correspond to a previous
257282 // allocation made by this allocator via `allocate_internal` or `realloc`.
258- // `recover_alloc_ptr_and_layout` guarantees this if `user_ptr` was valid.
259- ALLOCATOR
260- . lock ( )
261- . free ( alloc_start_ptr, original_total_layout) ;
283+ // `recover_alloc_info` guarantees this if `user_ptr` was valid.
284+ ALLOCATOR . lock ( ) . free ( start_ptr, original_layout) ;
262285 }
263286 Err ( _) => {
264287 // Indicates an internal logic error (layout recovery failed) or memory corruption
265288 // (metadata damaged or ptr wasn't from this allocator).
266289 // In a real system, log an error or trigger an assertion. Avoid crashing.
267290 // Leaking memory might be the safest option here if corruption is suspected.
268- return ;
269291 }
270292 }
271293}
@@ -275,50 +297,46 @@ pub unsafe extern "C" fn free(ptr: *mut c_void) {
275297///
276298/// # Safety
277299/// - `ptr` must be null or a pointer previously returned by `malloc`, `realloc`, or `aligned_alloc`.
278- /// - `new_size ` is the desired size for the new allocation.
300+ /// - `size ` is the desired size for the new allocation.
279301/// - If reallocation fails, the original pointer `ptr` remains valid and must still be freed.
280302/// - If reallocation succeeds, the original `ptr` is invalidated and the new pointer should be used.
281303#[ no_mangle]
282- pub unsafe extern "C" fn realloc ( ptr : * mut c_void , new_size : usize ) -> * mut c_void {
283- // Handle null pointer: standard requires this is equivalent to malloc(new_size ).
284- let Some ( user_ptr_non_null ) = NonNull :: new ( ptr. cast :: < u8 > ( ) ) else {
304+ pub unsafe extern "C" fn realloc ( ptr : * mut c_void , size : usize ) -> * mut c_void {
305+ // Handle null pointer: standard requires this is equivalent to malloc(size ).
306+ let Some ( user_ptr ) = NonNull :: new ( ptr. cast :: < u8 > ( ) ) else {
285307 // Use default alignment consistent with malloc.
286- let default_alignment = align_of :: < usize > ( ) ;
287- return allocate_internal ( new_size, default_alignment) ;
308+ return allocate_internal ( size, align_of :: < usize > ( ) ) ;
288309 } ;
289310
290- // Handle new_size == 0: standard requires this is equivalent to free(ptr) and returns null.
291- if new_size == 0 {
311+ // Handle size == 0: standard requires this is equivalent to free(ptr) and returns null.
312+ if size == 0 {
292313 free ( ptr) ;
293314 return ptr:: null_mut ( ) ;
294315 }
295316
296317 // --- Attempt Optimized Realloc ---
297318
298319 // 1. Recover original allocation info (pointer returned by talc and its layout).
299- let ( old_alloc_ptr, old_total_layout) = match recover_alloc_ptr_and_layout ( user_ptr_non_null ) {
320+ let ( old_alloc_ptr, old_total_layout) = match recover_alloc_info ( user_ptr ) {
300321 Ok ( res) => res,
301322 Err ( _) => return ptr:: null_mut ( ) , // Cannot recover layout - indicates corruption or invalid ptr.
302323 } ;
303324
304325 // 2. Read old metadata *again* to get the original alignment and user size.
305326 // Safety: Assumes ptr is valid and metadata readable.
306- let old_metadata = get_metadata_ptr_from_user_ptr ( user_ptr_non_null)
307- . as_ptr ( )
308- . read ( ) ;
309- let original_alignment = old_metadata. alignment ;
310- let old_user_size = old_metadata. size ;
327+ let old_metadata = get_metadata_ptr ( user_ptr) . as_ptr ( ) . read ( ) ;
328+ let alignment = old_metadata. alignment ;
311329
312330 // 3. Compare sizes and choose strategy.
313- match new_size . cmp ( & old_user_size ) {
331+ match size . cmp ( & old_metadata . size ) {
314332 Ordering :: Equal => {
315333 // Sizes are the same, no operation needed.
316334 ptr
317335 }
318336
319337 Ordering :: Greater => {
320338 // Calculate the required new total layout based on new user size and original alignment.
321- let ( new_total_layout, _) = match layout_for_allocation ( new_size , original_alignment ) {
339+ let ( new_total_layout, _) = match layout_for_allocation ( size , alignment ) {
322340 Ok ( l) => l,
323341 Err ( _) => return ptr:: null_mut ( ) , // New layout calculation failed.
324342 } ;
@@ -339,12 +357,9 @@ pub unsafe extern "C" fn realloc(ptr: *mut c_void, new_size: usize) -> *mut c_vo
339357 // We MUST update the metadata *in place*.
340358 drop ( allocator_lock) ; // Release lock before writing metadata
341359
342- let metadata_ptr = get_metadata_ptr_from_user_ptr ( user_ptr_non_null ) ;
360+ let metadata_ptr = get_metadata_ptr ( user_ptr ) ;
343361 // Safety: metadata_ptr is valid as pointer hasn't moved.
344- metadata_ptr. as_ptr ( ) . write ( Metadata {
345- size : new_size,
346- alignment : original_alignment,
347- } ) ;
362+ metadata_ptr. as_ptr ( ) . write ( Metadata { size, alignment } ) ;
348363
349364 // Return the original user pointer.
350365 ptr
@@ -355,17 +370,19 @@ pub unsafe extern "C" fn realloc(ptr: *mut c_void, new_size: usize) -> *mut c_vo
355370 drop ( allocator_lock) ;
356371
357372 // Allocate a completely new block.
358- let new_ptr_void = allocate_internal ( new_size , original_alignment ) ;
373+ let new_ptr_void = allocate_internal ( size , alignment ) ;
359374 if new_ptr_void. is_null ( ) {
360375 // Allocation failed, original pointer `ptr` is still valid.
361376 return ptr:: null_mut ( ) ;
362377 }
363378 let new_user_ptr = new_ptr_void. cast :: < u8 > ( ) ;
364379
365380 // Copy data from the old user pointer area to the new user pointer area.
366- let copy_size = old_user_size; // When growing, copy the original size.
367- // Safety: `ptr` and `new_user_ptr` are valid, non-overlapping.
368- ptr:: copy_nonoverlapping ( user_ptr_non_null. as_ptr ( ) , new_user_ptr, copy_size) ;
381+ let copy_size = old_metadata. size ;
382+
383+ // When growing, copy the original size.
384+ // Safety: `ptr` and `new_user_ptr` are valid, non-overlapping.
385+ ptr:: copy_nonoverlapping ( user_ptr. as_ptr ( ) , new_user_ptr, copy_size) ;
369386
370387 // Free the *original* allocation block (using recovered ptr and layout).
371388 // `free` will handle locking internally.
@@ -382,7 +399,7 @@ pub unsafe extern "C" fn realloc(ptr: *mut c_void, new_size: usize) -> *mut c_vo
382399 // --- Shrink ---
383400 // Calculate the new total layout for the smaller size.
384401 let ( new_total_layout, _new_user_data_offset) =
385- match layout_for_allocation ( new_size , original_alignment ) {
402+ match layout_for_allocation ( size , alignment ) {
386403 Ok ( l) => l,
387404 Err ( _) => return ptr:: null_mut ( ) , // Should not fail for smaller size if old layout was valid.
388405 } ;
@@ -395,40 +412,12 @@ pub unsafe extern "C" fn realloc(ptr: *mut c_void, new_size: usize) -> *mut c_vo
395412 allocator_lock. shrink ( old_alloc_ptr, old_total_layout, new_total_size) ;
396413 drop ( allocator_lock) ; // Release lock
397414
398- let metadata_ptr = get_metadata_ptr_from_user_ptr ( user_ptr_non_null ) ;
415+ let metadata_ptr = get_metadata_ptr ( user_ptr ) ;
399416 // Safety: metadata_ptr is valid.
400- metadata_ptr. as_ptr ( ) . write ( Metadata {
401- size : new_size,
402- alignment : original_alignment,
403- } ) ;
404- ptr // Return original pointer
417+ metadata_ptr. as_ptr ( ) . write ( Metadata { size, alignment } ) ;
418+
419+ // Return original pointer
420+ ptr
405421 }
406422 }
407423}
408-
409- /// Returns the usable size of the memory block pointed to by `ptr`.
410- /// This corresponds to the size originally requested during allocation (`malloc`, `aligned_alloc`, `realloc`).
411- /// Returns 0 if `ptr` is null.
412- ///
413- /// # Safety
414- /// - `ptr` must be null or a pointer previously returned by `malloc`, `realloc`,
415- /// or `aligned_alloc` from this specific allocator instance and implementation.
416- /// Passing any other pointer (including pointers offset from the original user pointer)
417- /// leads to Undefined Behavior.
418- /// - The behavior is undefined if the metadata preceding `ptr` has been corrupted.
419- #[ no_mangle]
420- pub unsafe extern "C" fn usable_size ( ptr : * mut c_void ) -> usize {
421- // Standard behavior: usable_size(NULL) returns 0.
422- let Some ( user_ptr) = NonNull :: new ( ptr. cast :: < u8 > ( ) ) else {
423- return 0 ;
424- } ;
425-
426- // Retrieve the stored size from the metadata located just before the user pointer.
427- // Safety: Assumes `ptr` is valid and points *after* our metadata header as designed.
428- let metadata_ptr = get_metadata_ptr_from_user_ptr ( user_ptr) ;
429- // Safety: Pointer is assumed valid by caller, points to readable Metadata.
430- let metadata = metadata_ptr. as_ptr ( ) . read ( ) ;
431-
432- // Return the user-requested size stored in the metadata.
433- metadata. size
434- }
0 commit comments