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.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 /** 116 * @return the underlying array where the data gets accumulated 117 */ 118 public byte[] getBuffer() { 119 return this.buf; 120 } 121 122 /** 123 * @return The current size of the buffer. 124 */ 125 public int size() { 126 return this.pos; 127 } 128}