• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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