From 2a3f226e325f3ff8456273444a281349cbbd680f Mon Sep 17 00:00:00 2001 From: Yulong Bai Date: Wed, 27 Dec 2017 15:40:16 +0100 Subject: QMenuBar::mouseReleaseEvent: fix triggering hidden actions While menubar actions are hidden, as menubar was too narrow to contain them, by clicking the blank area, where the action was supposed to place, can still trigger the action. Task-number: QTBUG-65488 Change-Id: I6b137e0717f634ebd3371dbcc2c1ce2089688374 Reviewed-by: Richard Moe Gustavsen --- src/widgets/widgets/qmenubar.cpp | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src') diff --git a/src/widgets/widgets/qmenubar.cpp b/src/widgets/widgets/qmenubar.cpp index 0c11afdc61..b0a75288e8 100644 --- a/src/widgets/widgets/qmenubar.cpp +++ b/src/widgets/widgets/qmenubar.cpp @@ -1080,6 +1080,10 @@ void QMenuBar::mouseReleaseEvent(QMouseEvent *e) d->mouseDown = false; QAction *action = d->actionAt(e->pos()); + + // do noting if the action is hidden + if (!d->isVisible(action)) + return; if((d->closePopupMode && action == d->currentAction) || !action || !action->menu()) { //we set the current action before activating //so that we let the leave event set the current back to 0 -- cgit v1.2.3 From 8e3c6f165b613c92131f2929a2c2c83054ecce72 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Fri, 19 Jan 2018 14:56:32 +0100 Subject: Improve debug formatting of QInputMethodQueryEvent Add more queries, output the query enumeration value and output the hints as flags. Change-Id: Icfc648a8d6e144074455ecebae1b25c3c3e1063e Reviewed-by: Shawn Rutledge --- src/gui/kernel/qevent.cpp | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/gui/kernel/qevent.cpp b/src/gui/kernel/qevent.cpp index be8ce1a011..50d9bbb2cc 100644 --- a/src/gui/kernel/qevent.cpp +++ b/src/gui/kernel/qevent.cpp @@ -3761,14 +3761,25 @@ static inline void formatInputMethodEvent(QDebug d, const QInputMethodEvent *e) static inline void formatInputMethodQueryEvent(QDebug d, const QInputMethodQueryEvent *e) { + QDebugStateSaver saver(d); + d.noquote(); const Qt::InputMethodQueries queries = e->queries(); d << "QInputMethodQueryEvent(queries=" << showbase << hex << int(queries) << noshowbase << dec << ", {"; - for (unsigned mask = 1; mask <= Qt::ImTextAfterCursor; mask<<=1) { + for (unsigned mask = 1; mask <= Qt::ImInputItemClipRectangle; mask<<=1) { if (queries & mask) { - const QVariant value = e->value(static_cast(mask)); - if (value.isValid()) - d << '[' << showbase << hex << mask << noshowbase << dec << '=' << value << "],"; + const Qt::InputMethodQuery query = static_cast(mask); + const QVariant value = e->value(query); + if (value.isValid()) { + d << '['; + QtDebugUtils::formatQEnum(d, query); + d << '='; + if (query == Qt::ImHints) + QtDebugUtils::formatQFlags(d, Qt::InputMethodHints(value.toInt())); + else + d << value.toString(); + d << "],"; + } } } d << "})"; -- cgit v1.2.3 From cb142954c54b7a6e391950d9209b5cea9252092b Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Thu, 14 Dec 2017 14:15:34 +0100 Subject: XCB: Implement native window dump for diaglib Extract a helper function to determine the window title from QXcbConnection and add an invokable function to the native interface that dumps the window tree similar to existing functionality on Windows. Change-Id: I5544d69ea2b801eb16d3b5b8d64021b3e567b0d8 Reviewed-by: Shawn Rutledge --- src/plugins/platforms/xcb/qxcbnativeinterface.cpp | 56 +++++++++++++++++++++++ src/plugins/platforms/xcb/qxcbnativeinterface.h | 2 + src/plugins/platforms/xcb/qxcbscreen.cpp | 12 +---- src/plugins/platforms/xcb/qxcbwindow.cpp | 13 ++++++ src/plugins/platforms/xcb/qxcbwindow.h | 2 + 5 files changed, 75 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/plugins/platforms/xcb/qxcbnativeinterface.cpp b/src/plugins/platforms/xcb/qxcbnativeinterface.cpp index d989761297..22d90d6ac2 100644 --- a/src/plugins/platforms/xcb/qxcbnativeinterface.cpp +++ b/src/plugins/platforms/xcb/qxcbnativeinterface.cpp @@ -673,4 +673,60 @@ void *QXcbNativeInterface::handlerNativeResourceForBackingStore(const QByteArray return nullptr; } +static void dumpNativeWindowsRecursion(const QXcbConnection *connection, xcb_window_t window, + int level, QTextStream &str) +{ + if (level) + str << QByteArray(2 * level, ' '); + + xcb_connection_t *conn = connection->xcb_connection(); + auto geomReply = Q_XCB_REPLY(xcb_get_geometry, conn, window); + if (!geomReply) + return; + const QRect geom(geomReply->x, geomReply->y, geomReply->width, geomReply->height); + if (!geom.isValid() || (geom.width() <= 3 && geom.height() <= 3)) + return; // Skip helper/dummy windows. + str << "0x"; + const int oldFieldWidth = str.fieldWidth(); + const QChar oldPadChar =str.padChar(); + str.setFieldWidth(8); + str.setPadChar(QLatin1Char('0')); + str << hex << window; + str.setFieldWidth(oldFieldWidth); + str.setPadChar(oldPadChar); + str << dec << " \"" + << QXcbWindow::windowTitle(connection, window) << "\" " + << geom.width() << 'x' << geom.height() << forcesign << geom.x() << geom.y() + << noforcesign << '\n'; + + auto reply = Q_XCB_REPLY(xcb_query_tree, conn, window); + if (reply) { + const int count = xcb_query_tree_children_length(reply.get()); + const xcb_window_t *children = xcb_query_tree_children(reply.get()); + for (int i = 0; i < count; ++i) + dumpNativeWindowsRecursion(connection, children[i], level + 1, str); + } +} + +QString QXcbNativeInterface::dumpConnectionNativeWindows(const QXcbConnection *connection, WId root) const +{ + QString result; + QTextStream str(&result); + if (root) { + dumpNativeWindowsRecursion(connection, xcb_window_t(root), 0, str); + } else { + for (const QXcbScreen *screen : connection->screens()) { + str << "Screen: \"" << screen->name() << "\"\n"; + dumpNativeWindowsRecursion(connection, screen->root(), 0, str); + str << '\n'; + } + } + return result; +} + +QString QXcbNativeInterface::dumpNativeWindows(WId root) const +{ + return dumpConnectionNativeWindows(QXcbIntegration::instance()->defaultConnection(), root); +} + QT_END_NAMESPACE diff --git a/src/plugins/platforms/xcb/qxcbnativeinterface.h b/src/plugins/platforms/xcb/qxcbnativeinterface.h index fb0db727aa..6a752c68ca 100644 --- a/src/plugins/platforms/xcb/qxcbnativeinterface.h +++ b/src/plugins/platforms/xcb/qxcbnativeinterface.h @@ -127,6 +127,8 @@ public: Q_INVOKABLE bool systrayVisualHasAlphaChannel(); Q_INVOKABLE bool requestSystemTrayWindowDock(const QWindow *window); Q_INVOKABLE QRect systemTrayWindowGlobalGeometry(const QWindow *window); + Q_INVOKABLE QString dumpConnectionNativeWindows(const QXcbConnection *connection, WId root) const; + Q_INVOKABLE QString dumpNativeWindows(WId root = 0) const; void addHandler(QXcbNativeInterfaceHandler *handler); void removeHandler(QXcbNativeInterfaceHandler *handler); diff --git a/src/plugins/platforms/xcb/qxcbscreen.cpp b/src/plugins/platforms/xcb/qxcbscreen.cpp index e3d9bc7a3d..49bf5181cc 100644 --- a/src/plugins/platforms/xcb/qxcbscreen.cpp +++ b/src/plugins/platforms/xcb/qxcbscreen.cpp @@ -91,16 +91,8 @@ QXcbVirtualDesktop::QXcbVirtualDesktop(QXcbConnection *connection, xcb_screen_t if (reply && reply->format == 32 && reply->type == XCB_ATOM_WINDOW) { xcb_window_t windowManager = *((xcb_window_t *)xcb_get_property_value(reply.get())); - if (windowManager != XCB_WINDOW_NONE) { - auto windowManagerReply = Q_XCB_REPLY_UNCHECKED(xcb_get_property, xcb_connection(), - false, windowManager, - atom(QXcbAtom::_NET_WM_NAME), - atom(QXcbAtom::UTF8_STRING), 0, 1024); - if (windowManagerReply && windowManagerReply->format == 8 && windowManagerReply->type == atom(QXcbAtom::UTF8_STRING)) { - m_windowManagerName = QString::fromUtf8((const char *)xcb_get_property_value(windowManagerReply.get()), - xcb_get_property_value_length(windowManagerReply.get())); - } - } + if (windowManager != XCB_WINDOW_NONE) + m_windowManagerName = QXcbWindow::windowTitle(connection, windowManager); } const xcb_query_extension_reply_t *sync_reply = xcb_get_extension_data(xcb_connection(), &xcb_sync_id); diff --git a/src/plugins/platforms/xcb/qxcbwindow.cpp b/src/plugins/platforms/xcb/qxcbwindow.cpp index 46698de158..89ace781ae 100644 --- a/src/plugins/platforms/xcb/qxcbwindow.cpp +++ b/src/plugins/platforms/xcb/qxcbwindow.cpp @@ -2835,5 +2835,18 @@ QXcbScreen *QXcbWindow::xcbScreen() const return static_cast(screen()); } +QString QXcbWindow::windowTitle(const QXcbConnection *conn, xcb_window_t window) +{ + const xcb_atom_t utf8Atom = conn->atom(QXcbAtom::UTF8_STRING); + auto reply = Q_XCB_REPLY_UNCHECKED(xcb_get_property, conn->xcb_connection(), + false, window, conn->atom(QXcbAtom::_NET_WM_NAME), + utf8Atom, 0, 1024); + if (reply && reply->format == 8 && reply->type == utf8Atom) { + const char *name = reinterpret_cast(xcb_get_property_value(reply.get())); + return QString::fromUtf8(name); + } + return QString(); +} + QT_END_NAMESPACE diff --git a/src/plugins/platforms/xcb/qxcbwindow.h b/src/plugins/platforms/xcb/qxcbwindow.h index 638ab26cea..65221394ea 100644 --- a/src/plugins/platforms/xcb/qxcbwindow.h +++ b/src/plugins/platforms/xcb/qxcbwindow.h @@ -182,6 +182,8 @@ public: virtual void create(); virtual void destroy(); + static QString windowTitle(const QXcbConnection *conn, xcb_window_t window); + public Q_SLOTS: void updateSyncRequestCounter(); -- cgit v1.2.3 From ae3a65122f7ca21ed88bf1fbcd88e9dcfab0e4c7 Mon Sep 17 00:00:00 2001 From: Albert Astals Cid Date: Tue, 23 Jan 2018 14:47:33 +0100 Subject: cups: Clear cups options at the start of QPrintPropertiesDialog::setupPrinter For some reason the cups options are kept globally instead of per printer at QCupsPrintEnginePrivate so when setting the printer options on accepting the print dialog, we first need to clear them and then call the other functions that set them, otherwise, we will "reuse" cups option set on the previous print. How to reproduce: * Open print dialog, set some advanced option to a non default value, easiest one is print in grayscale * Print * Check it prints in grayscale * Open print dialog, check the advanced options are all on default values * Print * Check it incorrectly prints in grayscale Change-Id: I59aacaf30db844ad40887d6b771f9354557852b6 Reviewed-by: Andy Shaw --- src/printsupport/dialogs/qprintdialog_unix.cpp | 4 ++++ src/printsupport/kernel/qcups.cpp | 5 +++++ src/printsupport/kernel/qcups_p.h | 1 + 3 files changed, 10 insertions(+) (limited to 'src') diff --git a/src/printsupport/dialogs/qprintdialog_unix.cpp b/src/printsupport/dialogs/qprintdialog_unix.cpp index 2cc5bfbb8c..caab7867dc 100644 --- a/src/printsupport/dialogs/qprintdialog_unix.cpp +++ b/src/printsupport/dialogs/qprintdialog_unix.cpp @@ -403,6 +403,10 @@ QPrintPropertiesDialog::~QPrintPropertiesDialog() void QPrintPropertiesDialog::setupPrinter() const { +#if QT_CONFIG(cups) + QCUPSSupport::clearCupsOptions(m_printer); +#endif + widget.pageSetup->setupPrinter(); #if QT_CONFIG(cupsjobwidget) m_jobOptions->setupPrinter(); diff --git a/src/printsupport/kernel/qcups.cpp b/src/printsupport/kernel/qcups.cpp index be170ed619..7e8e1707b2 100644 --- a/src/printsupport/kernel/qcups.cpp +++ b/src/printsupport/kernel/qcups.cpp @@ -77,6 +77,11 @@ void QCUPSSupport::clearCupsOption(QPrinter *printer, const QString &option) } } +void QCUPSSupport::clearCupsOptions(QPrinter *printer) +{ + setCupsOptions(printer, QStringList()); +} + static inline QString jobHoldToString(const QCUPSSupport::JobHoldUntil jobHold, const QTime holdUntilTime) { switch (jobHold) { diff --git a/src/printsupport/kernel/qcups_p.h b/src/printsupport/kernel/qcups_p.h index da2b087d5b..9a71483bb9 100644 --- a/src/printsupport/kernel/qcups_p.h +++ b/src/printsupport/kernel/qcups_p.h @@ -132,6 +132,7 @@ public: static void setCupsOption(QPrinter *printer, const QString &option, const QString &value); static void clearCupsOption(QPrinter *printer, const QString &option); + static void clearCupsOptions(QPrinter *printer); static void setJobHold(QPrinter *printer, const JobHoldUntil jobHold = NoHold, const QTime &holdUntilTime = QTime()); static void setJobBilling(QPrinter *printer, const QString &jobBilling = QString()); -- cgit v1.2.3 From ce60d9965e0dda6f925dfea70b6275dfd79e789b Mon Sep 17 00:00:00 2001 From: Gabriel de Dietrich Date: Mon, 15 Jan 2018 17:27:04 -0800 Subject: QFusionStyle: Properly draw frameless editable combo boxes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: If0b6f15dc0a1de38edec5b360b1b8b698d6ad5b8 Task-number: QTBUG-65728 Reviewed-by: Friedemann Kleint Reviewed-by: Morten Johan Sørvig --- src/widgets/styles/qfusionstyle.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/widgets/styles/qfusionstyle.cpp b/src/widgets/styles/qfusionstyle.cpp index a2b94d59d4..26f651906a 100644 --- a/src/widgets/styles/qfusionstyle.cpp +++ b/src/widgets/styles/qfusionstyle.cpp @@ -2737,6 +2737,8 @@ void QFusionStyle::drawComplexControl(ComplexControl control, const QStyleOption pixmapName += QLatin1String("-editable"); if (isEnabled) pixmapName += QLatin1String("-enabled"); + if (!comboBox->frame) + pixmapName += QLatin1String("-frameless"); if (!QPixmapCache::find(pixmapName, cache)) { cache = styleCachePixmap(comboBox->rect.size()); @@ -2762,7 +2764,8 @@ void QFusionStyle::drawComplexControl(ComplexControl control, const QStyleOption buttonOption.state &= ~State_MouseOver; } - proxy()->drawPrimitive(PE_FrameLineEdit, &buttonOption, &cachePainter, widget); + if (comboBox->frame) + proxy()->drawPrimitive(PE_FrameLineEdit, &buttonOption, &cachePainter, widget); // Draw button clipped cachePainter.save(); -- cgit v1.2.3 From 0c974c0169b55b6c4f49ea74f17863dedfc5e26d Mon Sep 17 00:00:00 2001 From: Gabriel de Dietrich Date: Wed, 17 Jan 2018 18:31:49 -0800 Subject: QMacStyle: PE_FrameLineEdit is HITheme-free MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CT_LineEdit is as well, so that makes QLineEdit free of HITheme APIs. Change-Id: Ia02ce9f1003e5ae9c8bf47dab9ada030feca98ba Reviewed-by: Morten Johan Sørvig --- src/plugins/styles/mac/qmacstyle_mac.mm | 47 ++++++++++++------------------ src/plugins/styles/mac/qmacstyle_mac_p_p.h | 3 +- 2 files changed, 20 insertions(+), 30 deletions(-) (limited to 'src') diff --git a/src/plugins/styles/mac/qmacstyle_mac.mm b/src/plugins/styles/mac/qmacstyle_mac.mm index 7972167cfa..849d0f3589 100644 --- a/src/plugins/styles/mac/qmacstyle_mac.mm +++ b/src/plugins/styles/mac/qmacstyle_mac.mm @@ -1916,6 +1916,9 @@ NSView *QMacStylePrivate::cocoaControl(CocoaControl widget) const // at construction time, and it cannot be changed later. bv = [[NSSlider alloc] initWithFrame:NSMakeRect(0, 0, 20, 200)]; break; + case TextField: + bv = [[NSTextField alloc] init]; + break; default: break; } @@ -3361,35 +3364,21 @@ void QMacStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPai case PE_FrameLineEdit: if (const QStyleOptionFrame *frame = qstyleoption_cast(opt)) { if (frame->state & State_Sunken) { - QColor baseColor(frame->palette.background().color()); - HIThemeFrameDrawInfo fdi; - fdi.version = qt_mac_hitheme_version; - fdi.state = tds; - int frame_size; - fdi.kind = frame->features & QStyleOptionFrame::Rounded ? kHIThemeFrameTextFieldRound : - kHIThemeFrameTextFieldSquare; - frame_size = qt_mac_aqua_get_metric(EditTextFrameOutset); - if ((frame->state & State_ReadOnly) || !(frame->state & State_Enabled)) - fdi.state = kThemeStateInactive; - else if (fdi.state == kThemeStatePressed) - // This pressed state doesn't make sense for a line edit frame. - // And Yosemite agrees with us. Otherwise it starts showing yellow pixels. - fdi.state = kThemeStateActive; - fdi.isFocused = (frame->state & State_HasFocus); - int lw = frame->lineWidth; - if (lw <= 0) - lw = proxy()->pixelMetric(PM_DefaultFrameWidth, frame, w); - { //clear to base color - p->save(); - p->setPen(QPen(baseColor, lw)); - p->setBrush(Qt::NoBrush); - p->drawRect(frame->rect); - p->restore(); - } - const auto frameMargins = QMargins(frame_size, frame_size, frame_size, frame_size); - const CGRect cgRect = frame->rect.marginsRemoved(frameMargins).toCGRect(); - - HIThemeDrawFrame(&cgRect, &fdi, cg, kHIThemeOrientationNormal); + const bool isEnabled = opt->state & State_Enabled; + const bool isReadOnly = opt->state & State_ReadOnly; + const bool isRounded = frame->features & QStyleOptionFrame::Rounded; + const auto cs = d->effectiveAquaSizeConstrain(opt, w, CT_LineEdit); + const auto cw = QMacStylePrivate::CocoaControl(QMacStylePrivate::TextField, cs); + auto *tf = static_cast(d->cocoaControl(cw)); + tf.enabled = isEnabled; + tf.editable = !isReadOnly; + tf.bezeled = YES; + static_cast(tf.cell).bezelStyle = isRounded ? NSTextFieldRoundedBezel : NSTextFieldSquareBezel; + tf.frame = opt->rect.toCGRect(); + d->drawNSViewInRect(cw, tf, opt->rect, p, w != nullptr, ^(CGContextRef ctx, const CGRect &rect) { + Q_UNUSED(ctx); + [tf.cell drawWithFrame:rect inView:tf]; + }); } else { QCommonStyle::drawPrimitive(pe, opt, p, w); } diff --git a/src/plugins/styles/mac/qmacstyle_mac_p_p.h b/src/plugins/styles/mac/qmacstyle_mac_p_p.h index 8eee80ce50..57c22cfd8e 100644 --- a/src/plugins/styles/mac/qmacstyle_mac_p_p.h +++ b/src/plugins/styles/mac/qmacstyle_mac_p_p.h @@ -196,7 +196,8 @@ public: Scroller_Vertical, Slider_Horizontal, Slider_Vertical, - Stepper // QSpinBox buttons + Stepper, // QSpinBox buttons + TextField }; typedef QPair CocoaControl; -- cgit v1.2.3 From 86821094a9cc3f1a3fe97832eaebf05d96156784 Mon Sep 17 00:00:00 2001 From: Gabriel de Dietrich Date: Wed, 17 Jan 2018 18:37:20 -0800 Subject: QMacStyle: declare block in drawNSViewInRect() as noescape MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: Ida67a963ab62c6b974eceeaf9d386b941f357798 Reviewed-by: Morten Johan Sørvig --- src/plugins/styles/mac/qmacstyle_mac.mm | 2 +- src/plugins/styles/mac/qmacstyle_mac_p_p.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/plugins/styles/mac/qmacstyle_mac.mm b/src/plugins/styles/mac/qmacstyle_mac.mm index 849d0f3589..aee2871917 100644 --- a/src/plugins/styles/mac/qmacstyle_mac.mm +++ b/src/plugins/styles/mac/qmacstyle_mac.mm @@ -1993,7 +1993,7 @@ NSCell *QMacStylePrivate::cocoaCell(CocoaControl widget) const return cell; } -void QMacStylePrivate::drawNSViewInRect(CocoaControl widget, NSView *view, const QRect &qtRect, QPainter *p, bool isQWidget, DrawRectBlock drawRectBlock) const +void QMacStylePrivate::drawNSViewInRect(CocoaControl widget, NSView *view, const QRect &qtRect, QPainter *p, bool isQWidget, __attribute__((noescape)) DrawRectBlock drawRectBlock) const { QPoint offset; if (widget == CocoaControl(Button_PopupButton, QStyleHelper::SizeSmall)) diff --git a/src/plugins/styles/mac/qmacstyle_mac_p_p.h b/src/plugins/styles/mac/qmacstyle_mac_p_p.h index 57c22cfd8e..e244cd0ff1 100644 --- a/src/plugins/styles/mac/qmacstyle_mac_p_p.h +++ b/src/plugins/styles/mac/qmacstyle_mac_p_p.h @@ -267,7 +267,7 @@ public: void setupVerticalInvertedXform(CGContextRef cg, bool reverse, bool vertical, const CGRect &rect) const; - void drawNSViewInRect(CocoaControl widget, NSView *view, const QRect &rect, QPainter *p, bool isQWidget = true, DrawRectBlock drawRectBlock = nil) const; + void drawNSViewInRect(CocoaControl widget, NSView *view, const QRect &rect, QPainter *p, bool isQWidget = true, __attribute__((noescape)) DrawRectBlock drawRectBlock = nil) const; void resolveCurrentNSView(QWindow *window) const; void drawFocusRing(QPainter *p, const QRect &targetRect, int hMargin, int vMargin, qreal radius = 0) const; -- cgit v1.2.3 From 1e905927f0c9e0da3eae69c1d60633cf7d6ad539 Mon Sep 17 00:00:00 2001 From: Gabriel de Dietrich Date: Thu, 18 Jan 2018 15:28:58 -0800 Subject: QMacStyle: CE_Splitter is HITheme-free MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I96a5ba5d685d1b8f95fd11489e4e95096d0fa9a4 Reviewed-by: Morten Johan Sørvig Reviewed-by: Timur Pocheptsov --- src/plugins/styles/mac/qmacstyle_mac.mm | 36 ++++++++++++++++++++++++------ src/plugins/styles/mac/qmacstyle_mac_p_p.h | 2 ++ 2 files changed, 31 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/plugins/styles/mac/qmacstyle_mac.mm b/src/plugins/styles/mac/qmacstyle_mac.mm index aee2871917..a5d94ec9b9 100644 --- a/src/plugins/styles/mac/qmacstyle_mac.mm +++ b/src/plugins/styles/mac/qmacstyle_mac.mm @@ -237,6 +237,19 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QIndeterminateProgressIndicator); @end +@interface QT_MANGLE_NAMESPACE(QVerticalSplitView) : NSSplitView +- (BOOL)isVertical; +@end + +QT_NAMESPACE_ALIAS_OBJC_CLASS(QVerticalSplitView); + +@implementation QVerticalSplitView +- (BOOL)isVertical +{ + return YES; +} +@end + QT_BEGIN_NAMESPACE // The following constants are used for adjusting the size @@ -1916,6 +1929,12 @@ NSView *QMacStylePrivate::cocoaControl(CocoaControl widget) const // at construction time, and it cannot be changed later. bv = [[NSSlider alloc] initWithFrame:NSMakeRect(0, 0, 20, 200)]; break; + case SplitView_Horizontal: + bv = [[NSSplitView alloc] init]; + break; + case SplitView_Vertical: + bv = [[QVerticalSplitView alloc] init]; + break; case TextField: bv = [[NSTextField alloc] init]; break; @@ -4465,13 +4484,16 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter break; } case CE_Splitter: - if (opt->rect.width() > 1 && opt->rect.height() > 1){ - HIThemeSplitterDrawInfo sdi; - sdi.version = qt_mac_hitheme_version; - sdi.state = tds; - sdi.adornment = kHIThemeSplitterAdornmentMetal; - CGRect cgRect = opt->rect.toCGRect(); - HIThemeDrawPaneSplitter(&cgRect, &sdi, cg, kHIThemeOrientationNormal); + if (opt->rect.width() > 1 && opt->rect.height() > 1) { + const bool isVertical = !(opt->state & QStyle::State_Horizontal); + // Qt refers to the layout orientation, while Cocoa refers to the divider's. + const auto ct = isVertical ? QMacStylePrivate::SplitView_Horizontal : QMacStylePrivate::SplitView_Vertical; + const auto cw = QMacStylePrivate::CocoaControl(ct, QStyleHelper::SizeLarge); + auto *sv = static_cast(d->cocoaControl(cw)); + sv.frame = opt->rect.toCGRect(); + d->drawNSViewInRect(cw, sv, opt->rect, p, w != nullptr, ^(CGContextRef ctx, const CGRect &rect) { + [sv drawDividerInRect:rect]; + }); } else { QPen oldPen = p->pen(); p->setPen(opt->palette.dark().color()); diff --git a/src/plugins/styles/mac/qmacstyle_mac_p_p.h b/src/plugins/styles/mac/qmacstyle_mac_p_p.h index e244cd0ff1..02f74294d1 100644 --- a/src/plugins/styles/mac/qmacstyle_mac_p_p.h +++ b/src/plugins/styles/mac/qmacstyle_mac_p_p.h @@ -196,6 +196,8 @@ public: Scroller_Vertical, Slider_Horizontal, Slider_Vertical, + SplitView_Horizontal, + SplitView_Vertical, Stepper, // QSpinBox buttons TextField }; -- cgit v1.2.3 From 933eac9645d6a54cc3f29043243711385e92cfe5 Mon Sep 17 00:00:00 2001 From: Allan Sandfeld Jensen Date: Fri, 19 Jan 2018 15:20:45 +0100 Subject: Correct since value of QNetworkRequest::Http2DirectAttribute It did not make it into 5.10 as far as I can tell. Change-Id: I01f950c476f2c98f4ed40d2cafd15ecc112a3427 Reviewed-by: Timur Pocheptsov Reviewed-by: Edward Welbourne --- src/network/access/qnetworkrequest.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/network/access/qnetworkrequest.cpp b/src/network/access/qnetworkrequest.cpp index 99f72a7955..1d7c5bec51 100644 --- a/src/network/access/qnetworkrequest.cpp +++ b/src/network/access/qnetworkrequest.cpp @@ -308,7 +308,7 @@ QT_BEGIN_NAMESPACE was specified, QNetworkAccessManager gives up, without attempting to fall back to HTTP/1.1. If both HTTP2AllowedAttribute and Http2DirectAttribute are set, Http2DirectAttribute takes priority. - (This value was introduced in 5.10.) + (This value was introduced in 5.11.) \omitvalue ResourceTypeAttribute -- cgit v1.2.3 From 34e8cafa29a5240bb7a426c69a5fd8e8ad741dc4 Mon Sep 17 00:00:00 2001 From: Allan Sandfeld Jensen Date: Tue, 23 Jan 2018 13:10:37 +0100 Subject: Document the new qfloat16 functions with the other qfloat16 functions Change-Id: I82d3fcda44a9a49d9027d865f8ed200134d0f79e Reviewed-by: Thiago Macieira --- src/corelib/global/qfloat16.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/corelib/global/qfloat16.cpp b/src/corelib/global/qfloat16.cpp index 3046a47292..fd608efe55 100644 --- a/src/corelib/global/qfloat16.cpp +++ b/src/corelib/global/qfloat16.cpp @@ -178,6 +178,7 @@ static void qFloatFromFloat16_fast(float *, const quint16 *, qsizetype) Q_DECL_N #endif /*! \since 5.11 + \relates Converts \a len floats from \a in to qfloat16 and stores them in \a out. Both \a in and \a out must have \a len allocated entries. @@ -193,6 +194,7 @@ Q_CORE_EXPORT void qFloatToFloat16(qfloat16 *out, const float *in, qsizetype len /*! \since 5.11 + \relates Converts \a len qfloat16 from \a in to floats and stores them in \a out. Both \a in and \a out must have \a len allocated entries. -- cgit v1.2.3 From 43c44d05ca6af4ec78c1dea84635375a637ff80d Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Thu, 30 Nov 2017 20:37:57 -0800 Subject: Update the overflow functions to include qsizetype Commit 29bc68cf169b8cf87d306a1c72e12eb9b6fbfce7 added support for unsigned and commit 5ff7a3d96e0ce0dcb3d388b53d038cdd40c7a975 later added support for int. This commit adds support for qsizetype, which isn't int on 64-bit platforms. We do this by reorganizing the code and using the generic version of __builtin_{add,sub,mul}_overflow from GCC 5 and Clang 3.8, which ICC 18 seems to support now too on Linux. That leaves older versions of GCC and Clang, as well as MSVC, ICC on Windows, and the GHS compiler, to use the generic implementations, as I've removed the assembly code those versions of GCC and Clang on x86 are now uncommon. Note: any older version of ICC probably breaks. We only support the latest. Change-Id: I9e2892cb6c374e93bcb7fffd14fc11bcd5f067a7 Reviewed-by: Allan Sandfeld Jensen --- src/corelib/global/qnumeric_p.h | 277 +++++++++++++++++----------------------- 1 file changed, 117 insertions(+), 160 deletions(-) (limited to 'src') diff --git a/src/corelib/global/qnumeric_p.h b/src/corelib/global/qnumeric_p.h index a352e39ef0..9b86a16516 100644 --- a/src/corelib/global/qnumeric_p.h +++ b/src/corelib/global/qnumeric_p.h @@ -1,7 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. -** Copyright (C) 2016 Intel Corporation. +** Copyright (C) 2018 Intel Corporation. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtCore module of the Qt Toolkit. @@ -58,8 +58,6 @@ #if defined(Q_CC_MSVC) # include -#elif defined(Q_CC_INTEL) -# include // for _addcarry_u #endif #if defined(Q_CC_MSVC) @@ -164,10 +162,33 @@ Q_DECL_CONST_FUNCTION static inline bool qt_is_finite(float f) } #ifndef Q_CLANG_QDOC -// -// Unsigned overflow math -// namespace { +// Overflow math. +// This provides efficient implementations for int, unsigned, qsizetype and +// size_t. Implementations for 8- and 16-bit types will work but may not be as +// efficient. Implementations for 64-bit may be missing on 32-bit platforms. + +#if (defined(Q_CC_GNU) && (Q_CC_GNU >= 500) || defined(Q_CC_INTEL)) || QT_HAS_BUILTIN(__builtin_add_overflowx) +// GCC 5, ICC 18, and Clang 3.8 have builtins to detect overflows + +template inline +typename std::enable_if::value || std::is_signed::value, bool>::type +add_overflow(T v1, T v2, T *r) +{ return __builtin_add_overflow(v1, v2, r); } + +template inline +typename std::enable_if::value || std::is_signed::value, bool>::type +sub_overflow(T v1, T v2, T *r) +{ return __builtin_sub_overflow(v1, v2, r); } + +template inline +typename std::enable_if::value || std::is_signed::value, bool>::type +mul_overflow(T v1, T v2, T *r) +{ return __builtin_mul_overflow(v1, v2, r); } + +#else +// Generic implementations + template inline typename std::enable_if::value, bool>::type add_overflow(T v1, T v2, T *r) { @@ -176,69 +197,92 @@ add_overflow(T v1, T v2, T *r) return v1 > T(v1 + v2); } +template inline typename std::enable_if::value, bool>::type +add_overflow(T v1, T v2, T *r) +{ + // Here's how we calculate the overflow: + // 1) unsigned addition is well-defined, so we can always execute it + // 2) conversion from unsigned back to signed is implementation- + // defined and in the implementations we use, it's a no-op. + // 3) signed integer overflow happens if the sign of the two input operands + // is the same but the sign of the result is different. In other words, + // the sign of the result must be the same as the sign of either + // operand. + + using U = typename std::make_unsigned::type; + *r = T(U(v1) + U(v2)); + + // If int is two's complement, assume all integer types are too. + if (std::is_same::value) { + // Two's complement equivalent (generates slightly shorter code): + // x ^ y is negative if x and y have different signs + // x & y is negative if x and y are negative + // (x ^ z) & (y ^ z) is negative if x and z have different signs + // AND y and z have different signs + return ((v1 ^ *r) & (v2 ^ *r)) < 0; + } + + bool s1 = (v1 < 0); + bool s2 = (v2 < 0); + bool sr = (*r < 0); + return s1 != sr && s2 != sr; + // also: return s1 == s2 && s1 != sr; +} + template inline typename std::enable_if::value, bool>::type +sub_overflow(T v1, T v2, T *r) +{ + // unsigned subtractions are well-defined + *r = v1 - v2; + return v1 < v2; +} + +template inline typename std::enable_if::value, bool>::type +sub_overflow(T v1, T v2, T *r) +{ + // See above for explanation. This is the same with some signs reversed. + // We can't use add_overflow(v1, -v2, r) because it would be UB if + // v2 == std::numeric_limits::min(). + + using U = typename std::make_unsigned::type; + *r = T(U(v1) - U(v2)); + + if (std::is_same::value) + return ((v1 ^ *r) & (~v2 ^ *r)) < 0; + + bool s1 = (v1 < 0); + bool s2 = !(v2 < 0); + bool sr = (*r < 0); + return s1 != sr && s2 != sr; + // also: return s1 == s2 && s1 != sr; +} + +template inline +typename std::enable_if::value || std::is_signed::value, bool>::type mul_overflow(T v1, T v2, T *r) { // use the next biggest type // Note: for 64-bit systems where __int128 isn't supported, this will cause an error. - // A fallback is present below. - typedef typename QIntegerForSize::Unsigned Larger; + using LargerInt = QIntegerForSize; + using Larger = typename std::conditional::value, + typename LargerInt::Signed, typename LargerInt::Unsigned>::type; Larger lr = Larger(v1) * Larger(v2); *r = T(lr); - return lr > std::numeric_limits::max(); + return lr > std::numeric_limits::max() || lr < std::numeric_limits::min(); } -#if defined(__SIZEOF_INT128__) -# define HAVE_MUL64_OVERFLOW -#endif - -// GCC 5 and Clang have builtins to detect overflows -#if (defined(Q_CC_GNU) && !defined(Q_CC_INTEL) && Q_CC_GNU >= 500) || QT_HAS_BUILTIN(__builtin_uadd_overflow) +# if defined(Q_CC_MSVC) && defined(Q_PROCESSOR_X86) +// We can use intrinsics for the unsigned operations with MSVC template <> inline bool add_overflow(unsigned v1, unsigned v2, unsigned *r) -{ return __builtin_uadd_overflow(v1, v2, r); } -#endif -#if (defined(Q_CC_GNU) && !defined(Q_CC_INTEL) && Q_CC_GNU >= 500) || QT_HAS_BUILTIN(__builtin_uaddl_overflow) -template <> inline bool add_overflow(unsigned long v1, unsigned long v2, unsigned long *r) -{ return __builtin_uaddl_overflow(v1, v2, r); } -#endif -#if (defined(Q_CC_GNU) && !defined(Q_CC_INTEL) && Q_CC_GNU >= 500) || QT_HAS_BUILTIN(__builtin_uaddll_overflow) -template <> inline bool add_overflow(unsigned long long v1, unsigned long long v2, unsigned long long *r) -{ return __builtin_uaddll_overflow(v1, v2, r); } -#endif +{ return _addcarry_u32(0, v1, v2, r); } -#if (defined(Q_CC_GNU) && !defined(Q_CC_INTEL) && Q_CC_GNU >= 500) || QT_HAS_BUILTIN(__builtin_umul_overflow) -template <> inline bool mul_overflow(unsigned v1, unsigned v2, unsigned *r) -{ return __builtin_umul_overflow(v1, v2, r); } -#endif -#if (defined(Q_CC_GNU) && !defined(Q_CC_INTEL) && Q_CC_GNU >= 500) || QT_HAS_BUILTIN(__builtin_umull_overflow) -template <> inline bool mul_overflow(unsigned long v1, unsigned long v2, unsigned long *r) -{ return __builtin_umull_overflow(v1, v2, r); } -#endif -#if (defined(Q_CC_GNU) && !defined(Q_CC_INTEL) && Q_CC_GNU >= 500) || QT_HAS_BUILTIN(__builtin_umulll_overflow) -template <> inline bool mul_overflow(unsigned long long v1, unsigned long long v2, unsigned long long *r) -{ return __builtin_umulll_overflow(v1, v2, r); } -# define HAVE_MUL64_OVERFLOW -#endif +// 32-bit mul_overflow is fine with the generic code above -#if (defined(Q_CC_MSVC) || defined(Q_CC_INTEL)) && defined(Q_PROCESSOR_X86) && !QT_HAS_BUILTIN(__builtin_uadd_overflow) -template <> inline bool add_overflow(unsigned v1, unsigned v2, unsigned *r) -{ return _addcarry_u32(0, v1, v2, r); } -# ifdef Q_CC_MSVC // longs are 32-bit -template <> inline bool add_overflow(unsigned long v1, unsigned long v2, unsigned long *r) -{ return _addcarry_u32(0, v1, v2, reinterpret_cast(r)); } -# endif -#endif -#if (defined(Q_CC_MSVC) || defined(Q_CC_INTEL)) && defined(Q_PROCESSOR_X86_64) && !QT_HAS_BUILTIN(__builtin_uadd_overflow) +# if defined(Q_PROCESSOR_X86_64) template <> inline bool add_overflow(quint64 v1, quint64 v2, quint64 *r) { return _addcarry_u64(0, v1, v2, reinterpret_cast(r)); } -# ifndef Q_CC_MSVC // longs are 64-bit -template <> inline bool add_overflow(unsigned long v1, unsigned long v2, unsigned long *r) -{ return _addcarry_u64(0, v1, v2, reinterpret_cast(r)); } -# endif -#endif -#if defined(Q_CC_MSVC) && (defined(Q_PROCESSOR_X86_64) || defined(Q_PROCESSOR_IA64)) && !QT_HAS_BUILTIN(__builtin_uadd_overflow) -#pragma intrinsic(_umul128) +# pragma intrinsic(_umul128) template <> inline bool mul_overflow(quint64 v1, quint64 v2, quint64 *r) { // use 128-bit multiplication with the _umul128 intrinsic @@ -247,117 +291,30 @@ template <> inline bool mul_overflow(quint64 v1, quint64 v2, quint64 *r) *r = _umul128(v1, v2, &high); return high; } -# define HAVE_MUL64_OVERFLOW -#endif - -#if !defined(HAVE_MUL64_OVERFLOW) && defined(__LP64__) -// no 128-bit multiplication, we need to figure out with a slow division -template <> inline bool mul_overflow(quint64 v1, quint64 v2, quint64 *r) -{ - if (v2 && v1 > std::numeric_limits::max() / v2) - return true; - *r = v1 * v2; - return false; -} -template <> inline bool mul_overflow(unsigned long v1, unsigned long v2, unsigned long *r) -{ - return mul_overflow(v1, v2, reinterpret_cast(r)); -} -#else -# undef HAVE_MUL64_OVERFLOW -#endif - -// -// Signed overflow math -// -// In C++, signed overflow math is Undefined Behavior. However, many CPUs do implement some way to -// check for overflow. Some compilers expose intrinsics to use this functionality. If the no -// intrinsic is exposed, overflow checking can be done by widening the result type and "manually" -// checking for overflow. Or, alternatively, by using inline assembly to use the CPU features. -// -// Only int overflow checking is implemented, because it's the only one used. -#if (defined(Q_CC_GNU) && !defined(Q_CC_INTEL) && Q_CC_GNU >= 500) || QT_HAS_BUILTIN(__builtin_sadd_overflow) -inline bool add_overflow(int v1, int v2, int *r) -{ return __builtin_sadd_overflow(v1, v2, r); } -#elif defined(Q_CC_GNU) && defined(Q_PROCESSOR_X86) -inline bool add_overflow(int v1, int v2, int *r) -{ - quint8 overflow = 0; - int res = v1; - - asm ("addl %2, %1\n" - "seto %0" - : "=q" (overflow), "=r" (res) - : "r" (v2), "1" (res) - : "cc" - ); - *r = res; - return overflow; -} -#else -inline bool add_overflow(int v1, int v2, int *r) -{ - qint64 t = qint64(v1) + v2; - *r = static_cast(t); - return t > std::numeric_limits::max() || t < std::numeric_limits::min(); -} -#endif -#if (defined(Q_CC_GNU) && !defined(Q_CC_INTEL) && Q_CC_GNU >= 500) || QT_HAS_BUILTIN(__builtin_ssub_overflow) -inline bool sub_overflow(int v1, int v2, int *r) -{ return __builtin_ssub_overflow(v1, v2, r); } -#elif defined(Q_CC_GNU) && defined(Q_PROCESSOR_X86) -inline bool sub_overflow(int v1, int v2, int *r) +# pragma intrinsic(_mul128) +template <> inline bool mul_overflow(qint64 v1, qint64 v2, qint64 *r) { - quint8 overflow = 0; - int res = v1; - - asm ("subl %2, %1\n" - "seto %0" - : "=q" (overflow), "=r" (res) - : "r" (v2), "1" (res) - : "cc" - ); - *r = res; - return overflow; + // Use 128-bit multiplication with the _mul128 intrinsic + // https://msdn.microsoft.com/en-us/library/82cxdw50.aspx + + // This is slightly more complex than the unsigned case above: the sign bit + // of 'low' must be replicated as the entire 'high', so the only valid + // values for 'high' are 0 and -1. + + qint64 high; + *r = _mul128(v1, v2, &high); + if (high == 0) + return *r < 0; + if (high == -1) + return *r >= 0; + return true; } -#else -inline bool sub_overflow(int v1, int v2, int *r) -{ - qint64 t = qint64(v1) - v2; - *r = static_cast(t); - return t > std::numeric_limits::max() || t < std::numeric_limits::min(); +# endif // x86-64 +# endif // MSVC x86 +#endif // !GCC } -#endif - -#if (defined(Q_CC_GNU) && !defined(Q_CC_INTEL) && Q_CC_GNU >= 500) || QT_HAS_BUILTIN(__builtin_smul_overflow) -inline bool mul_overflow(int v1, int v2, int *r) -{ return __builtin_smul_overflow(v1, v2, r); } -#elif defined(Q_CC_GNU) && defined(Q_PROCESSOR_X86) -inline bool mul_overflow(int v1, int v2, int *r) -{ - quint8 overflow = 0; - int res = v1; - - asm ("imul %2, %1\n" - "seto %0" - : "=q" (overflow), "=r" (res) - : "r" (v2), "1" (res) - : "cc" - ); - *r = res; - return overflow; -} -#else -inline bool mul_overflow(int v1, int v2, int *r) -{ - qint64 t = qint64(v1) * v2; - *r = static_cast(t); - return t > std::numeric_limits::max() || t < std::numeric_limits::min(); -} -#endif #endif // Q_CLANG_QDOC -} QT_END_NAMESPACE -- cgit v1.2.3 From 5927acaf652e2dfacd777a7491c217aefd1118ef Mon Sep 17 00:00:00 2001 From: Allan Sandfeld Jensen Date: Wed, 9 Aug 2017 15:57:00 +0200 Subject: Add support for converting from std::variant Adds inline handling of the C++17 type std::variant, so the type will be resolved if converted into a QVariant. Change-Id: I31809d70d7f347277389d42a3695836ec7a32d02 Reviewed-by: Thiago Macieira --- src/corelib/kernel/qvariant.cpp | 13 +++++++++++++ src/corelib/kernel/qvariant.h | 19 +++++++++++++++++++ 2 files changed, 32 insertions(+) (limited to 'src') diff --git a/src/corelib/kernel/qvariant.cpp b/src/corelib/kernel/qvariant.cpp index 9473ecbdf5..24f36b3b01 100644 --- a/src/corelib/kernel/qvariant.cpp +++ b/src/corelib/kernel/qvariant.cpp @@ -3892,6 +3892,19 @@ QDebug operator<<(QDebug dbg, const QVariant::Type p) \sa setValue(), value() */ +/*! \fn static inline QVariant fromStdVariant(const std::variant &value) + \since 5.11 + + Returns a QVariant with the type and value of the active variant of \a value. If + the active type is std::monostate a default QVariant is returned. + + \note With this method you do not need to register the variant as a Qt metatype, + since the std::variant is resolved before being stored. The component types + should be registered however. + + \sa fromValue() +*/ + /*! \fn template QVariant qVariantFromValue(const T &value) \relates QVariant diff --git a/src/corelib/kernel/qvariant.h b/src/corelib/kernel/qvariant.h index 29e67e9dd8..fe1ef1bdfc 100644 --- a/src/corelib/kernel/qvariant.h +++ b/src/corelib/kernel/qvariant.h @@ -53,6 +53,10 @@ #include #endif +#if QT_HAS_INCLUDE() && __cplusplus >= 201703L +#include +#endif + QT_BEGIN_NAMESPACE @@ -355,6 +359,16 @@ class Q_CORE_EXPORT QVariant static inline QVariant fromValue(const T &value) { return qVariantFromValue(value); } +#if QT_HAS_INCLUDE() && __cplusplus >= 201703L + template + static inline QVariant fromStdVariant(const std::variant &value) + { + if (value.valueless_by_exception()) + return QVariant(); + return std::visit([](const auto &arg) { return fromValue(arg); }, value); + } +#endif + template bool canConvert() const { return canConvert(qMetaTypeId()); } @@ -503,6 +517,11 @@ inline QVariant qVariantFromValue(const T &t) template <> inline QVariant qVariantFromValue(const QVariant &t) { return t; } +#if QT_HAS_INCLUDE() && __cplusplus >= 201703L +template <> +inline QVariant qVariantFromValue(const std::monostate &) { return QVariant(); } +#endif + template inline void qVariantSetValue(QVariant &v, const T &t) { -- cgit v1.2.3 From 9061b7bde0b2741492cc2e603c3086c5bd99980a Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Fri, 17 Nov 2017 15:06:03 -0800 Subject: qsimd_p.h: Reorganize and simplify the x86 intrinsics #includes All of our compilers support #include , so we don't need the legacy code that includes the earlier versions. Change-Id: I938b024e38bf4aac9154fffd14f80214d1d744c8 Reviewed-by: Allan Sandfeld Jensen --- src/corelib/tools/qsimd_p.h | 96 +++++++++++++-------------------------------- 1 file changed, 28 insertions(+), 68 deletions(-) (limited to 'src') diff --git a/src/corelib/tools/qsimd_p.h b/src/corelib/tools/qsimd_p.h index fafc3e37b0..eb56b31348 100644 --- a/src/corelib/tools/qsimd_p.h +++ b/src/corelib/tools/qsimd_p.h @@ -177,55 +177,37 @@ # define QT_FUNCTION_TARGET(x) #endif -#if defined(Q_CC_MSVC) && (defined(_M_AVX) || defined(__AVX__)) -// Visual Studio defines __AVX__ when /arch:AVX is passed, but not the earlier macros -// See: https://msdn.microsoft.com/en-us/library/b0084kay.aspx -// SSE2 is handled by _M_IX86_FP below -# define __SSE3__ 1 -# define __SSSE3__ 1 -// no Intel CPU supports SSE4a, so don't define it -# define __SSE4_1__ 1 -# define __SSE4_2__ 1 -# ifndef __AVX__ -# define __AVX__ 1 -# endif -#endif - -// SSE intrinsics -#if defined(__SSE2__) || (defined(QT_COMPILER_SUPPORTS_SSE2) && defined(QT_COMPILER_SUPPORTS_SIMD_ALWAYS)) -#if defined(QT_LINUXBASE) -/// this is an evil hack - the posix_memalign declaration in LSB -/// is wrong - see http://bugs.linuxbase.org/show_bug.cgi?id=2431 -# define posix_memalign _lsb_hack_posix_memalign -# include -# undef posix_memalign -#else -# include -#endif -#if defined(Q_CC_MSVC) && (defined(_M_X64) || _M_IX86_FP >= 2) -# define __SSE__ 1 -# define __SSE2__ 1 -#endif -#endif +#ifdef Q_PROCESSOR_X86 +/* -- x86 intrinsic support -- */ -// SSE3 intrinsics -#if defined(__SSE3__) || (defined(QT_COMPILER_SUPPORTS_SSE3) && defined(QT_COMPILER_SUPPORTS_SIMD_ALWAYS)) -#include -#endif +# if defined(Q_CC_MSVC) && (defined(_M_X64) || _M_IX86_FP >= 2) +// MSVC doesn't define __SSE2__, so do it ourselves +# define __SSE__ 1 +# define __SSE2__ 1 +# endif -// SSSE3 intrinsics -#if defined(__SSSE3__) || (defined(QT_COMPILER_SUPPORTS_SSSE3) && defined(QT_COMPILER_SUPPORTS_SIMD_ALWAYS)) -#include -#endif +# ifdef __SSE2__ +// #include the intrinsics +# include +# endif -// SSE4.1 intrinsics -#if defined(__SSE4_1__) || (defined(QT_COMPILER_SUPPORTS_SSE4_1) && defined(QT_COMPILER_SUPPORTS_SIMD_ALWAYS)) -#include -#endif +# if defined(Q_CC_GNU) && !defined(Q_CC_INTEL) +// GCC 4.4 and Clang 2.8 added a few more intrinsics there +# include +# endif -// SSE4.2 intrinsics -#if defined(__SSE4_2__) || (defined(QT_COMPILER_SUPPORTS_SSE4_2) && defined(QT_COMPILER_SUPPORTS_SIMD_ALWAYS)) -#include +# if defined(Q_CC_MSVC) && (defined(_M_AVX) || defined(__AVX__)) +// Visual Studio defines __AVX__ when /arch:AVX is passed, but not the earlier macros +// See: https://msdn.microsoft.com/en-us/library/b0084kay.aspx +# define __SSE3__ 1 +# define __SSSE3__ 1 +// no Intel CPU supports SSE4a, so don't define it +# define __SSE4_1__ 1 +# define __SSE4_2__ 1 +# ifndef __AVX__ +# define __AVX__ 1 +# endif +# endif # if defined(__SSE4_2__) && defined(QT_COMPILER_SUPPORTS_SIMD_ALWAYS) && (defined(Q_CC_INTEL) || defined(Q_CC_MSVC)) // POPCNT instructions: @@ -233,13 +215,8 @@ // (but neither MSVC nor the Intel compiler define this macro) # define __POPCNT__ 1 # endif -#endif // AVX intrinsics -#if defined(__AVX__) || (defined(QT_COMPILER_SUPPORTS_AVX) && defined(QT_COMPILER_SUPPORTS_SIMD_ALWAYS)) -// immintrin.h is the ultimate header, we don't need anything else after this -#include - # if defined(__AVX__) && defined(QT_COMPILER_SUPPORTS_SIMD_ALWAYS) && (defined(Q_CC_INTEL) || defined(Q_CC_MSVC)) // AES, PCLMULQDQ instructions: // All processors that support AVX support AES, PCLMULQDQ @@ -255,11 +232,6 @@ # define __F16C__ 1 # define __RDRND__ 1 # endif -#endif - -#if defined(__AES__) || defined(__PCLMUL__) || (defined(QT_COMPILER_SUPPORTS_AES) && defined(QT_COMPILER_SUPPORTS_SIMD_ALWAYS)) -# include -#endif #define QT_FUNCTION_TARGET_STRING_SSE2 "sse2" #define QT_FUNCTION_TARGET_STRING_SSE3 "sse3" @@ -288,19 +260,7 @@ #define QT_FUNCTION_TARGET_STRING_RDSEED "rdseed" #define QT_FUNCTION_TARGET_STRING_SHA "sha" -// other x86 intrinsics -#if defined(Q_PROCESSOR_X86) && ((defined(Q_CC_GNU) && (Q_CC_GNU >= 404)) \ - || (defined(Q_CC_CLANG) && (Q_CC_CLANG >= 208)) \ - || defined(Q_CC_INTEL)) -# define QT_COMPILER_SUPPORTS_X86INTRIN -# ifdef Q_CC_INTEL -// The Intel compiler has no -- all intrinsics are in ; -# include -# else -// GCC 4.4 and Clang 2.8 added a few more intrinsics there -# include -# endif -#endif +#endif /* Q_PROCESSOR_X86 */ // Clang compiler fix, see http://lists.llvm.org/pipermail/cfe-commits/Week-of-Mon-20160222/151168.html // This should be tweaked with an "upper version" of clang once we know which release fixes the -- cgit v1.2.3 From f55c73ede28d4455f555a28e401407326ac9b954 Mon Sep 17 00:00:00 2001 From: Lars Schmertmann Date: Tue, 23 May 2017 14:47:14 +0200 Subject: Introduce QSslConfiguration::backendConfig With this change it is possible to use all supported configurations in different backends without any new interfaces. Change-Id: Ib233539a970681d30ae3907258730e491f8d3531 Reviewed-by: Timur Pocheptsov --- src/network/ssl/qsslconfiguration.cpp | 56 ++++++++++++++++++++ src/network/ssl/qsslconfiguration.h | 5 ++ src/network/ssl/qsslconfiguration_p.h | 3 ++ src/network/ssl/qsslcontext_openssl.cpp | 71 ++++++++++++++++++++++++++ src/network/ssl/qsslcontext_openssl11.cpp | 4 ++ src/network/ssl/qsslcontext_openssl_p.h | 1 + src/network/ssl/qsslcontext_opensslpre11.cpp | 4 ++ src/network/ssl/qsslsocket.cpp | 2 + src/network/ssl/qsslsocket_openssl_symbols.cpp | 16 ++++++ src/network/ssl/qsslsocket_openssl_symbols_p.h | 8 +++ 10 files changed, 170 insertions(+) (limited to 'src') diff --git a/src/network/ssl/qsslconfiguration.cpp b/src/network/ssl/qsslconfiguration.cpp index 75a880f115..cbbbac85fe 100644 --- a/src/network/ssl/qsslconfiguration.cpp +++ b/src/network/ssl/qsslconfiguration.cpp @@ -221,6 +221,7 @@ bool QSslConfiguration::operator==(const QSslConfiguration &other) const d->peerVerifyMode == other.d->peerVerifyMode && d->peerVerifyDepth == other.d->peerVerifyDepth && d->allowRootCertOnDemandLoading == other.d->allowRootCertOnDemandLoading && + d->backendConfig == other.d->backendConfig && d->sslOptions == other.d->sslOptions && d->sslSession == other.d->sslSession && d->sslSessionTicketLifeTimeHint == other.d->sslSessionTicketLifeTimeHint && @@ -263,6 +264,7 @@ bool QSslConfiguration::isNull() const d->privateKey.isNull() && d->peerCertificate.isNull() && d->peerCertificateChain.count() == 0 && + d->backendConfig.isEmpty() && d->sslOptions == QSslConfigurationPrivate::defaultSslOptions && d->sslSession.isNull() && d->sslSessionTicketLifeTimeHint == -1 && @@ -869,6 +871,60 @@ void QSslConfiguration::setDiffieHellmanParameters(const QSslDiffieHellmanParame d->dhParams = dhparams; } +/*! + \since 5.11 + + Returns the backend-specific configuration. + + Only options set by addBackendConfig() or setBackendConfig() will be + returned. The internal standard configuration of the backend is not reported. + + \sa setBackendConfigOption(), setBackendConfig() + */ +QMap QSslConfiguration::backendConfig() const +{ + return d->backendConfig; +} + +/*! + \since 5.11 + + Sets an option in the backend-specific configuration. + + Options supported by the OpenSSL (>= 1.0.2) backend are available in the \l + {https://www.openssl.org/docs/manmaster/man3/SSL_CONF_cmd.html#SUPPORTED-CONFIGURATION-FILE-COMMANDS} + {supported configuration file commands} documentation. The expected type for + the \a value parameter is a QByteArray for all options. The \l + {https://www.openssl.org/docs/manmaster/man3/SSL_CONF_cmd.html#EXAMPLES}{examples} + show how to use some of the options. + + \note The backend-specific configuration will be applied after the general + configuration. Using the backend-specific configuration to set a general + configuration option again will overwrite the general configuration option. + + \sa backendConfig(), setBackendConfig() + */ +void QSslConfiguration::setBackendConfigOption(const QByteArray &name, const QVariant &value) +{ + d->backendConfig[name] = value; +} + +/*! + \since 5.11 + + Sets or clears the backend-specific configuration. + + Without a \a backendConfig parameter this function will clear the + backend-specific configuration. More information about the supported + options is available in the documentation of addBackendConfig(). + + \sa backendConfig(), setBackendConfigOption() + */ +void QSslConfiguration::setBackendConfig(const QMap &backendConfig) +{ + d->backendConfig = backendConfig; +} + /*! \since 5.3 diff --git a/src/network/ssl/qsslconfiguration.h b/src/network/ssl/qsslconfiguration.h index 1c57bebd65..b3264126dd 100644 --- a/src/network/ssl/qsslconfiguration.h +++ b/src/network/ssl/qsslconfiguration.h @@ -57,6 +57,7 @@ #define QSSLCONFIGURATION_H #include +#include #include #include #include @@ -149,6 +150,10 @@ public: QSslDiffieHellmanParameters diffieHellmanParameters() const; void setDiffieHellmanParameters(const QSslDiffieHellmanParameters &dhparams); + QMap backendConfig() const; + void setBackendConfigOption(const QByteArray &name, const QVariant &value); + void setBackendConfig(const QMap &backendConfig = QMap()); + static QSslConfiguration defaultConfiguration(); static void setDefaultConfiguration(const QSslConfiguration &configuration); diff --git a/src/network/ssl/qsslconfiguration_p.h b/src/network/ssl/qsslconfiguration_p.h index 6adf2c9b54..38a98239db 100644 --- a/src/network/ssl/qsslconfiguration_p.h +++ b/src/network/ssl/qsslconfiguration_p.h @@ -67,6 +67,7 @@ // We mean it. // +#include #include #include "qsslconfiguration.h" #include "qlist.h" @@ -123,6 +124,8 @@ public: QSslDiffieHellmanParameters dhParams; + QMap backendConfig; + QByteArray sslSession; int sslSessionTicketLifeTimeHint; diff --git a/src/network/ssl/qsslcontext_openssl.cpp b/src/network/ssl/qsslcontext_openssl.cpp index cef503710c..386c280659 100644 --- a/src/network/ssl/qsslcontext_openssl.cpp +++ b/src/network/ssl/qsslcontext_openssl.cpp @@ -49,6 +49,11 @@ QT_BEGIN_NAMESPACE +static inline QString msgErrorSettingBackendConfig(const QString &why) +{ + return QSslSocket::tr("Error when setting the OpenSSL configuration (%1)").arg(why); +} + QSslContext::QSslContext() : ctx(0), pkey(0), @@ -237,4 +242,70 @@ QString QSslContext::errorString() const return errorStr; } +// static +void QSslContext::applyBackendConfig(QSslContext *sslContext) +{ + if (sslContext->sslConfiguration.backendConfig().isEmpty()) + return; + +#if OPENSSL_VERSION_NUMBER >= 0x10002000L + if (QSslSocket::sslLibraryVersionNumber() >= 0x10002000L) { + QSharedPointer cctx(q_SSL_CONF_CTX_new(), &q_SSL_CONF_CTX_free); + if (cctx) { + q_SSL_CONF_CTX_set_ssl_ctx(cctx.data(), sslContext->ctx); + q_SSL_CONF_CTX_set_flags(cctx.data(), SSL_CONF_FLAG_FILE); + + const auto &backendConfig = sslContext->sslConfiguration.backendConfig(); + for (auto i = backendConfig.constBegin(); i != backendConfig.constEnd(); ++i) { + if (!i.value().canConvert(QMetaType::QByteArray)) { + sslContext->errorCode = QSslError::UnspecifiedError; + sslContext->errorStr = msgErrorSettingBackendConfig( + QSslSocket::tr("Expecting QByteArray for %1").arg( + QString::fromUtf8(i.key()))); + return; + } + + const QByteArray &value = i.value().toByteArray(); + const int result = q_SSL_CONF_cmd(cctx.data(), i.key().constData(), value.constData()); + if (result == 2) + continue; + + sslContext->errorCode = QSslError::UnspecifiedError; + switch (result) { + case 0: + sslContext->errorStr = msgErrorSettingBackendConfig( + QSslSocket::tr("An error occurred attempting to set %1 to %2").arg( + QString::fromUtf8(i.key()), QString::fromUtf8(value))); + return; + case 1: + sslContext->errorStr = msgErrorSettingBackendConfig( + QSslSocket::tr("Wrong value for %1 (%2)").arg( + QString::fromUtf8(i.key()), QString::fromUtf8(value))); + return; + default: + sslContext->errorStr = msgErrorSettingBackendConfig( + QSslSocket::tr("Unrecognized command %1 = %2").arg( + QString::fromUtf8(i.key()), QString::fromUtf8(value))); + return; + } + } + + if (q_SSL_CONF_CTX_finish(cctx.data()) == 0) { + sslContext->errorStr = msgErrorSettingBackendConfig(QSslSocket::tr("SSL_CONF_finish() failed")); + sslContext->errorCode = QSslError::UnspecifiedError; + } + } else { + sslContext->errorStr = msgErrorSettingBackendConfig(QSslSocket::tr("SSL_CONF_CTX_new() failed")); + sslContext->errorCode = QSslError::UnspecifiedError; + } + } else +#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L + { + // specific algorithms requested, but not possible to set + sslContext->errorCode = QSslError::UnspecifiedError; + sslContext->errorStr = msgErrorSettingBackendConfig( + QSslSocket::tr("OpenSSL version too old, need at least v1.0.2")); + } +} + QT_END_NAMESPACE diff --git a/src/network/ssl/qsslcontext_openssl11.cpp b/src/network/ssl/qsslcontext_openssl11.cpp index 787b6ae3f5..7be7be46b8 100644 --- a/src/network/ssl/qsslcontext_openssl11.cpp +++ b/src/network/ssl/qsslcontext_openssl11.cpp @@ -260,6 +260,7 @@ init_context: #ifdef OPENSSL_NO_EC sslContext->errorStr = msgErrorSettingEllipticCurves(QSslSocket::tr("OpenSSL version with disabled elliptic curves")); sslContext->errorCode = QSslError::UnspecifiedError; + return; #else // Set the curves to be used. std::vector curves; @@ -269,9 +270,12 @@ init_context: if (!q_SSL_CTX_ctrl(sslContext->ctx, SSL_CTRL_SET_CURVES, long(curves.size()), &curves[0])) { sslContext->errorStr = msgErrorSettingEllipticCurves(QSslSocketBackendPrivate::getErrorsFromOpenSsl()); sslContext->errorCode = QSslError::UnspecifiedError; + return; } #endif } + + applyBackendConfig(sslContext); } QT_END_NAMESPACE diff --git a/src/network/ssl/qsslcontext_openssl_p.h b/src/network/ssl/qsslcontext_openssl_p.h index 06a31af5e5..48beebf134 100644 --- a/src/network/ssl/qsslcontext_openssl_p.h +++ b/src/network/ssl/qsslcontext_openssl_p.h @@ -107,6 +107,7 @@ protected: private: static void initSslContext(QSslContext* sslContext, QSslSocket::SslMode mode, const QSslConfiguration &configuration, bool allowRootCertOnDemandLoading); + static void applyBackendConfig(QSslContext *sslContext); private: SSL_CTX* ctx; diff --git a/src/network/ssl/qsslcontext_opensslpre11.cpp b/src/network/ssl/qsslcontext_opensslpre11.cpp index 9c01c2f2dc..eea821804f 100644 --- a/src/network/ssl/qsslcontext_opensslpre11.cpp +++ b/src/network/ssl/qsslcontext_opensslpre11.cpp @@ -340,6 +340,7 @@ init_context: const_cast(reinterpret_cast(qcurves.data())))) { sslContext->errorStr = msgErrorSettingEllipticCurves(QSslSocketBackendPrivate::getErrorsFromOpenSsl()); sslContext->errorCode = QSslError::UnspecifiedError; + return; } } else #endif // OPENSSL_VERSION_NUMBER >= 0x10002000L && !defined(OPENSSL_NO_EC) @@ -347,8 +348,11 @@ init_context: // specific curves requested, but not possible to set -> error sslContext->errorStr = msgErrorSettingEllipticCurves(QSslSocket::tr("OpenSSL version too old, need at least v1.0.2")); sslContext->errorCode = QSslError::UnspecifiedError; + return; } } + + applyBackendConfig(sslContext); } QT_END_NAMESPACE diff --git a/src/network/ssl/qsslsocket.cpp b/src/network/ssl/qsslsocket.cpp index 9f07b53e4b..833d676192 100644 --- a/src/network/ssl/qsslsocket.cpp +++ b/src/network/ssl/qsslsocket.cpp @@ -922,6 +922,7 @@ void QSslSocket::setSslConfiguration(const QSslConfiguration &configuration) d->configuration.peerVerifyDepth = configuration.peerVerifyDepth(); d->configuration.peerVerifyMode = configuration.peerVerifyMode(); d->configuration.protocol = configuration.protocol(); + d->configuration.backendConfig = configuration.backendConfig(); d->configuration.sslOptions = configuration.d->sslOptions; d->configuration.sslSession = configuration.sessionTicket(); d->configuration.sslSessionTicketLifeTimeHint = configuration.sessionTicketLifeTimeHint(); @@ -2256,6 +2257,7 @@ void QSslConfigurationPrivate::deepCopyDefaultConfiguration(QSslConfigurationPri ptr->peerVerifyDepth = global->peerVerifyDepth; ptr->sslOptions = global->sslOptions; ptr->ellipticCurves = global->ellipticCurves; + ptr->backendConfig = global->backendConfig; } /*! diff --git a/src/network/ssl/qsslsocket_openssl_symbols.cpp b/src/network/ssl/qsslsocket_openssl_symbols.cpp index 1b73135935..9bb67771fd 100644 --- a/src/network/ssl/qsslsocket_openssl_symbols.cpp +++ b/src/network/ssl/qsslsocket_openssl_symbols.cpp @@ -402,6 +402,14 @@ DEFINEFUNC2(int, SSL_CTX_use_PrivateKey, SSL_CTX *a, a, EVP_PKEY *b, b, return - DEFINEFUNC2(int, SSL_CTX_use_RSAPrivateKey, SSL_CTX *a, a, RSA *b, b, return -1, return) DEFINEFUNC3(int, SSL_CTX_use_PrivateKey_file, SSL_CTX *a, a, const char *b, b, int c, c, return -1, return) DEFINEFUNC(X509_STORE *, SSL_CTX_get_cert_store, const SSL_CTX *a, a, return 0, return) +#if OPENSSL_VERSION_NUMBER >= 0x10002000L +DEFINEFUNC(SSL_CONF_CTX *, SSL_CONF_CTX_new, DUMMYARG, DUMMYARG, return 0, return); +DEFINEFUNC(void, SSL_CONF_CTX_free, SSL_CONF_CTX *a, a, return ,return); +DEFINEFUNC2(void, SSL_CONF_CTX_set_ssl_ctx, SSL_CONF_CTX *a, a, SSL_CTX *b, b, return, return); +DEFINEFUNC2(unsigned int, SSL_CONF_CTX_set_flags, SSL_CONF_CTX *a, a, unsigned int b, b, return 0, return); +DEFINEFUNC(int, SSL_CONF_CTX_finish, SSL_CONF_CTX *a, a, return 0, return); +DEFINEFUNC3(int, SSL_CONF_cmd, SSL_CONF_CTX *a, a, const char *b, b, const char *c, c, return 0, return); +#endif DEFINEFUNC(void, SSL_free, SSL *a, a, return, DUMMYARG) DEFINEFUNC(STACK_OF(SSL_CIPHER) *, SSL_get_ciphers, const SSL *a, a, return 0, return) #if OPENSSL_VERSION_NUMBER >= 0x10000000L @@ -1105,6 +1113,14 @@ bool q_resolveOpenSslSymbols() RESOLVEFUNC(SSL_CTX_use_RSAPrivateKey) RESOLVEFUNC(SSL_CTX_use_PrivateKey_file) RESOLVEFUNC(SSL_CTX_get_cert_store); +#if OPENSSL_VERSION_NUMBER >= 0x10002000L + RESOLVEFUNC(SSL_CONF_CTX_new); + RESOLVEFUNC(SSL_CONF_CTX_free); + RESOLVEFUNC(SSL_CONF_CTX_set_ssl_ctx); + RESOLVEFUNC(SSL_CONF_CTX_set_flags); + RESOLVEFUNC(SSL_CONF_CTX_finish); + RESOLVEFUNC(SSL_CONF_cmd); +#endif RESOLVEFUNC(SSL_accept) RESOLVEFUNC(SSL_clear) RESOLVEFUNC(SSL_connect) diff --git a/src/network/ssl/qsslsocket_openssl_symbols_p.h b/src/network/ssl/qsslsocket_openssl_symbols_p.h index 4cad0231cd..be67f38b64 100644 --- a/src/network/ssl/qsslsocket_openssl_symbols_p.h +++ b/src/network/ssl/qsslsocket_openssl_symbols_p.h @@ -352,6 +352,14 @@ int q_SSL_CTX_use_PrivateKey(SSL_CTX *a, EVP_PKEY *b); int q_SSL_CTX_use_RSAPrivateKey(SSL_CTX *a, RSA *b); int q_SSL_CTX_use_PrivateKey_file(SSL_CTX *a, const char *b, int c); X509_STORE *q_SSL_CTX_get_cert_store(const SSL_CTX *a); +#if OPENSSL_VERSION_NUMBER >= 0x10002000L +SSL_CONF_CTX *q_SSL_CONF_CTX_new(); +void q_SSL_CONF_CTX_free(SSL_CONF_CTX *a); +void q_SSL_CONF_CTX_set_ssl_ctx(SSL_CONF_CTX *a, SSL_CTX *b); +unsigned int q_SSL_CONF_CTX_set_flags(SSL_CONF_CTX *a, unsigned int b); +int q_SSL_CONF_CTX_finish(SSL_CONF_CTX *a); +int q_SSL_CONF_cmd(SSL_CONF_CTX *a, const char *b, const char *c); +#endif void q_SSL_free(SSL *a); STACK_OF(SSL_CIPHER) *q_SSL_get_ciphers(const SSL *a); #if OPENSSL_VERSION_NUMBER >= 0x10000000L -- cgit v1.2.3 From 0901d4290f6e7f0d1650b14fc085ecde7fb595ff Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Mon, 15 Jan 2018 15:32:45 +0100 Subject: QFileSystemModel/Win: Fix file system operations failing due to watchers File system operations like renaming/removing may fail on Windows when file system watchers are present. Add functions to QFileSystemModelPrivate to temporarily remove the watchers prior to such operations and to restore them in case of failure. Use them for rename/remove (within a feature check for QFileSystemWatcher and Q_OS_WIN). Task-number: QTBUG-65683 Change-Id: I90142901892fbf9b1e1206a3397a95ffd3c8f010 Reviewed-by: Edward Welbourne Reviewed-by: Shawn Rutledge --- src/widgets/dialogs/qfileinfogatherer_p.h | 7 +++ src/widgets/dialogs/qfilesystemmodel.cpp | 93 ++++++++++++++++++++++++++----- src/widgets/dialogs/qfilesystemmodel_p.h | 8 ++- 3 files changed, 91 insertions(+), 17 deletions(-) (limited to 'src') diff --git a/src/widgets/dialogs/qfileinfogatherer_p.h b/src/widgets/dialogs/qfileinfogatherer_p.h index bb13d4eb01..cc82f42850 100644 --- a/src/widgets/dialogs/qfileinfogatherer_p.h +++ b/src/widgets/dialogs/qfileinfogatherer_p.h @@ -167,6 +167,13 @@ public: explicit QFileInfoGatherer(QObject *parent = 0); ~QFileInfoGatherer(); +#if QT_CONFIG(filesystemwatcher) && defined(Q_OS_WIN) + QStringList watchedFiles() const { return watcher->files(); } + QStringList watchedDirectories() const { return watcher->directories(); } + void watchPaths(const QStringList &paths) { watcher->addPaths(paths); } + void unwatchPaths(const QStringList &paths) { watcher->removePaths(paths); } +#endif // filesystemwatcher && Q_OS_WIN + // only callable from this->thread(): void clear(); void removePath(const QString &path); diff --git a/src/widgets/dialogs/qfilesystemmodel.cpp b/src/widgets/dialogs/qfilesystemmodel.cpp index 14ac6ad31b..33b8b51216 100644 --- a/src/widgets/dialogs/qfilesystemmodel.cpp +++ b/src/widgets/dialogs/qfilesystemmodel.cpp @@ -207,14 +207,17 @@ bool QFileSystemModel::remove(const QModelIndex &aindex) const QString path = d->filePath(aindex); const QFileInfo fileInfo(path); +#if QT_CONFIG(filesystemwatcher) && defined(Q_OS_WIN) + // QTBUG-65683: Remove file system watchers prior to deletion to prevent + // failure due to locked files on Windows. + const QStringList watchedPaths = d->unwatchPathsAt(aindex); +#endif // filesystemwatcher && Q_OS_WIN const bool success = (fileInfo.isFile() || fileInfo.isSymLink()) ? QFile::remove(path) : QDir(path).removeRecursively(); -#ifndef QT_NO_FILESYSTEMWATCHER - if (success) { - QFileSystemModelPrivate * d = const_cast(d_func()); - d->fileInfoGatherer.removePath(path); - } -#endif +#if QT_CONFIG(filesystemwatcher) && defined(Q_OS_WIN) + if (!success) + d->watchPaths(watchedPaths); +#endif // filesystemwatcher && Q_OS_WIN return success; } @@ -851,6 +854,20 @@ QIcon QFileSystemModelPrivate::icon(const QModelIndex &index) const return node(index)->icon(); } +static void displayRenameFailedMessage(const QString &newName) +{ +#if QT_CONFIG(messagebox) + const QString message = + QFileSystemModel::tr("The name \"%1\" cannot be used." + "

Try using another name, with fewer characters or no punctuation marks.") + .arg(newName); + QMessageBox::information(nullptr, QFileSystemModel::tr("Invalid filename"), + message, QMessageBox::Ok); +#else + Q_UNUSED(newName) +#endif // QT_CONFIG(messagebox) +} + /*! \reimp */ @@ -871,15 +888,21 @@ bool QFileSystemModel::setData(const QModelIndex &idx, const QVariant &value, in const QString parentPath = filePath(parent(idx)); - if (newName.isEmpty() - || QDir::toNativeSeparators(newName).contains(QDir::separator()) - || !QDir(parentPath).rename(oldName, newName)) { -#if QT_CONFIG(messagebox) - QMessageBox::information(0, QFileSystemModel::tr("Invalid filename"), - QFileSystemModel::tr("The name \"%1\" can not be used.

Try using another name, with fewer characters or no punctuations marks.") - .arg(newName), - QMessageBox::Ok); -#endif // QT_CONFIG(messagebox) + if (newName.isEmpty() || QDir::toNativeSeparators(newName).contains(QDir::separator())) { + displayRenameFailedMessage(newName); + return false; + } + +#if QT_CONFIG(filesystemwatcher) && defined(Q_OS_WIN) + // QTBUG-65683: Remove file system watchers prior to renaming to prevent + // failure due to locked files on Windows. + const QStringList watchedPaths = d->unwatchPathsAt(idx); +#endif // filesystemwatcher && Q_OS_WIN + if (!QDir(parentPath).rename(oldName, newName)) { +#if QT_CONFIG(filesystemwatcher) && defined(Q_OS_WIN) + d->watchPaths(watchedPaths); +#endif + displayRenameFailedMessage(newName); return false; } else { /* @@ -1882,6 +1905,46 @@ void QFileSystemModelPrivate::_q_resolvedName(const QString &fileName, const QSt resolvedSymLinks[fileName] = resolvedName; } +#if QT_CONFIG(filesystemwatcher) && defined(Q_OS_WIN) +// Remove file system watchers at/below the index and return a list of previously +// watched files. This should be called prior to operations like rename/remove +// which might fail due to watchers on platforms like Windows. The watchers +// should be restored on failure. +QStringList QFileSystemModelPrivate::unwatchPathsAt(const QModelIndex &index) +{ + const QFileSystemModelPrivate::QFileSystemNode *indexNode = node(index); + if (indexNode == nullptr) + return QStringList(); + const Qt::CaseSensitivity caseSensitivity = indexNode->caseSensitive() + ? Qt::CaseSensitive : Qt::CaseInsensitive; + const QString path = indexNode->fileInfo().absoluteFilePath(); + + QStringList result; + const auto filter = [path, caseSensitivity] (const QString &watchedPath) + { + const int pathSize = path.size(); + if (pathSize == watchedPath.size()) { + return path.compare(watchedPath, caseSensitivity) == 0; + } else if (watchedPath.size() > pathSize) { + return watchedPath.at(pathSize) == QLatin1Char('/') + && watchedPath.startsWith(path, caseSensitivity); + } + return false; + }; + + const QStringList &watchedFiles = fileInfoGatherer.watchedFiles(); + std::copy_if(watchedFiles.cbegin(), watchedFiles.cend(), + std::back_inserter(result), filter); + + const QStringList &watchedDirectories = fileInfoGatherer.watchedDirectories(); + std::copy_if(watchedDirectories.cbegin(), watchedDirectories.cend(), + std::back_inserter(result), filter); + + fileInfoGatherer.unwatchPaths(result); + return result; +} +#endif // filesystemwatcher && Q_OS_WIN + /*! \internal */ diff --git a/src/widgets/dialogs/qfilesystemmodel_p.h b/src/widgets/dialogs/qfilesystemmodel_p.h index e8bae4f659..a2a02e2d41 100644 --- a/src/widgets/dialogs/qfilesystemmodel_p.h +++ b/src/widgets/dialogs/qfilesystemmodel_p.h @@ -297,9 +297,13 @@ public: static int naturalCompare(const QString &s1, const QString &s2, Qt::CaseSensitivity cs); QDir rootDir; -#ifndef QT_NO_FILESYSTEMWATCHER +#if QT_CONFIG(filesystemwatcher) +# ifdef Q_OS_WIN + QStringList unwatchPathsAt(const QModelIndex &); + void watchPaths(const QStringList &paths) { fileInfoGatherer.watchPaths(paths); } +# endif // Q_OS_WIN QFileInfoGatherer fileInfoGatherer; -#endif +#endif // filesystemwatcher QTimer delayedSortTimer; bool forceSort; int sortColumn; -- cgit v1.2.3 From 657894624521b580f59ff5f58b9c0e9be159dc1c Mon Sep 17 00:00:00 2001 From: Christian Ehrlicher Date: Wed, 20 Dec 2017 21:18:54 +0100 Subject: QListWidgetItem/QStandardItem: pass role to dataChanged() signal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit QAbstractItemModel::dataChanged() gained an optional role parameter with Qt5 which was not filled within QListWidgetItem/QStandardItem setData() functions Task-number: QTBUG-55903 Task-number: QTBUG-63766 Change-Id: I4da9346ef8401cc8633dc4b2ea7d00451d1e3942 Reviewed-by: Luca Beldi Reviewed-by: Thorbjørn Lund Martsum --- src/gui/itemmodels/qstandarditemmodel.cpp | 33 ++++++++++++++++++++++++------- src/gui/itemmodels/qstandarditemmodel.h | 1 + src/gui/itemmodels/qstandarditemmodel_p.h | 2 +- src/widgets/itemviews/qlistwidget.cpp | 17 ++++++++++------ src/widgets/itemviews/qlistwidget.h | 1 + src/widgets/itemviews/qlistwidget_p.h | 2 +- 6 files changed, 41 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/gui/itemmodels/qstandarditemmodel.cpp b/src/gui/itemmodels/qstandarditemmodel.cpp index 519995e82a..d1e0604caf 100644 --- a/src/gui/itemmodels/qstandarditemmodel.cpp +++ b/src/gui/itemmodels/qstandarditemmodel.cpp @@ -278,8 +278,24 @@ void QStandardItemPrivate::setItemData(const QMap &roles) if (newValues != values) { values.swap(newValues); - if (model) - model->d_func()->itemChanged(q); + if (model) { + QVector roleKeys; + roleKeys.reserve(roles.size() + 1); + bool hasEditRole = false; + bool hasDisplayRole = false; + for (auto it = roles.keyBegin(); it != roles.keyEnd(); ++it) { + roleKeys.push_back(*it); + if (*it == Qt::EditRole) + hasEditRole = true; + else if (*it == Qt::DisplayRole) + hasDisplayRole = true; + } + if (hasEditRole && !hasDisplayRole) + roleKeys.push_back(Qt::DisplayRole); + else if (!hasEditRole && hasDisplayRole) + roleKeys.push_back(Qt::EditRole); + model->d_func()->itemChanged(q, roleKeys); + } } } @@ -554,7 +570,7 @@ bool QStandardItemPrivate::insertColumns(int column, int count, const QList &roles) { Q_Q(QStandardItemModel); Q_ASSERT(item); @@ -570,8 +586,8 @@ void QStandardItemModelPrivate::itemChanged(QStandardItem *item) } } else { // Normal item - QModelIndex index = q->indexFromItem(item); - emit q->dataChanged(index, index); + const QModelIndex index = q->indexFromItem(item); + emit q->dataChanged(index, index, roles); } } @@ -885,6 +901,9 @@ void QStandardItem::setData(const QVariant &value, int role) { Q_D(QStandardItem); role = (role == Qt::EditRole) ? Qt::DisplayRole : role; + const QVector roles((role == Qt::DisplayRole) ? + QVector({Qt::DisplayRole, Qt::EditRole}) : + QVector({role})); QVector::iterator it; for (it = d->values.begin(); it != d->values.end(); ++it) { if ((*it).role == role) { @@ -896,13 +915,13 @@ void QStandardItem::setData(const QVariant &value, int role) d->values.erase(it); } if (d->model) - d->model->d_func()->itemChanged(this); + d->model->d_func()->itemChanged(this, roles); return; } } d->values.append(QStandardItemData(role, value)); if (d->model) - d->model->d_func()->itemChanged(this); + d->model->d_func()->itemChanged(this, roles); } /*! diff --git a/src/gui/itemmodels/qstandarditemmodel.h b/src/gui/itemmodels/qstandarditemmodel.h index c54e7b27d9..d8f06b629a 100644 --- a/src/gui/itemmodels/qstandarditemmodel.h +++ b/src/gui/itemmodels/qstandarditemmodel.h @@ -419,6 +419,7 @@ public: bool dropMimeData (const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override; Q_SIGNALS: + // ### Qt 6: add changed roles void itemChanged(QStandardItem *item); protected: diff --git a/src/gui/itemmodels/qstandarditemmodel_p.h b/src/gui/itemmodels/qstandarditemmodel_p.h index caee3ea34c..bd28ec3029 100644 --- a/src/gui/itemmodels/qstandarditemmodel_p.h +++ b/src/gui/itemmodels/qstandarditemmodel_p.h @@ -200,7 +200,7 @@ public: } void sort(QStandardItem *parent, int column, Qt::SortOrder order); - void itemChanged(QStandardItem *item); + void itemChanged(QStandardItem *item, const QVector &roles = QVector()); void rowsAboutToBeInserted(QStandardItem *parent, int start, int end); void columnsAboutToBeInserted(QStandardItem *parent, int start, int end); void rowsAboutToBeRemoved(QStandardItem *parent, int start, int end); diff --git a/src/widgets/itemviews/qlistwidget.cpp b/src/widgets/itemviews/qlistwidget.cpp index ad8aac1415..1fedad80aa 100644 --- a/src/widgets/itemviews/qlistwidget.cpp +++ b/src/widgets/itemviews/qlistwidget.cpp @@ -412,10 +412,10 @@ QList::iterator QListModel::sortedInsertionIterator( return std::lower_bound(begin, end, item, QListModelGreaterThan()); } -void QListModel::itemChanged(QListWidgetItem *item) +void QListModel::itemChanged(QListWidgetItem *item, const QVector &roles) { - QModelIndex idx = index(item); - emit dataChanged(idx, idx); + const QModelIndex idx = index(item); + emit dataChanged(idx, idx, roles); } QStringList QListModel::mimeTypes() const @@ -711,8 +711,12 @@ void QListWidgetItem::setData(int role, const QVariant &value) } if (!found) d->values.append(QWidgetItemData(role, value)); - if (QListModel *model = (view ? qobject_cast(view->model()) : 0)) - model->itemChanged(this); + if (QListModel *model = (view ? qobject_cast(view->model()) : nullptr)) { + const QVector roles((role == Qt::DisplayRole) ? + QVector({Qt::DisplayRole, Qt::EditRole}) : + QVector({role})); + model->itemChanged(this, roles); + } } /*! @@ -954,7 +958,8 @@ QDataStream &operator>>(QDataStream &in, QListWidgetItem &item) \sa Qt::ItemFlags */ -void QListWidgetItem::setFlags(Qt::ItemFlags aflags) { +void QListWidgetItem::setFlags(Qt::ItemFlags aflags) +{ itemFlags = aflags; if (QListModel *model = (view ? qobject_cast(view->model()) : 0)) model->itemChanged(this); diff --git a/src/widgets/itemviews/qlistwidget.h b/src/widgets/itemviews/qlistwidget.h index 3caa5ce6f2..8a31411429 100644 --- a/src/widgets/itemviews/qlistwidget.h +++ b/src/widgets/itemviews/qlistwidget.h @@ -269,6 +269,7 @@ Q_SIGNALS: void itemDoubleClicked(QListWidgetItem *item); void itemActivated(QListWidgetItem *item); void itemEntered(QListWidgetItem *item); + // ### Qt 6: add changed roles void itemChanged(QListWidgetItem *item); void currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous); diff --git a/src/widgets/itemviews/qlistwidget_p.h b/src/widgets/itemviews/qlistwidget_p.h index 492b05ff07..30b5016db6 100644 --- a/src/widgets/itemviews/qlistwidget_p.h +++ b/src/widgets/itemviews/qlistwidget_p.h @@ -119,7 +119,7 @@ public: const QList::iterator &end, Qt::SortOrder order, QListWidgetItem *item); - void itemChanged(QListWidgetItem *item); + void itemChanged(QListWidgetItem *item, const QVector &roles = QVector()); // dnd QStringList mimeTypes() const override; -- cgit v1.2.3 From a6b697ca13945a174cff9f3e9b1af1cf61c0bea5 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Tue, 12 Dec 2017 18:32:19 -0800 Subject: Create corelib/serialization and move existing file formats into it This is in preparation to adding CBOR support. We don't need yet another dir for CBOR and placing it in src/corelib/json is just wrong. Change-Id: I9741f017961b410c910dfffd14ffb9d870340fa6 Reviewed-by: Oswald Buddenhagen Reviewed-by: Lars Knoll --- src/corelib/corelib.pro | 3 +- src/corelib/io/io.pri | 6 - src/corelib/io/qdatastream.cpp | 1400 --------- src/corelib/io/qdatastream.h | 466 --- src/corelib/io/qdatastream_p.h | 73 - src/corelib/io/qtextstream.cpp | 3192 -------------------- src/corelib/io/qtextstream.h | 287 -- src/corelib/io/qtextstream_p.h | 200 -- src/corelib/json/json.pri | 17 - src/corelib/json/qjson.cpp | 455 --- src/corelib/json/qjson_p.h | 781 ----- src/corelib/json/qjsonarray.cpp | 1258 -------- src/corelib/json/qjsonarray.h | 274 -- src/corelib/json/qjsondocument.cpp | 667 ----- src/corelib/json/qjsondocument.h | 177 -- src/corelib/json/qjsonobject.cpp | 1312 --------- src/corelib/json/qjsonobject.h | 271 -- src/corelib/json/qjsonparser.cpp | 1027 ------- src/corelib/json/qjsonparser_p.h | 128 - src/corelib/json/qjsonvalue.cpp | 863 ------ src/corelib/json/qjsonvalue.h | 255 -- src/corelib/json/qjsonwriter.cpp | 231 -- src/corelib/json/qjsonwriter_p.h | 73 - src/corelib/serialization/.gitignore | 1 + src/corelib/serialization/make-xml-parser.sh | 51 + src/corelib/serialization/qdatastream.cpp | 1400 +++++++++ src/corelib/serialization/qdatastream.h | 466 +++ src/corelib/serialization/qdatastream_p.h | 73 + src/corelib/serialization/qjson.cpp | 455 +++ src/corelib/serialization/qjson_p.h | 781 +++++ src/corelib/serialization/qjsonarray.cpp | 1258 ++++++++ src/corelib/serialization/qjsonarray.h | 274 ++ src/corelib/serialization/qjsondocument.cpp | 667 +++++ src/corelib/serialization/qjsondocument.h | 177 ++ src/corelib/serialization/qjsonobject.cpp | 1312 +++++++++ src/corelib/serialization/qjsonobject.h | 271 ++ src/corelib/serialization/qjsonparser.cpp | 1027 +++++++ src/corelib/serialization/qjsonparser_p.h | 128 + src/corelib/serialization/qjsonvalue.cpp | 863 ++++++ src/corelib/serialization/qjsonvalue.h | 255 ++ src/corelib/serialization/qjsonwriter.cpp | 231 ++ src/corelib/serialization/qjsonwriter_p.h | 73 + src/corelib/serialization/qtextstream.cpp | 3192 ++++++++++++++++++++ src/corelib/serialization/qtextstream.h | 287 ++ src/corelib/serialization/qtextstream_p.h | 200 ++ src/corelib/serialization/qxmlstream.cpp | 4014 ++++++++++++++++++++++++++ src/corelib/serialization/qxmlstream.g | 1852 ++++++++++++ src/corelib/serialization/qxmlstream.h | 540 ++++ src/corelib/serialization/qxmlstream_p.h | 1972 +++++++++++++ src/corelib/serialization/qxmlutils.cpp | 390 +++ src/corelib/serialization/qxmlutils_p.h | 90 + src/corelib/serialization/serialization.pri | 30 + src/corelib/xml/.gitignore | 1 - src/corelib/xml/make-parser.sh | 51 - src/corelib/xml/qxmlstream.cpp | 4014 -------------------------- src/corelib/xml/qxmlstream.g | 1852 ------------ src/corelib/xml/qxmlstream.h | 540 ---- src/corelib/xml/qxmlstream_p.h | 1972 ------------- src/corelib/xml/qxmlutils.cpp | 390 --- src/corelib/xml/qxmlutils_p.h | 90 - src/corelib/xml/xml.pri | 10 - src/tools/bootstrap/bootstrap.pro | 22 +- 62 files changed, 22342 insertions(+), 22346 deletions(-) delete mode 100644 src/corelib/io/qdatastream.cpp delete mode 100644 src/corelib/io/qdatastream.h delete mode 100644 src/corelib/io/qdatastream_p.h delete mode 100644 src/corelib/io/qtextstream.cpp delete mode 100644 src/corelib/io/qtextstream.h delete mode 100644 src/corelib/io/qtextstream_p.h delete mode 100644 src/corelib/json/json.pri delete mode 100644 src/corelib/json/qjson.cpp delete mode 100644 src/corelib/json/qjson_p.h delete mode 100644 src/corelib/json/qjsonarray.cpp delete mode 100644 src/corelib/json/qjsonarray.h delete mode 100644 src/corelib/json/qjsondocument.cpp delete mode 100644 src/corelib/json/qjsondocument.h delete mode 100644 src/corelib/json/qjsonobject.cpp delete mode 100644 src/corelib/json/qjsonobject.h delete mode 100644 src/corelib/json/qjsonparser.cpp delete mode 100644 src/corelib/json/qjsonparser_p.h delete mode 100644 src/corelib/json/qjsonvalue.cpp delete mode 100644 src/corelib/json/qjsonvalue.h delete mode 100644 src/corelib/json/qjsonwriter.cpp delete mode 100644 src/corelib/json/qjsonwriter_p.h create mode 100644 src/corelib/serialization/.gitignore create mode 100755 src/corelib/serialization/make-xml-parser.sh create mode 100644 src/corelib/serialization/qdatastream.cpp create mode 100644 src/corelib/serialization/qdatastream.h create mode 100644 src/corelib/serialization/qdatastream_p.h create mode 100644 src/corelib/serialization/qjson.cpp create mode 100644 src/corelib/serialization/qjson_p.h create mode 100644 src/corelib/serialization/qjsonarray.cpp create mode 100644 src/corelib/serialization/qjsonarray.h create mode 100644 src/corelib/serialization/qjsondocument.cpp create mode 100644 src/corelib/serialization/qjsondocument.h create mode 100644 src/corelib/serialization/qjsonobject.cpp create mode 100644 src/corelib/serialization/qjsonobject.h create mode 100644 src/corelib/serialization/qjsonparser.cpp create mode 100644 src/corelib/serialization/qjsonparser_p.h create mode 100644 src/corelib/serialization/qjsonvalue.cpp create mode 100644 src/corelib/serialization/qjsonvalue.h create mode 100644 src/corelib/serialization/qjsonwriter.cpp create mode 100644 src/corelib/serialization/qjsonwriter_p.h create mode 100644 src/corelib/serialization/qtextstream.cpp create mode 100644 src/corelib/serialization/qtextstream.h create mode 100644 src/corelib/serialization/qtextstream_p.h create mode 100644 src/corelib/serialization/qxmlstream.cpp create mode 100644 src/corelib/serialization/qxmlstream.g create mode 100644 src/corelib/serialization/qxmlstream.h create mode 100644 src/corelib/serialization/qxmlstream_p.h create mode 100644 src/corelib/serialization/qxmlutils.cpp create mode 100644 src/corelib/serialization/qxmlutils_p.h create mode 100644 src/corelib/serialization/serialization.pri delete mode 100644 src/corelib/xml/.gitignore delete mode 100755 src/corelib/xml/make-parser.sh delete mode 100644 src/corelib/xml/qxmlstream.cpp delete mode 100644 src/corelib/xml/qxmlstream.g delete mode 100644 src/corelib/xml/qxmlstream.h delete mode 100644 src/corelib/xml/qxmlstream_p.h delete mode 100644 src/corelib/xml/qxmlutils.cpp delete mode 100644 src/corelib/xml/qxmlutils_p.h delete mode 100644 src/corelib/xml/xml.pri (limited to 'src') diff --git a/src/corelib/corelib.pro b/src/corelib/corelib.pro index 6dc4af1bfe..4cce7c0df3 100644 --- a/src/corelib/corelib.pro +++ b/src/corelib/corelib.pro @@ -35,13 +35,12 @@ include(thread/thread.pri) include(tools/tools.pri) include(io/io.pri) include(itemmodels/itemmodels.pri) -include(json/json.pri) include(plugin/plugin.pri) include(kernel/kernel.pri) include(codecs/codecs.pri) +include(serialization/serialization.pri) include(statemachine/statemachine.pri) include(mimetypes/mimetypes.pri) -include(xml/xml.pri) win32 { LIBS_PRIVATE += -lws2_32 diff --git a/src/corelib/io/io.pri b/src/corelib/io/io.pri index 4614fe2a6b..c6a5973306 100644 --- a/src/corelib/io/io.pri +++ b/src/corelib/io/io.pri @@ -3,8 +3,6 @@ HEADERS += \ io/qabstractfileengine_p.h \ io/qbuffer.h \ - io/qdatastream.h \ - io/qdatastream_p.h \ io/qdataurl_p.h \ io/qdebug.h \ io/qdebug_p.h \ @@ -22,8 +20,6 @@ HEADERS += \ io/qlockfile.h \ io/qlockfile_p.h \ io/qnoncontiguousbytedevice_p.h \ - io/qtextstream.h \ - io/qtextstream_p.h \ io/qtemporarydir.h \ io/qtemporaryfile.h \ io/qtemporaryfile_p.h \ @@ -57,7 +53,6 @@ HEADERS += \ SOURCES += \ io/qabstractfileengine.cpp \ io/qbuffer.cpp \ - io/qdatastream.cpp \ io/qdataurl.cpp \ io/qtldurl.cpp \ io/qdebug.cpp \ @@ -71,7 +66,6 @@ SOURCES += \ io/qlockfile.cpp \ io/qnoncontiguousbytedevice.cpp \ io/qstorageinfo.cpp \ - io/qtextstream.cpp \ io/qtemporarydir.cpp \ io/qtemporaryfile.cpp \ io/qresource.cpp \ diff --git a/src/corelib/io/qdatastream.cpp b/src/corelib/io/qdatastream.cpp deleted file mode 100644 index 8f419a4a46..0000000000 --- a/src/corelib/io/qdatastream.cpp +++ /dev/null @@ -1,1400 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtCore module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qdatastream.h" -#include "qdatastream_p.h" - -#if !defined(QT_NO_DATASTREAM) || defined(QT_BOOTSTRAPPED) -#include "qbuffer.h" -#include "qfloat16.h" -#include "qstring.h" -#include -#include -#include -#include "qendian.h" - -QT_BEGIN_NAMESPACE - -/*! - \class QDataStream - \inmodule QtCore - \reentrant - \brief The QDataStream class provides serialization of binary data - to a QIODevice. - - \ingroup io - - - A data stream is a binary stream of encoded information which is - 100% independent of the host computer's operating system, CPU or - byte order. For example, a data stream that is written by a PC - under Windows can be read by a Sun SPARC running Solaris. - - You can also use a data stream to read/write \l{raw}{raw - unencoded binary data}. If you want a "parsing" input stream, see - QTextStream. - - The QDataStream class implements the serialization of C++'s basic - data types, like \c char, \c short, \c int, \c{char *}, etc. - Serialization of more complex data is accomplished by breaking up - the data into primitive units. - - A data stream cooperates closely with a QIODevice. A QIODevice - represents an input/output medium one can read data from and write - data to. The QFile class is an example of an I/O device. - - Example (write binary data to a stream): - - \snippet code/src_corelib_io_qdatastream.cpp 0 - - Example (read binary data from a stream): - - \snippet code/src_corelib_io_qdatastream.cpp 1 - - Each item written to the stream is written in a predefined binary - format that varies depending on the item's type. Supported Qt - types include QBrush, QColor, QDateTime, QFont, QPixmap, QString, - QVariant and many others. For the complete list of all Qt types - supporting data streaming see \l{Serializing Qt Data Types}. - - For integers it is best to always cast to a Qt integer type for - writing, and to read back into the same Qt integer type. This - ensures that you get integers of the size you want and insulates - you from compiler and platform differences. - - To take one example, a \c{char *} string is written as a 32-bit - integer equal to the length of the string including the '\\0' byte, - followed by all the characters of the string including the - '\\0' byte. When reading a \c{char *} string, 4 bytes are read to - create the 32-bit length value, then that many characters for the - \c {char *} string including the '\\0' terminator are read. - - The initial I/O device is usually set in the constructor, but can be - changed with setDevice(). If you've reached the end of the data - (or if there is no I/O device set) atEnd() will return true. - - \section1 Versioning - - QDataStream's binary format has evolved since Qt 1.0, and is - likely to continue evolving to reflect changes done in Qt. When - inputting or outputting complex types, it's very important to - make sure that the same version of the stream (version()) is used - for reading and writing. If you need both forward and backward - compatibility, you can hardcode the version number in the - application: - - \snippet code/src_corelib_io_qdatastream.cpp 2 - - If you are producing a new binary data format, such as a file - format for documents created by your application, you could use a - QDataStream to write the data in a portable format. Typically, you - would write a brief header containing a magic string and a version - number to give yourself room for future expansion. For example: - - \snippet code/src_corelib_io_qdatastream.cpp 3 - - Then read it in with: - - \snippet code/src_corelib_io_qdatastream.cpp 4 - - You can select which byte order to use when serializing data. The - default setting is big endian (MSB first). Changing it to little - endian breaks the portability (unless the reader also changes to - little endian). We recommend keeping this setting unless you have - special requirements. - - \target raw - \section1 Reading and Writing Raw Binary Data - - You may wish to read/write your own raw binary data to/from the - data stream directly. Data may be read from the stream into a - preallocated \c{char *} using readRawData(). Similarly data can be - written to the stream using writeRawData(). Note that any - encoding/decoding of the data must be done by you. - - A similar pair of functions is readBytes() and writeBytes(). These - differ from their \e raw counterparts as follows: readBytes() - reads a quint32 which is taken to be the length of the data to be - read, then that number of bytes is read into the preallocated - \c{char *}; writeBytes() writes a quint32 containing the length of the - data, followed by the data. Note that any encoding/decoding of - the data (apart from the length quint32) must be done by you. - - \section1 Reading and Writing Qt Collection Classes - - The Qt container classes can also be serialized to a QDataStream. - These include QList, QLinkedList, QVector, QSet, QHash, and QMap. - The stream operators are declared as non-members of the classes. - - \target Serializing Qt Classes - \section1 Reading and Writing Other Qt Classes - - In addition to the overloaded stream operators documented here, - any Qt classes that you might want to serialize to a QDataStream - will have appropriate stream operators declared as non-member of - the class: - - \code - QDataStream &operator<<(QDataStream &, const QXxx &); - QDataStream &operator>>(QDataStream &, QXxx &); - \endcode - - For example, here are the stream operators declared as non-members - of the QImage class: - - \code - QDataStream & operator<< (QDataStream& stream, const QImage& image); - QDataStream & operator>> (QDataStream& stream, QImage& image); - \endcode - - To see if your favorite Qt class has similar stream operators - defined, check the \b {Related Non-Members} section of the - class's documentation page. - - \section1 Using Read Transactions - - When a data stream operates on an asynchronous device, the chunks of data - can arrive at arbitrary points in time. The QDataStream class implements - a transaction mechanism that provides the ability to read the data - atomically with a series of stream operators. As an example, you can - handle incomplete reads from a socket by using a transaction in a slot - connected to the readyRead() signal: - - \snippet code/src_corelib_io_qdatastream.cpp 6 - - If no full packet is received, this code restores the stream to the - initial position, after which you need to wait for more data to arrive. - - \sa QTextStream, QVariant -*/ - -/*! - \enum QDataStream::ByteOrder - - The byte order used for reading/writing the data. - - \value BigEndian Most significant byte first (the default) - \value LittleEndian Least significant byte first -*/ - -/*! - \enum QDataStream::FloatingPointPrecision - - The precision of floating point numbers used for reading/writing the data. This will only have - an effect if the version of the data stream is Qt_4_6 or higher. - - \warning The floating point precision must be set to the same value on the object that writes - and the object that reads the data stream. - - \value SinglePrecision All floating point numbers in the data stream have 32-bit precision. - \value DoublePrecision All floating point numbers in the data stream have 64-bit precision. - - \sa setFloatingPointPrecision(), floatingPointPrecision() -*/ - -/*! - \enum QDataStream::Status - - This enum describes the current status of the data stream. - - \value Ok The data stream is operating normally. - \value ReadPastEnd The data stream has read past the end of the - data in the underlying device. - \value ReadCorruptData The data stream has read corrupt data. - \value WriteFailed The data stream cannot write to the underlying device. -*/ - -/***************************************************************************** - QDataStream member functions - *****************************************************************************/ - -#define Q_VOID - -#undef CHECK_STREAM_PRECOND -#ifndef QT_NO_DEBUG -#define CHECK_STREAM_PRECOND(retVal) \ - if (!dev) { \ - qWarning("QDataStream: No device"); \ - return retVal; \ - } -#else -#define CHECK_STREAM_PRECOND(retVal) \ - if (!dev) { \ - return retVal; \ - } -#endif - -#define CHECK_STREAM_WRITE_PRECOND(retVal) \ - CHECK_STREAM_PRECOND(retVal) \ - if (q_status != Ok) \ - return retVal; - -#define CHECK_STREAM_TRANSACTION_PRECOND(retVal) \ - if (!d || d->transactionDepth == 0) { \ - qWarning("QDataStream: No transaction in progress"); \ - return retVal; \ - } - -/*! - Constructs a data stream that has no I/O device. - - \sa setDevice() -*/ - -QDataStream::QDataStream() -{ - dev = 0; - owndev = false; - byteorder = BigEndian; - ver = Qt_DefaultCompiledVersion; - noswap = QSysInfo::ByteOrder == QSysInfo::BigEndian; - q_status = Ok; -} - -/*! - Constructs a data stream that uses the I/O device \a d. - - \sa setDevice(), device() -*/ - -QDataStream::QDataStream(QIODevice *d) -{ - dev = d; // set device - owndev = false; - byteorder = BigEndian; // default byte order - ver = Qt_DefaultCompiledVersion; - noswap = QSysInfo::ByteOrder == QSysInfo::BigEndian; - q_status = Ok; -} - -/*! - \fn QDataStream::QDataStream(QByteArray *a, QIODevice::OpenMode mode) - - Constructs a data stream that operates on a byte array, \a a. The - \a mode describes how the device is to be used. - - Alternatively, you can use QDataStream(const QByteArray &) if you - just want to read from a byte array. - - Since QByteArray is not a QIODevice subclass, internally a QBuffer - is created to wrap the byte array. -*/ - -QDataStream::QDataStream(QByteArray *a, QIODevice::OpenMode flags) -{ - QBuffer *buf = new QBuffer(a); -#ifndef QT_NO_QOBJECT - buf->blockSignals(true); -#endif - buf->open(flags); - dev = buf; - owndev = true; - byteorder = BigEndian; - ver = Qt_DefaultCompiledVersion; - noswap = QSysInfo::ByteOrder == QSysInfo::BigEndian; - q_status = Ok; -} - -/*! - Constructs a read-only data stream that operates on byte array \a a. - Use QDataStream(QByteArray*, int) if you want to write to a byte - array. - - Since QByteArray is not a QIODevice subclass, internally a QBuffer - is created to wrap the byte array. -*/ -QDataStream::QDataStream(const QByteArray &a) -{ - QBuffer *buf = new QBuffer; -#ifndef QT_NO_QOBJECT - buf->blockSignals(true); -#endif - buf->setData(a); - buf->open(QIODevice::ReadOnly); - dev = buf; - owndev = true; - byteorder = BigEndian; - ver = Qt_DefaultCompiledVersion; - noswap = QSysInfo::ByteOrder == QSysInfo::BigEndian; - q_status = Ok; -} - -/*! - Destroys the data stream. - - The destructor will not affect the current I/O device, unless it is - an internal I/O device (e.g. a QBuffer) processing a QByteArray - passed in the \e constructor, in which case the internal I/O device - is destroyed. -*/ - -QDataStream::~QDataStream() -{ - if (owndev) - delete dev; -} - - -/*! - \fn QIODevice *QDataStream::device() const - - Returns the I/O device currently set, or 0 if no - device is currently set. - - \sa setDevice() -*/ - -/*! - void QDataStream::setDevice(QIODevice *d) - - Sets the I/O device to \a d, which can be 0 - to unset to current I/O device. - - \sa device() -*/ - -void QDataStream::setDevice(QIODevice *d) -{ - if (owndev) { - delete dev; - owndev = false; - } - dev = d; -} - -/*! - \obsolete - Unsets the I/O device. - Use setDevice(0) instead. -*/ - -void QDataStream::unsetDevice() -{ - setDevice(0); -} - - -/*! - \fn bool QDataStream::atEnd() const - - Returns \c true if the I/O device has reached the end position (end of - the stream or file) or if there is no I/O device set; otherwise - returns \c false. - - \sa QIODevice::atEnd() -*/ - -bool QDataStream::atEnd() const -{ - return dev ? dev->atEnd() : true; -} - -/*! - Returns the floating point precision of the data stream. - - \since 4.6 - - \sa FloatingPointPrecision, setFloatingPointPrecision() -*/ -QDataStream::FloatingPointPrecision QDataStream::floatingPointPrecision() const -{ - return d == 0 ? QDataStream::DoublePrecision : d->floatingPointPrecision; -} - -/*! - Sets the floating point precision of the data stream to \a precision. If the floating point precision is - DoublePrecision and the version of the data stream is Qt_4_6 or higher, all floating point - numbers will be written and read with 64-bit precision. If the floating point precision is - SinglePrecision and the version is Qt_4_6 or higher, all floating point numbers will be written - and read with 32-bit precision. - - For versions prior to Qt_4_6, the precision of floating point numbers in the data stream depends - on the stream operator called. - - The default is DoublePrecision. - - Note that this property does not affect the serialization or deserialization of \c qfloat16 - instances. - - \warning This property must be set to the same value on the object that writes and the object - that reads the data stream. - - \since 4.6 -*/ -void QDataStream::setFloatingPointPrecision(QDataStream::FloatingPointPrecision precision) -{ - if (d == 0) - d.reset(new QDataStreamPrivate()); - d->floatingPointPrecision = precision; -} - -/*! - Returns the status of the data stream. - - \sa Status, setStatus(), resetStatus() -*/ - -QDataStream::Status QDataStream::status() const -{ - return q_status; -} - -/*! - Resets the status of the data stream. - - \sa Status, status(), setStatus() -*/ -void QDataStream::resetStatus() -{ - q_status = Ok; -} - -/*! - Sets the status of the data stream to the \a status given. - - Subsequent calls to setStatus() are ignored until resetStatus() - is called. - - \sa Status, status(), resetStatus() -*/ -void QDataStream::setStatus(Status status) -{ - if (q_status == Ok) - q_status = status; -} - -/*! - \fn int QDataStream::byteOrder() const - - Returns the current byte order setting -- either BigEndian or - LittleEndian. - - \sa setByteOrder() -*/ - -/*! - Sets the serialization byte order to \a bo. - - The \a bo parameter can be QDataStream::BigEndian or - QDataStream::LittleEndian. - - The default setting is big endian. We recommend leaving this - setting unless you have special requirements. - - \sa byteOrder() -*/ - -void QDataStream::setByteOrder(ByteOrder bo) -{ - byteorder = bo; - if (QSysInfo::ByteOrder == QSysInfo::BigEndian) - noswap = (byteorder == BigEndian); - else - noswap = (byteorder == LittleEndian); -} - - -/*! - \enum QDataStream::Version - - This enum provides symbolic synonyms for the data serialization - format version numbers. - - \value Qt_1_0 Version 1 (Qt 1.x) - \value Qt_2_0 Version 2 (Qt 2.0) - \value Qt_2_1 Version 3 (Qt 2.1, 2.2, 2.3) - \value Qt_3_0 Version 4 (Qt 3.0) - \value Qt_3_1 Version 5 (Qt 3.1, 3.2) - \value Qt_3_3 Version 6 (Qt 3.3) - \value Qt_4_0 Version 7 (Qt 4.0, Qt 4.1) - \value Qt_4_1 Version 7 (Qt 4.0, Qt 4.1) - \value Qt_4_2 Version 8 (Qt 4.2) - \value Qt_4_3 Version 9 (Qt 4.3) - \value Qt_4_4 Version 10 (Qt 4.4) - \value Qt_4_5 Version 11 (Qt 4.5) - \value Qt_4_6 Version 12 (Qt 4.6, Qt 4.7, Qt 4.8) - \value Qt_4_7 Same as Qt_4_6. - \value Qt_4_8 Same as Qt_4_6. - \value Qt_4_9 Same as Qt_4_6. - \value Qt_5_0 Version 13 (Qt 5.0) - \value Qt_5_1 Version 14 (Qt 5.1) - \value Qt_5_2 Version 15 (Qt 5.2) - \value Qt_5_3 Same as Qt_5_2 - \value Qt_5_4 Version 16 (Qt 5.4) - \value Qt_5_5 Same as Qt_5_4 - \value Qt_5_6 Version 17 (Qt 5.6) - \value Qt_5_7 Same as Qt_5_6 - \value Qt_5_8 Same as Qt_5_6 - \value Qt_5_9 Same as Qt_5_6 - \value Qt_5_10 Same as Qt_5_6 - \value Qt_5_11 Same as Qt_5_6 - \omitvalue Qt_DefaultCompiledVersion - - \sa setVersion(), version() -*/ - -/*! - \fn int QDataStream::version() const - - Returns the version number of the data serialization format. - - \sa setVersion(), Version -*/ - -/*! - \fn void QDataStream::setVersion(int v) - - Sets the version number of the data serialization format to \a v, - a value of the \l Version enum. - - You don't \e have to set a version if you are using the current - version of Qt, but for your own custom binary formats we - recommend that you do; see \l{Versioning} in the Detailed - Description. - - To accommodate new functionality, the datastream serialization - format of some Qt classes has changed in some versions of Qt. If - you want to read data that was created by an earlier version of - Qt, or write data that can be read by a program that was compiled - with an earlier version of Qt, use this function to modify the - serialization format used by QDataStream. - - The \l Version enum provides symbolic constants for the different - versions of Qt. For example: - - \snippet code/src_corelib_io_qdatastream.cpp 5 - - \sa version(), Version -*/ - -/*! - \since 5.7 - - Starts a new read transaction on the stream. - - Defines a restorable point within the sequence of read operations. For - sequential devices, read data will be duplicated internally to allow - recovery in case of incomplete reads. For random-access devices, - this function saves the current position of the stream. Call - commitTransaction(), rollbackTransaction(), or abortTransaction() to - finish the current transaction. - - Once a transaction is started, subsequent calls to this function will make - the transaction recursive. Inner transactions act as agents of the - outermost transaction (i.e., report the status of read operations to the - outermost transaction, which can restore the position of the stream). - - \note Restoring to the point of the nested startTransaction() call is not - supported. - - When an error occurs during a transaction (including an inner transaction - failing), reading from the data stream is suspended (all subsequent read - operations return empty/zero values) and subsequent inner transactions are - forced to fail. Starting a new outermost transaction recovers from this - state. This behavior makes it unnecessary to error-check every read - operation separately. - - \sa commitTransaction(), rollbackTransaction(), abortTransaction() -*/ - -void QDataStream::startTransaction() -{ - CHECK_STREAM_PRECOND(Q_VOID) - - if (d == 0) - d.reset(new QDataStreamPrivate()); - - if (++d->transactionDepth == 1) { - dev->startTransaction(); - resetStatus(); - } -} - -/*! - \since 5.7 - - Completes a read transaction. Returns \c true if no read errors have - occurred during the transaction; otherwise returns \c false. - - If called on an inner transaction, committing will be postponed until - the outermost commitTransaction(), rollbackTransaction(), or - abortTransaction() call occurs. - - Otherwise, if the stream status indicates reading past the end of the - data, this function restores the stream data to the point of the - startTransaction() call. When this situation occurs, you need to wait for - more data to arrive, after which you start a new transaction. If the data - stream has read corrupt data or any of the inner transactions was aborted, - this function aborts the transaction. - - \sa startTransaction(), rollbackTransaction(), abortTransaction() -*/ - -bool QDataStream::commitTransaction() -{ - CHECK_STREAM_TRANSACTION_PRECOND(false) - if (--d->transactionDepth == 0) { - CHECK_STREAM_PRECOND(false) - - if (q_status == ReadPastEnd) { - dev->rollbackTransaction(); - return false; - } - dev->commitTransaction(); - } - return q_status == Ok; -} - -/*! - \since 5.7 - - Reverts a read transaction. - - This function is commonly used to rollback the transaction when an - incomplete read was detected prior to committing the transaction. - - If called on an inner transaction, reverting is delegated to the outermost - transaction, and subsequently started inner transactions are forced to - fail. - - For the outermost transaction, restores the stream data to the point of - the startTransaction() call. If the data stream has read corrupt data or - any of the inner transactions was aborted, this function aborts the - transaction. - - If the preceding stream operations were successful, sets the status of the - data stream to \value ReadPastEnd. - - \sa startTransaction(), commitTransaction(), abortTransaction() -*/ - -void QDataStream::rollbackTransaction() -{ - setStatus(ReadPastEnd); - - CHECK_STREAM_TRANSACTION_PRECOND(Q_VOID) - if (--d->transactionDepth != 0) - return; - - CHECK_STREAM_PRECOND(Q_VOID) - if (q_status == ReadPastEnd) - dev->rollbackTransaction(); - else - dev->commitTransaction(); -} - -/*! - \since 5.7 - - Aborts a read transaction. - - This function is commonly used to discard the transaction after - higher-level protocol errors or loss of stream synchronization. - - If called on an inner transaction, aborting is delegated to the outermost - transaction, and subsequently started inner transactions are forced to - fail. - - For the outermost transaction, discards the restoration point and any - internally duplicated data of the stream. Will not affect the current - read position of the stream. - - Sets the status of the data stream to \value ReadCorruptData. - - \sa startTransaction(), commitTransaction(), rollbackTransaction() -*/ - -void QDataStream::abortTransaction() -{ - q_status = ReadCorruptData; - - CHECK_STREAM_TRANSACTION_PRECOND(Q_VOID) - if (--d->transactionDepth != 0) - return; - - CHECK_STREAM_PRECOND(Q_VOID) - dev->commitTransaction(); -} - -/***************************************************************************** - QDataStream read functions - *****************************************************************************/ - -/*! - \internal -*/ - -int QDataStream::readBlock(char *data, int len) -{ - // Disable reads on failure in transacted stream - if (q_status != Ok && dev->isTransactionStarted()) - return -1; - - const int readResult = dev->read(data, len); - if (readResult != len) - setStatus(ReadPastEnd); - return readResult; -} - -/*! - \fn QDataStream &QDataStream::operator>>(std::nullptr_t &ptr) - \since 5.9 - \overload - - Simulates reading a \c{std::nullptr_t} from the stream into \a ptr and - returns a reference to the stream. This function does not actually read - anything from the stream, as \c{std::nullptr_t} values are stored as 0 - bytes. -*/ - -/*! - \fn QDataStream &QDataStream::operator>>(quint8 &i) - \overload - - Reads an unsigned byte from the stream into \a i, and returns a - reference to the stream. -*/ - -/*! - Reads a signed byte from the stream into \a i, and returns a - reference to the stream. -*/ - -QDataStream &QDataStream::operator>>(qint8 &i) -{ - i = 0; - CHECK_STREAM_PRECOND(*this) - char c; - if (readBlock(&c, 1) == 1) - i = qint8(c); - return *this; -} - - -/*! - \fn QDataStream &QDataStream::operator>>(quint16 &i) - \overload - - Reads an unsigned 16-bit integer from the stream into \a i, and - returns a reference to the stream. -*/ - -/*! - \overload - - Reads a signed 16-bit integer from the stream into \a i, and - returns a reference to the stream. -*/ - -QDataStream &QDataStream::operator>>(qint16 &i) -{ - i = 0; - CHECK_STREAM_PRECOND(*this) - if (readBlock(reinterpret_cast(&i), 2) != 2) { - i = 0; - } else { - if (!noswap) { - i = qbswap(i); - } - } - return *this; -} - - -/*! - \fn QDataStream &QDataStream::operator>>(quint32 &i) - \overload - - Reads an unsigned 32-bit integer from the stream into \a i, and - returns a reference to the stream. -*/ - -/*! - \overload - - Reads a signed 32-bit integer from the stream into \a i, and - returns a reference to the stream. -*/ - -QDataStream &QDataStream::operator>>(qint32 &i) -{ - i = 0; - CHECK_STREAM_PRECOND(*this) - if (readBlock(reinterpret_cast(&i), 4) != 4) { - i = 0; - } else { - if (!noswap) { - i = qbswap(i); - } - } - return *this; -} - -/*! - \fn QDataStream &QDataStream::operator>>(quint64 &i) - \overload - - Reads an unsigned 64-bit integer from the stream, into \a i, and - returns a reference to the stream. -*/ - -/*! - \overload - - Reads a signed 64-bit integer from the stream into \a i, and - returns a reference to the stream. -*/ - -QDataStream &QDataStream::operator>>(qint64 &i) -{ - i = qint64(0); - CHECK_STREAM_PRECOND(*this) - if (version() < 6) { - quint32 i1, i2; - *this >> i2 >> i1; - i = ((quint64)i1 << 32) + i2; - } else { - if (readBlock(reinterpret_cast(&i), 8) != 8) { - i = qint64(0); - } else { - if (!noswap) { - i = qbswap(i); - } - } - } - return *this; -} - -/*! - Reads a boolean value from the stream into \a i. Returns a - reference to the stream. -*/ -QDataStream &QDataStream::operator>>(bool &i) -{ - qint8 v; - *this >> v; - i = !!v; - return *this; -} - -/*! - \overload - - Reads a floating point number from the stream into \a f, - using the standard IEEE 754 format. Returns a reference to the - stream. - - \sa setFloatingPointPrecision() -*/ - -QDataStream &QDataStream::operator>>(float &f) -{ - if (version() >= QDataStream::Qt_4_6 - && floatingPointPrecision() == QDataStream::DoublePrecision) { - double d; - *this >> d; - f = d; - return *this; - } - - f = 0.0f; - CHECK_STREAM_PRECOND(*this) - if (readBlock(reinterpret_cast(&f), 4) != 4) { - f = 0.0f; - } else { - if (!noswap) { - union { - float val1; - quint32 val2; - } x; - x.val2 = qbswap(*reinterpret_cast(&f)); - f = x.val1; - } - } - return *this; -} - -/*! - \overload - - Reads a floating point number from the stream into \a f, - using the standard IEEE 754 format. Returns a reference to the - stream. - - \sa setFloatingPointPrecision() -*/ - -QDataStream &QDataStream::operator>>(double &f) -{ - if (version() >= QDataStream::Qt_4_6 - && floatingPointPrecision() == QDataStream::SinglePrecision) { - float d; - *this >> d; - f = d; - return *this; - } - - f = 0.0; - CHECK_STREAM_PRECOND(*this) - if (readBlock(reinterpret_cast(&f), 8) != 8) { - f = 0.0; - } else { - if (!noswap) { - union { - double val1; - quint64 val2; - } x; - x.val2 = qbswap(*reinterpret_cast(&f)); - f = x.val1; - } - } - return *this; -} - - -/*! - \overload - \since 5.9 - - Reads a floating point number from the stream into \a f, - using the standard IEEE 754 format. Returns a reference to the - stream. -*/ -QDataStream &QDataStream::operator>>(qfloat16 &f) -{ - return *this >> reinterpret_cast(f); -} - - -/*! - \overload - - Reads the '\\0'-terminated string \a s from the stream and returns - a reference to the stream. - - The string is deserialized using \c{readBytes()}. - - Space for the string is allocated using \c{new []} -- the caller must - destroy it with \c{delete []}. - - \sa readBytes(), readRawData() -*/ - -QDataStream &QDataStream::operator>>(char *&s) -{ - uint len = 0; - return readBytes(s, len); -} - - -/*! - Reads the buffer \a s from the stream and returns a reference to - the stream. - - The buffer \a s is allocated using \c{new []}. Destroy it with the - \c{delete []} operator. - - The \a l parameter is set to the length of the buffer. If the - string read is empty, \a l is set to 0 and \a s is set to - a null pointer. - - The serialization format is a quint32 length specifier first, - then \a l bytes of data. - - \sa readRawData(), writeBytes() -*/ - -QDataStream &QDataStream::readBytes(char *&s, uint &l) -{ - s = 0; - l = 0; - CHECK_STREAM_PRECOND(*this) - - quint32 len; - *this >> len; - if (len == 0) - return *this; - - const quint32 Step = 1024 * 1024; - quint32 allocated = 0; - char *prevBuf = 0; - char *curBuf = 0; - - do { - int blockSize = qMin(Step, len - allocated); - prevBuf = curBuf; - curBuf = new char[allocated + blockSize + 1]; - if (prevBuf) { - memcpy(curBuf, prevBuf, allocated); - delete [] prevBuf; - } - if (readBlock(curBuf + allocated, blockSize) != blockSize) { - delete [] curBuf; - return *this; - } - allocated += blockSize; - } while (allocated < len); - - s = curBuf; - s[len] = '\0'; - l = (uint)len; - return *this; -} - -/*! - Reads at most \a len bytes from the stream into \a s and returns the number of - bytes read. If an error occurs, this function returns -1. - - The buffer \a s must be preallocated. The data is \e not encoded. - - \sa readBytes(), QIODevice::read(), writeRawData() -*/ - -int QDataStream::readRawData(char *s, int len) -{ - CHECK_STREAM_PRECOND(-1) - return readBlock(s, len); -} - - -/***************************************************************************** - QDataStream write functions - *****************************************************************************/ - -/*! - \fn QDataStream &QDataStream::operator<<(std::nullptr_t ptr) - \since 5.9 - \overload - - Simulates writing a \c{std::nullptr_t}, \a ptr, to the stream and returns a - reference to the stream. This function does not actually write anything to - the stream, as \c{std::nullptr_t} values are stored as 0 bytes. -*/ - -/*! - \fn QDataStream &QDataStream::operator<<(quint8 i) - \overload - - Writes an unsigned byte, \a i, to the stream and returns a - reference to the stream. -*/ - -/*! - Writes a signed byte, \a i, to the stream and returns a reference - to the stream. -*/ - -QDataStream &QDataStream::operator<<(qint8 i) -{ - CHECK_STREAM_WRITE_PRECOND(*this) - if (!dev->putChar(i)) - q_status = WriteFailed; - return *this; -} - - -/*! - \fn QDataStream &QDataStream::operator<<(quint16 i) - \overload - - Writes an unsigned 16-bit integer, \a i, to the stream and returns - a reference to the stream. -*/ - -/*! - \overload - - Writes a signed 16-bit integer, \a i, to the stream and returns a - reference to the stream. -*/ - -QDataStream &QDataStream::operator<<(qint16 i) -{ - CHECK_STREAM_WRITE_PRECOND(*this) - if (!noswap) { - i = qbswap(i); - } - if (dev->write((char *)&i, sizeof(qint16)) != sizeof(qint16)) - q_status = WriteFailed; - return *this; -} - -/*! - \overload - - Writes a signed 32-bit integer, \a i, to the stream and returns a - reference to the stream. -*/ - -QDataStream &QDataStream::operator<<(qint32 i) -{ - CHECK_STREAM_WRITE_PRECOND(*this) - if (!noswap) { - i = qbswap(i); - } - if (dev->write((char *)&i, sizeof(qint32)) != sizeof(qint32)) - q_status = WriteFailed; - return *this; -} - -/*! - \fn QDataStream &QDataStream::operator<<(quint64 i) - \overload - - Writes an unsigned 64-bit integer, \a i, to the stream and returns a - reference to the stream. -*/ - -/*! - \overload - - Writes a signed 64-bit integer, \a i, to the stream and returns a - reference to the stream. -*/ - -QDataStream &QDataStream::operator<<(qint64 i) -{ - CHECK_STREAM_WRITE_PRECOND(*this) - if (version() < 6) { - quint32 i1 = i & 0xffffffff; - quint32 i2 = i >> 32; - *this << i2 << i1; - } else { - if (!noswap) { - i = qbswap(i); - } - if (dev->write((char *)&i, sizeof(qint64)) != sizeof(qint64)) - q_status = WriteFailed; - } - return *this; -} - -/*! - \fn QDataStream &QDataStream::operator<<(quint32 i) - \overload - - Writes an unsigned integer, \a i, to the stream as a 32-bit - unsigned integer (quint32). Returns a reference to the stream. -*/ - -/*! - Writes a boolean value, \a i, to the stream. Returns a reference - to the stream. -*/ - -QDataStream &QDataStream::operator<<(bool i) -{ - CHECK_STREAM_WRITE_PRECOND(*this) - if (!dev->putChar(qint8(i))) - q_status = WriteFailed; - return *this; -} - -/*! - \overload - - Writes a floating point number, \a f, to the stream using - the standard IEEE 754 format. Returns a reference to the stream. - - \sa setFloatingPointPrecision() -*/ - -QDataStream &QDataStream::operator<<(float f) -{ - if (version() >= QDataStream::Qt_4_6 - && floatingPointPrecision() == QDataStream::DoublePrecision) { - *this << double(f); - return *this; - } - - CHECK_STREAM_WRITE_PRECOND(*this) - float g = f; // fixes float-on-stack problem - if (!noswap) { - union { - float val1; - quint32 val2; - } x; - x.val1 = g; - x.val2 = qbswap(x.val2); - - if (dev->write((char *)&x.val2, sizeof(float)) != sizeof(float)) - q_status = WriteFailed; - return *this; - } - - if (dev->write((char *)&g, sizeof(float)) != sizeof(float)) - q_status = WriteFailed; - return *this; -} - - -/*! - \overload - - Writes a floating point number, \a f, to the stream using - the standard IEEE 754 format. Returns a reference to the stream. - - \sa setFloatingPointPrecision() -*/ - -QDataStream &QDataStream::operator<<(double f) -{ - if (version() >= QDataStream::Qt_4_6 - && floatingPointPrecision() == QDataStream::SinglePrecision) { - *this << float(f); - return *this; - } - - CHECK_STREAM_WRITE_PRECOND(*this) - if (noswap) { - if (dev->write((char *)&f, sizeof(double)) != sizeof(double)) - q_status = WriteFailed; - } else { - union { - double val1; - quint64 val2; - } x; - x.val1 = f; - x.val2 = qbswap(x.val2); - if (dev->write((char *)&x.val2, sizeof(double)) != sizeof(double)) - q_status = WriteFailed; - } - return *this; -} - - -/*! - \fn QDataStream &QDataStream::operator<<(qfloat16 f) - \overload - \since 5.9 - - Writes a floating point number, \a f, to the stream using - the standard IEEE 754 format. Returns a reference to the stream. -*/ -QDataStream &QDataStream::operator<<(qfloat16 f) -{ - return *this << reinterpret_cast(f); -} - -/*! - \overload - - Writes the '\\0'-terminated string \a s to the stream and returns a - reference to the stream. - - The string is serialized using \c{writeBytes()}. - - \sa writeBytes(), writeRawData() -*/ - -QDataStream &QDataStream::operator<<(const char *s) -{ - if (!s) { - *this << (quint32)0; - return *this; - } - uint len = qstrlen(s) + 1; // also write null terminator - *this << (quint32)len; // write length specifier - writeRawData(s, len); - return *this; -} - -/*! - Writes the length specifier \a len and the buffer \a s to the - stream and returns a reference to the stream. - - The \a len is serialized as a quint32, followed by \a len bytes - from \a s. Note that the data is \e not encoded. - - \sa writeRawData(), readBytes() -*/ - -QDataStream &QDataStream::writeBytes(const char *s, uint len) -{ - CHECK_STREAM_WRITE_PRECOND(*this) - *this << (quint32)len; // write length specifier - if (len) - writeRawData(s, len); - return *this; -} - - -/*! - Writes \a len bytes from \a s to the stream. Returns the - number of bytes actually written, or -1 on error. - The data is \e not encoded. - - \sa writeBytes(), QIODevice::write(), readRawData() -*/ - -int QDataStream::writeRawData(const char *s, int len) -{ - CHECK_STREAM_WRITE_PRECOND(-1) - int ret = dev->write(s, len); - if (ret != len) - q_status = WriteFailed; - return ret; -} - -/*! - \since 4.1 - - Skips \a len bytes from the device. Returns the number of bytes - actually skipped, or -1 on error. - - This is equivalent to calling readRawData() on a buffer of length - \a len and ignoring the buffer. - - \sa QIODevice::seek() -*/ -int QDataStream::skipRawData(int len) -{ - CHECK_STREAM_PRECOND(-1) - if (q_status != Ok && dev->isTransactionStarted()) - return -1; - - const int skipResult = dev->skip(len); - if (skipResult != len) - setStatus(ReadPastEnd); - return skipResult; -} - -QT_END_NAMESPACE - -#endif // QT_NO_DATASTREAM diff --git a/src/corelib/io/qdatastream.h b/src/corelib/io/qdatastream.h deleted file mode 100644 index 1f1b13686c..0000000000 --- a/src/corelib/io/qdatastream.h +++ /dev/null @@ -1,466 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtCore module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QDATASTREAM_H -#define QDATASTREAM_H - -#include -#include -#include - -#ifdef Status -#error qdatastream.h must be included before any header file that defines Status -#endif - -QT_BEGIN_NAMESPACE - -class qfloat16; -class QByteArray; -class QIODevice; - -template class QList; -template class QLinkedList; -template class QVector; -template class QSet; -template class QHash; -template class QMap; - -#if !defined(QT_NO_DATASTREAM) || defined(QT_BOOTSTRAPPED) -class QDataStreamPrivate; -namespace QtPrivate { -class StreamStateSaver; -} -class Q_CORE_EXPORT QDataStream -{ -public: - enum Version { - Qt_1_0 = 1, - Qt_2_0 = 2, - Qt_2_1 = 3, - Qt_3_0 = 4, - Qt_3_1 = 5, - Qt_3_3 = 6, - Qt_4_0 = 7, - Qt_4_1 = Qt_4_0, - Qt_4_2 = 8, - Qt_4_3 = 9, - Qt_4_4 = 10, - Qt_4_5 = 11, - Qt_4_6 = 12, - Qt_4_7 = Qt_4_6, - Qt_4_8 = Qt_4_7, - Qt_4_9 = Qt_4_8, - Qt_5_0 = 13, - Qt_5_1 = 14, - Qt_5_2 = 15, - Qt_5_3 = Qt_5_2, - Qt_5_4 = 16, - Qt_5_5 = Qt_5_4, - Qt_5_6 = 17, - Qt_5_7 = Qt_5_6, - Qt_5_8 = Qt_5_7, - Qt_5_9 = Qt_5_8, - Qt_5_10 = Qt_5_9, - Qt_5_11 = Qt_5_10, -#if QT_VERSION >= 0x050c00 -#error Add the datastream version for this Qt version and update Qt_DefaultCompiledVersion -#endif - Qt_DefaultCompiledVersion = Qt_5_11 - }; - - enum ByteOrder { - BigEndian = QSysInfo::BigEndian, - LittleEndian = QSysInfo::LittleEndian - }; - - enum Status { - Ok, - ReadPastEnd, - ReadCorruptData, - WriteFailed - }; - - enum FloatingPointPrecision { - SinglePrecision, - DoublePrecision - }; - - QDataStream(); - explicit QDataStream(QIODevice *); - QDataStream(QByteArray *, QIODevice::OpenMode flags); - QDataStream(const QByteArray &); - ~QDataStream(); - - QIODevice *device() const; - void setDevice(QIODevice *); - void unsetDevice(); - - bool atEnd() const; - - Status status() const; - void setStatus(Status status); - void resetStatus(); - - FloatingPointPrecision floatingPointPrecision() const; - void setFloatingPointPrecision(FloatingPointPrecision precision); - - ByteOrder byteOrder() const; - void setByteOrder(ByteOrder); - - int version() const; - void setVersion(int); - - QDataStream &operator>>(qint8 &i); - QDataStream &operator>>(quint8 &i); - QDataStream &operator>>(qint16 &i); - QDataStream &operator>>(quint16 &i); - QDataStream &operator>>(qint32 &i); - QDataStream &operator>>(quint32 &i); - QDataStream &operator>>(qint64 &i); - QDataStream &operator>>(quint64 &i); - QDataStream &operator>>(std::nullptr_t &ptr) { ptr = nullptr; return *this; } - - QDataStream &operator>>(bool &i); - QDataStream &operator>>(qfloat16 &f); - QDataStream &operator>>(float &f); - QDataStream &operator>>(double &f); - QDataStream &operator>>(char *&str); - - QDataStream &operator<<(qint8 i); - QDataStream &operator<<(quint8 i); - QDataStream &operator<<(qint16 i); - QDataStream &operator<<(quint16 i); - QDataStream &operator<<(qint32 i); - QDataStream &operator<<(quint32 i); - QDataStream &operator<<(qint64 i); - QDataStream &operator<<(quint64 i); - QDataStream &operator<<(std::nullptr_t) { return *this; } - QDataStream &operator<<(bool i); - QDataStream &operator<<(qfloat16 f); - QDataStream &operator<<(float f); - QDataStream &operator<<(double f); - QDataStream &operator<<(const char *str); - - QDataStream &readBytes(char *&, uint &len); - int readRawData(char *, int len); - - QDataStream &writeBytes(const char *, uint len); - int writeRawData(const char *, int len); - - int skipRawData(int len); - - void startTransaction(); - bool commitTransaction(); - void rollbackTransaction(); - void abortTransaction(); - -private: - Q_DISABLE_COPY(QDataStream) - - QScopedPointer d; - - QIODevice *dev; - bool owndev; - bool noswap; - ByteOrder byteorder; - int ver; - Status q_status; - - int readBlock(char *data, int len); - friend class QtPrivate::StreamStateSaver; -}; - -namespace QtPrivate { - -class StreamStateSaver -{ -public: - inline StreamStateSaver(QDataStream *s) : stream(s), oldStatus(s->status()) - { - if (!stream->dev || !stream->dev->isTransactionStarted()) - stream->resetStatus(); - } - inline ~StreamStateSaver() - { - if (oldStatus != QDataStream::Ok) { - stream->resetStatus(); - stream->setStatus(oldStatus); - } - } - -private: - QDataStream *stream; - QDataStream::Status oldStatus; -}; - -template -QDataStream &readArrayBasedContainer(QDataStream &s, Container &c) -{ - StreamStateSaver stateSaver(&s); - - c.clear(); - quint32 n; - s >> n; - c.reserve(n); - for (quint32 i = 0; i < n; ++i) { - typename Container::value_type t; - s >> t; - if (s.status() != QDataStream::Ok) { - c.clear(); - break; - } - c.append(t); - } - - return s; -} - -template -QDataStream &readListBasedContainer(QDataStream &s, Container &c) -{ - StreamStateSaver stateSaver(&s); - - c.clear(); - quint32 n; - s >> n; - for (quint32 i = 0; i < n; ++i) { - typename Container::value_type t; - s >> t; - if (s.status() != QDataStream::Ok) { - c.clear(); - break; - } - c << t; - } - - return s; -} - -template -QDataStream &readAssociativeContainer(QDataStream &s, Container &c) -{ - StreamStateSaver stateSaver(&s); - - c.clear(); - quint32 n; - s >> n; - for (quint32 i = 0; i < n; ++i) { - typename Container::key_type k; - typename Container::mapped_type t; - s >> k >> t; - if (s.status() != QDataStream::Ok) { - c.clear(); - break; - } - c.insertMulti(k, t); - } - - return s; -} - -template -QDataStream &writeSequentialContainer(QDataStream &s, const Container &c) -{ - s << quint32(c.size()); - for (const typename Container::value_type &t : c) - s << t; - - return s; -} - -template -QDataStream &writeAssociativeContainer(QDataStream &s, const Container &c) -{ - s << quint32(c.size()); - // Deserialization should occur in the reverse order. - // Otherwise, value() will return the least recently inserted - // value instead of the most recently inserted one. - auto it = c.constEnd(); - auto begin = c.constBegin(); - while (it != begin) { - --it; - s << it.key() << it.value(); - } - - return s; -} - -} // QtPrivate namespace - -/***************************************************************************** - QDataStream inline functions - *****************************************************************************/ - -inline QIODevice *QDataStream::device() const -{ return dev; } - -inline QDataStream::ByteOrder QDataStream::byteOrder() const -{ return byteorder; } - -inline int QDataStream::version() const -{ return ver; } - -inline void QDataStream::setVersion(int v) -{ ver = v; } - -inline QDataStream &QDataStream::operator>>(quint8 &i) -{ return *this >> reinterpret_cast(i); } - -inline QDataStream &QDataStream::operator>>(quint16 &i) -{ return *this >> reinterpret_cast(i); } - -inline QDataStream &QDataStream::operator>>(quint32 &i) -{ return *this >> reinterpret_cast(i); } - -inline QDataStream &QDataStream::operator>>(quint64 &i) -{ return *this >> reinterpret_cast(i); } - -inline QDataStream &QDataStream::operator<<(quint8 i) -{ return *this << qint8(i); } - -inline QDataStream &QDataStream::operator<<(quint16 i) -{ return *this << qint16(i); } - -inline QDataStream &QDataStream::operator<<(quint32 i) -{ return *this << qint32(i); } - -inline QDataStream &QDataStream::operator<<(quint64 i) -{ return *this << qint64(i); } - -template -inline QDataStream &operator<<(QDataStream &s, QFlags e) -{ return s << e.i; } - -template -inline QDataStream &operator>>(QDataStream &s, QFlags &e) -{ return s >> e.i; } - -template -inline QDataStream &operator>>(QDataStream &s, QList &l) -{ - return QtPrivate::readArrayBasedContainer(s, l); -} - -template -inline QDataStream &operator<<(QDataStream &s, const QList &l) -{ - return QtPrivate::writeSequentialContainer(s, l); -} - -template -inline QDataStream &operator>>(QDataStream &s, QLinkedList &l) -{ - return QtPrivate::readListBasedContainer(s, l); -} - -template -inline QDataStream &operator<<(QDataStream &s, const QLinkedList &l) -{ - return QtPrivate::writeSequentialContainer(s, l); -} - -template -inline QDataStream &operator>>(QDataStream &s, QVector &v) -{ - return QtPrivate::readArrayBasedContainer(s, v); -} - -template -inline QDataStream &operator<<(QDataStream &s, const QVector &v) -{ - return QtPrivate::writeSequentialContainer(s, v); -} - -template -inline QDataStream &operator>>(QDataStream &s, QSet &set) -{ - return QtPrivate::readListBasedContainer(s, set); -} - -template -inline QDataStream &operator<<(QDataStream &s, const QSet &set) -{ - return QtPrivate::writeSequentialContainer(s, set); -} - -template -inline QDataStream &operator>>(QDataStream &s, QHash &hash) -{ - return QtPrivate::readAssociativeContainer(s, hash); -} - -template -inline QDataStream &operator<<(QDataStream &s, const QHash &hash) -{ - return QtPrivate::writeAssociativeContainer(s, hash); -} - -template -inline QDataStream &operator>>(QDataStream &s, QMap &map) -{ - return QtPrivate::readAssociativeContainer(s, map); -} - -template -inline QDataStream &operator<<(QDataStream &s, const QMap &map) -{ - return QtPrivate::writeAssociativeContainer(s, map); -} - -#ifndef QT_NO_DATASTREAM -template -inline QDataStream& operator>>(QDataStream& s, QPair& p) -{ - s >> p.first >> p.second; - return s; -} - -template -inline QDataStream& operator<<(QDataStream& s, const QPair& p) -{ - s << p.first << p.second; - return s; -} -#endif - -#endif // QT_NO_DATASTREAM - -QT_END_NAMESPACE - -#endif // QDATASTREAM_H diff --git a/src/corelib/io/qdatastream_p.h b/src/corelib/io/qdatastream_p.h deleted file mode 100644 index 3ca0ae840e..0000000000 --- a/src/corelib/io/qdatastream_p.h +++ /dev/null @@ -1,73 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtCore module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QDATASTREAM_P_H -#define QDATASTREAM_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include -#include - -QT_BEGIN_NAMESPACE - -#if !defined(QT_NO_DATASTREAM) || defined(QT_BOOTSTRAPPED) -class QDataStreamPrivate -{ -public: - QDataStreamPrivate() : floatingPointPrecision(QDataStream::DoublePrecision), - transactionDepth(0) { } - - QDataStream::FloatingPointPrecision floatingPointPrecision; - int transactionDepth; -}; -#endif - -QT_END_NAMESPACE - -#endif // QDATASTREAM_P_H diff --git a/src/corelib/io/qtextstream.cpp b/src/corelib/io/qtextstream.cpp deleted file mode 100644 index ee3cb4efcb..0000000000 --- a/src/corelib/io/qtextstream.cpp +++ /dev/null @@ -1,3192 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Copyright (C) 2016 Intel Corporation. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtCore module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -//#define QTEXTSTREAM_DEBUG -static const int QTEXTSTREAM_BUFFERSIZE = 16384; - -/*! - \class QTextStream - \inmodule QtCore - - \brief The QTextStream class provides a convenient interface for - reading and writing text. - - \ingroup io - \ingroup string-processing - \reentrant - - QTextStream can operate on a QIODevice, a QByteArray or a - QString. Using QTextStream's streaming operators, you can - conveniently read and write words, lines and numbers. For - generating text, QTextStream supports formatting options for field - padding and alignment, and formatting of numbers. Example: - - \snippet code/src_corelib_io_qtextstream.cpp 0 - - It's also common to use QTextStream to read console input and write - console output. QTextStream is locale aware, and will automatically decode - standard input using the correct codec. Example: - - \snippet code/src_corelib_io_qtextstream.cpp 1 - - Besides using QTextStream's constructors, you can also set the - device or string QTextStream operates on by calling setDevice() or - setString(). You can seek to a position by calling seek(), and - atEnd() will return true when there is no data left to be read. If - you call flush(), QTextStream will empty all data from its write - buffer into the device and call flush() on the device. - - Internally, QTextStream uses a Unicode based buffer, and - QTextCodec is used by QTextStream to automatically support - different character sets. By default, QTextCodec::codecForLocale() - is used for reading and writing, but you can also set the codec by - calling setCodec(). Automatic Unicode detection is also - supported. When this feature is enabled (the default behavior), - QTextStream will detect the UTF-16 or the UTF-32 BOM (Byte Order Mark) and - switch to the appropriate UTF codec when reading. QTextStream - does not write a BOM by default, but you can enable this by calling - setGenerateByteOrderMark(true). When QTextStream operates on a QString - directly, the codec is disabled. - - There are three general ways to use QTextStream when reading text - files: - - \list - - \li Chunk by chunk, by calling readLine() or readAll(). - - \li Word by word. QTextStream supports streaming into \l {QString}s, - \l {QByteArray}s and char* buffers. Words are delimited by space, and - leading white space is automatically skipped. - - \li Character by character, by streaming into QChar or char types. - This method is often used for convenient input handling when - parsing files, independent of character encoding and end-of-line - semantics. To skip white space, call skipWhiteSpace(). - - \endlist - - Since the text stream uses a buffer, you should not read from - the stream using the implementation of a superclass. For instance, - if you have a QFile and read from it directly using - QFile::readLine() instead of using the stream, the text stream's - internal position will be out of sync with the file's position. - - By default, when reading numbers from a stream of text, - QTextStream will automatically detect the number's base - representation. For example, if the number starts with "0x", it is - assumed to be in hexadecimal form. If it starts with the digits - 1-9, it is assumed to be in decimal form, and so on. You can set - the integer base, thereby disabling the automatic detection, by - calling setIntegerBase(). Example: - - \snippet code/src_corelib_io_qtextstream.cpp 2 - - QTextStream supports many formatting options for generating text. - You can set the field width and pad character by calling - setFieldWidth() and setPadChar(). Use setFieldAlignment() to set - the alignment within each field. For real numbers, call - setRealNumberNotation() and setRealNumberPrecision() to set the - notation (SmartNotation, ScientificNotation, FixedNotation) and precision in - digits of the generated number. Some extra number formatting - options are also available through setNumberFlags(). - - \target QTextStream manipulators - - Like \c in the standard C++ library, QTextStream also - defines several global manipulator functions: - - \table - \header \li Manipulator \li Description - \row \li \c bin \li Same as setIntegerBase(2). - \row \li \c oct \li Same as setIntegerBase(8). - \row \li \c dec \li Same as setIntegerBase(10). - \row \li \c hex \li Same as setIntegerBase(16). - \row \li \c showbase \li Same as setNumberFlags(numberFlags() | ShowBase). - \row \li \c forcesign \li Same as setNumberFlags(numberFlags() | ForceSign). - \row \li \c forcepoint \li Same as setNumberFlags(numberFlags() | ForcePoint). - \row \li \c noshowbase \li Same as setNumberFlags(numberFlags() & ~ShowBase). - \row \li \c noforcesign \li Same as setNumberFlags(numberFlags() & ~ForceSign). - \row \li \c noforcepoint \li Same as setNumberFlags(numberFlags() & ~ForcePoint). - \row \li \c uppercasebase \li Same as setNumberFlags(numberFlags() | UppercaseBase). - \row \li \c uppercasedigits \li Same as setNumberFlags(numberFlags() | UppercaseDigits). - \row \li \c lowercasebase \li Same as setNumberFlags(numberFlags() & ~UppercaseBase). - \row \li \c lowercasedigits \li Same as setNumberFlags(numberFlags() & ~UppercaseDigits). - \row \li \c fixed \li Same as setRealNumberNotation(FixedNotation). - \row \li \c scientific \li Same as setRealNumberNotation(ScientificNotation). - \row \li \c left \li Same as setFieldAlignment(AlignLeft). - \row \li \c right \li Same as setFieldAlignment(AlignRight). - \row \li \c center \li Same as setFieldAlignment(AlignCenter). - \row \li \c endl \li Same as operator<<('\\n') and flush(). - \row \li \c flush \li Same as flush(). - \row \li \c reset \li Same as reset(). - \row \li \c ws \li Same as skipWhiteSpace(). - \row \li \c bom \li Same as setGenerateByteOrderMark(true). - \endtable - - In addition, Qt provides three global manipulators that take a - parameter: qSetFieldWidth(), qSetPadChar(), and - qSetRealNumberPrecision(). - - \sa QDataStream, QIODevice, QFile, QBuffer, QTcpSocket, {Text Codecs Example} -*/ - -/*! \enum QTextStream::RealNumberNotation - - This enum specifies which notations to use for expressing \c - float and \c double as strings. - - \value ScientificNotation Scientific notation (\c{printf()}'s \c %e flag). - \value FixedNotation Fixed-point notation (\c{printf()}'s \c %f flag). - \value SmartNotation Scientific or fixed-point notation, depending on which makes most sense (\c{printf()}'s \c %g flag). - - \sa setRealNumberNotation() -*/ - -/*! \enum QTextStream::FieldAlignment - - This enum specifies how to align text in fields when the field is - wider than the text that occupies it. - - \value AlignLeft Pad on the right side of fields. - \value AlignRight Pad on the left side of fields. - \value AlignCenter Pad on both sides of field. - \value AlignAccountingStyle Same as AlignRight, except that the - sign of a number is flush left. - - \sa setFieldAlignment() -*/ - -/*! \enum QTextStream::NumberFlag - - This enum specifies various flags that can be set to affect the - output of integers, \c{float}s, and \c{double}s. - - \value ShowBase Show the base as a prefix if the base - is 16 ("0x"), 8 ("0"), or 2 ("0b"). - \value ForcePoint Always put the decimal separator in numbers, even if - there are no decimals. - \value ForceSign Always put the sign in numbers, even for positive numbers. - \value UppercaseBase Use uppercase versions of base prefixes ("0X", "0B"). - \value UppercaseDigits Use uppercase letters for expressing - digits 10 to 35 instead of lowercase. - - \sa setNumberFlags() -*/ - -/*! \enum QTextStream::Status - - This enum describes the current status of the text stream. - - \value Ok The text stream is operating normally. - \value ReadPastEnd The text stream has read past the end of the - data in the underlying device. - \value ReadCorruptData The text stream has read corrupt data. - \value WriteFailed The text stream cannot write to the underlying device. - - \sa status() -*/ - -#include "qtextstream.h" -#include "private/qtextstream_p.h" -#include "qbuffer.h" -#include "qfile.h" -#include "qnumeric.h" -#include "qvarlengtharray.h" - -#include -#include "private/qlocale_p.h" - -#include -#include -#include - -#if defined QTEXTSTREAM_DEBUG -#include -#include "private/qtools_p.h" - -QT_BEGIN_NAMESPACE - -// Returns a human readable representation of the first \a len -// characters in \a data. -static QByteArray qt_prettyDebug(const char *data, int len, int maxSize) -{ - if (!data) return "(null)"; - QByteArray out; - for (int i = 0; i < len; ++i) { - char c = data[i]; - if (isprint(int(uchar(c)))) { - out += c; - } else switch (c) { - case '\n': out += "\\n"; break; - case '\r': out += "\\r"; break; - case '\t': out += "\\t"; break; - default: { - const char buf[] = { - '\\', - 'x', - QtMiscUtils::toHexLower(uchar(c) / 16), - QtMiscUtils::toHexLower(uchar(c) % 16), - 0 - }; - out += buf; - } - } - } - - if (len < maxSize) - out += "..."; - - return out; -} -QT_END_NAMESPACE - -#endif - -// A precondition macro -#define Q_VOID -#define CHECK_VALID_STREAM(x) do { \ - if (!d->string && !d->device) { \ - qWarning("QTextStream: No device"); \ - return x; \ - } } while (0) - -// Base implementations of operator>> for ints and reals -#define IMPLEMENT_STREAM_RIGHT_INT_OPERATOR(type) do { \ - Q_D(QTextStream); \ - CHECK_VALID_STREAM(*this); \ - qulonglong tmp; \ - switch (d->getNumber(&tmp)) { \ - case QTextStreamPrivate::npsOk: \ - i = (type)tmp; \ - break; \ - case QTextStreamPrivate::npsMissingDigit: \ - case QTextStreamPrivate::npsInvalidPrefix: \ - i = (type)0; \ - setStatus(atEnd() ? QTextStream::ReadPastEnd : QTextStream::ReadCorruptData); \ - break; \ - } \ - return *this; } while (0) - -#define IMPLEMENT_STREAM_RIGHT_REAL_OPERATOR(type) do { \ - Q_D(QTextStream); \ - CHECK_VALID_STREAM(*this); \ - double tmp; \ - if (d->getReal(&tmp)) { \ - f = (type)tmp; \ - } else { \ - f = (type)0; \ - setStatus(atEnd() ? QTextStream::ReadPastEnd : QTextStream::ReadCorruptData); \ - } \ - return *this; } while (0) - -QT_BEGIN_NAMESPACE - -//------------------------------------------------------------------- - -/*! - \internal -*/ -QTextStreamPrivate::QTextStreamPrivate(QTextStream *q_ptr) - : -#ifndef QT_NO_TEXTCODEC - readConverterSavedState(0), -#endif - readConverterSavedStateOffset(0), - locale(QLocale::c()) -{ - this->q_ptr = q_ptr; - reset(); -} - -/*! - \internal -*/ -QTextStreamPrivate::~QTextStreamPrivate() -{ - if (deleteDevice) { -#ifndef QT_NO_QOBJECT - device->blockSignals(true); -#endif - delete device; - } -#ifndef QT_NO_TEXTCODEC - delete readConverterSavedState; -#endif -} - -#ifndef QT_NO_TEXTCODEC -static void resetCodecConverterStateHelper(QTextCodec::ConverterState *state) -{ - state->~ConverterState(); - new (state) QTextCodec::ConverterState; -} - -static void copyConverterStateHelper(QTextCodec::ConverterState *dest, - const QTextCodec::ConverterState *src) -{ - // ### QTextCodec::ConverterState's copy constructors and assignments are - // private. This function copies the structure manually. - Q_ASSERT(!src->d); - dest->flags = src->flags; - dest->invalidChars = src->invalidChars; - dest->state_data[0] = src->state_data[0]; - dest->state_data[1] = src->state_data[1]; - dest->state_data[2] = src->state_data[2]; -} -#endif - -void QTextStreamPrivate::Params::reset() -{ - realNumberPrecision = 6; - integerBase = 0; - fieldWidth = 0; - padChar = QLatin1Char(' '); - fieldAlignment = QTextStream::AlignRight; - realNumberNotation = QTextStream::SmartNotation; - numberFlags = 0; -} - -/*! - \internal -*/ -void QTextStreamPrivate::reset() -{ - params.reset(); - - device = 0; - deleteDevice = false; - string = 0; - stringOffset = 0; - stringOpenMode = QIODevice::NotOpen; - - readBufferOffset = 0; - readBufferStartDevicePos = 0; - lastTokenSize = 0; - -#ifndef QT_NO_TEXTCODEC - codec = QTextCodec::codecForLocale(); - resetCodecConverterStateHelper(&readConverterState); - resetCodecConverterStateHelper(&writeConverterState); - delete readConverterSavedState; - readConverterSavedState = 0; - writeConverterState.flags |= QTextCodec::IgnoreHeader; - autoDetectUnicode = true; -#endif -} - -/*! - \internal -*/ -bool QTextStreamPrivate::fillReadBuffer(qint64 maxBytes) -{ - // no buffer next to the QString itself; this function should only - // be called internally, for devices. - Q_ASSERT(!string); - Q_ASSERT(device); - - // handle text translation and bypass the Text flag in the device. - bool textModeEnabled = device->isTextModeEnabled(); - if (textModeEnabled) - device->setTextModeEnabled(false); - - // read raw data into a temporary buffer - char buf[QTEXTSTREAM_BUFFERSIZE]; - qint64 bytesRead = 0; -#if defined(Q_OS_WIN) - // On Windows, there is no non-blocking stdin - so we fall back to reading - // lines instead. If there is no QOBJECT, we read lines for all sequential - // devices; otherwise, we read lines only for stdin. - QFile *file = 0; - Q_UNUSED(file); - if (device->isSequential() -#if !defined(QT_NO_QOBJECT) - && (file = qobject_cast(device)) && file->handle() == 0 -#endif - ) { - if (maxBytes != -1) - bytesRead = device->readLine(buf, qMin(sizeof(buf), maxBytes)); - else - bytesRead = device->readLine(buf, sizeof(buf)); - } else -#endif - { - if (maxBytes != -1) - bytesRead = device->read(buf, qMin(sizeof(buf), maxBytes)); - else - bytesRead = device->read(buf, sizeof(buf)); - } - - // reset the Text flag. - if (textModeEnabled) - device->setTextModeEnabled(true); - - if (bytesRead <= 0) - return false; - -#ifndef QT_NO_TEXTCODEC - // codec auto detection, explicitly defaults to locale encoding if the - // codec has been set to 0. - if (!codec || autoDetectUnicode) { - autoDetectUnicode = false; - - codec = QTextCodec::codecForUtfText(QByteArray::fromRawData(buf, bytesRead), codec); - if (!codec) { - codec = QTextCodec::codecForLocale(); - writeConverterState.flags |= QTextCodec::IgnoreHeader; - } - } -#if defined (QTEXTSTREAM_DEBUG) - qDebug("QTextStreamPrivate::fillReadBuffer(), using %s codec", - codec ? codec->name().constData() : "no"); -#endif -#endif - -#if defined (QTEXTSTREAM_DEBUG) - qDebug("QTextStreamPrivate::fillReadBuffer(), device->read(\"%s\", %d) == %d", - qt_prettyDebug(buf, qMin(32,int(bytesRead)) , int(bytesRead)).constData(), int(sizeof(buf)), int(bytesRead)); -#endif - - int oldReadBufferSize = readBuffer.size(); -#ifndef QT_NO_TEXTCODEC - // convert to unicode - readBuffer += Q_LIKELY(codec) ? codec->toUnicode(buf, bytesRead, &readConverterState) - : QString::fromLatin1(buf, bytesRead); -#else - readBuffer += QString::fromLatin1(buf, bytesRead); -#endif - - // remove all '\r\n' in the string. - if (readBuffer.size() > oldReadBufferSize && textModeEnabled) { - QChar CR = QLatin1Char('\r'); - QChar *writePtr = readBuffer.data() + oldReadBufferSize; - QChar *readPtr = readBuffer.data() + oldReadBufferSize; - QChar *endPtr = readBuffer.data() + readBuffer.size(); - - int n = oldReadBufferSize; - if (readPtr < endPtr) { - // Cut-off to avoid unnecessary self-copying. - while (*readPtr++ != CR) { - ++n; - if (++writePtr == endPtr) - break; - } - } - while (readPtr < endPtr) { - QChar ch = *readPtr++; - if (ch != CR) { - *writePtr++ = ch; - } else { - if (n < readBufferOffset) - --readBufferOffset; - --bytesRead; - } - ++n; - } - readBuffer.resize(writePtr - readBuffer.data()); - } - -#if defined (QTEXTSTREAM_DEBUG) - qDebug("QTextStreamPrivate::fillReadBuffer() read %d bytes from device. readBuffer = [%s]", int(bytesRead), - qt_prettyDebug(readBuffer.toLatin1(), readBuffer.size(), readBuffer.size()).data()); -#endif - return true; -} - -/*! - \internal -*/ -void QTextStreamPrivate::resetReadBuffer() -{ - readBuffer.clear(); - readBufferOffset = 0; - readBufferStartDevicePos = (device ? device->pos() : 0); -} - -/*! - \internal -*/ -void QTextStreamPrivate::flushWriteBuffer() -{ - // no buffer next to the QString itself; this function should only - // be called internally, for devices. - if (string || !device) - return; - - // Stream went bye-bye already. Appending further data may succeed again, - // but would create a corrupted stream anyway. - if (status != QTextStream::Ok) - return; - - if (writeBuffer.isEmpty()) - return; - -#if defined (Q_OS_WIN) - // handle text translation and bypass the Text flag in the device. - bool textModeEnabled = device->isTextModeEnabled(); - if (textModeEnabled) { - device->setTextModeEnabled(false); - writeBuffer.replace(QLatin1Char('\n'), QLatin1String("\r\n")); - } -#endif - -#ifndef QT_NO_TEXTCODEC - if (!codec) - codec = QTextCodec::codecForLocale(); -#if defined (QTEXTSTREAM_DEBUG) - qDebug("QTextStreamPrivate::flushWriteBuffer(), using %s codec (%s generating BOM)", - codec ? codec->name().constData() : "no", - !codec || (writeConverterState.flags & QTextCodec::IgnoreHeader) ? "not" : ""); -#endif - - // convert from unicode to raw data - // codec might be null if we're already inside global destructors (QTestCodec::codecForLocale returned null) - QByteArray data = Q_LIKELY(codec) ? codec->fromUnicode(writeBuffer.data(), writeBuffer.size(), &writeConverterState) - : writeBuffer.toLatin1(); -#else - QByteArray data = writeBuffer.toLatin1(); -#endif - writeBuffer.clear(); - - // write raw data to the device - qint64 bytesWritten = device->write(data); -#if defined (QTEXTSTREAM_DEBUG) - qDebug("QTextStreamPrivate::flushWriteBuffer(), device->write(\"%s\") == %d", - qt_prettyDebug(data.constData(), qMin(data.size(),32), data.size()).constData(), int(bytesWritten)); -#endif - -#if defined (Q_OS_WIN) - // reset the text flag - if (textModeEnabled) - device->setTextModeEnabled(true); -#endif - - if (bytesWritten <= 0) { - status = QTextStream::WriteFailed; - return; - } - - // flush the file -#ifndef QT_NO_QOBJECT - QFileDevice *file = qobject_cast(device); - bool flushed = !file || file->flush(); -#else - bool flushed = true; -#endif - -#if defined (QTEXTSTREAM_DEBUG) - qDebug("QTextStreamPrivate::flushWriteBuffer() wrote %d bytes", - int(bytesWritten)); -#endif - if (!flushed || bytesWritten != qint64(data.size())) - status = QTextStream::WriteFailed; -} - -QString QTextStreamPrivate::read(int maxlen) -{ - QString ret; - if (string) { - lastTokenSize = qMin(maxlen, string->size() - stringOffset); - ret = string->mid(stringOffset, lastTokenSize); - } else { - while (readBuffer.size() - readBufferOffset < maxlen && fillReadBuffer()) ; - lastTokenSize = qMin(maxlen, readBuffer.size() - readBufferOffset); - ret = readBuffer.mid(readBufferOffset, lastTokenSize); - } - consumeLastToken(); - -#if defined (QTEXTSTREAM_DEBUG) - qDebug("QTextStreamPrivate::read() maxlen = %d, token length = %d", maxlen, ret.length()); -#endif - return ret; -} - -/*! - \internal - - Scans no more than \a maxlen QChars in the current buffer for the - first \a delimiter. Stores a pointer to the start offset of the - token in \a ptr, and the length in QChars in \a length. -*/ -bool QTextStreamPrivate::scan(const QChar **ptr, int *length, int maxlen, TokenDelimiter delimiter) -{ - int totalSize = 0; - int delimSize = 0; - bool consumeDelimiter = false; - bool foundToken = false; - int startOffset = device ? readBufferOffset : stringOffset; - QChar lastChar; - - bool canStillReadFromDevice = true; - do { - int endOffset; - const QChar *chPtr; - if (device) { - chPtr = readBuffer.constData(); - endOffset = readBuffer.size(); - } else { - chPtr = string->constData(); - endOffset = string->size(); - } - chPtr += startOffset; - - for (; !foundToken && startOffset < endOffset && (!maxlen || totalSize < maxlen); ++startOffset) { - const QChar ch = *chPtr++; - ++totalSize; - - switch (delimiter) { - case Space: - if (ch.isSpace()) { - foundToken = true; - delimSize = 1; - } - break; - case NotSpace: - if (!ch.isSpace()) { - foundToken = true; - delimSize = 1; - } - break; - case EndOfLine: - if (ch == QLatin1Char('\n')) { - foundToken = true; - delimSize = (lastChar == QLatin1Char('\r')) ? 2 : 1; - consumeDelimiter = true; - } - lastChar = ch; - break; - } - } - } while (!foundToken - && (!maxlen || totalSize < maxlen) - && (device && (canStillReadFromDevice = fillReadBuffer()))); - - if (totalSize == 0) { -#if defined (QTEXTSTREAM_DEBUG) - qDebug("QTextStreamPrivate::scan() reached the end of input."); -#endif - return false; - } - - // if we find a '\r' at the end of the data when reading lines, - // don't make it part of the line. - if (delimiter == EndOfLine && totalSize > 0 && !foundToken) { - if (((string && stringOffset + totalSize == string->size()) || (device && device->atEnd())) - && lastChar == QLatin1Char('\r')) { - consumeDelimiter = true; - ++delimSize; - } - } - - // set the read offset and length of the token - if (length) - *length = totalSize - delimSize; - if (ptr) - *ptr = readPtr(); - - // update last token size. the callee will call consumeLastToken() when - // done. - lastTokenSize = totalSize; - if (!consumeDelimiter) - lastTokenSize -= delimSize; - -#if defined (QTEXTSTREAM_DEBUG) - qDebug("QTextStreamPrivate::scan(%p, %p, %d, %x) token length = %d, delimiter = %d", - ptr, length, maxlen, (int)delimiter, totalSize - delimSize, delimSize); -#endif - return true; -} - -/*! - \internal -*/ -inline const QChar *QTextStreamPrivate::readPtr() const -{ - Q_ASSERT(readBufferOffset <= readBuffer.size()); - if (string) - return string->constData() + stringOffset; - return readBuffer.constData() + readBufferOffset; -} - -/*! - \internal -*/ -inline void QTextStreamPrivate::consumeLastToken() -{ - if (lastTokenSize) - consume(lastTokenSize); - lastTokenSize = 0; -} - -/*! - \internal -*/ -inline void QTextStreamPrivate::consume(int size) -{ -#if defined (QTEXTSTREAM_DEBUG) - qDebug("QTextStreamPrivate::consume(%d)", size); -#endif - if (string) { - stringOffset += size; - if (stringOffset > string->size()) - stringOffset = string->size(); - } else { - readBufferOffset += size; - if (readBufferOffset >= readBuffer.size()) { - readBufferOffset = 0; - readBuffer.clear(); - saveConverterState(device->pos()); - } else if (readBufferOffset > QTEXTSTREAM_BUFFERSIZE) { - readBuffer = readBuffer.remove(0,readBufferOffset); - readConverterSavedStateOffset += readBufferOffset; - readBufferOffset = 0; - } - } -} - -/*! - \internal -*/ -inline void QTextStreamPrivate::saveConverterState(qint64 newPos) -{ -#ifndef QT_NO_TEXTCODEC - if (readConverterState.d) { - // converter cannot be copied, so don't save anything - // don't update readBufferStartDevicePos either - return; - } - - if (!readConverterSavedState) - readConverterSavedState = new QTextCodec::ConverterState; - copyConverterStateHelper(readConverterSavedState, &readConverterState); -#endif - - readBufferStartDevicePos = newPos; - readConverterSavedStateOffset = 0; -} - -/*! - \internal -*/ -inline void QTextStreamPrivate::restoreToSavedConverterState() -{ -#ifndef QT_NO_TEXTCODEC - if (readConverterSavedState) { - // we have a saved state - // that means the converter can be copied - copyConverterStateHelper(&readConverterState, readConverterSavedState); - } else { - // the only state we could save was the initial - // so reset to that - resetCodecConverterStateHelper(&readConverterState); - } -#endif -} - -/*! - \internal -*/ -void QTextStreamPrivate::write(const QChar *data, int len) -{ - if (string) { - // ### What about seek()?? - string->append(data, len); - } else { - writeBuffer.append(data, len); - if (writeBuffer.size() > QTEXTSTREAM_BUFFERSIZE) - flushWriteBuffer(); - } -} - -/*! - \internal -*/ -inline void QTextStreamPrivate::write(QChar ch) -{ - if (string) { - // ### What about seek()?? - string->append(ch); - } else { - writeBuffer += ch; - if (writeBuffer.size() > QTEXTSTREAM_BUFFERSIZE) - flushWriteBuffer(); - } -} - -/*! - \internal -*/ -void QTextStreamPrivate::write(QLatin1String data) -{ - if (string) { - // ### What about seek()?? - string->append(data); - } else { - writeBuffer += data; - if (writeBuffer.size() > QTEXTSTREAM_BUFFERSIZE) - flushWriteBuffer(); - } -} - -/*! - \internal -*/ -void QTextStreamPrivate::writePadding(int len) -{ - if (string) { - // ### What about seek()?? - string->resize(string->size() + len, params.padChar); - } else { - writeBuffer.resize(writeBuffer.size() + len, params.padChar); - if (writeBuffer.size() > QTEXTSTREAM_BUFFERSIZE) - flushWriteBuffer(); - } -} - -/*! - \internal -*/ -inline bool QTextStreamPrivate::getChar(QChar *ch) -{ - if ((string && stringOffset == string->size()) - || (device && readBuffer.isEmpty() && !fillReadBuffer())) { - if (ch) - *ch = 0; - return false; - } - if (ch) - *ch = *readPtr(); - consume(1); - return true; -} - -/*! - \internal -*/ -inline void QTextStreamPrivate::ungetChar(QChar ch) -{ - if (string) { - if (stringOffset == 0) - string->prepend(ch); - else - (*string)[--stringOffset] = ch; - return; - } - - if (readBufferOffset == 0) { - readBuffer.prepend(ch); - return; - } - - readBuffer[--readBufferOffset] = ch; -} - -/*! - \internal -*/ -inline void QTextStreamPrivate::putChar(QChar ch) -{ - if (params.fieldWidth > 0) - putString(&ch, 1); - else - write(ch); -} - - -/*! - \internal -*/ -QTextStreamPrivate::PaddingResult QTextStreamPrivate::padding(int len) const -{ - Q_ASSERT(params.fieldWidth > len); // calling padding() when no padding is needed is an error - - int left = 0, right = 0; - - const int padSize = params.fieldWidth - len; - - switch (params.fieldAlignment) { - case QTextStream::AlignLeft: - right = padSize; - break; - case QTextStream::AlignRight: - case QTextStream::AlignAccountingStyle: - left = padSize; - break; - case QTextStream::AlignCenter: - left = padSize/2; - right = padSize - padSize/2; - break; - } - return { left, right }; -} - -/*! - \internal -*/ -void QTextStreamPrivate::putString(const QChar *data, int len, bool number) -{ - if (Q_UNLIKELY(params.fieldWidth > len)) { - - // handle padding: - - const PaddingResult pad = padding(len); - - if (params.fieldAlignment == QTextStream::AlignAccountingStyle && number) { - const QChar sign = len > 0 ? data[0] : QChar(); - if (sign == locale.negativeSign() || sign == locale.positiveSign()) { - // write the sign before the padding, then skip it later - write(&sign, 1); - ++data; - --len; - } - } - - writePadding(pad.left); - write(data, len); - writePadding(pad.right); - } else { - write(data, len); - } -} - -/*! - \internal -*/ -void QTextStreamPrivate::putString(QLatin1String data, bool number) -{ - if (Q_UNLIKELY(params.fieldWidth > data.size())) { - - // handle padding - - const PaddingResult pad = padding(data.size()); - - if (params.fieldAlignment == QTextStream::AlignAccountingStyle && number) { - const QChar sign = data.size() > 0 ? QLatin1Char(*data.data()) : QChar(); - if (sign == locale.negativeSign() || sign == locale.positiveSign()) { - // write the sign before the padding, then skip it later - write(&sign, 1); - data = QLatin1String(data.data() + 1, data.size() - 1); - } - } - - writePadding(pad.left); - write(data); - writePadding(pad.right); - } else { - write(data); - } -} - -/*! - Constructs a QTextStream. Before you can use it for reading or - writing, you must assign a device or a string. - - \sa setDevice(), setString() -*/ -QTextStream::QTextStream() - : d_ptr(new QTextStreamPrivate(this)) -{ -#if defined (QTEXTSTREAM_DEBUG) - qDebug("QTextStream::QTextStream()"); -#endif - Q_D(QTextStream); - d->status = Ok; -} - -/*! - Constructs a QTextStream that operates on \a device. -*/ -QTextStream::QTextStream(QIODevice *device) - : d_ptr(new QTextStreamPrivate(this)) -{ -#if defined (QTEXTSTREAM_DEBUG) - qDebug("QTextStream::QTextStream(QIODevice *device == *%p)", - device); -#endif - Q_D(QTextStream); - d->device = device; -#ifndef QT_NO_QOBJECT - d->deviceClosedNotifier.setupDevice(this, d->device); -#endif - d->status = Ok; -} - -/*! - Constructs a QTextStream that operates on \a string, using \a - openMode to define the open mode. -*/ -QTextStream::QTextStream(QString *string, QIODevice::OpenMode openMode) - : d_ptr(new QTextStreamPrivate(this)) -{ -#if defined (QTEXTSTREAM_DEBUG) - qDebug("QTextStream::QTextStream(QString *string == *%p, openMode = %d)", - string, int(openMode)); -#endif - Q_D(QTextStream); - d->string = string; - d->stringOpenMode = openMode; - d->status = Ok; -} - -/*! - Constructs a QTextStream that operates on \a array, using \a - openMode to define the open mode. Internally, the array is wrapped - by a QBuffer. -*/ -QTextStream::QTextStream(QByteArray *array, QIODevice::OpenMode openMode) - : d_ptr(new QTextStreamPrivate(this)) -{ -#if defined (QTEXTSTREAM_DEBUG) - qDebug("QTextStream::QTextStream(QByteArray *array == *%p, openMode = %d)", - array, int(openMode)); -#endif - Q_D(QTextStream); - d->device = new QBuffer(array); - d->device->open(openMode); - d->deleteDevice = true; -#ifndef QT_NO_QOBJECT - d->deviceClosedNotifier.setupDevice(this, d->device); -#endif - d->status = Ok; -} - -/*! - Constructs a QTextStream that operates on \a array, using \a - openMode to define the open mode. The array is accessed as - read-only, regardless of the values in \a openMode. - - This constructor is convenient for working on constant - strings. Example: - - \snippet code/src_corelib_io_qtextstream.cpp 3 -*/ -QTextStream::QTextStream(const QByteArray &array, QIODevice::OpenMode openMode) - : d_ptr(new QTextStreamPrivate(this)) -{ -#if defined (QTEXTSTREAM_DEBUG) - qDebug("QTextStream::QTextStream(const QByteArray &array == *(%p), openMode = %d)", - &array, int(openMode)); -#endif - QBuffer *buffer = new QBuffer; - buffer->setData(array); - buffer->open(openMode); - - Q_D(QTextStream); - d->device = buffer; - d->deleteDevice = true; -#ifndef QT_NO_QOBJECT - d->deviceClosedNotifier.setupDevice(this, d->device); -#endif - d->status = Ok; -} - -/*! - Constructs a QTextStream that operates on \a fileHandle, using \a - openMode to define the open mode. Internally, a QFile is created - to handle the FILE pointer. - - This constructor is useful for working directly with the common - FILE based input and output streams: stdin, stdout and stderr. Example: - - \snippet code/src_corelib_io_qtextstream.cpp 4 -*/ - -QTextStream::QTextStream(FILE *fileHandle, QIODevice::OpenMode openMode) - : d_ptr(new QTextStreamPrivate(this)) -{ -#if defined (QTEXTSTREAM_DEBUG) - qDebug("QTextStream::QTextStream(FILE *fileHandle = %p, openMode = %d)", - fileHandle, int(openMode)); -#endif - QFile *file = new QFile; - file->open(fileHandle, openMode); - - Q_D(QTextStream); - d->device = file; - d->deleteDevice = true; -#ifndef QT_NO_QOBJECT - d->deviceClosedNotifier.setupDevice(this, d->device); -#endif - d->status = Ok; -} - -/*! - Destroys the QTextStream. - - If the stream operates on a device, flush() will be called - implicitly. Otherwise, the device is unaffected. -*/ -QTextStream::~QTextStream() -{ - Q_D(QTextStream); -#if defined (QTEXTSTREAM_DEBUG) - qDebug("QTextStream::~QTextStream()"); -#endif - if (!d->writeBuffer.isEmpty()) - d->flushWriteBuffer(); -} - -/*! - Resets QTextStream's formatting options, bringing it back to its - original constructed state. The device, string and any buffered - data is left untouched. -*/ -void QTextStream::reset() -{ - Q_D(QTextStream); - - d->params.reset(); -} - -/*! - Flushes any buffered data waiting to be written to the device. - - If QTextStream operates on a string, this function does nothing. -*/ -void QTextStream::flush() -{ - Q_D(QTextStream); - d->flushWriteBuffer(); -} - -/*! - Seeks to the position \a pos in the device. Returns \c true on - success; otherwise returns \c false. -*/ -bool QTextStream::seek(qint64 pos) -{ - Q_D(QTextStream); - d->lastTokenSize = 0; - - if (d->device) { - // Empty the write buffer - d->flushWriteBuffer(); - if (!d->device->seek(pos)) - return false; - d->resetReadBuffer(); - -#ifndef QT_NO_TEXTCODEC - // Reset the codec converter states. - resetCodecConverterStateHelper(&d->readConverterState); - resetCodecConverterStateHelper(&d->writeConverterState); - delete d->readConverterSavedState; - d->readConverterSavedState = 0; - d->writeConverterState.flags |= QTextCodec::IgnoreHeader; -#endif - return true; - } - - // string - if (d->string && pos <= d->string->size()) { - d->stringOffset = int(pos); - return true; - } - return false; -} - -/*! - \since 4.2 - - Returns the device position corresponding to the current position of the - stream, or -1 if an error occurs (e.g., if there is no device or string, - or if there's a device error). - - Because QTextStream is buffered, this function may have to - seek the device to reconstruct a valid device position. This - operation can be expensive, so you may want to avoid calling this - function in a tight loop. - - \sa seek() -*/ -qint64 QTextStream::pos() const -{ - Q_D(const QTextStream); - if (d->device) { - // Cutoff - if (d->readBuffer.isEmpty()) - return d->device->pos(); - if (d->device->isSequential()) - return 0; - - // Seek the device - if (!d->device->seek(d->readBufferStartDevicePos)) - return qint64(-1); - - // Reset the read buffer - QTextStreamPrivate *thatd = const_cast(d); - thatd->readBuffer.clear(); - -#ifndef QT_NO_TEXTCODEC - thatd->restoreToSavedConverterState(); - if (d->readBufferStartDevicePos == 0) - thatd->autoDetectUnicode = true; -#endif - - // Rewind the device to get to the current position Ensure that - // readBufferOffset is unaffected by fillReadBuffer() - int oldReadBufferOffset = d->readBufferOffset + d->readConverterSavedStateOffset; - while (d->readBuffer.size() < oldReadBufferOffset) { - if (!thatd->fillReadBuffer(1)) - return qint64(-1); - } - thatd->readBufferOffset = oldReadBufferOffset; - thatd->readConverterSavedStateOffset = 0; - - // Return the device position. - return d->device->pos(); - } - - if (d->string) - return d->stringOffset; - - qWarning("QTextStream::pos: no device"); - return qint64(-1); -} - -/*! - Reads and discards whitespace from the stream until either a - non-space character is detected, or until atEnd() returns - true. This function is useful when reading a stream character by - character. - - Whitespace characters are all characters for which - QChar::isSpace() returns \c true. - - \sa operator>>() -*/ -void QTextStream::skipWhiteSpace() -{ - Q_D(QTextStream); - CHECK_VALID_STREAM(Q_VOID); - d->scan(0, 0, 0, QTextStreamPrivate::NotSpace); - d->consumeLastToken(); -} - -/*! - Sets the current device to \a device. If a device has already been - assigned, QTextStream will call flush() before the old device is - replaced. - - \note This function resets locale to the default locale ('C') - and codec to the default codec, QTextCodec::codecForLocale(). - - \sa device(), setString() -*/ -void QTextStream::setDevice(QIODevice *device) -{ - Q_D(QTextStream); - flush(); - if (d->deleteDevice) { -#ifndef QT_NO_QOBJECT - d->deviceClosedNotifier.disconnect(); -#endif - delete d->device; - d->deleteDevice = false; - } - - d->reset(); - d->status = Ok; - d->device = device; - d->resetReadBuffer(); -#ifndef QT_NO_QOBJECT - d->deviceClosedNotifier.setupDevice(this, d->device); -#endif -} - -/*! - Returns the current device associated with the QTextStream, - or 0 if no device has been assigned. - - \sa setDevice(), string() -*/ -QIODevice *QTextStream::device() const -{ - Q_D(const QTextStream); - return d->device; -} - -/*! - Sets the current string to \a string, using the given \a - openMode. If a device has already been assigned, QTextStream will - call flush() before replacing it. - - \sa string(), setDevice() -*/ -void QTextStream::setString(QString *string, QIODevice::OpenMode openMode) -{ - Q_D(QTextStream); - flush(); - if (d->deleteDevice) { -#ifndef QT_NO_QOBJECT - d->deviceClosedNotifier.disconnect(); - d->device->blockSignals(true); -#endif - delete d->device; - d->deleteDevice = false; - } - - d->reset(); - d->status = Ok; - d->string = string; - d->stringOpenMode = openMode; -} - -/*! - Returns the current string assigned to the QTextStream, or 0 if no - string has been assigned. - - \sa setString(), device() -*/ -QString *QTextStream::string() const -{ - Q_D(const QTextStream); - return d->string; -} - -/*! - Sets the field alignment to \a mode. When used together with - setFieldWidth(), this function allows you to generate formatted - output with text aligned to the left, to the right or center - aligned. - - \sa fieldAlignment(), setFieldWidth() -*/ -void QTextStream::setFieldAlignment(FieldAlignment mode) -{ - Q_D(QTextStream); - d->params.fieldAlignment = mode; -} - -/*! - Returns the current field alignment. - - \sa setFieldAlignment(), fieldWidth() -*/ -QTextStream::FieldAlignment QTextStream::fieldAlignment() const -{ - Q_D(const QTextStream); - return d->params.fieldAlignment; -} - -/*! - Sets the pad character to \a ch. The default value is the ASCII - space character (' '), or QChar(0x20). This character is used to - fill in the space in fields when generating text. - - Example: - - \snippet code/src_corelib_io_qtextstream.cpp 5 - - The string \c s contains: - - \snippet code/src_corelib_io_qtextstream.cpp 6 - - \sa padChar(), setFieldWidth() -*/ -void QTextStream::setPadChar(QChar ch) -{ - Q_D(QTextStream); - d->params.padChar = ch; -} - -/*! - Returns the current pad character. - - \sa setPadChar(), setFieldWidth() -*/ -QChar QTextStream::padChar() const -{ - Q_D(const QTextStream); - return d->params.padChar; -} - -/*! - Sets the current field width to \a width. If \a width is 0 (the - default), the field width is equal to the length of the generated - text. - - \note The field width applies to every element appended to this - stream after this function has been called (e.g., it also pads - endl). This behavior is different from similar classes in the STL, - where the field width only applies to the next element. - - \sa fieldWidth(), setPadChar() -*/ -void QTextStream::setFieldWidth(int width) -{ - Q_D(QTextStream); - d->params.fieldWidth = width; -} - -/*! - Returns the current field width. - - \sa setFieldWidth() -*/ -int QTextStream::fieldWidth() const -{ - Q_D(const QTextStream); - return d->params.fieldWidth; -} - -/*! - Sets the current number flags to \a flags. \a flags is a set of - flags from the NumberFlag enum, and describes options for - formatting generated code (e.g., whether or not to always write - the base or sign of a number). - - \sa numberFlags(), setIntegerBase(), setRealNumberNotation() -*/ -void QTextStream::setNumberFlags(NumberFlags flags) -{ - Q_D(QTextStream); - d->params.numberFlags = flags; -} - -/*! - Returns the current number flags. - - \sa setNumberFlags(), integerBase(), realNumberNotation() -*/ -QTextStream::NumberFlags QTextStream::numberFlags() const -{ - Q_D(const QTextStream); - return d->params.numberFlags; -} - -/*! - Sets the base of integers to \a base, both for reading and for - generating numbers. \a base can be either 2 (binary), 8 (octal), - 10 (decimal) or 16 (hexadecimal). If \a base is 0, QTextStream - will attempt to detect the base by inspecting the data on the - stream. When generating numbers, QTextStream assumes base is 10 - unless the base has been set explicitly. - - \sa integerBase(), QString::number(), setNumberFlags() -*/ -void QTextStream::setIntegerBase(int base) -{ - Q_D(QTextStream); - d->params.integerBase = base; -} - -/*! - Returns the current base of integers. 0 means that the base is - detected when reading, or 10 (decimal) when generating numbers. - - \sa setIntegerBase(), QString::number(), numberFlags() -*/ -int QTextStream::integerBase() const -{ - Q_D(const QTextStream); - return d->params.integerBase; -} - -/*! - Sets the real number notation to \a notation (SmartNotation, - FixedNotation, ScientificNotation). When reading and generating - numbers, QTextStream uses this value to detect the formatting of - real numbers. - - \sa realNumberNotation(), setRealNumberPrecision(), setNumberFlags(), setIntegerBase() -*/ -void QTextStream::setRealNumberNotation(RealNumberNotation notation) -{ - Q_D(QTextStream); - d->params.realNumberNotation = notation; -} - -/*! - Returns the current real number notation. - - \sa setRealNumberNotation(), realNumberPrecision(), numberFlags(), integerBase() -*/ -QTextStream::RealNumberNotation QTextStream::realNumberNotation() const -{ - Q_D(const QTextStream); - return d->params.realNumberNotation; -} - -/*! - Sets the precision of real numbers to \a precision. This value - describes the number of fraction digits QTextStream should - write when generating real numbers. - - The precision cannot be a negative value. The default value is 6. - - \sa realNumberPrecision(), setRealNumberNotation() -*/ -void QTextStream::setRealNumberPrecision(int precision) -{ - Q_D(QTextStream); - if (precision < 0) { - qWarning("QTextStream::setRealNumberPrecision: Invalid precision (%d)", precision); - d->params.realNumberPrecision = 6; - return; - } - d->params.realNumberPrecision = precision; -} - -/*! - Returns the current real number precision, or the number of fraction - digits QTextStream will write when generating real numbers. - - \sa setRealNumberNotation(), realNumberNotation(), numberFlags(), integerBase() -*/ -int QTextStream::realNumberPrecision() const -{ - Q_D(const QTextStream); - return d->params.realNumberPrecision; -} - -/*! - Returns the status of the text stream. - - \sa QTextStream::Status, setStatus(), resetStatus() -*/ - -QTextStream::Status QTextStream::status() const -{ - Q_D(const QTextStream); - return d->status; -} - -/*! - \since 4.1 - - Resets the status of the text stream. - - \sa QTextStream::Status, status(), setStatus() -*/ -void QTextStream::resetStatus() -{ - Q_D(QTextStream); - d->status = Ok; -} - -/*! - \since 4.1 - - Sets the status of the text stream to the \a status given. - - Subsequent calls to setStatus() are ignored until resetStatus() - is called. - - \sa Status, status(), resetStatus() -*/ -void QTextStream::setStatus(Status status) -{ - Q_D(QTextStream); - if (d->status == Ok) - d->status = status; -} - -/*! - Returns \c true if there is no more data to be read from the - QTextStream; otherwise returns \c false. This is similar to, but not - the same as calling QIODevice::atEnd(), as QTextStream also takes - into account its internal Unicode buffer. -*/ -bool QTextStream::atEnd() const -{ - Q_D(const QTextStream); - CHECK_VALID_STREAM(true); - - if (d->string) - return d->string->size() == d->stringOffset; - return d->readBuffer.isEmpty() && d->device->atEnd(); -} - -/*! - Reads the entire content of the stream, and returns it as a - QString. Avoid this function when working on large files, as it - will consume a significant amount of memory. - - Calling \l {QTextStream::readLine()}{readLine()} is better if you do not know how much data is - available. - - \sa readLine() -*/ -QString QTextStream::readAll() -{ - Q_D(QTextStream); - CHECK_VALID_STREAM(QString()); - - return d->read(INT_MAX); -} - -/*! - Reads one line of text from the stream, and returns it as a - QString. The maximum allowed line length is set to \a maxlen. If - the stream contains lines longer than this, then the lines will be - split after \a maxlen characters and returned in parts. - - If \a maxlen is 0, the lines can be of any length. - - The returned line has no trailing end-of-line characters ("\\n" - or "\\r\\n"), so calling QString::trimmed() can be unnecessary. - - If the stream has read to the end of the file, \l {QTextStream::readLine()}{readLine()} - will return a null QString. For strings, or for devices that support it, - you can explicitly test for the end of the stream using atEnd(). - - \sa readAll(), QIODevice::readLine() -*/ -QString QTextStream::readLine(qint64 maxlen) -{ - QString line; - - readLineInto(&line, maxlen); - return line; -} - -/*! - \since 5.5 - - Reads one line of text from the stream into \a line. - If \a line is 0, the read line is not stored. - - The maximum allowed line length is set to \a maxlen. If - the stream contains lines longer than this, then the lines will be - split after \a maxlen characters and returned in parts. - - If \a maxlen is 0, the lines can be of any length. - - The resulting line has no trailing end-of-line characters ("\\n" - or "\\r\\n"), so calling QString::trimmed() can be unnecessary. - - If \a line has sufficient capacity for the data that is about to be - read, this function may not need to allocate new memory. Because of - this, it can be faster than readLine(). - - Returns \c false if the stream has read to the end of the file or - an error has occurred; otherwise returns \c true. The contents in - \a line before the call are discarded in any case. - - \sa readAll(), QIODevice::readLine() -*/ -bool QTextStream::readLineInto(QString *line, qint64 maxlen) -{ - Q_D(QTextStream); - // keep in sync with CHECK_VALID_STREAM - if (!d->string && !d->device) { - qWarning("QTextStream: No device"); - if (line && !line->isNull()) - line->resize(0); - return false; - } - - const QChar *readPtr; - int length; - if (!d->scan(&readPtr, &length, int(maxlen), QTextStreamPrivate::EndOfLine)) { - if (line && !line->isNull()) - line->resize(0); - return false; - } - - if (Q_LIKELY(line)) - line->setUnicode(readPtr, length); - d->consumeLastToken(); - return true; -} - -/*! - \since 4.1 - - Reads at most \a maxlen characters from the stream, and returns the data - read as a QString. - - \sa readAll(), readLine(), QIODevice::read() -*/ -QString QTextStream::read(qint64 maxlen) -{ - Q_D(QTextStream); - CHECK_VALID_STREAM(QString()); - - if (maxlen <= 0) - return QString::fromLatin1(""); // empty, not null - - return d->read(int(maxlen)); -} - -/*! - \internal -*/ -QTextStreamPrivate::NumberParsingStatus QTextStreamPrivate::getNumber(qulonglong *ret) -{ - scan(0, 0, 0, NotSpace); - consumeLastToken(); - - // detect int encoding - int base = params.integerBase; - if (base == 0) { - QChar ch; - if (!getChar(&ch)) - return npsInvalidPrefix; - if (ch == QLatin1Char('0')) { - QChar ch2; - if (!getChar(&ch2)) { - // Result is the number 0 - *ret = 0; - return npsOk; - } - ch2 = ch2.toLower(); - - if (ch2 == QLatin1Char('x')) { - base = 16; - } else if (ch2 == QLatin1Char('b')) { - base = 2; - } else if (ch2.isDigit() && ch2.digitValue() >= 0 && ch2.digitValue() <= 7) { - base = 8; - } else { - base = 10; - } - ungetChar(ch2); - } else if (ch == locale.negativeSign() || ch == locale.positiveSign() || ch.isDigit()) { - base = 10; - } else { - ungetChar(ch); - return npsInvalidPrefix; - } - ungetChar(ch); - // State of the stream is now the same as on entry - // (cursor is at prefix), - // and local variable 'base' has been set appropriately. - } - - qulonglong val=0; - switch (base) { - case 2: { - QChar pf1, pf2, dig; - // Parse prefix '0b' - if (!getChar(&pf1) || pf1 != QLatin1Char('0')) - return npsInvalidPrefix; - if (!getChar(&pf2) || pf2.toLower() != QLatin1Char('b')) - return npsInvalidPrefix; - // Parse digits - int ndigits = 0; - while (getChar(&dig)) { - int n = dig.toLower().unicode(); - if (n == '0' || n == '1') { - val <<= 1; - val += n - '0'; - } else { - ungetChar(dig); - break; - } - ndigits++; - } - if (ndigits == 0) { - // Unwind the prefix and abort - ungetChar(pf2); - ungetChar(pf1); - return npsMissingDigit; - } - break; - } - case 8: { - QChar pf, dig; - // Parse prefix '0' - if (!getChar(&pf) || pf != QLatin1Char('0')) - return npsInvalidPrefix; - // Parse digits - int ndigits = 0; - while (getChar(&dig)) { - int n = dig.toLower().unicode(); - if (n >= '0' && n <= '7') { - val *= 8; - val += n - '0'; - } else { - ungetChar(dig); - break; - } - ndigits++; - } - if (ndigits == 0) { - // Unwind the prefix and abort - ungetChar(pf); - return npsMissingDigit; - } - break; - } - case 10: { - // Parse sign (or first digit) - QChar sign; - int ndigits = 0; - if (!getChar(&sign)) - return npsMissingDigit; - if (sign != locale.negativeSign() && sign != locale.positiveSign()) { - if (!sign.isDigit()) { - ungetChar(sign); - return npsMissingDigit; - } - val += sign.digitValue(); - ndigits++; - } - // Parse digits - QChar ch; - while (getChar(&ch)) { - if (ch.isDigit()) { - val *= 10; - val += ch.digitValue(); - } else if (locale != QLocale::c() && ch == locale.groupSeparator()) { - continue; - } else { - ungetChar(ch); - break; - } - ndigits++; - } - if (ndigits == 0) - return npsMissingDigit; - if (sign == locale.negativeSign()) { - qlonglong ival = qlonglong(val); - if (ival > 0) - ival = -ival; - val = qulonglong(ival); - } - break; - } - case 16: { - QChar pf1, pf2, dig; - // Parse prefix ' 0x' - if (!getChar(&pf1) || pf1 != QLatin1Char('0')) - return npsInvalidPrefix; - if (!getChar(&pf2) || pf2.toLower() != QLatin1Char('x')) - return npsInvalidPrefix; - // Parse digits - int ndigits = 0; - while (getChar(&dig)) { - int n = dig.toLower().unicode(); - if (n >= '0' && n <= '9') { - val <<= 4; - val += n - '0'; - } else if (n >= 'a' && n <= 'f') { - val <<= 4; - val += 10 + (n - 'a'); - } else { - ungetChar(dig); - break; - } - ndigits++; - } - if (ndigits == 0) { - return npsMissingDigit; - } - break; - } - default: - // Unsupported integerBase - return npsInvalidPrefix; - } - - if (ret) - *ret = val; - return npsOk; -} - -/*! - \internal - (hihi) -*/ -bool QTextStreamPrivate::getReal(double *f) -{ - // We use a table-driven FSM to parse floating point numbers - // strtod() cannot be used directly since we may be reading from a - // QIODevice. - enum ParserState { - Init = 0, - Sign = 1, - Mantissa = 2, - Dot = 3, - Abscissa = 4, - ExpMark = 5, - ExpSign = 6, - Exponent = 7, - Nan1 = 8, - Nan2 = 9, - Inf1 = 10, - Inf2 = 11, - NanInf = 12, - Done = 13 - }; - enum InputToken { - None = 0, - InputSign = 1, - InputDigit = 2, - InputDot = 3, - InputExp = 4, - InputI = 5, - InputN = 6, - InputF = 7, - InputA = 8, - InputT = 9 - }; - - static const uchar table[13][10] = { - // None InputSign InputDigit InputDot InputExp InputI InputN InputF InputA InputT - { 0, Sign, Mantissa, Dot, 0, Inf1, Nan1, 0, 0, 0 }, // 0 Init - { 0, 0, Mantissa, Dot, 0, Inf1, Nan1, 0, 0, 0 }, // 1 Sign - { Done, Done, Mantissa, Dot, ExpMark, 0, 0, 0, 0, 0 }, // 2 Mantissa - { 0, 0, Abscissa, 0, 0, 0, 0, 0, 0, 0 }, // 3 Dot - { Done, Done, Abscissa, Done, ExpMark, 0, 0, 0, 0, 0 }, // 4 Abscissa - { 0, ExpSign, Exponent, 0, 0, 0, 0, 0, 0, 0 }, // 5 ExpMark - { 0, 0, Exponent, 0, 0, 0, 0, 0, 0, 0 }, // 6 ExpSign - { Done, Done, Exponent, Done, Done, 0, 0, 0, 0, 0 }, // 7 Exponent - { 0, 0, 0, 0, 0, 0, 0, 0, Nan2, 0 }, // 8 Nan1 - { 0, 0, 0, 0, 0, 0, NanInf, 0, 0, 0 }, // 9 Nan2 - { 0, 0, 0, 0, 0, 0, Inf2, 0, 0, 0 }, // 10 Inf1 - { 0, 0, 0, 0, 0, 0, 0, NanInf, 0, 0 }, // 11 Inf2 - { Done, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // 11 NanInf - }; - - ParserState state = Init; - InputToken input = None; - - scan(0, 0, 0, NotSpace); - consumeLastToken(); - - const int BufferSize = 128; - char buf[BufferSize]; - int i = 0; - - QChar c; - while (getChar(&c)) { - switch (c.unicode()) { - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - input = InputDigit; - break; - case 'i': case 'I': - input = InputI; - break; - case 'n': case 'N': - input = InputN; - break; - case 'f': case 'F': - input = InputF; - break; - case 'a': case 'A': - input = InputA; - break; - case 't': case 'T': - input = InputT; - break; - default: { - QChar lc = c.toLower(); - if (lc == locale.decimalPoint().toLower()) - input = InputDot; - else if (lc == locale.exponential().toLower()) - input = InputExp; - else if (lc == locale.negativeSign().toLower() - || lc == locale.positiveSign().toLower()) - input = InputSign; - else if (locale != QLocale::c() // backward-compatibility - && lc == locale.groupSeparator().toLower()) - input = InputDigit; // well, it isn't a digit, but no one cares. - else - input = None; - } - break; - } - - state = ParserState(table[state][input]); - - if (state == Init || state == Done || i > (BufferSize - 5)) { - ungetChar(c); - if (i > (BufferSize - 5)) { // ignore rest of digits - while (getChar(&c)) { - if (!c.isDigit()) { - ungetChar(c); - break; - } - } - } - break; - } - - buf[i++] = c.toLatin1(); - } - - if (i == 0) - return false; - if (!f) - return true; - buf[i] = '\0'; - - // backward-compatibility. Old implementation supported +nan/-nan - // for some reason. QLocale only checks for lower-case - // nan/+inf/-inf, so here we also check for uppercase and mixed - // case versions. - if (!qstricmp(buf, "nan") || !qstricmp(buf, "+nan") || !qstricmp(buf, "-nan")) { - *f = qSNaN(); - return true; - } else if (!qstricmp(buf, "+inf") || !qstricmp(buf, "inf")) { - *f = qInf(); - return true; - } else if (!qstricmp(buf, "-inf")) { - *f = -qInf(); - return true; - } - bool ok; - *f = locale.toDouble(QString::fromLatin1(buf), &ok); - return ok; -} - -/*! - Reads a character from the stream and stores it in \a c. Returns a - reference to the QTextStream, so several operators can be - nested. Example: - - \snippet code/src_corelib_io_qtextstream.cpp 7 - - Whitespace is \e not skipped. -*/ - -QTextStream &QTextStream::operator>>(QChar &c) -{ - Q_D(QTextStream); - CHECK_VALID_STREAM(*this); - d->scan(0, 0, 0, QTextStreamPrivate::NotSpace); - if (!d->getChar(&c)) - setStatus(ReadPastEnd); - return *this; -} - -/*! - \overload - - Reads a character from the stream and stores it in \a c. The - character from the stream is converted to ISO-5589-1 before it is - stored. - - \sa QChar::toLatin1() -*/ -QTextStream &QTextStream::operator>>(char &c) -{ - QChar ch; - *this >> ch; - c = ch.toLatin1(); - return *this; -} - -/*! - Reads an integer from the stream and stores it in \a i, then - returns a reference to the QTextStream. The number is cast to - the correct type before it is stored. If no number was detected on - the stream, \a i is set to 0. - - By default, QTextStream will attempt to detect the base of the - number using the following rules: - - \table - \header \li Prefix \li Base - \row \li "0b" or "0B" \li 2 (binary) - \row \li "0" followed by "0-7" \li 8 (octal) - \row \li "0" otherwise \li 10 (decimal) - \row \li "0x" or "0X" \li 16 (hexadecimal) - \row \li "1" to "9" \li 10 (decimal) - \endtable - - By calling setIntegerBase(), you can specify the integer base - explicitly. This will disable the auto-detection, and speed up - QTextStream slightly. - - Leading whitespace is skipped. -*/ -QTextStream &QTextStream::operator>>(signed short &i) -{ - IMPLEMENT_STREAM_RIGHT_INT_OPERATOR(signed short); -} - -/*! - \overload - - Stores the integer in the unsigned short \a i. -*/ -QTextStream &QTextStream::operator>>(unsigned short &i) -{ - IMPLEMENT_STREAM_RIGHT_INT_OPERATOR(unsigned short); -} - -/*! - \overload - - Stores the integer in the signed int \a i. -*/ -QTextStream &QTextStream::operator>>(signed int &i) -{ - IMPLEMENT_STREAM_RIGHT_INT_OPERATOR(signed int); -} - -/*! - \overload - - Stores the integer in the unsigned int \a i. -*/ -QTextStream &QTextStream::operator>>(unsigned int &i) -{ - IMPLEMENT_STREAM_RIGHT_INT_OPERATOR(unsigned int); -} - -/*! - \overload - - Stores the integer in the signed long \a i. -*/ -QTextStream &QTextStream::operator>>(signed long &i) -{ - IMPLEMENT_STREAM_RIGHT_INT_OPERATOR(signed long); -} - -/*! - \overload - - Stores the integer in the unsigned long \a i. -*/ -QTextStream &QTextStream::operator>>(unsigned long &i) -{ - IMPLEMENT_STREAM_RIGHT_INT_OPERATOR(unsigned long); -} - -/*! - \overload - - Stores the integer in the qlonglong \a i. -*/ -QTextStream &QTextStream::operator>>(qlonglong &i) -{ - IMPLEMENT_STREAM_RIGHT_INT_OPERATOR(qlonglong); -} - -/*! - \overload - - Stores the integer in the qulonglong \a i. -*/ -QTextStream &QTextStream::operator>>(qulonglong &i) -{ - IMPLEMENT_STREAM_RIGHT_INT_OPERATOR(qulonglong); -} - -/*! - Reads a real number from the stream and stores it in \a f, then - returns a reference to the QTextStream. The number is cast to - the correct type. If no real number is detect on the stream, \a f - is set to 0.0. - - As a special exception, QTextStream allows the strings "nan" and "inf" to - represent NAN and INF floats or doubles. - - Leading whitespace is skipped. -*/ -QTextStream &QTextStream::operator>>(float &f) -{ - IMPLEMENT_STREAM_RIGHT_REAL_OPERATOR(float); -} - -/*! - \overload - - Stores the real number in the double \a f. -*/ -QTextStream &QTextStream::operator>>(double &f) -{ - IMPLEMENT_STREAM_RIGHT_REAL_OPERATOR(double); -} - -/*! - Reads a word from the stream and stores it in \a str, then returns - a reference to the stream. Words are separated by whitespace - (i.e., all characters for which QChar::isSpace() returns \c true). - - Leading whitespace is skipped. -*/ -QTextStream &QTextStream::operator>>(QString &str) -{ - Q_D(QTextStream); - CHECK_VALID_STREAM(*this); - - str.clear(); - d->scan(0, 0, 0, QTextStreamPrivate::NotSpace); - d->consumeLastToken(); - - const QChar *ptr; - int length; - if (!d->scan(&ptr, &length, 0, QTextStreamPrivate::Space)) { - setStatus(ReadPastEnd); - return *this; - } - - str = QString(ptr, length); - d->consumeLastToken(); - return *this; -} - -/*! - \overload - - Converts the word to ISO-8859-1, then stores it in \a array. - - \sa QString::toLatin1() -*/ -QTextStream &QTextStream::operator>>(QByteArray &array) -{ - Q_D(QTextStream); - CHECK_VALID_STREAM(*this); - - array.clear(); - d->scan(0, 0, 0, QTextStreamPrivate::NotSpace); - d->consumeLastToken(); - - const QChar *ptr; - int length; - if (!d->scan(&ptr, &length, 0, QTextStreamPrivate::Space)) { - setStatus(ReadPastEnd); - return *this; - } - - for (int i = 0; i < length; ++i) - array += ptr[i].toLatin1(); - - d->consumeLastToken(); - return *this; -} - -/*! - \overload - - Stores the word in \a c, terminated by a '\\0' character. If no word is - available, only the '\\0' character is stored. - - Warning: Although convenient, this operator is dangerous and must - be used with care. QTextStream assumes that \a c points to a - buffer with enough space to hold the word. If the buffer is too - small, your application may crash. - - If possible, use the QByteArray operator instead. -*/ -QTextStream &QTextStream::operator>>(char *c) -{ - Q_D(QTextStream); - *c = 0; - CHECK_VALID_STREAM(*this); - d->scan(0, 0, 0, QTextStreamPrivate::NotSpace); - d->consumeLastToken(); - - const QChar *ptr; - int length; - if (!d->scan(&ptr, &length, 0, QTextStreamPrivate::Space)) { - setStatus(ReadPastEnd); - return *this; - } - - for (int i = 0; i < length; ++i) - *c++ = ptr[i].toLatin1(); - *c = '\0'; - d->consumeLastToken(); - return *this; -} - -/*! - \internal - */ -void QTextStreamPrivate::putNumber(qulonglong number, bool negative) -{ - QString result; - - unsigned flags = 0; - const QTextStream::NumberFlags numberFlags = params.numberFlags; - if (numberFlags & QTextStream::ShowBase) - flags |= QLocaleData::ShowBase; - if (numberFlags & QTextStream::ForceSign) - flags |= QLocaleData::AlwaysShowSign; - if (numberFlags & QTextStream::UppercaseBase) - flags |= QLocaleData::UppercaseBase; - if (numberFlags & QTextStream::UppercaseDigits) - flags |= QLocaleData::CapitalEorX; - - // add thousands group separators. For backward compatibility we - // don't add a group separator for C locale. - if (locale != QLocale::c() && !locale.numberOptions().testFlag(QLocale::OmitGroupSeparator)) - flags |= QLocaleData::ThousandsGroup; - - const QLocaleData *dd = locale.d->m_data; - int base = params.integerBase ? params.integerBase : 10; - if (negative && base == 10) { - result = dd->longLongToString(-static_cast(number), -1, - base, -1, flags); - } else if (negative) { - // Workaround for backward compatibility for writing negative - // numbers in octal and hex: - // QTextStream(result) << showbase << hex << -1 << oct << -1 - // should output: -0x1 -0b1 - result = dd->unsLongLongToString(number, -1, base, -1, flags); - result.prepend(locale.negativeSign()); - } else { - result = dd->unsLongLongToString(number, -1, base, -1, flags); - // workaround for backward compatibility - in octal form with - // ShowBase flag set zero should be written as '00' - if (number == 0 && base == 8 && params.numberFlags & QTextStream::ShowBase - && result == QLatin1String("0")) { - result.prepend(QLatin1Char('0')); - } - } - putString(result, true); -} - -/*! - Writes the character \a c to the stream, then returns a reference - to the QTextStream. - - \sa setFieldWidth() -*/ -QTextStream &QTextStream::operator<<(QChar c) -{ - Q_D(QTextStream); - CHECK_VALID_STREAM(*this); - d->putChar(c); - return *this; -} - -/*! - \overload - - Converts \a c from ASCII to a QChar, then writes it to the stream. -*/ -QTextStream &QTextStream::operator<<(char c) -{ - Q_D(QTextStream); - CHECK_VALID_STREAM(*this); - d->putChar(QChar::fromLatin1(c)); - return *this; -} - -/*! - Writes the integer number \a i to the stream, then returns a - reference to the QTextStream. By default, the number is stored in - decimal form, but you can also set the base by calling - setIntegerBase(). - - \sa setFieldWidth(), setNumberFlags() -*/ -QTextStream &QTextStream::operator<<(signed short i) -{ - Q_D(QTextStream); - CHECK_VALID_STREAM(*this); - d->putNumber((qulonglong)qAbs(qlonglong(i)), i < 0); - return *this; -} - -/*! - \overload - - Writes the unsigned short \a i to the stream. -*/ -QTextStream &QTextStream::operator<<(unsigned short i) -{ - Q_D(QTextStream); - CHECK_VALID_STREAM(*this); - d->putNumber((qulonglong)i, false); - return *this; -} - -/*! - \overload - - Writes the signed int \a i to the stream. -*/ -QTextStream &QTextStream::operator<<(signed int i) -{ - Q_D(QTextStream); - CHECK_VALID_STREAM(*this); - d->putNumber((qulonglong)qAbs(qlonglong(i)), i < 0); - return *this; -} - -/*! - \overload - - Writes the unsigned int \a i to the stream. -*/ -QTextStream &QTextStream::operator<<(unsigned int i) -{ - Q_D(QTextStream); - CHECK_VALID_STREAM(*this); - d->putNumber((qulonglong)i, false); - return *this; -} - -/*! - \overload - - Writes the signed long \a i to the stream. -*/ -QTextStream &QTextStream::operator<<(signed long i) -{ - Q_D(QTextStream); - CHECK_VALID_STREAM(*this); - d->putNumber((qulonglong)qAbs(qlonglong(i)), i < 0); - return *this; -} - -/*! - \overload - - Writes the unsigned long \a i to the stream. -*/ -QTextStream &QTextStream::operator<<(unsigned long i) -{ - Q_D(QTextStream); - CHECK_VALID_STREAM(*this); - d->putNumber((qulonglong)i, false); - return *this; -} - -/*! - \overload - - Writes the qlonglong \a i to the stream. -*/ -QTextStream &QTextStream::operator<<(qlonglong i) -{ - Q_D(QTextStream); - CHECK_VALID_STREAM(*this); - d->putNumber((qulonglong)qAbs(i), i < 0); - return *this; -} - -/*! - \overload - - Writes the qulonglong \a i to the stream. -*/ -QTextStream &QTextStream::operator<<(qulonglong i) -{ - Q_D(QTextStream); - CHECK_VALID_STREAM(*this); - d->putNumber(i, false); - return *this; -} - -/*! - Writes the real number \a f to the stream, then returns a - reference to the QTextStream. By default, QTextStream stores it - using SmartNotation, with up to 6 digits of precision. You can - change the textual representation QTextStream will use for real - numbers by calling setRealNumberNotation(), - setRealNumberPrecision() and setNumberFlags(). - - \sa setFieldWidth(), setRealNumberNotation(), - setRealNumberPrecision(), setNumberFlags() -*/ -QTextStream &QTextStream::operator<<(float f) -{ - return *this << double(f); -} - -/*! - \overload - - Writes the double \a f to the stream. -*/ -QTextStream &QTextStream::operator<<(double f) -{ - Q_D(QTextStream); - CHECK_VALID_STREAM(*this); - - QLocaleData::DoubleForm form = QLocaleData::DFDecimal; - switch (realNumberNotation()) { - case FixedNotation: - form = QLocaleData::DFDecimal; - break; - case ScientificNotation: - form = QLocaleData::DFExponent; - break; - case SmartNotation: - form = QLocaleData::DFSignificantDigits; - break; - } - - uint flags = 0; - const QLocale::NumberOptions numberOptions = locale().numberOptions(); - if (numberFlags() & ShowBase) - flags |= QLocaleData::ShowBase; - if (numberFlags() & ForceSign) - flags |= QLocaleData::AlwaysShowSign; - if (numberFlags() & UppercaseBase) - flags |= QLocaleData::UppercaseBase; - if (numberFlags() & UppercaseDigits) - flags |= QLocaleData::CapitalEorX; - if (numberFlags() & ForcePoint) { - flags |= QLocaleData::ForcePoint; - - // Only for backwards compatibility - flags |= QLocaleData::AddTrailingZeroes | QLocaleData::ShowBase; - } - if (locale() != QLocale::c() && !(numberOptions & QLocale::OmitGroupSeparator)) - flags |= QLocaleData::ThousandsGroup; - if (!(numberOptions & QLocale::OmitLeadingZeroInExponent)) - flags |= QLocaleData::ZeroPadExponent; - if (numberOptions & QLocale::IncludeTrailingZeroesAfterDot) - flags |= QLocaleData::AddTrailingZeroes; - - const QLocaleData *dd = d->locale.d->m_data; - QString num = dd->doubleToString(f, d->params.realNumberPrecision, form, -1, flags); - d->putString(num, true); - return *this; -} - -/*! - Writes the string \a string to the stream, and returns a reference - to the QTextStream. The string is first encoded using the assigned - codec (the default codec is QTextCodec::codecForLocale()) before - it is written to the stream. - - \sa setFieldWidth(), setCodec() -*/ -QTextStream &QTextStream::operator<<(const QString &string) -{ - Q_D(QTextStream); - CHECK_VALID_STREAM(*this); - d->putString(string); - return *this; -} - -/*! - \overload - - Writes \a string to the stream, and returns a reference to the - QTextStream. -*/ -QTextStream &QTextStream::operator<<(QLatin1String string) -{ - Q_D(QTextStream); - CHECK_VALID_STREAM(*this); - d->putString(string); - return *this; -} - -/*! - \since 5.6 - \overload - - Writes \a string to the stream, and returns a reference to the - QTextStream. -*/ -QTextStream &QTextStream::operator<<(const QStringRef &string) -{ - Q_D(QTextStream); - CHECK_VALID_STREAM(*this); - d->putString(string.data(), string.size()); - return *this; -} - -/*! - \overload - - Writes \a array to the stream. The contents of \a array are - converted with QString::fromUtf8(). -*/ -QTextStream &QTextStream::operator<<(const QByteArray &array) -{ - Q_D(QTextStream); - CHECK_VALID_STREAM(*this); - d->putString(QString::fromUtf8(array.constData(), array.length())); - return *this; -} - -/*! - \overload - - Writes the constant string pointed to by \a string to the stream. \a - string is assumed to be in ISO-8859-1 encoding. This operator - is convenient when working with constant string data. Example: - - \snippet code/src_corelib_io_qtextstream.cpp 8 - - Warning: QTextStream assumes that \a string points to a string of - text, terminated by a '\\0' character. If there is no terminating - '\\0' character, your application may crash. -*/ -QTextStream &QTextStream::operator<<(const char *string) -{ - Q_D(QTextStream); - CHECK_VALID_STREAM(*this); - // ### Qt6: consider changing to UTF-8 - d->putString(QLatin1String(string)); - return *this; -} - -/*! - \overload - - Writes \a ptr to the stream as a hexadecimal number with a base. -*/ - -QTextStream &QTextStream::operator<<(const void *ptr) -{ - Q_D(QTextStream); - CHECK_VALID_STREAM(*this); - const int oldBase = d->params.integerBase; - const NumberFlags oldFlags = d->params.numberFlags; - d->params.integerBase = 16; - d->params.numberFlags |= ShowBase; - d->putNumber(reinterpret_cast(ptr), false); - d->params.integerBase = oldBase; - d->params.numberFlags = oldFlags; - return *this; -} - -/*! - \relates QTextStream - - Calls QTextStream::setIntegerBase(2) on \a stream and returns \a - stream. - - \sa oct(), dec(), hex(), {QTextStream manipulators} -*/ -QTextStream &bin(QTextStream &stream) -{ - stream.setIntegerBase(2); - return stream; -} - -/*! - \relates QTextStream - - Calls QTextStream::setIntegerBase(8) on \a stream and returns \a - stream. - - \sa bin(), dec(), hex(), {QTextStream manipulators} -*/ -QTextStream &oct(QTextStream &stream) -{ - stream.setIntegerBase(8); - return stream; -} - -/*! - \relates QTextStream - - Calls QTextStream::setIntegerBase(10) on \a stream and returns \a - stream. - - \sa bin(), oct(), hex(), {QTextStream manipulators} -*/ -QTextStream &dec(QTextStream &stream) -{ - stream.setIntegerBase(10); - return stream; -} - -/*! - \relates QTextStream - - Calls QTextStream::setIntegerBase(16) on \a stream and returns \a - stream. - - \note The hex modifier can only be used for writing to streams. - \sa bin(), oct(), dec(), {QTextStream manipulators} -*/ -QTextStream &hex(QTextStream &stream) -{ - stream.setIntegerBase(16); - return stream; -} - -/*! - \relates QTextStream - - Calls QTextStream::setNumberFlags(QTextStream::numberFlags() | - QTextStream::ShowBase) on \a stream and returns \a stream. - - \sa noshowbase(), forcesign(), forcepoint(), {QTextStream manipulators} -*/ -QTextStream &showbase(QTextStream &stream) -{ - stream.setNumberFlags(stream.numberFlags() | QTextStream::ShowBase); - return stream; -} - -/*! - \relates QTextStream - - Calls QTextStream::setNumberFlags(QTextStream::numberFlags() | - QTextStream::ForceSign) on \a stream and returns \a stream. - - \sa noforcesign(), forcepoint(), showbase(), {QTextStream manipulators} -*/ -QTextStream &forcesign(QTextStream &stream) -{ - stream.setNumberFlags(stream.numberFlags() | QTextStream::ForceSign); - return stream; -} - -/*! - \relates QTextStream - - Calls QTextStream::setNumberFlags(QTextStream::numberFlags() | - QTextStream::ForcePoint) on \a stream and returns \a stream. - - \sa noforcepoint(), forcesign(), showbase(), {QTextStream manipulators} -*/ -QTextStream &forcepoint(QTextStream &stream) -{ - stream.setNumberFlags(stream.numberFlags() | QTextStream::ForcePoint); - return stream; -} - -/*! - \relates QTextStream - - Calls QTextStream::setNumberFlags(QTextStream::numberFlags() & - ~QTextStream::ShowBase) on \a stream and returns \a stream. - - \sa showbase(), noforcesign(), noforcepoint(), {QTextStream manipulators} -*/ -QTextStream &noshowbase(QTextStream &stream) -{ - stream.setNumberFlags(stream.numberFlags() &= ~QTextStream::ShowBase); - return stream; -} - -/*! - \relates QTextStream - - Calls QTextStream::setNumberFlags(QTextStream::numberFlags() & - ~QTextStream::ForceSign) on \a stream and returns \a stream. - - \sa forcesign(), noforcepoint(), noshowbase(), {QTextStream manipulators} -*/ -QTextStream &noforcesign(QTextStream &stream) -{ - stream.setNumberFlags(stream.numberFlags() &= ~QTextStream::ForceSign); - return stream; -} - -/*! - \relates QTextStream - - Calls QTextStream::setNumberFlags(QTextStream::numberFlags() & - ~QTextStream::ForcePoint) on \a stream and returns \a stream. - - \sa forcepoint(), noforcesign(), noshowbase(), {QTextStream manipulators} -*/ -QTextStream &noforcepoint(QTextStream &stream) -{ - stream.setNumberFlags(stream.numberFlags() &= ~QTextStream::ForcePoint); - return stream; -} - -/*! - \relates QTextStream - - Calls QTextStream::setNumberFlags(QTextStream::numberFlags() | - QTextStream::UppercaseBase) on \a stream and returns \a stream. - - \sa lowercasebase(), uppercasedigits(), {QTextStream manipulators} -*/ -QTextStream &uppercasebase(QTextStream &stream) -{ - stream.setNumberFlags(stream.numberFlags() | QTextStream::UppercaseBase); - return stream; -} - -/*! - \relates QTextStream - - Calls QTextStream::setNumberFlags(QTextStream::numberFlags() | - QTextStream::UppercaseDigits) on \a stream and returns \a stream. - - \sa lowercasedigits(), uppercasebase(), {QTextStream manipulators} -*/ -QTextStream &uppercasedigits(QTextStream &stream) -{ - stream.setNumberFlags(stream.numberFlags() | QTextStream::UppercaseDigits); - return stream; -} - -/*! - \relates QTextStream - - Calls QTextStream::setNumberFlags(QTextStream::numberFlags() & - ~QTextStream::UppercaseBase) on \a stream and returns \a stream. - - \sa uppercasebase(), lowercasedigits(), {QTextStream manipulators} -*/ -QTextStream &lowercasebase(QTextStream &stream) -{ - stream.setNumberFlags(stream.numberFlags() & ~QTextStream::UppercaseBase); - return stream; -} - -/*! - \relates QTextStream - - Calls QTextStream::setNumberFlags(QTextStream::numberFlags() & - ~QTextStream::UppercaseDigits) on \a stream and returns \a stream. - - \sa uppercasedigits(), lowercasebase(), {QTextStream manipulators} -*/ -QTextStream &lowercasedigits(QTextStream &stream) -{ - stream.setNumberFlags(stream.numberFlags() & ~QTextStream::UppercaseDigits); - return stream; -} - -/*! - \relates QTextStream - - Calls QTextStream::setRealNumberNotation(QTextStream::FixedNotation) - on \a stream and returns \a stream. - - \sa scientific(), {QTextStream manipulators} -*/ -QTextStream &fixed(QTextStream &stream) -{ - stream.setRealNumberNotation(QTextStream::FixedNotation); - return stream; -} - -/*! - \relates QTextStream - - Calls QTextStream::setRealNumberNotation(QTextStream::ScientificNotation) - on \a stream and returns \a stream. - - \sa fixed(), {QTextStream manipulators} -*/ -QTextStream &scientific(QTextStream &stream) -{ - stream.setRealNumberNotation(QTextStream::ScientificNotation); - return stream; -} - -/*! - \relates QTextStream - - Calls QTextStream::setFieldAlignment(QTextStream::AlignLeft) - on \a stream and returns \a stream. - - \sa {QTextStream::}{right()}, {QTextStream::}{center()}, {QTextStream manipulators} -*/ -QTextStream &left(QTextStream &stream) -{ - stream.setFieldAlignment(QTextStream::AlignLeft); - return stream; -} - -/*! - \relates QTextStream - - Calls QTextStream::setFieldAlignment(QTextStream::AlignRight) - on \a stream and returns \a stream. - - \sa {QTextStream::}{left()}, {QTextStream::}{center()}, {QTextStream manipulators} -*/ -QTextStream &right(QTextStream &stream) -{ - stream.setFieldAlignment(QTextStream::AlignRight); - return stream; -} - -/*! - \relates QTextStream - - Calls QTextStream::setFieldAlignment(QTextStream::AlignCenter) - on \a stream and returns \a stream. - - \sa {QTextStream::}{left()}, {QTextStream::}{right()}, {QTextStream manipulators} -*/ -QTextStream ¢er(QTextStream &stream) -{ - stream.setFieldAlignment(QTextStream::AlignCenter); - return stream; -} - -/*! - \relates QTextStream - - Writes '\\n' to the \a stream and flushes the stream. - - Equivalent to - - \snippet code/src_corelib_io_qtextstream.cpp 9 - - Note: On Windows, all '\\n' characters are written as '\\r\\n' if - QTextStream's device or string is opened using the QIODevice::Text flag. - - \sa flush(), reset(), {QTextStream manipulators} -*/ -QTextStream &endl(QTextStream &stream) -{ - return stream << QLatin1Char('\n') << flush; -} - -/*! - \relates QTextStream - - Calls QTextStream::flush() on \a stream and returns \a stream. - - \sa endl(), reset(), {QTextStream manipulators} -*/ -QTextStream &flush(QTextStream &stream) -{ - stream.flush(); - return stream; -} - -/*! - \relates QTextStream - - Calls QTextStream::reset() on \a stream and returns \a stream. - - \sa flush(), {QTextStream manipulators} -*/ -QTextStream &reset(QTextStream &stream) -{ - stream.reset(); - return stream; -} - -/*! - \relates QTextStream - - Calls \l {QTextStream::}{skipWhiteSpace()} on \a stream and returns \a stream. - - \sa {QTextStream manipulators} -*/ -QTextStream &ws(QTextStream &stream) -{ - stream.skipWhiteSpace(); - return stream; -} - -/*! - \fn QTextStreamManipulator qSetFieldWidth(int width) - \relates QTextStream - - Equivalent to QTextStream::setFieldWidth(\a width). -*/ - -/*! - \fn QTextStreamManipulator qSetPadChar(QChar ch) - \relates QTextStream - - Equivalent to QTextStream::setPadChar(\a ch). -*/ - -/*! - \fn QTextStreamManipulator qSetRealNumberPrecision(int precision) - \relates QTextStream - - Equivalent to QTextStream::setRealNumberPrecision(\a precision). -*/ - -#ifndef QT_NO_TEXTCODEC -/*! - \relates QTextStream - - Toggles insertion of the Byte Order Mark on \a stream when QTextStream is - used with a UTF codec. - - \sa QTextStream::setGenerateByteOrderMark(), {QTextStream manipulators} -*/ -QTextStream &bom(QTextStream &stream) -{ - stream.setGenerateByteOrderMark(true); - return stream; -} - -/*! - Sets the codec for this stream to \a codec. The codec is used for - decoding any data that is read from the assigned device, and for - encoding any data that is written. By default, - QTextCodec::codecForLocale() is used, and automatic unicode - detection is enabled. - - If QTextStream operates on a string, this function does nothing. - - \warning If you call this function while the text stream is reading - from an open sequential socket, the internal buffer may still contain - text decoded using the old codec. - - \sa codec(), setAutoDetectUnicode(), setLocale() -*/ -void QTextStream::setCodec(QTextCodec *codec) -{ - Q_D(QTextStream); - qint64 seekPos = -1; - if (!d->readBuffer.isEmpty()) { - if (!d->device->isSequential()) { - seekPos = pos(); - } - } - d->codec = codec; - if (seekPos >=0 && !d->readBuffer.isEmpty()) - seek(seekPos); -} - -/*! - Sets the codec for this stream to the QTextCodec for the encoding - specified by \a codecName. Common values for \c codecName include - "ISO 8859-1", "UTF-8", and "UTF-16". If the encoding isn't - recognized, nothing happens. - - Example: - - \snippet code/src_corelib_io_qtextstream.cpp 10 - - \sa QTextCodec::codecForName(), setLocale() -*/ -void QTextStream::setCodec(const char *codecName) -{ - QTextCodec *codec = QTextCodec::codecForName(codecName); - if (codec) - setCodec(codec); -} - -/*! - Returns the codec that is current assigned to the stream. - - \sa setCodec(), setAutoDetectUnicode(), locale() -*/ -QTextCodec *QTextStream::codec() const -{ - Q_D(const QTextStream); - return d->codec; -} - -/*! - If \a enabled is true, QTextStream will attempt to detect Unicode - encoding by peeking into the stream data to see if it can find the - UTF-16 or UTF-32 BOM (Byte Order Mark). If this mark is found, QTextStream - will replace the current codec with the UTF codec. - - This function can be used together with setCodec(). It is common - to set the codec to UTF-8, and then enable UTF-16 detection. - - \sa autoDetectUnicode(), setCodec() -*/ -void QTextStream::setAutoDetectUnicode(bool enabled) -{ - Q_D(QTextStream); - d->autoDetectUnicode = enabled; -} - -/*! - Returns \c true if automatic Unicode detection is enabled, otherwise - returns \c false. Automatic Unicode detection is enabled by default. - - \sa setAutoDetectUnicode(), setCodec() -*/ -bool QTextStream::autoDetectUnicode() const -{ - Q_D(const QTextStream); - return d->autoDetectUnicode; -} - -/*! - If \a generate is true and a UTF codec is used, QTextStream will insert - the BOM (Byte Order Mark) before any data has been written to the - device. If \a generate is false, no BOM will be inserted. This function - must be called before any data is written. Otherwise, it does nothing. - - \sa generateByteOrderMark(), bom() -*/ -void QTextStream::setGenerateByteOrderMark(bool generate) -{ - Q_D(QTextStream); - if (d->writeBuffer.isEmpty()) { - d->writeConverterState.flags.setFlag(QTextCodec::IgnoreHeader, !generate); - } -} - -/*! - Returns \c true if QTextStream is set to generate the UTF BOM (Byte Order - Mark) when using a UTF codec; otherwise returns \c false. UTF BOM generation is - set to false by default. - - \sa setGenerateByteOrderMark() -*/ -bool QTextStream::generateByteOrderMark() const -{ - Q_D(const QTextStream); - return (d->writeConverterState.flags & QTextCodec::IgnoreHeader) == 0; -} - -#endif - -/*! - \since 4.5 - - Sets the locale for this stream to \a locale. The specified locale is - used for conversions between numbers and their string representations. - - The default locale is C and it is a special case - the thousands - group separator is not used for backward compatibility reasons. - - \sa locale() -*/ -void QTextStream::setLocale(const QLocale &locale) -{ - Q_D(QTextStream); - d->locale = locale; -} - -/*! - \since 4.5 - - Returns the locale for this stream. The default locale is C. - - \sa setLocale() -*/ -QLocale QTextStream::locale() const -{ - Q_D(const QTextStream); - return d->locale; -} - -QT_END_NAMESPACE - -#ifndef QT_NO_QOBJECT -#include "moc_qtextstream_p.cpp" -#endif diff --git a/src/corelib/io/qtextstream.h b/src/corelib/io/qtextstream.h deleted file mode 100644 index ee0b09419d..0000000000 --- a/src/corelib/io/qtextstream.h +++ /dev/null @@ -1,287 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtCore module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QTEXTSTREAM_H -#define QTEXTSTREAM_H - -#include -#include -#include -#include -#include - -#include - -#ifdef Status -#error qtextstream.h must be included before any header file that defines Status -#endif - -QT_BEGIN_NAMESPACE - - -class QTextCodec; -class QTextDecoder; - -class QTextStreamPrivate; -class Q_CORE_EXPORT QTextStream // text stream class -{ - Q_DECLARE_PRIVATE(QTextStream) - -public: - enum RealNumberNotation { - SmartNotation, - FixedNotation, - ScientificNotation - }; - enum FieldAlignment { - AlignLeft, - AlignRight, - AlignCenter, - AlignAccountingStyle - }; - enum Status { - Ok, - ReadPastEnd, - ReadCorruptData, - WriteFailed - }; - enum NumberFlag { - ShowBase = 0x1, - ForcePoint = 0x2, - ForceSign = 0x4, - UppercaseBase = 0x8, - UppercaseDigits = 0x10 - }; - Q_DECLARE_FLAGS(NumberFlags, NumberFlag) - - QTextStream(); - explicit QTextStream(QIODevice *device); - explicit QTextStream(FILE *fileHandle, QIODevice::OpenMode openMode = QIODevice::ReadWrite); - explicit QTextStream(QString *string, QIODevice::OpenMode openMode = QIODevice::ReadWrite); - explicit QTextStream(QByteArray *array, QIODevice::OpenMode openMode = QIODevice::ReadWrite); - explicit QTextStream(const QByteArray &array, QIODevice::OpenMode openMode = QIODevice::ReadOnly); - virtual ~QTextStream(); - -#ifndef QT_NO_TEXTCODEC - void setCodec(QTextCodec *codec); - void setCodec(const char *codecName); - QTextCodec *codec() const; - void setAutoDetectUnicode(bool enabled); - bool autoDetectUnicode() const; - void setGenerateByteOrderMark(bool generate); - bool generateByteOrderMark() const; -#endif - - void setLocale(const QLocale &locale); - QLocale locale() const; - - void setDevice(QIODevice *device); - QIODevice *device() const; - - void setString(QString *string, QIODevice::OpenMode openMode = QIODevice::ReadWrite); - QString *string() const; - - Status status() const; - void setStatus(Status status); - void resetStatus(); - - bool atEnd() const; - void reset(); - void flush(); - bool seek(qint64 pos); - qint64 pos() const; - - void skipWhiteSpace(); - - QString readLine(qint64 maxlen = 0); - bool readLineInto(QString *line, qint64 maxlen = 0); - QString readAll(); - QString read(qint64 maxlen); - - void setFieldAlignment(FieldAlignment alignment); - FieldAlignment fieldAlignment() const; - - void setPadChar(QChar ch); - QChar padChar() const; - - void setFieldWidth(int width); - int fieldWidth() const; - - void setNumberFlags(NumberFlags flags); - NumberFlags numberFlags() const; - - void setIntegerBase(int base); - int integerBase() const; - - void setRealNumberNotation(RealNumberNotation notation); - RealNumberNotation realNumberNotation() const; - - void setRealNumberPrecision(int precision); - int realNumberPrecision() const; - - QTextStream &operator>>(QChar &ch); - QTextStream &operator>>(char &ch); - QTextStream &operator>>(signed short &i); - QTextStream &operator>>(unsigned short &i); - QTextStream &operator>>(signed int &i); - QTextStream &operator>>(unsigned int &i); - QTextStream &operator>>(signed long &i); - QTextStream &operator>>(unsigned long &i); - QTextStream &operator>>(qlonglong &i); - QTextStream &operator>>(qulonglong &i); - QTextStream &operator>>(float &f); - QTextStream &operator>>(double &f); - QTextStream &operator>>(QString &s); - QTextStream &operator>>(QByteArray &array); - QTextStream &operator>>(char *c); - - QTextStream &operator<<(QChar ch); - QTextStream &operator<<(char ch); - QTextStream &operator<<(signed short i); - QTextStream &operator<<(unsigned short i); - QTextStream &operator<<(signed int i); - QTextStream &operator<<(unsigned int i); - QTextStream &operator<<(signed long i); - QTextStream &operator<<(unsigned long i); - QTextStream &operator<<(qlonglong i); - QTextStream &operator<<(qulonglong i); - QTextStream &operator<<(float f); - QTextStream &operator<<(double f); - QTextStream &operator<<(const QString &s); - QTextStream &operator<<(QLatin1String s); - QTextStream &operator<<(const QStringRef &s); - QTextStream &operator<<(const QByteArray &array); - QTextStream &operator<<(const char *c); - QTextStream &operator<<(const void *ptr); - -private: - Q_DISABLE_COPY(QTextStream) - friend class QDebugStateSaverPrivate; - friend class QDebug; - - QScopedPointer d_ptr; -}; - -Q_DECLARE_OPERATORS_FOR_FLAGS(QTextStream::NumberFlags) - -/***************************************************************************** - QTextStream manipulators - *****************************************************************************/ - -typedef QTextStream & (*QTextStreamFunction)(QTextStream &);// manipulator function -typedef void (QTextStream::*QTSMFI)(int); // manipulator w/int argument -typedef void (QTextStream::*QTSMFC)(QChar); // manipulator w/QChar argument - - -class Q_CORE_EXPORT QTextStreamManipulator -{ -public: - Q_DECL_CONSTEXPR QTextStreamManipulator(QTSMFI m, int a) Q_DECL_NOTHROW : mf(m), mc(nullptr), arg(a), ch() {} - Q_DECL_CONSTEXPR QTextStreamManipulator(QTSMFC m, QChar c) Q_DECL_NOTHROW : mf(nullptr), mc(m), arg(-1), ch(c) {} - void exec(QTextStream &s) { if (mf) { (s.*mf)(arg); } else { (s.*mc)(ch); } } - -private: - QTSMFI mf; // QTextStream member function - QTSMFC mc; // QTextStream member function - int arg; // member function argument - QChar ch; -}; - -inline QTextStream &operator>>(QTextStream &s, QTextStreamFunction f) -{ return (*f)(s); } - -inline QTextStream &operator<<(QTextStream &s, QTextStreamFunction f) -{ return (*f)(s); } - -inline QTextStream &operator<<(QTextStream &s, QTextStreamManipulator m) -{ m.exec(s); return s; } - -Q_CORE_EXPORT QTextStream &bin(QTextStream &s); -Q_CORE_EXPORT QTextStream &oct(QTextStream &s); -Q_CORE_EXPORT QTextStream &dec(QTextStream &s); -Q_CORE_EXPORT QTextStream &hex(QTextStream &s); - -Q_CORE_EXPORT QTextStream &showbase(QTextStream &s); -Q_CORE_EXPORT QTextStream &forcesign(QTextStream &s); -Q_CORE_EXPORT QTextStream &forcepoint(QTextStream &s); -Q_CORE_EXPORT QTextStream &noshowbase(QTextStream &s); -Q_CORE_EXPORT QTextStream &noforcesign(QTextStream &s); -Q_CORE_EXPORT QTextStream &noforcepoint(QTextStream &s); - -Q_CORE_EXPORT QTextStream &uppercasebase(QTextStream &s); -Q_CORE_EXPORT QTextStream &uppercasedigits(QTextStream &s); -Q_CORE_EXPORT QTextStream &lowercasebase(QTextStream &s); -Q_CORE_EXPORT QTextStream &lowercasedigits(QTextStream &s); - -Q_CORE_EXPORT QTextStream &fixed(QTextStream &s); -Q_CORE_EXPORT QTextStream &scientific(QTextStream &s); - -Q_CORE_EXPORT QTextStream &left(QTextStream &s); -Q_CORE_EXPORT QTextStream &right(QTextStream &s); -Q_CORE_EXPORT QTextStream ¢er(QTextStream &s); - -Q_CORE_EXPORT QTextStream &endl(QTextStream &s); -Q_CORE_EXPORT QTextStream &flush(QTextStream &s); -Q_CORE_EXPORT QTextStream &reset(QTextStream &s); - -Q_CORE_EXPORT QTextStream &bom(QTextStream &s); - -Q_CORE_EXPORT QTextStream &ws(QTextStream &s); - -inline QTextStreamManipulator qSetFieldWidth(int width) -{ - QTSMFI func = &QTextStream::setFieldWidth; - return QTextStreamManipulator(func,width); -} - -inline QTextStreamManipulator qSetPadChar(QChar ch) -{ - QTSMFC func = &QTextStream::setPadChar; - return QTextStreamManipulator(func, ch); -} - -inline QTextStreamManipulator qSetRealNumberPrecision(int precision) -{ - QTSMFI func = &QTextStream::setRealNumberPrecision; - return QTextStreamManipulator(func, precision); -} - -QT_END_NAMESPACE - -#endif // QTEXTSTREAM_H diff --git a/src/corelib/io/qtextstream_p.h b/src/corelib/io/qtextstream_p.h deleted file mode 100644 index a642beddc4..0000000000 --- a/src/corelib/io/qtextstream_p.h +++ /dev/null @@ -1,200 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Copyright (C) 2016 Intel Corporation. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtCore module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QTEXTSTREAM_P_H -#define QTEXTSTREAM_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include -#include "qtextstream.h" -#ifndef QT_NO_TEXTCODEC -#include "qtextcodec.h" -#endif - -QT_BEGIN_NAMESPACE - -#ifndef QT_NO_QOBJECT -class QDeviceClosedNotifier : public QObject -{ - Q_OBJECT -public: - inline QDeviceClosedNotifier() - { } - - inline void setupDevice(QTextStream *stream, QIODevice *device) - { - disconnect(); - if (device) - connect(device, SIGNAL(aboutToClose()), this, SLOT(flushStream())); - this->stream = stream; - } - -public Q_SLOTS: - inline void flushStream() { stream->flush(); } - -private: - QTextStream *stream; -}; -#endif - -class QTextStreamPrivate -{ - Q_DECLARE_PUBLIC(QTextStream) -public: - // streaming parameters - class Params - { - public: - void reset(); - - int realNumberPrecision; - int integerBase; - int fieldWidth; - QChar padChar; - QTextStream::FieldAlignment fieldAlignment; - QTextStream::RealNumberNotation realNumberNotation; - QTextStream::NumberFlags numberFlags; - }; - - QTextStreamPrivate(QTextStream *q_ptr); - ~QTextStreamPrivate(); - void reset(); - - // device - QIODevice *device; -#ifndef QT_NO_QOBJECT - QDeviceClosedNotifier deviceClosedNotifier; -#endif - - // string - QString *string; - int stringOffset; - QIODevice::OpenMode stringOpenMode; - -#ifndef QT_NO_TEXTCODEC - // codec - QTextCodec *codec; - QTextCodec::ConverterState readConverterState; - QTextCodec::ConverterState writeConverterState; - QTextCodec::ConverterState *readConverterSavedState; -#endif - - QString writeBuffer; - QString readBuffer; - int readBufferOffset; - int readConverterSavedStateOffset; //the offset between readBufferStartDevicePos and that start of the buffer - qint64 readBufferStartDevicePos; - - Params params; - - // status - QTextStream::Status status; - QLocale locale; - QTextStream *q_ptr; - - int lastTokenSize; - bool deleteDevice; -#ifndef QT_NO_TEXTCODEC - bool autoDetectUnicode; -#endif - - // i/o - enum TokenDelimiter { - Space, - NotSpace, - EndOfLine - }; - - QString read(int maxlen); - bool scan(const QChar **ptr, int *tokenLength, - int maxlen, TokenDelimiter delimiter); - inline const QChar *readPtr() const; - inline void consumeLastToken(); - inline void consume(int nchars); - void saveConverterState(qint64 newPos); - void restoreToSavedConverterState(); - - // Return value type for getNumber() - enum NumberParsingStatus { - npsOk, - npsMissingDigit, - npsInvalidPrefix - }; - - inline bool getChar(QChar *ch); - inline void ungetChar(QChar ch); - NumberParsingStatus getNumber(qulonglong *l); - bool getReal(double *f); - - inline void write(const QString &data) { write(data.begin(), data.length()); } - inline void write(QChar ch); - void write(const QChar *data, int len); - void write(QLatin1String data); - void writePadding(int len); - inline void putString(const QString &ch, bool number = false) { putString(ch.constData(), ch.length(), number); } - void putString(const QChar *data, int len, bool number = false); - void putString(QLatin1String data, bool number = false); - inline void putChar(QChar ch); - void putNumber(qulonglong number, bool negative); - - struct PaddingResult { - int left, right; - }; - PaddingResult padding(int len) const; - - // buffers - bool fillReadBuffer(qint64 maxBytes = -1); - void resetReadBuffer(); - void flushWriteBuffer(); -}; - -QT_END_NAMESPACE - -#endif // QTEXTSTREAM_P_H diff --git a/src/corelib/json/json.pri b/src/corelib/json/json.pri deleted file mode 100644 index 1a4e2a72bf..0000000000 --- a/src/corelib/json/json.pri +++ /dev/null @@ -1,17 +0,0 @@ -HEADERS += \ - json/qjson_p.h \ - json/qjsondocument.h \ - json/qjsonobject.h \ - json/qjsonvalue.h \ - json/qjsonarray.h \ - json/qjsonwriter_p.h \ - json/qjsonparser_p.h - -SOURCES += \ - json/qjson.cpp \ - json/qjsondocument.cpp \ - json/qjsonobject.cpp \ - json/qjsonarray.cpp \ - json/qjsonvalue.cpp \ - json/qjsonwriter.cpp \ - json/qjsonparser.cpp diff --git a/src/corelib/json/qjson.cpp b/src/corelib/json/qjson.cpp deleted file mode 100644 index e4bca3bcd0..0000000000 --- a/src/corelib/json/qjson.cpp +++ /dev/null @@ -1,455 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtCore module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qjson_p.h" -#include - -QT_BEGIN_NAMESPACE - -namespace QJsonPrivate -{ - -static Q_CONSTEXPR Base emptyArray = { { qle_uint(sizeof(Base)) }, { 0 }, { qle_uint(0) } }; -static Q_CONSTEXPR Base emptyObject = { { qle_uint(sizeof(Base)) }, { 0 }, { qle_uint(0) } }; - -void Data::compact() -{ - Q_ASSERT(sizeof(Value) == sizeof(offset)); - - if (!compactionCounter) - return; - - Base *base = header->root(); - int reserve = 0; - if (base->is_object) { - Object *o = static_cast(base); - for (int i = 0; i < (int)o->length; ++i) - reserve += o->entryAt(i)->usedStorage(o); - } else { - Array *a = static_cast(base); - for (int i = 0; i < (int)a->length; ++i) - reserve += (*a)[i].usedStorage(a); - } - - int size = sizeof(Base) + reserve + base->length*sizeof(offset); - int alloc = sizeof(Header) + size; - Header *h = (Header *) malloc(alloc); - h->tag = QJsonDocument::BinaryFormatTag; - h->version = 1; - Base *b = h->root(); - b->size = size; - b->is_object = header->root()->is_object; - b->length = base->length; - b->tableOffset = reserve + sizeof(Array); - - int offset = sizeof(Base); - if (b->is_object) { - Object *o = static_cast(base); - Object *no = static_cast(b); - - for (int i = 0; i < (int)o->length; ++i) { - no->table()[i] = offset; - - const Entry *e = o->entryAt(i); - Entry *ne = no->entryAt(i); - int s = e->size(); - memcpy(ne, e, s); - offset += s; - int dataSize = e->value.usedStorage(o); - if (dataSize) { - memcpy((char *)no + offset, e->value.data(o), dataSize); - ne->value.value = offset; - offset += dataSize; - } - } - } else { - Array *a = static_cast(base); - Array *na = static_cast(b); - - for (int i = 0; i < (int)a->length; ++i) { - const Value &v = (*a)[i]; - Value &nv = (*na)[i]; - nv = v; - int dataSize = v.usedStorage(a); - if (dataSize) { - memcpy((char *)na + offset, v.data(a), dataSize); - nv.value = offset; - offset += dataSize; - } - } - } - Q_ASSERT(offset == (int)b->tableOffset); - - free(header); - header = h; - this->alloc = alloc; - compactionCounter = 0; -} - -bool Data::valid() const -{ - if (header->tag != QJsonDocument::BinaryFormatTag || header->version != 1u) - return false; - - bool res = false; - Base *root = header->root(); - int maxSize = alloc - sizeof(Header); - if (root->is_object) - res = static_cast(root)->isValid(maxSize); - else - res = static_cast(root)->isValid(maxSize); - - return res; -} - - -int Base::reserveSpace(uint dataSize, int posInTable, uint numItems, bool replace) -{ - Q_ASSERT(posInTable >= 0 && posInTable <= (int)length); - if (size + dataSize >= Value::MaxSize) { - qWarning("QJson: Document too large to store in data structure %d %d %d", (uint)size, dataSize, Value::MaxSize); - return 0; - } - - offset off = tableOffset; - // move table to new position - if (replace) { - memmove((char *)(table()) + dataSize, table(), length*sizeof(offset)); - } else { - memmove((char *)(table() + posInTable + numItems) + dataSize, table() + posInTable, (length - posInTable)*sizeof(offset)); - memmove((char *)(table()) + dataSize, table(), posInTable*sizeof(offset)); - } - tableOffset += dataSize; - for (int i = 0; i < (int)numItems; ++i) - table()[posInTable + i] = off; - size += dataSize; - if (!replace) { - length += numItems; - size += numItems * sizeof(offset); - } - return off; -} - -void Base::removeItems(int pos, int numItems) -{ - Q_ASSERT(pos >= 0 && pos <= (int)length); - if (pos + numItems < (int)length) - memmove(table() + pos, table() + pos + numItems, (length - pos - numItems)*sizeof(offset)); - length -= numItems; -} - -int Object::indexOf(const QString &key, bool *exists) const -{ - int min = 0; - int n = length; - while (n > 0) { - int half = n >> 1; - int middle = min + half; - if (*entryAt(middle) >= key) { - n = half; - } else { - min = middle + 1; - n -= half + 1; - } - } - if (min < (int)length && *entryAt(min) == key) { - *exists = true; - return min; - } - *exists = false; - return min; -} - -int Object::indexOf(QLatin1String key, bool *exists) const -{ - int min = 0; - int n = length; - while (n > 0) { - int half = n >> 1; - int middle = min + half; - if (*entryAt(middle) >= key) { - n = half; - } else { - min = middle + 1; - n -= half + 1; - } - } - if (min < (int)length && *entryAt(min) == key) { - *exists = true; - return min; - } - *exists = false; - return min; -} - -bool Object::isValid(int maxSize) const -{ - if (size > (uint)maxSize || tableOffset + length*sizeof(offset) > size) - return false; - - QString lastKey; - for (uint i = 0; i < length; ++i) { - offset entryOffset = table()[i]; - if (entryOffset + sizeof(Entry) >= tableOffset) - return false; - Entry *e = entryAt(i); - if (!e->isValid(tableOffset - table()[i])) - return false; - QString key = e->key(); - if (key < lastKey) - return false; - if (!e->value.isValid(this)) - return false; - lastKey = key; - } - return true; -} - - - -bool Array::isValid(int maxSize) const -{ - if (size > (uint)maxSize || tableOffset + length*sizeof(offset) > size) - return false; - - for (uint i = 0; i < length; ++i) { - if (!at(i).isValid(this)) - return false; - } - return true; -} - - -bool Entry::operator ==(const QString &key) const -{ - if (value.latinKey) - return (shallowLatin1Key() == key); - else - return (shallowKey() == key); -} - -bool Entry::operator==(QLatin1String key) const -{ - if (value.latinKey) - return shallowLatin1Key() == key; - else - return shallowKey() == key; -} - -bool Entry::operator ==(const Entry &other) const -{ - if (value.latinKey) { - if (other.value.latinKey) - return shallowLatin1Key() == other.shallowLatin1Key(); - return shallowLatin1Key() == other.shallowKey(); - } else if (other.value.latinKey) { - return shallowKey() == other.shallowLatin1Key(); - } - return shallowKey() == other.shallowKey(); -} - -bool Entry::operator >=(const Entry &other) const -{ - if (value.latinKey) { - if (other.value.latinKey) - return shallowLatin1Key() >= other.shallowLatin1Key(); - return shallowLatin1Key() >= other.shallowKey(); - } else if (other.value.latinKey) { - return shallowKey() >= other.shallowLatin1Key(); - } - return shallowKey() >= other.shallowKey(); -} - - -int Value::usedStorage(const Base *b) const -{ - int s = 0; - switch (type) { - case QJsonValue::Double: - if (latinOrIntValue) - break; - s = sizeof(double); - break; - case QJsonValue::String: { - char *d = data(b); - if (latinOrIntValue) - s = sizeof(ushort) + qFromLittleEndian(*(ushort *)d); - else - s = sizeof(int) + sizeof(ushort) * qFromLittleEndian(*(int *)d); - break; - } - case QJsonValue::Array: - case QJsonValue::Object: - s = base(b)->size; - break; - case QJsonValue::Null: - case QJsonValue::Bool: - default: - break; - } - return alignedSize(s); -} - -bool Value::isValid(const Base *b) const -{ - int offset = 0; - switch (type) { - case QJsonValue::Double: - if (latinOrIntValue) - break; - Q_FALLTHROUGH(); - case QJsonValue::String: - case QJsonValue::Array: - case QJsonValue::Object: - offset = value; - break; - case QJsonValue::Null: - case QJsonValue::Bool: - default: - break; - } - - if (!offset) - return true; - if (offset + sizeof(uint) > b->tableOffset) - return false; - - int s = usedStorage(b); - if (!s) - return true; - if (s < 0 || s > (int)b->tableOffset - offset) - return false; - if (type == QJsonValue::Array) - return static_cast(base(b))->isValid(s); - if (type == QJsonValue::Object) - return static_cast(base(b))->isValid(s); - return true; -} - -/*! - \internal - */ -int Value::requiredStorage(QJsonValue &v, bool *compressed) -{ - *compressed = false; - switch (v.t) { - case QJsonValue::Double: - if (QJsonPrivate::compressedNumber(v.dbl) != INT_MAX) { - *compressed = true; - return 0; - } - return sizeof(double); - case QJsonValue::String: { - QString s = v.toString(); - *compressed = QJsonPrivate::useCompressed(s); - return QJsonPrivate::qStringSize(s, *compressed); - } - case QJsonValue::Array: - case QJsonValue::Object: - if (v.d && v.d->compactionCounter) { - v.detach(); - v.d->compact(); - v.base = static_cast(v.d->header->root()); - } - return v.base ? uint(v.base->size) : sizeof(QJsonPrivate::Base); - case QJsonValue::Undefined: - case QJsonValue::Null: - case QJsonValue::Bool: - break; - } - return 0; -} - -/*! - \internal - */ -uint Value::valueToStore(const QJsonValue &v, uint offset) -{ - switch (v.t) { - case QJsonValue::Undefined: - case QJsonValue::Null: - break; - case QJsonValue::Bool: - return v.b; - case QJsonValue::Double: { - int c = QJsonPrivate::compressedNumber(v.dbl); - if (c != INT_MAX) - return c; - } - Q_FALLTHROUGH(); - case QJsonValue::String: - case QJsonValue::Array: - case QJsonValue::Object: - return offset; - } - return 0; -} - -/*! - \internal - */ -void Value::copyData(const QJsonValue &v, char *dest, bool compressed) -{ - switch (v.t) { - case QJsonValue::Double: - if (!compressed) { - qToLittleEndian(v.ui, dest); - } - break; - case QJsonValue::String: { - QString str = v.toString(); - QJsonPrivate::copyString(dest, str, compressed); - break; - } - case QJsonValue::Array: - case QJsonValue::Object: { - const QJsonPrivate::Base *b = v.base; - if (!b) - b = (v.t == QJsonValue::Array ? &emptyArray : &emptyObject); - memcpy(dest, b, b->size); - break; - } - default: - break; - } -} - -} // namespace QJsonPrivate - -QT_END_NAMESPACE diff --git a/src/corelib/json/qjson_p.h b/src/corelib/json/qjson_p.h deleted file mode 100644 index 131528376b..0000000000 --- a/src/corelib/json/qjson_p.h +++ /dev/null @@ -1,781 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Copyright (C) 2016 Intel Corporation. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtCore module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QJSON_P_H -#define QJSON_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "private/qendian_p.h" -#include "private/qsimd_p.h" - -#include -#include - -QT_BEGIN_NAMESPACE - -/* - This defines a binary data structure for Json data. The data structure is optimised for fast reading - and minimum allocations. The whole data structure can be mmap'ed and used directly. - - In most cases the binary structure is not as space efficient as a utf8 encoded text representation, but - much faster to access. - - The size requirements are: - - String: - Latin1 data: 2 bytes header + string.length() - Full Unicode: 4 bytes header + 2*(string.length()) - - Values: 4 bytes + size of data (size can be 0 for some data) - bool: 0 bytes - double: 8 bytes (0 if integer with less than 27bits) - string: see above - array: size of array - object: size of object - Array: 12 bytes + 4*length + size of Value data - Object: 12 bytes + 8*length + size of Key Strings + size of Value data - - For an example such as - - { // object: 12 + 5*8 = 52 - "firstName": "John", // key 12, value 8 = 20 - "lastName" : "Smith", // key 12, value 8 = 20 - "age" : 25, // key 8, value 0 = 8 - "address" : // key 12, object below = 140 - { // object: 12 + 4*8 - "streetAddress": "21 2nd Street", // key 16, value 16 - "city" : "New York", // key 8, value 12 - "state" : "NY", // key 8, value 4 - "postalCode" : "10021" // key 12, value 8 - }, // object total: 128 - "phoneNumber": // key: 16, value array below = 172 - [ // array: 12 + 2*4 + values below: 156 - { // object 12 + 2*8 - "type" : "home", // key 8, value 8 - "number": "212 555-1234" // key 8, value 16 - }, // object total: 68 - { // object 12 + 2*8 - "type" : "fax", // key 8, value 8 - "number": "646 555-4567" // key 8, value 16 - } // object total: 68 - ] // array total: 156 - } // great total: 412 bytes - - The uncompressed text file used roughly 500 bytes, so in this case we end up using about - the same space as the text representation. - - Other measurements have shown a slightly bigger binary size than a compact text - representation where all possible whitespace was stripped out. -*/ -#define Q_DECLARE_JSONPRIVATE_TYPEINFO(Class, Flags) } Q_DECLARE_TYPEINFO(QJsonPrivate::Class, Flags); namespace QJsonPrivate { -namespace QJsonPrivate { - -class Array; -class Object; -class Value; -class Entry; - -template -using q_littleendian = QLEInteger; - -typedef q_littleendian qle_short; -typedef q_littleendian qle_ushort; -typedef q_littleendian qle_int; -typedef q_littleendian qle_uint; - -template -using qle_bitfield = QLEIntegerBitfield; - -template -using qle_signedbitfield = QLEIntegerBitfield; - -typedef qle_uint offset; - -// round the size up to the next 4 byte boundary -inline int alignedSize(int size) { return (size + 3) & ~3; } - -static inline bool useCompressed(const QString &s) -{ - if (s.length() >= 0x8000) - return false; - const ushort *uc = (const ushort *)s.constData(); - const ushort *e = uc + s.length(); - while (uc < e) { - if (*uc > 0xff) - return false; - ++uc; - } - return true; -} - -static inline int qStringSize(const QString &string, bool compress) -{ - int l = 2 + string.length(); - if (!compress) - l *= 2; - return alignedSize(l); -} - -// returns INT_MAX if it can't compress it into 28 bits -static inline int compressedNumber(double d) -{ - // this relies on details of how ieee floats are represented - const int exponent_off = 52; - const quint64 fraction_mask = 0x000fffffffffffffull; - const quint64 exponent_mask = 0x7ff0000000000000ull; - - quint64 val; - memcpy (&val, &d, sizeof(double)); - int exp = (int)((val & exponent_mask) >> exponent_off) - 1023; - if (exp < 0 || exp > 25) - return INT_MAX; - - quint64 non_int = val & (fraction_mask >> exp); - if (non_int) - return INT_MAX; - - bool neg = (val >> 63) != 0; - val &= fraction_mask; - val |= ((quint64)1 << 52); - int res = (int)(val >> (52 - exp)); - return neg ? -res : res; -} - -class Latin1String; - -class String -{ -public: - explicit String(const char *data) { d = (Data *)data; } - - struct Data { - qle_uint length; - qle_ushort utf16[1]; - }; - - Data *d; - - int byteSize() const { return sizeof(uint) + sizeof(ushort) * d->length; } - bool isValid(int maxSize) const { - // Check byteSize() <= maxSize, avoiding integer overflow - maxSize -= sizeof(uint); - return maxSize >= 0 && uint(d->length) <= maxSize / sizeof(ushort); - } - - inline String &operator=(const QString &str) - { - d->length = str.length(); -#if Q_BYTE_ORDER == Q_BIG_ENDIAN - const ushort *uc = (const ushort *)str.unicode(); - for (int i = 0; i < str.length(); ++i) - d->utf16[i] = uc[i]; -#else - memcpy(d->utf16, str.unicode(), str.length()*sizeof(ushort)); -#endif - if (str.length() & 1) - d->utf16[str.length()] = 0; - return *this; - } - - inline bool operator ==(const QString &str) const { - int slen = str.length(); - int l = d->length; - if (slen != l) - return false; - const ushort *s = (const ushort *)str.constData(); - const qle_ushort *a = d->utf16; - const ushort *b = s; - while (l-- && *a == *b) - a++,b++; - return (l == -1); - } - inline bool operator !=(const QString &str) const { - return !operator ==(str); - } - inline bool operator >=(const QString &str) const { - // ### - return toString() >= str; - } - - inline bool operator<(const Latin1String &str) const; - inline bool operator>=(const Latin1String &str) const { return !operator <(str); } - inline bool operator ==(const Latin1String &str) const; - - inline bool operator ==(const String &str) const { - if (d->length != str.d->length) - return false; - return !memcmp(d->utf16, str.d->utf16, d->length*sizeof(ushort)); - } - inline bool operator<(const String &other) const; - inline bool operator >=(const String &other) const { return !(*this < other); } - - inline QString toString() const { -#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN - return QString((QChar *)d->utf16, d->length); -#else - int l = d->length; - QString str(l, Qt::Uninitialized); - QChar *ch = str.data(); - for (int i = 0; i < l; ++i) - ch[i] = QChar(d->utf16[i]); - return str; -#endif - } - -}; - -class Latin1String -{ -public: - explicit Latin1String(const char *data) { d = (Data *)data; } - - struct Data { - qle_ushort length; - char latin1[1]; - }; - Data *d; - - int byteSize() const { return sizeof(ushort) + sizeof(char)*(d->length); } - bool isValid(int maxSize) const { - return byteSize() <= maxSize; - } - - inline Latin1String &operator=(const QString &str) - { - int len = d->length = str.length(); - uchar *l = (uchar *)d->latin1; - const ushort *uc = (const ushort *)str.unicode(); - int i = 0; -#ifdef __SSE2__ - for ( ; i + 16 <= len; i += 16) { - __m128i chunk1 = _mm_loadu_si128((__m128i*)&uc[i]); // load - __m128i chunk2 = _mm_loadu_si128((__m128i*)&uc[i + 8]); // load - // pack the two vector to 16 x 8bits elements - const __m128i result = _mm_packus_epi16(chunk1, chunk2); - _mm_storeu_si128((__m128i*)&l[i], result); // store - } -# ifdef Q_PROCESSOR_X86_64 - // we can do one more round, of 8 characters - if (i + 8 <= len) { - __m128i chunk = _mm_loadu_si128((__m128i*)&uc[i]); // load - // pack with itself, we'll discard the high part anyway - chunk = _mm_packus_epi16(chunk, chunk); - // unaligned 64-bit store - qToUnaligned(_mm_cvtsi128_si64(chunk), l + i); - i += 8; - } -# endif -#endif - for ( ; i < len; ++i) - l[i] = uc[i]; - for ( ; (quintptr)(l+i) & 0x3; ++i) - l[i] = 0; - return *this; - } - - QLatin1String toQLatin1String() const Q_DECL_NOTHROW { - return QLatin1String(d->latin1, d->length); - } - - inline bool operator<(const String &str) const - { - const qle_ushort *uc = (qle_ushort *) str.d->utf16; - if (!uc || *uc == 0) - return false; - - const uchar *c = (uchar *)d->latin1; - const uchar *e = c + qMin((int)d->length, (int)str.d->length); - - while (c < e) { - if (*c != *uc) - break; - ++c; - ++uc; - } - return (c == e ? (int)d->length < (int)str.d->length : *c < *uc); - - } - inline bool operator ==(const String &str) const { - return (str == *this); - } - inline bool operator >=(const String &str) const { - return !(*this < str); - } - - inline QString toString() const { - return QString::fromLatin1(d->latin1, d->length); - } -}; - -#define DEF_OP(op) \ - inline bool operator op(Latin1String lhs, Latin1String rhs) Q_DECL_NOTHROW \ - { \ - return lhs.toQLatin1String() op rhs.toQLatin1String(); \ - } \ - inline bool operator op(QLatin1String lhs, Latin1String rhs) Q_DECL_NOTHROW \ - { \ - return lhs op rhs.toQLatin1String(); \ - } \ - inline bool operator op(Latin1String lhs, QLatin1String rhs) Q_DECL_NOTHROW \ - { \ - return lhs.toQLatin1String() op rhs; \ - } \ - inline bool operator op(const QString &lhs, Latin1String rhs) Q_DECL_NOTHROW \ - { \ - return lhs op rhs.toQLatin1String(); \ - } \ - inline bool operator op(Latin1String lhs, const QString &rhs) Q_DECL_NOTHROW \ - { \ - return lhs.toQLatin1String() op rhs; \ - } \ - /*end*/ -DEF_OP(==) -DEF_OP(!=) -DEF_OP(< ) -DEF_OP(> ) -DEF_OP(<=) -DEF_OP(>=) -#undef DEF_OP - -inline bool String::operator ==(const Latin1String &str) const -{ - if ((int)d->length != (int)str.d->length) - return false; - const qle_ushort *uc = d->utf16; - const qle_ushort *e = uc + d->length; - const uchar *c = (uchar *)str.d->latin1; - - while (uc < e) { - if (*uc != *c) - return false; - ++uc; - ++c; - } - return true; -} - -inline bool String::operator <(const String &other) const -{ - int alen = d->length; - int blen = other.d->length; - int l = qMin(alen, blen); - qle_ushort *a = d->utf16; - qle_ushort *b = other.d->utf16; - - while (l-- && *a == *b) - a++,b++; - if (l==-1) - return (alen < blen); - return (ushort)*a < (ushort)*b; -} - -inline bool String::operator<(const Latin1String &str) const -{ - const uchar *c = (uchar *) str.d->latin1; - if (!c || *c == 0) - return false; - - const qle_ushort *uc = d->utf16; - const qle_ushort *e = uc + qMin((int)d->length, (int)str.d->length); - - while (uc < e) { - if (*uc != *c) - break; - ++uc; - ++c; - } - return (uc == e ? (int)d->length < (int)str.d->length : (ushort)*uc < *c); - -} - -static inline void copyString(char *dest, const QString &str, bool compress) -{ - if (compress) { - Latin1String string(dest); - string = str; - } else { - String string(dest); - string = str; - } -} - - -/* - Base is the base class for both Object and Array. Both classe work more or less the same way. - The class starts with a header (defined by the struct below), then followed by data (the data for - values in the Array case and Entry's (see below) for objects. - - After the data a table follows (tableOffset points to it) containing Value objects for Arrays, and - offsets from the beginning of the object to Entry's in the case of Object. - - Entry's in the Object's table are lexicographically sorted by key in the table(). This allows the usage - of a binary search over the keys in an Object. - */ -class Base -{ -public: - qle_uint size; - union { - uint _dummy; - qle_bitfield<0, 1> is_object; - qle_bitfield<1, 31> length; - }; - offset tableOffset; - // content follows here - - inline bool isObject() const { return !!is_object; } - inline bool isArray() const { return !isObject(); } - - inline offset *table() const { return (offset *) (((char *) this) + tableOffset); } - - int reserveSpace(uint dataSize, int posInTable, uint numItems, bool replace); - void removeItems(int pos, int numItems); -}; - -class Object : public Base -{ -public: - Entry *entryAt(int i) const { - return reinterpret_cast(((char *)this) + table()[i]); - } - int indexOf(const QString &key, bool *exists) const; - int indexOf(QLatin1String key, bool *exists) const; - - bool isValid(int maxSize) const; -}; - - -class Array : public Base -{ -public: - inline Value at(int i) const; - inline Value &operator [](int i); - - bool isValid(int maxSize) const; -}; - - -class Value -{ -public: - enum { - MaxSize = (1<<27) - 1 - }; - union { - uint _dummy; - qle_bitfield<0, 3> type; - qle_bitfield<3, 1> latinOrIntValue; - qle_bitfield<4, 1> latinKey; - qle_bitfield<5, 27> value; - qle_signedbitfield<5, 27> int_value; - }; - - inline char *data(const Base *b) const { return ((char *)b) + value; } - int usedStorage(const Base *b) const; - - bool toBoolean() const; - double toDouble(const Base *b) const; - QString toString(const Base *b) const; - String asString(const Base *b) const; - Latin1String asLatin1String(const Base *b) const; - Base *base(const Base *b) const; - - bool isValid(const Base *b) const; - - static int requiredStorage(QJsonValue &v, bool *compressed); - static uint valueToStore(const QJsonValue &v, uint offset); - static void copyData(const QJsonValue &v, char *dest, bool compressed); -}; -Q_DECLARE_JSONPRIVATE_TYPEINFO(Value, Q_PRIMITIVE_TYPE) - -inline Value Array::at(int i) const -{ - return *(Value *) (table() + i); -} - -inline Value &Array::operator [](int i) -{ - return *(Value *) (table() + i); -} - - - -class Entry { -public: - Value value; - // key - // value data follows key - - uint size() const { - int s = sizeof(Entry); - if (value.latinKey) - s += shallowLatin1Key().byteSize(); - else - s += shallowKey().byteSize(); - return alignedSize(s); - } - - int usedStorage(Base *b) const { - return size() + value.usedStorage(b); - } - - String shallowKey() const - { - Q_ASSERT(!value.latinKey); - return String((const char *)this + sizeof(Entry)); - } - Latin1String shallowLatin1Key() const - { - Q_ASSERT(value.latinKey); - return Latin1String((const char *)this + sizeof(Entry)); - } - QString key() const - { - if (value.latinKey) { - return shallowLatin1Key().toString(); - } - return shallowKey().toString(); - } - - bool isValid(int maxSize) const { - if (maxSize < (int)sizeof(Entry)) - return false; - maxSize -= sizeof(Entry); - if (value.latinKey) - return shallowLatin1Key().isValid(maxSize); - return shallowKey().isValid(maxSize); - } - - bool operator ==(const QString &key) const; - inline bool operator !=(const QString &key) const { return !operator ==(key); } - inline bool operator >=(const QString &key) const; - - bool operator==(QLatin1String key) const; - inline bool operator!=(QLatin1String key) const { return !operator ==(key); } - inline bool operator>=(QLatin1String key) const; - - bool operator ==(const Entry &other) const; - bool operator >=(const Entry &other) const; -}; - -inline bool Entry::operator >=(const QString &key) const -{ - if (value.latinKey) - return (shallowLatin1Key() >= key); - else - return (shallowKey() >= key); -} - -inline bool Entry::operator >=(QLatin1String key) const -{ - if (value.latinKey) - return shallowLatin1Key() >= key; - else - return shallowKey() >= key; -} - -inline bool operator <(const QString &key, const Entry &e) -{ return e >= key; } - -inline bool operator<(QLatin1String key, const Entry &e) -{ return e >= key; } - - -class Header { -public: - qle_uint tag; // 'qbjs' - qle_uint version; // 1 - Base *root() { return (Base *)(this + 1); } -}; - - -inline bool Value::toBoolean() const -{ - Q_ASSERT(type == QJsonValue::Bool); - return value != 0; -} - -inline double Value::toDouble(const Base *b) const -{ - Q_ASSERT(type == QJsonValue::Double); - if (latinOrIntValue) - return int_value; - - quint64 i = qFromLittleEndian((const uchar *)b + value); - double d; - memcpy(&d, &i, sizeof(double)); - return d; -} - -inline String Value::asString(const Base *b) const -{ - Q_ASSERT(type == QJsonValue::String && !latinOrIntValue); - return String(data(b)); -} - -inline Latin1String Value::asLatin1String(const Base *b) const -{ - Q_ASSERT(type == QJsonValue::String && latinOrIntValue); - return Latin1String(data(b)); -} - -inline QString Value::toString(const Base *b) const -{ - if (latinOrIntValue) - return asLatin1String(b).toString(); - else - return asString(b).toString(); -} - -inline Base *Value::base(const Base *b) const -{ - Q_ASSERT(type == QJsonValue::Array || type == QJsonValue::Object); - return reinterpret_cast(data(b)); -} - -class Data { -public: - enum Validation { - Unchecked, - Validated, - Invalid - }; - - QAtomicInt ref; - int alloc; - union { - char *rawData; - Header *header; - }; - uint compactionCounter : 31; - uint ownsData : 1; - - inline Data(char *raw, int a) - : alloc(a), rawData(raw), compactionCounter(0), ownsData(true) - { - } - inline Data(int reserved, QJsonValue::Type valueType) - : rawData(0), compactionCounter(0), ownsData(true) - { - Q_ASSERT(valueType == QJsonValue::Array || valueType == QJsonValue::Object); - - alloc = sizeof(Header) + sizeof(Base) + reserved + sizeof(offset); - header = (Header *)malloc(alloc); - Q_CHECK_PTR(header); - header->tag = QJsonDocument::BinaryFormatTag; - header->version = 1; - Base *b = header->root(); - b->size = sizeof(Base); - b->is_object = (valueType == QJsonValue::Object); - b->tableOffset = sizeof(Base); - b->length = 0; - } - inline ~Data() - { if (ownsData) free(rawData); } - - uint offsetOf(const void *ptr) const { return (uint)(((char *)ptr - rawData)); } - - QJsonObject toObject(Object *o) const - { - return QJsonObject(const_cast(this), o); - } - - QJsonArray toArray(Array *a) const - { - return QJsonArray(const_cast(this), a); - } - - Data *clone(Base *b, int reserve = 0) - { - int size = sizeof(Header) + b->size; - if (b == header->root() && ref.load() == 1 && alloc >= size + reserve) - return this; - - if (reserve) { - if (reserve < 128) - reserve = 128; - size = qMax(size + reserve, qMin(size *2, (int)Value::MaxSize)); - if (size > Value::MaxSize) { - qWarning("QJson: Document too large to store in data structure"); - return 0; - } - } - char *raw = (char *)malloc(size); - Q_CHECK_PTR(raw); - memcpy(raw + sizeof(Header), b, b->size); - Header *h = (Header *)raw; - h->tag = QJsonDocument::BinaryFormatTag; - h->version = 1; - Data *d = new Data(raw, size); - d->compactionCounter = (b == header->root()) ? compactionCounter : 0; - return d; - } - - void compact(); - bool valid() const; - -private: - Q_DISABLE_COPY(Data) -}; - -} - -QT_END_NAMESPACE - -#endif // QJSON_P_H diff --git a/src/corelib/json/qjsonarray.cpp b/src/corelib/json/qjsonarray.cpp deleted file mode 100644 index c5a5aaf39d..0000000000 --- a/src/corelib/json/qjsonarray.cpp +++ /dev/null @@ -1,1258 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtCore module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include -#include -#include -#include -#include -#include - -#include "qjsonwriter_p.h" -#include "qjson_p.h" - -QT_BEGIN_NAMESPACE - -/*! - \class QJsonArray - \inmodule QtCore - \ingroup json - \ingroup shared - \reentrant - \since 5.0 - - \brief The QJsonArray class encapsulates a JSON array. - - A JSON array is a list of values. The list can be manipulated by inserting and - removing QJsonValue's from the array. - - A QJsonArray can be converted to and from a QVariantList. You can query the - number of entries with size(), insert(), and removeAt() entries from it - and iterate over its content using the standard C++ iterator pattern. - - QJsonArray is an implicitly shared class and shares the data with the document - it has been created from as long as it is not being modified. - - You can convert the array to and from text based JSON through QJsonDocument. - - \sa {JSON Support in Qt}, {JSON Save Game Example} -*/ - -/*! - \typedef QJsonArray::Iterator - - Qt-style synonym for QJsonArray::iterator. -*/ - -/*! - \typedef QJsonArray::ConstIterator - - Qt-style synonym for QJsonArray::const_iterator. -*/ - -/*! - \typedef QJsonArray::size_type - - Typedef for int. Provided for STL compatibility. -*/ - -/*! - \typedef QJsonArray::value_type - - Typedef for QJsonValue. Provided for STL compatibility. -*/ - -/*! - \typedef QJsonArray::difference_type - - Typedef for int. Provided for STL compatibility. -*/ - -/*! - \typedef QJsonArray::pointer - - Typedef for QJsonValue *. Provided for STL compatibility. -*/ - -/*! - \typedef QJsonArray::const_pointer - - Typedef for const QJsonValue *. Provided for STL compatibility. -*/ - -/*! - \typedef QJsonArray::reference - - Typedef for QJsonValue &. Provided for STL compatibility. -*/ - -/*! - \typedef QJsonArray::const_reference - - Typedef for const QJsonValue &. Provided for STL compatibility. -*/ - -/*! - Creates an empty array. - */ -QJsonArray::QJsonArray() - : d(0), a(0) -{ -} - -/*! - \fn QJsonArray::QJsonArray(std::initializer_list args) - \since 5.4 - Creates an array initialized from \a args initialization list. - - QJsonArray can be constructed in a way similar to JSON notation, - for example: - \code - QJsonArray array = { 1, 2.2, QString() }; - \endcode - */ - -/*! - \internal - */ -QJsonArray::QJsonArray(QJsonPrivate::Data *data, QJsonPrivate::Array *array) - : d(data), a(array) -{ - Q_ASSERT(data); - Q_ASSERT(array); - d->ref.ref(); -} - -/*! - This method replaces part of QJsonArray(std::initializer_list args) . - The constructor needs to be inline, but we do not want to leak implementation details - of this class. - \note this method is called for an uninitialized object - \internal - */ -void QJsonArray::initialize() -{ - d = 0; - a = 0; -} - -/*! - Deletes the array. - */ -QJsonArray::~QJsonArray() -{ - if (d && !d->ref.deref()) - delete d; -} - -/*! - Creates a copy of \a other. - - Since QJsonArray is implicitly shared, the copy is shallow - as long as the object doesn't get modified. - */ -QJsonArray::QJsonArray(const QJsonArray &other) -{ - d = other.d; - a = other.a; - if (d) - d->ref.ref(); -} - -/*! - Assigns \a other to this array. - */ -QJsonArray &QJsonArray::operator =(const QJsonArray &other) -{ - if (d != other.d) { - if (d && !d->ref.deref()) - delete d; - d = other.d; - if (d) - d->ref.ref(); - } - a = other.a; - - return *this; -} - -/*! - \fn QJsonArray::QJsonArray(QJsonArray &&other) - \since 5.10 - - Move-constructs a QJsonArray from \a other. -*/ - -/*! - \fn QJsonArray &QJsonArray::operator =(QJsonArray &&other) - \since 5.10 - - Move-assigns \a other to this array. -*/ - -/*! - \fn void QJsonArray::swap(QJsonArray &other) - \since 5.10 - - Swaps the array \a other with this. This operation is very fast and never fails. -*/ - -/*! \fn QJsonArray &QJsonArray::operator+=(const QJsonValue &value) - - Appends \a value to the array, and returns a reference to the array itself. - - \since 5.3 - \sa append(), operator<<() -*/ - -/*! \fn QJsonArray QJsonArray::operator+(const QJsonValue &value) const - - Returns an array that contains all the items in this array followed - by the provided \a value. - - \since 5.3 - \sa operator+=() -*/ - -/*! \fn QJsonArray &QJsonArray::operator<<(const QJsonValue &value) - - Appends \a value to the array, and returns a reference to the array itself. - - \since 5.3 - \sa operator+=(), append() -*/ - -/*! - Converts the string list \a list to a QJsonArray. - - The values in \a list will be converted to JSON values. - - \sa toVariantList(), QJsonValue::fromVariant() - */ -QJsonArray QJsonArray::fromStringList(const QStringList &list) -{ - QJsonArray array; - for (QStringList::const_iterator it = list.constBegin(); it != list.constEnd(); ++it) - array.append(QJsonValue(*it)); - return array; -} - -/*! - Converts the variant list \a list to a QJsonArray. - - The QVariant values in \a list will be converted to JSON values. - - \sa toVariantList(), QJsonValue::fromVariant() - */ -QJsonArray QJsonArray::fromVariantList(const QVariantList &list) -{ - QJsonArray array; - if (list.isEmpty()) - return array; - - array.detach2(1024); - - QVector values; - values.resize(list.size()); - QJsonPrivate::Value *valueData = values.data(); - uint currentOffset = sizeof(QJsonPrivate::Base); - - for (int i = 0; i < list.size(); ++i) { - QJsonValue val = QJsonValue::fromVariant(list.at(i)); - - bool latinOrIntValue; - int valueSize = QJsonPrivate::Value::requiredStorage(val, &latinOrIntValue); - - if (!array.detach2(valueSize)) - return QJsonArray(); - - QJsonPrivate::Value *v = valueData + i; - v->type = (val.t == QJsonValue::Undefined ? QJsonValue::Null : val.t); - v->latinOrIntValue = latinOrIntValue; - v->latinKey = false; - v->value = QJsonPrivate::Value::valueToStore(val, currentOffset); - if (valueSize) - QJsonPrivate::Value::copyData(val, (char *)array.a + currentOffset, latinOrIntValue); - - currentOffset += valueSize; - array.a->size = currentOffset; - } - - // write table - array.a->tableOffset = currentOffset; - if (!array.detach2(sizeof(QJsonPrivate::offset)*values.size())) - return QJsonArray(); - memcpy(array.a->table(), values.constData(), values.size()*sizeof(uint)); - array.a->length = values.size(); - array.a->size = currentOffset + sizeof(QJsonPrivate::offset)*values.size(); - - return array; -} - -/*! - Converts this object to a QVariantList. - - Returns the created map. - */ -QVariantList QJsonArray::toVariantList() const -{ - QVariantList list; - - if (a) { - list.reserve(a->length); - for (int i = 0; i < (int)a->length; ++i) - list.append(QJsonValue(d, a, a->at(i)).toVariant()); - } - return list; -} - - -/*! - Returns the number of values stored in the array. - */ -int QJsonArray::size() const -{ - if (!d) - return 0; - - return (int)a->length; -} - -/*! - \fn QJsonArray::count() const - - Same as size(). - - \sa size() -*/ - -/*! - Returns \c true if the object is empty. This is the same as size() == 0. - - \sa size() - */ -bool QJsonArray::isEmpty() const -{ - if (!d) - return true; - - return !a->length; -} - -/*! - Returns a QJsonValue representing the value for index \a i. - - The returned QJsonValue is \c Undefined, if \a i is out of bounds. - - */ -QJsonValue QJsonArray::at(int i) const -{ - if (!a || i < 0 || i >= (int)a->length) - return QJsonValue(QJsonValue::Undefined); - - return QJsonValue(d, a, a->at(i)); -} - -/*! - Returns the first value stored in the array. - - Same as \c at(0). - - \sa at() - */ -QJsonValue QJsonArray::first() const -{ - return at(0); -} - -/*! - Returns the last value stored in the array. - - Same as \c{at(size() - 1)}. - - \sa at() - */ -QJsonValue QJsonArray::last() const -{ - return at(a ? (a->length - 1) : 0); -} - -/*! - Inserts \a value at the beginning of the array. - - This is the same as \c{insert(0, value)} and will prepend \a value to the array. - - \sa append(), insert() - */ -void QJsonArray::prepend(const QJsonValue &value) -{ - insert(0, value); -} - -/*! - Inserts \a value at the end of the array. - - \sa prepend(), insert() - */ -void QJsonArray::append(const QJsonValue &value) -{ - insert(a ? (int)a->length : 0, value); -} - -/*! - Removes the value at index position \a i. \a i must be a valid - index position in the array (i.e., \c{0 <= i < size()}). - - \sa insert(), replace() - */ -void QJsonArray::removeAt(int i) -{ - if (!a || i < 0 || i >= (int)a->length) - return; - - detach2(); - a->removeItems(i, 1); - ++d->compactionCounter; - if (d->compactionCounter > 32u && d->compactionCounter >= unsigned(a->length) / 2u) - compact(); -} - -/*! \fn void QJsonArray::removeFirst() - - Removes the first item in the array. Calling this function is - equivalent to calling \c{removeAt(0)}. The array must not be empty. If - the array can be empty, call isEmpty() before calling this - function. - - \sa removeAt(), removeLast() -*/ - -/*! \fn void QJsonArray::removeLast() - - Removes the last item in the array. Calling this function is - equivalent to calling \c{removeAt(size() - 1)}. The array must not be - empty. If the array can be empty, call isEmpty() before calling - this function. - - \sa removeAt(), removeFirst() -*/ - -/*! - Removes the item at index position \a i and returns it. \a i must - be a valid index position in the array (i.e., \c{0 <= i < size()}). - - If you don't use the return value, removeAt() is more efficient. - - \sa removeAt() - */ -QJsonValue QJsonArray::takeAt(int i) -{ - if (!a || i < 0 || i >= (int)a->length) - return QJsonValue(QJsonValue::Undefined); - - QJsonValue v(d, a, a->at(i)); - removeAt(i); // detaches - return v; -} - -/*! - Inserts \a value at index position \a i in the array. If \a i - is \c 0, the value is prepended to the array. If \a i is size(), the - value is appended to the array. - - \sa append(), prepend(), replace(), removeAt() - */ -void QJsonArray::insert(int i, const QJsonValue &value) -{ - Q_ASSERT (i >= 0 && i <= (a ? (int)a->length : 0)); - QJsonValue val = value; - - bool compressed; - int valueSize = QJsonPrivate::Value::requiredStorage(val, &compressed); - - if (!detach2(valueSize + sizeof(QJsonPrivate::Value))) - return; - - if (!a->length) - a->tableOffset = sizeof(QJsonPrivate::Array); - - int valueOffset = a->reserveSpace(valueSize, i, 1, false); - if (!valueOffset) - return; - - QJsonPrivate::Value &v = (*a)[i]; - v.type = (val.t == QJsonValue::Undefined ? QJsonValue::Null : val.t); - v.latinOrIntValue = compressed; - v.latinKey = false; - v.value = QJsonPrivate::Value::valueToStore(val, valueOffset); - if (valueSize) - QJsonPrivate::Value::copyData(val, (char *)a + valueOffset, compressed); -} - -/*! - \fn QJsonArray::iterator QJsonArray::insert(iterator before, const QJsonValue &value) - - Inserts \a value before the position pointed to by \a before, and returns an iterator - pointing to the newly inserted item. - - \sa erase(), insert() -*/ - -/*! - \fn QJsonArray::iterator QJsonArray::erase(iterator it) - - Removes the item pointed to by \a it, and returns an iterator pointing to the - next item. - - \sa removeAt() -*/ - -/*! - Replaces the item at index position \a i with \a value. \a i must - be a valid index position in the array (i.e., \c{0 <= i < size()}). - - \sa operator[](), removeAt() - */ -void QJsonArray::replace(int i, const QJsonValue &value) -{ - Q_ASSERT (a && i >= 0 && i < (int)(a->length)); - QJsonValue val = value; - - bool compressed; - int valueSize = QJsonPrivate::Value::requiredStorage(val, &compressed); - - if (!detach2(valueSize)) - return; - - if (!a->length) - a->tableOffset = sizeof(QJsonPrivate::Array); - - int valueOffset = a->reserveSpace(valueSize, i, 1, true); - if (!valueOffset) - return; - - QJsonPrivate::Value &v = (*a)[i]; - v.type = (val.t == QJsonValue::Undefined ? QJsonValue::Null : val.t); - v.latinOrIntValue = compressed; - v.latinKey = false; - v.value = QJsonPrivate::Value::valueToStore(val, valueOffset); - if (valueSize) - QJsonPrivate::Value::copyData(val, (char *)a + valueOffset, compressed); - - ++d->compactionCounter; - if (d->compactionCounter > 32u && d->compactionCounter >= unsigned(a->length) / 2u) - compact(); -} - -/*! - Returns \c true if the array contains an occurrence of \a value, otherwise \c false. - - \sa count() - */ -bool QJsonArray::contains(const QJsonValue &value) const -{ - for (int i = 0; i < size(); i++) { - if (at(i) == value) - return true; - } - return false; -} - -/*! - Returns the value at index position \a i as a modifiable reference. - \a i must be a valid index position in the array (i.e., \c{0 <= i < - size()}). - - The return value is of type QJsonValueRef, a helper class for QJsonArray - and QJsonObject. When you get an object of type QJsonValueRef, you can - use it as if it were a reference to a QJsonValue. If you assign to it, - the assignment will apply to the character in the QJsonArray of QJsonObject - from which you got the reference. - - \sa at() - */ -QJsonValueRef QJsonArray::operator [](int i) -{ - Q_ASSERT(a && i >= 0 && i < (int)a->length); - return QJsonValueRef(this, i); -} - -/*! - \overload - - Same as at(). - */ -QJsonValue QJsonArray::operator[](int i) const -{ - return at(i); -} - -/*! - Returns \c true if this array is equal to \a other. - */ -bool QJsonArray::operator==(const QJsonArray &other) const -{ - if (a == other.a) - return true; - - if (!a) - return !other.a->length; - if (!other.a) - return !a->length; - if (a->length != other.a->length) - return false; - - for (int i = 0; i < (int)a->length; ++i) { - if (QJsonValue(d, a, a->at(i)) != QJsonValue(other.d, other.a, other.a->at(i))) - return false; - } - return true; -} - -/*! - Returns \c true if this array is not equal to \a other. - */ -bool QJsonArray::operator!=(const QJsonArray &other) const -{ - return !(*this == other); -} - -/*! \fn QJsonArray::iterator QJsonArray::begin() - - Returns an \l{STL-style iterators}{STL-style iterator} pointing to the first item in - the array. - - \sa constBegin(), end() -*/ - -/*! \fn QJsonArray::const_iterator QJsonArray::begin() const - - \overload -*/ - -/*! \fn QJsonArray::const_iterator QJsonArray::constBegin() const - - Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the first item - in the array. - - \sa begin(), constEnd() -*/ - -/*! \fn QJsonArray::iterator QJsonArray::end() - - Returns an \l{STL-style iterators}{STL-style iterator} pointing to the imaginary item - after the last item in the array. - - \sa begin(), constEnd() -*/ - -/*! \fn const_iterator QJsonArray::end() const - - \overload -*/ - -/*! \fn QJsonArray::const_iterator QJsonArray::constEnd() const - - Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the imaginary - item after the last item in the array. - - \sa constBegin(), end() -*/ - -/*! \fn void QJsonArray::push_back(const QJsonValue &value) - - This function is provided for STL compatibility. It is equivalent - to \l{QJsonArray::append()}{append(value)} and will append \a value to the array. -*/ - -/*! \fn void QJsonArray::push_front(const QJsonValue &value) - - This function is provided for STL compatibility. It is equivalent - to \l{QJsonArray::prepend()}{prepend(value)} and will prepend \a value to the array. -*/ - -/*! \fn void QJsonArray::pop_front() - - This function is provided for STL compatibility. It is equivalent - to removeFirst(). The array must not be empty. If the array can be - empty, call isEmpty() before calling this function. -*/ - -/*! \fn void QJsonArray::pop_back() - - This function is provided for STL compatibility. It is equivalent - to removeLast(). The array must not be empty. If the array can be - empty, call isEmpty() before calling this function. -*/ - -/*! \fn bool QJsonArray::empty() const - - This function is provided for STL compatibility. It is equivalent - to isEmpty() and returns \c true if the array is empty. -*/ - -/*! \class QJsonArray::iterator - \inmodule QtCore - \brief The QJsonArray::iterator class provides an STL-style non-const iterator for QJsonArray. - - QJsonArray::iterator allows you to iterate over a QJsonArray - and to modify the array item associated with the - iterator. If you want to iterate over a const QJsonArray, use - QJsonArray::const_iterator instead. It is generally a good practice to - use QJsonArray::const_iterator on a non-const QJsonArray as well, unless - you need to change the QJsonArray through the iterator. Const - iterators are slightly faster and improves code readability. - - The default QJsonArray::iterator constructor creates an uninitialized - iterator. You must initialize it using a QJsonArray function like - QJsonArray::begin(), QJsonArray::end(), or QJsonArray::insert() before you can - start iterating. - - Most QJsonArray functions accept an integer index rather than an - iterator. For that reason, iterators are rarely useful in - connection with QJsonArray. One place where STL-style iterators do - make sense is as arguments to \l{generic algorithms}. - - Multiple iterators can be used on the same array. However, be - aware that any non-const function call performed on the QJsonArray - will render all existing iterators undefined. - - \sa QJsonArray::const_iterator -*/ - -/*! \typedef QJsonArray::iterator::iterator_category - - A synonym for \e {std::random_access_iterator_tag} indicating - this iterator is a random access iterator. -*/ - -/*! \typedef QJsonArray::iterator::difference_type - - \internal -*/ - -/*! \typedef QJsonArray::iterator::value_type - - \internal -*/ - -/*! \typedef QJsonArray::iterator::reference - - \internal -*/ - -/*! \typedef QJsonArray::iterator::pointer - - \internal -*/ - -/*! \fn QJsonArray::iterator::iterator() - - Constructs an uninitialized iterator. - - Functions like operator*() and operator++() should not be called - on an uninitialized iterator. Use operator=() to assign a value - to it before using it. - - \sa QJsonArray::begin(), QJsonArray::end() -*/ - -/*! \fn QJsonArray::iterator::iterator(QJsonArray *array, int index) - \internal -*/ - -/*! \fn QJsonValueRef QJsonArray::iterator::operator*() const - - - Returns a modifiable reference to the current item. - - You can change the value of an item by using operator*() on the - left side of an assignment. - - The return value is of type QJsonValueRef, a helper class for QJsonArray - and QJsonObject. When you get an object of type QJsonValueRef, you can - use it as if it were a reference to a QJsonValue. If you assign to it, - the assignment will apply to the character in the QJsonArray of QJsonObject - from which you got the reference. -*/ - -/*! \fn QJsonValueRef *QJsonArray::iterator::operator->() const - - Returns a pointer to a modifiable reference to the current item. -*/ - -/*! \fn QJsonValueRef QJsonArray::iterator::operator[](int j) const - - Returns a modifiable reference to the item at offset \a j from the - item pointed to by this iterator (the item at position \c{*this + j}). - - This function is provided to make QJsonArray iterators behave like C++ - pointers. - - The return value is of type QJsonValueRef, a helper class for QJsonArray - and QJsonObject. When you get an object of type QJsonValueRef, you can - use it as if it were a reference to a QJsonValue. If you assign to it, - the assignment will apply to the character in the QJsonArray of QJsonObject - from which you got the reference. - - \sa operator+() -*/ - -/*! - \fn bool QJsonArray::iterator::operator==(const iterator &other) const - \fn bool QJsonArray::iterator::operator==(const const_iterator &other) const - - Returns \c true if \a other points to the same item as this - iterator; otherwise returns \c false. - - \sa operator!=() -*/ - -/*! - \fn bool QJsonArray::iterator::operator!=(const iterator &other) const - \fn bool QJsonArray::iterator::operator!=(const const_iterator &other) const - - Returns \c true if \a other points to a different item than this - iterator; otherwise returns \c false. - - \sa operator==() -*/ - -/*! - \fn bool QJsonArray::iterator::operator<(const iterator& other) const - \fn bool QJsonArray::iterator::operator<(const const_iterator& other) const - - Returns \c true if the item pointed to by this iterator is less than - the item pointed to by the \a other iterator. -*/ - -/*! - \fn bool QJsonArray::iterator::operator<=(const iterator& other) const - \fn bool QJsonArray::iterator::operator<=(const const_iterator& other) const - - Returns \c true if the item pointed to by this iterator is less than - or equal to the item pointed to by the \a other iterator. -*/ - -/*! - \fn bool QJsonArray::iterator::operator>(const iterator& other) const - \fn bool QJsonArray::iterator::operator>(const const_iterator& other) const - - Returns \c true if the item pointed to by this iterator is greater - than the item pointed to by the \a other iterator. -*/ - -/*! - \fn bool QJsonArray::iterator::operator>=(const iterator& other) const - \fn bool QJsonArray::iterator::operator>=(const const_iterator& other) const - - Returns \c true if the item pointed to by this iterator is greater - than or equal to the item pointed to by the \a other iterator. -*/ - -/*! \fn QJsonArray::iterator &QJsonArray::iterator::operator++() - - The prefix ++ operator, \c{++it}, advances the iterator to the - next item in the array and returns an iterator to the new current - item. - - Calling this function on QJsonArray::end() leads to undefined results. - - \sa operator--() -*/ - -/*! \fn QJsonArray::iterator QJsonArray::iterator::operator++(int) - - \overload - - The postfix ++ operator, \c{it++}, advances the iterator to the - next item in the array and returns an iterator to the previously - current item. -*/ - -/*! \fn QJsonArray::iterator &QJsonArray::iterator::operator--() - - The prefix -- operator, \c{--it}, makes the preceding item - current and returns an iterator to the new current item. - - Calling this function on QJsonArray::begin() leads to undefined results. - - \sa operator++() -*/ - -/*! \fn QJsonArray::iterator QJsonArray::iterator::operator--(int) - - \overload - - The postfix -- operator, \c{it--}, makes the preceding item - current and returns an iterator to the previously current item. -*/ - -/*! \fn QJsonArray::iterator &QJsonArray::iterator::operator+=(int j) - - Advances the iterator by \a j items. If \a j is negative, the - iterator goes backward. - - \sa operator-=(), operator+() -*/ - -/*! \fn QJsonArray::iterator &QJsonArray::iterator::operator-=(int j) - - Makes the iterator go back by \a j items. If \a j is negative, - the iterator goes forward. - - \sa operator+=(), operator-() -*/ - -/*! \fn QJsonArray::iterator QJsonArray::iterator::operator+(int j) const - - Returns an iterator to the item at \a j positions forward from - this iterator. If \a j is negative, the iterator goes backward. - - \sa operator-(), operator+=() -*/ - -/*! \fn QJsonArray::iterator QJsonArray::iterator::operator-(int j) const - - Returns an iterator to the item at \a j positions backward from - this iterator. If \a j is negative, the iterator goes forward. - - \sa operator+(), operator-=() -*/ - -/*! \fn int QJsonArray::iterator::operator-(iterator other) const - - Returns the number of items between the item pointed to by \a - other and the item pointed to by this iterator. -*/ - -/*! \class QJsonArray::const_iterator - \inmodule QtCore - \brief The QJsonArray::const_iterator class provides an STL-style const iterator for QJsonArray. - - QJsonArray::const_iterator allows you to iterate over a - QJsonArray. If you want to modify the QJsonArray as - you iterate over it, use QJsonArray::iterator instead. It is generally a - good practice to use QJsonArray::const_iterator on a non-const QJsonArray - as well, unless you need to change the QJsonArray through the - iterator. Const iterators are slightly faster and improves - code readability. - - The default QJsonArray::const_iterator constructor creates an - uninitialized iterator. You must initialize it using a QJsonArray - function like QJsonArray::constBegin(), QJsonArray::constEnd(), or - QJsonArray::insert() before you can start iterating. - - Most QJsonArray functions accept an integer index rather than an - iterator. For that reason, iterators are rarely useful in - connection with QJsonArray. One place where STL-style iterators do - make sense is as arguments to \l{generic algorithms}. - - Multiple iterators can be used on the same array. However, be - aware that any non-const function call performed on the QJsonArray - will render all existing iterators undefined. - - \sa QJsonArray::iterator -*/ - -/*! \fn QJsonArray::const_iterator::const_iterator() - - Constructs an uninitialized iterator. - - Functions like operator*() and operator++() should not be called - on an uninitialized iterator. Use operator=() to assign a value - to it before using it. - - \sa QJsonArray::constBegin(), QJsonArray::constEnd() -*/ - -/*! \fn QJsonArray::const_iterator::const_iterator(const QJsonArray *array, int index) - \internal -*/ - -/*! \typedef QJsonArray::const_iterator::iterator_category - - A synonym for \e {std::random_access_iterator_tag} indicating - this iterator is a random access iterator. -*/ - -/*! \typedef QJsonArray::const_iterator::difference_type - - \internal -*/ - -/*! \typedef QJsonArray::const_iterator::value_type - - \internal -*/ - -/*! \typedef QJsonArray::const_iterator::reference - - \internal -*/ - -/*! \typedef QJsonArray::const_iterator::pointer - - \internal -*/ - -/*! \fn QJsonArray::const_iterator::const_iterator(const const_iterator &other) - - Constructs a copy of \a other. -*/ - -/*! \fn QJsonArray::const_iterator::const_iterator(const iterator &other) - - Constructs a copy of \a other. -*/ - -/*! \fn QJsonValue QJsonArray::const_iterator::operator*() const - - Returns the current item. -*/ - -/*! \fn QJsonValue *QJsonArray::const_iterator::operator->() const - - Returns a pointer to the current item. -*/ - -/*! \fn QJsonValue QJsonArray::const_iterator::operator[](int j) const - - Returns the item at offset \a j from the item pointed to by this iterator (the item at - position \c{*this + j}). - - This function is provided to make QJsonArray iterators behave like C++ - pointers. - - \sa operator+() -*/ - -/*! \fn bool QJsonArray::const_iterator::operator==(const const_iterator &other) const - - Returns \c true if \a other points to the same item as this - iterator; otherwise returns \c false. - - \sa operator!=() -*/ - -/*! \fn bool QJsonArray::const_iterator::operator!=(const const_iterator &other) const - - Returns \c true if \a other points to a different item than this - iterator; otherwise returns \c false. - - \sa operator==() -*/ - -/*! - \fn bool QJsonArray::const_iterator::operator<(const const_iterator& other) const - - Returns \c true if the item pointed to by this iterator is less than - the item pointed to by the \a other iterator. -*/ - -/*! - \fn bool QJsonArray::const_iterator::operator<=(const const_iterator& other) const - - Returns \c true if the item pointed to by this iterator is less than - or equal to the item pointed to by the \a other iterator. -*/ - -/*! - \fn bool QJsonArray::const_iterator::operator>(const const_iterator& other) const - - Returns \c true if the item pointed to by this iterator is greater - than the item pointed to by the \a other iterator. -*/ - -/*! - \fn bool QJsonArray::const_iterator::operator>=(const const_iterator& other) const - - Returns \c true if the item pointed to by this iterator is greater - than or equal to the item pointed to by the \a other iterator. -*/ - -/*! \fn QJsonArray::const_iterator &QJsonArray::const_iterator::operator++() - - The prefix ++ operator, \c{++it}, advances the iterator to the - next item in the array and returns an iterator to the new current - item. - - Calling this function on QJsonArray::end() leads to undefined results. - - \sa operator--() -*/ - -/*! \fn QJsonArray::const_iterator QJsonArray::const_iterator::operator++(int) - - \overload - - The postfix ++ operator, \c{it++}, advances the iterator to the - next item in the array and returns an iterator to the previously - current item. -*/ - -/*! \fn QJsonArray::const_iterator &QJsonArray::const_iterator::operator--() - - The prefix -- operator, \c{--it}, makes the preceding item - current and returns an iterator to the new current item. - - Calling this function on QJsonArray::begin() leads to undefined results. - - \sa operator++() -*/ - -/*! \fn QJsonArray::const_iterator QJsonArray::const_iterator::operator--(int) - - \overload - - The postfix -- operator, \c{it--}, makes the preceding item - current and returns an iterator to the previously current item. -*/ - -/*! \fn QJsonArray::const_iterator &QJsonArray::const_iterator::operator+=(int j) - - Advances the iterator by \a j items. If \a j is negative, the - iterator goes backward. - - \sa operator-=(), operator+() -*/ - -/*! \fn QJsonArray::const_iterator &QJsonArray::const_iterator::operator-=(int j) - - Makes the iterator go back by \a j items. If \a j is negative, - the iterator goes forward. - - \sa operator+=(), operator-() -*/ - -/*! \fn QJsonArray::const_iterator QJsonArray::const_iterator::operator+(int j) const - - Returns an iterator to the item at \a j positions forward from - this iterator. If \a j is negative, the iterator goes backward. - - \sa operator-(), operator+=() -*/ - -/*! \fn QJsonArray::const_iterator QJsonArray::const_iterator::operator-(int j) const - - Returns an iterator to the item at \a j positions backward from - this iterator. If \a j is negative, the iterator goes forward. - - \sa operator+(), operator-=() -*/ - -/*! \fn int QJsonArray::const_iterator::operator-(const_iterator other) const - - Returns the number of items between the item pointed to by \a - other and the item pointed to by this iterator. -*/ - - -/*! - \internal - */ -void QJsonArray::detach(uint reserve) -{ - Q_UNUSED(reserve) - Q_ASSERT(!reserve); - detach2(0); -} - -/*! - \internal - */ -bool QJsonArray::detach2(uint reserve) -{ - if (!d) { - if (reserve >= QJsonPrivate::Value::MaxSize) { - qWarning("QJson: Document too large to store in data structure"); - return false; - } - d = new QJsonPrivate::Data(reserve, QJsonValue::Array); - a = static_cast(d->header->root()); - d->ref.ref(); - return true; - } - if (reserve == 0 && d->ref.load() == 1) - return true; - - QJsonPrivate::Data *x = d->clone(a, reserve); - if (!x) - return false; - x->ref.ref(); - if (!d->ref.deref()) - delete d; - d = x; - a = static_cast(d->header->root()); - return true; -} - -/*! - \internal - */ -void QJsonArray::compact() -{ - if (!d || !d->compactionCounter) - return; - - detach2(); - d->compact(); - a = static_cast(d->header->root()); -} - - -#if !defined(QT_NO_DEBUG_STREAM) && !defined(QT_JSON_READONLY) -QDebug operator<<(QDebug dbg, const QJsonArray &a) -{ - QDebugStateSaver saver(dbg); - if (!a.a) { - dbg << "QJsonArray()"; - return dbg; - } - QByteArray json; - QJsonPrivate::Writer::arrayToJson(a.a, json, 0, true); - dbg.nospace() << "QJsonArray(" - << json.constData() // print as utf-8 string without extra quotation marks - << ")"; - return dbg; -} -#endif - -QT_END_NAMESPACE - diff --git a/src/corelib/json/qjsonarray.h b/src/corelib/json/qjsonarray.h deleted file mode 100644 index 8d41138c97..0000000000 --- a/src/corelib/json/qjsonarray.h +++ /dev/null @@ -1,274 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtCore module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QJSONARRAY_H -#define QJSONARRAY_H - -#include -#include -#if defined(Q_COMPILER_INITIALIZER_LISTS) -#include -#endif - -QT_BEGIN_NAMESPACE - -class QDebug; -class QStringList; -template class QList; -typedef QList QVariantList; - -class Q_CORE_EXPORT QJsonArray -{ -public: - QJsonArray(); - -#if defined(Q_COMPILER_INITIALIZER_LISTS) || defined(Q_QDOC) - QJsonArray(std::initializer_list args) - { - initialize(); - for (std::initializer_list::const_iterator i = args.begin(); i != args.end(); ++i) - append(*i); - } -#endif - - ~QJsonArray(); - - QJsonArray(const QJsonArray &other); - QJsonArray &operator =(const QJsonArray &other); - - QJsonArray(QJsonArray &&other) Q_DECL_NOTHROW - : d(other.d), - a(other.a) - { - other.d = nullptr; - other.a = nullptr; - } - - QJsonArray &operator =(QJsonArray &&other) Q_DECL_NOTHROW - { - swap(other); - return *this; - } - - static QJsonArray fromStringList(const QStringList &list); - static QJsonArray fromVariantList(const QVariantList &list); - QVariantList toVariantList() const; - - int size() const; - inline int count() const { return size(); } - - bool isEmpty() const; - QJsonValue at(int i) const; - QJsonValue first() const; - QJsonValue last() const; - - void prepend(const QJsonValue &value); - void append(const QJsonValue &value); - void removeAt(int i); - QJsonValue takeAt(int i); - inline void removeFirst() { removeAt(0); } - inline void removeLast() { removeAt(size() - 1); } - - void insert(int i, const QJsonValue &value); - void replace(int i, const QJsonValue &value); - - bool contains(const QJsonValue &element) const; - QJsonValueRef operator[](int i); - QJsonValue operator[](int i) const; - - bool operator==(const QJsonArray &other) const; - bool operator!=(const QJsonArray &other) const; - - void swap(QJsonArray &other) Q_DECL_NOTHROW - { - qSwap(d, other.d); - qSwap(a, other.a); - } - - class const_iterator; - - class iterator { - public: - QJsonArray *a; - int i; - typedef std::random_access_iterator_tag iterator_category; - typedef int difference_type; - typedef QJsonValue value_type; - typedef QJsonValueRef reference; - typedef QJsonValueRefPtr pointer; - - inline iterator() : a(nullptr), i(0) { } - explicit inline iterator(QJsonArray *array, int index) : a(array), i(index) { } - - inline QJsonValueRef operator*() const { return QJsonValueRef(a, i); } -#ifdef Q_QDOC - inline QJsonValueRef* operator->() const; -#else - inline QJsonValueRefPtr operator->() const { return QJsonValueRefPtr(a, i); } -#endif - inline QJsonValueRef operator[](int j) const { return QJsonValueRef(a, i + j); } - - inline bool operator==(const iterator &o) const { return i == o.i; } - inline bool operator!=(const iterator &o) const { return i != o.i; } - inline bool operator<(const iterator& other) const { return i < other.i; } - inline bool operator<=(const iterator& other) const { return i <= other.i; } - inline bool operator>(const iterator& other) const { return i > other.i; } - inline bool operator>=(const iterator& other) const { return i >= other.i; } - inline bool operator==(const const_iterator &o) const { return i == o.i; } - inline bool operator!=(const const_iterator &o) const { return i != o.i; } - inline bool operator<(const const_iterator& other) const { return i < other.i; } - inline bool operator<=(const const_iterator& other) const { return i <= other.i; } - inline bool operator>(const const_iterator& other) const { return i > other.i; } - inline bool operator>=(const const_iterator& other) const { return i >= other.i; } - inline iterator &operator++() { ++i; return *this; } - inline iterator operator++(int) { iterator n = *this; ++i; return n; } - inline iterator &operator--() { i--; return *this; } - inline iterator operator--(int) { iterator n = *this; i--; return n; } - inline iterator &operator+=(int j) { i+=j; return *this; } - inline iterator &operator-=(int j) { i-=j; return *this; } - inline iterator operator+(int j) const { return iterator(a, i+j); } - inline iterator operator-(int j) const { return iterator(a, i-j); } - inline int operator-(iterator j) const { return i - j.i; } - }; - friend class iterator; - - class const_iterator { - public: - const QJsonArray *a; - int i; - typedef std::random_access_iterator_tag iterator_category; - typedef qptrdiff difference_type; - typedef QJsonValue value_type; - typedef QJsonValue reference; - typedef QJsonValuePtr pointer; - - inline const_iterator() : a(nullptr), i(0) { } - explicit inline const_iterator(const QJsonArray *array, int index) : a(array), i(index) { } -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - inline const_iterator(const const_iterator &o) : a(o.a), i(o.i) {} // ### Qt 6: Removed so class can be trivially-copyable -#endif - inline const_iterator(const iterator &o) : a(o.a), i(o.i) {} - - inline QJsonValue operator*() const { return a->at(i); } -#ifdef Q_QDOC - inline QJsonValue* operator->() const; -#else - inline QJsonValuePtr operator->() const { return QJsonValuePtr(a->at(i)); } -#endif - inline QJsonValue operator[](int j) const { return a->at(i+j); } - inline bool operator==(const const_iterator &o) const { return i == o.i; } - inline bool operator!=(const const_iterator &o) const { return i != o.i; } - inline bool operator<(const const_iterator& other) const { return i < other.i; } - inline bool operator<=(const const_iterator& other) const { return i <= other.i; } - inline bool operator>(const const_iterator& other) const { return i > other.i; } - inline bool operator>=(const const_iterator& other) const { return i >= other.i; } - inline const_iterator &operator++() { ++i; return *this; } - inline const_iterator operator++(int) { const_iterator n = *this; ++i; return n; } - inline const_iterator &operator--() { i--; return *this; } - inline const_iterator operator--(int) { const_iterator n = *this; i--; return n; } - inline const_iterator &operator+=(int j) { i+=j; return *this; } - inline const_iterator &operator-=(int j) { i-=j; return *this; } - inline const_iterator operator+(int j) const { return const_iterator(a, i+j); } - inline const_iterator operator-(int j) const { return const_iterator(a, i-j); } - inline int operator-(const_iterator j) const { return i - j.i; } - }; - friend class const_iterator; - - // stl style - inline iterator begin() { detach2(); return iterator(this, 0); } - inline const_iterator begin() const { return const_iterator(this, 0); } - inline const_iterator constBegin() const { return const_iterator(this, 0); } - inline iterator end() { detach2(); return iterator(this, size()); } - inline const_iterator end() const { return const_iterator(this, size()); } - inline const_iterator constEnd() const { return const_iterator(this, size()); } - iterator insert(iterator before, const QJsonValue &value) { insert(before.i, value); return before; } - iterator erase(iterator it) { removeAt(it.i); return it; } - - // more Qt - typedef iterator Iterator; - typedef const_iterator ConstIterator; - - // convenience - inline QJsonArray operator+(const QJsonValue &v) const - { QJsonArray n = *this; n += v; return n; } - inline QJsonArray &operator+=(const QJsonValue &v) - { append(v); return *this; } - inline QJsonArray &operator<< (const QJsonValue &v) - { append(v); return *this; } - - // stl compatibility - inline void push_back(const QJsonValue &t) { append(t); } - inline void push_front(const QJsonValue &t) { prepend(t); } - inline void pop_front() { removeFirst(); } - inline void pop_back() { removeLast(); } - inline bool empty() const { return isEmpty(); } - typedef int size_type; - typedef QJsonValue value_type; - typedef value_type *pointer; - typedef const value_type *const_pointer; - typedef QJsonValueRef reference; - typedef QJsonValue const_reference; - typedef int difference_type; - -private: - friend class QJsonPrivate::Data; - friend class QJsonValue; - friend class QJsonDocument; - friend Q_CORE_EXPORT QDebug operator<<(QDebug, const QJsonArray &); - - QJsonArray(QJsonPrivate::Data *data, QJsonPrivate::Array *array); - void initialize(); - void compact(); - // ### Qt 6: remove me and merge with detach2 - void detach(uint reserve = 0); - bool detach2(uint reserve = 0); - - QJsonPrivate::Data *d; - QJsonPrivate::Array *a; -}; - -Q_DECLARE_SHARED_NOT_MOVABLE_UNTIL_QT6(QJsonArray) - -#if !defined(QT_NO_DEBUG_STREAM) && !defined(QT_JSON_READONLY) -Q_CORE_EXPORT QDebug operator<<(QDebug, const QJsonArray &); -#endif - -QT_END_NAMESPACE - -#endif // QJSONARRAY_H diff --git a/src/corelib/json/qjsondocument.cpp b/src/corelib/json/qjsondocument.cpp deleted file mode 100644 index 9794bca60d..0000000000 --- a/src/corelib/json/qjsondocument.cpp +++ /dev/null @@ -1,667 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtCore module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include "qjsonwriter_p.h" -#include "qjsonparser_p.h" -#include "qjson_p.h" - -QT_BEGIN_NAMESPACE - -/*! \class QJsonDocument - \inmodule QtCore - \ingroup json - \ingroup shared - \reentrant - \since 5.0 - - \brief The QJsonDocument class provides a way to read and write JSON documents. - - QJsonDocument is a class that wraps a complete JSON document and can read and - write this document both from a UTF-8 encoded text based representation as well - as Qt's own binary format. - - A JSON document can be converted from its text-based representation to a QJsonDocument - using QJsonDocument::fromJson(). toJson() converts it back to text. The parser is very - fast and efficient and converts the JSON to the binary representation used by Qt. - - Validity of the parsed document can be queried with !isNull() - - A document can be queried as to whether it contains an array or an object using isArray() - and isObject(). The array or object contained in the document can be retrieved using - array() or object() and then read or manipulated. - - A document can also be created from a stored binary representation using fromBinaryData() or - fromRawData(). - - \sa {JSON Support in Qt}, {JSON Save Game Example} -*/ - -/*! - * Constructs an empty and invalid document. - */ -QJsonDocument::QJsonDocument() - : d(0) -{ -} - -/*! - * Creates a QJsonDocument from \a object. - */ -QJsonDocument::QJsonDocument(const QJsonObject &object) - : d(0) -{ - setObject(object); -} - -/*! - * Constructs a QJsonDocument from \a array. - */ -QJsonDocument::QJsonDocument(const QJsonArray &array) - : d(0) -{ - setArray(array); -} - -/*! - \internal - */ -QJsonDocument::QJsonDocument(QJsonPrivate::Data *data) - : d(data) -{ - Q_ASSERT(d); - d->ref.ref(); -} - -/*! - Deletes the document. - - Binary data set with fromRawData is not freed. - */ -QJsonDocument::~QJsonDocument() -{ - if (d && !d->ref.deref()) - delete d; -} - -/*! - * Creates a copy of the \a other document. - */ -QJsonDocument::QJsonDocument(const QJsonDocument &other) -{ - d = other.d; - if (d) - d->ref.ref(); -} - -/*! - * Assigns the \a other document to this QJsonDocument. - * Returns a reference to this object. - */ -QJsonDocument &QJsonDocument::operator =(const QJsonDocument &other) -{ - if (d != other.d) { - if (d && !d->ref.deref()) - delete d; - d = other.d; - if (d) - d->ref.ref(); - } - - return *this; -} - -/*! - \fn QJsonDocument::QJsonDocument(QJsonDocument &&other) - \since 5.10 - - Move-constructs a QJsonDocument from \a other. -*/ - -/*! - \fn QJsonDocument &QJsonDocument::operator =(QJsonDocument &&other) - \since 5.10 - - Move-assigns \a other to this document. -*/ - -/*! - \fn void QJsonDocument::swap(QJsonDocument &other) - \since 5.10 - - Swaps the document \a other with this. This operation is very fast and never fails. -*/ - - -/*! \enum QJsonDocument::DataValidation - - This value is used to tell QJsonDocument whether to validate the binary data - when converting to a QJsonDocument using fromBinaryData() or fromRawData(). - - \value Validate Validate the data before using it. This is the default. - \value BypassValidation Bypasses data validation. Only use if you received the - data from a trusted place and know it's valid, as using of invalid data can crash - the application. - */ - -/*! - Creates a QJsonDocument that uses the first \a size bytes from - \a data. It assumes \a data contains a binary encoded JSON document. - The created document does not take ownership of \a data and the caller - has to guarantee that \a data will not be deleted or modified as long as - any QJsonDocument, QJsonObject or QJsonArray still references the data. - - \a data has to be aligned to a 4 byte boundary. - - \a validation decides whether the data is checked for validity before being used. - By default the data is validated. If the \a data is not valid, the method returns - a null document. - - Returns a QJsonDocument representing the data. - - \sa rawData(), fromBinaryData(), isNull(), DataValidation - */ -QJsonDocument QJsonDocument::fromRawData(const char *data, int size, DataValidation validation) -{ - if (quintptr(data) & 3) { - qWarning("QJsonDocument::fromRawData: data has to have 4 byte alignment"); - return QJsonDocument(); - } - - QJsonPrivate::Data *d = new QJsonPrivate::Data((char *)data, size); - d->ownsData = false; - - if (validation != BypassValidation && !d->valid()) { - delete d; - return QJsonDocument(); - } - - return QJsonDocument(d); -} - -/*! - Returns the raw binary representation of the data - \a size will contain the size of the returned data. - - This method is useful to e.g. stream the JSON document - in it's binary form to a file. - */ -const char *QJsonDocument::rawData(int *size) const -{ - if (!d) { - *size = 0; - return 0; - } - *size = d->alloc; - return d->rawData; -} - -/*! - Creates a QJsonDocument from \a data. - - \a validation decides whether the data is checked for validity before being used. - By default the data is validated. If the \a data is not valid, the method returns - a null document. - - \sa toBinaryData(), fromRawData(), isNull(), DataValidation - */ -QJsonDocument QJsonDocument::fromBinaryData(const QByteArray &data, DataValidation validation) -{ - if (data.size() < (int)(sizeof(QJsonPrivate::Header) + sizeof(QJsonPrivate::Base))) - return QJsonDocument(); - - QJsonPrivate::Header h; - memcpy(&h, data.constData(), sizeof(QJsonPrivate::Header)); - QJsonPrivate::Base root; - memcpy(&root, data.constData() + sizeof(QJsonPrivate::Header), sizeof(QJsonPrivate::Base)); - - // do basic checks here, so we don't try to allocate more memory than we can. - if (h.tag != QJsonDocument::BinaryFormatTag || h.version != 1u || - sizeof(QJsonPrivate::Header) + root.size > (uint)data.size()) - return QJsonDocument(); - - const uint size = sizeof(QJsonPrivate::Header) + root.size; - char *raw = (char *)malloc(size); - if (!raw) - return QJsonDocument(); - - memcpy(raw, data.constData(), size); - QJsonPrivate::Data *d = new QJsonPrivate::Data(raw, size); - - if (validation != BypassValidation && !d->valid()) { - delete d; - return QJsonDocument(); - } - - return QJsonDocument(d); -} - -/*! - Creates a QJsonDocument from the QVariant \a variant. - - If the \a variant contains any other type than a QVariantMap, - QVariantHash, QVariantList or QStringList, the returned document is invalid. - - \sa toVariant() - */ -QJsonDocument QJsonDocument::fromVariant(const QVariant &variant) -{ - QJsonDocument doc; - switch (variant.type()) { - case QVariant::Map: - doc.setObject(QJsonObject::fromVariantMap(variant.toMap())); - break; - case QVariant::Hash: - doc.setObject(QJsonObject::fromVariantHash(variant.toHash())); - break; - case QVariant::List: - doc.setArray(QJsonArray::fromVariantList(variant.toList())); - break; - case QVariant::StringList: - doc.setArray(QJsonArray::fromStringList(variant.toStringList())); - break; - default: - break; - } - return doc; -} - -/*! - Returns a QVariant representing the Json document. - - The returned variant will be a QVariantList if the document is - a QJsonArray and a QVariantMap if the document is a QJsonObject. - - \sa fromVariant(), QJsonValue::toVariant() - */ -QVariant QJsonDocument::toVariant() const -{ - if (!d) - return QVariant(); - - if (d->header->root()->isArray()) - return QJsonArray(d, static_cast(d->header->root())).toVariantList(); - else - return QJsonObject(d, static_cast(d->header->root())).toVariantMap(); -} - -/*! - Converts the QJsonDocument to a UTF-8 encoded JSON document. - - \sa fromJson() - */ -#if !defined(QT_JSON_READONLY) || defined(Q_CLANG_QDOC) -QByteArray QJsonDocument::toJson() const -{ - return toJson(Indented); -} -#endif - -/*! - \enum QJsonDocument::JsonFormat - - This value defines the format of the JSON byte array produced - when converting to a QJsonDocument using toJson(). - - \value Indented Defines human readable output as follows: - \code - { - "Array": [ - true, - 999, - "string" - ], - "Key": "Value", - "null": null - } - \endcode - - \value Compact Defines a compact output as follows: - \code - {"Array":[true,999,"string"],"Key":"Value","null":null} - \endcode - */ - -/*! - Converts the QJsonDocument to a UTF-8 encoded JSON document in the provided \a format. - - \sa fromJson(), JsonFormat - */ -#if !defined(QT_JSON_READONLY) || defined(Q_CLANG_QDOC) -QByteArray QJsonDocument::toJson(JsonFormat format) const -{ - QByteArray json; - if (!d) - return json; - - if (d->header->root()->isArray()) - QJsonPrivate::Writer::arrayToJson(static_cast(d->header->root()), json, 0, (format == Compact)); - else - QJsonPrivate::Writer::objectToJson(static_cast(d->header->root()), json, 0, (format == Compact)); - - return json; -} -#endif - -/*! - Parses \a json as a UTF-8 encoded JSON document, and creates a QJsonDocument - from it. - - Returns a valid (non-null) QJsonDocument if the parsing succeeds. If it fails, - the returned document will be null, and the optional \a error variable will contain - further details about the error. - - \sa toJson(), QJsonParseError, isNull() - */ -QJsonDocument QJsonDocument::fromJson(const QByteArray &json, QJsonParseError *error) -{ - QJsonPrivate::Parser parser(json.constData(), json.length()); - return parser.parse(error); -} - -/*! - Returns \c true if the document doesn't contain any data. - */ -bool QJsonDocument::isEmpty() const -{ - if (!d) - return true; - - return false; -} - -/*! - Returns a binary representation of the document. - - The binary representation is also the native format used internally in Qt, - and is very efficient and fast to convert to and from. - - The binary format can be stored on disk and interchanged with other applications - or computers. fromBinaryData() can be used to convert it back into a - JSON document. - - \sa fromBinaryData() - */ -QByteArray QJsonDocument::toBinaryData() const -{ - if (!d || !d->rawData) - return QByteArray(); - - return QByteArray(d->rawData, d->header->root()->size + sizeof(QJsonPrivate::Header)); -} - -/*! - Returns \c true if the document contains an array. - - \sa array(), isObject() - */ -bool QJsonDocument::isArray() const -{ - if (!d) - return false; - - QJsonPrivate::Header *h = (QJsonPrivate::Header *)d->rawData; - return h->root()->isArray(); -} - -/*! - Returns \c true if the document contains an object. - - \sa object(), isArray() - */ -bool QJsonDocument::isObject() const -{ - if (!d) - return false; - - QJsonPrivate::Header *h = (QJsonPrivate::Header *)d->rawData; - return h->root()->isObject(); -} - -/*! - Returns the QJsonObject contained in the document. - - Returns an empty object if the document contains an - array. - - \sa isObject(), array(), setObject() - */ -QJsonObject QJsonDocument::object() const -{ - if (d) { - QJsonPrivate::Base *b = d->header->root(); - if (b->isObject()) - return QJsonObject(d, static_cast(b)); - } - return QJsonObject(); -} - -/*! - Returns the QJsonArray contained in the document. - - Returns an empty array if the document contains an - object. - - \sa isArray(), object(), setArray() - */ -QJsonArray QJsonDocument::array() const -{ - if (d) { - QJsonPrivate::Base *b = d->header->root(); - if (b->isArray()) - return QJsonArray(d, static_cast(b)); - } - return QJsonArray(); -} - -/*! - Sets \a object as the main object of this document. - - \sa setArray(), object() - */ -void QJsonDocument::setObject(const QJsonObject &object) -{ - if (d && !d->ref.deref()) - delete d; - - d = object.d; - - if (!d) { - d = new QJsonPrivate::Data(0, QJsonValue::Object); - } else if (d->compactionCounter || object.o != d->header->root()) { - QJsonObject o(object); - if (d->compactionCounter) - o.compact(); - else - o.detach2(); - d = o.d; - d->ref.ref(); - return; - } - d->ref.ref(); -} - -/*! - Sets \a array as the main object of this document. - - \sa setObject(), array() - */ -void QJsonDocument::setArray(const QJsonArray &array) -{ - if (d && !d->ref.deref()) - delete d; - - d = array.d; - - if (!d) { - d = new QJsonPrivate::Data(0, QJsonValue::Array); - } else if (d->compactionCounter || array.a != d->header->root()) { - QJsonArray a(array); - if (d->compactionCounter) - a.compact(); - else - a.detach2(); - d = a.d; - d->ref.ref(); - return; - } - d->ref.ref(); -} - -/*! - Returns a QJsonValue representing the value for the key \a key. - - Equivalent to calling object().value(key). - - The returned QJsonValue is QJsonValue::Undefined if the key does not exist, - or if isObject() is false. - - \since 5.10 - - \sa QJsonValue, QJsonValue::isUndefined(), QJsonObject - */ -const QJsonValue QJsonDocument::operator[](const QString &key) const -{ - if (!isObject()) - return QJsonValue(QJsonValue::Undefined); - - return object().value(key); -} - -/*! - \overload - \since 5.10 -*/ -const QJsonValue QJsonDocument::operator[](QLatin1String key) const -{ - if (!isObject()) - return QJsonValue(QJsonValue::Undefined); - - return object().value(key); -} - -/*! - Returns a QJsonValue representing the value for index \a i. - - Equivalent to calling array().at(i). - - The returned QJsonValue is QJsonValue::Undefined, if \a i is out of bounds, - or if isArray() is false. - - \since 5.10 - - \sa QJsonValue, QJsonValue::isUndefined(), QJsonArray - */ -const QJsonValue QJsonDocument::operator[](int i) const -{ - if (!isArray()) - return QJsonValue(QJsonValue::Undefined); - - return array().at(i); -} - -/*! - Returns \c true if the \a other document is equal to this document. - */ -bool QJsonDocument::operator==(const QJsonDocument &other) const -{ - if (d == other.d) - return true; - - if (!d || !other.d) - return false; - - if (d->header->root()->isArray() != other.d->header->root()->isArray()) - return false; - - if (d->header->root()->isObject()) - return QJsonObject(d, static_cast(d->header->root())) - == QJsonObject(other.d, static_cast(other.d->header->root())); - else - return QJsonArray(d, static_cast(d->header->root())) - == QJsonArray(other.d, static_cast(other.d->header->root())); -} - -/*! - \fn bool QJsonDocument::operator!=(const QJsonDocument &other) const - - returns \c true if \a other is not equal to this document - */ - -/*! - returns \c true if this document is null. - - Null documents are documents created through the default constructor. - - Documents created from UTF-8 encoded text or the binary format are - validated during parsing. If validation fails, the returned document - will also be null. - */ -bool QJsonDocument::isNull() const -{ - return (d == 0); -} - -#if !defined(QT_NO_DEBUG_STREAM) && !defined(QT_JSON_READONLY) -QDebug operator<<(QDebug dbg, const QJsonDocument &o) -{ - QDebugStateSaver saver(dbg); - if (!o.d) { - dbg << "QJsonDocument()"; - return dbg; - } - QByteArray json; - if (o.d->header->root()->isArray()) - QJsonPrivate::Writer::arrayToJson(static_cast(o.d->header->root()), json, 0, true); - else - QJsonPrivate::Writer::objectToJson(static_cast(o.d->header->root()), json, 0, true); - dbg.nospace() << "QJsonDocument(" - << json.constData() // print as utf-8 string without extra quotation marks - << ')'; - return dbg; -} -#endif - -QT_END_NAMESPACE diff --git a/src/corelib/json/qjsondocument.h b/src/corelib/json/qjsondocument.h deleted file mode 100644 index b784890c54..0000000000 --- a/src/corelib/json/qjsondocument.h +++ /dev/null @@ -1,177 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtCore module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QJSONDOCUMENT_H -#define QJSONDOCUMENT_H - -#include - -QT_BEGIN_NAMESPACE - -class QDebug; - -namespace QJsonPrivate { - class Parser; -} - -struct Q_CORE_EXPORT QJsonParseError -{ - enum ParseError { - NoError = 0, - UnterminatedObject, - MissingNameSeparator, - UnterminatedArray, - MissingValueSeparator, - IllegalValue, - TerminationByNumber, - IllegalNumber, - IllegalEscapeSequence, - IllegalUTF8String, - UnterminatedString, - MissingObject, - DeepNesting, - DocumentTooLarge, - GarbageAtEnd - }; - - QString errorString() const; - - int offset; - ParseError error; -}; - -class Q_CORE_EXPORT QJsonDocument -{ -public: -#ifdef Q_LITTLE_ENDIAN - static const uint BinaryFormatTag = ('q') | ('b' << 8) | ('j' << 16) | ('s' << 24); -#else - static const uint BinaryFormatTag = ('q' << 24) | ('b' << 16) | ('j' << 8) | ('s'); -#endif - - QJsonDocument(); - explicit QJsonDocument(const QJsonObject &object); - explicit QJsonDocument(const QJsonArray &array); - ~QJsonDocument(); - - QJsonDocument(const QJsonDocument &other); - QJsonDocument &operator =(const QJsonDocument &other); - - QJsonDocument(QJsonDocument &&other) Q_DECL_NOTHROW - : d(other.d) - { - other.d = nullptr; - } - - QJsonDocument &operator =(QJsonDocument &&other) Q_DECL_NOTHROW - { - swap(other); - return *this; - } - - void swap(QJsonDocument &other) Q_DECL_NOTHROW - { - qSwap(d, other.d); - } - - enum DataValidation { - Validate, - BypassValidation - }; - - static QJsonDocument fromRawData(const char *data, int size, DataValidation validation = Validate); - const char *rawData(int *size) const; - - static QJsonDocument fromBinaryData(const QByteArray &data, DataValidation validation = Validate); - QByteArray toBinaryData() const; - - static QJsonDocument fromVariant(const QVariant &variant); - QVariant toVariant() const; - - enum JsonFormat { - Indented, - Compact - }; - - static QJsonDocument fromJson(const QByteArray &json, QJsonParseError *error = nullptr); - -#if !defined(QT_JSON_READONLY) || defined(Q_CLANG_QDOC) - QByteArray toJson() const; //### Merge in Qt6 - QByteArray toJson(JsonFormat format) const; -#endif - - bool isEmpty() const; - bool isArray() const; - bool isObject() const; - - QJsonObject object() const; - QJsonArray array() const; - - void setObject(const QJsonObject &object); - void setArray(const QJsonArray &array); - - const QJsonValue operator[](const QString &key) const; - const QJsonValue operator[](QLatin1String key) const; - const QJsonValue operator[](int i) const; - - bool operator==(const QJsonDocument &other) const; - bool operator!=(const QJsonDocument &other) const { return !(*this == other); } - - bool isNull() const; - -private: - friend class QJsonValue; - friend class QJsonPrivate::Data; - friend class QJsonPrivate::Parser; - friend Q_CORE_EXPORT QDebug operator<<(QDebug, const QJsonDocument &); - - QJsonDocument(QJsonPrivate::Data *data); - - QJsonPrivate::Data *d; -}; - -Q_DECLARE_SHARED_NOT_MOVABLE_UNTIL_QT6(QJsonDocument) - -#if !defined(QT_NO_DEBUG_STREAM) && !defined(QT_JSON_READONLY) -Q_CORE_EXPORT QDebug operator<<(QDebug, const QJsonDocument &); -#endif - -QT_END_NAMESPACE - -#endif // QJSONDOCUMENT_H diff --git a/src/corelib/json/qjsonobject.cpp b/src/corelib/json/qjsonobject.cpp deleted file mode 100644 index 4a316c8a6f..0000000000 --- a/src/corelib/json/qjsonobject.cpp +++ /dev/null @@ -1,1312 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtCore module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include "qjson_p.h" -#include "qjsonwriter_p.h" - -QT_BEGIN_NAMESPACE - -/*! - \class QJsonObject - \inmodule QtCore - \ingroup json - \ingroup shared - \reentrant - \since 5.0 - - \brief The QJsonObject class encapsulates a JSON object. - - A JSON object is a list of key value pairs, where the keys are unique strings - and the values are represented by a QJsonValue. - - A QJsonObject can be converted to and from a QVariantMap. You can query the - number of (key, value) pairs with size(), insert(), and remove() entries from it - and iterate over its content using the standard C++ iterator pattern. - - QJsonObject is an implicitly shared class, and shares the data with the document - it has been created from as long as it is not being modified. - - You can convert the object to and from text based JSON through QJsonDocument. - - \sa {JSON Support in Qt}, {JSON Save Game Example} -*/ - -/*! - \typedef QJsonObject::Iterator - - Qt-style synonym for QJsonObject::iterator. -*/ - -/*! - \typedef QJsonObject::ConstIterator - - Qt-style synonym for QJsonObject::const_iterator. -*/ - -/*! - \typedef QJsonObject::key_type - - Typedef for QString. Provided for STL compatibility. -*/ - -/*! - \typedef QJsonObject::mapped_type - - Typedef for QJsonValue. Provided for STL compatibility. -*/ - -/*! - \typedef QJsonObject::size_type - - Typedef for int. Provided for STL compatibility. -*/ - - -/*! - Constructs an empty JSON object. - - \sa isEmpty() - */ -QJsonObject::QJsonObject() - : d(0), o(0) -{ -} - -/*! - \fn QJsonObject::QJsonObject(std::initializer_list > args) - \since 5.4 - Constructs a QJsonObject instance initialized from \a args initialization list. - For example: - \code - QJsonObject object - { - {"property1", 1}, - {"property2", 2} - }; - \endcode -*/ - -/*! - \internal - */ -QJsonObject::QJsonObject(QJsonPrivate::Data *data, QJsonPrivate::Object *object) - : d(data), o(object) -{ - Q_ASSERT(d); - Q_ASSERT(o); - d->ref.ref(); -} - -/*! - This method replaces part of the QJsonObject(std::initializer_list> args) body. - The constructor needs to be inline, but we do not want to leak implementation details - of this class. - \note this method is called for an uninitialized object - \internal - */ - -void QJsonObject::initialize() -{ - d = 0; - o = 0; -} - -/*! - Destroys the object. - */ -QJsonObject::~QJsonObject() -{ - if (d && !d->ref.deref()) - delete d; -} - -/*! - Creates a copy of \a other. - - Since QJsonObject is implicitly shared, the copy is shallow - as long as the object does not get modified. - */ -QJsonObject::QJsonObject(const QJsonObject &other) -{ - d = other.d; - o = other.o; - if (d) - d->ref.ref(); -} - -/*! - Assigns \a other to this object. - */ -QJsonObject &QJsonObject::operator =(const QJsonObject &other) -{ - if (d != other.d) { - if (d && !d->ref.deref()) - delete d; - d = other.d; - if (d) - d->ref.ref(); - } - o = other.o; - - return *this; -} - -/*! - \fn QJsonObject::QJsonObject(QJsonObject &&other) - \since 5.10 - - Move-constructs a QJsonObject from \a other. -*/ - -/*! - \fn QJsonObject &QJsonObject::operator =(QJsonObject &&other) - \since 5.10 - - Move-assigns \a other to this object. -*/ - -/*! - \fn void QJsonObject::swap(QJsonObject &other) - \since 5.10 - - Swaps the object \a other with this. This operation is very fast and never fails. -*/ - - -/*! - Converts the variant map \a map to a QJsonObject. - - The keys in \a map will be used as the keys in the JSON object, - and the QVariant values will be converted to JSON values. - - \sa fromVariantHash(), toVariantMap(), QJsonValue::fromVariant() - */ -QJsonObject QJsonObject::fromVariantMap(const QVariantMap &map) -{ - QJsonObject object; - if (map.isEmpty()) - return object; - - object.detach2(1024); - - QVector offsets; - QJsonPrivate::offset currentOffset; - currentOffset = sizeof(QJsonPrivate::Base); - - // the map is already sorted, so we can simply append one entry after the other and - // write the offset table at the end - for (QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it) { - QString key = it.key(); - QJsonValue val = QJsonValue::fromVariant(it.value()); - - bool latinOrIntValue; - int valueSize = QJsonPrivate::Value::requiredStorage(val, &latinOrIntValue); - - bool latinKey = QJsonPrivate::useCompressed(key); - int valueOffset = sizeof(QJsonPrivate::Entry) + QJsonPrivate::qStringSize(key, latinKey); - int requiredSize = valueOffset + valueSize; - - if (!object.detach2(requiredSize + sizeof(QJsonPrivate::offset))) // offset for the new index entry - return QJsonObject(); - - QJsonPrivate::Entry *e = reinterpret_cast(reinterpret_cast(object.o) + currentOffset); - e->value.type = val.t; - e->value.latinKey = latinKey; - e->value.latinOrIntValue = latinOrIntValue; - e->value.value = QJsonPrivate::Value::valueToStore(val, (char *)e - (char *)object.o + valueOffset); - QJsonPrivate::copyString((char *)(e + 1), key, latinKey); - if (valueSize) - QJsonPrivate::Value::copyData(val, (char *)e + valueOffset, latinOrIntValue); - - offsets << currentOffset; - currentOffset += requiredSize; - object.o->size = currentOffset; - } - - // write table - object.o->tableOffset = currentOffset; - if (!object.detach2(sizeof(QJsonPrivate::offset)*offsets.size())) - return QJsonObject(); - memcpy(object.o->table(), offsets.constData(), offsets.size()*sizeof(uint)); - object.o->length = offsets.size(); - object.o->size = currentOffset + sizeof(QJsonPrivate::offset)*offsets.size(); - - return object; -} - -/*! - Converts this object to a QVariantMap. - - Returns the created map. - - \sa toVariantHash() - */ -QVariantMap QJsonObject::toVariantMap() const -{ - QVariantMap map; - if (o) { - for (uint i = 0; i < o->length; ++i) { - QJsonPrivate::Entry *e = o->entryAt(i); - map.insert(e->key(), QJsonValue(d, o, e->value).toVariant()); - } - } - return map; -} - -/*! - Converts the variant hash \a hash to a QJsonObject. - \since 5.5 - - The keys in \a hash will be used as the keys in the JSON object, - and the QVariant values will be converted to JSON values. - - \sa fromVariantMap(), toVariantHash(), QJsonValue::fromVariant() - */ -QJsonObject QJsonObject::fromVariantHash(const QVariantHash &hash) -{ - // ### this is implemented the trivial way, not the most efficient way - - QJsonObject object; - for (QVariantHash::const_iterator it = hash.constBegin(); it != hash.constEnd(); ++it) - object.insert(it.key(), QJsonValue::fromVariant(it.value())); - return object; -} - -/*! - Converts this object to a QVariantHash. - \since 5.5 - - Returns the created hash. - - \sa toVariantMap() - */ -QVariantHash QJsonObject::toVariantHash() const -{ - QVariantHash hash; - if (o) { - hash.reserve(o->length); - for (uint i = 0; i < o->length; ++i) { - QJsonPrivate::Entry *e = o->entryAt(i); - hash.insert(e->key(), QJsonValue(d, o, e->value).toVariant()); - } - } - return hash; -} - -/*! - Returns a list of all keys in this object. - - The list is sorted lexographically. - */ -QStringList QJsonObject::keys() const -{ - QStringList keys; - if (o) { - keys.reserve(o->length); - for (uint i = 0; i < o->length; ++i) { - QJsonPrivate::Entry *e = o->entryAt(i); - keys.append(e->key()); - } - } - return keys; -} - -/*! - Returns the number of (key, value) pairs stored in the object. - */ -int QJsonObject::size() const -{ - if (!d) - return 0; - - return o->length; -} - -/*! - Returns \c true if the object is empty. This is the same as size() == 0. - - \sa size() - */ -bool QJsonObject::isEmpty() const -{ - if (!d) - return true; - - return !o->length; -} - -/*! - Returns a QJsonValue representing the value for the key \a key. - - The returned QJsonValue is QJsonValue::Undefined if the key does not exist. - - \sa QJsonValue, QJsonValue::isUndefined() - */ -QJsonValue QJsonObject::value(const QString &key) const -{ - if (!d) - return QJsonValue(QJsonValue::Undefined); - - bool keyExists; - int i = o->indexOf(key, &keyExists); - if (!keyExists) - return QJsonValue(QJsonValue::Undefined); - return QJsonValue(d, o, o->entryAt(i)->value); -} - -/*! - \overload - \since 5.7 -*/ -QJsonValue QJsonObject::value(QLatin1String key) const -{ - if (!d) - return QJsonValue(QJsonValue::Undefined); - - bool keyExists; - int i = o->indexOf(key, &keyExists); - if (!keyExists) - return QJsonValue(QJsonValue::Undefined); - return QJsonValue(d, o, o->entryAt(i)->value); -} - -/*! - Returns a QJsonValue representing the value for the key \a key. - - This does the same as value(). - - The returned QJsonValue is QJsonValue::Undefined if the key does not exist. - - \sa value(), QJsonValue, QJsonValue::isUndefined() - */ -QJsonValue QJsonObject::operator [](const QString &key) const -{ - return value(key); -} - -/*! - \fn QJsonValue QJsonObject::operator [](QLatin1String key) const - - \overload - \since 5.7 -*/ - -/*! - Returns a reference to the value for \a key. - - The return value is of type QJsonValueRef, a helper class for QJsonArray - and QJsonObject. When you get an object of type QJsonValueRef, you can - use it as if it were a reference to a QJsonValue. If you assign to it, - the assignment will apply to the element in the QJsonArray or QJsonObject - from which you got the reference. - - \sa value() - */ -QJsonValueRef QJsonObject::operator [](const QString &key) -{ - // ### somewhat inefficient, as we lookup the key twice if it doesn't yet exist - bool keyExists = false; - int index = o ? o->indexOf(key, &keyExists) : -1; - if (!keyExists) { - iterator i = insert(key, QJsonValue()); - index = i.i; - } - return QJsonValueRef(this, index); -} - -/*! - \overload - \since 5.7 -*/ -QJsonValueRef QJsonObject::operator [](QLatin1String key) -{ - // ### optimize me - return operator[](QString(key)); -} - -/*! - Inserts a new item with the key \a key and a value of \a value. - - If there is already an item with the key \a key, then that item's value - is replaced with \a value. - - Returns an iterator pointing to the inserted item. - - If the value is QJsonValue::Undefined, it will cause the key to get removed - from the object. The returned iterator will then point to end(). - - \sa remove(), take(), QJsonObject::iterator, end() - */ -QJsonObject::iterator QJsonObject::insert(const QString &key, const QJsonValue &value) -{ - if (value.t == QJsonValue::Undefined) { - remove(key); - return end(); - } - QJsonValue val = value; - - bool latinOrIntValue; - int valueSize = QJsonPrivate::Value::requiredStorage(val, &latinOrIntValue); - - bool latinKey = QJsonPrivate::useCompressed(key); - int valueOffset = sizeof(QJsonPrivate::Entry) + QJsonPrivate::qStringSize(key, latinKey); - int requiredSize = valueOffset + valueSize; - - if (!detach2(requiredSize + sizeof(QJsonPrivate::offset))) // offset for the new index entry - return iterator(); - - if (!o->length) - o->tableOffset = sizeof(QJsonPrivate::Object); - - bool keyExists = false; - int pos = o->indexOf(key, &keyExists); - if (keyExists) - ++d->compactionCounter; - - uint off = o->reserveSpace(requiredSize, pos, 1, keyExists); - if (!off) - return end(); - - QJsonPrivate::Entry *e = o->entryAt(pos); - e->value.type = val.t; - e->value.latinKey = latinKey; - e->value.latinOrIntValue = latinOrIntValue; - e->value.value = QJsonPrivate::Value::valueToStore(val, (char *)e - (char *)o + valueOffset); - QJsonPrivate::copyString((char *)(e + 1), key, latinKey); - if (valueSize) - QJsonPrivate::Value::copyData(val, (char *)e + valueOffset, latinOrIntValue); - - if (d->compactionCounter > 32u && d->compactionCounter >= unsigned(o->length) / 2u) - compact(); - - return iterator(this, pos); -} - -/*! - Removes \a key from the object. - - \sa insert(), take() - */ -void QJsonObject::remove(const QString &key) -{ - if (!d) - return; - - bool keyExists; - int index = o->indexOf(key, &keyExists); - if (!keyExists) - return; - - detach2(); - o->removeItems(index, 1); - ++d->compactionCounter; - if (d->compactionCounter > 32u && d->compactionCounter >= unsigned(o->length) / 2u) - compact(); -} - -/*! - Removes \a key from the object. - - Returns a QJsonValue containing the value referenced by \a key. - If \a key was not contained in the object, the returned QJsonValue - is QJsonValue::Undefined. - - \sa insert(), remove(), QJsonValue - */ -QJsonValue QJsonObject::take(const QString &key) -{ - if (!o) - return QJsonValue(QJsonValue::Undefined); - - bool keyExists; - int index = o->indexOf(key, &keyExists); - if (!keyExists) - return QJsonValue(QJsonValue::Undefined); - - QJsonValue v(d, o, o->entryAt(index)->value); - detach2(); - o->removeItems(index, 1); - ++d->compactionCounter; - if (d->compactionCounter > 32u && d->compactionCounter >= unsigned(o->length) / 2u) - compact(); - - return v; -} - -/*! - Returns \c true if the object contains key \a key. - - \sa insert(), remove(), take() - */ -bool QJsonObject::contains(const QString &key) const -{ - if (!o) - return false; - - bool keyExists; - o->indexOf(key, &keyExists); - return keyExists; -} - -/*! - \overload - \since 5.7 -*/ -bool QJsonObject::contains(QLatin1String key) const -{ - if (!o) - return false; - - bool keyExists; - o->indexOf(key, &keyExists); - return keyExists; -} - -/*! - Returns \c true if \a other is equal to this object. - */ -bool QJsonObject::operator==(const QJsonObject &other) const -{ - if (o == other.o) - return true; - - if (!o) - return !other.o->length; - if (!other.o) - return !o->length; - if (o->length != other.o->length) - return false; - - for (uint i = 0; i < o->length; ++i) { - QJsonPrivate::Entry *e = o->entryAt(i); - QJsonValue v(d, o, e->value); - if (other.value(e->key()) != v) - return false; - } - - return true; -} - -/*! - Returns \c true if \a other is not equal to this object. - */ -bool QJsonObject::operator!=(const QJsonObject &other) const -{ - return !(*this == other); -} - -/*! - Removes the (key, value) pair pointed to by the iterator \a it - from the map, and returns an iterator to the next item in the - map. - - \sa remove() - */ -QJsonObject::iterator QJsonObject::erase(QJsonObject::iterator it) -{ - Q_ASSERT(d && d->ref.load() == 1); - if (it.o != this || it.i < 0 || it.i >= (int)o->length) - return iterator(this, o->length); - - int index = it.i; - - o->removeItems(index, 1); - ++d->compactionCounter; - if (d->compactionCounter > 32u && d->compactionCounter >= unsigned(o->length) / 2u) - compact(); - - // iterator hasn't changed - return it; -} - -/*! - Returns an iterator pointing to the item with key \a key in the - map. - - If the map contains no item with key \a key, the function - returns end(). - */ -QJsonObject::iterator QJsonObject::find(const QString &key) -{ - bool keyExists = false; - int index = o ? o->indexOf(key, &keyExists) : 0; - if (!keyExists) - return end(); - detach2(); - return iterator(this, index); -} - -/*! - \overload - \since 5.7 -*/ -QJsonObject::iterator QJsonObject::find(QLatin1String key) -{ - bool keyExists = false; - int index = o ? o->indexOf(key, &keyExists) : 0; - if (!keyExists) - return end(); - detach2(); - return iterator(this, index); -} - -/*! \fn QJsonObject::const_iterator QJsonObject::find(const QString &key) const - - \overload -*/ - -/*! \fn QJsonObject::const_iterator QJsonObject::find(QLatin1String key) const - - \overload - \since 5.7 -*/ - -/*! - Returns a const iterator pointing to the item with key \a key in the - map. - - If the map contains no item with key \a key, the function - returns constEnd(). - */ -QJsonObject::const_iterator QJsonObject::constFind(const QString &key) const -{ - bool keyExists = false; - int index = o ? o->indexOf(key, &keyExists) : 0; - if (!keyExists) - return end(); - return const_iterator(this, index); -} - -/*! - \overload - \since 5.7 -*/ -QJsonObject::const_iterator QJsonObject::constFind(QLatin1String key) const -{ - bool keyExists = false; - int index = o ? o->indexOf(key, &keyExists) : 0; - if (!keyExists) - return end(); - return const_iterator(this, index); -} - -/*! \fn int QJsonObject::count() const - - \overload - - Same as size(). -*/ - -/*! \fn int QJsonObject::length() const - - \overload - - Same as size(). -*/ - -/*! \fn QJsonObject::iterator QJsonObject::begin() - - Returns an \l{STL-style iterators}{STL-style iterator} pointing to the first item in - the object. - - \sa constBegin(), end() -*/ - -/*! \fn QJsonObject::const_iterator QJsonObject::begin() const - - \overload -*/ - -/*! \fn QJsonObject::const_iterator QJsonObject::constBegin() const - - Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the first item - in the object. - - \sa begin(), constEnd() -*/ - -/*! \fn QJsonObject::iterator QJsonObject::end() - - Returns an \l{STL-style iterators}{STL-style iterator} pointing to the imaginary item - after the last item in the object. - - \sa begin(), constEnd() -*/ - -/*! \fn QJsonObject::const_iterator QJsonObject::end() const - - \overload -*/ - -/*! \fn QJsonObject::const_iterator QJsonObject::constEnd() const - - Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the imaginary - item after the last item in the object. - - \sa constBegin(), end() -*/ - -/*! - \fn bool QJsonObject::empty() const - - This function is provided for STL compatibility. It is equivalent - to isEmpty(), returning \c true if the object is empty; otherwise - returning \c false. -*/ - -/*! \class QJsonObject::iterator - \inmodule QtCore - \ingroup json - \reentrant - \since 5.0 - - \brief The QJsonObject::iterator class provides an STL-style non-const iterator for QJsonObject. - - QJsonObject::iterator allows you to iterate over a QJsonObject - and to modify the value (but not the key) stored under - a particular key. If you want to iterate over a const QJsonObject, you - should use QJsonObject::const_iterator. It is generally good practice to - use QJsonObject::const_iterator on a non-const QJsonObject as well, unless you - need to change the QJsonObject through the iterator. Const iterators are - slightly faster, and improve code readability. - - The default QJsonObject::iterator constructor creates an uninitialized - iterator. You must initialize it using a QJsonObject function like - QJsonObject::begin(), QJsonObject::end(), or QJsonObject::find() before you can - start iterating. - - Multiple iterators can be used on the same object. Existing iterators will however - become dangling once the object gets modified. - - \sa QJsonObject::const_iterator, {JSON Support in Qt}, {JSON Save Game Example} -*/ - -/*! \typedef QJsonObject::iterator::difference_type - - \internal -*/ - -/*! \typedef QJsonObject::iterator::iterator_category - - A synonym for \e {std::random_access_iterator_tag} indicating - this iterator is a random-access iterator. - - \note In Qt versions before 5.6, this was set by mistake to - \e {std::bidirectional_iterator_tag}. -*/ - -/*! \typedef QJsonObject::iterator::reference - - \internal -*/ - -/*! \typedef QJsonObject::iterator::value_type - - \internal -*/ - -/*! \typedef QJsonObject::iterator::pointer - - \internal -*/ - -/*! \fn QJsonObject::iterator::iterator() - - Constructs an uninitialized iterator. - - Functions like key(), value(), and operator++() must not be - called on an uninitialized iterator. Use operator=() to assign a - value to it before using it. - - \sa QJsonObject::begin(), QJsonObject::end() -*/ - -/*! \fn QJsonObject::iterator::iterator(QJsonObject *obj, int index) - \internal -*/ - -/*! \fn QString QJsonObject::iterator::key() const - - Returns the current item's key. - - There is no direct way of changing an item's key through an - iterator, although it can be done by calling QJsonObject::erase() - followed by QJsonObject::insert(). - - \sa value() -*/ - -/*! \fn QJsonValueRef QJsonObject::iterator::value() const - - Returns a modifiable reference to the current item's value. - - You can change the value of an item by using value() on - the left side of an assignment. - - The return value is of type QJsonValueRef, a helper class for QJsonArray - and QJsonObject. When you get an object of type QJsonValueRef, you can - use it as if it were a reference to a QJsonValue. If you assign to it, - the assignment will apply to the element in the QJsonArray or QJsonObject - from which you got the reference. - - \sa key(), operator*() -*/ - -/*! \fn QJsonValueRef QJsonObject::iterator::operator*() const - - Returns a modifiable reference to the current item's value. - - Same as value(). - - The return value is of type QJsonValueRef, a helper class for QJsonArray - and QJsonObject. When you get an object of type QJsonValueRef, you can - use it as if it were a reference to a QJsonValue. If you assign to it, - the assignment will apply to the element in the QJsonArray or QJsonObject - from which you got the reference. - - \sa key() -*/ - -/*! \fn QJsonValueRef *QJsonObject::iterator::operator->() const - - Returns a pointer to a modifiable reference to the current item. -*/ - -/*! - \fn bool QJsonObject::iterator::operator==(const iterator &other) const - \fn bool QJsonObject::iterator::operator==(const const_iterator &other) const - - Returns \c true if \a other points to the same item as this - iterator; otherwise returns \c false. - - \sa operator!=() -*/ - -/*! - \fn bool QJsonObject::iterator::operator!=(const iterator &other) const - \fn bool QJsonObject::iterator::operator!=(const const_iterator &other) const - - Returns \c true if \a other points to a different item than this - iterator; otherwise returns \c false. - - \sa operator==() -*/ - -/*! \fn QJsonObject::iterator QJsonObject::iterator::operator++() - - The prefix ++ operator, \c{++i}, advances the iterator to the - next item in the object and returns an iterator to the new current - item. - - Calling this function on QJsonObject::end() leads to undefined results. - - \sa operator--() -*/ - -/*! \fn QJsonObject::iterator QJsonObject::iterator::operator++(int) - - \overload - - The postfix ++ operator, \c{i++}, advances the iterator to the - next item in the object and returns an iterator to the previously - current item. -*/ - -/*! \fn QJsonObject::iterator QJsonObject::iterator::operator--() - - The prefix -- operator, \c{--i}, makes the preceding item - current and returns an iterator pointing to the new current item. - - Calling this function on QJsonObject::begin() leads to undefined - results. - - \sa operator++() -*/ - -/*! \fn QJsonObject::iterator QJsonObject::iterator::operator--(int) - - \overload - - The postfix -- operator, \c{i--}, makes the preceding item - current and returns an iterator pointing to the previously - current item. -*/ - -/*! \fn QJsonObject::iterator QJsonObject::iterator::operator+(int j) const - - Returns an iterator to the item at \a j positions forward from - this iterator. If \a j is negative, the iterator goes backward. - - \sa operator-() - -*/ - -/*! \fn QJsonObject::iterator QJsonObject::iterator::operator-(int j) const - - Returns an iterator to the item at \a j positions backward from - this iterator. If \a j is negative, the iterator goes forward. - - \sa operator+() -*/ - -/*! \fn QJsonObject::iterator &QJsonObject::iterator::operator+=(int j) - - Advances the iterator by \a j items. If \a j is negative, the - iterator goes backward. - - \sa operator-=(), operator+() -*/ - -/*! \fn QJsonObject::iterator &QJsonObject::iterator::operator-=(int j) - - Makes the iterator go back by \a j items. If \a j is negative, - the iterator goes forward. - - \sa operator+=(), operator-() -*/ - -/*! - \class QJsonObject::const_iterator - \inmodule QtCore - \ingroup json - \since 5.0 - \brief The QJsonObject::const_iterator class provides an STL-style const iterator for QJsonObject. - - QJsonObject::const_iterator allows you to iterate over a QJsonObject. - If you want to modify the QJsonObject as you iterate - over it, you must use QJsonObject::iterator instead. It is generally - good practice to use QJsonObject::const_iterator on a non-const QJsonObject as - well, unless you need to change the QJsonObject through the iterator. - Const iterators are slightly faster and improve code - readability. - - The default QJsonObject::const_iterator constructor creates an - uninitialized iterator. You must initialize it using a QJsonObject - function like QJsonObject::constBegin(), QJsonObject::constEnd(), or - QJsonObject::find() before you can start iterating. - - Multiple iterators can be used on the same object. Existing iterators - will however become dangling if the object gets modified. - - \sa QJsonObject::iterator, {JSON Support in Qt}, {JSON Save Game Example} -*/ - -/*! \typedef QJsonObject::const_iterator::difference_type - - \internal -*/ - -/*! \typedef QJsonObject::const_iterator::iterator_category - - A synonym for \e {std::random_access_iterator_tag} indicating - this iterator is a random-access iterator. - - \note In Qt versions before 5.6, this was set by mistake to - \e {std::bidirectional_iterator_tag}. -*/ - -/*! \typedef QJsonObject::const_iterator::reference - - \internal -*/ - -/*! \typedef QJsonObject::const_iterator::value_type - - \internal -*/ - -/*! \typedef QJsonObject::const_iterator::pointer - - \internal -*/ - -/*! \fn QJsonObject::const_iterator::const_iterator() - - Constructs an uninitialized iterator. - - Functions like key(), value(), and operator++() must not be - called on an uninitialized iterator. Use operator=() to assign a - value to it before using it. - - \sa QJsonObject::constBegin(), QJsonObject::constEnd() -*/ - -/*! \fn QJsonObject::const_iterator::const_iterator(const QJsonObject *obj, int index) - \internal -*/ - -/*! \fn QJsonObject::const_iterator::const_iterator(const iterator &other) - - Constructs a copy of \a other. -*/ - -/*! \fn QString QJsonObject::const_iterator::key() const - - Returns the current item's key. - - \sa value() -*/ - -/*! \fn QJsonValue QJsonObject::const_iterator::value() const - - Returns the current item's value. - - \sa key(), operator*() -*/ - -/*! \fn QJsonValue QJsonObject::const_iterator::operator*() const - - Returns the current item's value. - - Same as value(). - - \sa key() -*/ - -/*! \fn QJsonValue *QJsonObject::const_iterator::operator->() const - - Returns a pointer to the current item. -*/ - -/*! \fn bool QJsonObject::const_iterator::operator==(const const_iterator &other) const - \fn bool QJsonObject::const_iterator::operator==(const iterator &other) const - - Returns \c true if \a other points to the same item as this - iterator; otherwise returns \c false. - - \sa operator!=() -*/ - -/*! \fn bool QJsonObject::const_iterator::operator!=(const const_iterator &other) const - \fn bool QJsonObject::const_iterator::operator!=(const iterator &other) const - - Returns \c true if \a other points to a different item than this - iterator; otherwise returns \c false. - - \sa operator==() -*/ - -/*! \fn QJsonObject::const_iterator QJsonObject::const_iterator::operator++() - - The prefix ++ operator, \c{++i}, advances the iterator to the - next item in the object and returns an iterator to the new current - item. - - Calling this function on QJsonObject::end() leads to undefined results. - - \sa operator--() -*/ - -/*! \fn QJsonObject::const_iterator QJsonObject::const_iterator::operator++(int) - - \overload - - The postfix ++ operator, \c{i++}, advances the iterator to the - next item in the object and returns an iterator to the previously - current item. -*/ - -/*! \fn QJsonObject::const_iterator &QJsonObject::const_iterator::operator--() - - The prefix -- operator, \c{--i}, makes the preceding item - current and returns an iterator pointing to the new current item. - - Calling this function on QJsonObject::begin() leads to undefined - results. - - \sa operator++() -*/ - -/*! \fn QJsonObject::const_iterator QJsonObject::const_iterator::operator--(int) - - \overload - - The postfix -- operator, \c{i--}, makes the preceding item - current and returns an iterator pointing to the previously - current item. -*/ - -/*! \fn QJsonObject::const_iterator QJsonObject::const_iterator::operator+(int j) const - - Returns an iterator to the item at \a j positions forward from - this iterator. If \a j is negative, the iterator goes backward. - - This operation can be slow for large \a j values. - - \sa operator-() -*/ - -/*! \fn QJsonObject::const_iterator QJsonObject::const_iterator::operator-(int j) const - - Returns an iterator to the item at \a j positions backward from - this iterator. If \a j is negative, the iterator goes forward. - - This operation can be slow for large \a j values. - - \sa operator+() -*/ - -/*! \fn QJsonObject::const_iterator &QJsonObject::const_iterator::operator+=(int j) - - Advances the iterator by \a j items. If \a j is negative, the - iterator goes backward. - - This operation can be slow for large \a j values. - - \sa operator-=(), operator+() -*/ - -/*! \fn QJsonObject::const_iterator &QJsonObject::const_iterator::operator-=(int j) - - Makes the iterator go back by \a j items. If \a j is negative, - the iterator goes forward. - - This operation can be slow for large \a j values. - - \sa operator+=(), operator-() -*/ - - -/*! - \internal - */ -void QJsonObject::detach(uint reserve) -{ - Q_UNUSED(reserve) - Q_ASSERT(!reserve); - detach2(reserve); -} - -bool QJsonObject::detach2(uint reserve) -{ - if (!d) { - if (reserve >= QJsonPrivate::Value::MaxSize) { - qWarning("QJson: Document too large to store in data structure"); - return false; - } - d = new QJsonPrivate::Data(reserve, QJsonValue::Object); - o = static_cast(d->header->root()); - d->ref.ref(); - return true; - } - if (reserve == 0 && d->ref.load() == 1) - return true; - - QJsonPrivate::Data *x = d->clone(o, reserve); - if (!x) - return false; - x->ref.ref(); - if (!d->ref.deref()) - delete d; - d = x; - o = static_cast(d->header->root()); - return true; -} - -/*! - \internal - */ -void QJsonObject::compact() -{ - if (!d || !d->compactionCounter) - return; - - detach2(); - d->compact(); - o = static_cast(d->header->root()); -} - -/*! - \internal - */ -QString QJsonObject::keyAt(int i) const -{ - Q_ASSERT(o && i >= 0 && i < (int)o->length); - - QJsonPrivate::Entry *e = o->entryAt(i); - return e->key(); -} - -/*! - \internal - */ -QJsonValue QJsonObject::valueAt(int i) const -{ - if (!o || i < 0 || i >= (int)o->length) - return QJsonValue(QJsonValue::Undefined); - - QJsonPrivate::Entry *e = o->entryAt(i); - return QJsonValue(d, o, e->value); -} - -/*! - \internal - */ -void QJsonObject::setValueAt(int i, const QJsonValue &val) -{ - Q_ASSERT(o && i >= 0 && i < (int)o->length); - - QJsonPrivate::Entry *e = o->entryAt(i); - insert(e->key(), val); -} - -#if !defined(QT_NO_DEBUG_STREAM) && !defined(QT_JSON_READONLY) -QDebug operator<<(QDebug dbg, const QJsonObject &o) -{ - QDebugStateSaver saver(dbg); - if (!o.o) { - dbg << "QJsonObject()"; - return dbg; - } - QByteArray json; - QJsonPrivate::Writer::objectToJson(o.o, json, 0, true); - dbg.nospace() << "QJsonObject(" - << json.constData() // print as utf-8 string without extra quotation marks - << ")"; - return dbg; -} -#endif - -QT_END_NAMESPACE diff --git a/src/corelib/json/qjsonobject.h b/src/corelib/json/qjsonobject.h deleted file mode 100644 index 610bce694c..0000000000 --- a/src/corelib/json/qjsonobject.h +++ /dev/null @@ -1,271 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtCore module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QJSONOBJECT_H -#define QJSONOBJECT_H - -#include -#include -#ifdef Q_COMPILER_INITIALIZER_LISTS -#include -#include -#endif - -QT_BEGIN_NAMESPACE - -class QDebug; -template class QMap; -typedef QMap QVariantMap; -template class QHash; -typedef QHash QVariantHash; - -class Q_CORE_EXPORT QJsonObject -{ -public: - QJsonObject(); - -#if defined(Q_COMPILER_INITIALIZER_LISTS) || defined(Q_QDOC) - QJsonObject(std::initializer_list > args) - { - initialize(); - for (std::initializer_list >::const_iterator i = args.begin(); i != args.end(); ++i) - insert(i->first, i->second); - } -#endif - - ~QJsonObject(); - - QJsonObject(const QJsonObject &other); - QJsonObject &operator =(const QJsonObject &other); - - QJsonObject(QJsonObject &&other) Q_DECL_NOTHROW - : d(other.d), o(other.o) - { - other.d = nullptr; - other.o = nullptr; - } - - QJsonObject &operator =(QJsonObject &&other) Q_DECL_NOTHROW - { - swap(other); - return *this; - } - - void swap(QJsonObject &other) Q_DECL_NOTHROW - { - qSwap(d, other.d); - qSwap(o, other.o); - } - - static QJsonObject fromVariantMap(const QVariantMap &map); - QVariantMap toVariantMap() const; - static QJsonObject fromVariantHash(const QVariantHash &map); - QVariantHash toVariantHash() const; - - QStringList keys() const; - int size() const; - inline int count() const { return size(); } - inline int length() const { return size(); } - bool isEmpty() const; - - QJsonValue value(const QString &key) const; - QJsonValue value(QLatin1String key) const; - QJsonValue operator[] (const QString &key) const; - QJsonValue operator[] (QLatin1String key) const { return value(key); } - QJsonValueRef operator[] (const QString &key); - QJsonValueRef operator[] (QLatin1String key); - - void remove(const QString &key); - QJsonValue take(const QString &key); - bool contains(const QString &key) const; - bool contains(QLatin1String key) const; - - bool operator==(const QJsonObject &other) const; - bool operator!=(const QJsonObject &other) const; - - class const_iterator; - - class iterator - { - friend class const_iterator; - friend class QJsonObject; - QJsonObject *o; - int i; - - public: - typedef std::random_access_iterator_tag iterator_category; - typedef int difference_type; - typedef QJsonValue value_type; - typedef QJsonValueRef reference; - typedef QJsonValuePtr pointer; - - Q_DECL_CONSTEXPR inline iterator() : o(nullptr), i(0) {} - Q_DECL_CONSTEXPR inline iterator(QJsonObject *obj, int index) : o(obj), i(index) {} - - inline QString key() const { return o->keyAt(i); } - inline QJsonValueRef value() const { return QJsonValueRef(o, i); } - inline QJsonValueRef operator*() const { return QJsonValueRef(o, i); } -#ifdef Q_QDOC - inline QJsonValueRef* operator->() const; -#else - inline QJsonValueRefPtr operator->() const { return QJsonValueRefPtr(o, i); } -#endif - inline bool operator==(const iterator &other) const { return i == other.i; } - inline bool operator!=(const iterator &other) const { return i != other.i; } - - inline iterator &operator++() { ++i; return *this; } - inline iterator operator++(int) { iterator r = *this; ++i; return r; } - inline iterator &operator--() { --i; return *this; } - inline iterator operator--(int) { iterator r = *this; --i; return r; } - inline iterator operator+(int j) const - { iterator r = *this; r.i += j; return r; } - inline iterator operator-(int j) const { return operator+(-j); } - inline iterator &operator+=(int j) { i += j; return *this; } - inline iterator &operator-=(int j) { i -= j; return *this; } - - public: - inline bool operator==(const const_iterator &other) const { return i == other.i; } - inline bool operator!=(const const_iterator &other) const { return i != other.i; } - }; - friend class iterator; - - class const_iterator - { - friend class iterator; - const QJsonObject *o; - int i; - - public: - typedef std::random_access_iterator_tag iterator_category; - typedef int difference_type; - typedef QJsonValue value_type; - typedef QJsonValue reference; - typedef QJsonValuePtr pointer; - - Q_DECL_CONSTEXPR inline const_iterator() : o(nullptr), i(0) {} - Q_DECL_CONSTEXPR inline const_iterator(const QJsonObject *obj, int index) - : o(obj), i(index) {} - inline const_iterator(const iterator &other) - : o(other.o), i(other.i) {} - - inline QString key() const { return o->keyAt(i); } - inline QJsonValue value() const { return o->valueAt(i); } - inline QJsonValue operator*() const { return o->valueAt(i); } -#ifdef Q_QDOC - inline QJsonValue* operator->() const; -#else - inline QJsonValuePtr operator->() const { return QJsonValuePtr(o->valueAt(i)); } -#endif - inline bool operator==(const const_iterator &other) const { return i == other.i; } - inline bool operator!=(const const_iterator &other) const { return i != other.i; } - - inline const_iterator &operator++() { ++i; return *this; } - inline const_iterator operator++(int) { const_iterator r = *this; ++i; return r; } - inline const_iterator &operator--() { --i; return *this; } - inline const_iterator operator--(int) { const_iterator r = *this; --i; return r; } - inline const_iterator operator+(int j) const - { const_iterator r = *this; r.i += j; return r; } - inline const_iterator operator-(int j) const { return operator+(-j); } - inline const_iterator &operator+=(int j) { i += j; return *this; } - inline const_iterator &operator-=(int j) { i -= j; return *this; } - - inline bool operator==(const iterator &other) const { return i == other.i; } - inline bool operator!=(const iterator &other) const { return i != other.i; } - }; - friend class const_iterator; - - // STL style - inline iterator begin() { detach2(); return iterator(this, 0); } - inline const_iterator begin() const { return const_iterator(this, 0); } - inline const_iterator constBegin() const { return const_iterator(this, 0); } - inline iterator end() { detach2(); return iterator(this, size()); } - inline const_iterator end() const { return const_iterator(this, size()); } - inline const_iterator constEnd() const { return const_iterator(this, size()); } - iterator erase(iterator it); - - // more Qt - typedef iterator Iterator; - typedef const_iterator ConstIterator; - iterator find(const QString &key); - iterator find(QLatin1String key); - const_iterator find(const QString &key) const { return constFind(key); } - const_iterator find(QLatin1String key) const { return constFind(key); } - const_iterator constFind(const QString &key) const; - const_iterator constFind(QLatin1String key) const; - iterator insert(const QString &key, const QJsonValue &value); - - // STL compatibility - typedef QJsonValue mapped_type; - typedef QString key_type; - typedef int size_type; - - inline bool empty() const { return isEmpty(); } - -private: - friend class QJsonPrivate::Data; - friend class QJsonValue; - friend class QJsonDocument; - friend class QJsonValueRef; - - friend Q_CORE_EXPORT QDebug operator<<(QDebug, const QJsonObject &); - - QJsonObject(QJsonPrivate::Data *data, QJsonPrivate::Object *object); - void initialize(); - // ### Qt 6: remove me and merge with detach2 - void detach(uint reserve = 0); - bool detach2(uint reserve = 0); - void compact(); - - QString keyAt(int i) const; - QJsonValue valueAt(int i) const; - void setValueAt(int i, const QJsonValue &val); - - QJsonPrivate::Data *d; - QJsonPrivate::Object *o; -}; - -Q_DECLARE_SHARED_NOT_MOVABLE_UNTIL_QT6(QJsonObject) - -#if !defined(QT_NO_DEBUG_STREAM) && !defined(QT_JSON_READONLY) -Q_CORE_EXPORT QDebug operator<<(QDebug, const QJsonObject &); -#endif - -QT_END_NAMESPACE - -#endif // QJSONOBJECT_H diff --git a/src/corelib/json/qjsonparser.cpp b/src/corelib/json/qjsonparser.cpp deleted file mode 100644 index 39738b90a8..0000000000 --- a/src/corelib/json/qjsonparser.cpp +++ /dev/null @@ -1,1027 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Copyright (C) 2016 Intel Corporation. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtCore module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QT_BOOTSTRAPPED -#include -#endif -#include -#include "qjsonparser_p.h" -#include "qjson_p.h" -#include "private/qutfcodec_p.h" - -//#define PARSER_DEBUG -#ifdef PARSER_DEBUG -static int indent = 0; -#define BEGIN qDebug() << QByteArray(4*indent++, ' ').constData() << "pos=" << current -#define END --indent -#define DEBUG qDebug() << QByteArray(4*indent, ' ').constData() -#else -#define BEGIN if (1) ; else qDebug() -#define END do {} while (0) -#define DEBUG if (1) ; else qDebug() -#endif - -static const int nestingLimit = 1024; - -QT_BEGIN_NAMESPACE - -// error strings for the JSON parser -#define JSONERR_OK QT_TRANSLATE_NOOP("QJsonParseError", "no error occurred") -#define JSONERR_UNTERM_OBJ QT_TRANSLATE_NOOP("QJsonParseError", "unterminated object") -#define JSONERR_MISS_NSEP QT_TRANSLATE_NOOP("QJsonParseError", "missing name separator") -#define JSONERR_UNTERM_AR QT_TRANSLATE_NOOP("QJsonParseError", "unterminated array") -#define JSONERR_MISS_VSEP QT_TRANSLATE_NOOP("QJsonParseError", "missing value separator") -#define JSONERR_ILLEGAL_VAL QT_TRANSLATE_NOOP("QJsonParseError", "illegal value") -#define JSONERR_END_OF_NUM QT_TRANSLATE_NOOP("QJsonParseError", "invalid termination by number") -#define JSONERR_ILLEGAL_NUM QT_TRANSLATE_NOOP("QJsonParseError", "illegal number") -#define JSONERR_STR_ESC_SEQ QT_TRANSLATE_NOOP("QJsonParseError", "invalid escape sequence") -#define JSONERR_STR_UTF8 QT_TRANSLATE_NOOP("QJsonParseError", "invalid UTF8 string") -#define JSONERR_UTERM_STR QT_TRANSLATE_NOOP("QJsonParseError", "unterminated string") -#define JSONERR_MISS_OBJ QT_TRANSLATE_NOOP("QJsonParseError", "object is missing after a comma") -#define JSONERR_DEEP_NEST QT_TRANSLATE_NOOP("QJsonParseError", "too deeply nested document") -#define JSONERR_DOC_LARGE QT_TRANSLATE_NOOP("QJsonParseError", "too large document") -#define JSONERR_GARBAGEEND QT_TRANSLATE_NOOP("QJsonParseError", "garbage at the end of the document") - -/*! - \class QJsonParseError - \inmodule QtCore - \ingroup json - \ingroup shared - \reentrant - \since 5.0 - - \brief The QJsonParseError class is used to report errors during JSON parsing. - - \sa {JSON Support in Qt}, {JSON Save Game Example} -*/ - -/*! - \enum QJsonParseError::ParseError - - This enum describes the type of error that occurred during the parsing of a JSON document. - - \value NoError No error occurred - \value UnterminatedObject An object is not correctly terminated with a closing curly bracket - \value MissingNameSeparator A comma separating different items is missing - \value UnterminatedArray The array is not correctly terminated with a closing square bracket - \value MissingValueSeparator A colon separating keys from values inside objects is missing - \value IllegalValue The value is illegal - \value TerminationByNumber The input stream ended while parsing a number - \value IllegalNumber The number is not well formed - \value IllegalEscapeSequence An illegal escape sequence occurred in the input - \value IllegalUTF8String An illegal UTF8 sequence occurred in the input - \value UnterminatedString A string wasn't terminated with a quote - \value MissingObject An object was expected but couldn't be found - \value DeepNesting The JSON document is too deeply nested for the parser to parse it - \value DocumentTooLarge The JSON document is too large for the parser to parse it - \value GarbageAtEnd The parsed document contains additional garbage characters at the end - -*/ - -/*! - \variable QJsonParseError::error - - Contains the type of the parse error. Is equal to QJsonParseError::NoError if the document - was parsed correctly. - - \sa ParseError, errorString() -*/ - - -/*! - \variable QJsonParseError::offset - - Contains the offset in the input string where the parse error occurred. - - \sa error, errorString() -*/ - -/*! - Returns the human-readable message appropriate to the reported JSON parsing error. - - \sa error - */ -QString QJsonParseError::errorString() const -{ - const char *sz = ""; - switch (error) { - case NoError: - sz = JSONERR_OK; - break; - case UnterminatedObject: - sz = JSONERR_UNTERM_OBJ; - break; - case MissingNameSeparator: - sz = JSONERR_MISS_NSEP; - break; - case UnterminatedArray: - sz = JSONERR_UNTERM_AR; - break; - case MissingValueSeparator: - sz = JSONERR_MISS_VSEP; - break; - case IllegalValue: - sz = JSONERR_ILLEGAL_VAL; - break; - case TerminationByNumber: - sz = JSONERR_END_OF_NUM; - break; - case IllegalNumber: - sz = JSONERR_ILLEGAL_NUM; - break; - case IllegalEscapeSequence: - sz = JSONERR_STR_ESC_SEQ; - break; - case IllegalUTF8String: - sz = JSONERR_STR_UTF8; - break; - case UnterminatedString: - sz = JSONERR_UTERM_STR; - break; - case MissingObject: - sz = JSONERR_MISS_OBJ; - break; - case DeepNesting: - sz = JSONERR_DEEP_NEST; - break; - case DocumentTooLarge: - sz = JSONERR_DOC_LARGE; - break; - case GarbageAtEnd: - sz = JSONERR_GARBAGEEND; - break; - } -#ifndef QT_BOOTSTRAPPED - return QCoreApplication::translate("QJsonParseError", sz); -#else - return QLatin1String(sz); -#endif -} - -using namespace QJsonPrivate; - -Parser::Parser(const char *json, int length) - : head(json), json(json), data(0), dataLength(0), current(0), nestingLevel(0), lastError(QJsonParseError::NoError) -{ - end = json + length; -} - - - -/* - -begin-array = ws %x5B ws ; [ left square bracket - -begin-object = ws %x7B ws ; { left curly bracket - -end-array = ws %x5D ws ; ] right square bracket - -end-object = ws %x7D ws ; } right curly bracket - -name-separator = ws %x3A ws ; : colon - -value-separator = ws %x2C ws ; , comma - -Insignificant whitespace is allowed before or after any of the six -structural characters. - -ws = *( - %x20 / ; Space - %x09 / ; Horizontal tab - %x0A / ; Line feed or New line - %x0D ; Carriage return - ) - -*/ - -enum { - Space = 0x20, - Tab = 0x09, - LineFeed = 0x0a, - Return = 0x0d, - BeginArray = 0x5b, - BeginObject = 0x7b, - EndArray = 0x5d, - EndObject = 0x7d, - NameSeparator = 0x3a, - ValueSeparator = 0x2c, - Quote = 0x22 -}; - -void Parser::eatBOM() -{ - // eat UTF-8 byte order mark - uchar utf8bom[3] = { 0xef, 0xbb, 0xbf }; - if (end - json > 3 && - (uchar)json[0] == utf8bom[0] && - (uchar)json[1] == utf8bom[1] && - (uchar)json[2] == utf8bom[2]) - json += 3; -} - -bool Parser::eatSpace() -{ - while (json < end) { - if (*json > Space) - break; - if (*json != Space && - *json != Tab && - *json != LineFeed && - *json != Return) - break; - ++json; - } - return (json < end); -} - -char Parser::nextToken() -{ - if (!eatSpace()) - return 0; - char token = *json++; - switch (token) { - case BeginArray: - case BeginObject: - case NameSeparator: - case ValueSeparator: - case EndArray: - case EndObject: - case Quote: - break; - default: - token = 0; - break; - } - return token; -} - -/* - JSON-text = object / array -*/ -QJsonDocument Parser::parse(QJsonParseError *error) -{ -#ifdef PARSER_DEBUG - indent = 0; - qDebug(">>>>> parser begin"); -#endif - // allocate some space - dataLength = qMax(end - json, (ptrdiff_t) 256); - data = (char *)malloc(dataLength); - - // fill in Header data - QJsonPrivate::Header *h = (QJsonPrivate::Header *)data; - h->tag = QJsonDocument::BinaryFormatTag; - h->version = 1u; - - current = sizeof(QJsonPrivate::Header); - - eatBOM(); - char token = nextToken(); - - DEBUG << hex << (uint)token; - if (token == BeginArray) { - if (!parseArray()) - goto error; - } else if (token == BeginObject) { - if (!parseObject()) - goto error; - } else { - lastError = QJsonParseError::IllegalValue; - goto error; - } - - eatSpace(); - if (json < end) { - lastError = QJsonParseError::GarbageAtEnd; - goto error; - } - - END; - { - if (error) { - error->offset = 0; - error->error = QJsonParseError::NoError; - } - QJsonPrivate::Data *d = new QJsonPrivate::Data(data, current); - return QJsonDocument(d); - } - -error: -#ifdef PARSER_DEBUG - qDebug(">>>>> parser error"); -#endif - if (error) { - error->offset = json - head; - error->error = lastError; - } - free(data); - return QJsonDocument(); -} - - -void Parser::ParsedObject::insert(uint offset) { - const QJsonPrivate::Entry *newEntry = reinterpret_cast(parser->data + objectPosition + offset); - int min = 0; - int n = offsets.size(); - while (n > 0) { - int half = n >> 1; - int middle = min + half; - if (*entryAt(middle) >= *newEntry) { - n = half; - } else { - min = middle + 1; - n -= half + 1; - } - } - if (min < offsets.size() && *entryAt(min) == *newEntry) { - offsets[min] = offset; - } else { - offsets.insert(min, offset); - } -} - -/* - object = begin-object [ member *( value-separator member ) ] - end-object -*/ - -bool Parser::parseObject() -{ - if (++nestingLevel > nestingLimit) { - lastError = QJsonParseError::DeepNesting; - return false; - } - - int objectOffset = reserveSpace(sizeof(QJsonPrivate::Object)); - if (objectOffset < 0) - return false; - BEGIN << "parseObject pos=" << objectOffset << current << json; - - ParsedObject parsedObject(this, objectOffset); - - char token = nextToken(); - while (token == Quote) { - int off = current - objectOffset; - if (!parseMember(objectOffset)) - return false; - parsedObject.insert(off); - token = nextToken(); - if (token != ValueSeparator) - break; - token = nextToken(); - if (token == EndObject) { - lastError = QJsonParseError::MissingObject; - return false; - } - } - - DEBUG << "end token=" << token; - if (token != EndObject) { - lastError = QJsonParseError::UnterminatedObject; - return false; - } - - DEBUG << "numEntries" << parsedObject.offsets.size(); - int table = objectOffset; - // finalize the object - if (parsedObject.offsets.size()) { - int tableSize = parsedObject.offsets.size()*sizeof(uint); - table = reserveSpace(tableSize); - if (table < 0) - return false; - -#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN - memcpy(data + table, parsedObject.offsets.constData(), tableSize); -#else - offset *o = (offset *)(data + table); - for (int i = 0; i < parsedObject.offsets.size(); ++i) - o[i] = parsedObject.offsets[i]; - -#endif - } - - QJsonPrivate::Object *o = (QJsonPrivate::Object *)(data + objectOffset); - o->tableOffset = table - objectOffset; - o->size = current - objectOffset; - o->is_object = true; - o->length = parsedObject.offsets.size(); - - DEBUG << "current=" << current; - END; - - --nestingLevel; - return true; -} - -/* - member = string name-separator value -*/ -bool Parser::parseMember(int baseOffset) -{ - int entryOffset = reserveSpace(sizeof(QJsonPrivate::Entry)); - if (entryOffset < 0) - return false; - BEGIN << "parseMember pos=" << entryOffset; - - bool latin1; - if (!parseString(&latin1)) - return false; - char token = nextToken(); - if (token != NameSeparator) { - lastError = QJsonParseError::MissingNameSeparator; - return false; - } - if (!eatSpace()) { - lastError = QJsonParseError::UnterminatedObject; - return false; - } - QJsonPrivate::Value val; - if (!parseValue(&val, baseOffset)) - return false; - - // finalize the entry - QJsonPrivate::Entry *e = (QJsonPrivate::Entry *)(data + entryOffset); - e->value = val; - e->value.latinKey = latin1; - - END; - return true; -} - -namespace { - struct ValueArray { - static const int prealloc = 128; - ValueArray() : data(stackValues), alloc(prealloc), size(0) {} - ~ValueArray() { if (data != stackValues) free(data); } - - inline bool grow() { - alloc *= 2; - if (data == stackValues) { - QJsonPrivate::Value *newValues = static_cast(malloc(alloc*sizeof(QJsonPrivate::Value))); - if (!newValues) - return false; - memcpy(newValues, data, size*sizeof(QJsonPrivate::Value)); - data = newValues; - } else { - void *newValues = realloc(data, alloc * sizeof(QJsonPrivate::Value)); - if (!newValues) - return false; - data = static_cast(newValues); - } - return true; - } - bool append(const QJsonPrivate::Value &v) { - if (alloc == size && !grow()) - return false; - data[size] = v; - ++size; - return true; - } - - QJsonPrivate::Value stackValues[prealloc]; - QJsonPrivate::Value *data; - int alloc; - int size; - }; -} - -/* - array = begin-array [ value *( value-separator value ) ] end-array -*/ -bool Parser::parseArray() -{ - BEGIN << "parseArray"; - - if (++nestingLevel > nestingLimit) { - lastError = QJsonParseError::DeepNesting; - return false; - } - - int arrayOffset = reserveSpace(sizeof(QJsonPrivate::Array)); - if (arrayOffset < 0) - return false; - - ValueArray values; - - if (!eatSpace()) { - lastError = QJsonParseError::UnterminatedArray; - return false; - } - if (*json == EndArray) { - nextToken(); - } else { - while (1) { - if (!eatSpace()) { - lastError = QJsonParseError::UnterminatedArray; - return false; - } - QJsonPrivate::Value val; - if (!parseValue(&val, arrayOffset)) - return false; - if (!values.append(val)) { - lastError = QJsonParseError::DocumentTooLarge; - return false; - } - char token = nextToken(); - if (token == EndArray) - break; - else if (token != ValueSeparator) { - if (!eatSpace()) - lastError = QJsonParseError::UnterminatedArray; - else - lastError = QJsonParseError::MissingValueSeparator; - return false; - } - } - } - - DEBUG << "size =" << values.size; - int table = arrayOffset; - // finalize the object - if (values.size) { - int tableSize = values.size*sizeof(QJsonPrivate::Value); - table = reserveSpace(tableSize); - if (table < 0) - return false; - memcpy(data + table, values.data, tableSize); - } - - QJsonPrivate::Array *a = (QJsonPrivate::Array *)(data + arrayOffset); - a->tableOffset = table - arrayOffset; - a->size = current - arrayOffset; - a->is_object = false; - a->length = values.size; - - DEBUG << "current=" << current; - END; - - --nestingLevel; - return true; -} - -/* -value = false / null / true / object / array / number / string - -*/ - -bool Parser::parseValue(QJsonPrivate::Value *val, int baseOffset) -{ - BEGIN << "parse Value" << json; - val->_dummy = 0; - - switch (*json++) { - case 'n': - if (end - json < 4) { - lastError = QJsonParseError::IllegalValue; - return false; - } - if (*json++ == 'u' && - *json++ == 'l' && - *json++ == 'l') { - val->type = QJsonValue::Null; - DEBUG << "value: null"; - END; - return true; - } - lastError = QJsonParseError::IllegalValue; - return false; - case 't': - if (end - json < 4) { - lastError = QJsonParseError::IllegalValue; - return false; - } - if (*json++ == 'r' && - *json++ == 'u' && - *json++ == 'e') { - val->type = QJsonValue::Bool; - val->value = true; - DEBUG << "value: true"; - END; - return true; - } - lastError = QJsonParseError::IllegalValue; - return false; - case 'f': - if (end - json < 5) { - lastError = QJsonParseError::IllegalValue; - return false; - } - if (*json++ == 'a' && - *json++ == 'l' && - *json++ == 's' && - *json++ == 'e') { - val->type = QJsonValue::Bool; - val->value = false; - DEBUG << "value: false"; - END; - return true; - } - lastError = QJsonParseError::IllegalValue; - return false; - case Quote: { - val->type = QJsonValue::String; - if (current - baseOffset >= Value::MaxSize) { - lastError = QJsonParseError::DocumentTooLarge; - return false; - } - val->value = current - baseOffset; - bool latin1; - if (!parseString(&latin1)) - return false; - val->latinOrIntValue = latin1; - DEBUG << "value: string"; - END; - return true; - } - case BeginArray: - val->type = QJsonValue::Array; - if (current - baseOffset >= Value::MaxSize) { - lastError = QJsonParseError::DocumentTooLarge; - return false; - } - val->value = current - baseOffset; - if (!parseArray()) - return false; - DEBUG << "value: array"; - END; - return true; - case BeginObject: - val->type = QJsonValue::Object; - if (current - baseOffset >= Value::MaxSize) { - lastError = QJsonParseError::DocumentTooLarge; - return false; - } - val->value = current - baseOffset; - if (!parseObject()) - return false; - DEBUG << "value: object"; - END; - return true; - case ValueSeparator: - // Essentially missing value, but after a colon, not after a comma - // like the other MissingObject errors. - lastError = QJsonParseError::IllegalValue; - return false; - case EndObject: - case EndArray: - lastError = QJsonParseError::MissingObject; - return false; - default: - --json; - if (!parseNumber(val, baseOffset)) - return false; - DEBUG << "value: number"; - END; - } - - return true; -} - - - - - -/* - number = [ minus ] int [ frac ] [ exp ] - decimal-point = %x2E ; . - digit1-9 = %x31-39 ; 1-9 - e = %x65 / %x45 ; e E - exp = e [ minus / plus ] 1*DIGIT - frac = decimal-point 1*DIGIT - int = zero / ( digit1-9 *DIGIT ) - minus = %x2D ; - - plus = %x2B ; + - zero = %x30 ; 0 - -*/ - -bool Parser::parseNumber(QJsonPrivate::Value *val, int baseOffset) -{ - BEGIN << "parseNumber" << json; - val->type = QJsonValue::Double; - - const char *start = json; - bool isInt = true; - - // minus - if (json < end && *json == '-') - ++json; - - // int = zero / ( digit1-9 *DIGIT ) - if (json < end && *json == '0') { - ++json; - } else { - while (json < end && *json >= '0' && *json <= '9') - ++json; - } - - // frac = decimal-point 1*DIGIT - if (json < end && *json == '.') { - isInt = false; - ++json; - while (json < end && *json >= '0' && *json <= '9') - ++json; - } - - // exp = e [ minus / plus ] 1*DIGIT - if (json < end && (*json == 'e' || *json == 'E')) { - isInt = false; - ++json; - if (json < end && (*json == '-' || *json == '+')) - ++json; - while (json < end && *json >= '0' && *json <= '9') - ++json; - } - - if (json >= end) { - lastError = QJsonParseError::TerminationByNumber; - return false; - } - - QByteArray number(start, json - start); - DEBUG << "numberstring" << number; - - if (isInt) { - bool ok; - int n = number.toInt(&ok); - if (ok && n < (1<<25) && n > -(1<<25)) { - val->int_value = n; - val->latinOrIntValue = true; - END; - return true; - } - } - - bool ok; - union { - quint64 ui; - double d; - }; - d = number.toDouble(&ok); - - if (!ok) { - lastError = QJsonParseError::IllegalNumber; - return false; - } - - int pos = reserveSpace(sizeof(double)); - if (pos < 0) - return false; - qToLittleEndian(ui, data + pos); - if (current - baseOffset >= Value::MaxSize) { - lastError = QJsonParseError::DocumentTooLarge; - return false; - } - val->value = pos - baseOffset; - val->latinOrIntValue = false; - - END; - return true; -} - -/* - - string = quotation-mark *char quotation-mark - - char = unescaped / - escape ( - %x22 / ; " quotation mark U+0022 - %x5C / ; \ reverse solidus U+005C - %x2F / ; / solidus U+002F - %x62 / ; b backspace U+0008 - %x66 / ; f form feed U+000C - %x6E / ; n line feed U+000A - %x72 / ; r carriage return U+000D - %x74 / ; t tab U+0009 - %x75 4HEXDIG ) ; uXXXX U+XXXX - - escape = %x5C ; \ - - quotation-mark = %x22 ; " - - unescaped = %x20-21 / %x23-5B / %x5D-10FFFF - */ -static inline bool addHexDigit(char digit, uint *result) -{ - *result <<= 4; - if (digit >= '0' && digit <= '9') - *result |= (digit - '0'); - else if (digit >= 'a' && digit <= 'f') - *result |= (digit - 'a') + 10; - else if (digit >= 'A' && digit <= 'F') - *result |= (digit - 'A') + 10; - else - return false; - return true; -} - -static inline bool scanEscapeSequence(const char *&json, const char *end, uint *ch) -{ - ++json; - if (json >= end) - return false; - - DEBUG << "scan escape" << (char)*json; - uint escaped = *json++; - switch (escaped) { - case '"': - *ch = '"'; break; - case '\\': - *ch = '\\'; break; - case '/': - *ch = '/'; break; - case 'b': - *ch = 0x8; break; - case 'f': - *ch = 0xc; break; - case 'n': - *ch = 0xa; break; - case 'r': - *ch = 0xd; break; - case 't': - *ch = 0x9; break; - case 'u': { - *ch = 0; - if (json > end - 4) - return false; - for (int i = 0; i < 4; ++i) { - if (!addHexDigit(*json, ch)) - return false; - ++json; - } - return true; - } - default: - // this is not as strict as one could be, but allows for more Json files - // to be parsed correctly. - *ch = escaped; - return true; - } - return true; -} - -static inline bool scanUtf8Char(const char *&json, const char *end, uint *result) -{ - const uchar *&src = reinterpret_cast(json); - const uchar *uend = reinterpret_cast(end); - uchar b = *src++; - int res = QUtf8Functions::fromUtf8(b, result, src, uend); - if (res < 0) { - // decoding error, backtrack the character we read above - --json; - return false; - } - - return true; -} - -bool Parser::parseString(bool *latin1) -{ - *latin1 = true; - - const char *start = json; - int outStart = current; - - // try to write out a latin1 string - - int stringPos = reserveSpace(2); - if (stringPos < 0) - return false; - - BEGIN << "parse string stringPos=" << stringPos << json; - while (json < end) { - uint ch = 0; - if (*json == '"') - break; - else if (*json == '\\') { - if (!scanEscapeSequence(json, end, &ch)) { - lastError = QJsonParseError::IllegalEscapeSequence; - return false; - } - } else { - if (!scanUtf8Char(json, end, &ch)) { - lastError = QJsonParseError::IllegalUTF8String; - return false; - } - } - // bail out if the string is not pure latin1 or too long to hold as a latin1string (which has only 16 bit for the length) - if (ch > 0xff || json - start >= 0x8000) { - *latin1 = false; - break; - } - int pos = reserveSpace(1); - if (pos < 0) - return false; - DEBUG << " " << ch << (char)ch; - data[pos] = (uchar)ch; - } - ++json; - DEBUG << "end of string"; - if (json >= end) { - lastError = QJsonParseError::UnterminatedString; - return false; - } - - // no unicode string, we are done - if (*latin1) { - // write string length - *(QJsonPrivate::qle_ushort *)(data + stringPos) = ushort(current - outStart - sizeof(ushort)); - int pos = reserveSpace((4 - current) & 3); - if (pos < 0) - return false; - while (pos & 3) - data[pos++] = 0; - END; - return true; - } - - *latin1 = false; - DEBUG << "not latin"; - - json = start; - current = outStart + sizeof(int); - - while (json < end) { - uint ch = 0; - if (*json == '"') - break; - else if (*json == '\\') { - if (!scanEscapeSequence(json, end, &ch)) { - lastError = QJsonParseError::IllegalEscapeSequence; - return false; - } - } else { - if (!scanUtf8Char(json, end, &ch)) { - lastError = QJsonParseError::IllegalUTF8String; - return false; - } - } - if (QChar::requiresSurrogates(ch)) { - int pos = reserveSpace(4); - if (pos < 0) - return false; - *(QJsonPrivate::qle_ushort *)(data + pos) = QChar::highSurrogate(ch); - *(QJsonPrivate::qle_ushort *)(data + pos + 2) = QChar::lowSurrogate(ch); - } else { - int pos = reserveSpace(2); - if (pos < 0) - return false; - *(QJsonPrivate::qle_ushort *)(data + pos) = (ushort)ch; - } - } - ++json; - - if (json >= end) { - lastError = QJsonParseError::UnterminatedString; - return false; - } - - // write string length - *(QJsonPrivate::qle_int *)(data + stringPos) = (current - outStart - sizeof(int))/2; - int pos = reserveSpace((4 - current) & 3); - if (pos < 0) - return false; - while (pos & 3) - data[pos++] = 0; - END; - return true; -} - -QT_END_NAMESPACE diff --git a/src/corelib/json/qjsonparser_p.h b/src/corelib/json/qjsonparser_p.h deleted file mode 100644 index 379256847f..0000000000 --- a/src/corelib/json/qjsonparser_p.h +++ /dev/null @@ -1,128 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtCore module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QJSONPARSER_P_H -#define QJSONPARSER_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include -#include -#include - -QT_BEGIN_NAMESPACE - -namespace QJsonPrivate { - -class Parser -{ -public: - Parser(const char *json, int length); - - QJsonDocument parse(QJsonParseError *error); - - class ParsedObject - { - public: - ParsedObject(Parser *p, int pos) : parser(p), objectPosition(pos) { - offsets.reserve(64); - } - void insert(uint offset); - - Parser *parser; - int objectPosition; - QVector offsets; - - inline QJsonPrivate::Entry *entryAt(int i) const { - return reinterpret_cast(parser->data + objectPosition + offsets[i]); - } - }; - - -private: - inline void eatBOM(); - inline bool eatSpace(); - inline char nextToken(); - - bool parseObject(); - bool parseArray(); - bool parseMember(int baseOffset); - bool parseString(bool *latin1); - bool parseValue(QJsonPrivate::Value *val, int baseOffset); - bool parseNumber(QJsonPrivate::Value *val, int baseOffset); - const char *head; - const char *json; - const char *end; - - char *data; - int dataLength; - int current; - int nestingLevel; - QJsonParseError::ParseError lastError; - - inline int reserveSpace(int space) { - if (current + space >= dataLength) { - dataLength = 2*dataLength + space; - char *newData = (char *)realloc(data, dataLength); - if (!newData) { - lastError = QJsonParseError::DocumentTooLarge; - return -1; - } - data = newData; - } - int pos = current; - current += space; - return pos; - } -}; - -} - -QT_END_NAMESPACE - -#endif diff --git a/src/corelib/json/qjsonvalue.cpp b/src/corelib/json/qjsonvalue.cpp deleted file mode 100644 index 33707b6ec3..0000000000 --- a/src/corelib/json/qjsonvalue.cpp +++ /dev/null @@ -1,863 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtCore module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include -#include -#include -#include -#include -#include - -#include "qjson_p.h" - -QT_BEGIN_NAMESPACE - -/*! - \class QJsonValue - \inmodule QtCore - \ingroup json - \ingroup shared - \reentrant - \since 5.0 - - \brief The QJsonValue class encapsulates a value in JSON. - - A value in JSON can be one of 6 basic types: - - JSON is a format to store structured data. It has 6 basic data types: - - \list - \li bool QJsonValue::Bool - \li double QJsonValue::Double - \li string QJsonValue::String - \li array QJsonValue::Array - \li object QJsonValue::Object - \li null QJsonValue::Null - \endlist - - A value can represent any of the above data types. In addition, QJsonValue has one special - flag to represent undefined values. This can be queried with isUndefined(). - - The type of the value can be queried with type() or accessors like isBool(), isString(), and so on. - Likewise, the value can be converted to the type stored in it using the toBool(), toString() and so on. - - Values are strictly typed internally and contrary to QVariant will not attempt to do any implicit type - conversions. This implies that converting to a type that is not stored in the value will return a default - constructed return value. - - \section1 QJsonValueRef - - QJsonValueRef is a helper class for QJsonArray and QJsonObject. - When you get an object of type QJsonValueRef, you can - use it as if it were a reference to a QJsonValue. If you assign to it, - the assignment will apply to the element in the QJsonArray or QJsonObject - from which you got the reference. - - The following methods return QJsonValueRef: - \list - \li \l {QJsonArray}::operator[](int i) - \li \l {QJsonObject}::operator[](const QString & key) const - \endlist - - \sa {JSON Support in Qt}, {JSON Save Game Example} -*/ - -/*! - Creates a QJsonValue of type \a type. - - The default is to create a Null value. - */ -QJsonValue::QJsonValue(Type type) - : ui(0), d(0), t(type) -{ -} - -/*! - \internal - */ -QJsonValue::QJsonValue(QJsonPrivate::Data *data, QJsonPrivate::Base *base, const QJsonPrivate::Value &v) - : d(0) -{ - t = (Type)(uint)v.type; - switch (t) { - case Undefined: - case Null: - dbl = 0; - break; - case Bool: - b = v.toBoolean(); - break; - case Double: - dbl = v.toDouble(base); - break; - case String: { - QString s = v.toString(base); - stringData = s.data_ptr(); - stringData->ref.ref(); - break; - } - case Array: - case Object: - d = data; - this->base = v.base(base); - break; - } - if (d) - d->ref.ref(); -} - -/*! - Creates a value of type Bool, with value \a b. - */ -QJsonValue::QJsonValue(bool b) - : d(0), t(Bool) -{ - this->b = b; -} - -/*! - Creates a value of type Double, with value \a n. - */ -QJsonValue::QJsonValue(double n) - : d(0), t(Double) -{ - this->dbl = n; -} - -/*! - \overload - Creates a value of type Double, with value \a n. - */ -QJsonValue::QJsonValue(int n) - : d(0), t(Double) -{ - this->dbl = n; -} - -/*! - \overload - Creates a value of type Double, with value \a n. - NOTE: the integer limits for IEEE 754 double precision data is 2^53 (-9007199254740992 to +9007199254740992). - If you pass in values outside this range expect a loss of precision to occur. - */ -QJsonValue::QJsonValue(qint64 n) - : d(0), t(Double) -{ - this->dbl = double(n); -} - -/*! - Creates a value of type String, with value \a s. - */ -QJsonValue::QJsonValue(const QString &s) - : d(0), t(String) -{ - stringDataFromQStringHelper(s); -} - -/*! - \fn QJsonValue::QJsonValue(const char *s) - - Creates a value of type String with value \a s, assuming - UTF-8 encoding of the input. - - You can disable this constructor by defining \c - QT_NO_CAST_FROM_ASCII when you compile your applications. - - \since 5.3 - */ - -void QJsonValue::stringDataFromQStringHelper(const QString &string) -{ - stringData = *(QStringData **)(&string); - stringData->ref.ref(); -} - -/*! - Creates a value of type String, with value \a s. - */ -QJsonValue::QJsonValue(QLatin1String s) - : d(0), t(String) -{ - // ### FIXME: Avoid creating the temp QString below - QString str(s); - stringDataFromQStringHelper(str); -} - -/*! - Creates a value of type Array, with value \a a. - */ -QJsonValue::QJsonValue(const QJsonArray &a) - : d(a.d), t(Array) -{ - base = a.a; - if (d) - d->ref.ref(); -} - -/*! - Creates a value of type Object, with value \a o. - */ -QJsonValue::QJsonValue(const QJsonObject &o) - : d(o.d), t(Object) -{ - base = o.o; - if (d) - d->ref.ref(); -} - - -/*! - Destroys the value. - */ -QJsonValue::~QJsonValue() -{ - if (t == String && stringData && !stringData->ref.deref()) - free(stringData); - - if (d && !d->ref.deref()) - delete d; -} - -/*! - Creates a copy of \a other. - */ -QJsonValue::QJsonValue(const QJsonValue &other) -{ - t = other.t; - d = other.d; - ui = other.ui; - if (d) - d->ref.ref(); - - if (t == String && stringData) - stringData->ref.ref(); -} - -/*! - Assigns the value stored in \a other to this object. - */ -QJsonValue &QJsonValue::operator =(const QJsonValue &other) -{ - QJsonValue copy(other); - swap(copy); - return *this; -} - -/*! - \fn QJsonValue::QJsonValue(QJsonValue &&other) - \since 5.10 - - Move-constructs a QJsonValue from \a other. -*/ - -/*! - \fn QJsonValue &QJsonValue::operator =(QJsonValue &&other) - \since 5.10 - - Move-assigns \a other to this value. -*/ - -/*! - \fn void QJsonValue::swap(QJsonValue &other) - \since 5.10 - - Swaps the value \a other with this. This operation is very fast and never fails. -*/ - -/*! - \fn bool QJsonValue::isNull() const - - Returns \c true if the value is null. -*/ - -/*! - \fn bool QJsonValue::isBool() const - - Returns \c true if the value contains a boolean. - - \sa toBool() - */ - -/*! - \fn bool QJsonValue::isDouble() const - - Returns \c true if the value contains a double. - - \sa toDouble() - */ - -/*! - \fn bool QJsonValue::isString() const - - Returns \c true if the value contains a string. - - \sa toString() - */ - -/*! - \fn bool QJsonValue::isArray() const - - Returns \c true if the value contains an array. - - \sa toArray() - */ - -/*! - \fn bool QJsonValue::isObject() const - - Returns \c true if the value contains an object. - - \sa toObject() - */ - -/*! - \fn bool QJsonValue::isUndefined() const - - Returns \c true if the value is undefined. This can happen in certain - error cases as e.g. accessing a non existing key in a QJsonObject. - */ - - -/*! - Converts \a variant to a QJsonValue and returns it. - - The conversion will convert QVariant types as follows: - - \table - \header - \li Source type - \li Destination type - \row - \li - \list - \li QMetaType::Nullptr - \endlist - \li QJsonValue::Null - \row - \li - \list - \li QMetaType::Bool - \endlist - \li QJsonValue::Bool - \row - \li - \list - \li QMetaType::Int - \li QMetaType::UInt - \li QMetaType::LongLong - \li QMetaType::ULongLong - \li QMetaType::Float - \li QMetaType::Double - \endlist - \li QJsonValue::Double - \row - \li - \list - \li QMetaType::QString - \endlist - \li QJsonValue::String - \row - \li - \list - \li QMetaType::QStringList - \li QMetaType::QVariantList - \endlist - \li QJsonValue::Array - \row - \li - \list - \li QMetaType::QVariantMap - \li QMetaType::QVariantHash - \endlist - \li QJsonValue::Object - \endtable - - For all other QVariant types a conversion to a QString will be attempted. If the returned string - is empty, a Null QJsonValue will be stored, otherwise a String value using the returned QString. - - \sa toVariant() - */ -QJsonValue QJsonValue::fromVariant(const QVariant &variant) -{ - switch (variant.userType()) { - case QMetaType::Nullptr: - return QJsonValue(Null); - case QVariant::Bool: - return QJsonValue(variant.toBool()); - case QVariant::Int: - case QMetaType::Float: - case QVariant::Double: - case QVariant::LongLong: - case QVariant::ULongLong: - case QVariant::UInt: - return QJsonValue(variant.toDouble()); - case QVariant::String: - return QJsonValue(variant.toString()); - case QVariant::StringList: - return QJsonValue(QJsonArray::fromStringList(variant.toStringList())); - case QVariant::List: - return QJsonValue(QJsonArray::fromVariantList(variant.toList())); - case QVariant::Map: - return QJsonValue(QJsonObject::fromVariantMap(variant.toMap())); - case QVariant::Hash: - return QJsonValue(QJsonObject::fromVariantHash(variant.toHash())); -#ifndef QT_BOOTSTRAPPED - case QMetaType::QJsonValue: - return variant.toJsonValue(); - case QMetaType::QJsonObject: - return variant.toJsonObject(); - case QMetaType::QJsonArray: - return variant.toJsonArray(); - case QMetaType::QJsonDocument: { - QJsonDocument doc = variant.toJsonDocument(); - return doc.isArray() ? QJsonValue(doc.array()) : QJsonValue(doc.object()); - } -#endif - default: - break; - } - QString string = variant.toString(); - if (string.isEmpty()) - return QJsonValue(); - return QJsonValue(string); -} - -/*! - Converts the value to a \l {QVariant::}{QVariant()}. - - The QJsonValue types will be converted as follows: - - \value Null QMetaType::Nullptr - \value Bool QMetaType::Bool - \value Double QMetaType::Double - \value String QString - \value Array QVariantList - \value Object QVariantMap - \value Undefined \l {QVariant::}{QVariant()} - - \sa fromVariant() - */ -QVariant QJsonValue::toVariant() const -{ - switch (t) { - case Bool: - return b; - case Double: - return dbl; - case String: - return toString(); - case Array: - return d ? - QJsonArray(d, static_cast(base)).toVariantList() : - QVariantList(); - case Object: - return d ? - QJsonObject(d, static_cast(base)).toVariantMap() : - QVariantMap(); - case Null: - return QVariant::fromValue(nullptr); - case Undefined: - break; - } - return QVariant(); -} - -/*! - \enum QJsonValue::Type - - This enum describes the type of the JSON value. - - \value Null A Null value - \value Bool A boolean value. Use toBool() to convert to a bool. - \value Double A double. Use toDouble() to convert to a double. - \value String A string. Use toString() to convert to a QString. - \value Array An array. Use toArray() to convert to a QJsonArray. - \value Object An object. Use toObject() to convert to a QJsonObject. - \value Undefined The value is undefined. This is usually returned as an - error condition, when trying to read an out of bounds value - in an array or a non existent key in an object. -*/ - -/*! - Returns the type of the value. - - \sa QJsonValue::Type - */ -QJsonValue::Type QJsonValue::type() const -{ - return t; -} - -/*! - Converts the value to a bool and returns it. - - If type() is not bool, the \a defaultValue will be returned. - */ -bool QJsonValue::toBool(bool defaultValue) const -{ - if (t != Bool) - return defaultValue; - return b; -} - -/*! - Converts the value to an int and returns it. - - If type() is not Double or the value is not a whole number, - the \a defaultValue will be returned. - */ -int QJsonValue::toInt(int defaultValue) const -{ - if (t == Double && int(dbl) == dbl) - return int(dbl); - return defaultValue; -} - -/*! - Converts the value to a double and returns it. - - If type() is not Double, the \a defaultValue will be returned. - */ -double QJsonValue::toDouble(double defaultValue) const -{ - if (t != Double) - return defaultValue; - return dbl; -} - -/*! - Converts the value to a QString and returns it. - - If type() is not String, the \a defaultValue will be returned. - */ -QString QJsonValue::toString(const QString &defaultValue) const -{ - if (t != String) - return defaultValue; - stringData->ref.ref(); // the constructor below doesn't add a ref. - QStringDataPtr holder = { stringData }; - return QString(holder); -} - -/*! - Converts the value to a QString and returns it. - - If type() is not String, a null QString will be returned. - - \sa QString::isNull() - */ -QString QJsonValue::toString() const -{ - if (t != String) - return QString(); - stringData->ref.ref(); // the constructor below doesn't add a ref. - QStringDataPtr holder = { stringData }; - return QString(holder); -} - -/*! - Converts the value to an array and returns it. - - If type() is not Array, the \a defaultValue will be returned. - */ -QJsonArray QJsonValue::toArray(const QJsonArray &defaultValue) const -{ - if (!d || t != Array) - return defaultValue; - - return QJsonArray(d, static_cast(base)); -} - -/*! - \overload - - Converts the value to an array and returns it. - - If type() is not Array, a \l{QJsonArray::}{QJsonArray()} will be returned. - */ -QJsonArray QJsonValue::toArray() const -{ - return toArray(QJsonArray()); -} - -/*! - Converts the value to an object and returns it. - - If type() is not Object, the \a defaultValue will be returned. - */ -QJsonObject QJsonValue::toObject(const QJsonObject &defaultValue) const -{ - if (!d || t != Object) - return defaultValue; - - return QJsonObject(d, static_cast(base)); -} - -/*! - \overload - - Converts the value to an object and returns it. - - If type() is not Object, the \l {QJsonObject::}{QJsonObject()} will be returned. -*/ -QJsonObject QJsonValue::toObject() const -{ - return toObject(QJsonObject()); -} - -/*! - Returns a QJsonValue representing the value for the key \a key. - - Equivalent to calling toObject().value(key). - - The returned QJsonValue is QJsonValue::Undefined if the key does not exist, - or if isObject() is false. - - \since 5.10 - - \sa QJsonValue, QJsonValue::isUndefined(), QJsonObject - */ -const QJsonValue QJsonValue::operator[](const QString &key) const -{ - if (!isObject()) - return QJsonValue(QJsonValue::Undefined); - - return toObject().value(key); -} - -/*! - \overload - \since 5.10 -*/ -const QJsonValue QJsonValue::operator[](QLatin1String key) const -{ - if (!isObject()) - return QJsonValue(QJsonValue::Undefined); - - return toObject().value(key); -} - -/*! - Returns a QJsonValue representing the value for index \a i. - - Equivalent to calling toArray().at(i). - - The returned QJsonValue is QJsonValue::Undefined, if \a i is out of bounds, - or if isArray() is false. - - \since 5.10 - - \sa QJsonValue, QJsonValue::isUndefined(), QJsonArray - */ -const QJsonValue QJsonValue::operator[](int i) const -{ - if (!isArray()) - return QJsonValue(QJsonValue::Undefined); - - return toArray().at(i); -} - -/*! - Returns \c true if the value is equal to \a other. - */ -bool QJsonValue::operator==(const QJsonValue &other) const -{ - if (t != other.t) - return false; - - switch (t) { - case Undefined: - case Null: - break; - case Bool: - return b == other.b; - case Double: - return dbl == other.dbl; - case String: - return toString() == other.toString(); - case Array: - if (base == other.base) - return true; - if (!base) - return !other.base->length; - if (!other.base) - return !base->length; - return QJsonArray(d, static_cast(base)) - == QJsonArray(other.d, static_cast(other.base)); - case Object: - if (base == other.base) - return true; - if (!base) - return !other.base->length; - if (!other.base) - return !base->length; - return QJsonObject(d, static_cast(base)) - == QJsonObject(other.d, static_cast(other.base)); - } - return true; -} - -/*! - Returns \c true if the value is not equal to \a other. - */ -bool QJsonValue::operator!=(const QJsonValue &other) const -{ - return !(*this == other); -} - -/*! - \internal - */ -void QJsonValue::detach() -{ - if (!d) - return; - - QJsonPrivate::Data *x = d->clone(base); - x->ref.ref(); - if (!d->ref.deref()) - delete d; - d = x; - base = static_cast(d->header->root()); -} - - -/*! - \class QJsonValueRef - \inmodule QtCore - \reentrant - \brief The QJsonValueRef class is a helper class for QJsonValue. - - \internal - - \ingroup json - - When you get an object of type QJsonValueRef, if you can assign to it, - the assignment will apply to the character in the string from - which you got the reference. That is its whole purpose in life. - - You can use it exactly in the same way as a reference to a QJsonValue. - - The QJsonValueRef becomes invalid once modifications are made to the - string: if you want to keep the character, copy it into a QJsonValue. - - Most of the QJsonValue member functions also exist in QJsonValueRef. - However, they are not explicitly documented here. -*/ - - -QJsonValueRef &QJsonValueRef::operator =(const QJsonValue &val) -{ - if (is_object) - o->setValueAt(index, val); - else - a->replace(index, val); - - return *this; -} - -QJsonValueRef &QJsonValueRef::operator =(const QJsonValueRef &ref) -{ - if (is_object) - o->setValueAt(index, ref); - else - a->replace(index, ref); - - return *this; -} - -QVariant QJsonValueRef::toVariant() const -{ - return toValue().toVariant(); -} - -QJsonArray QJsonValueRef::toArray() const -{ - return toValue().toArray(); -} - -QJsonObject QJsonValueRef::toObject() const -{ - return toValue().toObject(); -} - -QJsonValue QJsonValueRef::toValue() const -{ - if (!is_object) - return a->at(index); - return o->valueAt(index); -} - -#if !defined(QT_NO_DEBUG_STREAM) && !defined(QT_JSON_READONLY) -QDebug operator<<(QDebug dbg, const QJsonValue &o) -{ - QDebugStateSaver saver(dbg); - switch (o.t) { - case QJsonValue::Undefined: - dbg << "QJsonValue(undefined)"; - break; - case QJsonValue::Null: - dbg << "QJsonValue(null)"; - break; - case QJsonValue::Bool: - dbg.nospace() << "QJsonValue(bool, " << o.toBool() << ')'; - break; - case QJsonValue::Double: - dbg.nospace() << "QJsonValue(double, " << o.toDouble() << ')'; - break; - case QJsonValue::String: - dbg.nospace() << "QJsonValue(string, " << o.toString() << ')'; - break; - case QJsonValue::Array: - dbg.nospace() << "QJsonValue(array, "; - dbg << o.toArray(); - dbg << ')'; - break; - case QJsonValue::Object: - dbg.nospace() << "QJsonValue(object, "; - dbg << o.toObject(); - dbg << ')'; - break; - } - return dbg; -} -#endif - -QT_END_NAMESPACE diff --git a/src/corelib/json/qjsonvalue.h b/src/corelib/json/qjsonvalue.h deleted file mode 100644 index 96538ebbf9..0000000000 --- a/src/corelib/json/qjsonvalue.h +++ /dev/null @@ -1,255 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtCore module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QJSONVALUE_H -#define QJSONVALUE_H - -#include -#include - -QT_BEGIN_NAMESPACE - -class QDebug; -class QVariant; -class QJsonArray; -class QJsonObject; - -namespace QJsonPrivate { - class Data; - class Base; - class Object; - class Header; - class Array; - class Value; - class Entry; -} - -class Q_CORE_EXPORT QJsonValue -{ -public: - enum Type { - Null = 0x0, - Bool = 0x1, - Double = 0x2, - String = 0x3, - Array = 0x4, - Object = 0x5, - Undefined = 0x80 - }; - - QJsonValue(Type = Null); - QJsonValue(bool b); - QJsonValue(double n); - QJsonValue(int n); - QJsonValue(qint64 n); - QJsonValue(const QString &s); - QJsonValue(QLatin1String s); -#ifndef QT_NO_CAST_FROM_ASCII - inline QT_ASCII_CAST_WARN QJsonValue(const char *s) - : d(nullptr), t(String) { stringDataFromQStringHelper(QString::fromUtf8(s)); } -#endif - QJsonValue(const QJsonArray &a); - QJsonValue(const QJsonObject &o); - - ~QJsonValue(); - - QJsonValue(const QJsonValue &other); - QJsonValue &operator =(const QJsonValue &other); - - QJsonValue(QJsonValue &&other) Q_DECL_NOTHROW - : ui(other.ui), - d(other.d), - t(other.t) - { - other.ui = 0; - other.d = nullptr; - other.t = Null; - } - - QJsonValue &operator =(QJsonValue &&other) Q_DECL_NOTHROW - { - swap(other); - return *this; - } - - void swap(QJsonValue &other) Q_DECL_NOTHROW - { - qSwap(ui, other.ui); - qSwap(d, other.d); - qSwap(t, other.t); - } - - static QJsonValue fromVariant(const QVariant &variant); - QVariant toVariant() const; - - Type type() const; - inline bool isNull() const { return type() == Null; } - inline bool isBool() const { return type() == Bool; } - inline bool isDouble() const { return type() == Double; } - inline bool isString() const { return type() == String; } - inline bool isArray() const { return type() == Array; } - inline bool isObject() const { return type() == Object; } - inline bool isUndefined() const { return type() == Undefined; } - - bool toBool(bool defaultValue = false) const; - int toInt(int defaultValue = 0) const; - double toDouble(double defaultValue = 0) const; - QString toString() const; - QString toString(const QString &defaultValue) const; - QJsonArray toArray() const; - QJsonArray toArray(const QJsonArray &defaultValue) const; - QJsonObject toObject() const; - QJsonObject toObject(const QJsonObject &defaultValue) const; - - const QJsonValue operator[](const QString &key) const; - const QJsonValue operator[](QLatin1String key) const; - const QJsonValue operator[](int i) const; - - bool operator==(const QJsonValue &other) const; - bool operator!=(const QJsonValue &other) const; - -private: - // avoid implicit conversions from char * to bool - inline QJsonValue(const void *) {} - friend class QJsonPrivate::Value; - friend class QJsonArray; - friend class QJsonObject; - friend Q_CORE_EXPORT QDebug operator<<(QDebug, const QJsonValue &); - - QJsonValue(QJsonPrivate::Data *d, QJsonPrivate::Base *b, const QJsonPrivate::Value& v); - void stringDataFromQStringHelper(const QString &string); - - void detach(); - - union { - quint64 ui; - bool b; - double dbl; - QStringData *stringData; - QJsonPrivate::Base *base; - }; - QJsonPrivate::Data *d; // needed for Objects and Arrays - Type t; -}; - -class Q_CORE_EXPORT QJsonValueRef -{ -public: - QJsonValueRef(QJsonArray *array, int idx) - : a(array), is_object(false), index(idx) {} - QJsonValueRef(QJsonObject *object, int idx) - : o(object), is_object(true), index(idx) {} - - inline operator QJsonValue() const { return toValue(); } - QJsonValueRef &operator = (const QJsonValue &val); - QJsonValueRef &operator = (const QJsonValueRef &val); - - QVariant toVariant() const; - inline QJsonValue::Type type() const { return toValue().type(); } - inline bool isNull() const { return type() == QJsonValue::Null; } - inline bool isBool() const { return type() == QJsonValue::Bool; } - inline bool isDouble() const { return type() == QJsonValue::Double; } - inline bool isString() const { return type() == QJsonValue::String; } - inline bool isArray() const { return type() == QJsonValue::Array; } - inline bool isObject() const { return type() == QJsonValue::Object; } - inline bool isUndefined() const { return type() == QJsonValue::Undefined; } - - inline bool toBool() const { return toValue().toBool(); } - inline int toInt() const { return toValue().toInt(); } - inline double toDouble() const { return toValue().toDouble(); } - inline QString toString() const { return toValue().toString(); } - QJsonArray toArray() const; - QJsonObject toObject() const; - - // ### Qt 6: Add default values - inline bool toBool(bool defaultValue) const { return toValue().toBool(defaultValue); } - inline int toInt(int defaultValue) const { return toValue().toInt(defaultValue); } - inline double toDouble(double defaultValue) const { return toValue().toDouble(defaultValue); } - inline QString toString(const QString &defaultValue) const { return toValue().toString(defaultValue); } - - inline bool operator==(const QJsonValue &other) const { return toValue() == other; } - inline bool operator!=(const QJsonValue &other) const { return toValue() != other; } - -private: - QJsonValue toValue() const; - - union { - QJsonArray *a; - QJsonObject *o; - }; - uint is_object : 1; - uint index : 31; -}; - -#ifndef Q_QDOC -// ### Qt 6: Get rid of these fake pointer classes -class QJsonValuePtr -{ - QJsonValue value; -public: - explicit QJsonValuePtr(const QJsonValue& val) - : value(val) {} - - QJsonValue& operator*() { return value; } - QJsonValue* operator->() { return &value; } -}; - -class QJsonValueRefPtr -{ - QJsonValueRef valueRef; -public: - QJsonValueRefPtr(QJsonArray *array, int idx) - : valueRef(array, idx) {} - QJsonValueRefPtr(QJsonObject *object, int idx) - : valueRef(object, idx) {} - - QJsonValueRef& operator*() { return valueRef; } - QJsonValueRef* operator->() { return &valueRef; } -}; -#endif - -Q_DECLARE_SHARED_NOT_MOVABLE_UNTIL_QT6(QJsonValue) - -#if !defined(QT_NO_DEBUG_STREAM) && !defined(QT_JSON_READONLY) -Q_CORE_EXPORT QDebug operator<<(QDebug, const QJsonValue &); -#endif - -QT_END_NAMESPACE - -#endif // QJSONVALUE_H diff --git a/src/corelib/json/qjsonwriter.cpp b/src/corelib/json/qjsonwriter.cpp deleted file mode 100644 index 12ce20ef09..0000000000 --- a/src/corelib/json/qjsonwriter.cpp +++ /dev/null @@ -1,231 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Copyright (C) 2016 Intel Corporation. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtCore module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include -#include -#include "qjsonwriter_p.h" -#include "qjson_p.h" -#include "private/qutfcodec_p.h" - -QT_BEGIN_NAMESPACE - -using namespace QJsonPrivate; - -static void objectContentToJson(const QJsonPrivate::Object *o, QByteArray &json, int indent, bool compact); -static void arrayContentToJson(const QJsonPrivate::Array *a, QByteArray &json, int indent, bool compact); - -static inline uchar hexdig(uint u) -{ - return (u < 0xa ? '0' + u : 'a' + u - 0xa); -} - -static QByteArray escapedString(const QString &s) -{ - const uchar replacement = '?'; - QByteArray ba(s.length(), Qt::Uninitialized); - - uchar *cursor = reinterpret_cast(const_cast(ba.constData())); - const uchar *ba_end = cursor + ba.length(); - const ushort *src = reinterpret_cast(s.constBegin()); - const ushort *const end = reinterpret_cast(s.constEnd()); - - while (src != end) { - if (cursor >= ba_end - 6) { - // ensure we have enough space - int pos = cursor - (const uchar *)ba.constData(); - ba.resize(ba.size()*2); - cursor = (uchar *)ba.data() + pos; - ba_end = (const uchar *)ba.constData() + ba.length(); - } - - uint u = *src++; - if (u < 0x80) { - if (u < 0x20 || u == 0x22 || u == 0x5c) { - *cursor++ = '\\'; - switch (u) { - case 0x22: - *cursor++ = '"'; - break; - case 0x5c: - *cursor++ = '\\'; - break; - case 0x8: - *cursor++ = 'b'; - break; - case 0xc: - *cursor++ = 'f'; - break; - case 0xa: - *cursor++ = 'n'; - break; - case 0xd: - *cursor++ = 'r'; - break; - case 0x9: - *cursor++ = 't'; - break; - default: - *cursor++ = 'u'; - *cursor++ = '0'; - *cursor++ = '0'; - *cursor++ = hexdig(u>>4); - *cursor++ = hexdig(u & 0xf); - } - } else { - *cursor++ = (uchar)u; - } - } else { - if (QUtf8Functions::toUtf8(u, cursor, src, end) < 0) - *cursor++ = replacement; - } - } - - ba.resize(cursor - (const uchar *)ba.constData()); - return ba; -} - -static void valueToJson(const QJsonPrivate::Base *b, const QJsonPrivate::Value &v, QByteArray &json, int indent, bool compact) -{ - QJsonValue::Type type = (QJsonValue::Type)(uint)v.type; - switch (type) { - case QJsonValue::Bool: - json += v.toBoolean() ? "true" : "false"; - break; - case QJsonValue::Double: { - const double d = v.toDouble(b); - if (qIsFinite(d)) { // +2 to format to ensure the expected precision - const double abs = std::abs(d); - json += QByteArray::number(d, abs == static_cast(abs) ? 'f' : 'g', QLocale::FloatingPointShortest); - } else { - json += "null"; // +INF || -INF || NaN (see RFC4627#section2.4) - } - break; - } - case QJsonValue::String: - json += '"'; - json += escapedString(v.toString(b)); - json += '"'; - break; - case QJsonValue::Array: - json += compact ? "[" : "[\n"; - arrayContentToJson(static_cast(v.base(b)), json, indent + (compact ? 0 : 1), compact); - json += QByteArray(4*indent, ' '); - json += ']'; - break; - case QJsonValue::Object: - json += compact ? "{" : "{\n"; - objectContentToJson(static_cast(v.base(b)), json, indent + (compact ? 0 : 1), compact); - json += QByteArray(4*indent, ' '); - json += '}'; - break; - case QJsonValue::Null: - default: - json += "null"; - } -} - -static void arrayContentToJson(const QJsonPrivate::Array *a, QByteArray &json, int indent, bool compact) -{ - if (!a || !a->length) - return; - - QByteArray indentString(4*indent, ' '); - - uint i = 0; - while (1) { - json += indentString; - valueToJson(a, a->at(i), json, indent, compact); - - if (++i == a->length) { - if (!compact) - json += '\n'; - break; - } - - json += compact ? "," : ",\n"; - } -} - - -static void objectContentToJson(const QJsonPrivate::Object *o, QByteArray &json, int indent, bool compact) -{ - if (!o || !o->length) - return; - - QByteArray indentString(4*indent, ' '); - - uint i = 0; - while (1) { - QJsonPrivate::Entry *e = o->entryAt(i); - json += indentString; - json += '"'; - json += escapedString(e->key()); - json += compact ? "\":" : "\": "; - valueToJson(o, e->value, json, indent, compact); - - if (++i == o->length) { - if (!compact) - json += '\n'; - break; - } - - json += compact ? "," : ",\n"; - } -} - -void Writer::objectToJson(const QJsonPrivate::Object *o, QByteArray &json, int indent, bool compact) -{ - json.reserve(json.size() + (o ? (int)o->size : 16)); - json += compact ? "{" : "{\n"; - objectContentToJson(o, json, indent + (compact ? 0 : 1), compact); - json += QByteArray(4*indent, ' '); - json += compact ? "}" : "}\n"; -} - -void Writer::arrayToJson(const QJsonPrivate::Array *a, QByteArray &json, int indent, bool compact) -{ - json.reserve(json.size() + (a ? (int)a->size : 16)); - json += compact ? "[" : "[\n"; - arrayContentToJson(a, json, indent + (compact ? 0 : 1), compact); - json += QByteArray(4*indent, ' '); - json += compact ? "]" : "]\n"; -} - -QT_END_NAMESPACE diff --git a/src/corelib/json/qjsonwriter_p.h b/src/corelib/json/qjsonwriter_p.h deleted file mode 100644 index 76a8460449..0000000000 --- a/src/corelib/json/qjsonwriter_p.h +++ /dev/null @@ -1,73 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtCore module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QJSONWRITER_P_H -#define QJSONWRITER_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include -#include - -QT_BEGIN_NAMESPACE - -namespace QJsonPrivate -{ - -class Writer -{ -public: - static void objectToJson(const QJsonPrivate::Object *o, QByteArray &json, int indent, bool compact = false); - static void arrayToJson(const QJsonPrivate::Array *a, QByteArray &json, int indent, bool compact = false); -}; - -} - -QT_END_NAMESPACE - -#endif diff --git a/src/corelib/serialization/.gitignore b/src/corelib/serialization/.gitignore new file mode 100644 index 0000000000..89f9ac04aa --- /dev/null +++ b/src/corelib/serialization/.gitignore @@ -0,0 +1 @@ +out/ diff --git a/src/corelib/serialization/make-xml-parser.sh b/src/corelib/serialization/make-xml-parser.sh new file mode 100755 index 0000000000..0296e4c22b --- /dev/null +++ b/src/corelib/serialization/make-xml-parser.sh @@ -0,0 +1,51 @@ +#!/bin/sh +############################################################################# +## +## Copyright (C) 2016 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is the build configuration utility of the Qt Toolkit. +## +## $QT_BEGIN_LICENSE:LGPL$ +## Commercial License Usage +## Licensees holding valid commercial Qt licenses may use this file in +## accordance with the commercial license agreement provided with the +## Software or, alternatively, in accordance with the terms contained in +## a written agreement between you and The Qt Company. For licensing terms +## and conditions see https://www.qt.io/terms-conditions. For further +## information use the contact form at https://www.qt.io/contact-us. +## +## GNU Lesser General Public License Usage +## Alternatively, this file may be used under the terms of the GNU Lesser +## General Public License version 3 as published by the Free Software +## Foundation and appearing in the file LICENSE.LGPL3 included in the +## packaging of this file. Please review the following information to +## ensure the GNU Lesser General Public License version 3 requirements +## will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU +## General Public License version 2.0 or (at your option) the GNU General +## Public license version 3 or any later version approved by the KDE Free +## Qt Foundation. The licenses are as published by the Free Software +## Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +## included in the packaging of this file. Please review the following +## information to ensure the GNU General Public License requirements will +## be met: https://www.gnu.org/licenses/gpl-2.0.html and +## https://www.gnu.org/licenses/gpl-3.0.html. +## +## $QT_END_LICENSE$ +## +############################################################################# + +me=$(dirname $0) +mkdir -p $me/out +(cd $me/out && ../../../../util/qlalr/qlalr --qt --no-debug --no-lines ../qxmlstream.g) + +for f in $me/out/*.h; do + n=$(basename $f) + cp $f $n +done + +git diff . + diff --git a/src/corelib/serialization/qdatastream.cpp b/src/corelib/serialization/qdatastream.cpp new file mode 100644 index 0000000000..8f419a4a46 --- /dev/null +++ b/src/corelib/serialization/qdatastream.cpp @@ -0,0 +1,1400 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdatastream.h" +#include "qdatastream_p.h" + +#if !defined(QT_NO_DATASTREAM) || defined(QT_BOOTSTRAPPED) +#include "qbuffer.h" +#include "qfloat16.h" +#include "qstring.h" +#include +#include +#include +#include "qendian.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QDataStream + \inmodule QtCore + \reentrant + \brief The QDataStream class provides serialization of binary data + to a QIODevice. + + \ingroup io + + + A data stream is a binary stream of encoded information which is + 100% independent of the host computer's operating system, CPU or + byte order. For example, a data stream that is written by a PC + under Windows can be read by a Sun SPARC running Solaris. + + You can also use a data stream to read/write \l{raw}{raw + unencoded binary data}. If you want a "parsing" input stream, see + QTextStream. + + The QDataStream class implements the serialization of C++'s basic + data types, like \c char, \c short, \c int, \c{char *}, etc. + Serialization of more complex data is accomplished by breaking up + the data into primitive units. + + A data stream cooperates closely with a QIODevice. A QIODevice + represents an input/output medium one can read data from and write + data to. The QFile class is an example of an I/O device. + + Example (write binary data to a stream): + + \snippet code/src_corelib_io_qdatastream.cpp 0 + + Example (read binary data from a stream): + + \snippet code/src_corelib_io_qdatastream.cpp 1 + + Each item written to the stream is written in a predefined binary + format that varies depending on the item's type. Supported Qt + types include QBrush, QColor, QDateTime, QFont, QPixmap, QString, + QVariant and many others. For the complete list of all Qt types + supporting data streaming see \l{Serializing Qt Data Types}. + + For integers it is best to always cast to a Qt integer type for + writing, and to read back into the same Qt integer type. This + ensures that you get integers of the size you want and insulates + you from compiler and platform differences. + + To take one example, a \c{char *} string is written as a 32-bit + integer equal to the length of the string including the '\\0' byte, + followed by all the characters of the string including the + '\\0' byte. When reading a \c{char *} string, 4 bytes are read to + create the 32-bit length value, then that many characters for the + \c {char *} string including the '\\0' terminator are read. + + The initial I/O device is usually set in the constructor, but can be + changed with setDevice(). If you've reached the end of the data + (or if there is no I/O device set) atEnd() will return true. + + \section1 Versioning + + QDataStream's binary format has evolved since Qt 1.0, and is + likely to continue evolving to reflect changes done in Qt. When + inputting or outputting complex types, it's very important to + make sure that the same version of the stream (version()) is used + for reading and writing. If you need both forward and backward + compatibility, you can hardcode the version number in the + application: + + \snippet code/src_corelib_io_qdatastream.cpp 2 + + If you are producing a new binary data format, such as a file + format for documents created by your application, you could use a + QDataStream to write the data in a portable format. Typically, you + would write a brief header containing a magic string and a version + number to give yourself room for future expansion. For example: + + \snippet code/src_corelib_io_qdatastream.cpp 3 + + Then read it in with: + + \snippet code/src_corelib_io_qdatastream.cpp 4 + + You can select which byte order to use when serializing data. The + default setting is big endian (MSB first). Changing it to little + endian breaks the portability (unless the reader also changes to + little endian). We recommend keeping this setting unless you have + special requirements. + + \target raw + \section1 Reading and Writing Raw Binary Data + + You may wish to read/write your own raw binary data to/from the + data stream directly. Data may be read from the stream into a + preallocated \c{char *} using readRawData(). Similarly data can be + written to the stream using writeRawData(). Note that any + encoding/decoding of the data must be done by you. + + A similar pair of functions is readBytes() and writeBytes(). These + differ from their \e raw counterparts as follows: readBytes() + reads a quint32 which is taken to be the length of the data to be + read, then that number of bytes is read into the preallocated + \c{char *}; writeBytes() writes a quint32 containing the length of the + data, followed by the data. Note that any encoding/decoding of + the data (apart from the length quint32) must be done by you. + + \section1 Reading and Writing Qt Collection Classes + + The Qt container classes can also be serialized to a QDataStream. + These include QList, QLinkedList, QVector, QSet, QHash, and QMap. + The stream operators are declared as non-members of the classes. + + \target Serializing Qt Classes + \section1 Reading and Writing Other Qt Classes + + In addition to the overloaded stream operators documented here, + any Qt classes that you might want to serialize to a QDataStream + will have appropriate stream operators declared as non-member of + the class: + + \code + QDataStream &operator<<(QDataStream &, const QXxx &); + QDataStream &operator>>(QDataStream &, QXxx &); + \endcode + + For example, here are the stream operators declared as non-members + of the QImage class: + + \code + QDataStream & operator<< (QDataStream& stream, const QImage& image); + QDataStream & operator>> (QDataStream& stream, QImage& image); + \endcode + + To see if your favorite Qt class has similar stream operators + defined, check the \b {Related Non-Members} section of the + class's documentation page. + + \section1 Using Read Transactions + + When a data stream operates on an asynchronous device, the chunks of data + can arrive at arbitrary points in time. The QDataStream class implements + a transaction mechanism that provides the ability to read the data + atomically with a series of stream operators. As an example, you can + handle incomplete reads from a socket by using a transaction in a slot + connected to the readyRead() signal: + + \snippet code/src_corelib_io_qdatastream.cpp 6 + + If no full packet is received, this code restores the stream to the + initial position, after which you need to wait for more data to arrive. + + \sa QTextStream, QVariant +*/ + +/*! + \enum QDataStream::ByteOrder + + The byte order used for reading/writing the data. + + \value BigEndian Most significant byte first (the default) + \value LittleEndian Least significant byte first +*/ + +/*! + \enum QDataStream::FloatingPointPrecision + + The precision of floating point numbers used for reading/writing the data. This will only have + an effect if the version of the data stream is Qt_4_6 or higher. + + \warning The floating point precision must be set to the same value on the object that writes + and the object that reads the data stream. + + \value SinglePrecision All floating point numbers in the data stream have 32-bit precision. + \value DoublePrecision All floating point numbers in the data stream have 64-bit precision. + + \sa setFloatingPointPrecision(), floatingPointPrecision() +*/ + +/*! + \enum QDataStream::Status + + This enum describes the current status of the data stream. + + \value Ok The data stream is operating normally. + \value ReadPastEnd The data stream has read past the end of the + data in the underlying device. + \value ReadCorruptData The data stream has read corrupt data. + \value WriteFailed The data stream cannot write to the underlying device. +*/ + +/***************************************************************************** + QDataStream member functions + *****************************************************************************/ + +#define Q_VOID + +#undef CHECK_STREAM_PRECOND +#ifndef QT_NO_DEBUG +#define CHECK_STREAM_PRECOND(retVal) \ + if (!dev) { \ + qWarning("QDataStream: No device"); \ + return retVal; \ + } +#else +#define CHECK_STREAM_PRECOND(retVal) \ + if (!dev) { \ + return retVal; \ + } +#endif + +#define CHECK_STREAM_WRITE_PRECOND(retVal) \ + CHECK_STREAM_PRECOND(retVal) \ + if (q_status != Ok) \ + return retVal; + +#define CHECK_STREAM_TRANSACTION_PRECOND(retVal) \ + if (!d || d->transactionDepth == 0) { \ + qWarning("QDataStream: No transaction in progress"); \ + return retVal; \ + } + +/*! + Constructs a data stream that has no I/O device. + + \sa setDevice() +*/ + +QDataStream::QDataStream() +{ + dev = 0; + owndev = false; + byteorder = BigEndian; + ver = Qt_DefaultCompiledVersion; + noswap = QSysInfo::ByteOrder == QSysInfo::BigEndian; + q_status = Ok; +} + +/*! + Constructs a data stream that uses the I/O device \a d. + + \sa setDevice(), device() +*/ + +QDataStream::QDataStream(QIODevice *d) +{ + dev = d; // set device + owndev = false; + byteorder = BigEndian; // default byte order + ver = Qt_DefaultCompiledVersion; + noswap = QSysInfo::ByteOrder == QSysInfo::BigEndian; + q_status = Ok; +} + +/*! + \fn QDataStream::QDataStream(QByteArray *a, QIODevice::OpenMode mode) + + Constructs a data stream that operates on a byte array, \a a. The + \a mode describes how the device is to be used. + + Alternatively, you can use QDataStream(const QByteArray &) if you + just want to read from a byte array. + + Since QByteArray is not a QIODevice subclass, internally a QBuffer + is created to wrap the byte array. +*/ + +QDataStream::QDataStream(QByteArray *a, QIODevice::OpenMode flags) +{ + QBuffer *buf = new QBuffer(a); +#ifndef QT_NO_QOBJECT + buf->blockSignals(true); +#endif + buf->open(flags); + dev = buf; + owndev = true; + byteorder = BigEndian; + ver = Qt_DefaultCompiledVersion; + noswap = QSysInfo::ByteOrder == QSysInfo::BigEndian; + q_status = Ok; +} + +/*! + Constructs a read-only data stream that operates on byte array \a a. + Use QDataStream(QByteArray*, int) if you want to write to a byte + array. + + Since QByteArray is not a QIODevice subclass, internally a QBuffer + is created to wrap the byte array. +*/ +QDataStream::QDataStream(const QByteArray &a) +{ + QBuffer *buf = new QBuffer; +#ifndef QT_NO_QOBJECT + buf->blockSignals(true); +#endif + buf->setData(a); + buf->open(QIODevice::ReadOnly); + dev = buf; + owndev = true; + byteorder = BigEndian; + ver = Qt_DefaultCompiledVersion; + noswap = QSysInfo::ByteOrder == QSysInfo::BigEndian; + q_status = Ok; +} + +/*! + Destroys the data stream. + + The destructor will not affect the current I/O device, unless it is + an internal I/O device (e.g. a QBuffer) processing a QByteArray + passed in the \e constructor, in which case the internal I/O device + is destroyed. +*/ + +QDataStream::~QDataStream() +{ + if (owndev) + delete dev; +} + + +/*! + \fn QIODevice *QDataStream::device() const + + Returns the I/O device currently set, or 0 if no + device is currently set. + + \sa setDevice() +*/ + +/*! + void QDataStream::setDevice(QIODevice *d) + + Sets the I/O device to \a d, which can be 0 + to unset to current I/O device. + + \sa device() +*/ + +void QDataStream::setDevice(QIODevice *d) +{ + if (owndev) { + delete dev; + owndev = false; + } + dev = d; +} + +/*! + \obsolete + Unsets the I/O device. + Use setDevice(0) instead. +*/ + +void QDataStream::unsetDevice() +{ + setDevice(0); +} + + +/*! + \fn bool QDataStream::atEnd() const + + Returns \c true if the I/O device has reached the end position (end of + the stream or file) or if there is no I/O device set; otherwise + returns \c false. + + \sa QIODevice::atEnd() +*/ + +bool QDataStream::atEnd() const +{ + return dev ? dev->atEnd() : true; +} + +/*! + Returns the floating point precision of the data stream. + + \since 4.6 + + \sa FloatingPointPrecision, setFloatingPointPrecision() +*/ +QDataStream::FloatingPointPrecision QDataStream::floatingPointPrecision() const +{ + return d == 0 ? QDataStream::DoublePrecision : d->floatingPointPrecision; +} + +/*! + Sets the floating point precision of the data stream to \a precision. If the floating point precision is + DoublePrecision and the version of the data stream is Qt_4_6 or higher, all floating point + numbers will be written and read with 64-bit precision. If the floating point precision is + SinglePrecision and the version is Qt_4_6 or higher, all floating point numbers will be written + and read with 32-bit precision. + + For versions prior to Qt_4_6, the precision of floating point numbers in the data stream depends + on the stream operator called. + + The default is DoublePrecision. + + Note that this property does not affect the serialization or deserialization of \c qfloat16 + instances. + + \warning This property must be set to the same value on the object that writes and the object + that reads the data stream. + + \since 4.6 +*/ +void QDataStream::setFloatingPointPrecision(QDataStream::FloatingPointPrecision precision) +{ + if (d == 0) + d.reset(new QDataStreamPrivate()); + d->floatingPointPrecision = precision; +} + +/*! + Returns the status of the data stream. + + \sa Status, setStatus(), resetStatus() +*/ + +QDataStream::Status QDataStream::status() const +{ + return q_status; +} + +/*! + Resets the status of the data stream. + + \sa Status, status(), setStatus() +*/ +void QDataStream::resetStatus() +{ + q_status = Ok; +} + +/*! + Sets the status of the data stream to the \a status given. + + Subsequent calls to setStatus() are ignored until resetStatus() + is called. + + \sa Status, status(), resetStatus() +*/ +void QDataStream::setStatus(Status status) +{ + if (q_status == Ok) + q_status = status; +} + +/*! + \fn int QDataStream::byteOrder() const + + Returns the current byte order setting -- either BigEndian or + LittleEndian. + + \sa setByteOrder() +*/ + +/*! + Sets the serialization byte order to \a bo. + + The \a bo parameter can be QDataStream::BigEndian or + QDataStream::LittleEndian. + + The default setting is big endian. We recommend leaving this + setting unless you have special requirements. + + \sa byteOrder() +*/ + +void QDataStream::setByteOrder(ByteOrder bo) +{ + byteorder = bo; + if (QSysInfo::ByteOrder == QSysInfo::BigEndian) + noswap = (byteorder == BigEndian); + else + noswap = (byteorder == LittleEndian); +} + + +/*! + \enum QDataStream::Version + + This enum provides symbolic synonyms for the data serialization + format version numbers. + + \value Qt_1_0 Version 1 (Qt 1.x) + \value Qt_2_0 Version 2 (Qt 2.0) + \value Qt_2_1 Version 3 (Qt 2.1, 2.2, 2.3) + \value Qt_3_0 Version 4 (Qt 3.0) + \value Qt_3_1 Version 5 (Qt 3.1, 3.2) + \value Qt_3_3 Version 6 (Qt 3.3) + \value Qt_4_0 Version 7 (Qt 4.0, Qt 4.1) + \value Qt_4_1 Version 7 (Qt 4.0, Qt 4.1) + \value Qt_4_2 Version 8 (Qt 4.2) + \value Qt_4_3 Version 9 (Qt 4.3) + \value Qt_4_4 Version 10 (Qt 4.4) + \value Qt_4_5 Version 11 (Qt 4.5) + \value Qt_4_6 Version 12 (Qt 4.6, Qt 4.7, Qt 4.8) + \value Qt_4_7 Same as Qt_4_6. + \value Qt_4_8 Same as Qt_4_6. + \value Qt_4_9 Same as Qt_4_6. + \value Qt_5_0 Version 13 (Qt 5.0) + \value Qt_5_1 Version 14 (Qt 5.1) + \value Qt_5_2 Version 15 (Qt 5.2) + \value Qt_5_3 Same as Qt_5_2 + \value Qt_5_4 Version 16 (Qt 5.4) + \value Qt_5_5 Same as Qt_5_4 + \value Qt_5_6 Version 17 (Qt 5.6) + \value Qt_5_7 Same as Qt_5_6 + \value Qt_5_8 Same as Qt_5_6 + \value Qt_5_9 Same as Qt_5_6 + \value Qt_5_10 Same as Qt_5_6 + \value Qt_5_11 Same as Qt_5_6 + \omitvalue Qt_DefaultCompiledVersion + + \sa setVersion(), version() +*/ + +/*! + \fn int QDataStream::version() const + + Returns the version number of the data serialization format. + + \sa setVersion(), Version +*/ + +/*! + \fn void QDataStream::setVersion(int v) + + Sets the version number of the data serialization format to \a v, + a value of the \l Version enum. + + You don't \e have to set a version if you are using the current + version of Qt, but for your own custom binary formats we + recommend that you do; see \l{Versioning} in the Detailed + Description. + + To accommodate new functionality, the datastream serialization + format of some Qt classes has changed in some versions of Qt. If + you want to read data that was created by an earlier version of + Qt, or write data that can be read by a program that was compiled + with an earlier version of Qt, use this function to modify the + serialization format used by QDataStream. + + The \l Version enum provides symbolic constants for the different + versions of Qt. For example: + + \snippet code/src_corelib_io_qdatastream.cpp 5 + + \sa version(), Version +*/ + +/*! + \since 5.7 + + Starts a new read transaction on the stream. + + Defines a restorable point within the sequence of read operations. For + sequential devices, read data will be duplicated internally to allow + recovery in case of incomplete reads. For random-access devices, + this function saves the current position of the stream. Call + commitTransaction(), rollbackTransaction(), or abortTransaction() to + finish the current transaction. + + Once a transaction is started, subsequent calls to this function will make + the transaction recursive. Inner transactions act as agents of the + outermost transaction (i.e., report the status of read operations to the + outermost transaction, which can restore the position of the stream). + + \note Restoring to the point of the nested startTransaction() call is not + supported. + + When an error occurs during a transaction (including an inner transaction + failing), reading from the data stream is suspended (all subsequent read + operations return empty/zero values) and subsequent inner transactions are + forced to fail. Starting a new outermost transaction recovers from this + state. This behavior makes it unnecessary to error-check every read + operation separately. + + \sa commitTransaction(), rollbackTransaction(), abortTransaction() +*/ + +void QDataStream::startTransaction() +{ + CHECK_STREAM_PRECOND(Q_VOID) + + if (d == 0) + d.reset(new QDataStreamPrivate()); + + if (++d->transactionDepth == 1) { + dev->startTransaction(); + resetStatus(); + } +} + +/*! + \since 5.7 + + Completes a read transaction. Returns \c true if no read errors have + occurred during the transaction; otherwise returns \c false. + + If called on an inner transaction, committing will be postponed until + the outermost commitTransaction(), rollbackTransaction(), or + abortTransaction() call occurs. + + Otherwise, if the stream status indicates reading past the end of the + data, this function restores the stream data to the point of the + startTransaction() call. When this situation occurs, you need to wait for + more data to arrive, after which you start a new transaction. If the data + stream has read corrupt data or any of the inner transactions was aborted, + this function aborts the transaction. + + \sa startTransaction(), rollbackTransaction(), abortTransaction() +*/ + +bool QDataStream::commitTransaction() +{ + CHECK_STREAM_TRANSACTION_PRECOND(false) + if (--d->transactionDepth == 0) { + CHECK_STREAM_PRECOND(false) + + if (q_status == ReadPastEnd) { + dev->rollbackTransaction(); + return false; + } + dev->commitTransaction(); + } + return q_status == Ok; +} + +/*! + \since 5.7 + + Reverts a read transaction. + + This function is commonly used to rollback the transaction when an + incomplete read was detected prior to committing the transaction. + + If called on an inner transaction, reverting is delegated to the outermost + transaction, and subsequently started inner transactions are forced to + fail. + + For the outermost transaction, restores the stream data to the point of + the startTransaction() call. If the data stream has read corrupt data or + any of the inner transactions was aborted, this function aborts the + transaction. + + If the preceding stream operations were successful, sets the status of the + data stream to \value ReadPastEnd. + + \sa startTransaction(), commitTransaction(), abortTransaction() +*/ + +void QDataStream::rollbackTransaction() +{ + setStatus(ReadPastEnd); + + CHECK_STREAM_TRANSACTION_PRECOND(Q_VOID) + if (--d->transactionDepth != 0) + return; + + CHECK_STREAM_PRECOND(Q_VOID) + if (q_status == ReadPastEnd) + dev->rollbackTransaction(); + else + dev->commitTransaction(); +} + +/*! + \since 5.7 + + Aborts a read transaction. + + This function is commonly used to discard the transaction after + higher-level protocol errors or loss of stream synchronization. + + If called on an inner transaction, aborting is delegated to the outermost + transaction, and subsequently started inner transactions are forced to + fail. + + For the outermost transaction, discards the restoration point and any + internally duplicated data of the stream. Will not affect the current + read position of the stream. + + Sets the status of the data stream to \value ReadCorruptData. + + \sa startTransaction(), commitTransaction(), rollbackTransaction() +*/ + +void QDataStream::abortTransaction() +{ + q_status = ReadCorruptData; + + CHECK_STREAM_TRANSACTION_PRECOND(Q_VOID) + if (--d->transactionDepth != 0) + return; + + CHECK_STREAM_PRECOND(Q_VOID) + dev->commitTransaction(); +} + +/***************************************************************************** + QDataStream read functions + *****************************************************************************/ + +/*! + \internal +*/ + +int QDataStream::readBlock(char *data, int len) +{ + // Disable reads on failure in transacted stream + if (q_status != Ok && dev->isTransactionStarted()) + return -1; + + const int readResult = dev->read(data, len); + if (readResult != len) + setStatus(ReadPastEnd); + return readResult; +} + +/*! + \fn QDataStream &QDataStream::operator>>(std::nullptr_t &ptr) + \since 5.9 + \overload + + Simulates reading a \c{std::nullptr_t} from the stream into \a ptr and + returns a reference to the stream. This function does not actually read + anything from the stream, as \c{std::nullptr_t} values are stored as 0 + bytes. +*/ + +/*! + \fn QDataStream &QDataStream::operator>>(quint8 &i) + \overload + + Reads an unsigned byte from the stream into \a i, and returns a + reference to the stream. +*/ + +/*! + Reads a signed byte from the stream into \a i, and returns a + reference to the stream. +*/ + +QDataStream &QDataStream::operator>>(qint8 &i) +{ + i = 0; + CHECK_STREAM_PRECOND(*this) + char c; + if (readBlock(&c, 1) == 1) + i = qint8(c); + return *this; +} + + +/*! + \fn QDataStream &QDataStream::operator>>(quint16 &i) + \overload + + Reads an unsigned 16-bit integer from the stream into \a i, and + returns a reference to the stream. +*/ + +/*! + \overload + + Reads a signed 16-bit integer from the stream into \a i, and + returns a reference to the stream. +*/ + +QDataStream &QDataStream::operator>>(qint16 &i) +{ + i = 0; + CHECK_STREAM_PRECOND(*this) + if (readBlock(reinterpret_cast(&i), 2) != 2) { + i = 0; + } else { + if (!noswap) { + i = qbswap(i); + } + } + return *this; +} + + +/*! + \fn QDataStream &QDataStream::operator>>(quint32 &i) + \overload + + Reads an unsigned 32-bit integer from the stream into \a i, and + returns a reference to the stream. +*/ + +/*! + \overload + + Reads a signed 32-bit integer from the stream into \a i, and + returns a reference to the stream. +*/ + +QDataStream &QDataStream::operator>>(qint32 &i) +{ + i = 0; + CHECK_STREAM_PRECOND(*this) + if (readBlock(reinterpret_cast(&i), 4) != 4) { + i = 0; + } else { + if (!noswap) { + i = qbswap(i); + } + } + return *this; +} + +/*! + \fn QDataStream &QDataStream::operator>>(quint64 &i) + \overload + + Reads an unsigned 64-bit integer from the stream, into \a i, and + returns a reference to the stream. +*/ + +/*! + \overload + + Reads a signed 64-bit integer from the stream into \a i, and + returns a reference to the stream. +*/ + +QDataStream &QDataStream::operator>>(qint64 &i) +{ + i = qint64(0); + CHECK_STREAM_PRECOND(*this) + if (version() < 6) { + quint32 i1, i2; + *this >> i2 >> i1; + i = ((quint64)i1 << 32) + i2; + } else { + if (readBlock(reinterpret_cast(&i), 8) != 8) { + i = qint64(0); + } else { + if (!noswap) { + i = qbswap(i); + } + } + } + return *this; +} + +/*! + Reads a boolean value from the stream into \a i. Returns a + reference to the stream. +*/ +QDataStream &QDataStream::operator>>(bool &i) +{ + qint8 v; + *this >> v; + i = !!v; + return *this; +} + +/*! + \overload + + Reads a floating point number from the stream into \a f, + using the standard IEEE 754 format. Returns a reference to the + stream. + + \sa setFloatingPointPrecision() +*/ + +QDataStream &QDataStream::operator>>(float &f) +{ + if (version() >= QDataStream::Qt_4_6 + && floatingPointPrecision() == QDataStream::DoublePrecision) { + double d; + *this >> d; + f = d; + return *this; + } + + f = 0.0f; + CHECK_STREAM_PRECOND(*this) + if (readBlock(reinterpret_cast(&f), 4) != 4) { + f = 0.0f; + } else { + if (!noswap) { + union { + float val1; + quint32 val2; + } x; + x.val2 = qbswap(*reinterpret_cast(&f)); + f = x.val1; + } + } + return *this; +} + +/*! + \overload + + Reads a floating point number from the stream into \a f, + using the standard IEEE 754 format. Returns a reference to the + stream. + + \sa setFloatingPointPrecision() +*/ + +QDataStream &QDataStream::operator>>(double &f) +{ + if (version() >= QDataStream::Qt_4_6 + && floatingPointPrecision() == QDataStream::SinglePrecision) { + float d; + *this >> d; + f = d; + return *this; + } + + f = 0.0; + CHECK_STREAM_PRECOND(*this) + if (readBlock(reinterpret_cast(&f), 8) != 8) { + f = 0.0; + } else { + if (!noswap) { + union { + double val1; + quint64 val2; + } x; + x.val2 = qbswap(*reinterpret_cast(&f)); + f = x.val1; + } + } + return *this; +} + + +/*! + \overload + \since 5.9 + + Reads a floating point number from the stream into \a f, + using the standard IEEE 754 format. Returns a reference to the + stream. +*/ +QDataStream &QDataStream::operator>>(qfloat16 &f) +{ + return *this >> reinterpret_cast(f); +} + + +/*! + \overload + + Reads the '\\0'-terminated string \a s from the stream and returns + a reference to the stream. + + The string is deserialized using \c{readBytes()}. + + Space for the string is allocated using \c{new []} -- the caller must + destroy it with \c{delete []}. + + \sa readBytes(), readRawData() +*/ + +QDataStream &QDataStream::operator>>(char *&s) +{ + uint len = 0; + return readBytes(s, len); +} + + +/*! + Reads the buffer \a s from the stream and returns a reference to + the stream. + + The buffer \a s is allocated using \c{new []}. Destroy it with the + \c{delete []} operator. + + The \a l parameter is set to the length of the buffer. If the + string read is empty, \a l is set to 0 and \a s is set to + a null pointer. + + The serialization format is a quint32 length specifier first, + then \a l bytes of data. + + \sa readRawData(), writeBytes() +*/ + +QDataStream &QDataStream::readBytes(char *&s, uint &l) +{ + s = 0; + l = 0; + CHECK_STREAM_PRECOND(*this) + + quint32 len; + *this >> len; + if (len == 0) + return *this; + + const quint32 Step = 1024 * 1024; + quint32 allocated = 0; + char *prevBuf = 0; + char *curBuf = 0; + + do { + int blockSize = qMin(Step, len - allocated); + prevBuf = curBuf; + curBuf = new char[allocated + blockSize + 1]; + if (prevBuf) { + memcpy(curBuf, prevBuf, allocated); + delete [] prevBuf; + } + if (readBlock(curBuf + allocated, blockSize) != blockSize) { + delete [] curBuf; + return *this; + } + allocated += blockSize; + } while (allocated < len); + + s = curBuf; + s[len] = '\0'; + l = (uint)len; + return *this; +} + +/*! + Reads at most \a len bytes from the stream into \a s and returns the number of + bytes read. If an error occurs, this function returns -1. + + The buffer \a s must be preallocated. The data is \e not encoded. + + \sa readBytes(), QIODevice::read(), writeRawData() +*/ + +int QDataStream::readRawData(char *s, int len) +{ + CHECK_STREAM_PRECOND(-1) + return readBlock(s, len); +} + + +/***************************************************************************** + QDataStream write functions + *****************************************************************************/ + +/*! + \fn QDataStream &QDataStream::operator<<(std::nullptr_t ptr) + \since 5.9 + \overload + + Simulates writing a \c{std::nullptr_t}, \a ptr, to the stream and returns a + reference to the stream. This function does not actually write anything to + the stream, as \c{std::nullptr_t} values are stored as 0 bytes. +*/ + +/*! + \fn QDataStream &QDataStream::operator<<(quint8 i) + \overload + + Writes an unsigned byte, \a i, to the stream and returns a + reference to the stream. +*/ + +/*! + Writes a signed byte, \a i, to the stream and returns a reference + to the stream. +*/ + +QDataStream &QDataStream::operator<<(qint8 i) +{ + CHECK_STREAM_WRITE_PRECOND(*this) + if (!dev->putChar(i)) + q_status = WriteFailed; + return *this; +} + + +/*! + \fn QDataStream &QDataStream::operator<<(quint16 i) + \overload + + Writes an unsigned 16-bit integer, \a i, to the stream and returns + a reference to the stream. +*/ + +/*! + \overload + + Writes a signed 16-bit integer, \a i, to the stream and returns a + reference to the stream. +*/ + +QDataStream &QDataStream::operator<<(qint16 i) +{ + CHECK_STREAM_WRITE_PRECOND(*this) + if (!noswap) { + i = qbswap(i); + } + if (dev->write((char *)&i, sizeof(qint16)) != sizeof(qint16)) + q_status = WriteFailed; + return *this; +} + +/*! + \overload + + Writes a signed 32-bit integer, \a i, to the stream and returns a + reference to the stream. +*/ + +QDataStream &QDataStream::operator<<(qint32 i) +{ + CHECK_STREAM_WRITE_PRECOND(*this) + if (!noswap) { + i = qbswap(i); + } + if (dev->write((char *)&i, sizeof(qint32)) != sizeof(qint32)) + q_status = WriteFailed; + return *this; +} + +/*! + \fn QDataStream &QDataStream::operator<<(quint64 i) + \overload + + Writes an unsigned 64-bit integer, \a i, to the stream and returns a + reference to the stream. +*/ + +/*! + \overload + + Writes a signed 64-bit integer, \a i, to the stream and returns a + reference to the stream. +*/ + +QDataStream &QDataStream::operator<<(qint64 i) +{ + CHECK_STREAM_WRITE_PRECOND(*this) + if (version() < 6) { + quint32 i1 = i & 0xffffffff; + quint32 i2 = i >> 32; + *this << i2 << i1; + } else { + if (!noswap) { + i = qbswap(i); + } + if (dev->write((char *)&i, sizeof(qint64)) != sizeof(qint64)) + q_status = WriteFailed; + } + return *this; +} + +/*! + \fn QDataStream &QDataStream::operator<<(quint32 i) + \overload + + Writes an unsigned integer, \a i, to the stream as a 32-bit + unsigned integer (quint32). Returns a reference to the stream. +*/ + +/*! + Writes a boolean value, \a i, to the stream. Returns a reference + to the stream. +*/ + +QDataStream &QDataStream::operator<<(bool i) +{ + CHECK_STREAM_WRITE_PRECOND(*this) + if (!dev->putChar(qint8(i))) + q_status = WriteFailed; + return *this; +} + +/*! + \overload + + Writes a floating point number, \a f, to the stream using + the standard IEEE 754 format. Returns a reference to the stream. + + \sa setFloatingPointPrecision() +*/ + +QDataStream &QDataStream::operator<<(float f) +{ + if (version() >= QDataStream::Qt_4_6 + && floatingPointPrecision() == QDataStream::DoublePrecision) { + *this << double(f); + return *this; + } + + CHECK_STREAM_WRITE_PRECOND(*this) + float g = f; // fixes float-on-stack problem + if (!noswap) { + union { + float val1; + quint32 val2; + } x; + x.val1 = g; + x.val2 = qbswap(x.val2); + + if (dev->write((char *)&x.val2, sizeof(float)) != sizeof(float)) + q_status = WriteFailed; + return *this; + } + + if (dev->write((char *)&g, sizeof(float)) != sizeof(float)) + q_status = WriteFailed; + return *this; +} + + +/*! + \overload + + Writes a floating point number, \a f, to the stream using + the standard IEEE 754 format. Returns a reference to the stream. + + \sa setFloatingPointPrecision() +*/ + +QDataStream &QDataStream::operator<<(double f) +{ + if (version() >= QDataStream::Qt_4_6 + && floatingPointPrecision() == QDataStream::SinglePrecision) { + *this << float(f); + return *this; + } + + CHECK_STREAM_WRITE_PRECOND(*this) + if (noswap) { + if (dev->write((char *)&f, sizeof(double)) != sizeof(double)) + q_status = WriteFailed; + } else { + union { + double val1; + quint64 val2; + } x; + x.val1 = f; + x.val2 = qbswap(x.val2); + if (dev->write((char *)&x.val2, sizeof(double)) != sizeof(double)) + q_status = WriteFailed; + } + return *this; +} + + +/*! + \fn QDataStream &QDataStream::operator<<(qfloat16 f) + \overload + \since 5.9 + + Writes a floating point number, \a f, to the stream using + the standard IEEE 754 format. Returns a reference to the stream. +*/ +QDataStream &QDataStream::operator<<(qfloat16 f) +{ + return *this << reinterpret_cast(f); +} + +/*! + \overload + + Writes the '\\0'-terminated string \a s to the stream and returns a + reference to the stream. + + The string is serialized using \c{writeBytes()}. + + \sa writeBytes(), writeRawData() +*/ + +QDataStream &QDataStream::operator<<(const char *s) +{ + if (!s) { + *this << (quint32)0; + return *this; + } + uint len = qstrlen(s) + 1; // also write null terminator + *this << (quint32)len; // write length specifier + writeRawData(s, len); + return *this; +} + +/*! + Writes the length specifier \a len and the buffer \a s to the + stream and returns a reference to the stream. + + The \a len is serialized as a quint32, followed by \a len bytes + from \a s. Note that the data is \e not encoded. + + \sa writeRawData(), readBytes() +*/ + +QDataStream &QDataStream::writeBytes(const char *s, uint len) +{ + CHECK_STREAM_WRITE_PRECOND(*this) + *this << (quint32)len; // write length specifier + if (len) + writeRawData(s, len); + return *this; +} + + +/*! + Writes \a len bytes from \a s to the stream. Returns the + number of bytes actually written, or -1 on error. + The data is \e not encoded. + + \sa writeBytes(), QIODevice::write(), readRawData() +*/ + +int QDataStream::writeRawData(const char *s, int len) +{ + CHECK_STREAM_WRITE_PRECOND(-1) + int ret = dev->write(s, len); + if (ret != len) + q_status = WriteFailed; + return ret; +} + +/*! + \since 4.1 + + Skips \a len bytes from the device. Returns the number of bytes + actually skipped, or -1 on error. + + This is equivalent to calling readRawData() on a buffer of length + \a len and ignoring the buffer. + + \sa QIODevice::seek() +*/ +int QDataStream::skipRawData(int len) +{ + CHECK_STREAM_PRECOND(-1) + if (q_status != Ok && dev->isTransactionStarted()) + return -1; + + const int skipResult = dev->skip(len); + if (skipResult != len) + setStatus(ReadPastEnd); + return skipResult; +} + +QT_END_NAMESPACE + +#endif // QT_NO_DATASTREAM diff --git a/src/corelib/serialization/qdatastream.h b/src/corelib/serialization/qdatastream.h new file mode 100644 index 0000000000..1f1b13686c --- /dev/null +++ b/src/corelib/serialization/qdatastream.h @@ -0,0 +1,466 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDATASTREAM_H +#define QDATASTREAM_H + +#include +#include +#include + +#ifdef Status +#error qdatastream.h must be included before any header file that defines Status +#endif + +QT_BEGIN_NAMESPACE + +class qfloat16; +class QByteArray; +class QIODevice; + +template class QList; +template class QLinkedList; +template class QVector; +template class QSet; +template class QHash; +template class QMap; + +#if !defined(QT_NO_DATASTREAM) || defined(QT_BOOTSTRAPPED) +class QDataStreamPrivate; +namespace QtPrivate { +class StreamStateSaver; +} +class Q_CORE_EXPORT QDataStream +{ +public: + enum Version { + Qt_1_0 = 1, + Qt_2_0 = 2, + Qt_2_1 = 3, + Qt_3_0 = 4, + Qt_3_1 = 5, + Qt_3_3 = 6, + Qt_4_0 = 7, + Qt_4_1 = Qt_4_0, + Qt_4_2 = 8, + Qt_4_3 = 9, + Qt_4_4 = 10, + Qt_4_5 = 11, + Qt_4_6 = 12, + Qt_4_7 = Qt_4_6, + Qt_4_8 = Qt_4_7, + Qt_4_9 = Qt_4_8, + Qt_5_0 = 13, + Qt_5_1 = 14, + Qt_5_2 = 15, + Qt_5_3 = Qt_5_2, + Qt_5_4 = 16, + Qt_5_5 = Qt_5_4, + Qt_5_6 = 17, + Qt_5_7 = Qt_5_6, + Qt_5_8 = Qt_5_7, + Qt_5_9 = Qt_5_8, + Qt_5_10 = Qt_5_9, + Qt_5_11 = Qt_5_10, +#if QT_VERSION >= 0x050c00 +#error Add the datastream version for this Qt version and update Qt_DefaultCompiledVersion +#endif + Qt_DefaultCompiledVersion = Qt_5_11 + }; + + enum ByteOrder { + BigEndian = QSysInfo::BigEndian, + LittleEndian = QSysInfo::LittleEndian + }; + + enum Status { + Ok, + ReadPastEnd, + ReadCorruptData, + WriteFailed + }; + + enum FloatingPointPrecision { + SinglePrecision, + DoublePrecision + }; + + QDataStream(); + explicit QDataStream(QIODevice *); + QDataStream(QByteArray *, QIODevice::OpenMode flags); + QDataStream(const QByteArray &); + ~QDataStream(); + + QIODevice *device() const; + void setDevice(QIODevice *); + void unsetDevice(); + + bool atEnd() const; + + Status status() const; + void setStatus(Status status); + void resetStatus(); + + FloatingPointPrecision floatingPointPrecision() const; + void setFloatingPointPrecision(FloatingPointPrecision precision); + + ByteOrder byteOrder() const; + void setByteOrder(ByteOrder); + + int version() const; + void setVersion(int); + + QDataStream &operator>>(qint8 &i); + QDataStream &operator>>(quint8 &i); + QDataStream &operator>>(qint16 &i); + QDataStream &operator>>(quint16 &i); + QDataStream &operator>>(qint32 &i); + QDataStream &operator>>(quint32 &i); + QDataStream &operator>>(qint64 &i); + QDataStream &operator>>(quint64 &i); + QDataStream &operator>>(std::nullptr_t &ptr) { ptr = nullptr; return *this; } + + QDataStream &operator>>(bool &i); + QDataStream &operator>>(qfloat16 &f); + QDataStream &operator>>(float &f); + QDataStream &operator>>(double &f); + QDataStream &operator>>(char *&str); + + QDataStream &operator<<(qint8 i); + QDataStream &operator<<(quint8 i); + QDataStream &operator<<(qint16 i); + QDataStream &operator<<(quint16 i); + QDataStream &operator<<(qint32 i); + QDataStream &operator<<(quint32 i); + QDataStream &operator<<(qint64 i); + QDataStream &operator<<(quint64 i); + QDataStream &operator<<(std::nullptr_t) { return *this; } + QDataStream &operator<<(bool i); + QDataStream &operator<<(qfloat16 f); + QDataStream &operator<<(float f); + QDataStream &operator<<(double f); + QDataStream &operator<<(const char *str); + + QDataStream &readBytes(char *&, uint &len); + int readRawData(char *, int len); + + QDataStream &writeBytes(const char *, uint len); + int writeRawData(const char *, int len); + + int skipRawData(int len); + + void startTransaction(); + bool commitTransaction(); + void rollbackTransaction(); + void abortTransaction(); + +private: + Q_DISABLE_COPY(QDataStream) + + QScopedPointer d; + + QIODevice *dev; + bool owndev; + bool noswap; + ByteOrder byteorder; + int ver; + Status q_status; + + int readBlock(char *data, int len); + friend class QtPrivate::StreamStateSaver; +}; + +namespace QtPrivate { + +class StreamStateSaver +{ +public: + inline StreamStateSaver(QDataStream *s) : stream(s), oldStatus(s->status()) + { + if (!stream->dev || !stream->dev->isTransactionStarted()) + stream->resetStatus(); + } + inline ~StreamStateSaver() + { + if (oldStatus != QDataStream::Ok) { + stream->resetStatus(); + stream->setStatus(oldStatus); + } + } + +private: + QDataStream *stream; + QDataStream::Status oldStatus; +}; + +template +QDataStream &readArrayBasedContainer(QDataStream &s, Container &c) +{ + StreamStateSaver stateSaver(&s); + + c.clear(); + quint32 n; + s >> n; + c.reserve(n); + for (quint32 i = 0; i < n; ++i) { + typename Container::value_type t; + s >> t; + if (s.status() != QDataStream::Ok) { + c.clear(); + break; + } + c.append(t); + } + + return s; +} + +template +QDataStream &readListBasedContainer(QDataStream &s, Container &c) +{ + StreamStateSaver stateSaver(&s); + + c.clear(); + quint32 n; + s >> n; + for (quint32 i = 0; i < n; ++i) { + typename Container::value_type t; + s >> t; + if (s.status() != QDataStream::Ok) { + c.clear(); + break; + } + c << t; + } + + return s; +} + +template +QDataStream &readAssociativeContainer(QDataStream &s, Container &c) +{ + StreamStateSaver stateSaver(&s); + + c.clear(); + quint32 n; + s >> n; + for (quint32 i = 0; i < n; ++i) { + typename Container::key_type k; + typename Container::mapped_type t; + s >> k >> t; + if (s.status() != QDataStream::Ok) { + c.clear(); + break; + } + c.insertMulti(k, t); + } + + return s; +} + +template +QDataStream &writeSequentialContainer(QDataStream &s, const Container &c) +{ + s << quint32(c.size()); + for (const typename Container::value_type &t : c) + s << t; + + return s; +} + +template +QDataStream &writeAssociativeContainer(QDataStream &s, const Container &c) +{ + s << quint32(c.size()); + // Deserialization should occur in the reverse order. + // Otherwise, value() will return the least recently inserted + // value instead of the most recently inserted one. + auto it = c.constEnd(); + auto begin = c.constBegin(); + while (it != begin) { + --it; + s << it.key() << it.value(); + } + + return s; +} + +} // QtPrivate namespace + +/***************************************************************************** + QDataStream inline functions + *****************************************************************************/ + +inline QIODevice *QDataStream::device() const +{ return dev; } + +inline QDataStream::ByteOrder QDataStream::byteOrder() const +{ return byteorder; } + +inline int QDataStream::version() const +{ return ver; } + +inline void QDataStream::setVersion(int v) +{ ver = v; } + +inline QDataStream &QDataStream::operator>>(quint8 &i) +{ return *this >> reinterpret_cast(i); } + +inline QDataStream &QDataStream::operator>>(quint16 &i) +{ return *this >> reinterpret_cast(i); } + +inline QDataStream &QDataStream::operator>>(quint32 &i) +{ return *this >> reinterpret_cast(i); } + +inline QDataStream &QDataStream::operator>>(quint64 &i) +{ return *this >> reinterpret_cast(i); } + +inline QDataStream &QDataStream::operator<<(quint8 i) +{ return *this << qint8(i); } + +inline QDataStream &QDataStream::operator<<(quint16 i) +{ return *this << qint16(i); } + +inline QDataStream &QDataStream::operator<<(quint32 i) +{ return *this << qint32(i); } + +inline QDataStream &QDataStream::operator<<(quint64 i) +{ return *this << qint64(i); } + +template +inline QDataStream &operator<<(QDataStream &s, QFlags e) +{ return s << e.i; } + +template +inline QDataStream &operator>>(QDataStream &s, QFlags &e) +{ return s >> e.i; } + +template +inline QDataStream &operator>>(QDataStream &s, QList &l) +{ + return QtPrivate::readArrayBasedContainer(s, l); +} + +template +inline QDataStream &operator<<(QDataStream &s, const QList &l) +{ + return QtPrivate::writeSequentialContainer(s, l); +} + +template +inline QDataStream &operator>>(QDataStream &s, QLinkedList &l) +{ + return QtPrivate::readListBasedContainer(s, l); +} + +template +inline QDataStream &operator<<(QDataStream &s, const QLinkedList &l) +{ + return QtPrivate::writeSequentialContainer(s, l); +} + +template +inline QDataStream &operator>>(QDataStream &s, QVector &v) +{ + return QtPrivate::readArrayBasedContainer(s, v); +} + +template +inline QDataStream &operator<<(QDataStream &s, const QVector &v) +{ + return QtPrivate::writeSequentialContainer(s, v); +} + +template +inline QDataStream &operator>>(QDataStream &s, QSet &set) +{ + return QtPrivate::readListBasedContainer(s, set); +} + +template +inline QDataStream &operator<<(QDataStream &s, const QSet &set) +{ + return QtPrivate::writeSequentialContainer(s, set); +} + +template +inline QDataStream &operator>>(QDataStream &s, QHash &hash) +{ + return QtPrivate::readAssociativeContainer(s, hash); +} + +template +inline QDataStream &operator<<(QDataStream &s, const QHash &hash) +{ + return QtPrivate::writeAssociativeContainer(s, hash); +} + +template +inline QDataStream &operator>>(QDataStream &s, QMap &map) +{ + return QtPrivate::readAssociativeContainer(s, map); +} + +template +inline QDataStream &operator<<(QDataStream &s, const QMap &map) +{ + return QtPrivate::writeAssociativeContainer(s, map); +} + +#ifndef QT_NO_DATASTREAM +template +inline QDataStream& operator>>(QDataStream& s, QPair& p) +{ + s >> p.first >> p.second; + return s; +} + +template +inline QDataStream& operator<<(QDataStream& s, const QPair& p) +{ + s << p.first << p.second; + return s; +} +#endif + +#endif // QT_NO_DATASTREAM + +QT_END_NAMESPACE + +#endif // QDATASTREAM_H diff --git a/src/corelib/serialization/qdatastream_p.h b/src/corelib/serialization/qdatastream_p.h new file mode 100644 index 0000000000..3ca0ae840e --- /dev/null +++ b/src/corelib/serialization/qdatastream_p.h @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDATASTREAM_P_H +#define QDATASTREAM_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include + +QT_BEGIN_NAMESPACE + +#if !defined(QT_NO_DATASTREAM) || defined(QT_BOOTSTRAPPED) +class QDataStreamPrivate +{ +public: + QDataStreamPrivate() : floatingPointPrecision(QDataStream::DoublePrecision), + transactionDepth(0) { } + + QDataStream::FloatingPointPrecision floatingPointPrecision; + int transactionDepth; +}; +#endif + +QT_END_NAMESPACE + +#endif // QDATASTREAM_P_H diff --git a/src/corelib/serialization/qjson.cpp b/src/corelib/serialization/qjson.cpp new file mode 100644 index 0000000000..e4bca3bcd0 --- /dev/null +++ b/src/corelib/serialization/qjson.cpp @@ -0,0 +1,455 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qjson_p.h" +#include + +QT_BEGIN_NAMESPACE + +namespace QJsonPrivate +{ + +static Q_CONSTEXPR Base emptyArray = { { qle_uint(sizeof(Base)) }, { 0 }, { qle_uint(0) } }; +static Q_CONSTEXPR Base emptyObject = { { qle_uint(sizeof(Base)) }, { 0 }, { qle_uint(0) } }; + +void Data::compact() +{ + Q_ASSERT(sizeof(Value) == sizeof(offset)); + + if (!compactionCounter) + return; + + Base *base = header->root(); + int reserve = 0; + if (base->is_object) { + Object *o = static_cast(base); + for (int i = 0; i < (int)o->length; ++i) + reserve += o->entryAt(i)->usedStorage(o); + } else { + Array *a = static_cast(base); + for (int i = 0; i < (int)a->length; ++i) + reserve += (*a)[i].usedStorage(a); + } + + int size = sizeof(Base) + reserve + base->length*sizeof(offset); + int alloc = sizeof(Header) + size; + Header *h = (Header *) malloc(alloc); + h->tag = QJsonDocument::BinaryFormatTag; + h->version = 1; + Base *b = h->root(); + b->size = size; + b->is_object = header->root()->is_object; + b->length = base->length; + b->tableOffset = reserve + sizeof(Array); + + int offset = sizeof(Base); + if (b->is_object) { + Object *o = static_cast(base); + Object *no = static_cast(b); + + for (int i = 0; i < (int)o->length; ++i) { + no->table()[i] = offset; + + const Entry *e = o->entryAt(i); + Entry *ne = no->entryAt(i); + int s = e->size(); + memcpy(ne, e, s); + offset += s; + int dataSize = e->value.usedStorage(o); + if (dataSize) { + memcpy((char *)no + offset, e->value.data(o), dataSize); + ne->value.value = offset; + offset += dataSize; + } + } + } else { + Array *a = static_cast(base); + Array *na = static_cast(b); + + for (int i = 0; i < (int)a->length; ++i) { + const Value &v = (*a)[i]; + Value &nv = (*na)[i]; + nv = v; + int dataSize = v.usedStorage(a); + if (dataSize) { + memcpy((char *)na + offset, v.data(a), dataSize); + nv.value = offset; + offset += dataSize; + } + } + } + Q_ASSERT(offset == (int)b->tableOffset); + + free(header); + header = h; + this->alloc = alloc; + compactionCounter = 0; +} + +bool Data::valid() const +{ + if (header->tag != QJsonDocument::BinaryFormatTag || header->version != 1u) + return false; + + bool res = false; + Base *root = header->root(); + int maxSize = alloc - sizeof(Header); + if (root->is_object) + res = static_cast(root)->isValid(maxSize); + else + res = static_cast(root)->isValid(maxSize); + + return res; +} + + +int Base::reserveSpace(uint dataSize, int posInTable, uint numItems, bool replace) +{ + Q_ASSERT(posInTable >= 0 && posInTable <= (int)length); + if (size + dataSize >= Value::MaxSize) { + qWarning("QJson: Document too large to store in data structure %d %d %d", (uint)size, dataSize, Value::MaxSize); + return 0; + } + + offset off = tableOffset; + // move table to new position + if (replace) { + memmove((char *)(table()) + dataSize, table(), length*sizeof(offset)); + } else { + memmove((char *)(table() + posInTable + numItems) + dataSize, table() + posInTable, (length - posInTable)*sizeof(offset)); + memmove((char *)(table()) + dataSize, table(), posInTable*sizeof(offset)); + } + tableOffset += dataSize; + for (int i = 0; i < (int)numItems; ++i) + table()[posInTable + i] = off; + size += dataSize; + if (!replace) { + length += numItems; + size += numItems * sizeof(offset); + } + return off; +} + +void Base::removeItems(int pos, int numItems) +{ + Q_ASSERT(pos >= 0 && pos <= (int)length); + if (pos + numItems < (int)length) + memmove(table() + pos, table() + pos + numItems, (length - pos - numItems)*sizeof(offset)); + length -= numItems; +} + +int Object::indexOf(const QString &key, bool *exists) const +{ + int min = 0; + int n = length; + while (n > 0) { + int half = n >> 1; + int middle = min + half; + if (*entryAt(middle) >= key) { + n = half; + } else { + min = middle + 1; + n -= half + 1; + } + } + if (min < (int)length && *entryAt(min) == key) { + *exists = true; + return min; + } + *exists = false; + return min; +} + +int Object::indexOf(QLatin1String key, bool *exists) const +{ + int min = 0; + int n = length; + while (n > 0) { + int half = n >> 1; + int middle = min + half; + if (*entryAt(middle) >= key) { + n = half; + } else { + min = middle + 1; + n -= half + 1; + } + } + if (min < (int)length && *entryAt(min) == key) { + *exists = true; + return min; + } + *exists = false; + return min; +} + +bool Object::isValid(int maxSize) const +{ + if (size > (uint)maxSize || tableOffset + length*sizeof(offset) > size) + return false; + + QString lastKey; + for (uint i = 0; i < length; ++i) { + offset entryOffset = table()[i]; + if (entryOffset + sizeof(Entry) >= tableOffset) + return false; + Entry *e = entryAt(i); + if (!e->isValid(tableOffset - table()[i])) + return false; + QString key = e->key(); + if (key < lastKey) + return false; + if (!e->value.isValid(this)) + return false; + lastKey = key; + } + return true; +} + + + +bool Array::isValid(int maxSize) const +{ + if (size > (uint)maxSize || tableOffset + length*sizeof(offset) > size) + return false; + + for (uint i = 0; i < length; ++i) { + if (!at(i).isValid(this)) + return false; + } + return true; +} + + +bool Entry::operator ==(const QString &key) const +{ + if (value.latinKey) + return (shallowLatin1Key() == key); + else + return (shallowKey() == key); +} + +bool Entry::operator==(QLatin1String key) const +{ + if (value.latinKey) + return shallowLatin1Key() == key; + else + return shallowKey() == key; +} + +bool Entry::operator ==(const Entry &other) const +{ + if (value.latinKey) { + if (other.value.latinKey) + return shallowLatin1Key() == other.shallowLatin1Key(); + return shallowLatin1Key() == other.shallowKey(); + } else if (other.value.latinKey) { + return shallowKey() == other.shallowLatin1Key(); + } + return shallowKey() == other.shallowKey(); +} + +bool Entry::operator >=(const Entry &other) const +{ + if (value.latinKey) { + if (other.value.latinKey) + return shallowLatin1Key() >= other.shallowLatin1Key(); + return shallowLatin1Key() >= other.shallowKey(); + } else if (other.value.latinKey) { + return shallowKey() >= other.shallowLatin1Key(); + } + return shallowKey() >= other.shallowKey(); +} + + +int Value::usedStorage(const Base *b) const +{ + int s = 0; + switch (type) { + case QJsonValue::Double: + if (latinOrIntValue) + break; + s = sizeof(double); + break; + case QJsonValue::String: { + char *d = data(b); + if (latinOrIntValue) + s = sizeof(ushort) + qFromLittleEndian(*(ushort *)d); + else + s = sizeof(int) + sizeof(ushort) * qFromLittleEndian(*(int *)d); + break; + } + case QJsonValue::Array: + case QJsonValue::Object: + s = base(b)->size; + break; + case QJsonValue::Null: + case QJsonValue::Bool: + default: + break; + } + return alignedSize(s); +} + +bool Value::isValid(const Base *b) const +{ + int offset = 0; + switch (type) { + case QJsonValue::Double: + if (latinOrIntValue) + break; + Q_FALLTHROUGH(); + case QJsonValue::String: + case QJsonValue::Array: + case QJsonValue::Object: + offset = value; + break; + case QJsonValue::Null: + case QJsonValue::Bool: + default: + break; + } + + if (!offset) + return true; + if (offset + sizeof(uint) > b->tableOffset) + return false; + + int s = usedStorage(b); + if (!s) + return true; + if (s < 0 || s > (int)b->tableOffset - offset) + return false; + if (type == QJsonValue::Array) + return static_cast(base(b))->isValid(s); + if (type == QJsonValue::Object) + return static_cast(base(b))->isValid(s); + return true; +} + +/*! + \internal + */ +int Value::requiredStorage(QJsonValue &v, bool *compressed) +{ + *compressed = false; + switch (v.t) { + case QJsonValue::Double: + if (QJsonPrivate::compressedNumber(v.dbl) != INT_MAX) { + *compressed = true; + return 0; + } + return sizeof(double); + case QJsonValue::String: { + QString s = v.toString(); + *compressed = QJsonPrivate::useCompressed(s); + return QJsonPrivate::qStringSize(s, *compressed); + } + case QJsonValue::Array: + case QJsonValue::Object: + if (v.d && v.d->compactionCounter) { + v.detach(); + v.d->compact(); + v.base = static_cast(v.d->header->root()); + } + return v.base ? uint(v.base->size) : sizeof(QJsonPrivate::Base); + case QJsonValue::Undefined: + case QJsonValue::Null: + case QJsonValue::Bool: + break; + } + return 0; +} + +/*! + \internal + */ +uint Value::valueToStore(const QJsonValue &v, uint offset) +{ + switch (v.t) { + case QJsonValue::Undefined: + case QJsonValue::Null: + break; + case QJsonValue::Bool: + return v.b; + case QJsonValue::Double: { + int c = QJsonPrivate::compressedNumber(v.dbl); + if (c != INT_MAX) + return c; + } + Q_FALLTHROUGH(); + case QJsonValue::String: + case QJsonValue::Array: + case QJsonValue::Object: + return offset; + } + return 0; +} + +/*! + \internal + */ +void Value::copyData(const QJsonValue &v, char *dest, bool compressed) +{ + switch (v.t) { + case QJsonValue::Double: + if (!compressed) { + qToLittleEndian(v.ui, dest); + } + break; + case QJsonValue::String: { + QString str = v.toString(); + QJsonPrivate::copyString(dest, str, compressed); + break; + } + case QJsonValue::Array: + case QJsonValue::Object: { + const QJsonPrivate::Base *b = v.base; + if (!b) + b = (v.t == QJsonValue::Array ? &emptyArray : &emptyObject); + memcpy(dest, b, b->size); + break; + } + default: + break; + } +} + +} // namespace QJsonPrivate + +QT_END_NAMESPACE diff --git a/src/corelib/serialization/qjson_p.h b/src/corelib/serialization/qjson_p.h new file mode 100644 index 0000000000..131528376b --- /dev/null +++ b/src/corelib/serialization/qjson_p.h @@ -0,0 +1,781 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2016 Intel Corporation. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QJSON_P_H +#define QJSON_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "private/qendian_p.h" +#include "private/qsimd_p.h" + +#include +#include + +QT_BEGIN_NAMESPACE + +/* + This defines a binary data structure for Json data. The data structure is optimised for fast reading + and minimum allocations. The whole data structure can be mmap'ed and used directly. + + In most cases the binary structure is not as space efficient as a utf8 encoded text representation, but + much faster to access. + + The size requirements are: + + String: + Latin1 data: 2 bytes header + string.length() + Full Unicode: 4 bytes header + 2*(string.length()) + + Values: 4 bytes + size of data (size can be 0 for some data) + bool: 0 bytes + double: 8 bytes (0 if integer with less than 27bits) + string: see above + array: size of array + object: size of object + Array: 12 bytes + 4*length + size of Value data + Object: 12 bytes + 8*length + size of Key Strings + size of Value data + + For an example such as + + { // object: 12 + 5*8 = 52 + "firstName": "John", // key 12, value 8 = 20 + "lastName" : "Smith", // key 12, value 8 = 20 + "age" : 25, // key 8, value 0 = 8 + "address" : // key 12, object below = 140 + { // object: 12 + 4*8 + "streetAddress": "21 2nd Street", // key 16, value 16 + "city" : "New York", // key 8, value 12 + "state" : "NY", // key 8, value 4 + "postalCode" : "10021" // key 12, value 8 + }, // object total: 128 + "phoneNumber": // key: 16, value array below = 172 + [ // array: 12 + 2*4 + values below: 156 + { // object 12 + 2*8 + "type" : "home", // key 8, value 8 + "number": "212 555-1234" // key 8, value 16 + }, // object total: 68 + { // object 12 + 2*8 + "type" : "fax", // key 8, value 8 + "number": "646 555-4567" // key 8, value 16 + } // object total: 68 + ] // array total: 156 + } // great total: 412 bytes + + The uncompressed text file used roughly 500 bytes, so in this case we end up using about + the same space as the text representation. + + Other measurements have shown a slightly bigger binary size than a compact text + representation where all possible whitespace was stripped out. +*/ +#define Q_DECLARE_JSONPRIVATE_TYPEINFO(Class, Flags) } Q_DECLARE_TYPEINFO(QJsonPrivate::Class, Flags); namespace QJsonPrivate { +namespace QJsonPrivate { + +class Array; +class Object; +class Value; +class Entry; + +template +using q_littleendian = QLEInteger; + +typedef q_littleendian qle_short; +typedef q_littleendian qle_ushort; +typedef q_littleendian qle_int; +typedef q_littleendian qle_uint; + +template +using qle_bitfield = QLEIntegerBitfield; + +template +using qle_signedbitfield = QLEIntegerBitfield; + +typedef qle_uint offset; + +// round the size up to the next 4 byte boundary +inline int alignedSize(int size) { return (size + 3) & ~3; } + +static inline bool useCompressed(const QString &s) +{ + if (s.length() >= 0x8000) + return false; + const ushort *uc = (const ushort *)s.constData(); + const ushort *e = uc + s.length(); + while (uc < e) { + if (*uc > 0xff) + return false; + ++uc; + } + return true; +} + +static inline int qStringSize(const QString &string, bool compress) +{ + int l = 2 + string.length(); + if (!compress) + l *= 2; + return alignedSize(l); +} + +// returns INT_MAX if it can't compress it into 28 bits +static inline int compressedNumber(double d) +{ + // this relies on details of how ieee floats are represented + const int exponent_off = 52; + const quint64 fraction_mask = 0x000fffffffffffffull; + const quint64 exponent_mask = 0x7ff0000000000000ull; + + quint64 val; + memcpy (&val, &d, sizeof(double)); + int exp = (int)((val & exponent_mask) >> exponent_off) - 1023; + if (exp < 0 || exp > 25) + return INT_MAX; + + quint64 non_int = val & (fraction_mask >> exp); + if (non_int) + return INT_MAX; + + bool neg = (val >> 63) != 0; + val &= fraction_mask; + val |= ((quint64)1 << 52); + int res = (int)(val >> (52 - exp)); + return neg ? -res : res; +} + +class Latin1String; + +class String +{ +public: + explicit String(const char *data) { d = (Data *)data; } + + struct Data { + qle_uint length; + qle_ushort utf16[1]; + }; + + Data *d; + + int byteSize() const { return sizeof(uint) + sizeof(ushort) * d->length; } + bool isValid(int maxSize) const { + // Check byteSize() <= maxSize, avoiding integer overflow + maxSize -= sizeof(uint); + return maxSize >= 0 && uint(d->length) <= maxSize / sizeof(ushort); + } + + inline String &operator=(const QString &str) + { + d->length = str.length(); +#if Q_BYTE_ORDER == Q_BIG_ENDIAN + const ushort *uc = (const ushort *)str.unicode(); + for (int i = 0; i < str.length(); ++i) + d->utf16[i] = uc[i]; +#else + memcpy(d->utf16, str.unicode(), str.length()*sizeof(ushort)); +#endif + if (str.length() & 1) + d->utf16[str.length()] = 0; + return *this; + } + + inline bool operator ==(const QString &str) const { + int slen = str.length(); + int l = d->length; + if (slen != l) + return false; + const ushort *s = (const ushort *)str.constData(); + const qle_ushort *a = d->utf16; + const ushort *b = s; + while (l-- && *a == *b) + a++,b++; + return (l == -1); + } + inline bool operator !=(const QString &str) const { + return !operator ==(str); + } + inline bool operator >=(const QString &str) const { + // ### + return toString() >= str; + } + + inline bool operator<(const Latin1String &str) const; + inline bool operator>=(const Latin1String &str) const { return !operator <(str); } + inline bool operator ==(const Latin1String &str) const; + + inline bool operator ==(const String &str) const { + if (d->length != str.d->length) + return false; + return !memcmp(d->utf16, str.d->utf16, d->length*sizeof(ushort)); + } + inline bool operator<(const String &other) const; + inline bool operator >=(const String &other) const { return !(*this < other); } + + inline QString toString() const { +#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN + return QString((QChar *)d->utf16, d->length); +#else + int l = d->length; + QString str(l, Qt::Uninitialized); + QChar *ch = str.data(); + for (int i = 0; i < l; ++i) + ch[i] = QChar(d->utf16[i]); + return str; +#endif + } + +}; + +class Latin1String +{ +public: + explicit Latin1String(const char *data) { d = (Data *)data; } + + struct Data { + qle_ushort length; + char latin1[1]; + }; + Data *d; + + int byteSize() const { return sizeof(ushort) + sizeof(char)*(d->length); } + bool isValid(int maxSize) const { + return byteSize() <= maxSize; + } + + inline Latin1String &operator=(const QString &str) + { + int len = d->length = str.length(); + uchar *l = (uchar *)d->latin1; + const ushort *uc = (const ushort *)str.unicode(); + int i = 0; +#ifdef __SSE2__ + for ( ; i + 16 <= len; i += 16) { + __m128i chunk1 = _mm_loadu_si128((__m128i*)&uc[i]); // load + __m128i chunk2 = _mm_loadu_si128((__m128i*)&uc[i + 8]); // load + // pack the two vector to 16 x 8bits elements + const __m128i result = _mm_packus_epi16(chunk1, chunk2); + _mm_storeu_si128((__m128i*)&l[i], result); // store + } +# ifdef Q_PROCESSOR_X86_64 + // we can do one more round, of 8 characters + if (i + 8 <= len) { + __m128i chunk = _mm_loadu_si128((__m128i*)&uc[i]); // load + // pack with itself, we'll discard the high part anyway + chunk = _mm_packus_epi16(chunk, chunk); + // unaligned 64-bit store + qToUnaligned(_mm_cvtsi128_si64(chunk), l + i); + i += 8; + } +# endif +#endif + for ( ; i < len; ++i) + l[i] = uc[i]; + for ( ; (quintptr)(l+i) & 0x3; ++i) + l[i] = 0; + return *this; + } + + QLatin1String toQLatin1String() const Q_DECL_NOTHROW { + return QLatin1String(d->latin1, d->length); + } + + inline bool operator<(const String &str) const + { + const qle_ushort *uc = (qle_ushort *) str.d->utf16; + if (!uc || *uc == 0) + return false; + + const uchar *c = (uchar *)d->latin1; + const uchar *e = c + qMin((int)d->length, (int)str.d->length); + + while (c < e) { + if (*c != *uc) + break; + ++c; + ++uc; + } + return (c == e ? (int)d->length < (int)str.d->length : *c < *uc); + + } + inline bool operator ==(const String &str) const { + return (str == *this); + } + inline bool operator >=(const String &str) const { + return !(*this < str); + } + + inline QString toString() const { + return QString::fromLatin1(d->latin1, d->length); + } +}; + +#define DEF_OP(op) \ + inline bool operator op(Latin1String lhs, Latin1String rhs) Q_DECL_NOTHROW \ + { \ + return lhs.toQLatin1String() op rhs.toQLatin1String(); \ + } \ + inline bool operator op(QLatin1String lhs, Latin1String rhs) Q_DECL_NOTHROW \ + { \ + return lhs op rhs.toQLatin1String(); \ + } \ + inline bool operator op(Latin1String lhs, QLatin1String rhs) Q_DECL_NOTHROW \ + { \ + return lhs.toQLatin1String() op rhs; \ + } \ + inline bool operator op(const QString &lhs, Latin1String rhs) Q_DECL_NOTHROW \ + { \ + return lhs op rhs.toQLatin1String(); \ + } \ + inline bool operator op(Latin1String lhs, const QString &rhs) Q_DECL_NOTHROW \ + { \ + return lhs.toQLatin1String() op rhs; \ + } \ + /*end*/ +DEF_OP(==) +DEF_OP(!=) +DEF_OP(< ) +DEF_OP(> ) +DEF_OP(<=) +DEF_OP(>=) +#undef DEF_OP + +inline bool String::operator ==(const Latin1String &str) const +{ + if ((int)d->length != (int)str.d->length) + return false; + const qle_ushort *uc = d->utf16; + const qle_ushort *e = uc + d->length; + const uchar *c = (uchar *)str.d->latin1; + + while (uc < e) { + if (*uc != *c) + return false; + ++uc; + ++c; + } + return true; +} + +inline bool String::operator <(const String &other) const +{ + int alen = d->length; + int blen = other.d->length; + int l = qMin(alen, blen); + qle_ushort *a = d->utf16; + qle_ushort *b = other.d->utf16; + + while (l-- && *a == *b) + a++,b++; + if (l==-1) + return (alen < blen); + return (ushort)*a < (ushort)*b; +} + +inline bool String::operator<(const Latin1String &str) const +{ + const uchar *c = (uchar *) str.d->latin1; + if (!c || *c == 0) + return false; + + const qle_ushort *uc = d->utf16; + const qle_ushort *e = uc + qMin((int)d->length, (int)str.d->length); + + while (uc < e) { + if (*uc != *c) + break; + ++uc; + ++c; + } + return (uc == e ? (int)d->length < (int)str.d->length : (ushort)*uc < *c); + +} + +static inline void copyString(char *dest, const QString &str, bool compress) +{ + if (compress) { + Latin1String string(dest); + string = str; + } else { + String string(dest); + string = str; + } +} + + +/* + Base is the base class for both Object and Array. Both classe work more or less the same way. + The class starts with a header (defined by the struct below), then followed by data (the data for + values in the Array case and Entry's (see below) for objects. + + After the data a table follows (tableOffset points to it) containing Value objects for Arrays, and + offsets from the beginning of the object to Entry's in the case of Object. + + Entry's in the Object's table are lexicographically sorted by key in the table(). This allows the usage + of a binary search over the keys in an Object. + */ +class Base +{ +public: + qle_uint size; + union { + uint _dummy; + qle_bitfield<0, 1> is_object; + qle_bitfield<1, 31> length; + }; + offset tableOffset; + // content follows here + + inline bool isObject() const { return !!is_object; } + inline bool isArray() const { return !isObject(); } + + inline offset *table() const { return (offset *) (((char *) this) + tableOffset); } + + int reserveSpace(uint dataSize, int posInTable, uint numItems, bool replace); + void removeItems(int pos, int numItems); +}; + +class Object : public Base +{ +public: + Entry *entryAt(int i) const { + return reinterpret_cast(((char *)this) + table()[i]); + } + int indexOf(const QString &key, bool *exists) const; + int indexOf(QLatin1String key, bool *exists) const; + + bool isValid(int maxSize) const; +}; + + +class Array : public Base +{ +public: + inline Value at(int i) const; + inline Value &operator [](int i); + + bool isValid(int maxSize) const; +}; + + +class Value +{ +public: + enum { + MaxSize = (1<<27) - 1 + }; + union { + uint _dummy; + qle_bitfield<0, 3> type; + qle_bitfield<3, 1> latinOrIntValue; + qle_bitfield<4, 1> latinKey; + qle_bitfield<5, 27> value; + qle_signedbitfield<5, 27> int_value; + }; + + inline char *data(const Base *b) const { return ((char *)b) + value; } + int usedStorage(const Base *b) const; + + bool toBoolean() const; + double toDouble(const Base *b) const; + QString toString(const Base *b) const; + String asString(const Base *b) const; + Latin1String asLatin1String(const Base *b) const; + Base *base(const Base *b) const; + + bool isValid(const Base *b) const; + + static int requiredStorage(QJsonValue &v, bool *compressed); + static uint valueToStore(const QJsonValue &v, uint offset); + static void copyData(const QJsonValue &v, char *dest, bool compressed); +}; +Q_DECLARE_JSONPRIVATE_TYPEINFO(Value, Q_PRIMITIVE_TYPE) + +inline Value Array::at(int i) const +{ + return *(Value *) (table() + i); +} + +inline Value &Array::operator [](int i) +{ + return *(Value *) (table() + i); +} + + + +class Entry { +public: + Value value; + // key + // value data follows key + + uint size() const { + int s = sizeof(Entry); + if (value.latinKey) + s += shallowLatin1Key().byteSize(); + else + s += shallowKey().byteSize(); + return alignedSize(s); + } + + int usedStorage(Base *b) const { + return size() + value.usedStorage(b); + } + + String shallowKey() const + { + Q_ASSERT(!value.latinKey); + return String((const char *)this + sizeof(Entry)); + } + Latin1String shallowLatin1Key() const + { + Q_ASSERT(value.latinKey); + return Latin1String((const char *)this + sizeof(Entry)); + } + QString key() const + { + if (value.latinKey) { + return shallowLatin1Key().toString(); + } + return shallowKey().toString(); + } + + bool isValid(int maxSize) const { + if (maxSize < (int)sizeof(Entry)) + return false; + maxSize -= sizeof(Entry); + if (value.latinKey) + return shallowLatin1Key().isValid(maxSize); + return shallowKey().isValid(maxSize); + } + + bool operator ==(const QString &key) const; + inline bool operator !=(const QString &key) const { return !operator ==(key); } + inline bool operator >=(const QString &key) const; + + bool operator==(QLatin1String key) const; + inline bool operator!=(QLatin1String key) const { return !operator ==(key); } + inline bool operator>=(QLatin1String key) const; + + bool operator ==(const Entry &other) const; + bool operator >=(const Entry &other) const; +}; + +inline bool Entry::operator >=(const QString &key) const +{ + if (value.latinKey) + return (shallowLatin1Key() >= key); + else + return (shallowKey() >= key); +} + +inline bool Entry::operator >=(QLatin1String key) const +{ + if (value.latinKey) + return shallowLatin1Key() >= key; + else + return shallowKey() >= key; +} + +inline bool operator <(const QString &key, const Entry &e) +{ return e >= key; } + +inline bool operator<(QLatin1String key, const Entry &e) +{ return e >= key; } + + +class Header { +public: + qle_uint tag; // 'qbjs' + qle_uint version; // 1 + Base *root() { return (Base *)(this + 1); } +}; + + +inline bool Value::toBoolean() const +{ + Q_ASSERT(type == QJsonValue::Bool); + return value != 0; +} + +inline double Value::toDouble(const Base *b) const +{ + Q_ASSERT(type == QJsonValue::Double); + if (latinOrIntValue) + return int_value; + + quint64 i = qFromLittleEndian((const uchar *)b + value); + double d; + memcpy(&d, &i, sizeof(double)); + return d; +} + +inline String Value::asString(const Base *b) const +{ + Q_ASSERT(type == QJsonValue::String && !latinOrIntValue); + return String(data(b)); +} + +inline Latin1String Value::asLatin1String(const Base *b) const +{ + Q_ASSERT(type == QJsonValue::String && latinOrIntValue); + return Latin1String(data(b)); +} + +inline QString Value::toString(const Base *b) const +{ + if (latinOrIntValue) + return asLatin1String(b).toString(); + else + return asString(b).toString(); +} + +inline Base *Value::base(const Base *b) const +{ + Q_ASSERT(type == QJsonValue::Array || type == QJsonValue::Object); + return reinterpret_cast(data(b)); +} + +class Data { +public: + enum Validation { + Unchecked, + Validated, + Invalid + }; + + QAtomicInt ref; + int alloc; + union { + char *rawData; + Header *header; + }; + uint compactionCounter : 31; + uint ownsData : 1; + + inline Data(char *raw, int a) + : alloc(a), rawData(raw), compactionCounter(0), ownsData(true) + { + } + inline Data(int reserved, QJsonValue::Type valueType) + : rawData(0), compactionCounter(0), ownsData(true) + { + Q_ASSERT(valueType == QJsonValue::Array || valueType == QJsonValue::Object); + + alloc = sizeof(Header) + sizeof(Base) + reserved + sizeof(offset); + header = (Header *)malloc(alloc); + Q_CHECK_PTR(header); + header->tag = QJsonDocument::BinaryFormatTag; + header->version = 1; + Base *b = header->root(); + b->size = sizeof(Base); + b->is_object = (valueType == QJsonValue::Object); + b->tableOffset = sizeof(Base); + b->length = 0; + } + inline ~Data() + { if (ownsData) free(rawData); } + + uint offsetOf(const void *ptr) const { return (uint)(((char *)ptr - rawData)); } + + QJsonObject toObject(Object *o) const + { + return QJsonObject(const_cast(this), o); + } + + QJsonArray toArray(Array *a) const + { + return QJsonArray(const_cast(this), a); + } + + Data *clone(Base *b, int reserve = 0) + { + int size = sizeof(Header) + b->size; + if (b == header->root() && ref.load() == 1 && alloc >= size + reserve) + return this; + + if (reserve) { + if (reserve < 128) + reserve = 128; + size = qMax(size + reserve, qMin(size *2, (int)Value::MaxSize)); + if (size > Value::MaxSize) { + qWarning("QJson: Document too large to store in data structure"); + return 0; + } + } + char *raw = (char *)malloc(size); + Q_CHECK_PTR(raw); + memcpy(raw + sizeof(Header), b, b->size); + Header *h = (Header *)raw; + h->tag = QJsonDocument::BinaryFormatTag; + h->version = 1; + Data *d = new Data(raw, size); + d->compactionCounter = (b == header->root()) ? compactionCounter : 0; + return d; + } + + void compact(); + bool valid() const; + +private: + Q_DISABLE_COPY(Data) +}; + +} + +QT_END_NAMESPACE + +#endif // QJSON_P_H diff --git a/src/corelib/serialization/qjsonarray.cpp b/src/corelib/serialization/qjsonarray.cpp new file mode 100644 index 0000000000..c5a5aaf39d --- /dev/null +++ b/src/corelib/serialization/qjsonarray.cpp @@ -0,0 +1,1258 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include +#include +#include + +#include "qjsonwriter_p.h" +#include "qjson_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QJsonArray + \inmodule QtCore + \ingroup json + \ingroup shared + \reentrant + \since 5.0 + + \brief The QJsonArray class encapsulates a JSON array. + + A JSON array is a list of values. The list can be manipulated by inserting and + removing QJsonValue's from the array. + + A QJsonArray can be converted to and from a QVariantList. You can query the + number of entries with size(), insert(), and removeAt() entries from it + and iterate over its content using the standard C++ iterator pattern. + + QJsonArray is an implicitly shared class and shares the data with the document + it has been created from as long as it is not being modified. + + You can convert the array to and from text based JSON through QJsonDocument. + + \sa {JSON Support in Qt}, {JSON Save Game Example} +*/ + +/*! + \typedef QJsonArray::Iterator + + Qt-style synonym for QJsonArray::iterator. +*/ + +/*! + \typedef QJsonArray::ConstIterator + + Qt-style synonym for QJsonArray::const_iterator. +*/ + +/*! + \typedef QJsonArray::size_type + + Typedef for int. Provided for STL compatibility. +*/ + +/*! + \typedef QJsonArray::value_type + + Typedef for QJsonValue. Provided for STL compatibility. +*/ + +/*! + \typedef QJsonArray::difference_type + + Typedef for int. Provided for STL compatibility. +*/ + +/*! + \typedef QJsonArray::pointer + + Typedef for QJsonValue *. Provided for STL compatibility. +*/ + +/*! + \typedef QJsonArray::const_pointer + + Typedef for const QJsonValue *. Provided for STL compatibility. +*/ + +/*! + \typedef QJsonArray::reference + + Typedef for QJsonValue &. Provided for STL compatibility. +*/ + +/*! + \typedef QJsonArray::const_reference + + Typedef for const QJsonValue &. Provided for STL compatibility. +*/ + +/*! + Creates an empty array. + */ +QJsonArray::QJsonArray() + : d(0), a(0) +{ +} + +/*! + \fn QJsonArray::QJsonArray(std::initializer_list args) + \since 5.4 + Creates an array initialized from \a args initialization list. + + QJsonArray can be constructed in a way similar to JSON notation, + for example: + \code + QJsonArray array = { 1, 2.2, QString() }; + \endcode + */ + +/*! + \internal + */ +QJsonArray::QJsonArray(QJsonPrivate::Data *data, QJsonPrivate::Array *array) + : d(data), a(array) +{ + Q_ASSERT(data); + Q_ASSERT(array); + d->ref.ref(); +} + +/*! + This method replaces part of QJsonArray(std::initializer_list args) . + The constructor needs to be inline, but we do not want to leak implementation details + of this class. + \note this method is called for an uninitialized object + \internal + */ +void QJsonArray::initialize() +{ + d = 0; + a = 0; +} + +/*! + Deletes the array. + */ +QJsonArray::~QJsonArray() +{ + if (d && !d->ref.deref()) + delete d; +} + +/*! + Creates a copy of \a other. + + Since QJsonArray is implicitly shared, the copy is shallow + as long as the object doesn't get modified. + */ +QJsonArray::QJsonArray(const QJsonArray &other) +{ + d = other.d; + a = other.a; + if (d) + d->ref.ref(); +} + +/*! + Assigns \a other to this array. + */ +QJsonArray &QJsonArray::operator =(const QJsonArray &other) +{ + if (d != other.d) { + if (d && !d->ref.deref()) + delete d; + d = other.d; + if (d) + d->ref.ref(); + } + a = other.a; + + return *this; +} + +/*! + \fn QJsonArray::QJsonArray(QJsonArray &&other) + \since 5.10 + + Move-constructs a QJsonArray from \a other. +*/ + +/*! + \fn QJsonArray &QJsonArray::operator =(QJsonArray &&other) + \since 5.10 + + Move-assigns \a other to this array. +*/ + +/*! + \fn void QJsonArray::swap(QJsonArray &other) + \since 5.10 + + Swaps the array \a other with this. This operation is very fast and never fails. +*/ + +/*! \fn QJsonArray &QJsonArray::operator+=(const QJsonValue &value) + + Appends \a value to the array, and returns a reference to the array itself. + + \since 5.3 + \sa append(), operator<<() +*/ + +/*! \fn QJsonArray QJsonArray::operator+(const QJsonValue &value) const + + Returns an array that contains all the items in this array followed + by the provided \a value. + + \since 5.3 + \sa operator+=() +*/ + +/*! \fn QJsonArray &QJsonArray::operator<<(const QJsonValue &value) + + Appends \a value to the array, and returns a reference to the array itself. + + \since 5.3 + \sa operator+=(), append() +*/ + +/*! + Converts the string list \a list to a QJsonArray. + + The values in \a list will be converted to JSON values. + + \sa toVariantList(), QJsonValue::fromVariant() + */ +QJsonArray QJsonArray::fromStringList(const QStringList &list) +{ + QJsonArray array; + for (QStringList::const_iterator it = list.constBegin(); it != list.constEnd(); ++it) + array.append(QJsonValue(*it)); + return array; +} + +/*! + Converts the variant list \a list to a QJsonArray. + + The QVariant values in \a list will be converted to JSON values. + + \sa toVariantList(), QJsonValue::fromVariant() + */ +QJsonArray QJsonArray::fromVariantList(const QVariantList &list) +{ + QJsonArray array; + if (list.isEmpty()) + return array; + + array.detach2(1024); + + QVector values; + values.resize(list.size()); + QJsonPrivate::Value *valueData = values.data(); + uint currentOffset = sizeof(QJsonPrivate::Base); + + for (int i = 0; i < list.size(); ++i) { + QJsonValue val = QJsonValue::fromVariant(list.at(i)); + + bool latinOrIntValue; + int valueSize = QJsonPrivate::Value::requiredStorage(val, &latinOrIntValue); + + if (!array.detach2(valueSize)) + return QJsonArray(); + + QJsonPrivate::Value *v = valueData + i; + v->type = (val.t == QJsonValue::Undefined ? QJsonValue::Null : val.t); + v->latinOrIntValue = latinOrIntValue; + v->latinKey = false; + v->value = QJsonPrivate::Value::valueToStore(val, currentOffset); + if (valueSize) + QJsonPrivate::Value::copyData(val, (char *)array.a + currentOffset, latinOrIntValue); + + currentOffset += valueSize; + array.a->size = currentOffset; + } + + // write table + array.a->tableOffset = currentOffset; + if (!array.detach2(sizeof(QJsonPrivate::offset)*values.size())) + return QJsonArray(); + memcpy(array.a->table(), values.constData(), values.size()*sizeof(uint)); + array.a->length = values.size(); + array.a->size = currentOffset + sizeof(QJsonPrivate::offset)*values.size(); + + return array; +} + +/*! + Converts this object to a QVariantList. + + Returns the created map. + */ +QVariantList QJsonArray::toVariantList() const +{ + QVariantList list; + + if (a) { + list.reserve(a->length); + for (int i = 0; i < (int)a->length; ++i) + list.append(QJsonValue(d, a, a->at(i)).toVariant()); + } + return list; +} + + +/*! + Returns the number of values stored in the array. + */ +int QJsonArray::size() const +{ + if (!d) + return 0; + + return (int)a->length; +} + +/*! + \fn QJsonArray::count() const + + Same as size(). + + \sa size() +*/ + +/*! + Returns \c true if the object is empty. This is the same as size() == 0. + + \sa size() + */ +bool QJsonArray::isEmpty() const +{ + if (!d) + return true; + + return !a->length; +} + +/*! + Returns a QJsonValue representing the value for index \a i. + + The returned QJsonValue is \c Undefined, if \a i is out of bounds. + + */ +QJsonValue QJsonArray::at(int i) const +{ + if (!a || i < 0 || i >= (int)a->length) + return QJsonValue(QJsonValue::Undefined); + + return QJsonValue(d, a, a->at(i)); +} + +/*! + Returns the first value stored in the array. + + Same as \c at(0). + + \sa at() + */ +QJsonValue QJsonArray::first() const +{ + return at(0); +} + +/*! + Returns the last value stored in the array. + + Same as \c{at(size() - 1)}. + + \sa at() + */ +QJsonValue QJsonArray::last() const +{ + return at(a ? (a->length - 1) : 0); +} + +/*! + Inserts \a value at the beginning of the array. + + This is the same as \c{insert(0, value)} and will prepend \a value to the array. + + \sa append(), insert() + */ +void QJsonArray::prepend(const QJsonValue &value) +{ + insert(0, value); +} + +/*! + Inserts \a value at the end of the array. + + \sa prepend(), insert() + */ +void QJsonArray::append(const QJsonValue &value) +{ + insert(a ? (int)a->length : 0, value); +} + +/*! + Removes the value at index position \a i. \a i must be a valid + index position in the array (i.e., \c{0 <= i < size()}). + + \sa insert(), replace() + */ +void QJsonArray::removeAt(int i) +{ + if (!a || i < 0 || i >= (int)a->length) + return; + + detach2(); + a->removeItems(i, 1); + ++d->compactionCounter; + if (d->compactionCounter > 32u && d->compactionCounter >= unsigned(a->length) / 2u) + compact(); +} + +/*! \fn void QJsonArray::removeFirst() + + Removes the first item in the array. Calling this function is + equivalent to calling \c{removeAt(0)}. The array must not be empty. If + the array can be empty, call isEmpty() before calling this + function. + + \sa removeAt(), removeLast() +*/ + +/*! \fn void QJsonArray::removeLast() + + Removes the last item in the array. Calling this function is + equivalent to calling \c{removeAt(size() - 1)}. The array must not be + empty. If the array can be empty, call isEmpty() before calling + this function. + + \sa removeAt(), removeFirst() +*/ + +/*! + Removes the item at index position \a i and returns it. \a i must + be a valid index position in the array (i.e., \c{0 <= i < size()}). + + If you don't use the return value, removeAt() is more efficient. + + \sa removeAt() + */ +QJsonValue QJsonArray::takeAt(int i) +{ + if (!a || i < 0 || i >= (int)a->length) + return QJsonValue(QJsonValue::Undefined); + + QJsonValue v(d, a, a->at(i)); + removeAt(i); // detaches + return v; +} + +/*! + Inserts \a value at index position \a i in the array. If \a i + is \c 0, the value is prepended to the array. If \a i is size(), the + value is appended to the array. + + \sa append(), prepend(), replace(), removeAt() + */ +void QJsonArray::insert(int i, const QJsonValue &value) +{ + Q_ASSERT (i >= 0 && i <= (a ? (int)a->length : 0)); + QJsonValue val = value; + + bool compressed; + int valueSize = QJsonPrivate::Value::requiredStorage(val, &compressed); + + if (!detach2(valueSize + sizeof(QJsonPrivate::Value))) + return; + + if (!a->length) + a->tableOffset = sizeof(QJsonPrivate::Array); + + int valueOffset = a->reserveSpace(valueSize, i, 1, false); + if (!valueOffset) + return; + + QJsonPrivate::Value &v = (*a)[i]; + v.type = (val.t == QJsonValue::Undefined ? QJsonValue::Null : val.t); + v.latinOrIntValue = compressed; + v.latinKey = false; + v.value = QJsonPrivate::Value::valueToStore(val, valueOffset); + if (valueSize) + QJsonPrivate::Value::copyData(val, (char *)a + valueOffset, compressed); +} + +/*! + \fn QJsonArray::iterator QJsonArray::insert(iterator before, const QJsonValue &value) + + Inserts \a value before the position pointed to by \a before, and returns an iterator + pointing to the newly inserted item. + + \sa erase(), insert() +*/ + +/*! + \fn QJsonArray::iterator QJsonArray::erase(iterator it) + + Removes the item pointed to by \a it, and returns an iterator pointing to the + next item. + + \sa removeAt() +*/ + +/*! + Replaces the item at index position \a i with \a value. \a i must + be a valid index position in the array (i.e., \c{0 <= i < size()}). + + \sa operator[](), removeAt() + */ +void QJsonArray::replace(int i, const QJsonValue &value) +{ + Q_ASSERT (a && i >= 0 && i < (int)(a->length)); + QJsonValue val = value; + + bool compressed; + int valueSize = QJsonPrivate::Value::requiredStorage(val, &compressed); + + if (!detach2(valueSize)) + return; + + if (!a->length) + a->tableOffset = sizeof(QJsonPrivate::Array); + + int valueOffset = a->reserveSpace(valueSize, i, 1, true); + if (!valueOffset) + return; + + QJsonPrivate::Value &v = (*a)[i]; + v.type = (val.t == QJsonValue::Undefined ? QJsonValue::Null : val.t); + v.latinOrIntValue = compressed; + v.latinKey = false; + v.value = QJsonPrivate::Value::valueToStore(val, valueOffset); + if (valueSize) + QJsonPrivate::Value::copyData(val, (char *)a + valueOffset, compressed); + + ++d->compactionCounter; + if (d->compactionCounter > 32u && d->compactionCounter >= unsigned(a->length) / 2u) + compact(); +} + +/*! + Returns \c true if the array contains an occurrence of \a value, otherwise \c false. + + \sa count() + */ +bool QJsonArray::contains(const QJsonValue &value) const +{ + for (int i = 0; i < size(); i++) { + if (at(i) == value) + return true; + } + return false; +} + +/*! + Returns the value at index position \a i as a modifiable reference. + \a i must be a valid index position in the array (i.e., \c{0 <= i < + size()}). + + The return value is of type QJsonValueRef, a helper class for QJsonArray + and QJsonObject. When you get an object of type QJsonValueRef, you can + use it as if it were a reference to a QJsonValue. If you assign to it, + the assignment will apply to the character in the QJsonArray of QJsonObject + from which you got the reference. + + \sa at() + */ +QJsonValueRef QJsonArray::operator [](int i) +{ + Q_ASSERT(a && i >= 0 && i < (int)a->length); + return QJsonValueRef(this, i); +} + +/*! + \overload + + Same as at(). + */ +QJsonValue QJsonArray::operator[](int i) const +{ + return at(i); +} + +/*! + Returns \c true if this array is equal to \a other. + */ +bool QJsonArray::operator==(const QJsonArray &other) const +{ + if (a == other.a) + return true; + + if (!a) + return !other.a->length; + if (!other.a) + return !a->length; + if (a->length != other.a->length) + return false; + + for (int i = 0; i < (int)a->length; ++i) { + if (QJsonValue(d, a, a->at(i)) != QJsonValue(other.d, other.a, other.a->at(i))) + return false; + } + return true; +} + +/*! + Returns \c true if this array is not equal to \a other. + */ +bool QJsonArray::operator!=(const QJsonArray &other) const +{ + return !(*this == other); +} + +/*! \fn QJsonArray::iterator QJsonArray::begin() + + Returns an \l{STL-style iterators}{STL-style iterator} pointing to the first item in + the array. + + \sa constBegin(), end() +*/ + +/*! \fn QJsonArray::const_iterator QJsonArray::begin() const + + \overload +*/ + +/*! \fn QJsonArray::const_iterator QJsonArray::constBegin() const + + Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the first item + in the array. + + \sa begin(), constEnd() +*/ + +/*! \fn QJsonArray::iterator QJsonArray::end() + + Returns an \l{STL-style iterators}{STL-style iterator} pointing to the imaginary item + after the last item in the array. + + \sa begin(), constEnd() +*/ + +/*! \fn const_iterator QJsonArray::end() const + + \overload +*/ + +/*! \fn QJsonArray::const_iterator QJsonArray::constEnd() const + + Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the imaginary + item after the last item in the array. + + \sa constBegin(), end() +*/ + +/*! \fn void QJsonArray::push_back(const QJsonValue &value) + + This function is provided for STL compatibility. It is equivalent + to \l{QJsonArray::append()}{append(value)} and will append \a value to the array. +*/ + +/*! \fn void QJsonArray::push_front(const QJsonValue &value) + + This function is provided for STL compatibility. It is equivalent + to \l{QJsonArray::prepend()}{prepend(value)} and will prepend \a value to the array. +*/ + +/*! \fn void QJsonArray::pop_front() + + This function is provided for STL compatibility. It is equivalent + to removeFirst(). The array must not be empty. If the array can be + empty, call isEmpty() before calling this function. +*/ + +/*! \fn void QJsonArray::pop_back() + + This function is provided for STL compatibility. It is equivalent + to removeLast(). The array must not be empty. If the array can be + empty, call isEmpty() before calling this function. +*/ + +/*! \fn bool QJsonArray::empty() const + + This function is provided for STL compatibility. It is equivalent + to isEmpty() and returns \c true if the array is empty. +*/ + +/*! \class QJsonArray::iterator + \inmodule QtCore + \brief The QJsonArray::iterator class provides an STL-style non-const iterator for QJsonArray. + + QJsonArray::iterator allows you to iterate over a QJsonArray + and to modify the array item associated with the + iterator. If you want to iterate over a const QJsonArray, use + QJsonArray::const_iterator instead. It is generally a good practice to + use QJsonArray::const_iterator on a non-const QJsonArray as well, unless + you need to change the QJsonArray through the iterator. Const + iterators are slightly faster and improves code readability. + + The default QJsonArray::iterator constructor creates an uninitialized + iterator. You must initialize it using a QJsonArray function like + QJsonArray::begin(), QJsonArray::end(), or QJsonArray::insert() before you can + start iterating. + + Most QJsonArray functions accept an integer index rather than an + iterator. For that reason, iterators are rarely useful in + connection with QJsonArray. One place where STL-style iterators do + make sense is as arguments to \l{generic algorithms}. + + Multiple iterators can be used on the same array. However, be + aware that any non-const function call performed on the QJsonArray + will render all existing iterators undefined. + + \sa QJsonArray::const_iterator +*/ + +/*! \typedef QJsonArray::iterator::iterator_category + + A synonym for \e {std::random_access_iterator_tag} indicating + this iterator is a random access iterator. +*/ + +/*! \typedef QJsonArray::iterator::difference_type + + \internal +*/ + +/*! \typedef QJsonArray::iterator::value_type + + \internal +*/ + +/*! \typedef QJsonArray::iterator::reference + + \internal +*/ + +/*! \typedef QJsonArray::iterator::pointer + + \internal +*/ + +/*! \fn QJsonArray::iterator::iterator() + + Constructs an uninitialized iterator. + + Functions like operator*() and operator++() should not be called + on an uninitialized iterator. Use operator=() to assign a value + to it before using it. + + \sa QJsonArray::begin(), QJsonArray::end() +*/ + +/*! \fn QJsonArray::iterator::iterator(QJsonArray *array, int index) + \internal +*/ + +/*! \fn QJsonValueRef QJsonArray::iterator::operator*() const + + + Returns a modifiable reference to the current item. + + You can change the value of an item by using operator*() on the + left side of an assignment. + + The return value is of type QJsonValueRef, a helper class for QJsonArray + and QJsonObject. When you get an object of type QJsonValueRef, you can + use it as if it were a reference to a QJsonValue. If you assign to it, + the assignment will apply to the character in the QJsonArray of QJsonObject + from which you got the reference. +*/ + +/*! \fn QJsonValueRef *QJsonArray::iterator::operator->() const + + Returns a pointer to a modifiable reference to the current item. +*/ + +/*! \fn QJsonValueRef QJsonArray::iterator::operator[](int j) const + + Returns a modifiable reference to the item at offset \a j from the + item pointed to by this iterator (the item at position \c{*this + j}). + + This function is provided to make QJsonArray iterators behave like C++ + pointers. + + The return value is of type QJsonValueRef, a helper class for QJsonArray + and QJsonObject. When you get an object of type QJsonValueRef, you can + use it as if it were a reference to a QJsonValue. If you assign to it, + the assignment will apply to the character in the QJsonArray of QJsonObject + from which you got the reference. + + \sa operator+() +*/ + +/*! + \fn bool QJsonArray::iterator::operator==(const iterator &other) const + \fn bool QJsonArray::iterator::operator==(const const_iterator &other) const + + Returns \c true if \a other points to the same item as this + iterator; otherwise returns \c false. + + \sa operator!=() +*/ + +/*! + \fn bool QJsonArray::iterator::operator!=(const iterator &other) const + \fn bool QJsonArray::iterator::operator!=(const const_iterator &other) const + + Returns \c true if \a other points to a different item than this + iterator; otherwise returns \c false. + + \sa operator==() +*/ + +/*! + \fn bool QJsonArray::iterator::operator<(const iterator& other) const + \fn bool QJsonArray::iterator::operator<(const const_iterator& other) const + + Returns \c true if the item pointed to by this iterator is less than + the item pointed to by the \a other iterator. +*/ + +/*! + \fn bool QJsonArray::iterator::operator<=(const iterator& other) const + \fn bool QJsonArray::iterator::operator<=(const const_iterator& other) const + + Returns \c true if the item pointed to by this iterator is less than + or equal to the item pointed to by the \a other iterator. +*/ + +/*! + \fn bool QJsonArray::iterator::operator>(const iterator& other) const + \fn bool QJsonArray::iterator::operator>(const const_iterator& other) const + + Returns \c true if the item pointed to by this iterator is greater + than the item pointed to by the \a other iterator. +*/ + +/*! + \fn bool QJsonArray::iterator::operator>=(const iterator& other) const + \fn bool QJsonArray::iterator::operator>=(const const_iterator& other) const + + Returns \c true if the item pointed to by this iterator is greater + than or equal to the item pointed to by the \a other iterator. +*/ + +/*! \fn QJsonArray::iterator &QJsonArray::iterator::operator++() + + The prefix ++ operator, \c{++it}, advances the iterator to the + next item in the array and returns an iterator to the new current + item. + + Calling this function on QJsonArray::end() leads to undefined results. + + \sa operator--() +*/ + +/*! \fn QJsonArray::iterator QJsonArray::iterator::operator++(int) + + \overload + + The postfix ++ operator, \c{it++}, advances the iterator to the + next item in the array and returns an iterator to the previously + current item. +*/ + +/*! \fn QJsonArray::iterator &QJsonArray::iterator::operator--() + + The prefix -- operator, \c{--it}, makes the preceding item + current and returns an iterator to the new current item. + + Calling this function on QJsonArray::begin() leads to undefined results. + + \sa operator++() +*/ + +/*! \fn QJsonArray::iterator QJsonArray::iterator::operator--(int) + + \overload + + The postfix -- operator, \c{it--}, makes the preceding item + current and returns an iterator to the previously current item. +*/ + +/*! \fn QJsonArray::iterator &QJsonArray::iterator::operator+=(int j) + + Advances the iterator by \a j items. If \a j is negative, the + iterator goes backward. + + \sa operator-=(), operator+() +*/ + +/*! \fn QJsonArray::iterator &QJsonArray::iterator::operator-=(int j) + + Makes the iterator go back by \a j items. If \a j is negative, + the iterator goes forward. + + \sa operator+=(), operator-() +*/ + +/*! \fn QJsonArray::iterator QJsonArray::iterator::operator+(int j) const + + Returns an iterator to the item at \a j positions forward from + this iterator. If \a j is negative, the iterator goes backward. + + \sa operator-(), operator+=() +*/ + +/*! \fn QJsonArray::iterator QJsonArray::iterator::operator-(int j) const + + Returns an iterator to the item at \a j positions backward from + this iterator. If \a j is negative, the iterator goes forward. + + \sa operator+(), operator-=() +*/ + +/*! \fn int QJsonArray::iterator::operator-(iterator other) const + + Returns the number of items between the item pointed to by \a + other and the item pointed to by this iterator. +*/ + +/*! \class QJsonArray::const_iterator + \inmodule QtCore + \brief The QJsonArray::const_iterator class provides an STL-style const iterator for QJsonArray. + + QJsonArray::const_iterator allows you to iterate over a + QJsonArray. If you want to modify the QJsonArray as + you iterate over it, use QJsonArray::iterator instead. It is generally a + good practice to use QJsonArray::const_iterator on a non-const QJsonArray + as well, unless you need to change the QJsonArray through the + iterator. Const iterators are slightly faster and improves + code readability. + + The default QJsonArray::const_iterator constructor creates an + uninitialized iterator. You must initialize it using a QJsonArray + function like QJsonArray::constBegin(), QJsonArray::constEnd(), or + QJsonArray::insert() before you can start iterating. + + Most QJsonArray functions accept an integer index rather than an + iterator. For that reason, iterators are rarely useful in + connection with QJsonArray. One place where STL-style iterators do + make sense is as arguments to \l{generic algorithms}. + + Multiple iterators can be used on the same array. However, be + aware that any non-const function call performed on the QJsonArray + will render all existing iterators undefined. + + \sa QJsonArray::iterator +*/ + +/*! \fn QJsonArray::const_iterator::const_iterator() + + Constructs an uninitialized iterator. + + Functions like operator*() and operator++() should not be called + on an uninitialized iterator. Use operator=() to assign a value + to it before using it. + + \sa QJsonArray::constBegin(), QJsonArray::constEnd() +*/ + +/*! \fn QJsonArray::const_iterator::const_iterator(const QJsonArray *array, int index) + \internal +*/ + +/*! \typedef QJsonArray::const_iterator::iterator_category + + A synonym for \e {std::random_access_iterator_tag} indicating + this iterator is a random access iterator. +*/ + +/*! \typedef QJsonArray::const_iterator::difference_type + + \internal +*/ + +/*! \typedef QJsonArray::const_iterator::value_type + + \internal +*/ + +/*! \typedef QJsonArray::const_iterator::reference + + \internal +*/ + +/*! \typedef QJsonArray::const_iterator::pointer + + \internal +*/ + +/*! \fn QJsonArray::const_iterator::const_iterator(const const_iterator &other) + + Constructs a copy of \a other. +*/ + +/*! \fn QJsonArray::const_iterator::const_iterator(const iterator &other) + + Constructs a copy of \a other. +*/ + +/*! \fn QJsonValue QJsonArray::const_iterator::operator*() const + + Returns the current item. +*/ + +/*! \fn QJsonValue *QJsonArray::const_iterator::operator->() const + + Returns a pointer to the current item. +*/ + +/*! \fn QJsonValue QJsonArray::const_iterator::operator[](int j) const + + Returns the item at offset \a j from the item pointed to by this iterator (the item at + position \c{*this + j}). + + This function is provided to make QJsonArray iterators behave like C++ + pointers. + + \sa operator+() +*/ + +/*! \fn bool QJsonArray::const_iterator::operator==(const const_iterator &other) const + + Returns \c true if \a other points to the same item as this + iterator; otherwise returns \c false. + + \sa operator!=() +*/ + +/*! \fn bool QJsonArray::const_iterator::operator!=(const const_iterator &other) const + + Returns \c true if \a other points to a different item than this + iterator; otherwise returns \c false. + + \sa operator==() +*/ + +/*! + \fn bool QJsonArray::const_iterator::operator<(const const_iterator& other) const + + Returns \c true if the item pointed to by this iterator is less than + the item pointed to by the \a other iterator. +*/ + +/*! + \fn bool QJsonArray::const_iterator::operator<=(const const_iterator& other) const + + Returns \c true if the item pointed to by this iterator is less than + or equal to the item pointed to by the \a other iterator. +*/ + +/*! + \fn bool QJsonArray::const_iterator::operator>(const const_iterator& other) const + + Returns \c true if the item pointed to by this iterator is greater + than the item pointed to by the \a other iterator. +*/ + +/*! + \fn bool QJsonArray::const_iterator::operator>=(const const_iterator& other) const + + Returns \c true if the item pointed to by this iterator is greater + than or equal to the item pointed to by the \a other iterator. +*/ + +/*! \fn QJsonArray::const_iterator &QJsonArray::const_iterator::operator++() + + The prefix ++ operator, \c{++it}, advances the iterator to the + next item in the array and returns an iterator to the new current + item. + + Calling this function on QJsonArray::end() leads to undefined results. + + \sa operator--() +*/ + +/*! \fn QJsonArray::const_iterator QJsonArray::const_iterator::operator++(int) + + \overload + + The postfix ++ operator, \c{it++}, advances the iterator to the + next item in the array and returns an iterator to the previously + current item. +*/ + +/*! \fn QJsonArray::const_iterator &QJsonArray::const_iterator::operator--() + + The prefix -- operator, \c{--it}, makes the preceding item + current and returns an iterator to the new current item. + + Calling this function on QJsonArray::begin() leads to undefined results. + + \sa operator++() +*/ + +/*! \fn QJsonArray::const_iterator QJsonArray::const_iterator::operator--(int) + + \overload + + The postfix -- operator, \c{it--}, makes the preceding item + current and returns an iterator to the previously current item. +*/ + +/*! \fn QJsonArray::const_iterator &QJsonArray::const_iterator::operator+=(int j) + + Advances the iterator by \a j items. If \a j is negative, the + iterator goes backward. + + \sa operator-=(), operator+() +*/ + +/*! \fn QJsonArray::const_iterator &QJsonArray::const_iterator::operator-=(int j) + + Makes the iterator go back by \a j items. If \a j is negative, + the iterator goes forward. + + \sa operator+=(), operator-() +*/ + +/*! \fn QJsonArray::const_iterator QJsonArray::const_iterator::operator+(int j) const + + Returns an iterator to the item at \a j positions forward from + this iterator. If \a j is negative, the iterator goes backward. + + \sa operator-(), operator+=() +*/ + +/*! \fn QJsonArray::const_iterator QJsonArray::const_iterator::operator-(int j) const + + Returns an iterator to the item at \a j positions backward from + this iterator. If \a j is negative, the iterator goes forward. + + \sa operator+(), operator-=() +*/ + +/*! \fn int QJsonArray::const_iterator::operator-(const_iterator other) const + + Returns the number of items between the item pointed to by \a + other and the item pointed to by this iterator. +*/ + + +/*! + \internal + */ +void QJsonArray::detach(uint reserve) +{ + Q_UNUSED(reserve) + Q_ASSERT(!reserve); + detach2(0); +} + +/*! + \internal + */ +bool QJsonArray::detach2(uint reserve) +{ + if (!d) { + if (reserve >= QJsonPrivate::Value::MaxSize) { + qWarning("QJson: Document too large to store in data structure"); + return false; + } + d = new QJsonPrivate::Data(reserve, QJsonValue::Array); + a = static_cast(d->header->root()); + d->ref.ref(); + return true; + } + if (reserve == 0 && d->ref.load() == 1) + return true; + + QJsonPrivate::Data *x = d->clone(a, reserve); + if (!x) + return false; + x->ref.ref(); + if (!d->ref.deref()) + delete d; + d = x; + a = static_cast(d->header->root()); + return true; +} + +/*! + \internal + */ +void QJsonArray::compact() +{ + if (!d || !d->compactionCounter) + return; + + detach2(); + d->compact(); + a = static_cast(d->header->root()); +} + + +#if !defined(QT_NO_DEBUG_STREAM) && !defined(QT_JSON_READONLY) +QDebug operator<<(QDebug dbg, const QJsonArray &a) +{ + QDebugStateSaver saver(dbg); + if (!a.a) { + dbg << "QJsonArray()"; + return dbg; + } + QByteArray json; + QJsonPrivate::Writer::arrayToJson(a.a, json, 0, true); + dbg.nospace() << "QJsonArray(" + << json.constData() // print as utf-8 string without extra quotation marks + << ")"; + return dbg; +} +#endif + +QT_END_NAMESPACE + diff --git a/src/corelib/serialization/qjsonarray.h b/src/corelib/serialization/qjsonarray.h new file mode 100644 index 0000000000..8d41138c97 --- /dev/null +++ b/src/corelib/serialization/qjsonarray.h @@ -0,0 +1,274 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QJSONARRAY_H +#define QJSONARRAY_H + +#include +#include +#if defined(Q_COMPILER_INITIALIZER_LISTS) +#include +#endif + +QT_BEGIN_NAMESPACE + +class QDebug; +class QStringList; +template class QList; +typedef QList QVariantList; + +class Q_CORE_EXPORT QJsonArray +{ +public: + QJsonArray(); + +#if defined(Q_COMPILER_INITIALIZER_LISTS) || defined(Q_QDOC) + QJsonArray(std::initializer_list args) + { + initialize(); + for (std::initializer_list::const_iterator i = args.begin(); i != args.end(); ++i) + append(*i); + } +#endif + + ~QJsonArray(); + + QJsonArray(const QJsonArray &other); + QJsonArray &operator =(const QJsonArray &other); + + QJsonArray(QJsonArray &&other) Q_DECL_NOTHROW + : d(other.d), + a(other.a) + { + other.d = nullptr; + other.a = nullptr; + } + + QJsonArray &operator =(QJsonArray &&other) Q_DECL_NOTHROW + { + swap(other); + return *this; + } + + static QJsonArray fromStringList(const QStringList &list); + static QJsonArray fromVariantList(const QVariantList &list); + QVariantList toVariantList() const; + + int size() const; + inline int count() const { return size(); } + + bool isEmpty() const; + QJsonValue at(int i) const; + QJsonValue first() const; + QJsonValue last() const; + + void prepend(const QJsonValue &value); + void append(const QJsonValue &value); + void removeAt(int i); + QJsonValue takeAt(int i); + inline void removeFirst() { removeAt(0); } + inline void removeLast() { removeAt(size() - 1); } + + void insert(int i, const QJsonValue &value); + void replace(int i, const QJsonValue &value); + + bool contains(const QJsonValue &element) const; + QJsonValueRef operator[](int i); + QJsonValue operator[](int i) const; + + bool operator==(const QJsonArray &other) const; + bool operator!=(const QJsonArray &other) const; + + void swap(QJsonArray &other) Q_DECL_NOTHROW + { + qSwap(d, other.d); + qSwap(a, other.a); + } + + class const_iterator; + + class iterator { + public: + QJsonArray *a; + int i; + typedef std::random_access_iterator_tag iterator_category; + typedef int difference_type; + typedef QJsonValue value_type; + typedef QJsonValueRef reference; + typedef QJsonValueRefPtr pointer; + + inline iterator() : a(nullptr), i(0) { } + explicit inline iterator(QJsonArray *array, int index) : a(array), i(index) { } + + inline QJsonValueRef operator*() const { return QJsonValueRef(a, i); } +#ifdef Q_QDOC + inline QJsonValueRef* operator->() const; +#else + inline QJsonValueRefPtr operator->() const { return QJsonValueRefPtr(a, i); } +#endif + inline QJsonValueRef operator[](int j) const { return QJsonValueRef(a, i + j); } + + inline bool operator==(const iterator &o) const { return i == o.i; } + inline bool operator!=(const iterator &o) const { return i != o.i; } + inline bool operator<(const iterator& other) const { return i < other.i; } + inline bool operator<=(const iterator& other) const { return i <= other.i; } + inline bool operator>(const iterator& other) const { return i > other.i; } + inline bool operator>=(const iterator& other) const { return i >= other.i; } + inline bool operator==(const const_iterator &o) const { return i == o.i; } + inline bool operator!=(const const_iterator &o) const { return i != o.i; } + inline bool operator<(const const_iterator& other) const { return i < other.i; } + inline bool operator<=(const const_iterator& other) const { return i <= other.i; } + inline bool operator>(const const_iterator& other) const { return i > other.i; } + inline bool operator>=(const const_iterator& other) const { return i >= other.i; } + inline iterator &operator++() { ++i; return *this; } + inline iterator operator++(int) { iterator n = *this; ++i; return n; } + inline iterator &operator--() { i--; return *this; } + inline iterator operator--(int) { iterator n = *this; i--; return n; } + inline iterator &operator+=(int j) { i+=j; return *this; } + inline iterator &operator-=(int j) { i-=j; return *this; } + inline iterator operator+(int j) const { return iterator(a, i+j); } + inline iterator operator-(int j) const { return iterator(a, i-j); } + inline int operator-(iterator j) const { return i - j.i; } + }; + friend class iterator; + + class const_iterator { + public: + const QJsonArray *a; + int i; + typedef std::random_access_iterator_tag iterator_category; + typedef qptrdiff difference_type; + typedef QJsonValue value_type; + typedef QJsonValue reference; + typedef QJsonValuePtr pointer; + + inline const_iterator() : a(nullptr), i(0) { } + explicit inline const_iterator(const QJsonArray *array, int index) : a(array), i(index) { } +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + inline const_iterator(const const_iterator &o) : a(o.a), i(o.i) {} // ### Qt 6: Removed so class can be trivially-copyable +#endif + inline const_iterator(const iterator &o) : a(o.a), i(o.i) {} + + inline QJsonValue operator*() const { return a->at(i); } +#ifdef Q_QDOC + inline QJsonValue* operator->() const; +#else + inline QJsonValuePtr operator->() const { return QJsonValuePtr(a->at(i)); } +#endif + inline QJsonValue operator[](int j) const { return a->at(i+j); } + inline bool operator==(const const_iterator &o) const { return i == o.i; } + inline bool operator!=(const const_iterator &o) const { return i != o.i; } + inline bool operator<(const const_iterator& other) const { return i < other.i; } + inline bool operator<=(const const_iterator& other) const { return i <= other.i; } + inline bool operator>(const const_iterator& other) const { return i > other.i; } + inline bool operator>=(const const_iterator& other) const { return i >= other.i; } + inline const_iterator &operator++() { ++i; return *this; } + inline const_iterator operator++(int) { const_iterator n = *this; ++i; return n; } + inline const_iterator &operator--() { i--; return *this; } + inline const_iterator operator--(int) { const_iterator n = *this; i--; return n; } + inline const_iterator &operator+=(int j) { i+=j; return *this; } + inline const_iterator &operator-=(int j) { i-=j; return *this; } + inline const_iterator operator+(int j) const { return const_iterator(a, i+j); } + inline const_iterator operator-(int j) const { return const_iterator(a, i-j); } + inline int operator-(const_iterator j) const { return i - j.i; } + }; + friend class const_iterator; + + // stl style + inline iterator begin() { detach2(); return iterator(this, 0); } + inline const_iterator begin() const { return const_iterator(this, 0); } + inline const_iterator constBegin() const { return const_iterator(this, 0); } + inline iterator end() { detach2(); return iterator(this, size()); } + inline const_iterator end() const { return const_iterator(this, size()); } + inline const_iterator constEnd() const { return const_iterator(this, size()); } + iterator insert(iterator before, const QJsonValue &value) { insert(before.i, value); return before; } + iterator erase(iterator it) { removeAt(it.i); return it; } + + // more Qt + typedef iterator Iterator; + typedef const_iterator ConstIterator; + + // convenience + inline QJsonArray operator+(const QJsonValue &v) const + { QJsonArray n = *this; n += v; return n; } + inline QJsonArray &operator+=(const QJsonValue &v) + { append(v); return *this; } + inline QJsonArray &operator<< (const QJsonValue &v) + { append(v); return *this; } + + // stl compatibility + inline void push_back(const QJsonValue &t) { append(t); } + inline void push_front(const QJsonValue &t) { prepend(t); } + inline void pop_front() { removeFirst(); } + inline void pop_back() { removeLast(); } + inline bool empty() const { return isEmpty(); } + typedef int size_type; + typedef QJsonValue value_type; + typedef value_type *pointer; + typedef const value_type *const_pointer; + typedef QJsonValueRef reference; + typedef QJsonValue const_reference; + typedef int difference_type; + +private: + friend class QJsonPrivate::Data; + friend class QJsonValue; + friend class QJsonDocument; + friend Q_CORE_EXPORT QDebug operator<<(QDebug, const QJsonArray &); + + QJsonArray(QJsonPrivate::Data *data, QJsonPrivate::Array *array); + void initialize(); + void compact(); + // ### Qt 6: remove me and merge with detach2 + void detach(uint reserve = 0); + bool detach2(uint reserve = 0); + + QJsonPrivate::Data *d; + QJsonPrivate::Array *a; +}; + +Q_DECLARE_SHARED_NOT_MOVABLE_UNTIL_QT6(QJsonArray) + +#if !defined(QT_NO_DEBUG_STREAM) && !defined(QT_JSON_READONLY) +Q_CORE_EXPORT QDebug operator<<(QDebug, const QJsonArray &); +#endif + +QT_END_NAMESPACE + +#endif // QJSONARRAY_H diff --git a/src/corelib/serialization/qjsondocument.cpp b/src/corelib/serialization/qjsondocument.cpp new file mode 100644 index 0000000000..9794bca60d --- /dev/null +++ b/src/corelib/serialization/qjsondocument.cpp @@ -0,0 +1,667 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include "qjsonwriter_p.h" +#include "qjsonparser_p.h" +#include "qjson_p.h" + +QT_BEGIN_NAMESPACE + +/*! \class QJsonDocument + \inmodule QtCore + \ingroup json + \ingroup shared + \reentrant + \since 5.0 + + \brief The QJsonDocument class provides a way to read and write JSON documents. + + QJsonDocument is a class that wraps a complete JSON document and can read and + write this document both from a UTF-8 encoded text based representation as well + as Qt's own binary format. + + A JSON document can be converted from its text-based representation to a QJsonDocument + using QJsonDocument::fromJson(). toJson() converts it back to text. The parser is very + fast and efficient and converts the JSON to the binary representation used by Qt. + + Validity of the parsed document can be queried with !isNull() + + A document can be queried as to whether it contains an array or an object using isArray() + and isObject(). The array or object contained in the document can be retrieved using + array() or object() and then read or manipulated. + + A document can also be created from a stored binary representation using fromBinaryData() or + fromRawData(). + + \sa {JSON Support in Qt}, {JSON Save Game Example} +*/ + +/*! + * Constructs an empty and invalid document. + */ +QJsonDocument::QJsonDocument() + : d(0) +{ +} + +/*! + * Creates a QJsonDocument from \a object. + */ +QJsonDocument::QJsonDocument(const QJsonObject &object) + : d(0) +{ + setObject(object); +} + +/*! + * Constructs a QJsonDocument from \a array. + */ +QJsonDocument::QJsonDocument(const QJsonArray &array) + : d(0) +{ + setArray(array); +} + +/*! + \internal + */ +QJsonDocument::QJsonDocument(QJsonPrivate::Data *data) + : d(data) +{ + Q_ASSERT(d); + d->ref.ref(); +} + +/*! + Deletes the document. + + Binary data set with fromRawData is not freed. + */ +QJsonDocument::~QJsonDocument() +{ + if (d && !d->ref.deref()) + delete d; +} + +/*! + * Creates a copy of the \a other document. + */ +QJsonDocument::QJsonDocument(const QJsonDocument &other) +{ + d = other.d; + if (d) + d->ref.ref(); +} + +/*! + * Assigns the \a other document to this QJsonDocument. + * Returns a reference to this object. + */ +QJsonDocument &QJsonDocument::operator =(const QJsonDocument &other) +{ + if (d != other.d) { + if (d && !d->ref.deref()) + delete d; + d = other.d; + if (d) + d->ref.ref(); + } + + return *this; +} + +/*! + \fn QJsonDocument::QJsonDocument(QJsonDocument &&other) + \since 5.10 + + Move-constructs a QJsonDocument from \a other. +*/ + +/*! + \fn QJsonDocument &QJsonDocument::operator =(QJsonDocument &&other) + \since 5.10 + + Move-assigns \a other to this document. +*/ + +/*! + \fn void QJsonDocument::swap(QJsonDocument &other) + \since 5.10 + + Swaps the document \a other with this. This operation is very fast and never fails. +*/ + + +/*! \enum QJsonDocument::DataValidation + + This value is used to tell QJsonDocument whether to validate the binary data + when converting to a QJsonDocument using fromBinaryData() or fromRawData(). + + \value Validate Validate the data before using it. This is the default. + \value BypassValidation Bypasses data validation. Only use if you received the + data from a trusted place and know it's valid, as using of invalid data can crash + the application. + */ + +/*! + Creates a QJsonDocument that uses the first \a size bytes from + \a data. It assumes \a data contains a binary encoded JSON document. + The created document does not take ownership of \a data and the caller + has to guarantee that \a data will not be deleted or modified as long as + any QJsonDocument, QJsonObject or QJsonArray still references the data. + + \a data has to be aligned to a 4 byte boundary. + + \a validation decides whether the data is checked for validity before being used. + By default the data is validated. If the \a data is not valid, the method returns + a null document. + + Returns a QJsonDocument representing the data. + + \sa rawData(), fromBinaryData(), isNull(), DataValidation + */ +QJsonDocument QJsonDocument::fromRawData(const char *data, int size, DataValidation validation) +{ + if (quintptr(data) & 3) { + qWarning("QJsonDocument::fromRawData: data has to have 4 byte alignment"); + return QJsonDocument(); + } + + QJsonPrivate::Data *d = new QJsonPrivate::Data((char *)data, size); + d->ownsData = false; + + if (validation != BypassValidation && !d->valid()) { + delete d; + return QJsonDocument(); + } + + return QJsonDocument(d); +} + +/*! + Returns the raw binary representation of the data + \a size will contain the size of the returned data. + + This method is useful to e.g. stream the JSON document + in it's binary form to a file. + */ +const char *QJsonDocument::rawData(int *size) const +{ + if (!d) { + *size = 0; + return 0; + } + *size = d->alloc; + return d->rawData; +} + +/*! + Creates a QJsonDocument from \a data. + + \a validation decides whether the data is checked for validity before being used. + By default the data is validated. If the \a data is not valid, the method returns + a null document. + + \sa toBinaryData(), fromRawData(), isNull(), DataValidation + */ +QJsonDocument QJsonDocument::fromBinaryData(const QByteArray &data, DataValidation validation) +{ + if (data.size() < (int)(sizeof(QJsonPrivate::Header) + sizeof(QJsonPrivate::Base))) + return QJsonDocument(); + + QJsonPrivate::Header h; + memcpy(&h, data.constData(), sizeof(QJsonPrivate::Header)); + QJsonPrivate::Base root; + memcpy(&root, data.constData() + sizeof(QJsonPrivate::Header), sizeof(QJsonPrivate::Base)); + + // do basic checks here, so we don't try to allocate more memory than we can. + if (h.tag != QJsonDocument::BinaryFormatTag || h.version != 1u || + sizeof(QJsonPrivate::Header) + root.size > (uint)data.size()) + return QJsonDocument(); + + const uint size = sizeof(QJsonPrivate::Header) + root.size; + char *raw = (char *)malloc(size); + if (!raw) + return QJsonDocument(); + + memcpy(raw, data.constData(), size); + QJsonPrivate::Data *d = new QJsonPrivate::Data(raw, size); + + if (validation != BypassValidation && !d->valid()) { + delete d; + return QJsonDocument(); + } + + return QJsonDocument(d); +} + +/*! + Creates a QJsonDocument from the QVariant \a variant. + + If the \a variant contains any other type than a QVariantMap, + QVariantHash, QVariantList or QStringList, the returned document is invalid. + + \sa toVariant() + */ +QJsonDocument QJsonDocument::fromVariant(const QVariant &variant) +{ + QJsonDocument doc; + switch (variant.type()) { + case QVariant::Map: + doc.setObject(QJsonObject::fromVariantMap(variant.toMap())); + break; + case QVariant::Hash: + doc.setObject(QJsonObject::fromVariantHash(variant.toHash())); + break; + case QVariant::List: + doc.setArray(QJsonArray::fromVariantList(variant.toList())); + break; + case QVariant::StringList: + doc.setArray(QJsonArray::fromStringList(variant.toStringList())); + break; + default: + break; + } + return doc; +} + +/*! + Returns a QVariant representing the Json document. + + The returned variant will be a QVariantList if the document is + a QJsonArray and a QVariantMap if the document is a QJsonObject. + + \sa fromVariant(), QJsonValue::toVariant() + */ +QVariant QJsonDocument::toVariant() const +{ + if (!d) + return QVariant(); + + if (d->header->root()->isArray()) + return QJsonArray(d, static_cast(d->header->root())).toVariantList(); + else + return QJsonObject(d, static_cast(d->header->root())).toVariantMap(); +} + +/*! + Converts the QJsonDocument to a UTF-8 encoded JSON document. + + \sa fromJson() + */ +#if !defined(QT_JSON_READONLY) || defined(Q_CLANG_QDOC) +QByteArray QJsonDocument::toJson() const +{ + return toJson(Indented); +} +#endif + +/*! + \enum QJsonDocument::JsonFormat + + This value defines the format of the JSON byte array produced + when converting to a QJsonDocument using toJson(). + + \value Indented Defines human readable output as follows: + \code + { + "Array": [ + true, + 999, + "string" + ], + "Key": "Value", + "null": null + } + \endcode + + \value Compact Defines a compact output as follows: + \code + {"Array":[true,999,"string"],"Key":"Value","null":null} + \endcode + */ + +/*! + Converts the QJsonDocument to a UTF-8 encoded JSON document in the provided \a format. + + \sa fromJson(), JsonFormat + */ +#if !defined(QT_JSON_READONLY) || defined(Q_CLANG_QDOC) +QByteArray QJsonDocument::toJson(JsonFormat format) const +{ + QByteArray json; + if (!d) + return json; + + if (d->header->root()->isArray()) + QJsonPrivate::Writer::arrayToJson(static_cast(d->header->root()), json, 0, (format == Compact)); + else + QJsonPrivate::Writer::objectToJson(static_cast(d->header->root()), json, 0, (format == Compact)); + + return json; +} +#endif + +/*! + Parses \a json as a UTF-8 encoded JSON document, and creates a QJsonDocument + from it. + + Returns a valid (non-null) QJsonDocument if the parsing succeeds. If it fails, + the returned document will be null, and the optional \a error variable will contain + further details about the error. + + \sa toJson(), QJsonParseError, isNull() + */ +QJsonDocument QJsonDocument::fromJson(const QByteArray &json, QJsonParseError *error) +{ + QJsonPrivate::Parser parser(json.constData(), json.length()); + return parser.parse(error); +} + +/*! + Returns \c true if the document doesn't contain any data. + */ +bool QJsonDocument::isEmpty() const +{ + if (!d) + return true; + + return false; +} + +/*! + Returns a binary representation of the document. + + The binary representation is also the native format used internally in Qt, + and is very efficient and fast to convert to and from. + + The binary format can be stored on disk and interchanged with other applications + or computers. fromBinaryData() can be used to convert it back into a + JSON document. + + \sa fromBinaryData() + */ +QByteArray QJsonDocument::toBinaryData() const +{ + if (!d || !d->rawData) + return QByteArray(); + + return QByteArray(d->rawData, d->header->root()->size + sizeof(QJsonPrivate::Header)); +} + +/*! + Returns \c true if the document contains an array. + + \sa array(), isObject() + */ +bool QJsonDocument::isArray() const +{ + if (!d) + return false; + + QJsonPrivate::Header *h = (QJsonPrivate::Header *)d->rawData; + return h->root()->isArray(); +} + +/*! + Returns \c true if the document contains an object. + + \sa object(), isArray() + */ +bool QJsonDocument::isObject() const +{ + if (!d) + return false; + + QJsonPrivate::Header *h = (QJsonPrivate::Header *)d->rawData; + return h->root()->isObject(); +} + +/*! + Returns the QJsonObject contained in the document. + + Returns an empty object if the document contains an + array. + + \sa isObject(), array(), setObject() + */ +QJsonObject QJsonDocument::object() const +{ + if (d) { + QJsonPrivate::Base *b = d->header->root(); + if (b->isObject()) + return QJsonObject(d, static_cast(b)); + } + return QJsonObject(); +} + +/*! + Returns the QJsonArray contained in the document. + + Returns an empty array if the document contains an + object. + + \sa isArray(), object(), setArray() + */ +QJsonArray QJsonDocument::array() const +{ + if (d) { + QJsonPrivate::Base *b = d->header->root(); + if (b->isArray()) + return QJsonArray(d, static_cast(b)); + } + return QJsonArray(); +} + +/*! + Sets \a object as the main object of this document. + + \sa setArray(), object() + */ +void QJsonDocument::setObject(const QJsonObject &object) +{ + if (d && !d->ref.deref()) + delete d; + + d = object.d; + + if (!d) { + d = new QJsonPrivate::Data(0, QJsonValue::Object); + } else if (d->compactionCounter || object.o != d->header->root()) { + QJsonObject o(object); + if (d->compactionCounter) + o.compact(); + else + o.detach2(); + d = o.d; + d->ref.ref(); + return; + } + d->ref.ref(); +} + +/*! + Sets \a array as the main object of this document. + + \sa setObject(), array() + */ +void QJsonDocument::setArray(const QJsonArray &array) +{ + if (d && !d->ref.deref()) + delete d; + + d = array.d; + + if (!d) { + d = new QJsonPrivate::Data(0, QJsonValue::Array); + } else if (d->compactionCounter || array.a != d->header->root()) { + QJsonArray a(array); + if (d->compactionCounter) + a.compact(); + else + a.detach2(); + d = a.d; + d->ref.ref(); + return; + } + d->ref.ref(); +} + +/*! + Returns a QJsonValue representing the value for the key \a key. + + Equivalent to calling object().value(key). + + The returned QJsonValue is QJsonValue::Undefined if the key does not exist, + or if isObject() is false. + + \since 5.10 + + \sa QJsonValue, QJsonValue::isUndefined(), QJsonObject + */ +const QJsonValue QJsonDocument::operator[](const QString &key) const +{ + if (!isObject()) + return QJsonValue(QJsonValue::Undefined); + + return object().value(key); +} + +/*! + \overload + \since 5.10 +*/ +const QJsonValue QJsonDocument::operator[](QLatin1String key) const +{ + if (!isObject()) + return QJsonValue(QJsonValue::Undefined); + + return object().value(key); +} + +/*! + Returns a QJsonValue representing the value for index \a i. + + Equivalent to calling array().at(i). + + The returned QJsonValue is QJsonValue::Undefined, if \a i is out of bounds, + or if isArray() is false. + + \since 5.10 + + \sa QJsonValue, QJsonValue::isUndefined(), QJsonArray + */ +const QJsonValue QJsonDocument::operator[](int i) const +{ + if (!isArray()) + return QJsonValue(QJsonValue::Undefined); + + return array().at(i); +} + +/*! + Returns \c true if the \a other document is equal to this document. + */ +bool QJsonDocument::operator==(const QJsonDocument &other) const +{ + if (d == other.d) + return true; + + if (!d || !other.d) + return false; + + if (d->header->root()->isArray() != other.d->header->root()->isArray()) + return false; + + if (d->header->root()->isObject()) + return QJsonObject(d, static_cast(d->header->root())) + == QJsonObject(other.d, static_cast(other.d->header->root())); + else + return QJsonArray(d, static_cast(d->header->root())) + == QJsonArray(other.d, static_cast(other.d->header->root())); +} + +/*! + \fn bool QJsonDocument::operator!=(const QJsonDocument &other) const + + returns \c true if \a other is not equal to this document + */ + +/*! + returns \c true if this document is null. + + Null documents are documents created through the default constructor. + + Documents created from UTF-8 encoded text or the binary format are + validated during parsing. If validation fails, the returned document + will also be null. + */ +bool QJsonDocument::isNull() const +{ + return (d == 0); +} + +#if !defined(QT_NO_DEBUG_STREAM) && !defined(QT_JSON_READONLY) +QDebug operator<<(QDebug dbg, const QJsonDocument &o) +{ + QDebugStateSaver saver(dbg); + if (!o.d) { + dbg << "QJsonDocument()"; + return dbg; + } + QByteArray json; + if (o.d->header->root()->isArray()) + QJsonPrivate::Writer::arrayToJson(static_cast(o.d->header->root()), json, 0, true); + else + QJsonPrivate::Writer::objectToJson(static_cast(o.d->header->root()), json, 0, true); + dbg.nospace() << "QJsonDocument(" + << json.constData() // print as utf-8 string without extra quotation marks + << ')'; + return dbg; +} +#endif + +QT_END_NAMESPACE diff --git a/src/corelib/serialization/qjsondocument.h b/src/corelib/serialization/qjsondocument.h new file mode 100644 index 0000000000..b784890c54 --- /dev/null +++ b/src/corelib/serialization/qjsondocument.h @@ -0,0 +1,177 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QJSONDOCUMENT_H +#define QJSONDOCUMENT_H + +#include + +QT_BEGIN_NAMESPACE + +class QDebug; + +namespace QJsonPrivate { + class Parser; +} + +struct Q_CORE_EXPORT QJsonParseError +{ + enum ParseError { + NoError = 0, + UnterminatedObject, + MissingNameSeparator, + UnterminatedArray, + MissingValueSeparator, + IllegalValue, + TerminationByNumber, + IllegalNumber, + IllegalEscapeSequence, + IllegalUTF8String, + UnterminatedString, + MissingObject, + DeepNesting, + DocumentTooLarge, + GarbageAtEnd + }; + + QString errorString() const; + + int offset; + ParseError error; +}; + +class Q_CORE_EXPORT QJsonDocument +{ +public: +#ifdef Q_LITTLE_ENDIAN + static const uint BinaryFormatTag = ('q') | ('b' << 8) | ('j' << 16) | ('s' << 24); +#else + static const uint BinaryFormatTag = ('q' << 24) | ('b' << 16) | ('j' << 8) | ('s'); +#endif + + QJsonDocument(); + explicit QJsonDocument(const QJsonObject &object); + explicit QJsonDocument(const QJsonArray &array); + ~QJsonDocument(); + + QJsonDocument(const QJsonDocument &other); + QJsonDocument &operator =(const QJsonDocument &other); + + QJsonDocument(QJsonDocument &&other) Q_DECL_NOTHROW + : d(other.d) + { + other.d = nullptr; + } + + QJsonDocument &operator =(QJsonDocument &&other) Q_DECL_NOTHROW + { + swap(other); + return *this; + } + + void swap(QJsonDocument &other) Q_DECL_NOTHROW + { + qSwap(d, other.d); + } + + enum DataValidation { + Validate, + BypassValidation + }; + + static QJsonDocument fromRawData(const char *data, int size, DataValidation validation = Validate); + const char *rawData(int *size) const; + + static QJsonDocument fromBinaryData(const QByteArray &data, DataValidation validation = Validate); + QByteArray toBinaryData() const; + + static QJsonDocument fromVariant(const QVariant &variant); + QVariant toVariant() const; + + enum JsonFormat { + Indented, + Compact + }; + + static QJsonDocument fromJson(const QByteArray &json, QJsonParseError *error = nullptr); + +#if !defined(QT_JSON_READONLY) || defined(Q_CLANG_QDOC) + QByteArray toJson() const; //### Merge in Qt6 + QByteArray toJson(JsonFormat format) const; +#endif + + bool isEmpty() const; + bool isArray() const; + bool isObject() const; + + QJsonObject object() const; + QJsonArray array() const; + + void setObject(const QJsonObject &object); + void setArray(const QJsonArray &array); + + const QJsonValue operator[](const QString &key) const; + const QJsonValue operator[](QLatin1String key) const; + const QJsonValue operator[](int i) const; + + bool operator==(const QJsonDocument &other) const; + bool operator!=(const QJsonDocument &other) const { return !(*this == other); } + + bool isNull() const; + +private: + friend class QJsonValue; + friend class QJsonPrivate::Data; + friend class QJsonPrivate::Parser; + friend Q_CORE_EXPORT QDebug operator<<(QDebug, const QJsonDocument &); + + QJsonDocument(QJsonPrivate::Data *data); + + QJsonPrivate::Data *d; +}; + +Q_DECLARE_SHARED_NOT_MOVABLE_UNTIL_QT6(QJsonDocument) + +#if !defined(QT_NO_DEBUG_STREAM) && !defined(QT_JSON_READONLY) +Q_CORE_EXPORT QDebug operator<<(QDebug, const QJsonDocument &); +#endif + +QT_END_NAMESPACE + +#endif // QJSONDOCUMENT_H diff --git a/src/corelib/serialization/qjsonobject.cpp b/src/corelib/serialization/qjsonobject.cpp new file mode 100644 index 0000000000..4a316c8a6f --- /dev/null +++ b/src/corelib/serialization/qjsonobject.cpp @@ -0,0 +1,1312 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include "qjson_p.h" +#include "qjsonwriter_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QJsonObject + \inmodule QtCore + \ingroup json + \ingroup shared + \reentrant + \since 5.0 + + \brief The QJsonObject class encapsulates a JSON object. + + A JSON object is a list of key value pairs, where the keys are unique strings + and the values are represented by a QJsonValue. + + A QJsonObject can be converted to and from a QVariantMap. You can query the + number of (key, value) pairs with size(), insert(), and remove() entries from it + and iterate over its content using the standard C++ iterator pattern. + + QJsonObject is an implicitly shared class, and shares the data with the document + it has been created from as long as it is not being modified. + + You can convert the object to and from text based JSON through QJsonDocument. + + \sa {JSON Support in Qt}, {JSON Save Game Example} +*/ + +/*! + \typedef QJsonObject::Iterator + + Qt-style synonym for QJsonObject::iterator. +*/ + +/*! + \typedef QJsonObject::ConstIterator + + Qt-style synonym for QJsonObject::const_iterator. +*/ + +/*! + \typedef QJsonObject::key_type + + Typedef for QString. Provided for STL compatibility. +*/ + +/*! + \typedef QJsonObject::mapped_type + + Typedef for QJsonValue. Provided for STL compatibility. +*/ + +/*! + \typedef QJsonObject::size_type + + Typedef for int. Provided for STL compatibility. +*/ + + +/*! + Constructs an empty JSON object. + + \sa isEmpty() + */ +QJsonObject::QJsonObject() + : d(0), o(0) +{ +} + +/*! + \fn QJsonObject::QJsonObject(std::initializer_list > args) + \since 5.4 + Constructs a QJsonObject instance initialized from \a args initialization list. + For example: + \code + QJsonObject object + { + {"property1", 1}, + {"property2", 2} + }; + \endcode +*/ + +/*! + \internal + */ +QJsonObject::QJsonObject(QJsonPrivate::Data *data, QJsonPrivate::Object *object) + : d(data), o(object) +{ + Q_ASSERT(d); + Q_ASSERT(o); + d->ref.ref(); +} + +/*! + This method replaces part of the QJsonObject(std::initializer_list> args) body. + The constructor needs to be inline, but we do not want to leak implementation details + of this class. + \note this method is called for an uninitialized object + \internal + */ + +void QJsonObject::initialize() +{ + d = 0; + o = 0; +} + +/*! + Destroys the object. + */ +QJsonObject::~QJsonObject() +{ + if (d && !d->ref.deref()) + delete d; +} + +/*! + Creates a copy of \a other. + + Since QJsonObject is implicitly shared, the copy is shallow + as long as the object does not get modified. + */ +QJsonObject::QJsonObject(const QJsonObject &other) +{ + d = other.d; + o = other.o; + if (d) + d->ref.ref(); +} + +/*! + Assigns \a other to this object. + */ +QJsonObject &QJsonObject::operator =(const QJsonObject &other) +{ + if (d != other.d) { + if (d && !d->ref.deref()) + delete d; + d = other.d; + if (d) + d->ref.ref(); + } + o = other.o; + + return *this; +} + +/*! + \fn QJsonObject::QJsonObject(QJsonObject &&other) + \since 5.10 + + Move-constructs a QJsonObject from \a other. +*/ + +/*! + \fn QJsonObject &QJsonObject::operator =(QJsonObject &&other) + \since 5.10 + + Move-assigns \a other to this object. +*/ + +/*! + \fn void QJsonObject::swap(QJsonObject &other) + \since 5.10 + + Swaps the object \a other with this. This operation is very fast and never fails. +*/ + + +/*! + Converts the variant map \a map to a QJsonObject. + + The keys in \a map will be used as the keys in the JSON object, + and the QVariant values will be converted to JSON values. + + \sa fromVariantHash(), toVariantMap(), QJsonValue::fromVariant() + */ +QJsonObject QJsonObject::fromVariantMap(const QVariantMap &map) +{ + QJsonObject object; + if (map.isEmpty()) + return object; + + object.detach2(1024); + + QVector offsets; + QJsonPrivate::offset currentOffset; + currentOffset = sizeof(QJsonPrivate::Base); + + // the map is already sorted, so we can simply append one entry after the other and + // write the offset table at the end + for (QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it) { + QString key = it.key(); + QJsonValue val = QJsonValue::fromVariant(it.value()); + + bool latinOrIntValue; + int valueSize = QJsonPrivate::Value::requiredStorage(val, &latinOrIntValue); + + bool latinKey = QJsonPrivate::useCompressed(key); + int valueOffset = sizeof(QJsonPrivate::Entry) + QJsonPrivate::qStringSize(key, latinKey); + int requiredSize = valueOffset + valueSize; + + if (!object.detach2(requiredSize + sizeof(QJsonPrivate::offset))) // offset for the new index entry + return QJsonObject(); + + QJsonPrivate::Entry *e = reinterpret_cast(reinterpret_cast(object.o) + currentOffset); + e->value.type = val.t; + e->value.latinKey = latinKey; + e->value.latinOrIntValue = latinOrIntValue; + e->value.value = QJsonPrivate::Value::valueToStore(val, (char *)e - (char *)object.o + valueOffset); + QJsonPrivate::copyString((char *)(e + 1), key, latinKey); + if (valueSize) + QJsonPrivate::Value::copyData(val, (char *)e + valueOffset, latinOrIntValue); + + offsets << currentOffset; + currentOffset += requiredSize; + object.o->size = currentOffset; + } + + // write table + object.o->tableOffset = currentOffset; + if (!object.detach2(sizeof(QJsonPrivate::offset)*offsets.size())) + return QJsonObject(); + memcpy(object.o->table(), offsets.constData(), offsets.size()*sizeof(uint)); + object.o->length = offsets.size(); + object.o->size = currentOffset + sizeof(QJsonPrivate::offset)*offsets.size(); + + return object; +} + +/*! + Converts this object to a QVariantMap. + + Returns the created map. + + \sa toVariantHash() + */ +QVariantMap QJsonObject::toVariantMap() const +{ + QVariantMap map; + if (o) { + for (uint i = 0; i < o->length; ++i) { + QJsonPrivate::Entry *e = o->entryAt(i); + map.insert(e->key(), QJsonValue(d, o, e->value).toVariant()); + } + } + return map; +} + +/*! + Converts the variant hash \a hash to a QJsonObject. + \since 5.5 + + The keys in \a hash will be used as the keys in the JSON object, + and the QVariant values will be converted to JSON values. + + \sa fromVariantMap(), toVariantHash(), QJsonValue::fromVariant() + */ +QJsonObject QJsonObject::fromVariantHash(const QVariantHash &hash) +{ + // ### this is implemented the trivial way, not the most efficient way + + QJsonObject object; + for (QVariantHash::const_iterator it = hash.constBegin(); it != hash.constEnd(); ++it) + object.insert(it.key(), QJsonValue::fromVariant(it.value())); + return object; +} + +/*! + Converts this object to a QVariantHash. + \since 5.5 + + Returns the created hash. + + \sa toVariantMap() + */ +QVariantHash QJsonObject::toVariantHash() const +{ + QVariantHash hash; + if (o) { + hash.reserve(o->length); + for (uint i = 0; i < o->length; ++i) { + QJsonPrivate::Entry *e = o->entryAt(i); + hash.insert(e->key(), QJsonValue(d, o, e->value).toVariant()); + } + } + return hash; +} + +/*! + Returns a list of all keys in this object. + + The list is sorted lexographically. + */ +QStringList QJsonObject::keys() const +{ + QStringList keys; + if (o) { + keys.reserve(o->length); + for (uint i = 0; i < o->length; ++i) { + QJsonPrivate::Entry *e = o->entryAt(i); + keys.append(e->key()); + } + } + return keys; +} + +/*! + Returns the number of (key, value) pairs stored in the object. + */ +int QJsonObject::size() const +{ + if (!d) + return 0; + + return o->length; +} + +/*! + Returns \c true if the object is empty. This is the same as size() == 0. + + \sa size() + */ +bool QJsonObject::isEmpty() const +{ + if (!d) + return true; + + return !o->length; +} + +/*! + Returns a QJsonValue representing the value for the key \a key. + + The returned QJsonValue is QJsonValue::Undefined if the key does not exist. + + \sa QJsonValue, QJsonValue::isUndefined() + */ +QJsonValue QJsonObject::value(const QString &key) const +{ + if (!d) + return QJsonValue(QJsonValue::Undefined); + + bool keyExists; + int i = o->indexOf(key, &keyExists); + if (!keyExists) + return QJsonValue(QJsonValue::Undefined); + return QJsonValue(d, o, o->entryAt(i)->value); +} + +/*! + \overload + \since 5.7 +*/ +QJsonValue QJsonObject::value(QLatin1String key) const +{ + if (!d) + return QJsonValue(QJsonValue::Undefined); + + bool keyExists; + int i = o->indexOf(key, &keyExists); + if (!keyExists) + return QJsonValue(QJsonValue::Undefined); + return QJsonValue(d, o, o->entryAt(i)->value); +} + +/*! + Returns a QJsonValue representing the value for the key \a key. + + This does the same as value(). + + The returned QJsonValue is QJsonValue::Undefined if the key does not exist. + + \sa value(), QJsonValue, QJsonValue::isUndefined() + */ +QJsonValue QJsonObject::operator [](const QString &key) const +{ + return value(key); +} + +/*! + \fn QJsonValue QJsonObject::operator [](QLatin1String key) const + + \overload + \since 5.7 +*/ + +/*! + Returns a reference to the value for \a key. + + The return value is of type QJsonValueRef, a helper class for QJsonArray + and QJsonObject. When you get an object of type QJsonValueRef, you can + use it as if it were a reference to a QJsonValue. If you assign to it, + the assignment will apply to the element in the QJsonArray or QJsonObject + from which you got the reference. + + \sa value() + */ +QJsonValueRef QJsonObject::operator [](const QString &key) +{ + // ### somewhat inefficient, as we lookup the key twice if it doesn't yet exist + bool keyExists = false; + int index = o ? o->indexOf(key, &keyExists) : -1; + if (!keyExists) { + iterator i = insert(key, QJsonValue()); + index = i.i; + } + return QJsonValueRef(this, index); +} + +/*! + \overload + \since 5.7 +*/ +QJsonValueRef QJsonObject::operator [](QLatin1String key) +{ + // ### optimize me + return operator[](QString(key)); +} + +/*! + Inserts a new item with the key \a key and a value of \a value. + + If there is already an item with the key \a key, then that item's value + is replaced with \a value. + + Returns an iterator pointing to the inserted item. + + If the value is QJsonValue::Undefined, it will cause the key to get removed + from the object. The returned iterator will then point to end(). + + \sa remove(), take(), QJsonObject::iterator, end() + */ +QJsonObject::iterator QJsonObject::insert(const QString &key, const QJsonValue &value) +{ + if (value.t == QJsonValue::Undefined) { + remove(key); + return end(); + } + QJsonValue val = value; + + bool latinOrIntValue; + int valueSize = QJsonPrivate::Value::requiredStorage(val, &latinOrIntValue); + + bool latinKey = QJsonPrivate::useCompressed(key); + int valueOffset = sizeof(QJsonPrivate::Entry) + QJsonPrivate::qStringSize(key, latinKey); + int requiredSize = valueOffset + valueSize; + + if (!detach2(requiredSize + sizeof(QJsonPrivate::offset))) // offset for the new index entry + return iterator(); + + if (!o->length) + o->tableOffset = sizeof(QJsonPrivate::Object); + + bool keyExists = false; + int pos = o->indexOf(key, &keyExists); + if (keyExists) + ++d->compactionCounter; + + uint off = o->reserveSpace(requiredSize, pos, 1, keyExists); + if (!off) + return end(); + + QJsonPrivate::Entry *e = o->entryAt(pos); + e->value.type = val.t; + e->value.latinKey = latinKey; + e->value.latinOrIntValue = latinOrIntValue; + e->value.value = QJsonPrivate::Value::valueToStore(val, (char *)e - (char *)o + valueOffset); + QJsonPrivate::copyString((char *)(e + 1), key, latinKey); + if (valueSize) + QJsonPrivate::Value::copyData(val, (char *)e + valueOffset, latinOrIntValue); + + if (d->compactionCounter > 32u && d->compactionCounter >= unsigned(o->length) / 2u) + compact(); + + return iterator(this, pos); +} + +/*! + Removes \a key from the object. + + \sa insert(), take() + */ +void QJsonObject::remove(const QString &key) +{ + if (!d) + return; + + bool keyExists; + int index = o->indexOf(key, &keyExists); + if (!keyExists) + return; + + detach2(); + o->removeItems(index, 1); + ++d->compactionCounter; + if (d->compactionCounter > 32u && d->compactionCounter >= unsigned(o->length) / 2u) + compact(); +} + +/*! + Removes \a key from the object. + + Returns a QJsonValue containing the value referenced by \a key. + If \a key was not contained in the object, the returned QJsonValue + is QJsonValue::Undefined. + + \sa insert(), remove(), QJsonValue + */ +QJsonValue QJsonObject::take(const QString &key) +{ + if (!o) + return QJsonValue(QJsonValue::Undefined); + + bool keyExists; + int index = o->indexOf(key, &keyExists); + if (!keyExists) + return QJsonValue(QJsonValue::Undefined); + + QJsonValue v(d, o, o->entryAt(index)->value); + detach2(); + o->removeItems(index, 1); + ++d->compactionCounter; + if (d->compactionCounter > 32u && d->compactionCounter >= unsigned(o->length) / 2u) + compact(); + + return v; +} + +/*! + Returns \c true if the object contains key \a key. + + \sa insert(), remove(), take() + */ +bool QJsonObject::contains(const QString &key) const +{ + if (!o) + return false; + + bool keyExists; + o->indexOf(key, &keyExists); + return keyExists; +} + +/*! + \overload + \since 5.7 +*/ +bool QJsonObject::contains(QLatin1String key) const +{ + if (!o) + return false; + + bool keyExists; + o->indexOf(key, &keyExists); + return keyExists; +} + +/*! + Returns \c true if \a other is equal to this object. + */ +bool QJsonObject::operator==(const QJsonObject &other) const +{ + if (o == other.o) + return true; + + if (!o) + return !other.o->length; + if (!other.o) + return !o->length; + if (o->length != other.o->length) + return false; + + for (uint i = 0; i < o->length; ++i) { + QJsonPrivate::Entry *e = o->entryAt(i); + QJsonValue v(d, o, e->value); + if (other.value(e->key()) != v) + return false; + } + + return true; +} + +/*! + Returns \c true if \a other is not equal to this object. + */ +bool QJsonObject::operator!=(const QJsonObject &other) const +{ + return !(*this == other); +} + +/*! + Removes the (key, value) pair pointed to by the iterator \a it + from the map, and returns an iterator to the next item in the + map. + + \sa remove() + */ +QJsonObject::iterator QJsonObject::erase(QJsonObject::iterator it) +{ + Q_ASSERT(d && d->ref.load() == 1); + if (it.o != this || it.i < 0 || it.i >= (int)o->length) + return iterator(this, o->length); + + int index = it.i; + + o->removeItems(index, 1); + ++d->compactionCounter; + if (d->compactionCounter > 32u && d->compactionCounter >= unsigned(o->length) / 2u) + compact(); + + // iterator hasn't changed + return it; +} + +/*! + Returns an iterator pointing to the item with key \a key in the + map. + + If the map contains no item with key \a key, the function + returns end(). + */ +QJsonObject::iterator QJsonObject::find(const QString &key) +{ + bool keyExists = false; + int index = o ? o->indexOf(key, &keyExists) : 0; + if (!keyExists) + return end(); + detach2(); + return iterator(this, index); +} + +/*! + \overload + \since 5.7 +*/ +QJsonObject::iterator QJsonObject::find(QLatin1String key) +{ + bool keyExists = false; + int index = o ? o->indexOf(key, &keyExists) : 0; + if (!keyExists) + return end(); + detach2(); + return iterator(this, index); +} + +/*! \fn QJsonObject::const_iterator QJsonObject::find(const QString &key) const + + \overload +*/ + +/*! \fn QJsonObject::const_iterator QJsonObject::find(QLatin1String key) const + + \overload + \since 5.7 +*/ + +/*! + Returns a const iterator pointing to the item with key \a key in the + map. + + If the map contains no item with key \a key, the function + returns constEnd(). + */ +QJsonObject::const_iterator QJsonObject::constFind(const QString &key) const +{ + bool keyExists = false; + int index = o ? o->indexOf(key, &keyExists) : 0; + if (!keyExists) + return end(); + return const_iterator(this, index); +} + +/*! + \overload + \since 5.7 +*/ +QJsonObject::const_iterator QJsonObject::constFind(QLatin1String key) const +{ + bool keyExists = false; + int index = o ? o->indexOf(key, &keyExists) : 0; + if (!keyExists) + return end(); + return const_iterator(this, index); +} + +/*! \fn int QJsonObject::count() const + + \overload + + Same as size(). +*/ + +/*! \fn int QJsonObject::length() const + + \overload + + Same as size(). +*/ + +/*! \fn QJsonObject::iterator QJsonObject::begin() + + Returns an \l{STL-style iterators}{STL-style iterator} pointing to the first item in + the object. + + \sa constBegin(), end() +*/ + +/*! \fn QJsonObject::const_iterator QJsonObject::begin() const + + \overload +*/ + +/*! \fn QJsonObject::const_iterator QJsonObject::constBegin() const + + Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the first item + in the object. + + \sa begin(), constEnd() +*/ + +/*! \fn QJsonObject::iterator QJsonObject::end() + + Returns an \l{STL-style iterators}{STL-style iterator} pointing to the imaginary item + after the last item in the object. + + \sa begin(), constEnd() +*/ + +/*! \fn QJsonObject::const_iterator QJsonObject::end() const + + \overload +*/ + +/*! \fn QJsonObject::const_iterator QJsonObject::constEnd() const + + Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the imaginary + item after the last item in the object. + + \sa constBegin(), end() +*/ + +/*! + \fn bool QJsonObject::empty() const + + This function is provided for STL compatibility. It is equivalent + to isEmpty(), returning \c true if the object is empty; otherwise + returning \c false. +*/ + +/*! \class QJsonObject::iterator + \inmodule QtCore + \ingroup json + \reentrant + \since 5.0 + + \brief The QJsonObject::iterator class provides an STL-style non-const iterator for QJsonObject. + + QJsonObject::iterator allows you to iterate over a QJsonObject + and to modify the value (but not the key) stored under + a particular key. If you want to iterate over a const QJsonObject, you + should use QJsonObject::const_iterator. It is generally good practice to + use QJsonObject::const_iterator on a non-const QJsonObject as well, unless you + need to change the QJsonObject through the iterator. Const iterators are + slightly faster, and improve code readability. + + The default QJsonObject::iterator constructor creates an uninitialized + iterator. You must initialize it using a QJsonObject function like + QJsonObject::begin(), QJsonObject::end(), or QJsonObject::find() before you can + start iterating. + + Multiple iterators can be used on the same object. Existing iterators will however + become dangling once the object gets modified. + + \sa QJsonObject::const_iterator, {JSON Support in Qt}, {JSON Save Game Example} +*/ + +/*! \typedef QJsonObject::iterator::difference_type + + \internal +*/ + +/*! \typedef QJsonObject::iterator::iterator_category + + A synonym for \e {std::random_access_iterator_tag} indicating + this iterator is a random-access iterator. + + \note In Qt versions before 5.6, this was set by mistake to + \e {std::bidirectional_iterator_tag}. +*/ + +/*! \typedef QJsonObject::iterator::reference + + \internal +*/ + +/*! \typedef QJsonObject::iterator::value_type + + \internal +*/ + +/*! \typedef QJsonObject::iterator::pointer + + \internal +*/ + +/*! \fn QJsonObject::iterator::iterator() + + Constructs an uninitialized iterator. + + Functions like key(), value(), and operator++() must not be + called on an uninitialized iterator. Use operator=() to assign a + value to it before using it. + + \sa QJsonObject::begin(), QJsonObject::end() +*/ + +/*! \fn QJsonObject::iterator::iterator(QJsonObject *obj, int index) + \internal +*/ + +/*! \fn QString QJsonObject::iterator::key() const + + Returns the current item's key. + + There is no direct way of changing an item's key through an + iterator, although it can be done by calling QJsonObject::erase() + followed by QJsonObject::insert(). + + \sa value() +*/ + +/*! \fn QJsonValueRef QJsonObject::iterator::value() const + + Returns a modifiable reference to the current item's value. + + You can change the value of an item by using value() on + the left side of an assignment. + + The return value is of type QJsonValueRef, a helper class for QJsonArray + and QJsonObject. When you get an object of type QJsonValueRef, you can + use it as if it were a reference to a QJsonValue. If you assign to it, + the assignment will apply to the element in the QJsonArray or QJsonObject + from which you got the reference. + + \sa key(), operator*() +*/ + +/*! \fn QJsonValueRef QJsonObject::iterator::operator*() const + + Returns a modifiable reference to the current item's value. + + Same as value(). + + The return value is of type QJsonValueRef, a helper class for QJsonArray + and QJsonObject. When you get an object of type QJsonValueRef, you can + use it as if it were a reference to a QJsonValue. If you assign to it, + the assignment will apply to the element in the QJsonArray or QJsonObject + from which you got the reference. + + \sa key() +*/ + +/*! \fn QJsonValueRef *QJsonObject::iterator::operator->() const + + Returns a pointer to a modifiable reference to the current item. +*/ + +/*! + \fn bool QJsonObject::iterator::operator==(const iterator &other) const + \fn bool QJsonObject::iterator::operator==(const const_iterator &other) const + + Returns \c true if \a other points to the same item as this + iterator; otherwise returns \c false. + + \sa operator!=() +*/ + +/*! + \fn bool QJsonObject::iterator::operator!=(const iterator &other) const + \fn bool QJsonObject::iterator::operator!=(const const_iterator &other) const + + Returns \c true if \a other points to a different item than this + iterator; otherwise returns \c false. + + \sa operator==() +*/ + +/*! \fn QJsonObject::iterator QJsonObject::iterator::operator++() + + The prefix ++ operator, \c{++i}, advances the iterator to the + next item in the object and returns an iterator to the new current + item. + + Calling this function on QJsonObject::end() leads to undefined results. + + \sa operator--() +*/ + +/*! \fn QJsonObject::iterator QJsonObject::iterator::operator++(int) + + \overload + + The postfix ++ operator, \c{i++}, advances the iterator to the + next item in the object and returns an iterator to the previously + current item. +*/ + +/*! \fn QJsonObject::iterator QJsonObject::iterator::operator--() + + The prefix -- operator, \c{--i}, makes the preceding item + current and returns an iterator pointing to the new current item. + + Calling this function on QJsonObject::begin() leads to undefined + results. + + \sa operator++() +*/ + +/*! \fn QJsonObject::iterator QJsonObject::iterator::operator--(int) + + \overload + + The postfix -- operator, \c{i--}, makes the preceding item + current and returns an iterator pointing to the previously + current item. +*/ + +/*! \fn QJsonObject::iterator QJsonObject::iterator::operator+(int j) const + + Returns an iterator to the item at \a j positions forward from + this iterator. If \a j is negative, the iterator goes backward. + + \sa operator-() + +*/ + +/*! \fn QJsonObject::iterator QJsonObject::iterator::operator-(int j) const + + Returns an iterator to the item at \a j positions backward from + this iterator. If \a j is negative, the iterator goes forward. + + \sa operator+() +*/ + +/*! \fn QJsonObject::iterator &QJsonObject::iterator::operator+=(int j) + + Advances the iterator by \a j items. If \a j is negative, the + iterator goes backward. + + \sa operator-=(), operator+() +*/ + +/*! \fn QJsonObject::iterator &QJsonObject::iterator::operator-=(int j) + + Makes the iterator go back by \a j items. If \a j is negative, + the iterator goes forward. + + \sa operator+=(), operator-() +*/ + +/*! + \class QJsonObject::const_iterator + \inmodule QtCore + \ingroup json + \since 5.0 + \brief The QJsonObject::const_iterator class provides an STL-style const iterator for QJsonObject. + + QJsonObject::const_iterator allows you to iterate over a QJsonObject. + If you want to modify the QJsonObject as you iterate + over it, you must use QJsonObject::iterator instead. It is generally + good practice to use QJsonObject::const_iterator on a non-const QJsonObject as + well, unless you need to change the QJsonObject through the iterator. + Const iterators are slightly faster and improve code + readability. + + The default QJsonObject::const_iterator constructor creates an + uninitialized iterator. You must initialize it using a QJsonObject + function like QJsonObject::constBegin(), QJsonObject::constEnd(), or + QJsonObject::find() before you can start iterating. + + Multiple iterators can be used on the same object. Existing iterators + will however become dangling if the object gets modified. + + \sa QJsonObject::iterator, {JSON Support in Qt}, {JSON Save Game Example} +*/ + +/*! \typedef QJsonObject::const_iterator::difference_type + + \internal +*/ + +/*! \typedef QJsonObject::const_iterator::iterator_category + + A synonym for \e {std::random_access_iterator_tag} indicating + this iterator is a random-access iterator. + + \note In Qt versions before 5.6, this was set by mistake to + \e {std::bidirectional_iterator_tag}. +*/ + +/*! \typedef QJsonObject::const_iterator::reference + + \internal +*/ + +/*! \typedef QJsonObject::const_iterator::value_type + + \internal +*/ + +/*! \typedef QJsonObject::const_iterator::pointer + + \internal +*/ + +/*! \fn QJsonObject::const_iterator::const_iterator() + + Constructs an uninitialized iterator. + + Functions like key(), value(), and operator++() must not be + called on an uninitialized iterator. Use operator=() to assign a + value to it before using it. + + \sa QJsonObject::constBegin(), QJsonObject::constEnd() +*/ + +/*! \fn QJsonObject::const_iterator::const_iterator(const QJsonObject *obj, int index) + \internal +*/ + +/*! \fn QJsonObject::const_iterator::const_iterator(const iterator &other) + + Constructs a copy of \a other. +*/ + +/*! \fn QString QJsonObject::const_iterator::key() const + + Returns the current item's key. + + \sa value() +*/ + +/*! \fn QJsonValue QJsonObject::const_iterator::value() const + + Returns the current item's value. + + \sa key(), operator*() +*/ + +/*! \fn QJsonValue QJsonObject::const_iterator::operator*() const + + Returns the current item's value. + + Same as value(). + + \sa key() +*/ + +/*! \fn QJsonValue *QJsonObject::const_iterator::operator->() const + + Returns a pointer to the current item. +*/ + +/*! \fn bool QJsonObject::const_iterator::operator==(const const_iterator &other) const + \fn bool QJsonObject::const_iterator::operator==(const iterator &other) const + + Returns \c true if \a other points to the same item as this + iterator; otherwise returns \c false. + + \sa operator!=() +*/ + +/*! \fn bool QJsonObject::const_iterator::operator!=(const const_iterator &other) const + \fn bool QJsonObject::const_iterator::operator!=(const iterator &other) const + + Returns \c true if \a other points to a different item than this + iterator; otherwise returns \c false. + + \sa operator==() +*/ + +/*! \fn QJsonObject::const_iterator QJsonObject::const_iterator::operator++() + + The prefix ++ operator, \c{++i}, advances the iterator to the + next item in the object and returns an iterator to the new current + item. + + Calling this function on QJsonObject::end() leads to undefined results. + + \sa operator--() +*/ + +/*! \fn QJsonObject::const_iterator QJsonObject::const_iterator::operator++(int) + + \overload + + The postfix ++ operator, \c{i++}, advances the iterator to the + next item in the object and returns an iterator to the previously + current item. +*/ + +/*! \fn QJsonObject::const_iterator &QJsonObject::const_iterator::operator--() + + The prefix -- operator, \c{--i}, makes the preceding item + current and returns an iterator pointing to the new current item. + + Calling this function on QJsonObject::begin() leads to undefined + results. + + \sa operator++() +*/ + +/*! \fn QJsonObject::const_iterator QJsonObject::const_iterator::operator--(int) + + \overload + + The postfix -- operator, \c{i--}, makes the preceding item + current and returns an iterator pointing to the previously + current item. +*/ + +/*! \fn QJsonObject::const_iterator QJsonObject::const_iterator::operator+(int j) const + + Returns an iterator to the item at \a j positions forward from + this iterator. If \a j is negative, the iterator goes backward. + + This operation can be slow for large \a j values. + + \sa operator-() +*/ + +/*! \fn QJsonObject::const_iterator QJsonObject::const_iterator::operator-(int j) const + + Returns an iterator to the item at \a j positions backward from + this iterator. If \a j is negative, the iterator goes forward. + + This operation can be slow for large \a j values. + + \sa operator+() +*/ + +/*! \fn QJsonObject::const_iterator &QJsonObject::const_iterator::operator+=(int j) + + Advances the iterator by \a j items. If \a j is negative, the + iterator goes backward. + + This operation can be slow for large \a j values. + + \sa operator-=(), operator+() +*/ + +/*! \fn QJsonObject::const_iterator &QJsonObject::const_iterator::operator-=(int j) + + Makes the iterator go back by \a j items. If \a j is negative, + the iterator goes forward. + + This operation can be slow for large \a j values. + + \sa operator+=(), operator-() +*/ + + +/*! + \internal + */ +void QJsonObject::detach(uint reserve) +{ + Q_UNUSED(reserve) + Q_ASSERT(!reserve); + detach2(reserve); +} + +bool QJsonObject::detach2(uint reserve) +{ + if (!d) { + if (reserve >= QJsonPrivate::Value::MaxSize) { + qWarning("QJson: Document too large to store in data structure"); + return false; + } + d = new QJsonPrivate::Data(reserve, QJsonValue::Object); + o = static_cast(d->header->root()); + d->ref.ref(); + return true; + } + if (reserve == 0 && d->ref.load() == 1) + return true; + + QJsonPrivate::Data *x = d->clone(o, reserve); + if (!x) + return false; + x->ref.ref(); + if (!d->ref.deref()) + delete d; + d = x; + o = static_cast(d->header->root()); + return true; +} + +/*! + \internal + */ +void QJsonObject::compact() +{ + if (!d || !d->compactionCounter) + return; + + detach2(); + d->compact(); + o = static_cast(d->header->root()); +} + +/*! + \internal + */ +QString QJsonObject::keyAt(int i) const +{ + Q_ASSERT(o && i >= 0 && i < (int)o->length); + + QJsonPrivate::Entry *e = o->entryAt(i); + return e->key(); +} + +/*! + \internal + */ +QJsonValue QJsonObject::valueAt(int i) const +{ + if (!o || i < 0 || i >= (int)o->length) + return QJsonValue(QJsonValue::Undefined); + + QJsonPrivate::Entry *e = o->entryAt(i); + return QJsonValue(d, o, e->value); +} + +/*! + \internal + */ +void QJsonObject::setValueAt(int i, const QJsonValue &val) +{ + Q_ASSERT(o && i >= 0 && i < (int)o->length); + + QJsonPrivate::Entry *e = o->entryAt(i); + insert(e->key(), val); +} + +#if !defined(QT_NO_DEBUG_STREAM) && !defined(QT_JSON_READONLY) +QDebug operator<<(QDebug dbg, const QJsonObject &o) +{ + QDebugStateSaver saver(dbg); + if (!o.o) { + dbg << "QJsonObject()"; + return dbg; + } + QByteArray json; + QJsonPrivate::Writer::objectToJson(o.o, json, 0, true); + dbg.nospace() << "QJsonObject(" + << json.constData() // print as utf-8 string without extra quotation marks + << ")"; + return dbg; +} +#endif + +QT_END_NAMESPACE diff --git a/src/corelib/serialization/qjsonobject.h b/src/corelib/serialization/qjsonobject.h new file mode 100644 index 0000000000..610bce694c --- /dev/null +++ b/src/corelib/serialization/qjsonobject.h @@ -0,0 +1,271 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QJSONOBJECT_H +#define QJSONOBJECT_H + +#include +#include +#ifdef Q_COMPILER_INITIALIZER_LISTS +#include +#include +#endif + +QT_BEGIN_NAMESPACE + +class QDebug; +template class QMap; +typedef QMap QVariantMap; +template class QHash; +typedef QHash QVariantHash; + +class Q_CORE_EXPORT QJsonObject +{ +public: + QJsonObject(); + +#if defined(Q_COMPILER_INITIALIZER_LISTS) || defined(Q_QDOC) + QJsonObject(std::initializer_list > args) + { + initialize(); + for (std::initializer_list >::const_iterator i = args.begin(); i != args.end(); ++i) + insert(i->first, i->second); + } +#endif + + ~QJsonObject(); + + QJsonObject(const QJsonObject &other); + QJsonObject &operator =(const QJsonObject &other); + + QJsonObject(QJsonObject &&other) Q_DECL_NOTHROW + : d(other.d), o(other.o) + { + other.d = nullptr; + other.o = nullptr; + } + + QJsonObject &operator =(QJsonObject &&other) Q_DECL_NOTHROW + { + swap(other); + return *this; + } + + void swap(QJsonObject &other) Q_DECL_NOTHROW + { + qSwap(d, other.d); + qSwap(o, other.o); + } + + static QJsonObject fromVariantMap(const QVariantMap &map); + QVariantMap toVariantMap() const; + static QJsonObject fromVariantHash(const QVariantHash &map); + QVariantHash toVariantHash() const; + + QStringList keys() const; + int size() const; + inline int count() const { return size(); } + inline int length() const { return size(); } + bool isEmpty() const; + + QJsonValue value(const QString &key) const; + QJsonValue value(QLatin1String key) const; + QJsonValue operator[] (const QString &key) const; + QJsonValue operator[] (QLatin1String key) const { return value(key); } + QJsonValueRef operator[] (const QString &key); + QJsonValueRef operator[] (QLatin1String key); + + void remove(const QString &key); + QJsonValue take(const QString &key); + bool contains(const QString &key) const; + bool contains(QLatin1String key) const; + + bool operator==(const QJsonObject &other) const; + bool operator!=(const QJsonObject &other) const; + + class const_iterator; + + class iterator + { + friend class const_iterator; + friend class QJsonObject; + QJsonObject *o; + int i; + + public: + typedef std::random_access_iterator_tag iterator_category; + typedef int difference_type; + typedef QJsonValue value_type; + typedef QJsonValueRef reference; + typedef QJsonValuePtr pointer; + + Q_DECL_CONSTEXPR inline iterator() : o(nullptr), i(0) {} + Q_DECL_CONSTEXPR inline iterator(QJsonObject *obj, int index) : o(obj), i(index) {} + + inline QString key() const { return o->keyAt(i); } + inline QJsonValueRef value() const { return QJsonValueRef(o, i); } + inline QJsonValueRef operator*() const { return QJsonValueRef(o, i); } +#ifdef Q_QDOC + inline QJsonValueRef* operator->() const; +#else + inline QJsonValueRefPtr operator->() const { return QJsonValueRefPtr(o, i); } +#endif + inline bool operator==(const iterator &other) const { return i == other.i; } + inline bool operator!=(const iterator &other) const { return i != other.i; } + + inline iterator &operator++() { ++i; return *this; } + inline iterator operator++(int) { iterator r = *this; ++i; return r; } + inline iterator &operator--() { --i; return *this; } + inline iterator operator--(int) { iterator r = *this; --i; return r; } + inline iterator operator+(int j) const + { iterator r = *this; r.i += j; return r; } + inline iterator operator-(int j) const { return operator+(-j); } + inline iterator &operator+=(int j) { i += j; return *this; } + inline iterator &operator-=(int j) { i -= j; return *this; } + + public: + inline bool operator==(const const_iterator &other) const { return i == other.i; } + inline bool operator!=(const const_iterator &other) const { return i != other.i; } + }; + friend class iterator; + + class const_iterator + { + friend class iterator; + const QJsonObject *o; + int i; + + public: + typedef std::random_access_iterator_tag iterator_category; + typedef int difference_type; + typedef QJsonValue value_type; + typedef QJsonValue reference; + typedef QJsonValuePtr pointer; + + Q_DECL_CONSTEXPR inline const_iterator() : o(nullptr), i(0) {} + Q_DECL_CONSTEXPR inline const_iterator(const QJsonObject *obj, int index) + : o(obj), i(index) {} + inline const_iterator(const iterator &other) + : o(other.o), i(other.i) {} + + inline QString key() const { return o->keyAt(i); } + inline QJsonValue value() const { return o->valueAt(i); } + inline QJsonValue operator*() const { return o->valueAt(i); } +#ifdef Q_QDOC + inline QJsonValue* operator->() const; +#else + inline QJsonValuePtr operator->() const { return QJsonValuePtr(o->valueAt(i)); } +#endif + inline bool operator==(const const_iterator &other) const { return i == other.i; } + inline bool operator!=(const const_iterator &other) const { return i != other.i; } + + inline const_iterator &operator++() { ++i; return *this; } + inline const_iterator operator++(int) { const_iterator r = *this; ++i; return r; } + inline const_iterator &operator--() { --i; return *this; } + inline const_iterator operator--(int) { const_iterator r = *this; --i; return r; } + inline const_iterator operator+(int j) const + { const_iterator r = *this; r.i += j; return r; } + inline const_iterator operator-(int j) const { return operator+(-j); } + inline const_iterator &operator+=(int j) { i += j; return *this; } + inline const_iterator &operator-=(int j) { i -= j; return *this; } + + inline bool operator==(const iterator &other) const { return i == other.i; } + inline bool operator!=(const iterator &other) const { return i != other.i; } + }; + friend class const_iterator; + + // STL style + inline iterator begin() { detach2(); return iterator(this, 0); } + inline const_iterator begin() const { return const_iterator(this, 0); } + inline const_iterator constBegin() const { return const_iterator(this, 0); } + inline iterator end() { detach2(); return iterator(this, size()); } + inline const_iterator end() const { return const_iterator(this, size()); } + inline const_iterator constEnd() const { return const_iterator(this, size()); } + iterator erase(iterator it); + + // more Qt + typedef iterator Iterator; + typedef const_iterator ConstIterator; + iterator find(const QString &key); + iterator find(QLatin1String key); + const_iterator find(const QString &key) const { return constFind(key); } + const_iterator find(QLatin1String key) const { return constFind(key); } + const_iterator constFind(const QString &key) const; + const_iterator constFind(QLatin1String key) const; + iterator insert(const QString &key, const QJsonValue &value); + + // STL compatibility + typedef QJsonValue mapped_type; + typedef QString key_type; + typedef int size_type; + + inline bool empty() const { return isEmpty(); } + +private: + friend class QJsonPrivate::Data; + friend class QJsonValue; + friend class QJsonDocument; + friend class QJsonValueRef; + + friend Q_CORE_EXPORT QDebug operator<<(QDebug, const QJsonObject &); + + QJsonObject(QJsonPrivate::Data *data, QJsonPrivate::Object *object); + void initialize(); + // ### Qt 6: remove me and merge with detach2 + void detach(uint reserve = 0); + bool detach2(uint reserve = 0); + void compact(); + + QString keyAt(int i) const; + QJsonValue valueAt(int i) const; + void setValueAt(int i, const QJsonValue &val); + + QJsonPrivate::Data *d; + QJsonPrivate::Object *o; +}; + +Q_DECLARE_SHARED_NOT_MOVABLE_UNTIL_QT6(QJsonObject) + +#if !defined(QT_NO_DEBUG_STREAM) && !defined(QT_JSON_READONLY) +Q_CORE_EXPORT QDebug operator<<(QDebug, const QJsonObject &); +#endif + +QT_END_NAMESPACE + +#endif // QJSONOBJECT_H diff --git a/src/corelib/serialization/qjsonparser.cpp b/src/corelib/serialization/qjsonparser.cpp new file mode 100644 index 0000000000..39738b90a8 --- /dev/null +++ b/src/corelib/serialization/qjsonparser.cpp @@ -0,0 +1,1027 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2016 Intel Corporation. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT_BOOTSTRAPPED +#include +#endif +#include +#include "qjsonparser_p.h" +#include "qjson_p.h" +#include "private/qutfcodec_p.h" + +//#define PARSER_DEBUG +#ifdef PARSER_DEBUG +static int indent = 0; +#define BEGIN qDebug() << QByteArray(4*indent++, ' ').constData() << "pos=" << current +#define END --indent +#define DEBUG qDebug() << QByteArray(4*indent, ' ').constData() +#else +#define BEGIN if (1) ; else qDebug() +#define END do {} while (0) +#define DEBUG if (1) ; else qDebug() +#endif + +static const int nestingLimit = 1024; + +QT_BEGIN_NAMESPACE + +// error strings for the JSON parser +#define JSONERR_OK QT_TRANSLATE_NOOP("QJsonParseError", "no error occurred") +#define JSONERR_UNTERM_OBJ QT_TRANSLATE_NOOP("QJsonParseError", "unterminated object") +#define JSONERR_MISS_NSEP QT_TRANSLATE_NOOP("QJsonParseError", "missing name separator") +#define JSONERR_UNTERM_AR QT_TRANSLATE_NOOP("QJsonParseError", "unterminated array") +#define JSONERR_MISS_VSEP QT_TRANSLATE_NOOP("QJsonParseError", "missing value separator") +#define JSONERR_ILLEGAL_VAL QT_TRANSLATE_NOOP("QJsonParseError", "illegal value") +#define JSONERR_END_OF_NUM QT_TRANSLATE_NOOP("QJsonParseError", "invalid termination by number") +#define JSONERR_ILLEGAL_NUM QT_TRANSLATE_NOOP("QJsonParseError", "illegal number") +#define JSONERR_STR_ESC_SEQ QT_TRANSLATE_NOOP("QJsonParseError", "invalid escape sequence") +#define JSONERR_STR_UTF8 QT_TRANSLATE_NOOP("QJsonParseError", "invalid UTF8 string") +#define JSONERR_UTERM_STR QT_TRANSLATE_NOOP("QJsonParseError", "unterminated string") +#define JSONERR_MISS_OBJ QT_TRANSLATE_NOOP("QJsonParseError", "object is missing after a comma") +#define JSONERR_DEEP_NEST QT_TRANSLATE_NOOP("QJsonParseError", "too deeply nested document") +#define JSONERR_DOC_LARGE QT_TRANSLATE_NOOP("QJsonParseError", "too large document") +#define JSONERR_GARBAGEEND QT_TRANSLATE_NOOP("QJsonParseError", "garbage at the end of the document") + +/*! + \class QJsonParseError + \inmodule QtCore + \ingroup json + \ingroup shared + \reentrant + \since 5.0 + + \brief The QJsonParseError class is used to report errors during JSON parsing. + + \sa {JSON Support in Qt}, {JSON Save Game Example} +*/ + +/*! + \enum QJsonParseError::ParseError + + This enum describes the type of error that occurred during the parsing of a JSON document. + + \value NoError No error occurred + \value UnterminatedObject An object is not correctly terminated with a closing curly bracket + \value MissingNameSeparator A comma separating different items is missing + \value UnterminatedArray The array is not correctly terminated with a closing square bracket + \value MissingValueSeparator A colon separating keys from values inside objects is missing + \value IllegalValue The value is illegal + \value TerminationByNumber The input stream ended while parsing a number + \value IllegalNumber The number is not well formed + \value IllegalEscapeSequence An illegal escape sequence occurred in the input + \value IllegalUTF8String An illegal UTF8 sequence occurred in the input + \value UnterminatedString A string wasn't terminated with a quote + \value MissingObject An object was expected but couldn't be found + \value DeepNesting The JSON document is too deeply nested for the parser to parse it + \value DocumentTooLarge The JSON document is too large for the parser to parse it + \value GarbageAtEnd The parsed document contains additional garbage characters at the end + +*/ + +/*! + \variable QJsonParseError::error + + Contains the type of the parse error. Is equal to QJsonParseError::NoError if the document + was parsed correctly. + + \sa ParseError, errorString() +*/ + + +/*! + \variable QJsonParseError::offset + + Contains the offset in the input string where the parse error occurred. + + \sa error, errorString() +*/ + +/*! + Returns the human-readable message appropriate to the reported JSON parsing error. + + \sa error + */ +QString QJsonParseError::errorString() const +{ + const char *sz = ""; + switch (error) { + case NoError: + sz = JSONERR_OK; + break; + case UnterminatedObject: + sz = JSONERR_UNTERM_OBJ; + break; + case MissingNameSeparator: + sz = JSONERR_MISS_NSEP; + break; + case UnterminatedArray: + sz = JSONERR_UNTERM_AR; + break; + case MissingValueSeparator: + sz = JSONERR_MISS_VSEP; + break; + case IllegalValue: + sz = JSONERR_ILLEGAL_VAL; + break; + case TerminationByNumber: + sz = JSONERR_END_OF_NUM; + break; + case IllegalNumber: + sz = JSONERR_ILLEGAL_NUM; + break; + case IllegalEscapeSequence: + sz = JSONERR_STR_ESC_SEQ; + break; + case IllegalUTF8String: + sz = JSONERR_STR_UTF8; + break; + case UnterminatedString: + sz = JSONERR_UTERM_STR; + break; + case MissingObject: + sz = JSONERR_MISS_OBJ; + break; + case DeepNesting: + sz = JSONERR_DEEP_NEST; + break; + case DocumentTooLarge: + sz = JSONERR_DOC_LARGE; + break; + case GarbageAtEnd: + sz = JSONERR_GARBAGEEND; + break; + } +#ifndef QT_BOOTSTRAPPED + return QCoreApplication::translate("QJsonParseError", sz); +#else + return QLatin1String(sz); +#endif +} + +using namespace QJsonPrivate; + +Parser::Parser(const char *json, int length) + : head(json), json(json), data(0), dataLength(0), current(0), nestingLevel(0), lastError(QJsonParseError::NoError) +{ + end = json + length; +} + + + +/* + +begin-array = ws %x5B ws ; [ left square bracket + +begin-object = ws %x7B ws ; { left curly bracket + +end-array = ws %x5D ws ; ] right square bracket + +end-object = ws %x7D ws ; } right curly bracket + +name-separator = ws %x3A ws ; : colon + +value-separator = ws %x2C ws ; , comma + +Insignificant whitespace is allowed before or after any of the six +structural characters. + +ws = *( + %x20 / ; Space + %x09 / ; Horizontal tab + %x0A / ; Line feed or New line + %x0D ; Carriage return + ) + +*/ + +enum { + Space = 0x20, + Tab = 0x09, + LineFeed = 0x0a, + Return = 0x0d, + BeginArray = 0x5b, + BeginObject = 0x7b, + EndArray = 0x5d, + EndObject = 0x7d, + NameSeparator = 0x3a, + ValueSeparator = 0x2c, + Quote = 0x22 +}; + +void Parser::eatBOM() +{ + // eat UTF-8 byte order mark + uchar utf8bom[3] = { 0xef, 0xbb, 0xbf }; + if (end - json > 3 && + (uchar)json[0] == utf8bom[0] && + (uchar)json[1] == utf8bom[1] && + (uchar)json[2] == utf8bom[2]) + json += 3; +} + +bool Parser::eatSpace() +{ + while (json < end) { + if (*json > Space) + break; + if (*json != Space && + *json != Tab && + *json != LineFeed && + *json != Return) + break; + ++json; + } + return (json < end); +} + +char Parser::nextToken() +{ + if (!eatSpace()) + return 0; + char token = *json++; + switch (token) { + case BeginArray: + case BeginObject: + case NameSeparator: + case ValueSeparator: + case EndArray: + case EndObject: + case Quote: + break; + default: + token = 0; + break; + } + return token; +} + +/* + JSON-text = object / array +*/ +QJsonDocument Parser::parse(QJsonParseError *error) +{ +#ifdef PARSER_DEBUG + indent = 0; + qDebug(">>>>> parser begin"); +#endif + // allocate some space + dataLength = qMax(end - json, (ptrdiff_t) 256); + data = (char *)malloc(dataLength); + + // fill in Header data + QJsonPrivate::Header *h = (QJsonPrivate::Header *)data; + h->tag = QJsonDocument::BinaryFormatTag; + h->version = 1u; + + current = sizeof(QJsonPrivate::Header); + + eatBOM(); + char token = nextToken(); + + DEBUG << hex << (uint)token; + if (token == BeginArray) { + if (!parseArray()) + goto error; + } else if (token == BeginObject) { + if (!parseObject()) + goto error; + } else { + lastError = QJsonParseError::IllegalValue; + goto error; + } + + eatSpace(); + if (json < end) { + lastError = QJsonParseError::GarbageAtEnd; + goto error; + } + + END; + { + if (error) { + error->offset = 0; + error->error = QJsonParseError::NoError; + } + QJsonPrivate::Data *d = new QJsonPrivate::Data(data, current); + return QJsonDocument(d); + } + +error: +#ifdef PARSER_DEBUG + qDebug(">>>>> parser error"); +#endif + if (error) { + error->offset = json - head; + error->error = lastError; + } + free(data); + return QJsonDocument(); +} + + +void Parser::ParsedObject::insert(uint offset) { + const QJsonPrivate::Entry *newEntry = reinterpret_cast(parser->data + objectPosition + offset); + int min = 0; + int n = offsets.size(); + while (n > 0) { + int half = n >> 1; + int middle = min + half; + if (*entryAt(middle) >= *newEntry) { + n = half; + } else { + min = middle + 1; + n -= half + 1; + } + } + if (min < offsets.size() && *entryAt(min) == *newEntry) { + offsets[min] = offset; + } else { + offsets.insert(min, offset); + } +} + +/* + object = begin-object [ member *( value-separator member ) ] + end-object +*/ + +bool Parser::parseObject() +{ + if (++nestingLevel > nestingLimit) { + lastError = QJsonParseError::DeepNesting; + return false; + } + + int objectOffset = reserveSpace(sizeof(QJsonPrivate::Object)); + if (objectOffset < 0) + return false; + BEGIN << "parseObject pos=" << objectOffset << current << json; + + ParsedObject parsedObject(this, objectOffset); + + char token = nextToken(); + while (token == Quote) { + int off = current - objectOffset; + if (!parseMember(objectOffset)) + return false; + parsedObject.insert(off); + token = nextToken(); + if (token != ValueSeparator) + break; + token = nextToken(); + if (token == EndObject) { + lastError = QJsonParseError::MissingObject; + return false; + } + } + + DEBUG << "end token=" << token; + if (token != EndObject) { + lastError = QJsonParseError::UnterminatedObject; + return false; + } + + DEBUG << "numEntries" << parsedObject.offsets.size(); + int table = objectOffset; + // finalize the object + if (parsedObject.offsets.size()) { + int tableSize = parsedObject.offsets.size()*sizeof(uint); + table = reserveSpace(tableSize); + if (table < 0) + return false; + +#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN + memcpy(data + table, parsedObject.offsets.constData(), tableSize); +#else + offset *o = (offset *)(data + table); + for (int i = 0; i < parsedObject.offsets.size(); ++i) + o[i] = parsedObject.offsets[i]; + +#endif + } + + QJsonPrivate::Object *o = (QJsonPrivate::Object *)(data + objectOffset); + o->tableOffset = table - objectOffset; + o->size = current - objectOffset; + o->is_object = true; + o->length = parsedObject.offsets.size(); + + DEBUG << "current=" << current; + END; + + --nestingLevel; + return true; +} + +/* + member = string name-separator value +*/ +bool Parser::parseMember(int baseOffset) +{ + int entryOffset = reserveSpace(sizeof(QJsonPrivate::Entry)); + if (entryOffset < 0) + return false; + BEGIN << "parseMember pos=" << entryOffset; + + bool latin1; + if (!parseString(&latin1)) + return false; + char token = nextToken(); + if (token != NameSeparator) { + lastError = QJsonParseError::MissingNameSeparator; + return false; + } + if (!eatSpace()) { + lastError = QJsonParseError::UnterminatedObject; + return false; + } + QJsonPrivate::Value val; + if (!parseValue(&val, baseOffset)) + return false; + + // finalize the entry + QJsonPrivate::Entry *e = (QJsonPrivate::Entry *)(data + entryOffset); + e->value = val; + e->value.latinKey = latin1; + + END; + return true; +} + +namespace { + struct ValueArray { + static const int prealloc = 128; + ValueArray() : data(stackValues), alloc(prealloc), size(0) {} + ~ValueArray() { if (data != stackValues) free(data); } + + inline bool grow() { + alloc *= 2; + if (data == stackValues) { + QJsonPrivate::Value *newValues = static_cast(malloc(alloc*sizeof(QJsonPrivate::Value))); + if (!newValues) + return false; + memcpy(newValues, data, size*sizeof(QJsonPrivate::Value)); + data = newValues; + } else { + void *newValues = realloc(data, alloc * sizeof(QJsonPrivate::Value)); + if (!newValues) + return false; + data = static_cast(newValues); + } + return true; + } + bool append(const QJsonPrivate::Value &v) { + if (alloc == size && !grow()) + return false; + data[size] = v; + ++size; + return true; + } + + QJsonPrivate::Value stackValues[prealloc]; + QJsonPrivate::Value *data; + int alloc; + int size; + }; +} + +/* + array = begin-array [ value *( value-separator value ) ] end-array +*/ +bool Parser::parseArray() +{ + BEGIN << "parseArray"; + + if (++nestingLevel > nestingLimit) { + lastError = QJsonParseError::DeepNesting; + return false; + } + + int arrayOffset = reserveSpace(sizeof(QJsonPrivate::Array)); + if (arrayOffset < 0) + return false; + + ValueArray values; + + if (!eatSpace()) { + lastError = QJsonParseError::UnterminatedArray; + return false; + } + if (*json == EndArray) { + nextToken(); + } else { + while (1) { + if (!eatSpace()) { + lastError = QJsonParseError::UnterminatedArray; + return false; + } + QJsonPrivate::Value val; + if (!parseValue(&val, arrayOffset)) + return false; + if (!values.append(val)) { + lastError = QJsonParseError::DocumentTooLarge; + return false; + } + char token = nextToken(); + if (token == EndArray) + break; + else if (token != ValueSeparator) { + if (!eatSpace()) + lastError = QJsonParseError::UnterminatedArray; + else + lastError = QJsonParseError::MissingValueSeparator; + return false; + } + } + } + + DEBUG << "size =" << values.size; + int table = arrayOffset; + // finalize the object + if (values.size) { + int tableSize = values.size*sizeof(QJsonPrivate::Value); + table = reserveSpace(tableSize); + if (table < 0) + return false; + memcpy(data + table, values.data, tableSize); + } + + QJsonPrivate::Array *a = (QJsonPrivate::Array *)(data + arrayOffset); + a->tableOffset = table - arrayOffset; + a->size = current - arrayOffset; + a->is_object = false; + a->length = values.size; + + DEBUG << "current=" << current; + END; + + --nestingLevel; + return true; +} + +/* +value = false / null / true / object / array / number / string + +*/ + +bool Parser::parseValue(QJsonPrivate::Value *val, int baseOffset) +{ + BEGIN << "parse Value" << json; + val->_dummy = 0; + + switch (*json++) { + case 'n': + if (end - json < 4) { + lastError = QJsonParseError::IllegalValue; + return false; + } + if (*json++ == 'u' && + *json++ == 'l' && + *json++ == 'l') { + val->type = QJsonValue::Null; + DEBUG << "value: null"; + END; + return true; + } + lastError = QJsonParseError::IllegalValue; + return false; + case 't': + if (end - json < 4) { + lastError = QJsonParseError::IllegalValue; + return false; + } + if (*json++ == 'r' && + *json++ == 'u' && + *json++ == 'e') { + val->type = QJsonValue::Bool; + val->value = true; + DEBUG << "value: true"; + END; + return true; + } + lastError = QJsonParseError::IllegalValue; + return false; + case 'f': + if (end - json < 5) { + lastError = QJsonParseError::IllegalValue; + return false; + } + if (*json++ == 'a' && + *json++ == 'l' && + *json++ == 's' && + *json++ == 'e') { + val->type = QJsonValue::Bool; + val->value = false; + DEBUG << "value: false"; + END; + return true; + } + lastError = QJsonParseError::IllegalValue; + return false; + case Quote: { + val->type = QJsonValue::String; + if (current - baseOffset >= Value::MaxSize) { + lastError = QJsonParseError::DocumentTooLarge; + return false; + } + val->value = current - baseOffset; + bool latin1; + if (!parseString(&latin1)) + return false; + val->latinOrIntValue = latin1; + DEBUG << "value: string"; + END; + return true; + } + case BeginArray: + val->type = QJsonValue::Array; + if (current - baseOffset >= Value::MaxSize) { + lastError = QJsonParseError::DocumentTooLarge; + return false; + } + val->value = current - baseOffset; + if (!parseArray()) + return false; + DEBUG << "value: array"; + END; + return true; + case BeginObject: + val->type = QJsonValue::Object; + if (current - baseOffset >= Value::MaxSize) { + lastError = QJsonParseError::DocumentTooLarge; + return false; + } + val->value = current - baseOffset; + if (!parseObject()) + return false; + DEBUG << "value: object"; + END; + return true; + case ValueSeparator: + // Essentially missing value, but after a colon, not after a comma + // like the other MissingObject errors. + lastError = QJsonParseError::IllegalValue; + return false; + case EndObject: + case EndArray: + lastError = QJsonParseError::MissingObject; + return false; + default: + --json; + if (!parseNumber(val, baseOffset)) + return false; + DEBUG << "value: number"; + END; + } + + return true; +} + + + + + +/* + number = [ minus ] int [ frac ] [ exp ] + decimal-point = %x2E ; . + digit1-9 = %x31-39 ; 1-9 + e = %x65 / %x45 ; e E + exp = e [ minus / plus ] 1*DIGIT + frac = decimal-point 1*DIGIT + int = zero / ( digit1-9 *DIGIT ) + minus = %x2D ; - + plus = %x2B ; + + zero = %x30 ; 0 + +*/ + +bool Parser::parseNumber(QJsonPrivate::Value *val, int baseOffset) +{ + BEGIN << "parseNumber" << json; + val->type = QJsonValue::Double; + + const char *start = json; + bool isInt = true; + + // minus + if (json < end && *json == '-') + ++json; + + // int = zero / ( digit1-9 *DIGIT ) + if (json < end && *json == '0') { + ++json; + } else { + while (json < end && *json >= '0' && *json <= '9') + ++json; + } + + // frac = decimal-point 1*DIGIT + if (json < end && *json == '.') { + isInt = false; + ++json; + while (json < end && *json >= '0' && *json <= '9') + ++json; + } + + // exp = e [ minus / plus ] 1*DIGIT + if (json < end && (*json == 'e' || *json == 'E')) { + isInt = false; + ++json; + if (json < end && (*json == '-' || *json == '+')) + ++json; + while (json < end && *json >= '0' && *json <= '9') + ++json; + } + + if (json >= end) { + lastError = QJsonParseError::TerminationByNumber; + return false; + } + + QByteArray number(start, json - start); + DEBUG << "numberstring" << number; + + if (isInt) { + bool ok; + int n = number.toInt(&ok); + if (ok && n < (1<<25) && n > -(1<<25)) { + val->int_value = n; + val->latinOrIntValue = true; + END; + return true; + } + } + + bool ok; + union { + quint64 ui; + double d; + }; + d = number.toDouble(&ok); + + if (!ok) { + lastError = QJsonParseError::IllegalNumber; + return false; + } + + int pos = reserveSpace(sizeof(double)); + if (pos < 0) + return false; + qToLittleEndian(ui, data + pos); + if (current - baseOffset >= Value::MaxSize) { + lastError = QJsonParseError::DocumentTooLarge; + return false; + } + val->value = pos - baseOffset; + val->latinOrIntValue = false; + + END; + return true; +} + +/* + + string = quotation-mark *char quotation-mark + + char = unescaped / + escape ( + %x22 / ; " quotation mark U+0022 + %x5C / ; \ reverse solidus U+005C + %x2F / ; / solidus U+002F + %x62 / ; b backspace U+0008 + %x66 / ; f form feed U+000C + %x6E / ; n line feed U+000A + %x72 / ; r carriage return U+000D + %x74 / ; t tab U+0009 + %x75 4HEXDIG ) ; uXXXX U+XXXX + + escape = %x5C ; \ + + quotation-mark = %x22 ; " + + unescaped = %x20-21 / %x23-5B / %x5D-10FFFF + */ +static inline bool addHexDigit(char digit, uint *result) +{ + *result <<= 4; + if (digit >= '0' && digit <= '9') + *result |= (digit - '0'); + else if (digit >= 'a' && digit <= 'f') + *result |= (digit - 'a') + 10; + else if (digit >= 'A' && digit <= 'F') + *result |= (digit - 'A') + 10; + else + return false; + return true; +} + +static inline bool scanEscapeSequence(const char *&json, const char *end, uint *ch) +{ + ++json; + if (json >= end) + return false; + + DEBUG << "scan escape" << (char)*json; + uint escaped = *json++; + switch (escaped) { + case '"': + *ch = '"'; break; + case '\\': + *ch = '\\'; break; + case '/': + *ch = '/'; break; + case 'b': + *ch = 0x8; break; + case 'f': + *ch = 0xc; break; + case 'n': + *ch = 0xa; break; + case 'r': + *ch = 0xd; break; + case 't': + *ch = 0x9; break; + case 'u': { + *ch = 0; + if (json > end - 4) + return false; + for (int i = 0; i < 4; ++i) { + if (!addHexDigit(*json, ch)) + return false; + ++json; + } + return true; + } + default: + // this is not as strict as one could be, but allows for more Json files + // to be parsed correctly. + *ch = escaped; + return true; + } + return true; +} + +static inline bool scanUtf8Char(const char *&json, const char *end, uint *result) +{ + const uchar *&src = reinterpret_cast(json); + const uchar *uend = reinterpret_cast(end); + uchar b = *src++; + int res = QUtf8Functions::fromUtf8(b, result, src, uend); + if (res < 0) { + // decoding error, backtrack the character we read above + --json; + return false; + } + + return true; +} + +bool Parser::parseString(bool *latin1) +{ + *latin1 = true; + + const char *start = json; + int outStart = current; + + // try to write out a latin1 string + + int stringPos = reserveSpace(2); + if (stringPos < 0) + return false; + + BEGIN << "parse string stringPos=" << stringPos << json; + while (json < end) { + uint ch = 0; + if (*json == '"') + break; + else if (*json == '\\') { + if (!scanEscapeSequence(json, end, &ch)) { + lastError = QJsonParseError::IllegalEscapeSequence; + return false; + } + } else { + if (!scanUtf8Char(json, end, &ch)) { + lastError = QJsonParseError::IllegalUTF8String; + return false; + } + } + // bail out if the string is not pure latin1 or too long to hold as a latin1string (which has only 16 bit for the length) + if (ch > 0xff || json - start >= 0x8000) { + *latin1 = false; + break; + } + int pos = reserveSpace(1); + if (pos < 0) + return false; + DEBUG << " " << ch << (char)ch; + data[pos] = (uchar)ch; + } + ++json; + DEBUG << "end of string"; + if (json >= end) { + lastError = QJsonParseError::UnterminatedString; + return false; + } + + // no unicode string, we are done + if (*latin1) { + // write string length + *(QJsonPrivate::qle_ushort *)(data + stringPos) = ushort(current - outStart - sizeof(ushort)); + int pos = reserveSpace((4 - current) & 3); + if (pos < 0) + return false; + while (pos & 3) + data[pos++] = 0; + END; + return true; + } + + *latin1 = false; + DEBUG << "not latin"; + + json = start; + current = outStart + sizeof(int); + + while (json < end) { + uint ch = 0; + if (*json == '"') + break; + else if (*json == '\\') { + if (!scanEscapeSequence(json, end, &ch)) { + lastError = QJsonParseError::IllegalEscapeSequence; + return false; + } + } else { + if (!scanUtf8Char(json, end, &ch)) { + lastError = QJsonParseError::IllegalUTF8String; + return false; + } + } + if (QChar::requiresSurrogates(ch)) { + int pos = reserveSpace(4); + if (pos < 0) + return false; + *(QJsonPrivate::qle_ushort *)(data + pos) = QChar::highSurrogate(ch); + *(QJsonPrivate::qle_ushort *)(data + pos + 2) = QChar::lowSurrogate(ch); + } else { + int pos = reserveSpace(2); + if (pos < 0) + return false; + *(QJsonPrivate::qle_ushort *)(data + pos) = (ushort)ch; + } + } + ++json; + + if (json >= end) { + lastError = QJsonParseError::UnterminatedString; + return false; + } + + // write string length + *(QJsonPrivate::qle_int *)(data + stringPos) = (current - outStart - sizeof(int))/2; + int pos = reserveSpace((4 - current) & 3); + if (pos < 0) + return false; + while (pos & 3) + data[pos++] = 0; + END; + return true; +} + +QT_END_NAMESPACE diff --git a/src/corelib/serialization/qjsonparser_p.h b/src/corelib/serialization/qjsonparser_p.h new file mode 100644 index 0000000000..379256847f --- /dev/null +++ b/src/corelib/serialization/qjsonparser_p.h @@ -0,0 +1,128 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QJSONPARSER_P_H +#define QJSONPARSER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +namespace QJsonPrivate { + +class Parser +{ +public: + Parser(const char *json, int length); + + QJsonDocument parse(QJsonParseError *error); + + class ParsedObject + { + public: + ParsedObject(Parser *p, int pos) : parser(p), objectPosition(pos) { + offsets.reserve(64); + } + void insert(uint offset); + + Parser *parser; + int objectPosition; + QVector offsets; + + inline QJsonPrivate::Entry *entryAt(int i) const { + return reinterpret_cast(parser->data + objectPosition + offsets[i]); + } + }; + + +private: + inline void eatBOM(); + inline bool eatSpace(); + inline char nextToken(); + + bool parseObject(); + bool parseArray(); + bool parseMember(int baseOffset); + bool parseString(bool *latin1); + bool parseValue(QJsonPrivate::Value *val, int baseOffset); + bool parseNumber(QJsonPrivate::Value *val, int baseOffset); + const char *head; + const char *json; + const char *end; + + char *data; + int dataLength; + int current; + int nestingLevel; + QJsonParseError::ParseError lastError; + + inline int reserveSpace(int space) { + if (current + space >= dataLength) { + dataLength = 2*dataLength + space; + char *newData = (char *)realloc(data, dataLength); + if (!newData) { + lastError = QJsonParseError::DocumentTooLarge; + return -1; + } + data = newData; + } + int pos = current; + current += space; + return pos; + } +}; + +} + +QT_END_NAMESPACE + +#endif diff --git a/src/corelib/serialization/qjsonvalue.cpp b/src/corelib/serialization/qjsonvalue.cpp new file mode 100644 index 0000000000..33707b6ec3 --- /dev/null +++ b/src/corelib/serialization/qjsonvalue.cpp @@ -0,0 +1,863 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include +#include +#include + +#include "qjson_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QJsonValue + \inmodule QtCore + \ingroup json + \ingroup shared + \reentrant + \since 5.0 + + \brief The QJsonValue class encapsulates a value in JSON. + + A value in JSON can be one of 6 basic types: + + JSON is a format to store structured data. It has 6 basic data types: + + \list + \li bool QJsonValue::Bool + \li double QJsonValue::Double + \li string QJsonValue::String + \li array QJsonValue::Array + \li object QJsonValue::Object + \li null QJsonValue::Null + \endlist + + A value can represent any of the above data types. In addition, QJsonValue has one special + flag to represent undefined values. This can be queried with isUndefined(). + + The type of the value can be queried with type() or accessors like isBool(), isString(), and so on. + Likewise, the value can be converted to the type stored in it using the toBool(), toString() and so on. + + Values are strictly typed internally and contrary to QVariant will not attempt to do any implicit type + conversions. This implies that converting to a type that is not stored in the value will return a default + constructed return value. + + \section1 QJsonValueRef + + QJsonValueRef is a helper class for QJsonArray and QJsonObject. + When you get an object of type QJsonValueRef, you can + use it as if it were a reference to a QJsonValue. If you assign to it, + the assignment will apply to the element in the QJsonArray or QJsonObject + from which you got the reference. + + The following methods return QJsonValueRef: + \list + \li \l {QJsonArray}::operator[](int i) + \li \l {QJsonObject}::operator[](const QString & key) const + \endlist + + \sa {JSON Support in Qt}, {JSON Save Game Example} +*/ + +/*! + Creates a QJsonValue of type \a type. + + The default is to create a Null value. + */ +QJsonValue::QJsonValue(Type type) + : ui(0), d(0), t(type) +{ +} + +/*! + \internal + */ +QJsonValue::QJsonValue(QJsonPrivate::Data *data, QJsonPrivate::Base *base, const QJsonPrivate::Value &v) + : d(0) +{ + t = (Type)(uint)v.type; + switch (t) { + case Undefined: + case Null: + dbl = 0; + break; + case Bool: + b = v.toBoolean(); + break; + case Double: + dbl = v.toDouble(base); + break; + case String: { + QString s = v.toString(base); + stringData = s.data_ptr(); + stringData->ref.ref(); + break; + } + case Array: + case Object: + d = data; + this->base = v.base(base); + break; + } + if (d) + d->ref.ref(); +} + +/*! + Creates a value of type Bool, with value \a b. + */ +QJsonValue::QJsonValue(bool b) + : d(0), t(Bool) +{ + this->b = b; +} + +/*! + Creates a value of type Double, with value \a n. + */ +QJsonValue::QJsonValue(double n) + : d(0), t(Double) +{ + this->dbl = n; +} + +/*! + \overload + Creates a value of type Double, with value \a n. + */ +QJsonValue::QJsonValue(int n) + : d(0), t(Double) +{ + this->dbl = n; +} + +/*! + \overload + Creates a value of type Double, with value \a n. + NOTE: the integer limits for IEEE 754 double precision data is 2^53 (-9007199254740992 to +9007199254740992). + If you pass in values outside this range expect a loss of precision to occur. + */ +QJsonValue::QJsonValue(qint64 n) + : d(0), t(Double) +{ + this->dbl = double(n); +} + +/*! + Creates a value of type String, with value \a s. + */ +QJsonValue::QJsonValue(const QString &s) + : d(0), t(String) +{ + stringDataFromQStringHelper(s); +} + +/*! + \fn QJsonValue::QJsonValue(const char *s) + + Creates a value of type String with value \a s, assuming + UTF-8 encoding of the input. + + You can disable this constructor by defining \c + QT_NO_CAST_FROM_ASCII when you compile your applications. + + \since 5.3 + */ + +void QJsonValue::stringDataFromQStringHelper(const QString &string) +{ + stringData = *(QStringData **)(&string); + stringData->ref.ref(); +} + +/*! + Creates a value of type String, with value \a s. + */ +QJsonValue::QJsonValue(QLatin1String s) + : d(0), t(String) +{ + // ### FIXME: Avoid creating the temp QString below + QString str(s); + stringDataFromQStringHelper(str); +} + +/*! + Creates a value of type Array, with value \a a. + */ +QJsonValue::QJsonValue(const QJsonArray &a) + : d(a.d), t(Array) +{ + base = a.a; + if (d) + d->ref.ref(); +} + +/*! + Creates a value of type Object, with value \a o. + */ +QJsonValue::QJsonValue(const QJsonObject &o) + : d(o.d), t(Object) +{ + base = o.o; + if (d) + d->ref.ref(); +} + + +/*! + Destroys the value. + */ +QJsonValue::~QJsonValue() +{ + if (t == String && stringData && !stringData->ref.deref()) + free(stringData); + + if (d && !d->ref.deref()) + delete d; +} + +/*! + Creates a copy of \a other. + */ +QJsonValue::QJsonValue(const QJsonValue &other) +{ + t = other.t; + d = other.d; + ui = other.ui; + if (d) + d->ref.ref(); + + if (t == String && stringData) + stringData->ref.ref(); +} + +/*! + Assigns the value stored in \a other to this object. + */ +QJsonValue &QJsonValue::operator =(const QJsonValue &other) +{ + QJsonValue copy(other); + swap(copy); + return *this; +} + +/*! + \fn QJsonValue::QJsonValue(QJsonValue &&other) + \since 5.10 + + Move-constructs a QJsonValue from \a other. +*/ + +/*! + \fn QJsonValue &QJsonValue::operator =(QJsonValue &&other) + \since 5.10 + + Move-assigns \a other to this value. +*/ + +/*! + \fn void QJsonValue::swap(QJsonValue &other) + \since 5.10 + + Swaps the value \a other with this. This operation is very fast and never fails. +*/ + +/*! + \fn bool QJsonValue::isNull() const + + Returns \c true if the value is null. +*/ + +/*! + \fn bool QJsonValue::isBool() const + + Returns \c true if the value contains a boolean. + + \sa toBool() + */ + +/*! + \fn bool QJsonValue::isDouble() const + + Returns \c true if the value contains a double. + + \sa toDouble() + */ + +/*! + \fn bool QJsonValue::isString() const + + Returns \c true if the value contains a string. + + \sa toString() + */ + +/*! + \fn bool QJsonValue::isArray() const + + Returns \c true if the value contains an array. + + \sa toArray() + */ + +/*! + \fn bool QJsonValue::isObject() const + + Returns \c true if the value contains an object. + + \sa toObject() + */ + +/*! + \fn bool QJsonValue::isUndefined() const + + Returns \c true if the value is undefined. This can happen in certain + error cases as e.g. accessing a non existing key in a QJsonObject. + */ + + +/*! + Converts \a variant to a QJsonValue and returns it. + + The conversion will convert QVariant types as follows: + + \table + \header + \li Source type + \li Destination type + \row + \li + \list + \li QMetaType::Nullptr + \endlist + \li QJsonValue::Null + \row + \li + \list + \li QMetaType::Bool + \endlist + \li QJsonValue::Bool + \row + \li + \list + \li QMetaType::Int + \li QMetaType::UInt + \li QMetaType::LongLong + \li QMetaType::ULongLong + \li QMetaType::Float + \li QMetaType::Double + \endlist + \li QJsonValue::Double + \row + \li + \list + \li QMetaType::QString + \endlist + \li QJsonValue::String + \row + \li + \list + \li QMetaType::QStringList + \li QMetaType::QVariantList + \endlist + \li QJsonValue::Array + \row + \li + \list + \li QMetaType::QVariantMap + \li QMetaType::QVariantHash + \endlist + \li QJsonValue::Object + \endtable + + For all other QVariant types a conversion to a QString will be attempted. If the returned string + is empty, a Null QJsonValue will be stored, otherwise a String value using the returned QString. + + \sa toVariant() + */ +QJsonValue QJsonValue::fromVariant(const QVariant &variant) +{ + switch (variant.userType()) { + case QMetaType::Nullptr: + return QJsonValue(Null); + case QVariant::Bool: + return QJsonValue(variant.toBool()); + case QVariant::Int: + case QMetaType::Float: + case QVariant::Double: + case QVariant::LongLong: + case QVariant::ULongLong: + case QVariant::UInt: + return QJsonValue(variant.toDouble()); + case QVariant::String: + return QJsonValue(variant.toString()); + case QVariant::StringList: + return QJsonValue(QJsonArray::fromStringList(variant.toStringList())); + case QVariant::List: + return QJsonValue(QJsonArray::fromVariantList(variant.toList())); + case QVariant::Map: + return QJsonValue(QJsonObject::fromVariantMap(variant.toMap())); + case QVariant::Hash: + return QJsonValue(QJsonObject::fromVariantHash(variant.toHash())); +#ifndef QT_BOOTSTRAPPED + case QMetaType::QJsonValue: + return variant.toJsonValue(); + case QMetaType::QJsonObject: + return variant.toJsonObject(); + case QMetaType::QJsonArray: + return variant.toJsonArray(); + case QMetaType::QJsonDocument: { + QJsonDocument doc = variant.toJsonDocument(); + return doc.isArray() ? QJsonValue(doc.array()) : QJsonValue(doc.object()); + } +#endif + default: + break; + } + QString string = variant.toString(); + if (string.isEmpty()) + return QJsonValue(); + return QJsonValue(string); +} + +/*! + Converts the value to a \l {QVariant::}{QVariant()}. + + The QJsonValue types will be converted as follows: + + \value Null QMetaType::Nullptr + \value Bool QMetaType::Bool + \value Double QMetaType::Double + \value String QString + \value Array QVariantList + \value Object QVariantMap + \value Undefined \l {QVariant::}{QVariant()} + + \sa fromVariant() + */ +QVariant QJsonValue::toVariant() const +{ + switch (t) { + case Bool: + return b; + case Double: + return dbl; + case String: + return toString(); + case Array: + return d ? + QJsonArray(d, static_cast(base)).toVariantList() : + QVariantList(); + case Object: + return d ? + QJsonObject(d, static_cast(base)).toVariantMap() : + QVariantMap(); + case Null: + return QVariant::fromValue(nullptr); + case Undefined: + break; + } + return QVariant(); +} + +/*! + \enum QJsonValue::Type + + This enum describes the type of the JSON value. + + \value Null A Null value + \value Bool A boolean value. Use toBool() to convert to a bool. + \value Double A double. Use toDouble() to convert to a double. + \value String A string. Use toString() to convert to a QString. + \value Array An array. Use toArray() to convert to a QJsonArray. + \value Object An object. Use toObject() to convert to a QJsonObject. + \value Undefined The value is undefined. This is usually returned as an + error condition, when trying to read an out of bounds value + in an array or a non existent key in an object. +*/ + +/*! + Returns the type of the value. + + \sa QJsonValue::Type + */ +QJsonValue::Type QJsonValue::type() const +{ + return t; +} + +/*! + Converts the value to a bool and returns it. + + If type() is not bool, the \a defaultValue will be returned. + */ +bool QJsonValue::toBool(bool defaultValue) const +{ + if (t != Bool) + return defaultValue; + return b; +} + +/*! + Converts the value to an int and returns it. + + If type() is not Double or the value is not a whole number, + the \a defaultValue will be returned. + */ +int QJsonValue::toInt(int defaultValue) const +{ + if (t == Double && int(dbl) == dbl) + return int(dbl); + return defaultValue; +} + +/*! + Converts the value to a double and returns it. + + If type() is not Double, the \a defaultValue will be returned. + */ +double QJsonValue::toDouble(double defaultValue) const +{ + if (t != Double) + return defaultValue; + return dbl; +} + +/*! + Converts the value to a QString and returns it. + + If type() is not String, the \a defaultValue will be returned. + */ +QString QJsonValue::toString(const QString &defaultValue) const +{ + if (t != String) + return defaultValue; + stringData->ref.ref(); // the constructor below doesn't add a ref. + QStringDataPtr holder = { stringData }; + return QString(holder); +} + +/*! + Converts the value to a QString and returns it. + + If type() is not String, a null QString will be returned. + + \sa QString::isNull() + */ +QString QJsonValue::toString() const +{ + if (t != String) + return QString(); + stringData->ref.ref(); // the constructor below doesn't add a ref. + QStringDataPtr holder = { stringData }; + return QString(holder); +} + +/*! + Converts the value to an array and returns it. + + If type() is not Array, the \a defaultValue will be returned. + */ +QJsonArray QJsonValue::toArray(const QJsonArray &defaultValue) const +{ + if (!d || t != Array) + return defaultValue; + + return QJsonArray(d, static_cast(base)); +} + +/*! + \overload + + Converts the value to an array and returns it. + + If type() is not Array, a \l{QJsonArray::}{QJsonArray()} will be returned. + */ +QJsonArray QJsonValue::toArray() const +{ + return toArray(QJsonArray()); +} + +/*! + Converts the value to an object and returns it. + + If type() is not Object, the \a defaultValue will be returned. + */ +QJsonObject QJsonValue::toObject(const QJsonObject &defaultValue) const +{ + if (!d || t != Object) + return defaultValue; + + return QJsonObject(d, static_cast(base)); +} + +/*! + \overload + + Converts the value to an object and returns it. + + If type() is not Object, the \l {QJsonObject::}{QJsonObject()} will be returned. +*/ +QJsonObject QJsonValue::toObject() const +{ + return toObject(QJsonObject()); +} + +/*! + Returns a QJsonValue representing the value for the key \a key. + + Equivalent to calling toObject().value(key). + + The returned QJsonValue is QJsonValue::Undefined if the key does not exist, + or if isObject() is false. + + \since 5.10 + + \sa QJsonValue, QJsonValue::isUndefined(), QJsonObject + */ +const QJsonValue QJsonValue::operator[](const QString &key) const +{ + if (!isObject()) + return QJsonValue(QJsonValue::Undefined); + + return toObject().value(key); +} + +/*! + \overload + \since 5.10 +*/ +const QJsonValue QJsonValue::operator[](QLatin1String key) const +{ + if (!isObject()) + return QJsonValue(QJsonValue::Undefined); + + return toObject().value(key); +} + +/*! + Returns a QJsonValue representing the value for index \a i. + + Equivalent to calling toArray().at(i). + + The returned QJsonValue is QJsonValue::Undefined, if \a i is out of bounds, + or if isArray() is false. + + \since 5.10 + + \sa QJsonValue, QJsonValue::isUndefined(), QJsonArray + */ +const QJsonValue QJsonValue::operator[](int i) const +{ + if (!isArray()) + return QJsonValue(QJsonValue::Undefined); + + return toArray().at(i); +} + +/*! + Returns \c true if the value is equal to \a other. + */ +bool QJsonValue::operator==(const QJsonValue &other) const +{ + if (t != other.t) + return false; + + switch (t) { + case Undefined: + case Null: + break; + case Bool: + return b == other.b; + case Double: + return dbl == other.dbl; + case String: + return toString() == other.toString(); + case Array: + if (base == other.base) + return true; + if (!base) + return !other.base->length; + if (!other.base) + return !base->length; + return QJsonArray(d, static_cast(base)) + == QJsonArray(other.d, static_cast(other.base)); + case Object: + if (base == other.base) + return true; + if (!base) + return !other.base->length; + if (!other.base) + return !base->length; + return QJsonObject(d, static_cast(base)) + == QJsonObject(other.d, static_cast(other.base)); + } + return true; +} + +/*! + Returns \c true if the value is not equal to \a other. + */ +bool QJsonValue::operator!=(const QJsonValue &other) const +{ + return !(*this == other); +} + +/*! + \internal + */ +void QJsonValue::detach() +{ + if (!d) + return; + + QJsonPrivate::Data *x = d->clone(base); + x->ref.ref(); + if (!d->ref.deref()) + delete d; + d = x; + base = static_cast(d->header->root()); +} + + +/*! + \class QJsonValueRef + \inmodule QtCore + \reentrant + \brief The QJsonValueRef class is a helper class for QJsonValue. + + \internal + + \ingroup json + + When you get an object of type QJsonValueRef, if you can assign to it, + the assignment will apply to the character in the string from + which you got the reference. That is its whole purpose in life. + + You can use it exactly in the same way as a reference to a QJsonValue. + + The QJsonValueRef becomes invalid once modifications are made to the + string: if you want to keep the character, copy it into a QJsonValue. + + Most of the QJsonValue member functions also exist in QJsonValueRef. + However, they are not explicitly documented here. +*/ + + +QJsonValueRef &QJsonValueRef::operator =(const QJsonValue &val) +{ + if (is_object) + o->setValueAt(index, val); + else + a->replace(index, val); + + return *this; +} + +QJsonValueRef &QJsonValueRef::operator =(const QJsonValueRef &ref) +{ + if (is_object) + o->setValueAt(index, ref); + else + a->replace(index, ref); + + return *this; +} + +QVariant QJsonValueRef::toVariant() const +{ + return toValue().toVariant(); +} + +QJsonArray QJsonValueRef::toArray() const +{ + return toValue().toArray(); +} + +QJsonObject QJsonValueRef::toObject() const +{ + return toValue().toObject(); +} + +QJsonValue QJsonValueRef::toValue() const +{ + if (!is_object) + return a->at(index); + return o->valueAt(index); +} + +#if !defined(QT_NO_DEBUG_STREAM) && !defined(QT_JSON_READONLY) +QDebug operator<<(QDebug dbg, const QJsonValue &o) +{ + QDebugStateSaver saver(dbg); + switch (o.t) { + case QJsonValue::Undefined: + dbg << "QJsonValue(undefined)"; + break; + case QJsonValue::Null: + dbg << "QJsonValue(null)"; + break; + case QJsonValue::Bool: + dbg.nospace() << "QJsonValue(bool, " << o.toBool() << ')'; + break; + case QJsonValue::Double: + dbg.nospace() << "QJsonValue(double, " << o.toDouble() << ')'; + break; + case QJsonValue::String: + dbg.nospace() << "QJsonValue(string, " << o.toString() << ')'; + break; + case QJsonValue::Array: + dbg.nospace() << "QJsonValue(array, "; + dbg << o.toArray(); + dbg << ')'; + break; + case QJsonValue::Object: + dbg.nospace() << "QJsonValue(object, "; + dbg << o.toObject(); + dbg << ')'; + break; + } + return dbg; +} +#endif + +QT_END_NAMESPACE diff --git a/src/corelib/serialization/qjsonvalue.h b/src/corelib/serialization/qjsonvalue.h new file mode 100644 index 0000000000..96538ebbf9 --- /dev/null +++ b/src/corelib/serialization/qjsonvalue.h @@ -0,0 +1,255 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QJSONVALUE_H +#define QJSONVALUE_H + +#include +#include + +QT_BEGIN_NAMESPACE + +class QDebug; +class QVariant; +class QJsonArray; +class QJsonObject; + +namespace QJsonPrivate { + class Data; + class Base; + class Object; + class Header; + class Array; + class Value; + class Entry; +} + +class Q_CORE_EXPORT QJsonValue +{ +public: + enum Type { + Null = 0x0, + Bool = 0x1, + Double = 0x2, + String = 0x3, + Array = 0x4, + Object = 0x5, + Undefined = 0x80 + }; + + QJsonValue(Type = Null); + QJsonValue(bool b); + QJsonValue(double n); + QJsonValue(int n); + QJsonValue(qint64 n); + QJsonValue(const QString &s); + QJsonValue(QLatin1String s); +#ifndef QT_NO_CAST_FROM_ASCII + inline QT_ASCII_CAST_WARN QJsonValue(const char *s) + : d(nullptr), t(String) { stringDataFromQStringHelper(QString::fromUtf8(s)); } +#endif + QJsonValue(const QJsonArray &a); + QJsonValue(const QJsonObject &o); + + ~QJsonValue(); + + QJsonValue(const QJsonValue &other); + QJsonValue &operator =(const QJsonValue &other); + + QJsonValue(QJsonValue &&other) Q_DECL_NOTHROW + : ui(other.ui), + d(other.d), + t(other.t) + { + other.ui = 0; + other.d = nullptr; + other.t = Null; + } + + QJsonValue &operator =(QJsonValue &&other) Q_DECL_NOTHROW + { + swap(other); + return *this; + } + + void swap(QJsonValue &other) Q_DECL_NOTHROW + { + qSwap(ui, other.ui); + qSwap(d, other.d); + qSwap(t, other.t); + } + + static QJsonValue fromVariant(const QVariant &variant); + QVariant toVariant() const; + + Type type() const; + inline bool isNull() const { return type() == Null; } + inline bool isBool() const { return type() == Bool; } + inline bool isDouble() const { return type() == Double; } + inline bool isString() const { return type() == String; } + inline bool isArray() const { return type() == Array; } + inline bool isObject() const { return type() == Object; } + inline bool isUndefined() const { return type() == Undefined; } + + bool toBool(bool defaultValue = false) const; + int toInt(int defaultValue = 0) const; + double toDouble(double defaultValue = 0) const; + QString toString() const; + QString toString(const QString &defaultValue) const; + QJsonArray toArray() const; + QJsonArray toArray(const QJsonArray &defaultValue) const; + QJsonObject toObject() const; + QJsonObject toObject(const QJsonObject &defaultValue) const; + + const QJsonValue operator[](const QString &key) const; + const QJsonValue operator[](QLatin1String key) const; + const QJsonValue operator[](int i) const; + + bool operator==(const QJsonValue &other) const; + bool operator!=(const QJsonValue &other) const; + +private: + // avoid implicit conversions from char * to bool + inline QJsonValue(const void *) {} + friend class QJsonPrivate::Value; + friend class QJsonArray; + friend class QJsonObject; + friend Q_CORE_EXPORT QDebug operator<<(QDebug, const QJsonValue &); + + QJsonValue(QJsonPrivate::Data *d, QJsonPrivate::Base *b, const QJsonPrivate::Value& v); + void stringDataFromQStringHelper(const QString &string); + + void detach(); + + union { + quint64 ui; + bool b; + double dbl; + QStringData *stringData; + QJsonPrivate::Base *base; + }; + QJsonPrivate::Data *d; // needed for Objects and Arrays + Type t; +}; + +class Q_CORE_EXPORT QJsonValueRef +{ +public: + QJsonValueRef(QJsonArray *array, int idx) + : a(array), is_object(false), index(idx) {} + QJsonValueRef(QJsonObject *object, int idx) + : o(object), is_object(true), index(idx) {} + + inline operator QJsonValue() const { return toValue(); } + QJsonValueRef &operator = (const QJsonValue &val); + QJsonValueRef &operator = (const QJsonValueRef &val); + + QVariant toVariant() const; + inline QJsonValue::Type type() const { return toValue().type(); } + inline bool isNull() const { return type() == QJsonValue::Null; } + inline bool isBool() const { return type() == QJsonValue::Bool; } + inline bool isDouble() const { return type() == QJsonValue::Double; } + inline bool isString() const { return type() == QJsonValue::String; } + inline bool isArray() const { return type() == QJsonValue::Array; } + inline bool isObject() const { return type() == QJsonValue::Object; } + inline bool isUndefined() const { return type() == QJsonValue::Undefined; } + + inline bool toBool() const { return toValue().toBool(); } + inline int toInt() const { return toValue().toInt(); } + inline double toDouble() const { return toValue().toDouble(); } + inline QString toString() const { return toValue().toString(); } + QJsonArray toArray() const; + QJsonObject toObject() const; + + // ### Qt 6: Add default values + inline bool toBool(bool defaultValue) const { return toValue().toBool(defaultValue); } + inline int toInt(int defaultValue) const { return toValue().toInt(defaultValue); } + inline double toDouble(double defaultValue) const { return toValue().toDouble(defaultValue); } + inline QString toString(const QString &defaultValue) const { return toValue().toString(defaultValue); } + + inline bool operator==(const QJsonValue &other) const { return toValue() == other; } + inline bool operator!=(const QJsonValue &other) const { return toValue() != other; } + +private: + QJsonValue toValue() const; + + union { + QJsonArray *a; + QJsonObject *o; + }; + uint is_object : 1; + uint index : 31; +}; + +#ifndef Q_QDOC +// ### Qt 6: Get rid of these fake pointer classes +class QJsonValuePtr +{ + QJsonValue value; +public: + explicit QJsonValuePtr(const QJsonValue& val) + : value(val) {} + + QJsonValue& operator*() { return value; } + QJsonValue* operator->() { return &value; } +}; + +class QJsonValueRefPtr +{ + QJsonValueRef valueRef; +public: + QJsonValueRefPtr(QJsonArray *array, int idx) + : valueRef(array, idx) {} + QJsonValueRefPtr(QJsonObject *object, int idx) + : valueRef(object, idx) {} + + QJsonValueRef& operator*() { return valueRef; } + QJsonValueRef* operator->() { return &valueRef; } +}; +#endif + +Q_DECLARE_SHARED_NOT_MOVABLE_UNTIL_QT6(QJsonValue) + +#if !defined(QT_NO_DEBUG_STREAM) && !defined(QT_JSON_READONLY) +Q_CORE_EXPORT QDebug operator<<(QDebug, const QJsonValue &); +#endif + +QT_END_NAMESPACE + +#endif // QJSONVALUE_H diff --git a/src/corelib/serialization/qjsonwriter.cpp b/src/corelib/serialization/qjsonwriter.cpp new file mode 100644 index 0000000000..12ce20ef09 --- /dev/null +++ b/src/corelib/serialization/qjsonwriter.cpp @@ -0,0 +1,231 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2016 Intel Corporation. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include "qjsonwriter_p.h" +#include "qjson_p.h" +#include "private/qutfcodec_p.h" + +QT_BEGIN_NAMESPACE + +using namespace QJsonPrivate; + +static void objectContentToJson(const QJsonPrivate::Object *o, QByteArray &json, int indent, bool compact); +static void arrayContentToJson(const QJsonPrivate::Array *a, QByteArray &json, int indent, bool compact); + +static inline uchar hexdig(uint u) +{ + return (u < 0xa ? '0' + u : 'a' + u - 0xa); +} + +static QByteArray escapedString(const QString &s) +{ + const uchar replacement = '?'; + QByteArray ba(s.length(), Qt::Uninitialized); + + uchar *cursor = reinterpret_cast(const_cast(ba.constData())); + const uchar *ba_end = cursor + ba.length(); + const ushort *src = reinterpret_cast(s.constBegin()); + const ushort *const end = reinterpret_cast(s.constEnd()); + + while (src != end) { + if (cursor >= ba_end - 6) { + // ensure we have enough space + int pos = cursor - (const uchar *)ba.constData(); + ba.resize(ba.size()*2); + cursor = (uchar *)ba.data() + pos; + ba_end = (const uchar *)ba.constData() + ba.length(); + } + + uint u = *src++; + if (u < 0x80) { + if (u < 0x20 || u == 0x22 || u == 0x5c) { + *cursor++ = '\\'; + switch (u) { + case 0x22: + *cursor++ = '"'; + break; + case 0x5c: + *cursor++ = '\\'; + break; + case 0x8: + *cursor++ = 'b'; + break; + case 0xc: + *cursor++ = 'f'; + break; + case 0xa: + *cursor++ = 'n'; + break; + case 0xd: + *cursor++ = 'r'; + break; + case 0x9: + *cursor++ = 't'; + break; + default: + *cursor++ = 'u'; + *cursor++ = '0'; + *cursor++ = '0'; + *cursor++ = hexdig(u>>4); + *cursor++ = hexdig(u & 0xf); + } + } else { + *cursor++ = (uchar)u; + } + } else { + if (QUtf8Functions::toUtf8(u, cursor, src, end) < 0) + *cursor++ = replacement; + } + } + + ba.resize(cursor - (const uchar *)ba.constData()); + return ba; +} + +static void valueToJson(const QJsonPrivate::Base *b, const QJsonPrivate::Value &v, QByteArray &json, int indent, bool compact) +{ + QJsonValue::Type type = (QJsonValue::Type)(uint)v.type; + switch (type) { + case QJsonValue::Bool: + json += v.toBoolean() ? "true" : "false"; + break; + case QJsonValue::Double: { + const double d = v.toDouble(b); + if (qIsFinite(d)) { // +2 to format to ensure the expected precision + const double abs = std::abs(d); + json += QByteArray::number(d, abs == static_cast(abs) ? 'f' : 'g', QLocale::FloatingPointShortest); + } else { + json += "null"; // +INF || -INF || NaN (see RFC4627#section2.4) + } + break; + } + case QJsonValue::String: + json += '"'; + json += escapedString(v.toString(b)); + json += '"'; + break; + case QJsonValue::Array: + json += compact ? "[" : "[\n"; + arrayContentToJson(static_cast(v.base(b)), json, indent + (compact ? 0 : 1), compact); + json += QByteArray(4*indent, ' '); + json += ']'; + break; + case QJsonValue::Object: + json += compact ? "{" : "{\n"; + objectContentToJson(static_cast(v.base(b)), json, indent + (compact ? 0 : 1), compact); + json += QByteArray(4*indent, ' '); + json += '}'; + break; + case QJsonValue::Null: + default: + json += "null"; + } +} + +static void arrayContentToJson(const QJsonPrivate::Array *a, QByteArray &json, int indent, bool compact) +{ + if (!a || !a->length) + return; + + QByteArray indentString(4*indent, ' '); + + uint i = 0; + while (1) { + json += indentString; + valueToJson(a, a->at(i), json, indent, compact); + + if (++i == a->length) { + if (!compact) + json += '\n'; + break; + } + + json += compact ? "," : ",\n"; + } +} + + +static void objectContentToJson(const QJsonPrivate::Object *o, QByteArray &json, int indent, bool compact) +{ + if (!o || !o->length) + return; + + QByteArray indentString(4*indent, ' '); + + uint i = 0; + while (1) { + QJsonPrivate::Entry *e = o->entryAt(i); + json += indentString; + json += '"'; + json += escapedString(e->key()); + json += compact ? "\":" : "\": "; + valueToJson(o, e->value, json, indent, compact); + + if (++i == o->length) { + if (!compact) + json += '\n'; + break; + } + + json += compact ? "," : ",\n"; + } +} + +void Writer::objectToJson(const QJsonPrivate::Object *o, QByteArray &json, int indent, bool compact) +{ + json.reserve(json.size() + (o ? (int)o->size : 16)); + json += compact ? "{" : "{\n"; + objectContentToJson(o, json, indent + (compact ? 0 : 1), compact); + json += QByteArray(4*indent, ' '); + json += compact ? "}" : "}\n"; +} + +void Writer::arrayToJson(const QJsonPrivate::Array *a, QByteArray &json, int indent, bool compact) +{ + json.reserve(json.size() + (a ? (int)a->size : 16)); + json += compact ? "[" : "[\n"; + arrayContentToJson(a, json, indent + (compact ? 0 : 1), compact); + json += QByteArray(4*indent, ' '); + json += compact ? "]" : "]\n"; +} + +QT_END_NAMESPACE diff --git a/src/corelib/serialization/qjsonwriter_p.h b/src/corelib/serialization/qjsonwriter_p.h new file mode 100644 index 0000000000..76a8460449 --- /dev/null +++ b/src/corelib/serialization/qjsonwriter_p.h @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QJSONWRITER_P_H +#define QJSONWRITER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include + +QT_BEGIN_NAMESPACE + +namespace QJsonPrivate +{ + +class Writer +{ +public: + static void objectToJson(const QJsonPrivate::Object *o, QByteArray &json, int indent, bool compact = false); + static void arrayToJson(const QJsonPrivate::Array *a, QByteArray &json, int indent, bool compact = false); +}; + +} + +QT_END_NAMESPACE + +#endif diff --git a/src/corelib/serialization/qtextstream.cpp b/src/corelib/serialization/qtextstream.cpp new file mode 100644 index 0000000000..ee3cb4efcb --- /dev/null +++ b/src/corelib/serialization/qtextstream.cpp @@ -0,0 +1,3192 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2016 Intel Corporation. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//#define QTEXTSTREAM_DEBUG +static const int QTEXTSTREAM_BUFFERSIZE = 16384; + +/*! + \class QTextStream + \inmodule QtCore + + \brief The QTextStream class provides a convenient interface for + reading and writing text. + + \ingroup io + \ingroup string-processing + \reentrant + + QTextStream can operate on a QIODevice, a QByteArray or a + QString. Using QTextStream's streaming operators, you can + conveniently read and write words, lines and numbers. For + generating text, QTextStream supports formatting options for field + padding and alignment, and formatting of numbers. Example: + + \snippet code/src_corelib_io_qtextstream.cpp 0 + + It's also common to use QTextStream to read console input and write + console output. QTextStream is locale aware, and will automatically decode + standard input using the correct codec. Example: + + \snippet code/src_corelib_io_qtextstream.cpp 1 + + Besides using QTextStream's constructors, you can also set the + device or string QTextStream operates on by calling setDevice() or + setString(). You can seek to a position by calling seek(), and + atEnd() will return true when there is no data left to be read. If + you call flush(), QTextStream will empty all data from its write + buffer into the device and call flush() on the device. + + Internally, QTextStream uses a Unicode based buffer, and + QTextCodec is used by QTextStream to automatically support + different character sets. By default, QTextCodec::codecForLocale() + is used for reading and writing, but you can also set the codec by + calling setCodec(). Automatic Unicode detection is also + supported. When this feature is enabled (the default behavior), + QTextStream will detect the UTF-16 or the UTF-32 BOM (Byte Order Mark) and + switch to the appropriate UTF codec when reading. QTextStream + does not write a BOM by default, but you can enable this by calling + setGenerateByteOrderMark(true). When QTextStream operates on a QString + directly, the codec is disabled. + + There are three general ways to use QTextStream when reading text + files: + + \list + + \li Chunk by chunk, by calling readLine() or readAll(). + + \li Word by word. QTextStream supports streaming into \l {QString}s, + \l {QByteArray}s and char* buffers. Words are delimited by space, and + leading white space is automatically skipped. + + \li Character by character, by streaming into QChar or char types. + This method is often used for convenient input handling when + parsing files, independent of character encoding and end-of-line + semantics. To skip white space, call skipWhiteSpace(). + + \endlist + + Since the text stream uses a buffer, you should not read from + the stream using the implementation of a superclass. For instance, + if you have a QFile and read from it directly using + QFile::readLine() instead of using the stream, the text stream's + internal position will be out of sync with the file's position. + + By default, when reading numbers from a stream of text, + QTextStream will automatically detect the number's base + representation. For example, if the number starts with "0x", it is + assumed to be in hexadecimal form. If it starts with the digits + 1-9, it is assumed to be in decimal form, and so on. You can set + the integer base, thereby disabling the automatic detection, by + calling setIntegerBase(). Example: + + \snippet code/src_corelib_io_qtextstream.cpp 2 + + QTextStream supports many formatting options for generating text. + You can set the field width and pad character by calling + setFieldWidth() and setPadChar(). Use setFieldAlignment() to set + the alignment within each field. For real numbers, call + setRealNumberNotation() and setRealNumberPrecision() to set the + notation (SmartNotation, ScientificNotation, FixedNotation) and precision in + digits of the generated number. Some extra number formatting + options are also available through setNumberFlags(). + + \target QTextStream manipulators + + Like \c in the standard C++ library, QTextStream also + defines several global manipulator functions: + + \table + \header \li Manipulator \li Description + \row \li \c bin \li Same as setIntegerBase(2). + \row \li \c oct \li Same as setIntegerBase(8). + \row \li \c dec \li Same as setIntegerBase(10). + \row \li \c hex \li Same as setIntegerBase(16). + \row \li \c showbase \li Same as setNumberFlags(numberFlags() | ShowBase). + \row \li \c forcesign \li Same as setNumberFlags(numberFlags() | ForceSign). + \row \li \c forcepoint \li Same as setNumberFlags(numberFlags() | ForcePoint). + \row \li \c noshowbase \li Same as setNumberFlags(numberFlags() & ~ShowBase). + \row \li \c noforcesign \li Same as setNumberFlags(numberFlags() & ~ForceSign). + \row \li \c noforcepoint \li Same as setNumberFlags(numberFlags() & ~ForcePoint). + \row \li \c uppercasebase \li Same as setNumberFlags(numberFlags() | UppercaseBase). + \row \li \c uppercasedigits \li Same as setNumberFlags(numberFlags() | UppercaseDigits). + \row \li \c lowercasebase \li Same as setNumberFlags(numberFlags() & ~UppercaseBase). + \row \li \c lowercasedigits \li Same as setNumberFlags(numberFlags() & ~UppercaseDigits). + \row \li \c fixed \li Same as setRealNumberNotation(FixedNotation). + \row \li \c scientific \li Same as setRealNumberNotation(ScientificNotation). + \row \li \c left \li Same as setFieldAlignment(AlignLeft). + \row \li \c right \li Same as setFieldAlignment(AlignRight). + \row \li \c center \li Same as setFieldAlignment(AlignCenter). + \row \li \c endl \li Same as operator<<('\\n') and flush(). + \row \li \c flush \li Same as flush(). + \row \li \c reset \li Same as reset(). + \row \li \c ws \li Same as skipWhiteSpace(). + \row \li \c bom \li Same as setGenerateByteOrderMark(true). + \endtable + + In addition, Qt provides three global manipulators that take a + parameter: qSetFieldWidth(), qSetPadChar(), and + qSetRealNumberPrecision(). + + \sa QDataStream, QIODevice, QFile, QBuffer, QTcpSocket, {Text Codecs Example} +*/ + +/*! \enum QTextStream::RealNumberNotation + + This enum specifies which notations to use for expressing \c + float and \c double as strings. + + \value ScientificNotation Scientific notation (\c{printf()}'s \c %e flag). + \value FixedNotation Fixed-point notation (\c{printf()}'s \c %f flag). + \value SmartNotation Scientific or fixed-point notation, depending on which makes most sense (\c{printf()}'s \c %g flag). + + \sa setRealNumberNotation() +*/ + +/*! \enum QTextStream::FieldAlignment + + This enum specifies how to align text in fields when the field is + wider than the text that occupies it. + + \value AlignLeft Pad on the right side of fields. + \value AlignRight Pad on the left side of fields. + \value AlignCenter Pad on both sides of field. + \value AlignAccountingStyle Same as AlignRight, except that the + sign of a number is flush left. + + \sa setFieldAlignment() +*/ + +/*! \enum QTextStream::NumberFlag + + This enum specifies various flags that can be set to affect the + output of integers, \c{float}s, and \c{double}s. + + \value ShowBase Show the base as a prefix if the base + is 16 ("0x"), 8 ("0"), or 2 ("0b"). + \value ForcePoint Always put the decimal separator in numbers, even if + there are no decimals. + \value ForceSign Always put the sign in numbers, even for positive numbers. + \value UppercaseBase Use uppercase versions of base prefixes ("0X", "0B"). + \value UppercaseDigits Use uppercase letters for expressing + digits 10 to 35 instead of lowercase. + + \sa setNumberFlags() +*/ + +/*! \enum QTextStream::Status + + This enum describes the current status of the text stream. + + \value Ok The text stream is operating normally. + \value ReadPastEnd The text stream has read past the end of the + data in the underlying device. + \value ReadCorruptData The text stream has read corrupt data. + \value WriteFailed The text stream cannot write to the underlying device. + + \sa status() +*/ + +#include "qtextstream.h" +#include "private/qtextstream_p.h" +#include "qbuffer.h" +#include "qfile.h" +#include "qnumeric.h" +#include "qvarlengtharray.h" + +#include +#include "private/qlocale_p.h" + +#include +#include +#include + +#if defined QTEXTSTREAM_DEBUG +#include +#include "private/qtools_p.h" + +QT_BEGIN_NAMESPACE + +// Returns a human readable representation of the first \a len +// characters in \a data. +static QByteArray qt_prettyDebug(const char *data, int len, int maxSize) +{ + if (!data) return "(null)"; + QByteArray out; + for (int i = 0; i < len; ++i) { + char c = data[i]; + if (isprint(int(uchar(c)))) { + out += c; + } else switch (c) { + case '\n': out += "\\n"; break; + case '\r': out += "\\r"; break; + case '\t': out += "\\t"; break; + default: { + const char buf[] = { + '\\', + 'x', + QtMiscUtils::toHexLower(uchar(c) / 16), + QtMiscUtils::toHexLower(uchar(c) % 16), + 0 + }; + out += buf; + } + } + } + + if (len < maxSize) + out += "..."; + + return out; +} +QT_END_NAMESPACE + +#endif + +// A precondition macro +#define Q_VOID +#define CHECK_VALID_STREAM(x) do { \ + if (!d->string && !d->device) { \ + qWarning("QTextStream: No device"); \ + return x; \ + } } while (0) + +// Base implementations of operator>> for ints and reals +#define IMPLEMENT_STREAM_RIGHT_INT_OPERATOR(type) do { \ + Q_D(QTextStream); \ + CHECK_VALID_STREAM(*this); \ + qulonglong tmp; \ + switch (d->getNumber(&tmp)) { \ + case QTextStreamPrivate::npsOk: \ + i = (type)tmp; \ + break; \ + case QTextStreamPrivate::npsMissingDigit: \ + case QTextStreamPrivate::npsInvalidPrefix: \ + i = (type)0; \ + setStatus(atEnd() ? QTextStream::ReadPastEnd : QTextStream::ReadCorruptData); \ + break; \ + } \ + return *this; } while (0) + +#define IMPLEMENT_STREAM_RIGHT_REAL_OPERATOR(type) do { \ + Q_D(QTextStream); \ + CHECK_VALID_STREAM(*this); \ + double tmp; \ + if (d->getReal(&tmp)) { \ + f = (type)tmp; \ + } else { \ + f = (type)0; \ + setStatus(atEnd() ? QTextStream::ReadPastEnd : QTextStream::ReadCorruptData); \ + } \ + return *this; } while (0) + +QT_BEGIN_NAMESPACE + +//------------------------------------------------------------------- + +/*! + \internal +*/ +QTextStreamPrivate::QTextStreamPrivate(QTextStream *q_ptr) + : +#ifndef QT_NO_TEXTCODEC + readConverterSavedState(0), +#endif + readConverterSavedStateOffset(0), + locale(QLocale::c()) +{ + this->q_ptr = q_ptr; + reset(); +} + +/*! + \internal +*/ +QTextStreamPrivate::~QTextStreamPrivate() +{ + if (deleteDevice) { +#ifndef QT_NO_QOBJECT + device->blockSignals(true); +#endif + delete device; + } +#ifndef QT_NO_TEXTCODEC + delete readConverterSavedState; +#endif +} + +#ifndef QT_NO_TEXTCODEC +static void resetCodecConverterStateHelper(QTextCodec::ConverterState *state) +{ + state->~ConverterState(); + new (state) QTextCodec::ConverterState; +} + +static void copyConverterStateHelper(QTextCodec::ConverterState *dest, + const QTextCodec::ConverterState *src) +{ + // ### QTextCodec::ConverterState's copy constructors and assignments are + // private. This function copies the structure manually. + Q_ASSERT(!src->d); + dest->flags = src->flags; + dest->invalidChars = src->invalidChars; + dest->state_data[0] = src->state_data[0]; + dest->state_data[1] = src->state_data[1]; + dest->state_data[2] = src->state_data[2]; +} +#endif + +void QTextStreamPrivate::Params::reset() +{ + realNumberPrecision = 6; + integerBase = 0; + fieldWidth = 0; + padChar = QLatin1Char(' '); + fieldAlignment = QTextStream::AlignRight; + realNumberNotation = QTextStream::SmartNotation; + numberFlags = 0; +} + +/*! + \internal +*/ +void QTextStreamPrivate::reset() +{ + params.reset(); + + device = 0; + deleteDevice = false; + string = 0; + stringOffset = 0; + stringOpenMode = QIODevice::NotOpen; + + readBufferOffset = 0; + readBufferStartDevicePos = 0; + lastTokenSize = 0; + +#ifndef QT_NO_TEXTCODEC + codec = QTextCodec::codecForLocale(); + resetCodecConverterStateHelper(&readConverterState); + resetCodecConverterStateHelper(&writeConverterState); + delete readConverterSavedState; + readConverterSavedState = 0; + writeConverterState.flags |= QTextCodec::IgnoreHeader; + autoDetectUnicode = true; +#endif +} + +/*! + \internal +*/ +bool QTextStreamPrivate::fillReadBuffer(qint64 maxBytes) +{ + // no buffer next to the QString itself; this function should only + // be called internally, for devices. + Q_ASSERT(!string); + Q_ASSERT(device); + + // handle text translation and bypass the Text flag in the device. + bool textModeEnabled = device->isTextModeEnabled(); + if (textModeEnabled) + device->setTextModeEnabled(false); + + // read raw data into a temporary buffer + char buf[QTEXTSTREAM_BUFFERSIZE]; + qint64 bytesRead = 0; +#if defined(Q_OS_WIN) + // On Windows, there is no non-blocking stdin - so we fall back to reading + // lines instead. If there is no QOBJECT, we read lines for all sequential + // devices; otherwise, we read lines only for stdin. + QFile *file = 0; + Q_UNUSED(file); + if (device->isSequential() +#if !defined(QT_NO_QOBJECT) + && (file = qobject_cast(device)) && file->handle() == 0 +#endif + ) { + if (maxBytes != -1) + bytesRead = device->readLine(buf, qMin(sizeof(buf), maxBytes)); + else + bytesRead = device->readLine(buf, sizeof(buf)); + } else +#endif + { + if (maxBytes != -1) + bytesRead = device->read(buf, qMin(sizeof(buf), maxBytes)); + else + bytesRead = device->read(buf, sizeof(buf)); + } + + // reset the Text flag. + if (textModeEnabled) + device->setTextModeEnabled(true); + + if (bytesRead <= 0) + return false; + +#ifndef QT_NO_TEXTCODEC + // codec auto detection, explicitly defaults to locale encoding if the + // codec has been set to 0. + if (!codec || autoDetectUnicode) { + autoDetectUnicode = false; + + codec = QTextCodec::codecForUtfText(QByteArray::fromRawData(buf, bytesRead), codec); + if (!codec) { + codec = QTextCodec::codecForLocale(); + writeConverterState.flags |= QTextCodec::IgnoreHeader; + } + } +#if defined (QTEXTSTREAM_DEBUG) + qDebug("QTextStreamPrivate::fillReadBuffer(), using %s codec", + codec ? codec->name().constData() : "no"); +#endif +#endif + +#if defined (QTEXTSTREAM_DEBUG) + qDebug("QTextStreamPrivate::fillReadBuffer(), device->read(\"%s\", %d) == %d", + qt_prettyDebug(buf, qMin(32,int(bytesRead)) , int(bytesRead)).constData(), int(sizeof(buf)), int(bytesRead)); +#endif + + int oldReadBufferSize = readBuffer.size(); +#ifndef QT_NO_TEXTCODEC + // convert to unicode + readBuffer += Q_LIKELY(codec) ? codec->toUnicode(buf, bytesRead, &readConverterState) + : QString::fromLatin1(buf, bytesRead); +#else + readBuffer += QString::fromLatin1(buf, bytesRead); +#endif + + // remove all '\r\n' in the string. + if (readBuffer.size() > oldReadBufferSize && textModeEnabled) { + QChar CR = QLatin1Char('\r'); + QChar *writePtr = readBuffer.data() + oldReadBufferSize; + QChar *readPtr = readBuffer.data() + oldReadBufferSize; + QChar *endPtr = readBuffer.data() + readBuffer.size(); + + int n = oldReadBufferSize; + if (readPtr < endPtr) { + // Cut-off to avoid unnecessary self-copying. + while (*readPtr++ != CR) { + ++n; + if (++writePtr == endPtr) + break; + } + } + while (readPtr < endPtr) { + QChar ch = *readPtr++; + if (ch != CR) { + *writePtr++ = ch; + } else { + if (n < readBufferOffset) + --readBufferOffset; + --bytesRead; + } + ++n; + } + readBuffer.resize(writePtr - readBuffer.data()); + } + +#if defined (QTEXTSTREAM_DEBUG) + qDebug("QTextStreamPrivate::fillReadBuffer() read %d bytes from device. readBuffer = [%s]", int(bytesRead), + qt_prettyDebug(readBuffer.toLatin1(), readBuffer.size(), readBuffer.size()).data()); +#endif + return true; +} + +/*! + \internal +*/ +void QTextStreamPrivate::resetReadBuffer() +{ + readBuffer.clear(); + readBufferOffset = 0; + readBufferStartDevicePos = (device ? device->pos() : 0); +} + +/*! + \internal +*/ +void QTextStreamPrivate::flushWriteBuffer() +{ + // no buffer next to the QString itself; this function should only + // be called internally, for devices. + if (string || !device) + return; + + // Stream went bye-bye already. Appending further data may succeed again, + // but would create a corrupted stream anyway. + if (status != QTextStream::Ok) + return; + + if (writeBuffer.isEmpty()) + return; + +#if defined (Q_OS_WIN) + // handle text translation and bypass the Text flag in the device. + bool textModeEnabled = device->isTextModeEnabled(); + if (textModeEnabled) { + device->setTextModeEnabled(false); + writeBuffer.replace(QLatin1Char('\n'), QLatin1String("\r\n")); + } +#endif + +#ifndef QT_NO_TEXTCODEC + if (!codec) + codec = QTextCodec::codecForLocale(); +#if defined (QTEXTSTREAM_DEBUG) + qDebug("QTextStreamPrivate::flushWriteBuffer(), using %s codec (%s generating BOM)", + codec ? codec->name().constData() : "no", + !codec || (writeConverterState.flags & QTextCodec::IgnoreHeader) ? "not" : ""); +#endif + + // convert from unicode to raw data + // codec might be null if we're already inside global destructors (QTestCodec::codecForLocale returned null) + QByteArray data = Q_LIKELY(codec) ? codec->fromUnicode(writeBuffer.data(), writeBuffer.size(), &writeConverterState) + : writeBuffer.toLatin1(); +#else + QByteArray data = writeBuffer.toLatin1(); +#endif + writeBuffer.clear(); + + // write raw data to the device + qint64 bytesWritten = device->write(data); +#if defined (QTEXTSTREAM_DEBUG) + qDebug("QTextStreamPrivate::flushWriteBuffer(), device->write(\"%s\") == %d", + qt_prettyDebug(data.constData(), qMin(data.size(),32), data.size()).constData(), int(bytesWritten)); +#endif + +#if defined (Q_OS_WIN) + // reset the text flag + if (textModeEnabled) + device->setTextModeEnabled(true); +#endif + + if (bytesWritten <= 0) { + status = QTextStream::WriteFailed; + return; + } + + // flush the file +#ifndef QT_NO_QOBJECT + QFileDevice *file = qobject_cast(device); + bool flushed = !file || file->flush(); +#else + bool flushed = true; +#endif + +#if defined (QTEXTSTREAM_DEBUG) + qDebug("QTextStreamPrivate::flushWriteBuffer() wrote %d bytes", + int(bytesWritten)); +#endif + if (!flushed || bytesWritten != qint64(data.size())) + status = QTextStream::WriteFailed; +} + +QString QTextStreamPrivate::read(int maxlen) +{ + QString ret; + if (string) { + lastTokenSize = qMin(maxlen, string->size() - stringOffset); + ret = string->mid(stringOffset, lastTokenSize); + } else { + while (readBuffer.size() - readBufferOffset < maxlen && fillReadBuffer()) ; + lastTokenSize = qMin(maxlen, readBuffer.size() - readBufferOffset); + ret = readBuffer.mid(readBufferOffset, lastTokenSize); + } + consumeLastToken(); + +#if defined (QTEXTSTREAM_DEBUG) + qDebug("QTextStreamPrivate::read() maxlen = %d, token length = %d", maxlen, ret.length()); +#endif + return ret; +} + +/*! + \internal + + Scans no more than \a maxlen QChars in the current buffer for the + first \a delimiter. Stores a pointer to the start offset of the + token in \a ptr, and the length in QChars in \a length. +*/ +bool QTextStreamPrivate::scan(const QChar **ptr, int *length, int maxlen, TokenDelimiter delimiter) +{ + int totalSize = 0; + int delimSize = 0; + bool consumeDelimiter = false; + bool foundToken = false; + int startOffset = device ? readBufferOffset : stringOffset; + QChar lastChar; + + bool canStillReadFromDevice = true; + do { + int endOffset; + const QChar *chPtr; + if (device) { + chPtr = readBuffer.constData(); + endOffset = readBuffer.size(); + } else { + chPtr = string->constData(); + endOffset = string->size(); + } + chPtr += startOffset; + + for (; !foundToken && startOffset < endOffset && (!maxlen || totalSize < maxlen); ++startOffset) { + const QChar ch = *chPtr++; + ++totalSize; + + switch (delimiter) { + case Space: + if (ch.isSpace()) { + foundToken = true; + delimSize = 1; + } + break; + case NotSpace: + if (!ch.isSpace()) { + foundToken = true; + delimSize = 1; + } + break; + case EndOfLine: + if (ch == QLatin1Char('\n')) { + foundToken = true; + delimSize = (lastChar == QLatin1Char('\r')) ? 2 : 1; + consumeDelimiter = true; + } + lastChar = ch; + break; + } + } + } while (!foundToken + && (!maxlen || totalSize < maxlen) + && (device && (canStillReadFromDevice = fillReadBuffer()))); + + if (totalSize == 0) { +#if defined (QTEXTSTREAM_DEBUG) + qDebug("QTextStreamPrivate::scan() reached the end of input."); +#endif + return false; + } + + // if we find a '\r' at the end of the data when reading lines, + // don't make it part of the line. + if (delimiter == EndOfLine && totalSize > 0 && !foundToken) { + if (((string && stringOffset + totalSize == string->size()) || (device && device->atEnd())) + && lastChar == QLatin1Char('\r')) { + consumeDelimiter = true; + ++delimSize; + } + } + + // set the read offset and length of the token + if (length) + *length = totalSize - delimSize; + if (ptr) + *ptr = readPtr(); + + // update last token size. the callee will call consumeLastToken() when + // done. + lastTokenSize = totalSize; + if (!consumeDelimiter) + lastTokenSize -= delimSize; + +#if defined (QTEXTSTREAM_DEBUG) + qDebug("QTextStreamPrivate::scan(%p, %p, %d, %x) token length = %d, delimiter = %d", + ptr, length, maxlen, (int)delimiter, totalSize - delimSize, delimSize); +#endif + return true; +} + +/*! + \internal +*/ +inline const QChar *QTextStreamPrivate::readPtr() const +{ + Q_ASSERT(readBufferOffset <= readBuffer.size()); + if (string) + return string->constData() + stringOffset; + return readBuffer.constData() + readBufferOffset; +} + +/*! + \internal +*/ +inline void QTextStreamPrivate::consumeLastToken() +{ + if (lastTokenSize) + consume(lastTokenSize); + lastTokenSize = 0; +} + +/*! + \internal +*/ +inline void QTextStreamPrivate::consume(int size) +{ +#if defined (QTEXTSTREAM_DEBUG) + qDebug("QTextStreamPrivate::consume(%d)", size); +#endif + if (string) { + stringOffset += size; + if (stringOffset > string->size()) + stringOffset = string->size(); + } else { + readBufferOffset += size; + if (readBufferOffset >= readBuffer.size()) { + readBufferOffset = 0; + readBuffer.clear(); + saveConverterState(device->pos()); + } else if (readBufferOffset > QTEXTSTREAM_BUFFERSIZE) { + readBuffer = readBuffer.remove(0,readBufferOffset); + readConverterSavedStateOffset += readBufferOffset; + readBufferOffset = 0; + } + } +} + +/*! + \internal +*/ +inline void QTextStreamPrivate::saveConverterState(qint64 newPos) +{ +#ifndef QT_NO_TEXTCODEC + if (readConverterState.d) { + // converter cannot be copied, so don't save anything + // don't update readBufferStartDevicePos either + return; + } + + if (!readConverterSavedState) + readConverterSavedState = new QTextCodec::ConverterState; + copyConverterStateHelper(readConverterSavedState, &readConverterState); +#endif + + readBufferStartDevicePos = newPos; + readConverterSavedStateOffset = 0; +} + +/*! + \internal +*/ +inline void QTextStreamPrivate::restoreToSavedConverterState() +{ +#ifndef QT_NO_TEXTCODEC + if (readConverterSavedState) { + // we have a saved state + // that means the converter can be copied + copyConverterStateHelper(&readConverterState, readConverterSavedState); + } else { + // the only state we could save was the initial + // so reset to that + resetCodecConverterStateHelper(&readConverterState); + } +#endif +} + +/*! + \internal +*/ +void QTextStreamPrivate::write(const QChar *data, int len) +{ + if (string) { + // ### What about seek()?? + string->append(data, len); + } else { + writeBuffer.append(data, len); + if (writeBuffer.size() > QTEXTSTREAM_BUFFERSIZE) + flushWriteBuffer(); + } +} + +/*! + \internal +*/ +inline void QTextStreamPrivate::write(QChar ch) +{ + if (string) { + // ### What about seek()?? + string->append(ch); + } else { + writeBuffer += ch; + if (writeBuffer.size() > QTEXTSTREAM_BUFFERSIZE) + flushWriteBuffer(); + } +} + +/*! + \internal +*/ +void QTextStreamPrivate::write(QLatin1String data) +{ + if (string) { + // ### What about seek()?? + string->append(data); + } else { + writeBuffer += data; + if (writeBuffer.size() > QTEXTSTREAM_BUFFERSIZE) + flushWriteBuffer(); + } +} + +/*! + \internal +*/ +void QTextStreamPrivate::writePadding(int len) +{ + if (string) { + // ### What about seek()?? + string->resize(string->size() + len, params.padChar); + } else { + writeBuffer.resize(writeBuffer.size() + len, params.padChar); + if (writeBuffer.size() > QTEXTSTREAM_BUFFERSIZE) + flushWriteBuffer(); + } +} + +/*! + \internal +*/ +inline bool QTextStreamPrivate::getChar(QChar *ch) +{ + if ((string && stringOffset == string->size()) + || (device && readBuffer.isEmpty() && !fillReadBuffer())) { + if (ch) + *ch = 0; + return false; + } + if (ch) + *ch = *readPtr(); + consume(1); + return true; +} + +/*! + \internal +*/ +inline void QTextStreamPrivate::ungetChar(QChar ch) +{ + if (string) { + if (stringOffset == 0) + string->prepend(ch); + else + (*string)[--stringOffset] = ch; + return; + } + + if (readBufferOffset == 0) { + readBuffer.prepend(ch); + return; + } + + readBuffer[--readBufferOffset] = ch; +} + +/*! + \internal +*/ +inline void QTextStreamPrivate::putChar(QChar ch) +{ + if (params.fieldWidth > 0) + putString(&ch, 1); + else + write(ch); +} + + +/*! + \internal +*/ +QTextStreamPrivate::PaddingResult QTextStreamPrivate::padding(int len) const +{ + Q_ASSERT(params.fieldWidth > len); // calling padding() when no padding is needed is an error + + int left = 0, right = 0; + + const int padSize = params.fieldWidth - len; + + switch (params.fieldAlignment) { + case QTextStream::AlignLeft: + right = padSize; + break; + case QTextStream::AlignRight: + case QTextStream::AlignAccountingStyle: + left = padSize; + break; + case QTextStream::AlignCenter: + left = padSize/2; + right = padSize - padSize/2; + break; + } + return { left, right }; +} + +/*! + \internal +*/ +void QTextStreamPrivate::putString(const QChar *data, int len, bool number) +{ + if (Q_UNLIKELY(params.fieldWidth > len)) { + + // handle padding: + + const PaddingResult pad = padding(len); + + if (params.fieldAlignment == QTextStream::AlignAccountingStyle && number) { + const QChar sign = len > 0 ? data[0] : QChar(); + if (sign == locale.negativeSign() || sign == locale.positiveSign()) { + // write the sign before the padding, then skip it later + write(&sign, 1); + ++data; + --len; + } + } + + writePadding(pad.left); + write(data, len); + writePadding(pad.right); + } else { + write(data, len); + } +} + +/*! + \internal +*/ +void QTextStreamPrivate::putString(QLatin1String data, bool number) +{ + if (Q_UNLIKELY(params.fieldWidth > data.size())) { + + // handle padding + + const PaddingResult pad = padding(data.size()); + + if (params.fieldAlignment == QTextStream::AlignAccountingStyle && number) { + const QChar sign = data.size() > 0 ? QLatin1Char(*data.data()) : QChar(); + if (sign == locale.negativeSign() || sign == locale.positiveSign()) { + // write the sign before the padding, then skip it later + write(&sign, 1); + data = QLatin1String(data.data() + 1, data.size() - 1); + } + } + + writePadding(pad.left); + write(data); + writePadding(pad.right); + } else { + write(data); + } +} + +/*! + Constructs a QTextStream. Before you can use it for reading or + writing, you must assign a device or a string. + + \sa setDevice(), setString() +*/ +QTextStream::QTextStream() + : d_ptr(new QTextStreamPrivate(this)) +{ +#if defined (QTEXTSTREAM_DEBUG) + qDebug("QTextStream::QTextStream()"); +#endif + Q_D(QTextStream); + d->status = Ok; +} + +/*! + Constructs a QTextStream that operates on \a device. +*/ +QTextStream::QTextStream(QIODevice *device) + : d_ptr(new QTextStreamPrivate(this)) +{ +#if defined (QTEXTSTREAM_DEBUG) + qDebug("QTextStream::QTextStream(QIODevice *device == *%p)", + device); +#endif + Q_D(QTextStream); + d->device = device; +#ifndef QT_NO_QOBJECT + d->deviceClosedNotifier.setupDevice(this, d->device); +#endif + d->status = Ok; +} + +/*! + Constructs a QTextStream that operates on \a string, using \a + openMode to define the open mode. +*/ +QTextStream::QTextStream(QString *string, QIODevice::OpenMode openMode) + : d_ptr(new QTextStreamPrivate(this)) +{ +#if defined (QTEXTSTREAM_DEBUG) + qDebug("QTextStream::QTextStream(QString *string == *%p, openMode = %d)", + string, int(openMode)); +#endif + Q_D(QTextStream); + d->string = string; + d->stringOpenMode = openMode; + d->status = Ok; +} + +/*! + Constructs a QTextStream that operates on \a array, using \a + openMode to define the open mode. Internally, the array is wrapped + by a QBuffer. +*/ +QTextStream::QTextStream(QByteArray *array, QIODevice::OpenMode openMode) + : d_ptr(new QTextStreamPrivate(this)) +{ +#if defined (QTEXTSTREAM_DEBUG) + qDebug("QTextStream::QTextStream(QByteArray *array == *%p, openMode = %d)", + array, int(openMode)); +#endif + Q_D(QTextStream); + d->device = new QBuffer(array); + d->device->open(openMode); + d->deleteDevice = true; +#ifndef QT_NO_QOBJECT + d->deviceClosedNotifier.setupDevice(this, d->device); +#endif + d->status = Ok; +} + +/*! + Constructs a QTextStream that operates on \a array, using \a + openMode to define the open mode. The array is accessed as + read-only, regardless of the values in \a openMode. + + This constructor is convenient for working on constant + strings. Example: + + \snippet code/src_corelib_io_qtextstream.cpp 3 +*/ +QTextStream::QTextStream(const QByteArray &array, QIODevice::OpenMode openMode) + : d_ptr(new QTextStreamPrivate(this)) +{ +#if defined (QTEXTSTREAM_DEBUG) + qDebug("QTextStream::QTextStream(const QByteArray &array == *(%p), openMode = %d)", + &array, int(openMode)); +#endif + QBuffer *buffer = new QBuffer; + buffer->setData(array); + buffer->open(openMode); + + Q_D(QTextStream); + d->device = buffer; + d->deleteDevice = true; +#ifndef QT_NO_QOBJECT + d->deviceClosedNotifier.setupDevice(this, d->device); +#endif + d->status = Ok; +} + +/*! + Constructs a QTextStream that operates on \a fileHandle, using \a + openMode to define the open mode. Internally, a QFile is created + to handle the FILE pointer. + + This constructor is useful for working directly with the common + FILE based input and output streams: stdin, stdout and stderr. Example: + + \snippet code/src_corelib_io_qtextstream.cpp 4 +*/ + +QTextStream::QTextStream(FILE *fileHandle, QIODevice::OpenMode openMode) + : d_ptr(new QTextStreamPrivate(this)) +{ +#if defined (QTEXTSTREAM_DEBUG) + qDebug("QTextStream::QTextStream(FILE *fileHandle = %p, openMode = %d)", + fileHandle, int(openMode)); +#endif + QFile *file = new QFile; + file->open(fileHandle, openMode); + + Q_D(QTextStream); + d->device = file; + d->deleteDevice = true; +#ifndef QT_NO_QOBJECT + d->deviceClosedNotifier.setupDevice(this, d->device); +#endif + d->status = Ok; +} + +/*! + Destroys the QTextStream. + + If the stream operates on a device, flush() will be called + implicitly. Otherwise, the device is unaffected. +*/ +QTextStream::~QTextStream() +{ + Q_D(QTextStream); +#if defined (QTEXTSTREAM_DEBUG) + qDebug("QTextStream::~QTextStream()"); +#endif + if (!d->writeBuffer.isEmpty()) + d->flushWriteBuffer(); +} + +/*! + Resets QTextStream's formatting options, bringing it back to its + original constructed state. The device, string and any buffered + data is left untouched. +*/ +void QTextStream::reset() +{ + Q_D(QTextStream); + + d->params.reset(); +} + +/*! + Flushes any buffered data waiting to be written to the device. + + If QTextStream operates on a string, this function does nothing. +*/ +void QTextStream::flush() +{ + Q_D(QTextStream); + d->flushWriteBuffer(); +} + +/*! + Seeks to the position \a pos in the device. Returns \c true on + success; otherwise returns \c false. +*/ +bool QTextStream::seek(qint64 pos) +{ + Q_D(QTextStream); + d->lastTokenSize = 0; + + if (d->device) { + // Empty the write buffer + d->flushWriteBuffer(); + if (!d->device->seek(pos)) + return false; + d->resetReadBuffer(); + +#ifndef QT_NO_TEXTCODEC + // Reset the codec converter states. + resetCodecConverterStateHelper(&d->readConverterState); + resetCodecConverterStateHelper(&d->writeConverterState); + delete d->readConverterSavedState; + d->readConverterSavedState = 0; + d->writeConverterState.flags |= QTextCodec::IgnoreHeader; +#endif + return true; + } + + // string + if (d->string && pos <= d->string->size()) { + d->stringOffset = int(pos); + return true; + } + return false; +} + +/*! + \since 4.2 + + Returns the device position corresponding to the current position of the + stream, or -1 if an error occurs (e.g., if there is no device or string, + or if there's a device error). + + Because QTextStream is buffered, this function may have to + seek the device to reconstruct a valid device position. This + operation can be expensive, so you may want to avoid calling this + function in a tight loop. + + \sa seek() +*/ +qint64 QTextStream::pos() const +{ + Q_D(const QTextStream); + if (d->device) { + // Cutoff + if (d->readBuffer.isEmpty()) + return d->device->pos(); + if (d->device->isSequential()) + return 0; + + // Seek the device + if (!d->device->seek(d->readBufferStartDevicePos)) + return qint64(-1); + + // Reset the read buffer + QTextStreamPrivate *thatd = const_cast(d); + thatd->readBuffer.clear(); + +#ifndef QT_NO_TEXTCODEC + thatd->restoreToSavedConverterState(); + if (d->readBufferStartDevicePos == 0) + thatd->autoDetectUnicode = true; +#endif + + // Rewind the device to get to the current position Ensure that + // readBufferOffset is unaffected by fillReadBuffer() + int oldReadBufferOffset = d->readBufferOffset + d->readConverterSavedStateOffset; + while (d->readBuffer.size() < oldReadBufferOffset) { + if (!thatd->fillReadBuffer(1)) + return qint64(-1); + } + thatd->readBufferOffset = oldReadBufferOffset; + thatd->readConverterSavedStateOffset = 0; + + // Return the device position. + return d->device->pos(); + } + + if (d->string) + return d->stringOffset; + + qWarning("QTextStream::pos: no device"); + return qint64(-1); +} + +/*! + Reads and discards whitespace from the stream until either a + non-space character is detected, or until atEnd() returns + true. This function is useful when reading a stream character by + character. + + Whitespace characters are all characters for which + QChar::isSpace() returns \c true. + + \sa operator>>() +*/ +void QTextStream::skipWhiteSpace() +{ + Q_D(QTextStream); + CHECK_VALID_STREAM(Q_VOID); + d->scan(0, 0, 0, QTextStreamPrivate::NotSpace); + d->consumeLastToken(); +} + +/*! + Sets the current device to \a device. If a device has already been + assigned, QTextStream will call flush() before the old device is + replaced. + + \note This function resets locale to the default locale ('C') + and codec to the default codec, QTextCodec::codecForLocale(). + + \sa device(), setString() +*/ +void QTextStream::setDevice(QIODevice *device) +{ + Q_D(QTextStream); + flush(); + if (d->deleteDevice) { +#ifndef QT_NO_QOBJECT + d->deviceClosedNotifier.disconnect(); +#endif + delete d->device; + d->deleteDevice = false; + } + + d->reset(); + d->status = Ok; + d->device = device; + d->resetReadBuffer(); +#ifndef QT_NO_QOBJECT + d->deviceClosedNotifier.setupDevice(this, d->device); +#endif +} + +/*! + Returns the current device associated with the QTextStream, + or 0 if no device has been assigned. + + \sa setDevice(), string() +*/ +QIODevice *QTextStream::device() const +{ + Q_D(const QTextStream); + return d->device; +} + +/*! + Sets the current string to \a string, using the given \a + openMode. If a device has already been assigned, QTextStream will + call flush() before replacing it. + + \sa string(), setDevice() +*/ +void QTextStream::setString(QString *string, QIODevice::OpenMode openMode) +{ + Q_D(QTextStream); + flush(); + if (d->deleteDevice) { +#ifndef QT_NO_QOBJECT + d->deviceClosedNotifier.disconnect(); + d->device->blockSignals(true); +#endif + delete d->device; + d->deleteDevice = false; + } + + d->reset(); + d->status = Ok; + d->string = string; + d->stringOpenMode = openMode; +} + +/*! + Returns the current string assigned to the QTextStream, or 0 if no + string has been assigned. + + \sa setString(), device() +*/ +QString *QTextStream::string() const +{ + Q_D(const QTextStream); + return d->string; +} + +/*! + Sets the field alignment to \a mode. When used together with + setFieldWidth(), this function allows you to generate formatted + output with text aligned to the left, to the right or center + aligned. + + \sa fieldAlignment(), setFieldWidth() +*/ +void QTextStream::setFieldAlignment(FieldAlignment mode) +{ + Q_D(QTextStream); + d->params.fieldAlignment = mode; +} + +/*! + Returns the current field alignment. + + \sa setFieldAlignment(), fieldWidth() +*/ +QTextStream::FieldAlignment QTextStream::fieldAlignment() const +{ + Q_D(const QTextStream); + return d->params.fieldAlignment; +} + +/*! + Sets the pad character to \a ch. The default value is the ASCII + space character (' '), or QChar(0x20). This character is used to + fill in the space in fields when generating text. + + Example: + + \snippet code/src_corelib_io_qtextstream.cpp 5 + + The string \c s contains: + + \snippet code/src_corelib_io_qtextstream.cpp 6 + + \sa padChar(), setFieldWidth() +*/ +void QTextStream::setPadChar(QChar ch) +{ + Q_D(QTextStream); + d->params.padChar = ch; +} + +/*! + Returns the current pad character. + + \sa setPadChar(), setFieldWidth() +*/ +QChar QTextStream::padChar() const +{ + Q_D(const QTextStream); + return d->params.padChar; +} + +/*! + Sets the current field width to \a width. If \a width is 0 (the + default), the field width is equal to the length of the generated + text. + + \note The field width applies to every element appended to this + stream after this function has been called (e.g., it also pads + endl). This behavior is different from similar classes in the STL, + where the field width only applies to the next element. + + \sa fieldWidth(), setPadChar() +*/ +void QTextStream::setFieldWidth(int width) +{ + Q_D(QTextStream); + d->params.fieldWidth = width; +} + +/*! + Returns the current field width. + + \sa setFieldWidth() +*/ +int QTextStream::fieldWidth() const +{ + Q_D(const QTextStream); + return d->params.fieldWidth; +} + +/*! + Sets the current number flags to \a flags. \a flags is a set of + flags from the NumberFlag enum, and describes options for + formatting generated code (e.g., whether or not to always write + the base or sign of a number). + + \sa numberFlags(), setIntegerBase(), setRealNumberNotation() +*/ +void QTextStream::setNumberFlags(NumberFlags flags) +{ + Q_D(QTextStream); + d->params.numberFlags = flags; +} + +/*! + Returns the current number flags. + + \sa setNumberFlags(), integerBase(), realNumberNotation() +*/ +QTextStream::NumberFlags QTextStream::numberFlags() const +{ + Q_D(const QTextStream); + return d->params.numberFlags; +} + +/*! + Sets the base of integers to \a base, both for reading and for + generating numbers. \a base can be either 2 (binary), 8 (octal), + 10 (decimal) or 16 (hexadecimal). If \a base is 0, QTextStream + will attempt to detect the base by inspecting the data on the + stream. When generating numbers, QTextStream assumes base is 10 + unless the base has been set explicitly. + + \sa integerBase(), QString::number(), setNumberFlags() +*/ +void QTextStream::setIntegerBase(int base) +{ + Q_D(QTextStream); + d->params.integerBase = base; +} + +/*! + Returns the current base of integers. 0 means that the base is + detected when reading, or 10 (decimal) when generating numbers. + + \sa setIntegerBase(), QString::number(), numberFlags() +*/ +int QTextStream::integerBase() const +{ + Q_D(const QTextStream); + return d->params.integerBase; +} + +/*! + Sets the real number notation to \a notation (SmartNotation, + FixedNotation, ScientificNotation). When reading and generating + numbers, QTextStream uses this value to detect the formatting of + real numbers. + + \sa realNumberNotation(), setRealNumberPrecision(), setNumberFlags(), setIntegerBase() +*/ +void QTextStream::setRealNumberNotation(RealNumberNotation notation) +{ + Q_D(QTextStream); + d->params.realNumberNotation = notation; +} + +/*! + Returns the current real number notation. + + \sa setRealNumberNotation(), realNumberPrecision(), numberFlags(), integerBase() +*/ +QTextStream::RealNumberNotation QTextStream::realNumberNotation() const +{ + Q_D(const QTextStream); + return d->params.realNumberNotation; +} + +/*! + Sets the precision of real numbers to \a precision. This value + describes the number of fraction digits QTextStream should + write when generating real numbers. + + The precision cannot be a negative value. The default value is 6. + + \sa realNumberPrecision(), setRealNumberNotation() +*/ +void QTextStream::setRealNumberPrecision(int precision) +{ + Q_D(QTextStream); + if (precision < 0) { + qWarning("QTextStream::setRealNumberPrecision: Invalid precision (%d)", precision); + d->params.realNumberPrecision = 6; + return; + } + d->params.realNumberPrecision = precision; +} + +/*! + Returns the current real number precision, or the number of fraction + digits QTextStream will write when generating real numbers. + + \sa setRealNumberNotation(), realNumberNotation(), numberFlags(), integerBase() +*/ +int QTextStream::realNumberPrecision() const +{ + Q_D(const QTextStream); + return d->params.realNumberPrecision; +} + +/*! + Returns the status of the text stream. + + \sa QTextStream::Status, setStatus(), resetStatus() +*/ + +QTextStream::Status QTextStream::status() const +{ + Q_D(const QTextStream); + return d->status; +} + +/*! + \since 4.1 + + Resets the status of the text stream. + + \sa QTextStream::Status, status(), setStatus() +*/ +void QTextStream::resetStatus() +{ + Q_D(QTextStream); + d->status = Ok; +} + +/*! + \since 4.1 + + Sets the status of the text stream to the \a status given. + + Subsequent calls to setStatus() are ignored until resetStatus() + is called. + + \sa Status, status(), resetStatus() +*/ +void QTextStream::setStatus(Status status) +{ + Q_D(QTextStream); + if (d->status == Ok) + d->status = status; +} + +/*! + Returns \c true if there is no more data to be read from the + QTextStream; otherwise returns \c false. This is similar to, but not + the same as calling QIODevice::atEnd(), as QTextStream also takes + into account its internal Unicode buffer. +*/ +bool QTextStream::atEnd() const +{ + Q_D(const QTextStream); + CHECK_VALID_STREAM(true); + + if (d->string) + return d->string->size() == d->stringOffset; + return d->readBuffer.isEmpty() && d->device->atEnd(); +} + +/*! + Reads the entire content of the stream, and returns it as a + QString. Avoid this function when working on large files, as it + will consume a significant amount of memory. + + Calling \l {QTextStream::readLine()}{readLine()} is better if you do not know how much data is + available. + + \sa readLine() +*/ +QString QTextStream::readAll() +{ + Q_D(QTextStream); + CHECK_VALID_STREAM(QString()); + + return d->read(INT_MAX); +} + +/*! + Reads one line of text from the stream, and returns it as a + QString. The maximum allowed line length is set to \a maxlen. If + the stream contains lines longer than this, then the lines will be + split after \a maxlen characters and returned in parts. + + If \a maxlen is 0, the lines can be of any length. + + The returned line has no trailing end-of-line characters ("\\n" + or "\\r\\n"), so calling QString::trimmed() can be unnecessary. + + If the stream has read to the end of the file, \l {QTextStream::readLine()}{readLine()} + will return a null QString. For strings, or for devices that support it, + you can explicitly test for the end of the stream using atEnd(). + + \sa readAll(), QIODevice::readLine() +*/ +QString QTextStream::readLine(qint64 maxlen) +{ + QString line; + + readLineInto(&line, maxlen); + return line; +} + +/*! + \since 5.5 + + Reads one line of text from the stream into \a line. + If \a line is 0, the read line is not stored. + + The maximum allowed line length is set to \a maxlen. If + the stream contains lines longer than this, then the lines will be + split after \a maxlen characters and returned in parts. + + If \a maxlen is 0, the lines can be of any length. + + The resulting line has no trailing end-of-line characters ("\\n" + or "\\r\\n"), so calling QString::trimmed() can be unnecessary. + + If \a line has sufficient capacity for the data that is about to be + read, this function may not need to allocate new memory. Because of + this, it can be faster than readLine(). + + Returns \c false if the stream has read to the end of the file or + an error has occurred; otherwise returns \c true. The contents in + \a line before the call are discarded in any case. + + \sa readAll(), QIODevice::readLine() +*/ +bool QTextStream::readLineInto(QString *line, qint64 maxlen) +{ + Q_D(QTextStream); + // keep in sync with CHECK_VALID_STREAM + if (!d->string && !d->device) { + qWarning("QTextStream: No device"); + if (line && !line->isNull()) + line->resize(0); + return false; + } + + const QChar *readPtr; + int length; + if (!d->scan(&readPtr, &length, int(maxlen), QTextStreamPrivate::EndOfLine)) { + if (line && !line->isNull()) + line->resize(0); + return false; + } + + if (Q_LIKELY(line)) + line->setUnicode(readPtr, length); + d->consumeLastToken(); + return true; +} + +/*! + \since 4.1 + + Reads at most \a maxlen characters from the stream, and returns the data + read as a QString. + + \sa readAll(), readLine(), QIODevice::read() +*/ +QString QTextStream::read(qint64 maxlen) +{ + Q_D(QTextStream); + CHECK_VALID_STREAM(QString()); + + if (maxlen <= 0) + return QString::fromLatin1(""); // empty, not null + + return d->read(int(maxlen)); +} + +/*! + \internal +*/ +QTextStreamPrivate::NumberParsingStatus QTextStreamPrivate::getNumber(qulonglong *ret) +{ + scan(0, 0, 0, NotSpace); + consumeLastToken(); + + // detect int encoding + int base = params.integerBase; + if (base == 0) { + QChar ch; + if (!getChar(&ch)) + return npsInvalidPrefix; + if (ch == QLatin1Char('0')) { + QChar ch2; + if (!getChar(&ch2)) { + // Result is the number 0 + *ret = 0; + return npsOk; + } + ch2 = ch2.toLower(); + + if (ch2 == QLatin1Char('x')) { + base = 16; + } else if (ch2 == QLatin1Char('b')) { + base = 2; + } else if (ch2.isDigit() && ch2.digitValue() >= 0 && ch2.digitValue() <= 7) { + base = 8; + } else { + base = 10; + } + ungetChar(ch2); + } else if (ch == locale.negativeSign() || ch == locale.positiveSign() || ch.isDigit()) { + base = 10; + } else { + ungetChar(ch); + return npsInvalidPrefix; + } + ungetChar(ch); + // State of the stream is now the same as on entry + // (cursor is at prefix), + // and local variable 'base' has been set appropriately. + } + + qulonglong val=0; + switch (base) { + case 2: { + QChar pf1, pf2, dig; + // Parse prefix '0b' + if (!getChar(&pf1) || pf1 != QLatin1Char('0')) + return npsInvalidPrefix; + if (!getChar(&pf2) || pf2.toLower() != QLatin1Char('b')) + return npsInvalidPrefix; + // Parse digits + int ndigits = 0; + while (getChar(&dig)) { + int n = dig.toLower().unicode(); + if (n == '0' || n == '1') { + val <<= 1; + val += n - '0'; + } else { + ungetChar(dig); + break; + } + ndigits++; + } + if (ndigits == 0) { + // Unwind the prefix and abort + ungetChar(pf2); + ungetChar(pf1); + return npsMissingDigit; + } + break; + } + case 8: { + QChar pf, dig; + // Parse prefix '0' + if (!getChar(&pf) || pf != QLatin1Char('0')) + return npsInvalidPrefix; + // Parse digits + int ndigits = 0; + while (getChar(&dig)) { + int n = dig.toLower().unicode(); + if (n >= '0' && n <= '7') { + val *= 8; + val += n - '0'; + } else { + ungetChar(dig); + break; + } + ndigits++; + } + if (ndigits == 0) { + // Unwind the prefix and abort + ungetChar(pf); + return npsMissingDigit; + } + break; + } + case 10: { + // Parse sign (or first digit) + QChar sign; + int ndigits = 0; + if (!getChar(&sign)) + return npsMissingDigit; + if (sign != locale.negativeSign() && sign != locale.positiveSign()) { + if (!sign.isDigit()) { + ungetChar(sign); + return npsMissingDigit; + } + val += sign.digitValue(); + ndigits++; + } + // Parse digits + QChar ch; + while (getChar(&ch)) { + if (ch.isDigit()) { + val *= 10; + val += ch.digitValue(); + } else if (locale != QLocale::c() && ch == locale.groupSeparator()) { + continue; + } else { + ungetChar(ch); + break; + } + ndigits++; + } + if (ndigits == 0) + return npsMissingDigit; + if (sign == locale.negativeSign()) { + qlonglong ival = qlonglong(val); + if (ival > 0) + ival = -ival; + val = qulonglong(ival); + } + break; + } + case 16: { + QChar pf1, pf2, dig; + // Parse prefix ' 0x' + if (!getChar(&pf1) || pf1 != QLatin1Char('0')) + return npsInvalidPrefix; + if (!getChar(&pf2) || pf2.toLower() != QLatin1Char('x')) + return npsInvalidPrefix; + // Parse digits + int ndigits = 0; + while (getChar(&dig)) { + int n = dig.toLower().unicode(); + if (n >= '0' && n <= '9') { + val <<= 4; + val += n - '0'; + } else if (n >= 'a' && n <= 'f') { + val <<= 4; + val += 10 + (n - 'a'); + } else { + ungetChar(dig); + break; + } + ndigits++; + } + if (ndigits == 0) { + return npsMissingDigit; + } + break; + } + default: + // Unsupported integerBase + return npsInvalidPrefix; + } + + if (ret) + *ret = val; + return npsOk; +} + +/*! + \internal + (hihi) +*/ +bool QTextStreamPrivate::getReal(double *f) +{ + // We use a table-driven FSM to parse floating point numbers + // strtod() cannot be used directly since we may be reading from a + // QIODevice. + enum ParserState { + Init = 0, + Sign = 1, + Mantissa = 2, + Dot = 3, + Abscissa = 4, + ExpMark = 5, + ExpSign = 6, + Exponent = 7, + Nan1 = 8, + Nan2 = 9, + Inf1 = 10, + Inf2 = 11, + NanInf = 12, + Done = 13 + }; + enum InputToken { + None = 0, + InputSign = 1, + InputDigit = 2, + InputDot = 3, + InputExp = 4, + InputI = 5, + InputN = 6, + InputF = 7, + InputA = 8, + InputT = 9 + }; + + static const uchar table[13][10] = { + // None InputSign InputDigit InputDot InputExp InputI InputN InputF InputA InputT + { 0, Sign, Mantissa, Dot, 0, Inf1, Nan1, 0, 0, 0 }, // 0 Init + { 0, 0, Mantissa, Dot, 0, Inf1, Nan1, 0, 0, 0 }, // 1 Sign + { Done, Done, Mantissa, Dot, ExpMark, 0, 0, 0, 0, 0 }, // 2 Mantissa + { 0, 0, Abscissa, 0, 0, 0, 0, 0, 0, 0 }, // 3 Dot + { Done, Done, Abscissa, Done, ExpMark, 0, 0, 0, 0, 0 }, // 4 Abscissa + { 0, ExpSign, Exponent, 0, 0, 0, 0, 0, 0, 0 }, // 5 ExpMark + { 0, 0, Exponent, 0, 0, 0, 0, 0, 0, 0 }, // 6 ExpSign + { Done, Done, Exponent, Done, Done, 0, 0, 0, 0, 0 }, // 7 Exponent + { 0, 0, 0, 0, 0, 0, 0, 0, Nan2, 0 }, // 8 Nan1 + { 0, 0, 0, 0, 0, 0, NanInf, 0, 0, 0 }, // 9 Nan2 + { 0, 0, 0, 0, 0, 0, Inf2, 0, 0, 0 }, // 10 Inf1 + { 0, 0, 0, 0, 0, 0, 0, NanInf, 0, 0 }, // 11 Inf2 + { Done, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // 11 NanInf + }; + + ParserState state = Init; + InputToken input = None; + + scan(0, 0, 0, NotSpace); + consumeLastToken(); + + const int BufferSize = 128; + char buf[BufferSize]; + int i = 0; + + QChar c; + while (getChar(&c)) { + switch (c.unicode()) { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + input = InputDigit; + break; + case 'i': case 'I': + input = InputI; + break; + case 'n': case 'N': + input = InputN; + break; + case 'f': case 'F': + input = InputF; + break; + case 'a': case 'A': + input = InputA; + break; + case 't': case 'T': + input = InputT; + break; + default: { + QChar lc = c.toLower(); + if (lc == locale.decimalPoint().toLower()) + input = InputDot; + else if (lc == locale.exponential().toLower()) + input = InputExp; + else if (lc == locale.negativeSign().toLower() + || lc == locale.positiveSign().toLower()) + input = InputSign; + else if (locale != QLocale::c() // backward-compatibility + && lc == locale.groupSeparator().toLower()) + input = InputDigit; // well, it isn't a digit, but no one cares. + else + input = None; + } + break; + } + + state = ParserState(table[state][input]); + + if (state == Init || state == Done || i > (BufferSize - 5)) { + ungetChar(c); + if (i > (BufferSize - 5)) { // ignore rest of digits + while (getChar(&c)) { + if (!c.isDigit()) { + ungetChar(c); + break; + } + } + } + break; + } + + buf[i++] = c.toLatin1(); + } + + if (i == 0) + return false; + if (!f) + return true; + buf[i] = '\0'; + + // backward-compatibility. Old implementation supported +nan/-nan + // for some reason. QLocale only checks for lower-case + // nan/+inf/-inf, so here we also check for uppercase and mixed + // case versions. + if (!qstricmp(buf, "nan") || !qstricmp(buf, "+nan") || !qstricmp(buf, "-nan")) { + *f = qSNaN(); + return true; + } else if (!qstricmp(buf, "+inf") || !qstricmp(buf, "inf")) { + *f = qInf(); + return true; + } else if (!qstricmp(buf, "-inf")) { + *f = -qInf(); + return true; + } + bool ok; + *f = locale.toDouble(QString::fromLatin1(buf), &ok); + return ok; +} + +/*! + Reads a character from the stream and stores it in \a c. Returns a + reference to the QTextStream, so several operators can be + nested. Example: + + \snippet code/src_corelib_io_qtextstream.cpp 7 + + Whitespace is \e not skipped. +*/ + +QTextStream &QTextStream::operator>>(QChar &c) +{ + Q_D(QTextStream); + CHECK_VALID_STREAM(*this); + d->scan(0, 0, 0, QTextStreamPrivate::NotSpace); + if (!d->getChar(&c)) + setStatus(ReadPastEnd); + return *this; +} + +/*! + \overload + + Reads a character from the stream and stores it in \a c. The + character from the stream is converted to ISO-5589-1 before it is + stored. + + \sa QChar::toLatin1() +*/ +QTextStream &QTextStream::operator>>(char &c) +{ + QChar ch; + *this >> ch; + c = ch.toLatin1(); + return *this; +} + +/*! + Reads an integer from the stream and stores it in \a i, then + returns a reference to the QTextStream. The number is cast to + the correct type before it is stored. If no number was detected on + the stream, \a i is set to 0. + + By default, QTextStream will attempt to detect the base of the + number using the following rules: + + \table + \header \li Prefix \li Base + \row \li "0b" or "0B" \li 2 (binary) + \row \li "0" followed by "0-7" \li 8 (octal) + \row \li "0" otherwise \li 10 (decimal) + \row \li "0x" or "0X" \li 16 (hexadecimal) + \row \li "1" to "9" \li 10 (decimal) + \endtable + + By calling setIntegerBase(), you can specify the integer base + explicitly. This will disable the auto-detection, and speed up + QTextStream slightly. + + Leading whitespace is skipped. +*/ +QTextStream &QTextStream::operator>>(signed short &i) +{ + IMPLEMENT_STREAM_RIGHT_INT_OPERATOR(signed short); +} + +/*! + \overload + + Stores the integer in the unsigned short \a i. +*/ +QTextStream &QTextStream::operator>>(unsigned short &i) +{ + IMPLEMENT_STREAM_RIGHT_INT_OPERATOR(unsigned short); +} + +/*! + \overload + + Stores the integer in the signed int \a i. +*/ +QTextStream &QTextStream::operator>>(signed int &i) +{ + IMPLEMENT_STREAM_RIGHT_INT_OPERATOR(signed int); +} + +/*! + \overload + + Stores the integer in the unsigned int \a i. +*/ +QTextStream &QTextStream::operator>>(unsigned int &i) +{ + IMPLEMENT_STREAM_RIGHT_INT_OPERATOR(unsigned int); +} + +/*! + \overload + + Stores the integer in the signed long \a i. +*/ +QTextStream &QTextStream::operator>>(signed long &i) +{ + IMPLEMENT_STREAM_RIGHT_INT_OPERATOR(signed long); +} + +/*! + \overload + + Stores the integer in the unsigned long \a i. +*/ +QTextStream &QTextStream::operator>>(unsigned long &i) +{ + IMPLEMENT_STREAM_RIGHT_INT_OPERATOR(unsigned long); +} + +/*! + \overload + + Stores the integer in the qlonglong \a i. +*/ +QTextStream &QTextStream::operator>>(qlonglong &i) +{ + IMPLEMENT_STREAM_RIGHT_INT_OPERATOR(qlonglong); +} + +/*! + \overload + + Stores the integer in the qulonglong \a i. +*/ +QTextStream &QTextStream::operator>>(qulonglong &i) +{ + IMPLEMENT_STREAM_RIGHT_INT_OPERATOR(qulonglong); +} + +/*! + Reads a real number from the stream and stores it in \a f, then + returns a reference to the QTextStream. The number is cast to + the correct type. If no real number is detect on the stream, \a f + is set to 0.0. + + As a special exception, QTextStream allows the strings "nan" and "inf" to + represent NAN and INF floats or doubles. + + Leading whitespace is skipped. +*/ +QTextStream &QTextStream::operator>>(float &f) +{ + IMPLEMENT_STREAM_RIGHT_REAL_OPERATOR(float); +} + +/*! + \overload + + Stores the real number in the double \a f. +*/ +QTextStream &QTextStream::operator>>(double &f) +{ + IMPLEMENT_STREAM_RIGHT_REAL_OPERATOR(double); +} + +/*! + Reads a word from the stream and stores it in \a str, then returns + a reference to the stream. Words are separated by whitespace + (i.e., all characters for which QChar::isSpace() returns \c true). + + Leading whitespace is skipped. +*/ +QTextStream &QTextStream::operator>>(QString &str) +{ + Q_D(QTextStream); + CHECK_VALID_STREAM(*this); + + str.clear(); + d->scan(0, 0, 0, QTextStreamPrivate::NotSpace); + d->consumeLastToken(); + + const QChar *ptr; + int length; + if (!d->scan(&ptr, &length, 0, QTextStreamPrivate::Space)) { + setStatus(ReadPastEnd); + return *this; + } + + str = QString(ptr, length); + d->consumeLastToken(); + return *this; +} + +/*! + \overload + + Converts the word to ISO-8859-1, then stores it in \a array. + + \sa QString::toLatin1() +*/ +QTextStream &QTextStream::operator>>(QByteArray &array) +{ + Q_D(QTextStream); + CHECK_VALID_STREAM(*this); + + array.clear(); + d->scan(0, 0, 0, QTextStreamPrivate::NotSpace); + d->consumeLastToken(); + + const QChar *ptr; + int length; + if (!d->scan(&ptr, &length, 0, QTextStreamPrivate::Space)) { + setStatus(ReadPastEnd); + return *this; + } + + for (int i = 0; i < length; ++i) + array += ptr[i].toLatin1(); + + d->consumeLastToken(); + return *this; +} + +/*! + \overload + + Stores the word in \a c, terminated by a '\\0' character. If no word is + available, only the '\\0' character is stored. + + Warning: Although convenient, this operator is dangerous and must + be used with care. QTextStream assumes that \a c points to a + buffer with enough space to hold the word. If the buffer is too + small, your application may crash. + + If possible, use the QByteArray operator instead. +*/ +QTextStream &QTextStream::operator>>(char *c) +{ + Q_D(QTextStream); + *c = 0; + CHECK_VALID_STREAM(*this); + d->scan(0, 0, 0, QTextStreamPrivate::NotSpace); + d->consumeLastToken(); + + const QChar *ptr; + int length; + if (!d->scan(&ptr, &length, 0, QTextStreamPrivate::Space)) { + setStatus(ReadPastEnd); + return *this; + } + + for (int i = 0; i < length; ++i) + *c++ = ptr[i].toLatin1(); + *c = '\0'; + d->consumeLastToken(); + return *this; +} + +/*! + \internal + */ +void QTextStreamPrivate::putNumber(qulonglong number, bool negative) +{ + QString result; + + unsigned flags = 0; + const QTextStream::NumberFlags numberFlags = params.numberFlags; + if (numberFlags & QTextStream::ShowBase) + flags |= QLocaleData::ShowBase; + if (numberFlags & QTextStream::ForceSign) + flags |= QLocaleData::AlwaysShowSign; + if (numberFlags & QTextStream::UppercaseBase) + flags |= QLocaleData::UppercaseBase; + if (numberFlags & QTextStream::UppercaseDigits) + flags |= QLocaleData::CapitalEorX; + + // add thousands group separators. For backward compatibility we + // don't add a group separator for C locale. + if (locale != QLocale::c() && !locale.numberOptions().testFlag(QLocale::OmitGroupSeparator)) + flags |= QLocaleData::ThousandsGroup; + + const QLocaleData *dd = locale.d->m_data; + int base = params.integerBase ? params.integerBase : 10; + if (negative && base == 10) { + result = dd->longLongToString(-static_cast(number), -1, + base, -1, flags); + } else if (negative) { + // Workaround for backward compatibility for writing negative + // numbers in octal and hex: + // QTextStream(result) << showbase << hex << -1 << oct << -1 + // should output: -0x1 -0b1 + result = dd->unsLongLongToString(number, -1, base, -1, flags); + result.prepend(locale.negativeSign()); + } else { + result = dd->unsLongLongToString(number, -1, base, -1, flags); + // workaround for backward compatibility - in octal form with + // ShowBase flag set zero should be written as '00' + if (number == 0 && base == 8 && params.numberFlags & QTextStream::ShowBase + && result == QLatin1String("0")) { + result.prepend(QLatin1Char('0')); + } + } + putString(result, true); +} + +/*! + Writes the character \a c to the stream, then returns a reference + to the QTextStream. + + \sa setFieldWidth() +*/ +QTextStream &QTextStream::operator<<(QChar c) +{ + Q_D(QTextStream); + CHECK_VALID_STREAM(*this); + d->putChar(c); + return *this; +} + +/*! + \overload + + Converts \a c from ASCII to a QChar, then writes it to the stream. +*/ +QTextStream &QTextStream::operator<<(char c) +{ + Q_D(QTextStream); + CHECK_VALID_STREAM(*this); + d->putChar(QChar::fromLatin1(c)); + return *this; +} + +/*! + Writes the integer number \a i to the stream, then returns a + reference to the QTextStream. By default, the number is stored in + decimal form, but you can also set the base by calling + setIntegerBase(). + + \sa setFieldWidth(), setNumberFlags() +*/ +QTextStream &QTextStream::operator<<(signed short i) +{ + Q_D(QTextStream); + CHECK_VALID_STREAM(*this); + d->putNumber((qulonglong)qAbs(qlonglong(i)), i < 0); + return *this; +} + +/*! + \overload + + Writes the unsigned short \a i to the stream. +*/ +QTextStream &QTextStream::operator<<(unsigned short i) +{ + Q_D(QTextStream); + CHECK_VALID_STREAM(*this); + d->putNumber((qulonglong)i, false); + return *this; +} + +/*! + \overload + + Writes the signed int \a i to the stream. +*/ +QTextStream &QTextStream::operator<<(signed int i) +{ + Q_D(QTextStream); + CHECK_VALID_STREAM(*this); + d->putNumber((qulonglong)qAbs(qlonglong(i)), i < 0); + return *this; +} + +/*! + \overload + + Writes the unsigned int \a i to the stream. +*/ +QTextStream &QTextStream::operator<<(unsigned int i) +{ + Q_D(QTextStream); + CHECK_VALID_STREAM(*this); + d->putNumber((qulonglong)i, false); + return *this; +} + +/*! + \overload + + Writes the signed long \a i to the stream. +*/ +QTextStream &QTextStream::operator<<(signed long i) +{ + Q_D(QTextStream); + CHECK_VALID_STREAM(*this); + d->putNumber((qulonglong)qAbs(qlonglong(i)), i < 0); + return *this; +} + +/*! + \overload + + Writes the unsigned long \a i to the stream. +*/ +QTextStream &QTextStream::operator<<(unsigned long i) +{ + Q_D(QTextStream); + CHECK_VALID_STREAM(*this); + d->putNumber((qulonglong)i, false); + return *this; +} + +/*! + \overload + + Writes the qlonglong \a i to the stream. +*/ +QTextStream &QTextStream::operator<<(qlonglong i) +{ + Q_D(QTextStream); + CHECK_VALID_STREAM(*this); + d->putNumber((qulonglong)qAbs(i), i < 0); + return *this; +} + +/*! + \overload + + Writes the qulonglong \a i to the stream. +*/ +QTextStream &QTextStream::operator<<(qulonglong i) +{ + Q_D(QTextStream); + CHECK_VALID_STREAM(*this); + d->putNumber(i, false); + return *this; +} + +/*! + Writes the real number \a f to the stream, then returns a + reference to the QTextStream. By default, QTextStream stores it + using SmartNotation, with up to 6 digits of precision. You can + change the textual representation QTextStream will use for real + numbers by calling setRealNumberNotation(), + setRealNumberPrecision() and setNumberFlags(). + + \sa setFieldWidth(), setRealNumberNotation(), + setRealNumberPrecision(), setNumberFlags() +*/ +QTextStream &QTextStream::operator<<(float f) +{ + return *this << double(f); +} + +/*! + \overload + + Writes the double \a f to the stream. +*/ +QTextStream &QTextStream::operator<<(double f) +{ + Q_D(QTextStream); + CHECK_VALID_STREAM(*this); + + QLocaleData::DoubleForm form = QLocaleData::DFDecimal; + switch (realNumberNotation()) { + case FixedNotation: + form = QLocaleData::DFDecimal; + break; + case ScientificNotation: + form = QLocaleData::DFExponent; + break; + case SmartNotation: + form = QLocaleData::DFSignificantDigits; + break; + } + + uint flags = 0; + const QLocale::NumberOptions numberOptions = locale().numberOptions(); + if (numberFlags() & ShowBase) + flags |= QLocaleData::ShowBase; + if (numberFlags() & ForceSign) + flags |= QLocaleData::AlwaysShowSign; + if (numberFlags() & UppercaseBase) + flags |= QLocaleData::UppercaseBase; + if (numberFlags() & UppercaseDigits) + flags |= QLocaleData::CapitalEorX; + if (numberFlags() & ForcePoint) { + flags |= QLocaleData::ForcePoint; + + // Only for backwards compatibility + flags |= QLocaleData::AddTrailingZeroes | QLocaleData::ShowBase; + } + if (locale() != QLocale::c() && !(numberOptions & QLocale::OmitGroupSeparator)) + flags |= QLocaleData::ThousandsGroup; + if (!(numberOptions & QLocale::OmitLeadingZeroInExponent)) + flags |= QLocaleData::ZeroPadExponent; + if (numberOptions & QLocale::IncludeTrailingZeroesAfterDot) + flags |= QLocaleData::AddTrailingZeroes; + + const QLocaleData *dd = d->locale.d->m_data; + QString num = dd->doubleToString(f, d->params.realNumberPrecision, form, -1, flags); + d->putString(num, true); + return *this; +} + +/*! + Writes the string \a string to the stream, and returns a reference + to the QTextStream. The string is first encoded using the assigned + codec (the default codec is QTextCodec::codecForLocale()) before + it is written to the stream. + + \sa setFieldWidth(), setCodec() +*/ +QTextStream &QTextStream::operator<<(const QString &string) +{ + Q_D(QTextStream); + CHECK_VALID_STREAM(*this); + d->putString(string); + return *this; +} + +/*! + \overload + + Writes \a string to the stream, and returns a reference to the + QTextStream. +*/ +QTextStream &QTextStream::operator<<(QLatin1String string) +{ + Q_D(QTextStream); + CHECK_VALID_STREAM(*this); + d->putString(string); + return *this; +} + +/*! + \since 5.6 + \overload + + Writes \a string to the stream, and returns a reference to the + QTextStream. +*/ +QTextStream &QTextStream::operator<<(const QStringRef &string) +{ + Q_D(QTextStream); + CHECK_VALID_STREAM(*this); + d->putString(string.data(), string.size()); + return *this; +} + +/*! + \overload + + Writes \a array to the stream. The contents of \a array are + converted with QString::fromUtf8(). +*/ +QTextStream &QTextStream::operator<<(const QByteArray &array) +{ + Q_D(QTextStream); + CHECK_VALID_STREAM(*this); + d->putString(QString::fromUtf8(array.constData(), array.length())); + return *this; +} + +/*! + \overload + + Writes the constant string pointed to by \a string to the stream. \a + string is assumed to be in ISO-8859-1 encoding. This operator + is convenient when working with constant string data. Example: + + \snippet code/src_corelib_io_qtextstream.cpp 8 + + Warning: QTextStream assumes that \a string points to a string of + text, terminated by a '\\0' character. If there is no terminating + '\\0' character, your application may crash. +*/ +QTextStream &QTextStream::operator<<(const char *string) +{ + Q_D(QTextStream); + CHECK_VALID_STREAM(*this); + // ### Qt6: consider changing to UTF-8 + d->putString(QLatin1String(string)); + return *this; +} + +/*! + \overload + + Writes \a ptr to the stream as a hexadecimal number with a base. +*/ + +QTextStream &QTextStream::operator<<(const void *ptr) +{ + Q_D(QTextStream); + CHECK_VALID_STREAM(*this); + const int oldBase = d->params.integerBase; + const NumberFlags oldFlags = d->params.numberFlags; + d->params.integerBase = 16; + d->params.numberFlags |= ShowBase; + d->putNumber(reinterpret_cast(ptr), false); + d->params.integerBase = oldBase; + d->params.numberFlags = oldFlags; + return *this; +} + +/*! + \relates QTextStream + + Calls QTextStream::setIntegerBase(2) on \a stream and returns \a + stream. + + \sa oct(), dec(), hex(), {QTextStream manipulators} +*/ +QTextStream &bin(QTextStream &stream) +{ + stream.setIntegerBase(2); + return stream; +} + +/*! + \relates QTextStream + + Calls QTextStream::setIntegerBase(8) on \a stream and returns \a + stream. + + \sa bin(), dec(), hex(), {QTextStream manipulators} +*/ +QTextStream &oct(QTextStream &stream) +{ + stream.setIntegerBase(8); + return stream; +} + +/*! + \relates QTextStream + + Calls QTextStream::setIntegerBase(10) on \a stream and returns \a + stream. + + \sa bin(), oct(), hex(), {QTextStream manipulators} +*/ +QTextStream &dec(QTextStream &stream) +{ + stream.setIntegerBase(10); + return stream; +} + +/*! + \relates QTextStream + + Calls QTextStream::setIntegerBase(16) on \a stream and returns \a + stream. + + \note The hex modifier can only be used for writing to streams. + \sa bin(), oct(), dec(), {QTextStream manipulators} +*/ +QTextStream &hex(QTextStream &stream) +{ + stream.setIntegerBase(16); + return stream; +} + +/*! + \relates QTextStream + + Calls QTextStream::setNumberFlags(QTextStream::numberFlags() | + QTextStream::ShowBase) on \a stream and returns \a stream. + + \sa noshowbase(), forcesign(), forcepoint(), {QTextStream manipulators} +*/ +QTextStream &showbase(QTextStream &stream) +{ + stream.setNumberFlags(stream.numberFlags() | QTextStream::ShowBase); + return stream; +} + +/*! + \relates QTextStream + + Calls QTextStream::setNumberFlags(QTextStream::numberFlags() | + QTextStream::ForceSign) on \a stream and returns \a stream. + + \sa noforcesign(), forcepoint(), showbase(), {QTextStream manipulators} +*/ +QTextStream &forcesign(QTextStream &stream) +{ + stream.setNumberFlags(stream.numberFlags() | QTextStream::ForceSign); + return stream; +} + +/*! + \relates QTextStream + + Calls QTextStream::setNumberFlags(QTextStream::numberFlags() | + QTextStream::ForcePoint) on \a stream and returns \a stream. + + \sa noforcepoint(), forcesign(), showbase(), {QTextStream manipulators} +*/ +QTextStream &forcepoint(QTextStream &stream) +{ + stream.setNumberFlags(stream.numberFlags() | QTextStream::ForcePoint); + return stream; +} + +/*! + \relates QTextStream + + Calls QTextStream::setNumberFlags(QTextStream::numberFlags() & + ~QTextStream::ShowBase) on \a stream and returns \a stream. + + \sa showbase(), noforcesign(), noforcepoint(), {QTextStream manipulators} +*/ +QTextStream &noshowbase(QTextStream &stream) +{ + stream.setNumberFlags(stream.numberFlags() &= ~QTextStream::ShowBase); + return stream; +} + +/*! + \relates QTextStream + + Calls QTextStream::setNumberFlags(QTextStream::numberFlags() & + ~QTextStream::ForceSign) on \a stream and returns \a stream. + + \sa forcesign(), noforcepoint(), noshowbase(), {QTextStream manipulators} +*/ +QTextStream &noforcesign(QTextStream &stream) +{ + stream.setNumberFlags(stream.numberFlags() &= ~QTextStream::ForceSign); + return stream; +} + +/*! + \relates QTextStream + + Calls QTextStream::setNumberFlags(QTextStream::numberFlags() & + ~QTextStream::ForcePoint) on \a stream and returns \a stream. + + \sa forcepoint(), noforcesign(), noshowbase(), {QTextStream manipulators} +*/ +QTextStream &noforcepoint(QTextStream &stream) +{ + stream.setNumberFlags(stream.numberFlags() &= ~QTextStream::ForcePoint); + return stream; +} + +/*! + \relates QTextStream + + Calls QTextStream::setNumberFlags(QTextStream::numberFlags() | + QTextStream::UppercaseBase) on \a stream and returns \a stream. + + \sa lowercasebase(), uppercasedigits(), {QTextStream manipulators} +*/ +QTextStream &uppercasebase(QTextStream &stream) +{ + stream.setNumberFlags(stream.numberFlags() | QTextStream::UppercaseBase); + return stream; +} + +/*! + \relates QTextStream + + Calls QTextStream::setNumberFlags(QTextStream::numberFlags() | + QTextStream::UppercaseDigits) on \a stream and returns \a stream. + + \sa lowercasedigits(), uppercasebase(), {QTextStream manipulators} +*/ +QTextStream &uppercasedigits(QTextStream &stream) +{ + stream.setNumberFlags(stream.numberFlags() | QTextStream::UppercaseDigits); + return stream; +} + +/*! + \relates QTextStream + + Calls QTextStream::setNumberFlags(QTextStream::numberFlags() & + ~QTextStream::UppercaseBase) on \a stream and returns \a stream. + + \sa uppercasebase(), lowercasedigits(), {QTextStream manipulators} +*/ +QTextStream &lowercasebase(QTextStream &stream) +{ + stream.setNumberFlags(stream.numberFlags() & ~QTextStream::UppercaseBase); + return stream; +} + +/*! + \relates QTextStream + + Calls QTextStream::setNumberFlags(QTextStream::numberFlags() & + ~QTextStream::UppercaseDigits) on \a stream and returns \a stream. + + \sa uppercasedigits(), lowercasebase(), {QTextStream manipulators} +*/ +QTextStream &lowercasedigits(QTextStream &stream) +{ + stream.setNumberFlags(stream.numberFlags() & ~QTextStream::UppercaseDigits); + return stream; +} + +/*! + \relates QTextStream + + Calls QTextStream::setRealNumberNotation(QTextStream::FixedNotation) + on \a stream and returns \a stream. + + \sa scientific(), {QTextStream manipulators} +*/ +QTextStream &fixed(QTextStream &stream) +{ + stream.setRealNumberNotation(QTextStream::FixedNotation); + return stream; +} + +/*! + \relates QTextStream + + Calls QTextStream::setRealNumberNotation(QTextStream::ScientificNotation) + on \a stream and returns \a stream. + + \sa fixed(), {QTextStream manipulators} +*/ +QTextStream &scientific(QTextStream &stream) +{ + stream.setRealNumberNotation(QTextStream::ScientificNotation); + return stream; +} + +/*! + \relates QTextStream + + Calls QTextStream::setFieldAlignment(QTextStream::AlignLeft) + on \a stream and returns \a stream. + + \sa {QTextStream::}{right()}, {QTextStream::}{center()}, {QTextStream manipulators} +*/ +QTextStream &left(QTextStream &stream) +{ + stream.setFieldAlignment(QTextStream::AlignLeft); + return stream; +} + +/*! + \relates QTextStream + + Calls QTextStream::setFieldAlignment(QTextStream::AlignRight) + on \a stream and returns \a stream. + + \sa {QTextStream::}{left()}, {QTextStream::}{center()}, {QTextStream manipulators} +*/ +QTextStream &right(QTextStream &stream) +{ + stream.setFieldAlignment(QTextStream::AlignRight); + return stream; +} + +/*! + \relates QTextStream + + Calls QTextStream::setFieldAlignment(QTextStream::AlignCenter) + on \a stream and returns \a stream. + + \sa {QTextStream::}{left()}, {QTextStream::}{right()}, {QTextStream manipulators} +*/ +QTextStream ¢er(QTextStream &stream) +{ + stream.setFieldAlignment(QTextStream::AlignCenter); + return stream; +} + +/*! + \relates QTextStream + + Writes '\\n' to the \a stream and flushes the stream. + + Equivalent to + + \snippet code/src_corelib_io_qtextstream.cpp 9 + + Note: On Windows, all '\\n' characters are written as '\\r\\n' if + QTextStream's device or string is opened using the QIODevice::Text flag. + + \sa flush(), reset(), {QTextStream manipulators} +*/ +QTextStream &endl(QTextStream &stream) +{ + return stream << QLatin1Char('\n') << flush; +} + +/*! + \relates QTextStream + + Calls QTextStream::flush() on \a stream and returns \a stream. + + \sa endl(), reset(), {QTextStream manipulators} +*/ +QTextStream &flush(QTextStream &stream) +{ + stream.flush(); + return stream; +} + +/*! + \relates QTextStream + + Calls QTextStream::reset() on \a stream and returns \a stream. + + \sa flush(), {QTextStream manipulators} +*/ +QTextStream &reset(QTextStream &stream) +{ + stream.reset(); + return stream; +} + +/*! + \relates QTextStream + + Calls \l {QTextStream::}{skipWhiteSpace()} on \a stream and returns \a stream. + + \sa {QTextStream manipulators} +*/ +QTextStream &ws(QTextStream &stream) +{ + stream.skipWhiteSpace(); + return stream; +} + +/*! + \fn QTextStreamManipulator qSetFieldWidth(int width) + \relates QTextStream + + Equivalent to QTextStream::setFieldWidth(\a width). +*/ + +/*! + \fn QTextStreamManipulator qSetPadChar(QChar ch) + \relates QTextStream + + Equivalent to QTextStream::setPadChar(\a ch). +*/ + +/*! + \fn QTextStreamManipulator qSetRealNumberPrecision(int precision) + \relates QTextStream + + Equivalent to QTextStream::setRealNumberPrecision(\a precision). +*/ + +#ifndef QT_NO_TEXTCODEC +/*! + \relates QTextStream + + Toggles insertion of the Byte Order Mark on \a stream when QTextStream is + used with a UTF codec. + + \sa QTextStream::setGenerateByteOrderMark(), {QTextStream manipulators} +*/ +QTextStream &bom(QTextStream &stream) +{ + stream.setGenerateByteOrderMark(true); + return stream; +} + +/*! + Sets the codec for this stream to \a codec. The codec is used for + decoding any data that is read from the assigned device, and for + encoding any data that is written. By default, + QTextCodec::codecForLocale() is used, and automatic unicode + detection is enabled. + + If QTextStream operates on a string, this function does nothing. + + \warning If you call this function while the text stream is reading + from an open sequential socket, the internal buffer may still contain + text decoded using the old codec. + + \sa codec(), setAutoDetectUnicode(), setLocale() +*/ +void QTextStream::setCodec(QTextCodec *codec) +{ + Q_D(QTextStream); + qint64 seekPos = -1; + if (!d->readBuffer.isEmpty()) { + if (!d->device->isSequential()) { + seekPos = pos(); + } + } + d->codec = codec; + if (seekPos >=0 && !d->readBuffer.isEmpty()) + seek(seekPos); +} + +/*! + Sets the codec for this stream to the QTextCodec for the encoding + specified by \a codecName. Common values for \c codecName include + "ISO 8859-1", "UTF-8", and "UTF-16". If the encoding isn't + recognized, nothing happens. + + Example: + + \snippet code/src_corelib_io_qtextstream.cpp 10 + + \sa QTextCodec::codecForName(), setLocale() +*/ +void QTextStream::setCodec(const char *codecName) +{ + QTextCodec *codec = QTextCodec::codecForName(codecName); + if (codec) + setCodec(codec); +} + +/*! + Returns the codec that is current assigned to the stream. + + \sa setCodec(), setAutoDetectUnicode(), locale() +*/ +QTextCodec *QTextStream::codec() const +{ + Q_D(const QTextStream); + return d->codec; +} + +/*! + If \a enabled is true, QTextStream will attempt to detect Unicode + encoding by peeking into the stream data to see if it can find the + UTF-16 or UTF-32 BOM (Byte Order Mark). If this mark is found, QTextStream + will replace the current codec with the UTF codec. + + This function can be used together with setCodec(). It is common + to set the codec to UTF-8, and then enable UTF-16 detection. + + \sa autoDetectUnicode(), setCodec() +*/ +void QTextStream::setAutoDetectUnicode(bool enabled) +{ + Q_D(QTextStream); + d->autoDetectUnicode = enabled; +} + +/*! + Returns \c true if automatic Unicode detection is enabled, otherwise + returns \c false. Automatic Unicode detection is enabled by default. + + \sa setAutoDetectUnicode(), setCodec() +*/ +bool QTextStream::autoDetectUnicode() const +{ + Q_D(const QTextStream); + return d->autoDetectUnicode; +} + +/*! + If \a generate is true and a UTF codec is used, QTextStream will insert + the BOM (Byte Order Mark) before any data has been written to the + device. If \a generate is false, no BOM will be inserted. This function + must be called before any data is written. Otherwise, it does nothing. + + \sa generateByteOrderMark(), bom() +*/ +void QTextStream::setGenerateByteOrderMark(bool generate) +{ + Q_D(QTextStream); + if (d->writeBuffer.isEmpty()) { + d->writeConverterState.flags.setFlag(QTextCodec::IgnoreHeader, !generate); + } +} + +/*! + Returns \c true if QTextStream is set to generate the UTF BOM (Byte Order + Mark) when using a UTF codec; otherwise returns \c false. UTF BOM generation is + set to false by default. + + \sa setGenerateByteOrderMark() +*/ +bool QTextStream::generateByteOrderMark() const +{ + Q_D(const QTextStream); + return (d->writeConverterState.flags & QTextCodec::IgnoreHeader) == 0; +} + +#endif + +/*! + \since 4.5 + + Sets the locale for this stream to \a locale. The specified locale is + used for conversions between numbers and their string representations. + + The default locale is C and it is a special case - the thousands + group separator is not used for backward compatibility reasons. + + \sa locale() +*/ +void QTextStream::setLocale(const QLocale &locale) +{ + Q_D(QTextStream); + d->locale = locale; +} + +/*! + \since 4.5 + + Returns the locale for this stream. The default locale is C. + + \sa setLocale() +*/ +QLocale QTextStream::locale() const +{ + Q_D(const QTextStream); + return d->locale; +} + +QT_END_NAMESPACE + +#ifndef QT_NO_QOBJECT +#include "moc_qtextstream_p.cpp" +#endif diff --git a/src/corelib/serialization/qtextstream.h b/src/corelib/serialization/qtextstream.h new file mode 100644 index 0000000000..ee0b09419d --- /dev/null +++ b/src/corelib/serialization/qtextstream.h @@ -0,0 +1,287 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTEXTSTREAM_H +#define QTEXTSTREAM_H + +#include +#include +#include +#include +#include + +#include + +#ifdef Status +#error qtextstream.h must be included before any header file that defines Status +#endif + +QT_BEGIN_NAMESPACE + + +class QTextCodec; +class QTextDecoder; + +class QTextStreamPrivate; +class Q_CORE_EXPORT QTextStream // text stream class +{ + Q_DECLARE_PRIVATE(QTextStream) + +public: + enum RealNumberNotation { + SmartNotation, + FixedNotation, + ScientificNotation + }; + enum FieldAlignment { + AlignLeft, + AlignRight, + AlignCenter, + AlignAccountingStyle + }; + enum Status { + Ok, + ReadPastEnd, + ReadCorruptData, + WriteFailed + }; + enum NumberFlag { + ShowBase = 0x1, + ForcePoint = 0x2, + ForceSign = 0x4, + UppercaseBase = 0x8, + UppercaseDigits = 0x10 + }; + Q_DECLARE_FLAGS(NumberFlags, NumberFlag) + + QTextStream(); + explicit QTextStream(QIODevice *device); + explicit QTextStream(FILE *fileHandle, QIODevice::OpenMode openMode = QIODevice::ReadWrite); + explicit QTextStream(QString *string, QIODevice::OpenMode openMode = QIODevice::ReadWrite); + explicit QTextStream(QByteArray *array, QIODevice::OpenMode openMode = QIODevice::ReadWrite); + explicit QTextStream(const QByteArray &array, QIODevice::OpenMode openMode = QIODevice::ReadOnly); + virtual ~QTextStream(); + +#ifndef QT_NO_TEXTCODEC + void setCodec(QTextCodec *codec); + void setCodec(const char *codecName); + QTextCodec *codec() const; + void setAutoDetectUnicode(bool enabled); + bool autoDetectUnicode() const; + void setGenerateByteOrderMark(bool generate); + bool generateByteOrderMark() const; +#endif + + void setLocale(const QLocale &locale); + QLocale locale() const; + + void setDevice(QIODevice *device); + QIODevice *device() const; + + void setString(QString *string, QIODevice::OpenMode openMode = QIODevice::ReadWrite); + QString *string() const; + + Status status() const; + void setStatus(Status status); + void resetStatus(); + + bool atEnd() const; + void reset(); + void flush(); + bool seek(qint64 pos); + qint64 pos() const; + + void skipWhiteSpace(); + + QString readLine(qint64 maxlen = 0); + bool readLineInto(QString *line, qint64 maxlen = 0); + QString readAll(); + QString read(qint64 maxlen); + + void setFieldAlignment(FieldAlignment alignment); + FieldAlignment fieldAlignment() const; + + void setPadChar(QChar ch); + QChar padChar() const; + + void setFieldWidth(int width); + int fieldWidth() const; + + void setNumberFlags(NumberFlags flags); + NumberFlags numberFlags() const; + + void setIntegerBase(int base); + int integerBase() const; + + void setRealNumberNotation(RealNumberNotation notation); + RealNumberNotation realNumberNotation() const; + + void setRealNumberPrecision(int precision); + int realNumberPrecision() const; + + QTextStream &operator>>(QChar &ch); + QTextStream &operator>>(char &ch); + QTextStream &operator>>(signed short &i); + QTextStream &operator>>(unsigned short &i); + QTextStream &operator>>(signed int &i); + QTextStream &operator>>(unsigned int &i); + QTextStream &operator>>(signed long &i); + QTextStream &operator>>(unsigned long &i); + QTextStream &operator>>(qlonglong &i); + QTextStream &operator>>(qulonglong &i); + QTextStream &operator>>(float &f); + QTextStream &operator>>(double &f); + QTextStream &operator>>(QString &s); + QTextStream &operator>>(QByteArray &array); + QTextStream &operator>>(char *c); + + QTextStream &operator<<(QChar ch); + QTextStream &operator<<(char ch); + QTextStream &operator<<(signed short i); + QTextStream &operator<<(unsigned short i); + QTextStream &operator<<(signed int i); + QTextStream &operator<<(unsigned int i); + QTextStream &operator<<(signed long i); + QTextStream &operator<<(unsigned long i); + QTextStream &operator<<(qlonglong i); + QTextStream &operator<<(qulonglong i); + QTextStream &operator<<(float f); + QTextStream &operator<<(double f); + QTextStream &operator<<(const QString &s); + QTextStream &operator<<(QLatin1String s); + QTextStream &operator<<(const QStringRef &s); + QTextStream &operator<<(const QByteArray &array); + QTextStream &operator<<(const char *c); + QTextStream &operator<<(const void *ptr); + +private: + Q_DISABLE_COPY(QTextStream) + friend class QDebugStateSaverPrivate; + friend class QDebug; + + QScopedPointer d_ptr; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QTextStream::NumberFlags) + +/***************************************************************************** + QTextStream manipulators + *****************************************************************************/ + +typedef QTextStream & (*QTextStreamFunction)(QTextStream &);// manipulator function +typedef void (QTextStream::*QTSMFI)(int); // manipulator w/int argument +typedef void (QTextStream::*QTSMFC)(QChar); // manipulator w/QChar argument + + +class Q_CORE_EXPORT QTextStreamManipulator +{ +public: + Q_DECL_CONSTEXPR QTextStreamManipulator(QTSMFI m, int a) Q_DECL_NOTHROW : mf(m), mc(nullptr), arg(a), ch() {} + Q_DECL_CONSTEXPR QTextStreamManipulator(QTSMFC m, QChar c) Q_DECL_NOTHROW : mf(nullptr), mc(m), arg(-1), ch(c) {} + void exec(QTextStream &s) { if (mf) { (s.*mf)(arg); } else { (s.*mc)(ch); } } + +private: + QTSMFI mf; // QTextStream member function + QTSMFC mc; // QTextStream member function + int arg; // member function argument + QChar ch; +}; + +inline QTextStream &operator>>(QTextStream &s, QTextStreamFunction f) +{ return (*f)(s); } + +inline QTextStream &operator<<(QTextStream &s, QTextStreamFunction f) +{ return (*f)(s); } + +inline QTextStream &operator<<(QTextStream &s, QTextStreamManipulator m) +{ m.exec(s); return s; } + +Q_CORE_EXPORT QTextStream &bin(QTextStream &s); +Q_CORE_EXPORT QTextStream &oct(QTextStream &s); +Q_CORE_EXPORT QTextStream &dec(QTextStream &s); +Q_CORE_EXPORT QTextStream &hex(QTextStream &s); + +Q_CORE_EXPORT QTextStream &showbase(QTextStream &s); +Q_CORE_EXPORT QTextStream &forcesign(QTextStream &s); +Q_CORE_EXPORT QTextStream &forcepoint(QTextStream &s); +Q_CORE_EXPORT QTextStream &noshowbase(QTextStream &s); +Q_CORE_EXPORT QTextStream &noforcesign(QTextStream &s); +Q_CORE_EXPORT QTextStream &noforcepoint(QTextStream &s); + +Q_CORE_EXPORT QTextStream &uppercasebase(QTextStream &s); +Q_CORE_EXPORT QTextStream &uppercasedigits(QTextStream &s); +Q_CORE_EXPORT QTextStream &lowercasebase(QTextStream &s); +Q_CORE_EXPORT QTextStream &lowercasedigits(QTextStream &s); + +Q_CORE_EXPORT QTextStream &fixed(QTextStream &s); +Q_CORE_EXPORT QTextStream &scientific(QTextStream &s); + +Q_CORE_EXPORT QTextStream &left(QTextStream &s); +Q_CORE_EXPORT QTextStream &right(QTextStream &s); +Q_CORE_EXPORT QTextStream ¢er(QTextStream &s); + +Q_CORE_EXPORT QTextStream &endl(QTextStream &s); +Q_CORE_EXPORT QTextStream &flush(QTextStream &s); +Q_CORE_EXPORT QTextStream &reset(QTextStream &s); + +Q_CORE_EXPORT QTextStream &bom(QTextStream &s); + +Q_CORE_EXPORT QTextStream &ws(QTextStream &s); + +inline QTextStreamManipulator qSetFieldWidth(int width) +{ + QTSMFI func = &QTextStream::setFieldWidth; + return QTextStreamManipulator(func,width); +} + +inline QTextStreamManipulator qSetPadChar(QChar ch) +{ + QTSMFC func = &QTextStream::setPadChar; + return QTextStreamManipulator(func, ch); +} + +inline QTextStreamManipulator qSetRealNumberPrecision(int precision) +{ + QTSMFI func = &QTextStream::setRealNumberPrecision; + return QTextStreamManipulator(func, precision); +} + +QT_END_NAMESPACE + +#endif // QTEXTSTREAM_H diff --git a/src/corelib/serialization/qtextstream_p.h b/src/corelib/serialization/qtextstream_p.h new file mode 100644 index 0000000000..a642beddc4 --- /dev/null +++ b/src/corelib/serialization/qtextstream_p.h @@ -0,0 +1,200 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2016 Intel Corporation. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTEXTSTREAM_P_H +#define QTEXTSTREAM_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include "qtextstream.h" +#ifndef QT_NO_TEXTCODEC +#include "qtextcodec.h" +#endif + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_QOBJECT +class QDeviceClosedNotifier : public QObject +{ + Q_OBJECT +public: + inline QDeviceClosedNotifier() + { } + + inline void setupDevice(QTextStream *stream, QIODevice *device) + { + disconnect(); + if (device) + connect(device, SIGNAL(aboutToClose()), this, SLOT(flushStream())); + this->stream = stream; + } + +public Q_SLOTS: + inline void flushStream() { stream->flush(); } + +private: + QTextStream *stream; +}; +#endif + +class QTextStreamPrivate +{ + Q_DECLARE_PUBLIC(QTextStream) +public: + // streaming parameters + class Params + { + public: + void reset(); + + int realNumberPrecision; + int integerBase; + int fieldWidth; + QChar padChar; + QTextStream::FieldAlignment fieldAlignment; + QTextStream::RealNumberNotation realNumberNotation; + QTextStream::NumberFlags numberFlags; + }; + + QTextStreamPrivate(QTextStream *q_ptr); + ~QTextStreamPrivate(); + void reset(); + + // device + QIODevice *device; +#ifndef QT_NO_QOBJECT + QDeviceClosedNotifier deviceClosedNotifier; +#endif + + // string + QString *string; + int stringOffset; + QIODevice::OpenMode stringOpenMode; + +#ifndef QT_NO_TEXTCODEC + // codec + QTextCodec *codec; + QTextCodec::ConverterState readConverterState; + QTextCodec::ConverterState writeConverterState; + QTextCodec::ConverterState *readConverterSavedState; +#endif + + QString writeBuffer; + QString readBuffer; + int readBufferOffset; + int readConverterSavedStateOffset; //the offset between readBufferStartDevicePos and that start of the buffer + qint64 readBufferStartDevicePos; + + Params params; + + // status + QTextStream::Status status; + QLocale locale; + QTextStream *q_ptr; + + int lastTokenSize; + bool deleteDevice; +#ifndef QT_NO_TEXTCODEC + bool autoDetectUnicode; +#endif + + // i/o + enum TokenDelimiter { + Space, + NotSpace, + EndOfLine + }; + + QString read(int maxlen); + bool scan(const QChar **ptr, int *tokenLength, + int maxlen, TokenDelimiter delimiter); + inline const QChar *readPtr() const; + inline void consumeLastToken(); + inline void consume(int nchars); + void saveConverterState(qint64 newPos); + void restoreToSavedConverterState(); + + // Return value type for getNumber() + enum NumberParsingStatus { + npsOk, + npsMissingDigit, + npsInvalidPrefix + }; + + inline bool getChar(QChar *ch); + inline void ungetChar(QChar ch); + NumberParsingStatus getNumber(qulonglong *l); + bool getReal(double *f); + + inline void write(const QString &data) { write(data.begin(), data.length()); } + inline void write(QChar ch); + void write(const QChar *data, int len); + void write(QLatin1String data); + void writePadding(int len); + inline void putString(const QString &ch, bool number = false) { putString(ch.constData(), ch.length(), number); } + void putString(const QChar *data, int len, bool number = false); + void putString(QLatin1String data, bool number = false); + inline void putChar(QChar ch); + void putNumber(qulonglong number, bool negative); + + struct PaddingResult { + int left, right; + }; + PaddingResult padding(int len) const; + + // buffers + bool fillReadBuffer(qint64 maxBytes = -1); + void resetReadBuffer(); + void flushWriteBuffer(); +}; + +QT_END_NAMESPACE + +#endif // QTEXTSTREAM_P_H diff --git a/src/corelib/serialization/qxmlstream.cpp b/src/corelib/serialization/qxmlstream.cpp new file mode 100644 index 0000000000..9b5295a17e --- /dev/null +++ b/src/corelib/serialization/qxmlstream.cpp @@ -0,0 +1,4014 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "QtCore/qxmlstream.h" + +#ifndef QT_NO_XMLSTREAM + +#include "qxmlutils_p.h" +#include +#include +#include +#include +#include +#include +#ifndef QT_BOOTSTRAPPED +#include +#else +// This specialization of Q_DECLARE_TR_FUNCTIONS is not in qcoreapplication.h, +// because that header depends on QObject being available, which is not the +// case for most bootstrapped applications. +#define Q_DECLARE_TR_FUNCTIONS(context) \ +public: \ + static inline QString tr(const char *sourceText, const char *comment = 0) \ + { Q_UNUSED(comment); return QString::fromLatin1(sourceText); } \ + static inline QString trUtf8(const char *sourceText, const char *comment = 0) \ + { Q_UNUSED(comment); return QString::fromLatin1(sourceText); } \ + static inline QString tr(const char *sourceText, const char*, int) \ + { return QString::fromLatin1(sourceText); } \ + static inline QString trUtf8(const char *sourceText, const char*, int) \ + { return QString::fromLatin1(sourceText); } \ +private: +#endif +QT_BEGIN_NAMESPACE + +#include "qxmlstream_p.h" + +enum { StreamEOF = ~0U }; + +/*! + \enum QXmlStreamReader::TokenType + + This enum specifies the type of token the reader just read. + + \value NoToken The reader has not yet read anything. + + \value Invalid An error has occurred, reported in error() and + errorString(). + + \value StartDocument The reader reports the XML version number in + documentVersion(), and the encoding as specified in the XML + document in documentEncoding(). If the document is declared + standalone, isStandaloneDocument() returns \c true; otherwise it + returns \c false. + + \value EndDocument The reader reports the end of the document. + + \value StartElement The reader reports the start of an element + with namespaceUri() and name(). Empty elements are also reported + as StartElement, followed directly by EndElement. The convenience + function readElementText() can be called to concatenate all + content until the corresponding EndElement. Attributes are + reported in attributes(), namespace declarations in + namespaceDeclarations(). + + \value EndElement The reader reports the end of an element with + namespaceUri() and name(). + + \value Characters The reader reports characters in text(). If the + characters are all white-space, isWhitespace() returns \c true. If + the characters stem from a CDATA section, isCDATA() returns \c true. + + \value Comment The reader reports a comment in text(). + + \value DTD The reader reports a DTD in text(), notation + declarations in notationDeclarations(), and entity declarations in + entityDeclarations(). Details of the DTD declaration are reported + in in dtdName(), dtdPublicId(), and dtdSystemId(). + + \value EntityReference The reader reports an entity reference that + could not be resolved. The name of the reference is reported in + name(), the replacement text in text(). + + \value ProcessingInstruction The reader reports a processing + instruction in processingInstructionTarget() and + processingInstructionData(). +*/ + +/*! + \enum QXmlStreamReader::ReadElementTextBehaviour + + This enum specifies the different behaviours of readElementText(). + + \value ErrorOnUnexpectedElement Raise an UnexpectedElementError and return + what was read so far when a child element is encountered. + + \value IncludeChildElements Recursively include the text from child elements. + + \value SkipChildElements Skip child elements. + + \since 4.6 +*/ + +/*! + \enum QXmlStreamReader::Error + + This enum specifies different error cases + + \value NoError No error has occurred. + + \value CustomError A custom error has been raised with + raiseError() + + \value NotWellFormedError The parser internally raised an error + due to the read XML not being well-formed. + + \value PrematureEndOfDocumentError The input stream ended before a + well-formed XML document was parsed. Recovery from this error is + possible if more XML arrives in the stream, either by calling + addData() or by waiting for it to arrive on the device(). + + \value UnexpectedElementError The parser encountered an element + that was different to those it expected. + +*/ + +/*! + \class QXmlStreamEntityResolver + \inmodule QtCore + \reentrant + \since 4.4 + + \brief The QXmlStreamEntityResolver class provides an entity + resolver for a QXmlStreamReader. + + \ingroup xml-tools + */ + +/*! + Destroys the entity resolver. + */ +QXmlStreamEntityResolver::~QXmlStreamEntityResolver() +{ +} + +/*! + \internal + +This function is a stub for later functionality. +*/ +QString QXmlStreamEntityResolver::resolveEntity(const QString& /*publicId*/, const QString& /*systemId*/) +{ + return QString(); +} + + +/*! + Resolves the undeclared entity \a name and returns its replacement + text. If the entity is also unknown to the entity resolver, it + returns an empty string. + + The default implementation always returns an empty string. +*/ + +QString QXmlStreamEntityResolver::resolveUndeclaredEntity(const QString &/*name*/) +{ + return QString(); +} + +#ifndef QT_NO_XMLSTREAMREADER + +QString QXmlStreamReaderPrivate::resolveUndeclaredEntity(const QString &name) +{ + if (entityResolver) + return entityResolver->resolveUndeclaredEntity(name); + return QString(); +} + + + +/*! + \since 4.4 + + Makes \a resolver the new entityResolver(). + + The stream reader does \e not take ownership of the resolver. It's + the callers responsibility to ensure that the resolver is valid + during the entire life-time of the stream reader object, or until + another resolver or 0 is set. + + \sa entityResolver() + */ +void QXmlStreamReader::setEntityResolver(QXmlStreamEntityResolver *resolver) +{ + Q_D(QXmlStreamReader); + d->entityResolver = resolver; +} + +/*! + \since 4.4 + + Returns the entity resolver, or 0 if there is no entity resolver. + + \sa setEntityResolver() + */ +QXmlStreamEntityResolver *QXmlStreamReader::entityResolver() const +{ + Q_D(const QXmlStreamReader); + return d->entityResolver; +} + + + +/*! + \class QXmlStreamReader + \inmodule QtCore + \reentrant + \since 4.3 + + \brief The QXmlStreamReader class provides a fast parser for reading + well-formed XML via a simple streaming API. + + + \ingroup xml-tools + + QXmlStreamReader is a faster and more convenient replacement for + Qt's own SAX parser (see QXmlSimpleReader). In some cases it might + also be a faster and more convenient alternative for use in + applications that would otherwise use a DOM tree (see QDomDocument). + QXmlStreamReader reads data either from a QIODevice (see + setDevice()), or from a raw QByteArray (see addData()). + + Qt provides QXmlStreamWriter for writing XML. + + The basic concept of a stream reader is to report an XML document as + a stream of tokens, similar to SAX. The main difference between + QXmlStreamReader and SAX is \e how these XML tokens are reported. + With SAX, the application must provide handlers (callback functions) + that receive so-called XML \e events from the parser at the parser's + convenience. With QXmlStreamReader, the application code itself + drives the loop and pulls \e tokens from the reader, one after + another, as it needs them. This is done by calling readNext(), where + the reader reads from the input stream until it completes the next + token, at which point it returns the tokenType(). A set of + convenient functions including isStartElement() and text() can then + be used to examine the token to obtain information about what has + been read. The big advantage of this \e pulling approach is the + possibility to build recursive descent parsers with it, meaning you + can split your XML parsing code easily into different methods or + classes. This makes it easy to keep track of the application's own + state when parsing XML. + + A typical loop with QXmlStreamReader looks like this: + + \snippet code/src_corelib_xml_qxmlstream.cpp 0 + + + QXmlStreamReader is a well-formed XML 1.0 parser that does \e not + include external parsed entities. As long as no error occurs, the + application code can thus be assured that the data provided by the + stream reader satisfies the W3C's criteria for well-formed XML. For + example, you can be certain that all tags are indeed nested and + closed properly, that references to internal entities have been + replaced with the correct replacement text, and that attributes have + been normalized or added according to the internal subset of the + DTD. + + If an error occurs while parsing, atEnd() and hasError() return + true, and error() returns the error that occurred. The functions + errorString(), lineNumber(), columnNumber(), and characterOffset() + are for constructing an appropriate error or warning message. To + simplify application code, QXmlStreamReader contains a raiseError() + mechanism that lets you raise custom errors that trigger the same + error handling described. + + The \l{QXmlStream Bookmarks Example} illustrates how to use the + recursive descent technique to read an XML bookmark file (XBEL) with + a stream reader. + + \section1 Namespaces + + QXmlStream understands and resolves XML namespaces. E.g. in case of + a StartElement, namespaceUri() returns the namespace the element is + in, and name() returns the element's \e local name. The combination + of namespaceUri and name uniquely identifies an element. If a + namespace prefix was not declared in the XML entities parsed by the + reader, the namespaceUri is empty. + + If you parse XML data that does not utilize namespaces according to + the XML specification or doesn't use namespaces at all, you can use + the element's qualifiedName() instead. A qualified name is the + element's prefix() followed by colon followed by the element's local + name() - exactly like the element appears in the raw XML data. Since + the mapping namespaceUri to prefix is neither unique nor universal, + qualifiedName() should be avoided for namespace-compliant XML data. + + In order to parse standalone documents that do use undeclared + namespace prefixes, you can turn off namespace processing completely + with the \l namespaceProcessing property. + + \section1 Incremental Parsing + + QXmlStreamReader is an incremental parser. It can handle the case + where the document can't be parsed all at once because it arrives in + chunks (e.g. from multiple files, or over a network connection). + When the reader runs out of data before the complete document has + been parsed, it reports a PrematureEndOfDocumentError. When more + data arrives, either because of a call to addData() or because more + data is available through the network device(), the reader recovers + from the PrematureEndOfDocumentError error and continues parsing the + new data with the next call to readNext(). + + For example, if your application reads data from the network using a + \l{QNetworkAccessManager} {network access manager}, you would issue + a \l{QNetworkRequest} {network request} to the manager and receive a + \l{QNetworkReply} {network reply} in return. Since a QNetworkReply + is a QIODevice, you connect its \l{QIODevice::readyRead()} + {readyRead()} signal to a custom slot, e.g. \c{slotReadyRead()} in + the code snippet shown in the discussion for QNetworkAccessManager. + In this slot, you read all available data with + \l{QIODevice::readAll()} {readAll()} and pass it to the XML + stream reader using addData(). Then you call your custom parsing + function that reads the XML events from the reader. + + \section1 Performance and Memory Consumption + + QXmlStreamReader is memory-conservative by design, since it doesn't + store the entire XML document tree in memory, but only the current + token at the time it is reported. In addition, QXmlStreamReader + avoids the many small string allocations that it normally takes to + map an XML document to a convenient and Qt-ish API. It does this by + reporting all string data as QStringRef rather than real QString + objects. QStringRef is a thin wrapper around QString substrings that + provides a subset of the QString API without the memory allocation + and reference-counting overhead. Calling + \l{QStringRef::toString()}{toString()} on any of those objects + returns an equivalent real QString object. + +*/ + + +/*! + Constructs a stream reader. + + \sa setDevice(), addData() + */ +QXmlStreamReader::QXmlStreamReader() + : d_ptr(new QXmlStreamReaderPrivate(this)) +{ +} + +/*! Creates a new stream reader that reads from \a device. + +\sa setDevice(), clear() + */ +QXmlStreamReader::QXmlStreamReader(QIODevice *device) + : d_ptr(new QXmlStreamReaderPrivate(this)) +{ + setDevice(device); +} + +/*! + Creates a new stream reader that reads from \a data. + + \sa addData(), clear(), setDevice() + */ +QXmlStreamReader::QXmlStreamReader(const QByteArray &data) + : d_ptr(new QXmlStreamReaderPrivate(this)) +{ + Q_D(QXmlStreamReader); + d->dataBuffer = data; +} + +/*! + Creates a new stream reader that reads from \a data. + + \sa addData(), clear(), setDevice() + */ +QXmlStreamReader::QXmlStreamReader(const QString &data) + : d_ptr(new QXmlStreamReaderPrivate(this)) +{ + Q_D(QXmlStreamReader); +#ifdef QT_NO_TEXTCODEC + d->dataBuffer = data.toLatin1(); +#else + d->dataBuffer = d->codec->fromUnicode(data); + d->decoder = d->codec->makeDecoder(); +#endif + d->lockEncoding = true; + +} + +/*! + Creates a new stream reader that reads from \a data. + + \sa addData(), clear(), setDevice() + */ +QXmlStreamReader::QXmlStreamReader(const char *data) + : d_ptr(new QXmlStreamReaderPrivate(this)) +{ + Q_D(QXmlStreamReader); + d->dataBuffer = QByteArray(data); +} + +/*! + Destructs the reader. + */ +QXmlStreamReader::~QXmlStreamReader() +{ + Q_D(QXmlStreamReader); + if (d->deleteDevice) + delete d->device; +} + +/*! \fn bool QXmlStreamReader::hasError() const + Returns \c true if an error has occurred, otherwise \c false. + + \sa errorString(), error() + */ + +/*! + Sets the current device to \a device. Setting the device resets + the stream to its initial state. + + \sa device(), clear() +*/ +void QXmlStreamReader::setDevice(QIODevice *device) +{ + Q_D(QXmlStreamReader); + if (d->deleteDevice) { + delete d->device; + d->deleteDevice = false; + } + d->device = device; + d->init(); + +} + +/*! + Returns the current device associated with the QXmlStreamReader, + or 0 if no device has been assigned. + + \sa setDevice() +*/ +QIODevice *QXmlStreamReader::device() const +{ + Q_D(const QXmlStreamReader); + return d->device; +} + + +/*! + Adds more \a data for the reader to read. This function does + nothing if the reader has a device(). + + \sa readNext(), clear() + */ +void QXmlStreamReader::addData(const QByteArray &data) +{ + Q_D(QXmlStreamReader); + if (d->device) { + qWarning("QXmlStreamReader: addData() with device()"); + return; + } + d->dataBuffer += data; +} + +/*! + Adds more \a data for the reader to read. This function does + nothing if the reader has a device(). + + \sa readNext(), clear() + */ +void QXmlStreamReader::addData(const QString &data) +{ + Q_D(QXmlStreamReader); + d->lockEncoding = true; +#ifdef QT_NO_TEXTCODEC + addData(data.toLatin1()); +#else + addData(d->codec->fromUnicode(data)); +#endif +} + +/*! + Adds more \a data for the reader to read. This function does + nothing if the reader has a device(). + + \sa readNext(), clear() + */ +void QXmlStreamReader::addData(const char *data) +{ + addData(QByteArray(data)); +} + +/*! + Removes any device() or data from the reader and resets its + internal state to the initial state. + + \sa addData() + */ +void QXmlStreamReader::clear() +{ + Q_D(QXmlStreamReader); + d->init(); + if (d->device) { + if (d->deleteDevice) + delete d->device; + d->device = 0; + } +} + +/*! + Returns \c true if the reader has read until the end of the XML + document, or if an error() has occurred and reading has been + aborted. Otherwise, it returns \c false. + + When atEnd() and hasError() return true and error() returns + PrematureEndOfDocumentError, it means the XML has been well-formed + so far, but a complete XML document has not been parsed. The next + chunk of XML can be added with addData(), if the XML is being read + from a QByteArray, or by waiting for more data to arrive if the + XML is being read from a QIODevice. Either way, atEnd() will + return false once more data is available. + + \sa hasError(), error(), device(), QIODevice::atEnd() + */ +bool QXmlStreamReader::atEnd() const +{ + Q_D(const QXmlStreamReader); + if (d->atEnd + && ((d->type == QXmlStreamReader::Invalid && d->error == PrematureEndOfDocumentError) + || (d->type == QXmlStreamReader::EndDocument))) { + if (d->device) + return d->device->atEnd(); + else + return !d->dataBuffer.size(); + } + return (d->atEnd || d->type == QXmlStreamReader::Invalid); +} + + +/*! + Reads the next token and returns its type. + + With one exception, once an error() is reported by readNext(), + further reading of the XML stream is not possible. Then atEnd() + returns \c true, hasError() returns \c true, and this function returns + QXmlStreamReader::Invalid. + + The exception is when error() returns PrematureEndOfDocumentError. + This error is reported when the end of an otherwise well-formed + chunk of XML is reached, but the chunk doesn't represent a complete + XML document. In that case, parsing \e can be resumed by calling + addData() to add the next chunk of XML, when the stream is being + read from a QByteArray, or by waiting for more data to arrive when + the stream is being read from a device(). + + \sa tokenType(), tokenString() + */ +QXmlStreamReader::TokenType QXmlStreamReader::readNext() +{ + Q_D(QXmlStreamReader); + if (d->type != Invalid) { + if (!d->hasCheckedStartDocument) + if (!d->checkStartDocument()) + return d->type; // synthetic StartDocument or error + d->parse(); + if (d->atEnd && d->type != EndDocument && d->type != Invalid) + d->raiseError(PrematureEndOfDocumentError); + else if (!d->atEnd && d->type == EndDocument) + d->raiseWellFormedError(QXmlStream::tr("Extra content at end of document.")); + } else if (d->error == PrematureEndOfDocumentError) { + // resume error + d->type = NoToken; + d->atEnd = false; + d->token = -1; + return readNext(); + } + return d->type; +} + + +/*! + Returns the type of the current token. + + The current token can also be queried with the convenience functions + isStartDocument(), isEndDocument(), isStartElement(), + isEndElement(), isCharacters(), isComment(), isDTD(), + isEntityReference(), and isProcessingInstruction(). + + \sa tokenString() + */ +QXmlStreamReader::TokenType QXmlStreamReader::tokenType() const +{ + Q_D(const QXmlStreamReader); + return d->type; +} + +/*! + Reads until the next start element within the current element. Returns \c true + when a start element was reached. When the end element was reached, or when + an error occurred, false is returned. + + The current element is the element matching the most recently parsed start + element of which a matching end element has not yet been reached. When the + parser has reached the end element, the current element becomes the parent + element. + + This is a convenience function for when you're only concerned with parsing + XML elements. The \l{QXmlStream Bookmarks Example} makes extensive use of + this function. + + \since 4.6 + \sa readNext() + */ +bool QXmlStreamReader::readNextStartElement() +{ + while (readNext() != Invalid) { + if (isEndElement()) + return false; + else if (isStartElement()) + return true; + } + return false; +} + +/*! + Reads until the end of the current element, skipping any child nodes. + This function is useful for skipping unknown elements. + + The current element is the element matching the most recently parsed start + element of which a matching end element has not yet been reached. When the + parser has reached the end element, the current element becomes the parent + element. + + \since 4.6 + */ +void QXmlStreamReader::skipCurrentElement() +{ + int depth = 1; + while (depth && readNext() != Invalid) { + if (isEndElement()) + --depth; + else if (isStartElement()) + ++depth; + } +} + +/* + * Use the following Perl script to generate the error string index list: +===== PERL SCRIPT ==== +print "static const char QXmlStreamReader_tokenTypeString_string[] =\n"; +$counter = 0; +$i = 0; +while () { + chomp; + print " \"$_\\0\"\n"; + $sizes[$i++] = $counter; + $counter += length 1 + $_; +} +print " \"\\0\";\n\nstatic const short QXmlStreamReader_tokenTypeString_indices[] = {\n "; +for ($j = 0; $j < $i; ++$j) { + printf "$sizes[$j], "; +} +print "0\n};\n"; +===== PERL SCRIPT ==== + + * The input data is as follows (copied from qxmlstream.h): +NoToken +Invalid +StartDocument +EndDocument +StartElement +EndElement +Characters +Comment +DTD +EntityReference +ProcessingInstruction +*/ +static const char QXmlStreamReader_tokenTypeString_string[] = + "NoToken\0" + "Invalid\0" + "StartDocument\0" + "EndDocument\0" + "StartElement\0" + "EndElement\0" + "Characters\0" + "Comment\0" + "DTD\0" + "EntityReference\0" + "ProcessingInstruction\0"; + +static const short QXmlStreamReader_tokenTypeString_indices[] = { + 0, 8, 16, 30, 42, 55, 66, 77, 85, 89, 105, 0 +}; + + +/*! + \property QXmlStreamReader::namespaceProcessing + The namespace-processing flag of the stream reader + + This property controls whether or not the stream reader processes + namespaces. If enabled, the reader processes namespaces, otherwise + it does not. + + By default, namespace-processing is enabled. +*/ + + +void QXmlStreamReader::setNamespaceProcessing(bool enable) +{ + Q_D(QXmlStreamReader); + d->namespaceProcessing = enable; +} + +bool QXmlStreamReader::namespaceProcessing() const +{ + Q_D(const QXmlStreamReader); + return d->namespaceProcessing; +} + +/*! Returns the reader's current token as string. + +\sa tokenType() +*/ +QString QXmlStreamReader::tokenString() const +{ + Q_D(const QXmlStreamReader); + return QLatin1String(QXmlStreamReader_tokenTypeString_string + + QXmlStreamReader_tokenTypeString_indices[d->type]); +} + +#endif // QT_NO_XMLSTREAMREADER + +QXmlStreamPrivateTagStack::QXmlStreamPrivateTagStack() +{ + tagStack.reserve(16); + tagStackStringStorage.reserve(32); + tagStackStringStorageSize = 0; + NamespaceDeclaration &namespaceDeclaration = namespaceDeclarations.push(); + namespaceDeclaration.prefix = addToStringStorage(QStringViewLiteral("xml")); + namespaceDeclaration.namespaceUri = addToStringStorage(QStringViewLiteral("http://www.w3.org/XML/1998/namespace")); + initialTagStackStringStorageSize = tagStackStringStorageSize; +} + +#ifndef QT_NO_XMLSTREAMREADER + +QXmlStreamReaderPrivate::QXmlStreamReaderPrivate(QXmlStreamReader *q) + :q_ptr(q) +{ + device = 0; + deleteDevice = false; +#ifndef QT_NO_TEXTCODEC + decoder = 0; +#endif + stack_size = 64; + sym_stack = 0; + state_stack = 0; + reallocateStack(); + entityResolver = 0; + init(); +#define ADD_PREDEFINED(n, v) \ + do { \ + Entity e = Entity::createLiteral(QLatin1String(n), QLatin1String(v)); \ + entityHash.insert(qToStringViewIgnoringNull(e.name), std::move(e)); \ + } while (false) + ADD_PREDEFINED("lt", "<"); + ADD_PREDEFINED("gt", ">"); + ADD_PREDEFINED("amp", "&"); + ADD_PREDEFINED("apos", "'"); + ADD_PREDEFINED("quot", "\""); +#undef ADD_PREDEFINED +} + +void QXmlStreamReaderPrivate::init() +{ + scanDtd = false; + token = -1; + token_char = 0; + isEmptyElement = false; + isWhitespace = true; + isCDATA = false; + standalone = false; + tos = 0; + resumeReduction = 0; + state_stack[tos++] = 0; + state_stack[tos] = 0; + putStack.clear(); + putStack.reserve(32); + textBuffer.clear(); + textBuffer.reserve(256); + tagStack.clear(); + tagsDone = false; + attributes.clear(); + attributes.reserve(16); + lineNumber = lastLineStart = characterOffset = 0; + readBufferPos = 0; + nbytesread = 0; +#ifndef QT_NO_TEXTCODEC + codec = QTextCodec::codecForMib(106); // utf8 + delete decoder; + decoder = 0; +#endif + attributeStack.clear(); + attributeStack.reserve(16); + entityParser = 0; + hasCheckedStartDocument = false; + normalizeLiterals = false; + hasSeenTag = false; + atEnd = false; + inParseEntity = false; + referenceToUnparsedEntityDetected = false; + referenceToParameterEntityDetected = false; + hasExternalDtdSubset = false; + lockEncoding = false; + namespaceProcessing = true; + rawReadBuffer.clear(); + dataBuffer.clear(); + readBuffer.clear(); + tagStackStringStorageSize = initialTagStackStringStorageSize; + + type = QXmlStreamReader::NoToken; + error = QXmlStreamReader::NoError; +} + +/* + Well-formed requires that we verify entity values. We do this with a + standard parser. + */ +void QXmlStreamReaderPrivate::parseEntity(const QString &value) +{ + Q_Q(QXmlStreamReader); + + if (value.isEmpty()) + return; + + + if (!entityParser) + entityParser = new QXmlStreamReaderPrivate(q); + else + entityParser->init(); + entityParser->inParseEntity = true; + entityParser->readBuffer = value; + entityParser->injectToken(PARSE_ENTITY); + while (!entityParser->atEnd && entityParser->type != QXmlStreamReader::Invalid) + entityParser->parse(); + if (entityParser->type == QXmlStreamReader::Invalid || entityParser->tagStack.size()) + raiseWellFormedError(QXmlStream::tr("Invalid entity value.")); + +} + +inline void QXmlStreamReaderPrivate::reallocateStack() +{ + stack_size <<= 1; + sym_stack = reinterpret_cast (realloc(sym_stack, stack_size * sizeof(Value))); + Q_CHECK_PTR(sym_stack); + state_stack = reinterpret_cast (realloc(state_stack, stack_size * sizeof(int))); + Q_CHECK_PTR(state_stack); +} + + +QXmlStreamReaderPrivate::~QXmlStreamReaderPrivate() +{ +#ifndef QT_NO_TEXTCODEC + delete decoder; +#endif + free(sym_stack); + free(state_stack); + delete entityParser; +} + + +inline uint QXmlStreamReaderPrivate::filterCarriageReturn() +{ + uint peekc = peekChar(); + if (peekc == '\n') { + if (putStack.size()) + putStack.pop(); + else + ++readBufferPos; + return peekc; + } + if (peekc == StreamEOF) { + putChar('\r'); + return 0; + } + return '\n'; +} + +/*! + \internal + If the end of the file is encountered, ~0 is returned. + */ +inline uint QXmlStreamReaderPrivate::getChar() +{ + uint c; + if (putStack.size()) { + c = atEnd ? StreamEOF : putStack.pop(); + } else { + if (readBufferPos < readBuffer.size()) + c = readBuffer.at(readBufferPos++).unicode(); + else + c = getChar_helper(); + } + + return c; +} + +inline uint QXmlStreamReaderPrivate::peekChar() +{ + uint c; + if (putStack.size()) { + c = putStack.top(); + } else if (readBufferPos < readBuffer.size()) { + c = readBuffer.at(readBufferPos).unicode(); + } else { + if ((c = getChar_helper()) != StreamEOF) + --readBufferPos; + } + + return c; +} + +/*! + \internal + + Scans characters until \a str is encountered, and validates the characters + as according to the Char[2] production and do the line-ending normalization. + If any character is invalid, false is returned, otherwise true upon success. + + If \a tokenToInject is not less than zero, injectToken() is called with + \a tokenToInject when \a str is found. + + If any error occurred, false is returned, otherwise true. + */ +bool QXmlStreamReaderPrivate::scanUntil(const char *str, short tokenToInject) +{ + int pos = textBuffer.size(); + int oldLineNumber = lineNumber; + + uint c; + while ((c = getChar()) != StreamEOF) { + /* First, we do the validation & normalization. */ + switch (c) { + case '\r': + if ((c = filterCarriageReturn()) == 0) + break; + Q_FALLTHROUGH(); + case '\n': + ++lineNumber; + lastLineStart = characterOffset + readBufferPos; + Q_FALLTHROUGH(); + case '\t': + textBuffer += QChar(c); + continue; + default: + if (c < 0x20 || (c > 0xFFFD && c < 0x10000) || c > QChar::LastValidCodePoint ) { + raiseWellFormedError(QXmlStream::tr("Invalid XML character.")); + lineNumber = oldLineNumber; + return false; + } + textBuffer += QChar(c); + } + + + /* Second, attempt to lookup str. */ + if (c == uint(*str)) { + if (!*(str + 1)) { + if (tokenToInject >= 0) + injectToken(tokenToInject); + return true; + } else { + if (scanString(str + 1, tokenToInject, false)) + return true; + } + } + } + putString(textBuffer, pos); + textBuffer.resize(pos); + lineNumber = oldLineNumber; + return false; +} + +bool QXmlStreamReaderPrivate::scanString(const char *str, short tokenToInject, bool requireSpace) +{ + int n = 0; + while (str[n]) { + uint c = getChar(); + if (c != ushort(str[n])) { + if (c != StreamEOF) + putChar(c); + while (n--) { + putChar(ushort(str[n])); + } + return false; + } + ++n; + } + for (int i = 0; i < n; ++i) + textBuffer += QChar(ushort(str[i])); + if (requireSpace) { + int s = fastScanSpace(); + if (!s || atEnd) { + int pos = textBuffer.size() - n - s; + putString(textBuffer, pos); + textBuffer.resize(pos); + return false; + } + } + if (tokenToInject >= 0) + injectToken(tokenToInject); + return true; +} + +bool QXmlStreamReaderPrivate::scanAfterLangleBang() +{ + switch (peekChar()) { + case '[': + return scanString(spell[CDATA_START], CDATA_START, false); + case 'D': + return scanString(spell[DOCTYPE], DOCTYPE); + case 'A': + return scanString(spell[ATTLIST], ATTLIST); + case 'N': + return scanString(spell[NOTATION], NOTATION); + case 'E': + if (scanString(spell[ELEMENT], ELEMENT)) + return true; + return scanString(spell[ENTITY], ENTITY); + + default: + ; + }; + return false; +} + +bool QXmlStreamReaderPrivate::scanPublicOrSystem() +{ + switch (peekChar()) { + case 'S': + return scanString(spell[SYSTEM], SYSTEM); + case 'P': + return scanString(spell[PUBLIC], PUBLIC); + default: + ; + } + return false; +} + +bool QXmlStreamReaderPrivate::scanNData() +{ + if (fastScanSpace()) { + if (scanString(spell[NDATA], NDATA)) + return true; + putChar(' '); + } + return false; +} + +bool QXmlStreamReaderPrivate::scanAfterDefaultDecl() +{ + switch (peekChar()) { + case 'R': + return scanString(spell[REQUIRED], REQUIRED, false); + case 'I': + return scanString(spell[IMPLIED], IMPLIED, false); + case 'F': + return scanString(spell[FIXED], FIXED, false); + default: + ; + } + return false; +} + +bool QXmlStreamReaderPrivate::scanAttType() +{ + switch (peekChar()) { + case 'C': + return scanString(spell[CDATA], CDATA); + case 'I': + if (scanString(spell[ID], ID)) + return true; + if (scanString(spell[IDREF], IDREF)) + return true; + return scanString(spell[IDREFS], IDREFS); + case 'E': + if (scanString(spell[ENTITY], ENTITY)) + return true; + return scanString(spell[ENTITIES], ENTITIES); + case 'N': + if (scanString(spell[NOTATION], NOTATION)) + return true; + if (scanString(spell[NMTOKEN], NMTOKEN)) + return true; + return scanString(spell[NMTOKENS], NMTOKENS); + default: + ; + } + return false; +} + +/*! + \internal + + Scan strings with quotes or apostrophes surround them. For instance, + attributes, the version and encoding field in the XML prolog and + entity declarations. + + If normalizeLiterals is set to true, the function also normalizes + whitespace. It is set to true when the first start tag is + encountered. + + */ +inline int QXmlStreamReaderPrivate::fastScanLiteralContent() +{ + int n = 0; + uint c; + while ((c = getChar()) != StreamEOF) { + switch (ushort(c)) { + case 0xfffe: + case 0xffff: + case 0: + /* The putChar() call is necessary so the parser re-gets + * the character from the input source, when raising an error. */ + putChar(c); + return n; + case '\r': + if (filterCarriageReturn() == 0) + return n; + Q_FALLTHROUGH(); + case '\n': + ++lineNumber; + lastLineStart = characterOffset + readBufferPos; + Q_FALLTHROUGH(); + case ' ': + case '\t': + if (normalizeLiterals) + textBuffer += QLatin1Char(' '); + else + textBuffer += QChar(c); + ++n; + break; + case '&': + case '<': + case '\"': + case '\'': + if (!(c & 0xff0000)) { + putChar(c); + return n; + } + Q_FALLTHROUGH(); + default: + if (c < 0x20) { + putChar(c); + return n; + } + textBuffer += QChar(c); + ++n; + } + } + return n; +} + +inline int QXmlStreamReaderPrivate::fastScanSpace() +{ + int n = 0; + uint c; + while ((c = getChar()) != StreamEOF) { + switch (c) { + case '\r': + if ((c = filterCarriageReturn()) == 0) + return n; + Q_FALLTHROUGH(); + case '\n': + ++lineNumber; + lastLineStart = characterOffset + readBufferPos; + Q_FALLTHROUGH(); + case ' ': + case '\t': + textBuffer += QChar(c); + ++n; + break; + default: + putChar(c); + return n; + } + } + return n; +} + +/*! + \internal + + Used for text nodes essentially. That is, characters appearing + inside elements. + */ +inline int QXmlStreamReaderPrivate::fastScanContentCharList() +{ + int n = 0; + uint c; + while ((c = getChar()) != StreamEOF) { + switch (ushort(c)) { + case 0xfffe: + case 0xffff: + case 0: + putChar(c); + return n; + case ']': { + isWhitespace = false; + int pos = textBuffer.size(); + textBuffer += QChar(ushort(c)); + ++n; + while ((c = getChar()) == ']') { + textBuffer += QChar(ushort(c)); + ++n; + } + if (c == 0) { + putString(textBuffer, pos); + textBuffer.resize(pos); + } else if (c == '>' && textBuffer.at(textBuffer.size()-2) == QLatin1Char(']')) { + raiseWellFormedError(QXmlStream::tr("Sequence ']]>' not allowed in content.")); + } else { + putChar(c); + break; + } + return n; + } break; + case '\r': + if ((c = filterCarriageReturn()) == 0) + return n; + Q_FALLTHROUGH(); + case '\n': + ++lineNumber; + lastLineStart = characterOffset + readBufferPos; + Q_FALLTHROUGH(); + case ' ': + case '\t': + textBuffer += QChar(ushort(c)); + ++n; + break; + case '&': + case '<': + if (!(c & 0xff0000)) { + putChar(c); + return n; + } + Q_FALLTHROUGH(); + default: + if (c < 0x20) { + putChar(c); + return n; + } + isWhitespace = false; + textBuffer += QChar(ushort(c)); + ++n; + } + } + return n; +} + +inline int QXmlStreamReaderPrivate::fastScanName(int *prefix) +{ + int n = 0; + uint c; + while ((c = getChar()) != StreamEOF) { + switch (c) { + case '\n': + case ' ': + case '\t': + case '\r': + case '&': + case '#': + case '\'': + case '\"': + case '<': + case '>': + case '[': + case ']': + case '=': + case '%': + case '/': + case ';': + case '?': + case '!': + case '^': + case '|': + case ',': + case '(': + case ')': + case '+': + case '*': + putChar(c); + if (prefix && *prefix == n+1) { + *prefix = 0; + putChar(':'); + --n; + } + return n; + case ':': + if (prefix) { + if (*prefix == 0) { + *prefix = n+2; + } else { // only one colon allowed according to the namespace spec. + putChar(c); + return n; + } + } else { + putChar(c); + return n; + } + Q_FALLTHROUGH(); + default: + textBuffer += QChar(c); + ++n; + } + } + + if (prefix) + *prefix = 0; + int pos = textBuffer.size() - n; + putString(textBuffer, pos); + textBuffer.resize(pos); + return 0; +} + +enum NameChar { NameBeginning, NameNotBeginning, NotName }; + +static const char Begi = static_cast(NameBeginning); +static const char NtBg = static_cast(NameNotBeginning); +static const char NotN = static_cast(NotName); + +static const char nameCharTable[128] = +{ +// 0x00 + NotN, NotN, NotN, NotN, NotN, NotN, NotN, NotN, + NotN, NotN, NotN, NotN, NotN, NotN, NotN, NotN, +// 0x10 + NotN, NotN, NotN, NotN, NotN, NotN, NotN, NotN, + NotN, NotN, NotN, NotN, NotN, NotN, NotN, NotN, +// 0x20 (0x2D is '-', 0x2E is '.') + NotN, NotN, NotN, NotN, NotN, NotN, NotN, NotN, + NotN, NotN, NotN, NotN, NotN, NtBg, NtBg, NotN, +// 0x30 (0x30..0x39 are '0'..'9', 0x3A is ':') + NtBg, NtBg, NtBg, NtBg, NtBg, NtBg, NtBg, NtBg, + NtBg, NtBg, Begi, NotN, NotN, NotN, NotN, NotN, +// 0x40 (0x41..0x5A are 'A'..'Z') + NotN, Begi, Begi, Begi, Begi, Begi, Begi, Begi, + Begi, Begi, Begi, Begi, Begi, Begi, Begi, Begi, +// 0x50 (0x5F is '_') + Begi, Begi, Begi, Begi, Begi, Begi, Begi, Begi, + Begi, Begi, Begi, NotN, NotN, NotN, NotN, Begi, +// 0x60 (0x61..0x7A are 'a'..'z') + NotN, Begi, Begi, Begi, Begi, Begi, Begi, Begi, + Begi, Begi, Begi, Begi, Begi, Begi, Begi, Begi, +// 0x70 + Begi, Begi, Begi, Begi, Begi, Begi, Begi, Begi, + Begi, Begi, Begi, NotN, NotN, NotN, NotN, NotN +}; + +static inline NameChar fastDetermineNameChar(QChar ch) +{ + ushort uc = ch.unicode(); + if (!(uc & ~0x7f)) // uc < 128 + return static_cast(nameCharTable[uc]); + + QChar::Category cat = ch.category(); + // ### some these categories might be slightly wrong + if ((cat >= QChar::Letter_Uppercase && cat <= QChar::Letter_Other) + || cat == QChar::Number_Letter) + return NameBeginning; + if ((cat >= QChar::Number_DecimalDigit && cat <= QChar::Number_Other) + || (cat >= QChar::Mark_NonSpacing && cat <= QChar::Mark_Enclosing)) + return NameNotBeginning; + return NotName; +} + +inline int QXmlStreamReaderPrivate::fastScanNMTOKEN() +{ + int n = 0; + uint c; + while ((c = getChar()) != StreamEOF) { + if (fastDetermineNameChar(c) == NotName) { + putChar(c); + return n; + } else { + ++n; + textBuffer += QChar(c); + } + } + + int pos = textBuffer.size() - n; + putString(textBuffer, pos); + textBuffer.resize(pos); + + return n; +} + +void QXmlStreamReaderPrivate::putString(const QString &s, int from) +{ + putStack.reserve(s.size()); + for (int i = s.size()-1; i >= from; --i) + putStack.rawPush() = s.at(i).unicode(); +} + +void QXmlStreamReaderPrivate::putStringLiteral(const QString &s) +{ + putStack.reserve(s.size()); + for (int i = s.size()-1; i >= 0; --i) + putStack.rawPush() = ((LETTER << 16) | s.at(i).unicode()); +} + +void QXmlStreamReaderPrivate::putReplacement(const QString &s) +{ + putStack.reserve(s.size()); + for (int i = s.size()-1; i >= 0; --i) { + ushort c = s.at(i).unicode(); + if (c == '\n' || c == '\r') + putStack.rawPush() = ((LETTER << 16) | c); + else + putStack.rawPush() = c; + } +} +void QXmlStreamReaderPrivate::putReplacementInAttributeValue(const QString &s) +{ + putStack.reserve(s.size()); + for (int i = s.size()-1; i >= 0; --i) { + ushort c = s.at(i).unicode(); + if (c == '&' || c == ';') + putStack.rawPush() = c; + else if (c == '\n' || c == '\r') + putStack.rawPush() = ' '; + else + putStack.rawPush() = ((LETTER << 16) | c); + } +} + +uint QXmlStreamReaderPrivate::getChar_helper() +{ + const int BUFFER_SIZE = 8192; + characterOffset += readBufferPos; + readBufferPos = 0; + readBuffer.resize(0); +#ifndef QT_NO_TEXTCODEC + if (decoder) +#endif + nbytesread = 0; + if (device) { + rawReadBuffer.resize(BUFFER_SIZE); + int nbytesreadOrMinus1 = device->read(rawReadBuffer.data() + nbytesread, BUFFER_SIZE - nbytesread); + nbytesread += qMax(nbytesreadOrMinus1, 0); + } else { + if (nbytesread) + rawReadBuffer += dataBuffer; + else + rawReadBuffer = dataBuffer; + nbytesread = rawReadBuffer.size(); + dataBuffer.clear(); + } + if (!nbytesread) { + atEnd = true; + return StreamEOF; + } + +#ifndef QT_NO_TEXTCODEC + if (!decoder) { + if (nbytesread < 4) { // the 4 is to cover 0xef 0xbb 0xbf plus + // one extra for the utf8 codec + atEnd = true; + return StreamEOF; + } + int mib = 106; // UTF-8 + + // look for byte order mark + uchar ch1 = rawReadBuffer.at(0); + uchar ch2 = rawReadBuffer.at(1); + uchar ch3 = rawReadBuffer.at(2); + uchar ch4 = rawReadBuffer.at(3); + + if ((ch1 == 0 && ch2 == 0 && ch3 == 0xfe && ch4 == 0xff) || + (ch1 == 0xff && ch2 == 0xfe && ch3 == 0 && ch4 == 0)) + mib = 1017; // UTF-32 with byte order mark + else if (ch1 == 0x3c && ch2 == 0x00 && ch3 == 0x00 && ch4 == 0x00) + mib = 1019; // UTF-32LE + else if (ch1 == 0x00 && ch2 == 0x00 && ch3 == 0x00 && ch4 == 0x3c) + mib = 1018; // UTF-32BE + else if ((ch1 == 0xfe && ch2 == 0xff) || (ch1 == 0xff && ch2 == 0xfe)) + mib = 1015; // UTF-16 with byte order mark + else if (ch1 == 0x3c && ch2 == 0x00) + mib = 1014; // UTF-16LE + else if (ch1 == 0x00 && ch2 == 0x3c) + mib = 1013; // UTF-16BE + codec = QTextCodec::codecForMib(mib); + Q_ASSERT(codec); + decoder = codec->makeDecoder(); + } + + decoder->toUnicode(&readBuffer, rawReadBuffer.constData(), nbytesread); + + if(lockEncoding && decoder->hasFailure()) { + raiseWellFormedError(QXmlStream::tr("Encountered incorrectly encoded content.")); + readBuffer.clear(); + return StreamEOF; + } +#else + readBuffer = QString::fromLatin1(rawReadBuffer.data(), nbytesread); +#endif // QT_NO_TEXTCODEC + + readBuffer.reserve(1); // keep capacity when calling resize() next time + + if (readBufferPos < readBuffer.size()) { + ushort c = readBuffer.at(readBufferPos++).unicode(); + return c; + } + + atEnd = true; + return StreamEOF; +} + +QStringRef QXmlStreamReaderPrivate::namespaceForPrefix(const QStringRef &prefix) +{ + for (int j = namespaceDeclarations.size() - 1; j >= 0; --j) { + const NamespaceDeclaration &namespaceDeclaration = namespaceDeclarations.at(j); + if (namespaceDeclaration.prefix == prefix) { + return namespaceDeclaration.namespaceUri; + } + } + +#if 1 + if (namespaceProcessing && !prefix.isEmpty()) + raiseWellFormedError(QXmlStream::tr("Namespace prefix '%1' not declared").arg(prefix)); +#endif + + return QStringRef(); +} + +/* + uses namespaceForPrefix and builds the attribute vector + */ +void QXmlStreamReaderPrivate::resolveTag() +{ + int n = attributeStack.size(); + + if (namespaceProcessing) { + for (int a = 0; a < dtdAttributes.size(); ++a) { + DtdAttribute &dtdAttribute = dtdAttributes[a]; + if (!dtdAttribute.isNamespaceAttribute + || dtdAttribute.defaultValue.isNull() + || dtdAttribute.tagName != qualifiedName + || dtdAttribute.attributeQualifiedName.isNull()) + continue; + int i = 0; + while (i < n && symName(attributeStack[i].key) != dtdAttribute.attributeQualifiedName) + ++i; + if (i != n) + continue; + if (dtdAttribute.attributePrefix.isEmpty() && dtdAttribute.attributeName == QLatin1String("xmlns")) { + NamespaceDeclaration &namespaceDeclaration = namespaceDeclarations.push(); + namespaceDeclaration.prefix.clear(); + + const QStringRef ns(dtdAttribute.defaultValue); + if(ns == QLatin1String("http://www.w3.org/2000/xmlns/") || + ns == QLatin1String("http://www.w3.org/XML/1998/namespace")) + raiseWellFormedError(QXmlStream::tr("Illegal namespace declaration.")); + else + namespaceDeclaration.namespaceUri = ns; + } else if (dtdAttribute.attributePrefix == QLatin1String("xmlns")) { + NamespaceDeclaration &namespaceDeclaration = namespaceDeclarations.push(); + QStringRef namespacePrefix = dtdAttribute.attributeName; + QStringRef namespaceUri = dtdAttribute.defaultValue; + if (((namespacePrefix == QLatin1String("xml")) + ^ (namespaceUri == QLatin1String("http://www.w3.org/XML/1998/namespace"))) + || namespaceUri == QLatin1String("http://www.w3.org/2000/xmlns/") + || namespaceUri.isEmpty() + || namespacePrefix == QLatin1String("xmlns")) + raiseWellFormedError(QXmlStream::tr("Illegal namespace declaration.")); + + namespaceDeclaration.prefix = namespacePrefix; + namespaceDeclaration.namespaceUri = namespaceUri; + } + } + } + + tagStack.top().namespaceDeclaration.namespaceUri = namespaceUri = namespaceForPrefix(prefix); + + attributes.resize(n); + + for (int i = 0; i < n; ++i) { + QXmlStreamAttribute &attribute = attributes[i]; + Attribute &attrib = attributeStack[i]; + QStringRef prefix(symPrefix(attrib.key)); + QStringRef name(symString(attrib.key)); + QStringRef qualifiedName(symName(attrib.key)); + QStringRef value(symString(attrib.value)); + + attribute.m_name = QXmlStreamStringRef(name); + attribute.m_qualifiedName = QXmlStreamStringRef(qualifiedName); + attribute.m_value = QXmlStreamStringRef(value); + + if (!prefix.isEmpty()) { + QStringRef attributeNamespaceUri = namespaceForPrefix(prefix); + attribute.m_namespaceUri = QXmlStreamStringRef(attributeNamespaceUri); + } + + for (int j = 0; j < i; ++j) { + if (attributes[j].name() == attribute.name() + && attributes[j].namespaceUri() == attribute.namespaceUri() + && (namespaceProcessing || attributes[j].qualifiedName() == attribute.qualifiedName())) + raiseWellFormedError(QXmlStream::tr("Attribute '%1' redefined.").arg(attribute.qualifiedName())); + } + } + + for (int a = 0; a < dtdAttributes.size(); ++a) { + DtdAttribute &dtdAttribute = dtdAttributes[a]; + if (dtdAttribute.isNamespaceAttribute + || dtdAttribute.defaultValue.isNull() + || dtdAttribute.tagName != qualifiedName + || dtdAttribute.attributeQualifiedName.isNull()) + continue; + int i = 0; + while (i < n && symName(attributeStack[i].key) != dtdAttribute.attributeQualifiedName) + ++i; + if (i != n) + continue; + + + + QXmlStreamAttribute attribute; + attribute.m_name = QXmlStreamStringRef(dtdAttribute.attributeName); + attribute.m_qualifiedName = QXmlStreamStringRef(dtdAttribute.attributeQualifiedName); + attribute.m_value = QXmlStreamStringRef(dtdAttribute.defaultValue); + + if (!dtdAttribute.attributePrefix.isEmpty()) { + QStringRef attributeNamespaceUri = namespaceForPrefix(dtdAttribute.attributePrefix); + attribute.m_namespaceUri = QXmlStreamStringRef(attributeNamespaceUri); + } + attribute.m_isDefault = true; + attributes.append(attribute); + } + + attributeStack.clear(); +} + +void QXmlStreamReaderPrivate::resolvePublicNamespaces() +{ + const Tag &tag = tagStack.top(); + int n = namespaceDeclarations.size() - tag.namespaceDeclarationsSize; + publicNamespaceDeclarations.resize(n); + for (int i = 0; i < n; ++i) { + const NamespaceDeclaration &namespaceDeclaration = namespaceDeclarations.at(tag.namespaceDeclarationsSize + i); + QXmlStreamNamespaceDeclaration &publicNamespaceDeclaration = publicNamespaceDeclarations[i]; + publicNamespaceDeclaration.m_prefix = QXmlStreamStringRef(namespaceDeclaration.prefix); + publicNamespaceDeclaration.m_namespaceUri = QXmlStreamStringRef(namespaceDeclaration.namespaceUri); + } +} + +void QXmlStreamReaderPrivate::resolveDtd() +{ + publicNotationDeclarations.resize(notationDeclarations.size()); + for (int i = 0; i < notationDeclarations.size(); ++i) { + const QXmlStreamReaderPrivate::NotationDeclaration ¬ationDeclaration = notationDeclarations.at(i); + QXmlStreamNotationDeclaration &publicNotationDeclaration = publicNotationDeclarations[i]; + publicNotationDeclaration.m_name = QXmlStreamStringRef(notationDeclaration.name); + publicNotationDeclaration.m_systemId = QXmlStreamStringRef(notationDeclaration.systemId); + publicNotationDeclaration.m_publicId = QXmlStreamStringRef(notationDeclaration.publicId); + + } + notationDeclarations.clear(); + publicEntityDeclarations.resize(entityDeclarations.size()); + for (int i = 0; i < entityDeclarations.size(); ++i) { + const QXmlStreamReaderPrivate::EntityDeclaration &entityDeclaration = entityDeclarations.at(i); + QXmlStreamEntityDeclaration &publicEntityDeclaration = publicEntityDeclarations[i]; + publicEntityDeclaration.m_name = QXmlStreamStringRef(entityDeclaration.name); + publicEntityDeclaration.m_notationName = QXmlStreamStringRef(entityDeclaration.notationName); + publicEntityDeclaration.m_systemId = QXmlStreamStringRef(entityDeclaration.systemId); + publicEntityDeclaration.m_publicId = QXmlStreamStringRef(entityDeclaration.publicId); + publicEntityDeclaration.m_value = QXmlStreamStringRef(entityDeclaration.value); + } + entityDeclarations.clear(); + parameterEntityHash.clear(); +} + +uint QXmlStreamReaderPrivate::resolveCharRef(int symbolIndex) +{ + bool ok = true; + uint s; + // ### add toXShort to QStringRef? + if (sym(symbolIndex).c == 'x') + s = symString(symbolIndex, 1).toUInt(&ok, 16); + else + s = symString(symbolIndex).toUInt(&ok, 10); + + ok &= (s == 0x9 || s == 0xa || s == 0xd || (s >= 0x20 && s <= 0xd7ff) + || (s >= 0xe000 && s <= 0xfffd) || (s >= 0x10000 && s <= QChar::LastValidCodePoint)); + + return ok ? s : 0; +} + + +void QXmlStreamReaderPrivate::checkPublicLiteral(const QStringRef &publicId) +{ +//#x20 | #xD | #xA | [a-zA-Z0-9] | [-'()+,./:=?;!*#@$_%] + + const ushort *data = reinterpret_cast(publicId.constData()); + uchar c = 0; + int i; + for (i = publicId.size() - 1; i >= 0; --i) { + if (data[i] < 256) + switch ((c = data[i])) { + case ' ': case '\n': case '\r': case '-': case '(': case ')': + case '+': case ',': case '.': case '/': case ':': case '=': + case '?': case ';': case '!': case '*': case '#': case '@': + case '$': case '_': case '%': case '\'': case '\"': + continue; + default: + if ((c >= 'a' && c <= 'z') + || (c >= 'A' && c <= 'Z') + || (c >= '0' && c <= '9')) + continue; + } + break; + } + if (i >= 0) + raiseWellFormedError(QXmlStream::tr("Unexpected character '%1' in public id literal.").arg(QChar(QLatin1Char(c)))); +} + +/* + Checks whether the document starts with an xml declaration. If it + does, this function returns \c true; otherwise it sets up everything + for a synthetic start document event and returns \c false. + */ +bool QXmlStreamReaderPrivate::checkStartDocument() +{ + hasCheckedStartDocument = true; + + if (scanString(spell[XML], XML)) + return true; + + type = QXmlStreamReader::StartDocument; + if (atEnd) { + hasCheckedStartDocument = false; + raiseError(QXmlStreamReader::PrematureEndOfDocumentError); + } + return false; +} + +void QXmlStreamReaderPrivate::startDocument() +{ + QString err; + if (documentVersion != QLatin1String("1.0")) { + if (documentVersion.contains(QLatin1Char(' '))) + err = QXmlStream::tr("Invalid XML version string."); + else + err = QXmlStream::tr("Unsupported XML version."); + } + int n = attributeStack.size(); + + /* We use this bool to ensure that the pesudo attributes are in the + * proper order: + * + * [23] XMLDecl ::= '' */ + bool hasStandalone = false; + + for (int i = 0; err.isNull() && i < n; ++i) { + Attribute &attrib = attributeStack[i]; + QStringRef prefix(symPrefix(attrib.key)); + QStringRef key(symString(attrib.key)); + QStringRef value(symString(attrib.value)); + + if (prefix.isEmpty() && key == QLatin1String("encoding")) { + documentEncoding = value; + + if(hasStandalone) + err = QXmlStream::tr("The standalone pseudo attribute must appear after the encoding."); + if (!QXmlUtils::isEncName(value)) + err = QXmlStream::tr("%1 is an invalid encoding name.").arg(value); + else { +#ifdef QT_NO_TEXTCODEC + readBuffer = QString::fromLatin1(rawReadBuffer.data(), nbytesread); +#else + QTextCodec *const newCodec = QTextCodec::codecForName(value.toLatin1()); + if (!newCodec) + err = QXmlStream::tr("Encoding %1 is unsupported").arg(value); + else if (newCodec != codec && !lockEncoding) { + codec = newCodec; + delete decoder; + decoder = codec->makeDecoder(); + decoder->toUnicode(&readBuffer, rawReadBuffer.data(), nbytesread); + } +#endif // QT_NO_TEXTCODEC + } + } else if (prefix.isEmpty() && key == QLatin1String("standalone")) { + hasStandalone = true; + if (value == QLatin1String("yes")) + standalone = true; + else if (value == QLatin1String("no")) + standalone = false; + else + err = QXmlStream::tr("Standalone accepts only yes or no."); + } else { + err = QXmlStream::tr("Invalid attribute in XML declaration."); + } + } + + if (!err.isNull()) + raiseWellFormedError(err); + attributeStack.clear(); +} + + +void QXmlStreamReaderPrivate::raiseError(QXmlStreamReader::Error error, const QString& message) +{ + this->error = error; + errorString = message; + if (errorString.isNull()) { + if (error == QXmlStreamReader::PrematureEndOfDocumentError) + errorString = QXmlStream::tr("Premature end of document."); + else if (error == QXmlStreamReader::CustomError) + errorString = QXmlStream::tr("Invalid document."); + } + + type = QXmlStreamReader::Invalid; +} + +void QXmlStreamReaderPrivate::raiseWellFormedError(const QString &message) +{ + raiseError(QXmlStreamReader::NotWellFormedError, message); +} + +void QXmlStreamReaderPrivate::parseError() +{ + + if (token == EOF_SYMBOL) { + raiseError(QXmlStreamReader::PrematureEndOfDocumentError); + return; + } + const int nmax = 4; + QString error_message; + int ers = state_stack[tos]; + int nexpected = 0; + int expected[nmax]; + if (token != ERROR) + for (int tk = 0; tk < TERMINAL_COUNT; ++tk) { + int k = t_action(ers, tk); + if (k <= 0) + continue; + if (spell[tk]) { + if (nexpected < nmax) + expected[nexpected++] = tk; + } + } + + if (nexpected && nexpected < nmax) { + //: '' + QString exp_str = QXmlStream::tr("'%1'", "expected").arg(QLatin1String(spell[expected[0]])); + if (nexpected == 2) { + //: , '' + exp_str = QXmlStream::tr("%1 or '%2'", "expected").arg(exp_str, QLatin1String(spell[expected[1]])); + } else if (nexpected > 2) { + int s = 1; + for (; s < nexpected - 1; ++s) { + //: , '' + exp_str = QXmlStream::tr("%1, '%2'", "expected").arg(exp_str, QLatin1String(spell[expected[s]])); + } + //: , or '' + exp_str = QXmlStream::tr("%1, or '%2'", "expected").arg(exp_str, QLatin1String(spell[expected[s]])); + } + error_message = QXmlStream::tr("Expected %1, but got '%2'.").arg(exp_str, QLatin1String(spell[token])); + } else { + error_message = QXmlStream::tr("Unexpected '%1'.").arg(QLatin1String(spell[token])); + } + + raiseWellFormedError(error_message); +} + +void QXmlStreamReaderPrivate::resume(int rule) { + resumeReduction = rule; + if (error == QXmlStreamReader::NoError) + raiseError(QXmlStreamReader::PrematureEndOfDocumentError); +} + +/*! Returns the current line number, starting with 1. + +\sa columnNumber(), characterOffset() + */ +qint64 QXmlStreamReader::lineNumber() const +{ + Q_D(const QXmlStreamReader); + return d->lineNumber + 1; // in public we start with 1 +} + +/*! Returns the current column number, starting with 0. + +\sa lineNumber(), characterOffset() + */ +qint64 QXmlStreamReader::columnNumber() const +{ + Q_D(const QXmlStreamReader); + return d->characterOffset - d->lastLineStart + d->readBufferPos; +} + +/*! Returns the current character offset, starting with 0. + +\sa lineNumber(), columnNumber() +*/ +qint64 QXmlStreamReader::characterOffset() const +{ + Q_D(const QXmlStreamReader); + return d->characterOffset + d->readBufferPos; +} + + +/*! Returns the text of \l Characters, \l Comment, \l DTD, or + EntityReference. + */ +QStringRef QXmlStreamReader::text() const +{ + Q_D(const QXmlStreamReader); + return d->text; +} + + +/*! If the tokenType() is \l DTD, this function returns the DTD's + notation declarations. Otherwise an empty vector is returned. + + The QXmlStreamNotationDeclarations class is defined to be a QVector + of QXmlStreamNotationDeclaration. + */ +QXmlStreamNotationDeclarations QXmlStreamReader::notationDeclarations() const +{ + Q_D(const QXmlStreamReader); + if (d->notationDeclarations.size()) + const_cast(d)->resolveDtd(); + return d->publicNotationDeclarations; +} + + +/*! If the tokenType() is \l DTD, this function returns the DTD's + unparsed (external) entity declarations. Otherwise an empty vector is returned. + + The QXmlStreamEntityDeclarations class is defined to be a QVector + of QXmlStreamEntityDeclaration. + */ +QXmlStreamEntityDeclarations QXmlStreamReader::entityDeclarations() const +{ + Q_D(const QXmlStreamReader); + if (d->entityDeclarations.size()) + const_cast(d)->resolveDtd(); + return d->publicEntityDeclarations; +} + +/*! + \since 4.4 + + If the tokenType() is \l DTD, this function returns the DTD's + name. Otherwise an empty string is returned. + + */ +QStringRef QXmlStreamReader::dtdName() const +{ + Q_D(const QXmlStreamReader); + if (d->type == QXmlStreamReader::DTD) + return d->dtdName; + return QStringRef(); +} + +/*! + \since 4.4 + + If the tokenType() is \l DTD, this function returns the DTD's + public identifier. Otherwise an empty string is returned. + + */ +QStringRef QXmlStreamReader::dtdPublicId() const +{ + Q_D(const QXmlStreamReader); + if (d->type == QXmlStreamReader::DTD) + return d->dtdPublicId; + return QStringRef(); +} + +/*! + \since 4.4 + + If the tokenType() is \l DTD, this function returns the DTD's + system identifier. Otherwise an empty string is returned. + + */ +QStringRef QXmlStreamReader::dtdSystemId() const +{ + Q_D(const QXmlStreamReader); + if (d->type == QXmlStreamReader::DTD) + return d->dtdSystemId; + return QStringRef(); +} + +/*! If the tokenType() is \l StartElement, this function returns the + element's namespace declarations. Otherwise an empty vector is + returned. + + The QXmlStreamNamespaceDeclarations class is defined to be a QVector + of QXmlStreamNamespaceDeclaration. + + \sa addExtraNamespaceDeclaration(), addExtraNamespaceDeclarations() + */ +QXmlStreamNamespaceDeclarations QXmlStreamReader::namespaceDeclarations() const +{ + Q_D(const QXmlStreamReader); + if (d->publicNamespaceDeclarations.isEmpty() && d->type == StartElement) + const_cast(d)->resolvePublicNamespaces(); + return d->publicNamespaceDeclarations; +} + + +/*! + \since 4.4 + + Adds an \a extraNamespaceDeclaration. The declaration will be + valid for children of the current element, or - should the function + be called before any elements are read - for the entire XML + document. + + \sa namespaceDeclarations(), addExtraNamespaceDeclarations(), setNamespaceProcessing() + */ +void QXmlStreamReader::addExtraNamespaceDeclaration(const QXmlStreamNamespaceDeclaration &extraNamespaceDeclaration) +{ + Q_D(QXmlStreamReader); + QXmlStreamReaderPrivate::NamespaceDeclaration &namespaceDeclaration = d->namespaceDeclarations.push(); + namespaceDeclaration.prefix = d->addToStringStorage(extraNamespaceDeclaration.prefix()); + namespaceDeclaration.namespaceUri = d->addToStringStorage(extraNamespaceDeclaration.namespaceUri()); +} + +/*! + \since 4.4 + + Adds a vector of declarations specified by \a extraNamespaceDeclarations. + + \sa namespaceDeclarations(), addExtraNamespaceDeclaration() + */ +void QXmlStreamReader::addExtraNamespaceDeclarations(const QXmlStreamNamespaceDeclarations &extraNamespaceDeclarations) +{ + for (int i = 0; i < extraNamespaceDeclarations.size(); ++i) + addExtraNamespaceDeclaration(extraNamespaceDeclarations.at(i)); +} + + +/*! Convenience function to be called in case a StartElement was + read. Reads until the corresponding EndElement and returns all text + in-between. In case of no error, the current token (see tokenType()) + after having called this function is EndElement. + + The function concatenates text() when it reads either \l Characters + or EntityReference tokens, but skips ProcessingInstruction and \l + Comment. If the current token is not StartElement, an empty string is + returned. + + The \a behaviour defines what happens in case anything else is + read before reaching EndElement. The function can include the text from + child elements (useful for example for HTML), ignore child elements, or + raise an UnexpectedElementError and return what was read so far (default). + + \since 4.6 + */ +QString QXmlStreamReader::readElementText(ReadElementTextBehaviour behaviour) +{ + Q_D(QXmlStreamReader); + if (isStartElement()) { + QString result; + forever { + switch (readNext()) { + case Characters: + case EntityReference: + result.insert(result.size(), d->text.unicode(), d->text.size()); + break; + case EndElement: + return result; + case ProcessingInstruction: + case Comment: + break; + case StartElement: + if (behaviour == SkipChildElements) { + skipCurrentElement(); + break; + } else if (behaviour == IncludeChildElements) { + result += readElementText(behaviour); + break; + } + Q_FALLTHROUGH(); + default: + if (d->error || behaviour == ErrorOnUnexpectedElement) { + if (!d->error) + d->raiseError(UnexpectedElementError, QXmlStream::tr("Expected character data.")); + return result; + } + } + } + } + return QString(); +} + +/*! Raises a custom error with an optional error \a message. + + \sa error(), errorString() + */ +void QXmlStreamReader::raiseError(const QString& message) +{ + Q_D(QXmlStreamReader); + d->raiseError(CustomError, message); +} + +/*! + Returns the error message that was set with raiseError(). + + \sa error(), lineNumber(), columnNumber(), characterOffset() + */ +QString QXmlStreamReader::errorString() const +{ + Q_D(const QXmlStreamReader); + if (d->type == QXmlStreamReader::Invalid) + return d->errorString; + return QString(); +} + +/*! Returns the type of the current error, or NoError if no error occurred. + + \sa errorString(), raiseError() + */ +QXmlStreamReader::Error QXmlStreamReader::error() const +{ + Q_D(const QXmlStreamReader); + if (d->type == QXmlStreamReader::Invalid) + return d->error; + return NoError; +} + +/*! + Returns the target of a ProcessingInstruction. + */ +QStringRef QXmlStreamReader::processingInstructionTarget() const +{ + Q_D(const QXmlStreamReader); + return d->processingInstructionTarget; +} + +/*! + Returns the data of a ProcessingInstruction. + */ +QStringRef QXmlStreamReader::processingInstructionData() const +{ + Q_D(const QXmlStreamReader); + return d->processingInstructionData; +} + + + +/*! + Returns the local name of a StartElement, EndElement, or an EntityReference. + + \sa namespaceUri(), qualifiedName() + */ +QStringRef QXmlStreamReader::name() const +{ + Q_D(const QXmlStreamReader); + return d->name; +} + +/*! + Returns the namespaceUri of a StartElement or EndElement. + + \sa name(), qualifiedName() + */ +QStringRef QXmlStreamReader::namespaceUri() const +{ + Q_D(const QXmlStreamReader); + return d->namespaceUri; +} + +/*! + Returns the qualified name of a StartElement or EndElement; + + A qualified name is the raw name of an element in the XML data. It + consists of the namespace prefix, followed by colon, followed by the + element's local name. Since the namespace prefix is not unique (the + same prefix can point to different namespaces and different prefixes + can point to the same namespace), you shouldn't use qualifiedName(), + but the resolved namespaceUri() and the attribute's local name(). + + \sa name(), prefix(), namespaceUri() + */ +QStringRef QXmlStreamReader::qualifiedName() const +{ + Q_D(const QXmlStreamReader); + return d->qualifiedName; +} + + + +/*! + \since 4.4 + + Returns the prefix of a StartElement or EndElement. + + \sa name(), qualifiedName() +*/ +QStringRef QXmlStreamReader::prefix() const +{ + Q_D(const QXmlStreamReader); + return d->prefix; +} + +/*! + Returns the attributes of a StartElement. + */ +QXmlStreamAttributes QXmlStreamReader::attributes() const +{ + Q_D(const QXmlStreamReader); + return d->attributes; +} + +#endif // QT_NO_XMLSTREAMREADER + +/*! + \class QXmlStreamAttribute + \inmodule QtCore + \since 4.3 + \reentrant + \brief The QXmlStreamAttribute class represents a single XML attribute + + \ingroup xml-tools + + An attribute consists of an optionally empty namespaceUri(), a + name(), a value(), and an isDefault() attribute. + + The raw XML attribute name is returned as qualifiedName(). +*/ + +/*! + Creates an empty attribute. + */ +QXmlStreamAttribute::QXmlStreamAttribute() +{ + m_isDefault = false; +} + +/*! + Destructs an attribute. + */ +QXmlStreamAttribute::~QXmlStreamAttribute() +{ +} + +/*! Constructs an attribute in the namespace described with \a + namespaceUri with \a name and value \a value. + */ +QXmlStreamAttribute::QXmlStreamAttribute(const QString &namespaceUri, const QString &name, const QString &value) +{ + m_namespaceUri = QXmlStreamStringRef(QStringRef(&namespaceUri)); + m_name = m_qualifiedName = QXmlStreamStringRef(QStringRef(&name)); + m_value = QXmlStreamStringRef(QStringRef(&value)); + m_namespaceUri = QXmlStreamStringRef(QStringRef(&namespaceUri)); +} + +/*! + Constructs an attribute with qualified name \a qualifiedName and value \a value. + */ +QXmlStreamAttribute::QXmlStreamAttribute(const QString &qualifiedName, const QString &value) +{ + int colon = qualifiedName.indexOf(QLatin1Char(':')); + m_name = QXmlStreamStringRef(QStringRef(&qualifiedName, + colon + 1, + qualifiedName.size() - (colon + 1))); + m_qualifiedName = QXmlStreamStringRef(QStringRef(&qualifiedName)); + m_value = QXmlStreamStringRef(QStringRef(&value)); +} + +/*! \fn QStringRef QXmlStreamAttribute::namespaceUri() const + + Returns the attribute's resolved namespaceUri, or an empty string + reference if the attribute does not have a defined namespace. + */ +/*! \fn QStringRef QXmlStreamAttribute::name() const + Returns the attribute's local name. + */ +/*! \fn QStringRef QXmlStreamAttribute::qualifiedName() const + Returns the attribute's qualified name. + + A qualified name is the raw name of an attribute in the XML + data. It consists of the namespace prefix(), followed by colon, + followed by the attribute's local name(). Since the namespace prefix + is not unique (the same prefix can point to different namespaces + and different prefixes can point to the same namespace), you + shouldn't use qualifiedName(), but the resolved namespaceUri() and + the attribute's local name(). + */ +/*! + \fn QStringRef QXmlStreamAttribute::prefix() const + \since 4.4 + Returns the attribute's namespace prefix. + + \sa name(), qualifiedName() + +*/ + +/*! \fn QStringRef QXmlStreamAttribute::value() const + Returns the attribute's value. + */ + +/*! \fn bool QXmlStreamAttribute::isDefault() const + + Returns \c true if the parser added this attribute with a default + value following an ATTLIST declaration in the DTD; otherwise + returns \c false. +*/ +/*! \fn bool QXmlStreamAttribute::operator==(const QXmlStreamAttribute &other) const + + Compares this attribute with \a other and returns \c true if they are + equal; otherwise returns \c false. + */ +/*! \fn bool QXmlStreamAttribute::operator!=(const QXmlStreamAttribute &other) const + + Compares this attribute with \a other and returns \c true if they are + not equal; otherwise returns \c false. + */ + + +/*! + Creates a copy of \a other. + */ +QXmlStreamAttribute::QXmlStreamAttribute(const QXmlStreamAttribute &other) +{ + *this = other; +} + +/*! + Assigns \a other to this attribute. + */ +QXmlStreamAttribute& QXmlStreamAttribute::operator=(const QXmlStreamAttribute &other) +{ + m_name = other.m_name; + m_namespaceUri = other.m_namespaceUri; + m_qualifiedName = other.m_qualifiedName; + m_value = other.m_value; + m_isDefault = other.m_isDefault; + return *this; +} + + +/*! + \class QXmlStreamAttributes + \inmodule QtCore + \since 4.3 + \reentrant + \brief The QXmlStreamAttributes class represents a vector of QXmlStreamAttribute. + + Attributes are returned by a QXmlStreamReader in + \l{QXmlStreamReader::attributes()} {attributes()} when the reader + reports a \l {QXmlStreamReader::StartElement}{start element}. The + class can also be used with a QXmlStreamWriter as an argument to + \l {QXmlStreamWriter::writeAttributes()}{writeAttributes()}. + + The convenience function value() loops over the vector and returns + an attribute value for a given namespaceUri and an attribute's + name. + + New attributes can be added with append(). + + \ingroup xml-tools +*/ + +/*! + \fn QXmlStreamAttributes::QXmlStreamAttributes() + + A constructor for QXmlStreamAttributes. +*/ + +/*! + \typedef QXmlStreamNotationDeclarations + \relates QXmlStreamNotationDeclaration + + Synonym for QVector. +*/ + + +/*! + \class QXmlStreamNotationDeclaration + \inmodule QtCore + \since 4.3 + \reentrant + \brief The QXmlStreamNotationDeclaration class represents a DTD notation declaration. + + \ingroup xml-tools + + An notation declaration consists of a name(), a systemId(), and a publicId(). +*/ + +/*! + Creates an empty notation declaration. +*/ +QXmlStreamNotationDeclaration::QXmlStreamNotationDeclaration() +{ +} +/*! + Creates a copy of \a other. + */ +QXmlStreamNotationDeclaration::QXmlStreamNotationDeclaration(const QXmlStreamNotationDeclaration &other) +{ + *this = other; +} + +/*! + Assigns \a other to this notation declaration. + */ +QXmlStreamNotationDeclaration& QXmlStreamNotationDeclaration::operator=(const QXmlStreamNotationDeclaration &other) +{ + m_name = other.m_name; + m_systemId = other.m_systemId; + m_publicId = other.m_publicId; + return *this; +} + +/*! +Destructs this notation declaration. +*/ +QXmlStreamNotationDeclaration::~QXmlStreamNotationDeclaration() +{ +} + +/*! \fn QStringRef QXmlStreamNotationDeclaration::name() const + +Returns the notation name. +*/ +/*! \fn QStringRef QXmlStreamNotationDeclaration::systemId() const + +Returns the system identifier. +*/ +/*! \fn QStringRef QXmlStreamNotationDeclaration::publicId() const + +Returns the public identifier. +*/ + +/*! \fn inline bool QXmlStreamNotationDeclaration::operator==(const QXmlStreamNotationDeclaration &other) const + + Compares this notation declaration with \a other and returns \c true + if they are equal; otherwise returns \c false. + */ +/*! \fn inline bool QXmlStreamNotationDeclaration::operator!=(const QXmlStreamNotationDeclaration &other) const + + Compares this notation declaration with \a other and returns \c true + if they are not equal; otherwise returns \c false. + */ + +/*! + \typedef QXmlStreamNamespaceDeclarations + \relates QXmlStreamNamespaceDeclaration + + Synonym for QVector. +*/ + +/*! + \class QXmlStreamNamespaceDeclaration + \inmodule QtCore + \since 4.3 + \reentrant + \brief The QXmlStreamNamespaceDeclaration class represents a namespace declaration. + + \ingroup xml-tools + + An namespace declaration consists of a prefix() and a namespaceUri(). +*/ +/*! \fn inline bool QXmlStreamNamespaceDeclaration::operator==(const QXmlStreamNamespaceDeclaration &other) const + + Compares this namespace declaration with \a other and returns \c true + if they are equal; otherwise returns \c false. + */ +/*! \fn inline bool QXmlStreamNamespaceDeclaration::operator!=(const QXmlStreamNamespaceDeclaration &other) const + + Compares this namespace declaration with \a other and returns \c true + if they are not equal; otherwise returns \c false. + */ + +/*! + Creates an empty namespace declaration. +*/ +QXmlStreamNamespaceDeclaration::QXmlStreamNamespaceDeclaration() +{ +} + +/*! + \since 4.4 + + Creates a namespace declaration with \a prefix and \a namespaceUri. +*/ +QXmlStreamNamespaceDeclaration::QXmlStreamNamespaceDeclaration(const QString &prefix, const QString &namespaceUri) +{ + m_prefix = prefix; + m_namespaceUri = namespaceUri; +} + +/*! + Creates a copy of \a other. + */ +QXmlStreamNamespaceDeclaration::QXmlStreamNamespaceDeclaration(const QXmlStreamNamespaceDeclaration &other) +{ + *this = other; +} + +/*! + Assigns \a other to this namespace declaration. + */ +QXmlStreamNamespaceDeclaration& QXmlStreamNamespaceDeclaration::operator=(const QXmlStreamNamespaceDeclaration &other) +{ + m_prefix = other.m_prefix; + m_namespaceUri = other.m_namespaceUri; + return *this; +} +/*! +Destructs this namespace declaration. +*/ +QXmlStreamNamespaceDeclaration::~QXmlStreamNamespaceDeclaration() +{ +} + +/*! \fn QStringRef QXmlStreamNamespaceDeclaration::prefix() const + +Returns the prefix. +*/ +/*! \fn QStringRef QXmlStreamNamespaceDeclaration::namespaceUri() const + +Returns the namespaceUri. +*/ + + + + +/*! + \typedef QXmlStreamEntityDeclarations + \relates QXmlStreamEntityDeclaration + + Synonym for QVector. +*/ + +/*! + \class QXmlStreamStringRef + \inmodule QtCore + \since 4.3 + \internal +*/ + +/*! + \class QXmlStreamEntityDeclaration + \inmodule QtCore + \since 4.3 + \reentrant + \brief The QXmlStreamEntityDeclaration class represents a DTD entity declaration. + + \ingroup xml-tools + + An entity declaration consists of a name(), a notationName(), a + systemId(), a publicId(), and a value(). +*/ + +/*! + Creates an empty entity declaration. +*/ +QXmlStreamEntityDeclaration::QXmlStreamEntityDeclaration() +{ +} + +/*! + Creates a copy of \a other. + */ +QXmlStreamEntityDeclaration::QXmlStreamEntityDeclaration(const QXmlStreamEntityDeclaration &other) +{ + *this = other; +} + +/*! + Assigns \a other to this entity declaration. + */ +QXmlStreamEntityDeclaration& QXmlStreamEntityDeclaration::operator=(const QXmlStreamEntityDeclaration &other) +{ + m_name = other.m_name; + m_notationName = other.m_notationName; + m_systemId = other.m_systemId; + m_publicId = other.m_publicId; + m_value = other.m_value; + return *this; +} + +/*! + Destructs this entity declaration. +*/ +QXmlStreamEntityDeclaration::~QXmlStreamEntityDeclaration() +{ +} + +/*! \fn QXmlStreamStringRef::swap(QXmlStreamStringRef &other) + \since 5.6 + + Swaps this string reference's contents with \a other. + This function is very fast and never fails. +*/ + +/*! \fn QStringRef QXmlStreamEntityDeclaration::name() const + +Returns the entity name. +*/ +/*! \fn QStringRef QXmlStreamEntityDeclaration::notationName() const + +Returns the notation name. +*/ +/*! \fn QStringRef QXmlStreamEntityDeclaration::systemId() const + +Returns the system identifier. +*/ +/*! \fn QStringRef QXmlStreamEntityDeclaration::publicId() const + +Returns the public identifier. +*/ +/*! \fn QStringRef QXmlStreamEntityDeclaration::value() const + +Returns the entity's value. +*/ + +/*! \fn bool QXmlStreamEntityDeclaration::operator==(const QXmlStreamEntityDeclaration &other) const + + Compares this entity declaration with \a other and returns \c true if + they are equal; otherwise returns \c false. + */ +/*! \fn bool QXmlStreamEntityDeclaration::operator!=(const QXmlStreamEntityDeclaration &other) const + + Compares this entity declaration with \a other and returns \c true if + they are not equal; otherwise returns \c false. + */ + +/*! Returns the value of the attribute \a name in the namespace + described with \a namespaceUri, or an empty string reference if the + attribute is not defined. The \a namespaceUri can be empty. + */ +QStringRef QXmlStreamAttributes::value(const QString &namespaceUri, const QString &name) const +{ + for (int i = 0; i < size(); ++i) { + const QXmlStreamAttribute &attribute = at(i); + if (attribute.name() == name && attribute.namespaceUri() == namespaceUri) + return attribute.value(); + } + return QStringRef(); +} + +/*!\overload + Returns the value of the attribute \a name in the namespace + described with \a namespaceUri, or an empty string reference if the + attribute is not defined. The \a namespaceUri can be empty. + */ +QStringRef QXmlStreamAttributes::value(const QString &namespaceUri, QLatin1String name) const +{ + for (int i = 0; i < size(); ++i) { + const QXmlStreamAttribute &attribute = at(i); + if (attribute.name() == name && attribute.namespaceUri() == namespaceUri) + return attribute.value(); + } + return QStringRef(); +} + +/*!\overload + Returns the value of the attribute \a name in the namespace + described with \a namespaceUri, or an empty string reference if the + attribute is not defined. The \a namespaceUri can be empty. + */ +QStringRef QXmlStreamAttributes::value(QLatin1String namespaceUri, QLatin1String name) const +{ + for (int i = 0; i < size(); ++i) { + const QXmlStreamAttribute &attribute = at(i); + if (attribute.name() == name && attribute.namespaceUri() == namespaceUri) + return attribute.value(); + } + return QStringRef(); +} + +/*!\overload + + Returns the value of the attribute with qualified name \a + qualifiedName , or an empty string reference if the attribute is not + defined. A qualified name is the raw name of an attribute in the XML + data. It consists of the namespace prefix, followed by colon, + followed by the attribute's local name. Since the namespace prefix + is not unique (the same prefix can point to different namespaces and + different prefixes can point to the same namespace), you shouldn't + use qualified names, but a resolved namespaceUri and the attribute's + local name. + */ +QStringRef QXmlStreamAttributes::value(const QString &qualifiedName) const +{ + for (int i = 0; i < size(); ++i) { + const QXmlStreamAttribute &attribute = at(i); + if (attribute.qualifiedName() == qualifiedName) + return attribute.value(); + } + return QStringRef(); +} + +/*!\overload + + Returns the value of the attribute with qualified name \a + qualifiedName , or an empty string reference if the attribute is not + defined. A qualified name is the raw name of an attribute in the XML + data. It consists of the namespace prefix, followed by colon, + followed by the attribute's local name. Since the namespace prefix + is not unique (the same prefix can point to different namespaces and + different prefixes can point to the same namespace), you shouldn't + use qualified names, but a resolved namespaceUri and the attribute's + local name. + */ +QStringRef QXmlStreamAttributes::value(QLatin1String qualifiedName) const +{ + for (int i = 0; i < size(); ++i) { + const QXmlStreamAttribute &attribute = at(i); + if (attribute.qualifiedName() == qualifiedName) + return attribute.value(); + } + return QStringRef(); +} + +/*!Appends a new attribute with \a name in the namespace + described with \a namespaceUri, and value \a value. The \a + namespaceUri can be empty. + */ +void QXmlStreamAttributes::append(const QString &namespaceUri, const QString &name, const QString &value) +{ + append(QXmlStreamAttribute(namespaceUri, name, value)); +} + +/*!\overload + Appends a new attribute with qualified name \a qualifiedName and + value \a value. + */ +void QXmlStreamAttributes::append(const QString &qualifiedName, const QString &value) +{ + append(QXmlStreamAttribute(qualifiedName, value)); +} + +#ifndef QT_NO_XMLSTREAMREADER + +/*! \fn bool QXmlStreamReader::isStartDocument() const + Returns \c true if tokenType() equals \l StartDocument; otherwise returns \c false. +*/ +/*! \fn bool QXmlStreamReader::isEndDocument() const + Returns \c true if tokenType() equals \l EndDocument; otherwise returns \c false. +*/ +/*! \fn bool QXmlStreamReader::isStartElement() const + Returns \c true if tokenType() equals \l StartElement; otherwise returns \c false. +*/ +/*! \fn bool QXmlStreamReader::isEndElement() const + Returns \c true if tokenType() equals \l EndElement; otherwise returns \c false. +*/ +/*! \fn bool QXmlStreamReader::isCharacters() const + Returns \c true if tokenType() equals \l Characters; otherwise returns \c false. + + \sa isWhitespace(), isCDATA() +*/ +/*! \fn bool QXmlStreamReader::isComment() const + Returns \c true if tokenType() equals \l Comment; otherwise returns \c false. +*/ +/*! \fn bool QXmlStreamReader::isDTD() const + Returns \c true if tokenType() equals \l DTD; otherwise returns \c false. +*/ +/*! \fn bool QXmlStreamReader::isEntityReference() const + Returns \c true if tokenType() equals \l EntityReference; otherwise returns \c false. +*/ +/*! \fn bool QXmlStreamReader::isProcessingInstruction() const + Returns \c true if tokenType() equals \l ProcessingInstruction; otherwise returns \c false. +*/ + +/*! Returns \c true if the reader reports characters that only consist + of white-space; otherwise returns \c false. + + \sa isCharacters(), text() +*/ +bool QXmlStreamReader::isWhitespace() const +{ + Q_D(const QXmlStreamReader); + return d->type == QXmlStreamReader::Characters && d->isWhitespace; +} + +/*! Returns \c true if the reader reports characters that stem from a + CDATA section; otherwise returns \c false. + + \sa isCharacters(), text() +*/ +bool QXmlStreamReader::isCDATA() const +{ + Q_D(const QXmlStreamReader); + return d->type == QXmlStreamReader::Characters && d->isCDATA; +} + + + +/*! + Returns \c true if this document has been declared standalone in the + XML declaration; otherwise returns \c false. + + If no XML declaration has been parsed, this function returns \c false. + */ +bool QXmlStreamReader::isStandaloneDocument() const +{ + Q_D(const QXmlStreamReader); + return d->standalone; +} + + +/*! + \since 4.4 + + If the tokenType() is \l StartDocument, this function returns the + version string as specified in the XML declaration. + Otherwise an empty string is returned. + */ +QStringRef QXmlStreamReader::documentVersion() const +{ + Q_D(const QXmlStreamReader); + if (d->type == QXmlStreamReader::StartDocument) + return d->documentVersion; + return QStringRef(); +} + +/*! + \since 4.4 + + If the tokenType() is \l StartDocument, this function returns the + encoding string as specified in the XML declaration. + Otherwise an empty string is returned. + */ +QStringRef QXmlStreamReader::documentEncoding() const +{ + Q_D(const QXmlStreamReader); + if (d->type == QXmlStreamReader::StartDocument) + return d->documentEncoding; + return QStringRef(); +} + +#endif // QT_NO_XMLSTREAMREADER + +/*! + \class QXmlStreamWriter + \inmodule QtCore + \since 4.3 + \reentrant + + \brief The QXmlStreamWriter class provides an XML writer with a + simple streaming API. + + \ingroup xml-tools + + QXmlStreamWriter is the counterpart to QXmlStreamReader for writing + XML. Like its related class, it operates on a QIODevice specified + with setDevice(). The API is simple and straightforward: for every + XML token or event you want to write, the writer provides a + specialized function. + + You start a document with writeStartDocument() and end it with + writeEndDocument(). This will implicitly close all remaining open + tags. + + Element tags are opened with writeStartElement() followed by + writeAttribute() or writeAttributes(), element content, and then + writeEndElement(). A shorter form writeEmptyElement() can be used + to write empty elements, followed by writeAttributes(). + + Element content consists of either characters, entity references or + nested elements. It is written with writeCharacters(), which also + takes care of escaping all forbidden characters and character + sequences, writeEntityReference(), or subsequent calls to + writeStartElement(). A convenience method writeTextElement() can be + used for writing terminal elements that contain nothing but text. + + The following abridged code snippet shows the basic use of the class + to write formatted XML with indentation: + + \snippet qxmlstreamwriter/main.cpp start stream + \dots + \snippet qxmlstreamwriter/main.cpp write element + \dots + \snippet qxmlstreamwriter/main.cpp finish stream + + QXmlStreamWriter takes care of prefixing namespaces, all you have to + do is specify the \c namespaceUri when writing elements or + attributes. If you must conform to certain prefixes, you can force + the writer to use them by declaring the namespaces manually with + either writeNamespace() or writeDefaultNamespace(). Alternatively, + you can bypass the stream writer's namespace support and use + overloaded methods that take a qualified name instead. The namespace + \e http://www.w3.org/XML/1998/namespace is implicit and mapped to the + prefix \e xml. + + The stream writer can automatically format the generated XML data by + adding line-breaks and indentation to empty sections between + elements, making the XML data more readable for humans and easier to + work with for most source code management systems. The feature can + be turned on with the \l autoFormatting property, and customized + with the \l autoFormattingIndent property. + + Other functions are writeCDATA(), writeComment(), + writeProcessingInstruction(), and writeDTD(). Chaining of XML + streams is supported with writeCurrentToken(). + + By default, QXmlStreamWriter encodes XML in UTF-8. Different + encodings can be enforced using setCodec(). + + If an error occurs while writing to the underlying device, hasError() + starts returning true and subsequent writes are ignored. + + The \l{QXmlStream Bookmarks Example} illustrates how to use a + stream writer to write an XML bookmark file (XBEL) that + was previously read in by a QXmlStreamReader. + +*/ + +#ifndef QT_NO_XMLSTREAMWRITER + +class QXmlStreamWriterPrivate : public QXmlStreamPrivateTagStack { + QXmlStreamWriter *q_ptr; + Q_DECLARE_PUBLIC(QXmlStreamWriter) +public: + QXmlStreamWriterPrivate(QXmlStreamWriter *q); + ~QXmlStreamWriterPrivate() { + if (deleteDevice) + delete device; +#ifndef QT_NO_TEXTCODEC + delete encoder; +#endif + } + + void write(const QStringRef &); + void write(const QString &); + void writeEscaped(const QString &, bool escapeWhitespace = false); + void write(const char *s, int len); + template void write(const char (&s)[N]) { write(s, N - 1); } + bool finishStartElement(bool contents = true); + void writeStartElement(const QString &namespaceUri, const QString &name); + QIODevice *device; + QString *stringDevice; + uint deleteDevice :1; + uint inStartElement :1; + uint inEmptyElement :1; + uint lastWasStartElement :1; + uint wroteSomething :1; + uint hasIoError :1; + uint hasEncodingError :1; + uint autoFormatting :1; + uint isCodecASCIICompatible :1; + QByteArray autoFormattingIndent; + NamespaceDeclaration emptyNamespace; + int lastNamespaceDeclaration; + +#ifndef QT_NO_TEXTCODEC + QTextCodec *codec; + QTextEncoder *encoder; +#endif + void checkIfASCIICompatibleCodec(); + + NamespaceDeclaration &findNamespace(const QString &namespaceUri, bool writeDeclaration = false, bool noDefault = false); + void writeNamespaceDeclaration(const NamespaceDeclaration &namespaceDeclaration); + + int namespacePrefixCount; + + void indent(int level); +}; + + +QXmlStreamWriterPrivate::QXmlStreamWriterPrivate(QXmlStreamWriter *q) + :autoFormattingIndent(4, ' ') +{ + q_ptr = q; + device = 0; + stringDevice = 0; + deleteDevice = false; +#ifndef QT_NO_TEXTCODEC + codec = QTextCodec::codecForMib(106); // utf8 + encoder = codec->makeEncoder(QTextCodec::IgnoreHeader); // no byte order mark for utf8 +#endif + checkIfASCIICompatibleCodec(); + inStartElement = inEmptyElement = false; + wroteSomething = false; + hasIoError = false; + hasEncodingError = false; + lastWasStartElement = false; + lastNamespaceDeclaration = 1; + autoFormatting = false; + namespacePrefixCount = 0; +} + +void QXmlStreamWriterPrivate::checkIfASCIICompatibleCodec() +{ +#ifndef QT_NO_TEXTCODEC + Q_ASSERT(encoder); + // test ASCII-compatibility using the letter 'a' + QChar letterA = QLatin1Char('a'); + const QByteArray bytesA = encoder->fromUnicode(&letterA, 1); + const bool isCodecASCIICompatibleA = (bytesA.count() == 1) && (bytesA[0] == 0x61) ; + QChar letterLess = QLatin1Char('<'); + const QByteArray bytesLess = encoder->fromUnicode(&letterLess, 1); + const bool isCodecASCIICompatibleLess = (bytesLess.count() == 1) && (bytesLess[0] == 0x3C) ; + isCodecASCIICompatible = isCodecASCIICompatibleA && isCodecASCIICompatibleLess ; +#else + isCodecASCIICompatible = true; +#endif +} + +void QXmlStreamWriterPrivate::write(const QStringRef &s) +{ + if (device) { + if (hasIoError) + return; +#ifdef QT_NO_TEXTCODEC + QByteArray bytes = s.toLatin1(); +#else + QByteArray bytes = encoder->fromUnicode(s.constData(), s.size()); + if (encoder->hasFailure()) { + hasEncodingError = true; + return; + } +#endif + if (device->write(bytes) != bytes.size()) + hasIoError = true; + } + else if (stringDevice) + s.appendTo(stringDevice); + else + qWarning("QXmlStreamWriter: No device"); +} + +void QXmlStreamWriterPrivate::write(const QString &s) +{ + if (device) { + if (hasIoError) + return; +#ifdef QT_NO_TEXTCODEC + QByteArray bytes = s.toLatin1(); +#else + QByteArray bytes = encoder->fromUnicode(s); + if (encoder->hasFailure()) { + hasEncodingError = true; + return; + } +#endif + if (device->write(bytes) != bytes.size()) + hasIoError = true; + } + else if (stringDevice) + stringDevice->append(s); + else + qWarning("QXmlStreamWriter: No device"); +} + +void QXmlStreamWriterPrivate::writeEscaped(const QString &s, bool escapeWhitespace) +{ + QString escaped; + escaped.reserve(s.size()); + for ( int i = 0; i < s.size(); ++i ) { + QChar c = s.at(i); + switch (c.unicode()) { + case '<': + escaped.append(QLatin1String("<")); + break; + case '>': + escaped.append(QLatin1String(">")); + break; + case '&': + escaped.append(QLatin1String("&")); + break; + case '\"': + escaped.append(QLatin1String(""")); + break; + case '\t': + if (escapeWhitespace) + escaped.append(QLatin1String(" ")); + else + escaped += c; + break; + case '\n': + if (escapeWhitespace) + escaped.append(QLatin1String(" ")); + else + escaped += c; + break; + case '\v': + case '\f': + hasEncodingError = true; + break; + case '\r': + if (escapeWhitespace) + escaped.append(QLatin1String(" ")); + else + escaped += c; + break; + default: + if (c.unicode() > 0x1f && c.unicode() < 0xfffe) + escaped += c; + else + hasEncodingError = true; + break; + } + } + write(escaped); +} + +// Converts from ASCII to output encoding +void QXmlStreamWriterPrivate::write(const char *s, int len) +{ + if (device) { + if (hasIoError) + return; + if (isCodecASCIICompatible) { + if (device->write(s, len) != len) + hasIoError = true; + return; + } + } + + write(QString::fromLatin1(s, len)); +} + +void QXmlStreamWriterPrivate::writeNamespaceDeclaration(const NamespaceDeclaration &namespaceDeclaration) { + if (namespaceDeclaration.prefix.isEmpty()) { + write(" xmlns=\""); + write(namespaceDeclaration.namespaceUri); + write("\""); + } else { + write(" xmlns:"); + write(namespaceDeclaration.prefix); + write("=\""); + write(namespaceDeclaration.namespaceUri); + write("\""); + } +} + +bool QXmlStreamWriterPrivate::finishStartElement(bool contents) +{ + bool hadSomethingWritten = wroteSomething; + wroteSomething = contents; + if (!inStartElement) + return hadSomethingWritten; + + if (inEmptyElement) { + write("/>"); + QXmlStreamWriterPrivate::Tag &tag = tagStack_pop(); + lastNamespaceDeclaration = tag.namespaceDeclarationsSize; + lastWasStartElement = false; + } else { + write(">"); + } + inStartElement = inEmptyElement = false; + lastNamespaceDeclaration = namespaceDeclarations.size(); + return hadSomethingWritten; +} + +QXmlStreamPrivateTagStack::NamespaceDeclaration &QXmlStreamWriterPrivate::findNamespace(const QString &namespaceUri, bool writeDeclaration, bool noDefault) +{ + for (int j = namespaceDeclarations.size() - 1; j >= 0; --j) { + NamespaceDeclaration &namespaceDeclaration = namespaceDeclarations[j]; + if (namespaceDeclaration.namespaceUri == namespaceUri) { + if (!noDefault || !namespaceDeclaration.prefix.isEmpty()) + return namespaceDeclaration; + } + } + if (namespaceUri.isEmpty()) + return emptyNamespace; + NamespaceDeclaration &namespaceDeclaration = namespaceDeclarations.push(); + if (namespaceUri.isEmpty()) { + namespaceDeclaration.prefix.clear(); + } else { + QString s; + int n = ++namespacePrefixCount; + forever { + s = QLatin1Char('n') + QString::number(n++); + int j = namespaceDeclarations.size() - 2; + while (j >= 0 && namespaceDeclarations.at(j).prefix != s) + --j; + if (j < 0) + break; + } + namespaceDeclaration.prefix = addToStringStorage(s); + } + namespaceDeclaration.namespaceUri = addToStringStorage(namespaceUri); + if (writeDeclaration) + writeNamespaceDeclaration(namespaceDeclaration); + return namespaceDeclaration; +} + + + +void QXmlStreamWriterPrivate::indent(int level) +{ + write("\n"); + for (int i = level; i > 0; --i) + write(autoFormattingIndent.constData(), autoFormattingIndent.length()); +} + + +/*! + Constructs a stream writer. + + \sa setDevice() + */ +QXmlStreamWriter::QXmlStreamWriter() + : d_ptr(new QXmlStreamWriterPrivate(this)) +{ +} + +/*! + Constructs a stream writer that writes into \a device; + */ +QXmlStreamWriter::QXmlStreamWriter(QIODevice *device) + : d_ptr(new QXmlStreamWriterPrivate(this)) +{ + Q_D(QXmlStreamWriter); + d->device = device; +} + +/*! Constructs a stream writer that writes into \a array. This is the + same as creating an xml writer that operates on a QBuffer device + which in turn operates on \a array. + */ +QXmlStreamWriter::QXmlStreamWriter(QByteArray *array) + : d_ptr(new QXmlStreamWriterPrivate(this)) +{ + Q_D(QXmlStreamWriter); + d->device = new QBuffer(array); + d->device->open(QIODevice::WriteOnly); + d->deleteDevice = true; +} + + +/*! Constructs a stream writer that writes into \a string. + */ +QXmlStreamWriter::QXmlStreamWriter(QString *string) + : d_ptr(new QXmlStreamWriterPrivate(this)) +{ + Q_D(QXmlStreamWriter); + d->stringDevice = string; +} + +/*! + Destructor. +*/ +QXmlStreamWriter::~QXmlStreamWriter() +{ +} + + +/*! + Sets the current device to \a device. If you want the stream to + write into a QByteArray, you can create a QBuffer device. + + \sa device() +*/ +void QXmlStreamWriter::setDevice(QIODevice *device) +{ + Q_D(QXmlStreamWriter); + if (device == d->device) + return; + d->stringDevice = 0; + if (d->deleteDevice) { + delete d->device; + d->deleteDevice = false; + } + d->device = device; +} + +/*! + Returns the current device associated with the QXmlStreamWriter, + or 0 if no device has been assigned. + + \sa setDevice() +*/ +QIODevice *QXmlStreamWriter::device() const +{ + Q_D(const QXmlStreamWriter); + return d->device; +} + + +#ifndef QT_NO_TEXTCODEC +/*! + Sets the codec for this stream to \a codec. The codec is used for + encoding any data that is written. By default, QXmlStreamWriter + uses UTF-8. + + The encoding information is stored in the initial xml tag which + gets written when you call writeStartDocument(). Call this + function before calling writeStartDocument(). + + \sa codec() +*/ +void QXmlStreamWriter::setCodec(QTextCodec *codec) +{ + Q_D(QXmlStreamWriter); + if (codec) { + d->codec = codec; + delete d->encoder; + d->encoder = codec->makeEncoder(QTextCodec::IgnoreHeader); // no byte order mark for utf8 + d->checkIfASCIICompatibleCodec(); + } +} + +/*! + Sets the codec for this stream to the QTextCodec for the encoding + specified by \a codecName. Common values for \c codecName include + "ISO 8859-1", "UTF-8", and "UTF-16". If the encoding isn't + recognized, nothing happens. + + \sa QTextCodec::codecForName() +*/ +void QXmlStreamWriter::setCodec(const char *codecName) +{ + setCodec(QTextCodec::codecForName(codecName)); +} + +/*! + Returns the codec that is currently assigned to the stream. + + \sa setCodec() +*/ +QTextCodec *QXmlStreamWriter::codec() const +{ + Q_D(const QXmlStreamWriter); + return d->codec; +} +#endif // QT_NO_TEXTCODEC + +/*! + \property QXmlStreamWriter::autoFormatting + \since 4.4 + The auto-formatting flag of the stream writer + + This property controls whether or not the stream writer + automatically formats the generated XML data. If enabled, the + writer automatically adds line-breaks and indentation to empty + sections between elements (ignorable whitespace). The main purpose + of auto-formatting is to split the data into several lines, and to + increase readability for a human reader. The indentation depth can + be controlled through the \l autoFormattingIndent property. + + By default, auto-formatting is disabled. +*/ + +/*! + \since 4.4 + + Enables auto formatting if \a enable is \c true, otherwise + disables it. + + The default value is \c false. + */ +void QXmlStreamWriter::setAutoFormatting(bool enable) +{ + Q_D(QXmlStreamWriter); + d->autoFormatting = enable; +} + +/*! + \since 4.4 + + Returns \c true if auto formattting is enabled, otherwise \c false. + */ +bool QXmlStreamWriter::autoFormatting() const +{ + Q_D(const QXmlStreamWriter); + return d->autoFormatting; +} + +/*! + \property QXmlStreamWriter::autoFormattingIndent + \since 4.4 + + \brief the number of spaces or tabs used for indentation when + auto-formatting is enabled. Positive numbers indicate spaces, + negative numbers tabs. + + The default indentation is 4. + + \sa autoFormatting +*/ + + +void QXmlStreamWriter::setAutoFormattingIndent(int spacesOrTabs) +{ + Q_D(QXmlStreamWriter); + d->autoFormattingIndent = QByteArray(qAbs(spacesOrTabs), spacesOrTabs >= 0 ? ' ' : '\t'); +} + +int QXmlStreamWriter::autoFormattingIndent() const +{ + Q_D(const QXmlStreamWriter); + return d->autoFormattingIndent.count(' ') - d->autoFormattingIndent.count('\t'); +} + +/*! + Returns \c true if writing failed. + + This can happen if the stream failed to write to the underlying + device or if the data to be written contained invalid characters. + + The error status is never reset. Writes happening after the error + occurred may be ignored, even if the error condition is cleared. + */ +bool QXmlStreamWriter::hasError() const +{ + Q_D(const QXmlStreamWriter); + return d->hasIoError || d->hasEncodingError; +} + +/*! + \overload + Writes an attribute with \a qualifiedName and \a value. + + + This function can only be called after writeStartElement() before + any content is written, or after writeEmptyElement(). + */ +void QXmlStreamWriter::writeAttribute(const QString &qualifiedName, const QString &value) +{ + Q_D(QXmlStreamWriter); + Q_ASSERT(d->inStartElement); + Q_ASSERT(qualifiedName.count(QLatin1Char(':')) <= 1); + d->write(" "); + d->write(qualifiedName); + d->write("=\""); + d->writeEscaped(value, true); + d->write("\""); +} + +/*! Writes an attribute with \a name and \a value, prefixed for + the specified \a namespaceUri. If the namespace has not been + declared yet, QXmlStreamWriter will generate a namespace declaration + for it. + + This function can only be called after writeStartElement() before + any content is written, or after writeEmptyElement(). + */ +void QXmlStreamWriter::writeAttribute(const QString &namespaceUri, const QString &name, const QString &value) +{ + Q_D(QXmlStreamWriter); + Q_ASSERT(d->inStartElement); + Q_ASSERT(!name.contains(QLatin1Char(':'))); + QXmlStreamWriterPrivate::NamespaceDeclaration &namespaceDeclaration = d->findNamespace(namespaceUri, true, true); + d->write(" "); + if (!namespaceDeclaration.prefix.isEmpty()) { + d->write(namespaceDeclaration.prefix); + d->write(":"); + } + d->write(name); + d->write("=\""); + d->writeEscaped(value, true); + d->write("\""); +} + +/*! + \overload + + Writes the \a attribute. + + This function can only be called after writeStartElement() before + any content is written, or after writeEmptyElement(). + */ +void QXmlStreamWriter::writeAttribute(const QXmlStreamAttribute& attribute) +{ + if (attribute.namespaceUri().isEmpty()) + writeAttribute(attribute.qualifiedName().toString(), + attribute.value().toString()); + else + writeAttribute(attribute.namespaceUri().toString(), + attribute.name().toString(), + attribute.value().toString()); +} + + +/*! Writes the attribute vector \a attributes. If a namespace + referenced in an attribute not been declared yet, QXmlStreamWriter + will generate a namespace declaration for it. + + This function can only be called after writeStartElement() before + any content is written, or after writeEmptyElement(). + + \sa writeAttribute(), writeNamespace() + */ +void QXmlStreamWriter::writeAttributes(const QXmlStreamAttributes& attributes) +{ + Q_D(QXmlStreamWriter); + Q_ASSERT(d->inStartElement); + Q_UNUSED(d); + for (int i = 0; i < attributes.size(); ++i) + writeAttribute(attributes.at(i)); +} + + +/*! Writes \a text as CDATA section. If \a text contains the + forbidden character sequence "]]>", it is split into different CDATA + sections. + + This function mainly exists for completeness. Normally you should + not need use it, because writeCharacters() automatically escapes all + non-content characters. + */ +void QXmlStreamWriter::writeCDATA(const QString &text) +{ + Q_D(QXmlStreamWriter); + d->finishStartElement(); + QString copy(text); + copy.replace(QLatin1String("]]>"), QLatin1String("]]]]>")); + d->write("write(copy); + d->write("]]>"); +} + + +/*! Writes \a text. The characters "<", "&", and "\"" are escaped as entity + references "<", "&, and """. To avoid the forbidden sequence + "]]>", ">" is also escaped as ">". + + \sa writeEntityReference() + */ +void QXmlStreamWriter::writeCharacters(const QString &text) +{ + Q_D(QXmlStreamWriter); + d->finishStartElement(); + d->writeEscaped(text); +} + + +/*! Writes \a text as XML comment, where \a text must not contain the + forbidden sequence "--" or end with "-". Note that XML does not + provide any way to escape "-" in a comment. + */ +void QXmlStreamWriter::writeComment(const QString &text) +{ + Q_D(QXmlStreamWriter); + Q_ASSERT(!text.contains(QLatin1String("--")) && !text.endsWith(QLatin1Char('-'))); + if (!d->finishStartElement(false) && d->autoFormatting) + d->indent(d->tagStack.size()); + d->write(""); + d->inStartElement = d->lastWasStartElement = false; +} + + +/*! Writes a DTD section. The \a dtd represents the entire + doctypedecl production from the XML 1.0 specification. + */ +void QXmlStreamWriter::writeDTD(const QString &dtd) +{ + Q_D(QXmlStreamWriter); + d->finishStartElement(); + if (d->autoFormatting) + d->write("\n"); + d->write(dtd); + if (d->autoFormatting) + d->write("\n"); +} + + + +/*! \overload + Writes an empty element with qualified name \a qualifiedName. + Subsequent calls to writeAttribute() will add attributes to this element. +*/ +void QXmlStreamWriter::writeEmptyElement(const QString &qualifiedName) +{ + Q_D(QXmlStreamWriter); + Q_ASSERT(qualifiedName.count(QLatin1Char(':')) <= 1); + d->writeStartElement(QString(), qualifiedName); + d->inEmptyElement = true; +} + + +/*! Writes an empty element with \a name, prefixed for the specified + \a namespaceUri. If the namespace has not been declared, + QXmlStreamWriter will generate a namespace declaration for it. + Subsequent calls to writeAttribute() will add attributes to this element. + + \sa writeNamespace() + */ +void QXmlStreamWriter::writeEmptyElement(const QString &namespaceUri, const QString &name) +{ + Q_D(QXmlStreamWriter); + Q_ASSERT(!name.contains(QLatin1Char(':'))); + d->writeStartElement(namespaceUri, name); + d->inEmptyElement = true; +} + + +/*!\overload + Writes a text element with \a qualifiedName and \a text. + + + This is a convenience function equivalent to: + \snippet code/src_corelib_xml_qxmlstream.cpp 1 + +*/ +void QXmlStreamWriter::writeTextElement(const QString &qualifiedName, const QString &text) +{ + writeStartElement(qualifiedName); + writeCharacters(text); + writeEndElement(); +} + +/*! Writes a text element with \a name, prefixed for the specified \a + namespaceUri, and \a text. If the namespace has not been + declared, QXmlStreamWriter will generate a namespace declaration + for it. + + + This is a convenience function equivalent to: + \snippet code/src_corelib_xml_qxmlstream.cpp 2 + +*/ +void QXmlStreamWriter::writeTextElement(const QString &namespaceUri, const QString &name, const QString &text) +{ + writeStartElement(namespaceUri, name); + writeCharacters(text); + writeEndElement(); +} + + +/*! + Closes all remaining open start elements and writes a newline. + + \sa writeStartDocument() + */ +void QXmlStreamWriter::writeEndDocument() +{ + Q_D(QXmlStreamWriter); + while (d->tagStack.size()) + writeEndElement(); + d->write("\n"); +} + +/*! + Closes the previous start element. + + \sa writeStartElement() + */ +void QXmlStreamWriter::writeEndElement() +{ + Q_D(QXmlStreamWriter); + if (d->tagStack.isEmpty()) + return; + + // shortcut: if nothing was written, close as empty tag + if (d->inStartElement && !d->inEmptyElement) { + d->write("/>"); + d->lastWasStartElement = d->inStartElement = false; + QXmlStreamWriterPrivate::Tag &tag = d->tagStack_pop(); + d->lastNamespaceDeclaration = tag.namespaceDeclarationsSize; + return; + } + + if (!d->finishStartElement(false) && !d->lastWasStartElement && d->autoFormatting) + d->indent(d->tagStack.size()-1); + if (d->tagStack.isEmpty()) + return; + d->lastWasStartElement = false; + QXmlStreamWriterPrivate::Tag &tag = d->tagStack_pop(); + d->lastNamespaceDeclaration = tag.namespaceDeclarationsSize; + d->write("write(tag.namespaceDeclaration.prefix); + d->write(":"); + } + d->write(tag.name); + d->write(">"); +} + + + +/*! + Writes the entity reference \a name to the stream, as "&\a{name};". + */ +void QXmlStreamWriter::writeEntityReference(const QString &name) +{ + Q_D(QXmlStreamWriter); + d->finishStartElement(); + d->write("&"); + d->write(name); + d->write(";"); +} + + +/*! Writes a namespace declaration for \a namespaceUri with \a + prefix. If \a prefix is empty, QXmlStreamWriter assigns a unique + prefix consisting of the letter 'n' followed by a number. + + If writeStartElement() or writeEmptyElement() was called, the + declaration applies to the current element; otherwise it applies to + the next child element. + + Note that the prefix \e xml is both predefined and reserved for + \e http://www.w3.org/XML/1998/namespace, which in turn cannot be + bound to any other prefix. The prefix \e xmlns and its URI + \e http://www.w3.org/2000/xmlns/ are used for the namespace mechanism + itself and thus completely forbidden in declarations. + + */ +void QXmlStreamWriter::writeNamespace(const QString &namespaceUri, const QString &prefix) +{ + Q_D(QXmlStreamWriter); + Q_ASSERT(prefix != QLatin1String("xmlns")); + if (prefix.isEmpty()) { + d->findNamespace(namespaceUri, d->inStartElement); + } else { + Q_ASSERT(!((prefix == QLatin1String("xml")) ^ (namespaceUri == QLatin1String("http://www.w3.org/XML/1998/namespace")))); + Q_ASSERT(namespaceUri != QLatin1String("http://www.w3.org/2000/xmlns/")); + QXmlStreamWriterPrivate::NamespaceDeclaration &namespaceDeclaration = d->namespaceDeclarations.push(); + namespaceDeclaration.prefix = d->addToStringStorage(prefix); + namespaceDeclaration.namespaceUri = d->addToStringStorage(namespaceUri); + if (d->inStartElement) + d->writeNamespaceDeclaration(namespaceDeclaration); + } +} + + +/*! Writes a default namespace declaration for \a namespaceUri. + + If writeStartElement() or writeEmptyElement() was called, the + declaration applies to the current element; otherwise it applies to + the next child element. + + Note that the namespaces \e http://www.w3.org/XML/1998/namespace + (bound to \e xmlns) and \e http://www.w3.org/2000/xmlns/ (bound to + \e xml) by definition cannot be declared as default. + */ +void QXmlStreamWriter::writeDefaultNamespace(const QString &namespaceUri) +{ + Q_D(QXmlStreamWriter); + Q_ASSERT(namespaceUri != QLatin1String("http://www.w3.org/XML/1998/namespace")); + Q_ASSERT(namespaceUri != QLatin1String("http://www.w3.org/2000/xmlns/")); + QXmlStreamWriterPrivate::NamespaceDeclaration &namespaceDeclaration = d->namespaceDeclarations.push(); + namespaceDeclaration.prefix.clear(); + namespaceDeclaration.namespaceUri = d->addToStringStorage(namespaceUri); + if (d->inStartElement) + d->writeNamespaceDeclaration(namespaceDeclaration); +} + + +/*! + Writes an XML processing instruction with \a target and \a data, + where \a data must not contain the sequence "?>". + */ +void QXmlStreamWriter::writeProcessingInstruction(const QString &target, const QString &data) +{ + Q_D(QXmlStreamWriter); + Q_ASSERT(!data.contains(QLatin1String("?>"))); + if (!d->finishStartElement(false) && d->autoFormatting) + d->indent(d->tagStack.size()); + d->write("write(target); + if (!data.isNull()) { + d->write(" "); + d->write(data); + } + d->write("?>"); +} + + + +/*!\overload + + Writes a document start with XML version number "1.0". This also + writes the encoding information. + + \sa writeEndDocument(), setCodec() + \since 4.5 + */ +void QXmlStreamWriter::writeStartDocument() +{ + writeStartDocument(QLatin1String("1.0")); +} + + +/*! + Writes a document start with the XML version number \a version. + + \sa writeEndDocument() + */ +void QXmlStreamWriter::writeStartDocument(const QString &version) +{ + Q_D(QXmlStreamWriter); + d->finishStartElement(false); + d->write("write(version); + if (d->device) { // stringDevice does not get any encoding + d->write("\" encoding=\""); +#ifdef QT_NO_TEXTCODEC + d->write("iso-8859-1"); +#else + const QByteArray name = d->codec->name(); + d->write(name.constData(), name.length()); +#endif + } + d->write("\"?>"); +} + +/*! Writes a document start with the XML version number \a version + and a standalone attribute \a standalone. + + \sa writeEndDocument() + \since 4.5 + */ +void QXmlStreamWriter::writeStartDocument(const QString &version, bool standalone) +{ + Q_D(QXmlStreamWriter); + d->finishStartElement(false); + d->write("write(version); + if (d->device) { // stringDevice does not get any encoding + d->write("\" encoding=\""); +#ifdef QT_NO_TEXTCODEC + d->write("iso-8859-1"); +#else + const QByteArray name = d->codec->name(); + d->write(name.constData(), name.length()); +#endif + } + if (standalone) + d->write("\" standalone=\"yes\"?>"); + else + d->write("\" standalone=\"no\"?>"); +} + + +/*!\overload + + Writes a start element with \a qualifiedName. Subsequent calls to + writeAttribute() will add attributes to this element. + + \sa writeEndElement(), writeEmptyElement() + */ +void QXmlStreamWriter::writeStartElement(const QString &qualifiedName) +{ + Q_D(QXmlStreamWriter); + Q_ASSERT(qualifiedName.count(QLatin1Char(':')) <= 1); + d->writeStartElement(QString(), qualifiedName); +} + + +/*! Writes a start element with \a name, prefixed for the specified + \a namespaceUri. If the namespace has not been declared yet, + QXmlStreamWriter will generate a namespace declaration for + it. Subsequent calls to writeAttribute() will add attributes to this + element. + + \sa writeNamespace(), writeEndElement(), writeEmptyElement() + */ +void QXmlStreamWriter::writeStartElement(const QString &namespaceUri, const QString &name) +{ + Q_D(QXmlStreamWriter); + Q_ASSERT(!name.contains(QLatin1Char(':'))); + d->writeStartElement(namespaceUri, name); +} + +void QXmlStreamWriterPrivate::writeStartElement(const QString &namespaceUri, const QString &name) +{ + if (!finishStartElement(false) && autoFormatting) + indent(tagStack.size()); + + Tag &tag = tagStack_push(); + tag.name = addToStringStorage(name); + tag.namespaceDeclaration = findNamespace(namespaceUri); + write("<"); + if (!tag.namespaceDeclaration.prefix.isEmpty()) { + write(tag.namespaceDeclaration.prefix); + write(":"); + } + write(tag.name); + inStartElement = lastWasStartElement = true; + + for (int i = lastNamespaceDeclaration; i < namespaceDeclarations.size(); ++i) + writeNamespaceDeclaration(namespaceDeclarations[i]); + tag.namespaceDeclarationsSize = lastNamespaceDeclaration; +} + +#ifndef QT_NO_XMLSTREAMREADER +/*! Writes the current state of the \a reader. All possible valid + states are supported. + + The purpose of this function is to support chained processing of XML data. + + \sa QXmlStreamReader::tokenType() + */ +void QXmlStreamWriter::writeCurrentToken(const QXmlStreamReader &reader) +{ + switch (reader.tokenType()) { + case QXmlStreamReader::NoToken: + break; + case QXmlStreamReader::StartDocument: + writeStartDocument(); + break; + case QXmlStreamReader::EndDocument: + writeEndDocument(); + break; + case QXmlStreamReader::StartElement: { + QXmlStreamNamespaceDeclarations namespaceDeclarations = reader.namespaceDeclarations(); + for (int i = 0; i < namespaceDeclarations.size(); ++i) { + const QXmlStreamNamespaceDeclaration &namespaceDeclaration = namespaceDeclarations.at(i); + writeNamespace(namespaceDeclaration.namespaceUri().toString(), + namespaceDeclaration.prefix().toString()); + } + writeStartElement(reader.namespaceUri().toString(), reader.name().toString()); + writeAttributes(reader.attributes()); + } break; + case QXmlStreamReader::EndElement: + writeEndElement(); + break; + case QXmlStreamReader::Characters: + if (reader.isCDATA()) + writeCDATA(reader.text().toString()); + else + writeCharacters(reader.text().toString()); + break; + case QXmlStreamReader::Comment: + writeComment(reader.text().toString()); + break; + case QXmlStreamReader::DTD: + writeDTD(reader.text().toString()); + break; + case QXmlStreamReader::EntityReference: + writeEntityReference(reader.name().toString()); + break; + case QXmlStreamReader::ProcessingInstruction: + writeProcessingInstruction(reader.processingInstructionTarget().toString(), + reader.processingInstructionData().toString()); + break; + default: + Q_ASSERT(reader.tokenType() != QXmlStreamReader::Invalid); + qWarning("QXmlStreamWriter: writeCurrentToken() with invalid state."); + break; + } +} + +/*! + \fn bool QXmlStreamAttributes::hasAttribute(const QString &qualifiedName) const + \since 4.5 + + Returns \c true if this QXmlStreamAttributes has an attribute whose + qualified name is \a qualifiedName; otherwise returns \c false. + + Note that this is not namespace aware. For instance, if this + QXmlStreamAttributes contains an attribute whose lexical name is "xlink:href" + this doesn't tell that an attribute named \c href in the XLink namespace is + present, since the \c xlink prefix can be bound to any namespace. Use the + overload that takes a namespace URI and a local name as parameter, for + namespace aware code. +*/ + +/*! + \fn bool QXmlStreamAttributes::hasAttribute(QLatin1String qualifiedName) const + \overload + \since 4.5 +*/ + +/*! + \fn bool QXmlStreamAttributes::hasAttribute(const QString &namespaceUri, + const QString &name) const + \overload + \since 4.5 + + Returns \c true if this QXmlStreamAttributes has an attribute whose + namespace URI and name correspond to \a namespaceUri and \a name; + otherwise returns \c false. +*/ + +#endif // QT_NO_XMLSTREAMREADER +#endif // QT_NO_XMLSTREAMWRITER + +QT_END_NAMESPACE + +#endif // QT_NO_XMLSTREAM diff --git a/src/corelib/serialization/qxmlstream.g b/src/corelib/serialization/qxmlstream.g new file mode 100644 index 0000000000..fd69a6e4af --- /dev/null +++ b/src/corelib/serialization/qxmlstream.g @@ -0,0 +1,1852 @@ +---------------------------------------------------------------------------- +-- +-- Copyright (C) 2016 The Qt Company Ltd. +-- Contact: https://www.qt.io/licensing/ +-- +-- This file is part of the QtCore module of the Qt Toolkit. +-- +-- $QT_BEGIN_LICENSE:LGPL$ +-- Commercial License Usage +-- Licensees holding valid commercial Qt licenses may use this file in +-- accordance with the commercial license agreement provided with the +-- Software or, alternatively, in accordance with the terms contained in +-- a written agreement between you and The Qt Company. For licensing terms +-- and conditions see https://www.qt.io/terms-conditions. For further +-- information use the contact form at https://www.qt.io/contact-us. +-- +-- GNU Lesser General Public License Usage +-- Alternatively, this file may be used under the terms of the GNU Lesser +-- General Public License version 3 as published by the Free Software +-- Foundation and appearing in the file LICENSE.LGPL3 included in the +-- packaging of this file. Please review the following information to +-- ensure the GNU Lesser General Public License version 3 requirements +-- will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +-- +-- GNU General Public License Usage +-- Alternatively, this file may be used under the terms of the GNU +-- General Public License version 2.0 or (at your option) the GNU General +-- Public license version 3 or any later version approved by the KDE Free +-- Qt Foundation. The licenses are as published by the Free Software +-- Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +-- included in the packaging of this file. Please review the following +-- information to ensure the GNU General Public License requirements will +-- be met: https://www.gnu.org/licenses/gpl-2.0.html and +-- https://www.gnu.org/licenses/gpl-3.0.html. +-- +-- $QT_END_LICENSE$ +-- +---------------------------------------------------------------------------- + +%parser QXmlStreamReader_Table + +%merged_output qxmlstream_p.h + +%token NOTOKEN +%token SPACE " " +%token LANGLE "<" +%token RANGLE ">" +%token AMPERSAND "&" +%token HASH "#" +%token QUOTE "\'" +%token DBLQUOTE "\"" +%token LBRACK "[" +%token RBRACK "]" +%token LPAREN "(" +%token RPAREN ")" +%token PIPE "|" +%token EQ "=" +%token PERCENT "%" +%token SLASH "/" +%token COLON ":" +%token SEMICOLON ";" +%token COMMA "," +%token DASH "-" +%token PLUS "+" +%token STAR "*" +%token DOT "." +%token QUESTIONMARK "?" +%token BANG "!" +%token LETTER "[a-zA-Z]" +%token DIGIT "[0-9]" + +-- after langle_bang +%token CDATA_START "[CDATA[" +%token DOCTYPE "DOCTYPE" +%token ELEMENT "ELEMENT" +%token ATTLIST "ATTLIST" +%token ENTITY "ENTITY" +%token NOTATION "NOTATION" + +-- entity decl +%token SYSTEM "SYSTEM" +%token PUBLIC "PUBLIC" +%token NDATA "NDATA" + +-- default decl +%token REQUIRED "REQUIRED" +%token IMPLIED "IMPLIED" +%token FIXED "FIXED" + +-- conent spec +%token EMPTY "EMPTY" +%token ANY "ANY" +%token PCDATA "PCDATA" + +-- error +%token ERROR + +-- entities +%token PARSE_ENTITY +%token ENTITY_DONE +%token UNRESOLVED_ENTITY + +-- att type +%token CDATA "CDATA" +%token ID "ID" +%token IDREF "IDREF" +%token IDREFS "IDREFS" +%token ENTITY "ENTITY" +%token ENTITIES "ENTITIES" +%token NMTOKEN "NMTOKEN" +%token NMTOKENS "NMTOKENS" + +-- xml declaration +%token XML " class QXmlStreamSimpleStack { + T *data; + int tos, cap; +public: + inline QXmlStreamSimpleStack():data(0), tos(-1), cap(0){} + inline ~QXmlStreamSimpleStack(){ if (data) free(data); } + + inline void reserve(int extraCapacity) { + if (tos + extraCapacity + 1 > cap) { + cap = qMax(tos + extraCapacity + 1, cap << 1 ); + data = reinterpret_cast(realloc(data, cap * sizeof(T))); + Q_CHECK_PTR(data); + } + } + + inline T &push() { reserve(1); return data[++tos]; } + inline T &rawPush() { return data[++tos]; } + inline const T &top() const { return data[tos]; } + inline T &top() { return data[tos]; } + inline T &pop() { return data[tos--]; } + inline T &operator[](int index) { return data[index]; } + inline const T &at(int index) const { return data[index]; } + inline int size() const { return tos + 1; } + inline void resize(int s) { tos = s - 1; } + inline bool isEmpty() const { return tos < 0; } + inline void clear() { tos = -1; } +}; + + +class QXmlStream +{ + Q_DECLARE_TR_FUNCTIONS(QXmlStream) +}; + +class QXmlStreamPrivateTagStack { +public: + struct NamespaceDeclaration + { + QStringRef prefix; + QStringRef namespaceUri; + }; + + struct Tag + { + QStringRef name; + QStringRef qualifiedName; + NamespaceDeclaration namespaceDeclaration; + int tagStackStringStorageSize; + int namespaceDeclarationsSize; + }; + + + QXmlStreamPrivateTagStack(); + QXmlStreamSimpleStack namespaceDeclarations; + QString tagStackStringStorage; + int tagStackStringStorageSize; + int initialTagStackStringStorageSize; + bool tagsDone; + + inline QStringRef addToStringStorage(const QStringRef &s) { + return addToStringStorage(qToStringViewIgnoringNull(s)); + } + inline QStringRef addToStringStorage(const QString &s) { + return addToStringStorage(qToStringViewIgnoringNull(s)); + } + QStringRef addToStringStorage(QStringView s) + { + int pos = tagStackStringStorageSize; + int sz = s.size(); + if (pos != tagStackStringStorage.size()) + tagStackStringStorage.resize(pos); + tagStackStringStorage.append(s.data(), sz); + tagStackStringStorageSize += sz; + return QStringRef(&tagStackStringStorage, pos, sz); + } + + QXmlStreamSimpleStack tagStack; + + + inline Tag &tagStack_pop() { + Tag& tag = tagStack.pop(); + tagStackStringStorageSize = tag.tagStackStringStorageSize; + namespaceDeclarations.resize(tag.namespaceDeclarationsSize); + tagsDone = tagStack.isEmpty(); + return tag; + } + inline Tag &tagStack_push() { + Tag &tag = tagStack.push(); + tag.tagStackStringStorageSize = tagStackStringStorageSize; + tag.namespaceDeclarationsSize = namespaceDeclarations.size(); + return tag; + } +}; + + +class QXmlStreamEntityResolver; +#ifndef QT_NO_XMLSTREAMREADER +class QXmlStreamReaderPrivate : public QXmlStreamReader_Table, public QXmlStreamPrivateTagStack{ + QXmlStreamReader *q_ptr; + Q_DECLARE_PUBLIC(QXmlStreamReader) +public: + QXmlStreamReaderPrivate(QXmlStreamReader *q); + ~QXmlStreamReaderPrivate(); + void init(); + + QByteArray rawReadBuffer; + QByteArray dataBuffer; + uchar firstByte; + qint64 nbytesread; + QString readBuffer; + int readBufferPos; + QXmlStreamSimpleStack putStack; + struct Entity { + Entity() = default; + Entity(const QString &name, const QString &value) + : name(name), value(value), external(false), unparsed(false), literal(false), + hasBeenParsed(false), isCurrentlyReferenced(false){} + static inline Entity createLiteral(QLatin1String name, QLatin1String value) + { Entity result(name, value); result.literal = result.hasBeenParsed = true; return result; } + QString name, value; + uint external : 1; + uint unparsed : 1; + uint literal : 1; + uint hasBeenParsed : 1; + uint isCurrentlyReferenced : 1; + }; + // these hash tables use a QStringView as a key to avoid creating QStrings + // just for lookup. The keys are usually views into Entity::name and thus + // are guaranteed to have the same lifetime as the referenced data: + QHash entityHash; + QHash parameterEntityHash; + QXmlStreamSimpleStackentityReferenceStack; + inline bool referenceEntity(Entity &entity) { + if (entity.isCurrentlyReferenced) { + raiseWellFormedError(QXmlStream::tr("Recursive entity detected.")); + return false; + } + entity.isCurrentlyReferenced = true; + entityReferenceStack.push() = &entity; + injectToken(ENTITY_DONE); + return true; + } + + + QIODevice *device; + bool deleteDevice; +#ifndef QT_NO_TEXTCODEC + QTextCodec *codec; + QTextDecoder *decoder; +#endif + bool atEnd; + + /*! + \sa setType() + */ + QXmlStreamReader::TokenType type; + QXmlStreamReader::Error error; + QString errorString; + QString unresolvedEntity; + + qint64 lineNumber, lastLineStart, characterOffset; + + + void write(const QString &); + void write(const char *); + + + QXmlStreamAttributes attributes; + QStringRef namespaceForPrefix(const QStringRef &prefix); + void resolveTag(); + void resolvePublicNamespaces(); + void resolveDtd(); + uint resolveCharRef(int symbolIndex); + bool checkStartDocument(); + void startDocument(); + void parseError(); + void checkPublicLiteral(const QStringRef &publicId); + + bool scanDtd; + QStringRef lastAttributeValue; + bool lastAttributeIsCData; + struct DtdAttribute { + QStringRef tagName; + QStringRef attributeQualifiedName; + QStringRef attributePrefix; + QStringRef attributeName; + QStringRef defaultValue; + bool isCDATA; + bool isNamespaceAttribute; + }; + QXmlStreamSimpleStack dtdAttributes; + struct NotationDeclaration { + QStringRef name; + QStringRef publicId; + QStringRef systemId; + }; + QXmlStreamSimpleStack notationDeclarations; + QXmlStreamNotationDeclarations publicNotationDeclarations; + QXmlStreamNamespaceDeclarations publicNamespaceDeclarations; + + struct EntityDeclaration { + QStringRef name; + QStringRef notationName; + QStringRef publicId; + QStringRef systemId; + QStringRef value; + bool parameter; + bool external; + inline void clear() { + name.clear(); + notationName.clear(); + publicId.clear(); + systemId.clear(); + value.clear(); + parameter = external = false; + } + }; + QXmlStreamSimpleStack entityDeclarations; + QXmlStreamEntityDeclarations publicEntityDeclarations; + + QStringRef text; + + QStringRef prefix, namespaceUri, qualifiedName, name; + QStringRef processingInstructionTarget, processingInstructionData; + QStringRef dtdName, dtdPublicId, dtdSystemId; + QStringRef documentVersion, documentEncoding; + uint isEmptyElement : 1; + uint isWhitespace : 1; + uint isCDATA : 1; + uint standalone : 1; + uint hasCheckedStartDocument : 1; + uint normalizeLiterals : 1; + uint hasSeenTag : 1; + uint inParseEntity : 1; + uint referenceToUnparsedEntityDetected : 1; + uint referenceToParameterEntityDetected : 1; + uint hasExternalDtdSubset : 1; + uint lockEncoding : 1; + uint namespaceProcessing : 1; + + int resumeReduction; + void resume(int rule); + + inline bool entitiesMustBeDeclared() const { + return (!inParseEntity + && (standalone + || (!referenceToUnparsedEntityDetected + && !referenceToParameterEntityDetected // Errata 13 as of 2006-04-25 + && !hasExternalDtdSubset))); + } + + // qlalr parser + int tos; + int stack_size; + struct Value { + int pos; + int len; + int prefix; + ushort c; + }; + + Value *sym_stack; + int *state_stack; + inline void reallocateStack(); + inline Value &sym(int index) const + { return sym_stack[tos + index - 1]; } + QString textBuffer; + inline void clearTextBuffer() { + if (!scanDtd) { + textBuffer.resize(0); + textBuffer.reserve(256); + } + } + struct Attribute { + Value key; + Value value; + }; + QXmlStreamSimpleStack attributeStack; + + inline QStringRef symString(int index) { + const Value &symbol = sym(index); + return QStringRef(&textBuffer, symbol.pos + symbol.prefix, symbol.len - symbol.prefix); + } + QStringView symView(int index) const + { + const Value &symbol = sym(index); + return QStringView(textBuffer.data() + symbol.pos, symbol.len).mid(symbol.prefix); + } + inline QStringRef symName(int index) { + const Value &symbol = sym(index); + return QStringRef(&textBuffer, symbol.pos, symbol.len); + } + inline QStringRef symString(int index, int offset) { + const Value &symbol = sym(index); + return QStringRef(&textBuffer, symbol.pos + symbol.prefix + offset, symbol.len - symbol.prefix - offset); + } + inline QStringRef symPrefix(int index) { + const Value &symbol = sym(index); + if (symbol.prefix) + return QStringRef(&textBuffer, symbol.pos, symbol.prefix - 1); + return QStringRef(); + } + inline QStringRef symString(const Value &symbol) { + return QStringRef(&textBuffer, symbol.pos + symbol.prefix, symbol.len - symbol.prefix); + } + inline QStringRef symName(const Value &symbol) { + return QStringRef(&textBuffer, symbol.pos, symbol.len); + } + inline QStringRef symPrefix(const Value &symbol) { + if (symbol.prefix) + return QStringRef(&textBuffer, symbol.pos, symbol.prefix - 1); + return QStringRef(); + } + + inline void clearSym() { Value &val = sym(1); val.pos = textBuffer.size(); val.len = 0; } + + + short token; + uint token_char; + + uint filterCarriageReturn(); + inline uint getChar(); + inline uint peekChar(); + inline void putChar(uint c) { putStack.push() = c; } + inline void putChar(QChar c) { putStack.push() = c.unicode(); } + void putString(const QString &s, int from = 0); + void putStringLiteral(const QString &s); + void putReplacement(const QString &s); + void putReplacementInAttributeValue(const QString &s); + uint getChar_helper(); + + bool scanUntil(const char *str, short tokenToInject = -1); + bool scanString(const char *str, short tokenToInject, bool requireSpace = true); + inline void injectToken(ushort tokenToInject) { + putChar(int(tokenToInject) << 16); + } + + QString resolveUndeclaredEntity(const QString &name); + void parseEntity(const QString &value); + QXmlStreamReaderPrivate *entityParser; + + bool scanAfterLangleBang(); + bool scanPublicOrSystem(); + bool scanNData(); + bool scanAfterDefaultDecl(); + bool scanAttType(); + + + // scan optimization functions. Not strictly necessary but LALR is + // not very well suited for scanning fast + int fastScanLiteralContent(); + int fastScanSpace(); + int fastScanContentCharList(); + int fastScanName(int *prefix = 0); + inline int fastScanNMTOKEN(); + + + bool parse(); + inline void consumeRule(int); + + void raiseError(QXmlStreamReader::Error error, const QString& message = QString()); + void raiseWellFormedError(const QString &message); + + QXmlStreamEntityResolver *entityResolver; + +private: + /*! \internal + Never assign to variable type directly. Instead use this function. + + This prevents errors from being ignored. + */ + inline void setType(const QXmlStreamReader::TokenType t) + { + if(type != QXmlStreamReader::Invalid) + type = t; + } +}; + +bool QXmlStreamReaderPrivate::parse() +{ + // cleanup currently reported token + + switch (type) { + case QXmlStreamReader::StartElement: + name.clear(); + prefix.clear(); + qualifiedName.clear(); + namespaceUri.clear(); + publicNamespaceDeclarations.clear(); + attributes.clear(); + if (isEmptyElement) { + setType(QXmlStreamReader::EndElement); + Tag &tag = tagStack_pop(); + namespaceUri = tag.namespaceDeclaration.namespaceUri; + name = tag.name; + qualifiedName = tag.qualifiedName; + isEmptyElement = false; + return true; + } + clearTextBuffer(); + break; + case QXmlStreamReader::EndElement: + name.clear(); + prefix.clear(); + qualifiedName.clear(); + namespaceUri.clear(); + clearTextBuffer(); + break; + case QXmlStreamReader::DTD: + publicNotationDeclarations.clear(); + publicEntityDeclarations.clear(); + dtdName.clear(); + dtdPublicId.clear(); + dtdSystemId.clear(); + Q_FALLTHROUGH(); + case QXmlStreamReader::Comment: + case QXmlStreamReader::Characters: + isCDATA = false; + isWhitespace = true; + text.clear(); + clearTextBuffer(); + break; + case QXmlStreamReader::EntityReference: + text.clear(); + name.clear(); + clearTextBuffer(); + break; + case QXmlStreamReader::ProcessingInstruction: + processingInstructionTarget.clear(); + processingInstructionData.clear(); + clearTextBuffer(); + break; + case QXmlStreamReader::NoToken: + case QXmlStreamReader::Invalid: + break; + case QXmlStreamReader::StartDocument: + lockEncoding = true; + documentVersion.clear(); + documentEncoding.clear(); +#ifndef QT_NO_TEXTCODEC + if (decoder && decoder->hasFailure()) { + raiseWellFormedError(QXmlStream::tr("Encountered incorrectly encoded content.")); + readBuffer.clear(); + return false; + } +#endif + Q_FALLTHROUGH(); + default: + clearTextBuffer(); + ; + } + + setType(QXmlStreamReader::NoToken); + + + // the main parse loop + int act, r; + + if (resumeReduction) { + act = state_stack[tos-1]; + r = resumeReduction; + resumeReduction = 0; + goto ResumeReduction; + } + + act = state_stack[tos]; + + forever { + if (token == -1 && - TERMINAL_COUNT != action_index[act]) { + uint cu = getChar(); + token = NOTOKEN; + token_char = cu == ~0U ? cu : ushort(cu); + if ((cu != ~0U) && (cu & 0xff0000)) { + token = cu >> 16; + } else switch (token_char) { + case 0xfffe: + case 0xffff: + token = ERROR; + break; + case '\r': + token = SPACE; + if (cu == '\r') { + if ((token_char = filterCarriageReturn())) { + ++lineNumber; + lastLineStart = characterOffset + readBufferPos; + break; + } + } else { + break; + } + Q_FALLTHROUGH(); + case ~0U: { + token = EOF_SYMBOL; + if (!tagsDone && !inParseEntity) { + int a = t_action(act, token); + if (a < 0) { + raiseError(QXmlStreamReader::PrematureEndOfDocumentError); + return false; + } + } + + } break; + case '\n': + ++lineNumber; + lastLineStart = characterOffset + readBufferPos; + Q_FALLTHROUGH(); + case ' ': + case '\t': + token = SPACE; + break; + case '&': + token = AMPERSAND; + break; + case '#': + token = HASH; + break; + case '\'': + token = QUOTE; + break; + case '\"': + token = DBLQUOTE; + break; + case '<': + token = LANGLE; + break; + case '>': + token = RANGLE; + break; + case '[': + token = LBRACK; + break; + case ']': + token = RBRACK; + break; + case '(': + token = LPAREN; + break; + case ')': + token = RPAREN; + break; + case '|': + token = PIPE; + break; + case '=': + token = EQ; + break; + case '%': + token = PERCENT; + break; + case '/': + token = SLASH; + break; + case ':': + token = COLON; + break; + case ';': + token = SEMICOLON; + break; + case ',': + token = COMMA; + break; + case '-': + token = DASH; + break; + case '+': + token = PLUS; + break; + case '*': + token = STAR; + break; + case '.': + token = DOT; + break; + case '?': + token = QUESTIONMARK; + break; + case '!': + token = BANG; + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + token = DIGIT; + break; + default: + if (cu < 0x20) + token = NOTOKEN; + else + token = LETTER; + break; + } + } + + act = t_action (act, token); + if (act == ACCEPT_STATE) { + // reset the parser in case someone resumes (process instructions can follow a valid document) + tos = 0; + state_stack[tos++] = 0; + state_stack[tos] = 0; + return true; + } else if (act > 0) { + if (++tos == stack_size-1) + reallocateStack(); + + Value &val = sym_stack[tos]; + val.c = token_char; + val.pos = textBuffer.size(); + val.prefix = 0; + val.len = 1; + if (token_char) + textBuffer += QChar(token_char); + + state_stack[tos] = act; + token = -1; + + + } else if (act < 0) { + r = - act - 1; + +#if defined (QLALR_DEBUG) + int ridx = rule_index[r]; + printf ("%3d) %s ::=", r + 1, spell[rule_info[ridx]]); + ++ridx; + for (int i = ridx; i < ridx + rhs[r]; ++i) { + int symbol = rule_info[i]; + if (const char *name = spell[symbol]) + printf (" %s", name); + else + printf (" #%d", symbol); + } + printf ("\n"); +#endif + + tos -= rhs[r]; + act = state_stack[tos++]; + ResumeReduction: + switch (r) { +./ + +document ::= PARSE_ENTITY content; +/. + case $rule_number: + setType(QXmlStreamReader::EndDocument); + break; +./ + +document ::= prolog; +/. + case $rule_number: + if (type != QXmlStreamReader::Invalid) { + if (hasSeenTag || inParseEntity) { + setType(QXmlStreamReader::EndDocument); + } else { + raiseError(QXmlStreamReader::NotWellFormedError, QXmlStream::tr("Start tag expected.")); + // reset the parser + tos = 0; + state_stack[tos++] = 0; + state_stack[tos] = 0; + return false; + } + } + break; +./ + + +prolog ::= prolog stag content etag; +prolog ::= prolog empty_element_tag; +prolog ::= prolog comment; +prolog ::= prolog xml_decl; +prolog ::= prolog processing_instruction; +prolog ::= prolog doctype_decl; +prolog ::= prolog SPACE; +prolog ::=; + +entity_done ::= ENTITY_DONE; +/. + case $rule_number: + entityReferenceStack.pop()->isCurrentlyReferenced = false; + clearSym(); + break; +./ + + +xml_decl_start ::= XML; +/. + case $rule_number: + if (!scanString(spell[VERSION], VERSION, false) && atEnd) { + resume($rule_number); + return false; + } + break; +./ + +xml_decl ::= xml_decl_start VERSION space_opt EQ space_opt literal attribute_list_opt QUESTIONMARK RANGLE; +/. + case $rule_number: + setType(QXmlStreamReader::StartDocument); + documentVersion = symString(6); + startDocument(); + break; +./ + +external_id ::= SYSTEM literal; +/. + case $rule_number: + hasExternalDtdSubset = true; + dtdSystemId = symString(2); + break; +./ +external_id ::= PUBLIC public_literal space literal; +/. + case $rule_number: + checkPublicLiteral(symString(2)); + dtdPublicId = symString(2); + dtdSystemId = symString(4); + hasExternalDtdSubset = true; + break; +./ +external_id ::=; + +doctype_decl_start ::= langle_bang DOCTYPE qname space; +/. + case $rule_number: + if (!scanPublicOrSystem() && atEnd) { + resume($rule_number); + return false; + } + dtdName = symString(3); + break; +./ + +doctype_decl ::= langle_bang DOCTYPE qname RANGLE; +/. + case $rule_number:./ +doctype_decl ::= langle_bang DOCTYPE qname markup space_opt RANGLE; +/. + case $rule_number: + dtdName = symString(3); + // fall through +./ +doctype_decl ::= doctype_decl_start external_id space_opt markup space_opt RANGLE; +/. + case $rule_number:./ +doctype_decl ::= doctype_decl_start external_id space_opt RANGLE; +/. + case $rule_number: + setType(QXmlStreamReader::DTD); + text = &textBuffer; + break; +./ + +markup_start ::= LBRACK; +/. + case $rule_number: + scanDtd = true; + break; +./ + +markup ::= markup_start markup_list RBRACK; +/. + case $rule_number: + scanDtd = false; + break; +./ + + +markup_list ::= markup_decl | space | pereference; +markup_list ::= markup_list markup_decl | markup_list space | markup_list pereference; +markup_list ::=; + +markup_decl ::= element_decl | attlist_decl | entity_decl | entity_done | notation_decl | processing_instruction | comment; + + +element_decl_start ::= langle_bang ELEMENT qname space; +/. + case $rule_number: + if (!scanString(spell[EMPTY], EMPTY, false) + && !scanString(spell[ANY], ANY, false) + && atEnd) { + resume($rule_number); + return false; + } + break; +./ + +element_decl ::= element_decl_start content_spec space_opt RANGLE; + + +content_spec ::= EMPTY | ANY | mixed | children; + +pcdata_start ::= HASH; +/. + case $rule_number: + if (!scanString(spell[PCDATA], PCDATA, false) && atEnd) { + resume($rule_number); + return false; + } + break; +./ + +pcdata ::= pcdata_start PCDATA; + +questionmark_or_star_or_plus_opt ::= QUESTIONMARK | STAR | PLUS; +questionmark_or_star_or_plus_opt ::=; + +cp ::= qname questionmark_or_star_or_plus_opt | choice_or_seq questionmark_or_star_or_plus_opt; + +cp_pipe_or_comma_list ::= cp space_opt; +cp_pipe_or_comma_list ::= cp space_opt PIPE space_opt cp_pipe_list space_opt; +cp_pipe_or_comma_list ::= cp space_opt COMMA space_opt cp_comma_list space_opt; +cp_pipe_list ::= cp | cp_pipe_list space_opt PIPE space_opt cp; +cp_comma_list ::= cp | cp_comma_list space_opt COMMA space_opt cp; + + +name_pipe_list ::= PIPE space_opt qname; +name_pipe_list ::= name_pipe_list space_opt PIPE space_opt qname; + +star_opt ::= | STAR; + +mixed ::= LPAREN space_opt pcdata space_opt RPAREN star_opt; +mixed ::= LPAREN space_opt pcdata space_opt name_pipe_list space_opt RPAREN STAR; + +choice_or_seq ::= LPAREN space_opt cp_pipe_or_comma_list RPAREN; + +children ::= choice_or_seq questionmark_or_star_or_plus_opt; + + +nmtoken_pipe_list ::= nmtoken; +nmtoken_pipe_list ::= nmtoken_pipe_list space_opt PIPE space_opt nmtoken; + + +att_type ::= CDATA; +/. + case $rule_number: { + lastAttributeIsCData = true; + } break; +./ +att_type ::= ID | IDREF | IDREFS | ENTITY | ENTITIES | NMTOKEN | NMTOKENS; +att_type ::= LPAREN space_opt nmtoken_pipe_list space_opt RPAREN space; +att_type ::= NOTATION LPAREN space_opt nmtoken_pipe_list space_opt RPAREN space; + + +default_declhash ::= HASH; +/. + case $rule_number: + if (!scanAfterDefaultDecl() && atEnd) { + resume($rule_number); + return false; + } + break; +./ + +default_decl ::= default_declhash REQUIRED; +default_decl ::= default_declhash IMPLIED; +default_decl ::= attribute_value; +default_decl ::= default_declhash FIXED space attribute_value; +attdef_start ::= space qname space; +/. + case $rule_number: + sym(1) = sym(2); + lastAttributeValue.clear(); + lastAttributeIsCData = false; + if (!scanAttType() && atEnd) { + resume($rule_number); + return false; + } + break; +./ + +attdef ::= attdef_start att_type default_decl; +/. + case $rule_number: { + DtdAttribute &dtdAttribute = dtdAttributes.push(); + dtdAttribute.tagName.clear(); + dtdAttribute.isCDATA = lastAttributeIsCData; + dtdAttribute.attributePrefix = addToStringStorage(symPrefix(1)); + dtdAttribute.attributeName = addToStringStorage(symString(1)); + dtdAttribute.attributeQualifiedName = addToStringStorage(symName(1)); + dtdAttribute.isNamespaceAttribute = (dtdAttribute.attributePrefix == QLatin1String("xmlns") + || (dtdAttribute.attributePrefix.isEmpty() + && dtdAttribute.attributeName == QLatin1String("xmlns"))); + if (lastAttributeValue.isNull()) { + dtdAttribute.defaultValue.clear(); + } else { + if (dtdAttribute.isCDATA) + dtdAttribute.defaultValue = addToStringStorage(lastAttributeValue); + else + dtdAttribute.defaultValue = addToStringStorage(lastAttributeValue.toString().simplified()); + + } + } break; +./ + +attdef_list ::= attdef; +attdef_list ::= attdef_list attdef; + +attlist_decl ::= langle_bang ATTLIST qname space_opt RANGLE; +attlist_decl ::= langle_bang ATTLIST qname attdef_list space_opt RANGLE; +/. + case $rule_number: { + if (referenceToUnparsedEntityDetected && !standalone) + break; + int n = dtdAttributes.size(); + QStringRef tagName = addToStringStorage(symName(3)); + while (n--) { + DtdAttribute &dtdAttribute = dtdAttributes[n]; + if (!dtdAttribute.tagName.isNull()) + break; + dtdAttribute.tagName = tagName; + for (int i = 0; i < n; ++i) { + if ((dtdAttributes[i].tagName.isNull() || dtdAttributes[i].tagName == tagName) + && dtdAttributes[i].attributeQualifiedName == dtdAttribute.attributeQualifiedName) { + dtdAttribute.attributeQualifiedName.clear(); // redefined, delete it + break; + } + } + } + } break; +./ + +entity_decl_start ::= langle_bang ENTITY name space; +/. + case $rule_number: { + if (!scanPublicOrSystem() && atEnd) { + resume($rule_number); + return false; + } + EntityDeclaration &entityDeclaration = entityDeclarations.push(); + entityDeclaration.clear(); + entityDeclaration.name = symString(3); + } break; +./ + +entity_decl_start ::= langle_bang ENTITY PERCENT space name space; +/. + case $rule_number: { + if (!scanPublicOrSystem() && atEnd) { + resume($rule_number); + return false; + } + EntityDeclaration &entityDeclaration = entityDeclarations.push(); + entityDeclaration.clear(); + entityDeclaration.name = symString(5); + entityDeclaration.parameter = true; + } break; +./ + +entity_decl_external ::= entity_decl_start SYSTEM literal; +/. + case $rule_number: { + if (!scanNData() && atEnd) { + resume($rule_number); + return false; + } + EntityDeclaration &entityDeclaration = entityDeclarations.top(); + entityDeclaration.systemId = symString(3); + entityDeclaration.external = true; + } break; +./ + +entity_decl_external ::= entity_decl_start PUBLIC public_literal space literal; +/. + case $rule_number: { + if (!scanNData() && atEnd) { + resume($rule_number); + return false; + } + EntityDeclaration &entityDeclaration = entityDeclarations.top(); + checkPublicLiteral((entityDeclaration.publicId = symString(3))); + entityDeclaration.systemId = symString(5); + entityDeclaration.external = true; + } break; +./ + +entity_decl ::= entity_decl_external NDATA name space_opt RANGLE; +/. + case $rule_number: { + EntityDeclaration &entityDeclaration = entityDeclarations.top(); + entityDeclaration.notationName = symString(3); + if (entityDeclaration.parameter) + raiseWellFormedError(QXmlStream::tr("NDATA in parameter entity declaration.")); + } + Q_FALLTHROUGH(); +./ + +entity_decl ::= entity_decl_external space_opt RANGLE; +/. + case $rule_number:./ + +entity_decl ::= entity_decl_start entity_value space_opt RANGLE; +/. + case $rule_number: { + if (referenceToUnparsedEntityDetected && !standalone) { + entityDeclarations.pop(); + break; + } + EntityDeclaration &entityDeclaration = entityDeclarations.top(); + if (!entityDeclaration.external) + entityDeclaration.value = symString(2); + auto &hash = entityDeclaration.parameter ? parameterEntityHash : entityHash; + if (!hash.contains(qToStringViewIgnoringNull(entityDeclaration.name))) { + Entity entity(entityDeclaration.name.toString(), + entityDeclaration.value.toString()); + entity.unparsed = (!entityDeclaration.notationName.isNull()); + entity.external = entityDeclaration.external; + hash.insert(qToStringViewIgnoringNull(entity.name), entity); + } + } break; +./ + + +processing_instruction ::= LANGLE QUESTIONMARK name space; +/. + case $rule_number: { + setType(QXmlStreamReader::ProcessingInstruction); + int pos = sym(4).pos + sym(4).len; + processingInstructionTarget = symString(3); + if (scanUntil("?>")) { + processingInstructionData = QStringRef(&textBuffer, pos, textBuffer.size() - pos - 2); + if (!processingInstructionTarget.compare(QLatin1String("xml"), Qt::CaseInsensitive)) { + raiseWellFormedError(QXmlStream::tr("XML declaration not at start of document.")); + } + else if (!QXmlUtils::isNCName(processingInstructionTarget)) + raiseWellFormedError(QXmlStream::tr("%1 is an invalid processing instruction name.") + .arg(processingInstructionTarget)); + } else if (type != QXmlStreamReader::Invalid){ + resume($rule_number); + return false; + } + } break; +./ + +processing_instruction ::= LANGLE QUESTIONMARK name QUESTIONMARK RANGLE; +/. + case $rule_number: + setType(QXmlStreamReader::ProcessingInstruction); + processingInstructionTarget = symString(3); + if (!processingInstructionTarget.compare(QLatin1String("xml"), Qt::CaseInsensitive)) + raiseWellFormedError(QXmlStream::tr("Invalid processing instruction name.")); + break; +./ + + +langle_bang ::= LANGLE BANG; +/. + case $rule_number: + if (!scanAfterLangleBang() && atEnd) { + resume($rule_number); + return false; + } + break; +./ + +comment_start ::= langle_bang DASH DASH; +/. + case $rule_number: + if (!scanUntil("--")) { + resume($rule_number); + return false; + } + break; +./ + +comment ::= comment_start RANGLE; +/. + case $rule_number: { + setType(QXmlStreamReader::Comment); + int pos = sym(1).pos + 4; + text = QStringRef(&textBuffer, pos, textBuffer.size() - pos - 3); + } break; +./ + + +cdata ::= langle_bang CDATA_START; +/. + case $rule_number: { + setType(QXmlStreamReader::Characters); + isCDATA = true; + isWhitespace = false; + int pos = sym(2).pos; + if (scanUntil("]]>", -1)) { + text = QStringRef(&textBuffer, pos, textBuffer.size() - pos - 3); + } else { + resume($rule_number); + return false; + } + } break; +./ + +notation_decl_start ::= langle_bang NOTATION name space; +/. + case $rule_number: { + if (!scanPublicOrSystem() && atEnd) { + resume($rule_number); + return false; + } + NotationDeclaration ¬ationDeclaration = notationDeclarations.push(); + notationDeclaration.name = symString(3); + } break; +./ + +notation_decl ::= notation_decl_start SYSTEM literal space_opt RANGLE; +/. + case $rule_number: { + NotationDeclaration ¬ationDeclaration = notationDeclarations.top(); + notationDeclaration.systemId = symString(3); + notationDeclaration.publicId.clear(); + } break; +./ + +notation_decl ::= notation_decl_start PUBLIC public_literal space_opt RANGLE; +/. + case $rule_number: { + NotationDeclaration ¬ationDeclaration = notationDeclarations.top(); + notationDeclaration.systemId.clear(); + checkPublicLiteral((notationDeclaration.publicId = symString(3))); + } break; +./ + +notation_decl ::= notation_decl_start PUBLIC public_literal space literal space_opt RANGLE; +/. + case $rule_number: { + NotationDeclaration ¬ationDeclaration = notationDeclarations.top(); + checkPublicLiteral((notationDeclaration.publicId = symString(3))); + notationDeclaration.systemId = symString(5); + } break; +./ + + + +content_char ::= RANGLE | HASH | LBRACK | RBRACK | LPAREN | RPAREN | PIPE | EQ | PERCENT | SLASH | COLON | SEMICOLON | COMMA | DASH | PLUS | STAR | DOT | QUESTIONMARK | BANG | QUOTE | DBLQUOTE | LETTER | DIGIT; + +scan_content_char ::= content_char; +/. + case $rule_number: + isWhitespace = false; + Q_FALLTHROUGH(); +./ + +scan_content_char ::= SPACE; +/. + case $rule_number: + sym(1).len += fastScanContentCharList(); + if (atEnd && !inParseEntity) { + resume($rule_number); + return false; + } + break; +./ + +content_char_list ::= content_char_list char_ref; +content_char_list ::= content_char_list entity_ref; +content_char_list ::= content_char_list entity_done; +content_char_list ::= content_char_list scan_content_char; +content_char_list ::= char_ref; +content_char_list ::= entity_ref; +content_char_list ::= entity_done; +content_char_list ::= scan_content_char; + + +character_content ::= content_char_list %prec SHIFT_THERE; +/. + case $rule_number: + if (!textBuffer.isEmpty()) { + setType(QXmlStreamReader::Characters); + text = &textBuffer; + } + break; +./ + +literal ::= QUOTE QUOTE; +/. + case $rule_number:./ +literal ::= DBLQUOTE DBLQUOTE; +/. + case $rule_number: + clearSym(); + break; +./ +literal ::= QUOTE literal_content_with_dblquote QUOTE; +/. + case $rule_number:./ +literal ::= DBLQUOTE literal_content_with_quote DBLQUOTE; +/. + case $rule_number: + sym(1) = sym(2); + break; +./ + +literal_content_with_dblquote ::= literal_content_with_dblquote literal_content; +/. + case $rule_number:./ +literal_content_with_quote ::= literal_content_with_quote literal_content; +/. + case $rule_number:./ +literal_content_with_dblquote ::= literal_content_with_dblquote DBLQUOTE; +/. + case $rule_number:./ +literal_content_with_quote ::= literal_content_with_quote QUOTE; +/. + case $rule_number: + sym(1).len += sym(2).len; + break; +./ +literal_content_with_dblquote ::= literal_content; +literal_content_with_quote ::= literal_content; +literal_content_with_dblquote ::= DBLQUOTE; +literal_content_with_quote ::= QUOTE; + +literal_content_start ::= LETTER | DIGIT | RANGLE | HASH | LBRACK | RBRACK | LPAREN | RPAREN | PIPE | EQ | PERCENT | SLASH | COLON | SEMICOLON | COMMA | DASH | PLUS | STAR | DOT | QUESTIONMARK | BANG; + +literal_content_start ::= SPACE; +/. + case $rule_number: + if (normalizeLiterals) + textBuffer.data()[textBuffer.size()-1] = QLatin1Char(' '); + break; +./ + +literal_content ::= literal_content_start; +/. + case $rule_number: + sym(1).len += fastScanLiteralContent(); + if (atEnd) { + resume($rule_number); + return false; + } + break; +./ + + +public_literal ::= literal; +/. + case $rule_number: { + if (!QXmlUtils::isPublicID(symString(1))) { + raiseWellFormedError(QXmlStream::tr("%1 is an invalid PUBLIC identifier.").arg(symString(1))); + resume($rule_number); + return false; + } + } break; +./ + +entity_value ::= QUOTE QUOTE; +/. + case $rule_number:./ +entity_value ::= DBLQUOTE DBLQUOTE; +/. + case $rule_number: + clearSym(); + break; +./ + +entity_value ::= QUOTE entity_value_content_with_dblquote QUOTE; +/. + case $rule_number:./ +entity_value ::= DBLQUOTE entity_value_content_with_quote DBLQUOTE; +/. + case $rule_number: + sym(1) = sym(2); + break; +./ + +entity_value_content_with_dblquote ::= entity_value_content_with_dblquote entity_value_content; +/. + case $rule_number:./ +entity_value_content_with_quote ::= entity_value_content_with_quote entity_value_content; +/. + case $rule_number:./ +entity_value_content_with_dblquote ::= entity_value_content_with_dblquote DBLQUOTE; +/. + case $rule_number:./ +entity_value_content_with_quote ::= entity_value_content_with_quote QUOTE; +/. + case $rule_number: + sym(1).len += sym(2).len; + break; +./ +entity_value_content_with_dblquote ::= entity_value_content; +entity_value_content_with_quote ::= entity_value_content; +entity_value_content_with_dblquote ::= DBLQUOTE; +entity_value_content_with_quote ::= QUOTE; + +entity_value_content ::= LETTER | DIGIT | LANGLE | RANGLE | HASH | LBRACK | RBRACK | LPAREN | RPAREN | PIPE | EQ | SLASH | COLON | SEMICOLON | COMMA | SPACE | DASH | PLUS | STAR | DOT | QUESTIONMARK | BANG; +entity_value_content ::= char_ref | entity_ref_in_entity_value | entity_done; + + +attribute_value ::= QUOTE QUOTE; +/. + case $rule_number:./ +attribute_value ::= DBLQUOTE DBLQUOTE; +/. + case $rule_number: + clearSym(); + break; +./ +attribute_value ::= QUOTE attribute_value_content_with_dblquote QUOTE; +/. + case $rule_number:./ +attribute_value ::= DBLQUOTE attribute_value_content_with_quote DBLQUOTE; +/. + case $rule_number: + sym(1) = sym(2); + lastAttributeValue = symString(1); + break; +./ + +attribute_value_content_with_dblquote ::= attribute_value_content_with_dblquote attribute_value_content; +/. + case $rule_number:./ +attribute_value_content_with_quote ::= attribute_value_content_with_quote attribute_value_content; +/. + case $rule_number:./ +attribute_value_content_with_dblquote ::= attribute_value_content_with_dblquote DBLQUOTE; +/. + case $rule_number:./ +attribute_value_content_with_quote ::= attribute_value_content_with_quote QUOTE; +/. + case $rule_number: + sym(1).len += sym(2).len; + break; +./ +attribute_value_content_with_dblquote ::= attribute_value_content | DBLQUOTE; +attribute_value_content_with_quote ::= attribute_value_content | QUOTE; + +attribute_value_content ::= literal_content | char_ref | entity_ref_in_attribute_value | entity_done; + +attribute ::= qname space_opt EQ space_opt attribute_value; +/. + case $rule_number: { + QStringRef prefix = symPrefix(1); + if (prefix.isEmpty() && symString(1) == QLatin1String("xmlns") && namespaceProcessing) { + NamespaceDeclaration &namespaceDeclaration = namespaceDeclarations.push(); + namespaceDeclaration.prefix.clear(); + + const QStringRef ns(symString(5)); + if(ns == QLatin1String("http://www.w3.org/2000/xmlns/") || + ns == QLatin1String("http://www.w3.org/XML/1998/namespace")) + raiseWellFormedError(QXmlStream::tr("Illegal namespace declaration.")); + else + namespaceDeclaration.namespaceUri = addToStringStorage(ns); + } else { + Attribute &attribute = attributeStack.push(); + attribute.key = sym(1); + attribute.value = sym(5); + + QStringRef attributeQualifiedName = symName(1); + bool normalize = false; + for (int a = 0; a < dtdAttributes.size(); ++a) { + DtdAttribute &dtdAttribute = dtdAttributes[a]; + if (!dtdAttribute.isCDATA + && dtdAttribute.tagName == qualifiedName + && dtdAttribute.attributeQualifiedName == attributeQualifiedName + ) { + normalize = true; + break; + } + } + if (normalize) { + // normalize attribute value (simplify and trim) + int pos = textBuffer.size(); + int n = 0; + bool wasSpace = true; + for (int i = 0; i < attribute.value.len; ++i) { + QChar c = textBuffer.at(attribute.value.pos + i); + if (c.unicode() == ' ') { + if (wasSpace) + continue; + wasSpace = true; + } else { + wasSpace = false; + } + textBuffer += textBuffer.at(attribute.value.pos + i); + ++n; + } + if (wasSpace) + while (n && textBuffer.at(pos + n - 1).unicode() == ' ') + --n; + attribute.value.pos = pos; + attribute.value.len = n; + } + if (prefix == QLatin1String("xmlns") && namespaceProcessing) { + NamespaceDeclaration &namespaceDeclaration = namespaceDeclarations.push(); + QStringRef namespacePrefix = symString(attribute.key); + QStringRef namespaceUri = symString(attribute.value); + attributeStack.pop(); + if (((namespacePrefix == QLatin1String("xml")) + ^ (namespaceUri == QLatin1String("http://www.w3.org/XML/1998/namespace"))) + || namespaceUri == QLatin1String("http://www.w3.org/2000/xmlns/") + || namespaceUri.isEmpty() + || namespacePrefix == QLatin1String("xmlns")) + raiseWellFormedError(QXmlStream::tr("Illegal namespace declaration.")); + + namespaceDeclaration.prefix = addToStringStorage(namespacePrefix); + namespaceDeclaration.namespaceUri = addToStringStorage(namespaceUri); + } + } + } break; +./ + + + +attribute_list_opt ::= | space | space attribute_list space_opt; +attribute_list ::= attribute | attribute_list space attribute; + +stag_start ::= LANGLE qname; +/. + case $rule_number: { + normalizeLiterals = true; + Tag &tag = tagStack_push(); + prefix = tag.namespaceDeclaration.prefix = addToStringStorage(symPrefix(2)); + name = tag.name = addToStringStorage(symString(2)); + qualifiedName = tag.qualifiedName = addToStringStorage(symName(2)); + if ((!prefix.isEmpty() && !QXmlUtils::isNCName(prefix)) || !QXmlUtils::isNCName(name)) + raiseWellFormedError(QXmlStream::tr("Invalid XML name.")); + } break; +./ + + +empty_element_tag ::= stag_start attribute_list_opt SLASH RANGLE; +/. + case $rule_number: + isEmptyElement = true; + Q_FALLTHROUGH(); +./ + + +stag ::= stag_start attribute_list_opt RANGLE; +/. + case $rule_number: + setType(QXmlStreamReader::StartElement); + resolveTag(); + if (tagStack.size() == 1 && hasSeenTag && !inParseEntity) + raiseWellFormedError(QXmlStream::tr("Extra content at end of document.")); + hasSeenTag = true; + break; +./ + + +etag ::= LANGLE SLASH qname space_opt RANGLE; +/. + case $rule_number: { + setType(QXmlStreamReader::EndElement); + Tag &tag = tagStack_pop(); + + namespaceUri = tag.namespaceDeclaration.namespaceUri; + name = tag.name; + qualifiedName = tag.qualifiedName; + if (qualifiedName != symName(3)) + raiseWellFormedError(QXmlStream::tr("Opening and ending tag mismatch.")); + } break; +./ + + +unresolved_entity ::= UNRESOLVED_ENTITY; +/. + case $rule_number: + if (entitiesMustBeDeclared()) { + raiseWellFormedError(QXmlStream::tr("Entity '%1' not declared.").arg(unresolvedEntity)); + break; + } + setType(QXmlStreamReader::EntityReference); + name = &unresolvedEntity; + break; +./ + +entity_ref ::= AMPERSAND name SEMICOLON; +/. + case $rule_number: { + sym(1).len += sym(2).len + 1; + QStringView reference = symView(2); + if (entityHash.contains(reference)) { + Entity &entity = entityHash[reference]; + if (entity.unparsed) { + raiseWellFormedError(QXmlStream::tr("Reference to unparsed entity '%1'.").arg(reference)); + } else { + if (!entity.hasBeenParsed) { + parseEntity(entity.value); + entity.hasBeenParsed = true; + } + if (entity.literal) + putStringLiteral(entity.value); + else if (referenceEntity(entity)) + putReplacement(entity.value); + textBuffer.chop(2 + sym(2).len); + clearSym(); + } + break; + } + + if (entityResolver) { + QString replacementText = resolveUndeclaredEntity(reference.toString()); + if (!replacementText.isNull()) { + putReplacement(replacementText); + textBuffer.chop(2 + sym(2).len); + clearSym(); + break; + } + } + + injectToken(UNRESOLVED_ENTITY); + unresolvedEntity = symString(2).toString(); + textBuffer.chop(2 + sym(2).len); + clearSym(); + + } break; +./ + +pereference ::= PERCENT name SEMICOLON; +/. + case $rule_number: { + sym(1).len += sym(2).len + 1; + QStringView reference = symView(2); + if (parameterEntityHash.contains(reference)) { + referenceToParameterEntityDetected = true; + Entity &entity = parameterEntityHash[reference]; + if (entity.unparsed || entity.external) { + referenceToUnparsedEntityDetected = true; + } else { + if (referenceEntity(entity)) + putString(entity.value); + textBuffer.chop(2 + sym(2).len); + clearSym(); + } + } else if (entitiesMustBeDeclared()) { + raiseWellFormedError(QXmlStream::tr("Entity '%1' not declared.").arg(symString(2))); + } + } break; +./ + + + +entity_ref_in_entity_value ::= AMPERSAND name SEMICOLON; +/. + case $rule_number: + sym(1).len += sym(2).len + 1; + break; +./ + +entity_ref_in_attribute_value ::= AMPERSAND name SEMICOLON; +/. + case $rule_number: { + sym(1).len += sym(2).len + 1; + QStringView reference = symView(2); + if (entityHash.contains(reference)) { + Entity &entity = entityHash[reference]; + if (entity.unparsed || entity.value.isNull()) { + raiseWellFormedError(QXmlStream::tr("Reference to external entity '%1' in attribute value.").arg(reference)); + break; + } + if (!entity.hasBeenParsed) { + parseEntity(entity.value); + entity.hasBeenParsed = true; + } + if (entity.literal) + putStringLiteral(entity.value); + else if (referenceEntity(entity)) + putReplacementInAttributeValue(entity.value); + textBuffer.chop(2 + sym(2).len); + clearSym(); + break; + } + + if (entityResolver) { + QString replacementText = resolveUndeclaredEntity(reference.toString()); + if (!replacementText.isNull()) { + putReplacement(replacementText); + textBuffer.chop(2 + sym(2).len); + clearSym(); + break; + } + } + if (entitiesMustBeDeclared()) { + raiseWellFormedError(QXmlStream::tr("Entity '%1' not declared.").arg(reference)); + } + } break; +./ + +char_ref ::= AMPERSAND HASH char_ref_value SEMICOLON; +/. + case $rule_number: { + if (uint s = resolveCharRef(3)) { + if (s >= 0xffff) + putStringLiteral(QString::fromUcs4(&s, 1)); + else + putChar((LETTER << 16) | s); + + textBuffer.chop(3 + sym(3).len); + clearSym(); + } else { + raiseWellFormedError(QXmlStream::tr("Invalid character reference.")); + } + } break; +./ + + +char_ref_value ::= LETTER | DIGIT; +char_ref_value ::= char_ref_value LETTER; +/. + case $rule_number:./ +char_ref_value ::= char_ref_value DIGIT; +/. + case $rule_number: + sym(1).len += sym(2).len; + break; +./ + + +content ::= content character_content; +content ::= content stag content etag; +content ::= content empty_element_tag; +content ::= content comment; +content ::= content cdata; +content ::= content xml_decl; +content ::= content processing_instruction; +content ::= content doctype_decl; +content ::= content unresolved_entity; +content ::= ; + + +space ::= SPACE; +/. + case $rule_number: + sym(1).len += fastScanSpace(); + if (atEnd) { + resume($rule_number); + return false; + } + break; +./ + + +space_opt ::=; +space_opt ::= space; + +qname ::= LETTER; +/. + case $rule_number: { + sym(1).len += fastScanName(&sym(1).prefix); + if (atEnd) { + resume($rule_number); + return false; + } + } break; +./ + +name ::= LETTER; +/. + case $rule_number: + sym(1).len += fastScanName(); + if (atEnd) { + resume($rule_number); + return false; + } + break; +./ + +nmtoken ::= LETTER; +/. + case $rule_number:./ +nmtoken ::= DIGIT; +/. + case $rule_number:./ +nmtoken ::= DOT; +/. + case $rule_number:./ +nmtoken ::= DASH; +/. + case $rule_number:./ +nmtoken ::= COLON; +/. + case $rule_number: + sym(1).len += fastScanNMTOKEN(); + if (atEnd) { + resume($rule_number); + return false; + } + + break; +./ + + +/. + default: + ; + } // switch + act = state_stack[tos] = nt_action (act, lhs[r] - TERMINAL_COUNT); + if (type != QXmlStreamReader::NoToken) + return true; + } else { + parseError(); + break; + } + } + return false; +} +#endif //QT_NO_XMLSTREAMREADER.xml + +./ diff --git a/src/corelib/serialization/qxmlstream.h b/src/corelib/serialization/qxmlstream.h new file mode 100644 index 0000000000..2350d12dd6 --- /dev/null +++ b/src/corelib/serialization/qxmlstream.h @@ -0,0 +1,540 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QXMLSTREAM_H +#define QXMLSTREAM_H + +#include + +#ifndef QT_NO_XMLSTREAM + +#include +#include +#include + +QT_BEGIN_NAMESPACE + + +class Q_CORE_EXPORT QXmlStreamStringRef { + QString m_string; + int m_position, m_size; +public: + inline QXmlStreamStringRef():m_position(0), m_size(0){} + inline QXmlStreamStringRef(const QStringRef &aString) + :m_string(aString.string()?*aString.string():QString()), m_position(aString.position()), m_size(aString.size()){} + QXmlStreamStringRef(const QString &aString) : m_string(aString), m_position(0), m_size(m_string.size()) {} +#ifdef Q_COMPILER_RVALUE_REFS + QXmlStreamStringRef(QString &&aString) Q_DECL_NOTHROW : m_string(std::move(aString)), m_position(0), m_size(m_string.size()) {} +#endif + +#if QT_VERSION < QT_VERSION_CHECK(6,0,0) + QXmlStreamStringRef(const QXmlStreamStringRef &other) // = default + : m_string(other.m_string), m_position(other.m_position), m_size(other.m_size) {} +#ifdef Q_COMPILER_RVALUE_REFS + QXmlStreamStringRef(QXmlStreamStringRef &&other) Q_DECL_NOTHROW // = default + : m_string(std::move(other.m_string)), m_position(other.m_position), m_size(other.m_size) {} + QXmlStreamStringRef &operator=(QXmlStreamStringRef &&other) Q_DECL_NOTHROW // = default + { swap(other); return *this; } +#endif + QXmlStreamStringRef &operator=(const QXmlStreamStringRef &other) // = default + { m_string = other.m_string; m_position = other.m_position; m_size = other.m_size; return *this; } + inline ~QXmlStreamStringRef() {} // ### this prevents (or deprecates) all the move/copy special member functions, + // ### that's why we need to provide them by hand above. We can't remove it in + // ### Qt 5, since that would change the way its passed to functions. In Qt 6, remove all. +#endif // Qt < 6.0 + + void swap(QXmlStreamStringRef &other) Q_DECL_NOTHROW + { + qSwap(m_string, other.m_string); + qSwap(m_position, other.m_position); + qSwap(m_size, other.m_size); + } + + inline void clear() { m_string.clear(); m_position = m_size = 0; } + inline operator QStringRef() const { return QStringRef(&m_string, m_position, m_size); } + inline const QString *string() const { return &m_string; } + inline int position() const { return m_position; } + inline int size() const { return m_size; } +}; +Q_DECLARE_SHARED_NOT_MOVABLE_UNTIL_QT6(QXmlStreamStringRef) + + +class QXmlStreamReaderPrivate; +class QXmlStreamAttributes; +class Q_CORE_EXPORT QXmlStreamAttribute { + QXmlStreamStringRef m_name, m_namespaceUri, m_qualifiedName, m_value; +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + void *reserved; +#endif + uint m_isDefault : 1; + friend class QXmlStreamReaderPrivate; + friend class QXmlStreamAttributes; +public: + QXmlStreamAttribute(); + QXmlStreamAttribute(const QString &qualifiedName, const QString &value); +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + QXmlStreamAttribute(const QString &namespaceUri, const QString &name, const QString &value); + QXmlStreamAttribute(const QXmlStreamAttribute &); +#ifdef Q_COMPILER_RVALUE_REFS + QXmlStreamAttribute(QXmlStreamAttribute &&other) Q_DECL_NOTHROW // = default; + : m_name(std::move(other.m_name)), + m_namespaceUri(std::move(other.m_namespaceUri)), + m_qualifiedName(std::move(other.m_qualifiedName)), + m_value(std::move(other.m_value)), + reserved(other.reserved), + m_isDefault(other.m_isDefault) + { + other.reserved = nullptr; + } + QXmlStreamAttribute &operator=(QXmlStreamAttribute &&other) Q_DECL_NOTHROW // = default; + { + m_name = std::move(other.m_name); + m_namespaceUri = std::move(other.m_namespaceUri); + m_qualifiedName = std::move(other.m_qualifiedName); + m_value = std::move(other.m_value); + qSwap(reserved, other.reserved); + m_isDefault = other.m_isDefault; + return *this; + } +#endif + QXmlStreamAttribute& operator=(const QXmlStreamAttribute &); + ~QXmlStreamAttribute(); +#endif // < Qt 6 + + inline QStringRef namespaceUri() const { return m_namespaceUri; } + inline QStringRef name() const { return m_name; } + inline QStringRef qualifiedName() const { return m_qualifiedName; } + inline QStringRef prefix() const { + return QStringRef(m_qualifiedName.string(), + m_qualifiedName.position(), + qMax(0, m_qualifiedName.size() - m_name.size() - 1)); + } + inline QStringRef value() const { return m_value; } + inline bool isDefault() const { return m_isDefault; } + inline bool operator==(const QXmlStreamAttribute &other) const { + return (value() == other.value() + && (namespaceUri().isNull() ? (qualifiedName() == other.qualifiedName()) + : (namespaceUri() == other.namespaceUri() && name() == other.name()))); + } + inline bool operator!=(const QXmlStreamAttribute &other) const + { return !operator==(other); } +}; + +Q_DECLARE_TYPEINFO(QXmlStreamAttribute, Q_MOVABLE_TYPE); + +class Q_CORE_EXPORT QXmlStreamAttributes : public QVector +{ +public: + inline QXmlStreamAttributes() {} + QStringRef value(const QString &namespaceUri, const QString &name) const; + QStringRef value(const QString &namespaceUri, QLatin1String name) const; + QStringRef value(QLatin1String namespaceUri, QLatin1String name) const; + QStringRef value(const QString &qualifiedName) const; + QStringRef value(QLatin1String qualifiedName) const; + void append(const QString &namespaceUri, const QString &name, const QString &value); + void append(const QString &qualifiedName, const QString &value); + + inline bool hasAttribute(const QString &qualifiedName) const + { + return !value(qualifiedName).isNull(); + } + + inline bool hasAttribute(QLatin1String qualifiedName) const + { + return !value(qualifiedName).isNull(); + } + + inline bool hasAttribute(const QString &namespaceUri, const QString &name) const + { + return !value(namespaceUri, name).isNull(); + } + + using QVector::append; +}; + +class Q_CORE_EXPORT QXmlStreamNamespaceDeclaration { + QXmlStreamStringRef m_prefix, m_namespaceUri; +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + void *reserved; +#endif + + friend class QXmlStreamReaderPrivate; +public: + QXmlStreamNamespaceDeclaration(); +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + QXmlStreamNamespaceDeclaration(const QXmlStreamNamespaceDeclaration &); + QXmlStreamNamespaceDeclaration(QXmlStreamNamespaceDeclaration &&other) Q_DECL_NOTHROW // = default + : m_prefix(std::move(other.m_prefix)), + m_namespaceUri(std::move(other.m_namespaceUri)), + reserved(other.reserved) + { + other.reserved = nullptr; + } + QXmlStreamNamespaceDeclaration &operator=(QXmlStreamNamespaceDeclaration &&other) Q_DECL_NOTHROW // = default + { + m_prefix = std::move(other.m_prefix); + m_namespaceUri = std::move(other.m_namespaceUri); + qSwap(reserved, other.reserved); + return *this; + } + QXmlStreamNamespaceDeclaration(const QString &prefix, const QString &namespaceUri); + ~QXmlStreamNamespaceDeclaration(); + QXmlStreamNamespaceDeclaration& operator=(const QXmlStreamNamespaceDeclaration &); +#endif // < Qt 6 + + inline QStringRef prefix() const { return m_prefix; } + inline QStringRef namespaceUri() const { return m_namespaceUri; } + inline bool operator==(const QXmlStreamNamespaceDeclaration &other) const { + return (prefix() == other.prefix() && namespaceUri() == other.namespaceUri()); + } + inline bool operator!=(const QXmlStreamNamespaceDeclaration &other) const + { return !operator==(other); } +}; + +Q_DECLARE_TYPEINFO(QXmlStreamNamespaceDeclaration, Q_MOVABLE_TYPE); +typedef QVector QXmlStreamNamespaceDeclarations; + +class Q_CORE_EXPORT QXmlStreamNotationDeclaration { + QXmlStreamStringRef m_name, m_systemId, m_publicId; +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + void *reserved; +#endif + + friend class QXmlStreamReaderPrivate; +public: + QXmlStreamNotationDeclaration(); +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + ~QXmlStreamNotationDeclaration(); + QXmlStreamNotationDeclaration(const QXmlStreamNotationDeclaration &); + QXmlStreamNotationDeclaration(QXmlStreamNotationDeclaration &&other) Q_DECL_NOTHROW // = default + : m_name(std::move(other.m_name)), + m_systemId(std::move(other.m_systemId)), + m_publicId(std::move(other.m_publicId)), + reserved(other.reserved) + { + other.reserved = nullptr; + } + QXmlStreamNotationDeclaration& operator=(const QXmlStreamNotationDeclaration &); + QXmlStreamNotationDeclaration &operator=(QXmlStreamNotationDeclaration &&other) Q_DECL_NOTHROW // = default + { + m_name = std::move(other.m_name); + m_systemId = std::move(other.m_systemId); + m_publicId = std::move(other.m_publicId); + qSwap(reserved, other.reserved); + return *this; + } +#endif // < Qt 6 + + inline QStringRef name() const { return m_name; } + inline QStringRef systemId() const { return m_systemId; } + inline QStringRef publicId() const { return m_publicId; } + inline bool operator==(const QXmlStreamNotationDeclaration &other) const { + return (name() == other.name() && systemId() == other.systemId() + && publicId() == other.publicId()); + } + inline bool operator!=(const QXmlStreamNotationDeclaration &other) const + { return !operator==(other); } +}; + +Q_DECLARE_TYPEINFO(QXmlStreamNotationDeclaration, Q_MOVABLE_TYPE); +typedef QVector QXmlStreamNotationDeclarations; + +class Q_CORE_EXPORT QXmlStreamEntityDeclaration { + QXmlStreamStringRef m_name, m_notationName, m_systemId, m_publicId, m_value; +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + void *reserved; +#endif + + friend class QXmlStreamReaderPrivate; +public: + QXmlStreamEntityDeclaration(); +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + ~QXmlStreamEntityDeclaration(); + QXmlStreamEntityDeclaration(const QXmlStreamEntityDeclaration &); + QXmlStreamEntityDeclaration(QXmlStreamEntityDeclaration &&other) Q_DECL_NOTHROW // = default + : m_name(std::move(other.m_name)), + m_notationName(std::move(other.m_notationName)), + m_systemId(std::move(other.m_systemId)), + m_publicId(std::move(other.m_publicId)), + m_value(std::move(other.m_value)), + reserved(other.reserved) + { + other.reserved = nullptr; + } + QXmlStreamEntityDeclaration& operator=(const QXmlStreamEntityDeclaration &); + QXmlStreamEntityDeclaration &operator=(QXmlStreamEntityDeclaration &&other) Q_DECL_NOTHROW // = default + { + m_name = std::move(other.m_name); + m_notationName = std::move(other.m_notationName); + m_systemId = std::move(other.m_systemId); + m_publicId = std::move(other.m_publicId); + m_value = std::move(other.m_value); + qSwap(reserved, other.reserved); + return *this; + } +#endif // < Qt 6 + + inline QStringRef name() const { return m_name; } + inline QStringRef notationName() const { return m_notationName; } + inline QStringRef systemId() const { return m_systemId; } + inline QStringRef publicId() const { return m_publicId; } + inline QStringRef value() const { return m_value; } + inline bool operator==(const QXmlStreamEntityDeclaration &other) const { + return (name() == other.name() + && notationName() == other.notationName() + && systemId() == other.systemId() + && publicId() == other.publicId() + && value() == other.value()); + } + inline bool operator!=(const QXmlStreamEntityDeclaration &other) const + { return !operator==(other); } +}; + +Q_DECLARE_TYPEINFO(QXmlStreamEntityDeclaration, Q_MOVABLE_TYPE); +typedef QVector QXmlStreamEntityDeclarations; + + +class Q_CORE_EXPORT QXmlStreamEntityResolver +{ +public: + virtual ~QXmlStreamEntityResolver(); + virtual QString resolveEntity(const QString& publicId, const QString& systemId); + virtual QString resolveUndeclaredEntity(const QString &name); +}; + +#ifndef QT_NO_XMLSTREAMREADER +class Q_CORE_EXPORT QXmlStreamReader { + QDOC_PROPERTY(bool namespaceProcessing READ namespaceProcessing WRITE setNamespaceProcessing) +public: + enum TokenType { + NoToken = 0, + Invalid, + StartDocument, + EndDocument, + StartElement, + EndElement, + Characters, + Comment, + DTD, + EntityReference, + ProcessingInstruction + }; + + + QXmlStreamReader(); + explicit QXmlStreamReader(QIODevice *device); + explicit QXmlStreamReader(const QByteArray &data); + explicit QXmlStreamReader(const QString &data); + explicit QXmlStreamReader(const char * data); + ~QXmlStreamReader(); + + void setDevice(QIODevice *device); + QIODevice *device() const; + void addData(const QByteArray &data); + void addData(const QString &data); + void addData(const char *data); + void clear(); + + + bool atEnd() const; + TokenType readNext(); + + bool readNextStartElement(); + void skipCurrentElement(); + + TokenType tokenType() const; + QString tokenString() const; + + void setNamespaceProcessing(bool); + bool namespaceProcessing() const; + + inline bool isStartDocument() const { return tokenType() == StartDocument; } + inline bool isEndDocument() const { return tokenType() == EndDocument; } + inline bool isStartElement() const { return tokenType() == StartElement; } + inline bool isEndElement() const { return tokenType() == EndElement; } + inline bool isCharacters() const { return tokenType() == Characters; } + bool isWhitespace() const; + bool isCDATA() const; + inline bool isComment() const { return tokenType() == Comment; } + inline bool isDTD() const { return tokenType() == DTD; } + inline bool isEntityReference() const { return tokenType() == EntityReference; } + inline bool isProcessingInstruction() const { return tokenType() == ProcessingInstruction; } + + bool isStandaloneDocument() const; + QStringRef documentVersion() const; + QStringRef documentEncoding() const; + + qint64 lineNumber() const; + qint64 columnNumber() const; + qint64 characterOffset() const; + + QXmlStreamAttributes attributes() const; + + enum ReadElementTextBehaviour { + ErrorOnUnexpectedElement, + IncludeChildElements, + SkipChildElements + }; + QString readElementText(ReadElementTextBehaviour behaviour = ErrorOnUnexpectedElement); + + QStringRef name() const; + QStringRef namespaceUri() const; + QStringRef qualifiedName() const; + QStringRef prefix() const; + + QStringRef processingInstructionTarget() const; + QStringRef processingInstructionData() const; + + QStringRef text() const; + + QXmlStreamNamespaceDeclarations namespaceDeclarations() const; + void addExtraNamespaceDeclaration(const QXmlStreamNamespaceDeclaration &extraNamespaceDeclaraction); + void addExtraNamespaceDeclarations(const QXmlStreamNamespaceDeclarations &extraNamespaceDeclaractions); + QXmlStreamNotationDeclarations notationDeclarations() const; + QXmlStreamEntityDeclarations entityDeclarations() const; + QStringRef dtdName() const; + QStringRef dtdPublicId() const; + QStringRef dtdSystemId() const; + + + enum Error { + NoError, + UnexpectedElementError, + CustomError, + NotWellFormedError, + PrematureEndOfDocumentError + }; + void raiseError(const QString& message = QString()); + QString errorString() const; + Error error() const; + + inline bool hasError() const + { + return error() != NoError; + } + + void setEntityResolver(QXmlStreamEntityResolver *resolver); + QXmlStreamEntityResolver *entityResolver() const; + +private: + Q_DISABLE_COPY(QXmlStreamReader) + Q_DECLARE_PRIVATE(QXmlStreamReader) + QScopedPointer d_ptr; + +}; +#endif // QT_NO_XMLSTREAMREADER + +#ifndef QT_NO_XMLSTREAMWRITER + +class QXmlStreamWriterPrivate; + +class Q_CORE_EXPORT QXmlStreamWriter +{ + QDOC_PROPERTY(bool autoFormatting READ autoFormatting WRITE setAutoFormatting) + QDOC_PROPERTY(int autoFormattingIndent READ autoFormattingIndent WRITE setAutoFormattingIndent) +public: + QXmlStreamWriter(); + explicit QXmlStreamWriter(QIODevice *device); + explicit QXmlStreamWriter(QByteArray *array); + explicit QXmlStreamWriter(QString *string); + ~QXmlStreamWriter(); + + void setDevice(QIODevice *device); + QIODevice *device() const; + +#ifndef QT_NO_TEXTCODEC + void setCodec(QTextCodec *codec); + void setCodec(const char *codecName); + QTextCodec *codec() const; +#endif + + void setAutoFormatting(bool); + bool autoFormatting() const; + + void setAutoFormattingIndent(int spacesOrTabs); + int autoFormattingIndent() const; + + void writeAttribute(const QString &qualifiedName, const QString &value); + void writeAttribute(const QString &namespaceUri, const QString &name, const QString &value); + void writeAttribute(const QXmlStreamAttribute& attribute); + void writeAttributes(const QXmlStreamAttributes& attributes); + + void writeCDATA(const QString &text); + void writeCharacters(const QString &text); + void writeComment(const QString &text); + + void writeDTD(const QString &dtd); + + void writeEmptyElement(const QString &qualifiedName); + void writeEmptyElement(const QString &namespaceUri, const QString &name); + + void writeTextElement(const QString &qualifiedName, const QString &text); + void writeTextElement(const QString &namespaceUri, const QString &name, const QString &text); + + void writeEndDocument(); + void writeEndElement(); + + void writeEntityReference(const QString &name); + void writeNamespace(const QString &namespaceUri, const QString &prefix = QString()); + void writeDefaultNamespace(const QString &namespaceUri); + void writeProcessingInstruction(const QString &target, const QString &data = QString()); + + void writeStartDocument(); + void writeStartDocument(const QString &version); + void writeStartDocument(const QString &version, bool standalone); + void writeStartElement(const QString &qualifiedName); + void writeStartElement(const QString &namespaceUri, const QString &name); + +#ifndef QT_NO_XMLSTREAMREADER + void writeCurrentToken(const QXmlStreamReader &reader); +#endif + + bool hasError() const; + +private: + Q_DISABLE_COPY(QXmlStreamWriter) + Q_DECLARE_PRIVATE(QXmlStreamWriter) + QScopedPointer d_ptr; +}; +#endif // QT_NO_XMLSTREAMWRITER + +QT_END_NAMESPACE + +#endif // QT_NO_XMLSTREAM +#endif // QXMLSTREAM_H diff --git a/src/corelib/serialization/qxmlstream_p.h b/src/corelib/serialization/qxmlstream_p.h new file mode 100644 index 0000000000..5645d812eb --- /dev/null +++ b/src/corelib/serialization/qxmlstream_p.h @@ -0,0 +1,1972 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include + +// This file was generated by qlalr - DO NOT EDIT! +#ifndef QXMLSTREAM_P_H +#define QXMLSTREAM_P_H + +#if defined(ERROR) +# undef ERROR +#endif + +class QXmlStreamReader_Table +{ +public: + enum VariousConstants { + EOF_SYMBOL = 0, + AMPERSAND = 5, + ANY = 41, + ATTLIST = 31, + BANG = 25, + CDATA = 47, + CDATA_START = 28, + COLON = 17, + COMMA = 19, + DASH = 20, + DBLQUOTE = 8, + DIGIT = 27, + DOCTYPE = 29, + DOT = 23, + ELEMENT = 30, + EMPTY = 40, + ENTITIES = 51, + ENTITY = 32, + ENTITY_DONE = 45, + EQ = 14, + ERROR = 43, + FIXED = 39, + HASH = 6, + ID = 48, + IDREF = 49, + IDREFS = 50, + IMPLIED = 38, + LANGLE = 3, + LBRACK = 9, + LETTER = 26, + LPAREN = 11, + NDATA = 36, + NMTOKEN = 52, + NMTOKENS = 53, + NOTATION = 33, + NOTOKEN = 1, + PARSE_ENTITY = 44, + PCDATA = 42, + PERCENT = 15, + PIPE = 13, + PLUS = 21, + PUBLIC = 35, + QUESTIONMARK = 24, + QUOTE = 7, + RANGLE = 4, + RBRACK = 10, + REQUIRED = 37, + RPAREN = 12, + SEMICOLON = 18, + SHIFT_THERE = 56, + SLASH = 16, + SPACE = 2, + STAR = 22, + SYSTEM = 34, + UNRESOLVED_ENTITY = 46, + VERSION = 55, + XML = 54, + + ACCEPT_STATE = 416, + RULE_COUNT = 270, + STATE_COUNT = 427, + TERMINAL_COUNT = 57, + NON_TERMINAL_COUNT = 84, + + GOTO_INDEX_OFFSET = 427, + GOTO_INFO_OFFSET = 1017, + GOTO_CHECK_OFFSET = 1017 + }; + + static const char *const spell []; + static const short lhs []; + static const short rhs []; + static const short goto_default []; + static const short action_default []; + static const short action_index []; + static const short action_info []; + static const short action_check []; + + static inline int nt_action (int state, int nt) + { + const int yyn = action_index [GOTO_INDEX_OFFSET + state] + nt; + if (yyn < 0 || action_check [GOTO_CHECK_OFFSET + yyn] != nt) + return goto_default [nt]; + + return action_info [GOTO_INFO_OFFSET + yyn]; + } + + static inline int t_action (int state, int token) + { + const int yyn = action_index [state] + token; + + if (yyn < 0 || action_check [yyn] != token) + return - action_default [state]; + + return action_info [yyn]; + } +}; + + +const char *const QXmlStreamReader_Table::spell [] = { + "end of file", 0, " ", "<", ">", "&", "#", "\'", "\"", "[", + "]", "(", ")", "|", "=", "%", "/", ":", ";", ",", + "-", "+", "*", ".", "?", "!", "[a-zA-Z]", "[0-9]", "[CDATA[", "DOCTYPE", + "ELEMENT", "ATTLIST", "ENTITY", "NOTATION", "SYSTEM", "PUBLIC", "NDATA", "REQUIRED", "IMPLIED", "FIXED", + "EMPTY", "ANY", "PCDATA", 0, 0, 0, 0, "CDATA", "ID", "IDREF", + "IDREFS", "ENTITIES", "NMTOKEN", "NMTOKENS", " class QXmlStreamSimpleStack { + T *data; + int tos, cap; +public: + inline QXmlStreamSimpleStack():data(0), tos(-1), cap(0){} + inline ~QXmlStreamSimpleStack(){ if (data) free(data); } + + inline void reserve(int extraCapacity) { + if (tos + extraCapacity + 1 > cap) { + cap = qMax(tos + extraCapacity + 1, cap << 1 ); + data = reinterpret_cast(realloc(data, cap * sizeof(T))); + Q_CHECK_PTR(data); + } + } + + inline T &push() { reserve(1); return data[++tos]; } + inline T &rawPush() { return data[++tos]; } + inline const T &top() const { return data[tos]; } + inline T &top() { return data[tos]; } + inline T &pop() { return data[tos--]; } + inline T &operator[](int index) { return data[index]; } + inline const T &at(int index) const { return data[index]; } + inline int size() const { return tos + 1; } + inline void resize(int s) { tos = s - 1; } + inline bool isEmpty() const { return tos < 0; } + inline void clear() { tos = -1; } +}; + + +class QXmlStream +{ + Q_DECLARE_TR_FUNCTIONS(QXmlStream) +}; + +class QXmlStreamPrivateTagStack { +public: + struct NamespaceDeclaration + { + QStringRef prefix; + QStringRef namespaceUri; + }; + + struct Tag + { + QStringRef name; + QStringRef qualifiedName; + NamespaceDeclaration namespaceDeclaration; + int tagStackStringStorageSize; + int namespaceDeclarationsSize; + }; + + + QXmlStreamPrivateTagStack(); + QXmlStreamSimpleStack namespaceDeclarations; + QString tagStackStringStorage; + int tagStackStringStorageSize; + int initialTagStackStringStorageSize; + bool tagsDone; + + inline QStringRef addToStringStorage(const QStringRef &s) { + return addToStringStorage(qToStringViewIgnoringNull(s)); + } + inline QStringRef addToStringStorage(const QString &s) { + return addToStringStorage(qToStringViewIgnoringNull(s)); + } + QStringRef addToStringStorage(QStringView s) + { + int pos = tagStackStringStorageSize; + int sz = s.size(); + if (pos != tagStackStringStorage.size()) + tagStackStringStorage.resize(pos); + tagStackStringStorage.append(s.data(), sz); + tagStackStringStorageSize += sz; + return QStringRef(&tagStackStringStorage, pos, sz); + } + + QXmlStreamSimpleStack tagStack; + + + inline Tag &tagStack_pop() { + Tag& tag = tagStack.pop(); + tagStackStringStorageSize = tag.tagStackStringStorageSize; + namespaceDeclarations.resize(tag.namespaceDeclarationsSize); + tagsDone = tagStack.isEmpty(); + return tag; + } + inline Tag &tagStack_push() { + Tag &tag = tagStack.push(); + tag.tagStackStringStorageSize = tagStackStringStorageSize; + tag.namespaceDeclarationsSize = namespaceDeclarations.size(); + return tag; + } +}; + + +class QXmlStreamEntityResolver; +#ifndef QT_NO_XMLSTREAMREADER +class QXmlStreamReaderPrivate : public QXmlStreamReader_Table, public QXmlStreamPrivateTagStack{ + QXmlStreamReader *q_ptr; + Q_DECLARE_PUBLIC(QXmlStreamReader) +public: + QXmlStreamReaderPrivate(QXmlStreamReader *q); + ~QXmlStreamReaderPrivate(); + void init(); + + QByteArray rawReadBuffer; + QByteArray dataBuffer; + uchar firstByte; + qint64 nbytesread; + QString readBuffer; + int readBufferPos; + QXmlStreamSimpleStack putStack; + struct Entity { + Entity() = default; + Entity(const QString &name, const QString &value) + : name(name), value(value), external(false), unparsed(false), literal(false), + hasBeenParsed(false), isCurrentlyReferenced(false){} + static inline Entity createLiteral(QLatin1String name, QLatin1String value) + { Entity result(name, value); result.literal = result.hasBeenParsed = true; return result; } + QString name, value; + uint external : 1; + uint unparsed : 1; + uint literal : 1; + uint hasBeenParsed : 1; + uint isCurrentlyReferenced : 1; + }; + // these hash tables use a QStringView as a key to avoid creating QStrings + // just for lookup. The keys are usually views into Entity::name and thus + // are guaranteed to have the same lifetime as the referenced data: + QHash entityHash; + QHash parameterEntityHash; + QXmlStreamSimpleStackentityReferenceStack; + inline bool referenceEntity(Entity &entity) { + if (entity.isCurrentlyReferenced) { + raiseWellFormedError(QXmlStream::tr("Recursive entity detected.")); + return false; + } + entity.isCurrentlyReferenced = true; + entityReferenceStack.push() = &entity; + injectToken(ENTITY_DONE); + return true; + } + + + QIODevice *device; + bool deleteDevice; +#ifndef QT_NO_TEXTCODEC + QTextCodec *codec; + QTextDecoder *decoder; +#endif + bool atEnd; + + /*! + \sa setType() + */ + QXmlStreamReader::TokenType type; + QXmlStreamReader::Error error; + QString errorString; + QString unresolvedEntity; + + qint64 lineNumber, lastLineStart, characterOffset; + + + void write(const QString &); + void write(const char *); + + + QXmlStreamAttributes attributes; + QStringRef namespaceForPrefix(const QStringRef &prefix); + void resolveTag(); + void resolvePublicNamespaces(); + void resolveDtd(); + uint resolveCharRef(int symbolIndex); + bool checkStartDocument(); + void startDocument(); + void parseError(); + void checkPublicLiteral(const QStringRef &publicId); + + bool scanDtd; + QStringRef lastAttributeValue; + bool lastAttributeIsCData; + struct DtdAttribute { + QStringRef tagName; + QStringRef attributeQualifiedName; + QStringRef attributePrefix; + QStringRef attributeName; + QStringRef defaultValue; + bool isCDATA; + bool isNamespaceAttribute; + }; + QXmlStreamSimpleStack dtdAttributes; + struct NotationDeclaration { + QStringRef name; + QStringRef publicId; + QStringRef systemId; + }; + QXmlStreamSimpleStack notationDeclarations; + QXmlStreamNotationDeclarations publicNotationDeclarations; + QXmlStreamNamespaceDeclarations publicNamespaceDeclarations; + + struct EntityDeclaration { + QStringRef name; + QStringRef notationName; + QStringRef publicId; + QStringRef systemId; + QStringRef value; + bool parameter; + bool external; + inline void clear() { + name.clear(); + notationName.clear(); + publicId.clear(); + systemId.clear(); + value.clear(); + parameter = external = false; + } + }; + QXmlStreamSimpleStack entityDeclarations; + QXmlStreamEntityDeclarations publicEntityDeclarations; + + QStringRef text; + + QStringRef prefix, namespaceUri, qualifiedName, name; + QStringRef processingInstructionTarget, processingInstructionData; + QStringRef dtdName, dtdPublicId, dtdSystemId; + QStringRef documentVersion, documentEncoding; + uint isEmptyElement : 1; + uint isWhitespace : 1; + uint isCDATA : 1; + uint standalone : 1; + uint hasCheckedStartDocument : 1; + uint normalizeLiterals : 1; + uint hasSeenTag : 1; + uint inParseEntity : 1; + uint referenceToUnparsedEntityDetected : 1; + uint referenceToParameterEntityDetected : 1; + uint hasExternalDtdSubset : 1; + uint lockEncoding : 1; + uint namespaceProcessing : 1; + + int resumeReduction; + void resume(int rule); + + inline bool entitiesMustBeDeclared() const { + return (!inParseEntity + && (standalone + || (!referenceToUnparsedEntityDetected + && !referenceToParameterEntityDetected // Errata 13 as of 2006-04-25 + && !hasExternalDtdSubset))); + } + + // qlalr parser + int tos; + int stack_size; + struct Value { + int pos; + int len; + int prefix; + ushort c; + }; + + Value *sym_stack; + int *state_stack; + inline void reallocateStack(); + inline Value &sym(int index) const + { return sym_stack[tos + index - 1]; } + QString textBuffer; + inline void clearTextBuffer() { + if (!scanDtd) { + textBuffer.resize(0); + textBuffer.reserve(256); + } + } + struct Attribute { + Value key; + Value value; + }; + QXmlStreamSimpleStack attributeStack; + + inline QStringRef symString(int index) { + const Value &symbol = sym(index); + return QStringRef(&textBuffer, symbol.pos + symbol.prefix, symbol.len - symbol.prefix); + } + QStringView symView(int index) const + { + const Value &symbol = sym(index); + return QStringView(textBuffer.data() + symbol.pos, symbol.len).mid(symbol.prefix); + } + inline QStringRef symName(int index) { + const Value &symbol = sym(index); + return QStringRef(&textBuffer, symbol.pos, symbol.len); + } + inline QStringRef symString(int index, int offset) { + const Value &symbol = sym(index); + return QStringRef(&textBuffer, symbol.pos + symbol.prefix + offset, symbol.len - symbol.prefix - offset); + } + inline QStringRef symPrefix(int index) { + const Value &symbol = sym(index); + if (symbol.prefix) + return QStringRef(&textBuffer, symbol.pos, symbol.prefix - 1); + return QStringRef(); + } + inline QStringRef symString(const Value &symbol) { + return QStringRef(&textBuffer, symbol.pos + symbol.prefix, symbol.len - symbol.prefix); + } + inline QStringRef symName(const Value &symbol) { + return QStringRef(&textBuffer, symbol.pos, symbol.len); + } + inline QStringRef symPrefix(const Value &symbol) { + if (symbol.prefix) + return QStringRef(&textBuffer, symbol.pos, symbol.prefix - 1); + return QStringRef(); + } + + inline void clearSym() { Value &val = sym(1); val.pos = textBuffer.size(); val.len = 0; } + + + short token; + uint token_char; + + uint filterCarriageReturn(); + inline uint getChar(); + inline uint peekChar(); + inline void putChar(uint c) { putStack.push() = c; } + inline void putChar(QChar c) { putStack.push() = c.unicode(); } + void putString(const QString &s, int from = 0); + void putStringLiteral(const QString &s); + void putReplacement(const QString &s); + void putReplacementInAttributeValue(const QString &s); + uint getChar_helper(); + + bool scanUntil(const char *str, short tokenToInject = -1); + bool scanString(const char *str, short tokenToInject, bool requireSpace = true); + inline void injectToken(ushort tokenToInject) { + putChar(int(tokenToInject) << 16); + } + + QString resolveUndeclaredEntity(const QString &name); + void parseEntity(const QString &value); + QXmlStreamReaderPrivate *entityParser; + + bool scanAfterLangleBang(); + bool scanPublicOrSystem(); + bool scanNData(); + bool scanAfterDefaultDecl(); + bool scanAttType(); + + + // scan optimization functions. Not strictly necessary but LALR is + // not very well suited for scanning fast + int fastScanLiteralContent(); + int fastScanSpace(); + int fastScanContentCharList(); + int fastScanName(int *prefix = 0); + inline int fastScanNMTOKEN(); + + + bool parse(); + inline void consumeRule(int); + + void raiseError(QXmlStreamReader::Error error, const QString& message = QString()); + void raiseWellFormedError(const QString &message); + + QXmlStreamEntityResolver *entityResolver; + +private: + /*! \internal + Never assign to variable type directly. Instead use this function. + + This prevents errors from being ignored. + */ + inline void setType(const QXmlStreamReader::TokenType t) + { + if(type != QXmlStreamReader::Invalid) + type = t; + } +}; + +bool QXmlStreamReaderPrivate::parse() +{ + // cleanup currently reported token + + switch (type) { + case QXmlStreamReader::StartElement: + name.clear(); + prefix.clear(); + qualifiedName.clear(); + namespaceUri.clear(); + publicNamespaceDeclarations.clear(); + attributes.clear(); + if (isEmptyElement) { + setType(QXmlStreamReader::EndElement); + Tag &tag = tagStack_pop(); + namespaceUri = tag.namespaceDeclaration.namespaceUri; + name = tag.name; + qualifiedName = tag.qualifiedName; + isEmptyElement = false; + return true; + } + clearTextBuffer(); + break; + case QXmlStreamReader::EndElement: + name.clear(); + prefix.clear(); + qualifiedName.clear(); + namespaceUri.clear(); + clearTextBuffer(); + break; + case QXmlStreamReader::DTD: + publicNotationDeclarations.clear(); + publicEntityDeclarations.clear(); + dtdName.clear(); + dtdPublicId.clear(); + dtdSystemId.clear(); + Q_FALLTHROUGH(); + case QXmlStreamReader::Comment: + case QXmlStreamReader::Characters: + isCDATA = false; + isWhitespace = true; + text.clear(); + clearTextBuffer(); + break; + case QXmlStreamReader::EntityReference: + text.clear(); + name.clear(); + clearTextBuffer(); + break; + case QXmlStreamReader::ProcessingInstruction: + processingInstructionTarget.clear(); + processingInstructionData.clear(); + clearTextBuffer(); + break; + case QXmlStreamReader::NoToken: + case QXmlStreamReader::Invalid: + break; + case QXmlStreamReader::StartDocument: + lockEncoding = true; + documentVersion.clear(); + documentEncoding.clear(); +#ifndef QT_NO_TEXTCODEC + if (decoder && decoder->hasFailure()) { + raiseWellFormedError(QXmlStream::tr("Encountered incorrectly encoded content.")); + readBuffer.clear(); + return false; + } +#endif + Q_FALLTHROUGH(); + default: + clearTextBuffer(); + ; + } + + setType(QXmlStreamReader::NoToken); + + + // the main parse loop + int act, r; + + if (resumeReduction) { + act = state_stack[tos-1]; + r = resumeReduction; + resumeReduction = 0; + goto ResumeReduction; + } + + act = state_stack[tos]; + + forever { + if (token == -1 && - TERMINAL_COUNT != action_index[act]) { + uint cu = getChar(); + token = NOTOKEN; + token_char = cu == ~0U ? cu : ushort(cu); + if ((cu != ~0U) && (cu & 0xff0000)) { + token = cu >> 16; + } else switch (token_char) { + case 0xfffe: + case 0xffff: + token = ERROR; + break; + case '\r': + token = SPACE; + if (cu == '\r') { + if ((token_char = filterCarriageReturn())) { + ++lineNumber; + lastLineStart = characterOffset + readBufferPos; + break; + } + } else { + break; + } + Q_FALLTHROUGH(); + case ~0U: { + token = EOF_SYMBOL; + if (!tagsDone && !inParseEntity) { + int a = t_action(act, token); + if (a < 0) { + raiseError(QXmlStreamReader::PrematureEndOfDocumentError); + return false; + } + } + + } break; + case '\n': + ++lineNumber; + lastLineStart = characterOffset + readBufferPos; + Q_FALLTHROUGH(); + case ' ': + case '\t': + token = SPACE; + break; + case '&': + token = AMPERSAND; + break; + case '#': + token = HASH; + break; + case '\'': + token = QUOTE; + break; + case '\"': + token = DBLQUOTE; + break; + case '<': + token = LANGLE; + break; + case '>': + token = RANGLE; + break; + case '[': + token = LBRACK; + break; + case ']': + token = RBRACK; + break; + case '(': + token = LPAREN; + break; + case ')': + token = RPAREN; + break; + case '|': + token = PIPE; + break; + case '=': + token = EQ; + break; + case '%': + token = PERCENT; + break; + case '/': + token = SLASH; + break; + case ':': + token = COLON; + break; + case ';': + token = SEMICOLON; + break; + case ',': + token = COMMA; + break; + case '-': + token = DASH; + break; + case '+': + token = PLUS; + break; + case '*': + token = STAR; + break; + case '.': + token = DOT; + break; + case '?': + token = QUESTIONMARK; + break; + case '!': + token = BANG; + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + token = DIGIT; + break; + default: + if (cu < 0x20) + token = NOTOKEN; + else + token = LETTER; + break; + } + } + + act = t_action (act, token); + if (act == ACCEPT_STATE) { + // reset the parser in case someone resumes (process instructions can follow a valid document) + tos = 0; + state_stack[tos++] = 0; + state_stack[tos] = 0; + return true; + } else if (act > 0) { + if (++tos == stack_size-1) + reallocateStack(); + + Value &val = sym_stack[tos]; + val.c = token_char; + val.pos = textBuffer.size(); + val.prefix = 0; + val.len = 1; + if (token_char) + textBuffer += QChar(token_char); + + state_stack[tos] = act; + token = -1; + + + } else if (act < 0) { + r = - act - 1; + +#if defined (QLALR_DEBUG) + int ridx = rule_index[r]; + printf ("%3d) %s ::=", r + 1, spell[rule_info[ridx]]); + ++ridx; + for (int i = ridx; i < ridx + rhs[r]; ++i) { + int symbol = rule_info[i]; + if (const char *name = spell[symbol]) + printf (" %s", name); + else + printf (" #%d", symbol); + } + printf ("\n"); +#endif + + tos -= rhs[r]; + act = state_stack[tos++]; + ResumeReduction: + switch (r) { + + case 0: + setType(QXmlStreamReader::EndDocument); + break; + + case 1: + if (type != QXmlStreamReader::Invalid) { + if (hasSeenTag || inParseEntity) { + setType(QXmlStreamReader::EndDocument); + } else { + raiseError(QXmlStreamReader::NotWellFormedError, QXmlStream::tr("Start tag expected.")); + // reset the parser + tos = 0; + state_stack[tos++] = 0; + state_stack[tos] = 0; + return false; + } + } + break; + + case 10: + entityReferenceStack.pop()->isCurrentlyReferenced = false; + clearSym(); + break; + + case 11: + if (!scanString(spell[VERSION], VERSION, false) && atEnd) { + resume(11); + return false; + } + break; + + case 12: + setType(QXmlStreamReader::StartDocument); + documentVersion = symString(6); + startDocument(); + break; + + case 13: + hasExternalDtdSubset = true; + dtdSystemId = symString(2); + break; + + case 14: + checkPublicLiteral(symString(2)); + dtdPublicId = symString(2); + dtdSystemId = symString(4); + hasExternalDtdSubset = true; + break; + + case 16: + if (!scanPublicOrSystem() && atEnd) { + resume(16); + return false; + } + dtdName = symString(3); + break; + + case 17: + case 18: + dtdName = symString(3); + Q_FALLTHROUGH(); + + case 19: + case 20: + setType(QXmlStreamReader::DTD); + text = &textBuffer; + break; + + case 21: + scanDtd = true; + break; + + case 22: + scanDtd = false; + break; + + case 37: + if (!scanString(spell[EMPTY], EMPTY, false) + && !scanString(spell[ANY], ANY, false) + && atEnd) { + resume(37); + return false; + } + break; + + case 43: + if (!scanString(spell[PCDATA], PCDATA, false) && atEnd) { + resume(43); + return false; + } + break; + + case 68: { + lastAttributeIsCData = true; + } break; + + case 78: + if (!scanAfterDefaultDecl() && atEnd) { + resume(78); + return false; + } + break; + + case 83: + sym(1) = sym(2); + lastAttributeValue.clear(); + lastAttributeIsCData = false; + if (!scanAttType() && atEnd) { + resume(83); + return false; + } + break; + + case 84: { + DtdAttribute &dtdAttribute = dtdAttributes.push(); + dtdAttribute.tagName.clear(); + dtdAttribute.isCDATA = lastAttributeIsCData; + dtdAttribute.attributePrefix = addToStringStorage(symPrefix(1)); + dtdAttribute.attributeName = addToStringStorage(symString(1)); + dtdAttribute.attributeQualifiedName = addToStringStorage(symName(1)); + dtdAttribute.isNamespaceAttribute = (dtdAttribute.attributePrefix == QLatin1String("xmlns") + || (dtdAttribute.attributePrefix.isEmpty() + && dtdAttribute.attributeName == QLatin1String("xmlns"))); + if (lastAttributeValue.isNull()) { + dtdAttribute.defaultValue.clear(); + } else { + if (dtdAttribute.isCDATA) + dtdAttribute.defaultValue = addToStringStorage(lastAttributeValue); + else + dtdAttribute.defaultValue = addToStringStorage(lastAttributeValue.toString().simplified()); + + } + } break; + + case 88: { + if (referenceToUnparsedEntityDetected && !standalone) + break; + int n = dtdAttributes.size(); + QStringRef tagName = addToStringStorage(symName(3)); + while (n--) { + DtdAttribute &dtdAttribute = dtdAttributes[n]; + if (!dtdAttribute.tagName.isNull()) + break; + dtdAttribute.tagName = tagName; + for (int i = 0; i < n; ++i) { + if ((dtdAttributes[i].tagName.isNull() || dtdAttributes[i].tagName == tagName) + && dtdAttributes[i].attributeQualifiedName == dtdAttribute.attributeQualifiedName) { + dtdAttribute.attributeQualifiedName.clear(); // redefined, delete it + break; + } + } + } + } break; + + case 89: { + if (!scanPublicOrSystem() && atEnd) { + resume(89); + return false; + } + EntityDeclaration &entityDeclaration = entityDeclarations.push(); + entityDeclaration.clear(); + entityDeclaration.name = symString(3); + } break; + + case 90: { + if (!scanPublicOrSystem() && atEnd) { + resume(90); + return false; + } + EntityDeclaration &entityDeclaration = entityDeclarations.push(); + entityDeclaration.clear(); + entityDeclaration.name = symString(5); + entityDeclaration.parameter = true; + } break; + + case 91: { + if (!scanNData() && atEnd) { + resume(91); + return false; + } + EntityDeclaration &entityDeclaration = entityDeclarations.top(); + entityDeclaration.systemId = symString(3); + entityDeclaration.external = true; + } break; + + case 92: { + if (!scanNData() && atEnd) { + resume(92); + return false; + } + EntityDeclaration &entityDeclaration = entityDeclarations.top(); + checkPublicLiteral((entityDeclaration.publicId = symString(3))); + entityDeclaration.systemId = symString(5); + entityDeclaration.external = true; + } break; + + case 93: { + EntityDeclaration &entityDeclaration = entityDeclarations.top(); + entityDeclaration.notationName = symString(3); + if (entityDeclaration.parameter) + raiseWellFormedError(QXmlStream::tr("NDATA in parameter entity declaration.")); + } + Q_FALLTHROUGH(); + + case 94: + case 95: { + if (referenceToUnparsedEntityDetected && !standalone) { + entityDeclarations.pop(); + break; + } + EntityDeclaration &entityDeclaration = entityDeclarations.top(); + if (!entityDeclaration.external) + entityDeclaration.value = symString(2); + auto &hash = entityDeclaration.parameter ? parameterEntityHash : entityHash; + if (!hash.contains(qToStringViewIgnoringNull(entityDeclaration.name))) { + Entity entity(entityDeclaration.name.toString(), + entityDeclaration.value.toString()); + entity.unparsed = (!entityDeclaration.notationName.isNull()); + entity.external = entityDeclaration.external; + hash.insert(qToStringViewIgnoringNull(entity.name), entity); + } + } break; + + case 96: { + setType(QXmlStreamReader::ProcessingInstruction); + int pos = sym(4).pos + sym(4).len; + processingInstructionTarget = symString(3); + if (scanUntil("?>")) { + processingInstructionData = QStringRef(&textBuffer, pos, textBuffer.size() - pos - 2); + if (!processingInstructionTarget.compare(QLatin1String("xml"), Qt::CaseInsensitive)) { + raiseWellFormedError(QXmlStream::tr("XML declaration not at start of document.")); + } + else if (!QXmlUtils::isNCName(processingInstructionTarget)) + raiseWellFormedError(QXmlStream::tr("%1 is an invalid processing instruction name.") + .arg(processingInstructionTarget)); + } else if (type != QXmlStreamReader::Invalid){ + resume(96); + return false; + } + } break; + + case 97: + setType(QXmlStreamReader::ProcessingInstruction); + processingInstructionTarget = symString(3); + if (!processingInstructionTarget.compare(QLatin1String("xml"), Qt::CaseInsensitive)) + raiseWellFormedError(QXmlStream::tr("Invalid processing instruction name.")); + break; + + case 98: + if (!scanAfterLangleBang() && atEnd) { + resume(98); + return false; + } + break; + + case 99: + if (!scanUntil("--")) { + resume(99); + return false; + } + break; + + case 100: { + setType(QXmlStreamReader::Comment); + int pos = sym(1).pos + 4; + text = QStringRef(&textBuffer, pos, textBuffer.size() - pos - 3); + } break; + + case 101: { + setType(QXmlStreamReader::Characters); + isCDATA = true; + isWhitespace = false; + int pos = sym(2).pos; + if (scanUntil("]]>", -1)) { + text = QStringRef(&textBuffer, pos, textBuffer.size() - pos - 3); + } else { + resume(101); + return false; + } + } break; + + case 102: { + if (!scanPublicOrSystem() && atEnd) { + resume(102); + return false; + } + NotationDeclaration ¬ationDeclaration = notationDeclarations.push(); + notationDeclaration.name = symString(3); + } break; + + case 103: { + NotationDeclaration ¬ationDeclaration = notationDeclarations.top(); + notationDeclaration.systemId = symString(3); + notationDeclaration.publicId.clear(); + } break; + + case 104: { + NotationDeclaration ¬ationDeclaration = notationDeclarations.top(); + notationDeclaration.systemId.clear(); + checkPublicLiteral((notationDeclaration.publicId = symString(3))); + } break; + + case 105: { + NotationDeclaration ¬ationDeclaration = notationDeclarations.top(); + checkPublicLiteral((notationDeclaration.publicId = symString(3))); + notationDeclaration.systemId = symString(5); + } break; + + case 129: + isWhitespace = false; + Q_FALLTHROUGH(); + + case 130: + sym(1).len += fastScanContentCharList(); + if (atEnd && !inParseEntity) { + resume(130); + return false; + } + break; + + case 139: + if (!textBuffer.isEmpty()) { + setType(QXmlStreamReader::Characters); + text = &textBuffer; + } + break; + + case 140: + case 141: + clearSym(); + break; + + case 142: + case 143: + sym(1) = sym(2); + break; + + case 144: + case 145: + case 146: + case 147: + sym(1).len += sym(2).len; + break; + + case 173: + if (normalizeLiterals) + textBuffer.data()[textBuffer.size()-1] = QLatin1Char(' '); + break; + + case 174: + sym(1).len += fastScanLiteralContent(); + if (atEnd) { + resume(174); + return false; + } + break; + + case 175: { + if (!QXmlUtils::isPublicID(symString(1))) { + raiseWellFormedError(QXmlStream::tr("%1 is an invalid PUBLIC identifier.").arg(symString(1))); + resume(175); + return false; + } + } break; + + case 176: + case 177: + clearSym(); + break; + + case 178: + case 179: + sym(1) = sym(2); + break; + + case 180: + case 181: + case 182: + case 183: + sym(1).len += sym(2).len; + break; + + case 213: + case 214: + clearSym(); + break; + + case 215: + case 216: + sym(1) = sym(2); + lastAttributeValue = symString(1); + break; + + case 217: + case 218: + case 219: + case 220: + sym(1).len += sym(2).len; + break; + + case 229: { + QStringRef prefix = symPrefix(1); + if (prefix.isEmpty() && symString(1) == QLatin1String("xmlns") && namespaceProcessing) { + NamespaceDeclaration &namespaceDeclaration = namespaceDeclarations.push(); + namespaceDeclaration.prefix.clear(); + + const QStringRef ns(symString(5)); + if(ns == QLatin1String("http://www.w3.org/2000/xmlns/") || + ns == QLatin1String("http://www.w3.org/XML/1998/namespace")) + raiseWellFormedError(QXmlStream::tr("Illegal namespace declaration.")); + else + namespaceDeclaration.namespaceUri = addToStringStorage(ns); + } else { + Attribute &attribute = attributeStack.push(); + attribute.key = sym(1); + attribute.value = sym(5); + + QStringRef attributeQualifiedName = symName(1); + bool normalize = false; + for (int a = 0; a < dtdAttributes.size(); ++a) { + DtdAttribute &dtdAttribute = dtdAttributes[a]; + if (!dtdAttribute.isCDATA + && dtdAttribute.tagName == qualifiedName + && dtdAttribute.attributeQualifiedName == attributeQualifiedName + ) { + normalize = true; + break; + } + } + if (normalize) { + // normalize attribute value (simplify and trim) + int pos = textBuffer.size(); + int n = 0; + bool wasSpace = true; + for (int i = 0; i < attribute.value.len; ++i) { + QChar c = textBuffer.at(attribute.value.pos + i); + if (c.unicode() == ' ') { + if (wasSpace) + continue; + wasSpace = true; + } else { + wasSpace = false; + } + textBuffer += textBuffer.at(attribute.value.pos + i); + ++n; + } + if (wasSpace) + while (n && textBuffer.at(pos + n - 1).unicode() == ' ') + --n; + attribute.value.pos = pos; + attribute.value.len = n; + } + if (prefix == QLatin1String("xmlns") && namespaceProcessing) { + NamespaceDeclaration &namespaceDeclaration = namespaceDeclarations.push(); + QStringRef namespacePrefix = symString(attribute.key); + QStringRef namespaceUri = symString(attribute.value); + attributeStack.pop(); + if (((namespacePrefix == QLatin1String("xml")) + ^ (namespaceUri == QLatin1String("http://www.w3.org/XML/1998/namespace"))) + || namespaceUri == QLatin1String("http://www.w3.org/2000/xmlns/") + || namespaceUri.isEmpty() + || namespacePrefix == QLatin1String("xmlns")) + raiseWellFormedError(QXmlStream::tr("Illegal namespace declaration.")); + + namespaceDeclaration.prefix = addToStringStorage(namespacePrefix); + namespaceDeclaration.namespaceUri = addToStringStorage(namespaceUri); + } + } + } break; + + case 235: { + normalizeLiterals = true; + Tag &tag = tagStack_push(); + prefix = tag.namespaceDeclaration.prefix = addToStringStorage(symPrefix(2)); + name = tag.name = addToStringStorage(symString(2)); + qualifiedName = tag.qualifiedName = addToStringStorage(symName(2)); + if ((!prefix.isEmpty() && !QXmlUtils::isNCName(prefix)) || !QXmlUtils::isNCName(name)) + raiseWellFormedError(QXmlStream::tr("Invalid XML name.")); + } break; + + case 236: + isEmptyElement = true; + Q_FALLTHROUGH(); + + case 237: + setType(QXmlStreamReader::StartElement); + resolveTag(); + if (tagStack.size() == 1 && hasSeenTag && !inParseEntity) + raiseWellFormedError(QXmlStream::tr("Extra content at end of document.")); + hasSeenTag = true; + break; + + case 238: { + setType(QXmlStreamReader::EndElement); + Tag &tag = tagStack_pop(); + + namespaceUri = tag.namespaceDeclaration.namespaceUri; + name = tag.name; + qualifiedName = tag.qualifiedName; + if (qualifiedName != symName(3)) + raiseWellFormedError(QXmlStream::tr("Opening and ending tag mismatch.")); + } break; + + case 239: + if (entitiesMustBeDeclared()) { + raiseWellFormedError(QXmlStream::tr("Entity '%1' not declared.").arg(unresolvedEntity)); + break; + } + setType(QXmlStreamReader::EntityReference); + name = &unresolvedEntity; + break; + + case 240: { + sym(1).len += sym(2).len + 1; + QStringView reference = symView(2); + if (entityHash.contains(reference)) { + Entity &entity = entityHash[reference]; + if (entity.unparsed) { + raiseWellFormedError(QXmlStream::tr("Reference to unparsed entity '%1'.").arg(reference)); + } else { + if (!entity.hasBeenParsed) { + parseEntity(entity.value); + entity.hasBeenParsed = true; + } + if (entity.literal) + putStringLiteral(entity.value); + else if (referenceEntity(entity)) + putReplacement(entity.value); + textBuffer.chop(2 + sym(2).len); + clearSym(); + } + break; + } + + if (entityResolver) { + QString replacementText = resolveUndeclaredEntity(reference.toString()); + if (!replacementText.isNull()) { + putReplacement(replacementText); + textBuffer.chop(2 + sym(2).len); + clearSym(); + break; + } + } + + injectToken(UNRESOLVED_ENTITY); + unresolvedEntity = symString(2).toString(); + textBuffer.chop(2 + sym(2).len); + clearSym(); + + } break; + + case 241: { + sym(1).len += sym(2).len + 1; + QStringView reference = symView(2); + if (parameterEntityHash.contains(reference)) { + referenceToParameterEntityDetected = true; + Entity &entity = parameterEntityHash[reference]; + if (entity.unparsed || entity.external) { + referenceToUnparsedEntityDetected = true; + } else { + if (referenceEntity(entity)) + putString(entity.value); + textBuffer.chop(2 + sym(2).len); + clearSym(); + } + } else if (entitiesMustBeDeclared()) { + raiseWellFormedError(QXmlStream::tr("Entity '%1' not declared.").arg(symString(2))); + } + } break; + + case 242: + sym(1).len += sym(2).len + 1; + break; + + case 243: { + sym(1).len += sym(2).len + 1; + QStringView reference = symView(2); + if (entityHash.contains(reference)) { + Entity &entity = entityHash[reference]; + if (entity.unparsed || entity.value.isNull()) { + raiseWellFormedError(QXmlStream::tr("Reference to external entity '%1' in attribute value.").arg(reference)); + break; + } + if (!entity.hasBeenParsed) { + parseEntity(entity.value); + entity.hasBeenParsed = true; + } + if (entity.literal) + putStringLiteral(entity.value); + else if (referenceEntity(entity)) + putReplacementInAttributeValue(entity.value); + textBuffer.chop(2 + sym(2).len); + clearSym(); + break; + } + + if (entityResolver) { + QString replacementText = resolveUndeclaredEntity(reference.toString()); + if (!replacementText.isNull()) { + putReplacement(replacementText); + textBuffer.chop(2 + sym(2).len); + clearSym(); + break; + } + } + if (entitiesMustBeDeclared()) { + raiseWellFormedError(QXmlStream::tr("Entity '%1' not declared.").arg(reference)); + } + } break; + + case 244: { + if (uint s = resolveCharRef(3)) { + if (s >= 0xffff) + putStringLiteral(QString::fromUcs4(&s, 1)); + else + putChar((LETTER << 16) | s); + + textBuffer.chop(3 + sym(3).len); + clearSym(); + } else { + raiseWellFormedError(QXmlStream::tr("Invalid character reference.")); + } + } break; + + case 247: + case 248: + sym(1).len += sym(2).len; + break; + + case 259: + sym(1).len += fastScanSpace(); + if (atEnd) { + resume(259); + return false; + } + break; + + case 262: { + sym(1).len += fastScanName(&sym(1).prefix); + if (atEnd) { + resume(262); + return false; + } + } break; + + case 263: + sym(1).len += fastScanName(); + if (atEnd) { + resume(263); + return false; + } + break; + + case 264: + case 265: + case 266: + case 267: + case 268: + sym(1).len += fastScanNMTOKEN(); + if (atEnd) { + resume(268); + return false; + } + + break; + + default: + ; + } // switch + act = state_stack[tos] = nt_action (act, lhs[r] - TERMINAL_COUNT); + if (type != QXmlStreamReader::NoToken) + return true; + } else { + parseError(); + break; + } + } + return false; +} +#endif //QT_NO_XMLSTREAMREADER.xml + + +#endif // QXMLSTREAM_P_H + diff --git a/src/corelib/serialization/qxmlutils.cpp b/src/corelib/serialization/qxmlutils.cpp new file mode 100644 index 0000000000..01c84251fd --- /dev/null +++ b/src/corelib/serialization/qxmlutils.cpp @@ -0,0 +1,390 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#include "qxmlutils_p.h" + +QT_BEGIN_NAMESPACE + +/* TODO: + * - isNameChar() doesn't have to be public, it's only needed in + * qdom.cpp -- refactor fixedXmlName() to use isNCName() + * - A lot of functions can be inlined. + */ + +class QXmlCharRange +{ +public: + ushort min; + ushort max; +}; +typedef const QXmlCharRange *RangeIter; + +/*! + Performs a binary search between \a begin and \a end inclusive, to check whether \a + c is contained. Remember that the QXmlCharRange instances must be in numeric order. + */ +bool QXmlUtils::rangeContains(RangeIter begin, RangeIter end, const QChar c) +{ + const ushort cp(c.unicode()); + + // check the first two ranges "manually" as characters in that + // range are checked very often and we avoid the binary search below. + + if (cp <= begin->max) + return cp >= begin->min; + + ++begin; + + if (begin == end) + return false; + + if (cp <= begin->max) + return cp >= begin->min; + + while (begin != end) { + int delta = (end - begin) / 2; + RangeIter mid = begin + delta; + + if (mid->min > cp) + end = mid; + else if (mid->max < cp) + begin = mid; + else + return true; + + if (delta == 0) + break; + } + + return false; +} + +// [85] BaseChar ::= ... + +static const QXmlCharRange g_base_begin[] = +{ + {0x0041, 0x005A}, {0x0061, 0x007A}, {0x00C0, 0x00D6}, {0x00D8, 0x00F6}, {0x00F8, 0x00FF}, + {0x0100, 0x0131}, {0x0134, 0x013E}, {0x0141, 0x0148}, {0x014A, 0x017E}, {0x0180, 0x01C3}, + {0x01CD, 0x01F0}, {0x01F4, 0x01F5}, {0x01FA, 0x0217}, {0x0250, 0x02A8}, {0x02BB, 0x02C1}, + {0x0386, 0x0386}, {0x0388, 0x038A}, {0x038C, 0x038C}, {0x038E, 0x03A1}, {0x03A3, 0x03CE}, + {0x03D0, 0x03D6}, {0x03DA, 0x03DA}, {0x03DC, 0x03DC}, {0x03DE, 0x03DE}, {0x03E0, 0x03E0}, + {0x03E2, 0x03F3}, {0x0401, 0x040C}, {0x040E, 0x044F}, {0x0451, 0x045C}, {0x045E, 0x0481}, + {0x0490, 0x04C4}, {0x04C7, 0x04C8}, {0x04CB, 0x04CC}, {0x04D0, 0x04EB}, {0x04EE, 0x04F5}, + {0x04F8, 0x04F9}, {0x0531, 0x0556}, {0x0559, 0x0559}, {0x0561, 0x0586}, {0x05D0, 0x05EA}, + {0x05F0, 0x05F2}, {0x0621, 0x063A}, {0x0641, 0x064A}, {0x0671, 0x06B7}, {0x06BA, 0x06BE}, + {0x06C0, 0x06CE}, {0x06D0, 0x06D3}, {0x06D5, 0x06D5}, {0x06E5, 0x06E6}, {0x0905, 0x0939}, + {0x093D, 0x093D}, {0x0958, 0x0961}, {0x0985, 0x098C}, {0x098F, 0x0990}, {0x0993, 0x09A8}, + {0x09AA, 0x09B0}, {0x09B2, 0x09B2}, {0x09B6, 0x09B9}, {0x09DC, 0x09DD}, {0x09DF, 0x09E1}, + {0x09F0, 0x09F1}, {0x0A05, 0x0A0A}, {0x0A0F, 0x0A10}, {0x0A13, 0x0A28}, {0x0A2A, 0x0A30}, + {0x0A32, 0x0A33}, {0x0A35, 0x0A36}, {0x0A38, 0x0A39}, {0x0A59, 0x0A5C}, {0x0A5E, 0x0A5E}, + {0x0A72, 0x0A74}, {0x0A85, 0x0A8B}, {0x0A8D, 0x0A8D}, {0x0A8F, 0x0A91}, {0x0A93, 0x0AA8}, + {0x0AAA, 0x0AB0}, {0x0AB2, 0x0AB3}, {0x0AB5, 0x0AB9}, {0x0ABD, 0x0ABD}, {0x0AE0, 0x0AE0}, + {0x0B05, 0x0B0C}, {0x0B0F, 0x0B10}, {0x0B13, 0x0B28}, {0x0B2A, 0x0B30}, {0x0B32, 0x0B33}, + {0x0B36, 0x0B39}, {0x0B3D, 0x0B3D}, {0x0B5C, 0x0B5D}, {0x0B5F, 0x0B61}, {0x0B85, 0x0B8A}, + {0x0B8E, 0x0B90}, {0x0B92, 0x0B95}, {0x0B99, 0x0B9A}, {0x0B9C, 0x0B9C}, {0x0B9E, 0x0B9F}, + {0x0BA3, 0x0BA4}, {0x0BA8, 0x0BAA}, {0x0BAE, 0x0BB5}, {0x0BB7, 0x0BB9}, {0x0C05, 0x0C0C}, + {0x0C0E, 0x0C10}, {0x0C12, 0x0C28}, {0x0C2A, 0x0C33}, {0x0C35, 0x0C39}, {0x0C60, 0x0C61}, + {0x0C85, 0x0C8C}, {0x0C8E, 0x0C90}, {0x0C92, 0x0CA8}, {0x0CAA, 0x0CB3}, {0x0CB5, 0x0CB9}, + {0x0CDE, 0x0CDE}, {0x0CE0, 0x0CE1}, {0x0D05, 0x0D0C}, {0x0D0E, 0x0D10}, {0x0D12, 0x0D28}, + {0x0D2A, 0x0D39}, {0x0D60, 0x0D61}, {0x0E01, 0x0E2E}, {0x0E30, 0x0E30}, {0x0E32, 0x0E33}, + {0x0E40, 0x0E45}, {0x0E81, 0x0E82}, {0x0E84, 0x0E84}, {0x0E87, 0x0E88}, {0x0E8A, 0x0E8A}, + {0x0E8D, 0x0E8D}, {0x0E94, 0x0E97}, {0x0E99, 0x0E9F}, {0x0EA1, 0x0EA3}, {0x0EA5, 0x0EA5}, + {0x0EA7, 0x0EA7}, {0x0EAA, 0x0EAB}, {0x0EAD, 0x0EAE}, {0x0EB0, 0x0EB0}, {0x0EB2, 0x0EB3}, + {0x0EBD, 0x0EBD}, {0x0EC0, 0x0EC4}, {0x0F40, 0x0F47}, {0x0F49, 0x0F69}, {0x10A0, 0x10C5}, + {0x10D0, 0x10F6}, {0x1100, 0x1100}, {0x1102, 0x1103}, {0x1105, 0x1107}, {0x1109, 0x1109}, + {0x110B, 0x110C}, {0x110E, 0x1112}, {0x113C, 0x113C}, {0x113E, 0x113E}, {0x1140, 0x1140}, + {0x114C, 0x114C}, {0x114E, 0x114E}, {0x1150, 0x1150}, {0x1154, 0x1155}, {0x1159, 0x1159}, + {0x115F, 0x1161}, {0x1163, 0x1163}, {0x1165, 0x1165}, {0x1167, 0x1167}, {0x1169, 0x1169}, + {0x116D, 0x116E}, {0x1172, 0x1173}, {0x1175, 0x1175}, {0x119E, 0x119E}, {0x11A8, 0x11A8}, + {0x11AB, 0x11AB}, {0x11AE, 0x11AF}, {0x11B7, 0x11B8}, {0x11BA, 0x11BA}, {0x11BC, 0x11C2}, + {0x11EB, 0x11EB}, {0x11F0, 0x11F0}, {0x11F9, 0x11F9}, {0x1E00, 0x1E9B}, {0x1EA0, 0x1EF9}, + {0x1F00, 0x1F15}, {0x1F18, 0x1F1D}, {0x1F20, 0x1F45}, {0x1F48, 0x1F4D}, {0x1F50, 0x1F57}, + {0x1F59, 0x1F59}, {0x1F5B, 0x1F5B}, {0x1F5D, 0x1F5D}, {0x1F5F, 0x1F7D}, {0x1F80, 0x1FB4}, + {0x1FB6, 0x1FBC}, {0x1FBE, 0x1FBE}, {0x1FC2, 0x1FC4}, {0x1FC6, 0x1FCC}, {0x1FD0, 0x1FD3}, + {0x1FD6, 0x1FDB}, {0x1FE0, 0x1FEC}, {0x1FF2, 0x1FF4}, {0x1FF6, 0x1FFC}, {0x2126, 0x2126}, + {0x212A, 0x212B}, {0x212E, 0x212E}, {0x2180, 0x2182}, {0x3041, 0x3094}, {0x30A1, 0x30FA}, + {0x3105, 0x312C}, {0xAC00, 0xD7A3} +}; +static const RangeIter g_base_end = g_base_begin + sizeof(g_base_begin) / sizeof(QXmlCharRange); + +static const QXmlCharRange g_ideographic_begin[] = +{ + {0x3007, 0x3007}, {0x3021, 0x3029}, {0x4E00, 0x9FA5} +}; +static const RangeIter g_ideographic_end = g_ideographic_begin + sizeof(g_ideographic_begin) / sizeof(QXmlCharRange); + +bool QXmlUtils::isIdeographic(const QChar c) +{ + return rangeContains(g_ideographic_begin, g_ideographic_end, c); +} + +static const QXmlCharRange g_combining_begin[] = +{ + {0x0300, 0x0345}, {0x0360, 0x0361}, {0x0483, 0x0486}, {0x0591, 0x05A1}, {0x05A3, 0x05B9}, + {0x05BB, 0x05BD}, {0x05BF, 0x05BF}, {0x05C1, 0x05C2}, {0x05C4, 0x05C4}, {0x064B, 0x0652}, + {0x0670, 0x0670}, {0x06D6, 0x06DC}, {0x06DD, 0x06DF}, {0x06E0, 0x06E4}, {0x06E7, 0x06E8}, + {0x06EA, 0x06ED}, {0x0901, 0x0903}, {0x093C, 0x093C}, {0x093E, 0x094C}, {0x094D, 0x094D}, + {0x0951, 0x0954}, {0x0962, 0x0963}, {0x0981, 0x0983}, {0x09BC, 0x09BC}, {0x09BE, 0x09BE}, + {0x09BF, 0x09BF}, {0x09C0, 0x09C4}, {0x09C7, 0x09C8}, {0x09CB, 0x09CD}, {0x09D7, 0x09D7}, + {0x09E2, 0x09E3}, {0x0A02, 0x0A02}, {0x0A3C, 0x0A3C}, {0x0A3E, 0x0A3E}, {0x0A3F, 0x0A3F}, + {0x0A40, 0x0A42}, {0x0A47, 0x0A48}, {0x0A4B, 0x0A4D}, {0x0A70, 0x0A71}, {0x0A81, 0x0A83}, + {0x0ABC, 0x0ABC}, {0x0ABE, 0x0AC5}, {0x0AC7, 0x0AC9}, {0x0ACB, 0x0ACD}, {0x0B01, 0x0B03}, + {0x0B3C, 0x0B3C}, {0x0B3E, 0x0B43}, {0x0B47, 0x0B48}, {0x0B4B, 0x0B4D}, {0x0B56, 0x0B57}, + {0x0B82, 0x0B83}, {0x0BBE, 0x0BC2}, {0x0BC6, 0x0BC8}, {0x0BCA, 0x0BCD}, {0x0BD7, 0x0BD7}, + {0x0C01, 0x0C03}, {0x0C3E, 0x0C44}, {0x0C46, 0x0C48}, {0x0C4A, 0x0C4D}, {0x0C55, 0x0C56}, + {0x0C82, 0x0C83}, {0x0CBE, 0x0CC4}, {0x0CC6, 0x0CC8}, {0x0CCA, 0x0CCD}, {0x0CD5, 0x0CD6}, + {0x0D02, 0x0D03}, {0x0D3E, 0x0D43}, {0x0D46, 0x0D48}, {0x0D4A, 0x0D4D}, {0x0D57, 0x0D57}, + {0x0E31, 0x0E31}, {0x0E34, 0x0E3A}, {0x0E47, 0x0E4E}, {0x0EB1, 0x0EB1}, {0x0EB4, 0x0EB9}, + {0x0EBB, 0x0EBC}, {0x0EC8, 0x0ECD}, {0x0F18, 0x0F19}, {0x0F35, 0x0F35}, {0x0F37, 0x0F37}, + {0x0F39, 0x0F39}, {0x0F3E, 0x0F3E}, {0x0F3F, 0x0F3F}, {0x0F71, 0x0F84}, {0x0F86, 0x0F8B}, + {0x0F90, 0x0F95}, {0x0F97, 0x0F97}, {0x0F99, 0x0FAD}, {0x0FB1, 0x0FB7}, {0x0FB9, 0x0FB9}, + {0x20D0, 0x20DC}, {0x20E1, 0x20E1}, {0x302A, 0x302F}, {0x3099, 0x3099}, {0x309A, 0x309A} +}; +static const RangeIter g_combining_end = g_combining_begin + sizeof(g_combining_begin) / sizeof(QXmlCharRange); + +bool QXmlUtils::isCombiningChar(const QChar c) +{ + return rangeContains(g_combining_begin, g_combining_end, c); +} + +// [88] Digit ::= ... +static const QXmlCharRange g_digit_begin[] = +{ + {0x0030, 0x0039}, {0x0660, 0x0669}, {0x06F0, 0x06F9}, {0x0966, 0x096F}, {0x09E6, 0x09EF}, + {0x0A66, 0x0A6F}, {0x0AE6, 0x0AEF}, {0x0B66, 0x0B6F}, {0x0BE7, 0x0BEF}, {0x0C66, 0x0C6F}, + {0x0CE6, 0x0CEF}, {0x0D66, 0x0D6F}, {0x0E50, 0x0E59}, {0x0ED0, 0x0ED9}, {0x0F20, 0x0F29} +}; +static const RangeIter g_digit_end = g_digit_begin + sizeof(g_digit_begin) / sizeof(QXmlCharRange); + +bool QXmlUtils::isDigit(const QChar c) +{ + return rangeContains(g_digit_begin, g_digit_end, c); +} + +// [89] Extender ::= ... +static const QXmlCharRange g_extender_begin[] = +{ + {0x00B7, 0x00B7}, {0x02D0, 0x02D0}, {0x02D1, 0x02D1}, {0x0387, 0x0387}, {0x0640, 0x0640}, + {0x0E46, 0x0E46}, {0x0EC6, 0x0EC6}, {0x3005, 0x3005}, {0x3031, 0x3035}, {0x309D, 0x309E}, + {0x30FC, 0x30FE} +}; +static const RangeIter g_extender_end = g_extender_begin + sizeof(g_extender_begin) / sizeof(QXmlCharRange); + +bool QXmlUtils::isExtender(const QChar c) +{ + return rangeContains(g_extender_begin, g_extender_end, c); +} + +bool QXmlUtils::isBaseChar(const QChar c) +{ + return rangeContains(g_base_begin, g_base_end, c); +} + +/*! + \internal + + Determines whether \a encName is a valid instance of production [81]EncName in the XML 1.0 + specification. If it is, true is returned, otherwise false. + + \sa {http://www.w3.org/TR/REC-xml/#NT-EncName}, + {Extensible Markup Language (XML) 1.0 (Fourth Edition), [81] EncName} + */ +bool QXmlUtils::isEncName(QStringView encName) +{ + // Valid encoding names are given by "[A-Za-z][A-Za-z0-9._\\-]*" + if (encName.isEmpty()) + return false; + const auto first = encName.front().unicode(); + if (!((first >= 'a' && first <= 'z') || (first >= 'A' && first <= 'Z'))) + return false; + for (QChar ch : encName.mid(1)) { + const auto cp = ch.unicode(); + if ((cp >= 'a' && cp <= 'z') + || (cp >= 'A' && cp <= 'Z') + || (cp >= '0' && cp <= '9') + || cp == '.' || cp == '_' || cp == '-') { + continue; + } + return false; + } + return true; +} + +/*! + \internal + + Determines whether \a c is a valid instance of production [84]Letter in the XML 1.0 + specification. If it is, true is returned, otherwise false. + + \sa {http://www.w3.org/TR/REC-xml/#NT-Letter}, + {Extensible Markup Language (XML) 1.0 (Fourth Edition), [84] Letter} + */ +bool QXmlUtils::isLetter(const QChar c) +{ + return isBaseChar(c) || isIdeographic(c); +} + +/*! + \internal + + Determines whether \a c is a valid instance of production [2]Char in the XML 1.0 + specification. If it is, true is returned, otherwise false. + + \sa {http://www.w3.org/TR/REC-xml/#NT-Char}, + {Extensible Markup Language (XML) 1.0 (Fourth Edition), [2] Char} + */ +bool QXmlUtils::isChar(const QChar c) +{ + return (c.unicode() >= 0x0020 && c.unicode() <= 0xD7FF) + || c.unicode() == 0x0009 + || c.unicode() == 0x000A + || c.unicode() == 0x000D + || (c.unicode() >= 0xE000 && c.unicode() <= 0xFFFD); +} + +/*! + \internal + + Determines whether \a c is a valid instance of + production [4]NameChar in the XML 1.0 specification. If it + is, true is returned, otherwise false. + + \sa {http://www.w3.org/TR/REC-xml/#NT-NameChar}, + {Extensible Markup Language (XML) 1.0 (Fourth Edition), [4] NameChar} + */ +bool QXmlUtils::isNameChar(const QChar c) +{ + return isBaseChar(c) + || isDigit(c) + || c.unicode() == '.' + || c.unicode() == '-' + || c.unicode() == '_' + || c.unicode() == ':' + || isCombiningChar(c) + || isIdeographic(c) + || isExtender(c); +} + +/*! + \internal + + Determines whether \a c is a valid instance of + production [12] PubidLiteral in the XML 1.0 specification. If it + is, true is returned, otherwise false. + + \sa {http://www.w3.org/TR/REC-xml/#NT-PubidLiteral}, + {Extensible Markup Language (XML) 1.0 (Fourth Edition), [12] PubidLiteral} + */ +bool QXmlUtils::isPublicID(QStringView candidate) +{ + for (QChar ch : candidate) { + const ushort cp = ch.unicode(); + + if ((cp >= 'a' && cp <= 'z') + || (cp >= 'A' && cp <= 'Z') + || (cp >= '0' && cp <= '9')) + { + continue; + } + + switch (cp) + { + /* Fallthrough all these. */ + case 0x20: + case 0x0D: + case 0x0A: + case '-': + case '\'': + case '(': + case ')': + case '+': + case ',': + case '.': + case '/': + case ':': + case '=': + case '?': + case ';': + case '!': + case '*': + case '#': + case '@': + case '$': + case '_': + case '%': + continue; + default: + return false; + } + } + + return true; +} + +/*! + \internal + + Determines whether \a c is a valid instance of + production [4]NCName in the XML 1.0 Namespaces specification. If it + is, true is returned, otherwise false. + + \sa {http://www.w3.org/TR/REC-xml-names/#NT-NCName}, + {W3CNamespaces in XML 1.0 (Second Edition), [4] NCName} + */ +bool QXmlUtils::isNCName(QStringView ncName) +{ + if(ncName.isEmpty()) + return false; + + const QChar first(ncName.at(0)); + + if(!QXmlUtils::isLetter(first) && first.unicode() != '_' && first.unicode() != ':') + return false; + + for (QChar at : ncName) { + if(!QXmlUtils::isNameChar(at) || at == QLatin1Char(':')) + return false; + } + + return true; +} + +QT_END_NAMESPACE diff --git a/src/corelib/serialization/qxmlutils_p.h b/src/corelib/serialization/qxmlutils_p.h new file mode 100644 index 0000000000..db6bddd5be --- /dev/null +++ b/src/corelib/serialization/qxmlutils_p.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QXMLUTILS_P_H +#define QXMLUTILS_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of qapplication_*.cpp, qwidget*.cpp and qfiledialog.cpp. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#include +#include + +QT_BEGIN_NAMESPACE + +class QString; +class QChar; +class QXmlCharRange; + +/*! + \internal + \short This class contains helper functions related to XML, for validating character classes, + productions in the XML specification, and so on. + */ +class Q_CORE_EXPORT QXmlUtils +{ +public: + static bool isEncName(QStringView encName); + static bool isChar(const QChar c); + static bool isNameChar(const QChar c); + static bool isLetter(const QChar c); + static bool isNCName(QStringView ncName); + static bool isPublicID(QStringView candidate); + +private: + typedef const QXmlCharRange *RangeIter; + static bool rangeContains(RangeIter begin, RangeIter end, const QChar c); + static bool isBaseChar(const QChar c); + static bool isDigit(const QChar c); + static bool isExtender(const QChar c); + static bool isIdeographic(const QChar c); + static bool isCombiningChar(const QChar c); +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/corelib/serialization/serialization.pri b/src/corelib/serialization/serialization.pri new file mode 100644 index 0000000000..3d039dc30f --- /dev/null +++ b/src/corelib/serialization/serialization.pri @@ -0,0 +1,30 @@ +# Qt data formats core module + +HEADERS += \ + serialization/qdatastream.h \ + serialization/qdatastream_p.h \ + serialization/qjson_p.h \ + serialization/qjsondocument.h \ + serialization/qjsonobject.h \ + serialization/qjsonvalue.h \ + serialization/qjsonarray.h \ + serialization/qjsonwriter_p.h \ + serialization/qjsonparser_p.h \ + serialization/qtextstream.h \ + serialization/qtextstream_p.h \ + serialization/qxmlstream.h \ + serialization/qxmlstream_p.h \ + serialization/qxmlutils_p.h + +SOURCES += \ + serialization/qdatastream.cpp \ + serialization/qjson.cpp \ + serialization/qjsondocument.cpp \ + serialization/qjsonobject.cpp \ + serialization/qjsonarray.cpp \ + serialization/qjsonvalue.cpp \ + serialization/qjsonwriter.cpp \ + serialization/qjsonparser.cpp \ + serialization/qtextstream.cpp \ + serialization/qxmlstream.cpp \ + serialization/qxmlutils.cpp diff --git a/src/corelib/xml/.gitignore b/src/corelib/xml/.gitignore deleted file mode 100644 index 89f9ac04aa..0000000000 --- a/src/corelib/xml/.gitignore +++ /dev/null @@ -1 +0,0 @@ -out/ diff --git a/src/corelib/xml/make-parser.sh b/src/corelib/xml/make-parser.sh deleted file mode 100755 index 0296e4c22b..0000000000 --- a/src/corelib/xml/make-parser.sh +++ /dev/null @@ -1,51 +0,0 @@ -#!/bin/sh -############################################################################# -## -## Copyright (C) 2016 The Qt Company Ltd. -## Contact: https://www.qt.io/licensing/ -## -## This file is the build configuration utility of the Qt Toolkit. -## -## $QT_BEGIN_LICENSE:LGPL$ -## Commercial License Usage -## Licensees holding valid commercial Qt licenses may use this file in -## accordance with the commercial license agreement provided with the -## Software or, alternatively, in accordance with the terms contained in -## a written agreement between you and The Qt Company. For licensing terms -## and conditions see https://www.qt.io/terms-conditions. For further -## information use the contact form at https://www.qt.io/contact-us. -## -## GNU Lesser General Public License Usage -## Alternatively, this file may be used under the terms of the GNU Lesser -## General Public License version 3 as published by the Free Software -## Foundation and appearing in the file LICENSE.LGPL3 included in the -## packaging of this file. Please review the following information to -## ensure the GNU Lesser General Public License version 3 requirements -## will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -## -## GNU General Public License Usage -## Alternatively, this file may be used under the terms of the GNU -## General Public License version 2.0 or (at your option) the GNU General -## Public license version 3 or any later version approved by the KDE Free -## Qt Foundation. The licenses are as published by the Free Software -## Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -## included in the packaging of this file. Please review the following -## information to ensure the GNU General Public License requirements will -## be met: https://www.gnu.org/licenses/gpl-2.0.html and -## https://www.gnu.org/licenses/gpl-3.0.html. -## -## $QT_END_LICENSE$ -## -############################################################################# - -me=$(dirname $0) -mkdir -p $me/out -(cd $me/out && ../../../../util/qlalr/qlalr --qt --no-debug --no-lines ../qxmlstream.g) - -for f in $me/out/*.h; do - n=$(basename $f) - cp $f $n -done - -git diff . - diff --git a/src/corelib/xml/qxmlstream.cpp b/src/corelib/xml/qxmlstream.cpp deleted file mode 100644 index 9b5295a17e..0000000000 --- a/src/corelib/xml/qxmlstream.cpp +++ /dev/null @@ -1,4014 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtCore module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "QtCore/qxmlstream.h" - -#ifndef QT_NO_XMLSTREAM - -#include "qxmlutils_p.h" -#include -#include -#include -#include -#include -#include -#ifndef QT_BOOTSTRAPPED -#include -#else -// This specialization of Q_DECLARE_TR_FUNCTIONS is not in qcoreapplication.h, -// because that header depends on QObject being available, which is not the -// case for most bootstrapped applications. -#define Q_DECLARE_TR_FUNCTIONS(context) \ -public: \ - static inline QString tr(const char *sourceText, const char *comment = 0) \ - { Q_UNUSED(comment); return QString::fromLatin1(sourceText); } \ - static inline QString trUtf8(const char *sourceText, const char *comment = 0) \ - { Q_UNUSED(comment); return QString::fromLatin1(sourceText); } \ - static inline QString tr(const char *sourceText, const char*, int) \ - { return QString::fromLatin1(sourceText); } \ - static inline QString trUtf8(const char *sourceText, const char*, int) \ - { return QString::fromLatin1(sourceText); } \ -private: -#endif -QT_BEGIN_NAMESPACE - -#include "qxmlstream_p.h" - -enum { StreamEOF = ~0U }; - -/*! - \enum QXmlStreamReader::TokenType - - This enum specifies the type of token the reader just read. - - \value NoToken The reader has not yet read anything. - - \value Invalid An error has occurred, reported in error() and - errorString(). - - \value StartDocument The reader reports the XML version number in - documentVersion(), and the encoding as specified in the XML - document in documentEncoding(). If the document is declared - standalone, isStandaloneDocument() returns \c true; otherwise it - returns \c false. - - \value EndDocument The reader reports the end of the document. - - \value StartElement The reader reports the start of an element - with namespaceUri() and name(). Empty elements are also reported - as StartElement, followed directly by EndElement. The convenience - function readElementText() can be called to concatenate all - content until the corresponding EndElement. Attributes are - reported in attributes(), namespace declarations in - namespaceDeclarations(). - - \value EndElement The reader reports the end of an element with - namespaceUri() and name(). - - \value Characters The reader reports characters in text(). If the - characters are all white-space, isWhitespace() returns \c true. If - the characters stem from a CDATA section, isCDATA() returns \c true. - - \value Comment The reader reports a comment in text(). - - \value DTD The reader reports a DTD in text(), notation - declarations in notationDeclarations(), and entity declarations in - entityDeclarations(). Details of the DTD declaration are reported - in in dtdName(), dtdPublicId(), and dtdSystemId(). - - \value EntityReference The reader reports an entity reference that - could not be resolved. The name of the reference is reported in - name(), the replacement text in text(). - - \value ProcessingInstruction The reader reports a processing - instruction in processingInstructionTarget() and - processingInstructionData(). -*/ - -/*! - \enum QXmlStreamReader::ReadElementTextBehaviour - - This enum specifies the different behaviours of readElementText(). - - \value ErrorOnUnexpectedElement Raise an UnexpectedElementError and return - what was read so far when a child element is encountered. - - \value IncludeChildElements Recursively include the text from child elements. - - \value SkipChildElements Skip child elements. - - \since 4.6 -*/ - -/*! - \enum QXmlStreamReader::Error - - This enum specifies different error cases - - \value NoError No error has occurred. - - \value CustomError A custom error has been raised with - raiseError() - - \value NotWellFormedError The parser internally raised an error - due to the read XML not being well-formed. - - \value PrematureEndOfDocumentError The input stream ended before a - well-formed XML document was parsed. Recovery from this error is - possible if more XML arrives in the stream, either by calling - addData() or by waiting for it to arrive on the device(). - - \value UnexpectedElementError The parser encountered an element - that was different to those it expected. - -*/ - -/*! - \class QXmlStreamEntityResolver - \inmodule QtCore - \reentrant - \since 4.4 - - \brief The QXmlStreamEntityResolver class provides an entity - resolver for a QXmlStreamReader. - - \ingroup xml-tools - */ - -/*! - Destroys the entity resolver. - */ -QXmlStreamEntityResolver::~QXmlStreamEntityResolver() -{ -} - -/*! - \internal - -This function is a stub for later functionality. -*/ -QString QXmlStreamEntityResolver::resolveEntity(const QString& /*publicId*/, const QString& /*systemId*/) -{ - return QString(); -} - - -/*! - Resolves the undeclared entity \a name and returns its replacement - text. If the entity is also unknown to the entity resolver, it - returns an empty string. - - The default implementation always returns an empty string. -*/ - -QString QXmlStreamEntityResolver::resolveUndeclaredEntity(const QString &/*name*/) -{ - return QString(); -} - -#ifndef QT_NO_XMLSTREAMREADER - -QString QXmlStreamReaderPrivate::resolveUndeclaredEntity(const QString &name) -{ - if (entityResolver) - return entityResolver->resolveUndeclaredEntity(name); - return QString(); -} - - - -/*! - \since 4.4 - - Makes \a resolver the new entityResolver(). - - The stream reader does \e not take ownership of the resolver. It's - the callers responsibility to ensure that the resolver is valid - during the entire life-time of the stream reader object, or until - another resolver or 0 is set. - - \sa entityResolver() - */ -void QXmlStreamReader::setEntityResolver(QXmlStreamEntityResolver *resolver) -{ - Q_D(QXmlStreamReader); - d->entityResolver = resolver; -} - -/*! - \since 4.4 - - Returns the entity resolver, or 0 if there is no entity resolver. - - \sa setEntityResolver() - */ -QXmlStreamEntityResolver *QXmlStreamReader::entityResolver() const -{ - Q_D(const QXmlStreamReader); - return d->entityResolver; -} - - - -/*! - \class QXmlStreamReader - \inmodule QtCore - \reentrant - \since 4.3 - - \brief The QXmlStreamReader class provides a fast parser for reading - well-formed XML via a simple streaming API. - - - \ingroup xml-tools - - QXmlStreamReader is a faster and more convenient replacement for - Qt's own SAX parser (see QXmlSimpleReader). In some cases it might - also be a faster and more convenient alternative for use in - applications that would otherwise use a DOM tree (see QDomDocument). - QXmlStreamReader reads data either from a QIODevice (see - setDevice()), or from a raw QByteArray (see addData()). - - Qt provides QXmlStreamWriter for writing XML. - - The basic concept of a stream reader is to report an XML document as - a stream of tokens, similar to SAX. The main difference between - QXmlStreamReader and SAX is \e how these XML tokens are reported. - With SAX, the application must provide handlers (callback functions) - that receive so-called XML \e events from the parser at the parser's - convenience. With QXmlStreamReader, the application code itself - drives the loop and pulls \e tokens from the reader, one after - another, as it needs them. This is done by calling readNext(), where - the reader reads from the input stream until it completes the next - token, at which point it returns the tokenType(). A set of - convenient functions including isStartElement() and text() can then - be used to examine the token to obtain information about what has - been read. The big advantage of this \e pulling approach is the - possibility to build recursive descent parsers with it, meaning you - can split your XML parsing code easily into different methods or - classes. This makes it easy to keep track of the application's own - state when parsing XML. - - A typical loop with QXmlStreamReader looks like this: - - \snippet code/src_corelib_xml_qxmlstream.cpp 0 - - - QXmlStreamReader is a well-formed XML 1.0 parser that does \e not - include external parsed entities. As long as no error occurs, the - application code can thus be assured that the data provided by the - stream reader satisfies the W3C's criteria for well-formed XML. For - example, you can be certain that all tags are indeed nested and - closed properly, that references to internal entities have been - replaced with the correct replacement text, and that attributes have - been normalized or added according to the internal subset of the - DTD. - - If an error occurs while parsing, atEnd() and hasError() return - true, and error() returns the error that occurred. The functions - errorString(), lineNumber(), columnNumber(), and characterOffset() - are for constructing an appropriate error or warning message. To - simplify application code, QXmlStreamReader contains a raiseError() - mechanism that lets you raise custom errors that trigger the same - error handling described. - - The \l{QXmlStream Bookmarks Example} illustrates how to use the - recursive descent technique to read an XML bookmark file (XBEL) with - a stream reader. - - \section1 Namespaces - - QXmlStream understands and resolves XML namespaces. E.g. in case of - a StartElement, namespaceUri() returns the namespace the element is - in, and name() returns the element's \e local name. The combination - of namespaceUri and name uniquely identifies an element. If a - namespace prefix was not declared in the XML entities parsed by the - reader, the namespaceUri is empty. - - If you parse XML data that does not utilize namespaces according to - the XML specification or doesn't use namespaces at all, you can use - the element's qualifiedName() instead. A qualified name is the - element's prefix() followed by colon followed by the element's local - name() - exactly like the element appears in the raw XML data. Since - the mapping namespaceUri to prefix is neither unique nor universal, - qualifiedName() should be avoided for namespace-compliant XML data. - - In order to parse standalone documents that do use undeclared - namespace prefixes, you can turn off namespace processing completely - with the \l namespaceProcessing property. - - \section1 Incremental Parsing - - QXmlStreamReader is an incremental parser. It can handle the case - where the document can't be parsed all at once because it arrives in - chunks (e.g. from multiple files, or over a network connection). - When the reader runs out of data before the complete document has - been parsed, it reports a PrematureEndOfDocumentError. When more - data arrives, either because of a call to addData() or because more - data is available through the network device(), the reader recovers - from the PrematureEndOfDocumentError error and continues parsing the - new data with the next call to readNext(). - - For example, if your application reads data from the network using a - \l{QNetworkAccessManager} {network access manager}, you would issue - a \l{QNetworkRequest} {network request} to the manager and receive a - \l{QNetworkReply} {network reply} in return. Since a QNetworkReply - is a QIODevice, you connect its \l{QIODevice::readyRead()} - {readyRead()} signal to a custom slot, e.g. \c{slotReadyRead()} in - the code snippet shown in the discussion for QNetworkAccessManager. - In this slot, you read all available data with - \l{QIODevice::readAll()} {readAll()} and pass it to the XML - stream reader using addData(). Then you call your custom parsing - function that reads the XML events from the reader. - - \section1 Performance and Memory Consumption - - QXmlStreamReader is memory-conservative by design, since it doesn't - store the entire XML document tree in memory, but only the current - token at the time it is reported. In addition, QXmlStreamReader - avoids the many small string allocations that it normally takes to - map an XML document to a convenient and Qt-ish API. It does this by - reporting all string data as QStringRef rather than real QString - objects. QStringRef is a thin wrapper around QString substrings that - provides a subset of the QString API without the memory allocation - and reference-counting overhead. Calling - \l{QStringRef::toString()}{toString()} on any of those objects - returns an equivalent real QString object. - -*/ - - -/*! - Constructs a stream reader. - - \sa setDevice(), addData() - */ -QXmlStreamReader::QXmlStreamReader() - : d_ptr(new QXmlStreamReaderPrivate(this)) -{ -} - -/*! Creates a new stream reader that reads from \a device. - -\sa setDevice(), clear() - */ -QXmlStreamReader::QXmlStreamReader(QIODevice *device) - : d_ptr(new QXmlStreamReaderPrivate(this)) -{ - setDevice(device); -} - -/*! - Creates a new stream reader that reads from \a data. - - \sa addData(), clear(), setDevice() - */ -QXmlStreamReader::QXmlStreamReader(const QByteArray &data) - : d_ptr(new QXmlStreamReaderPrivate(this)) -{ - Q_D(QXmlStreamReader); - d->dataBuffer = data; -} - -/*! - Creates a new stream reader that reads from \a data. - - \sa addData(), clear(), setDevice() - */ -QXmlStreamReader::QXmlStreamReader(const QString &data) - : d_ptr(new QXmlStreamReaderPrivate(this)) -{ - Q_D(QXmlStreamReader); -#ifdef QT_NO_TEXTCODEC - d->dataBuffer = data.toLatin1(); -#else - d->dataBuffer = d->codec->fromUnicode(data); - d->decoder = d->codec->makeDecoder(); -#endif - d->lockEncoding = true; - -} - -/*! - Creates a new stream reader that reads from \a data. - - \sa addData(), clear(), setDevice() - */ -QXmlStreamReader::QXmlStreamReader(const char *data) - : d_ptr(new QXmlStreamReaderPrivate(this)) -{ - Q_D(QXmlStreamReader); - d->dataBuffer = QByteArray(data); -} - -/*! - Destructs the reader. - */ -QXmlStreamReader::~QXmlStreamReader() -{ - Q_D(QXmlStreamReader); - if (d->deleteDevice) - delete d->device; -} - -/*! \fn bool QXmlStreamReader::hasError() const - Returns \c true if an error has occurred, otherwise \c false. - - \sa errorString(), error() - */ - -/*! - Sets the current device to \a device. Setting the device resets - the stream to its initial state. - - \sa device(), clear() -*/ -void QXmlStreamReader::setDevice(QIODevice *device) -{ - Q_D(QXmlStreamReader); - if (d->deleteDevice) { - delete d->device; - d->deleteDevice = false; - } - d->device = device; - d->init(); - -} - -/*! - Returns the current device associated with the QXmlStreamReader, - or 0 if no device has been assigned. - - \sa setDevice() -*/ -QIODevice *QXmlStreamReader::device() const -{ - Q_D(const QXmlStreamReader); - return d->device; -} - - -/*! - Adds more \a data for the reader to read. This function does - nothing if the reader has a device(). - - \sa readNext(), clear() - */ -void QXmlStreamReader::addData(const QByteArray &data) -{ - Q_D(QXmlStreamReader); - if (d->device) { - qWarning("QXmlStreamReader: addData() with device()"); - return; - } - d->dataBuffer += data; -} - -/*! - Adds more \a data for the reader to read. This function does - nothing if the reader has a device(). - - \sa readNext(), clear() - */ -void QXmlStreamReader::addData(const QString &data) -{ - Q_D(QXmlStreamReader); - d->lockEncoding = true; -#ifdef QT_NO_TEXTCODEC - addData(data.toLatin1()); -#else - addData(d->codec->fromUnicode(data)); -#endif -} - -/*! - Adds more \a data for the reader to read. This function does - nothing if the reader has a device(). - - \sa readNext(), clear() - */ -void QXmlStreamReader::addData(const char *data) -{ - addData(QByteArray(data)); -} - -/*! - Removes any device() or data from the reader and resets its - internal state to the initial state. - - \sa addData() - */ -void QXmlStreamReader::clear() -{ - Q_D(QXmlStreamReader); - d->init(); - if (d->device) { - if (d->deleteDevice) - delete d->device; - d->device = 0; - } -} - -/*! - Returns \c true if the reader has read until the end of the XML - document, or if an error() has occurred and reading has been - aborted. Otherwise, it returns \c false. - - When atEnd() and hasError() return true and error() returns - PrematureEndOfDocumentError, it means the XML has been well-formed - so far, but a complete XML document has not been parsed. The next - chunk of XML can be added with addData(), if the XML is being read - from a QByteArray, or by waiting for more data to arrive if the - XML is being read from a QIODevice. Either way, atEnd() will - return false once more data is available. - - \sa hasError(), error(), device(), QIODevice::atEnd() - */ -bool QXmlStreamReader::atEnd() const -{ - Q_D(const QXmlStreamReader); - if (d->atEnd - && ((d->type == QXmlStreamReader::Invalid && d->error == PrematureEndOfDocumentError) - || (d->type == QXmlStreamReader::EndDocument))) { - if (d->device) - return d->device->atEnd(); - else - return !d->dataBuffer.size(); - } - return (d->atEnd || d->type == QXmlStreamReader::Invalid); -} - - -/*! - Reads the next token and returns its type. - - With one exception, once an error() is reported by readNext(), - further reading of the XML stream is not possible. Then atEnd() - returns \c true, hasError() returns \c true, and this function returns - QXmlStreamReader::Invalid. - - The exception is when error() returns PrematureEndOfDocumentError. - This error is reported when the end of an otherwise well-formed - chunk of XML is reached, but the chunk doesn't represent a complete - XML document. In that case, parsing \e can be resumed by calling - addData() to add the next chunk of XML, when the stream is being - read from a QByteArray, or by waiting for more data to arrive when - the stream is being read from a device(). - - \sa tokenType(), tokenString() - */ -QXmlStreamReader::TokenType QXmlStreamReader::readNext() -{ - Q_D(QXmlStreamReader); - if (d->type != Invalid) { - if (!d->hasCheckedStartDocument) - if (!d->checkStartDocument()) - return d->type; // synthetic StartDocument or error - d->parse(); - if (d->atEnd && d->type != EndDocument && d->type != Invalid) - d->raiseError(PrematureEndOfDocumentError); - else if (!d->atEnd && d->type == EndDocument) - d->raiseWellFormedError(QXmlStream::tr("Extra content at end of document.")); - } else if (d->error == PrematureEndOfDocumentError) { - // resume error - d->type = NoToken; - d->atEnd = false; - d->token = -1; - return readNext(); - } - return d->type; -} - - -/*! - Returns the type of the current token. - - The current token can also be queried with the convenience functions - isStartDocument(), isEndDocument(), isStartElement(), - isEndElement(), isCharacters(), isComment(), isDTD(), - isEntityReference(), and isProcessingInstruction(). - - \sa tokenString() - */ -QXmlStreamReader::TokenType QXmlStreamReader::tokenType() const -{ - Q_D(const QXmlStreamReader); - return d->type; -} - -/*! - Reads until the next start element within the current element. Returns \c true - when a start element was reached. When the end element was reached, or when - an error occurred, false is returned. - - The current element is the element matching the most recently parsed start - element of which a matching end element has not yet been reached. When the - parser has reached the end element, the current element becomes the parent - element. - - This is a convenience function for when you're only concerned with parsing - XML elements. The \l{QXmlStream Bookmarks Example} makes extensive use of - this function. - - \since 4.6 - \sa readNext() - */ -bool QXmlStreamReader::readNextStartElement() -{ - while (readNext() != Invalid) { - if (isEndElement()) - return false; - else if (isStartElement()) - return true; - } - return false; -} - -/*! - Reads until the end of the current element, skipping any child nodes. - This function is useful for skipping unknown elements. - - The current element is the element matching the most recently parsed start - element of which a matching end element has not yet been reached. When the - parser has reached the end element, the current element becomes the parent - element. - - \since 4.6 - */ -void QXmlStreamReader::skipCurrentElement() -{ - int depth = 1; - while (depth && readNext() != Invalid) { - if (isEndElement()) - --depth; - else if (isStartElement()) - ++depth; - } -} - -/* - * Use the following Perl script to generate the error string index list: -===== PERL SCRIPT ==== -print "static const char QXmlStreamReader_tokenTypeString_string[] =\n"; -$counter = 0; -$i = 0; -while () { - chomp; - print " \"$_\\0\"\n"; - $sizes[$i++] = $counter; - $counter += length 1 + $_; -} -print " \"\\0\";\n\nstatic const short QXmlStreamReader_tokenTypeString_indices[] = {\n "; -for ($j = 0; $j < $i; ++$j) { - printf "$sizes[$j], "; -} -print "0\n};\n"; -===== PERL SCRIPT ==== - - * The input data is as follows (copied from qxmlstream.h): -NoToken -Invalid -StartDocument -EndDocument -StartElement -EndElement -Characters -Comment -DTD -EntityReference -ProcessingInstruction -*/ -static const char QXmlStreamReader_tokenTypeString_string[] = - "NoToken\0" - "Invalid\0" - "StartDocument\0" - "EndDocument\0" - "StartElement\0" - "EndElement\0" - "Characters\0" - "Comment\0" - "DTD\0" - "EntityReference\0" - "ProcessingInstruction\0"; - -static const short QXmlStreamReader_tokenTypeString_indices[] = { - 0, 8, 16, 30, 42, 55, 66, 77, 85, 89, 105, 0 -}; - - -/*! - \property QXmlStreamReader::namespaceProcessing - The namespace-processing flag of the stream reader - - This property controls whether or not the stream reader processes - namespaces. If enabled, the reader processes namespaces, otherwise - it does not. - - By default, namespace-processing is enabled. -*/ - - -void QXmlStreamReader::setNamespaceProcessing(bool enable) -{ - Q_D(QXmlStreamReader); - d->namespaceProcessing = enable; -} - -bool QXmlStreamReader::namespaceProcessing() const -{ - Q_D(const QXmlStreamReader); - return d->namespaceProcessing; -} - -/*! Returns the reader's current token as string. - -\sa tokenType() -*/ -QString QXmlStreamReader::tokenString() const -{ - Q_D(const QXmlStreamReader); - return QLatin1String(QXmlStreamReader_tokenTypeString_string + - QXmlStreamReader_tokenTypeString_indices[d->type]); -} - -#endif // QT_NO_XMLSTREAMREADER - -QXmlStreamPrivateTagStack::QXmlStreamPrivateTagStack() -{ - tagStack.reserve(16); - tagStackStringStorage.reserve(32); - tagStackStringStorageSize = 0; - NamespaceDeclaration &namespaceDeclaration = namespaceDeclarations.push(); - namespaceDeclaration.prefix = addToStringStorage(QStringViewLiteral("xml")); - namespaceDeclaration.namespaceUri = addToStringStorage(QStringViewLiteral("http://www.w3.org/XML/1998/namespace")); - initialTagStackStringStorageSize = tagStackStringStorageSize; -} - -#ifndef QT_NO_XMLSTREAMREADER - -QXmlStreamReaderPrivate::QXmlStreamReaderPrivate(QXmlStreamReader *q) - :q_ptr(q) -{ - device = 0; - deleteDevice = false; -#ifndef QT_NO_TEXTCODEC - decoder = 0; -#endif - stack_size = 64; - sym_stack = 0; - state_stack = 0; - reallocateStack(); - entityResolver = 0; - init(); -#define ADD_PREDEFINED(n, v) \ - do { \ - Entity e = Entity::createLiteral(QLatin1String(n), QLatin1String(v)); \ - entityHash.insert(qToStringViewIgnoringNull(e.name), std::move(e)); \ - } while (false) - ADD_PREDEFINED("lt", "<"); - ADD_PREDEFINED("gt", ">"); - ADD_PREDEFINED("amp", "&"); - ADD_PREDEFINED("apos", "'"); - ADD_PREDEFINED("quot", "\""); -#undef ADD_PREDEFINED -} - -void QXmlStreamReaderPrivate::init() -{ - scanDtd = false; - token = -1; - token_char = 0; - isEmptyElement = false; - isWhitespace = true; - isCDATA = false; - standalone = false; - tos = 0; - resumeReduction = 0; - state_stack[tos++] = 0; - state_stack[tos] = 0; - putStack.clear(); - putStack.reserve(32); - textBuffer.clear(); - textBuffer.reserve(256); - tagStack.clear(); - tagsDone = false; - attributes.clear(); - attributes.reserve(16); - lineNumber = lastLineStart = characterOffset = 0; - readBufferPos = 0; - nbytesread = 0; -#ifndef QT_NO_TEXTCODEC - codec = QTextCodec::codecForMib(106); // utf8 - delete decoder; - decoder = 0; -#endif - attributeStack.clear(); - attributeStack.reserve(16); - entityParser = 0; - hasCheckedStartDocument = false; - normalizeLiterals = false; - hasSeenTag = false; - atEnd = false; - inParseEntity = false; - referenceToUnparsedEntityDetected = false; - referenceToParameterEntityDetected = false; - hasExternalDtdSubset = false; - lockEncoding = false; - namespaceProcessing = true; - rawReadBuffer.clear(); - dataBuffer.clear(); - readBuffer.clear(); - tagStackStringStorageSize = initialTagStackStringStorageSize; - - type = QXmlStreamReader::NoToken; - error = QXmlStreamReader::NoError; -} - -/* - Well-formed requires that we verify entity values. We do this with a - standard parser. - */ -void QXmlStreamReaderPrivate::parseEntity(const QString &value) -{ - Q_Q(QXmlStreamReader); - - if (value.isEmpty()) - return; - - - if (!entityParser) - entityParser = new QXmlStreamReaderPrivate(q); - else - entityParser->init(); - entityParser->inParseEntity = true; - entityParser->readBuffer = value; - entityParser->injectToken(PARSE_ENTITY); - while (!entityParser->atEnd && entityParser->type != QXmlStreamReader::Invalid) - entityParser->parse(); - if (entityParser->type == QXmlStreamReader::Invalid || entityParser->tagStack.size()) - raiseWellFormedError(QXmlStream::tr("Invalid entity value.")); - -} - -inline void QXmlStreamReaderPrivate::reallocateStack() -{ - stack_size <<= 1; - sym_stack = reinterpret_cast (realloc(sym_stack, stack_size * sizeof(Value))); - Q_CHECK_PTR(sym_stack); - state_stack = reinterpret_cast (realloc(state_stack, stack_size * sizeof(int))); - Q_CHECK_PTR(state_stack); -} - - -QXmlStreamReaderPrivate::~QXmlStreamReaderPrivate() -{ -#ifndef QT_NO_TEXTCODEC - delete decoder; -#endif - free(sym_stack); - free(state_stack); - delete entityParser; -} - - -inline uint QXmlStreamReaderPrivate::filterCarriageReturn() -{ - uint peekc = peekChar(); - if (peekc == '\n') { - if (putStack.size()) - putStack.pop(); - else - ++readBufferPos; - return peekc; - } - if (peekc == StreamEOF) { - putChar('\r'); - return 0; - } - return '\n'; -} - -/*! - \internal - If the end of the file is encountered, ~0 is returned. - */ -inline uint QXmlStreamReaderPrivate::getChar() -{ - uint c; - if (putStack.size()) { - c = atEnd ? StreamEOF : putStack.pop(); - } else { - if (readBufferPos < readBuffer.size()) - c = readBuffer.at(readBufferPos++).unicode(); - else - c = getChar_helper(); - } - - return c; -} - -inline uint QXmlStreamReaderPrivate::peekChar() -{ - uint c; - if (putStack.size()) { - c = putStack.top(); - } else if (readBufferPos < readBuffer.size()) { - c = readBuffer.at(readBufferPos).unicode(); - } else { - if ((c = getChar_helper()) != StreamEOF) - --readBufferPos; - } - - return c; -} - -/*! - \internal - - Scans characters until \a str is encountered, and validates the characters - as according to the Char[2] production and do the line-ending normalization. - If any character is invalid, false is returned, otherwise true upon success. - - If \a tokenToInject is not less than zero, injectToken() is called with - \a tokenToInject when \a str is found. - - If any error occurred, false is returned, otherwise true. - */ -bool QXmlStreamReaderPrivate::scanUntil(const char *str, short tokenToInject) -{ - int pos = textBuffer.size(); - int oldLineNumber = lineNumber; - - uint c; - while ((c = getChar()) != StreamEOF) { - /* First, we do the validation & normalization. */ - switch (c) { - case '\r': - if ((c = filterCarriageReturn()) == 0) - break; - Q_FALLTHROUGH(); - case '\n': - ++lineNumber; - lastLineStart = characterOffset + readBufferPos; - Q_FALLTHROUGH(); - case '\t': - textBuffer += QChar(c); - continue; - default: - if (c < 0x20 || (c > 0xFFFD && c < 0x10000) || c > QChar::LastValidCodePoint ) { - raiseWellFormedError(QXmlStream::tr("Invalid XML character.")); - lineNumber = oldLineNumber; - return false; - } - textBuffer += QChar(c); - } - - - /* Second, attempt to lookup str. */ - if (c == uint(*str)) { - if (!*(str + 1)) { - if (tokenToInject >= 0) - injectToken(tokenToInject); - return true; - } else { - if (scanString(str + 1, tokenToInject, false)) - return true; - } - } - } - putString(textBuffer, pos); - textBuffer.resize(pos); - lineNumber = oldLineNumber; - return false; -} - -bool QXmlStreamReaderPrivate::scanString(const char *str, short tokenToInject, bool requireSpace) -{ - int n = 0; - while (str[n]) { - uint c = getChar(); - if (c != ushort(str[n])) { - if (c != StreamEOF) - putChar(c); - while (n--) { - putChar(ushort(str[n])); - } - return false; - } - ++n; - } - for (int i = 0; i < n; ++i) - textBuffer += QChar(ushort(str[i])); - if (requireSpace) { - int s = fastScanSpace(); - if (!s || atEnd) { - int pos = textBuffer.size() - n - s; - putString(textBuffer, pos); - textBuffer.resize(pos); - return false; - } - } - if (tokenToInject >= 0) - injectToken(tokenToInject); - return true; -} - -bool QXmlStreamReaderPrivate::scanAfterLangleBang() -{ - switch (peekChar()) { - case '[': - return scanString(spell[CDATA_START], CDATA_START, false); - case 'D': - return scanString(spell[DOCTYPE], DOCTYPE); - case 'A': - return scanString(spell[ATTLIST], ATTLIST); - case 'N': - return scanString(spell[NOTATION], NOTATION); - case 'E': - if (scanString(spell[ELEMENT], ELEMENT)) - return true; - return scanString(spell[ENTITY], ENTITY); - - default: - ; - }; - return false; -} - -bool QXmlStreamReaderPrivate::scanPublicOrSystem() -{ - switch (peekChar()) { - case 'S': - return scanString(spell[SYSTEM], SYSTEM); - case 'P': - return scanString(spell[PUBLIC], PUBLIC); - default: - ; - } - return false; -} - -bool QXmlStreamReaderPrivate::scanNData() -{ - if (fastScanSpace()) { - if (scanString(spell[NDATA], NDATA)) - return true; - putChar(' '); - } - return false; -} - -bool QXmlStreamReaderPrivate::scanAfterDefaultDecl() -{ - switch (peekChar()) { - case 'R': - return scanString(spell[REQUIRED], REQUIRED, false); - case 'I': - return scanString(spell[IMPLIED], IMPLIED, false); - case 'F': - return scanString(spell[FIXED], FIXED, false); - default: - ; - } - return false; -} - -bool QXmlStreamReaderPrivate::scanAttType() -{ - switch (peekChar()) { - case 'C': - return scanString(spell[CDATA], CDATA); - case 'I': - if (scanString(spell[ID], ID)) - return true; - if (scanString(spell[IDREF], IDREF)) - return true; - return scanString(spell[IDREFS], IDREFS); - case 'E': - if (scanString(spell[ENTITY], ENTITY)) - return true; - return scanString(spell[ENTITIES], ENTITIES); - case 'N': - if (scanString(spell[NOTATION], NOTATION)) - return true; - if (scanString(spell[NMTOKEN], NMTOKEN)) - return true; - return scanString(spell[NMTOKENS], NMTOKENS); - default: - ; - } - return false; -} - -/*! - \internal - - Scan strings with quotes or apostrophes surround them. For instance, - attributes, the version and encoding field in the XML prolog and - entity declarations. - - If normalizeLiterals is set to true, the function also normalizes - whitespace. It is set to true when the first start tag is - encountered. - - */ -inline int QXmlStreamReaderPrivate::fastScanLiteralContent() -{ - int n = 0; - uint c; - while ((c = getChar()) != StreamEOF) { - switch (ushort(c)) { - case 0xfffe: - case 0xffff: - case 0: - /* The putChar() call is necessary so the parser re-gets - * the character from the input source, when raising an error. */ - putChar(c); - return n; - case '\r': - if (filterCarriageReturn() == 0) - return n; - Q_FALLTHROUGH(); - case '\n': - ++lineNumber; - lastLineStart = characterOffset + readBufferPos; - Q_FALLTHROUGH(); - case ' ': - case '\t': - if (normalizeLiterals) - textBuffer += QLatin1Char(' '); - else - textBuffer += QChar(c); - ++n; - break; - case '&': - case '<': - case '\"': - case '\'': - if (!(c & 0xff0000)) { - putChar(c); - return n; - } - Q_FALLTHROUGH(); - default: - if (c < 0x20) { - putChar(c); - return n; - } - textBuffer += QChar(c); - ++n; - } - } - return n; -} - -inline int QXmlStreamReaderPrivate::fastScanSpace() -{ - int n = 0; - uint c; - while ((c = getChar()) != StreamEOF) { - switch (c) { - case '\r': - if ((c = filterCarriageReturn()) == 0) - return n; - Q_FALLTHROUGH(); - case '\n': - ++lineNumber; - lastLineStart = characterOffset + readBufferPos; - Q_FALLTHROUGH(); - case ' ': - case '\t': - textBuffer += QChar(c); - ++n; - break; - default: - putChar(c); - return n; - } - } - return n; -} - -/*! - \internal - - Used for text nodes essentially. That is, characters appearing - inside elements. - */ -inline int QXmlStreamReaderPrivate::fastScanContentCharList() -{ - int n = 0; - uint c; - while ((c = getChar()) != StreamEOF) { - switch (ushort(c)) { - case 0xfffe: - case 0xffff: - case 0: - putChar(c); - return n; - case ']': { - isWhitespace = false; - int pos = textBuffer.size(); - textBuffer += QChar(ushort(c)); - ++n; - while ((c = getChar()) == ']') { - textBuffer += QChar(ushort(c)); - ++n; - } - if (c == 0) { - putString(textBuffer, pos); - textBuffer.resize(pos); - } else if (c == '>' && textBuffer.at(textBuffer.size()-2) == QLatin1Char(']')) { - raiseWellFormedError(QXmlStream::tr("Sequence ']]>' not allowed in content.")); - } else { - putChar(c); - break; - } - return n; - } break; - case '\r': - if ((c = filterCarriageReturn()) == 0) - return n; - Q_FALLTHROUGH(); - case '\n': - ++lineNumber; - lastLineStart = characterOffset + readBufferPos; - Q_FALLTHROUGH(); - case ' ': - case '\t': - textBuffer += QChar(ushort(c)); - ++n; - break; - case '&': - case '<': - if (!(c & 0xff0000)) { - putChar(c); - return n; - } - Q_FALLTHROUGH(); - default: - if (c < 0x20) { - putChar(c); - return n; - } - isWhitespace = false; - textBuffer += QChar(ushort(c)); - ++n; - } - } - return n; -} - -inline int QXmlStreamReaderPrivate::fastScanName(int *prefix) -{ - int n = 0; - uint c; - while ((c = getChar()) != StreamEOF) { - switch (c) { - case '\n': - case ' ': - case '\t': - case '\r': - case '&': - case '#': - case '\'': - case '\"': - case '<': - case '>': - case '[': - case ']': - case '=': - case '%': - case '/': - case ';': - case '?': - case '!': - case '^': - case '|': - case ',': - case '(': - case ')': - case '+': - case '*': - putChar(c); - if (prefix && *prefix == n+1) { - *prefix = 0; - putChar(':'); - --n; - } - return n; - case ':': - if (prefix) { - if (*prefix == 0) { - *prefix = n+2; - } else { // only one colon allowed according to the namespace spec. - putChar(c); - return n; - } - } else { - putChar(c); - return n; - } - Q_FALLTHROUGH(); - default: - textBuffer += QChar(c); - ++n; - } - } - - if (prefix) - *prefix = 0; - int pos = textBuffer.size() - n; - putString(textBuffer, pos); - textBuffer.resize(pos); - return 0; -} - -enum NameChar { NameBeginning, NameNotBeginning, NotName }; - -static const char Begi = static_cast(NameBeginning); -static const char NtBg = static_cast(NameNotBeginning); -static const char NotN = static_cast(NotName); - -static const char nameCharTable[128] = -{ -// 0x00 - NotN, NotN, NotN, NotN, NotN, NotN, NotN, NotN, - NotN, NotN, NotN, NotN, NotN, NotN, NotN, NotN, -// 0x10 - NotN, NotN, NotN, NotN, NotN, NotN, NotN, NotN, - NotN, NotN, NotN, NotN, NotN, NotN, NotN, NotN, -// 0x20 (0x2D is '-', 0x2E is '.') - NotN, NotN, NotN, NotN, NotN, NotN, NotN, NotN, - NotN, NotN, NotN, NotN, NotN, NtBg, NtBg, NotN, -// 0x30 (0x30..0x39 are '0'..'9', 0x3A is ':') - NtBg, NtBg, NtBg, NtBg, NtBg, NtBg, NtBg, NtBg, - NtBg, NtBg, Begi, NotN, NotN, NotN, NotN, NotN, -// 0x40 (0x41..0x5A are 'A'..'Z') - NotN, Begi, Begi, Begi, Begi, Begi, Begi, Begi, - Begi, Begi, Begi, Begi, Begi, Begi, Begi, Begi, -// 0x50 (0x5F is '_') - Begi, Begi, Begi, Begi, Begi, Begi, Begi, Begi, - Begi, Begi, Begi, NotN, NotN, NotN, NotN, Begi, -// 0x60 (0x61..0x7A are 'a'..'z') - NotN, Begi, Begi, Begi, Begi, Begi, Begi, Begi, - Begi, Begi, Begi, Begi, Begi, Begi, Begi, Begi, -// 0x70 - Begi, Begi, Begi, Begi, Begi, Begi, Begi, Begi, - Begi, Begi, Begi, NotN, NotN, NotN, NotN, NotN -}; - -static inline NameChar fastDetermineNameChar(QChar ch) -{ - ushort uc = ch.unicode(); - if (!(uc & ~0x7f)) // uc < 128 - return static_cast(nameCharTable[uc]); - - QChar::Category cat = ch.category(); - // ### some these categories might be slightly wrong - if ((cat >= QChar::Letter_Uppercase && cat <= QChar::Letter_Other) - || cat == QChar::Number_Letter) - return NameBeginning; - if ((cat >= QChar::Number_DecimalDigit && cat <= QChar::Number_Other) - || (cat >= QChar::Mark_NonSpacing && cat <= QChar::Mark_Enclosing)) - return NameNotBeginning; - return NotName; -} - -inline int QXmlStreamReaderPrivate::fastScanNMTOKEN() -{ - int n = 0; - uint c; - while ((c = getChar()) != StreamEOF) { - if (fastDetermineNameChar(c) == NotName) { - putChar(c); - return n; - } else { - ++n; - textBuffer += QChar(c); - } - } - - int pos = textBuffer.size() - n; - putString(textBuffer, pos); - textBuffer.resize(pos); - - return n; -} - -void QXmlStreamReaderPrivate::putString(const QString &s, int from) -{ - putStack.reserve(s.size()); - for (int i = s.size()-1; i >= from; --i) - putStack.rawPush() = s.at(i).unicode(); -} - -void QXmlStreamReaderPrivate::putStringLiteral(const QString &s) -{ - putStack.reserve(s.size()); - for (int i = s.size()-1; i >= 0; --i) - putStack.rawPush() = ((LETTER << 16) | s.at(i).unicode()); -} - -void QXmlStreamReaderPrivate::putReplacement(const QString &s) -{ - putStack.reserve(s.size()); - for (int i = s.size()-1; i >= 0; --i) { - ushort c = s.at(i).unicode(); - if (c == '\n' || c == '\r') - putStack.rawPush() = ((LETTER << 16) | c); - else - putStack.rawPush() = c; - } -} -void QXmlStreamReaderPrivate::putReplacementInAttributeValue(const QString &s) -{ - putStack.reserve(s.size()); - for (int i = s.size()-1; i >= 0; --i) { - ushort c = s.at(i).unicode(); - if (c == '&' || c == ';') - putStack.rawPush() = c; - else if (c == '\n' || c == '\r') - putStack.rawPush() = ' '; - else - putStack.rawPush() = ((LETTER << 16) | c); - } -} - -uint QXmlStreamReaderPrivate::getChar_helper() -{ - const int BUFFER_SIZE = 8192; - characterOffset += readBufferPos; - readBufferPos = 0; - readBuffer.resize(0); -#ifndef QT_NO_TEXTCODEC - if (decoder) -#endif - nbytesread = 0; - if (device) { - rawReadBuffer.resize(BUFFER_SIZE); - int nbytesreadOrMinus1 = device->read(rawReadBuffer.data() + nbytesread, BUFFER_SIZE - nbytesread); - nbytesread += qMax(nbytesreadOrMinus1, 0); - } else { - if (nbytesread) - rawReadBuffer += dataBuffer; - else - rawReadBuffer = dataBuffer; - nbytesread = rawReadBuffer.size(); - dataBuffer.clear(); - } - if (!nbytesread) { - atEnd = true; - return StreamEOF; - } - -#ifndef QT_NO_TEXTCODEC - if (!decoder) { - if (nbytesread < 4) { // the 4 is to cover 0xef 0xbb 0xbf plus - // one extra for the utf8 codec - atEnd = true; - return StreamEOF; - } - int mib = 106; // UTF-8 - - // look for byte order mark - uchar ch1 = rawReadBuffer.at(0); - uchar ch2 = rawReadBuffer.at(1); - uchar ch3 = rawReadBuffer.at(2); - uchar ch4 = rawReadBuffer.at(3); - - if ((ch1 == 0 && ch2 == 0 && ch3 == 0xfe && ch4 == 0xff) || - (ch1 == 0xff && ch2 == 0xfe && ch3 == 0 && ch4 == 0)) - mib = 1017; // UTF-32 with byte order mark - else if (ch1 == 0x3c && ch2 == 0x00 && ch3 == 0x00 && ch4 == 0x00) - mib = 1019; // UTF-32LE - else if (ch1 == 0x00 && ch2 == 0x00 && ch3 == 0x00 && ch4 == 0x3c) - mib = 1018; // UTF-32BE - else if ((ch1 == 0xfe && ch2 == 0xff) || (ch1 == 0xff && ch2 == 0xfe)) - mib = 1015; // UTF-16 with byte order mark - else if (ch1 == 0x3c && ch2 == 0x00) - mib = 1014; // UTF-16LE - else if (ch1 == 0x00 && ch2 == 0x3c) - mib = 1013; // UTF-16BE - codec = QTextCodec::codecForMib(mib); - Q_ASSERT(codec); - decoder = codec->makeDecoder(); - } - - decoder->toUnicode(&readBuffer, rawReadBuffer.constData(), nbytesread); - - if(lockEncoding && decoder->hasFailure()) { - raiseWellFormedError(QXmlStream::tr("Encountered incorrectly encoded content.")); - readBuffer.clear(); - return StreamEOF; - } -#else - readBuffer = QString::fromLatin1(rawReadBuffer.data(), nbytesread); -#endif // QT_NO_TEXTCODEC - - readBuffer.reserve(1); // keep capacity when calling resize() next time - - if (readBufferPos < readBuffer.size()) { - ushort c = readBuffer.at(readBufferPos++).unicode(); - return c; - } - - atEnd = true; - return StreamEOF; -} - -QStringRef QXmlStreamReaderPrivate::namespaceForPrefix(const QStringRef &prefix) -{ - for (int j = namespaceDeclarations.size() - 1; j >= 0; --j) { - const NamespaceDeclaration &namespaceDeclaration = namespaceDeclarations.at(j); - if (namespaceDeclaration.prefix == prefix) { - return namespaceDeclaration.namespaceUri; - } - } - -#if 1 - if (namespaceProcessing && !prefix.isEmpty()) - raiseWellFormedError(QXmlStream::tr("Namespace prefix '%1' not declared").arg(prefix)); -#endif - - return QStringRef(); -} - -/* - uses namespaceForPrefix and builds the attribute vector - */ -void QXmlStreamReaderPrivate::resolveTag() -{ - int n = attributeStack.size(); - - if (namespaceProcessing) { - for (int a = 0; a < dtdAttributes.size(); ++a) { - DtdAttribute &dtdAttribute = dtdAttributes[a]; - if (!dtdAttribute.isNamespaceAttribute - || dtdAttribute.defaultValue.isNull() - || dtdAttribute.tagName != qualifiedName - || dtdAttribute.attributeQualifiedName.isNull()) - continue; - int i = 0; - while (i < n && symName(attributeStack[i].key) != dtdAttribute.attributeQualifiedName) - ++i; - if (i != n) - continue; - if (dtdAttribute.attributePrefix.isEmpty() && dtdAttribute.attributeName == QLatin1String("xmlns")) { - NamespaceDeclaration &namespaceDeclaration = namespaceDeclarations.push(); - namespaceDeclaration.prefix.clear(); - - const QStringRef ns(dtdAttribute.defaultValue); - if(ns == QLatin1String("http://www.w3.org/2000/xmlns/") || - ns == QLatin1String("http://www.w3.org/XML/1998/namespace")) - raiseWellFormedError(QXmlStream::tr("Illegal namespace declaration.")); - else - namespaceDeclaration.namespaceUri = ns; - } else if (dtdAttribute.attributePrefix == QLatin1String("xmlns")) { - NamespaceDeclaration &namespaceDeclaration = namespaceDeclarations.push(); - QStringRef namespacePrefix = dtdAttribute.attributeName; - QStringRef namespaceUri = dtdAttribute.defaultValue; - if (((namespacePrefix == QLatin1String("xml")) - ^ (namespaceUri == QLatin1String("http://www.w3.org/XML/1998/namespace"))) - || namespaceUri == QLatin1String("http://www.w3.org/2000/xmlns/") - || namespaceUri.isEmpty() - || namespacePrefix == QLatin1String("xmlns")) - raiseWellFormedError(QXmlStream::tr("Illegal namespace declaration.")); - - namespaceDeclaration.prefix = namespacePrefix; - namespaceDeclaration.namespaceUri = namespaceUri; - } - } - } - - tagStack.top().namespaceDeclaration.namespaceUri = namespaceUri = namespaceForPrefix(prefix); - - attributes.resize(n); - - for (int i = 0; i < n; ++i) { - QXmlStreamAttribute &attribute = attributes[i]; - Attribute &attrib = attributeStack[i]; - QStringRef prefix(symPrefix(attrib.key)); - QStringRef name(symString(attrib.key)); - QStringRef qualifiedName(symName(attrib.key)); - QStringRef value(symString(attrib.value)); - - attribute.m_name = QXmlStreamStringRef(name); - attribute.m_qualifiedName = QXmlStreamStringRef(qualifiedName); - attribute.m_value = QXmlStreamStringRef(value); - - if (!prefix.isEmpty()) { - QStringRef attributeNamespaceUri = namespaceForPrefix(prefix); - attribute.m_namespaceUri = QXmlStreamStringRef(attributeNamespaceUri); - } - - for (int j = 0; j < i; ++j) { - if (attributes[j].name() == attribute.name() - && attributes[j].namespaceUri() == attribute.namespaceUri() - && (namespaceProcessing || attributes[j].qualifiedName() == attribute.qualifiedName())) - raiseWellFormedError(QXmlStream::tr("Attribute '%1' redefined.").arg(attribute.qualifiedName())); - } - } - - for (int a = 0; a < dtdAttributes.size(); ++a) { - DtdAttribute &dtdAttribute = dtdAttributes[a]; - if (dtdAttribute.isNamespaceAttribute - || dtdAttribute.defaultValue.isNull() - || dtdAttribute.tagName != qualifiedName - || dtdAttribute.attributeQualifiedName.isNull()) - continue; - int i = 0; - while (i < n && symName(attributeStack[i].key) != dtdAttribute.attributeQualifiedName) - ++i; - if (i != n) - continue; - - - - QXmlStreamAttribute attribute; - attribute.m_name = QXmlStreamStringRef(dtdAttribute.attributeName); - attribute.m_qualifiedName = QXmlStreamStringRef(dtdAttribute.attributeQualifiedName); - attribute.m_value = QXmlStreamStringRef(dtdAttribute.defaultValue); - - if (!dtdAttribute.attributePrefix.isEmpty()) { - QStringRef attributeNamespaceUri = namespaceForPrefix(dtdAttribute.attributePrefix); - attribute.m_namespaceUri = QXmlStreamStringRef(attributeNamespaceUri); - } - attribute.m_isDefault = true; - attributes.append(attribute); - } - - attributeStack.clear(); -} - -void QXmlStreamReaderPrivate::resolvePublicNamespaces() -{ - const Tag &tag = tagStack.top(); - int n = namespaceDeclarations.size() - tag.namespaceDeclarationsSize; - publicNamespaceDeclarations.resize(n); - for (int i = 0; i < n; ++i) { - const NamespaceDeclaration &namespaceDeclaration = namespaceDeclarations.at(tag.namespaceDeclarationsSize + i); - QXmlStreamNamespaceDeclaration &publicNamespaceDeclaration = publicNamespaceDeclarations[i]; - publicNamespaceDeclaration.m_prefix = QXmlStreamStringRef(namespaceDeclaration.prefix); - publicNamespaceDeclaration.m_namespaceUri = QXmlStreamStringRef(namespaceDeclaration.namespaceUri); - } -} - -void QXmlStreamReaderPrivate::resolveDtd() -{ - publicNotationDeclarations.resize(notationDeclarations.size()); - for (int i = 0; i < notationDeclarations.size(); ++i) { - const QXmlStreamReaderPrivate::NotationDeclaration ¬ationDeclaration = notationDeclarations.at(i); - QXmlStreamNotationDeclaration &publicNotationDeclaration = publicNotationDeclarations[i]; - publicNotationDeclaration.m_name = QXmlStreamStringRef(notationDeclaration.name); - publicNotationDeclaration.m_systemId = QXmlStreamStringRef(notationDeclaration.systemId); - publicNotationDeclaration.m_publicId = QXmlStreamStringRef(notationDeclaration.publicId); - - } - notationDeclarations.clear(); - publicEntityDeclarations.resize(entityDeclarations.size()); - for (int i = 0; i < entityDeclarations.size(); ++i) { - const QXmlStreamReaderPrivate::EntityDeclaration &entityDeclaration = entityDeclarations.at(i); - QXmlStreamEntityDeclaration &publicEntityDeclaration = publicEntityDeclarations[i]; - publicEntityDeclaration.m_name = QXmlStreamStringRef(entityDeclaration.name); - publicEntityDeclaration.m_notationName = QXmlStreamStringRef(entityDeclaration.notationName); - publicEntityDeclaration.m_systemId = QXmlStreamStringRef(entityDeclaration.systemId); - publicEntityDeclaration.m_publicId = QXmlStreamStringRef(entityDeclaration.publicId); - publicEntityDeclaration.m_value = QXmlStreamStringRef(entityDeclaration.value); - } - entityDeclarations.clear(); - parameterEntityHash.clear(); -} - -uint QXmlStreamReaderPrivate::resolveCharRef(int symbolIndex) -{ - bool ok = true; - uint s; - // ### add toXShort to QStringRef? - if (sym(symbolIndex).c == 'x') - s = symString(symbolIndex, 1).toUInt(&ok, 16); - else - s = symString(symbolIndex).toUInt(&ok, 10); - - ok &= (s == 0x9 || s == 0xa || s == 0xd || (s >= 0x20 && s <= 0xd7ff) - || (s >= 0xe000 && s <= 0xfffd) || (s >= 0x10000 && s <= QChar::LastValidCodePoint)); - - return ok ? s : 0; -} - - -void QXmlStreamReaderPrivate::checkPublicLiteral(const QStringRef &publicId) -{ -//#x20 | #xD | #xA | [a-zA-Z0-9] | [-'()+,./:=?;!*#@$_%] - - const ushort *data = reinterpret_cast(publicId.constData()); - uchar c = 0; - int i; - for (i = publicId.size() - 1; i >= 0; --i) { - if (data[i] < 256) - switch ((c = data[i])) { - case ' ': case '\n': case '\r': case '-': case '(': case ')': - case '+': case ',': case '.': case '/': case ':': case '=': - case '?': case ';': case '!': case '*': case '#': case '@': - case '$': case '_': case '%': case '\'': case '\"': - continue; - default: - if ((c >= 'a' && c <= 'z') - || (c >= 'A' && c <= 'Z') - || (c >= '0' && c <= '9')) - continue; - } - break; - } - if (i >= 0) - raiseWellFormedError(QXmlStream::tr("Unexpected character '%1' in public id literal.").arg(QChar(QLatin1Char(c)))); -} - -/* - Checks whether the document starts with an xml declaration. If it - does, this function returns \c true; otherwise it sets up everything - for a synthetic start document event and returns \c false. - */ -bool QXmlStreamReaderPrivate::checkStartDocument() -{ - hasCheckedStartDocument = true; - - if (scanString(spell[XML], XML)) - return true; - - type = QXmlStreamReader::StartDocument; - if (atEnd) { - hasCheckedStartDocument = false; - raiseError(QXmlStreamReader::PrematureEndOfDocumentError); - } - return false; -} - -void QXmlStreamReaderPrivate::startDocument() -{ - QString err; - if (documentVersion != QLatin1String("1.0")) { - if (documentVersion.contains(QLatin1Char(' '))) - err = QXmlStream::tr("Invalid XML version string."); - else - err = QXmlStream::tr("Unsupported XML version."); - } - int n = attributeStack.size(); - - /* We use this bool to ensure that the pesudo attributes are in the - * proper order: - * - * [23] XMLDecl ::= '' */ - bool hasStandalone = false; - - for (int i = 0; err.isNull() && i < n; ++i) { - Attribute &attrib = attributeStack[i]; - QStringRef prefix(symPrefix(attrib.key)); - QStringRef key(symString(attrib.key)); - QStringRef value(symString(attrib.value)); - - if (prefix.isEmpty() && key == QLatin1String("encoding")) { - documentEncoding = value; - - if(hasStandalone) - err = QXmlStream::tr("The standalone pseudo attribute must appear after the encoding."); - if (!QXmlUtils::isEncName(value)) - err = QXmlStream::tr("%1 is an invalid encoding name.").arg(value); - else { -#ifdef QT_NO_TEXTCODEC - readBuffer = QString::fromLatin1(rawReadBuffer.data(), nbytesread); -#else - QTextCodec *const newCodec = QTextCodec::codecForName(value.toLatin1()); - if (!newCodec) - err = QXmlStream::tr("Encoding %1 is unsupported").arg(value); - else if (newCodec != codec && !lockEncoding) { - codec = newCodec; - delete decoder; - decoder = codec->makeDecoder(); - decoder->toUnicode(&readBuffer, rawReadBuffer.data(), nbytesread); - } -#endif // QT_NO_TEXTCODEC - } - } else if (prefix.isEmpty() && key == QLatin1String("standalone")) { - hasStandalone = true; - if (value == QLatin1String("yes")) - standalone = true; - else if (value == QLatin1String("no")) - standalone = false; - else - err = QXmlStream::tr("Standalone accepts only yes or no."); - } else { - err = QXmlStream::tr("Invalid attribute in XML declaration."); - } - } - - if (!err.isNull()) - raiseWellFormedError(err); - attributeStack.clear(); -} - - -void QXmlStreamReaderPrivate::raiseError(QXmlStreamReader::Error error, const QString& message) -{ - this->error = error; - errorString = message; - if (errorString.isNull()) { - if (error == QXmlStreamReader::PrematureEndOfDocumentError) - errorString = QXmlStream::tr("Premature end of document."); - else if (error == QXmlStreamReader::CustomError) - errorString = QXmlStream::tr("Invalid document."); - } - - type = QXmlStreamReader::Invalid; -} - -void QXmlStreamReaderPrivate::raiseWellFormedError(const QString &message) -{ - raiseError(QXmlStreamReader::NotWellFormedError, message); -} - -void QXmlStreamReaderPrivate::parseError() -{ - - if (token == EOF_SYMBOL) { - raiseError(QXmlStreamReader::PrematureEndOfDocumentError); - return; - } - const int nmax = 4; - QString error_message; - int ers = state_stack[tos]; - int nexpected = 0; - int expected[nmax]; - if (token != ERROR) - for (int tk = 0; tk < TERMINAL_COUNT; ++tk) { - int k = t_action(ers, tk); - if (k <= 0) - continue; - if (spell[tk]) { - if (nexpected < nmax) - expected[nexpected++] = tk; - } - } - - if (nexpected && nexpected < nmax) { - //: '' - QString exp_str = QXmlStream::tr("'%1'", "expected").arg(QLatin1String(spell[expected[0]])); - if (nexpected == 2) { - //: , '' - exp_str = QXmlStream::tr("%1 or '%2'", "expected").arg(exp_str, QLatin1String(spell[expected[1]])); - } else if (nexpected > 2) { - int s = 1; - for (; s < nexpected - 1; ++s) { - //: , '' - exp_str = QXmlStream::tr("%1, '%2'", "expected").arg(exp_str, QLatin1String(spell[expected[s]])); - } - //: , or '' - exp_str = QXmlStream::tr("%1, or '%2'", "expected").arg(exp_str, QLatin1String(spell[expected[s]])); - } - error_message = QXmlStream::tr("Expected %1, but got '%2'.").arg(exp_str, QLatin1String(spell[token])); - } else { - error_message = QXmlStream::tr("Unexpected '%1'.").arg(QLatin1String(spell[token])); - } - - raiseWellFormedError(error_message); -} - -void QXmlStreamReaderPrivate::resume(int rule) { - resumeReduction = rule; - if (error == QXmlStreamReader::NoError) - raiseError(QXmlStreamReader::PrematureEndOfDocumentError); -} - -/*! Returns the current line number, starting with 1. - -\sa columnNumber(), characterOffset() - */ -qint64 QXmlStreamReader::lineNumber() const -{ - Q_D(const QXmlStreamReader); - return d->lineNumber + 1; // in public we start with 1 -} - -/*! Returns the current column number, starting with 0. - -\sa lineNumber(), characterOffset() - */ -qint64 QXmlStreamReader::columnNumber() const -{ - Q_D(const QXmlStreamReader); - return d->characterOffset - d->lastLineStart + d->readBufferPos; -} - -/*! Returns the current character offset, starting with 0. - -\sa lineNumber(), columnNumber() -*/ -qint64 QXmlStreamReader::characterOffset() const -{ - Q_D(const QXmlStreamReader); - return d->characterOffset + d->readBufferPos; -} - - -/*! Returns the text of \l Characters, \l Comment, \l DTD, or - EntityReference. - */ -QStringRef QXmlStreamReader::text() const -{ - Q_D(const QXmlStreamReader); - return d->text; -} - - -/*! If the tokenType() is \l DTD, this function returns the DTD's - notation declarations. Otherwise an empty vector is returned. - - The QXmlStreamNotationDeclarations class is defined to be a QVector - of QXmlStreamNotationDeclaration. - */ -QXmlStreamNotationDeclarations QXmlStreamReader::notationDeclarations() const -{ - Q_D(const QXmlStreamReader); - if (d->notationDeclarations.size()) - const_cast(d)->resolveDtd(); - return d->publicNotationDeclarations; -} - - -/*! If the tokenType() is \l DTD, this function returns the DTD's - unparsed (external) entity declarations. Otherwise an empty vector is returned. - - The QXmlStreamEntityDeclarations class is defined to be a QVector - of QXmlStreamEntityDeclaration. - */ -QXmlStreamEntityDeclarations QXmlStreamReader::entityDeclarations() const -{ - Q_D(const QXmlStreamReader); - if (d->entityDeclarations.size()) - const_cast(d)->resolveDtd(); - return d->publicEntityDeclarations; -} - -/*! - \since 4.4 - - If the tokenType() is \l DTD, this function returns the DTD's - name. Otherwise an empty string is returned. - - */ -QStringRef QXmlStreamReader::dtdName() const -{ - Q_D(const QXmlStreamReader); - if (d->type == QXmlStreamReader::DTD) - return d->dtdName; - return QStringRef(); -} - -/*! - \since 4.4 - - If the tokenType() is \l DTD, this function returns the DTD's - public identifier. Otherwise an empty string is returned. - - */ -QStringRef QXmlStreamReader::dtdPublicId() const -{ - Q_D(const QXmlStreamReader); - if (d->type == QXmlStreamReader::DTD) - return d->dtdPublicId; - return QStringRef(); -} - -/*! - \since 4.4 - - If the tokenType() is \l DTD, this function returns the DTD's - system identifier. Otherwise an empty string is returned. - - */ -QStringRef QXmlStreamReader::dtdSystemId() const -{ - Q_D(const QXmlStreamReader); - if (d->type == QXmlStreamReader::DTD) - return d->dtdSystemId; - return QStringRef(); -} - -/*! If the tokenType() is \l StartElement, this function returns the - element's namespace declarations. Otherwise an empty vector is - returned. - - The QXmlStreamNamespaceDeclarations class is defined to be a QVector - of QXmlStreamNamespaceDeclaration. - - \sa addExtraNamespaceDeclaration(), addExtraNamespaceDeclarations() - */ -QXmlStreamNamespaceDeclarations QXmlStreamReader::namespaceDeclarations() const -{ - Q_D(const QXmlStreamReader); - if (d->publicNamespaceDeclarations.isEmpty() && d->type == StartElement) - const_cast(d)->resolvePublicNamespaces(); - return d->publicNamespaceDeclarations; -} - - -/*! - \since 4.4 - - Adds an \a extraNamespaceDeclaration. The declaration will be - valid for children of the current element, or - should the function - be called before any elements are read - for the entire XML - document. - - \sa namespaceDeclarations(), addExtraNamespaceDeclarations(), setNamespaceProcessing() - */ -void QXmlStreamReader::addExtraNamespaceDeclaration(const QXmlStreamNamespaceDeclaration &extraNamespaceDeclaration) -{ - Q_D(QXmlStreamReader); - QXmlStreamReaderPrivate::NamespaceDeclaration &namespaceDeclaration = d->namespaceDeclarations.push(); - namespaceDeclaration.prefix = d->addToStringStorage(extraNamespaceDeclaration.prefix()); - namespaceDeclaration.namespaceUri = d->addToStringStorage(extraNamespaceDeclaration.namespaceUri()); -} - -/*! - \since 4.4 - - Adds a vector of declarations specified by \a extraNamespaceDeclarations. - - \sa namespaceDeclarations(), addExtraNamespaceDeclaration() - */ -void QXmlStreamReader::addExtraNamespaceDeclarations(const QXmlStreamNamespaceDeclarations &extraNamespaceDeclarations) -{ - for (int i = 0; i < extraNamespaceDeclarations.size(); ++i) - addExtraNamespaceDeclaration(extraNamespaceDeclarations.at(i)); -} - - -/*! Convenience function to be called in case a StartElement was - read. Reads until the corresponding EndElement and returns all text - in-between. In case of no error, the current token (see tokenType()) - after having called this function is EndElement. - - The function concatenates text() when it reads either \l Characters - or EntityReference tokens, but skips ProcessingInstruction and \l - Comment. If the current token is not StartElement, an empty string is - returned. - - The \a behaviour defines what happens in case anything else is - read before reaching EndElement. The function can include the text from - child elements (useful for example for HTML), ignore child elements, or - raise an UnexpectedElementError and return what was read so far (default). - - \since 4.6 - */ -QString QXmlStreamReader::readElementText(ReadElementTextBehaviour behaviour) -{ - Q_D(QXmlStreamReader); - if (isStartElement()) { - QString result; - forever { - switch (readNext()) { - case Characters: - case EntityReference: - result.insert(result.size(), d->text.unicode(), d->text.size()); - break; - case EndElement: - return result; - case ProcessingInstruction: - case Comment: - break; - case StartElement: - if (behaviour == SkipChildElements) { - skipCurrentElement(); - break; - } else if (behaviour == IncludeChildElements) { - result += readElementText(behaviour); - break; - } - Q_FALLTHROUGH(); - default: - if (d->error || behaviour == ErrorOnUnexpectedElement) { - if (!d->error) - d->raiseError(UnexpectedElementError, QXmlStream::tr("Expected character data.")); - return result; - } - } - } - } - return QString(); -} - -/*! Raises a custom error with an optional error \a message. - - \sa error(), errorString() - */ -void QXmlStreamReader::raiseError(const QString& message) -{ - Q_D(QXmlStreamReader); - d->raiseError(CustomError, message); -} - -/*! - Returns the error message that was set with raiseError(). - - \sa error(), lineNumber(), columnNumber(), characterOffset() - */ -QString QXmlStreamReader::errorString() const -{ - Q_D(const QXmlStreamReader); - if (d->type == QXmlStreamReader::Invalid) - return d->errorString; - return QString(); -} - -/*! Returns the type of the current error, or NoError if no error occurred. - - \sa errorString(), raiseError() - */ -QXmlStreamReader::Error QXmlStreamReader::error() const -{ - Q_D(const QXmlStreamReader); - if (d->type == QXmlStreamReader::Invalid) - return d->error; - return NoError; -} - -/*! - Returns the target of a ProcessingInstruction. - */ -QStringRef QXmlStreamReader::processingInstructionTarget() const -{ - Q_D(const QXmlStreamReader); - return d->processingInstructionTarget; -} - -/*! - Returns the data of a ProcessingInstruction. - */ -QStringRef QXmlStreamReader::processingInstructionData() const -{ - Q_D(const QXmlStreamReader); - return d->processingInstructionData; -} - - - -/*! - Returns the local name of a StartElement, EndElement, or an EntityReference. - - \sa namespaceUri(), qualifiedName() - */ -QStringRef QXmlStreamReader::name() const -{ - Q_D(const QXmlStreamReader); - return d->name; -} - -/*! - Returns the namespaceUri of a StartElement or EndElement. - - \sa name(), qualifiedName() - */ -QStringRef QXmlStreamReader::namespaceUri() const -{ - Q_D(const QXmlStreamReader); - return d->namespaceUri; -} - -/*! - Returns the qualified name of a StartElement or EndElement; - - A qualified name is the raw name of an element in the XML data. It - consists of the namespace prefix, followed by colon, followed by the - element's local name. Since the namespace prefix is not unique (the - same prefix can point to different namespaces and different prefixes - can point to the same namespace), you shouldn't use qualifiedName(), - but the resolved namespaceUri() and the attribute's local name(). - - \sa name(), prefix(), namespaceUri() - */ -QStringRef QXmlStreamReader::qualifiedName() const -{ - Q_D(const QXmlStreamReader); - return d->qualifiedName; -} - - - -/*! - \since 4.4 - - Returns the prefix of a StartElement or EndElement. - - \sa name(), qualifiedName() -*/ -QStringRef QXmlStreamReader::prefix() const -{ - Q_D(const QXmlStreamReader); - return d->prefix; -} - -/*! - Returns the attributes of a StartElement. - */ -QXmlStreamAttributes QXmlStreamReader::attributes() const -{ - Q_D(const QXmlStreamReader); - return d->attributes; -} - -#endif // QT_NO_XMLSTREAMREADER - -/*! - \class QXmlStreamAttribute - \inmodule QtCore - \since 4.3 - \reentrant - \brief The QXmlStreamAttribute class represents a single XML attribute - - \ingroup xml-tools - - An attribute consists of an optionally empty namespaceUri(), a - name(), a value(), and an isDefault() attribute. - - The raw XML attribute name is returned as qualifiedName(). -*/ - -/*! - Creates an empty attribute. - */ -QXmlStreamAttribute::QXmlStreamAttribute() -{ - m_isDefault = false; -} - -/*! - Destructs an attribute. - */ -QXmlStreamAttribute::~QXmlStreamAttribute() -{ -} - -/*! Constructs an attribute in the namespace described with \a - namespaceUri with \a name and value \a value. - */ -QXmlStreamAttribute::QXmlStreamAttribute(const QString &namespaceUri, const QString &name, const QString &value) -{ - m_namespaceUri = QXmlStreamStringRef(QStringRef(&namespaceUri)); - m_name = m_qualifiedName = QXmlStreamStringRef(QStringRef(&name)); - m_value = QXmlStreamStringRef(QStringRef(&value)); - m_namespaceUri = QXmlStreamStringRef(QStringRef(&namespaceUri)); -} - -/*! - Constructs an attribute with qualified name \a qualifiedName and value \a value. - */ -QXmlStreamAttribute::QXmlStreamAttribute(const QString &qualifiedName, const QString &value) -{ - int colon = qualifiedName.indexOf(QLatin1Char(':')); - m_name = QXmlStreamStringRef(QStringRef(&qualifiedName, - colon + 1, - qualifiedName.size() - (colon + 1))); - m_qualifiedName = QXmlStreamStringRef(QStringRef(&qualifiedName)); - m_value = QXmlStreamStringRef(QStringRef(&value)); -} - -/*! \fn QStringRef QXmlStreamAttribute::namespaceUri() const - - Returns the attribute's resolved namespaceUri, or an empty string - reference if the attribute does not have a defined namespace. - */ -/*! \fn QStringRef QXmlStreamAttribute::name() const - Returns the attribute's local name. - */ -/*! \fn QStringRef QXmlStreamAttribute::qualifiedName() const - Returns the attribute's qualified name. - - A qualified name is the raw name of an attribute in the XML - data. It consists of the namespace prefix(), followed by colon, - followed by the attribute's local name(). Since the namespace prefix - is not unique (the same prefix can point to different namespaces - and different prefixes can point to the same namespace), you - shouldn't use qualifiedName(), but the resolved namespaceUri() and - the attribute's local name(). - */ -/*! - \fn QStringRef QXmlStreamAttribute::prefix() const - \since 4.4 - Returns the attribute's namespace prefix. - - \sa name(), qualifiedName() - -*/ - -/*! \fn QStringRef QXmlStreamAttribute::value() const - Returns the attribute's value. - */ - -/*! \fn bool QXmlStreamAttribute::isDefault() const - - Returns \c true if the parser added this attribute with a default - value following an ATTLIST declaration in the DTD; otherwise - returns \c false. -*/ -/*! \fn bool QXmlStreamAttribute::operator==(const QXmlStreamAttribute &other) const - - Compares this attribute with \a other and returns \c true if they are - equal; otherwise returns \c false. - */ -/*! \fn bool QXmlStreamAttribute::operator!=(const QXmlStreamAttribute &other) const - - Compares this attribute with \a other and returns \c true if they are - not equal; otherwise returns \c false. - */ - - -/*! - Creates a copy of \a other. - */ -QXmlStreamAttribute::QXmlStreamAttribute(const QXmlStreamAttribute &other) -{ - *this = other; -} - -/*! - Assigns \a other to this attribute. - */ -QXmlStreamAttribute& QXmlStreamAttribute::operator=(const QXmlStreamAttribute &other) -{ - m_name = other.m_name; - m_namespaceUri = other.m_namespaceUri; - m_qualifiedName = other.m_qualifiedName; - m_value = other.m_value; - m_isDefault = other.m_isDefault; - return *this; -} - - -/*! - \class QXmlStreamAttributes - \inmodule QtCore - \since 4.3 - \reentrant - \brief The QXmlStreamAttributes class represents a vector of QXmlStreamAttribute. - - Attributes are returned by a QXmlStreamReader in - \l{QXmlStreamReader::attributes()} {attributes()} when the reader - reports a \l {QXmlStreamReader::StartElement}{start element}. The - class can also be used with a QXmlStreamWriter as an argument to - \l {QXmlStreamWriter::writeAttributes()}{writeAttributes()}. - - The convenience function value() loops over the vector and returns - an attribute value for a given namespaceUri and an attribute's - name. - - New attributes can be added with append(). - - \ingroup xml-tools -*/ - -/*! - \fn QXmlStreamAttributes::QXmlStreamAttributes() - - A constructor for QXmlStreamAttributes. -*/ - -/*! - \typedef QXmlStreamNotationDeclarations - \relates QXmlStreamNotationDeclaration - - Synonym for QVector. -*/ - - -/*! - \class QXmlStreamNotationDeclaration - \inmodule QtCore - \since 4.3 - \reentrant - \brief The QXmlStreamNotationDeclaration class represents a DTD notation declaration. - - \ingroup xml-tools - - An notation declaration consists of a name(), a systemId(), and a publicId(). -*/ - -/*! - Creates an empty notation declaration. -*/ -QXmlStreamNotationDeclaration::QXmlStreamNotationDeclaration() -{ -} -/*! - Creates a copy of \a other. - */ -QXmlStreamNotationDeclaration::QXmlStreamNotationDeclaration(const QXmlStreamNotationDeclaration &other) -{ - *this = other; -} - -/*! - Assigns \a other to this notation declaration. - */ -QXmlStreamNotationDeclaration& QXmlStreamNotationDeclaration::operator=(const QXmlStreamNotationDeclaration &other) -{ - m_name = other.m_name; - m_systemId = other.m_systemId; - m_publicId = other.m_publicId; - return *this; -} - -/*! -Destructs this notation declaration. -*/ -QXmlStreamNotationDeclaration::~QXmlStreamNotationDeclaration() -{ -} - -/*! \fn QStringRef QXmlStreamNotationDeclaration::name() const - -Returns the notation name. -*/ -/*! \fn QStringRef QXmlStreamNotationDeclaration::systemId() const - -Returns the system identifier. -*/ -/*! \fn QStringRef QXmlStreamNotationDeclaration::publicId() const - -Returns the public identifier. -*/ - -/*! \fn inline bool QXmlStreamNotationDeclaration::operator==(const QXmlStreamNotationDeclaration &other) const - - Compares this notation declaration with \a other and returns \c true - if they are equal; otherwise returns \c false. - */ -/*! \fn inline bool QXmlStreamNotationDeclaration::operator!=(const QXmlStreamNotationDeclaration &other) const - - Compares this notation declaration with \a other and returns \c true - if they are not equal; otherwise returns \c false. - */ - -/*! - \typedef QXmlStreamNamespaceDeclarations - \relates QXmlStreamNamespaceDeclaration - - Synonym for QVector. -*/ - -/*! - \class QXmlStreamNamespaceDeclaration - \inmodule QtCore - \since 4.3 - \reentrant - \brief The QXmlStreamNamespaceDeclaration class represents a namespace declaration. - - \ingroup xml-tools - - An namespace declaration consists of a prefix() and a namespaceUri(). -*/ -/*! \fn inline bool QXmlStreamNamespaceDeclaration::operator==(const QXmlStreamNamespaceDeclaration &other) const - - Compares this namespace declaration with \a other and returns \c true - if they are equal; otherwise returns \c false. - */ -/*! \fn inline bool QXmlStreamNamespaceDeclaration::operator!=(const QXmlStreamNamespaceDeclaration &other) const - - Compares this namespace declaration with \a other and returns \c true - if they are not equal; otherwise returns \c false. - */ - -/*! - Creates an empty namespace declaration. -*/ -QXmlStreamNamespaceDeclaration::QXmlStreamNamespaceDeclaration() -{ -} - -/*! - \since 4.4 - - Creates a namespace declaration with \a prefix and \a namespaceUri. -*/ -QXmlStreamNamespaceDeclaration::QXmlStreamNamespaceDeclaration(const QString &prefix, const QString &namespaceUri) -{ - m_prefix = prefix; - m_namespaceUri = namespaceUri; -} - -/*! - Creates a copy of \a other. - */ -QXmlStreamNamespaceDeclaration::QXmlStreamNamespaceDeclaration(const QXmlStreamNamespaceDeclaration &other) -{ - *this = other; -} - -/*! - Assigns \a other to this namespace declaration. - */ -QXmlStreamNamespaceDeclaration& QXmlStreamNamespaceDeclaration::operator=(const QXmlStreamNamespaceDeclaration &other) -{ - m_prefix = other.m_prefix; - m_namespaceUri = other.m_namespaceUri; - return *this; -} -/*! -Destructs this namespace declaration. -*/ -QXmlStreamNamespaceDeclaration::~QXmlStreamNamespaceDeclaration() -{ -} - -/*! \fn QStringRef QXmlStreamNamespaceDeclaration::prefix() const - -Returns the prefix. -*/ -/*! \fn QStringRef QXmlStreamNamespaceDeclaration::namespaceUri() const - -Returns the namespaceUri. -*/ - - - - -/*! - \typedef QXmlStreamEntityDeclarations - \relates QXmlStreamEntityDeclaration - - Synonym for QVector. -*/ - -/*! - \class QXmlStreamStringRef - \inmodule QtCore - \since 4.3 - \internal -*/ - -/*! - \class QXmlStreamEntityDeclaration - \inmodule QtCore - \since 4.3 - \reentrant - \brief The QXmlStreamEntityDeclaration class represents a DTD entity declaration. - - \ingroup xml-tools - - An entity declaration consists of a name(), a notationName(), a - systemId(), a publicId(), and a value(). -*/ - -/*! - Creates an empty entity declaration. -*/ -QXmlStreamEntityDeclaration::QXmlStreamEntityDeclaration() -{ -} - -/*! - Creates a copy of \a other. - */ -QXmlStreamEntityDeclaration::QXmlStreamEntityDeclaration(const QXmlStreamEntityDeclaration &other) -{ - *this = other; -} - -/*! - Assigns \a other to this entity declaration. - */ -QXmlStreamEntityDeclaration& QXmlStreamEntityDeclaration::operator=(const QXmlStreamEntityDeclaration &other) -{ - m_name = other.m_name; - m_notationName = other.m_notationName; - m_systemId = other.m_systemId; - m_publicId = other.m_publicId; - m_value = other.m_value; - return *this; -} - -/*! - Destructs this entity declaration. -*/ -QXmlStreamEntityDeclaration::~QXmlStreamEntityDeclaration() -{ -} - -/*! \fn QXmlStreamStringRef::swap(QXmlStreamStringRef &other) - \since 5.6 - - Swaps this string reference's contents with \a other. - This function is very fast and never fails. -*/ - -/*! \fn QStringRef QXmlStreamEntityDeclaration::name() const - -Returns the entity name. -*/ -/*! \fn QStringRef QXmlStreamEntityDeclaration::notationName() const - -Returns the notation name. -*/ -/*! \fn QStringRef QXmlStreamEntityDeclaration::systemId() const - -Returns the system identifier. -*/ -/*! \fn QStringRef QXmlStreamEntityDeclaration::publicId() const - -Returns the public identifier. -*/ -/*! \fn QStringRef QXmlStreamEntityDeclaration::value() const - -Returns the entity's value. -*/ - -/*! \fn bool QXmlStreamEntityDeclaration::operator==(const QXmlStreamEntityDeclaration &other) const - - Compares this entity declaration with \a other and returns \c true if - they are equal; otherwise returns \c false. - */ -/*! \fn bool QXmlStreamEntityDeclaration::operator!=(const QXmlStreamEntityDeclaration &other) const - - Compares this entity declaration with \a other and returns \c true if - they are not equal; otherwise returns \c false. - */ - -/*! Returns the value of the attribute \a name in the namespace - described with \a namespaceUri, or an empty string reference if the - attribute is not defined. The \a namespaceUri can be empty. - */ -QStringRef QXmlStreamAttributes::value(const QString &namespaceUri, const QString &name) const -{ - for (int i = 0; i < size(); ++i) { - const QXmlStreamAttribute &attribute = at(i); - if (attribute.name() == name && attribute.namespaceUri() == namespaceUri) - return attribute.value(); - } - return QStringRef(); -} - -/*!\overload - Returns the value of the attribute \a name in the namespace - described with \a namespaceUri, or an empty string reference if the - attribute is not defined. The \a namespaceUri can be empty. - */ -QStringRef QXmlStreamAttributes::value(const QString &namespaceUri, QLatin1String name) const -{ - for (int i = 0; i < size(); ++i) { - const QXmlStreamAttribute &attribute = at(i); - if (attribute.name() == name && attribute.namespaceUri() == namespaceUri) - return attribute.value(); - } - return QStringRef(); -} - -/*!\overload - Returns the value of the attribute \a name in the namespace - described with \a namespaceUri, or an empty string reference if the - attribute is not defined. The \a namespaceUri can be empty. - */ -QStringRef QXmlStreamAttributes::value(QLatin1String namespaceUri, QLatin1String name) const -{ - for (int i = 0; i < size(); ++i) { - const QXmlStreamAttribute &attribute = at(i); - if (attribute.name() == name && attribute.namespaceUri() == namespaceUri) - return attribute.value(); - } - return QStringRef(); -} - -/*!\overload - - Returns the value of the attribute with qualified name \a - qualifiedName , or an empty string reference if the attribute is not - defined. A qualified name is the raw name of an attribute in the XML - data. It consists of the namespace prefix, followed by colon, - followed by the attribute's local name. Since the namespace prefix - is not unique (the same prefix can point to different namespaces and - different prefixes can point to the same namespace), you shouldn't - use qualified names, but a resolved namespaceUri and the attribute's - local name. - */ -QStringRef QXmlStreamAttributes::value(const QString &qualifiedName) const -{ - for (int i = 0; i < size(); ++i) { - const QXmlStreamAttribute &attribute = at(i); - if (attribute.qualifiedName() == qualifiedName) - return attribute.value(); - } - return QStringRef(); -} - -/*!\overload - - Returns the value of the attribute with qualified name \a - qualifiedName , or an empty string reference if the attribute is not - defined. A qualified name is the raw name of an attribute in the XML - data. It consists of the namespace prefix, followed by colon, - followed by the attribute's local name. Since the namespace prefix - is not unique (the same prefix can point to different namespaces and - different prefixes can point to the same namespace), you shouldn't - use qualified names, but a resolved namespaceUri and the attribute's - local name. - */ -QStringRef QXmlStreamAttributes::value(QLatin1String qualifiedName) const -{ - for (int i = 0; i < size(); ++i) { - const QXmlStreamAttribute &attribute = at(i); - if (attribute.qualifiedName() == qualifiedName) - return attribute.value(); - } - return QStringRef(); -} - -/*!Appends a new attribute with \a name in the namespace - described with \a namespaceUri, and value \a value. The \a - namespaceUri can be empty. - */ -void QXmlStreamAttributes::append(const QString &namespaceUri, const QString &name, const QString &value) -{ - append(QXmlStreamAttribute(namespaceUri, name, value)); -} - -/*!\overload - Appends a new attribute with qualified name \a qualifiedName and - value \a value. - */ -void QXmlStreamAttributes::append(const QString &qualifiedName, const QString &value) -{ - append(QXmlStreamAttribute(qualifiedName, value)); -} - -#ifndef QT_NO_XMLSTREAMREADER - -/*! \fn bool QXmlStreamReader::isStartDocument() const - Returns \c true if tokenType() equals \l StartDocument; otherwise returns \c false. -*/ -/*! \fn bool QXmlStreamReader::isEndDocument() const - Returns \c true if tokenType() equals \l EndDocument; otherwise returns \c false. -*/ -/*! \fn bool QXmlStreamReader::isStartElement() const - Returns \c true if tokenType() equals \l StartElement; otherwise returns \c false. -*/ -/*! \fn bool QXmlStreamReader::isEndElement() const - Returns \c true if tokenType() equals \l EndElement; otherwise returns \c false. -*/ -/*! \fn bool QXmlStreamReader::isCharacters() const - Returns \c true if tokenType() equals \l Characters; otherwise returns \c false. - - \sa isWhitespace(), isCDATA() -*/ -/*! \fn bool QXmlStreamReader::isComment() const - Returns \c true if tokenType() equals \l Comment; otherwise returns \c false. -*/ -/*! \fn bool QXmlStreamReader::isDTD() const - Returns \c true if tokenType() equals \l DTD; otherwise returns \c false. -*/ -/*! \fn bool QXmlStreamReader::isEntityReference() const - Returns \c true if tokenType() equals \l EntityReference; otherwise returns \c false. -*/ -/*! \fn bool QXmlStreamReader::isProcessingInstruction() const - Returns \c true if tokenType() equals \l ProcessingInstruction; otherwise returns \c false. -*/ - -/*! Returns \c true if the reader reports characters that only consist - of white-space; otherwise returns \c false. - - \sa isCharacters(), text() -*/ -bool QXmlStreamReader::isWhitespace() const -{ - Q_D(const QXmlStreamReader); - return d->type == QXmlStreamReader::Characters && d->isWhitespace; -} - -/*! Returns \c true if the reader reports characters that stem from a - CDATA section; otherwise returns \c false. - - \sa isCharacters(), text() -*/ -bool QXmlStreamReader::isCDATA() const -{ - Q_D(const QXmlStreamReader); - return d->type == QXmlStreamReader::Characters && d->isCDATA; -} - - - -/*! - Returns \c true if this document has been declared standalone in the - XML declaration; otherwise returns \c false. - - If no XML declaration has been parsed, this function returns \c false. - */ -bool QXmlStreamReader::isStandaloneDocument() const -{ - Q_D(const QXmlStreamReader); - return d->standalone; -} - - -/*! - \since 4.4 - - If the tokenType() is \l StartDocument, this function returns the - version string as specified in the XML declaration. - Otherwise an empty string is returned. - */ -QStringRef QXmlStreamReader::documentVersion() const -{ - Q_D(const QXmlStreamReader); - if (d->type == QXmlStreamReader::StartDocument) - return d->documentVersion; - return QStringRef(); -} - -/*! - \since 4.4 - - If the tokenType() is \l StartDocument, this function returns the - encoding string as specified in the XML declaration. - Otherwise an empty string is returned. - */ -QStringRef QXmlStreamReader::documentEncoding() const -{ - Q_D(const QXmlStreamReader); - if (d->type == QXmlStreamReader::StartDocument) - return d->documentEncoding; - return QStringRef(); -} - -#endif // QT_NO_XMLSTREAMREADER - -/*! - \class QXmlStreamWriter - \inmodule QtCore - \since 4.3 - \reentrant - - \brief The QXmlStreamWriter class provides an XML writer with a - simple streaming API. - - \ingroup xml-tools - - QXmlStreamWriter is the counterpart to QXmlStreamReader for writing - XML. Like its related class, it operates on a QIODevice specified - with setDevice(). The API is simple and straightforward: for every - XML token or event you want to write, the writer provides a - specialized function. - - You start a document with writeStartDocument() and end it with - writeEndDocument(). This will implicitly close all remaining open - tags. - - Element tags are opened with writeStartElement() followed by - writeAttribute() or writeAttributes(), element content, and then - writeEndElement(). A shorter form writeEmptyElement() can be used - to write empty elements, followed by writeAttributes(). - - Element content consists of either characters, entity references or - nested elements. It is written with writeCharacters(), which also - takes care of escaping all forbidden characters and character - sequences, writeEntityReference(), or subsequent calls to - writeStartElement(). A convenience method writeTextElement() can be - used for writing terminal elements that contain nothing but text. - - The following abridged code snippet shows the basic use of the class - to write formatted XML with indentation: - - \snippet qxmlstreamwriter/main.cpp start stream - \dots - \snippet qxmlstreamwriter/main.cpp write element - \dots - \snippet qxmlstreamwriter/main.cpp finish stream - - QXmlStreamWriter takes care of prefixing namespaces, all you have to - do is specify the \c namespaceUri when writing elements or - attributes. If you must conform to certain prefixes, you can force - the writer to use them by declaring the namespaces manually with - either writeNamespace() or writeDefaultNamespace(). Alternatively, - you can bypass the stream writer's namespace support and use - overloaded methods that take a qualified name instead. The namespace - \e http://www.w3.org/XML/1998/namespace is implicit and mapped to the - prefix \e xml. - - The stream writer can automatically format the generated XML data by - adding line-breaks and indentation to empty sections between - elements, making the XML data more readable for humans and easier to - work with for most source code management systems. The feature can - be turned on with the \l autoFormatting property, and customized - with the \l autoFormattingIndent property. - - Other functions are writeCDATA(), writeComment(), - writeProcessingInstruction(), and writeDTD(). Chaining of XML - streams is supported with writeCurrentToken(). - - By default, QXmlStreamWriter encodes XML in UTF-8. Different - encodings can be enforced using setCodec(). - - If an error occurs while writing to the underlying device, hasError() - starts returning true and subsequent writes are ignored. - - The \l{QXmlStream Bookmarks Example} illustrates how to use a - stream writer to write an XML bookmark file (XBEL) that - was previously read in by a QXmlStreamReader. - -*/ - -#ifndef QT_NO_XMLSTREAMWRITER - -class QXmlStreamWriterPrivate : public QXmlStreamPrivateTagStack { - QXmlStreamWriter *q_ptr; - Q_DECLARE_PUBLIC(QXmlStreamWriter) -public: - QXmlStreamWriterPrivate(QXmlStreamWriter *q); - ~QXmlStreamWriterPrivate() { - if (deleteDevice) - delete device; -#ifndef QT_NO_TEXTCODEC - delete encoder; -#endif - } - - void write(const QStringRef &); - void write(const QString &); - void writeEscaped(const QString &, bool escapeWhitespace = false); - void write(const char *s, int len); - template void write(const char (&s)[N]) { write(s, N - 1); } - bool finishStartElement(bool contents = true); - void writeStartElement(const QString &namespaceUri, const QString &name); - QIODevice *device; - QString *stringDevice; - uint deleteDevice :1; - uint inStartElement :1; - uint inEmptyElement :1; - uint lastWasStartElement :1; - uint wroteSomething :1; - uint hasIoError :1; - uint hasEncodingError :1; - uint autoFormatting :1; - uint isCodecASCIICompatible :1; - QByteArray autoFormattingIndent; - NamespaceDeclaration emptyNamespace; - int lastNamespaceDeclaration; - -#ifndef QT_NO_TEXTCODEC - QTextCodec *codec; - QTextEncoder *encoder; -#endif - void checkIfASCIICompatibleCodec(); - - NamespaceDeclaration &findNamespace(const QString &namespaceUri, bool writeDeclaration = false, bool noDefault = false); - void writeNamespaceDeclaration(const NamespaceDeclaration &namespaceDeclaration); - - int namespacePrefixCount; - - void indent(int level); -}; - - -QXmlStreamWriterPrivate::QXmlStreamWriterPrivate(QXmlStreamWriter *q) - :autoFormattingIndent(4, ' ') -{ - q_ptr = q; - device = 0; - stringDevice = 0; - deleteDevice = false; -#ifndef QT_NO_TEXTCODEC - codec = QTextCodec::codecForMib(106); // utf8 - encoder = codec->makeEncoder(QTextCodec::IgnoreHeader); // no byte order mark for utf8 -#endif - checkIfASCIICompatibleCodec(); - inStartElement = inEmptyElement = false; - wroteSomething = false; - hasIoError = false; - hasEncodingError = false; - lastWasStartElement = false; - lastNamespaceDeclaration = 1; - autoFormatting = false; - namespacePrefixCount = 0; -} - -void QXmlStreamWriterPrivate::checkIfASCIICompatibleCodec() -{ -#ifndef QT_NO_TEXTCODEC - Q_ASSERT(encoder); - // test ASCII-compatibility using the letter 'a' - QChar letterA = QLatin1Char('a'); - const QByteArray bytesA = encoder->fromUnicode(&letterA, 1); - const bool isCodecASCIICompatibleA = (bytesA.count() == 1) && (bytesA[0] == 0x61) ; - QChar letterLess = QLatin1Char('<'); - const QByteArray bytesLess = encoder->fromUnicode(&letterLess, 1); - const bool isCodecASCIICompatibleLess = (bytesLess.count() == 1) && (bytesLess[0] == 0x3C) ; - isCodecASCIICompatible = isCodecASCIICompatibleA && isCodecASCIICompatibleLess ; -#else - isCodecASCIICompatible = true; -#endif -} - -void QXmlStreamWriterPrivate::write(const QStringRef &s) -{ - if (device) { - if (hasIoError) - return; -#ifdef QT_NO_TEXTCODEC - QByteArray bytes = s.toLatin1(); -#else - QByteArray bytes = encoder->fromUnicode(s.constData(), s.size()); - if (encoder->hasFailure()) { - hasEncodingError = true; - return; - } -#endif - if (device->write(bytes) != bytes.size()) - hasIoError = true; - } - else if (stringDevice) - s.appendTo(stringDevice); - else - qWarning("QXmlStreamWriter: No device"); -} - -void QXmlStreamWriterPrivate::write(const QString &s) -{ - if (device) { - if (hasIoError) - return; -#ifdef QT_NO_TEXTCODEC - QByteArray bytes = s.toLatin1(); -#else - QByteArray bytes = encoder->fromUnicode(s); - if (encoder->hasFailure()) { - hasEncodingError = true; - return; - } -#endif - if (device->write(bytes) != bytes.size()) - hasIoError = true; - } - else if (stringDevice) - stringDevice->append(s); - else - qWarning("QXmlStreamWriter: No device"); -} - -void QXmlStreamWriterPrivate::writeEscaped(const QString &s, bool escapeWhitespace) -{ - QString escaped; - escaped.reserve(s.size()); - for ( int i = 0; i < s.size(); ++i ) { - QChar c = s.at(i); - switch (c.unicode()) { - case '<': - escaped.append(QLatin1String("<")); - break; - case '>': - escaped.append(QLatin1String(">")); - break; - case '&': - escaped.append(QLatin1String("&")); - break; - case '\"': - escaped.append(QLatin1String(""")); - break; - case '\t': - if (escapeWhitespace) - escaped.append(QLatin1String(" ")); - else - escaped += c; - break; - case '\n': - if (escapeWhitespace) - escaped.append(QLatin1String(" ")); - else - escaped += c; - break; - case '\v': - case '\f': - hasEncodingError = true; - break; - case '\r': - if (escapeWhitespace) - escaped.append(QLatin1String(" ")); - else - escaped += c; - break; - default: - if (c.unicode() > 0x1f && c.unicode() < 0xfffe) - escaped += c; - else - hasEncodingError = true; - break; - } - } - write(escaped); -} - -// Converts from ASCII to output encoding -void QXmlStreamWriterPrivate::write(const char *s, int len) -{ - if (device) { - if (hasIoError) - return; - if (isCodecASCIICompatible) { - if (device->write(s, len) != len) - hasIoError = true; - return; - } - } - - write(QString::fromLatin1(s, len)); -} - -void QXmlStreamWriterPrivate::writeNamespaceDeclaration(const NamespaceDeclaration &namespaceDeclaration) { - if (namespaceDeclaration.prefix.isEmpty()) { - write(" xmlns=\""); - write(namespaceDeclaration.namespaceUri); - write("\""); - } else { - write(" xmlns:"); - write(namespaceDeclaration.prefix); - write("=\""); - write(namespaceDeclaration.namespaceUri); - write("\""); - } -} - -bool QXmlStreamWriterPrivate::finishStartElement(bool contents) -{ - bool hadSomethingWritten = wroteSomething; - wroteSomething = contents; - if (!inStartElement) - return hadSomethingWritten; - - if (inEmptyElement) { - write("/>"); - QXmlStreamWriterPrivate::Tag &tag = tagStack_pop(); - lastNamespaceDeclaration = tag.namespaceDeclarationsSize; - lastWasStartElement = false; - } else { - write(">"); - } - inStartElement = inEmptyElement = false; - lastNamespaceDeclaration = namespaceDeclarations.size(); - return hadSomethingWritten; -} - -QXmlStreamPrivateTagStack::NamespaceDeclaration &QXmlStreamWriterPrivate::findNamespace(const QString &namespaceUri, bool writeDeclaration, bool noDefault) -{ - for (int j = namespaceDeclarations.size() - 1; j >= 0; --j) { - NamespaceDeclaration &namespaceDeclaration = namespaceDeclarations[j]; - if (namespaceDeclaration.namespaceUri == namespaceUri) { - if (!noDefault || !namespaceDeclaration.prefix.isEmpty()) - return namespaceDeclaration; - } - } - if (namespaceUri.isEmpty()) - return emptyNamespace; - NamespaceDeclaration &namespaceDeclaration = namespaceDeclarations.push(); - if (namespaceUri.isEmpty()) { - namespaceDeclaration.prefix.clear(); - } else { - QString s; - int n = ++namespacePrefixCount; - forever { - s = QLatin1Char('n') + QString::number(n++); - int j = namespaceDeclarations.size() - 2; - while (j >= 0 && namespaceDeclarations.at(j).prefix != s) - --j; - if (j < 0) - break; - } - namespaceDeclaration.prefix = addToStringStorage(s); - } - namespaceDeclaration.namespaceUri = addToStringStorage(namespaceUri); - if (writeDeclaration) - writeNamespaceDeclaration(namespaceDeclaration); - return namespaceDeclaration; -} - - - -void QXmlStreamWriterPrivate::indent(int level) -{ - write("\n"); - for (int i = level; i > 0; --i) - write(autoFormattingIndent.constData(), autoFormattingIndent.length()); -} - - -/*! - Constructs a stream writer. - - \sa setDevice() - */ -QXmlStreamWriter::QXmlStreamWriter() - : d_ptr(new QXmlStreamWriterPrivate(this)) -{ -} - -/*! - Constructs a stream writer that writes into \a device; - */ -QXmlStreamWriter::QXmlStreamWriter(QIODevice *device) - : d_ptr(new QXmlStreamWriterPrivate(this)) -{ - Q_D(QXmlStreamWriter); - d->device = device; -} - -/*! Constructs a stream writer that writes into \a array. This is the - same as creating an xml writer that operates on a QBuffer device - which in turn operates on \a array. - */ -QXmlStreamWriter::QXmlStreamWriter(QByteArray *array) - : d_ptr(new QXmlStreamWriterPrivate(this)) -{ - Q_D(QXmlStreamWriter); - d->device = new QBuffer(array); - d->device->open(QIODevice::WriteOnly); - d->deleteDevice = true; -} - - -/*! Constructs a stream writer that writes into \a string. - */ -QXmlStreamWriter::QXmlStreamWriter(QString *string) - : d_ptr(new QXmlStreamWriterPrivate(this)) -{ - Q_D(QXmlStreamWriter); - d->stringDevice = string; -} - -/*! - Destructor. -*/ -QXmlStreamWriter::~QXmlStreamWriter() -{ -} - - -/*! - Sets the current device to \a device. If you want the stream to - write into a QByteArray, you can create a QBuffer device. - - \sa device() -*/ -void QXmlStreamWriter::setDevice(QIODevice *device) -{ - Q_D(QXmlStreamWriter); - if (device == d->device) - return; - d->stringDevice = 0; - if (d->deleteDevice) { - delete d->device; - d->deleteDevice = false; - } - d->device = device; -} - -/*! - Returns the current device associated with the QXmlStreamWriter, - or 0 if no device has been assigned. - - \sa setDevice() -*/ -QIODevice *QXmlStreamWriter::device() const -{ - Q_D(const QXmlStreamWriter); - return d->device; -} - - -#ifndef QT_NO_TEXTCODEC -/*! - Sets the codec for this stream to \a codec. The codec is used for - encoding any data that is written. By default, QXmlStreamWriter - uses UTF-8. - - The encoding information is stored in the initial xml tag which - gets written when you call writeStartDocument(). Call this - function before calling writeStartDocument(). - - \sa codec() -*/ -void QXmlStreamWriter::setCodec(QTextCodec *codec) -{ - Q_D(QXmlStreamWriter); - if (codec) { - d->codec = codec; - delete d->encoder; - d->encoder = codec->makeEncoder(QTextCodec::IgnoreHeader); // no byte order mark for utf8 - d->checkIfASCIICompatibleCodec(); - } -} - -/*! - Sets the codec for this stream to the QTextCodec for the encoding - specified by \a codecName. Common values for \c codecName include - "ISO 8859-1", "UTF-8", and "UTF-16". If the encoding isn't - recognized, nothing happens. - - \sa QTextCodec::codecForName() -*/ -void QXmlStreamWriter::setCodec(const char *codecName) -{ - setCodec(QTextCodec::codecForName(codecName)); -} - -/*! - Returns the codec that is currently assigned to the stream. - - \sa setCodec() -*/ -QTextCodec *QXmlStreamWriter::codec() const -{ - Q_D(const QXmlStreamWriter); - return d->codec; -} -#endif // QT_NO_TEXTCODEC - -/*! - \property QXmlStreamWriter::autoFormatting - \since 4.4 - The auto-formatting flag of the stream writer - - This property controls whether or not the stream writer - automatically formats the generated XML data. If enabled, the - writer automatically adds line-breaks and indentation to empty - sections between elements (ignorable whitespace). The main purpose - of auto-formatting is to split the data into several lines, and to - increase readability for a human reader. The indentation depth can - be controlled through the \l autoFormattingIndent property. - - By default, auto-formatting is disabled. -*/ - -/*! - \since 4.4 - - Enables auto formatting if \a enable is \c true, otherwise - disables it. - - The default value is \c false. - */ -void QXmlStreamWriter::setAutoFormatting(bool enable) -{ - Q_D(QXmlStreamWriter); - d->autoFormatting = enable; -} - -/*! - \since 4.4 - - Returns \c true if auto formattting is enabled, otherwise \c false. - */ -bool QXmlStreamWriter::autoFormatting() const -{ - Q_D(const QXmlStreamWriter); - return d->autoFormatting; -} - -/*! - \property QXmlStreamWriter::autoFormattingIndent - \since 4.4 - - \brief the number of spaces or tabs used for indentation when - auto-formatting is enabled. Positive numbers indicate spaces, - negative numbers tabs. - - The default indentation is 4. - - \sa autoFormatting -*/ - - -void QXmlStreamWriter::setAutoFormattingIndent(int spacesOrTabs) -{ - Q_D(QXmlStreamWriter); - d->autoFormattingIndent = QByteArray(qAbs(spacesOrTabs), spacesOrTabs >= 0 ? ' ' : '\t'); -} - -int QXmlStreamWriter::autoFormattingIndent() const -{ - Q_D(const QXmlStreamWriter); - return d->autoFormattingIndent.count(' ') - d->autoFormattingIndent.count('\t'); -} - -/*! - Returns \c true if writing failed. - - This can happen if the stream failed to write to the underlying - device or if the data to be written contained invalid characters. - - The error status is never reset. Writes happening after the error - occurred may be ignored, even if the error condition is cleared. - */ -bool QXmlStreamWriter::hasError() const -{ - Q_D(const QXmlStreamWriter); - return d->hasIoError || d->hasEncodingError; -} - -/*! - \overload - Writes an attribute with \a qualifiedName and \a value. - - - This function can only be called after writeStartElement() before - any content is written, or after writeEmptyElement(). - */ -void QXmlStreamWriter::writeAttribute(const QString &qualifiedName, const QString &value) -{ - Q_D(QXmlStreamWriter); - Q_ASSERT(d->inStartElement); - Q_ASSERT(qualifiedName.count(QLatin1Char(':')) <= 1); - d->write(" "); - d->write(qualifiedName); - d->write("=\""); - d->writeEscaped(value, true); - d->write("\""); -} - -/*! Writes an attribute with \a name and \a value, prefixed for - the specified \a namespaceUri. If the namespace has not been - declared yet, QXmlStreamWriter will generate a namespace declaration - for it. - - This function can only be called after writeStartElement() before - any content is written, or after writeEmptyElement(). - */ -void QXmlStreamWriter::writeAttribute(const QString &namespaceUri, const QString &name, const QString &value) -{ - Q_D(QXmlStreamWriter); - Q_ASSERT(d->inStartElement); - Q_ASSERT(!name.contains(QLatin1Char(':'))); - QXmlStreamWriterPrivate::NamespaceDeclaration &namespaceDeclaration = d->findNamespace(namespaceUri, true, true); - d->write(" "); - if (!namespaceDeclaration.prefix.isEmpty()) { - d->write(namespaceDeclaration.prefix); - d->write(":"); - } - d->write(name); - d->write("=\""); - d->writeEscaped(value, true); - d->write("\""); -} - -/*! - \overload - - Writes the \a attribute. - - This function can only be called after writeStartElement() before - any content is written, or after writeEmptyElement(). - */ -void QXmlStreamWriter::writeAttribute(const QXmlStreamAttribute& attribute) -{ - if (attribute.namespaceUri().isEmpty()) - writeAttribute(attribute.qualifiedName().toString(), - attribute.value().toString()); - else - writeAttribute(attribute.namespaceUri().toString(), - attribute.name().toString(), - attribute.value().toString()); -} - - -/*! Writes the attribute vector \a attributes. If a namespace - referenced in an attribute not been declared yet, QXmlStreamWriter - will generate a namespace declaration for it. - - This function can only be called after writeStartElement() before - any content is written, or after writeEmptyElement(). - - \sa writeAttribute(), writeNamespace() - */ -void QXmlStreamWriter::writeAttributes(const QXmlStreamAttributes& attributes) -{ - Q_D(QXmlStreamWriter); - Q_ASSERT(d->inStartElement); - Q_UNUSED(d); - for (int i = 0; i < attributes.size(); ++i) - writeAttribute(attributes.at(i)); -} - - -/*! Writes \a text as CDATA section. If \a text contains the - forbidden character sequence "]]>", it is split into different CDATA - sections. - - This function mainly exists for completeness. Normally you should - not need use it, because writeCharacters() automatically escapes all - non-content characters. - */ -void QXmlStreamWriter::writeCDATA(const QString &text) -{ - Q_D(QXmlStreamWriter); - d->finishStartElement(); - QString copy(text); - copy.replace(QLatin1String("]]>"), QLatin1String("]]]]>")); - d->write("write(copy); - d->write("]]>"); -} - - -/*! Writes \a text. The characters "<", "&", and "\"" are escaped as entity - references "<", "&, and """. To avoid the forbidden sequence - "]]>", ">" is also escaped as ">". - - \sa writeEntityReference() - */ -void QXmlStreamWriter::writeCharacters(const QString &text) -{ - Q_D(QXmlStreamWriter); - d->finishStartElement(); - d->writeEscaped(text); -} - - -/*! Writes \a text as XML comment, where \a text must not contain the - forbidden sequence "--" or end with "-". Note that XML does not - provide any way to escape "-" in a comment. - */ -void QXmlStreamWriter::writeComment(const QString &text) -{ - Q_D(QXmlStreamWriter); - Q_ASSERT(!text.contains(QLatin1String("--")) && !text.endsWith(QLatin1Char('-'))); - if (!d->finishStartElement(false) && d->autoFormatting) - d->indent(d->tagStack.size()); - d->write(""); - d->inStartElement = d->lastWasStartElement = false; -} - - -/*! Writes a DTD section. The \a dtd represents the entire - doctypedecl production from the XML 1.0 specification. - */ -void QXmlStreamWriter::writeDTD(const QString &dtd) -{ - Q_D(QXmlStreamWriter); - d->finishStartElement(); - if (d->autoFormatting) - d->write("\n"); - d->write(dtd); - if (d->autoFormatting) - d->write("\n"); -} - - - -/*! \overload - Writes an empty element with qualified name \a qualifiedName. - Subsequent calls to writeAttribute() will add attributes to this element. -*/ -void QXmlStreamWriter::writeEmptyElement(const QString &qualifiedName) -{ - Q_D(QXmlStreamWriter); - Q_ASSERT(qualifiedName.count(QLatin1Char(':')) <= 1); - d->writeStartElement(QString(), qualifiedName); - d->inEmptyElement = true; -} - - -/*! Writes an empty element with \a name, prefixed for the specified - \a namespaceUri. If the namespace has not been declared, - QXmlStreamWriter will generate a namespace declaration for it. - Subsequent calls to writeAttribute() will add attributes to this element. - - \sa writeNamespace() - */ -void QXmlStreamWriter::writeEmptyElement(const QString &namespaceUri, const QString &name) -{ - Q_D(QXmlStreamWriter); - Q_ASSERT(!name.contains(QLatin1Char(':'))); - d->writeStartElement(namespaceUri, name); - d->inEmptyElement = true; -} - - -/*!\overload - Writes a text element with \a qualifiedName and \a text. - - - This is a convenience function equivalent to: - \snippet code/src_corelib_xml_qxmlstream.cpp 1 - -*/ -void QXmlStreamWriter::writeTextElement(const QString &qualifiedName, const QString &text) -{ - writeStartElement(qualifiedName); - writeCharacters(text); - writeEndElement(); -} - -/*! Writes a text element with \a name, prefixed for the specified \a - namespaceUri, and \a text. If the namespace has not been - declared, QXmlStreamWriter will generate a namespace declaration - for it. - - - This is a convenience function equivalent to: - \snippet code/src_corelib_xml_qxmlstream.cpp 2 - -*/ -void QXmlStreamWriter::writeTextElement(const QString &namespaceUri, const QString &name, const QString &text) -{ - writeStartElement(namespaceUri, name); - writeCharacters(text); - writeEndElement(); -} - - -/*! - Closes all remaining open start elements and writes a newline. - - \sa writeStartDocument() - */ -void QXmlStreamWriter::writeEndDocument() -{ - Q_D(QXmlStreamWriter); - while (d->tagStack.size()) - writeEndElement(); - d->write("\n"); -} - -/*! - Closes the previous start element. - - \sa writeStartElement() - */ -void QXmlStreamWriter::writeEndElement() -{ - Q_D(QXmlStreamWriter); - if (d->tagStack.isEmpty()) - return; - - // shortcut: if nothing was written, close as empty tag - if (d->inStartElement && !d->inEmptyElement) { - d->write("/>"); - d->lastWasStartElement = d->inStartElement = false; - QXmlStreamWriterPrivate::Tag &tag = d->tagStack_pop(); - d->lastNamespaceDeclaration = tag.namespaceDeclarationsSize; - return; - } - - if (!d->finishStartElement(false) && !d->lastWasStartElement && d->autoFormatting) - d->indent(d->tagStack.size()-1); - if (d->tagStack.isEmpty()) - return; - d->lastWasStartElement = false; - QXmlStreamWriterPrivate::Tag &tag = d->tagStack_pop(); - d->lastNamespaceDeclaration = tag.namespaceDeclarationsSize; - d->write("write(tag.namespaceDeclaration.prefix); - d->write(":"); - } - d->write(tag.name); - d->write(">"); -} - - - -/*! - Writes the entity reference \a name to the stream, as "&\a{name};". - */ -void QXmlStreamWriter::writeEntityReference(const QString &name) -{ - Q_D(QXmlStreamWriter); - d->finishStartElement(); - d->write("&"); - d->write(name); - d->write(";"); -} - - -/*! Writes a namespace declaration for \a namespaceUri with \a - prefix. If \a prefix is empty, QXmlStreamWriter assigns a unique - prefix consisting of the letter 'n' followed by a number. - - If writeStartElement() or writeEmptyElement() was called, the - declaration applies to the current element; otherwise it applies to - the next child element. - - Note that the prefix \e xml is both predefined and reserved for - \e http://www.w3.org/XML/1998/namespace, which in turn cannot be - bound to any other prefix. The prefix \e xmlns and its URI - \e http://www.w3.org/2000/xmlns/ are used for the namespace mechanism - itself and thus completely forbidden in declarations. - - */ -void QXmlStreamWriter::writeNamespace(const QString &namespaceUri, const QString &prefix) -{ - Q_D(QXmlStreamWriter); - Q_ASSERT(prefix != QLatin1String("xmlns")); - if (prefix.isEmpty()) { - d->findNamespace(namespaceUri, d->inStartElement); - } else { - Q_ASSERT(!((prefix == QLatin1String("xml")) ^ (namespaceUri == QLatin1String("http://www.w3.org/XML/1998/namespace")))); - Q_ASSERT(namespaceUri != QLatin1String("http://www.w3.org/2000/xmlns/")); - QXmlStreamWriterPrivate::NamespaceDeclaration &namespaceDeclaration = d->namespaceDeclarations.push(); - namespaceDeclaration.prefix = d->addToStringStorage(prefix); - namespaceDeclaration.namespaceUri = d->addToStringStorage(namespaceUri); - if (d->inStartElement) - d->writeNamespaceDeclaration(namespaceDeclaration); - } -} - - -/*! Writes a default namespace declaration for \a namespaceUri. - - If writeStartElement() or writeEmptyElement() was called, the - declaration applies to the current element; otherwise it applies to - the next child element. - - Note that the namespaces \e http://www.w3.org/XML/1998/namespace - (bound to \e xmlns) and \e http://www.w3.org/2000/xmlns/ (bound to - \e xml) by definition cannot be declared as default. - */ -void QXmlStreamWriter::writeDefaultNamespace(const QString &namespaceUri) -{ - Q_D(QXmlStreamWriter); - Q_ASSERT(namespaceUri != QLatin1String("http://www.w3.org/XML/1998/namespace")); - Q_ASSERT(namespaceUri != QLatin1String("http://www.w3.org/2000/xmlns/")); - QXmlStreamWriterPrivate::NamespaceDeclaration &namespaceDeclaration = d->namespaceDeclarations.push(); - namespaceDeclaration.prefix.clear(); - namespaceDeclaration.namespaceUri = d->addToStringStorage(namespaceUri); - if (d->inStartElement) - d->writeNamespaceDeclaration(namespaceDeclaration); -} - - -/*! - Writes an XML processing instruction with \a target and \a data, - where \a data must not contain the sequence "?>". - */ -void QXmlStreamWriter::writeProcessingInstruction(const QString &target, const QString &data) -{ - Q_D(QXmlStreamWriter); - Q_ASSERT(!data.contains(QLatin1String("?>"))); - if (!d->finishStartElement(false) && d->autoFormatting) - d->indent(d->tagStack.size()); - d->write("write(target); - if (!data.isNull()) { - d->write(" "); - d->write(data); - } - d->write("?>"); -} - - - -/*!\overload - - Writes a document start with XML version number "1.0". This also - writes the encoding information. - - \sa writeEndDocument(), setCodec() - \since 4.5 - */ -void QXmlStreamWriter::writeStartDocument() -{ - writeStartDocument(QLatin1String("1.0")); -} - - -/*! - Writes a document start with the XML version number \a version. - - \sa writeEndDocument() - */ -void QXmlStreamWriter::writeStartDocument(const QString &version) -{ - Q_D(QXmlStreamWriter); - d->finishStartElement(false); - d->write("write(version); - if (d->device) { // stringDevice does not get any encoding - d->write("\" encoding=\""); -#ifdef QT_NO_TEXTCODEC - d->write("iso-8859-1"); -#else - const QByteArray name = d->codec->name(); - d->write(name.constData(), name.length()); -#endif - } - d->write("\"?>"); -} - -/*! Writes a document start with the XML version number \a version - and a standalone attribute \a standalone. - - \sa writeEndDocument() - \since 4.5 - */ -void QXmlStreamWriter::writeStartDocument(const QString &version, bool standalone) -{ - Q_D(QXmlStreamWriter); - d->finishStartElement(false); - d->write("write(version); - if (d->device) { // stringDevice does not get any encoding - d->write("\" encoding=\""); -#ifdef QT_NO_TEXTCODEC - d->write("iso-8859-1"); -#else - const QByteArray name = d->codec->name(); - d->write(name.constData(), name.length()); -#endif - } - if (standalone) - d->write("\" standalone=\"yes\"?>"); - else - d->write("\" standalone=\"no\"?>"); -} - - -/*!\overload - - Writes a start element with \a qualifiedName. Subsequent calls to - writeAttribute() will add attributes to this element. - - \sa writeEndElement(), writeEmptyElement() - */ -void QXmlStreamWriter::writeStartElement(const QString &qualifiedName) -{ - Q_D(QXmlStreamWriter); - Q_ASSERT(qualifiedName.count(QLatin1Char(':')) <= 1); - d->writeStartElement(QString(), qualifiedName); -} - - -/*! Writes a start element with \a name, prefixed for the specified - \a namespaceUri. If the namespace has not been declared yet, - QXmlStreamWriter will generate a namespace declaration for - it. Subsequent calls to writeAttribute() will add attributes to this - element. - - \sa writeNamespace(), writeEndElement(), writeEmptyElement() - */ -void QXmlStreamWriter::writeStartElement(const QString &namespaceUri, const QString &name) -{ - Q_D(QXmlStreamWriter); - Q_ASSERT(!name.contains(QLatin1Char(':'))); - d->writeStartElement(namespaceUri, name); -} - -void QXmlStreamWriterPrivate::writeStartElement(const QString &namespaceUri, const QString &name) -{ - if (!finishStartElement(false) && autoFormatting) - indent(tagStack.size()); - - Tag &tag = tagStack_push(); - tag.name = addToStringStorage(name); - tag.namespaceDeclaration = findNamespace(namespaceUri); - write("<"); - if (!tag.namespaceDeclaration.prefix.isEmpty()) { - write(tag.namespaceDeclaration.prefix); - write(":"); - } - write(tag.name); - inStartElement = lastWasStartElement = true; - - for (int i = lastNamespaceDeclaration; i < namespaceDeclarations.size(); ++i) - writeNamespaceDeclaration(namespaceDeclarations[i]); - tag.namespaceDeclarationsSize = lastNamespaceDeclaration; -} - -#ifndef QT_NO_XMLSTREAMREADER -/*! Writes the current state of the \a reader. All possible valid - states are supported. - - The purpose of this function is to support chained processing of XML data. - - \sa QXmlStreamReader::tokenType() - */ -void QXmlStreamWriter::writeCurrentToken(const QXmlStreamReader &reader) -{ - switch (reader.tokenType()) { - case QXmlStreamReader::NoToken: - break; - case QXmlStreamReader::StartDocument: - writeStartDocument(); - break; - case QXmlStreamReader::EndDocument: - writeEndDocument(); - break; - case QXmlStreamReader::StartElement: { - QXmlStreamNamespaceDeclarations namespaceDeclarations = reader.namespaceDeclarations(); - for (int i = 0; i < namespaceDeclarations.size(); ++i) { - const QXmlStreamNamespaceDeclaration &namespaceDeclaration = namespaceDeclarations.at(i); - writeNamespace(namespaceDeclaration.namespaceUri().toString(), - namespaceDeclaration.prefix().toString()); - } - writeStartElement(reader.namespaceUri().toString(), reader.name().toString()); - writeAttributes(reader.attributes()); - } break; - case QXmlStreamReader::EndElement: - writeEndElement(); - break; - case QXmlStreamReader::Characters: - if (reader.isCDATA()) - writeCDATA(reader.text().toString()); - else - writeCharacters(reader.text().toString()); - break; - case QXmlStreamReader::Comment: - writeComment(reader.text().toString()); - break; - case QXmlStreamReader::DTD: - writeDTD(reader.text().toString()); - break; - case QXmlStreamReader::EntityReference: - writeEntityReference(reader.name().toString()); - break; - case QXmlStreamReader::ProcessingInstruction: - writeProcessingInstruction(reader.processingInstructionTarget().toString(), - reader.processingInstructionData().toString()); - break; - default: - Q_ASSERT(reader.tokenType() != QXmlStreamReader::Invalid); - qWarning("QXmlStreamWriter: writeCurrentToken() with invalid state."); - break; - } -} - -/*! - \fn bool QXmlStreamAttributes::hasAttribute(const QString &qualifiedName) const - \since 4.5 - - Returns \c true if this QXmlStreamAttributes has an attribute whose - qualified name is \a qualifiedName; otherwise returns \c false. - - Note that this is not namespace aware. For instance, if this - QXmlStreamAttributes contains an attribute whose lexical name is "xlink:href" - this doesn't tell that an attribute named \c href in the XLink namespace is - present, since the \c xlink prefix can be bound to any namespace. Use the - overload that takes a namespace URI and a local name as parameter, for - namespace aware code. -*/ - -/*! - \fn bool QXmlStreamAttributes::hasAttribute(QLatin1String qualifiedName) const - \overload - \since 4.5 -*/ - -/*! - \fn bool QXmlStreamAttributes::hasAttribute(const QString &namespaceUri, - const QString &name) const - \overload - \since 4.5 - - Returns \c true if this QXmlStreamAttributes has an attribute whose - namespace URI and name correspond to \a namespaceUri and \a name; - otherwise returns \c false. -*/ - -#endif // QT_NO_XMLSTREAMREADER -#endif // QT_NO_XMLSTREAMWRITER - -QT_END_NAMESPACE - -#endif // QT_NO_XMLSTREAM diff --git a/src/corelib/xml/qxmlstream.g b/src/corelib/xml/qxmlstream.g deleted file mode 100644 index fd69a6e4af..0000000000 --- a/src/corelib/xml/qxmlstream.g +++ /dev/null @@ -1,1852 +0,0 @@ ----------------------------------------------------------------------------- --- --- Copyright (C) 2016 The Qt Company Ltd. --- Contact: https://www.qt.io/licensing/ --- --- This file is part of the QtCore module of the Qt Toolkit. --- --- $QT_BEGIN_LICENSE:LGPL$ --- Commercial License Usage --- Licensees holding valid commercial Qt licenses may use this file in --- accordance with the commercial license agreement provided with the --- Software or, alternatively, in accordance with the terms contained in --- a written agreement between you and The Qt Company. For licensing terms --- and conditions see https://www.qt.io/terms-conditions. For further --- information use the contact form at https://www.qt.io/contact-us. --- --- GNU Lesser General Public License Usage --- Alternatively, this file may be used under the terms of the GNU Lesser --- General Public License version 3 as published by the Free Software --- Foundation and appearing in the file LICENSE.LGPL3 included in the --- packaging of this file. Please review the following information to --- ensure the GNU Lesser General Public License version 3 requirements --- will be met: https://www.gnu.org/licenses/lgpl-3.0.html. --- --- GNU General Public License Usage --- Alternatively, this file may be used under the terms of the GNU --- General Public License version 2.0 or (at your option) the GNU General --- Public license version 3 or any later version approved by the KDE Free --- Qt Foundation. The licenses are as published by the Free Software --- Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 --- included in the packaging of this file. Please review the following --- information to ensure the GNU General Public License requirements will --- be met: https://www.gnu.org/licenses/gpl-2.0.html and --- https://www.gnu.org/licenses/gpl-3.0.html. --- --- $QT_END_LICENSE$ --- ----------------------------------------------------------------------------- - -%parser QXmlStreamReader_Table - -%merged_output qxmlstream_p.h - -%token NOTOKEN -%token SPACE " " -%token LANGLE "<" -%token RANGLE ">" -%token AMPERSAND "&" -%token HASH "#" -%token QUOTE "\'" -%token DBLQUOTE "\"" -%token LBRACK "[" -%token RBRACK "]" -%token LPAREN "(" -%token RPAREN ")" -%token PIPE "|" -%token EQ "=" -%token PERCENT "%" -%token SLASH "/" -%token COLON ":" -%token SEMICOLON ";" -%token COMMA "," -%token DASH "-" -%token PLUS "+" -%token STAR "*" -%token DOT "." -%token QUESTIONMARK "?" -%token BANG "!" -%token LETTER "[a-zA-Z]" -%token DIGIT "[0-9]" - --- after langle_bang -%token CDATA_START "[CDATA[" -%token DOCTYPE "DOCTYPE" -%token ELEMENT "ELEMENT" -%token ATTLIST "ATTLIST" -%token ENTITY "ENTITY" -%token NOTATION "NOTATION" - --- entity decl -%token SYSTEM "SYSTEM" -%token PUBLIC "PUBLIC" -%token NDATA "NDATA" - --- default decl -%token REQUIRED "REQUIRED" -%token IMPLIED "IMPLIED" -%token FIXED "FIXED" - --- conent spec -%token EMPTY "EMPTY" -%token ANY "ANY" -%token PCDATA "PCDATA" - --- error -%token ERROR - --- entities -%token PARSE_ENTITY -%token ENTITY_DONE -%token UNRESOLVED_ENTITY - --- att type -%token CDATA "CDATA" -%token ID "ID" -%token IDREF "IDREF" -%token IDREFS "IDREFS" -%token ENTITY "ENTITY" -%token ENTITIES "ENTITIES" -%token NMTOKEN "NMTOKEN" -%token NMTOKENS "NMTOKENS" - --- xml declaration -%token XML " class QXmlStreamSimpleStack { - T *data; - int tos, cap; -public: - inline QXmlStreamSimpleStack():data(0), tos(-1), cap(0){} - inline ~QXmlStreamSimpleStack(){ if (data) free(data); } - - inline void reserve(int extraCapacity) { - if (tos + extraCapacity + 1 > cap) { - cap = qMax(tos + extraCapacity + 1, cap << 1 ); - data = reinterpret_cast(realloc(data, cap * sizeof(T))); - Q_CHECK_PTR(data); - } - } - - inline T &push() { reserve(1); return data[++tos]; } - inline T &rawPush() { return data[++tos]; } - inline const T &top() const { return data[tos]; } - inline T &top() { return data[tos]; } - inline T &pop() { return data[tos--]; } - inline T &operator[](int index) { return data[index]; } - inline const T &at(int index) const { return data[index]; } - inline int size() const { return tos + 1; } - inline void resize(int s) { tos = s - 1; } - inline bool isEmpty() const { return tos < 0; } - inline void clear() { tos = -1; } -}; - - -class QXmlStream -{ - Q_DECLARE_TR_FUNCTIONS(QXmlStream) -}; - -class QXmlStreamPrivateTagStack { -public: - struct NamespaceDeclaration - { - QStringRef prefix; - QStringRef namespaceUri; - }; - - struct Tag - { - QStringRef name; - QStringRef qualifiedName; - NamespaceDeclaration namespaceDeclaration; - int tagStackStringStorageSize; - int namespaceDeclarationsSize; - }; - - - QXmlStreamPrivateTagStack(); - QXmlStreamSimpleStack namespaceDeclarations; - QString tagStackStringStorage; - int tagStackStringStorageSize; - int initialTagStackStringStorageSize; - bool tagsDone; - - inline QStringRef addToStringStorage(const QStringRef &s) { - return addToStringStorage(qToStringViewIgnoringNull(s)); - } - inline QStringRef addToStringStorage(const QString &s) { - return addToStringStorage(qToStringViewIgnoringNull(s)); - } - QStringRef addToStringStorage(QStringView s) - { - int pos = tagStackStringStorageSize; - int sz = s.size(); - if (pos != tagStackStringStorage.size()) - tagStackStringStorage.resize(pos); - tagStackStringStorage.append(s.data(), sz); - tagStackStringStorageSize += sz; - return QStringRef(&tagStackStringStorage, pos, sz); - } - - QXmlStreamSimpleStack tagStack; - - - inline Tag &tagStack_pop() { - Tag& tag = tagStack.pop(); - tagStackStringStorageSize = tag.tagStackStringStorageSize; - namespaceDeclarations.resize(tag.namespaceDeclarationsSize); - tagsDone = tagStack.isEmpty(); - return tag; - } - inline Tag &tagStack_push() { - Tag &tag = tagStack.push(); - tag.tagStackStringStorageSize = tagStackStringStorageSize; - tag.namespaceDeclarationsSize = namespaceDeclarations.size(); - return tag; - } -}; - - -class QXmlStreamEntityResolver; -#ifndef QT_NO_XMLSTREAMREADER -class QXmlStreamReaderPrivate : public QXmlStreamReader_Table, public QXmlStreamPrivateTagStack{ - QXmlStreamReader *q_ptr; - Q_DECLARE_PUBLIC(QXmlStreamReader) -public: - QXmlStreamReaderPrivate(QXmlStreamReader *q); - ~QXmlStreamReaderPrivate(); - void init(); - - QByteArray rawReadBuffer; - QByteArray dataBuffer; - uchar firstByte; - qint64 nbytesread; - QString readBuffer; - int readBufferPos; - QXmlStreamSimpleStack putStack; - struct Entity { - Entity() = default; - Entity(const QString &name, const QString &value) - : name(name), value(value), external(false), unparsed(false), literal(false), - hasBeenParsed(false), isCurrentlyReferenced(false){} - static inline Entity createLiteral(QLatin1String name, QLatin1String value) - { Entity result(name, value); result.literal = result.hasBeenParsed = true; return result; } - QString name, value; - uint external : 1; - uint unparsed : 1; - uint literal : 1; - uint hasBeenParsed : 1; - uint isCurrentlyReferenced : 1; - }; - // these hash tables use a QStringView as a key to avoid creating QStrings - // just for lookup. The keys are usually views into Entity::name and thus - // are guaranteed to have the same lifetime as the referenced data: - QHash entityHash; - QHash parameterEntityHash; - QXmlStreamSimpleStackentityReferenceStack; - inline bool referenceEntity(Entity &entity) { - if (entity.isCurrentlyReferenced) { - raiseWellFormedError(QXmlStream::tr("Recursive entity detected.")); - return false; - } - entity.isCurrentlyReferenced = true; - entityReferenceStack.push() = &entity; - injectToken(ENTITY_DONE); - return true; - } - - - QIODevice *device; - bool deleteDevice; -#ifndef QT_NO_TEXTCODEC - QTextCodec *codec; - QTextDecoder *decoder; -#endif - bool atEnd; - - /*! - \sa setType() - */ - QXmlStreamReader::TokenType type; - QXmlStreamReader::Error error; - QString errorString; - QString unresolvedEntity; - - qint64 lineNumber, lastLineStart, characterOffset; - - - void write(const QString &); - void write(const char *); - - - QXmlStreamAttributes attributes; - QStringRef namespaceForPrefix(const QStringRef &prefix); - void resolveTag(); - void resolvePublicNamespaces(); - void resolveDtd(); - uint resolveCharRef(int symbolIndex); - bool checkStartDocument(); - void startDocument(); - void parseError(); - void checkPublicLiteral(const QStringRef &publicId); - - bool scanDtd; - QStringRef lastAttributeValue; - bool lastAttributeIsCData; - struct DtdAttribute { - QStringRef tagName; - QStringRef attributeQualifiedName; - QStringRef attributePrefix; - QStringRef attributeName; - QStringRef defaultValue; - bool isCDATA; - bool isNamespaceAttribute; - }; - QXmlStreamSimpleStack dtdAttributes; - struct NotationDeclaration { - QStringRef name; - QStringRef publicId; - QStringRef systemId; - }; - QXmlStreamSimpleStack notationDeclarations; - QXmlStreamNotationDeclarations publicNotationDeclarations; - QXmlStreamNamespaceDeclarations publicNamespaceDeclarations; - - struct EntityDeclaration { - QStringRef name; - QStringRef notationName; - QStringRef publicId; - QStringRef systemId; - QStringRef value; - bool parameter; - bool external; - inline void clear() { - name.clear(); - notationName.clear(); - publicId.clear(); - systemId.clear(); - value.clear(); - parameter = external = false; - } - }; - QXmlStreamSimpleStack entityDeclarations; - QXmlStreamEntityDeclarations publicEntityDeclarations; - - QStringRef text; - - QStringRef prefix, namespaceUri, qualifiedName, name; - QStringRef processingInstructionTarget, processingInstructionData; - QStringRef dtdName, dtdPublicId, dtdSystemId; - QStringRef documentVersion, documentEncoding; - uint isEmptyElement : 1; - uint isWhitespace : 1; - uint isCDATA : 1; - uint standalone : 1; - uint hasCheckedStartDocument : 1; - uint normalizeLiterals : 1; - uint hasSeenTag : 1; - uint inParseEntity : 1; - uint referenceToUnparsedEntityDetected : 1; - uint referenceToParameterEntityDetected : 1; - uint hasExternalDtdSubset : 1; - uint lockEncoding : 1; - uint namespaceProcessing : 1; - - int resumeReduction; - void resume(int rule); - - inline bool entitiesMustBeDeclared() const { - return (!inParseEntity - && (standalone - || (!referenceToUnparsedEntityDetected - && !referenceToParameterEntityDetected // Errata 13 as of 2006-04-25 - && !hasExternalDtdSubset))); - } - - // qlalr parser - int tos; - int stack_size; - struct Value { - int pos; - int len; - int prefix; - ushort c; - }; - - Value *sym_stack; - int *state_stack; - inline void reallocateStack(); - inline Value &sym(int index) const - { return sym_stack[tos + index - 1]; } - QString textBuffer; - inline void clearTextBuffer() { - if (!scanDtd) { - textBuffer.resize(0); - textBuffer.reserve(256); - } - } - struct Attribute { - Value key; - Value value; - }; - QXmlStreamSimpleStack attributeStack; - - inline QStringRef symString(int index) { - const Value &symbol = sym(index); - return QStringRef(&textBuffer, symbol.pos + symbol.prefix, symbol.len - symbol.prefix); - } - QStringView symView(int index) const - { - const Value &symbol = sym(index); - return QStringView(textBuffer.data() + symbol.pos, symbol.len).mid(symbol.prefix); - } - inline QStringRef symName(int index) { - const Value &symbol = sym(index); - return QStringRef(&textBuffer, symbol.pos, symbol.len); - } - inline QStringRef symString(int index, int offset) { - const Value &symbol = sym(index); - return QStringRef(&textBuffer, symbol.pos + symbol.prefix + offset, symbol.len - symbol.prefix - offset); - } - inline QStringRef symPrefix(int index) { - const Value &symbol = sym(index); - if (symbol.prefix) - return QStringRef(&textBuffer, symbol.pos, symbol.prefix - 1); - return QStringRef(); - } - inline QStringRef symString(const Value &symbol) { - return QStringRef(&textBuffer, symbol.pos + symbol.prefix, symbol.len - symbol.prefix); - } - inline QStringRef symName(const Value &symbol) { - return QStringRef(&textBuffer, symbol.pos, symbol.len); - } - inline QStringRef symPrefix(const Value &symbol) { - if (symbol.prefix) - return QStringRef(&textBuffer, symbol.pos, symbol.prefix - 1); - return QStringRef(); - } - - inline void clearSym() { Value &val = sym(1); val.pos = textBuffer.size(); val.len = 0; } - - - short token; - uint token_char; - - uint filterCarriageReturn(); - inline uint getChar(); - inline uint peekChar(); - inline void putChar(uint c) { putStack.push() = c; } - inline void putChar(QChar c) { putStack.push() = c.unicode(); } - void putString(const QString &s, int from = 0); - void putStringLiteral(const QString &s); - void putReplacement(const QString &s); - void putReplacementInAttributeValue(const QString &s); - uint getChar_helper(); - - bool scanUntil(const char *str, short tokenToInject = -1); - bool scanString(const char *str, short tokenToInject, bool requireSpace = true); - inline void injectToken(ushort tokenToInject) { - putChar(int(tokenToInject) << 16); - } - - QString resolveUndeclaredEntity(const QString &name); - void parseEntity(const QString &value); - QXmlStreamReaderPrivate *entityParser; - - bool scanAfterLangleBang(); - bool scanPublicOrSystem(); - bool scanNData(); - bool scanAfterDefaultDecl(); - bool scanAttType(); - - - // scan optimization functions. Not strictly necessary but LALR is - // not very well suited for scanning fast - int fastScanLiteralContent(); - int fastScanSpace(); - int fastScanContentCharList(); - int fastScanName(int *prefix = 0); - inline int fastScanNMTOKEN(); - - - bool parse(); - inline void consumeRule(int); - - void raiseError(QXmlStreamReader::Error error, const QString& message = QString()); - void raiseWellFormedError(const QString &message); - - QXmlStreamEntityResolver *entityResolver; - -private: - /*! \internal - Never assign to variable type directly. Instead use this function. - - This prevents errors from being ignored. - */ - inline void setType(const QXmlStreamReader::TokenType t) - { - if(type != QXmlStreamReader::Invalid) - type = t; - } -}; - -bool QXmlStreamReaderPrivate::parse() -{ - // cleanup currently reported token - - switch (type) { - case QXmlStreamReader::StartElement: - name.clear(); - prefix.clear(); - qualifiedName.clear(); - namespaceUri.clear(); - publicNamespaceDeclarations.clear(); - attributes.clear(); - if (isEmptyElement) { - setType(QXmlStreamReader::EndElement); - Tag &tag = tagStack_pop(); - namespaceUri = tag.namespaceDeclaration.namespaceUri; - name = tag.name; - qualifiedName = tag.qualifiedName; - isEmptyElement = false; - return true; - } - clearTextBuffer(); - break; - case QXmlStreamReader::EndElement: - name.clear(); - prefix.clear(); - qualifiedName.clear(); - namespaceUri.clear(); - clearTextBuffer(); - break; - case QXmlStreamReader::DTD: - publicNotationDeclarations.clear(); - publicEntityDeclarations.clear(); - dtdName.clear(); - dtdPublicId.clear(); - dtdSystemId.clear(); - Q_FALLTHROUGH(); - case QXmlStreamReader::Comment: - case QXmlStreamReader::Characters: - isCDATA = false; - isWhitespace = true; - text.clear(); - clearTextBuffer(); - break; - case QXmlStreamReader::EntityReference: - text.clear(); - name.clear(); - clearTextBuffer(); - break; - case QXmlStreamReader::ProcessingInstruction: - processingInstructionTarget.clear(); - processingInstructionData.clear(); - clearTextBuffer(); - break; - case QXmlStreamReader::NoToken: - case QXmlStreamReader::Invalid: - break; - case QXmlStreamReader::StartDocument: - lockEncoding = true; - documentVersion.clear(); - documentEncoding.clear(); -#ifndef QT_NO_TEXTCODEC - if (decoder && decoder->hasFailure()) { - raiseWellFormedError(QXmlStream::tr("Encountered incorrectly encoded content.")); - readBuffer.clear(); - return false; - } -#endif - Q_FALLTHROUGH(); - default: - clearTextBuffer(); - ; - } - - setType(QXmlStreamReader::NoToken); - - - // the main parse loop - int act, r; - - if (resumeReduction) { - act = state_stack[tos-1]; - r = resumeReduction; - resumeReduction = 0; - goto ResumeReduction; - } - - act = state_stack[tos]; - - forever { - if (token == -1 && - TERMINAL_COUNT != action_index[act]) { - uint cu = getChar(); - token = NOTOKEN; - token_char = cu == ~0U ? cu : ushort(cu); - if ((cu != ~0U) && (cu & 0xff0000)) { - token = cu >> 16; - } else switch (token_char) { - case 0xfffe: - case 0xffff: - token = ERROR; - break; - case '\r': - token = SPACE; - if (cu == '\r') { - if ((token_char = filterCarriageReturn())) { - ++lineNumber; - lastLineStart = characterOffset + readBufferPos; - break; - } - } else { - break; - } - Q_FALLTHROUGH(); - case ~0U: { - token = EOF_SYMBOL; - if (!tagsDone && !inParseEntity) { - int a = t_action(act, token); - if (a < 0) { - raiseError(QXmlStreamReader::PrematureEndOfDocumentError); - return false; - } - } - - } break; - case '\n': - ++lineNumber; - lastLineStart = characterOffset + readBufferPos; - Q_FALLTHROUGH(); - case ' ': - case '\t': - token = SPACE; - break; - case '&': - token = AMPERSAND; - break; - case '#': - token = HASH; - break; - case '\'': - token = QUOTE; - break; - case '\"': - token = DBLQUOTE; - break; - case '<': - token = LANGLE; - break; - case '>': - token = RANGLE; - break; - case '[': - token = LBRACK; - break; - case ']': - token = RBRACK; - break; - case '(': - token = LPAREN; - break; - case ')': - token = RPAREN; - break; - case '|': - token = PIPE; - break; - case '=': - token = EQ; - break; - case '%': - token = PERCENT; - break; - case '/': - token = SLASH; - break; - case ':': - token = COLON; - break; - case ';': - token = SEMICOLON; - break; - case ',': - token = COMMA; - break; - case '-': - token = DASH; - break; - case '+': - token = PLUS; - break; - case '*': - token = STAR; - break; - case '.': - token = DOT; - break; - case '?': - token = QUESTIONMARK; - break; - case '!': - token = BANG; - break; - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - token = DIGIT; - break; - default: - if (cu < 0x20) - token = NOTOKEN; - else - token = LETTER; - break; - } - } - - act = t_action (act, token); - if (act == ACCEPT_STATE) { - // reset the parser in case someone resumes (process instructions can follow a valid document) - tos = 0; - state_stack[tos++] = 0; - state_stack[tos] = 0; - return true; - } else if (act > 0) { - if (++tos == stack_size-1) - reallocateStack(); - - Value &val = sym_stack[tos]; - val.c = token_char; - val.pos = textBuffer.size(); - val.prefix = 0; - val.len = 1; - if (token_char) - textBuffer += QChar(token_char); - - state_stack[tos] = act; - token = -1; - - - } else if (act < 0) { - r = - act - 1; - -#if defined (QLALR_DEBUG) - int ridx = rule_index[r]; - printf ("%3d) %s ::=", r + 1, spell[rule_info[ridx]]); - ++ridx; - for (int i = ridx; i < ridx + rhs[r]; ++i) { - int symbol = rule_info[i]; - if (const char *name = spell[symbol]) - printf (" %s", name); - else - printf (" #%d", symbol); - } - printf ("\n"); -#endif - - tos -= rhs[r]; - act = state_stack[tos++]; - ResumeReduction: - switch (r) { -./ - -document ::= PARSE_ENTITY content; -/. - case $rule_number: - setType(QXmlStreamReader::EndDocument); - break; -./ - -document ::= prolog; -/. - case $rule_number: - if (type != QXmlStreamReader::Invalid) { - if (hasSeenTag || inParseEntity) { - setType(QXmlStreamReader::EndDocument); - } else { - raiseError(QXmlStreamReader::NotWellFormedError, QXmlStream::tr("Start tag expected.")); - // reset the parser - tos = 0; - state_stack[tos++] = 0; - state_stack[tos] = 0; - return false; - } - } - break; -./ - - -prolog ::= prolog stag content etag; -prolog ::= prolog empty_element_tag; -prolog ::= prolog comment; -prolog ::= prolog xml_decl; -prolog ::= prolog processing_instruction; -prolog ::= prolog doctype_decl; -prolog ::= prolog SPACE; -prolog ::=; - -entity_done ::= ENTITY_DONE; -/. - case $rule_number: - entityReferenceStack.pop()->isCurrentlyReferenced = false; - clearSym(); - break; -./ - - -xml_decl_start ::= XML; -/. - case $rule_number: - if (!scanString(spell[VERSION], VERSION, false) && atEnd) { - resume($rule_number); - return false; - } - break; -./ - -xml_decl ::= xml_decl_start VERSION space_opt EQ space_opt literal attribute_list_opt QUESTIONMARK RANGLE; -/. - case $rule_number: - setType(QXmlStreamReader::StartDocument); - documentVersion = symString(6); - startDocument(); - break; -./ - -external_id ::= SYSTEM literal; -/. - case $rule_number: - hasExternalDtdSubset = true; - dtdSystemId = symString(2); - break; -./ -external_id ::= PUBLIC public_literal space literal; -/. - case $rule_number: - checkPublicLiteral(symString(2)); - dtdPublicId = symString(2); - dtdSystemId = symString(4); - hasExternalDtdSubset = true; - break; -./ -external_id ::=; - -doctype_decl_start ::= langle_bang DOCTYPE qname space; -/. - case $rule_number: - if (!scanPublicOrSystem() && atEnd) { - resume($rule_number); - return false; - } - dtdName = symString(3); - break; -./ - -doctype_decl ::= langle_bang DOCTYPE qname RANGLE; -/. - case $rule_number:./ -doctype_decl ::= langle_bang DOCTYPE qname markup space_opt RANGLE; -/. - case $rule_number: - dtdName = symString(3); - // fall through -./ -doctype_decl ::= doctype_decl_start external_id space_opt markup space_opt RANGLE; -/. - case $rule_number:./ -doctype_decl ::= doctype_decl_start external_id space_opt RANGLE; -/. - case $rule_number: - setType(QXmlStreamReader::DTD); - text = &textBuffer; - break; -./ - -markup_start ::= LBRACK; -/. - case $rule_number: - scanDtd = true; - break; -./ - -markup ::= markup_start markup_list RBRACK; -/. - case $rule_number: - scanDtd = false; - break; -./ - - -markup_list ::= markup_decl | space | pereference; -markup_list ::= markup_list markup_decl | markup_list space | markup_list pereference; -markup_list ::=; - -markup_decl ::= element_decl | attlist_decl | entity_decl | entity_done | notation_decl | processing_instruction | comment; - - -element_decl_start ::= langle_bang ELEMENT qname space; -/. - case $rule_number: - if (!scanString(spell[EMPTY], EMPTY, false) - && !scanString(spell[ANY], ANY, false) - && atEnd) { - resume($rule_number); - return false; - } - break; -./ - -element_decl ::= element_decl_start content_spec space_opt RANGLE; - - -content_spec ::= EMPTY | ANY | mixed | children; - -pcdata_start ::= HASH; -/. - case $rule_number: - if (!scanString(spell[PCDATA], PCDATA, false) && atEnd) { - resume($rule_number); - return false; - } - break; -./ - -pcdata ::= pcdata_start PCDATA; - -questionmark_or_star_or_plus_opt ::= QUESTIONMARK | STAR | PLUS; -questionmark_or_star_or_plus_opt ::=; - -cp ::= qname questionmark_or_star_or_plus_opt | choice_or_seq questionmark_or_star_or_plus_opt; - -cp_pipe_or_comma_list ::= cp space_opt; -cp_pipe_or_comma_list ::= cp space_opt PIPE space_opt cp_pipe_list space_opt; -cp_pipe_or_comma_list ::= cp space_opt COMMA space_opt cp_comma_list space_opt; -cp_pipe_list ::= cp | cp_pipe_list space_opt PIPE space_opt cp; -cp_comma_list ::= cp | cp_comma_list space_opt COMMA space_opt cp; - - -name_pipe_list ::= PIPE space_opt qname; -name_pipe_list ::= name_pipe_list space_opt PIPE space_opt qname; - -star_opt ::= | STAR; - -mixed ::= LPAREN space_opt pcdata space_opt RPAREN star_opt; -mixed ::= LPAREN space_opt pcdata space_opt name_pipe_list space_opt RPAREN STAR; - -choice_or_seq ::= LPAREN space_opt cp_pipe_or_comma_list RPAREN; - -children ::= choice_or_seq questionmark_or_star_or_plus_opt; - - -nmtoken_pipe_list ::= nmtoken; -nmtoken_pipe_list ::= nmtoken_pipe_list space_opt PIPE space_opt nmtoken; - - -att_type ::= CDATA; -/. - case $rule_number: { - lastAttributeIsCData = true; - } break; -./ -att_type ::= ID | IDREF | IDREFS | ENTITY | ENTITIES | NMTOKEN | NMTOKENS; -att_type ::= LPAREN space_opt nmtoken_pipe_list space_opt RPAREN space; -att_type ::= NOTATION LPAREN space_opt nmtoken_pipe_list space_opt RPAREN space; - - -default_declhash ::= HASH; -/. - case $rule_number: - if (!scanAfterDefaultDecl() && atEnd) { - resume($rule_number); - return false; - } - break; -./ - -default_decl ::= default_declhash REQUIRED; -default_decl ::= default_declhash IMPLIED; -default_decl ::= attribute_value; -default_decl ::= default_declhash FIXED space attribute_value; -attdef_start ::= space qname space; -/. - case $rule_number: - sym(1) = sym(2); - lastAttributeValue.clear(); - lastAttributeIsCData = false; - if (!scanAttType() && atEnd) { - resume($rule_number); - return false; - } - break; -./ - -attdef ::= attdef_start att_type default_decl; -/. - case $rule_number: { - DtdAttribute &dtdAttribute = dtdAttributes.push(); - dtdAttribute.tagName.clear(); - dtdAttribute.isCDATA = lastAttributeIsCData; - dtdAttribute.attributePrefix = addToStringStorage(symPrefix(1)); - dtdAttribute.attributeName = addToStringStorage(symString(1)); - dtdAttribute.attributeQualifiedName = addToStringStorage(symName(1)); - dtdAttribute.isNamespaceAttribute = (dtdAttribute.attributePrefix == QLatin1String("xmlns") - || (dtdAttribute.attributePrefix.isEmpty() - && dtdAttribute.attributeName == QLatin1String("xmlns"))); - if (lastAttributeValue.isNull()) { - dtdAttribute.defaultValue.clear(); - } else { - if (dtdAttribute.isCDATA) - dtdAttribute.defaultValue = addToStringStorage(lastAttributeValue); - else - dtdAttribute.defaultValue = addToStringStorage(lastAttributeValue.toString().simplified()); - - } - } break; -./ - -attdef_list ::= attdef; -attdef_list ::= attdef_list attdef; - -attlist_decl ::= langle_bang ATTLIST qname space_opt RANGLE; -attlist_decl ::= langle_bang ATTLIST qname attdef_list space_opt RANGLE; -/. - case $rule_number: { - if (referenceToUnparsedEntityDetected && !standalone) - break; - int n = dtdAttributes.size(); - QStringRef tagName = addToStringStorage(symName(3)); - while (n--) { - DtdAttribute &dtdAttribute = dtdAttributes[n]; - if (!dtdAttribute.tagName.isNull()) - break; - dtdAttribute.tagName = tagName; - for (int i = 0; i < n; ++i) { - if ((dtdAttributes[i].tagName.isNull() || dtdAttributes[i].tagName == tagName) - && dtdAttributes[i].attributeQualifiedName == dtdAttribute.attributeQualifiedName) { - dtdAttribute.attributeQualifiedName.clear(); // redefined, delete it - break; - } - } - } - } break; -./ - -entity_decl_start ::= langle_bang ENTITY name space; -/. - case $rule_number: { - if (!scanPublicOrSystem() && atEnd) { - resume($rule_number); - return false; - } - EntityDeclaration &entityDeclaration = entityDeclarations.push(); - entityDeclaration.clear(); - entityDeclaration.name = symString(3); - } break; -./ - -entity_decl_start ::= langle_bang ENTITY PERCENT space name space; -/. - case $rule_number: { - if (!scanPublicOrSystem() && atEnd) { - resume($rule_number); - return false; - } - EntityDeclaration &entityDeclaration = entityDeclarations.push(); - entityDeclaration.clear(); - entityDeclaration.name = symString(5); - entityDeclaration.parameter = true; - } break; -./ - -entity_decl_external ::= entity_decl_start SYSTEM literal; -/. - case $rule_number: { - if (!scanNData() && atEnd) { - resume($rule_number); - return false; - } - EntityDeclaration &entityDeclaration = entityDeclarations.top(); - entityDeclaration.systemId = symString(3); - entityDeclaration.external = true; - } break; -./ - -entity_decl_external ::= entity_decl_start PUBLIC public_literal space literal; -/. - case $rule_number: { - if (!scanNData() && atEnd) { - resume($rule_number); - return false; - } - EntityDeclaration &entityDeclaration = entityDeclarations.top(); - checkPublicLiteral((entityDeclaration.publicId = symString(3))); - entityDeclaration.systemId = symString(5); - entityDeclaration.external = true; - } break; -./ - -entity_decl ::= entity_decl_external NDATA name space_opt RANGLE; -/. - case $rule_number: { - EntityDeclaration &entityDeclaration = entityDeclarations.top(); - entityDeclaration.notationName = symString(3); - if (entityDeclaration.parameter) - raiseWellFormedError(QXmlStream::tr("NDATA in parameter entity declaration.")); - } - Q_FALLTHROUGH(); -./ - -entity_decl ::= entity_decl_external space_opt RANGLE; -/. - case $rule_number:./ - -entity_decl ::= entity_decl_start entity_value space_opt RANGLE; -/. - case $rule_number: { - if (referenceToUnparsedEntityDetected && !standalone) { - entityDeclarations.pop(); - break; - } - EntityDeclaration &entityDeclaration = entityDeclarations.top(); - if (!entityDeclaration.external) - entityDeclaration.value = symString(2); - auto &hash = entityDeclaration.parameter ? parameterEntityHash : entityHash; - if (!hash.contains(qToStringViewIgnoringNull(entityDeclaration.name))) { - Entity entity(entityDeclaration.name.toString(), - entityDeclaration.value.toString()); - entity.unparsed = (!entityDeclaration.notationName.isNull()); - entity.external = entityDeclaration.external; - hash.insert(qToStringViewIgnoringNull(entity.name), entity); - } - } break; -./ - - -processing_instruction ::= LANGLE QUESTIONMARK name space; -/. - case $rule_number: { - setType(QXmlStreamReader::ProcessingInstruction); - int pos = sym(4).pos + sym(4).len; - processingInstructionTarget = symString(3); - if (scanUntil("?>")) { - processingInstructionData = QStringRef(&textBuffer, pos, textBuffer.size() - pos - 2); - if (!processingInstructionTarget.compare(QLatin1String("xml"), Qt::CaseInsensitive)) { - raiseWellFormedError(QXmlStream::tr("XML declaration not at start of document.")); - } - else if (!QXmlUtils::isNCName(processingInstructionTarget)) - raiseWellFormedError(QXmlStream::tr("%1 is an invalid processing instruction name.") - .arg(processingInstructionTarget)); - } else if (type != QXmlStreamReader::Invalid){ - resume($rule_number); - return false; - } - } break; -./ - -processing_instruction ::= LANGLE QUESTIONMARK name QUESTIONMARK RANGLE; -/. - case $rule_number: - setType(QXmlStreamReader::ProcessingInstruction); - processingInstructionTarget = symString(3); - if (!processingInstructionTarget.compare(QLatin1String("xml"), Qt::CaseInsensitive)) - raiseWellFormedError(QXmlStream::tr("Invalid processing instruction name.")); - break; -./ - - -langle_bang ::= LANGLE BANG; -/. - case $rule_number: - if (!scanAfterLangleBang() && atEnd) { - resume($rule_number); - return false; - } - break; -./ - -comment_start ::= langle_bang DASH DASH; -/. - case $rule_number: - if (!scanUntil("--")) { - resume($rule_number); - return false; - } - break; -./ - -comment ::= comment_start RANGLE; -/. - case $rule_number: { - setType(QXmlStreamReader::Comment); - int pos = sym(1).pos + 4; - text = QStringRef(&textBuffer, pos, textBuffer.size() - pos - 3); - } break; -./ - - -cdata ::= langle_bang CDATA_START; -/. - case $rule_number: { - setType(QXmlStreamReader::Characters); - isCDATA = true; - isWhitespace = false; - int pos = sym(2).pos; - if (scanUntil("]]>", -1)) { - text = QStringRef(&textBuffer, pos, textBuffer.size() - pos - 3); - } else { - resume($rule_number); - return false; - } - } break; -./ - -notation_decl_start ::= langle_bang NOTATION name space; -/. - case $rule_number: { - if (!scanPublicOrSystem() && atEnd) { - resume($rule_number); - return false; - } - NotationDeclaration ¬ationDeclaration = notationDeclarations.push(); - notationDeclaration.name = symString(3); - } break; -./ - -notation_decl ::= notation_decl_start SYSTEM literal space_opt RANGLE; -/. - case $rule_number: { - NotationDeclaration ¬ationDeclaration = notationDeclarations.top(); - notationDeclaration.systemId = symString(3); - notationDeclaration.publicId.clear(); - } break; -./ - -notation_decl ::= notation_decl_start PUBLIC public_literal space_opt RANGLE; -/. - case $rule_number: { - NotationDeclaration ¬ationDeclaration = notationDeclarations.top(); - notationDeclaration.systemId.clear(); - checkPublicLiteral((notationDeclaration.publicId = symString(3))); - } break; -./ - -notation_decl ::= notation_decl_start PUBLIC public_literal space literal space_opt RANGLE; -/. - case $rule_number: { - NotationDeclaration ¬ationDeclaration = notationDeclarations.top(); - checkPublicLiteral((notationDeclaration.publicId = symString(3))); - notationDeclaration.systemId = symString(5); - } break; -./ - - - -content_char ::= RANGLE | HASH | LBRACK | RBRACK | LPAREN | RPAREN | PIPE | EQ | PERCENT | SLASH | COLON | SEMICOLON | COMMA | DASH | PLUS | STAR | DOT | QUESTIONMARK | BANG | QUOTE | DBLQUOTE | LETTER | DIGIT; - -scan_content_char ::= content_char; -/. - case $rule_number: - isWhitespace = false; - Q_FALLTHROUGH(); -./ - -scan_content_char ::= SPACE; -/. - case $rule_number: - sym(1).len += fastScanContentCharList(); - if (atEnd && !inParseEntity) { - resume($rule_number); - return false; - } - break; -./ - -content_char_list ::= content_char_list char_ref; -content_char_list ::= content_char_list entity_ref; -content_char_list ::= content_char_list entity_done; -content_char_list ::= content_char_list scan_content_char; -content_char_list ::= char_ref; -content_char_list ::= entity_ref; -content_char_list ::= entity_done; -content_char_list ::= scan_content_char; - - -character_content ::= content_char_list %prec SHIFT_THERE; -/. - case $rule_number: - if (!textBuffer.isEmpty()) { - setType(QXmlStreamReader::Characters); - text = &textBuffer; - } - break; -./ - -literal ::= QUOTE QUOTE; -/. - case $rule_number:./ -literal ::= DBLQUOTE DBLQUOTE; -/. - case $rule_number: - clearSym(); - break; -./ -literal ::= QUOTE literal_content_with_dblquote QUOTE; -/. - case $rule_number:./ -literal ::= DBLQUOTE literal_content_with_quote DBLQUOTE; -/. - case $rule_number: - sym(1) = sym(2); - break; -./ - -literal_content_with_dblquote ::= literal_content_with_dblquote literal_content; -/. - case $rule_number:./ -literal_content_with_quote ::= literal_content_with_quote literal_content; -/. - case $rule_number:./ -literal_content_with_dblquote ::= literal_content_with_dblquote DBLQUOTE; -/. - case $rule_number:./ -literal_content_with_quote ::= literal_content_with_quote QUOTE; -/. - case $rule_number: - sym(1).len += sym(2).len; - break; -./ -literal_content_with_dblquote ::= literal_content; -literal_content_with_quote ::= literal_content; -literal_content_with_dblquote ::= DBLQUOTE; -literal_content_with_quote ::= QUOTE; - -literal_content_start ::= LETTER | DIGIT | RANGLE | HASH | LBRACK | RBRACK | LPAREN | RPAREN | PIPE | EQ | PERCENT | SLASH | COLON | SEMICOLON | COMMA | DASH | PLUS | STAR | DOT | QUESTIONMARK | BANG; - -literal_content_start ::= SPACE; -/. - case $rule_number: - if (normalizeLiterals) - textBuffer.data()[textBuffer.size()-1] = QLatin1Char(' '); - break; -./ - -literal_content ::= literal_content_start; -/. - case $rule_number: - sym(1).len += fastScanLiteralContent(); - if (atEnd) { - resume($rule_number); - return false; - } - break; -./ - - -public_literal ::= literal; -/. - case $rule_number: { - if (!QXmlUtils::isPublicID(symString(1))) { - raiseWellFormedError(QXmlStream::tr("%1 is an invalid PUBLIC identifier.").arg(symString(1))); - resume($rule_number); - return false; - } - } break; -./ - -entity_value ::= QUOTE QUOTE; -/. - case $rule_number:./ -entity_value ::= DBLQUOTE DBLQUOTE; -/. - case $rule_number: - clearSym(); - break; -./ - -entity_value ::= QUOTE entity_value_content_with_dblquote QUOTE; -/. - case $rule_number:./ -entity_value ::= DBLQUOTE entity_value_content_with_quote DBLQUOTE; -/. - case $rule_number: - sym(1) = sym(2); - break; -./ - -entity_value_content_with_dblquote ::= entity_value_content_with_dblquote entity_value_content; -/. - case $rule_number:./ -entity_value_content_with_quote ::= entity_value_content_with_quote entity_value_content; -/. - case $rule_number:./ -entity_value_content_with_dblquote ::= entity_value_content_with_dblquote DBLQUOTE; -/. - case $rule_number:./ -entity_value_content_with_quote ::= entity_value_content_with_quote QUOTE; -/. - case $rule_number: - sym(1).len += sym(2).len; - break; -./ -entity_value_content_with_dblquote ::= entity_value_content; -entity_value_content_with_quote ::= entity_value_content; -entity_value_content_with_dblquote ::= DBLQUOTE; -entity_value_content_with_quote ::= QUOTE; - -entity_value_content ::= LETTER | DIGIT | LANGLE | RANGLE | HASH | LBRACK | RBRACK | LPAREN | RPAREN | PIPE | EQ | SLASH | COLON | SEMICOLON | COMMA | SPACE | DASH | PLUS | STAR | DOT | QUESTIONMARK | BANG; -entity_value_content ::= char_ref | entity_ref_in_entity_value | entity_done; - - -attribute_value ::= QUOTE QUOTE; -/. - case $rule_number:./ -attribute_value ::= DBLQUOTE DBLQUOTE; -/. - case $rule_number: - clearSym(); - break; -./ -attribute_value ::= QUOTE attribute_value_content_with_dblquote QUOTE; -/. - case $rule_number:./ -attribute_value ::= DBLQUOTE attribute_value_content_with_quote DBLQUOTE; -/. - case $rule_number: - sym(1) = sym(2); - lastAttributeValue = symString(1); - break; -./ - -attribute_value_content_with_dblquote ::= attribute_value_content_with_dblquote attribute_value_content; -/. - case $rule_number:./ -attribute_value_content_with_quote ::= attribute_value_content_with_quote attribute_value_content; -/. - case $rule_number:./ -attribute_value_content_with_dblquote ::= attribute_value_content_with_dblquote DBLQUOTE; -/. - case $rule_number:./ -attribute_value_content_with_quote ::= attribute_value_content_with_quote QUOTE; -/. - case $rule_number: - sym(1).len += sym(2).len; - break; -./ -attribute_value_content_with_dblquote ::= attribute_value_content | DBLQUOTE; -attribute_value_content_with_quote ::= attribute_value_content | QUOTE; - -attribute_value_content ::= literal_content | char_ref | entity_ref_in_attribute_value | entity_done; - -attribute ::= qname space_opt EQ space_opt attribute_value; -/. - case $rule_number: { - QStringRef prefix = symPrefix(1); - if (prefix.isEmpty() && symString(1) == QLatin1String("xmlns") && namespaceProcessing) { - NamespaceDeclaration &namespaceDeclaration = namespaceDeclarations.push(); - namespaceDeclaration.prefix.clear(); - - const QStringRef ns(symString(5)); - if(ns == QLatin1String("http://www.w3.org/2000/xmlns/") || - ns == QLatin1String("http://www.w3.org/XML/1998/namespace")) - raiseWellFormedError(QXmlStream::tr("Illegal namespace declaration.")); - else - namespaceDeclaration.namespaceUri = addToStringStorage(ns); - } else { - Attribute &attribute = attributeStack.push(); - attribute.key = sym(1); - attribute.value = sym(5); - - QStringRef attributeQualifiedName = symName(1); - bool normalize = false; - for (int a = 0; a < dtdAttributes.size(); ++a) { - DtdAttribute &dtdAttribute = dtdAttributes[a]; - if (!dtdAttribute.isCDATA - && dtdAttribute.tagName == qualifiedName - && dtdAttribute.attributeQualifiedName == attributeQualifiedName - ) { - normalize = true; - break; - } - } - if (normalize) { - // normalize attribute value (simplify and trim) - int pos = textBuffer.size(); - int n = 0; - bool wasSpace = true; - for (int i = 0; i < attribute.value.len; ++i) { - QChar c = textBuffer.at(attribute.value.pos + i); - if (c.unicode() == ' ') { - if (wasSpace) - continue; - wasSpace = true; - } else { - wasSpace = false; - } - textBuffer += textBuffer.at(attribute.value.pos + i); - ++n; - } - if (wasSpace) - while (n && textBuffer.at(pos + n - 1).unicode() == ' ') - --n; - attribute.value.pos = pos; - attribute.value.len = n; - } - if (prefix == QLatin1String("xmlns") && namespaceProcessing) { - NamespaceDeclaration &namespaceDeclaration = namespaceDeclarations.push(); - QStringRef namespacePrefix = symString(attribute.key); - QStringRef namespaceUri = symString(attribute.value); - attributeStack.pop(); - if (((namespacePrefix == QLatin1String("xml")) - ^ (namespaceUri == QLatin1String("http://www.w3.org/XML/1998/namespace"))) - || namespaceUri == QLatin1String("http://www.w3.org/2000/xmlns/") - || namespaceUri.isEmpty() - || namespacePrefix == QLatin1String("xmlns")) - raiseWellFormedError(QXmlStream::tr("Illegal namespace declaration.")); - - namespaceDeclaration.prefix = addToStringStorage(namespacePrefix); - namespaceDeclaration.namespaceUri = addToStringStorage(namespaceUri); - } - } - } break; -./ - - - -attribute_list_opt ::= | space | space attribute_list space_opt; -attribute_list ::= attribute | attribute_list space attribute; - -stag_start ::= LANGLE qname; -/. - case $rule_number: { - normalizeLiterals = true; - Tag &tag = tagStack_push(); - prefix = tag.namespaceDeclaration.prefix = addToStringStorage(symPrefix(2)); - name = tag.name = addToStringStorage(symString(2)); - qualifiedName = tag.qualifiedName = addToStringStorage(symName(2)); - if ((!prefix.isEmpty() && !QXmlUtils::isNCName(prefix)) || !QXmlUtils::isNCName(name)) - raiseWellFormedError(QXmlStream::tr("Invalid XML name.")); - } break; -./ - - -empty_element_tag ::= stag_start attribute_list_opt SLASH RANGLE; -/. - case $rule_number: - isEmptyElement = true; - Q_FALLTHROUGH(); -./ - - -stag ::= stag_start attribute_list_opt RANGLE; -/. - case $rule_number: - setType(QXmlStreamReader::StartElement); - resolveTag(); - if (tagStack.size() == 1 && hasSeenTag && !inParseEntity) - raiseWellFormedError(QXmlStream::tr("Extra content at end of document.")); - hasSeenTag = true; - break; -./ - - -etag ::= LANGLE SLASH qname space_opt RANGLE; -/. - case $rule_number: { - setType(QXmlStreamReader::EndElement); - Tag &tag = tagStack_pop(); - - namespaceUri = tag.namespaceDeclaration.namespaceUri; - name = tag.name; - qualifiedName = tag.qualifiedName; - if (qualifiedName != symName(3)) - raiseWellFormedError(QXmlStream::tr("Opening and ending tag mismatch.")); - } break; -./ - - -unresolved_entity ::= UNRESOLVED_ENTITY; -/. - case $rule_number: - if (entitiesMustBeDeclared()) { - raiseWellFormedError(QXmlStream::tr("Entity '%1' not declared.").arg(unresolvedEntity)); - break; - } - setType(QXmlStreamReader::EntityReference); - name = &unresolvedEntity; - break; -./ - -entity_ref ::= AMPERSAND name SEMICOLON; -/. - case $rule_number: { - sym(1).len += sym(2).len + 1; - QStringView reference = symView(2); - if (entityHash.contains(reference)) { - Entity &entity = entityHash[reference]; - if (entity.unparsed) { - raiseWellFormedError(QXmlStream::tr("Reference to unparsed entity '%1'.").arg(reference)); - } else { - if (!entity.hasBeenParsed) { - parseEntity(entity.value); - entity.hasBeenParsed = true; - } - if (entity.literal) - putStringLiteral(entity.value); - else if (referenceEntity(entity)) - putReplacement(entity.value); - textBuffer.chop(2 + sym(2).len); - clearSym(); - } - break; - } - - if (entityResolver) { - QString replacementText = resolveUndeclaredEntity(reference.toString()); - if (!replacementText.isNull()) { - putReplacement(replacementText); - textBuffer.chop(2 + sym(2).len); - clearSym(); - break; - } - } - - injectToken(UNRESOLVED_ENTITY); - unresolvedEntity = symString(2).toString(); - textBuffer.chop(2 + sym(2).len); - clearSym(); - - } break; -./ - -pereference ::= PERCENT name SEMICOLON; -/. - case $rule_number: { - sym(1).len += sym(2).len + 1; - QStringView reference = symView(2); - if (parameterEntityHash.contains(reference)) { - referenceToParameterEntityDetected = true; - Entity &entity = parameterEntityHash[reference]; - if (entity.unparsed || entity.external) { - referenceToUnparsedEntityDetected = true; - } else { - if (referenceEntity(entity)) - putString(entity.value); - textBuffer.chop(2 + sym(2).len); - clearSym(); - } - } else if (entitiesMustBeDeclared()) { - raiseWellFormedError(QXmlStream::tr("Entity '%1' not declared.").arg(symString(2))); - } - } break; -./ - - - -entity_ref_in_entity_value ::= AMPERSAND name SEMICOLON; -/. - case $rule_number: - sym(1).len += sym(2).len + 1; - break; -./ - -entity_ref_in_attribute_value ::= AMPERSAND name SEMICOLON; -/. - case $rule_number: { - sym(1).len += sym(2).len + 1; - QStringView reference = symView(2); - if (entityHash.contains(reference)) { - Entity &entity = entityHash[reference]; - if (entity.unparsed || entity.value.isNull()) { - raiseWellFormedError(QXmlStream::tr("Reference to external entity '%1' in attribute value.").arg(reference)); - break; - } - if (!entity.hasBeenParsed) { - parseEntity(entity.value); - entity.hasBeenParsed = true; - } - if (entity.literal) - putStringLiteral(entity.value); - else if (referenceEntity(entity)) - putReplacementInAttributeValue(entity.value); - textBuffer.chop(2 + sym(2).len); - clearSym(); - break; - } - - if (entityResolver) { - QString replacementText = resolveUndeclaredEntity(reference.toString()); - if (!replacementText.isNull()) { - putReplacement(replacementText); - textBuffer.chop(2 + sym(2).len); - clearSym(); - break; - } - } - if (entitiesMustBeDeclared()) { - raiseWellFormedError(QXmlStream::tr("Entity '%1' not declared.").arg(reference)); - } - } break; -./ - -char_ref ::= AMPERSAND HASH char_ref_value SEMICOLON; -/. - case $rule_number: { - if (uint s = resolveCharRef(3)) { - if (s >= 0xffff) - putStringLiteral(QString::fromUcs4(&s, 1)); - else - putChar((LETTER << 16) | s); - - textBuffer.chop(3 + sym(3).len); - clearSym(); - } else { - raiseWellFormedError(QXmlStream::tr("Invalid character reference.")); - } - } break; -./ - - -char_ref_value ::= LETTER | DIGIT; -char_ref_value ::= char_ref_value LETTER; -/. - case $rule_number:./ -char_ref_value ::= char_ref_value DIGIT; -/. - case $rule_number: - sym(1).len += sym(2).len; - break; -./ - - -content ::= content character_content; -content ::= content stag content etag; -content ::= content empty_element_tag; -content ::= content comment; -content ::= content cdata; -content ::= content xml_decl; -content ::= content processing_instruction; -content ::= content doctype_decl; -content ::= content unresolved_entity; -content ::= ; - - -space ::= SPACE; -/. - case $rule_number: - sym(1).len += fastScanSpace(); - if (atEnd) { - resume($rule_number); - return false; - } - break; -./ - - -space_opt ::=; -space_opt ::= space; - -qname ::= LETTER; -/. - case $rule_number: { - sym(1).len += fastScanName(&sym(1).prefix); - if (atEnd) { - resume($rule_number); - return false; - } - } break; -./ - -name ::= LETTER; -/. - case $rule_number: - sym(1).len += fastScanName(); - if (atEnd) { - resume($rule_number); - return false; - } - break; -./ - -nmtoken ::= LETTER; -/. - case $rule_number:./ -nmtoken ::= DIGIT; -/. - case $rule_number:./ -nmtoken ::= DOT; -/. - case $rule_number:./ -nmtoken ::= DASH; -/. - case $rule_number:./ -nmtoken ::= COLON; -/. - case $rule_number: - sym(1).len += fastScanNMTOKEN(); - if (atEnd) { - resume($rule_number); - return false; - } - - break; -./ - - -/. - default: - ; - } // switch - act = state_stack[tos] = nt_action (act, lhs[r] - TERMINAL_COUNT); - if (type != QXmlStreamReader::NoToken) - return true; - } else { - parseError(); - break; - } - } - return false; -} -#endif //QT_NO_XMLSTREAMREADER.xml - -./ diff --git a/src/corelib/xml/qxmlstream.h b/src/corelib/xml/qxmlstream.h deleted file mode 100644 index 2350d12dd6..0000000000 --- a/src/corelib/xml/qxmlstream.h +++ /dev/null @@ -1,540 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtCore module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QXMLSTREAM_H -#define QXMLSTREAM_H - -#include - -#ifndef QT_NO_XMLSTREAM - -#include -#include -#include - -QT_BEGIN_NAMESPACE - - -class Q_CORE_EXPORT QXmlStreamStringRef { - QString m_string; - int m_position, m_size; -public: - inline QXmlStreamStringRef():m_position(0), m_size(0){} - inline QXmlStreamStringRef(const QStringRef &aString) - :m_string(aString.string()?*aString.string():QString()), m_position(aString.position()), m_size(aString.size()){} - QXmlStreamStringRef(const QString &aString) : m_string(aString), m_position(0), m_size(m_string.size()) {} -#ifdef Q_COMPILER_RVALUE_REFS - QXmlStreamStringRef(QString &&aString) Q_DECL_NOTHROW : m_string(std::move(aString)), m_position(0), m_size(m_string.size()) {} -#endif - -#if QT_VERSION < QT_VERSION_CHECK(6,0,0) - QXmlStreamStringRef(const QXmlStreamStringRef &other) // = default - : m_string(other.m_string), m_position(other.m_position), m_size(other.m_size) {} -#ifdef Q_COMPILER_RVALUE_REFS - QXmlStreamStringRef(QXmlStreamStringRef &&other) Q_DECL_NOTHROW // = default - : m_string(std::move(other.m_string)), m_position(other.m_position), m_size(other.m_size) {} - QXmlStreamStringRef &operator=(QXmlStreamStringRef &&other) Q_DECL_NOTHROW // = default - { swap(other); return *this; } -#endif - QXmlStreamStringRef &operator=(const QXmlStreamStringRef &other) // = default - { m_string = other.m_string; m_position = other.m_position; m_size = other.m_size; return *this; } - inline ~QXmlStreamStringRef() {} // ### this prevents (or deprecates) all the move/copy special member functions, - // ### that's why we need to provide them by hand above. We can't remove it in - // ### Qt 5, since that would change the way its passed to functions. In Qt 6, remove all. -#endif // Qt < 6.0 - - void swap(QXmlStreamStringRef &other) Q_DECL_NOTHROW - { - qSwap(m_string, other.m_string); - qSwap(m_position, other.m_position); - qSwap(m_size, other.m_size); - } - - inline void clear() { m_string.clear(); m_position = m_size = 0; } - inline operator QStringRef() const { return QStringRef(&m_string, m_position, m_size); } - inline const QString *string() const { return &m_string; } - inline int position() const { return m_position; } - inline int size() const { return m_size; } -}; -Q_DECLARE_SHARED_NOT_MOVABLE_UNTIL_QT6(QXmlStreamStringRef) - - -class QXmlStreamReaderPrivate; -class QXmlStreamAttributes; -class Q_CORE_EXPORT QXmlStreamAttribute { - QXmlStreamStringRef m_name, m_namespaceUri, m_qualifiedName, m_value; -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - void *reserved; -#endif - uint m_isDefault : 1; - friend class QXmlStreamReaderPrivate; - friend class QXmlStreamAttributes; -public: - QXmlStreamAttribute(); - QXmlStreamAttribute(const QString &qualifiedName, const QString &value); -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - QXmlStreamAttribute(const QString &namespaceUri, const QString &name, const QString &value); - QXmlStreamAttribute(const QXmlStreamAttribute &); -#ifdef Q_COMPILER_RVALUE_REFS - QXmlStreamAttribute(QXmlStreamAttribute &&other) Q_DECL_NOTHROW // = default; - : m_name(std::move(other.m_name)), - m_namespaceUri(std::move(other.m_namespaceUri)), - m_qualifiedName(std::move(other.m_qualifiedName)), - m_value(std::move(other.m_value)), - reserved(other.reserved), - m_isDefault(other.m_isDefault) - { - other.reserved = nullptr; - } - QXmlStreamAttribute &operator=(QXmlStreamAttribute &&other) Q_DECL_NOTHROW // = default; - { - m_name = std::move(other.m_name); - m_namespaceUri = std::move(other.m_namespaceUri); - m_qualifiedName = std::move(other.m_qualifiedName); - m_value = std::move(other.m_value); - qSwap(reserved, other.reserved); - m_isDefault = other.m_isDefault; - return *this; - } -#endif - QXmlStreamAttribute& operator=(const QXmlStreamAttribute &); - ~QXmlStreamAttribute(); -#endif // < Qt 6 - - inline QStringRef namespaceUri() const { return m_namespaceUri; } - inline QStringRef name() const { return m_name; } - inline QStringRef qualifiedName() const { return m_qualifiedName; } - inline QStringRef prefix() const { - return QStringRef(m_qualifiedName.string(), - m_qualifiedName.position(), - qMax(0, m_qualifiedName.size() - m_name.size() - 1)); - } - inline QStringRef value() const { return m_value; } - inline bool isDefault() const { return m_isDefault; } - inline bool operator==(const QXmlStreamAttribute &other) const { - return (value() == other.value() - && (namespaceUri().isNull() ? (qualifiedName() == other.qualifiedName()) - : (namespaceUri() == other.namespaceUri() && name() == other.name()))); - } - inline bool operator!=(const QXmlStreamAttribute &other) const - { return !operator==(other); } -}; - -Q_DECLARE_TYPEINFO(QXmlStreamAttribute, Q_MOVABLE_TYPE); - -class Q_CORE_EXPORT QXmlStreamAttributes : public QVector -{ -public: - inline QXmlStreamAttributes() {} - QStringRef value(const QString &namespaceUri, const QString &name) const; - QStringRef value(const QString &namespaceUri, QLatin1String name) const; - QStringRef value(QLatin1String namespaceUri, QLatin1String name) const; - QStringRef value(const QString &qualifiedName) const; - QStringRef value(QLatin1String qualifiedName) const; - void append(const QString &namespaceUri, const QString &name, const QString &value); - void append(const QString &qualifiedName, const QString &value); - - inline bool hasAttribute(const QString &qualifiedName) const - { - return !value(qualifiedName).isNull(); - } - - inline bool hasAttribute(QLatin1String qualifiedName) const - { - return !value(qualifiedName).isNull(); - } - - inline bool hasAttribute(const QString &namespaceUri, const QString &name) const - { - return !value(namespaceUri, name).isNull(); - } - - using QVector::append; -}; - -class Q_CORE_EXPORT QXmlStreamNamespaceDeclaration { - QXmlStreamStringRef m_prefix, m_namespaceUri; -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - void *reserved; -#endif - - friend class QXmlStreamReaderPrivate; -public: - QXmlStreamNamespaceDeclaration(); -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - QXmlStreamNamespaceDeclaration(const QXmlStreamNamespaceDeclaration &); - QXmlStreamNamespaceDeclaration(QXmlStreamNamespaceDeclaration &&other) Q_DECL_NOTHROW // = default - : m_prefix(std::move(other.m_prefix)), - m_namespaceUri(std::move(other.m_namespaceUri)), - reserved(other.reserved) - { - other.reserved = nullptr; - } - QXmlStreamNamespaceDeclaration &operator=(QXmlStreamNamespaceDeclaration &&other) Q_DECL_NOTHROW // = default - { - m_prefix = std::move(other.m_prefix); - m_namespaceUri = std::move(other.m_namespaceUri); - qSwap(reserved, other.reserved); - return *this; - } - QXmlStreamNamespaceDeclaration(const QString &prefix, const QString &namespaceUri); - ~QXmlStreamNamespaceDeclaration(); - QXmlStreamNamespaceDeclaration& operator=(const QXmlStreamNamespaceDeclaration &); -#endif // < Qt 6 - - inline QStringRef prefix() const { return m_prefix; } - inline QStringRef namespaceUri() const { return m_namespaceUri; } - inline bool operator==(const QXmlStreamNamespaceDeclaration &other) const { - return (prefix() == other.prefix() && namespaceUri() == other.namespaceUri()); - } - inline bool operator!=(const QXmlStreamNamespaceDeclaration &other) const - { return !operator==(other); } -}; - -Q_DECLARE_TYPEINFO(QXmlStreamNamespaceDeclaration, Q_MOVABLE_TYPE); -typedef QVector QXmlStreamNamespaceDeclarations; - -class Q_CORE_EXPORT QXmlStreamNotationDeclaration { - QXmlStreamStringRef m_name, m_systemId, m_publicId; -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - void *reserved; -#endif - - friend class QXmlStreamReaderPrivate; -public: - QXmlStreamNotationDeclaration(); -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - ~QXmlStreamNotationDeclaration(); - QXmlStreamNotationDeclaration(const QXmlStreamNotationDeclaration &); - QXmlStreamNotationDeclaration(QXmlStreamNotationDeclaration &&other) Q_DECL_NOTHROW // = default - : m_name(std::move(other.m_name)), - m_systemId(std::move(other.m_systemId)), - m_publicId(std::move(other.m_publicId)), - reserved(other.reserved) - { - other.reserved = nullptr; - } - QXmlStreamNotationDeclaration& operator=(const QXmlStreamNotationDeclaration &); - QXmlStreamNotationDeclaration &operator=(QXmlStreamNotationDeclaration &&other) Q_DECL_NOTHROW // = default - { - m_name = std::move(other.m_name); - m_systemId = std::move(other.m_systemId); - m_publicId = std::move(other.m_publicId); - qSwap(reserved, other.reserved); - return *this; - } -#endif // < Qt 6 - - inline QStringRef name() const { return m_name; } - inline QStringRef systemId() const { return m_systemId; } - inline QStringRef publicId() const { return m_publicId; } - inline bool operator==(const QXmlStreamNotationDeclaration &other) const { - return (name() == other.name() && systemId() == other.systemId() - && publicId() == other.publicId()); - } - inline bool operator!=(const QXmlStreamNotationDeclaration &other) const - { return !operator==(other); } -}; - -Q_DECLARE_TYPEINFO(QXmlStreamNotationDeclaration, Q_MOVABLE_TYPE); -typedef QVector QXmlStreamNotationDeclarations; - -class Q_CORE_EXPORT QXmlStreamEntityDeclaration { - QXmlStreamStringRef m_name, m_notationName, m_systemId, m_publicId, m_value; -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - void *reserved; -#endif - - friend class QXmlStreamReaderPrivate; -public: - QXmlStreamEntityDeclaration(); -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - ~QXmlStreamEntityDeclaration(); - QXmlStreamEntityDeclaration(const QXmlStreamEntityDeclaration &); - QXmlStreamEntityDeclaration(QXmlStreamEntityDeclaration &&other) Q_DECL_NOTHROW // = default - : m_name(std::move(other.m_name)), - m_notationName(std::move(other.m_notationName)), - m_systemId(std::move(other.m_systemId)), - m_publicId(std::move(other.m_publicId)), - m_value(std::move(other.m_value)), - reserved(other.reserved) - { - other.reserved = nullptr; - } - QXmlStreamEntityDeclaration& operator=(const QXmlStreamEntityDeclaration &); - QXmlStreamEntityDeclaration &operator=(QXmlStreamEntityDeclaration &&other) Q_DECL_NOTHROW // = default - { - m_name = std::move(other.m_name); - m_notationName = std::move(other.m_notationName); - m_systemId = std::move(other.m_systemId); - m_publicId = std::move(other.m_publicId); - m_value = std::move(other.m_value); - qSwap(reserved, other.reserved); - return *this; - } -#endif // < Qt 6 - - inline QStringRef name() const { return m_name; } - inline QStringRef notationName() const { return m_notationName; } - inline QStringRef systemId() const { return m_systemId; } - inline QStringRef publicId() const { return m_publicId; } - inline QStringRef value() const { return m_value; } - inline bool operator==(const QXmlStreamEntityDeclaration &other) const { - return (name() == other.name() - && notationName() == other.notationName() - && systemId() == other.systemId() - && publicId() == other.publicId() - && value() == other.value()); - } - inline bool operator!=(const QXmlStreamEntityDeclaration &other) const - { return !operator==(other); } -}; - -Q_DECLARE_TYPEINFO(QXmlStreamEntityDeclaration, Q_MOVABLE_TYPE); -typedef QVector QXmlStreamEntityDeclarations; - - -class Q_CORE_EXPORT QXmlStreamEntityResolver -{ -public: - virtual ~QXmlStreamEntityResolver(); - virtual QString resolveEntity(const QString& publicId, const QString& systemId); - virtual QString resolveUndeclaredEntity(const QString &name); -}; - -#ifndef QT_NO_XMLSTREAMREADER -class Q_CORE_EXPORT QXmlStreamReader { - QDOC_PROPERTY(bool namespaceProcessing READ namespaceProcessing WRITE setNamespaceProcessing) -public: - enum TokenType { - NoToken = 0, - Invalid, - StartDocument, - EndDocument, - StartElement, - EndElement, - Characters, - Comment, - DTD, - EntityReference, - ProcessingInstruction - }; - - - QXmlStreamReader(); - explicit QXmlStreamReader(QIODevice *device); - explicit QXmlStreamReader(const QByteArray &data); - explicit QXmlStreamReader(const QString &data); - explicit QXmlStreamReader(const char * data); - ~QXmlStreamReader(); - - void setDevice(QIODevice *device); - QIODevice *device() const; - void addData(const QByteArray &data); - void addData(const QString &data); - void addData(const char *data); - void clear(); - - - bool atEnd() const; - TokenType readNext(); - - bool readNextStartElement(); - void skipCurrentElement(); - - TokenType tokenType() const; - QString tokenString() const; - - void setNamespaceProcessing(bool); - bool namespaceProcessing() const; - - inline bool isStartDocument() const { return tokenType() == StartDocument; } - inline bool isEndDocument() const { return tokenType() == EndDocument; } - inline bool isStartElement() const { return tokenType() == StartElement; } - inline bool isEndElement() const { return tokenType() == EndElement; } - inline bool isCharacters() const { return tokenType() == Characters; } - bool isWhitespace() const; - bool isCDATA() const; - inline bool isComment() const { return tokenType() == Comment; } - inline bool isDTD() const { return tokenType() == DTD; } - inline bool isEntityReference() const { return tokenType() == EntityReference; } - inline bool isProcessingInstruction() const { return tokenType() == ProcessingInstruction; } - - bool isStandaloneDocument() const; - QStringRef documentVersion() const; - QStringRef documentEncoding() const; - - qint64 lineNumber() const; - qint64 columnNumber() const; - qint64 characterOffset() const; - - QXmlStreamAttributes attributes() const; - - enum ReadElementTextBehaviour { - ErrorOnUnexpectedElement, - IncludeChildElements, - SkipChildElements - }; - QString readElementText(ReadElementTextBehaviour behaviour = ErrorOnUnexpectedElement); - - QStringRef name() const; - QStringRef namespaceUri() const; - QStringRef qualifiedName() const; - QStringRef prefix() const; - - QStringRef processingInstructionTarget() const; - QStringRef processingInstructionData() const; - - QStringRef text() const; - - QXmlStreamNamespaceDeclarations namespaceDeclarations() const; - void addExtraNamespaceDeclaration(const QXmlStreamNamespaceDeclaration &extraNamespaceDeclaraction); - void addExtraNamespaceDeclarations(const QXmlStreamNamespaceDeclarations &extraNamespaceDeclaractions); - QXmlStreamNotationDeclarations notationDeclarations() const; - QXmlStreamEntityDeclarations entityDeclarations() const; - QStringRef dtdName() const; - QStringRef dtdPublicId() const; - QStringRef dtdSystemId() const; - - - enum Error { - NoError, - UnexpectedElementError, - CustomError, - NotWellFormedError, - PrematureEndOfDocumentError - }; - void raiseError(const QString& message = QString()); - QString errorString() const; - Error error() const; - - inline bool hasError() const - { - return error() != NoError; - } - - void setEntityResolver(QXmlStreamEntityResolver *resolver); - QXmlStreamEntityResolver *entityResolver() const; - -private: - Q_DISABLE_COPY(QXmlStreamReader) - Q_DECLARE_PRIVATE(QXmlStreamReader) - QScopedPointer d_ptr; - -}; -#endif // QT_NO_XMLSTREAMREADER - -#ifndef QT_NO_XMLSTREAMWRITER - -class QXmlStreamWriterPrivate; - -class Q_CORE_EXPORT QXmlStreamWriter -{ - QDOC_PROPERTY(bool autoFormatting READ autoFormatting WRITE setAutoFormatting) - QDOC_PROPERTY(int autoFormattingIndent READ autoFormattingIndent WRITE setAutoFormattingIndent) -public: - QXmlStreamWriter(); - explicit QXmlStreamWriter(QIODevice *device); - explicit QXmlStreamWriter(QByteArray *array); - explicit QXmlStreamWriter(QString *string); - ~QXmlStreamWriter(); - - void setDevice(QIODevice *device); - QIODevice *device() const; - -#ifndef QT_NO_TEXTCODEC - void setCodec(QTextCodec *codec); - void setCodec(const char *codecName); - QTextCodec *codec() const; -#endif - - void setAutoFormatting(bool); - bool autoFormatting() const; - - void setAutoFormattingIndent(int spacesOrTabs); - int autoFormattingIndent() const; - - void writeAttribute(const QString &qualifiedName, const QString &value); - void writeAttribute(const QString &namespaceUri, const QString &name, const QString &value); - void writeAttribute(const QXmlStreamAttribute& attribute); - void writeAttributes(const QXmlStreamAttributes& attributes); - - void writeCDATA(const QString &text); - void writeCharacters(const QString &text); - void writeComment(const QString &text); - - void writeDTD(const QString &dtd); - - void writeEmptyElement(const QString &qualifiedName); - void writeEmptyElement(const QString &namespaceUri, const QString &name); - - void writeTextElement(const QString &qualifiedName, const QString &text); - void writeTextElement(const QString &namespaceUri, const QString &name, const QString &text); - - void writeEndDocument(); - void writeEndElement(); - - void writeEntityReference(const QString &name); - void writeNamespace(const QString &namespaceUri, const QString &prefix = QString()); - void writeDefaultNamespace(const QString &namespaceUri); - void writeProcessingInstruction(const QString &target, const QString &data = QString()); - - void writeStartDocument(); - void writeStartDocument(const QString &version); - void writeStartDocument(const QString &version, bool standalone); - void writeStartElement(const QString &qualifiedName); - void writeStartElement(const QString &namespaceUri, const QString &name); - -#ifndef QT_NO_XMLSTREAMREADER - void writeCurrentToken(const QXmlStreamReader &reader); -#endif - - bool hasError() const; - -private: - Q_DISABLE_COPY(QXmlStreamWriter) - Q_DECLARE_PRIVATE(QXmlStreamWriter) - QScopedPointer d_ptr; -}; -#endif // QT_NO_XMLSTREAMWRITER - -QT_END_NAMESPACE - -#endif // QT_NO_XMLSTREAM -#endif // QXMLSTREAM_H diff --git a/src/corelib/xml/qxmlstream_p.h b/src/corelib/xml/qxmlstream_p.h deleted file mode 100644 index 5645d812eb..0000000000 --- a/src/corelib/xml/qxmlstream_p.h +++ /dev/null @@ -1,1972 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtCore module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists for the convenience -// of other Qt classes. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include - -// This file was generated by qlalr - DO NOT EDIT! -#ifndef QXMLSTREAM_P_H -#define QXMLSTREAM_P_H - -#if defined(ERROR) -# undef ERROR -#endif - -class QXmlStreamReader_Table -{ -public: - enum VariousConstants { - EOF_SYMBOL = 0, - AMPERSAND = 5, - ANY = 41, - ATTLIST = 31, - BANG = 25, - CDATA = 47, - CDATA_START = 28, - COLON = 17, - COMMA = 19, - DASH = 20, - DBLQUOTE = 8, - DIGIT = 27, - DOCTYPE = 29, - DOT = 23, - ELEMENT = 30, - EMPTY = 40, - ENTITIES = 51, - ENTITY = 32, - ENTITY_DONE = 45, - EQ = 14, - ERROR = 43, - FIXED = 39, - HASH = 6, - ID = 48, - IDREF = 49, - IDREFS = 50, - IMPLIED = 38, - LANGLE = 3, - LBRACK = 9, - LETTER = 26, - LPAREN = 11, - NDATA = 36, - NMTOKEN = 52, - NMTOKENS = 53, - NOTATION = 33, - NOTOKEN = 1, - PARSE_ENTITY = 44, - PCDATA = 42, - PERCENT = 15, - PIPE = 13, - PLUS = 21, - PUBLIC = 35, - QUESTIONMARK = 24, - QUOTE = 7, - RANGLE = 4, - RBRACK = 10, - REQUIRED = 37, - RPAREN = 12, - SEMICOLON = 18, - SHIFT_THERE = 56, - SLASH = 16, - SPACE = 2, - STAR = 22, - SYSTEM = 34, - UNRESOLVED_ENTITY = 46, - VERSION = 55, - XML = 54, - - ACCEPT_STATE = 416, - RULE_COUNT = 270, - STATE_COUNT = 427, - TERMINAL_COUNT = 57, - NON_TERMINAL_COUNT = 84, - - GOTO_INDEX_OFFSET = 427, - GOTO_INFO_OFFSET = 1017, - GOTO_CHECK_OFFSET = 1017 - }; - - static const char *const spell []; - static const short lhs []; - static const short rhs []; - static const short goto_default []; - static const short action_default []; - static const short action_index []; - static const short action_info []; - static const short action_check []; - - static inline int nt_action (int state, int nt) - { - const int yyn = action_index [GOTO_INDEX_OFFSET + state] + nt; - if (yyn < 0 || action_check [GOTO_CHECK_OFFSET + yyn] != nt) - return goto_default [nt]; - - return action_info [GOTO_INFO_OFFSET + yyn]; - } - - static inline int t_action (int state, int token) - { - const int yyn = action_index [state] + token; - - if (yyn < 0 || action_check [yyn] != token) - return - action_default [state]; - - return action_info [yyn]; - } -}; - - -const char *const QXmlStreamReader_Table::spell [] = { - "end of file", 0, " ", "<", ">", "&", "#", "\'", "\"", "[", - "]", "(", ")", "|", "=", "%", "/", ":", ";", ",", - "-", "+", "*", ".", "?", "!", "[a-zA-Z]", "[0-9]", "[CDATA[", "DOCTYPE", - "ELEMENT", "ATTLIST", "ENTITY", "NOTATION", "SYSTEM", "PUBLIC", "NDATA", "REQUIRED", "IMPLIED", "FIXED", - "EMPTY", "ANY", "PCDATA", 0, 0, 0, 0, "CDATA", "ID", "IDREF", - "IDREFS", "ENTITIES", "NMTOKEN", "NMTOKENS", " class QXmlStreamSimpleStack { - T *data; - int tos, cap; -public: - inline QXmlStreamSimpleStack():data(0), tos(-1), cap(0){} - inline ~QXmlStreamSimpleStack(){ if (data) free(data); } - - inline void reserve(int extraCapacity) { - if (tos + extraCapacity + 1 > cap) { - cap = qMax(tos + extraCapacity + 1, cap << 1 ); - data = reinterpret_cast(realloc(data, cap * sizeof(T))); - Q_CHECK_PTR(data); - } - } - - inline T &push() { reserve(1); return data[++tos]; } - inline T &rawPush() { return data[++tos]; } - inline const T &top() const { return data[tos]; } - inline T &top() { return data[tos]; } - inline T &pop() { return data[tos--]; } - inline T &operator[](int index) { return data[index]; } - inline const T &at(int index) const { return data[index]; } - inline int size() const { return tos + 1; } - inline void resize(int s) { tos = s - 1; } - inline bool isEmpty() const { return tos < 0; } - inline void clear() { tos = -1; } -}; - - -class QXmlStream -{ - Q_DECLARE_TR_FUNCTIONS(QXmlStream) -}; - -class QXmlStreamPrivateTagStack { -public: - struct NamespaceDeclaration - { - QStringRef prefix; - QStringRef namespaceUri; - }; - - struct Tag - { - QStringRef name; - QStringRef qualifiedName; - NamespaceDeclaration namespaceDeclaration; - int tagStackStringStorageSize; - int namespaceDeclarationsSize; - }; - - - QXmlStreamPrivateTagStack(); - QXmlStreamSimpleStack namespaceDeclarations; - QString tagStackStringStorage; - int tagStackStringStorageSize; - int initialTagStackStringStorageSize; - bool tagsDone; - - inline QStringRef addToStringStorage(const QStringRef &s) { - return addToStringStorage(qToStringViewIgnoringNull(s)); - } - inline QStringRef addToStringStorage(const QString &s) { - return addToStringStorage(qToStringViewIgnoringNull(s)); - } - QStringRef addToStringStorage(QStringView s) - { - int pos = tagStackStringStorageSize; - int sz = s.size(); - if (pos != tagStackStringStorage.size()) - tagStackStringStorage.resize(pos); - tagStackStringStorage.append(s.data(), sz); - tagStackStringStorageSize += sz; - return QStringRef(&tagStackStringStorage, pos, sz); - } - - QXmlStreamSimpleStack tagStack; - - - inline Tag &tagStack_pop() { - Tag& tag = tagStack.pop(); - tagStackStringStorageSize = tag.tagStackStringStorageSize; - namespaceDeclarations.resize(tag.namespaceDeclarationsSize); - tagsDone = tagStack.isEmpty(); - return tag; - } - inline Tag &tagStack_push() { - Tag &tag = tagStack.push(); - tag.tagStackStringStorageSize = tagStackStringStorageSize; - tag.namespaceDeclarationsSize = namespaceDeclarations.size(); - return tag; - } -}; - - -class QXmlStreamEntityResolver; -#ifndef QT_NO_XMLSTREAMREADER -class QXmlStreamReaderPrivate : public QXmlStreamReader_Table, public QXmlStreamPrivateTagStack{ - QXmlStreamReader *q_ptr; - Q_DECLARE_PUBLIC(QXmlStreamReader) -public: - QXmlStreamReaderPrivate(QXmlStreamReader *q); - ~QXmlStreamReaderPrivate(); - void init(); - - QByteArray rawReadBuffer; - QByteArray dataBuffer; - uchar firstByte; - qint64 nbytesread; - QString readBuffer; - int readBufferPos; - QXmlStreamSimpleStack putStack; - struct Entity { - Entity() = default; - Entity(const QString &name, const QString &value) - : name(name), value(value), external(false), unparsed(false), literal(false), - hasBeenParsed(false), isCurrentlyReferenced(false){} - static inline Entity createLiteral(QLatin1String name, QLatin1String value) - { Entity result(name, value); result.literal = result.hasBeenParsed = true; return result; } - QString name, value; - uint external : 1; - uint unparsed : 1; - uint literal : 1; - uint hasBeenParsed : 1; - uint isCurrentlyReferenced : 1; - }; - // these hash tables use a QStringView as a key to avoid creating QStrings - // just for lookup. The keys are usually views into Entity::name and thus - // are guaranteed to have the same lifetime as the referenced data: - QHash entityHash; - QHash parameterEntityHash; - QXmlStreamSimpleStackentityReferenceStack; - inline bool referenceEntity(Entity &entity) { - if (entity.isCurrentlyReferenced) { - raiseWellFormedError(QXmlStream::tr("Recursive entity detected.")); - return false; - } - entity.isCurrentlyReferenced = true; - entityReferenceStack.push() = &entity; - injectToken(ENTITY_DONE); - return true; - } - - - QIODevice *device; - bool deleteDevice; -#ifndef QT_NO_TEXTCODEC - QTextCodec *codec; - QTextDecoder *decoder; -#endif - bool atEnd; - - /*! - \sa setType() - */ - QXmlStreamReader::TokenType type; - QXmlStreamReader::Error error; - QString errorString; - QString unresolvedEntity; - - qint64 lineNumber, lastLineStart, characterOffset; - - - void write(const QString &); - void write(const char *); - - - QXmlStreamAttributes attributes; - QStringRef namespaceForPrefix(const QStringRef &prefix); - void resolveTag(); - void resolvePublicNamespaces(); - void resolveDtd(); - uint resolveCharRef(int symbolIndex); - bool checkStartDocument(); - void startDocument(); - void parseError(); - void checkPublicLiteral(const QStringRef &publicId); - - bool scanDtd; - QStringRef lastAttributeValue; - bool lastAttributeIsCData; - struct DtdAttribute { - QStringRef tagName; - QStringRef attributeQualifiedName; - QStringRef attributePrefix; - QStringRef attributeName; - QStringRef defaultValue; - bool isCDATA; - bool isNamespaceAttribute; - }; - QXmlStreamSimpleStack dtdAttributes; - struct NotationDeclaration { - QStringRef name; - QStringRef publicId; - QStringRef systemId; - }; - QXmlStreamSimpleStack notationDeclarations; - QXmlStreamNotationDeclarations publicNotationDeclarations; - QXmlStreamNamespaceDeclarations publicNamespaceDeclarations; - - struct EntityDeclaration { - QStringRef name; - QStringRef notationName; - QStringRef publicId; - QStringRef systemId; - QStringRef value; - bool parameter; - bool external; - inline void clear() { - name.clear(); - notationName.clear(); - publicId.clear(); - systemId.clear(); - value.clear(); - parameter = external = false; - } - }; - QXmlStreamSimpleStack entityDeclarations; - QXmlStreamEntityDeclarations publicEntityDeclarations; - - QStringRef text; - - QStringRef prefix, namespaceUri, qualifiedName, name; - QStringRef processingInstructionTarget, processingInstructionData; - QStringRef dtdName, dtdPublicId, dtdSystemId; - QStringRef documentVersion, documentEncoding; - uint isEmptyElement : 1; - uint isWhitespace : 1; - uint isCDATA : 1; - uint standalone : 1; - uint hasCheckedStartDocument : 1; - uint normalizeLiterals : 1; - uint hasSeenTag : 1; - uint inParseEntity : 1; - uint referenceToUnparsedEntityDetected : 1; - uint referenceToParameterEntityDetected : 1; - uint hasExternalDtdSubset : 1; - uint lockEncoding : 1; - uint namespaceProcessing : 1; - - int resumeReduction; - void resume(int rule); - - inline bool entitiesMustBeDeclared() const { - return (!inParseEntity - && (standalone - || (!referenceToUnparsedEntityDetected - && !referenceToParameterEntityDetected // Errata 13 as of 2006-04-25 - && !hasExternalDtdSubset))); - } - - // qlalr parser - int tos; - int stack_size; - struct Value { - int pos; - int len; - int prefix; - ushort c; - }; - - Value *sym_stack; - int *state_stack; - inline void reallocateStack(); - inline Value &sym(int index) const - { return sym_stack[tos + index - 1]; } - QString textBuffer; - inline void clearTextBuffer() { - if (!scanDtd) { - textBuffer.resize(0); - textBuffer.reserve(256); - } - } - struct Attribute { - Value key; - Value value; - }; - QXmlStreamSimpleStack attributeStack; - - inline QStringRef symString(int index) { - const Value &symbol = sym(index); - return QStringRef(&textBuffer, symbol.pos + symbol.prefix, symbol.len - symbol.prefix); - } - QStringView symView(int index) const - { - const Value &symbol = sym(index); - return QStringView(textBuffer.data() + symbol.pos, symbol.len).mid(symbol.prefix); - } - inline QStringRef symName(int index) { - const Value &symbol = sym(index); - return QStringRef(&textBuffer, symbol.pos, symbol.len); - } - inline QStringRef symString(int index, int offset) { - const Value &symbol = sym(index); - return QStringRef(&textBuffer, symbol.pos + symbol.prefix + offset, symbol.len - symbol.prefix - offset); - } - inline QStringRef symPrefix(int index) { - const Value &symbol = sym(index); - if (symbol.prefix) - return QStringRef(&textBuffer, symbol.pos, symbol.prefix - 1); - return QStringRef(); - } - inline QStringRef symString(const Value &symbol) { - return QStringRef(&textBuffer, symbol.pos + symbol.prefix, symbol.len - symbol.prefix); - } - inline QStringRef symName(const Value &symbol) { - return QStringRef(&textBuffer, symbol.pos, symbol.len); - } - inline QStringRef symPrefix(const Value &symbol) { - if (symbol.prefix) - return QStringRef(&textBuffer, symbol.pos, symbol.prefix - 1); - return QStringRef(); - } - - inline void clearSym() { Value &val = sym(1); val.pos = textBuffer.size(); val.len = 0; } - - - short token; - uint token_char; - - uint filterCarriageReturn(); - inline uint getChar(); - inline uint peekChar(); - inline void putChar(uint c) { putStack.push() = c; } - inline void putChar(QChar c) { putStack.push() = c.unicode(); } - void putString(const QString &s, int from = 0); - void putStringLiteral(const QString &s); - void putReplacement(const QString &s); - void putReplacementInAttributeValue(const QString &s); - uint getChar_helper(); - - bool scanUntil(const char *str, short tokenToInject = -1); - bool scanString(const char *str, short tokenToInject, bool requireSpace = true); - inline void injectToken(ushort tokenToInject) { - putChar(int(tokenToInject) << 16); - } - - QString resolveUndeclaredEntity(const QString &name); - void parseEntity(const QString &value); - QXmlStreamReaderPrivate *entityParser; - - bool scanAfterLangleBang(); - bool scanPublicOrSystem(); - bool scanNData(); - bool scanAfterDefaultDecl(); - bool scanAttType(); - - - // scan optimization functions. Not strictly necessary but LALR is - // not very well suited for scanning fast - int fastScanLiteralContent(); - int fastScanSpace(); - int fastScanContentCharList(); - int fastScanName(int *prefix = 0); - inline int fastScanNMTOKEN(); - - - bool parse(); - inline void consumeRule(int); - - void raiseError(QXmlStreamReader::Error error, const QString& message = QString()); - void raiseWellFormedError(const QString &message); - - QXmlStreamEntityResolver *entityResolver; - -private: - /*! \internal - Never assign to variable type directly. Instead use this function. - - This prevents errors from being ignored. - */ - inline void setType(const QXmlStreamReader::TokenType t) - { - if(type != QXmlStreamReader::Invalid) - type = t; - } -}; - -bool QXmlStreamReaderPrivate::parse() -{ - // cleanup currently reported token - - switch (type) { - case QXmlStreamReader::StartElement: - name.clear(); - prefix.clear(); - qualifiedName.clear(); - namespaceUri.clear(); - publicNamespaceDeclarations.clear(); - attributes.clear(); - if (isEmptyElement) { - setType(QXmlStreamReader::EndElement); - Tag &tag = tagStack_pop(); - namespaceUri = tag.namespaceDeclaration.namespaceUri; - name = tag.name; - qualifiedName = tag.qualifiedName; - isEmptyElement = false; - return true; - } - clearTextBuffer(); - break; - case QXmlStreamReader::EndElement: - name.clear(); - prefix.clear(); - qualifiedName.clear(); - namespaceUri.clear(); - clearTextBuffer(); - break; - case QXmlStreamReader::DTD: - publicNotationDeclarations.clear(); - publicEntityDeclarations.clear(); - dtdName.clear(); - dtdPublicId.clear(); - dtdSystemId.clear(); - Q_FALLTHROUGH(); - case QXmlStreamReader::Comment: - case QXmlStreamReader::Characters: - isCDATA = false; - isWhitespace = true; - text.clear(); - clearTextBuffer(); - break; - case QXmlStreamReader::EntityReference: - text.clear(); - name.clear(); - clearTextBuffer(); - break; - case QXmlStreamReader::ProcessingInstruction: - processingInstructionTarget.clear(); - processingInstructionData.clear(); - clearTextBuffer(); - break; - case QXmlStreamReader::NoToken: - case QXmlStreamReader::Invalid: - break; - case QXmlStreamReader::StartDocument: - lockEncoding = true; - documentVersion.clear(); - documentEncoding.clear(); -#ifndef QT_NO_TEXTCODEC - if (decoder && decoder->hasFailure()) { - raiseWellFormedError(QXmlStream::tr("Encountered incorrectly encoded content.")); - readBuffer.clear(); - return false; - } -#endif - Q_FALLTHROUGH(); - default: - clearTextBuffer(); - ; - } - - setType(QXmlStreamReader::NoToken); - - - // the main parse loop - int act, r; - - if (resumeReduction) { - act = state_stack[tos-1]; - r = resumeReduction; - resumeReduction = 0; - goto ResumeReduction; - } - - act = state_stack[tos]; - - forever { - if (token == -1 && - TERMINAL_COUNT != action_index[act]) { - uint cu = getChar(); - token = NOTOKEN; - token_char = cu == ~0U ? cu : ushort(cu); - if ((cu != ~0U) && (cu & 0xff0000)) { - token = cu >> 16; - } else switch (token_char) { - case 0xfffe: - case 0xffff: - token = ERROR; - break; - case '\r': - token = SPACE; - if (cu == '\r') { - if ((token_char = filterCarriageReturn())) { - ++lineNumber; - lastLineStart = characterOffset + readBufferPos; - break; - } - } else { - break; - } - Q_FALLTHROUGH(); - case ~0U: { - token = EOF_SYMBOL; - if (!tagsDone && !inParseEntity) { - int a = t_action(act, token); - if (a < 0) { - raiseError(QXmlStreamReader::PrematureEndOfDocumentError); - return false; - } - } - - } break; - case '\n': - ++lineNumber; - lastLineStart = characterOffset + readBufferPos; - Q_FALLTHROUGH(); - case ' ': - case '\t': - token = SPACE; - break; - case '&': - token = AMPERSAND; - break; - case '#': - token = HASH; - break; - case '\'': - token = QUOTE; - break; - case '\"': - token = DBLQUOTE; - break; - case '<': - token = LANGLE; - break; - case '>': - token = RANGLE; - break; - case '[': - token = LBRACK; - break; - case ']': - token = RBRACK; - break; - case '(': - token = LPAREN; - break; - case ')': - token = RPAREN; - break; - case '|': - token = PIPE; - break; - case '=': - token = EQ; - break; - case '%': - token = PERCENT; - break; - case '/': - token = SLASH; - break; - case ':': - token = COLON; - break; - case ';': - token = SEMICOLON; - break; - case ',': - token = COMMA; - break; - case '-': - token = DASH; - break; - case '+': - token = PLUS; - break; - case '*': - token = STAR; - break; - case '.': - token = DOT; - break; - case '?': - token = QUESTIONMARK; - break; - case '!': - token = BANG; - break; - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - token = DIGIT; - break; - default: - if (cu < 0x20) - token = NOTOKEN; - else - token = LETTER; - break; - } - } - - act = t_action (act, token); - if (act == ACCEPT_STATE) { - // reset the parser in case someone resumes (process instructions can follow a valid document) - tos = 0; - state_stack[tos++] = 0; - state_stack[tos] = 0; - return true; - } else if (act > 0) { - if (++tos == stack_size-1) - reallocateStack(); - - Value &val = sym_stack[tos]; - val.c = token_char; - val.pos = textBuffer.size(); - val.prefix = 0; - val.len = 1; - if (token_char) - textBuffer += QChar(token_char); - - state_stack[tos] = act; - token = -1; - - - } else if (act < 0) { - r = - act - 1; - -#if defined (QLALR_DEBUG) - int ridx = rule_index[r]; - printf ("%3d) %s ::=", r + 1, spell[rule_info[ridx]]); - ++ridx; - for (int i = ridx; i < ridx + rhs[r]; ++i) { - int symbol = rule_info[i]; - if (const char *name = spell[symbol]) - printf (" %s", name); - else - printf (" #%d", symbol); - } - printf ("\n"); -#endif - - tos -= rhs[r]; - act = state_stack[tos++]; - ResumeReduction: - switch (r) { - - case 0: - setType(QXmlStreamReader::EndDocument); - break; - - case 1: - if (type != QXmlStreamReader::Invalid) { - if (hasSeenTag || inParseEntity) { - setType(QXmlStreamReader::EndDocument); - } else { - raiseError(QXmlStreamReader::NotWellFormedError, QXmlStream::tr("Start tag expected.")); - // reset the parser - tos = 0; - state_stack[tos++] = 0; - state_stack[tos] = 0; - return false; - } - } - break; - - case 10: - entityReferenceStack.pop()->isCurrentlyReferenced = false; - clearSym(); - break; - - case 11: - if (!scanString(spell[VERSION], VERSION, false) && atEnd) { - resume(11); - return false; - } - break; - - case 12: - setType(QXmlStreamReader::StartDocument); - documentVersion = symString(6); - startDocument(); - break; - - case 13: - hasExternalDtdSubset = true; - dtdSystemId = symString(2); - break; - - case 14: - checkPublicLiteral(symString(2)); - dtdPublicId = symString(2); - dtdSystemId = symString(4); - hasExternalDtdSubset = true; - break; - - case 16: - if (!scanPublicOrSystem() && atEnd) { - resume(16); - return false; - } - dtdName = symString(3); - break; - - case 17: - case 18: - dtdName = symString(3); - Q_FALLTHROUGH(); - - case 19: - case 20: - setType(QXmlStreamReader::DTD); - text = &textBuffer; - break; - - case 21: - scanDtd = true; - break; - - case 22: - scanDtd = false; - break; - - case 37: - if (!scanString(spell[EMPTY], EMPTY, false) - && !scanString(spell[ANY], ANY, false) - && atEnd) { - resume(37); - return false; - } - break; - - case 43: - if (!scanString(spell[PCDATA], PCDATA, false) && atEnd) { - resume(43); - return false; - } - break; - - case 68: { - lastAttributeIsCData = true; - } break; - - case 78: - if (!scanAfterDefaultDecl() && atEnd) { - resume(78); - return false; - } - break; - - case 83: - sym(1) = sym(2); - lastAttributeValue.clear(); - lastAttributeIsCData = false; - if (!scanAttType() && atEnd) { - resume(83); - return false; - } - break; - - case 84: { - DtdAttribute &dtdAttribute = dtdAttributes.push(); - dtdAttribute.tagName.clear(); - dtdAttribute.isCDATA = lastAttributeIsCData; - dtdAttribute.attributePrefix = addToStringStorage(symPrefix(1)); - dtdAttribute.attributeName = addToStringStorage(symString(1)); - dtdAttribute.attributeQualifiedName = addToStringStorage(symName(1)); - dtdAttribute.isNamespaceAttribute = (dtdAttribute.attributePrefix == QLatin1String("xmlns") - || (dtdAttribute.attributePrefix.isEmpty() - && dtdAttribute.attributeName == QLatin1String("xmlns"))); - if (lastAttributeValue.isNull()) { - dtdAttribute.defaultValue.clear(); - } else { - if (dtdAttribute.isCDATA) - dtdAttribute.defaultValue = addToStringStorage(lastAttributeValue); - else - dtdAttribute.defaultValue = addToStringStorage(lastAttributeValue.toString().simplified()); - - } - } break; - - case 88: { - if (referenceToUnparsedEntityDetected && !standalone) - break; - int n = dtdAttributes.size(); - QStringRef tagName = addToStringStorage(symName(3)); - while (n--) { - DtdAttribute &dtdAttribute = dtdAttributes[n]; - if (!dtdAttribute.tagName.isNull()) - break; - dtdAttribute.tagName = tagName; - for (int i = 0; i < n; ++i) { - if ((dtdAttributes[i].tagName.isNull() || dtdAttributes[i].tagName == tagName) - && dtdAttributes[i].attributeQualifiedName == dtdAttribute.attributeQualifiedName) { - dtdAttribute.attributeQualifiedName.clear(); // redefined, delete it - break; - } - } - } - } break; - - case 89: { - if (!scanPublicOrSystem() && atEnd) { - resume(89); - return false; - } - EntityDeclaration &entityDeclaration = entityDeclarations.push(); - entityDeclaration.clear(); - entityDeclaration.name = symString(3); - } break; - - case 90: { - if (!scanPublicOrSystem() && atEnd) { - resume(90); - return false; - } - EntityDeclaration &entityDeclaration = entityDeclarations.push(); - entityDeclaration.clear(); - entityDeclaration.name = symString(5); - entityDeclaration.parameter = true; - } break; - - case 91: { - if (!scanNData() && atEnd) { - resume(91); - return false; - } - EntityDeclaration &entityDeclaration = entityDeclarations.top(); - entityDeclaration.systemId = symString(3); - entityDeclaration.external = true; - } break; - - case 92: { - if (!scanNData() && atEnd) { - resume(92); - return false; - } - EntityDeclaration &entityDeclaration = entityDeclarations.top(); - checkPublicLiteral((entityDeclaration.publicId = symString(3))); - entityDeclaration.systemId = symString(5); - entityDeclaration.external = true; - } break; - - case 93: { - EntityDeclaration &entityDeclaration = entityDeclarations.top(); - entityDeclaration.notationName = symString(3); - if (entityDeclaration.parameter) - raiseWellFormedError(QXmlStream::tr("NDATA in parameter entity declaration.")); - } - Q_FALLTHROUGH(); - - case 94: - case 95: { - if (referenceToUnparsedEntityDetected && !standalone) { - entityDeclarations.pop(); - break; - } - EntityDeclaration &entityDeclaration = entityDeclarations.top(); - if (!entityDeclaration.external) - entityDeclaration.value = symString(2); - auto &hash = entityDeclaration.parameter ? parameterEntityHash : entityHash; - if (!hash.contains(qToStringViewIgnoringNull(entityDeclaration.name))) { - Entity entity(entityDeclaration.name.toString(), - entityDeclaration.value.toString()); - entity.unparsed = (!entityDeclaration.notationName.isNull()); - entity.external = entityDeclaration.external; - hash.insert(qToStringViewIgnoringNull(entity.name), entity); - } - } break; - - case 96: { - setType(QXmlStreamReader::ProcessingInstruction); - int pos = sym(4).pos + sym(4).len; - processingInstructionTarget = symString(3); - if (scanUntil("?>")) { - processingInstructionData = QStringRef(&textBuffer, pos, textBuffer.size() - pos - 2); - if (!processingInstructionTarget.compare(QLatin1String("xml"), Qt::CaseInsensitive)) { - raiseWellFormedError(QXmlStream::tr("XML declaration not at start of document.")); - } - else if (!QXmlUtils::isNCName(processingInstructionTarget)) - raiseWellFormedError(QXmlStream::tr("%1 is an invalid processing instruction name.") - .arg(processingInstructionTarget)); - } else if (type != QXmlStreamReader::Invalid){ - resume(96); - return false; - } - } break; - - case 97: - setType(QXmlStreamReader::ProcessingInstruction); - processingInstructionTarget = symString(3); - if (!processingInstructionTarget.compare(QLatin1String("xml"), Qt::CaseInsensitive)) - raiseWellFormedError(QXmlStream::tr("Invalid processing instruction name.")); - break; - - case 98: - if (!scanAfterLangleBang() && atEnd) { - resume(98); - return false; - } - break; - - case 99: - if (!scanUntil("--")) { - resume(99); - return false; - } - break; - - case 100: { - setType(QXmlStreamReader::Comment); - int pos = sym(1).pos + 4; - text = QStringRef(&textBuffer, pos, textBuffer.size() - pos - 3); - } break; - - case 101: { - setType(QXmlStreamReader::Characters); - isCDATA = true; - isWhitespace = false; - int pos = sym(2).pos; - if (scanUntil("]]>", -1)) { - text = QStringRef(&textBuffer, pos, textBuffer.size() - pos - 3); - } else { - resume(101); - return false; - } - } break; - - case 102: { - if (!scanPublicOrSystem() && atEnd) { - resume(102); - return false; - } - NotationDeclaration ¬ationDeclaration = notationDeclarations.push(); - notationDeclaration.name = symString(3); - } break; - - case 103: { - NotationDeclaration ¬ationDeclaration = notationDeclarations.top(); - notationDeclaration.systemId = symString(3); - notationDeclaration.publicId.clear(); - } break; - - case 104: { - NotationDeclaration ¬ationDeclaration = notationDeclarations.top(); - notationDeclaration.systemId.clear(); - checkPublicLiteral((notationDeclaration.publicId = symString(3))); - } break; - - case 105: { - NotationDeclaration ¬ationDeclaration = notationDeclarations.top(); - checkPublicLiteral((notationDeclaration.publicId = symString(3))); - notationDeclaration.systemId = symString(5); - } break; - - case 129: - isWhitespace = false; - Q_FALLTHROUGH(); - - case 130: - sym(1).len += fastScanContentCharList(); - if (atEnd && !inParseEntity) { - resume(130); - return false; - } - break; - - case 139: - if (!textBuffer.isEmpty()) { - setType(QXmlStreamReader::Characters); - text = &textBuffer; - } - break; - - case 140: - case 141: - clearSym(); - break; - - case 142: - case 143: - sym(1) = sym(2); - break; - - case 144: - case 145: - case 146: - case 147: - sym(1).len += sym(2).len; - break; - - case 173: - if (normalizeLiterals) - textBuffer.data()[textBuffer.size()-1] = QLatin1Char(' '); - break; - - case 174: - sym(1).len += fastScanLiteralContent(); - if (atEnd) { - resume(174); - return false; - } - break; - - case 175: { - if (!QXmlUtils::isPublicID(symString(1))) { - raiseWellFormedError(QXmlStream::tr("%1 is an invalid PUBLIC identifier.").arg(symString(1))); - resume(175); - return false; - } - } break; - - case 176: - case 177: - clearSym(); - break; - - case 178: - case 179: - sym(1) = sym(2); - break; - - case 180: - case 181: - case 182: - case 183: - sym(1).len += sym(2).len; - break; - - case 213: - case 214: - clearSym(); - break; - - case 215: - case 216: - sym(1) = sym(2); - lastAttributeValue = symString(1); - break; - - case 217: - case 218: - case 219: - case 220: - sym(1).len += sym(2).len; - break; - - case 229: { - QStringRef prefix = symPrefix(1); - if (prefix.isEmpty() && symString(1) == QLatin1String("xmlns") && namespaceProcessing) { - NamespaceDeclaration &namespaceDeclaration = namespaceDeclarations.push(); - namespaceDeclaration.prefix.clear(); - - const QStringRef ns(symString(5)); - if(ns == QLatin1String("http://www.w3.org/2000/xmlns/") || - ns == QLatin1String("http://www.w3.org/XML/1998/namespace")) - raiseWellFormedError(QXmlStream::tr("Illegal namespace declaration.")); - else - namespaceDeclaration.namespaceUri = addToStringStorage(ns); - } else { - Attribute &attribute = attributeStack.push(); - attribute.key = sym(1); - attribute.value = sym(5); - - QStringRef attributeQualifiedName = symName(1); - bool normalize = false; - for (int a = 0; a < dtdAttributes.size(); ++a) { - DtdAttribute &dtdAttribute = dtdAttributes[a]; - if (!dtdAttribute.isCDATA - && dtdAttribute.tagName == qualifiedName - && dtdAttribute.attributeQualifiedName == attributeQualifiedName - ) { - normalize = true; - break; - } - } - if (normalize) { - // normalize attribute value (simplify and trim) - int pos = textBuffer.size(); - int n = 0; - bool wasSpace = true; - for (int i = 0; i < attribute.value.len; ++i) { - QChar c = textBuffer.at(attribute.value.pos + i); - if (c.unicode() == ' ') { - if (wasSpace) - continue; - wasSpace = true; - } else { - wasSpace = false; - } - textBuffer += textBuffer.at(attribute.value.pos + i); - ++n; - } - if (wasSpace) - while (n && textBuffer.at(pos + n - 1).unicode() == ' ') - --n; - attribute.value.pos = pos; - attribute.value.len = n; - } - if (prefix == QLatin1String("xmlns") && namespaceProcessing) { - NamespaceDeclaration &namespaceDeclaration = namespaceDeclarations.push(); - QStringRef namespacePrefix = symString(attribute.key); - QStringRef namespaceUri = symString(attribute.value); - attributeStack.pop(); - if (((namespacePrefix == QLatin1String("xml")) - ^ (namespaceUri == QLatin1String("http://www.w3.org/XML/1998/namespace"))) - || namespaceUri == QLatin1String("http://www.w3.org/2000/xmlns/") - || namespaceUri.isEmpty() - || namespacePrefix == QLatin1String("xmlns")) - raiseWellFormedError(QXmlStream::tr("Illegal namespace declaration.")); - - namespaceDeclaration.prefix = addToStringStorage(namespacePrefix); - namespaceDeclaration.namespaceUri = addToStringStorage(namespaceUri); - } - } - } break; - - case 235: { - normalizeLiterals = true; - Tag &tag = tagStack_push(); - prefix = tag.namespaceDeclaration.prefix = addToStringStorage(symPrefix(2)); - name = tag.name = addToStringStorage(symString(2)); - qualifiedName = tag.qualifiedName = addToStringStorage(symName(2)); - if ((!prefix.isEmpty() && !QXmlUtils::isNCName(prefix)) || !QXmlUtils::isNCName(name)) - raiseWellFormedError(QXmlStream::tr("Invalid XML name.")); - } break; - - case 236: - isEmptyElement = true; - Q_FALLTHROUGH(); - - case 237: - setType(QXmlStreamReader::StartElement); - resolveTag(); - if (tagStack.size() == 1 && hasSeenTag && !inParseEntity) - raiseWellFormedError(QXmlStream::tr("Extra content at end of document.")); - hasSeenTag = true; - break; - - case 238: { - setType(QXmlStreamReader::EndElement); - Tag &tag = tagStack_pop(); - - namespaceUri = tag.namespaceDeclaration.namespaceUri; - name = tag.name; - qualifiedName = tag.qualifiedName; - if (qualifiedName != symName(3)) - raiseWellFormedError(QXmlStream::tr("Opening and ending tag mismatch.")); - } break; - - case 239: - if (entitiesMustBeDeclared()) { - raiseWellFormedError(QXmlStream::tr("Entity '%1' not declared.").arg(unresolvedEntity)); - break; - } - setType(QXmlStreamReader::EntityReference); - name = &unresolvedEntity; - break; - - case 240: { - sym(1).len += sym(2).len + 1; - QStringView reference = symView(2); - if (entityHash.contains(reference)) { - Entity &entity = entityHash[reference]; - if (entity.unparsed) { - raiseWellFormedError(QXmlStream::tr("Reference to unparsed entity '%1'.").arg(reference)); - } else { - if (!entity.hasBeenParsed) { - parseEntity(entity.value); - entity.hasBeenParsed = true; - } - if (entity.literal) - putStringLiteral(entity.value); - else if (referenceEntity(entity)) - putReplacement(entity.value); - textBuffer.chop(2 + sym(2).len); - clearSym(); - } - break; - } - - if (entityResolver) { - QString replacementText = resolveUndeclaredEntity(reference.toString()); - if (!replacementText.isNull()) { - putReplacement(replacementText); - textBuffer.chop(2 + sym(2).len); - clearSym(); - break; - } - } - - injectToken(UNRESOLVED_ENTITY); - unresolvedEntity = symString(2).toString(); - textBuffer.chop(2 + sym(2).len); - clearSym(); - - } break; - - case 241: { - sym(1).len += sym(2).len + 1; - QStringView reference = symView(2); - if (parameterEntityHash.contains(reference)) { - referenceToParameterEntityDetected = true; - Entity &entity = parameterEntityHash[reference]; - if (entity.unparsed || entity.external) { - referenceToUnparsedEntityDetected = true; - } else { - if (referenceEntity(entity)) - putString(entity.value); - textBuffer.chop(2 + sym(2).len); - clearSym(); - } - } else if (entitiesMustBeDeclared()) { - raiseWellFormedError(QXmlStream::tr("Entity '%1' not declared.").arg(symString(2))); - } - } break; - - case 242: - sym(1).len += sym(2).len + 1; - break; - - case 243: { - sym(1).len += sym(2).len + 1; - QStringView reference = symView(2); - if (entityHash.contains(reference)) { - Entity &entity = entityHash[reference]; - if (entity.unparsed || entity.value.isNull()) { - raiseWellFormedError(QXmlStream::tr("Reference to external entity '%1' in attribute value.").arg(reference)); - break; - } - if (!entity.hasBeenParsed) { - parseEntity(entity.value); - entity.hasBeenParsed = true; - } - if (entity.literal) - putStringLiteral(entity.value); - else if (referenceEntity(entity)) - putReplacementInAttributeValue(entity.value); - textBuffer.chop(2 + sym(2).len); - clearSym(); - break; - } - - if (entityResolver) { - QString replacementText = resolveUndeclaredEntity(reference.toString()); - if (!replacementText.isNull()) { - putReplacement(replacementText); - textBuffer.chop(2 + sym(2).len); - clearSym(); - break; - } - } - if (entitiesMustBeDeclared()) { - raiseWellFormedError(QXmlStream::tr("Entity '%1' not declared.").arg(reference)); - } - } break; - - case 244: { - if (uint s = resolveCharRef(3)) { - if (s >= 0xffff) - putStringLiteral(QString::fromUcs4(&s, 1)); - else - putChar((LETTER << 16) | s); - - textBuffer.chop(3 + sym(3).len); - clearSym(); - } else { - raiseWellFormedError(QXmlStream::tr("Invalid character reference.")); - } - } break; - - case 247: - case 248: - sym(1).len += sym(2).len; - break; - - case 259: - sym(1).len += fastScanSpace(); - if (atEnd) { - resume(259); - return false; - } - break; - - case 262: { - sym(1).len += fastScanName(&sym(1).prefix); - if (atEnd) { - resume(262); - return false; - } - } break; - - case 263: - sym(1).len += fastScanName(); - if (atEnd) { - resume(263); - return false; - } - break; - - case 264: - case 265: - case 266: - case 267: - case 268: - sym(1).len += fastScanNMTOKEN(); - if (atEnd) { - resume(268); - return false; - } - - break; - - default: - ; - } // switch - act = state_stack[tos] = nt_action (act, lhs[r] - TERMINAL_COUNT); - if (type != QXmlStreamReader::NoToken) - return true; - } else { - parseError(); - break; - } - } - return false; -} -#endif //QT_NO_XMLSTREAMREADER.xml - - -#endif // QXMLSTREAM_P_H - diff --git a/src/corelib/xml/qxmlutils.cpp b/src/corelib/xml/qxmlutils.cpp deleted file mode 100644 index 01c84251fd..0000000000 --- a/src/corelib/xml/qxmlutils.cpp +++ /dev/null @@ -1,390 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtCore module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include - -#include "qxmlutils_p.h" - -QT_BEGIN_NAMESPACE - -/* TODO: - * - isNameChar() doesn't have to be public, it's only needed in - * qdom.cpp -- refactor fixedXmlName() to use isNCName() - * - A lot of functions can be inlined. - */ - -class QXmlCharRange -{ -public: - ushort min; - ushort max; -}; -typedef const QXmlCharRange *RangeIter; - -/*! - Performs a binary search between \a begin and \a end inclusive, to check whether \a - c is contained. Remember that the QXmlCharRange instances must be in numeric order. - */ -bool QXmlUtils::rangeContains(RangeIter begin, RangeIter end, const QChar c) -{ - const ushort cp(c.unicode()); - - // check the first two ranges "manually" as characters in that - // range are checked very often and we avoid the binary search below. - - if (cp <= begin->max) - return cp >= begin->min; - - ++begin; - - if (begin == end) - return false; - - if (cp <= begin->max) - return cp >= begin->min; - - while (begin != end) { - int delta = (end - begin) / 2; - RangeIter mid = begin + delta; - - if (mid->min > cp) - end = mid; - else if (mid->max < cp) - begin = mid; - else - return true; - - if (delta == 0) - break; - } - - return false; -} - -// [85] BaseChar ::= ... - -static const QXmlCharRange g_base_begin[] = -{ - {0x0041, 0x005A}, {0x0061, 0x007A}, {0x00C0, 0x00D6}, {0x00D8, 0x00F6}, {0x00F8, 0x00FF}, - {0x0100, 0x0131}, {0x0134, 0x013E}, {0x0141, 0x0148}, {0x014A, 0x017E}, {0x0180, 0x01C3}, - {0x01CD, 0x01F0}, {0x01F4, 0x01F5}, {0x01FA, 0x0217}, {0x0250, 0x02A8}, {0x02BB, 0x02C1}, - {0x0386, 0x0386}, {0x0388, 0x038A}, {0x038C, 0x038C}, {0x038E, 0x03A1}, {0x03A3, 0x03CE}, - {0x03D0, 0x03D6}, {0x03DA, 0x03DA}, {0x03DC, 0x03DC}, {0x03DE, 0x03DE}, {0x03E0, 0x03E0}, - {0x03E2, 0x03F3}, {0x0401, 0x040C}, {0x040E, 0x044F}, {0x0451, 0x045C}, {0x045E, 0x0481}, - {0x0490, 0x04C4}, {0x04C7, 0x04C8}, {0x04CB, 0x04CC}, {0x04D0, 0x04EB}, {0x04EE, 0x04F5}, - {0x04F8, 0x04F9}, {0x0531, 0x0556}, {0x0559, 0x0559}, {0x0561, 0x0586}, {0x05D0, 0x05EA}, - {0x05F0, 0x05F2}, {0x0621, 0x063A}, {0x0641, 0x064A}, {0x0671, 0x06B7}, {0x06BA, 0x06BE}, - {0x06C0, 0x06CE}, {0x06D0, 0x06D3}, {0x06D5, 0x06D5}, {0x06E5, 0x06E6}, {0x0905, 0x0939}, - {0x093D, 0x093D}, {0x0958, 0x0961}, {0x0985, 0x098C}, {0x098F, 0x0990}, {0x0993, 0x09A8}, - {0x09AA, 0x09B0}, {0x09B2, 0x09B2}, {0x09B6, 0x09B9}, {0x09DC, 0x09DD}, {0x09DF, 0x09E1}, - {0x09F0, 0x09F1}, {0x0A05, 0x0A0A}, {0x0A0F, 0x0A10}, {0x0A13, 0x0A28}, {0x0A2A, 0x0A30}, - {0x0A32, 0x0A33}, {0x0A35, 0x0A36}, {0x0A38, 0x0A39}, {0x0A59, 0x0A5C}, {0x0A5E, 0x0A5E}, - {0x0A72, 0x0A74}, {0x0A85, 0x0A8B}, {0x0A8D, 0x0A8D}, {0x0A8F, 0x0A91}, {0x0A93, 0x0AA8}, - {0x0AAA, 0x0AB0}, {0x0AB2, 0x0AB3}, {0x0AB5, 0x0AB9}, {0x0ABD, 0x0ABD}, {0x0AE0, 0x0AE0}, - {0x0B05, 0x0B0C}, {0x0B0F, 0x0B10}, {0x0B13, 0x0B28}, {0x0B2A, 0x0B30}, {0x0B32, 0x0B33}, - {0x0B36, 0x0B39}, {0x0B3D, 0x0B3D}, {0x0B5C, 0x0B5D}, {0x0B5F, 0x0B61}, {0x0B85, 0x0B8A}, - {0x0B8E, 0x0B90}, {0x0B92, 0x0B95}, {0x0B99, 0x0B9A}, {0x0B9C, 0x0B9C}, {0x0B9E, 0x0B9F}, - {0x0BA3, 0x0BA4}, {0x0BA8, 0x0BAA}, {0x0BAE, 0x0BB5}, {0x0BB7, 0x0BB9}, {0x0C05, 0x0C0C}, - {0x0C0E, 0x0C10}, {0x0C12, 0x0C28}, {0x0C2A, 0x0C33}, {0x0C35, 0x0C39}, {0x0C60, 0x0C61}, - {0x0C85, 0x0C8C}, {0x0C8E, 0x0C90}, {0x0C92, 0x0CA8}, {0x0CAA, 0x0CB3}, {0x0CB5, 0x0CB9}, - {0x0CDE, 0x0CDE}, {0x0CE0, 0x0CE1}, {0x0D05, 0x0D0C}, {0x0D0E, 0x0D10}, {0x0D12, 0x0D28}, - {0x0D2A, 0x0D39}, {0x0D60, 0x0D61}, {0x0E01, 0x0E2E}, {0x0E30, 0x0E30}, {0x0E32, 0x0E33}, - {0x0E40, 0x0E45}, {0x0E81, 0x0E82}, {0x0E84, 0x0E84}, {0x0E87, 0x0E88}, {0x0E8A, 0x0E8A}, - {0x0E8D, 0x0E8D}, {0x0E94, 0x0E97}, {0x0E99, 0x0E9F}, {0x0EA1, 0x0EA3}, {0x0EA5, 0x0EA5}, - {0x0EA7, 0x0EA7}, {0x0EAA, 0x0EAB}, {0x0EAD, 0x0EAE}, {0x0EB0, 0x0EB0}, {0x0EB2, 0x0EB3}, - {0x0EBD, 0x0EBD}, {0x0EC0, 0x0EC4}, {0x0F40, 0x0F47}, {0x0F49, 0x0F69}, {0x10A0, 0x10C5}, - {0x10D0, 0x10F6}, {0x1100, 0x1100}, {0x1102, 0x1103}, {0x1105, 0x1107}, {0x1109, 0x1109}, - {0x110B, 0x110C}, {0x110E, 0x1112}, {0x113C, 0x113C}, {0x113E, 0x113E}, {0x1140, 0x1140}, - {0x114C, 0x114C}, {0x114E, 0x114E}, {0x1150, 0x1150}, {0x1154, 0x1155}, {0x1159, 0x1159}, - {0x115F, 0x1161}, {0x1163, 0x1163}, {0x1165, 0x1165}, {0x1167, 0x1167}, {0x1169, 0x1169}, - {0x116D, 0x116E}, {0x1172, 0x1173}, {0x1175, 0x1175}, {0x119E, 0x119E}, {0x11A8, 0x11A8}, - {0x11AB, 0x11AB}, {0x11AE, 0x11AF}, {0x11B7, 0x11B8}, {0x11BA, 0x11BA}, {0x11BC, 0x11C2}, - {0x11EB, 0x11EB}, {0x11F0, 0x11F0}, {0x11F9, 0x11F9}, {0x1E00, 0x1E9B}, {0x1EA0, 0x1EF9}, - {0x1F00, 0x1F15}, {0x1F18, 0x1F1D}, {0x1F20, 0x1F45}, {0x1F48, 0x1F4D}, {0x1F50, 0x1F57}, - {0x1F59, 0x1F59}, {0x1F5B, 0x1F5B}, {0x1F5D, 0x1F5D}, {0x1F5F, 0x1F7D}, {0x1F80, 0x1FB4}, - {0x1FB6, 0x1FBC}, {0x1FBE, 0x1FBE}, {0x1FC2, 0x1FC4}, {0x1FC6, 0x1FCC}, {0x1FD0, 0x1FD3}, - {0x1FD6, 0x1FDB}, {0x1FE0, 0x1FEC}, {0x1FF2, 0x1FF4}, {0x1FF6, 0x1FFC}, {0x2126, 0x2126}, - {0x212A, 0x212B}, {0x212E, 0x212E}, {0x2180, 0x2182}, {0x3041, 0x3094}, {0x30A1, 0x30FA}, - {0x3105, 0x312C}, {0xAC00, 0xD7A3} -}; -static const RangeIter g_base_end = g_base_begin + sizeof(g_base_begin) / sizeof(QXmlCharRange); - -static const QXmlCharRange g_ideographic_begin[] = -{ - {0x3007, 0x3007}, {0x3021, 0x3029}, {0x4E00, 0x9FA5} -}; -static const RangeIter g_ideographic_end = g_ideographic_begin + sizeof(g_ideographic_begin) / sizeof(QXmlCharRange); - -bool QXmlUtils::isIdeographic(const QChar c) -{ - return rangeContains(g_ideographic_begin, g_ideographic_end, c); -} - -static const QXmlCharRange g_combining_begin[] = -{ - {0x0300, 0x0345}, {0x0360, 0x0361}, {0x0483, 0x0486}, {0x0591, 0x05A1}, {0x05A3, 0x05B9}, - {0x05BB, 0x05BD}, {0x05BF, 0x05BF}, {0x05C1, 0x05C2}, {0x05C4, 0x05C4}, {0x064B, 0x0652}, - {0x0670, 0x0670}, {0x06D6, 0x06DC}, {0x06DD, 0x06DF}, {0x06E0, 0x06E4}, {0x06E7, 0x06E8}, - {0x06EA, 0x06ED}, {0x0901, 0x0903}, {0x093C, 0x093C}, {0x093E, 0x094C}, {0x094D, 0x094D}, - {0x0951, 0x0954}, {0x0962, 0x0963}, {0x0981, 0x0983}, {0x09BC, 0x09BC}, {0x09BE, 0x09BE}, - {0x09BF, 0x09BF}, {0x09C0, 0x09C4}, {0x09C7, 0x09C8}, {0x09CB, 0x09CD}, {0x09D7, 0x09D7}, - {0x09E2, 0x09E3}, {0x0A02, 0x0A02}, {0x0A3C, 0x0A3C}, {0x0A3E, 0x0A3E}, {0x0A3F, 0x0A3F}, - {0x0A40, 0x0A42}, {0x0A47, 0x0A48}, {0x0A4B, 0x0A4D}, {0x0A70, 0x0A71}, {0x0A81, 0x0A83}, - {0x0ABC, 0x0ABC}, {0x0ABE, 0x0AC5}, {0x0AC7, 0x0AC9}, {0x0ACB, 0x0ACD}, {0x0B01, 0x0B03}, - {0x0B3C, 0x0B3C}, {0x0B3E, 0x0B43}, {0x0B47, 0x0B48}, {0x0B4B, 0x0B4D}, {0x0B56, 0x0B57}, - {0x0B82, 0x0B83}, {0x0BBE, 0x0BC2}, {0x0BC6, 0x0BC8}, {0x0BCA, 0x0BCD}, {0x0BD7, 0x0BD7}, - {0x0C01, 0x0C03}, {0x0C3E, 0x0C44}, {0x0C46, 0x0C48}, {0x0C4A, 0x0C4D}, {0x0C55, 0x0C56}, - {0x0C82, 0x0C83}, {0x0CBE, 0x0CC4}, {0x0CC6, 0x0CC8}, {0x0CCA, 0x0CCD}, {0x0CD5, 0x0CD6}, - {0x0D02, 0x0D03}, {0x0D3E, 0x0D43}, {0x0D46, 0x0D48}, {0x0D4A, 0x0D4D}, {0x0D57, 0x0D57}, - {0x0E31, 0x0E31}, {0x0E34, 0x0E3A}, {0x0E47, 0x0E4E}, {0x0EB1, 0x0EB1}, {0x0EB4, 0x0EB9}, - {0x0EBB, 0x0EBC}, {0x0EC8, 0x0ECD}, {0x0F18, 0x0F19}, {0x0F35, 0x0F35}, {0x0F37, 0x0F37}, - {0x0F39, 0x0F39}, {0x0F3E, 0x0F3E}, {0x0F3F, 0x0F3F}, {0x0F71, 0x0F84}, {0x0F86, 0x0F8B}, - {0x0F90, 0x0F95}, {0x0F97, 0x0F97}, {0x0F99, 0x0FAD}, {0x0FB1, 0x0FB7}, {0x0FB9, 0x0FB9}, - {0x20D0, 0x20DC}, {0x20E1, 0x20E1}, {0x302A, 0x302F}, {0x3099, 0x3099}, {0x309A, 0x309A} -}; -static const RangeIter g_combining_end = g_combining_begin + sizeof(g_combining_begin) / sizeof(QXmlCharRange); - -bool QXmlUtils::isCombiningChar(const QChar c) -{ - return rangeContains(g_combining_begin, g_combining_end, c); -} - -// [88] Digit ::= ... -static const QXmlCharRange g_digit_begin[] = -{ - {0x0030, 0x0039}, {0x0660, 0x0669}, {0x06F0, 0x06F9}, {0x0966, 0x096F}, {0x09E6, 0x09EF}, - {0x0A66, 0x0A6F}, {0x0AE6, 0x0AEF}, {0x0B66, 0x0B6F}, {0x0BE7, 0x0BEF}, {0x0C66, 0x0C6F}, - {0x0CE6, 0x0CEF}, {0x0D66, 0x0D6F}, {0x0E50, 0x0E59}, {0x0ED0, 0x0ED9}, {0x0F20, 0x0F29} -}; -static const RangeIter g_digit_end = g_digit_begin + sizeof(g_digit_begin) / sizeof(QXmlCharRange); - -bool QXmlUtils::isDigit(const QChar c) -{ - return rangeContains(g_digit_begin, g_digit_end, c); -} - -// [89] Extender ::= ... -static const QXmlCharRange g_extender_begin[] = -{ - {0x00B7, 0x00B7}, {0x02D0, 0x02D0}, {0x02D1, 0x02D1}, {0x0387, 0x0387}, {0x0640, 0x0640}, - {0x0E46, 0x0E46}, {0x0EC6, 0x0EC6}, {0x3005, 0x3005}, {0x3031, 0x3035}, {0x309D, 0x309E}, - {0x30FC, 0x30FE} -}; -static const RangeIter g_extender_end = g_extender_begin + sizeof(g_extender_begin) / sizeof(QXmlCharRange); - -bool QXmlUtils::isExtender(const QChar c) -{ - return rangeContains(g_extender_begin, g_extender_end, c); -} - -bool QXmlUtils::isBaseChar(const QChar c) -{ - return rangeContains(g_base_begin, g_base_end, c); -} - -/*! - \internal - - Determines whether \a encName is a valid instance of production [81]EncName in the XML 1.0 - specification. If it is, true is returned, otherwise false. - - \sa {http://www.w3.org/TR/REC-xml/#NT-EncName}, - {Extensible Markup Language (XML) 1.0 (Fourth Edition), [81] EncName} - */ -bool QXmlUtils::isEncName(QStringView encName) -{ - // Valid encoding names are given by "[A-Za-z][A-Za-z0-9._\\-]*" - if (encName.isEmpty()) - return false; - const auto first = encName.front().unicode(); - if (!((first >= 'a' && first <= 'z') || (first >= 'A' && first <= 'Z'))) - return false; - for (QChar ch : encName.mid(1)) { - const auto cp = ch.unicode(); - if ((cp >= 'a' && cp <= 'z') - || (cp >= 'A' && cp <= 'Z') - || (cp >= '0' && cp <= '9') - || cp == '.' || cp == '_' || cp == '-') { - continue; - } - return false; - } - return true; -} - -/*! - \internal - - Determines whether \a c is a valid instance of production [84]Letter in the XML 1.0 - specification. If it is, true is returned, otherwise false. - - \sa {http://www.w3.org/TR/REC-xml/#NT-Letter}, - {Extensible Markup Language (XML) 1.0 (Fourth Edition), [84] Letter} - */ -bool QXmlUtils::isLetter(const QChar c) -{ - return isBaseChar(c) || isIdeographic(c); -} - -/*! - \internal - - Determines whether \a c is a valid instance of production [2]Char in the XML 1.0 - specification. If it is, true is returned, otherwise false. - - \sa {http://www.w3.org/TR/REC-xml/#NT-Char}, - {Extensible Markup Language (XML) 1.0 (Fourth Edition), [2] Char} - */ -bool QXmlUtils::isChar(const QChar c) -{ - return (c.unicode() >= 0x0020 && c.unicode() <= 0xD7FF) - || c.unicode() == 0x0009 - || c.unicode() == 0x000A - || c.unicode() == 0x000D - || (c.unicode() >= 0xE000 && c.unicode() <= 0xFFFD); -} - -/*! - \internal - - Determines whether \a c is a valid instance of - production [4]NameChar in the XML 1.0 specification. If it - is, true is returned, otherwise false. - - \sa {http://www.w3.org/TR/REC-xml/#NT-NameChar}, - {Extensible Markup Language (XML) 1.0 (Fourth Edition), [4] NameChar} - */ -bool QXmlUtils::isNameChar(const QChar c) -{ - return isBaseChar(c) - || isDigit(c) - || c.unicode() == '.' - || c.unicode() == '-' - || c.unicode() == '_' - || c.unicode() == ':' - || isCombiningChar(c) - || isIdeographic(c) - || isExtender(c); -} - -/*! - \internal - - Determines whether \a c is a valid instance of - production [12] PubidLiteral in the XML 1.0 specification. If it - is, true is returned, otherwise false. - - \sa {http://www.w3.org/TR/REC-xml/#NT-PubidLiteral}, - {Extensible Markup Language (XML) 1.0 (Fourth Edition), [12] PubidLiteral} - */ -bool QXmlUtils::isPublicID(QStringView candidate) -{ - for (QChar ch : candidate) { - const ushort cp = ch.unicode(); - - if ((cp >= 'a' && cp <= 'z') - || (cp >= 'A' && cp <= 'Z') - || (cp >= '0' && cp <= '9')) - { - continue; - } - - switch (cp) - { - /* Fallthrough all these. */ - case 0x20: - case 0x0D: - case 0x0A: - case '-': - case '\'': - case '(': - case ')': - case '+': - case ',': - case '.': - case '/': - case ':': - case '=': - case '?': - case ';': - case '!': - case '*': - case '#': - case '@': - case '$': - case '_': - case '%': - continue; - default: - return false; - } - } - - return true; -} - -/*! - \internal - - Determines whether \a c is a valid instance of - production [4]NCName in the XML 1.0 Namespaces specification. If it - is, true is returned, otherwise false. - - \sa {http://www.w3.org/TR/REC-xml-names/#NT-NCName}, - {W3CNamespaces in XML 1.0 (Second Edition), [4] NCName} - */ -bool QXmlUtils::isNCName(QStringView ncName) -{ - if(ncName.isEmpty()) - return false; - - const QChar first(ncName.at(0)); - - if(!QXmlUtils::isLetter(first) && first.unicode() != '_' && first.unicode() != ':') - return false; - - for (QChar at : ncName) { - if(!QXmlUtils::isNameChar(at) || at == QLatin1Char(':')) - return false; - } - - return true; -} - -QT_END_NAMESPACE diff --git a/src/corelib/xml/qxmlutils_p.h b/src/corelib/xml/qxmlutils_p.h deleted file mode 100644 index db6bddd5be..0000000000 --- a/src/corelib/xml/qxmlutils_p.h +++ /dev/null @@ -1,90 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtCore module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QXMLUTILS_P_H -#define QXMLUTILS_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists for the convenience -// of qapplication_*.cpp, qwidget*.cpp and qfiledialog.cpp. This header -// file may change from version to version without notice, or even be removed. -// -// We mean it. -// - -#include -#include - -QT_BEGIN_NAMESPACE - -class QString; -class QChar; -class QXmlCharRange; - -/*! - \internal - \short This class contains helper functions related to XML, for validating character classes, - productions in the XML specification, and so on. - */ -class Q_CORE_EXPORT QXmlUtils -{ -public: - static bool isEncName(QStringView encName); - static bool isChar(const QChar c); - static bool isNameChar(const QChar c); - static bool isLetter(const QChar c); - static bool isNCName(QStringView ncName); - static bool isPublicID(QStringView candidate); - -private: - typedef const QXmlCharRange *RangeIter; - static bool rangeContains(RangeIter begin, RangeIter end, const QChar c); - static bool isBaseChar(const QChar c); - static bool isDigit(const QChar c); - static bool isExtender(const QChar c); - static bool isIdeographic(const QChar c); - static bool isCombiningChar(const QChar c); -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/corelib/xml/xml.pri b/src/corelib/xml/xml.pri deleted file mode 100644 index 2401c09ab7..0000000000 --- a/src/corelib/xml/xml.pri +++ /dev/null @@ -1,10 +0,0 @@ -# Qt xml core module - -HEADERS += \ - xml/qxmlstream.h \ - xml/qxmlstream_p.h \ - xml/qxmlutils_p.h - -SOURCES += \ - xml/qxmlstream.cpp \ - xml/qxmlutils.cpp diff --git a/src/tools/bootstrap/bootstrap.pro b/src/tools/bootstrap/bootstrap.pro index a88333f03a..a45382106a 100644 --- a/src/tools/bootstrap/bootstrap.pro +++ b/src/tools/bootstrap/bootstrap.pro @@ -32,7 +32,6 @@ SOURCES += \ ../../corelib/global/qrandom.cpp \ ../../corelib/io/qabstractfileengine.cpp \ ../../corelib/io/qbuffer.cpp \ - ../../corelib/io/qdatastream.cpp \ ../../corelib/io/qdebug.cpp \ ../../corelib/io/qdir.cpp \ ../../corelib/io/qdiriterator.cpp \ @@ -47,7 +46,6 @@ SOURCES += \ ../../corelib/io/qresource.cpp \ ../../corelib/io/qtemporarydir.cpp \ ../../corelib/io/qtemporaryfile.cpp \ - ../../corelib/io/qtextstream.cpp \ ../../corelib/io/qsavefile.cpp \ ../../corelib/io/qstandardpaths.cpp \ ../../corelib/io/qloggingcategory.cpp \ @@ -58,6 +56,17 @@ SOURCES += \ ../../corelib/kernel/qvariant.cpp \ ../../corelib/kernel/qsystemerror.cpp \ ../../corelib/plugin/quuid.cpp \ + ../../corelib/serialization/qdatastream.cpp \ + ../../corelib/serialization/qjson.cpp \ + ../../corelib/serialization/qjsondocument.cpp \ + ../../corelib/serialization/qjsonobject.cpp \ + ../../corelib/serialization/qjsonarray.cpp \ + ../../corelib/serialization/qjsonvalue.cpp \ + ../../corelib/serialization/qjsonparser.cpp \ + ../../corelib/serialization/qjsonwriter.cpp \ + ../../corelib/serialization/qtextstream.cpp \ + ../../corelib/serialization/qxmlutils.cpp \ + ../../corelib/serialization/qxmlstream.cpp \ ../../corelib/tools/qbitarray.cpp \ ../../corelib/tools/qbytearray.cpp \ ../../corelib/tools/qarraydata.cpp \ @@ -84,15 +93,6 @@ SOURCES += \ ../../corelib/tools/qstringlist.cpp \ ../../corelib/tools/qversionnumber.cpp \ ../../corelib/tools/qvsnprintf.cpp \ - ../../corelib/xml/qxmlutils.cpp \ - ../../corelib/xml/qxmlstream.cpp \ - ../../corelib/json/qjson.cpp \ - ../../corelib/json/qjsondocument.cpp \ - ../../corelib/json/qjsonobject.cpp \ - ../../corelib/json/qjsonarray.cpp \ - ../../corelib/json/qjsonvalue.cpp \ - ../../corelib/json/qjsonparser.cpp \ - ../../corelib/json/qjsonwriter.cpp \ ../../xml/dom/qdom.cpp \ ../../xml/sax/qxml.cpp -- cgit v1.2.3 From 67030038f3f07b3edb61eb56aa0c62bac67a7bf5 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Tue, 26 Dec 2017 10:18:49 -0200 Subject: Doc: explain how QXmlStream{Reader,Writer} deal with encoding in QString QXmlStreamWriter does not: it just writes the QString inputs to the output. QXmlStreamReader does: it first converts the QString to UTF-8 and stores locally. Then it tries to decode using the XML encoding header. Change-Id: I39332e0a867442d58082fffd1503d7652cb9fbff Reviewed-by: Martin Smith --- src/corelib/serialization/qxmlstream.cpp | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'src') diff --git a/src/corelib/serialization/qxmlstream.cpp b/src/corelib/serialization/qxmlstream.cpp index 9b5295a17e..a92dd71df5 100644 --- a/src/corelib/serialization/qxmlstream.cpp +++ b/src/corelib/serialization/qxmlstream.cpp @@ -409,6 +409,11 @@ QXmlStreamReader::QXmlStreamReader(const QByteArray &data) /*! Creates a new stream reader that reads from \a data. + This function should only be used if the XML header either says the encoding + is "UTF-8" or lacks any encoding information (the latter is the case of + QXmlStreamWriter writing to a QString). Any other encoding is likely going to + cause data corruption ("mojibake"). + \sa addData(), clear(), setDevice() */ QXmlStreamReader::QXmlStreamReader(const QString &data) @@ -3268,6 +3273,9 @@ QXmlStreamWriter::QXmlStreamWriter(QByteArray *array) /*! Constructs a stream writer that writes into \a string. + * + * Note that when writing to QString, QXmlStreamWriter ignores the codec set + * with setCodec(). See that function for more information. */ QXmlStreamWriter::QXmlStreamWriter(QString *string) : d_ptr(new QXmlStreamWriterPrivate(this)) @@ -3326,6 +3334,12 @@ QIODevice *QXmlStreamWriter::device() const gets written when you call writeStartDocument(). Call this function before calling writeStartDocument(). + \note When writing the XML to a QString, the codec information is ignored + and the XML header will not include any encoding information, since all + QStrings are UTF-16. If you later convert the QString to an 8-bit format, + you must arrange for the encoding information to be transmitted + out-of-band. + \sa codec() */ void QXmlStreamWriter::setCodec(QTextCodec *codec) @@ -3345,6 +3359,12 @@ void QXmlStreamWriter::setCodec(QTextCodec *codec) "ISO 8859-1", "UTF-8", and "UTF-16". If the encoding isn't recognized, nothing happens. + \note When writing the XML to a QString, the codec information is ignored + and the XML header will not include any encoding information, since all + QStrings are UTF-16. If you later convert the QString to an 8-bit format, + you must arrange for the encoding information to be transmitted + out-of-band. + \sa QTextCodec::codecForName() */ void QXmlStreamWriter::setCodec(const char *codecName) -- cgit v1.2.3 From 39f76b4325d1d5b7dd28e9e80f1a8ba7378c7e54 Mon Sep 17 00:00:00 2001 From: Allan Sandfeld Jensen Date: Thu, 25 Jan 2018 18:00:50 +0100 Subject: Fix broken rendering of RGB30 and ARGB32 on machines with AVX2 Two small changes late in the review process were flawed. Change-Id: I4b1f6e3fdb8e17000a2e11bc30aae1b29d9f43a9 Reviewed-by: Thiago Macieira --- src/gui/painting/qdrawhelper_avx2.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/gui/painting/qdrawhelper_avx2.cpp b/src/gui/painting/qdrawhelper_avx2.cpp index cf89f408b5..3a70524a9d 100644 --- a/src/gui/painting/qdrawhelper_avx2.cpp +++ b/src/gui/painting/qdrawhelper_avx2.cpp @@ -427,7 +427,7 @@ void QT_FASTCALL comp_func_Source_rgb64_avx2(QRgba64 *dst, const QRgba64 *src, i ::memcpy(dst, src, length * sizeof(QRgba64)); } else { const uint ca = const_alpha | (const_alpha << 8); // adjust to [0-65535] - const uint cia = 65535 - const_alpha; + const uint cia = 65535 - ca; int x = 0; @@ -493,7 +493,7 @@ void QT_FASTCALL comp_func_solid_SourceOver_rgb64_avx2(QRgba64 *destPixels, int if (const_alpha != 255) color = multiplyAlpha255(color, const_alpha); - const uint minusAlphaOfColor = ~ushort(color.alpha()); + const uint minusAlphaOfColor = 65535 - color.alpha(); int x = 0; quint64 *dst = (quint64 *) destPixels; const __m256i colorVector = _mm256_set1_epi64x(color); -- cgit v1.2.3 From c375503fa030de51e821db00e7ca6c1378eb34ba Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Sat, 20 Jan 2018 10:56:58 -0800 Subject: Add a few methods to check if a string is US-ASCII or Latin1 isLatin1(QLatin1String) is provided for completeness sake, in case some generic code operates on both QLatin1String and QString/QStringView. Change-Id: I5e421e32396d44e4b39efffd150b99a18eedf648 Reviewed-by: Allan Sandfeld Jensen Reviewed-by: Lars Knoll --- src/corelib/tools/qstring.cpp | 147 +++++++++++++++++++++++++++++++++- src/corelib/tools/qstring.h | 6 ++ src/corelib/tools/qstringalgorithms.h | 5 ++ 3 files changed, 157 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/corelib/tools/qstring.cpp b/src/corelib/tools/qstring.cpp index 5eeaa2a2a8..69751eb6dc 100644 --- a/src/corelib/tools/qstring.cpp +++ b/src/corelib/tools/qstring.cpp @@ -1,7 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. -** Copyright (C) 2016 Intel Corporation. +** Copyright (C) 2018 Intel Corporation. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtCore module of the Qt Toolkit. @@ -251,6 +251,151 @@ inline RetType UnrollTailLoop<0>::exec(Number, RetType returnIfExited, Functor1, } #endif +#ifdef __SSE2__ +static bool simdTestMask(const char *&ptr, const char *end, quint32 maskval) +{ +# if defined(__AVX2__) + // AVX2 implementation: test 32 bytes at a time + const __m256i mask256 = _mm256_broadcastd_epi32(_mm_cvtsi32_si128(maskval)); + while (ptr + 32 < end) { + __m256i data = _mm256_loadu_si256(reinterpret_cast(ptr)); + if (!_mm256_testz_si256(mask256, data)) + return false; + ptr += 32; + } + + const __m128i mask = _mm256_castsi256_si128(mask256); +# elif defined(__SSE4_1__) + // SSE 4.1 implementation: test 32 bytes at a time (two 16-byte + // comparisons, unrolled) + const __m128i mask = _mm_set1_epi32(maskval); + while (ptr + 32 < end) { + __m128i data1 = _mm_loadu_si128(reinterpret_cast(ptr)); + __m128i data2 = _mm_loadu_si128(reinterpret_cast(ptr + 16)); + if (!_mm_testz_si128(mask, data1)) + return false; + if (!_mm_testz_si128(mask, data2)) + return false; + ptr += 32; + } +# endif +# if defined(__SSE4_1__) + // AVX2 and SSE4.1: final 16-byte comparison + if (ptr + 16 < end) { + __m128i data1 = _mm_loadu_si128(reinterpret_cast(ptr)); + if (!_mm_testz_si128(mask, data1)) + return false; + ptr += 16; + } +# else + // SSE2 implementation: test 16 bytes at a time. + const __m128i mask = _mm_set1_epi32(maskval); + while (ptr + 16 < end) { + __m128i data = _mm_loadu_si128(reinterpret_cast(ptr)); + __m128i masked = _mm_andnot_si128(mask, data); + __m128i comparison = _mm_cmpeq_epi16(masked, _mm_setzero_si128()); + if (quint16(_mm_movemask_epi8(comparison)) != 0xffff) + return false; + ptr += 16; + } +# endif + + return true; +} +#endif + +bool QtPrivate::isAscii(QLatin1String s) Q_DECL_NOTHROW +{ + const char *ptr = s.begin(); + const char *end = s.end(); + +#if defined(__AVX2__) + if (!simdTestMask(ptr, end, 0x80808080)) + return false; +#elif defined(__SSE2__) + // Testing for the high bit can be done efficiently with just PMOVMSKB + while (ptr + 16 < end) { + __m128i data = _mm_loadu_si128(reinterpret_cast(ptr)); + quint32 mask = _mm_movemask_epi8(data); + if (mask) + return false; + ptr += 16; + } +#endif + + while (ptr + 4 < end) { + quint32 data = qFromUnaligned(ptr); + if (data & 0x80808080U) + return false; + ptr += 4; + } + + while (ptr != end) { + if (quint8(*ptr++) & 0x80) + return false; + } + return true; +} + +bool QtPrivate::isAscii(QStringView s) Q_DECL_NOTHROW +{ + const QChar *ptr = s.begin(); + const QChar *end = s.end(); + +#ifdef __SSE2__ + const char *ptr8 = reinterpret_cast(ptr); + const char *end8 = reinterpret_cast(end); + if (!simdTestMask(ptr8, end8, 0xff80ff80)) + return false; + ptr = reinterpret_cast(ptr8); +#endif + + while (ptr != end) { + if ((*ptr++).unicode() & 0xff80) + return false; + } + return true; +} + +bool QtPrivate::isLatin1(QStringView s) Q_DECL_NOTHROW +{ + const QChar *ptr = s.begin(); + const QChar *end = s.end(); + +#if defined(__SSE4_1__) + const char *ptr8 = reinterpret_cast(ptr); + const char *end8 = reinterpret_cast(end); + if (!simdTestMask(ptr8, end8, 0xff00ff00)) + return false; + ptr = reinterpret_cast(ptr8); +#elif defined(__SSE2__) + // Testing if every other byte is non-zero can be done efficiently by + // using PUNPCKHBW (unpack high order bytes) and comparing that to zero. + while (ptr + 32 < end) { + __m128i data1 = _mm_loadu_si128(reinterpret_cast(ptr)); + __m128i data2 = _mm_loadu_si128(reinterpret_cast(ptr + 16)); + __m128i high = _mm_unpackhi_epi8(data1, data2); + __m128i comparison = _mm_cmpeq_epi16(high, _mm_setzero_si128()); + if (_mm_movemask_epi8(comparison)) + return false; + ptr += 16; + } + if (ptr + 16 < end) { + __m128i data1 = _mm_loadu_si128(reinterpret_cast(ptr)); + __m128i high = _mm_unpackhi_epi8(data1, data1); + __m128i comparison = _mm_cmpeq_epi16(high, _mm_setzero_si128()); + if (_mm_movemask_epi8(comparison)) + return false; + } +#endif + + while (ptr != end) { + if ((*ptr++).unicode() > 0xff) + return false; + } + return true; +} + // conversion between Latin 1 and UTF-16 void qt_from_latin1(ushort *dst, const char *str, size_t size) Q_DECL_NOTHROW { diff --git a/src/corelib/tools/qstring.h b/src/corelib/tools/qstring.h index 808f388c89..b40a622c7c 100644 --- a/src/corelib/tools/qstring.h +++ b/src/corelib/tools/qstring.h @@ -201,6 +201,12 @@ Q_DECLARE_TYPEINFO(QLatin1String, Q_MOVABLE_TYPE); // Qt 4.x compatibility typedef QLatin1String QLatin1Literal; +// +// QLatin1String inline implementations +// +inline bool QtPrivate::isLatin1(QLatin1String) Q_DECL_NOTHROW +{ return true; } + // // QStringView members that require QLatin1String: // diff --git a/src/corelib/tools/qstringalgorithms.h b/src/corelib/tools/qstringalgorithms.h index 6146e525d9..8446d85239 100644 --- a/src/corelib/tools/qstringalgorithms.h +++ b/src/corelib/tools/qstringalgorithms.h @@ -82,6 +82,11 @@ Q_REQUIRED_RESULT Q_CORE_EXPORT QByteArray convertToLocal8Bit(QStringView str); Q_REQUIRED_RESULT Q_CORE_EXPORT QVector convertToUcs4(QStringView str); Q_REQUIRED_RESULT Q_CORE_EXPORT bool isRightToLeft(QStringView string); +Q_REQUIRED_RESULT Q_CORE_EXPORT bool isAscii(QLatin1String s) Q_DECL_NOTHROW; +Q_REQUIRED_RESULT Q_CORE_EXPORT bool isAscii(QStringView s) Q_DECL_NOTHROW; +Q_REQUIRED_RESULT bool isLatin1(QLatin1String s) Q_DECL_NOTHROW; // in qstring.h +Q_REQUIRED_RESULT Q_CORE_EXPORT bool isLatin1(QStringView s) Q_DECL_NOTHROW; + } // namespace QtPRivate QT_END_NAMESPACE -- cgit v1.2.3 From 08559d2b77c5a65a656c4de19c551fd2b291952c Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Mon, 22 Jan 2018 00:39:06 -0800 Subject: JSON: use the new isLatin1 function It's vectorized, so it should be faster than this version. Change-Id: I56b444f9d6274221a3b7fffd150c1519eb999cdc Reviewed-by: Lars Knoll --- src/corelib/serialization/qjson_p.h | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) (limited to 'src') diff --git a/src/corelib/serialization/qjson_p.h b/src/corelib/serialization/qjson_p.h index 131528376b..7743382806 100644 --- a/src/corelib/serialization/qjson_p.h +++ b/src/corelib/serialization/qjson_p.h @@ -154,14 +154,7 @@ static inline bool useCompressed(const QString &s) { if (s.length() >= 0x8000) return false; - const ushort *uc = (const ushort *)s.constData(); - const ushort *e = uc + s.length(); - while (uc < e) { - if (*uc > 0xff) - return false; - ++uc; - } - return true; + return QtPrivate::isLatin1(s); } static inline int qStringSize(const QString &string, bool compress) -- cgit v1.2.3 From 2b7de16fbe399daa00986f2d32d05cfe51966b66 Mon Sep 17 00:00:00 2001 From: David Faure Date: Sun, 28 Jan 2018 10:53:02 +0100 Subject: QMimeType: remove unwanted *.bin as preferredSuffix for octet-stream This leads to an automatically appended .bin when saving a file. https://bugs.freedesktop.org/show_bug.cgi?id=101667 https://bugs.kde.org/382437 Fixed upstream in shared-mime-info 1.10 Change-Id: I125a0bc72c91a082706bf2bf149adcf63ff1ec6b Reviewed-by: Thiago Macieira --- src/corelib/mimetypes/qmimetype.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/corelib/mimetypes/qmimetype.cpp b/src/corelib/mimetypes/qmimetype.cpp index 8f6237c1cb..d7590ecf1f 100644 --- a/src/corelib/mimetypes/qmimetype.cpp +++ b/src/corelib/mimetypes/qmimetype.cpp @@ -466,6 +466,8 @@ QStringList QMimeType::suffixes() const */ QString QMimeType::preferredSuffix() const { + if (isDefault()) // workaround for unwanted *.bin suffix for octet-stream, https://bugs.freedesktop.org/show_bug.cgi?id=101667, fixed upstream in 1.10 + return QString(); const QStringList suffixList = suffixes(); return suffixList.isEmpty() ? QString() : suffixList.at(0); } -- cgit v1.2.3 From 48bce2e8f0d787342f3e0f86335460fa25e8ac8f Mon Sep 17 00:00:00 2001 From: Rafael Roquetto Date: Wed, 1 Feb 2017 17:32:13 -0200 Subject: Support for LTTNG and ETW tracing This commit introduces minimal support for instrumentation within Qt. Currently, only LTTNG/Linux and ETW/Windows are supported. Change-Id: I59b48cf83acf5532a998bb493e6379e9177e14c8 Reviewed-by: Oswald Buddenhagen Reviewed-by: Shawn Rutledge Reviewed-by: Thiago Macieira --- src/corelib/configure.json | 36 +++- src/corelib/corelib.pro | 3 + src/corelib/global/qconfig-bootstrapped.h | 2 + src/corelib/global/qtrace_p.h | 126 +++++++++++++ src/corelib/kernel/qcoreapplication.cpp | 10 + src/corelib/plugin/qfactoryloader.cpp | 5 + src/corelib/plugin/qlibrary.cpp | 6 + src/corelib/plugin/quuid.cpp | 4 +- src/corelib/plugin/quuid.h | 4 +- src/corelib/qtcore.tracepoints | 5 + src/gui/gui.pro | 3 + src/gui/kernel/qguiapplication.cpp | 10 + src/gui/qtgui.tracepoints | 8 + src/gui/text/qfontdatabase.cpp | 10 + src/src.pro | 16 +- src/tools/tracegen/etw.cpp | 228 ++++++++++++++++++++++ src/tools/tracegen/etw.h | 48 +++++ src/tools/tracegen/helpers.cpp | 92 +++++++++ src/tools/tracegen/helpers.h | 57 ++++++ src/tools/tracegen/lttng.cpp | 215 +++++++++++++++++++++ src/tools/tracegen/lttng.h | 48 +++++ src/tools/tracegen/panic.cpp | 59 ++++++ src/tools/tracegen/panic.h | 45 +++++ src/tools/tracegen/provider.cpp | 304 ++++++++++++++++++++++++++++++ src/tools/tracegen/provider.h | 95 ++++++++++ src/tools/tracegen/qtheaders.cpp | 51 +++++ src/tools/tracegen/qtheaders.h | 45 +++++ src/tools/tracegen/tracegen.cpp | 109 +++++++++++ src/tools/tracegen/tracegen.pro | 21 +++ 29 files changed, 1657 insertions(+), 8 deletions(-) create mode 100644 src/corelib/global/qtrace_p.h create mode 100644 src/corelib/qtcore.tracepoints create mode 100644 src/gui/qtgui.tracepoints create mode 100644 src/tools/tracegen/etw.cpp create mode 100644 src/tools/tracegen/etw.h create mode 100644 src/tools/tracegen/helpers.cpp create mode 100644 src/tools/tracegen/helpers.h create mode 100644 src/tools/tracegen/lttng.cpp create mode 100644 src/tools/tracegen/lttng.h create mode 100644 src/tools/tracegen/panic.cpp create mode 100644 src/tools/tracegen/panic.h create mode 100644 src/tools/tracegen/provider.cpp create mode 100644 src/tools/tracegen/provider.h create mode 100644 src/tools/tracegen/qtheaders.cpp create mode 100644 src/tools/tracegen/qtheaders.h create mode 100644 src/tools/tracegen/tracegen.cpp create mode 100644 src/tools/tracegen/tracegen.pro (limited to 'src') diff --git a/src/corelib/configure.json b/src/corelib/configure.json index 8cd73d6ce4..9f9512942a 100644 --- a/src/corelib/configure.json +++ b/src/corelib/configure.json @@ -15,7 +15,8 @@ "posix-ipc": { "type": "boolean", "name": "ipc_posix" }, "pps": { "type": "boolean", "name": "qqnx_pps" }, "slog2": "boolean", - "syslog": "boolean" + "syslog": "boolean", + "trace": { "type": "optionalString", "values": [ "etw", "lttng", "no", "yes" ] } } }, @@ -147,6 +148,18 @@ "-lrt" ] }, + "lttng-ust": { + "label": "lttng-ust", + "test": { + "include": "lttng/ust-events.h", + "main": "lttng_session_destroy(nullptr);" + }, + "sources": [ + { "type": "pkgConfig", "args": "lttng-ust" }, + "-llttng-ust" + ], + "use": "libdl" + }, "pcre2": { "label": "PCRE2", "test": { @@ -858,6 +871,22 @@ "section": "Utilities", "output": [ "publicFeature" ] }, + "lttng": { + "label": "LTTNG", + "autoDetect": false, + "enable": "input.trace == 'lttng' || (input.trace =='yes' && config.linux)", + "disable": "input.trace == 'etw' || input.trace =='no'", + "condition": "config.linux && libs.lttng-ust", + "output": [ "privateFeature" ] + }, + "etw": { + "label": "ETW", + "autoDetect": false, + "enable": "input.trace == 'etw' || (input.trace == 'yes' && config.win32)", + "disable": "input.trace == 'lttng' || input.trace == 'no'", + "condition": "config.win32", + "output": [ "privateFeature" ] + }, "topleveldomain": { "label": "QUrl::topLevelDomain()", "purpose": "Provides support for extracting the top level domain from URLs. @@ -907,6 +936,11 @@ Please apply the patch corresponding to your Standard Library vendor, found in "glib", "iconv", "icu", + { + "message": "Tracing backend", + "type": "firstAvailableFeature", + "args": "etw lttng" + }, { "section": "Logging backends", "entries": [ diff --git a/src/corelib/corelib.pro b/src/corelib/corelib.pro index 4cce7c0df3..2079d2117f 100644 --- a/src/corelib/corelib.pro +++ b/src/corelib/corelib.pro @@ -6,6 +6,9 @@ MODULE = core # not corelib, as per project file MODULE_CONFIG = moc resources !isEmpty(QT_NAMESPACE): MODULE_DEFINES = QT_NAMESPACE=$$QT_NAMESPACE +TRACEPOINT_PROVIDER = $$PWD/qtcore.tracepoints +CONFIG += qt_tracepoints + CONFIG += $$MODULE_CONFIG DEFINES += $$MODULE_DEFINES DEFINES += QT_NO_USING_NAMESPACE QT_NO_FOREACH diff --git a/src/corelib/global/qconfig-bootstrapped.h b/src/corelib/global/qconfig-bootstrapped.h index 0df593941d..86ef1a2613 100644 --- a/src/corelib/global/qconfig-bootstrapped.h +++ b/src/corelib/global/qconfig-bootstrapped.h @@ -78,6 +78,7 @@ #define QT_FEATURE_cxx11_random (QT_HAS_INCLUDE() ? 1 : -1) #define QT_NO_DATASTREAM #define QT_FEATURE_datetimeparser -1 +#define QT_FEATURE_etw -1 #define QT_FEATURE_getauxval (QT_HAS_INCLUDE() ? 1 : -1) #define QT_FEATURE_getentropy -1 #define QT_NO_GEOM_VARIANT @@ -92,6 +93,7 @@ #else # define QT_FEATURE_linkat -1 #endif +#define QT_FEATURE_lttng -1 #define QT_NO_QOBJECT #define QT_FEATURE_process -1 #define QT_FEATURE_regularexpression -1 diff --git a/src/corelib/global/qtrace_p.h b/src/corelib/global/qtrace_p.h new file mode 100644 index 0000000000..ab8fc14af5 --- /dev/null +++ b/src/corelib/global/qtrace_p.h @@ -0,0 +1,126 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Rafael Roquetto +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTRACE_P_H +#define QTRACE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +/* + * The Qt tracepoints API consists of only three macros: + * + * - Q_TRACE(tracepoint, args...) + * Fires 'tracepoint' if it is enabled. + * + * - Q_UNCONDITIONAL_TRACE(tracepoint, args...) + * Fires 'tracepoint' unconditionally: no check is performed to query + * whether 'tracepoint' is enabled. + * + * - Q_TRACE_ENABLED(tracepoint) + * Returns 'true' if 'tracepoint' is enabled; false otherwise. + * + * When using LTTNG, Q_TRACE, Q_UNCONDITIONAL_TRACE and Q_TRACE_ENABLED map + * ultimately to tracepoint(), do_tracepoint() and tracepoint_enabled(), + * respectively, described on the lttng-ust manpage (man 3 lttng-ust). + * + * On ETW, Q_TRACE() and Q_UNCONDITIONAL_TRACE() are equivalent, ultimately + * amounting to a call to TraceLoggingWrite(), whereas Q_TRACE_ENABLED() + * wraps around TraceLoggingProviderEnabled(). + * + * A tracepoint provider is defined in a separate file, that follows the + * following format: + * + * tracepoint_name(arg_type arg_name, ...) + * + * For instance: + * + * qcoreapplication_ctor(int argc, const char * const argv) + * qcoreapplication_foo(int argc, const char[10] argv) + * qcoreapplication_baz(const char[len] some_string, unsigned int len) + * qcoreapplication_qstring(const QString &foo) + * qcoreapplication_qrect(const QRect &rect) + * + * The provider file is then parsed by src/tools/tracegen, which can be + * switched to output either ETW or LTTNG tracepoint definitions. The provider + * name is deduced to be basename(provider_file). + * + * To use the above (inside qtcore), you need to include + * . After that, the following call becomes + * possible: + * + * Q_TRACE(qcoreapplication_qrect, myRect); + * + * Currently, all C++ primitive non-pointer types are supported for + * arguments. Additionally, char * is supported, and is assumed to + * be a NULL-terminated string. Finally, the following subset of Qt types also + * currently supported: + * + * - QString + * - QByteArray + * - QUrl + * - QRect + * + * Dynamic arrays are supported using the syntax illustrated by + * qcoreapplication_baz above. + */ + +QT_BEGIN_NAMESPACE + +#if defined(Q_TRACEPOINT) && !defined(QT_BOOTSTRAPPED) +# define Q_TRACE(x, ...) QtPrivate::trace_ ## x(__VA_ARGS__) +# define Q_UNCONDITIONAL_TRACE(x, ...) QtPrivate::do_trace_ ## x(__VA_ARGS__) +# define Q_TRACE_ENABLED(x) QtPrivate::trace_ ## x ## _enabled() +#else +# define Q_TRACE(x, ...) +# define Q_UNCONDITIONAL_TRACE(x, ...) +# define Q_TRACE_ENABLED(x) false +#endif // defined(Q_TRACEPOINT) && !defined(QT_BOOTSTRAPPED) + +QT_END_NAMESPACE + +#endif // QTRACE_P_H diff --git a/src/corelib/kernel/qcoreapplication.cpp b/src/corelib/kernel/qcoreapplication.cpp index e93a14c116..4bab0b9f01 100644 --- a/src/corelib/kernel/qcoreapplication.cpp +++ b/src/corelib/kernel/qcoreapplication.cpp @@ -116,6 +116,12 @@ # include #endif +#ifdef QT_BOOTSTRAPPED +#include +#else +#include +#endif + #include QT_BEGIN_NAMESPACE @@ -790,6 +796,8 @@ QCoreApplication::QCoreApplication(int &argc, char **argv void QCoreApplicationPrivate::init() { + Q_TRACE(qcoreapplicationprivate_init_entry); + #if defined(Q_OS_MACOS) QMacAutoReleasePool pool; #endif @@ -873,6 +881,8 @@ void QCoreApplicationPrivate::init() #ifndef QT_NO_QOBJECT is_app_running = true; // No longer starting up. #endif + + Q_TRACE(qcoreapplicationprivate_init_exit); } /*! diff --git a/src/corelib/plugin/qfactoryloader.cpp b/src/corelib/plugin/qfactoryloader.cpp index 0cc193c325..a4be18a67f 100644 --- a/src/corelib/plugin/qfactoryloader.cpp +++ b/src/corelib/plugin/qfactoryloader.cpp @@ -54,6 +54,8 @@ #include "qjsonobject.h" #include "qjsonarray.h" +#include + QT_BEGIN_NAMESPACE class QFactoryLoaderPrivate : public QObjectPrivate @@ -142,6 +144,9 @@ void QFactoryLoader::update() if (qt_debug_component()) { qDebug() << "QFactoryLoader::QFactoryLoader() looking at" << fileName; } + + Q_TRACE(qfactoryloader_update, fileName); + library = QLibraryPrivate::findOrCreate(QFileInfo(fileName).canonicalFilePath()); if (!library->isPlugin()) { if (qt_debug_component()) { diff --git a/src/corelib/plugin/qlibrary.cpp b/src/corelib/plugin/qlibrary.cpp index ebad7f1751..bca6918b4a 100644 --- a/src/corelib/plugin/qlibrary.cpp +++ b/src/corelib/plugin/qlibrary.cpp @@ -64,6 +64,8 @@ #include "qelfparser_p.h" #include "qmachparser_p.h" +#include + QT_BEGIN_NAMESPACE #ifdef QT_NO_DEBUG @@ -535,6 +537,8 @@ bool QLibraryPrivate::load() if (fileName.isEmpty()) return false; + Q_TRACE(qlibraryprivate_load_entry, fileName); + bool ret = load_sys(); if (qt_debug_component()) { if (ret) { @@ -551,6 +555,8 @@ bool QLibraryPrivate::load() installCoverageTool(this); } + Q_TRACE(qlibraryprivate_load_exit, ret); + return ret; } diff --git a/src/corelib/plugin/quuid.cpp b/src/corelib/plugin/quuid.cpp index 3a1c0495fe..5dfbf23c33 100644 --- a/src/corelib/plugin/quuid.cpp +++ b/src/corelib/plugin/quuid.cpp @@ -143,7 +143,6 @@ static QUuid _q_uuidFromHex(const char *src) return QUuid(); } -#ifndef QT_BOOTSTRAPPED static QUuid createFromName(const QUuid &ns, const QByteArray &baseData, QCryptographicHash::Algorithm algorithm, int version) { QByteArray hashResult; @@ -166,7 +165,6 @@ static QUuid createFromName(const QUuid &ns, const QByteArray &baseData, QCrypto return result; } -#endif /*! \class QUuid @@ -488,12 +486,12 @@ QUuid QUuid::createUuidV3(const QUuid &ns, const QByteArray &baseData) { return createFromName(ns, baseData, QCryptographicHash::Md5, 3); } +#endif QUuid QUuid::createUuidV5(const QUuid &ns, const QByteArray &baseData) { return createFromName(ns, baseData, QCryptographicHash::Sha1, 5); } -#endif /*! Creates a QUuid object from the binary representation of the UUID, as diff --git a/src/corelib/plugin/quuid.h b/src/corelib/plugin/quuid.h index ee0a9f9dac..014b69831e 100644 --- a/src/corelib/plugin/quuid.h +++ b/src/corelib/plugin/quuid.h @@ -192,18 +192,20 @@ public: static QUuid createUuid(); #ifndef QT_BOOTSTRAPPED static QUuid createUuidV3(const QUuid &ns, const QByteArray &baseData); +#endif static QUuid createUuidV5(const QUuid &ns, const QByteArray &baseData); +#ifndef QT_BOOTSTRAPPED static inline QUuid createUuidV3(const QUuid &ns, const QString &baseData) { return QUuid::createUuidV3(ns, baseData.toUtf8()); } +#endif static inline QUuid createUuidV5(const QUuid &ns, const QString &baseData) { return QUuid::createUuidV5(ns, baseData.toUtf8()); } -#endif QUuid::Variant variant() const Q_DECL_NOTHROW; QUuid::Version version() const Q_DECL_NOTHROW; diff --git a/src/corelib/qtcore.tracepoints b/src/corelib/qtcore.tracepoints new file mode 100644 index 0000000000..e6b666ac74 --- /dev/null +++ b/src/corelib/qtcore.tracepoints @@ -0,0 +1,5 @@ +qcoreapplicationprivate_init_entry() +qcoreapplicationprivate_init_exit() +qfactoryloader_update(const QString &fileName) +qlibraryprivate_load_entry(const QString &fileName) +qlibraryprivate_load_exit(bool success) diff --git a/src/gui/gui.pro b/src/gui/gui.pro index f8cec00b82..759d6f3cbf 100644 --- a/src/gui/gui.pro +++ b/src/gui/gui.pro @@ -95,3 +95,6 @@ qtConfig(angle) { qtConfig(egl): CMAKE_EGL_INCDIRS = $$cmakePortablePaths($$QMAKE_INCDIR_EGL) QMAKE_DYNAMIC_LIST_FILE = $$PWD/QtGui.dynlist + +TRACEPOINT_PROVIDER = $$PWD/qtgui.tracepoints +CONFIG += qt_tracepoints diff --git a/src/gui/kernel/qguiapplication.cpp b/src/gui/kernel/qguiapplication.cpp index 3d85f47f34..70b4869442 100644 --- a/src/gui/kernel/qguiapplication.cpp +++ b/src/gui/kernel/qguiapplication.cpp @@ -111,6 +111,8 @@ # include #endif // Q_OS_WIN +#include + #include QT_BEGIN_NAMESPACE @@ -1360,6 +1362,8 @@ void QGuiApplicationPrivate::eventDispatcherReady() void QGuiApplicationPrivate::init() { + Q_TRACE(qguiapplicationprivate_init_entry); + #if defined(Q_OS_MACOS) QMacAutoReleasePool pool; #endif @@ -1522,6 +1526,8 @@ void QGuiApplicationPrivate::init() if (!QGuiApplicationPrivate::displayName) QObject::connect(q, &QGuiApplication::applicationNameChanged, q, &QGuiApplication::applicationDisplayNameChanged); + + Q_TRACE(qguiapplicationprivate_init_exit); } extern void qt_cleanupFontDatabase(); @@ -1756,6 +1762,8 @@ bool QGuiApplicationPrivate::processNativeEvent(QWindow *window, const QByteArra void QGuiApplicationPrivate::processWindowSystemEvent(QWindowSystemInterfacePrivate::WindowSystemEvent *e) { + Q_TRACE(qguiapplicationprivate_processwsevents_entry, e->type); + switch(e->type) { case QWindowSystemInterfacePrivate::Mouse: QGuiApplicationPrivate::processMouseEvent(static_cast(e)); @@ -1864,6 +1872,8 @@ void QGuiApplicationPrivate::processWindowSystemEvent(QWindowSystemInterfacePriv qWarning() << "Unknown user input event type:" << e->type; break; } + + Q_TRACE(qguiapplicationprivate_processwsevents_exit, e->type); } /*! \internal diff --git a/src/gui/qtgui.tracepoints b/src/gui/qtgui.tracepoints new file mode 100644 index 0000000000..aa8a8ede57 --- /dev/null +++ b/src/gui/qtgui.tracepoints @@ -0,0 +1,8 @@ +qfontdatabase_addapplicationfont(const QString &filename) +qfontdatabase_load(const QString &family, int pointSize) +qfontdatabase_loadengine(const QString &family, int pointSize) +qfontdatabaseprivate_addappfont(const QString &fileName) +qguiapplicationprivate_init_entry() +qguiapplicationprivate_init_exit() +qguiapplicationprivate_processwsevents_entry(int type) +qguiapplicationprivate_processwsevents_exit(int type) diff --git a/src/gui/text/qfontdatabase.cpp b/src/gui/text/qfontdatabase.cpp index 4be2197c68..404a722e29 100644 --- a/src/gui/text/qfontdatabase.cpp +++ b/src/gui/text/qfontdatabase.cpp @@ -74,6 +74,7 @@ # define FM_DEBUG if (false) qDebug #endif +#include QT_BEGIN_NAMESPACE @@ -1009,6 +1010,8 @@ QFontEngine *loadEngine(int script, const QFontDef &request, QFontEngine *engine = loadSingleEngine(script, request, family, foundry, style, size); if (engine && !(request.styleStrategy & QFont::NoFontMerging) && !engine->symbol) { + Q_TRACE(qfontdatabase_loadengine, request.family, request.pointSize); + QPlatformFontDatabase *pfdb = QGuiApplicationPrivate::platformIntegration()->fontDatabase(); QFontEngineMulti *pfMultiEngine = pfdb->fontEngineMulti(engine, QChar::Script(script)); if (!request.fallBackFamilies.isEmpty()) { @@ -2439,6 +2442,8 @@ int QFontDatabasePrivate::addAppFont(const QByteArray &fontData, const QString & font.data = fontData; font.fileName = fileName; + Q_TRACE(qfontdatabaseprivate_addappfont, fileName); + int i; for (i = 0; i < applicationFonts.count(); ++i) if (applicationFonts.at(i).families.isEmpty()) @@ -2494,6 +2499,9 @@ int QFontDatabase::addApplicationFont(const QString &fileName) QFile f(fileName); if (!f.open(QIODevice::ReadOnly)) return -1; + + Q_TRACE(qfontdatabase_addapplicationfont, fileName); + data = f.readAll(); } QMutexLocker locker(fontDatabaseMutex()); @@ -2792,6 +2800,8 @@ void QFontDatabase::load(const QFontPrivate *d, int script) QFontEngine *fe = nullptr; + Q_TRACE(qfontdatabase_load, req.family, req.pointSize); + req.fallBackFamilies = fallBackFamilies; if (!req.fallBackFamilies.isEmpty()) req.family = req.fallBackFamilies.takeFirst(); diff --git a/src/src.pro b/src/src.pro index a2064b1362..3b93b1a9d8 100644 --- a/src/src.pro +++ b/src/src.pro @@ -1,6 +1,6 @@ TEMPLATE = subdirs -QT_FOR_CONFIG += gui-private +QT_FOR_CONFIG += core-private gui-private include($$OUT_PWD/corelib/qtcore-config.pri) include($$OUT_PWD/gui/qtgui-config.pri) @@ -30,6 +30,10 @@ src_tools_qlalr.target = sub-qlalr force_bootstrap: src_tools_qlalr.depends = src_tools_bootstrap else: src_tools_qlalr.depends = src_corelib +src_tools_tracegen.subdir = tools/tracegen +src_tools_tracegen.target = sub-tracegen +src_tools_tracegen.depends = src_tools_bootstrap + src_tools_uic.subdir = tools/uic src_tools_uic.target = sub-uic force_bootstrap: src_tools_uic.depends = src_tools_bootstrap @@ -152,8 +156,13 @@ qtConfig(regularexpression):pcre2 { SUBDIRS += src_3rdparty_pcre2 src_corelib.depends += src_3rdparty_pcre2 } -SUBDIRS += src_corelib src_tools_qlalr TOOLS = src_tools_moc src_tools_rcc src_tools_qlalr src_tools_qfloat16_tables +!force_bootstrap:if(qtConfig(lttng)|qtConfig(etw)) { + SUBDIRS += src_tools_tracegen + src_corelib.depends += src_tools_tracegen + TOOLS += src_tools_tracegen +} +SUBDIRS += src_corelib src_tools_qlalr win32:SUBDIRS += src_winmain qtConfig(network) { SUBDIRS += src_network @@ -220,7 +229,8 @@ android: SUBDIRS += src_android src_3rdparty_gradle TR_EXCLUDE = \ src_tools_bootstrap src_tools_moc src_tools_rcc src_tools_uic src_tools_qlalr \ src_tools_bootstrap_dbus src_tools_qdbusxml2cpp src_tools_qdbuscpp2xml \ - src_3rdparty_pcre2 src_3rdparty_harfbuzzng src_3rdparty_freetype + src_3rdparty_pcre2 src_3rdparty_harfbuzzng src_3rdparty_freetype \ + src_tools_tracegen sub-tools.depends = $$TOOLS QMAKE_EXTRA_TARGETS = sub-tools diff --git a/src/tools/tracegen/etw.cpp b/src/tools/tracegen/etw.cpp new file mode 100644 index 0000000000..07f2d114b6 --- /dev/null +++ b/src/tools/tracegen/etw.cpp @@ -0,0 +1,228 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Rafael Roquetto +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "etw.h" +#include "provider.h" +#include "helpers.h" +#include "qtheaders.h" + +#include +#include +#include +#include +#include + +static inline QString providerVar(const QString &providerName) +{ + return providerName + QLatin1String("_provider"); +} + +static void writeEtwMacro(QTextStream &stream, const Tracepoint::Field &field) +{ + const QString &name = field.name; + + switch (field.backendType) { + case Tracepoint::Field::QtString: + stream << "TraceLoggingCountedWideString(reinterpret_cast(" + << name << ".utf16()), " << name << ".size(), \"" << name << "\")"; + return; + case Tracepoint::Field::QtByteArray: + stream << "TraceLoggingBinary(" << name << ".constData(), " + << name << ".size(), \"" << name << "\")"; + return; + case Tracepoint::Field::QtUrl: + stream << "TraceLoggingValue(" << name << ".toEncoded().constData(), \"" << name << "\")"; + return; + case Tracepoint::Field::QtRect: + stream << "TraceLoggingValue(" << name << ".x(), \"x\"), " + << "TraceLoggingValue(" << name << ".y(), \"y\"), " + << "TraceLoggingValue(" << name << ".width(), \"width\"), " + << "TraceLoggingValue(" << name << ".height(), \"height\")"; + return; + default: + break; + } + + stream << "TraceLoggingValue(" << name << ", \"" << name << "\")"; +} + +static QString createGuid(const QUuid &uuid) +{ + QString guid; + + QTextStream stream(&guid); + + hex(stream); + + stream << "(" + << "0x" << uuid.data1 << ", " + << "0x" << uuid.data2 << ", " + << "0x" << uuid.data3 << ", " + << "0x" << uuid.data4[0] << ", " + << "0x" << uuid.data4[1] << ", " + << "0x" << uuid.data4[2] << ", " + << "0x" << uuid.data4[3] << ", " + << "0x" << uuid.data4[4] << ", " + << "0x" << uuid.data4[5] << ", " + << "0x" << uuid.data4[6] << ", " + << "0x" << uuid.data4[7] + << ")"; + + return guid; +} + +static void writePrologue(QTextStream &stream, const QString &fileName, const QString &providerName) +{ + QUuid uuid = QUuid::createUuidV5(QUuid(), providerName.toLocal8Bit()); + + const QString provider = providerVar(providerName); + const QString guard = includeGuard(fileName); + const QString guid = createGuid(uuid); + const QString guidString = uuid.toString(); + + stream << "#ifndef " << guard << "\n" + << "#define " << guard << "\n" + << "#include \n" + << "#include \n"; + + /* TraceLogging API macros cannot deal with UTF8 + * source files, so we work around it like this + */ + stream << "#undef _TlgPragmaUtf8Begin\n" + "#undef _TlgPragmaUtf8End\n" + "#define _TlgPragmaUtf8Begin\n" + "#define _TlgPragmaUtf8End\n"; + + stream << qtHeaders(); + + stream << "\n"; + + stream << "#ifdef TRACEPOINT_DEFINE\n" + << "/* " << guidString << " */\n" + << "TRACELOGGING_DEFINE_PROVIDER(" << provider << ", \"" + << providerName <<"\", " << guid << ");\n\n"; + + stream << "static inline void registerProvider()\n" + << "{\n" + << " TraceLoggingRegister(" << provider << ");\n" + << "}\n\n"; + + stream << "static inline void unregisterProvider()\n" + << "{\n" + << " TraceLoggingUnregister(" << provider << ");\n" + << "}\n"; + + stream << "Q_CONSTRUCTOR_FUNCTION(registerProvider)\n" + << "Q_DESTRUCTOR_FUNCTION(unregisterProvider)\n\n"; + + stream << "#else\n" + << "TRACELOGGING_DECLARE_PROVIDER(" << provider << ");\n" + << "#endif // TRACEPOINT_DEFINE\n\n"; +} + +static void writeEpilogue(QTextStream &stream, const QString &fileName) +{ + stream << "\n#endif // " << includeGuard(fileName) << "\n" + << "#include \n"; +} + +static void writeWrapper(QTextStream &stream, const Tracepoint &tracepoint, + const QString &providerName) +{ + const QString argList = formatFunctionSignature(tracepoint.args); + const QString paramList = formatParameterList(tracepoint.args, ETW); + const QString &name = tracepoint.name; + const QString includeGuard = QStringLiteral("TP_%1_%2").arg(providerName).arg(name).toUpper(); + const QString provider = providerVar(providerName); + + stream << "\n"; + + stream << "inline void trace_" << name << "(" << argList << ")\n" + << "{\n" + << " TraceLoggingWrite(" << provider << ", \"" << name << "\""; + + for (const Tracepoint::Field &field : tracepoint.fields) { + stream << ",\n"; + stream << " "; + writeEtwMacro(stream, field); + } + + stream << ");\n" + << "}\n\n"; + + stream << "inline void do_trace_" << name << "(" << argList << ")\n" + << "{\n" + << " trace_" << name << "(" << paramList << ");\n" + << "}\n"; + + stream << "inline bool trace_" << name << "_enabled()\n" + << "{\n" + << " return TraceLoggingProviderEnabled(" << provider << ", 0, 0);\n" + << "}\n"; +} + +static void writeTracepoints(QTextStream &stream, const Provider &provider) +{ + if (provider.tracepoints.isEmpty()) + return; + + const QString includeGuard = QStringLiteral("TP_%1_PROVIDER").arg(provider.name).toUpper(); + + stream << "#if !defined(" << includeGuard << ") && !defined(TRACEPOINT_DEFINE)\n" + << "#define " << includeGuard << "\n" + << "namespace QtPrivate {\n"; + + for (const Tracepoint &t : provider.tracepoints) + writeWrapper(stream, t, provider.name); + + stream << "} // namespace QtPrivate\n" + << "#endif // " << includeGuard << "\n\n"; +} + +void writeEtw(QFile &file, const Provider &provider) +{ + QTextStream stream(&file); + + const QString fileName = QFileInfo(file.fileName()).fileName(); + + writePrologue(stream, fileName, provider.name); + writeTracepoints(stream, provider); + writeEpilogue(stream, fileName); +} + diff --git a/src/tools/tracegen/etw.h b/src/tools/tracegen/etw.h new file mode 100644 index 0000000000..5fc9b57eaa --- /dev/null +++ b/src/tools/tracegen/etw.h @@ -0,0 +1,48 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Rafael Roquetto +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef ETW_H +#define ETW_H + +struct Provider; +class QFile; + +void writeEtw(QFile &device, const Provider &p); + +#endif // ETW_H diff --git a/src/tools/tracegen/helpers.cpp b/src/tools/tracegen/helpers.cpp new file mode 100644 index 0000000000..f0ac7ed47f --- /dev/null +++ b/src/tools/tracegen/helpers.cpp @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Rafael Roquetto +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "helpers.h" +#include + +QString includeGuard(const QString &filename) +{ + QString guard = filename.toUpper(); + + for (int i = 0; i < guard.size(); ++i) { + if (!guard.at(i).isLetterOrNumber()) + guard[i] = QChar('_'); + } + + return guard; +} + +template +static QString joinArguments(const QVector &args, T joinFunction) +{ + QString ret; + bool first = true; + + for (const Tracepoint::Argument &arg : args) { + if (!first) + ret += QLatin1String(", "); + + ret += joinFunction(arg); + + first = false; + } + + return ret; +} + +QString formatFunctionSignature(const QVector &args) +{ + return joinArguments(args, [](const Tracepoint::Argument &arg) { + return QStringLiteral("%1 %2").arg(arg.type).arg(arg.name); + }); +} + +QString formatParameterList(const QVector &args, ParamType type) +{ + if (type == LTTNG) { + QString ret; + + for (const Tracepoint::Argument &arg : args) + ret += QLatin1String(", ") + arg.name; + + return ret; + } + + return joinArguments(args, [](const Tracepoint::Argument &arg) { return arg.name; }); +} diff --git a/src/tools/tracegen/helpers.h b/src/tools/tracegen/helpers.h new file mode 100644 index 0000000000..77f16a0da6 --- /dev/null +++ b/src/tools/tracegen/helpers.h @@ -0,0 +1,57 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Rafael Roquetto +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef HELPERS_H +#define HELPERS_H + +#include "provider.h" + +#include +#include + +enum ParamType { + LTTNG, + ETW +}; + +QString includeGuard(const QString &filename); +QString formatFunctionSignature(const QVector &args); +QString formatParameterList(const QVector &args, ParamType type); + +#endif // HELPERS_H diff --git a/src/tools/tracegen/lttng.cpp b/src/tools/tracegen/lttng.cpp new file mode 100644 index 0000000000..6c0d8cc88b --- /dev/null +++ b/src/tools/tracegen/lttng.cpp @@ -0,0 +1,215 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Rafael Roquetto +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "lttng.h" +#include "provider.h" +#include "helpers.h" +#include "panic.h" +#include "qtheaders.h" + +#include +#include +#include +#include +#include + +static void writeCtfMacro(QTextStream &stream, const Tracepoint::Field &field) +{ + const QString ¶mType = field.paramType; + const QString &name = field.name; + const QString &seqLen = field.seqLen; + const int arrayLen = field.arrayLen; + + switch (field.backendType) { + case Tracepoint::Field::Array: + stream << "ctf_array(" <\n\n"; +} + +static void writeEpilogue(QTextStream &stream, const QString &fileName) +{ + stream << "\n"; + stream << "#endif // " << includeGuard(fileName) << "\n" + << "#include \n" + << "#include \n"; +} + +static void writeWrapper(QTextStream &stream, + const Tracepoint &tracepoint, const QString &providerName) +{ + const QString argList = formatFunctionSignature(tracepoint.args); + const QString paramList = formatParameterList(tracepoint.args, LTTNG); + const QString &name = tracepoint.name; + const QString includeGuard = QStringLiteral("TP_%1_%2").arg(providerName).arg(name).toUpper(); + + /* prevents the redefinion of the inline wrapper functions + * once LTTNG recursively includes this header file + */ + stream << "\n" + << "#ifndef " << includeGuard << "\n" + << "#define " << includeGuard << "\n" + << "namespace QtPrivate {\n"; + + stream << "inline void trace_" << name << "(" << argList << ")\n" + << "{\n" + << " tracepoint(" << providerName << ", " << name << paramList << ");\n" + << "}\n"; + + stream << "inline void do_trace_" << name << "(" << argList << ")\n" + << "{\n" + << " do_tracepoint(" << providerName << ", " << name << paramList << ");\n" + << "}\n"; + + stream << "inline bool trace_" << name << "_enabled()\n" + << "{\n" + << " return tracepoint_enabled(" << providerName << ", " << name << ");\n" + << "}\n"; + + stream << "} // namespace QtPrivate\n" + << "#endif // " << includeGuard << "\n\n"; +} + +static void writeTracepoint(QTextStream &stream, + const Tracepoint &tracepoint, const QString &providerName) +{ + stream << "TRACEPOINT_EVENT(\n" + << " " << providerName << ",\n" + << " " << tracepoint.name << ",\n" + << " TP_ARGS("; + + const char *comma = nullptr; + + for (const Tracepoint::Argument &arg : tracepoint.args) { + stream << comma << arg.type << ", " << arg.name; + comma = ", "; + } + + stream << "),\n" + << " TP_FIELDS("; + + const char *newline = nullptr; + + for (const Tracepoint::Field &f : tracepoint.fields) { + stream << newline; + writeCtfMacro(stream, f); + newline = "\n "; + } + + stream << ")\n)\n\n"; +} + +static void writeTracepoints(QTextStream &stream, const Provider &provider) +{ + for (const Tracepoint &t : provider.tracepoints) { + writeTracepoint(stream, t, provider.name); + writeWrapper(stream, t, provider.name); + } +} + +void writeLttng(QFile &file, const Provider &provider) +{ + QTextStream stream(&file); + + const QString fileName = QFileInfo(file.fileName()).fileName(); + + writePrologue(stream, fileName, provider.name); + writeTracepoints(stream, provider); + writeEpilogue(stream, fileName); +} diff --git a/src/tools/tracegen/lttng.h b/src/tools/tracegen/lttng.h new file mode 100644 index 0000000000..0307b375bc --- /dev/null +++ b/src/tools/tracegen/lttng.h @@ -0,0 +1,48 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Rafael Roquetto +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef LTTNG_H +#define LTTNG_H + +struct Provider; +class QFile; + +void writeLttng(QFile &device, const Provider &p); + +#endif // LTTNG_H diff --git a/src/tools/tracegen/panic.cpp b/src/tools/tracegen/panic.cpp new file mode 100644 index 0000000000..d1e207764e --- /dev/null +++ b/src/tools/tracegen/panic.cpp @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Rafael Roquetto +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "panic.h" + +#include +#include +#include + +void panic(const char *fmt, ...) +{ + va_list ap; + + fprintf(stderr, "tracegen: fatal: "); + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + + fputc('\n', stderr); + + exit(EXIT_FAILURE); +} diff --git a/src/tools/tracegen/panic.h b/src/tools/tracegen/panic.h new file mode 100644 index 0000000000..c6b195af00 --- /dev/null +++ b/src/tools/tracegen/panic.h @@ -0,0 +1,45 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Rafael Roquetto +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef PANIC_H +#define PANIC_H + +void panic(const char *fmt, ...); + +#endif // PANIC_H diff --git a/src/tools/tracegen/provider.cpp b/src/tools/tracegen/provider.cpp new file mode 100644 index 0000000000..00e105377e --- /dev/null +++ b/src/tools/tracegen/provider.cpp @@ -0,0 +1,304 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Rafael Roquetto +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "provider.h" +#include "panic.h" + +#include +#include +#include +#include +#include + +#ifdef TRACEGEN_DEBUG +#include + +static void dumpTracepoint(const Tracepoint &t) +{ + qDebug() << "=== BEGIN TRACEPOINT ==="; + qDebug() << "name:" << t.name; + qDebug() << "ARGS\n"; + + int j = 0; + + for (auto i = t.args.constBegin(); i != t.args.constEnd(); ++i) { + qDebug() << "ARG[" << j << "] type:" << i->type; + qDebug() << "ARG[" << j << "] name:" << i->name; + qDebug() << "ARG[" << j << "] arrayLen:" << i->arrayLen; + ++j; + } + + qDebug() << "\nFIELDS\n"; + + j = 0; + + for (auto i = t.fields.constBegin(); i != t.fields.constEnd(); ++i) { + qDebug() << "FIELD[" << j << "] backend_type" << static_cast(i->backendType); + qDebug() << "FIELD[" << j << "] param_type" << i->paramType; + qDebug() << "FIELD[" << j << "] name" << i->name; + qDebug() << "FIELD[" << j << "] arrayLen" << i->arrayLen; + qDebug() << "FIELD[" << j << "] seqLen" << i->seqLen; + ++j; + } + + qDebug() << "=== END TRACEPOINT ===\n"; + +} +#endif + +static inline int arrayLength(const QString &rawType) +{ + /* matches the length of an ordinary array type + * Ex: foo[10] yields '10' + */ + static const QRegExp rx(QStringLiteral(".*\\[([0-9]+)\\].*")); + + if (!rx.exactMatch(rawType.trimmed())) + return 0; + + return rx.cap(1).toInt(); +} + +static inline QString sequenceLength(const QString &rawType) +{ + /* matches the identifier pointing to length of a CTF sequence type, which is + * a dynamic sized array. + * Ex: in qcoreapplication_foo(const char[len], some_string, unsigned int, * len) + * it will match the 'len' part of 'const char[len]') + */ + static const QRegExp rx(QStringLiteral(".*\\[([A-Za-z_][A-Za-z_0-9]*)\\].*")); + + if (!rx.exactMatch(rawType.trimmed())) + return QString(); + + return rx.cap(1); +} + +static QString decayArrayToPointer(QString type) +{ + /* decays an array to a pointer, i.e., if 'type' holds int[10], + * this function returns 'int *' + */ + static QRegExp rx(QStringLiteral("\\[(.+)\\]")); + + rx.setMinimal(true); + return type.replace(rx, QStringLiteral("*")); +} + +static QString removeBraces(QString type) +{ + static const QRegExp rx(QStringLiteral("\\[.*\\]")); + + return type.remove(rx); +} + +static Tracepoint::Field::BackendType backendType(QString rawType) +{ + static const struct { + const char *type; + Tracepoint::Field::BackendType backendType; + } typeTable[] = { + { "bool", Tracepoint::Field::Integer }, + { "short_int", Tracepoint::Field::Integer }, + { "signed_short", Tracepoint::Field::Integer }, + { "signed_short_int", Tracepoint::Field::Integer }, + { "unsigned_short", Tracepoint::Field::Integer }, + { "unsigned_short_int", Tracepoint::Field::Integer }, + { "int", Tracepoint::Field::Integer }, + { "signed", Tracepoint::Field::Integer }, + { "signed_int", Tracepoint::Field::Integer }, + { "unsigned", Tracepoint::Field::Integer }, + { "unsigned_int", Tracepoint::Field::Integer }, + { "long", Tracepoint::Field::Integer }, + { "long_int", Tracepoint::Field::Integer }, + { "signed_long", Tracepoint::Field::Integer }, + { "signed_long_int", Tracepoint::Field::Integer }, + { "unsigned_long", Tracepoint::Field::Integer }, + { "unsigned_long_int", Tracepoint::Field::Integer }, + { "long_long", Tracepoint::Field::Integer }, + { "long_long_int", Tracepoint::Field::Integer }, + { "signed_long_long", Tracepoint::Field::Integer }, + { "signed_long_long_int", Tracepoint::Field::Integer }, + { "unsigned_long_long", Tracepoint::Field::Integer }, + { "char", Tracepoint::Field::Integer }, + { "float", Tracepoint::Field::Float }, + { "double", Tracepoint::Field::Float }, + { "long_double", Tracepoint::Field::Float }, + { "char_ptr", Tracepoint::Field::String }, + { "QString", Tracepoint::Field::QtString }, + { "QByteArray", Tracepoint::Field::QtByteArray }, + { "QUrl", Tracepoint::Field::QtUrl }, + { "QRect", Tracepoint::Field::QtRect } + }; + + auto backendType = [](const QString &rawType) { + + static const size_t tableSize = sizeof (typeTable) / sizeof (typeTable[0]); + + for (size_t i = 0; i < tableSize; ++i) { + if (rawType == QLatin1String(typeTable[i].type)) + return typeTable[i].backendType; + } + + return Tracepoint::Field::Unknown; + }; + + if (arrayLength(rawType) > 0) + return Tracepoint::Field::Array; + + if (!sequenceLength(rawType).isNull()) + return Tracepoint::Field::Sequence; + + static const QRegExp constMatch(QStringLiteral("\\bconst\\b")); + rawType.remove(constMatch); + rawType.remove(QLatin1Char('&')); + + static const QRegExp ptrMatch(QStringLiteral("\\s*\\*\\s*")); + rawType.replace(ptrMatch, QStringLiteral("_ptr")); + rawType = rawType.trimmed(); + rawType.replace(QStringLiteral(" "), QStringLiteral("_")); + + return backendType(rawType.trimmed()); +} + +static Tracepoint parseTracepoint(const QString &name, const QStringList &args, + const QString &fileName, const int lineNumber) +{ + Tracepoint t; + t.name = name; + + if (args.isEmpty()) + return t; + + auto i = args.constBegin(); + auto end = args.constEnd(); + int argc = 0; + + static const QRegExp rx(QStringLiteral("(.*)\\b([A-Za-z_][A-Za-z0-9_]*)$")); + + while (i != end) { + rx.exactMatch(*i); + + const QString type = rx.cap(1).trimmed(); + + if (type.isNull()) { + panic("Missing parameter type for argument %d of %s (%s:%d)", + argc, qPrintable(name), qPrintable(fileName), lineNumber); + } + + const QString name = rx.cap(2).trimmed(); + + if (name.isNull()) { + panic("Missing parameter name for argument %d of %s (%s:%d)", + argc, qPrintable(name), qPrintable(fileName), lineNumber); + } + + int arrayLen = arrayLength(type); + + Tracepoint::Argument a; + a.arrayLen = arrayLen; + a.name = name; + a.type = decayArrayToPointer(type); + + t.args << std::move(a); + + Tracepoint::Field f; + f.backendType = backendType(type); + f.paramType = removeBraces(type); + f.name = name; + f.arrayLen = arrayLen; + f.seqLen = sequenceLength(type); + + t.fields << std::move(f); + + ++i; + } + + return t; +} + +Provider parseProvider(const QString &filename) +{ + QFile f(filename); + + if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) + panic("Cannot open %s: %s", qPrintable(filename), qPrintable(f.errorString())); + + QTextStream s(&f); + + static const QRegExp tracedef(QStringLiteral("([A-Za-z][A-Za-z0-9_]*)\\((.*)\\)")); + + int lineNumber = 0; + + Provider provider; + provider.name = QFileInfo(filename).baseName(); + + for (;;) { + QString line = s.readLine().trimmed(); + + if (line.isNull()) + break; + + if (line.isEmpty() || line.startsWith(QStringLiteral("#"))) { + ++lineNumber; + continue; + } + + if (tracedef.exactMatch(line)) { + const QString name = tracedef.cap(1); + QStringList args = tracedef.cap(2).split(QStringLiteral(","), QString::SkipEmptyParts); + + if (args.at(0).isNull()) + args.clear(); + + provider.tracepoints << parseTracepoint(name, args, filename, lineNumber); + } else { + panic("Syntax error whilre processing %s on line %d", qPrintable(filename), lineNumber); + } + + ++lineNumber; + } + +#ifdef TRACEGEN_DEBUG + for (auto i = provider.tracepoints.constBegin(); i != provider.tracepoints.constEnd(); ++i) + dumpTracepoint(*i); +#endif + + return provider; +} diff --git a/src/tools/tracegen/provider.h b/src/tools/tracegen/provider.h new file mode 100644 index 0000000000..d8cbd2662d --- /dev/null +++ b/src/tools/tracegen/provider.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Rafael Roquetto +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef PROVIDER_H +#define PROVIDER_H + +#include +#include +#include + +struct Tracepoint +{ + struct Argument + { + QString type; + QString name; + int arrayLen; + }; + + struct Field + { + enum BackendType { + Array, + Sequence, + Integer, + Float, + String, + QtString, + QtByteArray, + QtUrl, + QtRect, + Unknown + }; + + BackendType backendType; + QString paramType; + QString name; + int arrayLen; + QString seqLen; + }; + + QString name; + QVector args; + QVector fields; +}; + +struct Provider +{ + QString name; + QVector tracepoints; +}; + +Provider parseProvider(const QString &filename); + +Q_DECLARE_TYPEINFO(Tracepoint::Argument, Q_MOVABLE_TYPE); +Q_DECLARE_TYPEINFO(Tracepoint::Field, Q_MOVABLE_TYPE); +Q_DECLARE_TYPEINFO(Tracepoint, Q_MOVABLE_TYPE); + +#endif // PROVIDER_H diff --git a/src/tools/tracegen/qtheaders.cpp b/src/tools/tracegen/qtheaders.cpp new file mode 100644 index 0000000000..eec3488a6d --- /dev/null +++ b/src/tools/tracegen/qtheaders.cpp @@ -0,0 +1,51 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Rafael Roquetto +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qtheaders.h" + +const char *qtHeaders() +{ + static const char headers[] = "" + "#include \n" + "#include \n" + "#include \n" + "#include \n"; + + return headers; +} diff --git a/src/tools/tracegen/qtheaders.h b/src/tools/tracegen/qtheaders.h new file mode 100644 index 0000000000..b80d374ca8 --- /dev/null +++ b/src/tools/tracegen/qtheaders.h @@ -0,0 +1,45 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Rafael Roquetto +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTHEADERS_H +#define QTHEADERS_H + +const char *qtHeaders(); + +#endif // QTHEADERS_H diff --git a/src/tools/tracegen/tracegen.cpp b/src/tools/tracegen/tracegen.cpp new file mode 100644 index 0000000000..978fe406d0 --- /dev/null +++ b/src/tools/tracegen/tracegen.cpp @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Rafael Roquetto +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "provider.h" +#include "lttng.h" +#include "etw.h" +#include "panic.h" + +#include +#include + +enum class Target +{ + LTTNG, + ETW +}; + +static inline void usage(int status) +{ + printf("Usage: tracegen \n"); + exit(status); +} + +static void parseArgs(int argc, char *argv[], Target *target, QString *inFile, QString *outFile) +{ + if (argc == 1) + usage(EXIT_SUCCESS); + if (argc != 4) + usage(EXIT_FAILURE); + + const char *targetString = argv[1]; + + if (qstrcmp(targetString, "lttng") == 0) { + *target = Target::LTTNG; + } else if (qstrcmp(targetString, "etw") == 0) { + *target = Target::ETW; + } else { + fprintf(stderr, "Invalid target: %s\n", targetString); + usage(EXIT_FAILURE); + } + + *inFile = QLatin1String(argv[2]); + *outFile = QLatin1String(argv[3]); +} + +int main(int argc, char *argv[]) +{ + Target target = Target::LTTNG; + QString inFile; + QString outFile; + + parseArgs(argc, argv, &target, &inFile, &outFile); + + Provider p = parseProvider(inFile); + + QFile out(outFile); + + if (!out.open(QIODevice::WriteOnly | QIODevice::Truncate)) { + panic("Cannot open '%s' for writing: %s", + qPrintable(outFile), qPrintable(out.errorString())); + } + + switch (target) { + case Target::LTTNG: + writeLttng(out, p); + break; + case Target::ETW: + writeEtw(out, p); + break; + } + + return 0; +} diff --git a/src/tools/tracegen/tracegen.pro b/src/tools/tracegen/tracegen.pro new file mode 100644 index 0000000000..20f0bb2914 --- /dev/null +++ b/src/tools/tracegen/tracegen.pro @@ -0,0 +1,21 @@ +option(host_build) +CONFIG += force_bootstrap + +SOURCES += \ + etw.cpp \ + helpers.cpp \ + lttng.cpp \ + panic.cpp \ + provider.cpp \ + qtheaders.cpp \ + tracegen.cpp + +HEADERS += \ + etw.h \ + helpers.h \ + lttng.h \ + panic.h \ + provider.h \ + qtheaders.h + +load(qt_tool) -- cgit v1.2.3 From 678aae2c43726635053fede2a6c3875508cc3599 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Thu, 18 Jan 2018 20:44:43 -0800 Subject: QBitArray: add manipulate a dense bit array directly [ChangeLog][QtCore][QBitArray] Added fromBits(), which creates a QBitArray from a dense bit array, and bits(), which returns that. Change-Id: Ia9c88b83534240a5872dfffd150b1c8b1c36ced5 Reviewed-by: Lars Knoll --- src/corelib/tools/qbitarray.cpp | 40 ++++++++++++++++++++++++++++++++++++++++ src/corelib/tools/qbitarray.h | 3 +++ 2 files changed, 43 insertions(+) (limited to 'src') diff --git a/src/corelib/tools/qbitarray.cpp b/src/corelib/tools/qbitarray.cpp index 12e4687b3c..f68a807203 100644 --- a/src/corelib/tools/qbitarray.cpp +++ b/src/corelib/tools/qbitarray.cpp @@ -300,6 +300,46 @@ void QBitArray::fill(bool value, int begin, int end) setBit(begin++, value); } +/*! + \fn const char *QBitArray::bits() const + \since 5.11 + + Returns a pointer to a dense bit array for this QBitArray. Bits are counted + upwards from the least significant bit in each byte. The the number of bits + relevant in the last byte is given by \c{size() % 8}. + + \sa fromBits(), size() + */ + +/*! + \since 5.11 + + Creates a QBitArray with the dense bit array located at \a data, with \a + len bits. The byte array at \a data must be at least \a size / 8 (rounded up) + bytes long. + + If \a size is not a multiple of 8, this function will include the lowest + \a size % 8 bits from the last byte in \a data. + + \sa bits() + */ +QBitArray QBitArray::fromBits(const char *data, qsizetype size) +{ + QBitArray result; + qsizetype nbytes = (size + 7) / 8; + + result.d = QByteArray(nbytes + 1, Qt::Uninitialized); + char *bits = result.d.data(); + memcpy(bits + 1, data, nbytes); + + // clear any unused bits from the last byte + if (size & 7) + bits[nbytes] &= 0xffU >> (size & 7); + + *bits = result.d.size() * 8 - size; + return result; +} + /*! \fn bool QBitArray::isDetached() const \internal diff --git a/src/corelib/tools/qbitarray.h b/src/corelib/tools/qbitarray.h index 8fa5323127..ff40bf5654 100644 --- a/src/corelib/tools/qbitarray.h +++ b/src/corelib/tools/qbitarray.h @@ -104,6 +104,9 @@ public: inline void truncate(int pos) { if (pos < size()) resize(pos); } + const char *bits() const { return isEmpty() ? nullptr : d.constData() + 1; } + static QBitArray fromBits(const char *data, qsizetype len); + public: typedef QByteArray::DataPtr DataPtr; inline DataPtr &data_ptr() { return d.data_ptr(); } -- cgit v1.2.3 From e8bf3a2ce7fcc153fbb9732b02b51167c5c4aaf5 Mon Sep 17 00:00:00 2001 From: Jan Grulich Date: Mon, 15 Jan 2018 08:40:36 +0100 Subject: Fix opening of flatpak FileChooser portal My assumption before was that show() method calls exec(), but it seems to be vice-versa and so QML applications using QFileDialog were not able to open dialogs as they use visible property, which in turn calls show() method. I made the show() method to call new openPortal() method where I moved the actuall DBus call from exec() method. The exec() method now internally calls show(), at least this is my assumption and it seems to behave like that from my testing. Change-Id: I27a5b0198e9ff1a9ea031f9ae45b57ceae99c6ae Reviewed-by: Thiago Macieira --- .../platformthemes/flatpak/qflatpakfiledialog.cpp | 115 +++++++++++---------- .../platformthemes/flatpak/qflatpakfiledialog_p.h | 1 + 2 files changed, 61 insertions(+), 55 deletions(-) (limited to 'src') diff --git a/src/plugins/platformthemes/flatpak/qflatpakfiledialog.cpp b/src/plugins/platformthemes/flatpak/qflatpakfiledialog.cpp index c51223186e..1a24015ce5 100644 --- a/src/plugins/platformthemes/flatpak/qflatpakfiledialog.cpp +++ b/src/plugins/platformthemes/flatpak/qflatpakfiledialog.cpp @@ -147,61 +147,7 @@ void QFlatpakFileDialog::initializeDialog() setDirectory(options()->initialDirectory()); } -bool QFlatpakFileDialog::defaultNameFilterDisables() const -{ - return false; -} - -void QFlatpakFileDialog::setDirectory(const QUrl &directory) -{ - Q_D(QFlatpakFileDialog); - - d->directory = directory.path(); -} - -QUrl QFlatpakFileDialog::directory() const -{ - Q_D(const QFlatpakFileDialog); - - return d->directory; -} - -void QFlatpakFileDialog::selectFile(const QUrl &filename) -{ - Q_D(QFlatpakFileDialog); - - d->selectedFiles << filename.path(); -} - -QList QFlatpakFileDialog::selectedFiles() const -{ - Q_D(const QFlatpakFileDialog); - - QList files; - for (const QString &file : d->selectedFiles) { - files << QUrl(file); - } - return files; -} - -void QFlatpakFileDialog::setFilter() -{ - // TODO -} - -void QFlatpakFileDialog::selectNameFilter(const QString &filter) -{ - Q_UNUSED(filter); - // TODO -} - -QString QFlatpakFileDialog::selectedNameFilter() const -{ - // TODO - return QString(); -} - -void QFlatpakFileDialog::exec() +void QFlatpakFileDialog::openPortal() { Q_D(const QFlatpakFileDialog); @@ -305,7 +251,64 @@ void QFlatpakFileDialog::exec() SLOT(gotResponse(uint,QVariantMap))); } }); +} +bool QFlatpakFileDialog::defaultNameFilterDisables() const +{ + return false; +} + +void QFlatpakFileDialog::setDirectory(const QUrl &directory) +{ + Q_D(QFlatpakFileDialog); + + d->directory = directory.path(); +} + +QUrl QFlatpakFileDialog::directory() const +{ + Q_D(const QFlatpakFileDialog); + + return d->directory; +} + +void QFlatpakFileDialog::selectFile(const QUrl &filename) +{ + Q_D(QFlatpakFileDialog); + + d->selectedFiles << filename.path(); +} + +QList QFlatpakFileDialog::selectedFiles() const +{ + Q_D(const QFlatpakFileDialog); + + QList files; + for (const QString &file : d->selectedFiles) { + files << QUrl(file); + } + return files; +} + +void QFlatpakFileDialog::setFilter() +{ + // TODO +} + +void QFlatpakFileDialog::selectNameFilter(const QString &filter) +{ + Q_UNUSED(filter); + // TODO +} + +QString QFlatpakFileDialog::selectedNameFilter() const +{ + // TODO + return QString(); +} + +void QFlatpakFileDialog::exec() +{ // HACK we have to avoid returning until we emit that the dialog was accepted or rejected QEventLoop loop; loop.connect(this, SIGNAL(accept()), SLOT(quit())); @@ -327,6 +330,8 @@ bool QFlatpakFileDialog::show(Qt::WindowFlags windowFlags, Qt::WindowModality wi d->modal = windowModality != Qt::NonModal; d->winId = parent ? parent->winId() : 0; + openPortal(); + return true; } diff --git a/src/plugins/platformthemes/flatpak/qflatpakfiledialog_p.h b/src/plugins/platformthemes/flatpak/qflatpakfiledialog_p.h index 8f65d07e62..f3e195faa0 100644 --- a/src/plugins/platformthemes/flatpak/qflatpakfiledialog_p.h +++ b/src/plugins/platformthemes/flatpak/qflatpakfiledialog_p.h @@ -90,6 +90,7 @@ private Q_SLOTS: private: void initializeDialog(); + void openPortal(); QScopedPointer d_ptr; }; -- cgit v1.2.3 From cdebc1a138eb4e4272b0b96e6bfce4fd66851ce1 Mon Sep 17 00:00:00 2001 From: Anton Kudryavtsev Date: Tue, 30 Jan 2018 19:57:28 +0300 Subject: QString: optimize remove() In remove(const QString &str, Qt::CaseSensitivity cs) call remove(QChar c, Qt::CaseSensitivity cs) if str.size() is equal 1. It prevents quadratic behavior for that case. Change-Id: I9a7ab3019c580343533c8c6c6a04b6b0c8c1fb55 Reviewed-by: Thiago Macieira --- src/corelib/tools/qstring.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/corelib/tools/qstring.cpp b/src/corelib/tools/qstring.cpp index 69751eb6dc..4e7baa18b6 100644 --- a/src/corelib/tools/qstring.cpp +++ b/src/corelib/tools/qstring.cpp @@ -2599,10 +2599,15 @@ QString &QString::remove(int pos, int len) */ QString &QString::remove(const QString &str, Qt::CaseSensitivity cs) { - if (str.d->size) { - int i = 0; - while ((i = indexOf(str, i, cs)) != -1) - remove(i, str.d->size); + const int strSize = str.size(); + if (strSize) { + if (strSize == 1) { + remove(str.front(), cs); + } else { + int i = 0; + while ((i = indexOf(str, i, cs)) != -1) + remove(i, strSize); + } } return *this; } -- cgit v1.2.3 From 8f582a4cc5bd03a5d549de1f41cdb939c9abc23a Mon Sep 17 00:00:00 2001 From: Andre de la Rocha Date: Fri, 1 Dec 2017 16:36:58 +0100 Subject: Fix HiDPI rendering issues in QCommonStyle Fixed size/resolution of combo box and scroll bar arrows. Task-number: QTBUG-49374 Change-Id: I3016b082b5eb7149fbe0c0e740525ab8506ce391 Reviewed-by: Alessandro Portale --- src/widgets/styles/qcommonstyle.cpp | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/widgets/styles/qcommonstyle.cpp b/src/widgets/styles/qcommonstyle.cpp index 1190099c16..6fb55f0b5f 100644 --- a/src/widgets/styles/qcommonstyle.cpp +++ b/src/widgets/styles/qcommonstyle.cpp @@ -743,8 +743,9 @@ void QCommonStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, Q % QLatin1String(metaObject()->className()), opt, QSize(size, size)) % HexString(pe); if (!QPixmapCache::find(pixmapName, pixmap)) { - int border = size/5; - int sqsize = 2*(size/2); + qreal pixelRatio = p->device()->devicePixelRatioF(); + int border = qRound(pixelRatio*(size/5)); + int sqsize = qRound(pixelRatio*(2*(size/2))); QImage image(sqsize, sqsize, QImage::Format_ARGB32_Premultiplied); image.fill(0); QPainter imagePainter(&image); @@ -796,6 +797,7 @@ void QCommonStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, Q imagePainter.drawPolygon(a); imagePainter.end(); pixmap = QPixmap::fromImage(image); + pixmap.setDevicePixelRatio(pixelRatio); QPixmapCache::insert(pixmapName, pixmap); } int xOffset = r.x() + (r.width() - size)/2; @@ -4165,14 +4167,10 @@ QRect QCommonStyle::subControlRect(ComplexControl cc, const QStyleOptionComplex #if QT_CONFIG(combobox) case CC_ComboBox: if (const QStyleOptionComboBox *cb = qstyleoption_cast(opt)) { - int x = cb->rect.x(), - y = cb->rect.y(), - wi = cb->rect.width(), - he = cb->rect.height(); - int xpos = x; - int margin = cb->frame ? 3 : 0; - int bmarg = cb->frame ? 2 : 0; - xpos += wi - bmarg - 16; + const int x = cb->rect.x(), y = cb->rect.y(), wi = cb->rect.width(), he = cb->rect.height(); + const int margin = cb->frame ? qRound(QStyleHelper::dpiScaled(3)) : 0; + const int bmarg = cb->frame ? qRound(QStyleHelper::dpiScaled(2)) : 0; + const int xpos = x + wi - bmarg - qRound(QStyleHelper::dpiScaled(16)); switch (sc) { @@ -4180,10 +4178,10 @@ QRect QCommonStyle::subControlRect(ComplexControl cc, const QStyleOptionComplex ret = cb->rect; break; case SC_ComboBoxArrow: - ret.setRect(xpos, y + bmarg, 16, he - 2*bmarg); + ret.setRect(xpos, y + bmarg, qRound(QStyleHelper::dpiScaled(16)), he - 2*bmarg); break; case SC_ComboBoxEditField: - ret.setRect(x + margin, y + margin, wi - 2 * margin - 16, he - 2 * margin); + ret.setRect(x + margin, y + margin, wi - 2 * margin - qRound(QStyleHelper::dpiScaled(16)), he - 2 * margin); break; case SC_ComboBoxListBoxPopup: ret = cb->rect; -- cgit v1.2.3 From 0b2ebfd41446366ff6a79c3972e072e590962146 Mon Sep 17 00:00:00 2001 From: Andre de la Rocha Date: Fri, 1 Dec 2017 16:36:58 +0100 Subject: Fix HiDPI rendering issues in the Windows style Fixing miscellaneous rendering issues to make the Windows style look good on High DPI displays: - Fixed size/resolution of combo box arrows. - Fixed size/resolution of scroll bar arrows. - Fixed size/resolution of check boxes. - Fixed size/resolution of radio buttons. - Fixed the frame of default buttons. Task-number: QTBUG-49374 Task-number: QTBUG-65237 Change-Id: Ib7e2ef2ed027c50dbac23b16a73f7033000552f1 Reviewed-by: Andre de la Rocha Reviewed-by: Alessandro Portale --- src/widgets/styles/qwindowsstyle.cpp | 144 +++++++++++++---------------------- 1 file changed, 55 insertions(+), 89 deletions(-) (limited to 'src') diff --git a/src/widgets/styles/qwindowsstyle.cpp b/src/widgets/styles/qwindowsstyle.cpp index 7a84a4dcf8..89011350ec 100644 --- a/src/widgets/styles/qwindowsstyle.cpp +++ b/src/widgets/styles/qwindowsstyle.cpp @@ -798,9 +798,10 @@ void QWindowsStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, break; case PE_FrameDefaultButton: { QPen oldPen = p->pen(); - p->setPen(opt->palette.shadow().color()); - QRect rect = opt->rect; - rect.adjust(0, 0, -1, -1); + p->setPen(QPen(opt->palette.shadow().color(), 0)); + QRectF rect = opt->rect; + rect.adjust(QStyleHelper::dpiScaled(0.5), QStyleHelper::dpiScaled(0.5), + QStyleHelper::dpiScaled(-1.5), QStyleHelper::dpiScaled(-1.5)); p->drawRect(rect); p->setPen(oldPen); break; @@ -843,22 +844,16 @@ void QWindowsStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, } #endif // QT_CONFIG(itemviews) if (!(opt->state & State_Off)) { - QLineF lines[7]; - int i, xx, yy; - xx = opt->rect.x() + 3; - yy = opt->rect.y() + 5; - for (i = 0; i < 3; ++i) { - lines[i] = QLineF(xx, yy, xx, yy + 2); - ++xx; - ++yy; - } - yy -= 2; - for (i = 3; i < 7; ++i) { - lines[i] = QLineF(xx, yy, xx, yy + 2); - ++xx; - --yy; - } - p->drawLines(lines, 7); + QPointF points[6]; + points[0] = { opt->rect.x() + QStyleHelper::dpiScaled(3.5), opt->rect.y() + QStyleHelper::dpiScaled(5.5) }; + points[1] = { points[0].x(), points[0].y() + QStyleHelper::dpiScaled(2) }; + points[2] = { points[1].x() + QStyleHelper::dpiScaled(2), points[1].y() + QStyleHelper::dpiScaled(2) }; + points[3] = { points[2].x() + QStyleHelper::dpiScaled(4), points[2].y() - QStyleHelper::dpiScaled(4) }; + points[4] = { points[3].x(), points[3].y() - QStyleHelper::dpiScaled(2) }; + points[5] = { points[4].x() - QStyleHelper::dpiScaled(4), points[4].y() + QStyleHelper::dpiScaled(4) }; + p->setPen(QPen(opt->palette.text().color(), 0)); + p->setBrush(opt->palette.text().color()); + p->drawPolygon(points, 6); } if (doRestore) p->restore(); @@ -890,86 +885,57 @@ void QWindowsStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, break; case PE_IndicatorRadioButton: { -#define PTSARRLEN(x) sizeof(x)/(sizeof(QPoint)) - static const QPoint pts1[] = { // dark lines - QPoint(1, 9), QPoint(1, 8), QPoint(0, 7), QPoint(0, 4), QPoint(1, 3), QPoint(1, 2), - QPoint(2, 1), QPoint(3, 1), QPoint(4, 0), QPoint(7, 0), QPoint(8, 1), QPoint(9, 1) - }; - static const QPoint pts2[] = { // black lines - QPoint(2, 8), QPoint(1, 7), QPoint(1, 4), QPoint(2, 3), QPoint(2, 2), QPoint(3, 2), - QPoint(4, 1), QPoint(7, 1), QPoint(8, 2), QPoint(9, 2) - }; - static const QPoint pts3[] = { // background lines - QPoint(2, 9), QPoint(3, 9), QPoint(4, 10), QPoint(7, 10), QPoint(8, 9), QPoint(9, 9), - QPoint(9, 8), QPoint(10, 7), QPoint(10, 4), QPoint(9, 3) - }; - static const QPoint pts4[] = { // white lines - QPoint(2, 10), QPoint(3, 10), QPoint(4, 11), QPoint(7, 11), QPoint(8, 10), - QPoint(9, 10), QPoint(10, 9), QPoint(10, 8), QPoint(11, 7), QPoint(11, 4), - QPoint(10, 3), QPoint(10, 2) - }; - static const QPoint pts5[] = { // inner fill - QPoint(4, 2), QPoint(7, 2), QPoint(9, 4), QPoint(9, 7), QPoint(7, 9), QPoint(4, 9), - QPoint(2, 7), QPoint(2, 4) - }; - - // make sure the indicator is square - QRect ir = opt->rect; - - if (opt->rect.width() < opt->rect.height()) { - ir.setTop(opt->rect.top() + (opt->rect.height() - opt->rect.width()) / 2); - ir.setHeight(opt->rect.width()); - } else if (opt->rect.height() < opt->rect.width()) { - ir.setLeft(opt->rect.left() + (opt->rect.width() - opt->rect.height()) / 2); - ir.setWidth(opt->rect.height()); - } - + QRect r = opt->rect; p->save(); - p->setRenderHint(QPainter::Qt4CompatiblePainting); - bool down = opt->state & State_Sunken; - bool enabled = opt->state & State_Enabled; - bool on = opt->state & State_On; - QPolygon a; - - //center when rect is larger than indicator size - int xOffset = 0; - int yOffset = 0; - int indicatorWidth = proxy()->pixelMetric(PM_ExclusiveIndicatorWidth); - int indicatorHeight = proxy()->pixelMetric(PM_ExclusiveIndicatorHeight); - if (ir.width() > indicatorWidth) - xOffset += (ir.width() - indicatorWidth)/2; - if (ir.height() > indicatorHeight) - yOffset += (ir.height() - indicatorHeight)/2; - p->translate(xOffset, yOffset); - - p->translate(ir.x(), ir.y()); - + p->setRenderHint(QPainter::Antialiasing, true); + + QPointF circleCenter = r.center() + QPoint(1, 1); + qreal radius = (r.width() + (r.width() + 1) % 2) / 2.0 - 1; + + QPainterPath path1; + path1.addEllipse(circleCenter, radius, radius); + radius *= 0.85; + QPainterPath path2; + path2.addEllipse(circleCenter, radius, radius); + radius *= 0.85; + QPainterPath path3; + path3.addEllipse(circleCenter, radius, radius); + radius *= 0.5; + QPainterPath path4; + path4.addEllipse(circleCenter, radius, radius); + + QPolygon topLeftPol, bottomRightPol; + topLeftPol.setPoints(3, r.x(), r.y(), r.x(), r.y() + r.height(), r.x() + r.width(), r.y()); + bottomRightPol.setPoints(3, r.x(), r.y() + r.height(), r.x() + r.width(), r.y() + r.height(), r.x() + r.width(), r.y()); + + p->setClipRegion(QRegion(topLeftPol)); p->setPen(opt->palette.dark().color()); - p->drawPolyline(pts1, PTSARRLEN(pts1)); - + p->setBrush(opt->palette.dark().color()); + p->drawPath(path1); p->setPen(opt->palette.shadow().color()); - p->drawPolyline(pts2, PTSARRLEN(pts2)); + p->setBrush(opt->palette.shadow().color()); + p->drawPath(path2); + p->setClipRegion(QRegion(bottomRightPol)); + p->setPen(opt->palette.light().color()); + p->setBrush(opt->palette.light().color()); + p->drawPath(path1); p->setPen(opt->palette.midlight().color()); - p->drawPolyline(pts3, PTSARRLEN(pts3)); + p->setBrush(opt->palette.midlight().color()); + p->drawPath(path2); - p->setPen(opt->palette.light().color()); - p->drawPolyline(pts4, PTSARRLEN(pts4)); + QColor fillColor = ((opt->state & State_Sunken) || !(opt->state & State_Enabled)) ? + opt->palette.button().color() : opt->palette.base().color(); - QColor fillColor = (down || !enabled) - ? opt->palette.button().color() - : opt->palette.base().color(); + p->setClipping(false); p->setPen(fillColor); - p->setBrush(fillColor) ; - p->drawPolygon(pts5, PTSARRLEN(pts5)); + p->setBrush(fillColor); + p->drawPath(path3); - p->translate(-ir.x(), -ir.y()); // restore translate - - if (on) { - p->setPen(Qt::NoPen); + if (opt->state & State_On) { + p->setPen(opt->palette.text().color()); p->setBrush(opt->palette.text()); - p->drawRect(ir.x() + 5, ir.y() + 4, 2, 4); - p->drawRect(ir.x() + 4, ir.y() + 5, 4, 2); + p->drawPath(path4); } p->restore(); break; -- cgit v1.2.3 From 5c60e4b8f9cc88e48f5e7652eefe90e1366ae23d Mon Sep 17 00:00:00 2001 From: Andre de la Rocha Date: Fri, 1 Dec 2017 16:36:58 +0100 Subject: Fix HiDPI rendering issues in the WindowsVista style Fixing miscellaneous rendering issues to make the WindowsVista style look good on High DPI displays: - Fixed size/resolution of combo box arrows, and changed to native look. - Fixed vanishing horizontal line in the frame of line edit widgets. - Fixed gaps in combo box popup. - Fixed size/resolution of arrow in push button menu. Task-number: QTBUG-49374 Task-number: QTBUG-65237 Task-number: QTBUG-65238 Change-Id: If68c2fae7472def3c19636483af741ca8ed2c490 Reviewed-by: Alessandro Portale --- .../styles/windowsvista/qwindowsvistastyle.cpp | 49 ++++++++++++++-------- .../styles/windowsvista/qwindowsxpstyle.cpp | 23 +++++----- 2 files changed, 44 insertions(+), 28 deletions(-) (limited to 'src') diff --git a/src/plugins/styles/windowsvista/qwindowsvistastyle.cpp b/src/plugins/styles/windowsvista/qwindowsvistastyle.cpp index 078875033f..6add110249 100644 --- a/src/plugins/styles/windowsvista/qwindowsvistastyle.cpp +++ b/src/plugins/styles/windowsvista/qwindowsvistastyle.cpp @@ -990,7 +990,7 @@ void QWindowsVistaStyle::drawControl(ControlElement element, const QStyleOption XPThemeData theme(widget, 0, QWindowsXPStylePrivate::ToolBarTheme, TP_DROPDOWNBUTTON); if (theme.isValid()) { - const QSizeF size = theme.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget); + const QSizeF size = theme.size() * QStyleHelper::dpiScaled(1); if (!size.isEmpty()) { mbiw = qRound(size.width()); mbih = qRound(size.height()); @@ -1513,7 +1513,7 @@ void QWindowsVistaStyle::drawComplexControl(ComplexControl control, const QStyle if (d->transitionsEnabled() && canAnimate(option)) { - if (control == CC_ScrollBar || control == CC_SpinBox ) { + if (control == CC_ScrollBar || control == CC_SpinBox || control == CC_ComboBox) { QObject *styleObject = option->styleObject; // Can be widget or qquickitem @@ -1643,12 +1643,28 @@ void QWindowsVistaStyle::drawComplexControl(ComplexControl control, const QStyle } else { if (sub & SC_ComboBoxFrame) { - QStyleOptionButton btn; - btn.QStyleOption::operator=(*option); - btn.rect = option->rect.adjusted(-1, -1, 1, 1); - if (sub & SC_ComboBoxArrow) - btn.features = QStyleOptionButton::HasMenu; - proxy()->drawControl(QStyle::CE_PushButton, &btn, painter, widget); + XPThemeData theme(widget, painter, QWindowsXPStylePrivate::ComboboxTheme); + theme.rect = option->rect; + theme.partId = CP_READONLY; + if (!(cmb->state & State_Enabled)) + theme.stateId = CBXS_DISABLED; + else if (cmb->state & State_Sunken || cmb->state & State_On) + theme.stateId = CBXS_PRESSED; + else if (cmb->state & State_MouseOver) + theme.stateId = CBXS_HOT; + else + theme.stateId = CBXS_NORMAL; + d->drawBackground(theme); + } + if (sub & SC_ComboBoxArrow) { + XPThemeData theme(widget, painter, QWindowsXPStylePrivate::ComboboxTheme); + theme.rect = proxy()->subControlRect(CC_ComboBox, option, SC_ComboBoxArrow, widget); + theme.partId = option->direction == Qt::RightToLeft ? CP_DROPDOWNBUTTONLEFT : CP_DROPDOWNBUTTONRIGHT; + if (!(cmb->state & State_Enabled)) + theme.stateId = CBXS_DISABLED; + else + theme.stateId = CBXS_NORMAL; + d->drawBackground(theme); } } } @@ -2123,15 +2139,12 @@ QRect QWindowsVistaStyle::subControlRect(ComplexControl control, const QStyleOpt #if QT_CONFIG(combobox) case CC_ComboBox: if (const QStyleOptionComboBox *cb = qstyleoption_cast(option)) { - int x = cb->rect.x(), - y = cb->rect.y(), - wi = cb->rect.width(), - he = cb->rect.height(); - int xpos = x; - int margin = cb->frame ? 3 : 0; - int bmarg = cb->frame ? 2 : 0; - int arrowButtonWidth = bmarg + 16; - xpos += wi - arrowButtonWidth; + const int x = cb->rect.x(), y = cb->rect.y(), wi = cb->rect.width(), he = cb->rect.height(); + const int margin = cb->frame ? 3 : 0; + const int bmarg = cb->frame ? 2 : 0; + const int arrowWidth = qRound(QStyleHelper::dpiScaled(16)); + const int arrowButtonWidth = bmarg + arrowWidth; + const int xpos = x + wi - arrowButtonWidth; switch (subControl) { case SC_ComboBoxFrame: @@ -2141,7 +2154,7 @@ QRect QWindowsVistaStyle::subControlRect(ComplexControl control, const QStyleOpt rect.setRect(xpos, y , arrowButtonWidth, he); break; case SC_ComboBoxEditField: - rect.setRect(x + margin, y + margin, wi - 2 * margin - 16, he - 2 * margin); + rect.setRect(x + margin, y + margin, wi - 2 * margin - arrowWidth, he - 2 * margin); break; case SC_ComboBoxListBoxPopup: rect = cb->rect; diff --git a/src/plugins/styles/windowsvista/qwindowsxpstyle.cpp b/src/plugins/styles/windowsvista/qwindowsxpstyle.cpp index cf344c8f88..ff27cab98a 100644 --- a/src/plugins/styles/windowsvista/qwindowsxpstyle.cpp +++ b/src/plugins/styles/windowsvista/qwindowsxpstyle.cpp @@ -887,6 +887,7 @@ bool QWindowsXPStylePrivate::drawBackgroundThruNativeBuffer(XPThemeData &themeDa PROPERTYORIGIN origin = PO_NOTFOUND; GetThemePropertyOrigin(themeData.handle(), themeData.partId, themeData.stateId, TMT_BORDERSIZE, &origin); GetThemeInt(themeData.handle(), themeData.partId, themeData.stateId, TMT_BORDERSIZE, &borderSize); + borderSize *= additionalDevicePixelRatio; // Clip away border region if ((origin == PO_CLASS || origin == PO_PART || origin == PO_STATE) && borderSize > 0) { @@ -996,7 +997,7 @@ bool QWindowsXPStylePrivate::drawBackgroundThruNativeBuffer(XPThemeData &themeDa } if (addBorderContentClipping) - painter->setClipRegion(extraClip, Qt::IntersectClip); + painter->setClipRegion(scaleRegion(extraClip, 1.0 / additionalDevicePixelRatio), Qt::IntersectClip); if (!themeData.mirrorHorizontally && !themeData.mirrorVertically && !themeData.rotate) { if (!haveCachedPixmap) @@ -1479,11 +1480,12 @@ case PE_Frame: // GetThemeInt(theme.handle(), partId, stateId, TMT_BORDERCOLOR, &borderSize); // Inner white border - p->setPen(QPen(option->palette.base().color(), 1)); - p->drawRect(option->rect.adjusted(1, 1, -2, -2)); + p->setPen(QPen(option->palette.base().color(), 0)); + p->drawRect(QRectF(option->rect).adjusted(QStyleHelper::dpiScaled(0.5), QStyleHelper::dpiScaled(0.5), + QStyleHelper::dpiScaled(-1), QStyleHelper::dpiScaled(-1))); // Outer dark border - p->setPen(QPen(bordercolor, 1)); - p->drawRect(option->rect.adjusted(0, 0, -1, -1)); + p->setPen(QPen(bordercolor, 0)); + p->drawRect(QRectF(option->rect).adjusted(0, 0, QStyleHelper::dpiScaled(-0.5), QStyleHelper::dpiScaled(-0.5))); p->setPen(oldPen); return; } else if (fillType == BT_NONE) { @@ -3511,9 +3513,8 @@ QRect QWindowsXPStyle::subControlRect(ComplexControl cc, const QStyleOptionCompl case CC_ComboBox: if (const QStyleOptionComboBox *cmb = qstyleoption_cast(option)) { - int x = cmb->rect.x(), y = cmb->rect.y(), wi = cmb->rect.width(), he = cmb->rect.height(); - int xpos = x; - xpos += wi - 1 - 16; + const int x = cmb->rect.x(), y = cmb->rect.y(), wi = cmb->rect.width(), he = cmb->rect.height(); + const int xpos = x + wi - qRound(QStyleHelper::dpiScaled(1 + 16)); switch (subControl) { case SC_ComboBoxFrame: @@ -3521,11 +3522,13 @@ QRect QWindowsXPStyle::subControlRect(ComplexControl cc, const QStyleOptionCompl break; case SC_ComboBoxArrow: - rect = QRect(xpos, y+1, 16, he-2); + rect = QRect(xpos, y + qRound(QStyleHelper::dpiScaled(1)), + qRound(QStyleHelper::dpiScaled(16)), he - qRound(QStyleHelper::dpiScaled(2))); break; case SC_ComboBoxEditField: - rect = QRect(x+2, y+2, wi-3-16, he-4); + rect = QRect(x + qRound(QStyleHelper::dpiScaled(2)), y + qRound(QStyleHelper::dpiScaled(2)), + wi - qRound(QStyleHelper::dpiScaled(3 + 16)), he - qRound(QStyleHelper::dpiScaled(4))); break; case SC_ComboBoxListBoxPopup: -- cgit v1.2.3 From 0c0ee82bff31ff2733c5229cf39f678f80b2e7e6 Mon Sep 17 00:00:00 2001 From: Anton Kudryavtsev Date: Wed, 31 Jan 2018 20:26:38 +0300 Subject: QString:: add remove() overload taking QLatin1String [ChangeLog][QtCore][QString] Added remove() overload taking QLatin1String Change-Id: I11ddb8b8603144effe44f89d0d02e131a255122c Reviewed-by: Thiago Macieira --- src/corelib/tools/qstring.cpp | 46 +++++++++++++++++++++++++++++++++---------- src/corelib/tools/qstring.h | 1 + 2 files changed, 37 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/corelib/tools/qstring.cpp b/src/corelib/tools/qstring.cpp index 4e7baa18b6..4040d59b0a 100644 --- a/src/corelib/tools/qstring.cpp +++ b/src/corelib/tools/qstring.cpp @@ -2586,6 +2586,21 @@ QString &QString::remove(int pos, int len) return *this; } +template +static void removeStringImpl(QString &s, const T &needle, Qt::CaseSensitivity cs) +{ + const int needleSize = needle.size(); + if (needleSize) { + if (needleSize == 1) { + s.remove(needle.front(), cs); + } else { + int i = 0; + while ((i = s.indexOf(needle, i, cs)) != -1) + s.remove(i, needleSize); + } + } +} + /*! Removes every occurrence of the given \a str string in this string, and returns a reference to this string. @@ -2599,16 +2614,27 @@ QString &QString::remove(int pos, int len) */ QString &QString::remove(const QString &str, Qt::CaseSensitivity cs) { - const int strSize = str.size(); - if (strSize) { - if (strSize == 1) { - remove(str.front(), cs); - } else { - int i = 0; - while ((i = indexOf(str, i, cs)) != -1) - remove(i, strSize); - } - } + removeStringImpl(*this, str, cs); + return *this; +} + +/*! + \since 5.11 + \overload + + Removes every occurrence of the given \a str string in this + string, and returns a reference to this string. + + If \a cs is Qt::CaseSensitive (default), the search is + case sensitive; otherwise the search is case insensitive. + + This is the same as \c replace(str, "", cs). + + \sa replace() +*/ +QString &QString::remove(QLatin1String str, Qt::CaseSensitivity cs) +{ + removeStringImpl(*this, str, cs); return *this; } diff --git a/src/corelib/tools/qstring.h b/src/corelib/tools/qstring.h index b40a622c7c..0138ae4098 100644 --- a/src/corelib/tools/qstring.h +++ b/src/corelib/tools/qstring.h @@ -483,6 +483,7 @@ public: QString &remove(int i, int len); QString &remove(QChar c, Qt::CaseSensitivity cs = Qt::CaseSensitive); + QString &remove(QLatin1String s, Qt::CaseSensitivity cs = Qt::CaseSensitive); QString &remove(const QString &s, Qt::CaseSensitivity cs = Qt::CaseSensitive); QString &replace(int i, int len, QChar after); QString &replace(int i, int len, const QChar *s, int slen); -- cgit v1.2.3 From fd3f732489dfd3d522721a818e0e8f48fefce537 Mon Sep 17 00:00:00 2001 From: Allan Sandfeld Jensen Date: Mon, 29 Jan 2018 14:57:41 +0100 Subject: Add documentation entries for new qvector/qvarlength methods Change-Id: I4be1605ed8c9022795d5132203ad947e78481e67 Reviewed-by: Thiago Macieira --- src/corelib/tools/qvarlengtharray.qdoc | 21 +++++++++++++++++++++ src/corelib/tools/qvector.qdoc | 23 +++++++++++++++++++++++ 2 files changed, 44 insertions(+) (limited to 'src') diff --git a/src/corelib/tools/qvarlengtharray.qdoc b/src/corelib/tools/qvarlengtharray.qdoc index ef2fffa44c..dd86a80e9d 100644 --- a/src/corelib/tools/qvarlengtharray.qdoc +++ b/src/corelib/tools/qvarlengtharray.qdoc @@ -533,6 +533,13 @@ \sa append(), insert() */ +/*! + \fn template void QVarLengthArray::prepend(T &&value) + \since 5.11 + + \overload +*/ + /*! \fn template void QVarLengthArray::replace(int i, const T &value) \since 4.8 @@ -721,6 +728,14 @@ vector. */ + +/*! \fn template void QVarLengthArray::insert(int i, T &&value) + + \overload + \since 5.11 + +*/ + /*! \fn template QVarLengthArray::iterator QVarLengthArray::insert(const_iterator before, const T &value) \overload @@ -730,6 +745,12 @@ \a before. Returns an iterator pointing at the inserted item. */ +/*! \fn template QVarLengthArray::iterator QVarLengthArray::insert(const_iterator before, T &&value) + + \overload + \since 5.11 +*/ + /*! \fn template QVarLengthArray::iterator QVarLengthArray::insert(const_iterator before, int count, const T &value) \since 4.8 diff --git a/src/corelib/tools/qvector.qdoc b/src/corelib/tools/qvector.qdoc index 089aaec3fd..e344a9023d 100644 --- a/src/corelib/tools/qvector.qdoc +++ b/src/corelib/tools/qvector.qdoc @@ -605,6 +605,16 @@ \sa append(), insert() */ +/*! + \fn template void QVector::prepend(T &&value) + \since 5.11 + + \overload + + Inserts \a value at the beginning of the vector using move semantics. +*/ + + /*! \fn template void QVector::insert(int i, const T &value) Inserts \a value at index position \a i in the vector. If \a i is @@ -623,6 +633,14 @@ \sa append(), prepend(), remove() */ +/*! \fn template void QVector::insert(int i, T &&value) + \since 5.11 + + \overload + + Inserts \a value at index position \a i in the vector using move semantics. +*/ + /*! \fn template void QVector::insert(int i, int count, const T &value) \overload @@ -1090,6 +1108,11 @@ to prepend(\a value). */ +/*! \fn template void QVector::push_front(T &&value) + \since 5.11 + \overload +*/ + /*! \fn template void QVector::pop_front() This function is provided for STL compatibility. It is equivalent -- cgit v1.2.3 From 2950de715ec8c48511090dcd13e1c44ae23cb561 Mon Sep 17 00:00:00 2001 From: Christian Ehrlicher Date: Fri, 12 Jan 2018 20:33:53 +0100 Subject: QHeaderView: consider Qt::DecorationRole size within paintSection() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When an icon was given through Qt::DecorationRole, the available space for the text was not adjusted which created a graphical glitch. Task-number: QTBUG-62091 Change-Id: I0f20b6de95deed14fb882efde5c81b83ab3e9a7e Reviewed-by: Thorbjørn Lund Martsum --- src/widgets/itemviews/qheaderview.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/widgets/itemviews/qheaderview.cpp b/src/widgets/itemviews/qheaderview.cpp index 50287df6e0..585cfddff5 100644 --- a/src/widgets/itemviews/qheaderview.cpp +++ b/src/widgets/itemviews/qheaderview.cpp @@ -2848,14 +2848,18 @@ void QHeaderView::paintSection(QPainter *painter, const QRect &rect, int logical if (isSortIndicatorShown() && sortIndicatorSection() == logicalIndex && isHeaderArrowOnTheSide) margin += style()->pixelMetric(QStyle::PM_HeaderMarkSize, 0, this); - if (d->textElideMode != Qt::ElideNone) - opt.text = opt.fontMetrics.elidedText(opt.text, d->textElideMode , rect.width() - margin); - - QVariant variant = d->model->headerData(logicalIndex, d->orientation, - Qt::DecorationRole); + const QVariant variant = d->model->headerData(logicalIndex, d->orientation, + Qt::DecorationRole); opt.icon = qvariant_cast(variant); if (opt.icon.isNull()) opt.icon = qvariant_cast(variant); + if (!opt.icon.isNull()) // see CT_HeaderSection + margin += style()->pixelMetric(QStyle::PM_SmallIconSize, 0, this) + + style()->pixelMetric(QStyle::PM_HeaderMargin, 0, this); + + if (d->textElideMode != Qt::ElideNone) + opt.text = opt.fontMetrics.elidedText(opt.text, d->textElideMode , rect.width() - margin); + QVariant foregroundBrush = d->model->headerData(logicalIndex, d->orientation, Qt::ForegroundRole); if (foregroundBrush.canConvert()) -- cgit v1.2.3 From 8db29d92df0ef9fbc39d88945d16a16f43edf20e Mon Sep 17 00:00:00 2001 From: Christian Ehrlicher Date: Fri, 2 Feb 2018 07:57:57 +0100 Subject: QCommonStyle::drawControl: use PM_HeaderMargin instead magic number MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace '2' with the correct margin retrieved from PM_HeaderMargin during paniting of CE_HeaderLabel within QCommonStyle::drawControl(). Change-Id: I5a50e02f107a00f382a38e14c4d3fa8dcb97ad12 Reviewed-by: Thorbjørn Lund Martsum --- src/widgets/styles/qcommonstyle.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/widgets/styles/qcommonstyle.cpp b/src/widgets/styles/qcommonstyle.cpp index 6fb55f0b5f..1276eef1d1 100644 --- a/src/widgets/styles/qcommonstyle.cpp +++ b/src/widgets/styles/qcommonstyle.cpp @@ -1583,10 +1583,11 @@ void QCommonStyle::drawControl(ControlElement element, const QStyleOption *opt, aligned.width() * pixmap.devicePixelRatio(), pixmap.height() * pixmap.devicePixelRatio()); + const int margin = proxy()->pixelMetric(QStyle::PM_HeaderMargin, opt, widget); if (header->direction == Qt::LeftToRight) - rect.setLeft(rect.left() + pixw + 2); + rect.setLeft(rect.left() + pixw + margin); else - rect.setRight(rect.right() - pixw - 2); + rect.setRight(rect.right() - pixw - margin); } if (header->state & QStyle::State_On) { QFont fnt = p->font(); -- cgit v1.2.3 From 4772ac90fa84a81292477a4c9a283437fb5d791e Mon Sep 17 00:00:00 2001 From: Elvis Angelaccio Date: Thu, 26 Oct 2017 18:22:14 +0200 Subject: QLineEdit: implement quick text selection by mouse This is a standard feature in GtkEntry widgets or HTML elements. During a normal text selection by mouse (LeftButton press + mouse move event), it's now possible to quickly select all the text from the start of the selection to the end of the line edit by moving the mouse cursor down. By moving it up instead, all the text up to the start of the line edit gets selected. If the layout direction is right-to-left, the semantic of the mouse movement is inverted. This feature is only enabled if the y() of the mouse move event is bigger than a fixed threshold, to avoid unexpected selections in the normal case. This threshold is set by the QPlatformTheme and a value smaller than zero disables this feature. The threshold is updated whenever the style or the screen changes. [ChangeLog][QtWidgets][QLineEdit] Implemented quick text selection by mouse in QLineEdit. Change-Id: I4de33c2d11c033ec295de2b2ea81adf786324f4b Reviewed-by: Shawn Rutledge --- src/gui/kernel/qplatformintegration.cpp | 2 ++ src/gui/kernel/qplatformintegration.h | 1 + src/gui/kernel/qplatformtheme.cpp | 2 ++ src/gui/kernel/qplatformtheme.h | 3 ++- src/gui/kernel/qstylehints.cpp | 36 +++++++++++++++++++++++++++++++++ src/gui/kernel/qstylehints.h | 4 ++++ src/widgets/widgets/qlineedit.cpp | 14 ++++++++++++- src/widgets/widgets/qlineedit_p.cpp | 8 ++++++++ src/widgets/widgets/qlineedit_p.h | 4 +++- 9 files changed, 71 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/gui/kernel/qplatformintegration.cpp b/src/gui/kernel/qplatformintegration.cpp index 151151de23..448d670209 100644 --- a/src/gui/kernel/qplatformintegration.cpp +++ b/src/gui/kernel/qplatformintegration.cpp @@ -418,6 +418,8 @@ QVariant QPlatformIntegration::styleHint(StyleHint hint) const return QPlatformTheme::defaultThemeHint(QPlatformTheme::UiEffects); case WheelScrollLines: return QPlatformTheme::defaultThemeHint(QPlatformTheme::WheelScrollLines); + case MouseQuickSelectionThreshold: + return QPlatformTheme::defaultThemeHint(QPlatformTheme::MouseQuickSelectionThreshold); } return 0; diff --git a/src/gui/kernel/qplatformintegration.h b/src/gui/kernel/qplatformintegration.h index a7d9a87502..37884e1f78 100644 --- a/src/gui/kernel/qplatformintegration.h +++ b/src/gui/kernel/qplatformintegration.h @@ -164,6 +164,7 @@ public: UiEffects, WheelScrollLines, ShowShortcutsInContextMenus, + MouseQuickSelectionThreshold }; virtual QVariant styleHint(StyleHint hint) const; diff --git a/src/gui/kernel/qplatformtheme.cpp b/src/gui/kernel/qplatformtheme.cpp index 1856952805..277d976dde 100644 --- a/src/gui/kernel/qplatformtheme.cpp +++ b/src/gui/kernel/qplatformtheme.cpp @@ -559,6 +559,8 @@ QVariant QPlatformTheme::defaultThemeHint(ThemeHint hint) dist = defaultThemeHint(MouseDoubleClickDistance).toInt(&ok) * 2; return QVariant(ok ? dist : 10); } + case MouseQuickSelectionThreshold: + return QVariant(10); } return QVariant(); } diff --git a/src/gui/kernel/qplatformtheme.h b/src/gui/kernel/qplatformtheme.h index 87873d446f..1d6049a98d 100644 --- a/src/gui/kernel/qplatformtheme.h +++ b/src/gui/kernel/qplatformtheme.h @@ -117,7 +117,8 @@ public: WheelScrollLines, TouchDoubleTapDistance, ShowShortcutsInContextMenus, - IconFallbackSearchPaths + IconFallbackSearchPaths, + MouseQuickSelectionThreshold }; enum DialogType { diff --git a/src/gui/kernel/qstylehints.cpp b/src/gui/kernel/qstylehints.cpp index 0850228ee5..b2d968c046 100644 --- a/src/gui/kernel/qstylehints.cpp +++ b/src/gui/kernel/qstylehints.cpp @@ -79,6 +79,7 @@ public: , m_tabFocusBehavior(-1) , m_uiEffects(-1) , m_wheelScrollLines(-1) + , m_mouseQuickSelectionThreshold(-1) {} int m_mouseDoubleClickInterval; @@ -90,6 +91,7 @@ public: int m_tabFocusBehavior; int m_uiEffects; int m_wheelScrollLines; + int m_mouseQuickSelectionThreshold; }; /*! @@ -537,4 +539,38 @@ void QStyleHints::setWheelScrollLines(int scrollLines) emit wheelScrollLinesChanged(scrollLines); } +/*! + Sets the mouse quick selection threshold. + \internal + \sa mouseQuickSelectionThreshold() + \since 5.11 +*/ +void QStyleHints::setMouseQuickSelectionThreshold(int threshold) +{ + Q_D(QStyleHints); + if (d->m_mouseQuickSelectionThreshold == threshold) + return; + d->m_mouseQuickSelectionThreshold = threshold; + emit mouseDoubleClickIntervalChanged(threshold); +} + +/*! + \property QStyleHints::mouseQuickSelectionThreshold + \brief Quick selection mouse threshold in QLineEdit. + + This property defines how much the mouse cursor should be moved along the y axis + to trigger a quick selection during a normal QLineEdit text selection. + + If the property value is less than or equal to 0, the quick selection feature is disabled. + + \since 5.11 +*/ +int QStyleHints::mouseQuickSelectionThreshold() const +{ + Q_D(const QStyleHints); + if (d->m_mouseQuickSelectionThreshold >= 0) + return d->m_mouseQuickSelectionThreshold; + return themeableHint(QPlatformTheme::MouseQuickSelectionThreshold, QPlatformIntegration::MouseQuickSelectionThreshold).toInt(); +} + QT_END_NAMESPACE diff --git a/src/gui/kernel/qstylehints.h b/src/gui/kernel/qstylehints.h index 2c2e048b15..7b0683e9b1 100644 --- a/src/gui/kernel/qstylehints.h +++ b/src/gui/kernel/qstylehints.h @@ -73,6 +73,7 @@ class Q_GUI_EXPORT QStyleHints : public QObject Q_PROPERTY(bool singleClickActivation READ singleClickActivation STORED false CONSTANT FINAL) Q_PROPERTY(bool useHoverEffects READ useHoverEffects WRITE setUseHoverEffects NOTIFY useHoverEffectsChanged FINAL) Q_PROPERTY(int wheelScrollLines READ wheelScrollLines NOTIFY wheelScrollLinesChanged FINAL) + Q_PROPERTY(int mouseQuickSelectionThreshold READ mouseQuickSelectionThreshold WRITE setMouseQuickSelectionThreshold NOTIFY mouseQuickSelectionThresholdChanged FINAL) public: void setMouseDoubleClickInterval(int mouseDoubleClickInterval); @@ -104,6 +105,8 @@ public: void setUseHoverEffects(bool useHoverEffects); int wheelScrollLines() const; void setWheelScrollLines(int scrollLines); + void setMouseQuickSelectionThreshold(int threshold); + int mouseQuickSelectionThreshold() const; Q_SIGNALS: void cursorFlashTimeChanged(int cursorFlashTime); @@ -115,6 +118,7 @@ Q_SIGNALS: void tabFocusBehaviorChanged(Qt::TabFocusBehavior tabFocusBehavior); void useHoverEffectsChanged(bool useHoverEffects); void wheelScrollLinesChanged(int scrollLines); + void mouseQuickSelectionThresholdChanged(int threshold); private: friend class QGuiApplication; diff --git a/src/widgets/widgets/qlineedit.cpp b/src/widgets/widgets/qlineedit.cpp index 156b0e331c..e3b348f0ef 100644 --- a/src/widgets/widgets/qlineedit.cpp +++ b/src/widgets/widgets/qlineedit.cpp @@ -1466,6 +1466,8 @@ bool QLineEdit::event(QEvent * e) #endif } else if (e->type() == QEvent::Resize) { d->positionSideWidgets(); + } else if (e->type() == QEvent::StyleChange) { + d->initMouseYThreshold(); } #ifdef QT_KEYPAD_NAVIGATION if (QApplication::keypadNavigationEnabled()) { @@ -1546,7 +1548,17 @@ void QLineEdit::mouseMoveEvent(QMouseEvent * e) const bool select = (d->imHints & Qt::ImhNoPredictiveText); #endif #ifndef QT_NO_IM - if (d->control->composeMode() && select) { + if (d->mouseYThreshold > 0 && e->pos().y() > d->mousePressPos.y() + d->mouseYThreshold) { + if (layoutDirection() == Qt::RightToLeft) + d->control->home(select); + else + d->control->end(select); + } else if (d->mouseYThreshold > 0 && e->pos().y() + d->mouseYThreshold < d->mousePressPos.y()) { + if (layoutDirection() == Qt::RightToLeft) + d->control->end(select); + else + d->control->home(select); + } else if (d->control->composeMode() && select) { int startPos = d->xToPos(d->mousePressPos.x()); int currentPos = d->xToPos(e->pos().x()); if (startPos != currentPos) diff --git a/src/widgets/widgets/qlineedit_p.cpp b/src/widgets/widgets/qlineedit_p.cpp index c66b842223..6a8af53c97 100644 --- a/src/widgets/widgets/qlineedit_p.cpp +++ b/src/widgets/widgets/qlineedit_p.cpp @@ -56,6 +56,7 @@ #endif #include #include +#include #include QT_BEGIN_NAMESPACE @@ -232,6 +233,13 @@ void QLineEditPrivate::init(const QString& txt) q->setAcceptDrops(true); q->setAttribute(Qt::WA_MacShowFocusRect); + + initMouseYThreshold(); +} + +void QLineEditPrivate::initMouseYThreshold() +{ + mouseYThreshold = QGuiApplication::styleHints()->mouseQuickSelectionThreshold(); } QRect QLineEditPrivate::adjustedContentsRect() const diff --git a/src/widgets/widgets/qlineedit_p.h b/src/widgets/widgets/qlineedit_p.h index a3f549ad31..39f670b0b0 100644 --- a/src/widgets/widgets/qlineedit_p.h +++ b/src/widgets/widgets/qlineedit_p.h @@ -141,7 +141,7 @@ public: dragEnabled(0), clickCausedFocus(0), hscroll(0), vscroll(0), alignment(Qt::AlignLeading | Qt::AlignVCenter), leftTextMargin(0), topTextMargin(0), rightTextMargin(0), bottomTextMargin(0), - lastTextSize(0) + lastTextSize(0), mouseYThreshold(0) { } @@ -155,6 +155,7 @@ public: QPointer selectAllAction; #endif void init(const QString&); + void initMouseYThreshold(); QRect adjustedControlRect(const QRect &) const; @@ -253,6 +254,7 @@ private: SideWidgetEntryList leadingSideWidgets; SideWidgetEntryList trailingSideWidgets; int lastTextSize; + int mouseYThreshold; }; Q_DECLARE_TYPEINFO(QLineEditPrivate::SideWidgetEntry, Q_PRIMITIVE_TYPE); Q_DECLARE_TYPEINFO(QLineEditPrivate::SideWidgetLocation, Q_PRIMITIVE_TYPE); -- cgit v1.2.3 From 74045f8b9a21da412bc121e9b962cda385654911 Mon Sep 17 00:00:00 2001 From: Anton Kudryavtsev Date: Fri, 2 Feb 2018 19:49:18 +0300 Subject: Use new QString::remove(QLatin1String) Change-Id: I5b3560709a8fb230a177511c701fd0bf25938f0f Reviewed-by: Thiago Macieira --- src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm | 2 +- src/widgets/kernel/qaction.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm b/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm index 7ea3d130f7..98b9f7c9ba 100644 --- a/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm +++ b/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm @@ -198,7 +198,7 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSOpenSavePanelDelegate); static QString strippedText(QString s) { - s.remove( QString::fromLatin1("...") ); + s.remove(QLatin1String("...")); return QPlatformTheme::removeMnemonics(s).trimmed(); } diff --git a/src/widgets/kernel/qaction.cpp b/src/widgets/kernel/qaction.cpp index 853e27cd6e..967b0b8dde 100644 --- a/src/widgets/kernel/qaction.cpp +++ b/src/widgets/kernel/qaction.cpp @@ -66,7 +66,7 @@ QT_BEGIN_NAMESPACE */ static QString qt_strippedText(QString s) { - s.remove(QStringLiteral("...")); + s.remove(QLatin1String("...")); for (int i = 0; i < s.size(); ++i) { if (s.at(i) == QLatin1Char('&')) s.remove(i, 1); -- cgit v1.2.3 From b1fe198d8743e9581e8074d0cdc404a96fa8c077 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Wed, 31 Jan 2018 19:08:06 +0100 Subject: Simplify how we set thread name for UNIX threads Passing on the thread ID is confusing, as it's not really what the function does. The QNX code path can resolve the thread ID by itself. Change-Id: I5f0d54621058576cdcf3707d36a11762fe2383c8 Reviewed-by: Thiago Macieira --- src/corelib/thread/qthread_unix.cpp | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/corelib/thread/qthread_unix.cpp b/src/corelib/thread/qthread_unix.cpp index fb5c9fd770..1bb8e613e0 100644 --- a/src/corelib/thread/qthread_unix.cpp +++ b/src/corelib/thread/qthread_unix.cpp @@ -311,16 +311,14 @@ void QThreadPrivate::createEventDispatcher(QThreadData *data) #ifndef QT_NO_THREAD #if (defined(Q_OS_LINUX) || defined(Q_OS_MAC) || defined(Q_OS_QNX)) -static void setCurrentThreadName(pthread_t threadId, const char *name) +static void setCurrentThreadName(const char *name) { # if defined(Q_OS_LINUX) && !defined(QT_LINUXBASE) - Q_UNUSED(threadId); prctl(PR_SET_NAME, (unsigned long)name, 0, 0, 0); # elif defined(Q_OS_MAC) - Q_UNUSED(threadId); pthread_setname_np(name); # elif defined(Q_OS_QNX) - pthread_setname_np(threadId, name); + pthread_setname_np(pthread_self(), name); # endif } #endif @@ -361,14 +359,13 @@ void *QThreadPrivate::start(void *arg) #if (defined(Q_OS_LINUX) || defined(Q_OS_MAC) || defined(Q_OS_QNX)) { - // sets the name of the current thread. - QString objectName = thr->objectName(); - - pthread_t thread_id = from_HANDLE(data->threadId.load()); - if (Q_LIKELY(objectName.isEmpty())) - setCurrentThreadName(thread_id, thr->metaObject()->className()); + // Sets the name of the current thread. We can only do this + // when the thread is starting, as we don't have a cross + // platform way of setting the name of an arbitrary thread. + if (Q_LIKELY(thr->objectName().isEmpty())) + setCurrentThreadName(thr->metaObject()->className()); else - setCurrentThreadName(thread_id, objectName.toLocal8Bit()); + setCurrentThreadName(thr->objectName().toLocal8Bit()); } #endif -- cgit v1.2.3 From f4765ff5f6b93e7018693c599dd0a6d8ecd81d8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Thu, 18 Jan 2018 22:28:33 +0100 Subject: Modernize logging in CoreFoundation and iOS event dispatchers Instead of manually handling logging enablement, we use Qt's categorized logging system. Change-Id: I8d942601f76876030084c9fb130b7215ff6cc404 Reviewed-by: Thiago Macieira --- src/corelib/kernel/qeventdispatcher_cf.mm | 87 +++++++++++------------- src/corelib/kernel/qeventdispatcher_cf_p.h | 20 ++---- src/plugins/platforms/ios/qioseventdispatcher.mm | 39 ++++++----- 3 files changed, 63 insertions(+), 83 deletions(-) (limited to 'src') diff --git a/src/corelib/kernel/qeventdispatcher_cf.mm b/src/corelib/kernel/qeventdispatcher_cf.mm index 608dea5426..8499b3fd57 100644 --- a/src/corelib/kernel/qeventdispatcher_cf.mm +++ b/src/corelib/kernel/qeventdispatcher_cf.mm @@ -112,14 +112,15 @@ static CFStringRef runLoopMode(NSDictionary *dictionary) if (CFStringRef mode = runLoopMode(notification.userInfo)) m_runLoopModes.push(mode); else - qWarning("Encountered run loop push notification without run loop mode!"); + qCWarning(lcEventDispatcher) << "Encountered run loop push notification without run loop mode!"; } else if (CFStringHasSuffix((CFStringRef)notification.name, CFSTR("RunLoopModePopNotification"))) { CFStringRef mode = runLoopMode(notification.userInfo); if (CFStringCompare(mode, [self currentMode], 0) == kCFCompareEqualTo) m_runLoopModes.pop(); else - qWarning("Tried to pop run loop mode '%s' that was never pushed!", qPrintable(QString::fromCFString(mode))); + qCWarning(lcEventDispatcher) << "Tried to pop run loop mode" + << qPrintable(QString::fromCFString(mode)) << "that was never pushed!"; Q_ASSERT(m_runLoopModes.size() >= 1); } @@ -134,6 +135,9 @@ static CFStringRef runLoopMode(NSDictionary *dictionary) QT_BEGIN_NAMESPACE +Q_LOGGING_CATEGORY(lcEventDispatcher, "qt.eventdispatcher"); +Q_LOGGING_CATEGORY(lcEventDispatcherTimers, "qt.eventdispatcher.timers"); + class RunLoopDebugger : public QObject { Q_OBJECT @@ -177,10 +181,6 @@ QDebug operator<<(QDebug s, timespec tv) return s; } -#if DEBUG_EVENT_DISPATCHER -uint g_eventDispatcherIndentationLevel = 0; -#endif - static const CFTimeInterval kCFTimeIntervalMinimum = 0; static const CFTimeInterval kCFTimeIntervalDistantFuture = std::numeric_limits::max(); @@ -190,13 +190,7 @@ QEventDispatcherCoreFoundation::QEventDispatcherCoreFoundation(QObject *parent) : QAbstractEventDispatcher(parent) , m_processEvents(QEventLoop::EventLoopExec) , m_postedEventsRunLoopSource(this, &QEventDispatcherCoreFoundation::processPostedEvents) - , m_runLoopActivityObserver(this, &QEventDispatcherCoreFoundation::handleRunLoopActivity, -#if DEBUG_EVENT_DISPATCHER - kCFRunLoopAllActivities -#else - kCFRunLoopBeforeWaiting | kCFRunLoopAfterWaiting -#endif - ) + , m_runLoopActivityObserver(this, &QEventDispatcherCoreFoundation::handleRunLoopActivity, kCFRunLoopAllActivities) , m_runLoopModeTracker([[RunLoopModeTracker alloc] init]) , m_runLoopTimer(0) , m_blockedRunLoopTimer(0) @@ -247,14 +241,14 @@ bool QEventDispatcherCoreFoundation::processEvents(QEventLoop::ProcessEventsFlag bool eventsProcessed = false; if (flags & (QEventLoop::ExcludeUserInputEvents | QEventLoop::ExcludeSocketNotifiers)) - qWarning() << "processEvents() flags" << flags << "not supported on iOS"; + qCWarning(lcEventDispatcher) << "processEvents() flags" << flags << "not supported on iOS"; - qEventDispatcherDebug() << "Entering with " << flags; qIndent(); + qCDebug(lcEventDispatcher) << "Processing events with flags" << flags; if (m_blockedRunLoopTimer) { Q_ASSERT(m_blockedRunLoopTimer == m_runLoopTimer); - qEventDispatcherDebug() << "Recursing from blocked timer " << m_blockedRunLoopTimer; + qCDebug(lcEventDispatcher) << "Recursing from blocked timer" << m_blockedRunLoopTimer; m_runLoopTimer = 0; // Unset current timer to force creation of new timer updateTimers(); } @@ -266,7 +260,7 @@ bool QEventDispatcherCoreFoundation::processEvents(QEventLoop::ProcessEventsFlag m_postedEventsRunLoopSource.signal(); m_processEvents.deferredWakeUp = false; - qEventDispatcherDebug() << "Processed deferred wake-up"; + qCDebug(lcEventDispatcher) << "Processed deferred wake-up"; } // The documentation states that this signal is emitted after the event @@ -287,12 +281,12 @@ bool QEventDispatcherCoreFoundation::processEvents(QEventLoop::ProcessEventsFlag CFTimeInterval duration = (m_processEvents.flags & QEventLoop::WaitForMoreEvents) ? kCFTimeIntervalDistantFuture : kCFTimeIntervalMinimum; - qEventDispatcherDebug() << "Calling CFRunLoopRunInMode = " << qPrintable(QString::fromCFString(mode)) - << " for " << duration << " ms, processing single source = " << returnAfterSingleSourceHandled; qIndent(); + qCDebug(lcEventDispatcher) << "Calling CFRunLoopRunInMode =" << qPrintable(QString::fromCFString(mode)) + << "for" << duration << "ms, processing single source =" << returnAfterSingleSourceHandled; SInt32 result = CFRunLoopRunInMode(mode, duration, returnAfterSingleSourceHandled); - qUnIndent(); qEventDispatcherDebug() << "result = " << qPrintableResult(result); + qCDebug(lcEventDispatcher) << "result =" << qPrintableResult(result); eventsProcessed |= (result == kCFRunLoopRunHandledSource || m_processEvents.processedPostedEvents @@ -316,15 +310,15 @@ bool QEventDispatcherCoreFoundation::processEvents(QEventLoop::ProcessEventsFlag // immediately, since it has already been exited. if (!currentEventLoop()->isRunning()) { - qEventDispatcherDebug() << "Top level event loop was exited"; + qCDebug(lcEventDispatcher) << "Top level event loop was exited"; break; } else { - qEventDispatcherDebug() << "Top level event loop still running, making another pass"; + qCDebug(lcEventDispatcher) << "Top level event loop still running, making another pass"; } } else { // We were called manually, through processEvents(), and should stop processing // events, even if we didn't finish processing all the queued events. - qEventDispatcherDebug() << "Top level processEvents was interrupted"; + qCDebug(lcEventDispatcher) << "Top level processEvents was interrupted"; break; } } @@ -353,7 +347,7 @@ bool QEventDispatcherCoreFoundation::processEvents(QEventLoop::ProcessEventsFlag // date in the past (overdue) will fire on the next run loop pass. The Qt // APIs on the other hand document eg. zero-interval timers to always be // handled after processing all available window-system events. - qEventDispatcherDebug() << "Manually processing timers due to overdue timer"; + qCDebug(lcEventDispatcher) << "Manually processing timers due to overdue timer"; processTimers(0); eventsProcessed = true; } @@ -372,7 +366,7 @@ bool QEventDispatcherCoreFoundation::processEvents(QEventLoop::ProcessEventsFlag if (m_processEvents.deferredWakeUp) { m_postedEventsRunLoopSource.signal(); - qEventDispatcherDebug() << "Processed deferred wake-up"; + qCDebug(lcEventDispatcher) << "Processed deferred wake-up"; } bool wasInterrupted = m_processEvents.wasInterrupted; @@ -385,11 +379,11 @@ bool QEventDispatcherCoreFoundation::processEvents(QEventLoop::ProcessEventsFlag // others below it (eg, in the case of nested event loops). We need to trigger // another interrupt so that the parent processEvents call has a chance to check // if it should continue. - qEventDispatcherDebug() << "Forwarding interrupt in case of nested processEvents"; + qCDebug(lcEventDispatcher) << "Forwarding interrupt in case of nested processEvents"; interrupt(); } - qEventDispatcherDebug() << "Returning with eventsProcessed = " << eventsProcessed; qUnIndent(); + qCDebug(lcEventDispatcher) << "Returning with eventsProcessed =" << eventsProcessed; return eventsProcessed; } @@ -397,15 +391,14 @@ bool QEventDispatcherCoreFoundation::processEvents(QEventLoop::ProcessEventsFlag bool QEventDispatcherCoreFoundation::processPostedEvents() { if (m_processEvents.processedPostedEvents && !(m_processEvents.flags & QEventLoop::EventLoopExec)) { - qEventDispatcherDebug() << "Already processed events this pass"; + qCDebug(lcEventDispatcher) << "Already processed events this pass"; return false; } m_processEvents.processedPostedEvents = true; - qEventDispatcherDebug() << "Sending posted events for " << m_processEvents.flags; qIndent(); + qCDebug(lcEventDispatcher) << "Sending posted events for" << m_processEvents.flags; QCoreApplication::sendPostedEvents(); - qUnIndent(); return true; } @@ -413,12 +406,12 @@ bool QEventDispatcherCoreFoundation::processPostedEvents() void QEventDispatcherCoreFoundation::processTimers(CFRunLoopTimerRef timer) { if (m_processEvents.processedTimers && !(m_processEvents.flags & QEventLoop::EventLoopExec)) { - qEventDispatcherDebug() << "Already processed timers this pass"; + qCDebug(lcEventDispatcher) << "Already processed timers this pass"; m_processEvents.deferredUpdateTimers = true; return; } - qEventDispatcherDebug() << "CFRunLoopTimer " << timer << " fired, activating Qt timers"; qIndent(); + qCDebug(lcEventDispatcher) << "CFRunLoopTimer" << timer << "fired, activating Qt timers"; // Activating Qt timers might recurse into processEvents() if a timer-callback // brings up a new event-loop or tries to processes events manually. Although @@ -436,15 +429,15 @@ void QEventDispatcherCoreFoundation::processTimers(CFRunLoopTimerRef timer) m_blockedRunLoopTimer = previouslyBlockedRunLoopTimer; m_processEvents.processedTimers = true; - qUnIndent(); - // Now that the timer source is unblocked we may need to schedule it again updateTimers(); } +Q_LOGGING_CATEGORY(lcEventDispatcherActivity, "qt.eventdispatcher.activity") + void QEventDispatcherCoreFoundation::handleRunLoopActivity(CFRunLoopActivity activity) { - qEventDispatcherDebug() << qPrintableActivity(activity); + qCDebug(lcEventDispatcherActivity) << "Runloop entered activity" << qPrintableActivity(activity); switch (activity) { case kCFRunLoopBeforeWaiting: @@ -463,13 +456,11 @@ void QEventDispatcherCoreFoundation::handleRunLoopActivity(CFRunLoopActivity act case kCFRunLoopAfterWaiting: emit awake(); break; -#if DEBUG_EVENT_DISPATCHER case kCFRunLoopEntry: case kCFRunLoopBeforeTimers: case kCFRunLoopBeforeSources: case kCFRunLoopExit: break; -#endif default: Q_UNREACHABLE(); } @@ -502,19 +493,19 @@ void QEventDispatcherCoreFoundation::wakeUp() // posted event gets processed on the next processEvents() call, so we flag the // need to do a deferred wake-up. m_processEvents.deferredWakeUp = true; - qEventDispatcherDebug() << "Already processed posted events, deferring wakeUp"; + qCDebug(lcEventDispatcher) << "Already processed posted events, deferring wakeUp"; return; } m_postedEventsRunLoopSource.signal(); CFRunLoopWakeUp(CFRunLoopGetMain()); - qEventDispatcherDebug() << "Signaled posted event run-loop source"; + qCDebug(lcEventDispatcher) << "Signaled posted event run-loop source"; } void QEventDispatcherCoreFoundation::interrupt() { - qEventDispatcherDebug() << "Marking current processEvent as interrupted"; + qCDebug(lcEventDispatcher) << "Marking current processEvent as interrupted"; m_processEvents.wasInterrupted = true; CFRunLoopStop(CFRunLoopGetMain()); } @@ -540,8 +531,8 @@ void QEventDispatcherCoreFoundation::unregisterSocketNotifier(QSocketNotifier *n void QEventDispatcherCoreFoundation::registerTimer(int timerId, int interval, Qt::TimerType timerType, QObject *object) { - qEventDispatcherDebug() << "id = " << timerId << ", interval = " << interval - << ", type = " << timerType << ", object = " << object; + qCDebug(lcEventDispatcherTimers) << "Registering timer with id =" << timerId << "interval =" << interval + << "type =" << timerType << "object =" << object; Q_ASSERT(timerId > 0 && interval >= 0 && object); Q_ASSERT(object->thread() == thread() && thread() == QThread::currentThread()); @@ -557,7 +548,7 @@ bool QEventDispatcherCoreFoundation::unregisterTimer(int timerId) bool returnValue = m_timerInfoList.unregisterTimer(timerId); - qEventDispatcherDebug() << "id = " << timerId << ", timers left: " << m_timerInfoList.size(); + qCDebug(lcEventDispatcherTimers) << "Unegistered timer with id =" << timerId << "Timers left:" << m_timerInfoList.size(); updateTimers(); return returnValue; @@ -569,7 +560,7 @@ bool QEventDispatcherCoreFoundation::unregisterTimers(QObject *object) bool returnValue = m_timerInfoList.unregisterTimers(object); - qEventDispatcherDebug() << "object = " << object << ", timers left: " << m_timerInfoList.size(); + qCDebug(lcEventDispatcherTimers) << "Unegistered timers for object =" << object << "Timers left:" << m_timerInfoList.size(); updateTimers(); return returnValue; @@ -612,16 +603,16 @@ void QEventDispatcherCoreFoundation::updateTimers() }); CFRunLoopAddTimer(CFRunLoopGetMain(), m_runLoopTimer, kCFRunLoopCommonModes); - qEventDispatcherDebug() << "Created new CFRunLoopTimer " << m_runLoopTimer; + qCDebug(lcEventDispatcherTimers) << "Created new CFRunLoopTimer" << m_runLoopTimer; } else { CFRunLoopTimerSetNextFireDate(m_runLoopTimer, timeToFire); - qEventDispatcherDebug() << "Re-scheduled CFRunLoopTimer " << m_runLoopTimer; + qCDebug(lcEventDispatcherTimers) << "Re-scheduled CFRunLoopTimer" << m_runLoopTimer; } m_overdueTimerScheduled = !timespecToSeconds(tv); - qEventDispatcherDebug() << "Next timeout in " << tv << " seconds"; + qCDebug(lcEventDispatcherTimers) << "Next timeout in" << tv << "seconds"; } else { // No Qt timers are registered, so make sure we're not running any CF timers @@ -637,7 +628,7 @@ void QEventDispatcherCoreFoundation::invalidateTimer() return; CFRunLoopTimerInvalidate(m_runLoopTimer); - qEventDispatcherDebug() << "Invalidated CFRunLoopTimer " << m_runLoopTimer; + qCDebug(lcEventDispatcherTimers) << "Invalidated CFRunLoopTimer" << m_runLoopTimer; CFRelease(m_runLoopTimer); m_runLoopTimer = 0; diff --git a/src/corelib/kernel/qeventdispatcher_cf_p.h b/src/corelib/kernel/qeventdispatcher_cf_p.h index 8a234ebc40..a607ab7a15 100644 --- a/src/corelib/kernel/qeventdispatcher_cf_p.h +++ b/src/corelib/kernel/qeventdispatcher_cf_p.h @@ -85,19 +85,22 @@ // We mean it. // -#define DEBUG_EVENT_DISPATCHER 0 - #include #include #include #include #include +#include + #include Q_FORWARD_DECLARE_OBJC_CLASS(QT_MANGLE_NAMESPACE(RunLoopModeTracker)); QT_BEGIN_NAMESPACE +Q_DECLARE_LOGGING_CATEGORY(lcEventDispatcher); +Q_DECLARE_LOGGING_CATEGORY(lcEventDispatcherTimers) + class QEventDispatcherCoreFoundation; template @@ -269,17 +272,4 @@ private: QT_END_NAMESPACE -#if DEBUG_EVENT_DISPATCHER -extern uint g_eventDispatcherIndentationLevel; -#define qEventDispatcherDebug() qDebug().nospace() \ - << qPrintable(QString(QLatin1String("| ")).repeated(g_eventDispatcherIndentationLevel)) \ - << __FUNCTION__ << "(): " -#define qIndent() ++g_eventDispatcherIndentationLevel -#define qUnIndent() --g_eventDispatcherIndentationLevel -#else -#define qEventDispatcherDebug() QT_NO_QDEBUG_MACRO() -#define qIndent() -#define qUnIndent() -#endif - #endif // QEVENTDISPATCHER_CF_P_H diff --git a/src/plugins/platforms/ios/qioseventdispatcher.mm b/src/plugins/platforms/ios/qioseventdispatcher.mm index de2c30cdfb..a6f6a7aac9 100644 --- a/src/plugins/platforms/ios/qioseventdispatcher.mm +++ b/src/plugins/platforms/ios/qioseventdispatcher.mm @@ -228,7 +228,7 @@ extern "C" int qt_main_wrapper(int argc, char *argv[]) } } - qEventDispatcherDebug() << "Running UIApplicationMain"; qIndent(); + qCDebug(lcEventDispatcher) << "Running UIApplicationMain"; return UIApplicationMain(argc, argv, nil, NSStringFromClass([QIOSApplicationDelegate class])); } } @@ -263,7 +263,7 @@ static void __attribute__((noinline, noreturn)) user_main_trampoline() int exitCode = main(argc, argv); delete[] argv; - qEventDispatcherDebug() << "Returned from main with exit code " << exitCode; + qCDebug(lcEventDispatcher) << "Returned from main with exit code " << exitCode; if (Q_UNLIKELY(debugStackUsage)) userMainStack.printUsage(); @@ -322,7 +322,7 @@ static bool rootLevelRunLoopIntegration() + (void)applicationDidFinishLaunching:(NSNotification *)notification { - qCDebug(lcQpaApplication) << "Application launched with options" << notification.userInfo; + qCDebug(lcEventDispatcher) << "Application launched with options" << notification.userInfo; if (!isQtApplication()) return; @@ -331,7 +331,7 @@ static bool rootLevelRunLoopIntegration() // We schedule the main-redirection for the next run-loop pass, so that we // can return from this function and let UIApplicationMain finish its job. // This results in running Qt's application eventloop as a nested runloop. - qCDebug(lcQpaApplication) << "Scheduling main() on next run-loop pass"; + qCDebug(lcEventDispatcher) << "Scheduling main() on next run-loop pass"; CFRunLoopTimerRef userMainTimer = CFRunLoopTimerCreateWithHandler(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent(), 0, 0, 0, ^(CFRunLoopTimerRef) { user_main_trampoline(); }); CFRunLoopAddTimer(CFRunLoopGetMain(), userMainTimer, kCFRunLoopCommonModes); @@ -339,10 +339,10 @@ static bool rootLevelRunLoopIntegration() return; } + switch (setjmp(processEventEnterJumpPoint)) { case kJumpPointSetSuccessfully: - qCDebug(lcQpaApplication) << "Running main() on separate stack"; qIndent(); - + qCDebug(lcEventDispatcher) << "Running main() on separate stack"; // Redirect the stack pointer to the start of the reserved stack. This ensures // that when we longjmp out of the event dispatcher and continue execution, the // 'Qt main' call-stack will not be smashed, as it lives in a part of the stack @@ -360,7 +360,7 @@ static bool rootLevelRunLoopIntegration() case kJumpedFromEventDispatcherProcessEvents: // We've returned from the longjmp in the event dispatcher, // and the stack has been restored to its old self. - qUnIndent(); qCDebug(lcQpaApplication) << "Returned from processEvents"; + qCDebug(lcEventDispatcher) << "↳ Jumped from processEvents due to exec"; if (Q_UNLIKELY(debugStackUsage)) userMainStack.printUsage(); @@ -396,18 +396,18 @@ static const char kApplicationWillTerminateExitCode = char(SIGTERM | 0x80); applicationAboutToTerminate = true; switch (setjmp(applicationWillTerminateJumpPoint)) { case kJumpPointSetSuccessfully: - qEventDispatcherDebug() << "Exiting qApp with SIGTERM exit code"; qIndent(); + qCDebug(lcEventDispatcher) << "Exiting qApp with SIGTERM exit code"; qApp->exit(kApplicationWillTerminateExitCode); // The runloop will not exit when the application is about to terminate, // so we'll never see the exit activity and have a chance to return from // QEventLoop::exec(). We initiate the return manually as a workaround. - qEventDispatcherDebug() << "Manually triggering return from event loop exec"; + qCDebug(lcEventDispatcher) << "Manually triggering return from event loop exec"; static_cast(qApp->eventDispatcher())->interruptEventLoopExec(); break; case kJumpedFromUserMainTrampoline: // The user's main has returned, so we're ready to let iOS terminate the application - qUnIndent(); qEventDispatcherDebug() << "kJumpedFromUserMainTrampoline, allowing iOS to terminate"; + qCDebug(lcEventDispatcher) << "kJumpedFromUserMainTrampoline, allowing iOS to terminate"; break; default: qFatal("Unexpected jump result in event loop integration"); @@ -434,13 +434,15 @@ bool __attribute__((returns_twice)) QIOSEventDispatcher::processEvents(QEventLoo return QEventDispatcherCoreFoundation::processEvents(flags); if (applicationAboutToTerminate) { - qEventDispatcherDebug() << "Detected QEventLoop exec after application termination"; + qCDebug(lcEventDispatcher) << "Detected QEventLoop exec after application termination"; // Re-issue exit, and return immediately qApp->exit(kApplicationWillTerminateExitCode); return false; } if (!m_processEventLevel && (flags & QEventLoop::EventLoopExec)) { + qCDebug(lcEventDispatcher) << "Processing events with flags" << flags; + ++m_processEventLevel; m_runLoopExitObserver.addToMode(kCFRunLoopCommonModes); @@ -449,7 +451,7 @@ bool __attribute__((returns_twice)) QIOSEventDispatcher::processEvents(QEventLoo // is asked to exit, so that we can return from QEventLoop::exec(). switch (setjmp(processEventExitJumpPoint)) { case kJumpPointSetSuccessfully: - qEventDispatcherDebug() << "QEventLoop exec detected, jumping back to native runloop"; + qCDebug(lcEventDispatcher) << "QEventLoop exec detected, jumping back to system runloop ↵"; longjmp(processEventEnterJumpPoint, kJumpedFromEventDispatcherProcessEvents); break; case kJumpedFromEventLoopExecInterrupt: @@ -457,7 +459,7 @@ bool __attribute__((returns_twice)) QIOSEventDispatcher::processEvents(QEventLoo // signal), and we jumped back though processEventExitJumpPoint. We return from processEvents, // which will emit aboutToQuit if it's QApplication's event loop, and then return to the user's // main, which can do whatever it wants, including calling exec() on the application again. - qEventDispatcherDebug() << "kJumpedFromEventLoopExecInterrupt, returning with eventsProcessed = true"; + qCDebug(lcEventDispatcher) << "⇢ System runloop exited, returning with eventsProcessed = true"; return true; default: qFatal("Unexpected jump result in event loop integration"); @@ -485,9 +487,8 @@ bool QIOSEventDispatcher::processPostedEvents() if (!QEventDispatcherCoreFoundation::processPostedEvents()) return false; - qEventDispatcherDebug() << "Sending window system events for " << m_processEvents.flags; qIndent(); + qCDebug(lcEventDispatcher) << "Sending window system events for" << m_processEvents.flags; QWindowSystemInterface::sendWindowSystemEvents(m_processEvents.flags); - qUnIndent(); return true; } @@ -497,10 +498,8 @@ void QIOSEventDispatcher::handleRunLoopExit(CFRunLoopActivity activity) Q_UNUSED(activity); Q_ASSERT(activity == kCFRunLoopExit); - if (m_processEventLevel == 1 && !currentEventLoop()->isRunning()) { - qEventDispatcherDebug() << "Root runloop level exited"; + if (m_processEventLevel == 1 && !currentEventLoop()->isRunning()) interruptEventLoopExec(); - } } void QIOSEventDispatcher::interruptEventLoopExec() @@ -516,12 +515,12 @@ void QIOSEventDispatcher::interruptEventLoopExec() // processEvents, instead of back in didFinishLaunchingWithOptions. switch (setjmp(processEventEnterJumpPoint)) { case kJumpPointSetSuccessfully: - qEventDispatcherDebug() << "Jumping back to processEvents"; + qCDebug(lcEventDispatcher) << "Jumping into processEvents due to system runloop exit ⇢"; longjmp(processEventExitJumpPoint, kJumpedFromEventLoopExecInterrupt); break; case kJumpedFromEventDispatcherProcessEvents: // QEventLoop was re-executed - qEventDispatcherDebug() << "kJumpedFromEventDispatcherProcessEvents"; + qCDebug(lcEventDispatcher) << "↳ Jumped from processEvents due to re-exec"; break; default: qFatal("Unexpected jump result in event loop integration"); -- cgit v1.2.3 From fadaf7053d067e3346ce2b000b700dfc918448f2 Mon Sep 17 00:00:00 2001 From: Albert Astals Cid Date: Mon, 5 Feb 2018 12:10:55 +0100 Subject: Remove QPlatformPrinterSupport::createDefaultPrintDevice no one uses it Change-Id: I03d0350a44a19545d57b47d1b21d85f1a2059f4d Reviewed-by: Frederik Gladhorn --- src/printsupport/kernel/qplatformprintersupport.cpp | 5 ----- src/printsupport/kernel/qplatformprintersupport.h | 1 - 2 files changed, 6 deletions(-) (limited to 'src') diff --git a/src/printsupport/kernel/qplatformprintersupport.cpp b/src/printsupport/kernel/qplatformprintersupport.cpp index e12a292aec..a25dc6d45c 100644 --- a/src/printsupport/kernel/qplatformprintersupport.cpp +++ b/src/printsupport/kernel/qplatformprintersupport.cpp @@ -89,11 +89,6 @@ QPrintDevice QPlatformPrinterSupport::createPrintDevice(const QString &id) return QPrintDevice(); } -QPrintDevice QPlatformPrinterSupport::createDefaultPrintDevice() -{ - return createPrintDevice(defaultPrintDeviceId()); -} - QStringList QPlatformPrinterSupport::availablePrintDeviceIds() const { return QStringList(); diff --git a/src/printsupport/kernel/qplatformprintersupport.h b/src/printsupport/kernel/qplatformprintersupport.h index 6a4246adc0..413c1067c2 100644 --- a/src/printsupport/kernel/qplatformprintersupport.h +++ b/src/printsupport/kernel/qplatformprintersupport.h @@ -76,7 +76,6 @@ public: virtual QPaintEngine *createPaintEngine(QPrintEngine *, QPrinter::PrinterMode printerMode); virtual QPrintDevice createPrintDevice(const QString &id); - virtual QPrintDevice createDefaultPrintDevice(); virtual QStringList availablePrintDeviceIds() const; virtual QString defaultPrintDeviceId() const; -- cgit v1.2.3 From 10b9ff0a4de5eb88d8cac4a87265c032cc04718f Mon Sep 17 00:00:00 2001 From: Albert Astals Cid Date: Mon, 5 Feb 2018 11:01:01 +0100 Subject: Remove QCupsPrintEnginePrivate::setupDefaultPrinter it was declared but never used Change-Id: Iededb2943759cd2d66de95bcc1758dbcc4283ee3 Reviewed-by: Frederik Gladhorn --- src/plugins/printsupport/cups/qcupsprintengine.cpp | 32 ---------------------- src/plugins/printsupport/cups/qcupsprintengine_p.h | 1 - 2 files changed, 33 deletions(-) (limited to 'src') diff --git a/src/plugins/printsupport/cups/qcupsprintengine.cpp b/src/plugins/printsupport/cups/qcupsprintengine.cpp index 763f676e41..bd0d641e79 100644 --- a/src/plugins/printsupport/cups/qcupsprintengine.cpp +++ b/src/plugins/printsupport/cups/qcupsprintengine.cpp @@ -250,38 +250,6 @@ void QCupsPrintEnginePrivate::closePrintDevice() } } -void QCupsPrintEnginePrivate::setupDefaultPrinter() -{ - // Should never have reached here if no plugin available, but check just in case - QPlatformPrinterSupport *ps = QPlatformPrinterSupportPlugin::get(); - if (!ps) - return; - - // Get default printer id, if no default then use the first available - // TODO Find way to remove printerName from base class? - printerName = ps->defaultPrintDeviceId(); - if (printerName.isEmpty()) { - QStringList list = ps->availablePrintDeviceIds(); - if (list.size() > 0) - printerName = list.at(0); - } - - // Should never have reached here if no printers available, but check just in case - if (printerName.isEmpty()) - return; - - m_printDevice = ps->createPrintDevice(printerName); - if (!m_printDevice.isValid()) - return; - - // Setup the printer defaults - duplex = m_printDevice.defaultDuplexMode(); - grayscale = m_printDevice.defaultColorMode() == QPrint::GrayScale; - // CUPS server always supports collation, even if individual m_printDevice doesn't - collate = true; - setPageSize(m_printDevice.defaultPageSize()); -} - void QCupsPrintEnginePrivate::changePrinter(const QString &newPrinter) { // Don't waste time if same printer name diff --git a/src/plugins/printsupport/cups/qcupsprintengine_p.h b/src/plugins/printsupport/cups/qcupsprintengine_p.h index c99c48c7ea..d5363bb8cc 100644 --- a/src/plugins/printsupport/cups/qcupsprintengine_p.h +++ b/src/plugins/printsupport/cups/qcupsprintengine_p.h @@ -93,7 +93,6 @@ public: private: Q_DISABLE_COPY(QCupsPrintEnginePrivate) - void setupDefaultPrinter(); void changePrinter(const QString &newPrinter); void setPageSize(const QPageSize &pageSize); -- cgit v1.2.3 From 2e0849e79c851318da0f148dd4002b02cea66dbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Mill=C3=A1n=20Soto?= Date: Fri, 15 Sep 2017 00:30:38 +0200 Subject: Use placeholder text for accessible description of QLineEdit If the accessible description of a QLineEdit is requested and none has been set, the placeholder text will be provided if one is available. Change-Id: I4c0dad1d06fd3d8a6e00c963402d380c59bd7f05 Reviewed-by: Jesus Fernandez Reviewed-by: Frederik Gladhorn --- src/widgets/accessible/simplewidgets.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/widgets/accessible/simplewidgets.cpp b/src/widgets/accessible/simplewidgets.cpp index f0423ca825..efcf4cdc8b 100644 --- a/src/widgets/accessible/simplewidgets.cpp +++ b/src/widgets/accessible/simplewidgets.cpp @@ -704,6 +704,8 @@ QString QAccessibleLineEdit::text(QAccessible::Text t) const } if (str.isEmpty()) str = QAccessibleWidget::text(t); + if (str.isEmpty() && t == QAccessible::Description) + str = lineEdit()->placeholderText(); return str; } -- cgit v1.2.3 From cf1b732e21b8768c0e423f900a3f11f923863edd Mon Sep 17 00:00:00 2001 From: Sergio Martins Date: Fri, 2 Feb 2018 11:44:22 +0000 Subject: Introduce QT6_VIRTUAL and QT6_NOT_VIRTUAL macros To avoid QT_VERSION_CHECK ifdefs Change-Id: I364903964c72f4df19b8b7c10c19b82d24f63600 Reviewed-by: Lars Knoll --- src/corelib/global/qglobal.h | 5 +++++ src/widgets/graphicsview/qgraphicsscene.h | 6 +----- src/widgets/kernel/qlayout.h | 4 ++-- src/xml/sax/qxml.h | 6 ++---- 4 files changed, 10 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/corelib/global/qglobal.h b/src/corelib/global/qglobal.h index 7764707de0..aa9446221b 100644 --- a/src/corelib/global/qglobal.h +++ b/src/corelib/global/qglobal.h @@ -88,6 +88,11 @@ #if QT_VERSION >= QT_VERSION_CHECK(6,0,0) # define QT_NO_UNSHARABLE_CONTAINERS +# define QT6_VIRTUAL virtual +# define QT6_NOT_VIRTUAL +#else +# define QT6_VIRTUAL +# define QT6_NOT_VIRTUAL virtual #endif /* These two macros makes it possible to turn the builtin line expander into a diff --git a/src/widgets/graphicsview/qgraphicsscene.h b/src/widgets/graphicsview/qgraphicsscene.h index 5ecd2baab8..8efbcd273e 100644 --- a/src/widgets/graphicsview/qgraphicsscene.h +++ b/src/widgets/graphicsview/qgraphicsscene.h @@ -288,11 +288,7 @@ protected: QWidget *widget = nullptr); protected Q_SLOTS: - // ### Qt 6: make unconditional -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) - virtual -#endif - bool focusNextPrevChild(bool next); + QT6_VIRTUAL bool focusNextPrevChild(bool next); Q_SIGNALS: void changed(const QList ®ion); diff --git a/src/widgets/kernel/qlayout.h b/src/widgets/kernel/qlayout.h index abadf2e69d..bcc33a0811 100644 --- a/src/widgets/kernel/qlayout.h +++ b/src/widgets/kernel/qlayout.h @@ -126,8 +126,8 @@ public: bool isEmpty() const override; QSizePolicy::ControlTypes controlTypes() const override; - // ### Qt 6 make this function virtual - QLayoutItem *replaceWidget(QWidget *from, QWidget *to, Qt::FindChildOptions options = Qt::FindChildrenRecursively); + QT6_VIRTUAL QLayoutItem *replaceWidget(QWidget *from, QWidget *to, + Qt::FindChildOptions options = Qt::FindChildrenRecursively); int totalHeightForWidth(int w) const; QSize totalMinimumSize() const; diff --git a/src/xml/sax/qxml.h b/src/xml/sax/qxml.h index 7c5b5fe204..94dc8dfb8e 100644 --- a/src/xml/sax/qxml.h +++ b/src/xml/sax/qxml.h @@ -122,10 +122,8 @@ public: QXmlAttributes &operator=(const QXmlAttributes &) = default; QXmlAttributes &operator=(QXmlAttributes &&) Q_DECL_NOTHROW = default; #endif // default members -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - virtual // ### Qt 6: this value class don't need no virtual dtor -#endif - ~QXmlAttributes(); + + QT6_NOT_VIRTUAL ~QXmlAttributes(); void swap(QXmlAttributes &other) Q_DECL_NOTHROW { -- cgit v1.2.3 From a0717c60b30be5f7aa4c9dcde06a42f9ee532f0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Mill=C3=A1n=20Soto?= Date: Fri, 15 Sep 2017 20:15:00 +0200 Subject: Handle coordinates related to component window AtSpiAdaptor::componentInterface was not checking the coordinate type in GetAccessibleAtPoint. Now, if the coordinate type is ATSPI_COORD_TYPE_WINDOW the coordinates of the window will be added to the specified coordinates. Change-Id: Iba571109a8da300f4141d616a94ef4ac87918f98 Reviewed-by: Jesus Fernandez Reviewed-by: Frederik Gladhorn --- src/platformsupport/linuxaccessibility/atspiadaptor.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/platformsupport/linuxaccessibility/atspiadaptor.cpp b/src/platformsupport/linuxaccessibility/atspiadaptor.cpp index a936ec7aad..580cf0e31d 100644 --- a/src/platformsupport/linuxaccessibility/atspiadaptor.cpp +++ b/src/platformsupport/linuxaccessibility/atspiadaptor.cpp @@ -1601,7 +1601,13 @@ bool AtSpiAdaptor::componentInterface(QAccessibleInterface *interface, const QSt int x = message.arguments().at(0).toInt(); int y = message.arguments().at(1).toInt(); uint coordType = message.arguments().at(2).toUInt(); - Q_UNUSED (coordType) // FIXME + if (coordType == ATSPI_COORD_TYPE_WINDOW) { + QWindow * window = interface->window(); + if (window) { + x += window->position().x(); + y += window->position().y(); + } + } QAccessibleInterface * childInterface(interface->childAt(x, y)); QAccessibleInterface * iface = 0; -- cgit v1.2.3 From 076087717e4b6ee0953902b24676e54a73ff5fff Mon Sep 17 00:00:00 2001 From: Christian Ehrlicher Date: Fri, 2 Feb 2018 21:08:44 +0100 Subject: Documentation: Update CommonTableModel/StringListModel snippets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update CommonTableModel/StringListModel snippets: - 0 -> nullptr - use 'override' - replace "" with QString() - use QStringLiteral instead QString - pass role to dataChanged() signal Change-Id: I5949d1bd6fee3186f12191f1f6235ae18908096e Reviewed-by: Topi Reiniö --- .../doc/snippets/common-table-model/model.cpp | 12 ++++++------ .../doc/snippets/common-table-model/model.h | 22 +++++++++++----------- src/widgets/doc/snippets/stringlistmodel/model.cpp | 6 +++--- src/widgets/doc/snippets/stringlistmodel/model.h | 16 ++++++++-------- 4 files changed, 28 insertions(+), 28 deletions(-) (limited to 'src') diff --git a/src/widgets/doc/snippets/common-table-model/model.cpp b/src/widgets/doc/snippets/common-table-model/model.cpp index 96df100f26..2f6e9f1144 100644 --- a/src/widgets/doc/snippets/common-table-model/model.cpp +++ b/src/widgets/doc/snippets/common-table-model/model.cpp @@ -68,7 +68,7 @@ TableModel::TableModel(int rows, int columns, QObject *parent) QStringList newList; for (int column = 0; column < qMax(1, columns); ++column) { - newList.append(""); + newList.append(QString()); } for (int row = 0; row < qMax(1, rows); ++row) { @@ -129,9 +129,9 @@ QVariant TableModel::headerData(int section, Qt::Orientation orientation, return QVariant(); if (orientation == Qt::Horizontal) - return QString("Column %1").arg(section); + return QStringLiteral("Column %1").arg(section); else - return QString("Row %1").arg(section); + return QStringLiteral("Row %1").arg(section); } /*! @@ -164,7 +164,7 @@ bool TableModel::setData(const QModelIndex &index, return false; rowList[index.row()][index.column()] = value.toString(); - emit dataChanged(index, index); + emit dataChanged(index, index, {role}); return true; } @@ -180,7 +180,7 @@ bool TableModel::insertRows(int position, int rows, const QModelIndex &parent) for (int row = 0; row < rows; ++row) { QStringList items; for (int column = 0; column < columns; ++column) - items.append(""); + items.append(QString()); rowList.insert(position, items); } @@ -202,7 +202,7 @@ bool TableModel::insertColumns(int position, int columns, for (int row = 0; row < rows; ++row) { for (int column = position; column < columns; ++column) { - rowList[row].insert(position, ""); + rowList[row].insert(position, QString()); } } diff --git a/src/widgets/doc/snippets/common-table-model/model.h b/src/widgets/doc/snippets/common-table-model/model.h index 68e6c00702..5f152e0e78 100644 --- a/src/widgets/doc/snippets/common-table-model/model.h +++ b/src/widgets/doc/snippets/common-table-model/model.h @@ -60,22 +60,22 @@ class TableModel : public QAbstractTableModel Q_OBJECT public: - TableModel(int rows = 1, int columns = 1, QObject *parent = 0); + TableModel(int rows = 1, int columns = 1, QObject *parent = nullptr); - int rowCount(const QModelIndex &parent = QModelIndex()) const; - int columnCount(const QModelIndex &parent = QModelIndex()) const; - QVariant data(const QModelIndex &index, int role) const; + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + int columnCount(const QModelIndex &parent = QModelIndex()) const override; + QVariant data(const QModelIndex &index, int role) const override; QVariant headerData(int section, Qt::Orientation orientation, - int role = Qt::DisplayRole) const; + int role = Qt::DisplayRole) const override; - Qt::ItemFlags flags(const QModelIndex &index) const; + Qt::ItemFlags flags(const QModelIndex &index) const override; bool setData(const QModelIndex &index, const QVariant &value, - int role = Qt::EditRole); + int role = Qt::EditRole) override; - bool insertRows(int position, int rows, const QModelIndex &parent = QModelIndex()); - bool insertColumns(int position, int columns, const QModelIndex &parent = QModelIndex()); - bool removeRows(int position, int rows, const QModelIndex &parent = QModelIndex()); - bool removeColumns(int position, int columns, const QModelIndex &parent = QModelIndex()); + bool insertRows(int position, int rows, const QModelIndex &parent = QModelIndex()) override; + bool insertColumns(int position, int columns, const QModelIndex &parent = QModelIndex()) override; + bool removeRows(int position, int rows, const QModelIndex &parent = QModelIndex()) override; + bool removeColumns(int position, int columns, const QModelIndex &parent = QModelIndex()) override; private: QList rowList; diff --git a/src/widgets/doc/snippets/stringlistmodel/model.cpp b/src/widgets/doc/snippets/stringlistmodel/model.cpp index f07f6b254f..52e78847d2 100644 --- a/src/widgets/doc/snippets/stringlistmodel/model.cpp +++ b/src/widgets/doc/snippets/stringlistmodel/model.cpp @@ -135,9 +135,9 @@ QVariant StringListModel::headerData(int section, Qt::Orientation orientation, return QVariant(); if (orientation == Qt::Horizontal) - return QString("Column %1").arg(section); + return QStringLiteral("Column %1").arg(section); else - return QString("Row %1").arg(section); + return QStringLiteral("Row %1").arg(section); } //! [2] @@ -174,7 +174,7 @@ bool StringListModel::setData(const QModelIndex &index, if (index.isValid() && role == Qt::EditRole) { stringList.replace(index.row(), value.toString()); - emit dataChanged(index, index); + emit dataChanged(index, index, {role}); return true; } //! [4] //! [5] diff --git a/src/widgets/doc/snippets/stringlistmodel/model.h b/src/widgets/doc/snippets/stringlistmodel/model.h index 50769ef67f..7012152b27 100644 --- a/src/widgets/doc/snippets/stringlistmodel/model.h +++ b/src/widgets/doc/snippets/stringlistmodel/model.h @@ -61,26 +61,26 @@ class StringListModel : public QAbstractListModel Q_OBJECT public: - StringListModel(const QStringList &strings, QObject *parent = 0) + StringListModel(const QStringList &strings, QObject *parent = nullptr) : QAbstractListModel(parent), stringList(strings) {} - int rowCount(const QModelIndex &parent = QModelIndex()) const; - QVariant data(const QModelIndex &index, int role) const; + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + QVariant data(const QModelIndex &index, int role) const override; QVariant headerData(int section, Qt::Orientation orientation, //! [0] //! [1] - int role = Qt::DisplayRole) const; + int role = Qt::DisplayRole) const override; //! [1] //! [2] - Qt::ItemFlags flags(const QModelIndex &index) const; + Qt::ItemFlags flags(const QModelIndex &index) const override; bool setData(const QModelIndex &index, const QVariant &value, //! [2] //! [3] - int role = Qt::EditRole); + int role = Qt::EditRole) override; //! [3] //! [4] - bool insertRows(int position, int rows, const QModelIndex &index = QModelIndex()); - bool removeRows(int position, int rows, const QModelIndex &index = QModelIndex()); + bool insertRows(int position, int rows, const QModelIndex &index = QModelIndex()) override; + bool removeRows(int position, int rows, const QModelIndex &index = QModelIndex()) override; //! [4] //! [5] -- cgit v1.2.3 From fd80b8aaeb836811113cb06f8965e4b2f0deaeff Mon Sep 17 00:00:00 2001 From: Allan Sandfeld Jensen Date: Wed, 13 Dec 2017 11:17:58 +0100 Subject: Unalias some core drawhelper loops Some compilers will assume src and buffer are different and only vectorize the unaliased case and take a slow path when they are equal. In our case they are as often equal, so we need to manually unalias the variables to make sure both cases are fully optimized. Change-Id: I6ec86171dd179844facdf45376253c55980d9e36 Reviewed-by: Thiago Macieira --- src/gui/painting/qdrawhelper.cpp | 112 +++++++++++++++++++-------------------- src/gui/painting/qdrawhelper_p.h | 18 +++++-- 2 files changed, 67 insertions(+), 63 deletions(-) (limited to 'src') diff --git a/src/gui/painting/qdrawhelper.cpp b/src/gui/painting/qdrawhelper.cpp index 6ddfcdfaf4..23c8e42ded 100644 --- a/src/gui/painting/qdrawhelper.cpp +++ b/src/gui/painting/qdrawhelper.cpp @@ -174,29 +174,31 @@ template static const uint *QT_FASTCALL convertToRGB32(uint *buffer, const uint *src, int count, const QVector *, QDitherInfo *) { - Q_CONSTEXPR uint redMask = ((1 << redWidth()) - 1); - Q_CONSTEXPR uint greenMask = ((1 << greenWidth()) - 1); - Q_CONSTEXPR uint blueMask = ((1 << blueWidth()) - 1); + auto conversion = [](uint s) { + // MSVC needs these constexpr defined in here otherwise it will create a capture. + Q_CONSTEXPR uint redMask = ((1 << redWidth()) - 1); + Q_CONSTEXPR uint greenMask = ((1 << greenWidth()) - 1); + Q_CONSTEXPR uint blueMask = ((1 << blueWidth()) - 1); - Q_CONSTEXPR uchar redLeftShift = 8 - redWidth(); - Q_CONSTEXPR uchar greenLeftShift = 8 - greenWidth(); - Q_CONSTEXPR uchar blueLeftShift = 8 - blueWidth(); + Q_CONSTEXPR uchar redLeftShift = 8 - redWidth(); + Q_CONSTEXPR uchar greenLeftShift = 8 - greenWidth(); + Q_CONSTEXPR uchar blueLeftShift = 8 - blueWidth(); - Q_CONSTEXPR uchar redRightShift = 2 * redWidth() - 8; - Q_CONSTEXPR uchar greenRightShift = 2 * greenWidth() - 8; - Q_CONSTEXPR uchar blueRightShift = 2 * blueWidth() - 8; + Q_CONSTEXPR uchar redRightShift = 2 * redWidth() - 8; + Q_CONSTEXPR uchar greenRightShift = 2 * greenWidth() - 8; + Q_CONSTEXPR uchar blueRightShift = 2 * blueWidth() - 8; - for (int i = 0; i < count; ++i) { - uint red = (src[i] >> redShift()) & redMask; - uint green = (src[i] >> greenShift()) & greenMask; - uint blue = (src[i] >> blueShift()) & blueMask; + uint red = (s >> redShift()) & redMask; + uint green = (s >> greenShift()) & greenMask; + uint blue = (s >> blueShift()) & blueMask; red = ((red << redLeftShift) | (red >> redRightShift)) << 16; green = ((green << greenLeftShift) | (green >> greenRightShift)) << 8; blue = (blue << blueLeftShift) | (blue >> blueRightShift); - buffer[i] = 0xff000000 | red | green | blue; - } + return 0xff000000 | red | green | blue; + }; + UNALIASED_CONVERSION_LOOP(buffer, src, count, conversion); return buffer; } @@ -348,21 +350,21 @@ static const uint *QT_FASTCALL convertRGBFromARGB32PM(uint *buffer, const uint * // RGB32 -> RGB888 is not a precision loss. if (!dither || (rWidth == 8 && gWidth == 8 && bWidth == 8)) { - Q_CONSTEXPR uint rMask = (1 << rWidth) - 1; - Q_CONSTEXPR uint gMask = (1 << gWidth) - 1; - Q_CONSTEXPR uint bMask = (1 << bWidth) - 1; + auto conversion = [](uint s) { + const uint c = fromRGB ? s : qUnpremultiply(s); + Q_CONSTEXPR uint rMask = (1 << redWidth()) - 1; + Q_CONSTEXPR uint gMask = (1 << greenWidth()) - 1; + Q_CONSTEXPR uint bMask = (1 << blueWidth()) - 1; + Q_CONSTEXPR uchar rRightShift = 24 - redWidth(); + Q_CONSTEXPR uchar gRightShift = 16 - greenWidth(); + Q_CONSTEXPR uchar bRightShift = 8 - blueWidth(); - Q_CONSTEXPR uchar rRightShift = 24 - rWidth; - Q_CONSTEXPR uchar gRightShift = 16 - gWidth; - Q_CONSTEXPR uchar bRightShift = 8 - bWidth; - - for (int i = 0; i < count; ++i) { - const uint c = fromRGB ? src[i] : qUnpremultiply(src[i]); const uint r = ((c >> rRightShift) & rMask) << redShift(); const uint g = ((c >> gRightShift) & gMask) << greenShift(); const uint b = ((c >> bRightShift) & bMask) << blueShift(); - buffer[i] = r | g | b; - } + return r | g | b; + }; + UNALIASED_CONVERSION_LOOP(buffer, src, count, conversion); } else { // We do ordered dither by using a rounding conversion, but instead of // adding half of input precision, we add the adjusted result from the @@ -394,32 +396,32 @@ template static const uint *QT_FASTCALL convertARGBPMFromARGB32PM(uint *buffer, const uint *src, int count, const QVector *, QDitherInfo *dither) { - Q_CONSTEXPR uchar aWidth = alphaWidth(); - Q_CONSTEXPR uchar rWidth = redWidth(); - Q_CONSTEXPR uchar gWidth = greenWidth(); - Q_CONSTEXPR uchar bWidth = blueWidth(); - if (!dither) { - Q_CONSTEXPR uint aMask = (1 << aWidth) - 1; - Q_CONSTEXPR uint rMask = (1 << rWidth) - 1; - Q_CONSTEXPR uint gMask = (1 << gWidth) - 1; - Q_CONSTEXPR uint bMask = (1 << bWidth) - 1; - - Q_CONSTEXPR uchar aRightShift = 32 - aWidth; - Q_CONSTEXPR uchar rRightShift = 24 - rWidth; - Q_CONSTEXPR uchar gRightShift = 16 - gWidth; - Q_CONSTEXPR uchar bRightShift = 8 - bWidth; - - Q_CONSTEXPR uint aOpaque = aMask << alphaShift(); - for (int i = 0; i < count; ++i) { - const uint c = src[i]; + auto conversion = [](uint c) { + Q_CONSTEXPR uint aMask = (1 << alphaWidth()) - 1; + Q_CONSTEXPR uint rMask = (1 << redWidth()) - 1; + Q_CONSTEXPR uint gMask = (1 << greenWidth()) - 1; + Q_CONSTEXPR uint bMask = (1 << blueWidth()) - 1; + + Q_CONSTEXPR uchar aRightShift = 32 - alphaWidth(); + Q_CONSTEXPR uchar rRightShift = 24 - redWidth(); + Q_CONSTEXPR uchar gRightShift = 16 - greenWidth(); + Q_CONSTEXPR uchar bRightShift = 8 - blueWidth(); + + Q_CONSTEXPR uint aOpaque = aMask << alphaShift(); const uint a = fromRGB ? aOpaque : (((c >> aRightShift) & aMask) << alphaShift()); const uint r = ((c >> rRightShift) & rMask) << redShift(); const uint g = ((c >> gRightShift) & gMask) << greenShift(); const uint b = ((c >> bRightShift) & bMask) << blueShift(); - buffer[i] = a | r | g | b; - } + return a | r | g | b; + }; + UNALIASED_CONVERSION_LOOP(buffer, src, count, conversion); } else { + Q_CONSTEXPR uchar aWidth = alphaWidth(); + Q_CONSTEXPR uchar rWidth = redWidth(); + Q_CONSTEXPR uchar gWidth = greenWidth(); + Q_CONSTEXPR uchar bWidth = blueWidth(); + const uint *bayer_line = qt_bayer_matrix[dither->y & 15]; for (int i = 0; i < count; ++i) { const uint c = src[i]; @@ -514,8 +516,7 @@ static const uint *QT_FASTCALL convertARGB32ToARGB32PM(uint *buffer, const uint static const uint *QT_FASTCALL convertRGBA8888PMToARGB32PM(uint *buffer, const uint *src, int count, const QVector *, QDitherInfo *) { - for (int i = 0; i < count; ++i) - buffer[i] = RGBA2ARGB(src[i]); + UNALIASED_CONVERSION_LOOP(buffer, src, count, RGBA2ARGB); return buffer; } @@ -568,8 +569,7 @@ static const uint *QT_FASTCALL convertARGB32FromARGB32PM(uint *buffer, const uin static const uint *QT_FASTCALL convertRGBA8888PMFromARGB32PM(uint *buffer, const uint *src, int count, const QVector *, QDitherInfo *) { - for (int i = 0; i < count; ++i) - buffer[i] = ARGB2RGBA(src[i]); + UNALIASED_CONVERSION_LOOP(buffer, src, count, ARGB2RGBA); return buffer; } @@ -695,8 +695,7 @@ static const uint *QT_FASTCALL convertRGBA8888FromARGB32PM(uint *buffer, const u static const uint *QT_FASTCALL convertRGBXFromRGB32(uint *buffer, const uint *src, int count, const QVector *, QDitherInfo *) { - for (int i = 0; i < count; ++i) - buffer[i] = ARGB2RGBA(0xff000000 | src[i]); + UNALIASED_CONVERSION_LOOP(buffer, src, count, [](uint c) { return ARGB2RGBA(0xff000000 | c); }); return buffer; } @@ -713,8 +712,7 @@ static const uint *QT_FASTCALL convertA2RGB30PMToARGB32PM(uint *buffer, const ui const QVector *, QDitherInfo *dither) { if (!dither) { - for (int i = 0; i < count; ++i) - buffer[i] = qConvertA2rgb30ToArgb32(src[i]); + UNALIASED_CONVERSION_LOOP(buffer, src, count, qConvertA2rgb30ToArgb32); } else { for (int i = 0; i < count; ++i) { const uint c = src[i]; @@ -796,8 +794,7 @@ template static const uint *QT_FASTCALL convertA2RGB30PMFromARGB32PM(uint *buffer, const uint *src, int count, const QVector *, QDitherInfo *) { - for (int i = 0; i < count; ++i) - buffer[i] = qConvertArgb32ToA2rgb30(src[i]); + UNALIASED_CONVERSION_LOOP(buffer, src, count, qConvertArgb32ToA2rgb30); return buffer; } @@ -814,8 +811,7 @@ template static const uint *QT_FASTCALL convertRGB30FromARGB32PM(uint *buffer, const uint *src, int count, const QVector *, QDitherInfo *) { - for (int i = 0; i < count; ++i) - buffer[i] = qConvertRgb32ToRgb30(qUnpremultiply(src[i])); + UNALIASED_CONVERSION_LOOP(buffer, src, count, qConvertRgb32ToRgb30); return buffer; } diff --git a/src/gui/painting/qdrawhelper_p.h b/src/gui/painting/qdrawhelper_p.h index 4c2fe87355..ebf215a3eb 100644 --- a/src/gui/painting/qdrawhelper_p.h +++ b/src/gui/painting/qdrawhelper_p.h @@ -1104,22 +1104,30 @@ inline int qBlue565(quint16 rgb) { return (b << 3) | (b >> 2); } +// We manually unalias the variables to make sure the compiler +// fully optimizes both aliased and unaliased cases. +#define UNALIASED_CONVERSION_LOOP(buffer, src, count, conversion) \ + if (src == buffer) { \ + for (int i = 0; i < count; ++i) \ + buffer[i] = conversion(buffer[i]); \ + } else { \ + for (int i = 0; i < count; ++i) \ + buffer[i] = conversion(src[i]); \ + } + static Q_ALWAYS_INLINE const uint *qt_convertARGB32ToARGB32PM(uint *buffer, const uint *src, int count) { - for (int i = 0; i < count; ++i) - buffer[i] = qPremultiply(src[i]); + UNALIASED_CONVERSION_LOOP(buffer, src, count, qPremultiply); return buffer; } static Q_ALWAYS_INLINE const uint *qt_convertRGBA8888ToARGB32PM(uint *buffer, const uint *src, int count) { - for (int i = 0; i < count; ++i) - buffer[i] = qPremultiply(RGBA2ARGB(src[i])); + UNALIASED_CONVERSION_LOOP(buffer, src, count, [](uint s) { return qPremultiply(RGBA2ARGB(s));}); return buffer; } - const uint qt_bayer_matrix[16][16] = { { 0x1, 0xc0, 0x30, 0xf0, 0xc, 0xcc, 0x3c, 0xfc, 0x3, 0xc3, 0x33, 0xf3, 0xf, 0xcf, 0x3f, 0xff}, -- cgit v1.2.3