1 /**
2 * Copyright (c) 2021-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 #include "file_format_version.h"
17 #include "file-inl.h"
18 #include "os/file.h"
19 #include "os/mem.h"
20 #include "os/filesystem.h"
21 #include "mem/mem.h"
22 #include "panda_cache.h"
23
24 #include "utils/hash.h"
25 #include "utils/logger.h"
26 #include "utils/utf.h"
27 #include "utils/span.h"
28 #include "zip_archive.h"
29 #include "trace/trace.h"
30 #include "securec.h"
31
32 #include <cerrno>
33 #include <cstring>
34
35 #include <algorithm>
36 #include <memory>
37 #include <string>
38 #include <variant>
39 #include <cstdio>
40 #include <map>
41 namespace ark::panda_file {
42
43 // NOLINTNEXTLINE(readability-identifier-naming, modernize-avoid-c-arrays)
44 const char *ARCHIVE_FILENAME = "classes.abc";
45 // NOLINTNEXTLINE(readability-identifier-naming, modernize-avoid-c-arrays)
46 const char *ARCHIVE_SPLIT = "!/";
47
48 const std::array<uint8_t, File::MAGIC_SIZE> File::MAGIC {'P', 'A', 'N', 'D', 'A', '\0', '\0', '\0'};
49
50 // Name anonymous maps for perfing tools finding symbol file correctly.
51 // NOLINTNEXTLINE(readability-identifier-naming, modernize-avoid-c-arrays)
52 const char *ANONMAPNAME_PERFIX = "panda-";
53
GetMode(panda_file::File::OpenMode openMode)54 os::file::Mode GetMode(panda_file::File::OpenMode openMode)
55 {
56 switch (openMode) {
57 case File::READ_ONLY: {
58 return os::file::Mode::READONLY;
59 }
60 case File::READ_WRITE: {
61 #ifdef PANDA_TARGET_WINDOWS
62 return os::file::Mode::READWRITE;
63 #else
64 return os::file::Mode::READONLY;
65 #endif
66 }
67 case File::WRITE_ONLY: {
68 return os::file::Mode::WRITEONLY;
69 }
70 default: {
71 break;
72 }
73 }
74
75 UNREACHABLE();
76 }
77
GetProt(panda_file::File::OpenMode mode)78 static uint32_t GetProt(panda_file::File::OpenMode mode)
79 {
80 uint32_t prot = os::mem::MMAP_PROT_READ;
81 if (mode == File::READ_WRITE) {
82 prot |= os::mem::MMAP_PROT_WRITE;
83 }
84 return prot;
85 }
86
87 class AnonMemSet {
88 public:
89 using MemNameSet = std::map<std::string, std::string>;
90 using InsertResult = std::map<std::string, std::string>::iterator;
91
GetInstance()92 static AnonMemSet &GetInstance()
93 {
94 static AnonMemSet anonMemSet;
95 return anonMemSet;
96 }
97
Insert(const std::string & fileName,const std::string & anonMemName)98 InsertResult Insert(const std::string &fileName, const std::string &anonMemName)
99 {
100 return memNameSet_.emplace(fileName, anonMemName).first;
101 }
102
Remove(const std::string & fileName)103 void Remove(const std::string &fileName)
104 {
105 auto it = memNameSet_.find(fileName);
106 if (it != memNameSet_.end()) {
107 memNameSet_.erase(it);
108 }
109 }
110
111 private:
112 MemNameSet memNameSet_;
113 };
114
OpenPandaFileOrZip(std::string_view location,panda_file::File::OpenMode openMode)115 std::unique_ptr<const File> OpenPandaFileOrZip(std::string_view location, panda_file::File::OpenMode openMode)
116 {
117 std::string_view archiveFilename = ARCHIVE_FILENAME;
118 std::size_t archiveSplitIndex = location.find(ARCHIVE_SPLIT);
119 if (archiveSplitIndex != std::string::npos) {
120 archiveFilename = location.substr(archiveSplitIndex + 2); // 2 - archive split size
121 location = location.substr(0, archiveSplitIndex);
122 }
123
124 return OpenPandaFile(location, archiveFilename, openMode);
125 }
126
127 // NOLINTNEXTLINE(google-runtime-references)
OpenPandaFileFromZipErrorHandler(ZipArchiveHandle & handle)128 void OpenPandaFileFromZipErrorHandler(ZipArchiveHandle &handle)
129 {
130 if (handle != nullptr) {
131 if (ark::CloseArchiveFile(handle) != ZIPARCHIVE_OK) {
132 LOG(ERROR, PANDAFILE) << "CloseArchiveFile failed!";
133 }
134 }
135 }
136
OpenPandaFileFromZipFile(ZipArchiveHandle & handle,std::string_view location,EntryFileStat & entry,std::string_view archiveName)137 std::unique_ptr<const panda_file::File> OpenPandaFileFromZipFile(ZipArchiveHandle &handle, std::string_view location,
138 EntryFileStat &entry, std::string_view archiveName)
139 {
140 uint32_t uncompressedLength = entry.GetUncompressedSize();
141 if (uncompressedLength == 0) {
142 LOG(ERROR, PANDAFILE) << "Panda file has zero length!";
143 return nullptr;
144 }
145
146 size_t sizeToMmap = AlignUp(uncompressedLength, ark::os::mem::GetPageSize());
147 void *mem = os::mem::MapRWAnonymousRaw(sizeToMmap, false);
148 if (mem == nullptr) {
149 LOG(ERROR, PANDAFILE) << "Can't mmap anonymous!";
150 return nullptr;
151 }
152 os::mem::BytePtr ptr(reinterpret_cast<std::byte *>(mem), sizeToMmap, os::mem::MmapDeleter);
153 std::stringstream ss;
154 ss << ANONMAPNAME_PERFIX << archiveName << " extracted in memory from " << location;
155 auto it = AnonMemSet::GetInstance().Insert(std::string(location), ss.str());
156 auto ret = os::mem::TagAnonymousMemory(reinterpret_cast<void *>(ptr.Get()), sizeToMmap, it->second.c_str());
157 if (ret.has_value()) {
158 LOG(ERROR, PANDAFILE) << "Can't tag mmap anonymous!";
159 return nullptr;
160 }
161
162 auto extractError = ExtractToMemory(handle, reinterpret_cast<uint8_t *>(ptr.Get()), sizeToMmap);
163 if (extractError != 0) {
164 LOG(ERROR, PANDAFILE) << "Can't extract!";
165 return nullptr;
166 }
167
168 os::mem::ConstBytePtr constPtr = ptr.ToConst();
169 return panda_file::File::OpenFromMemory(std::move(constPtr), location);
170 }
171
172 // CC-OFFNXT(G.FUN.01-CPP) solid logic
173 // NOLINTNEXTLINE(google-runtime-references)
HandleArchive(ZipArchiveHandle & handle,FILE * fp,std::string_view location,EntryFileStat & entry,std::string_view archiveFilename,panda_file::File::OpenMode openMode)174 std::unique_ptr<const panda_file::File> HandleArchive(ZipArchiveHandle &handle, FILE *fp, std::string_view location,
175 EntryFileStat &entry, std::string_view archiveFilename,
176 panda_file::File::OpenMode openMode)
177 {
178 std::unique_ptr<const panda_file::File> file;
179 // compressed or not 4 aligned, use anonymous memory
180 if (entry.IsCompressed() || (entry.GetOffset() & 0x3U) != 0) {
181 file = OpenPandaFileFromZipFile(handle, location, entry, archiveFilename);
182 } else {
183 LOG(INFO, PANDAFILE) << "Pandafile is uncompressed and 4 bytes aligned";
184 file = panda_file::File::OpenUncompressedArchive(fileno(fp), location, entry.GetUncompressedSize(),
185 entry.GetOffset(), openMode);
186 }
187 return file;
188 }
189
OpenZipPandaFile(FILE * fp,std::string_view location,std::string_view archiveFilename,panda_file::File::OpenMode openMode)190 static std::unique_ptr<const panda_file::File> OpenZipPandaFile(FILE *fp, std::string_view location,
191 std::string_view archiveFilename,
192 panda_file::File::OpenMode openMode)
193 {
194 // Open Zipfile and do the extraction.
195 ZipArchiveHandle zipfile = nullptr;
196 auto openError = OpenArchiveFile(zipfile, fp);
197 if (openError != ZIPARCHIVE_OK) {
198 LOG(ERROR, PANDAFILE) << "Can't open archive " << location;
199 return nullptr;
200 }
201 bool tryDefault = archiveFilename.empty();
202 if (!tryDefault) {
203 if (LocateFile(zipfile, archiveFilename.data()) != ZIPARCHIVE_OK) {
204 LOG(INFO, PANDAFILE) << "Can't find entry with name '" << archiveFilename << "', will try "
205 << ARCHIVE_FILENAME;
206 tryDefault = true;
207 }
208 }
209 if (tryDefault) {
210 if (LocateFile(zipfile, ARCHIVE_FILENAME) != ZIPARCHIVE_OK) {
211 OpenPandaFileFromZipErrorHandler(zipfile);
212 LOG(ERROR, PANDAFILE) << "Can't find entry with " << ARCHIVE_FILENAME;
213 fclose(fp);
214 return nullptr;
215 }
216 }
217
218 EntryFileStat entry = EntryFileStat();
219 if (GetCurrentFileInfo(zipfile, &entry) != ZIPARCHIVE_OK) {
220 OpenPandaFileFromZipErrorHandler(zipfile);
221 LOG(ERROR, PANDAFILE) << "GetCurrentFileInfo error";
222 return nullptr;
223 }
224 // check that file is not empty, otherwise crash at CloseArchiveFile
225 if (entry.GetUncompressedSize() == 0) {
226 OpenPandaFileFromZipErrorHandler(zipfile);
227 LOG(ERROR, PANDAFILE) << "Invalid panda file '" << (tryDefault ? ARCHIVE_FILENAME : archiveFilename) << "'";
228 return nullptr;
229 }
230 if (OpenCurrentFile(zipfile) != ZIPARCHIVE_OK) {
231 CloseCurrentFile(zipfile);
232 OpenPandaFileFromZipErrorHandler(zipfile);
233 LOG(ERROR, PANDAFILE) << "Can't OpenCurrentFile!";
234 return nullptr;
235 }
236 GetCurrentFileOffset(zipfile, &entry);
237 auto file = HandleArchive(zipfile, fp, location, entry, archiveFilename, openMode);
238 CloseCurrentFile(zipfile);
239 OpenPandaFileFromZipErrorHandler(zipfile);
240 return file;
241 }
242
OpenPandaFile(std::string_view location,std::string_view archiveFilename,panda_file::File::OpenMode openMode)243 std::unique_ptr<const panda_file::File> OpenPandaFile(std::string_view location, std::string_view archiveFilename,
244 panda_file::File::OpenMode openMode)
245 {
246 trace::ScopedTrace scopedTrace("Open panda file " + std::string(location));
247 uint32_t magic;
248
249 #ifdef PANDA_TARGET_WINDOWS
250 constexpr char const *MODE = "rb";
251 #else
252 constexpr char const *MODE = "rbe";
253 #endif
254
255 FILE *fp = fopen(std::string(location).c_str(), MODE);
256 if (fp == nullptr) {
257 LOG(ERROR, PANDAFILE) << "Can't fopen location: " << location;
258 return nullptr;
259 }
260 fseek(fp, 0, SEEK_SET);
261 if (fread(&magic, sizeof(magic), 1, fp) != 1) {
262 fclose(fp);
263 LOG(ERROR, PANDAFILE) << "Can't read from file!(magic) " << location;
264 return nullptr;
265 }
266 fseek(fp, 0, SEEK_SET);
267 std::unique_ptr<const panda_file::File> file;
268 if (IsZipMagic(magic)) {
269 file = OpenZipPandaFile(fp, location, archiveFilename, openMode);
270 } else {
271 file = panda_file::File::Open(location, openMode);
272 }
273 fclose(fp);
274 return file;
275 }
276
OpenPandaFileFromMemory(const void * buffer,size_t size,std::string tag)277 std::unique_ptr<const File> OpenPandaFileFromMemory(const void *buffer, size_t size, std::string tag)
278 {
279 size_t sizeToMmap = AlignUp(size, ark::os::mem::GetPageSize());
280 void *mem = os::mem::MapRWAnonymousRaw(sizeToMmap, false);
281 if (mem == nullptr) {
282 return nullptr;
283 }
284
285 if (memcpy_s(mem, sizeToMmap, buffer, size) != 0) {
286 PLOG(ERROR, PANDAFILE) << "Failed to copy buffer into mem'";
287 }
288
289 if (!tag.empty()) {
290 if (tag == "ArkTS Code") {
291 std::string memAddr = std::to_string(ToUintPtr(mem));
292 tag = tag + ":" + memAddr;
293 }
294 auto ret = os::mem::TagAnonymousMemory(mem, sizeToMmap, tag.c_str());
295 if (ret.has_value()) {
296 PLOG(ERROR, PANDAFILE) << "Can't tag mmap anonymous, errno: " << errno;
297 }
298 }
299
300 os::mem::ConstBytePtr ptr(reinterpret_cast<std::byte *>(mem), sizeToMmap, os::mem::MmapDeleter);
301 if (ptr.Get() == nullptr) {
302 PLOG(ERROR, PANDAFILE) << "Failed to open panda file from memory'";
303 return nullptr;
304 }
305 std::hash<void *> hash;
306 return panda_file::File::OpenFromMemory(std::move(ptr), std::to_string(hash(mem)));
307 }
308
309 class ClassIdxIterator {
310 public:
311 // NOLINTNEXTLINE(readability-identifier-naming)
312 using value_type = const uint8_t *;
313 // NOLINTNEXTLINE(readability-identifier-naming)
314 using difference_type = std::ptrdiff_t;
315 // NOLINTNEXTLINE(readability-identifier-naming)
316 using pointer = uint32_t *;
317 // NOLINTNEXTLINE(readability-identifier-naming)
318 using reference = uint32_t &;
319 // NOLINTNEXTLINE(readability-identifier-naming)
320 using iterator_category = std::random_access_iterator_tag;
321
ClassIdxIterator(const File & file,const Span<const uint32_t> & span,size_t idx)322 ClassIdxIterator(const File &file, const Span<const uint32_t> &span, size_t idx)
323 : file_(file), span_(span), idx_(idx)
324 {
325 }
326
327 ClassIdxIterator(const ClassIdxIterator &other) = default;
328 ClassIdxIterator(ClassIdxIterator &&other) = default;
329 ~ClassIdxIterator() = default;
330
operator =(const ClassIdxIterator & other)331 ClassIdxIterator &operator=(const ClassIdxIterator &other)
332 {
333 if (&other != this) {
334 idx_ = other.idx_;
335 }
336
337 return *this;
338 }
339
operator =(ClassIdxIterator && other)340 ClassIdxIterator &operator=(ClassIdxIterator &&other) noexcept
341 {
342 idx_ = other.idx_;
343 return *this;
344 }
345
operator +=(size_t n)346 ClassIdxIterator &operator+=(size_t n)
347 {
348 idx_ += n;
349 return *this;
350 }
351
operator -=(size_t n)352 ClassIdxIterator &operator-=(size_t n)
353 {
354 idx_ -= n;
355 return *this;
356 }
357
operator ++()358 ClassIdxIterator &operator++()
359 {
360 ++idx_;
361 return *this;
362 }
363
operator --()364 ClassIdxIterator &operator--()
365 {
366 --idx_;
367 return *this;
368 }
369
operator -(const ClassIdxIterator & other)370 difference_type operator-(const ClassIdxIterator &other)
371 {
372 return static_cast<difference_type>(idx_ - other.idx_);
373 }
374
operator *() const375 value_type operator*() const
376 {
377 uint32_t id = span_[idx_];
378 return file_.GetStringData(File::EntityId(id)).data;
379 }
380
IsValid() const381 bool IsValid() const
382 {
383 return idx_ < span_.Size();
384 }
385
GetId() const386 uint32_t GetId() const
387 {
388 return span_[idx_];
389 }
390
Begin(const File & file,const Span<const uint32_t> & span)391 static ClassIdxIterator Begin(const File &file, const Span<const uint32_t> &span)
392 {
393 return ClassIdxIterator(file, span, 0);
394 }
395
End(const File & file,const Span<const uint32_t> & span)396 static ClassIdxIterator End(const File &file, const Span<const uint32_t> &span)
397 {
398 return ClassIdxIterator(file, span, span.Size());
399 }
400
401 private:
402 const File &file_;
403 const Span<const uint32_t> &span_;
404 size_t idx_;
405 };
406
File(std::string filename,os::mem::ConstBytePtr && base)407 File::File(std::string filename, os::mem::ConstBytePtr &&base)
408 : base_(std::forward<os::mem::ConstBytePtr>(base)),
409 filename_(std::move(filename)),
410 filenameHash_(CalcFilenameHash(filename_)),
411 fullFilename_(os::GetAbsolutePath(filename_)),
412 pandaCache_(std::make_unique<PandaCache>()),
413 uniqId_(MergeHashes(filenameHash_, GetHash32(reinterpret_cast<const uint8_t *>(GetHeader()), sizeof(Header))))
414 {
415 }
416
~File()417 File::~File()
418 {
419 AnonMemSet::GetInstance().Remove(filename_);
420 }
421
VersionToString(const std::array<uint8_t,File::VERSION_SIZE> & array)422 inline std::string VersionToString(const std::array<uint8_t, File::VERSION_SIZE> &array)
423 {
424 std::stringstream ss;
425
426 for (size_t i = 0; i < File::VERSION_SIZE - 1; ++i) {
427 ss << static_cast<int>(array[i]);
428 ss << ".";
429 }
430 ss << static_cast<int>(array[File::VERSION_SIZE - 1]);
431
432 return ss.str();
433 }
434
435 // We can't use default std::array's comparision operators and need to implement
436 // own ones due to the bug in gcc: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=95189
CompareVersions(const std::array<uint8_t,File::VERSION_SIZE> & lhs,const std::array<uint8_t,File::VERSION_SIZE> & rhs)437 inline int CompareVersions(const std::array<uint8_t, File::VERSION_SIZE> &lhs,
438 const std::array<uint8_t, File::VERSION_SIZE> &rhs)
439 {
440 for (size_t i = 0; i < File::VERSION_SIZE; i++) {
441 if (lhs[i] == rhs[i]) {
442 continue;
443 }
444 return lhs[i] - rhs[i];
445 }
446 return 0;
447 }
448
operator <(const std::array<uint8_t,File::VERSION_SIZE> & lhs,const std::array<uint8_t,File::VERSION_SIZE> & rhs)449 inline bool operator<(const std::array<uint8_t, File::VERSION_SIZE> &lhs,
450 const std::array<uint8_t, File::VERSION_SIZE> &rhs)
451 {
452 return CompareVersions(lhs, rhs) < 0;
453 }
454
operator >(const std::array<uint8_t,File::VERSION_SIZE> & lhs,const std::array<uint8_t,File::VERSION_SIZE> & rhs)455 inline bool operator>(const std::array<uint8_t, File::VERSION_SIZE> &lhs,
456 const std::array<uint8_t, File::VERSION_SIZE> &rhs)
457 {
458 return CompareVersions(lhs, rhs) > 0;
459 }
460
461 /* static */
Open(std::string_view filename,OpenMode openMode)462 std::unique_ptr<const File> File::Open(std::string_view filename, OpenMode openMode)
463 {
464 trace::ScopedTrace scopedTrace("Open panda file " + std::string(filename));
465 os::file::Mode mode = GetMode(openMode);
466 os::file::File file = os::file::Open(filename, mode);
467 if (!file.IsValid()) {
468 PLOG(ERROR, PANDAFILE) << "Failed to open panda file '" << filename << "'";
469 return nullptr;
470 }
471
472 os::file::FileHolder fhHolder(file);
473
474 auto res = file.GetFileSize();
475 if (!res) {
476 PLOG(ERROR, PANDAFILE) << "Failed to get size of panda file '" << filename << "'";
477 return nullptr;
478 }
479
480 size_t size = res.Value();
481 if (size < sizeof(File::Header)) {
482 LOG(ERROR, PANDAFILE) << "Invalid panda file '" << filename << "' - has not header";
483 return nullptr;
484 }
485
486 os::mem::ConstBytePtr ptr = os::mem::MapFile(file, GetProt(openMode), os::mem::MMAP_FLAG_PRIVATE, size).ToConst();
487 if (ptr.Get() == nullptr) {
488 PLOG(ERROR, PANDAFILE) << "Failed to map panda file '" << filename << "'";
489 return nullptr;
490 }
491
492 if (!CheckHeader(ptr, filename)) {
493 return nullptr;
494 }
495
496 // CC-OFFNXT(G.RES.09-CPP) ctor of class is private
497 return std::unique_ptr<File>(new File(filename.data(), std::move(ptr)));
498 }
499
OpenUncompressedArchive(int fd,const std::string_view & filename,size_t size,uint32_t offset,OpenMode openMode)500 std::unique_ptr<const File> File::OpenUncompressedArchive(int fd, const std::string_view &filename, size_t size,
501 uint32_t offset, OpenMode openMode)
502 {
503 trace::ScopedTrace scopedTrace("Open panda file " + std::string(filename));
504 auto file = os::file::File(fd);
505 if (!file.IsValid()) {
506 PLOG(ERROR, PANDAFILE) << "OpenUncompressedArchive: Failed to open panda file '" << filename << "'";
507 return nullptr;
508 }
509
510 if (size < sizeof(File::Header)) {
511 LOG(ERROR, PANDAFILE) << "Invalid panda file size '" << filename << "'";
512 return nullptr;
513 }
514 LOG(DEBUG, PANDAFILE) << " size=" << size << " offset=" << offset << " " << filename;
515
516 os::mem::ConstBytePtr ptr =
517 os::mem::MapFile(file, GetProt(openMode), os::mem::MMAP_FLAG_PRIVATE, size, offset).ToConst();
518 if (ptr.Get() == nullptr) {
519 PLOG(ERROR, PANDAFILE) << "Failed to map panda file '" << filename << "'";
520 return nullptr;
521 }
522 if (!CheckHeader(ptr, filename)) {
523 return nullptr;
524 }
525
526 // CC-OFFNXT(G.RES.09-CPP) ctor of class is private
527 return std::unique_ptr<File>(new File(filename.data(), std::move(ptr)));
528 }
529
CheckHeader(const os::mem::ConstBytePtr & ptr,const std::string_view & filename)530 bool CheckHeader(const os::mem::ConstBytePtr &ptr, const std::string_view &filename)
531 {
532 if (ptr.Get() == nullptr || ptr.GetSize() < sizeof(File::Header)) {
533 LOG(ERROR, PANDAFILE) << "Invalid panda file '" << filename << "'";
534 return false;
535 }
536 auto header = reinterpret_cast<const File::Header *>(reinterpret_cast<uintptr_t>(ptr.Get()));
537 if (header->magic != File::MAGIC) {
538 LOG(ERROR, PANDAFILE) << "Invalid magic number '";
539 return false;
540 }
541
542 auto fileVersion = header->version;
543
544 if (fileVersion < MIN_VERSION || fileVersion > VERSION) {
545 LOG(ERROR, PANDAFILE) << "Unable to open file '" << filename << "' with bytecode version "
546 << VersionToString(fileVersion);
547 if (fileVersion < MIN_VERSION) {
548 LOG(ERROR, PANDAFILE) << "Minimum supported version is " << VersionToString(MIN_VERSION);
549 } else {
550 LOG(ERROR, PANDAFILE) << "Maximum supported version is " << VersionToString(VERSION);
551 }
552 return false;
553 }
554
555 return true;
556 }
557
558 /* static */
OpenFromMemory(os::mem::ConstBytePtr && ptr)559 std::unique_ptr<const File> File::OpenFromMemory(os::mem::ConstBytePtr &&ptr)
560 {
561 if (!CheckHeader(ptr, std::string_view())) {
562 return nullptr;
563 }
564
565 // CC-OFFNXT(G.RES.09-CPP) ctor of class is private
566 return std::unique_ptr<File>(new File("", std::forward<os::mem::ConstBytePtr>(ptr)));
567 }
568
569 /* static */
OpenFromMemory(os::mem::ConstBytePtr && ptr,std::string_view filename)570 std::unique_ptr<const File> File::OpenFromMemory(os::mem::ConstBytePtr &&ptr, std::string_view filename)
571 {
572 trace::ScopedTrace scopedTrace("Open panda file from RAM " + std::string(filename));
573
574 if (!CheckHeader(ptr, filename)) {
575 return nullptr;
576 }
577
578 // CC-OFFNXT(G.RES.09-CPP) ctor of class is private
579 return std::unique_ptr<File>(new File(filename.data(), std::forward<os::mem::ConstBytePtr>(ptr)));
580 }
581
GetClassId(const uint8_t * mutf8Name) const582 File::EntityId File::GetClassId(const uint8_t *mutf8Name) const
583 {
584 auto classHashTable = GetClassHashTable();
585 if (!classHashTable.empty()) {
586 return GetClassIdFromClassHashTable(mutf8Name);
587 }
588
589 auto classIdx = GetClasses();
590
591 auto it = std::lower_bound(ClassIdxIterator::Begin(*this, classIdx), ClassIdxIterator::End(*this, classIdx),
592 mutf8Name, utf::Mutf8Less());
593 if (!it.IsValid()) {
594 return EntityId();
595 }
596
597 if (utf::CompareMUtf8ToMUtf8(mutf8Name, *it) == 0) {
598 return EntityId(it.GetId());
599 }
600
601 return EntityId();
602 }
603
CalcFilenameHash(const std::string & filename)604 uint32_t File::CalcFilenameHash(const std::string &filename)
605 {
606 return GetHash32String(reinterpret_cast<const uint8_t *>(filename.c_str()));
607 }
608
GetLiteralArraysId() const609 File::EntityId File::GetLiteralArraysId() const
610 {
611 const Header *header = GetHeader();
612 return EntityId(header->literalarrayIdxOff);
613 }
614
GetClassIdFromClassHashTable(const uint8_t * mutf8Name) const615 File::EntityId File::GetClassIdFromClassHashTable(const uint8_t *mutf8Name) const
616 {
617 auto classHashTable = GetClassHashTable();
618 auto hash = GetHash32String(mutf8Name);
619 auto pos = hash & (classHashTable.size() - 1);
620 auto entityPair = &classHashTable[pos];
621
622 if (entityPair->descriptorHash % classHashTable.size() != pos) {
623 return File::EntityId();
624 }
625
626 while (true) {
627 if (hash == entityPair->descriptorHash) {
628 auto entityId = File::EntityId(entityPair->entityIdOffset);
629 auto descriptor = GetStringData(entityId).data;
630 if (entityId.IsValid() && utf::CompareMUtf8ToMUtf8(descriptor, mutf8Name) == 0) {
631 return entityId;
632 }
633 }
634 if (entityPair->nextPos == 0) {
635 break;
636 }
637 entityPair = &classHashTable[entityPair->nextPos - 1];
638 }
639
640 return File::EntityId();
641 }
642
643 } // namespace ark::panda_file
644