From ede3ae2ed87accd6cd6e6237725a201333a2c546 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 14 Nov 2018 13:08:58 -0800 Subject: [PATCH 1/2] decode CBOR's undefined to null Bleh. We're not going to round-trip this but we shouldn't hit this anyways. Alternatively, we could just return an error. However, that may break things for some users... fixes #43 --- cbor/cborDecoder.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cbor/cborDecoder.go b/cbor/cborDecoder.go index 8cc14ff..463fef8 100644 --- a/cbor/cborDecoder.go +++ b/cbor/cborDecoder.go @@ -178,7 +178,7 @@ 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: + case cborSigilNil, cborSigilUndefined: tokenSlot.Type = TNull return true, nil case cborSigilFalse: From bbf0067358e2b4cc204f08a6409cf3975d68744f Mon Sep 17 00:00:00 2001 From: Eric Myhre Date: Tue, 20 Nov 2018 10:23:37 -0600 Subject: [PATCH 2/2] cbor: introduce CoerceUndefToNull decode option. 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 --- cbor/cborDecoder.go | 14 +++++++++++--- cbor/cborFixtures_test.go | 2 +- cbor/cborHelpers.go | 16 ++++++++-------- cbor/cborOptions.go | 2 ++ cmd/refmt/main.go | 8 ++++---- unmarshalHelpers.go | 16 ++++++++-------- 6 files changed, 34 insertions(+), 24 deletions(-) diff --git a/cbor/cborDecoder.go b/cbor/cborDecoder.go index 463fef8..e70b1a8 100644 --- a/cbor/cborDecoder.go +++ b/cbor/cborDecoder.go @@ -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), @@ -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 diff --git a/cbor/cborFixtures_test.go b/cbor/cborFixtures_test.go index ebe20f4..7221a19 100644 --- a/cbor/cborFixtures_test.go +++ b/cbor/cborFixtures_test.go @@ -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... diff --git a/cbor/cborHelpers.go b/cbor/cborHelpers.go index 1cbdcc3..f7c4b77 100644 --- a/cbor/cborHelpers.go +++ b/cbor/cborHelpers.go @@ -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 { @@ -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, diff --git a/cbor/cborOptions.go b/cbor/cborOptions.go index 6aa6a71..69ccd68 100644 --- a/cbor/cborOptions.go +++ b/cbor/cborOptions.go @@ -10,6 +10,8 @@ type EncodeOptions struct { func (EncodeOptions) IsEncodeOptions() {} type DecodeOptions struct { + CoerceUndefToNull bool + // future: options to validate canonical serial order } diff --git a/cmd/refmt/main.go b/cmd/refmt/main.go index 22d765e..a18effe 100644 --- a/cmd/refmt/main.go +++ b/cmd/refmt/main.go @@ -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() }, @@ -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() }, @@ -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() }, @@ -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() }, diff --git a/unmarshalHelpers.go b/unmarshalHelpers.go index b575ba9..3ebf9ef 100644 --- a/unmarshalHelpers.go +++ b/unmarshalHelpers.go @@ -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") } @@ -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") }