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)
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 (file_flags & ~acceptableFlags)
1137 if (version >= 0x01 && version <= 0x03) {
1139 setSource(version, b + tree_offset, b + name_offset, b + data_offset);
1146class QDynamicFileResourceRoot :
public QDynamicBufferResourceRoot
1149 static uchar *map_sys(QFile &file, qint64 base, qsizetype size);
1150 static void unmap_sys(
void *base, qsizetype size);
1155 uchar *unmapPointer;
1156 qsizetype unmapLength;
1159 QDynamicFileResourceRoot(
const QString &_root)
1160 : QDynamicBufferResourceRoot(_root), unmapPointer(
nullptr), unmapLength(0)
1162 ~QDynamicFileResourceRoot() {
1163 if (wasMemoryMapped())
1164 unmap_sys(unmapPointer, unmapLength);
1166 delete[] mappingBuffer();
1168 QString mappingFile()
const {
return fileName; }
1169 ResourceRootType type()
const override {
return Resource_File; }
1170 bool wasMemoryMapped()
const {
return unmapPointer; }
1172 bool registerSelf(
const QString &f);
1180# define MAP_FAILED reinterpret_cast<void *>(-1
)
1183void QDynamicFileResourceRoot::unmap_sys(
void *base, qsizetype size)
1185#if defined(QT_USE_MMAP)
1187#elif defined(Q_OS_WIN)
1189 UnmapViewOfFile(
reinterpret_cast<
void *>(base));
1194uchar *QDynamicFileResourceRoot::map_sys(QFile &file, qint64 offset, qsizetype size)
1196 Q_ASSERT(file.isOpen());
1197 void *ptr =
nullptr;
1199 size = qMin(file.size() - offset, (std::numeric_limits<qsizetype>::max)());
1200 int fd = file.handle();
1204#if defined(QT_USE_MMAP)
1205 int protection = PROT_READ;
1206 int flags = MAP_FILE | MAP_PRIVATE;
1207 ptr = QT_MMAP(
nullptr, size, protection, flags, fd, offset);
1208 if (ptr == MAP_FAILED)
1210#elif defined(Q_OS_WIN)
1211 HANDLE fileHandle =
reinterpret_cast<HANDLE>(_get_osfhandle(fd));
1212 if (fileHandle != INVALID_HANDLE_VALUE) {
1213 HANDLE mapHandle = CreateFileMapping(fileHandle, 0, PAGE_WRITECOPY, 0, 0, 0);
1215 ptr = MapViewOfFile(mapHandle, FILE_MAP_COPY, DWORD(offset >> 32), DWORD(offset), size);
1216 CloseHandle(mapHandle);
1220 return static_cast<uchar *>(ptr);
1223bool QDynamicFileResourceRoot::registerSelf(
const QString &f)
1226 if (!file.open(QIODevice::ReadOnly))
1229 qint64 data_len = file.size();
1230 if (data_len > std::numeric_limits<qsizetype>::max())
1233 uchar *data = map_sys(file, 0, data_len);
1234 bool fromMM = !!data;
1238 data =
new uchar[data_len];
1239 ok = (data_len == file.read(
reinterpret_cast<
char *>(data), data_len));
1247 if (data && QDynamicBufferResourceRoot::registerSelf(data, data_len)) {
1249 unmapPointer = data;
1250 unmapLength = data_len;
1261 if (r.startsWith(u':'))
1264 r = QDir::cleanPath(r);
1270
1271
1272
1273
1274
1275
1276
1277
1279bool QResource::registerResource(
const QString &rccFilename,
const QString &resourceRoot)
1281 QString r = qt_resource_fixResourceRoot(resourceRoot);
1282 if (!r.isEmpty() && r[0] != u'/') {
1283 qWarning(
"QDir::registerResource: Registering a resource [%ls] must be rooted in an "
1284 "absolute path (start with /) [%ls]",
1285 qUtf16Printable(rccFilename), qUtf16Printable(resourceRoot));
1289 QDynamicFileResourceRoot *root =
new QDynamicFileResourceRoot(r);
1290 if (root->registerSelf(rccFilename)) {
1292 const auto locker = qt_scoped_lock(resourceMutex());
1293 resourceList()->append(root);
1301
1302
1303
1304
1305
1306
1307
1308
1309
1311bool QResource::unregisterResource(
const QString &rccFilename,
const QString &resourceRoot)
1313 QString r = qt_resource_fixResourceRoot(resourceRoot);
1315 const auto locker = qt_scoped_lock(resourceMutex());
1316 ResourceList *list = resourceList();
1317 for (
int i = 0; i < list->size(); ++i) {
1318 QResourceRoot *res = list->at(i);
1319 if (res->type() == QResourceRoot::Resource_File) {
1320 QDynamicFileResourceRoot *root =
reinterpret_cast<QDynamicFileResourceRoot *>(res);
1321 if (root->mappingFile() == rccFilename && root->mappingRoot() == r) {
1323 if (!root->ref.deref()) {
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1348bool QResource::registerResource(
const uchar *rccData,
const QString &resourceRoot)
1350 QString r = qt_resource_fixResourceRoot(resourceRoot);
1351 if (!r.isEmpty() && r[0] != u'/') {
1352 qWarning(
"QDir::registerResource: Registering a resource [%p] must be rooted in an "
1353 "absolute path (start with /) [%ls]",
1354 rccData, qUtf16Printable(resourceRoot));
1358 QDynamicBufferResourceRoot *root =
new QDynamicBufferResourceRoot(r);
1359 if (root->registerSelf(rccData, -1)) {
1361 const auto locker = qt_scoped_lock(resourceMutex());
1362 resourceList()->append(root);
1370
1371
1372
1373
1374
1375
1376
1377
1378
1380bool QResource::unregisterResource(
const uchar *rccData,
const QString &resourceRoot)
1382 QString r = qt_resource_fixResourceRoot(resourceRoot);
1384 const auto locker = qt_scoped_lock(resourceMutex());
1385 ResourceList *list = resourceList();
1386 for (
int i = 0; i < list->size(); ++i) {
1387 QResourceRoot *res = list->at(i);
1388 if (res->type() == QResourceRoot::Resource_Buffer) {
1389 QDynamicBufferResourceRoot *root =
reinterpret_cast<QDynamicBufferResourceRoot *>(res);
1390 if (root->mappingBuffer() == rccData && root->mappingRoot() == r) {
1392 if (!root->ref.deref()) {
1403#if !defined(QT_BOOTSTRAPPED)
1408 Q_DECLARE_PUBLIC(QResourceFileEngine)
1411 bool unmap(uchar *ptr);
1412 void uncompress()
const;
1413 void mapUncompressed();
1414 bool mapUncompressed_sys();
1415 void unmapUncompressed_sys();
1419 bool mustUnmap =
false;
1422 static constexpr qsizetype RemapCompressedThreshold = 16384;
1430 unmapUncompressed_sys();
1440 QAbstractFileEngine(*
new QResourceFileEnginePrivate(
this))
1442 Q_D(QResourceFileEngine);
1443 d->resource.setFileName(file);
1452 Q_D(QResourceFileEngine);
1453 d->resource.setFileName(file);
1457 std::optional<QFile::Permissions> permissions)
1459 Q_UNUSED(permissions);
1461 Q_D(QResourceFileEngine);
1462 if (d->resource.fileName().isEmpty()) {
1463 qWarning(
"QResourceFileEngine::open: Missing file name");
1466 if (flags & QIODevice::WriteOnly)
1468 if (d->resource.compressionAlgorithm() != QResource::NoCompression) {
1470 if (d->uncompressed.isNull()) {
1471 d->errorString = QSystemError::stdString(EIO);
1475 if (!d->resource.isValid()) {
1476 d->errorString = QSystemError::stdString(ENOENT);
1484 Q_D(QResourceFileEngine);
1496 Q_D(QResourceFileEngine);
1497 if (len > size() - d->offset)
1498 len = size() - d->offset;
1501 if (!d->uncompressed.isNull())
1502 memcpy(data, d->uncompressed.constData() + d->offset, len);
1504 memcpy(data, d->resource.data() + d->offset, len);
1511 Q_D(
const QResourceFileEngine);
1512 return d->resource.isValid() ? d->resource.uncompressedSize() : 0;
1517 Q_D(
const QResourceFileEngine);
1523 Q_D(
const QResourceFileEngine);
1524 if (!d->resource.isValid())
1526 return d->offset == size();
1531 Q_D(QResourceFileEngine);
1532 if (!d->resource.isValid())
1535 if (d->offset > size())
1543 Q_D(
const QResourceFileEngine);
1544 QAbstractFileEngine::FileFlags ret;
1545 if (!d->resource.isValid())
1548 if (type & PermsMask)
1549 ret |= QAbstractFileEngine::FileFlags(ReadOwnerPerm | ReadUserPerm | ReadGroupPerm
1551 if (type & TypesMask) {
1552 if (d->resource.isDir())
1553 ret |= DirectoryType;
1557 if (type & FlagsMask) {
1559 if (d->resource.absoluteFilePath() ==
":/"_L1)
1567 Q_D(
const QResourceFileEngine);
1568 if (file == BaseName) {
1569 const qsizetype slash = d->resource.fileName().lastIndexOf(u'/');
1571 return d->resource.fileName();
1572 return d->resource.fileName().mid(slash + 1);
1573 }
else if (file == PathName || file == AbsolutePathName) {
1574 const QString path = (file == AbsolutePathName) ? d->resource.absoluteFilePath()
1575 : d->resource.fileName();
1576 const qsizetype slash = path.lastIndexOf(u'/');
1579 else if (slash <= 1)
1581 return path.left(slash);
1583 }
else if (file == CanonicalName || file == CanonicalPathName) {
1584 const QString absoluteFilePath = d->resource.absoluteFilePath();
1585 if (file == CanonicalPathName) {
1586 const qsizetype slash = absoluteFilePath.lastIndexOf(u'/');
1588 return absoluteFilePath.left(slash);
1590 return absoluteFilePath;
1592 return d->resource.fileName();
1597 static const uint nobodyID =
static_cast<uint>(-2);
1603 Q_D(
const QResourceFileEngine);
1604 if (time == QFile::FileModificationTime)
1605 return d->resource.lastModified();
1610
1611
1612QAbstractFileEngine::IteratorUniquePtr
1614 const QStringList &filterNames)
1621 Q_D(QResourceFileEngine);
1622 if (extension == MapExtension) {
1623 const auto *options =
static_cast<
const MapExtensionOption *>(option);
1624 auto *returnValue =
static_cast<MapExtensionReturn *>(output);
1625 returnValue->address = d->map(options->offset, options->size, options->flags);
1626 return (returnValue->address !=
nullptr);
1628 if (extension == UnMapExtension) {
1629 const auto *options =
static_cast<
const UnMapExtensionOption *>(option);
1630 return d->unmap(options->address);
1637 return (extension == UnMapExtension || extension == MapExtension);
1642 Q_Q(QResourceFileEngine);
1643 Q_ASSERT_X(resource.compressionAlgorithm() == QResource::NoCompression
1644 || !uncompressed.isNull(),
"QFile::map()",
1645 "open() should have uncompressed compressed resources");
1647 qint64 max = resource.uncompressedSize();
1649 if (offset < 0 || size <= 0 || !resource.isValid() ||
1650 qAddOverflow(offset, size, &end) || end > max) {
1651 q->setError(QFile::UnspecifiedError, QString());
1655 const uchar *address =
reinterpret_cast<
const uchar *>(uncompressed.constBegin());
1656 if (!uncompressed.isNull())
1657 return const_cast<uchar *>(address) + offset;
1660 address = resource.data();
1661 if (flags & QFile::MapPrivateOption) {
1664 address =
reinterpret_cast<
const uchar *>(uncompressed.constData());
1667 return const_cast<uchar *>(address) + offset;
1678 if (resource.compressionAlgorithm() == QResource::NoCompression
1679 || !uncompressed.isEmpty() || resource.size() == 0)
1681 uncompressed = resource.uncompressedData();
1686 Q_ASSERT(resource.compressionAlgorithm() == QResource::NoCompression);
1687 if (!uncompressed.isNull())
1690 if (resource.uncompressedSize() >= RemapCompressedThreshold) {
1691 if (mapUncompressed_sys())
1695 uncompressed = resource.uncompressedData();
1696 uncompressed.detach();
1699#if defined(MREMAP_MAYMOVE) && defined(MREMAP_DONTUNMAP)
1700inline bool QResourcePrivate::mayRemapData(
const QResource &resource)
1702 auto d = resource.d_func();
1708 const QResourceRoot *root = d->related.at(0);
1710 switch (root->type()) {
1711 case QResourceRoot::Resource_Builtin:
1713 case QResourceRoot::Resource_Buffer:
1715 case QResourceRoot::Resource_File:
1719 auto df =
static_cast<
const QDynamicFileResourceRoot *>(root);
1720 return df->wasMemoryMapped();
1728 auto getpagesize = [] {
1729 SYSTEM_INFO sysinfo;
1730 ::GetSystemInfo(&sysinfo);
1731 return sysinfo.dwAllocationGranularity;
1740 const quintptr pageMask = getpagesize() - 1;
1741 quintptr data = quintptr(location);
1742 quintptr begin = data & ~pageMask;
1743 quintptr end = (data + size + pageMask) & ~pageMask;
1744 r.begin =
reinterpret_cast<
void *>(begin);
1745 r.size = end - begin;
1746 r.offset = data & pageMask;
1752 auto r = mappingBoundaries(resource.data(), resource.uncompressedSize());
1753 void *ptr =
nullptr;
1755#if defined(MREMAP_MAYMOVE) && defined(MREMAP_DONTUNMAP)
1761 if (!QResourcePrivate::mayRemapData(resource))
1764 ptr = mremap(r.begin, r.size, r.size, MREMAP_MAYMOVE | MREMAP_DONTUNMAP);
1765 if (ptr == MAP_FAILED)
1770 if (mprotect(ptr, r.size, PROT_READ | PROT_WRITE) != 0) {
1771 munmap(ptr, r.size);
1774#elif defined(Q_OS_DARWIN)
1775 mach_port_t self = mach_task_self();
1776 vm_address_t addr = 0;
1777 vm_address_t mask = 0;
1778 bool anywhere =
true;
1780 vm_prot_t cur_prot = VM_PROT_READ | VM_PROT_WRITE;
1781 vm_prot_t max_prot = VM_PROT_ALL;
1782 kern_return_t res = vm_remap(self, &addr, r.size, mask, anywhere,
1783 self, vm_address_t(r.begin), copy, &cur_prot,
1784 &max_prot, VM_INHERIT_DEFAULT);
1785 if (res != KERN_SUCCESS)
1788 ptr =
reinterpret_cast<
void *>(addr);
1789 if ((max_prot & VM_PROT_WRITE) == 0 || mprotect(ptr, r.size, PROT_READ | PROT_WRITE) != 0) {
1790 munmap(ptr, r.size);
1797 const char *newdata =
static_cast<
char *>(ptr) + r.offset;
1798 uncompressed = QByteArray::fromRawData(newdata, resource.uncompressedSize());
1805 auto r = mappingBoundaries(uncompressed.constBegin(), uncompressed.size());
1806 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)