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

Last change on this file since 665 was 665, checked in by dkearney, 14 years ago

Updates to Rappture::Utils::progress for all languages
removed the dependancy on Rappture.Units from within number.py, it should only depend on Rappture which will include Rappture.Units
added Rappture.Units as a module to load when people import Rappture in python.
added -V pbs variable to queue.py to include qsub environment variables in the submitted job.
updated setup.py.in to install Rappture.Utils
added progress bar to all app-fermi examples showing how to use the Rappture::Utils::progress function in all languages.
added destructor definitions to Node classes in src2/core

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