10#include <private/qlocking_p.h>
20#include <qshareddata.h>
21#include <qplatformdefs.h>
23#include "private/qabstractfileengine_p.h"
24#include "private/qduplicatetracker_p.h"
25#include "private/qnumeric_p.h"
26#include "private/qsimd_p.h"
27#include "private/qtools_p.h"
28#include "private/qsystemerror_p.h"
38#if defined(Q_OS_UNIX) && !defined(Q_OS_INTEGRITY)
43# define MREMAP_DONTUNMAP 4
44# elif defined(Q_OS_DARWIN)
45# include <mach/mach.h>
46# include <mach/vm_map.h>
50# include <qt_windows.h>
57using namespace Qt::StringLiterals;
62#if defined(__ELF__
) || defined(__APPLE__)
63# define RCC_FEATURE_SYMBOL(feature)
64 extern Q_CORE_EXPORT const quint8 qt_resourceFeature ## feature;
65 const quint8 qt_resourceFeature ## feature = 0
;
67# define RCC_FEATURE_SYMBOL(feature)
68 Q_CORE_EXPORT quint8 qResourceFeature ## feature() { return 0
; }
75RCC_FEATURE_SYMBOL(Zstd)
78#undef RCC_FEATURE_SYMBOL
84 explicit QStringSplitter(QStringView sv)
85 : m_data(sv.data()), m_len(sv.size())
91 while (m_pos < m_len && m_data[m_pos] == m_splitChar)
96 inline QStringView next()
98 const qsizetype start = m_pos;
99 while (m_pos < m_len && m_data[m_pos] != m_splitChar)
101 return QStringView(m_data + start, m_pos - start);
107 QChar m_splitChar = u'/';
118 CompressedZstd = 0x04
122 const uchar *tree, *names, *payloads;
124 inline int findOffset(
int node)
const {
return node * (14 + (version >= 0x02 ? 8 : 0)); }
125 uint hash(
int node)
const;
126 QString name(
int node)
const;
127 bool nameMatches(
int node, QStringView other)
const;
128 short flags(
int node)
const;
130 mutable QAtomicInt ref;
132 inline QResourceRoot(): tree(
nullptr), names(
nullptr), payloads(
nullptr), version(0) {}
133 inline QResourceRoot(
int version,
const uchar *t,
const uchar *n,
const uchar *d) { setSource(version, t, n, d); }
134 virtual ~QResourceRoot() =
default;
135 Q_DISABLE_COPY_MOVE(QResourceRoot)
136 int findNode(
const QString &path,
const QLocale &locale)
const;
137 inline bool isContainer(
int node)
const {
return flags(node) & Directory; }
138 QResource::Compression compressionAlgo(
int node)
140 uint compressionFlags = flags(node) & (Compressed | CompressedZstd);
141 if (compressionFlags == Compressed)
142 return QResource::ZlibCompression;
143 if (compressionFlags == CompressedZstd)
144 return QResource::ZstdCompression;
145 return QResource::NoCompression;
147 const uchar *data(
int node, qint64 *size)
const;
148 qint64 lastModified(
int node)
const;
149 QStringList children(
int node)
const;
150 virtual QStringView mappingRoot()
const {
return {}; }
151 bool mappingRootSubdir(
const QString &path, QString *match =
nullptr)
const;
152 inline bool operator==(
const QResourceRoot &other)
const
153 {
return tree == other.tree && names == other.names && payloads == other.payloads && version == other.version; }
154 inline bool operator!=(
const QResourceRoot &other)
const
155 {
return !operator==(other); }
156 enum ResourceRootType { Resource_Builtin, Resource_File, Resource_Buffer };
157 virtual ResourceRootType type()
const {
return Resource_Builtin; }
160 inline void setSource(
int v,
const uchar *t,
const uchar *n,
const uchar *d) {
168static QString cleanPath(
const QString &_path)
170 QString path = QDir::cleanPath(_path);
173 if (path.startsWith(
"//"_L1))
181struct QResourceGlobalData
183 QRecursiveMutex resourceMutex;
187Q_GLOBAL_STATIC(QResourceGlobalData, resourceGlobalData)
190{
return resourceGlobalData->resourceMutex; }
193{
return &resourceGlobalData->resourceList; }
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
299 bool load(
const QString &file);
316 Q_DECLARE_PUBLIC(QResource)
321 absoluteFilePath.clear();
322 compressionAlgo = QResource::NoCompression;
328 for (
int i = 0; i < related.size(); ++i) {
329 QResourceRoot *root = related.at(i);
330 if (!root->ref.deref())
339 const auto locker = qt_scoped_lock(resourceMutex());
341 QString cleaned = cleanPath(file);
342 for (
int i = 0; i < list->size(); ++i) {
343 QResourceRoot *res = list->at(i);
344 const int node = res->findNode(cleaned, locale);
346 if (related.isEmpty()) {
349 data = res->data(node, &size);
350 compressionAlgo = res->compressionAlgo(node);
354 compressionAlgo = QResource::NoCompression;
356 lastModified = res->lastModified(node);
357 }
else if (res->isContainer(node) !=
container) {
358 qWarning(
"QResourceInfo: Resource [%s] has both data and children!",
359 file.toLatin1().constData());
363 }
else if (res->mappingRootSubdir(file)) {
367 compressionAlgo = QResource::NoCompression;
373 return !related.isEmpty();
378 if (!related.isEmpty())
380 if (resourceGlobalData.isDestroyed())
383 if (fileName ==
":"_L1)
384 that->fileName += u'/';
385 that->absoluteFilePath = fileName;
386 if (!that->absoluteFilePath.startsWith(u':'))
387 that->absoluteFilePath.prepend(u':');
389 QStringView path(fileName);
390 if (path.startsWith(u':'))
393 if (path.startsWith(u'/')) {
397 const QString searchPath(u'/' + path);
399 that->absoluteFilePath = u':' + searchPath;
406 if (!children.isEmpty() || !container || related.isEmpty())
409 QString path = absoluteFilePath, k;
410 if (path.startsWith(u':'))
412 QDuplicateTracker<QString> kids(related.size());
413 QString cleaned = cleanPath(path);
414 for (
int i = 0; i < related.size(); ++i) {
415 QResourceRoot *res = related.at(i);
416 if (res->mappingRootSubdir(path, &k) && !k.isEmpty()) {
417 if (!kids.hasSeen(k))
420 const int node = res->findNode(cleaned, locale);
422 QStringList related_children = res->children(node);
423 for (
int kid = 0; kid < related_children.size(); ++kid) {
424 k = related_children.at(kid);
425 if (!kids.hasSeen(k))
435 switch (compressionAlgo) {
436 case QResource::NoCompression:
439 case QResource::ZlibCompression:
440#ifndef QT_NO_COMPRESS
441 if (size_t(size) >=
sizeof(quint32))
442 return qFromBigEndian<quint32>(data);
444 Q_ASSERT(!
"QResource: Qt built without support for Zlib compression");
449 case QResource::ZstdCompression: {
451 size_t n = ZSTD_getFrameContentSize(data, size);
452 return ZSTD_isError(n) ? -1 : qint64(n);
455 Q_ASSERT(!
"QResource: Qt built without support for Zstd compression");
466#if defined(QT_NO_COMPRESS) && !QT_CONFIG(zstd)
468 Q_UNUSED(bufferSize);
471 switch (compressionAlgo) {
472 case QResource::NoCompression:
476 case QResource::ZlibCompression: {
477#ifndef QT_NO_COMPRESS
478 uLong len = uLong(bufferSize);
479 int res = ::uncompress(
reinterpret_cast<Bytef *>(buffer), &len, data +
sizeof(quint32),
480 uLong(size -
sizeof(quint32)));
482 qWarning(
"QResource: error decompressing zlib content (%d)", res);
491 case QResource::ZstdCompression: {
493 size_t usize = ZSTD_decompress(buffer, bufferSize, data, size);
494 if (ZSTD_isError(usize)) {
495 qWarning(
"QResource: error decompressing zstd content: %s", ZSTD_getErrorName(usize));
509
510
511
512
513
515QResource::QResource(
const QString &file,
const QLocale &locale)
516 : d_ptr(
new QResourcePrivate(
this, file, locale))
521
522
523QResource::~QResource()
528
529
530
531
532
533
535void QResource::setLocale(
const QLocale &locale)
543
544
546QLocale QResource::locale()
const
548 Q_D(
const QResource);
553
554
555
556
557
558
560void QResource::setFileName(
const QString &file)
568
569
570
571
572
574QString QResource::fileName()
const
576 Q_D(
const QResource);
577 d->ensureInitialized();
582
583
584
585
586
588QString QResource::absoluteFilePath()
const
590 Q_D(
const QResource);
591 d->ensureInitialized();
592 return d->absoluteFilePath;
596
597
598
599
601bool QResource::isValid()
const
603 Q_D(
const QResource);
604 d->ensureInitialized();
605 return !d->related.isEmpty();
609
610
611
612
613
614
615
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635QResource::Compression QResource::compressionAlgorithm()
const
637 Q_D(
const QResource);
638 d->ensureInitialized();
639 return Compression(d->compressionAlgo);
643
644
645
646
647
648
649
651qint64 QResource::size()
const
653 Q_D(
const QResource);
654 d->ensureInitialized();
659
660
661
662
663
664
665
666
667
668qint64 QResource::uncompressedSize()
const
670 Q_D(
const QResource);
671 d->ensureInitialized();
672 return d->uncompressedSize();
676
677
678
679
680
681
682
684const uchar *QResource::data()
const
686 Q_D(
const QResource);
687 d->ensureInitialized();
692
693
694
695
696
697
698
699
700
701
702
704QByteArray QResource::uncompressedData()
const
706 Q_D(
const QResource);
707 qint64 n = uncompressedSize();
710 if (n > std::numeric_limits<QByteArray::size_type>::max()) {
711 qWarning(
"QResource: compressed content does not fit into a QByteArray; use QFile instead");
714 if (d->compressionAlgo == NoCompression)
715 return QByteArray::fromRawData(
reinterpret_cast<
const char *>(d->data), n);
718 QByteArray result(n, Qt::Uninitialized);
719 n = d->decompress(result.data(), n);
728
729
730
731
732
733QDateTime QResource::lastModified()
const
735 Q_D(
const QResource);
736 d->ensureInitialized();
737 return d->lastModified ? QDateTime::fromMSecsSinceEpoch(d->lastModified) : QDateTime();
741
742
743
744
745
747bool QResource::isDir()
const
749 Q_D(
const QResource);
750 d->ensureInitialized();
755
756
757
758
759
761QStringList QResource::children()
const
763 Q_D(
const QResource);
768inline uint QResourceRoot::hash(
int node)
const
772 const int offset = findOffset(node);
773 qint32 name_offset = qFromBigEndian<qint32>(tree + offset);
775 return qFromBigEndian<quint32>(names + name_offset);
777inline QString QResourceRoot::name(
int node)
const
781 const int offset = findOffset(node);
784 qint32 name_offset = qFromBigEndian<qint32>(tree + offset);
785 quint16 name_length = qFromBigEndian<qint16>(names + name_offset);
789 ret.resize(name_length);
790 QChar *strData = ret.data();
791 qFromBigEndian<
char16_t>(names + name_offset, name_length, strData);
795inline bool QResourceRoot::nameMatches(
int node, QStringView other)
const
798 return other.isEmpty();
799 const int offset = findOffset(node);
801 qint32 name_offset = qFromBigEndian<qint32>(tree + offset);
802 qsizetype name_length = qFromBigEndian<qint16>(names + name_offset);
806 if (other.size() != name_length)
808 const QChar *strData =
reinterpret_cast<
const QChar *>(names + name_offset);
809 for (qsizetype ch = 0; ch < name_length; ++ch) {
810 if (other.at(ch) != qFromBigEndian<
char16_t>(strData + ch))
816int QResourceRoot::findNode(
const QString &_path,
const QLocale &locale)
const
818 QStringView path = _path;
820 const QStringView root = mappingRoot();
821 if (!root.isEmpty()) {
825 const bool rootEndsWithSlash = root.endsWith(u'/');
826 const qsizetype prefixLength = root.size() + !rootEndsWithSlash;
827 if (path.size() >= prefixLength && path.startsWith(root)
828 && (rootEndsWithSlash || path.at(root.size()) == u'/')) {
829 path = path.sliced(prefixLength - 1);
836#ifdef DEBUG_RESOURCE_MATCH
837 qDebug() <<
"!!!!" <<
"START" << path << locale.territory() << locale.language();
844 qint32 child_count = qFromBigEndian<qint32>(tree + 6);
845 qint32 child = qFromBigEndian<qint32>(tree + 10);
850 QStringSplitter splitter(path);
851 while (child_count && splitter.hasNext()) {
852 QStringView segment = splitter.next();
854#ifdef DEBUG_RESOURCE_MATCH
855 qDebug() <<
" CHILDREN" << segment;
856 for (
int j = 0; j < child_count; ++j) {
857 qDebug() <<
" " << child + j <<
" :: " << name(child + j);
860 const uint h = qt_hash(segment);
863 int l = 0, r = child_count - 1;
864 int sub_node = (l + r + 1) / 2;
866 const uint sub_node_hash = hash(child + sub_node);
867 if (h == sub_node_hash)
869 else if (h < sub_node_hash)
873 sub_node = (l + r + 1) / 2;
879 if (hash(sub_node) == h) {
880 while (sub_node > child && hash(sub_node - 1) == h)
882 for (; sub_node < child + child_count && hash(sub_node) == h;
884 if (nameMatches(sub_node, segment)) {
886 int offset = findOffset(sub_node);
887#ifdef DEBUG_RESOURCE_MATCH
888 qDebug() <<
" TRY" << sub_node << name(sub_node) << offset;
892 const qint16 flags = qFromBigEndian<qint16>(tree + offset);
895 if (!splitter.hasNext()) {
896 if (!(flags & Directory)) {
897 const qint16 territory = qFromBigEndian<qint16>(tree + offset);
900 const qint16 language = qFromBigEndian<qint16>(tree + offset);
902#ifdef DEBUG_RESOURCE_MATCH
903 qDebug() <<
" " <<
"LOCALE" << country << language;
905 if (territory == locale.territory() && language == locale.language()) {
906#ifdef DEBUG_RESOURCE_MATCH
907 qDebug() <<
"!!!!" <<
"FINISHED" << __LINE__ << sub_node;
910 }
else if ((territory == QLocale::AnyTerritory
911 && language == locale.language())
912 || (territory == QLocale::AnyTerritory
913 && language == QLocale::C
919#ifdef DEBUG_RESOURCE_MATCH
920 qDebug() <<
"!!!!" <<
"FINISHED" << __LINE__ << sub_node;
927 if (!(flags & Directory))
930 child_count = qFromBigEndian<qint32>(tree + offset);
932 child = qFromBigEndian<qint32>(tree + offset);
940#ifdef DEBUG_RESOURCE_MATCH
941 qDebug() <<
"!!!!" <<
"FINISHED" << __LINE__ << node;
945short QResourceRoot::flags(
int node)
const
949 const int offset = findOffset(node) + 4;
950 return qFromBigEndian<qint16>(tree + offset);
952const uchar *QResourceRoot::data(
int node, qint64 *size)
const
958 int offset = findOffset(node) + 4;
960 const qint16 flags = qFromBigEndian<qint16>(tree + offset);
965 if (!(flags & Directory)) {
966 const qint32 data_offset = qFromBigEndian<qint32>(tree + offset);
967 const quint32 data_length = qFromBigEndian<quint32>(payloads + data_offset);
968 const uchar *ret = payloads + data_offset + 4;
976qint64 QResourceRoot::lastModified(
int node)
const
978 if (node == -1 || version < 0x02)
981 const int offset = findOffset(node) + 14;
983 return qFromBigEndian<qint64>(tree + offset);
986QStringList QResourceRoot::children(
int node)
const
989 return QStringList();
990 int offset = findOffset(node) + 4;
992 const qint16 flags = qFromBigEndian<qint16>(tree + offset);
996 if (flags & Directory) {
997 const qint32 child_count = qFromBigEndian<qint32>(tree + offset);
999 const qint32 child_off = qFromBigEndian<qint32>(tree + offset);
1000 ret.reserve(child_count);
1001 for (
int i = child_off; i < child_off + child_count; ++i)
1006bool QResourceRoot::mappingRootSubdir(
const QString &path, QString *match)
const
1008 const QStringView root = mappingRoot();
1012 QStringSplitter rootIt(root);
1013 QStringSplitter pathIt(path);
1014 while (rootIt.hasNext()) {
1015 if (pathIt.hasNext()) {
1016 if (rootIt.next() != pathIt.next())
1021 *match = rootIt.next().toString();
1026 return !pathIt.hasNext();
1030 const unsigned char *name,
const unsigned char *data)
1032 if (resourceGlobalData.isDestroyed())
1034 const auto locker = qt_scoped_lock(resourceMutex());
1036 if (version >= 0x01 && version <= 0x3) {
1038 QResourceRoot res(version, tree, name, data);
1039 for (
int i = 0; i < list->size(); ++i) {
1040 if (*list->at(i) == res) {
1046 QResourceRoot *root =
new QResourceRoot(version, tree, name, data);
1056 const unsigned char *name,
const unsigned char *data)
1058 if (resourceGlobalData.isDestroyed())
1061 const auto locker = qt_scoped_lock(resourceMutex());
1062 if (version >= 0x01 && version <= 0x3) {
1063 QResourceRoot res(version, tree, name, data);
1065 for (
int i = 0; i < list->size();) {
1066 if (*list->at(i) == res) {
1067 QResourceRoot *root = list->takeAt(i);
1068 if (!root->ref.deref())
1081class QDynamicBufferResourceRoot :
public QResourceRoot
1084 const uchar *buffer;
1087 inline QDynamicBufferResourceRoot(
const QString &_root) : root(_root), buffer(
nullptr) { }
1088 inline ~QDynamicBufferResourceRoot() { }
1089 inline const uchar *mappingBuffer()
const {
return buffer; }
1090 QStringView mappingRoot()
const override {
return root; }
1091 ResourceRootType type()
const override {
return Resource_Buffer; }
1094 bool registerSelf(
const uchar *b, qsizetype size,
const QString &source = {})
1097 if (size >= 0 && size < 20)
1104 if (b[offset + 0] !=
'q' || b[offset + 1] !=
'r' || b[offset + 2] !=
'e'
1105 || b[offset + 3] !=
's') {
1110 const int version = qFromBigEndian<qint32>(b + offset);
1113 const int tree_offset = qFromBigEndian<qint32>(b + offset);
1116 const int data_offset = qFromBigEndian<qint32>(b + offset);
1119 const int name_offset = qFromBigEndian<qint32>(b + offset);
1122 quint32 file_flags = 0;
1124 file_flags = qFromBigEndian<qint32>(b + offset);
1129 if (size >= 0 && (tree_offset >= size || data_offset >= size || name_offset >= size))
1133 quint32 acceptableFlags = 0;
1134#ifndef QT_NO_COMPRESS
1135 acceptableFlags |= Compressed;
1137 if (QT_CONFIG(zstd))
1138 acceptableFlags |= CompressedZstd;
1139 if (
const quint32 unsupportedFlags = file_flags & ~acceptableFlags) {
1140 const QString id = source.isEmpty() ? QString::asprintf(
"<buffer at %p>", b) : source;
1141 qWarning(
"QResource: %s uses unsupported compression flags 0x%x (supported: 0x%x)",
1142 qUtf8Printable(id), unsupportedFlags, acceptableFlags);
1146 if (version >= 0x01 && version <= 0x03) {
1148 setSource(version, b + tree_offset, b + name_offset, b + data_offset);
1155class QDynamicFileResourceRoot :
public QDynamicBufferResourceRoot
1158 static uchar *map_sys(QFile &file, qint64 base, qsizetype size);
1159 static void unmap_sys(
void *base, qsizetype size);
1164 uchar *unmapPointer;
1165 qsizetype unmapLength;
1168 QDynamicFileResourceRoot(
const QString &_root)
1169 : QDynamicBufferResourceRoot(_root), unmapPointer(
nullptr), unmapLength(0)
1171 ~QDynamicFileResourceRoot() {
1172 if (wasMemoryMapped())
1173 unmap_sys(unmapPointer, unmapLength);
1175 delete[] mappingBuffer();
1177 QString mappingFile()
const {
return fileName; }
1178 ResourceRootType type()
const override {
return Resource_File; }
1179 bool wasMemoryMapped()
const {
return unmapPointer; }
1181 bool registerSelf(
const QString &f);
1189# define MAP_FAILED reinterpret_cast<void *>(-1
)
1192void QDynamicFileResourceRoot::unmap_sys(
void *base, qsizetype size)
1194#if defined(QT_USE_MMAP)
1196#elif defined(Q_OS_WIN)
1198 UnmapViewOfFile(
reinterpret_cast<
void *>(base));
1203uchar *QDynamicFileResourceRoot::map_sys(QFile &file, qint64 offset, qsizetype size)
1205 Q_ASSERT(file.isOpen());
1206 void *ptr =
nullptr;
1208 size = qMin(file.size() - offset, (std::numeric_limits<qsizetype>::max)());
1209 int fd = file.handle();
1213#if defined(QT_USE_MMAP)
1214 int protection = PROT_READ;
1215 int flags = MAP_FILE | MAP_PRIVATE;
1216 ptr = QT_MMAP(
nullptr, size, protection, flags, fd, offset);
1217 if (ptr == MAP_FAILED)
1219#elif defined(Q_OS_WIN)
1220 HANDLE fileHandle =
reinterpret_cast<HANDLE>(_get_osfhandle(fd));
1221 if (fileHandle != INVALID_HANDLE_VALUE) {
1222 HANDLE mapHandle = CreateFileMapping(fileHandle, 0, PAGE_WRITECOPY, 0, 0, 0);
1224 ptr = MapViewOfFile(mapHandle, FILE_MAP_COPY, DWORD(offset >> 32), DWORD(offset), size);
1225 CloseHandle(mapHandle);
1229 return static_cast<uchar *>(ptr);
1232bool QDynamicFileResourceRoot::registerSelf(
const QString &f)
1235 if (!file.open(QIODevice::ReadOnly))
1238 qint64 data_len = file.size();
1239 if (data_len > std::numeric_limits<qsizetype>::max())
1242 uchar *data = map_sys(file, 0, data_len);
1243 bool fromMM = !!data;
1247 data =
new uchar[data_len];
1248 ok = (data_len == file.read(
reinterpret_cast<
char *>(data), data_len));
1256 if (data && QDynamicBufferResourceRoot::registerSelf(data, data_len, f)) {
1258 unmapPointer = data;
1259 unmapLength = data_len;
1270 if (r.startsWith(u':'))
1273 r = QDir::cleanPath(r);
1279
1280
1281
1282
1283
1284
1285
1286
1288bool QResource::registerResource(
const QString &rccFilename,
const QString &resourceRoot)
1290 QString r = qt_resource_fixResourceRoot(resourceRoot);
1291 if (!r.isEmpty() && r[0] != u'/') {
1292 qWarning(
"QDir::registerResource: Registering a resource [%ls] must be rooted in an "
1293 "absolute path (start with /) [%ls]",
1294 qUtf16Printable(rccFilename), qUtf16Printable(resourceRoot));
1298 QDynamicFileResourceRoot *root =
new QDynamicFileResourceRoot(r);
1299 if (root->registerSelf(rccFilename)) {
1301 const auto locker = qt_scoped_lock(resourceMutex());
1302 resourceList()->append(root);
1310
1311
1312
1313
1314
1315
1316
1317
1318
1320bool QResource::unregisterResource(
const QString &rccFilename,
const QString &resourceRoot)
1322 QString r = qt_resource_fixResourceRoot(resourceRoot);
1324 const auto locker = qt_scoped_lock(resourceMutex());
1325 ResourceList *list = resourceList();
1326 for (
int i = 0; i < list->size(); ++i) {
1327 QResourceRoot *res = list->at(i);
1328 if (res->type() == QResourceRoot::Resource_File) {
1329 QDynamicFileResourceRoot *root =
reinterpret_cast<QDynamicFileResourceRoot *>(res);
1330 if (root->mappingFile() == rccFilename && root->mappingRoot() == r) {
1332 if (!root->ref.deref()) {
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1357bool QResource::registerResource(
const uchar *rccData,
const QString &resourceRoot)
1359 QString r = qt_resource_fixResourceRoot(resourceRoot);
1360 if (!r.isEmpty() && r[0] != u'/') {
1361 qWarning(
"QDir::registerResource: Registering a resource [%p] must be rooted in an "
1362 "absolute path (start with /) [%ls]",
1363 rccData, qUtf16Printable(resourceRoot));
1367 QDynamicBufferResourceRoot *root =
new QDynamicBufferResourceRoot(r);
1368 if (root->registerSelf(rccData, -1)) {
1370 const auto locker = qt_scoped_lock(resourceMutex());
1371 resourceList()->append(root);
1379
1380
1381
1382
1383
1384
1385
1386
1387
1389bool QResource::unregisterResource(
const uchar *rccData,
const QString &resourceRoot)
1391 QString r = qt_resource_fixResourceRoot(resourceRoot);
1393 const auto locker = qt_scoped_lock(resourceMutex());
1394 ResourceList *list = resourceList();
1395 for (
int i = 0; i < list->size(); ++i) {
1396 QResourceRoot *res = list->at(i);
1397 if (res->type() == QResourceRoot::Resource_Buffer) {
1398 QDynamicBufferResourceRoot *root =
reinterpret_cast<QDynamicBufferResourceRoot *>(res);
1399 if (root->mappingBuffer() == rccData && root->mappingRoot() == r) {
1401 if (!root->ref.deref()) {
1412#if !defined(QT_BOOTSTRAPPED)
1417 Q_DECLARE_PUBLIC(QResourceFileEngine)
1420 bool unmap(uchar *ptr);
1421 void uncompress()
const;
1422 void mapUncompressed();
1423 bool mapUncompressed_sys();
1424 void unmapUncompressed_sys();
1428 bool mustUnmap =
false;
1431 static constexpr qsizetype RemapCompressedThreshold = 16384;
1439 unmapUncompressed_sys();
1449 QAbstractFileEngine(*
new QResourceFileEnginePrivate(
this))
1451 Q_D(QResourceFileEngine);
1452 d->resource.setFileName(file);
1461 Q_D(QResourceFileEngine);
1462 d->resource.setFileName(file);
1466 std::optional<QFile::Permissions> permissions)
1468 Q_UNUSED(permissions);
1470 Q_D(QResourceFileEngine);
1471 if (d->resource.fileName().isEmpty()) {
1472 qWarning(
"QResourceFileEngine::open: Missing file name");
1475 if (flags & QIODevice::WriteOnly)
1477 if (d->resource.compressionAlgorithm() != QResource::NoCompression) {
1479 if (d->uncompressed.isNull()) {
1480 d->errorString = QSystemError::stdString(EIO);
1484 if (!d->resource.isValid()) {
1485 d->errorString = QSystemError::stdString(ENOENT);
1493 Q_D(QResourceFileEngine);
1505 Q_D(QResourceFileEngine);
1506 if (len > size() - d->offset)
1507 len = size() - d->offset;
1510 if (!d->uncompressed.isNull())
1511 memcpy(data, d->uncompressed.constData() + d->offset, len);
1513 memcpy(data, d->resource.data() + d->offset, len);
1520 Q_D(
const QResourceFileEngine);
1521 return d->resource.isValid() ? d->resource.uncompressedSize() : 0;
1526 Q_D(
const QResourceFileEngine);
1532 Q_D(
const QResourceFileEngine);
1533 if (!d->resource.isValid())
1535 return d->offset == size();
1540 Q_D(QResourceFileEngine);
1541 if (!d->resource.isValid())
1544 if (d->offset > size())
1552 Q_D(
const QResourceFileEngine);
1553 QAbstractFileEngine::FileFlags ret;
1554 if (!d->resource.isValid())
1557 if (type & PermsMask)
1558 ret |= QAbstractFileEngine::FileFlags(ReadOwnerPerm | ReadUserPerm | ReadGroupPerm
1560 if (type & TypesMask) {
1561 if (d->resource.isDir())
1562 ret |= DirectoryType;
1566 if (type & FlagsMask) {
1568 if (d->resource.absoluteFilePath() ==
":/"_L1)
1576 Q_D(
const QResourceFileEngine);
1577 if (file == BaseName) {
1578 const qsizetype slash = d->resource.fileName().lastIndexOf(u'/');
1580 return d->resource.fileName();
1581 return d->resource.fileName().mid(slash + 1);
1582 }
else if (file == PathName || file == AbsolutePathName) {
1583 const QString path = (file == AbsolutePathName) ? d->resource.absoluteFilePath()
1584 : d->resource.fileName();
1585 const qsizetype slash = path.lastIndexOf(u'/');
1588 else if (slash <= 1)
1590 return path.left(slash);
1592 }
else if (file == CanonicalName || file == CanonicalPathName) {
1593 const QString absoluteFilePath = d->resource.absoluteFilePath();
1594 if (file == CanonicalPathName) {
1595 const qsizetype slash = absoluteFilePath.lastIndexOf(u'/');
1597 return absoluteFilePath.left(slash);
1599 return absoluteFilePath;
1601 return d->resource.fileName();
1606 static const uint nobodyID =
static_cast<uint>(-2);
1612 Q_D(
const QResourceFileEngine);
1613 if (time == QFile::FileModificationTime)
1614 return d->resource.lastModified();
1619
1620
1621QAbstractFileEngine::IteratorUniquePtr
1623 const QStringList &filterNames)
1630 Q_D(QResourceFileEngine);
1631 if (extension == MapExtension) {
1632 const auto *options =
static_cast<
const MapExtensionOption *>(option);
1633 auto *returnValue =
static_cast<MapExtensionReturn *>(output);
1634 returnValue->address = d->map(options->offset, options->size, options->flags);
1635 return (returnValue->address !=
nullptr);
1637 if (extension == UnMapExtension) {
1638 const auto *options =
static_cast<
const UnMapExtensionOption *>(option);
1639 return d->unmap(options->address);
1646 return (extension == UnMapExtension || extension == MapExtension);
1651 Q_Q(QResourceFileEngine);
1652 Q_ASSERT_X(resource.compressionAlgorithm() == QResource::NoCompression
1653 || !uncompressed.isNull(),
"QFile::map()",
1654 "open() should have uncompressed compressed resources");
1656 qint64 max = resource.uncompressedSize();
1658 if (offset < 0 || size <= 0 || !resource.isValid() ||
1659 qAddOverflow(offset, size, &end) || end > max) {
1660 q->setError(QFile::UnspecifiedError, QString());
1664 const uchar *address =
reinterpret_cast<
const uchar *>(uncompressed.constBegin());
1665 if (!uncompressed.isNull())
1666 return const_cast<uchar *>(address) + offset;
1669 address = resource.data();
1670 if (flags & QFile::MapPrivateOption) {
1673 address =
reinterpret_cast<
const uchar *>(uncompressed.constData());
1676 return const_cast<uchar *>(address) + offset;
1687 if (resource.compressionAlgorithm() == QResource::NoCompression
1688 || !uncompressed.isEmpty() || resource.size() == 0)
1690 uncompressed = resource.uncompressedData();
1695 Q_ASSERT(resource.compressionAlgorithm() == QResource::NoCompression);
1696 if (!uncompressed.isNull())
1699 if (resource.uncompressedSize() >= RemapCompressedThreshold) {
1700 if (mapUncompressed_sys())
1704 uncompressed = resource.uncompressedData();
1705 uncompressed.detach();
1708#if defined(MREMAP_MAYMOVE) && defined(MREMAP_DONTUNMAP)
1709inline bool QResourcePrivate::mayRemapData(
const QResource &resource)
1711 auto d = resource.d_func();
1717 const QResourceRoot *root = d->related.at(0);
1719 switch (root->type()) {
1720 case QResourceRoot::Resource_Builtin:
1722 case QResourceRoot::Resource_Buffer:
1724 case QResourceRoot::Resource_File:
1728 auto df =
static_cast<
const QDynamicFileResourceRoot *>(root);
1729 return df->wasMemoryMapped();
1737 auto getpagesize = [] {
1738 SYSTEM_INFO sysinfo;
1739 ::GetSystemInfo(&sysinfo);
1740 return sysinfo.dwAllocationGranularity;
1749 const quintptr pageMask = getpagesize() - 1;
1750 quintptr data = quintptr(location);
1751 quintptr begin = data & ~pageMask;
1752 quintptr end = (data + size + pageMask) & ~pageMask;
1753 r.begin =
reinterpret_cast<
void *>(begin);
1754 r.size = end - begin;
1755 r.offset = data & pageMask;
1761 auto r = mappingBoundaries(resource.data(), resource.uncompressedSize());
1762 void *ptr =
nullptr;
1764#if defined(MREMAP_MAYMOVE) && defined(MREMAP_DONTUNMAP)
1770 if (!QResourcePrivate::mayRemapData(resource))
1773 ptr = mremap(r.begin, r.size, r.size, MREMAP_MAYMOVE | MREMAP_DONTUNMAP);
1774 if (ptr == MAP_FAILED)
1779 if (mprotect(ptr, r.size, PROT_READ | PROT_WRITE) != 0) {
1780 munmap(ptr, r.size);
1783#elif defined(Q_OS_DARWIN)
1784 mach_port_t self = mach_task_self();
1785 vm_address_t addr = 0;
1786 vm_address_t mask = 0;
1787 bool anywhere =
true;
1789 vm_prot_t cur_prot = VM_PROT_READ | VM_PROT_WRITE;
1790 vm_prot_t max_prot = VM_PROT_ALL;
1791 kern_return_t res = vm_remap(self, &addr, r.size, mask, anywhere,
1792 self, vm_address_t(r.begin), copy, &cur_prot,
1793 &max_prot, VM_INHERIT_DEFAULT);
1794 if (res != KERN_SUCCESS)
1797 ptr =
reinterpret_cast<
void *>(addr);
1798 if ((max_prot & VM_PROT_WRITE) == 0 || mprotect(ptr, r.size, PROT_READ | PROT_WRITE) != 0) {
1799 munmap(ptr, r.size);
1806 const char *newdata =
static_cast<
char *>(ptr) + r.offset;
1807 uncompressed = QByteArray::fromRawData(newdata, resource.uncompressedSize());
1814 auto r = mappingBoundaries(uncompressed.constBegin(), uncompressed.size());
1815 QDynamicFileResourceRoot::unmap_sys(r.begin, r.size);
~QResourceFileEnginePrivate()
QResourceFileEnginePrivate(QAbstractFileEngine *q)
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.
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.
qint64 size() const override
Returns the size of the file.
bool extension(Extension extension, const ExtensionOption *option=nullptr, ExtensionReturn *output=nullptr) override
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)
void ensureInitialized() const
QResourcePrivate(QResource *_q, const QString &file, const QLocale &locale)
qsizetype decompress(char *buffer, qsizetype bufferSize) const
bool load(const QString &file)
QVarLengthArray< QResourceRoot *, 1 > related
static ResourceList * resourceList()
#define RCC_FEATURE_SYMBOL(feature)
static QRecursiveMutex & resourceMutex()
static auto mappingBoundaries(const void *location, qsizetype size)
QList< QResourceRoot * > ResourceList
Q_CORE_EXPORT bool qUnregisterResourceData(int version, const unsigned char *tree, const unsigned char *name, const unsigned char *data)
static QString qt_resource_fixResourceRoot(QString r)
Q_CORE_EXPORT bool qRegisterResourceData(int version, const unsigned char *tree, const unsigned char *name, const unsigned char *data)