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)); }
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)); }
private static unsafe Utf8String MakeStringFrom(ReadOnlySpan <byte> input, ref byte[] borrowedArray, bool isQueryString) { // If there are any %xx characters in the string, unescape them now. int indexOfPercentChar = input.IndexOf((byte)'%'); if (indexOfPercentChar >= 0) { if (borrowedArray == null) { borrowedArray = ArrayPool <byte> .Shared.Rent(input.Length); } else if (borrowedArray.Length < input.Length) { ArrayPool <byte> .Shared.Return(borrowedArray); borrowedArray = ArrayPool <byte> .Shared.Rent(input.Length); } ReadOnlySpan <byte> slicedInput = input; int borrowedArrayOffset = 0; do { // Copy everything up until the % character we saw slicedInput.Slice(0, indexOfPercentChar).CopyTo(borrowedArray.AsSpan(borrowedArrayOffset)); slicedInput = slicedInput.Slice(indexOfPercentChar); borrowedArrayOffset += indexOfPercentChar; // Attempt percent-unescaping int byteValue = PercentUnescape(slicedInput); if (byteValue < 0) { // Not a valid %xx escape sequence - skip it borrowedArray[borrowedArrayOffset++] = (byte)'%'; slicedInput = slicedInput.Slice(1); } else { Debug.Assert(byteValue <= byte.MaxValue); borrowedArray[borrowedArrayOffset++] = (byte)byteValue; slicedInput = slicedInput.Slice(3); } indexOfPercentChar = slicedInput.IndexOf((byte)'%'); // and search again } while (indexOfPercentChar >= 0); // If there's any leftover data, copy it now, then point the input span to the rented buffer slicedInput.CopyTo(borrowedArray.AsSpan(borrowedArrayOffset)); input = borrowedArray.AsSpan(0, borrowedArrayOffset + slicedInput.Length); } if (isQueryString) { // Use pointers to smuggle the ref struct span across a closure fixed(byte *pInput = input) { return(Utf8String.Create(input.Length, (IntPtr)pInput, (span, state) => { new Span <byte>((byte *)state, span.Length).CopyTo(span); // replace '+' with ' ' while (!span.IsEmpty) { int idxOfChar = span.IndexOf((byte)'+'); if (idxOfChar < 0) { break; } span[idxOfChar] = (byte)' '; span = span.Slice(idxOfChar + 1); } })); } } else { // no need to replace '+' with ' ' return(new Utf8String(input)); } }
public StateProperty(string name) : this(name, Utf8String.Create(name)) { }