<FeatureInstantiatingStubAsIL Condition="'$(Platform)' != 'x86'">true</FeatureInstantiatingStubAsIL>
</PropertyGroup>
- <!-- Features we're currently flighting, but don't intend to ship in officially supported releases -->
- <PropertyGroup Condition="'$(IsPrerelease)' == 'true'">
- <FeatureUtf8String>true</FeatureUtf8String>
- <!-- FeatureXXX>true</FeatureXXX -->
- </PropertyGroup>
-
<PropertyGroup>
<DefineConstants Condition="'$(FeatureArrayStubAsIL)' == 'true'">$(DefineConstants);FEATURE_ARRAYSTUB_AS_IL</DefineConstants>
<DefineConstants Condition="'$(FeatureMulticastStubAsIL)' == 'true'">$(DefineConstants);FEATURE_MULTICASTSTUB_AS_IL</DefineConstants>
<DefineConstants Condition="'$(FeatureTypeEquivalence)' == 'true'">$(DefineConstants);FEATURE_TYPEEQUIVALENCE</DefineConstants>
<DefineConstants Condition="'$(FeatureBasicFreeze)' == 'true'">$(DefineConstants);FEATURE_BASICFREEZE</DefineConstants>
<DefineConstants Condition="'$(FeaturePortableShuffleThunks)' == 'true'">$(DefineConstants);FEATURE_PORTABLE_SHUFFLE_THUNKS</DefineConstants>
- <DefineConstants Condition="'$(FeatureUtf8String)' == 'true'">$(DefineConstants);FEATURE_UTF8STRING</DefineConstants>
<DefineConstants Condition="'$(ProfilingSupportedBuild)' == 'true'">$(DefineConstants);PROFILING_SUPPORTED</DefineConstants>
<DefineConstants Condition="'$(FeatureProfAttach)' == 'true'">$(DefineConstants);FEATURE_PROFAPI_ATTACH_DETACH</DefineConstants>
include(clrfeatures.cmake)
-# Features we're currently flighting, but don't intend to ship in officially supported releases
-if (PRERELEASE)
- add_definitions(-DFEATURE_UTF8STRING)
- # add_definitions(-DFEATURE_XXX)
-endif (PRERELEASE)
-
add_compile_definitions($<$<BOOL:$<TARGET_PROPERTY:DAC_COMPONENT>>:DACCESS_COMPILE>)
add_compile_definitions($<$<BOOL:$<TARGET_PROPERTY:CROSSGEN_COMPONENT>>:CROSSGEN_COMPILE>)
add_compile_definitions($<$<BOOL:$<TARGET_PROPERTY:CROSSGEN_COMPONENT>>:CROSS_COMPILE>)
<Compile Include="$(BclSourcesRoot)\Internal\Runtime\InteropServices\ComActivator.cs" />
<Compile Include="$(BclSourcesRoot)\Internal\Runtime\InteropServices\InMemoryAssemblyLoader.cs" />
</ItemGroup>
- <ItemGroup Condition="'$(FeatureUtf8String)' == 'true'">
- <Compile Include="$(BclSourcesRoot)\System\Utf8String.CoreCLR.cs" />
- </ItemGroup>
<ItemGroup Condition="'$(FeatureComWrappers)' == 'true'">
<Compile Include="$(BclSourcesRoot)\System\Runtime\InteropServices\ComWrappers.cs" />
</ItemGroup>
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System.ComponentModel;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-using System.Text.Unicode;
-using Internal.Runtime.CompilerServices;
-
-namespace System
-{
- public sealed partial class Utf8String
- {
- /*
- * INSTANCE FIELDS
- * Do not reorder these fields. They must match the layout of Utf8StringObject in object.h.
- */
-
- private readonly int _length;
- private readonly byte _firstByte;
-
- /*
- * INSTANCE PROPERTIES
- */
-
- /// <summary>
- /// Returns the length (in UTF-8 code units, or <see cref="byte"/>s) of this instance.
- /// </summary>
- public int Length => _length;
-
- /*
- * CONSTRUCTORS
- *
- * Defining a new constructor for string-like types (like Utf8String) requires changes both
- * to the managed code below and to the native VM code. See the comment at the top of
- * src/vm/ecall.cpp for instructions on how to add new overloads.
- *
- * These ctors validate their input, throwing ArgumentException if the input does not represent
- * well-formed UTF-8 data. (In the case of transcoding ctors, the ctors throw if the input does
- * not represent well-formed UTF-16 data.) There are Create* factory methods which allow the caller
- * to control this behavior with finer granularity, including performing U+FFFD replacement instead
- * of throwing, or even suppressing validation altogether if the caller knows the input to be well-
- * formed.
- *
- * The reason a throwing behavior was chosen by default is that we don't want to surprise developers
- * if ill-formed data loses fidelity while being round-tripped through this type. Developers should
- * perform an explicit gesture to opt-in to lossy behavior, such as calling the factories explicitly
- * documented as performing such replacement.
- */
-
- /// <summary>
- /// Creates a <see cref="Utf8String"/> instance from existing UTF-8 data.
- /// </summary>
- /// <param name="value">The existing UTF-8 data from which to create a new <see cref="Utf8String"/>.</param>
- /// <exception cref="ArgumentException">
- /// Thrown if <paramref name="value"/> does not represent well-formed UTF-8 data.
- /// </exception>
- /// <remarks>
- /// The UTF-8 data in <paramref name="value"/> is validated for well-formedness upon construction,
- /// and an exception is thrown if the input is ill-formed. To avoid this exception, consider using
- /// <see cref="TryCreateFrom(ReadOnlySpan{byte}, out Utf8String)"/> or <see cref="CreateFromRelaxed(ReadOnlySpan{byte})"/>.
- /// </remarks>
- [MethodImpl(MethodImplOptions.InternalCall)]
- public extern Utf8String(ReadOnlySpan<byte> value);
-
-#pragma warning disable CA1822 // Mark members as static
-
-#if !CORECLR
- static
-#endif
- private Utf8String Ctor(ReadOnlySpan<byte> value)
- {
- if (value.IsEmpty)
- {
- return Empty;
- }
-
- // Create and populate the Utf8String instance.
-
- Utf8String newString = FastAllocateSkipZeroInit(value.Length);
- Buffer.Memmove(ref newString.DangerousGetMutableReference(), ref MemoryMarshal.GetReference(value), (uint)value.Length);
-
- // Now perform validation.
- // Reminder: Perform validation over the copy, not over the source.
-
- if (!Utf8Utility.IsWellFormedUtf8(newString.AsBytes()))
- {
- throw new ArgumentException(
- message: SR.Utf8String_InputContainedMalformedUtf8,
- paramName: nameof(value));
- }
-
- return newString;
- }
-
- /// <summary>
- /// Creates a <see cref="Utf8String"/> instance from existing UTF-8 data.
- /// </summary>
- /// <exception cref="ArgumentException">
- /// Thrown if <paramref name="value"/> does not represent well-formed UTF-8 data.
- /// </exception>
- /// <remarks>
- /// The UTF-8 data in <paramref name="value"/> is validated for well-formedness upon construction,
- /// and an exception is thrown if the input is ill-formed. To avoid this exception, consider using
- /// <see cref="TryCreateFrom(ReadOnlySpan{byte}, out Utf8String)"/> or <see cref="CreateFromRelaxed(ReadOnlySpan{byte})"/>.
- /// </remarks>
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- public extern Utf8String(byte[] value, int startIndex, int length);
-
-#if !CORECLR
- static
-#endif
- private Utf8String Ctor(byte[] value, int startIndex, int length)
- {
- if (value is null)
- {
- ThrowHelper.ThrowArgumentNullException(ExceptionArgument.value);
- }
-
- return Ctor(new ReadOnlySpan<byte>(value, startIndex, length));
- }
-
- /// <summary>
- /// Creates a <see cref="Utf8String"/> instance from existing null-terminated UTF-8 data.
- /// </summary>
- /// <exception cref="ArgumentException">
- /// Thrown if <paramref name="value"/> does not represent well-formed UTF-8 data.
- /// </exception>
- /// <remarks>
- /// The UTF-8 data in <paramref name="value"/> is validated for well-formedness upon construction,
- /// and an exception is thrown if the input is ill-formed. To avoid this exception, consider using
- /// <see cref="TryCreateFrom(ReadOnlySpan{byte}, out Utf8String)"/> or <see cref="CreateFromRelaxed(ReadOnlySpan{byte})"/>.
- /// </remarks>
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- [CLSCompliant(false)]
- public extern unsafe Utf8String(byte* value);
-
-#if !CORECLR
- static
-#endif
- private unsafe Utf8String Ctor(byte* value)
- {
- if (value is null)
- {
- ThrowHelper.ThrowArgumentNullException(ExceptionArgument.value);
- }
-
- return Ctor(new ReadOnlySpan<byte>(value, string.strlen(value)));
- }
-
- /// <summary>
- /// Creates a <see cref="Utf8String"/> instance from existing UTF-16 data, transcoding the
- /// existing data to UTF-8 upon creation.
- /// </summary>
- /// <param name="value">The existing UTF-16 data from which to create a new <see cref="Utf8String"/>.</param>
- /// <exception cref="ArgumentException">
- /// Thrown if <paramref name="value"/> does not represent well-formed UTF-16 data.
- /// </exception>
- /// <remarks>
- /// The UTF-16 data in <paramref name="value"/> is validated for well-formedness upon construction,
- /// and an exception is thrown if the input is ill-formed. To avoid this exception, consider using
- /// <see cref="TryCreateFrom(ReadOnlySpan{char}, out Utf8String)"/> or <see cref="CreateFromRelaxed(ReadOnlySpan{char})"/>.
- /// </remarks>
- [MethodImpl(MethodImplOptions.InternalCall)]
- public extern Utf8String(ReadOnlySpan<char> value);
-
-#if !CORECLR
- static
-#endif
- private Utf8String Ctor(ReadOnlySpan<char> value)
- {
- Utf8String? newString = CreateFromUtf16Common(value, replaceInvalidSequences: false);
-
- if (newString is null)
- {
- // Input buffer contained invalid UTF-16 data.
-
- throw new ArgumentException(
- message: SR.Utf8String_InputContainedMalformedUtf16,
- paramName: nameof(value));
- }
-
- return newString;
- }
-
- /// <summary>
- /// Creates a <see cref="Utf8String"/> instance from existing UTF-16 data.
- /// </summary>
- /// <exception cref="ArgumentException">
- /// Thrown if <paramref name="value"/> does not represent well-formed UTF-16 data.
- /// </exception>
- /// <remarks>
- /// The UTF-16 data in <paramref name="value"/> is validated for well-formedness upon construction,
- /// and an exception is thrown if the input is ill-formed. To avoid this exception, consider using
- /// <see cref="TryCreateFrom(ReadOnlySpan{char}, out Utf8String)"/> or <see cref="CreateFromRelaxed(ReadOnlySpan{char})"/>.
- /// </remarks>
- [MethodImpl(MethodImplOptions.InternalCall)]
- public extern Utf8String(char[] value, int startIndex, int length);
-
-#if !CORECLR
- static
-#endif
- private Utf8String Ctor(char[] value, int startIndex, int length)
- {
- if (value is null)
- {
- ThrowHelper.ThrowArgumentNullException(ExceptionArgument.value);
- }
-
- return Ctor(new ReadOnlySpan<char>(value, startIndex, length));
- }
-
- /// <summary>
- /// Creates a <see cref="Utf8String"/> instance from existing null-terminated UTF-16 data.
- /// </summary>
- /// <exception cref="ArgumentException">
- /// Thrown if <paramref name="value"/> does not represent well-formed UTF-16 data.
- /// </exception>
- /// <remarks>
- /// The UTF-16 data in <paramref name="value"/> is validated for well-formedness upon construction,
- /// and an exception is thrown if the input is ill-formed. To avoid this exception, consider using
- /// <see cref="TryCreateFrom(ReadOnlySpan{char}, out Utf8String)"/> or <see cref="CreateFromRelaxed(ReadOnlySpan{char})"/>.
- /// </remarks>
- [MethodImpl(MethodImplOptions.InternalCall)]
- [CLSCompliant(false)]
- public extern unsafe Utf8String(char* value);
-
-#if !CORECLR
- static
-#endif
- private unsafe Utf8String Ctor(char* value)
- {
- if (value == null)
- {
- ThrowHelper.ThrowArgumentNullException(ExceptionArgument.value);
- }
-
- return Ctor(new ReadOnlySpan<char>(value, string.wcslen(value)));
- }
-
- /// <summary>
- /// Creates a <see cref="Utf8String"/> instance from existing UTF-16 data.
- /// </summary>
- /// <remarks>
- /// The UTF-16 data in <paramref name="value"/> is validated for well-formedness upon construction,
- /// and an exception is thrown if the input is ill-formed. To avoid this exception, consider using
- /// <see cref="TryCreateFrom(ReadOnlySpan{char}, out Utf8String)"/> or <see cref="CreateFromRelaxed(ReadOnlySpan{char})"/>.
- /// </remarks>
- [MethodImpl(MethodImplOptions.InternalCall)]
- public extern Utf8String(string value);
-
-#if !CORECLR
- static
-#endif
- private Utf8String Ctor(string value)
- {
- if (value is null)
- {
- ThrowHelper.ThrowArgumentNullException(ExceptionArgument.value);
- }
-
- return Ctor(value.AsSpan());
- }
-
-#pragma warning restore CA1822
-
- /*
- * METHODS
- */
-
- /// <summary>
- /// Similar to <see cref="Utf8Extensions.AsBytes(Utf8String)"/>, but skips the null check on the input.
- /// Throws a <see cref="NullReferenceException"/> if the input is null.
- /// </summary>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- internal ReadOnlySpan<byte> AsBytesSkipNullCheck()
- {
- // By dereferencing Length first, the JIT will skip the null check that normally precedes
- // most instance method calls, and it'll use the field dereference as the null check.
-
- int length = Length;
- return new ReadOnlySpan<byte>(ref DangerousGetMutableReference(), length);
- }
-
- /// <summary>
- /// Returns a <em>mutable</em> <see cref="Span{Byte}"/> that can be used to populate this
- /// <see cref="Utf8String"/> instance. Only to be used during construction.
- /// </summary>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- internal Span<byte> DangerousGetMutableSpan()
- {
- // By dereferencing Length first, the JIT will skip the null check that normally precedes
- // most instance method calls, and it'll use the field dereference as the null check.
-
- int length = Length;
- return new Span<byte>(ref DangerousGetMutableReference(), length);
- }
-
- /// <summary>
- /// Returns a <em>mutable</em> reference to the first byte of this <see cref="Utf8String"/>
- /// (or the null terminator if the string is empty).
- /// </summary>
- /// <returns></returns>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- internal ref byte DangerousGetMutableReference() => ref Unsafe.AsRef(in _firstByte);
-
- /// <summary>
- /// Gets an immutable reference that can be used in a <see langword="fixed"/> statement. The resulting
- /// reference can be pinned and used as a null-terminated <em>LPCUTF8STR</em>.
- /// </summary>
- /// <remarks>
- /// If this <see cref="Utf8String"/> instance is empty, returns a reference to the null terminator.
- /// </remarks>
- [EditorBrowsable(EditorBrowsableState.Never)] // for compiler use only
- public ref readonly byte GetPinnableReference() => ref _firstByte;
-
- /*
- * HELPER METHODS
- */
-
- /// <summary>
- /// Creates a new zero-initialized instance of the specified length. Actual storage allocated is "length + 1" bytes
- /// because instances are null-terminated.
- /// </summary>
- /// <remarks>
- /// The implementation of this method checks its input argument for overflow.
- /// </remarks>
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern Utf8String FastAllocate(int length);
- }
-}
// assert that String has overloaded the Clone() method
_ASSERTE(pMT != g_pStringClass);
-#ifdef FEATURE_UTF8STRING
- _ASSERTE(pMT != g_pUtf8StringClass);
-#endif // FEATURE_UTF8STRING
if (pMT->IsArray()) {
refClone = DupArrayForCloning((BASEARRAYREF)refClone);
DEFINE_DACVAR(ULONG, UNKNOWN_POINTER_TYPE, dac__g_pRuntimeTypeClass, ::g_pRuntimeTypeClass)
DEFINE_DACVAR(ULONG, UNKNOWN_POINTER_TYPE, dac__g_pCanonMethodTableClass, ::g_pCanonMethodTableClass)
DEFINE_DACVAR(ULONG, UNKNOWN_POINTER_TYPE, dac__g_pStringClass, ::g_pStringClass)
-#ifdef FEATURE_UTF8STRING
-DEFINE_DACVAR(ULONG, UNKNOWN_POINTER_TYPE, dac__g_pUtf8StringClass, ::g_pUtf8StringClass)
-#endif // FEATURE_UTF8STRING
DEFINE_DACVAR(ULONG, UNKNOWN_POINTER_TYPE, dac__g_pArrayClass, ::g_pArrayClass)
DEFINE_DACVAR(ULONG, UNKNOWN_POINTER_TYPE, dac__g_pSZArrayHelperClass, ::g_pSZArrayHelperClass)
DEFINE_DACVAR(ULONG, UNKNOWN_POINTER_TYPE, dac__g_pNullableClass, ::g_pNullableClass)
// Load String
g_pStringClass = CoreLibBinder::LoadPrimitiveType(ELEMENT_TYPE_STRING);
-#ifdef FEATURE_UTF8STRING
- // Load Utf8String
- g_pUtf8StringClass = CoreLibBinder::GetClass(CLASS__UTF8_STRING);
-#endif // FEATURE_UTF8STRING
-
#ifndef CROSSGEN_COMPILE
ECall::PopulateManagedStringConstructors();
#endif // CROSSGEN_COMPILE
#define g_ThreadClassName "System.Threading.Thread"
#define g_TypeClassName "System.Type"
-#ifdef FEATURE_UTF8STRING
-#define g_Utf8StringName "Utf8String"
-#endif // FEATURE_UTF8STRING
-
#define g_VariantClassName "System.Variant"
#define g_GuidClassName "System.Guid"
typedef DPTR(struct ReJitInfo) PTR_ReJitInfo;
typedef DPTR(struct SharedReJitInfo) PTR_SharedReJitInfo;
typedef DPTR(class StringObject) PTR_StringObject;
-#ifdef FEATURE_UTF8STRING
-typedef DPTR(class Utf8StringObject) PTR_Utf8StringObject;
-#endif // FEATURE_UTF8STRING
typedef DPTR(class TypeHandle) PTR_TypeHandle;
typedef VPTR(class VirtualCallStubManager) PTR_VirtualCallStubManager;
typedef VPTR(class VirtualCallStubManagerManager) PTR_VirtualCallStubManagerManager;
DEFINE_CLASS(RUNE, Text, Rune)
-#ifdef FEATURE_UTF8STRING
-DEFINE_CLASS(CHAR8, System, Char8)
-#endif // FEATURE_UTF8STRING
-
DEFINE_CLASS(ENUM, System, Enum)
DEFINE_CLASS(ENVIRONMENT, System, Environment)
DEFINE_METHOD(STRING, STRLEN, strlen, SM_PtrByte_RetInt)
DEFINE_PROPERTY(STRING, LENGTH, Length, Int)
-#ifdef FEATURE_UTF8STRING
-DEFINE_CLASS(UTF8_STRING, System, Utf8String)
-DEFINE_METHOD(UTF8_STRING, CTORF_READONLYSPANOFBYTE,Ctor, IM_ReadOnlySpanOfByte_RetUtf8Str)
-DEFINE_METHOD(UTF8_STRING, CTORF_READONLYSPANOFCHAR,Ctor, IM_ReadOnlySpanOfChar_RetUtf8Str)
-DEFINE_METHOD(UTF8_STRING, CTORF_BYTEARRAY_START_LEN,Ctor, IM_ArrByte_Int_Int_RetUtf8Str)
-DEFINE_METHOD(UTF8_STRING, CTORF_BYTEPTR, Ctor, IM_PtrByte_RetUtf8Str)
-DEFINE_METHOD(UTF8_STRING, CTORF_CHARARRAY_START_LEN,Ctor, IM_ArrChar_Int_Int_RetUtf8Str)
-DEFINE_METHOD(UTF8_STRING, CTORF_CHARPTR, Ctor, IM_PtrChar_RetUtf8Str)
-DEFINE_METHOD(UTF8_STRING, CTORF_STRING, Ctor, IM_String_RetUtf8Str)
-#endif // FEATURE_UTF8STRING
-
DEFINE_CLASS(STRING_BUILDER, Text, StringBuilder)
DEFINE_PROPERTY(STRING_BUILDER, LENGTH, Length, Int)
DEFINE_PROPERTY(STRING_BUILDER, CAPACITY, Capacity, Int)
#define NumberOfStringConstructors 9
-#ifdef FEATURE_UTF8STRING
-// METHOD__UTF8STRING__CTORF_XXX has to be in same order as ECall::Utf8StringCtorCharXxx
-#define METHOD__UTF8STRING__CTORF_FIRST METHOD__UTF8_STRING__CTORF_READONLYSPANOFBYTE
-static_assert_no_msg(METHOD__UTF8STRING__CTORF_FIRST + 0 == METHOD__UTF8_STRING__CTORF_READONLYSPANOFBYTE);
-static_assert_no_msg(METHOD__UTF8STRING__CTORF_FIRST + 1 == METHOD__UTF8_STRING__CTORF_READONLYSPANOFCHAR);
-static_assert_no_msg(METHOD__UTF8STRING__CTORF_FIRST + 2 == METHOD__UTF8_STRING__CTORF_BYTEARRAY_START_LEN);
-static_assert_no_msg(METHOD__UTF8STRING__CTORF_FIRST + 3 == METHOD__UTF8_STRING__CTORF_BYTEPTR);
-static_assert_no_msg(METHOD__UTF8STRING__CTORF_FIRST + 4 == METHOD__UTF8_STRING__CTORF_CHARARRAY_START_LEN);
-static_assert_no_msg(METHOD__UTF8STRING__CTORF_FIRST + 5 == METHOD__UTF8_STRING__CTORF_CHARPTR);
-static_assert_no_msg(METHOD__UTF8STRING__CTORF_FIRST + 6 == METHOD__UTF8_STRING__CTORF_STRING);
-
-// ECall::Utf8StringCtorCharXxx has to be in same order as METHOD__UTF8STRING__CTORF_XXX
-#define ECallUtf8String_Ctor_First ECall::Utf8StringCtorReadOnlySpanOfByteManaged
-static_assert_no_msg(ECallUtf8String_Ctor_First + 0 == ECall::Utf8StringCtorReadOnlySpanOfByteManaged);
-static_assert_no_msg(ECallUtf8String_Ctor_First + 1 == ECall::Utf8StringCtorReadOnlySpanOfCharManaged);
-static_assert_no_msg(ECallUtf8String_Ctor_First + 2 == ECall::Utf8StringCtorByteArrayStartLengthManaged);
-static_assert_no_msg(ECallUtf8String_Ctor_First + 3 == ECall::Utf8StringCtorBytePtrManaged);
-static_assert_no_msg(ECallUtf8String_Ctor_First + 4 == ECall::Utf8StringCtorCharArrayStartLengthManaged);
-static_assert_no_msg(ECallUtf8String_Ctor_First + 5 == ECall::Utf8StringCtorCharPtrManaged);
-static_assert_no_msg(ECallUtf8String_Ctor_First + 6 == ECall::Utf8StringCtorStringManaged);
-
-#define NumberOfUtf8StringConstructors 7
-#endif // FEATURE_UTF8STRING
-
void ECall::PopulateManagedStringConstructors()
{
STANDARD_VM_CONTRACT;
ECall::DynamicallyAssignFCallImpl(pDest, ECallCtor_First + i);
}
-#ifdef FEATURE_UTF8STRING
- _ASSERTE(g_pUtf8StringClass != NULL);
- for (int i = 0; i < NumberOfUtf8StringConstructors; i++)
- {
- MethodDesc* pMD = CoreLibBinder::GetMethod((BinderMethodID)(METHOD__UTF8STRING__CTORF_FIRST + i));
- _ASSERTE(pMD != NULL);
-
- PCODE pDest = pMD->GetMultiCallableAddrOfCode();
-
- ECall::DynamicallyAssignFCallImpl(pDest, ECallUtf8String_Ctor_First + i);
- }
-#endif // FEATURE_UTF8STRING
-
INDEBUG(fInitialized = true);
}
DYNAMICALLY_ASSIGNED_FCALL_IMPL(CtorSBytePtrStartLengthEncodingManaged, NULL) \
DYNAMICALLY_ASSIGNED_FCALL_IMPL(InternalGetCurrentThread, NULL) \
-#define _DYNAMICALLY_ASSIGNED_FCALLS_UTF8STRING() \
- DYNAMICALLY_ASSIGNED_FCALL_IMPL(FastAllocateUtf8String, FramedAllocateUtf8String) \
- DYNAMICALLY_ASSIGNED_FCALL_IMPL(Utf8StringCtorReadOnlySpanOfByteManaged, NULL) \
- DYNAMICALLY_ASSIGNED_FCALL_IMPL(Utf8StringCtorReadOnlySpanOfCharManaged, NULL) \
- DYNAMICALLY_ASSIGNED_FCALL_IMPL(Utf8StringCtorByteArrayStartLengthManaged, NULL) \
- DYNAMICALLY_ASSIGNED_FCALL_IMPL(Utf8StringCtorBytePtrManaged, NULL) \
- DYNAMICALLY_ASSIGNED_FCALL_IMPL(Utf8StringCtorCharArrayStartLengthManaged, NULL) \
- DYNAMICALLY_ASSIGNED_FCALL_IMPL(Utf8StringCtorCharPtrManaged, NULL) \
- DYNAMICALLY_ASSIGNED_FCALL_IMPL(Utf8StringCtorStringManaged, NULL) \
-
-#ifdef FEATURE_UTF8STRING
-#define DYNAMICALLY_ASSIGNED_FCALLS() _DYNAMICALLY_ASSIGNED_FCALLS_BASE() _DYNAMICALLY_ASSIGNED_FCALLS_UTF8STRING()
-#else
#define DYNAMICALLY_ASSIGNED_FCALLS() _DYNAMICALLY_ASSIGNED_FCALLS_BASE()
-#endif // FEATURE_UTF8STRING
enum
{
FCFuncElement("Intern", AppDomainNative::GetOrInternString)
FCFuncEnd()
-#ifdef FEATURE_UTF8STRING
-FCFuncStart(gUtf8StringFuncs)
- FCDynamic("FastAllocate", CORINFO_INTRINSIC_Illegal, ECall::FastAllocateUtf8String)
- FCDynamicSig(COR_CTOR_METHOD_NAME, &gsig_IM_ReadOnlySpanOfByte_RetVoid, CORINFO_INTRINSIC_Illegal, ECall::Utf8StringCtorReadOnlySpanOfByteManaged)
- FCDynamicSig(COR_CTOR_METHOD_NAME, &gsig_IM_ReadOnlySpanOfChar_RetVoid, CORINFO_INTRINSIC_Illegal, ECall::Utf8StringCtorReadOnlySpanOfCharManaged)
- FCDynamicSig(COR_CTOR_METHOD_NAME, &gsig_IM_ArrByte_Int_Int_RetVoid, CORINFO_INTRINSIC_Illegal, ECall::Utf8StringCtorByteArrayStartLengthManaged)
- FCDynamicSig(COR_CTOR_METHOD_NAME, &gsig_IM_PtrByte_RetVoid, CORINFO_INTRINSIC_Illegal, ECall::Utf8StringCtorBytePtrManaged)
- FCDynamicSig(COR_CTOR_METHOD_NAME, &gsig_IM_ArrChar_Int_Int_RetVoid, CORINFO_INTRINSIC_Illegal, ECall::Utf8StringCtorCharArrayStartLengthManaged)
- FCDynamicSig(COR_CTOR_METHOD_NAME, &gsig_IM_PtrChar_RetVoid, CORINFO_INTRINSIC_Illegal, ECall::Utf8StringCtorCharPtrManaged)
- FCDynamicSig(COR_CTOR_METHOD_NAME, &gsig_IM_Str_RetVoid, CORINFO_INTRINSIC_Illegal, ECall::Utf8StringCtorStringManaged)
-FCFuncEnd()
-#endif // FEATURE_UTF8STRING
-
FCFuncStart(gValueTypeFuncs)
FCFuncElement("CanCompareBits", ValueTypeHelper::CanCompareBits)
FCFuncElement("FastEqualsCheck", ValueTypeHelper::FastEqualsCheck)
FCClassElement("TypeNameParser", "System", gTypeNameParser)
FCClassElement("TypedReference", "System", gTypedReferenceFuncs)
FCClassElement("UnmanagedThreadPoolWorkItem", "System.Threading", gUnmanagedThreadPoolWorkItemFuncs)
-#ifdef FEATURE_UTF8STRING
-FCClassElement("Utf8String", "System", gUtf8StringFuncs)
-#endif // FEATURE_UTF8STRING
FCClassElement("ValueType", "System", gValueTypeFuncs)
#ifdef FEATURE_COMINTEROP
FCClassElement("Variant", "System", gVariantFuncs)
return ObjectToSTRINGREF(orString);
}
-#ifdef FEATURE_UTF8STRING
-UTF8STRINGREF AllocateUtf8String(DWORD cchStringLength)
-{
- CONTRACTL{
- THROWS;
- GC_TRIGGERS;
- MODE_COOPERATIVE; // returns an objref without pinning it => cooperative
- } CONTRACTL_END;
-
-#ifdef _DEBUG
- if (g_pConfig->ShouldInjectFault(INJECTFAULT_GCHEAP))
- {
- char *a = new char;
- delete a;
- }
-#endif
-
- // Limit the maximum string size to <2GB to mitigate risk of security issues caused by 32-bit integer
- // overflows in buffer size calculations.
- //
- // 0x7FFFFFBF is derived from the const 0x3FFFFFDF in SlowAllocateString.
- // Adding +1 (for null terminator) and multiplying by sizeof(WCHAR) means that
- // SlowAllocateString allows a maximum of 0x7FFFFFC0 bytes to be used for the
- // string data itself, with some additional buffer for object headers and other
- // data. Since we don't have the sizeof(WCHAR) multiplication here, we only need
- // -1 to account for the null terminator, leading to a max size of 0x7FFFFFBF.
- if (cchStringLength > 0x7FFFFFBF)
- ThrowOutOfMemory();
-
- SIZE_T totalSize = PtrAlign(Utf8StringObject::GetSize(cchStringLength));
- _ASSERTE(totalSize > cchStringLength);
-
- SetTypeHandleOnThreadForAlloc(TypeHandle(g_pUtf8StringClass));
-
- GC_ALLOC_FLAGS flags = GC_ALLOC_NO_FLAGS;
- if (totalSize >= g_pConfig->GetGCLOHThreshold())
- flags |= GC_ALLOC_LARGE_OBJECT_HEAP;
-
- Utf8StringObject* orString = (Utf8StringObject*)Alloc(totalSize, flags);
-
- // Initialize Object
- orString->SetMethodTable(g_pUtf8StringClass);
- orString->SetLength(cchStringLength);
-
- PublishObjectAndNotify(orString, flags);
- return ObjectToUTF8STRINGREF(orString);
-}
-#endif // FEATURE_UTF8STRING
-
#ifdef FEATURE_COMINTEROP_UNMANAGED_ACTIVATION
// OBJECTREF AllocateComClassObject(ComClassFactory* pComClsFac)
void AllocateComClassObject(ComClassFactory* pComClsFac, OBJECTREF* ppRefClass)
// Allocate a string
STRINGREF AllocateString( DWORD cchStringLength );
-#ifdef FEATURE_UTF8STRING
-UTF8STRINGREF AllocateUtf8String( DWORD cchStringLength );
-#endif // FEATURE_UTF8STRING
-
OBJECTREF DupArrayForCloning(BASEARRAYREF pRef);
// The JIT requests the EE to specify an allocation helper to use at each new-site.
}
HCIMPLEND
-#ifdef FEATURE_UTF8STRING
-HCIMPL1(Utf8StringObject*, AllocateUtf8String_MP_FastPortable, DWORD stringLength)
-{
- FCALL_CONTRACT;
-
- do
- {
- _ASSERTE(GCHeapUtilities::UseThreadAllocationContexts());
-
- // Instead of doing elaborate overflow checks, we just limit the number of elements. This will avoid all overflow
- // problems, as well as making sure big string objects are correctly allocated in the big object heap.
- if (stringLength >= LARGE_OBJECT_SIZE - 256)
- {
- break;
- }
-
- // This is typically the only call in the fast path. Making the call early seems to be better, as it allows the compiler
- // to use volatile registers for intermediate values. This reduces the number of push/pop instructions and eliminates
- // some reshuffling of intermediate values into nonvolatile registers around the call.
- Thread *thread = GetThread();
-
- SIZE_T totalSize = Utf8StringObject::GetSize(stringLength);
-
- // The method table's base size includes space for a terminating null character
- _ASSERTE(totalSize >= g_pUtf8StringClass->GetBaseSize());
- _ASSERTE(totalSize - g_pUtf8StringClass->GetBaseSize() == stringLength);
-
- SIZE_T alignedTotalSize = ALIGN_UP(totalSize, DATA_ALIGNMENT);
- _ASSERTE(alignedTotalSize >= totalSize);
- totalSize = alignedTotalSize;
-
- gc_alloc_context *allocContext = thread->GetAllocContext();
- BYTE *allocPtr = allocContext->alloc_ptr;
- _ASSERTE(allocPtr <= allocContext->alloc_limit);
- if (totalSize > static_cast<SIZE_T>(allocContext->alloc_limit - allocPtr))
- {
- break;
- }
- allocContext->alloc_ptr = allocPtr + totalSize;
-
- _ASSERTE(allocPtr != nullptr);
- Utf8StringObject *stringObject = reinterpret_cast<Utf8StringObject *>(allocPtr);
- stringObject->SetMethodTable(g_pUtf8StringClass);
- stringObject->SetLength(stringLength);
-
- return stringObject;
- } while (false);
-
- // Tail call to the slow helper
- ENDFORBIDGC();
- return HCCALL1(FramedAllocateUtf8String, stringLength);
-}
-HCIMPLEND
-#endif // FEATURE_UTF8STRING
-
#include <optdefault.h>
/*********************************************************************/
}
HCIMPLEND
-#ifdef FEATURE_UTF8STRING
-HCIMPL1(Utf8StringObject*, FramedAllocateUtf8String, DWORD stringLength)
-{
- FCALL_CONTRACT;
-
- UTF8STRINGREF result = NULL;
- HELPER_METHOD_FRAME_BEGIN_RET_0(); // Set up a frame
-
- result = AllocateUtf8String(stringLength);
-
- HELPER_METHOD_FRAME_END();
- return((Utf8StringObject*) OBJECTREFToObject(result));
-}
-HCIMPLEND
-#endif // FEATURE_UTF8STRING
-
/*********************************************************************/
OBJECTHANDLE ConstructStringLiteral(CORINFO_MODULE_HANDLE scopeHnd, mdToken metaTok)
{
if (methodTable == CoreLibBinder::GetClass(CLASS__BOOLEAN)
|| methodTable == CoreLibBinder::GetClass(CLASS__BYTE)
|| methodTable == CoreLibBinder::GetClass(CLASS__SBYTE)
-#ifdef FEATURE_UTF8STRING
- || methodTable == CoreLibBinder::GetClass(CLASS__CHAR8)
-#endif // FEATURE_UTF8STRING
|| methodTable == CoreLibBinder::GetClass(CLASS__CHAR)
|| methodTable == CoreLibBinder::GetClass(CLASS__INT16)
|| methodTable == CoreLibBinder::GetClass(CLASS__UINT16)
extern FCDECL1(StringObject*, UnframedAllocateString, DWORD stringLength);
extern FCDECL1(StringObject*, FramedAllocateString, DWORD stringLength);
-#ifdef FEATURE_UTF8STRING
-extern FCDECL1(Utf8StringObject*, AllocateUtf8String_MP_FastPortable, DWORD stringLength);
-extern FCDECL1(Utf8StringObject*, FramedAllocateUtf8String, DWORD stringLength);
-#endif // FEATURE_UTF8STRING
-
extern FCDECL2(Object*, JIT_NewArr1VC_MP_FastPortable, CORINFO_CLASS_HANDLE arrayMT, INT_PTR size);
extern FCDECL2(Object*, JIT_NewArr1OBJ_MP_FastPortable, CORINFO_CLASS_HANDLE arrayMT, INT_PTR size);
extern FCDECL2(Object*, JIT_NewArr1, CORINFO_CLASS_HANDLE arrayMT, INT_PTR size);
SetJitHelperFunction(CORINFO_HELP_NEWARR_1_OBJ, JIT_NewArr1OBJ_MP_FastPortable);
ECall::DynamicallyAssignFCallImpl(GetEEFuncEntryPoint(AllocateString_MP_FastPortable), ECall::FastAllocateString);
-#ifdef FEATURE_UTF8STRING
- ECall::DynamicallyAssignFCallImpl(GetEEFuncEntryPoint(AllocateUtf8String_MP_FastPortable), ECall::FastAllocateUtf8String);
-#endif // FEATURE_UTF8STRING
#else // TARGET_UNIX
// if (multi-proc || server GC)
if (GCHeapUtilities::UseThreadAllocationContexts())
SetJitHelperFunction(CORINFO_HELP_NEWARR_1_OBJ, JIT_NewArr1OBJ_MP_InlineGetThread);
ECall::DynamicallyAssignFCallImpl(GetEEFuncEntryPoint(AllocateStringFastMP_InlineGetThread), ECall::FastAllocateString);
-#ifdef FEATURE_UTF8STRING
- ECall::DynamicallyAssignFCallImpl(GetEEFuncEntryPoint(AllocateUtf8String_MP_FastPortable), ECall::FastAllocateUtf8String);
-#endif // FEATURE_UTF8STRING
}
else
{
SetJitHelperFunction(CORINFO_HELP_NEWARR_1_OBJ, JIT_NewArr1OBJ_UP);
ECall::DynamicallyAssignFCallImpl(GetEEFuncEntryPoint(AllocateStringFastUP), ECall::FastAllocateString);
-#ifdef FEATURE_UTF8STRING
- ECall::DynamicallyAssignFCallImpl(GetEEFuncEntryPoint(AllocateUtf8String_MP_FastPortable), ECall::FastAllocateUtf8String);
-#endif // FEATURE_UTF8STRING
}
#endif // TARGET_UNIX
}
if (obj->GetMethodTable() == g_pStringClass)
FC_RETURN_BOOL(TRUE);
-#ifdef FEATURE_UTF8STRING
- if (obj->GetMethodTable() == g_pUtf8StringClass)
- FC_RETURN_BOOL(TRUE);
-#endif // FEATURE_UTF8STRING
-
if (obj->GetMethodTable()->IsArray())
{
BASEARRAYREF asArray = (BASEARRAYREF)ObjectToOBJECTREF(obj);
if (obj->GetMethodTable() == g_pStringClass)
return;
-#ifdef FEATURE_UTF8STRING
- if (obj->GetMethodTable() == g_pUtf8StringClass)
- return;
-#endif // FEATURE_UTF8STRING
-
if (obj->GetMethodTable()->IsArray())
{
BASEARRAYREF asArray = (BASEARRAYREF) obj;
DEFINE_METASIG(IM(ArrByte_Int_Int_RetVoid, a(b) i i, v))
DEFINE_METASIG(IM(PtrByte_RetVoid, P(b), v))
-#ifdef FEATURE_UTF8STRING
-DEFINE_METASIG_T(IM(ReadOnlySpanOfByte_RetUtf8Str, GI(g(READONLY_SPAN), 1, b), C(UTF8_STRING)))
-DEFINE_METASIG_T(IM(ReadOnlySpanOfChar_RetUtf8Str, GI(g(READONLY_SPAN), 1, u), C(UTF8_STRING)))
-DEFINE_METASIG_T(IM(ArrByte_Int_Int_RetUtf8Str, a(b) i i, C(UTF8_STRING)))
-DEFINE_METASIG_T(IM(PtrByte_RetUtf8Str, P(b), C(UTF8_STRING)))
-DEFINE_METASIG_T(IM(ArrChar_Int_Int_RetUtf8Str, a(u) i i, C(UTF8_STRING)))
-DEFINE_METASIG_T(IM(PtrChar_RetUtf8Str, P(u), C(UTF8_STRING)))
-DEFINE_METASIG_T(IM(String_RetUtf8Str, s, C(UTF8_STRING)))
-#endif // FEATURE_UTF8STRING
-
DEFINE_METASIG(IM(Char_Char_RetStr, u u, s))
DEFINE_METASIG(IM(Char_Int_RetVoid, u i, v))
DEFINE_METASIG_T(SM(RetCultureInfo, _, C(CULTURE_INFO)))
pMT->SetComponentSize(2);
}
-#ifdef FEATURE_UTF8STRING
- else if (strcmp(name, g_Utf8StringName) == 0 && strcmp(nameSpace, g_SystemNS) == 0)
- {
- // Utf8Strings are not "normal" objects, so we need to mess with their method table a bit
- // so that the GC can figure out how big each string is...
- DWORD baseSize = Utf8StringObject::GetBaseSize();
- pMT->SetBaseSize(baseSize); // NULL character included
-
- GetHalfBakedClass()->SetBaseSizePadding(baseSize - bmtFP->NumInstanceFieldBytes);
-
- pMT->SetComponentSize(1);
- }
-#endif // FEATURE_UTF8STRING
else if (strcmp(name, g_CriticalFinalizerObjectName) == 0 && strcmp(nameSpace, g_ConstrainedExecutionNS) == 0)
{
// To introduce a class with a critical finalizer,
typedef DPTR(PTRArray) PTR_PTRArray;
class StringObject;
-#ifdef FEATURE_UTF8STRING
-class Utf8StringObject;
-#endif // FEATURE_UTF8STRING
#ifdef USE_CHECKED_OBJECTREFS
typedef REF<ArrayBase> BASEARRAYREF;
typedef REF<CHARArray> CHARARRAYREF;
typedef REF<PTRArray> PTRARRAYREF; // Warning: Use PtrArray only for single dimensional arrays, not multidim arrays.
typedef REF<StringObject> STRINGREF;
-#ifdef FEATURE_UTF8STRING
-typedef REF<Utf8StringObject> UTF8STRINGREF;
-#endif // FEATURE_UTF8STRING
#else // USE_CHECKED_OBJECTREFS
typedef PTR_CHARArray CHARARRAYREF;
typedef PTR_PTRArray PTRARRAYREF; // Warning: Use PtrArray only for single dimensional arrays, not multidim arrays.
typedef PTR_StringObject STRINGREF;
-#ifdef FEATURE_UTF8STRING
-typedef PTR_Utf8StringObject UTF8STRINGREF;
-#endif // FEATURE_UTF8STRING
#endif // USE_CHECKED_OBJECTREFS
};
-#ifdef FEATURE_UTF8STRING
-class Utf8StringObject : public Object
-{
-#ifdef DACCESS_COMPILE
- friend class ClrDataAccess;
-#endif
-
-private:
- DWORD m_StringLength;
- BYTE m_FirstChar;
-
-public:
- VOID SetLength(DWORD len) { LIMITED_METHOD_CONTRACT; _ASSERTE(len >= 0); m_StringLength = len; }
-
-protected:
- Utf8StringObject() { LIMITED_METHOD_CONTRACT; }
- ~Utf8StringObject() { LIMITED_METHOD_CONTRACT; }
-
-public:
-
- /*=================RefInterpretGetStringValuesDangerousForGC======================
- **N.B.: This perfoms no range checking and relies on the caller to have done this.
- **Args: (IN)ref -- the Utf8String to be interpretted.
- ** (OUT)chars -- a pointer to the characters in the buffer.
- ** (OUT)length -- a pointer to the length of the buffer.
- **Returns: void.
- **Exceptions: None.
- ==============================================================================*/
- // !!!! If you use this function, you have to be careful because chars is a pointer
- // !!!! to the data buffer of ref. If GC happens after this call, you need to make
- // !!!! sure that you have a pin handle on ref, or use GCPROTECT_BEGINPINNING on ref.
- void RefInterpretGetStringValuesDangerousForGC(__deref_out_ecount(*length + 1) CHAR **chars, int *length) {
- WRAPPER_NO_CONTRACT;
-
- _ASSERTE(GetGCSafeMethodTable() == g_pUtf8StringClass);
- *length = GetStringLength();
- *chars = GetBuffer();
-#ifdef _DEBUG
- EnableStressHeapHelper();
-#endif
- }
-
- DWORD GetStringLength() { LIMITED_METHOD_DAC_CONTRACT; return( m_StringLength );}
- CHAR* GetBuffer() { LIMITED_METHOD_CONTRACT; return (CHAR*)( dac_cast<TADDR>(this) + offsetof(Utf8StringObject, m_FirstChar) ); }
-
- static DWORD GetBaseSize();
- static SIZE_T GetSize(DWORD stringLength);
-};
-#endif // FEATURE_UTF8STRING
-
// This is the Method version of the Reflection object.
// A Method has adddition information.
// m_pMD - A pointer to the actual MethodDesc of the method.
return GetBaseSize() + strLen * sizeof(WCHAR);
}
-#ifdef FEATURE_UTF8STRING
-__forceinline /*static*/ DWORD Utf8StringObject::GetBaseSize()
-{
- LIMITED_METHOD_DAC_CONTRACT;
-
- return OBJECT_BASESIZE + sizeof(DWORD) /* length */ + sizeof(BYTE) /* null terminator */;
-}
-
-__forceinline /*static*/ SIZE_T Utf8StringObject::GetSize(DWORD strLen)
-{
- LIMITED_METHOD_DAC_CONTRACT;
-
- return GetBaseSize() + strLen;
-}
-#endif // FEATURE_UTF8STRING
-
#ifdef DACCESS_COMPILE
inline void Object::EnumMemoryRegions(void)
PREFIX_ASSUME(pMT != NULL);
//We don't allow unitialized Strings or Utf8Strings.
- if (pMT == g_pStringClass
-#ifdef FEATURE_UTF8STRING
- || pMT == g_pUtf8StringClass
-#endif // FEATURE_UTF8STRING
- ) {
+ if (pMT == g_pStringClass) {
COMPlusThrow(kArgumentException, W("Argument_NoUninitializedStrings"));
}
GPTR_IMPL(MethodTable, g_pRuntimeTypeClass);
GPTR_IMPL(MethodTable, g_pCanonMethodTableClass); // System.__Canon
GPTR_IMPL(MethodTable, g_pStringClass);
-#ifdef FEATURE_UTF8STRING
-GPTR_IMPL(MethodTable, g_pUtf8StringClass);
-#endif // FEATURE_UTF8STRING
GPTR_IMPL(MethodTable, g_pArrayClass);
GPTR_IMPL(MethodTable, g_pSZArrayHelperClass);
GPTR_IMPL(MethodTable, g_pNullableClass);
class IGCHeap;
class Object;
class StringObject;
-#ifdef FEATURE_UTF8STRING
-class Utf8StringObject;
-#endif // FEATURE_UTF8STRING
class ArrayClass;
class MethodTable;
class MethodDesc;
#define OBJECTREFToObject(objref) ((objref).operator-> ())
#define ObjectToSTRINGREF(obj) (STRINGREF(obj))
#define STRINGREFToObject(objref) (*( (StringObject**) &(objref) ))
-#ifdef FEATURE_UTF8STRING
-#define ObjectToUTF8STRINGREF(obj) (UTF8STRINGREF(obj))
-#define UTF8STRINGREFToObject(objref) (*( (Utf8StringObject**) &(objref) ))
-#endif // FEATURE_UTF8STRING
#else // _DEBUG_IMPL
#define OBJECTREFToObject(objref) ((PTR_Object) (objref))
#define ObjectToSTRINGREF(obj) ((PTR_StringObject) (obj))
#define STRINGREFToObject(objref) ((PTR_StringObject) (objref))
-#ifdef FEATURE_UTF8STRING
-#define ObjectToUTF8STRINGREF(obj) ((PTR_Utf8StringObject) (obj))
-#define UTF8STRINGREFToObject(objref) ((PTR_Utf8StringObject) (objref))
-#endif // FEATURE_UTF8STRING
#endif // _DEBUG_IMPL
GPTR_DECL(MethodTable, g_pRuntimeTypeClass);
GPTR_DECL(MethodTable, g_pCanonMethodTableClass); // System.__Canon
GPTR_DECL(MethodTable, g_pStringClass);
-#ifdef FEATURE_UTF8STRING
-GPTR_DECL(MethodTable, g_pUtf8StringClass);
-#endif // FEATURE_UTF8STRING
GPTR_DECL(MethodTable, g_pArrayClass);
GPTR_DECL(MethodTable, g_pSZArrayHelperClass);
GPTR_DECL(MethodTable, g_pNullableClass);
<ItemGroup>
<!-- Projects which are manually built. -->
<ProjectExclusions Include="$(CommonTestPath)System\Net\Prerequisites\**\*.csproj" />
- <!-- TODO: Reenable when mono supports utf8string. -->
- <ProjectExclusions Include="$(MSBuildThisFileDirectory)\System.Utf8String.Experimental\**\*.csproj" Condition="'$(RuntimeFlavor)' == 'Mono'" />
</ItemGroup>
<PropertyGroup>
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\RegisteredWaitHandle.Portable.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\ThreadInt64PersistentCounter.cs" />
</ItemGroup>
- <ItemGroup Condition="'$(FeatureUtf8String)' == 'true'">
- <Compile Include="$(MSBuildThisFileDirectory)System\Char8.cs" />
- <Compile Include="$(MSBuildThisFileDirectory)System\Utf8Extensions.cs" />
- <Compile Include="$(MSBuildThisFileDirectory)System\Utf8Extensions.CoreLib.cs" />
- <Compile Include="$(MSBuildThisFileDirectory)System\Utf8String.cs" />
- <Compile Include="$(MSBuildThisFileDirectory)System\Utf8String.Comparison.cs" />
- <Compile Include="$(MSBuildThisFileDirectory)System\Utf8String.Construction.cs" />
- <Compile Include="$(MSBuildThisFileDirectory)System\Utf8String.Conversion.cs" />
- <Compile Include="$(MSBuildThisFileDirectory)System\Utf8String.Enumeration.cs" />
- <Compile Include="$(MSBuildThisFileDirectory)System\Utf8String.Manipulation.cs" />
- <Compile Include="$(MSBuildThisFileDirectory)System\Utf8String.Searching.cs" />
- <Compile Include="$(MSBuildThisFileDirectory)System\Utf8StringSplitOptions.cs" />
- <Compile Include="$(MSBuildThisFileDirectory)System\Text\Utf8Span.cs" />
- <Compile Include="$(MSBuildThisFileDirectory)System\Text\Utf8Span.Comparison.cs" />
- <Compile Include="$(MSBuildThisFileDirectory)System\Text\Utf8Span.Conversion.cs" />
- <Compile Include="$(MSBuildThisFileDirectory)System\Text\Utf8Span.Enumeration.cs" />
- <Compile Include="$(MSBuildThisFileDirectory)System\Text\Utf8Span.Manipulation.cs" />
- <Compile Include="$(MSBuildThisFileDirectory)System\Text\Utf8Span.Searching.cs" />
- <Compile Include="$(MSBuildThisFileDirectory)System\Text\Utf8StringComparer.cs" />
- </ItemGroup>
</Project>
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-namespace System
-{
- /// <summary>
- /// Represents a UTF-8 code unit, the elemental type of <see cref="Utf8String"/>.
- /// </summary>
- public readonly struct Char8 : IComparable<Char8>, IEquatable<Char8>
- {
- private readonly byte _value;
-
- private Char8(byte value)
- {
- _value = value;
- }
-
- public static bool operator ==(Char8 left, Char8 right) => left._value == right._value;
- public static bool operator !=(Char8 left, Char8 right) => left._value != right._value;
- public static bool operator <(Char8 left, Char8 right) => left._value < right._value;
- public static bool operator <=(Char8 left, Char8 right) => left._value <= right._value;
- public static bool operator >(Char8 left, Char8 right) => left._value > right._value;
- public static bool operator >=(Char8 left, Char8 right) => left._value >= right._value;
-
- // Operators from Utf8Char to <other primitives>
- // TODO: Once C# gets support for checked operators, we should add those here.
-
- public static implicit operator byte(Char8 value) => value._value;
- [CLSCompliant(false)]
- public static explicit operator sbyte(Char8 value) => (sbyte)value._value; // explicit because can integer overflow
- public static explicit operator char(Char8 value) => (char)value._value; // explicit because don't want to encourage char conversion
- public static implicit operator short(Char8 value) => value._value;
- [CLSCompliant(false)]
- public static implicit operator ushort(Char8 value) => value._value;
- public static implicit operator int(Char8 value) => value._value;
- [CLSCompliant(false)]
- public static implicit operator uint(Char8 value) => value._value;
- public static implicit operator long(Char8 value) => value._value;
- [CLSCompliant(false)]
- public static implicit operator ulong(Char8 value) => value._value;
-
- // Operators from <other primitives> to Char8; most are explicit because narrowing conversions could be lossy
- // TODO: Once C# gets support for checked operators, we should add those here.
-
- public static implicit operator Char8(byte value) => new Char8(value);
- [CLSCompliant(false)]
- public static explicit operator Char8(sbyte value) => new Char8((byte)value);
- public static explicit operator Char8(char value) => new Char8((byte)value);
- public static explicit operator Char8(short value) => new Char8((byte)value);
- [CLSCompliant(false)]
- public static explicit operator Char8(ushort value) => new Char8((byte)value);
- public static explicit operator Char8(int value) => new Char8((byte)value);
- [CLSCompliant(false)]
- public static explicit operator Char8(uint value) => new Char8((byte)value);
- public static explicit operator Char8(long value) => new Char8((byte)value);
- [CLSCompliant(false)]
- public static explicit operator Char8(ulong value) => new Char8((byte)value);
-
- public int CompareTo(Char8 other) => this._value.CompareTo(other._value);
-
- public override bool Equals(object? obj) => (obj is Char8 other) && (this == other);
- public bool Equals(Char8 other) => this == other;
-
- public override int GetHashCode() => _value;
-
- public override string ToString() => _value.ToString("X2");
- }
-}
// 'obj is T[]' below also handles things like int[] <-> uint[] being convertible
Debug.Assert((obj == null)
|| (typeof(T) == typeof(char) && obj is string)
-#if FEATURE_UTF8STRING
- || ((typeof(T) == typeof(byte) || typeof(T) == typeof(Char8)) && obj is Utf8String)
-#endif // FEATURE_UTF8STRING
|| (obj is T[])
|| (obj is MemoryManager<T>));
{
return (_object is string str) ? str.Substring(_index, _length) : Span.ToString();
}
-#if FEATURE_UTF8STRING
- else if (typeof(T) == typeof(Char8))
- {
- // TODO_UTF8STRING: Call into optimized transcoding routine when it's available.
- Span<T> span = Span;
- return Encoding.UTF8.GetString(new ReadOnlySpan<byte>(ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)), span.Length));
- }
-#endif // FEATURE_UTF8STRING
return string.Format("System.Memory<{0}>[{1}]", typeof(T).Name, _length);
}
refToReturn = ref Unsafe.As<char, T>(ref Unsafe.As<string>(tmpObject).GetRawStringData());
lengthOfUnderlyingSpan = Unsafe.As<string>(tmpObject).Length;
}
-#if FEATURE_UTF8STRING
- else if ((typeof(T) == typeof(byte) || typeof(T) == typeof(Char8)) && tmpObject.GetType() == typeof(Utf8String))
- {
- refToReturn = ref Unsafe.As<byte, T>(ref Unsafe.As<Utf8String>(tmpObject).DangerousGetMutableReference());
- lengthOfUnderlyingSpan = Unsafe.As<Utf8String>(tmpObject).Length;
- }
-#endif // FEATURE_UTF8STRING
else if (RuntimeHelpers.ObjectHasComponentSize(tmpObject))
{
// We know the object is not null, it's not a string, and it is variable-length. The only
ref char stringData = ref Unsafe.Add(ref s.GetRawStringData(), _index);
return new MemoryHandle(Unsafe.AsPointer(ref stringData), handle);
}
-#if FEATURE_UTF8STRING
- else if ((typeof(T) == typeof(byte) || typeof(T) == typeof(Char8)) && tmpObject is Utf8String utf8String)
- {
- GCHandle handle = GCHandle.Alloc(tmpObject, GCHandleType.Pinned);
- ref byte stringData = ref utf8String.DangerousGetMutableReference(_index);
- return new MemoryHandle(Unsafe.AsPointer(ref stringData), handle);
- }
-#endif // FEATURE_UTF8STRING
else if (RuntimeHelpers.ObjectHasComponentSize(tmpObject))
{
// 'tmpObject is T[]' below also handles things like int[] <-> uint[] being convertible
// 'obj is T[]' below also handles things like int[] <-> uint[] being convertible
Debug.Assert((obj == null)
|| (typeof(T) == typeof(char) && obj is string)
-#if FEATURE_UTF8STRING
- || ((typeof(T) == typeof(byte) || typeof(T) == typeof(Char8)) && obj is Utf8String)
-#endif // FEATURE_UTF8STRING
|| (obj is T[])
|| (obj is MemoryManager<T>));
{
return (_object is string str) ? str.Substring(_index, _length) : Span.ToString();
}
-#if FEATURE_UTF8STRING
- else if (typeof(T) == typeof(Char8))
- {
- // TODO_UTF8STRING: Call into optimized transcoding routine when it's available.
- ReadOnlySpan<T> span = Span;
- return Encoding.UTF8.GetString(new ReadOnlySpan<byte>(ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)), span.Length));
- }
-#endif // FEATURE_UTF8STRING
return string.Format("System.ReadOnlyMemory<{0}>[{1}]", typeof(T).Name, _length);
}
refToReturn = ref Unsafe.As<char, T>(ref Unsafe.As<string>(tmpObject).GetRawStringData());
lengthOfUnderlyingSpan = Unsafe.As<string>(tmpObject).Length;
}
-#if FEATURE_UTF8STRING
- else if ((typeof(T) == typeof(byte) || typeof(T) == typeof(Char8)) && tmpObject.GetType() == typeof(Utf8String))
- {
- refToReturn = ref Unsafe.As<byte, T>(ref Unsafe.As<Utf8String>(tmpObject).DangerousGetMutableReference());
- lengthOfUnderlyingSpan = Unsafe.As<Utf8String>(tmpObject).Length;
- }
-#endif // FEATURE_UTF8STRING
else if (RuntimeHelpers.ObjectHasComponentSize(tmpObject))
{
// We know the object is not null, it's not a string, and it is variable-length. The only
ref char stringData = ref Unsafe.Add(ref s.GetRawStringData(), _index);
return new MemoryHandle(Unsafe.AsPointer(ref stringData), handle);
}
-#if FEATURE_UTF8STRING
- else if ((typeof(T) == typeof(byte) || typeof(T) == typeof(Char8)) && tmpObject is Utf8String utf8String)
- {
- GCHandle handle = GCHandle.Alloc(tmpObject, GCHandleType.Pinned);
- ref byte stringData = ref utf8String.DangerousGetMutableReference(_index);
- return new MemoryHandle(Unsafe.AsPointer(ref stringData), handle);
- }
-#endif // FEATURE_UTF8STRING
else if (RuntimeHelpers.ObjectHasComponentSize(tmpObject))
{
// 'tmpObject is T[]' below also handles things like int[] <-> uint[] being convertible
{
return new string(new ReadOnlySpan<char>(ref Unsafe.As<T, char>(ref _pointer.Value), _length));
}
-#if FEATURE_UTF8STRING
- else if (typeof(T) == typeof(Char8))
- {
- // TODO_UTF8STRING: Call into optimized transcoding routine when it's available.
- return Encoding.UTF8.GetString(new ReadOnlySpan<byte>(ref Unsafe.As<T, byte>(ref _pointer.Value), _length));
- }
-#endif // FEATURE_UTF8STRING
return string.Format("System.ReadOnlySpan<{0}>[{1}]", typeof(T).Name, _length);
}
if (obj != null && !(
(typeof(T) == typeof(char) && obj.GetType() == typeof(string))
-#if FEATURE_UTF8STRING
- || ((typeof(T) == typeof(byte) || typeof(T) == typeof(Char8)) && obj.GetType() == typeof(Utf8String))
-#endif // FEATURE_UTF8STRING
))
{
if (RuntimeHelpers.ObjectHasComponentSize(obj))
{
return new string(new ReadOnlySpan<char>(ref Unsafe.As<T, char>(ref _pointer.Value), _length));
}
-#if FEATURE_UTF8STRING
- else if (typeof(T) == typeof(Char8))
- {
- // TODO_UTF8STRING: Call into optimized transcoding routine when it's available.
- return Encoding.UTF8.GetString(new ReadOnlySpan<byte>(ref Unsafe.As<T, byte>(ref _pointer.Value), _length));
- }
-#endif // FEATURE_UTF8STRING
return string.Format("System.Span<{0}>[{1}]", typeof(T).Name, _length);
}
}
}
-#if FEATURE_UTF8STRING
- /// <summary>
- /// Returns a value stating whether <paramref name="utf8Data"/> contains only well-formed UTF-8 data.
- /// </summary>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static unsafe bool IsWellFormedUtf8(ReadOnlySpan<byte> utf8Data)
- {
- fixed (byte* pUtf8Data = &MemoryMarshal.GetReference(utf8Data))
- {
- // The return value here will point to the end of the span if the data is well-formed.
- byte* pFirstInvalidByte = GetPointerToFirstInvalidByte(pUtf8Data, utf8Data.Length, out int _, out _);
- return (pFirstInvalidByte == (pUtf8Data + (uint)utf8Data.Length));
- }
- }
-
- /// <summary>
- /// Returns <paramref name="value"/> if it is null or contains only well-formed UTF-8 data;
- /// otherwises allocates a new <see cref="Utf8String"/> instance containing the same data as
- /// <paramref name="value"/> but where all invalid UTF-8 sequences have been replaced
- /// with U+FFFD.
- /// </summary>
- public static Utf8String ValidateAndFixupUtf8String(Utf8String value)
- {
- if (value.Length == 0)
- {
- return value;
- }
-
- ReadOnlySpan<byte> valueAsBytes = value.AsBytes();
-
- int idxOfFirstInvalidData = GetIndexOfFirstInvalidUtf8Sequence(valueAsBytes, out _);
- if (idxOfFirstInvalidData < 0)
- {
- return value;
- }
-
- // TODO_UTF8STRING: Replace this with the faster implementation once it's available.
- // (The faster implementation is in the dev/utf8string_bak branch currently.)
-
- MemoryStream memStream = new MemoryStream();
-#if (!NETSTANDARD2_0 && !NETFRAMEWORK)
- memStream.Write(valueAsBytes.Slice(0, idxOfFirstInvalidData));
-
- valueAsBytes = valueAsBytes.Slice(idxOfFirstInvalidData);
- do
- {
- if (Rune.DecodeFromUtf8(valueAsBytes, out _, out int bytesConsumed) == OperationStatus.Done)
- {
- // Valid scalar value - copy data as-is to MemoryStream
- memStream.Write(valueAsBytes.Slice(0, bytesConsumed));
- }
- else
- {
- // Invalid scalar value - copy U+FFFD to MemoryStream
- memStream.Write(ReplacementCharSequence);
- }
-
- valueAsBytes = valueAsBytes.Slice(bytesConsumed);
- } while (!valueAsBytes.IsEmpty);
-#else
- if (!MemoryMarshal.TryGetArray(value.AsMemoryBytes(), out ArraySegment<byte> valueArraySegment))
- {
- Debug.Fail("Utf8String on netstandard should always be backed by an array.");
- }
-
- memStream.Write(valueArraySegment.Array, valueArraySegment.Offset, idxOfFirstInvalidData);
-
- valueArraySegment = new ArraySegment<byte>(
- valueArraySegment.Array,
- idxOfFirstInvalidData,
- valueArraySegment.Count - idxOfFirstInvalidData);
- do
- {
- if (Rune.DecodeFromUtf8(valueArraySegment, out _, out int bytesConsumed) == OperationStatus.Done)
- {
- // Valid scalar value - copy data as-is to MemoryStream
- memStream.Write(valueArraySegment.Array, valueArraySegment.Offset, bytesConsumed);
- }
- else
- {
- // Invalid scalar value - copy U+FFFD to MemoryStream
- memStream.Write(ReplacementCharSequence, 0, ReplacementCharSequence.Length);
- }
-
- valueArraySegment = new ArraySegment<byte>(
- valueArraySegment.Array,
- valueArraySegment.Offset + bytesConsumed,
- valueArraySegment.Count - bytesConsumed);
- } while (valueArraySegment.Count > 0);
-#endif
-
- bool success = memStream.TryGetBuffer(out ArraySegment<byte> memStreamBuffer);
- Debug.Assert(success, "Couldn't get underlying MemoryStream buffer.");
-
- return Utf8String.UnsafeCreateWithoutValidation(memStreamBuffer);
- }
-#endif // FEATURE_UTF8STRING
}
}
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System.Text.Unicode;
-
-namespace System.Text
-{
- public readonly ref partial struct Utf8Span
- {
- public static bool operator ==(Utf8Span left, Utf8Span right) => Equals(left, right);
- public static bool operator !=(Utf8Span left, Utf8Span right) => !Equals(left, right);
-
- public int CompareTo(Utf8Span other)
- {
- // TODO_UTF8STRING: This is ordinal, but String.CompareTo uses CurrentCulture.
- // Is this acceptable?
-
- return Utf8StringComparer.Ordinal.Compare(this, other);
- }
-
- public int CompareTo(Utf8Span other, StringComparison comparison)
- {
- // TODO_UTF8STRING: We can avoid the virtual dispatch by moving the switch into this method.
-
- return Utf8StringComparer.FromComparison(comparison).Compare(this, other);
- }
-
- /// <summary>
- /// Returns a value stating whether the current <see cref="Utf8Span"/> instance contains
- /// <paramref name="value"/>. An ordinal comparison is used.
- /// </summary>
- public bool Contains(char value)
- {
- return Rune.TryCreate(value, out Rune rune) && Contains(rune);
- }
-
- /// <summary>
- /// Returns a value stating whether the current <see cref="Utf8Span"/> instance contains
- /// <paramref name="value"/>. The specified comparison is used.
- /// </summary>
- public bool Contains(char value, StringComparison comparison)
- {
- return Rune.TryCreate(value, out Rune rune) && Contains(rune, comparison);
- }
-
- /// <summary>
- /// Returns a value stating whether the current <see cref="Utf8Span"/> instance contains
- /// the specified <see cref="Rune"/>. An ordinal comparison is used.
- /// </summary>
- public bool Contains(Rune value)
- {
- // TODO_UTF8STRING: This should be split into two methods:
- // One which operates on a single-byte (ASCII) search value,
- // the other which operates on a multi-byte (non-ASCII) search value.
-
- Span<byte> runeBytes = stackalloc byte[Utf8Utility.MaxBytesPerScalar];
- int runeBytesWritten = value.EncodeToUtf8(runeBytes);
-
- return (this.Bytes.IndexOf(runeBytes.Slice(0, runeBytesWritten)) >= 0);
- }
-
- /// <summary>
- /// Returns a value stating whether the current <see cref="Utf8Span"/> instance contains
- /// the specified <see cref="Rune"/>. The specified comparison is used.
- /// </summary>
- public bool Contains(Rune value, StringComparison comparison)
- {
- // TODO_UTF8STRING: Optimize me to avoid allocations.
-
-#if (!NETSTANDARD2_0 && !NETFRAMEWORK)
- return this.ToString().Contains(value.ToString(), comparison);
-#else
- return this.ToString().IndexOf(value.ToString(), comparison) >= 0;
-#endif
- }
-
- /// <summary>
- /// Returns a value stating whether the current <see cref="Utf8Span"/> instance contains <paramref name="value"/>.
- /// An ordinal comparison is used.
- /// </summary>
- public bool Contains(Utf8Span value)
- {
- return (this.Bytes.IndexOf(value.Bytes) >= 0);
- }
-
- /// <summary>
- /// Returns a value stating whether the current <see cref="Utf8Span"/> instance contains <paramref name="value"/>.
- /// The specified comparison is used.
- /// </summary>
- public bool Contains(Utf8Span value, StringComparison comparison)
- {
- // TODO_UTF8STRING: Optimize me to avoid allocations.
-
-#if (!NETSTANDARD2_0 && !NETFRAMEWORK)
- return this.ToString().Contains(value.ToString(), comparison);
-#else
- return this.ToString().IndexOf(value.ToString(), comparison) >= 0;
-#endif
- }
-
- /// <summary>
- /// Returns a value stating whether the current <see cref="Utf8Span"/> instance ends with
- /// <paramref name="value"/>. An ordinal comparison is used.
- /// </summary>
- public bool EndsWith(char value)
- {
- return Rune.TryCreate(value, out Rune rune) && EndsWith(rune);
- }
-
- /// <summary>
- /// Returns a value stating whether the current <see cref="Utf8Span"/> instance ends with
- /// <paramref name="value"/>. The specified comparison is used.
- /// </summary>
- public bool EndsWith(char value, StringComparison comparison)
- {
- return Rune.TryCreate(value, out Rune rune) && EndsWith(rune, comparison);
- }
-
- /// <summary>
- /// Returns a value stating whether the current <see cref="Utf8Span"/> instance ends with
- /// the specified <see cref="Rune"/>. An ordinal comparison is used.
- /// </summary>
- public bool EndsWith(Rune value)
- {
- // TODO_UTF8STRING: This should be split into two methods:
- // One which operates on a single-byte (ASCII) search value,
- // the other which operates on a multi-byte (non-ASCII) search value.
-
- Span<byte> runeBytes = stackalloc byte[Utf8Utility.MaxBytesPerScalar];
- int runeBytesWritten = value.EncodeToUtf8(runeBytes);
-
- return this.Bytes.EndsWith(runeBytes.Slice(0, runeBytesWritten));
- }
-
- /// <summary>
- /// Returns a value stating whether the current <see cref="Utf8Span"/> instance ends with
- /// the specified <see cref="Rune"/>. The specified comparison is used.
- /// </summary>
- public bool EndsWith(Rune value, StringComparison comparison)
- {
- // TODO_UTF8STRING: Optimize me to avoid allocations.
-
- return this.ToString().EndsWith(value.ToString(), comparison);
- }
-
- /// <summary>
- /// Returns a value stating whether the current <see cref="Utf8Span"/> instance ends with <paramref name="value"/>.
- /// An ordinal comparison is used.
- /// </summary>
- public bool EndsWith(Utf8Span value)
- {
- return this.Bytes.EndsWith(value.Bytes);
- }
-
- /// <summary>
- /// Returns a value stating whether the current <see cref="Utf8Span"/> instance ends with <paramref name="value"/>.
- /// The specified comparison is used.
- /// </summary>
- public bool EndsWith(Utf8Span value, StringComparison comparison)
- {
- // TODO_UTF8STRING: Optimize me to avoid allocations.
-
- return this.ToString().EndsWith(value.ToString(), comparison);
- }
-
- /// <summary>
- /// Returns a value stating whether the current <see cref="Utf8Span"/> instance begins with
- /// <paramref name="value"/>. An ordinal comparison is used.
- /// </summary>
- public bool StartsWith(char value)
- {
- return Rune.TryCreate(value, out Rune rune) && StartsWith(rune);
- }
-
- /// <summary>
- /// Returns a value stating whether the current <see cref="Utf8Span"/> instance begins with
- /// <paramref name="value"/>. The specified comparison is used.
- /// </summary>
- public bool StartsWith(char value, StringComparison comparison)
- {
- return Rune.TryCreate(value, out Rune rune) && StartsWith(rune, comparison);
- }
-
- /// <summary>
- /// Returns a value stating whether the current <see cref="Utf8Span"/> instance begins with
- /// the specified <see cref="Rune"/>. An ordinal comparison is used.
- /// </summary>
- public bool StartsWith(Rune value)
- {
- // TODO_UTF8STRING: This should be split into two methods:
- // One which operates on a single-byte (ASCII) search value,
- // the other which operates on a multi-byte (non-ASCII) search value.
-
- Span<byte> runeBytes = stackalloc byte[Utf8Utility.MaxBytesPerScalar];
- int runeBytesWritten = value.EncodeToUtf8(runeBytes);
-
- return this.Bytes.StartsWith(runeBytes.Slice(0, runeBytesWritten));
- }
-
- /// <summary>
- /// Returns a value stating whether the current <see cref="Utf8Span"/> instance begins with
- /// the specified <see cref="Rune"/>. The specified comparison is used.
- /// </summary>
- public bool StartsWith(Rune value, StringComparison comparison)
- {
- // TODO_UTF8STRING: Optimize me to avoid allocations.
-
- return this.ToString().StartsWith(value.ToString(), comparison);
- }
-
- /// <summary>
- /// Returns a value stating whether the current <see cref="Utf8Span"/> instance begins with <paramref name="value"/>.
- /// An ordinal comparison is used.
- /// </summary>
- public bool StartsWith(Utf8Span value)
- {
- return this.Bytes.StartsWith(value.Bytes);
- }
-
- /// <summary>
- /// Returns a value stating whether the current <see cref="Utf8Span"/> instance begins with <paramref name="value"/>.
- /// The specified comparison is used.
- /// </summary>
- public bool StartsWith(Utf8Span value, StringComparison comparison)
- {
- // TODO_UTF8STRING: Optimize me to avoid allocations.
-
- return this.ToString().StartsWith(value.ToString(), comparison);
- }
- }
-}
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System.Buffers;
-using System.Diagnostics;
-using System.Globalization;
-using System.Runtime.InteropServices;
-using System.Text.Unicode;
-
-namespace System.Text
-{
- public readonly ref partial struct Utf8Span
- {
- /// <summary>
- /// Returns a new <see cref="Utf8String"/> instance which represents this <see cref="Utf8Span"/> instance
- /// normalized using the specified Unicode normalization form.
- /// </summary>
- /// <remarks>
- /// The original <see cref="Utf8Span"/> is left unchanged by this operation.
- /// </remarks>
- public Utf8String Normalize(NormalizationForm normalizationForm = NormalizationForm.FormC)
- {
- // TODO_UTF8STRING: Reduce allocations in this code path.
-
- return new Utf8String(this.ToString().Normalize(normalizationForm));
- }
-
- /// <summary>
- /// Converts this <see cref="Utf8Span"/> to the desired Unicode normalization form, writing the
- /// UTF-8 result to the buffer <paramref name="destination"/>.
- /// </summary>
- /// <returns>
- /// The number of bytes written to <paramref name="destination"/>, or -1 if <paramref name="destination"/>
- /// is not large enough to hold the result of the normalization operation.
- /// </returns>
- /// <remarks>
- /// The original <see cref="Utf8Span"/> is left unchanged by this operation. Note that the the required
- /// length of <paramref name="destination"/> may be longer or shorter (in terms of UTF-8 byte count)
- /// than the input <see cref="Utf8Span"/>.
- /// </remarks>
- public int Normalize(Span<byte> destination, NormalizationForm normalizationForm = NormalizationForm.FormC)
- {
- // TODO_UTF8STRING: Reduce allocations in this code path.
-
- ReadOnlySpan<char> normalized = this.ToString().Normalize(normalizationForm).AsSpan();
- OperationStatus status = Utf8.FromUtf16(normalized, destination, out int _, out int bytesWritten, replaceInvalidSequences: false, isFinalBlock: true);
-
- Debug.Assert(status == OperationStatus.Done || status == OperationStatus.DestinationTooSmall, "Normalize shouldn't have produced malformed Unicode string.");
-
- if (status != OperationStatus.Done)
- {
- bytesWritten = -1; // "destination too small"
- }
-
- return bytesWritten;
- }
-
- /// <summary>
- /// Returns the entire <see cref="Utf8String"/> as an array of UTF-8 bytes.
- /// </summary>
- public byte[] ToByteArray() => Bytes.ToArray();
-
- /// <summary>
- /// Converts this <see cref="Utf8Span"/> to a <see langword="char[]"/>.
- /// </summary>
- public unsafe char[] ToCharArray()
- {
- if (IsEmpty)
- {
- return Array.Empty<char>();
- }
-
- // TODO_UTF8STRING: Since we know the underlying data is immutable, well-formed UTF-8,
- // we can perform transcoding using an optimized code path that skips all safety checks.
- // We should also consider skipping the two-pass if possible.
-
- fixed (byte* pbUtf8 = &DangerousGetMutableReference())
- {
- byte* pbUtf8Invalid = Utf8Utility.GetPointerToFirstInvalidByte(pbUtf8, this.Length, out int utf16CodeUnitCountAdjustment, out _);
- Debug.Assert(pbUtf8Invalid == pbUtf8 + this.Length, "Invalid UTF-8 data seen in buffer.");
-
- char[] asUtf16 = new char[this.Length + utf16CodeUnitCountAdjustment];
- fixed (char* pbUtf16 = asUtf16)
- {
- OperationStatus status = Utf8Utility.TranscodeToUtf16(pbUtf8, this.Length, pbUtf16, asUtf16.Length, out byte* pbUtf8End, out char* pchUtf16End);
- Debug.Assert(status == OperationStatus.Done, "The buffer changed out from under us unexpectedly?");
- Debug.Assert(pbUtf8End == pbUtf8 + this.Length, "The buffer changed out from under us unexpectedly?");
- Debug.Assert(pchUtf16End == pbUtf16 + asUtf16.Length, "The buffer changed out from under us unexpectedly?");
-
- return asUtf16;
- }
- }
- }
-
- /// <summary>
- /// Converts this <see cref="Utf8Span"/> instance to its UTF-16 equivalent, writing the result into
- /// the buffer <paramref name="destination"/>.
- /// </summary>
- /// <returns>
- /// The number of bytes written to <paramref name="destination"/>, or -1 if <paramref name="destination"/>
- /// is not large enough to hold the result of the transcoding operation.
- /// </returns>
- public int ToChars(Span<char> destination)
- {
- OperationStatus status = Utf8.ToUtf16(Bytes, destination, out int _, out int charsWritten, replaceInvalidSequences: false, isFinalBlock: true);
-
- Debug.Assert(status == OperationStatus.Done || status == OperationStatus.DestinationTooSmall, "Utf8Spans shouldn't contain ill-formed UTF-8 data.");
-
- if (status != OperationStatus.Done)
- {
- charsWritten = -1; // "destination too small"
- }
-
- return charsWritten;
- }
-
- /// <summary>
- /// Returns a new <see cref="Utf8String"/> instance which represents this <see cref="Utf8Span"/> instance
- /// converted to lowercase using <paramref name="culture"/>.
- /// </summary>
- /// <remarks>
- /// The original <see cref="Utf8Span"/> is left unchanged by this operation. Note that the returned
- /// <see cref="Utf8String"/> instance may be longer or shorter (in terms of UTF-8 byte count) than the
- /// input <see cref="Utf8Span"/>.
- /// </remarks>
- public Utf8String ToLower(CultureInfo culture)
- {
- // TODO_UTF8STRING: Avoid intermediate allocations.
-
- if (culture is null)
- {
- ThrowHelper.ThrowArgumentNullException(ExceptionArgument.culture);
- }
-
- return new Utf8String(this.ToString().ToLower(culture));
- }
-
- /// <summary>
- /// Converts this <see cref="Utf8Span"/> to lowercase using <paramref name="culture"/>, writing the
- /// UTF-8 result to the buffer <paramref name="destination"/>.
- /// </summary>
- /// <returns>
- /// The number of bytes written to <paramref name="destination"/>, or -1 if <paramref name="destination"/>
- /// is not large enough to hold the result of the case conversion operation.
- /// </returns>
- /// <remarks>
- /// The original <see cref="Utf8Span"/> is left unchanged by this operation. Note that the the required
- /// length of <paramref name="destination"/> may be longer or shorter (in terms of UTF-8 byte count)
- /// than the input <see cref="Utf8Span"/>.
- /// </remarks>
- public int ToLower(Span<byte> destination, CultureInfo culture)
- {
- // TODO_UTF8STRING: Avoid intermediate allocations.
-
- if (culture is null)
- {
- ThrowHelper.ThrowArgumentNullException(ExceptionArgument.culture);
- }
-
- ReadOnlySpan<char> asLower = this.ToString().ToLower(culture).AsSpan();
- OperationStatus status = Utf8.FromUtf16(asLower, destination, out int _, out int bytesWritten, replaceInvalidSequences: false, isFinalBlock: true);
-
- Debug.Assert(status == OperationStatus.Done || status == OperationStatus.DestinationTooSmall, "ToLower shouldn't have produced malformed Unicode string.");
-
- if (status != OperationStatus.Done)
- {
- bytesWritten = -1; // "destination too small"
- }
-
- return bytesWritten;
- }
-
- /// <summary>
- /// Returns a new <see cref="Utf8String"/> instance which represents this <see cref="Utf8Span"/> instance
- /// converted to lowercase using the invariant culture.
- /// </summary>
- /// <remarks>
- /// The original <see cref="Utf8Span"/> is left unchanged by this operation. For more information on the
- /// invariant culture, see the <see cref="CultureInfo.InvariantCulture"/> property. Note that the returned
- /// <see cref="Utf8String"/> instance may be longer or shorter (in terms of UTF-8 byte count) than the
- /// input <see cref="Utf8Span"/>.
- /// </remarks>
- public Utf8String ToLowerInvariant()
- {
- // TODO_UTF8STRING: Avoid intermediate allocations.
-
- return new Utf8String(this.ToString().ToLowerInvariant());
- }
-
- /// <summary>
- /// Converts this <see cref="Utf8Span"/> to lowercase using the invariant culture, writing the
- /// UTF-8 result to the buffer <paramref name="destination"/>.
- /// </summary>
- /// <returns>
- /// The number of bytes written to <paramref name="destination"/>, or -1 if <paramref name="destination"/>
- /// is not large enough to hold the result of the case conversion operation.
- /// </returns>
- /// <remarks>
- /// The original <see cref="Utf8Span"/> is left unchanged by this operation. For more information on the
- /// invariant culture, see the <see cref="CultureInfo.InvariantCulture"/> property. Note that the the required
- /// length of <paramref name="destination"/> may be longer or shorter (in terms of UTF-8 byte count)
- /// than the input <see cref="Utf8Span"/>.
- /// </remarks>
- public int ToLowerInvariant(Span<byte> destination)
- {
- // TODO_UTF8STRING: Avoid intermediate allocations.
-
- ReadOnlySpan<char> asLowerInvariant = this.ToString().ToLowerInvariant().AsSpan();
- OperationStatus status = Utf8.FromUtf16(asLowerInvariant, destination, out int _, out int bytesWritten, replaceInvalidSequences: false, isFinalBlock: true);
-
- Debug.Assert(status == OperationStatus.Done || status == OperationStatus.DestinationTooSmall, "ToLowerInvariant shouldn't have produced malformed Unicode string.");
-
- if (status != OperationStatus.Done)
- {
- bytesWritten = -1; // "destination too small"
- }
-
- return bytesWritten;
- }
-
- /// <summary>
- /// Returns a new <see cref="Utf8String"/> instance which represents this <see cref="Utf8Span"/> instance
- /// converted to uppercase using <paramref name="culture"/>.
- /// </summary>
- /// <remarks>
- /// The original <see cref="Utf8Span"/> is left unchanged by this operation. Note that the returned
- /// <see cref="Utf8String"/> instance may be longer or shorter (in terms of UTF-8 byte count) than the
- /// input <see cref="Utf8Span"/>.
- /// </remarks>
- public Utf8String ToUpper(CultureInfo culture)
- {
- // TODO_UTF8STRING: Avoid intermediate allocations.
-
- if (culture is null)
- {
- ThrowHelper.ThrowArgumentNullException(ExceptionArgument.culture);
- }
-
- return new Utf8String(this.ToString().ToUpper(culture));
- }
-
- /// <summary>
- /// Converts this <see cref="Utf8Span"/> to uppercase using <paramref name="culture"/>, writing the
- /// UTF-8 result to the buffer <paramref name="destination"/>.
- /// </summary>
- /// <returns>
- /// The number of bytes written to <paramref name="destination"/>, or -1 if <paramref name="destination"/>
- /// is not large enough to hold the result of the case conversion operation.
- /// </returns>
- /// <remarks>
- /// The original <see cref="Utf8Span"/> is left unchanged by this operation. Note that the the required
- /// length of <paramref name="destination"/> may be longer or shorter (in terms of UTF-8 byte count)
- /// than the input <see cref="Utf8Span"/>.
- /// </remarks>
- public int ToUpper(Span<byte> destination, CultureInfo culture)
- {
- // TODO_UTF8STRING: Avoid intermediate allocations.
-
- if (culture is null)
- {
- ThrowHelper.ThrowArgumentNullException(ExceptionArgument.culture);
- }
-
- ReadOnlySpan<char> asUpper = this.ToString().ToUpper(culture).AsSpan();
- OperationStatus status = Utf8.FromUtf16(asUpper, destination, out int _, out int bytesWritten, replaceInvalidSequences: false, isFinalBlock: true);
-
- Debug.Assert(status == OperationStatus.Done || status == OperationStatus.DestinationTooSmall, "ToUpper shouldn't have produced malformed Unicode string.");
-
- if (status != OperationStatus.Done)
- {
- bytesWritten = -1; // "destination too small"
- }
-
- return bytesWritten;
- }
-
- /// <summary>
- /// Returns a new <see cref="Utf8String"/> instance which represents this <see cref="Utf8Span"/> instance
- /// converted to uppercase using the invariant culture.
- /// </summary>
- /// <remarks>
- /// The original <see cref="Utf8Span"/> is left unchanged by this operation. For more information on the
- /// invariant culture, see the <see cref="CultureInfo.InvariantCulture"/> property. Note that the returned
- /// <see cref="Utf8String"/> instance may be longer or shorter (in terms of UTF-8 byte count) than the
- /// input <see cref="Utf8Span"/>.
- /// </remarks>
- public Utf8String ToUpperInvariant()
- {
- // TODO_UTF8STRING: Avoid intermediate allocations.
-
- return new Utf8String(this.ToString().ToUpperInvariant());
- }
-
- /// <summary>
- /// Converts this <see cref="Utf8Span"/> to uppercase using the invariant culture, writing the
- /// UTF-8 result to the buffer <paramref name="destination"/>.
- /// </summary>
- /// <returns>
- /// The number of bytes written to <paramref name="destination"/>, or -1 if <paramref name="destination"/>
- /// is not large enough to hold the result of the case conversion operation.
- /// </returns>
- /// <remarks>
- /// The original <see cref="Utf8Span"/> is left unchanged by this operation. For more information on the
- /// invariant culture, see the <see cref="CultureInfo.InvariantCulture"/> property. Note that the the required
- /// length of <paramref name="destination"/> may be longer or shorter (in terms of UTF-8 byte count)
- /// than the input <see cref="Utf8Span"/>.
- /// </remarks>
- public int ToUpperInvariant(Span<byte> destination)
- {
- // TODO_UTF8STRING: Avoid intermediate allocations.
-
- ReadOnlySpan<char> asUpperInvariant = this.ToString().ToUpperInvariant().AsSpan();
- OperationStatus status = Utf8.FromUtf16(asUpperInvariant, destination, out int _, out int bytesWritten, replaceInvalidSequences: false, isFinalBlock: true);
-
- Debug.Assert(status == OperationStatus.Done || status == OperationStatus.DestinationTooSmall, "ToUpperInvariant shouldn't have produced malformed Unicode string.");
-
- if (status != OperationStatus.Done)
- {
- bytesWritten = -1; // "destination too small"
- }
-
- return bytesWritten;
- }
- }
-}
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System.Buffers;
-using System.Diagnostics;
-
-namespace System.Text
-{
- public readonly ref partial struct Utf8Span
- {
- public CharEnumerable Chars => new CharEnumerable(this);
- public RuneEnumerable Runes => new RuneEnumerable(this);
-
- public readonly ref struct CharEnumerable
- {
- private readonly Utf8Span _span;
-
- internal CharEnumerable(Utf8Span span)
- {
- _span = span;
- }
-
- public Enumerator GetEnumerator() => new Enumerator(_span);
-
- public ref struct Enumerator
- {
- private uint _currentCharPair;
- private ReadOnlySpan<byte> _remainingUtf8Bytes;
-
- internal Enumerator(Utf8Span span)
- {
- _currentCharPair = default;
- _remainingUtf8Bytes = span.Bytes;
- }
-
- public char Current => (char)_currentCharPair;
-
- public bool MoveNext()
- {
- // We don't need to worry about tearing since this enumerator is a ref struct.
-
- if (_currentCharPair > char.MaxValue)
- {
- // There was a surrogate pair smuggled in here from a previous operation.
- // Shift out the high surrogate value and return immediately.
-
- _currentCharPair >>= 16;
- return true;
- }
-
- if (_remainingUtf8Bytes.IsEmpty)
- {
- return false;
- }
-
- // TODO_UTF8STRING: Since we assume Utf8String instances are well-formed, we may instead
- // call an optimized version of the "decode" routine below which skips well-formedness checks.
-
- OperationStatus status = Rune.DecodeFromUtf8(_remainingUtf8Bytes, out Rune currentRune, out int bytesConsumed);
- Debug.Assert(status == OperationStatus.Done, "Somebody fed us invalid data?");
-
- if (currentRune.IsBmp)
- {
- // Common case - BMP scalar value.
-
- _currentCharPair = (uint)currentRune.Value;
- }
- else
- {
- // Uncommon case - supplementary plane (astral) scalar value.
- // We'll smuggle the two UTF-16 code units into a single 32-bit value,
- // with the leading surrogate packed into the low 16 bits of the value,
- // and the trailing surrogate packed into the high 16 bits of the value.
-
- UnicodeUtility.GetUtf16SurrogatesFromSupplementaryPlaneScalar((uint)currentRune.Value, out char leadingCodeUnit, out char trailingCodeUnit);
- _currentCharPair = (uint)leadingCodeUnit + ((uint)trailingCodeUnit << 16);
- }
-
- // TODO_UTF8STRING: We can consider unsafe slicing below if we wish since we know we're
- // not going to overrun the end of the span.
-
- _remainingUtf8Bytes = _remainingUtf8Bytes.Slice(bytesConsumed);
- return true;
- }
- }
- }
-
- public readonly ref struct RuneEnumerable
- {
- private readonly Utf8Span _span;
-
- internal RuneEnumerable(Utf8Span span)
- {
- _span = span;
- }
-
- public Enumerator GetEnumerator() => new Enumerator(_span);
-
- public ref struct Enumerator
- {
- private Rune _currentRune;
- private ReadOnlySpan<byte> _remainingUtf8Bytes;
-
- internal Enumerator(Utf8Span span)
- {
- _currentRune = default;
- _remainingUtf8Bytes = span.Bytes;
- }
-
- public Rune Current => _currentRune;
-
- public bool MoveNext()
- {
- // We don't need to worry about tearing since this enumerator is a ref struct.
-
- if (_remainingUtf8Bytes.IsEmpty)
- {
- return false;
- }
-
- // TODO_UTF8STRING: Since we assume Utf8Span instances are well-formed, we may instead
- // call an optimized version of the "decode" routine below which skips well-formedness checks.
-
- OperationStatus status = Rune.DecodeFromUtf8(_remainingUtf8Bytes, out _currentRune, out int bytesConsumed);
- Debug.Assert(status == OperationStatus.Done, "Somebody fed us invalid data?");
-
- // TODO_UTF8STRING: We can consider unsafe slicing below if we wish since we know we're
- // not going to overrun the end of the span.
-
- _remainingUtf8Bytes = _remainingUtf8Bytes.Slice(bytesConsumed);
- return true;
- }
- }
- }
- }
-}
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System.ComponentModel;
-using System.Diagnostics;
-using System.Runtime.InteropServices;
-using System.Text.Unicode;
-
-namespace System.Text
-{
- public readonly ref partial struct Utf8Span
- {
- public SplitResult Split(char separator, Utf8StringSplitOptions options = Utf8StringSplitOptions.None)
- {
- if (!Rune.TryCreate(separator, out Rune rune))
- {
- throw new ArgumentOutOfRangeException(
- paramName: nameof(separator),
- message: SR.ArgumentOutOfRange_Utf16SurrogatesDisallowed);
- }
-
- Utf8String.CheckSplitOptions(options);
-
- return new SplitResult(this, rune, options);
- }
-
- public SplitResult Split(Rune separator, Utf8StringSplitOptions options = Utf8StringSplitOptions.None)
- {
- Utf8String.CheckSplitOptions(options);
-
- return new SplitResult(this, separator, options);
- }
-
- public SplitResult Split(Utf8Span separator, Utf8StringSplitOptions options = Utf8StringSplitOptions.None)
- {
- if (separator.IsEmpty)
- {
- throw new ArgumentException(
- paramName: nameof(separator),
- message: SR.Argument_CannotBeEmptySpan);
- }
-
- Utf8String.CheckSplitOptions(options);
-
- return new SplitResult(this, separator, options);
- }
-
- /// <summary>
- /// Locates <paramref name="separator"/> within this <see cref="Utf8Span"/> instance, creating <see cref="Utf8Span"/>
- /// instances which represent the data on either side of the separator. If <paramref name="separator"/> is not found
- /// within this <see cref="Utf8Span"/> instance, returns the tuple "(this, Empty)".
- /// </summary>
- /// <remarks>
- /// An ordinal search is performed.
- /// </remarks>
- public SplitOnResult SplitOn(char separator)
- {
- return TryFind(separator, out Range range) ? new SplitOnResult(this, range) : new SplitOnResult(this);
- }
-
- /// <summary>
- /// Locates <paramref name="separator"/> within this <see cref="Utf8Span"/> instance, creating <see cref="Utf8Span"/>
- /// instances which represent the data on either side of the separator. If <paramref name="separator"/> is not found
- /// within this <see cref="Utf8Span"/> instance, returns the tuple "(this, Empty)".
- /// </summary>
- /// <remarks>
- /// The search is performed using the specified <paramref name="comparisonType"/>.
- /// </remarks>
- public SplitOnResult SplitOn(char separator, StringComparison comparisonType)
- {
- return TryFind(separator, comparisonType, out Range range) ? new SplitOnResult(this, range) : new SplitOnResult(this);
- }
-
- /// <summary>
- /// Locates <paramref name="separator"/> within this <see cref="Utf8Span"/> instance, creating <see cref="Utf8Span"/>
- /// instances which represent the data on either side of the separator. If <paramref name="separator"/> is not found
- /// within this <see cref="Utf8Span"/> instance, returns the tuple "(this, Empty)".
- /// </summary>
- /// <remarks>
- /// An ordinal search is performed.
- /// </remarks>
- public SplitOnResult SplitOn(Rune separator)
- {
- return TryFind(separator, out Range range) ? new SplitOnResult(this, range) : new SplitOnResult(this);
- }
-
- /// <summary>
- /// Locates <paramref name="separator"/> within this <see cref="Utf8Span"/> instance, creating <see cref="Utf8Span"/>
- /// instances which represent the data on either side of the separator. If <paramref name="separator"/> is not found
- /// within this <see cref="Utf8Span"/> instance, returns the tuple "(this, Empty)".
- /// </summary>
- /// <remarks>
- /// The search is performed using the specified <paramref name="comparisonType"/>.
- /// </remarks>
- public SplitOnResult SplitOn(Rune separator, StringComparison comparisonType)
- {
- return TryFind(separator, comparisonType, out Range range) ? new SplitOnResult(this, range) : new SplitOnResult(this);
- }
-
- /// <summary>
- /// Locates <paramref name="separator"/> within this <see cref="Utf8Span"/> instance, creating <see cref="Utf8Span"/>
- /// instances which represent the data on either side of the separator. If <paramref name="separator"/> is not found
- /// within this <see cref="Utf8Span"/> instance, returns the tuple "(this, Empty)".
- /// </summary>
- /// <remarks>
- /// An ordinal search is performed.
- /// </remarks>
- public SplitOnResult SplitOn(Utf8Span separator)
- {
- return TryFind(separator, out Range range) ? new SplitOnResult(this, range) : new SplitOnResult(this);
- }
-
- /// <summary>
- /// Locates the last occurrence of <paramref name="separator"/> within this <see cref="Utf8Span"/> instance, creating <see cref="Utf8Span"/>
- /// instances which represent the data on either side of the separator. If <paramref name="separator"/> is not found
- /// within this <see cref="Utf8Span"/> instance, returns the tuple "(this, Empty)".
- /// </summary>
- /// <remarks>
- /// The search is performed using the specified <paramref name="comparisonType"/>.
- /// </remarks>
- public SplitOnResult SplitOn(Utf8Span separator, StringComparison comparisonType)
- {
- return TryFind(separator, comparisonType, out Range range) ? new SplitOnResult(this, range) : new SplitOnResult(this);
- }
-
- /// <summary>
- /// Locates the last occurrence of <paramref name="separator"/> within this <see cref="Utf8Span"/> instance, creating <see cref="Utf8Span"/>
- /// instances which represent the data on either side of the separator. If <paramref name="separator"/> is not found
- /// within this <see cref="Utf8Span"/> instance, returns the tuple "(this, Empty)".
- /// </summary>
- /// <remarks>
- /// An ordinal search is performed.
- /// </remarks>
- public SplitOnResult SplitOnLast(char separator)
- {
- return TryFindLast(separator, out Range range) ? new SplitOnResult(this, range) : new SplitOnResult(this);
- }
-
- /// <summary>
- /// Locates the last occurrence of <paramref name="separator"/> within this <see cref="Utf8Span"/> instance, creating <see cref="Utf8Span"/>
- /// instances which represent the data on either side of the separator. If <paramref name="separator"/> is not found
- /// within this <see cref="Utf8Span"/> instance, returns the tuple "(this, Empty)".
- /// </summary>
- /// <remarks>
- /// The search is performed using the specified <paramref name="comparisonType"/>.
- /// </remarks>
- public SplitOnResult SplitOnLast(char separator, StringComparison comparisonType)
- {
- return TryFindLast(separator, comparisonType, out Range range) ? new SplitOnResult(this, range) : new SplitOnResult(this);
- }
-
- /// <summary>
- /// Locates the last occurrence of <paramref name="separator"/> within this <see cref="Utf8Span"/> instance, creating <see cref="Utf8Span"/>
- /// instances which represent the data on either side of the separator. If <paramref name="separator"/> is not found
- /// within this <see cref="Utf8Span"/> instance, returns the tuple "(this, Empty)".
- /// </summary>
- /// <remarks>
- /// An ordinal search is performed.
- /// </remarks>
- public SplitOnResult SplitOnLast(Rune separator)
- {
- return TryFindLast(separator, out Range range) ? new SplitOnResult(this, range) : new SplitOnResult(this);
- }
-
- /// <summary>
- /// Locates the last occurrence of <paramref name="separator"/> within this <see cref="Utf8Span"/> instance, creating <see cref="Utf8Span"/>
- /// instances which represent the data on either side of the separator. If <paramref name="separator"/> is not found
- /// within this <see cref="Utf8Span"/> instance, returns the tuple "(this, Empty)".
- /// </summary>
- /// <remarks>
- /// The search is performed using the specified <paramref name="comparisonType"/>.
- /// </remarks>
- public SplitOnResult SplitOnLast(Rune separator, StringComparison comparisonType)
- {
- return TryFindLast(separator, comparisonType, out Range range) ? new SplitOnResult(this, range) : new SplitOnResult(this);
- }
-
- /// <summary>
- /// Locates the last occurrence of <paramref name="separator"/> within this <see cref="Utf8Span"/> instance, creating <see cref="Utf8Span"/>
- /// instances which represent the data on either side of the separator. If <paramref name="separator"/> is not found
- /// within this <see cref="Utf8Span"/> instance, returns the tuple "(this, Empty)".
- /// </summary>
- /// <remarks>
- /// An ordinal search is performed.
- /// </remarks>
- public SplitOnResult SplitOnLast(Utf8Span separator)
- {
- return TryFindLast(separator, out Range range) ? new SplitOnResult(this, range) : new SplitOnResult(this);
- }
-
- /// <summary>
- /// Locates the last occurrence of <paramref name="separator"/> within this <see cref="Utf8Span"/> instance, creating <see cref="Utf8Span"/>
- /// instances which represent the data on either side of the separator. If <paramref name="separator"/> is not found
- /// within this <see cref="Utf8Span"/> instance, returns the tuple "(this, Empty)".
- /// </summary>
- /// <remarks>
- /// The search is performed using the specified <paramref name="comparisonType"/>.
- /// </remarks>
- public SplitOnResult SplitOnLast(Utf8Span separator, StringComparison comparisonType)
- {
- return TryFindLast(separator, comparisonType, out Range range) ? new SplitOnResult(this, range) : new SplitOnResult(this);
- }
-
- /// <summary>
- /// Trims whitespace from the beginning and the end of this <see cref="Utf8Span"/>,
- /// returning a new <see cref="Utf8Span"/> containing the resulting slice.
- /// </summary>
- public Utf8Span Trim() => TrimHelper(TrimType.Both);
-
- /// <summary>
- /// Trims whitespace from only the end of this <see cref="Utf8Span"/>,
- /// returning a new <see cref="Utf8Span"/> containing the resulting slice.
- /// </summary>
- public Utf8Span TrimEnd() => TrimHelper(TrimType.Tail);
-
- internal Utf8Span TrimHelper(TrimType trimType)
- {
- ReadOnlySpan<byte> retSpan = Bytes;
-
- if ((trimType & TrimType.Head) != 0)
- {
- int indexOfFirstNonWhiteSpaceChar = Utf8Utility.GetIndexOfFirstNonWhiteSpaceChar(retSpan);
- Debug.Assert((uint)indexOfFirstNonWhiteSpaceChar <= (uint)retSpan.Length);
-
- // TODO_UTF8STRING: Can use an unsafe slicing routine below if we need a perf boost.
-
- retSpan = retSpan.Slice(indexOfFirstNonWhiteSpaceChar);
- }
-
- if ((trimType & TrimType.Tail) != 0)
- {
- int indexOfTrailingWhiteSpaceSequence = Utf8Utility.GetIndexOfTrailingWhiteSpaceSequence(retSpan);
- Debug.Assert((uint)indexOfTrailingWhiteSpaceSequence <= (uint)retSpan.Length);
-
- // TODO_UTF8STRING: Can use an unsafe slicing routine below if we need a perf boost.
-
- retSpan = retSpan.Slice(0, indexOfTrailingWhiteSpaceSequence);
- }
-
- return UnsafeCreateWithoutValidation(retSpan);
- }
-
- /// <summary>
- /// Trims whitespace from only the beginning of this <see cref="Utf8Span"/>,
- /// returning a new <see cref="Utf8Span"/> containing the resulting slice.
- /// </summary>
- public Utf8Span TrimStart() => TrimHelper(TrimType.Head);
-
- [StructLayout(LayoutKind.Auto)]
- public readonly ref struct SplitResult
- {
- private readonly State _state;
-
- internal SplitResult(Utf8Span source, Rune searchRune, Utf8StringSplitOptions splitOptions)
- {
- _state = new State
- {
- RemainingSearchSpace = source,
- SearchRune = searchRune.Value,
- SearchTerm = default,
- SplitOptions = splitOptions
- };
- }
-
- internal SplitResult(Utf8Span source, Utf8Span searchTerm, Utf8StringSplitOptions splitOptions)
- {
- _state = new State
- {
- RemainingSearchSpace = source,
- SearchRune = -1,
- SearchTerm = searchTerm,
- SplitOptions = splitOptions
- };
- }
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- public void Deconstruct(out Utf8Span item1, out Utf8Span item2)
- {
- _state.DeconstructHelper(in _state.RemainingSearchSpace, out item1, out item2);
- TrimIfNeeded(ref item2);
- }
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- public void Deconstruct(out Utf8Span item1, out Utf8Span item2, out Utf8Span item3)
- {
- _state.DeconstructHelper(in _state.RemainingSearchSpace, out item1, out Utf8Span remainder);
- _state.DeconstructHelper(in remainder, out item2, out item3);
- TrimIfNeeded(ref item3);
- }
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- public void Deconstruct(out Utf8Span item1, out Utf8Span item2, out Utf8Span item3, out Utf8Span item4)
- {
- _state.DeconstructHelper(in _state.RemainingSearchSpace, out item1, out Utf8Span remainder);
- _state.DeconstructHelper(in remainder, out item2, out remainder);
- _state.DeconstructHelper(in remainder, out item3, out item4);
- TrimIfNeeded(ref item4);
- }
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- public void Deconstruct(out Utf8Span item1, out Utf8Span item2, out Utf8Span item3, out Utf8Span item4, out Utf8Span item5)
- {
- _state.DeconstructHelper(in _state.RemainingSearchSpace, out item1, out Utf8Span remainder);
- _state.DeconstructHelper(in remainder, out item2, out remainder);
- _state.DeconstructHelper(in remainder, out item3, out remainder);
- _state.DeconstructHelper(in remainder, out item4, out item5);
- TrimIfNeeded(ref item5);
- }
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- public void Deconstruct(out Utf8Span item1, out Utf8Span item2, out Utf8Span item3, out Utf8Span item4, out Utf8Span item5, out Utf8Span item6)
- {
- _state.DeconstructHelper(in _state.RemainingSearchSpace, out item1, out Utf8Span remainder);
- _state.DeconstructHelper(in remainder, out item2, out remainder);
- _state.DeconstructHelper(in remainder, out item3, out remainder);
- _state.DeconstructHelper(in remainder, out item4, out remainder);
- _state.DeconstructHelper(in remainder, out item5, out item6);
- TrimIfNeeded(ref item6);
- }
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- public void Deconstruct(out Utf8Span item1, out Utf8Span item2, out Utf8Span item3, out Utf8Span item4, out Utf8Span item5, out Utf8Span item6, out Utf8Span item7)
- {
- _state.DeconstructHelper(in _state.RemainingSearchSpace, out item1, out Utf8Span remainder);
- _state.DeconstructHelper(in remainder, out item2, out remainder);
- _state.DeconstructHelper(in remainder, out item3, out remainder);
- _state.DeconstructHelper(in remainder, out item4, out remainder);
- _state.DeconstructHelper(in remainder, out item5, out remainder);
- _state.DeconstructHelper(in remainder, out item6, out item7);
- TrimIfNeeded(ref item7);
- }
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- public void Deconstruct(out Utf8Span item1, out Utf8Span item2, out Utf8Span item3, out Utf8Span item4, out Utf8Span item5, out Utf8Span item6, out Utf8Span item7, out Utf8Span item8)
- {
- _state.DeconstructHelper(in _state.RemainingSearchSpace, out item1, out Utf8Span remainder);
- _state.DeconstructHelper(in remainder, out item2, out remainder);
- _state.DeconstructHelper(in remainder, out item3, out remainder);
- _state.DeconstructHelper(in remainder, out item4, out remainder);
- _state.DeconstructHelper(in remainder, out item5, out remainder);
- _state.DeconstructHelper(in remainder, out item6, out remainder);
- _state.DeconstructHelper(in remainder, out item7, out item8);
- TrimIfNeeded(ref item8);
- }
-
- public Enumerator GetEnumerator() => new Enumerator(this);
-
- private void TrimIfNeeded(ref Utf8Span span)
- {
- if ((_state.SplitOptions & Utf8StringSplitOptions.TrimEntries) != 0)
- {
- span = span.Trim();
- }
- }
-
- [StructLayout(LayoutKind.Auto)]
- public ref struct Enumerator
- {
- private const Utf8StringSplitOptions HALT_ENUMERATION = (Utf8StringSplitOptions)int.MinValue;
-
- private Utf8Span _current;
- private State _state;
-
- internal Enumerator(SplitResult result)
- {
- _current = default;
- _state = result._state; // copy by value
- }
-
- public Utf8Span Current => _current;
-
- public bool MoveNext()
- {
- // Happy path: if the search term was found, then the two 'out' fields below are overwritten with
- // the contents of the (before, after) tuple, and we can return right away.
-
- if (_state.DeconstructHelper(in _state.RemainingSearchSpace, out _current, out _state.RemainingSearchSpace))
- {
- return true;
- }
-
- // At this point, the search term was not found within the search space. '_current' contains the last
- // bit of data after the final occurrence of the search term. We'll also set a flag saying that we've
- // completed enumeration.
-
- if (_current.IsEmpty && (_state.SplitOptions & Utf8StringSplitOptions.RemoveEmptyEntries) != 0)
- {
- return false;
- }
-
- if ((_state.SplitOptions & HALT_ENUMERATION) != 0)
- {
- return false;
- }
-
- _state.SplitOptions |= HALT_ENUMERATION; // prevents yielding <empty> forever at end of split
-
- return true;
- }
- }
-
- [StructLayout(LayoutKind.Auto)]
- private ref struct State // fully mutable
- {
- internal Utf8Span RemainingSearchSpace;
- internal int SearchRune; // -1 if not specified, takes less space than "Rune?"
- internal Utf8Span SearchTerm;
- internal Utf8StringSplitOptions SplitOptions;
-
- // Returns 'true' if a match was found, 'false' otherwise.
- internal readonly bool DeconstructHelper(in Utf8Span source, out Utf8Span firstItem, out Utf8Span remainder)
- {
- // n.b. Our callers might pass the same reference for 'source' and 'remainder'.
- // We need to take care not to read 'source' after writing 'remainder'.
-
- bool wasMatchFound;
- ref readonly Utf8Span searchSpan = ref source;
-
- while (true)
- {
- if (searchSpan.IsEmpty)
- {
- firstItem = searchSpan;
- remainder = default;
- wasMatchFound = false;
- break;
- }
-
- Range matchRange;
-
- if (SearchRune >= 0)
- {
-#if NETCOREAPP3_0
- wasMatchFound = searchSpan.TryFind(new Rune((uint)SearchRune), out matchRange);
-#else
- wasMatchFound = searchSpan.TryFind(Rune.UnsafeCreate((uint)SearchRune), out matchRange);
-#endif
- }
- else
- {
- wasMatchFound = searchSpan.TryFind(SearchTerm, out matchRange);
- }
-
- if (!wasMatchFound)
- {
- // If no match was found, we move 'source' to 'firstItem', trim if necessary, and return right away.
-
- firstItem = searchSpan;
-
- if ((SplitOptions & Utf8StringSplitOptions.TrimEntries) != 0)
- {
- firstItem = firstItem.Trim();
- }
-
- remainder = default;
- }
- else
- {
- // Otherwise, if a match was found, split the result across 'firstItem' and 'remainder',
- // applying trimming if necessary.
-
- firstItem = searchSpan[..matchRange.Start]; // TODO_UTF8STRING: Could use unsafe slicing as optimization
- remainder = searchSpan[matchRange.End..]; // TODO_UTF8STRING: Could use unsafe slicing as optimization
-
- if ((SplitOptions & Utf8StringSplitOptions.TrimEntries) != 0)
- {
- firstItem = firstItem.Trim();
- }
-
- // If we're asked to remove empty entries, loop until there's a real value in 'firstItem'.
-
- if ((SplitOptions & Utf8StringSplitOptions.RemoveEmptyEntries) != 0 && firstItem.IsEmpty)
- {
- searchSpan = ref remainder;
- continue;
- }
- }
-
- break; // loop only if explicit 'continue' statement was hit
- }
-
- return wasMatchFound;
- }
- }
- }
-
- [StructLayout(LayoutKind.Auto)]
- public readonly ref struct SplitOnResult
- {
- // Used when there is no match.
- internal SplitOnResult(Utf8Span originalSearchSpace)
- {
- Before = originalSearchSpace;
- After = Empty;
- }
-
- // Used when a match is found.
- internal SplitOnResult(Utf8Span originalSearchSpace, Range searchTermMatchRange)
- {
- (int startIndex, int length) = searchTermMatchRange.GetOffsetAndLength(originalSearchSpace.Length);
-
- // TODO_UTF8STRING: The below indexer performs correctness checks. We can skip these checks (and even the
- // bounds checks more generally) since we know the inputs are all valid and the containing struct is not
- // subject to tearing.
-
- Before = originalSearchSpace[..startIndex];
- After = originalSearchSpace[(startIndex + length)..];
- }
-
- public Utf8Span After { get; }
- public Utf8Span Before { get; }
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- public void Deconstruct(out Utf8Span before, out Utf8Span after)
- {
- before = Before;
- after = After;
- }
- }
- }
-}
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System.Diagnostics;
-using System.Globalization;
-using System.Text.Unicode;
-
-namespace System.Text
-{
- public readonly ref partial struct Utf8Span
- {
- /// <summary>
- /// Attempts to locate the target <paramref name="value"/> within this <see cref="Utf8Span"/> instance.
- /// If <paramref name="value"/> is found, returns <see langword="true"/> and sets <paramref name="range"/> to
- /// the location where <paramref name="value"/> occurs within this <see cref="Utf8Span"/> instance.
- /// If <paramref name="value"/> is not found, returns <see langword="false"/> and sets <paramref name="range"/>
- /// to <see langword="default"/>.
- /// </summary>
- /// <remarks>
- /// An ordinal search is performed.
- /// </remarks>
- public bool TryFind(char value, out Range range)
- {
- if (Rune.TryCreate(value, out Rune rune))
- {
- return TryFind(rune, out range);
- }
- else
- {
- // Surrogate chars can't exist in well-formed UTF-8 data - bail immediately.
-
- range = default;
- return false;
- }
- }
-
- /// <summary>
- /// Attempts to locate the target <paramref name="value"/> within this <see cref="Utf8Span"/> instance.
- /// If <paramref name="value"/> is found, returns <see langword="true"/> and sets <paramref name="range"/> to
- /// the location where <paramref name="value"/> occurs within this <see cref="Utf8Span"/> instance.
- /// If <paramref name="value"/> is not found, returns <see langword="false"/> and sets <paramref name="range"/>
- /// to <see langword="default"/>.
- /// </summary>
- /// <remarks>
- /// The search is performed using the specified <paramref name="comparisonType"/>.
- /// </remarks>
- public bool TryFind(char value, StringComparison comparisonType, out Range range)
- {
- if (Rune.TryCreate(value, out Rune rune))
- {
- return TryFind(rune, comparisonType, out range);
- }
- else
- {
- CheckStringComparison(comparisonType);
-
- // Surrogate chars can't exist in well-formed UTF-8 data - bail immediately.
-
- range = default;
- return false;
- }
- }
-
- /// <summary>
- /// Attempts to locate the target <paramref name="value"/> within this <see cref="Utf8Span"/> instance.
- /// If <paramref name="value"/> is found, returns <see langword="true"/> and sets <paramref name="range"/> to
- /// the location where <paramref name="value"/> occurs within this <see cref="Utf8Span"/> instance.
- /// If <paramref name="value"/> is not found, returns <see langword="false"/> and sets <paramref name="range"/>
- /// to <see langword="default"/>.
- /// </summary>
- /// <remarks>
- /// An ordinal search is performed.
- /// </remarks>
- public bool TryFind(Rune value, out Range range)
- {
- if (value.IsAscii)
- {
- // Special-case ASCII since it's a simple single byte search.
-
- int idx = Bytes.IndexOf((byte)value.Value);
- if (idx < 0)
- {
- range = default;
- return false;
- }
- else
- {
- range = idx..(idx + 1);
- return true;
- }
- }
- else
- {
- // Slower path: need to search a multi-byte sequence.
- // TODO_UTF8STRING: As an optimization, we could use unsafe APIs below since we
- // know Rune instances are well-formed and slicing is safe.
-
- Span<byte> runeBytes = stackalloc byte[Utf8Utility.MaxBytesPerScalar];
- int utf8ByteLengthOfRune = value.EncodeToUtf8(runeBytes);
-
- return TryFind(UnsafeCreateWithoutValidation(runeBytes.Slice(0, utf8ByteLengthOfRune)), out range);
- }
- }
-
- /// <summary>
- /// Attempts to locate the target <paramref name="value"/> within this <see cref="Utf8Span"/> instance.
- /// If <paramref name="value"/> is found, returns <see langword="true"/> and sets <paramref name="range"/> to
- /// the location where <paramref name="value"/> occurs within this <see cref="Utf8Span"/> instance.
- /// If <paramref name="value"/> is not found, returns <see langword="false"/> and sets <paramref name="range"/>
- /// to <see langword="default"/>.
- /// </summary>
- /// <remarks>
- /// The search is performed using the specified <paramref name="comparisonType"/>.
- /// </remarks>
- public bool TryFind(Rune value, StringComparison comparisonType, out Range range)
- {
- if (comparisonType == StringComparison.Ordinal)
- {
- return TryFind(value, out range);
- }
- else
- {
- // Slower path: not an ordinal comparison.
- // TODO_UTF8STRING: As an optimization, we could use unsafe APIs below since we
- // know Rune instances are well-formed and slicing is safe.
-
- Span<byte> runeBytes = stackalloc byte[Utf8Utility.MaxBytesPerScalar];
- int utf8ByteLengthOfRune = value.EncodeToUtf8(runeBytes);
-
- return TryFind(UnsafeCreateWithoutValidation(runeBytes.Slice(0, utf8ByteLengthOfRune)), comparisonType, out range);
- }
- }
-
- /// <summary>
- /// Attempts to locate the target <paramref name="value"/> within this <see cref="Utf8Span"/> instance.
- /// If <paramref name="value"/> is found, returns <see langword="true"/> and sets <paramref name="range"/> to
- /// the location where <paramref name="value"/> occurs within this <see cref="Utf8Span"/> instance.
- /// If <paramref name="value"/> is not found, returns <see langword="false"/> and sets <paramref name="range"/>
- /// to <see langword="default"/>.
- /// </summary>
- /// <remarks>
- /// An ordinal search is performed.
- /// </remarks>
- public bool TryFind(Utf8Span value, out Range range)
- {
- int idx;
-
- if (value.Bytes.Length == 1)
- {
- // Special-case ASCII since it's a simple single byte search.
-
- idx = this.Bytes.IndexOf(value.Bytes[0]);
- }
- else
- {
- // Slower path: need to search a multi-byte sequence.
-
- idx = this.Bytes.IndexOf(value.Bytes);
- }
-
- if (idx < 0)
- {
- range = default;
- return false;
- }
- else
- {
- range = idx..(idx + value.Bytes.Length);
- return true;
- }
- }
-
- /// <summary>
- /// Attempts to locate the target <paramref name="value"/> within this <see cref="Utf8Span"/> instance.
- /// If <paramref name="value"/> is found, returns <see langword="true"/> and sets <paramref name="range"/> to
- /// the location where <paramref name="value"/> occurs within this <see cref="Utf8Span"/> instance.
- /// If <paramref name="value"/> is not found, returns <see langword="false"/> and sets <paramref name="range"/>
- /// to <see langword="default"/>.
- /// </summary>
- /// <remarks>
- /// The search is performed using the specified <paramref name="comparisonType"/>.
- /// </remarks>
- public bool TryFind(Utf8Span value, StringComparison comparisonType, out Range range) => TryFind(value, comparisonType, out range, fromBeginning: true);
-
- private unsafe bool TryFind(Utf8Span value, StringComparison comparisonType, out Range range, bool fromBeginning)
- {
- CheckStringComparison(comparisonType);
-
- if (value.IsEmpty)
- {
- // sourceString.IndexOf/LastIndexOf(term, comparer) should return the minimum/maximum value index
- // for which the expression "sourceString.Substring(index).StartsWith(term, comparer)" is true.
- // The range we return to the caller should reflect this so that they can pull out the correct index.
-
- if (fromBeginning)
- {
- range = Index.Start..Index.Start;
- }
- else
- {
- range = Index.End..Index.End;
- }
- return true;
- }
-
- if (this.IsEmpty)
- {
- range = default;
- return false;
- }
-
- CompareInfo compareInfo = default!; // will be overwritten if it matters
- CompareOptions compareOptions = GetCaseCompareOfComparisonCulture(comparisonType);
-
- if (GlobalizationMode.Invariant)
- {
- // In the Invariant globalization mode, all comparisons are normalized to Ordinal or OrdinalIgnoreCase,
- // and even in "ignore case" we only map [a-z] <-> [A-Z]. All other code points remain unmapped.
-
- // TODO_UTF8STRING: We should take advantage of the property described above to avoid the UTF-16
- // transcoding step entirely.
-
- if (compareOptions == CompareOptions.None)
- {
- return (fromBeginning)
- ? TryFind(value, out range)
- : TryFindLast(value, out range); // call the ordinal search routine
- }
- }
- else
- {
- switch (comparisonType)
- {
- case StringComparison.Ordinal:
- return (fromBeginning)
- ? TryFind(value, out range)
- : TryFindLast(value, out range);
-
- case StringComparison.OrdinalIgnoreCase:
- // TODO_UTF8STRING: Can probably optimize this case.
-#if SYSTEM_PRIVATE_CORELIB
- compareInfo = CompareInfo.Invariant;
-#else
- compareInfo = CultureInfo.InvariantCulture.CompareInfo;
-#endif
- break;
-
- case StringComparison.CurrentCulture:
- case StringComparison.CurrentCultureIgnoreCase:
- compareInfo = CultureInfo.CurrentCulture.CompareInfo;
- break;
-
- default:
- Debug.Assert(comparisonType == StringComparison.InvariantCulture || comparisonType == StringComparison.InvariantCultureIgnoreCase);
-#if SYSTEM_PRIVATE_CORELIB
- compareInfo = CompareInfo.Invariant;
-#else
- compareInfo = CultureInfo.InvariantCulture.CompareInfo;
-#endif
- break;
- }
- }
-
- // TODO_UTF8STRING: Remove allocations below, and try to avoid the transcoding step if possible.
-
- string thisTranscodedToUtf16 = this.ToStringNoReplacement();
- string otherTranscodedToUtf16 = value.ToStringNoReplacement();
-
- int idx, matchLength;
-
-#if SYSTEM_PRIVATE_CORELIB
- if (GlobalizationMode.Invariant)
- {
- // If we got here, it meant we're doing an OrdinalIgnoreCase comparison.
-
- Debug.Assert(compareOptions == CompareOptions.IgnoreCase);
-
- idx = CompareInfo.InvariantIndexOf(thisTranscodedToUtf16, otherTranscodedToUtf16, ignoreCase: true, fromBeginning);
- matchLength = otherTranscodedToUtf16.Length; // If there was a match, it involved only simple case folding.
- }
- else
- {
- idx = (fromBeginning)
- ? compareInfo.IndexOf(thisTranscodedToUtf16, otherTranscodedToUtf16, compareOptions, out matchLength)
- : compareInfo.LastIndexOf(thisTranscodedToUtf16, otherTranscodedToUtf16, compareOptions, out matchLength);
- }
-#else
- Debug.Assert(comparisonType == StringComparison.OrdinalIgnoreCase);
-
- if (fromBeginning)
- {
- idx = compareInfo.IndexOf(thisTranscodedToUtf16, otherTranscodedToUtf16, 0, thisTranscodedToUtf16.Length, compareOptions);
- }
- else
- {
- idx = compareInfo.LastIndexOf(thisTranscodedToUtf16, otherTranscodedToUtf16, thisTranscodedToUtf16.Length, thisTranscodedToUtf16.Length, compareOptions);
- }
- matchLength = otherTranscodedToUtf16.Length;
-#endif
-
- if (idx < 0)
- {
- // No match found. Bail out now.
-
- range = default;
- return false;
- }
-
- // If we reached this point, we found a match. The 'idx' local is the index in the source
- // string (indexed by UTF-16 code units) where the match was found, and the 'matchLength'
- // local is the number of chars in the source string which constitute the match. This length
- // can be different than the length of the search string, as non-ordinal IndexOf operations
- // follow Unicode full case folding semantics and might also normalize characters like
- // digraphs.
-
-#if SYSTEM_PRIVATE_CORELIB
- fixed (char* pThisTranscodedToUtf16 = &thisTranscodedToUtf16.GetRawStringData())
-#else
- fixed (char* pThisTranscodedToUtf16 = thisTranscodedToUtf16)
-#endif
- {
- // First, we need to convert the UTF-16 'idx' to its UTF-8 equivalent.
-
- char* pStoppedCounting = Utf16Utility.GetPointerToFirstInvalidChar(pThisTranscodedToUtf16, idx, out long utf8CodeUnitCountAdjustment, out _);
- Debug.Assert(pStoppedCounting == pThisTranscodedToUtf16 + idx, "We shouldn't have generated an ill-formed UTF-16 temp string.");
- Debug.Assert((ulong)(idx + utf8CodeUnitCountAdjustment) <= (uint)this.Bytes.Length, "Start index should be within the source UTF-8 data.");
-
- // Normally when we produce a UTF-8 code unit count from a UTF-16 source we
- // need to perform 64-bit arithmetic so we don't overflow. But in this case
- // we know the true original source was UTF-8, so its length is known already
- // to fit into a signed 32-bit integer. So we'll perform an unchecked cast.
-
- int utf8StartIdx = idx + (int)utf8CodeUnitCountAdjustment;
-
- // Now we need to convert the UTF-16 'matchLength' to its UTF-8 equivalent.
-
- pStoppedCounting = Utf16Utility.GetPointerToFirstInvalidChar(pThisTranscodedToUtf16 + idx, matchLength, out utf8CodeUnitCountAdjustment, out _);
- Debug.Assert(pStoppedCounting == pThisTranscodedToUtf16 + idx + matchLength, "We shouldn't have generated an ill-formed UTF-16 temp string.");
- Debug.Assert((ulong)(utf8StartIdx + matchLength + utf8CodeUnitCountAdjustment) <= (uint)this.Bytes.Length, "End index should be within the source UTF-8 data.");
-
- int utf8EndIdx = utf8StartIdx + matchLength + (int)utf8CodeUnitCountAdjustment;
-
- // Some quick sanity checks on the return value before we return.
-
- Debug.Assert(0 <= utf8StartIdx);
- Debug.Assert(utf8StartIdx <= utf8EndIdx);
- Debug.Assert(utf8EndIdx <= this.Bytes.Length);
-
- range = utf8StartIdx..utf8EndIdx;
- return true;
- }
- }
-
- /// <summary>
- /// Attempts to locate the last occurrence of the target <paramref name="value"/> within this <see cref="Utf8Span"/> instance.
- /// If <paramref name="value"/> is found, returns <see langword="true"/> and sets <paramref name="range"/> to
- /// the location where <paramref name="value"/> occurs within this <see cref="Utf8Span"/> instance.
- /// If <paramref name="value"/> is not found, returns <see langword="false"/> and sets <paramref name="range"/>
- /// to <see langword="default"/>.
- /// </summary>
- /// <remarks>
- /// An ordinal search is performed.
- /// </remarks>
- public bool TryFindLast(char value, out Range range)
- {
- if (Rune.TryCreate(value, out Rune rune))
- {
- return TryFindLast(rune, out range);
- }
- else
- {
- // Surrogate chars can't exist in well-formed UTF-8 data - bail immediately.
-
- range = default;
- return false;
- }
- }
-
- /// <summary>
- /// Attempts to locate the last occurrence of the target <paramref name="value"/> within this <see cref="Utf8Span"/> instance.
- /// If <paramref name="value"/> is found, returns <see langword="true"/> and sets <paramref name="range"/> to
- /// the location where <paramref name="value"/> occurs within this <see cref="Utf8Span"/> instance.
- /// If <paramref name="value"/> is not found, returns <see langword="false"/> and sets <paramref name="range"/>
- /// to <see langword="default"/>.
- /// </summary>
- /// <remarks>
- /// The search is performed using the specified <paramref name="comparisonType"/>.
- /// </remarks>
- public bool TryFindLast(char value, StringComparison comparisonType, out Range range)
- {
- if (Rune.TryCreate(value, out Rune rune))
- {
- return TryFindLast(rune, comparisonType, out range);
- }
- else
- {
- CheckStringComparison(comparisonType);
-
- // Surrogate chars can't exist in well-formed UTF-8 data - bail immediately.
-
- range = default;
- return false;
- }
- }
-
- /// <summary>
- /// Attempts to locate the last occurrence of the target <paramref name="value"/> within this <see cref="Utf8Span"/> instance.
- /// If <paramref name="value"/> is found, returns <see langword="true"/> and sets <paramref name="range"/> to
- /// the location where <paramref name="value"/> occurs within this <see cref="Utf8Span"/> instance.
- /// If <paramref name="value"/> is not found, returns <see langword="false"/> and sets <paramref name="range"/>
- /// to <see langword="default"/>.
- /// </summary>
- /// <remarks>
- /// An ordinal search is performed.
- /// </remarks>
- public bool TryFindLast(Rune value, out Range range)
- {
- if (value.IsAscii)
- {
- // Special-case ASCII since it's a simple single byte search.
-
- int idx = Bytes.LastIndexOf((byte)value.Value);
- if (idx < 0)
- {
- range = default;
- return false;
- }
- else
- {
- range = idx..(idx + 1);
- return true;
- }
- }
- else
- {
- // Slower path: need to search a multi-byte sequence.
- // TODO_UTF8STRING: As an optimization, we could use unsafe APIs below since we
- // know Rune instances are well-formed and slicing is safe.
-
- Span<byte> runeBytes = stackalloc byte[Utf8Utility.MaxBytesPerScalar];
- int utf8ByteLengthOfRune = value.EncodeToUtf8(runeBytes);
-
- return TryFindLast(UnsafeCreateWithoutValidation(runeBytes.Slice(0, utf8ByteLengthOfRune)), out range);
- }
- }
-
- /// <summary>
- /// Attempts to locate the last occurrence of the target <paramref name="value"/> within this <see cref="Utf8Span"/> instance.
- /// If <paramref name="value"/> is found, returns <see langword="true"/> and sets <paramref name="range"/> to
- /// the location where <paramref name="value"/> occurs within this <see cref="Utf8Span"/> instance.
- /// If <paramref name="value"/> is not found, returns <see langword="false"/> and sets <paramref name="range"/>
- /// to <see langword="default"/>.
- /// </summary>
- /// <remarks>
- /// The search is performed using the specified <paramref name="comparisonType"/>.
- /// </remarks>
- public bool TryFindLast(Rune value, StringComparison comparisonType, out Range range)
- {
- if (comparisonType == StringComparison.Ordinal)
- {
- return TryFindLast(value, out range);
- }
- else
- {
- // Slower path: not an ordinal comparison.
- // TODO_UTF8STRING: As an optimization, we could use unsafe APIs below since we
- // know Rune instances are well-formed and slicing is safe.
-
- Span<byte> runeBytes = stackalloc byte[Utf8Utility.MaxBytesPerScalar];
- int utf8ByteLengthOfRune = value.EncodeToUtf8(runeBytes);
-
- return TryFindLast(UnsafeCreateWithoutValidation(runeBytes.Slice(0, utf8ByteLengthOfRune)), comparisonType, out range);
- }
- }
-
- /// <summary>
- /// Attempts to locate the last occurrence of the target <paramref name="value"/> within this <see cref="Utf8Span"/> instance.
- /// If <paramref name="value"/> is found, returns <see langword="true"/> and sets <paramref name="range"/> to
- /// the location where <paramref name="value"/> occurs within this <see cref="Utf8Span"/> instance.
- /// If <paramref name="value"/> is not found, returns <see langword="false"/> and sets <paramref name="range"/>
- /// to <see langword="default"/>.
- /// </summary>
- /// <remarks>
- /// An ordinal search is performed.
- /// </remarks>
- public bool TryFindLast(Utf8Span value, out Range range)
- {
- int idx;
-
- if (value.Bytes.Length <= 1)
- {
- if (value.Bytes.Length == 1)
- {
- idx = this.Bytes.LastIndexOf(value.Bytes[0]); // special-case ASCII since it's a single byte search
- }
- else
- {
- idx = this.Length; // the last empty substring always occurs at the end of the buffer
- }
- }
- else
- {
- // Slower path: need to search a multi-byte sequence.
-
- idx = this.Bytes.LastIndexOf(value.Bytes);
- }
-
- if (idx < 0)
- {
- range = default;
- return false;
- }
- else
- {
- range = idx..(idx + value.Bytes.Length);
- return true;
- }
- }
-
- /// <summary>
- /// Attempts to locate the last occurrence of the target <paramref name="value"/> within this <see cref="Utf8Span"/> instance.
- /// If <paramref name="value"/> is found, returns <see langword="true"/> and sets <paramref name="range"/> to
- /// the location where <paramref name="value"/> occurs within this <see cref="Utf8Span"/> instance.
- /// If <paramref name="value"/> is not found, returns <see langword="false"/> and sets <paramref name="range"/>
- /// to <see langword="default"/>.
- /// </summary>
- /// <remarks>
- /// The search is performed using the specified <paramref name="comparisonType"/>.
- /// </remarks>
- public bool TryFindLast(Utf8Span value, StringComparison comparisonType, out Range range) => TryFind(value, comparisonType, out range, fromBeginning: false);
-
- private static void CheckStringComparison(StringComparison comparisonType)
- {
-#if SYSTEM_PRIVATE_CORELIB
- string.CheckStringComparison(comparisonType);
-#else
- // Single comparison to check if comparisonType is within [CurrentCulture .. OrdinalIgnoreCase]
- if ((uint)comparisonType > (uint)StringComparison.OrdinalIgnoreCase)
- {
- ThrowHelper.ThrowArgumentException(SR.NotSupported_StringComparison, ExceptionArgument.comparisonType);
- }
-
- // There's no API that would allow getting the correct match length
- // for other StringComparisons.
- if (comparisonType != StringComparison.Ordinal &&
- comparisonType != StringComparison.OrdinalIgnoreCase)
- {
- ThrowHelper.ThrowNotSupportedException(SR.Utf8Span_TryFindOnlySupportsOrdinal);
- }
-#endif
- }
-
- private static CompareOptions GetCaseCompareOfComparisonCulture(StringComparison comparisonType)
- {
-#if SYSTEM_PRIVATE_CORELIB
- return string.GetCaseCompareOfComparisonCulture(comparisonType);
-#else
- Debug.Assert((uint)comparisonType <= (uint)StringComparison.OrdinalIgnoreCase);
-
- // Culture enums can be & with CompareOptions.IgnoreCase 0x01 to extract if IgnoreCase or CompareOptions.None 0x00
- //
- // CompareOptions.None 0x00
- // CompareOptions.IgnoreCase 0x01
- //
- // StringComparison.CurrentCulture: 0x00
- // StringComparison.InvariantCulture: 0x02
- // StringComparison.Ordinal 0x04
- //
- // StringComparison.CurrentCultureIgnoreCase: 0x01
- // StringComparison.InvariantCultureIgnoreCase: 0x03
- // StringComparison.OrdinalIgnoreCase 0x05
-
- return (CompareOptions)((int)comparisonType & (int)CompareOptions.IgnoreCase);
-#endif
- }
- }
-}
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System.Buffers;
-using System.ComponentModel;
-using System.Diagnostics;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-using System.Text.Unicode;
-
-#if SYSTEM_PRIVATE_CORELIB
-using Internal.Runtime.CompilerServices;
-#endif
-
-#pragma warning disable 0809 //warning CS0809: Obsolete member 'Utf8Span.Equals(object)' overrides non-obsolete member 'object.Equals(object)'
-
-namespace System.Text
-{
- [StructLayout(LayoutKind.Auto)]
- public readonly ref partial struct Utf8Span
- {
- /// <summary>
- /// Creates a <see cref="Utf8Span"/> from an existing <see cref="Utf8String"/> instance.
- /// </summary>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public Utf8Span(Utf8String? value)
- {
- Bytes = Utf8Extensions.AsBytes(value);
- }
-
- /// <summary>
- /// Ctor for internal use only. Caller _must_ validate both invariants hold:
- /// (a) the buffer represents well-formed UTF-8 data, and
- /// (b) the buffer is immutable.
- /// </summary>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private Utf8Span(ReadOnlySpan<byte> rawData)
- {
- // In debug builds, we want to ensure that the callers really did validate
- // the buffer for well-formedness. The entire line below is removed when
- // compiling release builds.
-
- Debug.Assert(Utf8Utility.GetIndexOfFirstInvalidUtf8Sequence(rawData, out _) == -1);
-
- Bytes = rawData;
- }
-
- public ReadOnlySpan<byte> Bytes { get; }
-
- public static Utf8Span Empty => default;
-
- public bool IsEmpty => Bytes.IsEmpty;
-
- /// <summary>
- /// Returns the length (in UTF-8 code units, or <see cref="byte"/>s) of this instance.
- /// </summary>
- public int Length => Bytes.Length;
-
- public Utf8Span this[Range range]
- {
- get
- {
- (int offset, int length) = range.GetOffsetAndLength(Length);
-
- // Check for a split across a multi-byte subsequence on the way out.
- // Reminder: Unlike Utf8String, we can't safely dereference past the end of the span.
-
- ref byte newRef = ref DangerousGetMutableReference(offset);
- if (length > 0 && Utf8Utility.IsUtf8ContinuationByte(newRef))
- {
- Utf8String.ThrowImproperStringSplit();
- }
-
- int endIdx = offset + length;
- if (endIdx < Length && Utf8Utility.IsUtf8ContinuationByte(DangerousGetMutableReference(endIdx)))
- {
- Utf8String.ThrowImproperStringSplit();
- }
-
-#if SYSTEM_PRIVATE_CORELIB
- return UnsafeCreateWithoutValidation(new ReadOnlySpan<byte>(ref newRef, length));
-#else
- return UnsafeCreateWithoutValidation(Bytes.Slice(offset, length));
-#endif
- }
- }
-
- /// <summary>
- /// Returns a <em>mutable</em> reference to the first byte of this <see cref="Utf8Span"/>
- /// (or, if this <see cref="Utf8Span"/> is empty, to where the first byte would be).
- /// </summary>
- /// <returns></returns>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- internal ref byte DangerousGetMutableReference() => ref MemoryMarshal.GetReference(Bytes);
-
- /// <summary>
- /// Returns a <em>mutable</em> reference to the element at index <paramref name="index"/>
- /// of this <see cref="Utf8Span"/> instance. The index is not bounds-checked.
- /// </summary>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- internal ref byte DangerousGetMutableReference(int index)
- {
- Debug.Assert(index >= 0, "Caller should've performed bounds checking.");
- return ref DangerousGetMutableReference((uint)index);
- }
-
- /// <summary>
- /// Returns a <em>mutable</em> reference to the element at index <paramref name="index"/>
- /// of this <see cref="Utf8Span"/> instance. The index is not bounds-checked.
- /// </summary>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- internal ref byte DangerousGetMutableReference(nuint index)
- {
- // Allow retrieving references to just past the end of the span (but shouldn't dereference this).
-
- Debug.Assert(index <= (uint)Length, "Caller should've performed bounds checking.");
-#if SYSTEM_PRIVATE_CORELIB
- return ref Unsafe.AddByteOffset(ref DangerousGetMutableReference(), index);
-#else
- return ref Unsafe.AddByteOffset(ref DangerousGetMutableReference(), (nint)index);
-#endif
- }
-
- public bool IsEmptyOrWhiteSpace() => (Utf8Utility.GetIndexOfFirstNonWhiteSpaceChar(Bytes) == Length);
-
- /// <summary>
- /// This method is not supported as spans cannot be boxed. To compare two spans, use operator==.
- /// <exception cref="System.NotSupportedException">
- /// Always thrown by this method.
- /// </exception>
- /// </summary>
- [Obsolete("Equals(object) on Utf8Span will always throw an exception. Use Equals(Utf8Span) or operator == instead.")]
- [EditorBrowsable(EditorBrowsableState.Never)]
- public override bool Equals(object? obj)
- {
- throw new NotSupportedException(SR.Utf8Span_CannotCallEqualsObject);
- }
-
- public bool Equals(Utf8Span other) => Equals(this, other);
-
- public bool Equals(Utf8Span other, StringComparison comparison) => Equals(this, other, comparison);
-
- public static bool Equals(Utf8Span left, Utf8Span right) => left.Bytes.SequenceEqual(right.Bytes);
-
- public static bool Equals(Utf8Span left, Utf8Span right, StringComparison comparison)
- {
- // TODO_UTF8STRING: This perf can be improved, including removing
- // the virtual dispatch by putting the switch directly in this method.
-
- return Utf8StringComparer.FromComparison(comparison).Equals(left, right);
- }
-
- public override int GetHashCode()
- {
- // TODO_UTF8STRING: Consider whether this should use a different seed than String.GetHashCode.
- // This method should only be called to calculate the hash code over spans that represent
- // UTF-8 textual data, not over arbitrary binary sequences.
-
- ulong seed = Marvin.DefaultSeed;
-#if SYSTEM_PRIVATE_CORELIB
- return Marvin.ComputeHash32(ref MemoryMarshal.GetReference(Bytes), (uint)Length /* in bytes */, (uint)seed, (uint)(seed >> 32));
-#else
- return Marvin.ComputeHash32(Bytes, seed);
-#endif
- }
-
- public int GetHashCode(StringComparison comparison)
- {
- // TODO_UTF8STRING: This perf can be improved, including removing
- // the virtual dispatch by putting the switch directly in this method.
-
- return Utf8StringComparer.FromComparison(comparison).GetHashCode(this);
- }
-
- /// <summary>
- /// Returns <see langword="true"/> if this UTF-8 text consists of all-ASCII data,
- /// <see langword="false"/> if there is any non-ASCII data within this UTF-8 text.
- /// </summary>
- /// <remarks>
- /// ASCII text is defined as text consisting only of scalar values in the range [ U+0000..U+007F ].
- /// Empty spans are considered to be all-ASCII. The runtime of this method is O(n).
- /// </remarks>
- public bool IsAscii()
- {
- // TODO_UTF8STRING: Use an API that takes 'ref byte' instead of a 'byte*' as a parameter.
-
- unsafe
- {
- fixed (byte* pData = &MemoryMarshal.GetReference(Bytes))
- {
- return (ASCIIUtility.GetIndexOfFirstNonAsciiByte(pData, (uint)Length) == (uint)Length);
- }
- }
- }
-
- /// <summary>
- /// Returns a value stating whether this <see cref="Utf8Span"/> instance is normalized
- /// using the specified Unicode normalization form.
- /// </summary>
- /// <param name="normalizationForm">The <see cref="NormalizationForm"/> to check.</param>
- /// <returns><see langword="true"/> if this <see cref="Utf8Span"/> instance represents text
- /// normalized under <paramref name="normalizationForm"/>, otherwise <see langword="false"/>.</returns>
- public bool IsNormalized(NormalizationForm normalizationForm = NormalizationForm.FormC)
- {
- // TODO_UTF8STRING: Avoid allocations in this code path.
-
- return ToString().IsNormalized(normalizationForm);
- }
-
- /// <summary>
- /// Gets an immutable reference that can be used in a <see langword="fixed"/> statement. Unlike
- /// <see cref="Utf8String"/>, the resulting reference is not guaranteed to be null-terminated.
- /// </summary>
- /// <remarks>
- /// If this <see cref="Utf8Span"/> instance is empty, returns <see langword="null"/>. Dereferencing
- /// such a reference will result in a <see cref="NullReferenceException"/> being generated.
- /// </remarks>
- [EditorBrowsable(EditorBrowsableState.Never)]
- public ref readonly byte GetPinnableReference()
- {
- // This returns null if the underlying span is empty. The reason for this is that unlike
- // Utf8String, these buffers are not guaranteed to be null-terminated, so it's not always
- // safe or meaningful to dereference the element just past the end of the buffer.
-
- return ref Bytes.GetPinnableReference();
- }
-
- public override string ToString()
- {
- // TODO_UTF8STRING: Since we know the underlying data is immutable, well-formed UTF-8,
- // we can perform transcoding using an optimized code path that skips all safety checks.
-
-#if (!NETSTANDARD2_0 && !NETFRAMEWORK)
- return Encoding.UTF8.GetString(Bytes);
-#else
- if (IsEmpty)
- {
- return string.Empty;
- }
-
- unsafe
- {
- fixed (byte* pBytes = Bytes)
- {
- return Encoding.UTF8.GetString(pBytes, Length);
- }
- }
-#endif
- }
-
- /// <summary>
- /// Converts this <see cref="Utf8Span"/> instance to a <see cref="string"/>.
- /// </summary>
- /// <remarks>
- /// This routine throws <see cref="InvalidOperationException"/> if the underlying instance
- /// contains invalid UTF-8 data.
- /// </remarks>
- internal unsafe string ToStringNoReplacement()
- {
- // TODO_UTF8STRING: Optimize the call below, potentially by avoiding the two-pass.
-
- fixed (byte* pData = &MemoryMarshal.GetReference(Bytes))
- {
- byte* pFirstInvalidByte = Utf8Utility.GetPointerToFirstInvalidByte(pData, Length, out int utf16CodeUnitCountAdjustment, out _);
- if (pFirstInvalidByte != pData + (uint)Length)
- {
- // Saw bad UTF-8 data.
- // TODO_UTF8STRING: Throw a better exception below?
-
- ThrowHelper.ThrowInvalidOperationException();
- }
-
- int utf16CharCount = Length + utf16CodeUnitCountAdjustment;
- Debug.Assert(utf16CharCount <= Length && utf16CharCount >= 0);
-
-#if (!NETSTANDARD2_0 && !NETFRAMEWORK)
- // TODO_UTF8STRING: Can we call string.FastAllocate directly?
- return string.Create(utf16CharCount, (pbData: (IntPtr)pData, cbData: Length), static (chars, state) =>
- {
- OperationStatus status = Utf8.ToUtf16(new ReadOnlySpan<byte>((byte*)state.pbData, state.cbData), chars, out _, out _, replaceInvalidSequences: false);
- Debug.Assert(status == OperationStatus.Done, "Did somebody mutate this Utf8String instance unexpectedly?");
- });
-#else
- char[] buffer = ArrayPool<char>.Shared.Rent(utf16CharCount);
- try
- {
- fixed (char* pBuffer = buffer)
- {
- Encoding.UTF8.GetChars(pData, Length, pBuffer, utf16CharCount);
- return new string(pBuffer, 0, utf16CharCount);
- }
- }
- finally
- {
- ArrayPool<char>.Shared.Return(buffer);
- }
-#endif
- }
- }
-
- public Utf8String ToUtf8String()
- {
- // TODO_UTF8STRING: Since we know the underlying data is immutable, well-formed UTF-8,
- // we can perform transcoding using an optimized code path that skips all safety checks.
-
- return Utf8String.UnsafeCreateWithoutValidation(Bytes);
- }
-
- /// <summary>
- /// Wraps a <see cref="Utf8Span"/> instance around the provided <paramref name="buffer"/>,
- /// skipping validation of the input data.
- /// </summary>
- /// <remarks>
- /// Callers must uphold the following two invariants:
- ///
- /// (a) <paramref name="buffer"/> consists only of well-formed UTF-8 data and does
- /// not contain invalid or incomplete UTF-8 subsequences; and
- /// (b) the contents of <paramref name="buffer"/> will not change for the duration
- /// of the returned <see cref="Utf8Span"/>'s existence.
- ///
- /// If these invariants are not maintained, the runtime may exhibit undefined behavior.
- /// </remarks>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static Utf8Span UnsafeCreateWithoutValidation(ReadOnlySpan<byte> buffer)
- {
- return new Utf8Span(buffer);
- }
- }
-}
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.Globalization;
-
-namespace System.Text
-{
- internal abstract class Utf8StringComparer : IComparer<Utf8String?>, IEqualityComparer<Utf8String?>
- {
- // Nobody except for nested classes can create instances of this type.
- private Utf8StringComparer() { }
-
- public static Utf8StringComparer CurrentCulture => new CultureAwareComparer(CultureInfo.CurrentCulture.CompareInfo, CompareOptions.None);
- public static Utf8StringComparer CurrentCultureIgnoreCase => new CultureAwareComparer(CultureInfo.CurrentCulture.CompareInfo, CompareOptions.IgnoreCase);
- public static Utf8StringComparer InvariantCulture => CultureAwareComparer.Invariant;
- public static Utf8StringComparer InvariantCultureIgnoreCase => CultureAwareComparer.InvariantIgnoreCase;
- public static Utf8StringComparer Ordinal => OrdinalComparer.Instance;
- public static Utf8StringComparer OrdinalIgnoreCase => OrdinalIgnoreCaseComparer.Instance;
-
- public static Utf8StringComparer Create(CultureInfo culture, bool ignoreCase) => Create(culture, ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None);
-
- public static Utf8StringComparer Create(CultureInfo culture, CompareOptions options)
- {
- if (culture is null)
- {
- ThrowHelper.ThrowArgumentNullException(ExceptionArgument.culture);
- }
-
- return new CultureAwareComparer(culture.CompareInfo, options);
- }
-
- public static Utf8StringComparer FromComparison(StringComparison comparisonType)
- {
- return comparisonType switch
- {
- StringComparison.CurrentCulture => CurrentCulture,
- StringComparison.CurrentCultureIgnoreCase => CurrentCultureIgnoreCase,
- StringComparison.InvariantCulture => InvariantCulture,
- StringComparison.InvariantCultureIgnoreCase => InvariantCultureIgnoreCase,
- StringComparison.Ordinal => Ordinal,
- StringComparison.OrdinalIgnoreCase => OrdinalIgnoreCase,
- _ => throw new ArgumentException(SR.NotSupported_StringComparison, nameof(comparisonType)),
- };
- }
-
- public abstract int Compare(Utf8String? x, Utf8String? y);
- public abstract int Compare(Utf8Span x, Utf8Span y);
- public abstract bool Equals(Utf8String? x, Utf8String? y);
- public abstract bool Equals(Utf8Span x, Utf8Span y);
-#pragma warning disable CS8614 // Remove warning disable when nullable attributes are respected
- public abstract int GetHashCode(Utf8String obj);
-#pragma warning restore CS8614
- public abstract int GetHashCode(Utf8Span obj);
-
- private sealed class CultureAwareComparer : Utf8StringComparer
- {
-#if SYSTEM_PRIVATE_CORELIB
- internal static readonly CultureAwareComparer Invariant = new CultureAwareComparer(CompareInfo.Invariant, CompareOptions.None);
- internal static readonly CultureAwareComparer InvariantIgnoreCase = new CultureAwareComparer(CompareInfo.Invariant, CompareOptions.IgnoreCase);
-#else
- internal static readonly CultureAwareComparer Invariant = new CultureAwareComparer(CultureInfo.InvariantCulture.CompareInfo, CompareOptions.None);
- internal static readonly CultureAwareComparer InvariantIgnoreCase = new CultureAwareComparer(CultureInfo.InvariantCulture.CompareInfo, CompareOptions.IgnoreCase);
-#endif
-
- private readonly CompareInfo _compareInfo;
- private readonly CompareOptions _options;
-
- internal CultureAwareComparer(CompareInfo compareInfo, CompareOptions options)
- {
- Debug.Assert(compareInfo != null);
-
- _compareInfo = compareInfo;
- _options = options;
- }
-
- public override int Compare(Utf8String? x, Utf8String? y)
- {
- // TODO_UTF8STRING: Avoid the allocations below.
-
- return _compareInfo.Compare(x?.ToString(), y?.ToString(), _options);
- }
-
- public override int Compare(Utf8Span x, Utf8Span y)
- {
- // TODO_UTF8STRING: Avoid the allocations below.
-
- return _compareInfo.Compare(x.ToString(), y.ToString(), _options);
- }
-
- public override bool Equals(Utf8String? x, Utf8String? y) => Compare(x, y) == 0;
- public override bool Equals(Utf8Span x, Utf8Span y) => Compare(x, y) == 0;
-
- public override int GetHashCode(Utf8String? obj)
- {
- // TODO_UTF8STRING: Avoid the allocations below.
-
- return (obj is null) ? 0 : _compareInfo.GetHashCode(obj.ToString(), _options);
- }
-
- public override int GetHashCode(Utf8Span obj)
- {
- // TODO_UTF8STRING: Avoid the allocations below.
-
- return _compareInfo.GetHashCode(obj.ToString(), _options);
- }
- }
-
- private sealed class OrdinalComparer : Utf8StringComparer
- {
- public static readonly OrdinalComparer Instance = new OrdinalComparer();
-
- // All accesses must be through the static factory.
- private OrdinalComparer() { }
-
- public override int Compare(Utf8String? x, Utf8String? y)
- {
- // TODO_UTF8STRING: Avoid the allocations below.
-
- return string.CompareOrdinal(x?.ToString(), y?.ToString());
- }
-
- public override int Compare(Utf8Span x, Utf8Span y)
- {
- // TODO_UTF8STRING: Avoid the allocations below.
-
- return string.CompareOrdinal(x.ToString(), y.ToString());
- }
-
- public override bool Equals(Utf8String? x, Utf8String? y) => Utf8String.Equals(x, y);
- public override bool Equals(Utf8Span x, Utf8Span y) => Utf8Span.Equals(x, y);
- public override int GetHashCode(Utf8String obj) => obj.GetHashCode();
- public override int GetHashCode(Utf8Span obj) => obj.GetHashCode();
- }
-
- private sealed class OrdinalIgnoreCaseComparer : Utf8StringComparer
- {
- public static readonly OrdinalIgnoreCaseComparer Instance = new OrdinalIgnoreCaseComparer();
-
- // All accesses must be through the static factory.
- private OrdinalIgnoreCaseComparer() { }
-
- public override int Compare(Utf8String? x, Utf8String? y)
- {
- // TODO_UTF8STRING: Avoid the allocations below.
-
- return StringComparer.OrdinalIgnoreCase.Compare(x?.ToString(), y?.ToString());
- }
-
- public override int Compare(Utf8Span x, Utf8Span y)
- {
- // TODO_UTF8STRING: Avoid the allocations below.
-
- return StringComparer.OrdinalIgnoreCase.Compare(x.ToString(), y.ToString());
- }
-
- public override bool Equals(Utf8String? x, Utf8String? y)
- {
- // TODO_UTF8STRING: Avoid the allocations below.
-
- return StringComparer.OrdinalIgnoreCase.Equals(x?.ToString(), y?.ToString());
- }
-
- public override bool Equals(Utf8Span x, Utf8Span y)
- {
- // TODO_UTF8STRING: Avoid the allocations below.
-
- return StringComparer.OrdinalIgnoreCase.Equals(x.ToString(), y.ToString());
- }
-
- public override int GetHashCode(Utf8String obj)
- {
- // TODO_UTF8STRING: Avoid the allocations below.
-
- return StringComparer.OrdinalIgnoreCase.GetHashCode(obj.ToString());
- }
-
- public override int GetHashCode(Utf8Span obj)
- {
- // TODO_UTF8STRING: Avoid the allocations below.
-
- return StringComparer.OrdinalIgnoreCase.GetHashCode(obj.ToString());
- }
- }
- }
-}
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System.Buffers;
-using System.Diagnostics;
-using System.Runtime.InteropServices;
-using System.Text;
-using System.Text.Unicode;
-
-namespace System
-{
- public sealed partial class Utf8String
- {
- /*
- * COMPARISON OF UTF-8 AGAINST UTF-16
- */
-
- /// <summary>
- /// Returns a value stating whether <paramref name="utf8Text"/> and <paramref name="utf16Text"/>
- /// represent the same data. An ordinal comparison is performed scalar-by-scalar.
- /// </summary>
- /// <remarks>
- /// This method returns <see langword="true"/> if both <paramref name="utf8Text"/> and
- /// <paramref name="utf16Text"/> are null, or if both are empty. This method returns <see langword="false"/>
- /// if either input contains an ill-formed subsequence. Otherwise, this method returns <see langword="true"/>
- /// if and only if both arguments decode to the same Unicode scalar value sequence.
- /// </remarks>
- public static bool AreEquivalent(Utf8String? utf8Text, string? utf16Text)
- {
- if (ReferenceEquals(utf8Text, utf16Text))
- {
- return true; // both are null
- }
-
- if (utf8Text is null || utf16Text is null)
- {
- return false; // null is never equivalent to non-null
- }
-
- if (utf8Text.Length == 0 && utf16Text.Length == 0)
- {
- return true; // empty is equivalent to empty
- }
-
- // Short-circuit: are the texts of sufficiently different lengths that
- // they could never be equivalent? This check allows us to skip the
- // normal decoding walk, which is O(n).
- //
- // The maximum length of a 'System.String' is around 1 billion elements,
- // so we can perform the multiplication within an unsigned 32-bit domain.
-
- Debug.Assert((ulong)utf16Text.Length * MAX_UTF8_BYTES_PER_UTF16_CHAR <= uint.MaxValue, "Did somebody change the max. allowed string length?");
-
- if (utf8Text.Length < utf16Text.Length
- || ((uint)utf16Text.Length * MAX_UTF8_BYTES_PER_UTF16_CHAR < (uint)utf8Text.Length))
- {
- return false;
- }
-
- return AreEquivalentOrdinalSkipShortCircuitingChecks(utf8Text.AsBytes(), utf16Text.AsSpan());
- }
-
- /// <summary>
- /// Returns a value stating whether <paramref name="utf8Text"/> and <paramref name="utf16Text"/>
- /// represent the same data. An ordinal comparison is performed scalar-by-scalar.
- /// </summary>
- /// <remarks>
- /// This method returns <see langword="true"/> if both <paramref name="utf8Text"/> and
- /// <paramref name="utf16Text"/> are empty. This method returns <see langword="false"/>
- /// if either input contains an ill-formed subsequence. Otherwise, this method returns <see langword="true"/>
- /// if and only if both arguments decode to the same Unicode scalar value sequence.
- /// </remarks>
- public static bool AreEquivalent(Utf8Span utf8Text, ReadOnlySpan<char> utf16Text) => AreEquivalent(utf8Text.Bytes, utf16Text);
-
- /// <summary>
- /// Returns a value stating whether <paramref name="utf8Text"/> and <paramref name="utf16Text"/>
- /// represent the same data. An ordinal comparison is performed scalar-by-scalar.
- /// </summary>
- /// <remarks>
- /// This method returns <see langword="true"/> if both <paramref name="utf8Text"/> and
- /// <paramref name="utf16Text"/> are empty. This method returns <see langword="false"/>
- /// if either input contains an ill-formed subsequence. Otherwise, this method returns <see langword="true"/>
- /// if and only if both arguments decode to the same Unicode scalar value sequence.
- /// </remarks>
- public static bool AreEquivalent(ReadOnlySpan<byte> utf8Text, ReadOnlySpan<char> utf16Text)
- {
- if (utf8Text.Length == 0 && utf16Text.Length == 0)
- {
- // Don't use IsEmpty for this check; JIT can optimize "Length == 0" better
- // for this particular scenario.
-
- return true;
- }
-
- // Same check as the (Utf8String, string) overload. The primary difference is that
- // since spans can be up to 2 billion elements in length, we need to perform
- // the multiplication step in the unsigned 64-bit domain to avoid integer overflow.
-
- if (utf8Text.Length < utf16Text.Length
- || ((ulong)(uint)utf16Text.Length * MAX_UTF8_BYTES_PER_UTF16_CHAR < (uint)utf8Text.Length))
- {
- return false;
- }
-
- return AreEquivalentOrdinalSkipShortCircuitingChecks(utf8Text, utf16Text);
- }
-
- private static bool AreEquivalentOrdinalSkipShortCircuitingChecks(ReadOnlySpan<byte> utf8Text, ReadOnlySpan<char> utf16Text)
- {
- while (!utf16Text.IsEmpty)
- {
- // If the next UTF-16 subsequence is malformed or incomplete, or if the next
- // UTF-8 subsequence is malformed or incomplete, or if they don't decode to
- // the exact same Unicode scalar value, fail.
- //
- // The Rune.DecodeFrom* APIs handle empty inputs just fine and return "Incomplete".
-
- // TODO_UTF8STRING: If we assume Utf8String contains well-formed UTF-8, we could
- // create a version of this method that calls a faster implementation of DecodeFromUtf8.
- // We'd need to be careful not to call that optimized routine if the user passed
- // us a normal ROS<byte> that didn't originate from a Utf8String or similar.
-
- if (Rune.DecodeFromUtf16(utf16Text, out Rune scalarFromUtf16, out int charsConsumedJustNow) != OperationStatus.Done
- || Rune.DecodeFromUtf8(utf8Text, out Rune scalarFromUtf8, out int bytesConsumedJustNow) != OperationStatus.Done
- || scalarFromUtf16 != scalarFromUtf8)
- {
- return false;
- }
-
- // TODO_UTF8STRING: As an optimization, we could perform unsafe slices below.
-
- utf16Text = utf16Text.Slice(charsConsumedJustNow);
- utf8Text = utf8Text.Slice(bytesConsumedJustNow);
- }
-
- // We decoded the entire UTF-16 input, and so far it has matched the decoded form
- // of the UTF-8 input. Now just make sure we've also decoded the entirety of the
- // UTF-8 data, otherwise the input strings aren't equivalent.
-
- return utf8Text.IsEmpty;
- }
-
- /// <summary>
- /// Returns a value stating whether the current <see cref="Utf8String"/> instance contains
- /// <paramref name="value"/>. An ordinal comparison is used.
- /// </summary>
- public bool Contains(char value)
- {
- return Rune.TryCreate(value, out Rune rune) && Contains(rune);
- }
-
- /// <summary>
- /// Returns a value stating whether the current <see cref="Utf8String"/> instance contains
- /// <paramref name="value"/>. The specified comparison is used.
- /// </summary>
- public bool Contains(char value, StringComparison comparison)
- {
- return Rune.TryCreate(value, out Rune rune) && Contains(rune, comparison);
- }
-
- /// <summary>
- /// Returns a value stating whether the current <see cref="Utf8String"/> instance contains
- /// the specified <see cref="Rune"/>. An ordinal comparison is used.
- /// </summary>
- public bool Contains(Rune value)
- {
- // TODO_UTF8STRING: This should be split into two methods:
- // One which operates on a single-byte (ASCII) search value,
- // the other which operates on a multi-byte (non-ASCII) search value.
-
- Span<byte> runeBytes = stackalloc byte[Utf8Utility.MaxBytesPerScalar];
- int runeBytesWritten = value.EncodeToUtf8(runeBytes);
-
-#if SYSTEM_PRIVATE_CORELIB
- return SpanHelpers.IndexOf(
- ref DangerousGetMutableReference(), Length,
- ref MemoryMarshal.GetReference(runeBytes), runeBytesWritten) >= 0;
-#else
- return GetSpan()
- .IndexOf(runeBytes.Slice(0, runeBytesWritten)) >= 0;
-#endif
- }
-
- /// <summary>
- /// Returns a value stating whether the current <see cref="Utf8String"/> instance contains
- /// the specified <see cref="Rune"/>. The specified comparison is used.
- /// </summary>
- public bool Contains(Rune value, StringComparison comparison)
- {
- // TODO_UTF8STRING: Optimize me to avoid allocations.
-
-#if (!NETSTANDARD2_0 && !NETFRAMEWORK)
- return ToString().Contains(value.ToString(), comparison);
-#else
- return ToString().IndexOf(value.ToString(), comparison) >= 0;
-#endif
- }
-
- /// <summary>
- /// Returns a value stating whether the current <see cref="Utf8String"/> instance contains <paramref name="value"/>.
- /// An ordinal comparison is used.
- /// </summary>
- public bool Contains(Utf8String value)
- {
- if (value is null)
- {
- ThrowHelper.ThrowArgumentNullException(ExceptionArgument.value);
- }
-
- return this.AsBytes().IndexOf(value.AsBytes()) >= 0;
- }
-
- /// <summary>
- /// Returns a value stating whether the current <see cref="Utf8String"/> instance contains <paramref name="value"/>.
- /// The specified comparison is used.
- /// </summary>
- public bool Contains(Utf8String value, StringComparison comparison)
- {
- if (value is null)
- {
- ThrowHelper.ThrowArgumentNullException(ExceptionArgument.value);
- }
-
- // TODO_UTF8STRING: Optimize me to avoid allocations.
-
-#if (!NETSTANDARD2_0 && !NETFRAMEWORK)
- return ToString().Contains(value.ToString(), comparison);
-#else
- return ToString().IndexOf(value.ToString(), comparison) >= 0;
-#endif
- }
-
- /// <summary>
- /// Returns a value stating whether the current <see cref="Utf8String"/> instance ends with
- /// <paramref name="value"/>. An ordinal comparison is used.
- /// </summary>
- public bool EndsWith(char value)
- {
- return Rune.TryCreate(value, out Rune rune) && EndsWith(rune);
- }
-
- /// <summary>
- /// Returns a value stating whether the current <see cref="Utf8String"/> instance ends with
- /// <paramref name="value"/>. The specified comparison is used.
- /// </summary>
- public bool EndsWith(char value, StringComparison comparison)
- {
- return Rune.TryCreate(value, out Rune rune) && EndsWith(rune, comparison);
- }
-
- /// <summary>
- /// Returns a value stating whether the current <see cref="Utf8String"/> instance ends with
- /// the specified <see cref="Rune"/>. An ordinal comparison is used.
- /// </summary>
- public bool EndsWith(Rune value)
- {
- // TODO_UTF8STRING: This should be split into two methods:
- // One which operates on a single-byte (ASCII) search value,
- // the other which operates on a multi-byte (non-ASCII) search value.
-
- Span<byte> runeBytes = stackalloc byte[Utf8Utility.MaxBytesPerScalar];
- int runeBytesWritten = value.EncodeToUtf8(runeBytes);
-
- return this.AsBytes().EndsWith(runeBytes.Slice(0, runeBytesWritten));
- }
-
- /// <summary>
- /// Returns a value stating whether the current <see cref="Utf8String"/> instance ends with
- /// the specified <see cref="Rune"/>. The specified comparison is used.
- /// </summary>
- public bool EndsWith(Rune value, StringComparison comparison)
- {
- // TODO_UTF8STRING: Optimize me to avoid allocations.
-
- return ToString().EndsWith(value.ToString(), comparison);
- }
-
- /// <summary>
- /// Returns a value stating whether the current <see cref="Utf8String"/> instance ends with <paramref name="value"/>.
- /// An ordinal comparison is used.
- /// </summary>
- public bool EndsWith(Utf8String value)
- {
- if (value is null)
- {
- ThrowHelper.ThrowArgumentNullException(ExceptionArgument.value);
- }
-
- return this.AsBytes().EndsWith(value.AsBytes());
- }
-
- /// <summary>
- /// Returns a value stating whether the current <see cref="Utf8String"/> instance ends with <paramref name="value"/>.
- /// The specified comparison is used.
- /// </summary>
- public bool EndsWith(Utf8String value, StringComparison comparison)
- {
- if (value is null)
- {
- ThrowHelper.ThrowArgumentNullException(ExceptionArgument.value);
- }
-
- // TODO_UTF8STRING: Optimize me to avoid allocations.
-
- return ToString().EndsWith(value.ToString(), comparison);
- }
-
- /// <summary>
- /// Returns a value stating whether the current <see cref="Utf8String"/> instance begins with
- /// <paramref name="value"/>. An ordinal comparison is used.
- /// </summary>
- public bool StartsWith(char value)
- {
- return Rune.TryCreate(value, out Rune rune) && StartsWith(rune);
- }
-
- /// <summary>
- /// Returns a value stating whether the current <see cref="Utf8String"/> instance begins with
- /// <paramref name="value"/>. The specified comparison is used.
- /// </summary>
- public bool StartsWith(char value, StringComparison comparison)
- {
- return Rune.TryCreate(value, out Rune rune) && StartsWith(rune, comparison);
- }
-
- /// <summary>
- /// Returns a value stating whether the current <see cref="Utf8String"/> instance begins with
- /// the specified <see cref="Rune"/>. An ordinal comparison is used.
- /// </summary>
- public bool StartsWith(Rune value)
- {
- // TODO_UTF8STRING: This should be split into two methods:
- // One which operates on a single-byte (ASCII) search value,
- // the other which operates on a multi-byte (non-ASCII) search value.
-
- Span<byte> runeBytes = stackalloc byte[Utf8Utility.MaxBytesPerScalar];
- int runeBytesWritten = value.EncodeToUtf8(runeBytes);
-
- return this.AsBytes().StartsWith(runeBytes.Slice(0, runeBytesWritten));
- }
-
- /// <summary>
- /// Returns a value stating whether the current <see cref="Utf8String"/> instance begins with
- /// the specified <see cref="Rune"/>. The specified comparison is used.
- /// </summary>
- public bool StartsWith(Rune value, StringComparison comparison)
- {
- // TODO_UTF8STRING: Optimize me to avoid allocations.
-
- return ToString().StartsWith(value.ToString(), comparison);
- }
-
- /// <summary>
- /// Returns a value stating whether the current <see cref="Utf8String"/> instance begins with <paramref name="value"/>.
- /// An ordinal comparison is used.
- /// </summary>
- public bool StartsWith(Utf8String value)
- {
- if (value is null)
- {
- ThrowHelper.ThrowArgumentNullException(ExceptionArgument.value);
- }
-
- return this.AsBytes().StartsWith(value.AsBytes());
- }
-
- /// <summary>
- /// Returns a value stating whether the current <see cref="Utf8String"/> instance begins with <paramref name="value"/>.
- /// The specified comparison is used.
- /// </summary>
- public bool StartsWith(Utf8String value, StringComparison comparison)
- {
- if (value is null)
- {
- ThrowHelper.ThrowArgumentNullException(ExceptionArgument.value);
- }
-
- // TODO_UTF8STRING: Optimize me to avoid allocations.
-
- return ToString().StartsWith(value.ToString(), comparison);
- }
- }
-}
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System.Buffers;
-using System.Diagnostics;
-using System.Diagnostics.CodeAnalysis;
-using System.Runtime.InteropServices;
-using System.Text;
-using System.Text.Unicode;
-
-namespace System
-{
- public sealed partial class Utf8String
- {
- private const int MAX_STACK_TRANSCODE_CHAR_COUNT = 128;
-
- // For values beyond U+FFFF, it's 4 UTF-8 bytes per 2 UTF-16 chars (2:1 ratio)
- private const int MAX_UTF8_BYTES_PER_UTF16_CHAR = 3;
-
- /*
- * STATIC FACTORIES
- */
-
- /// <summary>
- /// Creates a <see cref="Utf8String"/> instance from existing UTF-8 data.
- /// </summary>
- /// <param name="buffer">The existing data from which to create the new <see cref="Utf8String"/>.</param>
- /// <param name="value">
- /// When this method returns, contains a <see cref="Utf8String"/> with the same contents as <paramref name="buffer"/>
- /// if <paramref name="buffer"/> consists of well-formed UTF-8 data. Otherwise, <see langword="null"/>.
- /// </param>
- /// <returns>
- /// <see langword="true"/> if <paramref name="buffer"/> contains well-formed UTF-8 data and <paramref name="value"/>
- /// contains the <see cref="Utf8String"/> encapsulating a copy of that data. Otherwise, <see langword="false"/>.
- /// </returns>
- /// <remarks>
- /// This method is a non-throwing equivalent of the constructor <see cref="Utf8String(ReadOnlySpan{byte})"/>.
- /// </remarks>
- public static bool TryCreateFrom(ReadOnlySpan<byte> buffer, [NotNullWhen(true)] out Utf8String? value)
- {
- if (buffer.IsEmpty)
- {
- value = Empty; // it's valid to create a Utf8String instance from an empty buffer; we'll return the Empty singleton
- return true;
- }
-
- // Create and populate the Utf8String instance.
-
- Utf8String newString = FastAllocateSkipZeroInit(buffer.Length);
-#if SYSTEM_PRIVATE_CORELIB
- Buffer.Memmove(ref newString.DangerousGetMutableReference(), ref MemoryMarshal.GetReference(buffer), (uint)buffer.Length);
-#else
- buffer.CopyTo(newString.DangerousGetMutableSpan());
-#endif
-
- // Now perform validation.
- // Reminder: Perform validation over the copy, not over the source.
-
- if (Utf8Utility.IsWellFormedUtf8(newString.AsBytes()))
- {
- value = newString;
- return true;
- }
- else
- {
- value = default;
- return false;
- }
- }
-
- /// <summary>
- /// Creates a <see cref="Utf8String"/> instance from existing UTF-16 data, transcoding the
- /// existing data to UTF-8 upon creation.
- /// </summary>
- /// <param name="buffer">The existing UTF-16 data from which to create a new <see cref="Utf8String"/>.</param>
- /// <param name="value">
- /// When this method returns, contains a <see cref="Utf8String"/> with equivalent contents as <paramref name="buffer"/>
- /// if <paramref name="buffer"/> consists of well-formed UTF-16 data. Otherwise, <see langword="null"/>.
- /// </param>
- /// <returns>
- /// <see langword="true"/> if <paramref name="buffer"/> contains well-formed UTF-16 data and <paramref name="value"/>
- /// contains the <see cref="Utf8String"/> encapsulating equivalent data (as UTF-8). Otherwise, <see langword="false"/>.
- /// </returns>
- /// <remarks>
- /// This method is a non-throwing equivalent of the constructor <see cref="Utf8String(ReadOnlySpan{char})"/>.
- /// </remarks>
- public static bool TryCreateFrom(ReadOnlySpan<char> buffer, [NotNullWhen(true)] out Utf8String? value)
- {
- // Returning "false" from this method means only that the original input buffer didn't
- // contain well-formed UTF-16 data. This method could fail in other ways, such as
- // throwing an OutOfMemoryException if allocation of the output parameter fails.
-
- value = CreateFromUtf16Common(buffer, replaceInvalidSequences: false);
- return !(value is null);
- }
-
- /// <summary>
- /// Creates a <see cref="Utf8String"/> instance from existing UTF-8 data.
- /// </summary>
- /// <param name="buffer">The existing data from which to create the new <see cref="Utf8String"/>.</param>
- /// <remarks>
- /// If <paramref name="buffer"/> contains any ill-formed UTF-8 subsequences, those subsequences will
- /// be replaced with <see cref="Rune.ReplacementChar"/> in the returned <see cref="Utf8String"/> instance.
- /// This may result in the returned <see cref="Utf8String"/> having different contents (and thus a different
- /// total byte length) than the source parameter <paramref name="buffer"/>.
- /// </remarks>
- public static Utf8String CreateFromRelaxed(ReadOnlySpan<byte> buffer)
- {
- if (buffer.IsEmpty)
- {
- return Empty;
- }
-
- // Create and populate the Utf8String instance.
-
- Utf8String newString = FastAllocateSkipZeroInit(buffer.Length);
-#if SYSTEM_PRIVATE_CORELIB
- Buffer.Memmove(ref newString.DangerousGetMutableReference(), ref MemoryMarshal.GetReference(buffer), (uint)buffer.Length);
-#else
- buffer.CopyTo(newString.DangerousGetMutableSpan());
-#endif
-
- // Now perform validation & fixup.
-
- return Utf8Utility.ValidateAndFixupUtf8String(newString);
- }
-
- /// <summary>
- /// Creates a <see cref="Utf8String"/> instance from existing UTF-16 data.
- /// </summary>
- /// <param name="buffer">The existing data from which to create the new <see cref="Utf8String"/>.</param>
- /// <remarks>
- /// If <paramref name="buffer"/> contains any ill-formed UTF-16 subsequences, those subsequences will
- /// be replaced with <see cref="Rune.ReplacementChar"/> in the returned <see cref="Utf8String"/> instance.
- /// This may result in the original string data not round-tripping properly; that is, calling
- /// <see cref="ToString"/> on the returned <see cref="Utf8String"/> instance may produce a <see cref="string"/>
- /// whose contents differ from <paramref name="buffer"/>.
- /// </remarks>
- public static Utf8String CreateFromRelaxed(ReadOnlySpan<char> buffer)
- {
- Utf8String? newString = CreateFromUtf16Common(buffer, replaceInvalidSequences: true);
-
- if (newString is null)
- {
- // This shouldn't happen unless somebody mutated the input buffer in the middle
- // of data processing. We just fail in this scenario rather than retrying.
-
- throw new ArgumentException(
- message: SR.Utf8String_InputContainedMalformedUtf16,
- paramName: nameof(buffer));
- }
-
- return newString;
- }
-
- internal static Utf8String CreateFromRune(Rune value)
- {
- // Can skip zero-init since we're going to populate the entire buffer.
-
- Utf8String newString = FastAllocateSkipZeroInit(value.Utf8SequenceLength);
-
- if (value.IsAscii)
- {
- // Fast path: If an ASCII value, just allocate the one-byte string and fill in the single byte contents.
-
- newString.DangerousGetMutableReference() = (byte)value.Value;
- return newString;
- }
- else
- {
- // Slow path: If not ASCII, allocate a string of the appropriate length and fill in the multi-byte contents.
-
- int bytesWritten = value.EncodeToUtf8(newString.DangerousGetMutableSpan());
- Debug.Assert(newString.Length == bytesWritten);
- return newString;
- }
- }
-
- // Returns 'null' if the input buffer does not represent well-formed UTF-16 data and 'replaceInvalidSequences' is false.
- private static Utf8String? CreateFromUtf16Common(ReadOnlySpan<char> value, bool replaceInvalidSequences)
- {
- // Shortcut: Since we expect most strings to be small-ish, first try a one-pass
- // operation where we transcode directly on to the stack and then copy the validated
- // data into the new Utf8String instance. It's still O(n), but it should have a smaller
- // constant factor than a typical "count + transcode" combo.
-
- OperationStatus status;
- Utf8String newString;
-
- if (value.Length <= MAX_STACK_TRANSCODE_CHAR_COUNT /* in chars */)
- {
- if (value.IsEmpty)
- {
- return Empty;
- }
-
- Span<byte> scratch = stackalloc byte[MAX_STACK_TRANSCODE_CHAR_COUNT * MAX_UTF8_BYTES_PER_UTF16_CHAR]; // largest possible expansion, as explained below
- status = Utf8.FromUtf16(value, scratch, out _, out int scratchBytesWritten, replaceInvalidSequences);
- Debug.Assert(status == OperationStatus.Done || status == OperationStatus.InvalidData);
-
- if (status == OperationStatus.InvalidData)
- {
- return null;
- }
-
- // At this point we know transcoding succeeded, so the original input data was well-formed.
- // We'll memcpy the scratch buffer into the new Utf8String instance, which is very fast.
-
- newString = FastAllocateSkipZeroInit(scratchBytesWritten);
- scratch.Slice(0, scratchBytesWritten).CopyTo(newString.DangerousGetMutableSpan());
- return newString;
- }
-
- // First, determine how many UTF-8 bytes we'll need in order to represent this data.
- // This also checks the input data for well-formedness.
-
- long utf8CodeUnitCountAdjustment;
-
- unsafe
- {
- fixed (char* pChars = &MemoryMarshal.GetReference(value))
- {
- if (Utf16Utility.GetPointerToFirstInvalidChar(pChars, value.Length, out utf8CodeUnitCountAdjustment, out int _) != (pChars + (uint)value.Length))
- {
- return null;
- }
- }
- }
-
- // The max possible expansion transcoding UTF-16 to UTF-8 is that each input char corresponds
- // to 3 UTF-8 bytes. This is most common in CJK languages. Since the input buffer could be
- // up to int.MaxValue elements in length, we need to use a 64-bit value to hold the total
- // required UTF-8 byte length. However, the VM places restrictions on how large a Utf8String
- // instance can be, and the maximum allowed element count is just under int.MaxValue. (This
- // mirrors the restrictions already in place for System.String.) The VM will throw an
- // OutOfMemoryException if anybody tries to create a Utf8String instance larger than that,
- // so if we detect any sort of overflow we'll end up passing int.MaxValue down to the allocation
- // routine. This normalizes the OutOfMemoryException the caller sees.
-
- long totalUtf8BytesRequired = (uint)value.Length + utf8CodeUnitCountAdjustment;
- if (totalUtf8BytesRequired > int.MaxValue)
- {
- totalUtf8BytesRequired = int.MaxValue;
- }
-
- // We can get away with FastAllocateSkipZeroInit here because we're not going to return the
- // new Utf8String instance to the caller if we don't overwrite every byte of the buffer.
-
- newString = FastAllocateSkipZeroInit((int)totalUtf8BytesRequired);
-
- // Now transcode the UTF-16 input into the newly allocated Utf8String's buffer. We can't call the
- // "skip validation" transcoder because the caller could've mutated the input buffer between the
- // initial counting step and the transcoding step below.
-
- status = Utf8.FromUtf16(value, newString.DangerousGetMutableSpan(), out _, out int bytesWritten, replaceInvalidSequences: false);
- if (status != OperationStatus.Done || bytesWritten != newString.Length)
- {
- // Did somebody mutate our input buffer? Shouldn't be any other way this could happen.
-
- return null;
- }
-
- return newString;
- }
-
-#if !SYSTEM_PRIVATE_CORELIB
- // Returns 'null' if the input buffer does not represent well-formed UTF-16 data and 'replaceInvalidSequences' is false.
- private static byte[]? CreateBufferFromUtf16Common(ReadOnlySpan<char> value, bool replaceInvalidSequences)
- {
- // Shortcut: Since we expect most strings to be small-ish, first try a one-pass
- // operation where we transcode directly on to the stack and then copy the validated
- // data into the new Utf8String instance. It's still O(n), but it should have a smaller
- // constant factor than a typical "count + transcode" combo.
-
- OperationStatus status;
- byte[] newBuffer;
-
- if (value.Length <= MAX_STACK_TRANSCODE_CHAR_COUNT /* in chars */)
- {
- if (value.IsEmpty)
- {
- return Utf8String.Empty._bytes;
- }
-
- Span<byte> scratch = stackalloc byte[MAX_STACK_TRANSCODE_CHAR_COUNT * MAX_UTF8_BYTES_PER_UTF16_CHAR]; // largest possible expansion, as explained below
- status = Utf8.FromUtf16(value, scratch, out _, out int scratchBytesWritten, replaceInvalidSequences);
- Debug.Assert(status == OperationStatus.Done || status == OperationStatus.InvalidData);
-
- if (status == OperationStatus.InvalidData)
- {
- return null;
- }
-
- // At this point we know transcoding succeeded, so the original input data was well-formed.
- // We'll memcpy the scratch buffer into the new Utf8String instance, which is very fast.
-
- newBuffer = new byte[scratchBytesWritten + 1]; // null-terminated
- scratch.Slice(0, scratchBytesWritten).CopyTo(newBuffer);
- return newBuffer;
- }
-
- // First, determine how many UTF-8 bytes we'll need in order to represent this data.
- // This also checks the input data for well-formedness.
-
- long utf8CodeUnitCountAdjustment;
-
- unsafe
- {
- fixed (char* pChars = &MemoryMarshal.GetReference(value))
- {
- if (Utf16Utility.GetPointerToFirstInvalidChar(pChars, value.Length, out utf8CodeUnitCountAdjustment, out int _) != (pChars + (uint)value.Length))
- {
- return null;
- }
- }
- }
-
- // The max possible expansion transcoding UTF-16 to UTF-8 is that each input char corresponds
- // to 3 UTF-8 bytes. This is most common in CJK languages. Since the input buffer could be
- // up to int.MaxValue elements in length, we need to use a 64-bit value to hold the total
- // required UTF-8 byte length. However, the VM places restrictions on how large a Utf8String
- // instance can be, and the maximum allowed element count is just under int.MaxValue. (This
- // mirrors the restrictions already in place for System.String.) The VM will throw an
- // OutOfMemoryException if anybody tries to create a Utf8String instance larger than that,
- // so if we detect any sort of overflow we'll end up passing int.MaxValue down to the allocation
- // routine. This normalizes the OutOfMemoryException the caller sees.
-
- long totalUtf8BytesRequired = (uint)value.Length + utf8CodeUnitCountAdjustment;
- if (totalUtf8BytesRequired >= int.MaxValue)
- {
- totalUtf8BytesRequired = int.MaxValue - 1;
- }
-
- // We can get away with FastAllocateSkipZeroInit here because we're not going to return the
- // new Utf8String instance to the caller if we don't overwrite every byte of the buffer.
-
- newBuffer = new byte[(int)totalUtf8BytesRequired + 1]; // null-terminated
-
- // Now transcode the UTF-16 input into the newly allocated Utf8String's buffer. We can't call the
- // "skip validation" transcoder because the caller could've mutated the input buffer between the
- // initial counting step and the transcoding step below.
-
- status = Utf8.FromUtf16(value, newBuffer.AsSpan(0, newBuffer.Length - 1), out _, out int bytesWritten, replaceInvalidSequences: false);
- if (status != OperationStatus.Done || bytesWritten != newBuffer.Length - 1)
- {
- // Did somebody mutate our input buffer? Shouldn't be any other way this could happen.
-
- return null;
- }
-
- return newBuffer;
- }
-#endif // !SYSTEM_PRIVATE_CORELIB
-
-#if (!NETSTANDARD2_0 && !NETFRAMEWORK)
- /// <summary>
- /// Creates a new <see cref="Utf8String"/> instance, allowing the provided delegate to populate the
- /// instance data of the returned object.
- /// </summary>
- /// <typeparam name="TState">Type of the state object provided to <paramref name="action"/>.</typeparam>
- /// <param name="length">The length, in bytes, of the <see cref="Utf8String"/> instance to create.</param>
- /// <param name="state">The state object to provide to <paramref name="action"/>.</param>
- /// <param name="action">The callback which will be invoked to populate the returned <see cref="Utf8String"/>.</param>
- /// <exception cref="ArgumentException">
- /// Thrown if <paramref name="action"/> populates the buffer with ill-formed UTF-8 data.
- /// </exception>
- /// <remarks>
- /// The runtime will perform UTF-8 validation over the contents provided by the <paramref name="action"/> delegate.
- /// If an invalid UTF-8 subsequence is detected, an exception is thrown.
- /// </remarks>
- public static Utf8String Create<TState>(int length, TState state, SpanAction<byte, TState> action)
- {
- if (length < 0)
- {
- ThrowHelper.ThrowLengthArgumentOutOfRange_ArgumentOutOfRange_NeedNonNegNum();
- }
-
- if (action is null)
- {
- ThrowHelper.ThrowArgumentNullException(ExceptionArgument.action);
- }
-
- if (length == 0)
- {
- return Empty; // special-case empty input
- }
-
- // Create and populate the Utf8String instance.
- // Can't use FastAllocateSkipZeroInit here because we're handing the raw buffer to user code.
-
- Utf8String newString = FastAllocate(length);
- action(newString.DangerousGetMutableSpan(), state);
-
- // Now perform validation.
-
- if (!Utf8Utility.IsWellFormedUtf8(newString.AsBytes()))
- {
- throw new ArgumentException(
- message: SR.Utf8String_CallbackProvidedMalformedData,
- paramName: nameof(action));
- }
-
- return newString;
- }
-
- /// <summary>
- /// Creates a new <see cref="Utf8String"/> instance, allowing the provided delegate to populate the
- /// instance data of the returned object.
- /// </summary>
- /// <typeparam name="TState">Type of the state object provided to <paramref name="action"/>.</typeparam>
- /// <param name="length">The length, in bytes, of the <see cref="Utf8String"/> instance to create.</param>
- /// <param name="state">The state object to provide to <paramref name="action"/>.</param>
- /// <param name="action">The callback which will be invoked to populate the returned <see cref="Utf8String"/>.</param>
- /// <remarks>
- /// The runtime will perform UTF-8 validation over the contents provided by the <paramref name="action"/> delegate.
- /// If an invalid UTF-8 subsequence is detected, the invalid subsequence is replaced with <see cref="Rune.ReplacementChar"/>
- /// in the returned <see cref="Utf8String"/> instance. This could result in the returned <see cref="Utf8String"/> instance
- /// having a different byte length than specified by the <paramref name="length"/> parameter.
- /// </remarks>
- public static Utf8String CreateRelaxed<TState>(int length, TState state, SpanAction<byte, TState> action)
- {
- if (length < 0)
- {
- ThrowHelper.ThrowLengthArgumentOutOfRange_ArgumentOutOfRange_NeedNonNegNum();
- }
-
- if (action is null)
- {
- ThrowHelper.ThrowArgumentNullException(ExceptionArgument.action);
- }
-
- if (length == 0)
- {
- return Empty; // special-case empty input
- }
-
- // Create and populate the Utf8String instance.
- // Can't use FastAllocateSkipZeroInit here because we're handing the raw buffer to user code.
-
- Utf8String newString = FastAllocate(length);
- action(newString.DangerousGetMutableSpan(), state);
-
- // Now perform validation and fixup.
-
- return Utf8Utility.ValidateAndFixupUtf8String(newString);
- }
-
- /// <summary>
- /// Creates a new <see cref="Utf8String"/> instance, allowing the provided delegate to populate the
- /// instance data of the returned object. Please see remarks for important safety information about
- /// this method.
- /// </summary>
- /// <typeparam name="TState">Type of the state object provided to <paramref name="action"/>.</typeparam>
- /// <param name="length">The length, in bytes, of the <see cref="Utf8String"/> instance to create.</param>
- /// <param name="state">The state object to provide to <paramref name="action"/>.</param>
- /// <param name="action">The callback which will be invoked to populate the returned <see cref="Utf8String"/>.</param>
- /// <remarks>
- /// This factory method can be used as an optimization to skip the validation step that
- /// <see cref="Create{TState}(int, TState, SpanAction{byte, TState})"/> normally performs. The contract
- /// of this method requires that <paramref name="action"/> populate the buffer with well-formed UTF-8
- /// data, as <see cref="Utf8String"/> contractually guarantees that it contains only well-formed UTF-8 data,
- /// and runtime instability could occur if a caller violates this guarantee.
- /// </remarks>
- public static Utf8String UnsafeCreateWithoutValidation<TState>(int length, TState state, SpanAction<byte, TState> action)
- {
- if (length < 0)
- {
- ThrowHelper.ThrowLengthArgumentOutOfRange_ArgumentOutOfRange_NeedNonNegNum();
- }
-
- if (action is null)
- {
- ThrowHelper.ThrowArgumentNullException(ExceptionArgument.action);
- }
-
- if (length == 0)
- {
- return Empty; // special-case empty input
- }
-
- // Create and populate the Utf8String instance.
- // Can't use FastAllocateSkipZeroInit here because we're handing the raw buffer to user code.
-
- Utf8String newString = FastAllocate(length);
- action(newString.DangerousGetMutableSpan(), state);
-
- // The line below is removed entirely in release builds.
-
- Debug.Assert(Utf8Utility.IsWellFormedUtf8(newString.AsBytes()), "Callback populated the buffer with ill-formed UTF-8 data.");
-
- return newString;
- }
-#endif // !NETSTANDARD2_0
-
- /// <summary>
- /// Creates a new <see cref="Utf8String"/> instance populated with a copy of the provided contents.
- /// Please see remarks for important safety information about this method.
- /// </summary>
- /// <param name="utf8Contents">The contents to copy to the new <see cref="Utf8String"/>.</param>
- /// <remarks>
- /// This factory method can be used as an optimization to skip the validation step that the
- /// <see cref="Utf8String"/> constructors normally perform. The contract of this method requires that
- /// <paramref name="utf8Contents"/> contain only well-formed UTF-8 data, as <see cref="Utf8String"/>
- /// contractually guarantees that it contains only well-formed UTF-8 data, and runtime instability
- /// could occur if a caller violates this guarantee.
- /// </remarks>
- public static Utf8String UnsafeCreateWithoutValidation(ReadOnlySpan<byte> utf8Contents)
- {
- if (utf8Contents.IsEmpty)
- {
- return Empty; // special-case empty input
- }
-
- // Create and populate the Utf8String instance.
-
- Utf8String newString = FastAllocateSkipZeroInit(utf8Contents.Length);
- utf8Contents.CopyTo(newString.DangerousGetMutableSpan());
- // TODO_UTF8STRING: Zero-init was skipped above, but we didn't null-terminate the string
-
- // The line below is removed entirely in release builds.
-
- Debug.Assert(Utf8Utility.IsWellFormedUtf8(newString.AsBytes()), "Buffer contained ill-formed UTF-8 data.");
-
- return newString;
- }
-
- /*
- * HELPER METHODS
- */
-
- /// <summary>
- /// Creates a new instance of the specified length. Actual storage allocated is "length + 1" bytes
- /// because instances are null-terminated. Aside from the null terminator, the contents of the new
- /// instance are not zero-inited. Use only with first-party APIs which we know for a fact will
- /// initialize the entire contents of the Utf8String instance.
- /// </summary>
- /// <remarks>
- /// The implementation of this method checks its input argument for overflow.
- /// </remarks>
- private static Utf8String FastAllocateSkipZeroInit(int length)
- {
- // TODO_UTF8STRING: Actually skip zero-init.
-
- return FastAllocate(length);
- }
- }
-}
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System.Globalization;
-using System.Text;
-
-namespace System
-{
- public sealed partial class Utf8String
- {
- /// <summary>
- /// Returns a value stating whether this <see cref="Utf8String"/> instance is normalized
- /// using the specified Unicode normalization form.
- /// </summary>
- /// <param name="normalizationForm">The <see cref="NormalizationForm"/> to check.</param>
- /// <returns><see langword="true"/> if this <see cref="Utf8String"/> instance represents text
- /// normalized under <paramref name="normalizationForm"/>, otherwise <see langword="false"/>.</returns>
- public bool IsNormalized(NormalizationForm normalizationForm = NormalizationForm.FormC)
- {
- // TODO_UTF8STRING: Avoid allocations in this code path.
-
- return ToString().IsNormalized(normalizationForm);
- }
-
- /// <summary>
- /// Returns a new <see cref="Utf8String"/> instance which represents this <see cref="Utf8String"/> instance
- /// normalized using the specified Unicode normalization form.
- /// </summary>
- /// <remarks>
- /// The original <see cref="Utf8String"/> is left unchanged by this operation.
- /// </remarks>
- public Utf8String Normalize(NormalizationForm normalizationForm = NormalizationForm.FormC) => this.AsSpanSkipNullCheck().Normalize(normalizationForm);
-
- /// <summary>
- /// Converts this <see cref="Utf8String"/> to a <see langword="char[]"/>.
- /// </summary>
- public char[] ToCharArray() => this.AsSpanSkipNullCheck().ToCharArray();
-
- /// <summary>
- /// Returns a new <see cref="Utf8String"/> instance which represents this <see cref="Utf8String"/> instance
- /// converted to lowercase using <paramref name="culture"/>.
- /// </summary>
- /// <remarks>
- /// The original <see cref="Utf8String"/> is left unchanged by this operation. Note that the returned
- /// <see cref="Utf8String"/> instance may be longer or shorter (in terms of UTF-8 byte count) than the
- /// input <see cref="Utf8String"/>.
- /// </remarks>
- public Utf8String ToLower(CultureInfo culture) => this.AsSpanSkipNullCheck().ToLower(culture);
-
- /// <summary>
- /// Returns a new <see cref="Utf8String"/> instance which represents this <see cref="Utf8String"/> instance
- /// converted to lowercase using the invariant culture.
- /// </summary>
- /// <remarks>
- /// The original <see cref="Utf8String"/> is left unchanged by this operation. For more information on the
- /// invariant culture, see the <see cref="CultureInfo.InvariantCulture"/> property. Note that the returned
- /// <see cref="Utf8String"/> instance may be longer or shorter (in terms of UTF-8 byte count) than the
- /// input <see cref="Utf8String"/>.
- /// </remarks>
- public Utf8String ToLowerInvariant() => this.AsSpanSkipNullCheck().ToLowerInvariant();
-
- /// <summary>
- /// Returns a new <see cref="Utf8String"/> instance which represents this <see cref="Utf8String"/> instance
- /// converted to uppercase using <paramref name="culture"/>.
- /// </summary>
- /// <remarks>
- /// The original <see cref="Utf8String"/> is left unchanged by this operation. Note that the returned
- /// <see cref="Utf8String"/> instance may be longer or shorter (in terms of UTF-8 byte count) than the
- /// input <see cref="Utf8String"/>.
- /// </remarks>
- public Utf8String ToUpper(CultureInfo culture) => this.AsSpanSkipNullCheck().ToUpper(culture);
-
- /// <summary>
- /// Returns a new <see cref="Utf8String"/> instance which represents this <see cref="Utf8String"/> instance
- /// converted to uppercase using the invariant culture.
- /// </summary>
- /// <remarks>
- /// The original <see cref="Utf8String"/> is left unchanged by this operation. For more information on the
- /// invariant culture, see the <see cref="CultureInfo.InvariantCulture"/> property. Note that the returned
- /// <see cref="Utf8String"/> instance may be longer or shorter (in terms of UTF-8 byte count) than the
- /// input <see cref="Utf8String"/>.
- /// </remarks>
- public Utf8String ToUpperInvariant() => this.AsSpanSkipNullCheck().ToUpperInvariant();
- }
-}
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System.Buffers;
-using System.Collections;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.Text;
-
-namespace System
-{
- public sealed partial class Utf8String
- {
- /*
- * ENUMERABLE PROPERTY ACCESSORS
- */
-
- /// <summary>
- /// Allows enumeration over the individual UTF-8 bytes of this <see cref="Utf8String"/> instance.
- /// </summary>
- public ByteEnumerable Bytes => new ByteEnumerable(this);
-
- /// <summary>
- /// Allows enumeration over the UTF-16 code units (see <see cref="char"/>) which would result
- /// from transcoding this <see cref="Utf8String"/> instance to a UTF-16 <see cref="string"/>.
- /// </summary>
- public CharEnumerable Chars => new CharEnumerable(this);
-
- /// <summary>
- /// Allows enumeration over the Unicode scalar values (see <see cref="Rune"/>) which are
- /// encoded by this <see cref="Utf8String"/> instance.
- /// </summary>
- public RuneEnumerable Runes => new RuneEnumerable(this);
-
- /*
- * ENUMERATORS
- */
-
- public readonly struct ByteEnumerable : IEnumerable<byte>
- {
- private readonly Utf8String _obj;
-
- internal ByteEnumerable(Utf8String obj)
- {
- _obj = obj;
- }
-
- public Enumerator GetEnumerator() => new Enumerator(_obj);
-
- IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
-
- IEnumerator<byte> IEnumerable<byte>.GetEnumerator() => GetEnumerator();
-
- public struct Enumerator : IEnumerator<byte>
- {
- private readonly Utf8String _obj;
- private int _nextByteIdx;
-
- internal Enumerator(Utf8String obj)
- {
- _obj = obj;
- _nextByteIdx = 0;
- Current = default;
- }
-
- public byte Current { get; private set; }
-
- public bool MoveNext()
- {
- int nextByteIdx = _nextByteIdx;
- ReadOnlySpan<byte> objAsBytes = _obj.AsBytesSkipNullCheck();
-
- if ((uint)nextByteIdx < (uint)objAsBytes.Length)
- {
- Current = objAsBytes[nextByteIdx];
- _nextByteIdx = nextByteIdx + 1;
- return true;
- }
- else
- {
- return false;
- }
- }
-
- void IDisposable.Dispose()
- {
- // intentionally no-op
- }
-
- object IEnumerator.Current => Current;
-
- void IEnumerator.Reset() => throw NotImplemented.ByDesign;
- }
- }
-
- public readonly struct CharEnumerable : IEnumerable<char>
- {
- private readonly Utf8String _obj;
-
- internal CharEnumerable(Utf8String obj)
- {
- _obj = obj;
- }
-
- public Enumerator GetEnumerator() => new Enumerator(_obj);
-
- IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
-
- IEnumerator<char> IEnumerable<char>.GetEnumerator() => GetEnumerator();
-
- public struct Enumerator : IEnumerator<char>
- {
- private readonly Utf8String _obj;
- private uint _currentCharPair;
- private int _nextByteIdx;
-
- internal Enumerator(Utf8String obj)
- {
- _obj = obj;
- _currentCharPair = default;
- _nextByteIdx = 0;
- }
-
- public char Current => (char)_currentCharPair;
-
- public bool MoveNext()
- {
- // Make copies of fields to avoid tearing issues since we're
- // about to perform unsafe accesses.
-
- uint currentCharPair = _currentCharPair;
- if (currentCharPair > char.MaxValue)
- {
- // There was a surrogate pair smuggled in here from a previous operation.
- // Shift out the high surrogate value and return immediately.
-
- _currentCharPair = currentCharPair >> 16;
- return true;
- }
-
- ReadOnlySpan<byte> bytes = _obj.AsBytesSkipNullCheck();
- int nextByteIdx = _nextByteIdx;
-
- if ((uint)nextByteIdx >= (uint)bytes.Length)
- {
- return false; // no more data
- }
-
- // TODO_UTF8STRING: Can we skip correctness checks below?
- // Perhaps not, this enumerator struct is potentially tearable.
-
- OperationStatus status = Rune.DecodeFromUtf8(bytes.Slice(nextByteIdx), out Rune currentRune, out int bytesConsumedJustNow);
- Debug.Assert(status == OperationStatus.Done);
-
- _nextByteIdx = nextByteIdx + bytesConsumedJustNow;
-
- if (currentRune.IsBmp)
- {
- // Common case - BMP scalar value.
-
- _currentCharPair = (uint)currentRune.Value;
- }
- else
- {
- // Uncommon case - supplementary (astral) plane scalar value.
- // We'll smuggle the two UTF-16 code units into a single 32-bit value,
- // with the leading surrogate packed into the low 16 bits of the value,
- // and the trailing surrogate packed into the high 16 bits of the value.
-
- UnicodeUtility.GetUtf16SurrogatesFromSupplementaryPlaneScalar((uint)currentRune.Value, out char leadingCodeUnit, out char trailingCodeUnit);
- _currentCharPair = (uint)leadingCodeUnit + ((uint)trailingCodeUnit << 16);
- }
-
- return true;
- }
-
- void IDisposable.Dispose()
- {
- // intentionally no-op
- }
-
- object IEnumerator.Current => Current;
-
- void IEnumerator.Reset() => throw NotImplemented.ByDesign;
- }
- }
-
- public readonly struct RuneEnumerable : IEnumerable<Rune>
- {
- private readonly Utf8String _obj;
-
- internal RuneEnumerable(Utf8String obj)
- {
- _obj = obj;
- }
-
- public Enumerator GetEnumerator() => new Enumerator(_obj);
-
- IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
-
- IEnumerator<Rune> IEnumerable<Rune>.GetEnumerator() => GetEnumerator();
-
- public struct Enumerator : IEnumerator<Rune>
- {
- private readonly Utf8String _obj;
- private Rune _currentRune;
- private int _nextByteIdx;
-
- internal Enumerator(Utf8String obj)
- {
- _obj = obj;
- _currentRune = default;
- _nextByteIdx = 0;
- }
-
- public Rune Current => _currentRune;
-
- public bool MoveNext()
- {
- // Make copies of fields to avoid tearing issues since we're
- // about to perform unsafe accesses.
-
- ReadOnlySpan<byte> bytes = _obj.AsBytesSkipNullCheck();
- int nextByteIdx = _nextByteIdx;
-
- if ((uint)nextByteIdx >= (uint)bytes.Length)
- {
- return false; // no more data
- }
-
- bytes = bytes.Slice(nextByteIdx);
-
- // TODO_UTF8STRING: Can we skip correctness checks below?
- // Perhaps not, this enumerator struct is potentially tearable.
-
- OperationStatus status = Rune.DecodeFromUtf8(bytes, out _currentRune, out int bytesConsumedJustNow);
- Debug.Assert(status == OperationStatus.Done);
-
- _nextByteIdx = nextByteIdx + bytesConsumedJustNow;
- return true;
- }
-
- void IDisposable.Dispose()
- {
- // intentionally no-op
- }
-
- object IEnumerator.Current => Current;
-
- void IEnumerator.Reset() => throw NotImplemented.ByDesign;
- }
- }
- }
-}
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System.Collections;
-using System.Collections.Generic;
-using System.ComponentModel;
-using System.Diagnostics;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-using System.Text;
-using System.Text.Unicode;
-
-#if SYSTEM_PRIVATE_CORELIB
-using Internal.Runtime.CompilerServices;
-#endif
-
-namespace System
-{
- public sealed partial class Utf8String
- {
- [StackTraceHidden]
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- internal static void CheckSplitOptions(Utf8StringSplitOptions options)
- {
- if ((uint)options > (uint)(Utf8StringSplitOptions.RemoveEmptyEntries | Utf8StringSplitOptions.TrimEntries))
- {
- CheckSplitOptions_Throw(options);
- }
- }
-
- [StackTraceHidden]
- private static void CheckSplitOptions_Throw(Utf8StringSplitOptions options)
- {
- throw new ArgumentOutOfRangeException(
- paramName: nameof(options),
- message: SR.Format(SR.Arg_EnumIllegalVal, (int)options));
- }
-
- /// <summary>
- /// Substrings this <see cref="Utf8String"/> without bounds checking.
- /// </summary>
- private Utf8String InternalSubstring(int startIndex, int length)
- {
- Debug.Assert(startIndex >= 0, "StartIndex cannot be negative.");
- Debug.Assert(startIndex <= this.Length, "StartIndex cannot point beyond the end of the string (except to the null terminator).");
- Debug.Assert(length >= 0, "Length cannot be negative.");
- Debug.Assert(startIndex + length <= this.Length, "StartIndex and Length cannot point beyond the end of the string.");
-
- Debug.Assert(length != 0 && length != this.Length, "Caller should handle Length boundary conditions.");
-
- // Since Utf8String instances must contain well-formed UTF-8 data, we cannot allow a substring such that
- // either boundary of the new substring splits a multi-byte UTF-8 subsequence. Fortunately this is a very
- // easy check: since we assume the original buffer consisted entirely of well-formed UTF-8 data, all we
- // need to do is check that neither the substring we're about to create nor the substring that would
- // follow immediately thereafter begins with a UTF-8 continuation byte. Should this occur, it means that
- // the UTF-8 lead byte is in a prior substring, which would indicate a multi-byte sequence has been split.
- // It's ok for us to dereference the element immediately after the end of the Utf8String instance since
- // we know it's a null terminator.
-
- if (Utf8Utility.IsUtf8ContinuationByte(DangerousGetMutableReference(startIndex))
- || Utf8Utility.IsUtf8ContinuationByte(DangerousGetMutableReference(startIndex + length)))
- {
- ThrowImproperStringSplit();
- }
-
- Utf8String newString = FastAllocateSkipZeroInit(length);
-#if SYSTEM_PRIVATE_CORELIB
- Buffer.Memmove(ref newString.DangerousGetMutableReference(), ref this.DangerousGetMutableReference(startIndex), (uint)length);
-#else
- this.GetSpan().Slice(startIndex, length).CopyTo(newString.DangerousGetMutableSpan());
-#endif
-
- return newString;
- }
-
- private Utf8String InternalSubstringWithoutCorrectnessChecks(int startIndex, int length)
- {
- Debug.Assert(startIndex >= 0, "StartIndex cannot be negative.");
- Debug.Assert(startIndex <= this.Length, "StartIndex cannot point beyond the end of the string (except to the null terminator).");
- Debug.Assert(length >= 0, "Length cannot be negative.");
- Debug.Assert(startIndex + length <= this.Length, "StartIndex and Length cannot point beyond the end of the string.");
-
- // In debug mode, perform the checks anyway. It's ok if we read just past the end of the
- // Utf8String instance, since we'll just be reading the null terminator (which is safe).
-
- Debug.Assert(!Utf8Utility.IsUtf8ContinuationByte(DangerousGetMutableReference(startIndex)), "Somebody is trying to split this Utf8String improperly.");
- Debug.Assert(!Utf8Utility.IsUtf8ContinuationByte(DangerousGetMutableReference(startIndex + length)), "Somebody is trying to split this Utf8String improperly.");
-
- if (length == 0)
- {
- return Empty;
- }
- else if (length == this.Length)
- {
- return this;
- }
- else
- {
- Utf8String newString = FastAllocateSkipZeroInit(length);
-#if SYSTEM_PRIVATE_CORELIB
- Buffer.Memmove(ref newString.DangerousGetMutableReference(), ref this.DangerousGetMutableReference(startIndex), (uint)length);
-#else
- this.GetSpan().Slice(startIndex, length).CopyTo(newString.DangerousGetMutableSpan());
-#endif
- return newString;
- }
- }
-
- [StackTraceHidden]
- internal static void ThrowImproperStringSplit()
- {
- throw new InvalidOperationException(
- message: SR.Utf8String_CannotSplitMultibyteSubsequence);
- }
-
- internal Utf8String Substring(int startIndex, int length)
- {
- ValidateStartIndexAndLength(startIndex, length);
-
- // Optimizations: since instances are immutable, we can return 'this' or the known
- // Empty instance if the caller passed us a startIndex at the string boundary.
-
- if (length == 0)
- {
- return Empty;
- }
-
- if (length == this.Length)
- {
- return this;
- }
-
- return InternalSubstring(startIndex, length);
- }
-
- public SplitResult Split(char separator, Utf8StringSplitOptions options = Utf8StringSplitOptions.None)
- {
- if (!Rune.TryCreate(separator, out Rune rune))
- {
- throw new ArgumentOutOfRangeException(
- paramName: nameof(separator),
- message: SR.ArgumentOutOfRange_Utf16SurrogatesDisallowed);
- }
-
- CheckSplitOptions(options);
-
- return new SplitResult(this, rune, options);
- }
-
- public SplitResult Split(Rune separator, Utf8StringSplitOptions options = Utf8StringSplitOptions.None)
- {
- CheckSplitOptions(options);
-
- return new SplitResult(this, separator, options);
- }
-
- public SplitResult Split(Utf8String separator, Utf8StringSplitOptions options = Utf8StringSplitOptions.None)
- {
- if (IsNullOrEmpty(separator))
- {
- throw new ArgumentException(
- paramName: nameof(separator),
- message: SR.Argument_CannotBeNullOrEmpty);
- }
-
- CheckSplitOptions(options);
-
- return new SplitResult(this, separator, options);
- }
-
- /// <summary>
- /// Locates <paramref name="separator"/> within this <see cref="Utf8String"/> instance, creating <see cref="Utf8String"/>
- /// instances which represent the data on either side of the separator. If <paramref name="separator"/> is not found
- /// within this <see cref="Utf8String"/> instance, returns the tuple "(this, null)".
- /// </summary>
- /// <remarks>
- /// An ordinal search is performed.
- /// </remarks>
- public SplitOnResult SplitOn(char separator)
- {
- return TryFind(separator, out Range range) ? new SplitOnResult(this, range) : new SplitOnResult(this);
- }
-
- /// <summary>
- /// Locates <paramref name="separator"/> within this <see cref="Utf8String"/> instance, creating <see cref="Utf8String"/>
- /// instances which represent the data on either side of the separator. If <paramref name="separator"/> is not found
- /// within this <see cref="Utf8String"/> instance, returns the tuple "(this, null)".
- /// </summary>
- /// <remarks>
- /// The search is performed using the specified <paramref name="comparisonType"/>.
- /// </remarks>
- public SplitOnResult SplitOn(char separator, StringComparison comparisonType)
- {
- return TryFind(separator, comparisonType, out Range range) ? new SplitOnResult(this, range) : new SplitOnResult(this);
- }
-
- /// <summary>
- /// Locates <paramref name="separator"/> within this <see cref="Utf8String"/> instance, creating <see cref="Utf8String"/>
- /// instances which represent the data on either side of the separator. If <paramref name="separator"/> is not found
- /// within this <see cref="Utf8String"/> instance, returns the tuple "(this, null)".
- /// </summary>
- /// <remarks>
- /// An ordinal search is performed.
- /// </remarks>
- public SplitOnResult SplitOn(Rune separator)
- {
- return TryFind(separator, out Range range) ? new SplitOnResult(this, range) : new SplitOnResult(this);
- }
-
- /// <summary>
- /// Locates <paramref name="separator"/> within this <see cref="Utf8String"/> instance, creating <see cref="Utf8String"/>
- /// instances which represent the data on either side of the separator. If <paramref name="separator"/> is not found
- /// within this <see cref="Utf8String"/> instance, returns the tuple "(this, null)".
- /// </summary>
- /// <remarks>
- /// The search is performed using the specified <paramref name="comparisonType"/>.
- /// </remarks>
- public SplitOnResult SplitOn(Rune separator, StringComparison comparisonType)
- {
- return TryFind(separator, comparisonType, out Range range) ? new SplitOnResult(this, range) : new SplitOnResult(this);
- }
-
- /// <summary>
- /// Locates <paramref name="separator"/> within this <see cref="Utf8String"/> instance, creating <see cref="Utf8String"/>
- /// instances which represent the data on either side of the separator. If <paramref name="separator"/> is not found
- /// within this <see cref="Utf8String"/> instance, returns the tuple "(this, null)".
- /// </summary>
- /// <remarks>
- /// An ordinal search is performed.
- /// </remarks>
- public SplitOnResult SplitOn(Utf8String separator)
- {
- return TryFind(separator, out Range range) ? new SplitOnResult(this, range) : new SplitOnResult(this);
- }
-
- /// <summary>
- /// Locates the last occurrence of <paramref name="separator"/> within this <see cref="Utf8String"/> instance, creating <see cref="Utf8String"/>
- /// instances which represent the data on either side of the separator. If <paramref name="separator"/> is not found
- /// within this <see cref="Utf8String"/> instance, returns the tuple "(this, null)".
- /// </summary>
- /// <remarks>
- /// The search is performed using the specified <paramref name="comparisonType"/>.
- /// </remarks>
- public SplitOnResult SplitOn(Utf8String separator, StringComparison comparisonType)
- {
- return TryFind(separator, comparisonType, out Range range) ? new SplitOnResult(this, range) : new SplitOnResult(this);
- }
-
- /// <summary>
- /// Locates the last occurrence of <paramref name="separator"/> within this <see cref="Utf8String"/> instance, creating <see cref="Utf8String"/>
- /// instances which represent the data on either side of the separator. If <paramref name="separator"/> is not found
- /// within this <see cref="Utf8String"/> instance, returns the tuple "(this, null)".
- /// </summary>
- /// <remarks>
- /// An ordinal search is performed.
- /// </remarks>
- public SplitOnResult SplitOnLast(char separator)
- {
- return TryFindLast(separator, out Range range) ? new SplitOnResult(this, range) : new SplitOnResult(this);
- }
-
- /// <summary>
- /// Locates the last occurrence of <paramref name="separator"/> within this <see cref="Utf8String"/> instance, creating <see cref="Utf8String"/>
- /// instances which represent the data on either side of the separator. If <paramref name="separator"/> is not found
- /// within this <see cref="Utf8String"/> instance, returns the tuple "(this, null)".
- /// </summary>
- /// <remarks>
- /// The search is performed using the specified <paramref name="comparisonType"/>.
- /// </remarks>
- public SplitOnResult SplitOnLast(char separator, StringComparison comparisonType)
- {
- return TryFindLast(separator, comparisonType, out Range range) ? new SplitOnResult(this, range) : new SplitOnResult(this);
- }
-
- /// <summary>
- /// Locates the last occurrence of <paramref name="separator"/> within this <see cref="Utf8String"/> instance, creating <see cref="Utf8String"/>
- /// instances which represent the data on either side of the separator. If <paramref name="separator"/> is not found
- /// within this <see cref="Utf8String"/> instance, returns the tuple "(this, null)".
- /// </summary>
- /// <remarks>
- /// An ordinal search is performed.
- /// </remarks>
- public SplitOnResult SplitOnLast(Rune separator)
- {
- return TryFindLast(separator, out Range range) ? new SplitOnResult(this, range) : new SplitOnResult(this);
- }
-
- /// <summary>
- /// Locates the last occurrence of <paramref name="separator"/> within this <see cref="Utf8String"/> instance, creating <see cref="Utf8String"/>
- /// instances which represent the data on either side of the separator. If <paramref name="separator"/> is not found
- /// within this <see cref="Utf8String"/> instance, returns the tuple "(this, null)".
- /// </summary>
- /// <remarks>
- /// The search is performed using the specified <paramref name="comparisonType"/>.
- /// </remarks>
- public SplitOnResult SplitOnLast(Rune separator, StringComparison comparisonType)
- {
- return TryFindLast(separator, comparisonType, out Range range) ? new SplitOnResult(this, range) : new SplitOnResult(this);
- }
-
- /// <summary>
- /// Locates the last occurrence of <paramref name="separator"/> within this <see cref="Utf8String"/> instance, creating <see cref="Utf8String"/>
- /// instances which represent the data on either side of the separator. If <paramref name="separator"/> is not found
- /// within this <see cref="Utf8String"/> instance, returns the tuple "(this, null)".
- /// </summary>
- /// <remarks>
- /// An ordinal search is performed.
- /// </remarks>
- public SplitOnResult SplitOnLast(Utf8String separator)
- {
- return TryFindLast(separator, out Range range) ? new SplitOnResult(this, range) : new SplitOnResult(this);
- }
-
- /// <summary>
- /// Locates the last occurrence of <paramref name="separator"/> within this <see cref="Utf8String"/> instance, creating <see cref="Utf8String"/>
- /// instances which represent the data on either side of the separator. If <paramref name="separator"/> is not found
- /// within this <see cref="Utf8String"/> instance, returns the tuple "(this, null)".
- /// </summary>
- /// <remarks>
- /// The search is performed using the specified <paramref name="comparisonType"/>.
- /// </remarks>
- public SplitOnResult SplitOnLast(Utf8String separator, StringComparison comparisonType)
- {
- return TryFindLast(separator, comparisonType, out Range range) ? new SplitOnResult(this, range) : new SplitOnResult(this);
- }
-
- /// <summary>
- /// Trims whitespace from the beginning and the end of this <see cref="Utf8String"/>,
- /// returning a new <see cref="Utf8String"/> containing the resulting slice.
- /// </summary>
- public Utf8String Trim() => TrimHelper(TrimType.Both);
-
- /// <summary>
- /// Trims whitespace from only the end of this <see cref="Utf8String"/>,
- /// returning a new <see cref="Utf8String"/> containing the resulting slice.
- /// </summary>
- public Utf8String TrimEnd() => TrimHelper(TrimType.Tail);
-
- private Utf8String TrimHelper(TrimType trimType)
- {
- Utf8Span trimmedSpan = this.AsSpan().TrimHelper(trimType);
-
- // Try to avoid allocating a new Utf8String instance if possible.
- // Otherwise, allocate a new substring wrapped around the resulting slice.
-
- return (trimmedSpan.Length == this.Length) ? this : trimmedSpan.ToUtf8String();
- }
-
- /// <summary>
- /// Trims whitespace from only the beginning of this <see cref="Utf8String"/>,
- /// returning a new <see cref="Utf8String"/> containing the resulting slice.
- /// </summary>
- public Utf8String TrimStart() => TrimHelper(TrimType.Head);
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- [StackTraceHidden]
- private void ValidateStartIndexAndLength(int startIndex, int length)
- {
-#if TARGET_64BIT
- // See comment in Span<T>.Slice for how this works.
- if ((ulong)(uint)startIndex + (ulong)(uint)length > (ulong)(uint)this.Length)
- ThrowHelper.ThrowArgumentOutOfRangeException();
-#else
- if ((uint)startIndex > (uint)this.Length || (uint)length > (uint)(this.Length - startIndex))
- ThrowHelper.ThrowArgumentOutOfRangeException();
-#endif
- }
-
- [StructLayout(LayoutKind.Auto)]
- public readonly struct SplitResult : IEnumerable<Utf8String?>
- {
- private readonly State _state;
-
- internal SplitResult(Utf8String source, Rune searchRune, Utf8StringSplitOptions splitOptions)
- {
- _state = new State
- {
- FullSearchSpace = source,
- OffsetAtWhichToContinueSearch = 0,
- SearchRune = searchRune.Value,
- SearchTerm = default,
- SplitOptions = splitOptions
- };
- }
-
- internal SplitResult(Utf8String source, Utf8String searchTerm, Utf8StringSplitOptions splitOptions)
- {
- _state = new State
- {
- FullSearchSpace = source,
- OffsetAtWhichToContinueSearch = 0,
- SearchRune = -1,
- SearchTerm = searchTerm,
- SplitOptions = splitOptions
- };
- }
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- public void Deconstruct(out Utf8String? item1, out Utf8String? item2)
- {
- _state.DeconstructHelper(_state.GetRemainingSearchSpace(), out Utf8Span nextItem, out Utf8Span remainder);
- item1 = TrimIfNeeded(nextItem);
-
- item2 = TrimIfNeeded(remainder);
- }
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- public void Deconstruct(out Utf8String? item1, out Utf8String? item2, out Utf8String? item3)
- {
- _state.DeconstructHelper(_state.GetRemainingSearchSpace(), out Utf8Span nextItem, out Utf8Span remainder);
- item1 = TrimIfNeeded(nextItem);
-
- _state.DeconstructHelper(in remainder, out nextItem, out remainder);
- item2 = TrimIfNeeded(nextItem);
-
- item3 = TrimIfNeeded(remainder);
- }
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- public void Deconstruct(out Utf8String? item1, out Utf8String? item2, out Utf8String? item3, out Utf8String? item4)
- {
- _state.DeconstructHelper(_state.GetRemainingSearchSpace(), out Utf8Span nextItem, out Utf8Span remainder);
- item1 = TrimIfNeeded(nextItem);
-
- _state.DeconstructHelper(in remainder, out nextItem, out remainder);
- item2 = TrimIfNeeded(nextItem);
-
- _state.DeconstructHelper(in remainder, out nextItem, out remainder);
- item3 = TrimIfNeeded(nextItem);
-
- item4 = TrimIfNeeded(remainder);
- }
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- public void Deconstruct(out Utf8String? item1, out Utf8String? item2, out Utf8String? item3, out Utf8String? item4, out Utf8String? item5)
- {
- _state.DeconstructHelper(_state.GetRemainingSearchSpace(), out Utf8Span nextItem, out Utf8Span remainder);
- item1 = TrimIfNeeded(nextItem);
-
- _state.DeconstructHelper(in remainder, out nextItem, out remainder);
- item2 = TrimIfNeeded(nextItem);
-
- _state.DeconstructHelper(in remainder, out nextItem, out remainder);
- item3 = TrimIfNeeded(nextItem);
-
- _state.DeconstructHelper(in remainder, out nextItem, out remainder);
- item4 = TrimIfNeeded(nextItem);
-
- item5 = TrimIfNeeded(remainder);
- }
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- public void Deconstruct(out Utf8String? item1, out Utf8String? item2, out Utf8String? item3, out Utf8String? item4, out Utf8String? item5, out Utf8String? item6)
- {
- _state.DeconstructHelper(_state.GetRemainingSearchSpace(), out Utf8Span nextItem, out Utf8Span remainder);
- item1 = TrimIfNeeded(nextItem);
-
- _state.DeconstructHelper(in remainder, out nextItem, out remainder);
- item2 = TrimIfNeeded(nextItem);
-
- _state.DeconstructHelper(in remainder, out nextItem, out remainder);
- item3 = TrimIfNeeded(nextItem);
-
- _state.DeconstructHelper(in remainder, out nextItem, out remainder);
- item4 = TrimIfNeeded(nextItem);
-
- _state.DeconstructHelper(in remainder, out nextItem, out remainder);
- item5 = TrimIfNeeded(nextItem);
-
- item6 = TrimIfNeeded(remainder);
- }
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- public void Deconstruct(out Utf8String? item1, out Utf8String? item2, out Utf8String? item3, out Utf8String? item4, out Utf8String? item5, out Utf8String? item6, out Utf8String? item7)
- {
- _state.DeconstructHelper(_state.GetRemainingSearchSpace(), out Utf8Span nextItem, out Utf8Span remainder);
- item1 = TrimIfNeeded(nextItem);
-
- _state.DeconstructHelper(in remainder, out nextItem, out remainder);
- item2 = TrimIfNeeded(nextItem);
-
- _state.DeconstructHelper(in remainder, out nextItem, out remainder);
- item3 = TrimIfNeeded(nextItem);
-
- _state.DeconstructHelper(in remainder, out nextItem, out remainder);
- item4 = TrimIfNeeded(nextItem);
-
- _state.DeconstructHelper(in remainder, out nextItem, out remainder);
- item5 = TrimIfNeeded(nextItem);
-
- _state.DeconstructHelper(in remainder, out nextItem, out remainder);
- item6 = TrimIfNeeded(nextItem);
-
- item7 = TrimIfNeeded(remainder);
- }
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- public void Deconstruct(out Utf8String? item1, out Utf8String? item2, out Utf8String? item3, out Utf8String? item4, out Utf8String? item5, out Utf8String? item6, out Utf8String? item7, out Utf8String? item8)
- {
- _state.DeconstructHelper(_state.GetRemainingSearchSpace(), out Utf8Span nextItem, out Utf8Span remainder);
- item1 = TrimIfNeeded(nextItem);
-
- _state.DeconstructHelper(in remainder, out nextItem, out remainder);
- item2 = TrimIfNeeded(nextItem);
-
- _state.DeconstructHelper(in remainder, out nextItem, out remainder);
- item3 = TrimIfNeeded(nextItem);
-
- _state.DeconstructHelper(in remainder, out nextItem, out remainder);
- item4 = TrimIfNeeded(nextItem);
-
- _state.DeconstructHelper(in remainder, out nextItem, out remainder);
- item5 = TrimIfNeeded(nextItem);
-
- _state.DeconstructHelper(in remainder, out nextItem, out remainder);
- item6 = TrimIfNeeded(nextItem);
-
- _state.DeconstructHelper(in remainder, out nextItem, out remainder);
- item7 = TrimIfNeeded(nextItem);
-
- item8 = TrimIfNeeded(remainder);
- }
-
- public Enumerator GetEnumerator() => new Enumerator(this);
-
- private unsafe Utf8String? TrimIfNeeded(Utf8Span span)
- {
- if ((_state.SplitOptions & Utf8StringSplitOptions.TrimEntries) != 0)
- {
- span = span.Trim();
- }
-
- if (span.Length < _state.FullSearchSpace.Length)
- {
- if (!span.IsEmpty)
- {
- return span.ToUtf8String();
- }
- else
- {
- // normalize empty spans to null if needed, otherwise normalize to Utf8String.Empty
-
- if ((_state.SplitOptions & Utf8StringSplitOptions.RemoveEmptyEntries) != 0
- || Unsafe.AreSame(ref span.DangerousGetMutableReference(), ref Unsafe.AsRef<byte>(null)))
- {
- return null;
- }
-
- return Empty;
- }
- }
- else
- {
- // Don't bother making a copy of the entire Utf8String instance;
- // just return the original value.
-
- return _state.FullSearchSpace;
- }
- }
-
- IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
-
- IEnumerator<Utf8String?> IEnumerable<Utf8String?>.GetEnumerator() => GetEnumerator();
-
- [StructLayout(LayoutKind.Auto)]
- public struct Enumerator : IEnumerator<Utf8String?>
- {
- private const Utf8StringSplitOptions HALT_ENUMERATION = (Utf8StringSplitOptions)int.MinValue;
-
- private Utf8String? _current;
- private State _state;
-
- internal Enumerator(SplitResult result)
- {
- _current = null;
- _state = result._state; // copy by value
- }
-
- public Utf8String? Current => _current;
-
- public bool MoveNext()
- {
- bool wasMatchFound = _state.DeconstructHelper(_state.GetRemainingSearchSpace(), out Utf8Span firstItem, out Utf8Span remainder);
-
- _current = (firstItem.IsEmpty) ? Empty : (firstItem.Length == _state.FullSearchSpace.Length) ? _state.FullSearchSpace : firstItem.ToUtf8String();
- _state.OffsetAtWhichToContinueSearch = _state.FullSearchSpace.Length - remainder.Length;
-
- if (wasMatchFound)
- {
- return true;
- }
-
- // At this point, the search term was not found within the search space. '_current' contains the last
- // bit of data after the final occurrence of the search term. We'll also set a flag saying that we've
- // completed enumeration.
-
- if (firstItem.IsEmpty && (_state.SplitOptions & Utf8StringSplitOptions.RemoveEmptyEntries) != 0)
- {
- return false;
- }
-
- if ((_state.SplitOptions & HALT_ENUMERATION) != 0)
- {
- return false;
- }
-
- _state.SplitOptions |= HALT_ENUMERATION; // prevents yielding <empty> forever at end of split
-
- return true;
- }
-
- void IDisposable.Dispose()
- {
- // no-op
- }
-
- object? IEnumerator.Current => Current;
-
- void IEnumerator.Reset()
- {
- throw new NotSupportedException();
- }
- }
-
- [StructLayout(LayoutKind.Auto)]
- private struct State // fully mutable
- {
- internal Utf8String FullSearchSpace;
- internal int OffsetAtWhichToContinueSearch;
- internal int SearchRune; // -1 if not specified, takes less space than "Rune?"
- internal Utf8String? SearchTerm;
- internal Utf8StringSplitOptions SplitOptions;
-
- // Returns 'true' if a match was found, 'false' otherwise.
- internal readonly bool DeconstructHelper(in Utf8Span source, out Utf8Span firstItem, out Utf8Span remainder)
- {
- // n.b. Our callers might pass the same reference for 'source' and 'remainder'.
- // We need to take care not to read 'source' after writing 'remainder'.
-
- bool wasMatchFound;
- ref readonly Utf8Span searchSpan = ref source;
-
- while (true)
- {
- if (searchSpan.IsEmpty)
- {
- firstItem = searchSpan;
- remainder = default;
- wasMatchFound = false;
- break;
- }
-
- Range matchRange;
-
- int searchRune = SearchRune; // local copy so as to avoid struct tearing
- if (searchRune >= 0)
- {
-#if NETCOREAPP3_0
- wasMatchFound = searchSpan.TryFind(new Rune((uint)searchRune), out matchRange);
-#else
- wasMatchFound = searchSpan.TryFind(Rune.UnsafeCreate((uint)searchRune), out matchRange);
-#endif
- }
- else
- {
- wasMatchFound = searchSpan.TryFind(SearchTerm, out matchRange);
- }
-
- if (!wasMatchFound)
- {
- // If no match was found, we move 'source' to 'firstItem', trim if necessary, and return right away.
-
- firstItem = searchSpan;
-
- if ((SplitOptions & Utf8StringSplitOptions.TrimEntries) != 0)
- {
- firstItem = firstItem.Trim();
- }
-
- remainder = default;
- }
- else
- {
- // Otherwise, if a match was found, split the result across 'firstItem' and 'remainder',
- // applying trimming if necessary.
-
- firstItem = searchSpan[..matchRange.Start]; // TODO_UTF8STRING: Could use unsafe slicing as optimization
- remainder = searchSpan[matchRange.End..]; // TODO_UTF8STRING: Could use unsafe slicing as optimization
-
- if ((SplitOptions & Utf8StringSplitOptions.TrimEntries) != 0)
- {
- firstItem = firstItem.Trim();
- }
-
- // If we're asked to remove empty entries, loop until there's a real value in 'firstItem'.
-
- if ((SplitOptions & Utf8StringSplitOptions.RemoveEmptyEntries) != 0 && firstItem.IsEmpty)
- {
- searchSpan = ref remainder;
- continue;
- }
- }
-
- break; // loop only if explicit 'continue' statement was hit
- }
-
- return wasMatchFound;
- }
-
- internal Utf8Span GetRemainingSearchSpace()
- {
- // TODO_UTF8STRING: The slice below can be optimized by performing a specialized bounds check
- // and multi-byte subsequence check, since we don't need to check the end of the span.
- // If we do optimize this we need to remember to make local copies of the fields we're reading
- // to guard against torn structs.
-
- return FullSearchSpace.AsSpanSkipNullCheck()[OffsetAtWhichToContinueSearch..];
- }
- }
- }
-
- [StructLayout(LayoutKind.Auto)]
- public readonly struct SplitOnResult
- {
- // Used when there is no match.
- internal SplitOnResult(Utf8String originalSearchSpace)
- {
- Before = originalSearchSpace;
- After = null;
- }
-
- // Used when a match is found.
- internal SplitOnResult(Utf8String originalSearchSpace, Range searchTermMatchRange)
- {
- (int startIndex, int length) = searchTermMatchRange.GetOffsetAndLength(originalSearchSpace.Length);
-
- // TODO_UTF8STRING: The below indexer performs correctness checks. We can skip these checks (and even the
- // bounds checks more generally) since we know the inputs are all valid and the containing struct is not
- // subject to tearing.
-
- Before = originalSearchSpace[..startIndex];
- After = originalSearchSpace[(startIndex + length)..];
- }
-
- public Utf8String? After { get; }
- public Utf8String Before { get; }
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- public void Deconstruct(out Utf8String before, out Utf8String? after)
- {
- before = Before;
- after = After;
- }
- }
- }
-}
-
-#if !SYSTEM_PRIVATE_CORELIB
-namespace System.Diagnostics
-{
- [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Struct, Inherited = false)]
- internal sealed class StackTraceHiddenAttribute : Attribute
- {
- public StackTraceHiddenAttribute() { }
- }
-}
-#endif
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System.Text;
-
-namespace System
-{
- public sealed partial class Utf8String
- {
- /// <summary>
- /// Attempts to locate the target <paramref name="value"/> within this <see cref="Utf8String"/> instance.
- /// If <paramref name="value"/> is found, returns <see langword="true"/> and sets <paramref name="range"/> to
- /// the location where <paramref name="value"/> occurs within this <see cref="Utf8String"/> instance.
- /// If <paramref name="value"/> is not found, returns <see langword="false"/> and sets <paramref name="range"/>
- /// to <see langword="default"/>.
- /// </summary>
- /// <remarks>
- /// An ordinal search is performed.
- /// </remarks>
- public bool TryFind(char value, out Range range) => this.AsSpanSkipNullCheck().TryFind(value, out range);
-
- /// <summary>
- /// Attempts to locate the target <paramref name="value"/> within this <see cref="Utf8String"/> instance.
- /// If <paramref name="value"/> is found, returns <see langword="true"/> and sets <paramref name="range"/> to
- /// the location where <paramref name="value"/> occurs within this <see cref="Utf8String"/> instance.
- /// If <paramref name="value"/> is not found, returns <see langword="false"/> and sets <paramref name="range"/>
- /// to <see langword="default"/>.
- /// </summary>
- /// <remarks>
- /// The search is performed using the specified <paramref name="comparisonType"/>.
- /// </remarks>
- public bool TryFind(char value, StringComparison comparisonType, out Range range) => this.AsSpanSkipNullCheck().TryFind(value, comparisonType, out range);
-
- /// <summary>
- /// Attempts to locate the target <paramref name="value"/> within this <see cref="Utf8String"/> instance.
- /// If <paramref name="value"/> is found, returns <see langword="true"/> and sets <paramref name="range"/> to
- /// the location where <paramref name="value"/> occurs within this <see cref="Utf8String"/> instance.
- /// If <paramref name="value"/> is not found, returns <see langword="false"/> and sets <paramref name="range"/>
- /// to <see langword="default"/>.
- /// </summary>
- /// <remarks>
- /// An ordinal search is performed.
- /// </remarks>
- public bool TryFind(Rune value, out Range range) => this.AsSpanSkipNullCheck().TryFind(value, out range);
-
- /// <summary>
- /// Attempts to locate the target <paramref name="value"/> within this <see cref="Utf8String"/> instance.
- /// If <paramref name="value"/> is found, returns <see langword="true"/> and sets <paramref name="range"/> to
- /// the location where <paramref name="value"/> occurs within this <see cref="Utf8String"/> instance.
- /// If <paramref name="value"/> is not found, returns <see langword="false"/> and sets <paramref name="range"/>
- /// to <see langword="default"/>.
- /// </summary>
- /// <remarks>
- /// The search is performed using the specified <paramref name="comparisonType"/>.
- /// </remarks>
- public bool TryFind(Rune value, StringComparison comparisonType, out Range range) => this.AsSpanSkipNullCheck().TryFind(value, comparisonType, out range);
-
- /// <summary>
- /// Attempts to locate the target <paramref name="value"/> within this <see cref="Utf8String"/> instance.
- /// If <paramref name="value"/> is found, returns <see langword="true"/> and sets <paramref name="range"/> to
- /// the location where <paramref name="value"/> occurs within this <see cref="Utf8String"/> instance.
- /// If <paramref name="value"/> is not found, returns <see langword="false"/> and sets <paramref name="range"/>
- /// to <see langword="default"/>.
- /// </summary>
- /// <remarks>
- /// An ordinal search is performed.
- /// </remarks>
- public bool TryFind(Utf8String value, out Range range)
- {
- if (value is null)
- {
- ThrowHelper.ThrowArgumentNullException(ExceptionArgument.value);
- }
-
- return this.AsSpanSkipNullCheck().TryFind(value, out range);
- }
-
- /// <summary>
- /// Attempts to locate the target <paramref name="value"/> within this <see cref="Utf8String"/> instance.
- /// If <paramref name="value"/> is found, returns <see langword="true"/> and sets <paramref name="range"/> to
- /// the location where <paramref name="value"/> occurs within this <see cref="Utf8String"/> instance.
- /// If <paramref name="value"/> is not found, returns <see langword="false"/> and sets <paramref name="range"/>
- /// to <see langword="default"/>.
- /// </summary>
- /// <remarks>
- /// The search is performed using the specified <paramref name="comparisonType"/>.
- /// </remarks>
- public bool TryFind(Utf8String value, StringComparison comparisonType, out Range range)
- {
- if (value is null)
- {
- ThrowHelper.ThrowArgumentNullException(ExceptionArgument.value);
- }
-
- return this.AsSpanSkipNullCheck().TryFind(value, comparisonType, out range);
- }
-
- /// <summary>
- /// Attempts to locate the last occurrence of the target <paramref name="value"/> within this <see cref="Utf8String"/> instance.
- /// If <paramref name="value"/> is found, returns <see langword="true"/> and sets <paramref name="range"/> to
- /// the location where <paramref name="value"/> occurs within this <see cref="Utf8String"/> instance.
- /// If <paramref name="value"/> is not found, returns <see langword="false"/> and sets <paramref name="range"/>
- /// to <see langword="default"/>.
- /// </summary>
- /// <remarks>
- /// An ordinal search is performed.
- /// </remarks>
- public bool TryFindLast(char value, out Range range) => this.AsSpanSkipNullCheck().TryFindLast(value, out range);
-
- /// <summary>
- /// Attempts to locate the last occurrence of the target <paramref name="value"/> within this <see cref="Utf8String"/> instance.
- /// If <paramref name="value"/> is found, returns <see langword="true"/> and sets <paramref name="range"/> to
- /// the location where <paramref name="value"/> occurs within this <see cref="Utf8String"/> instance.
- /// If <paramref name="value"/> is not found, returns <see langword="false"/> and sets <paramref name="range"/>
- /// to <see langword="default"/>.
- /// </summary>
- /// <remarks>
- /// The search is performed using the specified <paramref name="comparisonType"/>.
- /// </remarks>
- public bool TryFindLast(char value, StringComparison comparisonType, out Range range) => this.AsSpanSkipNullCheck().TryFindLast(value, comparisonType, out range);
-
- /// <summary>
- /// Attempts to locate the last occurrence of the target <paramref name="value"/> within this <see cref="Utf8String"/> instance.
- /// If <paramref name="value"/> is found, returns <see langword="true"/> and sets <paramref name="range"/> to
- /// the location where <paramref name="value"/> occurs within this <see cref="Utf8String"/> instance.
- /// If <paramref name="value"/> is not found, returns <see langword="false"/> and sets <paramref name="range"/>
- /// to <see langword="default"/>.
- /// </summary>
- /// <remarks>
- /// An ordinal search is performed.
- /// </remarks>
- public bool TryFindLast(Rune value, out Range range) => this.AsSpanSkipNullCheck().TryFindLast(value, out range);
-
- /// <summary>
- /// Attempts to locate the last occurrence of the target <paramref name="value"/> within this <see cref="Utf8String"/> instance.
- /// If <paramref name="value"/> is found, returns <see langword="true"/> and sets <paramref name="range"/> to
- /// the location where <paramref name="value"/> occurs within this <see cref="Utf8String"/> instance.
- /// If <paramref name="value"/> is not found, returns <see langword="false"/> and sets <paramref name="range"/>
- /// to <see langword="default"/>.
- /// </summary>
- /// <remarks>
- /// The search is performed using the specified <paramref name="comparisonType"/>.
- /// </remarks>
- public bool TryFindLast(Rune value, StringComparison comparisonType, out Range range) => this.AsSpanSkipNullCheck().TryFindLast(value, comparisonType, out range);
-
- /// <summary>
- /// Attempts to locate the last occurrence of the target <paramref name="value"/> within this <see cref="Utf8String"/> instance.
- /// If <paramref name="value"/> is found, returns <see langword="true"/> and sets <paramref name="range"/> to
- /// the location where <paramref name="value"/> occurs within this <see cref="Utf8String"/> instance.
- /// If <paramref name="value"/> is not found, returns <see langword="false"/> and sets <paramref name="range"/>
- /// to <see langword="default"/>.
- /// </summary>
- /// <remarks>
- /// An ordinal search is performed.
- /// </remarks>
- public bool TryFindLast(Utf8String value, out Range range)
- {
- if (value is null)
- {
- ThrowHelper.ThrowArgumentNullException(ExceptionArgument.value);
- }
-
- return this.AsSpanSkipNullCheck().TryFindLast(value, out range);
- }
-
- /// <summary>
- /// Attempts to locate the last occurrence of the target <paramref name="value"/> within this <see cref="Utf8String"/> instance.
- /// If <paramref name="value"/> is found, returns <see langword="true"/> and sets <paramref name="range"/> to
- /// the location where <paramref name="value"/> occurs within this <see cref="Utf8String"/> instance.
- /// If <paramref name="value"/> is not found, returns <see langword="false"/> and sets <paramref name="range"/>
- /// to <see langword="default"/>.
- /// </summary>
- /// <remarks>
- /// The search is performed using the specified <paramref name="comparisonType"/>.
- /// </remarks>
- public bool TryFindLast(Utf8String value, StringComparison comparisonType, out Range range)
- {
- if (value is null)
- {
- ThrowHelper.ThrowArgumentNullException(ExceptionArgument.value);
- }
-
- return this.AsSpanSkipNullCheck().TryFindLast(value, comparisonType, out range);
- }
- }
-}
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System.Buffers;
-using System.Diagnostics;
-using System.Diagnostics.CodeAnalysis;
-using System.Runtime.CompilerServices;
-using System.Text;
-using System.Text.Unicode;
-
-#if SYSTEM_PRIVATE_CORELIB
-using Internal.Runtime.CompilerServices;
-#endif
-
-namespace System
-{
- /// <summary>
- /// Represents an immutable string of UTF-8 code units.
- /// </summary>
- public sealed partial class Utf8String : IComparable<Utf8String?>, IEquatable<Utf8String?>
- {
- /*
- * STATIC FIELDS
- */
-
- public static readonly Utf8String Empty = FastAllocate(0);
-
- /*
- * OPERATORS
- */
-
- /// <summary>
- /// Compares two <see cref="Utf8String"/> instances for equality using a <see cref="StringComparison.Ordinal"/> comparer.
- /// </summary>
- public static bool operator ==(Utf8String? left, Utf8String? right) => Equals(left, right);
-
- /// <summary>
- /// Compares two <see cref="Utf8String"/> instances for inequality using a <see cref="StringComparison.Ordinal"/> comparer.
- /// </summary>
- public static bool operator !=(Utf8String? left, Utf8String? right) => !Equals(left, right);
-
- /// <summary>
- /// Projects a <see cref="Utf8String"/> instance as a <see cref="Utf8Span"/>.
- /// </summary>
- /// <param name="value"></param>
- public static implicit operator Utf8Span(Utf8String? value) => new Utf8Span(value);
-
- /*
- * INDEXERS
- */
-
- public Utf8String this[Range range]
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- get
- {
- // The two lines immediately below provide no bounds checking.
- // The Substring method we call will both perform a bounds check
- // and check for an improper split across a multi-byte subsequence.
-
- int startIdx = range.Start.GetOffset(Length);
- int endIdx = range.End.GetOffset(Length);
-
- return Substring(startIdx, endIdx - startIdx);
- }
- }
-
- /*
- * METHODS
- */
-
- /// <summary>
- /// Similar to <see cref="Utf8Extensions.AsSpan(Utf8String)"/>, but skips the null check on the input.
- /// Throws a <see cref="NullReferenceException"/> if the input is null.
- /// </summary>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- internal Utf8Span AsSpanSkipNullCheck()
- {
- return Utf8Span.UnsafeCreateWithoutValidation(this.AsBytesSkipNullCheck());
- }
-
- public int CompareTo(Utf8String? other)
- {
- // TODO_UTF8STRING: This is ordinal, but String.CompareTo uses CurrentCulture.
- // Is this acceptable? Should we perhaps just remove the interface?
-
- return Utf8StringComparer.Ordinal.Compare(this, other);
- }
-
- public int CompareTo(Utf8String? other, StringComparison comparison)
- {
- // TODO_UTF8STRING: We can avoid the virtual dispatch by moving the switch into this method.
-
- return Utf8StringComparer.FromComparison(comparison).Compare(this, other);
- }
-
- /// <summary>
- /// Returns a <em>mutable</em> reference to the element at index <paramref name="index"/>
- /// of this <see cref="Utf8String"/> instance. The index is not bounds-checked.
- /// </summary>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- internal ref byte DangerousGetMutableReference(int index)
- {
- Debug.Assert(index >= 0, "Caller should've performed bounds checking.");
- return ref DangerousGetMutableReference((uint)index);
- }
-
- /// <summary>
- /// Returns a <em>mutable</em> reference to the element at index <paramref name="index"/>
- /// of this <see cref="Utf8String"/> instance. The index is not bounds-checked.
- /// </summary>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- internal ref byte DangerousGetMutableReference(nuint index)
- {
- // Allow retrieving references to the null terminator.
-
- Debug.Assert(index <= (uint)Length, "Caller should've performed bounds checking.");
-#if SYSTEM_PRIVATE_CORELIB
- return ref Unsafe.AddByteOffset(ref DangerousGetMutableReference(), index);
-#else
- return ref Unsafe.AddByteOffset(ref DangerousGetMutableReference(), (nint)index);
-#endif
- }
-
- /// <summary>
- /// Performs an equality comparison using a <see cref="StringComparison.Ordinal"/> comparer.
- /// </summary>
- public override bool Equals(object? obj)
- {
- return (obj is Utf8String other) && this.Equals(other);
- }
-
- /// <summary>
- /// Performs an equality comparison using a <see cref="StringComparison.Ordinal"/> comparer.
- /// </summary>
- public bool Equals(Utf8String? value)
- {
- // First, a very quick check for referential equality.
-
- if (ReferenceEquals(this, value))
- {
- return true;
- }
-
- // Otherwise, perform a simple bitwise equality check.
-
- return !(value is null)
- && this.Length == value.Length
-#if SYSTEM_PRIVATE_CORELIB
- && SpanHelpers.SequenceEqual(ref this.DangerousGetMutableReference(), ref value.DangerousGetMutableReference(), (uint)Length);
-#else
- && this.GetSpan().SequenceEqual(value.GetSpan());
-#endif
- }
-
- /// <summary>
- /// Performs an equality comparison using the specified <see cref="StringComparison"/>.
- /// </summary>
- public bool Equals(Utf8String? value, StringComparison comparison) => Equals(this, value, comparison);
-
- /// <summary>
- /// Compares two <see cref="Utf8String"/> instances using a <see cref="StringComparison.Ordinal"/> comparer.
- /// </summary>
- public static bool Equals(Utf8String? left, Utf8String? right)
- {
- // First, a very quick check for referential equality.
-
- if (ReferenceEquals(left, right))
- {
- return true;
- }
-
- // Otherwise, perform a simple bitwise equality check.
-
- return !(left is null)
- && !(right is null)
- && left.Length == right.Length
-#if SYSTEM_PRIVATE_CORELIB
- && SpanHelpers.SequenceEqual(ref left.DangerousGetMutableReference(), ref right.DangerousGetMutableReference(), (uint)left.Length);
-#else
- && left.GetSpan().SequenceEqual(right.GetSpan());
-#endif
- }
-
- /// <summary>
- /// Performs an equality comparison using the specified <see cref="StringComparison"/>.
- /// </summary>
- public static bool Equals(Utf8String? a, Utf8String? b, StringComparison comparison)
- {
- // TODO_UTF8STRING: This perf can be improved, including removing
- // the virtual dispatch by putting the switch directly in this method.
-
- return Utf8StringComparer.FromComparison(comparison).Equals(a, b);
- }
-
- /// <summary>
- /// Returns a hash code using a <see cref="StringComparison.Ordinal"/> comparison.
- /// </summary>
- public override int GetHashCode()
- {
- // TODO_UTF8STRING: Consider whether this should use a different seed than String.GetHashCode.
-
- ulong seed = Marvin.DefaultSeed;
-#if SYSTEM_PRIVATE_CORELIB
- return Marvin.ComputeHash32(ref DangerousGetMutableReference(), (uint)_length /* in bytes */, (uint)seed, (uint)(seed >> 32));
-#else
- return Marvin.ComputeHash32(_bytes, seed);
-#endif
- }
-
- /// <summary>
- /// Returns a hash code using the specified <see cref="StringComparison"/>.
- /// </summary>
- public int GetHashCode(StringComparison comparison)
- {
- // TODO_UTF8STRING: This perf can be improved, including removing
- // the virtual dispatch by putting the switch directly in this method.
-
- return Utf8StringComparer.FromComparison(comparison).GetHashCode(this);
- }
-
- /// <summary>
- /// Returns <see langword="true"/> if this UTF-8 text consists of all-ASCII data,
- /// <see langword="false"/> if there is any non-ASCII data within this UTF-8 text.
- /// </summary>
- /// <remarks>
- /// ASCII text is defined as text consisting only of scalar values in the range [ U+0000..U+007F ].
- /// Empty strings are considered to be all-ASCII. The runtime of this method is O(n).
- /// </remarks>
- public bool IsAscii()
- {
- return this.AsSpan().IsAscii();
- }
-
- /// <summary>
- /// Returns <see langword="true"/> if <paramref name="value"/> is <see langword="null"/> or zero length;
- /// <see langword="false"/> otherwise.
- /// </summary>
- public static bool IsNullOrEmpty([NotNullWhen(false)] Utf8String? value)
- {
- // Copied from String.IsNullOrEmpty. See that method for detailed comments on why this pattern is used.
- return (value is null || 0u >= (uint)value.Length) ? true : false;
- }
-
- public static bool IsNullOrWhiteSpace([NotNullWhen(false)] Utf8String? value)
- {
- return (value is null) || value.AsSpan().IsEmptyOrWhiteSpace();
- }
-
- /// <summary>
- /// Returns the entire <see cref="Utf8String"/> as an array of UTF-8 bytes.
- /// </summary>
- public byte[] ToByteArray() => this.AsSpanSkipNullCheck().ToByteArray();
-
- /// <summary>
- /// Converts this <see cref="Utf8String"/> instance to a <see cref="string"/>.
- /// </summary>
- public override string ToString()
- {
- // TODO_UTF8STRING: Optimize the call below, potentially by avoiding the two-pass.
-
-#if (!NETSTANDARD2_0 && !NETFRAMEWORK)
- return Encoding.UTF8.GetString(this.AsBytesSkipNullCheck());
-#else
- if (Length == 0)
- {
- return string.Empty;
- }
-
- unsafe
- {
- fixed (byte* pBytes = this.AsBytesSkipNullCheck())
- {
- return Encoding.UTF8.GetString(pBytes, Length);
- }
- }
-#endif
- }
- }
-}
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-namespace System
-{
- // TODO_UTF8STRING: This should be removed and we should use regular StringSplitOptions
- // once a 'TrimEntries' flag gets added to the type.
-
- [Flags]
- public enum Utf8StringSplitOptions
- {
- None = 0,
- RemoveEmptyEntries = 1,
- TrimEntries = 2
- }
-}
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
- <Import Project="..\Directory.Build.props" />
- <PropertyGroup>
- <!-- System.Memory uses the Open key, so we will also. -->
- <StrongNameKeyId>Open</StrongNameKeyId>
- </PropertyGroup>
-</Project>
\ No newline at end of file
+++ /dev/null
-The `Utf8String` and `Char8` types are now available for experimentation. They currently exist in the package __System.Utf8String.Experimental__. Because this is an experimental package, it is unsupported for use in production workloads.
-
-To install:
-
-```ps
-install-package System.Utf8String.Experimental -prerelease -source https://dotnetfeed.blob.core.windows.net/dotnet-core/index.json
-```
-
-This package can only be installed into a project targeting a __nightly__ build of coreclr or libraries. Anything under the _master_ column of https://github.com/dotnet/core-sdk would work, as would any coreclr + libraries built from your own dev box (as long as you're building from _master_ instead of _release/..._). Installing this onto a project targeting an official Preview build would not work, as official Preview builds come from the _release_ branch.
-
-It's possible that installing the package might fail with an error similar to that seen below.
-
-```txt
-install-package : NU1605: Detected package downgrade: Microsoft.NETCore.Platforms from 3.0.0-preview6.19251.6 to 3.0.0-preview6.19223.2. Reference the package directly from the project to select a different version.
-```
-
-This can occur if the NuGet client attempts to install a newer version of the package than allowed by the .NET Runtime version your application is targeting. For now you can work around this error by specifying the explicit package version in the install command. Match the version passed to the NuGet client (shown below) to the version specified in the error message (shown above).
-
-```ps
-install-package System.Utf8String.Experimental -prerelease -source https://dotnetfeed.blob.core.windows.net/dotnet-core/index.json -version 3.0.0-preview6.19223.2
-```
-
-Not all of the APIs are hooked up yet, but we have some preliminary APIs that allow experimentation with the feature, including basic creation and inspection of `Utf8String` instances, wrapping a `ReadOnlySpan<byte>` or a `ReadOnlyMemory<byte>` around a `Utf8String` instance, and passing a `Utf8String` instance through `HttpClient`. Full list of APIs available at https://github.com/dotnet/runtime/blob/master/src/libraries/System.Utf8String.Experimental/ref/System.Utf8String.Experimental.cs.
-
-Certain language features also work as expected.
-
-```cs
-Utf8String s1 = new Utf8String(/* ... */);
-
-// range indexers work
-Utf8String s2 = s1[2..5];
-
-// as does pinning
-fixed (byte* pUtf8 = s1) { /* use 'pUtf8' here */ }
-
-// and allocating a GCHandle
-GCHandle handle = GCHandle.Alloc(s1, GCHandleType.Pinned);
-```
-
-For more information on the feature and API usage, see https://github.com/dotnet/corefxlab/issues/2350 and https://github.com/dotnet/runtime/issues/933.
+++ /dev/null
-Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 15
-VisualStudioVersion = 15.0.27213.1
-MinimumVisualStudioVersion = 10.0.40219.1
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Utf8String.Experimental.Tests", "tests\System.Utf8String.Experimental.Tests.csproj", "{72E9FB32-4692-4692-A10B-9F053F8F1A88}"
- ProjectSection(ProjectDependencies) = postProject
- {D4266847-6692-481B-9459-6141DB7DA339} = {D4266847-6692-481B-9459-6141DB7DA339}
- EndProjectSection
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Utf8String.Experimental", "src\System.Utf8String.Experimental.csproj", "{D4266847-6692-481B-9459-6141DB7DA339}"
- ProjectSection(ProjectDependencies) = postProject
- {7AF57E6B-2CED-45C9-8BCA-5BBA60D018E0} = {7AF57E6B-2CED-45C9-8BCA-5BBA60D018E0}
- EndProjectSection
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Utf8String.Experimental", "ref\System.Utf8String.Experimental.csproj", "{7AF57E6B-2CED-45C9-8BCA-5BBA60D018E0}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{1A2F9F4A-A032-433E-B914-ADD5992BB178}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{E107E9C1-E893-4E87-987E-04EF0DCEAEFD}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ref", "ref", "{2E666815-2EDB-464B-9DF6-380BF4789AD4}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestUtilities", "..\Common\tests\TestUtilities\TestUtilities.csproj", "{911886E1-C0A6-4830-AAAE-BF320F644704}"
-EndProject
-Global
- GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug|Any CPU = Debug|Any CPU
- Release|Any CPU = Release|Any CPU
- EndGlobalSection
- GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {72E9FB32-4692-4692-A10B-9F053F8F1A88}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {72E9FB32-4692-4692-A10B-9F053F8F1A88}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {72E9FB32-4692-4692-A10B-9F053F8F1A88}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {72E9FB32-4692-4692-A10B-9F053F8F1A88}.Release|Any CPU.Build.0 = Release|Any CPU
- {D4266847-6692-481B-9459-6141DB7DA339}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {D4266847-6692-481B-9459-6141DB7DA339}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {D4266847-6692-481B-9459-6141DB7DA339}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {D4266847-6692-481B-9459-6141DB7DA339}.Release|Any CPU.Build.0 = Release|Any CPU
- {7AF57E6B-2CED-45C9-8BCA-5BBA60D018E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {7AF57E6B-2CED-45C9-8BCA-5BBA60D018E0}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {7AF57E6B-2CED-45C9-8BCA-5BBA60D018E0}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {7AF57E6B-2CED-45C9-8BCA-5BBA60D018E0}.Release|Any CPU.Build.0 = Release|Any CPU
- {911886E1-C0A6-4830-AAAE-BF320F644704}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {911886E1-C0A6-4830-AAAE-BF320F644704}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {911886E1-C0A6-4830-AAAE-BF320F644704}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {911886E1-C0A6-4830-AAAE-BF320F644704}.Release|Any CPU.Build.0 = Release|Any CPU
- EndGlobalSection
- GlobalSection(SolutionProperties) = preSolution
- HideSolutionNode = FALSE
- EndGlobalSection
- GlobalSection(NestedProjects) = preSolution
- {72E9FB32-4692-4692-A10B-9F053F8F1A88} = {1A2F9F4A-A032-433E-B914-ADD5992BB178}
- {D4266847-6692-481B-9459-6141DB7DA339} = {E107E9C1-E893-4E87-987E-04EF0DCEAEFD}
- {7AF57E6B-2CED-45C9-8BCA-5BBA60D018E0} = {2E666815-2EDB-464B-9DF6-380BF4789AD4}
- {911886E1-C0A6-4830-AAAE-BF320F644704} = {1A2F9F4A-A032-433E-B914-ADD5992BB178}
- EndGlobalSection
- GlobalSection(ExtensibilityGlobals) = postSolution
- SolutionGuid = {7196F6AB-8F22-4E4D-B6D1-3C2CFF86229C}
- EndGlobalSection
-EndGlobal
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
- <Import Project="$([MSBuild]::GetPathOfFileAbove(Directory.Build.props))" />
- <ItemGroup>
- <ProjectReference Include="..\src\System.Utf8String.Experimental.csproj">
- <SupportedFramework>net461;netcoreapp2.0;uap10.0.16299;$(AllXamarinFrameworks)</SupportedFramework>
- </ProjectReference>
- </ItemGroup>
- <Import Project="$([MSBuild]::GetPathOfFileAbove(Directory.Build.targets))" />
-</Project>
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-#if !NETSTANDARD2_0 && !NETFRAMEWORK
-[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Index))]
-[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Range))]
-
-#if !NETSTANDARD2_1
-[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Text.Rune))]
-#endif // !NETSTANDARD2_1
-
-#endif // !NETSTANDARD2_0
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// ------------------------------------------------------------------------------
-// Changes to this file must follow the https://aka.ms/api-review process.
-// ------------------------------------------------------------------------------
-
-namespace System
-{
- public readonly partial struct Index : System.IEquatable<System.Index>
- {
- private readonly int _dummyPrimitive;
- public Index(int value, bool fromEnd = false) { throw null; }
- public static System.Index End { get { throw null; } }
- public bool IsFromEnd { get { throw null; } }
- public static System.Index Start { get { throw null; } }
- public int Value { get { throw null; } }
- public bool Equals(System.Index other) { throw null; }
- public override bool Equals(object? value) { throw null; }
- public static System.Index FromEnd(int value) { throw null; }
- public static System.Index FromStart(int value) { throw null; }
- public override int GetHashCode() { throw null; }
- public int GetOffset(int length) { throw null; }
- public static implicit operator System.Index(int value) { throw null; }
- public override string ToString() { throw null; }
- }
- public readonly partial struct Range : System.IEquatable<System.Range>
- {
- private readonly int _dummyPrimitive;
- public Range(System.Index start, System.Index end) { throw null; }
- public static System.Range All { get { throw null; } }
- public System.Index End { get { throw null; } }
- public System.Index Start { get { throw null; } }
- public static System.Range EndAt(System.Index end) { throw null; }
- public override bool Equals(object? value) { throw null; }
- public bool Equals(System.Range other) { throw null; }
- public override int GetHashCode() { throw null; }
- public (int Offset, int Length) GetOffsetAndLength(int length) { throw null; }
- public static System.Range StartAt(System.Index start) { throw null; }
- public override string ToString() { throw null; }
- }
-}
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// ------------------------------------------------------------------------------
-// Changes to this file must follow the https://aka.ms/api-review process.
-// ------------------------------------------------------------------------------
-
-namespace System.Text
-{
- public readonly partial struct Rune : System.IComparable, System.IComparable<System.Text.Rune>, System.IEquatable<System.Text.Rune>
- {
- private readonly int _dummyPrimitive;
- public Rune(char ch) { throw null; }
- public Rune(char highSurrogate, char lowSurrogate) { throw null; }
- public Rune(int value) { throw null; }
- [System.CLSCompliantAttribute(false)]
- public Rune(uint value) { throw null; }
- public bool IsAscii { get { throw null; } }
- public bool IsBmp { get { throw null; } }
- public int Plane { get { throw null; } }
- public static System.Text.Rune ReplacementChar { get { throw null; } }
- public int Utf16SequenceLength { get { throw null; } }
- public int Utf8SequenceLength { get { throw null; } }
- public int Value { get { throw null; } }
- public int CompareTo(System.Text.Rune other) { throw null; }
- public static System.Buffers.OperationStatus DecodeFromUtf16(System.ReadOnlySpan<char> source, out System.Text.Rune result, out int charsConsumed) { throw null; }
- public static System.Buffers.OperationStatus DecodeFromUtf8(System.ReadOnlySpan<byte> source, out System.Text.Rune result, out int bytesConsumed) { throw null; }
- public static System.Buffers.OperationStatus DecodeLastFromUtf16(System.ReadOnlySpan<char> source, out System.Text.Rune result, out int charsConsumed) { throw null; }
- public static System.Buffers.OperationStatus DecodeLastFromUtf8(System.ReadOnlySpan<byte> source, out System.Text.Rune value, out int bytesConsumed) { throw null; }
- public int EncodeToUtf16(System.Span<char> destination) { throw null; }
- public int EncodeToUtf8(System.Span<byte> destination) { throw null; }
- public override bool Equals(object? obj) { throw null; }
- public bool Equals(System.Text.Rune other) { throw null; }
- public override int GetHashCode() { throw null; }
- public static double GetNumericValue(System.Text.Rune value) { throw null; }
- public static System.Text.Rune GetRuneAt(string input, int index) { throw null; }
- public static System.Globalization.UnicodeCategory GetUnicodeCategory(System.Text.Rune value) { throw null; }
- public static bool IsControl(System.Text.Rune value) { throw null; }
- public static bool IsDigit(System.Text.Rune value) { throw null; }
- public static bool IsLetter(System.Text.Rune value) { throw null; }
- public static bool IsLetterOrDigit(System.Text.Rune value) { throw null; }
- public static bool IsLower(System.Text.Rune value) { throw null; }
- public static bool IsNumber(System.Text.Rune value) { throw null; }
- public static bool IsPunctuation(System.Text.Rune value) { throw null; }
- public static bool IsSeparator(System.Text.Rune value) { throw null; }
- public static bool IsSymbol(System.Text.Rune value) { throw null; }
- public static bool IsUpper(System.Text.Rune value) { throw null; }
- public static bool IsValid(int value) { throw null; }
- [System.CLSCompliantAttribute(false)]
- public static bool IsValid(uint value) { throw null; }
- public static bool IsWhiteSpace(System.Text.Rune value) { throw null; }
- public static bool operator ==(System.Text.Rune left, System.Text.Rune right) { throw null; }
- public static explicit operator System.Text.Rune(char ch) { throw null; }
- public static explicit operator System.Text.Rune(int value) { throw null; }
- [System.CLSCompliantAttribute(false)]
- public static explicit operator System.Text.Rune(uint value) { throw null; }
- public static bool operator >(System.Text.Rune left, System.Text.Rune right) { throw null; }
- public static bool operator >=(System.Text.Rune left, System.Text.Rune right) { throw null; }
- public static bool operator !=(System.Text.Rune left, System.Text.Rune right) { throw null; }
- public static bool operator <(System.Text.Rune left, System.Text.Rune right) { throw null; }
- public static bool operator <=(System.Text.Rune left, System.Text.Rune right) { throw null; }
- public static System.Text.Rune ToLower(System.Text.Rune value, System.Globalization.CultureInfo culture) { throw null; }
- public static System.Text.Rune ToLowerInvariant(System.Text.Rune value) { throw null; }
- public override string ToString() { throw null; }
- public static System.Text.Rune ToUpper(System.Text.Rune value, System.Globalization.CultureInfo culture) { throw null; }
- public static System.Text.Rune ToUpperInvariant(System.Text.Rune value) { throw null; }
- public static bool TryCreate(char highSurrogate, char lowSurrogate, out System.Text.Rune result) { throw null; }
- public static bool TryCreate(char ch, out System.Text.Rune result) { throw null; }
- public static bool TryCreate(int value, out System.Text.Rune result) { throw null; }
- [System.CLSCompliantAttribute(false)]
- public static bool TryCreate(uint value, out System.Text.Rune result) { throw null; }
- public bool TryEncodeToUtf16(System.Span<char> destination, out int charsWritten) { throw null; }
- public bool TryEncodeToUtf8(System.Span<byte> destination, out int bytesWritten) { throw null; }
- public static bool TryGetRuneAt(string input, int index, out System.Text.Rune value) { throw null; }
- int IComparable.CompareTo(object? obj) { throw null; }
- }
-}
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// ------------------------------------------------------------------------------
-// Changes to this file must follow the https://aka.ms/api-review process.
-// ------------------------------------------------------------------------------
-
-namespace System
-{
- public readonly partial struct Char8 : System.IComparable<System.Char8>, System.IEquatable<System.Char8>
- {
- private readonly int _dummyPrimitive;
- public int CompareTo(System.Char8 other) { throw null; }
- public bool Equals(System.Char8 other) { throw null; }
- public override bool Equals(object? obj) { throw null; }
- public override int GetHashCode() { throw null; }
- public static bool operator ==(System.Char8 left, System.Char8 right) { throw null; }
- public static explicit operator System.Char8(char value) { throw null; }
- public static explicit operator char(System.Char8 value) { throw null; }
- [System.CLSCompliantAttribute(false)]
- public static explicit operator sbyte(System.Char8 value) { throw null; }
- public static explicit operator System.Char8(short value) { throw null; }
- public static explicit operator System.Char8(int value) { throw null; }
- public static explicit operator System.Char8(long value) { throw null; }
- [System.CLSCompliantAttribute(false)]
- public static explicit operator System.Char8(sbyte value) { throw null; }
- [System.CLSCompliantAttribute(false)]
- public static explicit operator System.Char8(ushort value) { throw null; }
- [System.CLSCompliantAttribute(false)]
- public static explicit operator System.Char8(uint value) { throw null; }
- [System.CLSCompliantAttribute(false)]
- public static explicit operator System.Char8(ulong value) { throw null; }
- public static bool operator >(System.Char8 left, System.Char8 right) { throw null; }
- public static bool operator >=(System.Char8 left, System.Char8 right) { throw null; }
- public static implicit operator System.Char8(byte value) { throw null; }
- public static implicit operator byte(System.Char8 value) { throw null; }
- public static implicit operator short(System.Char8 value) { throw null; }
- public static implicit operator int(System.Char8 value) { throw null; }
- public static implicit operator long(System.Char8 value) { throw null; }
- [System.CLSCompliantAttribute(false)]
- public static implicit operator ushort(System.Char8 value) { throw null; }
- [System.CLSCompliantAttribute(false)]
- public static implicit operator uint(System.Char8 value) { throw null; }
- [System.CLSCompliantAttribute(false)]
- public static implicit operator ulong(System.Char8 value) { throw null; }
- public static bool operator !=(System.Char8 left, System.Char8 right) { throw null; }
- public static bool operator <(System.Char8 left, System.Char8 right) { throw null; }
- public static bool operator <=(System.Char8 left, System.Char8 right) { throw null; }
- public override string ToString() { throw null; }
- }
- public static partial class Utf8Extensions
- {
- public static System.ReadOnlySpan<byte> AsBytes(this System.ReadOnlySpan<System.Char8> text) { throw null; }
- public static System.ReadOnlySpan<byte> AsBytes(this System.Utf8String? text) { throw null; }
- public static System.ReadOnlySpan<byte> AsBytes(this System.Utf8String? text, int start) { throw null; }
- public static System.ReadOnlySpan<byte> AsBytes(this System.Utf8String? text, int start, int length) { throw null; }
- public static System.ReadOnlyMemory<byte> AsMemoryBytes(this System.Utf8String? text) { throw null; }
- public static System.ReadOnlyMemory<byte> AsMemoryBytes(this System.Utf8String? text, System.Index startIndex) { throw null; }
- public static System.ReadOnlyMemory<byte> AsMemoryBytes(this System.Utf8String? text, int start) { throw null; }
- public static System.ReadOnlyMemory<byte> AsMemoryBytes(this System.Utf8String? text, int start, int length) { throw null; }
- public static System.ReadOnlyMemory<byte> AsMemoryBytes(this System.Utf8String? text, System.Range range) { throw null; }
- public static System.Text.Utf8Span AsSpan(this System.Utf8String? text) { throw null; }
- public static System.Text.Utf8Span AsSpan(this System.Utf8String? text, int start) { throw null; }
- public static System.Text.Utf8Span AsSpan(this System.Utf8String? text, int start, int length) { throw null; }
- public static System.Utf8String ToUtf8String(this System.Text.Rune rune) { throw null; }
- }
- public sealed partial class Utf8String : System.IComparable<System.Utf8String?>, System.IEquatable<System.Utf8String?>
- {
- public static readonly System.Utf8String Empty;
- [System.CLSCompliantAttribute(false)]
- public unsafe Utf8String(byte* value) { }
- public Utf8String(byte[] value, int startIndex, int length) { }
- [System.CLSCompliantAttribute(false)]
- public unsafe Utf8String(char* value) { }
- public Utf8String(char[] value, int startIndex, int length) { }
- public Utf8String(System.ReadOnlySpan<byte> value) { }
- public Utf8String(System.ReadOnlySpan<char> value) { }
- public Utf8String(string value) { }
- public ByteEnumerable Bytes { get { throw null; } }
- public CharEnumerable Chars { get { throw null; } }
- public int Length { get { throw null; } }
- public RuneEnumerable Runes { get { throw null; } }
- public static bool AreEquivalent(System.Utf8String? utf8Text, string? utf16Text) { throw null; }
- public static bool AreEquivalent(System.Text.Utf8Span utf8Text, System.ReadOnlySpan<char> utf16Text) { throw null; }
- public static bool AreEquivalent(System.ReadOnlySpan<byte> utf8Text, System.ReadOnlySpan<char> utf16Text) { throw null; }
- public int CompareTo(System.Utf8String? other) { throw null; }
- public int CompareTo(System.Utf8String? other, System.StringComparison comparison) { throw null; }
- public bool Contains(char value) { throw null; }
- public bool Contains(char value, System.StringComparison comparison) { throw null; }
- public bool Contains(System.Text.Rune value) { throw null; }
- public bool Contains(System.Text.Rune value, System.StringComparison comparison) { throw null; }
- public bool Contains(System.Utf8String value) { throw null; }
- public bool Contains(System.Utf8String value, System.StringComparison comparison) { throw null; }
-#if !NETSTANDARD2_0 && !NETFRAMEWORK
- public static System.Utf8String Create<TState>(int length, TState state, System.Buffers.SpanAction<byte, TState> action) { throw null; }
-#endif
- public static System.Utf8String CreateFromRelaxed(System.ReadOnlySpan<byte> buffer) { throw null; }
- public static System.Utf8String CreateFromRelaxed(System.ReadOnlySpan<char> buffer) { throw null; }
-#if !NETSTANDARD2_0 && !NETFRAMEWORK
- public static System.Utf8String CreateRelaxed<TState>(int length, TState state, System.Buffers.SpanAction<byte, TState> action) { throw null; }
-#endif
- public bool EndsWith(char value) { throw null; }
- public bool EndsWith(char value, System.StringComparison comparison) { throw null; }
- public bool EndsWith(System.Text.Rune value) { throw null; }
- public bool EndsWith(System.Text.Rune value, System.StringComparison comparison) { throw null; }
- public bool EndsWith(System.Utf8String value) { throw null; }
- public bool EndsWith(System.Utf8String value, System.StringComparison comparison) { throw null; }
- public override bool Equals(object? obj) { throw null; }
- public static bool Equals(System.Utf8String? a, System.Utf8String? b, System.StringComparison comparison) { throw null; }
- public static bool Equals(System.Utf8String? left, System.Utf8String? right) { throw null; }
- public bool Equals(System.Utf8String? value) { throw null; }
- public bool Equals(System.Utf8String? value, System.StringComparison comparison) { throw null; }
- public override int GetHashCode() { throw null; }
- public int GetHashCode(System.StringComparison comparison) { throw null; }
- [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
- public ref readonly byte GetPinnableReference() { throw null; }
- public static implicit operator System.Text.Utf8Span(System.Utf8String? value) { throw null; }
- public bool IsAscii() { throw null; }
- public bool IsNormalized(System.Text.NormalizationForm normalizationForm = System.Text.NormalizationForm.FormC) { throw null; }
- public static bool IsNullOrEmpty([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(false)] System.Utf8String? value) { throw null; }
- public static bool IsNullOrWhiteSpace([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(false)] System.Utf8String? value) { throw null; }
- public System.Utf8String Normalize(System.Text.NormalizationForm normalizationForm = System.Text.NormalizationForm.FormC) { throw null; }
- public static bool operator !=(System.Utf8String? left, System.Utf8String? right) { throw null; }
- public static bool operator ==(System.Utf8String? left, System.Utf8String? right) { throw null; }
- public SplitResult Split(char separator, System.Utf8StringSplitOptions options = System.Utf8StringSplitOptions.None) { throw null; }
- public SplitResult Split(System.Text.Rune separator, System.Utf8StringSplitOptions options = System.Utf8StringSplitOptions.None) { throw null; }
- public SplitResult Split(System.Utf8String separator, System.Utf8StringSplitOptions options = System.Utf8StringSplitOptions.None) { throw null; }
- public SplitOnResult SplitOn(char separator) { throw null; }
- public SplitOnResult SplitOn(char separator, System.StringComparison comparisonType) { throw null; }
- public SplitOnResult SplitOn(System.Text.Rune separator) { throw null; }
- public SplitOnResult SplitOn(System.Text.Rune separator, System.StringComparison comparisonType) { throw null; }
- public SplitOnResult SplitOn(System.Utf8String separator) { throw null; }
- public SplitOnResult SplitOn(System.Utf8String separator, System.StringComparison comparisonType) { throw null; }
- public SplitOnResult SplitOnLast(char separator) { throw null; }
- public SplitOnResult SplitOnLast(char separator, System.StringComparison comparisonType) { throw null; }
- public SplitOnResult SplitOnLast(System.Text.Rune separator) { throw null; }
- public SplitOnResult SplitOnLast(System.Text.Rune separator, System.StringComparison comparisonType) { throw null; }
- public SplitOnResult SplitOnLast(System.Utf8String separator) { throw null; }
- public SplitOnResult SplitOnLast(System.Utf8String separator, System.StringComparison comparisonType) { throw null; }
- public bool StartsWith(char value) { throw null; }
- public bool StartsWith(char value, System.StringComparison comparison) { throw null; }
- public bool StartsWith(System.Text.Rune value) { throw null; }
- public bool StartsWith(System.Text.Rune value, System.StringComparison comparison) { throw null; }
- public bool StartsWith(System.Utf8String value) { throw null; }
- public bool StartsWith(System.Utf8String value, System.StringComparison comparison) { throw null; }
- public System.Utf8String this[System.Range range] { get { throw null; } }
- public byte[] ToByteArray() { throw null; }
- public char[] ToCharArray() { throw null; }
- public System.Utf8String ToLower(System.Globalization.CultureInfo culture) { throw null; }
- public System.Utf8String ToLowerInvariant() { throw null; }
- public override string ToString() { throw null; }
- public System.Utf8String ToUpper(System.Globalization.CultureInfo culture) { throw null; }
- public System.Utf8String ToUpperInvariant() { throw null; }
- public System.Utf8String Trim() { throw null; }
- public System.Utf8String TrimEnd() { throw null; }
- public System.Utf8String TrimStart() { throw null; }
- public static bool TryCreateFrom(System.ReadOnlySpan<byte> buffer, [System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] out System.Utf8String? value) { throw null; }
- public static bool TryCreateFrom(System.ReadOnlySpan<char> buffer, [System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] out System.Utf8String? value) { throw null; }
- public bool TryFind(char value, out System.Range range) { throw null; }
- public bool TryFind(char value, System.StringComparison comparisonType, out System.Range range) { throw null; }
- public bool TryFind(System.Text.Rune value, out System.Range range) { throw null; }
- public bool TryFind(System.Text.Rune value, System.StringComparison comparisonType, out System.Range range) { throw null; }
- public bool TryFind(System.Utf8String value, out System.Range range) { throw null; }
- public bool TryFind(System.Utf8String value, System.StringComparison comparisonType, out System.Range range) { throw null; }
- public bool TryFindLast(char value, out System.Range range) { throw null; }
- public bool TryFindLast(char value, System.StringComparison comparisonType, out System.Range range) { throw null; }
- public bool TryFindLast(System.Text.Rune value, out System.Range range) { throw null; }
- public bool TryFindLast(System.Text.Rune value, System.StringComparison comparisonType, out System.Range range) { throw null; }
- public bool TryFindLast(System.Utf8String value, out System.Range range) { throw null; }
- public bool TryFindLast(System.Utf8String value, System.StringComparison comparisonType, out System.Range range) { throw null; }
- public static System.Utf8String UnsafeCreateWithoutValidation(System.ReadOnlySpan<byte> utf8Contents) { throw null; }
-#if !NETSTANDARD2_0 && !NETFRAMEWORK
- public static System.Utf8String UnsafeCreateWithoutValidation<TState>(int length, TState state, System.Buffers.SpanAction<byte, TState> action) { throw null; }
-#endif
- public readonly partial struct ByteEnumerable : System.Collections.Generic.IEnumerable<byte>
- {
- private readonly object _dummy;
- public Enumerator GetEnumerator() { throw null; }
- System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; }
- System.Collections.Generic.IEnumerator<byte> System.Collections.Generic.IEnumerable<byte>.GetEnumerator() { throw null; }
- public struct Enumerator : System.Collections.Generic.IEnumerator<byte>
- {
- private readonly object _dummy;
- private readonly int _dummyPrimitive;
- public readonly byte Current { get { throw null; } }
- public bool MoveNext() { throw null; }
- void System.IDisposable.Dispose() { }
- object System.Collections.IEnumerator.Current { get { throw null; } }
- void System.Collections.IEnumerator.Reset() { }
- }
- }
- public readonly partial struct CharEnumerable : System.Collections.Generic.IEnumerable<char>
- {
- private readonly object _dummy;
- public Enumerator GetEnumerator() { throw null; }
- System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; }
- System.Collections.Generic.IEnumerator<char> System.Collections.Generic.IEnumerable<char>.GetEnumerator() { throw null; }
- public struct Enumerator : System.Collections.Generic.IEnumerator<char>
- {
- private readonly object _dummy;
- private readonly int _dummyPrimitive;
- public char Current { get { throw null; } }
- public bool MoveNext() { throw null; }
- void System.IDisposable.Dispose() { }
- object System.Collections.IEnumerator.Current { get { throw null; } }
- void System.Collections.IEnumerator.Reset() { }
- }
- }
- public readonly partial struct RuneEnumerable : System.Collections.Generic.IEnumerable<System.Text.Rune>
- {
- private readonly object _dummy;
- public Enumerator GetEnumerator() { throw null; }
- System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; }
- System.Collections.Generic.IEnumerator<System.Text.Rune> System.Collections.Generic.IEnumerable<System.Text.Rune>.GetEnumerator() { throw null; }
- public struct Enumerator : System.Collections.Generic.IEnumerator<System.Text.Rune>
- {
- private readonly object _dummy;
- private readonly int _dummyPrimitive;
- public System.Text.Rune Current { get { throw null; } }
- public bool MoveNext() { throw null; }
- void System.IDisposable.Dispose() { }
- object System.Collections.IEnumerator.Current { get { throw null; } }
- void System.Collections.IEnumerator.Reset() { }
- }
- }
- public readonly struct SplitResult : System.Collections.Generic.IEnumerable<Utf8String?>
- {
- private readonly object _dummy;
- [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
- public void Deconstruct(out System.Utf8String? item1, out System.Utf8String? item2) { throw null; }
- [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
- public void Deconstruct(out System.Utf8String? item1, out System.Utf8String? item2, out System.Utf8String? item3) { throw null; }
- [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
- public void Deconstruct(out System.Utf8String? item1, out System.Utf8String? item2, out System.Utf8String? item3, out System.Utf8String? item4) { throw null; }
- [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
- public void Deconstruct(out System.Utf8String? item1, out System.Utf8String? item2, out System.Utf8String? item3, out System.Utf8String? item4, out System.Utf8String? item5) { throw null; }
- [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
- public void Deconstruct(out System.Utf8String? item1, out System.Utf8String? item2, out System.Utf8String? item3, out System.Utf8String? item4, out System.Utf8String? item5, out System.Utf8String? item6) { throw null; }
- [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
- public void Deconstruct(out System.Utf8String? item1, out System.Utf8String? item2, out System.Utf8String? item3, out System.Utf8String? item4, out System.Utf8String? item5, out System.Utf8String? item6, out System.Utf8String? item7) { throw null; }
- [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
- public void Deconstruct(out System.Utf8String? item1, out System.Utf8String? item2, out System.Utf8String? item3, out System.Utf8String? item4, out System.Utf8String? item5, out System.Utf8String? item6, out System.Utf8String? item7, out System.Utf8String? item8) { throw null; }
- public Enumerator GetEnumerator() { throw null; }
- System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; }
- System.Collections.Generic.IEnumerator<System.Utf8String?> System.Collections.Generic.IEnumerable<System.Utf8String?>.GetEnumerator() { throw null; }
- public struct Enumerator : System.Collections.Generic.IEnumerator<System.Utf8String?>
- {
- private readonly object _dummy;
- public System.Utf8String? Current { get { throw null; } }
- public bool MoveNext() { throw null; }
- void System.IDisposable.Dispose() { }
- object? System.Collections.IEnumerator.Current { get { throw null; } }
- void System.Collections.IEnumerator.Reset() { throw null; }
- }
- }
- public readonly struct SplitOnResult
- {
- private readonly object _dummy;
- public System.Utf8String? After { get { throw null; } }
- public System.Utf8String Before { get { throw null; } }
- [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
- public void Deconstruct(out System.Utf8String before, out System.Utf8String? after) { throw null; }
- }
- }
- [System.FlagsAttribute]
- public enum Utf8StringSplitOptions
- {
- None = 0,
- RemoveEmptyEntries = 1,
- TrimEntries = 2
- }
-}
-namespace System.Net.Http
-{
- public sealed partial class Utf8StringContent : System.Net.Http.HttpContent
- {
- public Utf8StringContent(System.Utf8String content) { }
- public Utf8StringContent(System.Utf8String content, string? mediaType) { }
- protected override System.Threading.Tasks.Task<System.IO.Stream> CreateContentReadStreamAsync() { throw null; }
- protected override System.Threading.Tasks.Task SerializeToStreamAsync(System.IO.Stream stream, System.Net.TransportContext? context) { throw null; }
- protected override bool TryComputeLength(out long length) { throw null; }
- }
-}
-namespace System.Text
-{
- public readonly ref partial struct Utf8Span
- {
- private readonly object _dummy;
- private readonly int _dummyPrimitive;
- public Utf8Span(System.Utf8String? value) { throw null; }
- public System.ReadOnlySpan<byte> Bytes { get { throw null; } }
- public CharEnumerable Chars { get { throw null; } }
- public static System.Text.Utf8Span Empty { get { throw null; } }
- public bool IsEmpty { get { throw null; } }
- public int Length { get { throw null; } }
- public RuneEnumerable Runes { get { throw null; } }
- public int CompareTo(System.Text.Utf8Span other) { throw null; }
- public int CompareTo(System.Text.Utf8Span other, System.StringComparison comparison) { throw null; }
- public bool Contains(char value) { throw null; }
- public bool Contains(char value, System.StringComparison comparison) { throw null; }
- public bool Contains(System.Text.Rune value) { throw null; }
- public bool Contains(System.Text.Rune value, System.StringComparison comparison) { throw null; }
- public bool Contains(System.Text.Utf8Span value) { throw null; }
- public bool Contains(System.Text.Utf8Span value, System.StringComparison comparison) { throw null; }
- public bool EndsWith(char value) { throw null; }
- public bool EndsWith(char value, System.StringComparison comparison) { throw null; }
- public bool EndsWith(System.Text.Rune value) { throw null; }
- public bool EndsWith(System.Text.Rune value, System.StringComparison comparison) { throw null; }
- public bool EndsWith(System.Text.Utf8Span value) { throw null; }
- public bool EndsWith(System.Text.Utf8Span value, System.StringComparison comparison) { throw null; }
- [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
- [System.ObsoleteAttribute("Equals(object) on Utf8Span will always throw an exception. Use Equals(Utf8Span) or == instead.")]
- public override bool Equals(object? obj) { throw null; }
- public bool Equals(System.Text.Utf8Span other) { throw null; }
- public bool Equals(System.Text.Utf8Span other, System.StringComparison comparison) { throw null; }
- public static bool Equals(System.Text.Utf8Span left, System.Text.Utf8Span right) { throw null; }
- public static bool Equals(System.Text.Utf8Span left, System.Text.Utf8Span right, System.StringComparison comparison) { throw null; }
- public override int GetHashCode() { throw null; }
- public int GetHashCode(System.StringComparison comparison) { throw null; }
- [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
- public ref readonly byte GetPinnableReference() { throw null; }
- public bool IsAscii() { throw null; }
- public bool IsEmptyOrWhiteSpace() { throw null; }
- public bool IsNormalized(System.Text.NormalizationForm normalizationForm = System.Text.NormalizationForm.FormC) { throw null; }
- public System.Utf8String Normalize(System.Text.NormalizationForm normalizationForm = System.Text.NormalizationForm.FormC) { throw null; }
- public int Normalize(System.Span<byte> destination, System.Text.NormalizationForm normalizationForm = System.Text.NormalizationForm.FormC) { throw null; }
- public static bool operator !=(System.Text.Utf8Span left, System.Text.Utf8Span right) { throw null; }
- public static bool operator ==(System.Text.Utf8Span left, System.Text.Utf8Span right) { throw null; }
- public System.Text.Utf8Span this[System.Range range] { get { throw null; } }
- public SplitResult Split(char separator, System.Utf8StringSplitOptions options = System.Utf8StringSplitOptions.None) { throw null; }
- public SplitResult Split(System.Text.Rune separator, System.Utf8StringSplitOptions options = System.Utf8StringSplitOptions.None) { throw null; }
- public SplitResult Split(System.Text.Utf8Span separator, System.Utf8StringSplitOptions options = System.Utf8StringSplitOptions.None) { throw null; }
- public SplitOnResult SplitOn(char separator) { throw null; }
- public SplitOnResult SplitOn(char separator, System.StringComparison comparisonType) { throw null; }
- public SplitOnResult SplitOn(System.Text.Rune separator) { throw null; }
- public SplitOnResult SplitOn(System.Text.Rune separator, System.StringComparison comparisonType) { throw null; }
- public SplitOnResult SplitOn(System.Text.Utf8Span separator) { throw null; }
- public SplitOnResult SplitOn(System.Text.Utf8Span separator, System.StringComparison comparisonType) { throw null; }
- public SplitOnResult SplitOnLast(char separator) { throw null; }
- public SplitOnResult SplitOnLast(char separator, System.StringComparison comparisonType) { throw null; }
- public SplitOnResult SplitOnLast(System.Text.Rune separator) { throw null; }
- public SplitOnResult SplitOnLast(System.Text.Rune separator, System.StringComparison comparisonType) { throw null; }
- public SplitOnResult SplitOnLast(System.Text.Utf8Span separator) { throw null; }
- public SplitOnResult SplitOnLast(System.Text.Utf8Span separator, System.StringComparison comparisonType) { throw null; }
- public bool StartsWith(char value) { throw null; }
- public bool StartsWith(char value, System.StringComparison comparison) { throw null; }
- public bool StartsWith(System.Text.Rune value) { throw null; }
- public bool StartsWith(System.Text.Rune value, System.StringComparison comparison) { throw null; }
- public bool StartsWith(System.Text.Utf8Span value) { throw null; }
- public bool StartsWith(System.Text.Utf8Span value, System.StringComparison comparison) { throw null; }
- public System.Text.Utf8Span Trim() { throw null; }
- public System.Text.Utf8Span TrimEnd() { throw null; }
- public System.Text.Utf8Span TrimStart() { throw null; }
- public byte[] ToByteArray() { throw null; }
- public char[] ToCharArray() { throw null; }
- public int ToChars(System.Span<char> destination) { throw null; }
- public System.Utf8String ToLower(System.Globalization.CultureInfo culture) { throw null; }
- public int ToLower(System.Span<byte> destination, System.Globalization.CultureInfo culture) { throw null; }
- public System.Utf8String ToLowerInvariant() { throw null; }
- public int ToLowerInvariant(System.Span<byte> destination) { throw null; }
- public override string ToString() { throw null; }
- public System.Utf8String ToUpper(System.Globalization.CultureInfo culture) { throw null; }
- public int ToUpper(System.Span<byte> destination, System.Globalization.CultureInfo culture) { throw null; }
- public System.Utf8String ToUpperInvariant() { throw null; }
- public int ToUpperInvariant(System.Span<byte> destination) { throw null; }
- public System.Utf8String ToUtf8String() { throw null; }
- public bool TryFind(char value, out System.Range range) { throw null; }
- public bool TryFind(char value, System.StringComparison comparisonType, out System.Range range) { throw null; }
- public bool TryFind(System.Text.Rune value, out System.Range range) { throw null; }
- public bool TryFind(System.Text.Rune value, System.StringComparison comparisonType, out System.Range range) { throw null; }
- public bool TryFind(System.Text.Utf8Span value, out System.Range range) { throw null; }
- public bool TryFind(System.Text.Utf8Span value, System.StringComparison comparisonType, out System.Range range) { throw null; }
- public bool TryFindLast(char value, out System.Range range) { throw null; }
- public bool TryFindLast(char value, System.StringComparison comparisonType, out System.Range range) { throw null; }
- public bool TryFindLast(System.Text.Rune value, out System.Range range) { throw null; }
- public bool TryFindLast(System.Text.Rune value, System.StringComparison comparisonType, out System.Range range) { throw null; }
- public bool TryFindLast(System.Text.Utf8Span value, out System.Range range) { throw null; }
- public bool TryFindLast(System.Text.Utf8Span value, System.StringComparison comparisonType, out System.Range range) { throw null; }
- public static System.Text.Utf8Span UnsafeCreateWithoutValidation(System.ReadOnlySpan<byte> buffer) { throw null; }
- public readonly ref struct CharEnumerable
- {
- private readonly object _dummy;
- private readonly int _dummyPrimitive;
- public Enumerator GetEnumerator() { throw null; }
- public ref struct Enumerator
- {
- private object _dummy;
- private int _dummyPrimitive;
- public char Current { get { throw null; } }
- public bool MoveNext() { throw null; }
- }
- }
- public readonly ref struct RuneEnumerable
- {
- private readonly object _dummy;
- private readonly int _dummyPrimitive;
- public Enumerator GetEnumerator() { throw null; }
- public ref struct Enumerator
- {
- private object _dummy;
- private int _dummyPrimitive;
- public System.Text.Rune Current { get { throw null; } }
- public bool MoveNext() { throw null; }
- }
- }
- public readonly ref struct SplitResult
- {
- private readonly object _dummy;
- private readonly int _dummyPrimitive;
- [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
- public void Deconstruct(out System.Text.Utf8Span item1, out System.Text.Utf8Span item2) { throw null; }
- [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
- public void Deconstruct(out System.Text.Utf8Span item1, out System.Text.Utf8Span item2, out System.Text.Utf8Span item3) { throw null; }
- [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
- public void Deconstruct(out System.Text.Utf8Span item1, out System.Text.Utf8Span item2, out System.Text.Utf8Span item3, out System.Text.Utf8Span item4) { throw null; }
- [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
- public void Deconstruct(out System.Text.Utf8Span item1, out System.Text.Utf8Span item2, out System.Text.Utf8Span item3, out System.Text.Utf8Span item4, out System.Text.Utf8Span item5) { throw null; }
- [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
- public void Deconstruct(out System.Text.Utf8Span item1, out System.Text.Utf8Span item2, out System.Text.Utf8Span item3, out System.Text.Utf8Span item4, out System.Text.Utf8Span item5, out System.Text.Utf8Span item6) { throw null; }
- [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
- public void Deconstruct(out System.Text.Utf8Span item1, out System.Text.Utf8Span item2, out System.Text.Utf8Span item3, out System.Text.Utf8Span item4, out System.Text.Utf8Span item5, out System.Text.Utf8Span item6, out System.Text.Utf8Span item7) { throw null; }
- [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
- public void Deconstruct(out System.Text.Utf8Span item1, out System.Text.Utf8Span item2, out System.Text.Utf8Span item3, out System.Text.Utf8Span item4, out System.Text.Utf8Span item5, out System.Text.Utf8Span item6, out System.Text.Utf8Span item7, out System.Text.Utf8Span item8) { throw null; }
- public Enumerator GetEnumerator() { throw null; }
- public ref struct Enumerator
- {
- private readonly object _dummy;
- private readonly int _dummyPrimitive;
- public System.Text.Utf8Span Current { get { throw null; } }
- public bool MoveNext() { throw null; }
- }
- }
- public readonly ref struct SplitOnResult
- {
- private readonly object _dummy;
- private readonly int _dummyPrimitive;
- public Utf8Span After { get { throw null; } }
- public Utf8Span Before { get { throw null; } }
- [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
- public void Deconstruct(out System.Text.Utf8Span before, out System.Text.Utf8Span after) { throw null; }
- }
- }
-}
+++ /dev/null
-<Project Sdk="Microsoft.NET.Sdk">
- <PropertyGroup>
- <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
- <!-- disable warnings about obsolete APIs -->
- <NoWarn>$(NoWarn);0809;0618</NoWarn>
- <TargetFrameworks>netstandard2.0;netstandard2.1;netcoreapp3.0;$(NetCoreAppCurrent);net461</TargetFrameworks>
- <Nullable>enable</Nullable>
- </PropertyGroup>
- <ItemGroup Condition="'$(IsPrerelease)' != 'false'">
- <Compile Include="System.Utf8String.Experimental.cs" />
- <Compile Include="System.Utf8String.Experimental.Forwards.cs" />
- </ItemGroup>
- <ItemGroup Condition="'$(IsPrerelease)' != 'false' and ('$(TargetFramework)' == 'netstandard2.0' or '$(TargetFramework)' == 'net461')">
- <Compile Include="System.Utf8String.Experimental.Range.cs" />
- <Compile Include="System.Utf8String.Experimental.Rune.cs" />
- </ItemGroup>
- <ItemGroup Condition="'$(IsPrerelease)' != 'false' and '$(TargetFramework)' == 'netstandard2.1'">
- <Compile Include="System.Utf8String.Experimental.Rune.cs" />
- </ItemGroup>
- <ItemGroup Condition="'$(IsPrerelease)' != 'false' and '$(TargetFramework)' == '$(NetCoreAppCurrent)'">
- <Compile Include="System.Utf8String.Experimental.net5.0.cs" />
- </ItemGroup>
- <ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0' or '$(TargetFramework)' == 'net461'">
- <PackageReference Include="System.Memory" Version="$(SystemMemoryVersion)" />
- </ItemGroup>
- <ItemGroup Condition="'$(TargetFramework)' == '$(NetCoreAppCurrent)'">
- <ProjectReference Include="$(LibrariesProjectRoot)System.Net.Http\ref\System.Net.Http.csproj" />
- <ProjectReference Include="$(LibrariesProjectRoot)System.Net.Primitives\ref\System.Net.Primitives.csproj" />
- <ProjectReference Include="$(LibrariesProjectRoot)System.Runtime\ref\System.Runtime.csproj" />
- <ProjectReference Include="$(LibrariesProjectRoot)System.Runtime.InteropServices\ref\System.Runtime.InteropServices.csproj" />
- </ItemGroup>
- <ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp3.0'">
- <Reference Include="netstandard" />
- <Reference Include="System.Net.Http" />
- <Reference Include="System.Net.Primitives" />
- <Reference Include="System.Runtime" />
- <Reference Include="System.Runtime.InteropServices" />
- </ItemGroup>
- <ItemGroup Condition="$(TargetFramework.StartsWith('net4'))">
- <Reference Include="System.Net.Http" />
- <PackageReference Include="System.ValueTuple" Version="$(SystemValueTupleVersion)" Condition="'$(TargetFramework)' == 'net461'" />
- </ItemGroup>
-</Project>
\ No newline at end of file
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// ------------------------------------------------------------------------------
-// Changes to this file must follow the https://aka.ms/api-review process.
-// ------------------------------------------------------------------------------
-
-namespace System
-{
- public static partial class Utf8Extensions
- {
- public static System.ReadOnlyMemory<System.Char8> AsMemory(this System.Utf8String? text) { throw null; }
- public static System.ReadOnlyMemory<System.Char8> AsMemory(this System.Utf8String? text, System.Index startIndex) { throw null; }
- public static System.ReadOnlyMemory<System.Char8> AsMemory(this System.Utf8String? text, int start) { throw null; }
- public static System.ReadOnlyMemory<System.Char8> AsMemory(this System.Utf8String? text, int start, int length) { throw null; }
- public static System.ReadOnlyMemory<System.Char8> AsMemory(this System.Utf8String? text, System.Range range) { throw null; }
- }
-}
-namespace System.Net.Http
-{
- public sealed partial class Utf8StringContent : System.Net.Http.HttpContent
- {
- protected override System.IO.Stream CreateContentReadStream(System.Threading.CancellationToken cancellationToken) { throw null; }
- protected override void SerializeToStream(System.IO.Stream stream, System.Net.TransportContext? context, System.Threading.CancellationToken cancellationToken) { }
- protected override System.Threading.Tasks.Task SerializeToStreamAsync(System.IO.Stream stream, System.Net.TransportContext? context, System.Threading.CancellationToken cancellationToken) { throw null; }
- }
-}
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-<root>
- <!--
- Microsoft ResX Schema
-
- Version 2.0
-
- The primary goals of this format is to allow a simple XML format
- that is mostly human readable. The generation and parsing of the
- various data types are done through the TypeConverter classes
- associated with the data types.
-
- Example:
-
- ... ado.net/XML headers & schema ...
- <resheader name="resmimetype">text/microsoft-resx</resheader>
- <resheader name="version">2.0</resheader>
- <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
- <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
- <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
- <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
- <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
- <value>[base64 mime encoded serialized .NET Framework object]</value>
- </data>
- <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
- <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
- <comment>This is a comment</comment>
- </data>
-
- There are any number of "resheader" rows that contain simple
- name/value pairs.
-
- Each data row contains a name, and value. The row also contains a
- type or mimetype. Type corresponds to a .NET class that support
- text/value conversion through the TypeConverter architecture.
- Classes that don't support this are serialized and stored with the
- mimetype set.
-
- The mimetype is used for serialized objects, and tells the
- ResXResourceReader how to depersist the object. This is currently not
- extensible. For a given mimetype the value must be set accordingly:
-
- Note - application/x-microsoft.net.object.binary.base64 is the format
- that the ResXResourceWriter will generate, however the reader can
- read any of the formats listed below.
-
- mimetype: application/x-microsoft.net.object.binary.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.soap.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.bytearray.base64
- value : The object must be serialized into a byte array
- : using a System.ComponentModel.TypeConverter
- : and then encoded with base64 encoding.
- -->
- <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
- <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
- <xsd:element name="root" msdata:IsDataSet="true">
- <xsd:complexType>
- <xsd:choice maxOccurs="unbounded">
- <xsd:element name="metadata">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" />
- </xsd:sequence>
- <xsd:attribute name="name" use="required" type="xsd:string" />
- <xsd:attribute name="type" type="xsd:string" />
- <xsd:attribute name="mimetype" type="xsd:string" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="assembly">
- <xsd:complexType>
- <xsd:attribute name="alias" type="xsd:string" />
- <xsd:attribute name="name" type="xsd:string" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="data">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
- <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
- <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="resheader">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" />
- </xsd:complexType>
- </xsd:element>
- </xsd:choice>
- </xsd:complexType>
- </xsd:element>
- </xsd:schema>
- <resheader name="resmimetype">
- <value>text/microsoft-resx</value>
- </resheader>
- <resheader name="version">
- <value>2.0</value>
- </resheader>
- <resheader name="reader">
- <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <resheader name="writer">
- <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <data name="ArgumentOutOfRange_Index" xml:space="preserve">
- <value>Index was out of range. Must be non-negative and less than the size of the collection.</value>
- </data>
- <data name="ArgumentOutOfRange_NeedNonNegNum" xml:space="preserve">
- <value>Non-negative number required.</value>
- </data>
- <data name="ArgumentOutOfRange_Utf16SurrogatesDisallowed" xml:space="preserve">
- <value>UTF-16 surrogate code points (U+D800..U+DFFF) are disallowed.</value>
- </data>
- <data name="Argument_CannotBeEmptySpan" xml:space="preserve">
- <value>Argument cannot be an empty span.</value>
- </data>
- <data name="Argument_CannotBeNullOrEmpty" xml:space="preserve">
- <value>Argument cannot be null or empty.</value>
- </data>
- <data name="Argument_CannotExtractScalar" xml:space="preserve">
- <value>Cannot extract a Unicode scalar value from the specified index in the input.</value>
- </data>
- <data name="Argument_DestinationTooShort" xml:space="preserve">
- <value>Destination is too short.</value>
- </data>
- <data name="Arg_EnumIllegalVal" xml:space="preserve">
- <value>Illegal enum value: {0}.</value>
- </data>
- <data name="Arg_MustBeNullTerminatedString" xml:space="preserve">
- <value>The string must be null-terminated.</value>
- </data>
- <data name="NotSupported_StringComparison" xml:space="preserve">
- <value>The string comparison type passed in is currently not supported.</value>
- </data>
- <data name="Utf8Span_CannotCallEqualsObject" xml:space="preserve">
- <value>Cannot call Utf8Span.Equals(object). Use Equals(Utf8Span) or operator == instead.</value>
- </data>
- <data name="Utf8Span_TryFindOnlySupportsOrdinal" xml:space="preserve">
- <value>UTF-8 searching only supports StringComparison Ordinal and OrdinalIgnoreCase on this platform.</value>
- </data>
- <data name="Utf8String_CallbackProvidedMalformedData" xml:space="preserve">
- <value>The callback populated its buffer with ill-formed UTF-8 data. Callbacks are required to populate the buffer only with well-formed UTF-8 data.</value>
- </data>
- <data name="Utf8String_CannotSplitMultibyteSubsequence" xml:space="preserve">
- <value>Cannot create the desired substring because it would split a multi-byte UTF-8 subsequence.</value>
- </data>
- <data name="Utf8String_InputContainedMalformedUtf16" xml:space="preserve">
- <value>The input buffer contained ill-formed UTF-16 data.</value>
- </data>
- <data name="Utf8String_InputContainedMalformedUtf8" xml:space="preserve">
- <value>The input buffer contained ill-formed UTF-8 data.</value>
- </data>
- <data name="Arg_MustBeRune" xml:space="preserve">
- <value>Object must be of type Rune.</value>
- </data>
-</root>
\ No newline at end of file
+++ /dev/null
-<Project Sdk="Microsoft.NET.Sdk">
- <PropertyGroup>
- <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
- <TargetFrameworks>$(NetCoreAppCurrent);netstandard2.0;netstandard2.1;netcoreapp3.0;net461</TargetFrameworks>
- <Nullable>enable</Nullable>
- <DefineConstants>$(DefineContants);FEATURE_UTF8STRING</DefineConstants>
- </PropertyGroup>
- <!-- DesignTimeBuild requires all the TargetFramework Derived Properties to not be present in the first property group. -->
- <PropertyGroup>
- <!-- CS3019: CLS attributes on internal types. Some shared source files are internal in this project. -->
- <!-- CS0162: Unreachable code detected from Intrinsics IsSupported being 'false'. -->
- <NoWarn Condition="'$(TargetFramework)' != '$(NetCoreAppCurrent)'">$(NoWarn);CS3019;CS0162</NoWarn>
- <IsPartialFacadeAssembly Condition="'$(TargetFramework)' != 'netstandard2.0' AND !$(TargetFramework.StartsWith('net4'))">true</IsPartialFacadeAssembly>
- </PropertyGroup>
- <ItemGroup Condition="'$(IsPrerelease)' != 'false'">
- <Compile Include="System\IO\Utf8StringStream.cs" />
- <Compile Include="System\Net\Http\Utf8StringContent.cs" />
- </ItemGroup>
- <ItemGroup Condition="'$(IsPrerelease)' != 'false' AND ('$(TargetFramework)' == 'netstandard2.0' or $(TargetFramework.StartsWith('net4')))">
- <Compile Include="$(CoreLibSharedDir)\System\Index.cs"
- Link="System\Index.cs" />
- <Compile Include="$(CoreLibSharedDir)\System\Numerics\BitOperations.cs"
- Link="System\Numerics\BitOperations.cs" />
- <Compile Include="$(CoreLibSharedDir)\System\Numerics\Hashing\HashHelpers.cs"
- Link="System\Numerics\Hashing\HashHelpers.cs" />
- <Compile Include="$(CoreLibSharedDir)\System\Range.cs"
- Link="System\Range.cs" />
- <Compile Include="$(CoreLibSharedDir)\System\Text\Rune.cs"
- Link="System\Text\Rune.cs" />
- <Compile Include="$(CoreLibSharedDir)\System\Text\Unicode\Utf8.cs"
- Link="System\Text\Unicode\Utf8.cs" />
- </ItemGroup>
- <ItemGroup Condition="'$(IsPrerelease)' != 'false' AND '$(TargetFramework)' == 'netstandard2.1'">
- <Compile Include="$(CoreLibSharedDir)\System\Numerics\BitOperations.cs"
- Link="System\Numerics\BitOperations.cs" />
- <Compile Include="$(CoreLibSharedDir)\System\Text\Rune.cs"
- Link="System\Text\Rune.cs" />
- <Compile Include="$(CoreLibSharedDir)\System\Text\Unicode\Utf8.cs"
- Link="System\Text\Unicode\Utf8.cs" />
- </ItemGroup>
- <ItemGroup Condition="'$(IsPrerelease)' != 'false' AND '$(TargetFramework)' != '$(NetCoreAppCurrent)'">
- <Compile Include="System\Globalization\GlobalizationMode.cs" />
- <Compile Include="System\Runtime\Intrinsics\Intrinsics.Shims.cs" />
- <Compile Include="$(CoreLibSharedDir)\System\Runtime\Intrinsics\Arm\AdvSimd.PlatformNotSupported.cs" Link="System\Runtime\Intrinsics\Arm\AdvSimd.PlatformNotSupported.cs" />
- <Compile Include="$(CoreLibSharedDir)\System\Runtime\Intrinsics\Arm\ArmBase.PlatformNotSupported.cs" Link="System\Runtime\Intrinsics\Arm\ArmBase.PlatformNotSupported.cs" />
- <Compile Include="System\ThrowHelper.cs" />
- <Compile Include="System\Utf8Extensions.Portable.cs" />
- <Compile Include="System\Utf8String.Portable.cs" />
- <Compile Include="$(CommonPath)System\Marvin.cs"
- Link="Common\System\Marvin.cs" />
- <Compile Include="$(CommonPath)System\NotImplemented.cs"
- Link="Common\System\NotImplemented.cs" />
- <Compile Include="$(CoreLibSharedDir)\System\Char8.cs"
- Link="System\Char8.cs" />
- <Compile Include="$(CoreLibSharedDir)\System\Text\TrimType.cs"
- Link="System\Text\TrimType.cs" />
- <Compile Include="$(CoreLibSharedDir)\System\Text\UnicodeDebug.cs"
- Link="System\Text\UnicodeDebug.cs" />
- <Compile Include="$(CoreLibSharedDir)\System\Text\UnicodeUtility.cs"
- Link="System\Text\UnicodeUtility.cs" />
- <Compile Include="$(CoreLibSharedDir)\System\Text\Unicode\Utf16Utility.cs"
- Link="System\Text\Unicode\Utf16Utility.cs" />
- <Compile Include="$(CoreLibSharedDir)\System\Text\Unicode\Utf16Utility.Validation.cs"
- Link="System\Text\Unicode\Utf16Utility.Validation.cs" />
- <Compile Include="$(CoreLibSharedDir)\System\Text\Utf8StringComparer.cs"
- Link="System\Text\Utf8StringComparer.cs" />
- <Compile Include="$(CoreLibSharedDir)\System\Text\ASCIIUtility.cs"
- Link="System\Text\ASCIIUtility.cs" />
- <Compile Include="$(CoreLibSharedDir)\System\Text\ASCIIUtility.Helpers.cs"
- Link="System\Text\ASCIIUtility.Helpers.cs" />
- <Compile Include="$(CoreLibSharedDir)\System\Text\Unicode\Utf8Utility.cs"
- Link="System\Text\Unicode\Utf8Utility.cs" />
- <Compile Include="$(CoreLibSharedDir)\System\Text\Unicode\Utf8Utility.Helpers.cs"
- Link="System\Text\Unicode\Utf8Utility.Helpers.cs" />
- <Compile Include="$(CoreLibSharedDir)\System\Text\Unicode\Utf8Utility.Validation.cs"
- Link="System\Text\Unicode\Utf8Utility.Validation.cs" />
- <Compile Include="$(CoreLibSharedDir)\System\Text\Unicode\Utf8Utility.Transcoding.cs"
- Link="System\Text\Unicode\Utf8Utility.Transcoding.cs" />
- <Compile Include="$(CoreLibSharedDir)\System\Text\Unicode\Utf8Utility.WhiteSpace.NonCoreLib.cs"
- Link="System\Text\Unicode\Utf8Utility.WhiteSpace.NonCoreLib.cs" />
- <Compile Include="$(CoreLibSharedDir)\System\Utf8StringSplitOptions.cs"
- Link="System\Utf8StringSplitOptions.cs" />
- <Compile Include="$(CoreLibSharedDir)\System\Utf8Extensions.cs"
- Link="System\Utf8Extensions.cs" />
- <Compile Include="$(CoreLibSharedDir)\System\Utf8String.cs"
- Link="System\Utf8String.cs" />
- <Compile Include="$(CoreLibSharedDir)\System\Utf8String.Comparison.cs"
- Link="System\Utf8String.Comparison.cs" />
- <Compile Include="$(CoreLibSharedDir)\System\Utf8String.Construction.cs"
- Link="System\Utf8String.Construction.cs" />
- <Compile Include="$(CoreLibSharedDir)\System\Utf8String.Conversion.cs"
- Link="System\Utf8String.Conversion.cs" />
- <Compile Include="$(CoreLibSharedDir)\System\Utf8String.Enumeration.cs"
- Link="System\Utf8String.Enumeration.cs" />
- <Compile Include="$(CoreLibSharedDir)\System\Utf8String.Manipulation.cs"
- Link="System\Utf8String.Manipulation.cs" />
- <Compile Include="$(CoreLibSharedDir)\System\Utf8String.Searching.cs"
- Link="System\Utf8String.Searching.cs" />
- <Compile Include="$(CoreLibSharedDir)\System\Text\Utf8Span.cs"
- Link="System\Text\Utf8Span.cs" />
- <Compile Include="$(CoreLibSharedDir)\System\Text\Utf8Span.Comparison.cs"
- Link="System\Text\Utf8Span.Comparison.cs" />
- <Compile Include="$(CoreLibSharedDir)\System\Text\Utf8Span.Conversion.cs"
- Link="System\Text\Utf8Span.Conversion.cs" />
- <Compile Include="$(CoreLibSharedDir)\System\Text\Utf8Span.Enumeration.cs"
- Link="System\Text\Utf8Span.Enumeration.cs" />
- <Compile Include="$(CoreLibSharedDir)\System\Text\Utf8Span.Manipulation.cs"
- Link="System\Text\Utf8Span.Manipulation.cs" />
- <Compile Include="$(CoreLibSharedDir)\System\Text\Utf8Span.Searching.cs"
- Link="System\Text\Utf8Span.Searching.cs" />
- </ItemGroup>
- <ItemGroup Condition="'$(IsPrerelease)' != 'false' AND '$(TargetFramework)' == '$(NetCoreAppCurrent)'">
- <Compile Include="System\Net\Http\Utf8StringContent.netcoreapp.cs" />
- </ItemGroup>
- <ItemGroup Condition="'$(TargetFramework)' != '$(NetCoreAppCurrent)'">
- <!-- Duplicate types with System.Runtime.Intrinsics. Excluding the default reference. -->
- <DefaultReferenceExclusion Include="System.Runtime.Intrinsics" />
- <ProjectReference Include="$(LibrariesProjectRoot)System.Runtime.CompilerServices.Unsafe\src\System.Runtime.CompilerServices.Unsafe.ilproj" />
- </ItemGroup>
- <ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0' or
- $(TargetFramework.StartsWith('net4'))">
- <PackageReference Include="System.Buffers" Version="$(SystemBuffersVersion)" />
- <PackageReference Include="System.Memory" Version="$(SystemMemoryVersion)" />
- <PackageReference Include="System.Numerics.Vectors" Version="$(SystemNumericsVectorsVersion)" />
- </ItemGroup>
- <ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.1'">
- <PackageReference Include="System.Numerics.Vectors" Version="$(SystemNumericsVectorsVersion)" />
- </ItemGroup>
- <ItemGroup Condition="'$(TargetFramework)' == '$(NetCoreAppCurrent)'">
- <!-- The predefined type 'Interlocked' is defined in multiple assemblies. -->
- <DefaultReferenceExclusion Include="System.Threading" />
- <ProjectReference Include="$(CoreLibProject)" />
- <ProjectReference Include="$(LibrariesProjectRoot)System.Runtime\src\System.Runtime.csproj" />
- <Reference Include="System.Net.Http" />
- <Reference Include="System.Net.Primitives" />
- </ItemGroup>
- <ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp3.0'">
- <Reference Include="netstandard" />
- <Reference Include="System.Diagnostics.Debug" />
- <Reference Include="System.Memory" />
- <Reference Include="System.Net.Http" />
- <Reference Include="System.Net.Primitives" />
- <Reference Include="System.Numerics.Vectors" />
- <Reference Include="System.Resources.ResourceManager" />
- <Reference Include="System.Runtime" />
- <Reference Include="System.Runtime.Extensions" />
- <Reference Include="System.Runtime.InteropServices" />
- <Reference Include="System.Security.Cryptography.Algorithms" />
- </ItemGroup>
- <ItemGroup Condition="$(TargetFramework.StartsWith('net4'))">
- <Reference Include="System.Net.Http" />
- <PackageReference Include="System.ValueTuple" Version="$(SystemValueTupleVersion)" Condition="'$(TargetFramework)' == 'net461'" />
- </ItemGroup>
-</Project>
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-namespace System.Globalization
-{
- internal static partial class GlobalizationMode
- {
- internal static bool Invariant { get; } // TODO: should we enable this?
- }
-}
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace System.IO
-{
- internal sealed class Utf8StringStream : Stream
- {
- private readonly Utf8String _content;
- private int _position;
-
- public Utf8StringStream(Utf8String content)
- {
- _content = content ?? Utf8String.Empty;
- }
-
- public override bool CanRead => true;
-
- public override bool CanSeek => true;
-
- public override bool CanTimeout => true;
-
- public override bool CanWrite => false;
-
- public override long Length => _content.Length;
-
- public override long Position
- {
- get => _position;
- set
- {
- if ((ulong)value > (uint)_content.Length)
- {
- throw new ArgumentOutOfRangeException(nameof(value));
- }
-
- _position = (int)value;
- }
- }
-
- public override void Flush()
- {
- /* no-op */
- }
-
- public override Task FlushAsync(CancellationToken cancellationToken)
- {
- /* no-op */
- return Task.CompletedTask;
- }
-
- public override int Read(byte[] buffer, int offset, int count)
- {
- return Read(new Span<byte>(buffer, offset, count));
- }
-
- public
-#if (!NETSTANDARD2_0 && !NETFRAMEWORK)
- override
-#endif
- int Read(Span<byte> buffer)
- {
- ReadOnlySpan<byte> contentToWrite = _content.AsBytes(_position);
- if (buffer.Length < contentToWrite.Length)
- {
- contentToWrite = contentToWrite.Slice(buffer.Length);
- }
-
- contentToWrite.CopyTo(buffer);
- _position += contentToWrite.Length;
-
- return contentToWrite.Length;
- }
-
- public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
- {
- return Task.FromResult(Read(new Span<byte>(buffer, offset, count)));
- }
-
-#if (!NETSTANDARD2_0 && !NETFRAMEWORK)
- public override ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default)
- {
- return new ValueTask<int>(Read(buffer.Span));
- }
-#endif
-
- public override int ReadByte()
- {
- int position = _position;
- if ((uint)position >= (uint)_content.Length)
- {
- return -1;
- }
-
- _position++;
- return _content.AsBytes()[position];
- }
-
- public override long Seek(long offset, SeekOrigin origin)
- {
- switch (origin)
- {
- case SeekOrigin.Begin:
- break;
- case SeekOrigin.Current:
- offset += _position;
- break;
- case SeekOrigin.End:
- offset += _content.Length;
- break;
- default:
- throw new ArgumentOutOfRangeException(nameof(origin));
- }
-
- if ((ulong)offset > (uint)_content.Length)
- {
- throw new ArgumentOutOfRangeException(nameof(offset));
- }
-
- _position = (int)offset;
- return offset;
- }
-
- public override void SetLength(long value) => throw new NotSupportedException();
-
- public override void Write(byte[] buffer, int offset, int count) => throw new NotSupportedException();
-
-#if (!NETSTANDARD2_0 && !NETFRAMEWORK)
- public override void Write(ReadOnlySpan<byte> buffer) => throw new NotSupportedException();
-#endif
-
- public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) => throw new NotSupportedException();
-
-#if (!NETSTANDARD2_0 && !NETFRAMEWORK)
- public override ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default) => throw new NotSupportedException();
-#endif
- public override void WriteByte(byte value) => throw new NotSupportedException();
- }
-}
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System.Buffers;
-using System.IO;
-using System.Net.Http.Headers;
-using System.Runtime.InteropServices;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace System.Net.Http
-{
- public sealed partial class Utf8StringContent : HttpContent
- {
- private const string DefaultMediaType = "text/plain";
-
- private readonly Utf8String _content;
-
- public Utf8StringContent(Utf8String content)
- : this(content, mediaType: null)
- {
- }
-
- public Utf8StringContent(Utf8String content, string? mediaType)
- {
- if (content is null)
- {
- throw new ArgumentNullException(nameof(content));
- }
-
- _content = content;
-
- // Initialize the 'Content-Type' header with information provided by parameters.
-
- Headers.ContentType = new MediaTypeHeaderValue(mediaType ?? DefaultMediaType)
- {
- CharSet = "utf-8" // Encoding.UTF8.WebName
- };
- }
-
- protected override Task<Stream> CreateContentReadStreamAsync() =>
- Task.FromResult<Stream>(new Utf8StringStream(_content));
-
-#if (NETSTANDARD2_0 || NETFRAMEWORK)
- protected override async Task SerializeToStreamAsync(Stream stream, TransportContext? context)
- {
- ReadOnlyMemory<byte> buffer = _content.AsMemoryBytes();
- if (MemoryMarshal.TryGetArray(buffer, out ArraySegment<byte> array))
- {
- await stream.WriteAsync(array.Array, array.Offset, array.Count).ConfigureAwait(false);
- }
- else
- {
- byte[] localBuffer = ArrayPool<byte>.Shared.Rent(buffer.Length);
- buffer.Span.CopyTo(localBuffer);
-
- await stream.WriteAsync(localBuffer, 0, buffer.Length).ConfigureAwait(false);
-
- ArrayPool<byte>.Shared.Return(localBuffer);
- }
- }
-#elif NETSTANDARD2_1 || NETCOREAPP3_0
- protected override Task SerializeToStreamAsync(Stream stream, TransportContext? context) =>
- stream.WriteAsync(_content.AsMemoryBytes()).AsTask();
-#else
- protected override Task SerializeToStreamAsync(Stream stream, TransportContext? context) =>
- SerializeToStreamAsync(stream, context, default);
-
- protected override Task SerializeToStreamAsync(Stream stream, TransportContext? context, CancellationToken cancellationToken) =>
- stream.WriteAsync(_content.AsMemoryBytes(), cancellationToken).AsTask();
-#endif
-
- protected override bool TryComputeLength(out long length)
- {
- length = _content.Length;
- return true;
- }
- }
-}
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System.Buffers;
-using System.IO;
-using System.Net.Http.Headers;
-using System.Runtime.InteropServices;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace System.Net.Http
-{
- public sealed partial class Utf8StringContent
- {
- protected override Stream CreateContentReadStream(CancellationToken cancellationToken) =>
- new Utf8StringStream(_content);
-
- protected override void SerializeToStream(Stream stream, TransportContext? context, CancellationToken cancellationToken) =>
- stream.Write(_content.AsBytes());
- }
-}
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-namespace System.Runtime.Intrinsics
-{
- internal static class Vector64
- {
- public static Vector64<ulong> Create(ulong value) => throw new PlatformNotSupportedException();
- public static Vector64<uint> CreateScalar(uint value) => throw new PlatformNotSupportedException();
- public static Vector64<byte> AsByte<T>(this Vector64<T> vector) where T : struct => throw new PlatformNotSupportedException();
- public static Vector64<uint> AsUInt32<T>(this Vector64<T> vector) where T : struct => throw new PlatformNotSupportedException();
- public static Vector64<T> GetLower<T>(this Vector128<T> vector) where T : struct => throw new PlatformNotSupportedException();
- public static Vector64<ulong> AsUInt64<T>(this Vector64<T> vector) where T : struct => throw new PlatformNotSupportedException();
- }
- internal readonly struct Vector64<T>
- where T : struct
- {
- }
-
- internal static class Vector128
- {
- public static Vector128<long> Create(long value) => throw new PlatformNotSupportedException();
- public static Vector128<short> Create(short value) => throw new PlatformNotSupportedException();
- public static Vector128<ulong> Create(ulong value) => throw new PlatformNotSupportedException();
- public static Vector128<ushort> Create(ushort value) => throw new PlatformNotSupportedException();
- public static Vector128<byte> Create(byte value) => throw new PlatformNotSupportedException();
- public static Vector128<uint> Create(uint value) => throw new PlatformNotSupportedException();
- public static Vector128<ulong> CreateScalarUnsafe(ulong value) => throw new PlatformNotSupportedException();
- public static Vector128<byte> AsByte<T>(this Vector128<T> vector) where T : struct => throw new PlatformNotSupportedException();
- public static Vector128<short> AsInt16<T>(this Vector128<T> vector) where T : struct => throw new PlatformNotSupportedException();
- public static Vector128<sbyte> AsSByte<T>(this Vector128<T> vector) where T : struct => throw new PlatformNotSupportedException();
- public static Vector128<ushort> AsUInt16<T>(this Vector128<T> vector) where T : struct => throw new PlatformNotSupportedException();
- public static Vector128<uint> AsUInt32<T>(this Vector128<T> vector) where T : struct => throw new PlatformNotSupportedException();
- public static Vector128<ulong> AsUInt64<T>(this Vector128<T> vector) where T : struct => throw new PlatformNotSupportedException();
- public static T GetElement<T>(this Vector128<T> vector, int index) where T : struct => throw new PlatformNotSupportedException();
- public static T ToScalar<T>(this Vector64<T> vector) where T : struct => throw new PlatformNotSupportedException();
- public static unsafe Vector128<ulong> CreateScalar(ulong value) => throw new PlatformNotSupportedException();
- public static T ToScalar<T>(this Vector128<T> vector) where T : struct => throw new PlatformNotSupportedException();
- }
- internal readonly struct Vector128<T>
- where T : struct
- {
- public static Vector128<T> Zero => throw new PlatformNotSupportedException();
- public static int Count => throw new PlatformNotSupportedException();
- }
-}
-
-namespace System.Runtime.Intrinsics.X86
-{
- internal static class X86Base
- {
- internal static class X64
- {
- public const bool IsSupported = false;
- internal static ulong BitScanForward(ulong value) => throw new PlatformNotSupportedException();
- internal static ulong BitScanReverse(ulong value) => throw new PlatformNotSupportedException();
- }
- public const bool IsSupported = false;
- internal static uint BitScanForward(uint value) => throw new PlatformNotSupportedException();
- internal static uint BitScanReverse(uint value) => throw new PlatformNotSupportedException();
- }
- internal abstract class Bmi1
- {
- public abstract class X64
- {
- public const bool IsSupported = false;
- public static ulong TrailingZeroCount(ulong value) => throw new PlatformNotSupportedException();
- }
- public const bool IsSupported = false;
- public static uint TrailingZeroCount(uint value) => throw new PlatformNotSupportedException();
- }
- internal abstract class Lzcnt
- {
- public abstract class X64
- {
- public const bool IsSupported = false;
- public static ulong LeadingZeroCount(ulong value) => throw new PlatformNotSupportedException();
- }
- public const bool IsSupported = false;
- public static uint LeadingZeroCount(uint value) => throw new PlatformNotSupportedException();
- }
- internal abstract class Popcnt
- {
- public abstract class X64
- {
- public const bool IsSupported = false;
- public static ulong PopCount(ulong value) => throw new PlatformNotSupportedException();
- }
- public const bool IsSupported = false;
- public static uint PopCount(uint value) => throw new PlatformNotSupportedException();
- }
-
- internal abstract class Sse2
- {
- public abstract class X64
- {
- public const bool IsSupported = false;
- public static Vector128<ulong> ConvertScalarToVector128UInt64(ulong value) => throw new PlatformNotSupportedException();
- public static ulong ConvertToUInt64(Vector128<ulong> value) => throw new PlatformNotSupportedException();
- }
- public const bool IsSupported = false;
- public static Vector128<ushort> Add(Vector128<ushort> left, Vector128<ushort> right) => throw new PlatformNotSupportedException();
- public static Vector128<ushort> AddSaturate(Vector128<ushort> left, Vector128<ushort> right) => throw new PlatformNotSupportedException();
- public static Vector128<ushort> AndNot(Vector128<ushort> left, Vector128<ushort> right) => throw new PlatformNotSupportedException();
- public static Vector128<short> CompareGreaterThan(Vector128<short> left, Vector128<short> right) => throw new PlatformNotSupportedException();
- public static Vector128<short> CompareLessThan(Vector128<short> left, Vector128<short> right) => throw new PlatformNotSupportedException();
- public static Vector128<uint> ConvertScalarToVector128UInt32(uint value) => throw new PlatformNotSupportedException();
- public static uint ConvertToUInt32(Vector128<uint> value) => throw new PlatformNotSupportedException();
- public static unsafe Vector128<byte> LoadAlignedVector128(byte* address) => throw new PlatformNotSupportedException();
- public static unsafe Vector128<ushort> LoadAlignedVector128(ushort* address) => throw new PlatformNotSupportedException();
- public static unsafe Vector128<byte> LoadVector128(byte* address) => throw new PlatformNotSupportedException();
- public static unsafe Vector128<short> LoadVector128(short* address) => throw new PlatformNotSupportedException();
- public static unsafe Vector128<ushort> LoadVector128(ushort* address) => throw new PlatformNotSupportedException();
- public static int MoveMask(Vector128<byte> value) => throw new PlatformNotSupportedException();
- public static Vector128<short> Or(Vector128<short> left, Vector128<short> right) => throw new PlatformNotSupportedException();
- public static Vector128<ushort> Or(Vector128<ushort> left, Vector128<ushort> right) => throw new PlatformNotSupportedException();
- public static Vector128<byte> PackUnsignedSaturate(Vector128<short> left, Vector128<short> right) => throw new PlatformNotSupportedException();
- public static Vector128<ushort> ShiftRightLogical(Vector128<ushort> value, byte count) => throw new PlatformNotSupportedException();
- public static unsafe void Store(byte* address, Vector128<byte> source) => throw new PlatformNotSupportedException();
- public static unsafe void StoreAligned(byte* address, Vector128<byte> source) => throw new PlatformNotSupportedException();
- public static unsafe void StoreScalar(ulong* address, Vector128<ulong> source) => throw new PlatformNotSupportedException();
- public static Vector128<ushort> Subtract(Vector128<ushort> left, Vector128<ushort> right) => throw new PlatformNotSupportedException();
- public static Vector128<byte> UnpackHigh(Vector128<byte> left, Vector128<byte> right) => throw new PlatformNotSupportedException();
- public static Vector128<byte> UnpackLow(Vector128<byte> left, Vector128<byte> right) => throw new PlatformNotSupportedException();
- }
-
- internal abstract class Sse41
- {
- public abstract class X64
- {
- public const bool IsSupported = false;
- }
- public const bool IsSupported = false;
- public static Vector128<ushort> Min(Vector128<ushort> left, Vector128<ushort> right) => throw new PlatformNotSupportedException();
- public static bool TestZ(Vector128<short> left, Vector128<short> right) => throw new PlatformNotSupportedException();
- public static bool TestZ(Vector128<ushort> left, Vector128<ushort> right) => throw new PlatformNotSupportedException();
- }
-}
-
-namespace System.Runtime.CompilerServices
-{
- // Calls to methods or references to fields marked with this attribute may be replaced at
- // some call sites with jit intrinsic expansions.
- // Types marked with this attribute may be specially treated by the runtime/compiler.
- [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Field, Inherited = false)]
- internal sealed class IntrinsicAttribute : Attribute
- {
- }
-}
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System.Diagnostics.CodeAnalysis;
-using System.Runtime.CompilerServices;
-
-namespace System
-{
- internal static class ThrowHelper
- {
- [DoesNotReturn]
- internal static void ThrowArgumentException(string resource, ExceptionArgument argument)
- {
- throw new ArgumentException(resource, argument.ToString());
- }
-
- [DoesNotReturn]
- internal static void ThrowArgumentNullException(ExceptionArgument argument) { throw CreateArgumentNullException(argument); }
- [MethodImpl(MethodImplOptions.NoInlining)]
- private static Exception CreateArgumentNullException(ExceptionArgument argument) { return new ArgumentNullException(argument.ToString()); }
-
- [DoesNotReturn]
- internal static void ThrowArgumentOutOfRangeException() { throw new ArgumentOutOfRangeException(); }
-
- internal static void ThrowArgumentOutOfRangeException(ExceptionArgument argument) { throw CreateArgumentOutOfRangeException(argument); }
- [MethodImpl(MethodImplOptions.NoInlining)]
- private static Exception CreateArgumentOutOfRangeException(ExceptionArgument argument) { return new ArgumentOutOfRangeException(argument.ToString()); }
-
- [DoesNotReturn]
- internal static void ThrowValueArgumentOutOfRange_NeedNonNegNumException()
- {
- throw GetArgumentOutOfRangeException(ExceptionArgument.value,
- SR.ArgumentOutOfRange_NeedNonNegNum);
- }
-
- [DoesNotReturn]
- internal static void ThrowLengthArgumentOutOfRange_ArgumentOutOfRange_NeedNonNegNum()
- {
- throw GetArgumentOutOfRangeException(ExceptionArgument.length,
- SR.ArgumentOutOfRange_NeedNonNegNum);
- }
-
- [DoesNotReturn]
- internal static void ThrowInvalidOperationException() { throw CreateInvalidOperationException(); }
- [MethodImpl(MethodImplOptions.NoInlining)]
- private static Exception CreateInvalidOperationException() { return new InvalidOperationException(); }
-
- [DoesNotReturn]
- internal static void ThrowArgumentException_DestinationTooShort()
- {
- throw new ArgumentException(SR.Argument_DestinationTooShort, "destination");
- }
-
- [DoesNotReturn]
- internal static void ThrowArgumentException_CannotExtractScalar(ExceptionArgument argument)
- {
- throw new ArgumentException(SR.Argument_CannotExtractScalar, argument.ToString());
- }
-
- internal static void ThrowArgumentOutOfRange_IndexException()
- {
- throw GetArgumentOutOfRangeException(ExceptionArgument.index,
- SR.ArgumentOutOfRange_Index);
- }
-
- private static ArgumentOutOfRangeException GetArgumentOutOfRangeException(ExceptionArgument argument, string resource)
- {
- return new ArgumentOutOfRangeException(argument.ToString(), resource);
- }
-
- [DoesNotReturn]
- internal static void ThrowNotSupportedException(string message)
- {
- throw new NotSupportedException(message);
- }
- }
-
- //
- // The convention for this enum is using the argument name as the enum name
- //
- internal enum ExceptionArgument
- {
- action,
- ch,
- comparisonType,
- culture,
- index,
- input,
- length,
- start,
- text,
- value,
- }
-}
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System.Runtime.CompilerServices;
-
-namespace System
-{
- public static partial class Utf8Extensions
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static ReadOnlySpan<byte> CreateSpan(Utf8String text) => text.GetSpan();
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static ReadOnlySpan<byte> CreateSpan(Utf8String text, int start) =>
- text.GetSpan().Slice(start);
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static ReadOnlySpan<byte> CreateSpan(Utf8String text, int start, int length) =>
- text.GetSpan().Slice(start, length);
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static ReadOnlyMemory<byte> CreateMemoryBytes(Utf8String text, int start, int length) =>
- text.CreateMemoryBytes(start, length);
- }
-}
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System.ComponentModel;
-using System.Diagnostics;
-using System.Diagnostics.CodeAnalysis;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-using System.Text.Unicode;
-
-namespace System
-{
- public sealed partial class Utf8String
- {
- private readonly byte[] _bytes;
-
- /// <summary>
- /// Returns the length (in UTF-8 code units, or <see cref="byte"/>s) of this instance.
- /// </summary>
- public int Length => _bytes.Length - 1; // -1 because the bytes are always null-terminated
-
- public Utf8String(ReadOnlySpan<byte> value)
- {
- _bytes = InitializeBuffer(value);
- }
-
- public Utf8String(byte[] value, int startIndex, int length)
- {
- if (value is null)
- {
- ThrowHelper.ThrowArgumentNullException(ExceptionArgument.value);
- }
-
- _bytes = InitializeBuffer(new ReadOnlySpan<byte>(value, startIndex, length));
- }
-
- [CLSCompliant(false)]
- public unsafe Utf8String(byte* value)
- {
- if (value is null)
- {
- ThrowHelper.ThrowArgumentNullException(ExceptionArgument.value);
- }
-
- _bytes = InitializeBuffer(new ReadOnlySpan<byte>(value, strlen(value)));
- }
-
- public Utf8String(ReadOnlySpan<char> value)
- {
- _bytes = InitializeBuffer(value);
- }
-
- public Utf8String(char[] value, int startIndex, int length)
- {
- if (value is null)
- {
- ThrowHelper.ThrowArgumentNullException(ExceptionArgument.value);
- }
-
- _bytes = InitializeBuffer(new ReadOnlySpan<char>(value, startIndex, length));
- }
-
- [CLSCompliant(false)]
- public unsafe Utf8String(char* value)
- {
- if (value == null)
- {
- ThrowHelper.ThrowArgumentNullException(ExceptionArgument.value);
- }
-
- _bytes = InitializeBuffer(new ReadOnlySpan<char>(value, wcslen(value)));
- }
-
- public Utf8String(string value)
- {
- if (value is null)
- {
- ThrowHelper.ThrowArgumentNullException(ExceptionArgument.value);
- }
-
- _bytes = InitializeBuffer(value.AsSpan());
- }
-
- private static byte[] InitializeBuffer(ReadOnlySpan<byte> value)
- {
- if (value.IsEmpty)
- {
- return Empty._bytes;
- }
-
- // Create and populate the Utf8String buffer.
-
- byte[] newBuffer = AllocateBuffer(value.Length);
- value.CopyTo(newBuffer);
-
- // Now perform validation.
- // Reminder: Perform validation over the copy, not over the source.
-
- if (!Utf8Utility.IsWellFormedUtf8(newBuffer))
- {
- throw new ArgumentException(
- message: SR.Utf8String_InputContainedMalformedUtf8,
- paramName: nameof(value));
- }
-
- return newBuffer;
- }
-
- private static byte[] InitializeBuffer(ReadOnlySpan<char> value)
- {
- byte[]? newBuffer = CreateBufferFromUtf16Common(value, replaceInvalidSequences: false);
-
- if (newBuffer is null)
- {
- // Input buffer contained invalid UTF-16 data.
-
- throw new ArgumentException(
- message: SR.Utf8String_InputContainedMalformedUtf16,
- paramName: nameof(value));
- }
-
- return newBuffer;
- }
-
- // This should only be called from FastAllocate
- private Utf8String(byte[] bytes)
- {
- _bytes = bytes;
- }
-
- /// <summary>
- /// Returns a <em>mutable</em> reference to the first byte of this <see cref="Utf8String"/>
- /// (or the null terminator if the string is empty).
- /// </summary>
- /// <returns></returns>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- internal ref byte DangerousGetMutableReference() =>
- ref MemoryMarshal.GetReference(_bytes.AsSpan());
-
- /// <summary>
- /// Returns a <em>mutable</em> <see cref="Span{Byte}"/> that can be used to populate this
- /// <see cref="Utf8String"/> instance. Only to be used during construction.
- /// </summary>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- internal Span<byte> DangerousGetMutableSpan()
- {
- Debug.Assert(Length > 0, $"This should only ever be called on a non-empty {nameof(Utf8String)}.");
- return _bytes.AsSpan(0, Length);
- }
-
- /// <summary>
- /// Returns a <see cref="ReadOnlySpan{Byte}"/> for this
- /// <see cref="Utf8String"/> instance.
- /// </summary>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- internal ReadOnlySpan<byte> GetSpan() => _bytes.AsSpan(0, Length);
-
- /// <summary>
- /// Gets an immutable reference that can be used in a <see langword="fixed"/> statement. The resulting
- /// reference can be pinned and used as a null-terminated <em>LPCUTF8STR</em>.
- /// </summary>
- /// <remarks>
- /// If this <see cref="Utf8String"/> instance is empty, returns a reference to the null terminator.
- /// </remarks>
- [EditorBrowsable(EditorBrowsableState.Never)] // for compiler use only
- public ref readonly byte GetPinnableReference() => ref _bytes.AsSpan().GetPinnableReference();
-
- /// <summary>
- /// Similar to <see cref="Utf8Extensions.AsBytes(Utf8String)"/>, but skips the null check on the input.
- /// Throws a <see cref="NullReferenceException"/> if the input is null.
- /// </summary>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- internal ReadOnlySpan<byte> AsBytesSkipNullCheck() => GetSpan();
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- internal ReadOnlyMemory<byte> CreateMemoryBytes(int start, int length) =>
- _bytes.AsMemory(start, length);
-
- /// <summary>
- /// Creates a new zero-initialized instance of the specified length. Actual storage allocated is "length + 1" bytes
- /// because instances are null-terminated.
- /// </summary>
- /// <remarks>
- /// The implementation of this method checks its input argument for overflow.
- /// </remarks>
- private static Utf8String FastAllocate(int length)
- {
- // just simulate a "fast allocate", since this is portable
- return new Utf8String(AllocateBuffer(length));
- }
-
- private static byte[] AllocateBuffer(int length)
- {
- Debug.Assert(length > 0);
-
- if (length == int.MaxValue)
- {
- // Ensure we don't overflow below. The VM will throw an OutOfMemoryException
- // if we try to create a byte[] this large anyway.
- length = int.MaxValue - 1;
- }
-
- // Actual storage allocated is "length + 1" bytes because instances are null-terminated.
- return new byte[length + 1];
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- internal static unsafe int wcslen(char* ptr)
- {
- // IndexOf processes memory in aligned chunks, and thus it won't crash even if it accesses memory beyond the null terminator.
- int length = new ReadOnlySpan<char>(ptr, int.MaxValue).IndexOf('\0');
- if (length < 0)
- {
- ThrowMustBeNullTerminatedString();
- }
-
- return length;
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static unsafe int strlen(byte* ptr)
- {
- // IndexOf processes memory in aligned chunks, and thus it won't crash even if it accesses memory beyond the null terminator.
- int length = new ReadOnlySpan<byte>(ptr, int.MaxValue).IndexOf((byte)'\0');
- if (length < 0)
- {
- ThrowMustBeNullTerminatedString();
- }
-
- return length;
- }
-
- [DoesNotReturn]
- private static void ThrowMustBeNullTerminatedString()
- {
- throw new ArgumentException(SR.Arg_MustBeNullTerminatedString);
- }
- }
-}
+++ /dev/null
-<Project Sdk="Microsoft.NET.Sdk">
- <PropertyGroup>
- <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
- <IncludePartialFacadeTests>true</IncludePartialFacadeTests>
- <TargetFrameworks>$(NetCoreAppCurrent);net461</TargetFrameworks>
- <TestRuntime>true</TestRuntime>
- <IncludeRemoteExecutor>true</IncludeRemoteExecutor>
- </PropertyGroup>
- <ItemGroup Condition="'$(IsPrerelease)' != 'false'">
- <Compile Include="System\Char8Tests.cs" />
- <Compile Include="System\MemoryTests.cs" />
- <Compile Include="System\Net\Http\Utf8StringContentTests.cs" />
- <Compile Include="System\ReflectionTests.cs" />
- <Compile Include="System\Utf8ExtensionsTests.cs" />
- <Compile Include="System\BoundedUtf8Span.cs" />
- <Compile Include="System\RangeEqualityComparer.cs" />
- <Compile Include="System\Utf8SpanTests.cs" />
- <Compile Include="System\Utf8SpanTests.Comparison.cs" />
- <Compile Include="System\Utf8SpanTests.Conversion.cs" />
- <Compile Include="System\Utf8SpanTests.Ctor.cs" />
- <Compile Include="System\Utf8SpanTests.Enumeration.cs" />
- <Compile Include="System\Utf8SpanTests.Manipulation.cs" />
- <Compile Include="System\Utf8SpanTests.Manipulation.TestData.cs" />
- <Compile Include="System\Utf8SpanTests.Searching.cs" />
- <Compile Include="System\Utf8SpanTests.Searching.TestData.cs" />
- <Compile Include="System\Utf8SpanTests.TestData.cs" />
- <Compile Include="System\Utf8StringTests.cs" />
- <Compile Include="System\Utf8StringTests.Comparison.cs" />
- <Compile Include="System\Utf8StringTests.Conversion.cs" />
- <Compile Include="System\Utf8StringTests.Ctor.cs" />
- <Compile Include="System\Utf8StringTests.Enumeration.cs" />
- <Compile Include="System\Utf8StringTests.Manipulation.cs" />
- <Compile Include="System\Utf8StringTests.Searching.cs" />
- <Compile Include="Xunit\SpanAssert.cs" />
- <Compile Include="System\Utf8TestUtilities.cs" />
- </ItemGroup>
- <ItemGroup Condition="'$(IsPrerelease)' != 'false' and '$(TargetFramework)' == '$(NetCoreAppCurrent)'">
- <Compile Include="System\Net\Http\Utf8StringContentTests.netcoreapp.cs" />
- <Compile Include="System\MemoryTests.netcoreapp.cs" />
- <Compile Include="System\ReflectionTests.netcoreapp.cs" />
- <Compile Include="System\Utf8ExtensionsTests.netcoreapp.cs" />
- <Compile Include="System\Utf8StringTests.Ctor.netcoreapp.cs" />
- </ItemGroup>
- <ItemGroup Condition="'$(IsPrerelease)' != 'false' and '$(TargetFramework)' == 'net461'">
- <Compile Include="System\MemoryTests.netfx.cs" />
- </ItemGroup>
- <ItemGroup>
- <ProjectReference Include="..\src\System.Utf8String.Experimental.csproj" />
- </ItemGroup>
- <ItemGroup Condition="'$(TargetFramework)' == 'net461'">
- <Reference Include="System.Net.Http" />
- </ItemGroup>
-</Project>
\ No newline at end of file
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System.Buffers;
-using System.Text;
-
-using static System.Tests.Utf8TestUtilities;
-
-namespace System.Tests
-{
- /// <summary>
- /// Allows creating <see cref="Utf8Span"/> instances that wrap <see cref="BoundedMemory{Byte}"/>.
- /// Useful for ensuring an API under test doesn't read past the end of the span.
- /// </summary>
- public sealed class BoundedUtf8Span : IDisposable
- {
- private readonly BoundedMemory<byte> _boundedMemory;
-
- public BoundedUtf8Span(string utf16Data, PoisonPagePlacement placement = PoisonPagePlacement.After)
- : this(utf16Data.AsSpan(), placement)
- {
- }
-
- public BoundedUtf8Span(ReadOnlySpan<char> utf16Data, PoisonPagePlacement placement = PoisonPagePlacement.After)
- : this(u8(utf16Data.ToString()).AsBytes(), placement)
- {
- }
-
- public BoundedUtf8Span(ReadOnlySpan<byte> utf8Data, PoisonPagePlacement placement = PoisonPagePlacement.After)
- {
- _boundedMemory = BoundedMemory.AllocateFromExistingData(utf8Data, placement);
- }
-
- public Utf8Span Span => Utf8Span.UnsafeCreateWithoutValidation(_boundedMemory.Span);
-
- public void Dispose()
- {
- _boundedMemory.Dispose();
- }
- }
-}
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System.Globalization;
-using Xunit;
-
-namespace System.Tests
-{
- [SkipOnMono("The features from System.Utf8String.Experimental namespace are experimental.")]
- public unsafe partial class Char8Tests
- {
- [Theory]
- [InlineData(10, 20, -1)]
- [InlineData(20, 10, 1)]
- [InlineData(30, 30, 0)]
- public static void CompareTo(int a, int b, int expectedSign)
- {
- Assert.Equal(expectedSign, Math.Sign(((Char8)a).CompareTo((Char8)b)));
- }
-
- [Theory]
- [InlineData(-1)]
- [InlineData(0xFF)]
- [InlineData(0x80)]
- [InlineData(0x00)]
- [InlineData(0x1234)]
- [InlineData(0x12345678)]
- [InlineData(0x1234567812345678)]
- public static void CastOperators(long value)
- {
- // Only the low byte is preserved when casting through Char8.
-
- Assert.Equal((byte)value, (byte)(Char8)(byte)value);
- Assert.Equal((sbyte)value, (sbyte)(Char8)(sbyte)value);
- Assert.Equal((char)(value & 0xFF), (char)(Char8)(char)value);
- Assert.Equal((short)(value & 0xFF), (short)(Char8)(short)value);
- Assert.Equal((ushort)(value & 0xFF), (ushort)(Char8)(ushort)value);
- Assert.Equal((int)(value & 0xFF), (int)(Char8)(int)value);
- Assert.Equal((uint)(value & 0xFF), (uint)(Char8)(uint)value);
- Assert.Equal((long)(value & 0xFF), (long)(Char8)(long)value);
- Assert.Equal((ulong)(value & 0xFF), (ulong)(Char8)(ulong)value);
- }
-
- [Fact]
- public static void EqualsObject()
- {
- Assert.False(((Char8)42).Equals((object)null));
- Assert.False(((Char8)42).Equals((object)(int)42));
- Assert.False(((Char8)42).Equals((object)(Char8)43));
- Assert.True(((Char8)42).Equals((object)(Char8)42));
- }
-
- [Fact]
- public static void EqualsChar8()
- {
- Assert.True(((Char8)42).Equals(42)); // implicit cast to Char8
- Assert.False(((Char8)42).Equals(43)); // implicit cast to Char8
- }
-
- [Fact]
- public static void GetHashCode_ReturnsValue()
- {
- for (int i = 0; i <= byte.MaxValue; i++)
- {
- Assert.Equal(i, ((Char8)i).GetHashCode());
- }
- }
-
- [Theory]
- [InlineData(10, 20, false)]
- [InlineData(20, 10, false)]
- [InlineData(30, 30, true)]
- public static void OperatorEquals(int a, int b, bool expected)
- {
- Assert.Equal(expected, (Char8)a == (Char8)b);
- Assert.NotEqual(expected, (Char8)a != (Char8)b);
- }
-
- [Theory]
- [InlineData(10, 20, true)]
- [InlineData(20, 10, false)]
- [InlineData(29, 30, true)]
- [InlineData(30, 30, false)]
- [InlineData(31, 30, false)]
- public static void OperatorLessThan(int a, int b, bool expected)
- {
- Assert.Equal(expected, (Char8)a < (Char8)b);
- Assert.NotEqual(expected, (Char8)a >= (Char8)b);
- }
-
- [Theory]
- [InlineData(10, 20, false)]
- [InlineData(20, 10, true)]
- [InlineData(29, 30, false)]
- [InlineData(30, 30, false)]
- [InlineData(31, 30, true)]
- public static void OperatorGreaterThan(int a, int b, bool expected)
- {
- Assert.Equal(expected, (Char8)a > (Char8)b);
- Assert.NotEqual(expected, (Char8)a <= (Char8)b);
- }
-
- [Fact]
- public static void ToString_ReturnsHexValue()
- {
- for (int i = 0; i <= byte.MaxValue; i++)
- {
- Assert.Equal(i.ToString("X2", CultureInfo.InvariantCulture), ((Char8)i).ToString());
- }
- }
- }
-}
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System.Buffers;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-using Xunit;
-
-using static System.Tests.Utf8TestUtilities;
-
-namespace System.Tests
-{
- [SkipOnMono("The features from System.Utf8String.Experimental namespace are experimental.")]
- public partial class MemoryTests
- {
- [Fact]
- public static unsafe void MemoryOfByte_WithUtf8String_Pin()
- {
- Utf8String theString = u8("Hello");
- ReadOnlyMemory<byte> rom = theString.AsMemoryBytes();
- MemoryHandle memHandle = default;
- try
- {
- memHandle = Unsafe.As<ReadOnlyMemory<byte>, Memory<byte>>(ref rom).Pin();
- Assert.True(memHandle.Pointer == Unsafe.AsPointer(ref Unsafe.AsRef(in theString.GetPinnableReference())));
- }
- finally
- {
- memHandle.Dispose();
- }
- }
-
- [Fact]
- public static void MemoryOfByte_WithUtf8String_ToString()
- {
- ReadOnlyMemory<byte> rom = u8("Hello").AsMemoryBytes();
- Assert.Equal("System.Memory<Byte>[5]", Unsafe.As<ReadOnlyMemory<byte>, Memory<byte>>(ref rom).ToString());
- }
-
- [Fact]
- public static unsafe void ReadOnlyMemoryOfByte_WithUtf8String_Pin()
- {
- Utf8String theString = u8("Hello");
- ReadOnlyMemory<byte> rom = theString.AsMemoryBytes();
- MemoryHandle memHandle = default;
- try
- {
- memHandle = rom.Pin();
- Assert.True(memHandle.Pointer == Unsafe.AsPointer(ref Unsafe.AsRef(in theString.GetPinnableReference())));
- }
- finally
- {
- memHandle.Dispose();
- }
- }
-
- [Fact]
- public static void ReadOnlyMemoryOfByte_WithUtf8String_ToString()
- {
- Assert.Equal("System.ReadOnlyMemory<Byte>[5]", u8("Hello").AsMemoryBytes().ToString());
- }
-
- [Fact]
- public static void ReadOnlySpanOfByte_ToString()
- {
- ReadOnlySpan<byte> span = stackalloc byte[] { (byte)'H', (byte)'i' };
- Assert.Equal("System.ReadOnlySpan<Byte>[2]", span.ToString());
- }
-
- [Fact]
- public static void SpanOfByte_ToString()
- {
- Span<byte> span = stackalloc byte[] { (byte)'H', (byte)'i' };
- Assert.Equal("System.Span<Byte>[2]", span.ToString());
- }
- }
-}
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System.Buffers;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-using Xunit;
-
-using static System.Tests.Utf8TestUtilities;
-
-namespace System.Tests
-{
- public partial class MemoryTests
- {
- [Fact]
- public static void MemoryMarshal_TryGetArrayOfByte_Utf8String()
- {
- ReadOnlyMemory<byte> rom = u8("Hello").AsMemoryBytes();
-
- Assert.False(MemoryMarshal.TryGetArray(rom, out ArraySegment<byte> segment));
- Assert.True(default(ArraySegment<byte>).Equals(segment));
- }
-
- [Fact]
- public static void MemoryMarshal_TryGetArrayOfChar8_Utf8String()
- {
- ReadOnlyMemory<Char8> rom = u8("Hello").AsMemory();
-
- Assert.False(MemoryMarshal.TryGetArray(rom, out ArraySegment<Char8> segment));
- Assert.True(default(ArraySegment<Char8>).Equals(segment));
- }
-
- [Fact]
- public static unsafe void MemoryOfChar8_WithUtf8String_Pin()
- {
- Utf8String theString = u8("Hello");
- ReadOnlyMemory<Char8> rom = theString.AsMemory();
- MemoryHandle memHandle = default;
- try
- {
- memHandle = Unsafe.As<ReadOnlyMemory<Char8>, Memory<Char8>>(ref rom).Pin();
- Assert.True(memHandle.Pointer == Unsafe.AsPointer(ref Unsafe.AsRef(in theString.GetPinnableReference())));
- }
- finally
- {
- memHandle.Dispose();
- }
- }
-
- [Fact]
- public static void MemoryOfChar8_WithUtf8String_ToString()
- {
- ReadOnlyMemory<Char8> rom = u8("Hello").AsMemory();
- Assert.Equal("Hello", Unsafe.As<ReadOnlyMemory<Char8>, Memory<Char8>>(ref rom).ToString());
- }
-
- [Fact]
- public static unsafe void ReadOnlyMemoryOfChar8_WithUtf8String_Pin()
- {
- Utf8String theString = u8("Hello");
- ReadOnlyMemory<Char8> rom = theString.AsMemory();
- MemoryHandle memHandle = default;
- try
- {
- memHandle = rom.Pin();
- Assert.True(memHandle.Pointer == Unsafe.AsPointer(ref Unsafe.AsRef(in theString.GetPinnableReference())));
- }
- finally
- {
- memHandle.Dispose();
- }
- }
-
- [Fact]
- public static void ReadOnlyMemoryOfChar8_WithUtf8String_ToString()
- {
- Assert.Equal("Hello", u8("Hello").AsMemory().ToString());
- }
-
- [Fact]
- public static void ReadOnlySpanOfChar8_ToString()
- {
- ReadOnlySpan<Char8> span = stackalloc Char8[] { (Char8)'H', (Char8)'i' };
- Assert.Equal("Hi", span.ToString());
- }
-
- [Fact]
- public static void SpanOfChar8_ToString()
- {
- Span<Char8> span = stackalloc Char8[] { (Char8)'H', (Char8)'i' };
- Assert.Equal("Hi", span.ToString());
- }
- }
-}
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System.Runtime.InteropServices;
-using Xunit;
-
-using static System.Tests.Utf8TestUtilities;
-
-namespace System.Tests
-{
- public partial class MemoryTests
- {
- [Fact]
- public static void MemoryMarshal_TryGetArrayOfByte_Utf8String()
- {
- ReadOnlyMemory<byte> rom = u8("Hello").AsMemoryBytes();
-
- Assert.True(MemoryMarshal.TryGetArray(rom, out ArraySegment<byte> segment));
- Assert.NotNull(segment.Array);
- Assert.Equal(0, segment.Offset);
- Assert.Equal(5, segment.Count);
- }
-
- [Fact]
- public static void ReadOnlySpanOfChar8_ToString()
- {
- // unable to override ReadOnlySpan.ToString on netfx
-
- ReadOnlySpan<Char8> span = stackalloc Char8[] { (Char8)'H', (Char8)'i' };
- Assert.Equal("System.ReadOnlySpan<Char8>[2]", span.ToString());
- }
-
- [Fact]
- public static void SpanOfChar8_ToString()
- {
- // unable to override Span.ToString on netfx
-
- Span<Char8> span = stackalloc Char8[] { (Char8)'H', (Char8)'i' };
- Assert.Equal("System.Span<Char8>[2]", span.ToString());
- }
- }
-}
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System.IO;
-using System.Text;
-using System.Threading.Tasks;
-using Xunit;
-
-using static System.Tests.Utf8TestUtilities;
-
-namespace System.Net.Http.Tests
-{
- [SkipOnMono("The features from System.Utf8String.Experimental namespace are experimental.")]
- public partial class Utf8StringContentTests
- {
- [Fact]
- public static void Ctor_NullContent_Throws()
- {
- Assert.Throws<ArgumentNullException>("content", () => new Utf8StringContent(null));
- Assert.Throws<ArgumentNullException>("content", () => new Utf8StringContent(null, "application/json"));
- }
-
- [Theory]
- [InlineData(null, "text/plain")]
- [InlineData("application/json", "application/json")]
- public static void Ctor_SetsContentTypeHeader(string mediaTypeForCtor, string expectedMediaType)
- {
- HttpContent httpContent = new Utf8StringContent(u8("Hello"), mediaTypeForCtor);
-
- Assert.Equal(expectedMediaType, httpContent.Headers.ContentType.MediaType);
- Assert.Equal(Encoding.UTF8.WebName, httpContent.Headers.ContentType.CharSet);
- }
-
- [Fact]
- public static async Task Ctor_CopyToAsync_GetStream()
- {
- MemoryStream memoryStream = new MemoryStream();
-
- await new Utf8StringContent(u8("Hello")).CopyToAsync(memoryStream);
-
- Assert.Equal(u8("Hello").ToByteArray(), memoryStream.ToArray());
- }
- }
-}
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System.IO;
-using System.Text;
-using System.Threading.Tasks;
-using Xunit;
-
-using static System.Tests.Utf8TestUtilities;
-
-namespace System.Net.Http.Tests
-{
- public partial class Utf8StringContentTests
- {
- [Fact]
- public static void Ctor_CopyTo_GetStream()
- {
- var memoryStream = new MemoryStream();
-
- new Utf8StringContent(u8("Hello")).CopyTo(memoryStream, default, default);
-
- Assert.Equal(u8("Hello").ToByteArray(), memoryStream.ToArray());
- }
-
- [Fact]
- public static void Ctor_ReadAsStream()
- {
- var content = new Utf8StringContent(u8("Hello"));
- Stream stream = content.ReadAsStream();
-
- var memoryStream = new MemoryStream();
- stream.CopyTo(memoryStream);
- memoryStream.Seek(0, SeekOrigin.Begin);
- Assert.Equal(u8("Hello").ToByteArray(), memoryStream.ToArray());
- }
- }
-}
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System.Collections.Generic;
-using Xunit;
-
-namespace System.Tests
-{
- /// <summary>
- /// Given a fixed length, compares two <see cref="Range"/> instances for equality.
- /// </summary>
- public sealed class RangeEqualityComparer : IEqualityComparer<Range>
- {
- private int _length;
-
- public RangeEqualityComparer(int length)
- {
- Assert.True(length >= 0);
-
- _length = length;
- }
-
- public bool Equals(Range x, Range y)
- {
- (int offsetX, int lengthX) = x.GetOffsetAndLength(_length);
- (int offsetY, int lengthY) = y.GetOffsetAndLength(_length);
-
- return offsetX == offsetY && lengthX == lengthY;
- }
-
- public int GetHashCode(Range obj)
- {
- (int offset, int length) = obj.GetOffsetAndLength(_length);
-#if NETCOREAPP
- return HashCode.Combine(offset, length);
-#else
- return Tuple.Create(offset, length).GetHashCode();
-#endif
- }
- }
-}
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System.Runtime.Serialization;
-using Xunit;
-
-using static System.Tests.Utf8TestUtilities;
-
-namespace System.Tests
-{
- [SkipOnMono("The features from System.Utf8String.Experimental namespace are experimental.")]
- public partial class ReflectionTests
- {
- [Fact]
- public static void ActivatorCreateInstance_CanCallParameterfulCtor()
- {
- Utf8String theString = (Utf8String)Activator.CreateInstance(typeof(Utf8String), "Hello");
- Assert.Equal(u8("Hello"), theString);
- }
-
- [Fact]
- public static void ActivatorCreateInstance_CannotCallParameterlessCtor()
- {
- Assert.Throws<MissingMethodException>(() => Activator.CreateInstance(typeof(Utf8String)));
- }
- }
-}
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System.Runtime.Serialization;
-using Xunit;
-
-namespace System.Tests
-{
- public partial class ReflectionTests
- {
- [Fact]
- public static void FormatterServices_GetUninitializedObject_Throws()
- {
- // Like String, shouldn't be able to create an uninitialized Utf8String.
-
- Assert.Throws<ArgumentException>(() => FormatterServices.GetSafeUninitializedObject(typeof(Utf8String)));
- Assert.Throws<ArgumentException>(() => FormatterServices.GetUninitializedObject(typeof(Utf8String)));
- }
- }
-}
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-using Xunit;
-
-using static System.Tests.Utf8TestUtilities;
-
-namespace System.Tests
-{
- [SkipOnMono("The features from System.Utf8String.Experimental namespace are experimental.")]
- public partial class Utf8ExtensionsTests
- {
- [Fact]
- public unsafe void AsBytes_FromSpan_Default()
- {
- // First, a default span should become a default span.
-
- Assert.True(default(ReadOnlySpan<byte>) == new ReadOnlySpan<Char8>().AsBytes());
-
- // Next, an empty but non-default span should become an empty but non-default span.
-
- Assert.True(new ReadOnlySpan<byte>((void*)0x12345, 0) == new ReadOnlySpan<Char8>((void*)0x12345, 0).AsBytes());
- }
-
- [Fact]
- public void AsBytes_FromUtf8String()
- {
- Assert.True(default(ReadOnlySpan<byte>) == ((Utf8String)null).AsBytes());
- }
-
- [Fact]
- public void AsBytes_FromUtf8String_WithStart()
- {
- Assert.True(default(ReadOnlySpan<byte>) == ((Utf8String)null).AsBytes(0));
- Assert.True(u8("Hello").AsBytes(5).IsEmpty);
-
- SpanAssert.Equal(new byte[] { (byte)'e', (byte)'l', (byte)'l', (byte)'o' }, u8("Hello").AsBytes(1));
- }
-
- [Fact]
- public void AsBytes_FromUtf8String_WithStart_ArgOutOfRange()
- {
- Assert.Throws<ArgumentOutOfRangeException>("start", () => ((Utf8String)null).AsBytes(1));
- Assert.Throws<ArgumentOutOfRangeException>("start", () => u8("Hello").AsBytes(-1));
- Assert.Throws<ArgumentOutOfRangeException>("start", () => u8("Hello").AsBytes(6));
- }
-
- [Fact]
- public void AsBytes_FromUtf8String_WithStartAndLength()
- {
- Assert.True(default(ReadOnlySpan<byte>) == ((Utf8String)null).AsBytes(0, 0));
- Assert.True(u8("Hello").AsBytes(5, 0).IsEmpty);
-
- SpanAssert.Equal(new byte[] { (byte)'e', (byte)'l', (byte)'l' }, u8("Hello").AsBytes(1, 3));
- }
-
- [Fact]
- public void AsBytes_FromUtf8String_WithStartAndLength_ArgOutOfRange()
- {
- Assert.Throws<ArgumentOutOfRangeException>("start", () => ((Utf8String)null).AsBytes(0, 1));
- Assert.Throws<ArgumentOutOfRangeException>("start", () => ((Utf8String)null).AsBytes(1, 0));
- Assert.Throws<ArgumentOutOfRangeException>("start", () => u8("Hello").AsBytes(5, 1));
- Assert.Throws<ArgumentOutOfRangeException>("start", () => u8("Hello").AsBytes(4, -2));
- }
-
- [Fact]
- public unsafe void AsMemoryBytes_FromUtf8String()
- {
- Assert.True(default(ReadOnlyMemory<byte>).Equals(((Utf8String)null).AsMemoryBytes()));
-
- Utf8String theString = u8("Hello");
- fixed (byte* pTheString = theString)
- {
- fixed (byte* pTheStringAsMemoryBytes = theString.AsMemoryBytes().Span)
- {
- Assert.True(pTheString == pTheStringAsMemoryBytes);
- }
- }
- }
-
- [Fact]
- public void AsMemoryBytes_FromUtf8String_WithStart()
- {
- Assert.True(default(ReadOnlyMemory<byte>).Equals(((Utf8String)null).AsMemoryBytes(0)));
- Assert.True(u8("Hello").AsMemoryBytes(5).IsEmpty);
-
- SpanAssert.Equal(new byte[] { (byte)'e', (byte)'l', (byte)'l', (byte)'o' }, u8("Hello").AsMemoryBytes(1).Span);
- }
-
- [Fact]
- public void AsMemoryBytes_FromUtf8String_WithStart_ArgOutOfRange()
- {
- Assert.Throws<ArgumentOutOfRangeException>("start", () => ((Utf8String)null).AsMemoryBytes(1));
- Assert.Throws<ArgumentOutOfRangeException>("start", () => u8("Hello").AsMemoryBytes(-1));
- Assert.Throws<ArgumentOutOfRangeException>("start", () => u8("Hello").AsMemoryBytes(6));
- }
-
- [Fact]
- public void AsMemoryBytes_FromUtf8String_WithStartAndLength()
- {
- Assert.True(default(ReadOnlyMemory<byte>).Equals(((Utf8String)null).AsMemoryBytes(0, 0)));
- Assert.True(u8("Hello").AsMemoryBytes(5, 0).IsEmpty);
-
- SpanAssert.Equal(new byte[] { (byte)'e', (byte)'l', (byte)'l' }, u8("Hello").AsMemoryBytes(1, 3).Span);
- }
-
- [Fact]
- public void AsMemoryBytes_FromUtf8String_WithStartAndLength_ArgOutOfRange()
- {
- Assert.Throws<ArgumentOutOfRangeException>("start", () => ((Utf8String)null).AsMemoryBytes(0, 1));
- Assert.Throws<ArgumentOutOfRangeException>("start", () => ((Utf8String)null).AsMemoryBytes(1, 0));
- Assert.Throws<ArgumentOutOfRangeException>("start", () => u8("Hello").AsMemoryBytes(5, 1));
- Assert.Throws<ArgumentOutOfRangeException>("start", () => u8("Hello").AsMemoryBytes(4, -2));
- }
-
- [Fact]
- public void AsSpan_FromUtf8String()
- {
- Assert.True(((Utf8String)null).AsSpan().Bytes == default); // referential equality check
-
- Utf8String theString = u8("Hello");
-
- Assert.True(Unsafe.AreSame(ref Unsafe.AsRef(in theString.GetPinnableReference()), ref Unsafe.AsRef(in theString.AsSpan().GetPinnableReference())));
- Assert.Equal(5, theString.AsSpan().Length);
- }
-
- [Fact]
- public void AsSpan_FromUtf8String_WithStart()
- {
- Assert.True(((Utf8String)null).AsSpan(0).Bytes == default); // referential equality check
- Assert.True(u8("Hello").AsSpan(5).IsEmpty);
- Assert.Equal(new byte[] { (byte)'e', (byte)'l', (byte)'l', (byte)'o' }, u8("Hello").AsSpan(1).Bytes.ToArray());
- }
-
- [Fact]
- public void AsSpan_FromUtf8String_WithStart_ArgOutOfRange()
- {
- Assert.Throws<ArgumentOutOfRangeException>("start", () => ((Utf8String)null).AsSpan(1));
- Assert.Throws<ArgumentOutOfRangeException>("start", () => u8("Hello").AsSpan(-1));
- Assert.Throws<ArgumentOutOfRangeException>("start", () => u8("Hello").AsSpan(6));
- }
-
- [Fact]
- public void AsSpan_FromUtf8String_WithStartAndLength()
- {
- Assert.True(((Utf8String)null).AsSpan(0, 0).Bytes == default); // referential equality check
- Assert.True(u8("Hello").AsSpan(5, 0).IsEmpty);
- Assert.Equal(new byte[] { (byte)'e', (byte)'l', (byte)'l' }, u8("Hello").AsSpan(1, 3).Bytes.ToArray());
- }
-
- [Fact]
- public void AsSpan_FromUtf8String_WithStartAndLength_ArgOutOfRange()
- {
- Assert.Throws<ArgumentOutOfRangeException>("start", () => ((Utf8String)null).AsSpan(0, 1));
- Assert.Throws<ArgumentOutOfRangeException>("start", () => ((Utf8String)null).AsSpan(1, 0));
- Assert.Throws<ArgumentOutOfRangeException>("start", () => u8("Hello").AsSpan(5, 1));
- Assert.Throws<ArgumentOutOfRangeException>("start", () => u8("Hello").AsSpan(4, -2));
- }
- }
-}
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-using Xunit;
-
-using static System.Tests.Utf8TestUtilities;
-
-namespace System.Tests
-{
- public partial class Utf8ExtensionsTests
- {
- [Fact]
- public void AsBytes_FromSpan_Default_netcoreapp()
- {
- // a span wrapping data should become a span wrapping that same data.
-
- Utf8String theString = u8("Hello");
-
- Assert.True(MemoryMarshal.CreateReadOnlySpan(ref Unsafe.AsRef(in theString.GetPinnableReference()), 5) == (theString.AsMemory().Span).AsBytes());
- }
-
- [Fact]
- public void AsBytes_FromUtf8String_netcoreapp()
- {
- Utf8String theString = u8("Hello");
- Assert.True(MemoryMarshal.CreateReadOnlySpan(ref Unsafe.AsRef(in theString.GetPinnableReference()), 5) == theString.AsBytes());
- }
-
- [Fact]
- public void AsMemory_FromUtf8String()
- {
- Assert.True(default(ReadOnlyMemory<Char8>).Equals(((Utf8String)null).AsMemory()));
-
- Utf8String theString = u8("Hello");
- Assert.True(MemoryMarshal.CreateReadOnlySpan(ref Unsafe.As<byte, Char8>(ref Unsafe.AsRef(in theString.GetPinnableReference())), 5) == theString.AsMemory().Span);
- }
-
- [Fact]
- public void AsMemory_FromUtf8String_WithStart()
- {
- Assert.True(default(ReadOnlyMemory<Char8>).Equals(((Utf8String)null).AsMemory(0)));
- Assert.True(u8("Hello").AsMemory(5).IsEmpty);
-
- SpanAssert.Equal(new Char8[] { (Char8)'e', (Char8)'l', (Char8)'l', (Char8)'o' }, u8("Hello").AsMemory(1).Span);
- }
-
- [Fact]
- public void AsMemory_FromUtf8String_WithStart_ArgOutOfRange()
- {
- Assert.Throws<ArgumentOutOfRangeException>("start", () => ((Utf8String)null).AsMemory(1));
- Assert.Throws<ArgumentOutOfRangeException>("start", () => u8("Hello").AsMemory(-1));
- Assert.Throws<ArgumentOutOfRangeException>("start", () => u8("Hello").AsMemory(6));
- }
-
- [Fact]
- public void AsMemory_FromUtf8String_WithStartAndLength()
- {
- Assert.True(default(ReadOnlyMemory<Char8>).Equals(((Utf8String)null).AsMemory(0, 0)));
- Assert.True(u8("Hello").AsMemory(5, 0).IsEmpty);
-
- SpanAssert.Equal(new Char8[] { (Char8)'e', (Char8)'l', (Char8)'l' }, u8("Hello").AsMemory(1, 3).Span);
- }
-
- [Fact]
- public void AsMemory_FromUtf8String_WithStartAndLength_ArgOutOfRange()
- {
- Assert.Throws<ArgumentOutOfRangeException>("start", () => ((Utf8String)null).AsMemory(0, 1));
- Assert.Throws<ArgumentOutOfRangeException>("start", () => ((Utf8String)null).AsMemory(1, 0));
- Assert.Throws<ArgumentOutOfRangeException>("start", () => u8("Hello").AsMemory(5, 1));
- Assert.Throws<ArgumentOutOfRangeException>("start", () => u8("Hello").AsMemory(4, -2));
- }
- }
-}
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System.Globalization;
-using System.Tests;
-using Microsoft.DotNet.RemoteExecutor;
-using Xunit;
-
-using static System.Tests.Utf8TestUtilities;
-
-namespace System.Text.Tests
-{
- public partial class Utf8SpanTests
- {
- [Fact]
- public static void Equals_Object_ThrowsNotSupported()
- {
- Utf8Span span = Utf8Span.Empty;
-
- Assert.Throws<NotSupportedException>(() => Utf8Span.Empty.Equals((object)null));
- Assert.Throws<NotSupportedException>(() => Utf8Span.Empty.Equals(new object()));
- }
-
- [Fact]
- public static void Equals_Ordinal()
- {
- // First make sure referential equality passes
-
- Utf8Span span1 = u8("Hello!");
- Utf8Span span2 = span1;
- AssertEqualOrdinal(span1, span2);
-
- // Now make sure deep equality passes
-
- span2 = Utf8Span.UnsafeCreateWithoutValidation(Encoding.UTF8.GetBytes("Hello!"));
- AssertEqualOrdinal(span1, span2);
-
- // Now mutate one of the inputs and make sure they're inequal
-
- span2 = u8("Bello!");
- AssertNotEqualOrdinal(span1, span2);
-
- // Finally, make sure null / null and null / empty are treated as the same
-
- AssertEqualOrdinal(Utf8Span.Empty, Utf8Span.Empty);
- AssertEqualOrdinal(Utf8Span.Empty, u8(""));
- }
-
- [PlatformSpecific(TestPlatforms.Windows)]
- [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNlsGlobalization))]
- [InlineData(null, null, StringComparison.OrdinalIgnoreCase, null, true)]
- [InlineData("encyclopaedia", "encyclopædia", StringComparison.OrdinalIgnoreCase, null, false)]
- [InlineData("encyclopaedia", "encyclopædia", StringComparison.InvariantCulture, null, true)]
- [InlineData("encyclopaedia", "ENCYCLOPÆDIA", StringComparison.InvariantCulture, null, false)]
- [InlineData("encyclopaedia", "encyclopædia", StringComparison.InvariantCultureIgnoreCase, null, true)]
- [InlineData("encyclopaedia", "ENCYCLOPÆDIA", StringComparison.InvariantCultureIgnoreCase, null, true)]
- [InlineData("Weiß", "WEISS", StringComparison.OrdinalIgnoreCase, null, false)]
- [InlineData("Weiß", "WEISS", StringComparison.InvariantCulture, null, false)]
- [InlineData("Weiß", "WEISS", StringComparison.InvariantCultureIgnoreCase, null, true)]
- [InlineData("Weiß", "WEISS", StringComparison.CurrentCulture, "de-DE", false)]
- [InlineData("Weiß", "WEISS", StringComparison.CurrentCultureIgnoreCase, "de-DE", true)]
- [InlineData("γένεσις", "ΓΈΝΕΣΙΣ", StringComparison.InvariantCultureIgnoreCase, null, true)]
- [InlineData("ıI", "iI", StringComparison.CurrentCulture, "tr-TR", false)]
- [InlineData("ıI", "iI", StringComparison.CurrentCultureIgnoreCase, "tr-TR", false)]
- [InlineData("İI", "iI", StringComparison.CurrentCultureIgnoreCase, "tr-TR", true)]
- public static void Equals_NonOrdinal(string str1, string str2, StringComparison comparison, string culture, bool shouldCompareAsEqual)
- {
- using (new ThreadCultureChange(culture))
- {
- using BoundedUtf8Span boundedSpan1 = new BoundedUtf8Span(str1);
- using BoundedUtf8Span boundedSpan2 = new BoundedUtf8Span(str2);
-
- Utf8Span span1 = boundedSpan1.Span;
- Utf8Span span2 = boundedSpan2.Span;
-
- Assert.Equal(shouldCompareAsEqual, span1.Equals(span2, comparison));
- Assert.Equal(shouldCompareAsEqual, span2.Equals(span1, comparison));
- Assert.Equal(shouldCompareAsEqual, Utf8Span.Equals(span1, span2, comparison));
- Assert.Equal(shouldCompareAsEqual, Utf8Span.Equals(span2, span1, comparison));
- }
- }
-
- private static void AssertEqualOrdinal(Utf8Span span1, Utf8Span span2)
- {
- Assert.True(span1.Equals(span2));
- Assert.True(span2.Equals(span1));
-
- Assert.True(span1.Equals(span2, StringComparison.Ordinal));
- Assert.True(span2.Equals(span1, StringComparison.Ordinal));
-
- Assert.True(Utf8Span.Equals(span1, span2));
- Assert.True(Utf8Span.Equals(span2, span1));
-
- Assert.True(Utf8Span.Equals(span1, span2, StringComparison.Ordinal));
- Assert.True(Utf8Span.Equals(span2, span1, StringComparison.Ordinal));
-
- Assert.True(span1 == span2);
- Assert.True(span2 == span1);
-
- Assert.False(span1 != span2);
- Assert.False(span2 != span1);
- }
-
- private static void AssertNotEqualOrdinal(Utf8Span span1, Utf8Span span2)
- {
- Assert.False(span1.Equals(span2));
- Assert.False(span2.Equals(span1));
-
- Assert.False(span1.Equals(span2, StringComparison.Ordinal));
- Assert.False(span2.Equals(span1, StringComparison.Ordinal));
-
- Assert.False(Utf8Span.Equals(span1, span2));
- Assert.False(Utf8Span.Equals(span2, span1));
-
- Assert.False(Utf8Span.Equals(span1, span2, StringComparison.Ordinal));
- Assert.False(Utf8Span.Equals(span2, span1, StringComparison.Ordinal));
-
- Assert.False(span1 == span2);
- Assert.False(span2 == span1);
-
- Assert.True(span1 != span2);
- Assert.True(span2 != span1);
- }
- }
-}
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System.Collections.Generic;
-using System.Globalization;
-using System.Tests;
-using Xunit;
-
-using static System.Tests.Utf8TestUtilities;
-
-using ustring = System.Utf8String;
-
-namespace System.Text.Tests
-{
- public partial class Utf8SpanTests
- {
- [Theory]
- [MemberData(nameof(NormalizationData))]
- public static void Normalize(string utf16Source, string utf16Expected, NormalizationForm normalizationForm)
- {
- using BoundedUtf8Span boundedSpan = new BoundedUtf8Span(utf16Source);
- Utf8Span utf8Source = boundedSpan.Span;
-
- // Quick IsNormalized tests
-
- Assert.Equal(utf16Source == utf16Expected, utf8Source.IsNormalized(normalizationForm));
-
- // Normalize and return new Utf8String instances
-
- ustring utf8Normalized = utf8Source.Normalize(normalizationForm);
- Assert.True(ustring.AreEquivalent(utf8Normalized, utf16Expected));
-
- // Normalize to byte arrays which are too small, expect -1 (failure)
-
- Assert.Equal(-1, utf8Source.Normalize(new byte[utf8Normalized.Length - 1], normalizationForm));
-
- // Normalize to byte arrays which are the correct length, expect success,
- // then compare buffer contents for ordinal equality.
-
- foreach (int bufferLength in new int[] { utf8Normalized.Length /* just right */, utf8Normalized.Length + 1 /* with extra room */})
- {
- byte[] dest = new byte[bufferLength];
- Assert.Equal(utf8Normalized.Length, utf8Source.Normalize(dest, normalizationForm));
- Utf8Span normalizedSpan = Utf8Span.UnsafeCreateWithoutValidation(dest.AsSpan().Slice(0, utf8Normalized.Length));
- Assert.True(utf8Normalized.AsSpan() == normalizedSpan); // ordinal equality
- Assert.True(normalizedSpan.IsNormalized(normalizationForm));
- }
- }
-
- [Theory]
- [MemberData(nameof(CaseConversionData))]
- public static void ToLower(string testData)
- {
- static void RunTest(string testData, string expected, CultureInfo culture)
- {
- using BoundedUtf8Span boundedSpan = new BoundedUtf8Span(testData);
- Utf8Span inputSpan = boundedSpan.Span;
-
- // First try the allocating APIs
-
- ustring expectedUtf8 = u8(expected) ?? ustring.Empty;
- ustring actualUtf8;
-
- if (culture is null)
- {
- actualUtf8 = inputSpan.ToLowerInvariant();
- }
- else
- {
- actualUtf8 = inputSpan.ToLower(culture);
- }
-
- Assert.Equal(expectedUtf8, actualUtf8);
-
- // Next, try the non-allocating APIs with too small a buffer
-
- if (expectedUtf8.Length > 0)
- {
- byte[] bufferTooSmall = new byte[expectedUtf8.Length - 1];
-
- if (culture is null)
- {
- Assert.Equal(-1, inputSpan.ToLowerInvariant(bufferTooSmall));
- }
- else
- {
- Assert.Equal(-1, inputSpan.ToLower(bufferTooSmall, culture));
- }
- }
-
- // Then the non-allocating APIs with a properly sized buffer
-
- foreach (int bufferSize in new[] { expectedUtf8.Length, expectedUtf8.Length + 1 })
- {
- byte[] buffer = new byte[expectedUtf8.Length];
-
- if (culture is null)
- {
- Assert.Equal(expectedUtf8.Length, inputSpan.ToLowerInvariant(buffer));
- }
- else
- {
- Assert.Equal(expectedUtf8.Length, inputSpan.ToLower(buffer, culture));
- }
-
- Assert.True(expectedUtf8.AsBytes().SequenceEqual(buffer));
- }
- }
-
- RunTest(testData, testData?.ToLowerInvariant(), null);
- RunTest(testData, testData?.ToLower(CultureInfo.InvariantCulture), CultureInfo.InvariantCulture);
- RunTest(testData, testData?.ToLower(CultureInfo.GetCultureInfo("en-US")), CultureInfo.GetCultureInfo("en-US"));
- RunTest(testData, testData?.ToLower(CultureInfo.GetCultureInfo("tr-TR")), CultureInfo.GetCultureInfo("tr-TR"));
- }
-
- [Theory]
- [MemberData(nameof(CaseConversionData))]
- public static void ToUpper(string testData)
- {
- static void RunTest(string testData, string expected, CultureInfo culture)
- {
- using BoundedUtf8Span boundedSpan = new BoundedUtf8Span(testData);
- Utf8Span inputSpan = boundedSpan.Span;
-
- // First try the allocating APIs
-
- ustring expectedUtf8 = u8(expected) ?? ustring.Empty;
- ustring actualUtf8;
-
- if (culture is null)
- {
- actualUtf8 = inputSpan.ToUpperInvariant();
- }
- else
- {
- actualUtf8 = inputSpan.ToUpper(culture);
- }
-
- Assert.Equal(expectedUtf8, actualUtf8);
-
- // Next, try the non-allocating APIs with too small a buffer
-
- if (expectedUtf8.Length > 0)
- {
- byte[] bufferTooSmall = new byte[expectedUtf8.Length - 1];
-
- if (culture is null)
- {
- Assert.Equal(-1, inputSpan.ToUpperInvariant(bufferTooSmall));
- }
- else
- {
- Assert.Equal(-1, inputSpan.ToUpper(bufferTooSmall, culture));
- }
- }
-
- // Then the non-allocating APIs with a properly sized buffer
-
- foreach (int bufferSize in new[] { expectedUtf8.Length, expectedUtf8.Length + 1 })
- {
- byte[] buffer = new byte[bufferSize];
-
- if (culture is null)
- {
- Assert.Equal(expectedUtf8.Length, inputSpan.ToUpperInvariant(buffer));
- }
- else
- {
- Assert.Equal(expectedUtf8.Length, inputSpan.ToUpper(buffer, culture));
- }
-
- Assert.True(expectedUtf8.AsBytes().SequenceEqual(buffer.AsSpan(0, expectedUtf8.Length)));
- }
- }
-
- RunTest(testData, testData?.ToUpperInvariant(), null);
- RunTest(testData, testData?.ToUpper(CultureInfo.InvariantCulture), CultureInfo.InvariantCulture);
- RunTest(testData, testData?.ToUpper(CultureInfo.GetCultureInfo("en-US")), CultureInfo.GetCultureInfo("en-US"));
- RunTest(testData, testData?.ToUpper(CultureInfo.GetCultureInfo("tr-TR")), CultureInfo.GetCultureInfo("tr-TR"));
- }
-
- public static IEnumerable<object[]> CaseConversionData()
- {
- string[] testCases = new string[]
- {
- null,
- string.Empty,
- "Hello",
- "iı", // dotted and dotless I
- "İI", // dotted and dotless I
- };
-
- foreach (string testCase in testCases)
- {
- yield return new object[] { testCase };
- }
- }
-
- public static IEnumerable<object[]> NormalizationData()
- {
- // These test cases are from the Unicode Standard Annex #15, Figure 6
- // https://unicode.org/reports/tr15/
-
- var testCases = new[]
- {
- new
- {
- Source = "\ufb01", // "fi" (LATIN SMALL LIGATURE FI)
- NFD = "\ufb01", // same as source
- NFC = "\ufb01", // same as source
- NFKD = "fi", // compatibility decomposed into ASCII chars
- NFKC = "fi", // compatibility decomposed into ASCII chars
- },
- new
- {
- Source = "2\u2075", // "2⁵" (SUPERSCRIPT FIVE)
- NFD = "2\u2075", // same as source
- NFC = "2\u2075", // same as source
- NFKD = "25", // compatibility decomposed into ASCII chars
- NFKC = "25", // compatibility decomposed into ASCII chars
- },
- new
- {
- Source = "\u1e9b\u0323", // 'ẛ' (LATIN SMALL LETTER LONG S WITH DOT ABOVE) + COMBINING DOT BELOW
- NFD = "\u017f\u0323\u0307", // 'ſ' (LATIN SMALL LETTER LONG S) + COMBINING DOT BELOW + COMBINING DOT ABOVE
- NFC = "\u1e9b\u0323", // same as source
- NFKD = "s\u0323\u0307", // ASCII 's' + COMBINING DOT BELOW + COMBINING DOT ABOVE
- NFKC = "\u1e69", // "ṩ" (LATIN SMALL LETTER S WITH DOT BELOW AND DOT ABOVE)
- }
- };
-
- foreach (var testCase in testCases)
- {
- yield return new object[] { testCase.Source, testCase.NFD, NormalizationForm.FormD };
- yield return new object[] { testCase.Source, testCase.NFC, NormalizationForm.FormC };
- yield return new object[] { testCase.Source, testCase.NFKD, NormalizationForm.FormKD };
- yield return new object[] { testCase.Source, testCase.NFKC, NormalizationForm.FormKC };
- }
- }
- }
-}
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-using Xunit;
-
-using static System.Tests.Utf8TestUtilities;
-
-using ustring = System.Utf8String;
-
-namespace System.Text.Tests
-{
- public unsafe partial class Utf8SpanTests
- {
- [Fact]
- public static void Ctor_EmptyUtf8String()
- {
- // Arrange
-
- ustring str = ustring.Empty;
-
- // Act
-
- Utf8Span span = new Utf8Span(str);
-
- // Assert
- // GetPinnableReference should be 'null' to match behavior of empty ROS<byte>.GetPinnableReference();
-
- Assert.True(span.IsEmpty);
- Assert.Equal(IntPtr.Zero, (IntPtr)(void*)Unsafe.AsPointer(ref Unsafe.AsRef(in span.GetPinnableReference())));
- Assert.True(Unsafe.AreSame(ref Unsafe.AsRef(in str.GetPinnableReference()), ref MemoryMarshal.GetReference(span.Bytes)));
- Assert.Equal(0, span.Length);
- }
-
- [Fact]
- public static void Ctor_NonEmptyUtf8String()
- {
- // Arrange
-
- ustring str = u8("Hello!");
-
- // Act
-
- Utf8Span span = new Utf8Span(str);
-
- // Assert
-
- Assert.False(span.IsEmpty);
- Assert.True(Unsafe.AreSame(ref Unsafe.AsRef(in str.GetPinnableReference()), ref Unsafe.AsRef(in span.GetPinnableReference())));
- Assert.True(Unsafe.AreSame(ref Unsafe.AsRef(in str.GetPinnableReference()), ref MemoryMarshal.GetReference(span.Bytes)));
- Assert.Equal(6, span.Length);
- }
-
- [Fact]
- public static void Ctor_NullUtf8String()
- {
- // Arrange
-
- ustring str = null;
-
- // Act
-
- Utf8Span span = new Utf8Span(str);
-
- // Assert
- // GetPinnableReference should be 'null' to match behavior of empty ROS<byte>.GetPinnableReference();
-
- Assert.True(span.IsEmpty);
- Assert.Equal(IntPtr.Zero, (IntPtr)(void*)Unsafe.AsPointer(ref Unsafe.AsRef(in span.GetPinnableReference())));
- Assert.Equal(IntPtr.Zero, (IntPtr)(void*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(span.Bytes)));
- Assert.Equal(0, span.Length);
- }
-
- [Fact]
- public static void Ctor_UnsafeFromByteSpan_NonEmpty()
- {
- // Arrange
-
- ReadOnlySpan<byte> original = new byte[] { 1, 2, 3, 4, 5 };
-
- // Act
-
- Utf8Span span = Utf8Span.UnsafeCreateWithoutValidation(original);
-
- // Assert
-
- Assert.False(span.IsEmpty);
- Assert.True(Unsafe.AreSame(ref Unsafe.AsRef(in original.GetPinnableReference()), ref Unsafe.AsRef(in span.GetPinnableReference())));
- Assert.True(Unsafe.AreSame(ref Unsafe.AsRef(in original.GetPinnableReference()), ref MemoryMarshal.GetReference(span.Bytes)));
- Assert.Equal(5, span.Length);
- }
-
- [Fact]
- public static void Ctor_UnsafeFromByteSpan_NonNullEmptyArray()
- {
- // Arrange
-
- ReadOnlySpan<byte> original = new byte[0];
-
- // Act
-
- Utf8Span span = Utf8Span.UnsafeCreateWithoutValidation(original);
-
- // Assert
- // GetPinnableReference should be 'null' to match behavior of empty ROS<byte>.GetPinnableReference();
-
- Assert.True(span.IsEmpty);
- Assert.Equal(IntPtr.Zero, (IntPtr)(void*)Unsafe.AsPointer(ref Unsafe.AsRef(in span.GetPinnableReference())));
- Assert.True(Unsafe.AreSame(ref MemoryMarshal.GetReference(original), ref MemoryMarshal.GetReference(span.Bytes)));
- Assert.Equal(0, span.Length);
- }
-
- [Fact]
- public static void Ctor_UnsafeFromByteSpan_Default()
- {
- // Arrange
-
- ReadOnlySpan<byte> original = default;
-
- // Act
-
- Utf8Span span = Utf8Span.UnsafeCreateWithoutValidation(original);
-
- // Assert
- // GetPinnableReference should be 'null' to match behavior of empty ROS<byte>.GetPinnableReference();
-
- Assert.True(span.IsEmpty);
- Assert.Equal(IntPtr.Zero, (IntPtr)(void*)Unsafe.AsPointer(ref Unsafe.AsRef(in span.GetPinnableReference())));
- Assert.Equal(IntPtr.Zero, (IntPtr)(void*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(span.Bytes)));
- Assert.Equal(0, span.Length);
- }
- }
-}
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System.Tests;
-using Xunit;
-
-namespace System.Text.Tests
-{
- public unsafe partial class Utf8SpanTests
- {
- [Fact]
- public static void CharsProperty_FromData()
- {
- using BoundedUtf8Span boundedSpan = new BoundedUtf8Span("\U00000012\U00000123\U00001234\U00101234\U00000012\U00000123\U00001234\U00101234");
- Utf8Span span = boundedSpan.Span;
-
- var charsEnumerator = span.Chars.GetEnumerator();
-
- Assert.True(charsEnumerator.MoveNext());
- Assert.Equal('\U00000012', charsEnumerator.Current);
- Assert.True(charsEnumerator.MoveNext());
- Assert.Equal('\U00000123', charsEnumerator.Current);
- Assert.True(charsEnumerator.MoveNext());
- Assert.Equal('\U00001234', charsEnumerator.Current);
- Assert.True(charsEnumerator.MoveNext());
- Assert.Equal('\uDBC4', charsEnumerator.Current);
- Assert.True(charsEnumerator.MoveNext());
- Assert.Equal('\uDE34', charsEnumerator.Current);
- Assert.True(charsEnumerator.MoveNext());
- Assert.Equal('\U00000012', charsEnumerator.Current);
- Assert.True(charsEnumerator.MoveNext());
- Assert.Equal('\U00000123', charsEnumerator.Current);
- Assert.True(charsEnumerator.MoveNext());
- Assert.Equal('\U00001234', charsEnumerator.Current);
- Assert.True(charsEnumerator.MoveNext());
- Assert.Equal('\uDBC4', charsEnumerator.Current);
- Assert.True(charsEnumerator.MoveNext());
- Assert.Equal('\uDE34', charsEnumerator.Current);
- Assert.False(charsEnumerator.MoveNext());
- }
-
- [Fact]
- public static void CharsProperty_FromEmpty()
- {
- Assert.False(Utf8Span.Empty.Chars.GetEnumerator().MoveNext());
- }
-
- [Fact]
- public static void RunesProperty_FromData()
- {
- using BoundedUtf8Span boundedSpan = new BoundedUtf8Span("\U00000012\U00000123\U00001234\U00101234\U00000012\U00000123\U00001234\U00101234");
- Utf8Span span = boundedSpan.Span;
-
- var runesEnumerator = span.Runes.GetEnumerator();
-
- Assert.True(runesEnumerator.MoveNext());
- Assert.Equal(new Rune(0x0012), runesEnumerator.Current);
- Assert.True(runesEnumerator.MoveNext());
- Assert.Equal(new Rune(0x0123), runesEnumerator.Current);
- Assert.True(runesEnumerator.MoveNext());
- Assert.Equal(new Rune(0x1234), runesEnumerator.Current);
- Assert.True(runesEnumerator.MoveNext());
- Assert.Equal(new Rune(0x101234), runesEnumerator.Current);
- Assert.True(runesEnumerator.MoveNext());
- Assert.Equal(new Rune(0x0012), runesEnumerator.Current);
- Assert.True(runesEnumerator.MoveNext());
- Assert.Equal(new Rune(0x0123), runesEnumerator.Current);
- Assert.True(runesEnumerator.MoveNext());
- Assert.Equal(new Rune(0x1234), runesEnumerator.Current);
- Assert.True(runesEnumerator.MoveNext());
- Assert.Equal(new Rune(0x101234), runesEnumerator.Current);
- Assert.False(runesEnumerator.MoveNext());
- }
-
- [Fact]
- public static void RunesProperty_FromEmpty()
- {
- Assert.False(Utf8Span.Empty.Runes.GetEnumerator().MoveNext());
- }
- }
-}
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System.Buffers;
-using System.Collections.Generic;
-using System.Globalization;
-using System.Tests;
-
-using static System.Tests.Utf8TestUtilities;
-
-using ustring = System.Utf8String;
-
-namespace System.Text.Tests
-{
- public partial class Utf8SpanTests
- {
- public static IEnumerable<object[]> SplitData_CharSeparator()
- {
- foreach (SplitTestData entry in SplitData_All())
- {
- if (!TryParseSearchTermAsChar(entry.SearchTerm, out char searchChar))
- {
- continue;
- }
-
- yield return new object[]
- {
- entry.Source,
- searchChar,
- entry.ExpectedRanges
- };
- }
- }
-
- public static IEnumerable<object[]> SplitData_RuneSeparator()
- {
- foreach (SplitTestData entry in SplitData_All())
- {
- if (!TryParseSearchTermAsRune(entry.SearchTerm, out Rune searchRune))
- {
- continue;
- }
-
- yield return new object[]
- {
- entry.Source,
- searchRune,
- entry.ExpectedRanges
- };
- }
- }
-
- public static IEnumerable<object[]> SplitData_Utf8SpanSeparator()
- {
- foreach (SplitTestData entry in SplitData_All())
- {
- if (!TryParseSearchTermAsUtf8String(entry.SearchTerm, out ustring searchTerm))
- {
- continue;
- }
-
- yield return new object[]
- {
- entry.Source,
- searchTerm,
- entry.ExpectedRanges
- };
- }
- }
-
- private static IEnumerable<SplitTestData> SplitData_All()
- {
- SplitTestData[] testDataEntries = new SplitTestData[]
- {
- new SplitTestData
- {
- // Empty source, searching for anything results in no match
- Source = null,
- SearchTerm = '\0',
- ExpectedRanges = new[] { 0..0 }
- },
- new SplitTestData
- {
- // If no match, then return original span
- Source = u8("Hello"),
- SearchTerm = 'x',
- ExpectedRanges = new[] { Range.All }
- },
- new SplitTestData
- {
- // Match returns multiple spans (some may be empty)
- Source = u8("Hello"),
- SearchTerm = 'l',
- ExpectedRanges = new[] { 0..2, 3..3, 4..5 }
- },
- new SplitTestData
- {
- // Match returns multiple spans (non-empty)
- Source = u8("Hello"),
- SearchTerm = "ell",
- ExpectedRanges = new[] { 0..1, ^1.. }
- },
- new SplitTestData
- {
- // Match returns multiple spans (non-empty, with whitespace)
- Source = u8("aax aaa xxax \u2028\u2029"), // includes LS, PS as whitespace
- SearchTerm = 'x',
- ExpectedRanges = new[] { 0..2, 3..8, 9..9, 10..11, 12.. }
- },
- new SplitTestData
- {
- // Matching on U+1F600 GRINNING FACE (with whitespace)
- Source = u8("x \U0001F600 y"),
- SearchTerm = new Rune(0x1F600),
- ExpectedRanges = new[] { 0..2, ^2.. }
- },
- };
-
- return testDataEntries;
- }
-
- public class SplitTestData
- {
- public ustring Source;
- public object SearchTerm;
- public Range[] ExpectedRanges;
- }
- }
-}
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System;
-using System.Linq;
-using System.Buffers;
-using System.Collections.Generic;
-using System.Tests;
-using Xunit;
-
-using static System.Tests.Utf8TestUtilities;
-
-using ustring = System.Utf8String;
-
-namespace System.Text.Tests
-{
- public partial class Utf8SpanTests
- {
- private delegate Utf8Span.SplitResult Utf8SpanSplitDelegate(Utf8Span span, Utf8StringSplitOptions splitOptions);
-
- [Fact]
- public static void Split_EmptySearchSpan_Throws()
- {
- // Shouldn't be able to split on an empty UTF-8 span.
- // Such an enumerator would iterate forever, so we forbid it.
-
- var ex = Assert.Throws<ArgumentException>(() => { u8("Hello").AsSpan().Split(Utf8Span.Empty); });
- Assert.Equal("separator", ex.ParamName);
- }
-
- [Fact]
- public static void Split_InvalidChar_Throws()
- {
- // Shouldn't be able to split on a standalone surrogate char
- // Other search methods (TryFind) return false when given a standalone surrogate char as input,
- // but the Split methods returns a complex data structure instead of a simple bool. So to keep
- // the logic of that data structure relatively simple we'll forbid the bad char at the call site.
-
- var ex = Assert.Throws<ArgumentOutOfRangeException>(() => { u8("Hello").AsSpan().Split('\ud800'); });
- Assert.Equal("separator", ex.ParamName);
- }
-
- [Fact]
- public static void Split_Char_NullInput()
- {
- // First, make sure that <null>.Split(',') yields a single-element [ <null> ].
-
- Utf8Span source = Utf8Span.Empty;
-
- var enumerator = source.Split(',').GetEnumerator();
- Assert.True(enumerator.MoveNext());
- Assert.True(source.Bytes == enumerator.Current.Bytes); // referential equality
- Assert.False(enumerator.MoveNext());
-
- // Next, make sure that if "remove empty entries" is specified, yields the empty set [ ].
-
- enumerator = source.Split(',', Utf8StringSplitOptions.RemoveEmptyEntries).GetEnumerator();
- Assert.False(enumerator.MoveNext());
- }
-
- [Theory]
- [MemberData(nameof(SplitData_CharSeparator))]
- public static void Split_Char(ustring source, char separator, Range[] expectedRanges)
- {
- SplitTest_Common(source, (span, splitOptions) => span.Split(separator, splitOptions), expectedRanges);
- }
-
- [Fact]
- public static void Split_Deconstruct()
- {
- using BoundedUtf8Span boundedSpan = new BoundedUtf8Span("a,b,c,d,e");
- Utf8Span span = boundedSpan.Span;
-
- // Note referential equality checks below (since we want to know exact slices
- // into the original buffer), not deep (textual) equality checks.
-
- {
- (Utf8Span a, Utf8Span b) = span.Split('x'); // not found
- Assert.True(a.Bytes == span.Bytes, "Expected referential equality of input.");
- Assert.True(b.Bytes == default);
- }
-
- {
- (Utf8Span a, Utf8Span b) = span.Split(',');
- Assert.True(a.Bytes == span.Bytes[..1]); // "a"
- Assert.True(b.Bytes == span.Bytes[2..]); // "b,c,d,e"
- }
-
- {
- (Utf8Span a, Utf8Span b, Utf8Span c, Utf8Span d, Utf8Span e) = span.Split(',');
- Assert.True(a.Bytes == span.Bytes[0..1]); // "a"
- Assert.True(b.Bytes == span.Bytes[2..3]); // "b"
- Assert.True(c.Bytes == span.Bytes[4..5]); // "c"
- Assert.True(d.Bytes == span.Bytes[6..7]); // "d"
- Assert.True(e.Bytes == span.Bytes[8..9]); // "e"
- }
-
- {
- (Utf8Span a, Utf8Span b, Utf8Span c, Utf8Span d, Utf8Span e, Utf8Span f, Utf8Span g, Utf8Span h) = span.Split(',');
- Assert.True(a.Bytes == span.Bytes[0..1]); // "a"
- Assert.True(b.Bytes == span.Bytes[2..3]); // "b"
- Assert.True(c.Bytes == span.Bytes[4..5]); // "c"
- Assert.True(d.Bytes == span.Bytes[6..7]); // "d"
- Assert.True(e.Bytes == span.Bytes[8..9]); // "e"
- Assert.True(f.Bytes == default);
- Assert.True(g.Bytes == default);
- Assert.True(h.Bytes == default);
- }
- }
-
- [Fact]
- public static void Split_Deconstruct_WithOptions()
- {
- using BoundedUtf8Span boundedSpan = new BoundedUtf8Span("a, , b, c,, d, e");
- Utf8Span span = boundedSpan.Span;
-
- // Note referential equality checks below (since we want to know exact slices
- // into the original buffer), not deep (textual) equality checks.
-
- {
- (Utf8Span a, Utf8Span b) = span.Split(',', Utf8StringSplitOptions.RemoveEmptyEntries);
- Assert.True(a.Bytes == span.Bytes[..1]); // "a"
- Assert.True(b.Bytes == span.Bytes[2..]); // " , b, c,, d, e"
- }
-
- {
- (Utf8Span a, Utf8Span x, Utf8Span b, Utf8Span c, Utf8Span d, Utf8Span e) = span.Split(',', Utf8StringSplitOptions.RemoveEmptyEntries);
- Assert.True(a.Bytes == span.Bytes[0..1]); // "a"
- Assert.True(x.Bytes == span.Bytes[2..3]); // " "
- Assert.True(b.Bytes == span.Bytes[4..6]); // " b"
- Assert.True(c.Bytes == span.Bytes[7..9]); // " c"
- Assert.True(d.Bytes == span.Bytes[11..13]); // " d"
- Assert.True(e.Bytes == span.Bytes[14..]); // " e"
- }
-
- {
- (Utf8Span a, Utf8Span b, Utf8Span c, Utf8Span d, Utf8Span e, Utf8Span f, Utf8Span g, Utf8Span h) = span.Split(',', Utf8StringSplitOptions.RemoveEmptyEntries | Utf8StringSplitOptions.TrimEntries);
- Assert.True(a.Bytes == span.Bytes[0..1]); // "a"
- Assert.True(b.Bytes == span.Bytes[5..6]); // "b"
- Assert.True(c.Bytes == span.Bytes[8..9]); // "c"
- Assert.True(d.Bytes == span.Bytes[12..13]); // "d"
- Assert.True(e.Bytes == span.Bytes[15..]); // "e"
- Assert.True(f.Bytes == default);
- Assert.True(g.Bytes == default);
- Assert.True(h.Bytes == default);
- }
- }
-
- [Theory]
- [MemberData(nameof(SplitData_RuneSeparator))]
- public static void Split_Rune(ustring source, Rune separator, Range[] expectedRanges)
- {
- SplitTest_Common(source, (span, splitOptions) => span.Split(separator, splitOptions), expectedRanges);
- }
-
- [Theory]
- [MemberData(nameof(SplitData_Utf8SpanSeparator))]
- public static void Split_Utf8Span(ustring source, ustring separator, Range[] expectedRanges)
- {
- SplitTest_Common(source, (span, splitOptions) => span.Split(separator.AsSpan(), splitOptions), expectedRanges);
- }
-
- private static void SplitTest_Common(ustring source, Utf8SpanSplitDelegate splitAction, Range[] expectedRanges)
- {
- using BoundedUtf8Span boundedSpan = new BoundedUtf8Span(source.AsBytes());
- Utf8Span span = boundedSpan.Span;
- int totalSpanLengthInBytes = span.Length;
- source = null; // to avoid inadvertently using this for the remainder of the method
-
- // First, run the split with default options and make sure the ranges are equivalent
-
- List<Range> actualRanges = new List<Range>();
- foreach (Utf8Span slice in splitAction(span, Utf8StringSplitOptions.None))
- {
- actualRanges.Add(GetRangeOfSubspan(span, slice));
- }
-
- Assert.Equal(expectedRanges, actualRanges, new RangeEqualityComparer(totalSpanLengthInBytes));
-
- // Next, run the split with empty entries removed
-
- actualRanges = new List<Range>();
- foreach (Utf8Span slice in splitAction(span, Utf8StringSplitOptions.RemoveEmptyEntries))
- {
- actualRanges.Add(GetRangeOfSubspan(span, slice));
- }
-
- Assert.Equal(expectedRanges.Where(range => !range.IsEmpty(totalSpanLengthInBytes)), actualRanges, new RangeEqualityComparer(totalSpanLengthInBytes));
-
- // Next, run the split with results trimmed (but allowing empty results)
-
- expectedRanges = (Range[])expectedRanges.Clone(); // clone the array since we're about to mutate it
- for (int i = 0; i < expectedRanges.Length; i++)
- {
- expectedRanges[i] = GetRangeOfSubspan(span, span[expectedRanges[i]].Trim());
- }
-
- actualRanges = new List<Range>();
- foreach (Utf8Span slice in splitAction(span, Utf8StringSplitOptions.TrimEntries))
- {
- actualRanges.Add(GetRangeOfSubspan(span, slice));
- }
-
- Assert.Equal(expectedRanges, actualRanges, new RangeEqualityComparer(totalSpanLengthInBytes));
-
- // Finally, run the split both trimmed and with empty entries removed
-
- actualRanges = new List<Range>();
- foreach (Utf8Span slice in splitAction(span, Utf8StringSplitOptions.TrimEntries | Utf8StringSplitOptions.RemoveEmptyEntries))
- {
- actualRanges.Add(GetRangeOfSubspan(span, slice));
- }
-
- Assert.Equal(expectedRanges.Where(range => !range.IsEmpty(totalSpanLengthInBytes)), actualRanges, new RangeEqualityComparer(totalSpanLengthInBytes));
- }
-
- [Theory]
- [MemberData(nameof(Trim_TestData))]
- public static void Trim(string input)
- {
- // Arrange
-
- using BoundedUtf8Span boundedSpan = new BoundedUtf8Span(input);
- Utf8Span span = boundedSpan.Span;
-
- // Act
-
- Utf8Span trimmed = span.Trim();
-
- // Assert
- // Compute the trim manually and ensure it matches the trimmed span's characteristics.
-
- ReadOnlySpan<byte> utf8Bytes = span.Bytes;
- while (!utf8Bytes.IsEmpty)
- {
- OperationStatus status = Rune.DecodeFromUtf8(utf8Bytes, out Rune decodedRune, out int bytesConsumed);
- Assert.Equal(OperationStatus.Done, status);
-
- if (!Rune.IsWhiteSpace(decodedRune))
- {
- break;
- }
-
- utf8Bytes = utf8Bytes.Slice(bytesConsumed);
- }
- while (!utf8Bytes.IsEmpty)
- {
- OperationStatus status = Rune.DecodeLastFromUtf8(utf8Bytes, out Rune decodedRune, out int bytesConsumed);
- Assert.Equal(OperationStatus.Done, status);
-
- if (!Rune.IsWhiteSpace(decodedRune))
- {
- break;
- }
-
- utf8Bytes = utf8Bytes[..^bytesConsumed];
- }
-
- Assert.True(trimmed.Bytes == utf8Bytes); // must be an exact buffer match (address + length)
- }
-
- [Theory]
- [MemberData(nameof(Trim_TestData))]
- public static void TrimEnd(string input)
- {
- // Arrange
-
- using BoundedUtf8Span boundedSpan = new BoundedUtf8Span(input);
- Utf8Span span = boundedSpan.Span;
-
- // Act
-
- Utf8Span trimmed = span.TrimEnd();
-
- // Assert
- // Compute the trim manually and ensure it matches the trimmed span's characteristics.
-
- ReadOnlySpan<byte> utf8Bytes = span.Bytes;
- while (!utf8Bytes.IsEmpty)
- {
- OperationStatus status = Rune.DecodeLastFromUtf8(utf8Bytes, out Rune decodedRune, out int bytesConsumed);
- Assert.Equal(OperationStatus.Done, status);
-
- if (!Rune.IsWhiteSpace(decodedRune))
- {
- break;
- }
-
- utf8Bytes = utf8Bytes[..^bytesConsumed];
- }
-
- Assert.True(trimmed.Bytes == utf8Bytes); // must be an exact buffer match (address + length)
- }
-
- [Theory]
- [MemberData(nameof(Trim_TestData))]
- public static void TrimStart(string input)
- {
- // Arrange
-
- using BoundedUtf8Span boundedSpan = new BoundedUtf8Span(input);
- Utf8Span span = boundedSpan.Span;
-
- // Act
-
- Utf8Span trimmed = span.TrimStart();
-
- // Assert
- // Compute the trim manually and ensure it matches the trimmed span's characteristics.
-
- ReadOnlySpan<byte> utf8Bytes = span.Bytes;
- while (!utf8Bytes.IsEmpty)
- {
- OperationStatus status = Rune.DecodeFromUtf8(utf8Bytes, out Rune decodedRune, out int bytesConsumed);
- Assert.Equal(OperationStatus.Done, status);
-
- if (!Rune.IsWhiteSpace(decodedRune))
- {
- break;
- }
-
- utf8Bytes = utf8Bytes.Slice(bytesConsumed);
- }
-
- Assert.True(trimmed.Bytes == utf8Bytes); // must be an exact buffer match (address + length)
- }
- }
-}
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System.Collections.Generic;
-using System.Globalization;
-
-using static System.Tests.Utf8TestUtilities;
-
-using ustring = System.Utf8String;
-
-namespace System.Text.Tests
-{
- public partial class Utf8SpanTests
- {
- public static IEnumerable<object[]> TryFindData_Char_Ordinal()
- {
- foreach (TryFindTestData entry in TryFindData_All())
- {
- if (!entry.Options.HasFlag(TryFindTestDataOptions.TestOrdinal) || entry.Options.HasFlag(TryFindTestDataOptions.TestIgnoreCaseOnly))
- {
- continue;
- }
-
- if (!TryParseSearchTermAsChar(entry.SearchTerm, out char searchChar))
- {
- continue;
- }
-
- yield return new object[]
- {
- entry.Source,
- searchChar,
- entry.ExpectedFirstMatch,
- entry.ExpectedLastMatch,
- };
- }
- }
-
- public static IEnumerable<object[]> TryFindData_Char_WithComparison()
- {
- foreach (TryFindTestData entry in TryFindData_All())
- {
- if (!TryParseSearchTermAsChar(entry.SearchTerm, out char searchChar))
- {
- continue;
- }
-
- if (entry.Options.HasFlag(TryFindTestDataOptions.TestOrdinal))
- {
- if (!entry.Options.HasFlag(TryFindTestDataOptions.TestIgnoreCaseOnly))
- {
- yield return new object[]
- {
- entry.Source,
- searchChar,
- StringComparison.Ordinal,
- null /* culture */,
- entry.ExpectedFirstMatch,
- entry.ExpectedLastMatch,
- };
- }
- if (!entry.Options.HasFlag(TryFindTestDataOptions.TestCaseSensitiveOnly))
- {
- yield return new object[]
- {
- entry.Source,
- searchChar,
- StringComparison.OrdinalIgnoreCase,
- null /* culture */,
- entry.ExpectedFirstMatch,
- entry.ExpectedLastMatch,
- };
- }
- }
-
- foreach (CultureInfo culture in entry.AdditionalCultures ?? Array.Empty<CultureInfo>())
- {
- if (culture == CultureInfo.InvariantCulture)
- {
- if (!entry.Options.HasFlag(TryFindTestDataOptions.TestIgnoreCaseOnly))
- {
- yield return new object[]
- {
- entry.Source,
- searchChar,
- StringComparison.InvariantCulture,
- null /* culture */,
- entry.ExpectedFirstMatch,
- entry.ExpectedLastMatch,
- };
- }
- if (!entry.Options.HasFlag(TryFindTestDataOptions.TestCaseSensitiveOnly))
- {
- yield return new object[]
- {
- entry.Source,
- searchChar,
- StringComparison.InvariantCultureIgnoreCase,
- null /* culture */,
- entry.ExpectedFirstMatch,
- entry.ExpectedLastMatch,
- };
- }
- }
-
- if (!entry.Options.HasFlag(TryFindTestDataOptions.TestIgnoreCaseOnly))
- {
- yield return new object[]
- {
- entry.Source,
- searchChar,
- StringComparison.CurrentCulture,
- culture,
- entry.ExpectedFirstMatch,
- entry.ExpectedLastMatch,
- };
- }
- if (!entry.Options.HasFlag(TryFindTestDataOptions.TestCaseSensitiveOnly))
- {
- yield return new object[]
- {
- entry.Source,
- searchChar,
- StringComparison.CurrentCultureIgnoreCase,
- culture,
- entry.ExpectedFirstMatch,
- entry.ExpectedLastMatch,
- };
- }
- }
- }
- }
-
- public static IEnumerable<object[]> TryFindData_Rune_Ordinal()
- {
- foreach (TryFindTestData entry in TryFindData_All())
- {
- if (!entry.Options.HasFlag(TryFindTestDataOptions.TestOrdinal) || entry.Options.HasFlag(TryFindTestDataOptions.TestIgnoreCaseOnly))
- {
- continue;
- }
-
- if (!TryParseSearchTermAsRune(entry.SearchTerm, out Rune searchRune))
- {
- continue;
- }
-
- yield return new object[]
- {
- entry.Source,
- searchRune,
- entry.ExpectedFirstMatch,
- entry.ExpectedLastMatch,
- };
- }
- }
-
- public static IEnumerable<object[]> TryFindData_Rune_WithComparison()
- {
- foreach (TryFindTestData entry in TryFindData_All())
- {
- if (!TryParseSearchTermAsRune(entry.SearchTerm, out Rune searchRune))
- {
- continue;
- }
-
- if (entry.Options.HasFlag(TryFindTestDataOptions.TestOrdinal))
- {
- if (!entry.Options.HasFlag(TryFindTestDataOptions.TestIgnoreCaseOnly))
- {
- yield return new object[]
- {
- entry.Source,
- searchRune,
- StringComparison.Ordinal,
- null /* culture */,
- entry.ExpectedFirstMatch,
- entry.ExpectedLastMatch,
- };
- }
- if (!entry.Options.HasFlag(TryFindTestDataOptions.TestCaseSensitiveOnly))
- {
- yield return new object[]
- {
- entry.Source,
- searchRune,
- StringComparison.OrdinalIgnoreCase,
- null /* culture */,
- entry.ExpectedFirstMatch,
- entry.ExpectedLastMatch,
- };
- }
- }
-
- foreach (CultureInfo culture in entry.AdditionalCultures ?? Array.Empty<CultureInfo>())
- {
- if (culture == CultureInfo.InvariantCulture)
- {
- if (!entry.Options.HasFlag(TryFindTestDataOptions.TestIgnoreCaseOnly))
- {
- yield return new object[]
- {
- entry.Source,
- searchRune,
- StringComparison.InvariantCulture,
- null /* culture */,
- entry.ExpectedFirstMatch,
- entry.ExpectedLastMatch,
- };
- }
- if (!entry.Options.HasFlag(TryFindTestDataOptions.TestCaseSensitiveOnly))
- {
- yield return new object[]
- {
- entry.Source,
- searchRune,
- StringComparison.InvariantCultureIgnoreCase,
- null /* culture */,
- entry.ExpectedFirstMatch,
- entry.ExpectedLastMatch,
- };
- }
- }
-
- if (!entry.Options.HasFlag(TryFindTestDataOptions.TestIgnoreCaseOnly))
- {
- yield return new object[]
- {
- entry.Source,
- searchRune,
- StringComparison.CurrentCulture,
- culture,
- entry.ExpectedFirstMatch,
- entry.ExpectedLastMatch,
- };
- }
- if (!entry.Options.HasFlag(TryFindTestDataOptions.TestCaseSensitiveOnly))
- {
- yield return new object[]
- {
- entry.Source,
- searchRune,
- StringComparison.CurrentCultureIgnoreCase,
- culture,
- entry.ExpectedFirstMatch,
- entry.ExpectedLastMatch,
- };
- }
- }
- }
- }
-
- public static IEnumerable<object[]> TryFindData_Utf8Span_Ordinal()
- {
- foreach (TryFindTestData entry in TryFindData_All())
- {
- if (!entry.Options.HasFlag(TryFindTestDataOptions.TestOrdinal) || entry.Options.HasFlag(TryFindTestDataOptions.TestIgnoreCaseOnly))
- {
- continue;
- }
-
- if (!TryParseSearchTermAsUtf8String(entry.SearchTerm, out ustring searchTerm))
- {
- continue;
- }
-
- yield return new object[]
- {
- entry.Source,
- searchTerm,
- entry.ExpectedFirstMatch,
- entry.ExpectedLastMatch,
- };
- }
- }
-
- public static IEnumerable<object[]> TryFindData_Utf8Span_WithComparison()
- {
- foreach (TryFindTestData entry in TryFindData_All())
- {
- if (!TryParseSearchTermAsUtf8String(entry.SearchTerm, out ustring searchTerm))
- {
- continue;
- }
-
- if (entry.Options.HasFlag(TryFindTestDataOptions.TestOrdinal))
- {
- if (!entry.Options.HasFlag(TryFindTestDataOptions.TestIgnoreCaseOnly))
- {
- yield return new object[]
- {
- entry.Source,
- searchTerm,
- StringComparison.Ordinal,
- null /* culture */,
- entry.ExpectedFirstMatch,
- entry.ExpectedLastMatch,
- };
- }
- if (!entry.Options.HasFlag(TryFindTestDataOptions.TestCaseSensitiveOnly))
- {
- yield return new object[]
- {
- entry.Source,
- searchTerm,
- StringComparison.OrdinalIgnoreCase,
- null /* culture */,
- entry.ExpectedFirstMatch,
- entry.ExpectedLastMatch,
- };
- }
- }
-
- foreach (CultureInfo culture in entry.AdditionalCultures ?? Array.Empty<CultureInfo>())
- {
- if (culture == CultureInfo.InvariantCulture)
- {
- if (!entry.Options.HasFlag(TryFindTestDataOptions.TestIgnoreCaseOnly))
- {
- yield return new object[]
- {
- entry.Source,
- searchTerm,
- StringComparison.InvariantCulture,
- null /* culture */,
- entry.ExpectedFirstMatch,
- entry.ExpectedLastMatch,
- };
- }
- if (!entry.Options.HasFlag(TryFindTestDataOptions.TestCaseSensitiveOnly))
- {
- yield return new object[]
- {
- entry.Source,
- searchTerm,
- StringComparison.InvariantCultureIgnoreCase,
- null /* culture */,
- entry.ExpectedFirstMatch,
- entry.ExpectedLastMatch,
- };
- }
- }
-
- if (!entry.Options.HasFlag(TryFindTestDataOptions.TestIgnoreCaseOnly))
- {
- yield return new object[]
- {
- entry.Source,
- searchTerm,
- StringComparison.CurrentCulture,
- culture,
- entry.ExpectedFirstMatch,
- entry.ExpectedLastMatch,
- };
- }
- if (!entry.Options.HasFlag(TryFindTestDataOptions.TestCaseSensitiveOnly))
- {
- yield return new object[]
- {
- entry.Source,
- searchTerm,
- StringComparison.CurrentCultureIgnoreCase,
- culture,
- entry.ExpectedFirstMatch,
- entry.ExpectedLastMatch,
- };
- }
- }
- }
- }
-
- private static IEnumerable<TryFindTestData> TryFindData_All()
- {
- CultureInfo inv = CultureInfo.InvariantCulture;
- CultureInfo en_US = CultureInfo.GetCultureInfo("en-US");
- CultureInfo tr_TR = CultureInfo.GetCultureInfo("tr-TR");
- CultureInfo hu_HU = CultureInfo.GetCultureInfo("hu-HU");
-
- TryFindTestData[] testDataEntries = new TryFindTestData[]
- {
- new TryFindTestData
- {
- // Searching for the empty string within the empty string should result in 0..0 / ^0..^0 across all comparers and all cultures
- Source = null,
- SearchTerm = null,
- Options = TryFindTestDataOptions.TestOrdinal,
- AdditionalCultures = new CultureInfo[] { inv, en_US, tr_TR, hu_HU },
- ExpectedFirstMatch = 0..0,
- ExpectedLastMatch = ^0..^0,
- },
- new TryFindTestData
- {
- // Searching for the empty string within a non-empty string should result in 0..0 / ^0..^0 across all comparers and all cultures
- Source = u8("Hello"),
- SearchTerm = null,
- Options = TryFindTestDataOptions.TestOrdinal,
- AdditionalCultures = new CultureInfo[] { inv, en_US, tr_TR, hu_HU },
- ExpectedFirstMatch = 0..0,
- ExpectedLastMatch = ^0..^0,
- },
- new TryFindTestData
- {
- // Searching for a non-empty string within an empty string should fail across all comparers and all cultures
- Source = null,
- SearchTerm = u8("Hello"),
- Options = TryFindTestDataOptions.TestOrdinal,
- AdditionalCultures = new CultureInfo[] { inv, en_US, tr_TR, hu_HU },
- ExpectedFirstMatch = null,
- ExpectedLastMatch = null,
- },
- new TryFindTestData
- {
- // Searching for the null terminator shouldn't match unless the input contains a null terminator
- Source = u8("Hello"),
- SearchTerm = '\0',
- Options = TryFindTestDataOptions.TestOrdinal,
- AdditionalCultures = null,
- ExpectedFirstMatch = null,
- ExpectedLastMatch = null,
- },
- new TryFindTestData
- {
- // Searching for the null terminator shouldn't match unless the input contains a null terminator
- Source = u8("H\0ell\0o"),
- SearchTerm = '\0',
- Options = TryFindTestDataOptions.TestOrdinal,
- AdditionalCultures = null,
- ExpectedFirstMatch = 1..2,
- ExpectedLastMatch = ^2..^1,
- },
- new TryFindTestData
- {
- // Simple ASCII search with success (case-sensitive)
- Source = u8("Hello"),
- SearchTerm = 'l',
- Options = TryFindTestDataOptions.TestOrdinal | TryFindTestDataOptions.TestCaseSensitiveOnly,
- AdditionalCultures = new CultureInfo[] { inv },
- ExpectedFirstMatch = 2..3,
- ExpectedLastMatch = 3..4,
- },
- new TryFindTestData
- {
- // Simple ASCII search with failure (case-sensitive)
- Source = u8("Hello"),
- SearchTerm = 'L',
- Options = TryFindTestDataOptions.TestOrdinal | TryFindTestDataOptions.TestCaseSensitiveOnly,
- AdditionalCultures = new CultureInfo[] { inv },
- ExpectedFirstMatch = null,
- ExpectedLastMatch = null,
- },
- new TryFindTestData
- {
- // Simple ASCII search with success (case-insensitive)
- Source = u8("Hello"),
- SearchTerm = 'L',
- Options = TryFindTestDataOptions.TestOrdinal | TryFindTestDataOptions.TestIgnoreCaseOnly,
- AdditionalCultures = new CultureInfo[] { inv },
- ExpectedFirstMatch = 2..3,
- ExpectedLastMatch = 3..4,
- },
- new TryFindTestData
- {
- // U+1F600 GRINNING FACE, should match an exact Rune search
- Source = u8("x\U0001F600y"),
- SearchTerm = new Rune(0x1F600),
- Options = TryFindTestDataOptions.TestOrdinal,
- AdditionalCultures = new CultureInfo[] { inv },
- ExpectedFirstMatch = 1..5,
- ExpectedLastMatch = 1..5,
- },
- new TryFindTestData
- {
- // U+1F600 GRINNING FACE, shouldn't match looking for individual UTF-16 surrogate chars
- Source = u8("x\ud83d\ude00y"),
- SearchTerm = '\ud83d',
- Options = TryFindTestDataOptions.TestOrdinal,
- AdditionalCultures = new CultureInfo[] { inv },
- ExpectedFirstMatch = null,
- ExpectedLastMatch = null,
- },
- new TryFindTestData
- {
- // U+1F600 GRINNING FACE, shouldn't match on the standalone [ F0 ] byte that begins the multi-byte sequence
- Source = u8("x\ud83d\ude00y"),
- SearchTerm = '\u00f0',
- Options = TryFindTestDataOptions.TestOrdinal,
- AdditionalCultures = new CultureInfo[] { inv },
- ExpectedFirstMatch = null,
- ExpectedLastMatch = null,
- },
- new TryFindTestData
- {
- // hu_HU shouldn't match "d" within "dz"
- Source = u8("ab_dz_ba"),
- SearchTerm = 'd',
- Options = TryFindTestDataOptions.None,
- AdditionalCultures = new CultureInfo[] { hu_HU },
- ExpectedFirstMatch = null,
- ExpectedLastMatch = null,
- },
- new TryFindTestData
- {
- // Turkish I, case-sensitive
- Source = u8("\u0069\u0130\u0131\u0049"), // iİıI
- SearchTerm = 'i',
- Options = TryFindTestDataOptions.TestCaseSensitiveOnly,
- AdditionalCultures = new CultureInfo[] { tr_TR },
- ExpectedFirstMatch = 0..1,
- ExpectedLastMatch = 0..1,
- },
- new TryFindTestData
- {
- // Turkish I, case-insensitive
- Source = u8("\u0069\u0130\u0131\u0049"), // iİıI
- SearchTerm = 'i',
- Options = TryFindTestDataOptions.TestIgnoreCaseOnly,
- AdditionalCultures = new CultureInfo[] { tr_TR },
- ExpectedFirstMatch = 0..1,
- ExpectedLastMatch = 1..3,
- },
- new TryFindTestData
- {
- // denormalized forms, no match
- Source = u8("a\u0308e\u0308A\u0308E\u0308"), // äëÄË (denormalized)
- SearchTerm = 'e', // shouldn't match letter paired with diacritic
- Options = TryFindTestDataOptions.None,
- AdditionalCultures = new CultureInfo[] { inv },
- ExpectedFirstMatch = null,
- ExpectedLastMatch = null,
- },
- new TryFindTestData
- {
- // denormalized forms, case-sensitive
- Source = u8("a\u0308e\u0308A\u0308E\u0308"), // äëÄË (denormalized)
- SearchTerm = '\u00eb', // ë, normalized form
- Options = TryFindTestDataOptions.TestCaseSensitiveOnly,
- AdditionalCultures = new CultureInfo[] { inv },
- ExpectedFirstMatch = 3..6,
- ExpectedLastMatch = 3..6,
- },
- new TryFindTestData
- {
- // denormalized forms, case-insensitive
- Source = u8("a\u0308e\u0308A\u0308E\u0308"), // äëÄË (denormalized)
- SearchTerm = '\u00eb', // ë, normalized form
- Options = TryFindTestDataOptions.TestIgnoreCaseOnly,
- AdditionalCultures = new CultureInfo[] { inv },
- ExpectedFirstMatch = 3..6,
- ExpectedLastMatch = ^3..,
- },
- };
-
- return testDataEntries;
- }
-
- public class TryFindTestData
- {
- public ustring Source;
- public object SearchTerm;
- public TryFindTestDataOptions Options;
- public CultureInfo[] AdditionalCultures;
- public Range? ExpectedFirstMatch;
- public Range? ExpectedLastMatch;
- }
-
- [Flags]
- public enum TryFindTestDataOptions
- {
- None = 0,
- TestOrdinal = 1 << 0,
- TestCaseSensitiveOnly = 1 << 1,
- TestIgnoreCaseOnly = 2 << 1,
- }
- }
-}
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System.Globalization;
-using System.Tests;
-using Xunit;
-
-using static System.Tests.Utf8TestUtilities;
-
-using ustring = System.Utf8String;
-
-namespace System.Text.Tests
-{
- /*
- * Please keep these tests in sync with those in Utf8StringTests.Searching.cs.
- */
-
- public unsafe partial class Utf8SpanTests
- {
- [Theory]
- [MemberData(nameof(TryFindData_Char_Ordinal))]
- public static void TryFind_Char_Ordinal(ustring source, char searchTerm, Range? expectedForwardMatch, Range? expectedBackwardMatch)
- {
- using BoundedUtf8Span boundedSpan = new BoundedUtf8Span(source.AsBytes());
- Utf8Span searchSpan = boundedSpan.Span;
- source = null; // to avoid accidentally using this for the remainder of the test
-
- // First, search forward
-
- bool wasFound = searchSpan.TryFind(searchTerm, out Range actualForwardMatch);
- Assert.Equal(expectedForwardMatch.HasValue, wasFound);
-
- if (wasFound)
- {
- AssertRangesEqual(searchSpan.Length, expectedForwardMatch.Value, actualForwardMatch);
- }
-
- // Also check Contains / StartsWith / SplitOn
-
- Assert.Equal(wasFound, searchSpan.Contains(searchTerm));
- Assert.Equal(wasFound && searchSpan.Bytes[..actualForwardMatch.Start].IsEmpty, searchSpan.StartsWith(searchTerm));
-
- (var before, var after) = searchSpan.SplitOn(searchTerm);
- if (wasFound)
- {
- Assert.True(searchSpan.Bytes[..actualForwardMatch.Start] == before.Bytes); // check for referential equality
- Assert.True(searchSpan.Bytes[actualForwardMatch.End..] == after.Bytes); // check for referential equality
- }
- else
- {
- Assert.True(searchSpan.Bytes == before.Bytes); // check for reference equality
- Assert.True(after.IsNull());
- }
-
- // Now search backward
-
- wasFound = searchSpan.TryFindLast(searchTerm, out Range actualBackwardMatch);
- Assert.Equal(expectedBackwardMatch.HasValue, wasFound);
-
- if (wasFound)
- {
- AssertRangesEqual(searchSpan.Length, expectedBackwardMatch.Value, actualBackwardMatch);
- }
-
- // Also check EndsWith / SplitOnLast
-
- Assert.Equal(wasFound && searchSpan.Bytes[actualBackwardMatch.End..].IsEmpty, searchSpan.EndsWith(searchTerm));
-
- (before, after) = searchSpan.SplitOnLast(searchTerm);
- if (wasFound)
- {
- Assert.True(searchSpan.Bytes[..actualBackwardMatch.Start] == before.Bytes); // check for referential equality
- Assert.True(searchSpan.Bytes[actualBackwardMatch.End..] == after.Bytes); // check for referential equality
- }
- else
- {
- Assert.True(searchSpan.Bytes == before.Bytes); // check for reference equality
- Assert.True(after.IsNull());
- }
- }
-
- [PlatformSpecific(TestPlatforms.Windows)]
- [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNlsGlobalization))]
- [MemberData(nameof(TryFindData_Char_WithComparison))]
- public static void TryFind_Char_WithComparison(ustring source, char searchTerm, StringComparison comparison, CultureInfo currentCulture, Range? expectedForwardMatch, Range? expectedBackwardMatch)
- {
- RunOnDedicatedThread(() =>
- {
- using BoundedUtf8Span boundedSpan = new BoundedUtf8Span(source.AsBytes());
- Utf8Span searchSpan = boundedSpan.Span;
- source = null; // to avoid accidentally using this for the remainder of the test
-
- if (currentCulture != null)
- {
- CultureInfo.CurrentCulture = currentCulture;
- }
-
- if (IsTryFindSupported(comparison))
- {
- // First, search forward
-
- bool wasFound = searchSpan.TryFind(searchTerm, comparison, out Range actualForwardMatch);
- Assert.Equal(expectedForwardMatch.HasValue, wasFound);
-
- if (wasFound)
- {
- AssertRangesEqual(searchSpan.Length, expectedForwardMatch.Value, actualForwardMatch);
- }
-
- // Also check Contains / StartsWith / SplitOn
-
- Assert.Equal(wasFound, searchSpan.Contains(searchTerm, comparison));
- Assert.Equal(wasFound && searchSpan.Bytes[..actualForwardMatch.Start].IsEmpty, searchSpan.StartsWith(searchTerm, comparison));
-
- (var before, var after) = searchSpan.SplitOn(searchTerm, comparison);
- if (wasFound)
- {
- Assert.True(searchSpan.Bytes[..actualForwardMatch.Start] == before.Bytes); // check for referential equality
- Assert.True(searchSpan.Bytes[actualForwardMatch.End..] == after.Bytes); // check for referential equality
- }
- else
- {
- Assert.True(searchSpan.Bytes == before.Bytes); // check for reference equality
- Assert.True(after.IsNull());
- }
-
- // Now search backward
-
- wasFound = searchSpan.TryFindLast(searchTerm, comparison, out Range actualBackwardMatch);
- Assert.Equal(expectedBackwardMatch.HasValue, wasFound);
-
- if (wasFound)
- {
- AssertRangesEqual(searchSpan.Length, expectedBackwardMatch.Value, actualBackwardMatch);
- }
-
- // Also check EndsWith / SplitOnLast
-
- Assert.Equal(wasFound && searchSpan.Bytes[actualBackwardMatch.End..].IsEmpty, searchSpan.EndsWith(searchTerm, comparison));
-
- (before, after) = searchSpan.SplitOnLast(searchTerm, comparison);
- if (wasFound)
- {
- Assert.True(searchSpan.Bytes[..actualBackwardMatch.Start] == before.Bytes); // check for referential equality
- Assert.True(searchSpan.Bytes[actualBackwardMatch.End..] == after.Bytes); // check for referential equality
- }
- else
- {
- Assert.True(searchSpan.Bytes == before.Bytes); // check for reference equality
- Assert.True(after.IsNull());
- }
- }
- else
- {
- Assert.Throws<NotSupportedException>(() => boundedSpan.Span.TryFind(searchTerm, comparison, out var _));
- Assert.Throws<NotSupportedException>(() => boundedSpan.Span.TryFindLast(searchTerm, comparison, out var _));
- Assert.Throws<NotSupportedException>(() => boundedSpan.Span.SplitOn(searchTerm, comparison));
- Assert.Throws<NotSupportedException>(() => boundedSpan.Span.SplitOnLast(searchTerm, comparison));
-
- Assert.Equal(expectedForwardMatch.HasValue, searchSpan.Contains(searchTerm, comparison));
- Assert.Equal(expectedForwardMatch.HasValue && searchSpan.Bytes[..expectedForwardMatch.Value.Start].IsEmpty, searchSpan.StartsWith(searchTerm, comparison));
- Assert.Equal(expectedBackwardMatch.HasValue && searchSpan.Bytes[expectedBackwardMatch.Value.End..].IsEmpty, searchSpan.EndsWith(searchTerm, comparison));
- }
- });
- }
-
- [Theory]
- [MemberData(nameof(TryFindData_Rune_Ordinal))]
- public static void TryFind_Rune_Ordinal(ustring source, Rune searchTerm, Range? expectedForwardMatch, Range? expectedBackwardMatch)
- {
- using BoundedUtf8Span boundedSpan = new BoundedUtf8Span(source.AsBytes());
- Utf8Span searchSpan = boundedSpan.Span;
- source = null; // to avoid accidentally using this for the remainder of the test
-
- // First, search forward
-
- bool wasFound = searchSpan.TryFind(searchTerm, out Range actualForwardMatch);
- Assert.Equal(expectedForwardMatch.HasValue, wasFound);
-
- if (wasFound)
- {
- AssertRangesEqual(searchSpan.Length, expectedForwardMatch.Value, actualForwardMatch);
- }
-
- // Also check Contains / StartsWith / SplitOn
-
- Assert.Equal(wasFound, searchSpan.Contains(searchTerm));
- Assert.Equal(wasFound && searchSpan.Bytes[..actualForwardMatch.Start].IsEmpty, searchSpan.StartsWith(searchTerm));
-
- (var before, var after) = searchSpan.SplitOn(searchTerm);
- if (wasFound)
- {
- Assert.True(searchSpan.Bytes[..actualForwardMatch.Start] == before.Bytes); // check for referential equality
- Assert.True(searchSpan.Bytes[actualForwardMatch.End..] == after.Bytes); // check for referential equality
- }
- else
- {
- Assert.True(searchSpan.Bytes == before.Bytes); // check for reference equality
- Assert.True(after.IsNull());
- }
-
- // Now search backward
-
- wasFound = searchSpan.TryFindLast(searchTerm, out Range actualBackwardMatch);
- Assert.Equal(expectedBackwardMatch.HasValue, wasFound);
-
- if (wasFound)
- {
- AssertRangesEqual(searchSpan.Length, expectedBackwardMatch.Value, actualBackwardMatch);
- }
-
- // Also check EndsWith / SplitOnLast
-
- Assert.Equal(wasFound && searchSpan.Bytes[actualBackwardMatch.End..].IsEmpty, searchSpan.EndsWith(searchTerm));
-
- (before, after) = searchSpan.SplitOnLast(searchTerm);
- if (wasFound)
- {
- Assert.True(searchSpan.Bytes[..actualBackwardMatch.Start] == before.Bytes); // check for referential equality
- Assert.True(searchSpan.Bytes[actualBackwardMatch.End..] == after.Bytes); // check for referential equality
- }
- else
- {
- Assert.True(searchSpan.Bytes == before.Bytes); // check for reference equality
- Assert.True(after.IsNull());
- }
- }
-
- [PlatformSpecific(TestPlatforms.Windows)]
- [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNlsGlobalization))]
- [MemberData(nameof(TryFindData_Rune_WithComparison))]
- public static void TryFind_Rune_WithComparison(ustring source, Rune searchTerm, StringComparison comparison, CultureInfo currentCulture, Range? expectedForwardMatch, Range? expectedBackwardMatch)
- {
- RunOnDedicatedThread(() =>
- {
- using BoundedUtf8Span boundedSpan = new BoundedUtf8Span(source.AsBytes());
- Utf8Span searchSpan = boundedSpan.Span;
- source = null; // to avoid accidentally using this for the remainder of the test
-
- if (currentCulture != null)
- {
- CultureInfo.CurrentCulture = currentCulture;
- }
-
- if (IsTryFindSupported(comparison))
- {
- // First, search forward
-
- bool wasFound = searchSpan.TryFind(searchTerm, comparison, out Range actualForwardMatch);
- Assert.Equal(expectedForwardMatch.HasValue, wasFound);
-
- if (wasFound)
- {
- AssertRangesEqual(searchSpan.Length, expectedForwardMatch.Value, actualForwardMatch);
- }
-
- // Also check Contains / StartsWith / SplitOn
-
- Assert.Equal(wasFound, searchSpan.Contains(searchTerm, comparison));
- Assert.Equal(wasFound && searchSpan.Bytes[..actualForwardMatch.Start].IsEmpty, searchSpan.StartsWith(searchTerm, comparison));
-
- (var before, var after) = searchSpan.SplitOn(searchTerm, comparison);
- if (wasFound)
- {
- Assert.True(searchSpan.Bytes[..actualForwardMatch.Start] == before.Bytes); // check for referential equality
- Assert.True(searchSpan.Bytes[actualForwardMatch.End..] == after.Bytes); // check for referential equality
- }
- else
- {
- Assert.True(searchSpan.Bytes == before.Bytes); // check for reference equality
- Assert.True(after.IsNull());
- }
-
- // Now search backward
-
- wasFound = searchSpan.TryFindLast(searchTerm, comparison, out Range actualBackwardMatch);
- Assert.Equal(expectedBackwardMatch.HasValue, wasFound);
-
- if (wasFound)
- {
- AssertRangesEqual(searchSpan.Length, expectedBackwardMatch.Value, actualBackwardMatch);
- }
-
- // Also check EndsWith / SplitOnLast
-
- Assert.Equal(wasFound && searchSpan.Bytes[actualBackwardMatch.End..].IsEmpty, searchSpan.EndsWith(searchTerm, comparison));
-
- (before, after) = searchSpan.SplitOnLast(searchTerm, comparison);
- if (wasFound)
- {
- Assert.True(searchSpan.Bytes[..actualBackwardMatch.Start] == before.Bytes); // check for referential equality
- Assert.True(searchSpan.Bytes[actualBackwardMatch.End..] == after.Bytes); // check for referential equality
- }
- else
- {
- Assert.True(searchSpan.Bytes == before.Bytes); // check for reference equality
- Assert.True(after.IsNull());
- }
- }
- else
- {
- Assert.Throws<NotSupportedException>(() => boundedSpan.Span.TryFind(searchTerm, comparison, out var _));
- Assert.Throws<NotSupportedException>(() => boundedSpan.Span.TryFindLast(searchTerm, comparison, out var _));
- Assert.Throws<NotSupportedException>(() => boundedSpan.Span.SplitOn(searchTerm, comparison));
- Assert.Throws<NotSupportedException>(() => boundedSpan.Span.SplitOnLast(searchTerm, comparison));
-
- Assert.Equal(expectedForwardMatch.HasValue, searchSpan.Contains(searchTerm, comparison));
- Assert.Equal(expectedForwardMatch.HasValue && searchSpan.Bytes[..expectedForwardMatch.Value.Start].IsEmpty, searchSpan.StartsWith(searchTerm, comparison));
- Assert.Equal(expectedBackwardMatch.HasValue && searchSpan.Bytes[expectedBackwardMatch.Value.End..].IsEmpty, searchSpan.EndsWith(searchTerm, comparison));
- }
- });
- }
-
- [Theory]
- [MemberData(nameof(TryFindData_Utf8Span_Ordinal))]
- public static void TryFind_Utf8Span_Ordinal(ustring source, ustring searchTerm, Range? expectedForwardMatch, Range? expectedBackwardMatch)
- {
- using BoundedUtf8Span boundedSpan = new BoundedUtf8Span(source.AsBytes());
- Utf8Span searchSpan = boundedSpan.Span;
- source = null; // to avoid accidentally using this for the remainder of the test
-
- // First, search forward
-
- bool wasFound = searchSpan.TryFind(searchTerm, out Range actualForwardMatch);
- Assert.Equal(expectedForwardMatch.HasValue, wasFound);
-
- if (wasFound)
- {
- AssertRangesEqual(searchSpan.Length, expectedForwardMatch.Value, actualForwardMatch);
- }
-
- // Also check Contains / StartsWith / SplitOn
-
- Assert.Equal(wasFound, searchSpan.Contains(searchTerm));
- Assert.Equal(wasFound && searchSpan.Bytes[..actualForwardMatch.Start].IsEmpty, searchSpan.StartsWith(searchTerm));
-
- (var before, var after) = searchSpan.SplitOn(searchTerm);
- if (wasFound)
- {
- Assert.True(searchSpan.Bytes[..actualForwardMatch.Start] == before.Bytes); // check for referential equality
- Assert.True(searchSpan.Bytes[actualForwardMatch.End..] == after.Bytes); // check for referential equality
- }
- else
- {
- Assert.True(searchSpan.Bytes == before.Bytes); // check for reference equality
- Assert.True(after.IsNull());
- }
-
- // Now search backward
-
- wasFound = searchSpan.TryFindLast(searchTerm, out Range actualBackwardMatch);
- Assert.Equal(expectedBackwardMatch.HasValue, wasFound);
-
- if (wasFound)
- {
- AssertRangesEqual(searchSpan.Length, expectedBackwardMatch.Value, actualBackwardMatch);
- }
-
- // Also check EndsWith / SplitOnLast
-
- Assert.Equal(wasFound && searchSpan.Bytes[actualBackwardMatch.End..].IsEmpty, searchSpan.EndsWith(searchTerm));
-
- (before, after) = searchSpan.SplitOnLast(searchTerm);
- if (wasFound)
- {
- Assert.True(searchSpan.Bytes[..actualBackwardMatch.Start] == before.Bytes); // check for referential equality
- Assert.True(searchSpan.Bytes[actualBackwardMatch.End..] == after.Bytes); // check for referential equality
- }
- else
- {
- Assert.True(searchSpan.Bytes == before.Bytes); // check for reference equality
- Assert.True(after.IsNull());
- }
- }
-
- [PlatformSpecific(TestPlatforms.Windows)]
- [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNlsGlobalization))]
- [MemberData(nameof(TryFindData_Utf8Span_WithComparison))]
- public static void TryFind_Utf8Span_WithComparison(ustring source, ustring searchTerm, StringComparison comparison, CultureInfo currentCulture, Range? expectedForwardMatch, Range? expectedBackwardMatch)
- {
- RunOnDedicatedThread(() =>
- {
- using BoundedUtf8Span boundedSpan = new BoundedUtf8Span(source.AsBytes());
- Utf8Span searchSpan = boundedSpan.Span;
- source = null; // to avoid accidentally using this for the remainder of the test
-
- if (currentCulture != null)
- {
- CultureInfo.CurrentCulture = currentCulture;
- }
-
- if (IsTryFindSupported(comparison))
- {
- // First, search forward
-
- bool wasFound = searchSpan.TryFind(searchTerm, comparison, out Range actualForwardMatch);
- Assert.Equal(expectedForwardMatch.HasValue, wasFound);
-
- if (wasFound)
- {
- AssertRangesEqual(searchSpan.Length, expectedForwardMatch.Value, actualForwardMatch);
- }
-
- // Also check Contains / StartsWith / SplitOn
-
- Assert.Equal(wasFound, searchSpan.Contains(searchTerm, comparison));
- Assert.Equal(wasFound && searchSpan.Bytes[..actualForwardMatch.Start].IsEmpty, searchSpan.StartsWith(searchTerm, comparison));
-
- (var before, var after) = searchSpan.SplitOn(searchTerm, comparison);
- if (wasFound)
- {
- Assert.True(searchSpan.Bytes[..actualForwardMatch.Start] == before.Bytes); // check for referential equality
- Assert.True(searchSpan.Bytes[actualForwardMatch.End..] == after.Bytes); // check for referential equality
- }
- else
- {
- Assert.True(searchSpan.Bytes == before.Bytes); // check for reference equality
- Assert.True(after.IsNull());
- }
-
- // Now search backward
-
- wasFound = searchSpan.TryFindLast(searchTerm, comparison, out Range actualBackwardMatch);
- Assert.Equal(expectedBackwardMatch.HasValue, wasFound);
-
- if (wasFound)
- {
- AssertRangesEqual(searchSpan.Length, expectedBackwardMatch.Value, actualBackwardMatch);
- }
-
- // Also check EndsWith / SplitOnLast
-
- Assert.Equal(wasFound && searchSpan.Bytes[actualBackwardMatch.End..].IsEmpty, searchSpan.EndsWith(searchTerm, comparison));
-
- (before, after) = searchSpan.SplitOnLast(searchTerm, comparison);
- if (wasFound)
- {
- Assert.True(searchSpan.Bytes[..actualBackwardMatch.Start] == before.Bytes); // check for referential equality
- Assert.True(searchSpan.Bytes[actualBackwardMatch.End..] == after.Bytes); // check for referential equality
- }
- else
- {
- Assert.True(searchSpan.Bytes == before.Bytes); // check for reference equality
- Assert.True(after.IsNull());
- }
- }
- else
- {
- Assert.Throws<NotSupportedException>(() => boundedSpan.Span.TryFind(searchTerm, comparison, out var _));
- Assert.Throws<NotSupportedException>(() => boundedSpan.Span.TryFindLast(searchTerm, comparison, out var _));
- Assert.Throws<NotSupportedException>(() => boundedSpan.Span.SplitOn(searchTerm, comparison));
- Assert.Throws<NotSupportedException>(() => boundedSpan.Span.SplitOnLast(searchTerm, comparison));
-
- Assert.Equal(expectedForwardMatch.HasValue, searchSpan.Contains(searchTerm, comparison));
- Assert.Equal(expectedForwardMatch.HasValue && searchSpan.Bytes[..expectedForwardMatch.Value.Start].IsEmpty, searchSpan.StartsWith(searchTerm, comparison));
- Assert.Equal(expectedBackwardMatch.HasValue && searchSpan.Bytes[expectedBackwardMatch.Value.End..].IsEmpty, searchSpan.EndsWith(searchTerm, comparison));
- }
- });
- }
- }
-}
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System.Buffers;
-using System.Collections.Generic;
-using System.Linq;
-
-using static System.Tests.Utf8TestUtilities;
-
-using ustring = System.Utf8String;
-
-namespace System.Text.Tests
-{
- public unsafe partial class Utf8SpanTests
- {
- /// <summary>
- /// All <see cref="Rune"/>s, U+0000..U+D800 and U+E000..U+10FFFF.
- /// </summary>
- private static IEnumerable<Rune> AllRunes
- {
- get
- {
- for (uint i = 0; i < 0xD800; i++)
- {
- yield return new Rune(i);
- }
- for (uint i = 0xE000; i <= 0x10FFFF; i++)
- {
- yield return new Rune(i);
- }
- }
- }
-
- /// <summary>
- /// All <see cref="Rune"/>s where <see cref="Rune.IsWhiteSpace(Rune)"/> returns <see langword="true"/>.
- /// </summary>
- private static readonly Lazy<Rune[]> WhiteSpaceRunes = new Lazy<Rune[]>(() => AllRunes.Where(Rune.IsWhiteSpace).ToArray());
-
- public static IEnumerable<object[]> Trim_TestData()
- {
- string[] testData = new string[]
- {
- null, // null
- "", // empty
- "\0", // contains null character - shouldn't be trimmed
- "Hello", // simple non-whitespace ASCII data
- "\u0009Hello\u000d", // C0 whitespace characters
- "\u0009\u0008\u0009Hello\u000e\u000b", // C0 whitespace + non-whitespace characters
- " Hello! ", // simple space chars (plus !, since it's adjacent to U+0020 SPACE)
- "\u0085\u0084\u0086\u0085", // U+0085 NEXT LINE (NEL), surrounded by adjacent non-whitespace chars
- };
-
- foreach (string entry in testData)
- {
- yield return new object[] { entry };
- }
-
- // A string with every possible whitespace character, just to test the limits
-
- StringBuilder builder = new StringBuilder();
- foreach (Rune whitespaceRune in WhiteSpaceRunes.Value)
- {
- builder.Append(whitespaceRune);
- }
- builder.Append("xyz");
- foreach (Rune whitespaceRune in WhiteSpaceRunes.Value)
- {
- builder.Append(whitespaceRune);
- }
-
- yield return new object[] { builder.ToString() };
- }
-
- private static bool TryParseSearchTermAsChar(object searchTerm, out char parsed)
- {
- if (searchTerm is char ch)
- {
- parsed = ch;
- return true;
- }
- else if (searchTerm is Rune r)
- {
- if (r.IsBmp)
- {
- parsed = (char)r.Value;
- return true;
- }
- }
- else if (searchTerm is string str)
- {
- if (str.Length == 1)
- {
- parsed = str[0];
- return true;
- }
- }
- else if (searchTerm is ustring ustr)
- {
- var enumerator = ustr.Chars.GetEnumerator();
- if (enumerator.MoveNext())
- {
- parsed = enumerator.Current;
- if (!enumerator.MoveNext())
- {
- return true;
- }
- }
- }
-
- parsed = default; // failed to turn the search term into a single char
- return false;
- }
-
- private static bool TryParseSearchTermAsRune(object searchTerm, out Rune parsed)
- {
- if (searchTerm is char ch)
- {
- return Rune.TryCreate(ch, out parsed);
- }
- else if (searchTerm is Rune r)
- {
- parsed = r;
- return true;
- }
- else if (searchTerm is string str)
- {
- if (Rune.DecodeFromUtf16(str.AsSpan(), out parsed, out int charsConsumed) == OperationStatus.Done
- && charsConsumed == str.Length)
- {
- return true;
- }
- }
- else if (searchTerm is ustring ustr)
- {
- if (Rune.DecodeFromUtf8(ustr.AsBytes(), out parsed, out int bytesConsumed) == OperationStatus.Done
- && bytesConsumed == ustr.Length)
- {
- return true;
- }
- }
-
- parsed = default; // failed to turn the search term into a single Rune
- return false;
- }
-
- private static bool TryParseSearchTermAsUtf8String(object searchTerm, out ustring parsed)
- {
- if (searchTerm is char ch)
- {
- if (Rune.TryCreate(ch, out Rune rune))
- {
- parsed = rune.ToUtf8String();
- return true;
- }
- }
- else if (searchTerm is Rune r)
- {
- parsed = r.ToUtf8String();
- return true;
- }
- else if (searchTerm is string str)
- {
- if (ustring.TryCreateFrom(str.AsSpan(), out parsed))
- {
- return true;
- }
- }
- else if (searchTerm is ustring ustr)
- {
- parsed = ustr;
- return true;
- }
-
- parsed = default; // failed to turn the search term into a ustring
- return false;
- }
- }
-}
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System.Buffers;
-using System.Collections.Generic;
-using System.Globalization;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-using System.Tests;
-using Xunit;
-
-using static System.Tests.Utf8TestUtilities;
-
-using ustring = System.Utf8String;
-
-namespace System.Text.Tests
-{
- [SkipOnMono("The features from System.Utf8String.Experimental namespace are experimental.")]
- public unsafe partial class Utf8SpanTests
- {
- [Fact]
- public static void BytesProperty_FromCustomBytes()
- {
- byte[] bytes = Encoding.UTF8.GetBytes("Hello!");
- Assert.True(bytes.AsSpan() == Utf8Span.UnsafeCreateWithoutValidation(bytes).Bytes);
- }
-
- [Fact]
- public static void BytesProperty_FromEmpty()
- {
- Assert.True(Utf8Span.Empty.Bytes == ReadOnlySpan<byte>.Empty);
- }
-
- [Fact]
- public static void BytesProperty_FromUtf8String()
- {
- ustring ustr = u8("Hello!");
- Utf8Span uspan = new Utf8Span(ustr);
-
- Assert.True(ustr.AsBytes() == uspan.Bytes);
- }
-
- [Fact]
- public static void EmptyProperty()
- {
- // Act
-
- Utf8Span span = Utf8Span.Empty;
-
- // Assert
- // GetPinnableReference should be 'null' to match behavior of empty ROS<byte>.GetPinnableReference();
-
- Assert.True(span.IsEmpty);
- Assert.Equal(IntPtr.Zero, (IntPtr)(void*)Unsafe.AsPointer(ref Unsafe.AsRef(in span.GetPinnableReference())));
- Assert.Equal(IntPtr.Zero, (IntPtr)(void*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(span.Bytes)));
- Assert.Equal(0, span.Length);
- }
-
- [Fact]
- public static void GetHashCode_Ordinal()
- {
- // Generate 17 all-null strings and make sure they have unique hash codes.
- // Assuming Marvin32 is a good PRF and has a full 32-bit output domain, we should
- // expect this test to fail only once every ~30 million runs due to the birthday paradox.
- // That should be good enough for inclusion as a unit test.
-
- HashSet<int> seenHashCodes = new HashSet<int>();
-
- for (int i = 0; i <= 16; i++)
- {
- using BoundedUtf8Span boundedSpan = new BoundedUtf8Span(new byte[i]);
- Utf8Span span = boundedSpan.Span;
-
- Assert.True(seenHashCodes.Add(span.GetHashCode()), "This hash code was previously seen.");
- }
- }
-
- [PlatformSpecific(TestPlatforms.Windows)]
- [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNlsGlobalization))]
- public static void GetHashCode_WithComparison()
- {
- // Since hash code generation is randomized, it's possible (though unlikely) that
- // we might see unanticipated collisions. It's ok if this unit test fails once in
- // every few million runs, but if the unit test becomes truly flaky then that would
- // be indicative of a larger problem with hash code generation.
- //
- // These tests also make sure that the hash code is computed over the buffer rather
- // than over the reference.
-
- // Ordinal
-
- {
- using BoundedUtf8Span boundedSpan = new BoundedUtf8Span("ababaaAA");
- Utf8Span span = boundedSpan.Span;
-
- Assert.Equal(span[0..2].GetHashCode(StringComparison.Ordinal), span[2..4].GetHashCode(StringComparison.Ordinal));
- Assert.NotEqual(span[4..6].GetHashCode(StringComparison.Ordinal), span[6..8].GetHashCode(StringComparison.Ordinal));
- Assert.Equal(Utf8Span.Empty.GetHashCode(StringComparison.Ordinal), span[^0..].GetHashCode(StringComparison.Ordinal)); // null should equal empty
- }
-
- // OrdinalIgnoreCase
-
- {
- using BoundedUtf8Span boundedSpan = new BoundedUtf8Span("ababaaAA");
- Utf8Span span = boundedSpan.Span;
-
- Assert.Equal(span[0..2].GetHashCode(StringComparison.OrdinalIgnoreCase), span[2..4].GetHashCode(StringComparison.OrdinalIgnoreCase));
- Assert.Equal(span[4..6].GetHashCode(StringComparison.OrdinalIgnoreCase), span[6..8].GetHashCode(StringComparison.OrdinalIgnoreCase));
- Assert.NotEqual(span[0..2].GetHashCode(StringComparison.OrdinalIgnoreCase), span[6..8].GetHashCode(StringComparison.OrdinalIgnoreCase));
- Assert.Equal(Utf8Span.Empty.GetHashCode(StringComparison.OrdinalIgnoreCase), span[^0..].GetHashCode(StringComparison.OrdinalIgnoreCase)); // null should equal empty
- }
-
- // InvariantCulture
-
- {
- using BoundedUtf8Span boundedSpan = new BoundedUtf8Span("ae\u00e6AE\u00c6"); // U+00E6 = 'æ' LATIN SMALL LETTER AE, U+00E6 = 'Æ' LATIN CAPITAL LETTER AE
- Utf8Span span = boundedSpan.Span;
-
- Assert.Equal(span[0..2].GetHashCode(StringComparison.InvariantCulture), span[2..4].GetHashCode(StringComparison.InvariantCulture));
- Assert.NotEqual(span[0..2].GetHashCode(StringComparison.InvariantCulture), span[4..6].GetHashCode(StringComparison.InvariantCulture));
- Assert.Equal(Utf8Span.Empty.GetHashCode(StringComparison.InvariantCulture), span[^0..].GetHashCode(StringComparison.InvariantCulture)); // null should equal empty
- }
-
- // InvariantCultureIgnoreCase
-
- {
- using BoundedUtf8Span boundedSpan = new BoundedUtf8Span("ae\u00e6AE\u00c6EA");
- Utf8Span span = boundedSpan.Span;
-
- Assert.Equal(span[0..2].GetHashCode(StringComparison.InvariantCultureIgnoreCase), span[2..4].GetHashCode(StringComparison.InvariantCultureIgnoreCase));
- Assert.Equal(span[0..2].GetHashCode(StringComparison.InvariantCultureIgnoreCase), span[6..8].GetHashCode(StringComparison.InvariantCultureIgnoreCase));
- Assert.NotEqual(span[0..2].GetHashCode(StringComparison.InvariantCultureIgnoreCase), span[8..10].GetHashCode(StringComparison.InvariantCultureIgnoreCase));
- Assert.Equal(Utf8Span.Empty.GetHashCode(StringComparison.InvariantCultureIgnoreCase), span[^0..].GetHashCode(StringComparison.InvariantCultureIgnoreCase)); // null should equal empty
- }
-
- // Invariant culture should not match Turkish I case conversion
-
- {
- using BoundedUtf8Span boundedSpan = new BoundedUtf8Span("i\u0130"); // U+0130 = 'İ' LATIN CAPITAL LETTER I WITH DOT ABOVE
- Utf8Span span = boundedSpan.Span;
-
- Assert.NotEqual(span[0..1].GetHashCode(StringComparison.InvariantCultureIgnoreCase), span[1..3].GetHashCode(StringComparison.InvariantCultureIgnoreCase));
- }
-
- // CurrentCulture (we'll use tr-TR)
-
- RunOnDedicatedThread(() =>
- {
- using BoundedUtf8Span boundedSpan = new BoundedUtf8Span("i\u0131\u0130Ii\u0131\u0130I"); // U+0131 = 'ı' LATIN SMALL LETTER DOTLESS I
- Utf8Span span = boundedSpan.Span;
-
- CultureInfo.CurrentCulture = CultureInfo.GetCultureInfo("tr-TR");
-
- Assert.Equal(span[0..6].GetHashCode(StringComparison.CurrentCulture), span[6..12].GetHashCode(StringComparison.CurrentCulture));
- Assert.NotEqual(span[0..1].GetHashCode(StringComparison.CurrentCulture), span[1..3].GetHashCode(StringComparison.CurrentCulture));
- Assert.Equal(Utf8Span.Empty.GetHashCode(StringComparison.CurrentCulture), span[^0..].GetHashCode(StringComparison.CurrentCulture)); // null should equal empty
- });
-
- // CurrentCultureIgnoreCase (we'll use tr-TR)
-
- RunOnDedicatedThread(() =>
- {
- using BoundedUtf8Span boundedSpan = new BoundedUtf8Span("i\u0131\u0130Ii\u0131\u0130I");
- Utf8Span span = boundedSpan.Span;
-
- CultureInfo.CurrentCulture = CultureInfo.GetCultureInfo("tr-TR");
-
- Assert.Equal(span[0..6].GetHashCode(StringComparison.CurrentCultureIgnoreCase), span[6..12].GetHashCode(StringComparison.CurrentCultureIgnoreCase));
- Assert.NotEqual(span[0..1].GetHashCode(StringComparison.CurrentCultureIgnoreCase), span[1..3].GetHashCode(StringComparison.CurrentCultureIgnoreCase)); // 'i' shouldn't match 'ı'
- Assert.Equal(span[0..1].GetHashCode(StringComparison.CurrentCultureIgnoreCase), span[3..5].GetHashCode(StringComparison.CurrentCultureIgnoreCase)); // 'i' should match 'İ'
- Assert.NotEqual(span[0..1].GetHashCode(StringComparison.CurrentCultureIgnoreCase), span[5..6].GetHashCode(StringComparison.CurrentCultureIgnoreCase)); // 'i' shouldn't match 'I'
- Assert.Equal(Utf8Span.Empty.GetHashCode(StringComparison.CurrentCultureIgnoreCase), span[^0..].GetHashCode(StringComparison.CurrentCultureIgnoreCase)); // null should equal empty
- });
- }
-
- [Theory]
- [InlineData("", true)]
- [InlineData("Hello", true)]
- [InlineData("\u1234", false)]
- public static void IsAscii(string input, bool expected)
- {
- using BoundedUtf8Span boundedSpan = new BoundedUtf8Span(input);
-
- Assert.Equal(expected, boundedSpan.Span.IsAscii());
- }
-
- [Theory]
- [InlineData(null, true)]
- [InlineData("", true)]
- [InlineData(" \u2028\u2029\t\v", true)]
- [InlineData(" x\r\n", false)]
- [InlineData("\r\nhello\r\n", false)]
- [InlineData("\r\n\0\r\n", false)]
- [InlineData("\r\n\r\n", true)]
- public static void IsEmptyOrWhiteSpace(string input, bool expected)
- {
- using BoundedUtf8Span boundedSpan = new BoundedUtf8Span(input);
-
- Assert.Equal(expected, boundedSpan.Span.IsEmptyOrWhiteSpace());
- }
-
- [Theory]
- [InlineData("", "..")]
- [InlineData("Hello", "1..")]
- [InlineData("Hello", "1..2")]
- [InlineData("Hello", "1..^2")]
- [InlineData("Hello", "^2..")]
- [InlineData("Hello", "^0..")]
- [InlineData("résumé", "1..^2")] // include first 'é', exclude last 'é'
- [InlineData("résumé", "^2..")] // include only last 'é'
- public static void Indexer_Success(string input, string rangeExpression)
- {
- Range range = ParseRangeExpr(rangeExpression);
-
- using BoundedUtf8Span boundedSpan = new BoundedUtf8Span(input);
- Utf8Span originalSpan = boundedSpan.Span;
- Utf8Span slicedSpan = originalSpan[range]; // shouldn't throw
-
- ref byte startOfOriginalSpan = ref MemoryMarshal.GetReference(originalSpan.Bytes);
- ref byte startOfSlicedSpan = ref MemoryMarshal.GetReference(slicedSpan.Bytes);
-
- // Now ensure the slice was correctly produced by comparing the references directly.
-
- (int offset, int length) = range.GetOffsetAndLength(originalSpan.Length);
- Assert.True(Unsafe.AreSame(ref startOfSlicedSpan, ref Unsafe.Add(ref startOfOriginalSpan, offset)));
- Assert.Equal(length, slicedSpan.Length);
- }
-
- [Theory]
- [InlineData("résumé", "2..")] // try to split the first 'é'
- [InlineData("résumé", "..^1")] // try to split the last 'é'
- public static void Indexer_ThrowsIfTryToSplitMultiByteSubsequence(string input, string rangeExpression)
- {
- Range range = ParseRangeExpr(rangeExpression);
-
- using BoundedUtf8Span boundedSpan = new BoundedUtf8Span(input);
-
- Assert.Throws<InvalidOperationException>(() => { var _ = boundedSpan.Span[range]; });
- }
-
-
- [Theory]
- [MemberData(nameof(TranscodingTestData))]
- public static void ToCharArrayTest(string expected)
- {
- // Arrange
-
- using BoundedUtf8Span boundedSpan = new BoundedUtf8Span(expected);
- Utf8Span span = boundedSpan.Span;
-
- // Act
-
- char[] returned = span.ToCharArray();
-
- // Assert
-
- Assert.Equal(expected, returned); // IEnumerable<char>
- }
-
- [Fact]
- public static void ToCharArrayTest_Null()
- {
- Assert.Same(Array.Empty<char>(), Utf8Span.Empty.ToCharArray());
- }
-
- [Theory]
- [MemberData(nameof(TranscodingTestData))]
- public static void ToCharsTest(string expected)
- {
- // Arrange
-
- using BoundedUtf8Span boundedSpan = new BoundedUtf8Span(expected);
- Utf8Span span = boundedSpan.Span;
-
- // Act & assert, first with improperly-sized buffer
-
- if (expected.Length > 0)
- {
- using BoundedMemory<char> boundedMemory = BoundedMemory.Allocate<char>(expected.Length - 1);
- Assert.Equal(-1, span.ToChars(boundedMemory.Span));
- }
-
- // Then with properly-sized buffer and too-large buffer
-
- for (int i = expected.Length; i <= expected.Length + 1; i++)
- {
- using BoundedMemory<char> boundedMemory = BoundedMemory.Allocate<char>(i);
- Assert.Equal(expected.Length, span.ToChars(boundedMemory.Span));
- Assert.True(boundedMemory.Span.Slice(0, expected.Length).SequenceEqual(expected.AsSpan()));
- }
- }
-
- [Fact]
- public static void ToCharsTest_Null()
- {
- for (int i = 0; i <= 1; i++) // test both with properly-sized buffer and with too-large buffer
- {
- using BoundedMemory<char> boundedMemory = BoundedMemory.Allocate<char>(i);
- Assert.Equal(0, Utf8Span.Empty.ToChars(boundedMemory.Span));
- }
- }
-
- [Theory]
- [MemberData(nameof(TranscodingTestData))]
- public static void ToStringTest(string expected)
- {
- // Arrange
-
- using BoundedUtf8Span boundedSpan = new BoundedUtf8Span(expected);
- Utf8Span span = boundedSpan.Span;
-
- // Act & assert
-
- Assert.Equal(expected, span.ToString());
- }
-
- [Fact]
- public static void ToStringTest_Null()
- {
- Assert.Same(string.Empty, Utf8Span.Empty.ToString());
- }
-
- [Theory]
- [MemberData(nameof(TranscodingTestData))]
- public static void ToUtf8StringTest(string expected)
- {
- // Arrange
-
- ustring utf8 = u8(expected);
-
- using BoundedUtf8Span boundedSpan = new BoundedUtf8Span(expected);
- Utf8Span span = boundedSpan.Span;
-
- // Act & assert
-
- Assert.Equal(utf8, span.ToUtf8String());
- }
-
- [Fact]
- public static void ToUtf8StringTest_Null()
- {
- Assert.Same(ustring.Empty, Utf8Span.Empty.ToUtf8String());
- }
-
- public static IEnumerable<object[]> TranscodingTestData()
- {
- yield return new object[] { "" }; // empty
- yield return new object[] { "Hello" }; // simple ASCII
- yield return new object[] { "a\U00000123b\U00001234c\U00101234d" }; // with multi-byte sequences of varying lengths
- yield return new object[] { "\uF8FF\uE000\U000FFFFF" }; // with scalars from the private use areas
- }
- }
-}
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using Xunit;
-
-using static System.Tests.Utf8TestUtilities;
-
-namespace System.Tests
-{
- public unsafe partial class Utf8StringTests
- {
- [Fact]
- public static void AreEquivalent_Utf8StringAndString_NullHandling()
- {
- Assert.True(Utf8String.AreEquivalent((Utf8String)null, (string)null));
- Assert.False(Utf8String.AreEquivalent(Utf8String.Empty, (string)null));
- Assert.False(Utf8String.AreEquivalent((Utf8String)null, string.Empty));
- Assert.True(Utf8String.AreEquivalent(Utf8String.Empty, string.Empty));
- }
-
- [Theory]
- [InlineData("Hello", "hello", false)]
- [InlineData("hello", "hello", true)]
- [InlineData("hello", "helloo", false)]
- [InlineData("hellooo", "helloo", false)]
- [InlineData("encyclopaedia", "encyclopaedia", true)]
- [InlineData("encyclopaedia", "encyclop\u00e6dia", false)]
- [InlineData("encyclop\u00e6dia", "encyclop\u00e6dia", true)]
- public static void AreEquivalent_Tests(string utf8Input, string utf16Input, bool expected)
- {
- Utf8String asUtf8 = u8(utf8Input);
-
- // Call all three overloads
-
- Assert.Equal(expected, Utf8String.AreEquivalent(asUtf8, utf16Input));
- Assert.Equal(expected, Utf8String.AreEquivalent(asUtf8.AsSpan(), utf16Input.AsSpan()));
- Assert.Equal(expected, Utf8String.AreEquivalent(asUtf8.AsBytes(), utf16Input.AsSpan()));
- }
-
- [Theory]
- [InlineData(new byte[] { 0xED, 0xA0, 0x80 }, new char[] { '\uD800' })] // don't support "wobbly" UTF-8
- [InlineData(new byte[] { 0xED, 0xA0, 0x80, 0xED, 0xBF, 0xBF }, new char[] { '\uD800', '\uDFFF' })] // don't support "wobbly" UTF-8
- [InlineData(new byte[] { 0xED }, new char[] { '\uD800' })] // don't support partials
- public static void AreEquivalent_IllFormedData_AlwaysReturnsFalse(byte[] asUtf8, char[] asUtf16)
- {
- Assert.False(Utf8String.AreEquivalent(asUtf8, asUtf16));
- }
- }
-}
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System.Collections.Generic;
-using System.Globalization;
-using System.Text;
-using System.Text.Tests;
-using Xunit;
-
-using static System.Tests.Utf8TestUtilities;
-
-namespace System.Tests
-{
- public unsafe partial class Utf8StringTests
- {
- public static IEnumerable<object[]> NormalizationData() => Utf8SpanTests.NormalizationData();
-
- [Theory]
- [MemberData(nameof(NormalizationData))]
- public static void Normalize(string utf16Source, string utf16Expected, NormalizationForm normalizationForm)
- {
- Utf8String utf8Source = u8(utf16Source);
-
- // Quick IsNormalized tests
-
- Assert.Equal(utf16Source == utf16Expected, utf8Source.IsNormalized(normalizationForm));
-
- // Normalize and return new Utf8String instances
-
- Utf8String utf8Normalized = utf8Source.Normalize(normalizationForm);
- Assert.True(Utf8String.AreEquivalent(utf8Normalized, utf16Expected));
- }
-
- public static IEnumerable<object[]> CaseConversionData() => Utf8SpanTests.CaseConversionData();
-
- [Theory]
- [MemberData(nameof(CaseConversionData))]
- public static void ToLower(string testData)
- {
- static void RunTest(string testData, string expected, CultureInfo culture)
- {
- if (culture is null)
- {
- Assert.Equal(u8(expected), u8(testData).ToLowerInvariant());
- }
- else
- {
- Assert.Equal(u8(expected), u8(testData).ToLower(culture));
- }
- }
-
- if (testData is null)
- {
- return; // no point in testing null "this" objects; we'll just null-ref
- }
-
- RunTest(testData, testData.ToLowerInvariant(), null);
- RunTest(testData, testData.ToLower(CultureInfo.InvariantCulture), CultureInfo.InvariantCulture);
- RunTest(testData, testData.ToLower(CultureInfo.GetCultureInfo("en-US")), CultureInfo.GetCultureInfo("en-US"));
- RunTest(testData, testData.ToLower(CultureInfo.GetCultureInfo("tr-TR")), CultureInfo.GetCultureInfo("tr-TR"));
- }
-
- [Theory]
- [MemberData(nameof(CaseConversionData))]
- public static void ToUpper(string testData)
- {
- static void RunTest(string testData, string expected, CultureInfo culture)
- {
- if (culture is null)
- {
- Assert.Equal(u8(expected), u8(testData).ToUpperInvariant());
- }
- else
- {
- Assert.Equal(u8(expected), u8(testData).ToUpper(culture));
- }
- }
-
- if (testData is null)
- {
- return; // no point in testing null "this" objects; we'll just null-ref
- }
-
- RunTest(testData, testData.ToUpperInvariant(), null);
- RunTest(testData, testData.ToUpper(CultureInfo.InvariantCulture), CultureInfo.InvariantCulture);
- RunTest(testData, testData.ToUpper(CultureInfo.GetCultureInfo("en-US")), CultureInfo.GetCultureInfo("en-US"));
- RunTest(testData, testData.ToUpper(CultureInfo.GetCultureInfo("tr-TR")), CultureInfo.GetCultureInfo("tr-TR"));
- }
- }
-}
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System.Buffers;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-using Xunit;
-
-using static System.Tests.Utf8TestUtilities;
-
-namespace System.Tests
-{
- public unsafe partial class Utf8StringTests
- {
- [Fact]
- public static void Ctor_ByteArrayOffset_Empty_ReturnsEmpty()
- {
- byte[] inputData = new byte[] { (byte)'H', (byte)'e', (byte)'l', (byte)'l', (byte)'o' };
- AssertSameAsEmpty(new Utf8String(inputData, 3, 0));
- }
-
- [Fact]
- public static void Ctor_ByteArrayOffset_ValidData_ReturnsOriginalContents()
- {
- byte[] inputData = new byte[] { (byte)'x', (byte)'H', (byte)'e', (byte)'l', (byte)'l', (byte)'o', (byte)'x' };
- Utf8String expected = u8("Hello");
-
- var actual = new Utf8String(inputData, 1, 5);
- Assert.Equal(expected, actual);
- }
-
- [Fact]
- public static void Ctor_ByteArrayOffset_InvalidData_Throws()
- {
- byte[] inputData = new byte[] { (byte)'x', (byte)'H', (byte)'e', (byte)0xFF, (byte)'l', (byte)'o', (byte)'x' };
-
- Assert.Throws<ArgumentException>(() => new Utf8String(inputData, 0, inputData.Length));
- }
-
- [Fact]
- public static void Ctor_ByteArrayOffset_NullValue_Throws()
- {
- var exception = Assert.Throws<ArgumentNullException>(() => new Utf8String((byte[])null, 0, 0));
- Assert.Equal("value", exception.ParamName);
- }
-
- [Fact]
- public static void Ctor_ByteArrayOffset_InvalidStartIndexOrLength_Throws()
- {
- byte[] inputData = new byte[] { (byte)'H', (byte)'e', (byte)'l', (byte)'l', (byte)'o' };
-
- Assert.Throws<ArgumentOutOfRangeException>(() => new Utf8String(inputData, 1, 5));
- }
-
- [Fact]
- public static void Ctor_BytePointer_Null_Throws()
- {
- var exception = Assert.Throws<ArgumentNullException>(() => new Utf8String((byte*)null));
- Assert.Equal("value", exception.ParamName);
- }
-
- [Fact]
- public static void Ctor_BytePointer_Empty_ReturnsEmpty()
- {
- byte[] inputData = new byte[] { 0 }; // standalone null byte
-
- using (BoundedMemory<byte> boundedMemory = BoundedMemory.AllocateFromExistingData(inputData))
- {
- AssertSameAsEmpty(new Utf8String((byte*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(boundedMemory.Span))));
- }
- }
-
- [Fact]
- public static void Ctor_BytePointer_ValidData_ReturnsOriginalContents()
- {
- byte[] inputData = new byte[] { (byte)'H', (byte)'e', (byte)'l', (byte)'l', (byte)'o', (byte)'\0' };
-
- using (BoundedMemory<byte> boundedMemory = BoundedMemory.AllocateFromExistingData(inputData))
- {
- Assert.Equal(u8("Hello"), new Utf8String((byte*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(boundedMemory.Span))));
- }
- }
-
- [Fact]
- public static void Ctor_BytePointer_InvalidData_Throws()
- {
- byte[] inputData = new byte[] { (byte)'H', (byte)'e', (byte)0xFF, (byte)'l', (byte)'o', (byte)'\0' };
-
- using (BoundedMemory<byte> boundedMemory = BoundedMemory.AllocateFromExistingData(inputData))
- {
- Assert.Throws<ArgumentException>(() => new Utf8String((byte*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(boundedMemory.Span))));
- }
- }
-
- [Fact]
- public static void Ctor_ByteSpan_Empty_ReturnsEmpty()
- {
- AssertSameAsEmpty(new Utf8String(ReadOnlySpan<byte>.Empty));
- }
-
- [Fact]
- public static void Ctor_ByteSpan_ValidData_ReturnsOriginalContents()
- {
- byte[] inputData = new byte[] { (byte)'H', (byte)'e', (byte)'l', (byte)'l', (byte)'o' };
- Utf8String expected = u8("Hello");
-
- var actual = new Utf8String(inputData.AsSpan());
- Assert.Equal(expected, actual);
- }
-
- [Fact]
- public static void Ctor_ByteSpan_InvalidData_Throws()
- {
- byte[] inputData = new byte[] { (byte)'H', (byte)'e', (byte)0xFF, (byte)'l', (byte)'o' };
-
- Assert.Throws<ArgumentException>(() => new Utf8String(inputData.AsSpan()));
- }
-
- [Fact]
- public static void Ctor_CharArrayOffset_Empty_ReturnsEmpty()
- {
- char[] inputData = "H\U00012345ello".ToCharArray(); // ok to have an empty slice in the middle of a multi-byte subsequence
- AssertSameAsEmpty(new Utf8String(inputData, 3, 0));
- }
-
- [Fact]
- public static void Ctor_CharArrayOffset_ValidData_ReturnsAsUtf8()
- {
- char[] inputData = "H\U00012345\u07ffello".ToCharArray();
- Utf8String expected = u8("\u07ffello");
-
- var actual = new Utf8String(inputData, 3, 5);
- Assert.Equal(expected, actual);
- }
-
- [Fact]
- public static void Ctor_CharArrayOffset_InvalidData_Throws()
- {
- char[] inputData = "H\ud800ello".ToCharArray();
-
- Assert.Throws<ArgumentException>(() => new Utf8String(inputData, 0, inputData.Length));
- }
-
- [Fact]
- public static void Ctor_CharArrayOffset_NullValue_Throws()
- {
- var exception = Assert.Throws<ArgumentNullException>(() => new Utf8String((char[])null, 0, 0));
- Assert.Equal("value", exception.ParamName);
- }
-
- [Fact]
- public static void Ctor_CharArrayOffset_InvalidStartIndexOrLength_Throws()
- {
- char[] inputData = "Hello".ToCharArray();
-
- Assert.Throws<ArgumentOutOfRangeException>(() => new Utf8String(inputData, 1, 5));
- }
-
- [Fact]
- public static void Ctor_CharPointer_Null_Throws()
- {
- var exception = Assert.Throws<ArgumentNullException>(() => new Utf8String((char*)null));
- Assert.Equal("value", exception.ParamName);
- }
-
- [Fact]
- public static void Ctor_CharPointer_Empty_ReturnsEmpty()
- {
- char[] inputData = new char[] { '\0' }; // standalone null char
-
- using (BoundedMemory<char> boundedMemory = BoundedMemory.AllocateFromExistingData(inputData))
- {
- AssertSameAsEmpty(new Utf8String((char*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(boundedMemory.Span))));
- }
- }
-
- [Fact]
- public static void Ctor_CharPointer_ValidData_ReturnsOriginalContents()
- {
- char[] inputData = "Hello\0".ToCharArray(); // need to manually null-terminate
-
- using (BoundedMemory<char> boundedMemory = BoundedMemory.AllocateFromExistingData(inputData))
- {
- Assert.Equal(u8("Hello"), new Utf8String((char*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(boundedMemory.Span))));
- }
- }
-
- [Fact]
- public static void Ctor_CharPointer_InvalidData_Throws()
- {
- char[] inputData = "He\ud800llo\0".ToCharArray(); // need to manually null-terminate
-
- using (BoundedMemory<char> boundedMemory = BoundedMemory.AllocateFromExistingData(inputData))
- {
- Assert.Throws<ArgumentException>(() => new Utf8String((char*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(boundedMemory.Span))));
- }
- }
-
- [Fact]
- public static void Ctor_CharSpan_Empty_ReturnsEmpty()
- {
- AssertSameAsEmpty(new Utf8String(ReadOnlySpan<char>.Empty));
- }
-
- [Fact]
- public static void Ctor_CharSpan_ValidData_ReturnsOriginalContents()
- {
- char[] inputData = "Hello".ToCharArray();
- Utf8String expected = u8("Hello");
-
- var actual = new Utf8String(inputData.AsSpan());
- Assert.Equal(expected, actual);
- }
-
- [Fact]
- public static void Ctor_CharSpan_InvalidData_Throws()
- {
- char[] inputData = "He\ud800llo".ToCharArray();
-
- Assert.Throws<ArgumentException>(() => new Utf8String(inputData.AsSpan()));
- }
-
- [Fact]
- public static void Ctor_String_Null_Throws()
- {
- var exception = Assert.Throws<ArgumentNullException>(() => new Utf8String((string)null));
- Assert.Equal("value", exception.ParamName);
- }
-
- [Fact]
- public static void Ctor_String_Empty_ReturnsEmpty()
- {
- AssertSameAsEmpty(new Utf8String(string.Empty));
- }
-
- [Fact]
- public static void Ctor_String_ValidData_ReturnsOriginalContents()
- {
- Assert.Equal(u8("Hello"), new Utf8String("Hello"));
- }
-
- [Fact]
- public static void Ctor_String_Long_ReturnsOriginalContents()
- {
- string longString = new string('a', 500);
- Assert.Equal(u8(longString), new Utf8String(longString));
- }
-
- [Fact]
- public static void Ctor_String_InvalidData_Throws()
- {
- Assert.Throws<ArgumentException>(() => new Utf8String("He\uD800lo"));
- }
-
- [Fact]
- public static void Ctor_NonValidating_FromByteSpan()
- {
- byte[] inputData = new byte[] { (byte)'x', (byte)'y', (byte)'z' };
- Utf8String actual = Utf8String.UnsafeCreateWithoutValidation(inputData);
-
- Assert.Equal(u8("xyz"), actual);
- }
-
- [Fact]
- public static void Ctor_CreateFromRelaxed_Utf16()
- {
- Assert.Same(Utf8String.Empty, Utf8String.CreateFromRelaxed(ReadOnlySpan<char>.Empty));
- Assert.Equal(u8("xy\uFFFDz"), Utf8String.CreateFromRelaxed("xy\ud800z".AsSpan()));
- }
-
- [Fact]
- public static void Ctor_CreateFromRelaxed_Utf8()
- {
- Assert.Same(Utf8String.Empty, Utf8String.CreateFromRelaxed(ReadOnlySpan<byte>.Empty));
- Assert.Equal(u8("xy\uFFFDz"), Utf8String.CreateFromRelaxed(new byte[] { (byte)'x', (byte)'y', 0xF4, 0x80, 0x80, (byte)'z' }));
- }
-
- [Fact]
- public static void Ctor_TryCreateFrom_Utf8()
- {
- Utf8String value;
-
- // Empty string
-
- Assert.True(Utf8String.TryCreateFrom(ReadOnlySpan<byte>.Empty, out value));
- Assert.Same(Utf8String.Empty, value);
-
- // Well-formed ASCII contents
-
- Assert.True(Utf8String.TryCreateFrom(new byte[] { (byte)'H', (byte)'e', (byte)'l', (byte)'l', (byte)'o' }, out value));
- Assert.Equal(u8("Hello"), value);
-
- // Well-formed non-ASCII contents
-
- Assert.True(Utf8String.TryCreateFrom(new byte[] { 0xF0, 0x9F, 0x91, 0xBD }, out value)); // U+1F47D EXTRATERRESTRIAL ALIEN
- Assert.Equal(u8("\U0001F47D"), value);
-
- // Ill-formed contents
-
- Assert.False(Utf8String.TryCreateFrom(new byte[] { 0xF0, 0x9F, 0x91, (byte)'x' }, out value));
- Assert.Null(value);
- }
-
- [Fact]
- public static void Ctor_TryCreateFrom_Utf16()
- {
- Utf8String value;
-
- // Empty string
-
- Assert.True(Utf8String.TryCreateFrom(ReadOnlySpan<char>.Empty, out value));
- Assert.Same(Utf8String.Empty, value);
-
- // Well-formed ASCII contents
-
- Assert.True(Utf8String.TryCreateFrom("Hello".AsSpan(), out value));
- Assert.Equal(u8("Hello"), value);
-
- // Well-formed non-ASCII contents
-
- Assert.True(Utf8String.TryCreateFrom("\U0001F47D".AsSpan(), out value)); // U+1F47D EXTRATERRESTRIAL ALIEN
- Assert.Equal(u8("\U0001F47D"), value);
-
- // Ill-formed contents
-
- Assert.False(Utf8String.TryCreateFrom("\uD800x".AsSpan(), out value));
- Assert.Null(value);
- }
-
- private static void AssertSameAsEmpty(Utf8String value)
- {
-#if NETFRAMEWORK
- // When OOB, we can't change the actual object returned from a constructor.
- // So just assert the underlying "_bytes" is the same.
- Assert.Equal(0, value.Length);
- Assert.True(Unsafe.AreSame(
- ref Unsafe.AsRef(in Utf8String.Empty.GetPinnableReference()),
- ref Unsafe.AsRef(in value.GetPinnableReference())));
-#else
- Assert.Same(Utf8String.Empty, value);
-#endif
- }
- }
-}
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System.Buffers;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-using Xunit;
-
-using static System.Tests.Utf8TestUtilities;
-
-namespace System.Tests
-{
- public unsafe partial class Utf8StringTests
- {
- [Fact]
- public static void Ctor_NonValidating_FromDelegate()
- {
- object expectedState = new object();
- SpanAction<byte, object> spanAction = (span, actualState) =>
- {
- Assert.Same(expectedState, actualState);
- Assert.NotEqual(0, span.Length); // shouldn't have been called for a zero-length span
-
- for (int i = 0; i < span.Length; i++)
- {
- Assert.Equal(0, span[i]); // should've been zero-inited
- span[i] = (byte)('a' + (i % 26)); // writes "abc...xyzabc...xyz..."
- }
- };
-
- ArgumentException exception = Assert.Throws<ArgumentOutOfRangeException>(() => Utf8String.UnsafeCreateWithoutValidation(-1, expectedState, spanAction));
- Assert.Equal("length", exception.ParamName);
-
- exception = Assert.Throws<ArgumentNullException>(() => Utf8String.UnsafeCreateWithoutValidation(10, expectedState, action: null));
- Assert.Equal("action", exception.ParamName);
-
- Assert.Same(Utf8String.Empty, Utf8String.UnsafeCreateWithoutValidation(0, expectedState, spanAction));
-
- Assert.Equal(u8("abcde"), Utf8String.UnsafeCreateWithoutValidation(5, expectedState, spanAction));
- }
-
- [Fact]
- public static void Ctor_Validating_FromDelegate()
- {
- object expectedState = new object();
- SpanAction<byte, object> spanAction = (span, actualState) =>
- {
- Assert.Same(expectedState, actualState);
- Assert.NotEqual(0, span.Length); // shouldn't have been called for a zero-length span
-
- for (int i = 0; i < span.Length; i++)
- {
- Assert.Equal(0, span[i]); // should've been zero-inited
- span[i] = (byte)('a' + (i % 26)); // writes "abc...xyzabc...xyz..."
- }
- };
-
- ArgumentException exception = Assert.Throws<ArgumentOutOfRangeException>(() => Utf8String.Create(-1, expectedState, spanAction));
- Assert.Equal("length", exception.ParamName);
-
- exception = Assert.Throws<ArgumentNullException>(() => Utf8String.Create(10, expectedState, action: null));
- Assert.Equal("action", exception.ParamName);
-
- Assert.Same(Utf8String.Empty, Utf8String.Create(0, expectedState, spanAction));
-
- Assert.Equal(u8("abcde"), Utf8String.Create(5, expectedState, spanAction));
- }
-
- [Fact]
- public static void Ctor_Validating_FromDelegate_ThrowsIfDelegateProvidesInvalidData()
- {
- SpanAction<byte, object> spanAction = (span, actualState) =>
- {
- span[0] = 0xFF; // never a valid UTF-8 byte
- };
-
- Assert.Throws<ArgumentException>(() => Utf8String.Create(10, new object(), spanAction));
- }
-
- [Fact]
- public static void Ctor_CreateRelaxed_FromDelegate()
- {
- object expectedState = new object();
- SpanAction<byte, object> spanAction = (span, actualState) =>
- {
- Assert.Same(expectedState, actualState);
- Assert.NotEqual(0, span.Length); // shouldn't have been called for a zero-length span
-
- for (int i = 0; i < span.Length; i++)
- {
- Assert.Equal(0, span[i]); // should've been zero-inited
- span[i] = 0xFF; // never a valid UTF-8 byte
- }
- };
-
- ArgumentException exception = Assert.Throws<ArgumentOutOfRangeException>(() => Utf8String.CreateRelaxed(-1, expectedState, spanAction));
- Assert.Equal("length", exception.ParamName);
-
- exception = Assert.Throws<ArgumentNullException>(() => Utf8String.CreateRelaxed(10, expectedState, action: null));
- Assert.Equal("action", exception.ParamName);
-
- Assert.Same(Utf8String.Empty, Utf8String.CreateRelaxed(0, expectedState, spanAction));
-
- Assert.Equal(u8("\uFFFD\uFFFD"), Utf8String.CreateRelaxed(2, expectedState, spanAction));
- }
- }
-}
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System.Text;
-using Xunit;
-
-using static System.Tests.Utf8TestUtilities;
-
-namespace System.Tests
-{
- public unsafe partial class Utf8StringTests
- {
- [Fact]
- public static void BytesProperty_FromData()
- {
- Utf8String ustr = u8("\U00000012\U00000123\U00001234\U00101234\U00000012\U00000123\U00001234\U00101234");
-
- Assert.Equal(new byte[]
- {
- 0x12,
- 0xC4, 0xA3,
- 0xE1, 0x88, 0xB4,
- 0xF4, 0x81, 0x88, 0xB4,
- 0x12,
- 0xC4, 0xA3,
- 0xE1, 0x88, 0xB4,
- 0xF4, 0x81, 0x88, 0xB4,
- }, ustr.Bytes);
- }
-
- [Fact]
- public static void BytesProperty_FromEmpty()
- {
- Assert.False(Utf8String.Empty.Bytes.GetEnumerator().MoveNext());
- }
-
- [Fact]
- public static void CharsProperty_FromData()
- {
- Utf8String ustr = u8("\U00000012\U00000123\U00001234\U00101234\U00000012\U00000123\U00001234\U00101234");
-
- Assert.Equal(new char[]
- {
- '\u0012',
- '\u0123',
- '\u1234',
- '\uDBC4', '\uDE34',
- '\u0012',
- '\u0123',
- '\u1234',
- '\uDBC4', '\uDE34',
- }, ustr.Chars);
- }
-
- [Fact]
- public static void CharsProperty_FromEmpty()
- {
- Assert.False(Utf8String.Empty.Chars.GetEnumerator().MoveNext());
- }
-
- [Fact]
- public static void RunesProperty_FromData()
- {
- Utf8String ustr = u8("\U00000012\U00000123\U00001234\U00101234\U00000012\U00000123\U00001234\U00101234");
-
- Assert.Equal(new Rune[]
- {
- new Rune(0x0012),
- new Rune(0x0123),
- new Rune(0x1234),
- new Rune(0x101234),
- new Rune(0x0012),
- new Rune(0x0123),
- new Rune(0x1234),
- new Rune(0x101234),
- }, ustr.Runes);
- }
-
- [Fact]
- public static void RunesProperty_FromEmpty()
- {
- Assert.False(Utf8String.Empty.Runes.GetEnumerator().MoveNext());
- }
- }
-}
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Text.Tests;
-using Xunit;
-
-using static System.Tests.Utf8TestUtilities;
-
-namespace System.Tests
-{
- public unsafe partial class Utf8StringTests
- {
- private delegate Utf8String.SplitResult Utf8StringSplitDelegate(Utf8String ustr, Utf8StringSplitOptions splitOptions);
-
- [Fact]
- public static void Split_Utf8StringSeparator_WithNullOrEmptySeparator_Throws()
- {
- var ex = Assert.Throws<ArgumentException>(() => { u8("Hello").Split((Utf8String)null); });
- Assert.Equal("separator", ex.ParamName);
-
- // Shouldn't be able to split on an empty Utf8String.
- // Such an enumerator would iterate forever, so we forbid it.
-
- ex = Assert.Throws<ArgumentException>(() => { u8("Hello").Split(Utf8String.Empty); });
- Assert.Equal("separator", ex.ParamName);
- }
-
- [Fact]
- public static void Split_InvalidChar_Throws()
- {
- // Shouldn't be able to split on a standalone surrogate char
- // Other search methods (TryFind) return false when given a standalone surrogate char as input,
- // but the Split methods returns a complex data structure instead of a simple bool. So to keep
- // the logic of that data structure relatively simple we'll forbid the bad char at the call site.
-
- var ex = Assert.Throws<ArgumentOutOfRangeException>(() => { u8("Hello").Split('\ud800'); });
- Assert.Equal("separator", ex.ParamName);
- }
-
- public static IEnumerable<object[]> SplitData_CharSeparator => Utf8SpanTests.SplitData_CharSeparator();
-
- [Theory]
- [MemberData(nameof(SplitData_CharSeparator))]
- public static void Split_Char(Utf8String source, char separator, Range[] expectedRanges)
- {
- SplitTest_Common(source ?? Utf8String.Empty, (ustr, splitOptions) => ustr.Split(separator, splitOptions), expectedRanges);
- }
-
- [Fact]
- public static void Split_Deconstruct()
- {
- Utf8String ustr = u8("a,b,c,d,e");
-
- {
- (Utf8String a, Utf8String b) = ustr.Split('x'); // not found
- Assert.Same(ustr, a); // Expected referential equality of input
- Assert.Null(b);
- }
-
- {
- (Utf8String a, Utf8String b) = ustr.Split(',');
- Assert.Equal(u8("a"), a);
- Assert.Equal(u8("b,c,d,e"), b);
- }
-
- {
- (Utf8String a, Utf8String b, Utf8String c, Utf8String d, Utf8String e) = ustr.Split(',');
- Assert.Equal(u8("a"), a);
- Assert.Equal(u8("b"), b);
- Assert.Equal(u8("c"), c);
- Assert.Equal(u8("d"), d);
- Assert.Equal(u8("e"), e);
- }
-
- {
- (Utf8String a, Utf8String b, Utf8String c, Utf8String d, Utf8String e, Utf8String f, Utf8String g, Utf8String h) = ustr.Split(',');
- Assert.Equal(u8("a"), a);
- Assert.Equal(u8("b"), b);
- Assert.Equal(u8("c"), c);
- Assert.Equal(u8("d"), d);
- Assert.Equal(u8("e"), e);
- Assert.Null(f);
- Assert.Null(g);
- Assert.Null(h);
- }
- }
-
- [Fact]
- public static void Split_Deconstruct_WithOptions()
- {
- Utf8String ustr = u8("a, , b, c,, d, e");
-
- // Note referential equality checks below (since we want to know exact slices
- // into the original buffer), not deep (textual) equality checks.
-
- {
- (Utf8String a, Utf8String b) = ustr.Split(',', Utf8StringSplitOptions.RemoveEmptyEntries);
- Assert.Equal(u8("a"), a);
- Assert.Equal(u8(" , b, c,, d, e"), b);
- }
-
- {
- (Utf8String a, Utf8String x, Utf8String b, Utf8String c, Utf8String d, Utf8String e) = ustr.Split(',', Utf8StringSplitOptions.RemoveEmptyEntries);
- Assert.Equal(u8("a"), a); // "a"
- Assert.Equal(u8(" "), x); // " "
- Assert.Equal(u8(" b"), b); // " b"
- Assert.Equal(u8(" c"), c); // " c"
- Assert.Equal(u8(" d"), d); // " d"
- Assert.Equal(u8(" e"), e); // " e"
- }
-
- {
- (Utf8String a, Utf8String b, Utf8String c, Utf8String d, Utf8String e, Utf8String f, Utf8String g, Utf8String h) = ustr.Split(',', Utf8StringSplitOptions.RemoveEmptyEntries | Utf8StringSplitOptions.TrimEntries);
- Assert.Equal(u8("a"), a);
- Assert.Equal(u8("b"), b);
- Assert.Equal(u8("c"), c);
- Assert.Equal(u8("d"), d);
- Assert.Equal(u8("e"), e);
- Assert.Null(f);
- Assert.Null(g);
- Assert.Null(h);
- }
- }
-
- public static IEnumerable<object[]> SplitData_RuneSeparator() => Utf8SpanTests.SplitData_RuneSeparator();
-
- [Theory]
- [MemberData(nameof(SplitData_RuneSeparator))]
- public static void Split_Rune(Utf8String source, Rune separator, Range[] expectedRanges)
- {
- SplitTest_Common(source ?? Utf8String.Empty, (ustr, splitOptions) => ustr.Split(separator, splitOptions), expectedRanges);
- }
-
- public static IEnumerable<object[]> SplitData_Utf8StringSeparator() => Utf8SpanTests.SplitData_Utf8SpanSeparator();
-
- [Theory]
- [MemberData(nameof(SplitData_Utf8StringSeparator))]
- public static void Split_Utf8String(Utf8String source, Utf8String separator, Range[] expectedRanges)
- {
- SplitTest_Common(source ?? Utf8String.Empty, (ustr, splitOptions) => ustr.Split(separator, splitOptions), expectedRanges);
- }
-
- private static void SplitTest_Common(Utf8String source, Utf8StringSplitDelegate splitAction, Range[] expectedRanges)
- {
- // First, run the split with default options and make sure the results are equivalent
-
- Assert.Equal(
- expected: expectedRanges.Select(range => source[range]),
- actual: splitAction(source, Utf8StringSplitOptions.None));
-
- // Next, run the split with empty entries removed
-
- Assert.Equal(
- expected: expectedRanges.Select(range => source[range]).Where(ustr => ustr.Length != 0),
- actual: splitAction(source, Utf8StringSplitOptions.RemoveEmptyEntries));
-
- // Next, run the split with results trimmed (but allowing empty results)
-
- Assert.Equal(
- expected: expectedRanges.Select(range => source[range].Trim()),
- actual: splitAction(source, Utf8StringSplitOptions.TrimEntries));
-
- // Finally, run the split both trimmed and with empty entries removed
-
- Assert.Equal(
- expected: expectedRanges.Select(range => source[range].Trim()).Where(ustr => ustr.Length != 0),
- actual: splitAction(source, Utf8StringSplitOptions.TrimEntries | Utf8StringSplitOptions.RemoveEmptyEntries));
- }
-
- public static IEnumerable<object[]> Trim_TestData() => Utf8SpanTests.Trim_TestData();
-
- [Theory]
- [MemberData(nameof(Trim_TestData))]
- public static void Trim(string input)
- {
- if (input is null)
- {
- return; // don't want to null ref
- }
-
- Utf8String utf8Input = u8(input);
-
- void RunTest(Func<Utf8String, Utf8String> utf8TrimAction, Func<string, string> utf16TrimAction)
- {
- Utf8String utf8Trimmed = utf8TrimAction(utf8Input);
- string utf16Trimmed = utf16TrimAction(input);
-
- if (utf16Trimmed.Length == input.Length)
- {
- Assert.Same(utf8Input, utf8Trimmed); // Trimming should no-op, return original input
- }
- else if (utf16Trimmed.Length == 0)
- {
- Assert.Same(Utf8String.Empty, utf8Trimmed); // Trimming an all-whitespace input, return Empty
- }
- else
- {
- Assert.True(Utf8String.AreEquivalent(utf8Trimmed, utf16Trimmed));
- }
- }
-
- RunTest(ustr => ustr.Trim(), str => str.Trim());
- RunTest(ustr => ustr.TrimStart(), str => str.TrimStart());
- RunTest(ustr => ustr.TrimEnd(), str => str.TrimEnd());
- }
- }
-}
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System.Collections.Generic;
-using System.Globalization;
-using System.Text;
-using System.Text.Tests;
-using Xunit;
-
-using static System.Tests.Utf8TestUtilities;
-
-using ustring = System.Utf8String;
-
-namespace System.Tests
-{
- /*
- * Please keep these tests in sync with those in Utf8SpanTests.Searching.cs.
- */
-
- public unsafe partial class Utf8StringTests
- {
- public static IEnumerable<object[]> TryFindData_Char_Ordinal() => Utf8SpanTests.TryFindData_Char_Ordinal();
-
- [Theory]
- [MemberData(nameof(TryFindData_Char_Ordinal))]
- public static void TryFind_Char_Ordinal(ustring source, char searchTerm, Range? expectedForwardMatch, Range? expectedBackwardMatch)
- {
- if (source is null)
- {
- return; // don't null ref
- }
-
- // First, search forward
-
- bool wasFound = source.TryFind(searchTerm, out Range actualForwardMatch);
- Assert.Equal(expectedForwardMatch.HasValue, wasFound);
-
- if (wasFound)
- {
- AssertRangesEqual(source.Length, expectedForwardMatch.Value, actualForwardMatch);
- }
-
- // Also check Contains / StartsWith / SplitOn
-
- Assert.Equal(wasFound, source.Contains(searchTerm));
- Assert.Equal(wasFound && source[..actualForwardMatch.Start].Length == 0, source.StartsWith(searchTerm));
-
- (var before, var after) = source.SplitOn(searchTerm);
- if (wasFound)
- {
- Assert.Equal(source[..actualForwardMatch.Start], before);
- Assert.Equal(source[actualForwardMatch.End..], after);
- }
- else
- {
- Assert.Same(source, before); // check for reference equality
- Assert.Null(after);
- }
-
- // Now search backward
-
- wasFound = source.TryFindLast(searchTerm, out Range actualBackwardMatch);
- Assert.Equal(expectedBackwardMatch.HasValue, wasFound);
-
- if (wasFound)
- {
- AssertRangesEqual(source.Length, expectedBackwardMatch.Value, actualBackwardMatch);
- }
-
- // Also check EndsWith / SplitOnLast
-
- Assert.Equal(wasFound && source[actualBackwardMatch.End..].Length == 0, source.EndsWith(searchTerm));
-
- (before, after) = source.SplitOnLast(searchTerm);
- if (wasFound)
- {
- Assert.Equal(source[..actualBackwardMatch.Start], before);
- Assert.Equal(source[actualBackwardMatch.End..], after);
- }
- else
- {
- Assert.Same(source, before); // check for reference equality
- Assert.Null(after);
- }
- }
-
- public static IEnumerable<object[]> TryFindData_Char_WithComparison() => Utf8SpanTests.TryFindData_Char_WithComparison();
-
- [PlatformSpecific(TestPlatforms.Windows)]
- [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNlsGlobalization))]
- [MemberData(nameof(TryFindData_Char_WithComparison))]
- public static void TryFind_Char_WithComparison(ustring source, char searchTerm, StringComparison comparison, CultureInfo currentCulture, Range? expectedForwardMatch, Range? expectedBackwardMatch)
- {
- if (source is null)
- {
- return; // don't null ref
- }
-
- RunOnDedicatedThread(() =>
- {
- if (currentCulture != null)
- {
- CultureInfo.CurrentCulture = currentCulture;
- }
-
- if (IsTryFindSupported(comparison))
- {
- // First, search forward
-
- bool wasFound = source.TryFind(searchTerm, comparison, out Range actualForwardMatch);
- Assert.Equal(expectedForwardMatch.HasValue, wasFound);
-
- if (wasFound)
- {
- AssertRangesEqual(source.Length, expectedForwardMatch.Value, actualForwardMatch);
- }
-
- // Also check Contains / StartsWith / SplitOn
-
- Assert.Equal(wasFound, source.Contains(searchTerm, comparison));
- Assert.Equal(wasFound && source[..actualForwardMatch.Start].Length == 0, source.StartsWith(searchTerm, comparison));
-
- (var before, var after) = source.SplitOn(searchTerm, comparison);
- if (wasFound)
- {
- Assert.Equal(source[..actualForwardMatch.Start], before);
- Assert.Equal(source[actualForwardMatch.End..], after);
- }
- else
- {
- Assert.Same(source, before); // check for reference equality
- Assert.Null(after);
- }
-
- // Now search backward
-
- wasFound = source.TryFindLast(searchTerm, comparison, out Range actualBackwardMatch);
- Assert.Equal(expectedBackwardMatch.HasValue, wasFound);
-
- if (wasFound)
- {
- AssertRangesEqual(source.Length, expectedBackwardMatch.Value, actualBackwardMatch);
- }
-
- // Also check EndsWith / SplitOnLast
-
- Assert.Equal(wasFound && source[actualBackwardMatch.End..].Length == 0, source.EndsWith(searchTerm, comparison));
-
- (before, after) = source.SplitOnLast(searchTerm, comparison);
- if (wasFound)
- {
- Assert.Equal(source[..actualBackwardMatch.Start], before);
- Assert.Equal(source[actualBackwardMatch.End..], after);
- }
- else
- {
- Assert.Same(source, before); // check for reference equality
- Assert.Null(after);
- }
- }
- else
- {
- Assert.Throws<NotSupportedException>(() => source.TryFind(searchTerm, comparison, out var _));
- Assert.Throws<NotSupportedException>(() => source.TryFindLast(searchTerm, comparison, out var _));
- Assert.Throws<NotSupportedException>(() => source.SplitOn(searchTerm, comparison));
- Assert.Throws<NotSupportedException>(() => source.SplitOnLast(searchTerm, comparison));
-
- Assert.Equal(expectedForwardMatch.HasValue, source.Contains(searchTerm, comparison));
- Assert.Equal(expectedForwardMatch.HasValue && source[..expectedForwardMatch.Value.Start].Length == 0, source.StartsWith(searchTerm, comparison));
- Assert.Equal(expectedBackwardMatch.HasValue && source[expectedBackwardMatch.Value.End..].Length == 0, source.EndsWith(searchTerm, comparison));
- }
- });
- }
-
- public static IEnumerable<object[]> TryFindData_Rune_Ordinal() => Utf8SpanTests.TryFindData_Rune_Ordinal();
-
- [Theory]
- [MemberData(nameof(TryFindData_Rune_Ordinal))]
- public static void TryFind_Rune_Ordinal(ustring source, Rune searchTerm, Range? expectedForwardMatch, Range? expectedBackwardMatch)
- {
- if (source is null)
- {
- return; // don't null ref
- }
-
- // First, search forward
-
- bool wasFound = source.TryFind(searchTerm, out Range actualForwardMatch);
- Assert.Equal(expectedForwardMatch.HasValue, wasFound);
-
- if (wasFound)
- {
- AssertRangesEqual(source.Length, expectedForwardMatch.Value, actualForwardMatch);
- }
-
- // Also check Contains / StartsWith / SplitOn
-
- Assert.Equal(wasFound, source.Contains(searchTerm));
- Assert.Equal(wasFound && source[..actualForwardMatch.Start].Length == 0, source.StartsWith(searchTerm));
-
- (var before, var after) = source.SplitOn(searchTerm);
- if (wasFound)
- {
- Assert.Equal(source[..actualForwardMatch.Start], before);
- Assert.Equal(source[actualForwardMatch.End..], after);
- }
- else
- {
- Assert.Same(source, before); // check for reference equality
- Assert.Null(after);
- }
-
- // Now search backward
-
- wasFound = source.TryFindLast(searchTerm, out Range actualBackwardMatch);
- Assert.Equal(expectedBackwardMatch.HasValue, wasFound);
-
- if (wasFound)
- {
- AssertRangesEqual(source.Length, expectedBackwardMatch.Value, actualBackwardMatch);
- }
-
- // Also check EndsWith / SplitOnLast
-
- Assert.Equal(wasFound && source[actualBackwardMatch.End..].Length == 0, source.EndsWith(searchTerm));
-
- (before, after) = source.SplitOnLast(searchTerm);
- if (wasFound)
- {
- Assert.Equal(source[..actualBackwardMatch.Start], before);
- Assert.Equal(source[actualBackwardMatch.End..], after);
- }
- else
- {
- Assert.Same(source, before); // check for reference equality
- Assert.Null(after);
- }
- }
-
- public static IEnumerable<object[]> TryFindData_Rune_WithComparison() => Utf8SpanTests.TryFindData_Rune_WithComparison();
-
- [PlatformSpecific(TestPlatforms.Windows)]
- [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNlsGlobalization))]
- [MemberData(nameof(TryFindData_Rune_WithComparison))]
- public static void TryFind_Rune_WithComparison(ustring source, Rune searchTerm, StringComparison comparison, CultureInfo currentCulture, Range? expectedForwardMatch, Range? expectedBackwardMatch)
- {
- if (source is null)
- {
- return; // don't null ref
- }
-
- RunOnDedicatedThread(() =>
- {
- if (currentCulture != null)
- {
- CultureInfo.CurrentCulture = currentCulture;
- }
-
- if (IsTryFindSupported(comparison))
- {
- // First, search forward
-
- bool wasFound = source.TryFind(searchTerm, comparison, out Range actualForwardMatch);
- Assert.Equal(expectedForwardMatch.HasValue, wasFound);
-
- if (wasFound)
- {
- AssertRangesEqual(source.Length, expectedForwardMatch.Value, actualForwardMatch);
- }
-
- // Also check Contains / StartsWith / SplitOn
-
- Assert.Equal(wasFound, source.Contains(searchTerm, comparison));
- Assert.Equal(wasFound && source[..actualForwardMatch.Start].Length == 0, source.StartsWith(searchTerm, comparison));
-
- (var before, var after) = source.SplitOn(searchTerm, comparison);
- if (wasFound)
- {
- Assert.Equal(source[..actualForwardMatch.Start], before);
- Assert.Equal(source[actualForwardMatch.End..], after);
- }
- else
- {
- Assert.Same(source, before); // check for reference equality
- Assert.Null(after);
- }
-
- // Now search backward
-
- wasFound = source.TryFindLast(searchTerm, comparison, out Range actualBackwardMatch);
- Assert.Equal(expectedBackwardMatch.HasValue, wasFound);
-
- if (wasFound)
- {
- AssertRangesEqual(source.Length, expectedBackwardMatch.Value, actualBackwardMatch);
- }
-
- // Also check EndsWith / SplitOnLast
-
- Assert.Equal(wasFound && source[actualBackwardMatch.End..].Length == 0, source.EndsWith(searchTerm, comparison));
-
- (before, after) = source.SplitOnLast(searchTerm, comparison);
- if (wasFound)
- {
- Assert.Equal(source[..actualBackwardMatch.Start], before);
- Assert.Equal(source[actualBackwardMatch.End..], after);
- }
- else
- {
- Assert.Same(source, before); // check for reference equality
- Assert.Null(after);
- }
- }
- else
- {
- Assert.Throws<NotSupportedException>(() =>source.TryFind(searchTerm, comparison, out var _));
- Assert.Throws<NotSupportedException>(() =>source.TryFindLast(searchTerm, comparison, out var _));
- Assert.Throws<NotSupportedException>(() =>source.SplitOn(searchTerm, comparison));
- Assert.Throws<NotSupportedException>(() =>source.SplitOnLast(searchTerm, comparison));
-
- Assert.Equal(expectedForwardMatch.HasValue, source.Contains(searchTerm, comparison));
- Assert.Equal(expectedForwardMatch.HasValue && source[..expectedForwardMatch.Value.Start].Length == 0, source.StartsWith(searchTerm, comparison));
- Assert.Equal(expectedBackwardMatch.HasValue && source[expectedBackwardMatch.Value.End..].Length == 0, source.EndsWith(searchTerm, comparison));
- }
- });
- }
-
- public static IEnumerable<object[]> TryFindData_Utf8String_Ordinal() => Utf8SpanTests.TryFindData_Utf8Span_Ordinal();
-
- [Theory]
- [MemberData(nameof(TryFindData_Utf8String_Ordinal))]
- public static void TryFind_Utf8String_Ordinal(ustring source, ustring searchTerm, Range? expectedForwardMatch, Range? expectedBackwardMatch)
- {
- if (source is null)
- {
- return; // don't null ref
- }
-
- // First, search forward
-
- bool wasFound = source.TryFind(searchTerm, out Range actualForwardMatch);
- Assert.Equal(expectedForwardMatch.HasValue, wasFound);
-
- if (wasFound)
- {
- AssertRangesEqual(source.Length, expectedForwardMatch.Value, actualForwardMatch);
- }
-
- // Also check Contains / StartsWith / SplitOn
-
- Assert.Equal(wasFound, source.Contains(searchTerm));
- Assert.Equal(wasFound && source[..actualForwardMatch.Start].Length == 0, source.StartsWith(searchTerm));
-
- (var before, var after) = source.SplitOn(searchTerm);
- if (wasFound)
- {
- Assert.Equal(source[..actualForwardMatch.Start], before);
- Assert.Equal(source[actualForwardMatch.End..], after);
- }
- else
- {
- Assert.Same(source, before); // check for reference equality
- Assert.Null(after);
- }
-
- // Now search backward
-
- wasFound = source.TryFindLast(searchTerm, out Range actualBackwardMatch);
- Assert.Equal(expectedBackwardMatch.HasValue, wasFound);
-
- if (wasFound)
- {
- AssertRangesEqual(source.Length, expectedBackwardMatch.Value, actualBackwardMatch);
- }
-
- // Also check EndsWith / SplitOnLast
-
- Assert.Equal(wasFound && source[actualBackwardMatch.End..].Length == 0, source.EndsWith(searchTerm));
-
- (before, after) = source.SplitOnLast(searchTerm);
- if (wasFound)
- {
- Assert.Equal(source[..actualBackwardMatch.Start], before);
- Assert.Equal(source[actualBackwardMatch.End..], after);
- }
- else
- {
- Assert.Same(source, before); // check for reference equality
- Assert.Null(after);
- }
- }
-
- public static IEnumerable<object[]> TryFindData_Utf8String_WithComparison() => Utf8SpanTests.TryFindData_Utf8Span_WithComparison();
-
- [PlatformSpecific(TestPlatforms.Windows)]
- [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNlsGlobalization))]
- [MemberData(nameof(TryFindData_Utf8String_WithComparison))]
- public static void TryFind_Utf8String_WithComparison(ustring source, ustring searchTerm, StringComparison comparison, CultureInfo currentCulture, Range? expectedForwardMatch, Range? expectedBackwardMatch)
- {
- if (source is null)
- {
- return; // don't null ref
- }
-
- RunOnDedicatedThread(() =>
- {
- if (currentCulture != null)
- {
- CultureInfo.CurrentCulture = currentCulture;
- }
-
- if (IsTryFindSupported(comparison))
- {
- // First, search forward
-
- bool wasFound = source.TryFind(searchTerm, comparison, out Range actualForwardMatch);
- Assert.Equal(expectedForwardMatch.HasValue, wasFound);
-
- if (wasFound)
- {
- AssertRangesEqual(source.Length, expectedForwardMatch.Value, actualForwardMatch);
- }
-
- // Also check Contains / StartsWith / SplitOn
-
- Assert.Equal(wasFound, source.Contains(searchTerm, comparison));
- Assert.Equal(wasFound && source[..actualForwardMatch.Start].Length == 0, source.StartsWith(searchTerm, comparison));
-
- (var before, var after) = source.SplitOn(searchTerm, comparison);
- if (wasFound)
- {
- Assert.Equal(source[..actualForwardMatch.Start], before);
- Assert.Equal(source[actualForwardMatch.End..], after);
- }
- else
- {
- Assert.Same(source, before); // check for reference equality
- Assert.Null(after);
- }
-
- // Now search backward
-
- wasFound = source.TryFindLast(searchTerm, comparison, out Range actualBackwardMatch);
- Assert.Equal(expectedBackwardMatch.HasValue, wasFound);
-
- if (wasFound)
- {
- AssertRangesEqual(source.Length, expectedBackwardMatch.Value, actualBackwardMatch);
- }
-
- // Also check EndsWith / SplitOnLast
-
- Assert.Equal(wasFound && source[actualBackwardMatch.End..].Length == 0, source.EndsWith(searchTerm, comparison));
-
- (before, after) = source.SplitOnLast(searchTerm, comparison);
- if (wasFound)
- {
- Assert.Equal(source[..actualBackwardMatch.Start], before);
- Assert.Equal(source[actualBackwardMatch.End..], after);
- }
- else
- {
- Assert.Same(source, before); // check for reference equality
- Assert.Null(after);
- }
- }
- else
- {
- Assert.Throws<NotSupportedException>(() => source.TryFind(searchTerm, comparison, out var _));
- Assert.Throws<NotSupportedException>(() => source.TryFindLast(searchTerm, comparison, out var _));
- Assert.Throws<NotSupportedException>(() => source.SplitOn(searchTerm, comparison));
- Assert.Throws<NotSupportedException>(() => source.SplitOnLast(searchTerm, comparison));
-
- Assert.Equal(expectedForwardMatch.HasValue, source.Contains(searchTerm, comparison));
- Assert.Equal(expectedForwardMatch.HasValue && source[..expectedForwardMatch.Value.Start].Length == 0, source.StartsWith(searchTerm, comparison));
- Assert.Equal(expectedBackwardMatch.HasValue && source[expectedBackwardMatch.Value.End..].Length == 0, source.EndsWith(searchTerm, comparison));
- }
- });
- }
-
- [Fact]
- public static void TryFind_WithNullUtf8String_Throws()
- {
- static void RunTest(Action action)
- {
- var exception = Assert.Throws<ArgumentNullException>(action);
- Assert.Equal("value", exception.ParamName);
- }
-
- ustring str = u8("Hello!");
- const ustring value = null;
- const StringComparison comparison = StringComparison.OrdinalIgnoreCase;
-
- // Run this test for a bunch of methods, not simply TryFind
-
- RunTest(() => str.Contains(value));
- RunTest(() => str.Contains(value, comparison));
- RunTest(() => str.EndsWith(value));
- RunTest(() => str.EndsWith(value, comparison));
- RunTest(() => str.SplitOn(value));
- RunTest(() => str.SplitOn(value, comparison));
- RunTest(() => str.SplitOnLast(value));
- RunTest(() => str.SplitOnLast(value, comparison));
- RunTest(() => str.StartsWith(value));
- RunTest(() => str.StartsWith(value, comparison));
- RunTest(() => str.TryFind(value, out _));
- RunTest(() => str.TryFind(value, comparison, out _));
- RunTest(() => str.TryFindLast(value, out _));
- RunTest(() => str.TryFindLast(value, comparison, out _));
- }
- }
-}
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System.Collections.Generic;
-using System.Globalization;
-using Xunit;
-
-using static System.Tests.Utf8TestUtilities;
-
-namespace System.Tests
-{
- [SkipOnMono("The features from System.Utf8String.Experimental namespace are experimental.")]
- public unsafe partial class Utf8StringTests
- {
- [Fact]
- public static void Empty_HasLengthZero()
- {
- Assert.Equal(0, Utf8String.Empty.Length);
- SpanAssert.Equal(ReadOnlySpan<byte>.Empty, Utf8String.Empty.AsBytes());
- }
-
- [Fact]
- public static void Empty_ReturnsSingleton()
- {
- Assert.Same(Utf8String.Empty, Utf8String.Empty);
- }
-
- [Theory]
- [InlineData(null, null, true)]
- [InlineData("", null, false)]
- [InlineData(null, "", false)]
- [InlineData("hello", null, false)]
- [InlineData(null, "hello", false)]
- [InlineData("hello", "hello", true)]
- [InlineData("hello", "Hello", false)]
- [InlineData("hello there", "hello", false)]
- public static void Equality_Ordinal(string aString, string bString, bool expected)
- {
- Utf8String a = u8(aString);
- Utf8String b = u8(bString);
-
- // Operators
-
- Assert.Equal(expected, a == b);
- Assert.NotEqual(expected, a != b);
-
- // Static methods
-
- Assert.Equal(expected, Utf8String.Equals(a, b));
- Assert.Equal(expected, Utf8String.Equals(a, b, StringComparison.Ordinal));
-
- // Instance methods
-
- if (a != null)
- {
- Assert.Equal(expected, a.Equals((object)b));
- Assert.Equal(expected, a.Equals(b));
- Assert.Equal(expected, a.Equals(b, StringComparison.Ordinal));
- }
- }
-
- [Fact]
- public static void GetHashCode_Ordinal()
- {
- // Generate 17 all-null strings and make sure they have unique hash codes.
- // Assuming Marvin32 is a good PRF and has a full 32-bit output domain, we should
- // expect this test to fail only once every ~30 million runs due to the birthday paradox.
- // That should be good enough for inclusion as a unit test.
-
- HashSet<int> seenHashCodes = new HashSet<int>();
-
- for (int i = 0; i <= 16; i++)
- {
- Utf8String ustr = new Utf8String(new byte[i]);
-
- Assert.True(seenHashCodes.Add(ustr.GetHashCode()), "This hash code was previously seen.");
- }
- }
-
- [PlatformSpecific(TestPlatforms.Windows)]
- [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNlsGlobalization))]
- public static void GetHashCode_WithComparison()
- {
- // Since hash code generation is randomized, it's possible (though unlikely) that
- // we might see unanticipated collisions. It's ok if this unit test fails once in
- // every few million runs, but if the unit test becomes truly flaky then that would
- // be indicative of a larger problem with hash code generation.
- //
- // These tests also make sure that the hash code is computed over the buffer rather
- // than over the reference.
-
- // Ordinal
-
- {
- Utf8String ustr = u8("ababaaAA");
-
- Assert.Equal(ustr[0..2].GetHashCode(StringComparison.Ordinal), ustr[2..4].GetHashCode(StringComparison.Ordinal));
- Assert.NotEqual(ustr[4..6].GetHashCode(StringComparison.Ordinal), ustr[6..8].GetHashCode(StringComparison.Ordinal));
- Assert.Equal(Utf8String.Empty.GetHashCode(StringComparison.Ordinal), ustr[^0..].GetHashCode(StringComparison.Ordinal));
- }
-
- // OrdinalIgnoreCase
-
- {
- Utf8String ustr = u8("ababaaAA");
-
- Assert.Equal(ustr[0..2].GetHashCode(StringComparison.OrdinalIgnoreCase), ustr[2..4].GetHashCode(StringComparison.OrdinalIgnoreCase));
- Assert.Equal(ustr[4..6].GetHashCode(StringComparison.OrdinalIgnoreCase), ustr[6..8].GetHashCode(StringComparison.OrdinalIgnoreCase));
- Assert.NotEqual(ustr[0..2].GetHashCode(StringComparison.OrdinalIgnoreCase), ustr[6..8].GetHashCode(StringComparison.OrdinalIgnoreCase));
- Assert.Equal(Utf8String.Empty.GetHashCode(StringComparison.OrdinalIgnoreCase), ustr[^0..].GetHashCode(StringComparison.OrdinalIgnoreCase));
- }
-
- // InvariantCulture
-
- {
- Utf8String ustr = u8("ae\u00e6AE\u00c6"); // U+00E6 = 'æ' LATIN SMALL LETTER AE, U+00E6 = 'Æ' LATIN CAPITAL LETTER AE
-
- Assert.Equal(ustr[0..2].GetHashCode(StringComparison.InvariantCulture), ustr[2..4].GetHashCode(StringComparison.InvariantCulture));
- Assert.NotEqual(ustr[0..2].GetHashCode(StringComparison.InvariantCulture), ustr[4..6].GetHashCode(StringComparison.InvariantCulture));
- Assert.Equal(Utf8String.Empty.GetHashCode(StringComparison.InvariantCulture), ustr[^0..].GetHashCode(StringComparison.InvariantCulture));
- }
-
- // InvariantCultureIgnoreCase
-
- {
- Utf8String ustr = u8("ae\u00e6AE\u00c6EA");
-
- Assert.Equal(ustr[0..2].GetHashCode(StringComparison.InvariantCultureIgnoreCase), ustr[2..4].GetHashCode(StringComparison.InvariantCultureIgnoreCase));
- Assert.Equal(ustr[0..2].GetHashCode(StringComparison.InvariantCultureIgnoreCase), ustr[6..8].GetHashCode(StringComparison.InvariantCultureIgnoreCase));
- Assert.NotEqual(ustr[0..2].GetHashCode(StringComparison.InvariantCultureIgnoreCase), ustr[8..10].GetHashCode(StringComparison.InvariantCultureIgnoreCase));
- Assert.Equal(Utf8String.Empty.GetHashCode(StringComparison.InvariantCultureIgnoreCase), ustr[^0..].GetHashCode(StringComparison.InvariantCultureIgnoreCase));
- }
-
- // Invariant culture should not match Turkish I case conversion
-
- {
- Utf8String ustr = u8("i\u0130"); // U+0130 = 'İ' LATIN CAPITAL LETTER I WITH DOT ABOVE
-
- Assert.NotEqual(ustr[0..1].GetHashCode(StringComparison.InvariantCultureIgnoreCase), ustr[1..3].GetHashCode(StringComparison.InvariantCultureIgnoreCase));
- }
-
- // CurrentCulture (we'll use tr-TR)
-
- RunOnDedicatedThread(() =>
- {
- Utf8String ustr = u8("i\u0131\u0130Ii\u0131\u0130I"); // U+0131 = 'ı' LATIN SMALL LETTER DOTLESS I
-
- CultureInfo.CurrentCulture = CultureInfo.GetCultureInfo("tr-TR");
-
- Assert.Equal(ustr[0..6].GetHashCode(StringComparison.CurrentCulture), ustr[6..12].GetHashCode(StringComparison.CurrentCulture));
- Assert.NotEqual(ustr[0..1].GetHashCode(StringComparison.CurrentCulture), ustr[1..3].GetHashCode(StringComparison.CurrentCulture));
- Assert.Equal(Utf8String.Empty.GetHashCode(StringComparison.CurrentCulture), ustr[^0..].GetHashCode(StringComparison.CurrentCulture));
- });
-
- // CurrentCultureIgnoreCase (we'll use tr-TR)
-
- RunOnDedicatedThread(() =>
- {
- Utf8String ustr = u8("i\u0131\u0130Ii\u0131\u0130I");
-
- CultureInfo.CurrentCulture = CultureInfo.GetCultureInfo("tr-TR");
-
- Assert.Equal(ustr[0..6].GetHashCode(StringComparison.CurrentCultureIgnoreCase), ustr[6..12].GetHashCode(StringComparison.CurrentCultureIgnoreCase));
- Assert.NotEqual(ustr[0..1].GetHashCode(StringComparison.CurrentCultureIgnoreCase), ustr[1..3].GetHashCode(StringComparison.CurrentCultureIgnoreCase)); // 'i' shouldn't match 'ı'
- Assert.Equal(ustr[0..1].GetHashCode(StringComparison.CurrentCultureIgnoreCase), ustr[3..5].GetHashCode(StringComparison.CurrentCultureIgnoreCase)); // 'i' should match 'İ'
- Assert.NotEqual(ustr[0..1].GetHashCode(StringComparison.CurrentCultureIgnoreCase), ustr[5..6].GetHashCode(StringComparison.CurrentCultureIgnoreCase)); // 'i' shouldn't match 'I'
- Assert.Equal(Utf8String.Empty.GetHashCode(StringComparison.CurrentCultureIgnoreCase), ustr[^0..].GetHashCode(StringComparison.CurrentCultureIgnoreCase));
- });
- }
-
- [Fact]
- public static void GetPinnableReference_CalledMultipleTimes_ReturnsSameValue()
- {
- var utf8 = u8("Hello!");
-
- fixed (byte* pA = utf8)
- fixed (byte* pB = utf8)
- {
- Assert.True(pA == pB);
- }
- }
-
- [Fact]
- public static void GetPinnableReference_Empty()
- {
- fixed (byte* pStr = Utf8String.Empty)
- {
- Assert.True(pStr != null);
- Assert.Equal((byte)0, *pStr); // should point to null terminator
- }
- }
-
- [Fact]
- public static void GetPinnableReference_NotEmpty()
- {
- fixed (byte* pStr = u8("Hello!"))
- {
- Assert.True(pStr != null);
-
- Assert.Equal((byte)'H', pStr[0]);
- Assert.Equal((byte)'e', pStr[1]);
- Assert.Equal((byte)'l', pStr[2]);
- Assert.Equal((byte)'l', pStr[3]);
- Assert.Equal((byte)'o', pStr[4]);
- Assert.Equal((byte)'!', pStr[5]);
- Assert.Equal((byte)'\0', pStr[6]);
- }
- }
-
- [Theory]
- [InlineData(null, true)]
- [InlineData("", true)]
- [InlineData("\r\n", false)]
- [InlineData("not empty", false)]
- public static void IsNullOrEmpty(string value, bool expectedIsNullOrEmpty)
- {
- Assert.Equal(expectedIsNullOrEmpty, Utf8String.IsNullOrEmpty(u8(value)));
- }
-
- [Theory]
- [InlineData(null, true)]
- [InlineData("", true)]
- [InlineData(" \u2028\u2029\t\v", true)]
- [InlineData(" x\r\n", false)]
- [InlineData("\r\nhello\r\n", false)]
- [InlineData("\r\n\0\r\n", false)]
- [InlineData("\r\n\r\n", true)]
- public static void IsNullOrWhiteSpace(string input, bool expected)
- {
- Assert.Equal(expected, Utf8String.IsNullOrWhiteSpace(u8(input)));
- }
-
- [Fact]
- public static void ToByteArray_Empty()
- {
-#if NETFRAMEWORK
- // An empty Span.ToArray doesn't return Array.Empty on netfx
- Assert.Equal(Array.Empty<byte>(), Utf8String.Empty.ToByteArray());
-#else
- Assert.Same(Array.Empty<byte>(), Utf8String.Empty.ToByteArray());
-#endif
- }
-
- [Fact]
- public static void ToByteArray_NotEmpty()
- {
- Assert.Equal(new byte[] { (byte)'H', (byte)'i' }, u8("Hi").ToByteArray());
- }
-
- [Fact]
- public static void ToCharArray_NotEmpty()
- {
- Assert.Equal("Hi".ToCharArray(), u8("Hi").ToCharArray());
- }
-
- [Fact]
- public static void ToCharArray_Empty()
- {
- Assert.Same(Array.Empty<char>(), Utf8String.Empty.ToCharArray());
- }
-
- [Theory]
- [InlineData("")]
- [InlineData("Hello!")]
- public static void ToString_ReturnsUtf16(string value)
- {
- Assert.Equal(value, u8(value).ToString());
- }
-
- [Theory]
- [InlineData("Hello", "6..")]
- [InlineData("Hello", "3..7")]
- [InlineData("Hello", "2..1")]
- [InlineData("Hello", "^10..")]
- public static void Indexer_Range_ArgOutOfRange_Throws(string strAsUtf16, string rangeAsString)
- {
- Utf8String utf8String = u8(strAsUtf16);
- Range range = ParseRangeExpr(rangeAsString);
-
- Assert.Throws<ArgumentOutOfRangeException>(() => utf8String[range]);
- }
-
- [Fact]
- public static void Indexer_Range_Success()
- {
- Utf8String utf8String = u8("Hello\u0800world.");
-
- Assert.Equal(u8("Hello"), utf8String[..5]);
- Assert.Equal(u8("world."), utf8String[^6..]);
- Assert.Equal(u8("o\u0800w"), utf8String[4..9]);
-
- Assert.Same(utf8String, utf8String[..]); // don't allocate new instance if slicing to entire string
- Assert.Same(Utf8String.Empty, utf8String[1..1]); // don't allocare new zero-length string instane
- Assert.Same(Utf8String.Empty, utf8String[6..6]); // ok to have a zero-length slice within a multi-byte sequence
- }
-
- [Fact]
- public static void Indexer_Range_TriesToSplitMultiByteSequence_Throws()
- {
- Utf8String utf8String = u8("Hello\u0800world.");
-
- Assert.Throws<InvalidOperationException>(() => utf8String[..6]);
- Assert.Throws<InvalidOperationException>(() => utf8String[6..]);
- Assert.Throws<InvalidOperationException>(() => utf8String[7..8]);
- }
- }
-}
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System.Globalization;
-using System.IO;
-using System.Reflection;
-using System.Runtime.CompilerServices;
-using System.Runtime.ExceptionServices;
-using System.Runtime.InteropServices;
-using System.Text;
-using System.Threading;
-using Xunit;
-
-namespace System.Tests
-{
- public static class Utf8TestUtilities
- {
- private static readonly Lazy<Func<int, Utf8String>> _utf8StringFactory = CreateUtf8StringFactory();
-
- private static Lazy<Func<int, Utf8String>> CreateUtf8StringFactory()
- {
- return new Lazy<Func<int, Utf8String>>(() =>
- {
- MethodInfo fastAllocateMethod = typeof(Utf8String).GetMethod("FastAllocate", BindingFlags.NonPublic | BindingFlags.Static, null, new[] { typeof(int) }, null);
- Assert.NotNull(fastAllocateMethod);
- return (Func<int, Utf8String>)fastAllocateMethod.CreateDelegate(typeof(Func<int, Utf8String>));
- });
- }
-
- public unsafe static bool IsNull(this Utf8Span span)
- {
- return Unsafe.AreSame(ref Unsafe.AsRef<byte>(null), ref MemoryMarshal.GetReference(span.Bytes));
- }
-
- /// <summary>
- /// Parses an expression of the form "a..b" and returns a <see cref="Range"/>.
- /// </summary>
- public static Range ParseRangeExpr(string expression) => ParseRangeExpr(expression.AsSpan());
-
- /// <summary>
- /// Parses an expression of the form "a..b" and returns a <see cref="Range"/>.
- /// </summary>
- public static Range ParseRangeExpr(ReadOnlySpan<char> expression)
- {
- int idxOfDots = expression.IndexOf("..".AsSpan(), StringComparison.Ordinal);
- if (idxOfDots < 0)
- {
- goto Error;
- }
-
- ReadOnlySpan<char> firstPart = expression[..idxOfDots].Trim();
- Index firstIndex = Index.Start;
-
- if (!firstPart.IsWhiteSpace())
- {
- bool fromEnd = false;
-
- if (!firstPart.IsEmpty && firstPart[0] == '^')
- {
- fromEnd = true;
- firstPart = firstPart[1..];
- }
-
- if (!int.TryParse(firstPart.ToString(), NumberStyles.AllowLeadingWhite | NumberStyles.AllowTrailingWhite, CultureInfo.InvariantCulture, out int startIndex))
- {
- goto Error;
- }
-
- firstIndex = new Index(startIndex, fromEnd);
- }
-
- ReadOnlySpan<char> secondPart = expression[(idxOfDots + 2)..].Trim();
- Index secondIndex = Index.End;
-
- if (!secondPart.IsWhiteSpace())
- {
- bool fromEnd = false;
-
- if (!secondPart.IsEmpty && secondPart[0] == '^')
- {
- fromEnd = true;
- secondPart = secondPart[1..];
- }
-
- if (!int.TryParse(secondPart.ToString(), NumberStyles.AllowLeadingWhite | NumberStyles.AllowTrailingWhite, CultureInfo.InvariantCulture, out int endIndex))
- {
- goto Error;
- }
-
- secondIndex = new Index(endIndex, fromEnd);
- }
-
- return new Range(firstIndex, secondIndex);
-
- Error:
- throw new ArgumentException($"Range expression '{expression.ToString()}' is invalid.");
- }
-
- public static void AssertRangesEqual(int originalLength, Range expected, Range actual)
- {
- Assert.Equal(expected, actual, new RangeEqualityComparer(originalLength));
- }
-
- /// <summary>
- /// Runs this test on its own dedicated thread; allows for setting CurrentCulture and other thread-statics.
- /// </summary>
- /// <param name="testCode"></param>
- public static void RunOnDedicatedThread(Action testCode)
- {
- Assert.NotNull(testCode);
-
- ExceptionDispatchInfo edi = default;
- Thread newThread = new Thread(() =>
- {
- try
- {
- testCode();
- }
- catch (Exception ex)
- {
- edi = ExceptionDispatchInfo.Capture(ex);
- }
- });
-
- newThread.Start();
- newThread.Join();
-
- if (edi != null)
- {
- edi.Throw();
- }
- }
-
- /// <summary>
- /// Mimics returning a literal <see cref="Utf8String"/> instance.
- /// </summary>
- public static Utf8String u8(string str)
- {
- if (str is null)
- {
- return null;
- }
- else if (str.Length == 0)
- {
- return Utf8String.Empty;
- }
-
- // First, transcode UTF-16 to UTF-8. We use direct by-scalar transcoding here
- // because we have good reference implementation tests for this and it'll help
- // catch any errors we introduce to our bulk transcoding implementations.
-
- MemoryStream memStream = new MemoryStream();
-
- byte[] utf8Bytes = new byte[4]; // 4 UTF-8 code units is the largest any scalar value can be encoded as
-
- int index = 0;
- while (index < str.Length)
- {
- if (Rune.TryGetRuneAt(str, index, out Rune value) && value.TryEncodeToUtf8(utf8Bytes, out int bytesWritten))
- {
- memStream.Write(utf8Bytes, 0, bytesWritten);
- index += value.Utf16SequenceLength;
- }
- else
- {
- throw new ArgumentException($"String '{str}' is not a well-formed UTF-16 string.");
- }
- }
-
- Assert.True(memStream.TryGetBuffer(out ArraySegment<byte> buffer));
-
- // Now allocate a UTF-8 string instance and set this as the contents.
-
- return Utf8String.UnsafeCreateWithoutValidation(buffer);
- }
-
- public unsafe static Range GetRangeOfSubspan<T>(ReadOnlySpan<T> outerSpan, ReadOnlySpan<T> innerSpan)
- {
- ulong byteOffset = (ulong)(void*)Unsafe.ByteOffset(ref MemoryMarshal.GetReference(outerSpan), ref MemoryMarshal.GetReference(innerSpan));
- ulong elementOffset = byteOffset / (uint)Unsafe.SizeOf<T>();
-
- checked
- {
- int elementOffsetAsInt = (int)elementOffset;
- Range retVal = elementOffsetAsInt..(elementOffsetAsInt + innerSpan.Length);
-
- _ = outerSpan[retVal]; // call the real slice logic to make sure we're really within the outer span
- return retVal;
- }
- }
-
- public static Range GetRangeOfSubspan(Utf8Span outerSpan, Utf8Span innerSpan)
- {
- return GetRangeOfSubspan(outerSpan.Bytes, innerSpan.Bytes);
- }
-
- public static bool IsEmpty(this Range range, int length)
- {
- (_, int actualLength) = range.GetOffsetAndLength(length);
- return (actualLength == 0);
- }
-
- public static bool IsTryFindSupported(StringComparison comparison) =>
- !PlatformDetection.IsNetFramework ||
- comparison == StringComparison.Ordinal ||
- comparison == StringComparison.OrdinalIgnoreCase;
- }
-}
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System;
-using System.Collections.Generic;
-
-namespace Xunit
-{
- public static class SpanAssert
- {
- public static void Equal<T>(ReadOnlySpan<T> a, ReadOnlySpan<T> b, IEqualityComparer<T> comparer = null) where T : IEquatable<T>
- {
- if (comparer is null)
- {
- Assert.Equal(a.ToArray(), b.ToArray());
- }
- else
- {
- Assert.Equal(a.ToArray(), b.ToArray(), comparer);
- }
- }
-
- public static void Equal<T>(Span<T> a, Span<T> b, IEqualityComparer<T> comparer = null) where T : IEquatable<T>
- {
- if (comparer is null)
- {
- Assert.Equal(a.ToArray(), b.ToArray());
- }
- else
- {
- Assert.Equal(a.ToArray(), b.ToArray(), comparer);
- }
- }
- }
-}
+++ /dev/null
-<Project>
- <PropertyGroup>
- <!-- Utf8String reference depends on System.Private.CoreLib on net5.0 so we can't run
- Verify Closure on it. -->
- <ShouldVerifyClosure>false</ShouldVerifyClosure>
- </PropertyGroup>
-</Project>
\ No newline at end of file
+++ /dev/null
-<Project>
- <PropertyGroup>
- <!-- Utf8String reference depends on System.Private.CoreLib on net5.0 so we can't run
- Verify Closure on it. -->
- <ShouldVerifyClosure>false</ShouldVerifyClosure>
- </PropertyGroup>
-</Project>
\ No newline at end of file
<Compile Include="$(BclSourcesRoot)\System\TypedReference.cs" />
<Compile Include="$(BclSourcesRoot)\System\TypeLoadException.Mono.cs" />
<Compile Include="$(BclSourcesRoot)\System\TypeNameParser.cs" />
- <Compile Include="$(BclSourcesRoot)\System\Utf8String.cs" />
<Compile Include="$(BclSourcesRoot)\System\ValueType.cs" />
<Compile Include="$(BclSourcesRoot)\System\WeakReference.Mono.cs" />
<Compile Include="$(BclSourcesRoot)\System\WeakReference.T.Mono.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\Loader\AssemblyLoadContext.Mono.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\Remoting\Contexts\Context.cs" />
<Compile Include="$(BclSourcesRoot)\System\Security\DynamicSecurityMethodAttribute.cs" />
- <Compile Include="$(BclSourcesRoot)\System\Text\Utf8Span.cs" />
<Compile Include="$(BclSourcesRoot)\System\Threading\Interlocked.Mono.cs" />
<Compile Include="$(BclSourcesRoot)\System\Threading\Monitor.Mono.cs" />
<Compile Include="$(BclSourcesRoot)\System\Threading\Overlapped.cs" />
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System.ComponentModel;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-
-namespace System.Text
-{
- [StructLayout(LayoutKind.Auto)]
- public readonly ref partial struct Utf8Span
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public Utf8Span(Utf8String? value)
- {
- throw new PlatformNotSupportedException();
- }
-
- public ReadOnlySpan<byte> Bytes { get; }
-
- public static Utf8Span Empty => default;
-
- public bool IsEmpty => throw new PlatformNotSupportedException();
-
- public int Length => throw new PlatformNotSupportedException();
-
- public Utf8Span this[Range range]
- {
- get
- {
- throw new PlatformNotSupportedException();
- }
- }
-
- public bool IsEmptyOrWhiteSpace() => throw new PlatformNotSupportedException();
-
- [Obsolete("Equals(object) on Utf8Span will always throw an exception. Use Equals(Utf8Span) or operator == instead.")]
- [EditorBrowsable(EditorBrowsableState.Never)]
-#pragma warning disable 0809 // Obsolete member 'Utf8Span.Equals(object)' overrides non-obsolete member 'object.Equals(object)'
- public override bool Equals(object? obj)
-#pragma warning restore 0809
- {
- throw new NotSupportedException(SR.Utf8Span_CannotCallEqualsObject);
- }
-
- public bool Equals(Utf8Span other) => throw new PlatformNotSupportedException();
-
- public bool Equals(Utf8Span other, StringComparison comparison) => throw new PlatformNotSupportedException();
-
- public static bool Equals(Utf8Span left, Utf8Span right) => throw new PlatformNotSupportedException();
-
- public static bool Equals(Utf8Span left, Utf8Span right, StringComparison comparison)
- {
- throw new PlatformNotSupportedException();
- }
-
- public override int GetHashCode()
- {
- throw new PlatformNotSupportedException();
- }
-
- public int GetHashCode(StringComparison comparison)
- {
- throw new PlatformNotSupportedException();
- }
-
- public bool IsAscii()
- {
- throw new PlatformNotSupportedException();
- }
-
- public bool IsNormalized(NormalizationForm normalizationForm = NormalizationForm.FormC)
- {
- throw new PlatformNotSupportedException();
- }
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- public ref readonly byte GetPinnableReference()
- {
- throw new PlatformNotSupportedException();
- }
-
- public override string ToString()
- {
- throw new PlatformNotSupportedException();
- }
-
- public Utf8String ToUtf8String()
- {
- throw new PlatformNotSupportedException();
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static Utf8Span UnsafeCreateWithoutValidation(ReadOnlySpan<byte> buffer)
- {
- throw new PlatformNotSupportedException();
- }
- }
-}
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System.ComponentModel;
-using System.Diagnostics.CodeAnalysis;
-using System.Runtime.CompilerServices;
-using System.Text;
-
-namespace System
-{
- /// <summary>
- /// Represents an immutable string of UTF-8 code units.
- /// </summary>
- public sealed partial class Utf8String : IComparable<Utf8String?>, IEquatable<Utf8String?>
- {
-#pragma warning disable CS8618
- public static readonly Utf8String Empty;
-#pragma warning restore CS8618
-
- public static bool operator ==(Utf8String? left, Utf8String? right) => throw new PlatformNotSupportedException();
- public static bool operator !=(Utf8String? left, Utf8String? right) => throw new PlatformNotSupportedException();
- public static implicit operator Utf8Span(Utf8String? value) => throw new PlatformNotSupportedException();
-
- public int Length => throw new PlatformNotSupportedException();
- public Utf8String this[Range range]
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- get
- {
- throw new PlatformNotSupportedException();
- }
- }
-
- public int CompareTo(Utf8String? other)
- {
- throw new PlatformNotSupportedException();
- }
-
- public int CompareTo(Utf8String? other, StringComparison comparison)
- {
- throw new PlatformNotSupportedException();
- }
-
- public override bool Equals(object? obj)
- {
- throw new PlatformNotSupportedException();
- }
-
- public bool Equals(Utf8String? value)
- {
- throw new PlatformNotSupportedException();
- }
-
- public bool Equals(Utf8String? value, StringComparison comparison) => throw new PlatformNotSupportedException();
-
- public static bool Equals(Utf8String? left, Utf8String? right)
- {
- throw new PlatformNotSupportedException();
- }
-
- public static bool Equals(Utf8String? a, Utf8String? b, StringComparison comparison)
- {
- throw new PlatformNotSupportedException();
- }
-
- public override int GetHashCode()
- {
- throw new PlatformNotSupportedException();
- }
-
- public int GetHashCode(StringComparison comparison)
- {
- throw new PlatformNotSupportedException();
- }
-
- [EditorBrowsable(EditorBrowsableState.Never)] // for compiler use only
- public ref readonly byte GetPinnableReference() => throw new PlatformNotSupportedException();
-
- public bool IsAscii()
- {
- throw new PlatformNotSupportedException();
- }
-
- public static bool IsNullOrEmpty([NotNullWhen(false)] Utf8String? value)
- {
- throw new PlatformNotSupportedException();
- }
-
- public static bool IsNullOrWhiteSpace([NotNullWhen(false)] Utf8String? value)
- {
- throw new PlatformNotSupportedException();
- }
-
- public byte[] ToByteArray() => throw new PlatformNotSupportedException();
-
- public override string ToString()
- {
- throw new PlatformNotSupportedException();
- }
- }
-}