1 /*
2 * Copyright (c) 2022 Huawei Device Co., Ltd.
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
16 #include "ecmascript/tagged_node.h"
17
18 #include "ecmascript/tagged_hash_array.h"
19 #include "ecmascript/tagged_queue.h"
20
21 namespace panda::ecmascript {
Treeing(JSThread * thread,const JSHandle<LinkedNode> & head)22 JSHandle<RBTreeNode> LinkedNode::Treeing(JSThread *thread, const JSHandle<LinkedNode> &head)
23 {
24 JSMutableHandle<RBTreeNode> rootNode(thread, JSTaggedValue::Hole());
25 JSMutableHandle<LinkedNode> next(thread, head);
26 JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
27 JSMutableHandle<JSTaggedValue> value(thread, JSTaggedValue::Undefined());
28 while (!next.GetTaggedValue().IsHole()) {
29 key.Update(next->GetKey());
30 value.Update(next->GetValue());
31 rootNode.Update(RBTreeNode::Set(thread, rootNode, next->GetHash().GetInt(), key, value));
32 rootNode->SetIsRed(thread, JSTaggedValue(false));
33 next.Update(next->GetNext());
34 }
35 return rootNode;
36 }
37
InitRBTreeNode(JSThread * thread,int hash,JSHandle<JSTaggedValue> key,JSHandle<JSTaggedValue> value,int count)38 void RBTreeNode::InitRBTreeNode(JSThread *thread, int hash, JSHandle<JSTaggedValue> key,
39 JSHandle<JSTaggedValue> value, int count)
40 {
41 InitTaggedNode(thread, hash, key, value);
42 SetLeft(thread, JSTaggedValue::Hole());
43 SetRight(thread, JSTaggedValue::Hole());
44 SetIsRed(thread, JSTaggedValue(true));
45 SetCount(count);
46 }
47
48 // number of node in subtree rooted at treeNode; 0 if treeNode is Hole
Count(JSTaggedValue nodeValue)49 uint32_t RBTreeNode::Count(JSTaggedValue nodeValue)
50 {
51 if (nodeValue.IsHole()) {
52 return 0;
53 }
54 return RBTreeNode::Cast(nodeValue.GetTaggedObject())->GetCount();
55 }
56
InOrderTraverse(JSThread * thread,const JSHandle<RBTreeNode> & treeNode,JSHandle<LinkedNode> & head,JSHandle<LinkedNode> & tail)57 void RBTreeNode::InOrderTraverse(JSThread *thread, const JSHandle<RBTreeNode> &treeNode,
58 JSHandle<LinkedNode> &head, JSHandle<LinkedNode> &tail)
59 {
60 if (!treeNode.GetTaggedValue().IsHole()) {
61 JSHandle<RBTreeNode> leftChild = JSHandle<RBTreeNode>(thread, treeNode->GetLeft());
62 InOrderTraverse(thread, leftChild, head, tail);
63 JSHandle<LinkedNode> linkedNode = TaggedHashArray::CreateLinkedNodeFrom(thread, treeNode);
64 if (tail.GetTaggedValue().IsHole()) {
65 head = linkedNode;
66 } else {
67 tail->SetNext(thread, linkedNode.GetTaggedValue());
68 }
69 tail = linkedNode;
70 JSHandle<RBTreeNode> rightChild(thread, treeNode->GetRight());
71 InOrderTraverse(thread, rightChild, head, tail);
72 }
73 }
74
Detreeing(JSThread * thread,const JSHandle<RBTreeNode> & root)75 JSHandle<LinkedNode> RBTreeNode::Detreeing(JSThread *thread, const JSHandle<RBTreeNode> &root)
76 {
77 JSHandle<LinkedNode> head(thread, JSTaggedValue::Hole());
78 JSHandle<LinkedNode> tail(thread, JSTaggedValue::Hole());
79
80 InOrderTraverse(thread, root, head, tail);
81
82 return head;
83 }
84
InOrderTraverse(JSThread * thread,const JSHandle<RBTreeNode> & treeNode,int bit,LinkedNodeStruct & nodeStruct)85 void RBTreeNode::InOrderTraverse(JSThread *thread, const JSHandle<RBTreeNode> &treeNode, int bit,
86 LinkedNodeStruct &nodeStruct)
87 {
88 if (!treeNode.GetTaggedValue().IsHole()) {
89 JSHandle<RBTreeNode> leftChild(thread, treeNode->GetLeft());
90 InOrderTraverse(thread, leftChild, bit, nodeStruct);
91
92 JSHandle<LinkedNode> linkedNode = TaggedHashArray::CreateLinkedNodeFrom(thread, treeNode);
93 // the elements from each bin must either stay at same index,
94 // or move with a power of two offset in the new table
95 if ((linkedNode->GetHash().GetInt() & bit) == 0) {
96 if (nodeStruct.lowerTail.GetTaggedValue().IsHole()) {
97 nodeStruct.lowerHead = linkedNode;
98 } else {
99 nodeStruct.lowerTail->SetNext(thread, linkedNode.GetTaggedValue());
100 }
101 nodeStruct.lowerTail = linkedNode;
102 } else {
103 if (nodeStruct.higherTail.GetTaggedValue().IsHole()) {
104 nodeStruct.higherHead = linkedNode;
105 } else {
106 nodeStruct.higherTail->SetNext(thread, linkedNode.GetTaggedValue());
107 }
108 nodeStruct.higherTail = linkedNode;
109 }
110
111 JSHandle<RBTreeNode> rightChild(thread, treeNode->GetRight());
112 InOrderTraverse(thread, rightChild, bit, nodeStruct);
113 }
114 }
115
Divide(JSThread * thread,JSHandle<TaggedHashArray> table,JSHandle<JSTaggedValue> nodeVa,int index,int bit)116 void RBTreeNode::Divide(JSThread *thread, JSHandle<TaggedHashArray> table,
117 JSHandle<JSTaggedValue> nodeVa, int index, int bit)
118 {
119 JSHandle<RBTreeNode> self = JSHandle<RBTreeNode>::Cast(nodeVa);
120 LinkedNodeStruct nodeStruct {JSHandle<LinkedNode>(thread, JSTaggedValue::Hole()),
121 JSHandle<LinkedNode>(thread, JSTaggedValue::Hole()),
122 JSHandle<LinkedNode>(thread, JSTaggedValue::Hole()),
123 JSHandle<LinkedNode>(thread, JSTaggedValue::Hole())};
124
125 InOrderTraverse(thread, self, bit, nodeStruct);
126
127 uint32_t loCount = 0;
128 uint32_t hiCount = 0;
129 JSMutableHandle<LinkedNode> lowerHead(thread, nodeStruct.lowerHead);
130 while (!lowerHead.GetTaggedValue().IsHole()) {
131 loCount++;
132 lowerHead.Update(lowerHead->GetNext());
133 }
134 JSMutableHandle<LinkedNode> higherHead(thread, nodeStruct.higherHead);
135 while (!higherHead.GetTaggedValue().IsHole()) {
136 loCount++;
137 higherHead.Update(higherHead->GetNext());
138 }
139
140 if (!nodeStruct.lowerHead.GetTaggedValue().IsHole()) {
141 if (loCount >= TaggedHashArray::TREEIFY_THRESHOLD) {
142 JSHandle<RBTreeNode> loRoot = LinkedNode::Treeing(thread, nodeStruct.lowerHead);
143 table->Set(thread, index, loRoot);
144 } else {
145 table->Set(thread, index, nodeStruct.lowerHead);
146 }
147 }
148 if (!nodeStruct.higherHead.GetTaggedValue().IsHole()) {
149 if (hiCount >= TaggedHashArray::TREEIFY_THRESHOLD) {
150 JSHandle<RBTreeNode> hiRoot = LinkedNode::Treeing(thread, nodeStruct.higherHead);
151 table->Set(thread, index + bit, hiRoot);
152 } else {
153 table->Set(thread, index + bit, nodeStruct.higherHead);
154 }
155 }
156 }
157
Compare(int hash1,JSTaggedValue key1,int hash2,JSTaggedValue key2)158 int RBTreeNode::Compare(int hash1, JSTaggedValue key1, int hash2, JSTaggedValue key2)
159 {
160 ASSERT(!key1.IsHole() && !key2.IsHole());
161 if (JSTaggedValue::SameValue(key1, key2)) {
162 return 0;
163 }
164 if (hash1 < hash2) {
165 return -1;
166 } else {
167 return 1;
168 }
169 }
170
IsRed(JSTaggedValue treeNodeValue)171 bool RBTreeNode::IsRed(JSTaggedValue treeNodeValue)
172 {
173 if (treeNodeValue.IsHole()) {
174 return false;
175 }
176 RBTreeNode *treeNode = RBTreeNode::Cast(treeNodeValue.GetTaggedObject());
177 return treeNode->GetIsRed().ToBoolean();
178 }
179
180 // insert the key-value pair in the subtree rooted at treeNode
Set(JSThread * thread,JSHandle<RBTreeNode> treeNode,int hash,JSHandle<JSTaggedValue> key,JSHandle<JSTaggedValue> value)181 JSHandle<RBTreeNode> RBTreeNode::Set(JSThread *thread, JSHandle<RBTreeNode> treeNode, int hash,
182 JSHandle<JSTaggedValue> key, JSHandle<JSTaggedValue> value)
183 {
184 if (treeNode.GetTaggedValue().IsHole()) {
185 treeNode = TaggedHashArray::NewTreeNode(thread, hash, key, value);
186 return treeNode;
187 }
188 JSHandle<JSTaggedValue> treeNodeKey(thread, treeNode->GetKey());
189 int cmp = Compare(hash, key.GetTaggedValue(), treeNode->GetHash().GetInt(), treeNodeKey.GetTaggedValue());
190 JSHandle<RBTreeNode> leftChild(thread, treeNode->GetLeft());
191 JSHandle<RBTreeNode> rightChild(thread, treeNode->GetRight());
192 if (cmp < 0) {
193 JSHandle<RBTreeNode> left = Set(thread, leftChild, hash, key, value);
194 treeNode->SetLeft(thread, left);
195 } else if (cmp > 0) {
196 JSHandle<RBTreeNode> right = Set(thread, rightChild, hash, key, value);
197 treeNode->SetRight(thread, right);
198 } else {
199 treeNode->SetValue(thread, value);
200 }
201
202 if (IsRed(treeNode->GetRight()) && !IsRed(treeNode->GetLeft())) {
203 treeNode = JSHandle<RBTreeNode>(thread, treeNode->RotateLeft(thread));
204 }
205 JSTaggedValue leftChildVa = treeNode->GetLeft();
206 if (!leftChildVa.IsHole()) {
207 leftChild = JSHandle<RBTreeNode>(thread, leftChildVa);
208 if (IsRed(treeNode->GetLeft()) && IsRed(leftChild->GetLeft())) {
209 treeNode = JSHandle<RBTreeNode>(thread, treeNode->RotateRight(thread));
210 }
211 }
212 if (IsRed(treeNode->GetLeft()) && IsRed(treeNode->GetRight())) {
213 treeNode->FlipColors(thread);
214 }
215
216 // 1 : root count
217 uint32_t count = Count(treeNode->GetLeft()) + Count(treeNode->GetRight()) + 1;
218 treeNode->SetCount(count);
219
220 return treeNode;
221 }
222
223 // make a right-leaning link lean to the left
RotateLeft(JSThread * thread)224 RBTreeNode *RBTreeNode::RotateLeft(JSThread *thread)
225 {
226 ASSERT(!JSTaggedValue(this).IsHole() && IsRed(GetRight()));
227 RBTreeNode *temp = RBTreeNode::Cast(GetRight().GetTaggedObject());
228 SetRight(thread, temp->GetLeft());
229 temp->SetLeft(thread, JSTaggedValue(this));
230 RBTreeNode *tempLeft = RBTreeNode::Cast(temp->GetLeft().GetTaggedObject());
231 temp->SetIsRed(thread, tempLeft->GetIsRed());
232 tempLeft->SetIsRed(thread, JSTaggedValue(true));
233
234 temp->SetCount(GetCount());
235 // 1 : root count
236 uint32_t count = Count(GetLeft()) + Count(GetRight()) + 1;
237 SetCount(count);
238
239 return temp;
240 }
241
242 // make a left-leaning link lean to the right
RotateRight(JSThread * thread)243 RBTreeNode *RBTreeNode::RotateRight(JSThread *thread)
244 {
245 ASSERT(!JSTaggedValue(this).IsHole() && IsRed(GetLeft()));
246 RBTreeNode *temp = RBTreeNode::Cast(GetLeft().GetTaggedObject());
247 SetLeft(thread, temp->GetRight());
248 temp->SetRight(thread, JSTaggedValue(this));
249 RBTreeNode *tempRight = RBTreeNode::Cast(temp->GetRight().GetTaggedObject());
250 temp->SetIsRed(thread, tempRight->GetIsRed());
251 tempRight->SetIsRed(thread, JSTaggedValue(true));
252
253 temp->SetCount(GetCount());
254 // 1 : root count
255 uint32_t count = Count(GetLeft()) + Count(GetRight()) + 1;
256 SetCount(count);
257
258 return temp;
259 }
260
261 // flip the colors of a node and its two children
FlipColors(JSThread * thread)262 void RBTreeNode::FlipColors(JSThread *thread)
263 {
264 SetIsRed(thread, JSTaggedValue(!GetIsRed().ToBoolean()));
265 RBTreeNode *leftChild = RBTreeNode::Cast(GetLeft().GetTaggedObject());
266 leftChild->SetIsRed(thread, JSTaggedValue(!leftChild->GetIsRed().ToBoolean()));
267 RBTreeNode *rightChild = RBTreeNode::Cast(GetRight().GetTaggedObject());
268 rightChild->SetIsRed(thread, JSTaggedValue(!rightChild->GetIsRed().ToBoolean()));
269 }
270
271 // restore red-black tree invariant
Balance(JSThread * thread,RBTreeNode * treeNode)272 JSTaggedValue RBTreeNode::Balance(JSThread *thread, RBTreeNode *treeNode)
273 {
274 if (IsRed(treeNode->GetRight()) && !IsRed(treeNode->GetLeft())) {
275 treeNode = treeNode->RotateLeft(thread);
276 }
277 JSTaggedValue leftValue = treeNode->GetLeft();
278 if (!leftValue.IsHole()) {
279 RBTreeNode *leftChild = RBTreeNode::Cast(leftValue.GetTaggedObject());
280 if (IsRed(treeNode->GetLeft()) && IsRed(leftChild->GetLeft())) {
281 treeNode = treeNode->RotateRight(thread);
282 }
283 }
284 if (IsRed(treeNode->GetLeft()) && IsRed(treeNode->GetRight())) {
285 treeNode->FlipColors(thread);
286 }
287 // 1 : root count
288 uint32_t count = Count(treeNode->GetLeft()) + Count(treeNode->GetRight()) + 1;
289 treeNode->SetCount(count);
290
291 return JSTaggedValue(treeNode);
292 }
293
MoveRedLeft(JSThread * thread)294 RBTreeNode *RBTreeNode::MoveRedLeft(JSThread *thread)
295 {
296 RBTreeNode *treeNode = this;
297 treeNode->FlipColors(thread);
298 RBTreeNode *rightChild = RBTreeNode::Cast(treeNode->GetRight().GetTaggedObject());
299 if (IsRed(rightChild->GetLeft())) {
300 rightChild = rightChild->RotateRight(thread);
301 treeNode->SetRight(thread, JSTaggedValue(rightChild));
302 treeNode = treeNode->RotateLeft(thread);
303 treeNode->FlipColors(thread);
304 }
305
306 return treeNode;
307 }
308
MoveRedRight(JSThread * thread)309 RBTreeNode *RBTreeNode::MoveRedRight(JSThread *thread)
310 {
311 RBTreeNode *treeNode = this;
312 treeNode->FlipColors(thread);
313 RBTreeNode *leftChild = RBTreeNode::Cast(treeNode->GetLeft().GetTaggedObject());
314 if (IsRed(leftChild->GetLeft())) {
315 treeNode = treeNode->RotateRight(thread);
316 treeNode->FlipColors(thread);
317 }
318
319 return treeNode;
320 }
321
322 // delete the key-value pair with the minimum key rooted at treeNode
DeleteMin(JSThread * thread,RBTreeNode * treeNode)323 JSTaggedValue RBTreeNode::DeleteMin(JSThread *thread, RBTreeNode *treeNode)
324 {
325 if (treeNode->GetLeft().IsHole()) {
326 return JSTaggedValue::Hole();
327 }
328 RBTreeNode *leftChild = RBTreeNode::Cast(treeNode->GetLeft().GetTaggedObject());
329 if (!IsRed(treeNode->GetLeft()) && !IsRed(leftChild->GetLeft())) {
330 treeNode = treeNode->MoveRedLeft(thread);
331 }
332
333 treeNode->SetLeft(thread, DeleteMin(thread, leftChild));
334 return Balance(thread, treeNode);
335 }
336
337 // delete the key-value pair with the given key rooted at treeNode
Delete(JSThread * thread,const JSTaggedValue & treeNodeVa,int hash,const JSTaggedValue & key,JSTaggedValue & oldValue)338 JSTaggedValue RBTreeNode::Delete(JSThread *thread, const JSTaggedValue &treeNodeVa, int hash,
339 const JSTaggedValue &key, JSTaggedValue &oldValue)
340 {
341 RBTreeNode *treeNode = RBTreeNode::Cast(treeNodeVa.GetTaggedObject());
342 JSTaggedValue leftChildVa = treeNode->GetLeft();
343 JSTaggedValue treeNodeKey = treeNode->GetKey();
344 int cmp = Compare(hash, key, treeNode->GetHash().GetInt(), treeNodeKey);
345 if (cmp < 0) {
346 if (!IsRed(treeNode->GetLeft()) && !IsRed(RBTreeNode::Cast(leftChildVa.GetTaggedObject())->GetLeft())) {
347 treeNode = treeNode->MoveRedLeft(thread);
348 }
349 leftChildVa = treeNode->GetLeft();
350 JSTaggedValue leftValue = Delete(thread, leftChildVa, hash, key, oldValue);
351 treeNode->SetLeft(thread, leftValue);
352 } else {
353 if (IsRed(treeNode->GetLeft())) {
354 treeNode = treeNode->RotateRight(thread);
355 treeNodeKey = treeNode->GetKey();
356 }
357 cmp = Compare(hash, key, treeNode->GetHash().GetInt(), treeNodeKey);
358 if (cmp == 0 && treeNode->GetRight().IsHole()) {
359 return JSTaggedValue::Hole();
360 }
361 JSTaggedValue rightChildVa = treeNode->GetRight();
362 if (!IsRed(rightChildVa) && !IsRed(RBTreeNode::Cast(rightChildVa.GetTaggedObject())->GetLeft())) {
363 treeNode = treeNode->MoveRedRight(thread);
364 treeNodeKey = treeNode->GetKey();
365 }
366
367 cmp = Compare(hash, key, treeNode->GetHash().GetInt(), treeNodeKey);
368 rightChildVa = treeNode->GetRight();
369 RBTreeNode *rightChild = RBTreeNode::Cast(rightChildVa.GetTaggedObject());
370 if (cmp == 0) {
371 oldValue = treeNode->GetValue();
372 RBTreeNode *minNode = rightChild->Min();
373 treeNode->SetKey(thread, minNode->GetKey());
374 treeNode->SetValue(thread, minNode->GetValue());
375 treeNode->SetHash(thread, minNode->GetHash());
376 treeNode->SetRight(thread, DeleteMin(thread, rightChild));
377 } else {
378 JSTaggedValue tmpValue = Delete(thread, rightChildVa, rightChild->GetHash().GetInt(), key, oldValue);
379 treeNode->SetRight(thread, tmpValue);
380 }
381 }
382 return Balance(thread, treeNode);
383 }
384
385 // the smallest key in subtree rooted at treeNode; hole if no such key
Min()386 RBTreeNode *RBTreeNode::Min()
387 {
388 if (GetLeft().IsHole()) {
389 return this;
390 } else {
391 return RBTreeNode::Cast(GetLeft().GetTaggedObject())->Min();
392 }
393 }
394
395 // node associated with the given key in subtree rooted at treeNode; null if no such key
GetTreeNode(JSThread * thread,JSHandle<JSTaggedValue> treeNodeVa,int hash,JSHandle<JSTaggedValue> key)396 JSTaggedValue RBTreeNode::GetTreeNode(JSThread *thread, JSHandle<JSTaggedValue> treeNodeVa,
397 int hash, JSHandle<JSTaggedValue> key)
398 {
399 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
400 JSMutableHandle<TaggedQueue> queue(thread, factory->NewTaggedQueue(0));
401 queue.Update(JSTaggedValue(TaggedQueue::Push(thread, queue, treeNodeVa)));
402 JSMutableHandle<RBTreeNode> root(thread, JSTaggedValue::Hole());
403 JSMutableHandle<JSTaggedValue> currentKey(thread, JSTaggedValue::Hole());
404 JSMutableHandle<JSTaggedValue> left(thread, JSTaggedValue::Hole());
405 JSMutableHandle<JSTaggedValue> right(thread, JSTaggedValue::Hole());
406 while (!queue->Empty()) {
407 root.Update(queue->Pop(thread));
408 currentKey.Update(root->GetKey());
409 if (root->GetHash().GetInt() == hash && (!currentKey->IsHole() && JSTaggedValue::SameValue(key, currentKey))) {
410 return root.GetTaggedValue();
411 }
412 if (!root->GetLeft().IsHole()) {
413 left.Update(root->GetLeft());
414 queue.Update(JSTaggedValue(TaggedQueue::Push(thread, queue, left)));
415 }
416 if (!root->GetRight().IsHole()) {
417 right.Update(root->GetRight());
418 queue.Update(JSTaggedValue(TaggedQueue::Push(thread, queue, right)));
419 }
420 }
421 return JSTaggedValue::Hole();
422 }
423 } // namespace panda::ecmascript
424