/** 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); } } }