1 | /* |
---|
2 | * ====================================================================== |
---|
3 | * Rappture::Buffer |
---|
4 | * |
---|
5 | * AUTHOR: Derrick Kearney, Purdue University |
---|
6 | * |
---|
7 | * Copyright (c) 2004-2012 HUBzero Foundation, LLC |
---|
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 *path) |
---|
115 | { |
---|
116 | status.addContext("Rappture::Buffer::load()"); |
---|
117 | |
---|
118 | FILE *f; |
---|
119 | f = fopen(path, "rb"); |
---|
120 | if (f == NULL) { |
---|
121 | status.addError("can't open \"%s\": %s", path, strerror(errno)); |
---|
122 | return false; |
---|
123 | } |
---|
124 | |
---|
125 | struct stat stat; |
---|
126 | if (fstat(fileno(f), &stat) < 0) { |
---|
127 | status.addError("can't stat \"%s\": %s", path, strerror(errno)); |
---|
128 | return false; |
---|
129 | } |
---|
130 | |
---|
131 | size_t oldSize, numBytesRead; |
---|
132 | |
---|
133 | // Save the # of elements in the current buffer. |
---|
134 | oldSize = count(); |
---|
135 | |
---|
136 | // Extend the buffer to accomodate the file contents. |
---|
137 | if (extend(stat.st_size) == 0) { |
---|
138 | status.addError("can't allocate %d bytes for file \"%s\": %s", |
---|
139 | stat.st_size, path, strerror(errno)); |
---|
140 | fclose(f); |
---|
141 | return false; |
---|
142 | } |
---|
143 | // Read the file contents directly onto the end of the old buffer. |
---|
144 | numBytesRead = fread((char *)bytes() + oldSize, sizeof(char), |
---|
145 | stat.st_size, f); |
---|
146 | fclose(f); |
---|
147 | if (numBytesRead != (size_t)stat.st_size) { |
---|
148 | status.addError("can't read %ld bytes from \"%s\": %s", stat.st_size, |
---|
149 | path, strerror(errno)); |
---|
150 | return false; |
---|
151 | } |
---|
152 | // Reset the # of elements in the buffer to the new count. |
---|
153 | count(stat.st_size + oldSize); |
---|
154 | return true; |
---|
155 | } |
---|
156 | |
---|
157 | |
---|
158 | bool |
---|
159 | Buffer::dump (Outcome &status, const char* path) |
---|
160 | { |
---|
161 | status.addContext("Rappture::Buffer::dump()"); |
---|
162 | |
---|
163 | FILE *f; |
---|
164 | f = fopen(path, "wb"); |
---|
165 | if (f == NULL) { |
---|
166 | status.addError("can't open \"%s\": %s\n", path, strerror(errno)); |
---|
167 | return false; |
---|
168 | } |
---|
169 | ssize_t nWritten; |
---|
170 | nWritten = fwrite(bytes(), sizeof(char), size(), f); |
---|
171 | fclose(f); // Close the file. |
---|
172 | |
---|
173 | if (nWritten != (ssize_t)size()) { |
---|
174 | status.addError("can't write %d bytes to \"%s\": %s\n", size(), |
---|
175 | path, strerror(errno)); |
---|
176 | return false; |
---|
177 | } |
---|
178 | return true; |
---|
179 | } |
---|
180 | |
---|
181 | |
---|
182 | bool |
---|
183 | Buffer::encode(Outcome &status, unsigned int flags) |
---|
184 | { |
---|
185 | SimpleCharBuffer bout; |
---|
186 | |
---|
187 | rewind(); |
---|
188 | |
---|
189 | switch (flags & (RPENC_Z | RPENC_B64)) { |
---|
190 | case 0: |
---|
191 | break; |
---|
192 | |
---|
193 | case RPENC_Z: // Compress only |
---|
194 | if (!do_compress(status, *this, bout)) { |
---|
195 | return false; |
---|
196 | } |
---|
197 | move(bout); |
---|
198 | break; |
---|
199 | |
---|
200 | case RPENC_B64: // Encode only |
---|
201 | if (!do_base64_enc(status, *this, bout)) { |
---|
202 | return false; |
---|
203 | } |
---|
204 | move(bout); |
---|
205 | break; |
---|
206 | |
---|
207 | case (RPENC_B64 | RPENC_Z): |
---|
208 | |
---|
209 | // It's always compress then encode |
---|
210 | if (!do_compress(status, *this, bout)) { |
---|
211 | return false; |
---|
212 | } |
---|
213 | if (!do_base64_enc(status, bout, *this)) { |
---|
214 | return false; |
---|
215 | } |
---|
216 | break; |
---|
217 | } |
---|
218 | return true; |
---|
219 | } |
---|
220 | |
---|
221 | |
---|
222 | bool |
---|
223 | Buffer::decode(Outcome &status, unsigned int flags) |
---|
224 | { |
---|
225 | SimpleCharBuffer bout; |
---|
226 | |
---|
227 | rewind(); |
---|
228 | |
---|
229 | switch (flags & (RPENC_Z | RPENC_B64)) { |
---|
230 | case 0: |
---|
231 | if (encoding::isBase64(bytes(), size())) { |
---|
232 | if (!do_base64_dec(status, *this, bout)) { |
---|
233 | return false; |
---|
234 | } |
---|
235 | move(bout); |
---|
236 | } |
---|
237 | bout.clear(); |
---|
238 | if (encoding::isBinary(bytes(), size())) { |
---|
239 | if (!do_decompress(status, *this, bout)) { |
---|
240 | return false; |
---|
241 | } |
---|
242 | move(bout); |
---|
243 | } |
---|
244 | break; |
---|
245 | |
---|
246 | case RPENC_Z: // Decompress only |
---|
247 | if (!do_decompress(status, *this, bout)) { |
---|
248 | return false; |
---|
249 | } |
---|
250 | move(bout); |
---|
251 | break; |
---|
252 | |
---|
253 | case RPENC_B64: // Decode only |
---|
254 | if (!do_base64_dec(status, *this, bout)) { |
---|
255 | return false; |
---|
256 | } |
---|
257 | move(bout); |
---|
258 | break; |
---|
259 | |
---|
260 | case (RPENC_B64 | RPENC_Z): |
---|
261 | |
---|
262 | // It's always decode then decompress |
---|
263 | if (!do_base64_dec(status, *this, bout)) { |
---|
264 | return false; |
---|
265 | } |
---|
266 | clear(); |
---|
267 | if (!do_decompress(status, bout, *this)) { |
---|
268 | return false; |
---|
269 | } |
---|
270 | break; |
---|
271 | } |
---|
272 | return true; |
---|
273 | } |
---|
274 | |
---|
275 | |
---|
276 | bool |
---|
277 | Buffer::do_compress(Outcome& status, SimpleCharBuffer& bin, |
---|
278 | SimpleCharBuffer& bout) |
---|
279 | { |
---|
280 | int ret=0, flush=0; |
---|
281 | z_stream strm; |
---|
282 | |
---|
283 | char in[CHUNK]; |
---|
284 | char out[CHUNK]; |
---|
285 | |
---|
286 | /* allocate deflate state */ |
---|
287 | strm.zalloc = Z_NULL; |
---|
288 | strm.zfree = Z_NULL; |
---|
289 | strm.opaque = Z_NULL; |
---|
290 | |
---|
291 | status.addContext("Rappture::Buffer::do_compress()"); |
---|
292 | |
---|
293 | ret = deflateInit2( &strm, _level, Z_DEFLATED, |
---|
294 | _windowBits+_compressionType, |
---|
295 | 8, Z_DEFAULT_STRATEGY); |
---|
296 | |
---|
297 | if (ret != Z_OK) { |
---|
298 | status.addError("error while initializing zlib stream object"); |
---|
299 | return false; |
---|
300 | } |
---|
301 | |
---|
302 | /* compress until end of file */ |
---|
303 | do { |
---|
304 | strm.avail_in = bin.read(in, CHUNK); |
---|
305 | if (bin.bad() == true) { |
---|
306 | (void)deflateEnd(&strm); |
---|
307 | // return Z_ERRNO; |
---|
308 | status.addError("error while compressing"); |
---|
309 | return false; |
---|
310 | } |
---|
311 | flush = bin.eof() ? Z_FINISH : Z_NO_FLUSH; |
---|
312 | strm.next_in = (Bytef*) in; |
---|
313 | /* run deflate() on input until output buffer not full, finish |
---|
314 | compression if all of source has been read in */ |
---|
315 | do { |
---|
316 | strm.avail_out = CHUNK; |
---|
317 | strm.next_out = (Bytef*) out; |
---|
318 | ret = deflate(&strm, flush); /* no bad return value */ |
---|
319 | assert(ret != Z_STREAM_ERROR); /* state not clobbered */ |
---|
320 | |
---|
321 | int have; |
---|
322 | have = CHUNK - strm.avail_out; |
---|
323 | /* write to file and check for error */ |
---|
324 | if ((have > 0) && (bout.append(out, have) == 0)) { |
---|
325 | (void)deflateEnd(&strm); |
---|
326 | bout.clear(); |
---|
327 | // return Z_ERRNO; |
---|
328 | status.addError("error writing compressed data to temp buffer numBytes=%d", have); |
---|
329 | return false; |
---|
330 | } |
---|
331 | |
---|
332 | } while (strm.avail_out == 0); |
---|
333 | assert(strm.avail_in == 0); /* all input will be used */ |
---|
334 | |
---|
335 | /* done when last data in file processed */ |
---|
336 | } while (flush != Z_FINISH); |
---|
337 | |
---|
338 | assert(ret == Z_STREAM_END); /* stream will be complete */ |
---|
339 | |
---|
340 | /* clean up and return */ |
---|
341 | (void)deflateEnd(&strm); |
---|
342 | // return Z_OK; |
---|
343 | return true; |
---|
344 | } |
---|
345 | |
---|
346 | bool |
---|
347 | Buffer::do_decompress(Outcome& status, SimpleCharBuffer& bin, |
---|
348 | SimpleCharBuffer& bout) |
---|
349 | { |
---|
350 | int ret; |
---|
351 | unsigned have; |
---|
352 | z_stream strm; |
---|
353 | |
---|
354 | char in[CHUNK]; |
---|
355 | char out[CHUNK]; |
---|
356 | |
---|
357 | int bytesWritten = 0; |
---|
358 | |
---|
359 | status.addContext("Rappture::Buffer::do_decompress()"); |
---|
360 | |
---|
361 | /* allocate inflate state */ |
---|
362 | strm.zalloc = Z_NULL; |
---|
363 | strm.zfree = Z_NULL; |
---|
364 | strm.opaque = Z_NULL; |
---|
365 | strm.avail_in = 0; |
---|
366 | strm.next_in = Z_NULL; |
---|
367 | ret = inflateInit2(&strm,_windowBits+_compressionType); |
---|
368 | if (ret != Z_OK) { |
---|
369 | status.addError("error while initializing zlib stream object"); |
---|
370 | return false; |
---|
371 | } |
---|
372 | |
---|
373 | /* decompress until deflate stream ends or end of file */ |
---|
374 | do { |
---|
375 | strm.avail_in = bin.read(in, CHUNK); |
---|
376 | if (bin.bad() == true) { |
---|
377 | (void)inflateEnd(&strm); |
---|
378 | // return Z_ERRNO; |
---|
379 | status.addError("error while compressing"); |
---|
380 | return false; |
---|
381 | } |
---|
382 | if (strm.avail_in == 0) |
---|
383 | break; |
---|
384 | strm.next_in = (unsigned char*) in; |
---|
385 | /* run inflate() on input until output buffer not full */ |
---|
386 | do { |
---|
387 | strm.avail_out = CHUNK; |
---|
388 | strm.next_out = (unsigned char*) out; |
---|
389 | ret = inflate(&strm, Z_NO_FLUSH); |
---|
390 | assert(ret != Z_STREAM_ERROR); /* state not clobbered */ |
---|
391 | switch (ret) { |
---|
392 | case Z_NEED_DICT: |
---|
393 | ret = Z_DATA_ERROR; /* and fall through */ |
---|
394 | case Z_DATA_ERROR: |
---|
395 | case Z_MEM_ERROR: |
---|
396 | (void)inflateEnd(&strm); |
---|
397 | bout.clear(); |
---|
398 | status.addError("memory error while inflating data"); |
---|
399 | return false; |
---|
400 | } |
---|
401 | have = CHUNK - strm.avail_out; |
---|
402 | bytesWritten = bout.append(out, have); |
---|
403 | if ( ( (unsigned) bytesWritten != have) ) { |
---|
404 | (void)inflateEnd(&strm); |
---|
405 | bout.clear(); |
---|
406 | // return Z_ERRNO; |
---|
407 | status.addError("error writing compressed data to temp buffer"); |
---|
408 | return false; |
---|
409 | } |
---|
410 | } while (strm.avail_out == 0); |
---|
411 | |
---|
412 | /* done when inflate() says it's done */ |
---|
413 | } while (ret != Z_STREAM_END); |
---|
414 | |
---|
415 | /* clean up and return */ |
---|
416 | (void)inflateEnd(&strm); |
---|
417 | // return ret == Z_STREAM_END ? Z_OK : Z_DATA_ERROR; |
---|
418 | return true; |
---|
419 | } |
---|
420 | |
---|
421 | |
---|
422 | bool |
---|
423 | Buffer::do_base64_enc(Outcome& status, const SimpleCharBuffer& bin, |
---|
424 | SimpleCharBuffer& bout ) |
---|
425 | { |
---|
426 | int tBufSize = 0; |
---|
427 | unsigned int tBufAvl = 2*bin.size(); |
---|
428 | char* tBuf = new char[tBufAvl]; |
---|
429 | |
---|
430 | base64::encoder E; |
---|
431 | |
---|
432 | tBufSize = E.encode(bin.bytes(),bin.size(),tBuf); |
---|
433 | tBufSize += E.encode_end(tBuf+tBufSize); |
---|
434 | |
---|
435 | bout = SimpleCharBuffer(tBuf,tBufSize); |
---|
436 | delete [] tBuf; |
---|
437 | |
---|
438 | return true; |
---|
439 | } |
---|
440 | |
---|
441 | |
---|
442 | bool |
---|
443 | Buffer::do_base64_dec(Outcome& status, const SimpleCharBuffer& bin, |
---|
444 | SimpleCharBuffer& bout ) |
---|
445 | { |
---|
446 | int tBufSize = 0; |
---|
447 | unsigned int tBufAvl = bin.size(); |
---|
448 | char* tBuf = new char[tBufAvl]; |
---|
449 | |
---|
450 | base64::decoder D; |
---|
451 | |
---|
452 | tBufSize = D.decode(bin.bytes(),bin.size(),tBuf); |
---|
453 | |
---|
454 | bout = SimpleCharBuffer(tBuf,tBufSize); |
---|
455 | delete [] tBuf; |
---|
456 | |
---|
457 | return true; |
---|
458 | } |
---|
459 | |
---|
460 | |
---|
461 | } |
---|
462 | |
---|
463 | |
---|
464 | |
---|