Skip to content

Commit dae08e9

Browse files
committed
Small shared two way pointer
1 parent fe4a642 commit dae08e9

File tree

3 files changed

+387
-0
lines changed

3 files changed

+387
-0
lines changed

src/util/small_shared_two_way_ptr.h

+287
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,287 @@
1+
/*******************************************************************\
2+
3+
Module: Small shared two-way pointer
4+
5+
Author: Daniel Poetzl
6+
7+
\*******************************************************************/
8+
9+
#ifndef CPROVER_UTIL_SMALL_SHARED_TWO_WAY_PTR_H
10+
#define CPROVER_UTIL_SMALL_SHARED_TWO_WAY_PTR_H
11+
12+
#include <type_traits>
13+
#include <limits>
14+
#include <utility>
15+
16+
#include "invariant.h"
17+
18+
template <typename Num>
19+
class small_shared_two_way_pointeet;
20+
21+
/// This class is similar to small_shared_ptrt and boost's intrusive_ptr. Like
22+
/// those, it stores the use count with the pointed-to object instead of in a
23+
/// separate control block. Additionally, it uses the MSB of the use count to
24+
/// indicate the type of the managed object (which is either of type U or V).
25+
///
26+
/// A possible use case is the implementation of data structures with sharing
27+
/// that consist of two different types of objects (such as a tree with internal
28+
/// nodes and leaf nodes). Storing the type with the use count avoids having to
29+
/// keep a separate `type` member or using `typeid` or `dynamic_cast`. Moreover,
30+
/// since the shared pointer is aware of the concrete type of the object being
31+
/// stored, it can delete it without requiring a virtual destructor or custom
32+
/// delete function (like std::shared_ptr).
33+
template <typename U, typename V>
34+
class small_shared_two_way_ptrt final
35+
{
36+
public:
37+
typedef decltype(std::declval<U>().use_count()) use_countt;
38+
39+
typedef small_shared_two_way_pointeet<use_countt> pointeet;
40+
41+
static_assert(std::is_base_of<pointeet, U>::value, "");
42+
static_assert(std::is_base_of<pointeet, V>::value, "");
43+
44+
small_shared_two_way_ptrt() = default;
45+
46+
explicit small_shared_two_way_ptrt(U *u) : p(u)
47+
{
48+
PRECONDITION(u != nullptr);
49+
PRECONDITION(u->use_count() == 0);
50+
51+
p->set_derived_u();
52+
p->increment_use_count();
53+
}
54+
55+
explicit small_shared_two_way_ptrt(V *v) : p(v)
56+
{
57+
PRECONDITION(v != nullptr);
58+
PRECONDITION(v->use_count() == 0);
59+
60+
p->set_derived_v();
61+
p->increment_use_count();
62+
}
63+
64+
small_shared_two_way_ptrt(const small_shared_two_way_ptrt &rhs) : p(rhs.p)
65+
{
66+
PRECONDITION(is_same_type(rhs));
67+
68+
if(p)
69+
{
70+
p->increment_use_count();
71+
}
72+
}
73+
74+
small_shared_two_way_ptrt(small_shared_two_way_ptrt &&rhs)
75+
{
76+
PRECONDITION(is_same_type(rhs));
77+
78+
swap(rhs);
79+
}
80+
81+
small_shared_two_way_ptrt &operator=(const small_shared_two_way_ptrt &rhs)
82+
{
83+
PRECONDITION(is_same_type(rhs));
84+
85+
small_shared_two_way_ptrt copy(rhs);
86+
swap(copy);
87+
return *this;
88+
}
89+
90+
small_shared_two_way_ptrt &operator=(small_shared_two_way_ptrt &&rhs)
91+
{
92+
PRECONDITION(is_same_type(rhs));
93+
94+
swap(rhs);
95+
return *this;
96+
}
97+
98+
~small_shared_two_way_ptrt()
99+
{
100+
if(!p)
101+
{
102+
return;
103+
}
104+
105+
auto use_count = p->use_count();
106+
107+
if(use_count == 1)
108+
{
109+
if(p->is_derived_u())
110+
{
111+
U *u = static_cast<U *>(p);
112+
delete u;
113+
}
114+
else
115+
{
116+
V *v = static_cast<V *>(p);
117+
delete v;
118+
}
119+
}
120+
else
121+
{
122+
p->decrement_use_count();
123+
}
124+
}
125+
126+
void swap(small_shared_two_way_ptrt &rhs)
127+
{
128+
PRECONDITION(is_same_type(rhs));
129+
130+
std::swap(p, rhs.p);
131+
}
132+
133+
use_countt use_count() const
134+
{
135+
return p ? p->use_count() : 0;
136+
}
137+
138+
/// Checks if converting the held raw pointer to `U*` is valid
139+
bool is_derived_u() const
140+
{
141+
return p == nullptr || p->is_derived_u();
142+
}
143+
144+
/// Checks if converting the held raw pointer to `V*` is valid
145+
bool is_derived_v() const
146+
{
147+
return p == nullptr || p->is_derived_v();
148+
}
149+
150+
pointeet *get() const
151+
{
152+
return p;
153+
}
154+
155+
U *get_derived_u() const
156+
{
157+
PRECONDITION(is_derived_u());
158+
159+
return static_cast<U *>(p);
160+
}
161+
162+
V *get_derived_v() const
163+
{
164+
PRECONDITION(is_derived_v());
165+
166+
return static_cast<V *>(p);
167+
}
168+
169+
/// Checks if the raw pointers held by `*this` and `other` both can be
170+
/// converted to either U* or V*
171+
bool is_same_type(const small_shared_two_way_ptrt &other) const
172+
{
173+
if (p == nullptr || other.p == nullptr)
174+
return true;
175+
176+
return p->is_same_type(*other.p);
177+
}
178+
179+
explicit operator bool() const
180+
{
181+
return p != nullptr;
182+
}
183+
184+
private:
185+
pointeet *p = nullptr;
186+
};
187+
188+
template <typename U, typename V, typename... Ts>
189+
small_shared_two_way_ptrt<U, V> make_shared_derived_u(Ts &&... ts)
190+
{
191+
return small_shared_two_way_ptrt<U, V>(new U(std::forward<Ts>(ts)...));
192+
}
193+
194+
template <typename U, typename V, typename... Ts>
195+
small_shared_two_way_ptrt<U, V> make_shared_derived_v(Ts &&... ts)
196+
{
197+
return small_shared_two_way_ptrt<U, V>(new V(std::forward<Ts>(ts)...));
198+
}
199+
200+
template <typename U, typename V>
201+
bool operator==(
202+
const small_shared_two_way_ptrt<U, V> &lhs,
203+
const small_shared_two_way_ptrt<U, V> &rhs)
204+
{
205+
return lhs.get() == rhs.get();
206+
}
207+
208+
template <typename U, typename V>
209+
bool operator!=(
210+
const small_shared_two_way_ptrt<U, V> &lhs,
211+
const small_shared_two_way_ptrt<U, V> &rhs)
212+
{
213+
return lhs.get() != rhs.get();
214+
}
215+
216+
template <typename Num>
217+
class small_shared_two_way_pointeet
218+
{
219+
public:
220+
static_assert(std::is_unsigned<Num>::value, "");
221+
222+
static constexpr int bit_idx = std::numeric_limits<Num>::digits - 1;
223+
static constexpr Num mask = ~((Num)1 << bit_idx);
224+
225+
small_shared_two_way_pointeet() = default;
226+
227+
// The use count shall be unaffected
228+
small_shared_two_way_pointeet(const small_shared_two_way_pointeet &rhs)
229+
{
230+
}
231+
232+
// The use count shall be unaffected
233+
small_shared_two_way_pointeet &
234+
operator=(const small_shared_two_way_pointeet &rhs)
235+
{
236+
return *this;
237+
}
238+
239+
Num use_count() const
240+
{
241+
return use_count_ & mask;
242+
}
243+
244+
void increment_use_count()
245+
{
246+
PRECONDITION((use_count_ & mask) < mask);
247+
248+
use_count_++;
249+
}
250+
251+
void decrement_use_count()
252+
{
253+
PRECONDITION((use_count_ & mask) > 0);
254+
255+
use_count_--;
256+
}
257+
258+
void set_derived_u()
259+
{
260+
use_count_ &= mask;
261+
}
262+
263+
void set_derived_v()
264+
{
265+
use_count_ |= ~mask;
266+
}
267+
268+
bool is_derived_u() const
269+
{
270+
return !(use_count_ & ~mask);
271+
}
272+
273+
bool is_derived_v() const
274+
{
275+
return use_count_ & ~mask;
276+
}
277+
278+
bool is_same_type(const small_shared_two_way_pointeet &other) const
279+
{
280+
return !((use_count_ ^ other.use_count_) & ~mask);
281+
}
282+
283+
private:
284+
Num use_count_ = 0;
285+
};
286+
287+
#endif

unit/Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ SRC += unit_tests.cpp \
5050
util/message.cpp \
5151
util/parameter_indices.cpp \
5252
util/simplify_expr.cpp \
53+
util/small_shared_two_way_ptr.cpp \
5354
util/symbol_table.cpp \
5455
catch_example.cpp \
5556
java_bytecode/java_virtual_functions/virtual_functions.cpp \
+99
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
#include <util/small_shared_two_way_ptr.h>
2+
#include <testing-utils/catch.hpp>
3+
4+
class D1 : public small_shared_two_way_pointeet<unsigned>
5+
{
6+
public:
7+
D1() = default;
8+
9+
D1(int i) : d1(i)
10+
{
11+
}
12+
13+
int d1;
14+
};
15+
16+
class D2 : public small_shared_two_way_pointeet<unsigned>
17+
{
18+
public:
19+
D2() = default;
20+
21+
D2(int i) : d2(i)
22+
{
23+
}
24+
25+
int d2;
26+
};
27+
28+
TEST_CASE("Small shared two-way pointer")
29+
{
30+
typedef small_shared_two_way_ptrt<D1, D2> spt;
31+
32+
SECTION("Types")
33+
{
34+
spt sp1;
35+
spt sp2(new D1());
36+
spt sp3(new D2());
37+
38+
REQUIRE(sp1.is_same_type(sp1));
39+
REQUIRE(sp2.is_same_type(sp2));
40+
REQUIRE(sp3.is_same_type(sp3));
41+
42+
REQUIRE(sp1.is_same_type(sp2));
43+
REQUIRE(sp1.is_same_type(sp3));
44+
45+
REQUIRE(!sp2.is_same_type(sp3));
46+
47+
REQUIRE(sp1.is_derived_u());
48+
REQUIRE(sp1.is_derived_v());
49+
50+
REQUIRE(sp2.is_derived_u());
51+
REQUIRE(!sp2.is_derived_v());
52+
53+
REQUIRE(sp3.is_derived_v());
54+
REQUIRE(!sp3.is_derived_u());
55+
}
56+
57+
SECTION("Basic")
58+
{
59+
spt sp1;
60+
REQUIRE(sp1.use_count() == 0);
61+
62+
const D1 *p;
63+
64+
p = sp1.get_derived_u();
65+
REQUIRE(p == nullptr);
66+
67+
spt sp2(new D1());
68+
REQUIRE(sp2.use_count() == 1);
69+
70+
p = sp2.get_derived_u();
71+
REQUIRE(p != nullptr);
72+
73+
spt sp3(sp2);
74+
REQUIRE(sp3.is_derived_u());
75+
REQUIRE(sp2.get_derived_u() == sp3.get_derived_u());
76+
REQUIRE(sp2.use_count() == 2);
77+
REQUIRE(sp3.use_count() == 2);
78+
79+
sp1 = sp2;
80+
REQUIRE(sp1.is_derived_u());
81+
REQUIRE(sp1.get_derived_u() == sp2.get_derived_u());
82+
REQUIRE(sp1.use_count() == 3);
83+
REQUIRE(sp2.use_count() == 3);
84+
REQUIRE(sp3.use_count() == 3);
85+
}
86+
87+
SECTION("Creation")
88+
{
89+
spt sp1 = make_shared_derived_u<D1, D2>();
90+
spt sp2 = make_shared_derived_v<D1, D2>();
91+
92+
REQUIRE(!sp1.is_same_type(sp2));
93+
94+
sp1 = make_shared_derived_u<D1, D2>(0);
95+
sp2 = make_shared_derived_v<D1, D2>(0);
96+
97+
REQUIRE(!sp1.is_same_type(sp2));
98+
}
99+
}

0 commit comments

Comments
 (0)