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 <cstddef>
13 #include <cstdint>
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 
26 namespace 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)) {} // NOLINT
61 
65  void reset() noexcept { _ptr = nullptr; }
66 
70  [[nodiscard]] uintptr_t mark() const noexcept {
71  return utils::rotate<lower_mark_bits>::right(reinterpret_cast<uintptr_t>(_ptr)) >> pointer_bits;
72  }
73 
77  [[nodiscard]] 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  }
82  return reinterpret_cast<T*>(ip);
83  }
84 
88  explicit operator bool() const noexcept { return _ptr != nullptr; }
89 
93  T* operator->() const noexcept { return get(); }
94 
98  T& operator*() const noexcept { return *get(); }
99 
100  inline friend bool operator==(const marked_ptr& l, const marked_ptr& r) { return l._ptr == r._ptr; }
101  inline friend bool operator!=(const marked_ptr& l, const marked_ptr& r) { return l._ptr != r._ptr; }
102 
103 private:
104  T* make_ptr(T* p, uintptr_t mark) noexcept {
105  assert((reinterpret_cast<uintptr_t>(p) & ~pointer_mask) == 0 &&
106  "bits reserved for masking are occupied by the pointer");
107 
108  auto ip = reinterpret_cast<uintptr_t>(p);
109  if constexpr (number_of_mark_bits == 0) {
110  assert(mark == 0);
111  return p;
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 { _ptr = p; } // NOLINT
136 
140  void reset() noexcept { _ptr = nullptr; }
141 
145  [[nodiscard]] uintptr_t mark() const noexcept { return 0; }
146 
150  [[nodiscard]] T* get() const noexcept { return _ptr; }
151 
155  explicit operator bool() const noexcept { return _ptr != nullptr; }
156 
160  T* operator->() const noexcept { return get(); }
161 
165  T& operator*() const noexcept { return *get(); }
166 
167  inline friend bool operator==(const marked_ptr& l, const marked_ptr& r) { return l._ptr == r._ptr; }
168  inline friend bool operator!=(const marked_ptr& l, const marked_ptr& r) { return l._ptr != r._ptr; }
169 
170 private:
171  T* _ptr;
172 };
173 } // namespace xenium
174 
175 #ifdef _MSC_VER
176  #pragma warning(pop)
177 #endif
178 
179 #endif
xenium::marked_ptr::marked_ptr
marked_ptr(T *p=nullptr, uintptr_t mark=0) noexcept
Construct a marked_ptr with an optional mark value.
Definition: marked_ptr.hpp:60
xenium::marked_ptr
A pointer with an embedded mark/tag value.
Definition: marked_ptr.hpp:41
xenium::marked_ptr::reset
void reset() noexcept
Reset the pointer to nullptr and the mark to 0.
Definition: marked_ptr.hpp:65
xenium::marked_ptr::operator->
T * operator->() const noexcept
Get pointer with mark bits stripped off.
Definition: marked_ptr.hpp:93
xenium::marked_ptr::mark
uintptr_t mark() const noexcept
Get the mark value.
Definition: marked_ptr.hpp:70
xenium::marked_ptr::get
T * get() const noexcept
Get underlying pointer (with mark bits stripped off).
Definition: marked_ptr.hpp:77
xenium::marked_ptr::operator*
T & operator*() const noexcept
Get reference to target of pointer.
Definition: marked_ptr.hpp:98