@@ -1213,6 +1213,104 @@ private:
12131213 __node_pointer __cache_root_;
12141214 __node_pointer __cache_elem_;
12151215 };
1216+
1217+ class __tree_deleter {
1218+ __node_allocator& __alloc_;
1219+
1220+ public:
1221+ using pointer = __node_pointer;
1222+
1223+ _LIBCPP_HIDE_FROM_ABI __tree_deleter (__node_allocator& __alloc) : __alloc_(__alloc) {}
1224+
1225+ #ifdef _LIBCPP_COMPILER_CLANG_BASED // FIXME: GCC complains about not being able to always_inline a recursive function
1226+ _LIBCPP_HIDE_FROM_ABI
1227+ #endif
1228+ void
1229+ operator ()(__node_pointer __ptr) {
1230+ if (!__ptr)
1231+ return ;
1232+
1233+ (*this )(static_cast <__node_pointer>(__ptr->__left_ ));
1234+
1235+ auto __right = __ptr->__right_ ;
1236+
1237+ __node_traits::destroy (__alloc_, std::addressof (__ptr->__value_ ));
1238+ __node_traits::deallocate (__alloc_, __ptr, 1 );
1239+
1240+ (*this )(static_cast <__node_pointer>(__right));
1241+ }
1242+ };
1243+
1244+ // This copy construction will always produce a correct red-black-tree assuming the incoming tree is correct, since we
1245+ // copy the exact structure 1:1. Since this is for copy construction _only_ we know that we get a correct tree. If we
1246+ // didn't get a correct tree, the invariants of __tree are broken and we have a much bigger problem than an improperly
1247+ // balanced tree.
1248+ #ifdef _LIBCPP_COMPILER_CLANG_BASED // FIXME: GCC complains about not being able to always_inline a recursive function
1249+ _LIBCPP_HIDE_FROM_ABI
1250+ #endif
1251+ __node_pointer
1252+ __copy_construct_tree (__node_pointer __src) {
1253+ if (!__src)
1254+ return nullptr ;
1255+
1256+ __node_holder __new_node = __construct_node (__src->__value_ );
1257+
1258+ unique_ptr<__node, __tree_deleter> __left (
1259+ __copy_construct_tree (static_cast <__node_pointer>(__src->__left_ )), __node_alloc_);
1260+ __node_pointer __right = __copy_construct_tree (static_cast <__node_pointer>(__src->__right_ ));
1261+
1262+ __node_pointer __new_node_ptr = __new_node.release ();
1263+
1264+ __new_node_ptr->__is_black_ = __src->__is_black_ ;
1265+ __new_node_ptr->__left_ = static_cast <__node_base_pointer>(__left.release ());
1266+ __new_node_ptr->__right_ = static_cast <__node_base_pointer>(__right);
1267+ if (__new_node_ptr->__left_ )
1268+ __new_node_ptr->__left_ ->__parent_ = static_cast <__end_node_pointer>(__new_node_ptr);
1269+ if (__new_node_ptr->__right_ )
1270+ __new_node_ptr->__right_ ->__parent_ = static_cast <__end_node_pointer>(__new_node_ptr);
1271+ return __new_node_ptr;
1272+ }
1273+
1274+ // This copy assignment will always produce a correct red-black-tree assuming the incoming tree is correct, since our
1275+ // own tree is a red-black-tree and the incoming tree is a red-black-tree. The invariants of a red-black-tree are
1276+ // temporarily not met until all of the incoming red-black tree is copied.
1277+ #ifdef _LIBCPP_COMPILER_CLANG_BASED // FIXME: GCC complains about not being able to always_inline a recursive function
1278+ _LIBCPP_HIDE_FROM_ABI
1279+ #endif
1280+ __node_pointer
1281+ __copy_assign_tree (__node_pointer __dest, __node_pointer __src) {
1282+ if (!__src) {
1283+ destroy (__dest);
1284+ return nullptr ;
1285+ }
1286+
1287+ __assign_value (__dest->__value_ , __src->__value_ );
1288+ __dest->__is_black_ = __src->__is_black_ ;
1289+
1290+ // If we already have a left node in the destination tree, reuse it and copy-assign recursively
1291+ if (__dest->__left_ ) {
1292+ __dest->__left_ = static_cast <__node_base_pointer>(__copy_assign_tree (
1293+ static_cast <__node_pointer>(__dest->__left_ ), static_cast <__node_pointer>(__src->__left_ )));
1294+
1295+ // Otherwise, we must create new nodes; copy-construct from here on
1296+ } else if (__src->__left_ ) {
1297+ auto __new_left = __copy_construct_tree (static_cast <__node_pointer>(__src->__left_ ));
1298+ __dest->__left_ = static_cast <__node_base_pointer>(__new_left);
1299+ __new_left->__parent_ = static_cast <__end_node_pointer>(__dest);
1300+ }
1301+
1302+ // Identical to the left case above, just for the right nodes
1303+ if (__dest->__right_ ) {
1304+ __dest->__right_ = static_cast <__node_base_pointer>(__copy_assign_tree (
1305+ static_cast <__node_pointer>(__dest->__right_ ), static_cast <__node_pointer>(__src->__right_ )));
1306+ } else if (__src->__right_ ) {
1307+ auto __new_right = __copy_construct_tree (static_cast <__node_pointer>(__src->__right_ ));
1308+ __dest->__right_ = static_cast <__node_base_pointer>(__new_right);
1309+ __new_right->__parent_ = static_cast <__end_node_pointer>(__dest);
1310+ }
1311+
1312+ return __dest;
1313+ }
12161314};
12171315
12181316template <class _Tp , class _Compare , class _Allocator >
@@ -1277,11 +1375,22 @@ __tree<_Tp, _Compare, _Allocator>::_DetachedTreeCache::__detach_next(__node_poin
12771375
12781376template <class _Tp , class _Compare , class _Allocator >
12791377__tree<_Tp, _Compare, _Allocator>& __tree<_Tp, _Compare, _Allocator>::operator =(const __tree& __t ) {
1280- if (this != std::addressof (__t )) {
1281- value_comp () = __t .value_comp ();
1282- __copy_assign_alloc (__t );
1283- __assign_multi (__t .begin (), __t .end ());
1378+ if (this == std::addressof (__t ))
1379+ return *this ;
1380+
1381+ value_comp () = __t .value_comp ();
1382+ __copy_assign_alloc (__t );
1383+
1384+ if (__size_ != 0 ) {
1385+ *__root_ptr () = static_cast <__node_base_pointer>(__copy_assign_tree (__root (), __t .__root ()));
1386+ } else {
1387+ *__root_ptr () = static_cast <__node_base_pointer>(__copy_construct_tree (__t .__root ()));
1388+ if (__root ())
1389+ __root ()->__parent_ = __end_node ();
12841390 }
1391+ __begin_node_ = static_cast <__end_node_pointer>(std::__tree_min (static_cast <__node_base_pointer>(__end_node ())));
1392+ __size_ = __t .size ();
1393+
12851394 return *this ;
12861395}
12871396
@@ -1327,11 +1436,17 @@ void __tree<_Tp, _Compare, _Allocator>::__assign_multi(_InputIterator __first, _
13271436
13281437template <class _Tp , class _Compare , class _Allocator >
13291438__tree<_Tp, _Compare, _Allocator>::__tree(const __tree& __t )
1330- : __begin_node_(),
1439+ : __begin_node_(__end_node() ),
13311440 __node_alloc_ (__node_traits::select_on_container_copy_construction(__t .__node_alloc())),
13321441 __size_(0 ),
13331442 __value_comp_(__t .value_comp()) {
1334- __begin_node_ = __end_node ();
1443+ if (__t .size () == 0 )
1444+ return ;
1445+
1446+ *__root_ptr () = static_cast <__node_base_pointer>(__copy_construct_tree (__t .__root ()));
1447+ __root ()->__parent_ = __end_node ();
1448+ __begin_node_ = static_cast <__end_node_pointer>(std::__tree_min (static_cast <__node_base_pointer>(__end_node ())));
1449+ __size_ = __t .size ();
13351450}
13361451
13371452template <class _Tp , class _Compare , class _Allocator >
@@ -1430,13 +1545,7 @@ __tree<_Tp, _Compare, _Allocator>::~__tree() {
14301545
14311546template <class _Tp , class _Compare , class _Allocator >
14321547void __tree<_Tp, _Compare, _Allocator>::destroy(__node_pointer __nd) _NOEXCEPT {
1433- if (__nd != nullptr ) {
1434- destroy (static_cast <__node_pointer>(__nd->__left_ ));
1435- destroy (static_cast <__node_pointer>(__nd->__right_ ));
1436- __node_allocator& __na = __node_alloc ();
1437- __node_traits::destroy (__na, std::addressof (__nd->__value_ ));
1438- __node_traits::deallocate (__na, __nd, 1 );
1439- }
1548+ (__tree_deleter (__node_alloc_))(__nd);
14401549}
14411550
14421551template <class _Tp , class _Compare , class _Allocator >
0 commit comments