
org.red5.io.amf3.Output Maven / Gradle / Ivy
/*
* RED5 Open Source Media Server - https://github.com/Red5/ Copyright 2006-2023 by respective authors (see below). All rights reserved. Licensed under the Apache License, Version
* 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless
* required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and limitations under the License.
*/
package org.red5.io.amf3;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import java.util.Collection;
import java.util.Date;
import java.util.Map;
import java.util.Set;
import java.util.Vector;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import net.sf.ehcache.Element;
import org.apache.commons.beanutils.BeanMap;
import org.apache.mina.core.buffer.IoBuffer;
import org.red5.annotations.Anonymous;
import org.red5.compatibility.flex.messaging.io.ObjectProxy;
import org.red5.io.amf.AMF;
import org.red5.io.object.RecordSet;
import org.red5.io.object.Serializer;
import org.red5.io.object.UnsignedInt;
import org.red5.io.utils.HexDump;
import org.red5.io.utils.XMLUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
/**
* AMF3 output writer
*
* @see org.red5.io.amf3.AMF3
* @see org.red5.io.amf3.Input
* @author The Red5 Project
* @author Joachim Bauch ([email protected])
* @author Harald Radi ([email protected])
*/
public class Output extends org.red5.io.amf.Output {
protected static Logger log = LoggerFactory.getLogger(Output.class);
/**
* Set to a value above 0 to disable writing of the AMF3 object tag.
*/
private int amf3_mode;
/**
* List of strings already written.
* */
private ConcurrentMap stringReferences;
/**
* Constructor of AMF3 output.
*
* @param buf
* instance of IoBuffer
* @see IoBuffer
*/
public Output(IoBuffer buf) {
super(buf);
amf3_mode = 0;
stringReferences = new ConcurrentHashMap(8, 0.9f, 2);
}
/**
* Force using AMF3 everywhere
*/
public void enforceAMF3() {
amf3_mode++;
}
/**
* Provide access to raw data.
*
* @return IoBuffer
*/
protected IoBuffer getBuffer() {
return buf;
}
protected void writeAMF3() {
if (amf3_mode == 0) {
buf.put(AMF.TYPE_AMF3_OBJECT);
}
}
/** {@inheritDoc} */
@Override
public void writeBoolean(Boolean bol) {
writeAMF3();
buf.put(bol ? AMF3.TYPE_BOOLEAN_TRUE : AMF3.TYPE_BOOLEAN_FALSE);
}
/** {@inheritDoc} */
@Override
public void writeNull() {
writeAMF3();
buf.put(AMF3.TYPE_NULL);
}
protected void putInteger(long value) {
if ((value >= -268435456) && (value <= 268435455)) {
value &= 0x1FFFFFFF;
}
if (value < 128) {
buf.put((byte) value);
} else if (value < 16384) {
buf.put((byte) (((value >> 7) & 0x7F) | 0x80));
buf.put((byte) (value & 0x7F));
} else if (value < 2097152) {
buf.put((byte) (((value >> 14) & 0x7F) | 0x80));
buf.put((byte) (((value >> 7) & 0x7F) | 0x80));
buf.put((byte) (value & 0x7F));
} else if (value < 1073741824) {
buf.put((byte) (((value >> 22) & 0x7F) | 0x80));
buf.put((byte) (((value >> 15) & 0x7F) | 0x80));
buf.put((byte) (((value >> 8) & 0x7F) | 0x80));
buf.put((byte) (value & 0xFF));
} else {
log.error("Integer out of range: {}", value);
}
}
protected static byte[] encodeString(String string) {
Element element = getStringCache().get(string);
byte[] encoded = (element == null ? null : (byte[]) element.getObjectValue());
if (encoded == null) {
ByteBuffer buf = AMF.CHARSET.encode(string);
encoded = new byte[buf.limit()];
buf.get(encoded);
getStringCache().put(new Element(string, encoded));
}
return encoded;
}
protected void putString(String str, byte[] encoded) {
final int len = encoded.length;
Integer pos = stringReferences.get(str);
if (pos != null) {
// Reference to existing string
putInteger(pos << 1);
return;
}
putInteger(len << 1 | 1);
buf.put(encoded);
stringReferences.put(str, stringReferences.size());
}
/** {@inheritDoc} */
@Override
public void putString(String string) {
// empty string
if ("".equals(string)) {
putInteger(1);
return;
}
final byte[] encoded = encodeString(string);
putString(string, encoded);
}
/** {@inheritDoc} */
@Override
public void writeNumber(Number num) {
writeAMF3();
if (num.longValue() < AMF3.MIN_INTEGER_VALUE || num.longValue() > AMF3.MAX_INTEGER_VALUE) {
// out of range for integer encoding
buf.put(AMF3.TYPE_NUMBER);
buf.putDouble(num.doubleValue());
} else if (num instanceof Long || num instanceof Integer || num instanceof Short || num instanceof Byte) {
buf.put(AMF3.TYPE_INTEGER);
putInteger(num.longValue());
} else {
buf.put(AMF3.TYPE_NUMBER);
buf.putDouble(num.doubleValue());
}
}
/** {@inheritDoc} */
@Override
public void writeString(String string) {
writeAMF3();
buf.put(AMF3.TYPE_STRING);
if ("".equals(string)) {
putInteger(1);
} else {
final byte[] encoded = encodeString(string);
putString(string, encoded);
}
}
/** {@inheritDoc} */
@Override
public void writeDate(Date date) {
writeAMF3();
buf.put(AMF3.TYPE_DATE);
if (hasReference(date)) {
putInteger(getReferenceId(date) << 1);
return;
}
storeReference(date);
putInteger(1);
buf.putDouble(date.getTime());
}
/** {@inheritDoc} */
@Override
public void writeArray(Collection> array) {
writeAMF3();
buf.put(AMF3.TYPE_ARRAY);
if (hasReference(array)) {
putInteger(getReferenceId(array) << 1);
return;
}
storeReference(array);
amf3_mode += 1;
int count = array.size();
putInteger(count << 1 | 1);
putString("");
for (Object item : array) {
Serializer.serialize(this, item);
}
amf3_mode -= 1;
}
/** {@inheritDoc} */
@Override
public void writeArray(Object[] array) {
writeAMF3();
buf.put(AMF3.TYPE_ARRAY);
if (hasReference(array)) {
putInteger(getReferenceId(array) << 1);
return;
}
storeReference(array);
amf3_mode += 1;
int count = array.length;
putInteger(count << 1 | 1);
putString("");
for (Object item : array) {
Serializer.serialize(this, item);
}
amf3_mode -= 1;
}
/** {@inheritDoc} */
@Override
public void writeArray(Object array) {
Class> componentType = array.getClass().getComponentType();
if (componentType.equals(Character.TYPE)) {
// write the char[] as a string
writeString(new String((char[]) array));
} else if (componentType.equals(Byte.TYPE)) {
writePrimitiveByteArray((byte[]) array);
} else {
writePrimitiveArrayFallback(array);
}
}
/**
* Use the specialized BYTEARRAY type.
*/
private void writePrimitiveByteArray(byte[] bytes) {
writeAMF3();
this.buf.put(AMF3.TYPE_BYTEARRAY);
if (hasReference(bytes)) {
putInteger(getReferenceId(bytes) << 1);
return;
}
storeReference(bytes);
int length = bytes.length;
putInteger(length << 1 | 0x1);
this.buf.put(bytes);
}
/**
* Use the general ARRAY type, writing the primitive array as an array of objects (the boxed primitives) instead.
*/
private void writePrimitiveArrayFallback(Object array) {
writeAMF3();
buf.put(AMF3.TYPE_ARRAY);
if (hasReference(array)) {
putInteger(getReferenceId(array) << 1);
return;
}
storeReference(array);
amf3_mode += 1;
int count = Array.getLength(array);
putInteger(count << 1 | 1);
putString("");
for (int i = 0; i < count; i++) {
Serializer.serialize(this, Array.get(array, i));
}
amf3_mode -= 1;
}
/** {@inheritDoc} */
@Override
public void writeMap(Map
© 2015 - 2025 Weber Informatics LLC | Privacy Policy