// 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.Buffers;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
}
[Fact]
+ public static unsafe void Ctor_CharPtr_DoesNotAccessInvalidPage()
+ {
+ // Allocates a buffer of all 'x' followed by a null terminator,
+ // then attempts to create a string instance from this at various offsets.
+
+ const int MaxCharCount = 128;
+ using BoundedMemory<char> boundedMemory = BoundedMemory.Allocate<char>(MaxCharCount);
+ boundedMemory.Span.Fill('x');
+ boundedMemory.Span[MaxCharCount - 1] = '\0';
+ boundedMemory.MakeReadonly();
+
+ using MemoryHandle memoryHandle = boundedMemory.Memory.Pin();
+
+ for (int i = 0; i < MaxCharCount; i++)
+ {
+ string expectedString = new string('x', MaxCharCount - i - 1);
+ string actualString = new string((char*)memoryHandle.Pointer + i);
+ Assert.Equal(expectedString, actualString);
+ }
+ }
+
+ [ConditionalFact(nameof(IsSimpleActiveCodePage))]
+ public static unsafe void Ctor_SBytePtr_DoesNotAccessInvalidPage()
+ {
+ // Allocates a buffer of all ' ' followed by a null terminator,
+ // then attempts to create a string instance from this at various offsets.
+ // We use U+0020 SPACE instead of any other character because it lives
+ // at offset 0x20 across every supported code page.
+
+ const int MaxByteCount = 128;
+ using BoundedMemory<sbyte> boundedMemory = BoundedMemory.Allocate<sbyte>(MaxByteCount);
+ boundedMemory.Span.Fill((sbyte)' ');
+ boundedMemory.Span[MaxByteCount - 1] = (sbyte)'\0';
+ boundedMemory.MakeReadonly();
+
+ using MemoryHandle memoryHandle = boundedMemory.Memory.Pin();
+
+ for (int i = 0; i < MaxByteCount; i++)
+ {
+ string expectedString = new string(' ', MaxByteCount - i - 1);
+ string actualString = new string((sbyte*)memoryHandle.Pointer + i);
+ Assert.Equal(expectedString, actualString);
+ }
+ }
+
+ [Fact]
public static void Create_InvalidArguments_Throw()
{
AssertExtensions.Throws<ArgumentNullException>("action", () => string.Create(-1, 0, null));
range = new Range(Index.FromEnd(s.Length + 1), Index.FromEnd(0));
Assert.Throws<ArgumentOutOfRangeException>(() => s1 = s.Substring(range));
}
+
+ /// <summary>
+ /// Returns true only if U+0020 SPACE is represented as the single byte 0x20 in the active code page.
+ /// </summary>
+ public unsafe static bool IsSimpleActiveCodePage
+ {
+ get
+ {
+ IntPtr pAnsiStr = IntPtr.Zero;
+ try
+ {
+ pAnsiStr = Marshal.StringToHGlobalAnsi(" ");
+ return ((byte*)pAnsiStr)[0] == (byte)' ' && ((byte*)pAnsiStr)[1] == (byte)'\0';
+ }
+ finally
+ {
+ if (pAnsiStr != IntPtr.Zero)
+ {
+ Marshal.FreeHGlobal(pAnsiStr);
+ }
+ }
+ }
+ }
}
}