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