internal bool TextEquals(int index, ReadOnlySpan <byte> otherUtf8Text, bool isPropertyName, bool shouldUnescape) { int matchIndex = isPropertyName ? index - JsonRow.Size : index; JsonRow row = _parsedData.Get(matchIndex); CheckExpectedType( isPropertyName ? JsonTokenType.PropertyName : JsonTokenType.String, row.TokenType); ReadOnlySpan <byte> data = _utf8Json.Span; ReadOnlySpan <byte> segment = data.Slice(row.Location, row.Length); if (otherUtf8Text.Length > segment.Length || (!shouldUnescape && otherUtf8Text.Length != segment.Length)) { return(false); } if (row.NeedUnescaping && shouldUnescape) { if (otherUtf8Text.Length < segment.Length / JsonConstants.MaxExpansionFactorWhileEscaping) { return(false); } int idx = segment.IndexOf(JsonConstants.BackSlash); Debug.Assert(idx != -1); if (!otherUtf8Text.StartsWith(segment.Slice(0, idx))) { return(false); } return(JsonReaderHelper.UnescapeAndCompare(segment.Slice(idx), otherUtf8Text.Slice(idx))); } return(segment.SequenceEqual(otherUtf8Text)); }
internal bool TextEquals(int index, ReadOnlySpan <char> otherText, bool isPropertyName) { int matchIndex = isPropertyName ? index - JsonRow.Size : index; byte[]? otherUtf8TextArray = null; int length = checked (otherText.Length * JsonConstants.MaxExpansionFactorWhileTranscoding); Span <byte> otherUtf8Text = length <= JsonConstants.StackallocThreshold ? stackalloc byte[JsonConstants.StackallocThreshold] : (otherUtf8TextArray = ArrayPool <byte> .Shared.Rent(length)); ReadOnlySpan <byte> utf16Text = MemoryMarshal.AsBytes(otherText); OperationStatus status = JsonReaderHelper.ToUtf8(utf16Text, otherUtf8Text, out int consumed, out int written); Debug.Assert(status != OperationStatus.DestinationTooSmall); bool result; if (status > OperationStatus.DestinationTooSmall) // Equivalent to: (status == NeedMoreData || status == InvalidData) { result = false; } else { Debug.Assert(status == OperationStatus.Done); Debug.Assert(consumed == utf16Text.Length); result = TextEquals(index, otherUtf8Text.Slice(0, written), isPropertyName, shouldUnescape: true); } if (otherUtf8TextArray != null) { otherUtf8Text.Slice(0, written).Clear(); ArrayPool <byte> .Shared.Return(otherUtf8TextArray); } return(result); }
internal string GetPropertyRawValueAsString(int valueIndex) { ReadOnlyMemory <byte> segment = GetPropertyRawValue(valueIndex); return(JsonReaderHelper.TranscodeHelper(segment.Span)); }
internal string GetRawValueAsString(int index) { ReadOnlyMemory <byte> segment = GetRawValue(index, includeQuotes: true); return(JsonReaderHelper.TranscodeHelper(segment.Span)); }
private bool TryGetNamedPropertyValue( int endIndex, ReadOnlySpan <byte> propertyName, out JwtElement value) { ReadOnlySpan <byte> documentSpan = _utf8Json.Span; Span <byte> utf8UnescapedStack = stackalloc byte[JsonConstants.StackallocThreshold]; // Move to the row before the EndObject int index = 0; while (index < endIndex) { JsonRow row = _parsedData.Get(index); Debug.Assert(row.TokenType == JsonTokenType.PropertyName); ReadOnlySpan <byte> currentPropertyName = documentSpan.Slice(row.Location, row.Length); if (row.NeedUnescaping) { // An escaped property name will be longer than an unescaped candidate, so only unescape // when the lengths are compatible. if (currentPropertyName.Length > propertyName.Length) { int idx = currentPropertyName.IndexOf(JsonConstants.BackSlash); Debug.Assert(idx >= 0); // If everything up to where the property name has a backslash matches, keep going. if (propertyName.Length > idx && currentPropertyName.Slice(0, idx).SequenceEqual(propertyName.Slice(0, idx))) { int remaining = currentPropertyName.Length - idx; int written = 0; byte[]? rented = null; try { Span <byte> utf8Unescaped = remaining <= utf8UnescapedStack.Length ? utf8UnescapedStack : (rented = ArrayPool <byte> .Shared.Rent(remaining)); // Only unescape the part we haven't processed. JsonReaderHelper.Unescape(currentPropertyName.Slice(idx), utf8Unescaped, 0, out written); // If the unescaped remainder matches the input remainder, it's a match. if (utf8Unescaped.Slice(0, written).SequenceEqual(propertyName.Slice(idx))) { // If the property name is a match, the answer is the next element. value = new JwtElement(this, index + JsonRow.Size); return(true); } } finally { if (rented != null) { rented.AsSpan(0, written).Clear(); ArrayPool <byte> .Shared.Return(rented); } } } } } else if (currentPropertyName.SequenceEqual(propertyName)) { // If the property name is a match, the answer is the next element. value = new JwtElement(this, index + JsonRow.Size); return(true); } // Move to the previous value index += JsonRow.Size * 2; } value = default; return(false); }
internal bool TryGetNamedPropertyValue(ReadOnlySpan <char> propertyName, out JwtElement value) { JsonRow row; int maxBytes = Utf8.GetMaxByteCount(propertyName.Length); int endIndex = _parsedData.Length; if (maxBytes < JsonConstants.StackallocThreshold) { Span <byte> utf8Name = stackalloc byte[JsonConstants.StackallocThreshold]; int len = JsonReaderHelper.GetUtf8FromText(propertyName, utf8Name); utf8Name = utf8Name.Slice(0, len); return(TryGetNamedPropertyValue( endIndex, utf8Name, out value)); } // Unescaping the property name will make the string shorter (or the same) // So the first viable candidate is one whose length in bytes matches, or // exceeds, our length in chars. // // The maximal escaping seems to be 6 -> 1 ("\u0030" => "0"), but just transcode // and switch once one viable long property is found. int minBytes = propertyName.Length; for (int candidateIndex = 0; candidateIndex <= endIndex; candidateIndex += JsonRow.Size * 2) { row = _parsedData.Get(candidateIndex); Debug.Assert(row.TokenType == JsonTokenType.PropertyName); if (row.Length >= minBytes) { byte[] tmpUtf8 = ArrayPool <byte> .Shared.Rent(maxBytes); Span <byte> utf8Name = default; try { int len = JsonReaderHelper.GetUtf8FromText(propertyName, tmpUtf8); utf8Name = tmpUtf8.AsSpan(0, len); return(TryGetNamedPropertyValue( candidateIndex, utf8Name, out value)); } finally { ArrayPool <byte> .Shared.Return(tmpUtf8); } } } // None of the property names were within the range that the UTF-8 encoding would have been. #if NET5_0_OR_GREATER Unsafe.SkipInit(out value); #else value = default; #endif return(false); }