|
| 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.Diagnostics.Contracts; |
| 6 | +using System.Security; |
| 7 | + |
| 8 | +namespace System.Text |
| 9 | +{ |
| 10 | + // Shared implementations for commonly overriden Encoding methods |
| 11 | + |
| 12 | + internal static class EncodingForwarder |
| 13 | + { |
| 14 | + // We normally have to duplicate a lot of code between UTF8Encoding, |
| 15 | + // UTF7Encoding, EncodingNLS, etc. because we want to override many |
| 16 | + // of the methods in all of those classes to just forward to the unsafe |
| 17 | + // version. (e.g. GetBytes(char[])) |
| 18 | + // Ideally, everything would just derive from EncodingNLS, but that's |
| 19 | + // not exposed in the public API, and C# prohibits a public class from |
| 20 | + // inheriting from an internal one. So, we have to override each of the |
| 21 | + // methods in question and repeat the argument validation/logic. |
| 22 | + |
| 23 | + // These set of methods exist so instead of duplicating code, we can |
| 24 | + // simply have those overriden methods call here to do the actual work. |
| 25 | + |
| 26 | + // NOTE: This class should ONLY be called from Encodings that override |
| 27 | + // the internal methods which accept an Encoder/DecoderNLS. The reason |
| 28 | + // for this is that by default, those methods just call the same overload |
| 29 | + // except without the encoder/decoder parameter. If an overriden method |
| 30 | + // without that parameter calls this class, which calls the overload with |
| 31 | + // the parameter, it will call the same method again, which will eventually |
| 32 | + // lead to a StackOverflowException. |
| 33 | + |
| 34 | + [SecuritySafeCritical] |
| 35 | + public unsafe static int GetByteCount(Encoding encoding, char[] chars, int index, int count) |
| 36 | + { |
| 37 | + // Validate parameters |
| 38 | + |
| 39 | + Contract.Assert(encoding != null); // this parameter should only be affected internally, so just do a debug check here |
| 40 | + if (chars == null) |
| 41 | + { |
| 42 | + throw new ArgumentNullException("chars", Environment.GetResourceString("ArgumentNull_Array")); |
| 43 | + } |
| 44 | + if (index < 0 || count < 0) |
| 45 | + { |
| 46 | + throw new ArgumentOutOfRangeException(index < 0 ? "index" : "count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); |
| 47 | + } |
| 48 | + if (chars.Length - index < count) |
| 49 | + { |
| 50 | + throw new ArgumentOutOfRangeException("chars", Environment.GetResourceString("ArgumentOutOfRange_IndexCountBuffer")); |
| 51 | + } |
| 52 | + Contract.EndContractBlock(); |
| 53 | + |
| 54 | + // If no input, return 0, avoid fixed empty array problem |
| 55 | + if (count == 0) |
| 56 | + return 0; |
| 57 | + |
| 58 | + // Just call the (internal) pointer version |
| 59 | + fixed (char* pChars = chars) |
| 60 | + return encoding.GetByteCount(pChars + index, count, encoder: null); |
| 61 | + } |
| 62 | + |
| 63 | + [SecuritySafeCritical] |
| 64 | + public unsafe static int GetByteCount(Encoding encoding, string s) |
| 65 | + { |
| 66 | + Contract.Assert(encoding != null); |
| 67 | + if (s == null) |
| 68 | + { |
| 69 | + string paramName = encoding is ASCIIEncoding ? "chars" : "s"; // ASCIIEncoding calls the string chars |
| 70 | + // UTF8Encoding does this as well, but it originally threw an ArgumentNull for "s" so don't check for that |
| 71 | + throw new ArgumentNullException(paramName); |
| 72 | + } |
| 73 | + Contract.EndContractBlock(); |
| 74 | + |
| 75 | + // NOTE: The behavior of fixed *is* defined by |
| 76 | + // the spec for empty strings, although not for |
| 77 | + // null strings/empty char arrays. See |
| 78 | + // http://stackoverflow.com/q/37757751/4077294 |
| 79 | + // Regardless, we may still want to check |
| 80 | + // for if (s.Length == 0) in the future |
| 81 | + // and short-circuit as an optimization (TODO). |
| 82 | + |
| 83 | + fixed (char* pChars = s) |
| 84 | + return encoding.GetByteCount(pChars, s.Length, encoder: null); |
| 85 | + } |
| 86 | + |
| 87 | + [SecurityCritical] |
| 88 | + public unsafe static int GetByteCount(Encoding encoding, char* chars, int count) |
| 89 | + { |
| 90 | + Contract.Assert(encoding != null); |
| 91 | + if (chars == null) |
| 92 | + { |
| 93 | + throw new ArgumentNullException("chars", Environment.GetResourceString("ArgumentNull_Array")); |
| 94 | + } |
| 95 | + if (count < 0) |
| 96 | + { |
| 97 | + throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); |
| 98 | + } |
| 99 | + Contract.EndContractBlock(); |
| 100 | + |
| 101 | + // Call the internal version, with an empty encoder |
| 102 | + return encoding.GetByteCount(chars, count, encoder: null); |
| 103 | + } |
| 104 | + |
| 105 | + [SecuritySafeCritical] |
| 106 | + public unsafe static int GetBytes(Encoding encoding, string s, int charIndex, int charCount, byte[] bytes, int byteIndex) |
| 107 | + { |
| 108 | + Contract.Assert(encoding != null); |
| 109 | + if (s == null || bytes == null) |
| 110 | + { |
| 111 | + string stringName = encoding is ASCIIEncoding ? "chars" : "s"; // ASCIIEncoding calls the first parameter chars |
| 112 | + throw new ArgumentNullException(s == null ? stringName : "bytes", Environment.GetResourceString("ArgumentNull_Array")); |
| 113 | + } |
| 114 | + if (charIndex < 0 || charCount < 0) |
| 115 | + { |
| 116 | + throw new ArgumentOutOfRangeException(charIndex < 0 ? "charIndex" : "charCount", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); |
| 117 | + } |
| 118 | + if (s.Length - charIndex < charCount) |
| 119 | + { |
| 120 | + string stringName = encoding is ASCIIEncoding ? "chars" : "s"; // ASCIIEncoding calls the first parameter chars |
| 121 | + // Duplicate the above check since we don't want the overhead of a type check on the general path |
| 122 | + throw new ArgumentOutOfRangeException(stringName, Environment.GetResourceString("ArgumentOutOfRange_IndexCount")); |
| 123 | + } |
| 124 | + if (byteIndex < 0 || byteIndex > bytes.Length) |
| 125 | + { |
| 126 | + throw new ArgumentOutOfRangeException("byteIndex", Environment.GetResourceString("ArgumentOutOfRange_Index")); |
| 127 | + } |
| 128 | + Contract.EndContractBlock(); |
| 129 | + |
| 130 | + int byteCount = bytes.Length - byteIndex; |
| 131 | + |
| 132 | + // Fixed doesn't like empty arrays |
| 133 | + if (bytes.Length == 0) |
| 134 | + bytes = new byte[1]; |
| 135 | + |
| 136 | + fixed (char* pChars = s) fixed (byte* pBytes = bytes) |
| 137 | + { |
| 138 | + return encoding.GetBytes(pChars + charIndex, charCount, pBytes + byteIndex, byteCount, encoder: null); |
| 139 | + } |
| 140 | + } |
| 141 | + |
| 142 | + [SecuritySafeCritical] |
| 143 | + public unsafe static int GetBytes(Encoding encoding, char[] chars, int charIndex, int charCount, byte[] bytes, int byteIndex) |
| 144 | + { |
| 145 | + Contract.Assert(encoding != null); |
| 146 | + if (chars == null || bytes == null) |
| 147 | + { |
| 148 | + throw new ArgumentNullException(chars == null ? "chars" : "bytes", Environment.GetResourceString("ArgumentNull_Array")); |
| 149 | + } |
| 150 | + if (charIndex < 0 || charCount < 0) |
| 151 | + { |
| 152 | + throw new ArgumentOutOfRangeException(charIndex < 0 ? "charIndex" : "charCount", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); |
| 153 | + } |
| 154 | + if (chars.Length - charIndex < charCount) |
| 155 | + { |
| 156 | + throw new ArgumentOutOfRangeException("chars", Environment.GetResourceString("ArgumentOutOfRange_IndexCountBuffer")); |
| 157 | + } |
| 158 | + if (byteIndex < 0 || byteIndex > bytes.Length) |
| 159 | + { |
| 160 | + throw new ArgumentOutOfRangeException("byteIndex", Environment.GetResourceString("ArgumentOutOfRange_Index")); |
| 161 | + } |
| 162 | + Contract.EndContractBlock(); |
| 163 | + |
| 164 | + // If nothing to encode return 0, avoid fixed problem |
| 165 | + if (charCount == 0) |
| 166 | + return 0; |
| 167 | + |
| 168 | + // Note that this is the # of bytes to decode, |
| 169 | + // not the size of the array |
| 170 | + int byteCount = bytes.Length - byteIndex; |
| 171 | + |
| 172 | + // Fixed doesn't like 0 length arrays. |
| 173 | + if (bytes.Length == 0) |
| 174 | + bytes = new byte[1]; |
| 175 | + |
| 176 | + // Just call the (internal) pointer version |
| 177 | + fixed (char* pChars = chars) fixed (byte* pBytes = bytes) |
| 178 | + { |
| 179 | + return encoding.GetBytes(pChars + charIndex, charCount, pBytes + byteIndex, byteCount, encoder: null); |
| 180 | + } |
| 181 | + } |
| 182 | + |
| 183 | + [SecurityCritical] |
| 184 | + public unsafe static int GetBytes(Encoding encoding, char* chars, int charCount, byte* bytes, int byteCount) |
| 185 | + { |
| 186 | + Contract.Assert(encoding != null); |
| 187 | + if (bytes == null || chars == null) |
| 188 | + { |
| 189 | + throw new ArgumentNullException(bytes == null ? "bytes" : "chars", Environment.GetResourceString("ArgumentNull_Array")); |
| 190 | + } |
| 191 | + if (charCount < 0 || byteCount < 0) |
| 192 | + { |
| 193 | + throw new ArgumentOutOfRangeException(charCount < 0 ? "charCount" : "byteCount", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); |
| 194 | + } |
| 195 | + Contract.EndContractBlock(); |
| 196 | + |
| 197 | + return encoding.GetBytes(chars, charCount, bytes, byteCount, encoder: null); |
| 198 | + } |
| 199 | + |
| 200 | + [SecuritySafeCritical] |
| 201 | + public unsafe static int GetCharCount(Encoding encoding, byte[] bytes, int index, int count) |
| 202 | + { |
| 203 | + Contract.Assert(encoding != null); |
| 204 | + if (bytes == null) |
| 205 | + { |
| 206 | + throw new ArgumentNullException("bytes", Environment.GetResourceString("ArgumentNull_Array")); |
| 207 | + } |
| 208 | + if (index < 0 || count < 0) |
| 209 | + { |
| 210 | + throw new ArgumentOutOfRangeException(index < 0 ? "index" : "count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); |
| 211 | + } |
| 212 | + if (bytes.Length - index < count) |
| 213 | + { |
| 214 | + throw new ArgumentOutOfRangeException("bytes", Environment.GetResourceString("ArgumentOutOfRange_IndexCountBuffer")); |
| 215 | + } |
| 216 | + Contract.EndContractBlock(); |
| 217 | + |
| 218 | + // If no input just return 0, fixed doesn't like 0 length arrays. |
| 219 | + if (count == 0) |
| 220 | + return 0; |
| 221 | + |
| 222 | + // Just call pointer version |
| 223 | + fixed (byte* pBytes = bytes) |
| 224 | + return encoding.GetCharCount(pBytes + index, count, decoder: null); |
| 225 | + } |
| 226 | + |
| 227 | + [SecurityCritical] |
| 228 | + public unsafe static int GetCharCount(Encoding encoding, byte* bytes, int count) |
| 229 | + { |
| 230 | + Contract.Assert(encoding != null); |
| 231 | + if (bytes == null) |
| 232 | + { |
| 233 | + throw new ArgumentNullException("bytes", Environment.GetResourceString("ArgumentNull_Array")); |
| 234 | + } |
| 235 | + if (count < 0) |
| 236 | + { |
| 237 | + throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); |
| 238 | + } |
| 239 | + Contract.EndContractBlock(); |
| 240 | + |
| 241 | + return encoding.GetCharCount(bytes, count, decoder: null); |
| 242 | + } |
| 243 | + |
| 244 | + [SecuritySafeCritical] |
| 245 | + public unsafe static int GetChars(Encoding encoding, byte[] bytes, int byteIndex, int byteCount, char[] chars, int charIndex) |
| 246 | + { |
| 247 | + Contract.Assert(encoding != null); |
| 248 | + if (bytes == null || chars == null) |
| 249 | + { |
| 250 | + throw new ArgumentNullException(bytes == null ? "bytes" : "chars", Environment.GetResourceString("ArgumentNull_Array")); |
| 251 | + } |
| 252 | + if (byteIndex < 0 || byteCount < 0) |
| 253 | + { |
| 254 | + throw new ArgumentOutOfRangeException(byteIndex < 0 ? "byteIndex" : "byteCount", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); |
| 255 | + } |
| 256 | + if (bytes.Length - byteIndex < byteCount) |
| 257 | + { |
| 258 | + throw new ArgumentOutOfRangeException("bytes", Environment.GetResourceString("ArgumentOutOfRange_IndexCountBuffer")); |
| 259 | + } |
| 260 | + if (charIndex < 0 || charIndex > chars.Length) |
| 261 | + { |
| 262 | + throw new ArgumentOutOfRangeException("charIndex", Environment.GetResourceString("ArgumentOutOfRange_Index")); |
| 263 | + } |
| 264 | + Contract.EndContractBlock(); |
| 265 | + |
| 266 | + if (byteCount == 0) |
| 267 | + return 0; |
| 268 | + |
| 269 | + // NOTE: This is the # of chars we can decode, |
| 270 | + // not the size of the array |
| 271 | + int charCount = chars.Length - charIndex; |
| 272 | + |
| 273 | + // Fixed doesn't like 0 length arrays. |
| 274 | + if (chars.Length == 0) |
| 275 | + chars = new char[1]; |
| 276 | + |
| 277 | + fixed (byte* pBytes = bytes) fixed (char* pChars = chars) |
| 278 | + { |
| 279 | + return encoding.GetChars(pBytes + byteIndex, byteCount, pChars + charIndex, charCount, decoder: null); |
| 280 | + } |
| 281 | + } |
| 282 | + |
| 283 | + [SecurityCritical] |
| 284 | + public unsafe static int GetChars(Encoding encoding, byte* bytes, int byteCount, char* chars, int charCount) |
| 285 | + { |
| 286 | + Contract.Assert(encoding != null); |
| 287 | + if (bytes == null || chars == null) |
| 288 | + { |
| 289 | + throw new ArgumentNullException(bytes == null ? "bytes" : "chars", Environment.GetResourceString("ArgumentNull_Array")); |
| 290 | + } |
| 291 | + if (charCount < 0 || byteCount < 0) |
| 292 | + { |
| 293 | + throw new ArgumentOutOfRangeException(charCount < 0 ? "charCount" : "byteCount", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); |
| 294 | + } |
| 295 | + Contract.EndContractBlock(); |
| 296 | + |
| 297 | + return encoding.GetChars(bytes, byteCount, chars, charCount, decoder: null); |
| 298 | + } |
| 299 | + |
| 300 | + [SecuritySafeCritical] |
| 301 | + public unsafe static string GetString(Encoding encoding, byte[] bytes, int index, int count) |
| 302 | + { |
| 303 | + Contract.Assert(encoding != null); |
| 304 | + if (bytes == null) |
| 305 | + { |
| 306 | + throw new ArgumentNullException("bytes", Environment.GetResourceString("ArgumentNull_Array")); |
| 307 | + } |
| 308 | + if (index < 0 || count < 0) |
| 309 | + { |
| 310 | + // ASCIIEncoding has different names for its parameters here (byteIndex, byteCount) |
| 311 | + bool ascii = encoding is ASCIIEncoding; |
| 312 | + string indexName = ascii ? "byteIndex" : "index"; |
| 313 | + string countName = ascii ? "byteCount" : "count"; |
| 314 | + throw new ArgumentOutOfRangeException(index < 0 ? indexName : countName, Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); |
| 315 | + } |
| 316 | + if (bytes.Length - index < count) |
| 317 | + { |
| 318 | + throw new ArgumentOutOfRangeException("bytes", Environment.GetResourceString("ArgumentOutOfRange_IndexCountBuffer")); |
| 319 | + } |
| 320 | + Contract.EndContractBlock(); |
| 321 | + |
| 322 | + // Avoid problems with empty input buffer |
| 323 | + if (count == 0) |
| 324 | + return string.Empty; |
| 325 | + |
| 326 | + // Call string.CreateStringFromEncoding here, which |
| 327 | + // allocates a string and lets the Encoding modify |
| 328 | + // it in place. This way, we don't have to allocate |
| 329 | + // an intermediary char[] to decode into and then |
| 330 | + // call the string constructor; instead we decode |
| 331 | + // directly into the string. |
| 332 | + |
| 333 | + fixed (byte* pBytes = bytes) |
| 334 | + { |
| 335 | + return string.CreateStringFromEncoding(pBytes + index, count, encoding); |
| 336 | + } |
| 337 | + } |
| 338 | + } |
| 339 | +} |
0 commit comments