/* * Copyright 2014 NAVER Corp. * * Licensed 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.navercorp.pinpoint.thrift.io; import java.io.IOException; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.nio.charset.Charset; import java.util.Arrays; /** * @author emeroad */ public class UnsafeByteArrayOutputStream extends ResettableByteArrayOutputStream { private static final String UTF8 = "UTF8"; private static final Charset UTF8_CHARSET = Charset.forName(UTF8); private final boolean autoExpand; /** * Creates a new byte array output stream. The buffer capacity is * initially 32 bytes, though its size increases if necessary. */ public UnsafeByteArrayOutputStream() { this(32); } /** * Creates a new byte array output stream, with a buffer capacity of * the specified size, in bytes. * * @param size the initial size. * @throws IllegalArgumentException if size is negative. */ public UnsafeByteArrayOutputStream(int size) { this(size, true); } public UnsafeByteArrayOutputStream(int size, boolean autoExpand) { super(size); this.autoExpand = autoExpand; } /** * 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 (isOverflow(newcount)) { if (autoExpand) { buf = Arrays.copyOf(buf, Math.max(buf.length << 1, newcount)); } else { throw new BufferOverflowException("Buffer size cannot exceed " + buf.length + ". (now:" + count + ", input-size:1"); } } 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 (isOverflow(newcount)) { if (autoExpand) { buf = Arrays.copyOf(buf, Math.max(buf.length << 1, newcount)); } else { throw new BufferOverflowException("Buffer size cannot exceed " + buf.length + ". (now:" + count + ", input-size:" + len); } } 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. * @throws java.io.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 buf; } /** * 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/> * <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() { try { return toString(UTF8); } catch (UnsupportedEncodingException ex) { return new String(buf, 0, count, UTF8_CHARSET); } } /** * 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/> * <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. * @throws java.io.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); } /** * 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 boolean isOverflow(int minCapacity) { if (minCapacity - buf.length > 0) { return true; } return false; } }