Documented methods and standardized on 'buf' for parameter name.

This commit is contained in:
scruzin 2019-01-12 15:33:14 +10:30
parent f7de9526c8
commit 396c809424
1 changed files with 183 additions and 165 deletions

View File

@ -34,7 +34,7 @@ LICENSE
Copyright (C) 2009-2010 Howard Chu 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. // See https://en.wikipedia.org/wiki/Action_Message_Format.
package amf package amf
@ -50,21 +50,21 @@ const (
typeNumber = iota typeNumber = iota
typeBoolean typeBoolean
typeString typeString
TypeObject // ToDo: consider not exporting this TypeObject
typeMovieClip // reserved, not implemented typeMovieClip
TypeNull // ToDo: consider not exporting this TypeNull
typeUndefined typeUndefined
typeReference typeReference
typeEcmaArray typeEcmaArray
TypeObjectEnd // ToDo: consider not exporting this TypeObjectEnd
typeStrictArray typeStrictArray
typeDate typeDate
typeLongString typeLongString
typeUnsupported typeUnsupported
typeRecordset // reserved, not implemented typeRecordset
typeXmlDoc typeXmlDoc
typeTypedObject typeTypedObject
typeAvmplus // reserved, not implemented typeAvmplus
typeInvalid = 0xff typeInvalid = 0xff
) )
@ -93,157 +93,170 @@ var (
ErrUnexpectedEnd = errors.New("amf: unexpected end") ErrUnexpectedEnd = errors.New("amf: unexpected end")
) )
// Basic decoding funcions. // DecodeInt16 decodes a 16-bit integer.
func DecodeInt16(data []byte) uint16 { func DecodeInt16(buf []byte) uint16 {
return uint16(binary.BigEndian.Uint16(data)) return uint16(binary.BigEndian.Uint16(buf))
} }
func DecodeInt24(data []byte) uint32 { // DecodeInt24 decodes a 24-bit integer.
return uint32(data[0])<<16 | uint32(data[1])<<8 | uint32(data[2]) func DecodeInt24(buf []byte) uint32 {
return uint32(buf[0])<<16 | uint32(buf[1])<<8 | uint32(buf[2])
} }
func DecodeInt32(data []byte) uint32 { // DecodeInt32 decodes a 32-bit integer.
return uint32(binary.BigEndian.Uint32(data)) func DecodeInt32(buf []byte) uint32 {
return uint32(binary.BigEndian.Uint32(buf))
} }
func DecodeString(data []byte) string { // DecodeString decodes a string that is less than 2^16 bytes long.
n := DecodeInt16(data) func DecodeString(buf []byte) string {
return string(data[2 : 2+n]) n := DecodeInt16(buf)
return string(buf[2 : 2+n])
} }
func DecodeLongString(data []byte) string { // DecodeLongString decodes a long string.
n := DecodeInt32(data) func DecodeLongString(buf []byte) string {
return string(data[2 : 2+n]) n := DecodeInt32(buf)
return string(buf[2 : 2+n])
} }
func DecodeNumber(data []byte) float64 { // DecodeNumber decodes a 64-bit floating-point number.
return math.Float64frombits(binary.BigEndian.Uint64(data)) func DecodeNumber(buf []byte) float64 {
return math.Float64frombits(binary.BigEndian.Uint64(buf))
} }
func DecodeBoolean(data []byte) bool { // DecodeBoolean decodes a boolean.
return data[0] != 0 func DecodeBoolean(buf []byte) bool {
return buf[0] != 0
} }
// Basic encoding functions. // EncodeInt24 encodes a 24-bit integer.
func EncodeInt24(dst []byte, val int32) ([]byte, error) { func EncodeInt24(buf []byte, val int32) ([]byte, error) {
if len(dst) < 3 { if len(buf) < 3 {
return nil, ErrShortBuffer return nil, ErrShortBuffer
} }
dst[0] = byte(val >> 16) buf[0] = byte(val >> 16)
dst[1] = byte(val >> 8) buf[1] = byte(val >> 8)
dst[2] = byte(val) buf[2] = byte(val)
if len(dst) == 3 { if len(buf) == 3 {
return nil, ErrEndOfBuffer return nil, ErrEndOfBuffer
} }
return dst[3:], nil return buf[3:], nil
} }
func EncodeInt32(dst []byte, val int32) ([]byte, error) { // EncodeInt32 encodes a 32-bit integer.
if len(dst) < 4 { func EncodeInt32(buf []byte, val int32) ([]byte, error) {
if len(buf) < 4 {
return nil, ErrShortBuffer return nil, ErrShortBuffer
} }
binary.BigEndian.PutUint32(dst, uint32(val)) binary.BigEndian.PutUint32(buf, uint32(val))
if len(dst) == 4 { if len(buf) == 4 {
return nil, ErrEndOfBuffer 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 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 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 return nil, ErrShortBuffer
} }
if len(val) < 65536 { if len(val) < 65536 {
dst[0] = typeString buf[0] = typeString
dst = dst[1:] buf = buf[1:]
binary.BigEndian.PutUint16(dst[:2], uint16(len(val))) binary.BigEndian.PutUint16(buf[:2], uint16(len(val)))
dst = dst[2:] buf = buf[2:]
copy(dst, val) copy(buf, val)
if len(dst) == len(val) { if len(buf) == len(val) {
return nil, ErrEndOfBuffer return nil, ErrEndOfBuffer
} }
return dst[len(val):], nil return buf[len(val):], nil
} }
dst[0] = typeLongString buf[0] = typeLongString
dst = dst[1:] buf = buf[1:]
binary.BigEndian.PutUint32(dst[:4], uint32(len(val))) binary.BigEndian.PutUint32(buf[:4], uint32(len(val)))
dst = dst[4:] buf = buf[4:]
copy(dst, val) copy(buf, val)
if len(dst) == len(val) { if len(buf) == len(val) {
return nil, ErrEndOfBuffer return nil, ErrEndOfBuffer
} }
return dst[len(val):], nil return buf[len(val):], nil
} }
func EncodeNumber(dst []byte, val float64) ([]byte, error) { // EncodeNumber encodes a 64-bit floating-point number.
if len(dst) < 9 { func EncodeNumber(buf []byte, val float64) ([]byte, error) {
if len(buf) < 9 {
return nil, ErrShortBuffer return nil, ErrShortBuffer
} }
dst[0] = typeNumber buf[0] = typeNumber
dst = dst[1:] buf = buf[1:]
binary.BigEndian.PutUint64(dst, math.Float64bits(val)) binary.BigEndian.PutUint64(buf, math.Float64bits(val))
return dst[8:], nil return buf[8:], nil
} }
func EncodeBoolean(dst []byte, val bool) ([]byte, error) { // EncodeBoolean encodes a boolean.
if len(dst) < 2 { func EncodeBoolean(buf []byte, val bool) ([]byte, error) {
if len(buf) < 2 {
return nil, ErrShortBuffer return nil, ErrShortBuffer
} }
dst[0] = typeBoolean buf[0] = typeBoolean
if val { if val {
dst[1] = 1 buf[1] = 1
} }
if len(dst) == 2 { if len(buf) == 2 {
return nil, ErrEndOfBuffer return nil, ErrEndOfBuffer
} }
return dst[2:], nil return buf[2:], nil
} }
func EncodeNamedString(dst []byte, key, val string) ([]byte, error) { // EncodeNamedString encodes a named string, where key is the name and val is the string value.
if 2+len(key) > len(dst) { func EncodeNamedString(buf []byte, key, val string) ([]byte, error) {
if 2+len(key) > len(buf) {
return nil, ErrShortBuffer return nil, ErrShortBuffer
} }
binary.BigEndian.PutUint16(dst[:2], uint16(len(key))) binary.BigEndian.PutUint16(buf[:2], uint16(len(key)))
dst = dst[2:] buf = buf[2:]
copy(dst, key) copy(buf, key)
if len(key) == len(dst) { if len(key) == len(buf) {
return nil, ErrEndOfBuffer 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) { // EncodeNamedNumber encodes a named number, where key is the name and val is the number value.
if 2+len(key) > len(dst) { func EncodeNamedNumber(buf []byte, key string, val float64) ([]byte, error) {
if 2+len(key) > len(buf) {
return nil, ErrShortBuffer return nil, ErrShortBuffer
} }
binary.BigEndian.PutUint16(dst[:2], uint16(len(key))) binary.BigEndian.PutUint16(buf[:2], uint16(len(key)))
dst = dst[2:] buf = buf[2:]
copy(dst, key) copy(buf, key)
if len(key) == len(dst) { if len(key) == len(buf) {
return nil, ErrEndOfBuffer 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) { // EncodeNamedNumber encodes a named boolean, where key is the name and val is the booelean value.
if 2+len(key) > len(dst) { func EncodeNamedBoolean(buf []byte, key string, val bool) ([]byte, error) {
if 2+len(key) > len(buf) {
return nil, ErrShortBuffer return nil, ErrShortBuffer
} }
binary.BigEndian.PutUint16(dst[:2], uint16(len(key))) binary.BigEndian.PutUint16(buf[:2], uint16(len(key)))
dst = dst[2:] buf = buf[2:]
copy(dst, key) copy(buf, key)
if len(key) == len(dst) { if len(key) == len(buf) {
return nil, ErrEndOfBuffer return nil, ErrEndOfBuffer
} }
return EncodeBoolean(dst[len(key):], val) return EncodeBoolean(buf[len(key):], val)
} }
// Property functions. // Property functions.
@ -272,114 +285,114 @@ func PropGetObject(prop *Property, obj *Object) {
} }
// PropEncode encodes a property. // PropEncode encodes a property.
func PropEncode(p *Property, dst []byte) ([]byte, error) { func PropEncode(p *Property, buf []byte) ([]byte, error) {
if p.dtype == typeInvalid { if p.dtype == typeInvalid {
return nil, ErrShortBuffer 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 return nil, ErrShortBuffer
} }
if p.dtype != TypeNull && len(p.name) != 0 { if p.dtype != TypeNull && len(p.name) != 0 {
binary.BigEndian.PutUint16(dst[:2], uint16(len(p.name))) binary.BigEndian.PutUint16(buf[:2], uint16(len(p.name)))
dst = dst[2:] buf = buf[2:]
copy(dst, p.name) copy(buf, p.name)
dst = dst[len(p.name):] buf = buf[len(p.name):]
} }
var err error var err error
switch p.dtype { switch p.dtype {
case typeNumber: case typeNumber:
dst, err = EncodeNumber(dst, p.number) buf, err = EncodeNumber(buf, p.number)
case typeBoolean: case typeBoolean:
dst, err = EncodeBoolean(dst, p.number != 0) buf, err = EncodeBoolean(buf, p.number != 0)
case typeString: case typeString:
dst, err = EncodeString(dst, p.str) buf, err = EncodeString(buf, p.str)
case TypeNull: case TypeNull:
if len(dst) < 2 { if len(buf) < 2 {
return nil, ErrShortBuffer return nil, ErrShortBuffer
} }
dst[0] = TypeNull buf[0] = TypeNull
dst = dst[1:] buf = buf[1:]
case TypeObject: case TypeObject:
dst, err = Encode(&p.obj, dst) buf, err = Encode(&p.obj, buf)
case typeEcmaArray: case typeEcmaArray:
dst, err = EncodeEcmaArray(&p.obj, dst) buf, err = EncodeEcmaArray(&p.obj, buf)
case typeStrictArray: case typeStrictArray:
dst, err = EncodeArray(&p.obj, dst) buf, err = EncodeArray(&p.obj, buf)
default: 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. // 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 = "" prop.name = ""
sz := len(data) sz := len(buf)
if len(data) == 0 { if len(buf) == 0 {
return 0, ErrEndOfBuffer return 0, ErrEndOfBuffer
} }
if decodeName { if decodeName {
if len(data) < 4 { if len(buf) < 4 {
return 0, ErrShortBuffer return 0, ErrShortBuffer
} }
n := DecodeInt16(data[:2]) n := DecodeInt16(buf[:2])
if int(n) > len(data)-2 { if int(n) > len(buf)-2 {
return 0, ErrDecodingName return 0, ErrDecodingName
} }
prop.name = DecodeString(data) prop.name = DecodeString(buf)
data = data[2+n:] buf = buf[2+n:]
} }
if len(data) == 0 { if len(buf) == 0 {
return 0, ErrEndOfBuffer return 0, ErrEndOfBuffer
} }
prop.dtype = uint8(data[0]) prop.dtype = uint8(buf[0])
data = data[1:] buf = buf[1:]
switch prop.dtype { switch prop.dtype {
case typeNumber: case typeNumber:
if len(data) < 8 { if len(buf) < 8 {
return 0, ErrShortBuffer return 0, ErrShortBuffer
} }
prop.number = DecodeNumber(data[:8]) prop.number = DecodeNumber(buf[:8])
data = data[8:] buf = buf[8:]
case typeBoolean: case typeBoolean:
return 0, ErrUnimplemented return 0, ErrUnimplemented
case typeString: case typeString:
n := DecodeInt16(data[:2]) n := DecodeInt16(buf[:2])
if len(data) < int(n+2) { if len(buf) < int(n+2) {
return 0, ErrShortBuffer return 0, ErrShortBuffer
} }
prop.str = DecodeString(data) prop.str = DecodeString(buf)
data = data[2+n:] buf = buf[2+n:]
case TypeObject: case TypeObject:
n, err := Decode(&prop.obj, data, true) n, err := Decode(&prop.obj, buf, true)
if err != nil { if err != nil {
return 0, err return 0, err
} }
data = data[n:] buf = buf[n:]
case TypeNull, typeUndefined, typeUnsupported: case TypeNull, typeUndefined, typeUnsupported:
prop.dtype = TypeNull prop.dtype = TypeNull
case typeEcmaArray: case typeEcmaArray:
data = data[4:] buf = buf[4:]
n, err := Decode(&prop.obj, data, true) n, err := Decode(&prop.obj, buf, true)
if err != nil { if err != nil {
return 0, err return 0, err
} }
data = data[n:] buf = buf[n:]
case TypeObjectEnd: case TypeObjectEnd:
return 0, ErrUnexpectedEnd return 0, ErrUnexpectedEnd
@ -388,102 +401,107 @@ func PropDecode(prop *Property, data []byte, decodeName bool) (int, error) {
return 0, ErrUnimplemented return 0, ErrUnimplemented
} }
return sz - len(data), nil return sz - len(buf), nil
} }
// Encode serializes an Object into its AMF representation. // Encode encodes an Object into its AMF representation.
func Encode(obj *Object, dst []byte) ([]byte, error) { func Encode(obj *Object, buf []byte) ([]byte, error) {
if len(dst) < 5 { if len(buf) < 5 {
return nil, ErrShortBuffer return nil, ErrShortBuffer
} }
dst[0] = TypeObject buf[0] = TypeObject
dst = dst[1:] buf = buf[1:]
for i := 0; i < len(obj.Props); i++ { for i := 0; i < len(obj.Props); i++ {
var err error var err error
dst, err = PropEncode(&obj.Props[i], dst) buf, err = PropEncode(&obj.Props[i], buf)
if err != nil { if err != nil {
return nil, err return nil, err
} }
} }
if len(dst) < 4 { if len(buf) < 4 {
return nil, ErrShortBuffer return nil, ErrShortBuffer
} }
return EncodeInt24(dst, TypeObjectEnd) return EncodeInt24(buf, TypeObjectEnd)
} }
func EncodeEcmaArray(obj *Object, dst []byte) ([]byte, error) { // EncodeEcmaArray encodes an ECMA array.
if len(dst) < 5 { func EncodeEcmaArray(obj *Object, buf []byte) ([]byte, error) {
if len(buf) < 5 {
return nil, ErrShortBuffer return nil, ErrShortBuffer
} }
dst[0] = typeEcmaArray buf[0] = typeEcmaArray
dst = dst[1:] buf = buf[1:]
binary.BigEndian.PutUint32(dst[:4], uint32(len(obj.Props))) binary.BigEndian.PutUint32(buf[:4], uint32(len(obj.Props)))
dst = dst[4:] buf = buf[4:]
for i := 0; i < len(obj.Props); i++ { for i := 0; i < len(obj.Props); i++ {
var err error var err error
dst, err = PropEncode(&obj.Props[i], dst) buf, err = PropEncode(&obj.Props[i], buf)
if err != nil { if err != nil {
return nil, err return nil, err
} }
} }
if len(dst) < 4 { if len(buf) < 4 {
return nil, ErrShortBuffer return nil, ErrShortBuffer
} }
return EncodeInt24(dst, TypeObjectEnd) return EncodeInt24(buf, TypeObjectEnd)
} }
func EncodeArray(obj *Object, dst []byte) ([]byte, error) { // EncodeArray encodes an array.
if len(dst) < 5 { func EncodeArray(obj *Object, buf []byte) ([]byte, error) {
if len(buf) < 5 {
return nil, ErrShortBuffer return nil, ErrShortBuffer
} }
dst[0] = typeStrictArray buf[0] = typeStrictArray
dst = dst[1:] buf = buf[1:]
binary.BigEndian.PutUint32(dst[:4], uint32(len(obj.Props))) binary.BigEndian.PutUint32(buf[:4], uint32(len(obj.Props)))
dst = dst[4:] buf = buf[4:]
for i := 0; i < len(obj.Props); i++ { for i := 0; i < len(obj.Props); i++ {
var err error var err error
dst, err = PropEncode(&obj.Props[i], dst) buf, err = PropEncode(&obj.Props[i], buf)
if err != nil { if err != nil {
return nil, err return nil, err
} }
} }
return dst, nil return buf, nil
} }
func Decode(obj *Object, data []byte, decodeName bool) (int, error) { // Decode decodes an object. Property names are only decoded if decodeName is true.
sz := len(data) func Decode(obj *Object, buf []byte, decodeName bool) (int, error) {
sz := len(buf)
obj.Props = obj.Props[:0] obj.Props = obj.Props[:0]
for len(data) != 0 { for len(buf) != 0 {
if len(data) >= 3 && DecodeInt24(data[:3]) == TypeObjectEnd { if len(buf) >= 3 && DecodeInt24(buf[:3]) == TypeObjectEnd {
data = data[3:] buf = buf[3:]
break break
} }
var prop Property var prop Property
n, err := PropDecode(&prop, data, decodeName) n, err := PropDecode(&prop, buf, decodeName)
if err != nil { if err != nil {
return 0, err return 0, err
} }
data = data[n:] buf = buf[n:]
obj.Props = append(obj.Props, prop) 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 >= 0 {
if idx < int32(len(obj.Props)) { if idx < len(obj.Props) {
return &obj.Props[idx] return &obj.Props[idx]
} }
} else { } else {
@ -493,5 +511,5 @@ func GetProp(obj *Object, name string, idx int32) *Property {
} }
} }
} }
return &Property{} return nil
} }