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 } 81 82 /** 83 * Write some data and increment the current position. 84 * 85 * The 8-bit limit on access to bitwise streams is intentional to 86 * avoid endianness issues. 87 * 88 * @param bits the amount of data to write (gte 0, lte 8) 89 * @param data to write, will be masked to expose only bits param from lsb 90 */ 91 public void write(int bits, int data) throws AccessException { 92 if ((bits < 0) || (bits > 8)) { 93 throw new AccessException("illegal write (" + bits + " bits)"); 94 } 95 possExpand(bits); 96 data &= (-1 >>> (32 - bits)); 97 int index = mPos >>> 3; 98 int offset = 16 - (mPos & 0x07) - bits; // &7==%8 99 data <<= offset; 100 mPos += bits; 101 mBuf[index] |= data >>> 8; 102 if (offset < 8) mBuf[index + 1] |= data & 0xFF; 103 } 104 105 /** 106 * Write data in bulk from a byte array and increment the current position. 107 * 108 * @param bits the amount of data to write 109 * @param arr the byte array containing data to be written 110 */ 111 public void writeByteArray(int bits, byte[] arr) throws AccessException { 112 for (int i = 0; i < arr.length; i++) { 113 int increment = Math.min(8, bits - (i << 3)); 114 if (increment > 0) { 115 write(increment, (byte)(arr[i] >>> (8 - increment))); 116 } 117 } 118 } 119 120 /** 121 * Increment the current position, implicitly writing zeros. 122 * 123 * @param bits the amount by which to increment the position 124 */ 125 public void skip(int bits) { 126 possExpand(bits); 127 mPos += bits; 128 } 129 } 130