10#include <private/qlocking_p.h>
19#include <qshareddata.h>
20#include <qplatformdefs.h>
22#include "private/qabstractfileengine_p.h"
23#include "private/qduplicatetracker_p.h"
24#include "private/qnumeric_p.h"
25#include "private/qsimd_p.h"
26#include "private/qtools_p.h"
27#include "private/qsystemerror_p.h"
37#if defined(Q_OS_UNIX) && !defined(Q_OS_INTEGRITY)
42# define MREMAP_DONTUNMAP 4
43# elif defined(Q_OS_DARWIN)
44# include <mach/mach.h>
45# include <mach/vm_map.h>
49# include <qt_windows.h>
56using namespace Qt::StringLiterals;
61#if defined(__ELF__
) || defined(__APPLE__)
62# define RCC_FEATURE_SYMBOL(feature)
63 extern Q_CORE_EXPORT const quint8 qt_resourceFeature ## feature;
64 const quint8 qt_resourceFeature ## feature = 0
;
66# define RCC_FEATURE_SYMBOL(feature)
67 Q_CORE_EXPORT quint8 qResourceFeature ## feature() { return 0
; }
74RCC_FEATURE_SYMBOL(Zstd)
77#undef RCC_FEATURE_SYMBOL
83 explicit QStringSplitter(QStringView sv)
84 : m_data(sv.data()), m_len(sv.size())
90 while (m_pos < m_len && m_data[m_pos] == m_splitChar)
95 inline QStringView next()
97 const qsizetype start = m_pos;
98 while (m_pos < m_len && m_data[m_pos] != m_splitChar)
100 return QStringView(m_data + start, m_pos - start);
106 QChar m_splitChar = u'/';
117 CompressedZstd = 0x04
121 const uchar *tree, *names, *payloads;
123 inline int findOffset(
int node)
const {
return node * (14 + (version >= 0x02 ? 8 : 0)); }
124 uint hash(
int node)
const;
125 QString name(
int node)
const;
126 short flags(
int node)
const;
128 mutable QAtomicInt ref;
130 inline QResourceRoot(): tree(
nullptr), names(
nullptr), payloads(
nullptr), version(0) {}
131 inline QResourceRoot(
int version,
const uchar *t,
const uchar *n,
const uchar *d) { setSource(version, t, n, d); }
132 virtual ~QResourceRoot() { }
133 int findNode(
const QString &path,
const QLocale &locale=QLocale())
const;
134 inline bool isContainer(
int node)
const {
return flags(node) & Directory; }
135 QResource::Compression compressionAlgo(
int node)
137 uint compressionFlags = flags(node) & (Compressed | CompressedZstd);
138 if (compressionFlags == Compressed)
139 return QResource::ZlibCompression;
140 if (compressionFlags == CompressedZstd)
141 return QResource::ZstdCompression;
142 return QResource::NoCompression;
144 const uchar *data(
int node, qint64 *size)
const;
145 qint64 lastModified(
int node)
const;
146 QStringList children(
int node)
const;
147 virtual QString mappingRoot()
const {
return QString(); }
148 bool mappingRootSubdir(
const QString &path, QString *match =
nullptr)
const;
149 inline bool operator==(
const QResourceRoot &other)
const
150 {
return tree == other.tree && names == other.names && payloads == other.payloads && version == other.version; }
151 inline bool operator!=(
const QResourceRoot &other)
const
152 {
return !operator==(other); }
153 enum ResourceRootType { Resource_Builtin, Resource_File, Resource_Buffer };
154 virtual ResourceRootType type()
const {
return Resource_Builtin; }
157 inline void setSource(
int v,
const uchar *t,
const uchar *n,
const uchar *d) {
165static QString cleanPath(
const QString &_path)
167 QString path = QDir::cleanPath(_path);
170 if (path.startsWith(
"//"_L1))
180struct QResourceGlobalData
182 QRecursiveMutex resourceMutex;
186Q_GLOBAL_STATIC(QResourceGlobalData, resourceGlobalData)
189{
return resourceGlobalData->resourceMutex; }
192{
return &resourceGlobalData->resourceList; }
195
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
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
294 bool load(
const QString &file);
311 Q_DECLARE_PUBLIC(QResource)
316 absoluteFilePath.clear();
317 compressionAlgo = QResource::NoCompression;
323 for (
int i = 0; i < related.size(); ++i) {
324 QResourceRoot *root = related.at(i);
325 if (!root->ref.deref())
334 const auto locker = qt_scoped_lock(resourceMutex());
336 QString cleaned = cleanPath(file);
337 for (
int i = 0; i < list->size(); ++i) {
338 QResourceRoot *res = list->at(i);
339 const int node = res->findNode(cleaned, locale);
341 if (related.isEmpty()) {
344 data = res->data(node, &size);
345 compressionAlgo = res->compressionAlgo(node);
349 compressionAlgo = QResource::NoCompression;
351 lastModified = res->lastModified(node);
352 }
else if (res->isContainer(node) !=
container) {
353 qWarning(
"QResourceInfo: Resource [%s] has both data and children!",
354 file.toLatin1().constData());
358 }
else if (res->mappingRootSubdir(file)) {
362 compressionAlgo = QResource::NoCompression;
368 return !related.isEmpty();
373 if (!related.isEmpty())
375 if (resourceGlobalData.isDestroyed())
378 if (fileName ==
":"_L1)
379 that->fileName += u'/';
380 that->absoluteFilePath = fileName;
381 if (!that->absoluteFilePath.startsWith(u':'))
382 that->absoluteFilePath.prepend(u':');
384 QStringView path(fileName);
385 if (path.startsWith(u':'))
388 if (path.startsWith(u'/')) {
392 const QString searchPath(u'/' + path);
394 that->absoluteFilePath = u':' + searchPath;
401 if (!children.isEmpty() || !container || related.isEmpty())
404 QString path = absoluteFilePath, k;
405 if (path.startsWith(u':'))
407 QDuplicateTracker<QString> kids(related.size());
408 QString cleaned = cleanPath(path);
409 for (
int i = 0; i < related.size(); ++i) {
410 QResourceRoot *res = related.at(i);
411 if (res->mappingRootSubdir(path, &k) && !k.isEmpty()) {
412 if (!kids.hasSeen(k))
415 const int node = res->findNode(cleaned);
417 QStringList related_children = res->children(node);
418 for (
int kid = 0; kid < related_children.size(); ++kid) {
419 k = related_children.at(kid);
420 if (!kids.hasSeen(k))
430 switch (compressionAlgo) {
431 case QResource::NoCompression:
434 case QResource::ZlibCompression:
435#ifndef QT_NO_COMPRESS
436 if (size_t(size) >=
sizeof(quint32))
437 return qFromBigEndian<quint32>(data);
439 Q_ASSERT(!
"QResource: Qt built without support for Zlib compression");
444 case QResource::ZstdCompression: {
446 size_t n = ZSTD_getFrameContentSize(data, size);
447 return ZSTD_isError(n) ? -1 : qint64(n);
450 Q_ASSERT(!
"QResource: Qt built without support for Zstd compression");
461#if defined(QT_NO_COMPRESS) && !QT_CONFIG(zstd)
463 Q_UNUSED(bufferSize);
466 switch (compressionAlgo) {
467 case QResource::NoCompression:
471 case QResource::ZlibCompression: {
472#ifndef QT_NO_COMPRESS
473 uLong len = uLong(bufferSize);
474 int res = ::uncompress(
reinterpret_cast<Bytef *>(buffer), &len, data +
sizeof(quint32),
475 uLong(size -
sizeof(quint32)));
477 qWarning(
"QResource: error decompressing zlib content (%d)", res);
486 case QResource::ZstdCompression: {
488 size_t usize = ZSTD_decompress(buffer, bufferSize, data, size);
489 if (ZSTD_isError(usize)) {
490 qWarning(
"QResource: error decompressing zstd content: %s", ZSTD_getErrorName(usize));
504
505
506
507
508
510QResource::QResource(
const QString &file,
const QLocale &locale) : d_ptr(
new QResourcePrivate(
this))
518
519
520QResource::~QResource()
525
526
527
528
529
530
532void QResource::setLocale(
const QLocale &locale)
540
541
543QLocale QResource::locale()
const
545 Q_D(
const QResource);
550
551
552
553
554
555
557void QResource::setFileName(
const QString &file)
565
566
567
568
569
571QString QResource::fileName()
const
573 Q_D(
const QResource);
574 d->ensureInitialized();
579
580
581
582
583
585QString QResource::absoluteFilePath()
const
587 Q_D(
const QResource);
588 d->ensureInitialized();
589 return d->absoluteFilePath;
593
594
595
596
598bool QResource::isValid()
const
600 Q_D(
const QResource);
601 d->ensureInitialized();
602 return !d->related.isEmpty();
606
607
608
609
610
611
612
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632QResource::Compression QResource::compressionAlgorithm()
const
634 Q_D(
const QResource);
635 d->ensureInitialized();
636 return Compression(d->compressionAlgo);
640
641
642
643
644
645
646
648qint64 QResource::size()
const
650 Q_D(
const QResource);
651 d->ensureInitialized();
656
657
658
659
660
661
662
663
664
665qint64 QResource::uncompressedSize()
const
667 Q_D(
const QResource);
668 d->ensureInitialized();
669 return d->uncompressedSize();
673
674
675
676
677
678
679
681const uchar *QResource::data()
const
683 Q_D(
const QResource);
684 d->ensureInitialized();
689
690
691
692
693
694
695
696
697
698
699
701QByteArray QResource::uncompressedData()
const
703 Q_D(
const QResource);
704 qint64 n = uncompressedSize();
707 if (n > std::numeric_limits<QByteArray::size_type>::max()) {
708 qWarning(
"QResource: compressed content does not fit into a QByteArray; use QFile instead");
711 if (d->compressionAlgo == NoCompression)
712 return QByteArray::fromRawData(
reinterpret_cast<
const char *>(d->data), n);
715 QByteArray result(n, Qt::Uninitialized);
716 n = d->decompress(result.data(), n);
725
726
727
728
729
730QDateTime QResource::lastModified()
const
732 Q_D(
const QResource);
733 d->ensureInitialized();
734 return d->lastModified ? QDateTime::fromMSecsSinceEpoch(d->lastModified) : QDateTime();
738
739
740
741
742
744bool QResource::isDir()
const
746 Q_D(
const QResource);
747 d->ensureInitialized();
752
753
754
755
756
758QStringList QResource::children()
const
760 Q_D(
const QResource);
765inline uint QResourceRoot::hash(
int node)
const
769 const int offset = findOffset(node);
770 qint32 name_offset = qFromBigEndian<qint32>(tree + offset);
772 return qFromBigEndian<quint32>(names + name_offset);
774inline QString QResourceRoot::name(
int node)
const
778 const int offset = findOffset(node);
781 qint32 name_offset = qFromBigEndian<qint32>(tree + offset);
782 quint16 name_length = qFromBigEndian<qint16>(names + name_offset);
786 ret.resize(name_length);
787 QChar *strData = ret.data();
788 qFromBigEndian<
char16_t>(names + name_offset, name_length, strData);
792int QResourceRoot::findNode(
const QString &_path,
const QLocale &locale)
const
794 QString path = _path;
796 QString root = mappingRoot();
797 if (!root.isEmpty()) {
801 if (!root.endsWith(u'/'))
803 if (path.size() >= root.size() && path.startsWith(root))
804 path = path.mid(root.size() - 1);
810#ifdef DEBUG_RESOURCE_MATCH
811 qDebug() <<
"!!!!" <<
"START" << path << locale.territory() << locale.language();
818 qint32 child_count = qFromBigEndian<qint32>(tree + 6);
819 qint32 child = qFromBigEndian<qint32>(tree + 10);
824 QStringSplitter splitter(path);
825 while (child_count && splitter.hasNext()) {
826 QStringView segment = splitter.next();
828#ifdef DEBUG_RESOURCE_MATCH
829 qDebug() <<
" CHILDREN" << segment;
830 for (
int j = 0; j < child_count; ++j) {
831 qDebug() <<
" " << child + j <<
" :: " << name(child + j);
834 const uint h = qt_hash(segment);
837 int l = 0, r = child_count - 1;
838 int sub_node = (l + r + 1) / 2;
840 const uint sub_node_hash = hash(child + sub_node);
841 if (h == sub_node_hash)
843 else if (h < sub_node_hash)
847 sub_node = (l + r + 1) / 2;
853 if (hash(sub_node) == h) {
854 while (sub_node > child && hash(sub_node - 1) == h)
856 for (; sub_node < child + child_count && hash(sub_node) == h;
858 if (name(sub_node) == segment) {
860 int offset = findOffset(sub_node);
861#ifdef DEBUG_RESOURCE_MATCH
862 qDebug() <<
" TRY" << sub_node << name(sub_node) << offset;
866 const qint16 flags = qFromBigEndian<qint16>(tree + offset);
869 if (!splitter.hasNext()) {
870 if (!(flags & Directory)) {
871 const qint16 territory = qFromBigEndian<qint16>(tree + offset);
874 const qint16 language = qFromBigEndian<qint16>(tree + offset);
876#ifdef DEBUG_RESOURCE_MATCH
877 qDebug() <<
" " <<
"LOCALE" << country << language;
879 if (territory == locale.territory() && language == locale.language()) {
880#ifdef DEBUG_RESOURCE_MATCH
881 qDebug() <<
"!!!!" <<
"FINISHED" << __LINE__ << sub_node;
884 }
else if ((territory == QLocale::AnyTerritory
885 && language == locale.language())
886 || (territory == QLocale::AnyTerritory
887 && language == QLocale::C
893#ifdef DEBUG_RESOURCE_MATCH
894 qDebug() <<
"!!!!" <<
"FINISHED" << __LINE__ << sub_node;
901 if (!(flags & Directory))
904 child_count = qFromBigEndian<qint32>(tree + offset);
906 child = qFromBigEndian<qint32>(tree + offset);
914#ifdef DEBUG_RESOURCE_MATCH
915 qDebug() <<
"!!!!" <<
"FINISHED" << __LINE__ << node;
919short QResourceRoot::flags(
int node)
const
923 const int offset = findOffset(node) + 4;
924 return qFromBigEndian<qint16>(tree + offset);
926const uchar *QResourceRoot::data(
int node, qint64 *size)
const
932 int offset = findOffset(node) + 4;
934 const qint16 flags = qFromBigEndian<qint16>(tree + offset);
939 if (!(flags & Directory)) {
940 const qint32 data_offset = qFromBigEndian<qint32>(tree + offset);
941 const quint32 data_length = qFromBigEndian<quint32>(payloads + data_offset);
942 const uchar *ret = payloads + data_offset + 4;
950qint64 QResourceRoot::lastModified(
int node)
const
952 if (node == -1 || version < 0x02)
955 const int offset = findOffset(node) + 14;
957 return qFromBigEndian<qint64>(tree + offset);
960QStringList QResourceRoot::children(
int node)
const
963 return QStringList();
964 int offset = findOffset(node) + 4;
966 const qint16 flags = qFromBigEndian<qint16>(tree + offset);
970 if (flags & Directory) {
971 const qint32 child_count = qFromBigEndian<qint32>(tree + offset);
973 const qint32 child_off = qFromBigEndian<qint32>(tree + offset);
974 ret.reserve(child_count);
975 for (
int i = child_off; i < child_off + child_count; ++i)
980bool QResourceRoot::mappingRootSubdir(
const QString &path, QString *match)
const
982 const QString root = mappingRoot();
986 QStringSplitter rootIt(root);
987 QStringSplitter pathIt(path);
988 while (rootIt.hasNext()) {
989 if (pathIt.hasNext()) {
990 if (rootIt.next() != pathIt.next())
995 *match = rootIt.next().toString();
1000 return !pathIt.hasNext();
1004 const unsigned char *name,
const unsigned char *data)
1006 if (resourceGlobalData.isDestroyed())
1008 const auto locker = qt_scoped_lock(resourceMutex());
1010 if (version >= 0x01 && version <= 0x3) {
1012 QResourceRoot res(version, tree, name, data);
1013 for (
int i = 0; i < list->size(); ++i) {
1014 if (*list->at(i) == res) {
1020 QResourceRoot *root =
new QResourceRoot(version, tree, name, data);
1030 const unsigned char *name,
const unsigned char *data)
1032 if (resourceGlobalData.isDestroyed())
1035 const auto locker = qt_scoped_lock(resourceMutex());
1036 if (version >= 0x01 && version <= 0x3) {
1037 QResourceRoot res(version, tree, name, data);
1039 for (
int i = 0; i < list->size();) {
1040 if (*list->at(i) == res) {
1041 QResourceRoot *root = list->takeAt(i);
1042 if (!root->ref.deref())
1055class QDynamicBufferResourceRoot :
public QResourceRoot
1058 const uchar *buffer;
1061 inline QDynamicBufferResourceRoot(
const QString &_root) : root(_root), buffer(
nullptr) { }
1062 inline ~QDynamicBufferResourceRoot() { }
1063 inline const uchar *mappingBuffer()
const {
return buffer; }
1064 QString mappingRoot()
const override {
return root; }
1065 ResourceRootType type()
const override {
return Resource_Buffer; }
1068 bool registerSelf(
const uchar *b, qsizetype size)
1071 if (size >= 0 && size < 20)
1078 if (b[offset + 0] !=
'q' || b[offset + 1] !=
'r' || b[offset + 2] !=
'e'
1079 || b[offset + 3] !=
's') {
1084 const int version = qFromBigEndian<qint32>(b + offset);
1087 const int tree_offset = qFromBigEndian<qint32>(b + offset);
1090 const int data_offset = qFromBigEndian<qint32>(b + offset);
1093 const int name_offset = qFromBigEndian<qint32>(b + offset);
1096 quint32 file_flags = 0;
1098 file_flags = qFromBigEndian<qint32>(b + offset);
1103 if (size >= 0 && (tree_offset >= size || data_offset >= size || name_offset >= size))
1107 quint32 acceptableFlags = 0;
1108#ifndef QT_NO_COMPRESS
1109 acceptableFlags |= Compressed;
1111 if (QT_CONFIG(zstd))
1112 acceptableFlags |= CompressedZstd;
1113 if (file_flags & ~acceptableFlags)
1116 if (version >= 0x01 && version <= 0x03) {
1118 setSource(version, b + tree_offset, b + name_offset, b + data_offset);
1125class QDynamicFileResourceRoot :
public QDynamicBufferResourceRoot
1128 static uchar *map_sys(QFile &file, qint64 base, qsizetype size);
1129 static void unmap_sys(
void *base, qsizetype size);
1134 uchar *unmapPointer;
1135 qsizetype unmapLength;
1138 QDynamicFileResourceRoot(
const QString &_root)
1139 : QDynamicBufferResourceRoot(_root), unmapPointer(
nullptr), unmapLength(0)
1141 ~QDynamicFileResourceRoot() {
1142 if (wasMemoryMapped())
1143 unmap_sys(unmapPointer, unmapLength);
1145 delete[] mappingBuffer();
1147 QString mappingFile()
const {
return fileName; }
1148 ResourceRootType type()
const override {
return Resource_File; }
1149 bool wasMemoryMapped()
const {
return unmapPointer; }
1151 bool registerSelf(
const QString &f);
1159# define MAP_FAILED reinterpret_cast<void *>(-1
)
1162void QDynamicFileResourceRoot::unmap_sys(
void *base, qsizetype size)
1164#if defined(QT_USE_MMAP)
1166#elif defined(Q_OS_WIN)
1168 UnmapViewOfFile(
reinterpret_cast<
void *>(base));
1173uchar *QDynamicFileResourceRoot::map_sys(QFile &file, qint64 offset, qsizetype size)
1175 Q_ASSERT(file.isOpen());
1176 void *ptr =
nullptr;
1178 size = qMin(file.size() - offset, (std::numeric_limits<qsizetype>::max)());
1179 int fd = file.handle();
1183#if defined(QT_USE_MMAP)
1184 int protection = PROT_READ;
1185 int flags = MAP_FILE | MAP_PRIVATE;
1186 ptr = QT_MMAP(
nullptr, size, protection, flags, fd, offset);
1187 if (ptr == MAP_FAILED)
1189#elif defined(Q_OS_WIN)
1190 HANDLE fileHandle =
reinterpret_cast<HANDLE>(_get_osfhandle(fd));
1191 if (fileHandle != INVALID_HANDLE_VALUE) {
1192 HANDLE mapHandle = CreateFileMapping(fileHandle, 0, PAGE_WRITECOPY, 0, 0, 0);
1194 ptr = MapViewOfFile(mapHandle, FILE_MAP_COPY, DWORD(offset >> 32), DWORD(offset), size);
1195 CloseHandle(mapHandle);
1199 return static_cast<uchar *>(ptr);
1202bool QDynamicFileResourceRoot::registerSelf(
const QString &f)
1205 if (!file.open(QIODevice::ReadOnly))
1208 qint64 data_len = file.size();
1209 if (data_len > std::numeric_limits<qsizetype>::max())
1212 uchar *data = map_sys(file, 0, data_len);
1213 bool fromMM = !!data;
1217 data =
new uchar[data_len];
1218 ok = (data_len == file.read(
reinterpret_cast<
char *>(data), data_len));
1226 if (data && QDynamicBufferResourceRoot::registerSelf(data, data_len)) {
1228 unmapPointer = data;
1229 unmapLength = data_len;
1240 if (r.startsWith(u':'))
1243 r = QDir::cleanPath(r);
1249
1250
1251
1252
1253
1254
1255
1256
1258bool QResource::registerResource(
const QString &rccFilename,
const QString &resourceRoot)
1260 QString r = qt_resource_fixResourceRoot(resourceRoot);
1261 if (!r.isEmpty() && r[0] != u'/') {
1262 qWarning(
"QDir::registerResource: Registering a resource [%ls] must be rooted in an "
1263 "absolute path (start with /) [%ls]",
1264 qUtf16Printable(rccFilename), qUtf16Printable(resourceRoot));
1268 QDynamicFileResourceRoot *root =
new QDynamicFileResourceRoot(r);
1269 if (root->registerSelf(rccFilename)) {
1271 const auto locker = qt_scoped_lock(resourceMutex());
1272 resourceList()->append(root);
1280
1281
1282
1283
1284
1285
1286
1287
1288
1290bool QResource::unregisterResource(
const QString &rccFilename,
const QString &resourceRoot)
1292 QString r = qt_resource_fixResourceRoot(resourceRoot);
1294 const auto locker = qt_scoped_lock(resourceMutex());
1295 ResourceList *list = resourceList();
1296 for (
int i = 0; i < list->size(); ++i) {
1297 QResourceRoot *res = list->at(i);
1298 if (res->type() == QResourceRoot::Resource_File) {
1299 QDynamicFileResourceRoot *root =
reinterpret_cast<QDynamicFileResourceRoot *>(res);
1300 if (root->mappingFile() == rccFilename && root->mappingRoot() == r) {
1302 if (!root->ref.deref()) {
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1327bool QResource::registerResource(
const uchar *rccData,
const QString &resourceRoot)
1329 QString r = qt_resource_fixResourceRoot(resourceRoot);
1330 if (!r.isEmpty() && r[0] != u'/') {
1331 qWarning(
"QDir::registerResource: Registering a resource [%p] must be rooted in an "
1332 "absolute path (start with /) [%ls]",
1333 rccData, qUtf16Printable(resourceRoot));
1337 QDynamicBufferResourceRoot *root =
new QDynamicBufferResourceRoot(r);
1338 if (root->registerSelf(rccData, -1)) {
1340 const auto locker = qt_scoped_lock(resourceMutex());
1341 resourceList()->append(root);
1349
1350
1351
1352
1353
1354
1355
1356
1357
1359bool QResource::unregisterResource(
const uchar *rccData,
const QString &resourceRoot)
1361 QString r = qt_resource_fixResourceRoot(resourceRoot);
1363 const auto locker = qt_scoped_lock(resourceMutex());
1364 ResourceList *list = resourceList();
1365 for (
int i = 0; i < list->size(); ++i) {
1366 QResourceRoot *res = list->at(i);
1367 if (res->type() == QResourceRoot::Resource_Buffer) {
1368 QDynamicBufferResourceRoot *root =
reinterpret_cast<QDynamicBufferResourceRoot *>(res);
1369 if (root->mappingBuffer() == rccData && root->mappingRoot() == r) {
1371 if (!root->ref.deref()) {
1382#if !defined(QT_BOOTSTRAPPED)
1387 Q_DECLARE_PUBLIC(QResourceFileEngine)
1390 bool unmap(uchar *ptr);
1391 void uncompress()
const;
1392 void mapUncompressed();
1393 bool mapUncompressed_sys();
1394 void unmapUncompressed_sys();
1398 bool mustUnmap =
false;
1401 static constexpr qsizetype RemapCompressedThreshold = 16384;
1409 unmapUncompressed_sys();
1419 QAbstractFileEngine(*
new QResourceFileEnginePrivate(
this))
1421 Q_D(QResourceFileEngine);
1422 d->resource.setFileName(file);
1431 Q_D(QResourceFileEngine);
1432 d->resource.setFileName(file);
1436 std::optional<QFile::Permissions> permissions)
1438 Q_UNUSED(permissions);
1440 Q_D(QResourceFileEngine);
1441 if (d->resource.fileName().isEmpty()) {
1442 qWarning(
"QResourceFileEngine::open: Missing file name");
1445 if (flags & QIODevice::WriteOnly)
1447 if (d->resource.compressionAlgorithm() != QResource::NoCompression) {
1449 if (d->uncompressed.isNull()) {
1450 d->errorString = QSystemError::stdString(EIO);
1454 if (!d->resource.isValid()) {
1455 d->errorString = QSystemError::stdString(ENOENT);
1463 Q_D(QResourceFileEngine);
1475 Q_D(QResourceFileEngine);
1476 if (len > size() - d->offset)
1477 len = size() - d->offset;
1480 if (!d->uncompressed.isNull())
1481 memcpy(data, d->uncompressed.constData() + d->offset, len);
1483 memcpy(data, d->resource.data() + d->offset, len);
1490 Q_D(
const QResourceFileEngine);
1491 return d->resource.isValid() ? d->resource.uncompressedSize() : 0;
1496 Q_D(
const QResourceFileEngine);
1502 Q_D(
const QResourceFileEngine);
1503 if (!d->resource.isValid())
1505 return d->offset == size();
1510 Q_D(QResourceFileEngine);
1511 if (!d->resource.isValid())
1514 if (d->offset > size())
1522 Q_D(
const QResourceFileEngine);
1523 QAbstractFileEngine::FileFlags ret;
1524 if (!d->resource.isValid())
1527 if (type & PermsMask)
1528 ret |= QAbstractFileEngine::FileFlags(ReadOwnerPerm | ReadUserPerm | ReadGroupPerm
1530 if (type & TypesMask) {
1531 if (d->resource.isDir())
1532 ret |= DirectoryType;
1536 if (type & FlagsMask) {
1538 if (d->resource.absoluteFilePath() ==
":/"_L1)
1546 Q_D(
const QResourceFileEngine);
1547 if (file == BaseName) {
1548 const qsizetype slash = d->resource.fileName().lastIndexOf(u'/');
1550 return d->resource.fileName();
1551 return d->resource.fileName().mid(slash + 1);
1552 }
else if (file == PathName || file == AbsolutePathName) {
1553 const QString path = (file == AbsolutePathName) ? d->resource.absoluteFilePath()
1554 : d->resource.fileName();
1555 const qsizetype slash = path.lastIndexOf(u'/');
1558 else if (slash <= 1)
1560 return path.left(slash);
1562 }
else if (file == CanonicalName || file == CanonicalPathName) {
1563 const QString absoluteFilePath = d->resource.absoluteFilePath();
1564 if (file == CanonicalPathName) {
1565 const qsizetype slash = absoluteFilePath.lastIndexOf(u'/');
1567 return absoluteFilePath.left(slash);
1569 return absoluteFilePath;
1571 return d->resource.fileName();
1576 static const uint nobodyID =
static_cast<uint>(-2);
1582 Q_D(
const QResourceFileEngine);
1583 if (time == QFile::FileModificationTime)
1584 return d->resource.lastModified();
1589
1590
1591QAbstractFileEngine::IteratorUniquePtr
1593 const QStringList &filterNames)
1600 Q_D(QResourceFileEngine);
1601 if (extension == MapExtension) {
1602 const auto *options =
static_cast<
const MapExtensionOption *>(option);
1603 auto *returnValue =
static_cast<MapExtensionReturn *>(output);
1604 returnValue->address = d->map(options->offset, options->size, options->flags);
1605 return (returnValue->address !=
nullptr);
1607 if (extension == UnMapExtension) {
1608 const auto *options =
static_cast<
const UnMapExtensionOption *>(option);
1609 return d->unmap(options->address);
1616 return (extension == UnMapExtension || extension == MapExtension);
1621 Q_Q(QResourceFileEngine);
1622 Q_ASSERT_X(resource.compressionAlgorithm() == QResource::NoCompression
1623 || !uncompressed.isNull(),
"QFile::map()",
1624 "open() should have uncompressed compressed resources");
1626 qint64 max = resource.uncompressedSize();
1628 if (offset < 0 || size <= 0 || !resource.isValid() ||
1629 qAddOverflow(offset, size, &end) || end > max) {
1630 q->setError(QFile::UnspecifiedError, QString());
1634 const uchar *address =
reinterpret_cast<
const uchar *>(uncompressed.constBegin());
1635 if (!uncompressed.isNull())
1636 return const_cast<uchar *>(address) + offset;
1639 address = resource.data();
1640 if (flags & QFile::MapPrivateOption) {
1643 address =
reinterpret_cast<
const uchar *>(uncompressed.constData());
1646 return const_cast<uchar *>(address) + offset;
1657 if (resource.compressionAlgorithm() == QResource::NoCompression
1658 || !uncompressed.isEmpty() || resource.size() == 0)
1660 uncompressed = resource.uncompressedData();
1665 Q_ASSERT(resource.compressionAlgorithm() == QResource::NoCompression);
1666 if (!uncompressed.isNull())
1669 if (resource.uncompressedSize() >= RemapCompressedThreshold) {
1670 if (mapUncompressed_sys())
1674 uncompressed = resource.uncompressedData();
1675 uncompressed.detach();
1678#if defined(MREMAP_MAYMOVE) && defined(MREMAP_DONTUNMAP)
1679inline bool QResourcePrivate::mayRemapData(
const QResource &resource)
1681 auto d = resource.d_func();
1687 const QResourceRoot *root = d->related.at(0);
1689 switch (root->type()) {
1690 case QResourceRoot::Resource_Builtin:
1692 case QResourceRoot::Resource_Buffer:
1694 case QResourceRoot::Resource_File:
1698 auto df =
static_cast<
const QDynamicFileResourceRoot *>(root);
1699 return df->wasMemoryMapped();
1707 auto getpagesize = [] {
1708 SYSTEM_INFO sysinfo;
1709 ::GetSystemInfo(&sysinfo);
1710 return sysinfo.dwAllocationGranularity;
1719 const quintptr pageMask = getpagesize() - 1;
1720 quintptr data = quintptr(location);
1721 quintptr begin = data & ~pageMask;
1722 quintptr end = (data + size + pageMask) & ~pageMask;
1723 r.begin =
reinterpret_cast<
void *>(begin);
1724 r.size = end - begin;
1725 r.offset = data & pageMask;
1731 auto r = mappingBoundaries(resource.data(), resource.uncompressedSize());
1732 void *ptr =
nullptr;
1734#if defined(MREMAP_MAYMOVE) && defined(MREMAP_DONTUNMAP)
1740 if (!QResourcePrivate::mayRemapData(resource))
1743 ptr = mremap(r.begin, r.size, r.size, MREMAP_MAYMOVE | MREMAP_DONTUNMAP);
1744 if (ptr == MAP_FAILED)
1749 if (mprotect(ptr, r.size, PROT_READ | PROT_WRITE) != 0) {
1750 munmap(ptr, r.size);
1753#elif defined(Q_OS_DARWIN)
1754 mach_port_t self = mach_task_self();
1755 vm_address_t addr = 0;
1756 vm_address_t mask = 0;
1757 bool anywhere =
true;
1759 vm_prot_t cur_prot = VM_PROT_READ | VM_PROT_WRITE;
1760 vm_prot_t max_prot = VM_PROT_ALL;
1761 kern_return_t res = vm_remap(self, &addr, r.size, mask, anywhere,
1762 self, vm_address_t(r.begin), copy, &cur_prot,
1763 &max_prot, VM_INHERIT_DEFAULT);
1764 if (res != KERN_SUCCESS)
1767 ptr =
reinterpret_cast<
void *>(addr);
1768 if ((max_prot & VM_PROT_WRITE) == 0 || mprotect(ptr, r.size, PROT_READ | PROT_WRITE) != 0) {
1769 munmap(ptr, r.size);
1776 const char *newdata =
static_cast<
char *>(ptr) + r.offset;
1777 uncompressed = QByteArray::fromRawData(newdata, resource.uncompressedSize());
1784 auto r = mappingBoundaries(uncompressed.constBegin(), uncompressed.size());
1785 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
QList< QResourceRoot * > related
QResourcePrivate(QResource *_q)
qsizetype decompress(char *buffer, qsizetype bufferSize) const
bool load(const QString &file)
static ResourceList * resourceList()
#define RCC_FEATURE_SYMBOL(feature)
static QRecursiveMutex & resourceMutex()
Q_DECLARE_TYPEINFO(QResourceRoot, Q_RELOCATABLE_TYPE)
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)