예제 #1
0
        /*
         * 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);
            }
        }
예제 #2
0
        /// <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);
        }
예제 #3
0
        /// <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);
        }
예제 #4
0
        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);
        }
예제 #5
0
        /// <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()));
        }
예제 #6
0
        /// <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);
        }
예제 #7
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));
        }
예제 #8
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());

            // The line below is removed entirely in release builds.

            Debug.Assert(Utf8Utility.IsWellFormedUtf8(newString.AsBytes()), "Buffer contained ill-formed UTF-8 data.");

            return(newString);
        }