/* * 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 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. 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); }
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> /// 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 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); }
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.Bytes.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)); }
/// <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()); // The line below is removed entirely in release builds. Debug.Assert(Utf8Utility.IsWellFormedUtf8(newString.AsBytes()), "Buffer contained ill-formed UTF-8 data."); return(newString); }