2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
19 #include "hphp/util/compilation-flags.h"
20 #include "hphp/util/assertions.h"
25 * RadixMap stores blocks of memory represented by [ptr,size), where both
26 * ptr and size have at least LgAlign zeroed low-bits. It supports
27 * insert, erase, get, iterate, and find. find(addr) retreives
28 * the [ptr,size) block that contains addr.
30 * Internally, the tree consists of nodes with Radix = 2^LgRadix slots each;
31 * larger values of LgRadix result in a wider, shallower tree. Each tree level
32 * represents LgRadix number of bits of the pointer stored at that level,
33 * with each slot representing a unique group of bits of the pointer.
34 * Together with LgAlign, the bits of a pointer are treated as follows:
36 * [prefix][LgRadix]...[LgRadix][LgAlign]
39 * The low LgAlign bits are ignored, and each interior LgRadix bits are
40 * used as the index into the node at that level. Low order bits index
41 * into leaf nodes in the tree. The highest order index accesses the root
42 * node of the tree. All pointers in the tree share the same common prefix,
43 * which is not used other than to recreate pointers and detect when the tree
44 * needs to be made deeper.
46 template<class T
, size_t LgAlign
= 3, size_t LgRadix
= 4>
48 static_assert(LgAlign
>= 1, "need one ptr/size bit for internal use");
49 static_assert(std::is_pointer
<T
>::value
, "T must be a pointer type");
50 static constexpr size_t Radix
= 1<<LgRadix
;
51 static constexpr size_t Align
= 1<<LgAlign
;
53 // initial root_prefix has bits set that cannot occur in any ptr
54 static constexpr size_t EmptyPrefix
= ~0ul;
56 // Implementation notes.
58 // All accessors work by descending the tree, doing their work.
59 // by convention, when "scale" is tracked, it is the tree level (starting
60 // at 0 for nodes that can't have children) times LgRadix. Each slot
61 // represents (1<<scale)+LgAlign bytes.
63 // Each slot can be in one of three states:
65 // leaf - size, representing a [ptr,size) rmap entry
66 // child - pointer to child node
68 // root_prefix contains the high order bits common to every ptr.
72 bool is_leaf(size_t i
) const {
73 return slots
[i
].size
& 1;
75 bool is_child(size_t i
) const {
76 return !is_leaf(i
) && slots
[i
].child
!= nullptr;
78 // Initialize slot i as a leaf. Caller must first call destroy_node()
79 // if the slot previously contained a child pointer.
80 void set_size(size_t i
, size_t size
) {
81 assert(size
!= 0 && size
% Align
== 0);
82 slots
[i
].size
= size
+ 1; // +1 to set the leaf flag
84 void set_child(size_t i
, Node
* child
) {
86 slots
[i
].child
= child
;
88 void erase(size_t i
) {
92 // Return the child at slot i, or null if the slot doesn't hold a child
93 // pointer. It's a bug to call this function on a leaf (ie a nonempty slot).
94 Node
* child(size_t i
) const {
96 return slots
[i
].child
;
98 size_t size(size_t i
) const {
100 return slots
[i
].size
- 1; // -1 to remove the leaf flag.
105 size_t size
; // if leaf
109 // recursively destroy node's children (if any), then free node.
110 NEVER_INLINE
void destroy_node(Node
* node
, int scale
) {
111 assert(scale
% LgRadix
== 0);
112 if (scale
> LgRadix
) {
113 for (size_t i
= 0; i
< Radix
; ++i
) {
114 if (node
->is_child(i
)) {
115 destroy_node(node
->child(i
), scale
- LgRadix
);
118 } else if (scale
== LgRadix
) {
119 for (size_t i
= 0; i
< Radix
; ++i
) {
120 if (node
->is_child(i
)) {
121 free(node
->child(i
));
126 assert(node
!= m_root
|| m_node_count
== 1);
132 if (m_root
) destroy_node(m_root
, m_root_scale
);
137 destroy_node(m_root
, m_root_scale
);
139 m_root_prefix
= EmptyPrefix
;
149 // Iteratively count the number of blocks currently stored.
150 size_t countBlocks() const {
152 iterate([&](T
, size_t) { ++n
; });
157 * Insert [ptr,size). Assumes ptr not already present, and [ptr,size)
158 * does not overlap any existing range.
160 void insert(T ptr
, const size_t size
) {
161 assert(size
>= Align
);
162 // valid pointers cannot have EmptyPrefix as their upper bits.
163 assert(upper(toKey(ptr
), m_root_scale
) != EmptyPrefix
);
164 assert(uintptr_t(ptr
) % Align
== 0 && size
% Align
== 0);
165 // this assert is too expensive to leave enabled by default.
166 //assert(!m_root || !find(ptr).ptr);
167 auto const k
= toKey(ptr
);
168 // compute the max scale at which [ptr,size) would fit perfectly;
169 // ie scale <= the lsb of both k and size/Align.
170 auto max_scale
= __builtin_ffsl(k
| (size
>> LgAlign
)) - 1;
172 // if necessary, make the tree deeper to expand addressable range
173 while (upper(k
, m_root_scale
) != m_root_prefix
||
174 m_root_scale
+ LgRadix
<= max_scale
) {
176 n
= m_root
= make_node();
177 // make tree deeper until block can be stored at its natural level
178 while (m_root_scale
+ LgRadix
<= max_scale
) {
179 m_root_scale
+= LgRadix
;
181 m_root_prefix
= upper(k
, m_root_scale
);
184 // deepen tree while new pointer is out of root's range, or if
185 // it should be stored at a higher level anyway.
187 auto scale
= m_root_scale
+ LgRadix
;
188 n
->set_child(index(m_root_prefix
, scale
), m_root
);
189 m_root_prefix
= upper(m_root_prefix
, scale
);
190 m_root_scale
= scale
;
193 // walk down tree, insert new range as high as possible
194 for (auto scale
= m_root_scale
;; scale
-= LgRadix
) {
195 auto i
= index(k
, scale
);
196 auto child
= n
->child(i
);
197 if (scale
<= max_scale
) {
199 destroy_node(child
, scale
- LgRadix
);
201 return n
->set_size(i
, size
);
205 n
->set_child(i
, child
);
213 * Return the size associated with ptr, 0 if no range is found.
214 * Ptr must be exactly equal the start of a range stored in the map.
216 size_t get(const void* ptr
) const {
217 // if !m_root, then m_root_prefix cannot match any pointer
218 assert(m_root
|| (m_root_prefix
== EmptyPrefix
&& m_root_scale
== 0));
219 auto const k
= toKey(ptr
);
220 auto scale
= m_root_scale
;
221 if (upper(k
, scale
) == m_root_prefix
) {
222 for (auto n
= m_root
; n
!= nullptr;) {
223 auto i
= index(k
, scale
);
225 return lower(ptr
, scale
) == 0 ? n
->size(i
) : 0;
235 * If ptr exactly equals the start of a range stored in the map, erase it
236 * from the map and return its size. Otherwise do nothing and return 0.
238 size_t erase(const void* ptr
) {
239 // if !m_root, then m_root_prefix cannot match any pointer
240 assert(m_root
|| (m_root_prefix
== EmptyPrefix
&& m_root_scale
== 0));
241 auto const k
= toKey(ptr
);
242 auto scale
= m_root_scale
;
243 // if the tree is empty, this check will fail
244 if (upper(k
, scale
) == m_root_prefix
) {
245 for (auto n
= m_root
; n
!= nullptr;) {
246 auto i
= index(k
, scale
);
248 if (lower(ptr
, scale
) == 0) {
249 auto size
= n
->size(i
);
263 * Iterate through the map in address order.
265 template<class Fn
> void iterate(Fn fn
) const {
267 for (size_t k
= m_root_prefix
, e
= k
+ slot_size(m_root_scale
+ LgRadix
);
270 auto scale
= m_root_scale
;
271 for (size_t i
= index(k
, scale
); i
< Radix
;) {
273 auto size
= n
->size(i
);
274 fn((T
)(k
<< LgAlign
), size
);
275 k
+= size
>> LgAlign
;
276 i
+= size
>> (scale
+ LgAlign
);
277 } else if (auto child
= n
->child(i
)) {
278 // down to next level without changing k
280 i
= index(k
, scale
-= LgRadix
);
282 // move to start of next slot at this level
283 k
= (k
+ slot_size(scale
)) & ~(slot_size(scale
) - 1);
291 * Return the entry enclosing ptr, or nullptr if no such entry exists.
293 struct FindResult
{ T ptr
; size_t size
; };
294 FindResult
find(const void* ptr
) const {
295 // if !m_root, then m_root_prefix cannot match any pointer
296 assert(m_root
|| (m_root_prefix
== EmptyPrefix
&& m_root_scale
== 0));
297 auto end
= m_root_prefix
+ slot_size(m_root_scale
+ LgRadix
) - 1;
298 auto needle
= toKey(ptr
);
299 for (auto k
= std::min(needle
, end
);
300 upper(k
, m_root_scale
) == m_root_prefix
;) {
302 auto scale
= m_root_scale
;
303 for (ssize_t i
= index(k
, scale
); i
>= 0;) {
305 k
&= ~(slot_size(scale
) - 1);
306 auto size
= n
->size(i
);
307 return needle
>= k
+ (size
>> LgAlign
) ? FindResult
{nullptr, 0} :
308 FindResult
{(T
)(k
<< LgAlign
), size
};
310 if (auto child
= n
->child(i
)) {
311 // down to next level without changing k
313 i
= index(k
, scale
-= LgRadix
);
315 // move to end of previous slot at this level
316 k
= (k
& ~(slot_size(scale
) - 1)) - 1;
318 assert(i
==-1 || i
== index(k
,scale
));
326 static size_t toKey(const void* ptr
) {
327 return size_t(ptr
) >> LgAlign
;
330 // extract middle bits; 0 = lowest LgRadix bits
331 static int index(size_t k
, size_t scale
) {
332 return (k
>> scale
) & (Radix
- 1);
335 // extract upper scale+LgRadix bits
336 static size_t upper(size_t k
, size_t scale
) {
337 return k
& ~(slot_size(scale
+ LgRadix
) - 1);
340 // extract the lower scale+LgAlign bits
341 static size_t lower(const void* ptr
, size_t scale
) {
342 return uintptr_t(ptr
) & ((1UL << (scale
+ LgAlign
)) - 1);
345 // return the number of leaf slots represented by each slot at this level.
346 // each leaf slot represents Align bytes.
347 static size_t slot_size(size_t scale
) {
353 return new (calloc(1, sizeof(Node
))) Node();
357 Node
* m_root
{nullptr};
358 size_t m_root_prefix
{EmptyPrefix
};