aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLasse Collin <lasse.collin@tukaani.org>2011-07-26 10:59:43 +0300
committerLasse Collin <lasse.collin@tukaani.org>2011-07-26 10:59:43 +0300
commitec6b126bdc31cfe7e1d2aa043691f291df33d7c1 (patch)
tree96b9e426a1f7249cd12d300b08e2766543fcdfa6
parent987aed7731e49f3014344755a7c9a8937af56ee3 (diff)
downloadxz-java-ec6b126bdc31cfe7e1d2aa043691f291df33d7c1.tar.gz
Add BCJ filters.
-rw-r--r--src/org/tukaani/xz/ARMOptions.java37
-rw-r--r--src/org/tukaani/xz/ARMThumbOptions.java37
-rw-r--r--src/org/tukaani/xz/BCJCoder.java35
-rw-r--r--src/org/tukaani/xz/BCJDecoder.java62
-rw-r--r--src/org/tukaani/xz/BCJEncoder.java51
-rw-r--r--src/org/tukaani/xz/BCJOptions.java60
-rw-r--r--src/org/tukaani/xz/BlockInputStream.java3
-rw-r--r--src/org/tukaani/xz/IA64Options.java37
-rw-r--r--src/org/tukaani/xz/PowerPCOptions.java37
-rw-r--r--src/org/tukaani/xz/SPARCOptions.java37
-rw-r--r--src/org/tukaani/xz/SimpleInputStream.java104
-rw-r--r--src/org/tukaani/xz/SimpleOutputStream.java85
-rw-r--r--src/org/tukaani/xz/X86Options.java37
-rw-r--r--src/org/tukaani/xz/simple/ARM.java50
-rw-r--r--src/org/tukaani/xz/simple/ARMThumb.java53
-rw-r--r--src/org/tukaani/xz/simple/IA64.java81
-rw-r--r--src/org/tukaani/xz/simple/PowerPC.java50
-rw-r--r--src/org/tukaani/xz/simple/SPARC.java56
-rw-r--r--src/org/tukaani/xz/simple/SimpleFilter.java14
-rw-r--r--src/org/tukaani/xz/simple/X86.java98
20 files changed, 1024 insertions, 0 deletions
diff --git a/src/org/tukaani/xz/ARMOptions.java b/src/org/tukaani/xz/ARMOptions.java
new file mode 100644
index 0000000..e39b831
--- /dev/null
+++ b/src/org/tukaani/xz/ARMOptions.java
@@ -0,0 +1,37 @@
+/*
+ * ARMOptions
+ *
+ * Author: Lasse Collin <lasse.collin@tukaani.org>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+package org.tukaani.xz;
+
+import java.io.InputStream;
+import java.io.IOException;
+import org.tukaani.xz.simple.ARM;
+
+/**
+ * BCJ filter for little endian ARM instructions.
+ */
+public class ARMOptions extends BCJOptions {
+ private static final int ALIGNMENT = 4;
+
+ public ARMOptions() {
+ super(ALIGNMENT);
+ }
+
+ public FinishableOutputStream getOutputStream(FinishableOutputStream out) {
+ return new SimpleOutputStream(out, new ARM(true, startOffset));
+ }
+
+ public InputStream getInputStream(InputStream in) {
+ return new SimpleInputStream(in, new ARM(false, startOffset));
+ }
+
+ FilterEncoder getFilterEncoder() {
+ return new BCJEncoder(this, BCJCoder.ARM_FILTER_ID);
+ }
+}
diff --git a/src/org/tukaani/xz/ARMThumbOptions.java b/src/org/tukaani/xz/ARMThumbOptions.java
new file mode 100644
index 0000000..cddcc4e
--- /dev/null
+++ b/src/org/tukaani/xz/ARMThumbOptions.java
@@ -0,0 +1,37 @@
+/*
+ * ARMThumbOptions
+ *
+ * Author: Lasse Collin <lasse.collin@tukaani.org>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+package org.tukaani.xz;
+
+import java.io.InputStream;
+import java.io.IOException;
+import org.tukaani.xz.simple.ARMThumb;
+
+/**
+ * BCJ filter for little endian ARM-Thumb instructions.
+ */
+public class ARMThumbOptions extends BCJOptions {
+ private static final int ALIGNMENT = 2;
+
+ public ARMThumbOptions() {
+ super(ALIGNMENT);
+ }
+
+ public FinishableOutputStream getOutputStream(FinishableOutputStream out) {
+ return new SimpleOutputStream(out, new ARMThumb(true, startOffset));
+ }
+
+ public InputStream getInputStream(InputStream in) {
+ return new SimpleInputStream(in, new ARMThumb(false, startOffset));
+ }
+
+ FilterEncoder getFilterEncoder() {
+ return new BCJEncoder(this, BCJCoder.ARMTHUMB_FILTER_ID);
+ }
+}
diff --git a/src/org/tukaani/xz/BCJCoder.java b/src/org/tukaani/xz/BCJCoder.java
new file mode 100644
index 0000000..81862f7
--- /dev/null
+++ b/src/org/tukaani/xz/BCJCoder.java
@@ -0,0 +1,35 @@
+/*
+ * BCJCoder
+ *
+ * Author: Lasse Collin <lasse.collin@tukaani.org>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+package org.tukaani.xz;
+
+abstract class BCJCoder implements FilterCoder {
+ public static final long X86_FILTER_ID = 0x04;
+ public static final long POWERPC_FILTER_ID = 0x05;
+ public static final long IA64_FILTER_ID = 0x06;
+ public static final long ARM_FILTER_ID = 0x07;
+ public static final long ARMTHUMB_FILTER_ID = 0x08;
+ public static final long SPARC_FILTER_ID = 0x09;
+
+ public static boolean isBCJFilterID(long filterID) {
+ return filterID >= 0x04 && filterID <= 0x09;
+ }
+
+ public boolean changesSize() {
+ return false;
+ }
+
+ public boolean nonLastOK() {
+ return true;
+ }
+
+ public boolean lastOK() {
+ return false;
+ }
+}
diff --git a/src/org/tukaani/xz/BCJDecoder.java b/src/org/tukaani/xz/BCJDecoder.java
new file mode 100644
index 0000000..f8a6ae2
--- /dev/null
+++ b/src/org/tukaani/xz/BCJDecoder.java
@@ -0,0 +1,62 @@
+/*
+ * BCJDecoder
+ *
+ * Author: Lasse Collin <lasse.collin@tukaani.org>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+package org.tukaani.xz;
+
+import java.io.InputStream;
+import org.tukaani.xz.simple.*;
+
+class BCJDecoder extends BCJCoder implements FilterDecoder {
+ private final long filterID;
+ private final int startOffset;
+
+ BCJDecoder(long filterID, byte[] props)
+ throws UnsupportedOptionsException {
+ assert isBCJFilterID(filterID);
+ this.filterID = filterID;
+
+ if (props.length == 0) {
+ startOffset = 0;
+ } else if (props.length == 4) {
+ int n = 0;
+ for (int i = 0; i < 4; ++i)
+ n |= (props[i] & 0xFF) << (i * 8);
+
+ startOffset = n;
+ } else {
+ throw new UnsupportedOptionsException(
+ "Unsupported BCJ filter properties");
+ }
+ }
+
+ public int getMemoryUsage() {
+ return SimpleInputStream.getMemoryUsage();
+ }
+
+ public InputStream getInputStream(InputStream in) {
+ SimpleFilter simpleFilter = null;
+
+ if (filterID == X86_FILTER_ID)
+ simpleFilter = new X86(false, startOffset);
+ else if (filterID == POWERPC_FILTER_ID)
+ simpleFilter = new PowerPC(false, startOffset);
+ else if (filterID == IA64_FILTER_ID)
+ simpleFilter = new IA64(false, startOffset);
+ else if (filterID == ARM_FILTER_ID)
+ simpleFilter = new ARM(false, startOffset);
+ else if (filterID == ARMTHUMB_FILTER_ID)
+ simpleFilter = new ARMThumb(false, startOffset);
+ else if (filterID == SPARC_FILTER_ID)
+ simpleFilter = new SPARC(false, startOffset);
+ else
+ assert false;
+
+ return new SimpleInputStream(in, simpleFilter);
+ }
+}
diff --git a/src/org/tukaani/xz/BCJEncoder.java b/src/org/tukaani/xz/BCJEncoder.java
new file mode 100644
index 0000000..f2569d5
--- /dev/null
+++ b/src/org/tukaani/xz/BCJEncoder.java
@@ -0,0 +1,51 @@
+/*
+ * BCJEncoder
+ *
+ * Author: Lasse Collin <lasse.collin@tukaani.org>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+package org.tukaani.xz;
+
+import java.io.OutputStream;
+import org.tukaani.xz.simple.SimpleFilter;
+
+class BCJEncoder extends BCJCoder implements FilterEncoder {
+ private final BCJOptions options;
+ private final long filterID;
+ private final byte[] props;
+
+ BCJEncoder(BCJOptions options, long filterID) {
+ assert isBCJFilterID(filterID);
+ int startOffset = options.getStartOffset();
+
+ if (startOffset == 0) {
+ props = new byte[0];
+ } else {
+ props = new byte[4];
+ for (int i = 0; i < 4; ++i)
+ props[i] = (byte)(startOffset >>> (i * 8));
+ }
+
+ this.filterID = filterID;
+ this.options = (BCJOptions)options.clone();
+ }
+
+ public long getFilterID() {
+ return filterID;
+ }
+
+ public byte[] getFilterProps() {
+ return props;
+ }
+
+ public boolean supportsFlushing() {
+ return false;
+ }
+
+ public FinishableOutputStream getOutputStream(FinishableOutputStream out) {
+ return options.getOutputStream(out);
+ }
+}
diff --git a/src/org/tukaani/xz/BCJOptions.java b/src/org/tukaani/xz/BCJOptions.java
new file mode 100644
index 0000000..a862154
--- /dev/null
+++ b/src/org/tukaani/xz/BCJOptions.java
@@ -0,0 +1,60 @@
+/*
+ * BCJOptions
+ *
+ * Author: Lasse Collin <lasse.collin@tukaani.org>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+package org.tukaani.xz;
+
+import java.io.InputStream;
+import java.io.IOException;
+
+abstract class BCJOptions extends FilterOptions {
+ private final int alignment;
+ int startOffset = 0;
+
+ BCJOptions(int alignment) {
+ this.alignment = alignment;
+ }
+
+ /**
+ * Sets the start offset for the address conversions.
+ * Normally this is useless so you shouldn't use this function.
+ * The default value is <code>0</code>.
+ */
+ public void setStartOffset(int startOffset)
+ throws UnsupportedOptionsException {
+ if ((startOffset & (alignment - 1)) != 0)
+ throw new UnsupportedOptionsException(
+ "Start offset must be a multiple of " + alignment);
+
+ this.startOffset = startOffset;
+ }
+
+ /**
+ * Gets the start offset.
+ */
+ public int getStartOffset() {
+ return startOffset;
+ }
+
+ public int getEncoderMemoryUsage() {
+ return SimpleOutputStream.getMemoryUsage();
+ }
+
+ public int getDecoderMemoryUsage() {
+ return SimpleInputStream.getMemoryUsage();
+ }
+
+ public Object clone() {
+ try {
+ return super.clone();
+ } catch (CloneNotSupportedException e) {
+ assert false;
+ throw new RuntimeException();
+ }
+ }
+}
diff --git a/src/org/tukaani/xz/BlockInputStream.java b/src/org/tukaani/xz/BlockInputStream.java
index 9ccc02c..0d5897a 100644
--- a/src/org/tukaani/xz/BlockInputStream.java
+++ b/src/org/tukaani/xz/BlockInputStream.java
@@ -126,6 +126,9 @@ class BlockInputStream extends InputStream {
else if (filterIDs[i] == DeltaCoder.FILTER_ID)
filters[i] = new DeltaDecoder(filterProps[i]);
+ else if (BCJDecoder.isBCJFilterID(filterIDs[i]))
+ filters[i] = new BCJDecoder(filterIDs[i], filterProps[i]);
+
else
throw new UnsupportedOptionsException(
"Unknown Filter ID " + filterIDs[i]);
diff --git a/src/org/tukaani/xz/IA64Options.java b/src/org/tukaani/xz/IA64Options.java
new file mode 100644
index 0000000..517d2ea
--- /dev/null
+++ b/src/org/tukaani/xz/IA64Options.java
@@ -0,0 +1,37 @@
+/*
+ * IA64Options
+ *
+ * Author: Lasse Collin <lasse.collin@tukaani.org>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+package org.tukaani.xz;
+
+import java.io.InputStream;
+import java.io.IOException;
+import org.tukaani.xz.simple.IA64;
+
+/**
+ * BCJ filter for Itanium (IA-64) instructions.
+ */
+public class IA64Options extends BCJOptions {
+ private static final int ALIGNMENT = 16;
+
+ public IA64Options() {
+ super(ALIGNMENT);
+ }
+
+ public FinishableOutputStream getOutputStream(FinishableOutputStream out) {
+ return new SimpleOutputStream(out, new IA64(true, startOffset));
+ }
+
+ public InputStream getInputStream(InputStream in) {
+ return new SimpleInputStream(in, new IA64(false, startOffset));
+ }
+
+ FilterEncoder getFilterEncoder() {
+ return new BCJEncoder(this, BCJCoder.IA64_FILTER_ID);
+ }
+}
diff --git a/src/org/tukaani/xz/PowerPCOptions.java b/src/org/tukaani/xz/PowerPCOptions.java
new file mode 100644
index 0000000..432b2f0
--- /dev/null
+++ b/src/org/tukaani/xz/PowerPCOptions.java
@@ -0,0 +1,37 @@
+/*
+ * PowerPCOptions
+ *
+ * Author: Lasse Collin <lasse.collin@tukaani.org>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+package org.tukaani.xz;
+
+import java.io.InputStream;
+import java.io.IOException;
+import org.tukaani.xz.simple.PowerPC;
+
+/**
+ * BCJ filter for big endian PowerPC instructions.
+ */
+public class PowerPCOptions extends BCJOptions {
+ private static final int ALIGNMENT = 4;
+
+ public PowerPCOptions() {
+ super(ALIGNMENT);
+ }
+
+ public FinishableOutputStream getOutputStream(FinishableOutputStream out) {
+ return new SimpleOutputStream(out, new PowerPC(true, startOffset));
+ }
+
+ public InputStream getInputStream(InputStream in) {
+ return new SimpleInputStream(in, new PowerPC(false, startOffset));
+ }
+
+ FilterEncoder getFilterEncoder() {
+ return new BCJEncoder(this, BCJCoder.POWERPC_FILTER_ID);
+ }
+}
diff --git a/src/org/tukaani/xz/SPARCOptions.java b/src/org/tukaani/xz/SPARCOptions.java
new file mode 100644
index 0000000..b0ae109
--- /dev/null
+++ b/src/org/tukaani/xz/SPARCOptions.java
@@ -0,0 +1,37 @@
+/*
+ * SPARCOptions
+ *
+ * Author: Lasse Collin <lasse.collin@tukaani.org>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+package org.tukaani.xz;
+
+import java.io.InputStream;
+import java.io.IOException;
+import org.tukaani.xz.simple.SPARC;
+
+/**
+ * BCJ filter for SPARC.
+ */
+public class SPARCOptions extends BCJOptions {
+ private static final int ALIGNMENT = 4;
+
+ public SPARCOptions() {
+ super(ALIGNMENT);
+ }
+
+ public FinishableOutputStream getOutputStream(FinishableOutputStream out) {
+ return new SimpleOutputStream(out, new SPARC(true, startOffset));
+ }
+
+ public InputStream getInputStream(InputStream in) {
+ return new SimpleInputStream(in, new SPARC(false, startOffset));
+ }
+
+ FilterEncoder getFilterEncoder() {
+ return new BCJEncoder(this, BCJCoder.SPARC_FILTER_ID);
+ }
+}
diff --git a/src/org/tukaani/xz/SimpleInputStream.java b/src/org/tukaani/xz/SimpleInputStream.java
new file mode 100644
index 0000000..385b8e4
--- /dev/null
+++ b/src/org/tukaani/xz/SimpleInputStream.java
@@ -0,0 +1,104 @@
+/*
+ * SimpleInputStream
+ *
+ * Author: Lasse Collin <lasse.collin@tukaani.org>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+package org.tukaani.xz;
+
+import java.io.InputStream;
+import java.io.IOException;
+import org.tukaani.xz.simple.SimpleFilter;
+
+class SimpleInputStream extends InputStream {
+ private static final int TMPBUF_SIZE = 4096;
+
+ private final InputStream in;
+ private final SimpleFilter simpleFilter;
+
+ private final byte[] tmpbuf = new byte[TMPBUF_SIZE];
+ private int pos = 0;
+ private int filtered = 0;
+ private int unfiltered = 0;
+
+ private boolean endReached = false;
+ private IOException exception = null;
+
+ static int getMemoryUsage() {
+ return 1 + TMPBUF_SIZE / 1024;
+ }
+
+ SimpleInputStream(InputStream in, SimpleFilter simpleFilter) {
+ this.in = in;
+ this.simpleFilter = simpleFilter;
+ }
+
+ public int read() throws IOException {
+ byte[] buf = new byte[1];
+ return read(buf, 0, 1) == -1 ? -1 : (buf[0] & 0xFF);
+ }
+
+ public int read(byte[] buf, int off, int len) throws IOException {
+ if (off < 0 || len < 0 || off + len < 0 || off + len > buf.length)
+ throw new IllegalArgumentException();
+
+ if (len == 0)
+ return 0;
+
+ if (exception != null)
+ throw exception;
+
+ try {
+ int size = 0;
+
+ while (true) {
+ // Copy filtered data into the caller-provided buffer.
+ int copySize = Math.min(filtered, len);
+ System.arraycopy(tmpbuf, pos, buf, off, copySize);
+ pos += copySize;
+ filtered -= copySize;
+ off += copySize;
+ len -= copySize;
+ size += copySize;
+
+ // If end of tmpbuf was reached, move the pending data to
+ // the beginning of the buffer so that more data can be
+ // copied into tmpbuf on the next loop iteration.
+ if (pos + filtered + unfiltered == TMPBUF_SIZE) {
+ System.arraycopy(tmpbuf, pos, tmpbuf, 0,
+ filtered + unfiltered);
+ pos = 0;
+ }
+
+ if (len == 0 || endReached)
+ return size > 0 ? size : -1;
+
+ assert filtered == 0;
+
+ // Get more data into the temporary buffer.
+ int inSize = TMPBUF_SIZE - (pos + filtered + unfiltered);
+ inSize = in.read(tmpbuf, pos + filtered + unfiltered, inSize);
+
+ if (inSize == -1) {
+ // Mark the remaining unfiltered bytes to be ready
+ // to be copied out.
+ endReached = true;
+ filtered = unfiltered;
+ unfiltered = 0;
+ } else {
+ // Filter the data in tmpbuf.
+ unfiltered += inSize;
+ filtered = simpleFilter.code(tmpbuf, pos, unfiltered);
+ assert filtered <= unfiltered;
+ unfiltered -= filtered;
+ }
+ }
+ } catch (IOException e) {
+ exception = e;
+ throw e;
+ }
+ }
+}
diff --git a/src/org/tukaani/xz/SimpleOutputStream.java b/src/org/tukaani/xz/SimpleOutputStream.java
new file mode 100644
index 0000000..f14ffe7
--- /dev/null
+++ b/src/org/tukaani/xz/SimpleOutputStream.java
@@ -0,0 +1,85 @@
+/*
+ * SimpleOutputStream
+ *
+ * Author: Lasse Collin <lasse.collin@tukaani.org>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+package org.tukaani.xz;
+
+import java.io.IOException;
+import org.tukaani.xz.simple.SimpleFilter;
+
+class SimpleOutputStream extends FinishableOutputStream {
+ private static final int TMPBUF_SIZE = 4096;
+
+ private final FinishableOutputStream out;
+ private final SimpleFilter simpleFilter;
+
+ private final byte[] tmpbuf = new byte[TMPBUF_SIZE];
+ private int pos = 0;
+ private int unfiltered = 0;
+
+ static int getMemoryUsage() {
+ return 1 + TMPBUF_SIZE / 1024;
+ }
+
+ SimpleOutputStream(FinishableOutputStream out,
+ SimpleFilter simpleFilter) {
+ this.out = out;
+ this.simpleFilter = simpleFilter;
+ }
+
+ public void write(int b) throws IOException {
+ byte[] buf = new byte[1];
+ buf[0] = (byte)b;
+ write(buf, 0, 1);
+ }
+
+ public void write(byte[] buf, int off, int len) throws IOException {
+ if (off < 0 || len < 0 || off + len < 0 || off + len > buf.length)
+ throw new IllegalArgumentException();
+
+ while (len > 0) {
+ // Copy more unfiltered data into tmpbuf.
+ int copySize = Math.min(len, TMPBUF_SIZE - (pos + unfiltered));
+ System.arraycopy(buf, off, tmpbuf, pos + unfiltered, copySize);
+ off += copySize;
+ len -= copySize;
+ unfiltered += copySize;
+
+ // Filter the data in tmpbuf.
+ int filtered = simpleFilter.code(tmpbuf, pos, unfiltered);
+ assert filtered <= unfiltered;
+ unfiltered -= filtered;
+
+ // Write out the filtered data.
+ out.write(tmpbuf, pos, filtered);
+ pos += filtered;
+
+ // If end of tmpbuf was reached, move the pending unfiltered
+ // data to the beginning of the buffer so that more data can
+ // be copied into tmpbuf on the next loop iteration.
+ if (pos + unfiltered == TMPBUF_SIZE) {
+ System.arraycopy(tmpbuf, pos, tmpbuf, 0, unfiltered);
+ pos = 0;
+ }
+ }
+ }
+
+ public void flush() throws IOException {
+ throw new UnsupportedOptionsException("Flushing is not supported");
+ }
+
+ public void finish() throws IOException {
+ out.write(tmpbuf, pos, unfiltered);
+ out.finish();
+ }
+
+ public void close() throws IOException {
+ out.write(tmpbuf, pos, unfiltered);
+ out.close();
+ }
+}
diff --git a/src/org/tukaani/xz/X86Options.java b/src/org/tukaani/xz/X86Options.java
new file mode 100644
index 0000000..9b865dc
--- /dev/null
+++ b/src/org/tukaani/xz/X86Options.java
@@ -0,0 +1,37 @@
+/*
+ * X86Options
+ *
+ * Author: Lasse Collin <lasse.collin@tukaani.org>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+package org.tukaani.xz;
+
+import java.io.InputStream;
+import java.io.IOException;
+import org.tukaani.xz.simple.X86;
+
+/**
+ * BCJ filter for x86 (32-bit and 64-bit) instructions.
+ */
+public class X86Options extends BCJOptions {
+ private static final int ALIGNMENT = 1;
+
+ public X86Options() {
+ super(ALIGNMENT);
+ }
+
+ public FinishableOutputStream getOutputStream(FinishableOutputStream out) {
+ return new SimpleOutputStream(out, new X86(true, startOffset));
+ }
+
+ public InputStream getInputStream(InputStream in) {
+ return new SimpleInputStream(in, new X86(false, startOffset));
+ }
+
+ FilterEncoder getFilterEncoder() {
+ return new BCJEncoder(this, BCJCoder.X86_FILTER_ID);
+ }
+}
diff --git a/src/org/tukaani/xz/simple/ARM.java b/src/org/tukaani/xz/simple/ARM.java
new file mode 100644
index 0000000..6febf78
--- /dev/null
+++ b/src/org/tukaani/xz/simple/ARM.java
@@ -0,0 +1,50 @@
+/*
+ * BCJ filter for little endian ARM instructions
+ *
+ * Authors: Lasse Collin <lasse.collin@tukaani.org>
+ * Igor Pavlov <http://7-zip.org/>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+package org.tukaani.xz.simple;
+
+public final class ARM implements SimpleFilter {
+ private final boolean isEncoder;
+ private int pos;
+
+ public ARM(boolean isEncoder, int startPos) {
+ this.isEncoder = isEncoder;
+ pos = startPos + 8;
+ }
+
+ public int code(byte[] buf, int off, int len) {
+ int end = off + len - 4;
+ int i;
+
+ for (i = off; i <= end; i += 4) {
+ if ((buf[i + 3] & 0xFF) == 0xEB) {
+ int src = ((buf[i + 2] & 0xFF) << 16)
+ | ((buf[i + 1] & 0xFF) << 8)
+ | (buf[i] & 0xFF);
+ src <<= 2;
+
+ int dest;
+ if (isEncoder)
+ dest = src + (pos + i - off);
+ else
+ dest = src - (pos + i - off);
+
+ dest >>>= 2;
+ buf[i + 2] = (byte)(dest >>> 16);
+ buf[i + 1] = (byte)(dest >>> 8);
+ buf[i] = (byte)dest;
+ }
+ }
+
+ i -= off;
+ pos += i;
+ return i;
+ }
+}
diff --git a/src/org/tukaani/xz/simple/ARMThumb.java b/src/org/tukaani/xz/simple/ARMThumb.java
new file mode 100644
index 0000000..b8e7ca9
--- /dev/null
+++ b/src/org/tukaani/xz/simple/ARMThumb.java
@@ -0,0 +1,53 @@
+/*
+ * BCJ filter for little endian ARM-Thumb instructions
+ *
+ * Authors: Lasse Collin <lasse.collin@tukaani.org>
+ * Igor Pavlov <http://7-zip.org/>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+package org.tukaani.xz.simple;
+
+public final class ARMThumb implements SimpleFilter {
+ private final boolean isEncoder;
+ private int pos;
+
+ public ARMThumb(boolean isEncoder, int startPos) {
+ this.isEncoder = isEncoder;
+ pos = startPos + 4;
+ }
+
+ public int code(byte[] buf, int off, int len) {
+ int end = off + len - 4;
+ int i;
+
+ for (i = off; i <= end; i += 2) {
+ if ((buf[i + 1] & 0xF8) == 0xF0 && (buf[i + 3] & 0xF8) == 0xF8) {
+ int src = ((buf[i + 1] & 0x07) << 19)
+ | ((buf[i] & 0xFF) << 11)
+ | ((buf[i + 3] & 0x07) << 8)
+ | (buf[i + 2] & 0xFF);
+ src <<= 1;
+
+ int dest;
+ if (isEncoder)
+ dest = src + (pos + i - off);
+ else
+ dest = src - (pos + i - off);
+
+ dest >>>= 1;
+ buf[i + 1] = (byte)(0xF0 | ((dest >>> 19) & 0x07));
+ buf[i] = (byte)(dest >>> 11);
+ buf[i + 3] = (byte)(0xF8 | ((dest >>> 8) & 0x07));
+ buf[i + 2] = (byte)dest;
+ i += 2;
+ }
+ }
+
+ i -= off;
+ pos += i;
+ return i;
+ }
+}
diff --git a/src/org/tukaani/xz/simple/IA64.java b/src/org/tukaani/xz/simple/IA64.java
new file mode 100644
index 0000000..776a1b7
--- /dev/null
+++ b/src/org/tukaani/xz/simple/IA64.java
@@ -0,0 +1,81 @@
+/*
+ * BCJ filter for Itanium (IA-64) instructions
+ *
+ * Authors: Lasse Collin <lasse.collin@tukaani.org>
+ * Igor Pavlov <http://7-zip.org/>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+package org.tukaani.xz.simple;
+
+public final class IA64 implements SimpleFilter {
+ private static final int[] BRANCH_TABLE = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 4, 4, 6, 6, 0, 0, 7, 7,
+ 4, 4, 0, 0, 4, 4, 0, 0 };
+
+ private final boolean isEncoder;
+ private int pos;
+
+ public IA64(boolean isEncoder, int startPos) {
+ this.isEncoder = isEncoder;
+ pos = startPos;
+ }
+
+ public int code(byte[] buf, int off, int len) {
+ int end = off + len - 16;
+ int i;
+
+ for (i = off; i <= end; i += 16) {
+ int instrTemplate = buf[i] & 0x1F;
+ int mask = BRANCH_TABLE[instrTemplate];
+
+ for (int slot = 0, bitPos = 5; slot < 3; ++slot, bitPos += 41) {
+ if (((mask >>> slot) & 1) == 0)
+ continue;
+
+ int bytePos = bitPos >>> 3;
+ int bitRes = bitPos & 7;
+
+ long instr = 0;
+ for (int j = 0; j < 6; ++j)
+ instr |= (buf[i + bytePos + j] & 0xFFL) << (8 * j);
+
+ long instrNorm = instr >>> bitRes;
+
+ if (((instrNorm >>> 37) & 0x0F) != 0x05
+ || ((instrNorm >>> 9) & 0x07) != 0x00)
+ continue;
+
+ int src = (int)((instrNorm >>> 13) & 0x0FFFFF);
+ src |= ((int)(instrNorm >>> 36) & 1) << 20;
+ src <<= 4;
+
+ int dest;
+ if (isEncoder)
+ dest = src + (pos + i - off);
+ else
+ dest = src - (pos + i - off);
+
+ dest >>>= 4;
+
+ instrNorm &= ~(0x8FFFFFL << 13);
+ instrNorm |= (dest & 0x0FFFFFL) << 13;
+ instrNorm |= (dest & 0x100000L) << (36 - 20);
+
+ instr &= (1 << bitRes) - 1;
+ instr |= instrNorm << bitRes;
+
+ for (int j = 0; j < 6; ++j)
+ buf[i + bytePos + j] = (byte)(instr >>> (8 * j));
+ }
+ }
+
+ i -= off;
+ pos += i;
+ return i;
+ }
+}
diff --git a/src/org/tukaani/xz/simple/PowerPC.java b/src/org/tukaani/xz/simple/PowerPC.java
new file mode 100644
index 0000000..b7400ab
--- /dev/null
+++ b/src/org/tukaani/xz/simple/PowerPC.java
@@ -0,0 +1,50 @@
+/*
+ * BCJ filter for big endian PowerPC instructions
+ *
+ * Authors: Lasse Collin <lasse.collin@tukaani.org>
+ * Igor Pavlov <http://7-zip.org/>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+package org.tukaani.xz.simple;
+
+public final class PowerPC implements SimpleFilter {
+ private final boolean isEncoder;
+ private int pos;
+
+ public PowerPC(boolean isEncoder, int startPos) {
+ this.isEncoder = isEncoder;
+ pos = startPos;
+ }
+
+ public int code(byte[] buf, int off, int len) {
+ int end = off + len - 4;
+ int i;
+
+ for (i = off; i <= end; i += 4) {
+ if ((buf[i] & 0xFC) == 0x48 && (buf[i + 3] & 0x03) == 0x01) {
+ int src = ((buf[i] & 0x03) << 24)
+ | ((buf[i + 1] & 0xFF) << 16)
+ | ((buf[i + 2] & 0xFF) << 8)
+ | (buf[i + 3] & 0xFC);
+
+ int dest;
+ if (isEncoder)
+ dest = src + (pos + i - off);
+ else
+ dest = src - (pos + i - off);
+
+ buf[i] = (byte)(0x48 | ((dest >>> 24) & 0x03));
+ buf[i + 1] = (byte)(dest >>> 16);
+ buf[i + 2] = (byte)(dest >>> 8);
+ buf[i + 3] = (byte)((buf[i + 3] & 0x03) | dest);
+ }
+ }
+
+ i -= off;
+ pos += i;
+ return i;
+ }
+}
diff --git a/src/org/tukaani/xz/simple/SPARC.java b/src/org/tukaani/xz/simple/SPARC.java
new file mode 100644
index 0000000..913c8ac
--- /dev/null
+++ b/src/org/tukaani/xz/simple/SPARC.java
@@ -0,0 +1,56 @@
+/*
+ * BCJ filter for SPARC instructions
+ *
+ * Authors: Lasse Collin <lasse.collin@tukaani.org>
+ * Igor Pavlov <http://7-zip.org/>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+package org.tukaani.xz.simple;
+
+public final class SPARC implements SimpleFilter {
+ private final boolean isEncoder;
+ private int pos;
+
+ public SPARC(boolean isEncoder, int startPos) {
+ this.isEncoder = isEncoder;
+ pos = startPos;
+ }
+
+ public int code(byte[] buf, int off, int len) {
+ int end = off + len - 4;
+ int i;
+
+ for (i = off; i <= end; i += 4) {
+ if ((buf[i] == 0x40 && (buf[i + 1] & 0xC0) == 0x00)
+ || (buf[i] == 0x7F && (buf[i + 1] & 0xC0) == 0xC0)) {
+ int src = ((buf[i] & 0xFF) << 24)
+ | ((buf[i + 1] & 0xFF) << 16)
+ | ((buf[i + 2] & 0xFF) << 8)
+ | (buf[i + 3] & 0xFF);
+ src <<= 2;
+
+ int dest;
+ if (isEncoder)
+ dest = src + (pos + i - off);
+ else
+ dest = src - (pos + i - off);
+
+ dest >>>= 2;
+ dest = (((0 - ((dest >>> 22) & 1)) << 22) & 0x3FFFFFFF)
+ | (dest & 0x3FFFFF) | 0x40000000;
+
+ buf[i] = (byte)(dest >>> 24);
+ buf[i + 1] = (byte)(dest >>> 16);
+ buf[i + 2] = (byte)(dest >>> 8);
+ buf[i + 3] = (byte)dest;
+ }
+ }
+
+ i -= off;
+ pos += i;
+ return i;
+ }
+}
diff --git a/src/org/tukaani/xz/simple/SimpleFilter.java b/src/org/tukaani/xz/simple/SimpleFilter.java
new file mode 100644
index 0000000..6f72906
--- /dev/null
+++ b/src/org/tukaani/xz/simple/SimpleFilter.java
@@ -0,0 +1,14 @@
+/*
+ * BCJ filter for little endian ARM instructions
+ *
+ * Author: Lasse Collin <lasse.collin@tukaani.org>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+package org.tukaani.xz.simple;
+
+public interface SimpleFilter {
+ int code(byte[] buf, int off, int len);
+}
diff --git a/src/org/tukaani/xz/simple/X86.java b/src/org/tukaani/xz/simple/X86.java
new file mode 100644
index 0000000..a05e08b
--- /dev/null
+++ b/src/org/tukaani/xz/simple/X86.java
@@ -0,0 +1,98 @@
+/*
+ * BCJ filter for x86 instructions
+ *
+ * Authors: Lasse Collin <lasse.collin@tukaani.org>
+ * Igor Pavlov <http://7-zip.org/>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+package org.tukaani.xz.simple;
+
+public final class X86 implements SimpleFilter {
+ private static final boolean[] MASK_TO_ALLOWED_STATUS
+ = {true, true, true, false, true, false, false, false};
+
+ private static final int[] MASK_TO_BIT_NUMBER = {0, 1, 2, 2, 3, 3, 3, 3};
+
+ private final boolean isEncoder;
+ private int pos;
+ private int prevMask = 0;
+
+ private static boolean test86MSByte(byte b) {
+ int i = b & 0xFF;
+ return i == 0x00 || i == 0xFF;
+ }
+
+ public X86(boolean isEncoder, int startPos) {
+ this.isEncoder = isEncoder;
+ pos = startPos + 5;
+ }
+
+ public int code(byte[] buf, int off, int len) {
+ int prevPos = off - 1;
+ int end = off + len - 5;
+ int i;
+
+ for (i = off; i <= end; ++i) {
+ if ((buf[i] & 0xFE) != 0xE8)
+ continue;
+
+ prevPos = i - prevPos;
+ if ((prevPos & ~3) != 0) { // (unsigned)prevPos > 3
+ prevMask = 0;
+ } else {
+ prevMask = (prevMask << (prevPos - 1)) & 7;
+ if (prevMask != 0) {
+ if (!MASK_TO_ALLOWED_STATUS[prevMask] || test86MSByte(
+ buf[i + 4 - MASK_TO_BIT_NUMBER[prevMask]])) {
+ prevPos = i;
+ prevMask = (prevMask << 1) | 1;
+ continue;
+ }
+ }
+ }
+
+ prevPos = i;
+
+ if (test86MSByte(buf[i + 4])) {
+ int src = (buf[i + 1] & 0xFF)
+ | ((buf[i + 2] & 0xFF) << 8)
+ | ((buf[i + 3] & 0xFF) << 16)
+ | ((buf[i + 4] & 0xFF) << 24);
+ int dest;
+ while (true) {
+ if (isEncoder)
+ dest = src + (pos + i - off);
+ else
+ dest = src - (pos + i - off);
+
+ if (prevMask == 0)
+ break;
+
+ int index = MASK_TO_BIT_NUMBER[prevMask] * 8;
+ if (!test86MSByte((byte)(dest >>> (24 - index))))
+ break;
+
+ src = dest ^ ((1 << (32 - index)) - 1);
+ }
+
+ buf[i + 1] = (byte)dest;
+ buf[i + 2] = (byte)(dest >>> 8);
+ buf[i + 3] = (byte)(dest >>> 16);
+ buf[i + 4] = (byte)(~(((dest >>> 24) & 1) - 1));
+ i += 4;
+ } else {
+ prevMask = (prevMask << 1) | 1;
+ }
+ }
+
+ prevPos = i - prevPos;
+ prevMask = ((prevPos & ~3) != 0) ? 0 : prevMask << (prevPos - 1);
+
+ i -= off;
+ pos += i;
+ return i;
+ }
+}