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