Beispiel #1
0
        internal string?[]? GetStringArray(int index)
        {
            JsonRow row = _parsedData.Get(index);

            JsonTokenType tokenType = row.TokenType;

            if (tokenType == JsonTokenType.Null)
            {
                return(null);
            }

            CheckExpectedType(JsonTokenType.StartArray, tokenType);

            ReadOnlySpan <byte> data    = _utf8Json.Span;
            ReadOnlySpan <byte> segment = data.Slice(row.Location, row.Length);

            string?[] array  = new string[row.NumberOfItems];
            var       reader = new Utf8JsonReader(segment);

            reader.Read();
            for (int i = 0; i < row.NumberOfItems; i++)
            {
                if (reader.Read())
                {
                    CheckExpectedType(JsonTokenType.String, reader.TokenType);
                    array[i] = reader.GetString();
                }
            }

            return(array);
        }
Beispiel #2
0
        internal JsonMetadata CopySegment(int startIndex, int endIndex)
        {
            Debug.Assert(
                endIndex > startIndex,
                $"endIndex={endIndex} was at or before startIndex={startIndex}");

            AssertValidIndex(startIndex);
            Debug.Assert(endIndex <= Length);

            JsonRow start  = Get(startIndex);
            int     length = endIndex - startIndex;

            byte[] newDatabase = new byte[length];
            _data.AsSpan(startIndex, length).CopyTo(newDatabase);

            Span <int> newDbInts      = MemoryMarshal.Cast <byte, int>(newDatabase);
            int        locationOffset = newDbInts[0];

            // Need to nudge one forward to account for the hidden quote on the string.
            if (start.TokenType == JsonTokenType.String)
            {
                locationOffset--;
            }

            for (int i = (length - JsonRow.Size) / sizeof(int); i >= 0; i -= JsonRow.Size / sizeof(int))
            {
                Debug.Assert(newDbInts[i] >= locationOffset);
                newDbInts[i] -= locationOffset;
            }

            return(new JsonMetadata(newDatabase));
        }
Beispiel #3
0
        internal string?GetString(int index, JsonTokenType expectedType)
        {
            JsonRow row = _parsedData.Get(index);

            JsonTokenType tokenType = row.TokenType;

            if (tokenType == JsonTokenType.Null)
            {
                return(null);
            }

            CheckExpectedType(expectedType, tokenType);

            ReadOnlySpan <byte> data    = _utf8Json.Span;
            ReadOnlySpan <byte> segment = data.Slice(row.Location, row.Length);
            string value;
            int    backslash = segment.IndexOf(JsonConstants.BackSlash);

            if (backslash < 0)
            {
                value = JsonReaderHelper.TranscodeHelper(segment);
            }
            else
            {
                value = JsonReaderHelper.GetUnescapedString(segment, backslash);
            }

            Debug.Assert(value != null);
            return(value);
        }
Beispiel #4
0
        private ReadOnlyMemory <byte> GetPropertyRawValue(int valueIndex)
        {
            // The property name is stored one row before the value
            JsonRow row = _parsedData.Get(valueIndex - JsonRow.Size);

            Debug.Assert(row.TokenType == JsonTokenType.PropertyName);

            // Subtract one for the open quote.
            int start = row.Location - 1;
            int end;

            row = _parsedData.Get(valueIndex);

            if (row.IsSimpleValue)
            {
                end = row.Location + row.Length;

                // If the value was a string, pick up the terminating quote.
                if (row.TokenType == JsonTokenType.String)
                {
                    end++;
                }

                return(_utf8Json.Slice(start, end - start));
            }

            int endElementIdx = GetEndIndex(valueIndex, includeEndElement: false);

            row = _parsedData.Get(endElementIdx);
            end = row.Location + row.Length;
            return(_utf8Json.Slice(start, end - start));
        }
Beispiel #5
0
        internal int GetMemberCount(int index)
        {
            JsonRow row = _parsedData.Get(index);

            CheckExpectedType(JsonTokenType.StartObject, row.TokenType);

            return(row.NumberOfItems);
        }
Beispiel #6
0
        internal int GetArrayLength(int index)
        {
            JsonRow row = _parsedData.Get(index);

            CheckExpectedType(JsonTokenType.StartArray, row.TokenType);

            return(row.Length);
        }
Beispiel #7
0
        internal TValue?Deserialize <TValue>(int index, JsonSerializerOptions?options = null)
            where TValue : class
        {
            JsonRow row = _parsedData.Get(index);

            ReadOnlySpan <byte> data    = _utf8Json.Span;
            ReadOnlySpan <byte> segment = data.Slice(row.Location, row.Length);

            return(JsonSerializer.Deserialize <TValue>(segment, options));
        }
Beispiel #8
0
        internal void Append(JsonTokenType tokenType, int startLocation, int length)
        {
            if (Length >= _data.Length - JsonRow.Size)
            {
                Enlarge();
            }

            JsonRow row = new JsonRow(tokenType, startLocation, length);

            MemoryMarshal.Write(_data.AsSpan(Length), ref row);
            Length += JsonRow.Size;
        }
Beispiel #9
0
        internal bool TryGetValue(int index, [NotNullWhen(true)] out JsonDocument?value)
        {
            JsonRow row = _parsedData.Get(index);

            ReadOnlySpan <byte> data    = _utf8Json.Span;
            ReadOnlySpan <byte> segment = data.Slice(row.Location, row.Length);
            var reader = new Utf8JsonReader(segment);

            if (JsonDocument.TryParseValue(ref reader, out var doc))
            {
                _disposableRegistry.Add(doc);
                value = doc;
                return(true);
            }

            value = null;
            return(false);
        }
Beispiel #10
0
        private ReadOnlyMemory <byte> GetRawValue(int index, bool includeQuotes)
        {
            JsonRow row = _parsedData.Get(index);

            if (row.IsSimpleValue)
            {
                if (includeQuotes && row.TokenType == JsonTokenType.String)
                {
                    // Start one character earlier than the value (the open quote)
                    // End one character after the value (the close quote)
                    return(_utf8Json.Slice(row.Location - 1, row.Length + 2));
                }

                return(_utf8Json.Slice(row.Location, row.Length));
            }

            return(_utf8Json.Slice(row.Location, row.Length));
        }
Beispiel #11
0
        internal int GetEndIndex(int index, bool includeEndElement)
        {
            JsonRow row = _parsedData.Get(index);

            if (row.IsSimpleValue)
            {
                return(index + JsonRow.Size);
            }

            int endIndex = index + JsonRow.Size * row.NumberOfItems;

            if (includeEndElement)
            {
                endIndex += JsonRow.Size;
            }

            return(endIndex);
        }
Beispiel #12
0
        internal bool TryGetValue(int index, out double value)
        {
            JsonRow row = _parsedData.Get(index);

            CheckExpectedType(JsonTokenType.Number, row.TokenType);

            ReadOnlySpan <byte> data    = _utf8Json.Span;
            ReadOnlySpan <byte> segment = data.Slice(row.Location, row.Length);

            if (Utf8Parser.TryParse(segment, out double tmp, out int bytesConsumed) &&
                segment.Length == bytesConsumed)
            {
                value = tmp;
                return(true);
            }

            value = 0;
            return(false);
        }
Beispiel #13
0
        internal JwtElement GetArrayIndexElement(int currentIndex, int arrayIndex)
        {
            JsonRow row = _parsedData.Get(currentIndex);

            CheckExpectedType(JsonTokenType.StartArray, row.TokenType);

            int arrayLength = row.Length;

            if ((uint)arrayIndex >= (uint)arrayLength)
            {
                throw new IndexOutOfRangeException();
            }

            if (!row.NeedUnescaping)
            {
                return(new JwtElement(this, currentIndex + ((arrayIndex + 1) * JsonRow.Size)));
            }

            int elementCount = 0;
            int objectOffset = currentIndex + JsonRow.Size;

            for (; objectOffset < _parsedData.Length; objectOffset += JsonRow.Size)
            {
                if (arrayIndex == elementCount)
                {
                    return(new JwtElement(this, objectOffset));
                }

                row = _parsedData.Get(objectOffset);

                if (!row.IsSimpleValue)
                {
                    objectOffset += JsonRow.Size * row.NumberOfItems;
                }

                elementCount++;
            }

            Debug.Fail(
                $"Ran out of database searching for array index {arrayIndex} from {currentIndex} when length was {arrayLength}");
            throw new IndexOutOfRangeException();
        }
Beispiel #14
0
        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));
        }
Beispiel #15
0
        internal bool TryGetValue(int index, out double value)
        {
            JsonRow row = _parsedData.Get(index);

            CheckExpectedType(JsonTokenType.Number, row.TokenType);

            ReadOnlySpan <byte> data    = _utf8Json.Span;
            ReadOnlySpan <byte> segment = data.Slice(row.Location, row.Length);

            if (Utf8Parser.TryParse(segment, out double tmp, out int bytesConsumed) &&
                segment.Length == bytesConsumed)
            {
                value = tmp;
                return(true);
            }

#if NET5_0_OR_GREATER
            Unsafe.SkipInit(out value);
#else
            value = default;
#endif
            return(false);
        }
Beispiel #16
0
        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);
        }