package jadx.core.xmlgen; import java.io.IOException; import java.io.InputStream; import java.nio.charset.Charset; import org.jetbrains.annotations.NotNull; public class ParserStream { protected static final Charset STRING_CHARSET_UTF16 = Charset.forName("UTF-16LE"); protected static final Charset STRING_CHARSET_UTF8 = Charset.forName("UTF-8"); private static final int[] EMPTY_INT_ARRAY = new int[0]; private static final byte[] EMPTY_BYTE_ARRAY = new byte[0]; private final InputStream input; private long readPos = 0; public ParserStream(@NotNull InputStream inputStream) { this.input = inputStream; } public long getPos() { return readPos; } public int readInt8() throws IOException { readPos++; return input.read(); } public int readInt16() throws IOException { readPos += 2; int b1 = input.read(); int b2 = input.read(); return (b2 & 0xFF) << 8 | b1 & 0xFF; } public int readInt32() throws IOException { readPos += 4; InputStream in = input; int b1 = in.read(); int b2 = in.read(); int b3 = in.read(); int b4 = in.read(); return b4 << 24 | (b3 & 0xFF) << 16 | (b2 & 0xFF) << 8 | b1 & 0xFF; } public long readUInt32() throws IOException { return readInt32() & 0xFFFFFFFFL; } public String readString16Fixed(int len) throws IOException { String str = new String(readInt8Array(len * 2), STRING_CHARSET_UTF16); return str.trim(); } public int[] readInt32Array(int count) throws IOException { if (count == 0) { return EMPTY_INT_ARRAY; } int[] arr = new int[count]; for (int i = 0; i < count; i++) { arr[i] = readInt32(); } return arr; } public byte[] readInt8Array(int count) throws IOException { if (count == 0) { return EMPTY_BYTE_ARRAY; } readPos += count; byte[] arr = new byte[count]; int pos = input.read(arr, 0, count); while (pos < count) { int read = input.read(arr, pos, count - pos); if (read == -1) { throw new IOException("No data, can't read " + count + " bytes"); } pos += read; } return arr; } public void skip(long count) throws IOException { readPos += count; long pos = input.skip(count); while (pos < count) { long skipped = input.skip(count - pos); if (skipped == -1) { throw new IOException("No data, can't skip " + count + " bytes"); } pos += skipped; } } public void checkInt8(int expected, String error) throws IOException { int v = readInt8(); if (v != expected) { throwException(error, expected, v); } } public void checkInt16(int expected, String error) throws IOException { int v = readInt16(); if (v != expected) { throwException(error, expected, v); } } private void throwException(String error, int expected, int actual) throws IOException { throw new IOException(error + ", expected: 0x" + Integer.toHexString(expected) + ", actual: 0x" + Integer.toHexString(actual) + ", offset: 0x" + Long.toHexString(getPos())); } public void checkPos(long expectedOffset, String error) throws IOException { if (getPos() != expectedOffset) { throw new IOException(error + ", expected offset: 0x" + Long.toHexString(expectedOffset) + ", actual: 0x" + Long.toHexString(getPos())); } } public void skipToPos(long expectedOffset, String error) throws IOException { long pos = getPos(); if (pos < expectedOffset) { skip(expectedOffset - pos); } checkPos(expectedOffset, error); } public void mark(int len) throws IOException { if (!input.markSupported()) { throw new IOException("Mark not supported for input stream " + input.getClass()); } input.mark(len); } public void reset() throws IOException { input.reset(); } @Override public String toString() { return "pos: 0x" + Long.toHexString(readPos); } }