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