/** fpaq0 - Stationary order 0 file arithmetic compressor. (C) 2004, Matt Mahoney under GPL, http://www.gnu.org/licenses/gpl.txt Translated to the D language by leonardo maffi, Feb 8 2008. Modified by , Copyright: GPL */ // To compile: dmd -O -release -inline fpaq0D.d // or: gdc -O3 -frelease fpaq0.d -o fpaq0D module fpaq0; import std.c.stdio: FILE, getc, putc, ftell, fopen, fclose, EOF; import std.c.time: clock, CLOCKS_PER_SEC; import std.stdio: writefln; import std.string: toStringz; import core.memory; @system /********************************************** A Predictor estimates the probability that the next bit of uncompressed data is 1. */ struct Predictor { int cxt = 1; // Context: last 0-8 bits with a leading 1 int[2][512] ct; // 0 and 1 counts in context cxt /** returns P(1) as a 12 bit number (0-4095). Assume a stationary order 0 stream of 9-bit symbols. */ nothrow pure const int p() { return 4096 * (ct[cxt][1] + 1) / (ct[cxt][0] + ct[cxt][1] + 2); } /// trains the predictor with the actual bit (0 or 1). nothrow pure void update(int y) { ct[cxt][y]++; if (ct[cxt][y] > 65534) { ct[cxt][0] >>= 1; ct[cxt][1] >>= 1; } cxt += cxt + y; if (cxt >= 512) cxt = 1; } } nothrow void compress(FILE* fin, FILE* fout) { Predictor predictor; uint x1, x2 = 0xffffffff; // Range, initially [0, 1), scaled by 2^32 uint x; // Last 4 input bytes of archive. uint y; uint xmid; for ( ; ; ) { int c = getc(fin); if (c == EOF) break; //e.encode(0); y = 0; xmid = x1 + ((x2 - x1) >> 12) * predictor.p(); assert(xmid >= x1 && xmid < x2); if (y) x2 = xmid; else x1 = xmid + 1; predictor.update(y); while (((x1 ^ x2) & 0xff000000) == 0) { putc(x2 >> 24, fout); x1 <<= 8; x2 = (x2 << 8) + 255; } for(int i = 7; i >= 0; --i){ //e.encode((c >> i) & 1); y = (c >> i) & 1; xmid = x1 + ((x2 - x1) >> 12) * predictor.p(); assert(xmid >= x1 && xmid < x2); if (y) x2 = xmid; else x1 = xmid + 1; predictor.update(y); while (((x1 ^ x2) & 0xff000000) == 0) { putc(x2 >> 24, fout); x1 <<= 8; x2 = (x2 << 8) + 255; } } } //e.encode(1) y = 1; xmid = x1 + ((x2 - x1) >> 12) * predictor.p(); assert(xmid >= x1 && xmid < x2); if (y) x2 = xmid; else x1 = xmid + 1; predictor.update(y); while (((x1 ^ x2) & 0xff000000) == 0) { putc(x2 >> 24, fout); x1 <<= 8; x2 = (x2 << 8) + 255; } // e.flush(); while (((x1 ^ x2) & 0xff000000) == 0) { putc(x2 >> 24, fout); x1 <<= 8; x2 = (x2 << 8) + 255; } putc(x2 >> 24, fout); // First unequal byte. } nothrow void decompress(FILE* fin, FILE* fout) { Predictor predictor; uint x1, x2 = 0xffffffff; // Range, initially [0, 1), scaled by 2^32 uint x; // Last 4 input bytes of archive. int c; uint xmid; int y; // Initialize x to the first 4 bytes of the archive foreach(i; 0 .. 4) { c = getc(fin); if (c == EOF) c = 0; x = (x << 8) + (c & 0xff); } // pre-encode for while xmid = x1 + ((x2 - x1) >> 12) * predictor.p(); assert(xmid >= x1 && xmid < x2); y = 0; if (x <= xmid) { y = 1; x2 = xmid; } else x1 = xmid + 1; predictor.update(y); while (((x1 ^ x2) & 0xff000000) == 0) { x1 <<= 8; x2 = (x2 << 8) + 255; int c2 = getc(fin); if (c2 == EOF) c2 = 0; x = (x << 8) + c2; } while (!y) { //acho que isto vai dar problema c = 1; while (c < 256) { xmid = x1 + ((x2 - x1) >> 12) * predictor.p(); assert(xmid >= x1 && xmid < x2); y = 0; if (x <= xmid) { y = 1; x2 = xmid; } else x1 = xmid + 1; predictor.update(y); while (((x1 ^ x2) & 0xff000000) == 0) { x1 <<= 8; x2 = (x2 << 8) + 255; int c2 = getc(fin); if (c2 == EOF) c2 = 0; x = (x << 8) + c2; } c += c + y; } putc(c - 256, fout); // second round, for while definition xmid = x1 + ((x2 - x1) >> 12) * predictor.p(); assert(xmid >= x1 && xmid < x2); y = 0; if (x <= xmid) { y = 1; x2 = xmid; } else x1 = xmid + 1; predictor.update(y); while (((x1 ^ x2) & 0xff000000) == 0) { x1 <<= 8; x2 = (x2 << 8) + 255; int c2 = getc(fin); if (c2 == EOF) c2 = 0; x = (x << 8) + c2; } } } int main(string[] args) { // Chech arguments: fpaq0 c/d input output if (args.length != 4 || (args[1] != "c" && args[1] != "d")) { writefln("To compress: fpaq0 c input output"); writefln("To decompress: fpaq0 d input output"); return 1; } auto start = clock(); // Start timer // Open files FILE* fin = fopen(args[2].toStringz(), "rb".ptr); if (!fin) { writefln(args[2]); return 1; } FILE* fout = fopen(args[3].toStringz(), "wb".ptr); if (!fout) { writefln(args[3]); return 1; } GC.disable; if (args[1] == "c") compress(fin, fout); else decompress(fin, fout); // Print results double stop = (clock() - start) / cast(double)CLOCKS_PER_SEC; writefln("%s (%d bytes) -> %s (%d bytes) in %1.2f s", args[2], ftell(fin), args[3], ftell(fout), stop); fclose(fin); fclose(fout); return 0; }