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 
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)) {}
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
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:92
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:97