1 // The TIMPI Message-Passing Parallelism Library.
2 // Copyright (C) 2002-2019 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner
3 
4 // This library is free software; you can redistribute it and/or
5 // modify it under the terms of the GNU Lesser General Public
6 // License as published by the Free Software Foundation; either
7 // version 2.1 of the License, or (at your option) any later version.
8 
9 // This library is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 // Lesser General Public License for more details.
13 
14 // You should have received a copy of the GNU Lesser General Public
15 // License along with this library; if not, write to the Free Software
16 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17 
18 
19 #ifndef TIMPI_PACKING_H
20 #define TIMPI_PACKING_H
21 
22 // TIMPI Includes
23 #include "timpi/timpi_assert.h"
24 #include "timpi/packing_forward.h"
25 #include "timpi/standard_type.h"
26 
27 // C++ includes
28 #include <array>
29 #include <cstring>     // memcpy
30 #include <iterator>
31 #include <list>
32 #include <map>
33 #include <set>
34 #include <tuple>
35 #include <type_traits> // enable_if, is_same
36 #include <unordered_map>
37 #include <unordered_set>
38 #include <utility>     // pair
39 #include <vector>
40 
41 
42 // FIXME: This *should* be in TIMPI namespace but we have libMesh
43 // users which already partially specialized it
44 namespace libMesh
45 {
46 
47 namespace Parallel
48 {
49 
50 /**
51  * Define data types and (un)serialization functions for use when
52  * encoding a potentially-variable-size object of type T.
53  *
54  * Users will need to specialize this class for their particular data
55  * types.
56  */
57 template <typename T, typename Enable>
58 class Packing {
59 public:
60   // Should be an MPI sendable type in specializations, e.g.
61   // typedef char buffer_type;
62   // typedef unsigned int buffer_type;
63 
64   // Should copy an encoding of the provided object into the provided
65   // output iterator (which is of type buffer_type)
66   template <typename OutputIter, typename Context>
67   static void pack(const T & object,
68                    OutputIter data_out,
69                    const Context * context);
70 
71   // Should return the number of array entries (of type buffer_type)
72   // required to encode the provided object
73   template <typename Context>
74   static unsigned int packable_size(const T & object,
75                                     const Context * context);
76 
77   // Should return the number of array entries which were used to
78   // encode the provided serialization of an object which begins at
79   // \p iter
80   template <typename BufferIter>
81   static unsigned int packed_size(BufferIter iter);
82 
83   // Decode a potentially-variable-size object from a subsequence of a
84   // data array, returning a heap-allocated pointer to the result.
85   template <typename BufferIter, typename Context>
86   static T unpack(BufferIter in, Context * ctx);
87 };
88 
89 // Idiom taken from https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Member_Detector
90 template <typename T>
91 class Has_buffer_type
92 {
93   using Yes = char[2];
94   using No = char[1];
95 
96   struct Fallback {
97     struct buffer_type {};
98   };
99   struct Derived : T, Fallback {};
100 
101   template <typename U> static Yes &test(U *);
102 
103   // this template must be more specialized in general than the Yes version because it involves a
104   // type-dependent expression...?
105   template <typename U> static No &test(typename U::buffer_type *);
106 
107 public:
108   static constexpr bool value = sizeof(test<Derived>(nullptr)) == sizeof(Yes);
109 };
110 
111 
112 // Metafunction to get a value_type from map and unordered_map with
113 // non-const keys, so we can create a key/value pair more easily
114 template <typename ValueType>
115 struct DefaultValueType {
116   typedef ValueType type;
117 };
118 
119 template <typename K, typename V>
120 struct DefaultValueType<std::pair<const K, V>> {
121   typedef std::pair<K, V> type;
122 };
123 
124 
125 // Superclass with utility methods for use with Packing partial
126 // specializations that mix fixed-size with Packing-required inner
127 // classes.
128 template <typename BufferType>
129 struct PackingMixedType
130 {
131   typedef BufferType buffer_type;
132 
133   template <typename T3>
134   struct IsFixed
135   {
136     static const bool value =
137       TIMPI::StandardType
138       <typename DefaultValueType<T3>::type>::is_fixed_type;
139   };
140 
141   template <typename T3>
142   struct BufferTypesPer
143   {
144     static const unsigned int value = (sizeof(T3) + sizeof(buffer_type) - 1) / sizeof(buffer_type);
145   };
146 
147   template <typename T3,
148             typename Context,
149             typename std::enable_if<IsFixed<T3>::value, int>::type = 0>
150   static unsigned int packable_size_comp(const T3 &, const Context *)
151   {
152     return BufferTypesPer<T3>::value;
153   }
154 
155   template <typename T3,
156             typename Context,
157             typename std::enable_if<!IsFixed<T3>::value, int>::type = 0>
158   static unsigned int packable_size_comp(const T3 & comp, const Context * ctx)
159   {
160     return Packing<T3>::packable_size(comp, ctx);
161   }
162 
163   template <typename T3,
164             typename OutputIter,
165             typename Context,
166             typename std::enable_if<IsFixed<T3>::value, int>::type = 0>
167   static void pack_comp(const T3 & comp, OutputIter data_out, const Context *)
168   {
169     buffer_type T3_as_buffer_types[BufferTypesPer<T3>::value];
170     std::memcpy(T3_as_buffer_types, &comp, sizeof(T3));
171     for (unsigned int i = 0; i != BufferTypesPer<T3>::value; ++i)
172       *data_out++ = T3_as_buffer_types[i];
173   }
174 
175   template <typename T1, typename T2,
176             typename OutputIter,
177             typename Context,
178             typename std::enable_if<IsFixed<std::pair<T1, T2>>::value, int>::type = 0>
179   static void pack_comp(const std::pair<T1, T2> & comp, OutputIter data_out, const Context * ctx)
180   {
181     pack_comp(comp.first, data_out, ctx);
182     pack_comp(comp.second, data_out, ctx);
183   }
184 
185   template <typename T3,
186             typename OutputIter,
187             typename Context,
188             typename std::enable_if<!IsFixed<T3>::value, int>::type = 0>
189   static void pack_comp(const T3 & comp, OutputIter data_out, const Context * ctx)
190   {
191     Packing<T3>::pack(comp, data_out, ctx);
192   }
193 
194   template <typename T3,
195             typename BufferIter,
196             typename Context,
197             typename std::enable_if<IsFixed<T3>::value, int>::type = 0>
198   static void unpack_comp(T3 & comp, BufferIter in, Context *)
199   {
200     std::memcpy(&comp, &(*in), sizeof(T3));
201   }
202 
203   template <typename T1, typename T2,
204             typename BufferIter,
205             typename Context,
206             typename std::enable_if<IsFixed<std::pair<T1, T2>>::value, int>::type = 0>
207   static void unpack_comp(std::pair<T1, T2> & comp, BufferIter in, Context * ctx)
208   {
209     unpack_comp(comp.first, in, ctx);
210 
211     in += packable_size_comp(comp.first, ctx);
212 
213     unpack_comp(comp.second, in, ctx);
214   }
215 
216 
217   template <typename T3,
218             typename BufferIter,
219             typename Context,
220             typename std::enable_if<!IsFixed<T3>::value, int>::type = 0>
221   static void unpack_comp(T3 & comp, BufferIter in, Context * ctx)
222   {
223     comp = Packing<T3>::unpack(in, ctx);
224   }
225 };
226 
227 
228 
229 
230 // Utility metafunction for use in Packing<std::pair>
231 template <typename T1, bool T1_has_buffer_type, typename T2, bool T2_has_buffer_type>
232 struct PairBufferTypeHelper {};
233 
234 template <typename T1, typename T2>
235 struct PairBufferTypeHelper<T1, true, T2, true>
236 {
237   static_assert(std::is_same<typename Packing<T1>::buffer_type, typename Packing<T2>::buffer_type>::value,
238                 "For ease of use we cannot pack two types that use two different buffer types");
239 
240   typedef typename Packing<T1>::buffer_type buffer_type;
241 };
242 
243 template <typename T1, typename T2>
244 struct PairBufferTypeHelper<T1, true, T2, false>
245 {
246   typedef typename Packing<T1>::buffer_type buffer_type;
247 };
248 
249 template <typename T1, typename T2>
250 struct PairBufferTypeHelper<T1, false, T2, true>
251 {
252   typedef typename Packing<T2>::buffer_type buffer_type;
253 };
254 
255 template <typename T1, typename T2>
256 struct PairBufferTypeHelper<T1, false, T2, false>
257 {
258   typedef unsigned int buffer_type;
259 };
260 
261 
262 // specialization for std::pair
263 template <typename T1, typename T2>
264 class Packing<std::pair<T1, T2>,
265               typename std::enable_if<!TIMPI::StandardType<std::pair<T1, T2>>::is_fixed_type>::type>
266 {
267 public:
268   typedef typename PairBufferTypeHelper
269       <T1, Has_buffer_type<Packing<T1>>::value,
270        T2, Has_buffer_type<Packing<T2>>::value>::buffer_type buffer_type;
271 
272   typedef PackingMixedType<buffer_type> Mixed;
273 
274   template <typename OutputIter, typename Context>
275   static void pack(const std::pair<T1, T2> & pr, OutputIter data_out, const Context * context);
276 
277   template <typename Context>
278   static unsigned int packable_size(const std::pair<T1, T2> & pr, const Context * context);
279 
280   template <typename BufferIter>
281   static unsigned int packed_size(BufferIter iter);
282 
283   template <typename BufferIter, typename Context>
284   static std::pair<T1, T2> unpack(BufferIter in, Context * ctx);
285 };
286 
287 template <typename T1, typename T2>
288 template <typename Context>
289 unsigned int
290 Packing<std::pair<T1, T2>,
291         typename std::enable_if<!TIMPI::StandardType<std::pair<T1, T2>>::is_fixed_type>::type>::
292     packable_size(const std::pair<T1, T2> & pr, const Context * ctx)
293 {
294   return 1 + Mixed::packable_size_comp(pr.first, ctx) +
295              Mixed::packable_size_comp(pr.second, ctx);
296 }
297 
298 template <typename T1, typename T2>
299 template <typename BufferIter>
300 unsigned int
301 Packing<std::pair<T1, T2>,
302         typename std::enable_if<!TIMPI::StandardType<std::pair<T1, T2>>::is_fixed_type>::type>::
303     packed_size(BufferIter iter)
304 {
305   // We recorded the size in the first buffer entry
306   return *iter;
307 }
308 
309 template <typename T1, typename T2>
310 template <typename OutputIter, typename Context>
311 void
312 Packing<std::pair<T1, T2>,
313         typename std::enable_if<!TIMPI::StandardType<std::pair<T1, T2>>::is_fixed_type>::type>::
314     pack(const std::pair<T1, T2> & pr, OutputIter data_out, const Context * ctx)
315 {
316   unsigned int size = packable_size(pr, ctx);
317 
318   // First write out info about the buffer size
319   *data_out++ = TIMPI::cast_int<buffer_type>(size);
320 
321   // Now pack the data
322   Mixed::pack_comp(pr.first, data_out, ctx);
323 
324   // TIMPI uses a back_inserter for `pack_range` so we don't (and can't)
325   // actually increment the iterator with operator+=. operator++ is a no-op
326   //
327   // data_out += packable_size_comp(pr.first, ctx);
328 
329   Mixed::pack_comp(pr.second, data_out, ctx);
330 }
331 
332 template <typename T1, typename T2>
333 template <typename BufferIter, typename Context>
334 std::pair<T1, T2>
335 Packing<std::pair<T1, T2>,
336         typename std::enable_if<!TIMPI::StandardType<std::pair<T1, T2>>::is_fixed_type>::type>::
337     unpack(BufferIter in, Context * ctx)
338 {
339   std::pair<T1, T2> pr;
340 
341   // We don't care about the size
342   in++;
343 
344   // Unpack the data
345   Mixed::unpack_comp(pr.first, in, ctx);
346 
347   // Make sure we increment the iterator
348   in += Mixed::packable_size_comp(pr.first, ctx);
349 
350   Mixed::unpack_comp(pr.second, in, ctx);
351 
352   return pr;
353 }
354 
355 
356 
357 
358 template <bool T1_has_buffer_type, bool MoreTypes_have_buffer_Type, typename T1, typename... MoreTypes>
359 struct TupleBufferTypeHelper {};
360 
361 template <typename T1, bool MoreTypes_have_buffer_Type>
362 struct TupleBufferTypeHelper<true, MoreTypes_have_buffer_Type, T1> {
363   typedef typename Packing<T1>::buffer_type buffer_type;
364 };
365 
366 template <typename T1, typename... MoreTypes>
367 struct TupleBufferTypeHelper<true, true, T1, MoreTypes...> {
368   static_assert(std::is_same<typename Packing<T1>::buffer_type,
369                              typename Packing<std::tuple<MoreTypes...>>::buffer_type>::value,
370                 "For ease of use we cannot pack two types that use two different buffer types");
371 
372   typedef typename Packing<T1>::buffer_type buffer_type;
373 };
374 
375 template <typename T1, typename... MoreTypes>
376 struct TupleBufferTypeHelper<true, false, T1, MoreTypes...> {
377   typedef typename Packing<T1>::buffer_type buffer_type;
378 };
379 
380 template <typename T1, typename... MoreTypes>
381 struct TupleBufferTypeHelper<false, true, T1, MoreTypes...> {
382   typedef typename Packing<std::tuple<MoreTypes...>>::buffer_type buffer_type;
383 };
384 
385 template <typename... Types>
386 struct TupleBufferType;
387 
388 template <typename T1, typename... MoreTypes>
389 struct TupleBufferType<T1, MoreTypes...> {
390   typedef typename
391     TupleBufferTypeHelper<Has_buffer_type<Packing<T1>>::value,
392                           Has_buffer_type<Packing<MoreTypes...>>::value,
393                           T1, MoreTypes...>::buffer_type buffer_type;
394 };
395 
396 
397 
398 // specializations for std::tuple
399 template <typename Enable>
400 class Packing<std::tuple<>, Enable> {};
401 
402 template <typename... Types>
403 class Packing<std::tuple<Types...>,
404               typename std::enable_if<!TIMPI::StandardType<std::tuple<Types...>>::is_fixed_type>::type>
405 {
406 public:
407   typedef typename TupleBufferType<Types...>::buffer_type buffer_type;
408 
409   typedef PackingMixedType<buffer_type> Mixed;
410 
411   template <typename OutputIter, typename Context>
412   static void pack(const std::tuple<Types...> & tup, OutputIter data_out, const Context * context);
413 
414   template <typename Context>
415   static unsigned int packable_size(const std::tuple<Types...> & tup, const Context * context);
416 
417   template <typename BufferIter>
418   static unsigned int packed_size(BufferIter iter);
419 
420   template <typename BufferIter, typename Context>
421   static std::tuple<Types...> unpack(BufferIter in, Context * ctx);
422 
423   template <typename Context,
424             std::size_t I>
425   static typename std::enable_if<I == sizeof...(Types), unsigned int>::type
426   tail_packable_size(const std::tuple<Types...> &,
427                      const Context *)
428   { return 0; }
429 
430   template <typename Context,
431             std::size_t I>
432   static typename std::enable_if<I < sizeof...(Types), unsigned int>::type
433   tail_packable_size(const std::tuple<Types...> &tup,
434                      const Context * ctx)
435   {
436     return Mixed::packable_size_comp(std::get<I>(tup), ctx) +
437       tail_packable_size<Context, I+1>(tup, ctx);
438   }
439 
440   template <typename Context,
441             typename OutputIter,
442             std::size_t I>
443   static typename std::enable_if<I == sizeof...(Types), void>::type
444   tail_pack_comp(const std::tuple<Types...> &,
445                  OutputIter,
446                  const Context *) {}
447 
448   template <typename Context,
449             typename OutputIter,
450             std::size_t I>
451   static typename std::enable_if<I < sizeof...(Types), void>::type
452   tail_pack_comp(const std::tuple<Types...> &tup,
453                  OutputIter data_out,
454                  const Context * ctx)
455   {
456     Mixed::pack_comp(std::get<I>(tup), data_out, ctx);
457     tail_pack_comp<Context, OutputIter, I+1>(tup, data_out, ctx);
458   }
459 
460   template <typename Context,
461             typename BufferIter,
462             std::size_t I>
463   static typename std::enable_if<I == sizeof...(Types), void>::type
464   tail_unpack_comp(std::tuple<Types...> &,
465                    BufferIter &,
466                    Context *) {}
467 
468   template <typename Context,
469             typename BufferIter,
470             std::size_t I>
471   static typename std::enable_if<I < sizeof...(Types), void>::type
472   tail_unpack_comp(std::tuple<Types...> &tup,
473                    BufferIter & in,
474                    Context * ctx)
475   {
476     Mixed::unpack_comp(std::get<I>(tup), in, ctx);
477 
478     // Make sure we increment the iterator.  The last increment will
479     // be unnecessary, since this is a copy of the iterator in the
480     // higher level unpacking code, but the first N-1 are critical.
481     in += Mixed::packable_size_comp(std::get<I>(tup), ctx);
482 
483     tail_unpack_comp<Context, BufferIter, I+1>(tup, in, ctx);
484   }
485 };
486 
487 
488 template <typename... Types>
489 template <typename Context>
490 unsigned int
491 Packing<std::tuple<Types...>,
492         typename std::enable_if<!TIMPI::StandardType<std::tuple<Types...>>::is_fixed_type>::type>::
493     packable_size(const std::tuple<Types...> & tup, const Context * ctx)
494 {
495   return 1 + tail_packable_size<Context, 0>(tup, ctx);
496 }
497 
498 template <typename... Types>
499 template <typename BufferIter>
500 unsigned int
501 Packing<std::tuple<Types...>,
502         typename std::enable_if<!TIMPI::StandardType<std::tuple<Types...>>::is_fixed_type>::type>::
503     packed_size(BufferIter iter)
504 {
505   // We recorded the size in the first buffer entry
506   return *iter;
507 }
508 
509 template <typename... Types>
510 template <typename OutputIter, typename Context>
511 void
512 Packing<std::tuple<Types...>,
513         typename std::enable_if<!TIMPI::StandardType<std::tuple<Types...>>::is_fixed_type>::type>::
514     pack(const std::tuple<Types...> & tup, OutputIter data_out, const Context * ctx)
515 {
516   unsigned int size = packable_size(tup, ctx);
517 
518   // First write out info about the buffer size
519   *data_out++ = TIMPI::cast_int<buffer_type>(size);
520 
521   // Now pack the data
522   tail_pack_comp<Context, OutputIter, 0>(tup, data_out, ctx);
523 }
524 
525 template <typename... Types>
526 template <typename BufferIter, typename Context>
527 std::tuple<Types...>
528 Packing<std::tuple<Types...>,
529         typename std::enable_if<!TIMPI::StandardType<std::tuple<Types...>>::is_fixed_type>::type>::
530     unpack(BufferIter in, Context * ctx)
531 {
532   std::tuple<Types...> tup;
533 
534   // We don't care about the size
535   in++;
536 
537   // Unpack the data
538   tail_unpack_comp<Context, BufferIter, 0>(tup, in, ctx);
539 
540   return tup;
541 }
542 
543 
544 
545 // specialization for std::array
546 template <typename T, std::size_t N>
547 class Packing<std::array<T, N>,
548               typename std::enable_if<!TIMPI::StandardType<T>::is_fixed_type>::type>
549 {
550 public:
551   typedef typename Packing<T>::buffer_type buffer_type;
552 
553   typedef PackingMixedType<buffer_type> Mixed;
554 
555   template <typename OutputIter, typename Context>
556   static void pack(const std::array<T, N> & a, OutputIter data_out, const Context * context);
557 
558   template <typename Context>
559   static unsigned int packable_size(const std::array<T, N> & a, const Context * context);
560 
561   template <typename BufferIter>
562   static unsigned int packed_size(BufferIter iter);
563 
564   template <typename BufferIter, typename Context>
565   static std::array<T, N> unpack(BufferIter in, Context * ctx);
566 };
567 
568 template <typename T, std::size_t N>
569 template <typename Context>
570 unsigned int
571 Packing<std::array<T, N>,
572         typename std::enable_if<!TIMPI::StandardType<T>::is_fixed_type>::type>::
573     packable_size(const std::array<T, N> & a, const Context * ctx)
574 {
575   unsigned int returnval = 1; // size
576   for (const auto & entry : a)
577     returnval += Mixed::packable_size_comp(entry, ctx);
578   return returnval;
579 }
580 
581 template <typename T, std::size_t N>
582 template <typename BufferIter>
583 unsigned int
584 Packing<std::array<T, N>,
585         typename std::enable_if<!TIMPI::StandardType<T>::is_fixed_type>::type>::
586     packed_size(BufferIter iter)
587 {
588   // We recorded the size in the first buffer entry
589   return *iter;
590 }
591 
592 template <typename T, std::size_t N>
593 template <typename OutputIter, typename Context>
594 void
595 Packing<std::array<T, N>,
596         typename std::enable_if<!TIMPI::StandardType<T>::is_fixed_type>::type>::
597     pack(const std::array<T, N> & a, OutputIter data_out, const Context * ctx)
598 {
599   unsigned int size = packable_size(a, ctx);
600 
601   // First write out info about the buffer size
602   *data_out++ = TIMPI::cast_int<buffer_type>(size);
603 
604   // Now pack the data
605   for (const auto & entry : a)
606     Mixed::pack_comp(entry, data_out, ctx);
607 }
608 
609 template <typename T, std::size_t N>
610 template <typename BufferIter, typename Context>
611 std::array<T, N>
612 Packing<std::array<T, N>,
613         typename std::enable_if<!TIMPI::StandardType<T>::is_fixed_type>::type>::
614     unpack(BufferIter in, Context * ctx)
615 {
616   std::array<T, N> a;
617 
618   // We don't care about the size
619   in++;
620 
621   // Unpack the data
622   for (auto & entry : a)
623     {
624       Mixed::unpack_comp(entry, in, ctx);
625 
626       // Make sure we increment the iterator
627       in += Mixed::packable_size_comp(entry, ctx);
628     }
629 
630   return a;
631 }
632 
633 
634 
635 // Metafunction to choose buffer types: use a specified
636 // Packing<class>::buffer_type for any class that has one; use
637 // unsigned int otherwise.
638 template <typename T, typename Enable=void>
639 struct DefaultBufferType;
640 
641 template <typename T>
642 struct DefaultBufferType <T, typename std::enable_if<Has_buffer_type<Packing<T>>::value>::type>
643 {
644   typedef typename Packing<T>::buffer_type type;
645 };
646 
647 template <typename T>
648 struct DefaultBufferType <T, typename std::enable_if<!Has_buffer_type<Packing<T>>::value>::type>
649 {
650   typedef unsigned int type;
651 };
652 
653 
654 // helper class for any homogeneous-type variable-size containers
655 // which define the usual iterator ranges, value_type, etc.
656 template <typename Container>
657 class PackingRange
658 {
659 public:
660   typedef typename
661     DefaultBufferType<typename Container::value_type>::type
662     buffer_type;
663 
664   typedef PackingMixedType<buffer_type> Mixed;
665 
666   template <typename OutputIter, typename Context>
667   static void pack(const Container & a,
668                    OutputIter data_out, const Context * context);
669 
670   template <typename Context>
671   static unsigned int packable_size(const Container & a,
672                                     const Context * context);
673 
674   template <typename BufferIter>
675   static unsigned int packed_size(BufferIter iter);
676 
677   template <typename BufferIter, typename Context>
678   static Container unpack(BufferIter in, Context * ctx);
679 };
680 
681 
682 template <typename Container>
683 template <typename Context>
684 unsigned int
685 PackingRange<Container>::packable_size(const Container & c, const Context * ctx)
686 {
687   unsigned int returnval = 1; // size
688   for (const auto & entry : c)
689     returnval += Mixed::packable_size_comp(entry, ctx);
690   return returnval;
691 }
692 
693 template <typename Container>
694 template <typename BufferIter>
695 unsigned int
696 PackingRange<Container>::packed_size(BufferIter iter)
697 {
698   // We recorded the size in the first buffer entry
699   return *iter;
700 }
701 
702 template <typename Container>
703 template <typename OutputIter, typename Context>
704 void
705 PackingRange<Container>::pack(const Container & c, OutputIter data_out, const Context * ctx)
706 {
707   unsigned int size = packable_size(c, ctx);
708 
709   // First write out info about the buffer size
710   *data_out++ = TIMPI::cast_int<buffer_type>(size);
711 
712   // Now pack the data
713   for (const auto & entry : c)
714     Mixed::pack_comp(entry, data_out, ctx);
715 }
716 
717 template <typename Container>
718 template <typename BufferIter, typename Context>
719 Container
720 PackingRange<Container>::unpack(BufferIter in, Context * ctx)
721 {
722   Container c;
723 
724   unsigned int size = packed_size(in);
725 
726   timpi_assert_greater(size, 0);
727 
728   // Now skip the size data itself
729   in++;
730   size--;
731 
732   // Unpack the data
733   std::size_t unpacked_size = 0;
734   while (unpacked_size < size)
735     {
736       typename DefaultValueType<typename Container::value_type>::type entry;
737       Mixed::unpack_comp(entry, in, ctx);
738 
739       c.insert(c.end(), entry);
740 
741       // Make sure we increment the iterator
742       const std::size_t unpacked_size_comp =
743         Mixed::packable_size_comp(entry, ctx);
744       in += unpacked_size_comp;
745       unpacked_size += unpacked_size_comp;
746     }
747 
748   // We should always finish at exactly the size we expected, not
749   // proceed past it
750   timpi_assert_equal_to(unpacked_size, size);
751 
752   return c;
753 }
754 
755 
756 
757 #define TIMPI_PACKING_RANGE_SUBCLASS(Container)           \
758 class Packing<Container> : public PackingRange<Container> \
759 {                                                         \
760 public:                                                   \
761   using typename PackingRange<Container>::buffer_type;    \
762                                                           \
763   using typename PackingRange<Container>::Mixed;          \
764                                                           \
765   using PackingRange<Container>::pack;                    \
766   using PackingRange<Container>::packable_size;           \
767   using PackingRange<Container>::packed_size;             \
768   using PackingRange<Container>::unpack;                  \
769 }
770 
771 
772 #define TIMPI_P_COMMA ,
773 
774 template <typename T, typename A>
775 TIMPI_PACKING_RANGE_SUBCLASS(std::list<T TIMPI_P_COMMA A>);
776 
777 template <typename K, typename T, typename C, typename A>
778 TIMPI_PACKING_RANGE_SUBCLASS(std::map<K TIMPI_P_COMMA T TIMPI_P_COMMA C TIMPI_P_COMMA A>);
779 
780 template <typename K, typename T, typename C, typename A>
781 TIMPI_PACKING_RANGE_SUBCLASS(std::multimap<K TIMPI_P_COMMA T TIMPI_P_COMMA C TIMPI_P_COMMA A>);
782 
783 template <typename K, typename C, typename A>
784 TIMPI_PACKING_RANGE_SUBCLASS(std::multiset<K TIMPI_P_COMMA C TIMPI_P_COMMA A>);
785 
786 template <typename K, typename C, typename A>
787 TIMPI_PACKING_RANGE_SUBCLASS(std::set<K TIMPI_P_COMMA C TIMPI_P_COMMA A>);
788 
789 template <typename K, typename T, typename H, typename KE, typename A>
790 TIMPI_PACKING_RANGE_SUBCLASS(std::unordered_map<K TIMPI_P_COMMA T TIMPI_P_COMMA H TIMPI_P_COMMA KE TIMPI_P_COMMA A>);
791 
792 template <typename K, typename T, typename H, typename KE, typename A>
793 TIMPI_PACKING_RANGE_SUBCLASS(std::unordered_multimap<K TIMPI_P_COMMA T TIMPI_P_COMMA H TIMPI_P_COMMA KE TIMPI_P_COMMA A>);
794 
795 template <typename K, typename H, typename KE, typename A>
796 TIMPI_PACKING_RANGE_SUBCLASS(std::unordered_multiset<K TIMPI_P_COMMA H TIMPI_P_COMMA KE TIMPI_P_COMMA A>);
797 
798 template <typename K, typename H, typename KE, typename A>
799 TIMPI_PACKING_RANGE_SUBCLASS(std::unordered_set<K TIMPI_P_COMMA H TIMPI_P_COMMA KE TIMPI_P_COMMA A>);
800 
801 
802 
803 #define TIMPI_HAVE_STRING_PACKING
804 
805 template <typename T>
806 class Packing<std::basic_string<T>> {
807 public:
808 
809   static const unsigned int size_bytes = 4;
810 
811   typedef T buffer_type;
812 
813   static unsigned int
814   get_string_len (typename std::vector<T>::const_iterator in)
815   {
816     unsigned int string_len = reinterpret_cast<const unsigned char &>(in[size_bytes-1]);
817     for (signed int i=size_bytes-2; i >= 0; --i)
818       {
819         string_len *= 256;
820         string_len += reinterpret_cast<const unsigned char &>(in[i]);
821       }
822     return string_len;
823   }
824 
825 
826   static unsigned int
827   packed_size (typename std::vector<T>::const_iterator in)
828   {
829     return get_string_len(in) + size_bytes;
830   }
831 
832   static unsigned int packable_size
833   (const std::basic_string<T> & s,
834    const void *)
835   {
836     return s.size() + size_bytes;
837   }
838 
839 
840   template <typename Iter>
841   static void pack (const std::basic_string<T> & b, Iter data_out,
842                     const void *)
843   {
844     unsigned int string_len = b.size();
845     for (unsigned int i=0; i != size_bytes; ++i)
846       {
847         *data_out++ = (string_len % 256);
848         string_len /= 256;
849       }
850     std::copy(b.begin(), b.end(), data_out);
851   }
852 
853   static std::basic_string<T>
854   unpack (typename std::vector<T>::const_iterator in, void *)
855   {
856     unsigned int string_len = get_string_len(in);
857 
858     std::ostringstream oss;
859     for (unsigned int i = 0; i < string_len; ++i)
860       oss << reinterpret_cast<const unsigned char &>(in[i+size_bytes]);
861 
862     in += size_bytes + string_len;
863 
864     return oss.str();
865   }
866 
867 };
868 
869 } // namespace Parallel
870 
871 } // namespace libMesh
872 
873 
874 namespace TIMPI {
875 
876 using libMesh::Parallel::Packing;
877 
878 /**
879  * Decode a range of potentially-variable-size objects from a data
880  * array.
881  */
882 template <typename Context, typename buffertype,
883           typename OutputIter, typename T>
884 inline void unpack_range (const typename std::vector<buffertype> & buffer,
885                           Context * context,
886                           OutputIter out,
887                           const T * output_type /* used only to infer T */);
888 
889 /**
890  * Encode a range of potentially-variable-size objects to a data
891  * array.
892  *
893  * The data will be buffered in vectors with lengths that do not
894  * exceed the sum of \p approx_buffer_size and the size of an
895  * individual packed object.
896  */
897 template <typename Context, typename buffertype, typename Iter>
898 inline Iter pack_range (const Context * context,
899                         Iter range_begin,
900                         const Iter range_end,
901                         typename std::vector<buffertype> & buffer,
902                         std::size_t approx_buffer_size = 1000000);
903 
904 /**
905  * Return the total buffer size needed to encode a range of
906  * potentially-variable-size objects to a data array.
907  */
908 template <typename Context, typename Iter>
909 inline std::size_t packed_range_size (const Context * context,
910                                       Iter range_begin,
911                                       const Iter range_end);
912 
913 // ------------------------------------------------------------
914 // Packing member functions, global functions
915 
916 /**
917  * Helper function for range packing
918  */
919 template <typename Context, typename Iter>
920 inline std::size_t packed_range_size (const Context * context,
921                                       Iter range_begin,
922                                       const Iter range_end)
923 {
924   typedef typename std::iterator_traits<Iter>::value_type T;
925 
926   std::size_t buffer_size = 0;
927   for (Iter range_count = range_begin;
928        range_count != range_end;
929        ++range_count)
930     {
931       buffer_size += Packing<T>::packable_size(*range_count, context);
932     }
933   return buffer_size;
934 }
935 
936 
937 /**
938  * Helper function for range packing
939  */
940 template <typename Context, typename buffertype, typename Iter>
941 inline Iter pack_range (const Context * context,
942                         Iter range_begin,
943                         const Iter range_end,
944                         std::vector<buffertype> & buffer,
945                         // When we serialize into buffers, we need to use large buffers to optimize MPI
946                         // bandwidth, but not so large as to risk allocation failures.  max_buffer_size
947                         // is measured in number of buffer type entries; number of bytes may be 4 or 8
948                         // times larger depending on configuration.
949                         std::size_t approx_buffer_size)
950 {
951   typedef typename std::iterator_traits<Iter>::value_type T;
952 
953   // Count the total size of and preallocate buffer for efficiency.
954   // Prepare to stop early if the buffer would be too large.
955   std::size_t buffer_size = 0;
956   Iter range_stop = range_begin;
957   for (; range_stop != range_end && buffer_size < approx_buffer_size;
958        ++range_stop)
959     {
960       std::size_t next_buffer_size =
961         Packing<T>::packable_size(*range_stop, context);
962       buffer_size += next_buffer_size;
963     }
964   buffer.reserve(buffer.size() + buffer_size);
965 
966   // Pack the objects into the buffer
967   for (; range_begin != range_stop; ++range_begin)
968     {
969 #ifndef NDEBUG
970       std::size_t old_size = buffer.size();
971 #endif
972 
973       Packing<T>::pack
974         (*range_begin, std::back_inserter(buffer), context);
975 
976 #ifndef NDEBUG
977       unsigned int my_packable_size =
978         Packing<T>::packable_size(*range_begin, context);
979       unsigned int my_packed_size =
980         Packing<T>::packed_size (buffer.begin() + old_size);
981       timpi_assert_equal_to (my_packable_size, my_packed_size);
982       timpi_assert_equal_to (buffer.size(), old_size + my_packable_size);
983 #endif
984     }
985 
986   return range_stop;
987 }
988 
989 
990 
991 /**
992  * Helper function for range unpacking
993  */
994 template <typename Context, typename buffertype,
995           typename OutputIter, typename T>
996 inline void unpack_range (const std::vector<buffertype> & buffer,
997                           Context * context,
998                           OutputIter out_iter,
999                           const T * /* output_type */)
1000 {
1001   // Loop through the buffer and unpack each object, returning the
1002   // object pointer via the output iterator
1003   typename std::vector<buffertype>::const_iterator
1004     next_object_start = buffer.begin();
1005 
1006   while (next_object_start < buffer.end())
1007     {
1008       *out_iter++ = Packing<T>::unpack(next_object_start, context);
1009       next_object_start +=
1010         Packing<T>::packed_size(next_object_start);
1011     }
1012 
1013   // We should have used up the exact amount of data in the buffer
1014   timpi_assert (next_object_start == buffer.end());
1015 }
1016 
1017 } // namespace TIMPI
1018 
1019 #endif // TIMPI_PACKING_H
1020