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/**
20fafb16c5ae18816bd048428e60f46aec1e993b20Marco Nelissen * An object that provides bitwise incremental write access to a byte array.
2183917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink *
2283917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink * This is useful, for example, when writing 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 BitwiseOutputStream {
2883917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink
2983917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink    // The byte array being written to, which will be grown as needed.
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 bit offset, given the current buf length.
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("BitwiseOutputStream access failed: " + s);
4483917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink        }
4583917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink    }
4683917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink
4783917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink    /**
4883917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink     * Create object from hint at desired size.
4983917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink     *
5083917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink     * @param startingLength initial internal byte array length in bytes
5183917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink     */
5283917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink    public BitwiseOutputStream(int startingLength) {
5383917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink        mBuf = new byte[startingLength];
54e564b19ed2f6b8b5667648254bc6c6859bb536e7Tammo Spalink        mEnd = startingLength << 3;
5583917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink        mPos = 0;
5683917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink    }
5783917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink
5883917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink    /**
5983917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink     * Return byte array containing accumulated data, sized to just fit.
6083917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink     *
6183917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink     * @return newly allocated byte array
6283917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink     */
6383917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink    public byte[] toByteArray() {
64e564b19ed2f6b8b5667648254bc6c6859bb536e7Tammo Spalink        int len = (mPos >>> 3) + ((mPos & 0x07) > 0 ? 1 : 0);  // &7==%8
6583917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink        byte[] newBuf = new byte[len];
6683917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink        System.arraycopy(mBuf, 0, newBuf, 0, len);
6783917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink        return newBuf;
6883917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink    }
6983917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink
7083917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink    /**
7183917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink     * Allocate a new internal buffer, if needed.
7283917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink     *
7383917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink     * @param bits additional bits to be accommodated
7483917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink     */
7583917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink    private void possExpand(int bits) {
7683917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink        if ((mPos + bits) < mEnd) return;
77e564b19ed2f6b8b5667648254bc6c6859bb536e7Tammo Spalink        byte[] newBuf = new byte[(mPos + bits) >>> 2];
78e564b19ed2f6b8b5667648254bc6c6859bb536e7Tammo Spalink        System.arraycopy(mBuf, 0, newBuf, 0, mEnd >>> 3);
7983917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink        mBuf = newBuf;
803e3c3f80a90b156ff500076f8655647dfb317acfJake Hamby        mEnd = newBuf.length << 3;
8183917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink    }
8283917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink
8383917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink    /**
8483917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink     * Write some data and increment the current position.
8583917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink     *
86c25dbf88fc21c707b72502c43695c2c31ff22a23Tammo Spalink     * The 8-bit limit on access to bitwise streams is intentional to
87c25dbf88fc21c707b72502c43695c2c31ff22a23Tammo Spalink     * avoid endianness issues.
88c25dbf88fc21c707b72502c43695c2c31ff22a23Tammo Spalink     *
8983917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink     * @param bits the amount of data to write (gte 0, lte 8)
9083917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink     * @param data to write, will be masked to expose only bits param from lsb
9183917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink     */
9283917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink    public void write(int bits, int data) throws AccessException {
9383917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink        if ((bits < 0) || (bits > 8)) {
9483917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink            throw new AccessException("illegal write (" + bits + " bits)");
9583917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink        }
9683917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink        possExpand(bits);
9783917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink        data &= (-1 >>> (32 - bits));
98e564b19ed2f6b8b5667648254bc6c6859bb536e7Tammo Spalink        int index = mPos >>> 3;
99e564b19ed2f6b8b5667648254bc6c6859bb536e7Tammo Spalink        int offset = 16 - (mPos & 0x07) - bits;  // &7==%8
10083917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink        data <<= offset;
10183917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink        mPos += bits;
102326c66d888f9b3ce82af93fe63e4828a4a4e3fbeTammo Spalink        mBuf[index] |= data >>> 8;
103326c66d888f9b3ce82af93fe63e4828a4a4e3fbeTammo Spalink        if (offset < 8) mBuf[index + 1] |= data & 0xFF;
10483917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink    }
10583917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink
10683917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink    /**
10783917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink     * Write data in bulk from a byte array and increment the current position.
10883917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink     *
10983917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink     * @param bits the amount of data to write
11083917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink     * @param arr the byte array containing data to be written
11183917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink     */
11283917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink    public void writeByteArray(int bits, byte[] arr) throws AccessException {
11383917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink        for (int i = 0; i < arr.length; i++) {
114e564b19ed2f6b8b5667648254bc6c6859bb536e7Tammo Spalink            int increment = Math.min(8, bits - (i << 3));
11583917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink            if (increment > 0) {
11683917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink                write(increment, (byte)(arr[i] >>> (8 - increment)));
11783917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink            }
11883917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink        }
11983917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink    }
12083917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink
12183917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink    /**
12283917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink     * Increment the current position, implicitly writing zeros.
12383917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink     *
12483917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink     * @param bits the amount by which to increment the position
12583917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink     */
12683917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink    public void skip(int bits) {
12783917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink        possExpand(bits);
12883917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink        mPos += bits;
12983917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink    }
13083917db040bd7498ebca3b74f879dc1c9e223d8eTammo Spalink}
131