Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Commit e74569c

Browse files
committed
Revert "Revert "Revert "Revert EncodingForwarder (#10805)" (#10824)""
This reverts commit c64139a.
1 parent c64139a commit e74569c

File tree

8 files changed

+559
-1723
lines changed

8 files changed

+559
-1723
lines changed

src/mscorlib/mscorlib.shared.sources.props

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -991,6 +991,7 @@
991991
<TextSources Include="$(BclSourcesRoot)\System\Text\EncoderFallback.cs" />
992992
<TextSources Include="$(BclSourcesRoot)\System\Text\EncoderReplacementFallback.cs" />
993993
<TextSources Include="$(BclSourcesRoot)\System\Text\Encoding.cs" />
994+
<TextSources Include="$(BclSourcesRoot)\System\Text\EncodingForwarder.cs" />
994995
<TextSources Include="$(BclSourcesRoot)\System\Text\EncodingInfo.cs" />
995996
<TextSources Include="$(BclSourcesRoot)\System\Text\EncodingNLS.cs" />
996997
<TextSources Include="$(BclSourcesRoot)\System\Text\EncodingProvider.cs" />

src/mscorlib/src/System/Text/ASCIIEncoding.cs

Lines changed: 38 additions & 290 deletions
Large diffs are not rendered by default.
Lines changed: 339 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,339 @@
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

Comments
 (0)