@@ -510,13 +510,56 @@ public interface IStack<T, TStack>
510510 Span < T > AsSpan ( ) ;
511511}
512512
513- #pragma warning restore CS0436
513+ internal static class StackTools < T , TStack >
514+ where TStack : struct , IStack < T , TStack >
515+ {
516+ #if NETSTANDARD2_0_OR_GREATER || NET472
517+ internal static readonly ConstructorInfo SpanConstructor =
518+ typeof ( Span < T > ) . GetConstructor ( new [ ] { typeof ( void * ) , typeof ( int ) } ) ;
519+
520+ internal delegate Span < T > AsSpanDelegate ( ref TStack stack , int capacity ) ;
521+
522+ internal static AsSpanDelegate CompileAsSpanDelegate ( )
523+ {
524+ var dynamicMethod = new DynamicMethod (
525+ "" ,
526+ typeof ( Span < T > ) ,
527+ new [ ] { typeof ( TStack ) . MakeByRefType ( ) , typeof ( int ) } , // todo: @perf pool this thing
528+ typeof ( TStack ) ,
529+ true
530+ ) ;
531+
532+ // Set capacity to the estimated size to avoid realloc, 1 + 1 + 1 + 5 + 1 = 9 bytes + a small buffer
533+ var il = dynamicMethod . GetILGenerator ( 16 ) ;
534+
535+ // IL to replicate: return new Span<T>(Unsafe.AsPointer(ref this), StackCapacity);
536+ il . Emit ( OpCodes . Ldarg_0 ) ; // Load 'ref this'
537+ il . Emit ( OpCodes . Conv_U ) ; // Convert managed reference to native unsigned int (void*)
538+ il . Emit ( OpCodes . Ldarg_1 ) ; // Load length (StackCapacity) argument
539+ il . Emit ( OpCodes . Newobj , SpanConstructor ) ;
540+ il . Emit ( OpCodes . Ret ) ;
514541
515- internal struct Stack2 < T >
542+ return ( AsSpanDelegate ) dynamicMethod . CreateDelegate ( typeof ( AsSpanDelegate ) ) ;
543+ }
544+
545+ // todo: @perf do we even need a lazy here?
546+ internal static readonly Lazy < AsSpanDelegate > LazyCompiledAsSpanDelegate = new ( CompileAsSpanDelegate ) ;
547+ #endif
548+ }
549+
550+ /// <summary>Implementation of `IStack` for 2 items on stack</summary>
551+ [ StructLayout ( LayoutKind . Sequential , Pack = 1 ) ]
552+ public struct Stack2 < T > : IStack < T , Stack2 < T > >
516553{
554+ /// <summary>Count of items on stack</summary>
517555 public const int StackCapacity = 2 ;
556+
518557 internal T _it0 , _it1 ;
519558
559+ /// <inheritdoc/>
560+ public int Capacity => StackCapacity ;
561+
562+ /// <inheritdoc/>
520563 [ UnscopedRef ]
521564 [ MethodImpl ( ( MethodImplOptions ) 256 ) ]
522565 public ref T GetSurePresentRef ( int index )
@@ -528,6 +571,45 @@ public ref T GetSurePresentRef(int index)
528571 default : return ref _it1 ;
529572 }
530573 }
574+
575+ /// <inheritdoc/>
576+ public T this [ int index ]
577+ {
578+ [ MethodImpl ( ( MethodImplOptions ) 256 ) ]
579+ get
580+ {
581+ Debug . Assert ( index < StackCapacity ) ;
582+ return index switch
583+ {
584+ 0 => _it0 ,
585+ _ => _it1 ,
586+ } ;
587+ }
588+ [ MethodImpl ( ( MethodImplOptions ) 256 ) ]
589+ set => Set ( index , in value ) ;
590+ }
591+
592+ /// <inheritdoc/>
593+ [ MethodImpl ( ( MethodImplOptions ) 256 ) ]
594+ public void Set ( int index , in T value )
595+ {
596+ Debug . Assert ( index < StackCapacity ) ;
597+ switch ( index )
598+ {
599+ case 0 : _it0 = value ; break ;
600+ default : _it1 = value ; break ;
601+ }
602+ }
603+
604+ /// <inheritdoc/>
605+ [ UnscopedRef ]
606+ [ MethodImpl ( ( MethodImplOptions ) 256 ) ]
607+ public Span < T > AsSpan ( ) =>
608+ #if NETSTANDARD2_0_OR_GREATER || NET472
609+ StackTools < T , Stack2 < T > > . LazyCompiledAsSpanDelegate . Value ( ref this , StackCapacity ) ;
610+ #else
611+ MemoryMarshal . CreateSpan ( ref Unsafe . As < Stack2 < T > , T > ( ref this ) , StackCapacity ) ;
612+ #endif
531613}
532614
533615/// <summary>Implementation of `IStack` for 4 items on stack</summary>
@@ -557,48 +639,6 @@ public ref T GetSurePresentRef(int index)
557639 }
558640 }
559641
560- #if NETSTANDARD2_0_OR_GREATER || NET472
561- private delegate Span < T > AsSpanDelegate ( ref Stack4 < T > stack , int capacity ) ;
562-
563- private static AsSpanDelegate CompileAsSpanDelegate ( )
564- {
565- var dynamicMethod = new DynamicMethod (
566- "__AsSpan_Stack4_" ,
567- typeof ( Span < T > ) ,
568- new [ ] { typeof ( Stack4 < T > ) . MakeByRefType ( ) , typeof ( int ) } ,
569- typeof ( Stack4 < T > ) . Module ,
570- true
571- ) ;
572-
573- var spanConstructor = typeof ( Span < T > ) . GetConstructor ( new [ ] { typeof ( void * ) , typeof ( int ) } ) ;
574- Debug . Assert ( spanConstructor != null ) ;
575-
576- // Set capacity to estimated size = 1 + 1 + 1 + 5 + 1 = 9 bytes + a small buffer
577- var il = dynamicMethod . GetILGenerator ( 16 ) ;
578-
579- // IL to replicate: return new Span<T>(Unsafe.AsPointer(ref this), StackCapacity);
580- il . Emit ( OpCodes . Ldarg_0 ) ; // Load 'ref this'
581- il . Emit ( OpCodes . Conv_U ) ; // Convert managed reference to native unsigned int (void*)
582- il . Emit ( OpCodes . Ldarg_1 ) ; // Load length (StackCapacity) argument
583- il . Emit ( OpCodes . Newobj , spanConstructor ) ;
584- il . Emit ( OpCodes . Ret ) ;
585-
586- return ( AsSpanDelegate ) dynamicMethod . CreateDelegate ( typeof ( AsSpanDelegate ) ) ;
587- }
588-
589- private static readonly Lazy < AsSpanDelegate > _lazyCompiledAsSpanDelegate = new ( CompileAsSpanDelegate ) ;
590-
591- /// <inheritdoc/>
592- [ UnscopedRef ]
593- [ MethodImpl ( ( MethodImplOptions ) 256 ) ]
594- public Span < T > AsSpan ( ) => _lazyCompiledAsSpanDelegate . Value ( ref this , StackCapacity ) ;
595- #else
596- /// <inheritdoc/>
597- [ UnscopedRef ]
598- [ MethodImpl ( ( MethodImplOptions ) 256 ) ]
599- public Span < T > AsSpan ( ) => MemoryMarshal . CreateSpan ( ref Unsafe . As < Stack4 < T > , T > ( ref this ) , StackCapacity ) ;
600- #endif
601-
602642 /// <inheritdoc/>
603643 public T this [ int index ]
604644 {
@@ -611,8 +651,7 @@ public T this[int index]
611651 0 => _it0 ,
612652 1 => _it1 ,
613653 2 => _it2 ,
614- 3 => _it3 ,
615- _ => default ,
654+ _ => _it3 ,
616655 } ;
617656 }
618657 [ MethodImpl ( ( MethodImplOptions ) 256 ) ]
@@ -629,10 +668,19 @@ public void Set(int index, in T value)
629668 case 0 : _it0 = value ; break ;
630669 case 1 : _it1 = value ; break ;
631670 case 2 : _it2 = value ; break ;
632- case 3 : _it3 = value ; break ;
633- default : break ;
671+ default : _it3 = value ; break ;
634672 }
635673 }
674+
675+ /// <inheritdoc/>
676+ [ UnscopedRef ]
677+ [ MethodImpl ( ( MethodImplOptions ) 256 ) ]
678+ public Span < T > AsSpan ( ) =>
679+ #if NETSTANDARD2_0_OR_GREATER || NET472
680+ StackTools < T , Stack4 < T > > . LazyCompiledAsSpanDelegate . Value ( ref this , StackCapacity ) ;
681+ #else
682+ MemoryMarshal . CreateSpan ( ref Unsafe . As < Stack4 < T > , T > ( ref this ) , StackCapacity ) ;
683+ #endif
636684}
637685
638686// todo: @wip
0 commit comments