66public static partial class Base32
77{
88 private const char UnitSeparator = ( char ) 0x1F ;
9- private const char MaxByte = ( char ) 0xFF ;
109
1110 private static readonly char [ ] Base32Table =
1211 [
@@ -40,23 +39,37 @@ public static string Encode(byte[] inArray)
4039
4140 private static byte [ ] Decode ( ReadOnlySpan < char > inputSpan , int [ ] decodeMap )
4241 {
43- // remove padding '=' characters from the end of the input
44- var trimmedInput = inputSpan . TrimEnd ( '=' ) ;
42+ // manually find the end position without padding '=' characters
43+ var inputLength = inputSpan . Length ;
44+ while ( inputLength > 0 && inputSpan [ inputLength - 1 ] == '=' )
45+ {
46+ inputLength -- ;
47+ }
4548
4649 // calculate the expected output byte array length
4750 // each 8 characters of base32 results in 5 bytes
48- var outputLength = ( trimmedInput . Length * 5 ) / 8 ;
51+ var outputLength = ( inputLength * 5 ) / 8 ;
4952
50- // stack allocation for small arrays
51- Span < byte > outputSpan = stackalloc byte [ outputLength ] ;
53+ // use stack allocation for small arrays (<=256 bytes), otherwise heap
54+ Span < byte > outputSpan = outputLength <= 256
55+ ? stackalloc byte [ outputLength ]
56+ : new byte [ outputLength ] ;
5257
5358 var bitBuffer = 0 ;
5459 var bitCount = 0 ;
5560 var outputIndex = 0 ;
5661
5762 // process each character in the Base32 input
58- foreach ( var c in trimmedInput )
63+ for ( var i = 0 ; i < inputLength ; i ++ )
5964 {
65+ var c = inputSpan [ i ] ;
66+
67+ // bounds check before array access
68+ if ( c > 255 )
69+ {
70+ throw new ArgumentException ( "Invalid character in Base32 string." ) ;
71+ }
72+
6073 // get the index value of the character from the Base32 alphabet
6174 var index = decodeMap [ c ] ;
6275 if ( index == - 1 )
@@ -69,13 +82,11 @@ private static byte[] Decode(ReadOnlySpan<char> inputSpan, int[] decodeMap)
6982 bitCount += 5 ;
7083
7184 // extract 8 bits (1 byte) whenever we have enough bits (>= 8)
72- if ( bitCount < 8 )
85+ if ( bitCount >= 8 )
7386 {
74- continue ;
87+ outputSpan [ outputIndex ++ ] = ( byte ) ( bitBuffer >> ( bitCount - 8 ) ) ;
88+ bitCount -= 8 ;
7589 }
76-
77- outputSpan [ outputIndex ++ ] = ( byte ) ( ( bitBuffer >> ( bitCount - 8 ) ) & MaxByte ) ;
78- bitCount -= 8 ;
7990 }
8091
8192 // convert the span to a byte array and return it
@@ -88,21 +99,23 @@ private static string Encode(ReadOnlySpan<byte> inArray, char[] base32Table)
8899 var outputLength = ( ( inArray . Length * 8 ) + 4 ) / 5 ;
89100
90101 // output length should be padded to a multiple of 8
91- var paddingLength = ( outputLength + 7 ) / 8 * 8 ;
102+ var paddingLength = ( outputLength + 7 ) & ~ 7 ; // faster than / 8 * 8
92103
93- // use stackalloc for small memory allocation
94- Span < char > outputSpan = stackalloc char [ paddingLength ] ;
104+ // use stack allocation for small arrays (<=256 bytes), otherwise heap
105+ Span < char > outputSpan = paddingLength <= 256
106+ ? stackalloc char [ paddingLength ]
107+ : new char [ paddingLength ] ;
95108
96109 // declare bit buffer and bit count
97110 var bitBuffer = 0 ;
98111 var bitCount = 0 ;
99112 var outputIndex = 0 ;
100113
101114 // process each byte in inArray
102- foreach ( var b in inArray )
115+ for ( var i = 0 ; i < inArray . Length ; i ++ )
103116 {
104117 // shift the buffer left by 8 bits and add the byte value to the buffer
105- bitBuffer = ( bitBuffer << 8 ) | b ;
118+ bitBuffer = ( bitBuffer << 8 ) | inArray [ i ] ;
106119 bitCount += 8 ;
107120
108121 // while we have 5 or more bits in the buffer, process a base32 character
@@ -123,13 +136,13 @@ private static string Encode(ReadOnlySpan<byte> inArray, char[] base32Table)
123136 }
124137
125138 // add padding '=' to make the result length a multiple of 8
126- while ( outputIndex % 8 != 0 )
139+ while ( outputIndex < paddingLength )
127140 {
128141 outputSpan [ outputIndex ++ ] = '=' ;
129142 }
130143
131144 // return the result as a string
132- return new string ( outputSpan [ ..outputIndex ] ) ;
145+ return new string ( outputSpan [ ..paddingLength ] ) ;
133146 }
134147
135148 private static int [ ] CreateDecodeMap ( char [ ] table )
0 commit comments