1 // Copyright (c) 2018 Google Inc.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "source/comp/move_to_front.h"
16
17 #include <algorithm>
18 #include <iomanip>
19 #include <iostream>
20 #include <ostream>
21 #include <sstream>
22 #include <unordered_set>
23 #include <utility>
24
25 namespace spvtools {
26 namespace comp {
27
Insert(uint32_t value)28 bool MoveToFront::Insert(uint32_t value) {
29 auto it = value_to_node_.find(value);
30 if (it != value_to_node_.end() && IsInTree(it->second)) return false;
31
32 const uint32_t old_size = GetSize();
33 (void)old_size;
34
35 InsertNode(CreateNode(next_timestamp_++, value));
36
37 last_accessed_value_ = value;
38 last_accessed_value_valid_ = true;
39
40 assert(value_to_node_.count(value));
41 assert(old_size + 1 == GetSize());
42 return true;
43 }
44
Remove(uint32_t value)45 bool MoveToFront::Remove(uint32_t value) {
46 auto it = value_to_node_.find(value);
47 if (it == value_to_node_.end()) return false;
48
49 if (!IsInTree(it->second)) return false;
50
51 if (last_accessed_value_ == value) last_accessed_value_valid_ = false;
52
53 const uint32_t orphan = RemoveNode(it->second);
54 (void)orphan;
55 // The node of |value| is still alive but it's orphaned now. Can still be
56 // reused later.
57 assert(!IsInTree(orphan));
58 assert(ValueOf(orphan) == value);
59 return true;
60 }
61
RankFromValue(uint32_t value,uint32_t * rank)62 bool MoveToFront::RankFromValue(uint32_t value, uint32_t* rank) {
63 if (last_accessed_value_valid_ && last_accessed_value_ == value) {
64 *rank = 1;
65 return true;
66 }
67
68 const uint32_t old_size = GetSize();
69 if (old_size == 1) {
70 if (ValueOf(root_) == value) {
71 *rank = 1;
72 return true;
73 } else {
74 return false;
75 }
76 }
77
78 const auto it = value_to_node_.find(value);
79 if (it == value_to_node_.end()) {
80 return false;
81 }
82
83 uint32_t target = it->second;
84
85 if (!IsInTree(target)) {
86 return false;
87 }
88
89 uint32_t node = target;
90 *rank = 1 + SizeOf(LeftOf(node));
91 while (node) {
92 if (IsRightChild(node)) *rank += 1 + SizeOf(LeftOf(ParentOf(node)));
93 node = ParentOf(node);
94 }
95
96 // Don't update timestamp if the node has rank 1.
97 if (*rank != 1) {
98 // Update timestamp and reposition the node.
99 target = RemoveNode(target);
100 assert(ValueOf(target) == value);
101 assert(old_size == GetSize() + 1);
102 MutableTimestampOf(target) = next_timestamp_++;
103 InsertNode(target);
104 assert(old_size == GetSize());
105 }
106
107 last_accessed_value_ = value;
108 last_accessed_value_valid_ = true;
109 return true;
110 }
111
HasValue(uint32_t value) const112 bool MoveToFront::HasValue(uint32_t value) const {
113 const auto it = value_to_node_.find(value);
114 if (it == value_to_node_.end()) {
115 return false;
116 }
117
118 return IsInTree(it->second);
119 }
120
Promote(uint32_t value)121 bool MoveToFront::Promote(uint32_t value) {
122 if (last_accessed_value_valid_ && last_accessed_value_ == value) {
123 return true;
124 }
125
126 const uint32_t old_size = GetSize();
127 if (old_size == 1) return ValueOf(root_) == value;
128
129 const auto it = value_to_node_.find(value);
130 if (it == value_to_node_.end()) {
131 return false;
132 }
133
134 uint32_t target = it->second;
135
136 if (!IsInTree(target)) {
137 return false;
138 }
139
140 // Update timestamp and reposition the node.
141 target = RemoveNode(target);
142 assert(ValueOf(target) == value);
143 assert(old_size == GetSize() + 1);
144
145 MutableTimestampOf(target) = next_timestamp_++;
146 InsertNode(target);
147 assert(old_size == GetSize());
148
149 last_accessed_value_ = value;
150 last_accessed_value_valid_ = true;
151 return true;
152 }
153
ValueFromRank(uint32_t rank,uint32_t * value)154 bool MoveToFront::ValueFromRank(uint32_t rank, uint32_t* value) {
155 if (last_accessed_value_valid_ && rank == 1) {
156 *value = last_accessed_value_;
157 return true;
158 }
159
160 const uint32_t old_size = GetSize();
161 if (rank <= 0 || rank > old_size) {
162 return false;
163 }
164
165 if (old_size == 1) {
166 *value = ValueOf(root_);
167 return true;
168 }
169
170 const bool update_timestamp = (rank != 1);
171
172 uint32_t node = root_;
173 while (node) {
174 const uint32_t left_subtree_num_nodes = SizeOf(LeftOf(node));
175 if (rank == left_subtree_num_nodes + 1) {
176 // This is the node we are looking for.
177 // Don't update timestamp if the node has rank 1.
178 if (update_timestamp) {
179 node = RemoveNode(node);
180 assert(old_size == GetSize() + 1);
181 MutableTimestampOf(node) = next_timestamp_++;
182 InsertNode(node);
183 assert(old_size == GetSize());
184 }
185 *value = ValueOf(node);
186 last_accessed_value_ = *value;
187 last_accessed_value_valid_ = true;
188 return true;
189 }
190
191 if (rank < left_subtree_num_nodes + 1) {
192 // Descend into the left subtree. The rank is still valid.
193 node = LeftOf(node);
194 } else {
195 // Descend into the right subtree. We leave behind the left subtree and
196 // the current node, adjust the |rank| accordingly.
197 rank -= left_subtree_num_nodes + 1;
198 node = RightOf(node);
199 }
200 }
201
202 assert(0);
203 return false;
204 }
205
CreateNode(uint32_t timestamp,uint32_t value)206 uint32_t MoveToFront::CreateNode(uint32_t timestamp, uint32_t value) {
207 uint32_t handle = static_cast<uint32_t>(nodes_.size());
208 const auto result = value_to_node_.emplace(value, handle);
209 if (result.second) {
210 // Create new node.
211 nodes_.emplace_back(Node());
212 Node& node = nodes_.back();
213 node.timestamp = timestamp;
214 node.value = value;
215 node.size = 1;
216 // Non-NIL nodes start with height 1 because their NIL children are
217 // leaves.
218 node.height = 1;
219 } else {
220 // Reuse old node.
221 handle = result.first->second;
222 assert(!IsInTree(handle));
223 assert(ValueOf(handle) == value);
224 assert(SizeOf(handle) == 1);
225 assert(HeightOf(handle) == 1);
226 MutableTimestampOf(handle) = timestamp;
227 }
228
229 return handle;
230 }
231
InsertNode(uint32_t node)232 void MoveToFront::InsertNode(uint32_t node) {
233 assert(!IsInTree(node));
234 assert(SizeOf(node) == 1);
235 assert(HeightOf(node) == 1);
236 assert(TimestampOf(node));
237
238 if (!root_) {
239 root_ = node;
240 return;
241 }
242
243 uint32_t iter = root_;
244 uint32_t parent = 0;
245
246 // Will determine if |node| will become the right or left child after
247 // insertion (but before balancing).
248 bool right_child = true;
249
250 // Find the node which will become |node|'s parent after insertion
251 // (but before balancing).
252 while (iter) {
253 parent = iter;
254 assert(TimestampOf(iter) != TimestampOf(node));
255 right_child = TimestampOf(iter) > TimestampOf(node);
256 iter = right_child ? RightOf(iter) : LeftOf(iter);
257 }
258
259 assert(parent);
260
261 // Connect node and parent.
262 MutableParentOf(node) = parent;
263 if (right_child)
264 MutableRightOf(parent) = node;
265 else
266 MutableLeftOf(parent) = node;
267
268 // Insertion is finished. Start the balancing process.
269 bool needs_rebalancing = true;
270 parent = ParentOf(node);
271
272 while (parent) {
273 UpdateNode(parent);
274
275 if (needs_rebalancing) {
276 const int parent_balance = BalanceOf(parent);
277
278 if (RightOf(parent) == node) {
279 // Added node to the right subtree.
280 if (parent_balance > 1) {
281 // Parent is right heavy, rotate left.
282 if (BalanceOf(node) < 0) RotateRight(node);
283 parent = RotateLeft(parent);
284 } else if (parent_balance == 0 || parent_balance == -1) {
285 // Parent is balanced or left heavy, no need to balance further.
286 needs_rebalancing = false;
287 }
288 } else {
289 // Added node to the left subtree.
290 if (parent_balance < -1) {
291 // Parent is left heavy, rotate right.
292 if (BalanceOf(node) > 0) RotateLeft(node);
293 parent = RotateRight(parent);
294 } else if (parent_balance == 0 || parent_balance == 1) {
295 // Parent is balanced or right heavy, no need to balance further.
296 needs_rebalancing = false;
297 }
298 }
299 }
300
301 assert(BalanceOf(parent) >= -1 && (BalanceOf(parent) <= 1));
302
303 node = parent;
304 parent = ParentOf(parent);
305 }
306 }
307
RemoveNode(uint32_t node)308 uint32_t MoveToFront::RemoveNode(uint32_t node) {
309 if (LeftOf(node) && RightOf(node)) {
310 // If |node| has two children, then use another node as scapegoat and swap
311 // their contents. We pick the scapegoat on the side of the tree which has
312 // more nodes.
313 const uint32_t scapegoat = SizeOf(LeftOf(node)) >= SizeOf(RightOf(node))
314 ? RightestDescendantOf(LeftOf(node))
315 : LeftestDescendantOf(RightOf(node));
316 assert(scapegoat);
317 std::swap(MutableValueOf(node), MutableValueOf(scapegoat));
318 std::swap(MutableTimestampOf(node), MutableTimestampOf(scapegoat));
319 value_to_node_[ValueOf(node)] = node;
320 value_to_node_[ValueOf(scapegoat)] = scapegoat;
321 node = scapegoat;
322 }
323
324 // |node| may have only one child at this point.
325 assert(!RightOf(node) || !LeftOf(node));
326
327 uint32_t parent = ParentOf(node);
328 uint32_t child = RightOf(node) ? RightOf(node) : LeftOf(node);
329
330 // Orphan |node| and reconnect parent and child.
331 if (child) MutableParentOf(child) = parent;
332
333 if (parent) {
334 if (LeftOf(parent) == node)
335 MutableLeftOf(parent) = child;
336 else
337 MutableRightOf(parent) = child;
338 }
339
340 MutableParentOf(node) = 0;
341 MutableLeftOf(node) = 0;
342 MutableRightOf(node) = 0;
343 UpdateNode(node);
344 const uint32_t orphan = node;
345
346 if (root_ == node) root_ = child;
347
348 // Removal is finished. Start the balancing process.
349 bool needs_rebalancing = true;
350 node = child;
351
352 while (parent) {
353 UpdateNode(parent);
354
355 if (needs_rebalancing) {
356 const int parent_balance = BalanceOf(parent);
357
358 if (parent_balance == 1 || parent_balance == -1) {
359 // The height of the subtree was not changed.
360 needs_rebalancing = false;
361 } else {
362 if (RightOf(parent) == node) {
363 // Removed node from the right subtree.
364 if (parent_balance < -1) {
365 // Parent is left heavy, rotate right.
366 const uint32_t sibling = LeftOf(parent);
367 if (BalanceOf(sibling) > 0) RotateLeft(sibling);
368 parent = RotateRight(parent);
369 }
370 } else {
371 // Removed node from the left subtree.
372 if (parent_balance > 1) {
373 // Parent is right heavy, rotate left.
374 const uint32_t sibling = RightOf(parent);
375 if (BalanceOf(sibling) < 0) RotateRight(sibling);
376 parent = RotateLeft(parent);
377 }
378 }
379 }
380 }
381
382 assert(BalanceOf(parent) >= -1 && (BalanceOf(parent) <= 1));
383
384 node = parent;
385 parent = ParentOf(parent);
386 }
387
388 return orphan;
389 }
390
RotateLeft(const uint32_t node)391 uint32_t MoveToFront::RotateLeft(const uint32_t node) {
392 const uint32_t pivot = RightOf(node);
393 assert(pivot);
394
395 // LeftOf(pivot) gets attached to node in place of pivot.
396 MutableRightOf(node) = LeftOf(pivot);
397 if (RightOf(node)) MutableParentOf(RightOf(node)) = node;
398
399 // Pivot gets attached to ParentOf(node) in place of node.
400 MutableParentOf(pivot) = ParentOf(node);
401 if (!ParentOf(node))
402 root_ = pivot;
403 else if (IsLeftChild(node))
404 MutableLeftOf(ParentOf(node)) = pivot;
405 else
406 MutableRightOf(ParentOf(node)) = pivot;
407
408 // Node is child of pivot.
409 MutableLeftOf(pivot) = node;
410 MutableParentOf(node) = pivot;
411
412 // Update both node and pivot. Pivot is the new parent of node, so node should
413 // be updated first.
414 UpdateNode(node);
415 UpdateNode(pivot);
416
417 return pivot;
418 }
419
RotateRight(const uint32_t node)420 uint32_t MoveToFront::RotateRight(const uint32_t node) {
421 const uint32_t pivot = LeftOf(node);
422 assert(pivot);
423
424 // RightOf(pivot) gets attached to node in place of pivot.
425 MutableLeftOf(node) = RightOf(pivot);
426 if (LeftOf(node)) MutableParentOf(LeftOf(node)) = node;
427
428 // Pivot gets attached to ParentOf(node) in place of node.
429 MutableParentOf(pivot) = ParentOf(node);
430 if (!ParentOf(node))
431 root_ = pivot;
432 else if (IsLeftChild(node))
433 MutableLeftOf(ParentOf(node)) = pivot;
434 else
435 MutableRightOf(ParentOf(node)) = pivot;
436
437 // Node is child of pivot.
438 MutableRightOf(pivot) = node;
439 MutableParentOf(node) = pivot;
440
441 // Update both node and pivot. Pivot is the new parent of node, so node should
442 // be updated first.
443 UpdateNode(node);
444 UpdateNode(pivot);
445
446 return pivot;
447 }
448
UpdateNode(uint32_t node)449 void MoveToFront::UpdateNode(uint32_t node) {
450 MutableSizeOf(node) = 1 + SizeOf(LeftOf(node)) + SizeOf(RightOf(node));
451 MutableHeightOf(node) =
452 1 + std::max(HeightOf(LeftOf(node)), HeightOf(RightOf(node)));
453 }
454
455 } // namespace comp
456 } // namespace spvtools
457