Skip to content

Commit

Permalink
Make RandomAccessReader.isMotorolaByteOrder read only.
Browse files Browse the repository at this point in the history
If the byte order changes during extraction, a new reader is made via WithByteOrder.
That new reader is naturally scoped to the sub-reading operation, and the byte order
does not need to be reverted.
  • Loading branch information
don-vip committed Jul 29, 2024
1 parent 499ea8a commit f1e1ea5
Show file tree
Hide file tree
Showing 14 changed files with 134 additions and 110 deletions.
16 changes: 6 additions & 10 deletions Source/com/drew/imaging/tiff/TiffReader.java
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public class TiffReader
* ignored or recovered from
* @throws IOException an error occurred while accessing the required data
*/
public void processTiff(@NotNull final RandomAccessReader reader,
public void processTiff(@NotNull RandomAccessReader reader,
@NotNull final TiffHandler handler) throws TiffProcessingException, IOException
{
// Standard TIFF
Expand All @@ -67,9 +67,9 @@ public void processTiff(@NotNull final RandomAccessReader reader,
short byteOrderIdentifier = reader.getInt16(0);

if (byteOrderIdentifier == 0x4d4d) { // "MM"
reader.setMotorolaByteOrder(true);
reader = reader.withByteOrder(true);
} else if (byteOrderIdentifier == 0x4949) { // "II"
reader.setMotorolaByteOrder(false);
reader = reader.withByteOrder(false);
} else {
throw new TiffProcessingException("Unclear distinction between Motorola/Intel byte ordering: " + byteOrderIdentifier);
}
Expand Down Expand Up @@ -134,7 +134,7 @@ public void processTiff(@NotNull final RandomAccessReader reader,
* @throws IOException an error occurred while accessing the required data
*/
public static void processIfd(@NotNull final TiffHandler handler,
@NotNull final RandomAccessReader reader,
@NotNull RandomAccessReader reader,
@NotNull final Set<Integer> processedIfdOffsets,
final int ifdOffset,
final boolean isBigTiff) throws IOException
Expand All @@ -161,15 +161,14 @@ public static void processIfd(@NotNull final TiffHandler handler,
// - 8 bytes: component count
// - 8 bytes: inline value, or offset pointer if too large to fit in eight bytes

Boolean resetByteOrder = null;
try {
// Check for directories we've already visited to avoid stack overflows when recursive/cyclic directory structures exist.
// Note that we track these offsets in the global frame, not the reader's local frame.
int globalIfdOffset = reader.toUnshiftedOffset(ifdOffset);

// remember that we've visited this directory so that we don't visit it again later
if (!processedIfdOffsets.add(globalIfdOffset)) {
return;
return;
}

// Validate IFD offset
Expand All @@ -188,9 +187,8 @@ public static void processIfd(@NotNull final TiffHandler handler,
// Here we detect switched bytes that suggest this problem, and temporarily swap the byte order.
// This was discussed in GitHub issue #136.
if (!isBigTiff && dirTagCount > 0xFF && (dirTagCount & 0xFF) == 0) {
resetByteOrder = reader.isMotorolaByteOrder();
dirTagCount >>= 8;
reader.setMotorolaByteOrder(!reader.isMotorolaByteOrder());
reader = reader.withByteOrder(!reader.isMotorolaByteOrder());
}

int dirLength = isBigTiff
Expand Down Expand Up @@ -312,8 +310,6 @@ public static void processIfd(@NotNull final TiffHandler handler,
}
} finally {
handler.endingIFD();
if (resetByteOrder != null)
reader.setMotorolaByteOrder(resetByteOrder);
}
}

Expand Down
23 changes: 19 additions & 4 deletions Source/com/drew/lang/ByteArrayReader.java
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,14 @@ public ByteArrayReader(@NotNull byte[] buffer)
@com.drew.lang.annotations.SuppressWarnings(value = "EI_EXPOSE_REP2", justification = "Design intent")
public ByteArrayReader(@NotNull byte[] buffer, int baseOffset)
{
this(buffer, baseOffset, true);
}

@SuppressWarnings({ "ConstantConditions" })
@com.drew.lang.annotations.SuppressWarnings(value = "EI_EXPOSE_REP2", justification = "Design intent")
public ByteArrayReader(@NotNull byte[] buffer, int baseOffset, boolean isMotorolaByteOrder)
{
super(isMotorolaByteOrder);
if (buffer == null)
throw new NullPointerException();
if (baseOffset < 0)
Expand All @@ -61,13 +69,20 @@ public ByteArrayReader(@NotNull byte[] buffer, int baseOffset)
}

@Override
public RandomAccessReader withShiftedBaseOffset(int shift) throws IOException {
public ByteArrayReader withByteOrder(boolean isMotorolaByteOrder) {
if (isMotorolaByteOrder == isMotorolaByteOrder()) {
return this;
} else {
return new ByteArrayReader(_buffer, _baseOffset, isMotorolaByteOrder);
}
}

@Override
public ByteArrayReader withShiftedBaseOffset(int shift) throws IOException {
if (shift == 0) {
return this;
} else {
RandomAccessReader reader = new ByteArrayReader(_buffer, _baseOffset + shift);
reader.setMotorolaByteOrder(isMotorolaByteOrder());
return reader;
return new ByteArrayReader(_buffer, _baseOffset + shift, isMotorolaByteOrder());
}
}

Expand Down
23 changes: 19 additions & 4 deletions Source/com/drew/lang/RandomAccessFileReader.java
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,14 @@ public RandomAccessFileReader(@NotNull RandomAccessFile file) throws IOException
@com.drew.lang.annotations.SuppressWarnings(value = "EI_EXPOSE_REP2", justification = "Design intent")
public RandomAccessFileReader(@NotNull RandomAccessFile file, int baseOffset) throws IOException
{
this(file, baseOffset, true);
}

@SuppressWarnings({ "ConstantConditions" })
@com.drew.lang.annotations.SuppressWarnings(value = "EI_EXPOSE_REP2", justification = "Design intent")
public RandomAccessFileReader(@NotNull RandomAccessFile file, int baseOffset, boolean isMotorolaByteOrder) throws IOException
{
super(isMotorolaByteOrder);
if (file == null)
throw new NullPointerException();

Expand All @@ -61,13 +69,20 @@ public RandomAccessFileReader(@NotNull RandomAccessFile file, int baseOffset) th
}

@Override
public RandomAccessReader withShiftedBaseOffset(int shift) throws IOException {
public RandomAccessFileReader withByteOrder(boolean isMotorolaByteOrder) throws IOException {
if (isMotorolaByteOrder == isMotorolaByteOrder()) {
return this;
} else {
return new RandomAccessFileReader(_file, _baseOffset, isMotorolaByteOrder);
}
}

@Override
public RandomAccessFileReader withShiftedBaseOffset(int shift) throws IOException {
if (shift == 0) {
return this;
} else {
RandomAccessReader reader = new RandomAccessFileReader(_file, _baseOffset + shift);
reader.setMotorolaByteOrder(isMotorolaByteOrder());
return reader;
return new RandomAccessFileReader(_file, _baseOffset + shift, isMotorolaByteOrder());
}
}

Expand Down
23 changes: 8 additions & 15 deletions Source/com/drew/lang/RandomAccessReader.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,14 @@
*/
public abstract class RandomAccessReader
{
private boolean _isMotorolaByteOrder = true;
private final boolean _isMotorolaByteOrder;

protected RandomAccessReader(boolean isMotorolaByteOrder)
{
_isMotorolaByteOrder = isMotorolaByteOrder;
}

public abstract RandomAccessReader withByteOrder(boolean isMotorolaByteOrder) throws IOException;

public abstract RandomAccessReader withShiftedBaseOffset(int shift) throws IOException;

Expand Down Expand Up @@ -106,20 +113,6 @@ public abstract class RandomAccessReader
*/
public abstract long getLength() throws IOException;

/**
* Sets the endianness of this reader.
* <ul>
* <li><code>true</code> for Motorola (or big) endianness (also known as network byte order), with MSB before LSB.</li>
* <li><code>false</code> for Intel (or little) endianness, with LSB before MSB.</li>
* </ul>
*
* @param motorolaByteOrder <code>true</code> for Motorola/big endian, <code>false</code> for Intel/little endian
*/
public void setMotorolaByteOrder(boolean motorolaByteOrder)
{
_isMotorolaByteOrder = motorolaByteOrder;
}

/**
* Gets the endianness of this reader.
* <ul>
Expand Down
37 changes: 29 additions & 8 deletions Source/com/drew/lang/RandomAccessStreamReader.java
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,12 @@ public RandomAccessStreamReader(@NotNull InputStream stream, int chunkLength)

public RandomAccessStreamReader(@NotNull InputStream stream, int chunkLength, long streamLength)
{
this(stream, chunkLength, streamLength, true);
}

public RandomAccessStreamReader(@NotNull InputStream stream, int chunkLength, long streamLength, boolean isMotorolaByteOrder)
{
super(isMotorolaByteOrder);
if (stream == null)
throw new NullPointerException();
if (chunkLength <= 0)
Expand Down Expand Up @@ -213,14 +219,21 @@ public byte[] getBytes(int index, int count) throws IOException
return bytes;
}

@Override
public RandomAccessReader withByteOrder(boolean isMotorolaByteOrder) {
if (isMotorolaByteOrder == isMotorolaByteOrder()) {
return this;
} else {
return new ShiftedRandomAccessStreamReader(this, 0, isMotorolaByteOrder);
}
}

@Override
public RandomAccessReader withShiftedBaseOffset(int shift) {
if (shift == 0) {
return this;
} else {
RandomAccessReader reader = new ShiftedRandomAccessStreamReader(this, shift);
reader.setMotorolaByteOrder(isMotorolaByteOrder());
return reader;
return new ShiftedRandomAccessStreamReader(this, shift, isMotorolaByteOrder());
}
}

Expand All @@ -229,8 +242,9 @@ private static class ShiftedRandomAccessStreamReader extends RandomAccessReader
private final RandomAccessStreamReader _baseReader;
private final int _baseOffset;

public ShiftedRandomAccessStreamReader(RandomAccessStreamReader baseReader, int baseOffset)
public ShiftedRandomAccessStreamReader(RandomAccessStreamReader baseReader, int baseOffset, boolean isMotorolaByteOrder)
{
super(isMotorolaByteOrder);
if (baseOffset < 0)
throw new IllegalArgumentException("Must be zero or greater.");

Expand All @@ -239,13 +253,20 @@ public ShiftedRandomAccessStreamReader(RandomAccessStreamReader baseReader, int
}

@Override
public RandomAccessReader withShiftedBaseOffset(int shift) {
public ShiftedRandomAccessStreamReader withByteOrder(boolean isMotorolaByteOrder) throws IOException {
if (isMotorolaByteOrder == isMotorolaByteOrder()) {
return this;
} else {
return new ShiftedRandomAccessStreamReader(_baseReader, _baseOffset, isMotorolaByteOrder);
}
}

@Override
public ShiftedRandomAccessStreamReader withShiftedBaseOffset(int shift) {
if (shift == 0) {
return this;
} else {
RandomAccessReader reader = new ShiftedRandomAccessStreamReader(_baseReader, _baseOffset + shift);
reader.setMotorolaByteOrder(isMotorolaByteOrder());
return reader;
return new ShiftedRandomAccessStreamReader(_baseReader, _baseOffset + shift, isMotorolaByteOrder());
}
}

Expand Down
12 changes: 7 additions & 5 deletions Source/com/drew/metadata/avi/AviRiffHandler.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 Drew Noakes and contributors
* Copyright 2002-2022 Drew Noakes and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -54,31 +54,34 @@ public AviRiffHandler(@NotNull Metadata metadata)
metadata.addDirectory(_directory);
}

@Override
public boolean shouldAcceptRiffIdentifier(@NotNull String identifier)
{
return identifier.equals(AviDirectory.FORMAT);
}

@Override
public boolean shouldAcceptChunk(@NotNull String fourCC)
{
return fourCC.equals(AviDirectory.CHUNK_STREAM_HEADER)
|| fourCC.equals(AviDirectory.CHUNK_MAIN_HEADER)
|| fourCC.equals(AviDirectory.CHUNK_DATETIME_ORIGINAL);
}

@Override
public boolean shouldAcceptList(@NotNull String fourCC)
{
return fourCC.equals(AviDirectory.LIST_HEADER)
|| fourCC.equals(AviDirectory.LIST_STREAM_HEADER)
|| fourCC.equals(AviDirectory.FORMAT);
}

@Override
public void processChunk(@NotNull String fourCC, @NotNull byte[] payload)
{
try {
if (fourCC.equals(AviDirectory.CHUNK_STREAM_HEADER)) {
ByteArrayReader reader = new ByteArrayReader(payload);
reader.setMotorolaByteOrder(false);
ByteArrayReader reader = new ByteArrayReader(payload, 0, false);

String fccType = new String(reader.getBytes(0, 4));
String fccHandler = new String(reader.getBytes(4, 4));
Expand Down Expand Up @@ -114,8 +117,7 @@ public void processChunk(@NotNull String fourCC, @NotNull byte[] payload)
}
}
} else if (fourCC.equals(AviDirectory.CHUNK_MAIN_HEADER)) {
ByteArrayReader reader = new ByteArrayReader(payload);
reader.setMotorolaByteOrder(false);
ByteArrayReader reader = new ByteArrayReader(payload, 0, false);

// int dwMicroSecPerFrame = reader.getInt32(0);
// int dwMaxBytesPerSec = reader.getInt32(4);
Expand Down
4 changes: 2 additions & 2 deletions Source/com/drew/metadata/eps/EpsReader.java
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public class EpsReader
*/
public void extract(@NotNull final InputStream inputStream, @NotNull final Metadata metadata) throws IOException
{
RandomAccessStreamReader reader = new RandomAccessStreamReader(inputStream);
RandomAccessReader reader = new RandomAccessStreamReader(inputStream);
EpsDirectory directory = new EpsDirectory();
metadata.addDirectory(directory);

Expand All @@ -64,7 +64,7 @@ public void extract(@NotNull final InputStream inputStream, @NotNull final Metad
*/
switch (reader.getInt32(0)) {
case 0xC5D0D3C6:
reader.setMotorolaByteOrder(false);
reader = reader.withByteOrder(false);
int postScriptOffset = reader.getInt32(4);
int postScriptLength = reader.getInt32(8);
int wmfOffset = reader.getInt32(12);
Expand Down
4 changes: 2 additions & 2 deletions Source/com/drew/metadata/exif/ExifDescriptorBase.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 Drew Noakes and contributors
* Copyright 2002-2022 Drew Noakes and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -1158,7 +1158,7 @@ private int[] decodeCfaPattern(int tagType)
if (end > values.length) // sanity check in case of byte order problems; calculated 'end' should be <= length of the values
{
// try swapping byte order (I have seen this order different than in EXIF)
reader.setMotorolaByteOrder(!reader.isMotorolaByteOrder());
reader = reader.withByteOrder(!reader.isMotorolaByteOrder());
item0 = reader.getInt16(0);
item1 = reader.getInt16(2);

Expand Down
Loading

0 comments on commit f1e1ea5

Please sign in to comment.