00001 #ifndef s11n_BIN64_H_INCLUDED 00002 #define s11n_BIN64_H_INCLUDED 1 00003 //////////////////////////////////////////////////////////////////////// 00004 // s11n.hpp: 00005 // Author: stephan beal <stephan@s11n.net> 00006 // License: Public Domain 00007 //////////////////////////////////////////////////////////////////////// 00008 00009 #include <s11n.net/s11n/base64enc.hpp> 00010 #include <s11n.net/s11n/base64dec.hpp> 00011 00012 namespace s11n { 00013 /** 00014 The base64 namespace encapsulates code for de/serializing binary 00015 data using base64 encoding/decoding. 00016 00017 Added in version 1.3.1. 00018 */ 00019 namespace base64 { 00020 00021 /** 00022 This is a helper for serializing binary data. It is a Serializable 00023 type, but does not meet all requirements for Serializables (namely, 00024 it is not DefaultConstructable). It is to be initialized with a 00025 pointer to some binary data (which is limited to a (char const *)) 00026 and the length of that data. 00027 00028 The intended usage goes something like this: 00029 00030 \code 00031 bindata_ser bin( myPtr, lengthOfMyPtr ); 00032 serialize( anS11nNode, bin ); 00033 \endcode 00034 00035 To deserialize it, use bindata_deser. 00036 00037 IMPORTANT WARNINGS: 00038 00039 Serializing binary data this way is not terribly efficient, due 00040 to the whole encoding/decoding process. Also most s11n data 00041 formats do some sort of entity translation when setting 00042 properties, and may peform very poorly when given huge 00043 inputs. Some formats may not like newlines in properties 00044 (simplexml_serializer comes to mind). Base64-encoded data is 00045 also larger than the original binary data. Thus the following 00046 recommendations: 00047 00048 - When serializing binary data using s11n, be nice to your CPU 00049 and restrict it to data of reasonable sizes (no 2GB blobs). 00050 00051 - Try the compact_serializer as your output format. It does not 00052 character translation and thus should perform much better than 00053 the other serializers on encoded data. 00054 */ 00055 struct bindata_ser 00056 { 00057 typedef std::string::value_type char_type; 00058 /** The binary data we want to serialize. */ 00059 char_type const * data; 00060 /** The length of this->data. */ 00061 size_t length; 00062 00063 /** 00064 Sets this object to point to begin, which is required to be 00065 at least n bytes long. 00066 */ 00067 bindata_ser( char_type const * begin, size_t n ) 00068 : data(begin), length(n) 00069 {} 00070 00071 /** 00072 Stores this->length and the base64-encoded form of this->data. 00073 */ 00074 template <typename NodeT> 00075 bool operator()( NodeT & dest ) const 00076 { 00077 if( ! this->length ) return false; 00078 typedef s11n::node_traits<NodeT> NTR; 00079 NTR::class_name( dest, "bindata_ser" ); 00080 std::ostringstream obuf; 00081 { 00082 std::istringstream ibuf( std::string( this->data, this->data + this->length ) ); 00083 s11n::base64::encoder E; 00084 E.encode( ibuf, obuf ); 00085 } 00086 NTR::set( dest, "base64", obuf.str() ); 00087 NTR::set( dest, "length", this->length ); 00088 return true; 00089 } 00090 }; 00091 00092 00093 /** 00094 bindata_deser is a helper to deserialize base64-encoded 00095 binary data to a (char *). It is intended to be used 00096 in conjunction with bindata_ser. 00097 00098 The intended usage goes something like this: 00099 00100 \code 00101 bindata_deser bin; 00102 deserialize( anS11nNode, bin ); 00103 // ... either take over bin.data or deallocate it ... 00104 \endcode 00105 00106 */ 00107 struct bindata_deser 00108 { 00109 typedef std::string::value_type char_type; 00110 /** 00111 The raw binary data. It is set by the deserialize 00112 operator. 00113 */ 00114 char_type * data; 00115 /** 00116 The length of the raw binary data. It is set by the deserialize 00117 operator. 00118 */ 00119 size_t length; 00120 bindata_deser() 00121 : data(0), length(0) 00122 {} 00123 00124 /** 00125 Uses malloc() to allocate count bytes. Returns 0 on error, at 00126 least theoretically (depends on the OS's allocator - some 00127 always return non-0 with the assumption that memory will become 00128 free at some point). 00129 */ 00130 static char * allocate( size_t count ) 00131 { 00132 return static_cast<char *>( malloc( count ) ); 00133 } 00134 00135 /** 00136 Deallocates tgt.data using free() and sets 00137 tgt's values to 0. 00138 */ 00139 static void deallocate( bindata_deser & tgt ) 00140 { 00141 free( tgt.data ); 00142 tgt.data = 0; 00143 tgt.length = 0; 00144 } 00145 00146 /** 00147 Decodes base64-encoded data from src and sets this->data to 00148 that data, and this->length to the length of that data. It 00149 allocates the memory using malloc(). The caller owns the 00150 deserialized data and must deallocate it at some point using 00151 free() or deallocate(thisObject). 00152 00153 If src does not appear to contain any data, false is 00154 returned. If it contains data but does not appear to be 00155 consistent (e.g. decoded data length does not match 00156 recorded length) or we cannot allocate enough memory to 00157 deserialize then an s11n_exception is thrown. 00158 00159 This is not a terribly efficient routine, requiring several 00160 copies of the binary data. Thus it should not be used with 00161 very large data sets. 00162 */ 00163 template <typename NodeT> 00164 bool operator()( NodeT const & src ) 00165 { 00166 typedef s11n::node_traits<NodeT> NTR; 00167 this->data = 0; 00168 this->length = NTR::get( src, "length", 0 ); 00169 if( !length ) return false; 00170 std::string cdata( NTR::get( src, "base64", std::string() ) ); 00171 if( cdata.empty() ) return false; 00172 00173 { 00174 std::ostringstream obuf; 00175 std::istringstream ibuf( cdata ); 00176 s11n::base64::decoder D; 00177 D.decode( ibuf, obuf ); 00178 cdata = obuf.str(); 00179 } 00180 00181 if( this->length != cdata.size() ) 00182 { 00183 throw s11n::s11n_exception( S11N_SOURCEINFO, 00184 "bindata_deser::deserialize: 'length' property [%u] does not correspond to actual data length [%u].", 00185 this->length, 00186 cdata.size() ); 00187 } 00188 // We could do the allocation before decoding, but then we'd take up yet another copy. 00189 this->data = allocate( this->length ); 00190 if( ! this->data ) 00191 { 00192 throw s11n::s11n_exception( S11N_SOURCEINFO, 00193 "bindata_deser::deserialize: could not allocate [%u] bytes for binary data.", 00194 this->length ); 00195 } 00196 memcpy( this->data, cdata.c_str(), this->length ); 00197 return true; 00198 } 00199 }; 00200 00201 00202 }} 00203 00204 #endif // s11n_BIN64_H_INCLUDED