예제 #1
0
        public ValueParseResult TryParse(ReadOnlySpan <byte> readerSpan, out string result, out int consumedLength, out int lineSpan, out int colSpan)
        {
            result         = null;
            consumedLength = 0;
            lineSpan       = 1;
            colSpan        = 0;

            // is input empty
            if (readerSpan.Length <= 0)
            {
                // did any prior processing occur
                return(this._inString
                    ? _cleanup(this, ValueParseResult.FailureEOF)
                    : ValueParseResult.EOF);
            }

            // if we are not continuing, ensure it's a string that's being parsed
            var startPos = 0;

            if (!this._inString)
            {
                if (readerSpan[consumedLength++] != JsonTokens.QuoteMark)
                {
                    if (Rune.DecodeFromUtf8(readerSpan, out var rune, out _) != OperationStatus.Done)
                    {
                        rune = default;
                    }

                    return(_cleanup(this, ValueParseResult.Failure("Unexpected token, expected \".", rune)));
                }

                startPos       = consumedLength;
                this._inString = true;
            }

            // if continuing, check if anything is pending in the buffer
            var         blen    = (int)this._buffContent;
            Span <char> decoded = stackalloc char[512];

            switch (this._buffContent)
            {
            // short escape: \" \\ \/ \b \f \n \r \t
            // long escape: \uXXXX
            case ContentType.EscapeSequence:
            case ContentType.ExtendedEscapeSequence:
                if (this._buffContent != ContentType.ExtendedEscapeSequence && readerSpan[0] == JsonTokens.UnicodePrefix)
                {
                    this._buffContent = ContentType.ExtendedEscapeSequence;
                    blen = (int)this._buffContent;
                }

                if (readerSpan.Length + this._buffPos < blen + 1)
                {
                    readerSpan.CopyTo(this.Buffer[this._buffPos..].Span);
예제 #2
0
        public ValueParseResult TryParse(ReadOnlySpan <byte> readerSpan, out bool result, out int consumedLength, out int lineSpan, out int colSpan)
        {
            result         = false;
            consumedLength = 0;
            lineSpan       = 1;
            colSpan        = 0;

            // is input empty
            if (readerSpan.Length <= 0)
            {
                // did any prior processing occur
                return(this._buffPos > 0
                    ? _cleanup(this, ValueParseResult.FailureEOF)
                    : ValueParseResult.EOF);
            }

            // determine what we're reading
            var expectedLength = 4;
            var src            = this._buffPos > 0 ? this.Buffer.Span : readerSpan;

            switch (src[0])
            {
            case JsonTokens.TrueFirst:
                result = true;
                break;

            case JsonTokens.FalseFirst:
                expectedLength = 5;
                break;

            default:
                this._buffPos = 0;
                if (Rune.DecodeFromUtf8(readerSpan, out var rune, out _) != OperationStatus.Done)
                {
                    rune = default;
                }

                return(ValueParseResult.Failure("Unexpected token, expected true/false.", rune));
            }

            // if reader buffer is too small, copy its contents then signal EOF
            var tooSmall = readerSpan.Length < expectedLength - this._buffPos;

            if (tooSmall || this._buffPos > 0)
            {
                var tlen = Math.Min(expectedLength - this._buffPos, readerSpan.Length);

                readerSpan.Slice(0, tlen).CopyTo(this.Buffer.Span[this._buffPos..]);
예제 #3
0
        public ValueParseResult TryParse(ReadOnlySpan <byte> readerSpan, out double result, out int consumedLength, out int lineSpan, out int colSpan)
        {
            result         = double.NaN;
            consumedLength = 0;
            lineSpan       = 1;
            colSpan        = 0;

            // if span is empty, and no parsing occured, signal EOF immediately
            if (readerSpan.Length <= 0 && this._lastPart == NumberPart.None)
            {
                return(ValueParseResult.EOF);
            }

            // if we are not continuing, check what we're parsing
            if (this.Buffer.Length == 0)
            {
                switch (readerSpan[consumedLength++])
                {
                // a number in JSON can begin with - or digits 0-9
                case JsonTokens.NumberSign:
                    this._currentStructure = NumberStructure.HasSign;
                    this._lastPart         = NumberPart.NumberSign;
                    break;

                // digit zero is a bit special in that if it's the first digit in a number, it becomes the only
                // legal digit before decimal point, hence special handling for it
                case JsonTokens.Digit0:
                    this._currentStructure = NumberStructure.LeadingZero;
                    this._lastPart         = NumberPart.FirstDigit;
                    break;

                // digits 1-9 are also valid as starting characters of a number, and unlike 0, they do not
                // restrict pre-decimal point digit count (though IEEE754 64-bit binary float limits still apply)
                case JsonTokens.Digit1:
                case JsonTokens.Digit2:
                case JsonTokens.Digit3:
                case JsonTokens.Digit4:
                case JsonTokens.Digit5:
                case JsonTokens.Digit6:
                case JsonTokens.Digit7:
                case JsonTokens.Digit8:
                case JsonTokens.Digit9:
                    this._currentStructure = NumberStructure.LeadingNonzero;
                    this._lastPart         = NumberPart.FirstDigit;
                    break;

                // not a legal character
                default:
                    if (Rune.DecodeFromUtf8(readerSpan, out var rune, out _) != OperationStatus.Done)
                    {
                        rune = default;
                    }

                    return(ValueParseResult.Failure("Unexpected token, expected 0-9 or -.", rune));
                }
            }

            // if we got empty when previous parsing occured, just don't parse, it's an end-of-content marker
            var completedParsing = false;

            if (readerSpan.Length > 0)
            {
                var offByOne = false;

                // try reading the number
                while (consumedLength < readerSpan.Length)
                {
                    switch (readerSpan[consumedLength++])
                    {
                    // digit 0 is special
                    // if it's the first digit in the non-fractional part, it is the only legal digit before decimal point
                    // otherwise it behaves like a regular digit
                    // this means it can appear:
                    // - as first digit before decimal point
                    // - as non-first digit before decimal point, if first digit was not a 0
                    // - as a digit after decimal point before exponent mark
                    // - as a digit after exponent mark or exponent sign
                    // see: https://www.json.org/img/number.png
                    case JsonTokens.Digit0:
                        if (this._lastPart == NumberPart.FirstDigit && this._currentStructure.HasFlag(NumberStructure.LeadingZero))
                        {
                            return(_cleanup(this, ValueParseResult.Failure("Digit in illegal separator. Expected decimal point.", new Rune(readerSpan[consumedLength - 1]))));
                        }

                        if (this._lastPart == NumberPart.NumberSign)
                        {
                            this._currentStructure |= NumberStructure.LeadingZero;
                            this._lastPart          = NumberPart.FirstDigit;
                        }
                        else
                        {
                            this._lastPart = this._lastPart switch
                            {
                                NumberPart.FirstDigit => NumberPart.Digit,
                                NumberPart.FractionDot => NumberPart.FractionDigit,
                                NumberPart.ExponentMarker or NumberPart.ExponentSign => NumberPart.ExponentDigit,
                                _ => this._lastPart
                            };
                        }
                        break;

                    // non-0 digits can appear:
                    // - as first digit before decimal points
                    // - as non-first digit before decimal point, if first digit was not a 0
                    // - as a digit after decimal point before exponent mark
                    // - as a digit after exponent mark or exponent sign
                    // see: https://www.json.org/img/number.png
                    case JsonTokens.Digit1:
                    case JsonTokens.Digit2:
                    case JsonTokens.Digit3:
                    case JsonTokens.Digit4:
                    case JsonTokens.Digit5:
                    case JsonTokens.Digit6:
                    case JsonTokens.Digit7:
                    case JsonTokens.Digit8:
                    case JsonTokens.Digit9:
                        if (this._lastPart == NumberPart.FirstDigit && this._currentStructure.HasFlag(NumberStructure.LeadingZero))
                        {
                            return(_cleanup(this, ValueParseResult.Failure("Digit in illegal separator. Expected decimal point.", new Rune(readerSpan[consumedLength - 1]))));
                        }

                        if (this._lastPart == NumberPart.NumberSign)
                        {
                            this._currentStructure |= NumberStructure.LeadingNonzero;
                            this._lastPart          = NumberPart.FirstDigit;
                        }
                        else
                        {
                            this._lastPart = this._lastPart switch
                            {
                                NumberPart.FirstDigit => NumberPart.Digit,
                                NumberPart.FractionDot => NumberPart.FractionDigit,
                                NumberPart.ExponentMarker or NumberPart.ExponentSign => NumberPart.ExponentDigit,
                                _ => this._lastPart
                            };
                        }
                        break;

                    // decimal separator can appear only after at least one digit, and only once
                    case JsonTokens.DecimalSeparator:
                        if (this._lastPart != NumberPart.Digit && this._lastPart != NumberPart.FirstDigit)
                        {
                            return(_cleanup(this, ValueParseResult.Failure("Unexpected decimal separator.", new Rune('.'))));
                        }

                        this._currentStructure |= NumberStructure.Fraction;
                        this._lastPart          = NumberPart.FractionDot;
                        break;

                    // exponent marker can appear only after at least one digit, or at least one digit after
                    // decimal point, and only once, regardless of variety
                    case JsonTokens.ExponentSmall:
                    case JsonTokens.ExponentCapital:
                        if (this._lastPart != NumberPart.FirstDigit && this._lastPart != NumberPart.Digit && this._lastPart != NumberPart.FractionDigit)
                        {
                            return(_cleanup(this, ValueParseResult.Failure("Unexpected exponent marker.", new Rune(readerSpan[consumedLength - 1]))));
                        }

                        this._currentStructure |= NumberStructure.Exponent;
                        this._lastPart          = NumberPart.ExponentMarker;

                        if (this._currentStructure.HasFlag(NumberStructure.Fraction))
                        {
                            this._currentStructure |= NumberStructure.FractionValid;
                        }

                        break;

                    // exponent sign can appear only after exponent marker
                    case JsonTokens.NumberSign:
                    case JsonTokens.ExponentSignPositive:
                        if (this._lastPart != NumberPart.ExponentMarker)
                        {
                            return(_cleanup(this, ValueParseResult.Failure("Unexpected exponent sign.", new Rune(readerSpan[consumedLength - 1]))));
                        }

                        this._currentStructure |= NumberStructure.SignedExponent;
                        this._lastPart          = NumberPart.ExponentSign;
                        break;

                    // this is a situation where a non number-character is encountered
                    // this is invalid if immediately after number sign, decimal point, exponent marker, or
                    // exponent sign, otherwise consider it a completed number
                    default:
                        switch (this._lastPart)
                        {
                        case NumberPart.NumberSign:
                        case NumberPart.FractionDot:
                        case NumberPart.ExponentMarker:
                        case NumberPart.ExponentSign:
                            if (Rune.DecodeFromUtf8(readerSpan[(consumedLength - 1)..], out var rune, out _) != OperationStatus.Done)
                            {
                                rune = default;
                            }

                            return(_cleanup(this, ValueParseResult.Failure("Unexpected token, expected 0-9.", rune)));
                        }

                        offByOne         = true;
                        completedParsing = true;
                        break;
                    }

                    // if parsing is completed, do not attempt to resume
                    if (completedParsing)
                    {
                        break;
                    }
                }
예제 #4
0
        public ValueParseResult TryParse(ReadOnlySpan <byte> readerSpan, out ImmutableArray <JsonValue> result, out int consumedLength, out int lineSpan, out int colSpan)
        {
            result         = default;
            consumedLength = 0;
            lineSpan       = 1;
            colSpan        = 0;

            // is input empty
            if (readerSpan.Length <= 0 && this._innerReader == null)
            {
                // did any prior processing occur
                return(this._arr != null
                    ? _cleanup(this, ValueParseResult.FailureEOF)
                    : ValueParseResult.EOF);
            }

            // if we are not continuing, ensure it's an object that's being parsed
            if (this._arr == null)
            {
                if (readerSpan[consumedLength++] != JsonTokens.OpeningBracket)
                {
                    if (Rune.DecodeFromUtf8(readerSpan, out var rune, out _) != OperationStatus.Done)
                    {
                        rune = default;
                    }

                    return(_cleanup(this, ValueParseResult.Failure("Unexpected token, expected {.", rune)));
                }

                this._expectedNext = ExpectedToken.ValueOrEnd;
                this._arr          = ImmutableArray.CreateBuilder <JsonValue>();
                ++this._colSpan;
                ++this._streamPos;
            }

            // if continuing, check if any value is being parsed
            if (this._innerReader != null)
            {
                // valid only if expecting value
                if (this._expectedNext != ExpectedToken.Value && this._expectedNext != ExpectedToken.ValueOrEnd)
                {
                    return(_cleanup(this, ValueParseResult.Failure("Invalid internal state.", default)));
                }

                // parse inner value
                ++consumedLength;
                var innerResult = this.ParseInner(readerSpan, ref consumedLength);
                switch (innerResult.Type)
                {
                case ValueParseResultType.Success:
                    this._innerReader.Reset();
                    this._innerReader = null;
                    break;

                case ValueParseResultType.EOF:
                    return(innerResult);

                case ValueParseResultType.Failure:
                    return(_cleanup(this, innerResult));
                }
            }

            // read and parse array items
            var completedParsing = false;

            while (consumedLength < readerSpan.Length)
            {
                switch (readerSpan[consumedLength++])
                {
                case JsonTokens.WhitespaceSpace:
                    ++this._colSpan;
                    ++this._streamPos;
                    break;

                case JsonTokens.WhitespaceHorizontalTab:
                    this._colSpan += 4;     // fite me
                    ++this._streamPos;
                    break;

                case JsonTokens.WhitespaceCarriageReturn:
                    // usually as part of CRLF, really no other reason for it to exist
                    // old macs don't exist
                    break;

                case JsonTokens.WhitespaceNewline:
                    ++this._lineSpan;
                    this._colSpan = 0;
                    ++this._streamPos;
                    break;

                case JsonTokens.ItemSeparator:
                    if (this._expectedNext != ExpectedToken.ItemSeparatorOrEnd)
                    {
                        return(_cleanup(this, ValueParseResult.Failure("Unexpected item separator.", new Rune(JsonTokens.ItemSeparator))));
                    }

                    ++this._colSpan;
                    ++this._streamPos;
                    this._expectedNext = ExpectedToken.Value;
                    break;

                case JsonTokens.ClosingBracket:
                    if (this._expectedNext != ExpectedToken.ItemSeparatorOrEnd && this._expectedNext != ExpectedToken.ValueOrEnd)
                    {
                        return(_cleanup(this, ValueParseResult.Failure("Unexpected array end.", new Rune(JsonTokens.ClosingBracket))));
                    }

                    ++this._colSpan;
                    ++this._streamPos;
                    completedParsing = true;
                    break;

                case JsonTokens.NullFirst:
                    if (this._expectedNext != ExpectedToken.Value && this._expectedNext != ExpectedToken.ValueOrEnd)
                    {
                        return(_cleanup(this, ValueParseResult.Failure("Unexpected array item (null).", new Rune(JsonTokens.NullFirst))));
                    }

                    this._innerReader = this._innerReaders.NullReader;
                    break;

                case JsonTokens.TrueFirst:
                case JsonTokens.FalseFirst:
                    if (this._expectedNext != ExpectedToken.Value && this._expectedNext != ExpectedToken.ValueOrEnd)
                    {
                        return(_cleanup(this, ValueParseResult.Failure("Unexpected array item (boolean).", new Rune(readerSpan[consumedLength - 1]))));
                    }

                    this._innerReader = this._innerReaders.BooleanReader;
                    break;

                case JsonTokens.NumberSign:
                case JsonTokens.Digit0:
                case JsonTokens.Digit1:
                case JsonTokens.Digit2:
                case JsonTokens.Digit3:
                case JsonTokens.Digit4:
                case JsonTokens.Digit5:
                case JsonTokens.Digit6:
                case JsonTokens.Digit7:
                case JsonTokens.Digit8:
                case JsonTokens.Digit9:
                    if (this._expectedNext != ExpectedToken.Value && this._expectedNext != ExpectedToken.ValueOrEnd)
                    {
                        return(_cleanup(this, ValueParseResult.Failure("Unexpected array item (number).", new Rune(readerSpan[consumedLength - 1]))));
                    }

                    this._innerReader = this._innerReaders.NumberReader;
                    break;

                case JsonTokens.QuoteMark:
                    if (this._expectedNext != ExpectedToken.Value && this._expectedNext != ExpectedToken.ValueOrEnd)
                    {
                        return(_cleanup(this, ValueParseResult.Failure("Unexpected array item (string).", new Rune(JsonTokens.QuoteMark))));
                    }

                    this._innerReader = this._innerReaders.StringReader;
                    break;

                case JsonTokens.OpeningBracket:
                    if (this._expectedNext != ExpectedToken.Value && this._expectedNext != ExpectedToken.ValueOrEnd)
                    {
                        return(_cleanup(this, ValueParseResult.Failure("Unexpected array item (array).", new Rune(JsonTokens.OpeningBracket))));
                    }

                    this._innerReader = new JsonArrayReader(this._innerReaders);
                    break;

                case JsonTokens.OpeningBrace:
                    if (this._expectedNext != ExpectedToken.Value && this._expectedNext != ExpectedToken.ValueOrEnd)
                    {
                        return(_cleanup(this, ValueParseResult.Failure("Unexpected array item (object).", new Rune(JsonTokens.OpeningBracket))));
                    }

                    this._innerReader = new JsonObjectReader(this._innerReaders);
                    break;

                default:
                    if (Rune.DecodeFromUtf8(readerSpan[(consumedLength - 1)..], out var rune, out _) != OperationStatus.Done)