CnC
 All Classes Namespaces Functions Variables Typedefs Enumerator Friends
serializer.h
00001 /* *******************************************************************************
00002  *  Copyright (c) 2007-2014, Intel Corporation
00003  *
00004  *  Redistribution and use in source and binary forms, with or without
00005  *  modification, are permitted provided that the following conditions are met:
00006  *
00007  *  * Redistributions of source code must retain the above copyright notice,
00008  *    this list of conditions and the following disclaimer.
00009  *  * Redistributions in binary form must reproduce the above copyright
00010  *    notice, this list of conditions and the following disclaimer in the
00011  *    documentation and/or other materials provided with the distribution.
00012  *  * Neither the name of Intel Corporation nor the names of its contributors
00013  *    may be used to endorse or promote products derived from this software
00014  *    without specific prior written permission.
00015  *
00016  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00017  *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00018  *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
00019  *  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
00020  *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
00021  *  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
00022  *  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00023  *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
00024  *  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
00025  *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00026  ********************************************************************************/
00027 
00028 /*
00029   Marshalling interface for distributed CnC.
00030  */
00031 
00032 #ifndef _CNC_SERIALIZER_H_
00033 #define _CNC_SERIALIZER_H_
00034 
00035 #include <cnc/internal/cnc_api.h>
00036 #include <cnc/internal/scalable_object.h>
00037 #include <complex>
00038 #include <vector>
00039 
00040 #include <cnc/internal/cnc_stddef.h> // size_type
00041 
00042 #include <memory> // std::allocator
00043 
00044 namespace CnC
00045 {
00046     class CNC_API serializer;
00047     class no_alloc{};
00048 
00049     //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00050 
00051 /**
00052    \defgroup serialization Serialization
00053     @{
00054    
00055     There are three possible ways of setting up the serialization
00056     of your struct resp. class:
00057    
00058     \defgroup ser_bit Serialization of simple structs/classes without pointers or virtual functions.
00059      @{
00060        If your struct does not contain pointers (nor virtual
00061        functions), instances of it may be serialized by simply
00062        copying them bytewise. In this case, it suffices to
00063        add your struct to the category bitwise_serializable which is done
00064        by providing the following global function:
00065        \code
00066            inline bitwise_serializable serializer_category( const Your_Class * ) {
00067                return bitwise_serializable();
00068            }
00069        \endcode
00070        As a short-cut, you can just use #CNC_BITWISE_SERIALIZABLE( T ) to declare your class as bitwise serializable:
00071        \code
00072        CNC_BITWISE_SERIALIZABLE( my_class );
00073        \endcode
00074     @}
00075     \defgroup ser_exp Non bitwise serializable objects
00076      @{
00077        You'll have to provide a serializer function for your class.
00078        One way of doing this is to derive your class from
00079        serializable and to provide a method "serialize":
00080        \code
00081            class Your_class : public serializable {
00082                ...
00083            public:
00084                void serialize( CnC::serializer & );
00085                ...
00086            };
00087        \endcode
00088        In the implementation of this serialize method you should
00089        use the operator& of the serializer class. Here is an example:
00090        \code
00091            class Your_class : public serializable {
00092            private:
00093                double x;
00094                float  y;
00095                int    len;
00096                char*  arr; // array of lenth len
00097                ...
00098            public:
00099                void serialize( CnC::serializer & buf ) {
00100                    buf & x
00101                        & y
00102                        & len;
00103                    buf & CnC::chunk< char >( arr, len_arr ); // chunk declared below
00104                    ...
00105                }
00106            }
00107        \endcode
00108        This method will called for both packing and unpacking
00109        (what will actually be done, depends on the internal mode
00110        of the serializer.)
00111        \n
00112        Alternatively, you can provide a global function \n
00113        \code
00114            void serialize( serializer& buf, Your_class& obj );
00115        \endcode
00116        which should use the operator& of the serializer class
00117        (same as for method 2. above). This is useful in cases
00118        where you want to leave your class unchanged.
00119     @}
00120     \defgroup ser_ptr Marshalling pointer types (e.g. items which are pointers)
00121      @{
00122        A common optimization used by CnC programs targeting the
00123        shared-memory runtime is to store pointers to large objects
00124        inside of item collections instead of copies of the objects.
00125        This is done to minimize the overhead of copying into and out
00126        of the item collection.  Programs written in this style can be
00127        sued with distCnC, but special care needs to be taken.
00128        \n
00129        Distributed CnC requires that data stored in item collections be
00130        serializable in order to communicate data between different
00131        processes.  Consequently, the programmer must modify such
00132        pointer-using CnC programs in two ways in order to use the
00133        distributed-memory runtime.  First, the programmer must provide a
00134        specialization of CnC::serialize for the pointer type pointing to T:
00135        \code
00136        void CnC::serialize( CnC::serializer & ser, T *& ptr ) {
00137            ser & CnC::chunk< Your_class[, allocator] >( ptr, 1 );
00138        }
00139        \endcode
00140         Please refer to CnC::chunk to learn about appropriate memory
00141         management.
00142        \n
00143        If the pointer represents a single object and not a C-style array,
00144        then the programmer may the use convenience macro:
00145        \code CNC_POINTER_SERIALIZABLE( T ); \endcode
00146        This macro implements the above method for the case when the
00147        pointer refers to only a single object (length == 1).  Note that
00148        if T is a type that contains template parameters, the programmer
00149        should create a typedef of the specific type being serialized and
00150        use this type as the argument for the macro.
00151        \n
00152        Next, programmers must ensure that type T itself implements a
00153        default constructor and is serializable by itself. The latter 
00154        as achieved as described above.
00155        It is the same method that programmers must provide if storing
00156        copies of objects instead of pointers to objects.
00157        \n
00158        Reading from and writing to item collections is not different in
00159        this style of writing CnC programs.  However, there are a few
00160        things to note:
00161        \n
00162        When an object is communicated from one process to another, an
00163        item must be allocated on the receiving end. By default, CnC::chunk
00164        uses the default allocator for this and so requires the default constructor 
00165        of T. After object creation, any fields are updated by the provided
00166        serialize methods. Thios default behavior simplifies the object
00167        allocation/deallocation: most of it is done automatically behind the scenes.
00168        The programmer should just pass an unitialized pointer to
00169        get().  The pointer will then point to the allocated object/array after
00170        the get completes.
00171        \n
00172        In advanced and potentially dangerous setups, the programmer might wish
00173        to store the object into memory that has been previously allocated.
00174        This can be done by an explicit copy and explicit object lifetime control.
00175        Please note that such a scenario can easily lead to uses of CnC which 
00176        do not comply with CnC's methodology, e.g. no side effects in steps and
00177        dynamic single assigments.
00178    
00179        \defgroup seralloc Serialization of std::shared_ptr
00180        @{
00181          std::shared_ptr are normally supported out-of-the-box (given the underlying type
00182          is serializable (similar to \ref ser_ptr).
00183    
00184          Only shared_ptrs to more complex pointer-types require special treatment
00185          to guarantee correct garbage collection. If
00186          - using std::shared:ptr
00187          - AND the pointer-class holds data which is dynamically allocated
00188          - AND this dynamic memory is NOT allocated in the default constructor but during marshalling with CnC::chunk
00189    
00190          then (and only then) you might want to use construct_array and destruct_array.
00191          It is probably most convenient to 
00192          - use the standard CnC::chunk mechanism during serialization
00193          - allocate dynamic memory in the constructors with construct_array
00194          - and deallocate it in the destructor using destruct_array
00195    
00196          This mechanism is used in the cholesky example which comes with this distribution.
00197    
00198          Alternatively you can use matching
00199          allocation/deallocation explicitly in your con/destructors and during serialization.
00200        @}
00201      @}
00202 **/
00203 
00204     class serializable {
00205     public:
00206         // must provide member function
00207         // void serialize( CnC::serializer& buf ) {
00208         //     /* use serializer::operator& for accessing buf */
00209         // }
00210     };
00211 
00212     template< class T > inline
00213     void serialize( serializer & buf, T & obj ) {
00214         obj.serialize( buf );
00215     }
00216 
00217 
00218     /// Specifies serialization category: explicit serialization via a "serialize" function
00219     class explicitly_serializable {};
00220 
00221     /// General case: a type belongs to the category explicitly_serializable,
00222     /// unless there is a specialization of the function template
00223     /// "serializer_category" (see below).
00224     template< class T > inline
00225     explicitly_serializable serializer_category( const T * )
00226     {
00227         return explicitly_serializable();
00228     }
00229 
00230     /// simple structs/classes are bitwise serializable.
00231 
00232     /// Specifies serialization category: byte-wise copy
00233     class bitwise_serializable {};
00234 
00235     /// \def CNC_BITWISE_SERIALIZABLE( T )
00236     /// Convenience macro for defining that a type should be treated
00237     /// as bitwise serializable:
00238 #   define CNC_BITWISE_SERIALIZABLE( T ) \
00239         inline CnC::bitwise_serializable serializer_category( const T * ) { \
00240             return CnC::bitwise_serializable(); \
00241         }
00242 
00243     //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00244 
00245     template< class A, class B > inline
00246     void serialize( CnC::serializer & buf, std::pair< A, B > & obj ) {
00247         buf & obj.first & obj.second;
00248     }
00249 
00250     template< class T >
00251     inline CnC::bitwise_serializable serializer_category( const std::complex< T > * ) {
00252         return CnC::bitwise_serializable();
00253     }
00254 
00255     //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00256 
00257     /**
00258        \class chunk
00259        \brief Serialization of arrays with and without automatic memory handling.
00260        
00261        Specified (to serializer::operator&) via
00262        \code
00263            CnC::chunk< type[, allocator] >( your_ptr, len )
00264        \endcode
00265        where your_ptr is a pointer variable to your array of <type>s.
00266        
00267        If the allocator is the special type CnC::no_alloc, the runtime assumes that 
00268        the programer appropriately allocates (if ser.is_unpacking())
00269        and deallocates (if ser:is_cleaning_up()) the array. This implies that
00270        the using serialize method/function needs to make sure it passes valid and correct
00271        pointers to chunk.
00272         
00273        If not CnC::no_alloc , the Allocator class must meet "allocator" requirements of ISO C++ Standard, Section 20.1.5
00274         
00275        When unpacking, your_ptr must be NULL, and it is allocated automatically using
00276        the given allocator and when cleaning up, your_ptr is deallocated accordingly.
00277        
00278        If using an allocator (in particular hre default std::allocator) object/array 
00279        allocation and deallocation is greatly simplified for the programmer.
00280        There is no need to allocate and/or deallocate objects/arrays manually.
00281     **/
00282     template< class T, class Allocator = std::allocator< T > >
00283     struct chunk
00284     {
00285         chunk( T *& arr, size_type len ) : m_arrVar( arr ), m_len( len ) {}
00286         template< template< class I > class Range, class J >
00287         chunk( T *& arr, const Range< J > & range )
00288             : m_arrVar( arr ), m_len( range.size() )
00289         {
00290             CNC_ASSERT( range.begin() == 0 );
00291         }
00292 
00293         T *& m_arrVar;
00294         size_type m_len;
00295     };
00296 
00297     //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00298 
00299     /// Convenience macro for declaring serializable pointers.
00300     /// The object being pointed to should be serializable.
00301 
00302 #    define CNC_POINTER_SERIALIZABLE( T )                       \
00303     namespace CnC {                                             \
00304         void serialize( serializer & ser, T *& t ) {  \
00305             ser & chunk< T >(t, 1);                        \
00306         }                                                       \
00307     }
00308     
00309     //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00310     //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00311 
00312     /// \defgroup serspec Automatic serialization of built-in types.
00313     /// They can be copied byte-wise.
00314     ///
00315     /// In order to add your own class to the category bitwise_serializable,
00316     /// add a specialization of "serializer_category" returning bitwise_serializable for your class.
00317     /// (e.g. by using CnC::CNC_BITWISE_SERIALIZABLE).
00318     ///  @{
00319     CNC_BITWISE_SERIALIZABLE( bool );               ///< bool is bitwise serializable
00320     CNC_BITWISE_SERIALIZABLE( char );               ///< char is bitwise serializable
00321     CNC_BITWISE_SERIALIZABLE( signed char );        ///< signed char is bitwise serializable
00322     CNC_BITWISE_SERIALIZABLE( unsigned char );      ///< unsigend char is bitwise serializable
00323     CNC_BITWISE_SERIALIZABLE( short );              ///< short is bitwise serializable
00324     CNC_BITWISE_SERIALIZABLE( unsigned short );     ///< unsigend short is bitwise serializable
00325     CNC_BITWISE_SERIALIZABLE( int );                ///< int is bitwise serializable
00326     CNC_BITWISE_SERIALIZABLE( unsigned int );       ///< unsigned int is bitwise serializable
00327     CNC_BITWISE_SERIALIZABLE( long );               ///< long is bitwise serializable
00328     CNC_BITWISE_SERIALIZABLE( unsigned long );      ///< unsigned long is bitwise serializable
00329     CNC_BITWISE_SERIALIZABLE( long long );          ///< long long is bitwise serializable
00330     CNC_BITWISE_SERIALIZABLE( unsigned long long ); ///< unsigned long long is bitwise serializable
00331     CNC_BITWISE_SERIALIZABLE( float );              ///< float is bitwise serializable
00332     CNC_BITWISE_SERIALIZABLE( double );             ///< double is bitwise serializable
00333     CNC_BITWISE_SERIALIZABLE( long double );        ///< long double is bitwise serializable
00334     ///  @}
00335 
00336     //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00337     //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00338 
00339     namespace Internal {
00340         class Buffer;
00341         class BufferAccess;
00342     }
00343 
00344     /// \brief Handles serilialization of data-objects.
00345     ///
00346     /// objects of this class are passed to serialization methods/functions.
00347     /// Try to use operator & only; using anything else is potentially dangerous.
00348     class CNC_API serializer : public Internal::scalable_object
00349     {
00350     public:
00351         enum Mode {
00352             MODE_PACKED_SIZE,
00353             MODE_PACK,
00354             MODE_UNPACK,
00355             MODE_CLEANUP
00356         };
00357 
00358         inline serializer( bool addCRC = false, bool addSize = false );
00359         inline ~serializer();
00360 
00361         /// (Re)allocates a buffer of the given size.
00362         inline void resize( size_type len );
00363 
00364         /// Swap internal representations of the given two serializers.
00365         friend void swap( serializer & s1, serializer & s2 );
00366 
00367         inline void set_mode_packed_size();
00368         inline void set_mode_pack();
00369         inline void set_mode_unpack();
00370         inline void set_mode_cleanup();
00371         inline void set_mode_pack( bool addCRC, bool addSize );
00372         inline void set_mode_unpack( bool addCRC, bool addSize );
00373         bool is_packing() const { return m_mode == MODE_PACK; }
00374         bool is_unpacking() const { return m_mode == MODE_UNPACK; }
00375         bool is_cleaning_up() const { return m_mode == MODE_CLEANUP; }
00376         size_type get_size() const { return m_packedSize; }
00377         /// @return size of header
00378         inline size_type get_header_size() const;
00379         /// @return the number of bytes already packed into the buffer
00380         inline size_type get_body_size() const;
00381         /// @return total number of bytes in buffer (header + data)
00382         inline size_type get_total_size() const;
00383         /// @return pointer to message header
00384         inline void * get_header() const;
00385         /// @return pointer to the message body (without header)
00386         inline void * get_body() const;
00387         /// @return body size from header, -1 if something goes wrong
00388         inline size_type unpack_header() const;
00389 
00390         /// Top-level packing interface, to be used in the "serialize"
00391         /// function of your classes.
00392         /// Applies the serializer to one object resp. an array of objects
00393         /// (e.g. packing them into the serializer or unpacking them from
00394         /// the serializer).
00395         /// Dispatches w.r.t. the packing category of the object's type
00396         /// (i.e. byte-wise copying or object serialization).
00397         template< class T > serializer & operator&( T & var );
00398         template< class T > serializer & operator&( const T & var );
00399         //template< class T > serializer & operator&( Internal::array_no_alloc_type< T > a ); //< used via array_no_alloc( yourArr, len )
00400         template< class T, class Allocator >
00401             serializer & operator&( chunk< T, Allocator > a ); //< used via auto_array( yourArrVar, len )
00402 
00403         class reserved;
00404         /// reserve current position in buffer to be filled later with a call to "complete"
00405         /// rserve/complete supported for bitwise-serializable types only
00406         template< class T > reserved reserve( const T & _obj );
00407         
00408         /// fill in data at position that has been reserved before
00409         /// rserve/complete supported for bitwise-serializable types only
00410         template< class T > void complete( const reserved & r, const T & _obj );
00411 
00412         // Low-level packing interface:
00413         template< class T > inline size_type packed_size( const T * arr, size_type len ) const {
00414             return packed_size( arr, len, serializer_category( arr ) );
00415         }
00416         template< class T > inline void pack( const T * arr, size_type len ) {
00417             pack( arr, len, serializer_category( arr ) );
00418         }
00419         template< class T > inline void unpack( T * arr, size_type len ) {
00420             unpack( arr, len, serializer_category( arr ) );
00421         }
00422         template< class T > inline void cleanup( T * arr, size_type len ) {
00423             cleanup( arr, len, serializer_category( arr ) );
00424         }
00425         template< class T > inline size_type packed_size( const T & var ) const { return packed_size( &var, 1 ); }
00426         template< class T > inline void pack( const T & var ) { return pack( &var, 1 ); }
00427         template< class T > inline void unpack( T & var ) { return unpack( &var, 1 ); }
00428         template< class T > inline void cleanup( T & var ) { return cleanup( &var, 1 ); }
00429 
00430         inline serializer( const serializer& );
00431 
00432         /// Allocates an array of type T and size num in pointer variable arrVar
00433         template< class T, class Allocator = std::allocator< T > >
00434         struct construct_array
00435         {
00436             construct_array( T *& arrVar, size_type num );
00437         };
00438         /// destructs the array of type T and isze num at arrVar and resets arrVar to NULL.
00439         template< class T, class Allocator = std::allocator< T > >
00440         struct destruct_array
00441         {
00442             destruct_array( T *& arrVar, size_type num );
00443         };
00444         ///  @}
00445         template< class T >
00446         struct construct_array< T, no_alloc >
00447         {
00448             construct_array( T *&, size_type ){}
00449         };
00450         template< class T >
00451         struct destruct_array< T, no_alloc >
00452         {
00453             destruct_array( T *&, size_type ){}
00454         };
00455 
00456     private:
00457         template< class T > inline size_type packed_size( const T * arr, size_type len, bitwise_serializable ) const;
00458         template< class T > inline void pack( const T * arr, size_type len, bitwise_serializable );
00459         template< class T > inline void unpack( T * arr, size_type len, bitwise_serializable );
00460         template< class T > inline void cleanup( T * arr, size_type len, bitwise_serializable );
00461         template< class T > inline size_type packed_size( const T * arr, size_type len, explicitly_serializable ) const;
00462         template< class T > inline void pack( const T * arr, size_type len, explicitly_serializable );
00463         template< class T > inline void unpack( T * arr, size_type len, explicitly_serializable );
00464         template< class T > inline void cleanup( T * arr, size_type len, explicitly_serializable );
00465 
00466 
00467         /// Don't allow copying
00468         void operator=( const serializer& );
00469 
00470         /// misc:
00471         size_type remaining_capacity() const;
00472 
00473         Internal::Buffer * m_buf;
00474         size_type          m_packedSize; /// < for MODE_PACKED_SIZE only
00475         Mode               m_mode;
00476         
00477         friend class Internal::BufferAccess;
00478     };
00479 
00480     ///  @}
00481 
00482 } // namespace CnC
00483 
00484 #include <cnc/internal/dist/Serializer.impl.h>
00485 
00486 #endif // _CNC_SERIALIZER_H_
 All Classes Namespaces Functions Variables Typedefs Enumerator Friends