• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.dexgen.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      */
ByteArray(byte[] bytes, int start, int end)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      */
ByteArray(byte[] bytes)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      */
size()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      */
slice(int start, int end)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      */
underlyingOffset(int offset, byte[] bytes)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      */
getByte(int off)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      */
getShort(int off)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      */
getInt(int off)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      */
getLong(int off)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      */
getUnsignedByte(int off)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      */
getUnsignedShort(int off)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      */
getBytes(byte[] out, int offset)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      */
checkOffsets(int s, int e)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      */
getByte0(int off)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      */
getUnsignedByte0(int off)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      */
makeDataInputStream()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      */
makeInputStream()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          */
getCursor()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 
MyInputStream()299         public MyInputStream() {
300             cursor = 0;
301             mark = 0;
302         }
303 
read()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 
read(byte[] arr, int offset, int length)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 
available()329         public int available() {
330             return size - cursor;
331         }
332 
mark(int reserve)333         public void mark(int reserve) {
334             mark = cursor;
335         }
336 
reset()337         public void reset() {
338             cursor = mark;
339         }
340 
markSupported()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 
MyDataInputStream(MyInputStream wrapped)355         public MyDataInputStream(MyInputStream wrapped) {
356             super(wrapped);
357 
358             this.wrapped = wrapped;
359         }
360     }
361 }
362