ByteArray.java revision 579d7739c53a2707ad711a2d2cae46d7d782f061
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
17package com.android.dx.util;
18
19import java.io.DataInputStream;
20import java.io.IOException;
21import 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 */
29public 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