1 //-----------------------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //-----------------------------------------------------------------------------
4 namespace System.Text
5 {
6     using System.Globalization;
7     using System.Runtime;
8     using System.Runtime.Serialization; //For SR
9     using System.Security;
10 
11     class Base64Encoding : Encoding
12     {
13         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.StyleCop.CSharp.SpacingRules", "SA1025:CodeMustNotContainMultipleWhitespaceInARow", Justification = "This alignment is optimal.")]
14         static byte[] char2val = new byte[128]
15         {
16             /*    0-15 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
17             /*   16-31 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
18             /*   32-47 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,   62, 0xFF, 0xFF, 0xFF,   63,
19             /*   48-63 */   52,   53,   54,   55,   56,   57,   58,   59,   60,   61, 0xFF, 0xFF, 0xFF,   64, 0xFF, 0xFF,
20             /*   64-79 */ 0xFF,    0,    1,    2,    3,    4,    5,    6,    7,    8,    9,   10,   11,   12,   13,   14,
21             /*   80-95 */   15,   16,   17,   18,   19,   20,   21,   22,   23,   24,   25, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
22             /*  96-111 */ 0xFF,   26,   27,   28,   29,   30,   31,   32,   33,   34,   35,   36,   37,   38,   39,   40,
23             /* 112-127 */   41,   42,   43,   44,   45,   46,   47,   48,   49,   50,   51, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
24         };
25 
26         static string val2char = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
27         static byte[] val2byte = new byte[]
28         {
29             (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G', (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N', (byte)'O', (byte)'P',
30             (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U', (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z', (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f',
31             (byte)'g', (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n', (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u', (byte)'v',
32             (byte)'w', (byte)'x', (byte)'y', (byte)'z', (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'+', (byte)'/'
33         };
34 
GetMaxByteCount(int charCount)35         public override int GetMaxByteCount(int charCount)
36         {
37             if (charCount < 0)
38                 throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("charCount", SR.GetString(SR.ValueMustBeNonNegative)));
39             if ((charCount % 4) != 0)
40                 throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.GetString(SR.XmlInvalidBase64Length, charCount.ToString(NumberFormatInfo.CurrentInfo))));
41             return charCount / 4 * 3;
42         }
43 
IsValidLeadBytes(int v1, int v2, int v3, int v4)44         bool IsValidLeadBytes(int v1, int v2, int v3, int v4)
45         {
46             // First two chars of a four char base64 sequence can't be ==, and must be valid
47             return ((v1 | v2) < 64) && ((v3 | v4) != 0xFF);
48         }
49 
IsValidTailBytes(int v3, int v4)50         bool IsValidTailBytes(int v3, int v4)
51         {
52             // If the third char is = then the fourth char must be =
53             return !(v3 == 64 && v4 != 64);
54         }
55 
56         [Fx.Tag.SecurityNote(Critical = "Contains unsafe code.",
57             Safe = "Unsafe code is effectively encapsulated, all inputs are validated.")]
58         [SecuritySafeCritical]
GetByteCount(char[] chars, int index, int count)59         unsafe public override int GetByteCount(char[] chars, int index, int count)
60         {
61             if (chars == null)
62                 throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("chars"));
63             if (index < 0)
64                 throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("index", SR.GetString(SR.ValueMustBeNonNegative)));
65             if (index > chars.Length)
66                 throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("index", SR.GetString(SR.OffsetExceedsBufferSize, chars.Length)));
67             if (count < 0)
68                 throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("count", SR.GetString(SR.ValueMustBeNonNegative)));
69             if (count > chars.Length - index)
70                 throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("count", SR.GetString(SR.SizeExceedsRemainingBufferSpace, chars.Length - index)));
71 
72             if (count == 0)
73                 return 0;
74             if ((count % 4) != 0)
75                 throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.GetString(SR.XmlInvalidBase64Length, count.ToString(NumberFormatInfo.CurrentInfo))));
76             fixed (byte* _char2val = char2val)
77             {
78                 fixed (char* _chars = &chars[index])
79                 {
80                     int totalCount = 0;
81                     char* pch = _chars;
82                     char* pchMax = _chars + count;
83                     while (pch < pchMax)
84                     {
85                         Fx.Assert(pch + 4 <= pchMax, "");
86                         char pch0 = pch[0];
87                         char pch1 = pch[1];
88                         char pch2 = pch[2];
89                         char pch3 = pch[3];
90 
91                         if ((pch0 | pch1 | pch2 | pch3) >= 128)
92                             throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.GetString(SR.XmlInvalidBase64Sequence, new string(pch, 0, 4), index + (int)(pch - _chars))));
93 
94                         // xx765432 xx107654 xx321076 xx543210
95                         // 76543210 76543210 76543210
96                         int v1 = _char2val[pch0];
97                         int v2 = _char2val[pch1];
98                         int v3 = _char2val[pch2];
99                         int v4 = _char2val[pch3];
100 
101                         if (!IsValidLeadBytes(v1, v2, v3, v4) || !IsValidTailBytes(v3, v4))
102                             throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.GetString(SR.XmlInvalidBase64Sequence, new string(pch, 0, 4), index + (int)(pch - _chars))));
103 
104                         int byteCount = (v4 != 64 ? 3 : (v3 != 64 ? 2 : 1));
105                         totalCount += byteCount;
106                         pch += 4;
107                     }
108                     return totalCount;
109                 }
110             }
111         }
112 
113         [Fx.Tag.SecurityNote(Critical = "Contains unsafe code.",
114             Safe = "Unsafe code is effectively encapsulated, all inputs are validated.")]
115         [SecuritySafeCritical]
GetBytes(char[] chars, int charIndex, int charCount, byte[] bytes, int byteIndex)116         unsafe public override int GetBytes(char[] chars, int charIndex, int charCount, byte[] bytes, int byteIndex)
117         {
118             if (chars == null)
119                 throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("chars"));
120 
121             if (charIndex < 0)
122                 throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("charIndex", SR.GetString(SR.ValueMustBeNonNegative)));
123             if (charIndex > chars.Length)
124                 throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("charIndex", SR.GetString(SR.OffsetExceedsBufferSize, chars.Length)));
125 
126             if (charCount < 0)
127                 throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("charCount", SR.GetString(SR.ValueMustBeNonNegative)));
128             if (charCount > chars.Length - charIndex)
129                 throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("charCount", SR.GetString(SR.SizeExceedsRemainingBufferSpace, chars.Length - charIndex)));
130 
131             if (bytes == null)
132                 throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("bytes"));
133             if (byteIndex < 0)
134                 throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("byteIndex", SR.GetString(SR.ValueMustBeNonNegative)));
135             if (byteIndex > bytes.Length)
136                 throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("byteIndex", SR.GetString(SR.OffsetExceedsBufferSize, bytes.Length)));
137 
138             if (charCount == 0)
139                 return 0;
140             if ((charCount % 4) != 0)
141                 throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.GetString(SR.XmlInvalidBase64Length, charCount.ToString(NumberFormatInfo.CurrentInfo))));
142             fixed (byte* _char2val = char2val)
143             {
144                 fixed (char* _chars = &chars[charIndex])
145                 {
146                     fixed (byte* _bytes = &bytes[byteIndex])
147                     {
148                         char* pch = _chars;
149                         char* pchMax = _chars + charCount;
150                         byte* pb = _bytes;
151                         byte* pbMax = _bytes + bytes.Length - byteIndex;
152                         while (pch < pchMax)
153                         {
154                             Fx.Assert(pch + 4 <= pchMax, "");
155                             char pch0 = pch[0];
156                             char pch1 = pch[1];
157                             char pch2 = pch[2];
158                             char pch3 = pch[3];
159 
160                             if ((pch0 | pch1 | pch2 | pch3) >= 128)
161                                 throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.GetString(SR.XmlInvalidBase64Sequence, new string(pch, 0, 4), charIndex + (int)(pch - _chars))));
162                             // xx765432 xx107654 xx321076 xx543210
163                             // 76543210 76543210 76543210
164 
165                             int v1 = _char2val[pch0];
166                             int v2 = _char2val[pch1];
167                             int v3 = _char2val[pch2];
168                             int v4 = _char2val[pch3];
169 
170                             if (!IsValidLeadBytes(v1, v2, v3, v4) || !IsValidTailBytes(v3, v4))
171                                 throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.GetString(SR.XmlInvalidBase64Sequence, new string(pch, 0, 4), charIndex + (int)(pch - _chars))));
172 
173                             int byteCount = (v4 != 64 ? 3 : (v3 != 64 ? 2 : 1));
174                             if (pb + byteCount > pbMax)
175                                 throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.GetString(SR.XmlArrayTooSmall), "bytes"));
176 
177                             pb[0] = (byte)((v1 << 2) | ((v2 >> 4) & 0x03));
178                             if (byteCount > 1)
179                             {
180                                 pb[1] = (byte)((v2 << 4) | ((v3 >> 2) & 0x0F));
181                                 if (byteCount > 2)
182                                 {
183                                     pb[2] = (byte)((v3 << 6) | ((v4 >> 0) & 0x3F));
184                                 }
185                             }
186                             pb += byteCount;
187                             pch += 4;
188                         }
189                         return (int)(pb - _bytes);
190                     }
191                 }
192             }
193         }
194 
195         [Fx.Tag.SecurityNote(Critical = "Contains unsafe code.",
196             Safe = "Unsafe code is effectively encapsulated, all inputs are validated.")]
197         [SecuritySafeCritical]
GetBytes(byte[] chars, int charIndex, int charCount, byte[] bytes, int byteIndex)198         unsafe public virtual int GetBytes(byte[] chars, int charIndex, int charCount, byte[] bytes, int byteIndex)
199         {
200             if (chars == null)
201                 throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("chars"));
202             if (charIndex < 0)
203                 throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("charIndex", SR.GetString(SR.ValueMustBeNonNegative)));
204             if (charIndex > chars.Length)
205                 throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("charIndex", SR.GetString(SR.OffsetExceedsBufferSize, chars.Length)));
206 
207             if (charCount < 0)
208                 throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("charCount", SR.GetString(SR.ValueMustBeNonNegative)));
209             if (charCount > chars.Length - charIndex)
210                 throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("charCount", SR.GetString(SR.SizeExceedsRemainingBufferSpace, chars.Length - charIndex)));
211 
212             if (bytes == null)
213                 throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("bytes"));
214             if (byteIndex < 0)
215                 throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("byteIndex", SR.GetString(SR.ValueMustBeNonNegative)));
216             if (byteIndex > bytes.Length)
217                 throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("byteIndex", SR.GetString(SR.OffsetExceedsBufferSize, bytes.Length)));
218 
219             if (charCount == 0)
220                 return 0;
221             if ((charCount % 4) != 0)
222                 throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.GetString(SR.XmlInvalidBase64Length, charCount.ToString(NumberFormatInfo.CurrentInfo))));
223             fixed (byte* _char2val = char2val)
224             {
225                 fixed (byte* _chars = &chars[charIndex])
226                 {
227                     fixed (byte* _bytes = &bytes[byteIndex])
228                     {
229                         byte* pch = _chars;
230                         byte* pchMax = _chars + charCount;
231                         byte* pb = _bytes;
232                         byte* pbMax = _bytes + bytes.Length - byteIndex;
233                         while (pch < pchMax)
234                         {
235                             Fx.Assert(pch + 4 <= pchMax, "");
236                             byte pch0 = pch[0];
237                             byte pch1 = pch[1];
238                             byte pch2 = pch[2];
239                             byte pch3 = pch[3];
240                             if ((pch0 | pch1 | pch2 | pch3) >= 128)
241                                 throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.GetString(SR.XmlInvalidBase64Sequence, new string((sbyte*)pch, 0, 4), charIndex + (int)(pch - _chars))));
242                             // xx765432 xx107654 xx321076 xx543210
243                             // 76543210 76543210 76543210
244 
245                             int v1 = _char2val[pch0];
246                             int v2 = _char2val[pch1];
247                             int v3 = _char2val[pch2];
248                             int v4 = _char2val[pch3];
249 
250                             if (!IsValidLeadBytes(v1, v2, v3, v4) || !IsValidTailBytes(v3, v4))
251                                 throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.GetString(SR.XmlInvalidBase64Sequence, new string((sbyte*)pch, 0, 4), charIndex + (int)(pch - _chars))));
252 
253                             int byteCount = (v4 != 64 ? 3 : (v3 != 64 ? 2 : 1));
254                             if (pb + byteCount > pbMax)
255                                 throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.GetString(SR.XmlArrayTooSmall), "bytes"));
256 
257                             pb[0] = (byte)((v1 << 2) | ((v2 >> 4) & 0x03));
258                             if (byteCount > 1)
259                             {
260                                 pb[1] = (byte)((v2 << 4) | ((v3 >> 2) & 0x0F));
261                                 if (byteCount > 2)
262                                 {
263                                     pb[2] = (byte)((v3 << 6) | ((v4 >> 0) & 0x3F));
264                                 }
265                             }
266                             pb += byteCount;
267                             pch += 4;
268                         }
269                         return (int)(pb - _bytes);
270                     }
271                 }
272             }
273         }
274 #if NO
GetEncoder()275         public override Encoder GetEncoder()
276         {
277             return new BufferedEncoder(this, 4);
278         }
279 
GetDecoder()280         public override Decoder GetDecoder()
281         {
282             return new BufferedDecoder(this, 3);
283         }
284 #endif
GetMaxCharCount(int byteCount)285         public override int GetMaxCharCount(int byteCount)
286         {
287             if (byteCount < 0 || byteCount > int.MaxValue / 4 * 3 - 2)
288                 throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("byteCount", SR.GetString(SR.ValueMustBeInRange, 0, int.MaxValue / 4 * 3 - 2)));
289             return ((byteCount + 2) / 3) * 4;
290         }
291 
GetCharCount(byte[] bytes, int index, int count)292         public override int GetCharCount(byte[] bytes, int index, int count)
293         {
294             return GetMaxCharCount(count);
295         }
296 
297         [Fx.Tag.SecurityNote(Critical = "Contains unsafe code.",
298             Safe = "Unsafe code is effectively encapsulated, all inputs are validated.")]
299         [SecuritySafeCritical]
GetChars(byte[] bytes, int byteIndex, int byteCount, char[] chars, int charIndex)300         unsafe public override int GetChars(byte[] bytes, int byteIndex, int byteCount, char[] chars, int charIndex)
301         {
302             if (bytes == null)
303                 throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("bytes"));
304             if (byteIndex < 0)
305                 throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("byteIndex", SR.GetString(SR.ValueMustBeNonNegative)));
306             if (byteIndex > bytes.Length)
307                 throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("byteIndex", SR.GetString(SR.OffsetExceedsBufferSize, bytes.Length)));
308             if (byteCount < 0)
309                 throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("byteCount", SR.GetString(SR.ValueMustBeNonNegative)));
310             if (byteCount > bytes.Length - byteIndex)
311                 throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("byteCount", SR.GetString(SR.SizeExceedsRemainingBufferSpace, bytes.Length - byteIndex)));
312 
313             int charCount = GetCharCount(bytes, byteIndex, byteCount);
314             if (chars == null)
315                 throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("chars"));
316             if (charIndex < 0)
317                 throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("charIndex", SR.GetString(SR.ValueMustBeNonNegative)));
318             if (charIndex > chars.Length)
319                 throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("charIndex", SR.GetString(SR.OffsetExceedsBufferSize, chars.Length)));
320             if (charCount < 0 || charCount > chars.Length - charIndex)
321                 throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.GetString(SR.XmlArrayTooSmall), "chars"));
322 
323             // We've computed exactly how many chars there are and verified that
324             // there's enough space in the char buffer, so we can proceed without
325             // checking the charCount.
326 
327             if (byteCount > 0)
328             {
329                 fixed (char* _val2char = val2char)
330                 {
331                     fixed (byte* _bytes = &bytes[byteIndex])
332                     {
333                         fixed (char* _chars = &chars[charIndex])
334                         {
335                             byte* pb = _bytes;
336                             byte* pbMax = pb + byteCount - 3;
337                             char* pch = _chars;
338 
339                             // Convert chunks of 3 bytes to 4 chars
340                             while (pb <= pbMax)
341                             {
342                                 // 76543210 76543210 76543210
343                                 // xx765432 xx107654 xx321076 xx543210
344 
345                                 // Inspect the code carefully before you change this
346                                 pch[0] = _val2char[(pb[0] >> 2)];
347                                 pch[1] = _val2char[((pb[0] & 0x03) << 4) | (pb[1] >> 4)];
348                                 pch[2] = _val2char[((pb[1] & 0x0F) << 2) | (pb[2] >> 6)];
349                                 pch[3] = _val2char[pb[2] & 0x3F];
350 
351                                 pb += 3;
352                                 pch += 4;
353                             }
354 
355                             // Handle 1 or 2 trailing bytes
356                             if (pb - pbMax == 2)
357                             {
358                                 // 1 trailing byte
359                                 // 76543210 xxxxxxxx xxxxxxxx
360                                 // xx765432 xx10xxxx xxxxxxxx xxxxxxxx
361                                 pch[0] = _val2char[(pb[0] >> 2)];
362                                 pch[1] = _val2char[((pb[0] & 0x03) << 4)];
363                                 pch[2] = '=';
364                                 pch[3] = '=';
365                             }
366                             else if (pb - pbMax == 1)
367                             {
368                                 // 2 trailing bytes
369                                 // 76543210 76543210 xxxxxxxx
370                                 // xx765432 xx107654 xx3210xx xxxxxxxx
371                                 pch[0] = _val2char[(pb[0] >> 2)];
372                                 pch[1] = _val2char[((pb[0] & 0x03) << 4) | (pb[1] >> 4)];
373                                 pch[2] = _val2char[((pb[1] & 0x0F) << 2)];
374                                 pch[3] = '=';
375                             }
376                             else
377                             {
378                                 // 0 trailing bytes
379                                 Fx.Assert(pb - pbMax == 3, "");
380                             }
381                         }
382                     }
383                 }
384             }
385 
386             return charCount;
387         }
388 
389         [Fx.Tag.SecurityNote(Critical = "Contains unsafe code.",
390             Safe = "Unsafe code is effectively encapsulated, all inputs are validated.")]
391         [SecuritySafeCritical]
GetChars(byte[] bytes, int byteIndex, int byteCount, byte[] chars, int charIndex)392         unsafe public int GetChars(byte[] bytes, int byteIndex, int byteCount, byte[] chars, int charIndex)
393         {
394             if (bytes == null)
395                 throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("bytes"));
396             if (byteIndex < 0)
397                 throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("byteIndex", SR.GetString(SR.ValueMustBeNonNegative)));
398             if (byteIndex > bytes.Length)
399                 throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("byteIndex", SR.GetString(SR.OffsetExceedsBufferSize, bytes.Length)));
400             if (byteCount < 0)
401                 throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("byteCount", SR.GetString(SR.ValueMustBeNonNegative)));
402             if (byteCount > bytes.Length - byteIndex)
403                 throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("byteCount", SR.GetString(SR.SizeExceedsRemainingBufferSpace, bytes.Length - byteIndex)));
404 
405             int charCount = GetCharCount(bytes, byteIndex, byteCount);
406             if (chars == null)
407                 throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("chars"));
408             if (charIndex < 0)
409                 throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("charIndex", SR.GetString(SR.ValueMustBeNonNegative)));
410             if (charIndex > chars.Length)
411                 throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("charIndex", SR.GetString(SR.OffsetExceedsBufferSize, chars.Length)));
412 
413             if (charCount < 0 || charCount > chars.Length - charIndex)
414                 throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.GetString(SR.XmlArrayTooSmall), "chars"));
415 
416             // We've computed exactly how many chars there are and verified that
417             // there's enough space in the char buffer, so we can proceed without
418             // checking the charCount.
419 
420             if (byteCount > 0)
421             {
422                 fixed (byte* _val2byte = val2byte)
423                 {
424                     fixed (byte* _bytes = &bytes[byteIndex])
425                     {
426                         fixed (byte* _chars = &chars[charIndex])
427                         {
428                             byte* pb = _bytes;
429                             byte* pbMax = pb + byteCount - 3;
430                             byte* pch = _chars;
431 
432                             // Convert chunks of 3 bytes to 4 chars
433                             while (pb <= pbMax)
434                             {
435                                 // 76543210 76543210 76543210
436                                 // xx765432 xx107654 xx321076 xx543210
437 
438                                 // Inspect the code carefully before you change this
439                                 pch[0] = _val2byte[(pb[0] >> 2)];
440                                 pch[1] = _val2byte[((pb[0] & 0x03) << 4) | (pb[1] >> 4)];
441                                 pch[2] = _val2byte[((pb[1] & 0x0F) << 2) | (pb[2] >> 6)];
442                                 pch[3] = _val2byte[pb[2] & 0x3F];
443 
444                                 pb += 3;
445                                 pch += 4;
446                             }
447 
448                             // Handle 1 or 2 trailing bytes
449                             if (pb - pbMax == 2)
450                             {
451                                 // 1 trailing byte
452                                 // 76543210 xxxxxxxx xxxxxxxx
453                                 // xx765432 xx10xxxx xxxxxxxx xxxxxxxx
454                                 pch[0] = _val2byte[(pb[0] >> 2)];
455                                 pch[1] = _val2byte[((pb[0] & 0x03) << 4)];
456                                 pch[2] = (byte)'=';
457                                 pch[3] = (byte)'=';
458                             }
459                             else if (pb - pbMax == 1)
460                             {
461                                 // 2 trailing bytes
462                                 // 76543210 76543210 xxxxxxxx
463                                 // xx765432 xx107654 xx3210xx xxxxxxxx
464                                 pch[0] = _val2byte[(pb[0] >> 2)];
465                                 pch[1] = _val2byte[((pb[0] & 0x03) << 4) | (pb[1] >> 4)];
466                                 pch[2] = _val2byte[((pb[1] & 0x0F) << 2)];
467                                 pch[3] = (byte)'=';
468                             }
469                             else
470                             {
471                                 // 0 trailing bytes
472                                 Fx.Assert(pb - pbMax == 3, "");
473                             }
474                         }
475                     }
476                 }
477             }
478 
479             return charCount;
480         }
481     }
482 }
483