se.l4.commons.serialization.format.BinaryOutput Maven / Gradle / Ivy
The newest version!
package se.l4.commons.serialization.format;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Arrays;
/**
* Output for custom binary format.
*
* @author Andreas Holstenson
*
*/
public class BinaryOutput
implements StreamingOutput
{
private static final int LEVELS = 20;
public static final int TAG_KEY = 0;
public static final int TAG_OBJECT_START = 1;
public static final int TAG_OBJECT_END = 2;
public static final int TAG_LIST_START = 3;
public static final int TAG_LIST_END = 4;
public static final int TAG_STRING = 10;
public static final int TAG_INT = 11;
public static final int TAG_LONG = 12;
public static final int TAG_NULL = 13;
public static final int TAG_FLOAT = 14;
public static final int TAG_DOUBLE = 15;
public static final int TAG_BOOLEAN = 16;
public static final int TAG_BYTE_ARRAY = 17;
public static final int TAG_POSITIVE_INT = 18;
public static final int TAG_POSITIVE_LONG = 19;
public static final int TAG_NEGATIVE_INT = 20;
public static final int TAG_NEGATIVE_LONG = 21;
private final OutputStream out;
private boolean[] lists;
private boolean[] hasData;
private int level;
public BinaryOutput(OutputStream out)
{
this.out = out;
lists = new boolean[LEVELS];
hasData = new boolean[LEVELS];
}
@Override
public void close()
throws IOException
{
flush();
out.close();
}
/**
* Increase the level by one.
*
* @param list
*/
private void increaseLevel(boolean list)
{
level++;
if(hasData.length == level)
{
// Grow lists when needed
hasData = Arrays.copyOf(hasData, hasData.length * 2);
lists = Arrays.copyOf(lists, hasData.length * 2);
}
hasData[level] = false;
lists[level] = list;
}
/**
* Decrease the level by one.
*
* @throws IOException
*/
private void decreaseLevel()
throws IOException
{
level--;
}
/**
* Start a write, will output commas and beautification if needed.
*
* @throws IOException
*/
private void startWrite()
throws IOException
{
// if(hasData[level]) writer.write(',');
hasData[level] = true;
}
/**
* Check if the name should be written or not.
*
* @return
*/
private boolean shouldOutputName()
{
return level != 0 && ! lists[level];
}
/**
* Write the name if needed.
*
* @param name
* @throws IOException
*/
private void writeName(String name)
throws IOException
{
if(shouldOutputName())
{
out.write(TAG_KEY);
writeStringNoTag(name);
}
}
/**
* Write an integer to the output stream without tagging it.
*
* @param value
* @throws IOException
*/
private void writeIntegerNoTag(int value)
throws IOException
{
while(true)
{
if((value & ~0x7F) == 0)
{
out.write(value);
break;
}
else
{
out.write((value & 0x7f) | 0x80);
value >>>= 7;
}
}
}
/**
* Write an integer to the output stream.
*
* @param value
* @throws IOException
*/
private void writeInteger(int value)
throws IOException
{
if(value < 0)
{
out.write(TAG_NEGATIVE_INT);
writeIntegerNoTag(-value);
}
else
{
out.write(TAG_POSITIVE_INT);
writeIntegerNoTag(value);
}
}
/**
* Write a long to the output stream.
*
* @param value
* @throws IOException
*/
private void writeLongNoTag(long value)
throws IOException
{
while(true)
{
if((value & ~0x7FL) == 0)
{
out.write((int) value);
break;
}
else
{
out.write(((int) value & 0x7f) | 0x80);
value >>>= 7;
}
}
}
/**
* Write a long to the output stream.
*
* @param value
* @throws IOException
*/
private void writeLong(long value)
throws IOException
{
if(value < 0)
{
out.write(TAG_NEGATIVE_LONG);
writeLongNoTag(- value);
}
else
{
out.write(TAG_POSITIVE_LONG);
writeLongNoTag(value);
}
}
/**
* Write a string to the output without tagging that its actually a string.
*
* @param value
* @throws IOException
*/
private void writeStringNoTag(String value)
throws IOException
{
writeIntegerNoTag(value.length());
for(int i=0, n=value.length(); i 0x07ff)
{
out.write((byte) (0xe0 | c >> 12 & 0x0f));
out.write((byte) (0x80 | c >> 6 & 0x3f));
out.write((byte) (0x80 | c >> 0 & 0x3f));
}
else
{
out.write((byte) (0xc0 | c >> 6 & 0x1f));
out.write((byte) (0x80 | c >> 0 & 0x3f));
}
}
}
private void writeString(String value)
throws IOException
{
out.write(TAG_STRING);
writeStringNoTag(value);
}
private void writeNull()
throws IOException
{
out.write(TAG_NULL);
}
private void writeFloat(float value)
throws IOException
{
out.write(TAG_FLOAT);
int i = Float.floatToRawIntBits(value);
out.write(i & 0xff);
out.write((i >> 8) & 0xff);
out.write((i >> 16) & 0xff);
out.write((i >> 24) & 0xff);
}
private void writeDouble(double value)
throws IOException
{
out.write(TAG_DOUBLE);
long l = Double.doubleToRawLongBits(value);
out.write((int) l & 0xff);
out.write((int) (l >> 8) & 0xff);
out.write((int) (l >> 16) & 0xff);
out.write((int) (l >> 24) & 0xff);
out.write((int) (l >> 32) & 0xff);
out.write((int) (l >> 40) & 0xff);
out.write((int) (l >> 48) & 0xff);
out.write((int) (l >> 56) & 0xff);
}
private void writeBoolean(boolean b)
throws IOException
{
out.write(TAG_BOOLEAN);
out.write(b ? 1 : 0);
}
private void writeByteArray(byte[] data)
throws IOException
{
out.write(TAG_BYTE_ARRAY);
writeIntegerNoTag(data.length);
out.write(data);
}
@Override
public void writeObjectStart(String name)
throws IOException
{
startWrite();
writeName(name);
out.write(TAG_OBJECT_START);
increaseLevel(false);
}
@Override
public void writeObjectEnd(String name)
throws IOException
{
decreaseLevel();
out.write(TAG_OBJECT_END);
}
@Override
public void writeListStart(String name)
throws IOException
{
startWrite();
writeName(name);
out.write(TAG_LIST_START);
increaseLevel(true);
}
@Override
public void writeListEnd(String name)
throws IOException
{
decreaseLevel();
out.write(TAG_LIST_END);
}
@Override
public void write(String name, String value)
throws IOException
{
startWrite();
writeName(name);
if(value == null)
{
writeNull();
}
else
{
writeString(value);
}
}
@Override
public void write(String name, int number)
throws IOException
{
startWrite();
writeName(name);
writeInteger(number);
}
@Override
public void write(String name, long number)
throws IOException
{
startWrite();
writeName(name);
writeLong(number);
}
@Override
public void write(String name, float number)
throws IOException
{
startWrite();
writeName(name);
writeFloat(number);
}
@Override
public void write(String name, double number)
throws IOException
{
startWrite();
writeName(name);
writeDouble(number);
}
@Override
public void write(String name, boolean bool)
throws IOException
{
startWrite();
writeName(name);
writeBoolean(bool);
}
@Override
public void write(String name, byte[] data)
throws IOException
{
startWrite();
writeName(name);
writeByteArray(data);
}
@Override
public void writeNull(String name)
throws IOException
{
writeName(name);
writeNull();
}
@Override
public void flush()
throws IOException
{
out.flush();
}
}