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;
024import org.apache.hadoop.hbase.nio.ByteBuff;
025import org.apache.hadoop.hbase.util.ByteBufferUtils;
026import org.apache.hadoop.hbase.util.Bytes;
027import org.apache.yetus.audience.InterfaceAudience;
028
029/**
030 * Our own implementation of ByteArrayOutputStream where all methods are NOT synchronized and
031 * supports writing ByteBuffer directly to it.
032 */
033@InterfaceAudience.Private
034public class ByteArrayOutputStream extends OutputStream implements ByteBufferWriter {
035
036  // Borrowed from openJDK:
037  // http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8-b132/java/util/ArrayList.java#221
038  private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
039
040  private byte[] buf;
041  private int pos = 0;
042
043  public ByteArrayOutputStream() {
044    this(32);
045  }
046
047  public ByteArrayOutputStream(int capacity) {
048    this.buf = new byte[capacity];
049  }
050
051  @Override
052  public void write(ByteBuffer b, int off, int len) {
053    checkSizeAndGrow(len);
054    ByteBufferUtils.copyFromBufferToArray(this.buf, b, off, this.pos, len);
055    this.pos += len;
056  }
057
058  @Override
059  public void writeInt(int i) {
060    checkSizeAndGrow(Bytes.SIZEOF_INT);
061    Bytes.putInt(this.buf, this.pos, i);
062    this.pos += Bytes.SIZEOF_INT;
063  }
064
065  @Override
066  public void write(int b) {
067    checkSizeAndGrow(Bytes.SIZEOF_BYTE);
068    buf[this.pos] = (byte) b;
069    this.pos++;
070  }
071
072  @Override
073  public void write(byte[] b, int off, int len) {
074    checkSizeAndGrow(len);
075    System.arraycopy(b, off, this.buf, this.pos, len);
076    this.pos += len;
077  }
078
079  private void checkSizeAndGrow(int extra) {
080    long capacityNeeded = this.pos + (long) extra;
081    if (capacityNeeded > this.buf.length) {
082      // guarantee it's possible to fit
083      if (capacityNeeded > MAX_ARRAY_SIZE) {
084        throw new BufferOverflowException();
085      }
086      // double until hit the cap
087      long nextCapacity = Math.min(this.buf.length << 1, MAX_ARRAY_SIZE);
088      // but make sure there is enough if twice the existing capacity is still too small
089      nextCapacity = Math.max(nextCapacity, capacityNeeded);
090      if (nextCapacity > MAX_ARRAY_SIZE) {
091        throw new BufferOverflowException();
092      }
093      byte[] newBuf = new byte[(int) nextCapacity];
094      System.arraycopy(buf, 0, newBuf, 0, buf.length);
095      buf = newBuf;
096    }
097  }
098
099  /**
100   * Resets the <code>pos</code> field of this byte array output stream to zero. The output stream
101   * can be used again.
102   */
103  public void reset() {
104    this.pos = 0;
105  }
106
107  /**
108   * Copies the content of this Stream into a new byte array.
109   * @return the contents of this output stream, as new byte array.
110   */
111  public byte[] toByteArray() {
112    return Arrays.copyOf(buf, pos);
113  }
114
115  public void toByteBuff(ByteBuff buff) {
116    buff.put(buf, 0, pos);
117  }
118
119  /** Returns the underlying array where the data gets accumulated */
120  public byte[] getBuffer() {
121    return this.buf;
122  }
123
124  /** Returns The current size of the buffer. */
125  public int size() {
126    return this.pos;
127  }
128}