summaryrefslogtreecommitdiffstats
path: root/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/extractor/VorbisBitArray.java
blob: 4ea27c01491504c38fa6aaad2d74dc952506762d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.mozilla.thirdparty.com.google.android.exoplayer2.extractor;

import org.mozilla.thirdparty.com.google.android.exoplayer2.util.Assertions;

/**
 * Wraps a byte array, providing methods that allow it to be read as a Vorbis bitstream.
 *
 * @see <a href="https://www.xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-360002">Vorbis bitpacking
 *     specification</a>
 */
public final class VorbisBitArray {

  private final byte[] data;
  private final int byteLimit;

  private int byteOffset;
  private int bitOffset;

  /**
   * Creates a new instance that wraps an existing array.
   *
   * @param data the array to wrap.
   */
  public VorbisBitArray(byte[] data) {
    this.data = data;
    byteLimit = data.length;
  }

  /**
   * Resets the reading position to zero.
   */
  public void reset() {
    byteOffset = 0;
    bitOffset = 0;
  }

  /**
   * Reads a single bit.
   *
   * @return {@code true} if the bit is set, {@code false} otherwise.
   */
  public boolean readBit() {
    boolean returnValue = (((data[byteOffset] & 0xFF) >> bitOffset) & 0x01) == 1;
    skipBits(1);
    return returnValue;
  }

  /**
   * Reads up to 32 bits.
   *
   * @param numBits The number of bits to read.
   * @return An integer whose bottom {@code numBits} bits hold the read data.
   */
  public int readBits(int numBits) {
    int tempByteOffset = byteOffset;
    int bitsRead = Math.min(numBits, 8 - bitOffset);
    int returnValue = ((data[tempByteOffset++] & 0xFF) >> bitOffset) & (0xFF >> (8 - bitsRead));
    while (bitsRead < numBits) {
      returnValue |= (data[tempByteOffset++] & 0xFF) << bitsRead;
      bitsRead += 8;
    }
    returnValue &= 0xFFFFFFFF >>> (32 - numBits);
    skipBits(numBits);
    return returnValue;
  }

  /**
   * Skips {@code numberOfBits} bits.
   *
   * @param numBits The number of bits to skip.
   */
  public void skipBits(int numBits) {
    int numBytes = numBits / 8;
    byteOffset += numBytes;
    bitOffset += numBits - (numBytes * 8);
    if (bitOffset > 7) {
      byteOffset++;
      bitOffset -= 8;
    }
    assertValidOffset();
  }

  /**
   * Returns the reading position in bits.
   */
  public int getPosition() {
    return byteOffset * 8 + bitOffset;
  }

  /**
   * Sets the reading position in bits.
   *
   * @param position The new reading position in bits.
   */
  public void setPosition(int position) {
    byteOffset = position / 8;
    bitOffset = position - (byteOffset * 8);
    assertValidOffset();
  }

  /**
   * Returns the number of remaining bits.
   */
  public int bitsLeft() {
    return (byteLimit - byteOffset) * 8 - bitOffset;
  }

  private void assertValidOffset() {
    // It is fine for position to be at the end of the array, but no further.
    Assertions.checkState(byteOffset >= 0
        && (byteOffset < byteLimit || (byteOffset == byteLimit && bitOffset == 0)));
  }

}