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_PGO_PROFILER_AP_FILE_POOL_TEMPLATE_H
17#define ECMASCRIPT_PGO_PROFILER_AP_FILE_POOL_TEMPLATE_H
18
19#include <cstdint>
20#include <fstream>
21#include <unordered_map>
22#include <utility>
23
24#include "ecmascript/common.h"
25#include "ecmascript/jspandafile/js_pandafile.h"
26#include "ecmascript/log_wrapper.h"
27#include "ecmascript/mem/c_string.h"
28#include "ecmascript/pgo_profiler/ap_file/pgo_file_info.h"
29#include "ecmascript/pgo_profiler/pgo_utils.h"
30#include "macros.h"
31
32namespace panda::ecmascript::pgo {
33template <typename Entry, typename V>
34class PoolTemplate : public PGOFileSectionInterface {
35public:
36    using IsReservedCb = std::function<bool(const V &value)>;
37    using GetReservedIdCb = std::function<ApEntityId(const V &value)>;
38
39    using SupportCb = std::function<bool(PGOProfilerHeader const *header)>;
40    using GetSectionCb = std::function<SectionInfo *(PGOProfilerHeader const *header)>;
41
42    PoolTemplate(std::string poolName, uint32_t reservedCount)
43        : poolName_(std::move(poolName)), RESERVED_COUNT(reservedCount) {};
44
45    ~PoolTemplate() override
46    {
47        Clear();
48    }
49
50    bool TryAdd(const V &value, ApEntityId &entryId)
51    {
52        auto it = valueToId_.find(value);
53        if (it != valueToId_.end()) {
54            entryId = it->second;
55            return true;
56        }
57
58        entryId = ApEntityId(IsReserved(value) ? (++reservedUsed_, GetReservedId(value))
59                                               : RESERVED_COUNT + pool_.size() - reservedUsed_);
60
61        auto result = pool_.emplace(entryId, value);
62        valueToId_.emplace(value, entryId);
63        auto &entry = result.first->second;
64        entry.SetEntryId(entryId);
65        return true;
66    }
67
68    bool GetEntryId(const V &value, ApEntityId &entryId) const
69    {
70        for (const auto &entry : pool_) {
71            if (entry.second.GetData() == value) {
72                entryId = entry.first;
73                return true;
74            }
75        }
76        return false;
77    }
78
79    const Entry *GetEntry(ApEntityId id) const
80    {
81        auto iter = pool_.find(id);
82        if (iter == pool_.end()) {
83            return nullptr;
84        }
85        return &(iter->second);
86    }
87
88    bool GetEntryIdByNormalizedName(const V &value, ApEntityId &entryId) const
89    {
90        for (const auto &entry : pool_) {
91            if (JSPandaFile::GetNormalizedFileDesc(entry.second.GetData()) == value) {
92                entryId = entry.first;
93                return true;
94            }
95        }
96        return false;
97    }
98
99    void Clear()
100    {
101        pool_.clear();
102        valueToId_.clear();
103        reservedUsed_ = 0;
104    }
105
106    bool Empty() const
107    {
108        return pool_.empty();
109    }
110
111    void Merge(const PoolTemplate &pool, const std::function<void(ApEntityId, ApEntityId)> &callback)
112    {
113        for (const auto &entry : pool.pool_) {
114            ApEntityId newId(0);
115            TryAdd(entry.second.GetData(), newId);
116            if (callback != nullptr) {
117                callback(entry.first, newId);
118            }
119        }
120    }
121
122    uint32_t ProcessToBinary([[maybe_unused]] PGOContext &context, std::fstream &stream) override
123    {
124        LOG_ECMA(DEBUG) << "ProcessToBinary. name: " << poolName_ << ", count: " << pool_.size();
125        SectionInfo secInfo;
126        secInfo.number_ = pool_.size();
127        secInfo.offset_ = sizeof(SectionInfo);
128        auto secInfoPos = stream.tellp();
129        stream.seekp(secInfo.offset_, std::ofstream::cur);
130        for (auto &entry : pool_) {
131            stream.write(reinterpret_cast<const char *>(&(entry.first)), sizeof(ApEntityId));
132            entry.second.ProcessToBinary(context, stream);
133        }
134        secInfo.size_ = static_cast<uint32_t>(stream.tellp()) - static_cast<uint32_t>(secInfoPos);
135        auto tail = stream.tellp();
136        stream.seekp(secInfoPos, std::ofstream::beg);
137        stream.write(reinterpret_cast<const char *>(&(secInfo)), sizeof(SectionInfo));
138        stream.seekp(tail, std::ofstream::beg);
139        return 1;
140    }
141
142    bool ProcessToText(std::ofstream &stream) override
143    {
144        bool isFirst = true;
145        for (auto &entry : pool_) {
146            if (isFirst) {
147                stream << DumpUtils::NEW_LINE;
148                stream << poolName_;
149                stream << DumpUtils::BLOCK_START;
150                isFirst = false;
151            }
152            stream << DumpUtils::NEW_LINE;
153            stream << std::to_string(entry.first);
154            stream << DumpUtils::SPACE;
155            stream << DumpUtils::ARRAY_START;
156            entry.second.ProcessToText(stream);
157            stream << DumpUtils::ARRAY_END;
158        }
159        if (!isFirst) {
160            stream << (DumpUtils::SPACE + DumpUtils::NEW_LINE);
161        }
162        return true;
163    }
164
165    void ProcessToJson(std::vector<ProfileType::StringMap> &abcPoolArray)
166    {
167        for (auto &entry : pool_) {
168            ProfileType::StringMap abcPool;
169            abcPool.insert(std::make_pair(DumpJsonUtils::ABC_ID, std::to_string(entry.first)));
170            abcPool.insert(std::make_pair(DumpJsonUtils::ABC_FILE, entry.second.GetData()));
171            abcPoolArray.push_back(abcPool);
172        }
173    }
174    uint32_t ParseFromBinary([[maybe_unused]] PGOContext &context, void **buffer,
175                             PGOProfilerHeader const *header) override
176    {
177        auto secInfo = base::ReadBuffer<SectionInfo>(buffer);
178        for (uint32_t i = 0; i < secInfo.number_; i++) {
179            auto entryId = base::ReadBuffer<ApEntityId>(buffer, sizeof(ApEntityId));
180            auto result = pool_.try_emplace(entryId);
181            result.first->second.SetEntryId(entryId);
182            result.first->second.ParseFromBinary(context, buffer, header);
183        }
184        return 1;
185    }
186
187    void SetIsReservedCb(const IsReservedCb &isReservedCb)
188    {
189        isReservedCb_ = isReservedCb;
190    }
191
192    void SetGetReservedIdCb(const GetReservedIdCb &getReservedIdCb)
193    {
194        getReservedIdCb_ = getReservedIdCb;
195    }
196
197    void SetSupportCb(const SupportCb &supportCb)
198    {
199        supportCb_ = supportCb;
200    }
201
202    void SetGetSectionCb(const GetSectionCb &getSectionCb)
203    {
204        getSectionCb_ = getSectionCb;
205    }
206
207    std::unordered_map<ApEntityId, Entry> &GetPool()
208    {
209        return pool_;
210    }
211
212    std::unordered_map<V, ApEntityId> &GetValueToId()
213    {
214        return valueToId_;
215    }
216
217private:
218    NO_COPY_SEMANTIC(PoolTemplate);
219    NO_MOVE_SEMANTIC(PoolTemplate);
220
221    bool Support(PGOProfilerHeader const *header) const override
222    {
223        return supportCb_(header);
224    }
225
226    SectionInfo *GetSection(PGOProfilerHeader const *header) const override
227    {
228        return getSectionCb_(header);
229    }
230
231    bool IsReserved(const V &value)
232    {
233        return isReservedCb_(value);
234    }
235
236    ApEntityId GetReservedId(const V &value)
237    {
238        return getReservedIdCb_(value);
239    }
240
241    const std::string poolName_;
242    const uint32_t RESERVED_COUNT {};
243    uint32_t reservedUsed_ {0};
244
245    IsReservedCb isReservedCb_;
246    GetReservedIdCb getReservedIdCb_;
247    SupportCb supportCb_;
248    GetSectionCb getSectionCb_;
249    std::unordered_map<ApEntityId, Entry> pool_;
250    std::unordered_map<V, ApEntityId> valueToId_;
251};
252}  // namespace panda::ecmascript::pgo
253
254namespace std {
255using panda::ecmascript::pgo::ProfileType;
256template<>
257struct hash<ProfileType> {
258    size_t operator()(const ProfileType& type) const noexcept
259    {
260        return type.GetRaw();
261    }
262};
263} // namespace std
264#endif  // ECMASCRIPT_PGO_PROFILER_AP_FILE_POOL_TEMPLATE_H
265