/* * minimal lzma implementation * * Copyright (C) 2002 Eric Biederman * Copyright (C) 2005 Joel Yliluoma * Copyright (C) 2007 coresystems GmbH * (Adapted by Stefan Reinauer for coresystems GmbH) * Copyright (C) 2007 Patrick Georgi * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA */ #include "C/Common/MyInitGuid.h" #include "C/7zip/Compress/LZMA/LZMAEncoder.h" #include #include #include #include #include #include #include #include #include #include #include const std::vector LZMACompress (const std::vector& buf); const std::vector LZMADeCompress (const std::vector& buf); static inline uint16_t R16(const void* p) { const unsigned char* data = (const unsigned char*)p; return (data[0] << 0) | (data[1] << 8); } static inline uint32_t R32(const void* p) { const unsigned char* data = (const unsigned char*)p; return R16(data) | (R16(data+2) << 16); } #define L (uint64_t) static inline uint64_t R64(const void* p) { const unsigned char* data = (const unsigned char*)p; return (L R32(data)) | ((L R32(data+4)) << 32); } #undef L static UInt32 SelectDictionarySizeFor(unsigned datasize) { #if 1 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 } class CInStreamRam: public ISequentialInStream, public CMyUnknownImp { const std::vector& input; size_t Pos; public: MY_UNKNOWN_IMP CInStreamRam(const std::vector& buf) : input(buf), Pos(0) { } virtual ~CInStreamRam() {} STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); }; STDMETHODIMP CInStreamRam::Read(void *data, UInt32 size, UInt32 *processedSize) { UInt32 remain = input.size() - Pos; if (size > remain) size = remain; std::memcpy(data, &input[Pos], size); Pos += size; if(processedSize != NULL) *processedSize = size; return S_OK; } class COutStreamRam: public ISequentialOutStream, public CMyUnknownImp { std::vector result; size_t Pos; public: MY_UNKNOWN_IMP COutStreamRam(): result(), Pos(0) { } virtual ~COutStreamRam() { } void Reserve(unsigned n) { result.reserve(n); } const std::vector& Get() const { return result; } HRESULT WriteByte(Byte b) { if(Pos >= result.size()) result.resize(Pos+1); result[Pos++] = b; return S_OK; } STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize); }; STDMETHODIMP COutStreamRam::Write(const void *data, UInt32 size, UInt32 *processedSize) { if(Pos+size > result.size()) result.resize(Pos+size); std::memcpy(&result[Pos], data, size); if(processedSize != NULL) *processedSize = size; Pos += size; return S_OK; } const std::vector LZMACompress(const std::vector& buf) { if(buf.empty()) return buf; const UInt32 dictionarysize = SelectDictionarySizeFor(buf.size()); NCompress::NLZMA::CEncoder *encoderSpec = new NCompress::NLZMA::CEncoder; CMyComPtr encoder = encoderSpec; const PROPID propIDs[] = { NCoderPropID::kAlgorithm, NCoderPropID::kDictionarySize, NCoderPropID::kNumFastBytes, }; const unsigned kNumProps = sizeof(propIDs) / sizeof(propIDs[0]); PROPVARIANT properties[kNumProps]; properties[0].vt = VT_UI4; properties[0].ulVal = (UInt32)2; properties[1].vt = VT_UI4; properties[1].ulVal = (UInt32)dictionarysize; properties[2].vt = VT_UI4; properties[2].ulVal = (UInt32)64; if (encoderSpec->SetCoderProperties(propIDs, properties, kNumProps) != S_OK) { Error: return std::vector (); } COutStreamRam *const outStreamSpec = new COutStreamRam; CMyComPtr outStream = outStreamSpec; CInStreamRam *const inStreamSpec = new CInStreamRam(buf); CMyComPtr inStream = inStreamSpec; outStreamSpec->Reserve(buf.size()); if (encoderSpec->WriteCoderProperties(outStream) != S_OK) goto Error; for (unsigned i = 0; i < 8; i++) { UInt64 t = (UInt64)buf.size(); outStreamSpec->WriteByte((Byte)((t) >> (8 * i))); } HRESULT lzmaResult = encoder->Code(inStream, outStream, 0, 0, 0); if (lzmaResult != S_OK) goto Error; return outStreamSpec->Get(); } #undef RC_NORMALIZE #include "C/7zip/Decompress/LzmaDecode.h" #include "C/7zip/Decompress/LzmaDecode.c" const std::vector LZMADeCompress (const std::vector& buf) { if(buf.size() <= 5+8) return std::vector (); uint_least64_t out_sizemax = R64(&buf[5]); std::vector result(out_sizemax); CLzmaDecoderState state; LzmaDecodeProperties(&state.Properties, &buf[0], LZMA_PROPERTIES_SIZE); state.Probs = new CProb[LzmaGetNumProbs(&state.Properties)]; SizeT in_done; SizeT out_done; LzmaDecode(&state, &buf[13], buf.size()-13, &in_done, &result[0], result.size(), &out_done); delete[] state.Probs; result.resize(out_done); return result; } #ifndef COMPACT int main(int argc, char *argv[]) { char *s; FILE *f, *infile, *outfile; int c; if (argc != 4) { std::fprintf(stderr, "'lzma e file1 file2' encodes file1 into file2.\n" "'lzma d file2 file1' decodes file2 into file1.\n"); return EXIT_FAILURE; } if (argc == 4) { if ((s = argv[1], s[1] || strpbrk(s, "DEde") == NULL) || (s = argv[2], (infile = fopen(s, "rb")) == NULL) || (s = argv[3], (outfile = fopen(s, "wb")) == NULL)) { std::fprintf(stderr, "??? %s\n", s); return EXIT_FAILURE; } } struct stat fs; int si; if (fstat(fileno(infile), &fs)) { std::perror(strerror(errno)); return EXIT_FAILURE; } si=fs.st_size; char *Buf=(char *)malloc(si); fread(Buf,si, 1, infile); std::vector result; if (toupper(*argv[1]) == 'E') result = LZMACompress(std::vector(Buf,Buf+si)); else result = LZMADeCompress(std::vector(Buf,Buf+si)); fwrite(&result[0], result.size(), 1, outfile); fclose(infile); fclose(outfile); return EXIT_SUCCESS; } #else 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 result; result = LZMACompress(std::vector(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 result; result = LZMADeCompress(std::vector(src, src + src_len)); if (result.size() <= dst_len) std::memcpy(dst, &result[0], result.size()); else { fprintf(stderr, "Not copying %d bytes to %d-byte buffer!\n", result.size(), dst_len); exit(1); } } } #endif