OLD | NEW |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 // This file contains the implementation of IdAllocator. | 5 // This file contains the implementation of IdAllocator. |
6 | 6 |
7 #include "gpu/command_buffer/common/id_allocator.h" | 7 #include "gpu/command_buffer/common/id_allocator.h" |
8 | 8 |
| 9 #include <limits> |
| 10 |
9 #include "base/logging.h" | 11 #include "base/logging.h" |
10 | 12 |
11 namespace gpu { | 13 namespace gpu { |
12 | 14 |
13 IdAllocator::IdAllocator() {} | 15 // IdAllocator stores the list of allocated ids in an AVL tree. The |
14 | 16 // tree node |IdAllocator::Node| represents maximal closed interval |
15 IdAllocator::~IdAllocator() {} | 17 // (min, max) of allocated ids. The tree is ordered by the range |
| 18 // minimum value. |
| 19 |
| 20 struct IdAllocator::Node { |
| 21 ResourceId min; |
| 22 ResourceId max; |
| 23 Node* left; |
| 24 Node* right; |
| 25 int height; |
| 26 |
| 27 Node(ResourceId min, ResourceId max) |
| 28 : min(min), max(max), left(NULL), right(NULL), height(0) {} |
| 29 }; |
| 30 |
| 31 IdAllocator::IdAllocator() : root_(NULL) { |
| 32 } |
| 33 |
| 34 IdAllocator::~IdAllocator() { |
| 35 if (root_ != NULL) { |
| 36 Destroy(root_); |
| 37 } |
| 38 } |
16 | 39 |
17 ResourceId IdAllocator::AllocateID() { | 40 ResourceId IdAllocator::AllocateID() { |
18 ResourceId id; | 41 return AllocateIDRange(1); |
19 ResourceIdSet::iterator iter = free_ids_.begin(); | 42 } |
20 if (iter != free_ids_.end()) { | 43 |
21 id = *iter; | 44 ResourceId IdAllocator::AllocateIDAtOrAbove(ResourceId desired_id) { |
| 45 if (desired_id == 0) { |
| 46 desired_id = 1; |
| 47 } |
| 48 |
| 49 if (root_ == NULL) { |
| 50 root_ = Insert(desired_id, desired_id, root_); |
| 51 return desired_id; |
| 52 } |
| 53 |
| 54 ResourceId result = 0; |
| 55 root_ = AddRange(1, |
| 56 &result, |
| 57 desired_id, |
| 58 std::numeric_limits<ResourceId>::max(), |
| 59 root_, |
| 60 NULL, |
| 61 NULL); |
| 62 |
| 63 if (result == 0) { |
| 64 root_ = AddRange(1, |
| 65 &result, |
| 66 1, |
| 67 std::numeric_limits<ResourceId>::max(), |
| 68 root_, |
| 69 NULL, |
| 70 NULL); |
| 71 } |
| 72 |
| 73 return result; |
| 74 } |
| 75 |
| 76 ResourceId IdAllocator::AllocateIDRange(uint32_t range) { |
| 77 if (root_ == NULL) { |
| 78 root_ = Insert(1, range, NULL); |
| 79 return 1; |
| 80 } |
| 81 |
| 82 ResourceId result = 0; |
| 83 root_ = AddRange(range, |
| 84 &result, |
| 85 1, |
| 86 std::numeric_limits<ResourceId>::max(), |
| 87 root_, |
| 88 NULL, |
| 89 NULL); |
| 90 return result; |
| 91 } |
| 92 |
| 93 bool IdAllocator::MarkAsUsed(ResourceId id) { |
| 94 if (root_ == NULL) { |
| 95 root_ = Insert(id, id, NULL); |
| 96 return true; |
| 97 } |
| 98 |
| 99 ResourceId result = 0; |
| 100 root_ = AddRange(1, &result, id, id, root_, NULL, NULL); |
| 101 return result != 0; |
| 102 } |
| 103 |
| 104 void IdAllocator::FreeID(ResourceId id) { |
| 105 FreeIDRange(id, 1); |
| 106 } |
| 107 |
| 108 void IdAllocator::FreeIDRange(ResourceId first_id, uint32_t range) { |
| 109 if (root_ == NULL) { |
| 110 return; |
| 111 } |
| 112 |
| 113 root_ = RemoveRange(first_id, first_id + range - 1u, root_); |
| 114 DCHECK(root_ == NULL || root_->min > 0); |
| 115 } |
| 116 |
| 117 bool IdAllocator::InUse(ResourceId id) const { |
| 118 if (id == 0) { |
| 119 return false; |
| 120 } |
| 121 |
| 122 Node* pos = root_; |
| 123 while (pos != NULL) { |
| 124 if (id >= pos->min && id <= pos->max) { |
| 125 return true; |
| 126 } |
| 127 |
| 128 if (id < pos->min) { |
| 129 pos = pos->left; |
| 130 } else { |
| 131 pos = pos->right; |
| 132 } |
| 133 } |
| 134 |
| 135 return false; |
| 136 } |
| 137 |
| 138 inline int IdAllocator::GetHeight(Node* node) { |
| 139 return node ? node->height : 0; |
| 140 } |
| 141 |
| 142 inline void IdAllocator::UpdateHeight(Node* node) { |
| 143 node->height = std::max(GetHeight(node->left), GetHeight(node->right)) + 1; |
| 144 } |
| 145 |
| 146 inline int IdAllocator::GetBalance(Node* node) { |
| 147 return node ? GetHeight(node->left) - GetHeight(node->right) : 0; |
| 148 } |
| 149 |
| 150 inline IdAllocator::Node* IdAllocator::RotateLeft(Node* node) { |
| 151 Node* new_root = node->right; |
| 152 node->right = new_root->left; |
| 153 new_root->left = node; |
| 154 UpdateHeight(node); |
| 155 UpdateHeight(new_root); |
| 156 return new_root; |
| 157 } |
| 158 |
| 159 inline IdAllocator::Node* IdAllocator::RotateRight(Node* node) { |
| 160 Node* new_root = node->left; |
| 161 node->left = new_root->right; |
| 162 new_root->right = node; |
| 163 UpdateHeight(node); |
| 164 UpdateHeight(new_root); |
| 165 return new_root; |
| 166 } |
| 167 |
| 168 IdAllocator::Node* IdAllocator::Balance(Node* node) { |
| 169 UpdateHeight(node); |
| 170 int balance = GetBalance(node); |
| 171 if (balance > 1) { |
| 172 if (GetBalance(node->left) >= 0) { |
| 173 return RotateRight(node); |
| 174 } |
| 175 node->left = RotateLeft(node->left); |
| 176 return RotateRight(node); |
| 177 } |
| 178 |
| 179 if (balance < -1) { |
| 180 if (GetBalance(node->right) <= 0) { |
| 181 return RotateLeft(node); |
| 182 } |
| 183 node->right = RotateRight(node->right); |
| 184 return RotateLeft(node); |
| 185 } |
| 186 |
| 187 return node; |
| 188 } |
| 189 |
| 190 // Finds the largest node of |max_subtree| and stores its |
| 191 // contents to |to_replace|. Deletes the found node. |
| 192 IdAllocator::Node* IdAllocator::ReplaceMax(Node* to_replace, |
| 193 Node* max_subtree) { |
| 194 if (max_subtree->right != NULL) { |
| 195 max_subtree->right = ReplaceMax(to_replace, max_subtree->right); |
| 196 return Balance(max_subtree); |
| 197 } |
| 198 |
| 199 to_replace->min = max_subtree->min; |
| 200 to_replace->max = max_subtree->max; |
| 201 Node* result = max_subtree->left; |
| 202 delete max_subtree; |
| 203 return result; |
| 204 } |
| 205 |
| 206 IdAllocator::Node* IdAllocator::Delete(Node* node) { |
| 207 if (node->left != NULL && node->right != NULL) { |
| 208 node->left = ReplaceMax(node, node->left); |
| 209 return Balance(node); |
| 210 } |
| 211 |
| 212 Node* result = node->left != NULL ? node->left : node->right; |
| 213 delete node; |
| 214 return result; |
| 215 } |
| 216 |
| 217 IdAllocator::Node* IdAllocator::Insert(ResourceId min, |
| 218 ResourceId max, |
| 219 Node* pos) { |
| 220 if (pos == NULL) { |
| 221 return new Node(min, max); |
| 222 } |
| 223 |
| 224 if (min < pos->min) { |
| 225 pos->left = Insert(min, max, pos->left); |
22 } else { | 226 } else { |
23 id = LastUsedId() + 1; | 227 pos->right = Insert(min, max, pos->right); |
24 if (!id) { | 228 } |
25 // We wrapped around to 0. | 229 |
26 id = FindFirstUnusedId(); | 230 return Balance(pos); |
27 } | 231 } |
28 } | 232 |
29 MarkAsUsed(id); | 233 // Allocates |range| amount of ids and stores the first id allocated |
30 return id; | 234 // in |first_id_out|. In case of failed allocation, |first_id_out| |
31 } | 235 // remains 0. The allocated id range will start at the smallest possible |
32 | 236 // value between closed range [|first_id_min|, |first_id_max|]. |
33 ResourceId IdAllocator::AllocateIDAtOrAbove(ResourceId desired_id) { | 237 |
34 ResourceId id; | 238 // Works by recursing through the tree. When taking the left branch |
35 ResourceIdSet::iterator iter = free_ids_.lower_bound(desired_id); | 239 // (searching the smaller id space), |right_limit| is updated. When taking |
36 if (iter != free_ids_.end()) { | 240 // the right branch (searching the larger id space), |left_limit| is updated. |
37 id = *iter; | 241 // When the id space is found, uses |left_limit| or |right_limit| to identify |
38 } else if (LastUsedId() < desired_id) { | 242 // whether the new id range needs to be merged at the end of the limiter or the |
39 id = desired_id; | 243 // current position in the tree. Additional option the special case where passed |
| 244 // in |
| 245 // |first_id_min| causes a new range to be created. |
| 246 IdAllocator::Node* IdAllocator::AddRange(uint32_t range, |
| 247 ResourceId* first_id_out, |
| 248 ResourceId first_id_min, |
| 249 ResourceId first_id_max, |
| 250 Node* node, |
| 251 Node* left_limit, |
| 252 Node* right_limit) { |
| 253 DCHECK(*first_id_out == 0); |
| 254 |
| 255 ResourceId new_range_max = first_id_min + range - 1u; |
| 256 if (new_range_max < first_id_min) { |
| 257 return node; |
| 258 } |
| 259 |
| 260 if (node->min > new_range_max) { |
| 261 if (node->left != NULL) { |
| 262 node->left = AddRange(range, |
| 263 first_id_out, |
| 264 first_id_min, |
| 265 first_id_max, |
| 266 node->left, |
| 267 left_limit, |
| 268 node); |
| 269 if (*first_id_out != 0) { |
| 270 return Balance(node); |
| 271 } |
| 272 } else { |
| 273 *first_id_out = first_id_min; |
| 274 bool merges_node = node->min - new_range_max == 1u; |
| 275 bool merges_left_limit = |
| 276 left_limit != NULL && (first_id_min - left_limit->max == 1u); |
| 277 if (merges_node && merges_left_limit) { |
| 278 left_limit->max = node->max; |
| 279 return Delete(node); |
| 280 } else if (merges_node) { |
| 281 node->min = first_id_min; |
| 282 return node; |
| 283 } else if (merges_left_limit) { |
| 284 left_limit->max = new_range_max; |
| 285 return node; |
| 286 } |
| 287 return Insert(first_id_min, new_range_max, node); |
| 288 } |
| 289 } |
| 290 |
| 291 if (first_id_min <= node->max) { |
| 292 first_id_min = node->max + 1u; |
| 293 if (first_id_min < node->max) { |
| 294 return node; |
| 295 } |
| 296 } |
| 297 |
| 298 if (first_id_min > first_id_max) { |
| 299 return node; |
| 300 } |
| 301 |
| 302 if (node->right != NULL) { |
| 303 node->right = AddRange(range, |
| 304 first_id_out, |
| 305 first_id_min, |
| 306 first_id_max, |
| 307 node->right, |
| 308 node, |
| 309 right_limit); |
| 310 if (*first_id_out != 0) { |
| 311 return Balance(node); |
| 312 } |
| 313 return node; |
| 314 } |
| 315 |
| 316 new_range_max = first_id_min + range - 1u; |
| 317 if (new_range_max < first_id_min) { |
| 318 return node; |
| 319 } |
| 320 |
| 321 if (right_limit != NULL) { |
| 322 if (new_range_max < right_limit->min) { |
| 323 *first_id_out = first_id_min; |
| 324 if (right_limit->min - new_range_max == 1u) { |
| 325 right_limit->min = node->min; |
| 326 return Delete(node); |
| 327 } |
| 328 node->max = new_range_max; |
| 329 return node; |
| 330 } |
40 } else { | 331 } else { |
41 id = LastUsedId() + 1; | 332 *first_id_out = first_id_min; |
42 if (!id) { | 333 if (first_id_min - node->max == 1u) { |
43 // We wrapped around to 0. | 334 node->max = new_range_max; |
44 id = FindFirstUnusedId(); | 335 return node; |
45 } | 336 } |
46 } | 337 return Insert(first_id_min, new_range_max, node); |
47 MarkAsUsed(id); | 338 } |
48 return id; | 339 |
49 } | 340 return node; |
50 | 341 } |
51 ResourceId IdAllocator::AllocateIDRange(uint32_t range) { | 342 |
52 DCHECK(range > 0); | 343 IdAllocator::Node* IdAllocator::RemoveRange(ResourceId first_id, |
53 if (range == 1) { | 344 ResourceId last_id, |
54 return AllocateID(); | 345 Node* pos) { |
55 } | 346 if (pos == NULL) { |
56 | 347 return NULL; |
57 ResourceId first_id = kInvalidResource; | 348 } |
58 | 349 |
59 { | 350 if (first_id < pos->min) { |
60 // Try to re-use ids from free_ids. | 351 pos->left = RemoveRange(first_id, std::min(pos->min, last_id), pos->left); |
61 ResourceIdSet::const_iterator iter = free_ids_.begin(); | 352 } |
62 ResourceIdSet::const_iterator end = free_ids_.end(); | 353 |
63 const ResourceIdSet::size_type size = free_ids_.size(); | 354 if (last_id > pos->max) { |
64 if (iter != end && size >= range) { | 355 pos->right = RemoveRange(std::max(pos->max, first_id), last_id, pos->right); |
65 ResourceId first = *iter; | 356 } |
66 ResourceIdSet::size_type needed_size = range; | 357 |
67 ResourceId prev = first; | 358 if (first_id <= pos->min) { |
68 ++iter; | 359 if (last_id >= pos->min) { |
69 for (; iter != end && size >= needed_size; ++iter) { | 360 ResourceId old_max = pos->max; |
70 if (prev + 1 == *iter) { | 361 if (last_id < old_max) { |
71 if (first + range - 1 == *iter) { | 362 pos->min = last_id + 1u; |
72 first_id = first; | 363 pos->max = old_max; |
73 break; | 364 } else { |
74 } | 365 pos = Delete(pos); |
75 } else { | |
76 needed_size += prev - first + 1; | |
77 first = *iter; | |
78 } | |
79 prev = *iter; | |
80 } | 366 } |
81 } | 367 } |
82 } | 368 } else if (first_id <= pos->max) { |
83 | 369 ResourceId old_max = pos->max; |
84 if (first_id == kInvalidResource) { | 370 pos->max = first_id - 1u; |
85 // Allocate after the last id. | 371 if (last_id < old_max) { |
86 ResourceId first = LastUsedId() + 1; | 372 pos = Insert(last_id + 1u, old_max, pos); |
87 if (first != 0) { | 373 } |
88 ResourceId last = first + range - 1; | 374 } |
89 if (first < last) { | 375 |
90 first_id = first; | 376 if (pos == NULL) { |
91 } | 377 return NULL; |
92 } | 378 } |
93 } | 379 |
94 | 380 // The nodes |pos->left| and |pos->right| are both |
95 if (first_id == kInvalidResource) { | 381 // balanced. However, |pos| is potentially unbalanced. Since |
96 // Scan the gaps between used ids. | 382 // multiple operations might be done during update of |pos->left| and |
97 ResourceId prev = 0; | 383 // |pos->right|, we must balance |pos| multiple times. |
98 for (auto id : used_ids_) { | 384 while (true) { |
99 if (id - prev > range) { | 385 int balance = GetBalance(pos); |
100 first_id = prev + 1; | 386 if (balance >= -1 && balance <= 1) { |
101 break; | 387 break; |
102 } | 388 } |
103 prev = id; | 389 pos = Balance(pos); |
104 } | 390 } |
105 } | 391 |
106 | 392 return pos; |
107 if (first_id != kInvalidResource) { | 393 } |
108 for (uint32 ii = 0; ii < range; ++ii) { | 394 |
109 MarkAsUsed(first_id + ii); | 395 void IdAllocator::Destroy(Node* pos) { |
110 } | 396 do { |
111 } | 397 if (pos->left != NULL) { |
112 | 398 Destroy(pos->left); |
113 return first_id; | 399 } |
114 } | 400 Node* right = pos->right; |
115 | 401 delete pos; |
116 bool IdAllocator::MarkAsUsed(ResourceId id) { | 402 pos = right; |
117 DCHECK(id); | 403 } while (pos != NULL); |
118 free_ids_.erase(id); | |
119 std::pair<ResourceIdSet::iterator, bool> result = used_ids_.insert(id); | |
120 return result.second; | |
121 } | |
122 | |
123 void IdAllocator::FreeID(ResourceId id) { | |
124 if (id) { | |
125 used_ids_.erase(id); | |
126 free_ids_.insert(id); | |
127 } | |
128 } | |
129 | |
130 void IdAllocator::FreeIDRange(ResourceId first_id, uint32 range) { | |
131 for (uint32 ii = 0; ii < range; ++ii) { | |
132 FreeID(first_id + ii); | |
133 } | |
134 } | |
135 | |
136 bool IdAllocator::InUse(ResourceId id) const { | |
137 return id == kInvalidResource || used_ids_.find(id) != used_ids_.end(); | |
138 } | |
139 | |
140 ResourceId IdAllocator::LastUsedId() const { | |
141 if (used_ids_.empty()) { | |
142 return 0u; | |
143 } else { | |
144 return *used_ids_.rbegin(); | |
145 } | |
146 } | |
147 | |
148 ResourceId IdAllocator::FindFirstUnusedId() const { | |
149 ResourceId id = 1; | |
150 for (ResourceIdSet::const_iterator it = used_ids_.begin(); | |
151 it != used_ids_.end(); ++it) { | |
152 if ((*it) != id) { | |
153 return id; | |
154 } | |
155 ++id; | |
156 } | |
157 return id; | |
158 } | 404 } |
159 | 405 |
160 } // namespace gpu | 406 } // namespace gpu |
OLD | NEW |