1 /* 2 * Copyright (C) 2007 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.dx.util; 18 19 import java.io.DataInputStream; 20 import java.io.IOException; 21 import java.io.InputStream; 22 import java.util.Arrays; 23 24 /** 25 * Wrapper for a {@code byte[]}, which provides read-only access and 26 * can "reveal" a partial slice of the underlying array. 27 * 28 * <b>Note:</b> Multibyte accessors all use big-endian order. 29 */ 30 public final class ByteArray { 31 /** {@code non-null;} underlying array */ 32 private final byte[] bytes; 33 34 /** {@code >= 0}; start index of the slice (inclusive) */ 35 private final int start; 36 37 /** {@code >= 0, <= bytes.length}; size computed as 38 * {@code end - start} (in the constructor) */ 39 private final int size; 40 41 /** 42 * Constructs an instance. 43 * 44 * @param bytes {@code non-null;} the underlying array 45 * @param start {@code >= 0;} start index of the slice (inclusive) 46 * @param end {@code >= start, <= bytes.length;} end index of 47 * the slice (exclusive) 48 */ 49 public ByteArray(byte[] bytes, int start, int end) { 50 if (bytes == null) { 51 throw new NullPointerException("bytes == null"); 52 } 53 54 if (start < 0) { 55 throw new IllegalArgumentException("start < 0"); 56 } 57 58 if (end < start) { 59 throw new IllegalArgumentException("end < start"); 60 } 61 62 if (end > bytes.length) { 63 throw new IllegalArgumentException("end > bytes.length"); 64 } 65 66 this.bytes = bytes; 67 this.start = start; 68 this.size = end - start; 69 } 70 71 /** 72 * Constructs an instance from an entire {@code byte[]}. 73 * 74 * @param bytes {@code non-null;} the underlying array 75 */ 76 public ByteArray(byte[] bytes) { 77 this(bytes, 0, bytes.length); 78 } 79 80 /** 81 * Gets the size of the array, in bytes. 82 * 83 * @return {@code >= 0;} the size 84 */ 85 public int size() { 86 return size; 87 } 88 89 /** 90 * Returns a slice (that is, a sub-array) of this instance. 91 * 92 * @param start {@code >= 0;} start index of the slice (inclusive) 93 * @param end {@code >= start, <= size();} end index of 94 * the slice (exclusive) 95 * @return {@code non-null;} the slice 96 */ 97 public ByteArray slice(int start, int end) { 98 checkOffsets(start, end); 99 byte[] slicedOut = Arrays.copyOfRange(bytes, start, end); 100 return new ByteArray(slicedOut); 101 } 102 103 /** 104 * Returns the offset into the given array represented by the given 105 * offset into this instance. 106 * 107 * @param offset offset into this instance 108 * @return corresponding offset into {@code bytes} 109 * @throws IllegalArgumentException thrown if {@code bytes} is 110 * not the underlying array of this instance 111 */ 112 public int underlyingOffset(int offset) { 113 return start + offset; 114 } 115 116 /** 117 * Gets the {@code signed byte} value at a particular offset. 118 * 119 * @param off {@code >= 0, < size();} offset to fetch 120 * @return {@code signed byte} at that offset 121 */ 122 public int getByte(int off) { 123 checkOffsets(off, off + 1); 124 return getByte0(off); 125 } 126 127 /** 128 * Gets the {@code signed short} value at a particular offset. 129 * 130 * @param off {@code >= 0, < (size() - 1);} offset to fetch 131 * @return {@code signed short} at that offset 132 */ 133 public int getShort(int off) { 134 checkOffsets(off, off + 2); 135 return (getByte0(off) << 8) | getUnsignedByte0(off + 1); 136 } 137 138 /** 139 * Gets the {@code signed int} value at a particular offset. 140 * 141 * @param off {@code >= 0, < (size() - 3);} offset to fetch 142 * @return {@code signed int} at that offset 143 */ 144 public int getInt(int off) { 145 checkOffsets(off, off + 4); 146 return (getByte0(off) << 24) | 147 (getUnsignedByte0(off + 1) << 16) | 148 (getUnsignedByte0(off + 2) << 8) | 149 getUnsignedByte0(off + 3); 150 } 151 152 /** 153 * Gets the {@code signed long} value at a particular offset. 154 * 155 * @param off {@code >= 0, < (size() - 7);} offset to fetch 156 * @return {@code signed int} at that offset 157 */ 158 public long getLong(int off) { 159 checkOffsets(off, off + 8); 160 int part1 = (getByte0(off) << 24) | 161 (getUnsignedByte0(off + 1) << 16) | 162 (getUnsignedByte0(off + 2) << 8) | 163 getUnsignedByte0(off + 3); 164 int part2 = (getByte0(off + 4) << 24) | 165 (getUnsignedByte0(off + 5) << 16) | 166 (getUnsignedByte0(off + 6) << 8) | 167 getUnsignedByte0(off + 7); 168 169 return (part2 & 0xffffffffL) | ((long) part1) << 32; 170 } 171 172 /** 173 * Gets the {@code unsigned byte} value at a particular offset. 174 * 175 * @param off {@code >= 0, < size();} offset to fetch 176 * @return {@code unsigned byte} at that offset 177 */ 178 public int getUnsignedByte(int off) { 179 checkOffsets(off, off + 1); 180 return getUnsignedByte0(off); 181 } 182 183 /** 184 * Gets the {@code unsigned short} value at a particular offset. 185 * 186 * @param off {@code >= 0, < (size() - 1);} offset to fetch 187 * @return {@code unsigned short} at that offset 188 */ 189 public int getUnsignedShort(int off) { 190 checkOffsets(off, off + 2); 191 return (getUnsignedByte0(off) << 8) | getUnsignedByte0(off + 1); 192 } 193 194 /** 195 * Copies the contents of this instance into the given raw 196 * {@code byte[]} at the given offset. The given array must be 197 * large enough. 198 * 199 * @param out {@code non-null;} array to hold the output 200 * @param offset {@code non-null;} index into {@code out} for the first 201 * byte of output 202 */ 203 public void getBytes(byte[] out, int offset) { 204 if ((out.length - offset) < size) { 205 throw new IndexOutOfBoundsException("(out.length - offset) < " + 206 "size()"); 207 } 208 209 System.arraycopy(bytes, start, out, offset, size); 210 } 211 212 /** 213 * Checks a range of offsets for validity, throwing if invalid. 214 * 215 * @param s start offset (inclusive) 216 * @param e end offset (exclusive) 217 */ 218 private void checkOffsets(int s, int e) { 219 if ((s < 0) || (e < s) || (e > size)) { 220 throw new IllegalArgumentException("bad range: " + s + ".." + e + 221 "; actual size " + size); 222 } 223 } 224 225 /** 226 * Gets the {@code signed byte} value at the given offset, 227 * without doing any argument checking. 228 * 229 * @param off offset to fetch 230 * @return byte at that offset 231 */ 232 private int getByte0(int off) { 233 return bytes[start + off]; 234 } 235 236 /** 237 * Gets the {@code unsigned byte} value at the given offset, 238 * without doing any argument checking. 239 * 240 * @param off offset to fetch 241 * @return byte at that offset 242 */ 243 private int getUnsignedByte0(int off) { 244 return bytes[start + off] & 0xff; 245 } 246 247 /** 248 * Gets a {@code DataInputStream} that reads from this instance, 249 * with the cursor starting at the beginning of this instance's data. 250 * <b>Note:</b> The returned instance may be cast to {@link GetCursor} 251 * if needed. 252 * 253 * @return {@code non-null;} an appropriately-constructed 254 * {@code DataInputStream} instance 255 */ 256 public MyDataInputStream makeDataInputStream() { 257 return new MyDataInputStream(makeInputStream()); 258 } 259 260 /** 261 * Gets a {@code InputStream} that reads from this instance, 262 * with the cursor starting at the beginning of this instance's data. 263 * <b>Note:</b> The returned instance may be cast to {@link GetCursor} 264 * if needed. 265 * 266 * @return {@code non-null;} an appropriately-constructed 267 * {@code InputStream} instancex 268 */ 269 public MyInputStream makeInputStream() { 270 return new MyInputStream(); 271 } 272 273 /** 274 * Helper interface that allows one to get the cursor (of a stream). 275 */ 276 public interface GetCursor { 277 /** 278 * Gets the current cursor. 279 * 280 * @return {@code 0..size();} the cursor 281 */ 282 int getCursor(); 283 } 284 285 /** 286 * Helper class for {@link #makeInputStream}, which implements the 287 * stream functionality. 288 */ 289 public class MyInputStream extends InputStream { 290 /** 0..size; the cursor */ 291 private int cursor; 292 293 /** 0..size; the mark */ 294 private int mark; 295 296 public MyInputStream() { 297 cursor = 0; 298 mark = 0; 299 } 300 301 @Override 302 public int read() throws IOException { 303 if (cursor >= size) { 304 return -1; 305 } 306 307 int result = getUnsignedByte0(cursor); 308 cursor++; 309 return result; 310 } 311 312 @Override 313 public int read(byte[] arr, int offset, int length) { 314 if ((offset + length) > arr.length) { 315 length = arr.length - offset; 316 } 317 318 int maxLength = size - cursor; 319 if (length > maxLength) { 320 length = maxLength; 321 } 322 323 System.arraycopy(bytes, cursor + start, arr, offset, length); 324 cursor += length; 325 return length; 326 } 327 328 @Override 329 public int available() { 330 return size - cursor; 331 } 332 333 @Override 334 public void mark(int reserve) { 335 mark = cursor; 336 } 337 338 @Override 339 public void reset() { 340 cursor = mark; 341 } 342 343 @Override 344 public boolean markSupported() { 345 return true; 346 } 347 } 348 349 /** 350 * Helper class for {@link #makeDataInputStream}. This is used 351 * simply so that the cursor of a wrapped {@link MyInputStream} 352 * instance may be easily determined. 353 */ 354 public static class MyDataInputStream extends DataInputStream { 355 /** {@code non-null;} the underlying {@link MyInputStream} */ 356 private final MyInputStream wrapped; 357 358 public MyDataInputStream(MyInputStream wrapped) { 359 super(wrapped); 360 361 this.wrapped = wrapped; 362 } 363 } 364 } 365