Encoding code clean up (#12864)
[platform/upstream/coreclr.git] / src / mscorlib / shared / System / Text / EncoderNLS.cs
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.Text;
6 using System;
7 using System.Diagnostics.Contracts;
8
9 namespace System.Text
10 {
11     // An Encoder is used to encode a sequence of blocks of characters into
12     // a sequence of blocks of bytes. Following instantiation of an encoder,
13     // sequential blocks of characters are converted into blocks of bytes through
14     // calls to the GetBytes method. The encoder maintains state between the
15     // conversions, allowing it to correctly encode character sequences that span
16     // adjacent blocks.
17     //
18     // Instances of specific implementations of the Encoder abstract base
19     // class are typically obtained through calls to the GetEncoder method
20     // of Encoding objects.
21     //
22
23     internal class EncoderNLS : Encoder
24     {
25         // Need a place for the last left over character, most of our encodings use this
26         internal char _charLeftOver;
27         private Encoding _encoding;
28         private bool _mustFlush;
29         internal bool _throwOnOverflow;
30         internal int _charsUsed;
31
32         internal EncoderNLS(Encoding encoding)
33         {
34             _encoding = encoding;
35             _fallback = _encoding.EncoderFallback;
36             this.Reset();
37         }
38
39         internal EncoderNLS()
40         {
41             _encoding = null;
42             this.Reset();
43         }
44
45         public override void Reset()
46         {
47             _charLeftOver = (char)0;
48             if (_fallbackBuffer != null)
49                 _fallbackBuffer.Reset();
50         }
51
52         public override unsafe int GetByteCount(char[] chars, int index, int count, bool flush)
53         {
54             // Validate input parameters
55             if (chars == null)
56                 throw new ArgumentNullException(nameof(chars),
57                       SR.ArgumentNull_Array);
58
59             if (index < 0 || count < 0)
60                 throw new ArgumentOutOfRangeException((index < 0 ? nameof(index) : nameof(count)),
61                       SR.ArgumentOutOfRange_NeedNonNegNum);
62
63             if (chars.Length - index < count)
64                 throw new ArgumentOutOfRangeException(nameof(chars),
65                       SR.ArgumentOutOfRange_IndexCountBuffer);
66             Contract.EndContractBlock();
67
68             // Avoid empty input problem
69             if (chars.Length == 0)
70                 chars = new char[1];
71
72             // Just call the pointer version
73             int result = -1;
74             fixed (char* pChars = &chars[0])
75             {
76                 result = GetByteCount(pChars + index, count, flush);
77             }
78             return result;
79         }
80
81         public unsafe override int GetByteCount(char* chars, int count, bool flush)
82         {
83             // Validate input parameters
84             if (chars == null)
85                 throw new ArgumentNullException(nameof(chars),
86                       SR.ArgumentNull_Array);
87
88             if (count < 0)
89                 throw new ArgumentOutOfRangeException(nameof(count),
90                       SR.ArgumentOutOfRange_NeedNonNegNum);
91             Contract.EndContractBlock();
92
93             _mustFlush = flush;
94             _throwOnOverflow = true;
95             return _encoding.GetByteCount(chars, count, this);
96         }
97
98         public override unsafe int GetBytes(char[] chars, int charIndex, int charCount,
99                                               byte[] bytes, int byteIndex, bool flush)
100         {
101             // Validate parameters
102             if (chars == null || bytes == null)
103                 throw new ArgumentNullException((chars == null ? nameof(chars) : nameof(bytes)),
104                       SR.ArgumentNull_Array);
105
106             if (charIndex < 0 || charCount < 0)
107                 throw new ArgumentOutOfRangeException((charIndex < 0 ? nameof(charIndex) : nameof(charCount)),
108                       SR.ArgumentOutOfRange_NeedNonNegNum);
109
110             if (chars.Length - charIndex < charCount)
111                 throw new ArgumentOutOfRangeException(nameof(chars),
112                       SR.ArgumentOutOfRange_IndexCountBuffer);
113
114             if (byteIndex < 0 || byteIndex > bytes.Length)
115                 throw new ArgumentOutOfRangeException(nameof(byteIndex),
116                      SR.ArgumentOutOfRange_Index);
117             Contract.EndContractBlock();
118
119             if (chars.Length == 0)
120                 chars = new char[1];
121
122             int byteCount = bytes.Length - byteIndex;
123             if (bytes.Length == 0)
124                 bytes = new byte[1];
125
126             // Just call pointer version
127             fixed (char* pChars = &chars[0])
128             fixed (byte* pBytes = &bytes[0])
129
130                 // Remember that charCount is # to decode, not size of array.
131                 return GetBytes(pChars + charIndex, charCount,
132                                 pBytes + byteIndex, byteCount, flush);
133         }
134
135         public unsafe override int GetBytes(char* chars, int charCount, byte* bytes, int byteCount, bool flush)
136         {
137             // Validate parameters
138             if (chars == null || bytes == null)
139                 throw new ArgumentNullException((chars == null ? nameof(chars) : nameof(bytes)),
140                       SR.ArgumentNull_Array);
141
142             if (byteCount < 0 || charCount < 0)
143                 throw new ArgumentOutOfRangeException((byteCount < 0 ? nameof(byteCount) : nameof(charCount)),
144                       SR.ArgumentOutOfRange_NeedNonNegNum);
145             Contract.EndContractBlock();
146
147             _mustFlush = flush;
148             _throwOnOverflow = true;
149             return _encoding.GetBytes(chars, charCount, bytes, byteCount, this);
150         }
151
152         // This method is used when your output buffer might not be large enough for the entire result.
153         // Just call the pointer version.  (This gets bytes)
154         public override unsafe void Convert(char[] chars, int charIndex, int charCount,
155                                               byte[] bytes, int byteIndex, int byteCount, bool flush,
156                                               out int charsUsed, out int bytesUsed, out bool completed)
157         {
158             // Validate parameters
159             if (chars == null || bytes == null)
160                 throw new ArgumentNullException((chars == null ? nameof(chars) : nameof(bytes)),
161                       SR.ArgumentNull_Array);
162
163             if (charIndex < 0 || charCount < 0)
164                 throw new ArgumentOutOfRangeException((charIndex < 0 ? nameof(charIndex) : nameof(charCount)),
165                       SR.ArgumentOutOfRange_NeedNonNegNum);
166
167             if (byteIndex < 0 || byteCount < 0)
168                 throw new ArgumentOutOfRangeException((byteIndex < 0 ? nameof(byteIndex) : nameof(byteCount)),
169                       SR.ArgumentOutOfRange_NeedNonNegNum);
170
171             if (chars.Length - charIndex < charCount)
172                 throw new ArgumentOutOfRangeException(nameof(chars),
173                       SR.ArgumentOutOfRange_IndexCountBuffer);
174
175             if (bytes.Length - byteIndex < byteCount)
176                 throw new ArgumentOutOfRangeException(nameof(bytes),
177                       SR.ArgumentOutOfRange_IndexCountBuffer);
178
179             Contract.EndContractBlock();
180
181             // Avoid empty input problem
182             if (chars.Length == 0)
183                 chars = new char[1];
184             if (bytes.Length == 0)
185                 bytes = new byte[1];
186
187             // Just call the pointer version (can't do this for non-msft encoders)
188             fixed (char* pChars = &chars[0])
189             {
190                 fixed (byte* pBytes = &bytes[0])
191                 {
192                     Convert(pChars + charIndex, charCount, pBytes + byteIndex, byteCount, flush,
193                         out charsUsed, out bytesUsed, out completed);
194                 }
195             }
196         }
197
198         // This is the version that uses pointers.  We call the base encoding worker function
199         // after setting our appropriate internal variables.  This is getting bytes
200         public override unsafe void Convert(char* chars, int charCount,
201                                               byte* bytes, int byteCount, bool flush,
202                                               out int charsUsed, out int bytesUsed, out bool completed)
203         {
204             // Validate input parameters
205             if (bytes == null || chars == null)
206                 throw new ArgumentNullException(bytes == null ? nameof(bytes) : nameof(chars),
207                     SR.ArgumentNull_Array);
208             if (charCount < 0 || byteCount < 0)
209                 throw new ArgumentOutOfRangeException((charCount < 0 ? nameof(charCount) : nameof(byteCount)),
210                     SR.ArgumentOutOfRange_NeedNonNegNum);
211             Contract.EndContractBlock();
212
213             // We don't want to throw
214             _mustFlush = flush;
215             _throwOnOverflow = false;
216             _charsUsed = 0;
217
218             // Do conversion
219             bytesUsed = _encoding.GetBytes(chars, charCount, bytes, byteCount, this);
220             charsUsed = _charsUsed;
221
222             // Its completed if they've used what they wanted AND if they didn't want flush or if we are flushed
223             completed = (charsUsed == charCount) && (!flush || !this.HasState) &&
224                 (_fallbackBuffer == null || _fallbackBuffer.Remaining == 0);
225
226             // Our data thingys are now full, we can return
227         }
228
229         public Encoding Encoding
230         {
231             get
232             {
233                 return _encoding;
234             }
235         }
236
237         public bool MustFlush
238         {
239             get
240             {
241                 return _mustFlush;
242             }
243         }
244
245
246         // Anything left in our encoder?
247         internal virtual bool HasState
248         {
249             get
250             {
251                 return (_charLeftOver != (char)0);
252             }
253         }
254
255         // Allow encoding to clear our must flush instead of throwing (in ThrowBytesOverflow)
256         internal void ClearMustFlush()
257         {
258             _mustFlush = false;
259         }
260     }
261 }