Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
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/QDebug>
12#include <qendian.h>
13
14#include <private/qoffsetstringarray_p.h>
15#include <private/qtools_p.h>
16
18
19using namespace Qt::StringLiterals;
20using namespace QtMiscUtils;
21
22// in the same order as Type!
23static constexpr auto magicRuleTypes = qOffsetStringArray(
24 "invalid",
25 "string",
26 "host16",
27 "host32",
28 "big16",
29 "big32",
30 "little16",
31 "little32",
32 "byte"
33);
34
36{
37 for (int i = String; i <= Byte; ++i) {
38 if (theTypeName == magicRuleTypes.at(i))
39 return Type(i);
40 }
41 return Invalid;
42}
43
48
50{
51 return m_type == other.m_type &&
52 m_value == other.m_value &&
53 m_startPos == other.m_startPos &&
54 m_endPos == other.m_endPos &&
55 m_mask == other.m_mask &&
56 m_pattern == other.m_pattern &&
57 m_number == other.m_number &&
58 m_numberMask == other.m_numberMask &&
59 m_matchFunction == other.m_matchFunction;
60}
61
62// Used by both providers
63bool QMimeMagicRule::matchSubstring(const char *dataPtr, qsizetype dataSize, int rangeStart, int rangeLength,
64 qsizetype valueLength, const char *valueData, const char *mask)
65{
66 // Size of searched data.
67 // Example: value="ABC", rangeLength=3 -> we need 3+3-1=5 bytes (ABCxx,xABCx,xxABC would match)
68 const qsizetype dataNeeded = qMin(rangeLength + valueLength - 1, dataSize - rangeStart);
69
70 if (!mask) {
71 // callgrind says QByteArray::indexOf is much slower, since our strings are typically too
72 // short for be worth Boyer-Moore matching (1 to 71 bytes, 11 bytes on average).
73 bool found = false;
74 for (int i = rangeStart; i < rangeStart + rangeLength; ++i) {
75 if (i + valueLength > dataSize)
76 break;
77
78 if (memcmp(valueData, dataPtr + i, valueLength) == 0) {
79 found = true;
80 break;
81 }
82 }
83 if (!found)
84 return false;
85 } else {
86 bool found = false;
87 const char *readDataBase = dataPtr + rangeStart;
88 // Example (continued from above):
89 // deviceSize is 4, so dataNeeded was max'ed to 4.
90 // maxStartPos = 4 - 3 + 1 = 2, and indeed
91 // we need to check for a match a positions 0 and 1 (ABCx and xABC).
92 const qsizetype maxStartPos = dataNeeded - valueLength + 1;
93 for (int i = 0; i < maxStartPos; ++i) {
94 const char *d = readDataBase + i;
95 bool valid = true;
96 for (int idx = 0; idx < valueLength; ++idx) {
97 if (((*d++) & mask[idx]) != (valueData[idx] & mask[idx])) {
98 valid = false;
99 break;
100 }
101 }
102 if (valid)
103 found = true;
104 }
105 if (!found)
106 return false;
107 }
108 //qDebug() << "Found" << value << "in" << searchedData;
109 return true;
110}
111
112bool QMimeMagicRule::matchString(const QByteArray &data) const
113{
114 const int rangeLength = m_endPos - m_startPos + 1;
115 return QMimeMagicRule::matchSubstring(data.constData(), data.size(), m_startPos, rangeLength, m_pattern.size(), m_pattern.constData(), m_mask.constData());
116}
117
118template <typename T>
119bool QMimeMagicRule::matchNumber(const QByteArray &data) const
120{
121 const T value(m_number);
122 const T mask(m_numberMask);
123
124 //qDebug() << "matchNumber" << "0x" << QString::number(m_number, 16) << "size" << sizeof(T);
125 //qDebug() << "mask" << QString::number(m_numberMask, 16);
126
127 const char *p = data.constData() + m_startPos;
128 const char *e = data.constData() + qMin(data.size() - int(sizeof(T)), m_endPos);
129 for ( ; p <= e; ++p) {
130 if ((qFromUnaligned<T>(p) & mask) == (value & mask))
131 return true;
132 }
133
134 return false;
135}
136
138{
140 char *data = pattern.data();
141
142 const char *p = value.constData();
143 const char *e = p + value.size();
144 for ( ; p < e; ++p) {
145 if (*p == '\\' && ++p < e) {
146 if (*p == 'x') { // hex (\\xff)
147 char c = 0;
148 for (int i = 0; i < 2 && p + 1 < e; ++i) {
149 ++p;
150 if (const int h = fromHex(*p); h != -1)
151 c = (c << 4) + h;
152 else
153 continue;
154 }
155 *data++ = c;
156 } else if (isOctalDigit(*p)) { // oct (\\7, or \\77, or \\377)
157 char c = *p - '0';
158 if (p + 1 < e && isOctalDigit(p[1])) {
159 c = (c << 3) + *(++p) - '0';
160 if (p + 1 < e && isOctalDigit(p[1]) && p[-1] <= '3')
161 c = (c << 3) + *(++p) - '0';
162 }
163 *data++ = c;
164 } else if (*p == 'n') {
165 *data++ = '\n';
166 } else if (*p == 'r') {
167 *data++ = '\r';
168 } else if (*p == 't') {
169 *data++ = '\t';
170 } else { // escaped
171 *data++ = *p;
172 }
173 } else {
174 *data++ = *p;
175 }
176 }
177 pattern.truncate(data - pattern.data());
178
179 return pattern;
180}
181
182// Evaluate a magic match rule like
183// <match value="must be converted with BinHex" type="string" offset="11"/>
184// <match value="0x9501" type="big16" offset="0:64"/>
185
187 const QByteArray &value,
188 const QString &offsets,
189 const QByteArray &mask,
190 QString *errorString)
191 : m_type(QMimeMagicRule::type(type.toLatin1())),
192 m_value(value),
193 m_mask(mask),
194 m_matchFunction(nullptr)
195{
196 if (Q_UNLIKELY(m_type == Invalid)) {
197 if (errorString)
198 *errorString = "Type "_L1 + type + " is not supported"_L1;
199 return;
200 }
201
202 // Parse for offset as "1" or "1:10"
203 const qsizetype colonIndex = offsets.indexOf(u':');
204 const QStringView startPosStr = QStringView{offsets}.mid(0, colonIndex); // \ These decay to returning 'offsets'
205 const QStringView endPosStr = QStringView{offsets}.mid(colonIndex + 1);// / unchanged when colonIndex == -1
206 if (Q_UNLIKELY(!QMimeTypeParserBase::parseNumber(startPosStr, &m_startPos, errorString)) ||
207 Q_UNLIKELY(!QMimeTypeParserBase::parseNumber(endPosStr, &m_endPos, errorString))) {
208 m_type = Invalid;
209 return;
210 }
211
212 if (Q_UNLIKELY(m_value.isEmpty())) {
213 m_type = Invalid;
214 if (errorString)
215 *errorString = QStringLiteral("Invalid empty magic rule value");
216 return;
217 }
218
219 if (m_type >= Host16 && m_type <= Byte) {
220 bool ok;
221 m_number = m_value.toUInt(&ok, 0); // autodetect base
222 if (Q_UNLIKELY(!ok)) {
223 m_type = Invalid;
224 if (errorString)
225 *errorString = "Invalid magic rule value \""_L1 + QLatin1StringView(m_value) + u'"';
226 return;
227 }
228 m_numberMask = !m_mask.isEmpty() ? m_mask.toUInt(&ok, 0) : 0; // autodetect base
229 }
230
231 switch (m_type) {
232 case String:
233 m_pattern = makePattern(m_value);
234 m_pattern.squeeze();
235 if (!m_mask.isEmpty()) {
236 if (Q_UNLIKELY(m_mask.size() < 4 || !m_mask.startsWith("0x"))) {
237 m_type = Invalid;
238 if (errorString)
239 *errorString = "Invalid magic rule mask \""_L1 + QLatin1StringView(m_mask) + u'"';
240 return;
241 }
243 m_mask.constData() + 2, m_mask.size() - 2));
244 if (Q_UNLIKELY(tempMask.size() != m_pattern.size())) {
245 m_type = Invalid;
246 if (errorString)
247 *errorString = "Invalid magic rule mask size \""_L1 + QLatin1StringView(m_mask) + u'"';
248 return;
249 }
250 m_mask = tempMask;
251 } else {
252 m_mask.fill(char(-1), m_pattern.size());
253 }
254 m_mask.squeeze();
255 m_matchFunction = &QMimeMagicRule::matchString;
256 break;
257 case Byte:
258 if (m_number <= quint8(-1)) {
259 if (m_numberMask == 0)
260 m_numberMask = quint8(-1);
261 m_matchFunction = &QMimeMagicRule::matchNumber<quint8>;
262 }
263 break;
264 case Big16:
265 case Little16:
266 if (m_number <= quint16(-1)) {
267 m_number = m_type == Little16 ? qFromLittleEndian<quint16>(m_number) : qFromBigEndian<quint16>(m_number);
268 if (m_numberMask != 0)
269 m_numberMask = m_type == Little16 ? qFromLittleEndian<quint16>(m_numberMask) : qFromBigEndian<quint16>(m_numberMask);
270 }
272 case Host16:
273 if (m_number <= quint16(-1)) {
274 if (m_numberMask == 0)
275 m_numberMask = quint16(-1);
276 m_matchFunction = &QMimeMagicRule::matchNumber<quint16>;
277 }
278 break;
279 case Big32:
280 case Little32:
281 m_number = m_type == Little32 ? qFromLittleEndian<quint32>(m_number) : qFromBigEndian<quint32>(m_number);
282 if (m_numberMask != 0)
283 m_numberMask = m_type == Little32 ? qFromLittleEndian<quint32>(m_numberMask) : qFromBigEndian<quint32>(m_numberMask);
285 case Host32:
286 if (m_numberMask == 0)
287 m_numberMask = quint32(-1);
288 m_matchFunction = &QMimeMagicRule::matchNumber<quint32>;
289 break;
290 default:
291 break;
292 }
293}
294
296{
297 QByteArray result = m_mask;
298 if (m_type == String) {
299 // restore '0x'
300 result = "0x" + result.toHex();
301 }
302 return result;
303}
304
306{
307 const bool ok = m_matchFunction && (this->*m_matchFunction)(data);
308 if (!ok)
309 return false;
310
311 // No submatch? Then we are done.
312 if (m_subMatches.isEmpty())
313 return true;
314
315 //qDebug() << "Checking" << m_subMatches.count() << "sub-rules";
316 // Check that one of the submatches matches too
318 it != end ; ++it ) {
319 if ((*it).matches(data)) {
320 // One of the hierarchies matched -> mimetype recognized.
321 return true;
322 }
323 }
324 return false;
325
326
327}
328
\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:494
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:124
bool startsWith(QByteArrayView bv) const
Definition qbytearray.h:223
bool isEmpty() const noexcept
Returns true if the byte array has size 0; otherwise returns false.
Definition qbytearray.h:107
void squeeze()
Releases any memory not required to store the array's data.
Definition qbytearray.h:642
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:409
bool isEmpty() const noexcept
Definition qlist.h:401
iterator end()
Definition qlist.h:626
iterator begin()
Definition qlist.h:625
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:78
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:129
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
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLenum type
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:50
unsigned short quint16
Definition qtypes.h:48
ptrdiff_t qsizetype
Definition qtypes.h:165
unsigned char quint8
Definition qtypes.h:46
QObject::connect nullptr
QSharedPointer< T > other(t)
[5]