summaryrefslogtreecommitdiff
path: root/decode.go
diff options
context:
space:
mode:
authorLuke Shumaker <lukeshu@lukeshu.com>2023-01-25 21:05:17 -0700
committerLuke Shumaker <lukeshu@lukeshu.com>2023-01-26 00:45:27 -0700
commitffee5c8516f3f55f82ed5bb8f0a4f340d485fa92 (patch)
tree0c10526b1ea57b043230402e9378b341c6966965 /decode.go
parent4148776399cb7ea5e10c74dc465e4e1e682cb399 (diff)
Write documentationv0.2.0
Diffstat (limited to 'decode.go')
-rw-r--r--decode.go115
1 files changed, 109 insertions, 6 deletions
diff --git a/decode.go b/decode.go
index 51c1ed5..f911ac3 100644
--- a/decode.go
+++ b/decode.go
@@ -1,6 +1,13 @@
// Copyright (C) 2022-2023 Luke Shumaker <lukeshu@lukeshu.com>
//
// SPDX-License-Identifier: GPL-2.0-or-later
+//
+// Some doc comments are
+// copyright 2010 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+//
+// SPDX-License-Identifier: BSD-3-Clause
package lowmemjson
@@ -19,6 +26,26 @@ import (
"git.lukeshu.com/go/lowmemjson/internal"
)
+// Decodable is the interface implemented by types that can decode a
+// JSON representation of themselves. Decodable is a
+// low-memory-overhead replacement for the json.Unmarshaler interface.
+//
+// The io.RuneScanner passed to DecodeJSON...
+//
+// - ...will return ErrInvalidUnreadRune .UnreadRune if the last
+// operation was not a successful .ReadRune() call.
+//
+// - ...will return EOF at the end of the JSON value; it is not
+// possible for DecodeJSON to read past the end of the value in to
+// another value.
+//
+// - ...if invalid JSON is encountered, will return the invalid rune
+// with err!=nil. Implementations are encouraged to simply
+// `return err` if .ReadRune returns an error.
+//
+// DecodeJSON is expected to consume the entire scanner until io.EOF
+// or another is encountered; if it does not, then the parent Decode
+// call will return a *DecodeTypeError.
type Decodable interface {
DecodeJSON(io.RuneScanner) error
}
@@ -28,6 +55,26 @@ type decodeStackItem struct {
idx any
}
+// A Decoder reads and decodes values from an input stream of JSON
+// elements.
+//
+// Decoder is analogous to, and has a similar API to the standard
+// library's encoding/json.Decoder. Differences are:
+//
+// - lowmemjson.NewDecoder takes an io.RuneScanner, while
+// json.NewDecoder takes an io.Reader.
+//
+// - lowmemjson.Decoder does not have a .Buffered() method, while
+// json.Decoder does.
+//
+// - lowmemjson.Decoder does not have a .Token() method, while
+// json.Decoder does.
+//
+// If something more similar to a json.Decoder is desired,
+// lowmemjson/compat/json.NewDecoder takes an io.Reader (and turns it
+// into an io.RuneScanner by wrapping it in a bufio.Reader), and
+// lowmemjson/compat/json.Decoder has a .Buffered() method; though
+// lowmemjson/compat/json.Decoder also lacks the .Token() method.
type Decoder struct {
io runeTypeScanner
@@ -42,6 +89,11 @@ type Decoder struct {
const maxNestingDepth = 10000
+// NewDecoder returns a new Decoder that reads from r.
+//
+// NewDecoder is analogous to the standard library's
+// encoding/json.NewDecoder, but takes an io.RuneScanner rather than
+// an io.Reader.
func NewDecoder(r io.RuneScanner) *Decoder {
return &Decoder{
io: &noWSRuneTypeScanner{
@@ -55,10 +107,35 @@ func NewDecoder(r io.RuneScanner) *Decoder {
}
}
+// DisallowUnknownFields causes the Decoder to return an error when
+// the destination is a struct and the input contains object keys
+// which do not match any non-ignored, exported fields in the
+// destination.
+//
+// This is identical to the standard library's
+// encoding/json.Decoder.DisallowUnknownFields.
func (dec *Decoder) DisallowUnknownFields() { dec.disallowUnknownFields = true }
-func (dec *Decoder) UseNumber() { dec.useNumber = true }
-func (dec *Decoder) InputOffset() int64 { return dec.io.InputOffset() }
+// UseNumber causes the Decoder to unmarshal a number into an
+// interface{} as a Number instead of as a float64.
+//
+// This is identical to the standard library's
+// encoding/json.Decoder.UseNumber.
+func (dec *Decoder) UseNumber() { dec.useNumber = true }
+
+// InputOffset returns the input stream byte offset of the current
+// decoder position. The offset gives the location of the rune that
+// will be returned from the next call to .ReadRune().
+//
+// This is identical to the standard library's
+// encoding/json.Decoder.InputOffset.
+func (dec *Decoder) InputOffset() int64 { return dec.io.InputOffset() }
+
+// More reports whether there is more to the stream of JSON elements,
+// or if the Decoder has reached EOF or an error.
+//
+// More is identical to the standard library's
+// encoding/json.Decoder.More.
func (dec *Decoder) More() bool {
dec.io.Reset()
_, _, t, e := dec.io.ReadRuneType()
@@ -105,8 +182,10 @@ func (dec *Decoder) stackName() string {
return strings.Join(fields, ".")
}
-// DecodeThenEOF is like decode, but emits an error if there is extra
-// data after the JSON.
+// DecodeThenEOF is like Decode, but emits an error if there is extra
+// data after the JSON. A JSON document is specified to be a single
+// JSON element; repeated calls to Decoder.Decode will happily decode
+// a stream of multiple JSON elements.
func (dec *Decoder) DecodeThenEOF(ptr any) (err error) {
if err := dec.Decode(ptr); err != nil {
return err
@@ -126,6 +205,16 @@ func (dec *Decoder) DecodeThenEOF(ptr any) (err error) {
return nil
}
+// Decode reads the next JSON element from the Decoder's input stream
+// and stores it in the value pointed to by ptr.
+//
+// See the [documentation for encoding/json.Unmarshal] for details
+// about the conversion of JSON into a Go value; Decode behaves
+// identically to that, with the exception that in addition to the
+// json.Unmarshaler interface it also checks for the Decodable
+// interface.
+//
+// [documentation for encoding/json.Unmarshal]: https://pkg.go.dev/encoding/json@go1.18#Unmarshal
func (dec *Decoder) Decode(ptr any) (err error) {
ptrVal := reflect.ValueOf(ptr)
if ptrVal.Kind() != reflect.Pointer || ptrVal.IsNil() || !ptrVal.Elem().CanSet() {
@@ -721,7 +810,14 @@ func (dec *Decoder) decodeAny() any {
}
}
-// DecodeObject is a helper function for implementing the Decoder interface.
+// DecodeObject is a helper function to ease implementing the
+// Decodable interface; allowing the lowmemjson package to handle
+// decoding the object syntax, while the Decodable only needs to
+// handle decoding the keys and values within the object.
+//
+// Outside of implementing Decodable.DecodeJSON methods, callers
+// should instead simply use NewDecoder(r).Decode(&val) rather than
+// attempting to call DecodeObject directly.
func DecodeObject(r io.RuneScanner, decodeKey, decodeVal func(io.RuneScanner) error) (err error) {
defer func() {
if r := recover(); r != nil {
@@ -784,7 +880,14 @@ func (dec *Decoder) decodeObject(gTyp reflect.Type, decodeKey, decodeVal func())
}
}
-// DecodeArray is a helper function for implementing the Decoder interface.
+// DecodeArray is a helper function to ease implementing the Decoder
+// interface; allowing the lowmemjson package to handle decoding the
+// array syntax, while the Decodable only needs to handle decoding
+// members within the array.
+//
+// Outside of implementing Decodable.DecodeJSON methods, callers
+// should instead simply use NewDecoder(r).Decode(&val) rather than
+// attempting to call DecodeArray directly.
func DecodeArray(r io.RuneScanner, decodeMember func(r io.RuneScanner) error) (err error) {
defer func() {
if r := recover(); r != nil {