99using ExtendedSystemObjects ;
1010using MemoryManager . Core ;
1111using System . Diagnostics ;
12+ using System . Runtime . CompilerServices ;
1213using System . Runtime . InteropServices ;
1314
1415namespace MemoryManager . Lanes
1516{
1617 /// <inheritdoc cref="IFastLane" />
1718 /// <summary>
18- /// LinearLane with Bump Allocator
19+ /// LinearLane with Bump Allocator
1920 /// </summary>
2021 /// <seealso cref="MemoryManager.Lanes.IFastLane" />
2122 /// <seealso cref="System.IDisposable" />
@@ -59,31 +60,43 @@ public sealed class LinearLane : IFastLane, IDisposable
5960 private int _nextFreeOffset ;
6061
6162 /// <summary>
62- /// The versions
63+ /// Pointer to the flat versions array. Index matches handle ID directly.
6364 /// </summary>
64- private readonly byte [ ] _versions ;
65+ private unsafe uint * _versions ;
66+
67+ /// <summary>
68+ /// Current capacity of the versions array.
69+ /// </summary>
70+ private int _versionsCapacity ;
71+
72+ /// <inheritdoc />
73+ public event Action < string > ? OnCompaction ;
74+
75+ /// <inheritdoc />
76+ public event Action < string , int , int > ? OnAllocationExtension ;
6577
6678 /// <summary>
6779 /// Initializes a new instance of the <see cref="LinearLane"/> class.
6880 /// </summary>
6981 /// <param name="size">The size.</param>
7082 /// <param name="slowLane">The slow lane.</param>
7183 /// <param name="maxEntries">The maximum entries.</param>
72- public LinearLane ( int size , SlowLane slowLane , int maxEntries = 1024 )
84+ public unsafe LinearLane ( int size , SlowLane slowLane , int maxEntries = 1024 )
7385 {
7486 _slowLane = slowLane ;
7587 Capacity = size ;
7688 Buffer = Marshal . AllocHGlobal ( size ) ;
77- _versions = new byte [ maxEntries ] ;
89+
90+ // INITIALIZATION: Allocate flat tracking for versions matching system high-water metrics
91+ _versionsCapacity = maxEntries + 1 ;
92+ _versions = ( uint * ) NativeMemory . AllocZeroed ( ( nuint ) _versionsCapacity , sizeof ( uint ) ) ;
93+
7894 _entries = new AllocationEntry [ maxEntries ] ;
7995 }
8096
8197 /// <summary>
8298 /// Gets the capacity.
8399 /// </summary>
84- /// <value>
85- /// The capacity.
86- /// </value>
87100 public int Capacity { get ; }
88101
89102 /// <inheritdoc />
@@ -92,59 +105,61 @@ public LinearLane(int size, SlowLane slowLane, int maxEntries = 1024)
92105 /// <summary>
93106 /// Gets the buffer.
94107 /// </summary>
95- /// <value>
96- /// The buffer.
97- /// </value>
98108 public nint Buffer { get ; private set ; }
99109
100110 /// <inheritdoc />
101111 public OneWayLane ? OneWayLane { get ; set ; }
102112
103- /// <summary>
104- /// Gets the redirects.
105- /// </summary>
106- /// <value>
107- /// The redirects.
108- /// </value>
109- private Dictionary < int , MemoryHandle > Redirects { get ; } = new ( ) ;
110-
111113 /// <inheritdoc />
112- public void Dispose ( )
114+ public unsafe void Dispose ( )
113115 {
114116 Marshal . FreeHGlobal ( Buffer ) ;
115117 _handleIndex . Clear ( ) ;
116- Redirects . Clear ( ) ;
118+
119+ if ( _versions != null )
120+ {
121+ NativeMemory . Free ( _versions ) ;
122+ _versions = null ;
123+ }
124+
117125 _entries = null ;
126+ GC . SuppressFinalize ( this ) ;
118127 }
119128
120129 /// <inheritdoc />
121130 public bool CanAllocate ( int size ) => _nextFreeOffset + size <= Capacity ;
122131
123132 /// <inheritdoc />
124- public MemoryHandle Allocate ( int size , AllocationPriority priority = AllocationPriority . Normal ,
133+ public unsafe MemoryHandle Allocate ( int size , AllocationPriority priority = AllocationPriority . Normal ,
125134 AllocationHints hints = AllocationHints . None , string ? debugName = null , int currentFrame = 0 )
126135 {
127136 if ( _entries == null ) throw new InvalidOperationException ( "LinearLane: Memory not reserved" ) ;
128137 if ( ! CanAllocate ( size ) )
129138 throw new OutOfMemoryException ( "LinearLane: Cannot allocate - Buffer is full. Requires Compaction." ) ;
130139
131140 var offset = _nextFreeOffset ;
132- _nextFreeOffset += size ; // BUMP!
141+ _nextFreeOffset += size ;
133142
134143 EnsureEntryCapacity ( EntryCount ) ;
135144 var id = MemoryLaneUtils . GetNextId ( _freeIds , ref _nextHandleId ) ;
136145
137146#if DEBUG
138147 if ( ! string . IsNullOrEmpty ( debugName ) ) _debugNames [ id ] = debugName ;
139148#endif
140- _versions [ id % _versions . Length ] ++ ;
141- var currentVersion = _versions [ id % _versions . Length ] ;
149+ // Dynamically scale version buffer if recycled handle IDs push past high-water thresholds
150+ if ( id >= _versionsCapacity )
151+ {
152+ GrowVersions ( id + 1 ) ;
153+ }
154+
155+ uint currentVersion = ++ _versions [ id ] ;
142156
143157 _entries [ EntryCount ] = new AllocationEntry
144158 {
145159 Offset = offset ,
146160 Size = size ,
147161 HandleId = id ,
162+ Version = currentVersion ,
148163 Priority = priority ,
149164 Hints = hints ,
150165 RedirectToId = 0 ,
@@ -167,10 +182,17 @@ public nint Resolve(MemoryHandle handle)
167182
168183 ref readonly var entry = ref _entries [ index ] ;
169184
170- if ( ! entry . IsStub || entry . RedirectToId == 0 ) return Buffer + entry . Offset ;
185+ if ( entry . Version != handle . Version )
186+ throw new AccessViolationException ( $ "Zombie Handle: LinearLane ID { handle . Id } is stale.") ;
171187
172- var slowHandle = new MemoryHandle ( entry . RedirectToId , entry . RedirectVersion , _slowLane ) ;
173- return _slowLane . Resolve ( slowHandle ) ;
188+ // 2. REDIRECTION (Stub Logic)
189+ if ( entry . IsStub && entry . RedirectToId != 0 )
190+ {
191+ var slowHandle = new MemoryHandle ( entry . RedirectToId , entry . RedirectVersion , _slowLane ) ;
192+ return _slowLane . Resolve ( slowHandle ) ;
193+ }
194+
195+ return Buffer + entry . Offset ;
174196 }
175197
176198 /// <inheritdoc />
@@ -261,21 +283,18 @@ public void ReplaceWithStub(MemoryHandle fastHandle, MemoryHandle slowHandle)
261283 var entry = _entries [ index ] ;
262284 entry . IsStub = true ;
263285 entry . RedirectToId = slowHandle . Id ;
286+
287+ entry . RedirectVersion = slowHandle . Version ;
288+
264289 entry . Offset = 0 ;
265290 entry . Size = 0 ;
266291
267292 _entries [ index ] = entry ;
268- Redirects [ fastHandle . Id ] = slowHandle ;
269293 }
270294
271295 /// <summary>
272296 /// Should move to slow lane.
273297 /// </summary>
274- /// <param name="entry">The entry.</param>
275- /// <param name="currentFrame">The current frame.</param>
276- /// <param name="maxAgeFrames">The maximum age frames.</param>
277- /// <param name="largeThreshold">The large threshold.</param>
278- /// <returns></returns>
279298 private bool ShouldMoveToSlowLane ( in AllocationEntry entry , int currentFrame , int maxAgeFrames ,
280299 int largeThreshold )
281300 {
@@ -289,7 +308,6 @@ private bool ShouldMoveToSlowLane(in AllocationEntry entry, int currentFrame, in
289308 /// <summary>
290309 /// Ensures the entry capacity.
291310 /// </summary>
292- /// <param name="requiredSlotIndex">Index of the required slot.</param>
293311 private void EnsureEntryCapacity ( int requiredSlotIndex )
294312 {
295313 var oldSize = _entries ! . Length ;
@@ -321,23 +339,10 @@ public int EstimateFragmentation()
321339 return ( int ) ( ( double ) ( allocatedBytes - usedBytes ) / allocatedBytes * 100 ) ;
322340 }
323341
324- /// <summary>
325- /// Usages the percentage.
326- /// </summary>
327- /// <returns>
328- /// Used memory Percentage
329- /// </returns>
342+ /// <inheritdoc />
330343 public double UsagePercentage ( ) => ( double ) ( Capacity - FreeSpace ( ) ) / Capacity ;
331344
332345 /// <inheritdoc />
333- /// <summary>
334- /// Retrieves the full allocation entry metadata for a given handle.
335- /// Passthroughs to MemoryLaneUtils
336- /// </summary>
337- /// <param name="handle">The handle identifying the allocation.</param>
338- /// <returns>
339- /// The allocation entry associated with the handle.
340- /// </returns>
341346 public AllocationEntry GetEntry ( MemoryHandle handle ) =>
342347 MemoryLaneUtils . GetEntry ( handle , _handleIndex , _entries ! , nameof ( LinearLane ) ) ;
343348
@@ -362,29 +367,38 @@ public IEnumerable<MemoryHandle> GetHandles()
362367 {
363368 if ( _entries == null ) yield break ;
364369
365- // We must iterate the keys and look up the version in the metadata
366370 foreach ( var id in _handleIndex . Keys )
367371 {
368372 if ( _handleIndex . TryGetValue ( id , out var index ) )
369373 {
370- // Grab the generation from the actual entry
371374 var version = _entries [ index ] . Version ;
372-
373- // Return the "Smart Handle" with its proof-of-life
374375 yield return new MemoryHandle ( id , version , this ) ;
375376 }
376377 }
377378 }
378379
379- /// <inheritdoc />
380- public event Action < string > ? OnCompaction ;
381-
382- /// <inheritdoc />
383- public event Action < string , int , int > ? OnAllocationExtension ;
384-
385380 /// <summary>
386- /// Logs the dump .
381+ /// Resizes the unmanaged versions buffer .
387382 /// </summary>
383+ [ MethodImpl ( MethodImplOptions . NoInlining ) ]
384+ private unsafe void GrowVersions ( int minCapacity )
385+ {
386+ var newCapacity = _versionsCapacity * 2 ;
387+ if ( newCapacity < minCapacity ) newCapacity = minCapacity ;
388+
389+ var newVersions = ( uint * ) NativeMemory . AllocZeroed ( ( nuint ) newCapacity , sizeof ( uint ) ) ;
390+
391+ if ( _versions != null )
392+ {
393+ Unsafe . CopyBlock ( newVersions , _versions , ( uint ) ( _versionsCapacity * sizeof ( uint ) ) ) ;
394+ NativeMemory . Free ( _versions ) ;
395+ }
396+
397+ _versions = newVersions ;
398+ _versionsCapacity = newCapacity ;
399+ }
400+
401+ /// <inheritdoc />
388402 public void LogDump ( ) => Trace . WriteLine ( DebugDump ( ) ) ;
389403 }
390404}
0 commit comments