1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4 
5 using System;
6 using System.Collections;
7 using System.Globalization;
8 using System.Runtime.InteropServices;
9 using System.Threading;
10 
11 namespace System.Text
12 {
13     // This class overrides Encoding with the things we need for our NLS Encodings
14     //
15     // All of the GetBytes/Chars GetByte/CharCount methods are just wrappers for the pointer
16     // plus decoder/encoder method that is our real workhorse.  Note that this is an internal
17     // class, so our public classes cannot derive from this class.  Because of this, all of the
18     // GetBytes/Chars GetByte/CharCount wrapper methods are duplicated in all of our public
19     // encodings, which currently include:
20     //
21     //      EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, & UnicodeEncoding
22     //
23     // So if you change the wrappers in this class, you must change the wrappers in the other classes
24     // as well because they should have the same behavior.
25 
26     internal abstract class EncodingNLS : Encoding
27     {
EncodingNLS(int codePage)28         protected EncodingNLS(int codePage) : base(codePage)
29         {
30         }
31 
32         // Returns the number of bytes required to encode a range of characters in
33         // a character array.
34         //
35         // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
36         // So if you fix this, fix the others.  Currently those include:
37         // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
38         // parent method is safe
GetByteCount(char[] chars, int index, int count)39         public override unsafe int GetByteCount(char[] chars, int index, int count)
40         {
41             // Validate input parameters
42             if (chars == null)
43                 throw new ArgumentNullException("chars", SR.ArgumentNull_Array);
44 
45             if (index < 0 || count < 0)
46                 throw new ArgumentOutOfRangeException((index < 0 ? "index" : "count"), SR.ArgumentOutOfRange_NeedNonNegNum);
47 
48             if (chars.Length - index < count)
49                 throw new ArgumentOutOfRangeException("chars", SR.ArgumentOutOfRange_IndexCountBuffer);
50 
51             // If no input, return 0, avoid fixed empty array problem
52             if (count == 0)
53                 return 0;
54 
55             // Just call the pointer version
56             fixed (char* pChars = chars)
57                 return GetByteCount(pChars + index, count, null);
58         }
59 
60         // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
61         // So if you fix this, fix the others.  Currently those include:
62         // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
63         // parent method is safe
GetByteCount(String s)64         public override unsafe int GetByteCount(String s)
65         {
66             // Validate input
67             if (s==null)
68                 throw new ArgumentNullException("s");
69 
70             fixed (char* pChars = s)
71                 return GetByteCount(pChars, s.Length, null);
72         }
73 
74         // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
75         // So if you fix this, fix the others.  Currently those include:
76         // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
GetByteCount(char* chars, int count)77         public override unsafe int GetByteCount(char* chars, int count)
78         {
79             // Validate Parameters
80             if (chars == null)
81                 throw new ArgumentNullException("chars", SR.ArgumentNull_Array);
82 
83             if (count < 0)
84                 throw new ArgumentOutOfRangeException("count", SR.ArgumentOutOfRange_NeedNonNegNum);
85 
86             // Call it with empty encoder
87             return GetByteCount(chars, count, null);
88         }
89 
90         // Parent method is safe.
91         // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
92         // So if you fix this, fix the others.  Currently those include:
93         // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
GetBytes(String s, int charIndex, int charCount, byte[] bytes, int byteIndex)94         public override unsafe int GetBytes(String s, int charIndex, int charCount,
95                                               byte[] bytes, int byteIndex)
96         {
97             if (s == null || bytes == null)
98                 throw new ArgumentNullException((s == null ? "s" : "bytes"), SR.ArgumentNull_Array);
99 
100             if (charIndex < 0 || charCount < 0)
101                 throw new ArgumentOutOfRangeException((charIndex < 0 ? "charIndex" : "charCount"), SR.ArgumentOutOfRange_NeedNonNegNum);
102 
103             if (s.Length - charIndex < charCount)
104                 throw new ArgumentOutOfRangeException("s", SR.ArgumentOutOfRange_IndexCount);
105 
106             if (byteIndex < 0 || byteIndex > bytes.Length)
107                 throw new ArgumentOutOfRangeException("byteIndex", SR.ArgumentOutOfRange_Index);
108 
109             int byteCount = bytes.Length - byteIndex;
110 
111             fixed (char* pChars = s) fixed (byte* pBytes = &MemoryMarshal.GetReference((Span<byte>)bytes))
112                 return GetBytes(pChars + charIndex, charCount, pBytes + byteIndex, byteCount, null);
113         }
114 
115         // Encodes a range of characters in a character array into a range of bytes
116         // in a byte array. An exception occurs if the byte array is not large
117         // enough to hold the complete encoding of the characters. The
118         // GetByteCount method can be used to determine the exact number of
119         // bytes that will be produced for a given range of characters.
120         // Alternatively, the GetMaxByteCount method can be used to
121         // determine the maximum number of bytes that will be produced for a given
122         // number of characters, regardless of the actual character values.
123         //
124         // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
125         // So if you fix this, fix the others.  Currently those include:
126         // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
127         // parent method is safe
GetBytes(char[] chars, int charIndex, int charCount, byte[] bytes, int byteIndex)128         public override unsafe int GetBytes(char[] chars, int charIndex, int charCount,
129                                                byte[] bytes, int byteIndex)
130         {
131             // Validate parameters
132             if (chars == null || bytes == null)
133                 throw new ArgumentNullException((chars == null ? "chars" : "bytes"), SR.ArgumentNull_Array);
134 
135             if (charIndex < 0 || charCount < 0)
136                 throw new ArgumentOutOfRangeException((charIndex < 0 ? "charIndex" : "charCount"), SR.ArgumentOutOfRange_NeedNonNegNum);
137 
138             if (chars.Length - charIndex < charCount)
139                 throw new ArgumentOutOfRangeException("chars", SR.ArgumentOutOfRange_IndexCountBuffer);
140 
141             if (byteIndex < 0 || byteIndex > bytes.Length)
142                 throw new ArgumentOutOfRangeException("byteIndex", SR.ArgumentOutOfRange_Index);
143 
144             // If nothing to encode return 0, avoid fixed problem
145             if (charCount == 0)
146                 return 0;
147 
148             // Just call pointer version
149             int byteCount = bytes.Length - byteIndex;
150 
151             fixed (char* pChars = chars) fixed (byte* pBytes = &MemoryMarshal.GetReference((Span<byte>)bytes))
152                 // Remember that byteCount is # to decode, not size of array.
153                 return GetBytes(pChars + charIndex, charCount, pBytes + byteIndex, byteCount, null);
154         }
155 
156         // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
157         // So if you fix this, fix the others.  Currently those include:
158         // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
GetBytes(char* chars, int charCount, byte* bytes, int byteCount)159         public override unsafe int GetBytes(char* chars, int charCount, byte* bytes, int byteCount)
160         {
161             // Validate Parameters
162             if (bytes == null || chars == null)
163                 throw new ArgumentNullException(bytes == null ? "bytes" : "chars", SR.ArgumentNull_Array);
164 
165             if (charCount < 0 || byteCount < 0)
166                 throw new ArgumentOutOfRangeException((charCount < 0 ? "charCount" : "byteCount"),  SR.ArgumentOutOfRange_NeedNonNegNum);
167 
168             return GetBytes(chars, charCount, bytes, byteCount, null);
169         }
170 
171         // Returns the number of characters produced by decoding a range of bytes
172         // in a byte array.
173         //
174         // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
175         // So if you fix this, fix the others.  Currently those include:
176         // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
177         // parent method is safe
GetCharCount(byte[] bytes, int index, int count)178         public override unsafe int GetCharCount(byte[] bytes, int index, int count)
179         {
180             // Validate Parameters
181             if (bytes == null)
182                 throw new ArgumentNullException("bytes", SR.ArgumentNull_Array);
183 
184             if (index < 0 || count < 0)
185                 throw new ArgumentOutOfRangeException((index < 0 ? "index" : "count"), SR.ArgumentOutOfRange_NeedNonNegNum);
186 
187             if (bytes.Length - index < count)
188                 throw new ArgumentOutOfRangeException("bytes", SR.ArgumentOutOfRange_IndexCountBuffer);
189 
190             // If no input just return 0, fixed doesn't like 0 length arrays
191             if (count == 0)
192                 return 0;
193 
194             // Just call pointer version
195             fixed (byte* pBytes = bytes)
196                 return GetCharCount(pBytes + index, count, null);
197         }
198 
199         // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
200         // So if you fix this, fix the others.  Currently those include:
201         // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
GetCharCount(byte* bytes, int count)202         public override unsafe int GetCharCount(byte* bytes, int count)
203         {
204             // Validate Parameters
205             if (bytes == null)
206                 throw new ArgumentNullException("bytes", SR.ArgumentNull_Array);
207 
208             if (count < 0)
209                 throw new ArgumentOutOfRangeException("count", SR.ArgumentOutOfRange_NeedNonNegNum);
210 
211             return GetCharCount(bytes, count, null);
212         }
213 
214         // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
215         // So if you fix this, fix the others.  Currently those include:
216         // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
217         // parent method is safe
GetChars(byte[] bytes, int byteIndex, int byteCount, char[] chars, int charIndex)218         public override unsafe int GetChars(byte[] bytes, int byteIndex, int byteCount,
219                                               char[] chars, int charIndex)
220         {
221             // Validate Parameters
222             if (bytes == null || chars == null)
223                 throw new ArgumentNullException(bytes == null ? "bytes" : "chars", SR.ArgumentNull_Array);
224 
225             if (byteIndex < 0 || byteCount < 0)
226                 throw new ArgumentOutOfRangeException((byteIndex < 0 ? "byteIndex" : "byteCount"), SR.ArgumentOutOfRange_NeedNonNegNum);
227 
228             if ( bytes.Length - byteIndex < byteCount)
229                 throw new ArgumentOutOfRangeException("bytes", SR.ArgumentOutOfRange_IndexCountBuffer);
230 
231             if (charIndex < 0 || charIndex > chars.Length)
232                 throw new ArgumentOutOfRangeException("charIndex", SR.ArgumentOutOfRange_Index);
233 
234             // If no input, return 0 & avoid fixed problem
235             if (byteCount == 0)
236                 return 0;
237 
238             // Just call pointer version
239             int charCount = chars.Length - charIndex;
240 
241             fixed (byte* pBytes = bytes) fixed (char* pChars = &MemoryMarshal.GetReference((Span<char>)chars))
242                 // Remember that charCount is # to decode, not size of array
243                 return GetChars(pBytes + byteIndex, byteCount, pChars + charIndex, charCount, null);
244         }
245 
246         // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
247         // So if you fix this, fix the others.  Currently those include:
248         // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
GetChars(byte* bytes, int byteCount, char* chars, int charCount)249         public unsafe override int GetChars(byte* bytes, int byteCount, char* chars, int charCount)
250         {
251             // Validate Parameters
252             if (bytes == null || chars == null)
253                 throw new ArgumentNullException(bytes == null ? "bytes" : "chars", SR.ArgumentNull_Array);
254 
255             if (charCount < 0 || byteCount < 0)
256                 throw new ArgumentOutOfRangeException((charCount < 0 ? "charCount" : "byteCount"), SR.ArgumentOutOfRange_NeedNonNegNum);
257 
258             return GetChars(bytes, byteCount, chars, charCount, null);
259         }
260 
261         // Returns a string containing the decoded representation of a range of
262         // bytes in a byte array.
263         //
264         // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
265         // So if you fix this, fix the others.  Currently those include:
266         // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
267         // parent method is safe
GetString(byte[] bytes, int index, int count)268         public override unsafe String GetString(byte[] bytes, int index, int count)
269         {
270             // Validate Parameters
271             if (bytes == null)
272                 throw new ArgumentNullException("bytes", SR.ArgumentNull_Array);
273 
274             if (index < 0 || count < 0)
275                 throw new ArgumentOutOfRangeException((index < 0 ? "index" : "count"), SR.ArgumentOutOfRange_NeedNonNegNum);
276 
277             if (bytes.Length - index < count)
278                 throw new ArgumentOutOfRangeException("bytes", SR.ArgumentOutOfRange_IndexCountBuffer);
279 
280             // Avoid problems with empty input buffer
281             if (count == 0) return String.Empty;
282 
283             fixed (byte* pBytes = bytes)
284                 return String.CreateStringFromEncoding(
285                     pBytes + index, count, this);
286         }
287 
GetDecoder()288         public override Decoder GetDecoder()
289         {
290             return new DecoderNLS(this);
291         }
292 
GetEncoder()293         public override Encoder GetEncoder()
294         {
295             return new EncoderNLS(this);
296         }
297     }
298 }
299