include/cinder/Stream.h
Go to the documentation of this file.
00001 /*
00002  Copyright (c) 2010, The Barbarian Group
00003  All rights reserved.
00004 
00005  Redistribution and use in source and binary forms, with or without modification, are permitted provided that
00006  the following conditions are met:
00007 
00008     * Redistributions of source code must retain the above copyright notice, this list of conditions and
00009     the following disclaimer.
00010     * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
00011     the following disclaimer in the documentation and/or other materials provided with the distribution.
00012 
00013  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
00014  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
00015  PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
00016  ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
00017  TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
00018  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
00019  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00020  POSSIBILITY OF SUCH DAMAGE.
00021 */
00022 
00023 #pragma once 
00024 
00025 #include "cinder/Cinder.h"
00026 #include "cinder/Buffer.h"
00027 #include "cinder/Exception.h"
00028 #include "cinder/Filesystem.h"
00029 
00030 #include <boost/noncopyable.hpp>
00031 
00032 #include <string>
00033 #ifndef __OBJC__
00034 #   include <boost/iostreams/concepts.hpp>
00035 #   include <boost/iostreams/stream.hpp>
00036 #endif
00037 
00038 namespace cinder {
00039 
00040 class StreamBase : private boost::noncopyable {
00041  public:
00042     virtual ~StreamBase() {}
00043     
00044     enum Endianness { STREAM_BIG_ENDIAN, STREAM_LITTLE_ENDIAN };
00045  
00047     static uint8_t      getNativeEndianness()
00048 #ifdef CINDER_LITTLE_ENDIAN
00049         { return STREAM_LITTLE_ENDIAN; }
00050 #else
00051         { return STREAM_BIG_ENDIAN; }
00052 #endif
00053  
00055     const fs::path&     getFileName() const { return mFileName; }
00057     void                setFileName( const fs::path &aFileName ) { mFileName = aFileName; }
00058 
00060     bool        getDeleteOnDestroy() const { return mDeleteOnDestroy; }
00062     void        setDeleteOnDestroy( bool enable = true ) { mDeleteOnDestroy = enable; }
00063 
00065     virtual off_t       tell() const = 0;
00066 
00068     virtual void        seekAbsolute( off_t absoluteOffset ) = 0;
00069     
00071     virtual void        seekRelative( off_t relativeOffset ) = 0;
00072  
00073  protected:
00074     StreamBase() : mDeleteOnDestroy( false ) {}
00075 
00076     fs::path                mFileName;
00077     bool                    mDeleteOnDestroy;
00078 };
00079 
00080 class OStream : public virtual StreamBase {
00081  public:
00082     virtual ~OStream() {}
00083 
00085     void        write( const std::string &s ) { writeData( s.c_str(), s.length() + 1 ); }
00086     void        write( const ci::fs::path &p ) { writeData( p.string().c_str(), p.string().length() + 1 ); }
00087     template<typename T>
00088     void        write( T t ) { IOWrite( &t, sizeof(T) ); }
00089     template<typename T>
00090     void        writeEndian( T t, uint8_t endian ) { if ( endian == STREAM_BIG_ENDIAN ) writeBig( t ); else writeLittle( t ); }
00091     template<typename T>
00092     void        writeBig( T t );
00093     template<typename T>
00094     void        writeLittle( T t );
00095 
00096     void        write( const Buffer &buffer );
00097     void        writeData( const void *src, size_t size );
00098 
00099  protected:
00100     OStream() : StreamBase() {}
00101  
00102     virtual void        IOWrite( const void *t, size_t size ) = 0;
00103 };
00104 
00105 
00106 typedef std::shared_ptr<class OStream>  OStreamRef;
00107 
00108 class IStream : public virtual StreamBase {
00109  public:
00110     virtual ~IStream() {};
00111 
00112     template<typename T>
00113     void        read( T *t ) { IORead( t, sizeof(T) ); }
00114     template<typename T>
00115     void        readEndian( T *t, uint8_t endian ) { if ( endian == STREAM_BIG_ENDIAN ) readBig( t ); else readLittle( t ); }
00116     template<typename T>
00117     void        readBig( T *t );
00118     template<typename T>
00119     void        readLittle( T *t );
00120 
00122     void        read( std::string *s );
00123     void        read( ci::fs::path *p );
00124     void        readFixedString( char *t, size_t maxSize, bool nullTerminate );
00125     void        readFixedString( std::string *t, size_t size );
00126     std::string readLine();
00127     
00128     void            readData( void *dest, size_t size );
00129     virtual size_t  readDataAvailable( void *dest, size_t maxSize ) = 0;
00130 
00131     virtual off_t       size() const = 0;   
00132     virtual bool        isEof() const = 0;
00133 
00134  protected:
00135     IStream() : StreamBase() {}
00136 
00137     virtual void        IORead( void *t, size_t size ) = 0;
00138         
00139     static const int    MINIMUM_BUFFER_SIZE = 8; // minimum bytes of random access a stream must offer relative to the file start
00140 };
00141 typedef std::shared_ptr<IStream>        IStreamRef;
00142 
00143 
00144 class IoStream : public IStream, public OStream {
00145  public:
00146     IoStream() : IStream(), OStream() {}
00147     virtual ~IoStream() {}
00148 };
00149 typedef std::shared_ptr<IoStream>       IoStreamRef;
00150 
00151 
00152 typedef std::shared_ptr<class IStreamFile>  IStreamFileRef;
00153 
00154 class IStreamFile : public IStream {
00155  public:
00157     static IStreamFileRef create( FILE *file, bool ownsFile = true, int32_t defaultBufferSize = 2048 );
00158     ~IStreamFile();
00159 
00160     size_t      readDataAvailable( void *dest, size_t maxSize );
00161     
00162     void        seekAbsolute( off_t absoluteOffset );
00163     void        seekRelative( off_t relativeOffset );
00164     off_t       tell() const;
00165     off_t       size() const;
00166     
00167     bool        isEof() const;
00168     
00169     FILE*       getFILE() { return mFile; }
00170 
00171  protected:
00172     IStreamFile( FILE *aFile, bool aOwnsFile = true, int32_t aDefaultBufferSize = 2048 );
00173 
00174     virtual void        IORead( void *t, size_t size );
00175     size_t              readDataImpl( void *dest, size_t maxSize );
00176  
00177     FILE                        *mFile;
00178     bool                        mOwnsFile;
00179     size_t                      mBufferSize, mDefaultBufferSize;
00180     std::shared_ptr<uint8_t>    mBuffer;
00181     off_t                       mBufferOffset; // actual offset to do IO from; incremented by IO
00182     off_t                       mBufferFileOffset; // beginning of the buffer in the file
00183     mutable off_t               mSize;
00184     mutable bool                mSizeCached;
00185 };
00186 
00187 
00188 typedef std::shared_ptr<class OStreamFile>  OStreamFileRef;
00189 
00190 class OStreamFile : public OStream {
00191   public:
00193     static OStreamFileRef   create( FILE *file, bool ownsFile = true );
00194     ~OStreamFile();
00195 
00196     virtual off_t       tell() const;
00197     virtual void        seekAbsolute( off_t absoluteOffset );
00198     virtual void        seekRelative( off_t relativeOffset );
00199 
00200     FILE*               getFILE() { return mFile; }
00201 
00202     
00203   protected:
00204     OStreamFile( FILE *aFile, bool aOwnsFile = true );
00205 
00206     virtual void        IOWrite( const void *t, size_t size );
00207 
00208     FILE*               mFile;
00209     bool                mOwnsFile;
00210 };
00211 
00212 
00213 typedef std::shared_ptr<class IoStreamFile>     IoStreamFileRef;
00214 
00215 class IoStreamFile : public IoStream {
00216  public:
00218     static IoStreamFileRef create( FILE *file, bool ownsFile = true, int32_t defaultBufferSize = 2048 );
00219     ~IoStreamFile();
00220 
00221     size_t      readDataAvailable( void *dest, size_t maxSize );
00222     
00223     void        seekAbsolute( off_t absoluteOffset );
00224     void        seekRelative( off_t relativeOffset );
00225     off_t       tell() const;
00226     off_t       size() const;
00227     
00228     bool        isEof() const;
00229     
00230     FILE*       getFILE() { return mFile; }
00231 
00232  protected:
00233     IoStreamFile( FILE *aFile, bool aOwnsFile = true, int32_t aDefaultBufferSize = 2048 );
00234     
00235     virtual void        IORead( void *t, size_t size );
00236     size_t              readDataImpl( void *dest, size_t maxSize );
00237     virtual void        IOWrite( const void *t, size_t size );
00238  
00239     FILE                        *mFile;
00240     bool                        mOwnsFile;
00241     int32_t                     mBufferSize, mDefaultBufferSize;
00242     std::shared_ptr<uint8_t>    mBuffer;
00243     off_t                       mBufferOffset; // actual offset to do IO from; incremented by IO
00244     off_t                       mBufferFileOffset; // beginning of the buffer in the file
00245     mutable off_t               mSize;
00246     mutable bool                mSizeCached;
00247 };
00248 
00249 
00250 typedef std::shared_ptr<class IStreamMem>   IStreamMemRef;
00251 class IStreamMem : public IStream {
00252  public:
00254     static IStreamMemRef        create( const void *data, size_t size );
00255     ~IStreamMem();
00256 
00257     size_t      readDataAvailable( void *dest, size_t maxSize );
00258     
00259     void        seekAbsolute( off_t absoluteOffset );
00260     void        seekRelative( off_t relativeOffset );
00262     off_t       tell() const;
00264     off_t       size() const { return static_cast<off_t>( mDataSize ); }
00265 
00267     bool        isEof() const;
00268     
00270     const void* getData() { return reinterpret_cast<const void*>( mData ); }
00271 
00272  protected:
00273     IStreamMem( const void *aData, size_t aDataSize );
00274 
00275     virtual void    IORead( void *t, size_t size );
00276  
00277     const uint8_t   *mData;
00278     size_t          mDataSize;
00279     size_t          mOffset;
00280 };
00281 
00282 
00283 typedef std::shared_ptr<class OStreamMem>       OStreamMemRef;
00284 
00285 class OStreamMem : public OStream {
00286  public:
00287     static OStreamMemRef        create( size_t bufferSizeHint = 4096 ) { return std::shared_ptr<OStreamMem>( new OStreamMem( bufferSizeHint ) ); }
00288 
00289     ~OStreamMem();
00290 
00291     virtual off_t       tell() const { return static_cast<off_t>( mOffset ); }
00292     virtual void        seekAbsolute( off_t absoluteOffset );
00293     virtual void        seekRelative( off_t relativeOffset );
00294 
00295     void*               getBuffer() { return mBuffer; }
00296     
00297  protected:
00298     OStreamMem( size_t bufferSizeHint );
00299 
00300     virtual void        IOWrite( const void *t, size_t size );
00301 
00302     void            *mBuffer;
00303     size_t          mDataSize;
00304     size_t          mOffset;
00305 };
00306 
00307 
00308 // This class is a utility to save and restore a stream's state
00309 class IStreamStateRestore {
00310  public:
00311     IStreamStateRestore( IStream &aStream ) : mStream( aStream ), mOffset( aStream.tell() ) {}
00312     ~IStreamStateRestore() {
00313         mStream.seekAbsolute( mOffset );
00314     }
00315     
00316  private:
00317     IStream     &mStream;
00318     off_t       mOffset;
00319 };
00320 
00322 IStreamFileRef  loadFileStream( const fs::path &path );
00324 OStreamFileRef  writeFileStream( const fs::path &path, bool createParents = true );
00326 IoStreamFileRef readWriteFileStream( const fs::path &path );
00327 
00329 void loadStreamMemory( IStreamRef is, std::shared_ptr<uint8_t> *resultData, size_t *resultDataSize );
00331 Buffer loadStreamBuffer( IStreamRef is );
00332 
00333 
00334 // Stream exception
00335 class StreamExc : public Exception {
00336 };
00337 
00338 class StreamExcOutOfMemory : public StreamExc {
00339 };
00340 
00341 #ifndef __OBJC__
00342 class cinder_stream_source {
00343  public:
00344     typedef char char_type;
00345     typedef boost::iostreams::source_tag category;
00346 
00347     cinder_stream_source( cinder::IStreamRef aStream ) : mStream( aStream ) {}
00348 
00349     std::streamsize read( char *s, std::streamsize n )
00350     {
00351         if( mStream->isEof() )
00352             return -1;
00353         
00354         return (std::streamsize)mStream->readDataAvailable( s, (size_t)n );
00355     }
00356 
00357  protected:
00358     IStreamRef      mStream; // a little kludgy but this is for convenience
00359 };
00360 
00361 typedef boost::iostreams::stream<cinder_stream_source> cinder_istream;
00362 
00363 class cinder_stream_sink {
00364  public:
00365     typedef char char_type;
00366     typedef boost::iostreams::sink_tag category;
00367 
00368     cinder_stream_sink( OStreamRef aStream ) : mStream( aStream ) {}
00369 
00370     std::streamsize write( const char *s, std::streamsize n )
00371     {
00372         mStream->writeData( s, (size_t)n );
00373         return n;
00374     }
00375 
00376  protected:
00377     OStreamRef      mStream;
00378 };
00379 
00380 typedef boost::iostreams::stream<cinder_stream_sink> cinder_ostream;
00381 
00382 class cinder_stream_bidirectional_device {
00383  public:
00384     typedef char char_type;
00385     typedef boost::iostreams::seekable_device_tag category;
00386 
00387     cinder_stream_bidirectional_device( cinder::IoStreamRef aStream ) : mStream( aStream ) {}
00388 
00389     std::streamsize read( char *s, std::streamsize n )
00390     {
00391         return static_cast<std::streamsize>( mStream->readDataAvailable( s, (size_t)n ) );
00392     }
00393 
00394     std::streamsize write( const char *s, std::streamsize n )
00395     {
00396         mStream->writeData( s, (size_t)n );
00397         return n;
00398     }
00399 
00400     boost::iostreams::stream_offset seek( boost::iostreams::stream_offset off, std::ios_base::seekdir way)
00401     {
00402         if( way == std::ios_base::beg ) {
00403             mStream->seekAbsolute( (off_t)off );
00404         }
00405         else if( way == std::ios_base::cur ) {
00406             mStream->seekRelative( (off_t)off );
00407         }
00408         else { // way == std::ios_base::end
00409             mStream->seekAbsolute( -(off_t)off );
00410         }
00411         return mStream->tell();
00412     }
00413 
00414  protected:
00415     IoStreamRef     mStream;
00416 };
00417 
00418 typedef boost::iostreams::stream<cinder_stream_bidirectional_device> cinder_iostream;
00419 
00420 #endif // ! __OBJC__
00421 
00422 } // namespace cinder