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() =
default;
133 Q_DISABLE_COPY_MOVE(QResourceRoot)
134 int findNode(
const QString &path,
const QLocale &locale=QLocale())
const;
135 inline bool isContainer(
int node)
const {
return flags(node) & Directory; }
136 QResource::Compression compressionAlgo(
int node)
138 uint compressionFlags = flags(node) & (Compressed | CompressedZstd);
139 if (compressionFlags == Compressed)
140 return QResource::ZlibCompression;
141 if (compressionFlags == CompressedZstd)
142 return QResource::ZstdCompression;
143 return QResource::NoCompression;
145 const uchar *data(
int node, qint64 *size)
const;
146 qint64 lastModified(
int node)
const;
147 QStringList children(
int node)
const;
148 virtual QString mappingRoot()
const {
return QString(); }
149 bool mappingRootSubdir(
const QString &path, QString *match =
nullptr)
const;
150 inline bool operator==(
const QResourceRoot &other)
const
151 {
return tree == other.tree && names == other.names && payloads == other.payloads && version == other.version; }
152 inline bool operator!=(
const QResourceRoot &other)
const
153 {
return !operator==(other); }
154 enum ResourceRootType { Resource_Builtin, Resource_File, Resource_Buffer };
155 virtual ResourceRootType type()
const {
return Resource_Builtin; }
158 inline void setSource(
int v,
const uchar *t,
const uchar *n,
const uchar *d) {
166static QString cleanPath(
const QString &_path)
168 QString path = QDir::cleanPath(_path);
171 if (path.startsWith(
"//"_L1))
179struct QResourceGlobalData
181 QRecursiveMutex resourceMutex;
185Q_GLOBAL_STATIC(QResourceGlobalData, resourceGlobalData)
188{
return resourceGlobalData->resourceMutex; }
191{
return &resourceGlobalData->resourceList; }
194
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
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
293 bool load(
const QString &file);
310 Q_DECLARE_PUBLIC(QResource)
315 absoluteFilePath.clear();
316 compressionAlgo = QResource::NoCompression;
322 for (
int i = 0; i < related.size(); ++i) {
323 QResourceRoot *root = related.at(i);
324 if (!root->ref.deref())
333 const auto locker = qt_scoped_lock(resourceMutex());
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);
340 if (related.isEmpty()) {
343 data = res->data(node, &size);
344 compressionAlgo = res->compressionAlgo(node);
348 compressionAlgo = QResource::NoCompression;
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());
357 }
else if (res->mappingRootSubdir(file)) {
361 compressionAlgo = QResource::NoCompression;
367 return !related.isEmpty();
372 if (!related.isEmpty())
374 if (resourceGlobalData.isDestroyed())
377 if (fileName ==
":"_L1)
378 that->fileName += u'/';
379 that->absoluteFilePath = fileName;
380 if (!that->absoluteFilePath.startsWith(u':'))
381 that->absoluteFilePath.prepend(u':');
383 QStringView path(fileName);
384 if (path.startsWith(u':'))
387 if (path.startsWith(u'/')) {
391 const QString searchPath(u'/' + path);
393 that->absoluteFilePath = u':' + searchPath;
400 if (!children.isEmpty() || !container || related.isEmpty())
403 QString path = absoluteFilePath, k;
404 if (path.startsWith(u':'))
406 QDuplicateTracker<QString> kids(related.size());
407 QString cleaned = cleanPath(path);
408 for (
int i = 0; i < related.size(); ++i) {
409 QResourceRoot *res = related.at(i);
410 if (res->mappingRootSubdir(path, &k) && !k.isEmpty()) {
411 if (!kids.hasSeen(k))
414 const int node = res->findNode(cleaned);
416 QStringList related_children = res->children(node);
417 for (
int kid = 0; kid < related_children.size(); ++kid) {
418 k = related_children.at(kid);
419 if (!kids.hasSeen(k))
429 switch (compressionAlgo) {
430 case QResource::NoCompression:
433 case QResource::ZlibCompression:
434#ifndef QT_NO_COMPRESS
435 if (size_t(size) >=
sizeof(quint32))
436 return qFromBigEndian<quint32>(data);
438 Q_ASSERT(!
"QResource: Qt built without support for Zlib compression");
443 case QResource::ZstdCompression: {
445 size_t n = ZSTD_getFrameContentSize(data, size);
446 return ZSTD_isError(n) ? -1 : qint64(n);
449 Q_ASSERT(!
"QResource: Qt built without support for Zstd compression");
460#if defined(QT_NO_COMPRESS) && !QT_CONFIG(zstd)
462 Q_UNUSED(bufferSize);
465 switch (compressionAlgo) {
466 case QResource::NoCompression:
470 case QResource::ZlibCompression: {
471#ifndef QT_NO_COMPRESS
472 uLong len = uLong(bufferSize);
473 int res = ::uncompress(
reinterpret_cast<Bytef *>(buffer), &len, data +
sizeof(quint32),
474 uLong(size -
sizeof(quint32)));
476 qWarning(
"QResource: error decompressing zlib content (%d)", res);
485 case QResource::ZstdCompression: {
487 size_t usize = ZSTD_decompress(buffer, bufferSize, data, size);
488 if (ZSTD_isError(usize)) {
489 qWarning(
"QResource: error decompressing zstd content: %s", ZSTD_getErrorName(usize));
503
504
505
506
507
509QResource::QResource(
const QString &file,
const QLocale &locale) : d_ptr(
new QResourcePrivate(
this))
517
518
519QResource::~QResource()
524
525
526
527
528
529
531void QResource::setLocale(
const QLocale &locale)
539
540
542QLocale QResource::locale()
const
544 Q_D(
const QResource);
549
550
551
552
553
554
556void QResource::setFileName(
const QString &file)
564
565
566
567
568
570QString QResource::fileName()
const
572 Q_D(
const QResource);
573 d->ensureInitialized();
578
579
580
581
582
584QString QResource::absoluteFilePath()
const
586 Q_D(
const QResource);
587 d->ensureInitialized();
588 return d->absoluteFilePath;
592
593
594
595
597bool QResource::isValid()
const
599 Q_D(
const QResource);
600 d->ensureInitialized();
601 return !d->related.isEmpty();
605
606
607
608
609
610
611
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631QResource::Compression QResource::compressionAlgorithm()
const
633 Q_D(
const QResource);
634 d->ensureInitialized();
635 return Compression(d->compressionAlgo);
639
640
641
642
643
644
645
647qint64 QResource::size()
const
649 Q_D(
const QResource);
650 d->ensureInitialized();
655
656
657
658
659
660
661
662
663
664qint64 QResource::uncompressedSize()
const
666 Q_D(
const QResource);
667 d->ensureInitialized();
668 return d->uncompressedSize();
672
673
674
675
676
677
678
680const uchar *QResource::data()
const
682 Q_D(
const QResource);
683 d->ensureInitialized();
688
689
690
691
692
693
694
695
696
697
698
700QByteArray QResource::uncompressedData()
const
702 Q_D(
const QResource);
703 qint64 n = uncompressedSize();
706 if (n > std::numeric_limits<QByteArray::size_type>::max()) {
707 qWarning(
"QResource: compressed content does not fit into a QByteArray; use QFile instead");
710 if (d->compressionAlgo == NoCompression)
711 return QByteArray::fromRawData(
reinterpret_cast<
const char *>(d->data), n);
714 QByteArray result(n, Qt::Uninitialized);
715 n = d->decompress(result.data(), n);
724
725
726
727
728
729QDateTime QResource::lastModified()
const
731 Q_D(
const QResource);
732 d->ensureInitialized();
733 return d->lastModified ? QDateTime::fromMSecsSinceEpoch(d->lastModified) : QDateTime();
737
738
739
740
741
743bool QResource::isDir()
const
745 Q_D(
const QResource);
746 d->ensureInitialized();
751
752
753
754
755
757QStringList QResource::children()
const
759 Q_D(
const QResource);
764inline uint QResourceRoot::hash(
int node)
const
768 const int offset = findOffset(node);
769 qint32 name_offset = qFromBigEndian<qint32>(tree + offset);
771 return qFromBigEndian<quint32>(names + name_offset);
773inline QString QResourceRoot::name(
int node)
const
777 const int offset = findOffset(node);
780 qint32 name_offset = qFromBigEndian<qint32>(tree + offset);
781 quint16 name_length = qFromBigEndian<qint16>(names + name_offset);
785 ret.resize(name_length);
786 QChar *strData = ret.data();
787 qFromBigEndian<
char16_t>(names + name_offset, name_length, strData);
791int QResourceRoot::findNode(
const QString &_path,
const QLocale &locale)
const
793 QString path = _path;
795 QString root = mappingRoot();
796 if (!root.isEmpty()) {
800 if (!root.endsWith(u'/'))
802 if (path.size() >= root.size() && path.startsWith(root))
803 path = path.mid(root.size() - 1);
809#ifdef DEBUG_RESOURCE_MATCH
810 qDebug() <<
"!!!!" <<
"START" << path << locale.territory() << locale.language();
817 qint32 child_count = qFromBigEndian<qint32>(tree + 6);
818 qint32 child = qFromBigEndian<qint32>(tree + 10);
823 QStringSplitter splitter(path);
824 while (child_count && splitter.hasNext()) {
825 QStringView segment = splitter.next();
827#ifdef DEBUG_RESOURCE_MATCH
828 qDebug() <<
" CHILDREN" << segment;
829 for (
int j = 0; j < child_count; ++j) {
830 qDebug() <<
" " << child + j <<
" :: " << name(child + j);
833 const uint h = qt_hash(segment);
836 int l = 0, r = child_count - 1;
837 int sub_node = (l + r + 1) / 2;
839 const uint sub_node_hash = hash(child + sub_node);
840 if (h == sub_node_hash)
842 else if (h < sub_node_hash)
846 sub_node = (l + r + 1) / 2;
852 if (hash(sub_node) == h) {
853 while (sub_node > child && hash(sub_node - 1) == h)
855 for (; sub_node < child + child_count && hash(sub_node) == h;
857 if (name(sub_node) == segment) {
859 int offset = findOffset(sub_node);
860#ifdef DEBUG_RESOURCE_MATCH
861 qDebug() <<
" TRY" << sub_node << name(sub_node) << offset;
865 const qint16 flags = qFromBigEndian<qint16>(tree + offset);
868 if (!splitter.hasNext()) {
869 if (!(flags & Directory)) {
870 const qint16 territory = qFromBigEndian<qint16>(tree + offset);
873 const qint16 language = qFromBigEndian<qint16>(tree + offset);
875#ifdef DEBUG_RESOURCE_MATCH
876 qDebug() <<
" " <<
"LOCALE" << country << language;
878 if (territory == locale.territory() && language == locale.language()) {
879#ifdef DEBUG_RESOURCE_MATCH
880 qDebug() <<
"!!!!" <<
"FINISHED" << __LINE__ << sub_node;
883 }
else if ((territory == QLocale::AnyTerritory
884 && language == locale.language())
885 || (territory == QLocale::AnyTerritory
886 && language == QLocale::C
892#ifdef DEBUG_RESOURCE_MATCH
893 qDebug() <<
"!!!!" <<
"FINISHED" << __LINE__ << sub_node;
900 if (!(flags & Directory))
903 child_count = qFromBigEndian<qint32>(tree + offset);
905 child = qFromBigEndian<qint32>(tree + offset);
913#ifdef DEBUG_RESOURCE_MATCH
914 qDebug() <<
"!!!!" <<
"FINISHED" << __LINE__ << node;
918short QResourceRoot::flags(
int node)
const
922 const int offset = findOffset(node) + 4;
923 return qFromBigEndian<qint16>(tree + offset);
925const uchar *QResourceRoot::data(
int node, qint64 *size)
const
931 int offset = findOffset(node) + 4;
933 const qint16 flags = qFromBigEndian<qint16>(tree + offset);
938 if (!(flags & Directory)) {
939 const qint32 data_offset = qFromBigEndian<qint32>(tree + offset);
940 const quint32 data_length = qFromBigEndian<quint32>(payloads + data_offset);
941 const uchar *ret = payloads + data_offset + 4;
949qint64 QResourceRoot::lastModified(
int node)
const
951 if (node == -1 || version < 0x02)
954 const int offset = findOffset(node) + 14;
956 return qFromBigEndian<qint64>(tree + offset);
959QStringList QResourceRoot::children(
int node)
const
962 return QStringList();
963 int offset = findOffset(node) + 4;
965 const qint16 flags = qFromBigEndian<qint16>(tree + offset);
969 if (flags & Directory) {
970 const qint32 child_count = qFromBigEndian<qint32>(tree + offset);
972 const qint32 child_off = qFromBigEndian<qint32>(tree + offset);
973 ret.reserve(child_count);
974 for (
int i = child_off; i < child_off + child_count; ++i)
979bool QResourceRoot::mappingRootSubdir(
const QString &path, QString *match)
const
981 const QString root = mappingRoot();
985 QStringSplitter rootIt(root);
986 QStringSplitter pathIt(path);
987 while (rootIt.hasNext()) {
988 if (pathIt.hasNext()) {
989 if (rootIt.next() != pathIt.next())
994 *match = rootIt.next().toString();
999 return !pathIt.hasNext();
1003 const unsigned char *name,
const unsigned char *data)
1005 if (resourceGlobalData.isDestroyed())
1007 const auto locker = qt_scoped_lock(resourceMutex());
1009 if (version >= 0x01 && version <= 0x3) {
1011 QResourceRoot res(version, tree, name, data);
1012 for (
int i = 0; i < list->size(); ++i) {
1013 if (*list->at(i) == res) {
1019 QResourceRoot *root =
new QResourceRoot(version, tree, name, data);
1029 const unsigned char *name,
const unsigned char *data)
1031 if (resourceGlobalData.isDestroyed())
1034 const auto locker = qt_scoped_lock(resourceMutex());
1035 if (version >= 0x01 && version <= 0x3) {
1036 QResourceRoot res(version, tree, name, data);
1038 for (
int i = 0; i < list->size();) {
1039 if (*list->at(i) == res) {
1040 QResourceRoot *root = list->takeAt(i);
1041 if (!root->ref.deref())
1054class QDynamicBufferResourceRoot :
public QResourceRoot
1057 const uchar *buffer;
1060 inline QDynamicBufferResourceRoot(
const QString &_root) : root(_root), buffer(
nullptr) { }
1061 inline ~QDynamicBufferResourceRoot() { }
1062 inline const uchar *mappingBuffer()
const {
return buffer; }
1063 QString mappingRoot()
const override {
return root; }
1064 ResourceRootType type()
const override {
return Resource_Buffer; }
1067 bool registerSelf(
const uchar *b, qsizetype size)
1070 if (size >= 0 && size < 20)
1077 if (b[offset + 0] !=
'q' || b[offset + 1] !=
'r' || b[offset + 2] !=
'e'
1078 || b[offset + 3] !=
's') {
1083 const int version = qFromBigEndian<qint32>(b + offset);
1086 const int tree_offset = qFromBigEndian<qint32>(b + offset);
1089 const int data_offset = qFromBigEndian<qint32>(b + offset);
1092 const int name_offset = qFromBigEndian<qint32>(b + offset);
1095 quint32 file_flags = 0;
1097 file_flags = qFromBigEndian<qint32>(b + offset);
1102 if (size >= 0 && (tree_offset >= size || data_offset >= size || name_offset >= size))
1106 quint32 acceptableFlags = 0;
1107#ifndef QT_NO_COMPRESS
1108 acceptableFlags |= Compressed;
1110 if (QT_CONFIG(zstd))
1111 acceptableFlags |= CompressedZstd;
1112 if (file_flags & ~acceptableFlags)
1115 if (version >= 0x01 && version <= 0x03) {
1117 setSource(version, b + tree_offset, b + name_offset, b + data_offset);
1124class QDynamicFileResourceRoot :
public QDynamicBufferResourceRoot
1127 static uchar *map_sys(QFile &file, qint64 base, qsizetype size);
1128 static void unmap_sys(
void *base, qsizetype size);
1133 uchar *unmapPointer;
1134 qsizetype unmapLength;
1137 QDynamicFileResourceRoot(
const QString &_root)
1138 : QDynamicBufferResourceRoot(_root), unmapPointer(
nullptr), unmapLength(0)
1140 ~QDynamicFileResourceRoot() {
1141 if (wasMemoryMapped())
1142 unmap_sys(unmapPointer, unmapLength);
1144 delete[] mappingBuffer();
1146 QString mappingFile()
const {
return fileName; }
1147 ResourceRootType type()
const override {
return Resource_File; }
1148 bool wasMemoryMapped()
const {
return unmapPointer; }
1150 bool registerSelf(
const QString &f);
1158# define MAP_FAILED reinterpret_cast<void *>(-1
)
1161void QDynamicFileResourceRoot::unmap_sys(
void *base, qsizetype size)
1163#if defined(QT_USE_MMAP)
1165#elif defined(Q_OS_WIN)
1167 UnmapViewOfFile(
reinterpret_cast<
void *>(base));
1172uchar *QDynamicFileResourceRoot::map_sys(QFile &file, qint64 offset, qsizetype size)
1174 Q_ASSERT(file.isOpen());
1175 void *ptr =
nullptr;
1177 size = qMin(file.size() - offset, (std::numeric_limits<qsizetype>::max)());
1178 int fd = file.handle();
1182#if defined(QT_USE_MMAP)
1183 int protection = PROT_READ;
1184 int flags = MAP_FILE | MAP_PRIVATE;
1185 ptr = QT_MMAP(
nullptr, size, protection, flags, fd, offset);
1186 if (ptr == MAP_FAILED)
1188#elif defined(Q_OS_WIN)
1189 HANDLE fileHandle =
reinterpret_cast<HANDLE>(_get_osfhandle(fd));
1190 if (fileHandle != INVALID_HANDLE_VALUE) {
1191 HANDLE mapHandle = CreateFileMapping(fileHandle, 0, PAGE_WRITECOPY, 0, 0, 0);
1193 ptr = MapViewOfFile(mapHandle, FILE_MAP_COPY, DWORD(offset >> 32), DWORD(offset), size);
1194 CloseHandle(mapHandle);
1198 return static_cast<uchar *>(ptr);
1201bool QDynamicFileResourceRoot::registerSelf(
const QString &f)
1204 if (!file.open(QIODevice::ReadOnly))
1207 qint64 data_len = file.size();
1208 if (data_len > std::numeric_limits<qsizetype>::max())
1211 uchar *data = map_sys(file, 0, data_len);
1212 bool fromMM = !!data;
1216 data =
new uchar[data_len];
1217 ok = (data_len == file.read(
reinterpret_cast<
char *>(data), data_len));
1225 if (data && QDynamicBufferResourceRoot::registerSelf(data, data_len)) {
1227 unmapPointer = data;
1228 unmapLength = data_len;
1239 if (r.startsWith(u':'))
1242 r = QDir::cleanPath(r);
1248
1249
1250
1251
1252
1253
1254
1255
1257bool QResource::registerResource(
const QString &rccFilename,
const QString &resourceRoot)
1259 QString r = qt_resource_fixResourceRoot(resourceRoot);
1260 if (!r.isEmpty() && r[0] != u'/') {
1261 qWarning(
"QDir::registerResource: Registering a resource [%ls] must be rooted in an "
1262 "absolute path (start with /) [%ls]",
1263 qUtf16Printable(rccFilename), qUtf16Printable(resourceRoot));
1267 QDynamicFileResourceRoot *root =
new QDynamicFileResourceRoot(r);
1268 if (root->registerSelf(rccFilename)) {
1270 const auto locker = qt_scoped_lock(resourceMutex());
1271 resourceList()->append(root);
1279
1280
1281
1282
1283
1284
1285
1286
1287
1289bool QResource::unregisterResource(
const QString &rccFilename,
const QString &resourceRoot)
1291 QString r = qt_resource_fixResourceRoot(resourceRoot);
1293 const auto locker = qt_scoped_lock(resourceMutex());
1294 ResourceList *list = resourceList();
1295 for (
int i = 0; i < list->size(); ++i) {
1296 QResourceRoot *res = list->at(i);
1297 if (res->type() == QResourceRoot::Resource_File) {
1298 QDynamicFileResourceRoot *root =
reinterpret_cast<QDynamicFileResourceRoot *>(res);
1299 if (root->mappingFile() == rccFilename && root->mappingRoot() == r) {
1301 if (!root->ref.deref()) {
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1326bool QResource::registerResource(
const uchar *rccData,
const QString &resourceRoot)
1328 QString r = qt_resource_fixResourceRoot(resourceRoot);
1329 if (!r.isEmpty() && r[0] != u'/') {
1330 qWarning(
"QDir::registerResource: Registering a resource [%p] must be rooted in an "
1331 "absolute path (start with /) [%ls]",
1332 rccData, qUtf16Printable(resourceRoot));
1336 QDynamicBufferResourceRoot *root =
new QDynamicBufferResourceRoot(r);
1337 if (root->registerSelf(rccData, -1)) {
1339 const auto locker = qt_scoped_lock(resourceMutex());
1340 resourceList()->append(root);
1348
1349
1350
1351
1352
1353
1354
1355
1356
1358bool QResource::unregisterResource(
const uchar *rccData,
const QString &resourceRoot)
1360 QString r = qt_resource_fixResourceRoot(resourceRoot);
1362 const auto locker = qt_scoped_lock(resourceMutex());
1363 ResourceList *list = resourceList();
1364 for (
int i = 0; i < list->size(); ++i) {
1365 QResourceRoot *res = list->at(i);
1366 if (res->type() == QResourceRoot::Resource_Buffer) {
1367 QDynamicBufferResourceRoot *root =
reinterpret_cast<QDynamicBufferResourceRoot *>(res);
1368 if (root->mappingBuffer() == rccData && root->mappingRoot() == r) {
1370 if (!root->ref.deref()) {
1381#if !defined(QT_BOOTSTRAPPED)
1386 Q_DECLARE_PUBLIC(QResourceFileEngine)
1389 bool unmap(uchar *ptr);
1390 void uncompress()
const;
1391 void mapUncompressed();
1392 bool mapUncompressed_sys();
1393 void unmapUncompressed_sys();
1397 bool mustUnmap =
false;
1400 static constexpr qsizetype RemapCompressedThreshold = 16384;
1408 unmapUncompressed_sys();
1418 QAbstractFileEngine(*
new QResourceFileEnginePrivate(
this))
1420 Q_D(QResourceFileEngine);
1421 d->resource.setFileName(file);
1430 Q_D(QResourceFileEngine);
1431 d->resource.setFileName(file);
1435 std::optional<QFile::Permissions> permissions)
1437 Q_UNUSED(permissions);
1439 Q_D(QResourceFileEngine);
1440 if (d->resource.fileName().isEmpty()) {
1441 qWarning(
"QResourceFileEngine::open: Missing file name");
1444 if (flags & QIODevice::WriteOnly)
1446 if (d->resource.compressionAlgorithm() != QResource::NoCompression) {
1448 if (d->uncompressed.isNull()) {
1449 d->errorString = QSystemError::stdString(EIO);
1453 if (!d->resource.isValid()) {
1454 d->errorString = QSystemError::stdString(ENOENT);
1462 Q_D(QResourceFileEngine);
1474 Q_D(QResourceFileEngine);
1475 if (len > size() - d->offset)
1476 len = size() - d->offset;
1479 if (!d->uncompressed.isNull())
1480 memcpy(data, d->uncompressed.constData() + d->offset, len);
1482 memcpy(data, d->resource.data() + d->offset, len);
1489 Q_D(
const QResourceFileEngine);
1490 return d->resource.isValid() ? d->resource.uncompressedSize() : 0;
1495 Q_D(
const QResourceFileEngine);
1501 Q_D(
const QResourceFileEngine);
1502 if (!d->resource.isValid())
1504 return d->offset == size();
1509 Q_D(QResourceFileEngine);
1510 if (!d->resource.isValid())
1513 if (d->offset > size())
1521 Q_D(
const QResourceFileEngine);
1522 QAbstractFileEngine::FileFlags ret;
1523 if (!d->resource.isValid())
1526 if (type & PermsMask)
1527 ret |= QAbstractFileEngine::FileFlags(ReadOwnerPerm | ReadUserPerm | ReadGroupPerm
1529 if (type & TypesMask) {
1530 if (d->resource.isDir())
1531 ret |= DirectoryType;
1535 if (type & FlagsMask) {
1537 if (d->resource.absoluteFilePath() ==
":/"_L1)
1545 Q_D(
const QResourceFileEngine);
1546 if (file == BaseName) {
1547 const qsizetype slash = d->resource.fileName().lastIndexOf(u'/');
1549 return d->resource.fileName();
1550 return d->resource.fileName().mid(slash + 1);
1551 }
else if (file == PathName || file == AbsolutePathName) {
1552 const QString path = (file == AbsolutePathName) ? d->resource.absoluteFilePath()
1553 : d->resource.fileName();
1554 const qsizetype slash = path.lastIndexOf(u'/');
1557 else if (slash <= 1)
1559 return path.left(slash);
1561 }
else if (file == CanonicalName || file == CanonicalPathName) {
1562 const QString absoluteFilePath = d->resource.absoluteFilePath();
1563 if (file == CanonicalPathName) {
1564 const qsizetype slash = absoluteFilePath.lastIndexOf(u'/');
1566 return absoluteFilePath.left(slash);
1568 return absoluteFilePath;
1570 return d->resource.fileName();
1575 static const uint nobodyID =
static_cast<uint>(-2);
1581 Q_D(
const QResourceFileEngine);
1582 if (time == QFile::FileModificationTime)
1583 return d->resource.lastModified();
1588
1589
1590QAbstractFileEngine::IteratorUniquePtr
1592 const QStringList &filterNames)
1599 Q_D(QResourceFileEngine);
1600 if (extension == MapExtension) {
1601 const auto *options =
static_cast<
const MapExtensionOption *>(option);
1602 auto *returnValue =
static_cast<MapExtensionReturn *>(output);
1603 returnValue->address = d->map(options->offset, options->size, options->flags);
1604 return (returnValue->address !=
nullptr);
1606 if (extension == UnMapExtension) {
1607 const auto *options =
static_cast<
const UnMapExtensionOption *>(option);
1608 return d->unmap(options->address);
1615 return (extension == UnMapExtension || extension == MapExtension);
1620 Q_Q(QResourceFileEngine);
1621 Q_ASSERT_X(resource.compressionAlgorithm() == QResource::NoCompression
1622 || !uncompressed.isNull(),
"QFile::map()",
1623 "open() should have uncompressed compressed resources");
1625 qint64 max = resource.uncompressedSize();
1627 if (offset < 0 || size <= 0 || !resource.isValid() ||
1628 qAddOverflow(offset, size, &end) || end > max) {
1629 q->setError(QFile::UnspecifiedError, QString());
1633 const uchar *address =
reinterpret_cast<
const uchar *>(uncompressed.constBegin());
1634 if (!uncompressed.isNull())
1635 return const_cast<uchar *>(address) + offset;
1638 address = resource.data();
1639 if (flags & QFile::MapPrivateOption) {
1642 address =
reinterpret_cast<
const uchar *>(uncompressed.constData());
1645 return const_cast<uchar *>(address) + offset;
1656 if (resource.compressionAlgorithm() == QResource::NoCompression
1657 || !uncompressed.isEmpty() || resource.size() == 0)
1659 uncompressed = resource.uncompressedData();
1664 Q_ASSERT(resource.compressionAlgorithm() == QResource::NoCompression);
1665 if (!uncompressed.isNull())
1668 if (resource.uncompressedSize() >= RemapCompressedThreshold) {
1669 if (mapUncompressed_sys())
1673 uncompressed = resource.uncompressedData();
1674 uncompressed.detach();
1677#if defined(MREMAP_MAYMOVE) && defined(MREMAP_DONTUNMAP)
1678inline bool QResourcePrivate::mayRemapData(
const QResource &resource)
1680 auto d = resource.d_func();
1686 const QResourceRoot *root = d->related.at(0);
1688 switch (root->type()) {
1689 case QResourceRoot::Resource_Builtin:
1691 case QResourceRoot::Resource_Buffer:
1693 case QResourceRoot::Resource_File:
1697 auto df =
static_cast<
const QDynamicFileResourceRoot *>(root);
1698 return df->wasMemoryMapped();
1706 auto getpagesize = [] {
1707 SYSTEM_INFO sysinfo;
1708 ::GetSystemInfo(&sysinfo);
1709 return sysinfo.dwAllocationGranularity;
1718 const quintptr pageMask = getpagesize() - 1;
1719 quintptr data = quintptr(location);
1720 quintptr begin = data & ~pageMask;
1721 quintptr end = (data + size + pageMask) & ~pageMask;
1722 r.begin =
reinterpret_cast<
void *>(begin);
1723 r.size = end - begin;
1724 r.offset = data & pageMask;
1730 auto r = mappingBoundaries(resource.data(), resource.uncompressedSize());
1731 void *ptr =
nullptr;
1733#if defined(MREMAP_MAYMOVE) && defined(MREMAP_DONTUNMAP)
1739 if (!QResourcePrivate::mayRemapData(resource))
1742 ptr = mremap(r.begin, r.size, r.size, MREMAP_MAYMOVE | MREMAP_DONTUNMAP);
1743 if (ptr == MAP_FAILED)
1748 if (mprotect(ptr, r.size, PROT_READ | PROT_WRITE) != 0) {
1749 munmap(ptr, r.size);
1752#elif defined(Q_OS_DARWIN)
1753 mach_port_t self = mach_task_self();
1754 vm_address_t addr = 0;
1755 vm_address_t mask = 0;
1756 bool anywhere =
true;
1758 vm_prot_t cur_prot = VM_PROT_READ | VM_PROT_WRITE;
1759 vm_prot_t max_prot = VM_PROT_ALL;
1760 kern_return_t res = vm_remap(self, &addr, r.size, mask, anywhere,
1761 self, vm_address_t(r.begin), copy, &cur_prot,
1762 &max_prot, VM_INHERIT_DEFAULT);
1763 if (res != KERN_SUCCESS)
1766 ptr =
reinterpret_cast<
void *>(addr);
1767 if ((max_prot & VM_PROT_WRITE) == 0 || mprotect(ptr, r.size, PROT_READ | PROT_WRITE) != 0) {
1768 munmap(ptr, r.size);
1775 const char *newdata =
static_cast<
char *>(ptr) + r.offset;
1776 uncompressed = QByteArray::fromRawData(newdata, resource.uncompressedSize());
1783 auto r = mappingBoundaries(uncompressed.constBegin(), uncompressed.size());
1784 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()
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)