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/*
18 * As per the Apache license requirements, this file has been modified
19 * from its original state.
20 *
21 * Such modifications are Copyright (C) 2010 Ben Gruver, and are released
22 * under the original license
23 */
24
25package org.jf.dexlib.Util;
26
27/**
28 * Wrapper for a <code>byte[]</code>, which provides read-only access and
29 * can "reveal" a partial slice of the underlying array.
30 *
31 * <b>Note:</b> Multibyte accessors all use big-endian order.
32 */
33public final class ByteArray {
34    /** non-null; underlying array */
35    private final byte[] bytes;
36
37    /** <code>&gt;= 0</code>; start index of the slice (inclusive) */
38    private final int start;
39
40    /** <code>&gt;= 0, &lt;= bytes.length</code>; size computed as
41     * <code>end - start</code> (in the constructor) */
42    private final int size;
43
44    /**
45     * Constructs an instance.
46     *
47     * @param bytes non-null; the underlying array
48     * @param start <code>&gt;= 0</code>; start index of the slice (inclusive)
49     * @param end <code>&gt;= start, &lt;= bytes.length</code>; end index of
50     * the slice (exclusive)
51     */
52    public ByteArray(byte[] bytes, int start, int end) {
53        if (bytes == null) {
54            throw new NullPointerException("bytes == null");
55        }
56
57        if (start < 0) {
58            throw new IllegalArgumentException("start < 0");
59        }
60
61        if (end < start) {
62            throw new IllegalArgumentException("end < start");
63        }
64
65        if (end > bytes.length) {
66            throw new IllegalArgumentException("end > bytes.length");
67        }
68
69        this.bytes = bytes;
70        this.start = start;
71        this.size = end - start;
72    }
73
74    /**
75     * Constructs an instance from an entire <code>byte[]</code>.
76     *
77     * @param bytes non-null; the underlying array
78     */
79    public ByteArray(byte[] bytes) {
80        this(bytes, 0, bytes.length);
81    }
82
83    /**
84     * Gets the size of the array, in bytes.
85     *
86     * @return &gt;= 0; the size
87     */
88    public int size() {
89        return size;
90    }
91
92    /**
93     * Returns a slice (that is, a sub-array) of this instance.
94     *
95     * @param start <code>&gt;= 0</code>; start index of the slice (inclusive)
96     * @param end <code>&gt;= start, &lt;= size()</code>; end index of
97     * the slice (exclusive)
98     * @return non-null; the slice
99     */
100    public ByteArray slice(int start, int end) {
101        checkOffsets(start, end);
102        return new ByteArray(bytes, start + this.start, end + this.start);
103    }
104
105    /**
106     * Returns the offset into the given array represented by the given
107     * offset into this instance.
108     *
109     * @param offset offset into this instance
110     * @param bytes non-null; (alleged) underlying array
111     * @return corresponding offset into <code>bytes</code>
112     * @throws IllegalArgumentException thrown if <code>bytes</code> is
113     * not the underlying array of this instance
114     */
115    public int underlyingOffset(int offset, byte[] bytes) {
116        if (bytes != this.bytes) {
117            throw new IllegalArgumentException("wrong bytes");
118        }
119
120        return start + offset;
121    }
122
123    /**
124     * Gets the <code>signed byte</code> value at a particular offset.
125     *
126     * @param off <code>&gt;= 0, &lt; size(); offset to fetch
127     * @return <code>signed byte</code> at that offset
128     */
129    public int getByte(int off) {
130        checkOffsets(off, off + 1);
131        return getByte0(off);
132    }
133
134    /**
135     * Gets the <code>signed short</code> value at a particular offset.
136     *
137     * @param off <code>&gt;= 0, &lt; (size() - 1); offset to fetch
138     * @return <code>signed short</code> at that offset
139     */
140    public int getShort(int off) {
141        checkOffsets(off, off + 2);
142        return (getByte0(off) << 8) | getUnsignedByte0(off + 1);
143    }
144
145    /**
146     * Gets the <code>signed int</code> value at a particular offset.
147     *
148     * @param off <code>&gt;= 0, &lt; (size() - 3); offset to fetch
149     * @return <code>signed int</code> at that offset
150     */
151    public int getInt(int off) {
152        checkOffsets(off, off + 4);
153        return (getByte0(off) << 24) |
154            (getUnsignedByte0(off + 1) << 16) |
155            (getUnsignedByte0(off + 2) << 8) |
156            getUnsignedByte0(off + 3);
157    }
158
159    /**
160     * Gets the <code>signed long</code> value at a particular offset.
161     *
162     * @param off <code>&gt;= 0, &lt; (size() - 7); offset to fetch
163     * @return <code>signed int</code> at that offset
164     */
165    public long getLong(int off) {
166        checkOffsets(off, off + 8);
167        int part1 = (getByte0(off) << 24) |
168            (getUnsignedByte0(off + 1) << 16) |
169            (getUnsignedByte0(off + 2) << 8) |
170            getUnsignedByte0(off + 3);
171        int part2 = (getByte0(off + 4) << 24) |
172            (getUnsignedByte0(off + 5) << 16) |
173            (getUnsignedByte0(off + 6) << 8) |
174            getUnsignedByte0(off + 7);
175
176        return (part2 & 0xffffffffL) | ((long) part1) << 32;
177    }
178
179    /**
180     * Gets the <code>unsigned byte</code> value at a particular offset.
181     *
182     * @param off <code>&gt;= 0, &lt; size(); offset to fetch
183     * @return <code>unsigned byte</code> at that offset
184     */
185    public int getUnsignedByte(int off) {
186        checkOffsets(off, off + 1);
187        return getUnsignedByte0(off);
188    }
189
190    /**
191     * Gets the <code>unsigned short</code> value at a particular offset.
192     *
193     * @param off <code>&gt;= 0, &lt; (size() - 1); offset to fetch
194     * @return <code>unsigned short</code> at that offset
195     */
196    public int getUnsignedShort(int off) {
197        checkOffsets(off, off + 2);
198        return (getUnsignedByte0(off) << 8) | getUnsignedByte0(off + 1);
199    }
200
201    /**
202     * Copies the contents of this instance into the given raw
203     * <code>byte[]</code> at the given offset. The given array must be
204     * large enough.
205     *
206     * @param out non-null; array to hold the output
207     * @param offset non-null; index into <code>out</code> for the first
208     * byte of output
209     */
210    public void getBytes(byte[] out, int offset) {
211        if ((out.length - offset) < size) {
212            throw new IndexOutOfBoundsException("(out.length - offset) < " +
213                                                "size()");
214        }
215
216        System.arraycopy(bytes, start, out, offset, size);
217    }
218
219    /**
220     * Checks a range of offsets for validity, throwing if invalid.
221     *
222     * @param s start offset (inclusive)
223     * @param e end offset (exclusive)
224     */
225    private void checkOffsets(int s, int e) {
226        if ((s < 0) || (e < s) || (e > size)) {
227            throw new IllegalArgumentException("bad range: " + s + ".." + e +
228                                               "; actual size " + size);
229        }
230    }
231
232    /**
233     * Gets the <code>signed byte</code> value at the given offset,
234     * without doing any argument checking.
235     *
236     * @param off offset to fetch
237     * @return byte at that offset
238     */
239    private int getByte0(int off) {
240        return bytes[start + off];
241    }
242
243    /**
244     * Gets the <code>unsigned byte</code> value at the given offset,
245     * without doing any argument checking.
246     *
247     * @param off offset to fetch
248     * @return byte at that offset
249     */
250    private int getUnsignedByte0(int off) {
251        return bytes[start + off] & 0xff;
252    }
253}