| #include "endian.hh" /* For R64 */ |
| |
| extern "C" { |
| #include "C/LzmaDec.h" |
| #include "C/LzmaEnc.h" |
| } |
| |
| #include "lzma.hh" |
| |
| #include <algorithm> // min,max,swap |
| #include <vector> |
| #include <string> |
| #include <cstring> // std::memcpy |
| |
| |
| #include <cstdio> |
| |
| #include <stdint.h> |
| |
| /* We don't want threads */ |
| #ifdef linux |
| #include <sched.h> |
| #define ForceSwitchThread() sched_yield() |
| #else |
| #define ForceSwitchThread() |
| #endif |
| |
| |
| int LZMA_verbose = 0; |
| |
| // -fb |
| unsigned LZMA_NumFastBytes = 273; |
| /*from lzma.txt: |
| Set number of fast bytes - [5, 273], default: 273 |
| Usually big number gives a little bit better compression ratio |
| and slower compression process. |
| from anonymous: |
| This one is hard to explain... To my knowledge (please correct me if I |
| am wrong), this refers to the optimal parsing algorithm. The algorithm |
| tries many different combinations of matches to find the best one. If a |
| match is found that is over the fb value, then it will not be optimised, |
| and will just be used straight. |
| This speeds up corner cases such as pic. |
| */ |
| |
| /* apparently, 0 and 1 are valid values. 0 = fast mode */ |
| unsigned LZMA_AlgorithmNo = 1; |
| |
| unsigned LZMA_MatchFinderCycles = 0; // default: 0 |
| |
| // -pb |
| unsigned LZMA_PosStateBits = 0; // default: 2, range: 0..4 |
| /*from lzma.txt: |
| pb switch is intended for periodical data |
| when period is equal 2^N. |
| */ |
| |
| |
| // -lp |
| unsigned LZMA_LiteralPosStateBits = 0; // default: 0, range: 0..4 |
| /*from lzma.txt: |
| lp switch is intended for periodical data when period is |
| equal 2^N. For example, for 32-bit (4 bytes) |
| periodical data you can use lp=2. |
| Often it's better to set lc0, if you change lp switch. |
| */ |
| |
| // -lc |
| unsigned LZMA_LiteralContextBits = 1; // default: 3, range: 0..8 |
| /*from lzma.txt: |
| Sometimes lc=4 gives gain for big files. |
| from anonymous: |
| The context for the literal coder is 2^(lc) long. The longer it is, the |
| better the statistics, but also the slower it adapts. A tradeoff, which |
| is why 3 or 4 is reccommended. |
| */ |
| |
| /* |
| |
| Discoveries: |
| |
| INODES: |
| Best LZMA for raw_inotab_inode(40->48): pb0 lp0 lc0 |
| Best LZMA for raw_root_inode(28->32): pb0 lp0 lc0 |
| |
| Start LZMA(rootdir, 736 bytes) |
| Yay result with pb0 lp0 lc0: 218 |
| Yay result with pb0 lp0 lc1: 217 |
| Best LZMA for rootdir(736->217): pb0 lp0 lc1 |
| |
| Start LZMA(inotab, 379112 bytes) |
| Yay result with pb0 lp0 lc0: 24504 |
| Best LZMA for inotab(379112->24504): pb0 lp0 lc0 |
| |
| BLKTAB: |
| Best LZMA for raw_blktab(10068->2940): pb2 lp2 lc0 |
| |
| ---with fastbytes=128--- |
| Start LZMA(blktab, 12536608 bytes) |
| Yay result with pb0 lp0 lc0: 1386141 |
| Yay result with pb0 lp1 lc0: 1308137 |
| Yay result with pb0 lp2 lc0: 1305403 |
| Yay result with pb0 lp3 lc0: 1303072 |
| Yay result with pb1 lp1 lc0: 1238990 |
| Yay result with pb1 lp2 lc0: 1227973 |
| Yay result with pb1 lp3 lc0: 1221205 |
| Yay result with pb2 lp1 lc0: 1197035 |
| Yay result with pb2 lp2 lc0: 1188979 |
| Yay result with pb2 lp3 lc0: 1184531 |
| Yay result with pb3 lp1 lc0: 1183866 |
| Yay result with pb3 lp2 lc0: 1172994 |
| Yay result with pb3 lp3 lc0: 1169048 |
| Best LZMA for blktab(12536608->1169048): pb3 lp3 lc0 |
| |
| It seems, lc=0 and pb=lp=N is a wise choice, |
| where N is 2 for packed blktab and 3 for unpacked. |
| |
| FBLOCKS: |
| For SPC sound+code data, the best results |
| are between: |
| pb0 lp0 lc0 (10%) |
| pb0 lp0 lc1 (90%) |
| For inotab, these were observed: |
| pb1 lp0 lc1 |
| pb2 lp0 lc0 |
| pb1 lp1 lc0 |
| pb3 lp1 lc0 |
| pb1 lp2 lc0 |
| pb2 lp1 lc0 |
| |
| For C source code data, the best results |
| are between: |
| pb1 lp0 lc3 (10%) |
| pb0 lp0 lc3 (90%) |
| Occasionally: |
| pb0 lp1 lc0 |
| pb0 lp0 lc3 (mostly) |
| pb0 lp0 lc2 |
| pb0 lp0 lc4 |
| Occasionally 2: |
| pb0 lp0 lc8 |
| pb0 lp0 lc4 |
| |
| BUT: |
| Best LZMA for fblock(204944->192060): pb0 lp4 lc8 -- surprise! (INOTAB PROBABLY) |
| |
| */ |
| |
| static UInt32 SelectDictionarySizeFor(unsigned datasize) |
| { |
| #if 1 |
| if(datasize >= (1 << 30U)) return 1 << 30U; |
| return datasize; |
| #else |
| #ifdef __GNUC__ |
| /* gnu c can optimize this switch statement into a fast binary |
| * search, but it cannot do so for the list of the if statements. |
| */ |
| switch(datasize) |
| { |
| case 0 ... 512 : return 512; |
| case 513 ... 1024: return 2048; |
| case 1025 ... 4096: return 8192; |
| case 4097 ... 16384: return 32768; |
| case 16385 ... 65536: return 528288; |
| case 65537 ... 528288: return 1048576*4; |
| case 528289 ... 786432: return 1048576*16; |
| default: return 1048576*32; |
| } |
| #else |
| if(datasize <= 512) return 512; |
| if(datasize <= 1024) return 1024; |
| if(datasize <= 4096) return 4096; |
| if(datasize <= 16384) return 32768; |
| if(datasize <= 65536) return 528288; |
| if(datasize <= 528288) return 1048576*4; |
| if(datasize <= 786432) reutrn 1048576*16; |
| return 32*1048576; |
| #endif |
| #endif |
| } |
| |
| static void *SzAlloc(void*, size_t size) |
| { return new unsigned char[size]; } |
| static void SzFree(void*, void *address) |
| { unsigned char*a = (unsigned char*)address; delete[] a; } |
| static ISzAlloc LZMAalloc = { SzAlloc, SzFree }; |
| |
| class MemReader: public ISeqInStream |
| { |
| public: |
| const unsigned char* const indata; |
| const size_t inlength; |
| size_t pos; |
| public: |
| MemReader(const unsigned char* d, size_t l) |
| : ISeqInStream(), indata(d), inlength(l), pos(0) |
| { |
| Read = ReadMethod; |
| } |
| static SRes ReadMethod(void *pp, void *buf, size_t *size) |
| { |
| MemReader& p = *(MemReader*)pp; |
| size_t rem = p.inlength-p.pos; |
| size_t read = *size; |
| if(read > rem) read= rem; |
| std::memcpy(buf, &p.indata[p.pos], read); |
| *size = read; |
| p.pos += read; |
| return SZ_OK; |
| } |
| }; |
| class MemWriter: public ISeqOutStream |
| { |
| public: |
| std::vector<unsigned char> buf; |
| public: |
| MemWriter(): ISeqOutStream(), buf() { Write = WriteMethod; } |
| |
| static size_t WriteMethod(void*pp, const void* from, size_t size) |
| { |
| MemWriter& p = *(MemWriter*)pp; |
| const unsigned char* i = (const unsigned char*)from; |
| p.buf.insert(p.buf.end(), i, i+size); |
| return size; |
| } |
| }; |
| |
| const std::vector<unsigned char> LZMACompress(const unsigned char* data, size_t length, |
| unsigned pb, |
| unsigned lp, |
| unsigned lc) |
| { |
| return LZMACompress(data,length, pb,lp,lc, |
| SelectDictionarySizeFor(length)); |
| } |
| |
| const std::vector<unsigned char> LZMACompress( |
| const unsigned char* data, size_t length, |
| unsigned pb, |
| unsigned lp, |
| unsigned lc, |
| unsigned dictionarysize) |
| { |
| if(!length) return std::vector<unsigned char>(); |
| |
| CLzmaEncProps props; |
| LzmaEncProps_Init(&props); |
| props.dictSize = dictionarysize; |
| props.pb = pb; |
| props.lp = lp; |
| props.lc = lc; |
| props.fb = LZMA_NumFastBytes; |
| props.mc = LZMA_MatchFinderCycles; |
| props.algo = LZMA_AlgorithmNo; |
| props.numThreads = 1; |
| |
| switch(LZMA_AlgorithmNo) |
| { |
| case 0: // quick: HC4 |
| props.btMode = 0; |
| props.level = 1; |
| break; |
| case 1: // full: BT4 |
| default: |
| props.level = 9; |
| props.btMode = 1; |
| props.numHashBytes = 4; |
| break; |
| } |
| |
| CLzmaEncHandle p = LzmaEnc_Create(&LZMAalloc); |
| struct AutoReleaseLzmaEnc |
| { |
| AutoReleaseLzmaEnc(CLzmaEncHandle pp) : p(pp) { } |
| ~AutoReleaseLzmaEnc() |
| { LzmaEnc_Destroy(p, &LZMAalloc, &LZMAalloc); } |
| CLzmaEncHandle p; |
| |
| |
| AutoReleaseLzmaEnc(const AutoReleaseLzmaEnc&); |
| void operator=(const AutoReleaseLzmaEnc&); |
| |
| } AutoReleaser(p); // Create a destructor that ensures |
| // that the CLzmaEncHandle is not leaked, even if an |
| // exception happens |
| |
| int res = LzmaEnc_SetProps(p, &props); |
| if(res != SZ_OK) |
| { |
| Error: |
| return std::vector<unsigned char> (); |
| } |
| |
| unsigned char propsEncoded[LZMA_PROPS_SIZE + 8]; |
| size_t propsSize = sizeof propsEncoded; |
| res = LzmaEnc_WriteProperties(p, propsEncoded, &propsSize); |
| if(res != SZ_OK) goto Error; |
| |
| MemReader is(data, length); |
| MemWriter os; |
| put_64(propsEncoded+LZMA_PROPS_SIZE, length); |
| os.buf.insert(os.buf.end(), propsEncoded, propsEncoded+LZMA_PROPS_SIZE+8); |
| |
| res = LzmaEnc_Encode(p, &os, &is, 0, &LZMAalloc, &LZMAalloc); |
| if(res != SZ_OK) goto Error; |
| |
| return os.buf; |
| } |
| |
| const std::vector<unsigned char> LZMACompress(const unsigned char* data, size_t length) |
| { |
| return LZMACompress(data, length, |
| LZMA_PosStateBits, |
| LZMA_LiteralPosStateBits, |
| LZMA_LiteralContextBits); |
| } |
| |
| #undef RC_NORMALIZE |
| |
| const std::vector<unsigned char> LZMADeCompress |
| (const unsigned char* data, size_t length, bool& ok) |
| { |
| if(length <= LZMA_PROPS_SIZE+8) |
| { |
| /*clearly_not_ok:*/ |
| ok = false; |
| return std::vector<unsigned char> (); |
| } |
| |
| uint_least64_t out_sizemax = get_64(&data[LZMA_PROPS_SIZE]); |
| |
| /*if(out_sizemax >= (size_t)~0ULL) |
| { |
| // cannot even allocate a vector this large. |
| goto clearly_not_ok; |
| }*/ |
| |
| std::vector<unsigned char> result(out_sizemax); |
| |
| ELzmaStatus status; |
| SizeT destlen = result.size(); |
| SizeT srclen = length-(LZMA_PROPS_SIZE+8); |
| int res = LzmaDecode( |
| &result[0], &destlen, |
| &data[LZMA_PROPS_SIZE+8], &srclen, |
| &data[0], LZMA_PROPS_SIZE, |
| LZMA_FINISH_END, |
| &status, |
| &LZMAalloc); |
| |
| /* |
| std::fprintf(stderr, "res=%d, status=%d, in_done=%d (buf=%d), out_done=%d (max=%d)\n", |
| res, |
| (int)status, |
| (int)srclen, (int)length, |
| (int)destlen, (int)out_sizemax); |
| */ |
| |
| ok = res == SZ_OK && (status == LZMA_STATUS_FINISHED_WITH_MARK |
| || status == LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK) |
| && srclen == (length-(LZMA_PROPS_SIZE+8)) |
| && destlen == out_sizemax; |
| return result; |
| } |
| |
| const std::vector<unsigned char> LZMADeCompress |
| (const unsigned char* data, size_t length) |
| { |
| bool ok_unused; |
| return LZMADeCompress(data, length, ok_unused); |
| } |
| |
| #if 0 |
| #include <stdio.h> |
| int main(void) |
| { |
| char Buf[2048*2048]; |
| int s = fread(Buf,1,sizeof(Buf),stdin); |
| std::vector<unsigned char> result = LZMADeCompress(std::vector<unsigned char>(Buf,Buf+s)); |
| fwrite(&result[0],1,result.size(),stdout); |
| } |
| #endif |
| |
| const std::vector<unsigned char> LZMACompressHeavy(const unsigned char* data, size_t length, |
| const char* why) |
| { |
| std::vector<unsigned char> bestresult; |
| char best[512]; |
| bool first = true; |
| if(LZMA_verbose >= 1) |
| { |
| std::fprintf(stderr, "Start LZMA(%s, %u bytes)\n", why, (unsigned)length); |
| std::fflush(stderr); |
| } |
| |
| unsigned minresultsize=0, maxresultsize=0; |
| unsigned sizemap[5][5][9] = {{{0}}}; |
| |
| bool use_small_dict = false; |
| |
| for(int compress_mode = 0; compress_mode < (5*5*9); ++compress_mode) |
| { |
| const unsigned pb = compress_mode % 5; |
| const unsigned lp = (compress_mode / 5) % 5; |
| const unsigned lc = (compress_mode / 5 / 5) % 9; |
| |
| std::vector<unsigned char> |
| result = use_small_dict |
| ? LZMACompress(data,length,pb,lp,lc, 4096) |
| : LZMACompress(data,length,pb,lp,lc); |
| |
| { |
| sizemap[pb][lp][lc] = result.size(); |
| |
| if(first || result.size() < minresultsize) minresultsize = result.size(); |
| if(first || result.size() > maxresultsize) maxresultsize = result.size(); |
| if(first || result.size() < bestresult.size()) |
| { |
| sprintf(best, "pb%u lp%u lc%u", |
| pb,lp,lc); |
| if(LZMA_verbose >= 1) |
| std::fprintf(stderr, "Yay result with %s: %u\n", best, (unsigned)result.size()); |
| bestresult.swap(result); |
| first = false; |
| } |
| else |
| { |
| char tmp[512]; |
| sprintf(tmp, "pb%u lp%u lc%u", |
| pb,lp,lc); |
| if(LZMA_verbose >= 2) |
| std::fprintf(stderr, "Blaa result with %s: %u\n", tmp, (unsigned)result.size()); |
| } |
| if(LZMA_verbose >= 2) |
| { |
| std::fprintf(stderr, "%*s\n", (5 * (4+9+2)), ""); |
| /* Visualize the size map: */ |
| std::string lines[6] = {}; |
| for(unsigned pbt = 0; pbt <= 4; ++pbt) |
| { |
| char buf[64]; sprintf(buf, "pb%u:%11s", pbt,""); |
| lines[0] += buf; |
| |
| for(unsigned lpt = 0; lpt <= 4; ++lpt) |
| { |
| char buf[64]; sprintf(buf, "lp%u:", lpt); |
| std::string line; |
| line += buf; |
| for(unsigned lct = 0; lct <= 8; ++lct) |
| { |
| unsigned s = sizemap[pbt][lpt][lct]; |
| char c; |
| if(!s) c = '.'; |
| else c = 'a' + ('z'-'a'+1) |
| * (s - minresultsize) |
| / (maxresultsize-minresultsize+1); |
| line += c; |
| } |
| lines[1 + lpt] += line + " "; |
| } |
| } |
| for(unsigned a=0; a<6; ++a) std::fprintf(stderr, "%s\n", lines[a].c_str()); |
| std::fprintf(stderr, "\33[%uA", 7); |
| } |
| } |
| } |
| if(LZMA_verbose >= 2) |
| std::fprintf(stderr, "\n\n\n\n\n\n\n\n"); |
| |
| if(LZMA_verbose >= 1) |
| { |
| std::fprintf(stderr, "Best LZMA for %s(%u->%u): %s\n", |
| why, |
| (unsigned)length, |
| (unsigned)bestresult.size(), |
| best); |
| } |
| std::fflush(stderr); |
| return bestresult; |
| } |
| |
| /* |
| |
| The LZMA compression power is controlled by these parameters: |
| Dictionary size (we use the maximum) |
| Compression algorithm (we use BT4, the heaviest available) |
| Number of fast bytes (we use the maximum) |
| pb (0..4), lp (0..4) and lc (0..8) -- the effect of these depends on data. |
| |
| Since the only parameters whose effect depends on the data to be compressed |
| are the three (pb, lp, lc), the "auto" and "full" compression algorithms |
| only try to find the optimal values for those. |
| |
| The "auto" LZMA compression algorithm is based on these two assumptions: |
| - It is possible to find the best value for each component (pb, lp, lc) |
| by individually testing the most effective one of them while keeping |
| the others static. |
| I.e., step 1: pb=<find best>, lp=0, lc=0 |
| step 2: pb=<use result>, lp=<find best>, lc=0 |
| step 3: pb=<use result>, lp=<use result>, lc=<find best> |
| final: pb=<use result>, lp=<use result>, lc=<use result> |
| - That the effect of each of these components forms a parabolic function |
| that has a starting point, ending point, and possibly a mountain or a |
| valley somewhere in the middle, but never a valley _and_ a mountain, nor |
| two valleys nor two mountains. |
| These assumptions are not always true, but it gets very close to the optimum. |
| |
| The ParabolicFinder class below finds the lowest point in a parabolic curve |
| with a small number of tests, determining the shape of the curve by sampling |
| a few cue values as needed. |
| |
| The algorithm is like this: |
| Never check any value more than once. |
| Check the first two values. |
| If they differ, then check the last in sequence. |
| If not, then check everything in sequential order. |
| If the first two values and the last form an ascending sequence, accept the first value. |
| If they form a descending sequence, start Focus Mode |
| such that the focus lower limit is index 2 and upper |
| limit is the second last. Then check the second last. |
| If they don't, then check the third value of sequence, |
| and everything else in sequential order. |
| If in Focus Mode, check if being in the lower or upper end of the focus. |
| If in upper end, check if the current value is bigger than the next one. |
| If it is, end the process, because the smallest value has already been found. |
| If not, next check the value at focus_low, and increase focus_low. |
| If in lower end, check if the current value is bigger than the previous one. |
| If it is, end the process, because the smallest value has already been found. |
| If not, next check the value at focus_high, and decrease focus_high. |
| |
| For any sample space, it generally does 3 tests, but if it detects a curve |
| forming a valley, it may do more. |
| |
| Note that ParabolicFinder does not _indicate_ the lowest value. It leaves that |
| to the caller. It just stops searching when it thinks that no lower value will |
| be found. |
| |
| Note: The effect of pb, lp and lc depend also on the dictionary size setting |
| and compression algorithm. You cannot estimate the optimal value for those |
| parameters reliably using different compression settings than in the actual case. |
| |
| */ |
| class ParabolicFinder |
| { |
| public: |
| enum QueryState { Unknown, Pending, Done }; |
| enum InstructionType { HereYouGo, WaitingResults, End }; |
| public: |
| ParabolicFinder(unsigned Start, unsigned End) |
| : begin(Start), |
| results(End-Start+1, 0), |
| state (End-Start+1, Unknown), |
| LeftRightSwap(false) |
| { |
| } |
| |
| InstructionType GetNextInstruction(unsigned& attempt) |
| { |
| InstructionType result = End; |
| |
| const int Last = begin + results.size()-1; |
| |
| #define RetIns(n) do{ result = (n); goto DoneCrit; }while(0) |
| #define RetVal(n) do{ state[attempt = (n)] = Pending; RetIns(HereYouGo); }while(0) |
| |
| { |
| /* |
| std::fprintf(stderr, "NextInstruction..."); |
| for(unsigned a=0; a<state.size(); ++a) |
| std::fprintf(stderr, " %u=%s", a, |
| state[a]==Unknown?"??" |
| :state[a]==Done?"Ok" |
| :".."); |
| std::fprintf(stderr, "\n");*/ |
| |
| if(CountUnknown() == 0) |
| { |
| // No unassigned slots remain. Don't need more workers. |
| RetIns(End); |
| } |
| |
| if(1) // scope for local variables |
| { |
| // Alternate which side to do next if both are available. |
| bool LeftSideFirst = LeftRightSwap ^= 1; |
| |
| // Check left side descend type |
| int LeftSideNext = -1; bool LeftSideDoable = false; |
| for(int c=0; c<=Last; ++c) |
| switch(state[c]) |
| { |
| case Unknown: LeftSideNext = c; LeftSideDoable = true; goto ExitLeftSideFor; |
| case Pending: LeftSideNext = c; LeftSideDoable = false; goto ExitLeftSideFor; |
| case Done: |
| if(c == 0) continue; |
| if(results[c] > results[c-1]) |
| { |
| // Left side stopped descending. |
| if(state[Last] != Unknown) RetIns(End); |
| goto ExitLeftSideFor; |
| } |
| else if(results[c] == results[c-1]) |
| LeftSideFirst = true; |
| } |
| ExitLeftSideFor: ; |
| |
| // Check right side descend type |
| int RightSideNext = -1; bool RightSideDoable = false; |
| for(int c=Last; c>=0; --c) |
| switch(state[c]) |
| { |
| case Unknown: RightSideNext = c; RightSideDoable = true; goto ExitRightSideFor; |
| case Pending: RightSideNext = c; RightSideDoable = false; goto ExitRightSideFor; |
| case Done: |
| if(c == Last) continue; |
| if(results[c] > results[c+1]) |
| { |
| // Right side stopped descending. |
| if(state[0] != Unknown) RetIns(End); |
| goto ExitRightSideFor; |
| } |
| else if(results[c] == results[c+1]) |
| LeftSideFirst = false; |
| } |
| ExitRightSideFor: ; |
| |
| if(!LeftSideFirst) |
| { std::swap(LeftSideDoable, RightSideDoable); |
| std::swap(LeftSideNext, RightSideNext); } |
| |
| if(LeftSideDoable) RetVal(LeftSideNext); |
| if(RightSideDoable) RetVal(RightSideNext); |
| |
| // If we have excess threads and work to do, give them something |
| if(CountHandled() > 2) if(LeftSideNext >= 0) RetVal(LeftSideNext); |
| if(CountHandled() > 3) if(RightSideNext >= 0) RetVal(RightSideNext); |
| |
| RetIns(WaitingResults); |
| } |
| |
| DoneCrit: ; |
| } |
| return result; |
| } |
| |
| void GotResult(unsigned attempt, unsigned value) |
| { |
| { |
| results[attempt] = value; |
| state[attempt] = Done; |
| } |
| } |
| |
| private: |
| unsigned CountUnknown() const |
| { |
| unsigned result=0; |
| for(size_t a=0, b=state.size(); a<b; ++a) |
| if(state[a] == Unknown) ++result; |
| return result; |
| } |
| unsigned CountHandled() const |
| { |
| return state.size() - CountUnknown(); |
| } |
| private: |
| unsigned begin; |
| std::vector<unsigned> results; |
| std::vector<QueryState> state; |
| bool LeftRightSwap; |
| }; |
| |
| static void LZMACompressAutoHelper( |
| const unsigned char* data, size_t length, |
| bool use_small_dict, |
| const char* why, |
| unsigned& pb, unsigned& lp, unsigned& lc, |
| unsigned& which_iterate, ParabolicFinder& finder, |
| bool&first, std::vector<unsigned char>& bestresult) |
| { |
| for(;;) |
| { |
| unsigned t=0; |
| switch(finder.GetNextInstruction(t)) |
| { |
| case ParabolicFinder::End: |
| return; |
| case ParabolicFinder::HereYouGo: |
| break; |
| case ParabolicFinder::WaitingResults: |
| ForceSwitchThread(); |
| continue; |
| } |
| |
| const unsigned try_pb = &which_iterate == &pb ? t : pb; |
| const unsigned try_lp = &which_iterate == &lp ? t : lp; |
| const unsigned try_lc = &which_iterate == &lc ? t : lc; |
| |
| if(LZMA_verbose >= 2) |
| std::fprintf(stderr, "%s:Trying pb%u lp%u lc%u\n", |
| why,try_pb,try_lp,try_lc); |
| |
| std::vector<unsigned char> result = use_small_dict |
| ? LZMACompress(data,length,try_pb,try_lp,try_lc, 65536) |
| : LZMACompress(data,length,try_pb,try_lp,try_lc); |
| |
| if(LZMA_verbose >= 2) |
| std::fprintf(stderr, "%s: pb%u lp%u lc%u -> %u\n", |
| why,try_pb,try_lp,try_lc, (unsigned)result.size()); |
| |
| finder.GotResult(t, result.size()); |
| |
| { |
| if(first || result.size() <= bestresult.size()) |
| { |
| first = false; |
| bestresult.swap(result); |
| which_iterate = t; |
| } |
| } |
| } |
| } |
| |
| |
| const std::vector<unsigned char> LZMACompressAuto(const unsigned char* data, size_t length, |
| const char* why) |
| { |
| if(LZMA_verbose >= 1) |
| { |
| std::fprintf(stderr, "Start LZMA(%s, %u bytes)\n", why, (unsigned)length); |
| std::fflush(stderr); |
| } |
| |
| unsigned backup_algorithm = LZMA_AlgorithmNo; |
| |
| bool use_small_dict = false;//length >= 1048576; |
| |
| if(use_small_dict) LZMA_AlgorithmNo = 0; |
| |
| unsigned pb=0, lp=0, lc=0; |
| |
| std::vector<unsigned char> bestresult; |
| |
| { |
| ParabolicFinder pb_finder(0,4); |
| ParabolicFinder lp_finder(0,4); |
| ParabolicFinder lc_finder(0,8); |
| bool first=true; |
| { |
| /* Using parallelism here. However, we need barriers after |
| * each step, because the comparisons are made based on the |
| * result size, and if the pb/lp/lc values other than the |
| * one being focused change, it won't work. Only one parameter |
| * must change in the loop. |
| */ |
| |
| /* step 1: find best value in pb axis */ |
| LZMACompressAutoHelper(data,length,use_small_dict,why, |
| pb, lp, lc, |
| pb, pb_finder, first, bestresult); |
| |
| |
| lp_finder.GotResult(lp, bestresult.size()); |
| |
| /* step 2: find best value in lp axis */ |
| LZMACompressAutoHelper(data,length,use_small_dict,why, |
| pb, lp, lc, |
| lp, lp_finder, first, bestresult); |
| |
| lc_finder.GotResult(lc, bestresult.size()); |
| |
| /* step 3: find best value in lc axis */ |
| LZMACompressAutoHelper(data,length,use_small_dict,why, |
| pb, lp, lc, |
| lc, lc_finder, first, bestresult); |
| } |
| } |
| |
| if(use_small_dict || LZMA_AlgorithmNo != backup_algorithm) |
| { |
| LZMA_AlgorithmNo = backup_algorithm; |
| bestresult = LZMACompress(data,length, pb,lp,lc); |
| } |
| |
| if(LZMA_verbose >= 1) |
| { |
| std::fprintf(stderr, "Best LZMA for %s(%u->%u): pb%u lp%u lc%u\n", |
| why, |
| (unsigned)length, |
| (unsigned)bestresult.size(), |
| pb,lp,lc); |
| } |
| std::fflush(stderr); |
| |
| return bestresult; |
| } |
| |
| const std::vector<unsigned char> |
| DoLZMACompress(int HeavyLevel, |
| const unsigned char* data, size_t length, |
| const char* why) |
| { |
| if(HeavyLevel >= 2) return LZMACompressHeavy(data,length, why); |
| if(HeavyLevel >= 1) return LZMACompressAuto(data,length, why); |
| return LZMACompress(data,length); |
| } |
| |
| extern "C" { |
| |
| /** |
| * Compress a buffer with lzma |
| * Don't copy the result back if it is too large. |
| * @param in a pointer to the buffer |
| * @param in_len the length in bytes |
| * @param out a pointer to a buffer of at least size in_len |
| * @param out_len a pointer to the compressed length of in |
| */ |
| |
| void do_lzma_compress(char *in, int in_len, char *out, int *out_len) { |
| std::vector<unsigned char> result; |
| result = LZMACompress(std::vector<unsigned char>(in, in + in_len)); |
| *out_len = result.size(); |
| if (*out_len < in_len) |
| std::memcpy(out, &result[0], *out_len); |
| } |
| |
| void do_lzma_uncompress(char *dst, int dst_len, char *src, int src_len) { |
| std::vector<unsigned char> result; |
| result = LZMADeCompress(std::vector<unsigned char>(src, src + src_len)); |
| if (result.size() <= (SizeT)dst_len) |
| std::memcpy(dst, &result[0], result.size()); |
| else |
| { |
| fprintf(stderr, "Not copying %d bytes to %d-byte buffer!\n", |
| (unsigned int)result.size(), dst_len); |
| exit(1); |
| } |
| } |
| |
| } |
| |