@@ -31,19 +31,23 @@ public void Shuffle(ReadOnlySpan<byte> source, Span<byte> destination)
3131
3232 SimdUtils . Shuffle . InverseMMShuffle ( this . Control , out uint p3 , out uint p2 , out uint p1 , out uint p0 ) ;
3333
34- Span < byte > temp = stackalloc byte [ 4 ] ;
35- ref byte t = ref MemoryMarshal . GetReference ( temp ) ;
36- ref uint tu = ref Unsafe . As < byte , uint > ( ref t ) ;
37-
3834 for ( nuint i = 0 , j = 0 ; i < ( uint ) source . Length ; i += 3 , j += 4 )
3935 {
40- ref byte s = ref Unsafe . Add ( ref sBase , i ) ;
41- tu = Unsafe . As < byte , uint > ( ref s ) | 0xFF000000 ;
42-
43- Unsafe . Add ( ref dBase , j + 0 ) = Unsafe. Add ( ref t , p0 ) ;
44- Unsafe . Add ( ref dBase , j + 1 ) = Unsafe. Add ( ref t , p1 ) ;
45- Unsafe . Add ( ref dBase , j + 2 ) = Unsafe. Add ( ref t , p2 ) ;
46- Unsafe . Add ( ref dBase , j + 3 ) = Unsafe. Add ( ref t , p3 ) ;
36+ // Expanding 3-byte pixels to 4 bytes can overwrite the next source
37+ // triplet when spans overlap. Assemble the padded pixel first, then
38+ // shuffle from the staged uint.
39+ uint packed =
40+ Unsafe . Add ( ref sBase , i + 0u ) |
41+ ( ( uint ) Unsafe . Add ( ref sBase , i + 1u ) << 8 ) |
42+ ( ( uint ) Unsafe . Add ( ref sBase , i + 2u ) << 16 ) |
43+ 0xFF000000 ;
44+
45+ ref byte pBase = ref Unsafe . As < uint , byte > ( ref packed ) ;
46+
47+ Unsafe . Add ( ref dBase , j + 0u ) = Unsafe. Add ( ref pBase , p0 ) ;
48+ Unsafe . Add ( ref dBase , j + 1u ) = Unsafe. Add ( ref pBase , p1 ) ;
49+ Unsafe . Add ( ref dBase , j + 2u ) = Unsafe. Add ( ref pBase , p2 ) ;
50+ Unsafe . Add ( ref dBase , j + 3u ) = Unsafe. Add ( ref pBase , p3 ) ;
4751 }
4852 }
4953}
@@ -65,18 +69,28 @@ public void Shuffle(ReadOnlySpan<byte> source, Span<byte> destination)
6569
6670 while ( Unsafe . IsAddressLessThan ( ref sBase , ref sLoopEnd ) )
6771 {
68- Unsafe . As < byte , uint > ( ref dBase ) = Unsafe. As < byte , uint > ( ref sBase ) | 0xFF000000 ;
72+ // The fast scalar path reads one extra byte past the source triplet.
73+ // Keep that widened read in a local before writing the expanded pixel
74+ // so overlapping destinations cannot change what was read.
75+ uint packed = Unsafe . As < byte , uint > ( ref sBase ) | 0xFF000000 ;
76+
77+ Unsafe . As < byte , uint > ( ref dBase ) = packed;
6978
7079 sBase = ref Unsafe . Add ( ref sBase , 3 ) ;
7180 dBase = ref Unsafe . Add ( ref dBase , 4 ) ;
7281 }
7382
7483 while ( Unsafe . IsAddressLessThan ( ref sBase , ref sEnd ) )
7584 {
76- Unsafe . Add ( ref dBase , 0 ) = Unsafe. Add ( ref sBase , 0 ) ;
77- Unsafe . Add ( ref dBase , 1 ) = Unsafe. Add ( ref sBase , 1 ) ;
78- Unsafe . Add ( ref dBase , 2 ) = Unsafe. Add ( ref sBase , 2 ) ;
79- Unsafe . Add ( ref dBase , 3 ) = byte . MaxValue ;
85+ // The final triplet cannot use the widened read above, so assemble
86+ // the same padded uint byte-by-byte before the overlapping store.
87+ uint packed =
88+ Unsafe . Add ( ref sBase , 0u ) |
89+ ( ( uint ) Unsafe . Add ( ref sBase , 1u ) << 8 ) |
90+ ( ( uint ) Unsafe . Add ( ref sBase , 2u ) << 16 ) |
91+ 0xFF000000 ;
92+
93+ Unsafe . As < byte , uint > ( ref dBase ) = packed;
8094
8195 sBase = ref Unsafe . Add ( ref sBase , 3 ) ;
8296 dBase = ref Unsafe . Add ( ref dBase , 4 ) ;
0 commit comments