183917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink/*
283917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink * Copyright (C) 2008 The Android Open Source Project
383917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink *
483917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink * Licensed under the Apache License, Version 2.0 (the "License");
583917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink * you may not use this file except in compliance with the License.
683917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink * You may obtain a copy of the License at
783917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink *
883917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink *      http://www.apache.org/licenses/LICENSE-2.0
983917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink *
1083917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink * Unless required by applicable law or agreed to in writing, software
1183917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink * distributed under the License is distributed on an "AS IS" BASIS,
1283917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1383917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink * See the License for the specific language governing permissions and
1483917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink * limitations under the License.
1583917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink */
1683917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink
1783917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalinkpackage com.android.internal.util;
1883917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink
1983917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink/**
2083917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink * An object that provides bitwise incremental read access to a byte array.
2183917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink *
2283917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink * This is useful, for example, when accessing a series of fields that
2383917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink * may not be aligned on byte boundaries.
2483917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink *
2583917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink * NOTE -- This class is not threadsafe.
2683917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink */
2783917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalinkpublic class BitwiseInputStream {
2883917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink
2983917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink    // The byte array being read from.
3083917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink    private byte[] mBuf;
3183917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink
3283917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink    // The current position offset, in bits, from the msb in byte 0.
3383917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink    private int mPos;
3483917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink
3583917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink    // The last valid bit offset.
3683917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink    private int mEnd;
3783917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink
3883917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink    /**
3983917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink     * An exception to report access problems.
4083917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink     */
4183917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink    public static class AccessException extends Exception {
4283917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink        public AccessException(String s) {
4383917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink            super("BitwiseInputStream access failed: " + s);
4483917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink        }
4583917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink    }
4683917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink
4783917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink    /**
4883917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink     * Create object from byte array.
4983917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink     *
5083917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink     * @param buf a byte array containing data
5183917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink     */
5283917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink    public BitwiseInputStream(byte buf[]) {
5383917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink        mBuf = buf;
54e564b19ed2f6b8b5667648254bc6c6859bb536e7Tammo Spalink        mEnd = buf.length << 3;
5583917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink        mPos = 0;
5683917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink    }
5783917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink
5883917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink    /**
5983917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink     * Return the number of bit still available for reading.
6083917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink     */
6183917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink    public int available() {
6283917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink        return mEnd - mPos;
6383917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink    }
6483917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink
6583917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink    /**
6683917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink     * Read some data and increment the current position.
6783917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink     *
68c25dbf88fc21c707b72502c43695c2c31ff22a23Tammo Spalink     * The 8-bit limit on access to bitwise streams is intentional to
69c25dbf88fc21c707b72502c43695c2c31ff22a23Tammo Spalink     * avoid endianness issues.
7083917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink     *
71c25dbf88fc21c707b72502c43695c2c31ff22a23Tammo Spalink     * @param bits the amount of data to read (gte 0, lte 8)
7283917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink     * @return byte of read data (possibly partially filled, from lsb)
7383917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink     */
74326c66d888f9b3ce82af93fe63e4828a4a4e3fbeTammo Spalink    public int read(int bits) throws AccessException {
75e564b19ed2f6b8b5667648254bc6c6859bb536e7Tammo Spalink        int index = mPos >>> 3;
76e564b19ed2f6b8b5667648254bc6c6859bb536e7Tammo Spalink        int offset = 16 - (mPos & 0x07) - bits;  // &7==%8
7783917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink        if ((bits < 0) || (bits > 8) || ((mPos + bits) > mEnd)) {
7883917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink            throw new AccessException("illegal read " +
7983917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink                "(pos " + mPos + ", end " + mEnd + ", bits " + bits + ")");
8083917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink        }
81326c66d888f9b3ce82af93fe63e4828a4a4e3fbeTammo Spalink        int data = (mBuf[index] & 0xFF) << 8;
82326c66d888f9b3ce82af93fe63e4828a4a4e3fbeTammo Spalink        if (offset < 8) data |= mBuf[index + 1] & 0xFF;
8383917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink        data >>>= offset;
8483917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink        data &= (-1 >>> (32 - bits));
8583917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink        mPos += bits;
86326c66d888f9b3ce82af93fe63e4828a4a4e3fbeTammo Spalink        return data;
8783917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink    }
8883917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink
8983917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink    /**
9083917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink     * Read data in bulk into a byte array and increment the current position.
9183917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink     *
9283917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink     * @param bits the amount of data to read
9383917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink     * @return newly allocated byte array of read data
9483917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink     */
9583917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink    public byte[] readByteArray(int bits) throws AccessException {
96e564b19ed2f6b8b5667648254bc6c6859bb536e7Tammo Spalink        int bytes = (bits >>> 3) + ((bits & 0x07) > 0 ? 1 : 0);  // &7==%8
9783917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink        byte[] arr = new byte[bytes];
9883917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink        for (int i = 0; i < bytes; i++) {
99e564b19ed2f6b8b5667648254bc6c6859bb536e7Tammo Spalink            int increment = Math.min(8, bits - (i << 3));
10083917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink            arr[i] = (byte)(read(increment) << (8 - increment));
10183917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink        }
10283917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink        return arr;
10383917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink    }
10483917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink
10583917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink    /**
10683917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink     * Increment the current position and ignore contained data.
10783917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink     *
10883917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink     * @param bits the amount by which to increment the position
10983917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink     */
11083917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink    public void skip(int bits) throws AccessException {
11183917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink        if ((mPos + bits) > mEnd) {
11283917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink            throw new AccessException("illegal skip " +
11383917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink                "(pos " + mPos + ", end " + mEnd + ", bits " + bits + ")");
11483917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink        }
11583917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink        mPos += bits;
11683917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink    }
11783917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink}
118