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

Last change on this file since 592 was 592, checked in by dkearney, 18 years ago

changed ints to unsigned ints inside of the rappture buffer object and fixed some corner cases.
adjusted the Ptr.h and Outcome.[h,cpp] files to get rid of warnings from not returning the correct data type.

File size: 20.3 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    }
626
627    if (base64) {
628        if (compress) {
629            bin.move(bout);
630            do_base64_enc(err,bin,bout);
631        }
632        else {
633            do_base64_enc(err,*this,bout);
634        }
635    }
636
637    if (!err) {
638        // write the encoded data to the internal buffer
639        move(bout);
640    }
641
642    return err;
643}
644
645
646Outcome
647Buffer::decode (bool decompress, bool base64)
648{
649    Outcome err;
650    SimpleBuffer bin;
651    SimpleBuffer bout;
652
653    rewind();
654
655    if (base64) {
656        do_base64_dec(err,*this,bout);
657    }
658
659    if (decompress) {
660        if (base64) {
661            bin.move(bout);
662            do_decompress(err,bin,bout);
663        }
664        else {
665            do_decompress(err,*this,bout);
666        }
667    }
668
669    if (!err) {
670        // write the decoded data to the internal buffer
671        move(bout);
672    }
673
674    return err;
675}
676
677
678void
679Buffer::do_compress(    Outcome& status,
680                        SimpleBuffer& bin,
681                        SimpleBuffer& bout  )
682{
683    int ret=0, flush=0;
684    unsigned have=0;
685    z_stream strm;
686
687    char in[CHUNK];
688    char out[CHUNK];
689
690    int bytesWritten = 0;
691
692    /* allocate deflate state */
693    strm.zalloc = Z_NULL;
694    strm.zfree = Z_NULL;
695    strm.opaque = Z_NULL;
696
697    ret = deflateInit2( &strm, _level, Z_DEFLATED,
698                        _windowBits+_compressionType,
699                        8, Z_DEFAULT_STRATEGY);
700
701    if (ret != Z_OK) {
702        status.error("error while initializing zlib stream object");
703        status.addContext("Rappture::Buffer::do_compress()");
704        return;
705    }
706
707    /* compress until end of file */
708    do {
709        strm.avail_in = bin.read(in, CHUNK);
710        if (bin.bad() == true) {
711            (void)deflateEnd(&strm);
712            // return Z_ERRNO;
713            status.error("error while compressing");
714            status.addContext("Rappture::Buffer::do_compress()");
715            return;
716        }
717        flush = bin.eof() ? Z_FINISH : Z_NO_FLUSH;
718        strm.next_in = (Bytef*) in;
719        /* run deflate() on input until output buffer not full, finish
720           compression if all of source has been read in */
721        do {
722            strm.avail_out = CHUNK;
723            strm.next_out = (Bytef*) out;
724            ret = deflate(&strm, flush);    /* no bad return value */
725            assert(ret != Z_STREAM_ERROR);  /* state not clobbered */
726            have = CHUNK - strm.avail_out;
727            /* write to file and check for error */
728            bytesWritten = bout.append(out, have);
729            if ( ( (unsigned) bytesWritten != have ) ) {
730                (void)deflateEnd(&strm);
731                bout.clear();
732                // return Z_ERRNO;
733                status.error("error writing compressed data to temp buffer");
734                status.addContext("Rappture::Buffer::do_compress()");
735                return;
736            }
737
738        } while (strm.avail_out == 0);
739        assert(strm.avail_in == 0);     /* all input will be used */
740
741        /* done when last data in file processed */
742    } while (flush != Z_FINISH);
743
744    assert(ret == Z_STREAM_END);        /* stream will be complete */
745
746    /* clean up and return */
747    (void)deflateEnd(&strm);
748    // return Z_OK;
749    return;
750}
751
752void
753Buffer::do_decompress(  Outcome& status,
754                        SimpleBuffer& bin,
755                        SimpleBuffer& bout  )
756{
757    int ret;
758    unsigned have;
759    z_stream strm;
760
761    char in[CHUNK];
762    char out[CHUNK];
763
764    int bytesWritten = 0;
765
766    /* allocate inflate state */
767    strm.zalloc = Z_NULL;
768    strm.zfree = Z_NULL;
769    strm.opaque = Z_NULL;
770    strm.avail_in = 0;
771    strm.next_in = Z_NULL;
772    ret = inflateInit2(&strm,_windowBits+_compressionType);
773    if (ret != Z_OK) {
774        status.error("error while initializing zlib stream object");
775        status.addContext("Rappture::Buffer::do_decompress()");
776        // return status;
777        return;
778    }
779
780    /* decompress until deflate stream ends or end of file */
781    do {
782        strm.avail_in = bin.read(in, CHUNK);
783        if (bin.bad() == true) {
784            (void)inflateEnd(&strm);
785            // return Z_ERRNO;
786            status.error("error while compressing");
787            status.addContext("Rappture::Buffer::do_decompress()");
788            // return status;
789            return;
790        }
791        if (strm.avail_in == 0)
792            break;
793        strm.next_in = (unsigned char*) in;
794        /* run inflate() on input until output buffer not full */
795        do {
796            strm.avail_out = CHUNK;
797            strm.next_out = (unsigned char*) out;
798            ret = inflate(&strm, Z_NO_FLUSH);
799            assert(ret != Z_STREAM_ERROR);  /* state not clobbered */
800            switch (ret) {
801            case Z_NEED_DICT:
802                ret = Z_DATA_ERROR;     /* and fall through */
803            case Z_DATA_ERROR:
804            case Z_MEM_ERROR:
805                (void)inflateEnd(&strm);
806                bout.clear();
807                status.error("memory error while inflating data");
808                status.addContext("Rappture::Buffer::do_decompress()");
809                return;
810            }
811            have = CHUNK - strm.avail_out;
812            bytesWritten = bout.append(out, have);
813            if ( ( (unsigned) bytesWritten != have) ) {
814                (void)inflateEnd(&strm);
815                bout.clear();
816                // return Z_ERRNO;
817                status.error("error writing compressed data to temp buffer");
818                status.addContext("Rappture::Buffer::do_decompress()");
819                return;
820            }
821        } while (strm.avail_out == 0);
822
823        /* done when inflate() says it's done */
824    } while (ret != Z_STREAM_END);
825
826    /* clean up and return */
827    (void)inflateEnd(&strm);
828    // return ret == Z_STREAM_END ? Z_OK : Z_DATA_ERROR;
829    return;
830}
831
832
833void
834Buffer::do_base64_enc(  Outcome& status,
835                        const SimpleBuffer& bin,
836                        SimpleBuffer& bout )
837{
838    int tBufSize = 0;
839    unsigned int tBufAvl = 2*bin.size();
840    char* tBuf = new char[tBufAvl];
841
842    base64::encoder E;
843
844    tBufSize = E.encode(bin.bytes(),bin.size(),tBuf);
845    tBufSize += E.encode_end(tBuf+tBufSize);
846
847    bout = SimpleBuffer(tBuf,tBufSize);
848    delete [] tBuf;
849
850    return;
851}
852
853
854void
855Buffer::do_base64_dec(  Outcome& status,
856                        const SimpleBuffer& bin,
857                        SimpleBuffer& bout )
858{
859    int tBufSize = 0;
860    unsigned int tBufAvl = bin.size();
861    char* tBuf = new char[tBufAvl];
862
863    base64::decoder D;
864
865    tBufSize = D.decode(bin.bytes(),bin.size(),tBuf);
866
867    bout = SimpleBuffer(tBuf,tBufSize);
868    delete [] tBuf;
869
870    return;
871}
Note: See TracBrowser for help on using the repository browser.