Since some people disapprove of white space cleanups mixed in regular commits
[coreboot.git] / util / cbfstool / lzma / minilzma.cc
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
41 const std::vector<unsigned char> LZMACompress
42     (const std::vector<unsigned char>& buf);
43
44 const std::vector<unsigned char> LZMADeCompress
45     (const std::vector<unsigned char>& buf);
46
47 static 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 }
52 static 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
60 static 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
68 static 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
102 class CInStreamRam: public ISequentialInStream, public CMyUnknownImp
103 {
104     const std::vector<unsigned char>& input;
105     size_t Pos;
106 public:
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
117 STDMETHODIMP 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
130 class COutStreamRam: public ISequentialOutStream, public CMyUnknownImp
131 {
132     std::vector<Byte> result;
133     size_t Pos;
134 public:
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
153 STDMETHODIMP 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
163 const 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
215 const 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
240 int 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
284 extern "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
295 void 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
303 void 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