View Javadoc

1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.apache.hadoop.hbase.io;
19  
20  import java.io.IOException;
21  import java.io.OutputStream;
22  import java.nio.BufferOverflowException;
23  import java.util.Arrays;
24  
25  import org.apache.hadoop.hbase.classification.InterfaceAudience;
26  import org.apache.hadoop.hbase.util.Bytes;
27  
28  /**
29   * Our own implementation of ByteArrayOutputStream where all methods are NOT
30   * synchronized and supports writing ByteBuffer directly to it.
31   */
32  @InterfaceAudience.Private
33  public class ByteArrayOutputStream extends OutputStream {
34  
35    // Borrowed from openJDK:
36    // http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8-b132/java/util/ArrayList.java#221
37    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
38  
39    private byte[] buf;
40    private int pos = 0;
41  
42    public ByteArrayOutputStream() {
43      this(32);
44    }
45  
46    public ByteArrayOutputStream(int capacity) {
47      this.buf = new byte[capacity];
48    }
49  
50    /**
51     * Writes an <code>int</code> to the underlying output stream as four
52     * bytes, high byte first.
53     * @param i the <code>int</code> to write
54     * @throws IOException if an I/O error occurs.
55    */
56    public void writeInt(int i) throws IOException {
57      checkSizeAndGrow(Bytes.SIZEOF_INT);
58      Bytes.putInt(this.buf, this.pos, i);
59      this.pos += Bytes.SIZEOF_INT;
60    }
61  
62    @Override
63    public void write(int b) throws IOException {
64      checkSizeAndGrow(Bytes.SIZEOF_BYTE);
65      buf[this.pos] = (byte) b;
66      this.pos++;
67    }
68  
69    @Override
70    public void write(byte[] b, int off, int len) throws IOException {
71      checkSizeAndGrow(len);
72      System.arraycopy(b, off, this.buf, this.pos, len);
73      this.pos += len;
74    }
75  
76    private void checkSizeAndGrow(int extra) {
77      long capacityNeeded = this.pos + (long) extra;
78      if (capacityNeeded > this.buf.length) {
79        // guarantee it's possible to fit
80        if (capacityNeeded > MAX_ARRAY_SIZE) {
81          throw new BufferOverflowException();
82        }
83        // double until hit the cap
84        long nextCapacity = Math.min(this.buf.length << 1, MAX_ARRAY_SIZE);
85        // but make sure there is enough if twice the existing capacity is still
86        // too small
87        nextCapacity = Math.max(nextCapacity, capacityNeeded);
88        if (nextCapacity > MAX_ARRAY_SIZE) {
89          throw new BufferOverflowException();
90        }
91        byte[] newBuf = new byte[(int) nextCapacity];
92        System.arraycopy(buf, 0, newBuf, 0, buf.length);
93        buf = newBuf;
94      }
95    }
96  
97    /**
98     * Resets the <code>pos</code> field of this byte array output stream to zero.
99     * The output stream can be used again.
100    */
101   public void reset() {
102     this.pos = 0;
103   }
104 
105   /**
106    * Copies the content of this Stream into a new byte array.
107    *
108    * @return the contents of this output stream, as new byte array.
109    */
110   public byte toByteArray()[] {
111     return Arrays.copyOf(buf, pos);
112   }
113 
114   /**
115    * @return the underlying array where the data gets accumulated
116    */
117   public byte[] getBuffer() {
118     return this.buf;
119   }
120 
121   /**
122    * @return The current size of the buffer.
123    */
124   public int size() {
125     return this.pos;
126   }
127 }