@@ -113,8 +113,10 @@ pub struct RawTransferMetadata2 {
113113 pub data_offset : u32 ,
114114 /// `true` if AST is TypeScript.
115115 pub is_ts : bool ,
116+ /// Length of source text in UTF-8 bytes.
117+ pub source_len : u32 ,
116118 /// Unique ID of this allocator.
117- pub ( crate ) id : u32 ,
119+ pub id : u32 ,
118120 /// `true` if memory allocation backing the `FixedSizeAllocator` can be freed.
119121 /// Is `true` if allocator is currently owned by only Rust or JS, but not both.
120122 ///
@@ -124,25 +126,26 @@ pub struct RawTransferMetadata2 {
124126 /// Memory will be freed when the `FixedSizeAllocator` is dropped on Rust side.
125127 /// * Also be set to `true` if `FixedSizeAllocator` is dropped on Rust side.
126128 /// Memory will be freed in finalizer when JS garbage collector collects the buffer.
127- pub ( crate ) can_be_freed : AtomicBool ,
129+ pub can_be_freed : AtomicBool ,
128130 /// Pointer to start of original allocation backing the `Allocator`.
129- pub ( crate ) alloc_ptr : NonNull < u8 > ,
130- /// Padding to pad struct to size 32 .
131- pub ( crate ) _padding : u64 ,
131+ pub alloc_ptr : NonNull < u8 > ,
132+ /// Pointer to start of `Allocator` chunk .
133+ pub chunk_ptr : NonNull < u8 > ,
132134}
133135use RawTransferMetadata2 as RawTransferMetadata ;
134136
135137const METADATA_SIZE : usize = size_of :: < RawTransferMetadata > ( ) ;
136138
137139impl RawTransferMetadata {
138- fn new ( id : u32 , alloc_ptr : NonNull < u8 > ) -> Self {
140+ fn new ( id : u32 , alloc_ptr : NonNull < u8 > , chunk_ptr : NonNull < u8 > ) -> Self {
139141 Self {
140142 data_offset : 0 ,
141143 is_ts : false ,
144+ source_len : 0 ,
142145 id,
143146 can_be_freed : AtomicBool :: new ( true ) ,
144147 alloc_ptr,
145- _padding : 0 ,
148+ chunk_ptr ,
146149 }
147150 }
148151}
@@ -165,7 +168,8 @@ impl RawTransferMetadata {
165168const ALLOC_SIZE : usize = BUFFER_SIZE + TWO_GIB ;
166169const ALLOC_ALIGN : usize = TWO_GIB ;
167170
168- const ALLOC_LAYOUT : Layout = match Layout :: from_size_align ( ALLOC_SIZE , ALLOC_ALIGN ) {
171+ /// Layout of backing allocations for fixed-size allocators.
172+ pub const ALLOC_LAYOUT : Layout = match Layout :: from_size_align ( ALLOC_SIZE , ALLOC_ALIGN ) {
169173 Ok ( layout) => layout,
170174 Err ( _) => unreachable ! ( ) ,
171175} ;
@@ -222,7 +226,7 @@ impl FixedSizeAllocator {
222226 let allocator = unsafe { Allocator :: from_raw_parts ( chunk_ptr, CHUNK_SIZE ) } ;
223227
224228 // Store metadata after allocator chunk
225- let metadata = RawTransferMetadata :: new ( id, alloc_ptr) ;
229+ let metadata = RawTransferMetadata :: new ( id, alloc_ptr, chunk_ptr ) ;
226230 // SAFETY: `chunk_ptr` is at least `BUFFER_SIZE` bytes from the end of the allocation.
227231 // `CHUNK_SIZE` had the size of `RawTransferMetadata` subtracted from it.
228232 // So there is space within the allocation for `RawTransferMetadata` after the `Allocator`'s chunk.
@@ -251,13 +255,28 @@ impl FixedSizeAllocator {
251255}
252256
253257impl Drop for FixedSizeAllocator {
258+ // TODO: Delete logging
254259 fn drop ( & mut self ) {
255260 // Get pointer to start of allocation backing this `FixedSizeAllocator`
256261 let alloc_ptr = {
257- let metadata_ptr = self . allocator . end_ptr ( ) . cast :: < RawTransferMetadata > ( ) ;
258262 // SAFETY: `FixedSizeAllocator` is being dropped, so no other references to data in it may exist.
259263 // `FixedSizeAllocator::new` wrote `RawTransferMetadata` to the location pointed to by `end_ptr`.
260- let metadata = unsafe { metadata_ptr. as_ref ( ) } ;
264+ let metadata = unsafe { self . allocator . metadata ( ) } ;
265+
266+ // If `can_be_freed` is already `true`, either this `Allocator` was never sent to JS side,
267+ // or JS garbage collector already collected it. We can deallocate the memory.
268+ // If not, set it to `true` and exit. Memory will be freed when JS garbage collector
269+ // collects the buffer.
270+ //
271+ // Maybe a more relaxed `Ordering` would be OK, but I (@overlookmotel) am not sure,
272+ // so going with `Ordering::SeqCst` to be on safe side.
273+ // Deallocation only happens at the end of the whole process, so it shouldn't matter much.
274+ // TODO: Figure out if can use `Ordering::Relaxed`.
275+ let can_be_freed = metadata. can_be_freed . fetch_or ( true , Ordering :: SeqCst ) ;
276+ if !can_be_freed {
277+ return ;
278+ }
279+
261280 metadata. alloc_ptr
262281 } ;
263282
@@ -269,3 +288,42 @@ impl Drop for FixedSizeAllocator {
269288// SAFETY: `Allocator` is `Send`.
270289// Moving `alloc_ptr: NonNull<u8>` across threads along with the `Allocator` is safe.
271290unsafe impl Send for FixedSizeAllocator { }
291+
292+ impl Allocator {
293+ /// Get reference to [`RawTransferMetadata`] for this `Allocator`.
294+ ///
295+ /// # SAFETY
296+ ///
297+ /// * This [`Allocator`] must have been created by a fixed-size [`AllocatorPool`].
298+ /// * No mutable references to [`RawTransferMetadata`] for this `Allocator` can exist
299+ /// while the reference returned by this method lives.
300+ pub unsafe fn metadata ( & self ) -> & RawTransferMetadata {
301+ // SAFETY:
302+ // * Caller guarantees that this `Allocator` was created by a fixed-size `AllocatorPool`.
303+ // `FixedSizeAllocator::new` wrote `RawTransferMetadata` to the location pointed to by `end_ptr`.
304+ // * Caller guarantees no mutable references to `RawTransferMetadata` exist
305+ // while this reference lives.
306+ unsafe { self . end_ptr ( ) . cast :: < RawTransferMetadata > ( ) . as_ref ( ) }
307+ }
308+
309+ /// Get mutable reference to [`RawTransferMetadata`] for this `Allocator`.
310+ ///
311+ /// # SAFETY
312+ ///
313+ /// * This [`Allocator`] must have been created by a fixed-size [`AllocatorPool`].
314+ /// * No other references to [`RawTransferMetadata`] for this `Allocator` can exist
315+ /// while the reference returned by this method lives.
316+ ///
317+ /// Note: This means we must be sure that buffer cannot be garbage-collected on JS side,
318+ /// as that creates a `&` ref to the `RawTransferMetadata`, and the finalizer that runs
319+ /// when that happens can run on another thread.
320+ #[ expect( clippy:: mut_from_ref) ]
321+ pub unsafe fn metadata_mut ( & self ) -> & mut RawTransferMetadata {
322+ // SAFETY:
323+ // * Caller guarantees that this `Allocator` was created by a fixed-size `AllocatorPool`.
324+ // `FixedSizeAllocator::new` wrote `RawTransferMetadata` to the location pointed to by `end_ptr`.
325+ // * Caller guarantees no other references to `RawTransferMetadata` exist
326+ // while this reference lives.
327+ unsafe { self . end_ptr ( ) . cast :: < RawTransferMetadata > ( ) . as_mut ( ) }
328+ }
329+ }
0 commit comments