From 396c809424b70c1b45b8b0633423a47d6c7117f7 Mon Sep 17 00:00:00 2001 From: scruzin Date: Sat, 12 Jan 2019 15:33:14 +1030 Subject: [PATCH] Documented methods and standardized on 'buf' for parameter name. --- rtmp/amf/amf.go | 348 +++++++++++++++++++++++++----------------------- 1 file changed, 183 insertions(+), 165 deletions(-) diff --git a/rtmp/amf/amf.go b/rtmp/amf/amf.go index a7e41afb..8b552f5e 100644 --- a/rtmp/amf/amf.go +++ b/rtmp/amf/amf.go @@ -34,7 +34,7 @@ LICENSE Copyright (C) 2009-2010 Howard Chu */ -// amf implements Action Message Format (AMF) encoding and decoding. +// Package amf implements Action Message Format (AMF) encoding and decoding. // See https://en.wikipedia.org/wiki/Action_Message_Format. package amf @@ -50,21 +50,21 @@ const ( typeNumber = iota typeBoolean typeString - TypeObject // ToDo: consider not exporting this - typeMovieClip // reserved, not implemented - TypeNull // ToDo: consider not exporting this + TypeObject + typeMovieClip + TypeNull typeUndefined typeReference typeEcmaArray - TypeObjectEnd // ToDo: consider not exporting this + TypeObjectEnd typeStrictArray typeDate typeLongString typeUnsupported - typeRecordset // reserved, not implemented + typeRecordset typeXmlDoc typeTypedObject - typeAvmplus // reserved, not implemented + typeAvmplus typeInvalid = 0xff ) @@ -93,157 +93,170 @@ var ( ErrUnexpectedEnd = errors.New("amf: unexpected end") ) -// Basic decoding funcions. -func DecodeInt16(data []byte) uint16 { - return uint16(binary.BigEndian.Uint16(data)) +// DecodeInt16 decodes a 16-bit integer. +func DecodeInt16(buf []byte) uint16 { + return uint16(binary.BigEndian.Uint16(buf)) } -func DecodeInt24(data []byte) uint32 { - return uint32(data[0])<<16 | uint32(data[1])<<8 | uint32(data[2]) +// DecodeInt24 decodes a 24-bit integer. +func DecodeInt24(buf []byte) uint32 { + return uint32(buf[0])<<16 | uint32(buf[1])<<8 | uint32(buf[2]) } -func DecodeInt32(data []byte) uint32 { - return uint32(binary.BigEndian.Uint32(data)) +// DecodeInt32 decodes a 32-bit integer. +func DecodeInt32(buf []byte) uint32 { + return uint32(binary.BigEndian.Uint32(buf)) } -func DecodeString(data []byte) string { - n := DecodeInt16(data) - return string(data[2 : 2+n]) +// DecodeString decodes a string that is less than 2^16 bytes long. +func DecodeString(buf []byte) string { + n := DecodeInt16(buf) + return string(buf[2 : 2+n]) } -func DecodeLongString(data []byte) string { - n := DecodeInt32(data) - return string(data[2 : 2+n]) +// DecodeLongString decodes a long string. +func DecodeLongString(buf []byte) string { + n := DecodeInt32(buf) + return string(buf[2 : 2+n]) } -func DecodeNumber(data []byte) float64 { - return math.Float64frombits(binary.BigEndian.Uint64(data)) +// DecodeNumber decodes a 64-bit floating-point number. +func DecodeNumber(buf []byte) float64 { + return math.Float64frombits(binary.BigEndian.Uint64(buf)) } -func DecodeBoolean(data []byte) bool { - return data[0] != 0 +// DecodeBoolean decodes a boolean. +func DecodeBoolean(buf []byte) bool { + return buf[0] != 0 } -// Basic encoding functions. -func EncodeInt24(dst []byte, val int32) ([]byte, error) { - if len(dst) < 3 { +// EncodeInt24 encodes a 24-bit integer. +func EncodeInt24(buf []byte, val int32) ([]byte, error) { + if len(buf) < 3 { return nil, ErrShortBuffer } - dst[0] = byte(val >> 16) - dst[1] = byte(val >> 8) - dst[2] = byte(val) - if len(dst) == 3 { + buf[0] = byte(val >> 16) + buf[1] = byte(val >> 8) + buf[2] = byte(val) + if len(buf) == 3 { return nil, ErrEndOfBuffer } - return dst[3:], nil + return buf[3:], nil } -func EncodeInt32(dst []byte, val int32) ([]byte, error) { - if len(dst) < 4 { +// EncodeInt32 encodes a 32-bit integer. +func EncodeInt32(buf []byte, val int32) ([]byte, error) { + if len(buf) < 4 { return nil, ErrShortBuffer } - binary.BigEndian.PutUint32(dst, uint32(val)) - if len(dst) == 4 { + binary.BigEndian.PutUint32(buf, uint32(val)) + if len(buf) == 4 { return nil, ErrEndOfBuffer } - return dst[4:], nil + return buf[4:], nil } -func EncodeString(dst []byte, val string) ([]byte, error) { +// EncodeString encodes a string. +func EncodeString(buf []byte, val string) ([]byte, error) { const typeSize = 1 - if len(val) < 65536 && len(val)+typeSize+binary.Size(int16(0)) > len(dst) { + if len(val) < 65536 && len(val)+typeSize+binary.Size(int16(0)) > len(buf) { return nil, ErrShortBuffer } - if len(val)+typeSize+binary.Size(int32(0)) > len(dst) { + if len(val)+typeSize+binary.Size(int32(0)) > len(buf) { return nil, ErrShortBuffer } if len(val) < 65536 { - dst[0] = typeString - dst = dst[1:] - binary.BigEndian.PutUint16(dst[:2], uint16(len(val))) - dst = dst[2:] - copy(dst, val) - if len(dst) == len(val) { + buf[0] = typeString + buf = buf[1:] + binary.BigEndian.PutUint16(buf[:2], uint16(len(val))) + buf = buf[2:] + copy(buf, val) + if len(buf) == len(val) { return nil, ErrEndOfBuffer } - return dst[len(val):], nil + return buf[len(val):], nil } - dst[0] = typeLongString - dst = dst[1:] - binary.BigEndian.PutUint32(dst[:4], uint32(len(val))) - dst = dst[4:] - copy(dst, val) - if len(dst) == len(val) { + buf[0] = typeLongString + buf = buf[1:] + binary.BigEndian.PutUint32(buf[:4], uint32(len(val))) + buf = buf[4:] + copy(buf, val) + if len(buf) == len(val) { return nil, ErrEndOfBuffer } - return dst[len(val):], nil + return buf[len(val):], nil } -func EncodeNumber(dst []byte, val float64) ([]byte, error) { - if len(dst) < 9 { +// EncodeNumber encodes a 64-bit floating-point number. +func EncodeNumber(buf []byte, val float64) ([]byte, error) { + if len(buf) < 9 { return nil, ErrShortBuffer } - dst[0] = typeNumber - dst = dst[1:] - binary.BigEndian.PutUint64(dst, math.Float64bits(val)) - return dst[8:], nil + buf[0] = typeNumber + buf = buf[1:] + binary.BigEndian.PutUint64(buf, math.Float64bits(val)) + return buf[8:], nil } -func EncodeBoolean(dst []byte, val bool) ([]byte, error) { - if len(dst) < 2 { +// EncodeBoolean encodes a boolean. +func EncodeBoolean(buf []byte, val bool) ([]byte, error) { + if len(buf) < 2 { return nil, ErrShortBuffer } - dst[0] = typeBoolean + buf[0] = typeBoolean if val { - dst[1] = 1 + buf[1] = 1 } - if len(dst) == 2 { + if len(buf) == 2 { return nil, ErrEndOfBuffer } - return dst[2:], nil + return buf[2:], nil } -func EncodeNamedString(dst []byte, key, val string) ([]byte, error) { - if 2+len(key) > len(dst) { +// EncodeNamedString encodes a named string, where key is the name and val is the string value. +func EncodeNamedString(buf []byte, key, val string) ([]byte, error) { + if 2+len(key) > len(buf) { return nil, ErrShortBuffer } - binary.BigEndian.PutUint16(dst[:2], uint16(len(key))) - dst = dst[2:] - copy(dst, key) - if len(key) == len(dst) { + binary.BigEndian.PutUint16(buf[:2], uint16(len(key))) + buf = buf[2:] + copy(buf, key) + if len(key) == len(buf) { return nil, ErrEndOfBuffer } - return EncodeString(dst[len(key):], val) + return EncodeString(buf[len(key):], val) } -func EncodeNamedNumber(dst []byte, key string, val float64) ([]byte, error) { - if 2+len(key) > len(dst) { +// EncodeNamedNumber encodes a named number, where key is the name and val is the number value. +func EncodeNamedNumber(buf []byte, key string, val float64) ([]byte, error) { + if 2+len(key) > len(buf) { return nil, ErrShortBuffer } - binary.BigEndian.PutUint16(dst[:2], uint16(len(key))) - dst = dst[2:] - copy(dst, key) - if len(key) == len(dst) { + binary.BigEndian.PutUint16(buf[:2], uint16(len(key))) + buf = buf[2:] + copy(buf, key) + if len(key) == len(buf) { return nil, ErrEndOfBuffer } - return EncodeNumber(dst[len(key):], val) + return EncodeNumber(buf[len(key):], val) } -func EncodeNamedBoolean(dst []byte, key string, val bool) ([]byte, error) { - if 2+len(key) > len(dst) { +// EncodeNamedNumber encodes a named boolean, where key is the name and val is the booelean value. +func EncodeNamedBoolean(buf []byte, key string, val bool) ([]byte, error) { + if 2+len(key) > len(buf) { return nil, ErrShortBuffer } - binary.BigEndian.PutUint16(dst[:2], uint16(len(key))) - dst = dst[2:] - copy(dst, key) - if len(key) == len(dst) { + binary.BigEndian.PutUint16(buf[:2], uint16(len(key))) + buf = buf[2:] + copy(buf, key) + if len(key) == len(buf) { return nil, ErrEndOfBuffer } - return EncodeBoolean(dst[len(key):], val) + return EncodeBoolean(buf[len(key):], val) } // Property functions. @@ -272,114 +285,114 @@ func PropGetObject(prop *Property, obj *Object) { } // PropEncode encodes a property. -func PropEncode(p *Property, dst []byte) ([]byte, error) { +func PropEncode(p *Property, buf []byte) ([]byte, error) { if p.dtype == typeInvalid { return nil, ErrShortBuffer } - if p.dtype != TypeNull && len(p.name)+2+1 >= len(dst) { + if p.dtype != TypeNull && len(p.name)+2+1 >= len(buf) { return nil, ErrShortBuffer } if p.dtype != TypeNull && len(p.name) != 0 { - binary.BigEndian.PutUint16(dst[:2], uint16(len(p.name))) - dst = dst[2:] - copy(dst, p.name) - dst = dst[len(p.name):] + binary.BigEndian.PutUint16(buf[:2], uint16(len(p.name))) + buf = buf[2:] + copy(buf, p.name) + buf = buf[len(p.name):] } var err error switch p.dtype { case typeNumber: - dst, err = EncodeNumber(dst, p.number) + buf, err = EncodeNumber(buf, p.number) case typeBoolean: - dst, err = EncodeBoolean(dst, p.number != 0) + buf, err = EncodeBoolean(buf, p.number != 0) case typeString: - dst, err = EncodeString(dst, p.str) + buf, err = EncodeString(buf, p.str) case TypeNull: - if len(dst) < 2 { + if len(buf) < 2 { return nil, ErrShortBuffer } - dst[0] = TypeNull - dst = dst[1:] + buf[0] = TypeNull + buf = buf[1:] case TypeObject: - dst, err = Encode(&p.obj, dst) + buf, err = Encode(&p.obj, buf) case typeEcmaArray: - dst, err = EncodeEcmaArray(&p.obj, dst) + buf, err = EncodeEcmaArray(&p.obj, buf) case typeStrictArray: - dst, err = EncodeArray(&p.obj, dst) + buf, err = EncodeArray(&p.obj, buf) default: - dst, err = nil, ErrInvalidType + buf, err = nil, ErrInvalidType } - return dst, err + return buf, err } // PropDecode decodes a property, returning the number of bytes consumed from the supplied buffer. -func PropDecode(prop *Property, data []byte, decodeName bool) (int, error) { +func PropDecode(prop *Property, buf []byte, decodeName bool) (int, error) { prop.name = "" - sz := len(data) - if len(data) == 0 { + sz := len(buf) + if len(buf) == 0 { return 0, ErrEndOfBuffer } if decodeName { - if len(data) < 4 { + if len(buf) < 4 { return 0, ErrShortBuffer } - n := DecodeInt16(data[:2]) - if int(n) > len(data)-2 { + n := DecodeInt16(buf[:2]) + if int(n) > len(buf)-2 { return 0, ErrDecodingName } - prop.name = DecodeString(data) - data = data[2+n:] + prop.name = DecodeString(buf) + buf = buf[2+n:] } - if len(data) == 0 { + if len(buf) == 0 { return 0, ErrEndOfBuffer } - prop.dtype = uint8(data[0]) - data = data[1:] + prop.dtype = uint8(buf[0]) + buf = buf[1:] switch prop.dtype { case typeNumber: - if len(data) < 8 { + if len(buf) < 8 { return 0, ErrShortBuffer } - prop.number = DecodeNumber(data[:8]) - data = data[8:] + prop.number = DecodeNumber(buf[:8]) + buf = buf[8:] case typeBoolean: return 0, ErrUnimplemented case typeString: - n := DecodeInt16(data[:2]) - if len(data) < int(n+2) { + n := DecodeInt16(buf[:2]) + if len(buf) < int(n+2) { return 0, ErrShortBuffer } - prop.str = DecodeString(data) - data = data[2+n:] + prop.str = DecodeString(buf) + buf = buf[2+n:] case TypeObject: - n, err := Decode(&prop.obj, data, true) + n, err := Decode(&prop.obj, buf, true) if err != nil { return 0, err } - data = data[n:] + buf = buf[n:] case TypeNull, typeUndefined, typeUnsupported: prop.dtype = TypeNull case typeEcmaArray: - data = data[4:] - n, err := Decode(&prop.obj, data, true) + buf = buf[4:] + n, err := Decode(&prop.obj, buf, true) if err != nil { return 0, err } - data = data[n:] + buf = buf[n:] case TypeObjectEnd: return 0, ErrUnexpectedEnd @@ -388,102 +401,107 @@ func PropDecode(prop *Property, data []byte, decodeName bool) (int, error) { return 0, ErrUnimplemented } - return sz - len(data), nil + return sz - len(buf), nil } -// Encode serializes an Object into its AMF representation. -func Encode(obj *Object, dst []byte) ([]byte, error) { - if len(dst) < 5 { +// Encode encodes an Object into its AMF representation. +func Encode(obj *Object, buf []byte) ([]byte, error) { + if len(buf) < 5 { return nil, ErrShortBuffer } - dst[0] = TypeObject - dst = dst[1:] + buf[0] = TypeObject + buf = buf[1:] for i := 0; i < len(obj.Props); i++ { var err error - dst, err = PropEncode(&obj.Props[i], dst) + buf, err = PropEncode(&obj.Props[i], buf) if err != nil { return nil, err } } - if len(dst) < 4 { + if len(buf) < 4 { return nil, ErrShortBuffer } - return EncodeInt24(dst, TypeObjectEnd) + return EncodeInt24(buf, TypeObjectEnd) } -func EncodeEcmaArray(obj *Object, dst []byte) ([]byte, error) { - if len(dst) < 5 { +// EncodeEcmaArray encodes an ECMA array. +func EncodeEcmaArray(obj *Object, buf []byte) ([]byte, error) { + if len(buf) < 5 { return nil, ErrShortBuffer } - dst[0] = typeEcmaArray - dst = dst[1:] - binary.BigEndian.PutUint32(dst[:4], uint32(len(obj.Props))) - dst = dst[4:] + buf[0] = typeEcmaArray + buf = buf[1:] + binary.BigEndian.PutUint32(buf[:4], uint32(len(obj.Props))) + buf = buf[4:] for i := 0; i < len(obj.Props); i++ { var err error - dst, err = PropEncode(&obj.Props[i], dst) + buf, err = PropEncode(&obj.Props[i], buf) if err != nil { return nil, err } } - if len(dst) < 4 { + if len(buf) < 4 { return nil, ErrShortBuffer } - return EncodeInt24(dst, TypeObjectEnd) + return EncodeInt24(buf, TypeObjectEnd) } -func EncodeArray(obj *Object, dst []byte) ([]byte, error) { - if len(dst) < 5 { +// EncodeArray encodes an array. +func EncodeArray(obj *Object, buf []byte) ([]byte, error) { + if len(buf) < 5 { return nil, ErrShortBuffer } - dst[0] = typeStrictArray - dst = dst[1:] - binary.BigEndian.PutUint32(dst[:4], uint32(len(obj.Props))) - dst = dst[4:] + buf[0] = typeStrictArray + buf = buf[1:] + binary.BigEndian.PutUint32(buf[:4], uint32(len(obj.Props))) + buf = buf[4:] for i := 0; i < len(obj.Props); i++ { var err error - dst, err = PropEncode(&obj.Props[i], dst) + buf, err = PropEncode(&obj.Props[i], buf) if err != nil { return nil, err } } - return dst, nil + return buf, nil } -func Decode(obj *Object, data []byte, decodeName bool) (int, error) { - sz := len(data) +// Decode decodes an object. Property names are only decoded if decodeName is true. +func Decode(obj *Object, buf []byte, decodeName bool) (int, error) { + sz := len(buf) obj.Props = obj.Props[:0] - for len(data) != 0 { - if len(data) >= 3 && DecodeInt24(data[:3]) == TypeObjectEnd { - data = data[3:] - break + for len(buf) != 0 { + if len(buf) >= 3 && DecodeInt24(buf[:3]) == TypeObjectEnd { + buf = buf[3:] + break } var prop Property - n, err := PropDecode(&prop, data, decodeName) + n, err := PropDecode(&prop, buf, decodeName) if err != nil { return 0, err } - data = data[n:] + buf = buf[n:] obj.Props = append(obj.Props, prop) } - return sz - len(data), nil + return sz - len(buf), nil } -func GetProp(obj *Object, name string, idx int32) *Property { +// GetProp returns an object's property, either by its index when idx is non-negative, or by its name when name otherwise. +// If the matching property is not found nil is returned. +func GetProp(obj *Object, name string, idx int) *Property { if idx >= 0 { - if idx < int32(len(obj.Props)) { + if idx < len(obj.Props) { return &obj.Props[idx] } } else { @@ -493,5 +511,5 @@ func GetProp(obj *Object, name string, idx int32) *Property { } } } - return &Property{} + return nil }