Home | History | Annotate | Download | only in util
      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