• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021 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 #ifndef ECMASCRIPT_TAGGED_HASH_TABLE_INL_H
17 #define ECMASCRIPT_TAGGED_HASH_TABLE_INL_H
18 
19 #include "ecmascript/object_factory.h"
20 #include "ecmascript/tagged_array-inl.h"
21 #include "ecmascript/tagged_hash_table.h"
22 
23 namespace panda::ecmascript {
24 template<typename Derived>
EntriesCount()25 int TaggedHashTable<Derived>::EntriesCount() const
26 {
27     return Get(NUMBER_OF_ENTRIES_INDEX).GetInt();
28 }
29 
30 template<typename Derived>
HoleEntriesCount()31 int TaggedHashTable<Derived>::HoleEntriesCount() const
32 {
33     return Get(NUMBER_OF_HOLE_ENTRIES_INDEX).GetInt();
34 }
35 
36 template<typename Derived>
Size()37 int TaggedHashTable<Derived>::Size() const
38 {
39     return Get(SIZE_INDEX).GetInt();
40 }
41 
42 template<typename Derived>
IncreaseEntries(const JSThread * thread)43 void TaggedHashTable<Derived>::IncreaseEntries(const JSThread *thread)
44 {
45     SetEntriesCount(thread, EntriesCount() + 1);
46 }
47 
48 template<typename Derived>
IncreaseHoleEntriesCount(const JSThread * thread,int number)49 void TaggedHashTable<Derived>::IncreaseHoleEntriesCount(const JSThread *thread, int number)
50 {
51     SetEntriesCount(thread, EntriesCount() - number);
52     SetHoleEntriesCount(thread, HoleEntriesCount() + number);
53 }
54 
55 template<typename Derived>
SetEntriesCount(const JSThread * thread,int nof)56 void TaggedHashTable<Derived>::SetEntriesCount(const JSThread *thread, int nof)
57 {
58     Set(thread, NUMBER_OF_ENTRIES_INDEX, JSTaggedValue(nof));
59 }
60 
61 template<typename Derived>
SetHoleEntriesCount(const JSThread * thread,int nod)62 void TaggedHashTable<Derived>::SetHoleEntriesCount(const JSThread *thread, int nod)
63 {
64     Set(thread, NUMBER_OF_HOLE_ENTRIES_INDEX, JSTaggedValue(nod));
65 }
66 
67 template<typename Derived>
SetHashTableSize(const JSThread * thread,int size)68 void TaggedHashTable<Derived>::SetHashTableSize(const JSThread *thread, int size)
69 {
70     Set(thread, SIZE_INDEX, JSTaggedValue(size));
71 }
72 
73 template<typename Derived>
GetAllKeys(const JSThread * thread,int offset,TaggedArray * keyArray)74 void TaggedHashTable<Derived>::GetAllKeys(const JSThread *thread, int offset, TaggedArray *keyArray) const
75 {
76     ASSERT_PRINT(offset + EntriesCount() <= static_cast<int>(keyArray->GetLength()),
77                  "keyArray size is not enough for dictionary");
78     int arrayIndex = 0;
79     int size = Size();
80     for (int hashIndex = 0; hashIndex < size; hashIndex++) {
81         JSTaggedValue key = GetKey(hashIndex);
82         if (!key.IsUndefined() && !key.IsHole()) {
83             keyArray->Set(thread, arrayIndex + offset, key);
84             arrayIndex++;
85         }
86     }
87 }
88 
89 // Find entry for key otherwise return -1.
90 template<typename Derived>
FindEntry(const JSTaggedValue & key)91 int TaggedHashTable<Derived>::FindEntry(const JSTaggedValue &key)
92 {
93     size_t size = Size();
94     int count = 1;
95     JSTaggedValue keyValue;
96     int hash = Derived::Hash(key);
97 
98     for (int entry = GetFirstPosition(hash, size);; entry = GetNextPosition(entry, count++, size)) {
99         keyValue = GetKey(entry);
100         if (keyValue.IsHole()) {
101             continue;
102         }
103         if (keyValue.IsUndefined()) {
104             return -1;
105         }
106         if (Derived::IsMatch(key, keyValue)) {
107             return entry;
108         }
109     }
110     return -1;
111 }
112 
113 // static
114 template<typename Derived>
RecalculateTableSize(int currentSize,int atLeastSize)115 int TaggedHashTable<Derived>::RecalculateTableSize(int currentSize, int atLeastSize)
116 {
117     // When the filled entries is greater than a quart of currentSize
118     // it need not to shrink
119     if (atLeastSize > (currentSize / 4)) {  // 4 : quarter
120         return currentSize;
121     }
122     // Recalculate table size
123     int newSize = ComputeHashTableSize(atLeastSize);
124     ASSERT_PRINT(newSize > atLeastSize, "new size must greater than atLeastSize");
125     // Don't go lower than room for MIN_SHRINK_SIZE elements.
126     if (newSize < MIN_SHRINK_SIZE) {
127         return currentSize;
128     }
129     return newSize;
130 }
131 
132 // static
133 template<typename Derived>
Shrink(const JSThread * thread,const JSHandle<Derived> & table,int additionalSize)134 JSHandle<Derived> TaggedHashTable<Derived>::Shrink(const JSThread *thread, const JSHandle<Derived> &table,
135                                                    int additionalSize)
136 {
137     int newSize = RecalculateTableSize(table->Size(), table->EntriesCount() + additionalSize);
138     if (newSize == table->Size()) {
139         return table;
140     }
141 
142     JSHandle<Derived> newTable = TaggedHashTable::Create(thread, newSize);
143 
144     table->Rehash(thread, *newTable);
145     return newTable;
146 }
147 
148 template<typename Derived>
IsNeedGrowHashTable(int numOfAddEntries)149 bool TaggedHashTable<Derived>::IsNeedGrowHashTable(int numOfAddEntries)
150 {
151     int entriesCount = EntriesCount();
152     int numOfDelEntries = HoleEntriesCount();
153     int currentSize = Size();
154     int numberFilled = entriesCount + numOfAddEntries;
155     // needn't to grow table:
156     //   1. after adding number entries, table have half free entries.
157     //   2. deleted entries are less than half of the free entries.
158     const int halfFree = 2;
159     if ((numberFilled < currentSize) && ((numOfDelEntries <= (currentSize - numberFilled) / halfFree))) {
160         int neededFree = numberFilled / halfFree;
161         if (numberFilled + neededFree <= currentSize) {
162             return false;
163         }
164     }
165     return true;
166 }
167 
168 template<typename Derived>
AddElement(const JSThread * thread,int entry,const JSHandle<JSTaggedValue> & key,const JSHandle<JSTaggedValue> & value)169 void TaggedHashTable<Derived>::AddElement(const JSThread *thread, int entry, const JSHandle<JSTaggedValue> &key,
170                                           const JSHandle<JSTaggedValue> &value)
171 {
172     this->SetKey(thread, entry, key.GetTaggedValue());
173     this->SetValue(thread, entry, value.GetTaggedValue());
174     this->IncreaseEntries(thread);
175 }
176 
177 template<typename Derived>
RemoveElement(const JSThread * thread,int entry)178 void TaggedHashTable<Derived>::RemoveElement(const JSThread *thread, int entry)
179 {
180     JSTaggedValue defaultValue(JSTaggedValue::Hole());
181     this->SetKey(thread, entry, defaultValue);
182     this->SetValue(thread, entry, defaultValue);
183     this->IncreaseHoleEntriesCount(thread);
184 }
185 
186 template<typename Derived>
Insert(const JSThread * thread,JSHandle<Derived> & table,const JSHandle<JSTaggedValue> & key,const JSHandle<JSTaggedValue> & value)187 JSHandle<Derived> TaggedHashTable<Derived>::Insert(const JSThread *thread, JSHandle<Derived> &table,
188                                                    const JSHandle<JSTaggedValue> &key,
189                                                    const JSHandle<JSTaggedValue> &value)
190 {
191     // Make sure the key object has an identity hash code.
192     int hash = Derived::Hash(key.GetTaggedValue());
193     int entry = table->FindEntry(key.GetTaggedValue());
194     if (entry != -1) {
195         table->SetValue(thread, entry, value.GetTaggedValue());
196         return table;
197     }
198 
199     JSHandle<Derived> newTable = GrowHashTable(thread, table);
200     newTable->AddElement(thread, newTable->FindInsertIndex(hash), key, value);
201     return newTable;
202 }
203 
204 template<typename Derived>
Remove(const JSThread * thread,JSHandle<Derived> & table,const JSHandle<JSTaggedValue> & key)205 JSHandle<Derived> TaggedHashTable<Derived>::Remove(const JSThread *thread, JSHandle<Derived> &table,
206                                                    const JSHandle<JSTaggedValue> &key)
207 {
208     int entry = table->FindEntry(key.GetTaggedValue());
209     if (entry == -1) {
210         return table;
211     }
212 
213     table->RemoveElement(thread, entry);
214     return Derived::Shrink(thread, *table);
215 }
216 
217 template<typename Derived>
Rehash(const JSThread * thread,Derived * newTable)218 void TaggedHashTable<Derived>::Rehash(const JSThread *thread, Derived *newTable)
219 {
220     if ((newTable == nullptr) || (newTable->Size() < EntriesCount())) {
221         return;
222     }
223     int currentSize = this->Size();
224     // Rehash elements to new table
225     for (int i = 0; i < currentSize; i++) {
226         int fromIndex = Derived::GetKeyIndex(i);
227         JSTaggedValue k = this->GetKey(i);
228         if (!IsKey(k)) {
229             continue;
230         }
231         int hash = Derived::Hash(k);
232         int insertionIndex = Derived::GetKeyIndex(newTable->FindInsertIndex(hash));
233         JSTaggedValue tv = Get(fromIndex);
234         newTable->Set(thread, insertionIndex, tv);
235         for (int j = 1; j < Derived::GetEntrySize(); j++) {
236             tv = Get(fromIndex + j);
237             newTable->Set(thread, insertionIndex + j, tv);
238         }
239     }
240     newTable->SetEntriesCount(thread, EntriesCount());
241     newTable->SetHoleEntriesCount(thread, 0);
242 }
243 
244 // static
245 template<typename Derived>
ComputeHashTableSize(uint32_t atLeastSize)246 int TaggedHashTable<Derived>::ComputeHashTableSize(uint32_t atLeastSize)
247 {
248     //  increase size for hash-collision
249     uint32_t rawSize = atLeastSize + (atLeastSize >> 1UL);
250     int newSize = static_cast<int>(helpers::math::GetPowerOfTwoValue32(rawSize));
251     return (newSize > MIN_SIZE) ? newSize : MIN_SIZE;
252 }
253 
254 template<typename Derived>
GrowHashTable(const JSThread * thread,const JSHandle<Derived> & table,int numOfAddedElements)255 JSHandle<Derived> TaggedHashTable<Derived>::GrowHashTable(const JSThread *thread, const JSHandle<Derived> &table,
256                                                           int numOfAddedElements)
257 {
258     if (!table->IsNeedGrowHashTable(numOfAddedElements)) {
259         return table;
260     }
261     int newSize = ComputeHashTableSize(table->Size() + numOfAddedElements);
262     int length = Derived::GetEntryIndex(newSize);
263     JSHandle<Derived> newTable(thread->GetEcmaVM()->GetFactory()->NewDictionaryArray(length));
264     newTable->SetHashTableSize(thread, newSize);
265     table->Rehash(thread, *newTable);
266     return newTable;
267 }
268 
269 template<typename Derived>
Create(const JSThread * thread,int entriesCount)270 JSHandle<Derived> TaggedHashTable<Derived>::Create(const JSThread *thread, int entriesCount)
271 {
272     ASSERT_PRINT((entriesCount > 0), "the size must be greater than zero");
273     auto size = static_cast<uint32_t>(entriesCount);
274     ASSERT_PRINT(helpers::math::IsPowerOfTwo(static_cast<uint32_t>(entriesCount)), "the size must be power of two");
275 
276     int length = Derived::GetEntryIndex(entriesCount);
277 
278     JSHandle<Derived> table(thread->GetEcmaVM()->GetFactory()->NewDictionaryArray(length));
279     table->SetEntriesCount(thread, 0);
280     table->SetHoleEntriesCount(thread, 0);
281     table->SetHashTableSize(thread, size);
282     return table;
283 }
284 
285 template<typename Derived>
SetKey(const JSThread * thread,int entry,const JSTaggedValue & key)286 void TaggedHashTable<Derived>::SetKey(const JSThread *thread, int entry, const JSTaggedValue &key)
287 {
288     int index = Derived::GetKeyIndex(entry);
289     if (UNLIKELY(index < 0 || index > static_cast<int>(GetLength()))) {
290         return;
291     }
292     Set(thread, index, key);
293 }
294 
295 template<typename Derived>
GetKey(int entry)296 JSTaggedValue TaggedHashTable<Derived>::GetKey(int entry) const
297 {
298     int index = Derived::GetKeyIndex(entry);
299     if (UNLIKELY((index < 0 || index > static_cast<int>(GetLength())))) {
300         return JSTaggedValue::Undefined();
301     }
302     return Get(index);
303 }
304 
305 template<typename Derived>
SetValue(const JSThread * thread,int entry,const JSTaggedValue & value)306 void TaggedHashTable<Derived>::SetValue(const JSThread *thread, int entry, const JSTaggedValue &value)
307 {
308     int index = Derived::GetValueIndex(entry);
309     if (UNLIKELY((index < 0 || index > static_cast<int>(GetLength())))) {
310         return;
311     }
312     Set(thread, index, value);
313 }
314 
315 template<typename Derived>
GetValue(int entry)316 JSTaggedValue TaggedHashTable<Derived>::GetValue(int entry) const
317 {
318     int index = Derived::GetValueIndex(entry);
319     if (UNLIKELY((index < 0 || index > static_cast<int>(GetLength())))) {
320         return JSTaggedValue::Undefined();
321     }
322     return Get(index);
323 }
324 
325 template<typename Derived>
FindInsertIndex(int hash)326 int TaggedHashTable<Derived>::FindInsertIndex(int hash)
327 {
328     int size = Size();
329     int count = 1;
330     // GrowHashTable will guarantee the hash table is never full.
331     for (int entry = GetFirstPosition(hash, size);; entry = GetNextPosition(entry, count++, size)) {
332         if (!IsKey(GetKey(entry))) {
333             return entry;
334         }
335     }
336 }
337 
338 template<typename Derived>
Create(const JSThread * thread,int numberOfElements)339 JSHandle<Derived> OrderTaggedHashTable<Derived>::Create(const JSThread *thread, int numberOfElements)
340 {
341     JSHandle<Derived> dict = HashTableT::Create(thread, numberOfElements);
342     dict->SetNextEnumerationIndex(thread, PropertyAttributes::INTIAL_PROPERTY_INDEX);
343     return dict;
344 }
345 
346 template<typename Derived>
PutIfAbsent(const JSThread * thread,const JSHandle<Derived> & table,const JSHandle<JSTaggedValue> & key,const JSHandle<JSTaggedValue> & value,const PropertyAttributes & metaData)347 JSHandle<Derived> OrderTaggedHashTable<Derived>::PutIfAbsent(const JSThread *thread, const JSHandle<Derived> &table,
348                                                              const JSHandle<JSTaggedValue> &key,
349                                                              const JSHandle<JSTaggedValue> &value,
350                                                              const PropertyAttributes &metaData)
351 {
352     int hash = Derived::Hash(key.GetTaggedValue());
353 
354     /* no need to add key if exist */
355     int entry = table->FindEntry(key.GetTaggedValue());
356     if (entry != -1) {
357         return table;
358     }
359     int enumIndex = table->NextEnumerationIndex(thread);
360     PropertyAttributes attr(metaData);
361     attr.SetDictionaryOrder(enumIndex);
362     // Check whether the table should be growed.
363     JSHandle<Derived> newTable = HashTableT::GrowHashTable(thread, table);
364 
365     // Compute the key object.
366     entry = newTable->FindInsertIndex(hash);
367     newTable->SetEntry(thread, entry, key.GetTaggedValue(), value.GetTaggedValue(), attr);
368 
369     newTable->IncreaseEntries(thread);
370     newTable->SetNextEnumerationIndex(thread, enumIndex + 1);
371     return newTable;
372 }
373 
374 template<typename Derived>
Put(const JSThread * thread,const JSHandle<Derived> & table,const JSHandle<JSTaggedValue> & key,const JSHandle<JSTaggedValue> & value,const PropertyAttributes & metaData)375 JSHandle<Derived> OrderTaggedHashTable<Derived>::Put(const JSThread *thread, const JSHandle<Derived> &table,
376                                                      const JSHandle<JSTaggedValue> &key,
377                                                      const JSHandle<JSTaggedValue> &value,
378                                                      const PropertyAttributes &metaData)
379 {
380     int hash = Derived::Hash(key.GetTaggedValue());
381     int enumIndex = table->NextEnumerationIndex(thread);
382     PropertyAttributes attr(metaData);
383     attr.SetDictionaryOrder(enumIndex);
384     int entry = table->FindEntry(key.GetTaggedValue());
385     if (entry != -1) {
386         table->SetEntry(thread, entry, key.GetTaggedValue(), value.GetTaggedValue(), attr);
387         return table;
388     }
389     // Check whether the table should be extended.
390     JSHandle<Derived> newTable = HashTableT::GrowHashTable(thread, table);
391 
392     // Compute the key object.
393     entry = newTable->FindInsertIndex(hash);
394     newTable->SetEntry(thread, entry, key.GetTaggedValue(), value.GetTaggedValue(), attr);
395 
396     newTable->IncreaseEntries(thread);
397     newTable->SetNextEnumerationIndex(thread, enumIndex + 1);
398     return newTable;
399 }
400 
401 template<typename Derived>
GetAllKeysIntoVector(const JSThread * thread,std::vector<JSTaggedValue> & vector)402 void TaggedHashTable<Derived>::GetAllKeysIntoVector(const JSThread *thread, std::vector<JSTaggedValue> &vector) const
403 {
404     int capacity = Size();
405     for (int hashIndex = 0; hashIndex < capacity; hashIndex++) {
406         JSTaggedValue key = GetKey(hashIndex);
407         if (!key.IsUndefined() && !key.IsHole()) {
408             vector.push_back(key);
409         }
410     }
411 }
412 
413 template<typename Derived>
Remove(const JSThread * thread,const JSHandle<Derived> & table,int entry)414 JSHandle<Derived> OrderTaggedHashTable<Derived>::Remove(const JSThread *thread, const JSHandle<Derived> &table,
415                                                         int entry)
416 {
417     if (!(table->IsKey(table->GetKey(entry)))) {
418         return table;
419     }
420     table->ClearEntry(thread, entry);
421     table->IncreaseHoleEntriesCount(thread);
422     return Shrink(thread, table);
423 }
424 
425 template<typename Derived>
NextEnumerationIndex(const JSThread * thread)426 int OrderTaggedHashTable<Derived>::NextEnumerationIndex(const JSThread *thread)
427 {
428     int index = GetNextEnumerationIndex();
429     auto table = Derived::Cast(this);
430 
431     if (!PropertyAttributes::IsValidIndex(index)) {
432         std::vector<int> indexOrder = GetEnumerationOrder();
433         int length = indexOrder.size();
434         for (int i = 0; i < length; i++) {
435             int oldIndex = indexOrder[i];
436             int enumIndex = PropertyAttributes::INTIAL_PROPERTY_INDEX + i;
437             PropertyAttributes attr = table->GetAttributes(oldIndex);
438             attr.SetDictionaryOrder(enumIndex);
439             table->SetAttributes(thread, oldIndex, attr);
440         }
441         index = PropertyAttributes::INTIAL_PROPERTY_INDEX + length;
442     }
443     return index;
444 }
445 
446 template<typename Derived>
GetEnumerationOrder()447 std::vector<int> OrderTaggedHashTable<Derived>::GetEnumerationOrder()
448 {
449     std::vector<int> result;
450     auto table = Derived::Cast(this);
451     int size = table->Size();
452     for (int i = 0; i < size; i++) {
453         if (table->IsKey(table->GetKey(i))) {
454             result.push_back(i);
455         }
456     }
457     std::sort(result.begin(), result.end(), [table](int a, int b) {
458         PropertyAttributes attrA = table->GetAttributes(a);
459         PropertyAttributes attrB = table->GetAttributes(b);
460         return attrA.GetDictionaryOrder() < attrB.GetDictionaryOrder();
461     });
462     return result;
463 }
464 }  // namespace panda::ecmascript
465 #endif  // ECMASCRIPT_TAGGED_HASH_TABLE_INL_H
466