• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-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 "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 panda::panda_file {
42 
43 // NOLINTNEXTLINE(readability-identifier-naming, modernize-avoid-c-arrays)
44 const char *g_archiveFilenames = "classes.abc";
45 // NOLINTNEXTLINE(readability-identifier-naming, modernize-avoid-c-arrays)
46 const char *g_archiveSplit = "!/";
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 *g_anonmapnamePerfix = "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 = g_archiveFilenames;
118     std::size_t archiveSplitIndex = location.find(g_archiveSplit);
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 (panda::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         CloseCurrentFile(handle);
143         OpenPandaFileFromZipErrorHandler(handle);
144         LOG(ERROR, PANDAFILE) << "Panda file has zero length!";
145         return nullptr;
146     }
147 
148     size_t sizeToMmap = AlignUp(uncompressedLength, panda::os::mem::GetPageSize());
149     void *mem = os::mem::MapRWAnonymousRaw(sizeToMmap, false);
150     if (mem == nullptr) {
151         CloseCurrentFile(handle);
152         OpenPandaFileFromZipErrorHandler(handle);
153         LOG(ERROR, PANDAFILE) << "Can't mmap anonymous!";
154         return nullptr;
155     }
156     os::mem::BytePtr ptr(reinterpret_cast<std::byte *>(mem), sizeToMmap, os::mem::MmapDeleter);
157     std::stringstream ss;
158     ss << g_anonmapnamePerfix << archiveName << " extracted in memory from " << location;
159     auto it = AnonMemSet::GetInstance().Insert(std::string(location), ss.str());
160     auto ret = os::mem::TagAnonymousMemory(reinterpret_cast<void *>(ptr.Get()), sizeToMmap, it->second.c_str());
161     if (ret.has_value()) {
162         CloseCurrentFile(handle);
163         OpenPandaFileFromZipErrorHandler(handle);
164         LOG(ERROR, PANDAFILE) << "Can't tag mmap anonymous!";
165         return nullptr;
166     }
167 
168     auto extractError = ExtractToMemory(handle, reinterpret_cast<uint8_t *>(ptr.Get()), sizeToMmap);
169     if (extractError != 0) {
170         CloseCurrentFile(handle);
171         OpenPandaFileFromZipErrorHandler(handle);
172         LOG(ERROR, PANDAFILE) << "Can't extract!";
173         return nullptr;
174     }
175 
176     os::mem::ConstBytePtr constPtr = ptr.ToConst();
177     return panda_file::File::OpenFromMemory(std::move(constPtr), location);
178 }
179 
180 // NOLINTNEXTLINE(google-runtime-references)
HandleArchive(ZipArchiveHandle & handle,FILE * fp,std::string_view location,EntryFileStat & entry,std::string_view archiveFilename,panda_file::File::OpenMode openMode)181 std::unique_ptr<const panda_file::File> HandleArchive(ZipArchiveHandle &handle, FILE *fp, std::string_view location,
182                                                       EntryFileStat &entry, std::string_view archiveFilename,
183                                                       panda_file::File::OpenMode openMode)
184 {
185     std::unique_ptr<const panda_file::File> file;
186     // compressed or not 4 aligned, use anonymous memory
187     if (entry.IsCompressed() || (entry.GetOffset() & 0x3U) != 0) {
188         file = OpenPandaFileFromZipFile(handle, location, entry, archiveFilename);
189     } else {
190         LOG(INFO, PANDAFILE) << "Pandafile is uncompressed and 4 bytes aligned";
191         file = panda_file::File::OpenUncompressedArchive(fileno(fp), location, entry.GetUncompressedSize(),
192                                                          entry.GetOffset(), openMode);
193     }
194     return file;
195 }
196 
OpenPandaFile(std::string_view location,std::string_view archiveFilename,panda_file::File::OpenMode openMode)197 std::unique_ptr<const panda_file::File> OpenPandaFile(std::string_view location, std::string_view archiveFilename,
198                                                       panda_file::File::OpenMode openMode)
199 {
200     trace::ScopedTrace scopedTrace("Open panda file " + std::string(location));
201     uint32_t magic;
202 
203 #ifdef PANDA_TARGET_WINDOWS
204     constexpr char const *mode = "rb";
205 #else
206     constexpr char const *mode = "rbe";
207 #endif
208 
209     FILE *fp = fopen(std::string(location).c_str(), mode);
210     if (fp == nullptr) {
211         LOG(ERROR, PANDAFILE) << "Can't fopen location: " << location;
212         return nullptr;
213     }
214     if (fseek(fp, 0, SEEK_SET) != 0) {
215         fclose(fp);
216         LOG(ERROR, PANDAFILE) << "Can't fseek location: " << location;
217         return nullptr;
218     }
219     if (fread(&magic, sizeof(magic), 1, fp) != 1) {
220         fclose(fp);
221         LOG(ERROR, PANDAFILE) << "Can't read from file!(magic) " << location;
222         return nullptr;
223     }
224     if (fseek(fp, 0, SEEK_SET) != 0) {
225         fclose(fp);
226         LOG(ERROR, PANDAFILE) << "Can't fseek location: " << location;
227         return nullptr;
228     }
229     std::unique_ptr<const panda_file::File> file;
230     if (IsZipMagic(magic)) {
231         // Open Zipfile and do the extraction.
232         ZipArchiveHandle zipfile = nullptr;
233         auto openError = OpenArchiveFile(zipfile, fp);
234         if (openError != ZIPARCHIVE_OK) {
235             LOG(ERROR, PANDAFILE) << "Can't open archive " << location;
236             return nullptr;
237         }
238         bool tryDefault = archiveFilename.empty();
239         if (!tryDefault) {
240             if (LocateFile(zipfile, archiveFilename.data()) != ZIPARCHIVE_OK) {
241                 LOG(INFO, PANDAFILE) << "Can't find entry with name '" << archiveFilename << "', will try "
242                                      << g_archiveFilenames;
243                 tryDefault = true;
244             }
245         }
246         if (tryDefault) {
247             if (LocateFile(zipfile, g_archiveFilenames) != ZIPARCHIVE_OK) {
248                 OpenPandaFileFromZipErrorHandler(zipfile);
249                 LOG(ERROR, PANDAFILE) << "Can't find entry with " << g_archiveFilenames;
250                 fclose(fp);
251                 return nullptr;
252             }
253         }
254 
255         EntryFileStat entry = EntryFileStat();
256         if (GetCurrentFileInfo(zipfile, &entry) != ZIPARCHIVE_OK) {
257             OpenPandaFileFromZipErrorHandler(zipfile);
258             LOG(ERROR, PANDAFILE) << "GetCurrentFileInfo error";
259             return nullptr;
260         }
261         // check that file is not empty, otherwise crash at CloseArchiveFile
262         if (entry.GetUncompressedSize() == 0) {
263             OpenPandaFileFromZipErrorHandler(zipfile);
264             const auto &filename = (tryDefault ? g_archiveFilenames : archiveFilename);
265             LOG(ERROR, PANDAFILE) << "Invalid panda file '" << filename << "'";
266             return nullptr;
267         }
268         if (OpenCurrentFile(zipfile) != ZIPARCHIVE_OK) {
269             CloseCurrentFile(zipfile);
270             OpenPandaFileFromZipErrorHandler(zipfile);
271             LOG(ERROR, PANDAFILE) << "Can't OpenCurrentFile!";
272             return nullptr;
273         }
274         GetCurrentFileOffset(zipfile, &entry);
275         file = HandleArchive(zipfile, fp, location, entry, archiveFilename, openMode);
276         CloseCurrentFile(zipfile);
277         if (panda::CloseArchiveFile(zipfile) != 0) {
278             LOG(ERROR, PANDAFILE) << "CloseArchive failed!";
279             return nullptr;
280         }
281     } else {
282         file = panda_file::File::Open(location, openMode);
283     }
284     fclose(fp);
285     return file;
286 }
287 
OpenPandaFileFromMemory(const void * buffer,size_t size)288 std::unique_ptr<const File> OpenPandaFileFromMemory(const void *buffer, size_t size)
289 {
290     size_t sizeToMmap = AlignUp(size, panda::os::mem::GetPageSize());
291     void *mem = os::mem::MapRWAnonymousRaw(sizeToMmap, false);
292     if (mem == nullptr) {
293         return nullptr;
294     }
295 
296     if (memcpy_s(mem, sizeToMmap, buffer, size) != 0) {
297         PLOG(ERROR, PANDAFILE) << "Failed to copy buffer into mem'";
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 
306     std::hash<void *> hash;
307     return panda_file::File::OpenFromMemory(std::move(ptr), std::to_string(hash(mem)));
308 }
309 
310 class ClassIdxIterator {
311 public:
312     // NOLINTNEXTLINE(readability-identifier-naming)
313     using value_type = const uint8_t *;
314     // NOLINTNEXTLINE(readability-identifier-naming)
315     using difference_type = std::ptrdiff_t;
316     // NOLINTNEXTLINE(readability-identifier-naming)
317     using pointer = uint32_t *;
318     // NOLINTNEXTLINE(readability-identifier-naming)
319     using reference = uint32_t &;
320     // NOLINTNEXTLINE(readability-identifier-naming)
321     using iterator_category = std::random_access_iterator_tag;
322 
ClassIdxIterator(const File & file,const Span<const uint32_t> & span,size_t idx)323     ClassIdxIterator(const File &file, const Span<const uint32_t> &span, size_t idx)
324         : file_(file), span_(span), idx_(idx)
325     {
326     }
327 
328     ClassIdxIterator(const ClassIdxIterator &other) = default;
329     ClassIdxIterator(ClassIdxIterator &&other) = default;
330     ~ClassIdxIterator() = default;
331 
operator =(const ClassIdxIterator & other)332     ClassIdxIterator &operator=(const ClassIdxIterator &other)
333     {
334         if (&other != this) {
335             idx_ = other.idx_;
336         }
337 
338         return *this;
339     }
340 
operator =(ClassIdxIterator && other)341     ClassIdxIterator &operator=(ClassIdxIterator &&other) noexcept
342     {
343         idx_ = other.idx_;
344         return *this;
345     }
346 
operator +=(size_t n)347     ClassIdxIterator &operator+=(size_t n)
348     {
349         idx_ += n;
350         return *this;
351     }
352 
operator -=(size_t n)353     ClassIdxIterator &operator-=(size_t n)
354     {
355         idx_ -= n;
356         return *this;
357     }
358 
operator ++()359     ClassIdxIterator &operator++()
360     {
361         ++idx_;
362         return *this;
363     }
364 
operator --()365     ClassIdxIterator &operator--()
366     {
367         --idx_;
368         return *this;
369     }
370 
operator -(const ClassIdxIterator & other)371     difference_type operator-(const ClassIdxIterator &other)
372     {
373         return static_cast<difference_type>(idx_ - other.idx_);
374     }
375 
operator *() const376     value_type operator*() const
377     {
378         uint32_t id = span_[idx_];
379         return file_.GetStringData(File::EntityId(id)).data;
380     }
381 
IsValid() const382     bool IsValid() const
383     {
384         return idx_ < span_.Size();
385     }
386 
GetId() const387     uint32_t GetId() const
388     {
389         return span_[idx_];
390     }
391 
Begin(const File & file,const Span<const uint32_t> & span)392     static ClassIdxIterator Begin(const File &file, const Span<const uint32_t> &span)
393     {
394         return ClassIdxIterator(file, span, 0);
395     }
396 
End(const File & file,const Span<const uint32_t> & span)397     static ClassIdxIterator End(const File &file, const Span<const uint32_t> &span)
398     {
399         return ClassIdxIterator(file, span, span.Size());
400     }
401 
402 private:
403     const File &file_;
404     const Span<const uint32_t> &span_;
405     size_t idx_;
406 };
407 
File(std::string filename,os::mem::ConstBytePtr && base)408 File::File(std::string filename, os::mem::ConstBytePtr &&base)
409     : base_(std::forward<os::mem::ConstBytePtr>(base)),
410       filename_(std::move(filename)),
411       filenameHash_(CalcFilenameHash(filename_)),
412       fullFilename_(os::GetAbsolutePath(filename_)),
413       pandaCache_(std::make_unique<PandaCache>()),
414       uniqId_(MergeHashes(filenameHash_, GetHash32(reinterpret_cast<const uint8_t *>(GetHeader()), sizeof(Header))))
415 {
416 }
417 
~File()418 File::~File()
419 {
420     AnonMemSet::GetInstance().Remove(filename_);
421 }
422 
VersionToString(const std::array<uint8_t,File::VERSION_SIZE> & array)423 inline std::string VersionToString(const std::array<uint8_t, File::VERSION_SIZE> &array)
424 {
425     std::stringstream ss;
426 
427     for (size_t i = 0; i < File::VERSION_SIZE - 1; ++i) {
428         ss << static_cast<int>(array[i]);
429         ss << ".";
430     }
431     ss << static_cast<int>(array[File::VERSION_SIZE - 1]);
432 
433     return ss.str();
434 }
435 
436 // We can't use default std::array's comparision operators and need to implement
437 // 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)438 inline int CompareVersions(const std::array<uint8_t, File::VERSION_SIZE> &lhs,
439                            const std::array<uint8_t, File::VERSION_SIZE> &rhs)
440 {
441     for (size_t i = 0; i < File::VERSION_SIZE; i++) {
442         if (lhs[i] == rhs[i]) {
443             continue;
444         }
445         return lhs[i] - rhs[i];
446     }
447     return 0;
448 }
449 
operator <(const std::array<uint8_t,File::VERSION_SIZE> & lhs,const std::array<uint8_t,File::VERSION_SIZE> & rhs)450 inline bool operator<(const std::array<uint8_t, File::VERSION_SIZE> &lhs,
451                       const std::array<uint8_t, File::VERSION_SIZE> &rhs)
452 {
453     return CompareVersions(lhs, rhs) < 0;
454 }
455 
operator >(const std::array<uint8_t,File::VERSION_SIZE> & lhs,const std::array<uint8_t,File::VERSION_SIZE> & rhs)456 inline bool operator>(const std::array<uint8_t, File::VERSION_SIZE> &lhs,
457                       const std::array<uint8_t, File::VERSION_SIZE> &rhs)
458 {
459     return CompareVersions(lhs, rhs) > 0;
460 }
461 
462 /* static */
Open(std::string_view filename,OpenMode openMode)463 std::unique_ptr<const File> File::Open(std::string_view filename, OpenMode openMode)
464 {
465     trace::ScopedTrace scopedTrace("Open panda file " + std::string(filename));
466     os::file::Mode mode = GetMode(openMode);
467     os::file::File file = os::file::Open(filename, mode);
468     if (!file.IsValid()) {
469         PLOG(ERROR, PANDAFILE) << "Failed to open panda file '" << filename << "'";
470         return nullptr;
471     }
472 
473     os::file::FileHolder fhHolder(file);
474 
475     auto res = file.GetFileSize();
476 
477     if (!res) {
478         PLOG(ERROR, PANDAFILE) << "Failed to get size of panda file '" << filename << "'";
479         return nullptr;
480     }
481 
482     size_t size = res.Value();
483 
484     if (size < sizeof(File::Header)) {
485         LOG(ERROR, PANDAFILE) << "Invalid panda file '" << filename << "' - has not header";
486         return nullptr;
487     }
488 
489     os::mem::ConstBytePtr ptr = os::mem::MapFile(file, GetProt(openMode), os::mem::MMAP_FLAG_PRIVATE, size).ToConst();
490     if (ptr.Get() == nullptr) {
491         PLOG(ERROR, PANDAFILE) << "Failed to map panda file '" << filename << "'";
492         return nullptr;
493     }
494 
495     if (!CheckHeader(ptr, filename)) {
496         return nullptr;
497     }
498 
499     return std::unique_ptr<File>(new File(filename.data(), std::move(ptr)));
500 }
501 
OpenUncompressedArchive(int fd,const std::string_view & filename,size_t size,uint32_t offset,OpenMode openMode)502 std::unique_ptr<const File> File::OpenUncompressedArchive(int fd, const std::string_view &filename, size_t size,
503                                                           uint32_t offset, OpenMode openMode)
504 {
505     trace::ScopedTrace scopedTrace("Open panda file " + std::string(filename));
506     auto file = os::file::File(fd);
507     if (!file.IsValid()) {
508         PLOG(ERROR, PANDAFILE) << "OpenUncompressedArchive: Failed to open panda file '" << filename << "'";
509         return nullptr;
510     }
511 
512     if (size < sizeof(File::Header)) {
513         LOG(ERROR, PANDAFILE) << "Invalid panda file size '" << filename << "'";
514         return nullptr;
515     }
516     LOG(DEBUG, PANDAFILE) << " size=" << size << " offset=" << offset << " " << filename;
517 
518     os::mem::ConstBytePtr ptr =
519         os::mem::MapFile(file, GetProt(openMode), os::mem::MMAP_FLAG_PRIVATE, size, offset).ToConst();
520     if (ptr.Get() == nullptr) {
521         PLOG(ERROR, PANDAFILE) << "Failed to map panda file '" << filename << "'";
522         return nullptr;
523     }
524     if (!CheckHeader(ptr, filename)) {
525         return nullptr;
526     }
527 
528     return std::unique_ptr<File>(new File(filename.data(), std::move(ptr)));
529 }
530 
CheckHeader(const os::mem::ConstBytePtr & ptr,const std::string_view & filename)531 bool CheckHeader(const os::mem::ConstBytePtr &ptr, const std::string_view &filename)
532 {
533     if (ptr.Get() == nullptr || ptr.GetSize() < sizeof(File::Header)) {
534         LOG(ERROR, PANDAFILE) << "Invalid panda file '" << filename << "'";
535         return false;
536     }
537     auto header = reinterpret_cast<const File::Header *>(reinterpret_cast<uintptr_t>(ptr.Get()));
538     if (header->magic != File::MAGIC) {
539         LOG(ERROR, PANDAFILE) << "Invalid magic number '";
540         return false;
541     }
542 
543     auto fileVersion = header->version;
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     return std::unique_ptr<File>(new File("", std::forward<os::mem::ConstBytePtr>(ptr)));
566 }
567 
568 /* static */
OpenFromMemory(os::mem::ConstBytePtr && ptr,std::string_view filename)569 std::unique_ptr<const File> File::OpenFromMemory(os::mem::ConstBytePtr &&ptr, std::string_view filename)
570 {
571     trace::ScopedTrace scopedTrace("Open panda file from RAM " + std::string(filename));
572 
573     if (!CheckHeader(ptr, filename)) {
574         return nullptr;
575     }
576 
577     return std::unique_ptr<File>(new File(filename.data(), std::forward<os::mem::ConstBytePtr>(ptr)));
578 }
579 
GetClassId(const uint8_t * mutf8Name) const580 File::EntityId File::GetClassId(const uint8_t *mutf8Name) const
581 {
582     auto classHashTable = GetClassHashTable();
583     if (!classHashTable.empty()) {
584         return GetClassIdFromClassHashTable(mutf8Name);
585     }
586 
587     auto classIdx = GetClasses();
588 
589     auto it = std::lower_bound(ClassIdxIterator::Begin(*this, classIdx), ClassIdxIterator::End(*this, classIdx),
590                                mutf8Name, utf::Mutf8Less());
591 
592     if (!it.IsValid()) {
593         return EntityId();
594     }
595 
596     if (utf::CompareMUtf8ToMUtf8(mutf8Name, *it) == 0) {
597         return EntityId(it.GetId());
598     }
599 
600     return EntityId();
601 }
602 
CalcFilenameHash(const std::string & filename)603 uint32_t File::CalcFilenameHash(const std::string &filename)
604 {
605     return GetHash32String(reinterpret_cast<const uint8_t *>(filename.c_str()));
606 }
607 
GetLiteralArraysId() const608 File::EntityId File::GetLiteralArraysId() const
609 {
610     const Header *header = GetHeader();
611     return EntityId(header->literalarrayIdxOff);
612 }
613 
GetClassIdFromClassHashTable(const uint8_t * mutf8Name) const614 File::EntityId File::GetClassIdFromClassHashTable(const uint8_t *mutf8Name) const
615 {
616     auto classHashTable = GetClassHashTable();
617     auto hash = GetHash32String(mutf8Name);
618     auto pos = hash & (classHashTable.size() - 1);
619     auto entityPair = &classHashTable[pos];
620     if (entityPair->descriptorHash % classHashTable.size() != pos) {
621         return File::EntityId();
622     }
623 
624     while (true) {
625         if (hash == entityPair->descriptorHash) {
626             auto entityId = File::EntityId(entityPair->entityIdOffset);
627             auto descriptor = GetStringData(entityId).data;
628             if (entityId.IsValid() && utf::CompareMUtf8ToMUtf8(descriptor, mutf8Name) == 0) {
629                 return entityId;
630             }
631         }
632         if (entityPair->nextPos == 0) {
633             break;
634         }
635         entityPair = &classHashTable[entityPair->nextPos - 1];
636     }
637 
638     return File::EntityId();
639 }
640 
641 }  // namespace panda::panda_file
642