/* 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 java.io.DataOutput; import java.io.IOException; import java.io.UTFDataFormatException; import java.nio.ByteBuffer; import java.util.Arrays; /** * This class provides a DataOutput implementation using java.nio.ByteBuffer. * The underlying byte buffer object operates on an initially allocated final * byte buffer 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 ByteBufferDataInput */ public class ByteBufferDataOutput 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 byte buffer object used for serialization */ private final ByteBuffer buffer; /** The next write position in the byte buffer */ private int pos = 0; /** * Creates a new ByteBufferDataOutput object with the given underlying byte * buffer size * * @param size the size of the byte array used to serialize the data */ public ByteBufferDataOutput(int size) { buffer = ByteBuffer.allocate(size); } @Override public void write(int b) throws IOException { ensureBuffer(SIZE_OF_BYTE); buffer.put((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) { ensureBuffer(b.length); buffer.put(b); 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) { ensureBuffer(len); buffer.put(b,off,len); pos += len; } } @Override public void writeBoolean(boolean v) throws IOException { ensureBuffer(SIZE_OF_BOOLEAN); buffer.put((byte)(v?1:0)); pos += SIZE_OF_BOOLEAN; } @Override public void writeByte(int v) throws IOException { ensureBuffer(SIZE_OF_BYTE); buffer.put((byte)v); pos += SIZE_OF_BYTE; } @Override public void writeShort(int v) throws IOException { ensureBuffer(SIZE_OF_SHORT); buffer.put((byte) (0xff & (v >> 8))); buffer.put((byte)(0xff & v)); pos += SIZE_OF_SHORT; } @Override public void writeChar(int v) throws IOException { ensureBuffer(SIZE_OF_CHAR); buffer.putChar((char) v); pos += SIZE_OF_CHAR; } @Override public void writeInt(int v) throws IOException { ensureBuffer(SIZE_OF_INT); buffer.putInt(v); pos += SIZE_OF_INT; } @Override public void writeLong(long v) throws IOException { ensureBuffer(SIZE_OF_LONG); buffer.putLong(v); pos += SIZE_OF_LONG; } @Override public void writeFloat(float v) throws IOException { ensureBuffer(SIZE_OF_FLOAT); buffer.putFloat(v); pos += SIZE_OF_FLOAT; } @Override public void writeDouble(double v) throws IOException { ensureBuffer(SIZE_OF_DOUBLE); buffer.putDouble(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))); } } } /** * Ensures that the buffer contains the requested amount of data. * * @param size the size of the requested data * @throws IOException if there is not enough data left */ private void ensureBuffer(int size) throws IOException { if (buffer.position()+size > buffer.capacity()) throw new IOException("Buffer overflow: tried to append "+size+ " bytes to the buffer of capacity "+buffer.capacity()+ ", that already contains "+buffer.position()+" bytes"); } /** * Returns the written bytes from the buffer. * @return the bytes written to the buffer */ public byte[] toByteArray(){ return Arrays.copyOf(buffer.array(), pos); } /** * Resets this data output. */ public void reset() { pos = 0; buffer.clear(); } }