source: trunk/src2/core/RpBuffer.cc @ 606

Last change on this file since 606 was 606, checked in by dkearney, 17 years ago

missed an error check

File size: 20.4 KB
Line 
1/*
2 * ======================================================================
3 *  Rappture::RpBuffer
4 *
5 *  AUTHOR:  Derrick Kearney, Purdue University
6 *
7 *  Copyright (c) 2004-2007  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 *  This code is based on the Tcl_DString facility included in the
14 *  Tcl source release, which includes the following copyright:
15 *
16 *  Copyright (c) 1987-1993 The Regents of the University of California.
17 *  Copyright (c) 1994-1998 Sun Microsystems, Inc.
18 *  Copyright (c) 2001 by Kevin B. Kenny.  All rights reserved.
19 *
20 *  This software is copyrighted by the Regents of the University of
21 *  California, Sun Microsystems, Inc., Scriptics Corporation,
22 *  and other parties.  The following terms apply to all files associated
23 *  with the software unless explicitly disclaimed in individual files.
24 *
25 *  The authors hereby grant permission to use, copy, modify, distribute,
26 *  and license this software and its documentation for any purpose, provided
27 *  that existing copyright notices are retained in all copies and that this
28 *  notice is included verbatim in any distributions. No written agreement,
29 *  license, or royalty fee is required for any of the authorized uses.
30 *  Modifications to this software may be copyrighted by their authors
31 *  and need not follow the licensing terms described here, provided that
32 *  the new terms are clearly indicated on the first page of each file where
33 *  they apply.
34 *
35 *  IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY
36 *  FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
37 *  ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY
38 *  DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE
39 *  POSSIBILITY OF SUCH DAMAGE.
40 *
41 *  THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
42 *  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
43 *  FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT.  THIS SOFTWARE
44 *  IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE
45 *  NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR
46 *  MODIFICATIONS.
47 *
48 *  GOVERNMENT USE: If you are acquiring this software on behalf of the
49 *  U.S. government, the Government shall have only "Restricted Rights"
50 *  in the software and related documentation as defined in the Federal·
51 *  Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2).  If you
52 *  are acquiring the software on behalf of the Department of Defense, the
53 *  software shall be classified as "Commercial Computer Software" and the
54 *  Government shall have only "Restricted Rights" as defined in Clause
55 *  252.227-7013 (c) (1) of DFARs.  Notwithstanding the foregoing, the
56 *  authors grant the U.S. Government and others acting in its behalf
57 *  permission to use and distribute the software in accordance with the
58 *  terms specified in this license.·
59 *
60 * ======================================================================
61 */
62
63#include "RpBuffer.h"
64
65using namespace Rappture;
66
67/**
68 * Construct an empty SimpleBuffer.
69 */
70SimpleBuffer::SimpleBuffer()
71{
72    bufferInit();
73}
74
75
76/**
77 * Construct a SimpleBuffer loaded with initial data.
78 *
79 * @param bytes pointer to bytes being stored.
80 * @param nbytes number of bytes being stored.
81 */
82SimpleBuffer::SimpleBuffer(const char* bytes, int nbytes)
83{
84    bufferInit();
85    append(bytes,nbytes);
86}
87
88
89/**
90 * Copy constructor
91 * @param SimpleBuffer object to copy
92 */
93SimpleBuffer::SimpleBuffer(const SimpleBuffer& b)
94{
95    bufferInit();
96    append(b.bytes(),b.size());
97}
98
99
100/**
101 * Assignment operator
102 * @param SimpleBuffer object to copy
103 */
104SimpleBuffer&
105SimpleBuffer::operator=(const SimpleBuffer& b)
106{
107    bufferFree();
108    bufferInit();
109    append(b.bytes(),b.size());
110    return *this;
111}
112
113
114/**
115 * Operator +
116 * @param SimpleBuffer object to add
117 * @param SimpleBuffer object to add
118 */
119SimpleBuffer
120SimpleBuffer::operator+(const SimpleBuffer& b) const
121{
122    SimpleBuffer newBuffer(*this);
123    newBuffer.operator+=(b);
124    return newBuffer;
125}
126
127
128/**
129 * Operator +=
130 * @param SimpleBuffer object to add
131 */
132SimpleBuffer&
133SimpleBuffer::operator+=(const SimpleBuffer& b)
134{
135    append(b.bytes(),b.size());
136    return *this;
137}
138
139
140/**
141 * Destructor
142 */
143SimpleBuffer::~SimpleBuffer()
144{
145    bufferFree();
146}
147
148
149/**
150 * Get the bytes currently stored in the buffer.  These bytes can
151 * be stored, and used later to construct another Buffer to
152 * decode the information.
153 *
154 * @return Pointer to the bytes in the buffer.
155 */
156const char*
157SimpleBuffer::bytes() const
158{
159    return _buf;
160}
161
162
163/**
164 * Get the number of bytes currently stored in the buffer.
165 * @return Number of the bytes used in the buffer.
166 */
167unsigned int
168SimpleBuffer::size() const
169{
170    return _size;
171}
172
173
174/**
175 * Clear the buffer, making it empty.
176 * @return Reference to this buffer.
177 */
178SimpleBuffer&
179SimpleBuffer::clear()
180{
181    bufferFree();
182    bufferInit();
183
184    return *this;
185}
186
187
188/**
189 * Append bytes to the end of this buffer
190 * @param Buffer object to copy
191 * @return Reference to this buffer.
192 */
193int
194SimpleBuffer::append(const char* bytes, int nbytes)
195{
196    unsigned int newSize = 0;
197    char *newBuffer = NULL;
198
199    // User specified NULL buffer to append
200    if (bytes == NULL) {
201        return 0;
202    }
203
204    if (nbytes == -1) {
205        // user signaled null terminated string
206        nbytes = strlen(bytes);
207    }
208
209    if (nbytes <= 0) {
210        // no data written, invalid option
211        return nbytes;
212    }
213
214    // Empty internal buffer, make sure its properly initialized.
215    if (_buf == NULL) {
216        bufferInit();
217    }
218
219    newSize = (unsigned int)(_size + nbytes);
220
221    // ensure that our smallest buffer is 200 bytes
222    if (newSize < (RP_BUFFER_MIN_SIZE/2)) {
223        newSize = (RP_BUFFER_MIN_SIZE/2);
224    }
225
226    /*
227     * Allocate a larger buffer for the string if the current one isn't
228     * large enough. Allocate extra space in the new buffer so that there
229     * will be room to grow before we have to allocate again.
230     */
231
232    if (newSize >= _spaceAvl) {
233        _spaceAvl = newSize * 2;
234        newBuffer = new char[_spaceAvl];
235        if (newBuffer == NULL) {
236            // return memory error
237            return -1;
238        }
239        if (_buf != NULL) {
240            memcpy((void*) newBuffer, (void*) _buf, (size_t) _size);
241            delete [] _buf;
242            _buf = NULL;
243        }
244        _buf = newBuffer;
245    }
246
247    memcpy((void*) (_buf + _size), (void*) bytes, (size_t) nbytes);
248
249    _size = _size + (unsigned int) nbytes;
250
251    return nbytes;
252}
253
254
255/**
256 * Read data from the buffer into a memory location provided by caller
257 * @param Pointer locating where to place read bytes.
258 * @param Size of the memory location.
259 * @return Number of bytes written to memory location
260 */
261int
262SimpleBuffer::read(const char* bytes, int nbytes)
263{
264    unsigned int bytesRead = 0;
265
266    // SimpleBuffer is empty.
267    if (_buf == NULL) {
268        return 0;
269    }
270
271    // User specified NULL buffer.
272    if (bytes == NULL) {
273        return 0;
274    }
275
276    // User specified negative buffer size
277    if (nbytes <= 0) {
278        return 0;
279    }
280
281    // make sure we don't read off the end of our buffer
282    if ( (_pos + nbytes) >= _size ) {
283        bytesRead = (_size - _pos);
284    }
285    else {
286        bytesRead = (unsigned int) nbytes;
287    }
288
289    if (bytesRead <= 0) {
290        return 0;
291    }
292
293    if (bytesRead > 0) {
294        memcpy((void*) bytes, (void*) (_buf+_pos), (size_t) bytesRead);
295    }
296
297    _pos = (_pos + bytesRead);
298
299    return (int)bytesRead;
300}
301
302
303/**
304 * Set buffer position indicator to spot within the buffer
305 * @param Offset from whence location in buffer.
306 * @param Place from where offset is added or subtracted.
307 * @return 0 on success, anything else is failure
308 */
309int
310SimpleBuffer::seek(int offset, int whence)
311{
312    int retVal = 0;
313
314    if (_buf == NULL) {
315        return -1 ;
316    }
317
318    if (whence == SEEK_SET) {
319        if (offset < 0) {
320            /* dont go off the beginning of data */
321            _pos = 0;
322        }
323        else if (offset >= (int)_size) {
324            /* dont go off the end of data */
325            _pos = _size - 1;
326        }
327        else {
328            _pos = (unsigned int)(_pos + offset);
329        }
330    }
331    else if (whence == SEEK_CUR) {
332        if ( (_pos + offset) < 0) {
333            /* dont go off the beginning of data */
334            _pos = 0;
335        }
336        else if ((_pos + offset) >= _size) {
337            /* dont go off the end of data */
338            _pos = _size - 1;
339        }
340        else {
341            _pos = (unsigned int)(_pos + offset);
342        }
343    }
344    else if (whence == SEEK_END) {
345        if (offset <= (-1*((int)_size))) {
346            /* dont go off the beginning of data */
347            _pos = 0;
348        }
349        else if (offset >= 0) {
350            /* dont go off the end of data */
351            _pos = _size - 1;
352        }
353        else {
354            _pos = (unsigned int)((_size - 1) + offset);
355        }
356    }
357    else {
358        retVal = -1;
359    }
360
361    return retVal;
362}
363
364
365/**
366 * Tell caller the offset of the position indicator from the start of buffer
367 * @return Number of bytes the position indicator is from start of buffer
368 */
369int
370SimpleBuffer::tell()
371{
372   return (int)_pos;
373}
374
375
376/**
377 * Read data from the buffer into a memory location provided by caller
378 */
379SimpleBuffer&
380SimpleBuffer::rewind()
381{
382    _pos = 0;
383    return *this;
384}
385
386
387/**
388 * Tell if the last file like operation (ie. read()) was successful
389 * or if there was a failure like eof, or bad memory
390 * @return True or false boolean value
391 */
392bool
393SimpleBuffer::good() const
394{
395    return (_fileState);
396}
397
398
399/**
400 * Tell if the last file like operation (ie. read()) failed
401 * Opposite of good()
402 * @return True or false boolean value
403 */
404bool
405SimpleBuffer::bad() const
406{
407    return (!_fileState);
408}
409
410
411/**
412 * Tell if the position flag is at the end of the buffer
413 * @return True or false boolean value
414 */
415bool
416SimpleBuffer::eof() const
417{
418    return (_pos >= _size);
419}
420
421
422/**
423 * Move the data from this SimpleBuffer to the SimpleBuffer provided by
424 * the caller. All data except the _pos is moved and this SimpleBuffer is
425 * re-initialized with bufferInit().
426 * @param SimpleBuffer to move the information to
427 * @return reference to this SimpleBuffer object.
428 */
429SimpleBuffer&
430SimpleBuffer::move(SimpleBuffer& b)
431{
432    bufferFree();
433
434    _buf = b._buf;
435    _pos = b._pos;
436    _size = b._size;
437    _spaceAvl = b._spaceAvl;
438    _fileState = b._fileState;
439
440    b.bufferInit();
441
442    return *this;
443}
444
445
446 /**
447  *  Initializes a dynamic buffer, discarding any previous contents
448  *  of the buffer. bufferFree() should have been called already
449  *  if the dynamic buffer was previously in use.
450  */
451void
452SimpleBuffer::bufferInit()
453{
454    _buf = NULL;
455    _pos = 0;
456    _size = 0;
457    _spaceAvl = 0;
458    _fileState = true;
459}
460
461
462/**
463 *  Frees up any memory allocated for the dynamic buffer and
464 *  reinitializes the buffer to an empty state.
465 */
466void
467SimpleBuffer::bufferFree()
468{
469    if (_buf != NULL) {
470        delete [] _buf;
471        _buf = NULL;
472    }
473    bufferInit();
474}
475
476
477/**
478 * Construct an empty Buffer.
479 */
480Buffer::Buffer()
481  : SimpleBuffer(),
482    _level(6),
483    _compressionType(RPCOMPRESS_GZIP),
484    _windowBits(15)
485{}
486
487
488/**
489 * Construct a Buffer loaded with initial data.
490 *
491 * @param bytes pointer to bytes being stored.
492 * @param nbytes number of bytes being stored.
493 */
494Buffer::Buffer(const char* bytes, int nbytes)
495  : SimpleBuffer(bytes,nbytes),
496    _level(6),
497    _compressionType(RPCOMPRESS_GZIP),
498    _windowBits(15)
499{}
500
501/**
502 * Copy constructor
503 * @param Buffer object to copy
504 */
505Buffer::Buffer(const Buffer& b)
506  : SimpleBuffer(b),
507    _level(b._level),
508    _compressionType(b._compressionType),
509    _windowBits(b._windowBits)
510{}
511
512/**
513 * Assignment operator
514 * @param Buffer object to copy
515 */
516Buffer&
517Buffer::operator=(const Buffer& b)
518{
519    SimpleBuffer::operator=(b);
520
521    _level = b._level;
522    _compressionType = b._compressionType;
523    _windowBits = b._windowBits;
524
525    return *this;
526}
527
528
529Buffer
530Buffer::operator+(const Buffer& b) const
531{
532    Buffer newBuffer(*this);
533    newBuffer.operator+=(b);
534    return newBuffer;
535}
536
537
538Buffer&
539Buffer::operator+=(const Buffer& b)
540{
541    SimpleBuffer::operator+=(b);
542    return *this;
543}
544
545
546Buffer::~Buffer()
547{}
548
549
550Outcome
551Buffer::load (const char* filePath)
552{
553    Outcome status;
554    std::ifstream::pos_type size = 0;
555    std::ifstream inFile;
556    char* memblock = NULL;
557
558
559    inFile.open(filePath, std::ios::in | std::ios::ate | std::ios::binary);
560    if (!inFile.is_open()) {
561        status.error("error while opening file");
562        status.addContext("Rappture::Buffer::load()");
563        return status;
564    }
565
566    size = inFile.tellg();
567    memblock = new char [size];
568    if (memblock == NULL) {
569        status.error("error while allocating memory");
570        status.addContext("Rappture::Buffer::load()");
571        inFile.close();
572        return status;
573    }
574
575    inFile.seekg(0,std::ios::beg);
576    inFile.read(memblock,size);
577
578    // save data in buffer object.
579    append(memblock,size);
580
581    // close files, free memory
582    inFile.close();
583    delete [] memblock;
584    memblock = NULL;
585
586    // exit nicely
587    return status;
588}
589
590
591Outcome
592Buffer::dump (const char* filePath)
593{
594    Outcome status;
595    std::ofstream outFile;
596
597    outFile.open(filePath, std::ios::out|std::ios::trunc|std::ios::binary);
598    if (!outFile.is_open()) {
599        status.error("error while opening file");
600        status.addContext("Rappture::Buffer::dump()");
601        return status;
602    }
603
604    outFile.write(bytes(),size());
605    outFile.close();
606
607    // exit nicely
608    return status;
609}
610
611
612Outcome
613Buffer::encode (bool compress, bool base64)
614{
615    Outcome err;
616    SimpleBuffer bin;
617    SimpleBuffer bout;
618
619    err.addContext("Rappture::Buffer::encode()");
620
621    rewind();
622
623    if (compress) {
624        do_compress(err,*this,bout);
625        if (err) {
626            return err;
627        }
628    }
629
630    if (base64) {
631        if (compress) {
632            bin.move(bout);
633            do_base64_enc(err,bin,bout);
634        }
635        else {
636            do_base64_enc(err,*this,bout);
637        }
638    }
639
640    if (!err) {
641        // write the encoded data to the internal buffer
642        move(bout);
643    }
644
645    return err;
646}
647
648
649Outcome
650Buffer::decode (bool decompress, bool base64)
651{
652    Outcome err;
653    SimpleBuffer bin;
654    SimpleBuffer bout;
655
656    rewind();
657
658    if (base64) {
659        do_base64_dec(err,*this,bout);
660        if (err) {
661            return err;
662        }
663    }
664
665    if (decompress) {
666        if (base64) {
667            bin.move(bout);
668            do_decompress(err,bin,bout);
669        }
670        else {
671            do_decompress(err,*this,bout);
672        }
673    }
674
675    if (!err) {
676        // write the decoded data to the internal buffer
677        move(bout);
678    }
679
680    return err;
681}
682
683
684void
685Buffer::do_compress(    Outcome& status,
686                        SimpleBuffer& bin,
687                        SimpleBuffer& bout  )
688{
689    int ret=0, flush=0;
690    unsigned have=0;
691    z_stream strm;
692
693    char in[CHUNK];
694    char out[CHUNK];
695
696    int bytesWritten = 0;
697
698    /* allocate deflate state */
699    strm.zalloc = Z_NULL;
700    strm.zfree = Z_NULL;
701    strm.opaque = Z_NULL;
702
703    ret = deflateInit2( &strm, _level, Z_DEFLATED,
704                        _windowBits+_compressionType,
705                        8, Z_DEFAULT_STRATEGY);
706
707    if (ret != Z_OK) {
708        status.error("error while initializing zlib stream object");
709        status.addContext("Rappture::Buffer::do_compress()");
710        return;
711    }
712
713    /* compress until end of file */
714    do {
715        strm.avail_in = bin.read(in, CHUNK);
716        if (bin.bad() == true) {
717            (void)deflateEnd(&strm);
718            // return Z_ERRNO;
719            status.error("error while compressing");
720            status.addContext("Rappture::Buffer::do_compress()");
721            return;
722        }
723        flush = bin.eof() ? Z_FINISH : Z_NO_FLUSH;
724        strm.next_in = (Bytef*) in;
725        /* run deflate() on input until output buffer not full, finish
726           compression if all of source has been read in */
727        do {
728            strm.avail_out = CHUNK;
729            strm.next_out = (Bytef*) out;
730            ret = deflate(&strm, flush);    /* no bad return value */
731            assert(ret != Z_STREAM_ERROR);  /* state not clobbered */
732            have = CHUNK - strm.avail_out;
733            /* write to file and check for error */
734            bytesWritten = bout.append(out, have);
735            if ( ( (unsigned) bytesWritten != have ) ) {
736                (void)deflateEnd(&strm);
737                bout.clear();
738                // return Z_ERRNO;
739                status.error("error writing compressed data to temp buffer");
740                status.addContext("Rappture::Buffer::do_compress()");
741                return;
742            }
743
744        } while (strm.avail_out == 0);
745        assert(strm.avail_in == 0);     /* all input will be used */
746
747        /* done when last data in file processed */
748    } while (flush != Z_FINISH);
749
750    assert(ret == Z_STREAM_END);        /* stream will be complete */
751
752    /* clean up and return */
753    (void)deflateEnd(&strm);
754    // return Z_OK;
755    return;
756}
757
758void
759Buffer::do_decompress(  Outcome& status,
760                        SimpleBuffer& bin,
761                        SimpleBuffer& bout  )
762{
763    int ret;
764    unsigned have;
765    z_stream strm;
766
767    char in[CHUNK];
768    char out[CHUNK];
769
770    int bytesWritten = 0;
771
772    /* allocate inflate state */
773    strm.zalloc = Z_NULL;
774    strm.zfree = Z_NULL;
775    strm.opaque = Z_NULL;
776    strm.avail_in = 0;
777    strm.next_in = Z_NULL;
778    ret = inflateInit2(&strm,_windowBits+_compressionType);
779    if (ret != Z_OK) {
780        status.error("error while initializing zlib stream object");
781        status.addContext("Rappture::Buffer::do_decompress()");
782        // return status;
783        return;
784    }
785
786    /* decompress until deflate stream ends or end of file */
787    do {
788        strm.avail_in = bin.read(in, CHUNK);
789        if (bin.bad() == true) {
790            (void)inflateEnd(&strm);
791            // return Z_ERRNO;
792            status.error("error while compressing");
793            status.addContext("Rappture::Buffer::do_decompress()");
794            // return status;
795            return;
796        }
797        if (strm.avail_in == 0)
798            break;
799        strm.next_in = (unsigned char*) in;
800        /* run inflate() on input until output buffer not full */
801        do {
802            strm.avail_out = CHUNK;
803            strm.next_out = (unsigned char*) out;
804            ret = inflate(&strm, Z_NO_FLUSH);
805            assert(ret != Z_STREAM_ERROR);  /* state not clobbered */
806            switch (ret) {
807            case Z_NEED_DICT:
808                ret = Z_DATA_ERROR;     /* and fall through */
809            case Z_DATA_ERROR:
810            case Z_MEM_ERROR:
811                (void)inflateEnd(&strm);
812                bout.clear();
813                status.error("memory error while inflating data");
814                status.addContext("Rappture::Buffer::do_decompress()");
815                return;
816            }
817            have = CHUNK - strm.avail_out;
818            bytesWritten = bout.append(out, have);
819            if ( ( (unsigned) bytesWritten != have) ) {
820                (void)inflateEnd(&strm);
821                bout.clear();
822                // return Z_ERRNO;
823                status.error("error writing compressed data to temp buffer");
824                status.addContext("Rappture::Buffer::do_decompress()");
825                return;
826            }
827        } while (strm.avail_out == 0);
828
829        /* done when inflate() says it's done */
830    } while (ret != Z_STREAM_END);
831
832    /* clean up and return */
833    (void)inflateEnd(&strm);
834    // return ret == Z_STREAM_END ? Z_OK : Z_DATA_ERROR;
835    return;
836}
837
838
839void
840Buffer::do_base64_enc(  Outcome& status,
841                        const SimpleBuffer& bin,
842                        SimpleBuffer& bout )
843{
844    int tBufSize = 0;
845    unsigned int tBufAvl = 2*bin.size();
846    char* tBuf = new char[tBufAvl];
847
848    base64::encoder E;
849
850    tBufSize = E.encode(bin.bytes(),bin.size(),tBuf);
851    tBufSize += E.encode_end(tBuf+tBufSize);
852
853    bout = SimpleBuffer(tBuf,tBufSize);
854    delete [] tBuf;
855
856    return;
857}
858
859
860void
861Buffer::do_base64_dec(  Outcome& status,
862                        const SimpleBuffer& bin,
863                        SimpleBuffer& bout )
864{
865    int tBufSize = 0;
866    unsigned int tBufAvl = bin.size();
867    char* tBuf = new char[tBufAvl];
868
869    base64::decoder D;
870
871    tBufSize = D.decode(bin.bytes(),bin.size(),tBuf);
872
873    bout = SimpleBuffer(tBuf,tBufSize);
874    delete [] tBuf;
875
876    return;
877}
Note: See TracBrowser for help on using the repository browser.