1 /* 2 * Copyright (C) 2013 The Android Open Source Project 3 * 4 * Licensed under the Apache 5 * License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package com.android.tradefed.util; 19 20 import java.io.ByteArrayInputStream; 21 import java.io.IOException; 22 import java.io.InputStream; 23 import java.io.OutputStream; 24 import java.io.SequenceInputStream; 25 26 /** 27 * An in-memory {@link OutputStream} that only keeps a maximum amount of data. 28 * <p/> 29 * This is implemented by keeping a circular byte array of fixed size. 30 * <p/> 31 * Not thread safe. 32 */ 33 public class FixedByteArrayOutputStream extends OutputStream { 34 35 private final byte[] mBuffer; 36 private int mWritePos = 0; 37 /** 38 * flag used to indicate if mWritePos has been wrapped to beginning of buffer. eg amount of 39 * data written to stream is greater than buffer size. 40 */ 41 private boolean mHasWrapped = false; 42 43 /** 44 * Creates a {@link FixedByteArrayOutputStream}. 45 * 46 * @param maxDataSize the approximate max size in bytes to keep in the output stream 47 */ 48 public FixedByteArrayOutputStream(int maxDataSize) { 49 mBuffer = new byte[maxDataSize]; 50 } 51 52 /** 53 * Gets a InputStream for reading collected output. 54 * <p/> 55 * Not thread safe. Assumes no data will be written while being read 56 */ 57 public InputStream getData() { 58 if (mHasWrapped) { 59 InputStream s1 = new ByteArrayInputStream(mBuffer, mWritePos, 60 mBuffer.length - mWritePos); 61 InputStream s2 = new ByteArrayInputStream(mBuffer, 0, mWritePos); 62 return new SequenceInputStream(s1, s2); 63 } else { 64 return new ByteArrayInputStream(mBuffer, 0, mWritePos); 65 } 66 } 67 68 @Override 69 public void write(int data) throws IOException { 70 mBuffer[mWritePos] = (byte)data; 71 mWritePos++; 72 if (mWritePos >= mBuffer.length) { 73 mHasWrapped = true; 74 mWritePos = 0; 75 } 76 } 77 78 @Override 79 public void write(byte[] b, int off, int len) throws IOException { 80 if (b == null) { 81 throw new NullPointerException(); 82 } 83 if (len >= mBuffer.length) { 84 // incoming is larger than buffer just take the tail end of the incoming 85 System.arraycopy(b, off + len - mBuffer.length, mBuffer, 0, mBuffer.length); 86 mHasWrapped = true; 87 mWritePos = 0; 88 } else { 89 // incoming is smaller, fill it into free space of buffer, wrap if needed 90 int freeSpace = mBuffer.length - mWritePos; 91 if (freeSpace >= len) { 92 // current write position to end of buffer is large enough for "len" 93 System.arraycopy(b, off, mBuffer, mWritePos, len); 94 } else { 95 // fill up free space of the buffer first 96 System.arraycopy(b, off, mBuffer, mWritePos, freeSpace); 97 // wrap around the circular buffer 98 System.arraycopy(b, off + freeSpace, mBuffer, 0, len - freeSpace); 99 } 100 // update write position 101 mWritePos += len; 102 if (mWritePos > mBuffer.length) { 103 mHasWrapped = true; 104 mWritePos = mWritePos % mBuffer.length; 105 } 106 } 107 } 108 109 /** 110 * @return the number of bytes currently stored. 111 */ 112 public long size() { 113 if (mHasWrapped) { 114 return mBuffer.length; 115 } else { 116 return mWritePos; 117 } 118 } 119 } 120