encoding/cbor/decode.go (263 lines of code) (raw):
package cbor
import (
"encoding/binary"
"fmt"
"math"
)
func decode(p []byte) (Value, int, error) {
if len(p) == 0 {
return nil, 0, fmt.Errorf("unexpected end of payload")
}
switch peekMajor(p) {
case majorTypeUint:
return decodeUint(p)
case majorTypeNegInt:
return decodeNegInt(p)
case majorTypeSlice:
return decodeSlice(p, majorTypeSlice)
case majorTypeString:
s, n, err := decodeSlice(p, majorTypeString)
return String(s), n, err
case majorTypeList:
return decodeList(p)
case majorTypeMap:
return decodeMap(p)
case majorTypeTag:
return decodeTag(p)
default: // majorType7
return decodeMajor7(p)
}
}
func decodeUint(p []byte) (Uint, int, error) {
i, off, err := decodeArgument(p)
if err != nil {
return 0, 0, fmt.Errorf("decode argument: %w", err)
}
return Uint(i), off, nil
}
func decodeNegInt(p []byte) (NegInt, int, error) {
i, off, err := decodeArgument(p)
if err != nil {
return 0, 0, fmt.Errorf("decode argument: %w", err)
}
return NegInt(i + 1), off, nil
}
// this routine is used for both string and slice major types, the value of
// inner specifies which context we're in (needed for validating subsegments
// inside indefinite encodings)
func decodeSlice(p []byte, inner majorType) (Slice, int, error) {
minor := peekMinor(p)
if minor == minorIndefinite {
return decodeSliceIndefinite(p, inner)
}
slen, off, err := decodeArgument(p)
if err != nil {
return nil, 0, fmt.Errorf("decode argument: %w", err)
}
p = p[off:]
if uint64(len(p)) < slen {
return nil, 0, fmt.Errorf("slice len %d greater than remaining buf len", slen)
}
return Slice(p[:slen]), off + int(slen), nil
}
func decodeSliceIndefinite(p []byte, inner majorType) (Slice, int, error) {
p = p[1:]
s := Slice{}
for off := 0; len(p) > 0; {
if p[0] == 0xff {
return s, off + 2, nil
}
if major := peekMajor(p); major != inner {
return nil, 0, fmt.Errorf("unexpected major type %d in indefinite slice", major)
}
if peekMinor(p) == minorIndefinite {
return nil, 0, fmt.Errorf("nested indefinite slice")
}
ss, n, err := decodeSlice(p, inner)
if err != nil {
return nil, 0, fmt.Errorf("decode subslice: %w", err)
}
p = p[n:]
s = append(s, ss...)
off += n
}
return nil, 0, fmt.Errorf("expected break marker")
}
func decodeList(p []byte) (List, int, error) {
minor := peekMinor(p)
if minor == minorIndefinite {
return decodeListIndefinite(p)
}
alen, off, err := decodeArgument(p)
if err != nil {
return nil, 0, fmt.Errorf("decode argument: %w", err)
}
p = p[off:]
l := List{}
for i := 0; i < int(alen); i++ {
item, n, err := decode(p)
if err != nil {
return nil, 0, fmt.Errorf("decode item: %w", err)
}
p = p[n:]
l = append(l, item)
off += n
}
return l, off, nil
}
func decodeListIndefinite(p []byte) (List, int, error) {
p = p[1:]
l := List{}
for off := 0; len(p) > 0; {
if p[0] == 0xff {
return l, off + 2, nil
}
item, n, err := decode(p)
if err != nil {
return nil, 0, fmt.Errorf("decode item: %w", err)
}
p = p[n:]
l = append(l, item)
off += n
}
return nil, 0, fmt.Errorf("expected break marker")
}
func decodeMap(p []byte) (Map, int, error) {
minor := peekMinor(p)
if minor == minorIndefinite {
return decodeMapIndefinite(p)
}
maplen, off, err := decodeArgument(p)
if err != nil {
return nil, 0, fmt.Errorf("decode argument: %w", err)
}
p = p[off:]
mp := Map{}
for i := 0; i < int(maplen); i++ {
if len(p) == 0 {
return nil, 0, fmt.Errorf("unexpected end of payload")
}
if major := peekMajor(p); major != majorTypeString {
return nil, 0, fmt.Errorf("unexpected major type %d for map key", major)
}
key, kn, err := decodeSlice(p, majorTypeString)
if err != nil {
return nil, 0, fmt.Errorf("decode key: %w", err)
}
p = p[kn:]
value, vn, err := decode(p)
if err != nil {
return nil, 0, fmt.Errorf("decode value: %w", err)
}
p = p[vn:]
mp[string(key)] = value
off += kn + vn
}
return mp, off, nil
}
func decodeMapIndefinite(p []byte) (Map, int, error) {
p = p[1:]
mp := Map{}
for off := 0; len(p) > 0; {
if p[0] == 0xff {
return mp, off + 2, nil
}
if major := peekMajor(p); major != majorTypeString {
return nil, 0, fmt.Errorf("unexpected major type %d for map key", major)
}
key, kn, err := decodeSlice(p, majorTypeString)
if err != nil {
return nil, 0, fmt.Errorf("decode key: %w", err)
}
p = p[kn:]
value, vn, err := decode(p)
if err != nil {
return nil, 0, fmt.Errorf("decode value: %w", err)
}
p = p[vn:]
mp[string(key)] = value
off += kn + vn
}
return nil, 0, fmt.Errorf("expected break marker")
}
func decodeTag(p []byte) (*Tag, int, error) {
id, off, err := decodeArgument(p)
if err != nil {
return nil, 0, fmt.Errorf("decode argument: %w", err)
}
p = p[off:]
v, n, err := decode(p)
if err != nil {
return nil, 0, fmt.Errorf("decode value: %w", err)
}
return &Tag{ID: id, Value: v}, off + n, nil
}
func decodeMajor7(p []byte) (Value, int, error) {
switch m := peekMinor(p); m {
case major7True, major7False:
return Bool(m == major7True), 1, nil
case major7Nil:
return &Nil{}, 1, nil
case major7Undefined:
return &Undefined{}, 1, nil
case major7Float16:
if len(p) < 3 {
return nil, 0, fmt.Errorf("incomplete float16 at end of buf")
}
b := binary.BigEndian.Uint16(p[1:])
return Float32(math.Float32frombits(float16to32(b))), 3, nil
case major7Float32:
if len(p) < 5 {
return nil, 0, fmt.Errorf("incomplete float32 at end of buf")
}
b := binary.BigEndian.Uint32(p[1:])
return Float32(math.Float32frombits(b)), 5, nil
case major7Float64:
if len(p) < 9 {
return nil, 0, fmt.Errorf("incomplete float64 at end of buf")
}
b := binary.BigEndian.Uint64(p[1:])
return Float64(math.Float64frombits(b)), 9, nil
default:
return nil, 0, fmt.Errorf("unexpected minor value %d", m)
}
}
func peekMajor(p []byte) majorType {
return majorType(p[0] & maskMajor >> 5)
}
func peekMinor(p []byte) byte {
return p[0] & maskMinor
}
// pulls the next argument out of the buffer
//
// expects one of the sized arguments and will error otherwise - callers that
// need to check for the indefinite flag must do so externally
func decodeArgument(p []byte) (uint64, int, error) {
minor := peekMinor(p)
if minor < minorArg1 {
return uint64(minor), 1, nil
}
switch minor {
case minorArg1, minorArg2, minorArg4, minorArg8:
argLen := mtol(minor)
if len(p) < argLen+1 {
return 0, 0, fmt.Errorf("arg len %d greater than remaining buf len", argLen)
}
return readArgument(p[1:], argLen), argLen + 1, nil
default:
return 0, 0, fmt.Errorf("unexpected minor value %d", minor)
}
}
// minor value to arg len in bytes, assumes minor was checked to be in [24,27]
func mtol(minor byte) int {
if minor == minorArg1 {
return 1
} else if minor == minorArg2 {
return 2
} else if minor == minorArg4 {
return 4
}
return 8
}
func readArgument(p []byte, len int) uint64 {
if len == 1 {
return uint64(p[0])
} else if len == 2 {
return uint64(binary.BigEndian.Uint16(p))
} else if len == 4 {
return uint64(binary.BigEndian.Uint32(p))
}
return uint64(binary.BigEndian.Uint64(p))
}