summaryrefslogtreecommitdiff
path: root/src/mscorlib/src/System/Text/EncodingForwarder.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/mscorlib/src/System/Text/EncodingForwarder.cs')
-rw-r--r--src/mscorlib/src/System/Text/EncodingForwarder.cs339
1 files changed, 339 insertions, 0 deletions
diff --git a/src/mscorlib/src/System/Text/EncodingForwarder.cs b/src/mscorlib/src/System/Text/EncodingForwarder.cs
new file mode 100644
index 0000000000..d4bcf800e3
--- /dev/null
+++ b/src/mscorlib/src/System/Text/EncodingForwarder.cs
@@ -0,0 +1,339 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Diagnostics.Contracts;
+using System.Security;
+
+namespace System.Text
+{
+ // Shared implementations for commonly overriden Encoding methods
+
+ internal static class EncodingForwarder
+ {
+ // We normally have to duplicate a lot of code between UTF8Encoding,
+ // UTF7Encoding, EncodingNLS, etc. because we want to override many
+ // of the methods in all of those classes to just forward to the unsafe
+ // version. (e.g. GetBytes(char[]))
+ // Ideally, everything would just derive from EncodingNLS, but that's
+ // not exposed in the public API, and C# prohibits a public class from
+ // inheriting from an internal one. So, we have to override each of the
+ // methods in question and repeat the argument validation/logic.
+
+ // These set of methods exist so instead of duplicating code, we can
+ // simply have those overriden methods call here to do the actual work.
+
+ // NOTE: This class should ONLY be called from Encodings that override
+ // the internal methods which accept an Encoder/DecoderNLS. The reason
+ // for this is that by default, those methods just call the same overload
+ // except without the encoder/decoder parameter. If an overriden method
+ // without that parameter calls this class, which calls the overload with
+ // the parameter, it will call the same method again, which will eventually
+ // lead to a StackOverflowException.
+
+ [SecuritySafeCritical]
+ public unsafe static int GetByteCount(Encoding encoding, char[] chars, int index, int count)
+ {
+ // Validate parameters
+
+ Contract.Assert(encoding != null); // this parameter should only be affected internally, so just do a debug check here
+ if (chars == null)
+ {
+ throw new ArgumentNullException("chars", Environment.GetResourceString("ArgumentNull_Array"));
+ }
+ if (index < 0 || count < 0)
+ {
+ throw new ArgumentOutOfRangeException(index < 0 ? "index" : "count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
+ }
+ if (chars.Length - index < count)
+ {
+ throw new ArgumentOutOfRangeException("chars", Environment.GetResourceString("ArgumentOutOfRange_IndexCountBuffer"));
+ }
+ Contract.EndContractBlock();
+
+ // If no input, return 0, avoid fixed empty array problem
+ if (count == 0)
+ return 0;
+
+ // Just call the (internal) pointer version
+ fixed (char* pChars = chars)
+ return encoding.GetByteCount(pChars + index, count, encoder: null);
+ }
+
+ [SecuritySafeCritical]
+ public unsafe static int GetByteCount(Encoding encoding, string s)
+ {
+ Contract.Assert(encoding != null);
+ if (s == null)
+ {
+ string paramName = encoding is ASCIIEncoding ? "chars" : "s"; // ASCIIEncoding calls the string chars
+ // UTF8Encoding does this as well, but it originally threw an ArgumentNull for "s" so don't check for that
+ throw new ArgumentNullException(paramName);
+ }
+ Contract.EndContractBlock();
+
+ // NOTE: The behavior of fixed *is* defined by
+ // the spec for empty strings, although not for
+ // null strings/empty char arrays. See
+ // http://stackoverflow.com/q/37757751/4077294
+ // Regardless, we may still want to check
+ // for if (s.Length == 0) in the future
+ // and short-circuit as an optimization (TODO).
+
+ fixed (char* pChars = s)
+ return encoding.GetByteCount(pChars, s.Length, encoder: null);
+ }
+
+ [SecurityCritical]
+ public unsafe static int GetByteCount(Encoding encoding, char* chars, int count)
+ {
+ Contract.Assert(encoding != null);
+ if (chars == null)
+ {
+ throw new ArgumentNullException("chars", Environment.GetResourceString("ArgumentNull_Array"));
+ }
+ if (count < 0)
+ {
+ throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
+ }
+ Contract.EndContractBlock();
+
+ // Call the internal version, with an empty encoder
+ return encoding.GetByteCount(chars, count, encoder: null);
+ }
+
+ [SecuritySafeCritical]
+ public unsafe static int GetBytes(Encoding encoding, string s, int charIndex, int charCount, byte[] bytes, int byteIndex)
+ {
+ Contract.Assert(encoding != null);
+ if (s == null || bytes == null)
+ {
+ string stringName = encoding is ASCIIEncoding ? "chars" : "s"; // ASCIIEncoding calls the first parameter chars
+ throw new ArgumentNullException(s == null ? stringName : "bytes", Environment.GetResourceString("ArgumentNull_Array"));
+ }
+ if (charIndex < 0 || charCount < 0)
+ {
+ throw new ArgumentOutOfRangeException(charIndex < 0 ? "charIndex" : "charCount", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
+ }
+ if (s.Length - charIndex < charCount)
+ {
+ string stringName = encoding is ASCIIEncoding ? "chars" : "s"; // ASCIIEncoding calls the first parameter chars
+ // Duplicate the above check since we don't want the overhead of a type check on the general path
+ throw new ArgumentOutOfRangeException(stringName, Environment.GetResourceString("ArgumentOutOfRange_IndexCount"));
+ }
+ if (byteIndex < 0 || byteIndex > bytes.Length)
+ {
+ throw new ArgumentOutOfRangeException("byteIndex", Environment.GetResourceString("ArgumentOutOfRange_Index"));
+ }
+ Contract.EndContractBlock();
+
+ int byteCount = bytes.Length - byteIndex;
+
+ // Fixed doesn't like empty arrays
+ if (bytes.Length == 0)
+ bytes = new byte[1];
+
+ fixed (char* pChars = s) fixed (byte* pBytes = bytes)
+ {
+ return encoding.GetBytes(pChars + charIndex, charCount, pBytes + byteIndex, byteCount, encoder: null);
+ }
+ }
+
+ [SecuritySafeCritical]
+ public unsafe static int GetBytes(Encoding encoding, char[] chars, int charIndex, int charCount, byte[] bytes, int byteIndex)
+ {
+ Contract.Assert(encoding != null);
+ if (chars == null || bytes == null)
+ {
+ throw new ArgumentNullException(chars == null ? "chars" : "bytes", Environment.GetResourceString("ArgumentNull_Array"));
+ }
+ if (charIndex < 0 || charCount < 0)
+ {
+ throw new ArgumentOutOfRangeException(charIndex < 0 ? "charIndex" : "charCount", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
+ }
+ if (chars.Length - charIndex < charCount)
+ {
+ throw new ArgumentOutOfRangeException("chars", Environment.GetResourceString("ArgumentOutOfRange_IndexCountBuffer"));
+ }
+ if (byteIndex < 0 || byteIndex > bytes.Length)
+ {
+ throw new ArgumentOutOfRangeException("byteIndex", Environment.GetResourceString("ArgumentOutOfRange_Index"));
+ }
+ Contract.EndContractBlock();
+
+ // If nothing to encode return 0, avoid fixed problem
+ if (charCount == 0)
+ return 0;
+
+ // Note that this is the # of bytes to decode,
+ // not the size of the array
+ int byteCount = bytes.Length - byteIndex;
+
+ // Fixed doesn't like 0 length arrays.
+ if (bytes.Length == 0)
+ bytes = new byte[1];
+
+ // Just call the (internal) pointer version
+ fixed (char* pChars = chars) fixed (byte* pBytes = bytes)
+ {
+ return encoding.GetBytes(pChars + charIndex, charCount, pBytes + byteIndex, byteCount, encoder: null);
+ }
+ }
+
+ [SecurityCritical]
+ public unsafe static int GetBytes(Encoding encoding, char* chars, int charCount, byte* bytes, int byteCount)
+ {
+ Contract.Assert(encoding != null);
+ if (bytes == null || chars == null)
+ {
+ throw new ArgumentNullException(bytes == null ? "bytes" : "chars", Environment.GetResourceString("ArgumentNull_Array"));
+ }
+ if (charCount < 0 || byteCount < 0)
+ {
+ throw new ArgumentOutOfRangeException(charCount < 0 ? "charCount" : "byteCount", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
+ }
+ Contract.EndContractBlock();
+
+ return encoding.GetBytes(chars, charCount, bytes, byteCount, encoder: null);
+ }
+
+ [SecuritySafeCritical]
+ public unsafe static int GetCharCount(Encoding encoding, byte[] bytes, int index, int count)
+ {
+ Contract.Assert(encoding != null);
+ if (bytes == null)
+ {
+ throw new ArgumentNullException("bytes", Environment.GetResourceString("ArgumentNull_Array"));
+ }
+ if (index < 0 || count < 0)
+ {
+ throw new ArgumentOutOfRangeException(index < 0 ? "index" : "count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
+ }
+ if (bytes.Length - index < count)
+ {
+ throw new ArgumentOutOfRangeException("bytes", Environment.GetResourceString("ArgumentOutOfRange_IndexCountBuffer"));
+ }
+ Contract.EndContractBlock();
+
+ // If no input just return 0, fixed doesn't like 0 length arrays.
+ if (count == 0)
+ return 0;
+
+ // Just call pointer version
+ fixed (byte* pBytes = bytes)
+ return encoding.GetCharCount(pBytes + index, count, decoder: null);
+ }
+
+ [SecurityCritical]
+ public unsafe static int GetCharCount(Encoding encoding, byte* bytes, int count)
+ {
+ Contract.Assert(encoding != null);
+ if (bytes == null)
+ {
+ throw new ArgumentNullException("bytes", Environment.GetResourceString("ArgumentNull_Array"));
+ }
+ if (count < 0)
+ {
+ throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
+ }
+ Contract.EndContractBlock();
+
+ return encoding.GetCharCount(bytes, count, decoder: null);
+ }
+
+ [SecuritySafeCritical]
+ public unsafe static int GetChars(Encoding encoding, byte[] bytes, int byteIndex, int byteCount, char[] chars, int charIndex)
+ {
+ Contract.Assert(encoding != null);
+ if (bytes == null || chars == null)
+ {
+ throw new ArgumentNullException(bytes == null ? "bytes" : "chars", Environment.GetResourceString("ArgumentNull_Array"));
+ }
+ if (byteIndex < 0 || byteCount < 0)
+ {
+ throw new ArgumentOutOfRangeException(byteIndex < 0 ? "byteIndex" : "byteCount", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
+ }
+ if (bytes.Length - byteIndex < byteCount)
+ {
+ throw new ArgumentOutOfRangeException("bytes", Environment.GetResourceString("ArgumentOutOfRange_IndexCountBuffer"));
+ }
+ if (charIndex < 0 || charIndex > chars.Length)
+ {
+ throw new ArgumentOutOfRangeException("charIndex", Environment.GetResourceString("ArgumentOutOfRange_Index"));
+ }
+ Contract.EndContractBlock();
+
+ if (byteCount == 0)
+ return 0;
+
+ // NOTE: This is the # of chars we can decode,
+ // not the size of the array
+ int charCount = chars.Length - charIndex;
+
+ // Fixed doesn't like 0 length arrays.
+ if (chars.Length == 0)
+ chars = new char[1];
+
+ fixed (byte* pBytes = bytes) fixed (char* pChars = chars)
+ {
+ return encoding.GetChars(pBytes + byteIndex, byteCount, pChars + charIndex, charCount, decoder: null);
+ }
+ }
+
+ [SecurityCritical]
+ public unsafe static int GetChars(Encoding encoding, byte* bytes, int byteCount, char* chars, int charCount)
+ {
+ Contract.Assert(encoding != null);
+ if (bytes == null || chars == null)
+ {
+ throw new ArgumentNullException(bytes == null ? "bytes" : "chars", Environment.GetResourceString("ArgumentNull_Array"));
+ }
+ if (charCount < 0 || byteCount < 0)
+ {
+ throw new ArgumentOutOfRangeException(charCount < 0 ? "charCount" : "byteCount", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
+ }
+ Contract.EndContractBlock();
+
+ return encoding.GetChars(bytes, byteCount, chars, charCount, decoder: null);
+ }
+
+ [SecuritySafeCritical]
+ public unsafe static string GetString(Encoding encoding, byte[] bytes, int index, int count)
+ {
+ Contract.Assert(encoding != null);
+ if (bytes == null)
+ {
+ throw new ArgumentNullException("bytes", Environment.GetResourceString("ArgumentNull_Array"));
+ }
+ if (index < 0 || count < 0)
+ {
+ // ASCIIEncoding has different names for its parameters here (byteIndex, byteCount)
+ bool ascii = encoding is ASCIIEncoding;
+ string indexName = ascii ? "byteIndex" : "index";
+ string countName = ascii ? "byteCount" : "count";
+ throw new ArgumentOutOfRangeException(index < 0 ? indexName : countName, Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
+ }
+ if (bytes.Length - index < count)
+ {
+ throw new ArgumentOutOfRangeException("bytes", Environment.GetResourceString("ArgumentOutOfRange_IndexCountBuffer"));
+ }
+ Contract.EndContractBlock();
+
+ // Avoid problems with empty input buffer
+ if (count == 0)
+ return string.Empty;
+
+ // Call string.CreateStringFromEncoding here, which
+ // allocates a string and lets the Encoding modify
+ // it in place. This way, we don't have to allocate
+ // an intermediary char[] to decode into and then
+ // call the string constructor; instead we decode
+ // directly into the string.
+
+ fixed (byte* pBytes = bytes)
+ {
+ return string.CreateStringFromEncoding(pBytes + index, count, encoding);
+ }
+ }
+ }
+}