Home | History | Annotate | Download | only in util
      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