BitwiseInputStream.java revision c25dbf88fc21c707b72502c43695c2c31ff22a23
1/*
2 * Copyright (C) 2008 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.internal.util;
18
19/**
20 * An object that provides bitwise incremental read access to a byte array.
21 *
22 * This is useful, for example, when accessing a series of fields that
23 * may not be aligned on byte boundaries.
24 *
25 * NOTE -- This class is not threadsafe.
26 */
27public class BitwiseInputStream {
28
29    // The byte array being read from.
30    private byte[] mBuf;
31
32    // The current position offset, in bits, from the msb in byte 0.
33    private int mPos;
34
35    // The last valid bit offset.
36    private int mEnd;
37
38    /**
39     * An exception to report access problems.
40     */
41    public static class AccessException extends Exception {
42        public AccessException(String s) {
43            super("BitwiseInputStream access failed: " + s);
44        }
45    }
46
47    /**
48     * Create object from byte array.
49     *
50     * @param buf a byte array containing data
51     */
52    public BitwiseInputStream(byte buf[]) {
53        mBuf = buf;
54        mEnd = buf.length << 3;
55        mPos = 0;
56    }
57
58    /**
59     * Return the number of bit still available for reading.
60     */
61    public int available() {
62        return mEnd - mPos;
63    }
64
65    /**
66     * Read some data and increment the current position.
67     *
68     * The 8-bit limit on access to bitwise streams is intentional to
69     * avoid endianness issues.
70     *
71     * @param bits the amount of data to read (gte 0, lte 8)
72     * @return byte of read data (possibly partially filled, from lsb)
73     */
74    public int read(int bits) throws AccessException {
75        int index = mPos >>> 3;
76        int offset = 16 - (mPos & 0x07) - bits;  // &7==%8
77        if ((bits < 0) || (bits > 8) || ((mPos + bits) > mEnd)) {
78            throw new AccessException("illegal read " +
79                "(pos " + mPos + ", end " + mEnd + ", bits " + bits + ")");
80        }
81        int data = (mBuf[index] & 0xFF) << 8;
82        if (offset < 8) data |= mBuf[index + 1] & 0xFF;
83        data >>>= offset;
84        data &= (-1 >>> (32 - bits));
85        mPos += bits;
86        return data;
87    }
88
89    /**
90     * Read data in bulk into a byte array and increment the current position.
91     *
92     * @param bits the amount of data to read
93     * @return newly allocated byte array of read data
94     */
95    public byte[] readByteArray(int bits) throws AccessException {
96        int bytes = (bits >>> 3) + ((bits & 0x07) > 0 ? 1 : 0);  // &7==%8
97        byte[] arr = new byte[bytes];
98        for (int i = 0; i < bytes; i++) {
99            int increment = Math.min(8, bits - (i << 3));
100            arr[i] = (byte)(read(increment) << (8 - increment));
101        }
102        return arr;
103    }
104
105    /**
106     * Increment the current position and ignore contained data.
107     *
108     * @param bits the amount by which to increment the position
109     */
110    public void skip(int bits) throws AccessException {
111        if ((mPos + bits) > mEnd) {
112            throw new AccessException("illegal skip " +
113                "(pos " + mPos + ", end " + mEnd + ", bits " + bits + ")");
114        }
115        mPos += bits;
116    }
117}
118