• Main Page
  • Namespaces
  • Classes
  • Files
  • File List

mumulib.h

Go to the documentation of this file.
00001 /**
00002  * @file   mumulib.h
00003  * @author Sergey Satskiy
00004  * @brief  C++ template library for mumufs.
00005  *
00006  * Copyright (C) 2007 - 2009 Sergey Satskiy.
00007  *
00008  * This file is released under the GPL.
00009  */
00010 
00011 
00012 
00013 #ifndef MUMULIB_H
00014 #define MUMULIB_H
00015 
00016 #include <stdio.h>
00017 #include <errno.h>
00018 #include <sys/types.h>
00019 #include <sys/stat.h>
00020 #include <fcntl.h>
00021 #include <string.h>
00022 #include <unistd.h>
00023 #include <sys/select.h>
00024 #include <sys/time.h>
00025 
00026 #include <string>
00027 #include <stdexcept>
00028 
00029 
00030     /**
00031      * @brief The library namespace
00032      */
00033 namespace mumu
00034 {
00035         /**
00036          * @brief The way to open a file
00037          */
00038     enum RWMode
00039     {
00040         ReadOnly  = O_RDONLY,
00041         WriteOnly = O_WRONLY,
00042         ReadWrite = O_RDWR
00043     };
00044 
00045         /**
00046          * @brief The template represents a single file on mumufs
00047          */
00048     template <typename  Type>
00049     class Entry
00050     {
00051         private:
00052             int     fd;     //!< File descritor
00053             RWMode  mode;   //!< How the file was opened
00054 
00055         public:
00056                 /**
00057                  * @brief Constructs a single entry. Creates the corresponding
00058                  *        file if it does not exist
00059                  * @param path Path to a file on mumufs.
00060                  * @param openMode How to open a file. Default is ReadWrite.
00061                  * @param isBlocking file opening mode. Default is blocking.
00062                  * @exception std::runtime_error in case of problems
00063                  *
00064                  * The blocking mode matters for read(...) calls only
00065                  */
00066             explicit Entry( const std::string &  path,
00067                             RWMode               openMode = ReadWrite,
00068                             bool                 isBlocking = true ) : fd( -1 ), mode( openMode )
00069             {
00070                 int     flags( openMode | O_CREAT );
00071                 if ( !isBlocking )  flags |= O_NONBLOCK;
00072 
00073                 fd = open( path.c_str(), flags, S_IRUSR | S_IWUSR |
00074                                                 S_IRGRP | S_IWGRP |
00075                                                 S_IROTH | S_IWOTH );
00076                 if ( fd < 0 )       throw std::runtime_error( "Cannot open the given entry." );
00077             }
00078 
00079                 /**
00080                  * @brief Closes the file
00081                  */
00082             ~Entry()
00083             {
00084                 close( fd );
00085             }
00086 
00087                 /**
00088                  * @brief Constructs a copy of entry
00089                  * @param rhs Pattern
00090                  * @exception std::runtime_error in case of errors
00091                  */
00092             Entry( const Entry &  rhs ) : fd( dup( rhs.fd ) )
00093             {
00094                 if ( fd < 0 )       throw std::runtime_error( "Cannot duplicate file descriptor." );
00095             }
00096 
00097         private:
00098                 /* No way for assigning */
00099             Entry &  operator= ( const Entry &  rhs );
00100 
00101         protected:
00102 
00103                 /**
00104                  * @brief Utility function to add two timeval values
00105                  * @param Left First timeval
00106                  * @param Right Second timeval
00107                  * @return Addition result
00108                  */
00109             struct timeval AddTimeval( const struct timeval &  Left,
00110                                        const struct timeval &  Right ) const
00111             {
00112                 struct timeval      Result;
00113 
00114                 Result.tv_sec = Left.tv_sec + Right.tv_sec;
00115                 Result.tv_usec = Left.tv_usec + Right.tv_usec;
00116                 while ( Result.tv_usec >= 1000000 )
00117                 {
00118                     ++Result.tv_sec;
00119                     Result.tv_usec -= 1000000;
00120                 }
00121                 return Result;
00122             }
00123 
00124                 /**
00125                  * @brief Utility function to subtract two timeval values
00126                  * @param Left First timeval
00127                  * @param Right Second timeval
00128                  * @return Subtraction result
00129                  */
00130             struct timeval SubTimeval( const struct timeval &  Left,
00131                                        const struct timeval &  Right ) const
00132             {
00133                 struct timeval      Result;
00134 
00135                 Result.tv_sec = Left.tv_sec - Right.tv_sec;
00136                 Result.tv_usec = Left.tv_usec - Right.tv_usec;
00137                 while ( Result.tv_usec < 0 )
00138                 {
00139                     --Result.tv_sec;
00140                     Result.tv_usec += 1000000;
00141                 }
00142                 return Result;
00143             }
00144 
00145                 /**
00146                  * @brief Utility function to compare two timeval values
00147                  * @param Left First timeval
00148                  * @param Right Second timeval
00149                  * @return First < Second
00150                  */
00151             bool LessTimeval( const struct timeval &  Left,
00152                               const struct timeval &  Right ) const
00153             {
00154                 if ( Left.tv_sec < Right.tv_sec )   return true;
00155                 if ( Left.tv_sec > Right.tv_sec )   return false;
00156                 return Left.tv_usec < Right.tv_usec;
00157             }
00158 
00159                 /**
00160                  * @brief Retrieves file size in bytes
00161                  * @return File size in bytes
00162                  * @exception Throws std::runtime_error in case of fstat() call
00163                  * errors
00164                  */
00165             off_t GetFileSize( void ) const
00166             {
00167                 struct stat     fileInfo;
00168 
00169                 if ( fstat( fd, &fileInfo ) != 0 )
00170                 {
00171                     throw std::runtime_error( "Cannot get fstat" );
00172                 }
00173                 return fileInfo.st_size;
00174             }
00175 
00176         public:
00177 
00178                 /**
00179                  * @brief select(...) for the file on mumufs
00180                  * @param inTimeout select timeout. It could be NULL. If NULL then waits forever.
00181                  * @return true if Entry is ready for reading without blocking.
00182                  * @exception if the open mode is WriteOnly
00183                  */
00184             bool Select( struct timeval *  inTimeout )
00185             {
00186                 if ( mode == WriteOnly ) throw std::runtime_error( "Select for WriteOnly mode is not supported" );
00187 
00188                 fd_set             Set;
00189                 struct timeval     AbsoluteEnd;
00190                 struct timeval     Timeout;
00191                 struct timeval *   Argument( NULL );
00192 
00193                 if ( inTimeout != NULL )
00194                 {
00195                     Timeout = *inTimeout;
00196                     Argument = &Timeout;
00197                     gettimeofday( &AbsoluteEnd, NULL );
00198                     AbsoluteEnd = AddTimeval( AbsoluteEnd, Timeout );
00199                 }
00200 
00201 
00202                 FD_ZERO( &Set );
00203                 FD_SET( fd, &Set );
00204 
00205                 int         RetVal;
00206 
00207                 for ( ; ; )
00208                 {
00209                     RetVal = select( fd + 1, &Set, NULL, NULL, Argument );
00210 
00211                         // We have exactly 1 fd so this check is enough for this specific case
00212                     if ( RetVal >= 1 ) return true;
00213                     if ( RetVal == 0 ) return false;
00214 
00215                         // The last case is RetVal < 0 => error
00216                     if ( errno == EINTR )
00217                     {
00218                         // It is the timeout exceeded, we need to call select again
00219                         // We can reach this point only if we had !NULL timeout
00220                         FD_ZERO( &Set );
00221                         FD_SET( fd, &Set );
00222 
00223                         struct timeval  Current;
00224                         gettimeofday( &Current, NULL );
00225 
00226                         if ( LessTimeval( AbsoluteEnd, Current ) )    return false;
00227 
00228                         Timeout = SubTimeval( AbsoluteEnd, Current );
00229                         continue;
00230                     }
00231 
00232                     throw std::runtime_error( "Select call error." );
00233                 }
00234             }
00235 
00236                 /**
00237                  * @brief Write a raw buffer to the entry
00238                  * @param Buffer Pointer to the buffer
00239                  * @param BufferSize Size of the buffer
00240                  * @exception std::runtime_error in case of problems
00241                  */
00242             void Write( const unsigned char *  Buffer, size_t  BufferSize )
00243             {
00244                 if ( mode == ReadOnly ) throw std::runtime_error( "Cannot write into ReadOnly entry" );
00245 
00246                 for ( ; ; )
00247                 {
00248                     int     Bytes( write( fd, Buffer, BufferSize ) );
00249                     if ( Bytes == static_cast<int>(BufferSize) ) return;
00250 
00251                     if ( Bytes == -1 && errno == EINTR ) continue;
00252 
00253                     throw std::runtime_error( "Error writing to file descriptor." );
00254                 }
00255             }
00256 
00257                 /**
00258                  * @brief Write the given object to the entry.
00259                  *        Be carefull, this is the generic implementation which
00260                  *        supposes that the data occupies a consequent memory
00261                  *        address space.
00262                  * @param T Object to be written
00263                  * @exception std::runtime_error in case of problems
00264                  */
00265             void  Write( const Type &  T )
00266             {
00267                 this->Write( reinterpret_cast<const unsigned char *>(&T), sizeof( T ) );
00268             }
00269 
00270 
00271                 /**
00272                  * @brief Reads raw data into the given buffer
00273                  * @param Buffer Pointer to the buffer
00274                  * @param BufferSize Size of the provided buffer
00275                  * @return Number of bytes that were actually read or -1 if the
00276                  *         buffer is not big enough
00277                  *         0 if no data is available
00278                  * @exception std::runtime_error in case of errors
00279                  */
00280             int  Read( unsigned char *  Buffer, size_t  BufferSize )
00281             {
00282                 if ( mode == WriteOnly ) throw std::runtime_error( "Cannot read from WriteOnly entry" );
00283 
00284                 for ( ; ; )
00285                 {
00286                     int     Bytes( read( fd, Buffer, BufferSize ) );
00287                     if ( Bytes > 0 ) return Bytes;
00288                     if ( Bytes == -1 && errno == EINTR ) continue;
00289                     if ( Bytes == -1 && errno == EAGAIN ) return 0;
00290                     if ( Bytes == -1 && errno == EINVAL ) return -1;
00291 
00292                     throw std::runtime_error( "Error reading from file descriptor." );
00293                 }
00294             }
00295 
00296                 /**
00297                  * @brief reading of entry. It suits for blocking mode.
00298                  * @return A constructed object
00299                  */
00300             Type  Read( void )
00301             {
00302                 Type    T;
00303                 Read( T );
00304                 return T;
00305             }
00306 
00307                 /**
00308                  * @brief reading of entry. It suits for non-blocking mode.
00309                  * @param T A reference to the object to be filled
00310                  * @exception std::runtime_error in case of errors
00311                  * @return 0 - no new data available, positive value - # of bytes read
00312                  */
00313             int  Read( Type &  T )
00314             {
00315                 if ( mode == WriteOnly ) throw std::runtime_error( "Cannot read from WriteOnly entry" );
00316                 for ( ; ; )
00317                 {
00318                     int     Bytes( read( fd, &T, sizeof( T ) ) );
00319                     if ( Bytes == sizeof( T ) ) return Bytes;
00320 
00321                     if ( Bytes == -1 && errno == EINTR ) continue;
00322                     if ( Bytes == -1 && errno == EAGAIN ) return 0;
00323 
00324                     throw std::runtime_error( "Error reading from file descriptor" );
00325                 }
00326             }
00327     };
00328 
00329 
00330         /**
00331          * @brief Write specialisation for std::string.
00332          * @param T Object to be written
00333          * @exception std::runtime_error in case of problems
00334          */
00335     template <>
00336     void  Entry< std::string >::Write( const std::string &  T )
00337     {
00338         if ( mode == ReadOnly ) throw std::runtime_error( "Cannot write into ReadOnly entry" );
00339 
00340         size_t      size( T.size() + 1 );   // +1 is for trailing \0
00341         for ( ; ; )
00342         {
00343             int  Bytes( write( fd, T.c_str(), size ) );
00344             if ( Bytes == static_cast< int >( size ) ) return;
00345 
00346             if ( Bytes == -1 && errno == EINTR ) continue;
00347 
00348             throw std::runtime_error( "Error writing to file descriptor" );
00349         }
00350     }
00351 
00352         /**
00353          * @brief reading specilisation for std::string. It suits for non-blocking mode.
00354          * @param S A reference to the object to be filled
00355          * @exception std::runtime_error in case of errors
00356          * @return 0 - no new data available, positive value - # of bytes read
00357          */
00358     template <>
00359     int  Entry< std::string >::Read( std::string &  S )
00360     {
00361         if ( mode == WriteOnly ) throw std::runtime_error( "Cannot read from WriteOnly entry" );
00362 
00363         off_t           BufferSize( GetFileSize() + 1 );
00364         char *          Buffer( new char[ BufferSize ] );
00365 
00366         for ( ; ; )
00367         {
00368             int     Bytes( read( fd, Buffer, BufferSize ) );
00369 
00370             if ( Bytes > 0 && Bytes < static_cast<int>( BufferSize ) )
00371             {
00372                 if ( Buffer[ Bytes - 1 ] == '\0' )  S = std::string( Buffer, Bytes - 1 );
00373                 else                                S = std::string( Buffer, Bytes );
00374                 delete [] Buffer;
00375                 return Bytes;
00376             }
00377 
00378             if ( Bytes == -1 && errno == EAGAIN ) { delete [] Buffer; return 0; }   // Blocking IO
00379             if ( Bytes == -1 && errno == EINTR ) continue;
00380             if ( Bytes == -1 && errno == EINVAL )
00381             {
00382                 delete [] Buffer;
00383                 BufferSize = GetFileSize() + 1;
00384                 Buffer = new char[ BufferSize ];
00385                 continue;
00386             }
00387 
00388             delete [] Buffer;
00389             throw std::runtime_error( "Error reading from file descriptor" );
00390         }
00391     }
00392 
00393 
00394         /**
00395          * @brief reading specialisation for std::string. It suits for non-blocking mode.
00396          * @exception std::runtime_error in case of errors
00397          * @return 0 - no new data available, positive value - # of bytes read
00398          */
00399     template <>
00400     std::string  Entry< std::string >::Read( void )
00401     {
00402         std::string     S;
00403         Read( S );
00404         return S;
00405     }
00406 
00407 }   // End of mumu namespace
00408 
00409 
00410 #endif
00411