xenium
marked_ptr.hpp
1//
2// Copyright (c) 2018-2020 Manuel Pöter.
3// Licensed under the MIT License. See LICENSE file in the project root for full license information.
4//
5
6#ifndef XENIUM_MARKED_PTR_HPP
7#define XENIUM_MARKED_PTR_HPP
8
9#include <xenium/utils.hpp>
10
11#include <cassert>
12#include <cstdint>
13#include <cstddef>
14
15#ifndef XENIUM_MAX_UPPER_MARK_BITS
16 #define XENIUM_MAX_UPPER_MARK_BITS 16
17#endif
18
19#ifdef _MSC_VER
20#pragma warning(push)
21// TODO - remove this after upgrading to C++17
22#pragma warning(disable: 4127) // conditional expression is constant
23#pragma warning(disable: 4293) // shift count negative or too big
24#endif
25
26namespace xenium {
40 template <class T, uintptr_t MarkBits, uintptr_t MaxUpperMarkBits = XENIUM_MAX_UPPER_MARK_BITS>
41 class marked_ptr {
42 static_assert(MarkBits > 0, "should never happen - compiler should pick the specilization for zero MarkBits!");
43 static constexpr uintptr_t pointer_bits = sizeof(T*) * 8 - MarkBits;
44 static constexpr uintptr_t MarkMask = (static_cast<uintptr_t>(1) << MarkBits) - 1;
45
46 static constexpr uintptr_t lower_mark_bits = MarkBits < MaxUpperMarkBits ? 0 : MarkBits - MaxUpperMarkBits;
47 static constexpr uintptr_t upper_mark_bits = MarkBits - lower_mark_bits;
48 static constexpr uintptr_t pointer_mask = ((static_cast<uintptr_t>(1) << pointer_bits) - 1) << lower_mark_bits;
49
50 public:
51 static constexpr uintptr_t number_of_mark_bits = MarkBits;
52 static_assert(MarkBits <= 32, "MarkBits must not be greater than 32.");
53 static_assert(sizeof(T*) == 8, "marked_ptr requires 64bit pointers.");
54
60 marked_ptr(T* p = nullptr, uintptr_t mark = 0) noexcept : ptr(make_ptr(p, mark)) {}
61
65 void reset() noexcept { ptr = nullptr; }
66
70 uintptr_t mark() const noexcept {
71 return utils::rotate<lower_mark_bits>::right(reinterpret_cast<uintptr_t>(ptr)) >> pointer_bits;
72 }
73
77 T* get() const noexcept {
78 auto ip = reinterpret_cast<uintptr_t>(ptr);
79 if constexpr(number_of_mark_bits != 0)
80 ip &= pointer_mask;
81 return reinterpret_cast<T*>(ip);
82 }
83
87 explicit operator bool() const noexcept { return ptr != nullptr; }
88
92 T* operator->() const noexcept { return get(); }
93
97 T& operator*() const noexcept { return *get(); }
98
99 inline friend bool operator==(const marked_ptr& l, const marked_ptr& r) { return l.ptr == r.ptr; }
100 inline friend bool operator!=(const marked_ptr& l, const marked_ptr& r) { return l.ptr != r.ptr; }
101
102 private:
103 T* make_ptr(T* p, uintptr_t mark) noexcept {
104 assert((reinterpret_cast<uintptr_t>(p) & ~pointer_mask) == 0 &&
105 "bits reserved for masking are occupied by the pointer");
106
107 uintptr_t ip = reinterpret_cast<uintptr_t>(p);
108 if constexpr (number_of_mark_bits == 0) {
109 assert(mark == 0);
110 return p;
111 }
112 else {
113 mark = utils::rotate<lower_mark_bits>::left(mark << pointer_bits);
114 return reinterpret_cast<T*>(ip | mark);
115 }
116 }
117
118 T* ptr;
119
120#ifdef _MSC_VER
121 // These members are only for the VS debugger visualizer (natvis).
122 enum Masking { MarkMask_ = MarkMask };
123 using PtrType = T*;
124#endif
125 };
126
127 template <class T, uintptr_t MaxUpperMarkBits>
128 class marked_ptr<T, 0, MaxUpperMarkBits> {
129 public:
130 static constexpr uintptr_t number_of_mark_bits = 0;
131
135 marked_ptr(T* p = nullptr) noexcept {
136 ptr = p;
137 }
138
142 void reset() noexcept { ptr = nullptr; }
143
147 uintptr_t mark() const noexcept { return 0; }
148
152 T* get() const noexcept { return ptr; }
153
157 explicit operator bool() const noexcept { return ptr != nullptr; }
158
162 T* operator->() const noexcept { return get(); }
163
167 T& operator*() const noexcept { return *get(); }
168
169 inline friend bool operator==(const marked_ptr& l, const marked_ptr& r) { return l.ptr == r.ptr; }
170 inline friend bool operator!=(const marked_ptr& l, const marked_ptr& r) { return l.ptr != r.ptr; }
171
172 private:
173 T* ptr;
174 };
175}
176
177#ifdef _MSC_VER
178#pragma warning(pop)
179#endif
180
181#endif
A pointer with an embedded mark/tag value.
Definition: marked_ptr.hpp:41
T * get() const noexcept
Get underlying pointer (with mark bits stripped off).
Definition: marked_ptr.hpp:77
T & operator*() const noexcept
Get reference to target of pointer.
Definition: marked_ptr.hpp:97
marked_ptr(T *p=nullptr, uintptr_t mark=0) noexcept
Construct a marked ptr with an optional mark value.
Definition: marked_ptr.hpp:60
uintptr_t mark() const noexcept
Get the mark value.
Definition: marked_ptr.hpp:70
T * operator->() const noexcept
Get pointer with mark bits stripped off.
Definition: marked_ptr.hpp:92
void reset() noexcept
Reset the pointer to nullptr and the mark to 0.
Definition: marked_ptr.hpp:65