mirror of https://github.com/gwuhaolin/livego.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
432 lines
9.4 KiB
432 lines
9.4 KiB
package amf |
|
|
|
import ( |
|
"encoding/binary" |
|
"fmt" |
|
"io" |
|
"reflect" |
|
"sort" |
|
"time" |
|
) |
|
|
|
// amf3 polymorphic router |
|
|
|
func (e *Encoder) EncodeAmf3(w io.Writer, val interface{}) (int, error) { |
|
if val == nil { |
|
return e.EncodeAmf3Null(w, true) |
|
} |
|
|
|
v := reflect.ValueOf(val) |
|
if !v.IsValid() { |
|
return e.EncodeAmf3Null(w, true) |
|
} |
|
|
|
switch v.Kind() { |
|
case reflect.String: |
|
return e.EncodeAmf3String(w, v.String(), true) |
|
case reflect.Bool: |
|
if v.Bool() { |
|
return e.EncodeAmf3True(w, true) |
|
} else { |
|
return e.EncodeAmf3False(w, true) |
|
} |
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32: |
|
n := v.Int() |
|
if n >= 0 && n <= AMF3_INTEGER_MAX { |
|
return e.EncodeAmf3Integer(w, uint32(n), true) |
|
} else { |
|
return e.EncodeAmf3Double(w, float64(n), true) |
|
} |
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32: |
|
n := v.Uint() |
|
if n <= AMF3_INTEGER_MAX { |
|
return e.EncodeAmf3Integer(w, uint32(n), true) |
|
} else { |
|
return e.EncodeAmf3Double(w, float64(n), true) |
|
} |
|
case reflect.Int64: |
|
return e.EncodeAmf3Double(w, float64(v.Int()), true) |
|
case reflect.Uint64: |
|
return e.EncodeAmf3Double(w, float64(v.Uint()), true) |
|
case reflect.Float32, reflect.Float64: |
|
return e.EncodeAmf3Double(w, float64(v.Float()), true) |
|
case reflect.Array, reflect.Slice: |
|
length := v.Len() |
|
arr := make(Array, length) |
|
for i := 0; i < length; i++ { |
|
arr[i] = v.Index(int(i)).Interface() |
|
} |
|
return e.EncodeAmf3Array(w, arr, true) |
|
case reflect.Map: |
|
obj, ok := val.(Object) |
|
if ok != true { |
|
return 0, fmt.Errorf("encode amf3: unable to create object from map") |
|
} |
|
|
|
to := *new(TypedObject) |
|
to.Object = obj |
|
|
|
return e.EncodeAmf3Object(w, to, true) |
|
} |
|
|
|
if tm, ok := val.(time.Time); ok { |
|
return e.EncodeAmf3Date(w, tm, true) |
|
} |
|
|
|
if to, ok := val.(TypedObject); ok { |
|
return e.EncodeAmf3Object(w, to, true) |
|
} |
|
|
|
return 0, fmt.Errorf("encode amf3: unsupported type %s", v.Type()) |
|
} |
|
|
|
// marker: 1 byte 0x00 |
|
// no additional data |
|
func (e *Encoder) EncodeAmf3Undefined(w io.Writer, encodeMarker bool) (n int, err error) { |
|
if encodeMarker { |
|
if err = WriteMarker(w, AMF3_UNDEFINED_MARKER); err != nil { |
|
return |
|
} |
|
n += 1 |
|
} |
|
|
|
return |
|
} |
|
|
|
// marker: 1 byte 0x01 |
|
// no additional data |
|
func (e *Encoder) EncodeAmf3Null(w io.Writer, encodeMarker bool) (n int, err error) { |
|
if encodeMarker { |
|
if err = WriteMarker(w, AMF3_NULL_MARKER); err != nil { |
|
return |
|
} |
|
n += 1 |
|
} |
|
|
|
return |
|
} |
|
|
|
// marker: 1 byte 0x02 |
|
// no additional data |
|
func (e *Encoder) EncodeAmf3False(w io.Writer, encodeMarker bool) (n int, err error) { |
|
if encodeMarker { |
|
if err = WriteMarker(w, AMF3_FALSE_MARKER); err != nil { |
|
return |
|
} |
|
n += 1 |
|
} |
|
|
|
return |
|
} |
|
|
|
// marker: 1 byte 0x03 |
|
// no additional data |
|
func (e *Encoder) EncodeAmf3True(w io.Writer, encodeMarker bool) (n int, err error) { |
|
if encodeMarker { |
|
if err = WriteMarker(w, AMF3_TRUE_MARKER); err != nil { |
|
return |
|
} |
|
n += 1 |
|
} |
|
|
|
return |
|
} |
|
|
|
// marker: 1 byte 0x04 |
|
func (e *Encoder) EncodeAmf3Integer(w io.Writer, val uint32, encodeMarker bool) (n int, err error) { |
|
if encodeMarker { |
|
if err = WriteMarker(w, AMF3_INTEGER_MARKER); err != nil { |
|
return |
|
} |
|
n += 1 |
|
} |
|
|
|
var m int |
|
m, err = e.encodeAmf3Uint29(w, val) |
|
if err != nil { |
|
return |
|
} |
|
n += m |
|
|
|
return |
|
} |
|
|
|
// marker: 1 byte 0x05 |
|
func (e *Encoder) EncodeAmf3Double(w io.Writer, val float64, encodeMarker bool) (n int, err error) { |
|
if encodeMarker { |
|
if err = WriteMarker(w, AMF3_DOUBLE_MARKER); err != nil { |
|
return |
|
} |
|
n += 1 |
|
} |
|
|
|
err = binary.Write(w, binary.BigEndian, &val) |
|
if err != nil { |
|
return |
|
} |
|
n += 8 |
|
|
|
return |
|
} |
|
|
|
// marker: 1 byte 0x06 |
|
// format: |
|
// - u29 reference int. if reference, no more data. if not reference, |
|
// length value of bytes to read to complete string. |
|
func (e *Encoder) EncodeAmf3String(w io.Writer, val string, encodeMarker bool) (n int, err error) { |
|
if encodeMarker { |
|
if err = WriteMarker(w, AMF3_STRING_MARKER); err != nil { |
|
return |
|
} |
|
n += 1 |
|
} |
|
|
|
var m int |
|
|
|
m, err = e.encodeAmf3Utf8(w, val) |
|
if err != nil { |
|
return |
|
} |
|
n += m |
|
|
|
return |
|
} |
|
|
|
// marker: 1 byte 0x08 |
|
// format: |
|
// - u29 reference int, if reference, no more data |
|
// - timestamp double |
|
func (e *Encoder) EncodeAmf3Date(w io.Writer, val time.Time, encodeMarker bool) (n int, err error) { |
|
if encodeMarker { |
|
if err = WriteMarker(w, AMF3_DATE_MARKER); err != nil { |
|
return |
|
} |
|
n += 1 |
|
} |
|
|
|
if err = WriteMarker(w, 0x01); err != nil { |
|
return n, fmt.Errorf("amf3 encode: cannot encode u29 for array: %s", err) |
|
} |
|
n += 1 |
|
|
|
u64 := float64(val.Unix()) * 1000.0 |
|
err = binary.Write(w, binary.BigEndian, &u64) |
|
if err != nil { |
|
return n, fmt.Errorf("amf3 encode: unable to write date double: %s", err) |
|
} |
|
n += 8 |
|
|
|
return |
|
} |
|
|
|
// marker: 1 byte 0x09 |
|
// format: |
|
// - u29 reference int. if reference, no more data. |
|
// - string representing associative array if present |
|
// - n values (length of u29) |
|
func (e *Encoder) EncodeAmf3Array(w io.Writer, val Array, encodeMarker bool) (n int, err error) { |
|
if encodeMarker { |
|
if err = WriteMarker(w, AMF3_ARRAY_MARKER); err != nil { |
|
return |
|
} |
|
n += 1 |
|
} |
|
|
|
var m int |
|
length := uint32(len(val)) |
|
u29 := uint32(length<<1) | 0x01 |
|
|
|
m, err = e.encodeAmf3Uint29(w, u29) |
|
if err != nil { |
|
return n, fmt.Errorf("amf3 encode: cannot encode u29 for array: %s", err) |
|
} |
|
n += m |
|
|
|
m, err = e.encodeAmf3Utf8(w, "") |
|
if err != nil { |
|
return n, fmt.Errorf("amf3 encode: cannot encode empty string for array: %s", err) |
|
} |
|
n += m |
|
|
|
for _, v := range val { |
|
m, err := e.EncodeAmf3(w, v) |
|
if err != nil { |
|
return n, fmt.Errorf("amf3 encode: cannot encode array element: %s", err) |
|
} |
|
n += m |
|
} |
|
|
|
return |
|
} |
|
|
|
// marker: 1 byte 0x0a |
|
// format: ugh |
|
func (e *Encoder) EncodeAmf3Object(w io.Writer, val TypedObject, encodeMarker bool) (n int, err error) { |
|
if encodeMarker { |
|
if err = WriteMarker(w, AMF3_OBJECT_MARKER); err != nil { |
|
return |
|
} |
|
n += 1 |
|
} |
|
|
|
m := 0 |
|
|
|
trait := *NewTrait() |
|
trait.Type = val.Type |
|
trait.Dynamic = false |
|
trait.Externalizable = false |
|
|
|
for k, _ := range val.Object { |
|
trait.Properties = append(trait.Properties, k) |
|
} |
|
|
|
sort.Strings(trait.Properties) |
|
|
|
var u29 uint32 = 0x03 |
|
if trait.Dynamic { |
|
u29 |= 0x02 << 2 |
|
} |
|
|
|
if trait.Externalizable { |
|
u29 |= 0x01 << 2 |
|
} |
|
|
|
u29 |= uint32(len(trait.Properties)) << 4 |
|
|
|
m, err = e.encodeAmf3Uint29(w, u29) |
|
if err != nil { |
|
return n, fmt.Errorf("amf3 encode: cannot encode trait header for object: %s", err) |
|
} |
|
n += m |
|
|
|
m, err = e.encodeAmf3Utf8(w, trait.Type) |
|
if err != nil { |
|
return n, fmt.Errorf("amf3 encode: cannot encode trait type for object: %s", err) |
|
} |
|
n += m |
|
|
|
for _, prop := range trait.Properties { |
|
m, err = e.encodeAmf3Utf8(w, prop) |
|
if err != nil { |
|
return n, fmt.Errorf("amf3 encode: cannot encode trait property for object: %s", err) |
|
} |
|
n += m |
|
} |
|
|
|
if trait.Externalizable { |
|
return n, fmt.Errorf("amf3 encode: cannot encode externalizable object") |
|
} |
|
|
|
for _, prop := range trait.Properties { |
|
m, err = e.EncodeAmf3(w, val.Object[prop]) |
|
if err != nil { |
|
return n, fmt.Errorf("amf3 encode: cannot encode sealed object value: %s", err) |
|
} |
|
n += m |
|
} |
|
|
|
if trait.Dynamic { |
|
for k, v := range val.Object { |
|
var foundProp bool = false |
|
for _, prop := range trait.Properties { |
|
if prop == k { |
|
foundProp = true |
|
break |
|
} |
|
} |
|
|
|
if foundProp != true { |
|
m, err = e.encodeAmf3Utf8(w, k) |
|
if err != nil { |
|
return n, fmt.Errorf("amf3 encode: cannot encode dynamic object property key: %s", err) |
|
} |
|
n += m |
|
|
|
m, err = e.EncodeAmf3(w, v) |
|
if err != nil { |
|
return n, fmt.Errorf("amf3 encode: cannot encode dynamic object value: %s", err) |
|
} |
|
n += m |
|
} |
|
|
|
m, err = e.encodeAmf3Utf8(w, "") |
|
if err != nil { |
|
return n, fmt.Errorf("amf3 encode: cannot encode dynamic object ending marker string: %s", err) |
|
} |
|
n += m |
|
} |
|
} |
|
|
|
return |
|
} |
|
|
|
// marker: 1 byte 0x0c |
|
// format: |
|
// - u29 reference int. if reference, no more data. if not reference, |
|
// length value of bytes to read . |
|
func (e *Encoder) EncodeAmf3ByteArray(w io.Writer, val []byte, encodeMarker bool) (n int, err error) { |
|
if encodeMarker { |
|
if err = WriteMarker(w, AMF3_BYTEARRAY_MARKER); err != nil { |
|
return |
|
} |
|
n += 1 |
|
} |
|
|
|
var m int |
|
|
|
length := uint32(len(val)) |
|
u29 := (length << 1) | 1 |
|
|
|
m, err = e.encodeAmf3Uint29(w, u29) |
|
if err != nil { |
|
return n, fmt.Errorf("amf3 encode: cannot encode u29 for bytearray: %s", err) |
|
} |
|
n += m |
|
|
|
m, err = w.Write(val) |
|
if err != nil { |
|
return n, fmt.Errorf("encode amf3: unable to encode bytearray value: %s", err) |
|
} |
|
n += m |
|
|
|
return |
|
} |
|
|
|
func (e *Encoder) encodeAmf3Utf8(w io.Writer, val string) (n int, err error) { |
|
length := uint32(len(val)) |
|
u29 := uint32(length<<1) | 0x01 |
|
|
|
var m int |
|
m, err = e.encodeAmf3Uint29(w, u29) |
|
if err != nil { |
|
return n, fmt.Errorf("amf3 encode: cannot encode u29 for string: %s", err) |
|
} |
|
n += m |
|
|
|
m, err = w.Write([]byte(val)) |
|
if err != nil { |
|
return n, fmt.Errorf("encode amf3: unable to encode string value: %s", err) |
|
} |
|
n += m |
|
|
|
return |
|
} |
|
|
|
func (e *Encoder) encodeAmf3Uint29(w io.Writer, val uint32) (n int, err error) { |
|
if val <= 0x0000007F { |
|
err = WriteByte(w, byte(val)) |
|
if err == nil { |
|
n += 1 |
|
} |
|
} else if val <= 0x00003FFF { |
|
n, err = w.Write([]byte{byte(val>>7 | 0x80), byte(val & 0x7F)}) |
|
} else if val <= 0x001FFFFF { |
|
n, err = w.Write([]byte{byte(val>>14 | 0x80), byte(val>>7&0x7F | 0x80), byte(val & 0x7F)}) |
|
} else if val <= 0x1FFFFFFF { |
|
n, err = w.Write([]byte{byte(val>>22 | 0x80), byte(val>>15&0x7F | 0x80), byte(val>>8&0x7F | 0x80), byte(val)}) |
|
} else { |
|
return n, fmt.Errorf("amf3 encode: cannot encode u29 with value %d (out of range)", val) |
|
} |
|
|
|
return |
|
}
|
|
|