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