Skip to content

Commit 35c4be7

Browse files
committed
Small shared two way pointer
1 parent 7d247da commit 35c4be7

File tree

3 files changed

+391
-0
lines changed

3 files changed

+391
-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 const int bit_idx = std::numeric_limits<Num>::digits - 1;
223+
static const 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
@@ -47,6 +47,7 @@ SRC += unit_tests.cpp \
4747
util/optional.cpp \
4848
util/parameter_indices.cpp \
4949
util/simplify_expr.cpp \
50+
util/small_shared_two_way_ptr.cpp \
5051
util/string_utils/split_string.cpp \
5152
util/string_utils/strip_string.cpp \
5253
util/symbol_table.cpp \
+103
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
/// Author: Daniel Poetzl
2+
3+
/// \file Tests for small shared two-way pointer
4+
5+
#include <util/small_shared_two_way_ptr.h>
6+
#include <testing-utils/catch.hpp>
7+
8+
class d1t : public small_shared_two_way_pointeet<unsigned>
9+
{
10+
public:
11+
d1t() = default;
12+
13+
explicit d1t(int i) : d1(i)
14+
{
15+
}
16+
17+
int d1;
18+
};
19+
20+
class d2t : public small_shared_two_way_pointeet<unsigned>
21+
{
22+
public:
23+
d2t() = default;
24+
25+
explicit d2t(int i) : d2(i)
26+
{
27+
}
28+
29+
int d2;
30+
};
31+
32+
TEST_CASE("Small shared two-way pointer")
33+
{
34+
typedef small_shared_two_way_ptrt<d1t, d2t> spt;
35+
36+
SECTION("Types")
37+
{
38+
spt sp1;
39+
spt sp2(new d1t());
40+
spt sp3(new d2t());
41+
42+
REQUIRE(sp1.is_same_type(sp1));
43+
REQUIRE(sp2.is_same_type(sp2));
44+
REQUIRE(sp3.is_same_type(sp3));
45+
46+
REQUIRE(sp1.is_same_type(sp2));
47+
REQUIRE(sp1.is_same_type(sp3));
48+
49+
REQUIRE(!sp2.is_same_type(sp3));
50+
51+
REQUIRE(sp1.is_derived_u());
52+
REQUIRE(sp1.is_derived_v());
53+
54+
REQUIRE(sp2.is_derived_u());
55+
REQUIRE(!sp2.is_derived_v());
56+
57+
REQUIRE(sp3.is_derived_v());
58+
REQUIRE(!sp3.is_derived_u());
59+
}
60+
61+
SECTION("Basic")
62+
{
63+
spt sp1;
64+
REQUIRE(sp1.use_count() == 0);
65+
66+
const d1t *p;
67+
68+
p = sp1.get_derived_u();
69+
REQUIRE(p == nullptr);
70+
71+
spt sp2(new d1t());
72+
REQUIRE(sp2.use_count() == 1);
73+
74+
p = sp2.get_derived_u();
75+
REQUIRE(p != nullptr);
76+
77+
spt sp3(sp2);
78+
REQUIRE(sp3.is_derived_u());
79+
REQUIRE(sp2.get_derived_u() == sp3.get_derived_u());
80+
REQUIRE(sp2.use_count() == 2);
81+
REQUIRE(sp3.use_count() == 2);
82+
83+
sp1 = sp2;
84+
REQUIRE(sp1.is_derived_u());
85+
REQUIRE(sp1.get_derived_u() == sp2.get_derived_u());
86+
REQUIRE(sp1.use_count() == 3);
87+
REQUIRE(sp2.use_count() == 3);
88+
REQUIRE(sp3.use_count() == 3);
89+
}
90+
91+
SECTION("Creation")
92+
{
93+
spt sp1 = make_shared_derived_u<d1t, d2t>();
94+
spt sp2 = make_shared_derived_v<d1t, d2t>();
95+
96+
REQUIRE(!sp1.is_same_type(sp2));
97+
98+
sp1 = make_shared_derived_u<d1t, d2t>(0);
99+
sp2 = make_shared_derived_v<d1t, d2t>(0);
100+
101+
REQUIRE(!sp1.is_same_type(sp2));
102+
}
103+
}

0 commit comments

Comments
 (0)