Skip to content

Commit 3fc9221

Browse files
committed
Small shared two way pointer
1 parent 0764f77 commit 3fc9221

File tree

3 files changed

+385
-0
lines changed

3 files changed

+385
-0
lines changed

src/util/small_shared_two_way_ptr.h

+288
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,288 @@
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+
/// with both being derived from T).
26+
///
27+
/// A possible use case is the implementation of data structures with sharing
28+
/// that consist of two different types of objects (such as a tree with internal
29+
/// nodes and leaf nodes). Storing the type with the use count avoids having to
30+
/// keep a separate `type` member or using `typeid` or `dynamic_cast`. Moreover,
31+
/// since the shared pointer is aware of the concrete type of the object being
32+
/// stored, it can delete it without requiring a virtual destructor or custom
33+
/// delete function (like std::shared_ptr).
34+
template <typename U, typename V>
35+
class small_shared_two_way_ptrt final
36+
{
37+
public:
38+
typedef decltype(std::declval<U>().use_count()) use_countt;
39+
40+
typedef small_shared_two_way_pointeet<use_countt> pointeet;
41+
42+
static_assert(std::is_base_of<pointeet, U>::value, "");
43+
static_assert(std::is_base_of<pointeet, V>::value, "");
44+
45+
small_shared_two_way_ptrt() = default;
46+
47+
explicit small_shared_two_way_ptrt(U *u) : p(u)
48+
{
49+
PRECONDITION(u != nullptr);
50+
PRECONDITION(u->use_count() == 0);
51+
52+
p->set_derived1();
53+
p->increment_use_count();
54+
}
55+
56+
explicit small_shared_two_way_ptrt(V *v) : p(v)
57+
{
58+
PRECONDITION(v != nullptr);
59+
PRECONDITION(v->use_count() == 0);
60+
61+
p->set_derived2();
62+
p->increment_use_count();
63+
}
64+
65+
small_shared_two_way_ptrt(const small_shared_two_way_ptrt &rhs) : p(rhs.p)
66+
{
67+
PRECONDITION(is_same(rhs));
68+
69+
if(p)
70+
{
71+
p->increment_use_count();
72+
}
73+
}
74+
75+
small_shared_two_way_ptrt(small_shared_two_way_ptrt &&rhs)
76+
{
77+
PRECONDITION(is_same(rhs));
78+
79+
swap(rhs);
80+
}
81+
82+
small_shared_two_way_ptrt &operator=(const small_shared_two_way_ptrt &rhs)
83+
{
84+
PRECONDITION(is_same(rhs));
85+
86+
small_shared_two_way_ptrt copy(rhs);
87+
swap(copy);
88+
return *this;
89+
}
90+
91+
small_shared_two_way_ptrt &operator=(small_shared_two_way_ptrt &&rhs)
92+
{
93+
PRECONDITION(is_same(rhs));
94+
95+
swap(rhs);
96+
return *this;
97+
}
98+
99+
~small_shared_two_way_ptrt()
100+
{
101+
if (!p)
102+
{
103+
return;
104+
}
105+
106+
auto use_count = p->use_count();
107+
108+
if(use_count == 1)
109+
{
110+
if (p->is_derived1())
111+
{
112+
U *u = static_cast<U *>(p);
113+
delete u;
114+
}
115+
else
116+
{
117+
V *v = static_cast<V *>(p);
118+
delete v;
119+
}
120+
}
121+
else
122+
{
123+
p->decrement_use_count();
124+
}
125+
}
126+
127+
void swap(small_shared_two_way_ptrt &rhs)
128+
{
129+
PRECONDITION(is_same(rhs));
130+
131+
std::swap(p, rhs.p);
132+
}
133+
134+
use_countt use_count() const
135+
{
136+
return p ? p->use_count() : 0;
137+
}
138+
139+
bool is_derived1() const
140+
{
141+
return p == nullptr || p->is_derived1();
142+
}
143+
144+
bool is_derived2() const
145+
{
146+
return p == nullptr || p->is_derived2();
147+
}
148+
149+
pointeet *get() const
150+
{
151+
return p;
152+
}
153+
154+
U *get_derived1() const
155+
{
156+
PRECONDITION(is_derived1());
157+
158+
return static_cast<U *>(p);
159+
}
160+
161+
V *get_derived2() const
162+
{
163+
PRECONDITION(is_derived2());
164+
165+
return static_cast<V *>(p);
166+
}
167+
168+
bool is_same(const small_shared_two_way_ptrt &other) const
169+
{
170+
const bool b1 = p == nullptr;
171+
const bool b2 = other.p == nullptr;
172+
173+
if (b1 || b2)
174+
return true;
175+
176+
return p->is_same(*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_derived1(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_derived2(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 = ~(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+
233+
// The use count shall be unaffected
234+
small_shared_two_way_pointeet &operator=(
235+
const small_shared_two_way_pointeet &rhs)
236+
{
237+
return *this;
238+
}
239+
240+
Num use_count() const
241+
{
242+
return use_count_ & mask;
243+
}
244+
245+
void increment_use_count()
246+
{
247+
PRECONDITION((use_count_ & mask) < mask);
248+
249+
use_count_++;
250+
}
251+
252+
void decrement_use_count()
253+
{
254+
PRECONDITION((use_count_ & mask) > 0);
255+
256+
use_count_--;
257+
}
258+
259+
void set_derived1()
260+
{
261+
use_count_ &= mask;
262+
}
263+
264+
void set_derived2()
265+
{
266+
use_count_ |= ~mask;
267+
}
268+
269+
bool is_derived1() const
270+
{
271+
return !(use_count_ & ~mask);
272+
}
273+
274+
bool is_derived2() const
275+
{
276+
return use_count_ & ~mask;
277+
}
278+
279+
bool is_same(const small_shared_two_way_pointeet &other) const
280+
{
281+
return !((use_count_ ^ other.use_count_) & ~mask);
282+
}
283+
284+
private:
285+
Num use_count_ = 0;
286+
};
287+
288+
#endif

unit/Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ SRC += unit_tests.cpp \
5454
catch_example.cpp \
5555
java_bytecode/java_virtual_functions/virtual_functions.cpp \
5656
java_bytecode/java_bytecode_parse_generics/parse_generic_superclasses.cpp \
57+
small_shared_two_way_ptr.cpp
5758
# Empty last line
5859

5960
INCLUDES= -I ../src/ -I.

unit/small_shared_two_way_ptr.cpp

+96
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
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+
int d1;
12+
};
13+
14+
class D2 : public small_shared_two_way_pointeet<unsigned>
15+
{
16+
public:
17+
D2() = default;
18+
19+
D2(int i) : d2(i) {}
20+
21+
int d2;
22+
};
23+
24+
TEST_CASE("Small shared two-way pointer")
25+
{
26+
typedef small_shared_two_way_ptrt<D1, D2> spt;
27+
28+
SECTION("Types")
29+
{
30+
spt sp1;
31+
spt sp2(new D1());
32+
spt sp3(new D2());
33+
34+
REQUIRE(sp1.is_same(sp1));
35+
REQUIRE(sp2.is_same(sp2));
36+
REQUIRE(sp3.is_same(sp3));
37+
38+
REQUIRE(sp1.is_same(sp2));
39+
REQUIRE(sp1.is_same(sp3));
40+
41+
REQUIRE(!sp2.is_same(sp3));
42+
43+
REQUIRE(sp1.is_derived1());
44+
REQUIRE(sp1.is_derived2());
45+
46+
REQUIRE(sp2.is_derived1());
47+
REQUIRE(!sp2.is_derived2());
48+
49+
REQUIRE(sp3.is_derived2());
50+
REQUIRE(!sp3.is_derived1());
51+
}
52+
53+
SECTION("Basic")
54+
{
55+
spt sp1;
56+
REQUIRE(sp1.use_count() == 0);
57+
58+
const D1 *p;
59+
60+
p = sp1.get_derived1();
61+
REQUIRE(p == nullptr);
62+
63+
spt sp2(new D1());
64+
REQUIRE(sp2.use_count() == 1);
65+
66+
p = sp2.get_derived1();
67+
REQUIRE(p != nullptr);
68+
69+
spt sp3(sp2);
70+
REQUIRE(sp3.is_derived1());
71+
REQUIRE(sp2.get_derived1() == sp3.get_derived1());
72+
REQUIRE(sp2.use_count() == 2);
73+
REQUIRE(sp3.use_count() == 2);
74+
75+
sp1 = sp2;
76+
REQUIRE(sp1.is_derived1());
77+
REQUIRE(sp1.get_derived1() == sp2.get_derived1());
78+
REQUIRE(sp1.use_count() == 3);
79+
REQUIRE(sp2.use_count() == 3);
80+
REQUIRE(sp3.use_count() == 3);
81+
}
82+
83+
SECTION("Creation")
84+
{
85+
spt sp1 = make_shared_derived1<D1, D2>();
86+
spt sp2 = make_shared_derived2<D1, D2>();
87+
88+
REQUIRE(!sp1.is_same(sp2));
89+
90+
sp1 = make_shared_derived1<D1, D2>(0);
91+
sp2 = make_shared_derived2<D1, D2>(0);
92+
93+
REQUIRE(!sp1.is_same(sp2));
94+
}
95+
}
96+

0 commit comments

Comments
 (0)