source: branches/1.6/src/core/RpBuffer.cc @ 6295

Last change on this file since 6295 was 5850, checked in by gah, 9 years ago

merge accumulative changes from 1.3 branch into uq branch

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