source: branches/blt4/src/core/RpBuffer.cc @ 2544

Last change on this file since 2544 was 2544, checked in by gah, 13 years ago

update from trunk

File size: 11.1 KB
Line 
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
27namespace Rappture {
28
29/**
30 * Construct an empty Buffer.
31 */
32Buffer::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 */
43Buffer::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 */
57Buffer::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 */
68Buffer::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 */
79Buffer&
80Buffer::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
92Buffer
93Buffer::operator+(const Buffer& b) const
94{
95    Buffer newBuffer(*this);
96    newBuffer.operator+=(b);
97    return newBuffer;
98}
99
100
101Buffer&
102Buffer::operator+=(const Buffer& b)
103{
104    SimpleCharBuffer::operator+=(b);
105    return *this;
106}
107
108
109Buffer::~Buffer()
110{}
111
112
113bool
114Buffer::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
158bool
159Buffer::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
182bool
183Buffer::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
222bool
223Buffer::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
276bool
277Buffer::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
346bool
347Buffer::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
422bool
423Buffer::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
442bool
443Buffer::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
Note: See TracBrowser for help on using the repository browser.