Skip to content
22 changes: 6 additions & 16 deletions java/src/PpmCompress.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;


/**
Expand Down Expand Up @@ -53,7 +52,7 @@ static void compress(InputStream in, BitOutputStream out) throws IOException {
// its frequency is 1 in the order -1 context but its frequency
// is 0 in all other contexts (which have non-negative order).
ArithmeticEncoder enc = new ArithmeticEncoder(32, out);
PpmModel model = new PpmModel(MODEL_ORDER, 257, 256);
PpmModel model = new PpmModel(MODEL_ORDER, 256);
int[] history = new int[0];

while (true) {
Expand All @@ -63,17 +62,10 @@ static void compress(InputStream in, BitOutputStream out) throws IOException {
break;
encodeSymbol(model, history, symbol, enc);
model.incrementContexts(history, symbol);

if (model.modelOrder >= 1) {
// Prepend current symbol, dropping oldest symbol if necessary
if (history.length < model.modelOrder)
history = Arrays.copyOf(history, history.length + 1);
System.arraycopy(history, 0, history, 1, history.length - 1);
history[0] = symbol;
}
history = model.addToHistory(history, symbol);
}

encodeSymbol(model, history, 256, enc); // EOF
encodeSymbol(model, history, model.escapeSymbol, enc); // EOF
enc.finish(); // Flush remaining code bits
}

Expand All @@ -87,18 +79,16 @@ private static void encodeSymbol(PpmModel model, int[] history, int symbol, Arit
for (int order = history.length; order >= 0; order--) {
PpmModel.Context ctx = model.rootContext;
for (int i = 0; i < order; i++) {
if (ctx.subcontexts == null)
throw new AssertionError();
ctx = ctx.subcontexts[history[i]];
ctx = ctx.getSubcontexts()[history[i]];
if (ctx == null)
continue outer;
}
if (symbol != 256 && ctx.frequencies.get(symbol) > 0) {
if (symbol != model.escapeSymbol && ctx.frequencies.get(symbol) > 0) {
enc.write(ctx.frequencies, symbol);
return;
}
// Else write context escape symbol and continue decrementing the order
enc.write(ctx.frequencies, 256);
enc.write(ctx.frequencies, model.escapeSymbol);
}
// Logic for order = -1
enc.write(model.orderMinus1Freqs, symbol);
Expand Down
20 changes: 5 additions & 15 deletions java/src/PpmDecompress.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Arrays;


/**
Expand Down Expand Up @@ -51,24 +50,17 @@ static void decompress(BitInputStream in, OutputStream out) throws IOException {
// its frequency is 1 in the order -1 context but its frequency
// is 0 in all other contexts (which have non-negative order).
ArithmeticDecoder dec = new ArithmeticDecoder(32, in);
PpmModel model = new PpmModel(MODEL_ORDER, 257, 256);
PpmModel model = new PpmModel(MODEL_ORDER, 256);
int[] history = new int[0];

while (true) {
// Decode and write one byte
int symbol = decodeSymbol(dec, model, history);
if (symbol == 256) // EOF symbol
if (symbol == model.escapeSymbol) // EOF symbol
break;
out.write(symbol);
model.incrementContexts(history, symbol);

if (model.modelOrder >= 1) {
// Prepend current symbol, dropping oldest symbol if necessary
if (history.length < model.modelOrder)
history = Arrays.copyOf(history, history.length + 1);
System.arraycopy(history, 0, history, 1, history.length - 1);
history[0] = symbol;
}
history = model.addToHistory(history, symbol);
}
}

Expand All @@ -81,14 +73,12 @@ private static int decodeSymbol(ArithmeticDecoder dec, PpmModel model, int[] his
for (int order = history.length; order >= 0; order--) {
PpmModel.Context ctx = model.rootContext;
for (int i = 0; i < order; i++) {
if (ctx.subcontexts == null)
throw new AssertionError();
ctx = ctx.subcontexts[history[i]];
ctx = ctx.getSubcontexts()[history[i]];
if (ctx == null)
continue outer;
}
int symbol = dec.read(ctx.frequencies);
if (symbol < 256)
if (symbol < model.escapeSymbol)
return symbol;
// Else we read the context escape symbol, so continue decrementing the order
}
Expand Down
50 changes: 29 additions & 21 deletions java/src/PpmModel.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,16 @@
*/


import java.util.Arrays;

final class PpmModel {

/*---- Fields ----*/

public final int modelOrder;

private final int symbolLimit;
private final int escapeSymbol;
final int escapeSymbol;

public final Context rootContext;
public final FrequencyTable orderMinus1Freqs;
Expand All @@ -23,16 +25,15 @@ final class PpmModel {

/*---- Constructors ----*/

public PpmModel(int order, int symbolLimit, int escapeSymbol) {
if (order < -1 || symbolLimit <= 0 || escapeSymbol < 0 || escapeSymbol >= symbolLimit)
public PpmModel(int order, int numSymbols) {
if (order < -1 || numSymbols <= 1)
throw new IllegalArgumentException();
this.modelOrder = order;
this.symbolLimit = symbolLimit;
this.escapeSymbol = escapeSymbol;
this.symbolLimit = numSymbols + 1;
this.escapeSymbol = numSymbols;

if (order >= 0) {
rootContext = new Context(symbolLimit, order >= 1);
rootContext.frequencies.increment(escapeSymbol);
rootContext = new Context(symbolLimit);
} else
rootContext = null;
orderMinus1Freqs = new FlatFrequencyTable(symbolLimit);
Expand All @@ -50,22 +51,27 @@ public void incrementContexts(int[] history, int symbol) {

Context ctx = rootContext;
ctx.frequencies.increment(symbol);
int i = 0;
for (int sym : history) {
Context[] subctxs = ctx.subcontexts;
if (subctxs == null)
throw new AssertionError();
Context[] subctxs = ctx.getSubcontexts();

if (subctxs[sym] == null) {
subctxs[sym] = new Context(symbolLimit, i + 1 < modelOrder);
subctxs[sym].frequencies.increment(escapeSymbol);
subctxs[sym] = new Context(symbolLimit);
}
ctx = subctxs[sym];
ctx.frequencies.increment(symbol);
i++;
}
}

public int[] addToHistory(int[] history, int symbol) {
if (modelOrder >= 1) {
// Prepend current symbol, dropping oldest symbol if necessary
if (history.length < modelOrder)
history = Arrays.copyOf(history, history.length + 1);
System.arraycopy(history, 0, history, 1, history.length - 1);
history[0] = symbol;
}
return history;
}


/*---- Helper structure ----*/
Expand All @@ -74,17 +80,19 @@ public static final class Context {

public final FrequencyTable frequencies;

public final Context[] subcontexts;
private Context[] subcontexts;


public Context(int symbols, boolean hasSubctx) {
public Context(int symbols) {
frequencies = new SimpleFrequencyTable(new int[symbols]);
if (hasSubctx)
subcontexts = new Context[symbols];
else
subcontexts = null;
frequencies.increment(symbols - 1);
}

public Context[] getSubcontexts() {
if (subcontexts == null) {
subcontexts = new Context[frequencies.getSymbolLimit()];
}
return subcontexts;
}
}

}