Branch data Line data Source code
1 : : /* Copyright 2006-2008 Savarese Software Research Corporation
2 : : *
3 : : * Licensed under the Apache License, Version 2.0 (the "License");
4 : : * you may not use this file except in compliance with the License.
5 : : * You may obtain a copy of the License at
6 : : *
7 : : * http://www.savarese.com/software/ApacheLicense-2.0
8 : : *
9 : : * Unless required by applicable law or agreed to in writing, software
10 : : * distributed under the License is distributed on an "AS IS" BASIS,
11 : : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : : * See the License for the specific language governing permissions and
13 : : * limitations under the License.
14 : : */
15 : :
16 : : /**
17 : : * @file
18 : : * This header defines serialization support classes and functions.
19 : : */
20 : :
21 : : #ifndef __SSRC_WISP_SERIALIZATION_H
22 : : #define __SSRC_WISP_SERIALIZATION_H
23 : :
24 : : #include <ssrc/wisp-packages.h>
25 : : #include <ssrc/spread/detail/ByteBuffer.h>
26 : :
27 : : #include <utility>
28 : :
29 : : #include <boost/iostreams/stream.hpp>
30 : : #include <boost/iostreams/device/array.hpp>
31 : :
32 : : #include <boost/archive/binary_iarchive.hpp>
33 : : #include <boost/archive/binary_oarchive.hpp>
34 : :
35 : : #include <boost/serialization/split_free.hpp>
36 : : #include <boost/serialization/binary_object.hpp>
37 : :
38 : : /**
39 : : * Opens the boost::serialization namespace so you can add custom serialize
40 : : * functions. Close the serialize function definition block with
41 : : * __END_BOOST_SERIALIZATION.
42 : : */
43 : : #define __BEGIN_BOOST_SERIALIZATION \
44 : : namespace boost { \
45 : : namespace serialization {
46 : :
47 : : /**
48 : : * Closes the boost::serialization namespace started with
49 : : * __BEGIN_BOOST_SERIALIZATION.
50 : : */
51 : : #define __END_BOOST_SERIALIZATION \
52 : : } \
53 : : }
54 : :
55 : : __BEGIN_NS_SSRC_WISP
56 : :
57 : : using NS_SSRC_SPREAD::detail::ByteBuffer;
58 : :
59 : : namespace detail {
60 : : /**
61 : : * This is a Boost iostreams direct device whose begin and end
62 : : * pointers can be modified. Unfortunately, Boost iostreams stream
63 : : * buffers make their own copies of the pointers. Therefore, even
64 : : * after resetting the device pointers, you have to reopen the
65 : : * stream buffer.
66 : : */
67 : : template<typename Ch, typename mode_type = boost::iostreams::seekable>
68 : : class array_device_adapter {
69 : : public:
70 : : typedef Ch char_type;
71 : : typedef std::pair<char_type*, char_type*> pair_type;
72 : :
73 : 438 : struct category :
74 : : public mode_type,
75 : : public boost::iostreams::device_tag,
76 : : public boost::iostreams::direct_tag
77 : : { };
78 : :
79 : : private:
80 : : char_type *_begin, *_end;
81 : :
82 : : public:
83 : :
84 : 26 : array_device_adapter() : _begin(0), _end(0) { }
85 : :
86 : : array_device_adapter(char_type* begin, char_type* end) :
87 : : _begin(begin), _end(end)
88 : : { }
89 : :
90 : : array_device_adapter(char_type* begin, std::size_t size) :
91 : : _begin(begin), _end(begin + size)
92 : : { }
93 : :
94 : : void init(char_type* begin, char_type* end) {
95 : : _begin = begin;
96 : : _end = end;
97 : : }
98 : :
99 : 219 : void init(char_type* begin, std::size_t size) {
100 : 219 : _begin = begin;
101 : 219 : _end = begin + size;
102 : 219 : }
103 : :
104 : : void set_size(std::size_t size) {
105 : : _end = _begin + size;
106 : : }
107 : :
108 : 219 : pair_type input_sequence() {
109 : 219 : return pair_type(_begin, _end);
110 : : }
111 : :
112 : 219 : pair_type output_sequence() {
113 : 219 : return pair_type(_begin, _end);
114 : : }
115 : : };
116 : :
117 : : typedef array_device_adapter<char> char_array_source;
118 : : typedef array_device_adapter<char> char_array_sink;
119 : :
120 : : // This class is an unfortunately required workaround for a change
121 : : // and possible bug in Boost 1.42.0 which causes stream_buffer::open to
122 : : // throw an "already open" exception even if the buffer has been closed.
123 : : template<typename DeviceAdapter>
124 : 52 : struct char_array_streambuf :
125 : : public boost::iostreams::stream_buffer<DeviceAdapter>
126 : : {
127 : : typedef boost::iostreams::stream_buffer<DeviceAdapter> super;
128 : : typedef
129 : : boost::iostreams::detail::direct_streambuf<DeviceAdapter,
130 : : typename super::traits_type>
131 : : direct_streambuf;
132 : :
133 : 219 : void open(const DeviceAdapter & device_adapter) {
134 : 219 : direct_streambuf::open(device_adapter, 0, 0);
135 : 219 : }
136 : : };
137 : :
138 : : typedef char_array_streambuf<char_array_source> char_array_source_buffer;
139 : : typedef char_array_streambuf<char_array_sink> char_array_sink_buffer;
140 : : }
141 : :
142 : : /**
143 : : * Packer serializes objects into ssrc::spread::detail::ByteBuffer instances
144 : : * (usually a ssrc::spread::Message instance). Each object type must have
145 : : * a Boost serialize function or method defined (or load and save).
146 : : *
147 : : * @param Archive The archive type to use for serialization.
148 : : */
149 : : template<typename Archive>
150 : 26 : class Packer {
151 : : public:
152 : : typedef detail::char_array_sink sink_type;
153 : : typedef detail::char_array_sink_buffer sink_buffer;
154 : : typedef Archive output_archive;
155 : :
156 : : protected:
157 : : sink_type _sink;
158 : : sink_buffer _out_buf;
159 : :
160 : : public:
161 : :
162 : : /**
163 : : * Serializes an object into a buffer. The type of the object is
164 : : * automatically inferred by the compiler. The object write starts
165 : : * at the current buffer offset. After the object is serialized,
166 : : * the buffer size is adjusted and its write offset advanced to the
167 : : * end of the buffer. The buffer must have enough room to hold the
168 : : * data written or an exception will be thrown (std::ios_base::failure).
169 : : *
170 : : * @param obj The object to serialize.
171 : : * @param buffer The buffer that will store the object.
172 : : * @param archive_flags Boost archive constructor flags.
173 : : * @return The number of bytes written.
174 : : * @throw boost::archive::archive_exception If there is an archive
175 : : * writing error.
176 : : * @throw std::ios_base::failure If the stream buffer fails, most
177 : : * likely because the buffer doesn't have enough room to hold the
178 : : * data.
179 : : */
180 : : template<typename T>
181 : 133 : unsigned int pack(const T & obj, ByteBuffer & buffer,
182 : : const unsigned int archive_flags =
183 : : boost::archive::no_header | boost::archive::no_codecvt |
184 : : boost::archive::no_tracking)
185 : : SSRC_DECL_THROW(boost::archive::archive_exception, std::ios_base::failure)
186 : : {
187 : : unsigned int offset, bytes_written;
188 : :
189 : 133 : _sink.init(&buffer[0], buffer.capacity());
190 [ + - + - : 133 : _out_buf.open(_sink);
+ - + - +
- + - +
- ]
191 [ + - + - : 133 : _out_buf.pubseekoff(buffer.offset(), std::basic_ios<sink_buffer>::beg);
+ - + - +
- + - +
- ]
192 : :
193 [ + - + - : 266 : output_archive out_ar(_out_buf, archive_flags);
+ - + - +
- + - +
- ]
194 [ + - + - : 133 : out_ar << obj;
+ - + - +
- + + +
- ]
195 : :
196 [ + - + - : 106 : offset = _out_buf.pubseekoff(0, std::basic_ios<sink_buffer>::cur);
+ - + - +
- + - +
- ]
197 : 106 : bytes_written = offset - buffer.offset();
198 [ + - + - : 106 : buffer.resize(offset);
+ - + - +
- + - +
- ]
199 [ + - + - : 106 : buffer.seek(offset);
+ - + - +
- + - +
- ]
200 : :
201 : 212 : return bytes_written;
202 : : }
203 : : };
204 : :
205 : : /**
206 : : * Unpacker deserializes objects from ssrc::spread::detail::ByteBuffer
207 : : * instances (usually a ssrc::spread::Message instance). Each object
208 : : * type must have a Boost serialize function or method defined (or load
209 : : * and save).
210 : : *
211 : : * @param Archive The archive type to use for serialization.
212 : : */
213 : : template<typename Archive>
214 : 26 : class Unpacker {
215 : : public:
216 : : typedef detail::char_array_source source_type;
217 : : typedef detail::char_array_sink_buffer source_buffer;
218 : : typedef Archive input_archive;
219 : :
220 : : protected:
221 : : source_type _source;
222 : : source_buffer _in_buf;
223 : :
224 : : public:
225 : :
226 : : /**
227 : : * Deserializes an object from a buffer. The type of the object is
228 : : * automatically inferred by the compiler. The object read starts
229 : : * at the current buffer offset. After the object is deserialized,
230 : : * the buffer offset is adjusted and its read offset advanced to the
231 : : * end of the data read.
232 : : *
233 : : * @param obj The deserialization target.
234 : : * @param buffer The buffer storing the serialized object.
235 : : * @param archive_flags Boost archive constructor flags.
236 : : * @return The number of bytes read.
237 : : * @throw boost::archive::archive_exception If there is an archive
238 : : * read error.
239 : : * @throw std::ios_base::failure If the stream buffer fails.
240 : : */
241 : : template<typename T>
242 : 84 : unsigned int unpack(T & obj, ByteBuffer & buffer,
243 : : const unsigned int archive_flags =
244 : : boost::archive::no_header | boost::archive::no_codecvt |
245 : : boost::archive::no_tracking)
246 : : SSRC_DECL_THROW(boost::archive::archive_exception, std::ios_base::failure)
247 : : {
248 : 84 : unsigned int bytes_read(0);
249 : :
250 [ + - + - : 84 : if(buffer.size() > 0) {
+ - + - ]
251 : : unsigned int offset;
252 : :
253 : 84 : _source.init(&buffer[0], buffer.size());
254 [ + - + - : 84 : _in_buf.open(_source);
+ - + - ]
255 [ + - + - : 84 : _in_buf.pubseekoff(buffer.offset(), std::basic_ios<source_buffer>::beg);
+ - + - ]
256 : :
257 [ + - + - : 168 : input_archive in_ar(_in_buf, archive_flags);
+ - + - ]
258 [ + - + - : 84 : in_ar >> obj;
+ - + - ]
259 : :
260 [ + - + - : 84 : offset = _in_buf.pubseekoff(0, std::basic_ios<source_buffer>::cur);
+ - + - ]
261 : 84 : bytes_read = offset - buffer.offset();
262 [ + - + - : 84 : buffer.seek(offset);
+ - + - ]
263 : : }
264 : :
265 : 84 : return bytes_read;
266 : : }
267 : :
268 : :
269 : : /**
270 : : * Deserializes an object from a void * buffer. The type of the object is
271 : : * automatically inferred by the compiler. We use this method primarily
272 : : * to unpack serialized objects from blobs without copying the buffer first.
273 : : *
274 : : * @param obj The deserialization target.
275 : : * @param buffer The buffer storing the serialized object.
276 : : * @param size The number of bytes in the buffer.
277 : : * @param archive_flags Boost archive constructor flags.
278 : : * @return The number of bytes read.
279 : : * @throw boost::archive::archive_exception If there is an archive
280 : : * read error.
281 : : * @throw std::ios_base::failure If the stream buffer fails.
282 : : */
283 : : template<typename T>
284 : 2 : unsigned int unpack(T & obj, const void *buffer, unsigned int size,
285 : : const unsigned int archive_flags =
286 : : boost::archive::no_header | boost::archive::no_codecvt |
287 : : boost::archive::no_tracking)
288 : : SSRC_DECL_THROW(boost::archive::archive_exception, std::ios_base::failure)
289 : : {
290 : 2 : unsigned int bytes_read(0);
291 : :
292 [ + - + - ]: 2 : if(size > 0) {
293 : 2 : _source.init(static_cast<typename detail::char_array_source::char_type *>(const_cast<void *>(buffer)), size);
294 [ + - + - ]: 2 : _in_buf.open(_source);
295 [ + - + - ]: 2 : _in_buf.pubseekoff(0, std::basic_ios<source_buffer>::beg);
296 : :
297 [ + - + - ]: 4 : input_archive in_ar(_in_buf, archive_flags);
298 [ + - + - ]: 2 : in_ar >> obj;
299 : :
300 [ + - + - ]: 2 : bytes_read = _in_buf.pubseekoff(0, std::basic_ios<source_buffer>::cur);
301 : : }
302 : :
303 : 2 : return bytes_read;
304 : : }
305 : : };
306 : :
307 : : template<typename P, typename U>
308 : : struct PackingTraits {
309 : : typedef P packer_type;
310 : : typedef U unpacker_type;
311 : : };
312 : :
313 : : typedef Packer<boost::archive::binary_oarchive> BinaryPacker;
314 : : typedef Unpacker<boost::archive::binary_iarchive> BinaryUnpacker;
315 : : typedef PackingTraits<BinaryPacker, BinaryUnpacker> BinaryPackingTraits;
316 : :
317 : : __END_NS_SSRC_WISP
318 : :
319 : : __BEGIN_BOOST_SERIALIZATION
320 : :
321 : : template<class Archive>
322 : : inline
323 : : void save_construct_data(Archive & ar,
324 : : const NS_SSRC_SPREAD::detail::ByteBuffer *buffer,
325 : : const unsigned int)
326 : : {
327 : : // Could use capacity, but lets keep it as small as we can.
328 : : ar << buffer->size();
329 : : }
330 : :
331 : : template<class Archive>
332 : : inline
333 : : void load_construct_data(Archive & ar,
334 : : NS_SSRC_SPREAD::detail::ByteBuffer *buffer,
335 : : const unsigned int)
336 : : {
337 : : unsigned int capacity;
338 : :
339 : : ar >> capacity;
340 : : ::new(buffer)NS_SSRC_SPREAD::detail::ByteBuffer(capacity);
341 : : }
342 : :
343 : : template<class Archive>
344 : 30 : inline void save(Archive & ar,
345 : : const NS_SSRC_SPREAD::detail::ByteBuffer & buffer,
346 : : const unsigned int)
347 : : {
348 : 30 : unsigned int size = buffer.size();
349 : :
350 [ + - ]: 30 : ar & size;
351 : :
352 [ + - ]: 30 : if(size > 0) {
353 : 30 : const binary_object obj(&buffer[0], size);
354 : 30 : unsigned int offset = buffer.offset();
355 [ + - + + ]: 30 : ar & offset & obj;
356 : : }
357 : 3 : }
358 : :
359 : : template<class Archive>
360 : 2 : inline void load(Archive & ar,
361 : : NS_SSRC_SPREAD::detail::ByteBuffer & buffer,
362 : : const unsigned int)
363 : : {
364 : : unsigned int size;
365 : :
366 [ + - ]: 2 : ar & size;
367 : :
368 [ + - ]: 2 : buffer.resize(size);
369 : :
370 [ + - ]: 2 : if(size > 0) {
371 : 2 : binary_object obj(&buffer[0], size);
372 : : unsigned int offset;
373 [ + - + - ]: 2 : ar & offset & obj;
374 [ + - ]: 2 : buffer.seek(offset);
375 : : } else
376 [ # # ]: 0 : buffer.rewind();
377 : 2 : }
378 : :
379 : : __END_BOOST_SERIALIZATION
380 : :
381 : 32 : BOOST_SERIALIZATION_SPLIT_FREE(NS_SSRC_SPREAD::detail::ByteBuffer)
382 : :
383 : : #endif
|