Skip to content

Commit

Permalink
cbor: introduce CoerceUndefToNull decode option.
Browse files Browse the repository at this point in the history
By default we will raise an error if we encounter the cbor "undefined"
byte, because it's a long way from a canonical object if it has one of
these bytes in it, and I think we should not be silent about that.
If you want to coerce it to null: that's also an option!

Diff is a bit larger than one might expect it should be because this
turns out to be the first decoding option we've actually passed
through all the way to the decoder.

Signed-off-by: Eric Myhre <hash@exultant.us>
  • Loading branch information
warpfork committed Nov 20, 2018
1 parent ede3ae2 commit bbf0067
Show file tree
Hide file tree
Showing 6 changed files with 34 additions and 24 deletions.
14 changes: 11 additions & 3 deletions cbor/cborDecoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,17 @@ import (
)

type Decoder struct {
r shared.SlickReader
cfg DecodeOptions
r shared.SlickReader

stack []decoderStep // When empty, and step returns done, all done.
step decoderStep // Shortcut to end of stack.
left []int // Statekeeping space for definite-len map and array.
}

func NewDecoder(r io.Reader) (d *Decoder) {
func NewDecoder(cfg DecodeOptions, r io.Reader) (d *Decoder) {
d = &Decoder{
cfg: cfg,
r: shared.NewReader(r),
stack: make([]decoderStep, 0, 10),
left: make([]int, 0, 10),
Expand Down Expand Up @@ -178,9 +180,15 @@ func (d *Decoder) step_acceptMapValue(tokenSlot *Token) (done bool, err error) {

func (d *Decoder) stepHelper_acceptValue(majorByte byte, tokenSlot *Token) (done bool, err error) {
switch majorByte {
case cborSigilNil, cborSigilUndefined:
case cborSigilNil:
tokenSlot.Type = TNull
return true, nil
case cborSigilUndefined:
if d.cfg.CoerceUndefToNull {
tokenSlot.Type = TNull
return true, nil
}
return true, fmt.Errorf("encountered cbor 'undefined' byte (%x) during decoding", cborSigilUndefined)
case cborSigilFalse:
tokenSlot.Type = TBool
tokenSlot.Bool = false
Expand Down
2 changes: 1 addition & 1 deletion cbor/cborFixtures_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ func checkEncoding(t *testing.T, sequence fixtures.Sequence, expectSerial []byte
func checkDecoding(t *testing.T, expectSequence fixtures.Sequence, serial []byte, expectErr error) {
t.Helper()
inputBuf := bytes.NewBuffer(serial)
tokenSrc := NewDecoder(inputBuf)
tokenSrc := NewDecoder(DecodeOptions{}, inputBuf)

// Run steps, advancing until the decoder reports it's done.
// If the decoder keeps yielding more tokens than we expect, that's fine...
Expand Down
16 changes: 8 additions & 8 deletions cbor/cborHelpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,12 +70,12 @@ func NewMarshallerAtlased(wr io.Writer, atl atlas.Atlas) *Marshaller {
return x
}

func Unmarshal(data []byte, v interface{}) error {
return NewUnmarshaller(bytes.NewBuffer(data)).Unmarshal(v)
func Unmarshal(cfg DecodeOptions, data []byte, v interface{}) error {
return NewUnmarshaller(cfg, bytes.NewBuffer(data)).Unmarshal(v)
}

func UnmarshalAtlased(data []byte, v interface{}, atl atlas.Atlas) error {
return NewUnmarshallerAtlased(bytes.NewBuffer(data), atl).Unmarshal(v)
func UnmarshalAtlased(cfg DecodeOptions, data []byte, v interface{}, atl atlas.Atlas) error {
return NewUnmarshallerAtlased(cfg, bytes.NewBuffer(data), atl).Unmarshal(v)
}

type Unmarshaller struct {
Expand All @@ -90,13 +90,13 @@ func (x *Unmarshaller) Unmarshal(v interface{}) error {
return x.pump.Run()
}

func NewUnmarshaller(r io.Reader) *Unmarshaller {
return NewUnmarshallerAtlased(r, atlas.MustBuild())
func NewUnmarshaller(cfg DecodeOptions, r io.Reader) *Unmarshaller {
return NewUnmarshallerAtlased(cfg, r, atlas.MustBuild())
}
func NewUnmarshallerAtlased(r io.Reader, atl atlas.Atlas) *Unmarshaller {
func NewUnmarshallerAtlased(cfg DecodeOptions, r io.Reader, atl atlas.Atlas) *Unmarshaller {
x := &Unmarshaller{
unmarshaller: obj.NewUnmarshaller(atl),
decoder: NewDecoder(r),
decoder: NewDecoder(cfg, r),
}
x.pump = shared.TokenPump{
x.decoder,
Expand Down
2 changes: 2 additions & 0 deletions cbor/cborOptions.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ type EncodeOptions struct {
func (EncodeOptions) IsEncodeOptions() {}

type DecodeOptions struct {
CoerceUndefToNull bool

// future: options to validate canonical serial order
}

Expand Down
8 changes: 4 additions & 4 deletions cmd/refmt/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ func Main(args []string, stdin io.Reader, stdout, stderr io.Writer) int {
Usage: "read cbor, then pretty print it",
Action: func(c *cli.Context) error {
return shared.TokenPump{
cbor.NewDecoder(stdin),
cbor.NewDecoder(cbor.DecodeOptions{}, stdin),
pretty.NewEncoder(stdout),
}.Run()
},
Expand All @@ -55,7 +55,7 @@ func Main(args []string, stdin io.Reader, stdout, stderr io.Writer) int {
Usage: "read cbor in hex, then pretty print it",
Action: func(c *cli.Context) error {
return shared.TokenPump{
cbor.NewDecoder(hexReader(stdin)),
cbor.NewDecoder(cbor.DecodeOptions{}, hexReader(stdin)),
pretty.NewEncoder(stdout),
}.Run()
},
Expand Down Expand Up @@ -102,7 +102,7 @@ func Main(args []string, stdin io.Reader, stdout, stderr io.Writer) int {
Usage: "read cbor, emit equivalent json",
Action: func(c *cli.Context) error {
return shared.TokenPump{
cbor.NewDecoder(stdin),
cbor.NewDecoder(cbor.DecodeOptions{}, stdin),
json.NewEncoder(stdout, json.EncodeOptions{}),
}.Run()
},
Expand All @@ -113,7 +113,7 @@ func Main(args []string, stdin io.Reader, stdout, stderr io.Writer) int {
Usage: "read cbor in hex, emit equivalent json",
Action: func(c *cli.Context) error {
return shared.TokenPump{
cbor.NewDecoder(hexReader(stdin)),
cbor.NewDecoder(cbor.DecodeOptions{}, hexReader(stdin)),
json.NewEncoder(stdout, json.EncodeOptions{}),
}.Run()
},
Expand Down
16 changes: 8 additions & 8 deletions unmarshalHelpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,22 @@ type DecodeOptions interface {
}

func Unmarshal(opts DecodeOptions, data []byte, v interface{}) error {
switch opts.(type) {
switch o2 := opts.(type) {
case json.DecodeOptions:
return json.Unmarshal(data, v)
case cbor.DecodeOptions:
return cbor.Unmarshal(data, v)
return cbor.Unmarshal(o2, data, v)
default:
panic("incorrect usage: unknown DecodeOptions type")
}
}

func UnmarshalAtlased(opts DecodeOptions, data []byte, v interface{}, atl atlas.Atlas) error {
switch opts.(type) {
switch o2 := opts.(type) {
case json.DecodeOptions:
return json.UnmarshalAtlased(data, v, atl)
case cbor.DecodeOptions:
return cbor.UnmarshalAtlased(data, v, atl)
return cbor.UnmarshalAtlased(o2, data, v, atl)
default:
panic("incorrect usage: unknown DecodeOptions type")
}
Expand All @@ -39,22 +39,22 @@ type Unmarshaller interface {
}

func NewUnmarshaller(opts DecodeOptions, r io.Reader) Unmarshaller {
switch opts.(type) {
switch o2 := opts.(type) {
case json.DecodeOptions:
return json.NewUnmarshaller(r)
case cbor.DecodeOptions:
return cbor.NewUnmarshaller(r)
return cbor.NewUnmarshaller(o2, r)
default:
panic("incorrect usage: unknown DecodeOptions type")
}
}

func NewUnmarshallerAtlased(opts DecodeOptions, r io.Reader, atl atlas.Atlas) Unmarshaller {
switch opts.(type) {
switch o2 := opts.(type) {
case json.DecodeOptions:
return json.NewUnmarshallerAtlased(r, atl)
case cbor.DecodeOptions:
return cbor.NewUnmarshallerAtlased(r, atl)
return cbor.NewUnmarshallerAtlased(o2, r, atl)
default:
panic("incorrect usage: unknown DecodeOptions type")
}
Expand Down

0 comments on commit bbf0067

Please sign in to comment.