| /* |
| * 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; |
| } |
| } |
| |