Mercurial > thymian
diff 3rdparty/compressor/fpaq0.cpp @ 0:a4671277546c tip
created the repository for the thymian project
| author | ferencd |
|---|---|
| date | Tue, 17 Aug 2021 11:19:54 +0200 |
| parents | |
| children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/3rdparty/compressor/fpaq0.cpp Tue Aug 17 11:19:54 2021 +0200 @@ -0,0 +1,161 @@ +// fpaq0 - Stationary order 0 file compressor. +// (C) 2004, Matt Mahoney under GPL, http://www.gnu.org/licenses/gpl.txt +// To compile: g++ -O fpaq0.cpp + +#include <cstdio> +#include <cstdlib> +#include <cstring> +#include <ctime> +#include <cassert> +#include <iostream> +#include "fpaq0.h" + +using namespace std; + + +// Constructor +Encoder::Encoder(Mode m, Archive &parchive): predictor(), mode(m), archive(parchive), x1(0), + x2(0xffffffff), x(0) +{ + + // In DECOMPRESS mode, initialize x to the first 4 bytes of the archive + if (mode==DECOMPRESS) + { + for (int i=0; i<4; ++i) + { + int c = archive.getc(); + if (c==EOF) c=0; + x=(x<<8)+(c&0xff); + } + } +} + +/* encode(y) -- Encode bit y by splitting the range [x1, x2] in proportion +to P(1) and P(0) as given by the predictor and narrowing to the appropriate +subrange. Output leading bytes of the range as they become known. */ + +inline void Encoder::encode(int y) +{ + + // Update the range + const uint32_t xmid = x1 + ((x2-x1) >> 12) * predictor.p(); + assert(xmid >= x1 && xmid < x2); + if (y) + x2=xmid; + else + x1=xmid+1; + predictor.update(y); + + // Shift equal MSB's out + while ( ((x1^x2) & 0xff000000) == 0) + { + archive.putc(x2>>24); + x1 <<= 8; + x2 = (x2<<8) + 255; + } +} + +/* Decode one bit from the archive, splitting [x1, x2] as in the encoder +and returning 1 or 0 depending on which subrange the archive point x is in. +*/ +inline int Encoder::decode() +{ + + // Update the range + const uint32_t xmid = x1 + ((x2-x1) >> 12) * predictor.p(); + assert(xmid >= x1 && xmid < x2); + int y=0; + if (x<=xmid) + { + y=1; + x2=xmid; + } + else + x1=xmid+1; + predictor.update(y); + + // Shift equal MSB's out + while (((x1^x2)&0xff000000)==0) + { + x1 <<= 8; + x2=(x2<<8)+255; + int c = archive.getc(); + if (c==EOF) c=0; + x = (x<<8) + c; + } + return y; +} + +// Should be called when there is no more to compress +void Encoder::flush() +{ + + // In COMPRESS mode, write out the remaining bytes of x, x1 < x < x2 + if (mode==COMPRESS) + { + while (((x1^x2)&0xff000000)==0) + { + archive.putc(x2>>24); + x1 <<= 8; + x2 = (x2<<8) + 255; + } + archive.putc(x2>>24); // First unequal byte + } +} + +std::vector<unsigned char> compress(const std::string& s) +{ + Archive a; + Encoder e(COMPRESS, a); + char c = 0; + size_t idx = 0; + while ( idx < s.length() ) + { + c = s[idx]; + e.encode(0); + for (int i=7; i>=0; --i) + { + e.encode((c>>i)&1); + } + idx ++; + } + e.encode(1); // EOF code + e.flush(); + + return a.data(); +} + +std::string decompress(const std::vector<unsigned char>& v) +{ + Archive a(v); + std::string result; + Encoder e(DECOMPRESS, a); + while (!e.decode()) + { + int c=1; + while (c<256) + c += c + e.decode(); + + char temp[2] = {0}; + temp[0] = c - 256; + + result += temp; + } + return result; +} + +int Predictor::p() const +{ + return 4096*(ct[cxt][1]+1)/(ct[cxt][0]+ct[cxt][1]+2); +} + +void Predictor::update(int y) +{ + if (++ct[cxt][y] > 65534) + { + ct[cxt][0] >>= 1; + ct[cxt][1] >>= 1; + } + if ((cxt+=cxt+y) >= 512) + cxt=1; +}
