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 bool nameMatches(
int node, QStringView other)
const;
127 short flags(
int node)
const;
129 mutable QAtomicInt ref;
131 inline QResourceRoot(): tree(
nullptr), names(
nullptr), payloads(
nullptr), version(0) {}
132 inline QResourceRoot(
int version,
const uchar *t,
const uchar *n,
const uchar *d) { setSource(version, t, n, d); }
133 virtual ~QResourceRoot() =
default;
134 Q_DISABLE_COPY_MOVE(QResourceRoot)
135 int findNode(
const QString &path,
const QLocale &locale=QLocale())
const;
136 inline bool isContainer(
int node)
const {
return flags(node) & Directory; }
137 QResource::Compression compressionAlgo(
int node)
139 uint compressionFlags = flags(node) & (Compressed | CompressedZstd);
140 if (compressionFlags == Compressed)
141 return QResource::ZlibCompression;
142 if (compressionFlags == CompressedZstd)
143 return QResource::ZstdCompression;
144 return QResource::NoCompression;
146 const uchar *data(
int node, qint64 *size)
const;
147 qint64 lastModified(
int node)
const;
148 QStringList children(
int node)
const;
149 virtual QString mappingRoot()
const {
return QString(); }
150 bool mappingRootSubdir(
const QString &path, QString *match =
nullptr)
const;
151 inline bool operator==(
const QResourceRoot &other)
const
152 {
return tree == other.tree && names == other.names && payloads == other.payloads && version == other.version; }
153 inline bool operator!=(
const QResourceRoot &other)
const
154 {
return !operator==(other); }
155 enum ResourceRootType { Resource_Builtin, Resource_File, Resource_Buffer };
156 virtual ResourceRootType type()
const {
return Resource_Builtin; }
159 inline void setSource(
int v,
const uchar *t,
const uchar *n,
const uchar *d) {
167static QString cleanPath(
const QString &_path)
169 QString path = QDir::cleanPath(_path);
172 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);
792inline bool QResourceRoot::nameMatches(
int node, QStringView other)
const
795 return other.isEmpty();
796 const int offset = findOffset(node);
798 qint32 name_offset = qFromBigEndian<qint32>(tree + offset);
799 qsizetype name_length = qFromBigEndian<qint16>(names + name_offset);
803 if (other.size() != name_length)
805 const QChar *strData =
reinterpret_cast<
const QChar *>(names + name_offset);
806 for (qsizetype ch = 0; ch < name_length; ++ch) {
807 if (other.at(ch) != qFromBigEndian<
char16_t>(strData + ch))
813int QResourceRoot::findNode(
const QString &_path,
const QLocale &locale)
const
815 QString path = _path;
817 QString root = mappingRoot();
818 if (!root.isEmpty()) {
822 if (!root.endsWith(u'/'))
824 if (path.size() >= root.size() && path.startsWith(root))
825 path = path.mid(root.size() - 1);
831#ifdef DEBUG_RESOURCE_MATCH
832 qDebug() <<
"!!!!" <<
"START" << path << locale.territory() << locale.language();
839 qint32 child_count = qFromBigEndian<qint32>(tree + 6);
840 qint32 child = qFromBigEndian<qint32>(tree + 10);
845 QStringSplitter splitter(path);
846 while (child_count && splitter.hasNext()) {
847 QStringView segment = splitter.next();
849#ifdef DEBUG_RESOURCE_MATCH
850 qDebug() <<
" CHILDREN" << segment;
851 for (
int j = 0; j < child_count; ++j) {
852 qDebug() <<
" " << child + j <<
" :: " << name(child + j);
855 const uint h = qt_hash(segment);
858 int l = 0, r = child_count - 1;
859 int sub_node = (l + r + 1) / 2;
861 const uint sub_node_hash = hash(child + sub_node);
862 if (h == sub_node_hash)
864 else if (h < sub_node_hash)
868 sub_node = (l + r + 1) / 2;
874 if (hash(sub_node) == h) {
875 while (sub_node > child && hash(sub_node - 1) == h)
877 for (; sub_node < child + child_count && hash(sub_node) == h;
879 if (nameMatches(sub_node, segment)) {
881 int offset = findOffset(sub_node);
882#ifdef DEBUG_RESOURCE_MATCH
883 qDebug() <<
" TRY" << sub_node << name(sub_node) << offset;
887 const qint16 flags = qFromBigEndian<qint16>(tree + offset);
890 if (!splitter.hasNext()) {
891 if (!(flags & Directory)) {
892 const qint16 territory = qFromBigEndian<qint16>(tree + offset);
895 const qint16 language = qFromBigEndian<qint16>(tree + offset);
897#ifdef DEBUG_RESOURCE_MATCH
898 qDebug() <<
" " <<
"LOCALE" << country << language;
900 if (territory == locale.territory() && language == locale.language()) {
901#ifdef DEBUG_RESOURCE_MATCH
902 qDebug() <<
"!!!!" <<
"FINISHED" << __LINE__ << sub_node;
905 }
else if ((territory == QLocale::AnyTerritory
906 && language == locale.language())
907 || (territory == QLocale::AnyTerritory
908 && language == QLocale::C
914#ifdef DEBUG_RESOURCE_MATCH
915 qDebug() <<
"!!!!" <<
"FINISHED" << __LINE__ << sub_node;
922 if (!(flags & Directory))
925 child_count = qFromBigEndian<qint32>(tree + offset);
927 child = qFromBigEndian<qint32>(tree + offset);
935#ifdef DEBUG_RESOURCE_MATCH
936 qDebug() <<
"!!!!" <<
"FINISHED" << __LINE__ << node;
940short QResourceRoot::flags(
int node)
const
944 const int offset = findOffset(node) + 4;
945 return qFromBigEndian<qint16>(tree + offset);
947const uchar *QResourceRoot::data(
int node, qint64 *size)
const
953 int offset = findOffset(node) + 4;
955 const qint16 flags = qFromBigEndian<qint16>(tree + offset);
960 if (!(flags & Directory)) {
961 const qint32 data_offset = qFromBigEndian<qint32>(tree + offset);
962 const quint32 data_length = qFromBigEndian<quint32>(payloads + data_offset);
963 const uchar *ret = payloads + data_offset + 4;
971qint64 QResourceRoot::lastModified(
int node)
const
973 if (node == -1 || version < 0x02)
976 const int offset = findOffset(node) + 14;
978 return qFromBigEndian<qint64>(tree + offset);
981QStringList QResourceRoot::children(
int node)
const
984 return QStringList();
985 int offset = findOffset(node) + 4;
987 const qint16 flags = qFromBigEndian<qint16>(tree + offset);
991 if (flags & Directory) {
992 const qint32 child_count = qFromBigEndian<qint32>(tree + offset);
994 const qint32 child_off = qFromBigEndian<qint32>(tree + offset);
995 ret.reserve(child_count);
996 for (
int i = child_off; i < child_off + child_count; ++i)
1001bool QResourceRoot::mappingRootSubdir(
const QString &path, QString *match)
const
1003 const QString root = mappingRoot();
1007 QStringSplitter rootIt(root);
1008 QStringSplitter pathIt(path);
1009 while (rootIt.hasNext()) {
1010 if (pathIt.hasNext()) {
1011 if (rootIt.next() != pathIt.next())
1016 *match = rootIt.next().toString();
1021 return !pathIt.hasNext();
1025 const unsigned char *name,
const unsigned char *data)
1027 if (resourceGlobalData.isDestroyed())
1029 const auto locker = qt_scoped_lock(resourceMutex());
1031 if (version >= 0x01 && version <= 0x3) {
1033 QResourceRoot res(version, tree, name, data);
1034 for (
int i = 0; i < list->size(); ++i) {
1035 if (*list->at(i) == res) {
1041 QResourceRoot *root =
new QResourceRoot(version, tree, name, data);
1051 const unsigned char *name,
const unsigned char *data)
1053 if (resourceGlobalData.isDestroyed())
1056 const auto locker = qt_scoped_lock(resourceMutex());
1057 if (version >= 0x01 && version <= 0x3) {
1058 QResourceRoot res(version, tree, name, data);
1060 for (
int i = 0; i < list->size();) {
1061 if (*list->at(i) == res) {
1062 QResourceRoot *root = list->takeAt(i);
1063 if (!root->ref.deref())
1076class QDynamicBufferResourceRoot :
public QResourceRoot
1079 const uchar *buffer;
1082 inline QDynamicBufferResourceRoot(
const QString &_root) : root(_root), buffer(
nullptr) { }
1083 inline ~QDynamicBufferResourceRoot() { }
1084 inline const uchar *mappingBuffer()
const {
return buffer; }
1085 QString mappingRoot()
const override {
return root; }
1086 ResourceRootType type()
const override {
return Resource_Buffer; }
1089 bool registerSelf(
const uchar *b, qsizetype size,
const QString &source = {})
1092 if (size >= 0 && size < 20)
1099 if (b[offset + 0] !=
'q' || b[offset + 1] !=
'r' || b[offset + 2] !=
'e'
1100 || b[offset + 3] !=
's') {
1105 const int version = qFromBigEndian<qint32>(b + offset);
1108 const int tree_offset = qFromBigEndian<qint32>(b + offset);
1111 const int data_offset = qFromBigEndian<qint32>(b + offset);
1114 const int name_offset = qFromBigEndian<qint32>(b + offset);
1117 quint32 file_flags = 0;
1119 file_flags = qFromBigEndian<qint32>(b + offset);
1124 if (size >= 0 && (tree_offset >= size || data_offset >= size || name_offset >= size))
1128 quint32 acceptableFlags = 0;
1129#ifndef QT_NO_COMPRESS
1130 acceptableFlags |= Compressed;
1132 if (QT_CONFIG(zstd))
1133 acceptableFlags |= CompressedZstd;
1134 if (
const quint32 unsupportedFlags = file_flags & ~acceptableFlags) {
1135 const QString id = source.isEmpty() ? QString::asprintf(
"<buffer at %p>", b) : source;
1136 qWarning(
"QResource: %s uses unsupported compression flags 0x%x (supported: 0x%x)",
1137 qUtf8Printable(id), unsupportedFlags, acceptableFlags);
1141 if (version >= 0x01 && version <= 0x03) {
1143 setSource(version, b + tree_offset, b + name_offset, b + data_offset);
1150class QDynamicFileResourceRoot :
public QDynamicBufferResourceRoot
1153 static uchar *map_sys(QFile &file, qint64 base, qsizetype size);
1154 static void unmap_sys(
void *base, qsizetype size);
1159 uchar *unmapPointer;
1160 qsizetype unmapLength;
1163 QDynamicFileResourceRoot(
const QString &_root)
1164 : QDynamicBufferResourceRoot(_root), unmapPointer(
nullptr), unmapLength(0)
1166 ~QDynamicFileResourceRoot() {
1167 if (wasMemoryMapped())
1168 unmap_sys(unmapPointer, unmapLength);
1170 delete[] mappingBuffer();
1172 QString mappingFile()
const {
return fileName; }
1173 ResourceRootType type()
const override {
return Resource_File; }
1174 bool wasMemoryMapped()
const {
return unmapPointer; }
1176 bool registerSelf(
const QString &f);
1184# define MAP_FAILED reinterpret_cast<void *>(-1
)
1187void QDynamicFileResourceRoot::unmap_sys(
void *base, qsizetype size)
1189#if defined(QT_USE_MMAP)
1191#elif defined(Q_OS_WIN)
1193 UnmapViewOfFile(
reinterpret_cast<
void *>(base));
1198uchar *QDynamicFileResourceRoot::map_sys(QFile &file, qint64 offset, qsizetype size)
1200 Q_ASSERT(file.isOpen());
1201 void *ptr =
nullptr;
1203 size = qMin(file.size() - offset, (std::numeric_limits<qsizetype>::max)());
1204 int fd = file.handle();
1208#if defined(QT_USE_MMAP)
1209 int protection = PROT_READ;
1210 int flags = MAP_FILE | MAP_PRIVATE;
1211 ptr = QT_MMAP(
nullptr, size, protection, flags, fd, offset);
1212 if (ptr == MAP_FAILED)
1214#elif defined(Q_OS_WIN)
1215 HANDLE fileHandle =
reinterpret_cast<HANDLE>(_get_osfhandle(fd));
1216 if (fileHandle != INVALID_HANDLE_VALUE) {
1217 HANDLE mapHandle = CreateFileMapping(fileHandle, 0, PAGE_WRITECOPY, 0, 0, 0);
1219 ptr = MapViewOfFile(mapHandle, FILE_MAP_COPY, DWORD(offset >> 32), DWORD(offset), size);
1220 CloseHandle(mapHandle);
1224 return static_cast<uchar *>(ptr);
1227bool QDynamicFileResourceRoot::registerSelf(
const QString &f)
1230 if (!file.open(QIODevice::ReadOnly))
1233 qint64 data_len = file.size();
1234 if (data_len > std::numeric_limits<qsizetype>::max())
1237 uchar *data = map_sys(file, 0, data_len);
1238 bool fromMM = !!data;
1242 data =
new uchar[data_len];
1243 ok = (data_len == file.read(
reinterpret_cast<
char *>(data), data_len));
1251 if (data && QDynamicBufferResourceRoot::registerSelf(data, data_len, f)) {
1253 unmapPointer = data;
1254 unmapLength = data_len;
1265 if (r.startsWith(u':'))
1268 r = QDir::cleanPath(r);
1274
1275
1276
1277
1278
1279
1280
1281
1283bool QResource::registerResource(
const QString &rccFilename,
const QString &resourceRoot)
1285 QString r = qt_resource_fixResourceRoot(resourceRoot);
1286 if (!r.isEmpty() && r[0] != u'/') {
1287 qWarning(
"QDir::registerResource: Registering a resource [%ls] must be rooted in an "
1288 "absolute path (start with /) [%ls]",
1289 qUtf16Printable(rccFilename), qUtf16Printable(resourceRoot));
1293 QDynamicFileResourceRoot *root =
new QDynamicFileResourceRoot(r);
1294 if (root->registerSelf(rccFilename)) {
1296 const auto locker = qt_scoped_lock(resourceMutex());
1297 resourceList()->append(root);
1305
1306
1307
1308
1309
1310
1311
1312
1313
1315bool QResource::unregisterResource(
const QString &rccFilename,
const QString &resourceRoot)
1317 QString r = qt_resource_fixResourceRoot(resourceRoot);
1319 const auto locker = qt_scoped_lock(resourceMutex());
1320 ResourceList *list = resourceList();
1321 for (
int i = 0; i < list->size(); ++i) {
1322 QResourceRoot *res = list->at(i);
1323 if (res->type() == QResourceRoot::Resource_File) {
1324 QDynamicFileResourceRoot *root =
reinterpret_cast<QDynamicFileResourceRoot *>(res);
1325 if (root->mappingFile() == rccFilename && root->mappingRoot() == r) {
1327 if (!root->ref.deref()) {
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1352bool QResource::registerResource(
const uchar *rccData,
const QString &resourceRoot)
1354 QString r = qt_resource_fixResourceRoot(resourceRoot);
1355 if (!r.isEmpty() && r[0] != u'/') {
1356 qWarning(
"QDir::registerResource: Registering a resource [%p] must be rooted in an "
1357 "absolute path (start with /) [%ls]",
1358 rccData, qUtf16Printable(resourceRoot));
1362 QDynamicBufferResourceRoot *root =
new QDynamicBufferResourceRoot(r);
1363 if (root->registerSelf(rccData, -1)) {
1365 const auto locker = qt_scoped_lock(resourceMutex());
1366 resourceList()->append(root);
1374
1375
1376
1377
1378
1379
1380
1381
1382
1384bool QResource::unregisterResource(
const uchar *rccData,
const QString &resourceRoot)
1386 QString r = qt_resource_fixResourceRoot(resourceRoot);
1388 const auto locker = qt_scoped_lock(resourceMutex());
1389 ResourceList *list = resourceList();
1390 for (
int i = 0; i < list->size(); ++i) {
1391 QResourceRoot *res = list->at(i);
1392 if (res->type() == QResourceRoot::Resource_Buffer) {
1393 QDynamicBufferResourceRoot *root =
reinterpret_cast<QDynamicBufferResourceRoot *>(res);
1394 if (root->mappingBuffer() == rccData && root->mappingRoot() == r) {
1396 if (!root->ref.deref()) {
1407#if !defined(QT_BOOTSTRAPPED)
1412 Q_DECLARE_PUBLIC(QResourceFileEngine)
1415 bool unmap(uchar *ptr);
1416 void uncompress()
const;
1417 void mapUncompressed();
1418 bool mapUncompressed_sys();
1419 void unmapUncompressed_sys();
1423 bool mustUnmap =
false;
1426 static constexpr qsizetype RemapCompressedThreshold = 16384;
1434 unmapUncompressed_sys();
1444 QAbstractFileEngine(*
new QResourceFileEnginePrivate(
this))
1446 Q_D(QResourceFileEngine);
1447 d->resource.setFileName(file);
1456 Q_D(QResourceFileEngine);
1457 d->resource.setFileName(file);
1461 std::optional<QFile::Permissions> permissions)
1463 Q_UNUSED(permissions);
1465 Q_D(QResourceFileEngine);
1466 if (d->resource.fileName().isEmpty()) {
1467 qWarning(
"QResourceFileEngine::open: Missing file name");
1470 if (flags & QIODevice::WriteOnly)
1472 if (d->resource.compressionAlgorithm() != QResource::NoCompression) {
1474 if (d->uncompressed.isNull()) {
1475 d->errorString = QSystemError::stdString(EIO);
1479 if (!d->resource.isValid()) {
1480 d->errorString = QSystemError::stdString(ENOENT);
1488 Q_D(QResourceFileEngine);
1500 Q_D(QResourceFileEngine);
1501 if (len > size() - d->offset)
1502 len = size() - d->offset;
1505 if (!d->uncompressed.isNull())
1506 memcpy(data, d->uncompressed.constData() + d->offset, len);
1508 memcpy(data, d->resource.data() + d->offset, len);
1515 Q_D(
const QResourceFileEngine);
1516 return d->resource.isValid() ? d->resource.uncompressedSize() : 0;
1521 Q_D(
const QResourceFileEngine);
1527 Q_D(
const QResourceFileEngine);
1528 if (!d->resource.isValid())
1530 return d->offset == size();
1535 Q_D(QResourceFileEngine);
1536 if (!d->resource.isValid())
1539 if (d->offset > size())
1547 Q_D(
const QResourceFileEngine);
1548 QAbstractFileEngine::FileFlags ret;
1549 if (!d->resource.isValid())
1552 if (type & PermsMask)
1553 ret |= QAbstractFileEngine::FileFlags(ReadOwnerPerm | ReadUserPerm | ReadGroupPerm
1555 if (type & TypesMask) {
1556 if (d->resource.isDir())
1557 ret |= DirectoryType;
1561 if (type & FlagsMask) {
1563 if (d->resource.absoluteFilePath() ==
":/"_L1)
1571 Q_D(
const QResourceFileEngine);
1572 if (file == BaseName) {
1573 const qsizetype slash = d->resource.fileName().lastIndexOf(u'/');
1575 return d->resource.fileName();
1576 return d->resource.fileName().mid(slash + 1);
1577 }
else if (file == PathName || file == AbsolutePathName) {
1578 const QString path = (file == AbsolutePathName) ? d->resource.absoluteFilePath()
1579 : d->resource.fileName();
1580 const qsizetype slash = path.lastIndexOf(u'/');
1583 else if (slash <= 1)
1585 return path.left(slash);
1587 }
else if (file == CanonicalName || file == CanonicalPathName) {
1588 const QString absoluteFilePath = d->resource.absoluteFilePath();
1589 if (file == CanonicalPathName) {
1590 const qsizetype slash = absoluteFilePath.lastIndexOf(u'/');
1592 return absoluteFilePath.left(slash);
1594 return absoluteFilePath;
1596 return d->resource.fileName();
1601 static const uint nobodyID =
static_cast<uint>(-2);
1607 Q_D(
const QResourceFileEngine);
1608 if (time == QFile::FileModificationTime)
1609 return d->resource.lastModified();
1614
1615
1616QAbstractFileEngine::IteratorUniquePtr
1618 const QStringList &filterNames)
1625 Q_D(QResourceFileEngine);
1626 if (extension == MapExtension) {
1627 const auto *options =
static_cast<
const MapExtensionOption *>(option);
1628 auto *returnValue =
static_cast<MapExtensionReturn *>(output);
1629 returnValue->address = d->map(options->offset, options->size, options->flags);
1630 return (returnValue->address !=
nullptr);
1632 if (extension == UnMapExtension) {
1633 const auto *options =
static_cast<
const UnMapExtensionOption *>(option);
1634 return d->unmap(options->address);
1641 return (extension == UnMapExtension || extension == MapExtension);
1646 Q_Q(QResourceFileEngine);
1647 Q_ASSERT_X(resource.compressionAlgorithm() == QResource::NoCompression
1648 || !uncompressed.isNull(),
"QFile::map()",
1649 "open() should have uncompressed compressed resources");
1651 qint64 max = resource.uncompressedSize();
1653 if (offset < 0 || size <= 0 || !resource.isValid() ||
1654 qAddOverflow(offset, size, &end) || end > max) {
1655 q->setError(QFile::UnspecifiedError, QString());
1659 const uchar *address =
reinterpret_cast<
const uchar *>(uncompressed.constBegin());
1660 if (!uncompressed.isNull())
1661 return const_cast<uchar *>(address) + offset;
1664 address = resource.data();
1665 if (flags & QFile::MapPrivateOption) {
1668 address =
reinterpret_cast<
const uchar *>(uncompressed.constData());
1671 return const_cast<uchar *>(address) + offset;
1682 if (resource.compressionAlgorithm() == QResource::NoCompression
1683 || !uncompressed.isEmpty() || resource.size() == 0)
1685 uncompressed = resource.uncompressedData();
1690 Q_ASSERT(resource.compressionAlgorithm() == QResource::NoCompression);
1691 if (!uncompressed.isNull())
1694 if (resource.uncompressedSize() >= RemapCompressedThreshold) {
1695 if (mapUncompressed_sys())
1699 uncompressed = resource.uncompressedData();
1700 uncompressed.detach();
1703#if defined(MREMAP_MAYMOVE) && defined(MREMAP_DONTUNMAP)
1704inline bool QResourcePrivate::mayRemapData(
const QResource &resource)
1706 auto d = resource.d_func();
1712 const QResourceRoot *root = d->related.at(0);
1714 switch (root->type()) {
1715 case QResourceRoot::Resource_Builtin:
1717 case QResourceRoot::Resource_Buffer:
1719 case QResourceRoot::Resource_File:
1723 auto df =
static_cast<
const QDynamicFileResourceRoot *>(root);
1724 return df->wasMemoryMapped();
1732 auto getpagesize = [] {
1733 SYSTEM_INFO sysinfo;
1734 ::GetSystemInfo(&sysinfo);
1735 return sysinfo.dwAllocationGranularity;
1744 const quintptr pageMask = getpagesize() - 1;
1745 quintptr data = quintptr(location);
1746 quintptr begin = data & ~pageMask;
1747 quintptr end = (data + size + pageMask) & ~pageMask;
1748 r.begin =
reinterpret_cast<
void *>(begin);
1749 r.size = end - begin;
1750 r.offset = data & pageMask;
1756 auto r = mappingBoundaries(resource.data(), resource.uncompressedSize());
1757 void *ptr =
nullptr;
1759#if defined(MREMAP_MAYMOVE) && defined(MREMAP_DONTUNMAP)
1765 if (!QResourcePrivate::mayRemapData(resource))
1768 ptr = mremap(r.begin, r.size, r.size, MREMAP_MAYMOVE | MREMAP_DONTUNMAP);
1769 if (ptr == MAP_FAILED)
1774 if (mprotect(ptr, r.size, PROT_READ | PROT_WRITE) != 0) {
1775 munmap(ptr, r.size);
1778#elif defined(Q_OS_DARWIN)
1779 mach_port_t self = mach_task_self();
1780 vm_address_t addr = 0;
1781 vm_address_t mask = 0;
1782 bool anywhere =
true;
1784 vm_prot_t cur_prot = VM_PROT_READ | VM_PROT_WRITE;
1785 vm_prot_t max_prot = VM_PROT_ALL;
1786 kern_return_t res = vm_remap(self, &addr, r.size, mask, anywhere,
1787 self, vm_address_t(r.begin), copy, &cur_prot,
1788 &max_prot, VM_INHERIT_DEFAULT);
1789 if (res != KERN_SUCCESS)
1792 ptr =
reinterpret_cast<
void *>(addr);
1793 if ((max_prot & VM_PROT_WRITE) == 0 || mprotect(ptr, r.size, PROT_READ | PROT_WRITE) != 0) {
1794 munmap(ptr, r.size);
1801 const char *newdata =
static_cast<
char *>(ptr) + r.offset;
1802 uncompressed = QByteArray::fromRawData(newdata, resource.uncompressedSize());
1809 auto r = mappingBoundaries(uncompressed.constBegin(), uncompressed.size());
1810 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)