Skip to content

Commit 8bc745e

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

File tree

3 files changed

+387
-0
lines changed

3 files changed

+387
-0
lines changed

src/util/small_shared_two_way_ptr.h

Lines changed: 287 additions & 0 deletions
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+
/// 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+
// 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_derived1()
259+
{
260+
use_count_ &= mask;
261+
}
262+
263+
void set_derived2()
264+
{
265+
use_count_ |= ~mask;
266+
}
267+
268+
bool is_derived1() const
269+
{
270+
return !(use_count_ & ~mask);
271+
}
272+
273+
bool is_derived2() const
274+
{
275+
return use_count_ & ~mask;
276+
}
277+
278+
bool is_same(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

Lines changed: 1 addition & 0 deletions
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

Lines changed: 99 additions & 0 deletions
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(sp1));
39+
REQUIRE(sp2.is_same(sp2));
40+
REQUIRE(sp3.is_same(sp3));
41+
42+
REQUIRE(sp1.is_same(sp2));
43+
REQUIRE(sp1.is_same(sp3));
44+
45+
REQUIRE(!sp2.is_same(sp3));
46+
47+
REQUIRE(sp1.is_derived1());
48+
REQUIRE(sp1.is_derived2());
49+
50+
REQUIRE(sp2.is_derived1());
51+
REQUIRE(!sp2.is_derived2());
52+
53+
REQUIRE(sp3.is_derived2());
54+
REQUIRE(!sp3.is_derived1());
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_derived1();
65+
REQUIRE(p == nullptr);
66+
67+
spt sp2(new D1());
68+
REQUIRE(sp2.use_count() == 1);
69+
70+
p = sp2.get_derived1();
71+
REQUIRE(p != nullptr);
72+
73+
spt sp3(sp2);
74+
REQUIRE(sp3.is_derived1());
75+
REQUIRE(sp2.get_derived1() == sp3.get_derived1());
76+
REQUIRE(sp2.use_count() == 2);
77+
REQUIRE(sp3.use_count() == 2);
78+
79+
sp1 = sp2;
80+
REQUIRE(sp1.is_derived1());
81+
REQUIRE(sp1.get_derived1() == sp2.get_derived1());
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_derived1<D1, D2>();
90+
spt sp2 = make_shared_derived2<D1, D2>();
91+
92+
REQUIRE(!sp1.is_same(sp2));
93+
94+
sp1 = make_shared_derived1<D1, D2>(0);
95+
sp2 = make_shared_derived2<D1, D2>(0);
96+
97+
REQUIRE(!sp1.is_same(sp2));
98+
}
99+
}

0 commit comments

Comments
 (0)