summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke Shumaker <lukeshu@lukeshu.com>2022-08-01 23:59:18 -0600
committerLuke Shumaker <lukeshu@lukeshu.com>2022-08-01 23:59:18 -0600
commit06d7be736ac93cd548db84d7f898d6ef0b257c1f (patch)
tree49af97146b75a2294fc5ffd3831bd9cd8f8a3a5b
parenta0950f4c29b0b72fc1df867dcfb4d4ad86ec3ca4 (diff)
overhaul decoder for better error messages
-rw-r--r--lib/lowmemjson/adapter_test.go31
-rw-r--r--lib/lowmemjson/decode.go509
-rw-r--r--lib/lowmemjson/encode.go10
3 files changed, 321 insertions, 229 deletions
diff --git a/lib/lowmemjson/adapter_test.go b/lib/lowmemjson/adapter_test.go
index 954381a..b1aec3e 100644
--- a/lib/lowmemjson/adapter_test.go
+++ b/lib/lowmemjson/adapter_test.go
@@ -71,36 +71,17 @@ func Unmarshal(data []byte, ptr any) error {
return Decode(bytes.NewReader(data), ptr)
}
-type _Decoder struct {
- src *bufio.Reader
- cfg decodeConfig
+func init() {
+ forceBufio = true
}
-func NewDecoder(r io.Reader) *_Decoder {
- return &_Decoder{
- src: bufio.NewReader(r),
- }
-}
-
-func (dec *_Decoder) DisallowUnknownFields() { dec.cfg.disallowUnknownFields = true }
-func (dec *_Decoder) UseNumber() { dec.cfg.useNumber = true }
-
-func (dec *_Decoder) Buffered() io.Reader {
- dat, _ := dec.src.Peek(dec.src.Buffered())
+func (dec *Decoder) Buffered() io.Reader {
+ buf := dec.r.(*bufio.Reader)
+ dat, _ := buf.Peek(buf.Buffered())
return bytes.NewReader(dat)
}
-func (dec *_Decoder) Decode(v any) error {
- return _Decode(dec.src, v, dec.cfg)
-}
-func (dec *_Decoder) InputOffset() int64 { return 0 }
-func (dec *_Decoder) More() bool {
- decodeWS(dec.src)
- _, ok := peekRuneOrEOF(dec.src)
- return ok
-}
-
-//func (dec *_Decoder) Token() (Token, error)
+//func (dec *Decoder) Token() (Token, error)
/////////////////////////////////////////////////////////////////////
diff --git a/lib/lowmemjson/decode.go b/lib/lowmemjson/decode.go
index aeca291..2dee59c 100644
--- a/lib/lowmemjson/decode.go
+++ b/lib/lowmemjson/decode.go
@@ -5,6 +5,7 @@
package lowmemjson
import (
+ "bufio"
"bytes"
"encoding"
"encoding/json"
@@ -15,76 +16,91 @@ import (
"strings"
)
-type Decoder interface {
+type Decodable interface {
DecodeJSON(io.RuneScanner) error
}
-type decodeError struct {
- Err error
-}
-
type runeBuffer interface {
io.Writer
WriteRune(rune) (int, error)
Reset()
}
-func readRune(r io.RuneReader) rune {
- c, _, err := r.ReadRune()
- if err != nil {
- panic(decodeError{err})
- }
- return c
-}
+type Decoder struct {
+ r io.RuneScanner
-func readRuneOrEOF(r io.RuneReader) (c rune, ok bool) {
- c, _, err := r.ReadRune()
- if err != nil {
- if err == io.EOF {
- return 0, false
- }
- panic(decodeError{err})
- }
- return c, true
+ // config
+ disallowUnknownFields bool
+ useNumber bool
+
+ // state
+ err error
+ curPos int64
+ nxtPos int64
+ stack []any
}
-func unreadRune(r io.RuneScanner) {
- if err := r.UnreadRune(); err != nil {
- panic(decodeError{err})
+var forceBufio bool
+
+func NewDecoder(r io.Reader) *Decoder {
+ rs, ok := r.(io.RuneScanner)
+ if forceBufio || !ok {
+ rs = bufio.NewReader(r)
+ }
+ return &Decoder{
+ r: rs,
}
}
-func peekRune(r io.RuneScanner) rune {
- c := readRune(r)
- unreadRune(r)
- return c
+func (dec *Decoder) DisallowUnknownFields() { dec.disallowUnknownFields = true }
+func (dec *Decoder) UseNumber() { dec.useNumber = true }
+func (dec *Decoder) InputOffset() int64 { return dec.curPos }
+
+func (dec *Decoder) More() bool {
+ dec.decodeWS()
+ _, ok := dec.peekRuneOrEOF()
+ return ok
}
-func peekRuneOrEOF(r io.RuneScanner) (rune, bool) {
- c, ok := readRuneOrEOF(r)
- if ok {
- unreadRune(r)
+func (dec *Decoder) stackStr() string {
+ var buf strings.Builder
+ buf.WriteString("v")
+ for _, item := range dec.stack {
+ fmt.Fprintf(&buf, "[%#v]", item)
}
- return c, ok
+ return buf.String()
}
-func expectRune(r io.RuneReader, exp rune) {
- act := readRune(r)
- if act != exp {
- panic(decodeError{fmt.Errorf("expected %c but got %c", exp, act)})
- }
+func (dec *Decoder) stackPush(idx any) {
+ dec.stack = append(dec.stack, idx)
+}
+func (dec *Decoder) stackPop() {
+ dec.stack = dec.stack[:len(dec.stack)-1]
}
-type decodeConfig struct {
- disallowUnknownFields bool
- useNumber bool
+type decodeError struct{}
+
+func (dec *Decoder) panicIO(err error) {
+ dec.err = fmt.Errorf("json: I/O error at input byte %v: %s: %w",
+ dec.nxtPos, dec.stackStr(), err)
+ panic(decodeError{})
+}
+func (dec *Decoder) panicSyntax(err error) {
+ dec.err = fmt.Errorf("json: syntax error at input byte %v: %s: %w",
+ dec.curPos, dec.stackStr(), err)
+ panic(decodeError{})
+}
+func (dec *Decoder) panicType(typ reflect.Type, err error) {
+ dec.err = fmt.Errorf("json: type mismatch error at input byte %v: %s: type %v: %w",
+ dec.curPos, dec.stackStr(), typ, err)
+ panic(decodeError{})
}
-func Decode(r io.RuneScanner, ptr any) (err error) {
- return _Decode(r, ptr, decodeConfig{})
+func Decode(r io.Reader, ptr any) error {
+ return NewDecoder(r).Decode(ptr)
}
-func _Decode(r io.RuneScanner, ptr any, cfg decodeConfig) (err error) {
+func (dec *Decoder) Decode(ptr any) (err error) {
ptrVal := reflect.ValueOf(ptr)
if ptrVal.Kind() != reflect.Pointer || ptrVal.IsNil() || !ptrVal.Elem().CanSet() {
return &json.InvalidUnmarshalError{
@@ -92,23 +108,101 @@ func _Decode(r io.RuneScanner, ptr any, cfg decodeConfig) (err error) {
}
}
+ if dec.err != nil {
+ return dec.err
+ }
+
defer func() {
if r := recover(); r != nil {
- if e, ok := r.(decodeError); ok {
- err = e.Err
+ if _, ok := r.(decodeError); ok {
+ err = dec.err
} else {
panic(r)
}
}
}()
- decodeWS(r)
- decode(r, ptrVal.Elem(), cfg)
+ dec.decodeWS()
+ dec.decode(ptrVal.Elem())
return nil
}
+func (dec *Decoder) readRune() rune {
+ c, size, err := dec.r.ReadRune()
+ if err != nil {
+ if err == io.EOF {
+ dec.panicSyntax(io.ErrUnexpectedEOF)
+ }
+ dec.panicIO(err)
+ }
+ dec.curPos = dec.nxtPos
+ dec.nxtPos = dec.curPos + int64(size)
+ return c
+}
+
+func (dec *Decoder) readRuneOrEOF() (c rune, ok bool) {
+ c, size, err := dec.r.ReadRune()
+ if err != nil {
+ if err == io.EOF {
+ return 0, false
+ }
+ dec.panicIO(err)
+ }
+ dec.curPos = dec.nxtPos
+ dec.nxtPos = dec.curPos + int64(size)
+ return c, true
+}
+
+func (dec *Decoder) unreadRune() {
+ if err := dec.r.UnreadRune(); err != nil {
+ // .UnreadRune() must succeed if the previous call was
+ // .ReadRune(), which it always is for this code.
+ panic(err)
+ }
+ dec.nxtPos = dec.curPos
+}
+
+func (dec *Decoder) peekRune() rune {
+ c, _, err := dec.r.ReadRune()
+ if err != nil {
+ if err == io.EOF {
+ dec.panicSyntax(io.ErrUnexpectedEOF)
+ }
+ dec.panicIO(err)
+ }
+ if err := dec.r.UnreadRune(); err != nil {
+ // .UnreadRune() must succeed if the previous call was
+ // .ReadRune(), which it always is for this code.
+ panic(err)
+ }
+ return c
+}
+
+func (dec *Decoder) peekRuneOrEOF() (rune, bool) {
+ c, _, err := dec.r.ReadRune()
+ if err != nil {
+ if err == io.EOF {
+ return 0, false
+ }
+ dec.panicIO(err)
+ }
+ if err := dec.r.UnreadRune(); err != nil {
+ // .UnreadRune() must succeed if the previous call was
+ // .ReadRune(), which it always is for this code.
+ panic(err)
+ }
+ return c, true
+}
+
+func (dec *Decoder) expectRune(exp rune) {
+ act := dec.readRune()
+ if act != exp {
+ dec.panicSyntax(fmt.Errorf("expected %q but got %q", exp, act))
+ }
+}
+
var (
rawMessagePtrType = reflect.TypeOf((*json.RawMessage)(nil))
- decoderType = reflect.TypeOf((*Decoder)(nil)).Elem()
+ decodableType = reflect.TypeOf((*Decodable)(nil)).Elem()
jsonUnmarshalerType = reflect.TypeOf((*json.Unmarshaler)(nil)).Elem()
textUnmarshalerType = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem()
)
@@ -132,106 +226,106 @@ var kind2bits = map[reflect.Kind]int{
reflect.Float64: 64,
}
-func decode(r io.RuneScanner, val reflect.Value, cfg decodeConfig) {
+func (dec *Decoder) decode(val reflect.Value) {
typ := val.Type()
switch {
case val.CanAddr() && reflect.PointerTo(typ) == rawMessagePtrType:
var buf bytes.Buffer
- scan(r, &buf)
+ dec.scan(&buf)
if err := val.Addr().Interface().(*json.RawMessage).UnmarshalJSON(buf.Bytes()); err != nil {
- panic(decodeError{err})
+ dec.panicSyntax(err)
}
- case val.CanAddr() && reflect.PointerTo(typ).Implements(decoderType):
- obj := val.Addr().Interface().(Decoder)
- if err := obj.DecodeJSON(r); err != nil {
- panic(decodeError{err})
+ case val.CanAddr() && reflect.PointerTo(typ).Implements(decodableType):
+ obj := val.Addr().Interface().(Decodable)
+ if err := obj.DecodeJSON(dec.r); err != nil {
+ dec.panicSyntax(err)
}
case val.CanAddr() && reflect.PointerTo(typ).Implements(jsonUnmarshalerType):
var buf bytes.Buffer
- scan(r, &buf)
+ dec.scan(&buf)
obj := val.Addr().Interface().(json.Unmarshaler)
if err := obj.UnmarshalJSON(buf.Bytes()); err != nil {
- panic(decodeError{err})
+ dec.panicSyntax(err)
}
case val.CanAddr() && reflect.PointerTo(typ).Implements(textUnmarshalerType):
var buf bytes.Buffer
- decodeString(r, &buf)
+ dec.decodeString(&buf)
obj := val.Addr().Interface().(encoding.TextUnmarshaler)
if err := obj.UnmarshalText(buf.Bytes()); err != nil {
- panic(decodeError{err})
+ dec.panicSyntax(err)
}
default:
kind := typ.Kind()
switch kind {
case reflect.Bool:
- val.SetBool(decodeBool(r))
+ val.SetBool(dec.decodeBool())
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
var buf strings.Builder
- scanNumber(r, &buf)
+ dec.scanNumber(&buf)
n, err := strconv.ParseInt(buf.String(), 10, kind2bits[kind])
if err != nil {
- panic(decodeError{err})
+ dec.panicSyntax(err)
}
val.SetInt(n)
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
var buf strings.Builder
- scanNumber(r, &buf)
+ dec.scanNumber(&buf)
n, err := strconv.ParseUint(buf.String(), 10, kind2bits[kind])
if err != nil {
- panic(decodeError{err})
+ dec.panicSyntax(err)
}
val.SetUint(n)
case reflect.Float32, reflect.Float64:
var buf strings.Builder
- scanNumber(r, &buf)
+ dec.scanNumber(&buf)
n, err := strconv.ParseFloat(buf.String(), kind2bits[kind])
if err != nil {
- panic(decodeError{err})
+ dec.panicSyntax(err)
}
val.SetFloat(n)
case reflect.String:
var buf strings.Builder
if typ == numberType {
- scanNumber(r, &buf)
+ dec.scanNumber(&buf)
val.SetString(buf.String())
} else {
- decodeString(r, &buf)
+ dec.decodeString(&buf)
val.SetString(buf.String())
}
case reflect.Interface:
if typ.NumMethod() > 0 {
- panic(decodeError{&json.UnsupportedTypeError{
- Type: typ,
- }})
+ dec.panicType(typ, fmt.Errorf("cannot decode in to non-empty interface"))
}
- switch peekRune(r) {
+ switch dec.peekRune() {
case 'n':
if !val.IsNil() && val.Elem().Kind() == reflect.Pointer && val.Elem().Elem().Kind() == reflect.Pointer {
// XXX: I can't justify this case, other than "it's what encoding/json does, but
// I don't understand their rationale".
- decode(r, val.Elem(), cfg)
+ dec.decode(val.Elem())
} else {
- decodeNull(r)
+ dec.decodeNull()
val.Set(reflect.Zero(typ))
}
default:
if !val.IsNil() && val.Elem().Kind() == reflect.Pointer {
- decode(r, val.Elem(), cfg)
+ dec.decode(val.Elem())
} else {
- val.Set(reflect.ValueOf(decodeAny(r, cfg)))
+ val.Set(reflect.ValueOf(dec.decodeAny()))
}
}
case reflect.Struct:
index := indexStruct(typ)
var nameBuf strings.Builder
- decodeObject(r, &nameBuf, func() {
+ dec.decodeObject(&nameBuf, func() {
name := nameBuf.String()
+ dec.stackPush(name)
+ defer dec.stackPop()
idx, ok := index.byName[name]
if !ok {
- if cfg.disallowUnknownFields {
- panic(decodeError{fmt.Errorf("unknown field %q", name)})
+ if dec.disallowUnknownFields {
+ dec.panicType(typ, fmt.Errorf("unknown field %q", name))
}
- scan(r, io.Discard)
+ dec.scan(io.Discard)
return
}
field := index.byPos[idx]
@@ -240,7 +334,7 @@ func decode(r io.RuneScanner, val reflect.Value, cfg decodeConfig) {
if fVal.Kind() == reflect.Pointer {
if fVal.IsNil() {
if !fVal.CanSet() { // https://golang.org/issue/21357
- panic(decodeError{fmt.Errorf("cannot set embedded pointer to unexported type %v", fVal.Type().Elem())})
+ dec.panicType(fVal.Type().Elem(), fmt.Errorf("cannot set embedded pointer to unexported type"))
}
fVal.Set(reflect.New(fVal.Type().Elem()))
}
@@ -249,10 +343,9 @@ func decode(r io.RuneScanner, val reflect.Value, cfg decodeConfig) {
fVal = fVal.Field(idx)
}
if field.Quote {
- var buf bytes.Buffer
- switch peekRune(r) {
+ switch dec.peekRune() {
case 'n':
- decodeNull(r)
+ dec.decodeNull()
switch fVal.Kind() {
// XXX: I can't justify this list, other than "it's what encoding/json
// does, but I don't understand their rationale".
@@ -260,33 +353,38 @@ func decode(r io.RuneScanner, val reflect.Value, cfg decodeConfig) {
fVal.Set(reflect.Zero(fVal.Type()))
}
case '"':
- decodeString(r, &buf)
- decode(&buf, fVal, cfg)
+ // TODO: Figure out how to do this without buffering.
+ var buf bytes.Buffer
+ subD := *dec // capture the .curPos *before* calling .decodeString
+ dec.decodeString(&buf)
+ subD.r = &buf
+ subD.decode(fVal)
default:
- panic(decodeError{fmt.Errorf("invalid character %q for ,string struct value", peekRune(r))})
+ dec.panicSyntax(fmt.Errorf(",string field: expected %q or %q but got %q",
+ 'n', '"', dec.peekRune()))
}
} else {
- decode(r, fVal, cfg)
+ dec.decode(fVal)
}
})
case reflect.Map:
- switch peekRune(r) {
+ switch dec.peekRune() {
case 'n':
- decodeNull(r)
+ dec.decodeNull()
val.Set(reflect.Zero(typ))
case '{':
if val.IsNil() {
val.Set(reflect.MakeMap(typ))
}
var nameBuf bytes.Buffer
- decodeObject(r, &nameBuf, func() {
+ dec.decodeObject(&nameBuf, func() {
nameValTyp := typ.Key()
nameValPtr := reflect.New(nameValTyp)
switch {
case reflect.PointerTo(nameValTyp).Implements(textUnmarshalerType):
obj := nameValPtr.Interface().(encoding.TextUnmarshaler)
if err := obj.UnmarshalText(nameBuf.Bytes()); err != nil {
- panic(decodeError{err})
+ dec.panicSyntax(err)
}
default:
switch nameValTyp.Kind() {
@@ -295,65 +393,72 @@ func decode(r io.RuneScanner, val reflect.Value, cfg decodeConfig) {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
n, err := strconv.ParseInt(nameBuf.String(), 10, kind2bits[nameValTyp.Kind()])
if err != nil {
- panic(decodeError{err})
+ dec.panicSyntax(err)
}
nameValPtr.Elem().SetInt(n)
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
n, err := strconv.ParseUint(nameBuf.String(), 10, kind2bits[nameValTyp.Kind()])
if err != nil {
- panic(decodeError{err})
+ dec.panicSyntax(err)
}
nameValPtr.Elem().SetUint(n)
default:
- panic(decodeError{fmt.Errorf("invalid map key type: %v", nameValTyp)})
+ dec.panicType(typ, fmt.Errorf("invalid map key type: %v", nameValTyp))
}
}
+ dec.stackPush(nameValPtr.Elem())
+ defer dec.stackPop()
fValPtr := reflect.New(typ.Elem())
- decode(r, fValPtr.Elem(), cfg)
+ dec.decode(fValPtr.Elem())
val.SetMapIndex(nameValPtr.Elem(), fValPtr.Elem())
})
default:
- panic(decodeError{fmt.Errorf("invalid character %q for map value", peekRune(r))})
+ dec.panicSyntax(fmt.Errorf("map: expected %q or %q bug got %q", 'n', '{', dec.peekRune()))
}
case reflect.Slice:
switch {
case typ.Elem().Kind() == reflect.Uint8:
var buf bytes.Buffer
- dec := newBase64Decoder(&buf)
- decodeString(r, dec)
+ dec.decodeString(newBase64Decoder(&buf))
val.Set(reflect.ValueOf(buf.Bytes()))
default:
- switch peekRune(r) {
+ switch dec.peekRune() {
case 'n':
- decodeNull(r)
+ dec.decodeNull()
val.Set(reflect.Zero(typ))
case '[':
if val.IsNil() {
val.Set(reflect.MakeSlice(typ, 0, 0))
}
- decodeArray(r, func() {
+ i := 0
+ dec.decodeArray(func() {
+ dec.stackPush(i)
+ defer dec.stackPop()
mValPtr := reflect.New(typ.Elem())
- decode(r, mValPtr.Elem(), cfg)
+ dec.decode(mValPtr.Elem())
val.Set(reflect.Append(val, mValPtr.Elem()))
+ i++
})
default:
- panic(decodeError{fmt.Errorf("invalid character %q for slice value", peekRune(r))})
+ dec.panicSyntax(fmt.Errorf("slice: expected %q or %q but got %q", 'n', '[', dec.peekRune()))
}
}
case reflect.Array:
i := 0
- decodeArray(r, func() {
+ dec.decodeArray(func() {
+ dec.stackPush(i)
+ defer dec.stackPop()
mValPtr := reflect.New(typ.Elem())
- decode(r, mValPtr.Elem(), cfg)
+ dec.decode(mValPtr.Elem())
val.Index(i).Set(mValPtr.Elem())
i++
})
case reflect.Pointer:
- switch peekRune(r) {
+ switch dec.peekRune() {
case 'n':
- decodeNull(r)
+ dec.decodeNull()
for val.IsNil() && typ.Elem().Kind() == reflect.Pointer {
val.Set(reflect.New(typ.Elem()))
val = val.Elem()
@@ -363,19 +468,17 @@ func decode(r io.RuneScanner, val reflect.Value, cfg decodeConfig) {
if val.IsNil() {
val.Set(reflect.New(typ.Elem()))
}
- decode(r, val.Elem(), cfg)
+ dec.decode(val.Elem())
}
default:
- panic(decodeError{&json.UnsupportedTypeError{
- Type: typ,
- }})
+ dec.panicType(typ, fmt.Errorf("unsupported type (kind=%v)", typ.Kind()))
}
}
}
-func decodeWS(r io.RuneScanner) {
+func (dec *Decoder) decodeWS() {
for {
- c, ok := readRuneOrEOF(r)
+ c, ok := dec.readRuneOrEOF()
if !ok {
return
}
@@ -385,25 +488,25 @@ func decodeWS(r io.RuneScanner) {
case 0x0020, 0x000A, 0x000D, 0x0009:
// do nothing
default:
- unreadRune(r)
+ dec.unreadRune()
return
}
}
}
-func scan(r io.RuneScanner, out io.Writer) {
+func (dec *Decoder) scan(out io.Writer) {
scanner := &ReEncoder{
Out: out,
Compact: true,
}
- if _, err := scanner.WriteRune(readRune(r)); err != nil {
- panic(decodeError{err})
+ if _, err := scanner.WriteRune(dec.readRune()); err != nil {
+ dec.panicSyntax(err)
}
scanner.bailAfterCurrent = true
var err error
var eof bool
for err == nil {
- c, ok := readRuneOrEOF(r)
+ c, ok := dec.readRuneOrEOF()
if ok {
_, err = scanner.WriteRune(c)
} else {
@@ -415,126 +518,132 @@ func scan(r io.RuneScanner, out io.Writer) {
if err != nil {
if err == errBailedAfterCurrent {
if !eof {
- unreadRune(r)
+ dec.unreadRune()
}
} else {
- panic(decodeError{err})
+ dec.panicSyntax(err)
}
}
}
-func scanNumber(r io.RuneScanner, out io.Writer) {
- c := peekRune(r)
+func (dec *Decoder) scanNumber(out io.Writer) {
+ c := dec.peekRune()
switch c {
case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
- scan(r, out)
+ dec.scan(out)
default:
- panic(decodeError{fmt.Errorf("expected a number but got %c", c)})
+ dec.panicSyntax(fmt.Errorf("number: expected %q or a digit, but got %q", '-', c))
}
}
-func decodeAny(r io.RuneScanner, cfg decodeConfig) any {
- c := peekRune(r)
+func (dec *Decoder) decodeAny() any {
+ c := dec.peekRune()
switch c {
case '{':
ret := make(map[string]any)
var nameBuf strings.Builder
- decodeObject(r, &nameBuf, func() {
- ret[nameBuf.String()] = decodeAny(r, cfg)
+ dec.decodeObject(&nameBuf, func() {
+ name := nameBuf.String()
+ dec.stackPush(name)
+ defer dec.stackPop()
+ ret[name] = dec.decodeAny()
})
return ret
case '[':
ret := []any{}
- decodeArray(r, func() {
- ret = append(ret, decodeAny(r, cfg))
+ dec.decodeArray(func() {
+ dec.stackPush(len(ret))
+ defer dec.stackPop()
+ ret = append(ret, dec.decodeAny())
})
return ret
case '"':
var buf strings.Builder
- decodeString(r, &buf)
+ dec.decodeString(&buf)
return buf.String()
case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
var buf strings.Builder
- scanNumber(r, &buf)
+ dec.scanNumber(&buf)
num := json.Number(buf.String())
- if cfg.useNumber {
+ if dec.useNumber {
return num
}
f64, err := num.Float64()
if err != nil {
- panic(decodeError{err})
+ dec.panicSyntax(err)
}
return f64
case 't', 'f':
- return decodeBool(r)
+ return dec.decodeBool()
case 'n':
- decodeNull(r)
+ dec.decodeNull()
return nil
default:
- panic(decodeError{fmt.Errorf("unexpected character: %c", c)})
+ dec.panicSyntax(fmt.Errorf("any: unexpected character: %c", c))
+ panic("not reached")
}
}
-func decodeObject(r io.RuneScanner, nameBuf runeBuffer, decodeKVal func()) {
- expectRune(r, '{')
- decodeWS(r)
- c := readRune(r)
+func (dec *Decoder) decodeObject(nameBuf runeBuffer, decodeKVal func()) {
+ dec.expectRune('{')
+ dec.decodeWS()
+ c := dec.readRune()
switch c {
case '"':
decodeMember:
- unreadRune(r)
+ dec.unreadRune()
nameBuf.Reset()
- decodeString(r, nameBuf)
- decodeWS(r)
- expectRune(r, ':')
- decodeWS(r)
+ dec.decodeString(nameBuf)
+ dec.decodeWS()
+ dec.expectRune(':')
+ dec.decodeWS()
decodeKVal()
- decodeWS(r)
- c := readRune(r)
+ dec.decodeWS()
+ c := dec.readRune()
switch c {
case ',':
- decodeWS(r)
- expectRune(r, '"')
+ dec.decodeWS()
+ dec.expectRune('"')
goto decodeMember
case '}':
return
default:
- panic(decodeError{fmt.Errorf("expected %c or %c but got %c", ',', '}', c)})
+ dec.panicSyntax(fmt.Errorf("object: expected %q or %q but got %q", ',', '}', c))
}
case '}':
return
default:
- panic(decodeError{fmt.Errorf("expected %c or %c but got %c", '"', '}', c)})
+ dec.panicSyntax(fmt.Errorf("object: expected %q or %q but got %q", '"', '}', c))
}
}
-func decodeArray(r io.RuneScanner, decodeMember func()) {
- expectRune(r, '[')
- decodeWS(r)
- c := readRune(r)
+func (dec *Decoder) decodeArray(decodeMember func()) {
+ dec.expectRune('[')
+ dec.decodeWS()
+ c := dec.readRune()
switch c {
case ']':
return
default:
- unreadRune(r)
+ dec.unreadRune()
decodeNextMember:
decodeMember()
- decodeWS(r)
- c := readRune(r)
+ dec.decodeWS()
+ c := dec.readRune()
switch c {
case ',':
- decodeWS(r)
+ dec.decodeWS()
goto decodeNextMember
case ']':
return
default:
- panic(decodeError{fmt.Errorf("expected %c or %c but got %c", ',', ']', c)})
+ dec.panicSyntax(fmt.Errorf("array: expected %c or %c but got %c", ',', ']', c))
}
}
}
-func decodeHex(r io.RuneReader) rune {
- c := readRune(r)
+func (dec *Decoder) decodeHex() rune {
+ c := dec.readRune()
switch {
case '0' <= c && c <= '9':
return c - '0'
@@ -543,89 +652,91 @@ func decodeHex(r io.RuneReader) rune {
case 'A' <= c && c <= 'F':
return c - 'A' + 10
default:
- panic(decodeError{fmt.Errorf("unexpected %c in unicode literal", c)})
+ dec.panicSyntax(fmt.Errorf("string: expected a hex digit but got %q", c))
+ panic("not reached")
}
}
-func decodeString(r io.RuneScanner, out io.Writer) {
- expectRune(r, '"')
+func (dec *Decoder) decodeString(out io.Writer) {
+ dec.expectRune('"')
for {
- c := readRune(r)
+ c := dec.readRune()
switch {
case 0x0020 <= c && c <= 0x10FFFF && c != '"' && c != '\\':
if _, err := writeRune(out, c); err != nil {
- panic(decodeError{err})
+ dec.panicSyntax(err)
}
case c == '\\':
- c = readRune(r)
+ c = dec.readRune()
switch c {
case '"':
if _, err := writeRune(out, '"'); err != nil {
- panic(decodeError{err})
+ dec.panicSyntax(err)
}
case '\\':
if _, err := writeRune(out, '\\'); err != nil {
- panic(decodeError{err})
+ dec.panicSyntax(err)
}
case 'b':
if _, err := writeRune(out, '\b'); err != nil {
- panic(decodeError{err})
+ dec.panicSyntax(err)
}
case 'f':
if _, err := writeRune(out, '\f'); err != nil {
- panic(decodeError{err})
+ dec.panicSyntax(err)
}
case 'n':
if _, err := writeRune(out, '\n'); err != nil {
- panic(decodeError{err})
+ dec.panicSyntax(err)
}
case 'r':
if _, err := writeRune(out, '\r'); err != nil {
- panic(decodeError{err})
+ dec.panicSyntax(err)
}
case 't':
if _, err := writeRune(out, '\t'); err != nil {
- panic(decodeError{err})
+ dec.panicSyntax(err)
}
case 'u':
- c = decodeHex(r)
- c = (c << 4) | decodeHex(r)
- c = (c << 4) | decodeHex(r)
- c = (c << 4) | decodeHex(r)
+ c = dec.decodeHex()
+ c = (c << 4) | dec.decodeHex()
+ c = (c << 4) | dec.decodeHex()
+ c = (c << 4) | dec.decodeHex()
if _, err := writeRune(out, c); err != nil {
- panic(decodeError{err})
+ dec.panicSyntax(err)
}
}
case c == '"':
return
default:
- panic(decodeError{fmt.Errorf("unexpected %c in string", c)})
+ dec.panicSyntax(fmt.Errorf("string: unexpected %c", c))
}
}
}
-func decodeBool(r io.RuneReader) bool {
- c := readRune(r)
+func (dec *Decoder) decodeBool() bool {
+ c := dec.readRune()
switch c {
case 't':
- expectRune(r, 'r')
- expectRune(r, 'u')
- expectRune(r, 'e')
+ dec.expectRune('r')
+ dec.expectRune('u')
+ dec.expectRune('e')
return true
case 'f':
- expectRune(r, 'a')
- expectRune(r, 'l')
- expectRune(r, 's')
- expectRune(r, 'e')
+ dec.expectRune('a')
+ dec.expectRune('l')
+ dec.expectRune('s')
+ dec.expectRune('e')
return false
default:
- panic(decodeError{fmt.Errorf("unexpected character: %c", c)})
+ dec.panicSyntax(fmt.Errorf("bool: expected %q or %q but got %q", 't', 'f', c))
+ panic("not reached")
}
}
-func decodeNull(r io.RuneReader) {
- expectRune(r, 'n')
- expectRune(r, 'u')
- expectRune(r, 'l')
- expectRune(r, 'l')
+func (dec *Decoder) decodeNull() {
+ dec.expectRune('n')
+ dec.expectRune('u')
+ dec.expectRune('l')
+ dec.expectRune('l')
}
diff --git a/lib/lowmemjson/encode.go b/lib/lowmemjson/encode.go
index c967785..c09fcc1 100644
--- a/lib/lowmemjson/encode.go
+++ b/lib/lowmemjson/encode.go
@@ -16,7 +16,7 @@ import (
"strings"
)
-type Encoder interface {
+type Encodable interface {
EncodeJSON(w io.Writer) error
}
@@ -54,7 +54,7 @@ func Encode(w io.Writer, obj any) (err error) {
}
var (
- encoderType = reflect.TypeOf((*Encoder)(nil)).Elem()
+ encodableType = reflect.TypeOf((*Encodable)(nil)).Elem()
jsonMarshalerType = reflect.TypeOf((*json.Marshaler)(nil)).Elem()
textMarshalerType = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem()
)
@@ -66,15 +66,15 @@ func encode(w io.Writer, val reflect.Value, quote bool) {
}
switch {
- case val.Kind() != reflect.Pointer && val.CanAddr() && reflect.PointerTo(val.Type()).Implements(encoderType):
+ case val.Kind() != reflect.Pointer && val.CanAddr() && reflect.PointerTo(val.Type()).Implements(encodableType):
val = val.Addr()
fallthrough
- case val.Type().Implements(encoderType):
+ case val.Type().Implements(encodableType):
if val.Kind() == reflect.Pointer && val.IsNil() {
encodeWriteString(w, "null")
return
}
- obj, ok := val.Interface().(Encoder)
+ obj, ok := val.Interface().(Encodable)
if !ok {
encodeWriteString(w, "null")
return