IBR-DTNSuite  0.10
Serializer.cpp
Go to the documentation of this file.
1 /*
2  * Serializer.cpp
3  *
4  * Copyright (C) 2011 IBR, TU Braunschweig
5  *
6  * Written-by: Johannes Morgenroth <morgenroth@ibr.cs.tu-bs.de>
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  * http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  *
20  */
21 
22 #include "ibrdtn/data/Serializer.h"
24 #include "ibrdtn/data/Bundle.h"
25 #include "ibrdtn/data/Block.h"
29 #include "ibrdtn/data/MetaBundle.h"
30 #include "ibrdtn/utils/Clock.h"
31 #include <ibrcommon/refcnt_ptr.h>
32 #include <ibrcommon/Logger.h>
33 #include <list>
34 #include <limits>
35 
36 #ifdef __DEVELOPMENT_ASSERTIONS__
37 #include <cassert>
38 #endif
39 
40 namespace dtn
41 {
42  namespace data
43  {
45  : _stream(stream), _compressable(false)
46  {
47  }
48 
49  DefaultSerializer::DefaultSerializer(std::ostream& stream, const Dictionary &d)
50  : _stream(stream), _dictionary(d), _compressable(false)
51  {
52  }
53 
55  {
56  // clear the dictionary
58 
59  // rebuild the dictionary
60  _dictionary.add(obj);
61 
62  // check if the bundle header could be compressed
64  }
65 
67  {
68  // rebuild the dictionary
69  rebuildDictionary(obj);
70 
71  // serialize the primary block
72  (*this) << (PrimaryBlock&)obj;
73 
74  // serialize all secondary blocks
75  for (Bundle::const_iterator iter = obj.begin(); iter != obj.end(); ++iter)
76  {
77  const Block &b = (**iter);
78  (*this) << b;
79  }
80 
81  return (*this);
82  }
83 
85  {
86  // rebuild the dictionary
88 
89  PrimaryBlock prim = obj._bundle;
91 
92  // set the application length according to the payload block size
94 
95  if (it != obj._bundle.end()) {
96  const dtn::data::PayloadBlock &payload = dynamic_cast<const dtn::data::PayloadBlock&>(**it);
97  prim.appdatalength = payload.getLength();
98  } else {
99  prim.appdatalength = 0;
100  }
101 
102  // set the fragmentation offset
103  prim.fragmentoffset += obj._offset;
104 
105  // serialize the primary block
106  (*this) << prim;
107 
108  // serialize all secondary blocks
109  bool post_payload = false;
110 
111  for (Bundle::const_iterator iter = obj._bundle.begin(); iter != obj._bundle.end(); ++iter)
112  {
113  const Block &b = (**iter);
114 
115  try {
116  // test if this is the payload block
117  const dtn::data::PayloadBlock &payload = dynamic_cast<const dtn::data::PayloadBlock&>(b);
118 
119  // serialize the clipped block
120  serialize(payload, obj._offset, obj._length);
121 
122  // we had serialized the payload block
123  post_payload = true;
124  } catch (const std::bad_cast&) {
125  // serialize this block if
126  // ... this block is before the payload block and marked as replicated in every fragment
127  // ... this block is after the payload block and all remaining bytes of the payload block are included
128  if (post_payload || b.get(dtn::data::Block::REPLICATE_IN_EVERY_FRAGMENT))
129  {
130  (*this) << b;
131  }
132  }
133  }
134 
135  return (*this);
136  }
137 
139  {
140  // check if all EID are compressable
141  bool compressable = ( obj.source.isCompressable() &&
142  obj.destination.isCompressable() &&
143  obj.reportto.isCompressable() &&
144  obj.custodian.isCompressable() );
145 
146  if (compressable)
147  {
148  // add EID of all secondary blocks
149  for (Bundle::const_iterator iter = obj.begin(); iter != obj.end(); ++iter)
150  {
151  const Block &b = (**iter);
152  const std::list<dtn::data::EID> eids = b.getEIDList();
153 
154  for (std::list<dtn::data::EID>::const_iterator eit = eids.begin(); eit != eids.end(); ++eit)
155  {
156  const dtn::data::EID &eid = (*eit);
157  if (!eid.isCompressable())
158  {
159  return false;
160  }
161  }
162  }
163  }
164 
165  return compressable;
166  }
167 
169  {
170  _stream << dtn::data::BUNDLE_VERSION; // bundle version
171  _stream << obj.procflags; // processing flags
172 
173  // predict the block length
174  Number len = 0;
175  dtn::data::Number primaryheader[14];
176 
177  primaryheader[8] = obj.timestamp; // timestamp
178  primaryheader[9] = obj.sequencenumber; // sequence number
179  primaryheader[10] = obj.lifetime; // lifetime
180 
182 
183  if (_compressable)
184  {
185  // destination reference
186  ref = obj.destination.getCompressed();
187  primaryheader[0] = ref.first;
188  primaryheader[1] = ref.second;
189 
190  // source reference
191  ref = obj.source.getCompressed();
192  primaryheader[2] = ref.first;
193  primaryheader[3] = ref.second;
194 
195  // reportto reference
196  ref = obj.reportto.getCompressed();
197  primaryheader[4] = ref.first;
198  primaryheader[5] = ref.second;
199 
200  // custodian reference
201  ref = obj.custodian.getCompressed();
202  primaryheader[6] = ref.first;
203  primaryheader[7] = ref.second;
204 
205  // dictionary size is zero in a compressed bundle header
206  primaryheader[11] = 0;
207  }
208  else
209  {
210  // destination reference
211  ref = _dictionary.getRef(obj.destination);
212  primaryheader[0] = ref.first;
213  primaryheader[1] = ref.second;
214 
215  // source reference
216  ref = _dictionary.getRef(obj.source);
217  primaryheader[2] = ref.first;
218  primaryheader[3] = ref.second;
219 
220  // reportto reference
221  ref = _dictionary.getRef(obj.reportto);
222  primaryheader[4] = ref.first;
223  primaryheader[5] = ref.second;
224 
225  // custodian reference
226  ref = _dictionary.getRef(obj.custodian);
227  primaryheader[6] = ref.first;
228  primaryheader[7] = ref.second;
229 
230  // dictionary size
231  primaryheader[11] = Number(_dictionary.getSize());
232  len += _dictionary.getSize();
233  }
234 
235  for (int i = 0; i < 12; ++i)
236  {
237  len += primaryheader[i].getLength();
238  }
239 
241  {
242  primaryheader[12] = obj.fragmentoffset;
243  primaryheader[13] = obj.appdatalength;
244 
245  len += primaryheader[12].getLength();
246  len += primaryheader[13].getLength();
247  }
248 
249  // write the block length
250  _stream << len;
251 
252  /*
253  * write the ref block of the dictionary
254  * this includes scheme and ssp for destination, source, reportto and custodian.
255  */
256  for (int i = 0; i < 11; ++i)
257  {
258  _stream << primaryheader[i];
259  }
260 
261  if (_compressable)
262  {
263  // write the size of the dictionary (always zero here)
264  _stream << primaryheader[11];
265  }
266  else
267  {
268  // write size of dictionary + bytearray
269  _stream << _dictionary;
270  }
271 
273  {
274  _stream << primaryheader[12]; // FRAGMENTATION_OFFSET
275  _stream << primaryheader[13]; // APPLICATION_DATA_LENGTH
276  }
277 
278  return (*this);
279  }
280 
282  {
283  _stream.put((char&)obj.getType());
284  _stream << obj.getProcessingFlags();
285 
286  const Block::eid_list &eids = obj.getEIDList();
287 
288 #ifdef __DEVELOPMENT_ASSERTIONS__
289  // test: BLOCK_CONTAINS_EIDS => (eids.size() > 0)
290  assert(!obj.get(Block::BLOCK_CONTAINS_EIDS) || (eids.size() > 0));
291 #endif
292 
294  {
295  _stream << Number(eids.size());
296  for (Block::eid_list::const_iterator it = eids.begin(); it != eids.end(); ++it)
297  {
299 
300  if (_compressable)
301  {
302  offsets = (*it).getCompressed();
303  }
304  else
305  {
306  offsets = _dictionary.getRef(*it);
307  }
308 
309  _stream << offsets.first;
310  _stream << offsets.second;
311  }
312  }
313 
314  // write size of the payload in the block
315  _stream << Number(obj.getLength());
316 
317  // write the payload of the block
318  Length slength = 0;
319  obj.serialize(_stream, slength);
320 
321  return (*this);
322  }
323 
324  Serializer& DefaultSerializer::serialize(const dtn::data::PayloadBlock& obj, const Length &clip_offset, const Length &clip_length)
325  {
326  _stream.put((char&)obj.getType());
327  _stream << obj.getProcessingFlags();
328 
329  const Block::eid_list &eids = obj.getEIDList();
330 
331 #ifdef __DEVELOPMENT_ASSERTIONS__
332  // test: BLOCK_CONTAINS_EIDS => (eids.size() > 0)
333  assert(!obj.get(Block::BLOCK_CONTAINS_EIDS) || (eids.size() > 0));
334 #endif
335 
337  {
338  _stream << Number(eids.size());
339  for (Block::eid_list::const_iterator it = eids.begin(); it != eids.end(); ++it)
340  {
342 
343  if (_compressable)
344  {
345  offsets = (*it).getCompressed();
346  }
347  else
348  {
349  offsets = _dictionary.getRef(*it);
350  }
351 
352  _stream << offsets.first;
353  _stream << offsets.second;
354  }
355  }
356 
357  // get the remaining payload size
358  Length payload_size = obj.getLength();
359  Length remain = payload_size - clip_offset;
360 
361  // check if the remaining data length is >= clip_length
362  if (payload_size < clip_offset)
363  {
364  // set the real predicted payload length
365  // write size of the payload in the block
366  _stream << Number(0);
367  }
368  else
369  if (remain > clip_length)
370  {
371  // set the real predicted payload length
372  // write size of the payload in the block
373  _stream << Number(clip_length);
374 
375  // now skip the <offset>-bytes and all bytes after <offset + length>
376  obj.serialize( _stream, clip_offset, clip_length );
377  }
378  else
379  {
380  // set the real predicted payload length
381  // write size of the payload in the block
382  _stream << Number(remain);
383 
384  // now skip the <offset>-bytes and all bytes after <offset + length>
385  obj.serialize( _stream, clip_offset, remain );
386  }
387 
388  return (*this);
389  }
390 
392  {
393  // rebuild the dictionary
394  rebuildDictionary(obj);
395 
396  Length len = 0;
397  len += getLength( (PrimaryBlock&)obj );
398 
399  // add size of all blocks
400  for (Bundle::const_iterator iter = obj.begin(); iter != obj.end(); ++iter)
401  {
402  const Block &b = (**iter);
403  len += getLength( b );
404  }
405 
406  return len;
407  }
408 
410  {
411  Length len = 0;
412 
413  len += sizeof(dtn::data::BUNDLE_VERSION); // bundle version
414  len += obj.procflags.getLength(); // processing flags
415 
416  // primary header
417  dtn::data::Number primaryheader[14];
419 
420  if (_compressable)
421  {
422  // destination reference
423  ref = obj.destination.getCompressed();
424  primaryheader[0] = ref.first;
425  primaryheader[1] = ref.second;
426 
427  // source reference
428  ref = obj.source.getCompressed();
429  primaryheader[2] = ref.first;
430  primaryheader[3] = ref.second;;
431 
432  // reportto reference
433  ref = obj.reportto.getCompressed();
434  primaryheader[4] = ref.first;
435  primaryheader[5] = ref.second;;
436 
437  // custodian reference
438  ref = obj.custodian.getCompressed();
439  primaryheader[6] = ref.first;
440  primaryheader[7] = ref.second;;
441 
442  // dictionary size
443  primaryheader[11] = Number(0);
444  }
445  else
446  {
447  // destination reference
448  ref = _dictionary.getRef(obj.destination);
449  primaryheader[0] = ref.first;
450  primaryheader[1] = ref.second;;
451 
452  // source reference
453  ref = _dictionary.getRef(obj.source);
454  primaryheader[2] = ref.first;
455  primaryheader[3] = ref.second;;
456 
457  // reportto reference
458  ref = _dictionary.getRef(obj.reportto);
459  primaryheader[4] = ref.first;
460  primaryheader[5] = ref.second;;
461 
462  // custodian reference
463  ref = _dictionary.getRef(obj.custodian);
464  primaryheader[6] = ref.first;
465  primaryheader[7] = ref.second;;
466 
467  // dictionary size
468  primaryheader[11] = Number(_dictionary.getSize());
469  }
470 
471  // timestamp
472  primaryheader[8] = obj.timestamp;
473 
474  // sequence number
475  primaryheader[9] = obj.sequencenumber;
476 
477  // lifetime
478  primaryheader[10] = obj.lifetime;
479 
480  for (int i = 0; i < 11; ++i)
481  {
482  len += primaryheader[i].getLength();
483  }
484 
485  if (_compressable)
486  {
487  // write the size of the dictionary (always zero here)
488  len += primaryheader[11].getLength();
489  }
490  else
491  {
492  // write size of dictionary + bytearray
493  len += primaryheader[11].getLength();
494  len += _dictionary.getSize();
495  }
496 
498  {
499  primaryheader[12] = obj.fragmentoffset;
500  primaryheader[13] = obj.appdatalength;
501 
502  len += primaryheader[12].getLength();
503  len += primaryheader[13].getLength();
504  }
505 
506  len += Number(len).getLength();
507 
508  return len;
509  }
510 
512  {
513  Length len = 0;
514 
515  len += sizeof(obj.getType());
516  len += obj.getProcessingFlags().getLength();
517 
518  const Block::eid_list &eids = obj.getEIDList();
519 
520 #ifdef __DEVELOPMENT_ASSERTIONS__
521  // test: BLOCK_CONTAINS_EIDS => (eids.size() > 0)
522  assert(!obj.get(Block::BLOCK_CONTAINS_EIDS) || (eids.size() > 0));
523 #endif
524 
526  {
527  len += dtn::data::Number(eids.size()).getLength();
528  for (Block::eid_list::const_iterator it = eids.begin(); it != eids.end(); ++it)
529  {
531  len += offsets.first.getLength();
532  len += offsets.second.getLength();
533  }
534  }
535 
536  // size of the payload in the block
537  Length payload_size = obj.getLength();
538  len += payload_size;
539 
540  // size of the payload size
541  len += Number(payload_size).getLength();
542 
543  return len;
544  }
545 
547  : _stream(stream), _validator(_default_validator), _compressed(false), _fragmentation(false)
548  {
549  }
550 
552  : _stream(stream), _validator(v), _compressed(false), _fragmentation(false)
553  {
554  }
555 
556  DefaultDeserializer::DefaultDeserializer(std::istream &stream, const Dictionary &d)
557  : _stream(stream), _validator(_default_validator), _dictionary(d), _compressed(false), _fragmentation(false)
558  {
559  }
560 
562  {
563  _fragmentation = val;
564  }
565 
567  {
568  // clear all blocks
569  obj.clear();
570 
571  // read the primary block
572  (*this) >> (PrimaryBlock&)obj;
573 
574  // read until the last block
575  bool lastblock = false;
576 
577  block_t block_type;
579 
580  // create a bundle builder
581  dtn::data::BundleBuilder builder(obj);
582 
583  // read all BLOCKs
584  while (!_stream.eof() && !lastblock)
585  {
586  // BLOCK_TYPE
587  _stream.get((char&)block_type);
588 
589  // read processing flags
590  _stream >> procflags;
591 
592  try {
593  // create a block object
594  dtn::data::Block &block = builder.insert(block_type, procflags);
595 
596  try {
597  // read block content
598  (*this).read(obj, block);
599  } catch (dtn::PayloadReceptionInterrupted &ex) {
600  // some debugging
601  IBRCOMMON_LOGGER_DEBUG_TAG("DefaultDeserializer", 15) << "Reception of bundle payload failed." << IBRCOMMON_LOGGER_ENDL;
602 
603  // interrupted transmission
604  if (!obj.get(dtn::data::PrimaryBlock::DONT_FRAGMENT) && (block.getLength() > 0) && _fragmentation)
605  {
606  IBRCOMMON_LOGGER_DEBUG_TAG("DefaultDeserializer", 25) << "Create a fragment." << IBRCOMMON_LOGGER_ENDL;
607 
609  {
611  obj.appdatalength = ex.length;
612  obj.fragmentoffset = 0;
613  }
614  }
615  else
616  {
617  throw;
618  }
619  }
621  // skip EIDs
622  if ( procflags.getBit(dtn::data::Block::BLOCK_CONTAINS_EIDS) )
623  {
624  Number eidcount;
625  _stream >> eidcount;
626 
627  for (unsigned int i = 0; eidcount > i; ++i)
628  {
629  Number scheme, ssp;
630  _stream >> scheme;
631  _stream >> ssp;
632  }
633  }
634 
635  // read the size of the payload in the block
636  Number block_size;
637  _stream >> block_size;
638 
639  // skip payload
640  _stream.ignore(block_size.get<std::streamsize>());
641  }
642 
643  lastblock = procflags.getBit(Block::LAST_BLOCK);
644  }
645 
646  // validate this bundle
647  _validator.validate(obj);
648 
649  return (*this);
650  }
651 
653  {
655  (*this) >> pb;
656 
657  obj.appdatalength = pb.appdatalength;
658  obj.custodian = pb.custodian;
659  obj.destination = pb.destination;
660  obj.expiretime = dtn::utils::Clock::getExpireTime(pb.timestamp, pb.lifetime);
662  obj.hopcount = 0;
663  obj.lifetime = pb.lifetime;
664  obj.offset = pb.fragmentoffset;
665  obj.procflags = pb.procflags;
667  obj.reportto = pb.reportto;
668  obj.sequencenumber = pb.sequencenumber;
669  obj.source = pb.source;
670  obj.timestamp = pb.timestamp;
671 
672  return (*this);
673  }
674 
676  {
677  char version = 0;
678  Number blocklength;
679 
680  // check for the right version
681  _stream.get(version);
682  if (version != dtn::data::BUNDLE_VERSION) throw dtn::InvalidProtocolException("Bundle version differ from ours.");
683 
684  // PROCFLAGS
685  _stream >> obj.procflags; // processing flags
686 
687  // BLOCK LENGTH
688  _stream >> blocklength;
689 
690  // EID References
692  for (int i = 0; i < 4; ++i)
693  {
694  _stream >> ref[i].first;
695  _stream >> ref[i].second;
696  }
697 
698  // timestamp
699  _stream >> obj.timestamp;
700 
701  // sequence number
702  _stream >> obj.sequencenumber;
703 
704  // lifetime
705  _stream >> obj.lifetime;
706 
707  try {
708  // dictionary
709  _stream >> _dictionary;
710 
711  // decode EIDs
712  obj.destination = _dictionary.get(ref[0].first, ref[0].second);
713  obj.source = _dictionary.get(ref[1].first, ref[1].second);
714  obj.reportto = _dictionary.get(ref[2].first, ref[2].second);
715  obj.custodian = _dictionary.get(ref[3].first, ref[3].second);
716  _compressed = false;
717  } catch (const dtn::InvalidDataException&) {
718  // error while reading the dictionary. We assume that this is a compressed bundle header.
719  obj.destination = dtn::data::EID(ref[0].first, ref[0].second);
720  obj.source = dtn::data::EID(ref[1].first, ref[1].second);
721  obj.reportto = dtn::data::EID(ref[2].first, ref[2].second);
722  obj.custodian = dtn::data::EID(ref[3].first, ref[3].second);
723  _compressed = true;
724  }
725 
726  // fragmentation?
728  {
729  _stream >> obj.fragmentoffset;
730  _stream >> obj.appdatalength;
731  }
732 
733  // validate this primary block
734  _validator.validate(obj);
735 
736  return (*this);
737  }
738 
740  {
741  // read EIDs
743  {
744  Number eidcount;
745  _stream >> eidcount;
746 
747  for (unsigned int i = 0; eidcount > i; ++i)
748  {
749  Number scheme, ssp;
750  _stream >> scheme;
751  _stream >> ssp;
752 
753  if (_compressed)
754  {
755  obj.addEID( dtn::data::EID(scheme, ssp) );
756  }
757  else
758  {
759  obj.addEID( _dictionary.get(scheme, ssp) );
760  }
761  }
762  }
763 
764  // read the size of the payload in the block
765  Number block_size;
766  _stream >> block_size;
767 
768  // validate this block
769  _validator.validate(obj, block_size);
770 
771  // read the payload of the block
772  obj.deserialize(_stream, block_size.get<dtn::data::Length>());
773 
774  return (*this);
775  }
776 
778  {
779  // read EIDs
781  {
782  Number eidcount;
783  _stream >> eidcount;
784 
785  for (unsigned int i = 0; eidcount > i; ++i)
786  {
787  Number scheme, ssp;
788  _stream >> scheme;
789  _stream >> ssp;
790 
791  if (_compressed)
792  {
793  obj.addEID( dtn::data::EID(scheme, ssp) );
794  }
795  else
796  {
797  obj.addEID( _dictionary.get(scheme, ssp) );
798  }
799  }
800  }
801 
802  // read the size of the payload in the block
803  Number block_size;
804  _stream >> block_size;
805 
806  // validate this block
807  _validator.validate(bundle, obj, block_size);
808 
809  // read the payload of the block
810  obj.deserialize(_stream, block_size.get<Length>());
811 
812  return (*this);
813  }
814 
816  {
817  }
818 
820  {
821  }
822 
824  {
825  }
826 
828  {
829 
830  }
831 
833  {
834 
835  }
836 
838  {
839 
840  }
841 
843  : DefaultSerializer(stream)
844  {
845  }
846 
848  {
849  }
850 
852  {
853  _stream.put((char&)obj.getType());
854  _stream << obj.getProcessingFlags();
855 
856  const Block::eid_list &eids = obj.getEIDList();
857 
858 #ifdef __DEVELOPMENT_ASSERTIONS__
859  // test: BLOCK_CONTAINS_EIDS => (_ids.size() > 0)
860  assert(!obj.get(Block::BLOCK_CONTAINS_EIDS) || (eids.size() > 0));
861 #endif
862 
864  {
865  _stream << Number(eids.size());
866  for (Block::eid_list::const_iterator it = eids.begin(); it != eids.end(); ++it)
867  {
868  dtn::data::BundleString str((*it).getString());
869  _stream << str;
870  }
871  }
872 
873  // write size of the payload in the block
874  _stream << Number(obj.getLength());
875 
876  // write the payload of the block
877  Length slength = 0;
878  obj.serialize(_stream, slength);
879 
880  return (*this);
881  }
882 
884  {
885  Length len = 0;
886 
887  len += sizeof(obj.getType());
888  len += obj.getProcessingFlags().getLength();
889 
890  const Block::eid_list &eids = obj.getEIDList();
891 
892 #ifdef __DEVELOPMENT_ASSERTIONS__
893  // test: BLOCK_CONTAINS_EIDS => (eids.size() > 0)
894  assert(!obj.get(Block::BLOCK_CONTAINS_EIDS) || (eids.size() > 0));
895 #endif
896 
898  {
899  len += dtn::data::Number(eids.size()).getLength();
900  for (Block::eid_list::const_iterator it = eids.begin(); it != eids.end(); ++it)
901  {
902  dtn::data::BundleString str((*it).getString());
903  len += str.getLength();
904  }
905  }
906 
907  // size of the payload in the block
908  len += obj.getLength();
909 
910  return len;
911  }
912 
914  : DefaultDeserializer(stream), _bundle(b)
915  {
916  }
917 
919  {
920  }
921 
923  {
924  BundleBuilder builder(_bundle);
925 
926  block_t block_type;
927  Bitset<Block::ProcFlags> procflags;
928 
929  // BLOCK_TYPE
930  _stream.get((char&)block_type);
931 
932  // read processing flags
933  _stream >> procflags;
934 
935  // create a block object
936  dtn::data::Block &obj = builder.insert(block_type, procflags);
937 
938  // read EIDs
940  {
941  Number eidcount;
942  _stream >> eidcount;
943 
944  for (unsigned int i = 0; eidcount > i; ++i)
945  {
947  _stream >> str;
948  obj.addEID(dtn::data::EID(str));
949  }
950  }
951 
952  // read the size of the payload in the block
953  Number block_size;
954  _stream >> block_size;
955 
956  // validate this block
957  _validator.validate(obj, block_size);
958 
959  // read the payload of the block
960  obj.deserialize(_stream, block_size.get<Length>());
961 
962  return obj;
963  }
964  }
965 }