1 /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3  *     Copyright 2017 Couchbase, Inc
4  *
5  *   Licensed under the Apache License, Version 2.0 (the "License");
6  *   you may not use this file except in compliance with the License.
7  *   You may obtain a copy of the License at
8  *
9  *       http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *   Unless required by applicable law or agreed to in writing, software
12  *   distributed under the License is distributed on an "AS IS" BASIS,
13  *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *   See the License for the specific language governing permissions and
15  *   limitations under the License.
16  */
17 
18 #pragma once
19 
20 #include <memory>
21 
22 #if (defined(__x86_64__) || defined(_M_X64) || defined(__s390x__))
23 
24 /*
25  * This class provides a tagged pointer, which means it is a pointer however
26  * the top 16 bits (the tag) can be used to hold user data.  This works
27  * because on x86-64 it only addresses using the bottom 48-bits (the top 16-bits
28  * are not used).
29  *
30  * A unsigned 16 bit value can be stored in the pointer using the setTag method.
31  * The value can be retrieved using the setTag method.
32  *
33  * To avoid an address error being raised it is important to mask out the top
34  * 16-bits when using the pointer.  This is achieved by using the getOBj method.
35  * setObj is used to set the pointer value, without affecting the tag.
36  */
37 
38 template <typename T>
39 class TaggedPtr {
40 public:
41     using element_type = T;
42 
43     // Need to define all methods which unique_ptr expects from a pointer type
TaggedPtr()44     TaggedPtr() : raw(0) {
45     }
46 
TaggedPtr(T* obj)47     TaggedPtr(T* obj) : TaggedPtr() {
48         set(obj);
49     }
50 
TaggedPtr(T* obj, uint16_t tag)51     TaggedPtr(T* obj, uint16_t tag) : TaggedPtr() {
52         set(obj);
53         setTag(tag);
54     }
55 
operator !=(const T* other) const56     bool operator!=(const T* other) const {
57         return get() != other;
58     }
59 
operator ==(const T* other) const60     bool operator==(const T* other) const {
61         return !operator!=(other);
62     }
63 
operator bool() const64     operator bool() const {
65         return extractPointer(raw) != 0;
66     }
67 
68     // Implement pointer operator to allow existing code to transparently
69     // access the underlying object (ignoring the tag).
70     T* operator->() const noexcept {
71         return get();
72     }
73 
74     // Required by MS Compiler
75     T& operator*() const noexcept {
76         return *get();
77     }
78 
setTag(uint16_t tag)79     void setTag(uint16_t tag) {
80         raw = extractPointer(raw);
81         raw |= (uintptr_t(tag) << 48ull);
82     }
83 
getTag() const84     uint16_t getTag() const {
85         return (extractTag(raw) >> 48ull);
86     }
87 
set(const T* obj)88     void set(const T* obj) {
89         raw = extractTag(raw);
90         raw |= extractPointer(reinterpret_cast<uintptr_t>(obj));
91     }
92 
get() const93     T* get() const {
94         return reinterpret_cast<T*>(extractPointer(raw));
95     }
96 
97     // Required by MS Compiler
pointer_to(element_type& r)98     static TaggedPtr<T> pointer_to(element_type& r) {
99         return TaggedPtr<T>(std::addressof(r));
100     }
101 
102     /**
103      * Sets the tag of a TaggedPtr that is wrapped inside a pointer
104      * e.g. SingleThreadedRCPtr
105      *
106      * @param ptr  pointer that wraps a TaggedPtr
107      * @param value  u16int value that is to be stored in the tag
108      */
109     template <typename Pointer>
updateTag(Pointer& ptr, uint16_t value)110     static void updateTag(Pointer& ptr, uint16_t value) {
111         auto taggedPtr = ptr.get();
112         taggedPtr.setTag(value);
113         ptr.reset(taggedPtr);
114     }
115 
116 private:
extractPointer(uintptr_t ptr) const117     uintptr_t extractPointer(uintptr_t ptr) const {
118         return (ptr & 0x0000ffffffffffffull);
119     }
120 
extractTag(uintptr_t ptr) const121     uintptr_t extractTag(uintptr_t ptr) const {
122         return (ptr & 0xffff000000000000ull);
123     }
124 
125     // Tag held in top 16 bits.
126     uintptr_t raw;
127 };
128 
129 #else
130 
131 #define CB_MEMORY_INEFFICIENT_TAGGED_PTR 1
132 
133 /*
134  * A memory inefficient version which use the full pointer _AND_ a 16 bit
135  * flag section for platforms which use the full address space of a 64 pointer
136  */
137 template <typename T>
138 class TaggedPtr {
139 public:
140     using element_type = T;
141 
TaggedPtr()142     TaggedPtr() : TaggedPtr(nullptr) {
143     }
144 
145     // Need to define all methods which unique_ptr expects from a pointer type
TaggedPtr(T* obj)146     TaggedPtr(T* obj) : TaggedPtr(obj, 0) {
147     }
148 
TaggedPtr(T* obj, uint16_t tag)149     TaggedPtr(T* obj, uint16_t tag) : thePointer(obj), theTag(tag) {
150     }
151 
operator !=(const T* other) const152     bool operator!=(const T* other) const {
153         return get() != other;
154     }
155 
operator ==(const T* other) const156     bool operator==(const T* other) const {
157         return !operator!=(other);
158     }
159 
operator bool() const160     operator bool() const {
161         return thePointer != nullptr;
162     }
163 
164     // Implement pointer operator to allow existing code to transparently
165     // access the underlying object (ignoring the tag).
166     T* operator->() const noexcept {
167         return get();
168     }
169 
170     // Required by MS Compiler
171     T& operator*() const noexcept {
172         return *get();
173     }
174 
setTag(uint16_t tag)175     void setTag(uint16_t tag) {
176         theTag = tag;
177     }
178 
getTag() const179     uint16_t getTag() const {
180         return theTag;
181     }
182 
set(const T* obj)183     void set(const T* obj) {
184         thePointer = const_cast<T*>(obj);
185     }
186 
get() const187     T* get() const {
188         return const_cast<T*>(thePointer);
189     }
190 
191     // Required by MS Compiler
pointer_to(element_type& r)192     static TaggedPtr<T> pointer_to(element_type& r) {
193         return TaggedPtr<T>(std::addressof(r));
194     }
195 
196     /**
197      * Sets the tag of a TaggedPtr that is wrapped inside a pointer
198      * e.g. SingleThreadedRCPtr
199      *
200      * @param ptr  pointer that wraps a TaggedPtr
201      * @param value  u16int value that is to be stored in the tag
202      */
203     template <typename Pointer>
updateTag(Pointer& ptr, uint16_t value)204     static void updateTag(Pointer& ptr, uint16_t value) {
205         auto taggedPtr = ptr.get();
206         taggedPtr.setTag(value);
207         ptr.reset(taggedPtr);
208     }
209 
210 private:
211     T* thePointer = nullptr;
212     uint16_t theTag = 0;
213 };
214 
215 #endif
216 
217 /*
218  * The class provides a Deleter for std::unique_ptr.  It defines a type pointer
219  * which is the TaggedPtr<T> and means that get() will return the TaggedPtr<T>.
220  * It also templates on the Deleter, so a customer deleter can be passed in.
221  */
222 template <typename T, typename S>
223 class TaggedPtrDeleter {
224 public:
225     // Need to specify a custom pointer type
226     using pointer = TaggedPtr<T>;
227 
operator ()(TaggedPtr<T> item)228     void operator()(TaggedPtr<T> item) {
229         S()(item.get());
230     }
231 };
232