Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qmimemagicrule.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4
5#define QT_NO_CAST_FROM_ASCII
6
7#include "qmimemagicrule_p.h"
8
9#include "qmimetypeparser_p.h"
10#include <QtCore/QList>
11#include <QtCore/QMap>
12#include <QtCore/QDebug>
13#include <qendian.h>
14
15#include <private/qoffsetstringarray_p.h>
16#include <private/qtools_p.h>
17
19
20using namespace Qt::StringLiterals;
21using namespace QtMiscUtils;
22
23// in the same order as Type!
24static constexpr auto magicRuleTypes = qOffsetStringArray(
25 "invalid",
26 "string",
27 "host16",
28 "host32",
29 "big16",
30 "big32",
31 "little16",
32 "little32",
33 "byte"
34);
35
37{
38 for (int i = String; i <= Byte; ++i) {
39 if (theTypeName == magicRuleTypes.at(i))
40 return Type(i);
41 }
42 return Invalid;
43}
44
46{
47 return magicRuleTypes.at(theType);
48}
49
51{
52 return m_type == other.m_type &&
53 m_value == other.m_value &&
54 m_startPos == other.m_startPos &&
55 m_endPos == other.m_endPos &&
56 m_mask == other.m_mask &&
57 m_pattern == other.m_pattern &&
58 m_number == other.m_number &&
59 m_numberMask == other.m_numberMask &&
60 m_matchFunction == other.m_matchFunction;
61}
62
63// Used by both providers
64bool QMimeMagicRule::matchSubstring(const char *dataPtr, qsizetype dataSize, int rangeStart, int rangeLength,
65 qsizetype valueLength, const char *valueData, const char *mask)
66{
67 // Size of searched data.
68 // Example: value="ABC", rangeLength=3 -> we need 3+3-1=5 bytes (ABCxx,xABCx,xxABC would match)
69 const qsizetype dataNeeded = qMin(rangeLength + valueLength - 1, dataSize - rangeStart);
70
71 if (!mask) {
72 // callgrind says QByteArray::indexOf is much slower, since our strings are typically too
73 // short for be worth Boyer-Moore matching (1 to 71 bytes, 11 bytes on average).
74 bool found = false;
75 for (int i = rangeStart; i < rangeStart + rangeLength; ++i) {
76 if (i + valueLength > dataSize)
77 break;
78
79 if (memcmp(valueData, dataPtr + i, valueLength) == 0) {
80 found = true;
81 break;
82 }
83 }
84 if (!found)
85 return false;
86 } else {
87 bool found = false;
88 const char *readDataBase = dataPtr + rangeStart;
89 // Example (continued from above):
90 // deviceSize is 4, so dataNeeded was max'ed to 4.
91 // maxStartPos = 4 - 3 + 1 = 2, and indeed
92 // we need to check for a match a positions 0 and 1 (ABCx and xABC).
93 const qsizetype maxStartPos = dataNeeded - valueLength + 1;
94 for (int i = 0; i < maxStartPos; ++i) {
95 const char *d = readDataBase + i;
96 bool valid = true;
97 for (int idx = 0; idx < valueLength; ++idx) {
98 if (((*d++) & mask[idx]) != (valueData[idx] & mask[idx])) {
99 valid = false;
100 break;
101 }
102 }
103 if (valid)
104 found = true;
105 }
106 if (!found)
107 return false;
108 }
109 //qDebug() << "Found" << value << "in" << searchedData;
110 return true;
111}
112
113bool QMimeMagicRule::matchString(const QByteArray &data) const
114{
115 const int rangeLength = m_endPos - m_startPos + 1;
116 return QMimeMagicRule::matchSubstring(data.constData(), data.size(), m_startPos, rangeLength, m_pattern.size(), m_pattern.constData(), m_mask.constData());
117}
118
119template <typename T>
120bool QMimeMagicRule::matchNumber(const QByteArray &data) const
121{
122 const T value(m_number);
123 const T mask(m_numberMask);
124
125 //qDebug() << "matchNumber" << "0x" << QString::number(m_number, 16) << "size" << sizeof(T);
126 //qDebug() << "mask" << QString::number(m_numberMask, 16);
127
128 const char *p = data.constData() + m_startPos;
129 const char *e = data.constData() + qMin(data.size() - int(sizeof(T)), m_endPos);
130 for ( ; p <= e; ++p) {
131 if ((qFromUnaligned<T>(p) & mask) == (value & mask))
132 return true;
133 }
134
135 return false;
136}
137
139{
141 char *data = pattern.data();
142
143 const char *p = value.constData();
144 const char *e = p + value.size();
145 for ( ; p < e; ++p) {
146 if (*p == '\\' && ++p < e) {
147 if (*p == 'x') { // hex (\\xff)
148 char c = 0;
149 for (int i = 0; i < 2 && p + 1 < e; ++i) {
150 ++p;
151 if (const int h = fromHex(*p); h != -1)
152 c = (c << 4) + h;
153 else
154 continue;
155 }
156 *data++ = c;
157 } else if (isOctalDigit(*p)) { // oct (\\7, or \\77, or \\377)
158 char c = *p - '0';
159 if (p + 1 < e && isOctalDigit(p[1])) {
160 c = (c << 3) + *(++p) - '0';
161 if (p + 1 < e && isOctalDigit(p[1]) && p[-1] <= '3')
162 c = (c << 3) + *(++p) - '0';
163 }
164 *data++ = c;
165 } else if (*p == 'n') {
166 *data++ = '\n';
167 } else if (*p == 'r') {
168 *data++ = '\r';
169 } else if (*p == 't') {
170 *data++ = '\t';
171 } else { // escaped
172 *data++ = *p;
173 }
174 } else {
175 *data++ = *p;
176 }
177 }
178 pattern.truncate(data - pattern.data());
179
180 return pattern;
181}
182
183// Evaluate a magic match rule like
184// <match value="must be converted with BinHex" type="string" offset="11"/>
185// <match value="0x9501" type="big16" offset="0:64"/>
186
188 const QByteArray &value,
189 const QString &offsets,
190 const QByteArray &mask,
191 QString *errorString)
192 : m_type(QMimeMagicRule::type(type.toLatin1())),
193 m_value(value),
194 m_mask(mask),
195 m_matchFunction(nullptr)
196{
197 if (Q_UNLIKELY(m_type == Invalid)) {
198 if (errorString)
199 *errorString = "Type "_L1 + type + " is not supported"_L1;
200 return;
201 }
202
203 // Parse for offset as "1" or "1:10"
204 const qsizetype colonIndex = offsets.indexOf(u':');
205 const QStringView startPosStr = QStringView{offsets}.mid(0, colonIndex); // \ These decay to returning 'offsets'
206 const QStringView endPosStr = QStringView{offsets}.mid(colonIndex + 1);// / unchanged when colonIndex == -1
207 if (Q_UNLIKELY(!QMimeTypeParserBase::parseNumber(startPosStr, &m_startPos, errorString)) ||
208 Q_UNLIKELY(!QMimeTypeParserBase::parseNumber(endPosStr, &m_endPos, errorString))) {
209 m_type = Invalid;
210 return;
211 }
212
213 if (Q_UNLIKELY(m_value.isEmpty())) {
214 m_type = Invalid;
215 if (errorString)
216 *errorString = QStringLiteral("Invalid empty magic rule value");
217 return;
218 }
219
220 if (m_type >= Host16 && m_type <= Byte) {
221 bool ok;
222 m_number = m_value.toUInt(&ok, 0); // autodetect base
223 if (Q_UNLIKELY(!ok)) {
224 m_type = Invalid;
225 if (errorString)
226 *errorString = "Invalid magic rule value \""_L1 + QLatin1StringView(m_value) + u'"';
227 return;
228 }
229 m_numberMask = !m_mask.isEmpty() ? m_mask.toUInt(&ok, 0) : 0; // autodetect base
230 }
231
232 switch (m_type) {
233 case String:
234 m_pattern = makePattern(m_value);
235 m_pattern.squeeze();
236 if (!m_mask.isEmpty()) {
237 if (Q_UNLIKELY(m_mask.size() < 4 || !m_mask.startsWith("0x"))) {
238 m_type = Invalid;
239 if (errorString)
240 *errorString = "Invalid magic rule mask \""_L1 + QLatin1StringView(m_mask) + u'"';
241 return;
242 }
244 m_mask.constData() + 2, m_mask.size() - 2));
245 if (Q_UNLIKELY(tempMask.size() != m_pattern.size())) {
246 m_type = Invalid;
247 if (errorString)
248 *errorString = "Invalid magic rule mask size \""_L1 + QLatin1StringView(m_mask) + u'"';
249 return;
250 }
251 m_mask = tempMask;
252 } else {
253 m_mask.fill(char(-1), m_pattern.size());
254 }
255 m_mask.squeeze();
256 m_matchFunction = &QMimeMagicRule::matchString;
257 break;
258 case Byte:
259 if (m_number <= quint8(-1)) {
260 if (m_numberMask == 0)
261 m_numberMask = quint8(-1);
262 m_matchFunction = &QMimeMagicRule::matchNumber<quint8>;
263 }
264 break;
265 case Big16:
266 case Little16:
267 if (m_number <= quint16(-1)) {
268 m_number = m_type == Little16 ? qFromLittleEndian<quint16>(m_number) : qFromBigEndian<quint16>(m_number);
269 if (m_numberMask != 0)
270 m_numberMask = m_type == Little16 ? qFromLittleEndian<quint16>(m_numberMask) : qFromBigEndian<quint16>(m_numberMask);
271 }
273 case Host16:
274 if (m_number <= quint16(-1)) {
275 if (m_numberMask == 0)
276 m_numberMask = quint16(-1);
277 m_matchFunction = &QMimeMagicRule::matchNumber<quint16>;
278 }
279 break;
280 case Big32:
281 case Little32:
282 m_number = m_type == Little32 ? qFromLittleEndian<quint32>(m_number) : qFromBigEndian<quint32>(m_number);
283 if (m_numberMask != 0)
284 m_numberMask = m_type == Little32 ? qFromLittleEndian<quint32>(m_numberMask) : qFromBigEndian<quint32>(m_numberMask);
286 case Host32:
287 if (m_numberMask == 0)
288 m_numberMask = quint32(-1);
289 m_matchFunction = &QMimeMagicRule::matchNumber<quint32>;
290 break;
291 default:
292 break;
293 }
294}
295
297{
298 QByteArray result = m_mask;
299 if (m_type == String) {
300 // restore '0x'
301 result = "0x" + result.toHex();
302 }
303 return result;
304}
305
307{
308 const bool ok = m_matchFunction && (this->*m_matchFunction)(data);
309 if (!ok)
310 return false;
311
312 // No submatch? Then we are done.
313 if (m_subMatches.isEmpty())
314 return true;
315
316 //qDebug() << "Checking" << m_subMatches.count() << "sub-rules";
317 // Check that one of the submatches matches too
319 it != end ; ++it ) {
320 if ((*it).matches(data)) {
321 // One of the hierarchies matched -> mimetype recognized.
322 return true;
323 }
324 }
325 return false;
326
327
328}
329
\inmodule QtCore
Definition qbytearray.h:57
uint toUInt(bool *ok=nullptr, int base=10) const
Returns the byte array converted to an {unsigned int} using base base, which is ten by default.
QByteArray & fill(char c, qsizetype size=-1)
Sets every byte in the byte array to ch.
qsizetype size() const noexcept
Returns the number of bytes in this byte array.
Definition qbytearray.h:474
static QByteArray fromHex(const QByteArray &hexEncoded)
Returns a decoded copy of the hex encoded array hexEncoded.
const char * constData() const noexcept
Returns a pointer to the const data stored in the byte array.
Definition qbytearray.h:122
bool startsWith(QByteArrayView bv) const
Definition qbytearray.h:170
bool isEmpty() const noexcept
Returns true if the byte array has size 0; otherwise returns false.
Definition qbytearray.h:106
void squeeze()
Releases any memory not required to store the array's data.
Definition qbytearray.h:565
QByteArray toHex(char separator='\0') const
Returns a hex encoded copy of the byte array.
static QByteArray fromRawData(const char *data, qsizetype size)
Constructs a QByteArray that uses the first size bytes of the data array.
Definition qbytearray.h:394
bool isEmpty() const noexcept
Definition qlist.h:390
iterator end()
Definition qlist.h:609
iterator begin()
Definition qlist.h:608
static QByteArray typeName(Type type)
QByteArray mask() const
QList< QMimeMagicRule > m_subMatches
static bool matchSubstring(const char *dataPtr, qsizetype dataSize, int rangeStart, int rangeLength, qsizetype valueLength, const char *valueData, const char *mask)
bool matches(const QByteArray &data) const
Type type() const
bool operator==(const QMimeMagicRule &other) const
QByteArray value() const
QMimeMagicRule(const QString &typeStr, const QByteArray &value, const QString &offsets, const QByteArray &mask, QString *errorString)
static bool parseNumber(QStringView n, int *target, QString *errorMessage)
\inmodule QtCore
Definition qstringview.h:76
constexpr QStringView mid(qsizetype pos, qsizetype n=-1) const noexcept
Returns the substring of length length starting at position start in this object.
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
static QString static QString qsizetype indexOf(QChar c, qsizetype from=0, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Definition qstring.cpp:4420
double e
QSet< QString >::iterator it
Combined button and popup list for selecting options.
constexpr bool isOctalDigit(char32_t c) noexcept
Definition qtools_p.h:57
constexpr int fromHex(char32_t c) noexcept
Definition qtools_p.h:44
constexpr Initialization Uninitialized
#define Q_FALLTHROUGH()
#define Q_UNLIKELY(x)
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
static QByteArray makePattern(const QByteArray &value)
static constexpr auto magicRuleTypes
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
constexpr auto qOffsetStringArray(const char(&...strings)[Nx]) noexcept
GLuint GLuint end
GLenum GLsizei dataSize
GLenum type
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLint GLint GLint GLint GLint GLint GLint GLbitfield mask
GLfloat GLfloat GLfloat GLfloat h
const GLubyte * c
GLuint GLsizei const GLuint const GLintptr * offsets
GLuint64EXT * result
[6]
GLfloat GLfloat p
[1]
GLubyte * pattern
#define QStringLiteral(str)
unsigned int quint32
Definition qtypes.h:45
unsigned short quint16
Definition qtypes.h:43
ptrdiff_t qsizetype
Definition qtypes.h:70
unsigned char quint8
Definition qtypes.h:41
QObject::connect nullptr
QSharedPointer< T > other(t)
[5]