Update
This commit is contained in:
1 parent
3530afb854
commit
6903fcff27
2146 files changed
+112256
-190703
No files matched your search
+140
-39
@@ -6,6 +6,7 @@ package cbor
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
)
|
||||
@@ -157,11 +158,32 @@ func (dec *Decoder) overwriteBuf(newBuf []byte) {
|
||||
dec.off = 0
|
||||
}
|
||||
|
||||
// indefDataItem tracks open indefinite-length data item during encoding.
|
||||
// typ is the CBOR type of the indefinite-length data item.
|
||||
// count is the number of items written so far.
|
||||
// For indefinite-length maps, count must be even (key/value pairs) when
|
||||
// the container is closed.
|
||||
type indefDataItem struct {
|
||||
typ cborType
|
||||
count int
|
||||
}
|
||||
|
||||
// IndefiniteLengthMapOddItemCountError indicates that EndIndefinite was
|
||||
// called on an open indefinite-length map with an odd number of items.
|
||||
type IndefiniteLengthMapOddItemCountError struct {
|
||||
count int
|
||||
}
|
||||
|
||||
func (e *IndefiniteLengthMapOddItemCountError) Error() string {
|
||||
return fmt.Sprintf("cbor: cannot end indefinite-length map with %d item(s)", e.count)
|
||||
}
|
||||
|
||||
// Encoder writes CBOR values to io.Writer.
|
||||
type Encoder struct {
|
||||
w io.Writer
|
||||
em *encMode
|
||||
indefTypes []cborType
|
||||
w io.Writer
|
||||
em *encMode
|
||||
indefs []indefDataItem
|
||||
scratch [1]byte // reused for single-byte writes (indefinite-length head and break code)
|
||||
}
|
||||
|
||||
// NewEncoder returns a new encoder that writes to w using the default encoding options.
|
||||
@@ -171,89 +193,166 @@ func NewEncoder(w io.Writer) *Encoder {
|
||||
|
||||
// Encode writes the CBOR encoding of v.
|
||||
func (enc *Encoder) Encode(v any) error {
|
||||
if len(enc.indefTypes) > 0 && v != nil {
|
||||
indefType := enc.indefTypes[len(enc.indefTypes)-1]
|
||||
if indefType == cborTypeTextString {
|
||||
k := reflect.TypeOf(v).Kind()
|
||||
if k != reflect.String {
|
||||
return errors.New("cbor: cannot encode item type " + k.String() + " for indefinite-length text string")
|
||||
}
|
||||
} else if indefType == cborTypeByteString {
|
||||
t := reflect.TypeOf(v)
|
||||
k := t.Kind()
|
||||
if (k != reflect.Array && k != reflect.Slice) || t.Elem().Kind() != reflect.Uint8 {
|
||||
return errors.New("cbor: cannot encode item type " + k.String() + " for indefinite-length byte string")
|
||||
}
|
||||
if len(enc.indefs) > 0 {
|
||||
err := validateIndefiniteLengthChunkByType(enc.indefs[len(enc.indefs)-1].typ, v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
buf := getEncodeBuffer()
|
||||
|
||||
err := encode(buf, enc.em, reflect.ValueOf(v))
|
||||
|
||||
// Validate the encoded chunk against the indefinite-length data item using a byte-based check.
|
||||
// This reliably detects chunks from cbor.Marshaler, registered tags, StringToByteString, etc.,
|
||||
// which may produce a chunk inconsistent with the parent's major type.
|
||||
// Applies only to indefinite-length byte/text string parents (RFC 8949 Section 3.2.3).
|
||||
if err == nil && len(enc.indefs) > 0 {
|
||||
err = validateIndefiniteLengthChunkByData(enc.indefs[len(enc.indefs)-1].typ, buf.Bytes(), v)
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
_, err = enc.w.Write(buf.Bytes())
|
||||
}
|
||||
|
||||
putEncodeBuffer(buf)
|
||||
return err
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(enc.indefs) > 0 {
|
||||
enc.indefs[len(enc.indefs)-1].count++
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// StartIndefiniteByteString starts byte string encoding of indefinite length.
|
||||
// StartIndefiniteByteString starts indefinite-length byte string encoding.
|
||||
// Subsequent calls of (*Encoder).Encode() encodes definite length byte strings
|
||||
// ("chunks") as one contiguous string until EndIndefinite is called.
|
||||
func (enc *Encoder) StartIndefiniteByteString() error {
|
||||
return enc.startIndefinite(cborTypeByteString)
|
||||
}
|
||||
|
||||
// StartIndefiniteTextString starts text string encoding of indefinite length.
|
||||
// StartIndefiniteTextString starts indefinite-length text string encoding.
|
||||
// Subsequent calls of (*Encoder).Encode() encodes definite length text strings
|
||||
// ("chunks") as one contiguous string until EndIndefinite is called.
|
||||
func (enc *Encoder) StartIndefiniteTextString() error {
|
||||
return enc.startIndefinite(cborTypeTextString)
|
||||
}
|
||||
|
||||
// StartIndefiniteArray starts array encoding of indefinite length.
|
||||
// StartIndefiniteArray starts indefinite-length array encoding.
|
||||
// Subsequent calls of (*Encoder).Encode() encodes elements of the array
|
||||
// until EndIndefinite is called.
|
||||
func (enc *Encoder) StartIndefiniteArray() error {
|
||||
return enc.startIndefinite(cborTypeArray)
|
||||
}
|
||||
|
||||
// StartIndefiniteMap starts array encoding of indefinite length.
|
||||
// StartIndefiniteMap starts indefinite-length map encoding.
|
||||
// Subsequent calls of (*Encoder).Encode() encodes elements of the map
|
||||
// until EndIndefinite is called.
|
||||
func (enc *Encoder) StartIndefiniteMap() error {
|
||||
return enc.startIndefinite(cborTypeMap)
|
||||
}
|
||||
|
||||
// EndIndefinite closes last opened indefinite length value.
|
||||
// EndIndefinite closes last opened indefinite-length value.
|
||||
// It returns *IndefiniteLengthMapOddItemCountError without writing the
|
||||
// "break" code if the open indefinite-length map has an odd number of
|
||||
// items; the encoder state is unchanged so the caller can write the
|
||||
// missing value and retry.
|
||||
func (enc *Encoder) EndIndefinite() error {
|
||||
if len(enc.indefTypes) == 0 {
|
||||
if len(enc.indefs) == 0 {
|
||||
return errors.New("cbor: cannot encode \"break\" code outside indefinite length values")
|
||||
}
|
||||
_, err := enc.w.Write([]byte{cborBreakFlag})
|
||||
if err == nil {
|
||||
enc.indefTypes = enc.indefTypes[:len(enc.indefTypes)-1]
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
var cborIndefHeader = map[cborType][]byte{
|
||||
cborTypeByteString: {cborByteStringWithIndefiniteLengthHead},
|
||||
cborTypeTextString: {cborTextStringWithIndefiniteLengthHead},
|
||||
cborTypeArray: {cborArrayWithIndefiniteLengthHead},
|
||||
cborTypeMap: {cborMapWithIndefiniteLengthHead},
|
||||
// Verify that indefinite-length map has even number of elements
|
||||
top := enc.indefs[len(enc.indefs)-1]
|
||||
if top.typ == cborTypeMap && top.count%2 != 0 {
|
||||
return &IndefiniteLengthMapOddItemCountError{count: top.count}
|
||||
}
|
||||
|
||||
// Write break code
|
||||
enc.scratch[0] = cborBreakFlag
|
||||
_, err := enc.w.Write(enc.scratch[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
enc.indefs = enc.indefs[:len(enc.indefs)-1]
|
||||
|
||||
// Increment parent container's item count because the child
|
||||
// (indefinite-length data item) is fully written to the stream.
|
||||
if len(enc.indefs) > 0 {
|
||||
enc.indefs[len(enc.indefs)-1].count++
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (enc *Encoder) startIndefinite(typ cborType) error {
|
||||
if enc.em.indefLength == IndefLengthForbidden {
|
||||
return &IndefiniteLengthError{typ}
|
||||
}
|
||||
_, err := enc.w.Write(cborIndefHeader[typ])
|
||||
if err == nil {
|
||||
enc.indefTypes = append(enc.indefTypes, typ)
|
||||
|
||||
// Verify that new indefinite-length data item is not a chunk in indefinite-length byte/text string.
|
||||
if len(enc.indefs) > 0 {
|
||||
parent := enc.indefs[len(enc.indefs)-1].typ
|
||||
if parent == cborTypeByteString || parent == cborTypeTextString {
|
||||
return errors.New("cbor: cannot encode indefinite-length " + typ.String() +
|
||||
" as chunk of indefinite-length " + parent.String())
|
||||
}
|
||||
}
|
||||
return err
|
||||
|
||||
// Write indefinite-length head.
|
||||
enc.scratch[0] = byte(typ) | additionalInformationAsIndefiniteLengthFlag
|
||||
_, err := enc.w.Write(enc.scratch[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
enc.indefs = append(enc.indefs, indefDataItem{typ: typ})
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateIndefiniteLengthChunkByType rejects chunks based solely on their Go type.
|
||||
func validateIndefiniteLengthChunkByType(indefiniteLengthCborType cborType, v any) error {
|
||||
if indefiniteLengthCborType != cborTypeByteString &&
|
||||
indefiniteLengthCborType != cborTypeTextString {
|
||||
return nil
|
||||
}
|
||||
if v == nil {
|
||||
return errors.New("cbor: cannot encode nil for indefinite-length " + indefiniteLengthCborType.String())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateIndefiniteLengthChunkByData checks that chunk is a definite-length
|
||||
// CBOR data item with a matching major type.
|
||||
// No-op for indefinite-length array/map, where any data item is valid.
|
||||
func validateIndefiniteLengthChunkByData(indefiniteLengthCborType cborType, chunk []byte, v any) error {
|
||||
if indefiniteLengthCborType != cborTypeByteString &&
|
||||
indefiniteLengthCborType != cborTypeTextString {
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(chunk) == 0 {
|
||||
return errors.New("cbor: cannot encode item type " + reflect.TypeOf(v).Kind().String() +
|
||||
" for indefinite-length " + indefiniteLengthCborType.String())
|
||||
}
|
||||
|
||||
t, ai := parseInitialByte(chunk[0])
|
||||
if t != indefiniteLengthCborType {
|
||||
return errors.New("cbor: cannot encode item type " + reflect.TypeOf(v).Kind().String() +
|
||||
" for indefinite-length " + indefiniteLengthCborType.String())
|
||||
}
|
||||
|
||||
if ai == additionalInformationAsIndefiniteLengthFlag {
|
||||
return errors.New("cbor: cannot encode indefinite-length " + indefiniteLengthCborType.String() +
|
||||
" as chunk of indefinite-length " + indefiniteLengthCborType.String())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RawMessage is a raw encoded CBOR value.
|
||||
@@ -262,7 +361,9 @@ type RawMessage []byte
|
||||
// MarshalCBOR returns m or CBOR nil if m is nil.
|
||||
func (m RawMessage) MarshalCBOR() ([]byte, error) {
|
||||
if len(m) == 0 {
|
||||
return cborNil, nil
|
||||
b := make([]byte, len(cborNil))
|
||||
copy(b, cborNil)
|
||||
return b, nil
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user