From 6ba16f05e9c36d4341da4590600eb2c4221ac642 Mon Sep 17 00:00:00 2001 From: Luke Shumaker Date: Sun, 21 Aug 2022 12:06:50 -0600 Subject: decode: Lean in to io.RuneScanner So that it's easier to compose your own .DecodeJSON methods, since .DecodeJSON takes an io.RuneScanner. --- decode.go | 19 ++++++------------- decode_scan.go | 10 +++------- decode_scan_test.go | 10 +++++----- 3 files changed, 14 insertions(+), 25 deletions(-) diff --git a/decode.go b/decode.go index 51402e7..98a0e5b 100644 --- a/decode.go +++ b/decode.go @@ -5,7 +5,6 @@ package lowmemjson import ( - "bufio" "bytes" "encoding" "encoding/json" @@ -47,15 +46,11 @@ type Decoder struct { const maxNestingDepth = 10000 -func NewDecoder(r io.Reader) *Decoder { - rr, ok := r.(io.RuneReader) - if !ok { - rr = bufio.NewReader(r) - } +func NewDecoder(r io.RuneScanner) *Decoder { return &Decoder{ io: &noWSRuneTypeScanner{ inner: &runeTypeScannerImpl{ - inner: rr, + inner: r, parser: Parser{ MaxDepth: maxNestingDepth, }, @@ -114,18 +109,16 @@ func (dec *Decoder) stackName() string { return strings.Join(fields, ".") } -func Decode(r interface { - io.Reader - io.RuneScanner // enforce that the reader have .UnreadRune() so that we don't risk reading too far when decoding a number -}, ptr any) error { +func Decode(r io.RuneScanner, ptr any) error { return NewDecoder(r).Decode(ptr) } -// DecodeThenEOF is like decode, but emits an error if there is extra data after the JSON. -func DecodeThenEOF(r io.Reader, ptr any) error { +func DecodeThenEOF(r io.RuneScanner, ptr any) error { return NewDecoder(r).DecodeThenEOF(ptr) } +// DecodeThenEOF is like decode, but emits an error if there is extra +// data after the JSON. func (dec *Decoder) DecodeThenEOF(ptr any) (err error) { if err := dec.Decode(ptr); err != nil { return err diff --git a/decode_scan.go b/decode_scan.go index fb8a6ec..eee61fc 100644 --- a/decode_scan.go +++ b/decode_scan.go @@ -28,7 +28,7 @@ type runeTypeScanner interface { // runeTypeScannerImpl ///////////////////////////////////////////////////////////////////////////// type runeTypeScannerImpl struct { - inner io.RuneReader + inner io.RuneScanner initialized bool @@ -73,9 +73,7 @@ func (sc *runeTypeScannerImpl) ReadRuneType() (rune, int, RuneType, error) { case sc.stuck: // do nothing case sc.repeat: - if _, ok := sc.inner.(io.RuneScanner); ok { - _, _, _ = sc.inner.ReadRune() - } + _, _, _ = sc.inner.ReadRune() default: var err error sc.rRune, sc.rSize, err = sc.inner.ReadRune() @@ -138,9 +136,7 @@ func (sc *runeTypeScannerImpl) UnreadRune() error { return ErrInvalidUnreadRune } sc.repeat = true - if rs, ok := sc.inner.(io.RuneScanner); ok { - _ = rs.UnreadRune() - } + _ = sc.inner.UnreadRune() return nil } diff --git a/decode_scan_test.go b/decode_scan_test.go index 27b60c0..70e2874 100644 --- a/decode_scan_test.go +++ b/decode_scan_test.go @@ -31,7 +31,7 @@ type runeTypeScannerTestcase struct { Exp []ReadRuneTypeResult } -func testRuneTypeScanner(t *testing.T, testcases map[string]runeTypeScannerTestcase, factory func(io.RuneReader) runeTypeScanner) { +func testRuneTypeScanner(t *testing.T, testcases map[string]runeTypeScannerTestcase, factory func(io.RuneScanner) runeTypeScanner) { for tcName, tc := range testcases { t.Run(tcName, func(t *testing.T) { reader := strings.NewReader(tc.Input) @@ -158,7 +158,7 @@ func TestRuneTypeScanner(t *testing.T) { {0, 0, RuneTypeError, &DecodeSyntaxError{Offset: 0, Err: io.EOF}}, }}, } - testRuneTypeScanner(t, testcases, func(reader io.RuneReader) runeTypeScanner { + testRuneTypeScanner(t, testcases, func(reader io.RuneScanner) runeTypeScanner { return &runeTypeScannerImpl{ inner: reader, } @@ -230,7 +230,7 @@ func TestNoWSRuneTypeScanner(t *testing.T) { {0, 0, RuneTypeError, &DecodeSyntaxError{Offset: 2, Err: io.ErrUnexpectedEOF}}, }}, } - testRuneTypeScanner(t, testcases, func(reader io.RuneReader) runeTypeScanner { + testRuneTypeScanner(t, testcases, func(reader io.RuneScanner) runeTypeScanner { return &noWSRuneTypeScanner{ inner: &runeTypeScannerImpl{ inner: reader, @@ -276,7 +276,7 @@ func TestElemRuneTypeScanner(t *testing.T) { }}, } t.Run("top-level", func(t *testing.T) { - testRuneTypeScanner(t, testcases, func(reader io.RuneReader) runeTypeScanner { + testRuneTypeScanner(t, testcases, func(reader io.RuneScanner) runeTypeScanner { return &elemRuneTypeScanner{ inner: &noWSRuneTypeScanner{ inner: &runeTypeScannerImpl{ @@ -297,7 +297,7 @@ func TestElemRuneTypeScanner(t *testing.T) { testcases[tcName] = tc } t.Run("child", func(t *testing.T) { - testRuneTypeScanner(t, testcases, func(reader io.RuneReader) runeTypeScanner { + testRuneTypeScanner(t, testcases, func(reader io.RuneScanner) runeTypeScanner { inner := &noWSRuneTypeScanner{ inner: &runeTypeScannerImpl{ inner: reader, -- cgit v1.2.3-54-g00ecf