/*
* myLib - https://github.com/taktod/myLib
* Copyright (c) 2014 ttProject. All rights reserved.
*
* Licensed under The MIT license.
*/
package com.ttProject.container.flv.amf;
import java.nio.ByteBuffer;
import com.ttProject.nio.channels.IReadChannel;
import com.ttProject.util.BufferUtil;
/**
* amf3
* @author taktod
*/
public class Amf3Value {
/**
* type
*/
public enum Type {
Undefined(0x00),
Null(0x01),
False(0x02),
True(0x03),
Integer(0x04),
Double(0x05),
String(0x06),
XmlDoc(0x07),
Date(0x08),
Array(0x09),
Object(0x0A),
Xml(0x0B),
ByteArray(0x0C),
VectorInt(0x0D),
VectorUint(0x0E),
VectorDouble(0x0F),
VectorObject(0x10),
Dictionary(0x11);
private final int value;
private Type(int value) {
this.value = value;
}
public int intValue() {
return value;
}
public static Type getType(int value) {
for(Type t : values()) {
if(t.intValue() == value) {
return t;
}
}
throw new RuntimeException("unanalyzable data.:" + value);
}
}
/**
* get object from IReadChannel
* @param source
* @return
* @throws Exception
*/
public static Object getValueObject(IReadChannel source) throws Exception {
ByteBuffer data = null;
Type type = Type.getType(BufferUtil.safeRead(source, 1).get());
switch(type) {
case Null:
return null;
case False:
return false;
case True:
return true;
case Integer:
return getU29Integer(source);
case Double:
return Double.longBitsToDouble(BufferUtil.safeRead(source, 8).getLong());
case Object:
{
data = BufferUtil.safeRead(source, 2);
if(data.get() != 0x0B) {
throw new Exception("unknwon code for object.");
}
if(data.get() != 0x01) {
throw new Exception("start code of object is unexpect value.");
}
// TODO should I use Amf3Object, instead of Map?
Amf0Object<String, Object> result = new Amf0Object<String, Object>();
// Map<String, Object> result = new HashMap<String, Object>();
// load data.
byte b;
while((b = BufferUtil.safeRead(source, 1).get()) != 0x01) {
if((b & 0x01) == 0) {
throw new Exception("reference base object is not coded yet.");
}
data = BufferUtil.safeRead(source, b >>> 1);
String key = new String(data.array()).intern();
result.put(key, getValueObject(source));
}
return result;
}
case String:
{
byte b = BufferUtil.safeRead(source, 1).get();
if((b & 0x01) != 0x01) {
throw new RuntimeException("reference base object is not coded yet for string.");
}
data = BufferUtil.safeRead(source, b >>> 1);
return new String(data.array()).intern();
}
case Undefined:
break;
default:
throw new Exception("unanalyzable data.:" + type);
}
return null;
}
/**
* get u29 integer.
* @param source
* @return
* @throws Exception
*/
public static int getU29Integer(IReadChannel source) throws Exception {
byte b = BufferUtil.safeRead(source, 1).get();
int data = 0;
while((b & 0x80) != 0x00) {
data = (data << 7) + (b & 0x7F);
b = BufferUtil.safeRead(source, 1).get();
}
data = (data << 7) + (b & 0x7F);
return data;
}
}