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