1 /* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.tradefed.util; 18 19 import java.util.Arrays; 20 21 /** 22 * A class to represent a lightweight byte array. The goal of this class is to avoid the 23 * significant space overhead of using Java classes. See, for instance: 24 * http://benjchristensen.com/2008/05/27/java-memory-usage-ints/ 25 */ 26 public class ByteArrayList { 27 private byte[] mStorage; 28 /** arbitrarily choose 128 bytes as the default size */ 29 private int mMaxBytes = 128; 30 private int mCurBytes = 0; 31 private float mGrowthFactor = 2.0f; 32 33 /** 34 * Constructs an empty list with an initial capacity of 128 bytes and growth factor of 2.0 35 */ ByteArrayList()36 public ByteArrayList() { 37 mStorage = new byte[mMaxBytes]; 38 } 39 40 /** 41 * Constructs an empty list with the specified initial capacity, and with a growth factor of 2.0 42 * 43 * @param defaultSize The initial capacity of the list, in bytes 44 */ ByteArrayList(int defaultSize)45 public ByteArrayList(int defaultSize) { 46 this(); 47 setSize(defaultSize); 48 } 49 50 /** 51 * Constructs an empty list with the specified initial capacity and growth factor 52 * 53 * @param defaultSize The initial capacity of the list, in bytes 54 * @param growthFactor The factor by which the capacity is multiplied when the list needs to 55 * auto-resize. Must be {@code >= 1.1f}. 56 */ ByteArrayList(int defaultSize, float growthFactor)57 public ByteArrayList(int defaultSize, float growthFactor) { 58 this(defaultSize); 59 60 if (growthFactor < 1.1f) { 61 throw new IllegalArgumentException("Growth factor must be at least 1.1"); 62 } 63 mGrowthFactor = growthFactor; 64 } 65 66 // Methods involved in managing size 67 /** 68 * Trims the capacity of this {@code ByteArrayList} instance to be the list's current size. 69 */ trimToSize()70 public void trimToSize() { 71 setSize(size()); 72 } 73 74 /** 75 * Increases the capacity of this {@code ByteArrayList} instance, if necessary, to ensure that 76 * it can hold at least the number of bytes specified by the minimum capacity argument. 77 * 78 * @param minCapacity The minimum capacity to ensure storage for, in bytes 79 */ ensureCapacity(int minCapacity)80 public void ensureCapacity(int minCapacity) { 81 if (minCapacity < 0) { 82 throw new IllegalArgumentException("Minimum capacity must be non-negative"); 83 } else if (minCapacity <= mMaxBytes) { 84 return; 85 } 86 87 int curSize = mMaxBytes; 88 if (curSize == 0) { 89 curSize = 1; 90 } 91 /** 92 * Need to grow the array 93 * Want: size * (growthFactor * k) >= minCapacity 94 * So: k >= minCapacity / (size * growthFactor) 95 * k = ceil(minCapacity / size / growthFactor) 96 */ 97 int growthFactorMultiples = (int)Math.ceil(minCapacity / mGrowthFactor / curSize); 98 // newSize = oldSize * (growthFactor * k) >= minCapacity, from above 99 float newSize = curSize * mGrowthFactor * growthFactorMultiples; 100 setSize((int)Math.ceil(newSize)); 101 } 102 103 /** 104 * Sets the storage capacity of the internal storage array to the size specified, truncating the 105 * internal byte array if needed. 106 * 107 * @param size The new capacity of the list, in bytes 108 */ setSize(int size)109 void setSize(int size) { 110 if (size < 0) { 111 throw new IllegalArgumentException("New size must be non-negative"); 112 } else if (size < mCurBytes) { 113 mCurBytes = size; 114 } 115 mMaxBytes = size; 116 117 byte[] newStorage = new byte[size]; 118 System.arraycopy(mStorage, 0, newStorage, 0, mCurBytes); 119 mStorage = newStorage; 120 } 121 122 // Extra methods specific to this class 123 /** 124 * Returns a copy of the contents of this {@code ByteArrayList} as a {@code byte[]}. 125 * 126 * @return A {@code byte[]} copy of the list contents 127 */ getContents()128 public byte[] getContents() { 129 byte[] contents = new byte[mCurBytes]; 130 System.arraycopy(mStorage, 0, contents, 0, mCurBytes); 131 return contents; 132 } 133 134 // Implement useful methods from the List interface 135 /** 136 * Appends the specified element to the end of this list 137 * 138 * @param b The {@code byte} to append to the list 139 * @return {@code true} 140 */ add(byte b)141 public boolean add(byte b) { 142 ensureCapacity(mCurBytes + 1); 143 mStorage[mCurBytes] = b; 144 mCurBytes++; 145 return true; 146 } 147 148 /** 149 * Appends the full contents of the supplied {@code byte[]} to the list. 150 * 151 * @param src The {@code byte[]} to append contents from 152 * @return {@code true} 153 */ addAll(byte[] src)154 public boolean addAll(byte[] src) { 155 if (src == null) { 156 throw new NullPointerException(); 157 } 158 159 return addAll(src, 0, src.length); 160 } 161 162 /** 163 * Appends the specified contents of the supplied {@code byte[]} to the list. 164 * 165 * @param src The {@code byte[]} to append contents from 166 * @param srcOffset The index of first element of {@code src} to append 167 * @param length The quantity of bytes to append to the list 168 * @return {@code true} 169 */ addAll(byte[] src, int srcOffset, int length)170 public boolean addAll(byte[] src, int srcOffset, int length) { 171 if (src == null) { 172 throw new NullPointerException(); 173 } 174 175 ensureCapacity(mCurBytes + length); 176 System.arraycopy(src, srcOffset, mStorage, mCurBytes, length); 177 mCurBytes += length; 178 return true; 179 } 180 181 /** 182 * Appends the full contents of the supplied {@link ByteArrayList} to the list. 183 * 184 * @param src The {@link ByteArrayList} to append contents from 185 * @return {@code true} 186 */ addall(ByteArrayList src)187 public boolean addall(ByteArrayList src) { 188 if (src == null) { 189 throw new NullPointerException(); 190 } 191 192 return addAll(src.getContents()); 193 } 194 195 /** 196 * Removes all of the elements from this list. 197 */ clear()198 public void clear() { 199 mCurBytes = 0; 200 } 201 202 /** 203 * {@inheritDoc} 204 */ 205 @Override equals(Object other)206 public boolean equals(Object other) { 207 if (!(other instanceof ByteArrayList)) { 208 return false; 209 } 210 ByteArrayList otherList = (ByteArrayList) other; 211 212 if (otherList.size() != size()) { 213 return false; 214 } 215 for (int i = 0; i < size(); i++) { 216 if (otherList.get(i) != get(i)) { 217 return false; 218 } 219 } 220 return true; 221 } 222 223 /** 224 * {@inheritDoc} 225 */ 226 @Override hashCode()227 public int hashCode() { 228 return Arrays.hashCode(mStorage); 229 } 230 231 /** 232 * Returns {@code true} if this list contains no bytes 233 */ isEmpty()234 public boolean isEmpty() { 235 return size() == 0; 236 } 237 238 /** 239 * Returns the element at the specified position in this list 240 * 241 * @param idx The index to return 242 */ get(int idx)243 public byte get(int idx) { 244 if (idx < 0 || idx >= size()) { 245 throw new IndexOutOfBoundsException(); 246 } 247 return mStorage[idx]; 248 } 249 250 /** 251 * Replaces the element at the specified position in this list with the specified element 252 * 253 * @param idx The index to replace 254 * @param b The {@code byte} to replace at that index 255 */ set(int idx, byte b)256 public byte set(int idx, byte b) { 257 if (idx < 0 || idx >= size()) { 258 throw new IndexOutOfBoundsException(); 259 } 260 byte curVal = mStorage[idx]; 261 mStorage[idx] = b; 262 return curVal; 263 } 264 265 /** 266 * Returns the number of bytes in this list 267 */ size()268 public int size() { 269 return mCurBytes; 270 } 271 272 // methods to facilitate testing getMaxSize()273 int getMaxSize() { 274 return mMaxBytes; 275 } 276 } 277 278