File indexing completed on 2025-01-31 10:03:44
0001
0002
0003
0004
0005
0006
0007 #ifndef BOOST_UNORDERED_DETAIL_FCA_HPP
0008 #define BOOST_UNORDERED_DETAIL_FCA_HPP
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045
0046
0047
0048
0049
0050
0051
0052
0053
0054
0055
0056
0057
0058
0059
0060
0061
0062
0063
0064
0065
0066
0067
0068
0069
0070
0071
0072
0073
0074
0075
0076
0077
0078
0079
0080
0081
0082
0083
0084
0085
0086
0087
0088
0089
0090
0091
0092
0093
0094
0095
0096
0097
0098
0099
0100
0101
0102
0103
0104
0105
0106
0107
0108
0109
0110
0111
0112
0113
0114
0115
0116 #include <boost/unordered/detail/prime_fmod.hpp>
0117 #include <boost/unordered/detail/serialize_tracked_address.hpp>
0118 #include <boost/unordered/detail/opt_storage.hpp>
0119
0120 #include <boost/assert.hpp>
0121 #include <boost/core/allocator_access.hpp>
0122 #include <boost/core/bit.hpp>
0123 #include <boost/core/empty_value.hpp>
0124 #include <boost/core/invoke_swap.hpp>
0125 #include <boost/core/no_exceptions_support.hpp>
0126 #include <boost/core/serialization.hpp>
0127 #include <boost/cstdint.hpp>
0128
0129 #include <boost/config.hpp>
0130
0131 #include <iterator>
0132
0133 namespace boost {
0134 namespace unordered {
0135 namespace detail {
0136
0137 template <class ValueType, class VoidPtr> struct node
0138 {
0139 typedef ValueType value_type;
0140 typedef typename boost::pointer_traits<VoidPtr>::template rebind_to<
0141 node>::type node_pointer;
0142
0143 node_pointer next;
0144 opt_storage<value_type> buf;
0145
0146 node() noexcept : next(), buf() {}
0147
0148 value_type* value_ptr() noexcept
0149 {
0150 return buf.address();
0151 }
0152
0153 value_type& value() noexcept
0154 {
0155 return *buf.address();
0156 }
0157 };
0158
0159 template <class Node, class VoidPtr> struct bucket
0160 {
0161 typedef typename boost::pointer_traits<VoidPtr>::template rebind_to<
0162 Node>::type node_pointer;
0163
0164 typedef typename boost::pointer_traits<VoidPtr>::template rebind_to<
0165 bucket>::type bucket_pointer;
0166
0167 node_pointer next;
0168
0169 bucket() noexcept : next() {}
0170 };
0171
0172 template <class Bucket> struct bucket_group
0173 {
0174 typedef typename Bucket::bucket_pointer bucket_pointer;
0175 typedef
0176 typename boost::pointer_traits<bucket_pointer>::template rebind_to<
0177 bucket_group>::type bucket_group_pointer;
0178
0179 BOOST_STATIC_CONSTANT(std::size_t, N = sizeof(std::size_t) * CHAR_BIT);
0180
0181 bucket_pointer buckets;
0182 std::size_t bitmask;
0183 bucket_group_pointer next, prev;
0184
0185 bucket_group() noexcept : buckets(), bitmask(0), next(), prev() {}
0186 ~bucket_group() {}
0187 };
0188
0189 inline std::size_t set_bit(std::size_t n) { return std::size_t(1) << n; }
0190
0191 inline std::size_t reset_bit(std::size_t n)
0192 {
0193 return ~(std::size_t(1) << n);
0194 }
0195
0196 inline std::size_t reset_first_bits(std::size_t n)
0197 {
0198 return ~(~(std::size_t(0)) >> (sizeof(std::size_t) * 8 - n));
0199 }
0200
0201 template <class Bucket> struct grouped_bucket_iterator
0202 {
0203 public:
0204 typedef typename Bucket::bucket_pointer bucket_pointer;
0205 typedef
0206 typename boost::pointer_traits<bucket_pointer>::template rebind_to<
0207 bucket_group<Bucket> >::type bucket_group_pointer;
0208
0209 typedef Bucket value_type;
0210 typedef typename boost::pointer_traits<bucket_pointer>::difference_type
0211 difference_type;
0212 typedef Bucket& reference;
0213 typedef Bucket* pointer;
0214 typedef std::forward_iterator_tag iterator_category;
0215
0216 private:
0217 bucket_pointer p;
0218 bucket_group_pointer pbg;
0219
0220 public:
0221 grouped_bucket_iterator() : p(), pbg() {}
0222
0223 reference operator*() const noexcept { return dereference(); }
0224 pointer operator->() const noexcept { return boost::to_address(p); }
0225
0226 grouped_bucket_iterator& operator++() noexcept
0227 {
0228 increment();
0229 return *this;
0230 }
0231
0232 grouped_bucket_iterator operator++(int) noexcept
0233 {
0234 grouped_bucket_iterator old = *this;
0235 increment();
0236 return old;
0237 }
0238
0239 bool operator==(grouped_bucket_iterator const& other) const noexcept
0240 {
0241 return equal(other);
0242 }
0243
0244 bool operator!=(grouped_bucket_iterator const& other) const noexcept
0245 {
0246 return !equal(other);
0247 }
0248
0249 private:
0250 template <typename, typename, typename>
0251 friend class grouped_bucket_array;
0252
0253 BOOST_STATIC_CONSTANT(std::size_t, N = bucket_group<Bucket>::N);
0254
0255 grouped_bucket_iterator(bucket_pointer p_, bucket_group_pointer pbg_)
0256 : p(p_), pbg(pbg_)
0257 {
0258 }
0259
0260 Bucket& dereference() const noexcept { return *p; }
0261
0262 bool equal(const grouped_bucket_iterator& x) const noexcept
0263 {
0264 return p == x.p;
0265 }
0266
0267 void increment() noexcept
0268 {
0269 std::size_t const offset = static_cast<std::size_t>(p - pbg->buckets);
0270
0271 std::size_t n = std::size_t(boost::core::countr_zero(
0272 pbg->bitmask & reset_first_bits(offset + 1)));
0273
0274 if (n < N) {
0275 p = pbg->buckets + static_cast<difference_type>(n);
0276 } else {
0277 pbg = pbg->next;
0278
0279 std::ptrdiff_t x = boost::core::countr_zero(pbg->bitmask);
0280 p = pbg->buckets + x;
0281 }
0282 }
0283
0284 template <typename Archive>
0285 friend void serialization_track(
0286 Archive& ar, grouped_bucket_iterator const& x)
0287 {
0288
0289 track_address(ar, x.p);
0290 track_address(ar, x.pbg);
0291 }
0292
0293 friend class boost::serialization::access;
0294
0295 template <typename Archive> void serialize(Archive& ar, unsigned int)
0296 {
0297
0298 serialize_tracked_address(ar, p);
0299 serialize_tracked_address(ar, pbg);
0300 }
0301 };
0302
0303 template <class Node> struct const_grouped_local_bucket_iterator;
0304
0305 template <class Node> struct grouped_local_bucket_iterator
0306 {
0307 typedef typename Node::node_pointer node_pointer;
0308
0309 public:
0310 typedef typename Node::value_type value_type;
0311 typedef value_type element_type;
0312 typedef value_type* pointer;
0313 typedef value_type& reference;
0314 typedef std::ptrdiff_t difference_type;
0315 typedef std::forward_iterator_tag iterator_category;
0316
0317 grouped_local_bucket_iterator() : p() {}
0318
0319 reference operator*() const noexcept { return dereference(); }
0320
0321 pointer operator->() const noexcept
0322 {
0323 return std::addressof(dereference());
0324 }
0325
0326 grouped_local_bucket_iterator& operator++() noexcept
0327 {
0328 increment();
0329 return *this;
0330 }
0331
0332 grouped_local_bucket_iterator operator++(int) noexcept
0333 {
0334 grouped_local_bucket_iterator old = *this;
0335 increment();
0336 return old;
0337 }
0338
0339 bool operator==(
0340 grouped_local_bucket_iterator const& other) const noexcept
0341 {
0342 return equal(other);
0343 }
0344
0345 bool operator!=(
0346 grouped_local_bucket_iterator const& other) const noexcept
0347 {
0348 return !equal(other);
0349 }
0350
0351 private:
0352 template <typename, typename, typename>
0353 friend class grouped_bucket_array;
0354
0355 template <class> friend struct const_grouped_local_bucket_iterator;
0356
0357 grouped_local_bucket_iterator(node_pointer p_) : p(p_) {}
0358
0359 value_type& dereference() const noexcept { return p->value(); }
0360
0361 bool equal(const grouped_local_bucket_iterator& x) const noexcept
0362 {
0363 return p == x.p;
0364 }
0365
0366 void increment() noexcept { p = p->next; }
0367
0368 node_pointer p;
0369 };
0370
0371 template <class Node> struct const_grouped_local_bucket_iterator
0372 {
0373 typedef typename Node::node_pointer node_pointer;
0374
0375 public:
0376 typedef typename Node::value_type const value_type;
0377 typedef value_type const element_type;
0378 typedef value_type const* pointer;
0379 typedef value_type const& reference;
0380 typedef std::ptrdiff_t difference_type;
0381 typedef std::forward_iterator_tag iterator_category;
0382
0383 const_grouped_local_bucket_iterator() : p() {}
0384 const_grouped_local_bucket_iterator(
0385 grouped_local_bucket_iterator<Node> it)
0386 : p(it.p)
0387 {
0388 }
0389
0390 reference operator*() const noexcept { return dereference(); }
0391
0392 pointer operator->() const noexcept
0393 {
0394 return std::addressof(dereference());
0395 }
0396
0397 const_grouped_local_bucket_iterator& operator++() noexcept
0398 {
0399 increment();
0400 return *this;
0401 }
0402
0403 const_grouped_local_bucket_iterator operator++(int) noexcept
0404 {
0405 const_grouped_local_bucket_iterator old = *this;
0406 increment();
0407 return old;
0408 }
0409
0410 bool operator==(
0411 const_grouped_local_bucket_iterator const& other) const noexcept
0412 {
0413 return equal(other);
0414 }
0415
0416 bool operator!=(
0417 const_grouped_local_bucket_iterator const& other) const noexcept
0418 {
0419 return !equal(other);
0420 }
0421
0422 private:
0423 template <typename, typename, typename>
0424 friend class grouped_bucket_array;
0425
0426 const_grouped_local_bucket_iterator(node_pointer p_) : p(p_) {}
0427
0428 value_type& dereference() const noexcept { return p->value(); }
0429
0430 bool equal(const const_grouped_local_bucket_iterator& x) const noexcept
0431 {
0432 return p == x.p;
0433 }
0434
0435 void increment() noexcept { p = p->next; }
0436
0437 node_pointer p;
0438 };
0439
0440 template <class T> struct span
0441 {
0442 T* begin() const noexcept { return data; }
0443 T* end() const noexcept { return data + size; }
0444
0445 T* data;
0446 std::size_t size;
0447
0448 span(T* data_, std::size_t size_) : data(data_), size(size_) {}
0449 };
0450
0451 template <class Bucket, class Allocator, class SizePolicy>
0452 class grouped_bucket_array
0453 : boost::empty_value<typename boost::allocator_rebind<Allocator,
0454 node<typename boost::allocator_value_type<Allocator>::type,
0455 typename boost::allocator_void_pointer<Allocator>::type> >::
0456 type>
0457 {
0458 typedef typename boost::allocator_value_type<Allocator>::type
0459 allocator_value_type;
0460 typedef
0461 typename boost::allocator_void_pointer<Allocator>::type void_pointer;
0462 typedef typename boost::allocator_difference_type<Allocator>::type
0463 difference_type;
0464
0465 public:
0466 typedef typename boost::allocator_rebind<Allocator,
0467 node<allocator_value_type, void_pointer> >::type node_allocator_type;
0468
0469 typedef node<allocator_value_type, void_pointer> node_type;
0470 typedef typename boost::allocator_pointer<node_allocator_type>::type
0471 node_pointer;
0472 typedef SizePolicy size_policy;
0473
0474 private:
0475 typedef typename boost::allocator_rebind<Allocator, Bucket>::type
0476 bucket_allocator_type;
0477 typedef typename boost::allocator_pointer<bucket_allocator_type>::type
0478 bucket_pointer;
0479 typedef boost::pointer_traits<bucket_pointer> bucket_pointer_traits;
0480
0481 typedef bucket_group<Bucket> group;
0482 typedef typename boost::allocator_rebind<Allocator, group>::type
0483 group_allocator_type;
0484 typedef typename boost::allocator_pointer<group_allocator_type>::type
0485 group_pointer;
0486 typedef typename boost::pointer_traits<group_pointer>
0487 group_pointer_traits;
0488
0489 public:
0490 typedef Bucket value_type;
0491 typedef Bucket bucket_type;
0492 typedef std::size_t size_type;
0493 typedef Allocator allocator_type;
0494 typedef grouped_bucket_iterator<Bucket> iterator;
0495 typedef grouped_local_bucket_iterator<node_type> local_iterator;
0496 typedef const_grouped_local_bucket_iterator<node_type>
0497 const_local_iterator;
0498
0499 private:
0500 std::size_t size_index_, size_;
0501 bucket_pointer buckets;
0502 group_pointer groups;
0503
0504 public:
0505 static std::size_t bucket_count_for(std::size_t num_buckets)
0506 {
0507 if (num_buckets == 0) {
0508 return 0;
0509 }
0510 return size_policy::size(size_policy::size_index(num_buckets));
0511 }
0512
0513 grouped_bucket_array()
0514 : empty_value<node_allocator_type>(
0515 empty_init_t(), node_allocator_type()),
0516 size_index_(0), size_(0), buckets(), groups()
0517 {
0518 }
0519
0520 grouped_bucket_array(size_type n, const Allocator& al)
0521 : empty_value<node_allocator_type>(empty_init_t(), al),
0522 size_index_(0), size_(0), buckets(), groups()
0523 {
0524 if (n == 0) {
0525 return;
0526 }
0527
0528 size_index_ = size_policy::size_index(n);
0529 size_ = size_policy::size(size_index_);
0530
0531 bucket_allocator_type bucket_alloc = this->get_bucket_allocator();
0532 group_allocator_type group_alloc = this->get_group_allocator();
0533
0534 size_type const num_buckets = buckets_len();
0535 size_type const num_groups = groups_len();
0536
0537 buckets = boost::allocator_allocate(bucket_alloc, num_buckets);
0538 BOOST_TRY
0539 {
0540 groups = boost::allocator_allocate(group_alloc, num_groups);
0541
0542 bucket_type* pb = boost::to_address(buckets);
0543 for (size_type i = 0; i < num_buckets; ++i) {
0544 new (pb + i) bucket_type();
0545 }
0546
0547 group* pg = boost::to_address(groups);
0548 for (size_type i = 0; i < num_groups; ++i) {
0549 new (pg + i) group();
0550 }
0551 }
0552 BOOST_CATCH(...)
0553 {
0554 boost::allocator_deallocate(bucket_alloc, buckets, num_buckets);
0555 BOOST_RETHROW
0556 }
0557 BOOST_CATCH_END
0558
0559 size_type const N = group::N;
0560 group_pointer pbg =
0561 groups + static_cast<difference_type>(num_groups - 1);
0562
0563 pbg->buckets =
0564 buckets + static_cast<difference_type>(N * (size_ / N));
0565 pbg->bitmask = set_bit(size_ % N);
0566 pbg->next = pbg->prev = pbg;
0567 }
0568
0569 ~grouped_bucket_array() { this->deallocate(); }
0570
0571 grouped_bucket_array(grouped_bucket_array const&) = delete;
0572 grouped_bucket_array& operator=(grouped_bucket_array const&) = delete;
0573
0574 grouped_bucket_array(grouped_bucket_array&& other) noexcept
0575 : empty_value<node_allocator_type>(
0576 empty_init_t(), other.get_node_allocator()),
0577 size_index_(other.size_index_),
0578 size_(other.size_),
0579 buckets(other.buckets),
0580 groups(other.groups)
0581 {
0582 other.size_ = 0;
0583 other.size_index_ = 0;
0584 other.buckets = bucket_pointer();
0585 other.groups = group_pointer();
0586 }
0587
0588 grouped_bucket_array& operator=(grouped_bucket_array&& other) noexcept
0589 {
0590 BOOST_ASSERT(
0591 this->get_node_allocator() == other.get_node_allocator());
0592
0593 if (this == std::addressof(other)) {
0594 return *this;
0595 }
0596
0597 this->deallocate();
0598 size_index_ = other.size_index_;
0599 size_ = other.size_;
0600
0601 buckets = other.buckets;
0602 groups = other.groups;
0603
0604 other.size_index_ = 0;
0605 other.size_ = 0;
0606 other.buckets = bucket_pointer();
0607 other.groups = group_pointer();
0608
0609 return *this;
0610 }
0611
0612 #if defined(BOOST_MSVC)
0613 #pragma warning(push)
0614 #pragma warning(disable : 4100)
0615 #endif
0616
0617 void deallocate() noexcept
0618 {
0619 if (buckets) {
0620 size_type const num_buckets = buckets_len();
0621 bucket_type* pb = boost::to_address(buckets);
0622 (void)pb;
0623
0624 for (size_type i = 0; i < num_buckets; ++i) {
0625 (pb + i)->~bucket_type();
0626 }
0627
0628 bucket_allocator_type bucket_alloc = this->get_bucket_allocator();
0629 boost::allocator_deallocate(bucket_alloc, buckets, num_buckets);
0630
0631 buckets = bucket_pointer();
0632 }
0633
0634 if (groups) {
0635 size_type const num_groups = groups_len();
0636 group* pg = boost::to_address(groups);
0637 (void)pg;
0638
0639 for (size_type i = 0; i < num_groups; ++i) {
0640 (pg + i)->~group();
0641 }
0642
0643 group_allocator_type group_alloc = this->get_group_allocator();
0644 boost::allocator_deallocate(group_alloc, groups, num_groups);
0645
0646 groups = group_pointer();
0647 }
0648 }
0649
0650 #if defined(BOOST_MSVC)
0651 #pragma warning(pop)
0652 #endif
0653
0654 void swap(grouped_bucket_array& other)
0655 {
0656 std::swap(size_index_, other.size_index_);
0657 std::swap(size_, other.size_);
0658 std::swap(buckets, other.buckets);
0659 std::swap(groups, other.groups);
0660
0661 bool b = boost::allocator_propagate_on_container_swap<
0662 allocator_type>::type::value;
0663 if (b) {
0664 boost::core::invoke_swap(
0665 get_node_allocator(), other.get_node_allocator());
0666 }
0667 }
0668
0669 node_allocator_type const& get_node_allocator() const
0670 {
0671 return empty_value<node_allocator_type>::get();
0672 }
0673
0674 node_allocator_type& get_node_allocator()
0675 {
0676 return empty_value<node_allocator_type>::get();
0677 }
0678
0679 bucket_allocator_type get_bucket_allocator() const
0680 {
0681 return this->get_node_allocator();
0682 }
0683
0684 group_allocator_type get_group_allocator() const
0685 {
0686 return this->get_node_allocator();
0687 }
0688
0689 size_type buckets_len() const noexcept { return size_ + 1; }
0690
0691 size_type groups_len() const noexcept { return size_ / group::N + 1; }
0692
0693 void reset_allocator(Allocator const& allocator_)
0694 {
0695 this->get_node_allocator() = node_allocator_type(allocator_);
0696 }
0697
0698 size_type bucket_count() const { return size_; }
0699
0700 iterator begin() const { return size_ == 0 ? end() : ++at(size_); }
0701
0702 iterator end() const
0703 {
0704
0705
0706 iterator pbg;
0707 pbg.p =
0708 buckets + static_cast<difference_type>(this->buckets_len() - 1);
0709 return pbg;
0710 }
0711
0712 local_iterator begin(size_type n) const
0713 {
0714 if (size_ == 0) {
0715 return this->end(n);
0716 }
0717
0718 return local_iterator(
0719 (buckets + static_cast<difference_type>(n))->next);
0720 }
0721
0722 local_iterator end(size_type) const { return local_iterator(); }
0723
0724 size_type capacity() const noexcept { return size_; }
0725
0726 iterator at(size_type n) const
0727 {
0728 if (size_ > 0) {
0729 std::size_t const N = group::N;
0730
0731 iterator pbg(buckets + static_cast<difference_type>(n),
0732 groups + static_cast<difference_type>(n / N));
0733
0734 return pbg;
0735 } else {
0736 return this->end();
0737 }
0738 }
0739
0740 span<Bucket> raw()
0741 {
0742 BOOST_ASSERT(size_ == 0 || size_ < this->buckets_len());
0743 return span<Bucket>(boost::to_address(buckets), size_);
0744 }
0745
0746 size_type position(std::size_t hash) const
0747 {
0748 return size_policy::position(hash, size_index_);
0749 }
0750
0751 void clear()
0752 {
0753 this->deallocate();
0754 size_index_ = 0;
0755 size_ = 0;
0756 }
0757
0758 void append_bucket_group(iterator itb) noexcept
0759 {
0760 std::size_t const N = group::N;
0761
0762 bool const is_empty_bucket = (!itb->next);
0763 if (is_empty_bucket) {
0764 bucket_pointer pb = itb.p;
0765 group_pointer pbg = itb.pbg;
0766
0767 std::size_t n =
0768 static_cast<std::size_t>(boost::to_address(pb) - &buckets[0]);
0769
0770 bool const is_empty_group = (!pbg->bitmask);
0771 if (is_empty_group) {
0772 size_type const num_groups = this->groups_len();
0773 group_pointer last_group =
0774 groups + static_cast<difference_type>(num_groups - 1);
0775
0776 pbg->buckets =
0777 buckets + static_cast<difference_type>(N * (n / N));
0778 pbg->next = last_group->next;
0779 pbg->next->prev = pbg;
0780 pbg->prev = last_group;
0781 pbg->prev->next = pbg;
0782 }
0783
0784 pbg->bitmask |= set_bit(n % N);
0785 }
0786 }
0787
0788 void insert_node(iterator itb, node_pointer p) noexcept
0789 {
0790 this->append_bucket_group(itb);
0791
0792 p->next = itb->next;
0793 itb->next = p;
0794 }
0795
0796 void insert_node_hint(
0797 iterator itb, node_pointer p, node_pointer hint) noexcept
0798 {
0799 this->append_bucket_group(itb);
0800
0801 if (hint) {
0802 p->next = hint->next;
0803 hint->next = p;
0804 } else {
0805 p->next = itb->next;
0806 itb->next = p;
0807 }
0808 }
0809
0810 void extract_node(iterator itb, node_pointer p) noexcept
0811 {
0812 node_pointer* pp = std::addressof(itb->next);
0813 while ((*pp) != p)
0814 pp = std::addressof((*pp)->next);
0815 *pp = p->next;
0816 if (!itb->next)
0817 unlink_bucket(itb);
0818 }
0819
0820 void extract_node_after(iterator itb, node_pointer* pp) noexcept
0821 {
0822 *pp = (*pp)->next;
0823 if (!itb->next)
0824 unlink_bucket(itb);
0825 }
0826
0827 void unlink_empty_buckets() noexcept
0828 {
0829 std::size_t const N = group::N;
0830
0831 group_pointer pbg = groups,
0832 last = groups + static_cast<difference_type>(
0833 this->groups_len() - 1);
0834
0835 for (; pbg != last; ++pbg) {
0836 if (!pbg->buckets) {
0837 continue;
0838 }
0839
0840 for (std::size_t n = 0; n < N; ++n) {
0841 bucket_pointer bs = pbg->buckets;
0842 bucket_type& b = bs[static_cast<std::ptrdiff_t>(n)];
0843 if (!b.next)
0844 pbg->bitmask &= reset_bit(n);
0845 }
0846 if (!pbg->bitmask && pbg->next)
0847 unlink_group(pbg);
0848 }
0849
0850
0851 for (std::size_t n = 0; n < size_ % N; ++n) {
0852 if (!pbg->buckets[static_cast<std::ptrdiff_t>(n)].next)
0853 pbg->bitmask &= reset_bit(n);
0854 }
0855 }
0856
0857 void unlink_bucket(iterator itb)
0858 {
0859 typename iterator::bucket_pointer p = itb.p;
0860 typename iterator::bucket_group_pointer pbg = itb.pbg;
0861 if (!(pbg->bitmask &=
0862 reset_bit(static_cast<std::size_t>(p - pbg->buckets))))
0863 unlink_group(pbg);
0864 }
0865
0866 private:
0867 void unlink_group(group_pointer pbg)
0868 {
0869 pbg->next->prev = pbg->prev;
0870 pbg->prev->next = pbg->next;
0871 pbg->prev = pbg->next = group_pointer();
0872 }
0873 };
0874 }
0875 }
0876 }
0877
0878 #endif