• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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 package com.google.android.exoplayer2.util;
17 
18 import com.google.android.exoplayer2.C;
19 import java.nio.charset.Charset;
20 
21 /**
22  * Wraps a byte array, providing methods that allow it to be read as a bitstream.
23  */
24 public final class ParsableBitArray {
25 
26   public byte[] data;
27 
28   // The offset within the data, stored as the current byte offset, and the bit offset within that
29   // byte (from 0 to 7).
30   private int byteOffset;
31   private int bitOffset;
32   private int byteLimit;
33 
34   /** Creates a new instance that initially has no backing data. */
ParsableBitArray()35   public ParsableBitArray() {
36     data = Util.EMPTY_BYTE_ARRAY;
37   }
38 
39   /**
40    * Creates a new instance that wraps an existing array.
41    *
42    * @param data The data to wrap.
43    */
ParsableBitArray(byte[] data)44   public ParsableBitArray(byte[] data) {
45     this(data, data.length);
46   }
47 
48   /**
49    * Creates a new instance that wraps an existing array.
50    *
51    * @param data The data to wrap.
52    * @param limit The limit in bytes.
53    */
ParsableBitArray(byte[] data, int limit)54   public ParsableBitArray(byte[] data, int limit) {
55     this.data = data;
56     byteLimit = limit;
57   }
58 
59   /**
60    * Updates the instance to wrap {@code data}, and resets the position to zero.
61    *
62    * @param data The array to wrap.
63    */
reset(byte[] data)64   public void reset(byte[] data) {
65     reset(data, data.length);
66   }
67 
68   /**
69    * Sets this instance's data, position and limit to match the provided {@code parsableByteArray}.
70    * Any modifications to the underlying data array will be visible in both instances
71    *
72    * @param parsableByteArray The {@link ParsableByteArray}.
73    */
reset(ParsableByteArray parsableByteArray)74   public void reset(ParsableByteArray parsableByteArray) {
75     reset(parsableByteArray.data, parsableByteArray.limit());
76     setPosition(parsableByteArray.getPosition() * 8);
77   }
78 
79   /**
80    * Updates the instance to wrap {@code data}, and resets the position to zero.
81    *
82    * @param data The array to wrap.
83    * @param limit The limit in bytes.
84    */
reset(byte[] data, int limit)85   public void reset(byte[] data, int limit) {
86     this.data = data;
87     byteOffset = 0;
88     bitOffset = 0;
89     byteLimit = limit;
90   }
91 
92   /**
93    * Returns the number of bits yet to be read.
94    */
bitsLeft()95   public int bitsLeft() {
96     return (byteLimit - byteOffset) * 8 - bitOffset;
97   }
98 
99   /**
100    * Returns the current bit offset.
101    */
getPosition()102   public int getPosition() {
103     return byteOffset * 8 + bitOffset;
104   }
105 
106   /**
107    * Returns the current byte offset. Must only be called when the position is byte aligned.
108    *
109    * @throws IllegalStateException If the position isn't byte aligned.
110    */
getBytePosition()111   public int getBytePosition() {
112     Assertions.checkState(bitOffset == 0);
113     return byteOffset;
114   }
115 
116   /**
117    * Sets the current bit offset.
118    *
119    * @param position The position to set.
120    */
setPosition(int position)121   public void setPosition(int position) {
122     byteOffset = position / 8;
123     bitOffset = position - (byteOffset * 8);
124     assertValidOffset();
125   }
126 
127   /**
128    * Skips a single bit.
129    */
skipBit()130   public void skipBit() {
131     if (++bitOffset == 8) {
132       bitOffset = 0;
133       byteOffset++;
134     }
135     assertValidOffset();
136   }
137 
138   /**
139    * Skips bits and moves current reading position forward.
140    *
141    * @param numBits The number of bits to skip.
142    */
skipBits(int numBits)143   public void skipBits(int numBits) {
144     int numBytes = numBits / 8;
145     byteOffset += numBytes;
146     bitOffset += numBits - (numBytes * 8);
147     if (bitOffset > 7) {
148       byteOffset++;
149       bitOffset -= 8;
150     }
151     assertValidOffset();
152   }
153 
154   /**
155    * Reads a single bit.
156    *
157    * @return Whether the bit is set.
158    */
readBit()159   public boolean readBit() {
160     boolean returnValue = (data[byteOffset] & (0x80 >> bitOffset)) != 0;
161     skipBit();
162     return returnValue;
163   }
164 
165   /**
166    * Reads up to 32 bits.
167    *
168    * @param numBits The number of bits to read.
169    * @return An integer whose bottom {@code numBits} bits hold the read data.
170    */
readBits(int numBits)171   public int readBits(int numBits) {
172     if (numBits == 0) {
173       return 0;
174     }
175     int returnValue = 0;
176     bitOffset += numBits;
177     while (bitOffset > 8) {
178       bitOffset -= 8;
179       returnValue |= (data[byteOffset++] & 0xFF) << bitOffset;
180     }
181     returnValue |= (data[byteOffset] & 0xFF) >> (8 - bitOffset);
182     returnValue &= 0xFFFFFFFF >>> (32 - numBits);
183     if (bitOffset == 8) {
184       bitOffset = 0;
185       byteOffset++;
186     }
187     assertValidOffset();
188     return returnValue;
189   }
190 
191   /**
192    * Reads up to 64 bits.
193    *
194    * @param numBits The number of bits to read.
195    * @return A long whose bottom {@code numBits} bits hold the read data.
196    */
readBitsToLong(int numBits)197   public long readBitsToLong(int numBits) {
198     if (numBits <= 32) {
199       return Util.toUnsignedLong(readBits(numBits));
200     }
201     return Util.toLong(readBits(numBits - 32), readBits(32));
202   }
203 
204   /**
205    * Reads {@code numBits} bits into {@code buffer}.
206    *
207    * @param buffer The array into which the read data should be written. The trailing {@code numBits
208    *     % 8} bits are written into the most significant bits of the last modified {@code buffer}
209    *     byte. The remaining ones are unmodified.
210    * @param offset The offset in {@code buffer} at which the read data should be written.
211    * @param numBits The number of bits to read.
212    */
readBits(byte[] buffer, int offset, int numBits)213   public void readBits(byte[] buffer, int offset, int numBits) {
214     // Whole bytes.
215     int to = offset + (numBits >> 3) /* numBits / 8 */;
216     for (int i = offset; i < to; i++) {
217       buffer[i] = (byte) (data[byteOffset++] << bitOffset);
218       buffer[i] = (byte) (buffer[i] | ((data[byteOffset] & 0xFF) >> (8 - bitOffset)));
219     }
220     // Trailing bits.
221     int bitsLeft = numBits & 7 /* numBits % 8 */;
222     if (bitsLeft == 0) {
223       return;
224     }
225     // Set bits that are going to be overwritten to 0.
226     buffer[to] = (byte) (buffer[to] & (0xFF >> bitsLeft));
227     if (bitOffset + bitsLeft > 8) {
228       // We read the rest of data[byteOffset] and increase byteOffset.
229       buffer[to] = (byte) (buffer[to] | ((data[byteOffset++] & 0xFF) << bitOffset));
230       bitOffset -= 8;
231     }
232     bitOffset += bitsLeft;
233     int lastDataByteTrailingBits = (data[byteOffset] & 0xFF) >> (8 - bitOffset);
234     buffer[to] |= (byte) (lastDataByteTrailingBits << (8 - bitsLeft));
235     if (bitOffset == 8) {
236       bitOffset = 0;
237       byteOffset++;
238     }
239     assertValidOffset();
240   }
241 
242   /**
243    * Aligns the position to the next byte boundary. Does nothing if the position is already aligned.
244    */
byteAlign()245   public void byteAlign() {
246     if (bitOffset == 0) {
247       return;
248     }
249     bitOffset = 0;
250     byteOffset++;
251     assertValidOffset();
252   }
253 
254   /**
255    * Reads the next {@code length} bytes into {@code buffer}. Must only be called when the position
256    * is byte aligned.
257    *
258    * @see System#arraycopy(Object, int, Object, int, int)
259    * @param buffer The array into which the read data should be written.
260    * @param offset The offset in {@code buffer} at which the read data should be written.
261    * @param length The number of bytes to read.
262    * @throws IllegalStateException If the position isn't byte aligned.
263    */
readBytes(byte[] buffer, int offset, int length)264   public void readBytes(byte[] buffer, int offset, int length) {
265     Assertions.checkState(bitOffset == 0);
266     System.arraycopy(data, byteOffset, buffer, offset, length);
267     byteOffset += length;
268     assertValidOffset();
269   }
270 
271   /**
272    * Skips the next {@code length} bytes. Must only be called when the position is byte aligned.
273    *
274    * @param length The number of bytes to read.
275    * @throws IllegalStateException If the position isn't byte aligned.
276    */
skipBytes(int length)277   public void skipBytes(int length) {
278     Assertions.checkState(bitOffset == 0);
279     byteOffset += length;
280     assertValidOffset();
281   }
282 
283   /**
284    * Reads the next {@code length} bytes as a UTF-8 string. Must only be called when the position is
285    * byte aligned.
286    *
287    * @param length The number of bytes to read.
288    * @return The string encoded by the bytes in UTF-8.
289    */
readBytesAsString(int length)290   public String readBytesAsString(int length) {
291     return readBytesAsString(length, Charset.forName(C.UTF8_NAME));
292   }
293 
294   /**
295    * Reads the next {@code length} bytes as a string encoded in {@link Charset}. Must only be called
296    * when the position is byte aligned.
297    *
298    * @param length The number of bytes to read.
299    * @param charset The character set of the encoded characters.
300    * @return The string encoded by the bytes in the specified character set.
301    */
readBytesAsString(int length, Charset charset)302   public String readBytesAsString(int length, Charset charset) {
303     byte[] bytes = new byte[length];
304     readBytes(bytes, 0, length);
305     return new String(bytes, charset);
306   }
307 
308   /**
309    * Overwrites {@code numBits} from this array using the {@code numBits} least significant bits
310    * from {@code value}. Bits are written in order from most significant to least significant. The
311    * read position is advanced by {@code numBits}.
312    *
313    * @param value The integer whose {@code numBits} least significant bits are written into {@link
314    *     #data}.
315    * @param numBits The number of bits to write.
316    */
putInt(int value, int numBits)317   public void putInt(int value, int numBits) {
318     int remainingBitsToRead = numBits;
319     if (numBits < 32) {
320       value &= (1 << numBits) - 1;
321     }
322     int firstByteReadSize = Math.min(8 - bitOffset, numBits);
323     int firstByteRightPaddingSize = 8 - bitOffset - firstByteReadSize;
324     int firstByteBitmask = (0xFF00 >> bitOffset) | ((1 << firstByteRightPaddingSize) - 1);
325     data[byteOffset] = (byte) (data[byteOffset] & firstByteBitmask);
326     int firstByteInputBits = value >>> (numBits - firstByteReadSize);
327     data[byteOffset] =
328         (byte) (data[byteOffset] | (firstByteInputBits << firstByteRightPaddingSize));
329     remainingBitsToRead -= firstByteReadSize;
330     int currentByteIndex = byteOffset + 1;
331     while (remainingBitsToRead > 8) {
332       data[currentByteIndex++] = (byte) (value >>> (remainingBitsToRead - 8));
333       remainingBitsToRead -= 8;
334     }
335     int lastByteRightPaddingSize = 8 - remainingBitsToRead;
336     data[currentByteIndex] =
337         (byte) (data[currentByteIndex] & ((1 << lastByteRightPaddingSize) - 1));
338     int lastByteInput = value & ((1 << remainingBitsToRead) - 1);
339     data[currentByteIndex] =
340         (byte) (data[currentByteIndex] | (lastByteInput << lastByteRightPaddingSize));
341     skipBits(numBits);
342     assertValidOffset();
343   }
344 
assertValidOffset()345   private void assertValidOffset() {
346     // It is fine for position to be at the end of the array, but no further.
347     Assertions.checkState(byteOffset >= 0
348         && (byteOffset < byteLimit || (byteOffset == byteLimit && bitOffset == 0)));
349   }
350 
351 }
352