Use Guava's LittleEndianDataInputStream.

This replaces the custom LittleEndianDataInputStream with
guava's implementation. To do this, I had to fix ExtDataInput
to better handle the case where skipBytes doesn't skip all the
bytes (the tests failed without this, and succeed with it). This
appears to be the main difference between the two implementations.
Guava's implementation is preferred because it is already a
dependency and because its license is clearer (the previous
implementation had a vague "public domain" comment in the thread
which may not be legally sufficient).

Fixes #1166
This commit is contained in:
Anthony Morris 2016-08-05 14:17:19 +01:00
parent 10d09f208c
commit e23eb9cf6b
4 changed files with 27 additions and 143 deletions

View File

@ -23,7 +23,7 @@ import brut.androlib.res.data.value.*;
import brut.util.Duo;
import brut.androlib.res.data.ResTable;
import brut.util.ExtDataInput;
import com.peterfranza.LittleEndianDataInputStream;
import com.google.common.io.LittleEndianDataInputStream;
import java.io.*;
import java.math.BigInteger;
import java.util.*;
@ -60,7 +60,10 @@ public class ARSCDecoder {
} else {
mFlagsOffsets = null;
}
mIn = new ExtDataInput(new LittleEndianDataInputStream(arscStream));
// We need to explicitly cast to DataInput as otherwise the constructor is ambiguous.
// We choose DataInput instead of InputStream as ExtDataInput wraps an InputStream in
// a DataInputStream which is big-endian and ignores the little-endian behavior.
mIn = new ExtDataInput((DataInput) new LittleEndianDataInputStream(arscStream));
mResTable = resTable;
mKeepBroken = keepBroken;
}

View File

@ -20,7 +20,8 @@ import android.util.TypedValue;
import brut.androlib.AndrolibException;
import brut.androlib.res.xml.ResXmlEncoders;
import brut.util.ExtDataInput;
import com.peterfranza.LittleEndianDataInputStream;
import com.google.common.io.LittleEndianDataInputStream;
import java.io.DataInput;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
@ -69,7 +70,10 @@ public class AXmlResourceParser implements XmlResourceParser {
public void open(InputStream stream) {
close();
if (stream != null) {
m_reader = new ExtDataInput(new LittleEndianDataInputStream(stream));
// We need to explicitly cast to DataInput as otherwise the constructor is ambiguous.
// We choose DataInput instead of InputStream as ExtDataInput wraps an InputStream in
// a DataInputStream which is big-endian and ignores the little-endian behavior.
m_reader = new ExtDataInput((DataInput) new LittleEndianDataInputStream(stream));
}
}

View File

@ -1,139 +0,0 @@
package com.peterfranza;
import java.io.DataInput;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
public class LittleEndianDataInputStream implements DataInput {
public LittleEndianDataInputStream(InputStream in) {
this.in = in;
this.d = new DataInputStream(in);
w = new byte[8];
}
public int available() throws IOException {
return d.available();
}
public final short readShort() throws IOException
{
d.readFully(w, 0, 2);
return (short)(
(w[1]&0xff) << 8 |
(w[0]&0xff));
}
/**
* Note, returns int even though it reads a short.
*/
public final int readUnsignedShort() throws IOException
{
d.readFully(w, 0, 2);
return (
(w[1]&0xff) << 8 |
(w[0]&0xff));
}
/**
* like DataInputStream.readChar except little endian.
*/
public final char readChar() throws IOException
{
d.readFully(w, 0, 2);
return (char) (
(w[1]&0xff) << 8 |
(w[0]&0xff));
}
/**
* like DataInputStream.readInt except little endian.
*/
public final int readInt() throws IOException
{
d.readFully(w, 0, 4);
return
(w[3]) << 24 |
(w[2]&0xff) << 16 |
(w[1]&0xff) << 8 |
(w[0]&0xff);
}
/**
* like DataInputStream.readLong except little endian.
*/
public final long readLong() throws IOException
{
d.readFully(w, 0, 8);
return
(long)(w[7]) << 56 |
(long)(w[6]&0xff) << 48 |
(long)(w[5]&0xff) << 40 |
(long)(w[4]&0xff) << 32 |
(long)(w[3]&0xff) << 24 |
(long)(w[2]&0xff) << 16 |
(long)(w[1]&0xff) << 8 |
(long)(w[0]&0xff);
}
public final float readFloat() throws IOException {
return Float.intBitsToFloat(readInt());
}
public final double readDouble() throws IOException {
return Double.longBitsToDouble(readLong());
}
public final int read(byte b[], int off, int len) throws IOException {
return in.read(b, off, len);
}
public final void readFully(byte b[]) throws IOException {
d.readFully(b, 0, b.length);
}
public final void readFully(byte b[], int off, int len) throws IOException {
d.readFully(b, off, len);
}
public final int skipBytes(int n) throws IOException {
return d.skipBytes(n);
}
public final boolean readBoolean() throws IOException {
return d.readBoolean();
}
public final byte readByte() throws IOException {
return d.readByte();
}
public int read() throws IOException {
return in.read();
}
public final int readUnsignedByte() throws IOException {
return d.readUnsignedByte();
}
@Deprecated
public final String readLine() throws IOException {
return d.readLine();
}
public final String readUTF() throws IOException {
return d.readUTF();
}
public final void close() throws IOException {
d.close();
}
private DataInputStream d; // to get at high level readFully methods of
// DataInputStream
private InputStream in; // to get at the low-level read methods of
// InputStream
private byte w[]; // work array for buffering input
}

View File

@ -76,6 +76,22 @@ public class ExtDataInput extends DataInputDelegate {
}
}
/**
* The general contract of DataInput doesn't guarantee all the bytes requested will be skipped
* and failure can occur for many reasons. We override this to try harder to skip all the bytes
* requested (this is similar to DataInputStream's wrapper).
*/
public final int skipBytes(int n) throws IOException {
int total = 0;
int cur = 0;
while ((total < n) && ((cur = (int) super.skipBytes(n - total)) > 0)) {
total += cur;
}
return total;
}
public String readNullEndedString(int length, boolean fixed)
throws IOException {
StringBuilder string = new StringBuilder(16);