blob: a041c90ae21320a125361e5524b389acb23307b3 [file] [log] [blame]
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.tradefed.util;
import java.util.Arrays;
/**
* A class to represent a lightweight byte array. The goal of this class is to avoid the
* significant space overhead of using Java classes. See, for instance:
* http://benjchristensen.com/2008/05/27/java-memory-usage-ints/
*/
public class ByteArrayList {
private byte[] mStorage;
/** arbitrarily choose 128 bytes as the default size */
private int mMaxBytes = 128;
private int mCurBytes = 0;
private float mGrowthFactor = 2.0f;
/**
* Constructs an empty list with an initial capacity of 128 bytes and growth factor of 2.0
*/
public ByteArrayList() {
mStorage = new byte[mMaxBytes];
}
/**
* Constructs an empty list with the specified initial capacity, and with a growth factor of 2.0
*
* @param defaultSize The initial capacity of the list, in bytes
*/
public ByteArrayList(int defaultSize) {
this();
setSize(defaultSize);
}
/**
* Constructs an empty list with the specified initial capacity and growth factor
*
* @param defaultSize The initial capacity of the list, in bytes
* @param growthFactor The factor by which the capacity is multiplied when the list needs to
* auto-resize. Must be {@code >= 1.1f}.
*/
public ByteArrayList(int defaultSize, float growthFactor) {
this(defaultSize);
if (growthFactor < 1.1f) {
throw new IllegalArgumentException("Growth factor must be at least 1.1");
}
mGrowthFactor = growthFactor;
}
// Methods involved in managing size
/**
* Trims the capacity of this {@code ByteArrayList} instance to be the list's current size.
*/
public void trimToSize() {
setSize(size());
}
/**
* Increases the capacity of this {@code ByteArrayList} instance, if necessary, to ensure that
* it can hold at least the number of bytes specified by the minimum capacity argument.
*
* @param minCapacity The minimum capacity to ensure storage for, in bytes
*/
public void ensureCapacity(int minCapacity) {
if (minCapacity < 0) {
throw new IllegalArgumentException("Minimum capacity must be non-negative");
} else if (minCapacity <= mMaxBytes) {
return;
}
int curSize = mMaxBytes;
if (curSize == 0) {
curSize = 1;
}
/**
* Need to grow the array
* Want: size * (growthFactor * k) >= minCapacity
* So: k >= minCapacity / (size * growthFactor)
* k = ceil(minCapacity / size / growthFactor)
*/
int growthFactorMultiples = (int)Math.ceil(minCapacity / mGrowthFactor / curSize);
// newSize = oldSize * (growthFactor * k) >= minCapacity, from above
float newSize = curSize * mGrowthFactor * growthFactorMultiples;
setSize((int)Math.ceil(newSize));
}
/**
* Sets the storage capacity of the internal storage array to the size specified, truncating the
* internal byte array if needed.
*
* @param size The new capacity of the list, in bytes
*/
void setSize(int size) {
if (size < 0) {
throw new IllegalArgumentException("New size must be non-negative");
} else if (size < mCurBytes) {
mCurBytes = size;
}
mMaxBytes = size;
byte[] newStorage = new byte[size];
System.arraycopy(mStorage, 0, newStorage, 0, mCurBytes);
mStorage = newStorage;
}
// Extra methods specific to this class
/**
* Returns a copy of the contents of this {@code ByteArrayList} as a {@code byte[]}.
*
* @return A {@code byte[]} copy of the list contents
*/
public byte[] getContents() {
byte[] contents = new byte[mCurBytes];
System.arraycopy(mStorage, 0, contents, 0, mCurBytes);
return contents;
}
// Implement useful methods from the List interface
/**
* Appends the specified element to the end of this list
*
* @param b The {@code byte} to append to the list
* @return {@code true}
*/
public boolean add(byte b) {
ensureCapacity(mCurBytes + 1);
mStorage[mCurBytes] = b;
mCurBytes++;
return true;
}
/**
* Appends the full contents of the supplied {@code byte[]} to the list.
*
* @param src The {@code byte[]} to append contents from
* @return {@code true}
*/
public boolean addAll(byte[] src) {
if (src == null) {
throw new NullPointerException();
}
return addAll(src, 0, src.length);
}
/**
* Appends the specified contents of the supplied {@code byte[]} to the list.
*
* @param src The {@code byte[]} to append contents from
* @param srcOffset The index of first element of {@code src} to append
* @param length The quantity of bytes to append to the list
* @return {@code true}
*/
public boolean addAll(byte[] src, int srcOffset, int length) {
if (src == null) {
throw new NullPointerException();
}
ensureCapacity(mCurBytes + length);
System.arraycopy(src, srcOffset, mStorage, mCurBytes, length);
mCurBytes += length;
return true;
}
/**
* Appends the full contents of the supplied {@link ByteArrayList} to the list.
*
* @param src The {@link ByteArrayList} to append contents from
* @return {@code true}
*/
public boolean addall(ByteArrayList src) {
if (src == null) {
throw new NullPointerException();
}
return addAll(src.getContents());
}
/**
* Removes all of the elements from this list.
*/
public void clear() {
mCurBytes = 0;
}
/**
* {@inheritDoc}
*/
@Override
public boolean equals(Object other) {
if (!(other instanceof ByteArrayList)) {
return false;
}
ByteArrayList otherList = (ByteArrayList) other;
if (otherList.size() != size()) {
return false;
}
for (int i = 0; i < size(); i++) {
if (otherList.get(i) != get(i)) {
return false;
}
}
return true;
}
/**
* {@inheritDoc}
*/
@Override
public int hashCode() {
return Arrays.hashCode(mStorage);
}
/**
* Returns {@code true} if this list contains no bytes
*/
public boolean isEmpty() {
return size() == 0;
}
/**
* Returns the element at the specified position in this list
*
* @param idx The index to return
*/
public byte get(int idx) {
if (idx < 0 || idx >= size()) {
throw new IndexOutOfBoundsException();
}
return mStorage[idx];
}
/**
* Replaces the element at the specified position in this list with the specified element
*
* @param idx The index to replace
* @param b The {@code byte} to replace at that index
*/
public byte set(int idx, byte b) {
if (idx < 0 || idx >= size()) {
throw new IndexOutOfBoundsException();
}
byte curVal = mStorage[idx];
mStorage[idx] = b;
return curVal;
}
/**
* Returns the number of bytes in this list
*/
public int size() {
return mCurBytes;
}
// methods to facilitate testing
int getMaxSize() {
return mMaxBytes;
}
}