/*
* Copyright (C) 2007 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.
*/
/*
* As per the Apache license requirements, this file has been modified
* from its original state.
*
* Such modifications are Copyright (C) 2010 Ben Gruver, and are released
* under the original license
*/
package org.jf.dexlib.Util;
/**
* Wrapper for a byte[]
, which provides read-only access and
* can "reveal" a partial slice of the underlying array.
*
* Note: Multibyte accessors all use big-endian order.
*/
public final class ByteArray {
/** non-null; underlying array */
private final byte[] bytes;
/** >= 0
; start index of the slice (inclusive) */
private final int start;
/** >= 0, <= bytes.length
; size computed as
* end - start
(in the constructor) */
private final int size;
/**
* Constructs an instance.
*
* @param bytes non-null; the underlying array
* @param start >= 0
; start index of the slice (inclusive)
* @param end >= start, <= bytes.length
; end index of
* the slice (exclusive)
*/
public ByteArray(byte[] bytes, int start, int end) {
if (bytes == null) {
throw new NullPointerException("bytes == null");
}
if (start < 0) {
throw new IllegalArgumentException("start < 0");
}
if (end < start) {
throw new IllegalArgumentException("end < start");
}
if (end > bytes.length) {
throw new IllegalArgumentException("end > bytes.length");
}
this.bytes = bytes;
this.start = start;
this.size = end - start;
}
/**
* Constructs an instance from an entire byte[]
.
*
* @param bytes non-null; the underlying array
*/
public ByteArray(byte[] bytes) {
this(bytes, 0, bytes.length);
}
/**
* Gets the size of the array, in bytes.
*
* @return >= 0; the size
*/
public int size() {
return size;
}
/**
* Returns a slice (that is, a sub-array) of this instance.
*
* @param start >= 0
; start index of the slice (inclusive)
* @param end >= start, <= size()
; end index of
* the slice (exclusive)
* @return non-null; the slice
*/
public ByteArray slice(int start, int end) {
checkOffsets(start, end);
return new ByteArray(bytes, start + this.start, end + this.start);
}
/**
* Returns the offset into the given array represented by the given
* offset into this instance.
*
* @param offset offset into this instance
* @param bytes non-null; (alleged) underlying array
* @return corresponding offset into bytes
* @throws IllegalArgumentException thrown if bytes
is
* not the underlying array of this instance
*/
public int underlyingOffset(int offset, byte[] bytes) {
if (bytes != this.bytes) {
throw new IllegalArgumentException("wrong bytes");
}
return start + offset;
}
/**
* Gets the signed byte
value at a particular offset.
*
* @param off >= 0, < size(); offset to fetch
* @return signed byte
at that offset
*/
public int getByte(int off) {
checkOffsets(off, off + 1);
return getByte0(off);
}
/**
* Gets the signed short
value at a particular offset.
*
* @param off >= 0, < (size() - 1); offset to fetch
* @return signed short
at that offset
*/
public int getShort(int off) {
checkOffsets(off, off + 2);
return (getByte0(off) << 8) | getUnsignedByte0(off + 1);
}
/**
* Gets the signed int
value at a particular offset.
*
* @param off >= 0, < (size() - 3); offset to fetch
* @return signed int
at that offset
*/
public int getInt(int off) {
checkOffsets(off, off + 4);
return (getByte0(off) << 24) |
(getUnsignedByte0(off + 1) << 16) |
(getUnsignedByte0(off + 2) << 8) |
getUnsignedByte0(off + 3);
}
/**
* Gets the signed long
value at a particular offset.
*
* @param off >= 0, < (size() - 7); offset to fetch
* @return signed int
at that offset
*/
public long getLong(int off) {
checkOffsets(off, off + 8);
int part1 = (getByte0(off) << 24) |
(getUnsignedByte0(off + 1) << 16) |
(getUnsignedByte0(off + 2) << 8) |
getUnsignedByte0(off + 3);
int part2 = (getByte0(off + 4) << 24) |
(getUnsignedByte0(off + 5) << 16) |
(getUnsignedByte0(off + 6) << 8) |
getUnsignedByte0(off + 7);
return (part2 & 0xffffffffL) | ((long) part1) << 32;
}
/**
* Gets the unsigned byte
value at a particular offset.
*
* @param off >= 0, < size(); offset to fetch
* @return unsigned byte
at that offset
*/
public int getUnsignedByte(int off) {
checkOffsets(off, off + 1);
return getUnsignedByte0(off);
}
/**
* Gets the unsigned short
value at a particular offset.
*
* @param off >= 0, < (size() - 1); offset to fetch
* @return unsigned short
at that offset
*/
public int getUnsignedShort(int off) {
checkOffsets(off, off + 2);
return (getUnsignedByte0(off) << 8) | getUnsignedByte0(off + 1);
}
/**
* Copies the contents of this instance into the given raw
* byte[]
at the given offset. The given array must be
* large enough.
*
* @param out non-null; array to hold the output
* @param offset non-null; index into out
for the first
* byte of output
*/
public void getBytes(byte[] out, int offset) {
if ((out.length - offset) < size) {
throw new IndexOutOfBoundsException("(out.length - offset) < " +
"size()");
}
System.arraycopy(bytes, start, out, offset, size);
}
/**
* Checks a range of offsets for validity, throwing if invalid.
*
* @param s start offset (inclusive)
* @param e end offset (exclusive)
*/
private void checkOffsets(int s, int e) {
if ((s < 0) || (e < s) || (e > size)) {
throw new IllegalArgumentException("bad range: " + s + ".." + e +
"; actual size " + size);
}
}
/**
* Gets the signed byte
value at the given offset,
* without doing any argument checking.
*
* @param off offset to fetch
* @return byte at that offset
*/
private int getByte0(int off) {
return bytes[start + off];
}
/**
* Gets the unsigned byte
value at the given offset,
* without doing any argument checking.
*
* @param off offset to fetch
* @return byte at that offset
*/
private int getUnsignedByte0(int off) {
return bytes[start + off] & 0xff;
}
}