@@ -24,6 +24,7 @@ use compio::buf::{IoBuf, IoBufMut, SetLen};
2424use std:: {
2525 mem:: MaybeUninit ,
2626 ops:: { Deref , DerefMut } ,
27+ sync:: Arc ,
2728} ;
2829
2930/// A buffer wrapper that participates in memory pooling.
@@ -265,10 +266,9 @@ impl PooledBuffer {
265266 /// After calling this method, the PooledBuffer becomes empty and will not
266267 /// return memory to the pool on drop (the frozen Bytes owns the allocation).
267268 /// The returned `Bytes` is Arc-backed, allowing cheap clones.
268- pub fn freeze ( & mut self ) -> Bytes {
269+ pub fn freeze_to_bytes ( & mut self ) -> Bytes {
269270 let buf = std:: mem:: replace ( & mut self . inner , AlignedBuffer :: new ( ALIGNMENT ) ) ;
270271
271- // Update pool accounting
272272 if self . from_pool
273273 && let Some ( bucket_idx) = self . original_bucket_idx
274274 {
@@ -278,10 +278,37 @@ impl PooledBuffer {
278278 self . original_capacity = 0 ;
279279 self . original_bucket_idx = None ;
280280
281- // Zero copy: Bytes takes ownership of the AlignedBuffer
282- // and will drop it when refcount reaches zero
283281 Bytes :: from_owner ( buf)
284282 }
283+
284+ pub fn freeze ( & mut self ) -> FrozenPooledBuffer {
285+ let buf = std:: mem:: replace ( & mut self . inner , AlignedBuffer :: new ( ALIGNMENT ) ) ;
286+ let len = buf. len ( ) ;
287+
288+ // Transfer pool metadata to frozen buffer
289+ let pool_meta = if self . from_pool {
290+ Some ( PoolMeta {
291+ original_capacity : self . original_capacity ,
292+ original_bucket_idx : self . original_bucket_idx ,
293+ } )
294+ } else {
295+ None
296+ } ;
297+
298+ // Reset self, pool accounting now lives in FrozenPooledBuffer
299+ self . from_pool = false ;
300+ self . original_capacity = 0 ;
301+ self . original_bucket_idx = None ;
302+
303+ FrozenPooledBuffer {
304+ inner : Arc :: new ( FrozenInner {
305+ buffer : buf,
306+ pool_meta,
307+ } ) ,
308+ offset : 0 ,
309+ len,
310+ }
311+ }
285312}
286313
287314impl Deref for PooledBuffer {
@@ -346,3 +373,133 @@ impl IoBufMut for PooledBuffer {
346373 unsafe { std:: slice:: from_raw_parts_mut ( ptr, cap) }
347374 }
348375}
376+
377+ #[ derive( Debug , Clone ) ]
378+ struct PoolMeta {
379+ original_capacity : usize ,
380+ original_bucket_idx : Option < usize > ,
381+ }
382+
383+ #[ derive( Debug ) ]
384+ struct FrozenInner {
385+ buffer : AlignedBuffer ,
386+ pool_meta : Option < PoolMeta > ,
387+ }
388+
389+ impl Drop for FrozenInner {
390+ fn drop ( & mut self ) {
391+ if let Some ( ref meta) = self . pool_meta {
392+ let buf = std:: mem:: replace ( & mut self . buffer , AlignedBuffer :: new ( ALIGNMENT ) ) ;
393+ buf. return_to_pool ( meta. original_capacity , true ) ;
394+ }
395+ }
396+ }
397+
398+ #[ derive( Clone , Debug ) ]
399+ pub struct FrozenPooledBuffer {
400+ inner : Arc < FrozenInner > ,
401+ offset : usize ,
402+ len : usize ,
403+ }
404+
405+ impl FrozenPooledBuffer {
406+ /// Try to reclaim the underlying `PooledBuffer` without copying.
407+ ///
408+ /// Success when:
409+ /// 1. This is the sole `Arc` reference (refcount == 1)
410+ /// 2. This view covers the entire buffer (not a sub-slice)
411+ ///
412+ ///
413+ /// On success, pool accounting is transferred back to the returned `PooledBuffer`.
414+ /// On failure, `self` is return unchanged
415+ pub fn thaw ( self ) -> Result < PooledBuffer , FrozenPooledBuffer > {
416+ // Sub-slice views can't reclaim the whole buffer
417+ if self . offset != 0 || self . len != self . inner . buffer . len ( ) {
418+ return Err ( self ) ;
419+ }
420+
421+ match Arc :: try_unwrap ( self . inner ) {
422+ Ok ( mut frozen_inner) => {
423+ let buffer =
424+ std:: mem:: replace ( & mut frozen_inner. buffer , AlignedBuffer :: new ( ALIGNMENT ) ) ;
425+
426+ // Extract pool metadata and prevent FrozenInner::drop from returning the buffer to
427+ // the pool -> we are taking ownership
428+ let pool_meta = frozen_inner. pool_meta . take ( ) ;
429+ let ( from_pool, original_capacity, original_bucket_idx) = match pool_meta {
430+ Some ( meta) => ( true , meta. original_capacity , meta. original_bucket_idx ) ,
431+ None => ( false , buffer. capacity ( ) , None ) ,
432+ } ;
433+
434+ Ok ( PooledBuffer {
435+ from_pool,
436+ original_capacity,
437+ original_bucket_idx,
438+ inner : buffer,
439+ } )
440+ }
441+ Err ( arc) => Err ( FrozenPooledBuffer {
442+ inner : arc,
443+ offset : self . offset ,
444+ len : self . len ,
445+ } ) ,
446+ }
447+ }
448+
449+ /// Create a subslice view. Try to be cheap.
450+ /// Panics if the range is out of bounds
451+ pub fn slice ( & self , range : std:: ops:: Range < usize > ) -> FrozenPooledBuffer {
452+ assert ! (
453+ range. end <= self . len,
454+ "slice out of bounds: {}..{} but len is {}" ,
455+ range. start,
456+ range. end,
457+ self . len
458+ ) ;
459+
460+ FrozenPooledBuffer {
461+ inner : Arc :: clone ( & self . inner ) ,
462+ offset : self . offset + range. start ,
463+ len : range. end - range. start ,
464+ }
465+ }
466+
467+ pub fn len ( & self ) -> usize {
468+ self . len
469+ }
470+
471+ pub fn is_empty ( & self ) -> bool {
472+ self . len == 0
473+ }
474+
475+ pub fn is_aligned ( & self ) -> bool {
476+ ( self . as_ref ( ) . as_ptr ( ) as usize ) . is_multiple_of ( ALIGNMENT )
477+ }
478+ }
479+
480+ impl AsRef < [ u8 ] > for FrozenPooledBuffer {
481+ fn as_ref ( & self ) -> & [ u8 ] {
482+ & self . inner . buffer [ self . offset ..self . offset + self . len ]
483+ }
484+ }
485+
486+ impl Deref for FrozenPooledBuffer {
487+ type Target = [ u8 ] ;
488+
489+ fn deref ( & self ) -> & Self :: Target {
490+ self . as_ref ( )
491+ }
492+ }
493+
494+ impl PartialEq for FrozenPooledBuffer {
495+ fn eq ( & self , other : & Self ) -> bool {
496+ self . as_ref ( ) == other. as_ref ( )
497+ }
498+ }
499+
500+ /// Allow passing FrozenPooledBuffer directly to DirectFile's write methods without any copy
501+ impl IoBuf for FrozenPooledBuffer {
502+ fn as_init ( & self ) -> & [ u8 ] {
503+ self . as_ref ( )
504+ }
505+ }
0 commit comments