Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qxmlstream_p.h
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// Qt-Security score:critical reason:data-parser
4
5//
6// W A R N I N G
7// -------------
8//
9// This file is not part of the Qt API. It exists for the convenience
10// of other Qt classes. This header file may change from version to
11// version without notice, or even be removed.
12//
13// We mean it.
14//
15
16#include <QtCore/private/qglobal_p.h>
17#include <qstringconverter.h>
18#include <qxmlstream.h>
20#include <QtCore/qhash.h>
21#include <QCoreApplication> // Q_DECLARE_TR_FUNCTIONS
22
23
24#include <memory>
25#include <optional>
26
27#ifndef QXMLSTREAM_P_H
28#define QXMLSTREAM_P_H
29
30QT_BEGIN_NAMESPACE
31
32namespace QtPrivate {
33
35{
36public:
37 const QString *m_string = nullptr;
40
41 constexpr XmlStringRef() = default;
42 constexpr inline XmlStringRef(const QString *string, qsizetype pos, qsizetype length)
44 {
45 }
46 XmlStringRef(const QString *string)
47 : XmlStringRef(string, 0, string->size())
48 {
49 }
50
52 if (!m_string)
53 return QXmlString();
55 d.setBegin(d.data() + m_pos);
56 d.size = m_size;
57 return QXmlString(std::move(d));
58 }
59
60 void clear() { m_string = nullptr; m_pos = 0; m_size= 0; }
62 bool isEmpty() const { return m_size == 0; }
63 bool isNull() const { return !m_string; }
64 QString toString() const { return view().toString(); }
65
67 {
68 verify(pos, 0);
70 }
72 {
73 verify(pos, n);
74 return XmlStringRef(m_string, m_pos + pos, n);
75 }
76
88
89#define MAKE_MEMBER(name)
90 auto name () const noexcept { return view(). name (); }
91 MAKE_MEMBER(data)
92 MAKE_MEMBER(size)
93 MAKE_MEMBER(empty)
94 MAKE_MEMBER(begin)
95 MAKE_MEMBER(end)
96 MAKE_MEMBER(cbegin)
97 MAKE_MEMBER(cend)
98 MAKE_MEMBER(rbegin)
99 MAKE_MEMBER(rend)
100 MAKE_MEMBER(crbegin)
101 MAKE_MEMBER(crend)
102#undef MAKE_MEMBER
103
104#define MAKE_OP(op)
105 friend auto operator op(const XmlStringRef &lhs, const XmlStringRef &rhs) noexcept { return lhs.view() op rhs.view(); }
106 /*end*/
107 MAKE_OP(==)
108 MAKE_OP(!=)
109 MAKE_OP(<=)
110 MAKE_OP(>=)
111 MAKE_OP(<)
112 MAKE_OP(>)
113#undef MAKE_OP
114#define MAKE_OP(op)
115 friend auto operator op(const XmlStringRef &lhs, QStringView rhs) noexcept { return lhs.view() op rhs; }
116 friend auto operator op(QStringView lhs, const XmlStringRef &rhs) noexcept { return lhs op rhs.view(); }
117 /*end*/
118 MAKE_OP(==)
119 MAKE_OP(!=)
120 MAKE_OP(<=)
121 MAKE_OP(>=)
122 MAKE_OP(<)
123 MAKE_OP(>)
124#undef MAKE_OP
125
126private:
127 Q_ALWAYS_INLINE
128 void verify([[maybe_unused]] qsizetype pos = 0,
129 [[maybe_unused]] qsizetype n = 1) const
130 {
131 Q_PRE(pos >= 0);
132 Q_PRE(pos <= size());
133 Q_PRE(n >= 0);
134 Q_PRE(n <= size() - pos);
135 }
136};
137
138}
139
140using namespace QtPrivate;
141
142template <typename T> class QXmlStreamSimpleStack
143{
145
146 T *data;
147 qsizetype tos, cap;
148public:
150 : data(nullptr), tos(-1), cap(0)
151 {}
153 {
154 if (data) {
155 std::destroy_n(data, size());
156 free(data);
157 }
158 }
159
160 inline void reserve(qsizetype extraCapacity)
161 {
162 if (tos + extraCapacity + 1 > cap) {
163 cap = qMax(tos + extraCapacity + 1, cap << 1 );
164 void *ptr = realloc(static_cast<void *>(data), cap * sizeof(T));
165 Q_CHECK_PTR(ptr);
166 data = reinterpret_cast<T *>(ptr);
167 }
168 }
169
170 inline T &push() { reserve(1); return rawPush(); }
171 inline T &rawPush() { return *new (data + (++tos)) T; }
172 inline const T &top() const { return data[tos]; }
173 inline T &top() { return data[tos]; }
174 inline T pop() { T t = std::move(data[tos]); std::destroy_at(data + tos); --tos; return t; }
175 inline T &operator[](qsizetype index) { return data[index]; }
176 inline const T &at(qsizetype index) const { return data[index]; }
177 inline qsizetype size() const { return tos + 1; }
178 inline void resize(qsizetype s) { tos = s - 1; }
179 inline bool isEmpty() const { return tos < 0; }
180 inline void clear() { tos = -1; }
181
182 using const_iterator = const T*;
183 using iterator = T*;
184 T *begin() { return data; }
185 const T *begin() const { return data; }
186 const T *cbegin() const { return begin(); }
187 T *end() { return data + size(); }
188 const T *end() const { return data + size(); }
189 const T *cend() const { return end(); }
190};
191
196
199public:
205
214
215
222
224 {
225 qsizetype pos = tagStackStringStorageSize;
226 if (pos != tagStackStringStorage.size())
227 tagStackStringStorage.resize(pos);
228 s.visit([&](auto s) { tagStackStringStorage.append(s); });
229 qsizetype sz = (tagStackStringStorage.size() - pos);
230 tagStackStringStorageSize += sz;
231 return XmlStringRef(&tagStackStringStorage, pos, sz);
232 }
233
235
236
237 inline Tag tagStack_pop() {
238 Tag tag = tagStack.pop();
239 tagStackStringStorageSize = tag.tagStackStringStorageSize;
240 namespaceDeclarations.resize(tag.namespaceDeclarationsSize);
241 tagsDone = tagStack.isEmpty();
242 return tag;
243 }
244 inline Tag &tagStack_push() {
245 Tag &tag = tagStack.push();
246 tag.tagStackStringStorageSize = tagStackStringStorageSize;
247 tag.namespaceDeclarationsSize = namespaceDeclarations.size();
248 return tag;
249 }
250};
251
252#if QT_CONFIG(xmlstreamreader)
253class QXmlStreamEntityResolver;
254class QXmlStreamReaderPrivate : public QXmlStreamGrammar, public QXmlStreamPrivateTagStack
255{
256 QXmlStreamReader *q_ptr;
257 Q_DECLARE_PUBLIC(QXmlStreamReader)
258public:
259 QXmlStreamReaderPrivate(QXmlStreamReader *q);
260 ~QXmlStreamReaderPrivate();
261 void init();
262
263 struct BufferAndEncoding
264 {
265 QByteArray buffer;
266 // System means that we didn't specify the encoding
267 QStringDecoder::Encoding encoding;
268
269 BufferAndEncoding()
270 : buffer(), encoding(QStringDecoder::System)
271 {}
272 BufferAndEncoding(const QByteArray &b, QStringDecoder::Encoding e)
273 : buffer(b), encoding(e)
274 {}
275 };
276 QList<BufferAndEncoding> dataInfo;
277 QStringDecoder chunkDecoder;
278 void appendDataWithEncoding(const QByteArray &data, QStringDecoder::Encoding enc);
279 void addData(const QByteArray &data, QStringDecoder::Encoding enc);
280
281 QByteArray rawReadBuffer;
282 qint64 nbytesread;
283 QString readBuffer;
284 qsizetype readBufferPos;
285 QXmlStreamSimpleStack<uint> putStack;
286 struct Entity {
287 Entity() = default;
288 Entity(const QString &name, const QString &value)
289 : name(name), value(value), external(false), unparsed(false), literal(false),
290 hasBeenParsed(false), isCurrentlyReferenced(false){}
291 static inline Entity createLiteral(QLatin1StringView name, QLatin1StringView value)
292 { Entity result(name, value); result.literal = result.hasBeenParsed = true; return result; }
293 QString name, value;
294 uint external : 1;
295 uint unparsed : 1;
296 uint literal : 1;
297 uint hasBeenParsed : 1;
298 uint isCurrentlyReferenced : 1;
299 };
300 // these hash tables use a QStringView as a key to avoid creating QStrings
301 // just for lookup. The keys are usually views into Entity::name and thus
302 // are guaranteed to have the same lifetime as the referenced data:
303 QHash<QStringView, Entity> entityHash;
304 QHash<QStringView, Entity> parameterEntityHash;
305 struct QEntityReference
306 {
307 QHash<QStringView, Entity> *hash;
308 QStringView name;
309 };
310 QXmlStreamSimpleStack<QEntityReference> entityReferenceStack;
311 int entityExpansionLimit = 4096;
312 int entityLength = 0;
313 inline bool referenceEntity(QHash<QStringView, Entity> *hash, Entity &entity)
314 {
315 Q_ASSERT(hash);
316 if (entity.isCurrentlyReferenced) {
317 raiseWellFormedError(QXmlStream::tr("Self-referencing entity detected."));
318 return false;
319 }
320 // entityLength represents the amount of additional characters the
321 // entity expands into (can be negative for e.g. &amp;). It's used to
322 // avoid DoS attacks through recursive entity expansions
323 entityLength += entity.value.size() - entity.name.size() - 2;
324 if (entityLength > entityExpansionLimit) {
325 raiseWellFormedError(QXmlStream::tr("Entity expands to more characters than the entity expansion limit."));
326 return false;
327 }
328 entity.isCurrentlyReferenced = true;
329 entityReferenceStack.push() = { hash, entity.name };
330 injectToken(ENTITY_DONE);
331 return true;
332 }
333
334
335 QIODevice *device;
336 bool deleteDevice;
337 QStringDecoder decoder;
338 bool atEnd;
339
340 enum class XmlContext
341 {
342 Prolog,
343 Body,
344 };
345
346 XmlContext currentContext = XmlContext::Prolog;
347 bool foundDTD = false;
348 bool isValidToken(QXmlStreamReader::TokenType type);
349 void checkToken();
350
351 /*!
352 \sa setType()
353 */
354 QXmlStreamReader::TokenType type;
355 QXmlStreamReader::Error error;
356 QString errorString;
357 QString unresolvedEntity;
358
359 qint64 lineNumber, lastLineStart, characterOffset;
360
361
362 void write(const QString &);
363 void write(const char *);
364
365
366 QXmlStreamAttributes attributes;
367 XmlStringRef namespaceForPrefix(QStringView prefix);
368 void resolveTag();
369 void resolvePublicNamespaces();
370 void resolveDtd();
371 uint resolveCharRef(int symbolIndex);
372 bool checkStartDocument();
373 void startDocument();
374 void parseError();
375 void checkPublicLiteral(QStringView publicId);
376
377 bool scanDtd;
378 XmlStringRef lastAttributeValue;
379 bool lastAttributeIsCData;
380 struct DtdAttribute {
381 XmlStringRef tagName;
382 XmlStringRef attributeQualifiedName;
383 XmlStringRef attributePrefix;
384 XmlStringRef attributeName;
385 XmlStringRef defaultValue;
386 bool isCDATA;
387 bool isNamespaceAttribute;
388 };
389 QXmlStreamSimpleStack<DtdAttribute> dtdAttributes;
390 struct NotationDeclaration {
391 XmlStringRef name;
392 XmlStringRef publicId;
393 XmlStringRef systemId;
394 };
395 QXmlStreamSimpleStack<NotationDeclaration> notationDeclarations;
396 QXmlStreamNotationDeclarations publicNotationDeclarations;
397 QXmlStreamNamespaceDeclarations publicNamespaceDeclarations;
398
399 struct EntityDeclaration {
400 XmlStringRef name;
401 XmlStringRef notationName;
402 XmlStringRef publicId;
403 XmlStringRef systemId;
404 XmlStringRef value;
405 bool parameter;
406 bool external;
407 inline void clear() {
408 name.clear();
409 notationName.clear();
410 publicId.clear();
411 systemId.clear();
412 value.clear();
413 parameter = external = false;
414 }
415 };
416 QXmlStreamSimpleStack<EntityDeclaration> entityDeclarations;
417 QXmlStreamEntityDeclarations publicEntityDeclarations;
418
419 XmlStringRef text;
420
421 XmlStringRef prefix, namespaceUri, qualifiedName, name;
422 XmlStringRef processingInstructionTarget, processingInstructionData;
423 XmlStringRef dtdName, dtdPublicId, dtdSystemId;
424 XmlStringRef documentVersion, documentEncoding;
425 uint isEmptyElement : 1;
426 uint isWhitespace : 1;
427 uint isCDATA : 1;
428 uint standalone : 1;
429 uint hasCheckedStartDocument : 1;
430 uint normalizeLiterals : 1;
431 uint hasSeenTag : 1;
432 uint inParseEntity : 1;
433 uint referenceToUnparsedEntityDetected : 1;
434 uint referenceToParameterEntityDetected : 1;
435 uint hasExternalDtdSubset : 1;
436 uint lockEncoding : 1;
437 uint namespaceProcessing : 1;
438 uint hasStandalone : 1; // TODO: expose in public API
439
440 int resumeReduction;
441 void resume(int rule);
442
443 inline bool entitiesMustBeDeclared() const {
444 return (!inParseEntity
445 && (standalone
446 || (!referenceToUnparsedEntityDetected
447 && !referenceToParameterEntityDetected // Errata 13 as of 2006-04-25
448 && !hasExternalDtdSubset)));
449 }
450
451 // qlalr parser
452 int tos;
453 int stack_size;
454 struct Value {
455 qsizetype pos; // offset into textBuffer
456 qsizetype len; // length incl. prefix (if any)
457 qint16 prefix; // prefix of a name (as in "prefix:name") limited to 4k in fastScanName()
458 ushort c;
459 };
460
461 Value *sym_stack;
462 int *state_stack;
463 inline void reallocateStack();
464 inline Value &sym(int index) const
465 { return sym_stack[tos + index - 1]; }
466 QString textBuffer;
467 inline void clearTextBuffer() {
468 if (!scanDtd) {
469 textBuffer.resize(0);
470 textBuffer.reserve(256);
471 }
472 }
473 struct Attribute {
474 Value key;
475 Value value;
476 };
477 QXmlStreamSimpleStack<Attribute> attributeStack;
478
479 inline XmlStringRef symString(int index) {
480 return symString(sym(index));
481 }
482 QStringView symView(int index) const
483 {
484 const Value &symbol = sym(index);
485 return QStringView(textBuffer.data() + symbol.pos, symbol.len).mid(symbol.prefix);
486 }
487 inline XmlStringRef symName(int index) {
488 return symName(sym(index));
489 }
490 inline XmlStringRef symPrefix(int index) {
491 return symPrefix(sym(index));
492 }
493 auto symQName(int index) {
494 struct R {
495 XmlStringRef m_data;
496 qsizetype m_prefix;
497
498 XmlStringRef all() const { return m_data; }
499 XmlStringRef qname() const { return m_data; }
500 XmlStringRef prefix() const
501 { return m_prefix ? m_data.sliced(0, m_prefix - 1) : XmlStringRef(); }
502 XmlStringRef localPart() const { return m_data.sliced(m_prefix); }
503
504 void resetBackingStorage(XmlStringRef newData) { m_data = newData; } // m_prefix stays!
505 };
506 const Value &symbol = sym(index);
507 return R{{&textBuffer, symbol.pos, symbol.len}, symbol.prefix};
508 }
509 inline XmlStringRef symString(const Value &symbol) {
510 return XmlStringRef(&textBuffer, symbol.pos + symbol.prefix, symbol.len - symbol.prefix);
511 }
512 inline XmlStringRef symName(const Value &symbol) {
513 return XmlStringRef(&textBuffer, symbol.pos, symbol.len);
514 }
515 inline XmlStringRef symPrefix(const Value &symbol) {
516 if (symbol.prefix)
517 return XmlStringRef(&textBuffer, symbol.pos, symbol.prefix - 1);
518 return XmlStringRef();
519 }
520
521 inline void clearSym() { Value &val = sym(1); val.pos = textBuffer.size(); val.len = 0; }
522
523
524 short token;
525 uint token_char;
526
527 uint filterCarriageReturn();
528 inline uint getChar();
529 inline uint peekChar();
530 inline void putChar(uint c) { putStack.push() = c; }
531 inline void putChar(QChar c) { putStack.push() = c.unicode(); }
532 void putString(QStringView s, qsizetype from = 0);
533 void putStringLiteral(QStringView s);
534 void putReplacement(QStringView s);
535 void putReplacementInAttributeValue(QStringView s);
536 uint getChar_helper();
537
538 bool scanUntil(const char *str, short tokenToInject = -1);
539 bool scanString(const char *str, short tokenToInject, bool requireSpace = true);
540 inline void injectToken(ushort tokenToInject) {
541 putChar(int(tokenToInject) << 16);
542 }
543
544 QString resolveUndeclaredEntity(const QString &name);
545 void parseEntity(const QString &value);
546 std::unique_ptr<QXmlStreamReaderPrivate> entityParser;
547
548 bool scanAfterLangleBang();
549 bool scanPublicOrSystem();
550 bool scanNData();
551 bool scanAfterDefaultDecl();
552 bool scanAttType();
553
554
555 // scan optimization functions. Not strictly necessary but LALR is
556 // not very well suited for scanning fast
557 qsizetype fastScanLiteralContent();
558 qsizetype fastScanSpace();
559 qsizetype fastScanContentCharList();
560 std::optional<qsizetype> fastScanName(Value *val = nullptr);
561 inline qsizetype fastScanNMTOKEN();
562
563
564 bool parse();
565 inline void consumeRule(int);
566
567 void raiseError(QXmlStreamReader::Error error, const QString& message = QString());
568 void raiseWellFormedError(const QString &message);
569 void raiseNamePrefixTooLongError();
570
571 QXmlStreamEntityResolver *entityResolver;
572
573private:
574 /*! \internal
575 Never assign to variable type directly. Instead use this function.
576
577 This prevents errors from being ignored.
578 */
579 inline void setType(const QXmlStreamReader::TokenType t)
580 {
581 if (type != QXmlStreamReader::Invalid)
582 type = t;
583 }
584};
585#endif // feature xmlstreamreader
586
587QT_END_NAMESPACE
588
589#endif // QXMLSTREAM_P_H
qsizetype initialTagStackStringStorageSize
XmlStringRef addToStringStorage(QAnyStringView s)
QXmlStreamSimpleStack< NamespaceDeclaration > namespaceDeclarations
QXmlStreamSimpleStack< Tag > tagStack
void resize(qsizetype s)
const T * cbegin() const
const T & top() const
void reserve(qsizetype extraCapacity)
const T * cend() const
const T * begin() const
const T & at(qsizetype index) const
T & operator[](qsizetype index)
qsizetype size() const
const T * end() const
XmlStringRef(const QString *string)
constexpr XmlStringRef()=default
const QString * m_string
constexpr XmlStringRef(const QString *string, qsizetype pos, qsizetype length)
#define MAKE_MEMBER(name)
#define MAKE_OP(op)
NamespaceDeclaration namespaceDeclaration