[IntervalMap] Add move and copy ctors and assignment operators
And update the unittest. Reviewed By: dblaikie Differential Revision: https://reviews.llvm.org/D136242
This commit is contained in:
parent
e8541e4b42
commit
dbbe82c6fd
|
@ -975,13 +975,13 @@ private:
|
||||||
// 0: Leaves in root.
|
// 0: Leaves in root.
|
||||||
// 1: Root points to leaf.
|
// 1: Root points to leaf.
|
||||||
// 2: root->branch->leaf ...
|
// 2: root->branch->leaf ...
|
||||||
unsigned height;
|
unsigned height = 0;
|
||||||
|
|
||||||
// Number of entries in the root node.
|
// Number of entries in the root node.
|
||||||
unsigned rootSize;
|
unsigned rootSize = 0;
|
||||||
|
|
||||||
// Allocator used for creating external nodes.
|
// Allocator used for creating external nodes.
|
||||||
Allocator &allocator;
|
Allocator *allocator = nullptr;
|
||||||
|
|
||||||
const RootLeaf &rootLeaf() const {
|
const RootLeaf &rootLeaf() const {
|
||||||
assert(!branched() && "Cannot acces leaf data in branched root");
|
assert(!branched() && "Cannot acces leaf data in branched root");
|
||||||
|
@ -1007,12 +1007,12 @@ private:
|
||||||
KeyT &rootBranchStart() { return rootBranchData().start; }
|
KeyT &rootBranchStart() { return rootBranchData().start; }
|
||||||
|
|
||||||
template <typename NodeT> NodeT *newNode() {
|
template <typename NodeT> NodeT *newNode() {
|
||||||
return new(allocator.template Allocate<NodeT>()) NodeT();
|
return new (allocator->template Allocate<NodeT>()) NodeT();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename NodeT> void deleteNode(NodeT *P) {
|
template <typename NodeT> void deleteNode(NodeT *P) {
|
||||||
P->~NodeT();
|
P->~NodeT();
|
||||||
allocator.Deallocate(P);
|
allocator->Deallocate(P);
|
||||||
}
|
}
|
||||||
|
|
||||||
IdxPair branchRoot(unsigned Position);
|
IdxPair branchRoot(unsigned Position);
|
||||||
|
@ -1038,20 +1038,59 @@ private:
|
||||||
void deleteNode(IntervalMapImpl::NodeRef Node, unsigned Level);
|
void deleteNode(IntervalMapImpl::NodeRef Node, unsigned Level);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit IntervalMap(Allocator &a) : height(0), rootSize(0), allocator(a) {
|
explicit IntervalMap(Allocator &a) : allocator(&a) {
|
||||||
new(&rootLeaf()) RootLeaf();
|
new (&rootLeaf()) RootLeaf();
|
||||||
}
|
}
|
||||||
|
|
||||||
// The default copy/move constructors and assignment operators would perform
|
///@{
|
||||||
// a shallow copy, leading to an incorrect internal state. To prevent
|
/// NOTE: The moved-from or copied-from object's allocator needs to have a
|
||||||
// accidental use, explicitly delete these operators.
|
/// lifetime equal to or exceeding the moved-to or copied-to object to avoid
|
||||||
// If necessary, implement them to perform a deep copy.
|
/// undefined behaviour.
|
||||||
IntervalMap(const IntervalMap &Other) = delete;
|
IntervalMap(IntervalMap const &RHS) : IntervalMap(*RHS.allocator) {
|
||||||
IntervalMap(IntervalMap &&Other) = delete;
|
// Future-proofing assertion: this function assumes the IntervalMap
|
||||||
// Note: these are already implicitly deleted, because RootLeaf (union
|
// constructor doesn't add any nodes.
|
||||||
// member) has a non-trivial assignment operator (because of std::pair).
|
assert(empty() && "Expected emptry tree");
|
||||||
IntervalMap &operator=(const IntervalMap &Other) = delete;
|
*this = RHS;
|
||||||
IntervalMap &operator=(IntervalMap &&Other) = delete;
|
}
|
||||||
|
IntervalMap &operator=(IntervalMap const &RHS) {
|
||||||
|
clear();
|
||||||
|
allocator = RHS.allocator;
|
||||||
|
for (auto It = RHS.begin(), End = RHS.end(); It != End; ++It)
|
||||||
|
insert(It.start(), It.stop(), It.value());
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
IntervalMap(IntervalMap &&RHS) : IntervalMap(*RHS.allocator) {
|
||||||
|
// Future-proofing assertion: this function assumes the IntervalMap
|
||||||
|
// constructor doesn't add any nodes.
|
||||||
|
assert(empty() && "Expected emptry tree");
|
||||||
|
*this = std::move(RHS);
|
||||||
|
}
|
||||||
|
IntervalMap &operator=(IntervalMap &&RHS) {
|
||||||
|
// Calling clear deallocates memory and switches to rootLeaf.
|
||||||
|
clear();
|
||||||
|
// Destroy the new rootLeaf.
|
||||||
|
rootLeaf().~RootLeaf();
|
||||||
|
|
||||||
|
height = RHS.height;
|
||||||
|
rootSize = RHS.rootSize;
|
||||||
|
allocator = RHS.allocator;
|
||||||
|
|
||||||
|
// rootLeaf and rootBranch are both uninitialized. Move RHS data into
|
||||||
|
// appropriate field.
|
||||||
|
if (RHS.branched()) {
|
||||||
|
rootBranch() = std::move(RHS.rootBranch());
|
||||||
|
// Prevent RHS deallocating memory LHS now owns by replacing RHS
|
||||||
|
// rootBranch with a new rootLeaf.
|
||||||
|
RHS.rootBranch().~RootBranch();
|
||||||
|
RHS.height = 0;
|
||||||
|
new (&RHS.rootLeaf()) RootLeaf();
|
||||||
|
} else {
|
||||||
|
rootLeaf() = std::move(RHS.rootLeaf());
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
///@}
|
||||||
|
|
||||||
~IntervalMap() {
|
~IntervalMap() {
|
||||||
clear();
|
clear();
|
||||||
|
|
|
@ -18,15 +18,6 @@ typedef IntervalMap<unsigned, unsigned, 4> UUMap;
|
||||||
typedef IntervalMap<unsigned, unsigned, 4,
|
typedef IntervalMap<unsigned, unsigned, 4,
|
||||||
IntervalMapHalfOpenInfo<unsigned>> UUHalfOpenMap;
|
IntervalMapHalfOpenInfo<unsigned>> UUHalfOpenMap;
|
||||||
|
|
||||||
static_assert(!std::is_copy_constructible<UUMap>::value,
|
|
||||||
"IntervalMap copy constructor should be deleted");
|
|
||||||
static_assert(!std::is_move_constructible<UUMap>::value,
|
|
||||||
"IntervalMap move constructor should be deleted");
|
|
||||||
static_assert(!std::is_copy_assignable<UUMap>::value,
|
|
||||||
"IntervalMap copy assignment should be deleted");
|
|
||||||
static_assert(!std::is_move_assignable<UUMap>::value,
|
|
||||||
"IntervalMap move assignment should be deleted");
|
|
||||||
|
|
||||||
// Empty map tests
|
// Empty map tests
|
||||||
TEST(IntervalMapTest, EmptyMap) {
|
TEST(IntervalMapTest, EmptyMap) {
|
||||||
UUMap::Allocator allocator;
|
UUMap::Allocator allocator;
|
||||||
|
@ -630,26 +621,73 @@ TEST(IntervalMapTest, RandomCoalescing) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void setupOverlaps(UUMap &M) {
|
||||||
|
M.insert(10, 20, 0);
|
||||||
|
M.insert(30, 40, 0);
|
||||||
|
M.insert(50, 60, 0);
|
||||||
|
// Add extra nodes to force allocations.
|
||||||
|
for (int i = 70; i < 100; i += 2)
|
||||||
|
M.insert(i, i + 1, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void checkOverlaps(UUMap &M) {
|
||||||
|
EXPECT_FALSE(M.overlaps(0, 9));
|
||||||
|
EXPECT_TRUE(M.overlaps(0, 10));
|
||||||
|
EXPECT_TRUE(M.overlaps(0, 15));
|
||||||
|
EXPECT_TRUE(M.overlaps(0, 25));
|
||||||
|
EXPECT_TRUE(M.overlaps(0, 45));
|
||||||
|
EXPECT_TRUE(M.overlaps(10, 45));
|
||||||
|
EXPECT_TRUE(M.overlaps(30, 45));
|
||||||
|
EXPECT_TRUE(M.overlaps(35, 36));
|
||||||
|
EXPECT_TRUE(M.overlaps(40, 45));
|
||||||
|
EXPECT_FALSE(M.overlaps(45, 45));
|
||||||
|
EXPECT_TRUE(M.overlaps(60, 60));
|
||||||
|
EXPECT_TRUE(M.overlaps(60, 66));
|
||||||
|
EXPECT_FALSE(M.overlaps(66, 66));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(IntervalMapTest, Copy) {
|
||||||
|
// Test that copy assignment and initialization works.
|
||||||
|
UUHalfOpenMap::Allocator Allocator;
|
||||||
|
UUMap Src(Allocator);
|
||||||
|
setupOverlaps(Src);
|
||||||
|
|
||||||
|
UUMap CopyAssignmentDst(Allocator);
|
||||||
|
CopyAssignmentDst = Src;
|
||||||
|
|
||||||
|
UUMap CopyInitDst(Src);
|
||||||
|
|
||||||
|
checkOverlaps(Src);
|
||||||
|
Src.clear();
|
||||||
|
|
||||||
|
checkOverlaps(CopyAssignmentDst);
|
||||||
|
checkOverlaps(CopyInitDst);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(IntervalMapTest, Move) {
|
||||||
|
// Test that move assignment and initialization works.
|
||||||
|
UUHalfOpenMap::Allocator Allocator;
|
||||||
|
// Double check that MoveAssignmentDst owns all its data by moving from an
|
||||||
|
// object that is destroyed before we call checkOverlaps.
|
||||||
|
UUMap MoveAssignmentDst(Allocator);
|
||||||
|
{
|
||||||
|
UUMap Src(Allocator);
|
||||||
|
setupOverlaps(Src);
|
||||||
|
MoveAssignmentDst = std::move(Src);
|
||||||
|
}
|
||||||
|
checkOverlaps(MoveAssignmentDst);
|
||||||
|
|
||||||
|
UUMap MoveInitSrc(Allocator);
|
||||||
|
setupOverlaps(MoveInitSrc);
|
||||||
|
UUMap MoveInitDst(std::move(MoveInitSrc));
|
||||||
|
checkOverlaps(MoveInitDst);
|
||||||
|
}
|
||||||
|
|
||||||
TEST(IntervalMapTest, Overlaps) {
|
TEST(IntervalMapTest, Overlaps) {
|
||||||
UUMap::Allocator allocator;
|
UUMap::Allocator allocator;
|
||||||
UUMap map(allocator);
|
UUMap map(allocator);
|
||||||
map.insert(10, 20, 0);
|
setupOverlaps(map);
|
||||||
map.insert(30, 40, 0);
|
checkOverlaps(map);
|
||||||
map.insert(50, 60, 0);
|
|
||||||
|
|
||||||
EXPECT_FALSE(map.overlaps(0, 9));
|
|
||||||
EXPECT_TRUE(map.overlaps(0, 10));
|
|
||||||
EXPECT_TRUE(map.overlaps(0, 15));
|
|
||||||
EXPECT_TRUE(map.overlaps(0, 25));
|
|
||||||
EXPECT_TRUE(map.overlaps(0, 45));
|
|
||||||
EXPECT_TRUE(map.overlaps(10, 45));
|
|
||||||
EXPECT_TRUE(map.overlaps(30, 45));
|
|
||||||
EXPECT_TRUE(map.overlaps(35, 36));
|
|
||||||
EXPECT_TRUE(map.overlaps(40, 45));
|
|
||||||
EXPECT_FALSE(map.overlaps(45, 45));
|
|
||||||
EXPECT_TRUE(map.overlaps(60, 60));
|
|
||||||
EXPECT_TRUE(map.overlaps(60, 66));
|
|
||||||
EXPECT_FALSE(map.overlaps(66, 66));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(IntervalMapTest, OverlapsHalfOpen) {
|
TEST(IntervalMapTest, OverlapsHalfOpen) {
|
||||||
|
|
Loading…
Reference in New Issue