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 }