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