/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.facebook.infrastructure.io; import java.io.*; import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.util.Arrays; /** * An implementation of the DataOutputStream interface. This class is completely thread * unsafe. * * Author : Avinash Lakshman ( alakshman@facebook.com) & Prashant Malik ( pmalik@facebook.com ) */ public class DataOutputBuffer extends DataOutputStream { /* * This is a clone of the ByteArrayOutputStream but w/o the unnecessary * synchronized keyword usage. */ public static class FastByteArrayOutputStream extends OutputStream { /** * The buffer where data is stored. */ protected byte buf[]; /** * The number of valid bytes in the buffer. */ protected int count; /** * Creates a new byte array output stream. The buffer capacity is * initially 32 bytes, though its size increases if necessary. */ public FastByteArrayOutputStream() { this(32); } /** * Creates a new byte array output stream, with a buffer capacity of the * specified size, in bytes. * * @param size * the initial size. * @exception IllegalArgumentException * if size is negative. */ public FastByteArrayOutputStream(int size) { if (size < 0) { throw new IllegalArgumentException("Negative initial size: " + size); } buf = new byte[size]; } /** * Writes the specified byte to this byte array output stream. * * @param b * the byte to be written. */ public void write(int b) { int newcount = count + 1; if (newcount > buf.length) { buf = Arrays.copyOf(buf, Math.max(buf.length << 1, newcount)); } buf[count] = (byte) b; count = newcount; } /** * Writes <code>len</code> bytes from the specified byte array * starting at offset <code>off</code> to this byte array output * stream. * * @param b * the data. * @param off * the start offset in the data. * @param len * the number of bytes to write. */ public void write(byte b[], int off, int len) { if ((off < 0) || (off > b.length) || (len < 0) || ((off + len) > b.length) || ((off + len) < 0)) { throw new IndexOutOfBoundsException(); } else if (len == 0) { return; } int newcount = count + len; if (newcount > buf.length) { buf = Arrays.copyOf(buf, Math.max(buf.length << 1, newcount)); } System.arraycopy(b, off, buf, count, len); count = newcount; } /** * Writes the complete contents of this byte array output stream to the * specified output stream argument, as if by calling the output * stream's write method using <code>out.write(buf, 0, count)</code>. * * @param out * the output stream to which to write the data. * @exception IOException * if an I/O error occurs. */ public void writeTo(OutputStream out) throws IOException { out.write(buf, 0, count); } /** * Resets the <code>count</code> field of this byte array output * stream to zero, so that all currently accumulated output in the * output stream is discarded. The output stream can be used again, * reusing the already allocated buffer space. * * @see java.io.ByteArrayInputStream#count */ public void reset() { count = 0; } /** * Creates a newly allocated byte array. Its size is the current size of * this output stream and the valid contents of the buffer have been * copied into it. * * @return the current contents of this output stream, as a byte array. * @see java.io.ByteArrayOutputStream#size() */ public byte toByteArray()[] { return Arrays.copyOf(buf, count); } /** * Returns the current size of the buffer. * * @return the value of the <code>count</code> field, which is the * number of valid bytes in this output stream. * @see java.io.ByteArrayOutputStream#count */ public int size() { return count; } /** * Converts the buffer's contents into a string decoding bytes using the * platform's default character set. The length of the new * <tt>String</tt> is a function of the character set, and hence may * not be equal to the size of the buffer. * * <p> * This method always replaces malformed-input and unmappable-character * sequences with the default replacement string for the platform's * default character set. The * {@linkplain java.nio.charset.CharsetDecoder} class should be used * when more control over the decoding process is required. * * @return String decoded from the buffer's contents. * @since JDK1.1 */ public String toString() { return new String(buf, 0, count); } /** * Converts the buffer's contents into a string by decoding the bytes * using the specified {@link java.nio.charset.Charset charsetName}. * The length of the new <tt>String</tt> is a function of the charset, * and hence may not be equal to the length of the byte array. * * <p> * This method always replaces malformed-input and unmappable-character * sequences with this charset's default replacement string. The {@link * java.nio.charset.CharsetDecoder} class should be used when more * control over the decoding process is required. * * @param charsetName * the name of a supported * {@linkplain java.nio.charset.Charset </code>charset<code>} * @return String decoded from the buffer's contents. * @exception UnsupportedEncodingException * If the named charset is not supported * @since JDK1.1 */ public String toString(String charsetName) throws UnsupportedEncodingException { return new String(buf, 0, count, charsetName); } /** * Creates a newly allocated string. Its size is the current size of the * output stream and the valid contents of the buffer have been copied * into it. Each character <i>c</i> in the resulting string is * constructed from the corresponding element <i>b</i> in the byte * array such that: <blockquote> * * <pre> * c == (char) (((hibyte & 0xff) << 8) | (b & 0xff)) * </pre> * * </blockquote> * * @deprecated This method does not properly convert bytes into * characters. As of JDK 1.1, the preferred way to do * this is via the <code>toString(String enc)</code> * method, which takes an encoding-name argument, or the * <code>toString()</code> method, which uses the * platform's default character encoding. * * @param hibyte * the high byte of each resulting Unicode character. * @return the current contents of the output stream, as a string. * @see java.io.ByteArrayOutputStream#size() * @see java.io.ByteArrayOutputStream#toString(String) * @see java.io.ByteArrayOutputStream#toString() */ @Deprecated public String toString(int hibyte) { return new String(buf, hibyte, 0, count); } /** * Closing a <tt>ByteArrayOutputStream</tt> has no effect. The methods * in this class can be called after the stream has been closed without * generating an <tt>IOException</tt>. * <p> * */ public void close() throws IOException { } } private static class Buffer extends FastByteArrayOutputStream { public byte[] getData() { return buf; } public int getLength() { return count; } public void reset() { count = 0; } public void write(DataInput in, int len) throws IOException { int newcount = count + len; if (newcount > buf.length) { byte newbuf[] = new byte[Math.max(buf.length << 1, newcount)]; System.arraycopy(buf, 0, newbuf, 0, count); buf = newbuf; } long start = System.currentTimeMillis(); in.readFully(buf, count, len); count = newcount; } public void write(ByteBuffer buffer, int len) throws IOException { int newcount = count + len; if (newcount > buf.length) { byte newbuf[] = new byte[Math.max(buf.length << 1, newcount)]; System.arraycopy(buf, 0, newbuf, 0, count); buf = newbuf; } long start = System.currentTimeMillis(); buffer.get(buf, count, len); count = newcount; } } private Buffer buffer; /** Constructs a new empty buffer. */ public DataOutputBuffer() { this(new Buffer()); } private DataOutputBuffer(Buffer buffer) { super(buffer); this.buffer = buffer; } /** * Returns the current contents of the buffer. Data is only valid to * {@link #getLength()}. */ public byte[] getData() { return buffer.getData(); } /** Returns the length of the valid data currently in the buffer. */ public int getLength() { return buffer.getLength(); } /** Resets the buffer to empty. */ public DataOutputBuffer reset() { this.written = 0; buffer.reset(); return this; } /** Writes bytes from a DataInput directly into the buffer. */ public void write(DataInput in, int length) throws IOException { buffer.write(in, length); } /** Writes bytes from a ByteBuffer directly into the buffer. */ public void write(ByteBuffer in, int length) throws IOException { buffer.write(in, length); } }