/**
Copyright (C) SYSTAP, LLC DBA Blazegraph 2006-2016. All rights reserved.
Contact:
SYSTAP, LLC DBA Blazegraph
2501 Calvert ST NW #106
Washington, DC 20008
licenses@blazegraph.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* Created on Apr 7, 2007
*/
package com.bigdata.io;
import java.io.ByteArrayOutputStream;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInput;
import java.io.OutputStream;
import com.bigdata.io.compression.IUnicodeCompressor;
import com.bigdata.journal.Name2Addr;
/**
* Fast special purpose serialization onto a managed byte[] buffer conforming to
* the {@link DataOutput} API.
* <p>
* Note: The base classes provide all of the same functionality without
* declaring {@link IOException} as a thrown exception.
*
* @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan Thompson</a>
* @version $Id$
*/
public class DataOutputBuffer extends ByteArrayBuffer implements DataOutput {
// private static transient final Logger log = Logger
// .getLogger(DataOutputBuffer.class);
/**
* Uses the default for {@link ByteArrayBuffer#ByteArrayBuffer()}.
*/
public DataOutputBuffer() {
super();
}
/**
* @param initialCapacity
* The initial capacity of the internal byte[].
*/
public DataOutputBuffer(final int initialCapacity) {
super(initialCapacity);
}
/**
* @param len
* The #of bytes of data already in the provided buffer.
* @param buf
* The buffer, with <i>len</i> pre-existing bytes of valid data.
* The buffer reference is used directly rather than making a
* copy of the data.
*/
public DataOutputBuffer(final int len, final byte[] buf) {
super(len/* pos */, buf.length/* readLimit */, buf);
}
/**
* Reads the entire input stream into the buffer. The data are then
* available in {@link #buf} from position 0 (inclusive) through position
* {@link #pos} (exclusive).
*/
public DataOutputBuffer(final InputStream in) throws IOException {
super();
// temporary buffer for read from the input stream.
final byte[] b = new byte[remaining()];
while(true) {
int nread = in.read(b);
if( nread == -1 ) break;
write(b,0,nread);
}
}
/**
* Reads the entire input stream into the buffer. The data are then
* available in {@link #buf} from position 0 (inclusive) through position
* {@link #pos} (exclusive).
*/
public DataOutputBuffer(final ObjectInput in) throws IOException {
super();
// temporary buffer for read from the input stream.
byte[] b = new byte[remaining()];
while(true) {
int nread = in.read(b);
if( nread == -1 ) break;
write(b,0,nread);
}
}
/**
* Conforms the return type to an instance of this class.
* {@inheritDoc}
*/
public DataOutputBuffer reset() {
return (DataOutputBuffer) super.reset();
}
/**
* Read <i>len</i> bytes into the buffer.
*
* @param in
* The input source.
* @param len
* The #of bytes to read.
*
* @throws EOFException
* if the EOF is reached before <i>len</i> bytes have been read.
* @throws IOException
* if an I/O error occurs.
*
* @todo read many bytes at a time.
* @todo write test.
*/
final public void write(final DataInput in, final int len)
throws IOException {
ensureCapacity(len);
int c = 0;
byte b;
while (c < len) {
b = in.readByte();
buf[this.pos++] = (byte) (b & 0xff);
c++;
}
limit = pos;
}
final public void writeBoolean(final boolean v) throws IOException {
if (pos + 1 > buf.length)
ensureCapacity(pos + 1);
buf[pos++] = v ? (byte) 1 : (byte) 0;
limit = pos;
}
final public void writeByte(final int v) throws IOException {
if (pos + 1 > buf.length)
ensureCapacity(pos + 1);
buf[pos++] = (byte) (v & 0xff);
limit = pos;
}
final public void writeDouble(final double v) throws IOException {
putDouble( v );
}
final public void writeFloat(final float v) throws IOException {
putFloat( v );
}
final public void writeInt(final int v) throws IOException {
putInt( v );
}
final public void writeLong(final long v) throws IOException {
putLong(v);
}
final public void writeShort(final int v) throws IOException {
// if (len + 2 > buf.length)
// ensureCapacity(len + 2);
//
// // big-endian
// buf[len++] = (byte) (v >>> 8);
// buf[len++] = (byte) (v >>> 0);
putShort( (short)v );
}
final public void writeChar(final int v) throws IOException {
if (pos + 2 > buf.length)
ensureCapacity(pos + 2);
buf[pos++] = (byte) (v >>> 8);
buf[pos++] = (byte) (v >>> 0);
limit = pos;
}
public void writeBytes(final String s) throws IOException {
// #of bytes == #of characters (writes only the low bytes).
final int len = s.length();
if (this.pos + len > buf.length)
ensureCapacity(this.pos + len);
for (int i = 0 ; i < len ; i++) {
write((byte)s.charAt(i));
}
limit = pos;
}
public void writeChars(final String s) throws IOException {
// #of characters (twice as many bytes).
final int len = s.length();
if (this.pos + (len * 2) > buf.length)
ensureCapacity(this.pos + (len * 2));
for (int i = 0 ; i < len ; i++) {
final char v = s.charAt(i);
buf[this.pos++] = (byte) (v >>> 8);
buf[this.pos++] = (byte) (v >>> 0);
// write((v >>> 8) & 0xFF);
//
// write((v >>> 0) & 0xFF);
}
limit = pos;
}
// This inefficiency has been fixed.
// /**
// * @todo This is not wildly efficient (it would be fine if
// * DataOutputStream#writeUTF(String str, DataOutput out)} was public)
// * but the use cases for serializing the nodes and leaves of a btree
// * do not suggest any requirement for Unicode (if you assume that the
// * application values are already being serialized as byte[]s - which
// * is always true when there is a client-server divide). It is used by
// * {@link Name2Addr} to store the index names. It used to be used to
// * write Value into the lexicon, but that is now handled using a
// * {@link IUnicodeCompressor}.
// *
// * @todo Consider changing the access modified on the desired method using
// * reflection.
// */
public void writeUTF(final String str) throws IOException {
// This is the old, inefficient version.
// final ByteArrayOutputStream baos = new ByteArrayOutputStream();
//
// final DataOutputStream dos = new DataOutputStream(baos);
//
// dos.writeUTF(str);
//
// dos.flush();
//
// write(baos.toByteArray());
// final ByteArrayOutputStream baos = new ByteArrayOutputStream();
// This is the new, more efficient version.
final DataOutputStream dos = new DataOutputStream(this);
dos.writeUTF(str);
dos.flush();
// write(baos.toByteArray());
}
/**
* Version of {@link #writeUTF(String)} which wraps the {@link IOException}.
*
* @param str
* The string.
*/
public void writeUTF2(final String str) {
try {
writeUTF(str);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}