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 package com.google.android.exoplayer2.util; 17 18 import static com.google.common.truth.Truth.assertThat; 19 import static org.junit.Assert.assertThrows; 20 21 import androidx.test.ext.junit.runners.AndroidJUnit4; 22 import com.google.android.exoplayer2.C; 23 import com.google.android.exoplayer2.testutil.TestUtil; 24 import java.nio.charset.Charset; 25 import org.junit.Test; 26 import org.junit.runner.RunWith; 27 28 /** Tests for {@link ParsableBitArray}. */ 29 @RunWith(AndroidJUnit4.class) 30 public final class ParsableBitArrayTest { 31 32 @Test readAllBytes()33 public void readAllBytes() { 34 byte[] testData = TestUtil.createByteArray(0x3C, 0xD2, 0x5F); 35 ParsableBitArray testArray = new ParsableBitArray(testData); 36 byte[] bytesRead = new byte[testData.length]; 37 38 testArray.readBytes(bytesRead, 0, testData.length); 39 40 assertThat(bytesRead).isEqualTo(testData); 41 assertThat(testArray.getPosition()).isEqualTo(testData.length * 8); 42 assertThat(testArray.getBytePosition()).isEqualTo(testData.length); 43 } 44 45 @Test readBitInSameByte()46 public void readBitInSameByte() { 47 byte[] testData = TestUtil.createByteArray(0, 0b00110000); 48 ParsableBitArray testArray = new ParsableBitArray(testData); 49 testArray.setPosition(10); 50 51 assertThat(testArray.readBit()).isTrue(); 52 assertThat(testArray.readBit()).isTrue(); 53 assertThat(testArray.readBit()).isFalse(); 54 assertThat(testArray.readBit()).isFalse(); 55 } 56 57 @Test readBitInMultipleBytes()58 public void readBitInMultipleBytes() { 59 byte[] testData = TestUtil.createByteArray(1, 1 << 7); 60 ParsableBitArray testArray = new ParsableBitArray(testData); 61 testArray.setPosition(6); 62 63 assertThat(testArray.readBit()).isFalse(); 64 assertThat(testArray.readBit()).isTrue(); 65 assertThat(testArray.readBit()).isTrue(); 66 assertThat(testArray.readBit()).isFalse(); 67 } 68 69 @Test readBits0Bits()70 public void readBits0Bits() { 71 byte[] testData = TestUtil.createByteArray(0x3C); 72 ParsableBitArray testArray = new ParsableBitArray(testData); 73 74 int result = testArray.readBits(0); 75 76 assertThat(result).isEqualTo(0); 77 } 78 79 @Test readBitsByteAligned()80 public void readBitsByteAligned() { 81 byte[] testData = TestUtil.createByteArray(0x3C, 0xD2, 0x5F, 0x01); 82 ParsableBitArray testArray = new ParsableBitArray(testData); 83 testArray.readBits(8); 84 85 int result = testArray.readBits(18); 86 87 assertThat(result).isEqualTo(0xD25F << 2); 88 assertThat(testArray.getPosition()).isEqualTo(26); 89 } 90 91 @Test readBitsNonByteAligned()92 public void readBitsNonByteAligned() { 93 byte[] testData = TestUtil.createByteArray(0x3C, 0xD2, 0x5F); 94 ParsableBitArray testArray = new ParsableBitArray(testData); 95 testArray.readBits(3); 96 97 int result = testArray.readBits(14); 98 99 assertThat(result).isEqualTo((0x3C & 0b11111) << 9 | 0xD2 << 1 | 0x5F >> 7); 100 assertThat(testArray.getPosition()).isEqualTo(17); 101 } 102 103 @Test readBitsNegativeValue()104 public void readBitsNegativeValue() { 105 byte[] testData = TestUtil.createByteArray(0xF0, 0, 0, 0); 106 ParsableBitArray testArray = new ParsableBitArray(testData); 107 108 int result = testArray.readBits(32); 109 110 assertThat(result).isEqualTo(0xF0000000); 111 } 112 113 @Test readBitsToLong0Bits()114 public void readBitsToLong0Bits() { 115 byte[] testData = TestUtil.createByteArray(0x3C); 116 ParsableBitArray testArray = new ParsableBitArray(testData); 117 118 long result = testArray.readBitsToLong(0); 119 120 assertThat(result).isEqualTo(0); 121 } 122 123 @Test readBitsToLongByteAligned()124 public void readBitsToLongByteAligned() { 125 byte[] testData = TestUtil.createByteArray(0x3C, 0xD2, 0x5F, 0x01, 0xFF, 0x14, 0x60); 126 ParsableBitArray testArray = new ParsableBitArray(testData); 127 testArray.readBits(8); 128 129 long result = testArray.readBitsToLong(45); 130 131 assertThat(result).isEqualTo(0xD25F01FF14L << 5 | 0x60 >> 3); 132 assertThat(testArray.getPosition()).isEqualTo(53); 133 } 134 135 @Test readBitsToLongNonByteAligned()136 public void readBitsToLongNonByteAligned() { 137 byte[] testData = TestUtil.createByteArray(0x3C, 0xD2, 0x5F, 0x01, 0xFF, 0x14, 0x60); 138 ParsableBitArray testArray = new ParsableBitArray(testData); 139 testArray.readBits(3); 140 141 long result = testArray.readBitsToLong(53); 142 143 assertThat(result).isEqualTo((0x3CL & 0b11111) << 48 | 0xD25F01FF1460L); 144 assertThat(testArray.getPosition()).isEqualTo(56); 145 } 146 147 @Test readBitsToLongNegativeValue()148 public void readBitsToLongNegativeValue() { 149 byte[] testData = TestUtil.createByteArray(0xF0, 0, 0, 0, 0, 0, 0, 0); 150 ParsableBitArray testArray = new ParsableBitArray(testData); 151 152 long result = testArray.readBitsToLong(64); 153 154 assertThat(result).isEqualTo(0xF000000000000000L); 155 } 156 157 @Test readBitsToByteArray()158 public void readBitsToByteArray() { 159 byte[] testData = TestUtil.createByteArray(0x3C, 0xD2, 0x5F, 0x01, 0xFF, 0x14, 0x60, 0x99); 160 ParsableBitArray testArray = new ParsableBitArray(testData); 161 162 int numBytes = testData.length; 163 byte[] result = new byte[numBytes]; 164 // Test read within byte boundaries. 165 testArray.readBits(result, 0, 6); 166 assertThat(result[0]).isEqualTo((byte) (testData[0] & 0xFC)); 167 // Test read across byte boundaries. 168 testArray.readBits(result, 0, 8); 169 assertThat(result[0]) 170 .isEqualTo((byte) (((testData[0] & 0x03) << 6) | ((testData[1] & 0xFC) >> 2))); 171 // Test reading across multiple bytes. 172 testArray.readBits(result, 1, numBytes * 8 - 14); 173 for (int i = 1; i < numBytes - 1; i++) { 174 assertThat(result[i]) 175 .isEqualTo((byte) (((testData[i] & 0x03) << 6) | ((testData[i + 1] & 0xFC) >> 2))); 176 } 177 assertThat(result[numBytes - 1]).isEqualTo((byte) ((testData[numBytes - 1] & 0x03) << 6)); 178 assertThat(testArray.bitsLeft()).isEqualTo(0); 179 // Test read last buffer byte across input data bytes. 180 testArray.setPosition(31); 181 result[3] = 0; 182 testArray.readBits(result, 3, 3); 183 assertThat(result[3]).isEqualTo((byte) 0xE0); 184 // Test read bits in the middle of a input data byte. 185 result[0] = 0; 186 assertThat(testArray.getPosition()).isEqualTo(34); 187 testArray.readBits(result, 0, 3); 188 assertThat(result[0]).isEqualTo((byte) 0xE0); 189 // Test read 0 bits. 190 testArray.setPosition(32); 191 result[1] = 0; 192 testArray.readBits(result, 1, 0); 193 assertThat(result[1]).isEqualTo((byte) 0); 194 // Test reading a number of bits divisible by 8. 195 testArray.setPosition(0); 196 testArray.readBits(result, 0, 16); 197 assertThat(result[0]).isEqualTo(testData[0]); 198 assertThat(result[1]).isEqualTo(testData[1]); 199 // Test least significant bits are unmodified. 200 result[1] = (byte) 0xFF; 201 testArray.readBits(result, 0, 9); 202 assertThat(result[0]).isEqualTo((byte) 0x5F); 203 assertThat(result[1]).isEqualTo((byte) 0x7F); 204 } 205 206 @Test skipBytes()207 public void skipBytes() { 208 byte[] testData = TestUtil.createByteArray(0x3C, 0xD2, 0x5F, 0x01); 209 ParsableBitArray testArray = new ParsableBitArray(testData); 210 211 testArray.skipBytes(2); 212 213 assertThat(testArray.readBits(8)).isEqualTo(0x5F); 214 } 215 216 @Test skipBitsByteAligned()217 public void skipBitsByteAligned() { 218 byte[] testData = TestUtil.createByteArray(0x3C, 0xD2, 0x5F, 0x01); 219 ParsableBitArray testArray = new ParsableBitArray(testData); 220 221 testArray.skipBits(16); 222 223 assertThat(testArray.readBits(8)).isEqualTo(0x5F); 224 } 225 226 @Test skipBitsNonByteAligned()227 public void skipBitsNonByteAligned() { 228 byte[] testData = TestUtil.createByteArray(0x3C, 0xD2, 0x5F, 0x01); 229 ParsableBitArray testArray = new ParsableBitArray(testData); 230 231 testArray.skipBits(5); 232 233 assertThat(testArray.readBits(11)).isEqualTo((0x3C & 0b111) << 8 | 0xD2); 234 } 235 236 @Test setPositionByteAligned()237 public void setPositionByteAligned() { 238 byte[] testData = TestUtil.createByteArray(0x3C, 0xD2, 0x5F, 0x01); 239 ParsableBitArray testArray = new ParsableBitArray(testData); 240 241 testArray.setPosition(16); 242 243 assertThat(testArray.readBits(8)).isEqualTo(0x5F); 244 } 245 246 @Test setPositionNonByteAligned()247 public void setPositionNonByteAligned() { 248 byte[] testData = TestUtil.createByteArray(0x3C, 0xD2, 0x5F, 0x01); 249 ParsableBitArray testArray = new ParsableBitArray(testData); 250 251 testArray.setPosition(5); 252 253 assertThat(testArray.readBits(11)).isEqualTo((0x3C & 0b111) << 8 | 0xD2); 254 } 255 256 @Test byteAlignFromNonByteAligned()257 public void byteAlignFromNonByteAligned() { 258 byte[] testData = TestUtil.createByteArray(0x3C, 0xD2, 0x5F, 0x01); 259 ParsableBitArray testArray = new ParsableBitArray(testData); 260 testArray.setPosition(11); 261 262 testArray.byteAlign(); 263 264 assertThat(testArray.getBytePosition()).isEqualTo(2); 265 assertThat(testArray.getPosition()).isEqualTo(16); 266 assertThat(testArray.readBits(8)).isEqualTo(0x5F); 267 } 268 269 @Test byteAlignFromByteAligned()270 public void byteAlignFromByteAligned() { 271 byte[] testData = TestUtil.createByteArray(0x3C, 0xD2, 0x5F, 0x01); 272 ParsableBitArray testArray = new ParsableBitArray(testData); 273 testArray.setPosition(16); 274 275 testArray.byteAlign(); // Should be a no-op. 276 277 assertThat(testArray.getBytePosition()).isEqualTo(2); 278 assertThat(testArray.getPosition()).isEqualTo(16); 279 assertThat(testArray.readBits(8)).isEqualTo(0x5F); 280 } 281 282 @Test readBytesAsStringDefaultsToUtf8()283 public void readBytesAsStringDefaultsToUtf8() { 284 byte[] testData = "a non-åscii strìng".getBytes(Charset.forName(C.UTF8_NAME)); 285 ParsableBitArray testArray = new ParsableBitArray(testData); 286 287 testArray.skipBytes(2); 288 assertThat(testArray.readBytesAsString(testData.length - 2)).isEqualTo("non-åscii strìng"); 289 } 290 291 @Test readBytesAsStringExplicitCharset()292 public void readBytesAsStringExplicitCharset() { 293 byte[] testData = "a non-åscii strìng".getBytes(Charset.forName(C.UTF16_NAME)); 294 ParsableBitArray testArray = new ParsableBitArray(testData); 295 296 testArray.skipBytes(6); 297 assertThat(testArray.readBytesAsString(testData.length - 6, Charset.forName(C.UTF16_NAME))) 298 .isEqualTo("non-åscii strìng"); 299 } 300 301 @Test readBytesNotByteAligned()302 public void readBytesNotByteAligned() { 303 String testString = "test string"; 304 byte[] testData = testString.getBytes(Charset.forName(C.UTF8_NAME)); 305 ParsableBitArray testArray = new ParsableBitArray(testData); 306 307 testArray.skipBit(); 308 assertThrows(IllegalStateException.class, () -> testArray.readBytesAsString(2)); 309 } 310 311 @Test putBitsWithinByte()312 public void putBitsWithinByte() { 313 ParsableBitArray output = new ParsableBitArray(new byte[4]); 314 output.skipBits(1); 315 316 output.putInt(0x3F, 5); 317 318 output.setPosition(0); 319 assertThat(output.readBits(8)).isEqualTo(0x1F << 2); // Check that only 5 bits are modified. 320 } 321 322 @Test putBitsAcrossTwoBytes()323 public void putBitsAcrossTwoBytes() { 324 ParsableBitArray output = new ParsableBitArray(new byte[4]); 325 output.setPosition(12); 326 327 output.putInt(0xFF, 8); 328 329 output.setPosition(8); 330 assertThat(output.readBits(16)).isEqualTo(0x0FF0); 331 } 332 333 @Test putBitsAcrossMultipleBytes()334 public void putBitsAcrossMultipleBytes() { 335 ParsableBitArray output = new ParsableBitArray(new byte[8]); 336 output.setPosition(31); // Writing starts at 31 to test the 30th bit is not modified. 337 338 output.putInt(0xFF146098, 30); // Write only 30 to test the 61st bit is not modified. 339 340 output.setPosition(30); 341 assertThat(output.readBits(32)).isEqualTo(0x3F146098 << 1); 342 } 343 344 @Test put32Bits()345 public void put32Bits() { 346 ParsableBitArray output = new ParsableBitArray(new byte[5]); 347 output.setPosition(4); 348 349 output.putInt(0xFF146098, 32); 350 351 output.setPosition(4); 352 assertThat(output.readBits(32)).isEqualTo(0xFF146098); 353 } 354 355 @Test putFullBytes()356 public void putFullBytes() { 357 ParsableBitArray output = new ParsableBitArray(new byte[2]); 358 359 output.putInt(0x81, 8); 360 361 output.setPosition(0); 362 assertThat(output.readBits(8)).isEqualTo(0x81); 363 } 364 365 @Test noOverwriting()366 public void noOverwriting() { 367 ParsableBitArray output = 368 new ParsableBitArray(TestUtil.createByteArray(0xFF, 0xFF, 0xFF, 0xFF, 0xFF)); 369 output.setPosition(1); 370 371 output.putInt(0, 30); 372 373 output.setPosition(0); 374 assertThat(output.readBits(32)).isEqualTo(0x80000001); 375 } 376 377 } 378