Update dependencies
This commit is contained in:
278
vendor/github.com/go-json-experiment/json/jsontext/decode.go
generated
vendored
278
vendor/github.com/go-json-experiment/json/jsontext/decode.go
generated
vendored
@@ -125,16 +125,16 @@ func NewDecoder(r io.Reader, opts ...Options) *Decoder {
|
||||
|
||||
// Reset resets a decoder such that it is reading afresh from r and
|
||||
// configured with the provided options. Reset must not be called on an
|
||||
// a Decoder passed to the [encoding/json/v2.UnmarshalerV2.UnmarshalJSONV2] method
|
||||
// or the [encoding/json/v2.UnmarshalFuncV2] function.
|
||||
// a Decoder passed to the [encoding/json/v2.UnmarshalerFrom.UnmarshalJSONFrom] method
|
||||
// or the [encoding/json/v2.UnmarshalFromFunc] function.
|
||||
func (d *Decoder) Reset(r io.Reader, opts ...Options) {
|
||||
switch {
|
||||
case d == nil:
|
||||
panic("jsontext: invalid nil Decoder")
|
||||
case r == nil:
|
||||
panic("jsontext: invalid nil io.Writer")
|
||||
panic("jsontext: invalid nil io.Reader")
|
||||
case d.s.Flags.Get(jsonflags.WithinArshalCall):
|
||||
panic("jsontext: cannot reset Decoder passed to json.UnmarshalerV2")
|
||||
panic("jsontext: cannot reset Decoder passed to json.UnmarshalerFrom")
|
||||
}
|
||||
d.s.reset(nil, r, opts...)
|
||||
}
|
||||
@@ -142,8 +142,21 @@ func (d *Decoder) Reset(r io.Reader, opts ...Options) {
|
||||
func (d *decoderState) reset(b []byte, r io.Reader, opts ...Options) {
|
||||
d.state.reset()
|
||||
d.decodeBuffer = decodeBuffer{buf: b, rd: r}
|
||||
d.Struct = jsonopts.Struct{}
|
||||
d.Struct.Join(opts...)
|
||||
opts2 := jsonopts.Struct{} // avoid mutating d.Struct in case it is part of opts
|
||||
opts2.Join(opts...)
|
||||
d.Struct = opts2
|
||||
}
|
||||
|
||||
// Options returns the options used to construct the encoder and
|
||||
// may additionally contain semantic options passed to a
|
||||
// [encoding/json/v2.UnmarshalDecode] call.
|
||||
//
|
||||
// If operating within
|
||||
// a [encoding/json/v2.UnmarshalerFrom.UnmarshalJSONFrom] method call or
|
||||
// a [encoding/json/v2.UnmarshalFromFunc] function call,
|
||||
// then the returned options are only valid within the call.
|
||||
func (d *Decoder) Options() Options {
|
||||
return &d.s.Struct
|
||||
}
|
||||
|
||||
var errBufferWriteAfterNext = errors.New("invalid bytes.Buffer.Write call after calling bytes.Buffer.Next")
|
||||
@@ -255,23 +268,40 @@ func (d *decodeBuffer) needMore(pos int) bool {
|
||||
return pos == len(d.buf)
|
||||
}
|
||||
|
||||
// injectSyntacticErrorWithPosition wraps a SyntacticError with the position,
|
||||
// otherwise it returns the error as is.
|
||||
// It takes a position relative to the start of the start of d.buf.
|
||||
func (d *decodeBuffer) injectSyntacticErrorWithPosition(err error, pos int) error {
|
||||
if serr, ok := err.(*SyntacticError); ok {
|
||||
return serr.withOffset(d.baseOffset + int64(pos))
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (d *decodeBuffer) offsetAt(pos int) int64 { return d.baseOffset + int64(pos) }
|
||||
func (d *decodeBuffer) previousOffsetStart() int64 { return d.baseOffset + int64(d.prevStart) }
|
||||
func (d *decodeBuffer) previousOffsetEnd() int64 { return d.baseOffset + int64(d.prevEnd) }
|
||||
func (d *decodeBuffer) PreviousBuffer() []byte { return d.buf[d.prevStart:d.prevEnd] }
|
||||
func (d *decodeBuffer) previousBuffer() []byte { return d.buf[d.prevStart:d.prevEnd] }
|
||||
func (d *decodeBuffer) unreadBuffer() []byte { return d.buf[d.prevEnd:len(d.buf)] }
|
||||
|
||||
// PreviousTokenOrValue returns the previously read token or value
|
||||
// unless it has been invalidated by a call to PeekKind.
|
||||
// If a token is just a delimiter, then this returns a 1-byte buffer.
|
||||
// This method is used for error reporting at the semantic layer.
|
||||
func (d *decodeBuffer) PreviousTokenOrValue() []byte {
|
||||
b := d.previousBuffer()
|
||||
// If peek was called, then the previous token or buffer is invalidated.
|
||||
if d.peekPos > 0 || len(b) > 0 && b[0] == invalidateBufferByte {
|
||||
return nil
|
||||
}
|
||||
// ReadToken does not preserve the buffer for null, bools, or delimiters.
|
||||
// Manually re-construct that buffer.
|
||||
if len(b) == 0 {
|
||||
b = d.buf[:d.prevEnd] // entirety of the previous buffer
|
||||
for _, tok := range []string{"null", "false", "true", "{", "}", "[", "]"} {
|
||||
if len(b) >= len(tok) && string(b[len(b)-len(tok):]) == tok {
|
||||
return b[len(b)-len(tok):]
|
||||
}
|
||||
}
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// PeekKind retrieves the next token kind, but does not advance the read offset.
|
||||
// It returns 0 if there are no more tokens.
|
||||
//
|
||||
// It returns 0 if an error occurs. Any such error is cached until
|
||||
// the next read call and it is the caller's responsibility to eventually
|
||||
// follow up a PeekKind call with a read call.
|
||||
func (d *Decoder) PeekKind() Kind {
|
||||
return d.s.PeekKind()
|
||||
}
|
||||
@@ -292,7 +322,7 @@ func (d *decoderState) PeekKind() Kind {
|
||||
if err == io.ErrUnexpectedEOF && d.Tokens.Depth() == 1 {
|
||||
err = io.EOF // EOF possibly if no Tokens present after top-level value
|
||||
}
|
||||
d.peekPos, d.peekErr = -1, err
|
||||
d.peekPos, d.peekErr = -1, wrapSyntacticError(d, err, pos, 0)
|
||||
return invalidKind
|
||||
}
|
||||
}
|
||||
@@ -305,6 +335,7 @@ func (d *decoderState) PeekKind() Kind {
|
||||
pos += jsonwire.ConsumeWhitespace(d.buf[pos:])
|
||||
if d.needMore(pos) {
|
||||
if pos, err = d.consumeWhitespace(pos); err != nil {
|
||||
err = wrapSyntacticError(d, err, pos, 0)
|
||||
d.peekPos, d.peekErr = -1, d.checkDelimBeforeIOError(delim, err)
|
||||
return invalidKind
|
||||
}
|
||||
@@ -339,12 +370,33 @@ func (d *decoderState) checkDelimBeforeIOError(delim byte, err error) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// CountNextDelimWhitespace counts the number of upcoming bytes of
|
||||
// delimiter or whitespace characters.
|
||||
// This method is used for error reporting at the semantic layer.
|
||||
func (d *decoderState) CountNextDelimWhitespace() int {
|
||||
d.PeekKind() // populate unreadBuffer
|
||||
return len(d.unreadBuffer()) - len(bytes.TrimLeft(d.unreadBuffer(), ",: \n\r\t"))
|
||||
}
|
||||
|
||||
// checkDelim checks whether delim is valid for the given next kind.
|
||||
func (d *decoderState) checkDelim(delim byte, next Kind) error {
|
||||
where := "at start of value"
|
||||
switch d.Tokens.needDelim(next) {
|
||||
case delim:
|
||||
return nil
|
||||
case ':':
|
||||
where = "after object name (expecting ':')"
|
||||
case ',':
|
||||
if d.Tokens.Last.isObject() {
|
||||
where = "after object value (expecting ',' or '}')"
|
||||
} else {
|
||||
where = "after array element (expecting ',' or ']')"
|
||||
}
|
||||
}
|
||||
pos := d.prevEnd // restore position to right after leading whitespace
|
||||
pos += jsonwire.ConsumeWhitespace(d.buf[pos:])
|
||||
err := d.Tokens.checkDelim(delim, next)
|
||||
return d.injectSyntacticErrorWithPosition(err, pos)
|
||||
err := jsonwire.NewInvalidCharacterError(d.buf[pos:], where)
|
||||
return wrapSyntacticError(d, err, pos, 0)
|
||||
}
|
||||
|
||||
// SkipValue is semantically equivalent to calling [Decoder.ReadValue] and discarding
|
||||
@@ -377,6 +429,30 @@ func (d *decoderState) SkipValue() error {
|
||||
}
|
||||
}
|
||||
|
||||
// SkipValueRemainder skips the remainder of a value
|
||||
// after reading a '{' or '[' token.
|
||||
func (d *decoderState) SkipValueRemainder() error {
|
||||
if d.Tokens.Depth()-1 > 0 && d.Tokens.Last.Length() == 0 {
|
||||
for n := d.Tokens.Depth(); d.Tokens.Depth() >= n; {
|
||||
if _, err := d.ReadToken(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SkipUntil skips all tokens until the state machine
|
||||
// is at or past the specified depth and length.
|
||||
func (d *decoderState) SkipUntil(depth int, length int64) error {
|
||||
for d.Tokens.Depth() > depth || (d.Tokens.Depth() == depth && d.Tokens.Last.Length() < length) {
|
||||
if _, err := d.ReadToken(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReadToken reads the next [Token], advancing the read offset.
|
||||
// The returned token is only valid until the next Peek, Read, or Skip call.
|
||||
// It returns [io.EOF] if there are no more tokens.
|
||||
@@ -408,7 +484,7 @@ func (d *decoderState) ReadToken() (Token, error) {
|
||||
if err == io.ErrUnexpectedEOF && d.Tokens.Depth() == 1 {
|
||||
err = io.EOF // EOF possibly if no Tokens present after top-level value
|
||||
}
|
||||
return Token{}, err
|
||||
return Token{}, wrapSyntacticError(d, err, pos, 0)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -420,6 +496,7 @@ func (d *decoderState) ReadToken() (Token, error) {
|
||||
pos += jsonwire.ConsumeWhitespace(d.buf[pos:])
|
||||
if d.needMore(pos) {
|
||||
if pos, err = d.consumeWhitespace(pos); err != nil {
|
||||
err = wrapSyntacticError(d, err, pos, 0)
|
||||
return Token{}, d.checkDelimBeforeIOError(delim, err)
|
||||
}
|
||||
}
|
||||
@@ -437,13 +514,13 @@ func (d *decoderState) ReadToken() (Token, error) {
|
||||
if jsonwire.ConsumeNull(d.buf[pos:]) == 0 {
|
||||
pos, err = d.consumeLiteral(pos, "null")
|
||||
if err != nil {
|
||||
return Token{}, d.injectSyntacticErrorWithPosition(err, pos)
|
||||
return Token{}, wrapSyntacticError(d, err, pos, +1)
|
||||
}
|
||||
} else {
|
||||
pos += len("null")
|
||||
}
|
||||
if err = d.Tokens.appendLiteral(); err != nil {
|
||||
return Token{}, d.injectSyntacticErrorWithPosition(err, pos-len("null")) // report position at start of literal
|
||||
return Token{}, wrapSyntacticError(d, err, pos-len("null"), +1) // report position at start of literal
|
||||
}
|
||||
d.prevStart, d.prevEnd = pos, pos
|
||||
return Null, nil
|
||||
@@ -452,13 +529,13 @@ func (d *decoderState) ReadToken() (Token, error) {
|
||||
if jsonwire.ConsumeFalse(d.buf[pos:]) == 0 {
|
||||
pos, err = d.consumeLiteral(pos, "false")
|
||||
if err != nil {
|
||||
return Token{}, d.injectSyntacticErrorWithPosition(err, pos)
|
||||
return Token{}, wrapSyntacticError(d, err, pos, +1)
|
||||
}
|
||||
} else {
|
||||
pos += len("false")
|
||||
}
|
||||
if err = d.Tokens.appendLiteral(); err != nil {
|
||||
return Token{}, d.injectSyntacticErrorWithPosition(err, pos-len("false")) // report position at start of literal
|
||||
return Token{}, wrapSyntacticError(d, err, pos-len("false"), +1) // report position at start of literal
|
||||
}
|
||||
d.prevStart, d.prevEnd = pos, pos
|
||||
return False, nil
|
||||
@@ -467,13 +544,13 @@ func (d *decoderState) ReadToken() (Token, error) {
|
||||
if jsonwire.ConsumeTrue(d.buf[pos:]) == 0 {
|
||||
pos, err = d.consumeLiteral(pos, "true")
|
||||
if err != nil {
|
||||
return Token{}, d.injectSyntacticErrorWithPosition(err, pos)
|
||||
return Token{}, wrapSyntacticError(d, err, pos, +1)
|
||||
}
|
||||
} else {
|
||||
pos += len("true")
|
||||
}
|
||||
if err = d.Tokens.appendLiteral(); err != nil {
|
||||
return Token{}, d.injectSyntacticErrorWithPosition(err, pos-len("true")) // report position at start of literal
|
||||
return Token{}, wrapSyntacticError(d, err, pos-len("true"), +1) // report position at start of literal
|
||||
}
|
||||
d.prevStart, d.prevEnd = pos, pos
|
||||
return True, nil
|
||||
@@ -486,23 +563,25 @@ func (d *decoderState) ReadToken() (Token, error) {
|
||||
newAbsPos := d.baseOffset + int64(pos)
|
||||
n = int(newAbsPos - oldAbsPos)
|
||||
if err != nil {
|
||||
return Token{}, d.injectSyntacticErrorWithPosition(err, pos)
|
||||
return Token{}, wrapSyntacticError(d, err, pos, +1)
|
||||
}
|
||||
} else {
|
||||
pos += n
|
||||
}
|
||||
if !d.Flags.Get(jsonflags.AllowDuplicateNames) && d.Tokens.Last.NeedObjectName() {
|
||||
if !d.Tokens.Last.isValidNamespace() {
|
||||
return Token{}, errInvalidNamespace
|
||||
}
|
||||
if d.Tokens.Last.isActiveNamespace() && !d.Namespaces.Last().insertQuoted(d.buf[pos-n:pos], flags.IsVerbatim()) {
|
||||
err = newDuplicateNameError(d.buf[pos-n : pos])
|
||||
return Token{}, d.injectSyntacticErrorWithPosition(err, pos-n) // report position at start of string
|
||||
if d.Tokens.Last.NeedObjectName() {
|
||||
if !d.Flags.Get(jsonflags.AllowDuplicateNames) {
|
||||
if !d.Tokens.Last.isValidNamespace() {
|
||||
return Token{}, wrapSyntacticError(d, errInvalidNamespace, pos-n, +1)
|
||||
}
|
||||
if d.Tokens.Last.isActiveNamespace() && !d.Namespaces.Last().insertQuoted(d.buf[pos-n:pos], flags.IsVerbatim()) {
|
||||
err = wrapWithObjectName(ErrDuplicateName, d.buf[pos-n:pos])
|
||||
return Token{}, wrapSyntacticError(d, err, pos-n, +1) // report position at start of string
|
||||
}
|
||||
}
|
||||
d.Names.ReplaceLastQuotedOffset(pos - n) // only replace if insertQuoted succeeds
|
||||
}
|
||||
if err = d.Tokens.appendString(); err != nil {
|
||||
return Token{}, d.injectSyntacticErrorWithPosition(err, pos-n) // report position at start of string
|
||||
return Token{}, wrapSyntacticError(d, err, pos-n, +1) // report position at start of string
|
||||
}
|
||||
d.prevStart, d.prevEnd = pos-n, pos
|
||||
return Token{raw: &d.decodeBuffer, num: uint64(d.previousOffsetStart())}, nil
|
||||
@@ -516,60 +595,60 @@ func (d *decoderState) ReadToken() (Token, error) {
|
||||
newAbsPos := d.baseOffset + int64(pos)
|
||||
n = int(newAbsPos - oldAbsPos)
|
||||
if err != nil {
|
||||
return Token{}, d.injectSyntacticErrorWithPosition(err, pos)
|
||||
return Token{}, wrapSyntacticError(d, err, pos, +1)
|
||||
}
|
||||
} else {
|
||||
pos += n
|
||||
}
|
||||
if err = d.Tokens.appendNumber(); err != nil {
|
||||
return Token{}, d.injectSyntacticErrorWithPosition(err, pos-n) // report position at start of number
|
||||
return Token{}, wrapSyntacticError(d, err, pos-n, +1) // report position at start of number
|
||||
}
|
||||
d.prevStart, d.prevEnd = pos-n, pos
|
||||
return Token{raw: &d.decodeBuffer, num: uint64(d.previousOffsetStart())}, nil
|
||||
|
||||
case '{':
|
||||
if err = d.Tokens.pushObject(); err != nil {
|
||||
return Token{}, d.injectSyntacticErrorWithPosition(err, pos)
|
||||
return Token{}, wrapSyntacticError(d, err, pos, +1)
|
||||
}
|
||||
d.Names.push()
|
||||
if !d.Flags.Get(jsonflags.AllowDuplicateNames) {
|
||||
d.Names.push()
|
||||
d.Namespaces.push()
|
||||
}
|
||||
pos += 1
|
||||
d.prevStart, d.prevEnd = pos, pos
|
||||
return ObjectStart, nil
|
||||
return BeginObject, nil
|
||||
|
||||
case '}':
|
||||
if err = d.Tokens.popObject(); err != nil {
|
||||
return Token{}, d.injectSyntacticErrorWithPosition(err, pos)
|
||||
return Token{}, wrapSyntacticError(d, err, pos, +1)
|
||||
}
|
||||
d.Names.pop()
|
||||
if !d.Flags.Get(jsonflags.AllowDuplicateNames) {
|
||||
d.Names.pop()
|
||||
d.Namespaces.pop()
|
||||
}
|
||||
pos += 1
|
||||
d.prevStart, d.prevEnd = pos, pos
|
||||
return ObjectEnd, nil
|
||||
return EndObject, nil
|
||||
|
||||
case '[':
|
||||
if err = d.Tokens.pushArray(); err != nil {
|
||||
return Token{}, d.injectSyntacticErrorWithPosition(err, pos)
|
||||
return Token{}, wrapSyntacticError(d, err, pos, +1)
|
||||
}
|
||||
pos += 1
|
||||
d.prevStart, d.prevEnd = pos, pos
|
||||
return ArrayStart, nil
|
||||
return BeginArray, nil
|
||||
|
||||
case ']':
|
||||
if err = d.Tokens.popArray(); err != nil {
|
||||
return Token{}, d.injectSyntacticErrorWithPosition(err, pos)
|
||||
return Token{}, wrapSyntacticError(d, err, pos, +1)
|
||||
}
|
||||
pos += 1
|
||||
d.prevStart, d.prevEnd = pos, pos
|
||||
return ArrayEnd, nil
|
||||
return EndArray, nil
|
||||
|
||||
default:
|
||||
err = newInvalidCharacterError(d.buf[pos:], "at start of token")
|
||||
return Token{}, d.injectSyntacticErrorWithPosition(err, pos)
|
||||
err = jsonwire.NewInvalidCharacterError(d.buf[pos:], "at start of value")
|
||||
return Token{}, wrapSyntacticError(d, err, pos, +1)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -612,7 +691,7 @@ func (d *decoderState) ReadValue(flags *jsonwire.ValueFlags) (Value, error) {
|
||||
if err == io.ErrUnexpectedEOF && d.Tokens.Depth() == 1 {
|
||||
err = io.EOF // EOF possibly if no Tokens present after top-level value
|
||||
}
|
||||
return nil, err
|
||||
return nil, wrapSyntacticError(d, err, pos, 0)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -624,6 +703,7 @@ func (d *decoderState) ReadValue(flags *jsonwire.ValueFlags) (Value, error) {
|
||||
pos += jsonwire.ConsumeWhitespace(d.buf[pos:])
|
||||
if d.needMore(pos) {
|
||||
if pos, err = d.consumeWhitespace(pos); err != nil {
|
||||
err = wrapSyntacticError(d, err, pos, 0)
|
||||
return nil, d.checkDelimBeforeIOError(delim, err)
|
||||
}
|
||||
}
|
||||
@@ -640,20 +720,22 @@ func (d *decoderState) ReadValue(flags *jsonwire.ValueFlags) (Value, error) {
|
||||
newAbsPos := d.baseOffset + int64(pos)
|
||||
n := int(newAbsPos - oldAbsPos)
|
||||
if err != nil {
|
||||
return nil, d.injectSyntacticErrorWithPosition(err, pos)
|
||||
return nil, wrapSyntacticError(d, err, pos, +1)
|
||||
}
|
||||
switch next {
|
||||
case 'n', 't', 'f':
|
||||
err = d.Tokens.appendLiteral()
|
||||
case '"':
|
||||
if !d.Flags.Get(jsonflags.AllowDuplicateNames) && d.Tokens.Last.NeedObjectName() {
|
||||
if !d.Tokens.Last.isValidNamespace() {
|
||||
err = errInvalidNamespace
|
||||
break
|
||||
}
|
||||
if d.Tokens.Last.isActiveNamespace() && !d.Namespaces.Last().insertQuoted(d.buf[pos-n:pos], flags.IsVerbatim()) {
|
||||
err = newDuplicateNameError(d.buf[pos-n : pos])
|
||||
break
|
||||
if d.Tokens.Last.NeedObjectName() {
|
||||
if !d.Flags.Get(jsonflags.AllowDuplicateNames) {
|
||||
if !d.Tokens.Last.isValidNamespace() {
|
||||
err = errInvalidNamespace
|
||||
break
|
||||
}
|
||||
if d.Tokens.Last.isActiveNamespace() && !d.Namespaces.Last().insertQuoted(d.buf[pos-n:pos], flags.IsVerbatim()) {
|
||||
err = wrapWithObjectName(ErrDuplicateName, d.buf[pos-n:pos])
|
||||
break
|
||||
}
|
||||
}
|
||||
d.Names.ReplaceLastQuotedOffset(pos - n) // only replace if insertQuoted succeeds
|
||||
}
|
||||
@@ -676,19 +758,36 @@ func (d *decoderState) ReadValue(flags *jsonwire.ValueFlags) (Value, error) {
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return nil, d.injectSyntacticErrorWithPosition(err, pos-n) // report position at start of value
|
||||
return nil, wrapSyntacticError(d, err, pos-n, +1) // report position at start of value
|
||||
}
|
||||
d.prevEnd = pos
|
||||
d.prevStart = pos - n
|
||||
return d.buf[pos-n : pos : pos], nil
|
||||
}
|
||||
|
||||
// CheckNextValue checks whether the next value is syntactically valid,
|
||||
// but does not advance the read offset.
|
||||
func (d *decoderState) CheckNextValue() error {
|
||||
d.PeekKind() // populates d.peekPos and d.peekErr
|
||||
pos, err := d.peekPos, d.peekErr
|
||||
d.peekPos, d.peekErr = 0, nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var flags jsonwire.ValueFlags
|
||||
if pos, err := d.consumeValue(&flags, pos, d.Tokens.Depth()); err != nil {
|
||||
return wrapSyntacticError(d, err, pos, +1)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CheckEOF verifies that the input has no more data.
|
||||
func (d *decoderState) CheckEOF() error {
|
||||
switch pos, err := d.consumeWhitespace(d.prevEnd); err {
|
||||
case nil:
|
||||
err := newInvalidCharacterError(d.buf[pos:], "after top-level value")
|
||||
return d.injectSyntacticErrorWithPosition(err, pos)
|
||||
err := jsonwire.NewInvalidCharacterError(d.buf[pos:], "after top-level value")
|
||||
return wrapSyntacticError(d, err, pos, 0)
|
||||
case io.ErrUnexpectedEOF:
|
||||
return nil
|
||||
default:
|
||||
@@ -763,14 +862,17 @@ func (d *decoderState) consumeValue(flags *jsonwire.ValueFlags, pos, depth int)
|
||||
case '[':
|
||||
return d.consumeArray(flags, pos, depth)
|
||||
default:
|
||||
return pos, newInvalidCharacterError(d.buf[pos:], "at start of value")
|
||||
if (d.Tokens.Last.isObject() && next == ']') || (d.Tokens.Last.isArray() && next == '}') {
|
||||
return pos, errMismatchDelim
|
||||
}
|
||||
return pos, jsonwire.NewInvalidCharacterError(d.buf[pos:], "at start of value")
|
||||
}
|
||||
if err == io.ErrUnexpectedEOF {
|
||||
absPos := d.baseOffset + int64(pos)
|
||||
err = d.fetch() // will mutate d.buf and invalidate pos
|
||||
pos = int(absPos - d.baseOffset)
|
||||
if err != nil {
|
||||
return pos, err
|
||||
return pos + n, err
|
||||
}
|
||||
continue
|
||||
}
|
||||
@@ -788,7 +890,7 @@ func (d *decoderState) consumeLiteral(pos int, lit string) (newPos int, err erro
|
||||
err = d.fetch() // will mutate d.buf and invalidate pos
|
||||
pos = int(absPos - d.baseOffset)
|
||||
if err != nil {
|
||||
return pos, err
|
||||
return pos + n, err
|
||||
}
|
||||
continue
|
||||
}
|
||||
@@ -807,7 +909,7 @@ func (d *decoderState) consumeString(flags *jsonwire.ValueFlags, pos int) (newPo
|
||||
err = d.fetch() // will mutate d.buf and invalidate pos
|
||||
pos = int(absPos - d.baseOffset)
|
||||
if err != nil {
|
||||
return pos, err
|
||||
return pos + n, err
|
||||
}
|
||||
continue
|
||||
}
|
||||
@@ -894,19 +996,21 @@ func (d *decoderState) consumeObject(flags *jsonwire.ValueFlags, pos, depth int)
|
||||
} else {
|
||||
pos += n
|
||||
}
|
||||
if !d.Flags.Get(jsonflags.AllowDuplicateNames) && !names.insertQuoted(d.buf[pos-n:pos], flags2.IsVerbatim()) {
|
||||
return pos - n, newDuplicateNameError(d.buf[pos-n : pos])
|
||||
quotedName := d.buf[pos-n : pos]
|
||||
if !d.Flags.Get(jsonflags.AllowDuplicateNames) && !names.insertQuoted(quotedName, flags2.IsVerbatim()) {
|
||||
return pos - n, wrapWithObjectName(ErrDuplicateName, quotedName)
|
||||
}
|
||||
|
||||
// Handle after name.
|
||||
pos += jsonwire.ConsumeWhitespace(d.buf[pos:])
|
||||
if d.needMore(pos) {
|
||||
if pos, err = d.consumeWhitespace(pos); err != nil {
|
||||
return pos, err
|
||||
return pos, wrapWithObjectName(err, quotedName)
|
||||
}
|
||||
}
|
||||
if d.buf[pos] != ':' {
|
||||
return pos, newInvalidCharacterError(d.buf[pos:], "after object name (expecting ':')")
|
||||
err := jsonwire.NewInvalidCharacterError(d.buf[pos:], "after object name (expecting ':')")
|
||||
return pos, wrapWithObjectName(err, quotedName)
|
||||
}
|
||||
pos++
|
||||
|
||||
@@ -914,12 +1018,12 @@ func (d *decoderState) consumeObject(flags *jsonwire.ValueFlags, pos, depth int)
|
||||
pos += jsonwire.ConsumeWhitespace(d.buf[pos:])
|
||||
if d.needMore(pos) {
|
||||
if pos, err = d.consumeWhitespace(pos); err != nil {
|
||||
return pos, err
|
||||
return pos, wrapWithObjectName(err, quotedName)
|
||||
}
|
||||
}
|
||||
pos, err = d.consumeValue(flags, pos, depth)
|
||||
if err != nil {
|
||||
return pos, err
|
||||
return pos, wrapWithObjectName(err, quotedName)
|
||||
}
|
||||
|
||||
// Handle after value.
|
||||
@@ -937,7 +1041,7 @@ func (d *decoderState) consumeObject(flags *jsonwire.ValueFlags, pos, depth int)
|
||||
pos++
|
||||
return pos, nil
|
||||
default:
|
||||
return pos, newInvalidCharacterError(d.buf[pos:], "after object value (expecting ',' or '}')")
|
||||
return pos, jsonwire.NewInvalidCharacterError(d.buf[pos:], "after object value (expecting ',' or '}')")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -965,6 +1069,7 @@ func (d *decoderState) consumeArray(flags *jsonwire.ValueFlags, pos, depth int)
|
||||
return pos, nil
|
||||
}
|
||||
|
||||
var idx int64
|
||||
depth++
|
||||
for {
|
||||
// Handle before value.
|
||||
@@ -976,7 +1081,7 @@ func (d *decoderState) consumeArray(flags *jsonwire.ValueFlags, pos, depth int)
|
||||
}
|
||||
pos, err = d.consumeValue(flags, pos, depth)
|
||||
if err != nil {
|
||||
return pos, err
|
||||
return pos, wrapWithArrayIndex(err, idx)
|
||||
}
|
||||
|
||||
// Handle after value.
|
||||
@@ -989,12 +1094,13 @@ func (d *decoderState) consumeArray(flags *jsonwire.ValueFlags, pos, depth int)
|
||||
switch d.buf[pos] {
|
||||
case ',':
|
||||
pos++
|
||||
idx++
|
||||
continue
|
||||
case ']':
|
||||
pos++
|
||||
return pos, nil
|
||||
default:
|
||||
return pos, newInvalidCharacterError(d.buf[pos:], "after array value (expecting ',' or ']')")
|
||||
return pos, jsonwire.NewInvalidCharacterError(d.buf[pos:], "after array element (expecting ',' or ']')")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1017,8 +1123,8 @@ func (d *Decoder) UnreadBuffer() []byte {
|
||||
|
||||
// StackDepth returns the depth of the state machine for read JSON data.
|
||||
// Each level on the stack represents a nested JSON object or array.
|
||||
// It is incremented whenever an [ObjectStart] or [ArrayStart] token is encountered
|
||||
// and decremented whenever an [ObjectEnd] or [ArrayEnd] token is encountered.
|
||||
// It is incremented whenever an [BeginObject] or [BeginArray] token is encountered
|
||||
// and decremented whenever an [EndObject] or [EndArray] token is encountered.
|
||||
// The depth is zero-indexed, where zero represents the top-level JSON value.
|
||||
func (d *Decoder) StackDepth() int {
|
||||
// NOTE: Keep in sync with Encoder.StackDepth.
|
||||
@@ -1037,7 +1143,7 @@ func (d *Decoder) StackDepth() int {
|
||||
// Each name and value in a JSON object is counted separately,
|
||||
// so the effective number of members would be half the length.
|
||||
// A complete JSON object must have an even length.
|
||||
func (d *Decoder) StackIndex(i int) (Kind, int) {
|
||||
func (d *Decoder) StackIndex(i int) (Kind, int64) {
|
||||
// NOTE: Keep in sync with Encoder.StackIndex.
|
||||
switch s := d.s.Tokens.index(i); {
|
||||
case i > 0 && s.isObject():
|
||||
@@ -1050,9 +1156,11 @@ func (d *Decoder) StackIndex(i int) (Kind, int) {
|
||||
}
|
||||
|
||||
// StackPointer returns a JSON Pointer (RFC 6901) to the most recently read value.
|
||||
// Object names are only present if [AllowDuplicateNames] is false, otherwise
|
||||
// object members are represented using their index within the object.
|
||||
func (d *Decoder) StackPointer() string {
|
||||
d.s.Names.copyQuotedBuffer(d.s.buf)
|
||||
return string(d.s.appendStackPointer(nil))
|
||||
func (d *Decoder) StackPointer() Pointer {
|
||||
return Pointer(d.s.AppendStackPointer(nil, -1))
|
||||
}
|
||||
|
||||
func (d *decoderState) AppendStackPointer(b []byte, where int) []byte {
|
||||
d.Names.copyQuotedBuffer(d.buf)
|
||||
return d.state.appendStackPointer(b, where)
|
||||
}
|
||||
|
||||
266
vendor/github.com/go-json-experiment/json/jsontext/encode.go
generated
vendored
266
vendor/github.com/go-json-experiment/json/jsontext/encode.go
generated
vendored
@@ -25,19 +25,19 @@ import (
|
||||
//
|
||||
// can be composed with the following calls (ignoring errors for brevity):
|
||||
//
|
||||
// e.WriteToken(ObjectStart) // {
|
||||
// e.WriteToken(BeginObject) // {
|
||||
// e.WriteToken(String("name")) // "name"
|
||||
// e.WriteToken(String("value")) // "value"
|
||||
// e.WriteValue(Value(`"array"`)) // "array"
|
||||
// e.WriteToken(ArrayStart) // [
|
||||
// e.WriteToken(BeginArray) // [
|
||||
// e.WriteToken(Null) // null
|
||||
// e.WriteToken(False) // false
|
||||
// e.WriteValue(Value("true")) // true
|
||||
// e.WriteToken(Float(3.14159)) // 3.14159
|
||||
// e.WriteToken(ArrayEnd) // ]
|
||||
// e.WriteToken(EndArray) // ]
|
||||
// e.WriteValue(Value(`"object"`)) // "object"
|
||||
// e.WriteValue(Value(`{"k":"v"}`)) // {"k":"v"}
|
||||
// e.WriteToken(ObjectEnd) // }
|
||||
// e.WriteToken(EndObject) // }
|
||||
//
|
||||
// The above is one of many possible sequence of calls and
|
||||
// may not represent the most sensible method to call for any given token/value.
|
||||
@@ -94,8 +94,8 @@ func NewEncoder(w io.Writer, opts ...Options) *Encoder {
|
||||
|
||||
// Reset resets an encoder such that it is writing afresh to w and
|
||||
// configured with the provided options. Reset must not be called on
|
||||
// a Encoder passed to the [encoding/json/v2.MarshalerV2.MarshalJSONV2] method
|
||||
// or the [encoding/json/v2.MarshalFuncV2] function.
|
||||
// a Encoder passed to the [encoding/json/v2.MarshalerTo.MarshalJSONTo] method
|
||||
// or the [encoding/json/v2.MarshalToFunc] function.
|
||||
func (e *Encoder) Reset(w io.Writer, opts ...Options) {
|
||||
switch {
|
||||
case e == nil:
|
||||
@@ -103,7 +103,7 @@ func (e *Encoder) Reset(w io.Writer, opts ...Options) {
|
||||
case w == nil:
|
||||
panic("jsontext: invalid nil io.Writer")
|
||||
case e.s.Flags.Get(jsonflags.WithinArshalCall):
|
||||
panic("jsontext: cannot reset Encoder passed to json.MarshalerV2")
|
||||
panic("jsontext: cannot reset Encoder passed to json.MarshalerTo")
|
||||
}
|
||||
e.s.reset(nil, w, opts...)
|
||||
}
|
||||
@@ -114,13 +114,35 @@ func (e *encoderState) reset(b []byte, w io.Writer, opts ...Options) {
|
||||
if bb, ok := w.(*bytes.Buffer); ok && bb != nil {
|
||||
e.Buf = bb.Bytes()[bb.Len():] // alias the unused buffer of bb
|
||||
}
|
||||
e.Struct = jsonopts.Struct{}
|
||||
e.Struct.Join(opts...)
|
||||
if e.Flags.Get(jsonflags.Expand) && !e.Flags.Has(jsonflags.Indent) {
|
||||
e.Indent = "\t"
|
||||
opts2 := jsonopts.Struct{} // avoid mutating e.Struct in case it is part of opts
|
||||
opts2.Join(opts...)
|
||||
e.Struct = opts2
|
||||
if e.Flags.Get(jsonflags.Multiline) {
|
||||
if !e.Flags.Has(jsonflags.SpaceAfterColon) {
|
||||
e.Flags.Set(jsonflags.SpaceAfterColon | 1)
|
||||
}
|
||||
if !e.Flags.Has(jsonflags.SpaceAfterComma) {
|
||||
e.Flags.Set(jsonflags.SpaceAfterComma | 0)
|
||||
}
|
||||
if !e.Flags.Has(jsonflags.Indent) {
|
||||
e.Flags.Set(jsonflags.Indent | 1)
|
||||
e.Indent = "\t"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Options returns the options used to construct the decoder and
|
||||
// may additionally contain semantic options passed to a
|
||||
// [encoding/json/v2.MarshalEncode] call.
|
||||
//
|
||||
// If operating within
|
||||
// a [encoding/json/v2.MarshalerTo.MarshalJSONTo] method call or
|
||||
// a [encoding/json/v2.MarshalToFunc] function call,
|
||||
// then the returned options are only valid within the call.
|
||||
func (e *Encoder) Options() Options {
|
||||
return &e.s.Struct
|
||||
}
|
||||
|
||||
// NeedFlush determines whether to flush at this point.
|
||||
func (e *encoderState) NeedFlush() bool {
|
||||
// NOTE: This function is carefully written to be inlinable.
|
||||
@@ -200,17 +222,7 @@ func (e *encoderState) Flush() error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// injectSyntacticErrorWithPosition wraps a SyntacticError with the position,
|
||||
// otherwise it returns the error as is.
|
||||
// It takes a position relative to the start of the start of e.buf.
|
||||
func (e *encodeBuffer) injectSyntacticErrorWithPosition(err error, pos int) error {
|
||||
if serr, ok := err.(*SyntacticError); ok {
|
||||
return serr.withOffset(e.baseOffset + int64(pos))
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (d *encodeBuffer) offsetAt(pos int) int64 { return d.baseOffset + int64(pos) }
|
||||
func (e *encodeBuffer) previousOffsetEnd() int64 { return e.baseOffset + int64(len(e.Buf)) }
|
||||
func (e *encodeBuffer) unflushedBuffer() []byte { return e.Buf }
|
||||
|
||||
@@ -219,7 +231,7 @@ func (e *encodeBuffer) unflushedBuffer() []byte { return e.Buf }
|
||||
func (e *encoderState) avoidFlush() bool {
|
||||
switch {
|
||||
case e.Tokens.Last.Length() == 0:
|
||||
// Never flush after ObjectStart or ArrayStart since we don't know yet
|
||||
// Never flush after BeginObject or BeginArray since we don't know yet
|
||||
// if the object or array will end up being empty.
|
||||
return true
|
||||
case e.Tokens.Last.needObjectValue():
|
||||
@@ -286,11 +298,11 @@ func (e *encoderState) UnwriteEmptyObjectMember(prevName *string) bool {
|
||||
if e.Tokens.Last.isActiveNamespace() {
|
||||
e.Namespaces.Last().removeLast()
|
||||
}
|
||||
e.Names.clearLast()
|
||||
if prevName != nil {
|
||||
e.Names.copyQuotedBuffer(e.Buf) // required by objectNameStack.replaceLastUnquotedName
|
||||
e.Names.replaceLastUnquotedName(*prevName)
|
||||
}
|
||||
}
|
||||
e.Names.clearLast()
|
||||
if prevName != nil {
|
||||
e.Names.copyQuotedBuffer(e.Buf) // required by objectNameStack.replaceLastUnquotedName
|
||||
e.Names.replaceLastUnquotedName(*prevName)
|
||||
}
|
||||
return true
|
||||
}
|
||||
@@ -314,8 +326,8 @@ func (e *encoderState) UnwriteOnlyObjectMemberName() string {
|
||||
if e.Tokens.Last.isActiveNamespace() {
|
||||
e.Namespaces.Last().removeLast()
|
||||
}
|
||||
e.Names.clearLast()
|
||||
}
|
||||
e.Names.clearLast()
|
||||
return name
|
||||
}
|
||||
|
||||
@@ -337,7 +349,7 @@ func (e *encoderState) WriteToken(t Token) error {
|
||||
|
||||
// Append any delimiters or optional whitespace.
|
||||
b = e.Tokens.MayAppendDelim(b, k)
|
||||
if e.Flags.Get(jsonflags.Expand) {
|
||||
if e.Flags.Get(jsonflags.AnyWhitespace) {
|
||||
b = e.appendWhitespace(b, k)
|
||||
}
|
||||
pos := len(b) // offset before the token
|
||||
@@ -358,20 +370,22 @@ func (e *encoderState) WriteToken(t Token) error {
|
||||
if b, err = t.appendString(b, &e.Flags); err != nil {
|
||||
break
|
||||
}
|
||||
if !e.Flags.Get(jsonflags.AllowDuplicateNames) && e.Tokens.Last.NeedObjectName() {
|
||||
if !e.Tokens.Last.isValidNamespace() {
|
||||
err = errInvalidNamespace
|
||||
break
|
||||
}
|
||||
if e.Tokens.Last.isActiveNamespace() && !e.Namespaces.Last().insertQuoted(b[pos:], false) {
|
||||
err = newDuplicateNameError(b[pos:])
|
||||
break
|
||||
if e.Tokens.Last.NeedObjectName() {
|
||||
if !e.Flags.Get(jsonflags.AllowDuplicateNames) {
|
||||
if !e.Tokens.Last.isValidNamespace() {
|
||||
err = errInvalidNamespace
|
||||
break
|
||||
}
|
||||
if e.Tokens.Last.isActiveNamespace() && !e.Namespaces.Last().insertQuoted(b[pos:], false) {
|
||||
err = wrapWithObjectName(ErrDuplicateName, b[pos:])
|
||||
break
|
||||
}
|
||||
}
|
||||
e.Names.ReplaceLastQuotedOffset(pos) // only replace if insertQuoted succeeds
|
||||
}
|
||||
err = e.Tokens.appendString()
|
||||
case '0':
|
||||
if b, err = t.appendNumber(b, e.Flags.Get(jsonflags.CanonicalizeNumbers)); err != nil {
|
||||
if b, err = t.appendNumber(b, &e.Flags); err != nil {
|
||||
break
|
||||
}
|
||||
err = e.Tokens.appendNumber()
|
||||
@@ -380,8 +394,8 @@ func (e *encoderState) WriteToken(t Token) error {
|
||||
if err = e.Tokens.pushObject(); err != nil {
|
||||
break
|
||||
}
|
||||
e.Names.push()
|
||||
if !e.Flags.Get(jsonflags.AllowDuplicateNames) {
|
||||
e.Names.push()
|
||||
e.Namespaces.push()
|
||||
}
|
||||
case '}':
|
||||
@@ -389,8 +403,8 @@ func (e *encoderState) WriteToken(t Token) error {
|
||||
if err = e.Tokens.popObject(); err != nil {
|
||||
break
|
||||
}
|
||||
e.Names.pop()
|
||||
if !e.Flags.Get(jsonflags.AllowDuplicateNames) {
|
||||
e.Names.pop()
|
||||
e.Namespaces.pop()
|
||||
}
|
||||
case '[':
|
||||
@@ -400,10 +414,10 @@ func (e *encoderState) WriteToken(t Token) error {
|
||||
b = append(b, ']')
|
||||
err = e.Tokens.popArray()
|
||||
default:
|
||||
err = &SyntacticError{str: "invalid json.Token"}
|
||||
err = errInvalidToken
|
||||
}
|
||||
if err != nil {
|
||||
return e.injectSyntacticErrorWithPosition(err, pos)
|
||||
return wrapSyntacticError(e, err, pos, +1)
|
||||
}
|
||||
|
||||
// Finish off the buffer and store it back into e.
|
||||
@@ -428,7 +442,7 @@ func (e *encoderState) AppendRaw(k Kind, safeASCII bool, appendFn func([]byte) (
|
||||
|
||||
// Append any delimiters or optional whitespace.
|
||||
b = e.Tokens.MayAppendDelim(b, k)
|
||||
if e.Flags.Get(jsonflags.Expand) {
|
||||
if e.Flags.Get(jsonflags.AnyWhitespace) {
|
||||
b = e.appendWhitespace(b, k)
|
||||
}
|
||||
pos := len(b) // offset before the token
|
||||
@@ -453,30 +467,32 @@ func (e *encoderState) AppendRaw(k Kind, safeASCII bool, appendFn func([]byte) (
|
||||
b, err = jsonwire.AppendQuote(b[:pos], string(b2), &e.Flags)
|
||||
e.unusedCache = b2[:0]
|
||||
if err != nil {
|
||||
return e.injectSyntacticErrorWithPosition(err, pos)
|
||||
return wrapSyntacticError(e, err, pos, +1)
|
||||
}
|
||||
}
|
||||
|
||||
// Update the state machine.
|
||||
if !e.Flags.Get(jsonflags.AllowDuplicateNames) && e.Tokens.Last.NeedObjectName() {
|
||||
if !e.Tokens.Last.isValidNamespace() {
|
||||
return errInvalidNamespace
|
||||
}
|
||||
if e.Tokens.Last.isActiveNamespace() && !e.Namespaces.Last().insertQuoted(b[pos:], isVerbatim) {
|
||||
err := newDuplicateNameError(b[pos:])
|
||||
return e.injectSyntacticErrorWithPosition(err, pos)
|
||||
if e.Tokens.Last.NeedObjectName() {
|
||||
if !e.Flags.Get(jsonflags.AllowDuplicateNames) {
|
||||
if !e.Tokens.Last.isValidNamespace() {
|
||||
return wrapSyntacticError(e, err, pos, +1)
|
||||
}
|
||||
if e.Tokens.Last.isActiveNamespace() && !e.Namespaces.Last().insertQuoted(b[pos:], isVerbatim) {
|
||||
err = wrapWithObjectName(ErrDuplicateName, b[pos:])
|
||||
return wrapSyntacticError(e, err, pos, +1)
|
||||
}
|
||||
}
|
||||
e.Names.ReplaceLastQuotedOffset(pos) // only replace if insertQuoted succeeds
|
||||
}
|
||||
if err := e.Tokens.appendString(); err != nil {
|
||||
return e.injectSyntacticErrorWithPosition(err, pos)
|
||||
return wrapSyntacticError(e, err, pos, +1)
|
||||
}
|
||||
case '0':
|
||||
if b, err = appendFn(b); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := e.Tokens.appendNumber(); err != nil {
|
||||
return e.injectSyntacticErrorWithPosition(err, pos)
|
||||
return wrapSyntacticError(e, err, pos, +1)
|
||||
}
|
||||
default:
|
||||
panic("BUG: invalid kind")
|
||||
@@ -513,7 +529,7 @@ func (e *encoderState) WriteValue(v Value) error {
|
||||
|
||||
// Append any delimiters or optional whitespace.
|
||||
b = e.Tokens.MayAppendDelim(b, k)
|
||||
if e.Flags.Get(jsonflags.Expand) {
|
||||
if e.Flags.Get(jsonflags.AnyWhitespace) {
|
||||
b = e.appendWhitespace(b, k)
|
||||
}
|
||||
pos := len(b) // offset before the value
|
||||
@@ -523,13 +539,13 @@ func (e *encoderState) WriteValue(v Value) error {
|
||||
n += jsonwire.ConsumeWhitespace(v[n:])
|
||||
b, m, err := e.reformatValue(b, v[n:], e.Tokens.Depth())
|
||||
if err != nil {
|
||||
return e.injectSyntacticErrorWithPosition(err, pos+n+m)
|
||||
return wrapSyntacticError(e, err, pos+n+m, +1)
|
||||
}
|
||||
n += m
|
||||
n += jsonwire.ConsumeWhitespace(v[n:])
|
||||
if len(v) > n {
|
||||
err = newInvalidCharacterError(v[n:], "after top-level value")
|
||||
return e.injectSyntacticErrorWithPosition(err, pos+n)
|
||||
err = jsonwire.NewInvalidCharacterError(v[n:], "after top-level value")
|
||||
return wrapSyntacticError(e, err, pos+n, 0)
|
||||
}
|
||||
|
||||
// Append the kind to the state machine.
|
||||
@@ -537,14 +553,16 @@ func (e *encoderState) WriteValue(v Value) error {
|
||||
case 'n', 'f', 't':
|
||||
err = e.Tokens.appendLiteral()
|
||||
case '"':
|
||||
if !e.Flags.Get(jsonflags.AllowDuplicateNames) && e.Tokens.Last.NeedObjectName() {
|
||||
if !e.Tokens.Last.isValidNamespace() {
|
||||
err = errInvalidNamespace
|
||||
break
|
||||
}
|
||||
if e.Tokens.Last.isActiveNamespace() && !e.Namespaces.Last().insertQuoted(b[pos:], false) {
|
||||
err = newDuplicateNameError(b[pos:])
|
||||
break
|
||||
if e.Tokens.Last.NeedObjectName() {
|
||||
if !e.Flags.Get(jsonflags.AllowDuplicateNames) {
|
||||
if !e.Tokens.Last.isValidNamespace() {
|
||||
err = errInvalidNamespace
|
||||
break
|
||||
}
|
||||
if e.Tokens.Last.isActiveNamespace() && !e.Namespaces.Last().insertQuoted(b[pos:], false) {
|
||||
err = wrapWithObjectName(ErrDuplicateName, b[pos:])
|
||||
break
|
||||
}
|
||||
}
|
||||
e.Names.ReplaceLastQuotedOffset(pos) // only replace if insertQuoted succeeds
|
||||
}
|
||||
@@ -558,6 +576,9 @@ func (e *encoderState) WriteValue(v Value) error {
|
||||
if err = e.Tokens.popObject(); err != nil {
|
||||
panic("BUG: popObject should never fail immediately after pushObject: " + err.Error())
|
||||
}
|
||||
if e.Flags.Get(jsonflags.ReorderRawObjects) {
|
||||
mustReorderObjects(b[pos:])
|
||||
}
|
||||
case '[':
|
||||
if err = e.Tokens.pushArray(); err != nil {
|
||||
break
|
||||
@@ -565,9 +586,12 @@ func (e *encoderState) WriteValue(v Value) error {
|
||||
if err = e.Tokens.popArray(); err != nil {
|
||||
panic("BUG: popArray should never fail immediately after pushArray: " + err.Error())
|
||||
}
|
||||
if e.Flags.Get(jsonflags.ReorderRawObjects) {
|
||||
mustReorderObjects(b[pos:])
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return e.injectSyntacticErrorWithPosition(err, pos)
|
||||
return wrapSyntacticError(e, err, pos, +1)
|
||||
}
|
||||
|
||||
// Finish off the buffer and store it back into e.
|
||||
@@ -578,13 +602,47 @@ func (e *encoderState) WriteValue(v Value) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// CountNextDelimWhitespace counts the number of bytes of delimiter and
|
||||
// whitespace bytes assuming the upcoming token is a JSON value.
|
||||
// This method is used for error reporting at the semantic layer.
|
||||
func (e *encoderState) CountNextDelimWhitespace() (n int) {
|
||||
const next = Kind('"') // arbitrary kind as next JSON value
|
||||
delim := e.Tokens.needDelim(next)
|
||||
if delim > 0 {
|
||||
n += len(",") | len(":")
|
||||
}
|
||||
if delim == ':' {
|
||||
if e.Flags.Get(jsonflags.SpaceAfterColon) {
|
||||
n += len(" ")
|
||||
}
|
||||
} else {
|
||||
if delim == ',' && e.Flags.Get(jsonflags.SpaceAfterComma) {
|
||||
n += len(" ")
|
||||
}
|
||||
if e.Flags.Get(jsonflags.Multiline) {
|
||||
if m := e.Tokens.NeedIndent(next); m > 0 {
|
||||
n += len("\n") + len(e.IndentPrefix) + (m-1)*len(e.Indent)
|
||||
}
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// appendWhitespace appends whitespace that immediately precedes the next token.
|
||||
func (e *encoderState) appendWhitespace(b []byte, next Kind) []byte {
|
||||
if e.Tokens.needDelim(next) == ':' {
|
||||
return append(b, ' ')
|
||||
if delim := e.Tokens.needDelim(next); delim == ':' {
|
||||
if e.Flags.Get(jsonflags.SpaceAfterColon) {
|
||||
b = append(b, ' ')
|
||||
}
|
||||
} else {
|
||||
return e.AppendIndent(b, e.Tokens.NeedIndent(next))
|
||||
if delim == ',' && e.Flags.Get(jsonflags.SpaceAfterComma) {
|
||||
b = append(b, ' ')
|
||||
}
|
||||
if e.Flags.Get(jsonflags.Multiline) {
|
||||
b = e.AppendIndent(b, e.Tokens.NeedIndent(next))
|
||||
}
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// AppendIndent appends the appropriate number of indentation characters
|
||||
@@ -630,22 +688,22 @@ func (e *encoderState) reformatValue(dst []byte, src Value, depth int) ([]byte,
|
||||
return append(dst, "true"...), len("true"), nil
|
||||
case '"':
|
||||
if n := jsonwire.ConsumeSimpleString(src); n > 0 {
|
||||
dst, src = append(dst, src[:n]...), src[n:] // copy simple strings verbatim
|
||||
dst = append(dst, src[:n]...) // copy simple strings verbatim
|
||||
return dst, n, nil
|
||||
}
|
||||
return jsonwire.ReformatString(dst, src, &e.Flags)
|
||||
case '0':
|
||||
if n := jsonwire.ConsumeSimpleNumber(src); n > 0 && !e.Flags.Get(jsonflags.CanonicalizeNumbers) {
|
||||
dst, src = append(dst, src[:n]...), src[n:] // copy simple numbers verbatim
|
||||
dst = append(dst, src[:n]...) // copy simple numbers verbatim
|
||||
return dst, n, nil
|
||||
}
|
||||
return jsonwire.ReformatNumber(dst, src, e.Flags.Get(jsonflags.CanonicalizeNumbers))
|
||||
return jsonwire.ReformatNumber(dst, src, &e.Flags)
|
||||
case '{':
|
||||
return e.reformatObject(dst, src, depth)
|
||||
case '[':
|
||||
return e.reformatArray(dst, src, depth)
|
||||
default:
|
||||
return dst, 0, newInvalidCharacterError(src, "at start of value")
|
||||
return dst, 0, jsonwire.NewInvalidCharacterError(src, "at start of value")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -683,7 +741,7 @@ func (e *encoderState) reformatObject(dst []byte, src Value, depth int) ([]byte,
|
||||
depth++
|
||||
for {
|
||||
// Append optional newline and indentation.
|
||||
if e.Flags.Get(jsonflags.Expand) {
|
||||
if e.Flags.Get(jsonflags.Multiline) {
|
||||
dst = e.AppendIndent(dst, depth)
|
||||
}
|
||||
|
||||
@@ -693,7 +751,8 @@ func (e *encoderState) reformatObject(dst []byte, src Value, depth int) ([]byte,
|
||||
return dst, n, io.ErrUnexpectedEOF
|
||||
}
|
||||
m := jsonwire.ConsumeSimpleString(src[n:])
|
||||
if m > 0 {
|
||||
isVerbatim := m > 0
|
||||
if isVerbatim {
|
||||
dst = append(dst, src[n:n+m]...)
|
||||
} else {
|
||||
dst, m, err = jsonwire.ReformatString(dst, src[n:], &e.Flags)
|
||||
@@ -701,34 +760,35 @@ func (e *encoderState) reformatObject(dst []byte, src Value, depth int) ([]byte,
|
||||
return dst, n + m, err
|
||||
}
|
||||
}
|
||||
// TODO: Specify whether the name is verbatim or not.
|
||||
if !e.Flags.Get(jsonflags.AllowDuplicateNames) && !names.insertQuoted(src[n:n+m], false) {
|
||||
return dst, n, newDuplicateNameError(src[n : n+m])
|
||||
quotedName := src[n : n+m]
|
||||
if !e.Flags.Get(jsonflags.AllowDuplicateNames) && !names.insertQuoted(quotedName, isVerbatim) {
|
||||
return dst, n, wrapWithObjectName(ErrDuplicateName, quotedName)
|
||||
}
|
||||
n += m
|
||||
|
||||
// Append colon.
|
||||
n += jsonwire.ConsumeWhitespace(src[n:])
|
||||
if uint(len(src)) <= uint(n) {
|
||||
return dst, n, io.ErrUnexpectedEOF
|
||||
return dst, n, wrapWithObjectName(io.ErrUnexpectedEOF, quotedName)
|
||||
}
|
||||
if src[n] != ':' {
|
||||
return dst, n, newInvalidCharacterError(src[n:], "after object name (expecting ':')")
|
||||
err = jsonwire.NewInvalidCharacterError(src[n:], "after object name (expecting ':')")
|
||||
return dst, n, wrapWithObjectName(err, quotedName)
|
||||
}
|
||||
dst = append(dst, ':')
|
||||
n += len(":")
|
||||
if e.Flags.Get(jsonflags.Expand) {
|
||||
if e.Flags.Get(jsonflags.SpaceAfterColon) {
|
||||
dst = append(dst, ' ')
|
||||
}
|
||||
|
||||
// Append object value.
|
||||
n += jsonwire.ConsumeWhitespace(src[n:])
|
||||
if uint(len(src)) <= uint(n) {
|
||||
return dst, n, io.ErrUnexpectedEOF
|
||||
return dst, n, wrapWithObjectName(io.ErrUnexpectedEOF, quotedName)
|
||||
}
|
||||
dst, m, err = e.reformatValue(dst, src[n:], depth)
|
||||
if err != nil {
|
||||
return dst, n + m, err
|
||||
return dst, n + m, wrapWithObjectName(err, quotedName)
|
||||
}
|
||||
n += m
|
||||
|
||||
@@ -740,17 +800,20 @@ func (e *encoderState) reformatObject(dst []byte, src Value, depth int) ([]byte,
|
||||
switch src[n] {
|
||||
case ',':
|
||||
dst = append(dst, ',')
|
||||
if e.Flags.Get(jsonflags.SpaceAfterComma) {
|
||||
dst = append(dst, ' ')
|
||||
}
|
||||
n += len(",")
|
||||
continue
|
||||
case '}':
|
||||
if e.Flags.Get(jsonflags.Expand) {
|
||||
if e.Flags.Get(jsonflags.Multiline) {
|
||||
dst = e.AppendIndent(dst, depth-1)
|
||||
}
|
||||
dst = append(dst, '}')
|
||||
n += len("}")
|
||||
return dst, n, nil
|
||||
default:
|
||||
return dst, n, newInvalidCharacterError(src[n:], "after object value (expecting ',' or '}')")
|
||||
return dst, n, jsonwire.NewInvalidCharacterError(src[n:], "after object value (expecting ',' or '}')")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -779,11 +842,12 @@ func (e *encoderState) reformatArray(dst []byte, src Value, depth int) ([]byte,
|
||||
return dst, n, nil
|
||||
}
|
||||
|
||||
var idx int64
|
||||
var err error
|
||||
depth++
|
||||
for {
|
||||
// Append optional newline and indentation.
|
||||
if e.Flags.Get(jsonflags.Expand) {
|
||||
if e.Flags.Get(jsonflags.Multiline) {
|
||||
dst = e.AppendIndent(dst, depth)
|
||||
}
|
||||
|
||||
@@ -795,7 +859,7 @@ func (e *encoderState) reformatArray(dst []byte, src Value, depth int) ([]byte,
|
||||
var m int
|
||||
dst, m, err = e.reformatValue(dst, src[n:], depth)
|
||||
if err != nil {
|
||||
return dst, n + m, err
|
||||
return dst, n + m, wrapWithArrayIndex(err, idx)
|
||||
}
|
||||
n += m
|
||||
|
||||
@@ -807,17 +871,21 @@ func (e *encoderState) reformatArray(dst []byte, src Value, depth int) ([]byte,
|
||||
switch src[n] {
|
||||
case ',':
|
||||
dst = append(dst, ',')
|
||||
if e.Flags.Get(jsonflags.SpaceAfterComma) {
|
||||
dst = append(dst, ' ')
|
||||
}
|
||||
n += len(",")
|
||||
idx++
|
||||
continue
|
||||
case ']':
|
||||
if e.Flags.Get(jsonflags.Expand) {
|
||||
if e.Flags.Get(jsonflags.Multiline) {
|
||||
dst = e.AppendIndent(dst, depth-1)
|
||||
}
|
||||
dst = append(dst, ']')
|
||||
n += len("]")
|
||||
return dst, n, nil
|
||||
default:
|
||||
return dst, n, newInvalidCharacterError(src[n:], "after array value (expecting ',' or ']')")
|
||||
return dst, n, jsonwire.NewInvalidCharacterError(src[n:], "after array value (expecting ',' or ']')")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -859,8 +927,8 @@ func (e *Encoder) UnusedBuffer() []byte {
|
||||
|
||||
// StackDepth returns the depth of the state machine for written JSON data.
|
||||
// Each level on the stack represents a nested JSON object or array.
|
||||
// It is incremented whenever an [ObjectStart] or [ArrayStart] token is encountered
|
||||
// and decremented whenever an [ObjectEnd] or [ArrayEnd] token is encountered.
|
||||
// It is incremented whenever an [BeginObject] or [BeginArray] token is encountered
|
||||
// and decremented whenever an [EndObject] or [EndArray] token is encountered.
|
||||
// The depth is zero-indexed, where zero represents the top-level JSON value.
|
||||
func (e *Encoder) StackDepth() int {
|
||||
// NOTE: Keep in sync with Decoder.StackDepth.
|
||||
@@ -879,7 +947,7 @@ func (e *Encoder) StackDepth() int {
|
||||
// Each name and value in a JSON object is counted separately,
|
||||
// so the effective number of members would be half the length.
|
||||
// A complete JSON object must have an even length.
|
||||
func (e *Encoder) StackIndex(i int) (Kind, int) {
|
||||
func (e *Encoder) StackIndex(i int) (Kind, int64) {
|
||||
// NOTE: Keep in sync with Decoder.StackIndex.
|
||||
switch s := e.s.Tokens.index(i); {
|
||||
case i > 0 && s.isObject():
|
||||
@@ -892,9 +960,11 @@ func (e *Encoder) StackIndex(i int) (Kind, int) {
|
||||
}
|
||||
|
||||
// StackPointer returns a JSON Pointer (RFC 6901) to the most recently written value.
|
||||
// Object names are only present if [AllowDuplicateNames] is false, otherwise
|
||||
// object members are represented using their index within the object.
|
||||
func (e *Encoder) StackPointer() string {
|
||||
e.s.Names.copyQuotedBuffer(e.s.Buf)
|
||||
return string(e.s.appendStackPointer(nil))
|
||||
func (e *Encoder) StackPointer() Pointer {
|
||||
return Pointer(e.s.AppendStackPointer(nil, -1))
|
||||
}
|
||||
|
||||
func (e *encoderState) AppendStackPointer(b []byte, where int) []byte {
|
||||
e.Names.copyQuotedBuffer(e.Buf)
|
||||
return e.state.appendStackPointer(b, where)
|
||||
}
|
||||
|
||||
152
vendor/github.com/go-json-experiment/json/jsontext/errors.go
generated
vendored
152
vendor/github.com/go-json-experiment/json/jsontext/errors.go
generated
vendored
@@ -5,6 +5,10 @@
|
||||
package jsontext
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"strconv"
|
||||
|
||||
"github.com/go-json-experiment/json/internal/jsonwire"
|
||||
)
|
||||
|
||||
@@ -32,29 +36,145 @@ type SyntacticError struct {
|
||||
|
||||
// ByteOffset indicates that an error occurred after this byte offset.
|
||||
ByteOffset int64
|
||||
str string
|
||||
// JSONPointer indicates that an error occurred within this JSON value
|
||||
// as indicated using the JSON Pointer notation (see RFC 6901).
|
||||
JSONPointer Pointer
|
||||
|
||||
// Err is the underlying error.
|
||||
Err error
|
||||
}
|
||||
|
||||
// wrapSyntacticError wraps an error and annotates it with a precise location
|
||||
// using the provided [encoderState] or [decoderState].
|
||||
// If err is an [ioError] or [io.EOF], then it is not wrapped.
|
||||
//
|
||||
// It takes a relative offset pos that can be resolved into
|
||||
// an absolute offset using state.offsetAt.
|
||||
//
|
||||
// It takes a where that specify how the JSON pointer is derived.
|
||||
// If the underlying error is a [pointerSuffixError],
|
||||
// then the suffix is appended to the derived pointer.
|
||||
func wrapSyntacticError(state interface {
|
||||
offsetAt(pos int) int64
|
||||
AppendStackPointer(b []byte, where int) []byte
|
||||
}, err error, pos, where int) error {
|
||||
if _, ok := err.(*ioError); err == io.EOF || ok {
|
||||
return err
|
||||
}
|
||||
offset := state.offsetAt(pos)
|
||||
ptr := state.AppendStackPointer(nil, where)
|
||||
if serr, ok := err.(*pointerSuffixError); ok {
|
||||
ptr = serr.appendPointer(ptr)
|
||||
err = serr.error
|
||||
}
|
||||
if d, ok := state.(*decoderState); ok && err == errMismatchDelim {
|
||||
where := "at start of value"
|
||||
if len(d.Tokens.Stack) > 0 && d.Tokens.Last.Length() > 0 {
|
||||
switch {
|
||||
case d.Tokens.Last.isArray():
|
||||
where = "after array element (expecting ',' or ']')"
|
||||
ptr = []byte(Pointer(ptr).Parent()) // problem is with parent array
|
||||
case d.Tokens.Last.isObject():
|
||||
where = "after object value (expecting ',' or '}')"
|
||||
ptr = []byte(Pointer(ptr).Parent()) // problem is with parent object
|
||||
}
|
||||
}
|
||||
err = jsonwire.NewInvalidCharacterError(d.buf[pos:], where)
|
||||
}
|
||||
return &SyntacticError{ByteOffset: offset, JSONPointer: Pointer(ptr), Err: err}
|
||||
}
|
||||
|
||||
func (e *SyntacticError) Error() string {
|
||||
return errorPrefix + e.str
|
||||
}
|
||||
func (e *SyntacticError) withOffset(pos int64) error {
|
||||
return &SyntacticError{ByteOffset: pos, str: e.str}
|
||||
pointer := e.JSONPointer
|
||||
offset := e.ByteOffset
|
||||
b := []byte(errorPrefix)
|
||||
if e.Err != nil {
|
||||
b = append(b, e.Err.Error()...)
|
||||
if e.Err == ErrDuplicateName {
|
||||
b = strconv.AppendQuote(append(b, ' '), pointer.LastToken())
|
||||
pointer = pointer.Parent()
|
||||
offset = 0 // not useful to print offset for duplicate names
|
||||
}
|
||||
} else {
|
||||
b = append(b, "syntactic error"...)
|
||||
}
|
||||
if pointer != "" {
|
||||
b = strconv.AppendQuote(append(b, " within "...), jsonwire.TruncatePointer(string(pointer), 100))
|
||||
}
|
||||
if offset > 0 {
|
||||
b = strconv.AppendInt(append(b, " after offset "...), offset, 10)
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
|
||||
func newDuplicateNameError[Bytes ~[]byte | ~string](quoted Bytes) *SyntacticError {
|
||||
return &SyntacticError{str: "duplicate name " + string(quoted) + " in object"}
|
||||
func (e *SyntacticError) Unwrap() error {
|
||||
return e.Err
|
||||
}
|
||||
|
||||
func newInvalidCharacterError[Bytes ~[]byte | ~string](prefix Bytes, where string) *SyntacticError {
|
||||
what := jsonwire.QuoteRune(prefix)
|
||||
return &SyntacticError{str: "invalid character " + what + " " + where}
|
||||
// pointerSuffixError represents a JSON pointer suffix to be appended
|
||||
// to [SyntacticError.JSONPointer]. It is an internal error type
|
||||
// used within this package and does not appear in the public API.
|
||||
//
|
||||
// This type is primarily used to annotate errors in Encoder.WriteValue
|
||||
// and Decoder.ReadValue with precise positions.
|
||||
// At the time WriteValue or ReadValue is called, a JSON pointer to the
|
||||
// upcoming value can be constructed using the Encoder/Decoder state.
|
||||
// However, tracking pointers within values during normal operation
|
||||
// would incur a performance penalty in the error-free case.
|
||||
//
|
||||
// To provide precise error locations without this overhead,
|
||||
// the error is wrapped with object names or array indices
|
||||
// as the call stack is popped when an error occurs.
|
||||
// Since this happens in reverse order, pointerSuffixError holds
|
||||
// the pointer in reverse and is only later reversed when appending to
|
||||
// the pointer prefix.
|
||||
//
|
||||
// For example, if the encoder is at "/alpha/bravo/charlie"
|
||||
// and an error occurs in WriteValue at "/xray/yankee/zulu", then
|
||||
// the final pointer should be "/alpha/bravo/charlie/xray/yankee/zulu".
|
||||
//
|
||||
// As pointerSuffixError is populated during the error return path,
|
||||
// it first contains "/zulu", then "/zulu/yankee",
|
||||
// and finally "/zulu/yankee/xray".
|
||||
// These tokens are reversed and concatenated to "/alpha/bravo/charlie"
|
||||
// to form the full pointer.
|
||||
type pointerSuffixError struct {
|
||||
error
|
||||
|
||||
// reversePointer is a JSON pointer, but with each token in reverse order.
|
||||
reversePointer []byte
|
||||
}
|
||||
|
||||
// TODO: Error types between "json", "jsontext", and "jsonwire" is a mess.
|
||||
// Clean this up.
|
||||
func init() {
|
||||
// Inject behavior in "jsonwire" so that it can produce SyntacticError types.
|
||||
jsonwire.NewError = func(s string) error { return &SyntacticError{str: s} }
|
||||
jsonwire.ErrInvalidUTF8 = &SyntacticError{str: jsonwire.ErrInvalidUTF8.Error()}
|
||||
// wrapWithObjectName wraps err with a JSON object name access,
|
||||
// which must be a valid quoted JSON string.
|
||||
func wrapWithObjectName(err error, quotedName []byte) error {
|
||||
serr, _ := err.(*pointerSuffixError)
|
||||
if serr == nil {
|
||||
serr = &pointerSuffixError{error: err}
|
||||
}
|
||||
name := jsonwire.UnquoteMayCopy(quotedName, false)
|
||||
serr.reversePointer = appendEscapePointerName(append(serr.reversePointer, '/'), name)
|
||||
return serr
|
||||
}
|
||||
|
||||
// wrapWithArrayIndex wraps err with a JSON array index access.
|
||||
func wrapWithArrayIndex(err error, index int64) error {
|
||||
serr, _ := err.(*pointerSuffixError)
|
||||
if serr == nil {
|
||||
serr = &pointerSuffixError{error: err}
|
||||
}
|
||||
serr.reversePointer = strconv.AppendUint(append(serr.reversePointer, '/'), uint64(index), 10)
|
||||
return serr
|
||||
}
|
||||
|
||||
// appendPointer appends the path encoded in e to the end of pointer.
|
||||
func (e *pointerSuffixError) appendPointer(pointer []byte) []byte {
|
||||
// Copy each token in reversePointer to the end of pointer in reverse order.
|
||||
// Double reversal means that the appended suffix is now in forward order.
|
||||
bi, bo := e.reversePointer, pointer
|
||||
for len(bi) > 0 {
|
||||
i := bytes.LastIndexByte(bi, '/')
|
||||
bi, bo = bi[:i], append(bo, bi[i:]...)
|
||||
}
|
||||
return bo
|
||||
}
|
||||
|
||||
14
vendor/github.com/go-json-experiment/json/jsontext/export.go
generated
vendored
14
vendor/github.com/go-json-experiment/json/jsontext/export.go
generated
vendored
@@ -69,15 +69,7 @@ func (export) PutStreamingDecoder(d *Decoder) {
|
||||
putStreamingDecoder(d)
|
||||
}
|
||||
|
||||
func (export) NewDuplicateNameError(quoted []byte, pos int64) error {
|
||||
return newDuplicateNameError(quoted).withOffset(pos)
|
||||
}
|
||||
func (export) NewInvalidCharacterError(prefix, where string, pos int64) error {
|
||||
return newInvalidCharacterError(prefix, where).withOffset(pos)
|
||||
}
|
||||
func (export) NewMissingNameError(pos int64) error {
|
||||
return errMissingName.withOffset(pos)
|
||||
}
|
||||
func (export) NewInvalidUTF8Error(pos int64) error {
|
||||
return errInvalidUTF8.withOffset(pos)
|
||||
func (export) IsIOError(err error) bool {
|
||||
_, ok := err.(*ioError)
|
||||
return ok
|
||||
}
|
||||
|
||||
149
vendor/github.com/go-json-experiment/json/jsontext/options.go
generated
vendored
149
vendor/github.com/go-json-experiment/json/jsontext/options.go
generated
vendored
@@ -17,6 +17,25 @@ import (
|
||||
// Each function takes in a variadic list of options, where properties
|
||||
// set in latter options override the value of previously set properties.
|
||||
//
|
||||
// There is a single Options type, which is used with both encoding and decoding.
|
||||
// Some options affect both operations, while others only affect one operation:
|
||||
//
|
||||
// - [AllowDuplicateNames] affects encoding and decoding
|
||||
// - [AllowInvalidUTF8] affects encoding and decoding
|
||||
// - [EscapeForHTML] affects encoding only
|
||||
// - [EscapeForJS] affects encoding only
|
||||
// - [PreserveRawStrings] affects encoding only
|
||||
// - [CanonicalizeRawInts] affects encoding only
|
||||
// - [CanonicalizeRawFloats] affects encoding only
|
||||
// - [ReorderRawObjects] affects encoding only
|
||||
// - [SpaceAfterColon] affects encoding only
|
||||
// - [SpaceAfterComma] affects encoding only
|
||||
// - [Multiline] affects encoding only
|
||||
// - [WithIndent] affects encoding only
|
||||
// - [WithIndentPrefix] affects encoding only
|
||||
//
|
||||
// Options that do not affect a particular operation are ignored.
|
||||
//
|
||||
// The Options type is identical to [encoding/json.Options] and
|
||||
// [encoding/json/v2.Options]. Options from the other packages may
|
||||
// be passed to functionality in this package, but are ignored.
|
||||
@@ -78,20 +97,122 @@ func EscapeForJS(v bool) Options {
|
||||
}
|
||||
}
|
||||
|
||||
// Expand specifies that the JSON output should be expanded,
|
||||
// where every JSON object member or JSON array element
|
||||
// appears on a new, indented line according to the nesting depth.
|
||||
// If an indent is not already specified, then it defaults to using "\t".
|
||||
//
|
||||
// If set to false, then the output is compact,
|
||||
// where no whitespace is emitted between JSON values.
|
||||
// PreserveRawStrings specifies that when encoding a raw JSON string in a
|
||||
// [Token] or [Value], pre-escaped sequences
|
||||
// in a JSON string are preserved to the output.
|
||||
// However, raw strings still respect [EscapeForHTML] and [EscapeForJS]
|
||||
// such that the relevant characters are escaped.
|
||||
// If [AllowInvalidUTF8] is enabled, bytes of invalid UTF-8
|
||||
// are preserved to the output.
|
||||
//
|
||||
// This only affects encoding and is ignored when decoding.
|
||||
func Expand(v bool) Options {
|
||||
func PreserveRawStrings(v bool) Options {
|
||||
if v {
|
||||
return jsonflags.Expand | 1
|
||||
return jsonflags.PreserveRawStrings | 1
|
||||
} else {
|
||||
return jsonflags.Expand | 0
|
||||
return jsonflags.PreserveRawStrings | 0
|
||||
}
|
||||
}
|
||||
|
||||
// CanonicalizeRawInts specifies that when encoding a raw JSON
|
||||
// integer number (i.e., a number without a fraction and exponent) in a
|
||||
// [Token] or [Value], the number is canonicalized
|
||||
// according to RFC 8785, section 3.2.2.3. As a special case,
|
||||
// the number -0 is canonicalized as 0.
|
||||
//
|
||||
// JSON numbers are treated as IEEE 754 double precision numbers.
|
||||
// Any numbers with precision beyond what is representable by that form
|
||||
// will lose their precision when canonicalized. For example,
|
||||
// integer values beyond ±2⁵³ will lose their precision.
|
||||
// For example, 1234567890123456789 is formatted as 1234567890123456800.
|
||||
//
|
||||
// This only affects encoding and is ignored when decoding.
|
||||
func CanonicalizeRawInts(v bool) Options {
|
||||
if v {
|
||||
return jsonflags.CanonicalizeRawInts | 1
|
||||
} else {
|
||||
return jsonflags.CanonicalizeRawInts | 0
|
||||
}
|
||||
}
|
||||
|
||||
// CanonicalizeRawFloats specifies that when encoding a raw JSON
|
||||
// floating-point number (i.e., a number with a fraction or exponent) in a
|
||||
// [Token] or [Value], the number is canonicalized
|
||||
// according to RFC 8785, section 3.2.2.3. As a special case,
|
||||
// the number -0 is canonicalized as 0.
|
||||
//
|
||||
// JSON numbers are treated as IEEE 754 double precision numbers.
|
||||
// It is safe to canonicalize a serialized single precision number and
|
||||
// parse it back as a single precision number and expect the same value.
|
||||
// If a number exceeds ±1.7976931348623157e+308, which is the maximum
|
||||
// finite number, then it saturated at that value and formatted as such.
|
||||
//
|
||||
// This only affects encoding and is ignored when decoding.
|
||||
func CanonicalizeRawFloats(v bool) Options {
|
||||
if v {
|
||||
return jsonflags.CanonicalizeRawFloats | 1
|
||||
} else {
|
||||
return jsonflags.CanonicalizeRawFloats | 0
|
||||
}
|
||||
}
|
||||
|
||||
// ReorderRawObjects specifies that when encoding a raw JSON object in a
|
||||
// [Value], the object members are reordered according to
|
||||
// RFC 8785, section 3.2.3.
|
||||
//
|
||||
// This only affects encoding and is ignored when decoding.
|
||||
func ReorderRawObjects(v bool) Options {
|
||||
if v {
|
||||
return jsonflags.ReorderRawObjects | 1
|
||||
} else {
|
||||
return jsonflags.ReorderRawObjects | 0
|
||||
}
|
||||
}
|
||||
|
||||
// SpaceAfterColon specifies that the JSON output should emit a space character
|
||||
// after each colon separator following a JSON object name.
|
||||
// If false, then no space character appears after the colon separator.
|
||||
//
|
||||
// This only affects encoding and is ignored when decoding.
|
||||
func SpaceAfterColon(v bool) Options {
|
||||
if v {
|
||||
return jsonflags.SpaceAfterColon | 1
|
||||
} else {
|
||||
return jsonflags.SpaceAfterColon | 0
|
||||
}
|
||||
}
|
||||
|
||||
// SpaceAfterComma specifies that the JSON output should emit a space character
|
||||
// after each comma separator following a JSON object value or array element.
|
||||
// If false, then no space character appears after the comma separator.
|
||||
//
|
||||
// This only affects encoding and is ignored when decoding.
|
||||
func SpaceAfterComma(v bool) Options {
|
||||
if v {
|
||||
return jsonflags.SpaceAfterComma | 1
|
||||
} else {
|
||||
return jsonflags.SpaceAfterComma | 0
|
||||
}
|
||||
}
|
||||
|
||||
// Multiline specifies that the JSON output should expand to multiple lines,
|
||||
// where every JSON object member or JSON array element appears on
|
||||
// a new, indented line according to the nesting depth.
|
||||
//
|
||||
// If [SpaceAfterColon] is not specified, then the default is true.
|
||||
// If [SpaceAfterComma] is not specified, then the default is false.
|
||||
// If [WithIndent] is not specified, then the default is "\t".
|
||||
//
|
||||
// If set to false, then the output is a single-line,
|
||||
// where the only whitespace emitted is determined by the current
|
||||
// values of [SpaceAfterColon] and [SpaceAfterComma].
|
||||
//
|
||||
// This only affects encoding and is ignored when decoding.
|
||||
func Multiline(v bool) Options {
|
||||
if v {
|
||||
return jsonflags.Multiline | 1
|
||||
} else {
|
||||
return jsonflags.Multiline | 0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,10 +223,10 @@ func Expand(v bool) Options {
|
||||
// The indent must only be composed of space or tab characters.
|
||||
//
|
||||
// If the intent to emit indented output without a preference for
|
||||
// the particular indent string, then use [Expand] instead.
|
||||
// the particular indent string, then use [Multiline] instead.
|
||||
//
|
||||
// This only affects encoding and is ignored when decoding.
|
||||
// Use of this option implies [Expand] being set to true.
|
||||
// Use of this option implies [Multiline] being set to true.
|
||||
func WithIndent(indent string) Options {
|
||||
// Fast-path: Return a constant for common indents, which avoids allocating.
|
||||
// These are derived from analyzing the Go module proxy on 2023-07-01.
|
||||
@@ -138,7 +259,7 @@ func WithIndent(indent string) Options {
|
||||
// The prefix must only be composed of space or tab characters.
|
||||
//
|
||||
// This only affects encoding and is ignored when decoding.
|
||||
// Use of this option implies [Expand] being set to true.
|
||||
// Use of this option implies [Multiline] being set to true.
|
||||
func WithIndentPrefix(prefix string) Options {
|
||||
if s := strings.Trim(prefix, " \t"); len(s) > 0 {
|
||||
panic("json: invalid character " + jsonwire.QuoteRune(s) + " in indent prefix")
|
||||
@@ -160,6 +281,7 @@ func WithIndentPrefix(prefix string) Options {
|
||||
//
|
||||
// A non-positive limit is equivalent to no limit at all.
|
||||
// If unspecified, the default limit is no limit at all.
|
||||
// This affects either encoding or decoding.
|
||||
func WithByteLimit(n int64) Options {
|
||||
return jsonopts.ByteLimit(max(n, 0))
|
||||
}
|
||||
@@ -172,6 +294,7 @@ func WithByteLimit(n int64) Options {
|
||||
//
|
||||
// A non-positive limit is equivalent to no limit at all.
|
||||
// If unspecified, the default limit is 10000.
|
||||
// This affects either encoding or decoding.
|
||||
func WithDepthLimit(n int) Options {
|
||||
return jsonopts.DepthLimit(max(n, 0))
|
||||
}
|
||||
|
||||
16
vendor/github.com/go-json-experiment/json/jsontext/quote.go
generated
vendored
16
vendor/github.com/go-json-experiment/json/jsontext/quote.go
generated
vendored
@@ -9,15 +9,18 @@ import (
|
||||
"github.com/go-json-experiment/json/internal/jsonwire"
|
||||
)
|
||||
|
||||
var errInvalidUTF8 = &SyntacticError{str: "invalid UTF-8 within string"}
|
||||
|
||||
// AppendQuote appends a double-quoted JSON string literal representing src
|
||||
// to dst and returns the extended buffer.
|
||||
// It uses the minimal string representation per RFC 8785, section 3.2.2.2.
|
||||
// Invalid UTF-8 bytes are replaced with the Unicode replacement character
|
||||
// and an error is returned at the end indicating the presence of invalid UTF-8.
|
||||
// The dst must not overlap with the src.
|
||||
func AppendQuote[Bytes ~[]byte | ~string](dst []byte, src Bytes) ([]byte, error) {
|
||||
return jsonwire.AppendQuote(dst, src, &jsonflags.Flags{})
|
||||
dst, err := jsonwire.AppendQuote(dst, src, &jsonflags.Flags{})
|
||||
if err != nil {
|
||||
err = &SyntacticError{Err: err}
|
||||
}
|
||||
return dst, err
|
||||
}
|
||||
|
||||
// AppendUnquote appends the decoded interpretation of src as a
|
||||
@@ -26,6 +29,11 @@ func AppendQuote[Bytes ~[]byte | ~string](dst []byte, src Bytes) ([]byte, error)
|
||||
// Invalid UTF-8 bytes are replaced with the Unicode replacement character
|
||||
// and an error is returned at the end indicating the presence of invalid UTF-8.
|
||||
// Any trailing bytes after the JSON string literal results in an error.
|
||||
// The dst must not overlap with the src.
|
||||
func AppendUnquote[Bytes ~[]byte | ~string](dst []byte, src Bytes) ([]byte, error) {
|
||||
return jsonwire.AppendUnquote(dst, src)
|
||||
dst, err := jsonwire.AppendUnquote(dst, src)
|
||||
if err != nil {
|
||||
err = &SyntacticError{Err: err}
|
||||
}
|
||||
return dst, err
|
||||
}
|
||||
|
||||
225
vendor/github.com/go-json-experiment/json/jsontext/state.go
generated
vendored
225
vendor/github.com/go-json-experiment/json/jsontext/state.go
generated
vendored
@@ -5,21 +5,45 @@
|
||||
package jsontext
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"iter"
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/go-json-experiment/json/internal/jsonwire"
|
||||
)
|
||||
|
||||
var (
|
||||
errMissingName = &SyntacticError{str: "missing string for object name"}
|
||||
errMissingColon = &SyntacticError{str: "missing character ':' after object name"}
|
||||
errMissingValue = &SyntacticError{str: "missing value after object name"}
|
||||
errMissingComma = &SyntacticError{str: "missing character ',' after object or array value"}
|
||||
errMismatchDelim = &SyntacticError{str: "mismatching structural token for object or array"}
|
||||
errMaxDepth = &SyntacticError{str: "exceeded max depth"}
|
||||
// ErrDuplicateName indicates that a JSON token could not be
|
||||
// encoded or decoded because it results in a duplicate JSON object name.
|
||||
// This error is directly wrapped within a [SyntacticError] when produced.
|
||||
//
|
||||
// The name of a duplicate JSON object member can be extracted as:
|
||||
//
|
||||
// err := ...
|
||||
// var serr jsontext.SyntacticError
|
||||
// if errors.As(err, &serr) && serr.Err == jsontext.ErrDuplicateName {
|
||||
// ptr := serr.JSONPointer // JSON pointer to duplicate name
|
||||
// name := ptr.LastToken() // duplicate name itself
|
||||
// ...
|
||||
// }
|
||||
//
|
||||
// This error is only returned if [AllowDuplicateNames] is false.
|
||||
var ErrDuplicateName = errors.New("duplicate object member name")
|
||||
|
||||
errInvalidNamespace = &SyntacticError{str: "object namespace is in an invalid state"}
|
||||
// ErrNonStringName indicates that a JSON token could not be
|
||||
// encoded or decoded because it is not a string,
|
||||
// as required for JSON object names according to RFC 8259, section 4.
|
||||
// This error is directly wrapped within a [SyntacticError] when produced.
|
||||
var ErrNonStringName = errors.New("object member name must be a string")
|
||||
|
||||
var (
|
||||
errMissingValue = errors.New("missing value after object name")
|
||||
errMismatchDelim = errors.New("mismatching structural token for object or array")
|
||||
errMaxDepth = errors.New("exceeded max depth")
|
||||
|
||||
errInvalidNamespace = errors.New("object namespace is in an invalid state")
|
||||
)
|
||||
|
||||
// Per RFC 8259, section 9, implementations may enforce a maximum depth.
|
||||
@@ -31,7 +55,6 @@ type state struct {
|
||||
Tokens stateMachine
|
||||
|
||||
// Names is a stack of object names.
|
||||
// Not used if AllowDuplicateNames is true.
|
||||
Names objectNameStack
|
||||
|
||||
// Namespaces is a stack of object namespaces.
|
||||
@@ -42,48 +65,151 @@ type state struct {
|
||||
Namespaces objectNamespaceStack
|
||||
}
|
||||
|
||||
// needObjectValue reports whether the next token should be an object value.
|
||||
// This method is used by [wrapSyntacticError].
|
||||
func (s *state) needObjectValue() bool {
|
||||
return s.Tokens.Last.needObjectValue()
|
||||
}
|
||||
|
||||
func (s *state) reset() {
|
||||
s.Tokens.reset()
|
||||
s.Names.reset()
|
||||
s.Namespaces.reset()
|
||||
}
|
||||
|
||||
// Pointer is a JSON Pointer (RFC 6901) that references a particular JSON value
|
||||
// relative to the root of the top-level JSON value.
|
||||
//
|
||||
// A Pointer is a slash-separated list of tokens, where each token is
|
||||
// either a JSON object name or an index to a JSON array element
|
||||
// encoded as a base-10 integer value.
|
||||
// It is impossible to distinguish between an array index and an object name
|
||||
// (that happens to be an base-10 encoded integer) without also knowing
|
||||
// the structure of the top-level JSON value that the pointer refers to.
|
||||
//
|
||||
// There is exactly one representation of a pointer to a particular value,
|
||||
// so comparability of Pointer values is equivalent to checking whether
|
||||
// they both point to the exact same value.
|
||||
type Pointer string
|
||||
|
||||
// IsValid reports whether p is a valid JSON Pointer according to RFC 6901.
|
||||
// Note that the concatenation of two valid pointers produces a valid pointer.
|
||||
func (p Pointer) IsValid() bool {
|
||||
for i, r := range p {
|
||||
switch {
|
||||
case r == '~' && (i+1 == len(p) || (p[i+1] != '0' && p[i+1] != '1')):
|
||||
return false // invalid escape
|
||||
case r == '\ufffd' && !strings.HasPrefix(string(p[i:]), "\ufffd"):
|
||||
return false // invalid UTF-8
|
||||
}
|
||||
}
|
||||
return len(p) == 0 || p[0] == '/'
|
||||
}
|
||||
|
||||
// Contains reports whether the JSON value that p points to
|
||||
// is equal to or contains the JSON value that pc points to.
|
||||
func (p Pointer) Contains(pc Pointer) bool {
|
||||
// Invariant: len(p) <= len(pc) if p.Contains(pc)
|
||||
suffix, ok := strings.CutPrefix(string(pc), string(p))
|
||||
return ok && (suffix == "" || suffix[0] == '/')
|
||||
}
|
||||
|
||||
// Parent strips off the last token and returns the remaining pointer.
|
||||
// The parent of an empty p is an empty string.
|
||||
func (p Pointer) Parent() Pointer {
|
||||
return p[:max(strings.LastIndexByte(string(p), '/'), 0)]
|
||||
}
|
||||
|
||||
// LastToken returns the last token in the pointer.
|
||||
// The last token of an empty p is an empty string.
|
||||
func (p Pointer) LastToken() string {
|
||||
last := p[max(strings.LastIndexByte(string(p), '/'), 0):]
|
||||
return unescapePointerToken(strings.TrimPrefix(string(last), "/"))
|
||||
}
|
||||
|
||||
// AppendToken appends a token to the end of p and returns the full pointer.
|
||||
func (p Pointer) AppendToken(tok string) Pointer {
|
||||
return Pointer(appendEscapePointerName([]byte(p+"/"), tok))
|
||||
}
|
||||
|
||||
// TODO: Add Pointer.AppendTokens,
|
||||
// but should this take in a ...string or an iter.Seq[string]?
|
||||
|
||||
// Tokens returns an iterator over the reference tokens in the JSON pointer,
|
||||
// starting from the first token until the last token (unless stopped early).
|
||||
func (p Pointer) Tokens() iter.Seq[string] {
|
||||
return func(yield func(string) bool) {
|
||||
for len(p) > 0 {
|
||||
p = Pointer(strings.TrimPrefix(string(p), "/"))
|
||||
i := min(uint(strings.IndexByte(string(p), '/')), uint(len(p)))
|
||||
if !yield(unescapePointerToken(string(p)[:i])) {
|
||||
return
|
||||
}
|
||||
p = p[i:]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func unescapePointerToken(token string) string {
|
||||
if strings.Contains(token, "~") {
|
||||
// Per RFC 6901, section 3, unescape '~' and '/' characters.
|
||||
token = strings.ReplaceAll(token, "~1", "/")
|
||||
token = strings.ReplaceAll(token, "~0", "~")
|
||||
}
|
||||
return token
|
||||
}
|
||||
|
||||
// appendStackPointer appends a JSON Pointer (RFC 6901) to the current value.
|
||||
// The returned pointer is only accurate if s.names is populated,
|
||||
// otherwise it uses the numeric index as the object member name.
|
||||
//
|
||||
// - If where is -1, then it points to the previously processed token.
|
||||
//
|
||||
// - If where is 0, then it points to the parent JSON object or array,
|
||||
// or an object member if in-between an object member key and value.
|
||||
// This is useful when the position is ambiguous whether
|
||||
// we are interested in the previous or next token, or
|
||||
// when we are uncertain whether the next token
|
||||
// continues or terminates the current object or array.
|
||||
//
|
||||
// - If where is +1, then it points to the next expected value,
|
||||
// assuming that it continues the current JSON object or array.
|
||||
// As a special case, if the next token is a JSON object name,
|
||||
// then it points to the parent JSON object.
|
||||
//
|
||||
// Invariant: Must call s.names.copyQuotedBuffer beforehand.
|
||||
func (s state) appendStackPointer(b []byte) []byte {
|
||||
func (s state) appendStackPointer(b []byte, where int) []byte {
|
||||
var objectDepth int
|
||||
for i := 1; i < s.Tokens.Depth(); i++ {
|
||||
e := s.Tokens.index(i)
|
||||
if e.Length() == 0 {
|
||||
break // empty object or array
|
||||
arrayDelta := -1 // by default point to previous array element
|
||||
if isLast := i == s.Tokens.Depth()-1; isLast {
|
||||
switch {
|
||||
case where < 0 && e.Length() == 0 || where == 0 && !e.needObjectValue() || where > 0 && e.NeedObjectName():
|
||||
return b
|
||||
case where > 0 && e.isArray():
|
||||
arrayDelta = 0 // point to next array element
|
||||
}
|
||||
}
|
||||
b = append(b, '/')
|
||||
switch {
|
||||
case e.isObject():
|
||||
if objectDepth < s.Names.length() {
|
||||
for _, c := range s.Names.getUnquoted(objectDepth) {
|
||||
// Per RFC 6901, section 3, escape '~' and '/' characters.
|
||||
switch c {
|
||||
case '~':
|
||||
b = append(b, "~0"...)
|
||||
case '/':
|
||||
b = append(b, "~1"...)
|
||||
default:
|
||||
b = append(b, c)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Since the names stack is unpopulated, the name is unknown.
|
||||
// As a best-effort replacement, use the numeric member index.
|
||||
// While inaccurate, it produces a syntactically valid pointer.
|
||||
b = strconv.AppendUint(b, uint64((e.Length()-1)/2), 10)
|
||||
}
|
||||
b = appendEscapePointerName(append(b, '/'), s.Names.getUnquoted(objectDepth))
|
||||
objectDepth++
|
||||
case e.isArray():
|
||||
b = strconv.AppendUint(b, uint64(e.Length()-1), 10)
|
||||
b = strconv.AppendUint(append(b, '/'), uint64(e.Length()+int64(arrayDelta)), 10)
|
||||
}
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func appendEscapePointerName[Bytes ~[]byte | ~string](b []byte, name Bytes) []byte {
|
||||
for _, r := range string(name) {
|
||||
// Per RFC 6901, section 3, escape '~' and '/' characters.
|
||||
switch r {
|
||||
case '~':
|
||||
b = append(b, "~0"...)
|
||||
case '/':
|
||||
b = append(b, "~1"...)
|
||||
default:
|
||||
b = utf8.AppendRune(b, r)
|
||||
}
|
||||
}
|
||||
return b
|
||||
@@ -133,7 +259,7 @@ func (m *stateMachine) index(i int) *stateEntry {
|
||||
|
||||
// DepthLength reports the current nested depth and
|
||||
// the length of the last JSON object or array.
|
||||
func (m stateMachine) DepthLength() (int, int) {
|
||||
func (m stateMachine) DepthLength() (int, int64) {
|
||||
return m.Depth(), m.Last.Length()
|
||||
}
|
||||
|
||||
@@ -142,7 +268,7 @@ func (m stateMachine) DepthLength() (int, int) {
|
||||
func (m *stateMachine) appendLiteral() error {
|
||||
switch {
|
||||
case m.Last.NeedObjectName():
|
||||
return errMissingName
|
||||
return ErrNonStringName
|
||||
case !m.Last.isValidNamespace():
|
||||
return errInvalidNamespace
|
||||
default:
|
||||
@@ -174,7 +300,7 @@ func (m *stateMachine) appendNumber() error {
|
||||
func (m *stateMachine) pushObject() error {
|
||||
switch {
|
||||
case m.Last.NeedObjectName():
|
||||
return errMissingName
|
||||
return ErrNonStringName
|
||||
case !m.Last.isValidNamespace():
|
||||
return errInvalidNamespace
|
||||
case len(m.Stack) == maxNestingDepth:
|
||||
@@ -209,7 +335,7 @@ func (m *stateMachine) popObject() error {
|
||||
func (m *stateMachine) pushArray() error {
|
||||
switch {
|
||||
case m.Last.NeedObjectName():
|
||||
return errMissingName
|
||||
return ErrNonStringName
|
||||
case !m.Last.isValidNamespace():
|
||||
return errInvalidNamespace
|
||||
case len(m.Stack) == maxNestingDepth:
|
||||
@@ -283,21 +409,6 @@ func (m stateMachine) needDelim(next Kind) (delim byte) {
|
||||
}
|
||||
}
|
||||
|
||||
// checkDelim reports whether the specified delimiter should be there given
|
||||
// the kind of the next token that appears immediately afterwards.
|
||||
func (m stateMachine) checkDelim(delim byte, next Kind) error {
|
||||
switch needDelim := m.needDelim(next); {
|
||||
case needDelim == delim:
|
||||
return nil
|
||||
case needDelim == ':':
|
||||
return errMissingColon
|
||||
case needDelim == ',':
|
||||
return errMissingComma
|
||||
default:
|
||||
return newInvalidCharacterError([]byte{delim}, "before next token")
|
||||
}
|
||||
}
|
||||
|
||||
// InvalidateDisabledNamespaces marks all disabled namespaces as invalid.
|
||||
//
|
||||
// For efficiency, Marshal and Unmarshal may disable namespaces since there are
|
||||
@@ -306,7 +417,7 @@ func (m stateMachine) checkDelim(delim byte, next Kind) error {
|
||||
// Mark the namespaces as invalid so that future method calls on
|
||||
// Encoder or Decoder will return an error.
|
||||
func (m *stateMachine) InvalidateDisabledNamespaces() {
|
||||
for i := 0; i < m.Depth(); i++ {
|
||||
for i := range m.Depth() {
|
||||
e := m.index(i)
|
||||
if !e.isActiveNamespace() {
|
||||
e.invalidateNamespace()
|
||||
@@ -342,8 +453,8 @@ const (
|
||||
|
||||
// Length reports the number of elements in the JSON object or array.
|
||||
// Each name and value in an object entry is treated as a separate element.
|
||||
func (e stateEntry) Length() int {
|
||||
return int(e & stateCountMask)
|
||||
func (e stateEntry) Length() int64 {
|
||||
return int64(e & stateCountMask)
|
||||
}
|
||||
|
||||
// isObject reports whether this is a JSON object.
|
||||
@@ -453,7 +564,7 @@ func (ns *objectNameStack) length() int {
|
||||
return len(ns.offsets)
|
||||
}
|
||||
|
||||
// getUnquoted retrieves the ith unquoted name in the namespace.
|
||||
// getUnquoted retrieves the ith unquoted name in the stack.
|
||||
// It returns an empty string if the last object is empty.
|
||||
//
|
||||
// Invariant: Must call copyQuotedBuffer beforehand.
|
||||
|
||||
56
vendor/github.com/go-json-experiment/json/jsontext/token.go
generated
vendored
56
vendor/github.com/go-json-experiment/json/jsontext/token.go
generated
vendored
@@ -5,6 +5,8 @@
|
||||
package jsontext
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"math"
|
||||
"strconv"
|
||||
|
||||
@@ -20,9 +22,11 @@ const (
|
||||
maxUint64 = math.MaxUint64
|
||||
minUint64 = 0 // for consistency and readability purposes
|
||||
|
||||
invalidTokenPanic = "invalid json.Token; it has been voided by a subsequent json.Decoder call"
|
||||
invalidTokenPanic = "invalid jsontext.Token; it has been voided by a subsequent json.Decoder call"
|
||||
)
|
||||
|
||||
var errInvalidToken = errors.New("invalid jsontext.Token")
|
||||
|
||||
// Token represents a lexical JSON token, which may be one of the following:
|
||||
// - a JSON literal (i.e., null, true, or false)
|
||||
// - a JSON string (e.g., "hello, world!")
|
||||
@@ -90,10 +94,10 @@ var (
|
||||
False Token = rawToken("false")
|
||||
True Token = rawToken("true")
|
||||
|
||||
ObjectStart Token = rawToken("{")
|
||||
ObjectEnd Token = rawToken("}")
|
||||
ArrayStart Token = rawToken("[")
|
||||
ArrayEnd Token = rawToken("]")
|
||||
BeginObject Token = rawToken("{")
|
||||
EndObject Token = rawToken("}")
|
||||
BeginArray Token = rawToken("[")
|
||||
EndArray Token = rawToken("]")
|
||||
|
||||
zeroString Token = rawToken(`""`)
|
||||
zeroNumber Token = rawToken(`0`)
|
||||
@@ -172,22 +176,21 @@ func (t Token) Clone() Token {
|
||||
return False
|
||||
case True.raw:
|
||||
return True
|
||||
case ObjectStart.raw:
|
||||
return ObjectStart
|
||||
case ObjectEnd.raw:
|
||||
return ObjectEnd
|
||||
case ArrayStart.raw:
|
||||
return ArrayStart
|
||||
case ArrayEnd.raw:
|
||||
return ArrayEnd
|
||||
case BeginObject.raw:
|
||||
return BeginObject
|
||||
case EndObject.raw:
|
||||
return EndObject
|
||||
case BeginArray.raw:
|
||||
return BeginArray
|
||||
case EndArray.raw:
|
||||
return EndArray
|
||||
}
|
||||
}
|
||||
|
||||
if uint64(raw.previousOffsetStart()) != t.num {
|
||||
panic(invalidTokenPanic)
|
||||
}
|
||||
// TODO(https://go.dev/issue/45038): Use bytes.Clone.
|
||||
buf := append([]byte(nil), raw.PreviousBuffer()...)
|
||||
buf := bytes.Clone(raw.previousBuffer())
|
||||
return Token{raw: &decodeBuffer{buf: buf, prevStart: 0, prevEnd: len(buf)}}
|
||||
}
|
||||
return t
|
||||
@@ -211,7 +214,7 @@ func (t Token) Bool() bool {
|
||||
func (t Token) appendString(dst []byte, flags *jsonflags.Flags) ([]byte, error) {
|
||||
if raw := t.raw; raw != nil {
|
||||
// Handle raw string value.
|
||||
buf := raw.PreviousBuffer()
|
||||
buf := raw.previousBuffer()
|
||||
if Kind(buf[0]) == '"' {
|
||||
if jsonwire.ConsumeSimpleString(buf) == len(buf) {
|
||||
return append(dst, buf...), nil
|
||||
@@ -245,7 +248,7 @@ func (t Token) string() (string, []byte) {
|
||||
if uint64(raw.previousOffsetStart()) != t.num {
|
||||
panic(invalidTokenPanic)
|
||||
}
|
||||
buf := raw.PreviousBuffer()
|
||||
buf := raw.previousBuffer()
|
||||
if buf[0] == '"' {
|
||||
// TODO: Preserve ValueFlags in Token?
|
||||
isVerbatim := jsonwire.ConsumeSimpleString(buf) == len(buf)
|
||||
@@ -268,20 +271,17 @@ func (t Token) string() (string, []byte) {
|
||||
return strconv.FormatUint(uint64(t.num), 10), nil
|
||||
}
|
||||
}
|
||||
return "<invalid json.Token>", nil
|
||||
return "<invalid jsontext.Token>", nil
|
||||
}
|
||||
|
||||
// appendNumber appends a JSON number to dst and returns it.
|
||||
// It panics if t is not a JSON number.
|
||||
func (t Token) appendNumber(dst []byte, canonicalize bool) ([]byte, error) {
|
||||
func (t Token) appendNumber(dst []byte, flags *jsonflags.Flags) ([]byte, error) {
|
||||
if raw := t.raw; raw != nil {
|
||||
// Handle raw number value.
|
||||
buf := raw.PreviousBuffer()
|
||||
buf := raw.previousBuffer()
|
||||
if Kind(buf[0]).normalize() == '0' {
|
||||
if !canonicalize {
|
||||
return append(dst, buf...), nil
|
||||
}
|
||||
dst, _, err := jsonwire.ReformatNumber(dst, buf, canonicalize)
|
||||
dst, _, err := jsonwire.ReformatNumber(dst, buf, flags)
|
||||
return dst, err
|
||||
}
|
||||
} else if t.num != 0 {
|
||||
@@ -309,7 +309,7 @@ func (t Token) Float() float64 {
|
||||
if uint64(raw.previousOffsetStart()) != t.num {
|
||||
panic(invalidTokenPanic)
|
||||
}
|
||||
buf := raw.PreviousBuffer()
|
||||
buf := raw.previousBuffer()
|
||||
if Kind(buf[0]).normalize() == '0' {
|
||||
fv, _ := jsonwire.ParseFloat(buf, 64)
|
||||
return fv
|
||||
@@ -353,7 +353,7 @@ func (t Token) Int() int64 {
|
||||
panic(invalidTokenPanic)
|
||||
}
|
||||
neg := false
|
||||
buf := raw.PreviousBuffer()
|
||||
buf := raw.previousBuffer()
|
||||
if len(buf) > 0 && buf[0] == '-' {
|
||||
neg, buf = true, buf[1:]
|
||||
}
|
||||
@@ -414,7 +414,7 @@ func (t Token) Uint() uint64 {
|
||||
panic(invalidTokenPanic)
|
||||
}
|
||||
neg := false
|
||||
buf := raw.PreviousBuffer()
|
||||
buf := raw.previousBuffer()
|
||||
if len(buf) > 0 && buf[0] == '-' {
|
||||
neg, buf = true, buf[1:]
|
||||
}
|
||||
@@ -512,7 +512,7 @@ func (k Kind) String() string {
|
||||
case ']':
|
||||
return "]"
|
||||
default:
|
||||
return "<invalid json.Kind: " + jsonwire.QuoteRune(string(k)) + ">"
|
||||
return "<invalid jsontext.Kind: " + jsonwire.QuoteRune(string(k)) + ">"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
324
vendor/github.com/go-json-experiment/json/jsontext/value.go
generated
vendored
324
vendor/github.com/go-json-experiment/json/jsontext/value.go
generated
vendored
@@ -9,7 +9,6 @@ import (
|
||||
"errors"
|
||||
"io"
|
||||
"slices"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/go-json-experiment/json/internal/jsonflags"
|
||||
@@ -18,6 +17,22 @@ import (
|
||||
|
||||
// NOTE: Value is analogous to v1 json.RawMessage.
|
||||
|
||||
// AppendFormat formats the JSON value in src and appends it to dst
|
||||
// according to the specified options.
|
||||
// See [Value.Format] for more details about the formatting behavior.
|
||||
//
|
||||
// The dst and src may overlap.
|
||||
// If an error is reported, then the entirety of src is appended to dst.
|
||||
func AppendFormat(dst, src []byte, opts ...Options) ([]byte, error) {
|
||||
e := getBufferedEncoder(opts...)
|
||||
defer putBufferedEncoder(e)
|
||||
e.s.Flags.Set(jsonflags.OmitTopLevelNewline | 1)
|
||||
if err := e.s.WriteValue(src); err != nil {
|
||||
return append(dst, src...), err
|
||||
}
|
||||
return append(dst, e.s.Buf...), nil
|
||||
}
|
||||
|
||||
// Value represents a single raw JSON value, which may be one of the following:
|
||||
// - a JSON literal (i.e., null, true, or false)
|
||||
// - a JSON string (e.g., "hello, world!")
|
||||
@@ -43,70 +58,159 @@ func (v Value) String() string {
|
||||
}
|
||||
|
||||
// IsValid reports whether the raw JSON value is syntactically valid
|
||||
// according to RFC 7493.
|
||||
// according to the specified options.
|
||||
//
|
||||
// By default (if no options are specified), it validates according to RFC 7493.
|
||||
// It verifies whether the input is properly encoded as UTF-8,
|
||||
// that escape sequences within strings decode to valid Unicode codepoints, and
|
||||
// that all names in each object are unique.
|
||||
// It does not verify whether numbers are representable within the limits
|
||||
// of any common numeric type (e.g., float64, int64, or uint64).
|
||||
func (v Value) IsValid() bool {
|
||||
d := getBufferedDecoder(v)
|
||||
//
|
||||
// Relevant options include:
|
||||
// - [AllowDuplicateNames]
|
||||
// - [AllowInvalidUTF8]
|
||||
//
|
||||
// All other options are ignored.
|
||||
func (v Value) IsValid(opts ...Options) bool {
|
||||
// TODO: Document support for [WithByteLimit] and [WithDepthLimit].
|
||||
d := getBufferedDecoder(v, opts...)
|
||||
defer putBufferedDecoder(d)
|
||||
_, errVal := d.ReadValue()
|
||||
_, errEOF := d.ReadToken()
|
||||
return errVal == nil && errEOF == io.EOF
|
||||
}
|
||||
|
||||
// Format formats the raw JSON value in place.
|
||||
//
|
||||
// By default (if no options are specified), it validates according to RFC 7493
|
||||
// and produces the minimal JSON representation, where
|
||||
// all whitespace is elided and JSON strings use the shortest encoding.
|
||||
//
|
||||
// Relevant options include:
|
||||
// - [AllowDuplicateNames]
|
||||
// - [AllowInvalidUTF8]
|
||||
// - [EscapeForHTML]
|
||||
// - [EscapeForJS]
|
||||
// - [PreserveRawStrings]
|
||||
// - [CanonicalizeRawInts]
|
||||
// - [CanonicalizeRawFloats]
|
||||
// - [ReorderRawObjects]
|
||||
// - [SpaceAfterColon]
|
||||
// - [SpaceAfterComma]
|
||||
// - [Multiline]
|
||||
// - [WithIndent]
|
||||
// - [WithIndentPrefix]
|
||||
//
|
||||
// All other options are ignored.
|
||||
//
|
||||
// It is guaranteed to succeed if the value is valid according to the same options.
|
||||
// If the value is already formatted, then the buffer is not mutated.
|
||||
func (v *Value) Format(opts ...Options) error {
|
||||
// TODO: Document support for [WithByteLimit] and [WithDepthLimit].
|
||||
return v.format(opts, nil)
|
||||
}
|
||||
|
||||
// format accepts two []Options to avoid the allocation appending them together.
|
||||
// It is equivalent to v.Format(append(opts1, opts2...)...).
|
||||
func (v *Value) format(opts1, opts2 []Options) error {
|
||||
e := getBufferedEncoder(opts1...)
|
||||
defer putBufferedEncoder(e)
|
||||
e.s.Join(opts2...)
|
||||
e.s.Flags.Set(jsonflags.OmitTopLevelNewline | 1)
|
||||
if err := e.s.WriteValue(*v); err != nil {
|
||||
return err
|
||||
}
|
||||
if !bytes.Equal(*v, e.s.Buf) {
|
||||
*v = append((*v)[:0], e.s.Buf...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Compact removes all whitespace from the raw JSON value.
|
||||
//
|
||||
// It does not reformat JSON strings to use any other representation.
|
||||
// It is guaranteed to succeed if the input is valid.
|
||||
// If the value is already compacted, then the buffer is not mutated.
|
||||
func (v *Value) Compact() error {
|
||||
return v.reformat(false, false, "", "")
|
||||
// It does not reformat JSON strings or numbers to use any other representation.
|
||||
// To maximize the set of JSON values that can be formatted,
|
||||
// this permits values with duplicate names and invalid UTF-8.
|
||||
//
|
||||
// Compact is equivalent to calling [Value.Format] with the following options:
|
||||
// - [AllowDuplicateNames](true)
|
||||
// - [AllowInvalidUTF8](true)
|
||||
// - [PreserveRawStrings](true)
|
||||
//
|
||||
// Any options specified by the caller are applied after the initial set
|
||||
// and may deliberately override prior options.
|
||||
func (v *Value) Compact(opts ...Options) error {
|
||||
return v.format([]Options{
|
||||
AllowDuplicateNames(true),
|
||||
AllowInvalidUTF8(true),
|
||||
PreserveRawStrings(true),
|
||||
}, opts)
|
||||
}
|
||||
|
||||
// Indent reformats the whitespace in the raw JSON value so that each element
|
||||
// in a JSON object or array begins on a new, indented line beginning with
|
||||
// prefix followed by one or more copies of indent according to the nesting.
|
||||
// The value does not begin with the prefix nor any indention,
|
||||
// to make it easier to embed inside other formatted JSON data.
|
||||
// in a JSON object or array begins on a indented line according to the nesting.
|
||||
//
|
||||
// It does not reformat JSON strings to use any other representation.
|
||||
// It is guaranteed to succeed if the input is valid.
|
||||
// If the value is already indented properly, then the buffer is not mutated.
|
||||
// It does not reformat JSON strings or numbers to use any other representation.
|
||||
// To maximize the set of JSON values that can be formatted,
|
||||
// this permits values with duplicate names and invalid UTF-8.
|
||||
//
|
||||
// The prefix and indent strings must be composed of only spaces and/or tabs.
|
||||
func (v *Value) Indent(prefix, indent string) error {
|
||||
return v.reformat(false, true, prefix, indent)
|
||||
// Indent is equivalent to calling [Value.Format] with the following options:
|
||||
// - [AllowDuplicateNames](true)
|
||||
// - [AllowInvalidUTF8](true)
|
||||
// - [PreserveRawStrings](true)
|
||||
// - [Multiline](true)
|
||||
//
|
||||
// Any options specified by the caller are applied after the initial set
|
||||
// and may deliberately override prior options.
|
||||
func (v *Value) Indent(opts ...Options) error {
|
||||
return v.format([]Options{
|
||||
AllowDuplicateNames(true),
|
||||
AllowInvalidUTF8(true),
|
||||
PreserveRawStrings(true),
|
||||
Multiline(true),
|
||||
}, opts)
|
||||
}
|
||||
|
||||
// Canonicalize canonicalizes the raw JSON value according to the
|
||||
// JSON Canonicalization Scheme (JCS) as defined by RFC 8785
|
||||
// where it produces a stable representation of a JSON value.
|
||||
//
|
||||
// JSON strings are formatted to use their minimal representation,
|
||||
// JSON numbers are formatted as double precision numbers according
|
||||
// to some stable serialization algorithm.
|
||||
// JSON object members are sorted in ascending order by name.
|
||||
// All whitespace is removed.
|
||||
//
|
||||
// The output stability is dependent on the stability of the application data
|
||||
// (see RFC 8785, Appendix E). It cannot produce stable output from
|
||||
// fundamentally unstable input. For example, if the JSON value
|
||||
// contains ephemeral data (e.g., a frequently changing timestamp),
|
||||
// then the value is still unstable regardless of whether this is called.
|
||||
//
|
||||
// Canonicalize is equivalent to calling [Value.Format] with the following options:
|
||||
// - [CanonicalizeRawInts](true)
|
||||
// - [CanonicalizeRawFloats](true)
|
||||
// - [ReorderRawObjects](true)
|
||||
//
|
||||
// Any options specified by the caller are applied after the initial set
|
||||
// and may deliberately override prior options.
|
||||
//
|
||||
// Note that JCS treats all JSON numbers as IEEE 754 double precision numbers.
|
||||
// Any numbers with precision beyond what is representable by that form
|
||||
// will lose their precision when canonicalized. For example, integer values
|
||||
// beyond ±2⁵³ will lose their precision. It is recommended that
|
||||
// int64 and uint64 data types be represented as a JSON string.
|
||||
// beyond ±2⁵³ will lose their precision. To preserve the original representation
|
||||
// of JSON integers, additionally set [CanonicalizeRawInts] to false:
|
||||
//
|
||||
// It is guaranteed to succeed if the input is valid.
|
||||
// If the value is already canonicalized, then the buffer is not mutated.
|
||||
func (v *Value) Canonicalize() error {
|
||||
return v.reformat(true, false, "", "")
|
||||
// v.Canonicalize(jsontext.CanonicalizeRawInts(false))
|
||||
func (v *Value) Canonicalize(opts ...Options) error {
|
||||
return v.format([]Options{
|
||||
CanonicalizeRawInts(true),
|
||||
CanonicalizeRawFloats(true),
|
||||
ReorderRawObjects(true),
|
||||
}, opts)
|
||||
}
|
||||
|
||||
// TODO: Instead of implementing the v1 Marshaler/Unmarshaler,
|
||||
// consider implementing the v2 versions instead.
|
||||
|
||||
// MarshalJSON returns v as the JSON encoding of v.
|
||||
// It returns the stored value as the raw JSON output without any validation.
|
||||
// If v is nil, then this returns a JSON null.
|
||||
@@ -123,7 +227,7 @@ func (v Value) MarshalJSON() ([]byte, error) {
|
||||
func (v *Value) UnmarshalJSON(b []byte) error {
|
||||
// NOTE: This matches the behavior of v1 json.RawMessage.UnmarshalJSON.
|
||||
if v == nil {
|
||||
return errors.New("json.Value: UnmarshalJSON on nil pointer")
|
||||
return errors.New("jsontext.Value: UnmarshalJSON on nil pointer")
|
||||
}
|
||||
*v = append((*v)[:0], b...)
|
||||
return nil
|
||||
@@ -138,93 +242,62 @@ func (v Value) Kind() Kind {
|
||||
return invalidKind
|
||||
}
|
||||
|
||||
func (v *Value) reformat(canonical, multiline bool, prefix, indent string) error {
|
||||
// Write the entire value to reformat all tokens and whitespace.
|
||||
e := getBufferedEncoder()
|
||||
defer putBufferedEncoder(e)
|
||||
eo := &e.s.Struct
|
||||
if canonical {
|
||||
eo.Flags.Set(jsonflags.AllowInvalidUTF8 | 0) // per RFC 8785, section 3.2.4
|
||||
eo.Flags.Set(jsonflags.AllowDuplicateNames | 0) // per RFC 8785, section 3.1
|
||||
eo.Flags.Set(jsonflags.CanonicalizeNumbers | 1) // per RFC 8785, section 3.2.2.3
|
||||
eo.Flags.Set(jsonflags.PreserveRawStrings | 0) // per RFC 8785, section 3.2.2.2
|
||||
eo.Flags.Set(jsonflags.EscapeForHTML | 0) // per RFC 8785, section 3.2.2.2
|
||||
eo.Flags.Set(jsonflags.EscapeForJS | 0) // per RFC 8785, section 3.2.2.2
|
||||
eo.Flags.Set(jsonflags.Expand | 0) // per RFC 8785, section 3.2.1
|
||||
} else {
|
||||
if s := strings.TrimLeft(prefix, " \t"); len(s) > 0 {
|
||||
panic("json: invalid character " + jsonwire.QuoteRune(s) + " in indent prefix")
|
||||
}
|
||||
if s := strings.TrimLeft(indent, " \t"); len(s) > 0 {
|
||||
panic("json: invalid character " + jsonwire.QuoteRune(s) + " in indent")
|
||||
}
|
||||
eo.Flags.Set(jsonflags.AllowInvalidUTF8 | 1)
|
||||
eo.Flags.Set(jsonflags.AllowDuplicateNames | 1)
|
||||
eo.Flags.Set(jsonflags.PreserveRawStrings | 1)
|
||||
if multiline {
|
||||
eo.Flags.Set(jsonflags.Expand | 1)
|
||||
eo.Flags.Set(jsonflags.Indent | 1)
|
||||
eo.Flags.Set(jsonflags.IndentPrefix | 1)
|
||||
eo.IndentPrefix = prefix
|
||||
eo.Indent = indent
|
||||
} else {
|
||||
eo.Flags.Set(jsonflags.Expand | 0)
|
||||
}
|
||||
}
|
||||
eo.Flags.Set(jsonflags.OmitTopLevelNewline | 1)
|
||||
if err := e.s.WriteValue(*v); err != nil {
|
||||
return err
|
||||
}
|
||||
const commaAndWhitespace = ", \n\r\t"
|
||||
|
||||
// For canonical output, we may need to reorder object members.
|
||||
if canonical {
|
||||
// Obtain a buffered encoder just to use its internal buffer as
|
||||
// a scratch buffer in reorderObjects for reordering object members.
|
||||
e2 := getBufferedEncoder()
|
||||
defer putBufferedEncoder(e2)
|
||||
|
||||
// Disable redundant checks performed earlier during encoding.
|
||||
d := getBufferedDecoder(e.s.Buf)
|
||||
defer putBufferedDecoder(d)
|
||||
d.s.Flags.Set(jsonflags.AllowDuplicateNames | jsonflags.AllowInvalidUTF8 | 1)
|
||||
reorderObjects(d, &e2.s.Buf) // per RFC 8785, section 3.2.3
|
||||
}
|
||||
|
||||
// Store the result back into the value if different.
|
||||
if !bytes.Equal(*v, e.s.Buf) {
|
||||
*v = append((*v)[:0], e.s.Buf...)
|
||||
}
|
||||
return nil
|
||||
type objectMember struct {
|
||||
// name is the unquoted name.
|
||||
name []byte // e.g., "name"
|
||||
// buffer is the entirety of the raw JSON object member
|
||||
// starting from right after the previous member (or opening '{')
|
||||
// until right after the member value.
|
||||
buffer []byte // e.g., `, \n\r\t"name": "value"`
|
||||
}
|
||||
|
||||
type memberName struct {
|
||||
// name is the unescaped name.
|
||||
name []byte
|
||||
// before and after are byte offsets into Decoder.buf that represents
|
||||
// the entire name/value pair. It may contain leading commas.
|
||||
before, after int64
|
||||
func (x objectMember) Compare(y objectMember) int {
|
||||
if c := jsonwire.CompareUTF16(x.name, y.name); c != 0 {
|
||||
return c
|
||||
}
|
||||
// With [AllowDuplicateNames] or [AllowInvalidUTF8],
|
||||
// names could be identical, so also sort using the member value.
|
||||
return jsonwire.CompareUTF16(
|
||||
bytes.TrimLeft(x.buffer, commaAndWhitespace),
|
||||
bytes.TrimLeft(y.buffer, commaAndWhitespace))
|
||||
}
|
||||
|
||||
var memberNamePool = sync.Pool{New: func() any { return new([]memberName) }}
|
||||
var objectMemberPool = sync.Pool{New: func() any { return new([]objectMember) }}
|
||||
|
||||
func getMemberNames() *[]memberName {
|
||||
ns := memberNamePool.Get().(*[]memberName)
|
||||
func getObjectMembers() *[]objectMember {
|
||||
ns := objectMemberPool.Get().(*[]objectMember)
|
||||
*ns = (*ns)[:0]
|
||||
return ns
|
||||
}
|
||||
func putMemberNames(ns *[]memberName) {
|
||||
func putObjectMembers(ns *[]objectMember) {
|
||||
if cap(*ns) < 1<<10 {
|
||||
clear(*ns) // avoid pinning name
|
||||
memberNamePool.Put(ns)
|
||||
clear(*ns) // avoid pinning name and buffer
|
||||
objectMemberPool.Put(ns)
|
||||
}
|
||||
}
|
||||
|
||||
// reorderObjects recursively reorders all object members in place
|
||||
// mustReorderObjects reorders in-place all object members in a JSON value,
|
||||
// which must be valid otherwise it panics.
|
||||
func mustReorderObjects(b []byte) {
|
||||
// Obtain a buffered encoder just to use its internal buffer as
|
||||
// a scratch buffer for reordering object members.
|
||||
e2 := getBufferedEncoder()
|
||||
defer putBufferedEncoder(e2)
|
||||
|
||||
// Disable unnecessary checks to syntactically parse the JSON value.
|
||||
d := getBufferedDecoder(b)
|
||||
defer putBufferedDecoder(d)
|
||||
d.s.Flags.Set(jsonflags.AllowDuplicateNames | jsonflags.AllowInvalidUTF8 | 1)
|
||||
mustReorderObjectsFromDecoder(d, &e2.s.Buf) // per RFC 8785, section 3.2.3
|
||||
}
|
||||
|
||||
// mustReorderObjectsFromDecoder recursively reorders all object members in place
|
||||
// according to the ordering specified in RFC 8785, section 3.2.3.
|
||||
//
|
||||
// Pre-conditions:
|
||||
// - The value is valid (i.e., no decoder errors should ever occur).
|
||||
// - The value is compact (i.e., no whitespace is present).
|
||||
// - Initial call is provided a Decoder reading from the start of v.
|
||||
//
|
||||
// Post-conditions:
|
||||
@@ -234,13 +307,13 @@ func putMemberNames(ns *[]memberName) {
|
||||
//
|
||||
// The runtime is approximately O(n·log(n)) + O(m·log(m)),
|
||||
// where n is len(v) and m is the total number of object members.
|
||||
func reorderObjects(d *Decoder, scratch *[]byte) {
|
||||
switch tok, _ := d.ReadToken(); tok.Kind() {
|
||||
func mustReorderObjectsFromDecoder(d *Decoder, scratch *[]byte) {
|
||||
switch tok, err := d.ReadToken(); tok.Kind() {
|
||||
case '{':
|
||||
// Iterate and collect the name and offsets for every object member.
|
||||
members := getMemberNames()
|
||||
defer putMemberNames(members)
|
||||
var prevName []byte
|
||||
members := getObjectMembers()
|
||||
defer putObjectMembers(members)
|
||||
var prevMember objectMember
|
||||
isSorted := true
|
||||
|
||||
beforeBody := d.InputOffset() // offset after '{'
|
||||
@@ -249,14 +322,15 @@ func reorderObjects(d *Decoder, scratch *[]byte) {
|
||||
var flags jsonwire.ValueFlags
|
||||
name, _ := d.s.ReadValue(&flags)
|
||||
name = jsonwire.UnquoteMayCopy(name, flags.IsVerbatim())
|
||||
reorderObjects(d, scratch)
|
||||
mustReorderObjectsFromDecoder(d, scratch)
|
||||
afterValue := d.InputOffset()
|
||||
|
||||
currMember := objectMember{name, d.s.buf[beforeName:afterValue]}
|
||||
if isSorted && len(*members) > 0 {
|
||||
isSorted = jsonwire.CompareUTF16(prevName, []byte(name)) < 0
|
||||
isSorted = objectMember.Compare(prevMember, currMember) < 0
|
||||
}
|
||||
*members = append(*members, memberName{name, beforeName, afterValue})
|
||||
prevName = name
|
||||
*members = append(*members, currMember)
|
||||
prevMember = currMember
|
||||
}
|
||||
afterBody := d.InputOffset() // offset before '}'
|
||||
d.ReadToken()
|
||||
@@ -265,9 +339,9 @@ func reorderObjects(d *Decoder, scratch *[]byte) {
|
||||
if isSorted {
|
||||
return
|
||||
}
|
||||
slices.SortFunc(*members, func(x, y memberName) int {
|
||||
return jsonwire.CompareUTF16(x.name, y.name)
|
||||
})
|
||||
firstBufferBeforeSorting := (*members)[0].buffer
|
||||
slices.SortFunc(*members, objectMember.Compare)
|
||||
firstBufferAfterSorting := (*members)[0].buffer
|
||||
|
||||
// Append the reordered members to a new buffer,
|
||||
// then copy the reordered members back over the original members.
|
||||
@@ -277,14 +351,24 @@ func reorderObjects(d *Decoder, scratch *[]byte) {
|
||||
//
|
||||
// The following invariant must hold:
|
||||
// sum([m.after-m.before for m in members]) == afterBody-beforeBody
|
||||
commaAndWhitespacePrefix := func(b []byte) []byte {
|
||||
return b[:len(b)-len(bytes.TrimLeft(b, commaAndWhitespace))]
|
||||
}
|
||||
sorted := (*scratch)[:0]
|
||||
for i, member := range *members {
|
||||
if d.s.buf[member.before] == ',' {
|
||||
member.before++ // trim leading comma
|
||||
}
|
||||
sorted = append(sorted, d.s.buf[member.before:member.after]...)
|
||||
if i < len(*members)-1 {
|
||||
sorted = append(sorted, ',') // append trailing comma
|
||||
switch {
|
||||
case i == 0 && &member.buffer[0] != &firstBufferBeforeSorting[0]:
|
||||
// First member after sorting is not the first member before sorting,
|
||||
// so use the prefix of the first member before sorting.
|
||||
sorted = append(sorted, commaAndWhitespacePrefix(firstBufferBeforeSorting)...)
|
||||
sorted = append(sorted, bytes.TrimLeft(member.buffer, commaAndWhitespace)...)
|
||||
case i != 0 && &member.buffer[0] == &firstBufferBeforeSorting[0]:
|
||||
// Later member after sorting is the first member before sorting,
|
||||
// so use the prefix of the first member after sorting.
|
||||
sorted = append(sorted, commaAndWhitespacePrefix(firstBufferAfterSorting)...)
|
||||
sorted = append(sorted, bytes.TrimLeft(member.buffer, commaAndWhitespace)...)
|
||||
default:
|
||||
sorted = append(sorted, member.buffer...)
|
||||
}
|
||||
}
|
||||
if int(afterBody-beforeBody) != len(sorted) {
|
||||
@@ -298,8 +382,12 @@ func reorderObjects(d *Decoder, scratch *[]byte) {
|
||||
}
|
||||
case '[':
|
||||
for d.PeekKind() != ']' {
|
||||
reorderObjects(d, scratch)
|
||||
mustReorderObjectsFromDecoder(d, scratch)
|
||||
}
|
||||
d.ReadToken()
|
||||
default:
|
||||
if err != nil {
|
||||
panic("BUG: " + err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user