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