Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qresource.cpp
Go to the documentation of this file.
1// Copyright (C) 2019 The Qt Company Ltd.
2// Copyright (C) 2020 Intel Corporation.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4
5#include "qresource.h"
6#include "qresource_p.h"
8#include "qset.h"
9#include <private/qlocking_p.h>
10#include "qdebug.h"
11#include "qlocale.h"
12#include "qglobal.h"
13#include "qlist.h"
14#include "qdatetime.h"
15#include "qbytearray.h"
16#include "qstringlist.h"
17#include "qendian.h"
18#include <qshareddata.h>
19#include <qplatformdefs.h>
20#include <qendian.h>
21#include "private/qabstractfileengine_p.h"
22#include "private/qduplicatetracker_p.h"
23#include "private/qnumeric_p.h"
24#include "private/qsimd_p.h"
25#include "private/qtools_p.h"
26#include "private/qsystemerror_p.h"
27
28#ifndef QT_NO_COMPRESS
29# include <zconf.h>
30# include <zlib.h>
31#endif
32#if QT_CONFIG(zstd)
33# include <zstd.h>
34#endif
35
36#if defined(Q_OS_UNIX) && !defined(Q_OS_INTEGRITY)
37# define QT_USE_MMAP
38# include <sys/mman.h>
39# ifdef Q_OS_LINUX
40// since 5.7, so define in case we're being compiled with older kernel headers
41# define MREMAP_DONTUNMAP 4
42# elif defined(Q_OS_DARWIN)
43# include <mach/mach.h>
44# include <mach/vm_map.h>
45# endif
46#endif
47#ifdef Q_OS_WIN
48# include <qt_windows.h>
49#endif
50
51//#define DEBUG_RESOURCE_MATCH
52
54
55using namespace Qt::StringLiterals;
56
57// Symbols used by code generated by RCC.
58// They cause compilation errors if the RCC content couldn't
59// be interpreted by this QtCore version.
60#if defined(__ELF__) || defined(__APPLE__) // same as RCC generates
61# define RCC_FEATURE_SYMBOL(feature) \
62 extern Q_CORE_EXPORT const quint8 qt_resourceFeature ## feature; \
63 const quint8 qt_resourceFeature ## feature = 0;
64#else
65# define RCC_FEATURE_SYMBOL(feature) \
66 Q_CORE_EXPORT quint8 qResourceFeature ## feature() { return 0; }
67#endif
68
69#ifndef QT_NO_COMPRESS
71#endif
72#if QT_CONFIG(zstd)
74#endif
75
76#undef RCC_FEATURE_SYMBOL
77
78namespace {
79class QStringSplitter
80{
81public:
82 explicit QStringSplitter(QStringView sv)
83 : m_data(sv.data()), m_len(sv.size())
84 {
85 }
86
87 inline bool hasNext()
88 {
89 while (m_pos < m_len && m_data[m_pos] == m_splitChar)
90 ++m_pos;
91 return m_pos < m_len;
92 }
93
94 inline QStringView next()
95 {
96 const qsizetype start = m_pos;
97 while (m_pos < m_len && m_data[m_pos] != m_splitChar)
98 ++m_pos;
99 return QStringView(m_data + start, m_pos - start);
100 }
101
102 const QChar *m_data;
103 qsizetype m_len;
104 qsizetype m_pos = 0;
105 QChar m_splitChar = u'/';
106};
107
108// resource glue
109class QResourceRoot
110{
111public:
112 enum Flags {
113 // must match rcc.h
114 Compressed = 0x01,
115 Directory = 0x02,
116 CompressedZstd = 0x04
117 };
118
119private:
120 const uchar *tree, *names, *payloads;
121 int version;
122 inline int findOffset(int node) const { return node * (14 + (version >= 0x02 ? 8 : 0)); } //sizeof each tree element
123 uint hash(int node) const;
124 QString name(int node) const;
125 short flags(int node) const;
126public:
127 mutable QAtomicInt ref;
128
129 inline QResourceRoot(): tree(nullptr), names(nullptr), payloads(nullptr), version(0) {}
130 inline QResourceRoot(int version, const uchar *t, const uchar *n, const uchar *d) { setSource(version, t, n, d); }
131 virtual ~QResourceRoot() { }
132 int findNode(const QString &path, const QLocale &locale=QLocale()) const;
133 inline bool isContainer(int node) const { return flags(node) & Directory; }
134 QResource::Compression compressionAlgo(int node)
135 {
136 uint compressionFlags = flags(node) & (Compressed | CompressedZstd);
137 if (compressionFlags == Compressed)
139 if (compressionFlags == CompressedZstd)
142 }
143 const uchar *data(int node, qint64 *size) const;
144 qint64 lastModified(int node) const;
145 QStringList children(int node) const;
146 virtual QString mappingRoot() const { return QString(); }
147 bool mappingRootSubdir(const QString &path, QString *match = nullptr) const;
148 inline bool operator==(const QResourceRoot &other) const
149 { return tree == other.tree && names == other.names && payloads == other.payloads && version == other.version; }
150 inline bool operator!=(const QResourceRoot &other) const
151 { return !operator==(other); }
152 enum ResourceRootType { Resource_Builtin, Resource_File, Resource_Buffer };
153 virtual ResourceRootType type() const { return Resource_Builtin; }
154
155protected:
156 inline void setSource(int v, const uchar *t, const uchar *n, const uchar *d) {
157 tree = t;
158 names = n;
159 payloads = d;
160 version = v;
161 }
162};
163
164static QString cleanPath(const QString &_path)
165{
167 // QDir::cleanPath does not remove two trailing slashes under _Windows_
168 // due to support for UNC paths. Remove those manually.
169 if (path.startsWith("//"_L1))
170 path.remove(0, 1);
171 return path;
172}
173} // unnamed namespace
174
176
177typedef QList<QResourceRoot*> ResourceList;
178namespace {
179struct QResourceGlobalData
180{
183};
184}
185Q_GLOBAL_STATIC(QResourceGlobalData, resourceGlobalData)
186
188{ return resourceGlobalData->resourceMutex; }
189
191{ return &resourceGlobalData->resourceList; }
192
284public:
285 inline QResourcePrivate(QResource *_q) : q_ptr(_q) { clear(); }
286 inline ~QResourcePrivate() { clear(); }
287
288 void ensureInitialized() const;
289 void ensureChildren() const;
291 qsizetype decompress(char *buffer, qsizetype bufferSize) const;
292
293 bool load(const QString &file);
294 void clear();
295
296 static bool mayRemapData(const QResource &resource);
297
300 QList<QResourceRoot *> related;
303 const uchar *data;
307 /* 2 or 6 padding bytes */
308
310 Q_DECLARE_PUBLIC(QResource)
311};
312
314{
317 data = nullptr;
318 size = 0;
319 children.clear();
320 lastModified = 0;
321 container = 0;
322 for (int i = 0; i < related.size(); ++i) {
323 QResourceRoot *root = related.at(i);
324 if (!root->ref.deref())
325 delete root;
326 }
327 related.clear();
328}
329
331{
332 related.clear();
333 const auto locker = qt_scoped_lock(resourceMutex());
334 const ResourceList *list = resourceList();
335 QString cleaned = cleanPath(file);
336 for (int i = 0; i < list->size(); ++i) {
337 QResourceRoot *res = list->at(i);
338 const int node = res->findNode(cleaned, locale);
339 if (node != -1) {
340 if (related.isEmpty()) {
341 container = res->isContainer(node);
342 if (!container) {
343 data = res->data(node, &size);
344 compressionAlgo = res->compressionAlgo(node);
345 } else {
346 data = nullptr;
347 size = 0;
349 }
350 lastModified = res->lastModified(node);
351 } else if (res->isContainer(node) != container) {
352 qWarning("QResourceInfo: Resource [%s] has both data and children!",
353 file.toLatin1().constData());
354 }
355 res->ref.ref();
357 } else if (res->mappingRootSubdir(file)) {
358 container = true;
359 data = nullptr;
360 size = 0;
362 lastModified = 0;
363 res->ref.ref();
365 }
366 }
367 return !related.isEmpty();
368}
369
371{
372 if (!related.isEmpty())
373 return;
374 QResourcePrivate *that = const_cast<QResourcePrivate *>(this);
375 if (fileName == ":"_L1)
376 that->fileName += u'/';
377 that->absoluteFilePath = fileName;
378 if (!that->absoluteFilePath.startsWith(u':'))
379 that->absoluteFilePath.prepend(u':');
380
382 if (path.startsWith(u':'))
383 path = path.mid(1);
384
385 if (path.startsWith(u'/')) {
386 that->load(path.toString());
387 } else {
388 // Should we search QDir::searchPath() before falling back to root ?
389 const QString searchPath(u'/' + path);
390 if (that->load(searchPath))
391 that->absoluteFilePath = u':' + searchPath;
392 }
393}
394
396{
398 if (!children.isEmpty() || !container || related.isEmpty())
399 return;
400
402 if (path.startsWith(u':'))
403 path = path.mid(1);
404 QDuplicateTracker<QString> kids(related.size());
405 QString cleaned = cleanPath(path);
406 for (int i = 0; i < related.size(); ++i) {
407 QResourceRoot *res = related.at(i);
408 if (res->mappingRootSubdir(path, &k) && !k.isEmpty()) {
409 if (!kids.hasSeen(k))
410 children += k;
411 } else {
412 const int node = res->findNode(cleaned);
413 if (node != -1) {
414 QStringList related_children = res->children(node);
415 for (int kid = 0; kid < related_children.size(); ++kid) {
416 k = related_children.at(kid);
417 if (!kids.hasSeen(k))
418 children += k;
419 }
420 }
421 }
422 }
423}
424
426{
427 switch (compressionAlgo) {
429 return size;
430
432#ifndef QT_NO_COMPRESS
433 if (size_t(size) >= sizeof(quint32))
434 return qFromBigEndian<quint32>(data);
435#else
436 Q_ASSERT(!"QResource: Qt built without support for Zlib compression");
437 Q_UNREACHABLE();
438#endif
439 break;
440
442#if QT_CONFIG(zstd)
443 size_t n = ZSTD_getFrameContentSize(data, size);
444 return ZSTD_isError(n) ? -1 : qint64(n);
445#else
446 // This should not happen because we've refused to load such resource
447 Q_ASSERT(!"QResource: Qt built without support for Zstd compression");
448 Q_UNREACHABLE();
449#endif
450 }
451 }
452 return -1;
453}
454
456{
457 Q_ASSERT(data);
458#if defined(QT_NO_COMPRESS) && !QT_CONFIG(zstd)
460 Q_UNUSED(bufferSize);
461#endif
462
463 switch (compressionAlgo) {
465 Q_UNREACHABLE();
466 break;
467
469#ifndef QT_NO_COMPRESS
470 uLong len = uLong(bufferSize);
471 int res = ::uncompress(reinterpret_cast<Bytef *>(buffer), &len, data + sizeof(quint32),
472 uLong(size - sizeof(quint32)));
473 if (res != Z_OK) {
474 qWarning("QResource: error decompressing zlib content (%d)", res);
475 return -1;
476 }
477 return len;
478#else
479 Q_UNREACHABLE();
480#endif
481 }
482
484#if QT_CONFIG(zstd)
485 size_t usize = ZSTD_decompress(buffer, bufferSize, data, size);
486 if (ZSTD_isError(usize)) {
487 qWarning("QResource: error decompressing zstd content: %s", ZSTD_getErrorName(usize));
488 return -1;
489 }
490 return usize;
491#else
492 Q_UNREACHABLE();
493#endif
494 }
495 }
496
497 return -1;
498}
499
508{
509 Q_D(QResource);
510 d->fileName = file;
511 d->locale = locale;
512}
513
520
529void QResource::setLocale(const QLocale &locale)
530{
531 Q_D(QResource);
532 d->clear();
533 d->locale = locale;
534}
535
541{
542 Q_D(const QResource);
543 return d->locale;
544}
545
555{
556 Q_D(QResource);
557 d->clear();
558 d->fileName = file;
559}
560
569{
570 Q_D(const QResource);
571 d->ensureInitialized();
572 return d->fileName;
573}
574
583{
584 Q_D(const QResource);
585 d->ensureInitialized();
586 return d->absoluteFilePath;
587}
588
596{
597 Q_D(const QResource);
598 d->ensureInitialized();
599 return !d->related.isEmpty();
600}
601
630{
631 Q_D(const QResource);
632 d->ensureInitialized();
633 return Compression(d->compressionAlgo);
634}
635
646{
647 Q_D(const QResource);
648 d->ensureInitialized();
649 return d->size;
650}
651
663{
664 Q_D(const QResource);
665 d->ensureInitialized();
666 return d->uncompressedSize();
667}
668
678const uchar *QResource::data() const
679{
680 Q_D(const QResource);
681 d->ensureInitialized();
682 return d->data;
683}
684
699{
700 Q_D(const QResource);
702 if (n < 0)
703 return QByteArray();
704 if (n > std::numeric_limits<QByteArray::size_type>::max()) {
705 qWarning("QResource: compressed content does not fit into a QByteArray; use QFile instead");
706 return QByteArray();
707 }
708 if (d->compressionAlgo == NoCompression)
709 return QByteArray::fromRawData(reinterpret_cast<const char *>(d->data), n);
710
711 // decompress
713 n = d->decompress(result.data(), n);
714 if (n < 0)
715 result.clear();
716 else
717 result.truncate(n);
718 return result;
719}
720
728{
729 Q_D(const QResource);
730 d->ensureInitialized();
731 return d->lastModified ? QDateTime::fromMSecsSinceEpoch(d->lastModified) : QDateTime();
732}
733
742{
743 Q_D(const QResource);
744 d->ensureInitialized();
745 return d->container;
746}
747
756{
757 Q_D(const QResource);
758 d->ensureChildren();
759 return d->children;
760}
761
762inline uint QResourceRoot::hash(int node) const
763{
764 if (!node) // root
765 return 0;
766 const int offset = findOffset(node);
767 qint32 name_offset = qFromBigEndian<qint32>(tree + offset);
768 name_offset += 2; // jump past name length
769 return qFromBigEndian<quint32>(names + name_offset);
770}
771inline QString QResourceRoot::name(int node) const
772{
773 if (!node) // root
774 return QString();
775 const int offset = findOffset(node);
776
777 QString ret;
778 qint32 name_offset = qFromBigEndian<qint32>(tree + offset);
779 quint16 name_length = qFromBigEndian<qint16>(names + name_offset);
780 name_offset += 2;
781 name_offset += 4; // jump past hash
782
783 ret.resize(name_length);
784 QChar *strData = ret.data();
785 qFromBigEndian<char16_t>(names + name_offset, name_length, strData);
786 return ret;
787}
788
789int QResourceRoot::findNode(const QString &_path, const QLocale &locale) const
790{
791 QString path = _path;
792 {
793 QString root = mappingRoot();
794 if (!root.isEmpty()) {
795 if (root == path) {
796 path = u'/';
797 } else {
798 if (!root.endsWith(u'/'))
799 root += u'/';
800 if (path.size() >= root.size() && path.startsWith(root))
801 path = path.mid(root.size() - 1);
802 if (path.isEmpty())
803 path = u'/';
804 }
805 }
806 }
807#ifdef DEBUG_RESOURCE_MATCH
808 qDebug() << "!!!!" << "START" << path << locale.territory() << locale.language();
809#endif
810
811 if (path == "/"_L1)
812 return 0;
813
814 // the root node is always first
815 qint32 child_count = qFromBigEndian<qint32>(tree + 6);
816 qint32 child = qFromBigEndian<qint32>(tree + 10);
817
818 // now iterate up the tree
819 int node = -1;
820
821 QStringSplitter splitter(path);
822 while (child_count && splitter.hasNext()) {
823 QStringView segment = splitter.next();
824
825#ifdef DEBUG_RESOURCE_MATCH
826 qDebug() << " CHILDREN" << segment;
827 for (int j = 0; j < child_count; ++j) {
828 qDebug() << " " << child + j << " :: " << name(child + j);
829 }
830#endif
831 const uint h = qt_hash(segment);
832
833 // do the binary search for the hash
834 int l = 0, r = child_count - 1;
835 int sub_node = (l + r + 1) / 2;
836 while (r != l) {
837 const uint sub_node_hash = hash(child + sub_node);
838 if (h == sub_node_hash)
839 break;
840 else if (h < sub_node_hash)
841 r = sub_node - 1;
842 else
843 l = sub_node;
844 sub_node = (l + r + 1) / 2;
845 }
846 sub_node += child;
847
848 // now do the "harder" compares
849 bool found = false;
850 if (hash(sub_node) == h) {
851 while (sub_node > child && hash(sub_node - 1) == h) // backup for collisions
852 --sub_node;
853 for (; sub_node < child + child_count && hash(sub_node) == h;
854 ++sub_node) { // here we go...
855 if (name(sub_node) == segment) {
856 found = true;
857 int offset = findOffset(sub_node);
858#ifdef DEBUG_RESOURCE_MATCH
859 qDebug() << " TRY" << sub_node << name(sub_node) << offset;
860#endif
861 offset += 4; // jump past name
862
863 const qint16 flags = qFromBigEndian<qint16>(tree + offset);
864 offset += 2;
865
866 if (!splitter.hasNext()) {
867 if (!(flags & Directory)) {
868 const qint16 territory = qFromBigEndian<qint16>(tree + offset);
869 offset += 2;
870
871 const qint16 language = qFromBigEndian<qint16>(tree + offset);
872 offset += 2;
873#ifdef DEBUG_RESOURCE_MATCH
874 qDebug() << " " << "LOCALE" << country << language;
875#endif
876 if (territory == locale.territory() && language == locale.language()) {
877#ifdef DEBUG_RESOURCE_MATCH
878 qDebug() << "!!!!" << "FINISHED" << __LINE__ << sub_node;
879#endif
880 return sub_node;
881 } else if ((territory == QLocale::AnyTerritory
882 && language == locale.language())
883 || (territory == QLocale::AnyTerritory
884 && language == QLocale::C
885 && node == -1)) {
886 node = sub_node;
887 }
888 continue;
889 } else {
890#ifdef DEBUG_RESOURCE_MATCH
891 qDebug() << "!!!!" << "FINISHED" << __LINE__ << sub_node;
892#endif
893
894 return sub_node;
895 }
896 }
897
898 if (!(flags & Directory))
899 return -1;
900
901 child_count = qFromBigEndian<qint32>(tree + offset);
902 offset += 4;
903 child = qFromBigEndian<qint32>(tree + offset);
904 break;
905 }
906 }
907 }
908 if (!found)
909 break;
910 }
911#ifdef DEBUG_RESOURCE_MATCH
912 qDebug() << "!!!!" << "FINISHED" << __LINE__ << node;
913#endif
914 return node;
915}
916short QResourceRoot::flags(int node) const
917{
918 if (node == -1)
919 return 0;
920 const int offset = findOffset(node) + 4; // jump past name
921 return qFromBigEndian<qint16>(tree + offset);
922}
923const uchar *QResourceRoot::data(int node, qint64 *size) const
924{
925 if (node == -1) {
926 *size = 0;
927 return nullptr;
928 }
929 int offset = findOffset(node) + 4; // jump past name
930
931 const qint16 flags = qFromBigEndian<qint16>(tree + offset);
932 offset += 2;
933
934 offset += 4; // jump past locale
935
936 if (!(flags & Directory)) {
937 const qint32 data_offset = qFromBigEndian<qint32>(tree + offset);
938 const quint32 data_length = qFromBigEndian<quint32>(payloads + data_offset);
939 const uchar *ret = payloads + data_offset + 4;
940 *size = data_length;
941 return ret;
942 }
943 *size = 0;
944 return nullptr;
945}
946
947qint64 QResourceRoot::lastModified(int node) const
948{
949 if (node == -1 || version < 0x02)
950 return 0;
951
952 const int offset = findOffset(node) + 14;
953
954 return qFromBigEndian<qint64>(tree + offset);
955}
956
957QStringList QResourceRoot::children(int node) const
958{
959 if (node == -1)
960 return QStringList();
961 int offset = findOffset(node) + 4; // jump past name
962
963 const qint16 flags = qFromBigEndian<qint16>(tree + offset);
964 offset += 2;
965
967 if (flags & Directory) {
968 const qint32 child_count = qFromBigEndian<qint32>(tree + offset);
969 offset += 4;
970 const qint32 child_off = qFromBigEndian<qint32>(tree + offset);
971 ret.reserve(child_count);
972 for (int i = child_off; i < child_off + child_count; ++i)
973 ret << name(i);
974 }
975 return ret;
976}
977bool QResourceRoot::mappingRootSubdir(const QString &path, QString *match) const
978{
979 const QString root = mappingRoot();
980 if (root.isEmpty())
981 return false;
982
983 QStringSplitter rootIt(root);
984 QStringSplitter pathIt(path);
985 while (rootIt.hasNext()) {
986 if (pathIt.hasNext()) {
987 if (rootIt.next() != pathIt.next()) // mismatch
988 return false;
989 } else {
990 // end of path, but not of root:
991 if (match)
992 *match = rootIt.next().toString();
993 return true;
994 }
995 }
996 // end of root
997 return !pathIt.hasNext();
998}
999
1000Q_CORE_EXPORT bool qRegisterResourceData(int version, const unsigned char *tree,
1001 const unsigned char *name, const unsigned char *data)
1002{
1003 if (resourceGlobalData.isDestroyed())
1004 return false;
1005 const auto locker = qt_scoped_lock(resourceMutex());
1007 if (version >= 0x01 && version <= 0x3) {
1008 bool found = false;
1009 QResourceRoot res(version, tree, name, data);
1010 for (int i = 0; i < list->size(); ++i) {
1011 if (*list->at(i) == res) {
1012 found = true;
1013 break;
1014 }
1015 }
1016 if (!found) {
1017 QResourceRoot *root = new QResourceRoot(version, tree, name, data);
1018 root->ref.ref();
1019 list->append(root);
1020 }
1021 return true;
1022 }
1023 return false;
1024}
1025
1026Q_CORE_EXPORT bool qUnregisterResourceData(int version, const unsigned char *tree,
1027 const unsigned char *name, const unsigned char *data)
1028{
1029 if (resourceGlobalData.isDestroyed())
1030 return false;
1031
1032 const auto locker = qt_scoped_lock(resourceMutex());
1033 if (version >= 0x01 && version <= 0x3) {
1034 QResourceRoot res(version, tree, name, data);
1036 for (int i = 0; i < list->size();) {
1037 if (*list->at(i) == res) {
1038 QResourceRoot *root = list->takeAt(i);
1039 if (!root->ref.deref())
1040 delete root;
1041 } else {
1042 ++i;
1043 }
1044 }
1045 return true;
1046 }
1047 return false;
1048}
1049
1050namespace {
1051// run time resource creation
1052class QDynamicBufferResourceRoot : public QResourceRoot
1053{
1054 QString root;
1055 const uchar *buffer;
1056
1057public:
1058 inline QDynamicBufferResourceRoot(const QString &_root) : root(_root), buffer(nullptr) { }
1059 inline ~QDynamicBufferResourceRoot() { }
1060 inline const uchar *mappingBuffer() const { return buffer; }
1061 QString mappingRoot() const override { return root; }
1062 ResourceRootType type() const override { return Resource_Buffer; }
1063
1064 // size == -1 means "unknown"
1065 bool registerSelf(const uchar *b, qsizetype size)
1066 {
1067 // 5 int "pointers"
1068 if (size >= 0 && size < 20)
1069 return false;
1070
1071 // setup the data now
1072 int offset = 0;
1073
1074 // magic number
1075 if (b[offset + 0] != 'q' || b[offset + 1] != 'r' || b[offset + 2] != 'e'
1076 || b[offset + 3] != 's') {
1077 return false;
1078 }
1079 offset += 4;
1080
1081 const int version = qFromBigEndian<qint32>(b + offset);
1082 offset += 4;
1083
1084 const int tree_offset = qFromBigEndian<qint32>(b + offset);
1085 offset += 4;
1086
1087 const int data_offset = qFromBigEndian<qint32>(b + offset);
1088 offset += 4;
1089
1090 const int name_offset = qFromBigEndian<qint32>(b + offset);
1091 offset += 4;
1092
1093 quint32 file_flags = 0;
1094 if (version >= 3) {
1095 file_flags = qFromBigEndian<qint32>(b + offset);
1096 offset += 4;
1097 }
1098
1099 // Some sanity checking for sizes. This is _not_ a security measure.
1100 if (size >= 0 && (tree_offset >= size || data_offset >= size || name_offset >= size))
1101 return false;
1102
1103 // And some sanity checking for features
1104 quint32 acceptableFlags = 0;
1105#ifndef QT_NO_COMPRESS
1106 acceptableFlags |= Compressed;
1107#endif
1108 if (QT_CONFIG(zstd))
1109 acceptableFlags |= CompressedZstd;
1110 if (file_flags & ~acceptableFlags)
1111 return false;
1112
1113 if (version >= 0x01 && version <= 0x03) {
1114 buffer = b;
1115 setSource(version, b + tree_offset, b + name_offset, b + data_offset);
1116 return true;
1117 }
1118 return false;
1119 }
1120};
1121
1122class QDynamicFileResourceRoot : public QDynamicBufferResourceRoot
1123{
1124public:
1125 static uchar *map_sys(QFile &file, qint64 base, qsizetype size);
1126 static void unmap_sys(void *base, qsizetype size);
1127
1128private:
1130 // for mmap'ed files, this is what needs to be unmapped.
1131 uchar *unmapPointer;
1132 qsizetype unmapLength;
1133
1134public:
1135 QDynamicFileResourceRoot(const QString &_root)
1136 : QDynamicBufferResourceRoot(_root), unmapPointer(nullptr), unmapLength(0)
1137 { }
1138 ~QDynamicFileResourceRoot() {
1139 if (wasMemoryMapped())
1140 unmap_sys(unmapPointer, unmapLength);
1141 else
1142 delete[] mappingBuffer();
1143 }
1144 QString mappingFile() const { return fileName; }
1145 ResourceRootType type() const override { return Resource_File; }
1146 bool wasMemoryMapped() const { return unmapPointer; }
1147
1148 bool registerSelf(const QString &f);
1149};
1150} // unnamed namespace
1151
1152#ifndef MAP_FILE
1153# define MAP_FILE 0
1154#endif
1155#ifndef MAP_FAILED
1156# define MAP_FAILED reinterpret_cast<void *>(-1)
1157#endif
1158
1159void QDynamicFileResourceRoot::unmap_sys(void *base, qsizetype size)
1160{
1161#if defined(QT_USE_MMAP)
1162 munmap(base, size);
1163#elif defined(Q_OS_WIN)
1164 Q_UNUSED(size)
1165 UnmapViewOfFile(reinterpret_cast<void *>(base));
1166#endif
1167}
1168
1169// Note: caller must ensure \a offset and \a size are acceptable to the OS.
1170uchar *QDynamicFileResourceRoot::map_sys(QFile &file, qint64 offset, qsizetype size)
1171{
1172 Q_ASSERT(file.isOpen());
1173 void *ptr = nullptr;
1174 if (size < 0)
1175 size = qMin(file.size() - offset, (std::numeric_limits<qsizetype>::max)());
1176
1177 // We don't use QFile::map() here because we want to dispose of the QFile object
1178#if defined(QT_USE_MMAP)
1179 int fd = file.handle();
1180 int protection = PROT_READ; // read-only memory
1181 int flags = MAP_FILE | MAP_PRIVATE; // swap-backed map from file
1182 ptr = QT_MMAP(nullptr, size, protection, flags, fd, offset);
1183 if (ptr == MAP_FAILED)
1184 ptr = nullptr;
1185#elif defined(Q_OS_WIN)
1186 int fd = file.handle();
1187 HANDLE fileHandle = reinterpret_cast<HANDLE>(_get_osfhandle(fd));
1188 if (fileHandle != INVALID_HANDLE_VALUE) {
1189 HANDLE mapHandle = CreateFileMapping(fileHandle, 0, PAGE_WRITECOPY, 0, 0, 0);
1190 if (mapHandle) {
1191 ptr = MapViewOfFile(mapHandle, FILE_MAP_COPY, DWORD(offset >> 32), DWORD(offset), size);
1192 CloseHandle(mapHandle);
1193 }
1194 }
1195#endif // QT_USE_MMAP
1196 return static_cast<uchar *>(ptr);
1197}
1198
1199bool QDynamicFileResourceRoot::registerSelf(const QString &f)
1200{
1201 QFile file(f);
1203 return false;
1204
1205 qint64 data_len = file.size();
1206 if (data_len > std::numeric_limits<qsizetype>::max())
1207 return false;
1208
1209 uchar *data = map_sys(file, 0, data_len);
1210 bool fromMM = !!data;
1211
1212 if (!fromMM) {
1213 bool ok = false;
1214 data = new uchar[data_len];
1215 ok = (data_len == file.read(reinterpret_cast<char *>(data), data_len));
1216 if (!ok) {
1217 delete[] data;
1218 data = nullptr;
1219 data_len = 0;
1220 return false;
1221 }
1222 }
1223 if (data && QDynamicBufferResourceRoot::registerSelf(data, data_len)) {
1224 if (fromMM) {
1225 unmapPointer = data;
1226 unmapLength = data_len;
1227 }
1228 fileName = f;
1229 return true;
1230 }
1231 return false;
1232}
1233
1235{
1236 if (!r.isEmpty()) {
1237 if (r.startsWith(u':'))
1238 r = r.mid(1);
1239 if (!r.isEmpty())
1240 r = QDir::cleanPath(r);
1241 }
1242 return r;
1243}
1244
1255bool QResource::registerResource(const QString &rccFilename, const QString &resourceRoot)
1256{
1257 QString r = qt_resource_fixResourceRoot(resourceRoot);
1258 if (!r.isEmpty() && r[0] != u'/') {
1259 qWarning("QDir::registerResource: Registering a resource [%ls] must be rooted in an "
1260 "absolute path (start with /) [%ls]",
1261 qUtf16Printable(rccFilename), qUtf16Printable(resourceRoot));
1262 return false;
1263 }
1264
1265 QDynamicFileResourceRoot *root = new QDynamicFileResourceRoot(r);
1266 if (root->registerSelf(rccFilename)) {
1267 root->ref.ref();
1268 const auto locker = qt_scoped_lock(resourceMutex());
1269 resourceList()->append(root);
1270 return true;
1271 }
1272 delete root;
1273 return false;
1274}
1275
1287bool QResource::unregisterResource(const QString &rccFilename, const QString &resourceRoot)
1288{
1289 QString r = qt_resource_fixResourceRoot(resourceRoot);
1290
1291 const auto locker = qt_scoped_lock(resourceMutex());
1293 for (int i = 0; i < list->size(); ++i) {
1294 QResourceRoot *res = list->at(i);
1295 if (res->type() == QResourceRoot::Resource_File) {
1296 QDynamicFileResourceRoot *root = reinterpret_cast<QDynamicFileResourceRoot *>(res);
1297 if (root->mappingFile() == rccFilename && root->mappingRoot() == r) {
1298 list->removeAt(i);
1299 if (!root->ref.deref()) {
1300 delete root;
1301 return true;
1302 }
1303 return false;
1304 }
1305 }
1306 }
1307 return false;
1308}
1309
1324bool QResource::registerResource(const uchar *rccData, const QString &resourceRoot)
1325{
1326 QString r = qt_resource_fixResourceRoot(resourceRoot);
1327 if (!r.isEmpty() && r[0] != u'/') {
1328 qWarning("QDir::registerResource: Registering a resource [%p] must be rooted in an "
1329 "absolute path (start with /) [%ls]",
1330 rccData, qUtf16Printable(resourceRoot));
1331 return false;
1332 }
1333
1334 QDynamicBufferResourceRoot *root = new QDynamicBufferResourceRoot(r);
1335 if (root->registerSelf(rccData, -1)) {
1336 root->ref.ref();
1337 const auto locker = qt_scoped_lock(resourceMutex());
1338 resourceList()->append(root);
1339 return true;
1340 }
1341 delete root;
1342 return false;
1343}
1344
1356bool QResource::unregisterResource(const uchar *rccData, const QString &resourceRoot)
1357{
1358 QString r = qt_resource_fixResourceRoot(resourceRoot);
1359
1360 const auto locker = qt_scoped_lock(resourceMutex());
1362 for (int i = 0; i < list->size(); ++i) {
1363 QResourceRoot *res = list->at(i);
1364 if (res->type() == QResourceRoot::Resource_Buffer) {
1365 QDynamicBufferResourceRoot *root = reinterpret_cast<QDynamicBufferResourceRoot *>(res);
1366 if (root->mappingBuffer() == rccData && root->mappingRoot() == r) {
1367 list->removeAt(i);
1368 if (!root->ref.deref()) {
1369 delete root;
1370 return true;
1371 }
1372 return false;
1373 }
1374 }
1375 }
1376 return false;
1377}
1378
1379#if !defined(QT_BOOTSTRAPPED)
1380// resource engine
1382{
1383protected:
1384 Q_DECLARE_PUBLIC(QResourceFileEngine)
1385private:
1386 uchar *map(qint64 offset, qint64 size, QFile::MemoryMapFlags flags);
1387 bool unmap(uchar *ptr);
1388 void uncompress() const;
1389 void mapUncompressed();
1390 bool mapUncompressed_sys();
1391 void unmapUncompressed_sys();
1392 qint64 offset = 0;
1393 QResource resource;
1394 mutable QByteArray uncompressed;
1395 bool mustUnmap = false;
1396
1397 // minimum size for which we'll try to re-open ourselves in mapUncompressed()
1398 static constexpr qsizetype RemapCompressedThreshold = 16384;
1399protected:
1401 {
1402 if (mustUnmap)
1403 unmapUncompressed_sys();
1404 }
1405};
1406
1408{
1409 return true;
1410}
1411
1418
1422
1424{
1426 d->resource.setFileName(file);
1427}
1428
1429bool QResourceFileEngine::open(QIODevice::OpenMode flags,
1430 std::optional<QFile::Permissions> permissions)
1431{
1432 Q_UNUSED(permissions);
1433
1435 if (d->resource.fileName().isEmpty()) {
1436 qWarning("QResourceFileEngine::open: Missing file name");
1437 return false;
1438 }
1440 return false;
1441 if (d->resource.compressionAlgorithm() != QResource::NoCompression) {
1442 d->uncompress();
1443 if (d->uncompressed.isNull()) {
1444 d->errorString = QSystemError::stdString(EIO);
1445 return false;
1446 }
1447 }
1448 if (!d->resource.isValid()) {
1449 d->errorString = QSystemError::stdString(ENOENT);
1450 return false;
1451 }
1452 return true;
1453}
1454
1456{
1458 d->offset = 0;
1459 return true;
1460}
1461
1463{
1464 return true;
1465}
1466
1468{
1470 if (len > size() - d->offset)
1471 len = size() - d->offset;
1472 if (len <= 0)
1473 return 0;
1474 if (!d->uncompressed.isNull())
1475 memcpy(data, d->uncompressed.constData() + d->offset, len);
1476 else
1477 memcpy(data, d->resource.data() + d->offset, len);
1478 d->offset += len;
1479 return len;
1480}
1481
1483{
1484 Q_D(const QResourceFileEngine);
1485 return d->resource.isValid() ? d->resource.uncompressedSize() : 0;
1486}
1487
1489{
1490 Q_D(const QResourceFileEngine);
1491 return d->offset;
1492}
1493
1495{
1496 Q_D(const QResourceFileEngine);
1497 if (!d->resource.isValid())
1498 return true;
1499 return d->offset == size();
1500}
1501
1503{
1505 if (!d->resource.isValid())
1506 return false;
1507
1508 if (d->offset > size())
1509 return false;
1510 d->offset = pos;
1511 return true;
1512}
1513
1514QAbstractFileEngine::FileFlags QResourceFileEngine::fileFlags(QAbstractFileEngine::FileFlags type) const
1515{
1516 Q_D(const QResourceFileEngine);
1517 QAbstractFileEngine::FileFlags ret;
1518 if (!d->resource.isValid())
1519 return ret;
1520
1521 if (type & PermsMask)
1522 ret |= QAbstractFileEngine::FileFlags(ReadOwnerPerm | ReadUserPerm | ReadGroupPerm
1523 | ReadOtherPerm);
1524 if (type & TypesMask) {
1525 if (d->resource.isDir())
1526 ret |= DirectoryType;
1527 else
1528 ret |= FileType;
1529 }
1530 if (type & FlagsMask) {
1531 ret |= ExistsFlag;
1532 if (d->resource.absoluteFilePath() == ":/"_L1)
1533 ret |= RootFlag;
1534 }
1535 return ret;
1536}
1537
1539{
1540 Q_D(const QResourceFileEngine);
1541 if (file == BaseName) {
1542 const qsizetype slash = d->resource.fileName().lastIndexOf(u'/');
1543 if (slash == -1)
1544 return d->resource.fileName();
1545 return d->resource.fileName().mid(slash + 1);
1546 } else if (file == PathName || file == AbsolutePathName) {
1547 const QString path = (file == AbsolutePathName) ? d->resource.absoluteFilePath()
1548 : d->resource.fileName();
1549 const qsizetype slash = path.lastIndexOf(u'/');
1550 if (slash == -1)
1551 return ":"_L1;
1552 else if (slash <= 1)
1553 return ":/"_L1;
1554 return path.left(slash);
1555
1556 } else if (file == CanonicalName || file == CanonicalPathName) {
1557 const QString absoluteFilePath = d->resource.absoluteFilePath();
1558 if (file == CanonicalPathName) {
1559 const qsizetype slash = absoluteFilePath.lastIndexOf(u'/');
1560 if (slash != -1)
1561 return absoluteFilePath.left(slash);
1562 }
1563 return absoluteFilePath;
1564 }
1565 return d->resource.fileName();
1566}
1567
1569{
1570 static const uint nobodyID = static_cast<uint>(-2);
1571 return nobodyID;
1572}
1573
1575{
1576 Q_D(const QResourceFileEngine);
1578 return d->resource.lastModified();
1579 return QDateTime();
1580}
1581
1587 const QStringList &filterNames)
1588{
1589 return std::make_unique<QResourceFileEngineIterator>(path, filters, filterNames);
1590}
1591
1593{
1595 if (extension == MapExtension) {
1596 const auto *options = static_cast<const MapExtensionOption *>(option);
1597 auto *returnValue = static_cast<MapExtensionReturn *>(output);
1598 returnValue->address = d->map(options->offset, options->size, options->flags);
1599 return (returnValue->address != nullptr);
1600 }
1601 if (extension == UnMapExtension) {
1602 const auto *options = static_cast<const UnMapExtensionOption *>(option);
1603 return d->unmap(options->address);
1604 }
1605 return false;
1606}
1607
1612
1613uchar *QResourceFileEnginePrivate::map(qint64 offset, qint64 size, QFile::MemoryMapFlags flags)
1614{
1617 || !uncompressed.isNull(), "QFile::map()",
1618 "open() should have uncompressed compressed resources");
1619
1620 qint64 max = resource.uncompressedSize();
1621 qint64 end;
1622 if (offset < 0 || size <= 0 || !resource.isValid() ||
1623 qAddOverflow(offset, size, &end) || end > max) {
1624 q->setError(QFile::UnspecifiedError, QString());
1625 return nullptr;
1626 }
1627
1628 const uchar *address = reinterpret_cast<const uchar *>(uncompressed.constBegin());
1629 if (!uncompressed.isNull())
1630 return const_cast<uchar *>(address) + offset;
1631
1632 // resource was not compressed
1633 address = resource.data();
1635 // We need to provide read-write memory
1636 mapUncompressed();
1637 address = reinterpret_cast<const uchar *>(uncompressed.constData());
1638 }
1639
1640 return const_cast<uchar *>(address) + offset;
1641}
1642
1643bool QResourceFileEnginePrivate::unmap(uchar *ptr)
1644{
1645 Q_UNUSED(ptr);
1646 return true;
1647}
1648
1649void QResourceFileEnginePrivate::uncompress() const
1650{
1652 || !uncompressed.isEmpty() || resource.size() == 0)
1653 return; // nothing to do
1654 uncompressed = resource.uncompressedData();
1655}
1656
1657void QResourceFileEnginePrivate::mapUncompressed()
1658{
1660 if (!uncompressed.isNull())
1661 return; // nothing to do
1662
1663 if (resource.uncompressedSize() >= RemapCompressedThreshold) {
1664 if (mapUncompressed_sys())
1665 return;
1666 }
1667
1668 uncompressed = resource.uncompressedData();
1669 uncompressed.detach();
1670}
1671
1672#if defined(MREMAP_MAYMOVE) && defined(MREMAP_DONTUNMAP)
1673inline bool QResourcePrivate::mayRemapData(const QResource &resource)
1674{
1675 auto d = resource.d_func();
1676
1677 // assumptions from load():
1678 // - d->related is not empty
1679 // - the first item in d->related is the one with our data
1680 // by current construction, it's also the only item
1681 const QResourceRoot *root = d->related.at(0);
1682
1683 switch (root->type()) {
1684 case QResourceRoot::Resource_Builtin:
1685 return true; // always acceptable, memory is read-only
1686 case QResourceRoot::Resource_Buffer:
1687 return false; // never acceptable, memory is heap
1688 case QResourceRoot::Resource_File:
1689 break;
1690 }
1691
1692 auto df = static_cast<const QDynamicFileResourceRoot *>(root);
1693 return df->wasMemoryMapped();
1694}
1695#endif
1696
1697// Returns the page boundaries of where \a location is located in memory.
1698static auto mappingBoundaries(const void *location, qsizetype size)
1699{
1700#ifdef Q_OS_WIN
1701 auto getpagesize = [] {
1702 SYSTEM_INFO sysinfo;
1703 ::GetSystemInfo(&sysinfo);
1704 return sysinfo.dwAllocationGranularity;
1705 };
1706#endif
1707 struct R {
1708 void *begin;
1711 } r;
1712
1713 const quintptr pageMask = getpagesize() - 1;
1715 quintptr begin = data & ~pageMask;
1716 quintptr end = (data + size + pageMask) & ~pageMask;
1717 r.begin = reinterpret_cast<void *>(begin);
1718 r.size = end - begin;
1719 r.offset = data & pageMask;
1720 return r;
1721}
1722
1723bool QResourceFileEnginePrivate::mapUncompressed_sys()
1724{
1725 auto r = mappingBoundaries(resource.data(), resource.uncompressedSize());
1726 void *ptr = nullptr;
1727
1728#if defined(MREMAP_MAYMOVE) && defined(MREMAP_DONTUNMAP)
1729 // Use MREMAP_MAYMOVE to tell the kernel to give us a new address and use
1730 // MREMAP_DONTUNMAP (supported since kernel 5.7) to request that it create
1731 // a new mapping of the same pages, instead of moving. We can only do that
1732 // for pages that are read-only, otherwise the kernel replaces the source
1733 // with pages full of nulls.
1734 if (!QResourcePrivate::mayRemapData(resource))
1735 return false;
1736
1737 ptr = mremap(r.begin, r.size, r.size, MREMAP_MAYMOVE | MREMAP_DONTUNMAP);
1738 if (ptr == MAP_FAILED)
1739 return false;
1740
1741 // Allow writing, which the documentation says we allow. This is safe
1742 // because MREMAP_DONTUNMAP only works for private mappings.
1743 if (mprotect(ptr, r.size, PROT_READ | PROT_WRITE) != 0) {
1744 munmap(ptr, r.size);
1745 return false;
1746 }
1747#elif defined(Q_OS_DARWIN)
1748 mach_port_t self = mach_task_self();
1749 vm_address_t addr = 0;
1750 vm_address_t mask = 0;
1751 bool anywhere = true;
1752 bool copy = true;
1753 vm_prot_t cur_prot = VM_PROT_READ | VM_PROT_WRITE;
1754 vm_prot_t max_prot = VM_PROT_ALL;
1755 kern_return_t res = vm_remap(self, &addr, r.size, mask, anywhere,
1756 self, vm_address_t(r.begin), copy, &cur_prot,
1757 &max_prot, VM_INHERIT_DEFAULT);
1758 if (res != KERN_SUCCESS)
1759 return false;
1760
1761 ptr = reinterpret_cast<void *>(addr);
1762 if ((max_prot & VM_PROT_WRITE) == 0 || mprotect(ptr, r.size, PROT_READ | PROT_WRITE) != 0) {
1763 munmap(ptr, r.size);
1764 return false;
1765 }
1766#endif
1767
1768 if (!ptr)
1769 return false;
1770 const char *newdata = static_cast<char *>(ptr) + r.offset;
1771 uncompressed = QByteArray::fromRawData(newdata, resource.uncompressedSize());
1772 mustUnmap = true;
1773 return true;
1774}
1775
1776void QResourceFileEnginePrivate::unmapUncompressed_sys()
1777{
1778 auto r = mappingBoundaries(uncompressed.constBegin(), uncompressed.size());
1779 QDynamicFileResourceRoot::unmap_sys(r.begin, r.size);
1780}
1781
1782#endif // !defined(QT_BOOTSTRAPPED)
1783
NSData * m_data
\inmodule QtCore \reentrant
FileOwner
\value OwnerUser The user who owns the file.
std::unique_ptr< Iterator > IteratorUniquePtr
FileName
These values are used to request a file name in a particular format.
\inmodule QtCore
Definition qatomic.h:112
bool ref() noexcept
bool deref() noexcept
\inmodule QtCore
Definition qbytearray.h:57
const_iterator constBegin() const noexcept
Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the first byte in the byte-ar...
Definition qbytearray.h:446
qsizetype size() const noexcept
Returns the number of bytes in this byte array.
Definition qbytearray.h:494
const char * constData() const noexcept
Returns a pointer to the const data stored in the byte array.
Definition qbytearray.h:124
bool isEmpty() const noexcept
Returns true if the byte array has size 0; otherwise returns false.
Definition qbytearray.h:107
void detach()
Definition qbytearray.h:625
bool isNull() const noexcept
Returns true if this byte array is null; otherwise returns false.
static QByteArray fromRawData(const char *data, qsizetype size)
Constructs a QByteArray that uses the first size bytes of the data array.
Definition qbytearray.h:409
\inmodule QtCore
\inmodule QtCore\reentrant
Definition qdatetime.h:283
static QDateTime fromMSecsSinceEpoch(qint64 msecs, const QTimeZone &timeZone)
static QString cleanPath(const QString &path)
Returns path with directory separators normalized (that is, platform-native separators converted to "...
Definition qdir.cpp:2398
@ FileModificationTime
Definition qfiledevice.h:61
int handle() const
Returns the file handle of the file.
\inmodule QtCore
Definition qfile.h:93
QFILE_MAYBE_NODISCARD bool open(OpenMode flags) override
Opens the file using OpenMode mode, returning true if successful; otherwise false.
Definition qfile.cpp:904
qint64 size() const override
\reimp
Definition qfile.cpp:1179
bool isOpen() const
Returns true if the device is open; otherwise returns false.
qint64 read(char *data, qint64 maxlen)
Reads at most maxSize bytes from the device into data, and returns the number of bytes read.
qsizetype size() const noexcept
Definition qlist.h:397
bool isEmpty() const noexcept
Definition qlist.h:401
void removeAt(qsizetype i)
Definition qlist.h:590
T takeAt(qsizetype i)
Definition qlist.h:609
const_reference at(qsizetype i) const noexcept
Definition qlist.h:446
void append(parameter_type t)
Definition qlist.h:458
void clear()
Definition qlist.h:434
@ AnyTerritory
Definition qlocale.h:568
\inmodule QtCore
Definition qmutex.h:309
bool caseSensitive() const override
Should return true if the underlying file system is case-sensitive; otherwise return false.
bool flush() override
Flushes the open file, returning true if successful; otherwise returns false.
qint64 read(char *data, qint64 maxlen) override
Reads a number of characters from the file into data.
bool close() override
Closes the file, returning true if successful; otherwise returns false.
bool open(QIODevice::OpenMode flags, std::optional< QFile::Permissions > permissions) override
Opens the file in the specified mode.
void setFileName(const QString &file) override
Sets the file engine's file name to file.
IteratorUniquePtr beginEntryList(const QString &path, QDir::Filters filters, const QStringList &filterNames) override
bool seek(qint64) override
Sets the file position to the given offset.
qint64 pos() const override
Returns the current file position.
QString fileName(QAbstractFileEngine::FileName file) const override
Return the file engine's current file name in the format specified by file.
QResourceFileEngine(const QString &path)
qint64 size() const override
Returns the size of the file.
FileFlags fileFlags(FileFlags type) const override
This function should return the set of OR'd flags that are true for the file engine's file,...
bool extension(Extension extension, const ExtensionOption *option=nullptr, ExtensionReturn *output=nullptr) override
QDateTime fileTime(QFile::FileTime time) const override
If time is BirthTime, return when the file was born (created).
virtual bool atEnd() const
uint ownerId(FileOwner) const override
If owner is OwnerUser return the ID of the user who owns the file.
bool supportsExtension(Extension extension) const override
void ensureChildren() const
static bool mayRemapData(const QResource &resource)
qint64 uncompressedSize() const Q_DECL_PURE_FUNCTION
QList< QResourceRoot * > related
QResource * q_ptr
void ensureInitialized() const
QResourcePrivate(QResource *_q)
QString absoluteFilePath
QStringList children
qsizetype decompress(char *buffer, qsizetype bufferSize) const
bool load(const QString &file)
\inmodule QtCore
Definition qresource.h:20
qint64 uncompressedSize() const
void setFileName(const QString &file)
Sets a QResource to point to file.
QResource(const QString &file=QString(), const QLocale &locale=QLocale())
Constructs a QResource pointing to file.
QStringList children() const
Returns a list of all resources in this directory, if the resource represents a file the list will be...
qint64 size() const
Returns the size of the stored data backing the resource.
const uchar * data() const
Returns direct access to a segment of read-only data, that this resource represents.
@ ZlibCompression
Definition qresource.h:24
@ ZstdCompression
Definition qresource.h:25
@ NoCompression
Definition qresource.h:23
static bool registerResource(const QString &rccFilename, const QString &resourceRoot=QString())
Registers the resource with the given rccFileName at the location in the resource tree specified by m...
QLocale locale() const
Returns the locale used to locate the data for the QResource.
QDateTime lastModified() const
void setLocale(const QLocale &locale)
Sets a QResource to only load the localization of resource to for locale.
QString absoluteFilePath() const
Returns the real path that this QResource represents, if the resource was found via the QDir::searchP...
QString fileName() const
Returns the full path to the file that this QResource represents as it was passed.
bool isValid() const
Returns true if the resource really exists in the resource hierarchy, false otherwise.
~QResource()
Releases the resources of the QResource object.
static bool unregisterResource(const QString &rccFilename, const QString &resourceRoot=QString())
Unregisters the resource with the given rccFileName at the location in the resource tree specified by...
QByteArray uncompressedData() const
bool isDir() const
Returns true if the resource represents a directory and thus may have children() in it,...
Compression compressionAlgorithm() const
\inmodule QtCore
\inmodule QtCore
Definition qstringview.h:78
constexpr qsizetype size() const noexcept
Returns the size of this string view, in UTF-16 code units (that is, surrogate pairs count as two for...
const_pointer data() const noexcept
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
qsizetype lastIndexOf(QChar c, Qt::CaseSensitivity cs=Qt::CaseSensitive) const noexcept
Definition qstring.h:296
QString mid(qsizetype position, qsizetype n=-1) const &
Definition qstring.cpp:5300
bool isEmpty() const noexcept
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:192
void clear()
Clears the contents of the string and makes it null.
Definition qstring.h:1252
qsizetype size() const noexcept
Returns the number of characters in this string.
Definition qstring.h:186
const QChar at(qsizetype i) const
Returns the character at the given index position in the string.
Definition qstring.h:1226
bool endsWith(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Returns true if the string ends with s; otherwise returns false.
Definition qstring.cpp:5506
QString & prepend(QChar c)
Definition qstring.h:478
static Q_CORE_EXPORT QString stdString(int errorCode=-1)
void extension()
[6]
Definition dialogs.cpp:230
#define this
Definition dialogs.cpp:9
QHash< int, QWidget * > hash
[35multi]
QMap< QString, QString > map
[6]
short next
Definition keywords.cpp:445
Q_QML_EXPORT QV4::ReturnedValue locale(QV4::ExecutionEngine *engine, const QString &localeName)
Provides locale specific properties and formatted data.
Combined button and popup list for selecting options.
void * HANDLE
constexpr Initialization Uninitialized
QString self
Definition language.cpp:58
static QByteArray cleaned(const QByteArray &input)
static jboolean copy(JNIEnv *, jobject)
#define Q_DECL_PURE_FUNCTION
QList< QString > QStringList
Constructs a string list that contains the given string, str.
constexpr bool operator!=(const timespec &t1, const timespec &t2)
typedef QByteArray(EGLAPIENTRYP PFNQGSGETDISPLAYSPROC)()
Flags
#define Q_GLOBAL_STATIC(TYPE, NAME,...)
uint qt_hash(QStringView key, uint chained) noexcept
Definition qhash.cpp:1325
#define qDebug
[1]
Definition qlogging.h:164
#define qWarning
Definition qlogging.h:166
return ret
static ControlElement< T > * ptr(QWidget *widget)
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
std::enable_if_t< std::is_unsigned_v< T >, bool > qAddOverflow(T v1, T v2, T *r)
Definition qnumeric.h:113
GLint location
GLboolean GLboolean GLboolean b
GLsizei const GLfloat * v
[13]
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLboolean r
[2]
GLuint GLuint end
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLfloat GLfloat f
GLenum GLuint buffer
GLenum type
GLbitfield flags
GLuint start
GLenum GLuint GLintptr offset
GLuint64 GLenum GLint fd
GLint ref
GLuint name
GLint GLint GLint GLint GLint GLint GLint GLbitfield mask
GLfloat n
GLfloat GLfloat GLfloat GLfloat h
GLuint res
GLuint segment
GLuint GLuint * names
GLenum const void * addr
GLdouble GLdouble t
Definition qopenglext.h:243
GLuint GLuint64EXT address
GLdouble GLdouble GLdouble GLdouble q
Definition qopenglext.h:259
GLsizei const GLchar *const * path
GLuint64EXT * result
[6]
GLuint GLenum option
GLenum GLsizei len
bool operator==(const QRandomGenerator &rng1, const QRandomGenerator &rng2)
Definition qrandom.cpp:1220
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define Q_ASSERT_X(cond, x, msg)
Definition qrandom.cpp:48
QtPrivate::QRegularExpressionMatchIteratorRangeBasedForIterator begin(const QRegularExpressionMatchIterator &iterator)
static ResourceList * resourceList()
#define MAP_FILE
#define RCC_FEATURE_SYMBOL(feature)
Definition qresource.cpp:65
static QRecursiveMutex & resourceMutex()
static auto mappingBoundaries(const void *location, qsizetype size)
Q_CORE_EXPORT bool qUnregisterResourceData(int version, const unsigned char *tree, const unsigned char *name, const unsigned char *data)
#define MAP_FAILED
static QString qt_resource_fixResourceRoot(QString r)
QList< QResourceRoot * > ResourceList
Q_CORE_EXPORT bool qRegisterResourceData(int version, const unsigned char *tree, const unsigned char *name, const unsigned char *data)
#define qUtf16Printable(string)
Definition qstring.h:1543
static QString absoluteFilePath(const Options *options, const QString &relativeFileName)
Definition main.cpp:1902
static bool hasNext(const Symbols &symbols, int i)
Definition main.cpp:78
#define QT_CONFIG(feature)
#define Q_UNUSED(x)
static bool match(const uchar *found, uint foundLen, const char *target, uint targetLen)
@ Q_RELOCATABLE_TYPE
Definition qtypeinfo.h:158
#define Q_DECLARE_TYPEINFO(TYPE, FLAGS)
Definition qtypeinfo.h:180
unsigned int quint32
Definition qtypes.h:50
unsigned char uchar
Definition qtypes.h:32
short qint16
Definition qtypes.h:47
unsigned short quint16
Definition qtypes.h:48
size_t quintptr
Definition qtypes.h:167
int qint32
Definition qtypes.h:49
ptrdiff_t qptrdiff
Definition qtypes.h:164
ptrdiff_t qsizetype
Definition qtypes.h:165
unsigned int uint
Definition qtypes.h:34
long long qint64
Definition qtypes.h:60
unsigned char quint8
Definition qtypes.h:46
static const uint base
Definition qurlidna.cpp:20
QT_BEGIN_NAMESPACE typedef uchar * output
QList< int > list
[14]
QFile file
[0]
QObject::connect nullptr
QSharedPointer< T > other(t)
[5]
const QStringList filters({"Image files (*.png *.xpm *.jpg)", "Text files (*.txt)", "Any files (*)" })
[6]
QLayoutItem * child
[0]