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}