1 /* 2 * Copyright (C) 2017 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 android.location.cts.asn1.base; 18 19 import com.google.common.annotations.VisibleForTesting; 20 import com.google.common.base.Preconditions; 21 22 import java.util.Arrays; 23 24 /** 25 * Outputs a stream of bits. 26 * 27 * <p>This class is not thread-safe. 28 * 29 */ 30 public final class BitStream { 31 32 /** 33 * The number of bytes that is initially allocated and by which the buffer 34 * gets expanded when necessary. 35 */ 36 static final int BUFFER_CHUNK = 50; 37 private static final int BITS_IN_BYTE = 8; 38 39 private byte[] buffer = new byte[BUFFER_CHUNK]; 40 /** 41 * The position in the buffer of the unfinished byte in progress. 42 */ 43 private int position = 0; 44 /** 45 * The number of high bits accumulated in the unfinished byte. 46 */ 47 private int setBits = 0; 48 getPaddedBytes()49 public byte[] getPaddedBytes() { 50 return Arrays.copyOf(buffer, position + (setBits == 0 ? 0 : 1)); 51 } 52 appendByte(byte data)53 public void appendByte(byte data) { 54 buffer[position] = (byte) (buffer[position] | (data & 0xFF) >> setBits); 55 incrementPosition(); 56 buffer[position] = (byte) (buffer[position] | (data << (BITS_IN_BYTE - setBits)) & 0xFF); 57 } 58 incrementPosition()59 private void incrementPosition() { 60 position++; 61 if (position >= buffer.length) { 62 buffer = Arrays.copyOf(buffer, buffer.length + BUFFER_CHUNK); 63 } 64 } 65 appendBit(boolean one)66 public void appendBit(boolean one) { 67 if (one) { 68 buffer[position] = (byte) (buffer[position] | 1 << (BITS_IN_BYTE - 1 - setBits)); 69 } 70 setBits++; 71 if (setBits == BITS_IN_BYTE) { 72 incrementPosition(); 73 setBits = 0; 74 } 75 } 76 getBitCount()77 public int getBitCount() { 78 return BITS_IN_BYTE * position + setBits; 79 } 80 81 /** 82 * Appends the lowest {@code howManyBits} from the {@code data} in order from 83 * most significant to least significant. 84 */ appendLowBits(int howManyBits, byte data)85 public void appendLowBits(int howManyBits, byte data) { 86 Preconditions.checkArgument(howManyBits < BITS_IN_BYTE); 87 int lowMask = (1 << howManyBits) - 1; 88 int highMask = ~lowMask; 89 int maskedData = data; 90 while (highMask < -1) { 91 maskedData &= ~highMask; 92 highMask >>= 1; 93 appendBit((maskedData & highMask) != 0); 94 } 95 } 96 97 private boolean beginByteAligned; 98 99 public boolean beginsByteAligned() { 100 return beginByteAligned; 101 } 102 103 public void setBeginByteAligned() { 104 beginByteAligned = true; 105 } 106 107 public void spoolToByteBoundary() { 108 if (setBits != 0) { 109 setBits = 0; 110 incrementPosition(); 111 } 112 } 113 } 114