Clean up + optimize ParsableByteArray.

This commit is contained in:
Oliver Woodman 2015-04-17 20:03:24 +01:00
parent 9092fad8e8
commit 508e13e0bd
11 changed files with 241 additions and 135 deletions

View file

@ -36,12 +36,12 @@ import com.google.android.exoplayer.util.Util;
long basePosition = position + mpegAudioHeader.frameSize;
// Read the VBRI header.
frame.skip(32);
frame.skipBytes(32);
int headerData = frame.readInt();
if (headerData != VBRI_HEADER) {
return null;
}
frame.skip(10);
frame.skipBytes(10);
int numFrames = frame.readInt();
if (numFrames <= 0) {
return null;

View file

@ -56,7 +56,7 @@ import com.google.android.exoplayer.util.Util;
xingBase = 9;
}
}
frame.skip(4 + xingBase);
frame.skipBytes(4 + xingBase);
int headerData = frame.readInt();
if (headerData != XING_HEADER && headerData != INFO_HEADER) {
return null;
@ -80,7 +80,7 @@ import com.google.android.exoplayer.util.Util;
long sizeBytes = frame.readUnsignedIntToInt();
// Read table-of-contents as (flags & 4) == 4.
frame.skip(1);
frame.skipBytes(1);
long[] tableOfContents = new long[99];
for (int i = 0; i < 99; i++) {
tableOfContents[i] = frame.readUnsignedByte();

View file

@ -121,7 +121,7 @@ import java.util.List;
int remainingSamplesPerChunkChanges = stsc.readUnsignedIntToInt() - 1;
Assertions.checkState(stsc.readInt() == 1, "stsc first chunk must be 1");
int samplesPerChunk = stsc.readUnsignedIntToInt();
stsc.skip(4); // Skip the sample description index.
stsc.skipBytes(4); // Skip the sample description index.
int nextSamplesPerChunkChangeChunkIndex = -1;
if (remainingSamplesPerChunkChanges > 0) {
// Store the chunk index when the samples-per-chunk will next change.
@ -220,7 +220,7 @@ import java.util.List;
// Change the samples-per-chunk if required.
if (chunkIndex == nextSamplesPerChunkChangeChunkIndex) {
samplesPerChunk = stsc.readUnsignedIntToInt();
stsc.skip(4); // Skip the sample description index.
stsc.skipBytes(4); // Skip the sample description index.
remainingSamplesPerChunkChanges--;
if (remainingSamplesPerChunkChanges > 0) {
nextSamplesPerChunkChangeChunkIndex = stsc.readUnsignedIntToInt() - 1;
@ -260,7 +260,7 @@ import java.util.List;
int fullAtom = mvhd.readInt();
int version = Atom.parseFullAtomVersion(fullAtom);
mvhd.skip(version == 0 ? 8 : 16);
mvhd.skipBytes(version == 0 ? 8 : 16);
return mvhd.readUnsignedInt();
}
@ -276,10 +276,10 @@ import java.util.List;
int fullAtom = tkhd.readInt();
int version = Atom.parseFullAtomVersion(fullAtom);
tkhd.skip(version == 0 ? 8 : 16);
tkhd.skipBytes(version == 0 ? 8 : 16);
int trackId = tkhd.readInt();
tkhd.skip(4);
tkhd.skipBytes(4);
boolean durationUnknown = true;
int durationPosition = tkhd.getPosition();
@ -292,7 +292,7 @@ import java.util.List;
}
long duration;
if (durationUnknown) {
tkhd.skip(durationByteCount);
tkhd.skipBytes(durationByteCount);
duration = -1;
} else {
duration = version == 0 ? tkhd.readUnsignedInt() : tkhd.readUnsignedLongToLong();
@ -323,7 +323,7 @@ import java.util.List;
int fullAtom = mdhd.readInt();
int version = Atom.parseFullAtomVersion(fullAtom);
mdhd.skip(version == 0 ? 8 : 16);
mdhd.skipBytes(version == 0 ? 8 : 16);
return mdhd.readUnsignedInt();
}
@ -365,11 +365,11 @@ import java.util.List;
int position, int size, long durationUs) {
parent.setPosition(position + Atom.HEADER_SIZE);
parent.skip(24);
parent.skipBytes(24);
int width = parent.readUnsignedShort();
int height = parent.readUnsignedShort();
float pixelWidthHeightRatio = 1;
parent.skip(50);
parent.skipBytes(50);
List<byte[]> initializationData = null;
TrackEncryptionBox trackEncryptionBox = null;
@ -434,7 +434,7 @@ import java.util.List;
if (childAtomType == Atom.TYPE_frma) {
parent.readInt(); // dataFormat.
} else if (childAtomType == Atom.TYPE_schm) {
parent.skip(4);
parent.skipBytes(4);
parent.readInt(); // schemeType. Expect cenc
parent.readInt(); // schemeVersion. Expect 0x00010000
} else if (childAtomType == Atom.TYPE_schi) {
@ -461,7 +461,7 @@ import java.util.List;
int childAtomSize = parent.readInt();
int childAtomType = parent.readInt();
if (childAtomType == Atom.TYPE_tenc) {
parent.skip(4);
parent.skipBytes(4);
int firstInt = parent.readInt();
boolean defaultIsEncrypted = (firstInt >> 8) == 1;
int defaultInitVectorSize = firstInt & 0xFF;
@ -479,10 +479,10 @@ import java.util.List;
long durationUs) {
parent.setPosition(position + Atom.HEADER_SIZE);
parent.skip(24);
parent.skipBytes(24);
int width = parent.readUnsignedShort();
int height = parent.readUnsignedShort();
parent.skip(50);
parent.skipBytes(50);
List<byte[]> initializationData = new ArrayList<byte[]>(1);
int childPosition = parent.getPosition();
@ -505,10 +505,10 @@ import java.util.List;
private static Pair<MediaFormat, TrackEncryptionBox> parseAudioSampleEntry(
ParsableByteArray parent, int atomType, int position, int size, long durationUs) {
parent.setPosition(position + Atom.HEADER_SIZE);
parent.skip(16);
parent.skipBytes(16);
int channelCount = parent.readUnsignedShort();
int sampleSize = parent.readUnsignedShort();
parent.skip(4);
parent.skipBytes(4);
int sampleRate = parent.readUnsignedFixedPoint1616();
int bitrate = MediaFormat.NO_VALUE;
@ -571,34 +571,34 @@ import java.util.List;
private static byte[] parseEsdsFromParent(ParsableByteArray parent, int position) {
parent.setPosition(position + Atom.HEADER_SIZE + 4);
// Start of the ES_Descriptor (defined in 14496-1)
parent.skip(1); // ES_Descriptor tag
parent.skipBytes(1); // ES_Descriptor tag
int varIntByte = parent.readUnsignedByte();
while (varIntByte > 127) {
varIntByte = parent.readUnsignedByte();
}
parent.skip(2); // ES_ID
parent.skipBytes(2); // ES_ID
int flags = parent.readUnsignedByte();
if ((flags & 0x80 /* streamDependenceFlag */) != 0) {
parent.skip(2);
parent.skipBytes(2);
}
if ((flags & 0x40 /* URL_Flag */) != 0) {
parent.skip(parent.readUnsignedShort());
parent.skipBytes(parent.readUnsignedShort());
}
if ((flags & 0x20 /* OCRstreamFlag */) != 0) {
parent.skip(2);
parent.skipBytes(2);
}
// Start of the DecoderConfigDescriptor (defined in 14496-1)
parent.skip(1); // DecoderConfigDescriptor tag
parent.skipBytes(1); // DecoderConfigDescriptor tag
varIntByte = parent.readUnsignedByte();
while (varIntByte > 127) {
varIntByte = parent.readUnsignedByte();
}
parent.skip(13);
parent.skipBytes(13);
// Start of AudioSpecificConfig (defined in 14496-3)
parent.skip(1); // AudioSpecificConfig tag
parent.skipBytes(1); // AudioSpecificConfig tag
varIntByte = parent.readUnsignedByte();
int varInt = varIntByte & 0x7F;
while (varIntByte > 127) {

View file

@ -342,7 +342,7 @@ public final class FragmentedMp4Extractor implements Extractor {
int fullAtom = saiz.readInt();
int flags = Atom.parseFullAtomFlags(fullAtom);
if ((flags & 0x01) == 1) {
saiz.skip(8);
saiz.skipBytes(8);
}
int defaultSampleInfoSize = saiz.readUnsignedByte();
@ -379,9 +379,9 @@ public final class FragmentedMp4Extractor implements Extractor {
int fullAtom = tfhd.readInt();
int flags = Atom.parseFullAtomFlags(fullAtom);
tfhd.skip(4); // trackId
tfhd.skipBytes(4); // trackId
if ((flags & 0x01 /* base_data_offset_present */) != 0) {
tfhd.skip(8);
tfhd.skipBytes(8);
}
int defaultSampleDescriptionIndex =
@ -427,7 +427,7 @@ public final class FragmentedMp4Extractor implements Extractor {
int sampleCount = trun.readUnsignedIntToInt();
if ((flags & 0x01 /* data_offset_present */) != 0) {
trun.skip(4);
trun.skipBytes(4);
}
boolean firstSampleFlagsPresent = (flags & 0x04 /* first_sample_flags_present */) != 0;
@ -528,7 +528,7 @@ public final class FragmentedMp4Extractor implements Extractor {
int fullAtom = atom.readInt();
int version = Atom.parseFullAtomVersion(fullAtom);
atom.skip(4);
atom.skipBytes(4);
long timescale = atom.readUnsignedInt();
long earliestPresentationTime;
long offset = inputPosition;
@ -540,7 +540,7 @@ public final class FragmentedMp4Extractor implements Extractor {
offset += atom.readUnsignedLongToLong();
}
atom.skip(2);
atom.skipBytes(2);
int referenceCount = atom.readUnsignedShort();
int[] sizes = new int[referenceCount];
@ -569,7 +569,7 @@ public final class FragmentedMp4Extractor implements Extractor {
timeUs = Util.scaleLargeTimestamp(time, C.MICROS_PER_SECOND, timescale);
durationsUs[i] = timeUs - timesUs[i];
atom.skip(4);
atom.skipBytes(4);
offset += sizes[i];
}
@ -670,7 +670,7 @@ public final class FragmentedMp4Extractor implements Extractor {
}
// Write the subsample encryption data.
int subsampleCount = sampleEncryptionData.readUnsignedShort();
sampleEncryptionData.skip(-2);
sampleEncryptionData.skipBytes(-2);
int subsampleDataLength = 2 + 6 * subsampleCount;
trackOutput.sampleData(sampleEncryptionData, subsampleDataLength);
return 1 + vectorSize + subsampleDataLength;

View file

@ -37,7 +37,7 @@ import com.google.android.exoplayer.util.ParsableByteArray;
@Override
public void consume(ParsableByteArray seiBuffer, long pesTimeUs, boolean startOfPacket) {
// Skip the NAL prefix and type.
seiBuffer.skip(4);
seiBuffer.skipBytes(4);
int b;
while (seiBuffer.bytesLeft() > 1 /* last byte will be rbsp_trailing_bits */) {
@ -58,7 +58,7 @@ import com.google.android.exoplayer.util.ParsableByteArray;
output.sampleData(seiBuffer, payloadSize);
output.sampleMetadata(pesTimeUs, C.SAMPLE_FLAG_SYNC, payloadSize, 0, null);
} else {
seiBuffer.skip(payloadSize);
seiBuffer.skipBytes(payloadSize);
}
}
}

View file

@ -104,7 +104,7 @@ public final class TsExtractor implements Extractor {
// Skip the adaptation field.
if (adaptationFieldExists) {
int adaptationFieldLength = tsPacketBuffer.readUnsignedByte();
tsPacketBuffer.skip(adaptationFieldLength);
tsPacketBuffer.skipBytes(adaptationFieldLength);
}
// Read the payload.
@ -172,7 +172,7 @@ public final class TsExtractor implements Extractor {
// Skip pointer.
if (payloadUnitStartIndicator) {
int pointerField = data.readUnsignedByte();
data.skip(pointerField);
data.skipBytes(pointerField);
}
data.readBytes(patScratch, 3);
@ -180,7 +180,7 @@ public final class TsExtractor implements Extractor {
int sectionLength = patScratch.readBits(12);
// transport_stream_id (16), reserved (2), version_number (5), current_next_indicator (1),
// section_number (8), last_section_number (8)
data.skip(5);
data.skipBytes(5);
int programCount = (sectionLength - 9) / 4;
for (int i = 0; i < programCount; i++) {
@ -212,7 +212,7 @@ public final class TsExtractor implements Extractor {
// Skip pointer.
if (payloadUnitStartIndicator) {
int pointerField = data.readUnsignedByte();
data.skip(pointerField);
data.skipBytes(pointerField);
}
data.readBytes(pmtScratch, 3);
@ -222,14 +222,14 @@ public final class TsExtractor implements Extractor {
// program_number (16), reserved (2), version_number (5), current_next_indicator (1),
// section_number (8), last_section_number (8), reserved (3), PCR_PID (13)
// Skip the rest of the PMT header.
data.skip(7);
data.skipBytes(7);
data.readBytes(pmtScratch, 2);
pmtScratch.skipBits(4);
int programInfoLength = pmtScratch.readBits(12);
// Skip the descriptors.
data.skip(programInfoLength);
data.skipBytes(programInfoLength);
int entriesSize = sectionLength - 9 /* Size of the rest of the fields before descriptors */
- programInfoLength - 4 /* CRC size */;
@ -242,7 +242,7 @@ public final class TsExtractor implements Extractor {
int esInfoLength = pmtScratch.readBits(12);
// Skip the descriptors.
data.skip(esInfoLength);
data.skipBytes(esInfoLength);
entriesSize -= esInfoLength + 5;
if (streamReaders.get(streamType) != null) {
@ -341,7 +341,7 @@ public final class TsExtractor implements Extractor {
while (data.bytesLeft() > 0) {
switch (state) {
case STATE_FINDING_HEADER:
data.skip(data.bytesLeft());
data.skipBytes(data.bytesLeft());
break;
case STATE_READING_HEADER:
if (continueRead(data, pesScratch.getData(), HEADER_SIZE)) {
@ -398,7 +398,7 @@ public final class TsExtractor implements Extractor {
if (bytesToRead <= 0) {
return true;
} else if (target == null) {
source.skip(bytesToRead);
source.skipBytes(bytesToRead);
} else {
source.readBytes(target, bytesRead, bytesToRead);
}

View file

@ -57,7 +57,7 @@ public class Id3Parser implements MetadataParser<Map<String, Object>> {
}
// Skip frame flags.
id3Data.skip(2);
id3Data.skipBytes(2);
// Check Frame ID == TXXX.
if (frameId0 == 'T' && frameId1 == 'X' && frameId2 == 'X' && frameId3 == 'X') {
int encoding = id3Data.readUnsignedByte();
@ -168,7 +168,7 @@ public class Id3Parser implements MetadataParser<Map<String, Object>> {
throw new ParserException(String.format(
"Unexpected ID3 file identifier, expected \"ID3\", actual \"%c%c%c\".", id1, id2, id3));
}
id3Buffer.skip(2); // Skip version.
id3Buffer.skipBytes(2); // Skip version.
int flags = id3Buffer.readUnsignedByte();
int id3Size = id3Buffer.readSynchSafeInt();
@ -177,7 +177,7 @@ public class Id3Parser implements MetadataParser<Map<String, Object>> {
if ((flags & 0x2) != 0) {
int extendedHeaderSize = id3Buffer.readSynchSafeInt();
if (extendedHeaderSize > 4) {
id3Buffer.skip(extendedHeaderSize - 4);
id3Buffer.skipBytes(extendedHeaderSize - 4);
}
id3Size -= extendedHeaderSize;
}

View file

@ -48,7 +48,7 @@ public final class H264Util {
public static byte[] parseChildNalUnit(ParsableByteArray atom) {
int length = atom.readUnsignedShort();
int offset = atom.getPosition();
atom.skip(length);
atom.skipBytes(length);
return CodecSpecificDataUtil.buildNalUnit(atom.data, offset, length);
}

View file

@ -123,9 +123,7 @@ public final class ParsableByteArray {
* @throws IllegalArgumentException Thrown if the new position is neither in nor at the end of the
* array.
*/
// TODO: Rename to skipBytes so that it's clearer how much data is being skipped in code where
// both ParsableBitArray and ParsableByteArray are in use.
public void skip(int bytes) {
public void skipBytes(int bytes) {
setPosition(position + bytes);
}
@ -136,8 +134,6 @@ public final class ParsableByteArray {
* @param bitArray The {@link ParsableBitArray} into which the bytes should be read.
* @param length The number of bytes to write.
*/
// TODO: It's possible to have bitArray directly index into the same array as is being wrapped
// by this instance. Decide whether it's worth doing this.
public void readBytes(ParsableBitArray bitArray, int length) {
readBytes(bitArray.getData(), 0, length);
bitArray.setPosition(0);
@ -165,52 +161,55 @@ public final class ParsableByteArray {
/** Reads the next byte as an unsigned value. */
public int readUnsignedByte() {
int result = shiftIntoInt(data, position, 1);
position += 1;
return result;
return (data[position++] & 0xFF);
}
/** Reads the next two bytes as an unsigned value. */
public int readUnsignedShort() {
int result = shiftIntoInt(data, position, 2);
position += 2;
return result;
return (data[position++] & 0xFF) << 8
| (data[position++] & 0xFF);
}
/** Reads the next three bytes as an unsigned value. */
public int readUnsignedInt24() {
int result = shiftIntoInt(data, position, 3);
position += 3;
return result;
return (data[position++] & 0xFF) << 16
| (data[position++] & 0xFF) << 8
| (data[position++] & 0xFF);
}
/** Reads the next four bytes as an unsigned value. */
public long readUnsignedInt() {
long result = shiftIntoLong(data, position, 4);
position += 4;
return result;
return (data[position++] & 0xFFL) << 24
| (data[position++] & 0xFFL) << 16
| (data[position++] & 0xFFL) << 8
| (data[position++] & 0xFFL);
}
/** Reads the next four bytes as a signed value. */
public int readInt() {
// shiftIntoInt inlined as performance optimization.
return (data[position++] & 0xFF) << 24
| (data[position++] & 0xFF) << 16
| (data[position++] & 0xFF) << 8
| data[position++] & 0xFF;
| (data[position++] & 0xFF);
}
/** Reads the next eight bytes as a signed value. */
public long readLong() {
long result = shiftIntoLong(data, position, 8);
position += 8;
return result;
return (data[position++] & 0xFFL) << 56
| (data[position++] & 0xFFL) << 48
| (data[position++] & 0xFFL) << 40
| (data[position++] & 0xFFL) << 32
| (data[position++] & 0xFFL) << 24
| (data[position++] & 0xFFL) << 16
| (data[position++] & 0xFFL) << 8
| (data[position++] & 0xFFL);
}
/** Reads the next four bytes, returning the integer portion of the fixed point 16.16 integer. */
public int readUnsignedFixedPoint1616() {
int result = shiftIntoInt(data, position, 2);
position += 4;
int result = (data[position++] & 0xFF) << 8
| (data[position++] & 0xFF);
position += 2; // Skip the non-integer portion.
return result;
}
@ -233,16 +232,12 @@ public final class ParsableByteArray {
/**
* Reads the next four bytes as an unsigned integer into an integer, if the top bit is a zero.
*
* @throws IllegalArgumentException Thrown if the top bit of the input data is set.
* @throws IllegalStateException Thrown if the top bit of the input data is set.
*/
public int readUnsignedIntToInt() {
// shiftIntoInt inlined as performance optimization.
final int result = (data[position++] & 0xFF) << 24
| (data[position++] & 0xFF) << 16
| (data[position++] & 0xFF) << 8
| data[position++] & 0xFF;
int result = readInt();
if (result < 0) {
throw new IllegalArgumentException("Top bit not zero: " + result);
throw new IllegalStateException("Top bit not zero: " + result);
}
return result;
}
@ -250,33 +245,12 @@ public final class ParsableByteArray {
/**
* Reads the next eight bytes as an unsigned long into a long, if the top bit is a zero.
*
* @throws IllegalArgumentException Thrown if the top bit of the input data is set.
* @throws IllegalStateException Thrown if the top bit of the input data is set.
*/
public long readUnsignedLongToLong() {
long result = shiftIntoLong(data, position, 8);
position += 8;
long result = readLong();
if (result < 0) {
throw new IllegalArgumentException("Top bit not zero: " + result);
}
return result;
}
/** Reads {@code length} bytes into an int at {@code offset} in {@code bytes}. */
private static int shiftIntoInt(byte[] bytes, int offset, int length) {
int result = 0xFF & bytes[offset];
for (int i = offset + 1; i < offset + length; i++) {
result <<= 8;
result |= 0xFF & bytes[i];
}
return result;
}
/** Reads {@code length} bytes into a long at {@code offset} in {@code bytes}. */
private static long shiftIntoLong(byte[] bytes, int offset, int length) {
long result = 0xFF & bytes[offset];
for (int i = offset + 1; i < offset + length; i++) {
result <<= 8;
result |= 0xFF & bytes[i];
throw new IllegalStateException("Top bit not zero: " + result);
}
return result;
}

View file

@ -57,7 +57,7 @@ public class UrlTemplateTest extends TestCase {
String template = "$IllegalId$";
try {
UrlTemplate.compile(template);
assertTrue(false);
fail();
} catch (IllegalArgumentException e) {
// Expected.
}

View file

@ -17,6 +17,7 @@ package com.google.android.exoplayer.util;
import junit.framework.TestCase;
import java.nio.ByteBuffer;
import java.util.Arrays;
/**
@ -24,49 +25,176 @@ import java.util.Arrays;
*/
public class ParsableByteArrayTest extends TestCase {
private static final byte[] ARRAY_ELEMENTS =
private static final byte[] TEST_DATA =
new byte[] {0x0F, (byte) 0xFF, (byte) 0x42, (byte) 0x0F, 0x00, 0x00, 0x00, 0x00};
private ParsableByteArray parsableByteArray;
@Override
public void setUp() {
parsableByteArray = new ParsableByteArray(ARRAY_ELEMENTS.length);
System.arraycopy(ARRAY_ELEMENTS, 0, parsableByteArray.data, 0, ARRAY_ELEMENTS.length);
private static ParsableByteArray getTestDataArray() {
ParsableByteArray testArray = new ParsableByteArray(TEST_DATA.length);
System.arraycopy(TEST_DATA, 0, testArray.data, 0, TEST_DATA.length);
return testArray;
}
public void testReadInt() {
// When reading a signed integer
int value = parsableByteArray.readInt();
// Then the read value is equal to the array elements interpreted as an int.
assertEquals((0xFF & ARRAY_ELEMENTS[0]) << 24 | (0xFF & ARRAY_ELEMENTS[1]) << 16
| (0xFF & ARRAY_ELEMENTS[2]) << 8 | (0xFF & ARRAY_ELEMENTS[3]), value);
testReadInt(0);
testReadInt(1);
testReadInt(-1);
testReadInt(Integer.MIN_VALUE);
testReadInt(Integer.MAX_VALUE);
}
public void testSkipBack() {
// When reading an unsigned integer
long value = parsableByteArray.readUnsignedInt();
private static void testReadInt(int testValue) {
ParsableByteArray testArray = new ParsableByteArray(
ByteBuffer.allocate(4).putInt(testValue).array());
int readValue = testArray.readInt();
// Then skipping back and reading gives the same value.
parsableByteArray.skip(-4);
assertEquals(value, parsableByteArray.readUnsignedInt());
// Assert that the value we read was the value we wrote.
assertEquals(testValue, readValue);
// And that the position advanced as expected.
assertEquals(4, testArray.getPosition());
// And that skipping back and reading gives the same results.
testArray.skipBytes(-4);
readValue = testArray.readInt();
assertEquals(testValue, readValue);
assertEquals(4, testArray.getPosition());
}
public void testReadUnsignedInt() {
testReadUnsignedInt(0);
testReadUnsignedInt(1);
testReadUnsignedInt(Integer.MAX_VALUE);
testReadUnsignedInt(Integer.MAX_VALUE + 1L);
testReadUnsignedInt(0xFFFFFFFFL);
}
private static void testReadUnsignedInt(long testValue) {
ParsableByteArray testArray = new ParsableByteArray(
Arrays.copyOfRange(ByteBuffer.allocate(8).putLong(testValue).array(), 4, 8));
long readValue = testArray.readUnsignedInt();
// Assert that the value we read was the value we wrote.
assertEquals(testValue, readValue);
// And that the position advanced as expected.
assertEquals(4, testArray.getPosition());
// And that skipping back and reading gives the same results.
testArray.skipBytes(-4);
readValue = testArray.readUnsignedInt();
assertEquals(testValue, readValue);
assertEquals(4, testArray.getPosition());
}
public void testReadUnsignedIntToInt() {
testReadUnsignedIntToInt(0);
testReadUnsignedIntToInt(1);
testReadUnsignedIntToInt(Integer.MAX_VALUE);
try {
testReadUnsignedIntToInt(-1);
fail();
} catch (IllegalStateException e) {
// Expected.
}
try {
testReadUnsignedIntToInt(Integer.MIN_VALUE);
fail();
} catch (IllegalStateException e) {
// Expected.
}
}
private static void testReadUnsignedIntToInt(int testValue) {
ParsableByteArray testArray = new ParsableByteArray(
ByteBuffer.allocate(4).putInt(testValue).array());
int readValue = testArray.readUnsignedIntToInt();
// Assert that the value we read was the value we wrote.
assertEquals(testValue, readValue);
// And that the position advanced as expected.
assertEquals(4, testArray.getPosition());
// And that skipping back and reading gives the same results.
testArray.skipBytes(-4);
readValue = testArray.readUnsignedIntToInt();
assertEquals(testValue, readValue);
assertEquals(4, testArray.getPosition());
}
public void testReadUnsignedLongToLong() {
testReadUnsignedLongToLong(0);
testReadUnsignedLongToLong(1);
testReadUnsignedLongToLong(Long.MAX_VALUE);
try {
testReadUnsignedLongToLong(-1);
fail();
} catch (IllegalStateException e) {
// Expected.
}
try {
testReadUnsignedLongToLong(Long.MIN_VALUE);
fail();
} catch (IllegalStateException e) {
// Expected.
}
}
private static void testReadUnsignedLongToLong(long testValue) {
ParsableByteArray testArray = new ParsableByteArray(
ByteBuffer.allocate(8).putLong(testValue).array());
long readValue = testArray.readUnsignedLongToLong();
// Assert that the value we read was the value we wrote.
assertEquals(testValue, readValue);
// And that the position advanced as expected.
assertEquals(8, testArray.getPosition());
// And that skipping back and reading gives the same results.
testArray.skipBytes(-8);
readValue = testArray.readUnsignedLongToLong();
assertEquals(testValue, readValue);
assertEquals(8, testArray.getPosition());
}
public void testReadLong() {
testReadLong(0);
testReadLong(1);
testReadLong(-1);
testReadLong(Long.MIN_VALUE);
testReadLong(Long.MAX_VALUE);
}
private static void testReadLong(long testValue) {
ParsableByteArray testArray = new ParsableByteArray(
ByteBuffer.allocate(8).putLong(testValue).array());
long readValue = testArray.readLong();
// Assert that the value we read was the value we wrote.
assertEquals(testValue, readValue);
// And that the position advanced as expected.
assertEquals(8, testArray.getPosition());
// And that skipping back and reading gives the same results.
testArray.skipBytes(-8);
readValue = testArray.readLong();
assertEquals(testValue, readValue);
assertEquals(8, testArray.getPosition());
}
public void testReadingMovesPosition() {
ParsableByteArray parsableByteArray = getTestDataArray();
// Given an array at the start
assertEquals(0, parsableByteArray.getPosition());
// When reading an integer, the position advances
parsableByteArray.readUnsignedInt();
assertEquals(4, parsableByteArray.getPosition());
}
public void testOutOfBoundsThrows() {
ParsableByteArray parsableByteArray = getTestDataArray();
// Given an array at the end
parsableByteArray.readUnsignedLongToLong();
assertEquals(ARRAY_ELEMENTS.length, parsableByteArray.getPosition());
assertEquals(TEST_DATA.length, parsableByteArray.getPosition());
// Then reading more data throws.
try {
parsableByteArray.readUnsignedInt();
@ -77,21 +205,23 @@ public class ParsableByteArrayTest extends TestCase {
}
public void testModificationsAffectParsableArray() {
ParsableByteArray parsableByteArray = getTestDataArray();
// When modifying the wrapped byte array
byte[] data = parsableByteArray.data;
long readValue = parsableByteArray.readUnsignedInt();
data[0] = (byte) (ARRAY_ELEMENTS[0] + 1);
data[0] = (byte) (TEST_DATA[0] + 1);
parsableByteArray.setPosition(0);
// Then the parsed value changes.
assertFalse(parsableByteArray.readUnsignedInt() == readValue);
}
public void testReadingUnsignedLongWithMsbSetThrows() {
ParsableByteArray parsableByteArray = getTestDataArray();
// Given an array with the most-significant bit set on the top byte
byte[] data = parsableByteArray.data;
data[0] = (byte) 0x80;
// Then reading an unsigned long throws.
try {
parsableByteArray.readUnsignedLongToLong();
@ -102,21 +232,23 @@ public class ParsableByteArrayTest extends TestCase {
}
public void testReadUnsignedFixedPoint1616() {
ParsableByteArray parsableByteArray = getTestDataArray();
// When reading the integer part of a 16.16 fixed point value
int value = parsableByteArray.readUnsignedFixedPoint1616();
// Then the read value is equal to the array elements interpreted as a short.
assertEquals((0xFF & ARRAY_ELEMENTS[0]) << 8 | (ARRAY_ELEMENTS[1] & 0xFF), value);
assertEquals((0xFF & TEST_DATA[0]) << 8 | (TEST_DATA[1] & 0xFF), value);
assertEquals(4, parsableByteArray.getPosition());
}
public void testReadingBytesReturnsCopy() {
ParsableByteArray parsableByteArray = getTestDataArray();
// When reading all the bytes back
int length = parsableByteArray.limit();
assertEquals(ARRAY_ELEMENTS.length, length);
assertEquals(TEST_DATA.length, length);
byte[] copy = new byte[length];
parsableByteArray.readBytes(copy, 0, length);
// Then the array elements are the same.
assertTrue(Arrays.equals(parsableByteArray.data, copy));
}