Skip to content

Commit

Permalink
Small shared two way pointer
Browse files Browse the repository at this point in the history
  • Loading branch information
danpoe committed Apr 16, 2018
1 parent 0764f77 commit 3fc9221
Show file tree
Hide file tree
Showing 3 changed files with 385 additions and 0 deletions.
288 changes: 288 additions & 0 deletions src/util/small_shared_two_way_ptr.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,288 @@
/*******************************************************************\
Module: Small shared two-way pointer
Author: Daniel Poetzl
\*******************************************************************/

#ifndef CPROVER_UTIL_SMALL_SHARED_TWO_WAY_PTR_H
#define CPROVER_UTIL_SMALL_SHARED_TWO_WAY_PTR_H

#include <type_traits>
#include <limits>
#include <utility>

#include "invariant.h"

template <typename Num>
class small_shared_two_way_pointeet;

/// This class is similar to small_shared_ptrt and boost's intrusive_ptr. Like
/// those, it stores the use count with the pointed-to object instead of in a
/// separate control block. Additionally, it uses the MSB of the use count to
/// indicate the type of the managed object (which is either of type U or V,
/// with both being derived from T).
///
/// A possible use case is the implementation of data structures with sharing
/// that consist of two different types of objects (such as a tree with internal
/// nodes and leaf nodes). Storing the type with the use count avoids having to
/// keep a separate `type` member or using `typeid` or `dynamic_cast`. Moreover,
/// since the shared pointer is aware of the concrete type of the object being
/// stored, it can delete it without requiring a virtual destructor or custom
/// delete function (like std::shared_ptr).
template <typename U, typename V>
class small_shared_two_way_ptrt final
{
public:
typedef decltype(std::declval<U>().use_count()) use_countt;

typedef small_shared_two_way_pointeet<use_countt> pointeet;

static_assert(std::is_base_of<pointeet, U>::value, "");
static_assert(std::is_base_of<pointeet, V>::value, "");

small_shared_two_way_ptrt() = default;

explicit small_shared_two_way_ptrt(U *u) : p(u)
{
PRECONDITION(u != nullptr);
PRECONDITION(u->use_count() == 0);

p->set_derived1();
p->increment_use_count();
}

explicit small_shared_two_way_ptrt(V *v) : p(v)
{
PRECONDITION(v != nullptr);
PRECONDITION(v->use_count() == 0);

p->set_derived2();
p->increment_use_count();
}

small_shared_two_way_ptrt(const small_shared_two_way_ptrt &rhs) : p(rhs.p)
{
PRECONDITION(is_same(rhs));

if(p)
{
p->increment_use_count();
}
}

small_shared_two_way_ptrt(small_shared_two_way_ptrt &&rhs)
{
PRECONDITION(is_same(rhs));

swap(rhs);
}

small_shared_two_way_ptrt &operator=(const small_shared_two_way_ptrt &rhs)
{
PRECONDITION(is_same(rhs));

small_shared_two_way_ptrt copy(rhs);
swap(copy);
return *this;
}

small_shared_two_way_ptrt &operator=(small_shared_two_way_ptrt &&rhs)
{
PRECONDITION(is_same(rhs));

swap(rhs);
return *this;
}

~small_shared_two_way_ptrt()
{
if (!p)
{
return;
}

auto use_count = p->use_count();

if(use_count == 1)
{
if (p->is_derived1())
{
U *u = static_cast<U *>(p);
delete u;
}
else
{
V *v = static_cast<V *>(p);
delete v;
}
}
else
{
p->decrement_use_count();
}
}

void swap(small_shared_two_way_ptrt &rhs)
{
PRECONDITION(is_same(rhs));

std::swap(p, rhs.p);
}

use_countt use_count() const
{
return p ? p->use_count() : 0;
}

bool is_derived1() const
{
return p == nullptr || p->is_derived1();
}

bool is_derived2() const
{
return p == nullptr || p->is_derived2();
}

pointeet *get() const
{
return p;
}

U *get_derived1() const
{
PRECONDITION(is_derived1());

return static_cast<U *>(p);
}

V *get_derived2() const
{
PRECONDITION(is_derived2());

return static_cast<V *>(p);
}

bool is_same(const small_shared_two_way_ptrt &other) const
{
const bool b1 = p == nullptr;
const bool b2 = other.p == nullptr;

if (b1 || b2)
return true;

return p->is_same(*other.p);
}

explicit operator bool() const
{
return p != nullptr;
}

private:
pointeet *p = nullptr;
};

template <typename U, typename V, typename... Ts>
small_shared_two_way_ptrt<U, V> make_shared_derived1(Ts &&... ts)
{
return small_shared_two_way_ptrt<U, V>(new U(std::forward<Ts>(ts)...));
}

template <typename U, typename V, typename... Ts>
small_shared_two_way_ptrt<U, V> make_shared_derived2(Ts &&... ts)
{
return small_shared_two_way_ptrt<U, V>(new V(std::forward<Ts>(ts)...));
}

template <typename U, typename V>
bool operator==(
const small_shared_two_way_ptrt<U, V> &lhs,
const small_shared_two_way_ptrt<U, V> &rhs)
{
return lhs.get() == rhs.get();
}

template <typename U, typename V>
bool operator!=(
const small_shared_two_way_ptrt<U, V> &lhs,
const small_shared_two_way_ptrt<U, V> &rhs)
{
return lhs.get() != rhs.get();
}

template <typename Num>
class small_shared_two_way_pointeet
{
public:
static_assert(std::is_unsigned<Num>::value, "");

static constexpr int bit_idx = std::numeric_limits<Num>::digits - 1;
static constexpr Num mask = ~(1 << bit_idx);

small_shared_two_way_pointeet() = default;

// The use count shall be unaffected
small_shared_two_way_pointeet(const small_shared_two_way_pointeet &rhs)
{

}

// The use count shall be unaffected
small_shared_two_way_pointeet &operator=(
const small_shared_two_way_pointeet &rhs)
{
return *this;
}

Num use_count() const
{
return use_count_ & mask;
}

void increment_use_count()
{
PRECONDITION((use_count_ & mask) < mask);

use_count_++;
}

void decrement_use_count()
{
PRECONDITION((use_count_ & mask) > 0);

use_count_--;
}

void set_derived1()
{
use_count_ &= mask;
}

void set_derived2()
{
use_count_ |= ~mask;
}

bool is_derived1() const
{
return !(use_count_ & ~mask);
}

bool is_derived2() const
{
return use_count_ & ~mask;
}

bool is_same(const small_shared_two_way_pointeet &other) const
{
return !((use_count_ ^ other.use_count_) & ~mask);
}

private:
Num use_count_ = 0;
};

#endif
1 change: 1 addition & 0 deletions unit/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ SRC += unit_tests.cpp \
catch_example.cpp \
java_bytecode/java_virtual_functions/virtual_functions.cpp \
java_bytecode/java_bytecode_parse_generics/parse_generic_superclasses.cpp \
small_shared_two_way_ptr.cpp
# Empty last line

INCLUDES= -I ../src/ -I.
Expand Down
96 changes: 96 additions & 0 deletions unit/small_shared_two_way_ptr.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
#include <util/small_shared_two_way_ptr.h>
#include <testing-utils/catch.hpp>

class D1 : public small_shared_two_way_pointeet<unsigned>
{
public:
D1() = default;

D1(int i) : d1(i) {}

int d1;
};

class D2 : public small_shared_two_way_pointeet<unsigned>
{
public:
D2() = default;

D2(int i) : d2(i) {}

int d2;
};

TEST_CASE("Small shared two-way pointer")
{
typedef small_shared_two_way_ptrt<D1, D2> spt;

SECTION("Types")
{
spt sp1;
spt sp2(new D1());
spt sp3(new D2());

REQUIRE(sp1.is_same(sp1));
REQUIRE(sp2.is_same(sp2));
REQUIRE(sp3.is_same(sp3));

REQUIRE(sp1.is_same(sp2));
REQUIRE(sp1.is_same(sp3));

REQUIRE(!sp2.is_same(sp3));

REQUIRE(sp1.is_derived1());
REQUIRE(sp1.is_derived2());

REQUIRE(sp2.is_derived1());
REQUIRE(!sp2.is_derived2());

REQUIRE(sp3.is_derived2());
REQUIRE(!sp3.is_derived1());
}

SECTION("Basic")
{
spt sp1;
REQUIRE(sp1.use_count() == 0);

const D1 *p;

p = sp1.get_derived1();
REQUIRE(p == nullptr);

spt sp2(new D1());
REQUIRE(sp2.use_count() == 1);

p = sp2.get_derived1();
REQUIRE(p != nullptr);

spt sp3(sp2);
REQUIRE(sp3.is_derived1());
REQUIRE(sp2.get_derived1() == sp3.get_derived1());
REQUIRE(sp2.use_count() == 2);
REQUIRE(sp3.use_count() == 2);

sp1 = sp2;
REQUIRE(sp1.is_derived1());
REQUIRE(sp1.get_derived1() == sp2.get_derived1());
REQUIRE(sp1.use_count() == 3);
REQUIRE(sp2.use_count() == 3);
REQUIRE(sp3.use_count() == 3);
}

SECTION("Creation")
{
spt sp1 = make_shared_derived1<D1, D2>();
spt sp2 = make_shared_derived2<D1, D2>();

REQUIRE(!sp1.is_same(sp2));

sp1 = make_shared_derived1<D1, D2>(0);
sp2 = make_shared_derived2<D1, D2>(0);

REQUIRE(!sp1.is_same(sp2));
}
}

0 comments on commit 3fc9221

Please sign in to comment.