1/* 2 * minimal lzma implementation 3 * 4 * Copyright (C) 2002 Eric Biederman 5 * Copyright (C) 2005 Joel Yliluoma 6 * Copyright (C) 2007 coresystems GmbH 7 * (Adapted by Stefan Reinauer <stepan@coresystems.de> for coresystems GmbH) 8 * Copyright (C) 2007 Patrick Georgi <patrick@georgi-clan.de> 9 * 10 * This program is free software; you can redistribute it and/or modify 11 * it under the terms of the GNU General Public License as published by 12 * the Free Software Foundation; either version 2 of the License, or 13 * (at your option) any later version. 14 * 15 * This program is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU General Public License for more details. 19 * 20 * You should have received a copy of the GNU General Public License 21 * along with this program; if not, write to the Free Software 22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA 23 */ 24 25#include "C/Common/MyInitGuid.h" 26#include "C/7zip/Compress/LZMA/LZMAEncoder.h" 27 28#include <sys/types.h> 29#include <sys/stat.h> 30#include <unistd.h> 31#include <errno.h> 32#include <cstring> 33#include <cstdio> 34#include <cstdlib> 35#include <cctype> 36 37#include <vector> 38#include <algorithm> 39#include <stdint.h> 40 41const std::vector<unsigned char> LZMACompress 42 (const std::vector<unsigned char>& buf); 43 44const std::vector<unsigned char> LZMADeCompress 45 (const std::vector<unsigned char>& buf); 46 47static inline uint16_t R16(const void* p) 48{ 49 const unsigned char* data = (const unsigned char*)p; 50 return (data[0] << 0) | (data[1] << 8); 51} 52static inline uint32_t R32(const void* p) 53{ 54 const unsigned char* data = (const unsigned char*)p; 55 return R16(data) | (R16(data+2) << 16); 56} 57 58#define L (uint64_t) 59 60static inline uint64_t R64(const void* p) 61{ 62 const unsigned char* data = (const unsigned char*)p; 63 return (L R32(data)) | ((L R32(data+4)) << 32); 64} 65 66#undef L 67 68static UInt32 SelectDictionarySizeFor(unsigned datasize) 69{ 70 #if 1 71 return datasize; 72 #else 73#ifdef __GNUC__ 74 /* gnu c can optimize this switch statement into a fast binary 75 * search, but it cannot do so for the list of the if statements. 76 */ 77 switch(datasize) 78 { 79 case 0 ... 512 : return 512; 80 case 513 ... 1024: return 2048; 81 case 1025 ... 4096: return 8192; 82 case 4097 ... 16384: return 32768; 83 case 16385 ... 65536: return 528288; 84 case 65537 ... 528288: return 1048576*4; 85 case 528289 ... 786432: return 1048576*16; 86 default: return 1048576*32; 87 } 88#else 89 if(datasize <= 512) return 512; 90 if(datasize <= 1024) return 1024; 91 if(datasize <= 4096) return 4096; 92 if(datasize <= 16384) return 32768; 93 if(datasize <= 65536) return 528288; 94 if(datasize <= 528288) return 1048576*4; 95 if(datasize <= 786432) reutrn 1048576*16; 96 return 32*1048576; 97#endif 98 #endif 99} 100 101 102class CInStreamRam: public ISequentialInStream, public CMyUnknownImp 103{ 104 const std::vector<unsigned char>& input; 105 size_t Pos; 106public: 107 MY_UNKNOWN_IMP 108 109 CInStreamRam(const std::vector<unsigned char>& buf) : input(buf), Pos(0) 110 { 111 } 112 virtual ~CInStreamRam() {} 113 114 STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); 115}; 116 117STDMETHODIMP CInStreamRam::Read(void *data, UInt32 size, UInt32 *processedSize) 118{ 119 UInt32 remain = input.size() - Pos; 120 if (size > remain) size = remain; 121 122 std::memcpy(data, &input[Pos], size); 123 Pos += size; 124 125 if(processedSize != NULL) *processedSize = size; 126 127 return S_OK; 128} 129 130class COutStreamRam: public ISequentialOutStream, public CMyUnknownImp 131{ 132 std::vector<Byte> result; 133 size_t Pos; 134public: 135 MY_UNKNOWN_IMP 136 137 COutStreamRam(): result(), Pos(0) { } 138 virtual ~COutStreamRam() { } 139 140 void Reserve(unsigned n) { result.reserve(n); } 141 const std::vector<Byte>& Get() const { return result; } 142 143 HRESULT WriteByte(Byte b) 144 { 145 if(Pos >= result.size()) result.resize(Pos+1); 146 result[Pos++] = b; 147 return S_OK; 148 } 149 150 STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize); 151}; 152 153STDMETHODIMP COutStreamRam::Write(const void *data, UInt32 size, UInt32 *processedSize) 154{ 155 if(Pos+size > result.size()) result.resize(Pos+size); 156 157 std::memcpy(&result[Pos], data, size); 158 if(processedSize != NULL) *processedSize = size; 159 Pos += size; 160 return S_OK; 161} 162 163const std::vector<unsigned char> LZMACompress(const std::vector<unsigned char>& buf) 164{ 165 if(buf.empty()) return buf; 166 167 const UInt32 dictionarysize = SelectDictionarySizeFor(buf.size()); 168 169 NCompress::NLZMA::CEncoder *encoderSpec = new NCompress::NLZMA::CEncoder; 170 CMyComPtr<ICompressCoder> encoder = encoderSpec; 171 const PROPID propIDs[] = 172 { 173 NCoderPropID::kAlgorithm, 174 NCoderPropID::kDictionarySize, 175 NCoderPropID::kNumFastBytes, 176 }; 177 const unsigned kNumProps = sizeof(propIDs) / sizeof(propIDs[0]); 178 PROPVARIANT properties[kNumProps]; 179 properties[0].vt = VT_UI4; properties[0].ulVal = (UInt32)2; 180 properties[1].vt = VT_UI4; properties[1].ulVal = (UInt32)dictionarysize; 181 properties[2].vt = VT_UI4; properties[2].ulVal = (UInt32)64; 182 183 if (encoderSpec->SetCoderProperties(propIDs, properties, kNumProps) != S_OK) 184 { 185 Error: 186 return std::vector<unsigned char> (); 187 } 188 189 COutStreamRam *const outStreamSpec = new COutStreamRam; 190 CMyComPtr<ISequentialOutStream> outStream = outStreamSpec; 191 CInStreamRam *const inStreamSpec = new CInStreamRam(buf); 192 CMyComPtr<ISequentialInStream> inStream = inStreamSpec; 193 194 outStreamSpec->Reserve(buf.size()); 195 196 if (encoderSpec->WriteCoderProperties(outStream) != S_OK) goto Error; 197 198 for (unsigned i = 0; i < 8; i++) 199 { 200 UInt64 t = (UInt64)buf.size(); 201 outStreamSpec->WriteByte((Byte)((t) >> (8 * i))); 202 } 203 204 HRESULT lzmaResult = encoder->Code(inStream, outStream, 0, 0, 0); 205 if (lzmaResult != S_OK) goto Error; 206 207 return outStreamSpec->Get(); 208} 209 210#undef RC_NORMALIZE 211 212#include "C/7zip/Decompress/LzmaDecode.h" 213#include "C/7zip/Decompress/LzmaDecode.c" 214 215const std::vector<unsigned char> LZMADeCompress 216 (const std::vector<unsigned char>& buf) 217{ 218 if(buf.size() <= 5+8) return std::vector<unsigned char> (); 219 220 uint_least64_t out_sizemax = R64(&buf[5]); 221 222 std::vector<unsigned char> result(out_sizemax); 223 224 CLzmaDecoderState state; 225 LzmaDecodeProperties(&state.Properties, &buf[0], LZMA_PROPERTIES_SIZE); 226 state.Probs = new CProb[LzmaGetNumProbs(&state.Properties)]; 227 228 SizeT in_done; 229 SizeT out_done; 230 LzmaDecode(&state, &buf[13], buf.size()-13, &in_done, 231 &result[0], result.size(), &out_done); 232 233 delete[] state.Probs; 234 235 result.resize(out_done); 236 return result; 237} 238 239#ifndef COMPACT 240int main(int argc, char *argv[]) 241{ 242 char *s; 243 FILE *f, *infile, *outfile; 244 int c; 245 246 if (argc != 4) { 247 std::fprintf(stderr, "'lzma e file1 file2' encodes file1 into file2.\n" 248 "'lzma d file2 file1' decodes file2 into file1.\n"); 249 250 return EXIT_FAILURE; 251 } 252 if (argc == 4) { 253 if ((s = argv[1], s[1] || strpbrk(s, "DEde") == NULL) 254 || (s = argv[2], (infile = fopen(s, "rb")) == NULL) 255 || (s = argv[3], (outfile = fopen(s, "wb")) == NULL)) { 256 std::fprintf(stderr, "??? %s\n", s); 257 return EXIT_FAILURE; 258 } 259 } 260 261 struct stat fs; 262 int si; 263 if (fstat(fileno(infile), &fs)) { 264 std::perror(strerror(errno)); 265 return EXIT_FAILURE; 266 } 267 si=fs.st_size; 268 269 char *Buf=(char *)malloc(si); 270 fread(Buf,si, 1, infile); 271 272 std::vector<unsigned char> result; 273 if (toupper(*argv[1]) == 'E') 274 result = LZMACompress(std::vector<unsigned char>(Buf,Buf+si)); 275 else 276 result = LZMADeCompress(std::vector<unsigned char>(Buf,Buf+si)); 277 278 fwrite(&result[0], result.size(), 1, outfile); 279 fclose(infile); 280 fclose(outfile); 281 return EXIT_SUCCESS; 282} 283#else 284extern "C" { 285 286/** 287 * Compress a buffer with lzma 288 * Don't copy the result back if it is too large. 289 * @param in a pointer to the buffer 290 * @param in_len the length in bytes 291 * @param out a pointer to a buffer of at least size in_len 292 * @param out_len a pointer to the compressed length of in 293 */ 294 295void do_lzma_compress(char *in, int in_len, char *out, int *out_len) { 296 std::vector<unsigned char> result; 297 result = LZMACompress(std::vector<unsigned char>(in, in + in_len)); 298 *out_len = result.size(); 299 if (*out_len < in_len) 300 std::memcpy(out, &result[0], *out_len); 301} 302 303void do_lzma_uncompress(char *dst, int dst_len, char *src, int src_len) { 304 std::vector<unsigned char> result; 305 result = LZMADeCompress(std::vector<unsigned char>(src, src + src_len)); 306 if (result.size() <= (SizeT)dst_len) 307 std::memcpy(dst, &result[0], result.size()); 308 else 309 { 310 fprintf(stderr, "Not copying %d bytes to %d-byte buffer!\n", 311 (unsigned int)result.size(), dst_len); 312 exit(1); 313 } 314} 315 316} 317#endif 318 319

