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