2018-08-24 03:55:36 +03:00
|
|
|
/*
|
|
|
|
NAME
|
|
|
|
amf.go
|
|
|
|
|
|
|
|
DESCRIPTION
|
2019-01-11 23:18:33 +03:00
|
|
|
Action Message Format (AMF) encoding/decoding functions.
|
|
|
|
See https://en.wikipedia.org/wiki/Action_Message_Format.
|
2018-08-24 03:55:36 +03:00
|
|
|
|
|
|
|
AUTHORS
|
|
|
|
Saxon Nelson-Milton <saxon@ausocean.org>
|
|
|
|
Dan Kortschak <dan@ausocean.org>
|
|
|
|
Jake Lane <jake@ausocean.org>
|
2019-01-11 23:12:34 +03:00
|
|
|
Alan Noble <alan@ausocean.org>
|
2018-08-24 03:55:36 +03:00
|
|
|
|
|
|
|
LICENSE
|
2019-01-11 23:12:34 +03:00
|
|
|
amf.go is Copyright (C) 2017-2019 the Australian Ocean Lab (AusOcean)
|
2018-08-24 03:55:36 +03:00
|
|
|
|
|
|
|
It is free software: you can redistribute it and/or modify them
|
|
|
|
under the terms of the GNU General Public License as published by the
|
|
|
|
Free Software Foundation, either version 3 of the License, or (at your
|
|
|
|
option) any later version.
|
|
|
|
|
|
|
|
It is distributed in the hope that it will be useful, but WITHOUT
|
|
|
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
|
|
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
|
|
for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
2018-08-30 14:01:19 +03:00
|
|
|
along with revid in gpl.txt. If not, see http://www.gnu.org/licenses.
|
2018-08-24 03:55:36 +03:00
|
|
|
|
2018-08-30 14:01:19 +03:00
|
|
|
Derived from librtmp under the GNU Lesser General Public License 2.1
|
|
|
|
Copyright (C) 2005-2008 Team XBMC http://www.xbmc.org
|
|
|
|
Copyright (C) 2008-2009 Andrej Stepanchuk
|
|
|
|
Copyright (C) 2009-2010 Howard Chu
|
2018-08-24 03:55:36 +03:00
|
|
|
*/
|
2019-01-11 23:43:27 +03:00
|
|
|
|
|
|
|
// amf implements Action Message Format (AMF) encoding and decoding.
|
|
|
|
// See https://en.wikipedia.org/wiki/Action_Message_Format.
|
|
|
|
package amf
|
2018-08-24 00:17:13 +03:00
|
|
|
|
2018-08-24 03:03:05 +03:00
|
|
|
import (
|
2018-09-06 07:13:40 +03:00
|
|
|
"encoding/binary"
|
2019-01-12 06:48:50 +03:00
|
|
|
"errors"
|
2018-09-12 15:12:10 +03:00
|
|
|
"math"
|
2018-08-24 03:03:05 +03:00
|
|
|
)
|
|
|
|
|
2019-01-11 23:43:27 +03:00
|
|
|
// AMF data types.
|
2019-01-12 01:26:57 +03:00
|
|
|
// NB: we export these sparingly.
|
2019-01-11 23:12:34 +03:00
|
|
|
const (
|
2019-01-12 01:26:57 +03:00
|
|
|
typeNumber = iota
|
|
|
|
typeBoolean
|
|
|
|
typeString
|
|
|
|
TypeObject // ToDo: consider not exporting this
|
|
|
|
typeMovieClip // reserved, not implemented
|
|
|
|
TypeNull // ToDo: consider not exporting this
|
|
|
|
typeUndefined
|
|
|
|
typeReference
|
|
|
|
typeEcmaArray
|
|
|
|
TypeObjectEnd // ToDo: consider not exporting this
|
|
|
|
typeStrictArray
|
|
|
|
typeDate
|
|
|
|
typeLongString
|
|
|
|
typeUnsupported
|
|
|
|
typeRecordset // reserved, not implemented
|
|
|
|
typeXmlDoc
|
|
|
|
typeTypedObject
|
|
|
|
typeAvmplus // reserved, not implemented
|
|
|
|
typeInvalid = 0xff
|
2018-08-24 00:19:47 +03:00
|
|
|
)
|
|
|
|
|
2019-01-12 01:26:57 +03:00
|
|
|
// AMF represents an AMF message (object), which is simply a collection of properties.
|
|
|
|
type Object struct {
|
2019-01-11 23:43:27 +03:00
|
|
|
Props []Property // ToDo: consider not exporting this
|
2019-01-11 23:12:34 +03:00
|
|
|
}
|
|
|
|
|
2019-01-11 23:43:27 +03:00
|
|
|
// Property represents an AMF property.
|
|
|
|
type Property struct {
|
2019-01-12 01:26:57 +03:00
|
|
|
name string
|
|
|
|
dtype uint8
|
2019-01-11 23:12:34 +03:00
|
|
|
number float64
|
2019-01-12 01:26:57 +03:00
|
|
|
str string
|
|
|
|
obj Object
|
2019-01-11 23:12:34 +03:00
|
|
|
}
|
|
|
|
|
2019-01-12 06:48:50 +03:00
|
|
|
// AMF errors.
|
|
|
|
var (
|
|
|
|
ErrShortBuffer = errors.New("amf: short buffer")
|
|
|
|
ErrEndOfBuffer = errors.New("amf: end of buffer")
|
|
|
|
ErrInvalidType = errors.New("amf: invalid type")
|
|
|
|
ErrUnimplemented = errors.New("amf: unimplemented feature")
|
|
|
|
ErrDecodingName = errors.New("amf: name decoding error")
|
|
|
|
ErrDecodingString = errors.New("amf: string decoding error")
|
2019-01-12 07:25:12 +03:00
|
|
|
ErrUnexpectedEnd = errors.New("amf: unexpected end")
|
2019-01-12 06:48:50 +03:00
|
|
|
)
|
|
|
|
|
|
|
|
// Basic decoding funcions.
|
2019-01-11 23:43:27 +03:00
|
|
|
func DecodeInt16(data []byte) uint16 {
|
2019-01-11 23:12:34 +03:00
|
|
|
return uint16(binary.BigEndian.Uint16(data))
|
2018-08-24 16:00:40 +03:00
|
|
|
}
|
2018-08-24 00:28:22 +03:00
|
|
|
|
2019-01-11 23:43:27 +03:00
|
|
|
func DecodeInt24(data []byte) uint32 {
|
2018-09-12 15:12:10 +03:00
|
|
|
return uint32(data[0])<<16 | uint32(data[1])<<8 | uint32(data[2])
|
2018-08-24 16:00:40 +03:00
|
|
|
}
|
2018-08-24 00:28:22 +03:00
|
|
|
|
2019-01-11 23:43:27 +03:00
|
|
|
func DecodeInt32(data []byte) uint32 {
|
2019-01-11 23:12:34 +03:00
|
|
|
return uint32(binary.BigEndian.Uint32(data))
|
2018-08-24 16:00:40 +03:00
|
|
|
}
|
|
|
|
|
2019-01-11 23:43:27 +03:00
|
|
|
func DecodeString(data []byte) string {
|
|
|
|
n := DecodeInt16(data)
|
2018-09-12 15:12:10 +03:00
|
|
|
return string(data[2 : 2+n])
|
2018-08-24 00:28:22 +03:00
|
|
|
}
|
|
|
|
|
2019-01-11 23:43:27 +03:00
|
|
|
func DecodeLongString(data []byte) string {
|
|
|
|
n := DecodeInt32(data)
|
2018-09-12 15:12:10 +03:00
|
|
|
return string(data[2 : 2+n])
|
2018-08-24 16:00:40 +03:00
|
|
|
}
|
2018-08-24 00:28:22 +03:00
|
|
|
|
2019-01-11 23:43:27 +03:00
|
|
|
func DecodeNumber(data []byte) float64 {
|
2018-09-12 15:12:10 +03:00
|
|
|
return math.Float64frombits(binary.BigEndian.Uint64(data))
|
2018-08-24 16:00:40 +03:00
|
|
|
}
|
2018-08-24 00:28:22 +03:00
|
|
|
|
2019-01-11 23:43:27 +03:00
|
|
|
func DecodeBoolean(data []byte) bool {
|
2018-09-12 15:12:10 +03:00
|
|
|
return data[0] != 0
|
2018-08-24 16:00:40 +03:00
|
|
|
}
|
2018-08-24 00:28:22 +03:00
|
|
|
|
2019-01-12 06:48:50 +03:00
|
|
|
// Basic encoding functions.
|
|
|
|
func EncodeInt24(dst []byte, val int32) ([]byte, error) {
|
2018-09-14 15:36:13 +03:00
|
|
|
if len(dst) < 3 {
|
2019-01-12 06:48:50 +03:00
|
|
|
return nil, ErrShortBuffer
|
2018-08-24 00:28:22 +03:00
|
|
|
}
|
2018-09-14 15:36:13 +03:00
|
|
|
dst[0] = byte(val >> 16)
|
|
|
|
dst[1] = byte(val >> 8)
|
|
|
|
dst[2] = byte(val)
|
|
|
|
if len(dst) == 3 {
|
2019-01-12 06:48:50 +03:00
|
|
|
return nil, ErrEndOfBuffer
|
2018-09-14 15:36:13 +03:00
|
|
|
}
|
2019-01-12 06:48:50 +03:00
|
|
|
return dst[3:], nil
|
2018-08-24 16:00:40 +03:00
|
|
|
}
|
2018-08-24 00:28:22 +03:00
|
|
|
|
2019-01-12 06:48:50 +03:00
|
|
|
func EncodeInt32(dst []byte, val int32) ([]byte, error) {
|
2018-09-14 15:36:13 +03:00
|
|
|
if len(dst) < 4 {
|
2019-01-12 06:48:50 +03:00
|
|
|
return nil, ErrShortBuffer
|
2018-09-14 15:36:13 +03:00
|
|
|
}
|
|
|
|
binary.BigEndian.PutUint32(dst, uint32(val))
|
|
|
|
if len(dst) == 4 {
|
2019-01-12 06:48:50 +03:00
|
|
|
return nil, ErrEndOfBuffer
|
2018-08-24 00:28:22 +03:00
|
|
|
}
|
2019-01-12 06:48:50 +03:00
|
|
|
return dst[4:], nil
|
2018-08-24 16:00:40 +03:00
|
|
|
}
|
2018-08-24 00:28:22 +03:00
|
|
|
|
2019-01-12 06:48:50 +03:00
|
|
|
func EncodeString(dst []byte, val string) ([]byte, error) {
|
2018-09-14 15:36:13 +03:00
|
|
|
const typeSize = 1
|
|
|
|
if len(val) < 65536 && len(val)+typeSize+binary.Size(int16(0)) > len(dst) {
|
2019-01-12 06:48:50 +03:00
|
|
|
return nil, ErrShortBuffer
|
2018-08-24 00:28:22 +03:00
|
|
|
}
|
2019-01-12 01:26:57 +03:00
|
|
|
|
2018-09-14 15:36:13 +03:00
|
|
|
if len(val)+typeSize+binary.Size(int32(0)) > len(dst) {
|
2019-01-12 06:48:50 +03:00
|
|
|
return nil, ErrShortBuffer
|
2018-09-06 07:13:40 +03:00
|
|
|
}
|
2018-09-14 15:36:13 +03:00
|
|
|
|
|
|
|
if len(val) < 65536 {
|
2019-01-12 01:26:57 +03:00
|
|
|
dst[0] = typeString
|
2018-09-14 15:36:13 +03:00
|
|
|
dst = dst[1:]
|
|
|
|
binary.BigEndian.PutUint16(dst[:2], uint16(len(val)))
|
|
|
|
dst = dst[2:]
|
|
|
|
copy(dst, val)
|
|
|
|
if len(dst) == len(val) {
|
2019-01-12 06:48:50 +03:00
|
|
|
return nil, ErrEndOfBuffer
|
2018-09-14 15:36:13 +03:00
|
|
|
}
|
2019-01-12 06:48:50 +03:00
|
|
|
return dst[len(val):], nil
|
2018-09-14 15:36:13 +03:00
|
|
|
}
|
2019-01-12 01:26:57 +03:00
|
|
|
|
|
|
|
dst[0] = typeLongString
|
2018-09-14 15:36:13 +03:00
|
|
|
dst = dst[1:]
|
|
|
|
binary.BigEndian.PutUint32(dst[:4], uint32(len(val)))
|
|
|
|
dst = dst[4:]
|
|
|
|
copy(dst, val)
|
|
|
|
if len(dst) == len(val) {
|
2019-01-12 06:48:50 +03:00
|
|
|
return nil, ErrEndOfBuffer
|
2018-08-24 16:00:40 +03:00
|
|
|
}
|
2019-01-12 06:48:50 +03:00
|
|
|
return dst[len(val):], nil
|
2018-08-24 00:28:22 +03:00
|
|
|
}
|
|
|
|
|
2019-01-12 06:48:50 +03:00
|
|
|
func EncodeNumber(dst []byte, val float64) ([]byte, error) {
|
2018-09-15 00:06:48 +03:00
|
|
|
if len(dst) < 9 {
|
2019-01-12 06:48:50 +03:00
|
|
|
return nil, ErrShortBuffer
|
2018-08-24 00:28:22 +03:00
|
|
|
}
|
2019-01-12 01:26:57 +03:00
|
|
|
dst[0] = typeNumber
|
2018-09-15 00:06:48 +03:00
|
|
|
dst = dst[1:]
|
|
|
|
binary.BigEndian.PutUint64(dst, math.Float64bits(val))
|
2019-01-12 06:48:50 +03:00
|
|
|
return dst[8:], nil
|
2018-08-24 00:28:22 +03:00
|
|
|
}
|
|
|
|
|
2019-01-12 06:48:50 +03:00
|
|
|
func EncodeBoolean(dst []byte, val bool) ([]byte, error) {
|
2018-09-14 15:45:31 +03:00
|
|
|
if len(dst) < 2 {
|
2019-01-12 06:48:50 +03:00
|
|
|
return nil, ErrShortBuffer
|
2018-08-24 00:28:22 +03:00
|
|
|
}
|
2019-01-12 01:26:57 +03:00
|
|
|
dst[0] = typeBoolean
|
2018-09-14 15:45:31 +03:00
|
|
|
if val {
|
|
|
|
dst[1] = 1
|
2018-08-24 00:28:22 +03:00
|
|
|
}
|
2018-09-14 15:45:31 +03:00
|
|
|
if len(dst) == 2 {
|
2019-01-12 06:48:50 +03:00
|
|
|
return nil, ErrEndOfBuffer
|
2018-09-14 15:45:31 +03:00
|
|
|
}
|
2019-01-12 06:48:50 +03:00
|
|
|
return dst[2:], nil
|
2018-09-14 15:45:31 +03:00
|
|
|
|
2018-08-24 16:00:40 +03:00
|
|
|
}
|
2018-08-24 00:28:22 +03:00
|
|
|
|
2019-01-12 06:48:50 +03:00
|
|
|
func EncodeNamedString(dst []byte, key, val string) ([]byte, error) {
|
2018-09-15 02:44:27 +03:00
|
|
|
if 2+len(key) > len(dst) {
|
2019-01-12 06:48:50 +03:00
|
|
|
return nil, ErrShortBuffer
|
2018-08-24 16:00:40 +03:00
|
|
|
}
|
2018-09-06 07:13:40 +03:00
|
|
|
binary.BigEndian.PutUint16(dst[:2], uint16(len(key)))
|
2018-09-15 02:44:27 +03:00
|
|
|
dst = dst[2:]
|
|
|
|
copy(dst, key)
|
|
|
|
if len(key) == len(dst) {
|
2019-01-12 06:48:50 +03:00
|
|
|
return nil, ErrEndOfBuffer
|
2018-09-15 02:44:27 +03:00
|
|
|
}
|
2019-01-11 23:43:27 +03:00
|
|
|
return EncodeString(dst[len(key):], val)
|
2018-08-24 00:28:22 +03:00
|
|
|
}
|
|
|
|
|
2019-01-12 06:48:50 +03:00
|
|
|
func EncodeNamedNumber(dst []byte, key string, val float64) ([]byte, error) {
|
2018-09-15 02:44:27 +03:00
|
|
|
if 2+len(key) > len(dst) {
|
2019-01-12 06:48:50 +03:00
|
|
|
return nil, ErrShortBuffer
|
2018-08-24 00:28:22 +03:00
|
|
|
}
|
2018-09-06 07:13:40 +03:00
|
|
|
binary.BigEndian.PutUint16(dst[:2], uint16(len(key)))
|
2018-09-15 02:44:27 +03:00
|
|
|
dst = dst[2:]
|
|
|
|
copy(dst, key)
|
|
|
|
if len(key) == len(dst) {
|
2019-01-12 06:48:50 +03:00
|
|
|
return nil, ErrEndOfBuffer
|
2018-09-15 02:44:27 +03:00
|
|
|
}
|
2019-01-11 23:43:27 +03:00
|
|
|
return EncodeNumber(dst[len(key):], val)
|
2018-08-24 00:28:22 +03:00
|
|
|
}
|
|
|
|
|
2019-01-12 06:48:50 +03:00
|
|
|
func EncodeNamedBoolean(dst []byte, key string, val bool) ([]byte, error) {
|
2018-09-15 02:44:27 +03:00
|
|
|
if 2+len(key) > len(dst) {
|
2019-01-12 06:48:50 +03:00
|
|
|
return nil, ErrShortBuffer
|
2018-08-24 00:28:22 +03:00
|
|
|
}
|
2018-09-06 07:13:40 +03:00
|
|
|
binary.BigEndian.PutUint16(dst[:2], uint16(len(key)))
|
2018-09-15 02:44:27 +03:00
|
|
|
dst = dst[2:]
|
|
|
|
copy(dst, key)
|
|
|
|
if len(key) == len(dst) {
|
2019-01-12 06:48:50 +03:00
|
|
|
return nil, ErrEndOfBuffer
|
2018-09-15 02:44:27 +03:00
|
|
|
}
|
2019-01-11 23:43:27 +03:00
|
|
|
return EncodeBoolean(dst[len(key):], val)
|
2018-08-24 16:00:40 +03:00
|
|
|
}
|
|
|
|
|
2019-01-12 06:48:50 +03:00
|
|
|
// Property functions.
|
|
|
|
|
2019-01-11 23:43:27 +03:00
|
|
|
func PropSetName(prop *Property, name string) {
|
2019-01-11 23:12:34 +03:00
|
|
|
prop.name = name
|
2018-08-24 16:00:40 +03:00
|
|
|
}
|
|
|
|
|
2019-01-11 23:43:27 +03:00
|
|
|
func PropGetNumber(prop *Property) float64 {
|
2019-01-12 01:26:57 +03:00
|
|
|
return prop.number
|
2018-08-24 00:28:22 +03:00
|
|
|
}
|
|
|
|
|
2019-01-11 23:43:27 +03:00
|
|
|
func PropGetString(prop *Property) string {
|
2019-01-12 01:26:57 +03:00
|
|
|
if prop.dtype == typeString {
|
|
|
|
return prop.str
|
2018-08-24 00:28:22 +03:00
|
|
|
}
|
2018-09-06 09:40:15 +03:00
|
|
|
return ""
|
2018-08-24 00:28:22 +03:00
|
|
|
}
|
|
|
|
|
2019-01-12 01:26:57 +03:00
|
|
|
func PropGetObject(prop *Property, obj *Object) {
|
|
|
|
if prop.dtype == TypeObject {
|
|
|
|
*obj = prop.obj
|
2018-08-24 00:28:22 +03:00
|
|
|
} else {
|
2019-01-12 01:26:57 +03:00
|
|
|
*obj = Object{}
|
2018-08-24 00:28:22 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-12 06:48:50 +03:00
|
|
|
// PropEncode encodes a property.
|
|
|
|
func PropEncode(p *Property, dst []byte) ([]byte, error) {
|
2019-01-12 01:26:57 +03:00
|
|
|
if p.dtype == typeInvalid {
|
2019-01-12 06:48:50 +03:00
|
|
|
return nil, ErrShortBuffer
|
2018-08-24 00:28:22 +03:00
|
|
|
}
|
|
|
|
|
2019-01-12 01:26:57 +03:00
|
|
|
if p.dtype != TypeNull && len(p.name)+2+1 >= len(dst) {
|
2019-01-12 06:48:50 +03:00
|
|
|
return nil, ErrShortBuffer
|
2018-08-24 16:00:40 +03:00
|
|
|
}
|
2018-08-24 00:28:22 +03:00
|
|
|
|
2019-01-12 01:26:57 +03:00
|
|
|
if p.dtype != TypeNull && len(p.name) != 0 {
|
2019-01-11 23:12:34 +03:00
|
|
|
binary.BigEndian.PutUint16(dst[:2], uint16(len(p.name)))
|
2018-09-15 03:53:45 +03:00
|
|
|
dst = dst[2:]
|
2019-01-11 23:12:34 +03:00
|
|
|
copy(dst, p.name)
|
|
|
|
dst = dst[len(p.name):]
|
|
|
|
}
|
|
|
|
|
2019-01-12 06:48:50 +03:00
|
|
|
var err error
|
2019-01-12 01:26:57 +03:00
|
|
|
switch p.dtype {
|
|
|
|
case typeNumber:
|
2019-01-12 06:48:50 +03:00
|
|
|
dst, err = EncodeNumber(dst, p.number)
|
2019-01-12 01:26:57 +03:00
|
|
|
case typeBoolean:
|
2019-01-12 06:48:50 +03:00
|
|
|
dst, err = EncodeBoolean(dst, p.number != 0)
|
2019-01-12 01:26:57 +03:00
|
|
|
case typeString:
|
2019-01-12 06:48:50 +03:00
|
|
|
dst, err = EncodeString(dst, p.str)
|
2019-01-12 01:26:57 +03:00
|
|
|
case TypeNull:
|
2018-09-15 03:53:45 +03:00
|
|
|
if len(dst) < 2 {
|
2019-01-12 06:48:50 +03:00
|
|
|
return nil, ErrShortBuffer
|
2018-08-24 16:00:40 +03:00
|
|
|
}
|
2019-01-12 01:26:57 +03:00
|
|
|
dst[0] = TypeNull
|
2018-09-15 04:55:07 +03:00
|
|
|
dst = dst[1:]
|
2019-01-12 01:26:57 +03:00
|
|
|
case TypeObject:
|
2019-01-12 06:48:50 +03:00
|
|
|
dst, err = Encode(&p.obj, dst)
|
2019-01-12 01:26:57 +03:00
|
|
|
case typeEcmaArray:
|
2019-01-12 06:48:50 +03:00
|
|
|
dst, err = EncodeEcmaArray(&p.obj, dst)
|
2019-01-12 01:26:57 +03:00
|
|
|
case typeStrictArray:
|
2019-01-12 06:48:50 +03:00
|
|
|
dst, err = EncodeArray(&p.obj, dst)
|
2018-08-24 16:00:40 +03:00
|
|
|
default:
|
2019-01-12 06:48:50 +03:00
|
|
|
dst, err = nil, ErrInvalidType
|
2018-08-24 16:00:40 +03:00
|
|
|
}
|
2019-01-12 06:48:50 +03:00
|
|
|
return dst, err
|
2018-08-24 16:00:40 +03:00
|
|
|
}
|
2018-08-24 00:28:22 +03:00
|
|
|
|
2019-01-12 07:25:12 +03:00
|
|
|
// PropDecode decodes a property, returning the number of bytes consumed from the supplied buffer.
|
|
|
|
func PropDecode(prop *Property, data []byte, decodeName bool) (int, error) {
|
2019-01-11 23:12:34 +03:00
|
|
|
prop.name = ""
|
2018-08-24 00:28:22 +03:00
|
|
|
|
2019-01-12 07:25:12 +03:00
|
|
|
sz := len(data)
|
2018-09-16 02:42:59 +03:00
|
|
|
if len(data) == 0 {
|
2019-01-12 07:25:12 +03:00
|
|
|
return 0, ErrEndOfBuffer
|
2018-08-24 00:28:22 +03:00
|
|
|
}
|
|
|
|
|
2019-01-12 07:25:12 +03:00
|
|
|
if decodeName {
|
|
|
|
if len(data) < 4 {
|
|
|
|
return 0, ErrShortBuffer
|
|
|
|
}
|
|
|
|
n := DecodeInt16(data[:2])
|
|
|
|
if int(n) > len(data)-2 {
|
|
|
|
return 0, ErrDecodingName
|
2018-08-24 00:28:22 +03:00
|
|
|
}
|
|
|
|
|
2019-01-11 23:43:27 +03:00
|
|
|
prop.name = DecodeString(data)
|
2019-01-12 07:25:12 +03:00
|
|
|
data = data[2+n:]
|
2018-08-24 00:28:22 +03:00
|
|
|
}
|
|
|
|
|
2018-09-16 02:42:59 +03:00
|
|
|
if len(data) == 0 {
|
2019-01-12 07:25:12 +03:00
|
|
|
return 0, ErrEndOfBuffer
|
2018-08-24 00:28:22 +03:00
|
|
|
}
|
|
|
|
|
2019-01-12 01:26:57 +03:00
|
|
|
prop.dtype = uint8(data[0])
|
2018-09-16 02:42:59 +03:00
|
|
|
data = data[1:]
|
2018-08-24 00:28:22 +03:00
|
|
|
|
2019-01-12 01:26:57 +03:00
|
|
|
switch prop.dtype {
|
|
|
|
case typeNumber:
|
2018-09-16 02:42:59 +03:00
|
|
|
if len(data) < 8 {
|
2019-01-12 07:25:12 +03:00
|
|
|
return 0, ErrShortBuffer
|
2018-08-24 00:28:22 +03:00
|
|
|
}
|
2019-01-12 01:26:57 +03:00
|
|
|
prop.number = DecodeNumber(data[:8])
|
2018-09-16 02:42:59 +03:00
|
|
|
data = data[8:]
|
2018-08-24 00:28:22 +03:00
|
|
|
|
2019-01-12 01:26:57 +03:00
|
|
|
case typeBoolean:
|
2019-01-12 07:25:12 +03:00
|
|
|
return 0, ErrUnimplemented
|
2018-08-28 13:40:13 +03:00
|
|
|
|
2019-01-12 01:26:57 +03:00
|
|
|
case typeString:
|
2019-01-12 07:25:12 +03:00
|
|
|
n := DecodeInt16(data[:2])
|
|
|
|
if len(data) < int(n+2) {
|
|
|
|
return 0, ErrShortBuffer
|
2018-08-24 00:28:22 +03:00
|
|
|
}
|
2019-01-12 01:26:57 +03:00
|
|
|
prop.str = DecodeString(data)
|
2019-01-12 07:25:12 +03:00
|
|
|
data = data[2+n:]
|
2018-08-24 00:28:22 +03:00
|
|
|
|
2019-01-12 01:26:57 +03:00
|
|
|
case TypeObject:
|
2019-01-12 07:25:12 +03:00
|
|
|
n, err := Decode(&prop.obj, data, true)
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
2018-08-24 00:28:22 +03:00
|
|
|
}
|
2019-01-12 07:25:12 +03:00
|
|
|
data = data[n:]
|
2018-08-24 00:28:22 +03:00
|
|
|
|
|
|
|
|
2019-01-12 01:26:57 +03:00
|
|
|
case TypeNull, typeUndefined, typeUnsupported:
|
|
|
|
prop.dtype = TypeNull
|
2018-08-24 00:28:22 +03:00
|
|
|
|
2019-01-12 01:26:57 +03:00
|
|
|
case typeEcmaArray:
|
2018-09-16 02:42:59 +03:00
|
|
|
data = data[4:]
|
2019-01-12 07:25:12 +03:00
|
|
|
n, err := Decode(&prop.obj, data, true)
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
2018-08-24 00:28:22 +03:00
|
|
|
}
|
2019-01-12 07:25:12 +03:00
|
|
|
data = data[n:]
|
2018-08-24 00:28:22 +03:00
|
|
|
|
2019-01-12 01:26:57 +03:00
|
|
|
case TypeObjectEnd:
|
2019-01-12 07:25:12 +03:00
|
|
|
return 0, ErrUnexpectedEnd
|
2018-08-28 13:40:13 +03:00
|
|
|
|
2018-08-24 00:28:22 +03:00
|
|
|
default:
|
2019-01-12 07:25:12 +03:00
|
|
|
return 0, ErrUnimplemented
|
2018-08-24 00:28:22 +03:00
|
|
|
}
|
|
|
|
|
2019-01-12 07:25:12 +03:00
|
|
|
return sz - len(data), nil
|
2018-08-24 00:28:22 +03:00
|
|
|
}
|
|
|
|
|
2019-01-11 23:43:27 +03:00
|
|
|
func PropReset(prop *Property) {
|
2019-01-12 01:26:57 +03:00
|
|
|
if prop.dtype == TypeObject || prop.dtype == typeEcmaArray ||
|
|
|
|
prop.dtype == typeStrictArray {
|
|
|
|
Reset(&prop.obj)
|
2018-08-24 16:00:40 +03:00
|
|
|
} else {
|
2019-01-12 01:26:57 +03:00
|
|
|
prop.str = ""
|
2018-08-24 00:28:22 +03:00
|
|
|
}
|
2019-01-12 01:26:57 +03:00
|
|
|
prop.dtype = typeInvalid
|
2018-08-24 00:28:22 +03:00
|
|
|
}
|
|
|
|
|
2019-01-12 06:48:50 +03:00
|
|
|
// Encode serializes an Object into its AMF representation.
|
|
|
|
func Encode(obj *Object, dst []byte) ([]byte, error) {
|
2018-09-15 04:12:16 +03:00
|
|
|
if len(dst) < 5 {
|
2019-01-12 06:48:50 +03:00
|
|
|
return nil, ErrShortBuffer
|
2018-08-24 00:28:22 +03:00
|
|
|
}
|
|
|
|
|
2019-01-12 01:26:57 +03:00
|
|
|
dst[0] = TypeObject
|
2018-09-15 04:12:16 +03:00
|
|
|
dst = dst[1:]
|
2018-08-24 16:00:40 +03:00
|
|
|
|
2019-01-12 01:26:57 +03:00
|
|
|
for i := 0; i < len(obj.Props); i++ {
|
2019-01-12 06:48:50 +03:00
|
|
|
var err error
|
|
|
|
dst, err = PropEncode(&obj.Props[i], dst)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2018-08-24 16:00:40 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-15 04:12:16 +03:00
|
|
|
if len(dst) < 4 {
|
2019-01-12 06:48:50 +03:00
|
|
|
return nil, ErrShortBuffer
|
2018-08-24 00:28:22 +03:00
|
|
|
}
|
2019-01-12 01:26:57 +03:00
|
|
|
return EncodeInt24(dst, TypeObjectEnd)
|
2018-08-24 00:28:22 +03:00
|
|
|
}
|
|
|
|
|
2019-01-12 06:48:50 +03:00
|
|
|
func EncodeEcmaArray(obj *Object, dst []byte) ([]byte, error) {
|
2018-09-15 04:19:17 +03:00
|
|
|
if len(dst) < 5 {
|
2019-01-12 06:48:50 +03:00
|
|
|
return nil, ErrShortBuffer
|
2018-08-24 16:00:40 +03:00
|
|
|
}
|
2018-08-24 00:28:22 +03:00
|
|
|
|
2019-01-12 01:26:57 +03:00
|
|
|
dst[0] = typeEcmaArray
|
2018-09-15 04:19:17 +03:00
|
|
|
dst = dst[1:]
|
2019-01-12 01:26:57 +03:00
|
|
|
binary.BigEndian.PutUint32(dst[:4], uint32(len(obj.Props)))
|
2018-09-15 04:19:17 +03:00
|
|
|
dst = dst[4:]
|
2018-08-24 16:00:40 +03:00
|
|
|
|
2019-01-12 01:26:57 +03:00
|
|
|
for i := 0; i < len(obj.Props); i++ {
|
2019-01-12 06:48:50 +03:00
|
|
|
var err error
|
|
|
|
dst, err = PropEncode(&obj.Props[i], dst)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2018-08-24 00:28:22 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-15 04:19:17 +03:00
|
|
|
if len(dst) < 4 {
|
2019-01-12 06:48:50 +03:00
|
|
|
return nil, ErrShortBuffer
|
2018-08-24 00:28:22 +03:00
|
|
|
}
|
2019-01-12 01:26:57 +03:00
|
|
|
return EncodeInt24(dst, TypeObjectEnd)
|
2018-08-24 00:28:22 +03:00
|
|
|
}
|
|
|
|
|
2019-01-12 06:48:50 +03:00
|
|
|
func EncodeArray(obj *Object, dst []byte) ([]byte, error) {
|
2018-09-15 04:22:36 +03:00
|
|
|
if len(dst) < 5 {
|
2019-01-12 06:48:50 +03:00
|
|
|
return nil, ErrShortBuffer
|
2018-08-24 16:00:40 +03:00
|
|
|
}
|
2018-08-24 00:28:22 +03:00
|
|
|
|
2019-01-12 01:26:57 +03:00
|
|
|
dst[0] = typeStrictArray
|
2018-09-15 04:22:36 +03:00
|
|
|
dst = dst[1:]
|
2019-01-12 01:26:57 +03:00
|
|
|
binary.BigEndian.PutUint32(dst[:4], uint32(len(obj.Props)))
|
2018-09-15 04:22:36 +03:00
|
|
|
dst = dst[4:]
|
2018-08-24 00:28:22 +03:00
|
|
|
|
2019-01-12 01:26:57 +03:00
|
|
|
for i := 0; i < len(obj.Props); i++ {
|
2019-01-12 06:48:50 +03:00
|
|
|
var err error
|
|
|
|
dst, err = PropEncode(&obj.Props[i], dst)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2018-08-24 16:00:40 +03:00
|
|
|
}
|
2018-08-24 00:28:22 +03:00
|
|
|
}
|
2018-08-24 16:00:40 +03:00
|
|
|
|
2019-01-12 06:48:50 +03:00
|
|
|
return dst, nil
|
2018-08-24 00:28:22 +03:00
|
|
|
}
|
|
|
|
|
2019-01-12 07:25:12 +03:00
|
|
|
func Decode(obj *Object, data []byte, decodeName bool) (int, error) {
|
|
|
|
sz := len(data)
|
2018-08-24 00:28:22 +03:00
|
|
|
|
2019-01-12 01:26:57 +03:00
|
|
|
obj.Props = obj.Props[:0]
|
2018-09-16 03:10:43 +03:00
|
|
|
for len(data) != 0 {
|
2019-01-12 01:26:57 +03:00
|
|
|
if len(data) >= 3 && DecodeInt24(data[:3]) == TypeObjectEnd {
|
2018-09-16 03:10:43 +03:00
|
|
|
data = data[3:]
|
2018-08-24 16:00:40 +03:00
|
|
|
break
|
|
|
|
}
|
|
|
|
|
2019-01-11 23:43:27 +03:00
|
|
|
var prop Property
|
2019-01-12 07:25:12 +03:00
|
|
|
n, err := PropDecode(&prop, data, decodeName)
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
2018-08-24 16:00:40 +03:00
|
|
|
}
|
2019-01-12 07:25:12 +03:00
|
|
|
data = data[n:]
|
2019-01-12 01:26:57 +03:00
|
|
|
obj.Props = append(obj.Props, prop)
|
2018-08-24 00:28:22 +03:00
|
|
|
}
|
|
|
|
|
2019-01-12 07:25:12 +03:00
|
|
|
return sz - len(data), nil
|
2018-08-24 00:28:22 +03:00
|
|
|
}
|
|
|
|
|
2019-01-12 01:26:57 +03:00
|
|
|
func GetProp(obj *Object, name string, idx int32) *Property {
|
2018-09-15 03:50:44 +03:00
|
|
|
if idx >= 0 {
|
2019-01-12 01:26:57 +03:00
|
|
|
if idx < int32(len(obj.Props)) {
|
|
|
|
return &obj.Props[idx]
|
2018-08-24 16:00:40 +03:00
|
|
|
}
|
|
|
|
} else {
|
2019-01-12 01:26:57 +03:00
|
|
|
for i, p := range obj.Props {
|
2019-01-11 23:12:34 +03:00
|
|
|
if p.name == name {
|
2019-01-12 01:26:57 +03:00
|
|
|
return &obj.Props[i]
|
2018-08-24 16:00:40 +03:00
|
|
|
}
|
|
|
|
}
|
2018-08-24 00:28:22 +03:00
|
|
|
}
|
2019-01-12 01:26:57 +03:00
|
|
|
return &Property{}
|
2018-08-24 00:28:22 +03:00
|
|
|
}
|
|
|
|
|
2019-01-12 01:26:57 +03:00
|
|
|
func Reset(obj *Object) {
|
|
|
|
for i := range obj.Props {
|
|
|
|
PropReset(&obj.Props[i])
|
2018-09-15 03:50:44 +03:00
|
|
|
}
|
2019-01-12 01:26:57 +03:00
|
|
|
*obj = Object{}
|
2018-08-24 00:28:22 +03:00
|
|
|
}
|