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.Text;
7 using System.Diagnostics;
8 
9 namespace System.Text
10 {
11     [Serializable]
12     internal sealed class DecoderDBCS : Decoder
13     {
14         private readonly Encoding _encoding;
15         private readonly byte[] _leadByteRanges = new byte[10]; // Max 5 ranges
16         private int _rangesCount;
17         private byte _leftOverLeadByte;
18 
DecoderDBCS(Encoding encoding)19         internal DecoderDBCS(Encoding encoding)
20         {
21             _encoding = encoding;
22             _rangesCount = Interop.Kernel32.GetLeadByteRanges(_encoding.CodePage, _leadByteRanges);
23             Reset();
24         }
25 
IsLeadByte(byte b)26         private bool IsLeadByte(byte b)
27         {
28             if (b < _leadByteRanges[0])
29                 return false;
30             int i = 0;
31             while (i < _rangesCount)
32             {
33                 if (b >= _leadByteRanges[i] && b <= _leadByteRanges[i + 1])
34                     return true;
35                 i += 2;
36             }
37             return false;
38         }
39 
Reset()40         public override void Reset()
41         {
42             _leftOverLeadByte = 0;
43         }
44 
GetCharCount(byte[] bytes, int index, int count)45         public override unsafe int GetCharCount(byte[] bytes, int index, int count)
46         {
47             return GetCharCount(bytes, index, count, false);
48         }
49 
GetCharCount(byte[] bytes, int index, int count, bool flush)50         public override unsafe int GetCharCount(byte[] bytes, int index, int count, bool flush)
51         {
52             if (bytes == null)
53                 throw new ArgumentNullException(nameof(bytes), SR.ArgumentNull_Array);
54 
55             if (index < 0 || count < 0)
56                 throw new ArgumentOutOfRangeException(index < 0 ? nameof(index) : nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
57 
58             if (bytes.Length - index < count)
59                 throw new ArgumentOutOfRangeException(nameof(bytes), SR.ArgumentOutOfRange_IndexCountBuffer);
60 
61             if (count == 0 && (_leftOverLeadByte == 0 || !flush))
62                 return 0;
63 
64             fixed (byte* pBytes = bytes)
65             {
66                 byte dummyByte;
67                 byte* pBuffer = pBytes == null ? &dummyByte : pBytes + index;
68 
69                 return GetCharCount(pBuffer, count, flush);
70             }
71         }
72 
ConvertWithLeftOverByte(byte* bytes, int count, char* chars, int charCount)73         private unsafe int ConvertWithLeftOverByte(byte* bytes, int count, char* chars, int charCount)
74         {
75             Debug.Assert(_leftOverLeadByte != 0);
76             byte* pTempBuffer = stackalloc byte[2];
77             pTempBuffer[0] = _leftOverLeadByte;
78 
79             int index = 0;
80 
81             if (count > 0)
82             {
83                 pTempBuffer[1] = bytes[0];
84                 index++;
85             }
86 
87             int result = OSEncoding.MultiByteToWideChar(_encoding.CodePage, pTempBuffer, index+1, chars, charCount);
88 
89             if (count - index > 0)
90                 result += OSEncoding.MultiByteToWideChar(
91                                         _encoding.CodePage, bytes + index,
92                                         count - index,
93                                         chars == null ? null : chars + result,
94                                         chars == null ? 0 : charCount - result);
95 
96             return result;
97         }
98 
GetCharCount(byte* bytes, int count, bool flush)99         public unsafe override int GetCharCount(byte* bytes, int count, bool flush)
100         {
101             if (bytes == null)
102                 throw new ArgumentNullException(nameof(bytes), SR.ArgumentNull_Array);
103 
104             if (count < 0)
105                 throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
106 
107             bool excludeLastByte = count > 0 && !flush && IsLastByteALeadByte(bytes, count);
108 
109             if (excludeLastByte)
110                 count--;
111 
112             if (_leftOverLeadByte == 0)
113             {
114                 if (count <= 0)
115                     return 0;
116 
117                 return OSEncoding.MultiByteToWideChar(_encoding.CodePage, bytes, count, null, 0);
118             }
119 
120             if (count == 0 && !excludeLastByte && !flush)
121                 return 0;
122 
123             return ConvertWithLeftOverByte(bytes, count, null, 0);
124         }
125 
GetChars(byte[] bytes, int byteIndex, int byteCount, char[] chars, int charIndex)126         public override unsafe int GetChars(byte[] bytes, int byteIndex, int byteCount,
127                                              char[] chars, int charIndex)
128         {
129             return GetChars(bytes, byteIndex, byteCount, chars, charIndex, false);
130         }
131 
GetChars(byte[] bytes, int byteIndex, int byteCount, char[] chars, int charIndex, bool flush)132         public override unsafe int GetChars(byte[] bytes, int byteIndex, int byteCount,
133                                              char[] chars, int charIndex, bool flush)
134         {
135             if (bytes == null || chars == null)
136                 throw new ArgumentNullException(bytes == null ? nameof(bytes) : nameof(chars), SR.ArgumentNull_Array);
137 
138             if (byteIndex < 0 || byteCount < 0)
139                 throw new ArgumentOutOfRangeException(byteIndex < 0 ? nameof(byteIndex) : nameof(byteCount), SR.ArgumentOutOfRange_NeedNonNegNum);
140 
141             if (bytes.Length - byteIndex < byteCount)
142                 throw new ArgumentOutOfRangeException(nameof(bytes), SR.ArgumentOutOfRange_IndexCountBuffer);
143 
144             if (charIndex < 0 || charIndex > chars.Length)
145                 throw new ArgumentOutOfRangeException(nameof(charIndex), SR.ArgumentOutOfRange_Index);
146 
147             if (chars.Length == 0)
148                 return 0;
149 
150             if (byteCount == 0 && (_leftOverLeadByte == 0 || !flush))
151                 return 0;
152 
153             fixed (char* pChars = &chars[0])
154             fixed (byte* pBytes = bytes)
155             {
156                 byte dummyByte;
157                 byte* pBuffer = pBytes == null ? &dummyByte : pBytes + byteIndex;
158 
159                 return GetChars(pBuffer, byteCount, pChars + charIndex, chars.Length - charIndex, flush);
160             }
161         }
162 
GetChars(byte* bytes, int byteCount, char* chars, int charCount, bool flush)163         public unsafe override int GetChars(byte* bytes, int byteCount, char* chars, int charCount, bool flush)
164         {
165             if (chars == null || bytes == null)
166                 throw new ArgumentNullException(chars == null ? nameof(chars) : nameof(bytes), SR.ArgumentNull_Array);
167 
168             if (byteCount < 0 || charCount < 0)
169                 throw new ArgumentOutOfRangeException(byteCount < 0 ? nameof(byteCount) : nameof(charCount), SR.ArgumentOutOfRange_NeedNonNegNum);
170 
171             if (charCount == 0)
172                 return 0;
173 
174             byte lastByte = byteCount > 0 && !flush && IsLastByteALeadByte(bytes, byteCount) ? bytes[byteCount - 1] : (byte) 0;
175 
176             if (lastByte != 0)
177                 byteCount--;
178 
179             if (_leftOverLeadByte == 0)
180             {
181                 if (byteCount <= 0)
182                 {
183                     _leftOverLeadByte = lastByte;
184                     return 0;
185                 }
186 
187                 int result =  OSEncoding.MultiByteToWideChar(_encoding.CodePage, bytes, byteCount, chars, charCount);
188                 _leftOverLeadByte = lastByte;
189                 return result;
190             }
191 
192             // we have left over lead byte
193             if (byteCount == 0 && lastByte == 0 && !flush)
194                 return 0;
195 
196             int res = ConvertWithLeftOverByte(bytes, byteCount, chars, charCount);
197             _leftOverLeadByte = lastByte;
198             return res;
199         }
200 
Convert(byte[] bytes, int byteIndex, int byteCount, char[] chars, int charIndex, int charCount, bool flush, out int bytesUsed, out int charsUsed, out bool completed)201         public override unsafe void Convert(byte[] bytes, int byteIndex, int byteCount,
202                                               char[] chars, int charIndex, int charCount, bool flush,
203                                               out int bytesUsed, out int charsUsed, out bool completed)
204         {
205             if (bytes == null || chars == null)
206                 throw new ArgumentNullException(bytes == null ? nameof(bytes) : nameof(chars), SR.ArgumentNull_Array);
207 
208             if (byteIndex < 0 || byteCount < 0)
209                 throw new ArgumentOutOfRangeException(byteIndex < 0 ? nameof(byteIndex) : nameof(byteCount), SR.ArgumentOutOfRange_NeedNonNegNum);
210 
211             if (charIndex < 0 || charCount < 0)
212                 throw new ArgumentOutOfRangeException(charIndex < 0 ? nameof(charIndex) : nameof(charCount), SR.ArgumentOutOfRange_NeedNonNegNum);
213 
214             if (bytes.Length - byteIndex < byteCount)
215                 throw new ArgumentOutOfRangeException(nameof(bytes), SR.ArgumentOutOfRange_IndexCountBuffer);
216 
217             if (chars.Length - charIndex < charCount)
218                 throw new ArgumentOutOfRangeException(nameof(chars), SR.ArgumentOutOfRange_IndexCountBuffer);
219 
220             if (charCount == 0 || (bytes.Length == 0 && (_leftOverLeadByte == 0 || !flush)))
221             {
222                 bytesUsed = 0;
223                 charsUsed = 0;
224                 completed = false;
225                 return;
226             }
227 
228             fixed (char* pChars = &chars[0])
229             fixed (byte* pBytes = bytes)
230             {
231                 byte dummyByte;
232                 byte* pBuffer = pBytes == null ? &dummyByte : pBytes + byteIndex;
233 
234                 Convert(pBuffer, byteCount, pChars + charIndex, charCount, flush, out bytesUsed, out charsUsed, out completed);
235             }
236         }
237 
Convert(byte* bytes, int byteCount, char* chars, int charCount, bool flush, out int bytesUsed, out int charsUsed, out bool completed)238         public unsafe override void Convert(byte* bytes, int byteCount,
239                                               char* chars, int charCount, bool flush,
240                                               out int bytesUsed, out int charsUsed, out bool completed)
241         {
242             if (chars == null || bytes == null)
243                 throw new ArgumentNullException(chars == null ? nameof(chars) : nameof(bytes), SR.ArgumentNull_Array);
244             if (byteCount < 0 || charCount < 0)
245                 throw new ArgumentOutOfRangeException(byteCount < 0 ? nameof(byteCount) : nameof(charCount), SR.ArgumentOutOfRange_NeedNonNegNum);
246 
247             int count = byteCount;
248             while (count > 0)
249             {
250                 int returnedCharCount = GetCharCount(bytes, count, flush);
251                 if (returnedCharCount <= charCount)
252                     break;
253 
254                 count /= 2;
255             }
256 
257             if (count > 0)
258             {
259                 // note GetChars can change the _leftOverLeadByte state
260                 charsUsed = GetChars(bytes, count, chars, charCount, flush);
261                 bytesUsed = count;
262                 completed = _leftOverLeadByte == 0 && byteCount == count;
263                 return;
264             }
265 
266             bytesUsed = 0;
267             charsUsed = 0;
268             completed = false;
269         }
270 
271         // not IsLastByteALeadByte depends on the _leftOverLeadByte state
IsLastByteALeadByte(byte* bytes, int count)272         private unsafe bool IsLastByteALeadByte(byte* bytes, int count)
273         {
274             if (!IsLeadByte(bytes[count - 1]))
275                 return false; // no need to process the buffer
276 
277             int index = 0;
278             if (_leftOverLeadByte != 0)
279                 index++; // trail byte
280 
281             while (index < count)
282             {
283                 if (IsLeadByte(bytes[index]))
284                 {
285                     index++;
286                     if (index >= count)
287                         return true;
288                 }
289                 index++;
290             }
291             return false;
292         }
293     }
294 }
295