Austin Schuh | 745610d | 2015-09-06 18:19:50 -0700 | [diff] [blame] | 1 | // -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- |
| 2 | // Copyright (c) 2005, Google Inc. |
| 3 | // All rights reserved. |
| 4 | // |
| 5 | // Redistribution and use in source and binary forms, with or without |
| 6 | // modification, are permitted provided that the following conditions are |
| 7 | // met: |
| 8 | // |
| 9 | // * Redistributions of source code must retain the above copyright |
| 10 | // notice, this list of conditions and the following disclaimer. |
| 11 | // * Redistributions in binary form must reproduce the above |
| 12 | // copyright notice, this list of conditions and the following disclaimer |
| 13 | // in the documentation and/or other materials provided with the |
| 14 | // distribution. |
| 15 | // * Neither the name of Google Inc. nor the names of its |
| 16 | // contributors may be used to endorse or promote products derived from |
| 17 | // this software without specific prior written permission. |
| 18 | // |
| 19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| 20 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| 21 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| 22 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| 23 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| 24 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| 25 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 26 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 27 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 28 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 29 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 30 | |
| 31 | // --- |
| 32 | // Author: Sanjay Ghemawat <opensource@google.com> |
| 33 | // |
| 34 | // A data structure used by the caching malloc. It maps from page# to |
| 35 | // a pointer that contains info about that page. We use two |
| 36 | // representations: one for 32-bit addresses, and another for 64 bit |
| 37 | // addresses. Both representations provide the same interface. The |
| 38 | // first representation is implemented as a flat array, the seconds as |
| 39 | // a three-level radix tree that strips away approximately 1/3rd of |
| 40 | // the bits every time. |
| 41 | // |
| 42 | // The BITS parameter should be the number of bits required to hold |
| 43 | // a page number. E.g., with 32 bit pointers and 4K pages (i.e., |
| 44 | // page offset fits in lower 12 bits), BITS == 20. |
| 45 | |
| 46 | #ifndef TCMALLOC_PAGEMAP_H_ |
| 47 | #define TCMALLOC_PAGEMAP_H_ |
| 48 | |
| 49 | #include "config.h" |
| 50 | |
| 51 | #include <stddef.h> // for NULL, size_t |
| 52 | #include <string.h> // for memset |
| 53 | #if defined HAVE_STDINT_H |
| 54 | #include <stdint.h> |
| 55 | #elif defined HAVE_INTTYPES_H |
| 56 | #include <inttypes.h> |
| 57 | #else |
| 58 | #include <sys/types.h> |
| 59 | #endif |
| 60 | #include "internal_logging.h" // for ASSERT |
| 61 | |
| 62 | // Single-level array |
| 63 | template <int BITS> |
| 64 | class TCMalloc_PageMap1 { |
| 65 | private: |
| 66 | static const int LENGTH = 1 << BITS; |
| 67 | |
| 68 | void** array_; |
| 69 | |
| 70 | public: |
| 71 | typedef uintptr_t Number; |
| 72 | |
| 73 | explicit TCMalloc_PageMap1(void* (*allocator)(size_t)) { |
| 74 | array_ = reinterpret_cast<void**>((*allocator)(sizeof(void*) << BITS)); |
| 75 | memset(array_, 0, sizeof(void*) << BITS); |
| 76 | } |
| 77 | |
| 78 | // Ensure that the map contains initialized entries "x .. x+n-1". |
| 79 | // Returns true if successful, false if we could not allocate memory. |
| 80 | bool Ensure(Number x, size_t n) { |
| 81 | // Nothing to do since flat array was allocated at start. All |
| 82 | // that's left is to check for overflow (that is, we don't want to |
| 83 | // ensure a number y where array_[y] would be an out-of-bounds |
| 84 | // access). |
| 85 | return n <= LENGTH - x; // an overflow-free way to do "x + n <= LENGTH" |
| 86 | } |
| 87 | |
| 88 | void PreallocateMoreMemory() {} |
| 89 | |
| 90 | // Return the current value for KEY. Returns NULL if not yet set, |
| 91 | // or if k is out of range. |
| 92 | void* get(Number k) const { |
| 93 | if ((k >> BITS) > 0) { |
| 94 | return NULL; |
| 95 | } |
| 96 | return array_[k]; |
| 97 | } |
| 98 | |
| 99 | // REQUIRES "k" is in range "[0,2^BITS-1]". |
| 100 | // REQUIRES "k" has been ensured before. |
| 101 | // |
| 102 | // Sets the value 'v' for key 'k'. |
| 103 | void set(Number k, void* v) { |
| 104 | array_[k] = v; |
| 105 | } |
| 106 | |
| 107 | // Return the first non-NULL pointer found in this map for |
| 108 | // a page number >= k. Returns NULL if no such number is found. |
| 109 | void* Next(Number k) const { |
| 110 | while (k < (1 << BITS)) { |
| 111 | if (array_[k] != NULL) return array_[k]; |
| 112 | k++; |
| 113 | } |
| 114 | return NULL; |
| 115 | } |
| 116 | }; |
| 117 | |
| 118 | // Two-level radix tree |
| 119 | template <int BITS> |
| 120 | class TCMalloc_PageMap2 { |
| 121 | private: |
| 122 | // Put 32 entries in the root and (2^BITS)/32 entries in each leaf. |
| 123 | static const int ROOT_BITS = 5; |
| 124 | static const int ROOT_LENGTH = 1 << ROOT_BITS; |
| 125 | |
| 126 | static const int LEAF_BITS = BITS - ROOT_BITS; |
| 127 | static const int LEAF_LENGTH = 1 << LEAF_BITS; |
| 128 | |
| 129 | // Leaf node |
| 130 | struct Leaf { |
| 131 | void* values[LEAF_LENGTH]; |
| 132 | }; |
| 133 | |
| 134 | Leaf* root_[ROOT_LENGTH]; // Pointers to 32 child nodes |
| 135 | void* (*allocator_)(size_t); // Memory allocator |
| 136 | |
| 137 | public: |
| 138 | typedef uintptr_t Number; |
| 139 | |
| 140 | explicit TCMalloc_PageMap2(void* (*allocator)(size_t)) { |
| 141 | allocator_ = allocator; |
| 142 | memset(root_, 0, sizeof(root_)); |
| 143 | } |
| 144 | |
| 145 | void* get(Number k) const { |
| 146 | const Number i1 = k >> LEAF_BITS; |
| 147 | const Number i2 = k & (LEAF_LENGTH-1); |
| 148 | if ((k >> BITS) > 0 || root_[i1] == NULL) { |
| 149 | return NULL; |
| 150 | } |
| 151 | return root_[i1]->values[i2]; |
| 152 | } |
| 153 | |
| 154 | void set(Number k, void* v) { |
| 155 | const Number i1 = k >> LEAF_BITS; |
| 156 | const Number i2 = k & (LEAF_LENGTH-1); |
| 157 | ASSERT(i1 < ROOT_LENGTH); |
| 158 | root_[i1]->values[i2] = v; |
| 159 | } |
| 160 | |
| 161 | bool Ensure(Number start, size_t n) { |
| 162 | for (Number key = start; key <= start + n - 1; ) { |
| 163 | const Number i1 = key >> LEAF_BITS; |
| 164 | |
| 165 | // Check for overflow |
| 166 | if (i1 >= ROOT_LENGTH) |
| 167 | return false; |
| 168 | |
| 169 | // Make 2nd level node if necessary |
| 170 | if (root_[i1] == NULL) { |
| 171 | Leaf* leaf = reinterpret_cast<Leaf*>((*allocator_)(sizeof(Leaf))); |
| 172 | if (leaf == NULL) return false; |
| 173 | memset(leaf, 0, sizeof(*leaf)); |
| 174 | root_[i1] = leaf; |
| 175 | } |
| 176 | |
| 177 | // Advance key past whatever is covered by this leaf node |
| 178 | key = ((key >> LEAF_BITS) + 1) << LEAF_BITS; |
| 179 | } |
| 180 | return true; |
| 181 | } |
| 182 | |
| 183 | void PreallocateMoreMemory() { |
| 184 | // Allocate enough to keep track of all possible pages |
| 185 | Ensure(0, 1 << BITS); |
| 186 | } |
| 187 | |
| 188 | void* Next(Number k) const { |
| 189 | while (k < (1 << BITS)) { |
| 190 | const Number i1 = k >> LEAF_BITS; |
| 191 | Leaf* leaf = root_[i1]; |
| 192 | if (leaf != NULL) { |
| 193 | // Scan forward in leaf |
| 194 | for (Number i2 = k & (LEAF_LENGTH - 1); i2 < LEAF_LENGTH; i2++) { |
| 195 | if (leaf->values[i2] != NULL) { |
| 196 | return leaf->values[i2]; |
| 197 | } |
| 198 | } |
| 199 | } |
| 200 | // Skip to next top-level entry |
| 201 | k = (i1 + 1) << LEAF_BITS; |
| 202 | } |
| 203 | return NULL; |
| 204 | } |
| 205 | }; |
| 206 | |
| 207 | // Three-level radix tree |
| 208 | template <int BITS> |
| 209 | class TCMalloc_PageMap3 { |
| 210 | private: |
| 211 | // How many bits should we consume at each interior level |
| 212 | static const int INTERIOR_BITS = (BITS + 2) / 3; // Round-up |
| 213 | static const int INTERIOR_LENGTH = 1 << INTERIOR_BITS; |
| 214 | |
| 215 | // How many bits should we consume at leaf level |
| 216 | static const int LEAF_BITS = BITS - 2*INTERIOR_BITS; |
| 217 | static const int LEAF_LENGTH = 1 << LEAF_BITS; |
| 218 | |
| 219 | // Interior node |
| 220 | struct Node { |
| 221 | Node* ptrs[INTERIOR_LENGTH]; |
| 222 | }; |
| 223 | |
| 224 | // Leaf node |
| 225 | struct Leaf { |
| 226 | void* values[LEAF_LENGTH]; |
| 227 | }; |
| 228 | |
| 229 | Node* root_; // Root of radix tree |
| 230 | void* (*allocator_)(size_t); // Memory allocator |
| 231 | |
| 232 | Node* NewNode() { |
| 233 | Node* result = reinterpret_cast<Node*>((*allocator_)(sizeof(Node))); |
| 234 | if (result != NULL) { |
| 235 | memset(result, 0, sizeof(*result)); |
| 236 | } |
| 237 | return result; |
| 238 | } |
| 239 | |
| 240 | public: |
| 241 | typedef uintptr_t Number; |
| 242 | |
| 243 | explicit TCMalloc_PageMap3(void* (*allocator)(size_t)) { |
| 244 | allocator_ = allocator; |
| 245 | root_ = NewNode(); |
| 246 | } |
| 247 | |
| 248 | void* get(Number k) const { |
| 249 | const Number i1 = k >> (LEAF_BITS + INTERIOR_BITS); |
| 250 | const Number i2 = (k >> LEAF_BITS) & (INTERIOR_LENGTH-1); |
| 251 | const Number i3 = k & (LEAF_LENGTH-1); |
| 252 | if ((k >> BITS) > 0 || |
| 253 | root_->ptrs[i1] == NULL || root_->ptrs[i1]->ptrs[i2] == NULL) { |
| 254 | return NULL; |
| 255 | } |
| 256 | return reinterpret_cast<Leaf*>(root_->ptrs[i1]->ptrs[i2])->values[i3]; |
| 257 | } |
| 258 | |
| 259 | void set(Number k, void* v) { |
| 260 | ASSERT(k >> BITS == 0); |
| 261 | const Number i1 = k >> (LEAF_BITS + INTERIOR_BITS); |
| 262 | const Number i2 = (k >> LEAF_BITS) & (INTERIOR_LENGTH-1); |
| 263 | const Number i3 = k & (LEAF_LENGTH-1); |
| 264 | reinterpret_cast<Leaf*>(root_->ptrs[i1]->ptrs[i2])->values[i3] = v; |
| 265 | } |
| 266 | |
| 267 | bool Ensure(Number start, size_t n) { |
| 268 | for (Number key = start; key <= start + n - 1; ) { |
| 269 | const Number i1 = key >> (LEAF_BITS + INTERIOR_BITS); |
| 270 | const Number i2 = (key >> LEAF_BITS) & (INTERIOR_LENGTH-1); |
| 271 | |
| 272 | // Check for overflow |
| 273 | if (i1 >= INTERIOR_LENGTH || i2 >= INTERIOR_LENGTH) |
| 274 | return false; |
| 275 | |
| 276 | // Make 2nd level node if necessary |
| 277 | if (root_->ptrs[i1] == NULL) { |
| 278 | Node* n = NewNode(); |
| 279 | if (n == NULL) return false; |
| 280 | root_->ptrs[i1] = n; |
| 281 | } |
| 282 | |
| 283 | // Make leaf node if necessary |
| 284 | if (root_->ptrs[i1]->ptrs[i2] == NULL) { |
| 285 | Leaf* leaf = reinterpret_cast<Leaf*>((*allocator_)(sizeof(Leaf))); |
| 286 | if (leaf == NULL) return false; |
| 287 | memset(leaf, 0, sizeof(*leaf)); |
| 288 | root_->ptrs[i1]->ptrs[i2] = reinterpret_cast<Node*>(leaf); |
| 289 | } |
| 290 | |
| 291 | // Advance key past whatever is covered by this leaf node |
| 292 | key = ((key >> LEAF_BITS) + 1) << LEAF_BITS; |
| 293 | } |
| 294 | return true; |
| 295 | } |
| 296 | |
| 297 | void PreallocateMoreMemory() { |
| 298 | } |
| 299 | |
| 300 | void* Next(Number k) const { |
| 301 | while (k < (Number(1) << BITS)) { |
| 302 | const Number i1 = k >> (LEAF_BITS + INTERIOR_BITS); |
| 303 | const Number i2 = (k >> LEAF_BITS) & (INTERIOR_LENGTH-1); |
| 304 | if (root_->ptrs[i1] == NULL) { |
| 305 | // Advance to next top-level entry |
| 306 | k = (i1 + 1) << (LEAF_BITS + INTERIOR_BITS); |
| 307 | } else { |
| 308 | Leaf* leaf = reinterpret_cast<Leaf*>(root_->ptrs[i1]->ptrs[i2]); |
| 309 | if (leaf != NULL) { |
| 310 | for (Number i3 = (k & (LEAF_LENGTH-1)); i3 < LEAF_LENGTH; i3++) { |
| 311 | if (leaf->values[i3] != NULL) { |
| 312 | return leaf->values[i3]; |
| 313 | } |
| 314 | } |
| 315 | } |
| 316 | // Advance to next interior entry |
| 317 | k = ((k >> LEAF_BITS) + 1) << LEAF_BITS; |
| 318 | } |
| 319 | } |
| 320 | return NULL; |
| 321 | } |
| 322 | }; |
| 323 | |
| 324 | #endif // TCMALLOC_PAGEMAP_H_ |