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