1 | /* |
---|
2 | * ====================================================================== |
---|
3 | * Rappture::Buffer |
---|
4 | * |
---|
5 | * AUTHOR: Derrick Kearney, Purdue University |
---|
6 | * |
---|
7 | * Copyright (c) 2004-2008 Purdue Research Foundation |
---|
8 | * ---------------------------------------------------------------------- |
---|
9 | * See the file "license.terms" for information on usage and |
---|
10 | * redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES. |
---|
11 | * ====================================================================== |
---|
12 | */ |
---|
13 | |
---|
14 | #include <assert.h> |
---|
15 | #include <errno.h> |
---|
16 | #include <stdio.h> |
---|
17 | #include <sys/types.h> |
---|
18 | #include <sys/stat.h> |
---|
19 | #include <unistd.h> |
---|
20 | #include <fstream> |
---|
21 | #include <zlib.h> |
---|
22 | #include "b64/encode.h" |
---|
23 | #include "b64/decode.h" |
---|
24 | #include "RpBuffer.h" |
---|
25 | #include "RpEncode.h" |
---|
26 | |
---|
27 | namespace Rappture { |
---|
28 | |
---|
29 | /** |
---|
30 | * Construct an empty Buffer. |
---|
31 | */ |
---|
32 | Buffer::Buffer() |
---|
33 | : SimpleCharBuffer(), |
---|
34 | _level(6), |
---|
35 | _compressionType(RPCOMPRESS_GZIP), |
---|
36 | _windowBits(15) |
---|
37 | {} |
---|
38 | |
---|
39 | |
---|
40 | /** |
---|
41 | * Construct an empty Buffer of specified size. |
---|
42 | */ |
---|
43 | Buffer::Buffer(int nbytes) |
---|
44 | : SimpleCharBuffer(nbytes), |
---|
45 | _level(6), |
---|
46 | _compressionType(RPCOMPRESS_GZIP), |
---|
47 | _windowBits(15) |
---|
48 | {} |
---|
49 | |
---|
50 | |
---|
51 | /** |
---|
52 | * Construct a Buffer loaded with initial data. |
---|
53 | * |
---|
54 | * @param bytes pointer to bytes being stored. |
---|
55 | * @param nbytes number of bytes being stored. |
---|
56 | */ |
---|
57 | Buffer::Buffer(const char* bytes, int nbytes) |
---|
58 | : SimpleCharBuffer(bytes,nbytes), |
---|
59 | _level(6), |
---|
60 | _compressionType(RPCOMPRESS_GZIP), |
---|
61 | _windowBits(15) |
---|
62 | {} |
---|
63 | |
---|
64 | /** |
---|
65 | * Copy constructor |
---|
66 | * @param Buffer object to copy |
---|
67 | */ |
---|
68 | Buffer::Buffer(const Buffer& b) |
---|
69 | : SimpleCharBuffer(b), |
---|
70 | _level(b._level), |
---|
71 | _compressionType(b._compressionType), |
---|
72 | _windowBits(b._windowBits) |
---|
73 | {} |
---|
74 | |
---|
75 | /** |
---|
76 | * Assignment operator |
---|
77 | * @param Buffer object to copy |
---|
78 | */ |
---|
79 | Buffer& |
---|
80 | Buffer::operator=(const Buffer& b) |
---|
81 | { |
---|
82 | SimpleCharBuffer::operator=(b); |
---|
83 | |
---|
84 | _level = b._level; |
---|
85 | _compressionType = b._compressionType; |
---|
86 | _windowBits = b._windowBits; |
---|
87 | |
---|
88 | return *this; |
---|
89 | } |
---|
90 | |
---|
91 | |
---|
92 | Buffer |
---|
93 | Buffer::operator+(const Buffer& b) const |
---|
94 | { |
---|
95 | Buffer newBuffer(*this); |
---|
96 | newBuffer.operator+=(b); |
---|
97 | return newBuffer; |
---|
98 | } |
---|
99 | |
---|
100 | |
---|
101 | Buffer& |
---|
102 | Buffer::operator+=(const Buffer& b) |
---|
103 | { |
---|
104 | SimpleCharBuffer::operator+=(b); |
---|
105 | return *this; |
---|
106 | } |
---|
107 | |
---|
108 | |
---|
109 | Buffer::~Buffer() |
---|
110 | {} |
---|
111 | |
---|
112 | |
---|
113 | bool |
---|
114 | Buffer::load (Outcome &status, const char* filePath) |
---|
115 | { |
---|
116 | status.addContext("Rappture::Buffer::load()"); |
---|
117 | |
---|
118 | FILE *f; |
---|
119 | f = fopen(filePath, "rb"); |
---|
120 | if (f == NULL) { |
---|
121 | status.addError("can't open \"%s\": %s", filePath, strerror(errno)); |
---|
122 | return false; |
---|
123 | } |
---|
124 | struct stat stat; |
---|
125 | if (fstat(fileno(f), &stat) < 0) { |
---|
126 | status.addError("can't stat \"%s\": %s", filePath, strerror(errno)); |
---|
127 | return false; |
---|
128 | } |
---|
129 | off_t size; |
---|
130 | size = stat.st_size; |
---|
131 | char* memblock; |
---|
132 | memblock = new char [size]; |
---|
133 | if (memblock == NULL) { |
---|
134 | status.addError("can't allocate %d bytes for file \"%s\": %s", |
---|
135 | size, filePath, strerror(errno)); |
---|
136 | fclose(f); |
---|
137 | return false; |
---|
138 | } |
---|
139 | |
---|
140 | // FIXME: better yet, create an "extend" method in the buffer and returns |
---|
141 | // the address of the char buffer so I can read the data directly |
---|
142 | // into the buffer. This eliminates memory new/copy/delete ops. |
---|
143 | |
---|
144 | size_t nRead; |
---|
145 | nRead = fread(memblock, sizeof(char), size, f); |
---|
146 | fclose(f); // Close the file. |
---|
147 | |
---|
148 | if (nRead != (size_t)size) { |
---|
149 | status.addError("can't read %d bytes from \"%s\": %s", size, filePath, |
---|
150 | strerror(errno)); |
---|
151 | return false; |
---|
152 | } |
---|
153 | |
---|
154 | int nBytes; |
---|
155 | nBytes = append(memblock, size); |
---|
156 | delete [] memblock; |
---|
157 | |
---|
158 | if (nBytes != size) { |
---|
159 | status.addError("can't append %d bytes from \"%s\" to buffer: %s", |
---|
160 | size, filePath, strerror(errno)); |
---|
161 | return false; |
---|
162 | } |
---|
163 | return true; |
---|
164 | } |
---|
165 | |
---|
166 | |
---|
167 | bool |
---|
168 | Buffer::dump (Outcome &status, const char* filePath) |
---|
169 | { |
---|
170 | status.addContext("Rappture::Buffer::dump()"); |
---|
171 | |
---|
172 | FILE *f; |
---|
173 | f = fopen(filePath, "wb"); |
---|
174 | if (f != NULL) { |
---|
175 | status.addError("can't open \"%s\": %s\n", filePath, strerror(errno)); |
---|
176 | return false; |
---|
177 | } |
---|
178 | ssize_t nWritten; |
---|
179 | nWritten = fwrite(bytes(), size(), sizeof(char), f); |
---|
180 | fclose(f); // Close the file. |
---|
181 | |
---|
182 | if (nWritten != (ssize_t)size()) { |
---|
183 | status.addError("can't write %d bytes to \"%s\": %s\n", size(), |
---|
184 | filePath, strerror(errno)); |
---|
185 | return false; |
---|
186 | } |
---|
187 | return true; |
---|
188 | } |
---|
189 | |
---|
190 | |
---|
191 | bool |
---|
192 | Buffer::encode(Outcome &status, unsigned int flags) |
---|
193 | { |
---|
194 | SimpleCharBuffer bout; |
---|
195 | |
---|
196 | rewind(); |
---|
197 | |
---|
198 | switch (flags & (RPENC_Z | RPENC_B64)) { |
---|
199 | case 0: |
---|
200 | break; |
---|
201 | |
---|
202 | case RPENC_Z: // Compress only |
---|
203 | if (!do_compress(status, *this, bout)) { |
---|
204 | return false; |
---|
205 | } |
---|
206 | move(bout); |
---|
207 | break; |
---|
208 | |
---|
209 | case RPENC_B64: // Encode only |
---|
210 | if (!do_base64_enc(status, *this, bout)) { |
---|
211 | return false; |
---|
212 | } |
---|
213 | move(bout); |
---|
214 | break; |
---|
215 | |
---|
216 | case (RPENC_B64 | RPENC_Z): |
---|
217 | |
---|
218 | // It's always compress then encode |
---|
219 | if (!do_compress(status, *this, bout)) { |
---|
220 | return false; |
---|
221 | } |
---|
222 | if (!do_base64_enc(status, bout, *this)) { |
---|
223 | return false; |
---|
224 | } |
---|
225 | break; |
---|
226 | } |
---|
227 | return true; |
---|
228 | } |
---|
229 | |
---|
230 | |
---|
231 | bool |
---|
232 | Buffer::decode(Outcome &status, unsigned int flags) |
---|
233 | { |
---|
234 | SimpleCharBuffer bout; |
---|
235 | |
---|
236 | rewind(); |
---|
237 | |
---|
238 | switch (flags & (RPENC_Z | RPENC_B64)) { |
---|
239 | case 0: |
---|
240 | if (encoding::isBase64(bytes(), size())) { |
---|
241 | if (!do_base64_dec(status, *this, bout)) { |
---|
242 | return false; |
---|
243 | } |
---|
244 | move(bout); |
---|
245 | } |
---|
246 | bout.clear(); |
---|
247 | if (encoding::isBinary(bytes(), size())) { |
---|
248 | if (!do_decompress(status, *this, bout)) { |
---|
249 | return false; |
---|
250 | } |
---|
251 | move(bout); |
---|
252 | } |
---|
253 | break; |
---|
254 | |
---|
255 | case RPENC_Z: // Decompress only |
---|
256 | if (!do_decompress(status, *this, bout)) { |
---|
257 | return false; |
---|
258 | } |
---|
259 | move(bout); |
---|
260 | break; |
---|
261 | |
---|
262 | case RPENC_B64: // Decode only |
---|
263 | if (!do_base64_dec(status, *this, bout)) { |
---|
264 | return false; |
---|
265 | } |
---|
266 | move(bout); |
---|
267 | break; |
---|
268 | |
---|
269 | case (RPENC_B64 | RPENC_Z): |
---|
270 | |
---|
271 | // It's always decode then decompress |
---|
272 | if (!do_base64_dec(status, *this, bout)) { |
---|
273 | return false; |
---|
274 | } |
---|
275 | clear(); |
---|
276 | if (!do_decompress(status, bout, *this)) { |
---|
277 | return false; |
---|
278 | } |
---|
279 | break; |
---|
280 | } |
---|
281 | return true; |
---|
282 | } |
---|
283 | |
---|
284 | |
---|
285 | bool |
---|
286 | Buffer::do_compress(Outcome& status, SimpleCharBuffer& bin, |
---|
287 | SimpleCharBuffer& bout) |
---|
288 | { |
---|
289 | int ret=0, flush=0; |
---|
290 | unsigned have=0; |
---|
291 | z_stream strm; |
---|
292 | |
---|
293 | char in[CHUNK]; |
---|
294 | char out[CHUNK]; |
---|
295 | |
---|
296 | int bytesWritten = 0; |
---|
297 | |
---|
298 | /* allocate deflate state */ |
---|
299 | strm.zalloc = Z_NULL; |
---|
300 | strm.zfree = Z_NULL; |
---|
301 | strm.opaque = Z_NULL; |
---|
302 | |
---|
303 | status.addContext("Rappture::Buffer::do_compress()"); |
---|
304 | |
---|
305 | ret = deflateInit2( &strm, _level, Z_DEFLATED, |
---|
306 | _windowBits+_compressionType, |
---|
307 | 8, Z_DEFAULT_STRATEGY); |
---|
308 | |
---|
309 | if (ret != Z_OK) { |
---|
310 | status.addError("error while initializing zlib stream object"); |
---|
311 | return false; |
---|
312 | } |
---|
313 | |
---|
314 | /* compress until end of file */ |
---|
315 | do { |
---|
316 | strm.avail_in = bin.read(in, CHUNK); |
---|
317 | if (bin.bad() == true) { |
---|
318 | (void)deflateEnd(&strm); |
---|
319 | // return Z_ERRNO; |
---|
320 | status.addError("error while compressing"); |
---|
321 | return false; |
---|
322 | } |
---|
323 | flush = bin.eof() ? Z_FINISH : Z_NO_FLUSH; |
---|
324 | strm.next_in = (Bytef*) in; |
---|
325 | /* run deflate() on input until output buffer not full, finish |
---|
326 | compression if all of source has been read in */ |
---|
327 | do { |
---|
328 | strm.avail_out = CHUNK; |
---|
329 | strm.next_out = (Bytef*) out; |
---|
330 | ret = deflate(&strm, flush); /* no bad return value */ |
---|
331 | assert(ret != Z_STREAM_ERROR); /* state not clobbered */ |
---|
332 | have = CHUNK - strm.avail_out; |
---|
333 | /* write to file and check for error */ |
---|
334 | bytesWritten = bout.append(out, have); |
---|
335 | if ( ( (unsigned) bytesWritten != have ) ) { |
---|
336 | (void)deflateEnd(&strm); |
---|
337 | bout.clear(); |
---|
338 | // return Z_ERRNO; |
---|
339 | status.addError("error writing compressed data to temp buffer"); |
---|
340 | return false; |
---|
341 | } |
---|
342 | |
---|
343 | } while (strm.avail_out == 0); |
---|
344 | assert(strm.avail_in == 0); /* all input will be used */ |
---|
345 | |
---|
346 | /* done when last data in file processed */ |
---|
347 | } while (flush != Z_FINISH); |
---|
348 | |
---|
349 | assert(ret == Z_STREAM_END); /* stream will be complete */ |
---|
350 | |
---|
351 | /* clean up and return */ |
---|
352 | (void)deflateEnd(&strm); |
---|
353 | // return Z_OK; |
---|
354 | return true; |
---|
355 | } |
---|
356 | |
---|
357 | bool |
---|
358 | Buffer::do_decompress(Outcome& status, SimpleCharBuffer& bin, |
---|
359 | SimpleCharBuffer& bout) |
---|
360 | { |
---|
361 | int ret; |
---|
362 | unsigned have; |
---|
363 | z_stream strm; |
---|
364 | |
---|
365 | char in[CHUNK]; |
---|
366 | char out[CHUNK]; |
---|
367 | |
---|
368 | int bytesWritten = 0; |
---|
369 | |
---|
370 | status.addContext("Rappture::Buffer::do_decompress()"); |
---|
371 | |
---|
372 | /* allocate inflate state */ |
---|
373 | strm.zalloc = Z_NULL; |
---|
374 | strm.zfree = Z_NULL; |
---|
375 | strm.opaque = Z_NULL; |
---|
376 | strm.avail_in = 0; |
---|
377 | strm.next_in = Z_NULL; |
---|
378 | ret = inflateInit2(&strm,_windowBits+_compressionType); |
---|
379 | if (ret != Z_OK) { |
---|
380 | status.addError("error while initializing zlib stream object"); |
---|
381 | return false; |
---|
382 | } |
---|
383 | |
---|
384 | /* decompress until deflate stream ends or end of file */ |
---|
385 | do { |
---|
386 | strm.avail_in = bin.read(in, CHUNK); |
---|
387 | if (bin.bad() == true) { |
---|
388 | (void)inflateEnd(&strm); |
---|
389 | // return Z_ERRNO; |
---|
390 | status.addError("error while compressing"); |
---|
391 | return false; |
---|
392 | } |
---|
393 | if (strm.avail_in == 0) |
---|
394 | break; |
---|
395 | strm.next_in = (unsigned char*) in; |
---|
396 | /* run inflate() on input until output buffer not full */ |
---|
397 | do { |
---|
398 | strm.avail_out = CHUNK; |
---|
399 | strm.next_out = (unsigned char*) out; |
---|
400 | ret = inflate(&strm, Z_NO_FLUSH); |
---|
401 | assert(ret != Z_STREAM_ERROR); /* state not clobbered */ |
---|
402 | switch (ret) { |
---|
403 | case Z_NEED_DICT: |
---|
404 | ret = Z_DATA_ERROR; /* and fall through */ |
---|
405 | case Z_DATA_ERROR: |
---|
406 | case Z_MEM_ERROR: |
---|
407 | (void)inflateEnd(&strm); |
---|
408 | bout.clear(); |
---|
409 | status.addError("memory error while inflating data"); |
---|
410 | return false; |
---|
411 | } |
---|
412 | have = CHUNK - strm.avail_out; |
---|
413 | bytesWritten = bout.append(out, have); |
---|
414 | if ( ( (unsigned) bytesWritten != have) ) { |
---|
415 | (void)inflateEnd(&strm); |
---|
416 | bout.clear(); |
---|
417 | // return Z_ERRNO; |
---|
418 | status.addError("error writing compressed data to temp buffer"); |
---|
419 | return false; |
---|
420 | } |
---|
421 | } while (strm.avail_out == 0); |
---|
422 | |
---|
423 | /* done when inflate() says it's done */ |
---|
424 | } while (ret != Z_STREAM_END); |
---|
425 | |
---|
426 | /* clean up and return */ |
---|
427 | (void)inflateEnd(&strm); |
---|
428 | // return ret == Z_STREAM_END ? Z_OK : Z_DATA_ERROR; |
---|
429 | return true; |
---|
430 | } |
---|
431 | |
---|
432 | |
---|
433 | bool |
---|
434 | Buffer::do_base64_enc(Outcome& status, const SimpleCharBuffer& bin, |
---|
435 | SimpleCharBuffer& bout ) |
---|
436 | { |
---|
437 | int tBufSize = 0; |
---|
438 | unsigned int tBufAvl = 2*bin.size(); |
---|
439 | char* tBuf = new char[tBufAvl]; |
---|
440 | |
---|
441 | base64::encoder E; |
---|
442 | |
---|
443 | tBufSize = E.encode(bin.bytes(),bin.size(),tBuf); |
---|
444 | tBufSize += E.encode_end(tBuf+tBufSize); |
---|
445 | |
---|
446 | bout = SimpleCharBuffer(tBuf,tBufSize); |
---|
447 | delete [] tBuf; |
---|
448 | |
---|
449 | return true; |
---|
450 | } |
---|
451 | |
---|
452 | |
---|
453 | bool |
---|
454 | Buffer::do_base64_dec(Outcome& status, const SimpleCharBuffer& bin, |
---|
455 | SimpleCharBuffer& bout ) |
---|
456 | { |
---|
457 | int tBufSize = 0; |
---|
458 | unsigned int tBufAvl = bin.size(); |
---|
459 | char* tBuf = new char[tBufAvl]; |
---|
460 | |
---|
461 | base64::decoder D; |
---|
462 | |
---|
463 | tBufSize = D.decode(bin.bytes(),bin.size(),tBuf); |
---|
464 | |
---|
465 | bout = SimpleCharBuffer(tBuf,tBufSize); |
---|
466 | delete [] tBuf; |
---|
467 | |
---|
468 | return true; |
---|
469 | } |
---|
470 | |
---|
471 | |
---|
472 | } |
---|
473 | |
---|
474 | |
---|
475 | |
---|