1/*
2 * Copyright (c) 2023-2024 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_BASE_FAST_JSON_STRINGIFY_H
17#define ECMASCRIPT_BASE_FAST_JSON_STRINGIFY_H
18
19#include "ecmascript/ecma_context.h"
20#include "ecmascript/js_tagged_value.h"
21#include "ecmascript/js_handle.h"
22#include "ecmascript/object_factory.h"
23#include "ecmascript/global_env.h"
24#include "ecmascript/mem/c_containers.h"
25
26namespace panda::ecmascript::base {
27class FastJsonStringifier {
28public:
29    static constexpr int32_t INVALID_INDEX = -1;
30    static constexpr int32_t JSON_CACHE_MASK = 62;
31    static constexpr int32_t JSON_CACHE_SIZE = 64;
32    static constexpr int32_t CACHE_MINIMUN_SIZIE = 5;
33    FastJsonStringifier() = default;
34
35    explicit FastJsonStringifier(JSThread *thread) : thread_(thread) {}
36
37    ~FastJsonStringifier() = default;
38    NO_COPY_SEMANTIC(FastJsonStringifier);
39    NO_MOVE_SEMANTIC(FastJsonStringifier);
40
41    JSHandle<JSTaggedValue> Stringify(const JSHandle<JSTaggedValue> &value);
42
43private:
44    JSTaggedValue SerializeJSONProperty(const JSHandle<JSTaggedValue> &value);
45    JSTaggedValue GetSerializeValue(const JSHandle<JSTaggedValue> &key, const JSHandle<JSTaggedValue> &value);
46    CString SerializeObjectKey(const JSHandle<JSTaggedValue> &key, bool hasContent);
47
48    bool SerializeJSONObject(const JSHandle<JSTaggedValue> &value);
49
50    bool SerializeJSArray(const JSHandle<JSTaggedValue> &value);
51    bool SerializeJSProxy(const JSHandle<JSTaggedValue> &object);
52
53    void SerializePrimitiveRef(const JSHandle<JSTaggedValue> &primitiveRef);
54
55    bool PushValue(const JSHandle<JSTaggedValue> &value);
56
57    void PopValue();
58
59    bool AppendJsonString(bool hasContent, CVector<std::pair<CString, int>> &strCache, int index);
60    bool FastAppendJsonString(bool hasContent, CString &key);
61    bool TryCacheSerializeElements(const JSHandle<JSObject> &obj, bool hasContent,
62                                   CVector<std::pair<CString, int>> &strCache);
63    bool SerializeElementsWithCache(const JSHandle<JSObject> &obj, bool hasContent,
64                                    CVector<std::pair<CString, int>> &strCache, uint32_t &cacheIndex,
65                                    uint32_t elementSize);
66    bool TryCacheSerializeKeys(const JSHandle<JSObject> &obj, bool hasContent,
67                               CVector<std::pair<CString, int>> &strCache);
68    bool TryCacheSerializeKeysFromPropertiesArray(const JSHandle<JSObject> &obj, bool hasContent,
69                                                  CVector<std::pair<CString, int>> &strCache);
70    bool TryCacheSerializeKeysFromEnumCache(const JSHandle<JSObject> &obj, bool hasContent,
71                                            CVector<std::pair<CString, int>> &strCache);
72    bool TryCacheSerializeKeysFromGlobalObject(const JSHandle<JSObject> &obj, bool hasContent,
73                                               CVector<std::pair<CString, int>> &strCache);
74    bool TryCacheSerializeKeysFromNameDictionary(const JSHandle<JSObject> &obj, bool hasContent,
75                                                 CVector<std::pair<CString, int>> &strCache);
76    bool SerializeKeysWithCache(const JSHandle<JSObject> &obj, bool hasContent,
77                                CVector<std::pair<CString, int>> &strCache, uint32_t &cacheIndex);
78    bool AppendJsonString(bool hasContent);
79    bool DefaultSerializeKeys(const JSHandle<JSObject> &obj, bool hasContent);
80    bool SerializeKeysFromCache(const JSHandle<JSObject> &obj, JSTaggedValue enumCache,
81                                const JSHandle<TaggedArray> &propertiesArr, bool hasContent);
82    bool SerializeKeysFromLayout(const JSHandle<JSObject> &obj, const JSHandle<JSHClass> &jsHclass,
83                                 const JSHandle<TaggedArray> &propertiesArr, bool hasContent);
84    bool SerializeKeysFromGlobalDictionary(const JSHandle<JSObject> &obj,
85                                           const JSHandle<TaggedArray> &propertiesArr,
86                                           bool hasContent);
87    bool SerializeKeysFromNameDictionary(const JSHandle<JSObject> &obj,
88                                         const JSHandle<TaggedArray> &propertiesArr,
89                                         bool hasContent);
90    bool SerializeKeyValue(const JSHandle<JSObject> &obj, JSTaggedValue key,
91                           const JSHandle<TaggedArray> &propertiesArr, bool hasContent);
92    bool DefaultSerializeElements(const JSHandle<JSObject> &obj, bool hasContent);
93    bool DefaultSerializeObject(const JSTaggedValue &object, uint32_t numOfKeys, uint32_t numOfElements);
94
95    inline void EraseKeyString(CString &keyStr, bool hasContent)
96    {
97        size_t keyLength = keyStr.length() + (hasContent ? 1 : 0) + 1;
98        result_.erase(result_.end() - keyLength, result_.end());
99    }
100
101    inline void FastSerializeObjectKey(CString &key, bool hasContent)
102    {
103        if (hasContent) {
104            result_ += ",";
105        }
106
107        result_ += key;
108        result_ += ":";
109    }
110
111    inline int32_t FindCache(JSHClass *hclass, size_t numOfKeys)
112    {
113        size_t index = GetHash(hclass, numOfKeys);
114        JSTaggedValue cacheHclass = hclassCache_->Get(index);
115        if (cacheHclass != JSTaggedValue::Hole()) {
116            if (JSHClass::Cast(cacheHclass.GetTaggedObject()) == hclass) {
117                return index;
118            } else {
119                cacheHclass = hclassCache_->Get(++index);
120                if (JSHClass::Cast(cacheHclass.GetTaggedObject()) == hclass) {
121                    return index;
122                } else {
123                    return INVALID_INDEX;
124                }
125            }
126        }
127        return INVALID_INDEX;
128    }
129
130    inline void SetCache(JSHClass *hclass, size_t numOfKeys, CVector<std::pair<CString, int>> &value)
131    {
132        size_t index = GetHash(hclass, numOfKeys);
133        JSTaggedValue cacheHclass = hclassCache_->Get(index);
134        if (cacheHclass != JSTaggedValue::Hole()) {
135            cacheHclass = hclassCache_->Get(++index);
136            if (cacheHclass != JSTaggedValue::Hole()) {
137                --index;
138            }
139        }
140        hclassCache_->Set(thread_, index, JSTaggedValue(hclass));
141        thread_->GetCurrentEcmaContext()->SetJsonStringifyCache(index, value);
142    }
143
144    inline size_t GetHash(JSHClass *hclass, size_t numOfKeys)
145    {
146        uintptr_t ptr = reinterpret_cast<uintptr_t>(hclass);
147        size_t hash = (ptr + numOfKeys) & JSON_CACHE_MASK;
148        return hash;
149    }
150
151    CString result_;
152    JSThread *thread_ {nullptr};
153    ObjectFactory *factory_ {nullptr};
154    CVector<JSHandle<JSTaggedValue>> stack_;
155    JSMutableHandle<JSTaggedValue> handleKey_ {};
156    JSMutableHandle<JSTaggedValue> handleValue_ {};
157    bool cacheable_ {true};
158    JSHandle<TaggedArray> hclassCache_ {};
159};
160}  // namespace panda::ecmascript::basekey
161#endif  // ECMASCRIPT_BASE_FAST_JSON_STRINGIFY_H
162