示例#1
0
        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);
        }