001/**
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase.io;
019
020import java.io.OutputStream;
021import java.nio.BufferOverflowException;
022import java.nio.ByteBuffer;
023import java.util.Arrays;
024
025import org.apache.hadoop.hbase.nio.ByteBuff;
026import org.apache.hadoop.hbase.util.ByteBufferUtils;
027import org.apache.hadoop.hbase.util.Bytes;
028import org.apache.yetus.audience.InterfaceAudience;
029
030/**
031 * Our own implementation of ByteArrayOutputStream where all methods are NOT synchronized and
032 * supports writing ByteBuffer directly to it.
033 */
034@InterfaceAudience.Private
035public class ByteArrayOutputStream extends OutputStream implements ByteBufferWriter {
036
037  // Borrowed from openJDK:
038  // http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8-b132/java/util/ArrayList.java#221
039  private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
040
041  private byte[] buf;
042  private int pos = 0;
043
044  public ByteArrayOutputStream() {
045    this(32);
046  }
047
048  public ByteArrayOutputStream(int capacity) {
049    this.buf = new byte[capacity];
050  }
051
052  @Override
053  public void write(ByteBuffer b, int off, int len) {
054    checkSizeAndGrow(len);
055    ByteBufferUtils.copyFromBufferToArray(this.buf, b, off, this.pos, len);
056    this.pos += len;
057  }
058
059  @Override
060  public void writeInt(int i) {
061    checkSizeAndGrow(Bytes.SIZEOF_INT);
062    Bytes.putInt(this.buf, this.pos, i);
063    this.pos += Bytes.SIZEOF_INT;
064  }
065
066  @Override
067  public void write(int b) {
068    checkSizeAndGrow(Bytes.SIZEOF_BYTE);
069    buf[this.pos] = (byte) b;
070    this.pos++;
071  }
072
073  @Override
074  public void write(byte[] b, int off, int len) {
075    checkSizeAndGrow(len);
076    System.arraycopy(b, off, this.buf, this.pos, len);
077    this.pos += len;
078  }
079
080  private void checkSizeAndGrow(int extra) {
081    long capacityNeeded = this.pos + (long) extra;
082    if (capacityNeeded > this.buf.length) {
083      // guarantee it's possible to fit
084      if (capacityNeeded > MAX_ARRAY_SIZE) {
085        throw new BufferOverflowException();
086      }
087      // double until hit the cap
088      long nextCapacity = Math.min(this.buf.length << 1, MAX_ARRAY_SIZE);
089      // but make sure there is enough if twice the existing capacity is still too small
090      nextCapacity = Math.max(nextCapacity, capacityNeeded);
091      if (nextCapacity > MAX_ARRAY_SIZE) {
092        throw new BufferOverflowException();
093      }
094      byte[] newBuf = new byte[(int) nextCapacity];
095      System.arraycopy(buf, 0, newBuf, 0, buf.length);
096      buf = newBuf;
097    }
098  }
099
100  /**
101   * Resets the <code>pos</code> field of this byte array output stream to zero. The output stream
102   * can be used again.
103   */
104  public void reset() {
105    this.pos = 0;
106  }
107
108  /**
109   * Copies the content of this Stream into a new byte array.
110   * @return  the contents of this output stream, as new byte array.
111   */
112  public byte[] toByteArray() {
113    return Arrays.copyOf(buf, pos);
114  }
115
116  public void toByteBuff(ByteBuff buff) {
117    buff.put(buf, 0, pos);
118  }
119
120  /**
121   * @return the underlying array where the data gets accumulated
122   */
123  public byte[] getBuffer() {
124    return this.buf;
125  }
126
127  /**
128   * @return The current size of the buffer.
129   */
130  public int size() {
131    return this.pos;
132  }
133}