diff --git a/cbor/cborDecoder.go b/cbor/cborDecoder.go index 8cc14ff..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), @@ -181,6 +183,12 @@ func (d *Decoder) stepHelper_acceptValue(majorByte byte, tokenSlot *Token) (done 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") }