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 import java.util.Arrays;
     23 
     24 /**
     25  * Wrapper for a {@code byte[]}, which provides read-only access and
     26  * can "reveal" a partial slice of the underlying array.
     27  *
     28  * <b>Note:</b> Multibyte accessors all use big-endian order.
     29  */
     30 public final class ByteArray {
     31     /** {@code non-null;} underlying array */
     32     private final byte[] bytes;
     33 
     34     /** {@code >= 0}; start index of the slice (inclusive) */
     35     private final int start;
     36 
     37     /** {@code >= 0, <= bytes.length}; size computed as
     38      * {@code end - start} (in the constructor) */
     39     private final int size;
     40 
     41     /**
     42      * Constructs an instance.
     43      *
     44      * @param bytes {@code non-null;} the underlying array
     45      * @param start {@code >= 0;} start index of the slice (inclusive)
     46      * @param end {@code >= start, <= bytes.length;} end index of
     47      * the slice (exclusive)
     48      */
     49     public ByteArray(byte[] bytes, int start, int end) {
     50         if (bytes == null) {
     51             throw new NullPointerException("bytes == null");
     52         }
     53 
     54         if (start < 0) {
     55             throw new IllegalArgumentException("start < 0");
     56         }
     57 
     58         if (end < start) {
     59             throw new IllegalArgumentException("end < start");
     60         }
     61 
     62         if (end > bytes.length) {
     63             throw new IllegalArgumentException("end > bytes.length");
     64         }
     65 
     66         this.bytes = bytes;
     67         this.start = start;
     68         this.size = end - start;
     69     }
     70 
     71     /**
     72      * Constructs an instance from an entire {@code byte[]}.
     73      *
     74      * @param bytes {@code non-null;} the underlying array
     75      */
     76     public ByteArray(byte[] bytes) {
     77         this(bytes, 0, bytes.length);
     78     }
     79 
     80     /**
     81      * Gets the size of the array, in bytes.
     82      *
     83      * @return {@code >= 0;} the size
     84      */
     85     public int size() {
     86         return size;
     87     }
     88 
     89     /**
     90      * Returns a slice (that is, a sub-array) of this instance.
     91      *
     92      * @param start {@code >= 0;} start index of the slice (inclusive)
     93      * @param end {@code >= start, <= size();} end index of
     94      * the slice (exclusive)
     95      * @return {@code non-null;} the slice
     96      */
     97     public ByteArray slice(int start, int end) {
     98         checkOffsets(start, end);
     99         byte[] slicedOut = Arrays.copyOfRange(bytes, start, end);
    100         return new ByteArray(slicedOut);
    101     }
    102 
    103     /**
    104      * Returns the offset into the given array represented by the given
    105      * offset into this instance.
    106      *
    107      * @param offset offset into this instance
    108      * @return corresponding offset into {@code bytes}
    109      * @throws IllegalArgumentException thrown if {@code bytes} is
    110      * not the underlying array of this instance
    111      */
    112     public int underlyingOffset(int offset) {
    113         return start + offset;
    114     }
    115 
    116     /**
    117      * Gets the {@code signed byte} value at a particular offset.
    118      *
    119      * @param off {@code >= 0, < size();} offset to fetch
    120      * @return {@code signed byte} at that offset
    121      */
    122     public int getByte(int off) {
    123         checkOffsets(off, off + 1);
    124         return getByte0(off);
    125     }
    126 
    127     /**
    128      * Gets the {@code signed short} value at a particular offset.
    129      *
    130      * @param off {@code >= 0, < (size() - 1);} offset to fetch
    131      * @return {@code signed short} at that offset
    132      */
    133     public int getShort(int off) {
    134         checkOffsets(off, off + 2);
    135         return (getByte0(off) << 8) | getUnsignedByte0(off + 1);
    136     }
    137 
    138     /**
    139      * Gets the {@code signed int} value at a particular offset.
    140      *
    141      * @param off {@code >= 0, < (size() - 3);} offset to fetch
    142      * @return {@code signed int} at that offset
    143      */
    144     public int getInt(int off) {
    145         checkOffsets(off, off + 4);
    146         return (getByte0(off) << 24) |
    147             (getUnsignedByte0(off + 1) << 16) |
    148             (getUnsignedByte0(off + 2) << 8) |
    149             getUnsignedByte0(off + 3);
    150     }
    151 
    152     /**
    153      * Gets the {@code signed long} value at a particular offset.
    154      *
    155      * @param off {@code >= 0, < (size() - 7);} offset to fetch
    156      * @return {@code signed int} at that offset
    157      */
    158     public long getLong(int off) {
    159         checkOffsets(off, off + 8);
    160         int part1 = (getByte0(off) << 24) |
    161             (getUnsignedByte0(off + 1) << 16) |
    162             (getUnsignedByte0(off + 2) << 8) |
    163             getUnsignedByte0(off + 3);
    164         int part2 = (getByte0(off + 4) << 24) |
    165             (getUnsignedByte0(off + 5) << 16) |
    166             (getUnsignedByte0(off + 6) << 8) |
    167             getUnsignedByte0(off + 7);
    168 
    169         return (part2 & 0xffffffffL) | ((long) part1) << 32;
    170     }
    171 
    172     /**
    173      * Gets the {@code unsigned byte} value at a particular offset.
    174      *
    175      * @param off {@code >= 0, < size();} offset to fetch
    176      * @return {@code unsigned byte} at that offset
    177      */
    178     public int getUnsignedByte(int off) {
    179         checkOffsets(off, off + 1);
    180         return getUnsignedByte0(off);
    181     }
    182 
    183     /**
    184      * Gets the {@code unsigned short} value at a particular offset.
    185      *
    186      * @param off {@code >= 0, < (size() - 1);} offset to fetch
    187      * @return {@code unsigned short} at that offset
    188      */
    189     public int getUnsignedShort(int off) {
    190         checkOffsets(off, off + 2);
    191         return (getUnsignedByte0(off) << 8) | getUnsignedByte0(off + 1);
    192     }
    193 
    194     /**
    195      * Copies the contents of this instance into the given raw
    196      * {@code byte[]} at the given offset. The given array must be
    197      * large enough.
    198      *
    199      * @param out {@code non-null;} array to hold the output
    200      * @param offset {@code non-null;} index into {@code out} for the first
    201      * byte of output
    202      */
    203     public void getBytes(byte[] out, int offset) {
    204         if ((out.length - offset) < size) {
    205             throw new IndexOutOfBoundsException("(out.length - offset) < " +
    206                                                 "size()");
    207         }
    208 
    209         System.arraycopy(bytes, start, out, offset, size);
    210     }
    211 
    212     /**
    213      * Checks a range of offsets for validity, throwing if invalid.
    214      *
    215      * @param s start offset (inclusive)
    216      * @param e end offset (exclusive)
    217      */
    218     private void checkOffsets(int s, int e) {
    219         if ((s < 0) || (e < s) || (e > size)) {
    220             throw new IllegalArgumentException("bad range: " + s + ".." + e +
    221                                                "; actual size " + size);
    222         }
    223     }
    224 
    225     /**
    226      * Gets the {@code signed byte} value at the given offset,
    227      * without doing any argument checking.
    228      *
    229      * @param off offset to fetch
    230      * @return byte at that offset
    231      */
    232     private int getByte0(int off) {
    233         return bytes[start + off];
    234     }
    235 
    236     /**
    237      * Gets the {@code unsigned byte} value at the given offset,
    238      * without doing any argument checking.
    239      *
    240      * @param off offset to fetch
    241      * @return byte at that offset
    242      */
    243     private int getUnsignedByte0(int off) {
    244         return bytes[start + off] & 0xff;
    245     }
    246 
    247     /**
    248      * Gets a {@code DataInputStream} that reads from this instance,
    249      * with the cursor starting at the beginning of this instance's data.
    250      * <b>Note:</b> The returned instance may be cast to {@link GetCursor}
    251      * if needed.
    252      *
    253      * @return {@code non-null;} an appropriately-constructed
    254      * {@code DataInputStream} instance
    255      */
    256     public MyDataInputStream makeDataInputStream() {
    257         return new MyDataInputStream(makeInputStream());
    258     }
    259 
    260     /**
    261      * Gets a {@code InputStream} that reads from this instance,
    262      * with the cursor starting at the beginning of this instance's data.
    263      * <b>Note:</b> The returned instance may be cast to {@link GetCursor}
    264      * if needed.
    265      *
    266      * @return {@code non-null;} an appropriately-constructed
    267      * {@code InputStream} instancex
    268      */
    269     public MyInputStream makeInputStream() {
    270         return new MyInputStream();
    271     }
    272 
    273     /**
    274      * Helper interface that allows one to get the cursor (of a stream).
    275      */
    276     public interface GetCursor {
    277         /**
    278          * Gets the current cursor.
    279          *
    280          * @return {@code 0..size();} the cursor
    281          */
    282         int getCursor();
    283     }
    284 
    285     /**
    286      * Helper class for {@link #makeInputStream}, which implements the
    287      * stream functionality.
    288      */
    289     public class MyInputStream extends InputStream {
    290         /** 0..size; the cursor */
    291         private int cursor;
    292 
    293         /** 0..size; the mark */
    294         private int mark;
    295 
    296         public MyInputStream() {
    297             cursor = 0;
    298             mark = 0;
    299         }
    300 
    301         @Override
    302         public int read() throws IOException {
    303             if (cursor >= size) {
    304                 return -1;
    305             }
    306 
    307             int result = getUnsignedByte0(cursor);
    308             cursor++;
    309             return result;
    310         }
    311 
    312         @Override
    313         public int read(byte[] arr, int offset, int length) {
    314             if ((offset + length) > arr.length) {
    315                 length = arr.length - offset;
    316             }
    317 
    318             int maxLength = size - cursor;
    319             if (length > maxLength) {
    320                 length = maxLength;
    321             }
    322 
    323             System.arraycopy(bytes, cursor + start, arr, offset, length);
    324             cursor += length;
    325             return length;
    326         }
    327 
    328         @Override
    329         public int available() {
    330             return size - cursor;
    331         }
    332 
    333         @Override
    334         public void mark(int reserve) {
    335             mark = cursor;
    336         }
    337 
    338         @Override
    339         public void reset() {
    340             cursor = mark;
    341         }
    342 
    343         @Override
    344         public boolean markSupported() {
    345             return true;
    346         }
    347     }
    348 
    349     /**
    350      * Helper class for {@link #makeDataInputStream}. This is used
    351      * simply so that the cursor of a wrapped {@link MyInputStream}
    352      * instance may be easily determined.
    353      */
    354     public static class MyDataInputStream extends DataInputStream {
    355         /** {@code non-null;} the underlying {@link MyInputStream} */
    356         private final MyInputStream wrapped;
    357 
    358         public MyDataInputStream(MyInputStream wrapped) {
    359             super(wrapped);
    360 
    361             this.wrapped = wrapped;
    362         }
    363     }
    364 }
    365