Refactor CBOR head funcs to remove magic numbers

This commit is contained in:
Faye Amacker 2024-05-27 16:06:38 -05:00
parent 03575b4950
commit b75278bc2e
5 changed files with 117 additions and 55 deletions

View File

@ -276,7 +276,8 @@ func getEncodingStructType(t reflect.Type) (*encodingStructType, error) {
copy(flds[i].cborNameByteString, flds[i].cborName)
// Reset encoded CBOR type to byte string, preserving the "additional
// information" bits:
flds[i].cborNameByteString[0] = byte(cborTypeByteString) | (flds[i].cborNameByteString[0] & 0x1f)
flds[i].cborNameByteString[0] = byte(cborTypeByteString) |
getAdditionalInformation(flds[i].cborNameByteString[0])
hasKeyAsStr = true
}

View File

@ -1311,6 +1311,39 @@ func (t cborType) String() string {
}
}
const (
// From RFC 8949.3:
// The initial byte of each encoded data item contains both information about the major type
// (the high-order 3 bits, described in Section 3.1) and additional information
// (the low-order 5 bits).
// typeMask is used to extract major type in initial byte of encoded data item.
typeMask = 0xe0
// additionalInformationMask is used to extract additional information in initial byte of encoded data item.
additionalInformationMask = 0x1f
)
func getType(b byte) cborType {
return cborType(b & typeMask)
}
func getAdditionalInformation(b byte) byte {
return b & additionalInformationMask
}
func parseInitialByte(b byte) (t cborType, ai byte) {
return getType(b), getAdditionalInformation(b)
}
const (
maxAdditionalInformationWithoutArgument = 23
additionalInformationWith1ByteArgument = 24
additionalInformationWith2ByteArgument = 25
additionalInformationWith4ByteArgument = 26
additionalInformationWith8ByteArgument = 27
)
const (
selfDescribedCBORTagNum = 55799
expectedLaterEncodingBase64URLTagNum = 21
@ -2791,32 +2824,38 @@ func (d *decoder) skip() {
// getHead assumes data is well-formed, and does not perform bounds checking.
func (d *decoder) getHead() (t cborType, ai byte, val uint64) {
t = cborType(d.data[d.off] & 0xe0)
ai = d.data[d.off] & 0x1f
t, ai = parseInitialByte(d.data[d.off])
val = uint64(ai)
d.off++
if ai < 24 {
if ai <= maxAdditionalInformationWithoutArgument {
return
}
if ai == 24 {
if ai == additionalInformationWith1ByteArgument {
val = uint64(d.data[d.off])
d.off++
return
}
if ai == 25 {
val = uint64(binary.BigEndian.Uint16(d.data[d.off : d.off+2]))
d.off += 2
if ai == additionalInformationWith2ByteArgument {
const argumentSize = 2
val = uint64(binary.BigEndian.Uint16(d.data[d.off : d.off+argumentSize]))
d.off += argumentSize
return
}
if ai == 26 {
val = uint64(binary.BigEndian.Uint32(d.data[d.off : d.off+4]))
d.off += 4
if ai == additionalInformationWith4ByteArgument {
const argumentSize = 4
val = uint64(binary.BigEndian.Uint32(d.data[d.off : d.off+argumentSize]))
d.off += argumentSize
return
}
if ai == 27 {
val = binary.BigEndian.Uint64(d.data[d.off : d.off+8])
d.off += 8
if ai == additionalInformationWith8ByteArgument {
const argumentSize = 8
val = binary.BigEndian.Uint64(d.data[d.off : d.off+argumentSize])
d.off += argumentSize
return
}
return
@ -2849,7 +2888,7 @@ func (d *decoder) reset(data []byte) {
}
func (d *decoder) nextCBORType() cborType {
return cborType(d.data[d.off] & 0xe0)
return getType(d.data[d.off])
}
func (d *decoder) nextCBORNil() bool {
@ -3069,7 +3108,7 @@ func convertByteSliceToByteString(v interface{}) (interface{}, bool) {
// validBuiltinTag checks that supported built-in tag numbers are followed by expected content types.
func validBuiltinTag(tagNum uint64, contentHead byte) error {
t := cborType(contentHead & 0xe0)
t := getType(contentHead)
switch tagNum {
case 0:
// Tag content (date/time text string in RFC 3339 format) must be string type.

View File

@ -1642,34 +1642,46 @@ func encodeTag(e *bytes.Buffer, em *encMode, v reflect.Value) error {
// encodeHead writes CBOR head of specified type t and returns number of bytes written.
func encodeHead(e *bytes.Buffer, t byte, n uint64) int {
if n <= 23 {
if n <= maxAdditionalInformationWithoutArgument {
const headSize = 1
e.WriteByte(t | byte(n))
return 1
return headSize
}
if n <= math.MaxUint8 {
scratch := [2]byte{t | byte(24), byte(n)}
e.Write(scratch[:2])
return 2
const headSize = 2
scratch := [headSize]byte{
t | byte(additionalInformationWith1ByteArgument),
byte(n),
}
e.Write(scratch[:])
return headSize
}
if n <= math.MaxUint16 {
var scratch [3]byte
scratch[0] = t | byte(25)
const headSize = 3
var scratch [headSize]byte
scratch[0] = t | byte(additionalInformationWith2ByteArgument)
binary.BigEndian.PutUint16(scratch[1:], uint16(n))
e.Write(scratch[:3])
return 3
e.Write(scratch[:])
return headSize
}
if n <= math.MaxUint32 {
var scratch [5]byte
scratch[0] = t | byte(26)
const headSize = 5
var scratch [headSize]byte
scratch[0] = t | byte(additionalInformationWith4ByteArgument)
binary.BigEndian.PutUint32(scratch[1:], uint32(n))
e.Write(scratch[:5])
return 5
e.Write(scratch[:])
return headSize
}
var scratch [9]byte
scratch[0] = t | byte(27)
const headSize = 9
var scratch [headSize]byte
scratch[0] = t | byte(additionalInformationWith8ByteArgument)
binary.BigEndian.PutUint64(scratch[1:], n)
e.Write(scratch[:9])
return 9
e.Write(scratch[:])
return headSize
}
var (

View File

@ -1388,7 +1388,7 @@ func (n *number3) UnmarshalCBOR(data []byte) (err error) {
return fmt.Errorf("wrong tag number %d, want %d", rawTag.Number, 100)
}
if rawTag.Content[0]&0xe0 != 0xa0 {
if getType(rawTag.Content[0]) != cborTypeMap {
return fmt.Errorf("wrong tag content type, want map")
}

View File

@ -99,7 +99,7 @@ func (d *decoder) wellformed(allowExtraData bool, checkBuiltinTags bool) error {
}
// wellformedInternal checks data's well-formedness and returns max depth and error.
func (d *decoder) wellformedInternal(depth int, checkBuiltinTags bool) (int, error) { // nolint:gocyclo
func (d *decoder) wellformedInternal(depth int, checkBuiltinTags bool) (int, error) { //nolint:gocyclo
t, ai, val, err := d.wellformedHead()
if err != nil {
return 0, err
@ -194,7 +194,7 @@ func (d *decoder) wellformedInternal(depth int, checkBuiltinTags bool) (int, err
Message: "bignum",
}
}
if cborType(d.data[d.off]&0xe0) != cborTypeTag {
if getType(d.data[d.off]) != cborTypeTag {
break
}
if _, _, tagNum, err = d.wellformedHead(); err != nil {
@ -224,11 +224,11 @@ func (d *decoder) wellformedIndefiniteString(t cborType, depth int, checkBuiltin
break
}
// Peek ahead to get next type and indefinite length status.
nt := cborType(d.data[d.off] & 0xe0)
nt, ai := parseInitialByte(d.data[d.off])
if t != nt {
return 0, &SyntaxError{"cbor: wrong element type " + nt.String() + " for indefinite-length " + t.String()}
}
if (d.data[d.off] & 0x1f) == 31 {
if ai == 31 {
return 0, &SyntaxError{"cbor: indefinite-length " + t.String() + " chunk is not definite-length"}
}
if depth, err = d.wellformedInternal(depth, checkBuiltinTags); err != nil {
@ -281,16 +281,18 @@ func (d *decoder) wellformedHead() (t cborType, ai byte, val uint64, err error)
return 0, 0, 0, io.ErrUnexpectedEOF
}
t = cborType(d.data[d.off] & 0xe0)
ai = d.data[d.off] & 0x1f
t, ai = parseInitialByte(d.data[d.off])
val = uint64(ai)
d.off++
dataLen--
if ai < 24 {
if ai <= maxAdditionalInformationWithoutArgument {
return t, ai, val, nil
}
if ai == 24 {
if dataLen < 2 {
if ai == additionalInformationWith1ByteArgument {
const argumentSize = 1
if dataLen < argumentSize {
return 0, 0, 0, io.ErrUnexpectedEOF
}
val = uint64(d.data[d.off])
@ -300,12 +302,14 @@ func (d *decoder) wellformedHead() (t cborType, ai byte, val uint64, err error)
}
return t, ai, val, nil
}
if ai == 25 {
if dataLen < 3 {
if ai == additionalInformationWith2ByteArgument {
const argumentSize = 2
if dataLen < argumentSize {
return 0, 0, 0, io.ErrUnexpectedEOF
}
val = uint64(binary.BigEndian.Uint16(d.data[d.off : d.off+2]))
d.off += 2
val = uint64(binary.BigEndian.Uint16(d.data[d.off : d.off+argumentSize]))
d.off += argumentSize
if t == cborTypePrimitives {
if err := d.acceptableFloat(float64(float16.Frombits(uint16(val)).Float32())); err != nil {
return 0, 0, 0, err
@ -313,12 +317,14 @@ func (d *decoder) wellformedHead() (t cborType, ai byte, val uint64, err error)
}
return t, ai, val, nil
}
if ai == 26 {
if dataLen < 5 {
if ai == additionalInformationWith4ByteArgument {
const argumentSize = 4
if dataLen < argumentSize {
return 0, 0, 0, io.ErrUnexpectedEOF
}
val = uint64(binary.BigEndian.Uint32(d.data[d.off : d.off+4]))
d.off += 4
val = uint64(binary.BigEndian.Uint32(d.data[d.off : d.off+argumentSize]))
d.off += argumentSize
if t == cborTypePrimitives {
if err := d.acceptableFloat(float64(math.Float32frombits(uint32(val)))); err != nil {
return 0, 0, 0, err
@ -326,12 +332,14 @@ func (d *decoder) wellformedHead() (t cborType, ai byte, val uint64, err error)
}
return t, ai, val, nil
}
if ai == 27 {
if dataLen < 9 {
if ai == additionalInformationWith8ByteArgument {
const argumentSize = 8
if dataLen < argumentSize {
return 0, 0, 0, io.ErrUnexpectedEOF
}
val = binary.BigEndian.Uint64(d.data[d.off : d.off+8])
d.off += 8
val = binary.BigEndian.Uint64(d.data[d.off : d.off+argumentSize])
d.off += argumentSize
if t == cborTypePrimitives {
if err := d.acceptableFloat(math.Float64frombits(val)); err != nil {
return 0, 0, 0, err
@ -339,6 +347,7 @@ func (d *decoder) wellformedHead() (t cborType, ai byte, val uint64, err error)
}
return t, ai, val, nil
}
if ai == 31 {
switch t {
case cborTypePositiveInt, cborTypeNegativeInt, cborTypeTag:
@ -348,6 +357,7 @@ func (d *decoder) wellformedHead() (t cborType, ai byte, val uint64, err error)
}
return t, ai, val, nil
}
// ai == 28, 29, 30
return 0, 0, 0, &SyntaxError{"cbor: invalid additional information " + strconv.Itoa(int(ai)) + " for type " + t.String()}
}