diff options
author | Luke Shumaker <lukeshu@lukeshu.com> | 2023-01-25 21:05:17 -0700 |
---|---|---|
committer | Luke Shumaker <lukeshu@lukeshu.com> | 2023-01-26 00:45:27 -0700 |
commit | ffee5c8516f3f55f82ed5bb8f0a4f340d485fa92 (patch) | |
tree | 0c10526b1ea57b043230402e9378b341c6966965 /decode.go | |
parent | 4148776399cb7ea5e10c74dc465e4e1e682cb399 (diff) |
Write documentationv0.2.0
Diffstat (limited to 'decode.go')
-rw-r--r-- | decode.go | 115 |
1 files changed, 109 insertions, 6 deletions
@@ -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 { |