|
| 1 | +// Package amf0 contains an AMF0 decoder and encoder. |
1 | 2 | package amf0
|
2 | 3 |
|
3 | 4 | import (
|
@@ -28,9 +29,15 @@ const (
|
28 | 29 |
|
29 | 30 | var errBufferTooShort = errors.New("buffer is too short")
|
30 | 31 |
|
| 32 | +// StrictArray is an AMF0 Strict Array. |
| 33 | +type StrictArray []interface{} |
| 34 | + |
| 35 | +// Data is a list of ActionScript object graphs. |
| 36 | +type Data []interface{} |
| 37 | + |
31 | 38 | // Unmarshal decodes AMF0 data.
|
32 |
| -func Unmarshal(buf []byte) ([]interface{}, error) { |
33 |
| - var out []interface{} |
| 39 | +func Unmarshal(buf []byte) (Data, error) { |
| 40 | + var out Data |
34 | 41 |
|
35 | 42 | for len(buf) != 0 {
|
36 | 43 | var item interface{}
|
@@ -210,3 +217,207 @@ func unmarshal(buf []byte) (interface{}, []byte, error) {
|
210 | 217 | return nil, nil, fmt.Errorf("unsupported marker 0x%.2x", marker)
|
211 | 218 | }
|
212 | 219 | }
|
| 220 | + |
| 221 | +// Marshal encodes AMF0 data. |
| 222 | +func (data Data) Marshal() ([]byte, error) { |
| 223 | + n, err := data.MarshalSize() |
| 224 | + if err != nil { |
| 225 | + return nil, err |
| 226 | + } |
| 227 | + |
| 228 | + buf := make([]byte, n) |
| 229 | + _, err = data.MarshalTo(buf) |
| 230 | + if err != nil { |
| 231 | + return nil, err |
| 232 | + } |
| 233 | + |
| 234 | + return buf, nil |
| 235 | +} |
| 236 | + |
| 237 | +// MarshalTo encodes AMF0 data into an existing buffer. |
| 238 | +func (data Data) MarshalTo(buf []byte) (int, error) { |
| 239 | + n := 0 |
| 240 | + |
| 241 | + for _, item := range data { |
| 242 | + n += marshalItem(item, buf[n:]) |
| 243 | + } |
| 244 | + |
| 245 | + return n, nil |
| 246 | +} |
| 247 | + |
| 248 | +// MarshalSize returns the size needed to encode data in AMF0. |
| 249 | +func (data Data) MarshalSize() (int, error) { |
| 250 | + n := 0 |
| 251 | + |
| 252 | + for _, item := range data { |
| 253 | + in, err := marshalSizeItem(item) |
| 254 | + if err != nil { |
| 255 | + return 0, err |
| 256 | + } |
| 257 | + |
| 258 | + n += in |
| 259 | + } |
| 260 | + |
| 261 | + return n, nil |
| 262 | +} |
| 263 | + |
| 264 | +func marshalSizeItem(item interface{}) (int, error) { |
| 265 | + switch item := item.(type) { |
| 266 | + case float64: |
| 267 | + return 9, nil |
| 268 | + |
| 269 | + case bool: |
| 270 | + return 2, nil |
| 271 | + |
| 272 | + case string: |
| 273 | + return 3 + len(item), nil |
| 274 | + |
| 275 | + case ECMAArray: |
| 276 | + n := 5 |
| 277 | + |
| 278 | + for _, entry := range item { |
| 279 | + en, err := marshalSizeItem(entry.Value) |
| 280 | + if err != nil { |
| 281 | + return 0, err |
| 282 | + } |
| 283 | + |
| 284 | + n += 2 + len(entry.Key) + en |
| 285 | + } |
| 286 | + |
| 287 | + n += 3 |
| 288 | + |
| 289 | + return n, nil |
| 290 | + |
| 291 | + case Object: |
| 292 | + n := 1 |
| 293 | + |
| 294 | + for _, entry := range item { |
| 295 | + en, err := marshalSizeItem(entry.Value) |
| 296 | + if err != nil { |
| 297 | + return 0, err |
| 298 | + } |
| 299 | + |
| 300 | + n += 2 + len(entry.Key) + en |
| 301 | + } |
| 302 | + |
| 303 | + n += 3 |
| 304 | + |
| 305 | + return n, nil |
| 306 | + |
| 307 | + case StrictArray: |
| 308 | + n := 5 |
| 309 | + |
| 310 | + for _, entry := range item { |
| 311 | + en, err := marshalSizeItem(entry) |
| 312 | + if err != nil { |
| 313 | + return 0, err |
| 314 | + } |
| 315 | + |
| 316 | + n += en |
| 317 | + } |
| 318 | + |
| 319 | + return n, nil |
| 320 | + |
| 321 | + case nil: |
| 322 | + return 1, nil |
| 323 | + |
| 324 | + default: |
| 325 | + return 0, fmt.Errorf("unsupported data type: %T", item) |
| 326 | + } |
| 327 | +} |
| 328 | + |
| 329 | +func marshalItem(item interface{}, buf []byte) int { |
| 330 | + switch item := item.(type) { |
| 331 | + case float64: |
| 332 | + v := math.Float64bits(item) |
| 333 | + buf[0] = markerNumber |
| 334 | + buf[1] = byte(v >> 56) |
| 335 | + buf[2] = byte(v >> 48) |
| 336 | + buf[3] = byte(v >> 40) |
| 337 | + buf[4] = byte(v >> 32) |
| 338 | + buf[5] = byte(v >> 24) |
| 339 | + buf[6] = byte(v >> 16) |
| 340 | + buf[7] = byte(v >> 8) |
| 341 | + buf[8] = byte(v) |
| 342 | + return 9 |
| 343 | + |
| 344 | + case bool: |
| 345 | + buf[0] = markerBoolean |
| 346 | + if item { |
| 347 | + buf[1] = 1 |
| 348 | + } |
| 349 | + return 2 |
| 350 | + |
| 351 | + case string: |
| 352 | + le := len(item) |
| 353 | + buf[0] = markerString |
| 354 | + buf[1] = byte(le >> 8) |
| 355 | + buf[2] = byte(le) |
| 356 | + copy(buf[3:], item) |
| 357 | + return 3 + le |
| 358 | + |
| 359 | + case ECMAArray: |
| 360 | + le := len(item) |
| 361 | + buf[0] = markerECMAArray |
| 362 | + buf[1] = byte(le >> 24) |
| 363 | + buf[2] = byte(le >> 16) |
| 364 | + buf[3] = byte(le >> 8) |
| 365 | + buf[4] = byte(le) |
| 366 | + n := 5 |
| 367 | + |
| 368 | + for _, entry := range item { |
| 369 | + le := len(entry.Key) |
| 370 | + buf[n] = byte(le >> 8) |
| 371 | + buf[n+1] = byte(le) |
| 372 | + copy(buf[n+2:], entry.Key) |
| 373 | + n += 2 + le |
| 374 | + |
| 375 | + n += marshalItem(entry.Value, buf[n:]) |
| 376 | + } |
| 377 | + |
| 378 | + buf[n] = 0 |
| 379 | + buf[n+1] = 0 |
| 380 | + buf[n+2] = markerObjectEnd |
| 381 | + |
| 382 | + return n + 3 |
| 383 | + |
| 384 | + case Object: |
| 385 | + buf[0] = markerObject |
| 386 | + n := 1 |
| 387 | + |
| 388 | + for _, entry := range item { |
| 389 | + le := len(entry.Key) |
| 390 | + buf[n] = byte(le >> 8) |
| 391 | + buf[n+1] = byte(le) |
| 392 | + copy(buf[n+2:], entry.Key) |
| 393 | + n += 2 + le |
| 394 | + |
| 395 | + n += marshalItem(entry.Value, buf[n:]) |
| 396 | + } |
| 397 | + |
| 398 | + buf[n] = 0 |
| 399 | + buf[n+1] = 0 |
| 400 | + buf[n+2] = markerObjectEnd |
| 401 | + |
| 402 | + return n + 3 |
| 403 | + |
| 404 | + case StrictArray: |
| 405 | + le := len(item) |
| 406 | + buf[0] = markerStrictArray |
| 407 | + buf[1] = byte(le >> 24) |
| 408 | + buf[2] = byte(le >> 16) |
| 409 | + buf[3] = byte(le >> 8) |
| 410 | + buf[4] = byte(le) |
| 411 | + n := 5 |
| 412 | + |
| 413 | + for _, entry := range item { |
| 414 | + n += marshalItem(entry, buf[n:]) |
| 415 | + } |
| 416 | + |
| 417 | + return n |
| 418 | + |
| 419 | + default: |
| 420 | + buf[0] = markerNull |
| 421 | + return 1 |
| 422 | + } |
| 423 | +} |
0 commit comments