Excise the ill-advised RLCOMP compression algorithm and simply leave the
[oota-llvm.git] / lib / Support / Compressor.cpp
1 //===- lib/Support/Compressor.cpp -------------------------------*- C++ -*-===//
2 // 
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file was developed by Reid Spencer and is distributed under the 
6 // University of Illinois Open Source License. See LICENSE.TXT for details.
7 // 
8 //===----------------------------------------------------------------------===//
9 //
10 // This file implements the llvm::Compressor class, an abstraction for memory
11 // block compression.
12 //
13 //===----------------------------------------------------------------------===//
14
15 #include "llvm/Config/config.h"
16 #include "llvm/Support/Compressor.h"
17 #include "llvm/ADT/StringExtras.h"
18 #include <cassert>
19 #include <string>
20
21 #ifdef HAVE_BZIP2
22 #include <bzlib.h>
23 #endif
24
25 #ifdef HAVE_ZLIB
26 #include <zlib.h>
27 #endif
28
29 namespace {
30
31 inline int getdata(char*& buffer, unsigned& size, 
32                    llvm::Compressor::OutputDataCallback* cb, void* context) {
33   buffer = 0;
34   size = 0;
35   int result = (*cb)(buffer, size, context);
36   assert(buffer != 0 && "Invalid result from Compressor callback");
37   assert(size != 0 && "Invalid result from Compressor callback");
38   return result;
39 }
40
41 //===----------------------------------------------------------------------===//
42 //=== NULLCOMP - a compression like set of routines that just copies data 
43 //===            without doing any compression. This is provided so that if the
44 //===            configured environment doesn't have a compression library the
45 //===            program can still work, albeit using more data/memory.
46 //===----------------------------------------------------------------------===//
47
48 struct NULLCOMP_stream {
49   // User provided fields
50   char* next_in;
51   unsigned avail_in;
52   char* next_out;
53   unsigned avail_out;
54
55   // Information fields
56   uint64_t output_count; // Total count of output bytes
57 };
58
59 void NULLCOMP_init(NULLCOMP_stream* s) {
60   s->output_count = 0;
61 }
62
63 bool NULLCOMP_compress(NULLCOMP_stream* s) {
64   assert(s && "Invalid NULLCOMP_stream");
65   assert(s->next_in != 0);
66   assert(s->next_out != 0);
67   assert(s->avail_in >= 1);
68   assert(s->avail_out >= 1);
69
70   if (s->avail_out >= s->avail_in) {
71     ::memcpy(s->next_out, s->next_in, s->avail_in);
72     s->output_count += s->avail_in;
73     s->avail_out -= s->avail_in;
74     s->next_in += s->avail_in;
75     s->avail_in = 0;
76     return true;
77   } else {
78     ::memcpy(s->next_out, s->next_in, s->avail_out);
79     s->output_count += s->avail_out;
80     s->avail_in -= s->avail_out;
81     s->next_in += s->avail_out;
82     s->avail_out = 0;
83     return false;
84   }
85 }
86
87 bool NULLCOMP_decompress(NULLCOMP_stream* s) {
88   assert(s && "Invalid NULLCOMP_stream");
89   assert(s->next_in != 0);
90   assert(s->next_out != 0);
91   assert(s->avail_in >= 1);
92   assert(s->avail_out >= 1);
93
94   if (s->avail_out >= s->avail_in) {
95     ::memcpy(s->next_out, s->next_in, s->avail_in);
96     s->output_count += s->avail_in;
97     s->avail_out -= s->avail_in;
98     s->next_in += s->avail_in;
99     s->avail_in = 0;
100     return true;
101   } else {
102     ::memcpy(s->next_out, s->next_in, s->avail_out);
103     s->output_count += s->avail_out;
104     s->avail_in -= s->avail_out;
105     s->next_in += s->avail_out;
106     s->avail_out = 0;
107     return false;
108   }
109 }
110
111 void NULLCOMP_end(NULLCOMP_stream* strm) {
112 }
113
114 }
115
116 namespace llvm {
117
118 // Compress in one of three ways
119 uint64_t Compressor::compress(char* in, unsigned size, OutputDataCallback* cb, 
120                               Algorithm hint, void* context ) {
121   assert(in && "Can't compress null buffer");
122   assert(size && "Can't compress empty buffer");
123   assert(cb && "Can't compress without a callback function");
124
125   uint64_t result = 0;
126
127   switch (hint) {
128     case COMP_TYPE_BZIP2: {
129 #if defined(HAVE_BZIP2)
130       // Set up the bz_stream
131       bz_stream bzdata;
132       bzdata.bzalloc = 0;
133       bzdata.bzfree = 0;
134       bzdata.opaque = 0;
135       bzdata.next_in = in;
136       bzdata.avail_in = size;
137       bzdata.next_out = 0;
138       bzdata.avail_out = 0;
139       switch ( BZ2_bzCompressInit(&bzdata, 9, 0, 100) ) {
140         case BZ_CONFIG_ERROR: throw std::string("bzip2 library mis-compiled");
141         case BZ_PARAM_ERROR:  throw std::string("Compressor internal error");
142         case BZ_MEM_ERROR:    throw std::string("Out of memory");
143         case BZ_OK:
144         default:
145           break;
146       }
147
148       // Get a block of memory
149       if (0 != getdata(bzdata.next_out, bzdata.avail_out,cb,context)) {
150         BZ2_bzCompressEnd(&bzdata);
151         throw std::string("Can't allocate output buffer");
152       }
153
154       // Put compression code in first byte
155       (*bzdata.next_out++) = COMP_TYPE_BZIP2;
156       bzdata.avail_out--;
157
158       // Compress it
159       int bzerr = BZ_FINISH_OK;
160       while (BZ_FINISH_OK == (bzerr = BZ2_bzCompress(&bzdata, BZ_FINISH))) {
161         if (0 != getdata(bzdata.next_out, bzdata.avail_out,cb,context)) {
162           BZ2_bzCompressEnd(&bzdata);
163           throw std::string("Can't allocate output buffer");
164         }
165       }
166       switch (bzerr) {
167         case BZ_SEQUENCE_ERROR:
168         case BZ_PARAM_ERROR: throw std::string("Param/Sequence error");
169         case BZ_FINISH_OK:
170         case BZ_STREAM_END: break;
171         default: throw std::string("Oops: ") + utostr(unsigned(bzerr));
172       }
173
174       // Finish
175       result = (static_cast<uint64_t>(bzdata.total_out_hi32) << 32) |
176           bzdata.total_out_lo32 + 1;
177
178       BZ2_bzCompressEnd(&bzdata);
179       break;
180 #else
181       // FALL THROUGH
182 #endif
183     }
184
185     case COMP_TYPE_ZLIB: {
186 #if defined(HAVE_ZLIB)
187       z_stream zdata;
188       zdata.zalloc = Z_NULL;
189       zdata.zfree = Z_NULL;
190       zdata.opaque = Z_NULL;
191       zdata.next_in = reinterpret_cast<Bytef*>(in);
192       zdata.avail_in = size;
193       if (Z_OK != deflateInit(&zdata,Z_BEST_COMPRESSION))
194         throw std::string(zdata.msg ? zdata.msg : "zlib error");
195
196       if (0 != getdata((char*&)(zdata.next_out), zdata.avail_out,cb,context)) {
197         deflateEnd(&zdata);
198         throw std::string("Can't allocate output buffer");
199       }
200
201       (*zdata.next_out++) = COMP_TYPE_ZLIB;
202       zdata.avail_out--;
203
204       int flush = 0;
205       while ( Z_OK == deflate(&zdata,0) && zdata.avail_out == 0) {
206         if (0 != getdata((char*&)zdata.next_out, zdata.avail_out, cb,context)) {
207           deflateEnd(&zdata);
208           throw std::string("Can't allocate output buffer");
209         }
210       }
211
212       while ( Z_STREAM_END != deflate(&zdata, Z_FINISH)) {
213         if (0 != getdata((char*&)zdata.next_out, zdata.avail_out, cb,context)) {
214           deflateEnd(&zdata);
215           throw std::string("Can't allocate output buffer");
216         }
217       }
218
219       result = static_cast<uint64_t>(zdata.total_out) + 1;
220       deflateEnd(&zdata);
221       break;
222
223 #else
224     // FALL THROUGH
225 #endif
226     }
227
228     case COMP_TYPE_SIMPLE: {
229       NULLCOMP_stream sdata;
230       sdata.next_in = in;
231       sdata.avail_in = size;
232       NULLCOMP_init(&sdata);
233
234       if (0 != getdata(sdata.next_out, sdata.avail_out,cb,context)) {
235         throw std::string("Can't allocate output buffer");
236       }
237
238       *(sdata.next_out++) = COMP_TYPE_SIMPLE;
239       sdata.avail_out--;
240
241       while (!NULLCOMP_compress(&sdata)) {
242         if (0 != getdata(sdata.next_out, sdata.avail_out,cb,context)) {
243           throw std::string("Can't allocate output buffer");
244         }
245       }
246
247       result = sdata.output_count + 1;
248       NULLCOMP_end(&sdata);
249       break;
250     }
251     default:
252       throw std::string("Invalid compression type hint");
253   }
254   return result;
255 }
256
257 // Decompress in one of three ways
258 uint64_t Compressor::decompress(char *in, unsigned size, 
259                                 OutputDataCallback* cb, void* context) {
260   assert(in && "Can't decompress null buffer");
261   assert(size > 1 && "Can't decompress empty buffer");
262   assert(cb && "Can't decompress without a callback function");
263
264   uint64_t result = 0;
265
266   switch (*in++) {
267     case COMP_TYPE_BZIP2: {
268 #if !defined(HAVE_BZIP2)
269       throw std::string("Can't decompress BZIP2 data");
270 #else
271       // Set up the bz_stream
272       bz_stream bzdata;
273       bzdata.bzalloc = 0;
274       bzdata.bzfree = 0;
275       bzdata.opaque = 0;
276       bzdata.next_in = in;
277       bzdata.avail_in = size - 1;
278       bzdata.next_out = 0;
279       bzdata.avail_out = 0;
280       switch ( BZ2_bzDecompressInit(&bzdata, 0, 0) ) {
281         case BZ_CONFIG_ERROR: throw std::string("bzip2 library mis-compiled");
282         case BZ_PARAM_ERROR:  throw std::string("Compressor internal error");
283         case BZ_MEM_ERROR:    throw std::string("Out of memory");
284         case BZ_OK:
285         default:
286           break;
287       }
288
289       // Get a block of memory
290       if (0 != getdata(bzdata.next_out, bzdata.avail_out,cb,context)) {
291         BZ2_bzDecompressEnd(&bzdata);
292         throw std::string("Can't allocate output buffer");
293       }
294
295       // Decompress it
296       int bzerr = BZ_OK;
297       while (BZ_OK == (bzerr = BZ2_bzDecompress(&bzdata))) {
298         if (0 != getdata(bzdata.next_out, bzdata.avail_out,cb,context)) {
299           BZ2_bzDecompressEnd(&bzdata);
300           throw std::string("Can't allocate output buffer");
301         }
302       }
303
304       switch (bzerr) {
305         case BZ_PARAM_ERROR:  throw std::string("Compressor internal error");
306         case BZ_MEM_ERROR:    throw std::string("Out of memory");
307         case BZ_DATA_ERROR:   throw std::string("Data integrity error");
308         case BZ_DATA_ERROR_MAGIC:throw std::string("Data is not BZIP2");
309         default: throw("Ooops");
310         case BZ_STREAM_END:
311           break;
312       }
313
314       // Finish
315       result = (static_cast<uint64_t>(bzdata.total_out_hi32) << 32) |
316         bzdata.total_out_lo32;
317       BZ2_bzDecompressEnd(&bzdata);
318       break;
319 #endif
320     }
321
322     case COMP_TYPE_ZLIB: {
323 #if !defined(HAVE_ZLIB)
324       throw std::string("Can't decompress ZLIB data");
325 #else
326       z_stream zdata;
327       zdata.zalloc = Z_NULL;
328       zdata.zfree = Z_NULL;
329       zdata.opaque = Z_NULL;
330       zdata.next_in = reinterpret_cast<Bytef*>(in);
331       zdata.avail_in = size - 1;
332       if ( Z_OK != inflateInit(&zdata))
333         throw std::string(zdata.msg ? zdata.msg : "zlib error");
334
335       if (0 != getdata((char*&)zdata.next_out, zdata.avail_out,cb,context)) {
336         inflateEnd(&zdata);
337         throw std::string("Can't allocate output buffer");
338       }
339
340       int zerr = Z_OK;
341       while (Z_OK == (zerr = inflate(&zdata,0))) {
342         if (0 != getdata((char*&)zdata.next_out, zdata.avail_out,cb,context)) {
343           inflateEnd(&zdata);
344           throw std::string("Can't allocate output buffer");
345         }
346       }
347
348       if (zerr != Z_STREAM_END)
349         throw std::string(zdata.msg?zdata.msg:"zlib error");
350
351       result = static_cast<uint64_t>(zdata.total_out);
352       inflateEnd(&zdata);
353       break;
354 #endif
355     }
356
357     case COMP_TYPE_SIMPLE: {
358       NULLCOMP_stream sdata;
359       sdata.next_in = in;
360       sdata.avail_in = size - 1;
361       NULLCOMP_init(&sdata);
362
363       if (0 != getdata(sdata.next_out, sdata.avail_out,cb,context)) {
364         throw std::string("Can't allocate output buffer");
365       }
366
367       while (!NULLCOMP_decompress(&sdata)) {
368         if (0 != getdata(sdata.next_out, sdata.avail_out,cb,context)) {
369           throw std::string("Can't allocate output buffer");
370         }
371       }
372
373       result = sdata.output_count;
374       NULLCOMP_end(&sdata);
375       break;
376     }
377
378     default:
379       throw std::string("Unknown type of compressed data");
380   }
381
382   return result;
383 }
384
385 }
386
387 // vim: sw=2 ai