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 17 package com.android.internal.util; 18 19 /** 20 * An object that provides bitwise incremental write access to a byte array. 21 * 22 * This is useful, for example, when writing a series of fields that 23 * may not be aligned on byte boundaries. 24 * 25 * NOTE -- This class is not threadsafe. 26 */ 27 public class BitwiseOutputStream { 28 29 // The byte array being written to, which will be grown as needed. 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 bit offset, given the current buf length. 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("BitwiseOutputStream access failed: " + s); 44 } 45 } 46 47 /** 48 * Create object from hint at desired size. 49 * 50 * @param startingLength initial internal byte array length in bytes 51 */ 52 public BitwiseOutputStream(int startingLength) { 53 mBuf = new byte[startingLength]; 54 mEnd = startingLength << 3; 55 mPos = 0; 56 } 57 58 /** 59 * Return byte array containing accumulated data, sized to just fit. 60 * 61 * @return newly allocated byte array 62 */ 63 public byte[] toByteArray() { 64 int len = (mPos >>> 3) + ((mPos & 0x07) > 0 ? 1 : 0); // &7==%8 65 byte[] newBuf = new byte[len]; 66 System.arraycopy(mBuf, 0, newBuf, 0, len); 67 return newBuf; 68 } 69 70 /** 71 * Allocate a new internal buffer, if needed. 72 * 73 * @param bits additional bits to be accommodated 74 */ 75 private void possExpand(int bits) { 76 if ((mPos + bits) < mEnd) return; 77 byte[] newBuf = new byte[(mPos + bits) >>> 2]; 78 System.arraycopy(mBuf, 0, newBuf, 0, mEnd >>> 3); 79 mBuf = newBuf; 80 mEnd = newBuf.length << 3; 81 } 82 83 /** 84 * Write some data and increment the current position. 85 * 86 * The 8-bit limit on access to bitwise streams is intentional to 87 * avoid endianness issues. 88 * 89 * @param bits the amount of data to write (gte 0, lte 8) 90 * @param data to write, will be masked to expose only bits param from lsb 91 */ 92 public void write(int bits, int data) throws AccessException { 93 if ((bits < 0) || (bits > 8)) { 94 throw new AccessException("illegal write (" + bits + " bits)"); 95 } 96 possExpand(bits); 97 data &= (-1 >>> (32 - bits)); 98 int index = mPos >>> 3; 99 int offset = 16 - (mPos & 0x07) - bits; // &7==%8 100 data <<= offset; 101 mPos += bits; 102 mBuf[index] |= data >>> 8; 103 if (offset < 8) mBuf[index + 1] |= data & 0xFF; 104 } 105 106 /** 107 * Write data in bulk from a byte array and increment the current position. 108 * 109 * @param bits the amount of data to write 110 * @param arr the byte array containing data to be written 111 */ 112 public void writeByteArray(int bits, byte[] arr) throws AccessException { 113 for (int i = 0; i < arr.length; i++) { 114 int increment = Math.min(8, bits - (i << 3)); 115 if (increment > 0) { 116 write(increment, (byte)(arr[i] >>> (8 - increment))); 117 } 118 } 119 } 120 121 /** 122 * Increment the current position, implicitly writing zeros. 123 * 124 * @param bits the amount by which to increment the position 125 */ 126 public void skip(int bits) { 127 possExpand(bits); 128 mPos += bits; 129 } 130 } 131