private static ReadOnlySpan <byte> GetUnescapedString(ReadOnlySpan <byte> utf8Source, int idx) { // The escaped name is always longer than the unescaped, so it is safe to use escaped name for the buffer length. int length = utf8Source.Length; byte[]? pooledName = null; Span <byte> unescapedName = length <= JsonConstants.StackallocThreshold ? stackalloc byte[length] : (pooledName = ArrayPool <byte> .Shared.Rent(length)); JsonReaderHelper.Unescape(utf8Source, unescapedName, idx, out int written); ReadOnlySpan <byte> propertyName = unescapedName.Slice(0, written).ToArray(); if (pooledName != null) { // We clear the array because it is "user data" (although a property name). new Span <byte>(pooledName, 0, written).Clear(); ArrayPool <byte> .Shared.Return(pooledName); } return(propertyName); }
private bool TryGetNamedPropertyValue( int startIndex, int endIndex, ReadOnlySpan <byte> propertyName, out JsonElement value) { ReadOnlySpan <byte> documentSpan = _utf8Json.Span; // Move to the row before the EndObject int index = endIndex - DbRow.Size; while (index > startIndex) { DbRow row = _parsedData.Get(index); Debug.Assert(row.TokenType != JsonTokenType.PropertyName); // Move before the value if (row.IsSimpleValue) { index -= DbRow.Size; } else { Debug.Assert(row.NumberOfRows > 0); index -= DbRow.Size * (row.NumberOfRows + 1); } row = _parsedData.Get(index); Debug.Assert(row.TokenType == JsonTokenType.PropertyName); ReadOnlySpan <byte> currentPropertyName = documentSpan.Slice(row.Location, row.SizeOrLength); if (row.HasComplexChildren) { // 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 <= JsonConstants.StackallocThreshold ? stackalloc byte[remaining] : (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 JsonElement(this, index + DbRow.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 JsonElement(this, index + DbRow.Size); return(true); } // Move to the previous value index -= DbRow.Size; } value = default; return(false); }