/* XXL: The eXtensible and fleXible Library for data processing
Copyright (C) 2000-2014 Prof. Dr. Bernhard Seeger
Head of the Database Research Group
Department of Mathematics and Computer Science
University of Marburg
Germany
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; If not, see <http://www.gnu.org/licenses/>.
http://code.google.com/p/xxl/
*/
package xxl.core.io;
import sun.misc.Unsafe;
import java.io.DataOutput;
import java.io.IOException;
import java.io.UTFDataFormatException;
import java.lang.reflect.Field;
/**
* This class provides a DataOutput implementation using java.lang.Unsafe.
* The underlying unsafe object operates on an initially allocated final
* byte array and is used for serialization of the primitive values.
*
* Additional to the methods defined in DataOutput, this implementation
* offers direct access to the written bytes and can be reset.
*
* @see java.io.DataOutput
* @see xxl.core.io.UnsafeDataInput
*/
public class UnsafeDataOutput implements DataOutput {
/** Size of a serialized boolean value */
private static final int SIZE_OF_BOOLEAN = 1;
/** Size of a serialized byte value */
private static final int SIZE_OF_BYTE = 1;
/** Size of a serialized short value */
private static final int SIZE_OF_SHORT= 2;
/** Size of a serialized char value */
private static final int SIZE_OF_CHAR= 2;
/** Size of a serialized integer value */
private static final int SIZE_OF_INT = 4;
/** Size of a serialized float value */
private static final int SIZE_OF_FLOAT = 4;
/** Size of a serialized long value */
private static final int SIZE_OF_LONG = 8;
/** Size of a serialized double value */
private static final int SIZE_OF_DOUBLE = 8;
/** The unsafe object used for serialization */
private static final Unsafe unsafe;
static
{
try
{
Field field = Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);
unsafe = (Unsafe)field.get(null);
}
catch (Exception e)
{
throw new RuntimeException(e);
}
}
/** Offset of a byte array */
private static final long byteArrayOffset = unsafe.arrayBaseOffset(byte[].class);
/** The next write position in the byte buffer */
private int pos = 0;
/** The byte buffer used with the unsafe object */
private final byte[] buffer;
/**
* Creates a new UsafeDataOutput object with the given underlying byte
* buffer size
*
* @param size the size of the byte array used with Unsafe to serialize the data
*/
public UnsafeDataOutput(int size) {
buffer = new byte[size];
}
@Override
public void write(int b) throws IOException {
unsafe.putByte(buffer, byteArrayOffset + pos, (byte)b);
pos += SIZE_OF_BYTE;
}
@Override
public void write(byte[] b) throws IOException {
if (b == null)
throw new NullPointerException();
else if (b.length > 0) {
unsafe.copyMemory(b, byteArrayOffset,
buffer, byteArrayOffset + pos,
b.length);
pos += b.length;
}
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
if (b == null)
throw new NullPointerException();
else if (len < 0 || off < 0 || (off+len) > b.length)
throw new IndexOutOfBoundsException();
else if (len > 0) {
unsafe.copyMemory(b, byteArrayOffset+off,
buffer, byteArrayOffset + pos,
len);
pos += len;
}
}
@Override
public void writeBoolean(boolean v) throws IOException {
unsafe.putBoolean(buffer, byteArrayOffset + pos, v);
pos += SIZE_OF_BOOLEAN;
}
@Override
public void writeByte(int v) throws IOException {
unsafe.putByte(buffer, byteArrayOffset + pos, (byte)v);
pos += SIZE_OF_BYTE;
}
@Override
public void writeShort(int v) throws IOException {
unsafe.putByte(buffer,byteArrayOffset + pos,(byte)(0xff & (v >> 8)));
pos += SIZE_OF_BYTE;
unsafe.putByte(buffer,byteArrayOffset + pos,(byte)(0xff & v));
pos += SIZE_OF_BYTE;
}
@Override
public void writeChar(int v) throws IOException {
unsafe.putChar(buffer, byteArrayOffset + pos, (char)v);
pos += SIZE_OF_CHAR;
}
@Override
public void writeInt(int v) throws IOException {
unsafe.putInt(buffer, byteArrayOffset + pos, v);
pos += SIZE_OF_INT;
}
@Override
public void writeLong(long v) throws IOException {
unsafe.putLong(buffer, byteArrayOffset + pos, v);
pos += SIZE_OF_LONG;
}
@Override
public void writeFloat(float v) throws IOException {
unsafe.putFloat(buffer, byteArrayOffset + pos, v);
pos += SIZE_OF_FLOAT;
}
@Override
public void writeDouble(double v) throws IOException {
unsafe.putDouble(buffer, byteArrayOffset + pos, v);
pos += SIZE_OF_DOUBLE;
}
@Override
public void writeBytes(String s) throws IOException {
if (s == null)
throw new NullPointerException();
else if (s.length() > 0) {
char[] result = new char[s.length()];
s.getChars(0,result.length,result,0);
for(int i = 0; i < result.length; i++)
writeByte(result[i]);
}
}
@Override
public void writeChars(String s) throws IOException {
if (s == null)
throw new NullPointerException();
else if (s.length() > 0) {
char[] result = new char[s.length()];
s.getChars(0,result.length,result,0);
for(int i = 0; i < result.length; i++)
writeChar(result[i]);
}
}
@Override
public void writeUTF(String s) throws IOException {
// Calculate the UTF length
int utflen = 0;
for(int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (c >= '\u0001' && c <= '\u007f') {
utflen++;
} else if (c == '\u0000' || (c >= '\u0080' && c <= '\u07ff')) {
utflen += 2;
} else if (c >= '\u0800' && c <= '\uffff') {
utflen += 3;
}
}
// Store UTF length
if (utflen > 65535)
throw new UTFDataFormatException("String input too long!");
else writeShort(utflen);
// Write the data
for(int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (c >= '\u0001' && c <= '\u007f') {
writeByte((byte)c);
} else if (c == '\u0000' || (c >= '\u0080' && c <= '\u07ff')) {
writeByte((byte)(0xc0 | (0x1f & (c >> 6))));
writeByte((byte)(0x80 | (0x3f & c)));
} else if (c >= '\u0800' && c <= '\uffff') {
writeByte((byte)(0xe0 | (0x0f & (c >> 12))));
writeByte((byte)(0x80 | (0x3f & (c >> 6))));
writeByte((byte)(0x80 | (0x3f & c)));
}
}
}
/**
* Returns the written bytes from the buffer.
* @return the bytes written to the buffer
*/
public byte[] toByteArray(){
return java.util.Arrays.copyOf(buffer, pos);
}
/**
* Resets this data output.
*/
public void reset() {
pos = 0;
}
}