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