From 06d7be736ac93cd548db84d7f898d6ef0b257c1f Mon Sep 17 00:00:00 2001 From: Luke Shumaker Date: Mon, 1 Aug 2022 23:59:18 -0600 Subject: overhaul decoder for better error messages --- lib/lowmemjson/adapter_test.go | 31 +-- lib/lowmemjson/decode.go | 509 +++++++++++++++++++++++++---------------- lib/lowmemjson/encode.go | 10 +- 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 -- cgit v1.2.3-2-g168b