ubuntu-touch-coreapps-reviewers team mailing list archive
-
ubuntu-touch-coreapps-reviewers team
-
Mailing list archive
-
Message #09559
[Merge] lp:~qqworini/ubuntu-rssreader-app/fix-crash-rewrite-opml into lp:ubuntu-rssreader-app
Joey Chan has proposed merging lp:~qqworini/ubuntu-rssreader-app/fix-crash-rewrite-opml into lp:ubuntu-rssreader-app.
Commit message:
fix crash bug, rewrite opml parser, update xml2json with latest upstream. fix bug 1543995.
Requested reviews:
Ubuntu Shorts Developers (ubuntu-shorts-dev)
Related bugs:
Bug #1543736 in Ubuntu Shorts App: "[RSS Reader] crashes when refreshing and no pics shown"
https://bugs.launchpad.net/ubuntu-rssreader-app/+bug/1543736
Bug #1543995 in Ubuntu Shorts App: "Shorts converts & into & when reading RDF feeds"
https://bugs.launchpad.net/ubuntu-rssreader-app/+bug/1543995
Bug #1546443 in Ubuntu Shorts App: "Shorts App crashes when fetching updates if "Use Google Search" is disabled"
https://bugs.launchpad.net/ubuntu-rssreader-app/+bug/1546443
Bug #1562315 in Ubuntu Shorts App: "Typo in Shorts"
https://bugs.launchpad.net/ubuntu-rssreader-app/+bug/1562315
Bug #1565406 in Ubuntu Shorts App: "Adult feeds excluded on OPML import"
https://bugs.launchpad.net/ubuntu-rssreader-app/+bug/1565406
For more details, see:
https://code.launchpad.net/~qqworini/ubuntu-rssreader-app/fix-crash-rewrite-opml/+merge/297077
fix crash bug, rewrite opml parser, update xml2json with latest upstream. fix bug 1543995.
--
Your team Ubuntu Shorts Developers is requested to review the proposed merge of lp:~qqworini/ubuntu-rssreader-app/fix-crash-rewrite-opml into lp:ubuntu-rssreader-app.
=== modified file '.bzrignore'
--- .bzrignore 2015-07-17 14:42:42 +0000
+++ .bzrignore 2016-06-10 15:12:59 +0000
@@ -1,1 +1,2 @@
RE:(?i).*\.user
+*.afc62ac
=== modified file 'shorts/po/com.ubuntu.shorts.pot'
--- shorts/po/com.ubuntu.shorts.pot 2016-03-23 01:45:49 +0000
+++ shorts/po/com.ubuntu.shorts.pot 2016-06-10 15:12:59 +0000
@@ -8,7 +8,7 @@
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2016-03-11 20:54+0800\n"
+"POT-Creation-Date: 2016-06-10 14:54+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@xxxxxx>\n"
@@ -18,7 +18,7 @@
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
-#: ../qml/components/ArticleViewItem.qml:120
+#: ../qml/components/ArticleViewItem.qml:125
msgid "Select article"
msgstr ""
@@ -50,51 +50,51 @@
msgid "Confirm"
msgstr ""
-#: ../qml/content/ImportFeeds.qml:305
+#: ../qml/content/ImportFeeds.qml:313
msgid "Please select an opml file"
msgstr ""
-#: ../qml/content/ImportFeeds.qml:310
+#: ../qml/content/ImportFeeds.qml:318
msgid "Open"
msgstr ""
-#: ../qml/content/ImportFeeds.qml:293
+#: ../qml/content/ImportFeeds.qml:301
msgid ""
"Attention please, before importing opml file, Shorts only support one opml "
"structure. <br><br>"
msgstr ""
#: ../qml/nongoogle/AppendNGFeedPage.qml:30 ../qml/pages/AppendFeedPage.qml:31
-#: ../qml/shorts-app.qml:265 ../qml/shorts-app.qml:403
-#: ../qml/shorts-app.qml:411
+#: ../qml/shorts-app.qml:267 ../qml/shorts-app.qml:405
+#: ../qml/shorts-app.qml:413
msgid "Add feeds"
msgstr ""
-#: ../qml/nongoogle/AppendNGFeedPage.qml:94
+#: ../qml/nongoogle/AppendNGFeedPage.qml:99
#: ../qml/pages/AppendFeedPage.qml:137
msgid "Type a keyword or URL"
msgstr ""
-#: ../qml/nongoogle/AppendNGFeedPage.qml:149
+#: ../qml/nongoogle/AppendNGFeedPage.qml:154
msgid "Feed Title:"
msgstr ""
-#: ../qml/nongoogle/AppendNGFeedPage.qml:157
-#: ../qml/nongoogle/AppendNGFeedPage.qml:172
+#: ../qml/nongoogle/AppendNGFeedPage.qml:162
+#: ../qml/nongoogle/AppendNGFeedPage.qml:177
msgid "No data"
msgstr ""
-#: ../qml/nongoogle/AppendNGFeedPage.qml:164
+#: ../qml/nongoogle/AppendNGFeedPage.qml:169
msgid "Feed Description:"
msgstr ""
-#: ../qml/nongoogle/AppendNGFeedPage.qml:197
+#: ../qml/nongoogle/AppendNGFeedPage.qml:202
#: ../qml/pages/AppendFeedPage.qml:243 ../qml/pages/CreateTopicPage.qml:38
-#: ../qml/pages/TopicManagement.qml:239 ../qml/shorts-app.qml:494
+#: ../qml/pages/TopicManagement.qml:239 ../qml/shorts-app.qml:496
msgid "Cancel"
msgstr ""
-#: ../qml/nongoogle/AppendNGFeedPage.qml:230
+#: ../qml/nongoogle/AppendNGFeedPage.qml:235
#: ../qml/pages/AppendFeedPage.qml:276
msgid "Next"
msgstr ""
@@ -320,7 +320,7 @@
msgid "Click here to report a bug"
msgstr ""
-#: ../qml/pages/PageSettings.qml:9 ../qml/shorts-app.qml:249
+#: ../qml/pages/PageSettings.qml:9 ../qml/shorts-app.qml:251
msgid "Settings"
msgstr ""
@@ -352,7 +352,7 @@
msgid "Import OPML"
msgstr ""
-#: ../qml/pages/TopicManagement.qml:13 ../qml/shorts-app.qml:229
+#: ../qml/pages/TopicManagement.qml:13 ../qml/shorts-app.qml:231
msgid "Edit topics"
msgstr ""
@@ -360,47 +360,47 @@
msgid "Add Feed"
msgstr ""
-#: ../qml/shorts-app.qml:196
+#: ../qml/shorts-app.qml:198
msgid "Refresh"
msgstr ""
-#: ../qml/shorts-app.qml:203
+#: ../qml/shorts-app.qml:205
msgid "Grid View"
msgstr ""
-#: ../qml/shorts-app.qml:203
+#: ../qml/shorts-app.qml:205
msgid "List view"
msgstr ""
-#: ../qml/shorts-app.qml:239
+#: ../qml/shorts-app.qml:241
msgid "Disable night mode"
msgstr ""
-#: ../qml/shorts-app.qml:239
+#: ../qml/shorts-app.qml:241
msgid "Enable night mode"
msgstr ""
-#: ../qml/shorts-app.qml:334
+#: ../qml/shorts-app.qml:336
msgid "Saved"
msgstr ""
-#: ../qml/shorts-app.qml:342 shorts.desktop.in.in.h:1
+#: ../qml/shorts-app.qml:344 shorts.desktop.in.in.h:1
msgid "Shorts"
msgstr ""
-#: ../qml/shorts-app.qml:486
+#: ../qml/shorts-app.qml:488
msgid "Checking for new articles"
msgstr ""
-#: ../qml/shorts-app.qml:510
+#: ../qml/shorts-app.qml:512
msgid "Perhaps some of the channels have not been updated."
msgstr ""
-#: ../qml/shorts-app.qml:511
+#: ../qml/shorts-app.qml:513
msgid "Errors occurred during the update"
msgstr ""
-#: ../qml/shorts-app.qml:525
+#: ../qml/shorts-app.qml:527
msgid "Ok"
msgstr ""
=== modified file 'shorts/po/zh_CN.po'
--- shorts/po/zh_CN.po 2016-03-24 06:05:40 +0000
+++ shorts/po/zh_CN.po 2016-06-10 15:12:59 +0000
@@ -6,11 +6,17 @@
msgid ""
msgstr ""
"Project-Id-Version: ubuntu-rssreader-app\n"
+<<<<<<< TREE
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2016-03-11 20:54+0800\n"
+=======
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2016-06-10 14:03+0000\n"
+>>>>>>> MERGE-SOURCE
"PO-Revision-Date: 2015-06-29 01:52+0000\n"
"Last-Translator: Ian Li <Unknown>\n"
"Language-Team: Chinese (Simplified) <zh_CN@xxxxxx>\n"
+"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
@@ -18,7 +24,7 @@
"X-Launchpad-Export-Date: 2016-03-24 06:05+0000\n"
"X-Generator: Launchpad (build 17958)\n"
-#: ../qml/components/ArticleViewItem.qml:120
+#: ../qml/components/ArticleViewItem.qml:125
msgid "Select article"
msgstr "选择文章"
@@ -36,7 +42,7 @@
#: ../qml/components/ReadingOptions.qml:98
msgid "Normal"
-msgstr ""
+msgstr "普通"
#: ../qml/components/ReadingOptions.qml:99
msgid "Large"
@@ -44,57 +50,65 @@
#: ../qml/content/ImportFeeds.qml:30
msgid "Import Feeds"
-msgstr ""
+msgstr "导入信息源"
#: ../qml/content/ImportFeeds.qml:42 ../qml/pages/TopicManagement.qml:258
msgid "Confirm"
msgstr "确认"
-#: ../qml/content/ImportFeeds.qml:305
+#: ../qml/content/ImportFeeds.qml:313
msgid "Please select an opml file"
-msgstr ""
+msgstr "请选择一个opml文件"
-#: ../qml/content/ImportFeeds.qml:310
+#: ../qml/content/ImportFeeds.qml:318
msgid "Open"
-msgstr ""
+msgstr "打开"
-#: ../qml/content/ImportFeeds.qml:293
+#: ../qml/content/ImportFeeds.qml:301
msgid ""
"Attention please, before importing opml file, Shorts only support one opml "
"structure. <br><br>"
-msgstr ""
+msgstr "请注意,在导入opml文件之前,摘要阅读只支持一种opml分类结构。<br><br>"
#: ../qml/nongoogle/AppendNGFeedPage.qml:30 ../qml/pages/AppendFeedPage.qml:31
+<<<<<<< TREE
#: ../qml/shorts-app.qml:265 ../qml/shorts-app.qml:403
#: ../qml/shorts-app.qml:411
+=======
+#: ../qml/shorts-app.qml:267 ../qml/shorts-app.qml:405
+#: ../qml/shorts-app.qml:413
+>>>>>>> MERGE-SOURCE
msgid "Add feeds"
msgstr "添加信息源"
-#: ../qml/nongoogle/AppendNGFeedPage.qml:94
-#: ../qml/pages/AppendFeedPage.qml:137
+#: ../qml/nongoogle/AppendNGFeedPage.qml:99 ../qml/pages/AppendFeedPage.qml:137
msgid "Type a keyword or URL"
msgstr "请输入关键字或网址"
-#: ../qml/nongoogle/AppendNGFeedPage.qml:149
+#: ../qml/nongoogle/AppendNGFeedPage.qml:154
msgid "Feed Title:"
-msgstr ""
+msgstr "信息源标题"
-#: ../qml/nongoogle/AppendNGFeedPage.qml:157
-#: ../qml/nongoogle/AppendNGFeedPage.qml:172
+#: ../qml/nongoogle/AppendNGFeedPage.qml:162
+#: ../qml/nongoogle/AppendNGFeedPage.qml:177
msgid "No data"
-msgstr ""
+msgstr "没有数据"
-#: ../qml/nongoogle/AppendNGFeedPage.qml:164
+#: ../qml/nongoogle/AppendNGFeedPage.qml:169
msgid "Feed Description:"
-msgstr ""
+msgstr "信息源描述"
-#: ../qml/nongoogle/AppendNGFeedPage.qml:197
+#: ../qml/nongoogle/AppendNGFeedPage.qml:202
#: ../qml/pages/AppendFeedPage.qml:243 ../qml/pages/CreateTopicPage.qml:38
+<<<<<<< TREE
#: ../qml/pages/TopicManagement.qml:239 ../qml/shorts-app.qml:494
+=======
+#: ../qml/pages/TopicManagement.qml:239 ../qml/shorts-app.qml:496
+>>>>>>> MERGE-SOURCE
msgid "Cancel"
msgstr "取消"
-#: ../qml/nongoogle/AppendNGFeedPage.qml:230
+#: ../qml/nongoogle/AppendNGFeedPage.qml:235
#: ../qml/pages/AppendFeedPage.qml:276
msgid "Next"
msgstr "下一项"
@@ -137,7 +151,7 @@
#: ../qml/pages/ArticleViewPage.qml:76
msgid "Share..."
-msgstr ""
+msgstr "分享到"
#: ../qml/pages/ChooseTopicPage.qml:13
msgid "Choose topic"
@@ -200,6 +214,7 @@
msgid "Topic: "
msgstr "话题: "
+<<<<<<< TREE
#: ../qml/pages/PageHelp.qml:15
msgid "Welcome to Shorts"
msgstr ""
@@ -321,31 +336,175 @@
msgstr ""
#: ../qml/pages/PageSettings.qml:9 ../qml/shorts-app.qml:249
+=======
+#: ../qml/pages/PageHelp.qml:15
+msgid "Welcome to Shorts"
+msgstr "欢迎使用摘要阅读"
+
+#: ../qml/pages/PageHelp.qml:17
+msgid "View Articles"
+msgstr "查看文章"
+
+#: ../qml/pages/PageHelp.qml:19
+msgid "Search RSS"
+msgstr "搜索RSS"
+
+#: ../qml/pages/PageHelp.qml:21
+msgid "Manage topics and feeds"
+msgstr "管理话题和信息源"
+
+#: ../qml/pages/PageHelp.qml:23
+msgid "Location"
+msgstr "位置"
+
+#: ../qml/pages/PageHelp.qml:25
+msgid "Enjoy"
+msgstr "请享用"
+
+#: ../qml/pages/PageHelp.qml:105
+msgid "Shorts is an RSS reader app developed by Canonical & Ubuntu Comunity."
+msgstr "摘要阅读是一个由Canonical公司及Ubuntu社区共同开发的RSS阅读软件。"
+
+#: ../qml/pages/PageHelp.qml:110
+msgid ""
+"Shorts provides most primary features which also can be seen in other RSS "
+"reader apps, with a beautiful Ubuntu style user interface."
+msgstr "摘要阅读提供了大多数其它RSS软件都有的功能,以及带有Ubuntu特色的用户界面"
+
+#: ../qml/pages/PageHelp.qml:117
+msgid "Swipe left to continue."
+msgstr "向左滑动以继续"
+
+#: ../qml/pages/PageHelp.qml:136
+msgid "Scroll left/right in grid mode or scroll up/down in list mode."
+msgstr "在方格模式左右滑动或者在列表模式上下滑动。"
+
+#: ../qml/pages/PageHelp.qml:163
+msgid ""
+"Swipe up will enter this search page, just type any simple words, or paste a "
+"link."
+msgstr "向上滑动可进入搜索页,输入关键字或直接粘帖链接即可。"
+
+#: ../qml/pages/PageHelp.qml:190
+msgid ""
+"Every feed must belong to one topic, swipe one item to delete feed/topic."
+msgstr "每一个信息源都必须有其所属的话题。通过向右滑动每一个项目可以删除信息源或话题。"
+
+#: ../qml/pages/PageHelp.qml:223
+msgid "Seems you are living in: "
+msgstr "貌似您住在:"
+
+#: ../qml/pages/PageHelp.qml:232
+msgid ""
+"Some regions/countries(e.g. China) may block Google service, if you're "
+"living there or plan to go there, we suggest to trun off the build-in Google "
+"RSS service in settings page, or nothing you should care."
+msgstr "因为谷歌已被某朝和谐,摘要阅读自带的谷歌RSS引擎将默认关闭,除非您已自备了梯子。"
+
+#: ../qml/pages/PageHelp.qml:239
+msgid ""
+"We detect that you're living in some region/country(e.g. China) which blocks "
+"Google service, so Shorts disable the build-in Google RSS service by "
+"default. You still can turn it on here or in settings page."
+msgstr "因为谷歌已被某朝和谐,摘要阅读自带的谷歌RSS引擎将默认关闭,除非您已自备了梯子。此选项可在设置页重新选择。"
+
+#: ../qml/pages/PageHelp.qml:250
+msgid "Use Google RSS engine: "
+msgstr "使用谷歌RSS引擎"
+
+#: ../qml/pages/PageHelp.qml:287
+msgid "Enjoy !"
+msgstr "请享用!"
+
+#: ../qml/pages/PageHelp.qml:297
+msgid "Do not show this help page again "
+msgstr "不再显示此帮助页。"
+
+#: ../qml/pages/PageHelp.qml:316
+msgid "Start using Shorts !"
+msgstr "开始使用摘要阅读"
+
+#: ../qml/pages/PageHelp.qml:340
+msgid "Skip"
+msgstr "跳过"
+
+#: ../qml/pages/PageInfo.qml:10
+msgid "About Shorts"
+msgstr "摘要阅读"
+
+#: ../qml/pages/PageInfo.qml:39
+msgid "Author"
+msgstr "作者"
+
+#: ../qml/pages/PageInfo.qml:53
+msgid "Copyleft"
+msgstr "版权"
+
+#: ../qml/pages/PageInfo.qml:57
+msgid "License with GPLv3"
+msgstr "使用GPLv3协议授权"
+
+#: ../qml/pages/PageInfo.qml:62
+msgid ""
+"Ubuntu Shorts App is the official rss reader app for Ubuntu Touch. We follow "
+"an open source model where the code is available to anyone to branch and "
+"hack on. The ubuntu shorts app follows a test driven development (TDD) where "
+"tests are written in parallel to feature implementation to help spot "
+"regressions easier."
+msgstr ""
+
+#: ../qml/pages/PageInfo.qml:68
+msgid "Click here to report a bug"
+msgstr "点击此处报告错误"
+
+#: ../qml/pages/PageSettings.qml:9 ../qml/shorts-app.qml:251
+>>>>>>> MERGE-SOURCE
msgid "Settings"
-msgstr ""
-
-#: ../qml/pages/PageSettings.qml:15
-msgid "Help"
-msgstr ""
-
-#: ../qml/pages/PageSettings.qml:20
-msgid "Info"
-msgstr ""
-
-#: ../qml/pages/PageSettings.qml:51
+<<<<<<< TREE
+msgstr ""
+
+#: ../qml/pages/PageSettings.qml:15
+msgid "Help"
+msgstr ""
+
+#: ../qml/pages/PageSettings.qml:20
+msgid "Info"
+msgstr ""
+
+#: ../qml/pages/PageSettings.qml:51
+=======
+msgstr "设置"
+
+#: ../qml/pages/PageSettings.qml:15
+msgid "Help"
+msgstr "帮助"
+
+#: ../qml/pages/PageSettings.qml:20
+msgid "Info"
+msgstr "说明"
+
+#: ../qml/pages/PageSettings.qml:51
+>>>>>>> MERGE-SOURCE
msgid ""
+<<<<<<< TREE
"Some special regions/countries block Google service, we suggest to turn off "
"the switch below if living in there."
msgstr ""
+=======
+"Some special regions/countries block Google service, we suggest to turn off "
+"the switch below if living in there."
+msgstr "因为谷歌已被某朝和谐,摘要阅读自带的谷歌RSS引擎将默认关闭,除非您已自备了梯子。"
+>>>>>>> MERGE-SOURCE
#: ../qml/pages/PageSettings.qml:59
msgid "Use Google Search: "
-msgstr ""
+msgstr "使用谷歌RSS引擎"
#: ../qml/pages/PageSettings.qml:84
msgid ""
"For those users, who want to import their RSS feeds from other sources, "
"please press the button below."
+<<<<<<< TREE
msgstr ""
#: ../qml/pages/PageSettings.qml:94
@@ -353,6 +512,15 @@
msgstr ""
#: ../qml/pages/TopicManagement.qml:13 ../qml/shorts-app.qml:229
+=======
+msgstr "需要导入自己RSS源的用户请点击下面的按钮。"
+
+#: ../qml/pages/PageSettings.qml:94
+msgid "Import OPML"
+msgstr "导入opml"
+
+#: ../qml/pages/TopicManagement.qml:13 ../qml/shorts-app.qml:231
+>>>>>>> MERGE-SOURCE
msgid "Edit topics"
msgstr "编辑话题"
@@ -360,47 +528,91 @@
msgid "Add Feed"
msgstr "添加信息源"
+<<<<<<< TREE
#: ../qml/shorts-app.qml:196
+=======
+#: ../qml/shorts-app.qml:198
+>>>>>>> MERGE-SOURCE
msgid "Refresh"
msgstr "刷新"
+<<<<<<< TREE
#: ../qml/shorts-app.qml:203
+=======
+#: ../qml/shorts-app.qml:205
+>>>>>>> MERGE-SOURCE
msgid "Grid View"
msgstr "网格视图"
+<<<<<<< TREE
#: ../qml/shorts-app.qml:203
+=======
+#: ../qml/shorts-app.qml:205
+>>>>>>> MERGE-SOURCE
msgid "List view"
msgstr "列表视图"
+<<<<<<< TREE
#: ../qml/shorts-app.qml:239
+=======
+#: ../qml/shorts-app.qml:241
+>>>>>>> MERGE-SOURCE
msgid "Disable night mode"
-msgstr ""
+msgstr "关闭夜间模式"
+<<<<<<< TREE
#: ../qml/shorts-app.qml:239
+=======
+#: ../qml/shorts-app.qml:241
+>>>>>>> MERGE-SOURCE
msgid "Enable night mode"
-msgstr ""
+msgstr "打开夜间模式"
+<<<<<<< TREE
#: ../qml/shorts-app.qml:334
+=======
+#: ../qml/shorts-app.qml:336
+>>>>>>> MERGE-SOURCE
msgid "Saved"
msgstr "已保存"
+<<<<<<< TREE
#: ../qml/shorts-app.qml:342 shorts.desktop.in.in.h:1
+=======
+#: ../qml/shorts-app.qml:344 shorts.desktop.in.in.h:1
+>>>>>>> MERGE-SOURCE
msgid "Shorts"
msgstr "摘要阅读"
+<<<<<<< TREE
#: ../qml/shorts-app.qml:486
+=======
+#: ../qml/shorts-app.qml:488
+>>>>>>> MERGE-SOURCE
msgid "Checking for new articles"
msgstr "正在查询新的文章"
+<<<<<<< TREE
#: ../qml/shorts-app.qml:510
+=======
+#: ../qml/shorts-app.qml:512
+>>>>>>> MERGE-SOURCE
msgid "Perhaps some of the channels have not been updated."
msgstr "部分频道内容可能未更新。"
+<<<<<<< TREE
#: ../qml/shorts-app.qml:511
+=======
+#: ../qml/shorts-app.qml:513
+>>>>>>> MERGE-SOURCE
msgid "Errors occurred during the update"
msgstr "更新过程中出错"
+<<<<<<< TREE
#: ../qml/shorts-app.qml:525
+=======
+#: ../qml/shorts-app.qml:527
+>>>>>>> MERGE-SOURCE
msgid "Ok"
msgstr "确认"
@@ -456,3 +668,38 @@
#: shorts.desktop.in.in.h:2
msgid "shorts;rss;reader;"
msgstr "简讯;rss;聚合;阅读;"
+
+#~ msgid ""
+#~ "For those who living in some special regions cannot access Google, the "
+#~ "switch below can disable Google RSS engine, Shorts will directly gets "
+#~ "data from RSS sources."
+#~ msgstr "因谷歌被某朝和谐,谷歌RSS引擎将默认关闭,除非您已备好梯子。"
+
+#~ msgid "+ Add feeds"
+#~ msgstr "+ 添加信息源"
+
+#~ msgid "Add online accounts"
+#~ msgstr "添加在线账号"
+
+#~ msgid "Online accounts are not available for now"
+#~ msgstr "在线账户现在不可用"
+
+#~ msgid "We are sorry"
+#~ msgstr "很抱歉"
+
+#~ msgid "Import subscriptions"
+#~ msgstr "导入订阅信息"
+
+#~ msgid "Importing subscriptions is not available for now"
+#~ msgstr "“导入订阅”现在不可用"
+
+#~ msgid ""
+#~ "Shorts detects that you're located in an area which blocks Google's IP."
+#~ "<br><br>"
+#~ msgstr "摘要阅读发现您所在的地方无法访问谷歌服务。"
+
+#~ msgid "Yes, please."
+#~ msgstr "好的。"
+
+#~ msgid "No, thanks."
+#~ msgstr "不用了。"
=== modified file 'shorts/qml/components/ArticleViewItem.qml'
--- shorts/qml/components/ArticleViewItem.qml 2016-01-16 10:55:38 +0000
+++ shorts/qml/components/ArticleViewItem.qml 2016-06-10 15:12:59 +0000
@@ -59,6 +59,11 @@
}
}
+ function escape2Html(str) {
+ var arrEntities={'lt':'<','gt':'>','nbsp':' ','amp':'&','quot':'"'};
+ return str.replace(/&(lt|gt|nbsp|amp|quot);/ig,function(all,t){return arrEntities[t];});
+ }
+
////////////////////////////////////////////// a listview to show the RSS content
ListView {
@@ -195,7 +200,7 @@
}
Label {
- text: model.content
+ text: escape2Html(model.content)
fontSize: {
switch(rssListview.contentFontSize) {
=== modified file 'shorts/qml/content/ContentPickerDialog.qml'
--- shorts/qml/content/ContentPickerDialog.qml 2016-01-10 09:04:00 +0000
+++ shorts/qml/content/ContentPickerDialog.qml 2016-06-10 15:12:59 +0000
@@ -29,6 +29,10 @@
id: picker
visible: false
+ header: PageHeader {
+ visible: false
+ }
+
property var pickerParent
ContentPeerPicker {
=== modified file 'shorts/qml/content/ImportFeeds.qml'
--- shorts/qml/content/ImportFeeds.qml 2016-01-14 15:35:31 +0000
+++ shorts/qml/content/ImportFeeds.qml 2016-06-10 15:12:59 +0000
@@ -45,7 +45,9 @@
DB.importOPMLobject(repeaterFeedList.opmlList)
mainView.reloadViews()
pageStack.pop()
+ pageStack.pop()
mainView.refresh()
+// print ("repeaterFeedList.opmlList: ", JSON.stringify(repeaterFeedList.opmlList[0]))
}
}
@@ -147,6 +149,7 @@
ListItem.Empty {
id: liStandardRoot
+ height: units.gu(6.5)
property var opmlRoot: repeaterFeedList.opmlList == undefined ?
undefined : repeaterFeedList.opmlList[index]
@@ -172,6 +175,7 @@
CheckBox {
id: checkboxRoot
+ anchors.verticalCenter: parent.verticalCenter
}
Label{
@@ -217,7 +221,8 @@
Repeater {
id: repeaterFeedsChildren
model: liStandardRoot.opmlRoot == undefined ?
- "" : liStandardRoot.opmlRoot.children
+// "" : liStandardRoot.opmlRoot.children
+ "" : liStandardRoot.opmlRoot.outline
ListItem.Standard {
id: listitemFeed
@@ -227,7 +232,10 @@
// height: visible ? units.gu(6) : 0
property var opmlFeed: liStandardRoot.opmlRoot == undefined ?
- undefined : liStandardRoot.opmlRoot.children[index]
+ undefined : liStandardRoot.opmlRoot.outline[index]
+// undefined : liStandardRoot.opmlRoot.children[index]
+// undefined : (liStandardRoot.opmlRoot.outline[index] == undefined ?
+// liStandardRoot.opmlRoot.outline : liStandardRoot.opmlRoot.outline[index])
Connections {
target: liStandardRoot
=== modified file 'shorts/qml/content/OpmlParser.qml'
--- shorts/qml/content/OpmlParser.qml 2016-01-10 09:04:00 +0000
+++ shorts/qml/content/OpmlParser.qml 2016-06-10 15:12:59 +0000
@@ -21,16 +21,39 @@
import QtQuick.XmlListModel 2.0
-import "../utils/databasemodule_v2.js" as DB
-//import "../."
Item {
id: opmlParser
property string opmlPath: ""
+ onOpmlPathChanged: {
+ utilities.loadOpml(opmlPath)
+ }
signal parseFinished(var opml)
+ Connections {
+ target: utilities
+
+ onSigOpmlJsom: {
+// print("opml json: ", JSON.stringify(result))
+// print("opml json: ", /*result.opml.body.outline[0].outline[0].xmlUrl,*/ JSON.stringify(result.opml.body.outline[0].outline))
+
+ // if only one feed in a folder, there's no JS array in it, only one obj, so I add an array in a folder, make the structure compatible
+ for (var i=0; i<result.opml.body.outline.length; i++) {
+ var outLine = result.opml.body.outline[i]
+ if (result.opml.body.outline[i].outline[0] == undefined) {
+ var tmpobj = result.opml.body.outline[i].outline
+ var tmparr = new Array
+ tmparr.push(tmpobj)
+ result.opml.body.outline[i].outline = tmparr
+ }
+ }
+
+ parseFinished(result.opml.body.outline)
+ }
+ }
+
XmlListModel {
id: modelOpml
property string subTitle
@@ -91,23 +114,23 @@
}
onStatusChanged: {
- console.log("model status:", status)
- if (status == XmlListModel.Ready) {
- if (!subTitle) {
- allRootsCount = count
- // get all root object
- getAllRootObj()
- }
- else {
- // load children object one by one
-// console.log("children objs: ", JSON.stringify(opmlObj))
-// console.log("model count: ", count)
- getChildObjsFromOneRoot()
- }
- }
- else if (status == XmlListModel.Error) {
- console.log("XmlListModel.Error: ", errorString())
- }
+// console.log("model status:", status)
+// if (status == XmlListModel.Ready) {
+// if (!subTitle) {
+// allRootsCount = count
+// // get all root object
+// getAllRootObj()
+// }
+// else {
+// // load children object one by one
+//// console.log("children objs: ", JSON.stringify(opmlObj))
+//// console.log("model count: ", count)
+// getChildObjsFromOneRoot()
+// }
+// }
+// else if (status == XmlListModel.Error) {
+// console.log("XmlListModel.Error: ", errorString())
+// }
}
query: subTitle ? "/opml/body/outline[@text='" + subTitle + "']/outline": "/opml/body/outline"
=== modified file 'shorts/qml/nongoogle/AppendNGFeedPage.qml'
--- shorts/qml/nongoogle/AppendNGFeedPage.qml 2016-01-04 11:13:30 +0000
+++ shorts/qml/nongoogle/AppendNGFeedPage.qml 2016-06-10 15:12:59 +0000
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013, 2014
+ * Copyright (C) 2013, 2014, 2015, 2016
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -24,9 +24,9 @@
import "../."
Page {
- id: appendFeedPage
+ id: appendFeedPageNG
- objectName: "appendfeedpage"
+ objectName: "appendFeedPageNG"
title: i18n.tr("Add feeds")
flickable: null
visible: false
@@ -58,15 +58,20 @@
id: xmlFeedApi
onLoadResult: {
+ if (mainStack.currentPage != appendFeedPageNG) { return }
+
if (!result.rss) {
print("onLoadResult failed")
}
else {
- var f = result.rss.channel
+ var e = result.rss.channel
- feedDesc = f.description ? (f.description["#text"] ? f.description["#text"] : f.description ) : ""
- feedTitle = f.title ? (f.title["#text"] ? f.title["#text"] : f.title ) : ""
- feedLink = f.link ? (f.link["#text"] ? f.link["#text"] : f.link) : ""
+ try {
+ feedDesc = e.description == undefined ? "" : e.description["#text"] == undefined ? e.description : e.description["#text"]
+ feedTitle = e.title == undefined ? "" : e.title["#text"] == undefined ? e.title : e.title["#text"]
+ feedLink = e.link == undefined ? "" : e.link["#text"] == undefined ? e.link : e.link["#text"]
+ }
+ catch (err) { ; }
feedObj = {
"url" : feedUrl,
"title" : feedTitle,
@@ -129,8 +134,8 @@
var isUrlEntered = (userInput.indexOf(" ") === -1 && userInput.indexOf(".") !== -1)
if (isUrlEntered) {
- if (userInput.indexOf("http://") !== 0)
- {userInput = "http://" + userInput}
+// if (userInput.indexOf("http://") !== 0)
+// {userInput = "http://" + userInput}
feedUrl = userInput
xmlFeedApi.loadFeed(userInput)
}
=== modified file 'shorts/qml/nongoogle/XmlNetwork.qml'
--- shorts/qml/nongoogle/XmlNetwork.qml 2016-03-11 13:04:57 +0000
+++ shorts/qml/nongoogle/XmlNetwork.qml 2016-06-10 15:12:59 +0000
@@ -1,53 +1,66 @@
import QtQuick 2.4
-import "../utils/xml2json.js" as XJ
QtObject {
id: rootObject
- property bool inProgress: __doc != null
+// property bool inProgress: __doc != null
+ property bool inProgress: false
signal findResult(var result)
signal loadResult(var result)
property var __doc: null
+ property variant connObj: Connections {
+ id: connUtilities
+ target: utilities
+
+ onSigResult: {
+ inProgress = false
+ loadResult(result)
+ }
+ }
+
/* Load feed by URL.
*/
- function loadFeed(feedUrl, num) {
- abort(true)
-
- if (num)
- num = Math.min(num, 100)
- else num = 50
-
- __doc = new XMLHttpRequest()
- var doc = __doc
-
- doc.onreadystatechange = function() {
-
-// print("xmlnetwork onreadystatechange: ", doc.readyState, doc.status, feedUrl)
- if (doc.readyState === XMLHttpRequest.DONE) {
-
- var resObj
-// print ("status & text: ", doc.status, doc.statusText, doc.responseText)
- if (doc.status == 200) {
- resObj = utilities.xmlToJson(doc.responseText)
-// resObj = utilities.xmlToJson(doc.responseText)
-// var x2js = new XJ.X2JS();
-// resObj = XJ.xmlToJSON.parseString(doc.responseText) //x2js.xml_str2json( doc.responseText );
- } else { // Error
- resObj = {"responseDetails" : doc.statusText,
- "responseStatus" : doc.status}
- }
-
- print ("resObj: ", resObj.rss )
- __doc = null
- loadResult(resObj)
- }
- }
-
- doc.open("GET", feedUrl, true);
- doc.send();
+ function loadFeed(feedUrl/*, num*/) {
+// connUtilities.target = utilities
+// abort(true)
+ inProgress = true
+ utilities.loadFeed(feedUrl)
+
+// if (num)
+// num = Math.min(num, 100)
+// else num = 50
+
+// __doc = new XMLHttpRequest()
+// var doc = __doc
+
+// doc.onreadystatechange = function() {
+
+//// print("xmlnetwork onreadystatechange: ", doc.readyState, doc.status, feedUrl)
+// if (doc.readyState === XMLHttpRequest.DONE) {
+
+// var resObj
+//// print ("status & text: ", doc.status, doc.statusText, doc.responseText)
+// if (doc.status == 200) {
+// resObj = utilities.xmlToJson(doc.responseText)
+//// resObj = utilities.xmlToJson(doc.responseText)
+//// var x2js = new XJ.X2JS();
+//// resObj = XJ.xmlToJSON.parseString(doc.responseText) //x2js.xml_str2json( doc.responseText );
+// } else { // Error
+// resObj = {"responseDetails" : doc.statusText,
+// "responseStatus" : doc.status}
+// }
+
+// print ("resObj: ", resObj.rss )
+// __doc = null
+// loadResult(resObj)
+// }
+// }
+
+// doc.open("GET", feedUrl, true);
+// doc.send();
}
/* Param "isAbortOnly" used to preserve
@@ -56,11 +69,13 @@
* additional recalculations.
*/
function abort(isAbortOnly) {
- if (__doc != null) {
- __doc.abort()
- if (!isAbortOnly)
- __doc = null
- }
+// if (__doc != null) {
+// __doc.abort()
+// if (!isAbortOnly)
+// __doc = null
+// }
+
+ utilities.abort()
}
/* Return true if some kind of errors detected.
=== modified file 'shorts/qml/shorts-app.qml'
--- shorts/qml/shorts-app.qml 2016-03-04 13:27:57 +0000
+++ shorts/qml/shorts-app.qml 2016-06-10 15:12:59 +0000
@@ -30,6 +30,8 @@
backgroundColor: "#F5F5F5"
+ property alias mainStack: pageStack
+
Component.onCompleted: {
// show help page in first use
if (optionsKeeper.needPopup) {
=== modified file 'shorts/qml/utils/databasemodule_v2.js'
--- shorts/qml/utils/databasemodule_v2.js 2016-03-04 13:27:57 +0000
+++ shorts/qml/utils/databasemodule_v2.js 2016-06-10 15:12:59 +0000
@@ -703,20 +703,20 @@
// topic(tag) end, tagID is the unique id of a topic
// insert feed start
- for (var j=0; j<opml[i].children.length; j++) {
- if (opml[i].children[j].isSelected) {
+ for (var j=0; j<opml[i].outline.length; j++) {
+ if (opml[i].outline[j].isSelected) {
/* Check uniqueness.
*/
var feedID = -1
- var feedResult = tx.executeSql("SELECT * FROM feed WHERE source=?", [opml[i].children[j].xmlUrl])
+ var feedResult = tx.executeSql("SELECT * FROM feed WHERE source=?", [opml[i].outline[j].xmlUrl])
if (feedResult.rows.length > 0) {
- console.log("Database, importOPMLobject: already exist feed with source: ", opml[i].children[j].xmlUrl, "ID", feedResult.rows.item(0).id)
+ console.log("Database, importOPMLobject: already exist feed with source: ", opml[i].outline[j].xmlUrl, "ID", feedResult.rows.item(0).id)
feedID = feedResult.rows.item(0).id
}
else {
// insert feed
feedResult = tx.executeSql('INSERT INTO feed (title, source) VALUES(?, ?)',
- [opml[i].children[j].title , opml[i].children[j].xmlUrl])
+ [opml[i].outline[j].title , opml[i].outline[j].xmlUrl])
feedID = feedResult.insertId
console.log("Database, importOPMLobject: feed INSERT ID: ", feedID)
}
@@ -735,6 +735,8 @@
}
}
}
+
+
}
else {
continue
=== modified file 'shorts/utilities.cpp'
--- shorts/utilities.cpp 2016-03-11 13:04:57 +0000
+++ shorts/utilities.cpp 2016-06-10 15:12:59 +0000
@@ -4,16 +4,22 @@
Utilities::Utilities(QObject *parent) :
QObject(parent)
{
-// qDebug() << "path: " << QDir::currentPath() ;
-// QFile xmlFile("xml");
-// xmlFile.open(QIODevice::ReadOnly | QIODevice::Text);
-
-// qDebug() << "json: " << xmlToJson(QString(xmlFile.readAll())).size();
+ _internalMgr = new QNetworkAccessManager(this);
+ _loadTimer = new QTimer(this);
+ _loadTimer->setSingleShot(true);
}
-QJsonObject Utilities::xmlToJson(const QString &xml)
+QJsonObject Utilities::xmlToJson(const QByteArray &xml)
{
- QByteArray ba = xml.toLocal8Bit();
+ // check if the input doc is an xml doc
+ QString xmldoc = QString(xml);
+ if (xmldoc.indexOf("<?xml") != 0)
+ {
+ return QJsonDocument::fromJson(QString('{"error":4, "errorString":"not xml doc"}').toLocal8Bit()).object();
+ }
+
+// QByteArray ba = xml.toLocal8Bit();
+ QByteArray ba = xml;
char* ch = ba.data();
std::string json = "";
try
@@ -30,6 +36,62 @@
return QJsonDocument::fromJson(QString::fromStdString(json).toLocal8Bit()).object();
}
+void Utilities::loadFeed(const QString &feedUrl)
+{
+ _loadTimer->stop();
+ connect(_loadTimer, SIGNAL(timeout()), this, SLOT(_internalReplySlot()));
+ _loadTimer->start(20000);
+ _internalReply = this->_internalMgr->get(QNetworkRequest(QUrl(feedUrl)));
+ connect(_internalReply, SIGNAL(finished()), this, SLOT(_internalReplySlot()));
+ connect(_internalReply, SIGNAL(sslErrors(QList<QSslError>)),
+ this, SLOT(slotSslErrors(QList<QSslError>)));
+}
+
+void Utilities::abort()
+{
+// qDebug() << "abort0 " ;
+ _loadTimer->stop();
+ if (this->_internalReply)
+ {
+// qDebug() << "abort1 " ;
+ if (this->_internalReply->isRunning())
+ {
+ this->_internalReply->abort();
+// qDebug() << "abort2 " ;
+ }
+ }
+}
+
+void Utilities::_internalReplySlot()
+{
+ abort() ;
+ if (_internalReply->error() != QNetworkReply::NoError)
+ {
+ QString errString = '{"responseDetails": "pls check error in QNetworkReply::NetworkError","responseStatus":'
+ + QString::number(_internalReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt()) + '}';
+ emit QJsonDocument::fromJson(errString.toLocal8Bit());
+ return ;
+ }
+ qDebug() << "HTTP status code: " << _internalReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() ;
+// qDebug() << "redirect url? " << _internalReply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
+ if (_internalReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 301)
+ {
+ loadFeed(_internalReply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl().toString());
+ return ;
+ }
+// qDebug() << "feed size: " << _internalReply->size() ;
+// qDebug() << "feed detail: " << _internalReply->readAll() ;
+ QByteArray data = _internalReply->readAll();
+// emit sigResult(xmlToJson(QString(data)));
+ emit sigResult(xmlToJson(data));
+// qDebug() << "signal emitted. " ;
+}
+
+void Utilities::slotSslErrors(QList<QSslError>)
+{
+ this->_internalReply->ignoreSslErrors();
+}
+
QStringList Utilities::htmlGetImg(const QString &html)
{
QRegExp imgTagRegex("\\<img[^\\>]*src\\s*=\\s*\"([^\"]*)\"[^\\>]*\\>", Qt::CaseInsensitive);
@@ -58,11 +120,23 @@
settings.setValue(key, value);
}
-// QJsonObject Utilities::test()
-// {
-// QFile xmlFile("xml");
-// xmlFile.open(QIODevice::ReadOnly | QIODevice::Text);
-//
-// QJsonObject obj = xmlToJson(QString(xmlFile.readAll()));
-// return obj;
-// }
+void Utilities::loadOpml(const QString &opmlUrl)
+{
+ QString tmpUrl = opmlUrl ;
+ tmpUrl = tmpUrl.replace("file://", "");
+ QFile *file=new QFile(tmpUrl);
+ bool isOpen = file->open(QIODevice::ReadOnly|QIODevice::Text);
+ if (!isOpen)
+ {
+ qDebug() << "error opening opml file ........" ;
+ return ;
+ }
+
+ // get all data & convert to json
+ QByteArray opmldata = file->readAll();
+ QString opmlJsonString = QString(QJsonDocument(xmlToJson(opmldata)).toJson());
+ opmlJsonString = opmlJsonString.replace("@", "");
+// qDebug() << "after replace @: " << opmlJsonString ;
+ //QJsonDocument::fromJson(QString::fromStdString(json).toLocal8Bit()).object();
+ emit sigOpmlJsom(QJsonDocument::fromJson(opmlJsonString.toLocal8Bit()).object());
+}
=== modified file 'shorts/utilities.h'
--- shorts/utilities.h 2016-03-04 13:27:57 +0000
+++ shorts/utilities.h 2016-06-10 15:12:59 +0000
@@ -2,6 +2,7 @@
#define UTILITIES_H
#include <QtCore>
+#include <QtNetwork>
class Utilities : public QObject
{
@@ -9,20 +10,38 @@
public:
explicit Utilities(QObject *parent = 0);
- Q_INVOKABLE QJsonObject xmlToJson(const QString &xml);
+ Q_INVOKABLE QJsonObject xmlToJson(const QByteArray &xml);
+ // load feed for non-google usage
+ Q_INVOKABLE void loadFeed(const QString &feedUrl);
+ Q_INVOKABLE void abort();
+
+ //
Q_INVOKABLE QStringList htmlGetImg(const QString &html);
// get & set settings' values
Q_INVOKABLE QString getSetting(const QString &key);
Q_INVOKABLE void setSetting(const QString &key, const QString &value);
+ // load opml file from local
+ Q_INVOKABLE void loadOpml(const QString &opmlUrl);
+
// test only
// Q_INVOKABLE QJsonObject test();
signals:
+ void sigResult(QJsonObject result);
+ void sigOpmlJsom(QJsonObject result);
public slots:
+private:
+ QNetworkAccessManager *_internalMgr;
+ QNetworkReply *_internalReply;
+ QTimer *_loadTimer;
+
+private slots:
+ void _internalReplySlot();
+ void slotSslErrors(QList<QSslError>);
};
#endif // UTILITIES_H
=== modified file 'shorts/xml2json/rapidjson/allocators.h'
--- shorts/xml2json/rapidjson/allocators.h 2016-03-04 13:27:57 +0000
+++ shorts/xml2json/rapidjson/allocators.h 2016-06-10 15:12:59 +0000
@@ -194,6 +194,9 @@
if (newSize == 0)
return NULL;
+ originalSize = RAPIDJSON_ALIGN(originalSize);
+ newSize = RAPIDJSON_ALIGN(newSize);
+
// Do not shrink if new size is smaller than original
if (originalSize >= newSize)
return originalPtr;
@@ -201,7 +204,6 @@
// Simply expand it if it is the last allocation and there is sufficient space
if (originalPtr == reinterpret_cast<char *>(chunkHead_) + RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + chunkHead_->size - originalSize) {
size_t increment = static_cast<size_t>(newSize - originalSize);
- increment = RAPIDJSON_ALIGN(increment);
if (chunkHead_->size + increment <= chunkHead_->capacity) {
chunkHead_->size += increment;
return originalPtr;
=== modified file 'shorts/xml2json/rapidjson/document.h'
--- shorts/xml2json/rapidjson/document.h 2016-03-04 13:27:57 +0000
+++ shorts/xml2json/rapidjson/document.h 2016-06-10 15:12:59 +0000
@@ -20,17 +20,21 @@
#include "reader.h"
#include "internal/meta.h"
#include "internal/strfunc.h"
+#include "memorystream.h"
+#include "encodedstream.h"
#include <new> // placement new
#ifdef _MSC_VER
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant
+RAPIDJSON_DIAG_OFF(4244) // conversion from kXxxFlags to 'uint16_t', possible loss of data
#endif
#ifdef __clang__
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(padded)
RAPIDJSON_DIAG_OFF(switch-enum)
+RAPIDJSON_DIAG_OFF(c++98-compat)
#endif
#ifdef __GNUC__
@@ -141,6 +145,7 @@
Otherwise, the copy constructor is implicitly defined.
*/
GenericMemberIterator(const NonConstIterator & it) : ptr_(it.ptr_) {}
+ Iterator& operator=(const NonConstIterator & it) { ptr_ = it.ptr_; return *this; }
//! @name stepping
//@{
@@ -293,7 +298,7 @@
*/
#endif
explicit GenericStringRef(const CharType* str)
- : s(str), length(internal::StrLen(str)){ RAPIDJSON_ASSERT(s != NULL); }
+ : s(str), length(internal::StrLen(str)){ RAPIDJSON_ASSERT(s != 0); }
//! Create constant string reference from pointer and length
#ifndef __clang__ // -Wdocumentation
@@ -305,7 +310,11 @@
*/
#endif
GenericStringRef(const CharType* str, SizeType len)
- : s(str), length(len) { RAPIDJSON_ASSERT(s != NULL); }
+ : s(str), length(len) { RAPIDJSON_ASSERT(s != 0); }
+
+ GenericStringRef(const GenericStringRef& rhs) : s(rhs.s), length(rhs.length) {}
+
+ GenericStringRef& operator=(const GenericStringRef& rhs) { s = rhs.s; length = rhs.length; }
//! implicit conversion to plain CharType pointer
operator const Ch *() const { return s; }
@@ -314,8 +323,6 @@
const SizeType length; //!< length of the string (excluding the trailing NULL terminator)
private:
- //! Disallow copy-assignment
- GenericStringRef operator=(const GenericStringRef&);
//! Disallow construction from non-const array
template<SizeType N>
GenericStringRef(CharType (&str)[N]) /* = delete */;
@@ -394,6 +401,127 @@
} // namespace internal
///////////////////////////////////////////////////////////////////////////////
+// TypeHelper
+
+namespace internal {
+
+template <typename ValueType, typename T>
+struct TypeHelper {};
+
+template<typename ValueType>
+struct TypeHelper<ValueType, bool> {
+ static bool Is(const ValueType& v) { return v.IsBool(); }
+ static bool Get(const ValueType& v) { return v.GetBool(); }
+ static ValueType& Set(ValueType& v, bool data) { return v.SetBool(data); }
+ static ValueType& Set(ValueType& v, bool data, typename ValueType::AllocatorType&) { return v.SetBool(data); }
+};
+
+template<typename ValueType>
+struct TypeHelper<ValueType, int> {
+ static bool Is(const ValueType& v) { return v.IsInt(); }
+ static int Get(const ValueType& v) { return v.GetInt(); }
+ static ValueType& Set(ValueType& v, int data) { return v.SetInt(data); }
+ static ValueType& Set(ValueType& v, int data, typename ValueType::AllocatorType&) { return v.SetInt(data); }
+};
+
+template<typename ValueType>
+struct TypeHelper<ValueType, unsigned> {
+ static bool Is(const ValueType& v) { return v.IsUint(); }
+ static unsigned Get(const ValueType& v) { return v.GetUint(); }
+ static ValueType& Set(ValueType& v, unsigned data) { return v.SetUint(data); }
+ static ValueType& Set(ValueType& v, unsigned data, typename ValueType::AllocatorType&) { return v.SetUint(data); }
+};
+
+template<typename ValueType>
+struct TypeHelper<ValueType, int64_t> {
+ static bool Is(const ValueType& v) { return v.IsInt64(); }
+ static int64_t Get(const ValueType& v) { return v.GetInt64(); }
+ static ValueType& Set(ValueType& v, int64_t data) { return v.SetInt64(data); }
+ static ValueType& Set(ValueType& v, int64_t data, typename ValueType::AllocatorType&) { return v.SetInt64(data); }
+};
+
+template<typename ValueType>
+struct TypeHelper<ValueType, uint64_t> {
+ static bool Is(const ValueType& v) { return v.IsUint64(); }
+ static uint64_t Get(const ValueType& v) { return v.GetUint64(); }
+ static ValueType& Set(ValueType& v, uint64_t data) { return v.SetUint64(data); }
+ static ValueType& Set(ValueType& v, uint64_t data, typename ValueType::AllocatorType&) { return v.SetUint64(data); }
+};
+
+template<typename ValueType>
+struct TypeHelper<ValueType, double> {
+ static bool Is(const ValueType& v) { return v.IsDouble(); }
+ static double Get(const ValueType& v) { return v.GetDouble(); }
+ static ValueType& Set(ValueType& v, double data) { return v.SetDouble(data); }
+ static ValueType& Set(ValueType& v, double data, typename ValueType::AllocatorType&) { return v.SetDouble(data); }
+};
+
+template<typename ValueType>
+struct TypeHelper<ValueType, float> {
+ static bool Is(const ValueType& v) { return v.IsFloat(); }
+ static float Get(const ValueType& v) { return v.GetFloat(); }
+ static ValueType& Set(ValueType& v, float data) { return v.SetFloat(data); }
+ static ValueType& Set(ValueType& v, float data, typename ValueType::AllocatorType&) { return v.SetFloat(data); }
+};
+
+template<typename ValueType>
+struct TypeHelper<ValueType, const typename ValueType::Ch*> {
+ typedef const typename ValueType::Ch* StringType;
+ static bool Is(const ValueType& v) { return v.IsString(); }
+ static StringType Get(const ValueType& v) { return v.GetString(); }
+ static ValueType& Set(ValueType& v, const StringType data) { return v.SetString(typename ValueType::StringRefType(data)); }
+ static ValueType& Set(ValueType& v, const StringType data, typename ValueType::AllocatorType& a) { return v.SetString(data, a); }
+};
+
+#if RAPIDJSON_HAS_STDSTRING
+template<typename ValueType>
+struct TypeHelper<ValueType, std::basic_string<typename ValueType::Ch> > {
+ typedef std::basic_string<typename ValueType::Ch> StringType;
+ static bool Is(const ValueType& v) { return v.IsString(); }
+ static StringType Get(const ValueType& v) { return v.GetString(); }
+ static ValueType& Set(ValueType& v, const StringType& data, typename ValueType::AllocatorType& a) { return v.SetString(data, a); }
+};
+#endif
+
+template<typename ValueType>
+struct TypeHelper<ValueType, typename ValueType::Array> {
+ typedef typename ValueType::Array ArrayType;
+ static bool Is(const ValueType& v) { return v.IsArray(); }
+ static ArrayType Get(ValueType& v) { return v.GetArray(); }
+ static ValueType& Set(ValueType& v, ArrayType data) { return v = data; }
+ static ValueType& Set(ValueType& v, ArrayType data, typename ValueType::AllocatorType&) { return v = data; }
+};
+
+template<typename ValueType>
+struct TypeHelper<ValueType, typename ValueType::ConstArray> {
+ typedef typename ValueType::ConstArray ArrayType;
+ static bool Is(const ValueType& v) { return v.IsArray(); }
+ static ArrayType Get(const ValueType& v) { return v.GetArray(); }
+};
+
+template<typename ValueType>
+struct TypeHelper<ValueType, typename ValueType::Object> {
+ typedef typename ValueType::Object ObjectType;
+ static bool Is(const ValueType& v) { return v.IsObject(); }
+ static ObjectType Get(ValueType& v) { return v.GetObject(); }
+ static ValueType& Set(ValueType& v, ObjectType data) { return v = data; }
+ static ValueType& Set(ValueType& v, ObjectType data, typename ValueType::AllocatorType&) { v = data; }
+};
+
+template<typename ValueType>
+struct TypeHelper<ValueType, typename ValueType::ConstObject> {
+ typedef typename ValueType::ConstObject ObjectType;
+ static bool Is(const ValueType& v) { return v.IsObject(); }
+ static ObjectType Get(const ValueType& v) { return v.GetObject(); }
+};
+
+} // namespace internal
+
+// Forward declarations
+template <bool, typename> class GenericArray;
+template <bool, typename> class GenericObject;
+
+///////////////////////////////////////////////////////////////////////////////
// GenericValue
//! Represents a JSON value. Use Value for UTF8 encoding and default allocator.
@@ -420,17 +548,21 @@
typedef GenericValue* ValueIterator; //!< Value iterator for iterating in array.
typedef const GenericValue* ConstValueIterator; //!< Constant value iterator for iterating in array.
typedef GenericValue<Encoding, Allocator> ValueType; //!< Value type of itself.
+ typedef GenericArray<false, ValueType> Array;
+ typedef GenericArray<true, ValueType> ConstArray;
+ typedef GenericObject<false, ValueType> Object;
+ typedef GenericObject<true, ValueType> ConstObject;
//!@name Constructors and destructor.
//@{
//! Default constructor creates a null value.
- GenericValue() RAPIDJSON_NOEXCEPT : data_(), flags_(kNullFlag) {}
+ GenericValue() RAPIDJSON_NOEXCEPT : data_() { data_.f.flags = kNullFlag; }
#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
//! Move constructor in C++11
- GenericValue(GenericValue&& rhs) RAPIDJSON_NOEXCEPT : data_(rhs.data_), flags_(rhs.flags_) {
- rhs.flags_ = kNullFlag; // give up contents
+ GenericValue(GenericValue&& rhs) RAPIDJSON_NOEXCEPT : data_(rhs.data_) {
+ rhs.data_.f.flags = kNullFlag; // give up contents
}
#endif
@@ -455,13 +587,13 @@
\param type Type of the value.
\note Default content for number is zero.
*/
- explicit GenericValue(Type type) RAPIDJSON_NOEXCEPT : data_(), flags_() {
- static const unsigned defaultFlags[7] = {
+ explicit GenericValue(Type type) RAPIDJSON_NOEXCEPT : data_() {
+ static const uint16_t defaultFlags[7] = {
kNullFlag, kFalseFlag, kTrueFlag, kObjectFlag, kArrayFlag, kShortStringFlag,
kNumberAnyFlag
};
RAPIDJSON_ASSERT(type <= kNumberType);
- flags_ = defaultFlags[type];
+ data_.f.flags = defaultFlags[type];
// Use ShortString to store empty string.
if (type == kStringType)
@@ -486,96 +618,122 @@
*/
#ifndef RAPIDJSON_DOXYGEN_RUNNING // hide SFINAE from Doxygen
template <typename T>
- explicit GenericValue(T b, RAPIDJSON_ENABLEIF((internal::IsSame<T,bool>))) RAPIDJSON_NOEXCEPT
+ explicit GenericValue(T b, RAPIDJSON_ENABLEIF((internal::IsSame<bool, T>))) RAPIDJSON_NOEXCEPT // See #472
#else
explicit GenericValue(bool b) RAPIDJSON_NOEXCEPT
#endif
- : data_(), flags_(b ? kTrueFlag : kFalseFlag) {
+ : data_() {
// safe-guard against failing SFINAE
RAPIDJSON_STATIC_ASSERT((internal::IsSame<bool,T>::Value));
+ data_.f.flags = b ? kTrueFlag : kFalseFlag;
}
//! Constructor for int value.
- explicit GenericValue(int i) RAPIDJSON_NOEXCEPT : data_(), flags_(kNumberIntFlag) {
+ explicit GenericValue(int i) RAPIDJSON_NOEXCEPT : data_() {
data_.n.i64 = i;
- if (i >= 0)
- flags_ |= kUintFlag | kUint64Flag;
+ data_.f.flags = (i >= 0) ? (kNumberIntFlag | kUintFlag | kUint64Flag) : kNumberIntFlag;
}
//! Constructor for unsigned value.
- explicit GenericValue(unsigned u) RAPIDJSON_NOEXCEPT : data_(), flags_(kNumberUintFlag) {
+ explicit GenericValue(unsigned u) RAPIDJSON_NOEXCEPT : data_() {
data_.n.u64 = u;
- if (!(u & 0x80000000))
- flags_ |= kIntFlag | kInt64Flag;
+ data_.f.flags = (u & 0x80000000) ? kNumberUintFlag : (kNumberUintFlag | kIntFlag | kInt64Flag);
}
//! Constructor for int64_t value.
- explicit GenericValue(int64_t i64) RAPIDJSON_NOEXCEPT : data_(), flags_(kNumberInt64Flag) {
+ explicit GenericValue(int64_t i64) RAPIDJSON_NOEXCEPT : data_() {
data_.n.i64 = i64;
+ data_.f.flags = kNumberInt64Flag;
if (i64 >= 0) {
- flags_ |= kNumberUint64Flag;
+ data_.f.flags |= kNumberUint64Flag;
if (!(static_cast<uint64_t>(i64) & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x00000000)))
- flags_ |= kUintFlag;
+ data_.f.flags |= kUintFlag;
if (!(static_cast<uint64_t>(i64) & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000)))
- flags_ |= kIntFlag;
+ data_.f.flags |= kIntFlag;
}
else if (i64 >= static_cast<int64_t>(RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000)))
- flags_ |= kIntFlag;
+ data_.f.flags |= kIntFlag;
}
//! Constructor for uint64_t value.
- explicit GenericValue(uint64_t u64) RAPIDJSON_NOEXCEPT : data_(), flags_(kNumberUint64Flag) {
+ explicit GenericValue(uint64_t u64) RAPIDJSON_NOEXCEPT : data_() {
data_.n.u64 = u64;
+ data_.f.flags = kNumberUint64Flag;
if (!(u64 & RAPIDJSON_UINT64_C2(0x80000000, 0x00000000)))
- flags_ |= kInt64Flag;
+ data_.f.flags |= kInt64Flag;
if (!(u64 & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x00000000)))
- flags_ |= kUintFlag;
+ data_.f.flags |= kUintFlag;
if (!(u64 & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000)))
- flags_ |= kIntFlag;
+ data_.f.flags |= kIntFlag;
}
//! Constructor for double value.
- explicit GenericValue(double d) RAPIDJSON_NOEXCEPT : data_(), flags_(kNumberDoubleFlag) { data_.n.d = d; }
-
- //! Constructor for constant string (i.e. do not make a copy of string)
- GenericValue(const Ch* s, SizeType length) RAPIDJSON_NOEXCEPT : data_(), flags_() { SetStringRaw(StringRef(s, length)); }
-
- //! Constructor for constant string (i.e. do not make a copy of string)
- explicit GenericValue(StringRefType s) RAPIDJSON_NOEXCEPT : data_(), flags_() { SetStringRaw(s); }
-
- //! Constructor for copy-string (i.e. do make a copy of string)
- GenericValue(const Ch* s, SizeType length, Allocator& allocator) : data_(), flags_() { SetStringRaw(StringRef(s, length), allocator); }
-
- //! Constructor for copy-string (i.e. do make a copy of string)
- GenericValue(const Ch*s, Allocator& allocator) : data_(), flags_() { SetStringRaw(StringRef(s), allocator); }
+ explicit GenericValue(double d) RAPIDJSON_NOEXCEPT : data_() { data_.n.d = d; data_.f.flags = kNumberDoubleFlag; }
+
+ //! Constructor for constant string (i.e. do not make a copy of string)
+ GenericValue(const Ch* s, SizeType length) RAPIDJSON_NOEXCEPT : data_() { SetStringRaw(StringRef(s, length)); }
+
+ //! Constructor for constant string (i.e. do not make a copy of string)
+ explicit GenericValue(StringRefType s) RAPIDJSON_NOEXCEPT : data_() { SetStringRaw(s); }
+
+ //! Constructor for copy-string (i.e. do make a copy of string)
+ GenericValue(const Ch* s, SizeType length, Allocator& allocator) : data_() { SetStringRaw(StringRef(s, length), allocator); }
+
+ //! Constructor for copy-string (i.e. do make a copy of string)
+ GenericValue(const Ch*s, Allocator& allocator) : data_() { SetStringRaw(StringRef(s), allocator); }
#if RAPIDJSON_HAS_STDSTRING
//! Constructor for copy-string from a string object (i.e. do make a copy of string)
/*! \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING.
*/
- GenericValue(const std::basic_string<Ch>& s, Allocator& allocator) : data_(), flags_() { SetStringRaw(StringRef(s), allocator); }
+ GenericValue(const std::basic_string<Ch>& s, Allocator& allocator) : data_() { SetStringRaw(StringRef(s), allocator); }
#endif
+ //! Constructor for Array.
+ /*!
+ \param a An array obtained by \c GetArray().
+ \note \c Array is always pass-by-value.
+ \note the source array is moved into this value and the sourec array becomes empty.
+ */
+ GenericValue(Array a) RAPIDJSON_NOEXCEPT : data_(a.value_.data_) {
+ a.value_.data_ = Data();
+ a.value_.data_.f.flags = kArrayFlag;
+ }
+
+ //! Constructor for Object.
+ /*!
+ \param o An object obtained by \c GetObject().
+ \note \c Object is always pass-by-value.
+ \note the source object is moved into this value and the sourec object becomes empty.
+ */
+ GenericValue(Object o) RAPIDJSON_NOEXCEPT : data_(o.value_.data_) {
+ o.value_.data_ = Data();
+ o.value_.data_.f.flags = kObjectFlag;
+ }
+
//! Destructor.
/*! Need to destruct elements of array, members of object, or copy-string.
*/
~GenericValue() {
if (Allocator::kNeedFree) { // Shortcut by Allocator's trait
- switch(flags_) {
+ switch(data_.f.flags) {
case kArrayFlag:
- for (GenericValue* v = data_.a.elements; v != data_.a.elements + data_.a.size; ++v)
- v->~GenericValue();
- Allocator::Free(data_.a.elements);
+ {
+ GenericValue* e = GetElementsPointer();
+ for (GenericValue* v = e; v != e + data_.a.size; ++v)
+ v->~GenericValue();
+ Allocator::Free(e);
+ }
break;
case kObjectFlag:
for (MemberIterator m = MemberBegin(); m != MemberEnd(); ++m)
m->~Member();
- Allocator::Free(data_.o.members);
+ Allocator::Free(GetMembersPointer());
break;
case kCopyStringFlag:
- Allocator::Free(const_cast<Ch*>(data_.s.str));
+ Allocator::Free(const_cast<Ch*>(GetStringPointer()));
break;
default:
@@ -773,20 +931,51 @@
//!@name Type
//@{
- Type GetType() const { return static_cast<Type>(flags_ & kTypeMask); }
- bool IsNull() const { return flags_ == kNullFlag; }
- bool IsFalse() const { return flags_ == kFalseFlag; }
- bool IsTrue() const { return flags_ == kTrueFlag; }
- bool IsBool() const { return (flags_ & kBoolFlag) != 0; }
- bool IsObject() const { return flags_ == kObjectFlag; }
- bool IsArray() const { return flags_ == kArrayFlag; }
- bool IsNumber() const { return (flags_ & kNumberFlag) != 0; }
- bool IsInt() const { return (flags_ & kIntFlag) != 0; }
- bool IsUint() const { return (flags_ & kUintFlag) != 0; }
- bool IsInt64() const { return (flags_ & kInt64Flag) != 0; }
- bool IsUint64() const { return (flags_ & kUint64Flag) != 0; }
- bool IsDouble() const { return (flags_ & kDoubleFlag) != 0; }
- bool IsString() const { return (flags_ & kStringFlag) != 0; }
+ Type GetType() const { return static_cast<Type>(data_.f.flags & kTypeMask); }
+ bool IsNull() const { return data_.f.flags == kNullFlag; }
+ bool IsFalse() const { return data_.f.flags == kFalseFlag; }
+ bool IsTrue() const { return data_.f.flags == kTrueFlag; }
+ bool IsBool() const { return (data_.f.flags & kBoolFlag) != 0; }
+ bool IsObject() const { return data_.f.flags == kObjectFlag; }
+ bool IsArray() const { return data_.f.flags == kArrayFlag; }
+ bool IsNumber() const { return (data_.f.flags & kNumberFlag) != 0; }
+ bool IsInt() const { return (data_.f.flags & kIntFlag) != 0; }
+ bool IsUint() const { return (data_.f.flags & kUintFlag) != 0; }
+ bool IsInt64() const { return (data_.f.flags & kInt64Flag) != 0; }
+ bool IsUint64() const { return (data_.f.flags & kUint64Flag) != 0; }
+ bool IsDouble() const { return (data_.f.flags & kDoubleFlag) != 0; }
+ bool IsString() const { return (data_.f.flags & kStringFlag) != 0; }
+
+ // Checks whether a number can be losslessly converted to a double.
+ bool IsLosslessDouble() const {
+ if (!IsNumber()) return false;
+ if (IsUint64()) {
+ uint64_t u = GetUint64();
+ volatile double d = static_cast<double>(u);
+ return static_cast<uint64_t>(d) == u;
+ }
+ if (IsInt64()) {
+ int64_t i = GetInt64();
+ volatile double d = static_cast<double>(i);
+ return static_cast< int64_t>(d) == i;
+ }
+ return true; // double, int, uint are always lossless
+ }
+
+ // Checks whether a number is a float (possible lossy).
+ bool IsFloat() const {
+ if ((data_.f.flags & kDoubleFlag) == 0)
+ return false;
+ double d = GetDouble();
+ return d >= -3.4028234e38 && d <= 3.4028234e38;
+ }
+ // Checks whether a number can be losslessly converted to a float.
+ bool IsLosslessFloat() const {
+ if (!IsNumber()) return false;
+ double a = GetDouble();
+ double b = static_cast<double>(static_cast<float>(a));
+ return a >= b && a <= b; // Prevent -Wfloat-equal
+ }
//@}
@@ -800,7 +989,7 @@
//!@name Bool
//@{
- bool GetBool() const { RAPIDJSON_ASSERT(IsBool()); return flags_ == kTrueFlag; }
+ bool GetBool() const { RAPIDJSON_ASSERT(IsBool()); return data_.f.flags == kTrueFlag; }
//!< Set boolean value
/*! \post IsBool() == true */
GenericValue& SetBool(bool b) { this->~GenericValue(); new (this) GenericValue(b); return *this; }
@@ -874,16 +1063,16 @@
//! Const member iterator
/*! \pre IsObject() == true */
- ConstMemberIterator MemberBegin() const { RAPIDJSON_ASSERT(IsObject()); return ConstMemberIterator(data_.o.members); }
+ ConstMemberIterator MemberBegin() const { RAPIDJSON_ASSERT(IsObject()); return ConstMemberIterator(GetMembersPointer()); }
//! Const \em past-the-end member iterator
/*! \pre IsObject() == true */
- ConstMemberIterator MemberEnd() const { RAPIDJSON_ASSERT(IsObject()); return ConstMemberIterator(data_.o.members + data_.o.size); }
+ ConstMemberIterator MemberEnd() const { RAPIDJSON_ASSERT(IsObject()); return ConstMemberIterator(GetMembersPointer() + data_.o.size); }
//! Member iterator
/*! \pre IsObject() == true */
- MemberIterator MemberBegin() { RAPIDJSON_ASSERT(IsObject()); return MemberIterator(data_.o.members); }
+ MemberIterator MemberBegin() { RAPIDJSON_ASSERT(IsObject()); return MemberIterator(GetMembersPointer()); }
//! \em Past-the-end member iterator
/*! \pre IsObject() == true */
- MemberIterator MemberEnd() { RAPIDJSON_ASSERT(IsObject()); return MemberIterator(data_.o.members + data_.o.size); }
+ MemberIterator MemberEnd() { RAPIDJSON_ASSERT(IsObject()); return MemberIterator(GetMembersPointer() + data_.o.size); }
//! Check whether a member exists in the object.
/*!
@@ -989,20 +1178,21 @@
RAPIDJSON_ASSERT(IsObject());
RAPIDJSON_ASSERT(name.IsString());
- Object& o = data_.o;
+ ObjectData& o = data_.o;
if (o.size >= o.capacity) {
if (o.capacity == 0) {
o.capacity = kDefaultObjectCapacity;
- o.members = reinterpret_cast<Member*>(allocator.Malloc(o.capacity * sizeof(Member)));
+ SetMembersPointer(reinterpret_cast<Member*>(allocator.Malloc(o.capacity * sizeof(Member))));
}
else {
SizeType oldCapacity = o.capacity;
o.capacity += (oldCapacity + 1) / 2; // grow by factor 1.5
- o.members = reinterpret_cast<Member*>(allocator.Realloc(o.members, oldCapacity * sizeof(Member), o.capacity * sizeof(Member)));
+ SetMembersPointer(reinterpret_cast<Member*>(allocator.Realloc(GetMembersPointer(), oldCapacity * sizeof(Member), o.capacity * sizeof(Member))));
}
}
- o.members[o.size].name.RawAssign(name);
- o.members[o.size].value.RawAssign(value);
+ Member* members = GetMembersPointer();
+ members[o.size].name.RawAssign(name);
+ members[o.size].value.RawAssign(value);
o.size++;
return *this;
}
@@ -1181,18 +1371,14 @@
MemberIterator RemoveMember(MemberIterator m) {
RAPIDJSON_ASSERT(IsObject());
RAPIDJSON_ASSERT(data_.o.size > 0);
- RAPIDJSON_ASSERT(data_.o.members != 0);
+ RAPIDJSON_ASSERT(GetMembersPointer() != 0);
RAPIDJSON_ASSERT(m >= MemberBegin() && m < MemberEnd());
- MemberIterator last(data_.o.members + (data_.o.size - 1));
- if (data_.o.size > 1 && m != last) {
- // Move the last one to this place
- *m = *last;
- }
- else {
- // Only one left, just destroy
- m->~Member();
- }
+ MemberIterator last(GetMembersPointer() + (data_.o.size - 1));
+ if (data_.o.size > 1 && m != last)
+ *m = *last; // Move the last one to this place
+ else
+ m->~Member(); // Only one left, just destroy
--data_.o.size;
return m;
}
@@ -1222,7 +1408,7 @@
MemberIterator EraseMember(ConstMemberIterator first, ConstMemberIterator last) {
RAPIDJSON_ASSERT(IsObject());
RAPIDJSON_ASSERT(data_.o.size > 0);
- RAPIDJSON_ASSERT(data_.o.members != 0);
+ RAPIDJSON_ASSERT(GetMembersPointer() != 0);
RAPIDJSON_ASSERT(first >= MemberBegin());
RAPIDJSON_ASSERT(first <= last);
RAPIDJSON_ASSERT(last <= MemberEnd());
@@ -1260,6 +1446,9 @@
return false;
}
+ Object GetObject() { RAPIDJSON_ASSERT(IsObject()); return Object(*this); }
+ ConstObject GetObject() const { RAPIDJSON_ASSERT(IsObject()); return ConstObject(*this); }
+
//@}
//!@name Array
@@ -1267,7 +1456,7 @@
//! Set this value as an empty array.
/*! \post IsArray == true */
- GenericValue& SetArray() { this->~GenericValue(); new (this) GenericValue(kArrayType); return *this; }
+ GenericValue& SetArray() { this->~GenericValue(); new (this) GenericValue(kArrayType); return *this; }
//! Get the number of elements in array.
SizeType Size() const { RAPIDJSON_ASSERT(IsArray()); return data_.a.size; }
@@ -1284,8 +1473,9 @@
*/
void Clear() {
RAPIDJSON_ASSERT(IsArray());
- for (SizeType i = 0; i < data_.a.size; ++i)
- data_.a.elements[i].~GenericValue();
+ GenericValue* e = GetElementsPointer();
+ for (GenericValue* v = e; v != e + data_.a.size; ++v)
+ v->~GenericValue();
data_.a.size = 0;
}
@@ -1297,16 +1487,16 @@
GenericValue& operator[](SizeType index) {
RAPIDJSON_ASSERT(IsArray());
RAPIDJSON_ASSERT(index < data_.a.size);
- return data_.a.elements[index];
+ return GetElementsPointer()[index];
}
const GenericValue& operator[](SizeType index) const { return const_cast<GenericValue&>(*this)[index]; }
//! Element iterator
/*! \pre IsArray() == true */
- ValueIterator Begin() { RAPIDJSON_ASSERT(IsArray()); return data_.a.elements; }
+ ValueIterator Begin() { RAPIDJSON_ASSERT(IsArray()); return GetElementsPointer(); }
//! \em Past-the-end element iterator
/*! \pre IsArray() == true */
- ValueIterator End() { RAPIDJSON_ASSERT(IsArray()); return data_.a.elements + data_.a.size; }
+ ValueIterator End() { RAPIDJSON_ASSERT(IsArray()); return GetElementsPointer() + data_.a.size; }
//! Constant element iterator
/*! \pre IsArray() == true */
ConstValueIterator Begin() const { return const_cast<GenericValue&>(*this).Begin(); }
@@ -1323,7 +1513,7 @@
GenericValue& Reserve(SizeType newCapacity, Allocator &allocator) {
RAPIDJSON_ASSERT(IsArray());
if (newCapacity > data_.a.capacity) {
- data_.a.elements = static_cast<GenericValue*>(allocator.Realloc(data_.a.elements, data_.a.capacity * sizeof(GenericValue), newCapacity * sizeof(GenericValue)));
+ SetElementsPointer(reinterpret_cast<GenericValue*>(allocator.Realloc(GetElementsPointer(), data_.a.capacity * sizeof(GenericValue), newCapacity * sizeof(GenericValue))));
data_.a.capacity = newCapacity;
}
return *this;
@@ -1343,7 +1533,7 @@
RAPIDJSON_ASSERT(IsArray());
if (data_.a.size >= data_.a.capacity)
Reserve(data_.a.capacity == 0 ? kDefaultArrayCapacity : (data_.a.capacity + (data_.a.capacity + 1) / 2), allocator);
- data_.a.elements[data_.a.size++].RawAssign(value);
+ GetElementsPointer()[data_.a.size++].RawAssign(value);
return *this;
}
@@ -1397,7 +1587,7 @@
GenericValue& PopBack() {
RAPIDJSON_ASSERT(IsArray());
RAPIDJSON_ASSERT(!Empty());
- data_.a.elements[--data_.a.size].~GenericValue();
+ GetElementsPointer()[--data_.a.size].~GenericValue();
return *this;
}
@@ -1423,7 +1613,7 @@
ValueIterator Erase(ConstValueIterator first, ConstValueIterator last) {
RAPIDJSON_ASSERT(IsArray());
RAPIDJSON_ASSERT(data_.a.size > 0);
- RAPIDJSON_ASSERT(data_.a.elements != 0);
+ RAPIDJSON_ASSERT(GetElementsPointer() != 0);
RAPIDJSON_ASSERT(first >= Begin());
RAPIDJSON_ASSERT(first <= last);
RAPIDJSON_ASSERT(last <= End());
@@ -1435,23 +1625,36 @@
return pos;
}
+ Array GetArray() { RAPIDJSON_ASSERT(IsArray()); return Array(*this); }
+ ConstArray GetArray() const { RAPIDJSON_ASSERT(IsArray()); return ConstArray(*this); }
+
//@}
//!@name Number
//@{
- int GetInt() const { RAPIDJSON_ASSERT(flags_ & kIntFlag); return data_.n.i.i; }
- unsigned GetUint() const { RAPIDJSON_ASSERT(flags_ & kUintFlag); return data_.n.u.u; }
- int64_t GetInt64() const { RAPIDJSON_ASSERT(flags_ & kInt64Flag); return data_.n.i64; }
- uint64_t GetUint64() const { RAPIDJSON_ASSERT(flags_ & kUint64Flag); return data_.n.u64; }
+ int GetInt() const { RAPIDJSON_ASSERT(data_.f.flags & kIntFlag); return data_.n.i.i; }
+ unsigned GetUint() const { RAPIDJSON_ASSERT(data_.f.flags & kUintFlag); return data_.n.u.u; }
+ int64_t GetInt64() const { RAPIDJSON_ASSERT(data_.f.flags & kInt64Flag); return data_.n.i64; }
+ uint64_t GetUint64() const { RAPIDJSON_ASSERT(data_.f.flags & kUint64Flag); return data_.n.u64; }
+ //! Get the value as double type.
+ /*! \note If the value is 64-bit integer type, it may lose precision. Use \c IsLosslessDouble() to check whether the converison is lossless.
+ */
double GetDouble() const {
RAPIDJSON_ASSERT(IsNumber());
- if ((flags_ & kDoubleFlag) != 0) return data_.n.d; // exact type, no conversion.
- if ((flags_ & kIntFlag) != 0) return data_.n.i.i; // int -> double
- if ((flags_ & kUintFlag) != 0) return data_.n.u.u; // unsigned -> double
- if ((flags_ & kInt64Flag) != 0) return static_cast<double>(data_.n.i64); // int64_t -> double (may lose precision)
- RAPIDJSON_ASSERT((flags_ & kUint64Flag) != 0); return static_cast<double>(data_.n.u64); // uint64_t -> double (may lose precision)
+ if ((data_.f.flags & kDoubleFlag) != 0) return data_.n.d; // exact type, no conversion.
+ if ((data_.f.flags & kIntFlag) != 0) return data_.n.i.i; // int -> double
+ if ((data_.f.flags & kUintFlag) != 0) return data_.n.u.u; // unsigned -> double
+ if ((data_.f.flags & kInt64Flag) != 0) return static_cast<double>(data_.n.i64); // int64_t -> double (may lose precision)
+ RAPIDJSON_ASSERT((data_.f.flags & kUint64Flag) != 0); return static_cast<double>(data_.n.u64); // uint64_t -> double (may lose precision)
+ }
+
+ //! Get the value as float type.
+ /*! \note If the value is 64-bit integer type, it may lose precision. Use \c IsLosslessFloat() to check whether the converison is lossless.
+ */
+ float GetFloat() const {
+ return static_cast<float>(GetDouble());
}
GenericValue& SetInt(int i) { this->~GenericValue(); new (this) GenericValue(i); return *this; }
@@ -1459,18 +1662,19 @@
GenericValue& SetInt64(int64_t i64) { this->~GenericValue(); new (this) GenericValue(i64); return *this; }
GenericValue& SetUint64(uint64_t u64) { this->~GenericValue(); new (this) GenericValue(u64); return *this; }
GenericValue& SetDouble(double d) { this->~GenericValue(); new (this) GenericValue(d); return *this; }
+ GenericValue& SetFloat(float f) { this->~GenericValue(); new (this) GenericValue(f); return *this; }
//@}
//!@name String
//@{
- const Ch* GetString() const { RAPIDJSON_ASSERT(IsString()); return ((flags_ & kInlineStrFlag) ? data_.ss.str : data_.s.str); }
+ const Ch* GetString() const { RAPIDJSON_ASSERT(IsString()); return (data_.f.flags & kInlineStrFlag) ? data_.ss.str : GetStringPointer(); }
//! Get the length of string.
/*! Since rapidjson permits "\\u0000" in the json string, strlen(v.GetString()) may not equal to v.GetStringLength().
*/
- SizeType GetStringLength() const { RAPIDJSON_ASSERT(IsString()); return ((flags_ & kInlineStrFlag) ? (data_.ss.GetLength()) : data_.s.length); }
+ SizeType GetStringLength() const { RAPIDJSON_ASSERT(IsString()); return ((data_.f.flags & kInlineStrFlag) ? (data_.ss.GetLength()) : data_.s.length); }
//! Set this value as a string without copying source string.
/*! This version has better performance with supplied length, and also support string containing null character.
@@ -1520,6 +1724,30 @@
//@}
+ //!@name Array
+ //@{
+
+ //! Templated version for checking whether this value is type T.
+ /*!
+ \tparam T Either \c bool, \c int, \c unsigned, \c int64_t, \c uint64_t, \c double, \c float, \c const \c char*, \c std::basic_string<Ch>
+ */
+ template <typename T>
+ bool Is() const { return internal::TypeHelper<ValueType, T>::Is(*this); }
+
+ template <typename T>
+ T Get() const { return internal::TypeHelper<ValueType, T>::Get(*this); }
+
+ template <typename T>
+ T Get() { return internal::TypeHelper<ValueType, T>::Get(*this); }
+
+ template<typename T>
+ ValueType& Set(const T& data) { return internal::TypeHelper<ValueType, T>::Set(*this, data); }
+
+ template<typename T>
+ ValueType& Set(const T& data, AllocatorType& allocator) { return internal::TypeHelper<ValueType, T>::Set(*this, data, allocator); }
+
+ //@}
+
//! Generate events of this value to a Handler.
/*! This function adopts the GoF visitor pattern.
Typical usage is to output this JSON value as JSON text via Writer, which is a Handler.
@@ -1535,35 +1763,35 @@
case kTrueType: return handler.Bool(true);
case kObjectType:
- if (!handler.StartObject())
+ if (RAPIDJSON_UNLIKELY(!handler.StartObject()))
return false;
for (ConstMemberIterator m = MemberBegin(); m != MemberEnd(); ++m) {
RAPIDJSON_ASSERT(m->name.IsString()); // User may change the type of name by MemberIterator.
- if (!handler.Key(m->name.GetString(), m->name.GetStringLength(), (m->name.flags_ & kCopyFlag) != 0))
+ if (RAPIDJSON_UNLIKELY(!handler.Key(m->name.GetString(), m->name.GetStringLength(), (m->name.data_.f.flags & kCopyFlag) != 0)))
return false;
- if (!m->value.Accept(handler))
+ if (RAPIDJSON_UNLIKELY(!m->value.Accept(handler)))
return false;
}
return handler.EndObject(data_.o.size);
case kArrayType:
- if (!handler.StartArray())
+ if (RAPIDJSON_UNLIKELY(!handler.StartArray()))
return false;
- for (GenericValue* v = data_.a.elements; v != data_.a.elements + data_.a.size; ++v)
- if (!v->Accept(handler))
+ for (const GenericValue* v = Begin(); v != End(); ++v)
+ if (RAPIDJSON_UNLIKELY(!v->Accept(handler)))
return false;
return handler.EndArray(data_.a.size);
case kStringType:
- return handler.String(GetString(), GetStringLength(), (flags_ & kCopyFlag) != 0);
+ return handler.String(GetString(), GetStringLength(), (data_.f.flags & kCopyFlag) != 0);
default:
RAPIDJSON_ASSERT(GetType() == kNumberType);
- if (IsInt()) return handler.Int(data_.n.i.i);
+ if (IsDouble()) return handler.Double(data_.n.d);
+ else if (IsInt()) return handler.Int(data_.n.i.i);
else if (IsUint()) return handler.Uint(data_.n.u.u);
else if (IsInt64()) return handler.Int64(data_.n.i64);
- else if (IsUint64()) return handler.Uint64(data_.n.u64);
- else return handler.Double(data_.n.d);
+ else return handler.Uint64(data_.n.u64);
}
}
@@ -1572,16 +1800,16 @@
template <typename, typename, typename> friend class GenericDocument;
enum {
- kBoolFlag = 0x100,
- kNumberFlag = 0x200,
- kIntFlag = 0x400,
- kUintFlag = 0x800,
- kInt64Flag = 0x1000,
- kUint64Flag = 0x2000,
- kDoubleFlag = 0x4000,
- kStringFlag = 0x100000,
- kCopyFlag = 0x200000,
- kInlineStrFlag = 0x400000,
+ kBoolFlag = 0x0008,
+ kNumberFlag = 0x0010,
+ kIntFlag = 0x0020,
+ kUintFlag = 0x0040,
+ kInt64Flag = 0x0080,
+ kUint64Flag = 0x0100,
+ kDoubleFlag = 0x0200,
+ kStringFlag = 0x0400,
+ kCopyFlag = 0x0800,
+ kInlineStrFlag = 0x1000,
// Initial flags of different types.
kNullFlag = kNullType,
@@ -1599,16 +1827,27 @@
kObjectFlag = kObjectType,
kArrayFlag = kArrayType,
- kTypeMask = 0xFF // bitwise-and with mask of 0xFF can be optimized by compiler
+ kTypeMask = 0x07
};
static const SizeType kDefaultArrayCapacity = 16;
static const SizeType kDefaultObjectCapacity = 16;
+ struct Flag {
+#if RAPIDJSON_48BITPOINTER_OPTIMIZATION
+ char payload[sizeof(SizeType) * 2 + 6]; // 2 x SizeType + lower 48-bit pointer
+#elif RAPIDJSON_64BIT
+ char payload[sizeof(SizeType) * 2 + sizeof(void*) + 6]; // 6 padding bytes
+#else
+ char payload[sizeof(SizeType) * 2 + sizeof(void*) + 2]; // 2 padding bytes
+#endif
+ uint16_t flags;
+ };
+
struct String {
+ SizeType length;
+ SizeType hashcode; //!< reserved
const Ch* str;
- SizeType length;
- unsigned hashcode; //!< reserved
}; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode
// implementation detail: ShortString can represent zero-terminated strings up to MaxSize chars
@@ -1617,10 +1856,10 @@
// to store has the maximal length of MaxSize then str[LenPos] will be 0 and therefore act as
// the string terminator as well. For getting the string length back from that value just use
// "MaxSize - str[LenPos]".
- // This allows to store 11-chars strings in 32-bit mode and 15-chars strings in 64-bit mode
- // inline (for `UTF8`-encoded strings).
+ // This allows to store 13-chars strings in 32-bit mode, 21-chars strings in 64-bit mode,
+ // 13-chars strings for RAPIDJSON_48BITPOINTER_OPTIMIZATION=1 inline (for `UTF8`-encoded strings).
struct ShortString {
- enum { MaxChars = sizeof(String) / sizeof(Ch), MaxSize = MaxChars - 1, LenPos = MaxSize };
+ enum { MaxChars = sizeof(static_cast<Flag*>(0)->payload) / sizeof(Ch), MaxSize = MaxChars - 1, LenPos = MaxSize };
Ch str[MaxChars];
inline static bool Usable(SizeType len) { return (MaxSize >= len); }
@@ -1654,69 +1893,79 @@
double d;
}; // 8 bytes
- struct Object {
+ struct ObjectData {
+ SizeType size;
+ SizeType capacity;
Member* members;
+ }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode
+
+ struct ArrayData {
SizeType size;
SizeType capacity;
- }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode
-
- struct Array {
GenericValue* elements;
- SizeType size;
- SizeType capacity;
}; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode
union Data {
String s;
ShortString ss;
Number n;
- Object o;
- Array a;
- }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode
+ ObjectData o;
+ ArrayData a;
+ Flag f;
+ }; // 16 bytes in 32-bit mode, 24 bytes in 64-bit mode, 16 bytes in 64-bit with RAPIDJSON_48BITPOINTER_OPTIMIZATION
+
+ RAPIDJSON_FORCEINLINE const Ch* GetStringPointer() const { return RAPIDJSON_GETPOINTER(Ch, data_.s.str); }
+ RAPIDJSON_FORCEINLINE const Ch* SetStringPointer(const Ch* str) { return RAPIDJSON_SETPOINTER(Ch, data_.s.str, str); }
+ RAPIDJSON_FORCEINLINE GenericValue* GetElementsPointer() const { return RAPIDJSON_GETPOINTER(GenericValue, data_.a.elements); }
+ RAPIDJSON_FORCEINLINE GenericValue* SetElementsPointer(GenericValue* elements) { return RAPIDJSON_SETPOINTER(GenericValue, data_.a.elements, elements); }
+ RAPIDJSON_FORCEINLINE Member* GetMembersPointer() const { return RAPIDJSON_GETPOINTER(Member, data_.o.members); }
+ RAPIDJSON_FORCEINLINE Member* SetMembersPointer(Member* members) { return RAPIDJSON_SETPOINTER(Member, data_.o.members, members); }
// Initialize this value as array with initial data, without calling destructor.
void SetArrayRaw(GenericValue* values, SizeType count, Allocator& allocator) {
- flags_ = kArrayFlag;
+ data_.f.flags = kArrayFlag;
if (count) {
- data_.a.elements = static_cast<GenericValue*>(allocator.Malloc(count * sizeof(GenericValue)));
- std::memcpy(data_.a.elements, values, count * sizeof(GenericValue));
+ GenericValue* e = static_cast<GenericValue*>(allocator.Malloc(count * sizeof(GenericValue)));
+ SetElementsPointer(e);
+ std::memcpy(e, values, count * sizeof(GenericValue));
}
else
- data_.a.elements = NULL;
+ SetElementsPointer(0);
data_.a.size = data_.a.capacity = count;
}
//! Initialize this value as object with initial data, without calling destructor.
void SetObjectRaw(Member* members, SizeType count, Allocator& allocator) {
- flags_ = kObjectFlag;
+ data_.f.flags = kObjectFlag;
if (count) {
- data_.o.members = static_cast<Member*>(allocator.Malloc(count * sizeof(Member)));
- std::memcpy(data_.o.members, members, count * sizeof(Member));
+ Member* m = static_cast<Member*>(allocator.Malloc(count * sizeof(Member)));
+ SetMembersPointer(m);
+ std::memcpy(m, members, count * sizeof(Member));
}
else
- data_.o.members = NULL;
+ SetMembersPointer(0);
data_.o.size = data_.o.capacity = count;
}
//! Initialize this value as constant string, without calling destructor.
void SetStringRaw(StringRefType s) RAPIDJSON_NOEXCEPT {
- flags_ = kConstStringFlag;
- data_.s.str = s;
+ data_.f.flags = kConstStringFlag;
+ SetStringPointer(s);
data_.s.length = s.length;
}
//! Initialize this value as copy string with initial data, without calling destructor.
void SetStringRaw(StringRefType s, Allocator& allocator) {
- Ch* str = NULL;
- if(ShortString::Usable(s.length)) {
- flags_ = kShortStringFlag;
+ Ch* str = 0;
+ if (ShortString::Usable(s.length)) {
+ data_.f.flags = kShortStringFlag;
data_.ss.SetLength(s.length);
str = data_.ss.str;
} else {
- flags_ = kCopyStringFlag;
+ data_.f.flags = kCopyStringFlag;
data_.s.length = s.length;
str = static_cast<Ch *>(allocator.Malloc((s.length + 1) * sizeof(Ch)));
- data_.s.str = str;
+ SetStringPointer(str);
}
std::memcpy(str, s, s.length * sizeof(Ch));
str[s.length] = '\0';
@@ -1725,8 +1974,8 @@
//! Assignment without calling destructor
void RawAssign(GenericValue& rhs) RAPIDJSON_NOEXCEPT {
data_ = rhs.data_;
- flags_ = rhs.flags_;
- rhs.flags_ = kNullFlag;
+ // data_.f.flags = rhs.data_.f.flags;
+ rhs.data_.f.flags = kNullFlag;
}
template <typename SourceAllocator>
@@ -1746,7 +1995,6 @@
}
Data data_;
- unsigned flags_;
};
//! GenericValue with UTF8 encoding
@@ -1869,6 +2117,21 @@
*/
friend inline void swap(GenericDocument& a, GenericDocument& b) RAPIDJSON_NOEXCEPT { a.Swap(b); }
+ //! Populate this document by a generator which produces SAX events.
+ /*! \tparam Generator A functor with <tt>bool f(Handler)</tt> prototype.
+ \param g Generator functor which sends SAX events to the parameter.
+ \return The document itself for fluent API.
+ */
+ template <typename Generator>
+ GenericDocument& Populate(Generator& g) {
+ ClearStackOnExit scope(*this);
+ if (g(*this)) {
+ RAPIDJSON_ASSERT(stack_.GetSize() == sizeof(ValueType)); // Got one and only one root object
+ ValueType::operator=(*stack_.template Pop<ValueType>(1));// Move value from stack to document
+ }
+ return *this;
+ }
+
//!@name Parse from stream
//!@{
@@ -1967,6 +2230,42 @@
GenericDocument& Parse(const Ch* str) {
return Parse<kParseDefaultFlags>(str);
}
+
+ template <unsigned parseFlags, typename SourceEncoding>
+ GenericDocument& Parse(const typename SourceEncoding::Ch* str, size_t length) {
+ RAPIDJSON_ASSERT(!(parseFlags & kParseInsituFlag));
+ MemoryStream ms(static_cast<const char*>(str), length * sizeof(typename SourceEncoding::Ch));
+ EncodedInputStream<SourceEncoding, MemoryStream> is(ms);
+ ParseStream<parseFlags, SourceEncoding>(is);
+ return *this;
+ }
+
+ template <unsigned parseFlags>
+ GenericDocument& Parse(const Ch* str, size_t length) {
+ return Parse<parseFlags, Encoding>(str, length);
+ }
+
+ GenericDocument& Parse(const Ch* str, size_t length) {
+ return Parse<kParseDefaultFlags>(str, length);
+ }
+
+#if RAPIDJSON_HAS_STDSTRING
+ template <unsigned parseFlags, typename SourceEncoding>
+ GenericDocument& Parse(const std::basic_string<typename SourceEncoding::Ch>& str) {
+ // c_str() is constant complexity according to standard. Should be faster than Parse(const char*, size_t)
+ return Parse<parseFlags, SourceEncoding>(str.c_str());
+ }
+
+ template <unsigned parseFlags>
+ GenericDocument& Parse(const std::basic_string<Ch>& str) {
+ return Parse<parseFlags, Encoding>(str.c_str());
+ }
+
+ GenericDocument& Parse(const std::basic_string<Ch>& str) {
+ return Parse<kParseDefaultFlags>(str);
+ }
+#endif // RAPIDJSON_HAS_STDSTRING
+
//!@}
//!@name Handling parse errors
@@ -2017,9 +2316,10 @@
};
// callers of the following private Handler functions
- template <typename,typename,typename> friend class GenericReader; // for parsing
+ // template <typename,typename,typename> friend class GenericReader; // for parsing
template <typename, typename> friend class GenericValue; // for deep copying
+public:
// Implementation of Handler
bool Null() { new (stack_.template Push<ValueType>()) ValueType(); return true; }
bool Bool(bool b) { new (stack_.template Push<ValueType>()) ValueType(b); return true; }
@@ -2029,6 +2329,14 @@
bool Uint64(uint64_t i) { new (stack_.template Push<ValueType>()) ValueType(i); return true; }
bool Double(double d) { new (stack_.template Push<ValueType>()) ValueType(d); return true; }
+ bool RawNumber(const Ch* str, SizeType length, bool copy) {
+ if (copy)
+ new (stack_.template Push<ValueType>()) ValueType(str, length, GetAllocator());
+ else
+ new (stack_.template Push<ValueType>()) ValueType(str, length);
+ return true;
+ }
+
bool String(const Ch* str, SizeType length, bool copy) {
if (copy)
new (stack_.template Push<ValueType>()) ValueType(str, length, GetAllocator());
@@ -2099,20 +2407,159 @@
}
break;
case kStringType:
- if (rhs.flags_ == kConstStringFlag) {
- flags_ = rhs.flags_;
+ if (rhs.data_.f.flags == kConstStringFlag) {
+ data_.f.flags = rhs.data_.f.flags;
data_ = *reinterpret_cast<const Data*>(&rhs.data_);
} else {
SetStringRaw(StringRef(rhs.GetString(), rhs.GetStringLength()), allocator);
}
break;
default:
- flags_ = rhs.flags_;
+ data_.f.flags = rhs.data_.f.flags;
data_ = *reinterpret_cast<const Data*>(&rhs.data_);
break;
}
}
+//! Helper class for accessing Value of array type.
+/*!
+ Instance of this helper class is obtained by \c GenericValue::GetArray().
+ In addition to all APIs for array type, it provides range-based for loop if \c RAPIDJSON_HAS_CXX11_RANGE_FOR=1.
+*/
+template <bool Const, typename ValueT>
+class GenericArray {
+public:
+ typedef GenericArray<true, ValueT> ConstArray;
+ typedef GenericArray<false, ValueT> Array;
+ typedef ValueT PlainType;
+ typedef typename internal::MaybeAddConst<Const,PlainType>::Type ValueType;
+ typedef ValueType* ValueIterator; // This may be const or non-const iterator
+ typedef const ValueT* ConstValueIterator;
+ typedef typename ValueType::AllocatorType AllocatorType;
+ typedef typename ValueType::StringRefType StringRefType;
+
+ template <typename, typename>
+ friend class GenericValue;
+
+ GenericArray(const GenericArray& rhs) : value_(rhs.value_) {}
+ GenericArray& operator=(const GenericArray& rhs) { value_ = rhs.value_; return *this; }
+ ~GenericArray() {}
+
+ SizeType Size() const { return value_.Size(); }
+ SizeType Capacity() const { return value_.Capacity(); }
+ bool Empty() const { return value_.Empty(); }
+ void Clear() const { value_.Clear(); }
+ ValueType& operator[](SizeType index) const { return value_[index]; }
+ ValueIterator Begin() const { return value_.Begin(); }
+ ValueIterator End() const { return value_.End(); }
+ GenericArray Reserve(SizeType newCapacity, AllocatorType &allocator) const { value_.Reserve(newCapacity, allocator); return *this; }
+ GenericArray PushBack(ValueType& value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; }
+#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
+ GenericArray PushBack(ValueType&& value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; }
+#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS
+ GenericArray PushBack(StringRefType value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; }
+ template <typename T> RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T>, internal::IsGenericValue<T> >), (const GenericArray&)) PushBack(T value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; }
+ GenericArray PopBack() const { value_.PopBack(); return *this; }
+ ValueIterator Erase(ConstValueIterator pos) const { return value_.Erase(pos); }
+ ValueIterator Erase(ConstValueIterator first, ConstValueIterator last) const { return value_.Erase(first, last); }
+
+#if RAPIDJSON_HAS_CXX11_RANGE_FOR
+ ValueIterator begin() const { return value_.Begin(); }
+ ValueIterator end() const { return value_.End(); }
+#endif
+
+private:
+ GenericArray();
+ GenericArray(ValueType& value) : value_(value) {}
+ ValueType& value_;
+};
+
+//! Helper class for accessing Value of object type.
+/*!
+ Instance of this helper class is obtained by \c GenericValue::GetObject().
+ In addition to all APIs for array type, it provides range-based for loop if \c RAPIDJSON_HAS_CXX11_RANGE_FOR=1.
+*/
+template <bool Const, typename ValueT>
+class GenericObject {
+public:
+ typedef GenericObject<true, ValueT> ConstObject;
+ typedef GenericObject<false, ValueT> Object;
+ typedef ValueT PlainType;
+ typedef typename internal::MaybeAddConst<Const,PlainType>::Type ValueType;
+ typedef GenericMemberIterator<Const, typename ValueT::EncodingType, typename ValueT::AllocatorType> MemberIterator; // This may be const or non-const iterator
+ typedef GenericMemberIterator<true, typename ValueT::EncodingType, typename ValueT::AllocatorType> ConstMemberIterator;
+ typedef typename ValueType::AllocatorType AllocatorType;
+ typedef typename ValueType::StringRefType StringRefType;
+ typedef typename ValueType::EncodingType EncodingType;
+ typedef typename ValueType::Ch Ch;
+
+ template <typename, typename>
+ friend class GenericValue;
+
+ GenericObject(const GenericObject& rhs) : value_(rhs.value_) {}
+ GenericObject& operator=(const GenericObject& rhs) { value_ = rhs.value_; return *this; }
+ ~GenericObject() {}
+
+ SizeType MemberCount() const { return value_.MemberCount(); }
+ bool ObjectEmpty() const { return value_.ObjectEmpty(); }
+ template <typename T> ValueType& operator[](T* name) const { return value_[name]; }
+ template <typename SourceAllocator> ValueType& operator[](const GenericValue<EncodingType, SourceAllocator>& name) const { return value_[name]; }
+#if RAPIDJSON_HAS_STDSTRING
+ ValueType& operator[](const std::basic_string<Ch>& name) const { return value_[name]; }
+#endif
+ MemberIterator MemberBegin() const { return value_.MemberBegin(); }
+ MemberIterator MemberEnd() const { return value_.MemberEnd(); }
+ bool HasMember(const Ch* name) const { return value_.HasMember(name); }
+#if RAPIDJSON_HAS_STDSTRING
+ bool HasMember(const std::basic_string<Ch>& name) const { return value_.HasMember(name); }
+#endif
+ template <typename SourceAllocator> bool HasMember(const GenericValue<EncodingType, SourceAllocator>& name) const { return value_.HasMember(name); }
+ MemberIterator FindMember(const Ch* name) const { return value_.FindMember(name); }
+ template <typename SourceAllocator> MemberIterator FindMember(const GenericValue<EncodingType, SourceAllocator>& name) const { return value_.FindMember(name); }
+#if RAPIDJSON_HAS_STDSTRING
+ MemberIterator FindMember(const std::basic_string<Ch>& name) const { return value_.FindMember(name); }
+#endif
+ GenericObject AddMember(ValueType& name, ValueType& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; }
+ GenericObject AddMember(ValueType& name, StringRefType value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; }
+#if RAPIDJSON_HAS_STDSTRING
+ GenericObject AddMember(ValueType& name, std::basic_string<Ch>& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; }
+#endif
+ template <typename T> RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T>, internal::IsGenericValue<T> >), (ValueType&)) AddMember(ValueType& name, T value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; }
+#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
+ GenericObject AddMember(ValueType&& name, ValueType&& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; }
+ GenericObject AddMember(ValueType&& name, ValueType& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; }
+ GenericObject AddMember(ValueType& name, ValueType&& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; }
+ GenericObject AddMember(StringRefType name, ValueType&& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; }
+#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS
+ GenericObject AddMember(StringRefType name, ValueType& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; }
+ GenericObject AddMember(StringRefType name, StringRefType value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; }
+ template <typename T> RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T>, internal::IsGenericValue<T> >), (GenericObject)) AddMember(StringRefType name, T value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; }
+ void RemoveAllMembers() { return value_.RemoveAllMembers(); }
+ bool RemoveMember(const Ch* name) const { return value_.RemoveMember(name); }
+#if RAPIDJSON_HAS_STDSTRING
+ bool RemoveMember(const std::basic_string<Ch>& name) const { return value_.RemoveMember(name); }
+#endif
+ template <typename SourceAllocator> bool RemoveMember(const GenericValue<EncodingType, SourceAllocator>& name) const { return value_.RemoveMember(name); }
+ MemberIterator RemoveMember(MemberIterator m) const { return value_.RemoveMember(m); }
+ MemberIterator EraseMember(ConstMemberIterator pos) const { return value_.EraseMember(pos); }
+ MemberIterator EraseMember(ConstMemberIterator first, ConstMemberIterator last) const { return value_.EraseMember(first, last); }
+ bool EraseMember(const Ch* name) const { return value_.EraseMember(name); }
+#if RAPIDJSON_HAS_STDSTRING
+ bool EraseMember(const std::basic_string<Ch>& name) const { return EraseMember(ValueType(StringRef(name))); }
+#endif
+ template <typename SourceAllocator> bool EraseMember(const GenericValue<EncodingType, SourceAllocator>& name) const { return value_.EraseMember(name); }
+
+#if RAPIDJSON_HAS_CXX11_RANGE_FOR
+ MemberIterator begin() const { return value_.MemberBegin(); }
+ MemberIterator end() const { return value_.MemberEnd(); }
+#endif
+
+private:
+ GenericObject();
+ GenericObject(ValueType& value) : value_(value) {}
+ ValueType& value_;
+};
+
RAPIDJSON_NAMESPACE_END
#ifdef _MSC_VER
=== modified file 'shorts/xml2json/rapidjson/encodedstream.h'
--- shorts/xml2json/rapidjson/encodedstream.h 2016-03-04 13:27:57 +0000
+++ shorts/xml2json/rapidjson/encodedstream.h 2016-06-10 15:12:59 +0000
@@ -15,7 +15,8 @@
#ifndef RAPIDJSON_ENCODEDSTREAM_H_
#define RAPIDJSON_ENCODEDSTREAM_H_
-#include "rapidjson.h"
+#include "stream.h"
+#include "memorystream.h"
#ifdef __GNUC__
RAPIDJSON_DIAG_PUSH
@@ -62,6 +63,34 @@
Ch current_;
};
+//! Specialized for UTF8 MemoryStream.
+template <>
+class EncodedInputStream<UTF8<>, MemoryStream> {
+public:
+ typedef UTF8<>::Ch Ch;
+
+ EncodedInputStream(MemoryStream& is) : is_(is) {
+ if (static_cast<unsigned char>(is_.Peek()) == 0xEFu) is_.Take();
+ if (static_cast<unsigned char>(is_.Peek()) == 0xBBu) is_.Take();
+ if (static_cast<unsigned char>(is_.Peek()) == 0xBFu) is_.Take();
+ }
+ Ch Peek() const { return is_.Peek(); }
+ Ch Take() { return is_.Take(); }
+ size_t Tell() const { return is_.Tell(); }
+
+ // Not implemented
+ void Put(Ch) {}
+ void Flush() {}
+ Ch* PutBegin() { return 0; }
+ size_t PutEnd(Ch*) { return 0; }
+
+ MemoryStream& is_;
+
+private:
+ EncodedInputStream(const EncodedInputStream&);
+ EncodedInputStream& operator=(const EncodedInputStream&);
+};
+
//! Output byte stream wrapper with statically bound encoding.
/*!
\tparam Encoding The interpretation of encoding of the stream. Either UTF8, UTF16LE, UTF16BE, UTF32LE, UTF32BE.
=== modified file 'shorts/xml2json/rapidjson/encodings.h'
--- shorts/xml2json/rapidjson/encodings.h 2016-03-04 13:27:57 +0000
+++ shorts/xml2json/rapidjson/encodings.h 2016-06-10 15:12:59 +0000
@@ -120,6 +120,28 @@
}
}
+ template<typename OutputStream>
+ static void EncodeUnsafe(OutputStream& os, unsigned codepoint) {
+ if (codepoint <= 0x7F)
+ PutUnsafe(os, static_cast<Ch>(codepoint & 0xFF));
+ else if (codepoint <= 0x7FF) {
+ PutUnsafe(os, static_cast<Ch>(0xC0 | ((codepoint >> 6) & 0xFF)));
+ PutUnsafe(os, static_cast<Ch>(0x80 | ((codepoint & 0x3F))));
+ }
+ else if (codepoint <= 0xFFFF) {
+ PutUnsafe(os, static_cast<Ch>(0xE0 | ((codepoint >> 12) & 0xFF)));
+ PutUnsafe(os, static_cast<Ch>(0x80 | ((codepoint >> 6) & 0x3F)));
+ PutUnsafe(os, static_cast<Ch>(0x80 | (codepoint & 0x3F)));
+ }
+ else {
+ RAPIDJSON_ASSERT(codepoint <= 0x10FFFF);
+ PutUnsafe(os, static_cast<Ch>(0xF0 | ((codepoint >> 18) & 0xFF)));
+ PutUnsafe(os, static_cast<Ch>(0x80 | ((codepoint >> 12) & 0x3F)));
+ PutUnsafe(os, static_cast<Ch>(0x80 | ((codepoint >> 6) & 0x3F)));
+ PutUnsafe(os, static_cast<Ch>(0x80 | (codepoint & 0x3F)));
+ }
+ }
+
template <typename InputStream>
static bool Decode(InputStream& is, unsigned* codepoint) {
#define COPY() c = is.Take(); *codepoint = (*codepoint << 6) | (static_cast<unsigned char>(c) & 0x3Fu)
@@ -261,6 +283,22 @@
}
}
+
+ template<typename OutputStream>
+ static void EncodeUnsafe(OutputStream& os, unsigned codepoint) {
+ RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2);
+ if (codepoint <= 0xFFFF) {
+ RAPIDJSON_ASSERT(codepoint < 0xD800 || codepoint > 0xDFFF); // Code point itself cannot be surrogate pair
+ PutUnsafe(os, static_cast<typename OutputStream::Ch>(codepoint));
+ }
+ else {
+ RAPIDJSON_ASSERT(codepoint <= 0x10FFFF);
+ unsigned v = codepoint - 0x10000;
+ PutUnsafe(os, static_cast<typename OutputStream::Ch>((v >> 10) | 0xD800));
+ PutUnsafe(os, (v & 0x3FF) | 0xDC00);
+ }
+ }
+
template <typename InputStream>
static bool Decode(InputStream& is, unsigned* codepoint) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 2);
@@ -386,6 +424,13 @@
os.Put(codepoint);
}
+ template<typename OutputStream>
+ static void EncodeUnsafe(OutputStream& os, unsigned codepoint) {
+ RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 4);
+ RAPIDJSON_ASSERT(codepoint <= 0x10FFFF);
+ PutUnsafe(os, codepoint);
+ }
+
template <typename InputStream>
static bool Decode(InputStream& is, unsigned* codepoint) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 4);
@@ -501,6 +546,12 @@
os.Put(static_cast<Ch>(codepoint & 0xFF));
}
+ template<typename OutputStream>
+ static void EncodeUnsafe(OutputStream& os, unsigned codepoint) {
+ RAPIDJSON_ASSERT(codepoint <= 0x7F);
+ PutUnsafe(os, static_cast<Ch>(codepoint & 0xFF));
+ }
+
template <typename InputStream>
static bool Decode(InputStream& is, unsigned* codepoint) {
uint8_t c = static_cast<uint8_t>(is.Take());
@@ -571,6 +622,13 @@
(*f[os.GetType()])(os, codepoint);
}
+ template<typename OutputStream>
+ RAPIDJSON_FORCEINLINE static void EncodeUnsafe(OutputStream& os, unsigned codepoint) {
+ typedef void (*EncodeFunc)(OutputStream&, unsigned);
+ static const EncodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(EncodeUnsafe) };
+ (*f[os.GetType()])(os, codepoint);
+ }
+
template <typename InputStream>
RAPIDJSON_FORCEINLINE static bool Decode(InputStream& is, unsigned* codepoint) {
typedef bool (*DecodeFunc)(InputStream&, unsigned*);
@@ -604,6 +662,15 @@
return true;
}
+ template<typename InputStream, typename OutputStream>
+ RAPIDJSON_FORCEINLINE static bool TranscodeUnsafe(InputStream& is, OutputStream& os) {
+ unsigned codepoint;
+ if (!SourceEncoding::Decode(is, &codepoint))
+ return false;
+ TargetEncoding::EncodeUnsafe(os, codepoint);
+ return true;
+ }
+
//! Validate one Unicode codepoint from an encoded stream.
template<typename InputStream, typename OutputStream>
RAPIDJSON_FORCEINLINE static bool Validate(InputStream& is, OutputStream& os) {
@@ -611,6 +678,10 @@
}
};
+// Forward declaration.
+template<typename Stream>
+inline void PutUnsafe(Stream& stream, typename Stream::Ch c);
+
//! Specialization of Transcoder with same source and target encoding.
template<typename Encoding>
struct Transcoder<Encoding, Encoding> {
@@ -621,6 +692,12 @@
}
template<typename InputStream, typename OutputStream>
+ RAPIDJSON_FORCEINLINE static bool TranscodeUnsafe(InputStream& is, OutputStream& os) {
+ PutUnsafe(os, is.Take()); // Just copy one code unit. This semantic is different from primary template class.
+ return true;
+ }
+
+ template<typename InputStream, typename OutputStream>
RAPIDJSON_FORCEINLINE static bool Validate(InputStream& is, OutputStream& os) {
return Encoding::Validate(is, os); // source/target encoding are the same
}
=== modified file 'shorts/xml2json/rapidjson/filereadstream.h'
--- shorts/xml2json/rapidjson/filereadstream.h 2016-03-04 13:27:57 +0000
+++ shorts/xml2json/rapidjson/filereadstream.h 2016-06-10 15:12:59 +0000
@@ -15,7 +15,7 @@
#ifndef RAPIDJSON_FILEREADSTREAM_H_
#define RAPIDJSON_FILEREADSTREAM_H_
-#include "rapidjson.h"
+#include "stream.h"
#include <cstdio>
#ifdef __clang__
=== modified file 'shorts/xml2json/rapidjson/filewritestream.h'
--- shorts/xml2json/rapidjson/filewritestream.h 2016-03-04 13:27:57 +0000
+++ shorts/xml2json/rapidjson/filewritestream.h 2016-06-10 15:12:59 +0000
@@ -15,7 +15,7 @@
#ifndef RAPIDJSON_FILEWRITESTREAM_H_
#define RAPIDJSON_FILEWRITESTREAM_H_
-#include "rapidjson.h"
+#include "stream.h"
#include <cstdio>
#ifdef __clang__
=== added file 'shorts/xml2json/rapidjson/fwd.h'
--- shorts/xml2json/rapidjson/fwd.h 1970-01-01 00:00:00 +0000
+++ shorts/xml2json/rapidjson/fwd.h 2016-06-10 15:12:59 +0000
@@ -0,0 +1,151 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+#ifndef RAPIDJSON_FWD_H_
+#define RAPIDJSON_FWD_H_
+
+#include "rapidjson.h"
+
+RAPIDJSON_NAMESPACE_BEGIN
+
+// encodings.h
+
+template<typename CharType> struct UTF8;
+template<typename CharType> struct UTF16;
+template<typename CharType> struct UTF16BE;
+template<typename CharType> struct UTF16LE;
+template<typename CharType> struct UTF32;
+template<typename CharType> struct UTF32BE;
+template<typename CharType> struct UTF32LE;
+template<typename CharType> struct ASCII;
+template<typename CharType> struct AutoUTF;
+
+template<typename SourceEncoding, typename TargetEncoding>
+struct Transcoder;
+
+// allocators.h
+
+class CrtAllocator;
+
+template <typename BaseAllocator>
+class MemoryPoolAllocator;
+
+// stream.h
+
+template <typename Encoding>
+struct GenericStringStream;
+
+typedef GenericStringStream<UTF8<char> > StringStream;
+
+template <typename Encoding>
+struct GenericInsituStringStream;
+
+typedef GenericInsituStringStream<UTF8<char> > InsituStringStream;
+
+// stringbuffer.h
+
+template <typename Encoding, typename Allocator>
+class GenericStringBuffer;
+
+typedef GenericStringBuffer<UTF8<char>, CrtAllocator> StringBuffer;
+
+// filereadstream.h
+
+class FileReadStream;
+
+// filewritestream.h
+
+class FileWriteStream;
+
+// memorybuffer.h
+
+template <typename Allocator>
+struct GenericMemoryBuffer;
+
+typedef GenericMemoryBuffer<CrtAllocator> MemoryBuffer;
+
+// memorystream.h
+
+struct MemoryStream;
+
+// reader.h
+
+template<typename Encoding, typename Derived>
+struct BaseReaderHandler;
+
+template <typename SourceEncoding, typename TargetEncoding, typename StackAllocator>
+class GenericReader;
+
+typedef GenericReader<UTF8<char>, UTF8<char>, CrtAllocator> Reader;
+
+// writer.h
+
+template<typename OutputStream, typename SourceEncoding, typename TargetEncoding, typename StackAllocator, unsigned writeFlags>
+class Writer;
+
+// prettywriter.h
+
+template<typename OutputStream, typename SourceEncoding, typename TargetEncoding, typename StackAllocator, unsigned writeFlags>
+class PrettyWriter;
+
+// document.h
+
+template <typename Encoding, typename Allocator>
+struct GenericMember;
+
+template <bool Const, typename Encoding, typename Allocator>
+class GenericMemberIterator;
+
+template<typename CharType>
+struct GenericStringRef;
+
+template <typename Encoding, typename Allocator>
+class GenericValue;
+
+typedef GenericValue<UTF8<char>, MemoryPoolAllocator<CrtAllocator> > Value;
+
+template <typename Encoding, typename Allocator, typename StackAllocator>
+class GenericDocument;
+
+typedef GenericDocument<UTF8<char>, MemoryPoolAllocator<CrtAllocator>, CrtAllocator> Document;
+
+// pointer.h
+
+template <typename ValueType, typename Allocator>
+class GenericPointer;
+
+typedef GenericPointer<Value, CrtAllocator> Pointer;
+
+// schema.h
+
+template <typename SchemaDocumentType>
+class IGenericRemoteSchemaDocumentProvider;
+
+template <typename ValueT, typename Allocator>
+class GenericSchemaDocument;
+
+typedef GenericSchemaDocument<Value, CrtAllocator> SchemaDocument;
+typedef IGenericRemoteSchemaDocumentProvider<SchemaDocument> IRemoteSchemaDocumentProvider;
+
+template <
+ typename SchemaDocumentType,
+ typename OutputHandler,
+ typename StateAllocator>
+class GenericSchemaValidator;
+
+typedef GenericSchemaValidator<SchemaDocument, BaseReaderHandler<UTF8<char>, void>, CrtAllocator> SchemaValidator;
+
+RAPIDJSON_NAMESPACE_END
+
+#endif // RAPIDJSON_RAPIDJSONFWD_H_
=== modified file 'shorts/xml2json/rapidjson/internal/biginteger.h' (properties changed: -x to +x)
=== modified file 'shorts/xml2json/rapidjson/internal/diyfp.h'
--- shorts/xml2json/rapidjson/internal/diyfp.h 2016-03-04 13:27:57 +0000
+++ shorts/xml2json/rapidjson/internal/diyfp.h 2016-06-10 15:12:59 +0000
@@ -41,7 +41,7 @@
#endif
struct DiyFp {
- DiyFp() {}
+ DiyFp() : f(), e() {}
DiyFp(uint64_t fp, int exp) : f(fp), e(exp) {}
=== modified file 'shorts/xml2json/rapidjson/internal/dtoa.h'
--- shorts/xml2json/rapidjson/internal/dtoa.h 2016-03-04 13:27:57 +0000
+++ shorts/xml2json/rapidjson/internal/dtoa.h 2016-06-10 15:12:59 +0000
@@ -29,6 +29,7 @@
#ifdef __GNUC__
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(effc++)
+RAPIDJSON_DIAG_OFF(array-bounds) // some gcc versions generate wrong warnings https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59124
#endif
inline void GrisuRound(char* buffer, int len, uint64_t delta, uint64_t rest, uint64_t ten_kappa, uint64_t wp_w) {
@@ -145,10 +146,10 @@
return buffer;
}
-inline char* Prettify(char* buffer, int length, int k) {
+inline char* Prettify(char* buffer, int length, int k, int maxDecimalPlaces) {
const int kk = length + k; // 10^(kk-1) <= v < 10^kk
- if (length <= kk && kk <= 21) {
+ if (0 <= k && kk <= 21) {
// 1234e7 -> 12340000000
for (int i = length; i < kk; i++)
buffer[i] = '0';
@@ -160,7 +161,16 @@
// 1234e-2 -> 12.34
std::memmove(&buffer[kk + 1], &buffer[kk], static_cast<size_t>(length - kk));
buffer[kk] = '.';
- return &buffer[length + 1];
+ if (0 > k + maxDecimalPlaces) {
+ // When maxDecimalPlaces = 2, 1.2345 -> 1.23, 1.102 -> 1.1
+ // Remove extra trailing zeros (at least one) after truncation.
+ for (int i = kk + maxDecimalPlaces; i > kk + 1; i--)
+ if (buffer[i] != '0')
+ return &buffer[i + 1];
+ return &buffer[kk + 2]; // Reserve one zero
+ }
+ else
+ return &buffer[length + 1];
}
else if (-6 < kk && kk <= 0) {
// 1234e-6 -> 0.001234
@@ -170,7 +180,23 @@
buffer[1] = '.';
for (int i = 2; i < offset; i++)
buffer[i] = '0';
- return &buffer[length + offset];
+ if (length - kk > maxDecimalPlaces) {
+ // When maxDecimalPlaces = 2, 0.123 -> 0.12, 0.102 -> 0.1
+ // Remove extra trailing zeros (at least one) after truncation.
+ for (int i = maxDecimalPlaces + 1; i > 2; i--)
+ if (buffer[i] != '0')
+ return &buffer[i + 1];
+ return &buffer[3]; // Reserve one zero
+ }
+ else
+ return &buffer[length + offset];
+ }
+ else if (kk < -maxDecimalPlaces) {
+ // Truncate to zero
+ buffer[0] = '0';
+ buffer[1] = '.';
+ buffer[2] = '0';
+ return &buffer[3];
}
else if (length == 1) {
// 1e30
@@ -186,7 +212,8 @@
}
}
-inline char* dtoa(double value, char* buffer) {
+inline char* dtoa(double value, char* buffer, int maxDecimalPlaces = 324) {
+ RAPIDJSON_ASSERT(maxDecimalPlaces >= 1);
Double d(value);
if (d.IsZero()) {
if (d.Sign())
@@ -203,7 +230,7 @@
}
int length, K;
Grisu2(value, buffer, &length, &K);
- return Prettify(buffer, length, K);
+ return Prettify(buffer, length, K, maxDecimalPlaces);
}
}
=== modified file 'shorts/xml2json/rapidjson/internal/ieee754.h'
--- shorts/xml2json/rapidjson/internal/ieee754.h 2016-03-04 13:27:57 +0000
+++ shorts/xml2json/rapidjson/internal/ieee754.h 2016-06-10 15:12:59 +0000
@@ -40,6 +40,7 @@
bool IsNan() const { return (u_ & kExponentMask) == kExponentMask && Significand() != 0; }
bool IsInf() const { return (u_ & kExponentMask) == kExponentMask && Significand() == 0; }
+ bool IsNanOrInf() const { return (u_ & kExponentMask) == kExponentMask; }
bool IsNormal() const { return (u_ & kExponentMask) != 0 || Significand() == 0; }
bool IsZero() const { return (u_ & (kExponentMask | kSignificandMask)) == 0; }
=== added file 'shorts/xml2json/rapidjson/internal/regex.h'
--- shorts/xml2json/rapidjson/internal/regex.h 1970-01-01 00:00:00 +0000
+++ shorts/xml2json/rapidjson/internal/regex.h 2016-06-10 15:12:59 +0000
@@ -0,0 +1,705 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+#ifndef RAPIDJSON_INTERNAL_REGEX_H_
+#define RAPIDJSON_INTERNAL_REGEX_H_
+
+#include "../allocators.h"
+#include "../stream.h"
+#include "stack.h"
+
+#ifdef __clang__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(padded)
+RAPIDJSON_DIAG_OFF(switch-enum)
+RAPIDJSON_DIAG_OFF(implicit-fallthrough)
+#endif
+
+#ifdef __GNUC__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(effc++)
+#endif
+
+#ifdef _MSC_VER
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated
+#endif
+
+#ifndef RAPIDJSON_REGEX_VERBOSE
+#define RAPIDJSON_REGEX_VERBOSE 0
+#endif
+
+RAPIDJSON_NAMESPACE_BEGIN
+namespace internal {
+
+///////////////////////////////////////////////////////////////////////////////
+// GenericRegex
+
+static const SizeType kRegexInvalidState = ~SizeType(0); //!< Represents an invalid index in GenericRegex::State::out, out1
+static const SizeType kRegexInvalidRange = ~SizeType(0);
+
+//! Regular expression engine with subset of ECMAscript grammar.
+/*!
+ Supported regular expression syntax:
+ - \c ab Concatenation
+ - \c a|b Alternation
+ - \c a? Zero or one
+ - \c a* Zero or more
+ - \c a+ One or more
+ - \c a{3} Exactly 3 times
+ - \c a{3,} At least 3 times
+ - \c a{3,5} 3 to 5 times
+ - \c (ab) Grouping
+ - \c ^a At the beginning
+ - \c a$ At the end
+ - \c . Any character
+ - \c [abc] Character classes
+ - \c [a-c] Character class range
+ - \c [a-z0-9_] Character class combination
+ - \c [^abc] Negated character classes
+ - \c [^a-c] Negated character class range
+ - \c [\b] Backspace (U+0008)
+ - \c \\| \\\\ ... Escape characters
+ - \c \\f Form feed (U+000C)
+ - \c \\n Line feed (U+000A)
+ - \c \\r Carriage return (U+000D)
+ - \c \\t Tab (U+0009)
+ - \c \\v Vertical tab (U+000B)
+
+ \note This is a Thompson NFA engine, implemented with reference to
+ Cox, Russ. "Regular Expression Matching Can Be Simple And Fast (but is slow in Java, Perl, PHP, Python, Ruby,...).",
+ https://swtch.com/~rsc/regexp/regexp1.html
+*/
+template <typename Encoding, typename Allocator = CrtAllocator>
+class GenericRegex {
+public:
+ typedef typename Encoding::Ch Ch;
+
+ GenericRegex(const Ch* source, Allocator* allocator = 0) :
+ states_(allocator, 256), ranges_(allocator, 256), root_(kRegexInvalidState), stateCount_(), rangeCount_(),
+ stateSet_(), state0_(allocator, 0), state1_(allocator, 0), anchorBegin_(), anchorEnd_()
+ {
+ GenericStringStream<Encoding> ss(source);
+ DecodedStream<GenericStringStream<Encoding> > ds(ss);
+ Parse(ds);
+ }
+
+ ~GenericRegex() {
+ Allocator::Free(stateSet_);
+ }
+
+ bool IsValid() const {
+ return root_ != kRegexInvalidState;
+ }
+
+ template <typename InputStream>
+ bool Match(InputStream& is) const {
+ return SearchWithAnchoring(is, true, true);
+ }
+
+ bool Match(const Ch* s) const {
+ GenericStringStream<Encoding> is(s);
+ return Match(is);
+ }
+
+ template <typename InputStream>
+ bool Search(InputStream& is) const {
+ return SearchWithAnchoring(is, anchorBegin_, anchorEnd_);
+ }
+
+ bool Search(const Ch* s) const {
+ GenericStringStream<Encoding> is(s);
+ return Search(is);
+ }
+
+private:
+ enum Operator {
+ kZeroOrOne,
+ kZeroOrMore,
+ kOneOrMore,
+ kConcatenation,
+ kAlternation,
+ kLeftParenthesis
+ };
+
+ static const unsigned kAnyCharacterClass = 0xFFFFFFFF; //!< For '.'
+ static const unsigned kRangeCharacterClass = 0xFFFFFFFE;
+ static const unsigned kRangeNegationFlag = 0x80000000;
+
+ struct Range {
+ unsigned start; //
+ unsigned end;
+ SizeType next;
+ };
+
+ struct State {
+ SizeType out; //!< Equals to kInvalid for matching state
+ SizeType out1; //!< Equals to non-kInvalid for split
+ SizeType rangeStart;
+ unsigned codepoint;
+ };
+
+ struct Frag {
+ Frag(SizeType s, SizeType o, SizeType m) : start(s), out(o), minIndex(m) {}
+ SizeType start;
+ SizeType out; //!< link-list of all output states
+ SizeType minIndex;
+ };
+
+ template <typename SourceStream>
+ class DecodedStream {
+ public:
+ DecodedStream(SourceStream& ss) : ss_(ss), codepoint_() { Decode(); }
+ unsigned Peek() { return codepoint_; }
+ unsigned Take() {
+ unsigned c = codepoint_;
+ if (c) // No further decoding when '\0'
+ Decode();
+ return c;
+ }
+
+ private:
+ void Decode() {
+ if (!Encoding::Decode(ss_, &codepoint_))
+ codepoint_ = 0;
+ }
+
+ SourceStream& ss_;
+ unsigned codepoint_;
+ };
+
+ State& GetState(SizeType index) {
+ RAPIDJSON_ASSERT(index < stateCount_);
+ return states_.template Bottom<State>()[index];
+ }
+
+ const State& GetState(SizeType index) const {
+ RAPIDJSON_ASSERT(index < stateCount_);
+ return states_.template Bottom<State>()[index];
+ }
+
+ Range& GetRange(SizeType index) {
+ RAPIDJSON_ASSERT(index < rangeCount_);
+ return ranges_.template Bottom<Range>()[index];
+ }
+
+ const Range& GetRange(SizeType index) const {
+ RAPIDJSON_ASSERT(index < rangeCount_);
+ return ranges_.template Bottom<Range>()[index];
+ }
+
+ template <typename InputStream>
+ void Parse(DecodedStream<InputStream>& ds) {
+ Allocator allocator;
+ Stack<Allocator> operandStack(&allocator, 256); // Frag
+ Stack<Allocator> operatorStack(&allocator, 256); // Operator
+ Stack<Allocator> atomCountStack(&allocator, 256); // unsigned (Atom per parenthesis)
+
+ *atomCountStack.template Push<unsigned>() = 0;
+
+ unsigned codepoint;
+ while (ds.Peek() != 0) {
+ switch (codepoint = ds.Take()) {
+ case '^':
+ anchorBegin_ = true;
+ break;
+
+ case '$':
+ anchorEnd_ = true;
+ break;
+
+ case '|':
+ while (!operatorStack.Empty() && *operatorStack.template Top<Operator>() < kAlternation)
+ if (!Eval(operandStack, *operatorStack.template Pop<Operator>(1)))
+ return;
+ *operatorStack.template Push<Operator>() = kAlternation;
+ *atomCountStack.template Top<unsigned>() = 0;
+ break;
+
+ case '(':
+ *operatorStack.template Push<Operator>() = kLeftParenthesis;
+ *atomCountStack.template Push<unsigned>() = 0;
+ break;
+
+ case ')':
+ while (!operatorStack.Empty() && *operatorStack.template Top<Operator>() != kLeftParenthesis)
+ if (!Eval(operandStack, *operatorStack.template Pop<Operator>(1)))
+ return;
+ if (operatorStack.Empty())
+ return;
+ operatorStack.template Pop<Operator>(1);
+ atomCountStack.template Pop<unsigned>(1);
+ ImplicitConcatenation(atomCountStack, operatorStack);
+ break;
+
+ case '?':
+ if (!Eval(operandStack, kZeroOrOne))
+ return;
+ break;
+
+ case '*':
+ if (!Eval(operandStack, kZeroOrMore))
+ return;
+ break;
+
+ case '+':
+ if (!Eval(operandStack, kOneOrMore))
+ return;
+ break;
+
+ case '{':
+ {
+ unsigned n, m;
+ if (!ParseUnsigned(ds, &n))
+ return;
+
+ if (ds.Peek() == ',') {
+ ds.Take();
+ if (ds.Peek() == '}')
+ m = kInfinityQuantifier;
+ else if (!ParseUnsigned(ds, &m) || m < n)
+ return;
+ }
+ else
+ m = n;
+
+ if (!EvalQuantifier(operandStack, n, m) || ds.Peek() != '}')
+ return;
+ ds.Take();
+ }
+ break;
+
+ case '.':
+ PushOperand(operandStack, kAnyCharacterClass);
+ ImplicitConcatenation(atomCountStack, operatorStack);
+ break;
+
+ case '[':
+ {
+ SizeType range;
+ if (!ParseRange(ds, &range))
+ return;
+ SizeType s = NewState(kRegexInvalidState, kRegexInvalidState, kRangeCharacterClass);
+ GetState(s).rangeStart = range;
+ *operandStack.template Push<Frag>() = Frag(s, s, s);
+ }
+ ImplicitConcatenation(atomCountStack, operatorStack);
+ break;
+
+ case '\\': // Escape character
+ if (!CharacterEscape(ds, &codepoint))
+ return; // Unsupported escape character
+ // fall through to default
+
+ default: // Pattern character
+ PushOperand(operandStack, codepoint);
+ ImplicitConcatenation(atomCountStack, operatorStack);
+ }
+ }
+
+ while (!operatorStack.Empty())
+ if (!Eval(operandStack, *operatorStack.template Pop<Operator>(1)))
+ return;
+
+ // Link the operand to matching state.
+ if (operandStack.GetSize() == sizeof(Frag)) {
+ Frag* e = operandStack.template Pop<Frag>(1);
+ Patch(e->out, NewState(kRegexInvalidState, kRegexInvalidState, 0));
+ root_ = e->start;
+
+#if RAPIDJSON_REGEX_VERBOSE
+ printf("root: %d\n", root_);
+ for (SizeType i = 0; i < stateCount_ ; i++) {
+ State& s = GetState(i);
+ printf("[%2d] out: %2d out1: %2d c: '%c'\n", i, s.out, s.out1, (char)s.codepoint);
+ }
+ printf("\n");
+#endif
+ }
+
+ // Preallocate buffer for SearchWithAnchoring()
+ RAPIDJSON_ASSERT(stateSet_ == 0);
+ if (stateCount_ > 0) {
+ stateSet_ = static_cast<unsigned*>(states_.GetAllocator().Malloc(GetStateSetSize()));
+ state0_.template Reserve<SizeType>(stateCount_);
+ state1_.template Reserve<SizeType>(stateCount_);
+ }
+ }
+
+ SizeType NewState(SizeType out, SizeType out1, unsigned codepoint) {
+ State* s = states_.template Push<State>();
+ s->out = out;
+ s->out1 = out1;
+ s->codepoint = codepoint;
+ s->rangeStart = kRegexInvalidRange;
+ return stateCount_++;
+ }
+
+ void PushOperand(Stack<Allocator>& operandStack, unsigned codepoint) {
+ SizeType s = NewState(kRegexInvalidState, kRegexInvalidState, codepoint);
+ *operandStack.template Push<Frag>() = Frag(s, s, s);
+ }
+
+ void ImplicitConcatenation(Stack<Allocator>& atomCountStack, Stack<Allocator>& operatorStack) {
+ if (*atomCountStack.template Top<unsigned>())
+ *operatorStack.template Push<Operator>() = kConcatenation;
+ (*atomCountStack.template Top<unsigned>())++;
+ }
+
+ SizeType Append(SizeType l1, SizeType l2) {
+ SizeType old = l1;
+ while (GetState(l1).out != kRegexInvalidState)
+ l1 = GetState(l1).out;
+ GetState(l1).out = l2;
+ return old;
+ }
+
+ void Patch(SizeType l, SizeType s) {
+ for (SizeType next; l != kRegexInvalidState; l = next) {
+ next = GetState(l).out;
+ GetState(l).out = s;
+ }
+ }
+
+ bool Eval(Stack<Allocator>& operandStack, Operator op) {
+ switch (op) {
+ case kConcatenation:
+ if (operandStack.GetSize() >= sizeof(Frag) * 2) {
+ Frag e2 = *operandStack.template Pop<Frag>(1);
+ Frag e1 = *operandStack.template Pop<Frag>(1);
+ Patch(e1.out, e2.start);
+ *operandStack.template Push<Frag>() = Frag(e1.start, e2.out, Min(e1.minIndex, e2.minIndex));
+ return true;
+ }
+ return false;
+
+ case kAlternation:
+ if (operandStack.GetSize() >= sizeof(Frag) * 2) {
+ Frag e2 = *operandStack.template Pop<Frag>(1);
+ Frag e1 = *operandStack.template Pop<Frag>(1);
+ SizeType s = NewState(e1.start, e2.start, 0);
+ *operandStack.template Push<Frag>() = Frag(s, Append(e1.out, e2.out), Min(e1.minIndex, e2.minIndex));
+ return true;
+ }
+ return false;
+
+ case kZeroOrOne:
+ if (operandStack.GetSize() >= sizeof(Frag)) {
+ Frag e = *operandStack.template Pop<Frag>(1);
+ SizeType s = NewState(kRegexInvalidState, e.start, 0);
+ *operandStack.template Push<Frag>() = Frag(s, Append(e.out, s), e.minIndex);
+ return true;
+ }
+ return false;
+
+ case kZeroOrMore:
+ if (operandStack.GetSize() >= sizeof(Frag)) {
+ Frag e = *operandStack.template Pop<Frag>(1);
+ SizeType s = NewState(kRegexInvalidState, e.start, 0);
+ Patch(e.out, s);
+ *operandStack.template Push<Frag>() = Frag(s, s, e.minIndex);
+ return true;
+ }
+ return false;
+
+ case kOneOrMore:
+ if (operandStack.GetSize() >= sizeof(Frag)) {
+ Frag e = *operandStack.template Pop<Frag>(1);
+ SizeType s = NewState(kRegexInvalidState, e.start, 0);
+ Patch(e.out, s);
+ *operandStack.template Push<Frag>() = Frag(e.start, s, e.minIndex);
+ return true;
+ }
+ return false;
+
+ default:
+ return false;
+ }
+ }
+
+ bool EvalQuantifier(Stack<Allocator>& operandStack, unsigned n, unsigned m) {
+ RAPIDJSON_ASSERT(n <= m);
+ if (operandStack.GetSize() < sizeof(Frag))
+ return false;
+
+ if (n == 0) {
+ if (m == 0) // a{0} not support
+ return false;
+ else if (m == kInfinityQuantifier)
+ Eval(operandStack, kZeroOrMore); // a{0,} -> a*
+ else {
+ Eval(operandStack, kZeroOrOne); // a{0,5} -> a?
+ for (unsigned i = 0; i < m - 1; i++)
+ CloneTopOperand(operandStack); // a{0,5} -> a? a? a? a? a?
+ for (unsigned i = 0; i < m - 1; i++)
+ Eval(operandStack, kConcatenation); // a{0,5} -> a?a?a?a?a?
+ }
+ return true;
+ }
+
+ for (unsigned i = 0; i < n - 1; i++) // a{3} -> a a a
+ CloneTopOperand(operandStack);
+
+ if (m == kInfinityQuantifier)
+ Eval(operandStack, kOneOrMore); // a{3,} -> a a a+
+ else if (m > n) {
+ CloneTopOperand(operandStack); // a{3,5} -> a a a a
+ Eval(operandStack, kZeroOrOne); // a{3,5} -> a a a a?
+ for (unsigned i = n; i < m - 1; i++)
+ CloneTopOperand(operandStack); // a{3,5} -> a a a a? a?
+ for (unsigned i = n; i < m; i++)
+ Eval(operandStack, kConcatenation); // a{3,5} -> a a aa?a?
+ }
+
+ for (unsigned i = 0; i < n - 1; i++)
+ Eval(operandStack, kConcatenation); // a{3} -> aaa, a{3,} -> aaa+, a{3.5} -> aaaa?a?
+
+ return true;
+ }
+
+ static SizeType Min(SizeType a, SizeType b) { return a < b ? a : b; }
+
+ void CloneTopOperand(Stack<Allocator>& operandStack) {
+ const Frag *src = operandStack.template Top<Frag>();
+ SizeType count = stateCount_ - src->minIndex; // Assumes top operand contains states in [src->minIndex, stateCount_)
+ State* s = states_.template Push<State>(count);
+ memcpy(s, &GetState(src->minIndex), count * sizeof(State));
+ for (SizeType j = 0; j < count; j++) {
+ if (s[j].out != kRegexInvalidState)
+ s[j].out += count;
+ if (s[j].out1 != kRegexInvalidState)
+ s[j].out1 += count;
+ }
+ *operandStack.template Push<Frag>() = Frag(src->start + count, src->out + count, src->minIndex + count);
+ stateCount_ += count;
+ }
+
+ template <typename InputStream>
+ bool ParseUnsigned(DecodedStream<InputStream>& ds, unsigned* u) {
+ unsigned r = 0;
+ if (ds.Peek() < '0' || ds.Peek() > '9')
+ return false;
+ while (ds.Peek() >= '0' && ds.Peek() <= '9') {
+ if (r >= 429496729 && ds.Peek() > '5') // 2^32 - 1 = 4294967295
+ return false; // overflow
+ r = r * 10 + (ds.Take() - '0');
+ }
+ *u = r;
+ return true;
+ }
+
+ template <typename InputStream>
+ bool ParseRange(DecodedStream<InputStream>& ds, SizeType* range) {
+ bool isBegin = true;
+ bool negate = false;
+ int step = 0;
+ SizeType start = kRegexInvalidRange;
+ SizeType current = kRegexInvalidRange;
+ unsigned codepoint;
+ while ((codepoint = ds.Take()) != 0) {
+ if (isBegin) {
+ isBegin = false;
+ if (codepoint == '^') {
+ negate = true;
+ continue;
+ }
+ }
+
+ switch (codepoint) {
+ case ']':
+ if (start == kRegexInvalidRange)
+ return false; // Error: nothing inside []
+ if (step == 2) { // Add trailing '-'
+ SizeType r = NewRange('-');
+ RAPIDJSON_ASSERT(current != kRegexInvalidRange);
+ GetRange(current).next = r;
+ }
+ if (negate)
+ GetRange(start).start |= kRangeNegationFlag;
+ *range = start;
+ return true;
+
+ case '\\':
+ if (ds.Peek() == 'b') {
+ ds.Take();
+ codepoint = 0x0008; // Escape backspace character
+ }
+ else if (!CharacterEscape(ds, &codepoint))
+ return false;
+ // fall through to default
+
+ default:
+ switch (step) {
+ case 1:
+ if (codepoint == '-') {
+ step++;
+ break;
+ }
+ // fall through to step 0 for other characters
+
+ case 0:
+ {
+ SizeType r = NewRange(codepoint);
+ if (current != kRegexInvalidRange)
+ GetRange(current).next = r;
+ if (start == kRegexInvalidRange)
+ start = r;
+ current = r;
+ }
+ step = 1;
+ break;
+
+ default:
+ RAPIDJSON_ASSERT(step == 2);
+ GetRange(current).end = codepoint;
+ step = 0;
+ }
+ }
+ }
+ return false;
+ }
+
+ SizeType NewRange(unsigned codepoint) {
+ Range* r = ranges_.template Push<Range>();
+ r->start = r->end = codepoint;
+ r->next = kRegexInvalidRange;
+ return rangeCount_++;
+ }
+
+ template <typename InputStream>
+ bool CharacterEscape(DecodedStream<InputStream>& ds, unsigned* escapedCodepoint) {
+ unsigned codepoint;
+ switch (codepoint = ds.Take()) {
+ case '^':
+ case '$':
+ case '|':
+ case '(':
+ case ')':
+ case '?':
+ case '*':
+ case '+':
+ case '.':
+ case '[':
+ case ']':
+ case '{':
+ case '}':
+ case '\\':
+ *escapedCodepoint = codepoint; return true;
+ case 'f': *escapedCodepoint = 0x000C; return true;
+ case 'n': *escapedCodepoint = 0x000A; return true;
+ case 'r': *escapedCodepoint = 0x000D; return true;
+ case 't': *escapedCodepoint = 0x0009; return true;
+ case 'v': *escapedCodepoint = 0x000B; return true;
+ default:
+ return false; // Unsupported escape character
+ }
+ }
+
+ template <typename InputStream>
+ bool SearchWithAnchoring(InputStream& is, bool anchorBegin, bool anchorEnd) const {
+ RAPIDJSON_ASSERT(IsValid());
+ DecodedStream<InputStream> ds(is);
+
+ state0_.Clear();
+ Stack<Allocator> *current = &state0_, *next = &state1_;
+ const size_t stateSetSize = GetStateSetSize();
+ std::memset(stateSet_, 0, stateSetSize);
+
+ bool matched = AddState(*current, root_);
+ unsigned codepoint;
+ while (!current->Empty() && (codepoint = ds.Take()) != 0) {
+ std::memset(stateSet_, 0, stateSetSize);
+ next->Clear();
+ matched = false;
+ for (const SizeType* s = current->template Bottom<SizeType>(); s != current->template End<SizeType>(); ++s) {
+ const State& sr = GetState(*s);
+ if (sr.codepoint == codepoint ||
+ sr.codepoint == kAnyCharacterClass ||
+ (sr.codepoint == kRangeCharacterClass && MatchRange(sr.rangeStart, codepoint)))
+ {
+ matched = AddState(*next, sr.out) || matched;
+ if (!anchorEnd && matched)
+ return true;
+ }
+ if (!anchorBegin)
+ AddState(*next, root_);
+ }
+ internal::Swap(current, next);
+ }
+
+ return matched;
+ }
+
+ size_t GetStateSetSize() const {
+ return (stateCount_ + 31) / 32 * 4;
+ }
+
+ // Return whether the added states is a match state
+ bool AddState(Stack<Allocator>& l, SizeType index) const {
+ if (index == kRegexInvalidState)
+ return true;
+
+ const State& s = GetState(index);
+ if (s.out1 != kRegexInvalidState) { // Split
+ bool matched = AddState(l, s.out);
+ return AddState(l, s.out1) || matched;
+ }
+ else if (!(stateSet_[index >> 5] & (1 << (index & 31)))) {
+ stateSet_[index >> 5] |= (1 << (index & 31));
+ *l.template PushUnsafe<SizeType>() = index;
+ }
+ return s.out == kRegexInvalidState; // by using PushUnsafe() above, we can ensure s is not validated due to reallocation.
+ }
+
+ bool MatchRange(SizeType rangeIndex, unsigned codepoint) const {
+ bool yes = (GetRange(rangeIndex).start & kRangeNegationFlag) == 0;
+ while (rangeIndex != kRegexInvalidRange) {
+ const Range& r = GetRange(rangeIndex);
+ if (codepoint >= (r.start & ~kRangeNegationFlag) && codepoint <= r.end)
+ return yes;
+ rangeIndex = r.next;
+ }
+ return !yes;
+ }
+
+ Stack<Allocator> states_;
+ Stack<Allocator> ranges_;
+ SizeType root_;
+ SizeType stateCount_;
+ SizeType rangeCount_;
+
+ static const unsigned kInfinityQuantifier = ~0u;
+
+ // For SearchWithAnchoring()
+ uint32_t* stateSet_; // allocated by states_.GetAllocator()
+ mutable Stack<Allocator> state0_;
+ mutable Stack<Allocator> state1_;
+ bool anchorBegin_;
+ bool anchorEnd_;
+};
+
+typedef GenericRegex<UTF8<> > Regex;
+
+} // namespace internal
+RAPIDJSON_NAMESPACE_END
+
+#ifdef __clang__
+RAPIDJSON_DIAG_POP
+#endif
+
+#ifdef _MSC_VER
+RAPIDJSON_DIAG_POP
+#endif
+
+#endif // RAPIDJSON_INTERNAL_REGEX_H_
=== modified file 'shorts/xml2json/rapidjson/internal/stack.h'
--- shorts/xml2json/rapidjson/internal/stack.h 2016-03-04 13:27:57 +0000
+++ shorts/xml2json/rapidjson/internal/stack.h 2016-06-10 15:12:59 +0000
@@ -15,9 +15,14 @@
#ifndef RAPIDJSON_INTERNAL_STACK_H_
#define RAPIDJSON_INTERNAL_STACK_H_
-#include "../rapidjson.h"
+#include "../allocators.h"
#include "swap.h"
+#if defined(__clang__)
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(c++98-compat)
+#endif
+
RAPIDJSON_NAMESPACE_BEGIN
namespace internal {
@@ -33,7 +38,6 @@
// Optimization note: Do not allocate memory for stack_ in constructor.
// Do it lazily when first Push() -> Expand() -> Resize().
Stack(Allocator* allocator, size_t stackCapacity) : allocator_(allocator), ownAllocator_(0), stack_(0), stackTop_(0), stackEnd_(0), initialCapacity_(stackCapacity) {
- RAPIDJSON_ASSERT(stackCapacity > 0);
}
#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
@@ -108,11 +112,21 @@
// Optimization note: try to minimize the size of this function for force inline.
// Expansion is run very infrequently, so it is moved to another (probably non-inline) function.
template<typename T>
- RAPIDJSON_FORCEINLINE T* Push(size_t count = 1) {
+ RAPIDJSON_FORCEINLINE void Reserve(size_t count = 1) {
// Expand the stack if needed
- if (stackTop_ + sizeof(T) * count >= stackEnd_)
+ if (RAPIDJSON_UNLIKELY(stackTop_ + sizeof(T) * count > stackEnd_))
Expand<T>(count);
-
+ }
+
+ template<typename T>
+ RAPIDJSON_FORCEINLINE T* Push(size_t count = 1) {
+ Reserve<T>(count);
+ return PushUnsafe<T>(count);
+ }
+
+ template<typename T>
+ RAPIDJSON_FORCEINLINE T* PushUnsafe(size_t count = 1) {
+ RAPIDJSON_ASSERT(stackTop_ + sizeof(T) * count <= stackEnd_);
T* ret = reinterpret_cast<T*>(stackTop_);
stackTop_ += sizeof(T) * count;
return ret;
@@ -132,8 +146,23 @@
}
template<typename T>
+ const T* Top() const {
+ RAPIDJSON_ASSERT(GetSize() >= sizeof(T));
+ return reinterpret_cast<T*>(stackTop_ - sizeof(T));
+ }
+
+ template<typename T>
+ T* End() { return reinterpret_cast<T*>(stackTop_); }
+
+ template<typename T>
+ const T* End() const { return reinterpret_cast<T*>(stackTop_); }
+
+ template<typename T>
T* Bottom() { return reinterpret_cast<T*>(stack_); }
+ template<typename T>
+ const T* Bottom() const { return reinterpret_cast<T*>(stack_); }
+
bool HasAllocator() const {
return allocator_ != 0;
}
@@ -142,6 +171,7 @@
RAPIDJSON_ASSERT(allocator_);
return *allocator_;
}
+
bool Empty() const { return stackTop_ == stack_; }
size_t GetSize() const { return static_cast<size_t>(stackTop_ - stack_); }
size_t GetCapacity() const { return static_cast<size_t>(stackEnd_ - stack_); }
@@ -193,4 +223,8 @@
} // namespace internal
RAPIDJSON_NAMESPACE_END
+#if defined(__clang__)
+RAPIDJSON_DIAG_POP
+#endif
+
#endif // RAPIDJSON_STACK_H_
=== modified file 'shorts/xml2json/rapidjson/internal/strfunc.h'
--- shorts/xml2json/rapidjson/internal/strfunc.h 2016-03-04 13:27:57 +0000
+++ shorts/xml2json/rapidjson/internal/strfunc.h 2016-06-10 15:12:59 +0000
@@ -15,7 +15,7 @@
#ifndef RAPIDJSON_INTERNAL_STRFUNC_H_
#define RAPIDJSON_INTERNAL_STRFUNC_H_
-#include "../rapidjson.h"
+#include "../stream.h"
RAPIDJSON_NAMESPACE_BEGIN
namespace internal {
@@ -33,6 +33,22 @@
return SizeType(p - s);
}
+//! Returns number of code points in a encoded string.
+template<typename Encoding>
+bool CountStringCodePoint(const typename Encoding::Ch* s, SizeType length, SizeType* outCount) {
+ GenericStringStream<Encoding> is(s);
+ const typename Encoding::Ch* end = s + length;
+ SizeType count = 0;
+ while (is.src_ < end) {
+ unsigned codepoint;
+ if (!Encoding::Decode(is, &codepoint))
+ return false;
+ count++;
+ }
+ *outCount = count;
+ return true;
+}
+
} // namespace internal
RAPIDJSON_NAMESPACE_END
=== modified file 'shorts/xml2json/rapidjson/internal/strtod.h'
--- shorts/xml2json/rapidjson/internal/strtod.h 2016-03-04 13:27:57 +0000
+++ shorts/xml2json/rapidjson/internal/strtod.h 2016-06-10 15:12:59 +0000
@@ -15,7 +15,6 @@
#ifndef RAPIDJSON_STRTOD_
#define RAPIDJSON_STRTOD_
-#include "../rapidjson.h"
#include "ieee754.h"
#include "biginteger.h"
#include "diyfp.h"
=== modified file 'shorts/xml2json/rapidjson/internal/swap.h'
--- shorts/xml2json/rapidjson/internal/swap.h 2016-03-04 13:27:57 +0000
+++ shorts/xml2json/rapidjson/internal/swap.h 2016-06-10 15:12:59 +0000
@@ -17,6 +17,11 @@
#include "../rapidjson.h"
+#if defined(__clang__)
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(c++98-compat)
+#endif
+
RAPIDJSON_NAMESPACE_BEGIN
namespace internal {
@@ -34,4 +39,8 @@
} // namespace internal
RAPIDJSON_NAMESPACE_END
+#if defined(__clang__)
+RAPIDJSON_DIAG_POP
+#endif
+
#endif // RAPIDJSON_INTERNAL_SWAP_H_
=== added file 'shorts/xml2json/rapidjson/istreamwrapper.h'
--- shorts/xml2json/rapidjson/istreamwrapper.h 1970-01-01 00:00:00 +0000
+++ shorts/xml2json/rapidjson/istreamwrapper.h 2016-06-10 15:12:59 +0000
@@ -0,0 +1,110 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+#include "stream.h"
+#include <iosfwd>
+
+#ifdef __clang__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(padded)
+#endif
+
+#ifdef _MSC_VER
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(4351) // new behavior: elements of array 'array' will be default initialized
+#endif
+
+RAPIDJSON_NAMESPACE_BEGIN
+
+//! Wrapper of \c std::basic_istream into RapidJSON's Stream concept.
+/*!
+ The classes can be wrapped including but not limited to:
+
+ - \c std::istringstream
+ - \c std::stringstream
+ - \c std::wistringstream
+ - \c std::wstringstream
+ - \c std::ifstream
+ - \c std::fstream
+ - \c std::wifstream
+ - \c std::wfstream
+
+ \tparam StreamType Class derived from \c std::basic_istream.
+*/
+
+template <typename StreamType>
+class BasicIStreamWrapper {
+public:
+ typedef typename StreamType::char_type Ch;
+ BasicIStreamWrapper(StreamType& stream) : stream_(stream), count_(), peekBuffer_() {}
+
+ Ch Peek() const {
+ typename StreamType::int_type c = stream_.peek();
+ return RAPIDJSON_LIKELY(c != StreamType::traits_type::eof()) ? static_cast<Ch>(c) : '\0';
+ }
+
+ Ch Take() {
+ typename StreamType::int_type c = stream_.get();
+ if (RAPIDJSON_LIKELY(c != StreamType::traits_type::eof())) {
+ count_++;
+ return static_cast<Ch>(c);
+ }
+ else
+ return '\0';
+ }
+
+ // tellg() may return -1 when failed. So we count by ourself.
+ size_t Tell() const { return count_; }
+
+ Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
+ void Put(Ch) { RAPIDJSON_ASSERT(false); }
+ void Flush() { RAPIDJSON_ASSERT(false); }
+ size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
+
+ // For encoding detection only.
+ const Ch* Peek4() const {
+ RAPIDJSON_ASSERT(sizeof(Ch) == 1); // Only usable for byte stream.
+ int i;
+ bool hasError = false;
+ for (i = 0; i < 4; ++i) {
+ typename StreamType::int_type c = stream_.get();
+ if (c == StreamType::traits_type::eof()) {
+ hasError = true;
+ stream_.clear();
+ break;
+ }
+ peekBuffer_[i] = static_cast<Ch>(c);
+ }
+ for (--i; i >= 0; --i)
+ stream_.putback(peekBuffer_[i]);
+ return !hasError ? peekBuffer_ : 0;
+ }
+
+private:
+ BasicIStreamWrapper(const BasicIStreamWrapper&);
+ BasicIStreamWrapper& operator=(const BasicIStreamWrapper&);
+
+ StreamType& stream_;
+ size_t count_; //!< Number of characters read. Note:
+ mutable Ch peekBuffer_[4];
+};
+
+typedef BasicIStreamWrapper<std::istream> IStreamWrapper;
+typedef BasicIStreamWrapper<std::wistream> WIStreamWrapper;
+
+#if defined(__clang__) || defined(_MSC_VER)
+RAPIDJSON_DIAG_POP
+#endif
+
+RAPIDJSON_NAMESPACE_END
=== modified file 'shorts/xml2json/rapidjson/memorybuffer.h'
--- shorts/xml2json/rapidjson/memorybuffer.h 2016-03-04 13:27:57 +0000
+++ shorts/xml2json/rapidjson/memorybuffer.h 2016-06-10 15:12:59 +0000
@@ -15,7 +15,7 @@
#ifndef RAPIDJSON_MEMORYBUFFER_H_
#define RAPIDJSON_MEMORYBUFFER_H_
-#include "rapidjson.h"
+#include "stream.h"
#include "internal/stack.h"
RAPIDJSON_NAMESPACE_BEGIN
=== modified file 'shorts/xml2json/rapidjson/memorystream.h'
--- shorts/xml2json/rapidjson/memorystream.h 2016-03-04 13:27:57 +0000
+++ shorts/xml2json/rapidjson/memorystream.h 2016-06-10 15:12:59 +0000
@@ -15,7 +15,7 @@
#ifndef RAPIDJSON_MEMORYSTREAM_H_
#define RAPIDJSON_MEMORYSTREAM_H_
-#include "rapidjson.h"
+#include "stream.h"
#ifdef __clang__
RAPIDJSON_DIAG_PUSH
@@ -42,8 +42,8 @@
MemoryStream(const Ch *src, size_t size) : src_(src), begin_(src), end_(src + size), size_(size) {}
- Ch Peek() const { return (src_ == end_) ? '\0' : *src_; }
- Ch Take() { return (src_ == end_) ? '\0' : *src_++; }
+ Ch Peek() const { return RAPIDJSON_UNLIKELY(src_ == end_) ? '\0' : *src_; }
+ Ch Take() { return RAPIDJSON_UNLIKELY(src_ == end_) ? '\0' : *src_++; }
size_t Tell() const { return static_cast<size_t>(src_ - begin_); }
Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
=== added file 'shorts/xml2json/rapidjson/ostreamwrapper.h'
--- shorts/xml2json/rapidjson/ostreamwrapper.h 1970-01-01 00:00:00 +0000
+++ shorts/xml2json/rapidjson/ostreamwrapper.h 2016-06-10 15:12:59 +0000
@@ -0,0 +1,76 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+#include "stream.h"
+#include <iosfwd>
+
+#ifdef __clang__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(padded)
+#endif
+
+RAPIDJSON_NAMESPACE_BEGIN
+
+//! Wrapper of \c std::basic_ostream into RapidJSON's Stream concept.
+/*!
+ The classes can be wrapped including but not limited to:
+
+ - \c std::ostringstream
+ - \c std::stringstream
+ - \c std::wpstringstream
+ - \c std::wstringstream
+ - \c std::ifstream
+ - \c std::fstream
+ - \c std::wofstream
+ - \c std::wfstream
+
+ \tparam StreamType Class derived from \c std::basic_ostream.
+*/
+
+template <typename StreamType>
+class BasicOStreamWrapper {
+public:
+ typedef typename StreamType::char_type Ch;
+ BasicOStreamWrapper(StreamType& stream) : stream_(stream) {}
+
+ void Put(Ch c) {
+ stream_.put(c);
+ }
+
+ void Flush() {
+ stream_.flush();
+ }
+
+ // Not implemented
+ char Peek() const { RAPIDJSON_ASSERT(false); return 0; }
+ char Take() { RAPIDJSON_ASSERT(false); return 0; }
+ size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; }
+ char* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
+ size_t PutEnd(char*) { RAPIDJSON_ASSERT(false); return 0; }
+
+private:
+ BasicOStreamWrapper(const BasicOStreamWrapper&);
+ BasicOStreamWrapper& operator=(const BasicOStreamWrapper&);
+
+ StreamType& stream_;
+};
+
+typedef BasicOStreamWrapper<std::ostream> OStreamWrapper;
+typedef BasicOStreamWrapper<std::wostream> WOStreamWrapper;
+
+#ifdef __clang__
+RAPIDJSON_DIAG_POP
+#endif
+
+RAPIDJSON_NAMESPACE_END
=== modified file 'shorts/xml2json/rapidjson/pointer.h'
--- shorts/xml2json/rapidjson/pointer.h 2016-03-04 13:27:57 +0000
+++ shorts/xml2json/rapidjson/pointer.h 2016-06-10 15:12:59 +0000
@@ -23,6 +23,11 @@
RAPIDJSON_DIAG_OFF(switch-enum)
#endif
+#ifdef _MSC_VER
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated
+#endif
+
RAPIDJSON_NAMESPACE_BEGIN
static const SizeType kPointerInvalidIndex = ~SizeType(0); //!< Represents an invalid index in GenericPointer::Token
@@ -101,7 +106,7 @@
//@{
//! Default constructor.
- GenericPointer() : allocator_(), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) {}
+ GenericPointer(Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) {}
//! Constructor that parses a string or URI fragment representation.
/*!
@@ -160,7 +165,7 @@
GenericPointer(const Token* tokens, size_t tokenCount) : allocator_(), ownAllocator_(), nameBuffer_(), tokens_(const_cast<Token*>(tokens)), tokenCount_(tokenCount), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) {}
//! Copy constructor.
- GenericPointer(const GenericPointer& rhs) : allocator_(), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) {
+ GenericPointer(const GenericPointer& rhs, Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) {
*this = rhs;
}
@@ -305,6 +310,9 @@
//@}
+ //! Get the allocator of this pointer.
+ Allocator& GetAllocator() { return *allocator_; }
+
//!@name Tokens
//@{
@@ -457,9 +465,18 @@
//! Query a value in a subtree.
/*!
\param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root.
+ \param unresolvedTokenIndex If the pointer cannot resolve a token in the pointer, this parameter can obtain the index of unresolved token.
\return Pointer to the value if it can be resolved. Otherwise null.
+
+ \note
+ There are only 3 situations when a value cannot be resolved:
+ 1. A value in the path is not an array nor object.
+ 2. An object value does not contain the token.
+ 3. A token is out of range of an array value.
+
+ Use unresolvedTokenIndex to retrieve the token index.
*/
- ValueType* Get(ValueType& root) const {
+ ValueType* Get(ValueType& root, size_t* unresolvedTokenIndex = 0) const {
RAPIDJSON_ASSERT(IsValid());
ValueType* v = &root;
for (const Token *t = tokens_; t != tokens_ + tokenCount_; ++t) {
@@ -468,18 +485,23 @@
{
typename ValueType::MemberIterator m = v->FindMember(GenericStringRef<Ch>(t->name, t->length));
if (m == v->MemberEnd())
- return 0;
+ break;
v = &m->value;
}
- break;
+ continue;
case kArrayType:
if (t->index == kPointerInvalidIndex || t->index >= v->Size())
- return 0;
+ break;
v = &((*v)[t->index]);
- break;
+ continue;
default:
- return 0;
+ break;
}
+
+ // Error: unresolved token
+ if (unresolvedTokenIndex)
+ *unresolvedTokenIndex = static_cast<size_t>(t - tokens_);
+ return 0;
}
return v;
}
@@ -489,7 +511,9 @@
\param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root.
\return Pointer to the value if it can be resolved. Otherwise null.
*/
- const ValueType* Get(const ValueType& root) const { return Get(const_cast<ValueType&>(root)); }
+ const ValueType* Get(const ValueType& root, size_t* unresolvedTokenIndex = 0) const {
+ return Get(const_cast<ValueType&>(root), unresolvedTokenIndex);
+ }
//@}
@@ -968,11 +992,11 @@
src_++;
Ch c = 0;
for (int j = 0; j < 2; j++) {
- c <<= 4;
+ c = static_cast<Ch>(c << 4);
Ch h = *src_;
- if (h >= '0' && h <= '9') c += h - '0';
- else if (h >= 'A' && h <= 'F') c += h - 'A' + 10;
- else if (h >= 'a' && h <= 'f') c += h - 'a' + 10;
+ if (h >= '0' && h <= '9') c = static_cast<Ch>(c + h - '0');
+ else if (h >= 'A' && h <= 'F') c = static_cast<Ch>(c + h - 'A' + 10);
+ else if (h >= 'a' && h <= 'f') c = static_cast<Ch>(c + h - 'a' + 10);
else {
valid_ = false;
return 0;
@@ -1050,23 +1074,23 @@
//////////////////////////////////////////////////////////////////////////////
template <typename T>
-typename T::ValueType* GetValueByPointer(T& root, const GenericPointer<typename T::ValueType>& pointer) {
- return pointer.Get(root);
+typename T::ValueType* GetValueByPointer(T& root, const GenericPointer<typename T::ValueType>& pointer, size_t* unresolvedTokenIndex = 0) {
+ return pointer.Get(root, unresolvedTokenIndex);
}
template <typename T>
-const typename T::ValueType* GetValueByPointer(const T& root, const GenericPointer<typename T::ValueType>& pointer) {
- return pointer.Get(root);
-}
-
-template <typename T, typename CharType, size_t N>
-typename T::ValueType* GetValueByPointer(T& root, const CharType (&source)[N]) {
- return GenericPointer<typename T::ValueType>(source, N - 1).Get(root);
-}
-
-template <typename T, typename CharType, size_t N>
-const typename T::ValueType* GetValueByPointer(const T& root, const CharType(&source)[N]) {
- return GenericPointer<typename T::ValueType>(source, N - 1).Get(root);
+const typename T::ValueType* GetValueByPointer(const T& root, const GenericPointer<typename T::ValueType>& pointer, size_t* unresolvedTokenIndex = 0) {
+ return pointer.Get(root, unresolvedTokenIndex);
+}
+
+template <typename T, typename CharType, size_t N>
+typename T::ValueType* GetValueByPointer(T& root, const CharType (&source)[N], size_t* unresolvedTokenIndex = 0) {
+ return GenericPointer<typename T::ValueType>(source, N - 1).Get(root, unresolvedTokenIndex);
+}
+
+template <typename T, typename CharType, size_t N>
+const typename T::ValueType* GetValueByPointer(const T& root, const CharType(&source)[N], size_t* unresolvedTokenIndex = 0) {
+ return GenericPointer<typename T::ValueType>(source, N - 1).Get(root, unresolvedTokenIndex);
}
//////////////////////////////////////////////////////////////////////////////
@@ -1323,4 +1347,8 @@
RAPIDJSON_DIAG_POP
#endif
+#ifdef _MSC_VER
+RAPIDJSON_DIAG_POP
+#endif
+
#endif // RAPIDJSON_POINTER_H_
=== modified file 'shorts/xml2json/rapidjson/prettywriter.h'
--- shorts/xml2json/rapidjson/prettywriter.h 2016-03-04 13:27:57 +0000
+++ shorts/xml2json/rapidjson/prettywriter.h 2016-06-10 15:12:59 +0000
@@ -24,6 +24,14 @@
RAPIDJSON_NAMESPACE_BEGIN
+//! Combination of PrettyWriter format flags.
+/*! \see PrettyWriter::SetFormatOptions
+ */
+enum PrettyFormatOptions {
+ kFormatDefault = 0, //!< Default pretty formatting.
+ kFormatSingleLineArray = 1 //!< Format arrays on a single line.
+};
+
//! Writer with indentation and spacing.
/*!
\tparam OutputStream Type of ouptut os.
@@ -31,8 +39,8 @@
\tparam TargetEncoding Encoding of output stream.
\tparam StackAllocator Type of allocator for allocating memory of stack.
*/
-template<typename OutputStream, typename SourceEncoding = UTF8<>, typename TargetEncoding = UTF8<>, typename StackAllocator = CrtAllocator>
-class PrettyWriter : public Writer<OutputStream, SourceEncoding, TargetEncoding, StackAllocator> {
+template<typename OutputStream, typename SourceEncoding = UTF8<>, typename TargetEncoding = UTF8<>, typename StackAllocator = CrtAllocator, unsigned writeFlags = kWriteDefaultFlags>
+class PrettyWriter : public Writer<OutputStream, SourceEncoding, TargetEncoding, StackAllocator, writeFlags> {
public:
typedef Writer<OutputStream, SourceEncoding, TargetEncoding, StackAllocator> Base;
typedef typename Base::Ch Ch;
@@ -42,8 +50,12 @@
\param allocator User supplied allocator. If it is null, it will create a private one.
\param levelDepth Initial capacity of stack.
*/
- PrettyWriter(OutputStream& os, StackAllocator* allocator = 0, size_t levelDepth = Base::kDefaultLevelDepth) :
- Base(os, allocator, levelDepth), indentChar_(' '), indentCharCount_(4) {}
+ explicit PrettyWriter(OutputStream& os, StackAllocator* allocator = 0, size_t levelDepth = Base::kDefaultLevelDepth) :
+ Base(os, allocator, levelDepth), indentChar_(' '), indentCharCount_(4), formatOptions_(kFormatDefault) {}
+
+
+ explicit PrettyWriter(StackAllocator* allocator = 0, size_t levelDepth = Base::kDefaultLevelDepth) :
+ Base(allocator, levelDepth), indentChar_(' '), indentCharCount_(4) {}
//! Set custom indentation.
/*! \param indentChar Character for indentation. Must be whitespace character (' ', '\\t', '\\n', '\\r').
@@ -57,6 +69,14 @@
return *this;
}
+ //! Set pretty writer formatting options.
+ /*! \param options Formatting options.
+ */
+ PrettyWriter& SetFormatOptions(PrettyFormatOptions options) {
+ formatOptions_ = options;
+ return *this;
+ }
+
/*! @name Implementation of Handler
\see Handler
*/
@@ -70,6 +90,12 @@
bool Uint64(uint64_t u64) { PrettyPrefix(kNumberType); return Base::WriteUint64(u64); }
bool Double(double d) { PrettyPrefix(kNumberType); return Base::WriteDouble(d); }
+ bool RawNumber(const Ch* str, SizeType length, bool copy = false) {
+ (void)copy;
+ PrettyPrefix(kNumberType);
+ return Base::WriteString(str, length);
+ }
+
bool String(const Ch* str, SizeType length, bool copy = false) {
(void)copy;
PrettyPrefix(kStringType);
@@ -120,7 +146,7 @@
RAPIDJSON_ASSERT(Base::level_stack_.template Top<typename Base::Level>()->inArray);
bool empty = Base::level_stack_.template Pop<typename Base::Level>(1)->valueCount == 0;
- if (!empty) {
+ if (!empty && !(formatOptions_ & kFormatSingleLineArray)) {
Base::os_->Put('\n');
WriteIndent();
}
@@ -142,6 +168,18 @@
bool Key(const Ch* str) { return Key(str, internal::StrLen(str)); }
//@}
+
+ //! Write a raw JSON value.
+ /*!
+ For user to write a stringified JSON as a value.
+
+ \param json A well-formed JSON value. It should not contain null character within [0, length - 1] range.
+ \param length Length of the json.
+ \param type Type of the root of json.
+ \note When using PrettyWriter::RawValue(), the result json may not be indented correctly.
+ */
+ bool RawValue(const Ch* json, size_t length, Type type) { PrettyPrefix(type); return Base::WriteRawValue(json, length); }
+
protected:
void PrettyPrefix(Type type) {
(void)type;
@@ -151,11 +189,14 @@
if (level->inArray) {
if (level->valueCount > 0) {
Base::os_->Put(','); // add comma if it is not the first element in array
- Base::os_->Put('\n');
- }
- else
- Base::os_->Put('\n');
- WriteIndent();
+ if (formatOptions_ & kFormatSingleLineArray)
+ Base::os_->Put(' ');
+ }
+
+ if (!(formatOptions_ & kFormatSingleLineArray)) {
+ Base::os_->Put('\n');
+ WriteIndent();
+ }
}
else { // in object
if (level->valueCount > 0) {
@@ -191,6 +232,7 @@
Ch indentChar_;
unsigned indentCharCount_;
+ PrettyFormatOptions formatOptions_;
private:
// Prohibit copy constructor & assignment operator.
=== modified file 'shorts/xml2json/rapidjson/rapidjson.h'
--- shorts/xml2json/rapidjson/rapidjson.h 2016-03-04 13:27:57 +0000
+++ shorts/xml2json/rapidjson/rapidjson.h 2016-06-10 15:12:59 +0000
@@ -159,7 +159,7 @@
*/
#ifndef RAPIDJSON_NO_INT64DEFINE
//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN
-#ifdef _MSC_VER
+#if defined(_MSC_VER) && (_MSC_VER < 1800) // Visual Studio 2013
#include "msinttypes/stdint.h"
#include "msinttypes/inttypes.h"
#else
@@ -178,9 +178,9 @@
#ifndef RAPIDJSON_FORCEINLINE
//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN
-#if defined(_MSC_VER) && !defined(NDEBUG)
+#if defined(_MSC_VER) && defined(NDEBUG)
#define RAPIDJSON_FORCEINLINE __forceinline
-#elif defined(__GNUC__) && __GNUC__ >= 4 && !defined(NDEBUG)
+#elif defined(__GNUC__) && __GNUC__ >= 4 && defined(NDEBUG)
#define RAPIDJSON_FORCEINLINE __attribute__((always_inline))
#else
#define RAPIDJSON_FORCEINLINE
@@ -265,7 +265,8 @@
\param x pointer to align
Some machines require strict data alignment. Currently the default uses 4 bytes
- alignment. User can customize by defining the RAPIDJSON_ALIGN function macro.
+ alignment on 32-bit platforms and 8 bytes alignment for 64-bit platforms.
+ User can customize by defining the RAPIDJSON_ALIGN function macro.
*/
#ifndef RAPIDJSON_ALIGN
#if RAPIDJSON_64BIT == 1
@@ -289,6 +290,36 @@
#endif
///////////////////////////////////////////////////////////////////////////////
+// RAPIDJSON_48BITPOINTER_OPTIMIZATION
+
+//! Use only lower 48-bit address for some pointers.
+/*!
+ \ingroup RAPIDJSON_CONFIG
+
+ This optimization uses the fact that current X86-64 architecture only implement lower 48-bit virtual address.
+ The higher 16-bit can be used for storing other data.
+ \c GenericValue uses this optimization to reduce its size form 24 bytes to 16 bytes in 64-bit architecture.
+*/
+#ifndef RAPIDJSON_48BITPOINTER_OPTIMIZATION
+#if defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || defined(_M_X64) || defined(_M_AMD64)
+#define RAPIDJSON_48BITPOINTER_OPTIMIZATION 1
+#else
+#define RAPIDJSON_48BITPOINTER_OPTIMIZATION 0
+#endif
+#endif // RAPIDJSON_48BITPOINTER_OPTIMIZATION
+
+#if RAPIDJSON_48BITPOINTER_OPTIMIZATION == 1
+#if RAPIDJSON_64BIT != 1
+#error RAPIDJSON_48BITPOINTER_OPTIMIZATION can only be set to 1 when RAPIDJSON_64BIT=1
+#endif
+#define RAPIDJSON_SETPOINTER(type, p, x) (p = reinterpret_cast<type *>((reinterpret_cast<uintptr_t>(p) & static_cast<uintptr_t>(RAPIDJSON_UINT64_C2(0xFFFF0000, 0x00000000))) | reinterpret_cast<uintptr_t>(reinterpret_cast<const void*>(x))))
+#define RAPIDJSON_GETPOINTER(type, p) (reinterpret_cast<type *>(reinterpret_cast<uintptr_t>(p) & static_cast<uintptr_t>(RAPIDJSON_UINT64_C2(0x0000FFFF, 0xFFFFFFFF))))
+#else
+#define RAPIDJSON_SETPOINTER(type, p, x) (p = (x))
+#define RAPIDJSON_GETPOINTER(type, p) (p)
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
// RAPIDJSON_SSE2/RAPIDJSON_SSE42/RAPIDJSON_SIMD
/*! \def RAPIDJSON_SIMD
@@ -410,6 +441,35 @@
#endif
///////////////////////////////////////////////////////////////////////////////
+// RAPIDJSON_LIKELY, RAPIDJSON_UNLIKELY
+
+//! Compiler branching hint for expression with high probability to be true.
+/*!
+ \ingroup RAPIDJSON_CONFIG
+ \param x Boolean expression likely to be true.
+*/
+#ifndef RAPIDJSON_LIKELY
+#if defined(__GNUC__) || defined(__clang__)
+#define RAPIDJSON_LIKELY(x) __builtin_expect(!!(x), 1)
+#else
+#define RAPIDJSON_LIKELY(x) (x)
+#endif
+#endif
+
+//! Compiler branching hint for expression with low probability to be true.
+/*!
+ \ingroup RAPIDJSON_CONFIG
+ \param x Boolean expression unlikely to be true.
+*/
+#ifndef RAPIDJSON_UNLIKELY
+#if defined(__GNUC__) || defined(__clang__)
+#define RAPIDJSON_UNLIKELY(x) __builtin_expect(!!(x), 0)
+#else
+#define RAPIDJSON_UNLIKELY(x) (x)
+#endif
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
// Helpers
//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN
@@ -501,6 +561,17 @@
#define RAPIDJSON_HAS_CXX11_TYPETRAITS 0
#endif
+#ifndef RAPIDJSON_HAS_CXX11_RANGE_FOR
+#if defined(__clang__)
+#define RAPIDJSON_HAS_CXX11_RANGE_FOR __has_feature(cxx_range_for)
+#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,3,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \
+ (defined(_MSC_VER) && _MSC_VER >= 1700)
+#define RAPIDJSON_HAS_CXX11_RANGE_FOR 1
+#else
+#define RAPIDJSON_HAS_CXX11_RANGE_FOR 0
+#endif
+#endif // RAPIDJSON_HAS_CXX11_RANGE_FOR
+
//!@endcond
///////////////////////////////////////////////////////////////////////////////
@@ -516,10 +587,7 @@
#endif
///////////////////////////////////////////////////////////////////////////////
-// Allocators and Encodings
-
-#include "allocators.h"
-#include "encodings.h"
+// Type
/*! \namespace rapidjson
\brief main RapidJSON namespace
@@ -527,148 +595,6 @@
*/
RAPIDJSON_NAMESPACE_BEGIN
-///////////////////////////////////////////////////////////////////////////////
-// Stream
-
-/*! \class rapidjson::Stream
- \brief Concept for reading and writing characters.
-
- For read-only stream, no need to implement PutBegin(), Put(), Flush() and PutEnd().
-
- For write-only stream, only need to implement Put() and Flush().
-
-\code
-concept Stream {
- typename Ch; //!< Character type of the stream.
-
- //! Read the current character from stream without moving the read cursor.
- Ch Peek() const;
-
- //! Read the current character from stream and moving the read cursor to next character.
- Ch Take();
-
- //! Get the current read cursor.
- //! \return Number of characters read from start.
- size_t Tell();
-
- //! Begin writing operation at the current read pointer.
- //! \return The begin writer pointer.
- Ch* PutBegin();
-
- //! Write a character.
- void Put(Ch c);
-
- //! Flush the buffer.
- void Flush();
-
- //! End the writing operation.
- //! \param begin The begin write pointer returned by PutBegin().
- //! \return Number of characters written.
- size_t PutEnd(Ch* begin);
-}
-\endcode
-*/
-
-//! Provides additional information for stream.
-/*!
- By using traits pattern, this type provides a default configuration for stream.
- For custom stream, this type can be specialized for other configuration.
- See TEST(Reader, CustomStringStream) in readertest.cpp for example.
-*/
-template<typename Stream>
-struct StreamTraits {
- //! Whether to make local copy of stream for optimization during parsing.
- /*!
- By default, for safety, streams do not use local copy optimization.
- Stream that can be copied fast should specialize this, like StreamTraits<StringStream>.
- */
- enum { copyOptimization = 0 };
-};
-
-//! Put N copies of a character to a stream.
-template<typename Stream, typename Ch>
-inline void PutN(Stream& stream, Ch c, size_t n) {
- for (size_t i = 0; i < n; i++)
- stream.Put(c);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// StringStream
-
-//! Read-only string stream.
-/*! \note implements Stream concept
-*/
-template <typename Encoding>
-struct GenericStringStream {
- typedef typename Encoding::Ch Ch;
-
- GenericStringStream(const Ch *src) : src_(src), head_(src) {}
-
- Ch Peek() const { return *src_; }
- Ch Take() { return *src_++; }
- size_t Tell() const { return static_cast<size_t>(src_ - head_); }
-
- Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
- void Put(Ch) { RAPIDJSON_ASSERT(false); }
- void Flush() { RAPIDJSON_ASSERT(false); }
- size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
-
- const Ch* src_; //!< Current read position.
- const Ch* head_; //!< Original head of the string.
-};
-
-template <typename Encoding>
-struct StreamTraits<GenericStringStream<Encoding> > {
- enum { copyOptimization = 1 };
-};
-
-//! String stream with UTF8 encoding.
-typedef GenericStringStream<UTF8<> > StringStream;
-
-///////////////////////////////////////////////////////////////////////////////
-// InsituStringStream
-
-//! A read-write string stream.
-/*! This string stream is particularly designed for in-situ parsing.
- \note implements Stream concept
-*/
-template <typename Encoding>
-struct GenericInsituStringStream {
- typedef typename Encoding::Ch Ch;
-
- GenericInsituStringStream(Ch *src) : src_(src), dst_(0), head_(src) {}
-
- // Read
- Ch Peek() { return *src_; }
- Ch Take() { return *src_++; }
- size_t Tell() { return static_cast<size_t>(src_ - head_); }
-
- // Write
- void Put(Ch c) { RAPIDJSON_ASSERT(dst_ != 0); *dst_++ = c; }
-
- Ch* PutBegin() { return dst_ = src_; }
- size_t PutEnd(Ch* begin) { return static_cast<size_t>(dst_ - begin); }
- void Flush() {}
-
- Ch* Push(size_t count) { Ch* begin = dst_; dst_ += count; return begin; }
- void Pop(size_t count) { dst_ -= count; }
-
- Ch* src_;
- Ch* dst_;
- Ch* head_;
-};
-
-template <typename Encoding>
-struct StreamTraits<GenericInsituStringStream<Encoding> > {
- enum { copyOptimization = 1 };
-};
-
-//! Insitu string stream with UTF8 encoding.
-typedef GenericInsituStringStream<UTF8<> > InsituStringStream;
-
-///////////////////////////////////////////////////////////////////////////////
-// Type
-
//! Type of JSON value
enum Type {
kNullType = 0, //!< null
=== modified file 'shorts/xml2json/rapidjson/reader.h'
--- shorts/xml2json/rapidjson/reader.h 2016-03-04 13:27:57 +0000
+++ shorts/xml2json/rapidjson/reader.h 2016-06-10 15:12:59 +0000
@@ -1,5 +1,5 @@
// Tencent is pleased to support the open source community by making RapidJSON available.
-//
+//
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
//
// Licensed under the MIT License (the "License"); you may not use this file except
@@ -7,9 +7,9 @@
//
// http://opensource.org/licenses/MIT
//
-// Unless required by applicable law or agreed to in writing, software distributed
-// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
-// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
#ifndef RAPIDJSON_READER_H_
@@ -17,8 +17,9 @@
/*! \file reader.h */
-#include "rapidjson.h"
-#include "encodings.h"
+#include "allocators.h"
+#include "stream.h"
+#include "encodedstream.h"
#include "internal/meta.h"
#include "internal/stack.h"
#include "internal/strtod.h"
@@ -55,7 +56,7 @@
#ifndef RAPIDJSON_PARSE_ERROR_EARLY_RETURN
#define RAPIDJSON_PARSE_ERROR_EARLY_RETURN(value) \
RAPIDJSON_MULTILINEMACRO_BEGIN \
- if (HasParseError()) { return value; } \
+ if (RAPIDJSON_UNLIKELY(HasParseError())) { return value; } \
RAPIDJSON_MULTILINEMACRO_END
#endif
#define RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID \
@@ -126,7 +127,7 @@
///////////////////////////////////////////////////////////////////////////////
// ParseFlag
-/*! \def RAPIDJSON_PARSE_DEFAULT_FLAGS
+/*! \def RAPIDJSON_PARSE_DEFAULT_FLAGS
\ingroup RAPIDJSON_CONFIG
\brief User-defined kParseDefaultFlags definition.
@@ -147,6 +148,8 @@
kParseStopWhenDoneFlag = 8, //!< After parsing a complete JSON root from stream, stop further processing the rest of stream. When this flag is used, parser will not generate kParseErrorDocumentRootNotSingular error.
kParseFullPrecisionFlag = 16, //!< Parse number in full precision (but slower).
kParseCommentsFlag = 32, //!< Allow one-line (//) and multi-line (/**/) comments.
+ kParseNumbersAsStringsFlag = 64, //!< Parse all numbers (ints/doubles) as strings.
+ kParseTrailingCommasFlag = 128, //!< Allow trailing commas at the end of objects and arrays.
kParseDefaultFlags = RAPIDJSON_PARSE_DEFAULT_FLAGS //!< Default parse flags. Can be customized by defining RAPIDJSON_PARSE_DEFAULT_FLAGS
};
@@ -155,7 +158,7 @@
/*! \class rapidjson::Handler
\brief Concept for receiving events from GenericReader upon parsing.
- The functions return true if no error occurs. If they return false,
+ The functions return true if no error occurs. If they return false,
the event publisher should terminate the process.
\code
concept Handler {
@@ -168,6 +171,8 @@
bool Int64(int64_t i);
bool Uint64(uint64_t i);
bool Double(double d);
+ /// enabled via kParseNumbersAsStringsFlag, string is not null-terminated (use length)
+ bool RawNumber(const Ch* str, SizeType length, bool copy);
bool String(const Ch* str, SizeType length, bool copy);
bool StartObject();
bool Key(const Ch* str, SizeType length, bool copy);
@@ -198,6 +203,8 @@
bool Int64(int64_t) { return static_cast<Override&>(*this).Default(); }
bool Uint64(uint64_t) { return static_cast<Override&>(*this).Default(); }
bool Double(double) { return static_cast<Override&>(*this).Default(); }
+ /// enabled via kParseNumbersAsStringsFlag, string is not null-terminated (use length)
+ bool RawNumber(const Ch* str, SizeType len, bool copy) { return static_cast<Override&>(*this).String(str, len, copy); }
bool String(const Ch*, SizeType, bool) { return static_cast<Override&>(*this).Default(); }
bool StartObject() { return static_cast<Override&>(*this).Default(); }
bool Key(const Ch* str, SizeType len, bool copy) { return static_cast<Override&>(*this).String(str, len, copy); }
@@ -255,10 +262,17 @@
internal::StreamLocalCopy<InputStream> copy(is);
InputStream& s(copy.s);
- while (s.Peek() == ' ' || s.Peek() == '\n' || s.Peek() == '\r' || s.Peek() == '\t')
+ typename InputStream::Ch c;
+ while ((c = s.Peek()) == ' ' || c == '\n' || c == '\r' || c == '\t')
s.Take();
}
+inline const char* SkipWhitespace(const char* p, const char* end) {
+ while (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t'))
+ ++p;
+ return p;
+}
+
#ifdef RAPIDJSON_SSE42
//! Skip whitespace with SSE 4.2 pcmpistrm instruction, testing 16 8-byte characters at once.
inline const char *SkipWhitespace_SIMD(const char* p) {
@@ -295,6 +309,34 @@
}
}
+inline const char *SkipWhitespace_SIMD(const char* p, const char* end) {
+ // Fast return for single non-whitespace
+ if (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t'))
+ ++p;
+ else
+ return p;
+
+ // The middle of string using SIMD
+ static const char whitespace[16] = " \n\r\t";
+ const __m128i w = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&whitespace[0]));
+
+ for (; p <= end - 16; p += 16) {
+ const __m128i s = _mm_loadu_si128(reinterpret_cast<const __m128i *>(p));
+ const int r = _mm_cvtsi128_si32(_mm_cmpistrm(w, s, _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_BIT_MASK | _SIDD_NEGATIVE_POLARITY));
+ if (r != 0) { // some of characters is non-whitespace
+#ifdef _MSC_VER // Find the index of first non-whitespace
+ unsigned long offset;
+ _BitScanForward(&offset, r);
+ return p + offset;
+#else
+ return p + __builtin_ffs(r) - 1;
+#endif
+ }
+ }
+
+ return SkipWhitespace(p, end);
+}
+
#elif defined(RAPIDJSON_SSE2)
//! Skip whitespace with SSE2 instructions, testing 16 8-byte characters at once.
@@ -314,16 +356,14 @@
return p;
// The rest of string
- static const char whitespaces[4][17] = {
- " ",
- "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n",
- "\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r",
- "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"};
+ #define C16(c) { c, c, c, c, c, c, c, c, c, c, c, c, c, c, c, c }
+ static const char whitespaces[4][16] = { C16(' '), C16('\n'), C16('\r'), C16('\t') };
+ #undef C16
- const __m128i w0 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&whitespaces[0][0]));
- const __m128i w1 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&whitespaces[1][0]));
- const __m128i w2 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&whitespaces[2][0]));
- const __m128i w3 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&whitespaces[3][0]));
+ const __m128i w0 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&whitespaces[0][0]));
+ const __m128i w1 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&whitespaces[1][0]));
+ const __m128i w2 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&whitespaces[2][0]));
+ const __m128i w3 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&whitespaces[3][0]));
for (;; p += 16) {
const __m128i s = _mm_load_si128(reinterpret_cast<const __m128i *>(p));
@@ -344,11 +384,49 @@
}
}
+inline const char *SkipWhitespace_SIMD(const char* p, const char* end) {
+ // Fast return for single non-whitespace
+ if (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t'))
+ ++p;
+ else
+ return p;
+
+ // The rest of string
+ #define C16(c) { c, c, c, c, c, c, c, c, c, c, c, c, c, c, c, c }
+ static const char whitespaces[4][16] = { C16(' '), C16('\n'), C16('\r'), C16('\t') };
+ #undef C16
+
+ const __m128i w0 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&whitespaces[0][0]));
+ const __m128i w1 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&whitespaces[1][0]));
+ const __m128i w2 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&whitespaces[2][0]));
+ const __m128i w3 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&whitespaces[3][0]));
+
+ for (; p <= end - 16; p += 16) {
+ const __m128i s = _mm_loadu_si128(reinterpret_cast<const __m128i *>(p));
+ __m128i x = _mm_cmpeq_epi8(s, w0);
+ x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w1));
+ x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w2));
+ x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w3));
+ unsigned short r = static_cast<unsigned short>(~_mm_movemask_epi8(x));
+ if (r != 0) { // some of characters may be non-whitespace
+#ifdef _MSC_VER // Find the index of first non-whitespace
+ unsigned long offset;
+ _BitScanForward(&offset, r);
+ return p + offset;
+#else
+ return p + __builtin_ffs(r) - 1;
+#endif
+ }
+ }
+
+ return SkipWhitespace(p, end);
+}
+
#endif // RAPIDJSON_SSE2
#ifdef RAPIDJSON_SIMD
//! Template function specialization for InsituStringStream
-template<> inline void SkipWhitespace(InsituStringStream& is) {
+template<> inline void SkipWhitespace(InsituStringStream& is) {
is.src_ = const_cast<char*>(SkipWhitespace_SIMD(is.src_));
}
@@ -356,23 +434,27 @@
template<> inline void SkipWhitespace(StringStream& is) {
is.src_ = SkipWhitespace_SIMD(is.src_);
}
+
+template<> inline void SkipWhitespace(EncodedInputStream<UTF8<>, MemoryStream>& is) {
+ is.is_.src_ = SkipWhitespace_SIMD(is.is_.src_, is.is_.end_);
+}
#endif // RAPIDJSON_SIMD
///////////////////////////////////////////////////////////////////////////////
// GenericReader
//! SAX-style JSON parser. Use \ref Reader for UTF8 encoding and default allocator.
-/*! GenericReader parses JSON text from a stream, and send events synchronously to an
+/*! GenericReader parses JSON text from a stream, and send events synchronously to an
object implementing Handler concept.
- It needs to allocate a stack for storing a single decoded string during
+ It needs to allocate a stack for storing a single decoded string during
non-destructive parsing.
- For in-situ parsing, the decoded string is directly written to the source
+ For in-situ parsing, the decoded string is directly written to the source
text string, no temporary buffer is required.
A GenericReader object can be reused for parsing multiple JSON text.
-
+
\tparam SourceEncoding Encoding of the input stream.
\tparam TargetEncoding Encoding of the parse output.
\tparam StackAllocator Allocator type for stack.
@@ -408,7 +490,7 @@
SkipWhitespaceAndComments<parseFlags>(is);
RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_);
- if (is.Peek() == '\0') {
+ if (RAPIDJSON_UNLIKELY(is.Peek() == '\0')) {
RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentEmpty, is.Tell());
RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_);
}
@@ -420,7 +502,7 @@
SkipWhitespaceAndComments<parseFlags>(is);
RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_);
- if (is.Peek() != '\0') {
+ if (RAPIDJSON_UNLIKELY(is.Peek() != '\0')) {
RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentRootNotSingular, is.Tell());
RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_);
}
@@ -444,7 +526,7 @@
//! Whether a parse error has occured in the last parsing.
bool HasParseError() const { return parseResult_.IsError(); }
-
+
//! Get the \ref ParseErrorCode of last parsing.
ParseErrorCode GetParseErrorCode() const { return parseResult_.Code(); }
@@ -476,29 +558,23 @@
SkipWhitespace(is);
if (parseFlags & kParseCommentsFlag) {
- while (is.Peek() == '/') {
- is.Take();
-
- if (is.Peek() == '*') {
- is.Take();
+ while (RAPIDJSON_UNLIKELY(Consume(is, '/'))) {
+ if (Consume(is, '*')) {
while (true) {
- if (is.Peek() == '\0')
+ if (RAPIDJSON_UNLIKELY(is.Peek() == '\0'))
RAPIDJSON_PARSE_ERROR(kParseErrorUnspecificSyntaxError, is.Tell());
-
- if (is.Take() == '*') {
- if (is.Peek() == '\0')
- RAPIDJSON_PARSE_ERROR(kParseErrorUnspecificSyntaxError, is.Tell());
-
- if (is.Take() == '/')
+ else if (Consume(is, '*')) {
+ if (Consume(is, '/'))
break;
}
+ else
+ is.Take();
}
- } else if (is.Peek() == '/') {
- is.Take();
- while (is.Peek() != '\0' && is.Take() != '\n') { }
- } else {
+ }
+ else if (RAPIDJSON_LIKELY(Consume(is, '/')))
+ while (is.Peek() != '\0' && is.Take() != '\n');
+ else
RAPIDJSON_PARSE_ERROR(kParseErrorUnspecificSyntaxError, is.Tell());
- }
SkipWhitespace(is);
}
@@ -510,22 +586,21 @@
void ParseObject(InputStream& is, Handler& handler) {
RAPIDJSON_ASSERT(is.Peek() == '{');
is.Take(); // Skip '{'
-
- if (!handler.StartObject())
+
+ if (RAPIDJSON_UNLIKELY(!handler.StartObject()))
RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell());
SkipWhitespaceAndComments<parseFlags>(is);
RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID;
- if (is.Peek() == '}') {
- is.Take();
- if (!handler.EndObject(0)) // empty object
+ if (Consume(is, '}')) {
+ if (RAPIDJSON_UNLIKELY(!handler.EndObject(0))) // empty object
RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell());
return;
}
for (SizeType memberCount = 0;;) {
- if (is.Peek() != '"')
+ if (RAPIDJSON_UNLIKELY(is.Peek() != '"'))
RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissName, is.Tell());
ParseString<parseFlags>(is, handler, true);
@@ -534,7 +609,7 @@
SkipWhitespaceAndComments<parseFlags>(is);
RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID;
- if (is.Take() != ':')
+ if (RAPIDJSON_UNLIKELY(!Consume(is, ':')))
RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissColon, is.Tell());
SkipWhitespaceAndComments<parseFlags>(is);
@@ -548,19 +623,30 @@
++memberCount;
- switch (is.Take()) {
+ switch (is.Peek()) {
case ',':
+ is.Take();
SkipWhitespaceAndComments<parseFlags>(is);
RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID;
break;
- case '}':
- if (!handler.EndObject(memberCount))
+ case '}':
+ is.Take();
+ if (RAPIDJSON_UNLIKELY(!handler.EndObject(memberCount)))
RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell());
return;
- default:
+ default:
RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, is.Tell());
break;
}
+
+ if (parseFlags & kParseTrailingCommasFlag) {
+ if (is.Peek() == '}') {
+ if (RAPIDJSON_UNLIKELY(!handler.EndObject(memberCount)))
+ RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell());
+ is.Take();
+ return;
+ }
+ }
}
}
@@ -569,16 +655,15 @@
void ParseArray(InputStream& is, Handler& handler) {
RAPIDJSON_ASSERT(is.Peek() == '[');
is.Take(); // Skip '['
-
- if (!handler.StartArray())
+
+ if (RAPIDJSON_UNLIKELY(!handler.StartArray()))
RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell());
-
+
SkipWhitespaceAndComments<parseFlags>(is);
RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID;
- if (is.Peek() == ']') {
- is.Take();
- if (!handler.EndArray(0)) // empty array
+ if (Consume(is, ']')) {
+ if (RAPIDJSON_UNLIKELY(!handler.EndArray(0))) // empty array
RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell());
return;
}
@@ -591,18 +676,25 @@
SkipWhitespaceAndComments<parseFlags>(is);
RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID;
- switch (is.Take()) {
- case ',':
- SkipWhitespaceAndComments<parseFlags>(is);
- RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID;
- break;
- case ']':
- if (!handler.EndArray(elementCount))
+ if (Consume(is, ',')) {
+ SkipWhitespaceAndComments<parseFlags>(is);
+ RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID;
+ }
+ else if (Consume(is, ']')) {
+ if (RAPIDJSON_UNLIKELY(!handler.EndArray(elementCount)))
+ RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell());
+ return;
+ }
+ else
+ RAPIDJSON_PARSE_ERROR(kParseErrorArrayMissCommaOrSquareBracket, is.Tell());
+
+ if (parseFlags & kParseTrailingCommasFlag) {
+ if (is.Peek() == ']') {
+ if (RAPIDJSON_UNLIKELY(!handler.EndArray(elementCount)))
RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell());
+ is.Take();
return;
- default:
- RAPIDJSON_PARSE_ERROR(kParseErrorArrayMissCommaOrSquareBracket, is.Tell());
- break;
+ }
}
}
}
@@ -612,12 +704,12 @@
RAPIDJSON_ASSERT(is.Peek() == 'n');
is.Take();
- if (is.Take() == 'u' && is.Take() == 'l' && is.Take() == 'l') {
- if (!handler.Null())
+ if (RAPIDJSON_LIKELY(Consume(is, 'u') && Consume(is, 'l') && Consume(is, 'l'))) {
+ if (RAPIDJSON_UNLIKELY(!handler.Null()))
RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell());
}
else
- RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell() - 1);
+ RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell());
}
template<unsigned parseFlags, typename InputStream, typename Handler>
@@ -625,12 +717,12 @@
RAPIDJSON_ASSERT(is.Peek() == 't');
is.Take();
- if (is.Take() == 'r' && is.Take() == 'u' && is.Take() == 'e') {
- if (!handler.Bool(true))
+ if (RAPIDJSON_LIKELY(Consume(is, 'r') && Consume(is, 'u') && Consume(is, 'e'))) {
+ if (RAPIDJSON_UNLIKELY(!handler.Bool(true)))
RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell());
}
else
- RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell() - 1);
+ RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell());
}
template<unsigned parseFlags, typename InputStream, typename Handler>
@@ -638,20 +730,30 @@
RAPIDJSON_ASSERT(is.Peek() == 'f');
is.Take();
- if (is.Take() == 'a' && is.Take() == 'l' && is.Take() == 's' && is.Take() == 'e') {
- if (!handler.Bool(false))
+ if (RAPIDJSON_LIKELY(Consume(is, 'a') && Consume(is, 'l') && Consume(is, 's') && Consume(is, 'e'))) {
+ if (RAPIDJSON_UNLIKELY(!handler.Bool(false)))
RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell());
}
else
- RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell() - 1);
+ RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell());
+ }
+
+ template<typename InputStream>
+ RAPIDJSON_FORCEINLINE static bool Consume(InputStream& is, typename InputStream::Ch expect) {
+ if (RAPIDJSON_LIKELY(is.Peek() == expect)) {
+ is.Take();
+ return true;
+ }
+ else
+ return false;
}
// Helper function to parse four hexidecimal digits in \uXXXX in ParseString().
template<typename InputStream>
- unsigned ParseHex4(InputStream& is) {
+ unsigned ParseHex4(InputStream& is, size_t escapeOffset) {
unsigned codepoint = 0;
for (int i = 0; i < 4; i++) {
- Ch c = is.Take();
+ Ch c = is.Peek();
codepoint <<= 4;
codepoint += static_cast<unsigned>(c);
if (c >= '0' && c <= '9')
@@ -661,9 +763,10 @@
else if (c >= 'a' && c <= 'f')
codepoint -= 'a' - 10;
else {
- RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorStringUnicodeEscapeInvalidHex, is.Tell() - 1);
+ RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorStringUnicodeEscapeInvalidHex, escapeOffset);
RAPIDJSON_PARSE_ERROR_EARLY_RETURN(0);
}
+ is.Take();
}
return codepoint;
}
@@ -678,7 +781,14 @@
*stack_.template Push<Ch>() = c;
++length_;
}
+
+ RAPIDJSON_FORCEINLINE void* Push(SizeType count) {
+ length_ += count;
+ return stack_.template Push<Ch>(count);
+ }
+
size_t Length() const { return length_; }
+
Ch* Pop() {
return stack_.template Pop<Ch>(length_);
}
@@ -697,6 +807,9 @@
internal::StreamLocalCopy<InputStream> copy(is);
InputStream& s(copy.s);
+ RAPIDJSON_ASSERT(s.Peek() == '\"');
+ s.Take(); // Skip '\"'
+
bool success = false;
if (parseFlags & kParseInsituFlag) {
typename InputStream::Ch *head = s.PutBegin();
@@ -715,7 +828,7 @@
const typename TargetEncoding::Ch* const str = stackStream.Pop();
success = (isKey ? handler.Key(str, length, true) : handler.String(str, length, true));
}
- if (!success)
+ if (RAPIDJSON_UNLIKELY(!success))
RAPIDJSON_PARSE_ERROR(kParseErrorTermination, s.Tell());
}
@@ -726,73 +839,247 @@
//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN
#define Z16 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
static const char escape[256] = {
- Z16, Z16, 0, 0,'\"', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'/',
- Z16, Z16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'\\', 0, 0, 0,
- 0, 0,'\b', 0, 0, 0,'\f', 0, 0, 0, 0, 0, 0, 0,'\n', 0,
- 0, 0,'\r', 0,'\t', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ Z16, Z16, 0, 0,'\"', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'/',
+ Z16, Z16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'\\', 0, 0, 0,
+ 0, 0,'\b', 0, 0, 0,'\f', 0, 0, 0, 0, 0, 0, 0,'\n', 0,
+ 0, 0,'\r', 0,'\t', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16
};
#undef Z16
//!@endcond
- RAPIDJSON_ASSERT(is.Peek() == '\"');
- is.Take(); // Skip '\"'
-
for (;;) {
+ // Scan and copy string before "\\\"" or < 0x20. This is an optional optimzation.
+ if (!(parseFlags & kParseValidateEncodingFlag))
+ ScanCopyUnescapedString(is, os);
+
Ch c = is.Peek();
- if (c == '\\') { // Escape
+ if (RAPIDJSON_UNLIKELY(c == '\\')) { // Escape
+ size_t escapeOffset = is.Tell(); // For invalid escaping, report the inital '\\' as error offset
is.Take();
- Ch e = is.Take();
- if ((sizeof(Ch) == 1 || unsigned(e) < 256) && escape[static_cast<unsigned char>(e)])
+ Ch e = is.Peek();
+ if ((sizeof(Ch) == 1 || unsigned(e) < 256) && RAPIDJSON_LIKELY(escape[static_cast<unsigned char>(e)])) {
+ is.Take();
os.Put(static_cast<typename TEncoding::Ch>(escape[static_cast<unsigned char>(e)]));
- else if (e == 'u') { // Unicode
- unsigned codepoint = ParseHex4(is);
+ }
+ else if (RAPIDJSON_LIKELY(e == 'u')) { // Unicode
+ is.Take();
+ unsigned codepoint = ParseHex4(is, escapeOffset);
RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID;
- if (codepoint >= 0xD800 && codepoint <= 0xDBFF) {
+ if (RAPIDJSON_UNLIKELY(codepoint >= 0xD800 && codepoint <= 0xDBFF)) {
// Handle UTF-16 surrogate pair
- if (is.Take() != '\\' || is.Take() != 'u')
- RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, is.Tell() - 2);
- unsigned codepoint2 = ParseHex4(is);
+ if (RAPIDJSON_UNLIKELY(!Consume(is, '\\') || !Consume(is, 'u')))
+ RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, escapeOffset);
+ unsigned codepoint2 = ParseHex4(is, escapeOffset);
RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID;
- if (codepoint2 < 0xDC00 || codepoint2 > 0xDFFF)
- RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, is.Tell() - 2);
+ if (RAPIDJSON_UNLIKELY(codepoint2 < 0xDC00 || codepoint2 > 0xDFFF))
+ RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, escapeOffset);
codepoint = (((codepoint - 0xD800) << 10) | (codepoint2 - 0xDC00)) + 0x10000;
}
TEncoding::Encode(os, codepoint);
}
else
- RAPIDJSON_PARSE_ERROR(kParseErrorStringEscapeInvalid, is.Tell() - 1);
+ RAPIDJSON_PARSE_ERROR(kParseErrorStringEscapeInvalid, escapeOffset);
}
- else if (c == '"') { // Closing double quote
+ else if (RAPIDJSON_UNLIKELY(c == '"')) { // Closing double quote
is.Take();
os.Put('\0'); // null-terminate the string
return;
}
- else if (c == '\0')
- RAPIDJSON_PARSE_ERROR(kParseErrorStringMissQuotationMark, is.Tell() - 1);
- else if (static_cast<unsigned>(c) < 0x20) // RFC 4627: unescaped = %x20-21 / %x23-5B / %x5D-10FFFF
- RAPIDJSON_PARSE_ERROR(kParseErrorStringEscapeInvalid, is.Tell() - 1);
+ else if (RAPIDJSON_UNLIKELY(static_cast<unsigned>(c) < 0x20)) { // RFC 4627: unescaped = %x20-21 / %x23-5B / %x5D-10FFFF
+ if (c == '\0')
+ RAPIDJSON_PARSE_ERROR(kParseErrorStringMissQuotationMark, is.Tell());
+ else
+ RAPIDJSON_PARSE_ERROR(kParseErrorStringEscapeInvalid, is.Tell());
+ }
else {
- if (parseFlags & kParseValidateEncodingFlag ?
- !Transcoder<SEncoding, TEncoding>::Validate(is, os) :
- !Transcoder<SEncoding, TEncoding>::Transcode(is, os))
- RAPIDJSON_PARSE_ERROR(kParseErrorStringInvalidEncoding, is.Tell());
- }
- }
- }
-
- template<typename InputStream, bool backup>
+ size_t offset = is.Tell();
+ if (RAPIDJSON_UNLIKELY((parseFlags & kParseValidateEncodingFlag ?
+ !Transcoder<SEncoding, TEncoding>::Validate(is, os) :
+ !Transcoder<SEncoding, TEncoding>::Transcode(is, os))))
+ RAPIDJSON_PARSE_ERROR(kParseErrorStringInvalidEncoding, offset);
+ }
+ }
+ }
+
+ template<typename InputStream, typename OutputStream>
+ static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(InputStream&, OutputStream&) {
+ // Do nothing for generic version
+ }
+
+#if defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42)
+ // StringStream -> StackStream<char>
+ static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(StringStream& is, StackStream<char>& os) {
+ const char* p = is.src_;
+
+ // Scan one by one until alignment (unaligned load may cross page boundary and cause crash)
+ const char* nextAligned = reinterpret_cast<const char*>((reinterpret_cast<size_t>(p) + 15) & static_cast<size_t>(~15));
+ while (p != nextAligned)
+ if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast<unsigned>(*p) < 0x20)) {
+ is.src_ = p;
+ return;
+ }
+ else
+ os.Put(*p++);
+
+ // The rest of string using SIMD
+ static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' };
+ static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' };
+ static const char space[16] = { 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19 };
+ const __m128i dq = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&dquote[0]));
+ const __m128i bs = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&bslash[0]));
+ const __m128i sp = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&space[0]));
+
+ for (;; p += 16) {
+ const __m128i s = _mm_load_si128(reinterpret_cast<const __m128i *>(p));
+ const __m128i t1 = _mm_cmpeq_epi8(s, dq);
+ const __m128i t2 = _mm_cmpeq_epi8(s, bs);
+ const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x19) == 0x19
+ const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3);
+ unsigned short r = static_cast<unsigned short>(_mm_movemask_epi8(x));
+ if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped
+ SizeType length;
+ #ifdef _MSC_VER // Find the index of first escaped
+ unsigned long offset;
+ _BitScanForward(&offset, r);
+ length = offset;
+ #else
+ length = static_cast<SizeType>(__builtin_ffs(r) - 1);
+ #endif
+ char* q = reinterpret_cast<char*>(os.Push(length));
+ for (size_t i = 0; i < length; i++)
+ q[i] = p[i];
+
+ p += length;
+ break;
+ }
+ _mm_storeu_si128(reinterpret_cast<__m128i *>(os.Push(16)), s);
+ }
+
+ is.src_ = p;
+ }
+
+ // InsituStringStream -> InsituStringStream
+ static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(InsituStringStream& is, InsituStringStream& os) {
+ RAPIDJSON_ASSERT(&is == &os);
+ (void)os;
+
+ if (is.src_ == is.dst_) {
+ SkipUnescapedString(is);
+ return;
+ }
+
+ char* p = is.src_;
+ char *q = is.dst_;
+
+ // Scan one by one until alignment (unaligned load may cross page boundary and cause crash)
+ const char* nextAligned = reinterpret_cast<const char*>((reinterpret_cast<size_t>(p) + 15) & static_cast<size_t>(~15));
+ while (p != nextAligned)
+ if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast<unsigned>(*p) < 0x20)) {
+ is.src_ = p;
+ is.dst_ = q;
+ return;
+ }
+ else
+ *q++ = *p++;
+
+ // The rest of string using SIMD
+ static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' };
+ static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' };
+ static const char space[16] = { 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19 };
+ const __m128i dq = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&dquote[0]));
+ const __m128i bs = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&bslash[0]));
+ const __m128i sp = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&space[0]));
+
+ for (;; p += 16, q += 16) {
+ const __m128i s = _mm_load_si128(reinterpret_cast<const __m128i *>(p));
+ const __m128i t1 = _mm_cmpeq_epi8(s, dq);
+ const __m128i t2 = _mm_cmpeq_epi8(s, bs);
+ const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x19) == 0x19
+ const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3);
+ unsigned short r = static_cast<unsigned short>(_mm_movemask_epi8(x));
+ if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped
+ size_t length;
+#ifdef _MSC_VER // Find the index of first escaped
+ unsigned long offset;
+ _BitScanForward(&offset, r);
+ length = offset;
+#else
+ length = static_cast<size_t>(__builtin_ffs(r) - 1);
+#endif
+ for (const char* pend = p + length; p != pend; )
+ *q++ = *p++;
+ break;
+ }
+ _mm_storeu_si128(reinterpret_cast<__m128i *>(q), s);
+ }
+
+ is.src_ = p;
+ is.dst_ = q;
+ }
+
+ // When read/write pointers are the same for insitu stream, just skip unescaped characters
+ static RAPIDJSON_FORCEINLINE void SkipUnescapedString(InsituStringStream& is) {
+ RAPIDJSON_ASSERT(is.src_ == is.dst_);
+ char* p = is.src_;
+
+ // Scan one by one until alignment (unaligned load may cross page boundary and cause crash)
+ const char* nextAligned = reinterpret_cast<const char*>((reinterpret_cast<size_t>(p) + 15) & static_cast<size_t>(~15));
+ for (; p != nextAligned; p++)
+ if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast<unsigned>(*p) < 0x20)) {
+ is.src_ = is.dst_ = p;
+ return;
+ }
+
+ // The rest of string using SIMD
+ static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' };
+ static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' };
+ static const char space[16] = { 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19 };
+ const __m128i dq = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&dquote[0]));
+ const __m128i bs = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&bslash[0]));
+ const __m128i sp = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&space[0]));
+
+ for (;; p += 16) {
+ const __m128i s = _mm_load_si128(reinterpret_cast<const __m128i *>(p));
+ const __m128i t1 = _mm_cmpeq_epi8(s, dq);
+ const __m128i t2 = _mm_cmpeq_epi8(s, bs);
+ const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x19) == 0x19
+ const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3);
+ unsigned short r = static_cast<unsigned short>(_mm_movemask_epi8(x));
+ if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped
+ size_t length;
+#ifdef _MSC_VER // Find the index of first escaped
+ unsigned long offset;
+ _BitScanForward(&offset, r);
+ length = offset;
+#else
+ length = static_cast<size_t>(__builtin_ffs(r) - 1);
+#endif
+ p += length;
+ break;
+ }
+ }
+
+ is.src_ = is.dst_ = p;
+ }
+#endif
+
+ template<typename InputStream, bool backup, bool pushOnTake>
class NumberStream;
template<typename InputStream>
- class NumberStream<InputStream, false> {
+ class NumberStream<InputStream, false, false> {
public:
+ typedef typename InputStream::Ch Ch;
+
NumberStream(GenericReader& reader, InputStream& s) : is(s) { (void)reader; }
~NumberStream() {}
RAPIDJSON_FORCEINLINE Ch Peek() const { return is.Peek(); }
RAPIDJSON_FORCEINLINE Ch TakePush() { return is.Take(); }
RAPIDJSON_FORCEINLINE Ch Take() { return is.Take(); }
+ RAPIDJSON_FORCEINLINE void Push(char) {}
+
size_t Tell() { return is.Tell(); }
size_t Length() { return 0; }
const char* Pop() { return 0; }
@@ -804,10 +1091,10 @@
};
template<typename InputStream>
- class NumberStream<InputStream, true> : public NumberStream<InputStream, false> {
- typedef NumberStream<InputStream, false> Base;
+ class NumberStream<InputStream, true, false> : public NumberStream<InputStream, false, false> {
+ typedef NumberStream<InputStream, false, false> Base;
public:
- NumberStream(GenericReader& reader, InputStream& is) : NumberStream<InputStream, false>(reader, is), stackStream(reader.stack_) {}
+ NumberStream(GenericReader& reader, InputStream& is) : Base(reader, is), stackStream(reader.stack_) {}
~NumberStream() {}
RAPIDJSON_FORCEINLINE Ch TakePush() {
@@ -815,6 +1102,10 @@
return Base::is.Take();
}
+ RAPIDJSON_FORCEINLINE void Push(char c) {
+ stackStream.Put(c);
+ }
+
size_t Length() { return stackStream.Length(); }
const char* Pop() {
@@ -826,34 +1117,47 @@
StackStream<char> stackStream;
};
+ template<typename InputStream>
+ class NumberStream<InputStream, true, true> : public NumberStream<InputStream, true, false> {
+ typedef NumberStream<InputStream, true, false> Base;
+ public:
+ NumberStream(GenericReader& reader, InputStream& is) : Base(reader, is) {}
+ ~NumberStream() {}
+
+ RAPIDJSON_FORCEINLINE Ch Take() { return Base::TakePush(); }
+ };
+
template<unsigned parseFlags, typename InputStream, typename Handler>
void ParseNumber(InputStream& is, Handler& handler) {
internal::StreamLocalCopy<InputStream> copy(is);
- NumberStream<InputStream, (parseFlags & kParseFullPrecisionFlag) != 0> s(*this, copy.s);
+ NumberStream<InputStream,
+ ((parseFlags & kParseNumbersAsStringsFlag) != 0) ?
+ ((parseFlags & kParseInsituFlag) == 0) :
+ ((parseFlags & kParseFullPrecisionFlag) != 0),
+ (parseFlags & kParseNumbersAsStringsFlag) != 0 &&
+ (parseFlags & kParseInsituFlag) == 0> s(*this, copy.s);
+
+ size_t startOffset = s.Tell();
// Parse minus
- bool minus = false;
- if (s.Peek() == '-') {
- minus = true;
- s.Take();
- }
+ bool minus = Consume(s, '-');
// Parse int: zero / ( digit1-9 *DIGIT )
unsigned i = 0;
uint64_t i64 = 0;
bool use64bit = false;
int significandDigit = 0;
- if (s.Peek() == '0') {
+ if (RAPIDJSON_UNLIKELY(s.Peek() == '0')) {
i = 0;
s.TakePush();
}
- else if (s.Peek() >= '1' && s.Peek() <= '9') {
+ else if (RAPIDJSON_LIKELY(s.Peek() >= '1' && s.Peek() <= '9')) {
i = static_cast<unsigned>(s.TakePush() - '0');
if (minus)
- while (s.Peek() >= '0' && s.Peek() <= '9') {
- if (i >= 214748364) { // 2^31 = 2147483648
- if (i != 214748364 || s.Peek() > '8') {
+ while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) {
+ if (RAPIDJSON_UNLIKELY(i >= 214748364)) { // 2^31 = 2147483648
+ if (RAPIDJSON_LIKELY(i != 214748364 || s.Peek() > '8')) {
i64 = i;
use64bit = true;
break;
@@ -863,9 +1167,9 @@
significandDigit++;
}
else
- while (s.Peek() >= '0' && s.Peek() <= '9') {
- if (i >= 429496729) { // 2^32 - 1 = 4294967295
- if (i != 429496729 || s.Peek() > '5') {
+ while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) {
+ if (RAPIDJSON_UNLIKELY(i >= 429496729)) { // 2^32 - 1 = 4294967295
+ if (RAPIDJSON_LIKELY(i != 429496729 || s.Peek() > '5')) {
i64 = i;
use64bit = true;
break;
@@ -882,11 +1186,11 @@
bool useDouble = false;
double d = 0.0;
if (use64bit) {
- if (minus)
- while (s.Peek() >= '0' && s.Peek() <= '9') {
- if (i64 >= RAPIDJSON_UINT64_C2(0x0CCCCCCC, 0xCCCCCCCC)) // 2^63 = 9223372036854775808
- if (i64 != RAPIDJSON_UINT64_C2(0x0CCCCCCC, 0xCCCCCCCC) || s.Peek() > '8') {
- d = i64;
+ if (minus)
+ while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) {
+ if (RAPIDJSON_UNLIKELY(i64 >= RAPIDJSON_UINT64_C2(0x0CCCCCCC, 0xCCCCCCCC))) // 2^63 = 9223372036854775808
+ if (RAPIDJSON_LIKELY(i64 != RAPIDJSON_UINT64_C2(0x0CCCCCCC, 0xCCCCCCCC) || s.Peek() > '8')) {
+ d = static_cast<double>(i64);
useDouble = true;
break;
}
@@ -894,10 +1198,10 @@
significandDigit++;
}
else
- while (s.Peek() >= '0' && s.Peek() <= '9') {
- if (i64 >= RAPIDJSON_UINT64_C2(0x19999999, 0x99999999)) // 2^64 - 1 = 18446744073709551615
- if (i64 != RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) || s.Peek() > '5') {
- d = i64;
+ while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) {
+ if (RAPIDJSON_UNLIKELY(i64 >= RAPIDJSON_UINT64_C2(0x19999999, 0x99999999))) // 2^64 - 1 = 18446744073709551615
+ if (RAPIDJSON_LIKELY(i64 != RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) || s.Peek() > '5')) {
+ d = static_cast<double>(i64);
useDouble = true;
break;
}
@@ -908,9 +1212,9 @@
// Force double for big integer
if (useDouble) {
- while (s.Peek() >= '0' && s.Peek() <= '9') {
- if (d >= 1.7976931348623157e307) // DBL_MAX / 10.0
- RAPIDJSON_PARSE_ERROR(kParseErrorNumberTooBig, s.Tell());
+ while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) {
+ if (RAPIDJSON_UNLIKELY(d >= 1.7976931348623157e307)) // DBL_MAX / 10.0
+ RAPIDJSON_PARSE_ERROR(kParseErrorNumberTooBig, startOffset);
d = d * 10 + (s.TakePush() - '0');
}
}
@@ -918,11 +1222,10 @@
// Parse frac = decimal-point 1*DIGIT
int expFrac = 0;
size_t decimalPosition;
- if (s.Peek() == '.') {
- s.Take();
+ if (Consume(s, '.')) {
decimalPosition = s.Length();
- if (!(s.Peek() >= '0' && s.Peek() <= '9'))
+ if (RAPIDJSON_UNLIKELY(!(s.Peek() >= '0' && s.Peek() <= '9')))
RAPIDJSON_PARSE_ERROR(kParseErrorNumberMissFraction, s.Tell());
if (!useDouble) {
@@ -930,8 +1233,8 @@
// Use i64 to store significand in 64-bit architecture
if (!use64bit)
i64 = i;
-
- while (s.Peek() >= '0' && s.Peek() <= '9') {
+
+ while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) {
if (i64 > RAPIDJSON_UINT64_C2(0x1FFFFF, 0xFFFFFFFF)) // 2^53 - 1 for fast path
break;
else {
@@ -950,11 +1253,11 @@
useDouble = true;
}
- while (s.Peek() >= '0' && s.Peek() <= '9') {
+ while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) {
if (significandDigit < 17) {
d = d * 10.0 + (s.TakePush() - '0');
--expFrac;
- if (d > 0.0)
+ if (RAPIDJSON_LIKELY(d > 0.0))
significandDigit++;
}
else
@@ -966,38 +1269,35 @@
// Parse exp = e [ minus / plus ] 1*DIGIT
int exp = 0;
- if (s.Peek() == 'e' || s.Peek() == 'E') {
+ if (Consume(s, 'e') || Consume(s, 'E')) {
if (!useDouble) {
- d = use64bit ? i64 : i;
+ d = static_cast<double>(use64bit ? i64 : i);
useDouble = true;
}
- s.Take();
bool expMinus = false;
- if (s.Peek() == '+')
- s.Take();
- else if (s.Peek() == '-') {
- s.Take();
+ if (Consume(s, '+'))
+ ;
+ else if (Consume(s, '-'))
expMinus = true;
- }
- if (s.Peek() >= '0' && s.Peek() <= '9') {
+ if (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) {
exp = static_cast<int>(s.Take() - '0');
if (expMinus) {
- while (s.Peek() >= '0' && s.Peek() <= '9') {
+ while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) {
exp = exp * 10 + static_cast<int>(s.Take() - '0');
if (exp >= 214748364) { // Issue #313: prevent overflow exponent
- while (s.Peek() >= '0' && s.Peek() <= '9') // Consume the rest of exponent
+ while (RAPIDJSON_UNLIKELY(s.Peek() >= '0' && s.Peek() <= '9')) // Consume the rest of exponent
s.Take();
}
}
}
else { // positive exp
int maxExp = 308 - expFrac;
- while (s.Peek() >= '0' && s.Peek() <= '9') {
+ while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) {
exp = exp * 10 + static_cast<int>(s.Take() - '0');
- if (exp > maxExp)
- RAPIDJSON_PARSE_ERROR(kParseErrorNumberTooBig, s.Tell());
+ if (RAPIDJSON_UNLIKELY(exp > maxExp))
+ RAPIDJSON_PARSE_ERROR(kParseErrorNumberTooBig, startOffset);
}
}
}
@@ -1010,34 +1310,60 @@
// Finish parsing, call event according to the type of number.
bool cont = true;
- size_t length = s.Length();
- const char* decimal = s.Pop(); // Pop stack no matter if it will be used or not.
-
- if (useDouble) {
- int p = exp + expFrac;
- if (parseFlags & kParseFullPrecisionFlag)
- d = internal::StrtodFullPrecision(d, p, decimal, length, decimalPosition, exp);
- else
- d = internal::StrtodNormalPrecision(d, p);
-
- cont = handler.Double(minus ? -d : d);
+
+ if (parseFlags & kParseNumbersAsStringsFlag) {
+ if (parseFlags & kParseInsituFlag) {
+ s.Pop(); // Pop stack no matter if it will be used or not.
+ typename InputStream::Ch* head = is.PutBegin();
+ const size_t length = s.Tell() - startOffset;
+ RAPIDJSON_ASSERT(length <= 0xFFFFFFFF);
+ // unable to insert the \0 character here, it will erase the comma after this number
+ const typename TargetEncoding::Ch* const str = reinterpret_cast<typename TargetEncoding::Ch*>(head);
+ cont = handler.RawNumber(str, SizeType(length), false);
+ }
+ else {
+ SizeType numCharsToCopy = static_cast<SizeType>(s.Length());
+ StringStream srcStream(s.Pop());
+ StackStream<typename TargetEncoding::Ch> dstStream(stack_);
+ while (numCharsToCopy--) {
+ Transcoder<UTF8<>, TargetEncoding>::Transcode(srcStream, dstStream);
+ }
+ dstStream.Put('\0');
+ const typename TargetEncoding::Ch* str = dstStream.Pop();
+ const SizeType length = static_cast<SizeType>(dstStream.Length()) - 1;
+ cont = handler.RawNumber(str, SizeType(length), true);
+ }
}
else {
- if (use64bit) {
- if (minus)
- cont = handler.Int64(static_cast<int64_t>(~i64 + 1));
- else
- cont = handler.Uint64(i64);
- }
- else {
- if (minus)
- cont = handler.Int(static_cast<int32_t>(~i + 1));
- else
- cont = handler.Uint(i);
- }
+ size_t length = s.Length();
+ const char* decimal = s.Pop(); // Pop stack no matter if it will be used or not.
+
+ if (useDouble) {
+ int p = exp + expFrac;
+ if (parseFlags & kParseFullPrecisionFlag)
+ d = internal::StrtodFullPrecision(d, p, decimal, length, decimalPosition, exp);
+ else
+ d = internal::StrtodNormalPrecision(d, p);
+
+ cont = handler.Double(minus ? -d : d);
+ }
+ else {
+ if (use64bit) {
+ if (minus)
+ cont = handler.Int64(static_cast<int64_t>(~i64 + 1));
+ else
+ cont = handler.Uint64(i64);
+ }
+ else {
+ if (minus)
+ cont = handler.Int(static_cast<int32_t>(~i + 1));
+ else
+ cont = handler.Uint(i);
+ }
+ }
}
- if (!cont)
- RAPIDJSON_PARSE_ERROR(kParseErrorTermination, s.Tell());
+ if (RAPIDJSON_UNLIKELY(!cont))
+ RAPIDJSON_PARSE_ERROR(kParseErrorTermination, startOffset);
}
// Parse any JSON value
@@ -1050,10 +1376,10 @@
case '"': ParseString<parseFlags>(is, handler); break;
case '{': ParseObject<parseFlags>(is, handler); break;
case '[': ParseArray <parseFlags>(is, handler); break;
- default :
+ default :
ParseNumber<parseFlags>(is, handler);
break;
-
+
}
}
@@ -1125,7 +1451,7 @@
#undef N
#undef N16
//!@endcond
-
+
if (sizeof(Ch) == 1 || static_cast<unsigned>(c) < 256)
return static_cast<Token>(tokenMap[static_cast<unsigned char>(c)]);
else
@@ -1222,7 +1548,7 @@
IterativeParsingErrorState, // Left bracket
IterativeParsingErrorState, // Right bracket
IterativeParsingErrorState, // Left curly bracket
- IterativeParsingErrorState, // Right curly bracket
+ IterativeParsingObjectFinishState, // Right curly bracket
IterativeParsingErrorState, // Comma
IterativeParsingErrorState, // Colon
IterativeParsingMemberKeyState, // String
@@ -1268,7 +1594,7 @@
// ElementDelimiter
{
IterativeParsingArrayInitialState, // Left bracket(push Element state)
- IterativeParsingErrorState, // Right bracket
+ IterativeParsingArrayFinishState, // Right bracket
IterativeParsingObjectInitialState, // Left curly bracket(push Element state)
IterativeParsingErrorState, // Right curly bracket
IterativeParsingErrorState, // Comma
@@ -1370,6 +1696,11 @@
case IterativeParsingObjectFinishState:
{
+ // Transit from delimiter is only allowed when trailing commas are enabled
+ if (!(parseFlags & kParseTrailingCommasFlag) && src == IterativeParsingMemberDelimiterState) {
+ RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorObjectMissName, is.Tell());
+ return IterativeParsingErrorState;
+ }
// Get member count.
SizeType c = *stack_.template Pop<SizeType>(1);
// If the object is not empty, count the last member.
@@ -1395,6 +1726,11 @@
case IterativeParsingArrayFinishState:
{
+ // Transit from delimiter is only allowed when trailing commas are enabled
+ if (!(parseFlags & kParseTrailingCommasFlag) && src == IterativeParsingElementDelimiterState) {
+ RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorValueInvalid, is.Tell());
+ return IterativeParsingErrorState;
+ }
// Get element count.
SizeType c = *stack_.template Pop<SizeType>(1);
// If the array is not empty, count the last element.
@@ -1446,7 +1782,7 @@
// Error flag has been set.
return;
}
-
+
switch (src) {
case IterativeParsingStartState: RAPIDJSON_PARSE_ERROR(kParseErrorDocumentEmpty, is.Tell()); return;
case IterativeParsingFinishState: RAPIDJSON_PARSE_ERROR(kParseErrorDocumentRootNotSingular, is.Tell()); return;
@@ -1454,9 +1790,12 @@
case IterativeParsingMemberDelimiterState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissName, is.Tell()); return;
case IterativeParsingMemberKeyState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissColon, is.Tell()); return;
case IterativeParsingMemberValueState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, is.Tell()); return;
+ case IterativeParsingKeyValueDelimiterState:
+ case IterativeParsingArrayInitialState:
+ case IterativeParsingElementDelimiterState: RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); return;
case IterativeParsingElementState: RAPIDJSON_PARSE_ERROR(kParseErrorArrayMissCommaOrSquareBracket, is.Tell()); return;
default: RAPIDJSON_PARSE_ERROR(kParseErrorUnspecificSyntaxError, is.Tell()); return;
- }
+ }
}
template <unsigned parseFlags, typename InputStream, typename Handler>
=== added file 'shorts/xml2json/rapidjson/schema.h'
--- shorts/xml2json/rapidjson/schema.h 1970-01-01 00:00:00 +0000
+++ shorts/xml2json/rapidjson/schema.h 2016-06-10 15:12:59 +0000
@@ -0,0 +1,2017 @@
+// Tencent is pleased to support the open source community by making RapidJSON available->
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip-> All rights reserved->
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License-> You may obtain a copy of the License at
+//
+// http://opensource->org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied-> See the License for the
+// specific language governing permissions and limitations under the License->
+
+#ifndef RAPIDJSON_SCHEMA_H_
+#define RAPIDJSON_SCHEMA_H_
+
+#include "document.h"
+#include "pointer.h"
+#include <cmath> // abs, floor
+
+#ifdef __clang__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(weak-vtables)
+RAPIDJSON_DIAG_OFF(exit-time-destructors)
+RAPIDJSON_DIAG_OFF(c++98-compat-pedantic)
+#endif
+
+#if !defined(RAPIDJSON_SCHEMA_USE_INTERNALREGEX)
+#define RAPIDJSON_SCHEMA_USE_INTERNALREGEX 1
+#else
+#define RAPIDJSON_SCHEMA_USE_INTERNALREGEX 0
+#endif
+
+#if !RAPIDJSON_SCHEMA_USE_INTERNALREGEX && !defined(RAPIDJSON_SCHEMA_USE_STDREGEX) && (__cplusplus >=201103L || (defined(_MSC_VER) && _MSC_VER >= 1800))
+#define RAPIDJSON_SCHEMA_USE_STDREGEX 1
+#else
+#define RAPIDJSON_SCHEMA_USE_STDREGEX 0
+#endif
+
+#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX
+#include "internal/regex.h"
+#elif RAPIDJSON_SCHEMA_USE_STDREGEX
+#include <regex>
+#endif
+
+#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX || RAPIDJSON_SCHEMA_USE_STDREGEX
+#define RAPIDJSON_SCHEMA_HAS_REGEX 1
+#else
+#define RAPIDJSON_SCHEMA_HAS_REGEX 0
+#endif
+
+#ifndef RAPIDJSON_SCHEMA_VERBOSE
+#define RAPIDJSON_SCHEMA_VERBOSE 0
+#endif
+
+#if RAPIDJSON_SCHEMA_VERBOSE
+#include "stringbuffer.h"
+#endif
+
+#if defined(__GNUC__)
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(effc++)
+#endif
+
+#ifdef __clang__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(variadic-macros)
+#endif
+
+#ifdef _MSC_VER
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated
+#endif
+
+RAPIDJSON_NAMESPACE_BEGIN
+
+///////////////////////////////////////////////////////////////////////////////
+// Verbose Utilities
+
+#if RAPIDJSON_SCHEMA_VERBOSE
+
+namespace internal {
+
+inline void PrintInvalidKeyword(const char* keyword) {
+ printf("Fail keyword: %s\n", keyword);
+}
+
+inline void PrintInvalidKeyword(const wchar_t* keyword) {
+ wprintf(L"Fail keyword: %ls\n", keyword);
+}
+
+inline void PrintInvalidDocument(const char* document) {
+ printf("Fail document: %s\n\n", document);
+}
+
+inline void PrintInvalidDocument(const wchar_t* document) {
+ wprintf(L"Fail document: %ls\n\n", document);
+}
+
+inline void PrintValidatorPointers(unsigned depth, const char* s, const char* d) {
+ printf("S: %*s%s\nD: %*s%s\n\n", depth * 4, " ", s, depth * 4, " ", d);
+}
+
+inline void PrintValidatorPointers(unsigned depth, const wchar_t* s, const wchar_t* d) {
+ wprintf(L"S: %*ls%ls\nD: %*ls%ls\n\n", depth * 4, L" ", s, depth * 4, L" ", d);
+}
+
+} // namespace internal
+
+#endif // RAPIDJSON_SCHEMA_VERBOSE
+
+///////////////////////////////////////////////////////////////////////////////
+// RAPIDJSON_INVALID_KEYWORD_RETURN
+
+#if RAPIDJSON_SCHEMA_VERBOSE
+#define RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword) internal::PrintInvalidKeyword(keyword)
+#else
+#define RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword)
+#endif
+
+#define RAPIDJSON_INVALID_KEYWORD_RETURN(keyword)\
+RAPIDJSON_MULTILINEMACRO_BEGIN\
+ context.invalidKeyword = keyword.GetString();\
+ RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword.GetString());\
+ return false;\
+RAPIDJSON_MULTILINEMACRO_END
+
+///////////////////////////////////////////////////////////////////////////////
+// Forward declarations
+
+template <typename ValueType, typename Allocator>
+class GenericSchemaDocument;
+
+namespace internal {
+
+template <typename SchemaDocumentType>
+class Schema;
+
+///////////////////////////////////////////////////////////////////////////////
+// ISchemaValidator
+
+class ISchemaValidator {
+public:
+ virtual ~ISchemaValidator() {}
+ virtual bool IsValid() const = 0;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// ISchemaStateFactory
+
+template <typename SchemaType>
+class ISchemaStateFactory {
+public:
+ virtual ~ISchemaStateFactory() {}
+ virtual ISchemaValidator* CreateSchemaValidator(const SchemaType&) = 0;
+ virtual void DestroySchemaValidator(ISchemaValidator* validator) = 0;
+ virtual void* CreateHasher() = 0;
+ virtual uint64_t GetHashCode(void* hasher) = 0;
+ virtual void DestroryHasher(void* hasher) = 0;
+ virtual void* MallocState(size_t size) = 0;
+ virtual void* ReallocState(void* originalPtr, size_t originalSize, size_t newSize) = 0;
+ virtual void FreeState(void* p) = 0;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// Hasher
+
+// For comparison of compound value
+template<typename Encoding, typename Allocator>
+class Hasher {
+public:
+ typedef typename Encoding::Ch Ch;
+
+ Hasher(Allocator* allocator = 0, size_t stackCapacity = kDefaultSize) : stack_(allocator, stackCapacity) {}
+
+ bool Null() { return WriteType(kNullType); }
+ bool Bool(bool b) { return WriteType(b ? kTrueType : kFalseType); }
+ bool Int(int i) { Number n; n.u.i = i; n.d = static_cast<double>(i); return WriteNumber(n); }
+ bool Uint(unsigned u) { Number n; n.u.u = u; n.d = static_cast<double>(u); return WriteNumber(n); }
+ bool Int64(int64_t i) { Number n; n.u.i = i; n.d = static_cast<double>(i); return WriteNumber(n); }
+ bool Uint64(uint64_t u) { Number n; n.u.u = u; n.d = static_cast<double>(u); return WriteNumber(n); }
+ bool Double(double d) {
+ Number n;
+ if (d < 0) n.u.i = static_cast<int64_t>(d);
+ else n.u.u = static_cast<uint64_t>(d);
+ n.d = d;
+ return WriteNumber(n);
+ }
+
+ bool RawNumber(const Ch* str, SizeType len, bool) {
+ WriteBuffer(kNumberType, str, len * sizeof(Ch));
+ return true;
+ }
+
+ bool String(const Ch* str, SizeType len, bool) {
+ WriteBuffer(kStringType, str, len * sizeof(Ch));
+ return true;
+ }
+
+ bool StartObject() { return true; }
+ bool Key(const Ch* str, SizeType len, bool copy) { return String(str, len, copy); }
+ bool EndObject(SizeType memberCount) {
+ uint64_t h = Hash(0, kObjectType);
+ uint64_t* kv = stack_.template Pop<uint64_t>(memberCount * 2);
+ for (SizeType i = 0; i < memberCount; i++)
+ h ^= Hash(kv[i * 2], kv[i * 2 + 1]); // Use xor to achieve member order insensitive
+ *stack_.template Push<uint64_t>() = h;
+ return true;
+ }
+
+ bool StartArray() { return true; }
+ bool EndArray(SizeType elementCount) {
+ uint64_t h = Hash(0, kArrayType);
+ uint64_t* e = stack_.template Pop<uint64_t>(elementCount);
+ for (SizeType i = 0; i < elementCount; i++)
+ h = Hash(h, e[i]); // Use hash to achieve element order sensitive
+ *stack_.template Push<uint64_t>() = h;
+ return true;
+ }
+
+ bool IsValid() const { return stack_.GetSize() == sizeof(uint64_t); }
+
+ uint64_t GetHashCode() const {
+ RAPIDJSON_ASSERT(IsValid());
+ return *stack_.template Top<uint64_t>();
+ }
+
+private:
+ static const size_t kDefaultSize = 256;
+ struct Number {
+ union U {
+ uint64_t u;
+ int64_t i;
+ }u;
+ double d;
+ };
+
+ bool WriteType(Type type) { return WriteBuffer(type, 0, 0); }
+
+ bool WriteNumber(const Number& n) { return WriteBuffer(kNumberType, &n, sizeof(n)); }
+
+ bool WriteBuffer(Type type, const void* data, size_t len) {
+ // FNV-1a from http://isthe.com/chongo/tech/comp/fnv/
+ uint64_t h = Hash(RAPIDJSON_UINT64_C2(0x84222325, 0xcbf29ce4), type);
+ const unsigned char* d = static_cast<const unsigned char*>(data);
+ for (size_t i = 0; i < len; i++)
+ h = Hash(h, d[i]);
+ *stack_.template Push<uint64_t>() = h;
+ return true;
+ }
+
+ static uint64_t Hash(uint64_t h, uint64_t d) {
+ static const uint64_t kPrime = RAPIDJSON_UINT64_C2(0x00000100, 0x000001b3);
+ h ^= d;
+ h *= kPrime;
+ return h;
+ }
+
+ Stack<Allocator> stack_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// SchemaValidationContext
+
+template <typename SchemaDocumentType>
+struct SchemaValidationContext {
+ typedef Schema<SchemaDocumentType> SchemaType;
+ typedef ISchemaStateFactory<SchemaType> SchemaValidatorFactoryType;
+ typedef typename SchemaType::ValueType ValueType;
+ typedef typename ValueType::Ch Ch;
+
+ enum PatternValidatorType {
+ kPatternValidatorOnly,
+ kPatternValidatorWithProperty,
+ kPatternValidatorWithAdditionalProperty
+ };
+
+ SchemaValidationContext(SchemaValidatorFactoryType& f, const SchemaType* s) :
+ factory(f),
+ schema(s),
+ valueSchema(),
+ invalidKeyword(),
+ hasher(),
+ arrayElementHashCodes(),
+ validators(),
+ validatorCount(),
+ patternPropertiesValidators(),
+ patternPropertiesValidatorCount(),
+ patternPropertiesSchemas(),
+ patternPropertiesSchemaCount(),
+ valuePatternValidatorType(kPatternValidatorOnly),
+ objectDependencies(),
+ inArray(false),
+ valueUniqueness(false),
+ arrayUniqueness(false)
+ {
+ }
+
+ ~SchemaValidationContext() {
+ if (hasher)
+ factory.DestroryHasher(hasher);
+ if (validators) {
+ for (SizeType i = 0; i < validatorCount; i++)
+ factory.DestroySchemaValidator(validators[i]);
+ factory.FreeState(validators);
+ }
+ if (patternPropertiesValidators) {
+ for (SizeType i = 0; i < patternPropertiesValidatorCount; i++)
+ factory.DestroySchemaValidator(patternPropertiesValidators[i]);
+ factory.FreeState(patternPropertiesValidators);
+ }
+ if (patternPropertiesSchemas)
+ factory.FreeState(patternPropertiesSchemas);
+ if (objectDependencies)
+ factory.FreeState(objectDependencies);
+ }
+
+ SchemaValidatorFactoryType& factory;
+ const SchemaType* schema;
+ const SchemaType* valueSchema;
+ const Ch* invalidKeyword;
+ void* hasher; // Only validator access
+ void* arrayElementHashCodes; // Only validator access this
+ ISchemaValidator** validators;
+ SizeType validatorCount;
+ ISchemaValidator** patternPropertiesValidators;
+ SizeType patternPropertiesValidatorCount;
+ const SchemaType** patternPropertiesSchemas;
+ SizeType patternPropertiesSchemaCount;
+ PatternValidatorType valuePatternValidatorType;
+ PatternValidatorType objectPatternValidatorType;
+ SizeType objectRequiredCount;
+ SizeType arrayElementIndex;
+ bool* objectDependencies;
+ bool inArray;
+ bool valueUniqueness;
+ bool arrayUniqueness;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// Schema
+
+template <typename SchemaDocumentType>
+class Schema {
+public:
+ typedef typename SchemaDocumentType::ValueType ValueType;
+ typedef typename SchemaDocumentType::AllocatorType AllocatorType;
+ typedef typename SchemaDocumentType::PointerType PointerType;
+ typedef typename ValueType::EncodingType EncodingType;
+ typedef typename EncodingType::Ch Ch;
+ typedef SchemaValidationContext<SchemaDocumentType> Context;
+ typedef Schema<SchemaDocumentType> SchemaType;
+ typedef GenericValue<EncodingType, AllocatorType> SValue;
+ friend class GenericSchemaDocument<ValueType, AllocatorType>;
+
+ Schema(SchemaDocumentType* schemaDocument, const PointerType& p, const ValueType& value, const ValueType& document, AllocatorType* allocator) :
+ allocator_(allocator),
+ enum_(),
+ enumCount_(),
+ not_(),
+ type_((1 << kTotalSchemaType) - 1), // typeless
+ validatorCount_(),
+ properties_(),
+ additionalPropertiesSchema_(),
+ patternProperties_(),
+ patternPropertyCount_(),
+ propertyCount_(),
+ requiredCount_(),
+ minProperties_(),
+ maxProperties_(SizeType(~0)),
+ additionalProperties_(true),
+ hasDependencies_(),
+ hasSchemaDependencies_(),
+ additionalItemsSchema_(),
+ itemsList_(),
+ itemsTuple_(),
+ itemsTupleCount_(),
+ minItems_(),
+ maxItems_(SizeType(~0)),
+ additionalItems_(true),
+ uniqueItems_(false),
+ pattern_(),
+ minLength_(0),
+ maxLength_(~SizeType(0)),
+ exclusiveMinimum_(false),
+ exclusiveMaximum_(false)
+ {
+ typedef typename SchemaDocumentType::ValueType ValueType;
+ typedef typename ValueType::ConstValueIterator ConstValueIterator;
+ typedef typename ValueType::ConstMemberIterator ConstMemberIterator;
+
+ if (!value.IsObject())
+ return;
+
+ if (const ValueType* v = GetMember(value, GetTypeString())) {
+ type_ = 0;
+ if (v->IsString())
+ AddType(*v);
+ else if (v->IsArray())
+ for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr)
+ AddType(*itr);
+ }
+
+ if (const ValueType* v = GetMember(value, GetEnumString()))
+ if (v->IsArray() && v->Size() > 0) {
+ enum_ = static_cast<uint64_t*>(allocator_->Malloc(sizeof(uint64_t) * v->Size()));
+ for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr) {
+ typedef Hasher<EncodingType, MemoryPoolAllocator<> > EnumHasherType;
+ char buffer[256 + 24];
+ MemoryPoolAllocator<> hasherAllocator(buffer, sizeof(buffer));
+ EnumHasherType h(&hasherAllocator, 256);
+ itr->Accept(h);
+ enum_[enumCount_++] = h.GetHashCode();
+ }
+ }
+
+ AssignIfExist(allOf_, *schemaDocument, p, value, GetAllOfString(), document);
+ AssignIfExist(anyOf_, *schemaDocument, p, value, GetAnyOfString(), document);
+ AssignIfExist(oneOf_, *schemaDocument, p, value, GetOneOfString(), document);
+
+ if (const ValueType* v = GetMember(value, GetNotString())) {
+ schemaDocument->CreateSchema(¬_, p.Append(GetNotString(), allocator_), *v, document);
+ notValidatorIndex_ = validatorCount_;
+ validatorCount_++;
+ }
+
+ // Object
+
+ const ValueType* properties = GetMember(value, GetPropertiesString());
+ const ValueType* required = GetMember(value, GetRequiredString());
+ const ValueType* dependencies = GetMember(value, GetDependenciesString());
+ {
+ // Gather properties from properties/required/dependencies
+ SValue allProperties(kArrayType);
+
+ if (properties && properties->IsObject())
+ for (ConstMemberIterator itr = properties->MemberBegin(); itr != properties->MemberEnd(); ++itr)
+ AddUniqueElement(allProperties, itr->name);
+
+ if (required && required->IsArray())
+ for (ConstValueIterator itr = required->Begin(); itr != required->End(); ++itr)
+ if (itr->IsString())
+ AddUniqueElement(allProperties, *itr);
+
+ if (dependencies && dependencies->IsObject())
+ for (ConstMemberIterator itr = dependencies->MemberBegin(); itr != dependencies->MemberEnd(); ++itr) {
+ AddUniqueElement(allProperties, itr->name);
+ if (itr->value.IsArray())
+ for (ConstValueIterator i = itr->value.Begin(); i != itr->value.End(); ++i)
+ if (i->IsString())
+ AddUniqueElement(allProperties, *i);
+ }
+
+ if (allProperties.Size() > 0) {
+ propertyCount_ = allProperties.Size();
+ properties_ = static_cast<Property*>(allocator_->Malloc(sizeof(Property) * propertyCount_));
+ for (SizeType i = 0; i < propertyCount_; i++) {
+ new (&properties_[i]) Property();
+ properties_[i].name = allProperties[i];
+ properties_[i].schema = GetTypeless();
+ }
+ }
+ }
+
+ if (properties && properties->IsObject()) {
+ PointerType q = p.Append(GetPropertiesString(), allocator_);
+ for (ConstMemberIterator itr = properties->MemberBegin(); itr != properties->MemberEnd(); ++itr) {
+ SizeType index;
+ if (FindPropertyIndex(itr->name, &index))
+ schemaDocument->CreateSchema(&properties_[index].schema, q.Append(itr->name, allocator_), itr->value, document);
+ }
+ }
+
+ if (const ValueType* v = GetMember(value, GetPatternPropertiesString())) {
+ PointerType q = p.Append(GetPatternPropertiesString(), allocator_);
+ patternProperties_ = static_cast<PatternProperty*>(allocator_->Malloc(sizeof(PatternProperty) * v->MemberCount()));
+ patternPropertyCount_ = 0;
+
+ for (ConstMemberIterator itr = v->MemberBegin(); itr != v->MemberEnd(); ++itr) {
+ new (&patternProperties_[patternPropertyCount_]) PatternProperty();
+ patternProperties_[patternPropertyCount_].pattern = CreatePattern(itr->name);
+ schemaDocument->CreateSchema(&patternProperties_[patternPropertyCount_].schema, q.Append(itr->name, allocator_), itr->value, document);
+ patternPropertyCount_++;
+ }
+ }
+
+ if (required && required->IsArray())
+ for (ConstValueIterator itr = required->Begin(); itr != required->End(); ++itr)
+ if (itr->IsString()) {
+ SizeType index;
+ if (FindPropertyIndex(*itr, &index)) {
+ properties_[index].required = true;
+ requiredCount_++;
+ }
+ }
+
+ if (dependencies && dependencies->IsObject()) {
+ PointerType q = p.Append(GetDependenciesString(), allocator_);
+ hasDependencies_ = true;
+ for (ConstMemberIterator itr = dependencies->MemberBegin(); itr != dependencies->MemberEnd(); ++itr) {
+ SizeType sourceIndex;
+ if (FindPropertyIndex(itr->name, &sourceIndex)) {
+ if (itr->value.IsArray()) {
+ properties_[sourceIndex].dependencies = static_cast<bool*>(allocator_->Malloc(sizeof(bool) * propertyCount_));
+ std::memset(properties_[sourceIndex].dependencies, 0, sizeof(bool)* propertyCount_);
+ for (ConstValueIterator targetItr = itr->value.Begin(); targetItr != itr->value.End(); ++targetItr) {
+ SizeType targetIndex;
+ if (FindPropertyIndex(*targetItr, &targetIndex))
+ properties_[sourceIndex].dependencies[targetIndex] = true;
+ }
+ }
+ else if (itr->value.IsObject()) {
+ hasSchemaDependencies_ = true;
+ schemaDocument->CreateSchema(&properties_[sourceIndex].dependenciesSchema, q.Append(itr->name, allocator_), itr->value, document);
+ properties_[sourceIndex].dependenciesValidatorIndex = validatorCount_;
+ validatorCount_++;
+ }
+ }
+ }
+ }
+
+ if (const ValueType* v = GetMember(value, GetAdditionalPropertiesString())) {
+ if (v->IsBool())
+ additionalProperties_ = v->GetBool();
+ else if (v->IsObject())
+ schemaDocument->CreateSchema(&additionalPropertiesSchema_, p.Append(GetAdditionalPropertiesString(), allocator_), *v, document);
+ }
+
+ AssignIfExist(minProperties_, value, GetMinPropertiesString());
+ AssignIfExist(maxProperties_, value, GetMaxPropertiesString());
+
+ // Array
+ if (const ValueType* v = GetMember(value, GetItemsString())) {
+ PointerType q = p.Append(GetItemsString(), allocator_);
+ if (v->IsObject()) // List validation
+ schemaDocument->CreateSchema(&itemsList_, q, *v, document);
+ else if (v->IsArray()) { // Tuple validation
+ itemsTuple_ = static_cast<const Schema**>(allocator_->Malloc(sizeof(const Schema*) * v->Size()));
+ SizeType index = 0;
+ for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr, index++)
+ schemaDocument->CreateSchema(&itemsTuple_[itemsTupleCount_++], q.Append(index, allocator_), *itr, document);
+ }
+ }
+
+ AssignIfExist(minItems_, value, GetMinItemsString());
+ AssignIfExist(maxItems_, value, GetMaxItemsString());
+
+ if (const ValueType* v = GetMember(value, GetAdditionalItemsString())) {
+ if (v->IsBool())
+ additionalItems_ = v->GetBool();
+ else if (v->IsObject())
+ schemaDocument->CreateSchema(&additionalItemsSchema_, p.Append(GetAdditionalItemsString(), allocator_), *v, document);
+ }
+
+ AssignIfExist(uniqueItems_, value, GetUniqueItemsString());
+
+ // String
+ AssignIfExist(minLength_, value, GetMinLengthString());
+ AssignIfExist(maxLength_, value, GetMaxLengthString());
+
+ if (const ValueType* v = GetMember(value, GetPatternString()))
+ pattern_ = CreatePattern(*v);
+
+ // Number
+ if (const ValueType* v = GetMember(value, GetMinimumString()))
+ if (v->IsNumber())
+ minimum_.CopyFrom(*v, *allocator_);
+
+ if (const ValueType* v = GetMember(value, GetMaximumString()))
+ if (v->IsNumber())
+ maximum_.CopyFrom(*v, *allocator_);
+
+ AssignIfExist(exclusiveMinimum_, value, GetExclusiveMinimumString());
+ AssignIfExist(exclusiveMaximum_, value, GetExclusiveMaximumString());
+
+ if (const ValueType* v = GetMember(value, GetMultipleOfString()))
+ if (v->IsNumber() && v->GetDouble() > 0.0)
+ multipleOf_.CopyFrom(*v, *allocator_);
+ }
+
+ ~Schema() {
+ allocator_->Free(enum_);
+ if (properties_) {
+ for (SizeType i = 0; i < propertyCount_; i++)
+ properties_[i].~Property();
+ AllocatorType::Free(properties_);
+ }
+ if (patternProperties_) {
+ for (SizeType i = 0; i < patternPropertyCount_; i++)
+ patternProperties_[i].~PatternProperty();
+ AllocatorType::Free(patternProperties_);
+ }
+ AllocatorType::Free(itemsTuple_);
+#if RAPIDJSON_SCHEMA_HAS_REGEX
+ if (pattern_) {
+ pattern_->~RegexType();
+ allocator_->Free(pattern_);
+ }
+#endif
+ }
+
+ bool BeginValue(Context& context) const {
+ if (context.inArray) {
+ if (uniqueItems_)
+ context.valueUniqueness = true;
+
+ if (itemsList_)
+ context.valueSchema = itemsList_;
+ else if (itemsTuple_) {
+ if (context.arrayElementIndex < itemsTupleCount_)
+ context.valueSchema = itemsTuple_[context.arrayElementIndex];
+ else if (additionalItemsSchema_)
+ context.valueSchema = additionalItemsSchema_;
+ else if (additionalItems_)
+ context.valueSchema = GetTypeless();
+ else
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetItemsString());
+ }
+ else
+ context.valueSchema = GetTypeless();
+
+ context.arrayElementIndex++;
+ }
+ return true;
+ }
+
+ RAPIDJSON_FORCEINLINE bool EndValue(Context& context) const {
+ if (context.patternPropertiesValidatorCount > 0) {
+ bool otherValid = false;
+ SizeType count = context.patternPropertiesValidatorCount;
+ if (context.objectPatternValidatorType != Context::kPatternValidatorOnly)
+ otherValid = context.patternPropertiesValidators[--count]->IsValid();
+
+ bool patternValid = true;
+ for (SizeType i = 0; i < count; i++)
+ if (!context.patternPropertiesValidators[i]->IsValid()) {
+ patternValid = false;
+ break;
+ }
+
+ if (context.objectPatternValidatorType == Context::kPatternValidatorOnly) {
+ if (!patternValid)
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString());
+ }
+ else if (context.objectPatternValidatorType == Context::kPatternValidatorWithProperty) {
+ if (!patternValid || !otherValid)
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString());
+ }
+ else if (!patternValid && !otherValid) // kPatternValidatorWithAdditionalProperty)
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString());
+ }
+
+ if (enum_) {
+ const uint64_t h = context.factory.GetHashCode(context.hasher);
+ for (SizeType i = 0; i < enumCount_; i++)
+ if (enum_[i] == h)
+ goto foundEnum;
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetEnumString());
+ foundEnum:;
+ }
+
+ if (allOf_.schemas)
+ for (SizeType i = allOf_.begin; i < allOf_.begin + allOf_.count; i++)
+ if (!context.validators[i]->IsValid())
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetAllOfString());
+
+ if (anyOf_.schemas) {
+ for (SizeType i = anyOf_.begin; i < anyOf_.begin + anyOf_.count; i++)
+ if (context.validators[i]->IsValid())
+ goto foundAny;
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetAnyOfString());
+ foundAny:;
+ }
+
+ if (oneOf_.schemas) {
+ bool oneValid = false;
+ for (SizeType i = oneOf_.begin; i < oneOf_.begin + oneOf_.count; i++)
+ if (context.validators[i]->IsValid()) {
+ if (oneValid)
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetOneOfString());
+ else
+ oneValid = true;
+ }
+ if (!oneValid)
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetOneOfString());
+ }
+
+ if (not_ && context.validators[notValidatorIndex_]->IsValid())
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetNotString());
+
+ return true;
+ }
+
+ bool Null(Context& context) const {
+ if (!(type_ & (1 << kNullSchemaType)))
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
+ return CreateParallelValidator(context);
+ }
+
+ bool Bool(Context& context, bool) const {
+ if (!(type_ & (1 << kBooleanSchemaType)))
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
+ return CreateParallelValidator(context);
+ }
+
+ bool Int(Context& context, int i) const {
+ if (!CheckInt(context, i))
+ return false;
+ return CreateParallelValidator(context);
+ }
+
+ bool Uint(Context& context, unsigned u) const {
+ if (!CheckUint(context, u))
+ return false;
+ return CreateParallelValidator(context);
+ }
+
+ bool Int64(Context& context, int64_t i) const {
+ if (!CheckInt(context, i))
+ return false;
+ return CreateParallelValidator(context);
+ }
+
+ bool Uint64(Context& context, uint64_t u) const {
+ if (!CheckUint(context, u))
+ return false;
+ return CreateParallelValidator(context);
+ }
+
+ bool Double(Context& context, double d) const {
+ if (!(type_ & (1 << kNumberSchemaType)))
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
+
+ if (!minimum_.IsNull() && !CheckDoubleMinimum(context, d))
+ return false;
+
+ if (!maximum_.IsNull() && !CheckDoubleMaximum(context, d))
+ return false;
+
+ if (!multipleOf_.IsNull() && !CheckDoubleMultipleOf(context, d))
+ return false;
+
+ return CreateParallelValidator(context);
+ }
+
+ bool String(Context& context, const Ch* str, SizeType length, bool) const {
+ if (!(type_ & (1 << kStringSchemaType)))
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
+
+ if (minLength_ != 0 || maxLength_ != SizeType(~0)) {
+ SizeType count;
+ if (internal::CountStringCodePoint<EncodingType>(str, length, &count)) {
+ if (count < minLength_)
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinLengthString());
+ if (count > maxLength_)
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxLengthString());
+ }
+ }
+
+ if (pattern_ && !IsPatternMatch(pattern_, str, length))
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternString());
+
+ return CreateParallelValidator(context);
+ }
+
+ bool StartObject(Context& context) const {
+ if (!(type_ & (1 << kObjectSchemaType)))
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
+
+ context.objectRequiredCount = 0;
+ if (hasDependencies_) {
+ context.objectDependencies = static_cast<bool*>(context.factory.MallocState(sizeof(bool) * propertyCount_));
+ std::memset(context.objectDependencies, 0, sizeof(bool) * propertyCount_);
+ }
+
+ if (patternProperties_) { // pre-allocate schema array
+ SizeType count = patternPropertyCount_ + 1; // extra for valuePatternValidatorType
+ context.patternPropertiesSchemas = static_cast<const SchemaType**>(context.factory.MallocState(sizeof(const SchemaType*) * count));
+ context.patternPropertiesSchemaCount = 0;
+ std::memset(context.patternPropertiesSchemas, 0, sizeof(SchemaType*) * count);
+ }
+
+ return CreateParallelValidator(context);
+ }
+
+ bool Key(Context& context, const Ch* str, SizeType len, bool) const {
+ if (patternProperties_) {
+ context.patternPropertiesSchemaCount = 0;
+ for (SizeType i = 0; i < patternPropertyCount_; i++)
+ if (patternProperties_[i].pattern && IsPatternMatch(patternProperties_[i].pattern, str, len))
+ context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = patternProperties_[i].schema;
+ }
+
+ SizeType index;
+ if (FindPropertyIndex(ValueType(str, len).Move(), &index)) {
+ if (context.patternPropertiesSchemaCount > 0) {
+ context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = properties_[index].schema;
+ context.valueSchema = GetTypeless();
+ context.valuePatternValidatorType = Context::kPatternValidatorWithProperty;
+ }
+ else
+ context.valueSchema = properties_[index].schema;
+
+ if (properties_[index].required)
+ context.objectRequiredCount++;
+
+ if (hasDependencies_)
+ context.objectDependencies[index] = true;
+
+ return true;
+ }
+
+ if (additionalPropertiesSchema_) {
+ if (additionalPropertiesSchema_ && context.patternPropertiesSchemaCount > 0) {
+ context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = additionalPropertiesSchema_;
+ context.valueSchema = GetTypeless();
+ context.valuePatternValidatorType = Context::kPatternValidatorWithAdditionalProperty;
+ }
+ else
+ context.valueSchema = additionalPropertiesSchema_;
+ return true;
+ }
+ else if (additionalProperties_) {
+ context.valueSchema = GetTypeless();
+ return true;
+ }
+
+ if (context.patternPropertiesSchemaCount == 0) // patternProperties are not additional properties
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetAdditionalPropertiesString());
+
+ return true;
+ }
+
+ bool EndObject(Context& context, SizeType memberCount) const {
+ if (context.objectRequiredCount != requiredCount_)
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetRequiredString());
+
+ if (memberCount < minProperties_)
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinPropertiesString());
+
+ if (memberCount > maxProperties_)
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxPropertiesString());
+
+ if (hasDependencies_) {
+ for (SizeType sourceIndex = 0; sourceIndex < propertyCount_; sourceIndex++)
+ if (context.objectDependencies[sourceIndex]) {
+ if (properties_[sourceIndex].dependencies) {
+ for (SizeType targetIndex = 0; targetIndex < propertyCount_; targetIndex++)
+ if (properties_[sourceIndex].dependencies[targetIndex] && !context.objectDependencies[targetIndex])
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetDependenciesString());
+ }
+ else if (properties_[sourceIndex].dependenciesSchema)
+ if (!context.validators[properties_[sourceIndex].dependenciesValidatorIndex]->IsValid())
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetDependenciesString());
+ }
+ }
+
+ return true;
+ }
+
+ bool StartArray(Context& context) const {
+ if (!(type_ & (1 << kArraySchemaType)))
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
+
+ context.arrayElementIndex = 0;
+ context.inArray = true;
+
+ return CreateParallelValidator(context);
+ }
+
+ bool EndArray(Context& context, SizeType elementCount) const {
+ context.inArray = false;
+
+ if (elementCount < minItems_)
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinItemsString());
+
+ if (elementCount > maxItems_)
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxItemsString());
+
+ return true;
+ }
+
+ // Generate functions for string literal according to Ch
+#define RAPIDJSON_STRING_(name, ...) \
+ static const ValueType& Get##name##String() {\
+ static const Ch s[] = { __VA_ARGS__, '\0' };\
+ static const ValueType v(s, sizeof(s) / sizeof(Ch) - 1);\
+ return v;\
+ }
+
+ RAPIDJSON_STRING_(Null, 'n', 'u', 'l', 'l')
+ RAPIDJSON_STRING_(Boolean, 'b', 'o', 'o', 'l', 'e', 'a', 'n')
+ RAPIDJSON_STRING_(Object, 'o', 'b', 'j', 'e', 'c', 't')
+ RAPIDJSON_STRING_(Array, 'a', 'r', 'r', 'a', 'y')
+ RAPIDJSON_STRING_(String, 's', 't', 'r', 'i', 'n', 'g')
+ RAPIDJSON_STRING_(Number, 'n', 'u', 'm', 'b', 'e', 'r')
+ RAPIDJSON_STRING_(Integer, 'i', 'n', 't', 'e', 'g', 'e', 'r')
+ RAPIDJSON_STRING_(Type, 't', 'y', 'p', 'e')
+ RAPIDJSON_STRING_(Enum, 'e', 'n', 'u', 'm')
+ RAPIDJSON_STRING_(AllOf, 'a', 'l', 'l', 'O', 'f')
+ RAPIDJSON_STRING_(AnyOf, 'a', 'n', 'y', 'O', 'f')
+ RAPIDJSON_STRING_(OneOf, 'o', 'n', 'e', 'O', 'f')
+ RAPIDJSON_STRING_(Not, 'n', 'o', 't')
+ RAPIDJSON_STRING_(Properties, 'p', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
+ RAPIDJSON_STRING_(Required, 'r', 'e', 'q', 'u', 'i', 'r', 'e', 'd')
+ RAPIDJSON_STRING_(Dependencies, 'd', 'e', 'p', 'e', 'n', 'd', 'e', 'n', 'c', 'i', 'e', 's')
+ RAPIDJSON_STRING_(PatternProperties, 'p', 'a', 't', 't', 'e', 'r', 'n', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
+ RAPIDJSON_STRING_(AdditionalProperties, 'a', 'd', 'd', 'i', 't', 'i', 'o', 'n', 'a', 'l', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
+ RAPIDJSON_STRING_(MinProperties, 'm', 'i', 'n', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
+ RAPIDJSON_STRING_(MaxProperties, 'm', 'a', 'x', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
+ RAPIDJSON_STRING_(Items, 'i', 't', 'e', 'm', 's')
+ RAPIDJSON_STRING_(MinItems, 'm', 'i', 'n', 'I', 't', 'e', 'm', 's')
+ RAPIDJSON_STRING_(MaxItems, 'm', 'a', 'x', 'I', 't', 'e', 'm', 's')
+ RAPIDJSON_STRING_(AdditionalItems, 'a', 'd', 'd', 'i', 't', 'i', 'o', 'n', 'a', 'l', 'I', 't', 'e', 'm', 's')
+ RAPIDJSON_STRING_(UniqueItems, 'u', 'n', 'i', 'q', 'u', 'e', 'I', 't', 'e', 'm', 's')
+ RAPIDJSON_STRING_(MinLength, 'm', 'i', 'n', 'L', 'e', 'n', 'g', 't', 'h')
+ RAPIDJSON_STRING_(MaxLength, 'm', 'a', 'x', 'L', 'e', 'n', 'g', 't', 'h')
+ RAPIDJSON_STRING_(Pattern, 'p', 'a', 't', 't', 'e', 'r', 'n')
+ RAPIDJSON_STRING_(Minimum, 'm', 'i', 'n', 'i', 'm', 'u', 'm')
+ RAPIDJSON_STRING_(Maximum, 'm', 'a', 'x', 'i', 'm', 'u', 'm')
+ RAPIDJSON_STRING_(ExclusiveMinimum, 'e', 'x', 'c', 'l', 'u', 's', 'i', 'v', 'e', 'M', 'i', 'n', 'i', 'm', 'u', 'm')
+ RAPIDJSON_STRING_(ExclusiveMaximum, 'e', 'x', 'c', 'l', 'u', 's', 'i', 'v', 'e', 'M', 'a', 'x', 'i', 'm', 'u', 'm')
+ RAPIDJSON_STRING_(MultipleOf, 'm', 'u', 'l', 't', 'i', 'p', 'l', 'e', 'O', 'f')
+
+#undef RAPIDJSON_STRING_
+
+private:
+ enum SchemaValueType {
+ kNullSchemaType,
+ kBooleanSchemaType,
+ kObjectSchemaType,
+ kArraySchemaType,
+ kStringSchemaType,
+ kNumberSchemaType,
+ kIntegerSchemaType,
+ kTotalSchemaType
+ };
+
+#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX
+ typedef internal::GenericRegex<EncodingType> RegexType;
+#elif RAPIDJSON_SCHEMA_USE_STDREGEX
+ typedef std::basic_regex<Ch> RegexType;
+#else
+ typedef char RegexType;
+#endif
+
+ struct SchemaArray {
+ SchemaArray() : schemas(), count() {}
+ ~SchemaArray() { AllocatorType::Free(schemas); }
+ const SchemaType** schemas;
+ SizeType begin; // begin index of context.validators
+ SizeType count;
+ };
+
+ static const SchemaType* GetTypeless() {
+ static SchemaType typeless(0, PointerType(), ValueType(kObjectType).Move(), ValueType(kObjectType).Move(), 0);
+ return &typeless;
+ }
+
+ template <typename V1, typename V2>
+ void AddUniqueElement(V1& a, const V2& v) {
+ for (typename V1::ConstValueIterator itr = a.Begin(); itr != a.End(); ++itr)
+ if (*itr == v)
+ return;
+ V1 c(v, *allocator_);
+ a.PushBack(c, *allocator_);
+ }
+
+ static const ValueType* GetMember(const ValueType& value, const ValueType& name) {
+ typename ValueType::ConstMemberIterator itr = value.FindMember(name);
+ return itr != value.MemberEnd() ? &(itr->value) : 0;
+ }
+
+ static void AssignIfExist(bool& out, const ValueType& value, const ValueType& name) {
+ if (const ValueType* v = GetMember(value, name))
+ if (v->IsBool())
+ out = v->GetBool();
+ }
+
+ static void AssignIfExist(SizeType& out, const ValueType& value, const ValueType& name) {
+ if (const ValueType* v = GetMember(value, name))
+ if (v->IsUint64() && v->GetUint64() <= SizeType(~0))
+ out = static_cast<SizeType>(v->GetUint64());
+ }
+
+ void AssignIfExist(SchemaArray& out, SchemaDocumentType& schemaDocument, const PointerType& p, const ValueType& value, const ValueType& name, const ValueType& document) {
+ if (const ValueType* v = GetMember(value, name)) {
+ if (v->IsArray() && v->Size() > 0) {
+ PointerType q = p.Append(name, allocator_);
+ out.count = v->Size();
+ out.schemas = static_cast<const Schema**>(allocator_->Malloc(out.count * sizeof(const Schema*)));
+ memset(out.schemas, 0, sizeof(Schema*)* out.count);
+ for (SizeType i = 0; i < out.count; i++)
+ schemaDocument.CreateSchema(&out.schemas[i], q.Append(i, allocator_), (*v)[i], document);
+ out.begin = validatorCount_;
+ validatorCount_ += out.count;
+ }
+ }
+ }
+
+#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX
+ template <typename ValueType>
+ RegexType* CreatePattern(const ValueType& value) {
+ if (value.IsString()) {
+ RegexType* r = new (allocator_->Malloc(sizeof(RegexType))) RegexType(value.GetString());
+ if (!r->IsValid()) {
+ r->~RegexType();
+ r = 0;
+ }
+ return r;
+ }
+ return 0;
+ }
+
+ static bool IsPatternMatch(const RegexType* pattern, const Ch *str, SizeType) {
+ return pattern->Search(str);
+ }
+#elif RAPIDJSON_SCHEMA_USE_STDREGEX
+ template <typename ValueType>
+ RegexType* CreatePattern(const ValueType& value) {
+ if (value.IsString())
+ try {
+ return new (allocator_->Malloc(sizeof(RegexType))) RegexType(value.GetString(), std::size_t(value.GetStringLength()), std::regex_constants::ECMAScript);
+ }
+ catch (const std::regex_error&) {
+ }
+ return 0;
+ }
+
+ static bool IsPatternMatch(const RegexType* pattern, const Ch *str, SizeType length) {
+ std::match_results<const Ch*> r;
+ return std::regex_search(str, str + length, r, *pattern);
+ }
+#else
+ template <typename ValueType>
+ RegexType* CreatePattern(const ValueType&) { return 0; }
+
+ static bool IsPatternMatch(const RegexType*, const Ch *, SizeType) { return true; }
+#endif // RAPIDJSON_SCHEMA_USE_STDREGEX
+
+ void AddType(const ValueType& type) {
+ if (type == GetNullString() ) type_ |= 1 << kNullSchemaType;
+ else if (type == GetBooleanString()) type_ |= 1 << kBooleanSchemaType;
+ else if (type == GetObjectString() ) type_ |= 1 << kObjectSchemaType;
+ else if (type == GetArrayString() ) type_ |= 1 << kArraySchemaType;
+ else if (type == GetStringString() ) type_ |= 1 << kStringSchemaType;
+ else if (type == GetIntegerString()) type_ |= 1 << kIntegerSchemaType;
+ else if (type == GetNumberString() ) type_ |= (1 << kNumberSchemaType) | (1 << kIntegerSchemaType);
+ }
+
+ bool CreateParallelValidator(Context& context) const {
+ if (enum_ || context.arrayUniqueness)
+ context.hasher = context.factory.CreateHasher();
+
+ if (validatorCount_) {
+ RAPIDJSON_ASSERT(context.validators == 0);
+ context.validators = static_cast<ISchemaValidator**>(context.factory.MallocState(sizeof(ISchemaValidator*) * validatorCount_));
+ context.validatorCount = validatorCount_;
+
+ if (allOf_.schemas)
+ CreateSchemaValidators(context, allOf_);
+
+ if (anyOf_.schemas)
+ CreateSchemaValidators(context, anyOf_);
+
+ if (oneOf_.schemas)
+ CreateSchemaValidators(context, oneOf_);
+
+ if (not_)
+ context.validators[notValidatorIndex_] = context.factory.CreateSchemaValidator(*not_);
+
+ if (hasSchemaDependencies_) {
+ for (SizeType i = 0; i < propertyCount_; i++)
+ if (properties_[i].dependenciesSchema)
+ context.validators[properties_[i].dependenciesValidatorIndex] = context.factory.CreateSchemaValidator(*properties_[i].dependenciesSchema);
+ }
+ }
+
+ return true;
+ }
+
+ void CreateSchemaValidators(Context& context, const SchemaArray& schemas) const {
+ for (SizeType i = 0; i < schemas.count; i++)
+ context.validators[schemas.begin + i] = context.factory.CreateSchemaValidator(*schemas.schemas[i]);
+ }
+
+ // O(n)
+ bool FindPropertyIndex(const ValueType& name, SizeType* outIndex) const {
+ SizeType len = name.GetStringLength();
+ const Ch* str = name.GetString();
+ for (SizeType index = 0; index < propertyCount_; index++)
+ if (properties_[index].name.GetStringLength() == len &&
+ (std::memcmp(properties_[index].name.GetString(), str, sizeof(Ch) * len) == 0))
+ {
+ *outIndex = index;
+ return true;
+ }
+ return false;
+ }
+
+ bool CheckInt(Context& context, int64_t i) const {
+ if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType))))
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
+
+ if (!minimum_.IsNull()) {
+ if (minimum_.IsInt64()) {
+ if (exclusiveMinimum_ ? i <= minimum_.GetInt64() : i < minimum_.GetInt64())
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString());
+ }
+ else if (!CheckDoubleMinimum(context, static_cast<double>(i)))
+ return false;
+ }
+
+ if (!maximum_.IsNull()) {
+ if (maximum_.IsInt64()) {
+ if (exclusiveMaximum_ ? i >= maximum_.GetInt64() : i > maximum_.GetInt64())
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString());
+ }
+ else if (!CheckDoubleMaximum(context, static_cast<double>(i)))
+ return false;
+ }
+
+ if (!multipleOf_.IsNull()) {
+ if (multipleOf_.IsUint64()) {
+ if (static_cast<uint64_t>(i >= 0 ? i : -i) % multipleOf_.GetUint64() != 0)
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString());
+ }
+ else if (!CheckDoubleMultipleOf(context, static_cast<double>(i)))
+ return false;
+ }
+
+ return true;
+ }
+
+ bool CheckUint(Context& context, uint64_t i) const {
+ if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType))))
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
+
+ if (!minimum_.IsNull()) {
+ if (minimum_.IsUint64()) {
+ if (exclusiveMinimum_ ? i <= minimum_.GetUint64() : i < minimum_.GetUint64())
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString());
+ }
+ else if (!CheckDoubleMinimum(context, static_cast<double>(i)))
+ return false;
+ }
+
+ if (!maximum_.IsNull()) {
+ if (maximum_.IsUint64()) {
+ if (exclusiveMaximum_ ? i >= maximum_.GetUint64() : i > maximum_.GetUint64())
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString());
+ }
+ else if (!CheckDoubleMaximum(context, static_cast<double>(i)))
+ return false;
+ }
+
+ if (!multipleOf_.IsNull()) {
+ if (multipleOf_.IsUint64()) {
+ if (i % multipleOf_.GetUint64() != 0)
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString());
+ }
+ else if (!CheckDoubleMultipleOf(context, static_cast<double>(i)))
+ return false;
+ }
+
+ return true;
+ }
+
+ bool CheckDoubleMinimum(Context& context, double d) const {
+ if (exclusiveMinimum_ ? d <= minimum_.GetDouble() : d < minimum_.GetDouble())
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString());
+ return true;
+ }
+
+ bool CheckDoubleMaximum(Context& context, double d) const {
+ if (exclusiveMaximum_ ? d >= maximum_.GetDouble() : d > maximum_.GetDouble())
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString());
+ return true;
+ }
+
+ bool CheckDoubleMultipleOf(Context& context, double d) const {
+ double a = std::abs(d), b = std::abs(multipleOf_.GetDouble());
+ double q = std::floor(a / b);
+ double r = a - q * b;
+ if (r > 0.0)
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString());
+ return true;
+ }
+
+ struct Property {
+ Property() : schema(), dependenciesSchema(), dependenciesValidatorIndex(), dependencies(), required(false) {}
+ ~Property() { AllocatorType::Free(dependencies); }
+ SValue name;
+ const SchemaType* schema;
+ const SchemaType* dependenciesSchema;
+ SizeType dependenciesValidatorIndex;
+ bool* dependencies;
+ bool required;
+ };
+
+ struct PatternProperty {
+ PatternProperty() : schema(), pattern() {}
+ ~PatternProperty() {
+ if (pattern) {
+ pattern->~RegexType();
+ AllocatorType::Free(pattern);
+ }
+ }
+ const SchemaType* schema;
+ RegexType* pattern;
+ };
+
+ AllocatorType* allocator_;
+ uint64_t* enum_;
+ SizeType enumCount_;
+ SchemaArray allOf_;
+ SchemaArray anyOf_;
+ SchemaArray oneOf_;
+ const SchemaType* not_;
+ unsigned type_; // bitmask of kSchemaType
+ SizeType validatorCount_;
+ SizeType notValidatorIndex_;
+
+ Property* properties_;
+ const SchemaType* additionalPropertiesSchema_;
+ PatternProperty* patternProperties_;
+ SizeType patternPropertyCount_;
+ SizeType propertyCount_;
+ SizeType requiredCount_;
+ SizeType minProperties_;
+ SizeType maxProperties_;
+ bool additionalProperties_;
+ bool hasDependencies_;
+ bool hasSchemaDependencies_;
+
+ const SchemaType* additionalItemsSchema_;
+ const SchemaType* itemsList_;
+ const SchemaType** itemsTuple_;
+ SizeType itemsTupleCount_;
+ SizeType minItems_;
+ SizeType maxItems_;
+ bool additionalItems_;
+ bool uniqueItems_;
+
+ RegexType* pattern_;
+ SizeType minLength_;
+ SizeType maxLength_;
+
+ SValue minimum_;
+ SValue maximum_;
+ SValue multipleOf_;
+ bool exclusiveMinimum_;
+ bool exclusiveMaximum_;
+};
+
+template<typename Stack, typename Ch>
+struct TokenHelper {
+ RAPIDJSON_FORCEINLINE static void AppendIndexToken(Stack& documentStack, SizeType index) {
+ *documentStack.template Push<Ch>() = '/';
+ char buffer[21];
+ size_t length = static_cast<size_t>((sizeof(SizeType) == 4 ? u32toa(index, buffer) : u64toa(index, buffer)) - buffer);
+ for (size_t i = 0; i < length; i++)
+ *documentStack.template Push<Ch>() = buffer[i];
+ }
+};
+
+// Partial specialized version for char to prevent buffer copying.
+template <typename Stack>
+struct TokenHelper<Stack, char> {
+ RAPIDJSON_FORCEINLINE static void AppendIndexToken(Stack& documentStack, SizeType index) {
+ if (sizeof(SizeType) == 4) {
+ char *buffer = documentStack.template Push<char>(1 + 10); // '/' + uint
+ *buffer++ = '/';
+ const char* end = internal::u32toa(index, buffer);
+ documentStack.template Pop<char>(static_cast<size_t>(10 - (end - buffer)));
+ }
+ else {
+ char *buffer = documentStack.template Push<char>(1 + 20); // '/' + uint64
+ *buffer++ = '/';
+ const char* end = internal::u64toa(index, buffer);
+ documentStack.template Pop<char>(static_cast<size_t>(20 - (end - buffer)));
+ }
+ }
+};
+
+} // namespace internal
+
+///////////////////////////////////////////////////////////////////////////////
+// IGenericRemoteSchemaDocumentProvider
+
+template <typename SchemaDocumentType>
+class IGenericRemoteSchemaDocumentProvider {
+public:
+ typedef typename SchemaDocumentType::Ch Ch;
+
+ virtual ~IGenericRemoteSchemaDocumentProvider() {}
+ virtual const SchemaDocumentType* GetRemoteDocument(const Ch* uri, SizeType length) = 0;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// GenericSchemaDocument
+
+//! JSON schema document.
+/*!
+ A JSON schema document is a compiled version of a JSON schema.
+ It is basically a tree of internal::Schema.
+
+ \note This is an immutable class (i.e. its instance cannot be modified after construction).
+ \tparam ValueT Type of JSON value (e.g. \c Value ), which also determine the encoding.
+ \tparam Allocator Allocator type for allocating memory of this document.
+*/
+template <typename ValueT, typename Allocator = CrtAllocator>
+class GenericSchemaDocument {
+public:
+ typedef ValueT ValueType;
+ typedef IGenericRemoteSchemaDocumentProvider<GenericSchemaDocument> IRemoteSchemaDocumentProviderType;
+ typedef Allocator AllocatorType;
+ typedef typename ValueType::EncodingType EncodingType;
+ typedef typename EncodingType::Ch Ch;
+ typedef internal::Schema<GenericSchemaDocument> SchemaType;
+ typedef GenericPointer<ValueType, Allocator> PointerType;
+ friend class internal::Schema<GenericSchemaDocument>;
+ template <typename, typename, typename>
+ friend class GenericSchemaValidator;
+
+ //! Constructor.
+ /*!
+ Compile a JSON document into schema document.
+
+ \param document A JSON document as source.
+ \param remoteProvider An optional remote schema document provider for resolving remote reference. Can be null.
+ \param allocator An optional allocator instance for allocating memory. Can be null.
+ */
+ GenericSchemaDocument(const ValueType& document, IRemoteSchemaDocumentProviderType* remoteProvider = 0, Allocator* allocator = 0) RAPIDJSON_NOEXCEPT :
+ remoteProvider_(remoteProvider),
+ allocator_(allocator),
+ ownAllocator_(),
+ root_(),
+ schemaMap_(allocator, kInitialSchemaMapSize),
+ schemaRef_(allocator, kInitialSchemaRefSize)
+ {
+ if (!allocator_)
+ ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator());
+
+ // Generate root schema, it will call CreateSchema() to create sub-schemas,
+ // And call AddRefSchema() if there are $ref.
+ CreateSchemaRecursive(&root_, PointerType(), document, document);
+
+ // Resolve $ref
+ while (!schemaRef_.Empty()) {
+ SchemaRefEntry* refEntry = schemaRef_.template Pop<SchemaRefEntry>(1);
+ if (const SchemaType* s = GetSchema(refEntry->target)) {
+ if (refEntry->schema)
+ *refEntry->schema = s;
+
+ // Create entry in map if not exist
+ if (!GetSchema(refEntry->source)) {
+ new (schemaMap_.template Push<SchemaEntry>()) SchemaEntry(refEntry->source, const_cast<SchemaType*>(s), false, allocator_);
+ }
+ }
+ refEntry->~SchemaRefEntry();
+ }
+
+ RAPIDJSON_ASSERT(root_ != 0);
+
+ schemaRef_.ShrinkToFit(); // Deallocate all memory for ref
+ }
+
+#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
+ //! Move constructor in C++11
+ GenericSchemaDocument(GenericSchemaDocument&& rhs) RAPIDJSON_NOEXCEPT :
+ remoteProvider_(rhs.remoteProvider_),
+ allocator_(rhs.allocator_),
+ ownAllocator_(rhs.ownAllocator_),
+ root_(rhs.root_),
+ schemaMap_(std::move(rhs.schemaMap_)),
+ schemaRef_(std::move(rhs.schemaRef_))
+ {
+ rhs.remoteProvider_ = 0;
+ rhs.allocator_ = 0;
+ rhs.ownAllocator_ = 0;
+ }
+#endif
+
+ //! Destructor
+ ~GenericSchemaDocument() {
+ while (!schemaMap_.Empty())
+ schemaMap_.template Pop<SchemaEntry>(1)->~SchemaEntry();
+
+ RAPIDJSON_DELETE(ownAllocator_);
+ }
+
+ //! Get the root schema.
+ const SchemaType& GetRoot() const { return *root_; }
+
+private:
+ //! Prohibit copying
+ GenericSchemaDocument(const GenericSchemaDocument&);
+ //! Prohibit assignment
+ GenericSchemaDocument& operator=(const GenericSchemaDocument&);
+
+ struct SchemaRefEntry {
+ SchemaRefEntry(const PointerType& s, const PointerType& t, const SchemaType** outSchema, Allocator *allocator) : source(s, allocator), target(t, allocator), schema(outSchema) {}
+ PointerType source;
+ PointerType target;
+ const SchemaType** schema;
+ };
+
+ struct SchemaEntry {
+ SchemaEntry(const PointerType& p, SchemaType* s, bool o, Allocator* allocator) : pointer(p, allocator), schema(s), owned(o) {}
+ ~SchemaEntry() {
+ if (owned) {
+ schema->~SchemaType();
+ Allocator::Free(schema);
+ }
+ }
+ PointerType pointer;
+ SchemaType* schema;
+ bool owned;
+ };
+
+ void CreateSchemaRecursive(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document) {
+ if (schema)
+ *schema = SchemaType::GetTypeless();
+
+ if (v.GetType() == kObjectType) {
+ const SchemaType* s = GetSchema(pointer);
+ if (!s)
+ CreateSchema(schema, pointer, v, document);
+ else if (schema)
+ *schema = s;
+
+ for (typename ValueType::ConstMemberIterator itr = v.MemberBegin(); itr != v.MemberEnd(); ++itr)
+ CreateSchemaRecursive(0, pointer.Append(itr->name, allocator_), itr->value, document);
+ }
+ else if (v.GetType() == kArrayType)
+ for (SizeType i = 0; i < v.Size(); i++)
+ CreateSchemaRecursive(0, pointer.Append(i, allocator_), v[i], document);
+ }
+
+ void CreateSchema(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document) {
+ RAPIDJSON_ASSERT(pointer.IsValid());
+ if (v.IsObject()) {
+ if (!HandleRefSchema(pointer, schema, v, document)) {
+ SchemaType* s = new (allocator_->Malloc(sizeof(SchemaType))) SchemaType(this, pointer, v, document, allocator_);
+ new (schemaMap_.template Push<SchemaEntry>()) SchemaEntry(pointer, s, true, allocator_);
+ if (schema)
+ *schema = s;
+ }
+ }
+ }
+
+ bool HandleRefSchema(const PointerType& source, const SchemaType** schema, const ValueType& v, const ValueType& document) {
+ static const Ch kRefString[] = { '$', 'r', 'e', 'f', '\0' };
+ static const ValueType kRefValue(kRefString, 4);
+
+ typename ValueType::ConstMemberIterator itr = v.FindMember(kRefValue);
+ if (itr == v.MemberEnd())
+ return false;
+
+ if (itr->value.IsString()) {
+ SizeType len = itr->value.GetStringLength();
+ if (len > 0) {
+ const Ch* s = itr->value.GetString();
+ SizeType i = 0;
+ while (i < len && s[i] != '#') // Find the first #
+ i++;
+
+ if (i > 0) { // Remote reference, resolve immediately
+ if (remoteProvider_) {
+ if (const GenericSchemaDocument* remoteDocument = remoteProvider_->GetRemoteDocument(s, i - 1)) {
+ PointerType pointer(&s[i], len - i, allocator_);
+ if (pointer.IsValid()) {
+ if (const SchemaType* sc = remoteDocument->GetSchema(pointer)) {
+ if (schema)
+ *schema = sc;
+ return true;
+ }
+ }
+ }
+ }
+ }
+ else if (s[i] == '#') { // Local reference, defer resolution
+ PointerType pointer(&s[i], len - i, allocator_);
+ if (pointer.IsValid()) {
+ if (const ValueType* nv = pointer.Get(document))
+ if (HandleRefSchema(source, schema, *nv, document))
+ return true;
+
+ new (schemaRef_.template Push<SchemaRefEntry>()) SchemaRefEntry(source, pointer, schema, allocator_);
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ const SchemaType* GetSchema(const PointerType& pointer) const {
+ for (const SchemaEntry* target = schemaMap_.template Bottom<SchemaEntry>(); target != schemaMap_.template End<SchemaEntry>(); ++target)
+ if (pointer == target->pointer)
+ return target->schema;
+ return 0;
+ }
+
+ PointerType GetPointer(const SchemaType* schema) const {
+ for (const SchemaEntry* target = schemaMap_.template Bottom<SchemaEntry>(); target != schemaMap_.template End<SchemaEntry>(); ++target)
+ if (schema == target->schema)
+ return target->pointer;
+ return PointerType();
+ }
+
+ static const size_t kInitialSchemaMapSize = 64;
+ static const size_t kInitialSchemaRefSize = 64;
+
+ IRemoteSchemaDocumentProviderType* remoteProvider_;
+ Allocator *allocator_;
+ Allocator *ownAllocator_;
+ const SchemaType* root_; //!< Root schema.
+ internal::Stack<Allocator> schemaMap_; // Stores created Pointer -> Schemas
+ internal::Stack<Allocator> schemaRef_; // Stores Pointer from $ref and schema which holds the $ref
+};
+
+//! GenericSchemaDocument using Value type.
+typedef GenericSchemaDocument<Value> SchemaDocument;
+//! IGenericRemoteSchemaDocumentProvider using SchemaDocument.
+typedef IGenericRemoteSchemaDocumentProvider<SchemaDocument> IRemoteSchemaDocumentProvider;
+
+///////////////////////////////////////////////////////////////////////////////
+// GenericSchemaValidator
+
+//! JSON Schema Validator.
+/*!
+ A SAX style JSON schema validator.
+ It uses a \c GenericSchemaDocument to validate SAX events.
+ It delegates the incoming SAX events to an output handler.
+ The default output handler does nothing.
+ It can be reused multiple times by calling \c Reset().
+
+ \tparam SchemaDocumentType Type of schema document.
+ \tparam OutputHandler Type of output handler. Default handler does nothing.
+ \tparam StateAllocator Allocator for storing the internal validation states.
+*/
+template <
+ typename SchemaDocumentType,
+ typename OutputHandler = BaseReaderHandler<typename SchemaDocumentType::SchemaType::EncodingType>,
+ typename StateAllocator = CrtAllocator>
+class GenericSchemaValidator :
+ public internal::ISchemaStateFactory<typename SchemaDocumentType::SchemaType>,
+ public internal::ISchemaValidator
+{
+public:
+ typedef typename SchemaDocumentType::SchemaType SchemaType;
+ typedef typename SchemaDocumentType::PointerType PointerType;
+ typedef typename SchemaType::EncodingType EncodingType;
+ typedef typename EncodingType::Ch Ch;
+
+ //! Constructor without output handler.
+ /*!
+ \param schemaDocument The schema document to conform to.
+ \param allocator Optional allocator for storing internal validation states.
+ \param schemaStackCapacity Optional initial capacity of schema path stack.
+ \param documentStackCapacity Optional initial capacity of document path stack.
+ */
+ GenericSchemaValidator(
+ const SchemaDocumentType& schemaDocument,
+ StateAllocator* allocator = 0,
+ size_t schemaStackCapacity = kDefaultSchemaStackCapacity,
+ size_t documentStackCapacity = kDefaultDocumentStackCapacity)
+ :
+ schemaDocument_(&schemaDocument),
+ root_(schemaDocument.GetRoot()),
+ outputHandler_(GetNullHandler()),
+ stateAllocator_(allocator),
+ ownStateAllocator_(0),
+ schemaStack_(allocator, schemaStackCapacity),
+ documentStack_(allocator, documentStackCapacity),
+ valid_(true)
+#if RAPIDJSON_SCHEMA_VERBOSE
+ , depth_(0)
+#endif
+ {
+ }
+
+ //! Constructor with output handler.
+ /*!
+ \param schemaDocument The schema document to conform to.
+ \param allocator Optional allocator for storing internal validation states.
+ \param schemaStackCapacity Optional initial capacity of schema path stack.
+ \param documentStackCapacity Optional initial capacity of document path stack.
+ */
+ GenericSchemaValidator(
+ const SchemaDocumentType& schemaDocument,
+ OutputHandler& outputHandler,
+ StateAllocator* allocator = 0,
+ size_t schemaStackCapacity = kDefaultSchemaStackCapacity,
+ size_t documentStackCapacity = kDefaultDocumentStackCapacity)
+ :
+ schemaDocument_(&schemaDocument),
+ root_(schemaDocument.GetRoot()),
+ outputHandler_(outputHandler),
+ stateAllocator_(allocator),
+ ownStateAllocator_(0),
+ schemaStack_(allocator, schemaStackCapacity),
+ documentStack_(allocator, documentStackCapacity),
+ valid_(true)
+#if RAPIDJSON_SCHEMA_VERBOSE
+ , depth_(0)
+#endif
+ {
+ }
+
+ //! Destructor.
+ ~GenericSchemaValidator() {
+ Reset();
+ RAPIDJSON_DELETE(ownStateAllocator_);
+ }
+
+ //! Reset the internal states.
+ void Reset() {
+ while (!schemaStack_.Empty())
+ PopSchema();
+ documentStack_.Clear();
+ valid_ = true;
+ }
+
+ //! Checks whether the current state is valid.
+ // Implementation of ISchemaValidator
+ virtual bool IsValid() const { return valid_; }
+
+ //! Gets the JSON pointer pointed to the invalid schema.
+ PointerType GetInvalidSchemaPointer() const {
+ return schemaStack_.Empty() ? PointerType() : schemaDocument_->GetPointer(&CurrentSchema());
+ }
+
+ //! Gets the keyword of invalid schema.
+ const Ch* GetInvalidSchemaKeyword() const {
+ return schemaStack_.Empty() ? 0 : CurrentContext().invalidKeyword;
+ }
+
+ //! Gets the JSON pointer pointed to the invalid value.
+ PointerType GetInvalidDocumentPointer() const {
+ return documentStack_.Empty() ? PointerType() : PointerType(documentStack_.template Bottom<Ch>(), documentStack_.GetSize() / sizeof(Ch));
+ }
+
+#if RAPIDJSON_SCHEMA_VERBOSE
+#define RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_() \
+RAPIDJSON_MULTILINEMACRO_BEGIN\
+ *documentStack_.template Push<Ch>() = '\0';\
+ documentStack_.template Pop<Ch>(1);\
+ internal::PrintInvalidDocument(documentStack_.template Bottom<Ch>());\
+RAPIDJSON_MULTILINEMACRO_END
+#else
+#define RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_()
+#endif
+
+#define RAPIDJSON_SCHEMA_HANDLE_BEGIN_(method, arg1)\
+ if (!valid_) return false; \
+ if (!BeginValue() || !CurrentSchema().method arg1) {\
+ RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_();\
+ return valid_ = false;\
+ }
+
+#define RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(method, arg2)\
+ for (Context* context = schemaStack_.template Bottom<Context>(); context != schemaStack_.template End<Context>(); context++) {\
+ if (context->hasher)\
+ static_cast<HasherType*>(context->hasher)->method arg2;\
+ if (context->validators)\
+ for (SizeType i_ = 0; i_ < context->validatorCount; i_++)\
+ static_cast<GenericSchemaValidator*>(context->validators[i_])->method arg2;\
+ if (context->patternPropertiesValidators)\
+ for (SizeType i_ = 0; i_ < context->patternPropertiesValidatorCount; i_++)\
+ static_cast<GenericSchemaValidator*>(context->patternPropertiesValidators[i_])->method arg2;\
+ }
+
+#define RAPIDJSON_SCHEMA_HANDLE_END_(method, arg2)\
+ return valid_ = EndValue() && outputHandler_.method arg2
+
+#define RAPIDJSON_SCHEMA_HANDLE_VALUE_(method, arg1, arg2) \
+ RAPIDJSON_SCHEMA_HANDLE_BEGIN_ (method, arg1);\
+ RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(method, arg2);\
+ RAPIDJSON_SCHEMA_HANDLE_END_ (method, arg2)
+
+ bool Null() { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Null, (CurrentContext() ), ( )); }
+ bool Bool(bool b) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Bool, (CurrentContext(), b), (b)); }
+ bool Int(int i) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Int, (CurrentContext(), i), (i)); }
+ bool Uint(unsigned u) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Uint, (CurrentContext(), u), (u)); }
+ bool Int64(int64_t i) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Int64, (CurrentContext(), i), (i)); }
+ bool Uint64(uint64_t u) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Uint64, (CurrentContext(), u), (u)); }
+ bool Double(double d) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Double, (CurrentContext(), d), (d)); }
+ bool RawNumber(const Ch* str, SizeType length, bool copy)
+ { RAPIDJSON_SCHEMA_HANDLE_VALUE_(String, (CurrentContext(), str, length, copy), (str, length, copy)); }
+ bool String(const Ch* str, SizeType length, bool copy)
+ { RAPIDJSON_SCHEMA_HANDLE_VALUE_(String, (CurrentContext(), str, length, copy), (str, length, copy)); }
+
+ bool StartObject() {
+ RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartObject, (CurrentContext()));
+ RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartObject, ());
+ return valid_ = outputHandler_.StartObject();
+ }
+
+ bool Key(const Ch* str, SizeType len, bool copy) {
+ if (!valid_) return false;
+ AppendToken(str, len);
+ if (!CurrentSchema().Key(CurrentContext(), str, len, copy)) return valid_ = false;
+ RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(Key, (str, len, copy));
+ return valid_ = outputHandler_.Key(str, len, copy);
+ }
+
+ bool EndObject(SizeType memberCount) {
+ if (!valid_) return false;
+ RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndObject, (memberCount));
+ if (!CurrentSchema().EndObject(CurrentContext(), memberCount)) return valid_ = false;
+ RAPIDJSON_SCHEMA_HANDLE_END_(EndObject, (memberCount));
+ }
+
+ bool StartArray() {
+ RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartArray, (CurrentContext()));
+ RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartArray, ());
+ return valid_ = outputHandler_.StartArray();
+ }
+
+ bool EndArray(SizeType elementCount) {
+ if (!valid_) return false;
+ RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndArray, (elementCount));
+ if (!CurrentSchema().EndArray(CurrentContext(), elementCount)) return valid_ = false;
+ RAPIDJSON_SCHEMA_HANDLE_END_(EndArray, (elementCount));
+ }
+
+#undef RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_
+#undef RAPIDJSON_SCHEMA_HANDLE_BEGIN_
+#undef RAPIDJSON_SCHEMA_HANDLE_PARALLEL_
+#undef RAPIDJSON_SCHEMA_HANDLE_VALUE_
+
+ // Implementation of ISchemaStateFactory<SchemaType>
+ virtual ISchemaValidator* CreateSchemaValidator(const SchemaType& root) {
+ return new (GetStateAllocator().Malloc(sizeof(GenericSchemaValidator))) GenericSchemaValidator(*schemaDocument_, root,
+#if RAPIDJSON_SCHEMA_VERBOSE
+ depth_ + 1,
+#endif
+ &GetStateAllocator());
+ }
+
+ virtual void DestroySchemaValidator(ISchemaValidator* validator) {
+ GenericSchemaValidator* v = static_cast<GenericSchemaValidator*>(validator);
+ v->~GenericSchemaValidator();
+ StateAllocator::Free(v);
+ }
+
+ virtual void* CreateHasher() {
+ return new (GetStateAllocator().Malloc(sizeof(HasherType))) HasherType(&GetStateAllocator());
+ }
+
+ virtual uint64_t GetHashCode(void* hasher) {
+ return static_cast<HasherType*>(hasher)->GetHashCode();
+ }
+
+ virtual void DestroryHasher(void* hasher) {
+ HasherType* h = static_cast<HasherType*>(hasher);
+ h->~HasherType();
+ StateAllocator::Free(h);
+ }
+
+ virtual void* MallocState(size_t size) {
+ return GetStateAllocator().Malloc(size);
+ }
+
+ virtual void* ReallocState(void* originalPtr, size_t originalSize, size_t newSize) {
+ return GetStateAllocator().Realloc(originalPtr, originalSize, newSize);
+ }
+
+ virtual void FreeState(void* p) {
+ return StateAllocator::Free(p);
+ }
+
+private:
+ typedef typename SchemaType::Context Context;
+ typedef GenericValue<UTF8<>, StateAllocator> HashCodeArray;
+ typedef internal::Hasher<EncodingType, StateAllocator> HasherType;
+
+ GenericSchemaValidator(
+ const SchemaDocumentType& schemaDocument,
+ const SchemaType& root,
+#if RAPIDJSON_SCHEMA_VERBOSE
+ unsigned depth,
+#endif
+ StateAllocator* allocator = 0,
+ size_t schemaStackCapacity = kDefaultSchemaStackCapacity,
+ size_t documentStackCapacity = kDefaultDocumentStackCapacity)
+ :
+ schemaDocument_(&schemaDocument),
+ root_(root),
+ outputHandler_(GetNullHandler()),
+ stateAllocator_(allocator),
+ ownStateAllocator_(0),
+ schemaStack_(allocator, schemaStackCapacity),
+ documentStack_(allocator, documentStackCapacity),
+ valid_(true)
+#if RAPIDJSON_SCHEMA_VERBOSE
+ , depth_(depth)
+#endif
+ {
+ }
+
+ StateAllocator& GetStateAllocator() {
+ if (!stateAllocator_)
+ stateAllocator_ = ownStateAllocator_ = RAPIDJSON_NEW(StateAllocator());
+ return *stateAllocator_;
+ }
+
+ bool BeginValue() {
+ if (schemaStack_.Empty())
+ PushSchema(root_);
+ else {
+ if (CurrentContext().inArray)
+ internal::TokenHelper<internal::Stack<StateAllocator>, Ch>::AppendIndexToken(documentStack_, CurrentContext().arrayElementIndex);
+
+ if (!CurrentSchema().BeginValue(CurrentContext()))
+ return false;
+
+ SizeType count = CurrentContext().patternPropertiesSchemaCount;
+ const SchemaType** sa = CurrentContext().patternPropertiesSchemas;
+ typename Context::PatternValidatorType patternValidatorType = CurrentContext().valuePatternValidatorType;
+ bool valueUniqueness = CurrentContext().valueUniqueness;
+ if (CurrentContext().valueSchema)
+ PushSchema(*CurrentContext().valueSchema);
+
+ if (count > 0) {
+ CurrentContext().objectPatternValidatorType = patternValidatorType;
+ ISchemaValidator**& va = CurrentContext().patternPropertiesValidators;
+ SizeType& validatorCount = CurrentContext().patternPropertiesValidatorCount;
+ va = static_cast<ISchemaValidator**>(MallocState(sizeof(ISchemaValidator*) * count));
+ for (SizeType i = 0; i < count; i++)
+ va[validatorCount++] = CreateSchemaValidator(*sa[i]);
+ }
+
+ CurrentContext().arrayUniqueness = valueUniqueness;
+ }
+ return true;
+ }
+
+ bool EndValue() {
+ if (!CurrentSchema().EndValue(CurrentContext()))
+ return false;
+
+#if RAPIDJSON_SCHEMA_VERBOSE
+ GenericStringBuffer<EncodingType> sb;
+ schemaDocument_->GetPointer(&CurrentSchema()).Stringify(sb);
+
+ *documentStack_.template Push<Ch>() = '\0';
+ documentStack_.template Pop<Ch>(1);
+ internal::PrintValidatorPointers(depth_, sb.GetString(), documentStack_.template Bottom<Ch>());
+#endif
+
+ uint64_t h = CurrentContext().arrayUniqueness ? static_cast<HasherType*>(CurrentContext().hasher)->GetHashCode() : 0;
+
+ PopSchema();
+
+ if (!schemaStack_.Empty()) {
+ Context& context = CurrentContext();
+ if (context.valueUniqueness) {
+ HashCodeArray* a = static_cast<HashCodeArray*>(context.arrayElementHashCodes);
+ if (!a)
+ CurrentContext().arrayElementHashCodes = a = new (GetStateAllocator().Malloc(sizeof(HashCodeArray))) HashCodeArray(kArrayType);
+ for (typename HashCodeArray::ConstValueIterator itr = a->Begin(); itr != a->End(); ++itr)
+ if (itr->GetUint64() == h)
+ RAPIDJSON_INVALID_KEYWORD_RETURN(SchemaType::GetUniqueItemsString());
+ a->PushBack(h, GetStateAllocator());
+ }
+ }
+
+ // Remove the last token of document pointer
+ while (!documentStack_.Empty() && *documentStack_.template Pop<Ch>(1) != '/')
+ ;
+
+ return true;
+ }
+
+ void AppendToken(const Ch* str, SizeType len) {
+ documentStack_.template Reserve<Ch>(1 + len * 2); // worst case all characters are escaped as two characters
+ *documentStack_.template PushUnsafe<Ch>() = '/';
+ for (SizeType i = 0; i < len; i++) {
+ if (str[i] == '~') {
+ *documentStack_.template PushUnsafe<Ch>() = '~';
+ *documentStack_.template PushUnsafe<Ch>() = '0';
+ }
+ else if (str[i] == '/') {
+ *documentStack_.template PushUnsafe<Ch>() = '~';
+ *documentStack_.template PushUnsafe<Ch>() = '1';
+ }
+ else
+ *documentStack_.template PushUnsafe<Ch>() = str[i];
+ }
+ }
+
+ RAPIDJSON_FORCEINLINE void PushSchema(const SchemaType& schema) { new (schemaStack_.template Push<Context>()) Context(*this, &schema); }
+
+ RAPIDJSON_FORCEINLINE void PopSchema() {
+ Context* c = schemaStack_.template Pop<Context>(1);
+ if (HashCodeArray* a = static_cast<HashCodeArray*>(c->arrayElementHashCodes)) {
+ a->~HashCodeArray();
+ StateAllocator::Free(a);
+ }
+ c->~Context();
+ }
+
+ const SchemaType& CurrentSchema() const { return *schemaStack_.template Top<Context>()->schema; }
+ Context& CurrentContext() { return *schemaStack_.template Top<Context>(); }
+ const Context& CurrentContext() const { return *schemaStack_.template Top<Context>(); }
+
+ static OutputHandler& GetNullHandler() {
+ static OutputHandler nullHandler;
+ return nullHandler;
+ }
+
+ static const size_t kDefaultSchemaStackCapacity = 1024;
+ static const size_t kDefaultDocumentStackCapacity = 256;
+ const SchemaDocumentType* schemaDocument_;
+ const SchemaType& root_;
+ OutputHandler& outputHandler_;
+ StateAllocator* stateAllocator_;
+ StateAllocator* ownStateAllocator_;
+ internal::Stack<StateAllocator> schemaStack_; //!< stack to store the current path of schema (BaseSchemaType *)
+ internal::Stack<StateAllocator> documentStack_; //!< stack to store the current path of validating document (Ch)
+ bool valid_;
+#if RAPIDJSON_SCHEMA_VERBOSE
+ unsigned depth_;
+#endif
+};
+
+typedef GenericSchemaValidator<SchemaDocument> SchemaValidator;
+
+///////////////////////////////////////////////////////////////////////////////
+// SchemaValidatingReader
+
+//! A helper class for parsing with validation.
+/*!
+ This helper class is a functor, designed as a parameter of \ref GenericDocument::Populate().
+
+ \tparam parseFlags Combination of \ref ParseFlag.
+ \tparam InputStream Type of input stream, implementing Stream concept.
+ \tparam SourceEncoding Encoding of the input stream.
+ \tparam SchemaDocumentType Type of schema document.
+ \tparam StackAllocator Allocator type for stack.
+*/
+template <
+ unsigned parseFlags,
+ typename InputStream,
+ typename SourceEncoding,
+ typename SchemaDocumentType = SchemaDocument,
+ typename StackAllocator = CrtAllocator>
+class SchemaValidatingReader {
+public:
+ typedef typename SchemaDocumentType::PointerType PointerType;
+ typedef typename InputStream::Ch Ch;
+
+ //! Constructor
+ /*!
+ \param is Input stream.
+ \param sd Schema document.
+ */
+ SchemaValidatingReader(InputStream& is, const SchemaDocumentType& sd) : is_(is), sd_(sd), invalidSchemaKeyword_(), isValid_(true) {}
+
+ template <typename Handler>
+ bool operator()(Handler& handler) {
+ GenericReader<SourceEncoding, typename SchemaDocumentType::EncodingType, StackAllocator> reader;
+ GenericSchemaValidator<SchemaDocumentType, Handler> validator(sd_, handler);
+ parseResult_ = reader.template Parse<parseFlags>(is_, validator);
+
+ isValid_ = validator.IsValid();
+ if (isValid_) {
+ invalidSchemaPointer_ = PointerType();
+ invalidSchemaKeyword_ = 0;
+ invalidDocumentPointer_ = PointerType();
+ }
+ else {
+ invalidSchemaPointer_ = validator.GetInvalidSchemaPointer();
+ invalidSchemaKeyword_ = validator.GetInvalidSchemaKeyword();
+ invalidDocumentPointer_ = validator.GetInvalidDocumentPointer();
+ }
+
+ return parseResult_;
+ }
+
+ const ParseResult& GetParseResult() const { return parseResult_; }
+ bool IsValid() const { return isValid_; }
+ const PointerType& GetInvalidSchemaPointer() const { return invalidSchemaPointer_; }
+ const Ch* GetInvalidSchemaKeyword() const { return invalidSchemaKeyword_; }
+ const PointerType& GetInvalidDocumentPointer() const { return invalidDocumentPointer_; }
+
+private:
+ InputStream& is_;
+ const SchemaDocumentType& sd_;
+
+ ParseResult parseResult_;
+ PointerType invalidSchemaPointer_;
+ const Ch* invalidSchemaKeyword_;
+ PointerType invalidDocumentPointer_;
+ bool isValid_;
+};
+
+RAPIDJSON_NAMESPACE_END
+
+#if defined(__GNUC__)
+RAPIDJSON_DIAG_POP
+#endif
+
+#ifdef __clang__
+RAPIDJSON_DIAG_POP
+#endif
+
+#ifdef _MSC_VER
+RAPIDJSON_DIAG_POP
+#endif
+
+#endif // RAPIDJSON_SCHEMA_H_
=== added file 'shorts/xml2json/rapidjson/stream.h'
--- shorts/xml2json/rapidjson/stream.h 1970-01-01 00:00:00 +0000
+++ shorts/xml2json/rapidjson/stream.h 2016-06-10 15:12:59 +0000
@@ -0,0 +1,179 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+#include "rapidjson.h"
+
+#ifndef RAPIDJSON_STREAM_H_
+#define RAPIDJSON_STREAM_H_
+
+#include "encodings.h"
+
+RAPIDJSON_NAMESPACE_BEGIN
+
+///////////////////////////////////////////////////////////////////////////////
+// Stream
+
+/*! \class rapidjson::Stream
+ \brief Concept for reading and writing characters.
+
+ For read-only stream, no need to implement PutBegin(), Put(), Flush() and PutEnd().
+
+ For write-only stream, only need to implement Put() and Flush().
+
+\code
+concept Stream {
+ typename Ch; //!< Character type of the stream.
+
+ //! Read the current character from stream without moving the read cursor.
+ Ch Peek() const;
+
+ //! Read the current character from stream and moving the read cursor to next character.
+ Ch Take();
+
+ //! Get the current read cursor.
+ //! \return Number of characters read from start.
+ size_t Tell();
+
+ //! Begin writing operation at the current read pointer.
+ //! \return The begin writer pointer.
+ Ch* PutBegin();
+
+ //! Write a character.
+ void Put(Ch c);
+
+ //! Flush the buffer.
+ void Flush();
+
+ //! End the writing operation.
+ //! \param begin The begin write pointer returned by PutBegin().
+ //! \return Number of characters written.
+ size_t PutEnd(Ch* begin);
+}
+\endcode
+*/
+
+//! Provides additional information for stream.
+/*!
+ By using traits pattern, this type provides a default configuration for stream.
+ For custom stream, this type can be specialized for other configuration.
+ See TEST(Reader, CustomStringStream) in readertest.cpp for example.
+*/
+template<typename Stream>
+struct StreamTraits {
+ //! Whether to make local copy of stream for optimization during parsing.
+ /*!
+ By default, for safety, streams do not use local copy optimization.
+ Stream that can be copied fast should specialize this, like StreamTraits<StringStream>.
+ */
+ enum { copyOptimization = 0 };
+};
+
+//! Reserve n characters for writing to a stream.
+template<typename Stream>
+inline void PutReserve(Stream& stream, size_t count) {
+ (void)stream;
+ (void)count;
+}
+
+//! Write character to a stream, presuming buffer is reserved.
+template<typename Stream>
+inline void PutUnsafe(Stream& stream, typename Stream::Ch c) {
+ stream.Put(c);
+}
+
+//! Put N copies of a character to a stream.
+template<typename Stream, typename Ch>
+inline void PutN(Stream& stream, Ch c, size_t n) {
+ PutReserve<Stream>(stream, n);
+ for (size_t i = 0; i < n; i++)
+ PutUnsafe(stream, c);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// StringStream
+
+//! Read-only string stream.
+/*! \note implements Stream concept
+*/
+template <typename Encoding>
+struct GenericStringStream {
+ typedef typename Encoding::Ch Ch;
+
+ GenericStringStream(const Ch *src) : src_(src), head_(src) {}
+
+ Ch Peek() const { return *src_; }
+ Ch Take() { return *src_++; }
+ size_t Tell() const { return static_cast<size_t>(src_ - head_); }
+
+ Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
+ void Put(Ch) { RAPIDJSON_ASSERT(false); }
+ void Flush() { RAPIDJSON_ASSERT(false); }
+ size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
+
+ const Ch* src_; //!< Current read position.
+ const Ch* head_; //!< Original head of the string.
+};
+
+template <typename Encoding>
+struct StreamTraits<GenericStringStream<Encoding> > {
+ enum { copyOptimization = 1 };
+};
+
+//! String stream with UTF8 encoding.
+typedef GenericStringStream<UTF8<> > StringStream;
+
+///////////////////////////////////////////////////////////////////////////////
+// InsituStringStream
+
+//! A read-write string stream.
+/*! This string stream is particularly designed for in-situ parsing.
+ \note implements Stream concept
+*/
+template <typename Encoding>
+struct GenericInsituStringStream {
+ typedef typename Encoding::Ch Ch;
+
+ GenericInsituStringStream(Ch *src) : src_(src), dst_(0), head_(src) {}
+
+ // Read
+ Ch Peek() { return *src_; }
+ Ch Take() { return *src_++; }
+ size_t Tell() { return static_cast<size_t>(src_ - head_); }
+
+ // Write
+ void Put(Ch c) { RAPIDJSON_ASSERT(dst_ != 0); *dst_++ = c; }
+
+ Ch* PutBegin() { return dst_ = src_; }
+ size_t PutEnd(Ch* begin) { return static_cast<size_t>(dst_ - begin); }
+ void Flush() {}
+
+ Ch* Push(size_t count) { Ch* begin = dst_; dst_ += count; return begin; }
+ void Pop(size_t count) { dst_ -= count; }
+
+ Ch* src_;
+ Ch* dst_;
+ Ch* head_;
+};
+
+template <typename Encoding>
+struct StreamTraits<GenericInsituStringStream<Encoding> > {
+ enum { copyOptimization = 1 };
+};
+
+//! Insitu string stream with UTF8 encoding.
+typedef GenericInsituStringStream<UTF8<> > InsituStringStream;
+
+RAPIDJSON_NAMESPACE_END
+
+#endif // RAPIDJSON_STREAM_H_
=== modified file 'shorts/xml2json/rapidjson/stringbuffer.h'
--- shorts/xml2json/rapidjson/stringbuffer.h 2016-03-04 13:27:57 +0000
+++ shorts/xml2json/rapidjson/stringbuffer.h 2016-06-10 15:12:59 +0000
@@ -15,7 +15,8 @@
#ifndef RAPIDJSON_STRINGBUFFER_H_
#define RAPIDJSON_STRINGBUFFER_H_
-#include "rapidjson.h"
+#include "stream.h"
+#include "internal/stack.h"
#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
#include <utility> // std::move
@@ -23,6 +24,11 @@
#include "internal/stack.h"
+#if defined(__clang__)
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(c++98-compat)
+#endif
+
RAPIDJSON_NAMESPACE_BEGIN
//! Represents an in-memory output stream.
@@ -48,6 +54,7 @@
#endif
void Put(Ch c) { *stack_.template Push<Ch>() = c; }
+ void PutUnsafe(Ch c) { *stack_.template PushUnsafe<Ch>() = c; }
void Flush() {}
void Clear() { stack_.Clear(); }
@@ -57,7 +64,10 @@
stack_.ShrinkToFit();
stack_.template Pop<Ch>(1);
}
+
+ void Reserve(size_t count) { stack_.template Reserve<Ch>(count); }
Ch* Push(size_t count) { return stack_.template Push<Ch>(count); }
+ Ch* PushUnsafe(size_t count) { return stack_.template PushUnsafe<Ch>(count); }
void Pop(size_t count) { stack_.template Pop<Ch>(count); }
const Ch* GetString() const {
@@ -82,6 +92,16 @@
//! String buffer with UTF8 encoding
typedef GenericStringBuffer<UTF8<> > StringBuffer;
+template<typename Encoding, typename Allocator>
+inline void PutReserve(GenericStringBuffer<Encoding, Allocator>& stream, size_t count) {
+ stream.Reserve(count);
+}
+
+template<typename Encoding, typename Allocator>
+inline void PutUnsafe(GenericStringBuffer<Encoding, Allocator>& stream, typename Encoding::Ch c) {
+ stream.PutUnsafe(c);
+}
+
//! Implement specialized version of PutN() with memset() for better performance.
template<>
inline void PutN(GenericStringBuffer<UTF8<> >& stream, char c, size_t n) {
@@ -90,4 +110,8 @@
RAPIDJSON_NAMESPACE_END
+#if defined(__clang__)
+RAPIDJSON_DIAG_POP
+#endif
+
#endif // RAPIDJSON_STRINGBUFFER_H_
=== modified file 'shorts/xml2json/rapidjson/writer.h'
--- shorts/xml2json/rapidjson/writer.h 2016-03-04 13:27:57 +0000
+++ shorts/xml2json/rapidjson/writer.h 2016-06-10 15:12:59 +0000
@@ -15,7 +15,7 @@
#ifndef RAPIDJSON_WRITER_H_
#define RAPIDJSON_WRITER_H_
-#include "rapidjson.h"
+#include "stream.h"
#include "internal/stack.h"
#include "internal/strfunc.h"
#include "internal/dtoa.h"
@@ -23,6 +23,16 @@
#include "stringbuffer.h"
#include <new> // placement new
+#if defined(RAPIDJSON_SIMD) && defined(_MSC_VER)
+#include <intrin.h>
+#pragma intrinsic(_BitScanForward)
+#endif
+#ifdef RAPIDJSON_SSE42
+#include <nmmintrin.h>
+#elif defined(RAPIDJSON_SSE2)
+#include <emmintrin.h>
+#endif
+
#ifdef _MSC_VER
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant
@@ -35,6 +45,26 @@
RAPIDJSON_NAMESPACE_BEGIN
+///////////////////////////////////////////////////////////////////////////////
+// WriteFlag
+
+/*! \def RAPIDJSON_WRITE_DEFAULT_FLAGS
+ \ingroup RAPIDJSON_CONFIG
+ \brief User-defined kWriteDefaultFlags definition.
+
+ User can define this as any \c WriteFlag combinations.
+*/
+#ifndef RAPIDJSON_WRITE_DEFAULT_FLAGS
+#define RAPIDJSON_WRITE_DEFAULT_FLAGS kWriteNoFlags
+#endif
+
+//! Combination of writeFlags
+enum WriteFlag {
+ kWriteNoFlags = 0, //!< No flags are set.
+ kWriteValidateEncodingFlag = 1, //!< Validate encoding of JSON strings.
+ kWriteDefaultFlags = RAPIDJSON_WRITE_DEFAULT_FLAGS //!< Default write flags. Can be customized by defining RAPIDJSON_WRITE_DEFAULT_FLAGS
+};
+
//! JSON writer
/*! Writer implements the concept Handler.
It generates JSON text by events to an output os.
@@ -51,11 +81,13 @@
\tparam StackAllocator Type of allocator for allocating memory of stack.
\note implements Handler concept
*/
-template<typename OutputStream, typename SourceEncoding = UTF8<>, typename TargetEncoding = UTF8<>, typename StackAllocator = CrtAllocator>
+template<typename OutputStream, typename SourceEncoding = UTF8<>, typename TargetEncoding = UTF8<>, typename StackAllocator = CrtAllocator, unsigned writeFlags = kWriteDefaultFlags>
class Writer {
public:
typedef typename SourceEncoding::Ch Ch;
+ static const int kDefaultMaxDecimalPlaces = 324;
+
//! Constructor
/*! \param os Output stream.
\param stackAllocator User supplied allocator. If it is null, it will create a private one.
@@ -63,11 +95,11 @@
*/
explicit
Writer(OutputStream& os, StackAllocator* stackAllocator = 0, size_t levelDepth = kDefaultLevelDepth) :
- os_(&os), level_stack_(stackAllocator, levelDepth * sizeof(Level)), hasRoot_(false) {}
+ os_(&os), level_stack_(stackAllocator, levelDepth * sizeof(Level)), maxDecimalPlaces_(kDefaultMaxDecimalPlaces), hasRoot_(false) {}
explicit
Writer(StackAllocator* allocator = 0, size_t levelDepth = kDefaultLevelDepth) :
- os_(0), level_stack_(allocator, levelDepth * sizeof(Level)), hasRoot_(false) {}
+ os_(0), level_stack_(allocator, levelDepth * sizeof(Level)), maxDecimalPlaces_(kDefaultMaxDecimalPlaces), hasRoot_(false) {}
//! Reset the writer with a new stream.
/*!
@@ -101,6 +133,35 @@
return hasRoot_ && level_stack_.Empty();
}
+ int GetMaxDecimalPlaces() const {
+ return maxDecimalPlaces_;
+ }
+
+ //! Sets the maximum number of decimal places for double output.
+ /*!
+ This setting truncates the output with specified number of decimal places.
+
+ For example,
+
+ \code
+ writer.SetMaxDecimalPlaces(3);
+ writer.StartArray();
+ writer.Double(0.12345); // "0.123"
+ writer.Double(0.0001); // "0.0"
+ writer.Double(1.234567890123456e30); // "1.234567890123456e30" (do not truncate significand for positive exponent)
+ writer.Double(1.23e-4); // "0.0" (do truncate significand for negative exponent)
+ writer.EndArray();
+ \endcode
+
+ The default setting does not truncate any decimal places. You can restore to this setting by calling
+ \code
+ writer.SetMaxDecimalPlaces(Writer::kDefaultMaxDecimalPlaces);
+ \endcode
+ */
+ void SetMaxDecimalPlaces(int maxDecimalPlaces) {
+ maxDecimalPlaces_ = maxDecimalPlaces;
+ }
+
/*!@name Implementation of Handler
\see Handler
*/
@@ -120,6 +181,12 @@
*/
bool Double(double d) { Prefix(kNumberType); return WriteDouble(d); }
+ bool RawNumber(const Ch* str, SizeType length, bool copy = false) {
+ (void)copy;
+ Prefix(kNumberType);
+ return WriteString(str, length);
+ }
+
bool String(const Ch* str, SizeType length, bool copy = false) {
(void)copy;
Prefix(kStringType);
@@ -139,14 +206,14 @@
}
bool Key(const Ch* str, SizeType length, bool copy = false) { return String(str, length, copy); }
-
+
bool EndObject(SizeType memberCount = 0) {
(void)memberCount;
RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level));
RAPIDJSON_ASSERT(!level_stack_.template Top<Level>()->inArray);
level_stack_.template Pop<Level>(1);
bool ret = WriteEndObject();
- if (level_stack_.Empty()) // end of json text
+ if (RAPIDJSON_UNLIKELY(level_stack_.Empty())) // end of json text
os_->Flush();
return ret;
}
@@ -163,7 +230,7 @@
RAPIDJSON_ASSERT(level_stack_.template Top<Level>()->inArray);
level_stack_.template Pop<Level>(1);
bool ret = WriteEndArray();
- if (level_stack_.Empty()) // end of json text
+ if (RAPIDJSON_UNLIKELY(level_stack_.Empty())) // end of json text
os_->Flush();
return ret;
}
@@ -178,6 +245,16 @@
//@}
+ //! Write a raw JSON value.
+ /*!
+ For user to write a stringified JSON as a value.
+
+ \param json A well-formed JSON value. It should not contain null character within [0, length - 1] range.
+ \param length Length of the json.
+ \param type Type of the root of json.
+ */
+ bool RawValue(const Ch* json, size_t length, Type type) { Prefix(type); return WriteRawValue(json, length); }
+
protected:
//! Information for each nested level
struct Level {
@@ -189,15 +266,18 @@
static const size_t kDefaultLevelDepth = 32;
bool WriteNull() {
- os_->Put('n'); os_->Put('u'); os_->Put('l'); os_->Put('l'); return true;
+ PutReserve(*os_, 4);
+ PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'u'); PutUnsafe(*os_, 'l'); PutUnsafe(*os_, 'l'); return true;
}
bool WriteBool(bool b) {
if (b) {
- os_->Put('t'); os_->Put('r'); os_->Put('u'); os_->Put('e');
+ PutReserve(*os_, 4);
+ PutUnsafe(*os_, 't'); PutUnsafe(*os_, 'r'); PutUnsafe(*os_, 'u'); PutUnsafe(*os_, 'e');
}
else {
- os_->Put('f'); os_->Put('a'); os_->Put('l'); os_->Put('s'); os_->Put('e');
+ PutReserve(*os_, 5);
+ PutUnsafe(*os_, 'f'); PutUnsafe(*os_, 'a'); PutUnsafe(*os_, 'l'); PutUnsafe(*os_, 's'); PutUnsafe(*os_, 'e');
}
return true;
}
@@ -205,40 +285,48 @@
bool WriteInt(int i) {
char buffer[11];
const char* end = internal::i32toa(i, buffer);
+ PutReserve(*os_, static_cast<size_t>(end - buffer));
for (const char* p = buffer; p != end; ++p)
- os_->Put(static_cast<typename TargetEncoding::Ch>(*p));
+ PutUnsafe(*os_, static_cast<typename TargetEncoding::Ch>(*p));
return true;
}
bool WriteUint(unsigned u) {
char buffer[10];
const char* end = internal::u32toa(u, buffer);
+ PutReserve(*os_, static_cast<size_t>(end - buffer));
for (const char* p = buffer; p != end; ++p)
- os_->Put(static_cast<typename TargetEncoding::Ch>(*p));
+ PutUnsafe(*os_, static_cast<typename TargetEncoding::Ch>(*p));
return true;
}
bool WriteInt64(int64_t i64) {
char buffer[21];
const char* end = internal::i64toa(i64, buffer);
+ PutReserve(*os_, static_cast<size_t>(end - buffer));
for (const char* p = buffer; p != end; ++p)
- os_->Put(static_cast<typename TargetEncoding::Ch>(*p));
+ PutUnsafe(*os_, static_cast<typename TargetEncoding::Ch>(*p));
return true;
}
bool WriteUint64(uint64_t u64) {
char buffer[20];
char* end = internal::u64toa(u64, buffer);
+ PutReserve(*os_, static_cast<size_t>(end - buffer));
for (char* p = buffer; p != end; ++p)
- os_->Put(static_cast<typename TargetEncoding::Ch>(*p));
+ PutUnsafe(*os_, static_cast<typename TargetEncoding::Ch>(*p));
return true;
}
bool WriteDouble(double d) {
+ if (internal::Double(d).IsNanOrInf())
+ return false;
+
char buffer[25];
- char* end = internal::dtoa(d, buffer);
+ char* end = internal::dtoa(d, buffer, maxDecimalPlaces_);
+ PutReserve(*os_, static_cast<size_t>(end - buffer));
for (char* p = buffer; p != end; ++p)
- os_->Put(static_cast<typename TargetEncoding::Ch>(*p));
+ PutUnsafe(*os_, static_cast<typename TargetEncoding::Ch>(*p));
return true;
}
@@ -256,22 +344,27 @@
#undef Z16
};
- os_->Put('\"');
+ if (TargetEncoding::supportUnicode)
+ PutReserve(*os_, 2 + length * 6); // "\uxxxx..."
+ else
+ PutReserve(*os_, 2 + length * 12); // "\uxxxx\uyyyy..."
+
+ PutUnsafe(*os_, '\"');
GenericStringStream<SourceEncoding> is(str);
- while (is.Tell() < length) {
+ while (ScanWriteUnescapedString(is, length)) {
const Ch c = is.Peek();
if (!TargetEncoding::supportUnicode && static_cast<unsigned>(c) >= 0x80) {
// Unicode escaping
unsigned codepoint;
- if (!SourceEncoding::Decode(is, &codepoint))
+ if (RAPIDJSON_UNLIKELY(!SourceEncoding::Decode(is, &codepoint)))
return false;
- os_->Put('\\');
- os_->Put('u');
+ PutUnsafe(*os_, '\\');
+ PutUnsafe(*os_, 'u');
if (codepoint <= 0xD7FF || (codepoint >= 0xE000 && codepoint <= 0xFFFF)) {
- os_->Put(hexDigits[(codepoint >> 12) & 15]);
- os_->Put(hexDigits[(codepoint >> 8) & 15]);
- os_->Put(hexDigits[(codepoint >> 4) & 15]);
- os_->Put(hexDigits[(codepoint ) & 15]);
+ PutUnsafe(*os_, hexDigits[(codepoint >> 12) & 15]);
+ PutUnsafe(*os_, hexDigits[(codepoint >> 8) & 15]);
+ PutUnsafe(*os_, hexDigits[(codepoint >> 4) & 15]);
+ PutUnsafe(*os_, hexDigits[(codepoint ) & 15]);
}
else {
RAPIDJSON_ASSERT(codepoint >= 0x010000 && codepoint <= 0x10FFFF);
@@ -279,45 +372,59 @@
unsigned s = codepoint - 0x010000;
unsigned lead = (s >> 10) + 0xD800;
unsigned trail = (s & 0x3FF) + 0xDC00;
- os_->Put(hexDigits[(lead >> 12) & 15]);
- os_->Put(hexDigits[(lead >> 8) & 15]);
- os_->Put(hexDigits[(lead >> 4) & 15]);
- os_->Put(hexDigits[(lead ) & 15]);
- os_->Put('\\');
- os_->Put('u');
- os_->Put(hexDigits[(trail >> 12) & 15]);
- os_->Put(hexDigits[(trail >> 8) & 15]);
- os_->Put(hexDigits[(trail >> 4) & 15]);
- os_->Put(hexDigits[(trail ) & 15]);
+ PutUnsafe(*os_, hexDigits[(lead >> 12) & 15]);
+ PutUnsafe(*os_, hexDigits[(lead >> 8) & 15]);
+ PutUnsafe(*os_, hexDigits[(lead >> 4) & 15]);
+ PutUnsafe(*os_, hexDigits[(lead ) & 15]);
+ PutUnsafe(*os_, '\\');
+ PutUnsafe(*os_, 'u');
+ PutUnsafe(*os_, hexDigits[(trail >> 12) & 15]);
+ PutUnsafe(*os_, hexDigits[(trail >> 8) & 15]);
+ PutUnsafe(*os_, hexDigits[(trail >> 4) & 15]);
+ PutUnsafe(*os_, hexDigits[(trail ) & 15]);
}
}
- else if ((sizeof(Ch) == 1 || static_cast<unsigned>(c) < 256) && escape[static_cast<unsigned char>(c)]) {
+ else if ((sizeof(Ch) == 1 || static_cast<unsigned>(c) < 256) && RAPIDJSON_UNLIKELY(escape[static_cast<unsigned char>(c)])) {
is.Take();
- os_->Put('\\');
- os_->Put(static_cast<typename TargetEncoding::Ch>(escape[static_cast<unsigned char>(c)]));
+ PutUnsafe(*os_, '\\');
+ PutUnsafe(*os_, static_cast<typename TargetEncoding::Ch>(escape[static_cast<unsigned char>(c)]));
if (escape[static_cast<unsigned char>(c)] == 'u') {
- os_->Put('0');
- os_->Put('0');
- os_->Put(hexDigits[static_cast<unsigned char>(c) >> 4]);
- os_->Put(hexDigits[static_cast<unsigned char>(c) & 0xF]);
+ PutUnsafe(*os_, '0');
+ PutUnsafe(*os_, '0');
+ PutUnsafe(*os_, hexDigits[static_cast<unsigned char>(c) >> 4]);
+ PutUnsafe(*os_, hexDigits[static_cast<unsigned char>(c) & 0xF]);
}
}
- else
- if (!Transcoder<SourceEncoding, TargetEncoding>::Transcode(is, *os_))
- return false;
+ else if (RAPIDJSON_UNLIKELY(!(writeFlags & kWriteValidateEncodingFlag ?
+ Transcoder<SourceEncoding, TargetEncoding>::Validate(is, *os_) :
+ Transcoder<SourceEncoding, TargetEncoding>::TranscodeUnsafe(is, *os_))))
+ return false;
}
- os_->Put('\"');
+ PutUnsafe(*os_, '\"');
return true;
}
+ bool ScanWriteUnescapedString(GenericStringStream<SourceEncoding>& is, size_t length) {
+ return RAPIDJSON_LIKELY(is.Tell() < length);
+ }
+
bool WriteStartObject() { os_->Put('{'); return true; }
bool WriteEndObject() { os_->Put('}'); return true; }
bool WriteStartArray() { os_->Put('['); return true; }
bool WriteEndArray() { os_->Put(']'); return true; }
+ bool WriteRawValue(const Ch* json, size_t length) {
+ PutReserve(*os_, length);
+ for (size_t i = 0; i < length; i++) {
+ RAPIDJSON_ASSERT(json[i] != '\0');
+ PutUnsafe(*os_, json[i]);
+ }
+ return true;
+ }
+
void Prefix(Type type) {
(void)type;
- if (level_stack_.GetSize() != 0) { // this value is not at root
+ if (RAPIDJSON_LIKELY(level_stack_.GetSize() != 0)) { // this value is not at root
Level* level = level_stack_.template Top<Level>();
if (level->valueCount > 0) {
if (level->inArray)
@@ -337,6 +444,7 @@
OutputStream* os_;
internal::Stack<StackAllocator> level_stack_;
+ int maxDecimalPlaces_;
bool hasRoot_;
private:
@@ -381,12 +489,78 @@
template<>
inline bool Writer<StringBuffer>::WriteDouble(double d) {
+ if (internal::Double(d).IsNanOrInf())
+ return false;
+
char *buffer = os_->Push(25);
- char* end = internal::dtoa(d, buffer);
+ char* end = internal::dtoa(d, buffer, maxDecimalPlaces_);
os_->Pop(static_cast<size_t>(25 - (end - buffer)));
return true;
}
+#if defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42)
+template<>
+inline bool Writer<StringBuffer>::ScanWriteUnescapedString(StringStream& is, size_t length) {
+ if (length < 16)
+ return RAPIDJSON_LIKELY(is.Tell() < length);
+
+ if (!RAPIDJSON_LIKELY(is.Tell() < length))
+ return false;
+
+ const char* p = is.src_;
+ const char* end = is.head_ + length;
+ const char* nextAligned = reinterpret_cast<const char*>((reinterpret_cast<size_t>(p) + 15) & static_cast<size_t>(~15));
+ const char* endAligned = reinterpret_cast<const char*>(reinterpret_cast<size_t>(end) & static_cast<size_t>(~15));
+ if (nextAligned > end)
+ return true;
+
+ while (p != nextAligned)
+ if (*p < 0x20 || *p == '\"' || *p == '\\') {
+ is.src_ = p;
+ return RAPIDJSON_LIKELY(is.Tell() < length);
+ }
+ else
+ os_->PutUnsafe(*p++);
+
+ // The rest of string using SIMD
+ static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' };
+ static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' };
+ static const char space[16] = { 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19 };
+ const __m128i dq = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&dquote[0]));
+ const __m128i bs = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&bslash[0]));
+ const __m128i sp = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&space[0]));
+
+ for (; p != endAligned; p += 16) {
+ const __m128i s = _mm_load_si128(reinterpret_cast<const __m128i *>(p));
+ const __m128i t1 = _mm_cmpeq_epi8(s, dq);
+ const __m128i t2 = _mm_cmpeq_epi8(s, bs);
+ const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x19) == 0x19
+ const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3);
+ unsigned short r = static_cast<unsigned short>(_mm_movemask_epi8(x));
+ if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped
+ SizeType len;
+#ifdef _MSC_VER // Find the index of first escaped
+ unsigned long offset;
+ _BitScanForward(&offset, r);
+ len = offset;
+#else
+ len = static_cast<SizeType>(__builtin_ffs(r) - 1);
+#endif
+ char* q = reinterpret_cast<char*>(os_->PushUnsafe(len));
+ for (size_t i = 0; i < len; i++)
+ q[i] = p[i];
+
+ p += len;
+ break;
+ }
+ _mm_storeu_si128(reinterpret_cast<__m128i *>(os_->PushUnsafe(16)), s);
+ }
+
+ is.src_ = p;
+ return RAPIDJSON_LIKELY(is.Tell() < length);
+}
+#endif // defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42)
+
RAPIDJSON_NAMESPACE_END
#ifdef _MSC_VER
=== modified file 'shorts/xml2json/rapidxml/rapidxml.hpp'
--- shorts/xml2json/rapidxml/rapidxml.hpp 2015-12-02 17:03:41 +0000
+++ shorts/xml2json/rapidxml/rapidxml.hpp 2016-06-10 15:12:59 +0000
@@ -1055,7 +1055,7 @@
*attrname++ = Ch(':');
while (*p) {
*attrname++ = *p++;
- if ((attrname - freeme) >= (prefix_size + 6)) break;
+ //if ((attrname - freeme) >= (prefix_size + 6)) break;
}
*attrname = Ch(0);
attrname = freeme;
=== modified file 'shorts/xml2json/xml2json.hpp'
--- shorts/xml2json/xml2json.hpp 2016-03-04 13:27:57 +0000
+++ shorts/xml2json/xml2json.hpp 2016-06-10 15:12:59 +0000
@@ -165,14 +165,16 @@
{
rapidjson::Value jsvalue_chd;
jsvalue_chd.SetObject();
+ //rapidjson::Value jsvalue_name(xmlnode_chd->name(), allocator);
+ //js_doc.AddMember(jsvalue_name, jsvalue_chd, allocator);
traverse_node(xmlnode_chd, jsvalue_chd, allocator);
js_doc.AddMember(rapidjson::StringRef(xmlnode_chd->name()), jsvalue_chd, allocator);
}
- delete xml_doc;
rapidjson::StringBuffer buffer;
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
js_doc.Accept(writer);
+ delete xml_doc;
return buffer.GetString();
}
Follow ups
-
[Merge] lp:~qqworini/ubuntu-rssreader-app/fix-crash-rewrite-opml into lp:ubuntu-rssreader-app
From: noreply, 2016-06-15
-
[Merge] lp:~qqworini/ubuntu-rssreader-app/fix-crash-rewrite-opml into lp:ubuntu-rssreader-app
From: Joey Chan, 2016-06-15
-
Re: [Merge] lp:~qqworini/ubuntu-rssreader-app/fix-crash-rewrite-opml into lp:ubuntu-rssreader-app
From: Jenkins Bot, 2016-06-15
-
Re: [Merge] lp:~qqworini/ubuntu-rssreader-app/fix-crash-rewrite-opml into lp:ubuntu-rssreader-app
From: Jenkins Bot, 2016-06-15
-
Re: [Merge] lp:~qqworini/ubuntu-rssreader-app/fix-crash-rewrite-opml into lp:ubuntu-rssreader-app
From: Jenkins Bot, 2016-06-15
-
Re: [Merge] lp:~qqworini/ubuntu-rssreader-app/fix-crash-rewrite-opml into lp:ubuntu-rssreader-app
From: Jenkins Bot, 2016-06-15
-
[Merge] lp:~qqworini/ubuntu-rssreader-app/fix-crash-rewrite-opml into lp:ubuntu-rssreader-app
From: Joey Chan, 2016-06-15
-
[Merge] lp:~qqworini/ubuntu-rssreader-app/fix-crash-rewrite-opml into lp:ubuntu-rssreader-app
From: Joey Chan, 2016-06-15
-
Re: [Merge] lp:~qqworini/ubuntu-rssreader-app/fix-crash-rewrite-opml into lp:ubuntu-rssreader-app
From: Jenkins Bot, 2016-06-10
-
[Merge] lp:~qqworini/ubuntu-rssreader-app/fix-crash-rewrite-opml into lp:ubuntu-rssreader-app
From: Joey Chan, 2016-06-10
-
[Merge] lp:~qqworini/ubuntu-rssreader-app/fix-crash-rewrite-opml into lp:ubuntu-rssreader-app
From: Joey Chan, 2016-06-10