Showing
46 changed files
with
13939 additions
and
69 deletions
| ... | @@ -86,6 +86,9 @@ | ... | @@ -86,6 +86,9 @@ |
| 86 | <ClCompile Include="curl_processor.cpp"> | 86 | <ClCompile Include="curl_processor.cpp"> |
| 87 | <Filter>Source Files</Filter> | 87 | <Filter>Source Files</Filter> |
| 88 | </ClCompile> | 88 | </ClCompile> |
| 89 | + <ClCompile Include="bibtex_processor.cpp"> | ||
| 90 | + <Filter>Source Files</Filter> | ||
| 91 | + </ClCompile> | ||
| 89 | </ItemGroup> | 92 | </ItemGroup> |
| 90 | <ItemGroup> | 93 | <ItemGroup> |
| 91 | <CustomBuild Include="PaperGraphWidget.h"> | 94 | <CustomBuild Include="PaperGraphWidget.h"> |
| ... | @@ -117,5 +120,8 @@ | ... | @@ -117,5 +120,8 @@ |
| 117 | <ClInclude Include="curl_processor.h"> | 120 | <ClInclude Include="curl_processor.h"> |
| 118 | <Filter>Header Files</Filter> | 121 | <Filter>Header Files</Filter> |
| 119 | </ClInclude> | 122 | </ClInclude> |
| 123 | + <ClInclude Include="bibtex_processor.h"> | ||
| 124 | + <Filter>Header Files</Filter> | ||
| 125 | + </ClInclude> | ||
| 120 | </ItemGroup> | 126 | </ItemGroup> |
| 121 | </Project> | 127 | </Project> |
| ... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
PaperGraph/bibtex_processor.cpp
0 → 100644
| 1 | +#include "stdafx.h" | ||
| 2 | +#include "bibtex_processor.h" | ||
| 3 | + | ||
| 4 | +////////////////////////////////////////////////////////////////// | ||
| 5 | +// constructor, destructor | ||
| 6 | +////////////////////////////////////////////////////////////////// | ||
| 7 | +bibtex_processor::bibtex_processor() { | ||
| 8 | +} | ||
| 9 | + | ||
| 10 | +bibtex_processor::~bibtex_processor() { | ||
| 11 | +} | ||
| 12 | + | ||
| 13 | + | ||
| 14 | +////////////////////////////////////////////////////////////////// | ||
| 15 | +// methods | ||
| 16 | +////////////////////////////////////////////////////////////////// | ||
| 17 | +void bibtex_processor::read(const std::string& bibtex_string) { | ||
| 18 | + bibtex::read(bibtex_string, entry); | ||
| 19 | +} | ||
| 20 | + | ||
| 21 | +bool bibtex_processor::get_value(const std::string& field_name, std::string& return_val) { | ||
| 22 | + if (entry.fields.empty()) return false; | ||
| 23 | + | ||
| 24 | + bool found = false; | ||
| 25 | + for (auto& field: entry.fields) { | ||
| 26 | + if (field.first == field_name) { | ||
| 27 | + found = true; | ||
| 28 | + return_val = field.second.front(); | ||
| 29 | + break; | ||
| 30 | + } | ||
| 31 | + } | ||
| 32 | + return found; | ||
| 33 | +} | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
PaperGraph/bibtex_processor.h
0 → 100644
| 1 | +#pragma once | ||
| 2 | +#include "stdafx.h" | ||
| 3 | + | ||
| 4 | +class bibtex_processor | ||
| 5 | +{ | ||
| 6 | + //private var | ||
| 7 | +private: | ||
| 8 | + bibtex::BibTeXEntry entry; | ||
| 9 | + | ||
| 10 | + //constructor, destructor | ||
| 11 | +public: | ||
| 12 | + bibtex_processor(); | ||
| 13 | + ~bibtex_processor(); | ||
| 14 | + | ||
| 15 | + //methods | ||
| 16 | +public: | ||
| 17 | + void read(const std::string& bibtex_string); | ||
| 18 | + bool get_value(const std::string& field_name, std::string& return_val); | ||
| 19 | +}; | ||
| 20 | + |
| ... | @@ -5,18 +5,18 @@ | ... | @@ -5,18 +5,18 @@ |
| 5 | ////////////////////////////////////////////////////////////////// | 5 | ////////////////////////////////////////////////////////////////// |
| 6 | // private func | 6 | // private func |
| 7 | ////////////////////////////////////////////////////////////////// | 7 | ////////////////////////////////////////////////////////////////// |
| 8 | -size_t curl_processor::write_callback(void *contents, size_t size, size_t nmemb, void *userp) { | 8 | +//size_t curl_processor::write_callback(void *contents, size_t size, size_t nmemb, void *userp) { |
| 9 | - //copy ptr | 9 | +// //copy ptr |
| 10 | - std::string* p_str = (std::string*)userp; | 10 | +// std::string* p_str = (std::string*)userp; |
| 11 | - | 11 | +// |
| 12 | - //clear | 12 | +// //clear |
| 13 | - if (!p_str->empty()) | 13 | +// if (!p_str->empty()) |
| 14 | - p_str->clear(); | 14 | +// p_str->clear(); |
| 15 | - | 15 | +// |
| 16 | - //write | 16 | +// //write |
| 17 | - p_str->append((char*)contents, size*nmemb); | 17 | +// p_str->append((char*)contents, size*nmemb); |
| 18 | - return size*nmemb; | 18 | +// return size*nmemb; |
| 19 | -} | 19 | +//} |
| 20 | 20 | ||
| 21 | 21 | ||
| 22 | ////////////////////////////////////////////////////////////////// | 22 | ////////////////////////////////////////////////////////////////// |
| ... | @@ -29,7 +29,7 @@ curl_processor::curl_processor() { | ... | @@ -29,7 +29,7 @@ curl_processor::curl_processor() { |
| 29 | } | 29 | } |
| 30 | 30 | ||
| 31 | //set curl option | 31 | //set curl option |
| 32 | - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, this->write_callback); | 32 | + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback); |
| 33 | curl_easy_setopt(curl, CURLOPT_WRITEDATA, &result_buffer); | 33 | curl_easy_setopt(curl, CURLOPT_WRITEDATA, &result_buffer); |
| 34 | } | 34 | } |
| 35 | 35 | ... | ... |
| 1 | -#ifndef CURL_PROCESSOR_H | 1 | +#pragma once |
| 2 | -#define CURL_PROCESSOR_H | 2 | +#include "stdafx.h" |
| 3 | - | ||
| 4 | -#include <stdafx.h> | ||
| 5 | 3 | ||
| 6 | class curl_processor | 4 | class curl_processor |
| 7 | { | 5 | { |
| ... | @@ -11,10 +9,6 @@ private: | ... | @@ -11,10 +9,6 @@ private: |
| 11 | CURLcode res; | 9 | CURLcode res; |
| 12 | std::string result_buffer; | 10 | std::string result_buffer; |
| 13 | 11 | ||
| 14 | - //private func | ||
| 15 | -private: | ||
| 16 | - size_t write_callback(void *contents, size_t size, size_t nmemb, void *userp); | ||
| 17 | - | ||
| 18 | //constructor, destructor | 12 | //constructor, destructor |
| 19 | public: | 13 | public: |
| 20 | curl_processor(); | 14 | curl_processor(); |
| ... | @@ -27,4 +21,3 @@ public: | ... | @@ -27,4 +21,3 @@ public: |
| 27 | bool perform(); | 21 | bool perform(); |
| 28 | }; | 22 | }; |
| 29 | 23 | ||
| 30 | -#endif // CURL_PROCESSOR_H | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
PaperGraph/include/bibtexentry.hpp
0 → 100644
| 1 | +// Copyright (c) 2012 Sergiu Dotenco | ||
| 2 | +// | ||
| 3 | +// Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| 4 | +// of this software and associated documentation files (the "Software"), to deal | ||
| 5 | +// in the Software without restriction, including without limitation the rights | ||
| 6 | +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
| 7 | +// copies of the Software, and to permit persons to whom the Software is | ||
| 8 | +// furnished to do so, subject to the following conditions: | ||
| 9 | +// | ||
| 10 | +// The above copyright notice and this permission notice shall be included in | ||
| 11 | +// all copies or substantial portions of the Software. | ||
| 12 | +// | ||
| 13 | +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| 14 | +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| 15 | +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| 16 | +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| 17 | +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| 18 | +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
| 19 | +// SOFTWARE. | ||
| 20 | + | ||
| 21 | +/** | ||
| 22 | + * @brief BibTeX entry. | ||
| 23 | + * @file bibtexentry.hpp | ||
| 24 | + * @author Sergiu Dotenco | ||
| 25 | + * @version 0.1 | ||
| 26 | + */ | ||
| 27 | + | ||
| 28 | +#ifndef BIBTEXENTRY_HPP | ||
| 29 | +#define BIBTEXENTRY_HPP | ||
| 30 | + | ||
| 31 | +#pragma once | ||
| 32 | + | ||
| 33 | +#include <string> | ||
| 34 | +#include <utility> | ||
| 35 | +#include <vector> | ||
| 36 | + | ||
| 37 | +#include <boost/fusion/include/adapt_struct.hpp> | ||
| 38 | +#include <boost/fusion/include/std_pair.hpp> | ||
| 39 | +#include <boost/operators.hpp> | ||
| 40 | +#include <boost/optional.hpp> | ||
| 41 | +#include <boost/spirit/include/qi.hpp> | ||
| 42 | +#include <boost/typeof/typeof.hpp> | ||
| 43 | + | ||
| 44 | +#ifndef BOOST_SPIRIT_AUTO | ||
| 45 | +#define BOOST_SPIRIT_AUTO(domain_, name, expr) \ | ||
| 46 | + typedef boost::proto::result_of:: \ | ||
| 47 | + deep_copy<BOOST_TYPEOF(expr)>::type name##_expr_type; \ | ||
| 48 | + BOOST_SPIRIT_ASSERT_MATCH( \ | ||
| 49 | + boost::spirit::domain_::domain, name##_expr_type); \ | ||
| 50 | + BOOST_AUTO(const name, boost::proto::deep_copy(expr)); | ||
| 51 | +#endif // BOOST_SPIRIT_AUTO | ||
| 52 | + | ||
| 53 | +namespace bibtex { | ||
| 54 | + | ||
| 55 | +namespace detail { | ||
| 56 | + | ||
| 57 | +namespace encoding = boost::spirit::standard_wide; | ||
| 58 | + | ||
| 59 | +} // namespace detail | ||
| 60 | + | ||
| 61 | +typedef std::vector<std::string> ValueVector; | ||
| 62 | +typedef std::pair<std::string, ValueVector> KeyValue; | ||
| 63 | +typedef std::vector<KeyValue> KeyValueVector; | ||
| 64 | +typedef detail::encoding::space_type Space; | ||
| 65 | + | ||
| 66 | +BOOST_SPIRIT_AUTO(qi, space, detail::encoding::space | | ||
| 67 | + '%' >> *(boost::spirit::qi::char_ - boost::spirit::qi::eol) | ||
| 68 | + >> boost::spirit::qi::eol) | ||
| 69 | + | ||
| 70 | +/** | ||
| 71 | + * @brief Single BibTeX entry. | ||
| 72 | + */ | ||
| 73 | +struct BibTeXEntry | ||
| 74 | + : boost::equality_comparable<BibTeXEntry> | ||
| 75 | +{ | ||
| 76 | + //! Entry's tag. | ||
| 77 | + std::string tag; | ||
| 78 | + //! Entry's optional key. | ||
| 79 | + boost::optional<std::string> key; | ||
| 80 | + //! Entry's key/value pairs. | ||
| 81 | + KeyValueVector fields; | ||
| 82 | +}; | ||
| 83 | + | ||
| 84 | +inline bool operator==(const BibTeXEntry& lhs, const BibTeXEntry& rhs) | ||
| 85 | +{ | ||
| 86 | + return lhs.tag == rhs.tag && lhs.tag == rhs.tag && | ||
| 87 | + lhs.fields == rhs.fields; | ||
| 88 | +} | ||
| 89 | + | ||
| 90 | +} // namespace bibtex | ||
| 91 | + | ||
| 92 | +BOOST_FUSION_ADAPT_STRUCT( | ||
| 93 | + bibtex::BibTeXEntry, | ||
| 94 | + (std::string, tag) | ||
| 95 | + (boost::optional<std::string>, key) | ||
| 96 | + (bibtex::KeyValueVector, fields) | ||
| 97 | +) | ||
| 98 | + | ||
| 99 | +#endif // BIBTEXENTRY_HPP |
PaperGraph/include/bibtexreader.hpp
0 → 100644
| 1 | +// Copyright (c) 2012 Sergiu Dotenco | ||
| 2 | +// | ||
| 3 | +// Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| 4 | +// of this software and associated documentation files (the "Software"), to deal | ||
| 5 | +// in the Software without restriction, including without limitation the rights | ||
| 6 | +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
| 7 | +// copies of the Software, and to permit persons to whom the Software is | ||
| 8 | +// furnished to do so, subject to the following conditions: | ||
| 9 | +// | ||
| 10 | +// The above copyright notice and this permission notice shall be included in | ||
| 11 | +// all copies or substantial portions of the Software. | ||
| 12 | +// | ||
| 13 | +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| 14 | +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| 15 | +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| 16 | +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| 17 | +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| 18 | +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
| 19 | +// SOFTWARE. | ||
| 20 | + | ||
| 21 | +/** | ||
| 22 | + * @brief Boost.Spirit based BibTeX parser. | ||
| 23 | + * @file bibtexreader.hpp | ||
| 24 | + * @author Sergiu Dotenco | ||
| 25 | + * @version 0.1 | ||
| 26 | + */ | ||
| 27 | + | ||
| 28 | +#ifndef BIBTEXREADER_HPP | ||
| 29 | +#define BIBTEXREADER_HPP | ||
| 30 | + | ||
| 31 | +#pragma once | ||
| 32 | + | ||
| 33 | +#include <istream> | ||
| 34 | +#include <string> | ||
| 35 | + | ||
| 36 | +#include <boost/fusion/include/vector.hpp> | ||
| 37 | +#include <boost/io/ios_state.hpp> | ||
| 38 | +#include <boost/mpl/and.hpp> | ||
| 39 | +#include <boost/range.hpp> | ||
| 40 | +#include <boost/spirit/include/phoenix.hpp> | ||
| 41 | +#include <boost/spirit/include/support_istream_iterator.hpp> | ||
| 42 | +#include <boost/utility/enable_if.hpp> | ||
| 43 | + | ||
| 44 | +#include "bibtexentry.hpp" | ||
| 45 | + | ||
| 46 | +namespace bibtex { | ||
| 47 | + | ||
| 48 | +/** | ||
| 49 | + * @brief Single BibTeX entry reader. | ||
| 50 | + * | ||
| 51 | + * @tparam ForwardIterator Input sequence iterator type. | ||
| 52 | + * @tparam Skipper Skipper type. | ||
| 53 | + */ | ||
| 54 | +template <class ForwardIterator, class Skipper> | ||
| 55 | +class BibTeXReader | ||
| 56 | + : public boost::spirit::qi::grammar<ForwardIterator, BibTeXEntry(), Skipper> | ||
| 57 | +{ | ||
| 58 | +public: | ||
| 59 | + BibTeXReader() | ||
| 60 | + : BibTeXReader::base_type(start_, "single BibTeX entry") | ||
| 61 | + { | ||
| 62 | + using namespace boost::spirit; | ||
| 63 | + namespace ph = boost::phoenix; | ||
| 64 | + namespace sn = detail::encoding; | ||
| 65 | + | ||
| 66 | + escapedBrace.add | ||
| 67 | + ("\\{", '{') | ||
| 68 | + ("\\}", '}') | ||
| 69 | + ; | ||
| 70 | + escapedQuote.add | ||
| 71 | + ("\\\"", '"') | ||
| 72 | + ; | ||
| 73 | + | ||
| 74 | + tag_ = +detail::encoding::alnum; | ||
| 75 | + entryKey_ = qi::lexeme[+(~qi::char_(',') - sn::space)]; | ||
| 76 | + key_ = qi::lexeme[+(~qi::char_("=,})") - sn::space)]; | ||
| 77 | + | ||
| 78 | + escapedText_ | ||
| 79 | + = !qi::lit('{') >> +(escapedBrace | ~qi::char_("{}")) | ||
| 80 | + ; | ||
| 81 | + | ||
| 82 | + quoteText_ | ||
| 83 | + = +(escapedQuote | ~qi::char_("\"{}")) | ||
| 84 | + ; | ||
| 85 | + | ||
| 86 | + innerBraceText_ | ||
| 87 | + %= | ||
| 88 | + qi::as_string | ||
| 89 | + [ | ||
| 90 | + qi::char_('{') | ||
| 91 | + >> | ||
| 92 | + *(innerBraceText_ | escapedText_) | ||
| 93 | + >> | ||
| 94 | + qi::char_('}') | ||
| 95 | + ] | ||
| 96 | + | | ||
| 97 | + escapedText_ | ||
| 98 | + ; | ||
| 99 | + | ||
| 100 | + innerQuoteText_ | ||
| 101 | + %= | ||
| 102 | + qi::as_string | ||
| 103 | + [ | ||
| 104 | + qi::char_('{') | ||
| 105 | + >> | ||
| 106 | + *(innerQuoteText_ | escapedText_) | ||
| 107 | + >> | ||
| 108 | + qi::char_('}') | ||
| 109 | + ] | ||
| 110 | + | | ||
| 111 | + quoteText_ | ||
| 112 | + ; | ||
| 113 | + | ||
| 114 | + quoted_ | ||
| 115 | + = qi::lexeme | ||
| 116 | + [ | ||
| 117 | + ( '"' >> *innerQuoteText_ >> '"' ) | ||
| 118 | + | | ||
| 119 | + ( '{' >> *innerBraceText_ >> '}' ) | ||
| 120 | + ] | ||
| 121 | + [ | ||
| 122 | + _val = ph::accumulate(boost::spirit::_1, ph::construct<std::string>()) | ||
| 123 | + ] | ||
| 124 | + ; | ||
| 125 | + | ||
| 126 | + value_ | ||
| 127 | + = quoted_ | ||
| 128 | + | +~qi::char_(",})#") | ||
| 129 | + ; | ||
| 130 | + | ||
| 131 | + values_ | ||
| 132 | + = value_ % '#' | ||
| 133 | + ; | ||
| 134 | + | ||
| 135 | + field_ | ||
| 136 | + = key_ >> '=' >> values_ | ||
| 137 | + ; | ||
| 138 | + | ||
| 139 | + fields_ = -(field_ % ','); | ||
| 140 | + | ||
| 141 | + body_ | ||
| 142 | + = -entryKey_ >> ',' // tolerate an empty key | ||
| 143 | + >> fields_ | ||
| 144 | + >> -qi::lit(',') // accept a trailing comma | ||
| 145 | + ; | ||
| 146 | + | ||
| 147 | + generic_ | ||
| 148 | + = | ||
| 149 | + '@' >> tag_ | ||
| 150 | + [ | ||
| 151 | + ph::at_c<0>(_val) = boost::spirit::_1 | ||
| 152 | + ] | ||
| 153 | + >> | ||
| 154 | + ( | ||
| 155 | + ('{' >> body_ >> '}') | ||
| 156 | + | | ||
| 157 | + ('(' >> body_ >> ')') | ||
| 158 | + ) | ||
| 159 | + [ | ||
| 160 | + ph::at_c<1>(_val) = ph::at_c<0>(boost::spirit::_1), | ||
| 161 | + ph::at_c<2>(_val) = ph::at_c<1>(boost::spirit::_1) | ||
| 162 | + ] | ||
| 163 | + ; | ||
| 164 | + | ||
| 165 | + string_ | ||
| 166 | + = | ||
| 167 | + '@' >> sn::no_case | ||
| 168 | + [ | ||
| 169 | + qi::string("string") | ||
| 170 | + [ | ||
| 171 | + ph::at_c<0>(_val) = boost::spirit::_1 | ||
| 172 | + ] | ||
| 173 | + ] | ||
| 174 | + >> | ||
| 175 | + ( | ||
| 176 | + ('{' >> field_ >> '}') | ||
| 177 | + | | ||
| 178 | + ('(' >> field_ >> ')') | ||
| 179 | + ) | ||
| 180 | + [ | ||
| 181 | + ph::assign(ph::bind(&BibTeXEntry::fields, _val), 1, boost::spirit::_1) | ||
| 182 | + ] | ||
| 183 | + ; | ||
| 184 | + | ||
| 185 | + simple_ | ||
| 186 | + = | ||
| 187 | + '@' >> sn::no_case | ||
| 188 | + [ | ||
| 189 | + ( | ||
| 190 | + sn::string("comment") | ||
| 191 | + | | ||
| 192 | + sn::string("include") | ||
| 193 | + | | ||
| 194 | + sn::string("preamble") | ||
| 195 | + ) | ||
| 196 | + [ | ||
| 197 | + ph::at_c<0>(_val) = boost::spirit::_1 | ||
| 198 | + ] | ||
| 199 | + ] | ||
| 200 | + >> | ||
| 201 | + ( | ||
| 202 | + ('{' >> values_ >> '}') | ||
| 203 | + | | ||
| 204 | + ('(' >> values_ >> ')') | ||
| 205 | + ) | ||
| 206 | + [ | ||
| 207 | + ph::assign(ph::bind(&BibTeXEntry::fields, _val), 1, | ||
| 208 | + ph::construct<KeyValue> | ||
| 209 | + (ph::construct<KeyValue::first_type>(), boost::spirit::_1)) | ||
| 210 | + ] | ||
| 211 | + ; | ||
| 212 | + | ||
| 213 | + entry_ | ||
| 214 | + = string_ | ||
| 215 | + | simple_ | ||
| 216 | + | generic_ | ||
| 217 | + ; | ||
| 218 | + | ||
| 219 | + junk_ | ||
| 220 | + = *~qi::lit('@') | ||
| 221 | + ; | ||
| 222 | + | ||
| 223 | + start_ | ||
| 224 | + = junk_ >> entry_ | ||
| 225 | + ; | ||
| 226 | + } | ||
| 227 | + | ||
| 228 | +private: | ||
| 229 | + typedef boost::spirit::qi::rule<ForwardIterator, std::string(), Skipper> | ||
| 230 | + StringRule; | ||
| 231 | + typedef boost::spirit::qi::rule<ForwardIterator, std::string()> | ||
| 232 | + SimpleStringRule; | ||
| 233 | + | ||
| 234 | + boost::spirit::qi::rule<ForwardIterator, BibTeXEntry(), Skipper> generic_; | ||
| 235 | + boost::spirit::qi::rule<ForwardIterator, BibTeXEntry(), Skipper> include_; | ||
| 236 | + boost::spirit::qi::rule<ForwardIterator, BibTeXEntry(), Skipper> simple_; | ||
| 237 | + boost::spirit::qi::rule<ForwardIterator, BibTeXEntry(), Skipper> start_; | ||
| 238 | + boost::spirit::qi::rule<ForwardIterator, BibTeXEntry(), Skipper> string_; | ||
| 239 | + boost::spirit::qi::rule<ForwardIterator, BibTeXEntry(), Skipper> entry_; | ||
| 240 | + boost::spirit::qi::rule<ForwardIterator, ValueVector(), Skipper> values_; | ||
| 241 | + boost::spirit::qi::rule<ForwardIterator, Skipper> junk_; | ||
| 242 | + boost::spirit::qi::rule<ForwardIterator, | ||
| 243 | + boost::fusion::vector<boost::optional<std::string>, | ||
| 244 | + KeyValueVector>(), Skipper> body_; | ||
| 245 | + boost::spirit::qi::rule<ForwardIterator, KeyValue(), Skipper> field_; | ||
| 246 | + boost::spirit::qi::rule<ForwardIterator, KeyValueVector(), Skipper> | ||
| 247 | + fields_; | ||
| 248 | + boost::spirit::qi::symbols<char, char> escapedBrace; | ||
| 249 | + boost::spirit::qi::symbols<char, char> escapedQuote; | ||
| 250 | + StringRule quoted_; | ||
| 251 | + StringRule entryKey_; | ||
| 252 | + StringRule key_; | ||
| 253 | + StringRule tag_; | ||
| 254 | + StringRule value_; | ||
| 255 | + SimpleStringRule innerBraceText_; | ||
| 256 | + SimpleStringRule escapedText_; | ||
| 257 | + SimpleStringRule innerQuoteText_; | ||
| 258 | + SimpleStringRule quoteText_; | ||
| 259 | +}; | ||
| 260 | + | ||
| 261 | +template<class ForwardIterator, class Skipper> | ||
| 262 | +inline bool read(ForwardIterator first, ForwardIterator last, Skipper& skipper, | ||
| 263 | + BibTeXEntry& entry) | ||
| 264 | +{ | ||
| 265 | + BibTeXReader<ForwardIterator, Skipper> parser; | ||
| 266 | + return boost::spirit::qi::phrase_parse(first, last, parser, skipper, entry); | ||
| 267 | +} | ||
| 268 | + | ||
| 269 | +template<class ForwardIterator, class Skipper, class Container> | ||
| 270 | +inline bool read(ForwardIterator first, ForwardIterator last, Skipper& skipper, | ||
| 271 | + Container& entries, boost::enable_if<boost::is_same< | ||
| 272 | + typename Container::value_type, BibTeXEntry> >* /*dummy*/ = NULL) | ||
| 273 | +{ | ||
| 274 | + BibTeXReader<ForwardIterator, Skipper> parser; | ||
| 275 | + return boost::spirit::qi::phrase_parse(first, last, *parser, skipper, | ||
| 276 | + entries); | ||
| 277 | +} | ||
| 278 | + | ||
| 279 | +template<class ForwardRange, class Skipper> | ||
| 280 | +inline bool read(const ForwardRange& range, Skipper& skipper, | ||
| 281 | + BibTeXEntry& entry) | ||
| 282 | +{ | ||
| 283 | + return read(boost::const_begin(range), boost::const_end(range), entry); | ||
| 284 | +} | ||
| 285 | + | ||
| 286 | +template<class ForwardRange, class Skipper, class Container> | ||
| 287 | +inline bool read(const ForwardRange& range, Skipper& skipper, | ||
| 288 | + Container& entries, boost::enable_if<boost::is_same< | ||
| 289 | + typename Container::value_type, BibTeXEntry> >* /*dummy*/ = NULL) | ||
| 290 | +{ | ||
| 291 | + return read(boost::const_begin(range), boost::const_end(range), entries); | ||
| 292 | +} | ||
| 293 | + | ||
| 294 | +template<class ForwardIterator> | ||
| 295 | +inline bool read(ForwardIterator first, ForwardIterator last, | ||
| 296 | + BibTeXEntry& entry) | ||
| 297 | +{ | ||
| 298 | + return read(first, last, bibtex::space, entry); | ||
| 299 | +} | ||
| 300 | + | ||
| 301 | +template<class ForwardIterator, class Container> | ||
| 302 | +inline bool read(ForwardIterator first, ForwardIterator last, | ||
| 303 | + Container& entries, boost::enable_if<boost::is_same< | ||
| 304 | + typename Container::value_type, BibTeXEntry> >* /*dummy*/ = NULL) | ||
| 305 | +{ | ||
| 306 | + return read(first, last, bibtex::space, entries); | ||
| 307 | +} | ||
| 308 | + | ||
| 309 | +template<class ForwardRange> | ||
| 310 | +inline bool read(const ForwardRange& range, BibTeXEntry& entry, | ||
| 311 | + typename boost::enable_if< | ||
| 312 | + boost::has_range_iterator<ForwardRange> >::type* /*dummy*/ = NULL) | ||
| 313 | +{ | ||
| 314 | + return read(range, bibtex::space, entry); | ||
| 315 | +} | ||
| 316 | + | ||
| 317 | +template<class ForwardRange, class Container> | ||
| 318 | +inline bool read(const ForwardRange& range, Container& entries, | ||
| 319 | + typename boost::enable_if< | ||
| 320 | + boost::has_range_iterator<ForwardRange> >::type* /*dummy*/ = NULL) | ||
| 321 | +{ | ||
| 322 | + return read(range, bibtex::space, entries); | ||
| 323 | +} | ||
| 324 | + | ||
| 325 | +template<class E, class T> | ||
| 326 | +inline bool read(std::basic_istream<E, T>& in, BibTeXEntry& e) | ||
| 327 | +{ | ||
| 328 | + boost::io::ios_flags_saver saver(in); | ||
| 329 | + in.unsetf(std::ios_base::skipws); | ||
| 330 | + | ||
| 331 | + typedef boost::spirit::basic_istream_iterator<E> istream_iterator; | ||
| 332 | + return read(istream_iterator(in), istream_iterator(), e); | ||
| 333 | +} | ||
| 334 | + | ||
| 335 | +template<class E, class T, class Container> | ||
| 336 | +inline bool read(std::basic_istream<E, T>& in, Container& entries) | ||
| 337 | +{ | ||
| 338 | + boost::io::ios_flags_saver saver(in); | ||
| 339 | + in.unsetf(std::ios_base::skipws); | ||
| 340 | + | ||
| 341 | + typedef boost::spirit::basic_istream_iterator<E> istream_iterator; | ||
| 342 | + return read(istream_iterator(in), istream_iterator(), entries); | ||
| 343 | +} | ||
| 344 | + | ||
| 345 | +template<class E, class T> | ||
| 346 | +inline std::basic_istream<E, T>& operator>>(std::basic_istream<E, T>& in, | ||
| 347 | + BibTeXEntry& entry) | ||
| 348 | +{ | ||
| 349 | + read(in, entry); | ||
| 350 | + return in; | ||
| 351 | +} | ||
| 352 | + | ||
| 353 | +template<class E, class T, class Range> | ||
| 354 | +inline typename boost::enable_if< | ||
| 355 | + boost::mpl::and_< | ||
| 356 | + boost::has_range_iterator<Range>, | ||
| 357 | + boost::is_same<typename Range::value_type, BibTeXEntry> | ||
| 358 | + >, std::basic_istream<E, T> | ||
| 359 | +>::type& operator>>(std::basic_istream<E, T>& in, Range& entries) | ||
| 360 | +{ | ||
| 361 | + read(in, entries); | ||
| 362 | + return in; | ||
| 363 | +} | ||
| 364 | + | ||
| 365 | +} // namespace bibtex | ||
| 366 | + | ||
| 367 | +#endif // BIBTEXREADER_HPP |
PaperGraph/include/bibtexwriter.hpp
0 → 100644
| 1 | +// Copyright (c) 2012 Sergiu Dotenco | ||
| 2 | +// | ||
| 3 | +// Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| 4 | +// of this software and associated documentation files (the "Software"), to deal | ||
| 5 | +// in the Software without restriction, including without limitation the rights | ||
| 6 | +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
| 7 | +// copies of the Software, and to permit persons to whom the Software is | ||
| 8 | +// furnished to do so, subject to the following conditions: | ||
| 9 | +// | ||
| 10 | +// The above copyright notice and this permission notice shall be included in | ||
| 11 | +// all copies or substantial portions of the Software. | ||
| 12 | +// | ||
| 13 | +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| 14 | +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| 15 | +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| 16 | +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| 17 | +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| 18 | +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
| 19 | +// SOFTWARE. | ||
| 20 | + | ||
| 21 | +/** | ||
| 22 | + * @brief Boost.Spirit based BibTeX generator. | ||
| 23 | + * @file bibtexwriter.hpp | ||
| 24 | + * @author Sergiu Dotenco | ||
| 25 | + * @version 0.1 | ||
| 26 | + */ | ||
| 27 | + | ||
| 28 | +#ifndef BIBTEXWRITER_HPP | ||
| 29 | +#define BIBTEXWRITER_HPP | ||
| 30 | + | ||
| 31 | +#pragma once | ||
| 32 | + | ||
| 33 | +#include <iterator> | ||
| 34 | +#include <ostream> | ||
| 35 | +#include <string> | ||
| 36 | + | ||
| 37 | +#include <boost/algorithm/string/case_conv.hpp> | ||
| 38 | +#include <boost/fusion/adapted/std_pair.hpp> | ||
| 39 | +#include <boost/mpl/and.hpp> | ||
| 40 | +#include <boost/range/has_range_iterator.hpp> | ||
| 41 | +#include <boost/spirit/include/karma.hpp> | ||
| 42 | +#include <boost/spirit/include/phoenix.hpp> | ||
| 43 | +#include <boost/type_traits/is_same.hpp> | ||
| 44 | +#include <boost/utility/enable_if.hpp> | ||
| 45 | + | ||
| 46 | +#include "bibtexentry.hpp" | ||
| 47 | + | ||
| 48 | +namespace bibtex { | ||
| 49 | + | ||
| 50 | +template<class OutputIterator, class Skipper> | ||
| 51 | +class BibTeXWriter | ||
| 52 | + : public boost::spirit::karma::grammar | ||
| 53 | + < | ||
| 54 | + OutputIterator, | ||
| 55 | + BibTeXEntry(), | ||
| 56 | + Skipper | ||
| 57 | + > | ||
| 58 | +{ | ||
| 59 | +public: | ||
| 60 | + BibTeXWriter() | ||
| 61 | + : BibTeXWriter::base_type(start_, "single BibTeX entry") | ||
| 62 | + { | ||
| 63 | + using namespace boost::spirit; | ||
| 64 | + namespace ph = boost::phoenix; | ||
| 65 | + namespace enc = detail::encoding; | ||
| 66 | + | ||
| 67 | + escapedBrace.add | ||
| 68 | + ('{', "\\{") | ||
| 69 | + ('}', "\\}") | ||
| 70 | + ; | ||
| 71 | + | ||
| 72 | + tag_ = +enc::alnum; | ||
| 73 | + key_ = +~karma::char_(','); | ||
| 74 | + value_ = *(escapedBrace | ~karma::char_("{}")); | ||
| 75 | + | ||
| 76 | + values_ | ||
| 77 | + = ('{' << value_ << '}') % " # "; | ||
| 78 | + ; | ||
| 79 | + | ||
| 80 | + field_ | ||
| 81 | + = enc::string | ||
| 82 | + << " = " | ||
| 83 | + << values_ | ||
| 84 | + ; | ||
| 85 | + | ||
| 86 | + fields_ | ||
| 87 | + = (repeat(4)[' '] << field_) % (',' << karma::eol) | ||
| 88 | + ; | ||
| 89 | + | ||
| 90 | + generic_ | ||
| 91 | + = '@' << tag_ << | ||
| 92 | + '{' | ||
| 93 | + << -key_ << ',' << karma::eol | ||
| 94 | + << fields_ << karma::eol << | ||
| 95 | + '}' | ||
| 96 | + ; | ||
| 97 | + | ||
| 98 | + simple_ | ||
| 99 | + = | ||
| 100 | + & | ||
| 101 | + ( | ||
| 102 | + karma::string("comment") | ||
| 103 | + | | ||
| 104 | + karma::string("include") | ||
| 105 | + | | ||
| 106 | + karma::string("preamble") | ||
| 107 | + ) | ||
| 108 | + [ | ||
| 109 | + _1 = ph::bind(tolower, ph::at_c<0>(_val)) | ||
| 110 | + ] | ||
| 111 | + << | ||
| 112 | + '@' | ||
| 113 | + << | ||
| 114 | + karma::string | ||
| 115 | + [ | ||
| 116 | + _1 = ph::at_c<0>(_val) | ||
| 117 | + ] | ||
| 118 | + << | ||
| 119 | + '{' | ||
| 120 | + << values_ | ||
| 121 | + [ | ||
| 122 | + _1 = ph::bind(&KeyValue::second, | ||
| 123 | + ph::front(ph::at_c<2>(_val))) | ||
| 124 | + ] | ||
| 125 | + << | ||
| 126 | + '}' | ||
| 127 | + ; | ||
| 128 | + | ||
| 129 | + string_ | ||
| 130 | + = | ||
| 131 | + &karma::string("string") | ||
| 132 | + [ | ||
| 133 | + _1 = ph::bind(tolower, ph::at_c<0>(_val)) | ||
| 134 | + ] | ||
| 135 | + << | ||
| 136 | + '@' | ||
| 137 | + << | ||
| 138 | + karma::string | ||
| 139 | + [ | ||
| 140 | + _1 = ph::at_c<0>(_val) | ||
| 141 | + ] | ||
| 142 | + << | ||
| 143 | + '{' | ||
| 144 | + << field_ | ||
| 145 | + [ | ||
| 146 | + ph::let(ph::local_names::_a = ph::front(ph::at_c<2>(_val))) | ||
| 147 | + [ | ||
| 148 | + _1 = ph::construct<KeyValue> | ||
| 149 | + (ph::bind(&KeyValue::first, ph::local_names::_a), | ||
| 150 | + ph::bind(&KeyValue::second, ph::local_names::_a)) | ||
| 151 | + ] | ||
| 152 | + ] | ||
| 153 | + << | ||
| 154 | + '}' | ||
| 155 | + ; | ||
| 156 | + | ||
| 157 | + start_ | ||
| 158 | + = string_ | ||
| 159 | + | simple_ | ||
| 160 | + | generic_ | ||
| 161 | + ; | ||
| 162 | + } | ||
| 163 | + | ||
| 164 | + static std::string tolower(const std::string& in) | ||
| 165 | + { | ||
| 166 | + return boost::algorithm::to_lower_copy(in); | ||
| 167 | + } | ||
| 168 | + | ||
| 169 | +private: | ||
| 170 | + boost::spirit::karma::rule<OutputIterator, BibTeXEntry(), Skipper> start_; | ||
| 171 | + boost::spirit::karma::rule<OutputIterator, BibTeXEntry(), Skipper> generic_; | ||
| 172 | + boost::spirit::karma::rule<OutputIterator, BibTeXEntry(), Skipper> simple_; | ||
| 173 | + boost::spirit::karma::rule<OutputIterator, BibTeXEntry()> string_; | ||
| 174 | + boost::spirit::karma::rule<OutputIterator, std::string()> tag_; | ||
| 175 | + boost::spirit::karma::rule<OutputIterator, std::string()> key_; | ||
| 176 | + boost::spirit::karma::rule<OutputIterator, std::string()> value_; | ||
| 177 | + boost::spirit::karma::rule<OutputIterator, ValueVector(), Skipper> | ||
| 178 | + values_; | ||
| 179 | + boost::spirit::karma::rule<OutputIterator, KeyValue(), Skipper> | ||
| 180 | + field_; | ||
| 181 | + boost::spirit::karma::rule<OutputIterator, KeyValueVector(), Skipper> | ||
| 182 | + fields_; | ||
| 183 | + boost::spirit::karma::symbols<char, const char*> escapedBrace; | ||
| 184 | +}; | ||
| 185 | + | ||
| 186 | +template<class OutputIterator, class Skipper> | ||
| 187 | +inline bool write(OutputIterator out, const Skipper& skipper, | ||
| 188 | + const BibTeXEntry& entry) | ||
| 189 | +{ | ||
| 190 | + BibTeXWriter<OutputIterator, Skipper> writer; | ||
| 191 | + return boost::spirit::karma::generate_delimited(out, writer, | ||
| 192 | + boost::spirit::karma::eol, entry); | ||
| 193 | +} | ||
| 194 | + | ||
| 195 | +template<class OutputIterator, class Skipper, class Range> | ||
| 196 | +inline bool write(OutputIterator out, Skipper& skipper, const Range& range, | ||
| 197 | + typename boost::enable_if<boost::has_range_iterator<Range> >::type* | ||
| 198 | + /*dummy*/ = NULL) | ||
| 199 | +{ | ||
| 200 | + BibTeXWriter<OutputIterator, Skipper> writer; | ||
| 201 | + return boost::spirit::karma::generate_delimited(out, *writer, | ||
| 202 | + boost::spirit::karma::eol, range); | ||
| 203 | +} | ||
| 204 | + | ||
| 205 | +template<class OutputIterator> | ||
| 206 | +inline bool write(OutputIterator out, const BibTeXEntry& entry) | ||
| 207 | +{ | ||
| 208 | + return write(out, space, entry); | ||
| 209 | +} | ||
| 210 | + | ||
| 211 | +template<class OutputIterator, class Range> | ||
| 212 | +inline bool write(OutputIterator out, const Range& range, | ||
| 213 | + typename boost::enable_if<boost::has_range_iterator<Range> >::type* | ||
| 214 | + /*dummy*/ = NULL) | ||
| 215 | +{ | ||
| 216 | + return write(out, space, range); | ||
| 217 | +} | ||
| 218 | + | ||
| 219 | +template<class E, class T> | ||
| 220 | +inline std::basic_ostream<E, T>& operator<<(std::basic_ostream<E, T>& out, | ||
| 221 | + const BibTeXEntry& entry) | ||
| 222 | +{ | ||
| 223 | + write(std::ostream_iterator<E>(out), entry); | ||
| 224 | + return out; | ||
| 225 | +} | ||
| 226 | + | ||
| 227 | +template<class E, class T, class Range> | ||
| 228 | +inline typename boost::enable_if< | ||
| 229 | + boost::mpl::and_< | ||
| 230 | + boost::has_range_iterator<Range>, | ||
| 231 | + boost::is_same<typename Range::value_type, BibTeXEntry> | ||
| 232 | + >, std::basic_ostream<E, T> | ||
| 233 | +>::type& operator<<(std::basic_ostream<E, T>& out, const Range& entries) | ||
| 234 | +{ | ||
| 235 | + write(std::ostream_iterator<E>(out), entries); | ||
| 236 | + return out; | ||
| 237 | +} | ||
| 238 | + | ||
| 239 | +} // namespace bibtex | ||
| 240 | + | ||
| 241 | +#endif // BIBTEXWRITER_HPP |
PaperGraph/include/rapidjson/allocators.h
0 → 100644
| 1 | +// Tencent is pleased to support the open source community by making RapidJSON available. | ||
| 2 | +// | ||
| 3 | +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. | ||
| 4 | +// | ||
| 5 | +// Licensed under the MIT License (the "License"); you may not use this file except | ||
| 6 | +// in compliance with the License. You may obtain a copy of the License at | ||
| 7 | +// | ||
| 8 | +// http://opensource.org/licenses/MIT | ||
| 9 | +// | ||
| 10 | +// Unless required by applicable law or agreed to in writing, software distributed | ||
| 11 | +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR | ||
| 12 | +// CONDITIONS OF ANY KIND, either express or implied. See the License for the | ||
| 13 | +// specific language governing permissions and limitations under the License. | ||
| 14 | + | ||
| 15 | +#ifndef RAPIDJSON_ALLOCATORS_H_ | ||
| 16 | +#define RAPIDJSON_ALLOCATORS_H_ | ||
| 17 | + | ||
| 18 | +#include "rapidjson.h" | ||
| 19 | + | ||
| 20 | +RAPIDJSON_NAMESPACE_BEGIN | ||
| 21 | + | ||
| 22 | +/////////////////////////////////////////////////////////////////////////////// | ||
| 23 | +// Allocator | ||
| 24 | + | ||
| 25 | +/*! \class rapidjson::Allocator | ||
| 26 | + \brief Concept for allocating, resizing and freeing memory block. | ||
| 27 | + | ||
| 28 | + Note that Malloc() and Realloc() are non-static but Free() is static. | ||
| 29 | + | ||
| 30 | + So if an allocator need to support Free(), it needs to put its pointer in | ||
| 31 | + the header of memory block. | ||
| 32 | + | ||
| 33 | +\code | ||
| 34 | +concept Allocator { | ||
| 35 | + static const bool kNeedFree; //!< Whether this allocator needs to call Free(). | ||
| 36 | + | ||
| 37 | + // Allocate a memory block. | ||
| 38 | + // \param size of the memory block in bytes. | ||
| 39 | + // \returns pointer to the memory block. | ||
| 40 | + void* Malloc(size_t size); | ||
| 41 | + | ||
| 42 | + // Resize a memory block. | ||
| 43 | + // \param originalPtr The pointer to current memory block. Null pointer is permitted. | ||
| 44 | + // \param originalSize The current size in bytes. (Design issue: since some allocator may not book-keep this, explicitly pass to it can save memory.) | ||
| 45 | + // \param newSize the new size in bytes. | ||
| 46 | + void* Realloc(void* originalPtr, size_t originalSize, size_t newSize); | ||
| 47 | + | ||
| 48 | + // Free a memory block. | ||
| 49 | + // \param pointer to the memory block. Null pointer is permitted. | ||
| 50 | + static void Free(void *ptr); | ||
| 51 | +}; | ||
| 52 | +\endcode | ||
| 53 | +*/ | ||
| 54 | + | ||
| 55 | +/////////////////////////////////////////////////////////////////////////////// | ||
| 56 | +// CrtAllocator | ||
| 57 | + | ||
| 58 | +//! C-runtime library allocator. | ||
| 59 | +/*! This class is just wrapper for standard C library memory routines. | ||
| 60 | + \note implements Allocator concept | ||
| 61 | +*/ | ||
| 62 | +class CrtAllocator { | ||
| 63 | +public: | ||
| 64 | + static const bool kNeedFree = true; | ||
| 65 | + void* Malloc(size_t size) { | ||
| 66 | + if (size) // behavior of malloc(0) is implementation defined. | ||
| 67 | + return std::malloc(size); | ||
| 68 | + else | ||
| 69 | + return NULL; // standardize to returning NULL. | ||
| 70 | + } | ||
| 71 | + void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) { | ||
| 72 | + (void)originalSize; | ||
| 73 | + if (newSize == 0) { | ||
| 74 | + std::free(originalPtr); | ||
| 75 | + return NULL; | ||
| 76 | + } | ||
| 77 | + return std::realloc(originalPtr, newSize); | ||
| 78 | + } | ||
| 79 | + static void Free(void *ptr) { std::free(ptr); } | ||
| 80 | +}; | ||
| 81 | + | ||
| 82 | +/////////////////////////////////////////////////////////////////////////////// | ||
| 83 | +// MemoryPoolAllocator | ||
| 84 | + | ||
| 85 | +//! Default memory allocator used by the parser and DOM. | ||
| 86 | +/*! This allocator allocate memory blocks from pre-allocated memory chunks. | ||
| 87 | + | ||
| 88 | + It does not free memory blocks. And Realloc() only allocate new memory. | ||
| 89 | + | ||
| 90 | + The memory chunks are allocated by BaseAllocator, which is CrtAllocator by default. | ||
| 91 | + | ||
| 92 | + User may also supply a buffer as the first chunk. | ||
| 93 | + | ||
| 94 | + If the user-buffer is full then additional chunks are allocated by BaseAllocator. | ||
| 95 | + | ||
| 96 | + The user-buffer is not deallocated by this allocator. | ||
| 97 | + | ||
| 98 | + \tparam BaseAllocator the allocator type for allocating memory chunks. Default is CrtAllocator. | ||
| 99 | + \note implements Allocator concept | ||
| 100 | +*/ | ||
| 101 | +template <typename BaseAllocator = CrtAllocator> | ||
| 102 | +class MemoryPoolAllocator { | ||
| 103 | +public: | ||
| 104 | + static const bool kNeedFree = false; //!< Tell users that no need to call Free() with this allocator. (concept Allocator) | ||
| 105 | + | ||
| 106 | + //! Constructor with chunkSize. | ||
| 107 | + /*! \param chunkSize The size of memory chunk. The default is kDefaultChunkSize. | ||
| 108 | + \param baseAllocator The allocator for allocating memory chunks. | ||
| 109 | + */ | ||
| 110 | + MemoryPoolAllocator(size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) : | ||
| 111 | + chunkHead_(0), chunk_capacity_(chunkSize), userBuffer_(0), baseAllocator_(baseAllocator), ownBaseAllocator_(0) | ||
| 112 | + { | ||
| 113 | + } | ||
| 114 | + | ||
| 115 | + //! Constructor with user-supplied buffer. | ||
| 116 | + /*! The user buffer will be used firstly. When it is full, memory pool allocates new chunk with chunk size. | ||
| 117 | + | ||
| 118 | + The user buffer will not be deallocated when this allocator is destructed. | ||
| 119 | + | ||
| 120 | + \param buffer User supplied buffer. | ||
| 121 | + \param size Size of the buffer in bytes. It must at least larger than sizeof(ChunkHeader). | ||
| 122 | + \param chunkSize The size of memory chunk. The default is kDefaultChunkSize. | ||
| 123 | + \param baseAllocator The allocator for allocating memory chunks. | ||
| 124 | + */ | ||
| 125 | + MemoryPoolAllocator(void *buffer, size_t size, size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) : | ||
| 126 | + chunkHead_(0), chunk_capacity_(chunkSize), userBuffer_(buffer), baseAllocator_(baseAllocator), ownBaseAllocator_(0) | ||
| 127 | + { | ||
| 128 | + RAPIDJSON_ASSERT(buffer != 0); | ||
| 129 | + RAPIDJSON_ASSERT(size > sizeof(ChunkHeader)); | ||
| 130 | + chunkHead_ = reinterpret_cast<ChunkHeader*>(buffer); | ||
| 131 | + chunkHead_->capacity = size - sizeof(ChunkHeader); | ||
| 132 | + chunkHead_->size = 0; | ||
| 133 | + chunkHead_->next = 0; | ||
| 134 | + } | ||
| 135 | + | ||
| 136 | + //! Destructor. | ||
| 137 | + /*! This deallocates all memory chunks, excluding the user-supplied buffer. | ||
| 138 | + */ | ||
| 139 | + ~MemoryPoolAllocator() { | ||
| 140 | + Clear(); | ||
| 141 | + RAPIDJSON_DELETE(ownBaseAllocator_); | ||
| 142 | + } | ||
| 143 | + | ||
| 144 | + //! Deallocates all memory chunks, excluding the user-supplied buffer. | ||
| 145 | + void Clear() { | ||
| 146 | + while (chunkHead_ && chunkHead_ != userBuffer_) { | ||
| 147 | + ChunkHeader* next = chunkHead_->next; | ||
| 148 | + baseAllocator_->Free(chunkHead_); | ||
| 149 | + chunkHead_ = next; | ||
| 150 | + } | ||
| 151 | + if (chunkHead_ && chunkHead_ == userBuffer_) | ||
| 152 | + chunkHead_->size = 0; // Clear user buffer | ||
| 153 | + } | ||
| 154 | + | ||
| 155 | + //! Computes the total capacity of allocated memory chunks. | ||
| 156 | + /*! \return total capacity in bytes. | ||
| 157 | + */ | ||
| 158 | + size_t Capacity() const { | ||
| 159 | + size_t capacity = 0; | ||
| 160 | + for (ChunkHeader* c = chunkHead_; c != 0; c = c->next) | ||
| 161 | + capacity += c->capacity; | ||
| 162 | + return capacity; | ||
| 163 | + } | ||
| 164 | + | ||
| 165 | + //! Computes the memory blocks allocated. | ||
| 166 | + /*! \return total used bytes. | ||
| 167 | + */ | ||
| 168 | + size_t Size() const { | ||
| 169 | + size_t size = 0; | ||
| 170 | + for (ChunkHeader* c = chunkHead_; c != 0; c = c->next) | ||
| 171 | + size += c->size; | ||
| 172 | + return size; | ||
| 173 | + } | ||
| 174 | + | ||
| 175 | + //! Allocates a memory block. (concept Allocator) | ||
| 176 | + void* Malloc(size_t size) { | ||
| 177 | + if (!size) | ||
| 178 | + return NULL; | ||
| 179 | + | ||
| 180 | + size = RAPIDJSON_ALIGN(size); | ||
| 181 | + if (chunkHead_ == 0 || chunkHead_->size + size > chunkHead_->capacity) | ||
| 182 | + if (!AddChunk(chunk_capacity_ > size ? chunk_capacity_ : size)) | ||
| 183 | + return NULL; | ||
| 184 | + | ||
| 185 | + void *buffer = reinterpret_cast<char *>(chunkHead_) + RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + chunkHead_->size; | ||
| 186 | + chunkHead_->size += size; | ||
| 187 | + return buffer; | ||
| 188 | + } | ||
| 189 | + | ||
| 190 | + //! Resizes a memory block (concept Allocator) | ||
| 191 | + void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) { | ||
| 192 | + if (originalPtr == 0) | ||
| 193 | + return Malloc(newSize); | ||
| 194 | + | ||
| 195 | + if (newSize == 0) | ||
| 196 | + return NULL; | ||
| 197 | + | ||
| 198 | + originalSize = RAPIDJSON_ALIGN(originalSize); | ||
| 199 | + newSize = RAPIDJSON_ALIGN(newSize); | ||
| 200 | + | ||
| 201 | + // Do not shrink if new size is smaller than original | ||
| 202 | + if (originalSize >= newSize) | ||
| 203 | + return originalPtr; | ||
| 204 | + | ||
| 205 | + // Simply expand it if it is the last allocation and there is sufficient space | ||
| 206 | + if (originalPtr == reinterpret_cast<char *>(chunkHead_) + RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + chunkHead_->size - originalSize) { | ||
| 207 | + size_t increment = static_cast<size_t>(newSize - originalSize); | ||
| 208 | + if (chunkHead_->size + increment <= chunkHead_->capacity) { | ||
| 209 | + chunkHead_->size += increment; | ||
| 210 | + return originalPtr; | ||
| 211 | + } | ||
| 212 | + } | ||
| 213 | + | ||
| 214 | + // Realloc process: allocate and copy memory, do not free original buffer. | ||
| 215 | + if (void* newBuffer = Malloc(newSize)) { | ||
| 216 | + if (originalSize) | ||
| 217 | + std::memcpy(newBuffer, originalPtr, originalSize); | ||
| 218 | + return newBuffer; | ||
| 219 | + } | ||
| 220 | + else | ||
| 221 | + return NULL; | ||
| 222 | + } | ||
| 223 | + | ||
| 224 | + //! Frees a memory block (concept Allocator) | ||
| 225 | + static void Free(void *ptr) { (void)ptr; } // Do nothing | ||
| 226 | + | ||
| 227 | +private: | ||
| 228 | + //! Copy constructor is not permitted. | ||
| 229 | + MemoryPoolAllocator(const MemoryPoolAllocator& rhs) /* = delete */; | ||
| 230 | + //! Copy assignment operator is not permitted. | ||
| 231 | + MemoryPoolAllocator& operator=(const MemoryPoolAllocator& rhs) /* = delete */; | ||
| 232 | + | ||
| 233 | + //! Creates a new chunk. | ||
| 234 | + /*! \param capacity Capacity of the chunk in bytes. | ||
| 235 | + \return true if success. | ||
| 236 | + */ | ||
| 237 | + bool AddChunk(size_t capacity) { | ||
| 238 | + if (!baseAllocator_) | ||
| 239 | + ownBaseAllocator_ = baseAllocator_ = RAPIDJSON_NEW(BaseAllocator)(); | ||
| 240 | + if (ChunkHeader* chunk = reinterpret_cast<ChunkHeader*>(baseAllocator_->Malloc(RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + capacity))) { | ||
| 241 | + chunk->capacity = capacity; | ||
| 242 | + chunk->size = 0; | ||
| 243 | + chunk->next = chunkHead_; | ||
| 244 | + chunkHead_ = chunk; | ||
| 245 | + return true; | ||
| 246 | + } | ||
| 247 | + else | ||
| 248 | + return false; | ||
| 249 | + } | ||
| 250 | + | ||
| 251 | + static const int kDefaultChunkCapacity = 64 * 1024; //!< Default chunk capacity. | ||
| 252 | + | ||
| 253 | + //! Chunk header for perpending to each chunk. | ||
| 254 | + /*! Chunks are stored as a singly linked list. | ||
| 255 | + */ | ||
| 256 | + struct ChunkHeader { | ||
| 257 | + size_t capacity; //!< Capacity of the chunk in bytes (excluding the header itself). | ||
| 258 | + size_t size; //!< Current size of allocated memory in bytes. | ||
| 259 | + ChunkHeader *next; //!< Next chunk in the linked list. | ||
| 260 | + }; | ||
| 261 | + | ||
| 262 | + ChunkHeader *chunkHead_; //!< Head of the chunk linked-list. Only the head chunk serves allocation. | ||
| 263 | + size_t chunk_capacity_; //!< The minimum capacity of chunk when they are allocated. | ||
| 264 | + void *userBuffer_; //!< User supplied buffer. | ||
| 265 | + BaseAllocator* baseAllocator_; //!< base allocator for allocating memory chunks. | ||
| 266 | + BaseAllocator* ownBaseAllocator_; //!< base allocator created by this object. | ||
| 267 | +}; | ||
| 268 | + | ||
| 269 | +RAPIDJSON_NAMESPACE_END | ||
| 270 | + | ||
| 271 | +#endif // RAPIDJSON_ENCODINGS_H_ |
PaperGraph/include/rapidjson/document.h
0 → 100644
This diff could not be displayed because it is too large.
PaperGraph/include/rapidjson/encodedstream.h
0 → 100644
| 1 | +// Tencent is pleased to support the open source community by making RapidJSON available. | ||
| 2 | +// | ||
| 3 | +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. | ||
| 4 | +// | ||
| 5 | +// Licensed under the MIT License (the "License"); you may not use this file except | ||
| 6 | +// in compliance with the License. You may obtain a copy of the License at | ||
| 7 | +// | ||
| 8 | +// http://opensource.org/licenses/MIT | ||
| 9 | +// | ||
| 10 | +// Unless required by applicable law or agreed to in writing, software distributed | ||
| 11 | +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR | ||
| 12 | +// CONDITIONS OF ANY KIND, either express or implied. See the License for the | ||
| 13 | +// specific language governing permissions and limitations under the License. | ||
| 14 | + | ||
| 15 | +#ifndef RAPIDJSON_ENCODEDSTREAM_H_ | ||
| 16 | +#define RAPIDJSON_ENCODEDSTREAM_H_ | ||
| 17 | + | ||
| 18 | +#include "stream.h" | ||
| 19 | +#include "memorystream.h" | ||
| 20 | + | ||
| 21 | +#ifdef __GNUC__ | ||
| 22 | +RAPIDJSON_DIAG_PUSH | ||
| 23 | +RAPIDJSON_DIAG_OFF(effc++) | ||
| 24 | +#endif | ||
| 25 | + | ||
| 26 | +#ifdef __clang__ | ||
| 27 | +RAPIDJSON_DIAG_PUSH | ||
| 28 | +RAPIDJSON_DIAG_OFF(padded) | ||
| 29 | +#endif | ||
| 30 | + | ||
| 31 | +RAPIDJSON_NAMESPACE_BEGIN | ||
| 32 | + | ||
| 33 | +//! Input byte stream wrapper with a statically bound encoding. | ||
| 34 | +/*! | ||
| 35 | + \tparam Encoding The interpretation of encoding of the stream. Either UTF8, UTF16LE, UTF16BE, UTF32LE, UTF32BE. | ||
| 36 | + \tparam InputByteStream Type of input byte stream. For example, FileReadStream. | ||
| 37 | +*/ | ||
| 38 | +template <typename Encoding, typename InputByteStream> | ||
| 39 | +class EncodedInputStream { | ||
| 40 | + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); | ||
| 41 | +public: | ||
| 42 | + typedef typename Encoding::Ch Ch; | ||
| 43 | + | ||
| 44 | + EncodedInputStream(InputByteStream& is) : is_(is) { | ||
| 45 | + current_ = Encoding::TakeBOM(is_); | ||
| 46 | + } | ||
| 47 | + | ||
| 48 | + Ch Peek() const { return current_; } | ||
| 49 | + Ch Take() { Ch c = current_; current_ = Encoding::Take(is_); return c; } | ||
| 50 | + size_t Tell() const { return is_.Tell(); } | ||
| 51 | + | ||
| 52 | + // Not implemented | ||
| 53 | + void Put(Ch) { RAPIDJSON_ASSERT(false); } | ||
| 54 | + void Flush() { RAPIDJSON_ASSERT(false); } | ||
| 55 | + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } | ||
| 56 | + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } | ||
| 57 | + | ||
| 58 | +private: | ||
| 59 | + EncodedInputStream(const EncodedInputStream&); | ||
| 60 | + EncodedInputStream& operator=(const EncodedInputStream&); | ||
| 61 | + | ||
| 62 | + InputByteStream& is_; | ||
| 63 | + Ch current_; | ||
| 64 | +}; | ||
| 65 | + | ||
| 66 | +//! Specialized for UTF8 MemoryStream. | ||
| 67 | +template <> | ||
| 68 | +class EncodedInputStream<UTF8<>, MemoryStream> { | ||
| 69 | +public: | ||
| 70 | + typedef UTF8<>::Ch Ch; | ||
| 71 | + | ||
| 72 | + EncodedInputStream(MemoryStream& is) : is_(is) { | ||
| 73 | + if (static_cast<unsigned char>(is_.Peek()) == 0xEFu) is_.Take(); | ||
| 74 | + if (static_cast<unsigned char>(is_.Peek()) == 0xBBu) is_.Take(); | ||
| 75 | + if (static_cast<unsigned char>(is_.Peek()) == 0xBFu) is_.Take(); | ||
| 76 | + } | ||
| 77 | + Ch Peek() const { return is_.Peek(); } | ||
| 78 | + Ch Take() { return is_.Take(); } | ||
| 79 | + size_t Tell() const { return is_.Tell(); } | ||
| 80 | + | ||
| 81 | + // Not implemented | ||
| 82 | + void Put(Ch) {} | ||
| 83 | + void Flush() {} | ||
| 84 | + Ch* PutBegin() { return 0; } | ||
| 85 | + size_t PutEnd(Ch*) { return 0; } | ||
| 86 | + | ||
| 87 | + MemoryStream& is_; | ||
| 88 | + | ||
| 89 | +private: | ||
| 90 | + EncodedInputStream(const EncodedInputStream&); | ||
| 91 | + EncodedInputStream& operator=(const EncodedInputStream&); | ||
| 92 | +}; | ||
| 93 | + | ||
| 94 | +//! Output byte stream wrapper with statically bound encoding. | ||
| 95 | +/*! | ||
| 96 | + \tparam Encoding The interpretation of encoding of the stream. Either UTF8, UTF16LE, UTF16BE, UTF32LE, UTF32BE. | ||
| 97 | + \tparam OutputByteStream Type of input byte stream. For example, FileWriteStream. | ||
| 98 | +*/ | ||
| 99 | +template <typename Encoding, typename OutputByteStream> | ||
| 100 | +class EncodedOutputStream { | ||
| 101 | + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); | ||
| 102 | +public: | ||
| 103 | + typedef typename Encoding::Ch Ch; | ||
| 104 | + | ||
| 105 | + EncodedOutputStream(OutputByteStream& os, bool putBOM = true) : os_(os) { | ||
| 106 | + if (putBOM) | ||
| 107 | + Encoding::PutBOM(os_); | ||
| 108 | + } | ||
| 109 | + | ||
| 110 | + void Put(Ch c) { Encoding::Put(os_, c); } | ||
| 111 | + void Flush() { os_.Flush(); } | ||
| 112 | + | ||
| 113 | + // Not implemented | ||
| 114 | + Ch Peek() const { RAPIDJSON_ASSERT(false); return 0;} | ||
| 115 | + Ch Take() { RAPIDJSON_ASSERT(false); return 0;} | ||
| 116 | + size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; } | ||
| 117 | + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } | ||
| 118 | + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } | ||
| 119 | + | ||
| 120 | +private: | ||
| 121 | + EncodedOutputStream(const EncodedOutputStream&); | ||
| 122 | + EncodedOutputStream& operator=(const EncodedOutputStream&); | ||
| 123 | + | ||
| 124 | + OutputByteStream& os_; | ||
| 125 | +}; | ||
| 126 | + | ||
| 127 | +#define RAPIDJSON_ENCODINGS_FUNC(x) UTF8<Ch>::x, UTF16LE<Ch>::x, UTF16BE<Ch>::x, UTF32LE<Ch>::x, UTF32BE<Ch>::x | ||
| 128 | + | ||
| 129 | +//! Input stream wrapper with dynamically bound encoding and automatic encoding detection. | ||
| 130 | +/*! | ||
| 131 | + \tparam CharType Type of character for reading. | ||
| 132 | + \tparam InputByteStream type of input byte stream to be wrapped. | ||
| 133 | +*/ | ||
| 134 | +template <typename CharType, typename InputByteStream> | ||
| 135 | +class AutoUTFInputStream { | ||
| 136 | + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); | ||
| 137 | +public: | ||
| 138 | + typedef CharType Ch; | ||
| 139 | + | ||
| 140 | + //! Constructor. | ||
| 141 | + /*! | ||
| 142 | + \param is input stream to be wrapped. | ||
| 143 | + \param type UTF encoding type if it is not detected from the stream. | ||
| 144 | + */ | ||
| 145 | + AutoUTFInputStream(InputByteStream& is, UTFType type = kUTF8) : is_(&is), type_(type), hasBOM_(false) { | ||
| 146 | + RAPIDJSON_ASSERT(type >= kUTF8 && type <= kUTF32BE); | ||
| 147 | + DetectType(); | ||
| 148 | + static const TakeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Take) }; | ||
| 149 | + takeFunc_ = f[type_]; | ||
| 150 | + current_ = takeFunc_(*is_); | ||
| 151 | + } | ||
| 152 | + | ||
| 153 | + UTFType GetType() const { return type_; } | ||
| 154 | + bool HasBOM() const { return hasBOM_; } | ||
| 155 | + | ||
| 156 | + Ch Peek() const { return current_; } | ||
| 157 | + Ch Take() { Ch c = current_; current_ = takeFunc_(*is_); return c; } | ||
| 158 | + size_t Tell() const { return is_->Tell(); } | ||
| 159 | + | ||
| 160 | + // Not implemented | ||
| 161 | + void Put(Ch) { RAPIDJSON_ASSERT(false); } | ||
| 162 | + void Flush() { RAPIDJSON_ASSERT(false); } | ||
| 163 | + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } | ||
| 164 | + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } | ||
| 165 | + | ||
| 166 | +private: | ||
| 167 | + AutoUTFInputStream(const AutoUTFInputStream&); | ||
| 168 | + AutoUTFInputStream& operator=(const AutoUTFInputStream&); | ||
| 169 | + | ||
| 170 | + // Detect encoding type with BOM or RFC 4627 | ||
| 171 | + void DetectType() { | ||
| 172 | + // BOM (Byte Order Mark): | ||
| 173 | + // 00 00 FE FF UTF-32BE | ||
| 174 | + // FF FE 00 00 UTF-32LE | ||
| 175 | + // FE FF UTF-16BE | ||
| 176 | + // FF FE UTF-16LE | ||
| 177 | + // EF BB BF UTF-8 | ||
| 178 | + | ||
| 179 | + const unsigned char* c = reinterpret_cast<const unsigned char *>(is_->Peek4()); | ||
| 180 | + if (!c) | ||
| 181 | + return; | ||
| 182 | + | ||
| 183 | + unsigned bom = static_cast<unsigned>(c[0] | (c[1] << 8) | (c[2] << 16) | (c[3] << 24)); | ||
| 184 | + hasBOM_ = false; | ||
| 185 | + if (bom == 0xFFFE0000) { type_ = kUTF32BE; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); is_->Take(); } | ||
| 186 | + else if (bom == 0x0000FEFF) { type_ = kUTF32LE; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); is_->Take(); } | ||
| 187 | + else if ((bom & 0xFFFF) == 0xFFFE) { type_ = kUTF16BE; hasBOM_ = true; is_->Take(); is_->Take(); } | ||
| 188 | + else if ((bom & 0xFFFF) == 0xFEFF) { type_ = kUTF16LE; hasBOM_ = true; is_->Take(); is_->Take(); } | ||
| 189 | + else if ((bom & 0xFFFFFF) == 0xBFBBEF) { type_ = kUTF8; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); } | ||
| 190 | + | ||
| 191 | + // RFC 4627: Section 3 | ||
| 192 | + // "Since the first two characters of a JSON text will always be ASCII | ||
| 193 | + // characters [RFC0020], it is possible to determine whether an octet | ||
| 194 | + // stream is UTF-8, UTF-16 (BE or LE), or UTF-32 (BE or LE) by looking | ||
| 195 | + // at the pattern of nulls in the first four octets." | ||
| 196 | + // 00 00 00 xx UTF-32BE | ||
| 197 | + // 00 xx 00 xx UTF-16BE | ||
| 198 | + // xx 00 00 00 UTF-32LE | ||
| 199 | + // xx 00 xx 00 UTF-16LE | ||
| 200 | + // xx xx xx xx UTF-8 | ||
| 201 | + | ||
| 202 | + if (!hasBOM_) { | ||
| 203 | + int pattern = (c[0] ? 1 : 0) | (c[1] ? 2 : 0) | (c[2] ? 4 : 0) | (c[3] ? 8 : 0); | ||
| 204 | + switch (pattern) { | ||
| 205 | + case 0x08: type_ = kUTF32BE; break; | ||
| 206 | + case 0x0A: type_ = kUTF16BE; break; | ||
| 207 | + case 0x01: type_ = kUTF32LE; break; | ||
| 208 | + case 0x05: type_ = kUTF16LE; break; | ||
| 209 | + case 0x0F: type_ = kUTF8; break; | ||
| 210 | + default: break; // Use type defined by user. | ||
| 211 | + } | ||
| 212 | + } | ||
| 213 | + | ||
| 214 | + // Runtime check whether the size of character type is sufficient. It only perform checks with assertion. | ||
| 215 | + if (type_ == kUTF16LE || type_ == kUTF16BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 2); | ||
| 216 | + if (type_ == kUTF32LE || type_ == kUTF32BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 4); | ||
| 217 | + } | ||
| 218 | + | ||
| 219 | + typedef Ch (*TakeFunc)(InputByteStream& is); | ||
| 220 | + InputByteStream* is_; | ||
| 221 | + UTFType type_; | ||
| 222 | + Ch current_; | ||
| 223 | + TakeFunc takeFunc_; | ||
| 224 | + bool hasBOM_; | ||
| 225 | +}; | ||
| 226 | + | ||
| 227 | +//! Output stream wrapper with dynamically bound encoding and automatic encoding detection. | ||
| 228 | +/*! | ||
| 229 | + \tparam CharType Type of character for writing. | ||
| 230 | + \tparam OutputByteStream type of output byte stream to be wrapped. | ||
| 231 | +*/ | ||
| 232 | +template <typename CharType, typename OutputByteStream> | ||
| 233 | +class AutoUTFOutputStream { | ||
| 234 | + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); | ||
| 235 | +public: | ||
| 236 | + typedef CharType Ch; | ||
| 237 | + | ||
| 238 | + //! Constructor. | ||
| 239 | + /*! | ||
| 240 | + \param os output stream to be wrapped. | ||
| 241 | + \param type UTF encoding type. | ||
| 242 | + \param putBOM Whether to write BOM at the beginning of the stream. | ||
| 243 | + */ | ||
| 244 | + AutoUTFOutputStream(OutputByteStream& os, UTFType type, bool putBOM) : os_(&os), type_(type) { | ||
| 245 | + RAPIDJSON_ASSERT(type >= kUTF8 && type <= kUTF32BE); | ||
| 246 | + | ||
| 247 | + // Runtime check whether the size of character type is sufficient. It only perform checks with assertion. | ||
| 248 | + if (type_ == kUTF16LE || type_ == kUTF16BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 2); | ||
| 249 | + if (type_ == kUTF32LE || type_ == kUTF32BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 4); | ||
| 250 | + | ||
| 251 | + static const PutFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Put) }; | ||
| 252 | + putFunc_ = f[type_]; | ||
| 253 | + | ||
| 254 | + if (putBOM) | ||
| 255 | + PutBOM(); | ||
| 256 | + } | ||
| 257 | + | ||
| 258 | + UTFType GetType() const { return type_; } | ||
| 259 | + | ||
| 260 | + void Put(Ch c) { putFunc_(*os_, c); } | ||
| 261 | + void Flush() { os_->Flush(); } | ||
| 262 | + | ||
| 263 | + // Not implemented | ||
| 264 | + Ch Peek() const { RAPIDJSON_ASSERT(false); return 0;} | ||
| 265 | + Ch Take() { RAPIDJSON_ASSERT(false); return 0;} | ||
| 266 | + size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; } | ||
| 267 | + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } | ||
| 268 | + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } | ||
| 269 | + | ||
| 270 | +private: | ||
| 271 | + AutoUTFOutputStream(const AutoUTFOutputStream&); | ||
| 272 | + AutoUTFOutputStream& operator=(const AutoUTFOutputStream&); | ||
| 273 | + | ||
| 274 | + void PutBOM() { | ||
| 275 | + typedef void (*PutBOMFunc)(OutputByteStream&); | ||
| 276 | + static const PutBOMFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(PutBOM) }; | ||
| 277 | + f[type_](*os_); | ||
| 278 | + } | ||
| 279 | + | ||
| 280 | + typedef void (*PutFunc)(OutputByteStream&, Ch); | ||
| 281 | + | ||
| 282 | + OutputByteStream* os_; | ||
| 283 | + UTFType type_; | ||
| 284 | + PutFunc putFunc_; | ||
| 285 | +}; | ||
| 286 | + | ||
| 287 | +#undef RAPIDJSON_ENCODINGS_FUNC | ||
| 288 | + | ||
| 289 | +RAPIDJSON_NAMESPACE_END | ||
| 290 | + | ||
| 291 | +#ifdef __clang__ | ||
| 292 | +RAPIDJSON_DIAG_POP | ||
| 293 | +#endif | ||
| 294 | + | ||
| 295 | +#ifdef __GNUC__ | ||
| 296 | +RAPIDJSON_DIAG_POP | ||
| 297 | +#endif | ||
| 298 | + | ||
| 299 | +#endif // RAPIDJSON_FILESTREAM_H_ |
PaperGraph/include/rapidjson/encodings.h
0 → 100644
| 1 | +// Tencent is pleased to support the open source community by making RapidJSON available. | ||
| 2 | +// | ||
| 3 | +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. | ||
| 4 | +// | ||
| 5 | +// Licensed under the MIT License (the "License"); you may not use this file except | ||
| 6 | +// in compliance with the License. You may obtain a copy of the License at | ||
| 7 | +// | ||
| 8 | +// http://opensource.org/licenses/MIT | ||
| 9 | +// | ||
| 10 | +// Unless required by applicable law or agreed to in writing, software distributed | ||
| 11 | +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR | ||
| 12 | +// CONDITIONS OF ANY KIND, either express or implied. See the License for the | ||
| 13 | +// specific language governing permissions and limitations under the License. | ||
| 14 | + | ||
| 15 | +#ifndef RAPIDJSON_ENCODINGS_H_ | ||
| 16 | +#define RAPIDJSON_ENCODINGS_H_ | ||
| 17 | + | ||
| 18 | +#include "rapidjson.h" | ||
| 19 | + | ||
| 20 | +#ifdef _MSC_VER | ||
| 21 | +RAPIDJSON_DIAG_PUSH | ||
| 22 | +RAPIDJSON_DIAG_OFF(4244) // conversion from 'type1' to 'type2', possible loss of data | ||
| 23 | +RAPIDJSON_DIAG_OFF(4702) // unreachable code | ||
| 24 | +#elif defined(__GNUC__) | ||
| 25 | +RAPIDJSON_DIAG_PUSH | ||
| 26 | +RAPIDJSON_DIAG_OFF(effc++) | ||
| 27 | +RAPIDJSON_DIAG_OFF(overflow) | ||
| 28 | +#endif | ||
| 29 | + | ||
| 30 | +RAPIDJSON_NAMESPACE_BEGIN | ||
| 31 | + | ||
| 32 | +/////////////////////////////////////////////////////////////////////////////// | ||
| 33 | +// Encoding | ||
| 34 | + | ||
| 35 | +/*! \class rapidjson::Encoding | ||
| 36 | + \brief Concept for encoding of Unicode characters. | ||
| 37 | + | ||
| 38 | +\code | ||
| 39 | +concept Encoding { | ||
| 40 | + typename Ch; //! Type of character. A "character" is actually a code unit in unicode's definition. | ||
| 41 | + | ||
| 42 | + enum { supportUnicode = 1 }; // or 0 if not supporting unicode | ||
| 43 | + | ||
| 44 | + //! \brief Encode a Unicode codepoint to an output stream. | ||
| 45 | + //! \param os Output stream. | ||
| 46 | + //! \param codepoint An unicode codepoint, ranging from 0x0 to 0x10FFFF inclusively. | ||
| 47 | + template<typename OutputStream> | ||
| 48 | + static void Encode(OutputStream& os, unsigned codepoint); | ||
| 49 | + | ||
| 50 | + //! \brief Decode a Unicode codepoint from an input stream. | ||
| 51 | + //! \param is Input stream. | ||
| 52 | + //! \param codepoint Output of the unicode codepoint. | ||
| 53 | + //! \return true if a valid codepoint can be decoded from the stream. | ||
| 54 | + template <typename InputStream> | ||
| 55 | + static bool Decode(InputStream& is, unsigned* codepoint); | ||
| 56 | + | ||
| 57 | + //! \brief Validate one Unicode codepoint from an encoded stream. | ||
| 58 | + //! \param is Input stream to obtain codepoint. | ||
| 59 | + //! \param os Output for copying one codepoint. | ||
| 60 | + //! \return true if it is valid. | ||
| 61 | + //! \note This function just validating and copying the codepoint without actually decode it. | ||
| 62 | + template <typename InputStream, typename OutputStream> | ||
| 63 | + static bool Validate(InputStream& is, OutputStream& os); | ||
| 64 | + | ||
| 65 | + // The following functions are deal with byte streams. | ||
| 66 | + | ||
| 67 | + //! Take a character from input byte stream, skip BOM if exist. | ||
| 68 | + template <typename InputByteStream> | ||
| 69 | + static CharType TakeBOM(InputByteStream& is); | ||
| 70 | + | ||
| 71 | + //! Take a character from input byte stream. | ||
| 72 | + template <typename InputByteStream> | ||
| 73 | + static Ch Take(InputByteStream& is); | ||
| 74 | + | ||
| 75 | + //! Put BOM to output byte stream. | ||
| 76 | + template <typename OutputByteStream> | ||
| 77 | + static void PutBOM(OutputByteStream& os); | ||
| 78 | + | ||
| 79 | + //! Put a character to output byte stream. | ||
| 80 | + template <typename OutputByteStream> | ||
| 81 | + static void Put(OutputByteStream& os, Ch c); | ||
| 82 | +}; | ||
| 83 | +\endcode | ||
| 84 | +*/ | ||
| 85 | + | ||
| 86 | +/////////////////////////////////////////////////////////////////////////////// | ||
| 87 | +// UTF8 | ||
| 88 | + | ||
| 89 | +//! UTF-8 encoding. | ||
| 90 | +/*! http://en.wikipedia.org/wiki/UTF-8 | ||
| 91 | + http://tools.ietf.org/html/rfc3629 | ||
| 92 | + \tparam CharType Code unit for storing 8-bit UTF-8 data. Default is char. | ||
| 93 | + \note implements Encoding concept | ||
| 94 | +*/ | ||
| 95 | +template<typename CharType = char> | ||
| 96 | +struct UTF8 { | ||
| 97 | + typedef CharType Ch; | ||
| 98 | + | ||
| 99 | + enum { supportUnicode = 1 }; | ||
| 100 | + | ||
| 101 | + template<typename OutputStream> | ||
| 102 | + static void Encode(OutputStream& os, unsigned codepoint) { | ||
| 103 | + if (codepoint <= 0x7F) | ||
| 104 | + os.Put(static_cast<Ch>(codepoint & 0xFF)); | ||
| 105 | + else if (codepoint <= 0x7FF) { | ||
| 106 | + os.Put(static_cast<Ch>(0xC0 | ((codepoint >> 6) & 0xFF))); | ||
| 107 | + os.Put(static_cast<Ch>(0x80 | ((codepoint & 0x3F)))); | ||
| 108 | + } | ||
| 109 | + else if (codepoint <= 0xFFFF) { | ||
| 110 | + os.Put(static_cast<Ch>(0xE0 | ((codepoint >> 12) & 0xFF))); | ||
| 111 | + os.Put(static_cast<Ch>(0x80 | ((codepoint >> 6) & 0x3F))); | ||
| 112 | + os.Put(static_cast<Ch>(0x80 | (codepoint & 0x3F))); | ||
| 113 | + } | ||
| 114 | + else { | ||
| 115 | + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); | ||
| 116 | + os.Put(static_cast<Ch>(0xF0 | ((codepoint >> 18) & 0xFF))); | ||
| 117 | + os.Put(static_cast<Ch>(0x80 | ((codepoint >> 12) & 0x3F))); | ||
| 118 | + os.Put(static_cast<Ch>(0x80 | ((codepoint >> 6) & 0x3F))); | ||
| 119 | + os.Put(static_cast<Ch>(0x80 | (codepoint & 0x3F))); | ||
| 120 | + } | ||
| 121 | + } | ||
| 122 | + | ||
| 123 | + template<typename OutputStream> | ||
| 124 | + static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { | ||
| 125 | + if (codepoint <= 0x7F) | ||
| 126 | + PutUnsafe(os, static_cast<Ch>(codepoint & 0xFF)); | ||
| 127 | + else if (codepoint <= 0x7FF) { | ||
| 128 | + PutUnsafe(os, static_cast<Ch>(0xC0 | ((codepoint >> 6) & 0xFF))); | ||
| 129 | + PutUnsafe(os, static_cast<Ch>(0x80 | ((codepoint & 0x3F)))); | ||
| 130 | + } | ||
| 131 | + else if (codepoint <= 0xFFFF) { | ||
| 132 | + PutUnsafe(os, static_cast<Ch>(0xE0 | ((codepoint >> 12) & 0xFF))); | ||
| 133 | + PutUnsafe(os, static_cast<Ch>(0x80 | ((codepoint >> 6) & 0x3F))); | ||
| 134 | + PutUnsafe(os, static_cast<Ch>(0x80 | (codepoint & 0x3F))); | ||
| 135 | + } | ||
| 136 | + else { | ||
| 137 | + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); | ||
| 138 | + PutUnsafe(os, static_cast<Ch>(0xF0 | ((codepoint >> 18) & 0xFF))); | ||
| 139 | + PutUnsafe(os, static_cast<Ch>(0x80 | ((codepoint >> 12) & 0x3F))); | ||
| 140 | + PutUnsafe(os, static_cast<Ch>(0x80 | ((codepoint >> 6) & 0x3F))); | ||
| 141 | + PutUnsafe(os, static_cast<Ch>(0x80 | (codepoint & 0x3F))); | ||
| 142 | + } | ||
| 143 | + } | ||
| 144 | + | ||
| 145 | + template <typename InputStream> | ||
| 146 | + static bool Decode(InputStream& is, unsigned* codepoint) { | ||
| 147 | +#define COPY() c = is.Take(); *codepoint = (*codepoint << 6) | (static_cast<unsigned char>(c) & 0x3Fu) | ||
| 148 | +#define TRANS(mask) result &= ((GetRange(static_cast<unsigned char>(c)) & mask) != 0) | ||
| 149 | +#define TAIL() COPY(); TRANS(0x70) | ||
| 150 | + typename InputStream::Ch c = is.Take(); | ||
| 151 | + if (!(c & 0x80)) { | ||
| 152 | + *codepoint = static_cast<unsigned char>(c); | ||
| 153 | + return true; | ||
| 154 | + } | ||
| 155 | + | ||
| 156 | + unsigned char type = GetRange(static_cast<unsigned char>(c)); | ||
| 157 | + if (type >= 32) { | ||
| 158 | + *codepoint = 0; | ||
| 159 | + } else { | ||
| 160 | + *codepoint = (0xFFu >> type) & static_cast<unsigned char>(c); | ||
| 161 | + } | ||
| 162 | + bool result = true; | ||
| 163 | + switch (type) { | ||
| 164 | + case 2: TAIL(); return result; | ||
| 165 | + case 3: TAIL(); TAIL(); return result; | ||
| 166 | + case 4: COPY(); TRANS(0x50); TAIL(); return result; | ||
| 167 | + case 5: COPY(); TRANS(0x10); TAIL(); TAIL(); return result; | ||
| 168 | + case 6: TAIL(); TAIL(); TAIL(); return result; | ||
| 169 | + case 10: COPY(); TRANS(0x20); TAIL(); return result; | ||
| 170 | + case 11: COPY(); TRANS(0x60); TAIL(); TAIL(); return result; | ||
| 171 | + default: return false; | ||
| 172 | + } | ||
| 173 | +#undef COPY | ||
| 174 | +#undef TRANS | ||
| 175 | +#undef TAIL | ||
| 176 | + } | ||
| 177 | + | ||
| 178 | + template <typename InputStream, typename OutputStream> | ||
| 179 | + static bool Validate(InputStream& is, OutputStream& os) { | ||
| 180 | +#define COPY() os.Put(c = is.Take()) | ||
| 181 | +#define TRANS(mask) result &= ((GetRange(static_cast<unsigned char>(c)) & mask) != 0) | ||
| 182 | +#define TAIL() COPY(); TRANS(0x70) | ||
| 183 | + Ch c; | ||
| 184 | + COPY(); | ||
| 185 | + if (!(c & 0x80)) | ||
| 186 | + return true; | ||
| 187 | + | ||
| 188 | + bool result = true; | ||
| 189 | + switch (GetRange(static_cast<unsigned char>(c))) { | ||
| 190 | + case 2: TAIL(); return result; | ||
| 191 | + case 3: TAIL(); TAIL(); return result; | ||
| 192 | + case 4: COPY(); TRANS(0x50); TAIL(); return result; | ||
| 193 | + case 5: COPY(); TRANS(0x10); TAIL(); TAIL(); return result; | ||
| 194 | + case 6: TAIL(); TAIL(); TAIL(); return result; | ||
| 195 | + case 10: COPY(); TRANS(0x20); TAIL(); return result; | ||
| 196 | + case 11: COPY(); TRANS(0x60); TAIL(); TAIL(); return result; | ||
| 197 | + default: return false; | ||
| 198 | + } | ||
| 199 | +#undef COPY | ||
| 200 | +#undef TRANS | ||
| 201 | +#undef TAIL | ||
| 202 | + } | ||
| 203 | + | ||
| 204 | + static unsigned char GetRange(unsigned char c) { | ||
| 205 | + // Referring to DFA of http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ | ||
| 206 | + // With new mapping 1 -> 0x10, 7 -> 0x20, 9 -> 0x40, such that AND operation can test multiple types. | ||
| 207 | + static const unsigned char type[] = { | ||
| 208 | + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, | ||
| 209 | + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, | ||
| 210 | + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, | ||
| 211 | + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, | ||
| 212 | + 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, | ||
| 213 | + 0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40, | ||
| 214 | + 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, | ||
| 215 | + 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, | ||
| 216 | + 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, | ||
| 217 | + 10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8, | ||
| 218 | + }; | ||
| 219 | + return type[c]; | ||
| 220 | + } | ||
| 221 | + | ||
| 222 | + template <typename InputByteStream> | ||
| 223 | + static CharType TakeBOM(InputByteStream& is) { | ||
| 224 | + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); | ||
| 225 | + typename InputByteStream::Ch c = Take(is); | ||
| 226 | + if (static_cast<unsigned char>(c) != 0xEFu) return c; | ||
| 227 | + c = is.Take(); | ||
| 228 | + if (static_cast<unsigned char>(c) != 0xBBu) return c; | ||
| 229 | + c = is.Take(); | ||
| 230 | + if (static_cast<unsigned char>(c) != 0xBFu) return c; | ||
| 231 | + c = is.Take(); | ||
| 232 | + return c; | ||
| 233 | + } | ||
| 234 | + | ||
| 235 | + template <typename InputByteStream> | ||
| 236 | + static Ch Take(InputByteStream& is) { | ||
| 237 | + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); | ||
| 238 | + return static_cast<Ch>(is.Take()); | ||
| 239 | + } | ||
| 240 | + | ||
| 241 | + template <typename OutputByteStream> | ||
| 242 | + static void PutBOM(OutputByteStream& os) { | ||
| 243 | + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); | ||
| 244 | + os.Put(static_cast<typename OutputByteStream::Ch>(0xEFu)); | ||
| 245 | + os.Put(static_cast<typename OutputByteStream::Ch>(0xBBu)); | ||
| 246 | + os.Put(static_cast<typename OutputByteStream::Ch>(0xBFu)); | ||
| 247 | + } | ||
| 248 | + | ||
| 249 | + template <typename OutputByteStream> | ||
| 250 | + static void Put(OutputByteStream& os, Ch c) { | ||
| 251 | + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); | ||
| 252 | + os.Put(static_cast<typename OutputByteStream::Ch>(c)); | ||
| 253 | + } | ||
| 254 | +}; | ||
| 255 | + | ||
| 256 | +/////////////////////////////////////////////////////////////////////////////// | ||
| 257 | +// UTF16 | ||
| 258 | + | ||
| 259 | +//! UTF-16 encoding. | ||
| 260 | +/*! http://en.wikipedia.org/wiki/UTF-16 | ||
| 261 | + http://tools.ietf.org/html/rfc2781 | ||
| 262 | + \tparam CharType Type for storing 16-bit UTF-16 data. Default is wchar_t. C++11 may use char16_t instead. | ||
| 263 | + \note implements Encoding concept | ||
| 264 | + | ||
| 265 | + \note For in-memory access, no need to concern endianness. The code units and code points are represented by CPU's endianness. | ||
| 266 | + For streaming, use UTF16LE and UTF16BE, which handle endianness. | ||
| 267 | +*/ | ||
| 268 | +template<typename CharType = wchar_t> | ||
| 269 | +struct UTF16 { | ||
| 270 | + typedef CharType Ch; | ||
| 271 | + RAPIDJSON_STATIC_ASSERT(sizeof(Ch) >= 2); | ||
| 272 | + | ||
| 273 | + enum { supportUnicode = 1 }; | ||
| 274 | + | ||
| 275 | + template<typename OutputStream> | ||
| 276 | + static void Encode(OutputStream& os, unsigned codepoint) { | ||
| 277 | + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2); | ||
| 278 | + if (codepoint <= 0xFFFF) { | ||
| 279 | + RAPIDJSON_ASSERT(codepoint < 0xD800 || codepoint > 0xDFFF); // Code point itself cannot be surrogate pair | ||
| 280 | + os.Put(static_cast<typename OutputStream::Ch>(codepoint)); | ||
| 281 | + } | ||
| 282 | + else { | ||
| 283 | + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); | ||
| 284 | + unsigned v = codepoint - 0x10000; | ||
| 285 | + os.Put(static_cast<typename OutputStream::Ch>((v >> 10) | 0xD800)); | ||
| 286 | + os.Put(static_cast<typename OutputStream::Ch>((v & 0x3FF) | 0xDC00)); | ||
| 287 | + } | ||
| 288 | + } | ||
| 289 | + | ||
| 290 | + | ||
| 291 | + template<typename OutputStream> | ||
| 292 | + static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { | ||
| 293 | + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2); | ||
| 294 | + if (codepoint <= 0xFFFF) { | ||
| 295 | + RAPIDJSON_ASSERT(codepoint < 0xD800 || codepoint > 0xDFFF); // Code point itself cannot be surrogate pair | ||
| 296 | + PutUnsafe(os, static_cast<typename OutputStream::Ch>(codepoint)); | ||
| 297 | + } | ||
| 298 | + else { | ||
| 299 | + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); | ||
| 300 | + unsigned v = codepoint - 0x10000; | ||
| 301 | + PutUnsafe(os, static_cast<typename OutputStream::Ch>((v >> 10) | 0xD800)); | ||
| 302 | + PutUnsafe(os, static_cast<typename OutputStream::Ch>((v & 0x3FF) | 0xDC00)); | ||
| 303 | + } | ||
| 304 | + } | ||
| 305 | + | ||
| 306 | + template <typename InputStream> | ||
| 307 | + static bool Decode(InputStream& is, unsigned* codepoint) { | ||
| 308 | + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 2); | ||
| 309 | + typename InputStream::Ch c = is.Take(); | ||
| 310 | + if (c < 0xD800 || c > 0xDFFF) { | ||
| 311 | + *codepoint = static_cast<unsigned>(c); | ||
| 312 | + return true; | ||
| 313 | + } | ||
| 314 | + else if (c <= 0xDBFF) { | ||
| 315 | + *codepoint = (static_cast<unsigned>(c) & 0x3FF) << 10; | ||
| 316 | + c = is.Take(); | ||
| 317 | + *codepoint |= (static_cast<unsigned>(c) & 0x3FF); | ||
| 318 | + *codepoint += 0x10000; | ||
| 319 | + return c >= 0xDC00 && c <= 0xDFFF; | ||
| 320 | + } | ||
| 321 | + return false; | ||
| 322 | + } | ||
| 323 | + | ||
| 324 | + template <typename InputStream, typename OutputStream> | ||
| 325 | + static bool Validate(InputStream& is, OutputStream& os) { | ||
| 326 | + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 2); | ||
| 327 | + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2); | ||
| 328 | + typename InputStream::Ch c; | ||
| 329 | + os.Put(static_cast<typename OutputStream::Ch>(c = is.Take())); | ||
| 330 | + if (c < 0xD800 || c > 0xDFFF) | ||
| 331 | + return true; | ||
| 332 | + else if (c <= 0xDBFF) { | ||
| 333 | + os.Put(c = is.Take()); | ||
| 334 | + return c >= 0xDC00 && c <= 0xDFFF; | ||
| 335 | + } | ||
| 336 | + return false; | ||
| 337 | + } | ||
| 338 | +}; | ||
| 339 | + | ||
| 340 | +//! UTF-16 little endian encoding. | ||
| 341 | +template<typename CharType = wchar_t> | ||
| 342 | +struct UTF16LE : UTF16<CharType> { | ||
| 343 | + template <typename InputByteStream> | ||
| 344 | + static CharType TakeBOM(InputByteStream& is) { | ||
| 345 | + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); | ||
| 346 | + CharType c = Take(is); | ||
| 347 | + return static_cast<uint16_t>(c) == 0xFEFFu ? Take(is) : c; | ||
| 348 | + } | ||
| 349 | + | ||
| 350 | + template <typename InputByteStream> | ||
| 351 | + static CharType Take(InputByteStream& is) { | ||
| 352 | + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); | ||
| 353 | + unsigned c = static_cast<uint8_t>(is.Take()); | ||
| 354 | + c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 8; | ||
| 355 | + return static_cast<CharType>(c); | ||
| 356 | + } | ||
| 357 | + | ||
| 358 | + template <typename OutputByteStream> | ||
| 359 | + static void PutBOM(OutputByteStream& os) { | ||
| 360 | + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); | ||
| 361 | + os.Put(static_cast<typename OutputByteStream::Ch>(0xFFu)); | ||
| 362 | + os.Put(static_cast<typename OutputByteStream::Ch>(0xFEu)); | ||
| 363 | + } | ||
| 364 | + | ||
| 365 | + template <typename OutputByteStream> | ||
| 366 | + static void Put(OutputByteStream& os, CharType c) { | ||
| 367 | + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); | ||
| 368 | + os.Put(static_cast<typename OutputByteStream::Ch>(static_cast<unsigned>(c) & 0xFFu)); | ||
| 369 | + os.Put(static_cast<typename OutputByteStream::Ch>((static_cast<unsigned>(c) >> 8) & 0xFFu)); | ||
| 370 | + } | ||
| 371 | +}; | ||
| 372 | + | ||
| 373 | +//! UTF-16 big endian encoding. | ||
| 374 | +template<typename CharType = wchar_t> | ||
| 375 | +struct UTF16BE : UTF16<CharType> { | ||
| 376 | + template <typename InputByteStream> | ||
| 377 | + static CharType TakeBOM(InputByteStream& is) { | ||
| 378 | + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); | ||
| 379 | + CharType c = Take(is); | ||
| 380 | + return static_cast<uint16_t>(c) == 0xFEFFu ? Take(is) : c; | ||
| 381 | + } | ||
| 382 | + | ||
| 383 | + template <typename InputByteStream> | ||
| 384 | + static CharType Take(InputByteStream& is) { | ||
| 385 | + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); | ||
| 386 | + unsigned c = static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 8; | ||
| 387 | + c |= static_cast<uint8_t>(is.Take()); | ||
| 388 | + return static_cast<CharType>(c); | ||
| 389 | + } | ||
| 390 | + | ||
| 391 | + template <typename OutputByteStream> | ||
| 392 | + static void PutBOM(OutputByteStream& os) { | ||
| 393 | + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); | ||
| 394 | + os.Put(static_cast<typename OutputByteStream::Ch>(0xFEu)); | ||
| 395 | + os.Put(static_cast<typename OutputByteStream::Ch>(0xFFu)); | ||
| 396 | + } | ||
| 397 | + | ||
| 398 | + template <typename OutputByteStream> | ||
| 399 | + static void Put(OutputByteStream& os, CharType c) { | ||
| 400 | + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); | ||
| 401 | + os.Put(static_cast<typename OutputByteStream::Ch>((static_cast<unsigned>(c) >> 8) & 0xFFu)); | ||
| 402 | + os.Put(static_cast<typename OutputByteStream::Ch>(static_cast<unsigned>(c) & 0xFFu)); | ||
| 403 | + } | ||
| 404 | +}; | ||
| 405 | + | ||
| 406 | +/////////////////////////////////////////////////////////////////////////////// | ||
| 407 | +// UTF32 | ||
| 408 | + | ||
| 409 | +//! UTF-32 encoding. | ||
| 410 | +/*! http://en.wikipedia.org/wiki/UTF-32 | ||
| 411 | + \tparam CharType Type for storing 32-bit UTF-32 data. Default is unsigned. C++11 may use char32_t instead. | ||
| 412 | + \note implements Encoding concept | ||
| 413 | + | ||
| 414 | + \note For in-memory access, no need to concern endianness. The code units and code points are represented by CPU's endianness. | ||
| 415 | + For streaming, use UTF32LE and UTF32BE, which handle endianness. | ||
| 416 | +*/ | ||
| 417 | +template<typename CharType = unsigned> | ||
| 418 | +struct UTF32 { | ||
| 419 | + typedef CharType Ch; | ||
| 420 | + RAPIDJSON_STATIC_ASSERT(sizeof(Ch) >= 4); | ||
| 421 | + | ||
| 422 | + enum { supportUnicode = 1 }; | ||
| 423 | + | ||
| 424 | + template<typename OutputStream> | ||
| 425 | + static void Encode(OutputStream& os, unsigned codepoint) { | ||
| 426 | + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 4); | ||
| 427 | + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); | ||
| 428 | + os.Put(codepoint); | ||
| 429 | + } | ||
| 430 | + | ||
| 431 | + template<typename OutputStream> | ||
| 432 | + static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { | ||
| 433 | + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 4); | ||
| 434 | + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); | ||
| 435 | + PutUnsafe(os, codepoint); | ||
| 436 | + } | ||
| 437 | + | ||
| 438 | + template <typename InputStream> | ||
| 439 | + static bool Decode(InputStream& is, unsigned* codepoint) { | ||
| 440 | + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 4); | ||
| 441 | + Ch c = is.Take(); | ||
| 442 | + *codepoint = c; | ||
| 443 | + return c <= 0x10FFFF; | ||
| 444 | + } | ||
| 445 | + | ||
| 446 | + template <typename InputStream, typename OutputStream> | ||
| 447 | + static bool Validate(InputStream& is, OutputStream& os) { | ||
| 448 | + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 4); | ||
| 449 | + Ch c; | ||
| 450 | + os.Put(c = is.Take()); | ||
| 451 | + return c <= 0x10FFFF; | ||
| 452 | + } | ||
| 453 | +}; | ||
| 454 | + | ||
| 455 | +//! UTF-32 little endian enocoding. | ||
| 456 | +template<typename CharType = unsigned> | ||
| 457 | +struct UTF32LE : UTF32<CharType> { | ||
| 458 | + template <typename InputByteStream> | ||
| 459 | + static CharType TakeBOM(InputByteStream& is) { | ||
| 460 | + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); | ||
| 461 | + CharType c = Take(is); | ||
| 462 | + return static_cast<uint32_t>(c) == 0x0000FEFFu ? Take(is) : c; | ||
| 463 | + } | ||
| 464 | + | ||
| 465 | + template <typename InputByteStream> | ||
| 466 | + static CharType Take(InputByteStream& is) { | ||
| 467 | + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); | ||
| 468 | + unsigned c = static_cast<uint8_t>(is.Take()); | ||
| 469 | + c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 8; | ||
| 470 | + c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 16; | ||
| 471 | + c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 24; | ||
| 472 | + return static_cast<CharType>(c); | ||
| 473 | + } | ||
| 474 | + | ||
| 475 | + template <typename OutputByteStream> | ||
| 476 | + static void PutBOM(OutputByteStream& os) { | ||
| 477 | + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); | ||
| 478 | + os.Put(static_cast<typename OutputByteStream::Ch>(0xFFu)); | ||
| 479 | + os.Put(static_cast<typename OutputByteStream::Ch>(0xFEu)); | ||
| 480 | + os.Put(static_cast<typename OutputByteStream::Ch>(0x00u)); | ||
| 481 | + os.Put(static_cast<typename OutputByteStream::Ch>(0x00u)); | ||
| 482 | + } | ||
| 483 | + | ||
| 484 | + template <typename OutputByteStream> | ||
| 485 | + static void Put(OutputByteStream& os, CharType c) { | ||
| 486 | + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); | ||
| 487 | + os.Put(static_cast<typename OutputByteStream::Ch>(c & 0xFFu)); | ||
| 488 | + os.Put(static_cast<typename OutputByteStream::Ch>((c >> 8) & 0xFFu)); | ||
| 489 | + os.Put(static_cast<typename OutputByteStream::Ch>((c >> 16) & 0xFFu)); | ||
| 490 | + os.Put(static_cast<typename OutputByteStream::Ch>((c >> 24) & 0xFFu)); | ||
| 491 | + } | ||
| 492 | +}; | ||
| 493 | + | ||
| 494 | +//! UTF-32 big endian encoding. | ||
| 495 | +template<typename CharType = unsigned> | ||
| 496 | +struct UTF32BE : UTF32<CharType> { | ||
| 497 | + template <typename InputByteStream> | ||
| 498 | + static CharType TakeBOM(InputByteStream& is) { | ||
| 499 | + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); | ||
| 500 | + CharType c = Take(is); | ||
| 501 | + return static_cast<uint32_t>(c) == 0x0000FEFFu ? Take(is) : c; | ||
| 502 | + } | ||
| 503 | + | ||
| 504 | + template <typename InputByteStream> | ||
| 505 | + static CharType Take(InputByteStream& is) { | ||
| 506 | + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); | ||
| 507 | + unsigned c = static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 24; | ||
| 508 | + c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 16; | ||
| 509 | + c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 8; | ||
| 510 | + c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take())); | ||
| 511 | + return static_cast<CharType>(c); | ||
| 512 | + } | ||
| 513 | + | ||
| 514 | + template <typename OutputByteStream> | ||
| 515 | + static void PutBOM(OutputByteStream& os) { | ||
| 516 | + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); | ||
| 517 | + os.Put(static_cast<typename OutputByteStream::Ch>(0x00u)); | ||
| 518 | + os.Put(static_cast<typename OutputByteStream::Ch>(0x00u)); | ||
| 519 | + os.Put(static_cast<typename OutputByteStream::Ch>(0xFEu)); | ||
| 520 | + os.Put(static_cast<typename OutputByteStream::Ch>(0xFFu)); | ||
| 521 | + } | ||
| 522 | + | ||
| 523 | + template <typename OutputByteStream> | ||
| 524 | + static void Put(OutputByteStream& os, CharType c) { | ||
| 525 | + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); | ||
| 526 | + os.Put(static_cast<typename OutputByteStream::Ch>((c >> 24) & 0xFFu)); | ||
| 527 | + os.Put(static_cast<typename OutputByteStream::Ch>((c >> 16) & 0xFFu)); | ||
| 528 | + os.Put(static_cast<typename OutputByteStream::Ch>((c >> 8) & 0xFFu)); | ||
| 529 | + os.Put(static_cast<typename OutputByteStream::Ch>(c & 0xFFu)); | ||
| 530 | + } | ||
| 531 | +}; | ||
| 532 | + | ||
| 533 | +/////////////////////////////////////////////////////////////////////////////// | ||
| 534 | +// ASCII | ||
| 535 | + | ||
| 536 | +//! ASCII encoding. | ||
| 537 | +/*! http://en.wikipedia.org/wiki/ASCII | ||
| 538 | + \tparam CharType Code unit for storing 7-bit ASCII data. Default is char. | ||
| 539 | + \note implements Encoding concept | ||
| 540 | +*/ | ||
| 541 | +template<typename CharType = char> | ||
| 542 | +struct ASCII { | ||
| 543 | + typedef CharType Ch; | ||
| 544 | + | ||
| 545 | + enum { supportUnicode = 0 }; | ||
| 546 | + | ||
| 547 | + template<typename OutputStream> | ||
| 548 | + static void Encode(OutputStream& os, unsigned codepoint) { | ||
| 549 | + RAPIDJSON_ASSERT(codepoint <= 0x7F); | ||
| 550 | + os.Put(static_cast<Ch>(codepoint & 0xFF)); | ||
| 551 | + } | ||
| 552 | + | ||
| 553 | + template<typename OutputStream> | ||
| 554 | + static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { | ||
| 555 | + RAPIDJSON_ASSERT(codepoint <= 0x7F); | ||
| 556 | + PutUnsafe(os, static_cast<Ch>(codepoint & 0xFF)); | ||
| 557 | + } | ||
| 558 | + | ||
| 559 | + template <typename InputStream> | ||
| 560 | + static bool Decode(InputStream& is, unsigned* codepoint) { | ||
| 561 | + uint8_t c = static_cast<uint8_t>(is.Take()); | ||
| 562 | + *codepoint = c; | ||
| 563 | + return c <= 0X7F; | ||
| 564 | + } | ||
| 565 | + | ||
| 566 | + template <typename InputStream, typename OutputStream> | ||
| 567 | + static bool Validate(InputStream& is, OutputStream& os) { | ||
| 568 | + uint8_t c = static_cast<uint8_t>(is.Take()); | ||
| 569 | + os.Put(static_cast<typename OutputStream::Ch>(c)); | ||
| 570 | + return c <= 0x7F; | ||
| 571 | + } | ||
| 572 | + | ||
| 573 | + template <typename InputByteStream> | ||
| 574 | + static CharType TakeBOM(InputByteStream& is) { | ||
| 575 | + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); | ||
| 576 | + uint8_t c = static_cast<uint8_t>(Take(is)); | ||
| 577 | + return static_cast<Ch>(c); | ||
| 578 | + } | ||
| 579 | + | ||
| 580 | + template <typename InputByteStream> | ||
| 581 | + static Ch Take(InputByteStream& is) { | ||
| 582 | + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); | ||
| 583 | + return static_cast<Ch>(is.Take()); | ||
| 584 | + } | ||
| 585 | + | ||
| 586 | + template <typename OutputByteStream> | ||
| 587 | + static void PutBOM(OutputByteStream& os) { | ||
| 588 | + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); | ||
| 589 | + (void)os; | ||
| 590 | + } | ||
| 591 | + | ||
| 592 | + template <typename OutputByteStream> | ||
| 593 | + static void Put(OutputByteStream& os, Ch c) { | ||
| 594 | + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); | ||
| 595 | + os.Put(static_cast<typename OutputByteStream::Ch>(c)); | ||
| 596 | + } | ||
| 597 | +}; | ||
| 598 | + | ||
| 599 | +/////////////////////////////////////////////////////////////////////////////// | ||
| 600 | +// AutoUTF | ||
| 601 | + | ||
| 602 | +//! Runtime-specified UTF encoding type of a stream. | ||
| 603 | +enum UTFType { | ||
| 604 | + kUTF8 = 0, //!< UTF-8. | ||
| 605 | + kUTF16LE = 1, //!< UTF-16 little endian. | ||
| 606 | + kUTF16BE = 2, //!< UTF-16 big endian. | ||
| 607 | + kUTF32LE = 3, //!< UTF-32 little endian. | ||
| 608 | + kUTF32BE = 4 //!< UTF-32 big endian. | ||
| 609 | +}; | ||
| 610 | + | ||
| 611 | +//! Dynamically select encoding according to stream's runtime-specified UTF encoding type. | ||
| 612 | +/*! \note This class can be used with AutoUTFInputtStream and AutoUTFOutputStream, which provides GetType(). | ||
| 613 | +*/ | ||
| 614 | +template<typename CharType> | ||
| 615 | +struct AutoUTF { | ||
| 616 | + typedef CharType Ch; | ||
| 617 | + | ||
| 618 | + enum { supportUnicode = 1 }; | ||
| 619 | + | ||
| 620 | +#define RAPIDJSON_ENCODINGS_FUNC(x) UTF8<Ch>::x, UTF16LE<Ch>::x, UTF16BE<Ch>::x, UTF32LE<Ch>::x, UTF32BE<Ch>::x | ||
| 621 | + | ||
| 622 | + template<typename OutputStream> | ||
| 623 | + static RAPIDJSON_FORCEINLINE void Encode(OutputStream& os, unsigned codepoint) { | ||
| 624 | + typedef void (*EncodeFunc)(OutputStream&, unsigned); | ||
| 625 | + static const EncodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Encode) }; | ||
| 626 | + (*f[os.GetType()])(os, codepoint); | ||
| 627 | + } | ||
| 628 | + | ||
| 629 | + template<typename OutputStream> | ||
| 630 | + static RAPIDJSON_FORCEINLINE void EncodeUnsafe(OutputStream& os, unsigned codepoint) { | ||
| 631 | + typedef void (*EncodeFunc)(OutputStream&, unsigned); | ||
| 632 | + static const EncodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(EncodeUnsafe) }; | ||
| 633 | + (*f[os.GetType()])(os, codepoint); | ||
| 634 | + } | ||
| 635 | + | ||
| 636 | + template <typename InputStream> | ||
| 637 | + static RAPIDJSON_FORCEINLINE bool Decode(InputStream& is, unsigned* codepoint) { | ||
| 638 | + typedef bool (*DecodeFunc)(InputStream&, unsigned*); | ||
| 639 | + static const DecodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Decode) }; | ||
| 640 | + return (*f[is.GetType()])(is, codepoint); | ||
| 641 | + } | ||
| 642 | + | ||
| 643 | + template <typename InputStream, typename OutputStream> | ||
| 644 | + static RAPIDJSON_FORCEINLINE bool Validate(InputStream& is, OutputStream& os) { | ||
| 645 | + typedef bool (*ValidateFunc)(InputStream&, OutputStream&); | ||
| 646 | + static const ValidateFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Validate) }; | ||
| 647 | + return (*f[is.GetType()])(is, os); | ||
| 648 | + } | ||
| 649 | + | ||
| 650 | +#undef RAPIDJSON_ENCODINGS_FUNC | ||
| 651 | +}; | ||
| 652 | + | ||
| 653 | +/////////////////////////////////////////////////////////////////////////////// | ||
| 654 | +// Transcoder | ||
| 655 | + | ||
| 656 | +//! Encoding conversion. | ||
| 657 | +template<typename SourceEncoding, typename TargetEncoding> | ||
| 658 | +struct Transcoder { | ||
| 659 | + //! Take one Unicode codepoint from source encoding, convert it to target encoding and put it to the output stream. | ||
| 660 | + template<typename InputStream, typename OutputStream> | ||
| 661 | + static RAPIDJSON_FORCEINLINE bool Transcode(InputStream& is, OutputStream& os) { | ||
| 662 | + unsigned codepoint; | ||
| 663 | + if (!SourceEncoding::Decode(is, &codepoint)) | ||
| 664 | + return false; | ||
| 665 | + TargetEncoding::Encode(os, codepoint); | ||
| 666 | + return true; | ||
| 667 | + } | ||
| 668 | + | ||
| 669 | + template<typename InputStream, typename OutputStream> | ||
| 670 | + static RAPIDJSON_FORCEINLINE bool TranscodeUnsafe(InputStream& is, OutputStream& os) { | ||
| 671 | + unsigned codepoint; | ||
| 672 | + if (!SourceEncoding::Decode(is, &codepoint)) | ||
| 673 | + return false; | ||
| 674 | + TargetEncoding::EncodeUnsafe(os, codepoint); | ||
| 675 | + return true; | ||
| 676 | + } | ||
| 677 | + | ||
| 678 | + //! Validate one Unicode codepoint from an encoded stream. | ||
| 679 | + template<typename InputStream, typename OutputStream> | ||
| 680 | + static RAPIDJSON_FORCEINLINE bool Validate(InputStream& is, OutputStream& os) { | ||
| 681 | + return Transcode(is, os); // Since source/target encoding is different, must transcode. | ||
| 682 | + } | ||
| 683 | +}; | ||
| 684 | + | ||
| 685 | +// Forward declaration. | ||
| 686 | +template<typename Stream> | ||
| 687 | +inline void PutUnsafe(Stream& stream, typename Stream::Ch c); | ||
| 688 | + | ||
| 689 | +//! Specialization of Transcoder with same source and target encoding. | ||
| 690 | +template<typename Encoding> | ||
| 691 | +struct Transcoder<Encoding, Encoding> { | ||
| 692 | + template<typename InputStream, typename OutputStream> | ||
| 693 | + static RAPIDJSON_FORCEINLINE bool Transcode(InputStream& is, OutputStream& os) { | ||
| 694 | + os.Put(is.Take()); // Just copy one code unit. This semantic is different from primary template class. | ||
| 695 | + return true; | ||
| 696 | + } | ||
| 697 | + | ||
| 698 | + template<typename InputStream, typename OutputStream> | ||
| 699 | + static RAPIDJSON_FORCEINLINE bool TranscodeUnsafe(InputStream& is, OutputStream& os) { | ||
| 700 | + PutUnsafe(os, is.Take()); // Just copy one code unit. This semantic is different from primary template class. | ||
| 701 | + return true; | ||
| 702 | + } | ||
| 703 | + | ||
| 704 | + template<typename InputStream, typename OutputStream> | ||
| 705 | + static RAPIDJSON_FORCEINLINE bool Validate(InputStream& is, OutputStream& os) { | ||
| 706 | + return Encoding::Validate(is, os); // source/target encoding are the same | ||
| 707 | + } | ||
| 708 | +}; | ||
| 709 | + | ||
| 710 | +RAPIDJSON_NAMESPACE_END | ||
| 711 | + | ||
| 712 | +#if defined(__GNUC__) || defined(_MSC_VER) | ||
| 713 | +RAPIDJSON_DIAG_POP | ||
| 714 | +#endif | ||
| 715 | + | ||
| 716 | +#endif // RAPIDJSON_ENCODINGS_H_ |
PaperGraph/include/rapidjson/error/en.h
0 → 100644
| 1 | +// Tencent is pleased to support the open source community by making RapidJSON available. | ||
| 2 | +// | ||
| 3 | +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. | ||
| 4 | +// | ||
| 5 | +// Licensed under the MIT License (the "License"); you may not use this file except | ||
| 6 | +// in compliance with the License. You may obtain a copy of the License at | ||
| 7 | +// | ||
| 8 | +// http://opensource.org/licenses/MIT | ||
| 9 | +// | ||
| 10 | +// Unless required by applicable law or agreed to in writing, software distributed | ||
| 11 | +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR | ||
| 12 | +// CONDITIONS OF ANY KIND, either express or implied. See the License for the | ||
| 13 | +// specific language governing permissions and limitations under the License. | ||
| 14 | + | ||
| 15 | +#ifndef RAPIDJSON_ERROR_EN_H_ | ||
| 16 | +#define RAPIDJSON_ERROR_EN_H_ | ||
| 17 | + | ||
| 18 | +#include "error.h" | ||
| 19 | + | ||
| 20 | +#ifdef __clang__ | ||
| 21 | +RAPIDJSON_DIAG_PUSH | ||
| 22 | +RAPIDJSON_DIAG_OFF(switch-enum) | ||
| 23 | +RAPIDJSON_DIAG_OFF(covered-switch-default) | ||
| 24 | +#endif | ||
| 25 | + | ||
| 26 | +RAPIDJSON_NAMESPACE_BEGIN | ||
| 27 | + | ||
| 28 | +//! Maps error code of parsing into error message. | ||
| 29 | +/*! | ||
| 30 | + \ingroup RAPIDJSON_ERRORS | ||
| 31 | + \param parseErrorCode Error code obtained in parsing. | ||
| 32 | + \return the error message. | ||
| 33 | + \note User can make a copy of this function for localization. | ||
| 34 | + Using switch-case is safer for future modification of error codes. | ||
| 35 | +*/ | ||
| 36 | +inline const RAPIDJSON_ERROR_CHARTYPE* GetParseError_En(ParseErrorCode parseErrorCode) { | ||
| 37 | + switch (parseErrorCode) { | ||
| 38 | + case kParseErrorNone: return RAPIDJSON_ERROR_STRING("No error."); | ||
| 39 | + | ||
| 40 | + case kParseErrorDocumentEmpty: return RAPIDJSON_ERROR_STRING("The document is empty."); | ||
| 41 | + case kParseErrorDocumentRootNotSingular: return RAPIDJSON_ERROR_STRING("The document root must not be followed by other values."); | ||
| 42 | + | ||
| 43 | + case kParseErrorValueInvalid: return RAPIDJSON_ERROR_STRING("Invalid value."); | ||
| 44 | + | ||
| 45 | + case kParseErrorObjectMissName: return RAPIDJSON_ERROR_STRING("Missing a name for object member."); | ||
| 46 | + case kParseErrorObjectMissColon: return RAPIDJSON_ERROR_STRING("Missing a colon after a name of object member."); | ||
| 47 | + case kParseErrorObjectMissCommaOrCurlyBracket: return RAPIDJSON_ERROR_STRING("Missing a comma or '}' after an object member."); | ||
| 48 | + | ||
| 49 | + case kParseErrorArrayMissCommaOrSquareBracket: return RAPIDJSON_ERROR_STRING("Missing a comma or ']' after an array element."); | ||
| 50 | + | ||
| 51 | + case kParseErrorStringUnicodeEscapeInvalidHex: return RAPIDJSON_ERROR_STRING("Incorrect hex digit after \\u escape in string."); | ||
| 52 | + case kParseErrorStringUnicodeSurrogateInvalid: return RAPIDJSON_ERROR_STRING("The surrogate pair in string is invalid."); | ||
| 53 | + case kParseErrorStringEscapeInvalid: return RAPIDJSON_ERROR_STRING("Invalid escape character in string."); | ||
| 54 | + case kParseErrorStringMissQuotationMark: return RAPIDJSON_ERROR_STRING("Missing a closing quotation mark in string."); | ||
| 55 | + case kParseErrorStringInvalidEncoding: return RAPIDJSON_ERROR_STRING("Invalid encoding in string."); | ||
| 56 | + | ||
| 57 | + case kParseErrorNumberTooBig: return RAPIDJSON_ERROR_STRING("Number too big to be stored in double."); | ||
| 58 | + case kParseErrorNumberMissFraction: return RAPIDJSON_ERROR_STRING("Miss fraction part in number."); | ||
| 59 | + case kParseErrorNumberMissExponent: return RAPIDJSON_ERROR_STRING("Miss exponent in number."); | ||
| 60 | + | ||
| 61 | + case kParseErrorTermination: return RAPIDJSON_ERROR_STRING("Terminate parsing due to Handler error."); | ||
| 62 | + case kParseErrorUnspecificSyntaxError: return RAPIDJSON_ERROR_STRING("Unspecific syntax error."); | ||
| 63 | + | ||
| 64 | + default: return RAPIDJSON_ERROR_STRING("Unknown error."); | ||
| 65 | + } | ||
| 66 | +} | ||
| 67 | + | ||
| 68 | +RAPIDJSON_NAMESPACE_END | ||
| 69 | + | ||
| 70 | +#ifdef __clang__ | ||
| 71 | +RAPIDJSON_DIAG_POP | ||
| 72 | +#endif | ||
| 73 | + | ||
| 74 | +#endif // RAPIDJSON_ERROR_EN_H_ |
PaperGraph/include/rapidjson/error/error.h
0 → 100644
| 1 | +// Tencent is pleased to support the open source community by making RapidJSON available. | ||
| 2 | +// | ||
| 3 | +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. | ||
| 4 | +// | ||
| 5 | +// Licensed under the MIT License (the "License"); you may not use this file except | ||
| 6 | +// in compliance with the License. You may obtain a copy of the License at | ||
| 7 | +// | ||
| 8 | +// http://opensource.org/licenses/MIT | ||
| 9 | +// | ||
| 10 | +// Unless required by applicable law or agreed to in writing, software distributed | ||
| 11 | +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR | ||
| 12 | +// CONDITIONS OF ANY KIND, either express or implied. See the License for the | ||
| 13 | +// specific language governing permissions and limitations under the License. | ||
| 14 | + | ||
| 15 | +#ifndef RAPIDJSON_ERROR_ERROR_H_ | ||
| 16 | +#define RAPIDJSON_ERROR_ERROR_H_ | ||
| 17 | + | ||
| 18 | +#include "../rapidjson.h" | ||
| 19 | + | ||
| 20 | +#ifdef __clang__ | ||
| 21 | +RAPIDJSON_DIAG_PUSH | ||
| 22 | +RAPIDJSON_DIAG_OFF(padded) | ||
| 23 | +#endif | ||
| 24 | + | ||
| 25 | +/*! \file error.h */ | ||
| 26 | + | ||
| 27 | +/*! \defgroup RAPIDJSON_ERRORS RapidJSON error handling */ | ||
| 28 | + | ||
| 29 | +/////////////////////////////////////////////////////////////////////////////// | ||
| 30 | +// RAPIDJSON_ERROR_CHARTYPE | ||
| 31 | + | ||
| 32 | +//! Character type of error messages. | ||
| 33 | +/*! \ingroup RAPIDJSON_ERRORS | ||
| 34 | + The default character type is \c char. | ||
| 35 | + On Windows, user can define this macro as \c TCHAR for supporting both | ||
| 36 | + unicode/non-unicode settings. | ||
| 37 | +*/ | ||
| 38 | +#ifndef RAPIDJSON_ERROR_CHARTYPE | ||
| 39 | +#define RAPIDJSON_ERROR_CHARTYPE char | ||
| 40 | +#endif | ||
| 41 | + | ||
| 42 | +/////////////////////////////////////////////////////////////////////////////// | ||
| 43 | +// RAPIDJSON_ERROR_STRING | ||
| 44 | + | ||
| 45 | +//! Macro for converting string literial to \ref RAPIDJSON_ERROR_CHARTYPE[]. | ||
| 46 | +/*! \ingroup RAPIDJSON_ERRORS | ||
| 47 | + By default this conversion macro does nothing. | ||
| 48 | + On Windows, user can define this macro as \c _T(x) for supporting both | ||
| 49 | + unicode/non-unicode settings. | ||
| 50 | +*/ | ||
| 51 | +#ifndef RAPIDJSON_ERROR_STRING | ||
| 52 | +#define RAPIDJSON_ERROR_STRING(x) x | ||
| 53 | +#endif | ||
| 54 | + | ||
| 55 | +RAPIDJSON_NAMESPACE_BEGIN | ||
| 56 | + | ||
| 57 | +/////////////////////////////////////////////////////////////////////////////// | ||
| 58 | +// ParseErrorCode | ||
| 59 | + | ||
| 60 | +//! Error code of parsing. | ||
| 61 | +/*! \ingroup RAPIDJSON_ERRORS | ||
| 62 | + \see GenericReader::Parse, GenericReader::GetParseErrorCode | ||
| 63 | +*/ | ||
| 64 | +enum ParseErrorCode { | ||
| 65 | + kParseErrorNone = 0, //!< No error. | ||
| 66 | + | ||
| 67 | + kParseErrorDocumentEmpty, //!< The document is empty. | ||
| 68 | + kParseErrorDocumentRootNotSingular, //!< The document root must not follow by other values. | ||
| 69 | + | ||
| 70 | + kParseErrorValueInvalid, //!< Invalid value. | ||
| 71 | + | ||
| 72 | + kParseErrorObjectMissName, //!< Missing a name for object member. | ||
| 73 | + kParseErrorObjectMissColon, //!< Missing a colon after a name of object member. | ||
| 74 | + kParseErrorObjectMissCommaOrCurlyBracket, //!< Missing a comma or '}' after an object member. | ||
| 75 | + | ||
| 76 | + kParseErrorArrayMissCommaOrSquareBracket, //!< Missing a comma or ']' after an array element. | ||
| 77 | + | ||
| 78 | + kParseErrorStringUnicodeEscapeInvalidHex, //!< Incorrect hex digit after \\u escape in string. | ||
| 79 | + kParseErrorStringUnicodeSurrogateInvalid, //!< The surrogate pair in string is invalid. | ||
| 80 | + kParseErrorStringEscapeInvalid, //!< Invalid escape character in string. | ||
| 81 | + kParseErrorStringMissQuotationMark, //!< Missing a closing quotation mark in string. | ||
| 82 | + kParseErrorStringInvalidEncoding, //!< Invalid encoding in string. | ||
| 83 | + | ||
| 84 | + kParseErrorNumberTooBig, //!< Number too big to be stored in double. | ||
| 85 | + kParseErrorNumberMissFraction, //!< Miss fraction part in number. | ||
| 86 | + kParseErrorNumberMissExponent, //!< Miss exponent in number. | ||
| 87 | + | ||
| 88 | + kParseErrorTermination, //!< Parsing was terminated. | ||
| 89 | + kParseErrorUnspecificSyntaxError //!< Unspecific syntax error. | ||
| 90 | +}; | ||
| 91 | + | ||
| 92 | +//! Result of parsing (wraps ParseErrorCode) | ||
| 93 | +/*! | ||
| 94 | + \ingroup RAPIDJSON_ERRORS | ||
| 95 | + \code | ||
| 96 | + Document doc; | ||
| 97 | + ParseResult ok = doc.Parse("[42]"); | ||
| 98 | + if (!ok) { | ||
| 99 | + fprintf(stderr, "JSON parse error: %s (%u)", | ||
| 100 | + GetParseError_En(ok.Code()), ok.Offset()); | ||
| 101 | + exit(EXIT_FAILURE); | ||
| 102 | + } | ||
| 103 | + \endcode | ||
| 104 | + \see GenericReader::Parse, GenericDocument::Parse | ||
| 105 | +*/ | ||
| 106 | +struct ParseResult { | ||
| 107 | +public: | ||
| 108 | + //! Default constructor, no error. | ||
| 109 | + ParseResult() : code_(kParseErrorNone), offset_(0) {} | ||
| 110 | + //! Constructor to set an error. | ||
| 111 | + ParseResult(ParseErrorCode code, size_t offset) : code_(code), offset_(offset) {} | ||
| 112 | + | ||
| 113 | + //! Get the error code. | ||
| 114 | + ParseErrorCode Code() const { return code_; } | ||
| 115 | + //! Get the error offset, if \ref IsError(), 0 otherwise. | ||
| 116 | + size_t Offset() const { return offset_; } | ||
| 117 | + | ||
| 118 | + //! Conversion to \c bool, returns \c true, iff !\ref IsError(). | ||
| 119 | + operator bool() const { return !IsError(); } | ||
| 120 | + //! Whether the result is an error. | ||
| 121 | + bool IsError() const { return code_ != kParseErrorNone; } | ||
| 122 | + | ||
| 123 | + bool operator==(const ParseResult& that) const { return code_ == that.code_; } | ||
| 124 | + bool operator==(ParseErrorCode code) const { return code_ == code; } | ||
| 125 | + friend bool operator==(ParseErrorCode code, const ParseResult & err) { return code == err.code_; } | ||
| 126 | + | ||
| 127 | + //! Reset error code. | ||
| 128 | + void Clear() { Set(kParseErrorNone); } | ||
| 129 | + //! Update error code and offset. | ||
| 130 | + void Set(ParseErrorCode code, size_t offset = 0) { code_ = code; offset_ = offset; } | ||
| 131 | + | ||
| 132 | +private: | ||
| 133 | + ParseErrorCode code_; | ||
| 134 | + size_t offset_; | ||
| 135 | +}; | ||
| 136 | + | ||
| 137 | +//! Function pointer type of GetParseError(). | ||
| 138 | +/*! \ingroup RAPIDJSON_ERRORS | ||
| 139 | + | ||
| 140 | + This is the prototype for \c GetParseError_X(), where \c X is a locale. | ||
| 141 | + User can dynamically change locale in runtime, e.g.: | ||
| 142 | +\code | ||
| 143 | + GetParseErrorFunc GetParseError = GetParseError_En; // or whatever | ||
| 144 | + const RAPIDJSON_ERROR_CHARTYPE* s = GetParseError(document.GetParseErrorCode()); | ||
| 145 | +\endcode | ||
| 146 | +*/ | ||
| 147 | +typedef const RAPIDJSON_ERROR_CHARTYPE* (*GetParseErrorFunc)(ParseErrorCode); | ||
| 148 | + | ||
| 149 | +RAPIDJSON_NAMESPACE_END | ||
| 150 | + | ||
| 151 | +#ifdef __clang__ | ||
| 152 | +RAPIDJSON_DIAG_POP | ||
| 153 | +#endif | ||
| 154 | + | ||
| 155 | +#endif // RAPIDJSON_ERROR_ERROR_H_ |
| 1 | +// Tencent is pleased to support the open source community by making RapidJSON available. | ||
| 2 | +// | ||
| 3 | +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. | ||
| 4 | +// | ||
| 5 | +// Licensed under the MIT License (the "License"); you may not use this file except | ||
| 6 | +// in compliance with the License. You may obtain a copy of the License at | ||
| 7 | +// | ||
| 8 | +// http://opensource.org/licenses/MIT | ||
| 9 | +// | ||
| 10 | +// Unless required by applicable law or agreed to in writing, software distributed | ||
| 11 | +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR | ||
| 12 | +// CONDITIONS OF ANY KIND, either express or implied. See the License for the | ||
| 13 | +// specific language governing permissions and limitations under the License. | ||
| 14 | + | ||
| 15 | +#ifndef RAPIDJSON_FILEREADSTREAM_H_ | ||
| 16 | +#define RAPIDJSON_FILEREADSTREAM_H_ | ||
| 17 | + | ||
| 18 | +#include "stream.h" | ||
| 19 | +#include <cstdio> | ||
| 20 | + | ||
| 21 | +#ifdef __clang__ | ||
| 22 | +RAPIDJSON_DIAG_PUSH | ||
| 23 | +RAPIDJSON_DIAG_OFF(padded) | ||
| 24 | +RAPIDJSON_DIAG_OFF(unreachable-code) | ||
| 25 | +RAPIDJSON_DIAG_OFF(missing-noreturn) | ||
| 26 | +#endif | ||
| 27 | + | ||
| 28 | +RAPIDJSON_NAMESPACE_BEGIN | ||
| 29 | + | ||
| 30 | +//! File byte stream for input using fread(). | ||
| 31 | +/*! | ||
| 32 | + \note implements Stream concept | ||
| 33 | +*/ | ||
| 34 | +class FileReadStream { | ||
| 35 | +public: | ||
| 36 | + typedef char Ch; //!< Character type (byte). | ||
| 37 | + | ||
| 38 | + //! Constructor. | ||
| 39 | + /*! | ||
| 40 | + \param fp File pointer opened for read. | ||
| 41 | + \param buffer user-supplied buffer. | ||
| 42 | + \param bufferSize size of buffer in bytes. Must >=4 bytes. | ||
| 43 | + */ | ||
| 44 | + FileReadStream(std::FILE* fp, char* buffer, size_t bufferSize) : fp_(fp), buffer_(buffer), bufferSize_(bufferSize), bufferLast_(0), current_(buffer_), readCount_(0), count_(0), eof_(false) { | ||
| 45 | + RAPIDJSON_ASSERT(fp_ != 0); | ||
| 46 | + RAPIDJSON_ASSERT(bufferSize >= 4); | ||
| 47 | + Read(); | ||
| 48 | + } | ||
| 49 | + | ||
| 50 | + Ch Peek() const { return *current_; } | ||
| 51 | + Ch Take() { Ch c = *current_; Read(); return c; } | ||
| 52 | + size_t Tell() const { return count_ + static_cast<size_t>(current_ - buffer_); } | ||
| 53 | + | ||
| 54 | + // Not implemented | ||
| 55 | + void Put(Ch) { RAPIDJSON_ASSERT(false); } | ||
| 56 | + void Flush() { RAPIDJSON_ASSERT(false); } | ||
| 57 | + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } | ||
| 58 | + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } | ||
| 59 | + | ||
| 60 | + // For encoding detection only. | ||
| 61 | + const Ch* Peek4() const { | ||
| 62 | + return (current_ + 4 <= bufferLast_) ? current_ : 0; | ||
| 63 | + } | ||
| 64 | + | ||
| 65 | +private: | ||
| 66 | + void Read() { | ||
| 67 | + if (current_ < bufferLast_) | ||
| 68 | + ++current_; | ||
| 69 | + else if (!eof_) { | ||
| 70 | + count_ += readCount_; | ||
| 71 | + readCount_ = fread(buffer_, 1, bufferSize_, fp_); | ||
| 72 | + bufferLast_ = buffer_ + readCount_ - 1; | ||
| 73 | + current_ = buffer_; | ||
| 74 | + | ||
| 75 | + if (readCount_ < bufferSize_) { | ||
| 76 | + buffer_[readCount_] = '\0'; | ||
| 77 | + ++bufferLast_; | ||
| 78 | + eof_ = true; | ||
| 79 | + } | ||
| 80 | + } | ||
| 81 | + } | ||
| 82 | + | ||
| 83 | + std::FILE* fp_; | ||
| 84 | + Ch *buffer_; | ||
| 85 | + size_t bufferSize_; | ||
| 86 | + Ch *bufferLast_; | ||
| 87 | + Ch *current_; | ||
| 88 | + size_t readCount_; | ||
| 89 | + size_t count_; //!< Number of characters read | ||
| 90 | + bool eof_; | ||
| 91 | +}; | ||
| 92 | + | ||
| 93 | +RAPIDJSON_NAMESPACE_END | ||
| 94 | + | ||
| 95 | +#ifdef __clang__ | ||
| 96 | +RAPIDJSON_DIAG_POP | ||
| 97 | +#endif | ||
| 98 | + | ||
| 99 | +#endif // RAPIDJSON_FILESTREAM_H_ |
| 1 | +// Tencent is pleased to support the open source community by making RapidJSON available. | ||
| 2 | +// | ||
| 3 | +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. | ||
| 4 | +// | ||
| 5 | +// Licensed under the MIT License (the "License"); you may not use this file except | ||
| 6 | +// in compliance with the License. You may obtain a copy of the License at | ||
| 7 | +// | ||
| 8 | +// http://opensource.org/licenses/MIT | ||
| 9 | +// | ||
| 10 | +// Unless required by applicable law or agreed to in writing, software distributed | ||
| 11 | +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR | ||
| 12 | +// CONDITIONS OF ANY KIND, either express or implied. See the License for the | ||
| 13 | +// specific language governing permissions and limitations under the License. | ||
| 14 | + | ||
| 15 | +#ifndef RAPIDJSON_FILEWRITESTREAM_H_ | ||
| 16 | +#define RAPIDJSON_FILEWRITESTREAM_H_ | ||
| 17 | + | ||
| 18 | +#include "stream.h" | ||
| 19 | +#include <cstdio> | ||
| 20 | + | ||
| 21 | +#ifdef __clang__ | ||
| 22 | +RAPIDJSON_DIAG_PUSH | ||
| 23 | +RAPIDJSON_DIAG_OFF(unreachable-code) | ||
| 24 | +#endif | ||
| 25 | + | ||
| 26 | +RAPIDJSON_NAMESPACE_BEGIN | ||
| 27 | + | ||
| 28 | +//! Wrapper of C file stream for input using fread(). | ||
| 29 | +/*! | ||
| 30 | + \note implements Stream concept | ||
| 31 | +*/ | ||
| 32 | +class FileWriteStream { | ||
| 33 | +public: | ||
| 34 | + typedef char Ch; //!< Character type. Only support char. | ||
| 35 | + | ||
| 36 | + FileWriteStream(std::FILE* fp, char* buffer, size_t bufferSize) : fp_(fp), buffer_(buffer), bufferEnd_(buffer + bufferSize), current_(buffer_) { | ||
| 37 | + RAPIDJSON_ASSERT(fp_ != 0); | ||
| 38 | + } | ||
| 39 | + | ||
| 40 | + void Put(char c) { | ||
| 41 | + if (current_ >= bufferEnd_) | ||
| 42 | + Flush(); | ||
| 43 | + | ||
| 44 | + *current_++ = c; | ||
| 45 | + } | ||
| 46 | + | ||
| 47 | + void PutN(char c, size_t n) { | ||
| 48 | + size_t avail = static_cast<size_t>(bufferEnd_ - current_); | ||
| 49 | + while (n > avail) { | ||
| 50 | + std::memset(current_, c, avail); | ||
| 51 | + current_ += avail; | ||
| 52 | + Flush(); | ||
| 53 | + n -= avail; | ||
| 54 | + avail = static_cast<size_t>(bufferEnd_ - current_); | ||
| 55 | + } | ||
| 56 | + | ||
| 57 | + if (n > 0) { | ||
| 58 | + std::memset(current_, c, n); | ||
| 59 | + current_ += n; | ||
| 60 | + } | ||
| 61 | + } | ||
| 62 | + | ||
| 63 | + void Flush() { | ||
| 64 | + if (current_ != buffer_) { | ||
| 65 | + size_t result = fwrite(buffer_, 1, static_cast<size_t>(current_ - buffer_), fp_); | ||
| 66 | + if (result < static_cast<size_t>(current_ - buffer_)) { | ||
| 67 | + // failure deliberately ignored at this time | ||
| 68 | + // added to avoid warn_unused_result build errors | ||
| 69 | + } | ||
| 70 | + current_ = buffer_; | ||
| 71 | + } | ||
| 72 | + } | ||
| 73 | + | ||
| 74 | + // Not implemented | ||
| 75 | + char Peek() const { RAPIDJSON_ASSERT(false); return 0; } | ||
| 76 | + char Take() { RAPIDJSON_ASSERT(false); return 0; } | ||
| 77 | + size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; } | ||
| 78 | + char* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } | ||
| 79 | + size_t PutEnd(char*) { RAPIDJSON_ASSERT(false); return 0; } | ||
| 80 | + | ||
| 81 | +private: | ||
| 82 | + // Prohibit copy constructor & assignment operator. | ||
| 83 | + FileWriteStream(const FileWriteStream&); | ||
| 84 | + FileWriteStream& operator=(const FileWriteStream&); | ||
| 85 | + | ||
| 86 | + std::FILE* fp_; | ||
| 87 | + char *buffer_; | ||
| 88 | + char *bufferEnd_; | ||
| 89 | + char *current_; | ||
| 90 | +}; | ||
| 91 | + | ||
| 92 | +//! Implement specialized version of PutN() with memset() for better performance. | ||
| 93 | +template<> | ||
| 94 | +inline void PutN(FileWriteStream& stream, char c, size_t n) { | ||
| 95 | + stream.PutN(c, n); | ||
| 96 | +} | ||
| 97 | + | ||
| 98 | +RAPIDJSON_NAMESPACE_END | ||
| 99 | + | ||
| 100 | +#ifdef __clang__ | ||
| 101 | +RAPIDJSON_DIAG_POP | ||
| 102 | +#endif | ||
| 103 | + | ||
| 104 | +#endif // RAPIDJSON_FILESTREAM_H_ |
PaperGraph/include/rapidjson/fwd.h
0 → 100644
| 1 | +// Tencent is pleased to support the open source community by making RapidJSON available. | ||
| 2 | +// | ||
| 3 | +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. | ||
| 4 | +// | ||
| 5 | +// Licensed under the MIT License (the "License"); you may not use this file except | ||
| 6 | +// in compliance with the License. You may obtain a copy of the License at | ||
| 7 | +// | ||
| 8 | +// http://opensource.org/licenses/MIT | ||
| 9 | +// | ||
| 10 | +// Unless required by applicable law or agreed to in writing, software distributed | ||
| 11 | +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR | ||
| 12 | +// CONDITIONS OF ANY KIND, either express or implied. See the License for the | ||
| 13 | +// specific language governing permissions and limitations under the License. | ||
| 14 | + | ||
| 15 | +#ifndef RAPIDJSON_FWD_H_ | ||
| 16 | +#define RAPIDJSON_FWD_H_ | ||
| 17 | + | ||
| 18 | +#include "rapidjson.h" | ||
| 19 | + | ||
| 20 | +RAPIDJSON_NAMESPACE_BEGIN | ||
| 21 | + | ||
| 22 | +// encodings.h | ||
| 23 | + | ||
| 24 | +template<typename CharType> struct UTF8; | ||
| 25 | +template<typename CharType> struct UTF16; | ||
| 26 | +template<typename CharType> struct UTF16BE; | ||
| 27 | +template<typename CharType> struct UTF16LE; | ||
| 28 | +template<typename CharType> struct UTF32; | ||
| 29 | +template<typename CharType> struct UTF32BE; | ||
| 30 | +template<typename CharType> struct UTF32LE; | ||
| 31 | +template<typename CharType> struct ASCII; | ||
| 32 | +template<typename CharType> struct AutoUTF; | ||
| 33 | + | ||
| 34 | +template<typename SourceEncoding, typename TargetEncoding> | ||
| 35 | +struct Transcoder; | ||
| 36 | + | ||
| 37 | +// allocators.h | ||
| 38 | + | ||
| 39 | +class CrtAllocator; | ||
| 40 | + | ||
| 41 | +template <typename BaseAllocator> | ||
| 42 | +class MemoryPoolAllocator; | ||
| 43 | + | ||
| 44 | +// stream.h | ||
| 45 | + | ||
| 46 | +template <typename Encoding> | ||
| 47 | +struct GenericStringStream; | ||
| 48 | + | ||
| 49 | +typedef GenericStringStream<UTF8<char> > StringStream; | ||
| 50 | + | ||
| 51 | +template <typename Encoding> | ||
| 52 | +struct GenericInsituStringStream; | ||
| 53 | + | ||
| 54 | +typedef GenericInsituStringStream<UTF8<char> > InsituStringStream; | ||
| 55 | + | ||
| 56 | +// stringbuffer.h | ||
| 57 | + | ||
| 58 | +template <typename Encoding, typename Allocator> | ||
| 59 | +class GenericStringBuffer; | ||
| 60 | + | ||
| 61 | +typedef GenericStringBuffer<UTF8<char>, CrtAllocator> StringBuffer; | ||
| 62 | + | ||
| 63 | +// filereadstream.h | ||
| 64 | + | ||
| 65 | +class FileReadStream; | ||
| 66 | + | ||
| 67 | +// filewritestream.h | ||
| 68 | + | ||
| 69 | +class FileWriteStream; | ||
| 70 | + | ||
| 71 | +// memorybuffer.h | ||
| 72 | + | ||
| 73 | +template <typename Allocator> | ||
| 74 | +struct GenericMemoryBuffer; | ||
| 75 | + | ||
| 76 | +typedef GenericMemoryBuffer<CrtAllocator> MemoryBuffer; | ||
| 77 | + | ||
| 78 | +// memorystream.h | ||
| 79 | + | ||
| 80 | +struct MemoryStream; | ||
| 81 | + | ||
| 82 | +// reader.h | ||
| 83 | + | ||
| 84 | +template<typename Encoding, typename Derived> | ||
| 85 | +struct BaseReaderHandler; | ||
| 86 | + | ||
| 87 | +template <typename SourceEncoding, typename TargetEncoding, typename StackAllocator> | ||
| 88 | +class GenericReader; | ||
| 89 | + | ||
| 90 | +typedef GenericReader<UTF8<char>, UTF8<char>, CrtAllocator> Reader; | ||
| 91 | + | ||
| 92 | +// writer.h | ||
| 93 | + | ||
| 94 | +template<typename OutputStream, typename SourceEncoding, typename TargetEncoding, typename StackAllocator, unsigned writeFlags> | ||
| 95 | +class Writer; | ||
| 96 | + | ||
| 97 | +// prettywriter.h | ||
| 98 | + | ||
| 99 | +template<typename OutputStream, typename SourceEncoding, typename TargetEncoding, typename StackAllocator, unsigned writeFlags> | ||
| 100 | +class PrettyWriter; | ||
| 101 | + | ||
| 102 | +// document.h | ||
| 103 | + | ||
| 104 | +template <typename Encoding, typename Allocator> | ||
| 105 | +struct GenericMember; | ||
| 106 | + | ||
| 107 | +template <bool Const, typename Encoding, typename Allocator> | ||
| 108 | +class GenericMemberIterator; | ||
| 109 | + | ||
| 110 | +template<typename CharType> | ||
| 111 | +struct GenericStringRef; | ||
| 112 | + | ||
| 113 | +template <typename Encoding, typename Allocator> | ||
| 114 | +class GenericValue; | ||
| 115 | + | ||
| 116 | +typedef GenericValue<UTF8<char>, MemoryPoolAllocator<CrtAllocator> > Value; | ||
| 117 | + | ||
| 118 | +template <typename Encoding, typename Allocator, typename StackAllocator> | ||
| 119 | +class GenericDocument; | ||
| 120 | + | ||
| 121 | +typedef GenericDocument<UTF8<char>, MemoryPoolAllocator<CrtAllocator>, CrtAllocator> Document; | ||
| 122 | + | ||
| 123 | +// pointer.h | ||
| 124 | + | ||
| 125 | +template <typename ValueType, typename Allocator> | ||
| 126 | +class GenericPointer; | ||
| 127 | + | ||
| 128 | +typedef GenericPointer<Value, CrtAllocator> Pointer; | ||
| 129 | + | ||
| 130 | +// schema.h | ||
| 131 | + | ||
| 132 | +template <typename SchemaDocumentType> | ||
| 133 | +class IGenericRemoteSchemaDocumentProvider; | ||
| 134 | + | ||
| 135 | +template <typename ValueT, typename Allocator> | ||
| 136 | +class GenericSchemaDocument; | ||
| 137 | + | ||
| 138 | +typedef GenericSchemaDocument<Value, CrtAllocator> SchemaDocument; | ||
| 139 | +typedef IGenericRemoteSchemaDocumentProvider<SchemaDocument> IRemoteSchemaDocumentProvider; | ||
| 140 | + | ||
| 141 | +template < | ||
| 142 | + typename SchemaDocumentType, | ||
| 143 | + typename OutputHandler, | ||
| 144 | + typename StateAllocator> | ||
| 145 | +class GenericSchemaValidator; | ||
| 146 | + | ||
| 147 | +typedef GenericSchemaValidator<SchemaDocument, BaseReaderHandler<UTF8<char>, void>, CrtAllocator> SchemaValidator; | ||
| 148 | + | ||
| 149 | +RAPIDJSON_NAMESPACE_END | ||
| 150 | + | ||
| 151 | +#endif // RAPIDJSON_RAPIDJSONFWD_H_ |
| 1 | +// Tencent is pleased to support the open source community by making RapidJSON available. | ||
| 2 | +// | ||
| 3 | +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. | ||
| 4 | +// | ||
| 5 | +// Licensed under the MIT License (the "License"); you may not use this file except | ||
| 6 | +// in compliance with the License. You may obtain a copy of the License at | ||
| 7 | +// | ||
| 8 | +// http://opensource.org/licenses/MIT | ||
| 9 | +// | ||
| 10 | +// Unless required by applicable law or agreed to in writing, software distributed | ||
| 11 | +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR | ||
| 12 | +// CONDITIONS OF ANY KIND, either express or implied. See the License for the | ||
| 13 | +// specific language governing permissions and limitations under the License. | ||
| 14 | + | ||
| 15 | +#ifndef RAPIDJSON_BIGINTEGER_H_ | ||
| 16 | +#define RAPIDJSON_BIGINTEGER_H_ | ||
| 17 | + | ||
| 18 | +#include "../rapidjson.h" | ||
| 19 | + | ||
| 20 | +#if defined(_MSC_VER) && defined(_M_AMD64) | ||
| 21 | +#include <intrin.h> // for _umul128 | ||
| 22 | +#pragma intrinsic(_umul128) | ||
| 23 | +#endif | ||
| 24 | + | ||
| 25 | +RAPIDJSON_NAMESPACE_BEGIN | ||
| 26 | +namespace internal { | ||
| 27 | + | ||
| 28 | +class BigInteger { | ||
| 29 | +public: | ||
| 30 | + typedef uint64_t Type; | ||
| 31 | + | ||
| 32 | + BigInteger(const BigInteger& rhs) : count_(rhs.count_) { | ||
| 33 | + std::memcpy(digits_, rhs.digits_, count_ * sizeof(Type)); | ||
| 34 | + } | ||
| 35 | + | ||
| 36 | + explicit BigInteger(uint64_t u) : count_(1) { | ||
| 37 | + digits_[0] = u; | ||
| 38 | + } | ||
| 39 | + | ||
| 40 | + BigInteger(const char* decimals, size_t length) : count_(1) { | ||
| 41 | + RAPIDJSON_ASSERT(length > 0); | ||
| 42 | + digits_[0] = 0; | ||
| 43 | + size_t i = 0; | ||
| 44 | + const size_t kMaxDigitPerIteration = 19; // 2^64 = 18446744073709551616 > 10^19 | ||
| 45 | + while (length >= kMaxDigitPerIteration) { | ||
| 46 | + AppendDecimal64(decimals + i, decimals + i + kMaxDigitPerIteration); | ||
| 47 | + length -= kMaxDigitPerIteration; | ||
| 48 | + i += kMaxDigitPerIteration; | ||
| 49 | + } | ||
| 50 | + | ||
| 51 | + if (length > 0) | ||
| 52 | + AppendDecimal64(decimals + i, decimals + i + length); | ||
| 53 | + } | ||
| 54 | + | ||
| 55 | + BigInteger& operator=(const BigInteger &rhs) | ||
| 56 | + { | ||
| 57 | + if (this != &rhs) { | ||
| 58 | + count_ = rhs.count_; | ||
| 59 | + std::memcpy(digits_, rhs.digits_, count_ * sizeof(Type)); | ||
| 60 | + } | ||
| 61 | + return *this; | ||
| 62 | + } | ||
| 63 | + | ||
| 64 | + BigInteger& operator=(uint64_t u) { | ||
| 65 | + digits_[0] = u; | ||
| 66 | + count_ = 1; | ||
| 67 | + return *this; | ||
| 68 | + } | ||
| 69 | + | ||
| 70 | + BigInteger& operator+=(uint64_t u) { | ||
| 71 | + Type backup = digits_[0]; | ||
| 72 | + digits_[0] += u; | ||
| 73 | + for (size_t i = 0; i < count_ - 1; i++) { | ||
| 74 | + if (digits_[i] >= backup) | ||
| 75 | + return *this; // no carry | ||
| 76 | + backup = digits_[i + 1]; | ||
| 77 | + digits_[i + 1] += 1; | ||
| 78 | + } | ||
| 79 | + | ||
| 80 | + // Last carry | ||
| 81 | + if (digits_[count_ - 1] < backup) | ||
| 82 | + PushBack(1); | ||
| 83 | + | ||
| 84 | + return *this; | ||
| 85 | + } | ||
| 86 | + | ||
| 87 | + BigInteger& operator*=(uint64_t u) { | ||
| 88 | + if (u == 0) return *this = 0; | ||
| 89 | + if (u == 1) return *this; | ||
| 90 | + if (*this == 1) return *this = u; | ||
| 91 | + | ||
| 92 | + uint64_t k = 0; | ||
| 93 | + for (size_t i = 0; i < count_; i++) { | ||
| 94 | + uint64_t hi; | ||
| 95 | + digits_[i] = MulAdd64(digits_[i], u, k, &hi); | ||
| 96 | + k = hi; | ||
| 97 | + } | ||
| 98 | + | ||
| 99 | + if (k > 0) | ||
| 100 | + PushBack(k); | ||
| 101 | + | ||
| 102 | + return *this; | ||
| 103 | + } | ||
| 104 | + | ||
| 105 | + BigInteger& operator*=(uint32_t u) { | ||
| 106 | + if (u == 0) return *this = 0; | ||
| 107 | + if (u == 1) return *this; | ||
| 108 | + if (*this == 1) return *this = u; | ||
| 109 | + | ||
| 110 | + uint64_t k = 0; | ||
| 111 | + for (size_t i = 0; i < count_; i++) { | ||
| 112 | + const uint64_t c = digits_[i] >> 32; | ||
| 113 | + const uint64_t d = digits_[i] & 0xFFFFFFFF; | ||
| 114 | + const uint64_t uc = u * c; | ||
| 115 | + const uint64_t ud = u * d; | ||
| 116 | + const uint64_t p0 = ud + k; | ||
| 117 | + const uint64_t p1 = uc + (p0 >> 32); | ||
| 118 | + digits_[i] = (p0 & 0xFFFFFFFF) | (p1 << 32); | ||
| 119 | + k = p1 >> 32; | ||
| 120 | + } | ||
| 121 | + | ||
| 122 | + if (k > 0) | ||
| 123 | + PushBack(k); | ||
| 124 | + | ||
| 125 | + return *this; | ||
| 126 | + } | ||
| 127 | + | ||
| 128 | + BigInteger& operator<<=(size_t shift) { | ||
| 129 | + if (IsZero() || shift == 0) return *this; | ||
| 130 | + | ||
| 131 | + size_t offset = shift / kTypeBit; | ||
| 132 | + size_t interShift = shift % kTypeBit; | ||
| 133 | + RAPIDJSON_ASSERT(count_ + offset <= kCapacity); | ||
| 134 | + | ||
| 135 | + if (interShift == 0) { | ||
| 136 | + std::memmove(&digits_[count_ - 1 + offset], &digits_[count_ - 1], count_ * sizeof(Type)); | ||
| 137 | + count_ += offset; | ||
| 138 | + } | ||
| 139 | + else { | ||
| 140 | + digits_[count_] = 0; | ||
| 141 | + for (size_t i = count_; i > 0; i--) | ||
| 142 | + digits_[i + offset] = (digits_[i] << interShift) | (digits_[i - 1] >> (kTypeBit - interShift)); | ||
| 143 | + digits_[offset] = digits_[0] << interShift; | ||
| 144 | + count_ += offset; | ||
| 145 | + if (digits_[count_]) | ||
| 146 | + count_++; | ||
| 147 | + } | ||
| 148 | + | ||
| 149 | + std::memset(digits_, 0, offset * sizeof(Type)); | ||
| 150 | + | ||
| 151 | + return *this; | ||
| 152 | + } | ||
| 153 | + | ||
| 154 | + bool operator==(const BigInteger& rhs) const { | ||
| 155 | + return count_ == rhs.count_ && std::memcmp(digits_, rhs.digits_, count_ * sizeof(Type)) == 0; | ||
| 156 | + } | ||
| 157 | + | ||
| 158 | + bool operator==(const Type rhs) const { | ||
| 159 | + return count_ == 1 && digits_[0] == rhs; | ||
| 160 | + } | ||
| 161 | + | ||
| 162 | + BigInteger& MultiplyPow5(unsigned exp) { | ||
| 163 | + static const uint32_t kPow5[12] = { | ||
| 164 | + 5, | ||
| 165 | + 5 * 5, | ||
| 166 | + 5 * 5 * 5, | ||
| 167 | + 5 * 5 * 5 * 5, | ||
| 168 | + 5 * 5 * 5 * 5 * 5, | ||
| 169 | + 5 * 5 * 5 * 5 * 5 * 5, | ||
| 170 | + 5 * 5 * 5 * 5 * 5 * 5 * 5, | ||
| 171 | + 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, | ||
| 172 | + 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, | ||
| 173 | + 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, | ||
| 174 | + 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, | ||
| 175 | + 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 | ||
| 176 | + }; | ||
| 177 | + if (exp == 0) return *this; | ||
| 178 | + for (; exp >= 27; exp -= 27) *this *= RAPIDJSON_UINT64_C2(0X6765C793, 0XFA10079D); // 5^27 | ||
| 179 | + for (; exp >= 13; exp -= 13) *this *= static_cast<uint32_t>(1220703125u); // 5^13 | ||
| 180 | + if (exp > 0) *this *= kPow5[exp - 1]; | ||
| 181 | + return *this; | ||
| 182 | + } | ||
| 183 | + | ||
| 184 | + // Compute absolute difference of this and rhs. | ||
| 185 | + // Assume this != rhs | ||
| 186 | + bool Difference(const BigInteger& rhs, BigInteger* out) const { | ||
| 187 | + int cmp = Compare(rhs); | ||
| 188 | + RAPIDJSON_ASSERT(cmp != 0); | ||
| 189 | + const BigInteger *a, *b; // Makes a > b | ||
| 190 | + bool ret; | ||
| 191 | + if (cmp < 0) { a = &rhs; b = this; ret = true; } | ||
| 192 | + else { a = this; b = &rhs; ret = false; } | ||
| 193 | + | ||
| 194 | + Type borrow = 0; | ||
| 195 | + for (size_t i = 0; i < a->count_; i++) { | ||
| 196 | + Type d = a->digits_[i] - borrow; | ||
| 197 | + if (i < b->count_) | ||
| 198 | + d -= b->digits_[i]; | ||
| 199 | + borrow = (d > a->digits_[i]) ? 1 : 0; | ||
| 200 | + out->digits_[i] = d; | ||
| 201 | + if (d != 0) | ||
| 202 | + out->count_ = i + 1; | ||
| 203 | + } | ||
| 204 | + | ||
| 205 | + return ret; | ||
| 206 | + } | ||
| 207 | + | ||
| 208 | + int Compare(const BigInteger& rhs) const { | ||
| 209 | + if (count_ != rhs.count_) | ||
| 210 | + return count_ < rhs.count_ ? -1 : 1; | ||
| 211 | + | ||
| 212 | + for (size_t i = count_; i-- > 0;) | ||
| 213 | + if (digits_[i] != rhs.digits_[i]) | ||
| 214 | + return digits_[i] < rhs.digits_[i] ? -1 : 1; | ||
| 215 | + | ||
| 216 | + return 0; | ||
| 217 | + } | ||
| 218 | + | ||
| 219 | + size_t GetCount() const { return count_; } | ||
| 220 | + Type GetDigit(size_t index) const { RAPIDJSON_ASSERT(index < count_); return digits_[index]; } | ||
| 221 | + bool IsZero() const { return count_ == 1 && digits_[0] == 0; } | ||
| 222 | + | ||
| 223 | +private: | ||
| 224 | + void AppendDecimal64(const char* begin, const char* end) { | ||
| 225 | + uint64_t u = ParseUint64(begin, end); | ||
| 226 | + if (IsZero()) | ||
| 227 | + *this = u; | ||
| 228 | + else { | ||
| 229 | + unsigned exp = static_cast<unsigned>(end - begin); | ||
| 230 | + (MultiplyPow5(exp) <<= exp) += u; // *this = *this * 10^exp + u | ||
| 231 | + } | ||
| 232 | + } | ||
| 233 | + | ||
| 234 | + void PushBack(Type digit) { | ||
| 235 | + RAPIDJSON_ASSERT(count_ < kCapacity); | ||
| 236 | + digits_[count_++] = digit; | ||
| 237 | + } | ||
| 238 | + | ||
| 239 | + static uint64_t ParseUint64(const char* begin, const char* end) { | ||
| 240 | + uint64_t r = 0; | ||
| 241 | + for (const char* p = begin; p != end; ++p) { | ||
| 242 | + RAPIDJSON_ASSERT(*p >= '0' && *p <= '9'); | ||
| 243 | + r = r * 10u + static_cast<unsigned>(*p - '0'); | ||
| 244 | + } | ||
| 245 | + return r; | ||
| 246 | + } | ||
| 247 | + | ||
| 248 | + // Assume a * b + k < 2^128 | ||
| 249 | + static uint64_t MulAdd64(uint64_t a, uint64_t b, uint64_t k, uint64_t* outHigh) { | ||
| 250 | +#if defined(_MSC_VER) && defined(_M_AMD64) | ||
| 251 | + uint64_t low = _umul128(a, b, outHigh) + k; | ||
| 252 | + if (low < k) | ||
| 253 | + (*outHigh)++; | ||
| 254 | + return low; | ||
| 255 | +#elif (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) && defined(__x86_64__) | ||
| 256 | + __extension__ typedef unsigned __int128 uint128; | ||
| 257 | + uint128 p = static_cast<uint128>(a) * static_cast<uint128>(b); | ||
| 258 | + p += k; | ||
| 259 | + *outHigh = static_cast<uint64_t>(p >> 64); | ||
| 260 | + return static_cast<uint64_t>(p); | ||
| 261 | +#else | ||
| 262 | + const uint64_t a0 = a & 0xFFFFFFFF, a1 = a >> 32, b0 = b & 0xFFFFFFFF, b1 = b >> 32; | ||
| 263 | + uint64_t x0 = a0 * b0, x1 = a0 * b1, x2 = a1 * b0, x3 = a1 * b1; | ||
| 264 | + x1 += (x0 >> 32); // can't give carry | ||
| 265 | + x1 += x2; | ||
| 266 | + if (x1 < x2) | ||
| 267 | + x3 += (static_cast<uint64_t>(1) << 32); | ||
| 268 | + uint64_t lo = (x1 << 32) + (x0 & 0xFFFFFFFF); | ||
| 269 | + uint64_t hi = x3 + (x1 >> 32); | ||
| 270 | + | ||
| 271 | + lo += k; | ||
| 272 | + if (lo < k) | ||
| 273 | + hi++; | ||
| 274 | + *outHigh = hi; | ||
| 275 | + return lo; | ||
| 276 | +#endif | ||
| 277 | + } | ||
| 278 | + | ||
| 279 | + static const size_t kBitCount = 3328; // 64bit * 54 > 10^1000 | ||
| 280 | + static const size_t kCapacity = kBitCount / sizeof(Type); | ||
| 281 | + static const size_t kTypeBit = sizeof(Type) * 8; | ||
| 282 | + | ||
| 283 | + Type digits_[kCapacity]; | ||
| 284 | + size_t count_; | ||
| 285 | +}; | ||
| 286 | + | ||
| 287 | +} // namespace internal | ||
| 288 | +RAPIDJSON_NAMESPACE_END | ||
| 289 | + | ||
| 290 | +#endif // RAPIDJSON_BIGINTEGER_H_ |
| 1 | +// Tencent is pleased to support the open source community by making RapidJSON available. | ||
| 2 | +// | ||
| 3 | +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. | ||
| 4 | +// | ||
| 5 | +// Licensed under the MIT License (the "License"); you may not use this file except | ||
| 6 | +// in compliance with the License. You may obtain a copy of the License at | ||
| 7 | +// | ||
| 8 | +// http://opensource.org/licenses/MIT | ||
| 9 | +// | ||
| 10 | +// Unless required by applicable law or agreed to in writing, software distributed | ||
| 11 | +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR | ||
| 12 | +// CONDITIONS OF ANY KIND, either express or implied. See the License for the | ||
| 13 | +// specific language governing permissions and limitations under the License. | ||
| 14 | + | ||
| 15 | +// This is a C++ header-only implementation of Grisu2 algorithm from the publication: | ||
| 16 | +// Loitsch, Florian. "Printing floating-point numbers quickly and accurately with | ||
| 17 | +// integers." ACM Sigplan Notices 45.6 (2010): 233-243. | ||
| 18 | + | ||
| 19 | +#ifndef RAPIDJSON_DIYFP_H_ | ||
| 20 | +#define RAPIDJSON_DIYFP_H_ | ||
| 21 | + | ||
| 22 | +#include "../rapidjson.h" | ||
| 23 | + | ||
| 24 | +#if defined(_MSC_VER) && defined(_M_AMD64) | ||
| 25 | +#include <intrin.h> | ||
| 26 | +#pragma intrinsic(_BitScanReverse64) | ||
| 27 | +#pragma intrinsic(_umul128) | ||
| 28 | +#endif | ||
| 29 | + | ||
| 30 | +RAPIDJSON_NAMESPACE_BEGIN | ||
| 31 | +namespace internal { | ||
| 32 | + | ||
| 33 | +#ifdef __GNUC__ | ||
| 34 | +RAPIDJSON_DIAG_PUSH | ||
| 35 | +RAPIDJSON_DIAG_OFF(effc++) | ||
| 36 | +#endif | ||
| 37 | + | ||
| 38 | +#ifdef __clang__ | ||
| 39 | +RAPIDJSON_DIAG_PUSH | ||
| 40 | +RAPIDJSON_DIAG_OFF(padded) | ||
| 41 | +#endif | ||
| 42 | + | ||
| 43 | +struct DiyFp { | ||
| 44 | + DiyFp() : f(), e() {} | ||
| 45 | + | ||
| 46 | + DiyFp(uint64_t fp, int exp) : f(fp), e(exp) {} | ||
| 47 | + | ||
| 48 | + explicit DiyFp(double d) { | ||
| 49 | + union { | ||
| 50 | + double d; | ||
| 51 | + uint64_t u64; | ||
| 52 | + } u = { d }; | ||
| 53 | + | ||
| 54 | + int biased_e = static_cast<int>((u.u64 & kDpExponentMask) >> kDpSignificandSize); | ||
| 55 | + uint64_t significand = (u.u64 & kDpSignificandMask); | ||
| 56 | + if (biased_e != 0) { | ||
| 57 | + f = significand + kDpHiddenBit; | ||
| 58 | + e = biased_e - kDpExponentBias; | ||
| 59 | + } | ||
| 60 | + else { | ||
| 61 | + f = significand; | ||
| 62 | + e = kDpMinExponent + 1; | ||
| 63 | + } | ||
| 64 | + } | ||
| 65 | + | ||
| 66 | + DiyFp operator-(const DiyFp& rhs) const { | ||
| 67 | + return DiyFp(f - rhs.f, e); | ||
| 68 | + } | ||
| 69 | + | ||
| 70 | + DiyFp operator*(const DiyFp& rhs) const { | ||
| 71 | +#if defined(_MSC_VER) && defined(_M_AMD64) | ||
| 72 | + uint64_t h; | ||
| 73 | + uint64_t l = _umul128(f, rhs.f, &h); | ||
| 74 | + if (l & (uint64_t(1) << 63)) // rounding | ||
| 75 | + h++; | ||
| 76 | + return DiyFp(h, e + rhs.e + 64); | ||
| 77 | +#elif (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) && defined(__x86_64__) | ||
| 78 | + __extension__ typedef unsigned __int128 uint128; | ||
| 79 | + uint128 p = static_cast<uint128>(f) * static_cast<uint128>(rhs.f); | ||
| 80 | + uint64_t h = static_cast<uint64_t>(p >> 64); | ||
| 81 | + uint64_t l = static_cast<uint64_t>(p); | ||
| 82 | + if (l & (uint64_t(1) << 63)) // rounding | ||
| 83 | + h++; | ||
| 84 | + return DiyFp(h, e + rhs.e + 64); | ||
| 85 | +#else | ||
| 86 | + const uint64_t M32 = 0xFFFFFFFF; | ||
| 87 | + const uint64_t a = f >> 32; | ||
| 88 | + const uint64_t b = f & M32; | ||
| 89 | + const uint64_t c = rhs.f >> 32; | ||
| 90 | + const uint64_t d = rhs.f & M32; | ||
| 91 | + const uint64_t ac = a * c; | ||
| 92 | + const uint64_t bc = b * c; | ||
| 93 | + const uint64_t ad = a * d; | ||
| 94 | + const uint64_t bd = b * d; | ||
| 95 | + uint64_t tmp = (bd >> 32) + (ad & M32) + (bc & M32); | ||
| 96 | + tmp += 1U << 31; /// mult_round | ||
| 97 | + return DiyFp(ac + (ad >> 32) + (bc >> 32) + (tmp >> 32), e + rhs.e + 64); | ||
| 98 | +#endif | ||
| 99 | + } | ||
| 100 | + | ||
| 101 | + DiyFp Normalize() const { | ||
| 102 | +#if defined(_MSC_VER) && defined(_M_AMD64) | ||
| 103 | + unsigned long index; | ||
| 104 | + _BitScanReverse64(&index, f); | ||
| 105 | + return DiyFp(f << (63 - index), e - (63 - index)); | ||
| 106 | +#elif defined(__GNUC__) && __GNUC__ >= 4 | ||
| 107 | + int s = __builtin_clzll(f); | ||
| 108 | + return DiyFp(f << s, e - s); | ||
| 109 | +#else | ||
| 110 | + DiyFp res = *this; | ||
| 111 | + while (!(res.f & (static_cast<uint64_t>(1) << 63))) { | ||
| 112 | + res.f <<= 1; | ||
| 113 | + res.e--; | ||
| 114 | + } | ||
| 115 | + return res; | ||
| 116 | +#endif | ||
| 117 | + } | ||
| 118 | + | ||
| 119 | + DiyFp NormalizeBoundary() const { | ||
| 120 | + DiyFp res = *this; | ||
| 121 | + while (!(res.f & (kDpHiddenBit << 1))) { | ||
| 122 | + res.f <<= 1; | ||
| 123 | + res.e--; | ||
| 124 | + } | ||
| 125 | + res.f <<= (kDiySignificandSize - kDpSignificandSize - 2); | ||
| 126 | + res.e = res.e - (kDiySignificandSize - kDpSignificandSize - 2); | ||
| 127 | + return res; | ||
| 128 | + } | ||
| 129 | + | ||
| 130 | + void NormalizedBoundaries(DiyFp* minus, DiyFp* plus) const { | ||
| 131 | + DiyFp pl = DiyFp((f << 1) + 1, e - 1).NormalizeBoundary(); | ||
| 132 | + DiyFp mi = (f == kDpHiddenBit) ? DiyFp((f << 2) - 1, e - 2) : DiyFp((f << 1) - 1, e - 1); | ||
| 133 | + mi.f <<= mi.e - pl.e; | ||
| 134 | + mi.e = pl.e; | ||
| 135 | + *plus = pl; | ||
| 136 | + *minus = mi; | ||
| 137 | + } | ||
| 138 | + | ||
| 139 | + double ToDouble() const { | ||
| 140 | + union { | ||
| 141 | + double d; | ||
| 142 | + uint64_t u64; | ||
| 143 | + }u; | ||
| 144 | + const uint64_t be = (e == kDpDenormalExponent && (f & kDpHiddenBit) == 0) ? 0 : | ||
| 145 | + static_cast<uint64_t>(e + kDpExponentBias); | ||
| 146 | + u.u64 = (f & kDpSignificandMask) | (be << kDpSignificandSize); | ||
| 147 | + return u.d; | ||
| 148 | + } | ||
| 149 | + | ||
| 150 | + static const int kDiySignificandSize = 64; | ||
| 151 | + static const int kDpSignificandSize = 52; | ||
| 152 | + static const int kDpExponentBias = 0x3FF + kDpSignificandSize; | ||
| 153 | + static const int kDpMaxExponent = 0x7FF - kDpExponentBias; | ||
| 154 | + static const int kDpMinExponent = -kDpExponentBias; | ||
| 155 | + static const int kDpDenormalExponent = -kDpExponentBias + 1; | ||
| 156 | + static const uint64_t kDpExponentMask = RAPIDJSON_UINT64_C2(0x7FF00000, 0x00000000); | ||
| 157 | + static const uint64_t kDpSignificandMask = RAPIDJSON_UINT64_C2(0x000FFFFF, 0xFFFFFFFF); | ||
| 158 | + static const uint64_t kDpHiddenBit = RAPIDJSON_UINT64_C2(0x00100000, 0x00000000); | ||
| 159 | + | ||
| 160 | + uint64_t f; | ||
| 161 | + int e; | ||
| 162 | +}; | ||
| 163 | + | ||
| 164 | +inline DiyFp GetCachedPowerByIndex(size_t index) { | ||
| 165 | + // 10^-348, 10^-340, ..., 10^340 | ||
| 166 | + static const uint64_t kCachedPowers_F[] = { | ||
| 167 | + RAPIDJSON_UINT64_C2(0xfa8fd5a0, 0x081c0288), RAPIDJSON_UINT64_C2(0xbaaee17f, 0xa23ebf76), | ||
| 168 | + RAPIDJSON_UINT64_C2(0x8b16fb20, 0x3055ac76), RAPIDJSON_UINT64_C2(0xcf42894a, 0x5dce35ea), | ||
| 169 | + RAPIDJSON_UINT64_C2(0x9a6bb0aa, 0x55653b2d), RAPIDJSON_UINT64_C2(0xe61acf03, 0x3d1a45df), | ||
| 170 | + RAPIDJSON_UINT64_C2(0xab70fe17, 0xc79ac6ca), RAPIDJSON_UINT64_C2(0xff77b1fc, 0xbebcdc4f), | ||
| 171 | + RAPIDJSON_UINT64_C2(0xbe5691ef, 0x416bd60c), RAPIDJSON_UINT64_C2(0x8dd01fad, 0x907ffc3c), | ||
| 172 | + RAPIDJSON_UINT64_C2(0xd3515c28, 0x31559a83), RAPIDJSON_UINT64_C2(0x9d71ac8f, 0xada6c9b5), | ||
| 173 | + RAPIDJSON_UINT64_C2(0xea9c2277, 0x23ee8bcb), RAPIDJSON_UINT64_C2(0xaecc4991, 0x4078536d), | ||
| 174 | + RAPIDJSON_UINT64_C2(0x823c1279, 0x5db6ce57), RAPIDJSON_UINT64_C2(0xc2109436, 0x4dfb5637), | ||
| 175 | + RAPIDJSON_UINT64_C2(0x9096ea6f, 0x3848984f), RAPIDJSON_UINT64_C2(0xd77485cb, 0x25823ac7), | ||
| 176 | + RAPIDJSON_UINT64_C2(0xa086cfcd, 0x97bf97f4), RAPIDJSON_UINT64_C2(0xef340a98, 0x172aace5), | ||
| 177 | + RAPIDJSON_UINT64_C2(0xb23867fb, 0x2a35b28e), RAPIDJSON_UINT64_C2(0x84c8d4df, 0xd2c63f3b), | ||
| 178 | + RAPIDJSON_UINT64_C2(0xc5dd4427, 0x1ad3cdba), RAPIDJSON_UINT64_C2(0x936b9fce, 0xbb25c996), | ||
| 179 | + RAPIDJSON_UINT64_C2(0xdbac6c24, 0x7d62a584), RAPIDJSON_UINT64_C2(0xa3ab6658, 0x0d5fdaf6), | ||
| 180 | + RAPIDJSON_UINT64_C2(0xf3e2f893, 0xdec3f126), RAPIDJSON_UINT64_C2(0xb5b5ada8, 0xaaff80b8), | ||
| 181 | + RAPIDJSON_UINT64_C2(0x87625f05, 0x6c7c4a8b), RAPIDJSON_UINT64_C2(0xc9bcff60, 0x34c13053), | ||
| 182 | + RAPIDJSON_UINT64_C2(0x964e858c, 0x91ba2655), RAPIDJSON_UINT64_C2(0xdff97724, 0x70297ebd), | ||
| 183 | + RAPIDJSON_UINT64_C2(0xa6dfbd9f, 0xb8e5b88f), RAPIDJSON_UINT64_C2(0xf8a95fcf, 0x88747d94), | ||
| 184 | + RAPIDJSON_UINT64_C2(0xb9447093, 0x8fa89bcf), RAPIDJSON_UINT64_C2(0x8a08f0f8, 0xbf0f156b), | ||
| 185 | + RAPIDJSON_UINT64_C2(0xcdb02555, 0x653131b6), RAPIDJSON_UINT64_C2(0x993fe2c6, 0xd07b7fac), | ||
| 186 | + RAPIDJSON_UINT64_C2(0xe45c10c4, 0x2a2b3b06), RAPIDJSON_UINT64_C2(0xaa242499, 0x697392d3), | ||
| 187 | + RAPIDJSON_UINT64_C2(0xfd87b5f2, 0x8300ca0e), RAPIDJSON_UINT64_C2(0xbce50864, 0x92111aeb), | ||
| 188 | + RAPIDJSON_UINT64_C2(0x8cbccc09, 0x6f5088cc), RAPIDJSON_UINT64_C2(0xd1b71758, 0xe219652c), | ||
| 189 | + RAPIDJSON_UINT64_C2(0x9c400000, 0x00000000), RAPIDJSON_UINT64_C2(0xe8d4a510, 0x00000000), | ||
| 190 | + RAPIDJSON_UINT64_C2(0xad78ebc5, 0xac620000), RAPIDJSON_UINT64_C2(0x813f3978, 0xf8940984), | ||
| 191 | + RAPIDJSON_UINT64_C2(0xc097ce7b, 0xc90715b3), RAPIDJSON_UINT64_C2(0x8f7e32ce, 0x7bea5c70), | ||
| 192 | + RAPIDJSON_UINT64_C2(0xd5d238a4, 0xabe98068), RAPIDJSON_UINT64_C2(0x9f4f2726, 0x179a2245), | ||
| 193 | + RAPIDJSON_UINT64_C2(0xed63a231, 0xd4c4fb27), RAPIDJSON_UINT64_C2(0xb0de6538, 0x8cc8ada8), | ||
| 194 | + RAPIDJSON_UINT64_C2(0x83c7088e, 0x1aab65db), RAPIDJSON_UINT64_C2(0xc45d1df9, 0x42711d9a), | ||
| 195 | + RAPIDJSON_UINT64_C2(0x924d692c, 0xa61be758), RAPIDJSON_UINT64_C2(0xda01ee64, 0x1a708dea), | ||
| 196 | + RAPIDJSON_UINT64_C2(0xa26da399, 0x9aef774a), RAPIDJSON_UINT64_C2(0xf209787b, 0xb47d6b85), | ||
| 197 | + RAPIDJSON_UINT64_C2(0xb454e4a1, 0x79dd1877), RAPIDJSON_UINT64_C2(0x865b8692, 0x5b9bc5c2), | ||
| 198 | + RAPIDJSON_UINT64_C2(0xc83553c5, 0xc8965d3d), RAPIDJSON_UINT64_C2(0x952ab45c, 0xfa97a0b3), | ||
| 199 | + RAPIDJSON_UINT64_C2(0xde469fbd, 0x99a05fe3), RAPIDJSON_UINT64_C2(0xa59bc234, 0xdb398c25), | ||
| 200 | + RAPIDJSON_UINT64_C2(0xf6c69a72, 0xa3989f5c), RAPIDJSON_UINT64_C2(0xb7dcbf53, 0x54e9bece), | ||
| 201 | + RAPIDJSON_UINT64_C2(0x88fcf317, 0xf22241e2), RAPIDJSON_UINT64_C2(0xcc20ce9b, 0xd35c78a5), | ||
| 202 | + RAPIDJSON_UINT64_C2(0x98165af3, 0x7b2153df), RAPIDJSON_UINT64_C2(0xe2a0b5dc, 0x971f303a), | ||
| 203 | + RAPIDJSON_UINT64_C2(0xa8d9d153, 0x5ce3b396), RAPIDJSON_UINT64_C2(0xfb9b7cd9, 0xa4a7443c), | ||
| 204 | + RAPIDJSON_UINT64_C2(0xbb764c4c, 0xa7a44410), RAPIDJSON_UINT64_C2(0x8bab8eef, 0xb6409c1a), | ||
| 205 | + RAPIDJSON_UINT64_C2(0xd01fef10, 0xa657842c), RAPIDJSON_UINT64_C2(0x9b10a4e5, 0xe9913129), | ||
| 206 | + RAPIDJSON_UINT64_C2(0xe7109bfb, 0xa19c0c9d), RAPIDJSON_UINT64_C2(0xac2820d9, 0x623bf429), | ||
| 207 | + RAPIDJSON_UINT64_C2(0x80444b5e, 0x7aa7cf85), RAPIDJSON_UINT64_C2(0xbf21e440, 0x03acdd2d), | ||
| 208 | + RAPIDJSON_UINT64_C2(0x8e679c2f, 0x5e44ff8f), RAPIDJSON_UINT64_C2(0xd433179d, 0x9c8cb841), | ||
| 209 | + RAPIDJSON_UINT64_C2(0x9e19db92, 0xb4e31ba9), RAPIDJSON_UINT64_C2(0xeb96bf6e, 0xbadf77d9), | ||
| 210 | + RAPIDJSON_UINT64_C2(0xaf87023b, 0x9bf0ee6b) | ||
| 211 | + }; | ||
| 212 | + static const int16_t kCachedPowers_E[] = { | ||
| 213 | + -1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980, | ||
| 214 | + -954, -927, -901, -874, -847, -821, -794, -768, -741, -715, | ||
| 215 | + -688, -661, -635, -608, -582, -555, -529, -502, -475, -449, | ||
| 216 | + -422, -396, -369, -343, -316, -289, -263, -236, -210, -183, | ||
| 217 | + -157, -130, -103, -77, -50, -24, 3, 30, 56, 83, | ||
| 218 | + 109, 136, 162, 189, 216, 242, 269, 295, 322, 348, | ||
| 219 | + 375, 402, 428, 455, 481, 508, 534, 561, 588, 614, | ||
| 220 | + 641, 667, 694, 720, 747, 774, 800, 827, 853, 880, | ||
| 221 | + 907, 933, 960, 986, 1013, 1039, 1066 | ||
| 222 | + }; | ||
| 223 | + return DiyFp(kCachedPowers_F[index], kCachedPowers_E[index]); | ||
| 224 | +} | ||
| 225 | + | ||
| 226 | +inline DiyFp GetCachedPower(int e, int* K) { | ||
| 227 | + | ||
| 228 | + //int k = static_cast<int>(ceil((-61 - e) * 0.30102999566398114)) + 374; | ||
| 229 | + double dk = (-61 - e) * 0.30102999566398114 + 347; // dk must be positive, so can do ceiling in positive | ||
| 230 | + int k = static_cast<int>(dk); | ||
| 231 | + if (dk - k > 0.0) | ||
| 232 | + k++; | ||
| 233 | + | ||
| 234 | + unsigned index = static_cast<unsigned>((k >> 3) + 1); | ||
| 235 | + *K = -(-348 + static_cast<int>(index << 3)); // decimal exponent no need lookup table | ||
| 236 | + | ||
| 237 | + return GetCachedPowerByIndex(index); | ||
| 238 | +} | ||
| 239 | + | ||
| 240 | +inline DiyFp GetCachedPower10(int exp, int *outExp) { | ||
| 241 | + unsigned index = (static_cast<unsigned>(exp) + 348u) / 8u; | ||
| 242 | + *outExp = -348 + static_cast<int>(index) * 8; | ||
| 243 | + return GetCachedPowerByIndex(index); | ||
| 244 | + } | ||
| 245 | + | ||
| 246 | +#ifdef __GNUC__ | ||
| 247 | +RAPIDJSON_DIAG_POP | ||
| 248 | +#endif | ||
| 249 | + | ||
| 250 | +#ifdef __clang__ | ||
| 251 | +RAPIDJSON_DIAG_POP | ||
| 252 | +RAPIDJSON_DIAG_OFF(padded) | ||
| 253 | +#endif | ||
| 254 | + | ||
| 255 | +} // namespace internal | ||
| 256 | +RAPIDJSON_NAMESPACE_END | ||
| 257 | + | ||
| 258 | +#endif // RAPIDJSON_DIYFP_H_ |
PaperGraph/include/rapidjson/internal/dtoa.h
0 → 100644
| 1 | +// Tencent is pleased to support the open source community by making RapidJSON available. | ||
| 2 | +// | ||
| 3 | +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. | ||
| 4 | +// | ||
| 5 | +// Licensed under the MIT License (the "License"); you may not use this file except | ||
| 6 | +// in compliance with the License. You may obtain a copy of the License at | ||
| 7 | +// | ||
| 8 | +// http://opensource.org/licenses/MIT | ||
| 9 | +// | ||
| 10 | +// Unless required by applicable law or agreed to in writing, software distributed | ||
| 11 | +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR | ||
| 12 | +// CONDITIONS OF ANY KIND, either express or implied. See the License for the | ||
| 13 | +// specific language governing permissions and limitations under the License. | ||
| 14 | + | ||
| 15 | +// This is a C++ header-only implementation of Grisu2 algorithm from the publication: | ||
| 16 | +// Loitsch, Florian. "Printing floating-point numbers quickly and accurately with | ||
| 17 | +// integers." ACM Sigplan Notices 45.6 (2010): 233-243. | ||
| 18 | + | ||
| 19 | +#ifndef RAPIDJSON_DTOA_ | ||
| 20 | +#define RAPIDJSON_DTOA_ | ||
| 21 | + | ||
| 22 | +#include "itoa.h" // GetDigitsLut() | ||
| 23 | +#include "diyfp.h" | ||
| 24 | +#include "ieee754.h" | ||
| 25 | + | ||
| 26 | +RAPIDJSON_NAMESPACE_BEGIN | ||
| 27 | +namespace internal { | ||
| 28 | + | ||
| 29 | +#ifdef __GNUC__ | ||
| 30 | +RAPIDJSON_DIAG_PUSH | ||
| 31 | +RAPIDJSON_DIAG_OFF(effc++) | ||
| 32 | +RAPIDJSON_DIAG_OFF(array-bounds) // some gcc versions generate wrong warnings https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59124 | ||
| 33 | +#endif | ||
| 34 | + | ||
| 35 | +inline void GrisuRound(char* buffer, int len, uint64_t delta, uint64_t rest, uint64_t ten_kappa, uint64_t wp_w) { | ||
| 36 | + while (rest < wp_w && delta - rest >= ten_kappa && | ||
| 37 | + (rest + ten_kappa < wp_w || /// closer | ||
| 38 | + wp_w - rest > rest + ten_kappa - wp_w)) { | ||
| 39 | + buffer[len - 1]--; | ||
| 40 | + rest += ten_kappa; | ||
| 41 | + } | ||
| 42 | +} | ||
| 43 | + | ||
| 44 | +inline int CountDecimalDigit32(uint32_t n) { | ||
| 45 | + // Simple pure C++ implementation was faster than __builtin_clz version in this situation. | ||
| 46 | + if (n < 10) return 1; | ||
| 47 | + if (n < 100) return 2; | ||
| 48 | + if (n < 1000) return 3; | ||
| 49 | + if (n < 10000) return 4; | ||
| 50 | + if (n < 100000) return 5; | ||
| 51 | + if (n < 1000000) return 6; | ||
| 52 | + if (n < 10000000) return 7; | ||
| 53 | + if (n < 100000000) return 8; | ||
| 54 | + // Will not reach 10 digits in DigitGen() | ||
| 55 | + //if (n < 1000000000) return 9; | ||
| 56 | + //return 10; | ||
| 57 | + return 9; | ||
| 58 | +} | ||
| 59 | + | ||
| 60 | +inline void DigitGen(const DiyFp& W, const DiyFp& Mp, uint64_t delta, char* buffer, int* len, int* K) { | ||
| 61 | + static const uint32_t kPow10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 }; | ||
| 62 | + const DiyFp one(uint64_t(1) << -Mp.e, Mp.e); | ||
| 63 | + const DiyFp wp_w = Mp - W; | ||
| 64 | + uint32_t p1 = static_cast<uint32_t>(Mp.f >> -one.e); | ||
| 65 | + uint64_t p2 = Mp.f & (one.f - 1); | ||
| 66 | + int kappa = CountDecimalDigit32(p1); // kappa in [0, 9] | ||
| 67 | + *len = 0; | ||
| 68 | + | ||
| 69 | + while (kappa > 0) { | ||
| 70 | + uint32_t d = 0; | ||
| 71 | + switch (kappa) { | ||
| 72 | + case 9: d = p1 / 100000000; p1 %= 100000000; break; | ||
| 73 | + case 8: d = p1 / 10000000; p1 %= 10000000; break; | ||
| 74 | + case 7: d = p1 / 1000000; p1 %= 1000000; break; | ||
| 75 | + case 6: d = p1 / 100000; p1 %= 100000; break; | ||
| 76 | + case 5: d = p1 / 10000; p1 %= 10000; break; | ||
| 77 | + case 4: d = p1 / 1000; p1 %= 1000; break; | ||
| 78 | + case 3: d = p1 / 100; p1 %= 100; break; | ||
| 79 | + case 2: d = p1 / 10; p1 %= 10; break; | ||
| 80 | + case 1: d = p1; p1 = 0; break; | ||
| 81 | + default:; | ||
| 82 | + } | ||
| 83 | + if (d || *len) | ||
| 84 | + buffer[(*len)++] = static_cast<char>('0' + static_cast<char>(d)); | ||
| 85 | + kappa--; | ||
| 86 | + uint64_t tmp = (static_cast<uint64_t>(p1) << -one.e) + p2; | ||
| 87 | + if (tmp <= delta) { | ||
| 88 | + *K += kappa; | ||
| 89 | + GrisuRound(buffer, *len, delta, tmp, static_cast<uint64_t>(kPow10[kappa]) << -one.e, wp_w.f); | ||
| 90 | + return; | ||
| 91 | + } | ||
| 92 | + } | ||
| 93 | + | ||
| 94 | + // kappa = 0 | ||
| 95 | + for (;;) { | ||
| 96 | + p2 *= 10; | ||
| 97 | + delta *= 10; | ||
| 98 | + char d = static_cast<char>(p2 >> -one.e); | ||
| 99 | + if (d || *len) | ||
| 100 | + buffer[(*len)++] = static_cast<char>('0' + d); | ||
| 101 | + p2 &= one.f - 1; | ||
| 102 | + kappa--; | ||
| 103 | + if (p2 < delta) { | ||
| 104 | + *K += kappa; | ||
| 105 | + int index = -kappa; | ||
| 106 | + GrisuRound(buffer, *len, delta, p2, one.f, wp_w.f * (index < 9 ? kPow10[index] : 0)); | ||
| 107 | + return; | ||
| 108 | + } | ||
| 109 | + } | ||
| 110 | +} | ||
| 111 | + | ||
| 112 | +inline void Grisu2(double value, char* buffer, int* length, int* K) { | ||
| 113 | + const DiyFp v(value); | ||
| 114 | + DiyFp w_m, w_p; | ||
| 115 | + v.NormalizedBoundaries(&w_m, &w_p); | ||
| 116 | + | ||
| 117 | + const DiyFp c_mk = GetCachedPower(w_p.e, K); | ||
| 118 | + const DiyFp W = v.Normalize() * c_mk; | ||
| 119 | + DiyFp Wp = w_p * c_mk; | ||
| 120 | + DiyFp Wm = w_m * c_mk; | ||
| 121 | + Wm.f++; | ||
| 122 | + Wp.f--; | ||
| 123 | + DigitGen(W, Wp, Wp.f - Wm.f, buffer, length, K); | ||
| 124 | +} | ||
| 125 | + | ||
| 126 | +inline char* WriteExponent(int K, char* buffer) { | ||
| 127 | + if (K < 0) { | ||
| 128 | + *buffer++ = '-'; | ||
| 129 | + K = -K; | ||
| 130 | + } | ||
| 131 | + | ||
| 132 | + if (K >= 100) { | ||
| 133 | + *buffer++ = static_cast<char>('0' + static_cast<char>(K / 100)); | ||
| 134 | + K %= 100; | ||
| 135 | + const char* d = GetDigitsLut() + K * 2; | ||
| 136 | + *buffer++ = d[0]; | ||
| 137 | + *buffer++ = d[1]; | ||
| 138 | + } | ||
| 139 | + else if (K >= 10) { | ||
| 140 | + const char* d = GetDigitsLut() + K * 2; | ||
| 141 | + *buffer++ = d[0]; | ||
| 142 | + *buffer++ = d[1]; | ||
| 143 | + } | ||
| 144 | + else | ||
| 145 | + *buffer++ = static_cast<char>('0' + static_cast<char>(K)); | ||
| 146 | + | ||
| 147 | + return buffer; | ||
| 148 | +} | ||
| 149 | + | ||
| 150 | +inline char* Prettify(char* buffer, int length, int k, int maxDecimalPlaces) { | ||
| 151 | + const int kk = length + k; // 10^(kk-1) <= v < 10^kk | ||
| 152 | + | ||
| 153 | + if (0 <= k && kk <= 21) { | ||
| 154 | + // 1234e7 -> 12340000000 | ||
| 155 | + for (int i = length; i < kk; i++) | ||
| 156 | + buffer[i] = '0'; | ||
| 157 | + buffer[kk] = '.'; | ||
| 158 | + buffer[kk + 1] = '0'; | ||
| 159 | + return &buffer[kk + 2]; | ||
| 160 | + } | ||
| 161 | + else if (0 < kk && kk <= 21) { | ||
| 162 | + // 1234e-2 -> 12.34 | ||
| 163 | + std::memmove(&buffer[kk + 1], &buffer[kk], static_cast<size_t>(length - kk)); | ||
| 164 | + buffer[kk] = '.'; | ||
| 165 | + if (0 > k + maxDecimalPlaces) { | ||
| 166 | + // When maxDecimalPlaces = 2, 1.2345 -> 1.23, 1.102 -> 1.1 | ||
| 167 | + // Remove extra trailing zeros (at least one) after truncation. | ||
| 168 | + for (int i = kk + maxDecimalPlaces; i > kk + 1; i--) | ||
| 169 | + if (buffer[i] != '0') | ||
| 170 | + return &buffer[i + 1]; | ||
| 171 | + return &buffer[kk + 2]; // Reserve one zero | ||
| 172 | + } | ||
| 173 | + else | ||
| 174 | + return &buffer[length + 1]; | ||
| 175 | + } | ||
| 176 | + else if (-6 < kk && kk <= 0) { | ||
| 177 | + // 1234e-6 -> 0.001234 | ||
| 178 | + const int offset = 2 - kk; | ||
| 179 | + std::memmove(&buffer[offset], &buffer[0], static_cast<size_t>(length)); | ||
| 180 | + buffer[0] = '0'; | ||
| 181 | + buffer[1] = '.'; | ||
| 182 | + for (int i = 2; i < offset; i++) | ||
| 183 | + buffer[i] = '0'; | ||
| 184 | + if (length - kk > maxDecimalPlaces) { | ||
| 185 | + // When maxDecimalPlaces = 2, 0.123 -> 0.12, 0.102 -> 0.1 | ||
| 186 | + // Remove extra trailing zeros (at least one) after truncation. | ||
| 187 | + for (int i = maxDecimalPlaces + 1; i > 2; i--) | ||
| 188 | + if (buffer[i] != '0') | ||
| 189 | + return &buffer[i + 1]; | ||
| 190 | + return &buffer[3]; // Reserve one zero | ||
| 191 | + } | ||
| 192 | + else | ||
| 193 | + return &buffer[length + offset]; | ||
| 194 | + } | ||
| 195 | + else if (kk < -maxDecimalPlaces) { | ||
| 196 | + // Truncate to zero | ||
| 197 | + buffer[0] = '0'; | ||
| 198 | + buffer[1] = '.'; | ||
| 199 | + buffer[2] = '0'; | ||
| 200 | + return &buffer[3]; | ||
| 201 | + } | ||
| 202 | + else if (length == 1) { | ||
| 203 | + // 1e30 | ||
| 204 | + buffer[1] = 'e'; | ||
| 205 | + return WriteExponent(kk - 1, &buffer[2]); | ||
| 206 | + } | ||
| 207 | + else { | ||
| 208 | + // 1234e30 -> 1.234e33 | ||
| 209 | + std::memmove(&buffer[2], &buffer[1], static_cast<size_t>(length - 1)); | ||
| 210 | + buffer[1] = '.'; | ||
| 211 | + buffer[length + 1] = 'e'; | ||
| 212 | + return WriteExponent(kk - 1, &buffer[0 + length + 2]); | ||
| 213 | + } | ||
| 214 | +} | ||
| 215 | + | ||
| 216 | +inline char* dtoa(double value, char* buffer, int maxDecimalPlaces = 324) { | ||
| 217 | + RAPIDJSON_ASSERT(maxDecimalPlaces >= 1); | ||
| 218 | + Double d(value); | ||
| 219 | + if (d.IsZero()) { | ||
| 220 | + if (d.Sign()) | ||
| 221 | + *buffer++ = '-'; // -0.0, Issue #289 | ||
| 222 | + buffer[0] = '0'; | ||
| 223 | + buffer[1] = '.'; | ||
| 224 | + buffer[2] = '0'; | ||
| 225 | + return &buffer[3]; | ||
| 226 | + } | ||
| 227 | + else { | ||
| 228 | + if (value < 0) { | ||
| 229 | + *buffer++ = '-'; | ||
| 230 | + value = -value; | ||
| 231 | + } | ||
| 232 | + int length, K; | ||
| 233 | + Grisu2(value, buffer, &length, &K); | ||
| 234 | + return Prettify(buffer, length, K, maxDecimalPlaces); | ||
| 235 | + } | ||
| 236 | +} | ||
| 237 | + | ||
| 238 | +#ifdef __GNUC__ | ||
| 239 | +RAPIDJSON_DIAG_POP | ||
| 240 | +#endif | ||
| 241 | + | ||
| 242 | +} // namespace internal | ||
| 243 | +RAPIDJSON_NAMESPACE_END | ||
| 244 | + | ||
| 245 | +#endif // RAPIDJSON_DTOA_ |
| 1 | +// Tencent is pleased to support the open source community by making RapidJSON available. | ||
| 2 | +// | ||
| 3 | +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. | ||
| 4 | +// | ||
| 5 | +// Licensed under the MIT License (the "License"); you may not use this file except | ||
| 6 | +// in compliance with the License. You may obtain a copy of the License at | ||
| 7 | +// | ||
| 8 | +// http://opensource.org/licenses/MIT | ||
| 9 | +// | ||
| 10 | +// Unless required by applicable law or agreed to in writing, software distributed | ||
| 11 | +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR | ||
| 12 | +// CONDITIONS OF ANY KIND, either express or implied. See the License for the | ||
| 13 | +// specific language governing permissions and limitations under the License. | ||
| 14 | + | ||
| 15 | +#ifndef RAPIDJSON_IEEE754_ | ||
| 16 | +#define RAPIDJSON_IEEE754_ | ||
| 17 | + | ||
| 18 | +#include "../rapidjson.h" | ||
| 19 | + | ||
| 20 | +RAPIDJSON_NAMESPACE_BEGIN | ||
| 21 | +namespace internal { | ||
| 22 | + | ||
| 23 | +class Double { | ||
| 24 | +public: | ||
| 25 | + Double() {} | ||
| 26 | + Double(double d) : d_(d) {} | ||
| 27 | + Double(uint64_t u) : u_(u) {} | ||
| 28 | + | ||
| 29 | + double Value() const { return d_; } | ||
| 30 | + uint64_t Uint64Value() const { return u_; } | ||
| 31 | + | ||
| 32 | + double NextPositiveDouble() const { | ||
| 33 | + RAPIDJSON_ASSERT(!Sign()); | ||
| 34 | + return Double(u_ + 1).Value(); | ||
| 35 | + } | ||
| 36 | + | ||
| 37 | + bool Sign() const { return (u_ & kSignMask) != 0; } | ||
| 38 | + uint64_t Significand() const { return u_ & kSignificandMask; } | ||
| 39 | + int Exponent() const { return static_cast<int>(((u_ & kExponentMask) >> kSignificandSize) - kExponentBias); } | ||
| 40 | + | ||
| 41 | + bool IsNan() const { return (u_ & kExponentMask) == kExponentMask && Significand() != 0; } | ||
| 42 | + bool IsInf() const { return (u_ & kExponentMask) == kExponentMask && Significand() == 0; } | ||
| 43 | + bool IsNanOrInf() const { return (u_ & kExponentMask) == kExponentMask; } | ||
| 44 | + bool IsNormal() const { return (u_ & kExponentMask) != 0 || Significand() == 0; } | ||
| 45 | + bool IsZero() const { return (u_ & (kExponentMask | kSignificandMask)) == 0; } | ||
| 46 | + | ||
| 47 | + uint64_t IntegerSignificand() const { return IsNormal() ? Significand() | kHiddenBit : Significand(); } | ||
| 48 | + int IntegerExponent() const { return (IsNormal() ? Exponent() : kDenormalExponent) - kSignificandSize; } | ||
| 49 | + uint64_t ToBias() const { return (u_ & kSignMask) ? ~u_ + 1 : u_ | kSignMask; } | ||
| 50 | + | ||
| 51 | + static int EffectiveSignificandSize(int order) { | ||
| 52 | + if (order >= -1021) | ||
| 53 | + return 53; | ||
| 54 | + else if (order <= -1074) | ||
| 55 | + return 0; | ||
| 56 | + else | ||
| 57 | + return order + 1074; | ||
| 58 | + } | ||
| 59 | + | ||
| 60 | +private: | ||
| 61 | + static const int kSignificandSize = 52; | ||
| 62 | + static const int kExponentBias = 0x3FF; | ||
| 63 | + static const int kDenormalExponent = 1 - kExponentBias; | ||
| 64 | + static const uint64_t kSignMask = RAPIDJSON_UINT64_C2(0x80000000, 0x00000000); | ||
| 65 | + static const uint64_t kExponentMask = RAPIDJSON_UINT64_C2(0x7FF00000, 0x00000000); | ||
| 66 | + static const uint64_t kSignificandMask = RAPIDJSON_UINT64_C2(0x000FFFFF, 0xFFFFFFFF); | ||
| 67 | + static const uint64_t kHiddenBit = RAPIDJSON_UINT64_C2(0x00100000, 0x00000000); | ||
| 68 | + | ||
| 69 | + union { | ||
| 70 | + double d_; | ||
| 71 | + uint64_t u_; | ||
| 72 | + }; | ||
| 73 | +}; | ||
| 74 | + | ||
| 75 | +} // namespace internal | ||
| 76 | +RAPIDJSON_NAMESPACE_END | ||
| 77 | + | ||
| 78 | +#endif // RAPIDJSON_IEEE754_ |
PaperGraph/include/rapidjson/internal/itoa.h
0 → 100644
| 1 | +// Tencent is pleased to support the open source community by making RapidJSON available. | ||
| 2 | +// | ||
| 3 | +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. | ||
| 4 | +// | ||
| 5 | +// Licensed under the MIT License (the "License"); you may not use this file except | ||
| 6 | +// in compliance with the License. You may obtain a copy of the License at | ||
| 7 | +// | ||
| 8 | +// http://opensource.org/licenses/MIT | ||
| 9 | +// | ||
| 10 | +// Unless required by applicable law or agreed to in writing, software distributed | ||
| 11 | +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR | ||
| 12 | +// CONDITIONS OF ANY KIND, either express or implied. See the License for the | ||
| 13 | +// specific language governing permissions and limitations under the License. | ||
| 14 | + | ||
| 15 | +#ifndef RAPIDJSON_ITOA_ | ||
| 16 | +#define RAPIDJSON_ITOA_ | ||
| 17 | + | ||
| 18 | +#include "../rapidjson.h" | ||
| 19 | + | ||
| 20 | +RAPIDJSON_NAMESPACE_BEGIN | ||
| 21 | +namespace internal { | ||
| 22 | + | ||
| 23 | +inline const char* GetDigitsLut() { | ||
| 24 | + static const char cDigitsLut[200] = { | ||
| 25 | + '0','0','0','1','0','2','0','3','0','4','0','5','0','6','0','7','0','8','0','9', | ||
| 26 | + '1','0','1','1','1','2','1','3','1','4','1','5','1','6','1','7','1','8','1','9', | ||
| 27 | + '2','0','2','1','2','2','2','3','2','4','2','5','2','6','2','7','2','8','2','9', | ||
| 28 | + '3','0','3','1','3','2','3','3','3','4','3','5','3','6','3','7','3','8','3','9', | ||
| 29 | + '4','0','4','1','4','2','4','3','4','4','4','5','4','6','4','7','4','8','4','9', | ||
| 30 | + '5','0','5','1','5','2','5','3','5','4','5','5','5','6','5','7','5','8','5','9', | ||
| 31 | + '6','0','6','1','6','2','6','3','6','4','6','5','6','6','6','7','6','8','6','9', | ||
| 32 | + '7','0','7','1','7','2','7','3','7','4','7','5','7','6','7','7','7','8','7','9', | ||
| 33 | + '8','0','8','1','8','2','8','3','8','4','8','5','8','6','8','7','8','8','8','9', | ||
| 34 | + '9','0','9','1','9','2','9','3','9','4','9','5','9','6','9','7','9','8','9','9' | ||
| 35 | + }; | ||
| 36 | + return cDigitsLut; | ||
| 37 | +} | ||
| 38 | + | ||
| 39 | +inline char* u32toa(uint32_t value, char* buffer) { | ||
| 40 | + const char* cDigitsLut = GetDigitsLut(); | ||
| 41 | + | ||
| 42 | + if (value < 10000) { | ||
| 43 | + const uint32_t d1 = (value / 100) << 1; | ||
| 44 | + const uint32_t d2 = (value % 100) << 1; | ||
| 45 | + | ||
| 46 | + if (value >= 1000) | ||
| 47 | + *buffer++ = cDigitsLut[d1]; | ||
| 48 | + if (value >= 100) | ||
| 49 | + *buffer++ = cDigitsLut[d1 + 1]; | ||
| 50 | + if (value >= 10) | ||
| 51 | + *buffer++ = cDigitsLut[d2]; | ||
| 52 | + *buffer++ = cDigitsLut[d2 + 1]; | ||
| 53 | + } | ||
| 54 | + else if (value < 100000000) { | ||
| 55 | + // value = bbbbcccc | ||
| 56 | + const uint32_t b = value / 10000; | ||
| 57 | + const uint32_t c = value % 10000; | ||
| 58 | + | ||
| 59 | + const uint32_t d1 = (b / 100) << 1; | ||
| 60 | + const uint32_t d2 = (b % 100) << 1; | ||
| 61 | + | ||
| 62 | + const uint32_t d3 = (c / 100) << 1; | ||
| 63 | + const uint32_t d4 = (c % 100) << 1; | ||
| 64 | + | ||
| 65 | + if (value >= 10000000) | ||
| 66 | + *buffer++ = cDigitsLut[d1]; | ||
| 67 | + if (value >= 1000000) | ||
| 68 | + *buffer++ = cDigitsLut[d1 + 1]; | ||
| 69 | + if (value >= 100000) | ||
| 70 | + *buffer++ = cDigitsLut[d2]; | ||
| 71 | + *buffer++ = cDigitsLut[d2 + 1]; | ||
| 72 | + | ||
| 73 | + *buffer++ = cDigitsLut[d3]; | ||
| 74 | + *buffer++ = cDigitsLut[d3 + 1]; | ||
| 75 | + *buffer++ = cDigitsLut[d4]; | ||
| 76 | + *buffer++ = cDigitsLut[d4 + 1]; | ||
| 77 | + } | ||
| 78 | + else { | ||
| 79 | + // value = aabbbbcccc in decimal | ||
| 80 | + | ||
| 81 | + const uint32_t a = value / 100000000; // 1 to 42 | ||
| 82 | + value %= 100000000; | ||
| 83 | + | ||
| 84 | + if (a >= 10) { | ||
| 85 | + const unsigned i = a << 1; | ||
| 86 | + *buffer++ = cDigitsLut[i]; | ||
| 87 | + *buffer++ = cDigitsLut[i + 1]; | ||
| 88 | + } | ||
| 89 | + else | ||
| 90 | + *buffer++ = static_cast<char>('0' + static_cast<char>(a)); | ||
| 91 | + | ||
| 92 | + const uint32_t b = value / 10000; // 0 to 9999 | ||
| 93 | + const uint32_t c = value % 10000; // 0 to 9999 | ||
| 94 | + | ||
| 95 | + const uint32_t d1 = (b / 100) << 1; | ||
| 96 | + const uint32_t d2 = (b % 100) << 1; | ||
| 97 | + | ||
| 98 | + const uint32_t d3 = (c / 100) << 1; | ||
| 99 | + const uint32_t d4 = (c % 100) << 1; | ||
| 100 | + | ||
| 101 | + *buffer++ = cDigitsLut[d1]; | ||
| 102 | + *buffer++ = cDigitsLut[d1 + 1]; | ||
| 103 | + *buffer++ = cDigitsLut[d2]; | ||
| 104 | + *buffer++ = cDigitsLut[d2 + 1]; | ||
| 105 | + *buffer++ = cDigitsLut[d3]; | ||
| 106 | + *buffer++ = cDigitsLut[d3 + 1]; | ||
| 107 | + *buffer++ = cDigitsLut[d4]; | ||
| 108 | + *buffer++ = cDigitsLut[d4 + 1]; | ||
| 109 | + } | ||
| 110 | + return buffer; | ||
| 111 | +} | ||
| 112 | + | ||
| 113 | +inline char* i32toa(int32_t value, char* buffer) { | ||
| 114 | + uint32_t u = static_cast<uint32_t>(value); | ||
| 115 | + if (value < 0) { | ||
| 116 | + *buffer++ = '-'; | ||
| 117 | + u = ~u + 1; | ||
| 118 | + } | ||
| 119 | + | ||
| 120 | + return u32toa(u, buffer); | ||
| 121 | +} | ||
| 122 | + | ||
| 123 | +inline char* u64toa(uint64_t value, char* buffer) { | ||
| 124 | + const char* cDigitsLut = GetDigitsLut(); | ||
| 125 | + const uint64_t kTen8 = 100000000; | ||
| 126 | + const uint64_t kTen9 = kTen8 * 10; | ||
| 127 | + const uint64_t kTen10 = kTen8 * 100; | ||
| 128 | + const uint64_t kTen11 = kTen8 * 1000; | ||
| 129 | + const uint64_t kTen12 = kTen8 * 10000; | ||
| 130 | + const uint64_t kTen13 = kTen8 * 100000; | ||
| 131 | + const uint64_t kTen14 = kTen8 * 1000000; | ||
| 132 | + const uint64_t kTen15 = kTen8 * 10000000; | ||
| 133 | + const uint64_t kTen16 = kTen8 * kTen8; | ||
| 134 | + | ||
| 135 | + if (value < kTen8) { | ||
| 136 | + uint32_t v = static_cast<uint32_t>(value); | ||
| 137 | + if (v < 10000) { | ||
| 138 | + const uint32_t d1 = (v / 100) << 1; | ||
| 139 | + const uint32_t d2 = (v % 100) << 1; | ||
| 140 | + | ||
| 141 | + if (v >= 1000) | ||
| 142 | + *buffer++ = cDigitsLut[d1]; | ||
| 143 | + if (v >= 100) | ||
| 144 | + *buffer++ = cDigitsLut[d1 + 1]; | ||
| 145 | + if (v >= 10) | ||
| 146 | + *buffer++ = cDigitsLut[d2]; | ||
| 147 | + *buffer++ = cDigitsLut[d2 + 1]; | ||
| 148 | + } | ||
| 149 | + else { | ||
| 150 | + // value = bbbbcccc | ||
| 151 | + const uint32_t b = v / 10000; | ||
| 152 | + const uint32_t c = v % 10000; | ||
| 153 | + | ||
| 154 | + const uint32_t d1 = (b / 100) << 1; | ||
| 155 | + const uint32_t d2 = (b % 100) << 1; | ||
| 156 | + | ||
| 157 | + const uint32_t d3 = (c / 100) << 1; | ||
| 158 | + const uint32_t d4 = (c % 100) << 1; | ||
| 159 | + | ||
| 160 | + if (value >= 10000000) | ||
| 161 | + *buffer++ = cDigitsLut[d1]; | ||
| 162 | + if (value >= 1000000) | ||
| 163 | + *buffer++ = cDigitsLut[d1 + 1]; | ||
| 164 | + if (value >= 100000) | ||
| 165 | + *buffer++ = cDigitsLut[d2]; | ||
| 166 | + *buffer++ = cDigitsLut[d2 + 1]; | ||
| 167 | + | ||
| 168 | + *buffer++ = cDigitsLut[d3]; | ||
| 169 | + *buffer++ = cDigitsLut[d3 + 1]; | ||
| 170 | + *buffer++ = cDigitsLut[d4]; | ||
| 171 | + *buffer++ = cDigitsLut[d4 + 1]; | ||
| 172 | + } | ||
| 173 | + } | ||
| 174 | + else if (value < kTen16) { | ||
| 175 | + const uint32_t v0 = static_cast<uint32_t>(value / kTen8); | ||
| 176 | + const uint32_t v1 = static_cast<uint32_t>(value % kTen8); | ||
| 177 | + | ||
| 178 | + const uint32_t b0 = v0 / 10000; | ||
| 179 | + const uint32_t c0 = v0 % 10000; | ||
| 180 | + | ||
| 181 | + const uint32_t d1 = (b0 / 100) << 1; | ||
| 182 | + const uint32_t d2 = (b0 % 100) << 1; | ||
| 183 | + | ||
| 184 | + const uint32_t d3 = (c0 / 100) << 1; | ||
| 185 | + const uint32_t d4 = (c0 % 100) << 1; | ||
| 186 | + | ||
| 187 | + const uint32_t b1 = v1 / 10000; | ||
| 188 | + const uint32_t c1 = v1 % 10000; | ||
| 189 | + | ||
| 190 | + const uint32_t d5 = (b1 / 100) << 1; | ||
| 191 | + const uint32_t d6 = (b1 % 100) << 1; | ||
| 192 | + | ||
| 193 | + const uint32_t d7 = (c1 / 100) << 1; | ||
| 194 | + const uint32_t d8 = (c1 % 100) << 1; | ||
| 195 | + | ||
| 196 | + if (value >= kTen15) | ||
| 197 | + *buffer++ = cDigitsLut[d1]; | ||
| 198 | + if (value >= kTen14) | ||
| 199 | + *buffer++ = cDigitsLut[d1 + 1]; | ||
| 200 | + if (value >= kTen13) | ||
| 201 | + *buffer++ = cDigitsLut[d2]; | ||
| 202 | + if (value >= kTen12) | ||
| 203 | + *buffer++ = cDigitsLut[d2 + 1]; | ||
| 204 | + if (value >= kTen11) | ||
| 205 | + *buffer++ = cDigitsLut[d3]; | ||
| 206 | + if (value >= kTen10) | ||
| 207 | + *buffer++ = cDigitsLut[d3 + 1]; | ||
| 208 | + if (value >= kTen9) | ||
| 209 | + *buffer++ = cDigitsLut[d4]; | ||
| 210 | + if (value >= kTen8) | ||
| 211 | + *buffer++ = cDigitsLut[d4 + 1]; | ||
| 212 | + | ||
| 213 | + *buffer++ = cDigitsLut[d5]; | ||
| 214 | + *buffer++ = cDigitsLut[d5 + 1]; | ||
| 215 | + *buffer++ = cDigitsLut[d6]; | ||
| 216 | + *buffer++ = cDigitsLut[d6 + 1]; | ||
| 217 | + *buffer++ = cDigitsLut[d7]; | ||
| 218 | + *buffer++ = cDigitsLut[d7 + 1]; | ||
| 219 | + *buffer++ = cDigitsLut[d8]; | ||
| 220 | + *buffer++ = cDigitsLut[d8 + 1]; | ||
| 221 | + } | ||
| 222 | + else { | ||
| 223 | + const uint32_t a = static_cast<uint32_t>(value / kTen16); // 1 to 1844 | ||
| 224 | + value %= kTen16; | ||
| 225 | + | ||
| 226 | + if (a < 10) | ||
| 227 | + *buffer++ = static_cast<char>('0' + static_cast<char>(a)); | ||
| 228 | + else if (a < 100) { | ||
| 229 | + const uint32_t i = a << 1; | ||
| 230 | + *buffer++ = cDigitsLut[i]; | ||
| 231 | + *buffer++ = cDigitsLut[i + 1]; | ||
| 232 | + } | ||
| 233 | + else if (a < 1000) { | ||
| 234 | + *buffer++ = static_cast<char>('0' + static_cast<char>(a / 100)); | ||
| 235 | + | ||
| 236 | + const uint32_t i = (a % 100) << 1; | ||
| 237 | + *buffer++ = cDigitsLut[i]; | ||
| 238 | + *buffer++ = cDigitsLut[i + 1]; | ||
| 239 | + } | ||
| 240 | + else { | ||
| 241 | + const uint32_t i = (a / 100) << 1; | ||
| 242 | + const uint32_t j = (a % 100) << 1; | ||
| 243 | + *buffer++ = cDigitsLut[i]; | ||
| 244 | + *buffer++ = cDigitsLut[i + 1]; | ||
| 245 | + *buffer++ = cDigitsLut[j]; | ||
| 246 | + *buffer++ = cDigitsLut[j + 1]; | ||
| 247 | + } | ||
| 248 | + | ||
| 249 | + const uint32_t v0 = static_cast<uint32_t>(value / kTen8); | ||
| 250 | + const uint32_t v1 = static_cast<uint32_t>(value % kTen8); | ||
| 251 | + | ||
| 252 | + const uint32_t b0 = v0 / 10000; | ||
| 253 | + const uint32_t c0 = v0 % 10000; | ||
| 254 | + | ||
| 255 | + const uint32_t d1 = (b0 / 100) << 1; | ||
| 256 | + const uint32_t d2 = (b0 % 100) << 1; | ||
| 257 | + | ||
| 258 | + const uint32_t d3 = (c0 / 100) << 1; | ||
| 259 | + const uint32_t d4 = (c0 % 100) << 1; | ||
| 260 | + | ||
| 261 | + const uint32_t b1 = v1 / 10000; | ||
| 262 | + const uint32_t c1 = v1 % 10000; | ||
| 263 | + | ||
| 264 | + const uint32_t d5 = (b1 / 100) << 1; | ||
| 265 | + const uint32_t d6 = (b1 % 100) << 1; | ||
| 266 | + | ||
| 267 | + const uint32_t d7 = (c1 / 100) << 1; | ||
| 268 | + const uint32_t d8 = (c1 % 100) << 1; | ||
| 269 | + | ||
| 270 | + *buffer++ = cDigitsLut[d1]; | ||
| 271 | + *buffer++ = cDigitsLut[d1 + 1]; | ||
| 272 | + *buffer++ = cDigitsLut[d2]; | ||
| 273 | + *buffer++ = cDigitsLut[d2 + 1]; | ||
| 274 | + *buffer++ = cDigitsLut[d3]; | ||
| 275 | + *buffer++ = cDigitsLut[d3 + 1]; | ||
| 276 | + *buffer++ = cDigitsLut[d4]; | ||
| 277 | + *buffer++ = cDigitsLut[d4 + 1]; | ||
| 278 | + *buffer++ = cDigitsLut[d5]; | ||
| 279 | + *buffer++ = cDigitsLut[d5 + 1]; | ||
| 280 | + *buffer++ = cDigitsLut[d6]; | ||
| 281 | + *buffer++ = cDigitsLut[d6 + 1]; | ||
| 282 | + *buffer++ = cDigitsLut[d7]; | ||
| 283 | + *buffer++ = cDigitsLut[d7 + 1]; | ||
| 284 | + *buffer++ = cDigitsLut[d8]; | ||
| 285 | + *buffer++ = cDigitsLut[d8 + 1]; | ||
| 286 | + } | ||
| 287 | + | ||
| 288 | + return buffer; | ||
| 289 | +} | ||
| 290 | + | ||
| 291 | +inline char* i64toa(int64_t value, char* buffer) { | ||
| 292 | + uint64_t u = static_cast<uint64_t>(value); | ||
| 293 | + if (value < 0) { | ||
| 294 | + *buffer++ = '-'; | ||
| 295 | + u = ~u + 1; | ||
| 296 | + } | ||
| 297 | + | ||
| 298 | + return u64toa(u, buffer); | ||
| 299 | +} | ||
| 300 | + | ||
| 301 | +} // namespace internal | ||
| 302 | +RAPIDJSON_NAMESPACE_END | ||
| 303 | + | ||
| 304 | +#endif // RAPIDJSON_ITOA_ |
PaperGraph/include/rapidjson/internal/meta.h
0 → 100644
| 1 | +// Tencent is pleased to support the open source community by making RapidJSON available. | ||
| 2 | +// | ||
| 3 | +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. | ||
| 4 | +// | ||
| 5 | +// Licensed under the MIT License (the "License"); you may not use this file except | ||
| 6 | +// in compliance with the License. You may obtain a copy of the License at | ||
| 7 | +// | ||
| 8 | +// http://opensource.org/licenses/MIT | ||
| 9 | +// | ||
| 10 | +// Unless required by applicable law or agreed to in writing, software distributed | ||
| 11 | +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR | ||
| 12 | +// CONDITIONS OF ANY KIND, either express or implied. See the License for the | ||
| 13 | +// specific language governing permissions and limitations under the License. | ||
| 14 | + | ||
| 15 | +#ifndef RAPIDJSON_INTERNAL_META_H_ | ||
| 16 | +#define RAPIDJSON_INTERNAL_META_H_ | ||
| 17 | + | ||
| 18 | +#include "../rapidjson.h" | ||
| 19 | + | ||
| 20 | +#ifdef __GNUC__ | ||
| 21 | +RAPIDJSON_DIAG_PUSH | ||
| 22 | +RAPIDJSON_DIAG_OFF(effc++) | ||
| 23 | +#endif | ||
| 24 | +#if defined(_MSC_VER) | ||
| 25 | +RAPIDJSON_DIAG_PUSH | ||
| 26 | +RAPIDJSON_DIAG_OFF(6334) | ||
| 27 | +#endif | ||
| 28 | + | ||
| 29 | +#if RAPIDJSON_HAS_CXX11_TYPETRAITS | ||
| 30 | +#include <type_traits> | ||
| 31 | +#endif | ||
| 32 | + | ||
| 33 | +//@cond RAPIDJSON_INTERNAL | ||
| 34 | +RAPIDJSON_NAMESPACE_BEGIN | ||
| 35 | +namespace internal { | ||
| 36 | + | ||
| 37 | +// Helper to wrap/convert arbitrary types to void, useful for arbitrary type matching | ||
| 38 | +template <typename T> struct Void { typedef void Type; }; | ||
| 39 | + | ||
| 40 | +/////////////////////////////////////////////////////////////////////////////// | ||
| 41 | +// BoolType, TrueType, FalseType | ||
| 42 | +// | ||
| 43 | +template <bool Cond> struct BoolType { | ||
| 44 | + static const bool Value = Cond; | ||
| 45 | + typedef BoolType Type; | ||
| 46 | +}; | ||
| 47 | +typedef BoolType<true> TrueType; | ||
| 48 | +typedef BoolType<false> FalseType; | ||
| 49 | + | ||
| 50 | + | ||
| 51 | +/////////////////////////////////////////////////////////////////////////////// | ||
| 52 | +// SelectIf, BoolExpr, NotExpr, AndExpr, OrExpr | ||
| 53 | +// | ||
| 54 | + | ||
| 55 | +template <bool C> struct SelectIfImpl { template <typename T1, typename T2> struct Apply { typedef T1 Type; }; }; | ||
| 56 | +template <> struct SelectIfImpl<false> { template <typename T1, typename T2> struct Apply { typedef T2 Type; }; }; | ||
| 57 | +template <bool C, typename T1, typename T2> struct SelectIfCond : SelectIfImpl<C>::template Apply<T1,T2> {}; | ||
| 58 | +template <typename C, typename T1, typename T2> struct SelectIf : SelectIfCond<C::Value, T1, T2> {}; | ||
| 59 | + | ||
| 60 | +template <bool Cond1, bool Cond2> struct AndExprCond : FalseType {}; | ||
| 61 | +template <> struct AndExprCond<true, true> : TrueType {}; | ||
| 62 | +template <bool Cond1, bool Cond2> struct OrExprCond : TrueType {}; | ||
| 63 | +template <> struct OrExprCond<false, false> : FalseType {}; | ||
| 64 | + | ||
| 65 | +template <typename C> struct BoolExpr : SelectIf<C,TrueType,FalseType>::Type {}; | ||
| 66 | +template <typename C> struct NotExpr : SelectIf<C,FalseType,TrueType>::Type {}; | ||
| 67 | +template <typename C1, typename C2> struct AndExpr : AndExprCond<C1::Value, C2::Value>::Type {}; | ||
| 68 | +template <typename C1, typename C2> struct OrExpr : OrExprCond<C1::Value, C2::Value>::Type {}; | ||
| 69 | + | ||
| 70 | + | ||
| 71 | +/////////////////////////////////////////////////////////////////////////////// | ||
| 72 | +// AddConst, MaybeAddConst, RemoveConst | ||
| 73 | +template <typename T> struct AddConst { typedef const T Type; }; | ||
| 74 | +template <bool Constify, typename T> struct MaybeAddConst : SelectIfCond<Constify, const T, T> {}; | ||
| 75 | +template <typename T> struct RemoveConst { typedef T Type; }; | ||
| 76 | +template <typename T> struct RemoveConst<const T> { typedef T Type; }; | ||
| 77 | + | ||
| 78 | + | ||
| 79 | +/////////////////////////////////////////////////////////////////////////////// | ||
| 80 | +// IsSame, IsConst, IsMoreConst, IsPointer | ||
| 81 | +// | ||
| 82 | +template <typename T, typename U> struct IsSame : FalseType {}; | ||
| 83 | +template <typename T> struct IsSame<T, T> : TrueType {}; | ||
| 84 | + | ||
| 85 | +template <typename T> struct IsConst : FalseType {}; | ||
| 86 | +template <typename T> struct IsConst<const T> : TrueType {}; | ||
| 87 | + | ||
| 88 | +template <typename CT, typename T> | ||
| 89 | +struct IsMoreConst | ||
| 90 | + : AndExpr<IsSame<typename RemoveConst<CT>::Type, typename RemoveConst<T>::Type>, | ||
| 91 | + BoolType<IsConst<CT>::Value >= IsConst<T>::Value> >::Type {}; | ||
| 92 | + | ||
| 93 | +template <typename T> struct IsPointer : FalseType {}; | ||
| 94 | +template <typename T> struct IsPointer<T*> : TrueType {}; | ||
| 95 | + | ||
| 96 | +/////////////////////////////////////////////////////////////////////////////// | ||
| 97 | +// IsBaseOf | ||
| 98 | +// | ||
| 99 | +#if RAPIDJSON_HAS_CXX11_TYPETRAITS | ||
| 100 | + | ||
| 101 | +template <typename B, typename D> struct IsBaseOf | ||
| 102 | + : BoolType< ::std::is_base_of<B,D>::value> {}; | ||
| 103 | + | ||
| 104 | +#else // simplified version adopted from Boost | ||
| 105 | + | ||
| 106 | +template<typename B, typename D> struct IsBaseOfImpl { | ||
| 107 | + RAPIDJSON_STATIC_ASSERT(sizeof(B) != 0); | ||
| 108 | + RAPIDJSON_STATIC_ASSERT(sizeof(D) != 0); | ||
| 109 | + | ||
| 110 | + typedef char (&Yes)[1]; | ||
| 111 | + typedef char (&No) [2]; | ||
| 112 | + | ||
| 113 | + template <typename T> | ||
| 114 | + static Yes Check(const D*, T); | ||
| 115 | + static No Check(const B*, int); | ||
| 116 | + | ||
| 117 | + struct Host { | ||
| 118 | + operator const B*() const; | ||
| 119 | + operator const D*(); | ||
| 120 | + }; | ||
| 121 | + | ||
| 122 | + enum { Value = (sizeof(Check(Host(), 0)) == sizeof(Yes)) }; | ||
| 123 | +}; | ||
| 124 | + | ||
| 125 | +template <typename B, typename D> struct IsBaseOf | ||
| 126 | + : OrExpr<IsSame<B, D>, BoolExpr<IsBaseOfImpl<B, D> > >::Type {}; | ||
| 127 | + | ||
| 128 | +#endif // RAPIDJSON_HAS_CXX11_TYPETRAITS | ||
| 129 | + | ||
| 130 | + | ||
| 131 | +////////////////////////////////////////////////////////////////////////// | ||
| 132 | +// EnableIf / DisableIf | ||
| 133 | +// | ||
| 134 | +template <bool Condition, typename T = void> struct EnableIfCond { typedef T Type; }; | ||
| 135 | +template <typename T> struct EnableIfCond<false, T> { /* empty */ }; | ||
| 136 | + | ||
| 137 | +template <bool Condition, typename T = void> struct DisableIfCond { typedef T Type; }; | ||
| 138 | +template <typename T> struct DisableIfCond<true, T> { /* empty */ }; | ||
| 139 | + | ||
| 140 | +template <typename Condition, typename T = void> | ||
| 141 | +struct EnableIf : EnableIfCond<Condition::Value, T> {}; | ||
| 142 | + | ||
| 143 | +template <typename Condition, typename T = void> | ||
| 144 | +struct DisableIf : DisableIfCond<Condition::Value, T> {}; | ||
| 145 | + | ||
| 146 | +// SFINAE helpers | ||
| 147 | +struct SfinaeTag {}; | ||
| 148 | +template <typename T> struct RemoveSfinaeTag; | ||
| 149 | +template <typename T> struct RemoveSfinaeTag<SfinaeTag&(*)(T)> { typedef T Type; }; | ||
| 150 | + | ||
| 151 | +#define RAPIDJSON_REMOVEFPTR_(type) \ | ||
| 152 | + typename ::RAPIDJSON_NAMESPACE::internal::RemoveSfinaeTag \ | ||
| 153 | + < ::RAPIDJSON_NAMESPACE::internal::SfinaeTag&(*) type>::Type | ||
| 154 | + | ||
| 155 | +#define RAPIDJSON_ENABLEIF(cond) \ | ||
| 156 | + typename ::RAPIDJSON_NAMESPACE::internal::EnableIf \ | ||
| 157 | + <RAPIDJSON_REMOVEFPTR_(cond)>::Type * = NULL | ||
| 158 | + | ||
| 159 | +#define RAPIDJSON_DISABLEIF(cond) \ | ||
| 160 | + typename ::RAPIDJSON_NAMESPACE::internal::DisableIf \ | ||
| 161 | + <RAPIDJSON_REMOVEFPTR_(cond)>::Type * = NULL | ||
| 162 | + | ||
| 163 | +#define RAPIDJSON_ENABLEIF_RETURN(cond,returntype) \ | ||
| 164 | + typename ::RAPIDJSON_NAMESPACE::internal::EnableIf \ | ||
| 165 | + <RAPIDJSON_REMOVEFPTR_(cond), \ | ||
| 166 | + RAPIDJSON_REMOVEFPTR_(returntype)>::Type | ||
| 167 | + | ||
| 168 | +#define RAPIDJSON_DISABLEIF_RETURN(cond,returntype) \ | ||
| 169 | + typename ::RAPIDJSON_NAMESPACE::internal::DisableIf \ | ||
| 170 | + <RAPIDJSON_REMOVEFPTR_(cond), \ | ||
| 171 | + RAPIDJSON_REMOVEFPTR_(returntype)>::Type | ||
| 172 | + | ||
| 173 | +} // namespace internal | ||
| 174 | +RAPIDJSON_NAMESPACE_END | ||
| 175 | +//@endcond | ||
| 176 | + | ||
| 177 | +#if defined(__GNUC__) || defined(_MSC_VER) | ||
| 178 | +RAPIDJSON_DIAG_POP | ||
| 179 | +#endif | ||
| 180 | + | ||
| 181 | +#endif // RAPIDJSON_INTERNAL_META_H_ |
| 1 | +// Tencent is pleased to support the open source community by making RapidJSON available. | ||
| 2 | +// | ||
| 3 | +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. | ||
| 4 | +// | ||
| 5 | +// Licensed under the MIT License (the "License"); you may not use this file except | ||
| 6 | +// in compliance with the License. You may obtain a copy of the License at | ||
| 7 | +// | ||
| 8 | +// http://opensource.org/licenses/MIT | ||
| 9 | +// | ||
| 10 | +// Unless required by applicable law or agreed to in writing, software distributed | ||
| 11 | +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR | ||
| 12 | +// CONDITIONS OF ANY KIND, either express or implied. See the License for the | ||
| 13 | +// specific language governing permissions and limitations under the License. | ||
| 14 | + | ||
| 15 | +#ifndef RAPIDJSON_POW10_ | ||
| 16 | +#define RAPIDJSON_POW10_ | ||
| 17 | + | ||
| 18 | +#include "../rapidjson.h" | ||
| 19 | + | ||
| 20 | +RAPIDJSON_NAMESPACE_BEGIN | ||
| 21 | +namespace internal { | ||
| 22 | + | ||
| 23 | +//! Computes integer powers of 10 in double (10.0^n). | ||
| 24 | +/*! This function uses lookup table for fast and accurate results. | ||
| 25 | + \param n non-negative exponent. Must <= 308. | ||
| 26 | + \return 10.0^n | ||
| 27 | +*/ | ||
| 28 | +inline double Pow10(int n) { | ||
| 29 | + static const double e[] = { // 1e-0...1e308: 309 * 8 bytes = 2472 bytes | ||
| 30 | + 1e+0, | ||
| 31 | + 1e+1, 1e+2, 1e+3, 1e+4, 1e+5, 1e+6, 1e+7, 1e+8, 1e+9, 1e+10, 1e+11, 1e+12, 1e+13, 1e+14, 1e+15, 1e+16, 1e+17, 1e+18, 1e+19, 1e+20, | ||
| 32 | + 1e+21, 1e+22, 1e+23, 1e+24, 1e+25, 1e+26, 1e+27, 1e+28, 1e+29, 1e+30, 1e+31, 1e+32, 1e+33, 1e+34, 1e+35, 1e+36, 1e+37, 1e+38, 1e+39, 1e+40, | ||
| 33 | + 1e+41, 1e+42, 1e+43, 1e+44, 1e+45, 1e+46, 1e+47, 1e+48, 1e+49, 1e+50, 1e+51, 1e+52, 1e+53, 1e+54, 1e+55, 1e+56, 1e+57, 1e+58, 1e+59, 1e+60, | ||
| 34 | + 1e+61, 1e+62, 1e+63, 1e+64, 1e+65, 1e+66, 1e+67, 1e+68, 1e+69, 1e+70, 1e+71, 1e+72, 1e+73, 1e+74, 1e+75, 1e+76, 1e+77, 1e+78, 1e+79, 1e+80, | ||
| 35 | + 1e+81, 1e+82, 1e+83, 1e+84, 1e+85, 1e+86, 1e+87, 1e+88, 1e+89, 1e+90, 1e+91, 1e+92, 1e+93, 1e+94, 1e+95, 1e+96, 1e+97, 1e+98, 1e+99, 1e+100, | ||
| 36 | + 1e+101,1e+102,1e+103,1e+104,1e+105,1e+106,1e+107,1e+108,1e+109,1e+110,1e+111,1e+112,1e+113,1e+114,1e+115,1e+116,1e+117,1e+118,1e+119,1e+120, | ||
| 37 | + 1e+121,1e+122,1e+123,1e+124,1e+125,1e+126,1e+127,1e+128,1e+129,1e+130,1e+131,1e+132,1e+133,1e+134,1e+135,1e+136,1e+137,1e+138,1e+139,1e+140, | ||
| 38 | + 1e+141,1e+142,1e+143,1e+144,1e+145,1e+146,1e+147,1e+148,1e+149,1e+150,1e+151,1e+152,1e+153,1e+154,1e+155,1e+156,1e+157,1e+158,1e+159,1e+160, | ||
| 39 | + 1e+161,1e+162,1e+163,1e+164,1e+165,1e+166,1e+167,1e+168,1e+169,1e+170,1e+171,1e+172,1e+173,1e+174,1e+175,1e+176,1e+177,1e+178,1e+179,1e+180, | ||
| 40 | + 1e+181,1e+182,1e+183,1e+184,1e+185,1e+186,1e+187,1e+188,1e+189,1e+190,1e+191,1e+192,1e+193,1e+194,1e+195,1e+196,1e+197,1e+198,1e+199,1e+200, | ||
| 41 | + 1e+201,1e+202,1e+203,1e+204,1e+205,1e+206,1e+207,1e+208,1e+209,1e+210,1e+211,1e+212,1e+213,1e+214,1e+215,1e+216,1e+217,1e+218,1e+219,1e+220, | ||
| 42 | + 1e+221,1e+222,1e+223,1e+224,1e+225,1e+226,1e+227,1e+228,1e+229,1e+230,1e+231,1e+232,1e+233,1e+234,1e+235,1e+236,1e+237,1e+238,1e+239,1e+240, | ||
| 43 | + 1e+241,1e+242,1e+243,1e+244,1e+245,1e+246,1e+247,1e+248,1e+249,1e+250,1e+251,1e+252,1e+253,1e+254,1e+255,1e+256,1e+257,1e+258,1e+259,1e+260, | ||
| 44 | + 1e+261,1e+262,1e+263,1e+264,1e+265,1e+266,1e+267,1e+268,1e+269,1e+270,1e+271,1e+272,1e+273,1e+274,1e+275,1e+276,1e+277,1e+278,1e+279,1e+280, | ||
| 45 | + 1e+281,1e+282,1e+283,1e+284,1e+285,1e+286,1e+287,1e+288,1e+289,1e+290,1e+291,1e+292,1e+293,1e+294,1e+295,1e+296,1e+297,1e+298,1e+299,1e+300, | ||
| 46 | + 1e+301,1e+302,1e+303,1e+304,1e+305,1e+306,1e+307,1e+308 | ||
| 47 | + }; | ||
| 48 | + RAPIDJSON_ASSERT(n >= 0 && n <= 308); | ||
| 49 | + return e[n]; | ||
| 50 | +} | ||
| 51 | + | ||
| 52 | +} // namespace internal | ||
| 53 | +RAPIDJSON_NAMESPACE_END | ||
| 54 | + | ||
| 55 | +#endif // RAPIDJSON_POW10_ |
| 1 | +// Tencent is pleased to support the open source community by making RapidJSON available. | ||
| 2 | +// | ||
| 3 | +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. | ||
| 4 | +// | ||
| 5 | +// Licensed under the MIT License (the "License"); you may not use this file except | ||
| 6 | +// in compliance with the License. You may obtain a copy of the License at | ||
| 7 | +// | ||
| 8 | +// http://opensource.org/licenses/MIT | ||
| 9 | +// | ||
| 10 | +// Unless required by applicable law or agreed to in writing, software distributed | ||
| 11 | +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR | ||
| 12 | +// CONDITIONS OF ANY KIND, either express or implied. See the License for the | ||
| 13 | +// specific language governing permissions and limitations under the License. | ||
| 14 | + | ||
| 15 | +#ifndef RAPIDJSON_INTERNAL_REGEX_H_ | ||
| 16 | +#define RAPIDJSON_INTERNAL_REGEX_H_ | ||
| 17 | + | ||
| 18 | +#include "../allocators.h" | ||
| 19 | +#include "../stream.h" | ||
| 20 | +#include "stack.h" | ||
| 21 | + | ||
| 22 | +#ifdef __clang__ | ||
| 23 | +RAPIDJSON_DIAG_PUSH | ||
| 24 | +RAPIDJSON_DIAG_OFF(padded) | ||
| 25 | +RAPIDJSON_DIAG_OFF(switch-enum) | ||
| 26 | +RAPIDJSON_DIAG_OFF(implicit-fallthrough) | ||
| 27 | +#endif | ||
| 28 | + | ||
| 29 | +#ifdef __GNUC__ | ||
| 30 | +RAPIDJSON_DIAG_PUSH | ||
| 31 | +RAPIDJSON_DIAG_OFF(effc++) | ||
| 32 | +#if __GNUC__ >= 7 | ||
| 33 | +RAPIDJSON_DIAG_OFF(implicit-fallthrough) | ||
| 34 | +#endif | ||
| 35 | +#endif | ||
| 36 | + | ||
| 37 | +#ifdef _MSC_VER | ||
| 38 | +RAPIDJSON_DIAG_PUSH | ||
| 39 | +RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated | ||
| 40 | +#endif | ||
| 41 | + | ||
| 42 | +#ifndef RAPIDJSON_REGEX_VERBOSE | ||
| 43 | +#define RAPIDJSON_REGEX_VERBOSE 0 | ||
| 44 | +#endif | ||
| 45 | + | ||
| 46 | +RAPIDJSON_NAMESPACE_BEGIN | ||
| 47 | +namespace internal { | ||
| 48 | + | ||
| 49 | +/////////////////////////////////////////////////////////////////////////////// | ||
| 50 | +// DecodedStream | ||
| 51 | + | ||
| 52 | +template <typename SourceStream, typename Encoding> | ||
| 53 | +class DecodedStream { | ||
| 54 | +public: | ||
| 55 | + DecodedStream(SourceStream& ss) : ss_(ss), codepoint_() { Decode(); } | ||
| 56 | + unsigned Peek() { return codepoint_; } | ||
| 57 | + unsigned Take() { | ||
| 58 | + unsigned c = codepoint_; | ||
| 59 | + if (c) // No further decoding when '\0' | ||
| 60 | + Decode(); | ||
| 61 | + return c; | ||
| 62 | + } | ||
| 63 | + | ||
| 64 | +private: | ||
| 65 | + void Decode() { | ||
| 66 | + if (!Encoding::Decode(ss_, &codepoint_)) | ||
| 67 | + codepoint_ = 0; | ||
| 68 | + } | ||
| 69 | + | ||
| 70 | + SourceStream& ss_; | ||
| 71 | + unsigned codepoint_; | ||
| 72 | +}; | ||
| 73 | + | ||
| 74 | +/////////////////////////////////////////////////////////////////////////////// | ||
| 75 | +// GenericRegex | ||
| 76 | + | ||
| 77 | +static const SizeType kRegexInvalidState = ~SizeType(0); //!< Represents an invalid index in GenericRegex::State::out, out1 | ||
| 78 | +static const SizeType kRegexInvalidRange = ~SizeType(0); | ||
| 79 | + | ||
| 80 | +template <typename Encoding, typename Allocator> | ||
| 81 | +class GenericRegexSearch; | ||
| 82 | + | ||
| 83 | +//! Regular expression engine with subset of ECMAscript grammar. | ||
| 84 | +/*! | ||
| 85 | + Supported regular expression syntax: | ||
| 86 | + - \c ab Concatenation | ||
| 87 | + - \c a|b Alternation | ||
| 88 | + - \c a? Zero or one | ||
| 89 | + - \c a* Zero or more | ||
| 90 | + - \c a+ One or more | ||
| 91 | + - \c a{3} Exactly 3 times | ||
| 92 | + - \c a{3,} At least 3 times | ||
| 93 | + - \c a{3,5} 3 to 5 times | ||
| 94 | + - \c (ab) Grouping | ||
| 95 | + - \c ^a At the beginning | ||
| 96 | + - \c a$ At the end | ||
| 97 | + - \c . Any character | ||
| 98 | + - \c [abc] Character classes | ||
| 99 | + - \c [a-c] Character class range | ||
| 100 | + - \c [a-z0-9_] Character class combination | ||
| 101 | + - \c [^abc] Negated character classes | ||
| 102 | + - \c [^a-c] Negated character class range | ||
| 103 | + - \c [\b] Backspace (U+0008) | ||
| 104 | + - \c \\| \\\\ ... Escape characters | ||
| 105 | + - \c \\f Form feed (U+000C) | ||
| 106 | + - \c \\n Line feed (U+000A) | ||
| 107 | + - \c \\r Carriage return (U+000D) | ||
| 108 | + - \c \\t Tab (U+0009) | ||
| 109 | + - \c \\v Vertical tab (U+000B) | ||
| 110 | + | ||
| 111 | + \note This is a Thompson NFA engine, implemented with reference to | ||
| 112 | + Cox, Russ. "Regular Expression Matching Can Be Simple And Fast (but is slow in Java, Perl, PHP, Python, Ruby,...).", | ||
| 113 | + https://swtch.com/~rsc/regexp/regexp1.html | ||
| 114 | +*/ | ||
| 115 | +template <typename Encoding, typename Allocator = CrtAllocator> | ||
| 116 | +class GenericRegex { | ||
| 117 | +public: | ||
| 118 | + typedef Encoding EncodingType; | ||
| 119 | + typedef typename Encoding::Ch Ch; | ||
| 120 | + template <typename, typename> friend class GenericRegexSearch; | ||
| 121 | + | ||
| 122 | + GenericRegex(const Ch* source, Allocator* allocator = 0) : | ||
| 123 | + states_(allocator, 256), ranges_(allocator, 256), root_(kRegexInvalidState), stateCount_(), rangeCount_(), | ||
| 124 | + anchorBegin_(), anchorEnd_() | ||
| 125 | + { | ||
| 126 | + GenericStringStream<Encoding> ss(source); | ||
| 127 | + DecodedStream<GenericStringStream<Encoding>, Encoding> ds(ss); | ||
| 128 | + Parse(ds); | ||
| 129 | + } | ||
| 130 | + | ||
| 131 | + ~GenericRegex() {} | ||
| 132 | + | ||
| 133 | + bool IsValid() const { | ||
| 134 | + return root_ != kRegexInvalidState; | ||
| 135 | + } | ||
| 136 | + | ||
| 137 | +private: | ||
| 138 | + enum Operator { | ||
| 139 | + kZeroOrOne, | ||
| 140 | + kZeroOrMore, | ||
| 141 | + kOneOrMore, | ||
| 142 | + kConcatenation, | ||
| 143 | + kAlternation, | ||
| 144 | + kLeftParenthesis | ||
| 145 | + }; | ||
| 146 | + | ||
| 147 | + static const unsigned kAnyCharacterClass = 0xFFFFFFFF; //!< For '.' | ||
| 148 | + static const unsigned kRangeCharacterClass = 0xFFFFFFFE; | ||
| 149 | + static const unsigned kRangeNegationFlag = 0x80000000; | ||
| 150 | + | ||
| 151 | + struct Range { | ||
| 152 | + unsigned start; // | ||
| 153 | + unsigned end; | ||
| 154 | + SizeType next; | ||
| 155 | + }; | ||
| 156 | + | ||
| 157 | + struct State { | ||
| 158 | + SizeType out; //!< Equals to kInvalid for matching state | ||
| 159 | + SizeType out1; //!< Equals to non-kInvalid for split | ||
| 160 | + SizeType rangeStart; | ||
| 161 | + unsigned codepoint; | ||
| 162 | + }; | ||
| 163 | + | ||
| 164 | + struct Frag { | ||
| 165 | + Frag(SizeType s, SizeType o, SizeType m) : start(s), out(o), minIndex(m) {} | ||
| 166 | + SizeType start; | ||
| 167 | + SizeType out; //!< link-list of all output states | ||
| 168 | + SizeType minIndex; | ||
| 169 | + }; | ||
| 170 | + | ||
| 171 | + State& GetState(SizeType index) { | ||
| 172 | + RAPIDJSON_ASSERT(index < stateCount_); | ||
| 173 | + return states_.template Bottom<State>()[index]; | ||
| 174 | + } | ||
| 175 | + | ||
| 176 | + const State& GetState(SizeType index) const { | ||
| 177 | + RAPIDJSON_ASSERT(index < stateCount_); | ||
| 178 | + return states_.template Bottom<State>()[index]; | ||
| 179 | + } | ||
| 180 | + | ||
| 181 | + Range& GetRange(SizeType index) { | ||
| 182 | + RAPIDJSON_ASSERT(index < rangeCount_); | ||
| 183 | + return ranges_.template Bottom<Range>()[index]; | ||
| 184 | + } | ||
| 185 | + | ||
| 186 | + const Range& GetRange(SizeType index) const { | ||
| 187 | + RAPIDJSON_ASSERT(index < rangeCount_); | ||
| 188 | + return ranges_.template Bottom<Range>()[index]; | ||
| 189 | + } | ||
| 190 | + | ||
| 191 | + template <typename InputStream> | ||
| 192 | + void Parse(DecodedStream<InputStream, Encoding>& ds) { | ||
| 193 | + Allocator allocator; | ||
| 194 | + Stack<Allocator> operandStack(&allocator, 256); // Frag | ||
| 195 | + Stack<Allocator> operatorStack(&allocator, 256); // Operator | ||
| 196 | + Stack<Allocator> atomCountStack(&allocator, 256); // unsigned (Atom per parenthesis) | ||
| 197 | + | ||
| 198 | + *atomCountStack.template Push<unsigned>() = 0; | ||
| 199 | + | ||
| 200 | + unsigned codepoint; | ||
| 201 | + while (ds.Peek() != 0) { | ||
| 202 | + switch (codepoint = ds.Take()) { | ||
| 203 | + case '^': | ||
| 204 | + anchorBegin_ = true; | ||
| 205 | + break; | ||
| 206 | + | ||
| 207 | + case '$': | ||
| 208 | + anchorEnd_ = true; | ||
| 209 | + break; | ||
| 210 | + | ||
| 211 | + case '|': | ||
| 212 | + while (!operatorStack.Empty() && *operatorStack.template Top<Operator>() < kAlternation) | ||
| 213 | + if (!Eval(operandStack, *operatorStack.template Pop<Operator>(1))) | ||
| 214 | + return; | ||
| 215 | + *operatorStack.template Push<Operator>() = kAlternation; | ||
| 216 | + *atomCountStack.template Top<unsigned>() = 0; | ||
| 217 | + break; | ||
| 218 | + | ||
| 219 | + case '(': | ||
| 220 | + *operatorStack.template Push<Operator>() = kLeftParenthesis; | ||
| 221 | + *atomCountStack.template Push<unsigned>() = 0; | ||
| 222 | + break; | ||
| 223 | + | ||
| 224 | + case ')': | ||
| 225 | + while (!operatorStack.Empty() && *operatorStack.template Top<Operator>() != kLeftParenthesis) | ||
| 226 | + if (!Eval(operandStack, *operatorStack.template Pop<Operator>(1))) | ||
| 227 | + return; | ||
| 228 | + if (operatorStack.Empty()) | ||
| 229 | + return; | ||
| 230 | + operatorStack.template Pop<Operator>(1); | ||
| 231 | + atomCountStack.template Pop<unsigned>(1); | ||
| 232 | + ImplicitConcatenation(atomCountStack, operatorStack); | ||
| 233 | + break; | ||
| 234 | + | ||
| 235 | + case '?': | ||
| 236 | + if (!Eval(operandStack, kZeroOrOne)) | ||
| 237 | + return; | ||
| 238 | + break; | ||
| 239 | + | ||
| 240 | + case '*': | ||
| 241 | + if (!Eval(operandStack, kZeroOrMore)) | ||
| 242 | + return; | ||
| 243 | + break; | ||
| 244 | + | ||
| 245 | + case '+': | ||
| 246 | + if (!Eval(operandStack, kOneOrMore)) | ||
| 247 | + return; | ||
| 248 | + break; | ||
| 249 | + | ||
| 250 | + case '{': | ||
| 251 | + { | ||
| 252 | + unsigned n, m; | ||
| 253 | + if (!ParseUnsigned(ds, &n)) | ||
| 254 | + return; | ||
| 255 | + | ||
| 256 | + if (ds.Peek() == ',') { | ||
| 257 | + ds.Take(); | ||
| 258 | + if (ds.Peek() == '}') | ||
| 259 | + m = kInfinityQuantifier; | ||
| 260 | + else if (!ParseUnsigned(ds, &m) || m < n) | ||
| 261 | + return; | ||
| 262 | + } | ||
| 263 | + else | ||
| 264 | + m = n; | ||
| 265 | + | ||
| 266 | + if (!EvalQuantifier(operandStack, n, m) || ds.Peek() != '}') | ||
| 267 | + return; | ||
| 268 | + ds.Take(); | ||
| 269 | + } | ||
| 270 | + break; | ||
| 271 | + | ||
| 272 | + case '.': | ||
| 273 | + PushOperand(operandStack, kAnyCharacterClass); | ||
| 274 | + ImplicitConcatenation(atomCountStack, operatorStack); | ||
| 275 | + break; | ||
| 276 | + | ||
| 277 | + case '[': | ||
| 278 | + { | ||
| 279 | + SizeType range; | ||
| 280 | + if (!ParseRange(ds, &range)) | ||
| 281 | + return; | ||
| 282 | + SizeType s = NewState(kRegexInvalidState, kRegexInvalidState, kRangeCharacterClass); | ||
| 283 | + GetState(s).rangeStart = range; | ||
| 284 | + *operandStack.template Push<Frag>() = Frag(s, s, s); | ||
| 285 | + } | ||
| 286 | + ImplicitConcatenation(atomCountStack, operatorStack); | ||
| 287 | + break; | ||
| 288 | + | ||
| 289 | + case '\\': // Escape character | ||
| 290 | + if (!CharacterEscape(ds, &codepoint)) | ||
| 291 | + return; // Unsupported escape character | ||
| 292 | + // fall through to default | ||
| 293 | + | ||
| 294 | + default: // Pattern character | ||
| 295 | + PushOperand(operandStack, codepoint); | ||
| 296 | + ImplicitConcatenation(atomCountStack, operatorStack); | ||
| 297 | + } | ||
| 298 | + } | ||
| 299 | + | ||
| 300 | + while (!operatorStack.Empty()) | ||
| 301 | + if (!Eval(operandStack, *operatorStack.template Pop<Operator>(1))) | ||
| 302 | + return; | ||
| 303 | + | ||
| 304 | + // Link the operand to matching state. | ||
| 305 | + if (operandStack.GetSize() == sizeof(Frag)) { | ||
| 306 | + Frag* e = operandStack.template Pop<Frag>(1); | ||
| 307 | + Patch(e->out, NewState(kRegexInvalidState, kRegexInvalidState, 0)); | ||
| 308 | + root_ = e->start; | ||
| 309 | + | ||
| 310 | +#if RAPIDJSON_REGEX_VERBOSE | ||
| 311 | + printf("root: %d\n", root_); | ||
| 312 | + for (SizeType i = 0; i < stateCount_ ; i++) { | ||
| 313 | + State& s = GetState(i); | ||
| 314 | + printf("[%2d] out: %2d out1: %2d c: '%c'\n", i, s.out, s.out1, (char)s.codepoint); | ||
| 315 | + } | ||
| 316 | + printf("\n"); | ||
| 317 | +#endif | ||
| 318 | + } | ||
| 319 | + } | ||
| 320 | + | ||
| 321 | + SizeType NewState(SizeType out, SizeType out1, unsigned codepoint) { | ||
| 322 | + State* s = states_.template Push<State>(); | ||
| 323 | + s->out = out; | ||
| 324 | + s->out1 = out1; | ||
| 325 | + s->codepoint = codepoint; | ||
| 326 | + s->rangeStart = kRegexInvalidRange; | ||
| 327 | + return stateCount_++; | ||
| 328 | + } | ||
| 329 | + | ||
| 330 | + void PushOperand(Stack<Allocator>& operandStack, unsigned codepoint) { | ||
| 331 | + SizeType s = NewState(kRegexInvalidState, kRegexInvalidState, codepoint); | ||
| 332 | + *operandStack.template Push<Frag>() = Frag(s, s, s); | ||
| 333 | + } | ||
| 334 | + | ||
| 335 | + void ImplicitConcatenation(Stack<Allocator>& atomCountStack, Stack<Allocator>& operatorStack) { | ||
| 336 | + if (*atomCountStack.template Top<unsigned>()) | ||
| 337 | + *operatorStack.template Push<Operator>() = kConcatenation; | ||
| 338 | + (*atomCountStack.template Top<unsigned>())++; | ||
| 339 | + } | ||
| 340 | + | ||
| 341 | + SizeType Append(SizeType l1, SizeType l2) { | ||
| 342 | + SizeType old = l1; | ||
| 343 | + while (GetState(l1).out != kRegexInvalidState) | ||
| 344 | + l1 = GetState(l1).out; | ||
| 345 | + GetState(l1).out = l2; | ||
| 346 | + return old; | ||
| 347 | + } | ||
| 348 | + | ||
| 349 | + void Patch(SizeType l, SizeType s) { | ||
| 350 | + for (SizeType next; l != kRegexInvalidState; l = next) { | ||
| 351 | + next = GetState(l).out; | ||
| 352 | + GetState(l).out = s; | ||
| 353 | + } | ||
| 354 | + } | ||
| 355 | + | ||
| 356 | + bool Eval(Stack<Allocator>& operandStack, Operator op) { | ||
| 357 | + switch (op) { | ||
| 358 | + case kConcatenation: | ||
| 359 | + RAPIDJSON_ASSERT(operandStack.GetSize() >= sizeof(Frag) * 2); | ||
| 360 | + { | ||
| 361 | + Frag e2 = *operandStack.template Pop<Frag>(1); | ||
| 362 | + Frag e1 = *operandStack.template Pop<Frag>(1); | ||
| 363 | + Patch(e1.out, e2.start); | ||
| 364 | + *operandStack.template Push<Frag>() = Frag(e1.start, e2.out, Min(e1.minIndex, e2.minIndex)); | ||
| 365 | + } | ||
| 366 | + return true; | ||
| 367 | + | ||
| 368 | + case kAlternation: | ||
| 369 | + if (operandStack.GetSize() >= sizeof(Frag) * 2) { | ||
| 370 | + Frag e2 = *operandStack.template Pop<Frag>(1); | ||
| 371 | + Frag e1 = *operandStack.template Pop<Frag>(1); | ||
| 372 | + SizeType s = NewState(e1.start, e2.start, 0); | ||
| 373 | + *operandStack.template Push<Frag>() = Frag(s, Append(e1.out, e2.out), Min(e1.minIndex, e2.minIndex)); | ||
| 374 | + return true; | ||
| 375 | + } | ||
| 376 | + return false; | ||
| 377 | + | ||
| 378 | + case kZeroOrOne: | ||
| 379 | + if (operandStack.GetSize() >= sizeof(Frag)) { | ||
| 380 | + Frag e = *operandStack.template Pop<Frag>(1); | ||
| 381 | + SizeType s = NewState(kRegexInvalidState, e.start, 0); | ||
| 382 | + *operandStack.template Push<Frag>() = Frag(s, Append(e.out, s), e.minIndex); | ||
| 383 | + return true; | ||
| 384 | + } | ||
| 385 | + return false; | ||
| 386 | + | ||
| 387 | + case kZeroOrMore: | ||
| 388 | + if (operandStack.GetSize() >= sizeof(Frag)) { | ||
| 389 | + Frag e = *operandStack.template Pop<Frag>(1); | ||
| 390 | + SizeType s = NewState(kRegexInvalidState, e.start, 0); | ||
| 391 | + Patch(e.out, s); | ||
| 392 | + *operandStack.template Push<Frag>() = Frag(s, s, e.minIndex); | ||
| 393 | + return true; | ||
| 394 | + } | ||
| 395 | + return false; | ||
| 396 | + | ||
| 397 | + default: | ||
| 398 | + RAPIDJSON_ASSERT(op == kOneOrMore); | ||
| 399 | + if (operandStack.GetSize() >= sizeof(Frag)) { | ||
| 400 | + Frag e = *operandStack.template Pop<Frag>(1); | ||
| 401 | + SizeType s = NewState(kRegexInvalidState, e.start, 0); | ||
| 402 | + Patch(e.out, s); | ||
| 403 | + *operandStack.template Push<Frag>() = Frag(e.start, s, e.minIndex); | ||
| 404 | + return true; | ||
| 405 | + } | ||
| 406 | + return false; | ||
| 407 | + } | ||
| 408 | + } | ||
| 409 | + | ||
| 410 | + bool EvalQuantifier(Stack<Allocator>& operandStack, unsigned n, unsigned m) { | ||
| 411 | + RAPIDJSON_ASSERT(n <= m); | ||
| 412 | + RAPIDJSON_ASSERT(operandStack.GetSize() >= sizeof(Frag)); | ||
| 413 | + | ||
| 414 | + if (n == 0) { | ||
| 415 | + if (m == 0) // a{0} not support | ||
| 416 | + return false; | ||
| 417 | + else if (m == kInfinityQuantifier) | ||
| 418 | + Eval(operandStack, kZeroOrMore); // a{0,} -> a* | ||
| 419 | + else { | ||
| 420 | + Eval(operandStack, kZeroOrOne); // a{0,5} -> a? | ||
| 421 | + for (unsigned i = 0; i < m - 1; i++) | ||
| 422 | + CloneTopOperand(operandStack); // a{0,5} -> a? a? a? a? a? | ||
| 423 | + for (unsigned i = 0; i < m - 1; i++) | ||
| 424 | + Eval(operandStack, kConcatenation); // a{0,5} -> a?a?a?a?a? | ||
| 425 | + } | ||
| 426 | + return true; | ||
| 427 | + } | ||
| 428 | + | ||
| 429 | + for (unsigned i = 0; i < n - 1; i++) // a{3} -> a a a | ||
| 430 | + CloneTopOperand(operandStack); | ||
| 431 | + | ||
| 432 | + if (m == kInfinityQuantifier) | ||
| 433 | + Eval(operandStack, kOneOrMore); // a{3,} -> a a a+ | ||
| 434 | + else if (m > n) { | ||
| 435 | + CloneTopOperand(operandStack); // a{3,5} -> a a a a | ||
| 436 | + Eval(operandStack, kZeroOrOne); // a{3,5} -> a a a a? | ||
| 437 | + for (unsigned i = n; i < m - 1; i++) | ||
| 438 | + CloneTopOperand(operandStack); // a{3,5} -> a a a a? a? | ||
| 439 | + for (unsigned i = n; i < m; i++) | ||
| 440 | + Eval(operandStack, kConcatenation); // a{3,5} -> a a aa?a? | ||
| 441 | + } | ||
| 442 | + | ||
| 443 | + for (unsigned i = 0; i < n - 1; i++) | ||
| 444 | + Eval(operandStack, kConcatenation); // a{3} -> aaa, a{3,} -> aaa+, a{3.5} -> aaaa?a? | ||
| 445 | + | ||
| 446 | + return true; | ||
| 447 | + } | ||
| 448 | + | ||
| 449 | + static SizeType Min(SizeType a, SizeType b) { return a < b ? a : b; } | ||
| 450 | + | ||
| 451 | + void CloneTopOperand(Stack<Allocator>& operandStack) { | ||
| 452 | + const Frag src = *operandStack.template Top<Frag>(); // Copy constructor to prevent invalidation | ||
| 453 | + SizeType count = stateCount_ - src.minIndex; // Assumes top operand contains states in [src->minIndex, stateCount_) | ||
| 454 | + State* s = states_.template Push<State>(count); | ||
| 455 | + memcpy(s, &GetState(src.minIndex), count * sizeof(State)); | ||
| 456 | + for (SizeType j = 0; j < count; j++) { | ||
| 457 | + if (s[j].out != kRegexInvalidState) | ||
| 458 | + s[j].out += count; | ||
| 459 | + if (s[j].out1 != kRegexInvalidState) | ||
| 460 | + s[j].out1 += count; | ||
| 461 | + } | ||
| 462 | + *operandStack.template Push<Frag>() = Frag(src.start + count, src.out + count, src.minIndex + count); | ||
| 463 | + stateCount_ += count; | ||
| 464 | + } | ||
| 465 | + | ||
| 466 | + template <typename InputStream> | ||
| 467 | + bool ParseUnsigned(DecodedStream<InputStream, Encoding>& ds, unsigned* u) { | ||
| 468 | + unsigned r = 0; | ||
| 469 | + if (ds.Peek() < '0' || ds.Peek() > '9') | ||
| 470 | + return false; | ||
| 471 | + while (ds.Peek() >= '0' && ds.Peek() <= '9') { | ||
| 472 | + if (r >= 429496729 && ds.Peek() > '5') // 2^32 - 1 = 4294967295 | ||
| 473 | + return false; // overflow | ||
| 474 | + r = r * 10 + (ds.Take() - '0'); | ||
| 475 | + } | ||
| 476 | + *u = r; | ||
| 477 | + return true; | ||
| 478 | + } | ||
| 479 | + | ||
| 480 | + template <typename InputStream> | ||
| 481 | + bool ParseRange(DecodedStream<InputStream, Encoding>& ds, SizeType* range) { | ||
| 482 | + bool isBegin = true; | ||
| 483 | + bool negate = false; | ||
| 484 | + int step = 0; | ||
| 485 | + SizeType start = kRegexInvalidRange; | ||
| 486 | + SizeType current = kRegexInvalidRange; | ||
| 487 | + unsigned codepoint; | ||
| 488 | + while ((codepoint = ds.Take()) != 0) { | ||
| 489 | + if (isBegin) { | ||
| 490 | + isBegin = false; | ||
| 491 | + if (codepoint == '^') { | ||
| 492 | + negate = true; | ||
| 493 | + continue; | ||
| 494 | + } | ||
| 495 | + } | ||
| 496 | + | ||
| 497 | + switch (codepoint) { | ||
| 498 | + case ']': | ||
| 499 | + if (start == kRegexInvalidRange) | ||
| 500 | + return false; // Error: nothing inside [] | ||
| 501 | + if (step == 2) { // Add trailing '-' | ||
| 502 | + SizeType r = NewRange('-'); | ||
| 503 | + RAPIDJSON_ASSERT(current != kRegexInvalidRange); | ||
| 504 | + GetRange(current).next = r; | ||
| 505 | + } | ||
| 506 | + if (negate) | ||
| 507 | + GetRange(start).start |= kRangeNegationFlag; | ||
| 508 | + *range = start; | ||
| 509 | + return true; | ||
| 510 | + | ||
| 511 | + case '\\': | ||
| 512 | + if (ds.Peek() == 'b') { | ||
| 513 | + ds.Take(); | ||
| 514 | + codepoint = 0x0008; // Escape backspace character | ||
| 515 | + } | ||
| 516 | + else if (!CharacterEscape(ds, &codepoint)) | ||
| 517 | + return false; | ||
| 518 | + // fall through to default | ||
| 519 | + | ||
| 520 | + default: | ||
| 521 | + switch (step) { | ||
| 522 | + case 1: | ||
| 523 | + if (codepoint == '-') { | ||
| 524 | + step++; | ||
| 525 | + break; | ||
| 526 | + } | ||
| 527 | + // fall through to step 0 for other characters | ||
| 528 | + | ||
| 529 | + case 0: | ||
| 530 | + { | ||
| 531 | + SizeType r = NewRange(codepoint); | ||
| 532 | + if (current != kRegexInvalidRange) | ||
| 533 | + GetRange(current).next = r; | ||
| 534 | + if (start == kRegexInvalidRange) | ||
| 535 | + start = r; | ||
| 536 | + current = r; | ||
| 537 | + } | ||
| 538 | + step = 1; | ||
| 539 | + break; | ||
| 540 | + | ||
| 541 | + default: | ||
| 542 | + RAPIDJSON_ASSERT(step == 2); | ||
| 543 | + GetRange(current).end = codepoint; | ||
| 544 | + step = 0; | ||
| 545 | + } | ||
| 546 | + } | ||
| 547 | + } | ||
| 548 | + return false; | ||
| 549 | + } | ||
| 550 | + | ||
| 551 | + SizeType NewRange(unsigned codepoint) { | ||
| 552 | + Range* r = ranges_.template Push<Range>(); | ||
| 553 | + r->start = r->end = codepoint; | ||
| 554 | + r->next = kRegexInvalidRange; | ||
| 555 | + return rangeCount_++; | ||
| 556 | + } | ||
| 557 | + | ||
| 558 | + template <typename InputStream> | ||
| 559 | + bool CharacterEscape(DecodedStream<InputStream, Encoding>& ds, unsigned* escapedCodepoint) { | ||
| 560 | + unsigned codepoint; | ||
| 561 | + switch (codepoint = ds.Take()) { | ||
| 562 | + case '^': | ||
| 563 | + case '$': | ||
| 564 | + case '|': | ||
| 565 | + case '(': | ||
| 566 | + case ')': | ||
| 567 | + case '?': | ||
| 568 | + case '*': | ||
| 569 | + case '+': | ||
| 570 | + case '.': | ||
| 571 | + case '[': | ||
| 572 | + case ']': | ||
| 573 | + case '{': | ||
| 574 | + case '}': | ||
| 575 | + case '\\': | ||
| 576 | + *escapedCodepoint = codepoint; return true; | ||
| 577 | + case 'f': *escapedCodepoint = 0x000C; return true; | ||
| 578 | + case 'n': *escapedCodepoint = 0x000A; return true; | ||
| 579 | + case 'r': *escapedCodepoint = 0x000D; return true; | ||
| 580 | + case 't': *escapedCodepoint = 0x0009; return true; | ||
| 581 | + case 'v': *escapedCodepoint = 0x000B; return true; | ||
| 582 | + default: | ||
| 583 | + return false; // Unsupported escape character | ||
| 584 | + } | ||
| 585 | + } | ||
| 586 | + | ||
| 587 | + Stack<Allocator> states_; | ||
| 588 | + Stack<Allocator> ranges_; | ||
| 589 | + SizeType root_; | ||
| 590 | + SizeType stateCount_; | ||
| 591 | + SizeType rangeCount_; | ||
| 592 | + | ||
| 593 | + static const unsigned kInfinityQuantifier = ~0u; | ||
| 594 | + | ||
| 595 | + // For SearchWithAnchoring() | ||
| 596 | + bool anchorBegin_; | ||
| 597 | + bool anchorEnd_; | ||
| 598 | +}; | ||
| 599 | + | ||
| 600 | +template <typename RegexType, typename Allocator = CrtAllocator> | ||
| 601 | +class GenericRegexSearch { | ||
| 602 | +public: | ||
| 603 | + typedef typename RegexType::EncodingType Encoding; | ||
| 604 | + typedef typename Encoding::Ch Ch; | ||
| 605 | + | ||
| 606 | + GenericRegexSearch(const RegexType& regex, Allocator* allocator = 0) : | ||
| 607 | + regex_(regex), allocator_(allocator), ownAllocator_(0), | ||
| 608 | + state0_(allocator, 0), state1_(allocator, 0), stateSet_() | ||
| 609 | + { | ||
| 610 | + RAPIDJSON_ASSERT(regex_.IsValid()); | ||
| 611 | + if (!allocator_) | ||
| 612 | + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); | ||
| 613 | + stateSet_ = static_cast<unsigned*>(allocator_->Malloc(GetStateSetSize())); | ||
| 614 | + state0_.template Reserve<SizeType>(regex_.stateCount_); | ||
| 615 | + state1_.template Reserve<SizeType>(regex_.stateCount_); | ||
| 616 | + } | ||
| 617 | + | ||
| 618 | + ~GenericRegexSearch() { | ||
| 619 | + Allocator::Free(stateSet_); | ||
| 620 | + RAPIDJSON_DELETE(ownAllocator_); | ||
| 621 | + } | ||
| 622 | + | ||
| 623 | + template <typename InputStream> | ||
| 624 | + bool Match(InputStream& is) { | ||
| 625 | + return SearchWithAnchoring(is, true, true); | ||
| 626 | + } | ||
| 627 | + | ||
| 628 | + bool Match(const Ch* s) { | ||
| 629 | + GenericStringStream<Encoding> is(s); | ||
| 630 | + return Match(is); | ||
| 631 | + } | ||
| 632 | + | ||
| 633 | + template <typename InputStream> | ||
| 634 | + bool Search(InputStream& is) { | ||
| 635 | + return SearchWithAnchoring(is, regex_.anchorBegin_, regex_.anchorEnd_); | ||
| 636 | + } | ||
| 637 | + | ||
| 638 | + bool Search(const Ch* s) { | ||
| 639 | + GenericStringStream<Encoding> is(s); | ||
| 640 | + return Search(is); | ||
| 641 | + } | ||
| 642 | + | ||
| 643 | +private: | ||
| 644 | + typedef typename RegexType::State State; | ||
| 645 | + typedef typename RegexType::Range Range; | ||
| 646 | + | ||
| 647 | + template <typename InputStream> | ||
| 648 | + bool SearchWithAnchoring(InputStream& is, bool anchorBegin, bool anchorEnd) { | ||
| 649 | + DecodedStream<InputStream, Encoding> ds(is); | ||
| 650 | + | ||
| 651 | + state0_.Clear(); | ||
| 652 | + Stack<Allocator> *current = &state0_, *next = &state1_; | ||
| 653 | + const size_t stateSetSize = GetStateSetSize(); | ||
| 654 | + std::memset(stateSet_, 0, stateSetSize); | ||
| 655 | + | ||
| 656 | + bool matched = AddState(*current, regex_.root_); | ||
| 657 | + unsigned codepoint; | ||
| 658 | + while (!current->Empty() && (codepoint = ds.Take()) != 0) { | ||
| 659 | + std::memset(stateSet_, 0, stateSetSize); | ||
| 660 | + next->Clear(); | ||
| 661 | + matched = false; | ||
| 662 | + for (const SizeType* s = current->template Bottom<SizeType>(); s != current->template End<SizeType>(); ++s) { | ||
| 663 | + const State& sr = regex_.GetState(*s); | ||
| 664 | + if (sr.codepoint == codepoint || | ||
| 665 | + sr.codepoint == RegexType::kAnyCharacterClass || | ||
| 666 | + (sr.codepoint == RegexType::kRangeCharacterClass && MatchRange(sr.rangeStart, codepoint))) | ||
| 667 | + { | ||
| 668 | + matched = AddState(*next, sr.out) || matched; | ||
| 669 | + if (!anchorEnd && matched) | ||
| 670 | + return true; | ||
| 671 | + } | ||
| 672 | + if (!anchorBegin) | ||
| 673 | + AddState(*next, regex_.root_); | ||
| 674 | + } | ||
| 675 | + internal::Swap(current, next); | ||
| 676 | + } | ||
| 677 | + | ||
| 678 | + return matched; | ||
| 679 | + } | ||
| 680 | + | ||
| 681 | + size_t GetStateSetSize() const { | ||
| 682 | + return (regex_.stateCount_ + 31) / 32 * 4; | ||
| 683 | + } | ||
| 684 | + | ||
| 685 | + // Return whether the added states is a match state | ||
| 686 | + bool AddState(Stack<Allocator>& l, SizeType index) { | ||
| 687 | + RAPIDJSON_ASSERT(index != kRegexInvalidState); | ||
| 688 | + | ||
| 689 | + const State& s = regex_.GetState(index); | ||
| 690 | + if (s.out1 != kRegexInvalidState) { // Split | ||
| 691 | + bool matched = AddState(l, s.out); | ||
| 692 | + return AddState(l, s.out1) || matched; | ||
| 693 | + } | ||
| 694 | + else if (!(stateSet_[index >> 5] & (1u << (index & 31)))) { | ||
| 695 | + stateSet_[index >> 5] |= (1u << (index & 31)); | ||
| 696 | + *l.template PushUnsafe<SizeType>() = index; | ||
| 697 | + } | ||
| 698 | + return s.out == kRegexInvalidState; // by using PushUnsafe() above, we can ensure s is not validated due to reallocation. | ||
| 699 | + } | ||
| 700 | + | ||
| 701 | + bool MatchRange(SizeType rangeIndex, unsigned codepoint) const { | ||
| 702 | + bool yes = (regex_.GetRange(rangeIndex).start & RegexType::kRangeNegationFlag) == 0; | ||
| 703 | + while (rangeIndex != kRegexInvalidRange) { | ||
| 704 | + const Range& r = regex_.GetRange(rangeIndex); | ||
| 705 | + if (codepoint >= (r.start & ~RegexType::kRangeNegationFlag) && codepoint <= r.end) | ||
| 706 | + return yes; | ||
| 707 | + rangeIndex = r.next; | ||
| 708 | + } | ||
| 709 | + return !yes; | ||
| 710 | + } | ||
| 711 | + | ||
| 712 | + const RegexType& regex_; | ||
| 713 | + Allocator* allocator_; | ||
| 714 | + Allocator* ownAllocator_; | ||
| 715 | + Stack<Allocator> state0_; | ||
| 716 | + Stack<Allocator> state1_; | ||
| 717 | + uint32_t* stateSet_; | ||
| 718 | +}; | ||
| 719 | + | ||
| 720 | +typedef GenericRegex<UTF8<> > Regex; | ||
| 721 | +typedef GenericRegexSearch<Regex> RegexSearch; | ||
| 722 | + | ||
| 723 | +} // namespace internal | ||
| 724 | +RAPIDJSON_NAMESPACE_END | ||
| 725 | + | ||
| 726 | +#ifdef __clang__ | ||
| 727 | +RAPIDJSON_DIAG_POP | ||
| 728 | +#endif | ||
| 729 | + | ||
| 730 | +#ifdef _MSC_VER | ||
| 731 | +RAPIDJSON_DIAG_POP | ||
| 732 | +#endif | ||
| 733 | + | ||
| 734 | +#endif // RAPIDJSON_INTERNAL_REGEX_H_ |
| 1 | +// Tencent is pleased to support the open source community by making RapidJSON available. | ||
| 2 | +// | ||
| 3 | +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. | ||
| 4 | +// | ||
| 5 | +// Licensed under the MIT License (the "License"); you may not use this file except | ||
| 6 | +// in compliance with the License. You may obtain a copy of the License at | ||
| 7 | +// | ||
| 8 | +// http://opensource.org/licenses/MIT | ||
| 9 | +// | ||
| 10 | +// Unless required by applicable law or agreed to in writing, software distributed | ||
| 11 | +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR | ||
| 12 | +// CONDITIONS OF ANY KIND, either express or implied. See the License for the | ||
| 13 | +// specific language governing permissions and limitations under the License. | ||
| 14 | + | ||
| 15 | +#ifndef RAPIDJSON_INTERNAL_STACK_H_ | ||
| 16 | +#define RAPIDJSON_INTERNAL_STACK_H_ | ||
| 17 | + | ||
| 18 | +#include "../allocators.h" | ||
| 19 | +#include "swap.h" | ||
| 20 | + | ||
| 21 | +#if defined(__clang__) | ||
| 22 | +RAPIDJSON_DIAG_PUSH | ||
| 23 | +RAPIDJSON_DIAG_OFF(c++98-compat) | ||
| 24 | +#endif | ||
| 25 | + | ||
| 26 | +RAPIDJSON_NAMESPACE_BEGIN | ||
| 27 | +namespace internal { | ||
| 28 | + | ||
| 29 | +/////////////////////////////////////////////////////////////////////////////// | ||
| 30 | +// Stack | ||
| 31 | + | ||
| 32 | +//! A type-unsafe stack for storing different types of data. | ||
| 33 | +/*! \tparam Allocator Allocator for allocating stack memory. | ||
| 34 | +*/ | ||
| 35 | +template <typename Allocator> | ||
| 36 | +class Stack { | ||
| 37 | +public: | ||
| 38 | + // Optimization note: Do not allocate memory for stack_ in constructor. | ||
| 39 | + // Do it lazily when first Push() -> Expand() -> Resize(). | ||
| 40 | + Stack(Allocator* allocator, size_t stackCapacity) : allocator_(allocator), ownAllocator_(0), stack_(0), stackTop_(0), stackEnd_(0), initialCapacity_(stackCapacity) { | ||
| 41 | + } | ||
| 42 | + | ||
| 43 | +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS | ||
| 44 | + Stack(Stack&& rhs) | ||
| 45 | + : allocator_(rhs.allocator_), | ||
| 46 | + ownAllocator_(rhs.ownAllocator_), | ||
| 47 | + stack_(rhs.stack_), | ||
| 48 | + stackTop_(rhs.stackTop_), | ||
| 49 | + stackEnd_(rhs.stackEnd_), | ||
| 50 | + initialCapacity_(rhs.initialCapacity_) | ||
| 51 | + { | ||
| 52 | + rhs.allocator_ = 0; | ||
| 53 | + rhs.ownAllocator_ = 0; | ||
| 54 | + rhs.stack_ = 0; | ||
| 55 | + rhs.stackTop_ = 0; | ||
| 56 | + rhs.stackEnd_ = 0; | ||
| 57 | + rhs.initialCapacity_ = 0; | ||
| 58 | + } | ||
| 59 | +#endif | ||
| 60 | + | ||
| 61 | + ~Stack() { | ||
| 62 | + Destroy(); | ||
| 63 | + } | ||
| 64 | + | ||
| 65 | +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS | ||
| 66 | + Stack& operator=(Stack&& rhs) { | ||
| 67 | + if (&rhs != this) | ||
| 68 | + { | ||
| 69 | + Destroy(); | ||
| 70 | + | ||
| 71 | + allocator_ = rhs.allocator_; | ||
| 72 | + ownAllocator_ = rhs.ownAllocator_; | ||
| 73 | + stack_ = rhs.stack_; | ||
| 74 | + stackTop_ = rhs.stackTop_; | ||
| 75 | + stackEnd_ = rhs.stackEnd_; | ||
| 76 | + initialCapacity_ = rhs.initialCapacity_; | ||
| 77 | + | ||
| 78 | + rhs.allocator_ = 0; | ||
| 79 | + rhs.ownAllocator_ = 0; | ||
| 80 | + rhs.stack_ = 0; | ||
| 81 | + rhs.stackTop_ = 0; | ||
| 82 | + rhs.stackEnd_ = 0; | ||
| 83 | + rhs.initialCapacity_ = 0; | ||
| 84 | + } | ||
| 85 | + return *this; | ||
| 86 | + } | ||
| 87 | +#endif | ||
| 88 | + | ||
| 89 | + void Swap(Stack& rhs) RAPIDJSON_NOEXCEPT { | ||
| 90 | + internal::Swap(allocator_, rhs.allocator_); | ||
| 91 | + internal::Swap(ownAllocator_, rhs.ownAllocator_); | ||
| 92 | + internal::Swap(stack_, rhs.stack_); | ||
| 93 | + internal::Swap(stackTop_, rhs.stackTop_); | ||
| 94 | + internal::Swap(stackEnd_, rhs.stackEnd_); | ||
| 95 | + internal::Swap(initialCapacity_, rhs.initialCapacity_); | ||
| 96 | + } | ||
| 97 | + | ||
| 98 | + void Clear() { stackTop_ = stack_; } | ||
| 99 | + | ||
| 100 | + void ShrinkToFit() { | ||
| 101 | + if (Empty()) { | ||
| 102 | + // If the stack is empty, completely deallocate the memory. | ||
| 103 | + Allocator::Free(stack_); | ||
| 104 | + stack_ = 0; | ||
| 105 | + stackTop_ = 0; | ||
| 106 | + stackEnd_ = 0; | ||
| 107 | + } | ||
| 108 | + else | ||
| 109 | + Resize(GetSize()); | ||
| 110 | + } | ||
| 111 | + | ||
| 112 | + // Optimization note: try to minimize the size of this function for force inline. | ||
| 113 | + // Expansion is run very infrequently, so it is moved to another (probably non-inline) function. | ||
| 114 | + template<typename T> | ||
| 115 | + RAPIDJSON_FORCEINLINE void Reserve(size_t count = 1) { | ||
| 116 | + // Expand the stack if needed | ||
| 117 | + if (RAPIDJSON_UNLIKELY(stackTop_ + sizeof(T) * count > stackEnd_)) | ||
| 118 | + Expand<T>(count); | ||
| 119 | + } | ||
| 120 | + | ||
| 121 | + template<typename T> | ||
| 122 | + RAPIDJSON_FORCEINLINE T* Push(size_t count = 1) { | ||
| 123 | + Reserve<T>(count); | ||
| 124 | + return PushUnsafe<T>(count); | ||
| 125 | + } | ||
| 126 | + | ||
| 127 | + template<typename T> | ||
| 128 | + RAPIDJSON_FORCEINLINE T* PushUnsafe(size_t count = 1) { | ||
| 129 | + RAPIDJSON_ASSERT(stackTop_); | ||
| 130 | + RAPIDJSON_ASSERT(stackTop_ + sizeof(T) * count <= stackEnd_); | ||
| 131 | + T* ret = reinterpret_cast<T*>(stackTop_); | ||
| 132 | + stackTop_ += sizeof(T) * count; | ||
| 133 | + return ret; | ||
| 134 | + } | ||
| 135 | + | ||
| 136 | + template<typename T> | ||
| 137 | + T* Pop(size_t count) { | ||
| 138 | + RAPIDJSON_ASSERT(GetSize() >= count * sizeof(T)); | ||
| 139 | + stackTop_ -= count * sizeof(T); | ||
| 140 | + return reinterpret_cast<T*>(stackTop_); | ||
| 141 | + } | ||
| 142 | + | ||
| 143 | + template<typename T> | ||
| 144 | + T* Top() { | ||
| 145 | + RAPIDJSON_ASSERT(GetSize() >= sizeof(T)); | ||
| 146 | + return reinterpret_cast<T*>(stackTop_ - sizeof(T)); | ||
| 147 | + } | ||
| 148 | + | ||
| 149 | + template<typename T> | ||
| 150 | + const T* Top() const { | ||
| 151 | + RAPIDJSON_ASSERT(GetSize() >= sizeof(T)); | ||
| 152 | + return reinterpret_cast<T*>(stackTop_ - sizeof(T)); | ||
| 153 | + } | ||
| 154 | + | ||
| 155 | + template<typename T> | ||
| 156 | + T* End() { return reinterpret_cast<T*>(stackTop_); } | ||
| 157 | + | ||
| 158 | + template<typename T> | ||
| 159 | + const T* End() const { return reinterpret_cast<T*>(stackTop_); } | ||
| 160 | + | ||
| 161 | + template<typename T> | ||
| 162 | + T* Bottom() { return reinterpret_cast<T*>(stack_); } | ||
| 163 | + | ||
| 164 | + template<typename T> | ||
| 165 | + const T* Bottom() const { return reinterpret_cast<T*>(stack_); } | ||
| 166 | + | ||
| 167 | + bool HasAllocator() const { | ||
| 168 | + return allocator_ != 0; | ||
| 169 | + } | ||
| 170 | + | ||
| 171 | + Allocator& GetAllocator() { | ||
| 172 | + RAPIDJSON_ASSERT(allocator_); | ||
| 173 | + return *allocator_; | ||
| 174 | + } | ||
| 175 | + | ||
| 176 | + bool Empty() const { return stackTop_ == stack_; } | ||
| 177 | + size_t GetSize() const { return static_cast<size_t>(stackTop_ - stack_); } | ||
| 178 | + size_t GetCapacity() const { return static_cast<size_t>(stackEnd_ - stack_); } | ||
| 179 | + | ||
| 180 | +private: | ||
| 181 | + template<typename T> | ||
| 182 | + void Expand(size_t count) { | ||
| 183 | + // Only expand the capacity if the current stack exists. Otherwise just create a stack with initial capacity. | ||
| 184 | + size_t newCapacity; | ||
| 185 | + if (stack_ == 0) { | ||
| 186 | + if (!allocator_) | ||
| 187 | + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); | ||
| 188 | + newCapacity = initialCapacity_; | ||
| 189 | + } else { | ||
| 190 | + newCapacity = GetCapacity(); | ||
| 191 | + newCapacity += (newCapacity + 1) / 2; | ||
| 192 | + } | ||
| 193 | + size_t newSize = GetSize() + sizeof(T) * count; | ||
| 194 | + if (newCapacity < newSize) | ||
| 195 | + newCapacity = newSize; | ||
| 196 | + | ||
| 197 | + Resize(newCapacity); | ||
| 198 | + } | ||
| 199 | + | ||
| 200 | + void Resize(size_t newCapacity) { | ||
| 201 | + const size_t size = GetSize(); // Backup the current size | ||
| 202 | + stack_ = static_cast<char*>(allocator_->Realloc(stack_, GetCapacity(), newCapacity)); | ||
| 203 | + stackTop_ = stack_ + size; | ||
| 204 | + stackEnd_ = stack_ + newCapacity; | ||
| 205 | + } | ||
| 206 | + | ||
| 207 | + void Destroy() { | ||
| 208 | + Allocator::Free(stack_); | ||
| 209 | + RAPIDJSON_DELETE(ownAllocator_); // Only delete if it is owned by the stack | ||
| 210 | + } | ||
| 211 | + | ||
| 212 | + // Prohibit copy constructor & assignment operator. | ||
| 213 | + Stack(const Stack&); | ||
| 214 | + Stack& operator=(const Stack&); | ||
| 215 | + | ||
| 216 | + Allocator* allocator_; | ||
| 217 | + Allocator* ownAllocator_; | ||
| 218 | + char *stack_; | ||
| 219 | + char *stackTop_; | ||
| 220 | + char *stackEnd_; | ||
| 221 | + size_t initialCapacity_; | ||
| 222 | +}; | ||
| 223 | + | ||
| 224 | +} // namespace internal | ||
| 225 | +RAPIDJSON_NAMESPACE_END | ||
| 226 | + | ||
| 227 | +#if defined(__clang__) | ||
| 228 | +RAPIDJSON_DIAG_POP | ||
| 229 | +#endif | ||
| 230 | + | ||
| 231 | +#endif // RAPIDJSON_STACK_H_ |
| 1 | +// Tencent is pleased to support the open source community by making RapidJSON available. | ||
| 2 | +// | ||
| 3 | +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. | ||
| 4 | +// | ||
| 5 | +// Licensed under the MIT License (the "License"); you may not use this file except | ||
| 6 | +// in compliance with the License. You may obtain a copy of the License at | ||
| 7 | +// | ||
| 8 | +// http://opensource.org/licenses/MIT | ||
| 9 | +// | ||
| 10 | +// Unless required by applicable law or agreed to in writing, software distributed | ||
| 11 | +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR | ||
| 12 | +// CONDITIONS OF ANY KIND, either express or implied. See the License for the | ||
| 13 | +// specific language governing permissions and limitations under the License. | ||
| 14 | + | ||
| 15 | +#ifndef RAPIDJSON_INTERNAL_STRFUNC_H_ | ||
| 16 | +#define RAPIDJSON_INTERNAL_STRFUNC_H_ | ||
| 17 | + | ||
| 18 | +#include "../stream.h" | ||
| 19 | +#include <cwchar> | ||
| 20 | + | ||
| 21 | +RAPIDJSON_NAMESPACE_BEGIN | ||
| 22 | +namespace internal { | ||
| 23 | + | ||
| 24 | +//! Custom strlen() which works on different character types. | ||
| 25 | +/*! \tparam Ch Character type (e.g. char, wchar_t, short) | ||
| 26 | + \param s Null-terminated input string. | ||
| 27 | + \return Number of characters in the string. | ||
| 28 | + \note This has the same semantics as strlen(), the return value is not number of Unicode codepoints. | ||
| 29 | +*/ | ||
| 30 | +template <typename Ch> | ||
| 31 | +inline SizeType StrLen(const Ch* s) { | ||
| 32 | + RAPIDJSON_ASSERT(s != 0); | ||
| 33 | + const Ch* p = s; | ||
| 34 | + while (*p) ++p; | ||
| 35 | + return SizeType(p - s); | ||
| 36 | +} | ||
| 37 | + | ||
| 38 | +template <> | ||
| 39 | +inline SizeType StrLen(const char* s) { | ||
| 40 | + return SizeType(std::strlen(s)); | ||
| 41 | +} | ||
| 42 | + | ||
| 43 | +template <> | ||
| 44 | +inline SizeType StrLen(const wchar_t* s) { | ||
| 45 | + return SizeType(std::wcslen(s)); | ||
| 46 | +} | ||
| 47 | + | ||
| 48 | +//! Returns number of code points in a encoded string. | ||
| 49 | +template<typename Encoding> | ||
| 50 | +bool CountStringCodePoint(const typename Encoding::Ch* s, SizeType length, SizeType* outCount) { | ||
| 51 | + RAPIDJSON_ASSERT(s != 0); | ||
| 52 | + RAPIDJSON_ASSERT(outCount != 0); | ||
| 53 | + GenericStringStream<Encoding> is(s); | ||
| 54 | + const typename Encoding::Ch* end = s + length; | ||
| 55 | + SizeType count = 0; | ||
| 56 | + while (is.src_ < end) { | ||
| 57 | + unsigned codepoint; | ||
| 58 | + if (!Encoding::Decode(is, &codepoint)) | ||
| 59 | + return false; | ||
| 60 | + count++; | ||
| 61 | + } | ||
| 62 | + *outCount = count; | ||
| 63 | + return true; | ||
| 64 | +} | ||
| 65 | + | ||
| 66 | +} // namespace internal | ||
| 67 | +RAPIDJSON_NAMESPACE_END | ||
| 68 | + | ||
| 69 | +#endif // RAPIDJSON_INTERNAL_STRFUNC_H_ |
| 1 | +// Tencent is pleased to support the open source community by making RapidJSON available. | ||
| 2 | +// | ||
| 3 | +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. | ||
| 4 | +// | ||
| 5 | +// Licensed under the MIT License (the "License"); you may not use this file except | ||
| 6 | +// in compliance with the License. You may obtain a copy of the License at | ||
| 7 | +// | ||
| 8 | +// http://opensource.org/licenses/MIT | ||
| 9 | +// | ||
| 10 | +// Unless required by applicable law or agreed to in writing, software distributed | ||
| 11 | +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR | ||
| 12 | +// CONDITIONS OF ANY KIND, either express or implied. See the License for the | ||
| 13 | +// specific language governing permissions and limitations under the License. | ||
| 14 | + | ||
| 15 | +#ifndef RAPIDJSON_STRTOD_ | ||
| 16 | +#define RAPIDJSON_STRTOD_ | ||
| 17 | + | ||
| 18 | +#include "ieee754.h" | ||
| 19 | +#include "biginteger.h" | ||
| 20 | +#include "diyfp.h" | ||
| 21 | +#include "pow10.h" | ||
| 22 | + | ||
| 23 | +RAPIDJSON_NAMESPACE_BEGIN | ||
| 24 | +namespace internal { | ||
| 25 | + | ||
| 26 | +inline double FastPath(double significand, int exp) { | ||
| 27 | + if (exp < -308) | ||
| 28 | + return 0.0; | ||
| 29 | + else if (exp >= 0) | ||
| 30 | + return significand * internal::Pow10(exp); | ||
| 31 | + else | ||
| 32 | + return significand / internal::Pow10(-exp); | ||
| 33 | +} | ||
| 34 | + | ||
| 35 | +inline double StrtodNormalPrecision(double d, int p) { | ||
| 36 | + if (p < -308) { | ||
| 37 | + // Prevent expSum < -308, making Pow10(p) = 0 | ||
| 38 | + d = FastPath(d, -308); | ||
| 39 | + d = FastPath(d, p + 308); | ||
| 40 | + } | ||
| 41 | + else | ||
| 42 | + d = FastPath(d, p); | ||
| 43 | + return d; | ||
| 44 | +} | ||
| 45 | + | ||
| 46 | +template <typename T> | ||
| 47 | +inline T Min3(T a, T b, T c) { | ||
| 48 | + T m = a; | ||
| 49 | + if (m > b) m = b; | ||
| 50 | + if (m > c) m = c; | ||
| 51 | + return m; | ||
| 52 | +} | ||
| 53 | + | ||
| 54 | +inline int CheckWithinHalfULP(double b, const BigInteger& d, int dExp) { | ||
| 55 | + const Double db(b); | ||
| 56 | + const uint64_t bInt = db.IntegerSignificand(); | ||
| 57 | + const int bExp = db.IntegerExponent(); | ||
| 58 | + const int hExp = bExp - 1; | ||
| 59 | + | ||
| 60 | + int dS_Exp2 = 0, dS_Exp5 = 0, bS_Exp2 = 0, bS_Exp5 = 0, hS_Exp2 = 0, hS_Exp5 = 0; | ||
| 61 | + | ||
| 62 | + // Adjust for decimal exponent | ||
| 63 | + if (dExp >= 0) { | ||
| 64 | + dS_Exp2 += dExp; | ||
| 65 | + dS_Exp5 += dExp; | ||
| 66 | + } | ||
| 67 | + else { | ||
| 68 | + bS_Exp2 -= dExp; | ||
| 69 | + bS_Exp5 -= dExp; | ||
| 70 | + hS_Exp2 -= dExp; | ||
| 71 | + hS_Exp5 -= dExp; | ||
| 72 | + } | ||
| 73 | + | ||
| 74 | + // Adjust for binary exponent | ||
| 75 | + if (bExp >= 0) | ||
| 76 | + bS_Exp2 += bExp; | ||
| 77 | + else { | ||
| 78 | + dS_Exp2 -= bExp; | ||
| 79 | + hS_Exp2 -= bExp; | ||
| 80 | + } | ||
| 81 | + | ||
| 82 | + // Adjust for half ulp exponent | ||
| 83 | + if (hExp >= 0) | ||
| 84 | + hS_Exp2 += hExp; | ||
| 85 | + else { | ||
| 86 | + dS_Exp2 -= hExp; | ||
| 87 | + bS_Exp2 -= hExp; | ||
| 88 | + } | ||
| 89 | + | ||
| 90 | + // Remove common power of two factor from all three scaled values | ||
| 91 | + int common_Exp2 = Min3(dS_Exp2, bS_Exp2, hS_Exp2); | ||
| 92 | + dS_Exp2 -= common_Exp2; | ||
| 93 | + bS_Exp2 -= common_Exp2; | ||
| 94 | + hS_Exp2 -= common_Exp2; | ||
| 95 | + | ||
| 96 | + BigInteger dS = d; | ||
| 97 | + dS.MultiplyPow5(static_cast<unsigned>(dS_Exp5)) <<= static_cast<unsigned>(dS_Exp2); | ||
| 98 | + | ||
| 99 | + BigInteger bS(bInt); | ||
| 100 | + bS.MultiplyPow5(static_cast<unsigned>(bS_Exp5)) <<= static_cast<unsigned>(bS_Exp2); | ||
| 101 | + | ||
| 102 | + BigInteger hS(1); | ||
| 103 | + hS.MultiplyPow5(static_cast<unsigned>(hS_Exp5)) <<= static_cast<unsigned>(hS_Exp2); | ||
| 104 | + | ||
| 105 | + BigInteger delta(0); | ||
| 106 | + dS.Difference(bS, &delta); | ||
| 107 | + | ||
| 108 | + return delta.Compare(hS); | ||
| 109 | +} | ||
| 110 | + | ||
| 111 | +inline bool StrtodFast(double d, int p, double* result) { | ||
| 112 | + // Use fast path for string-to-double conversion if possible | ||
| 113 | + // see http://www.exploringbinary.com/fast-path-decimal-to-floating-point-conversion/ | ||
| 114 | + if (p > 22 && p < 22 + 16) { | ||
| 115 | + // Fast Path Cases In Disguise | ||
| 116 | + d *= internal::Pow10(p - 22); | ||
| 117 | + p = 22; | ||
| 118 | + } | ||
| 119 | + | ||
| 120 | + if (p >= -22 && p <= 22 && d <= 9007199254740991.0) { // 2^53 - 1 | ||
| 121 | + *result = FastPath(d, p); | ||
| 122 | + return true; | ||
| 123 | + } | ||
| 124 | + else | ||
| 125 | + return false; | ||
| 126 | +} | ||
| 127 | + | ||
| 128 | +// Compute an approximation and see if it is within 1/2 ULP | ||
| 129 | +inline bool StrtodDiyFp(const char* decimals, size_t length, size_t decimalPosition, int exp, double* result) { | ||
| 130 | + uint64_t significand = 0; | ||
| 131 | + size_t i = 0; // 2^64 - 1 = 18446744073709551615, 1844674407370955161 = 0x1999999999999999 | ||
| 132 | + for (; i < length; i++) { | ||
| 133 | + if (significand > RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) || | ||
| 134 | + (significand == RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) && decimals[i] > '5')) | ||
| 135 | + break; | ||
| 136 | + significand = significand * 10u + static_cast<unsigned>(decimals[i] - '0'); | ||
| 137 | + } | ||
| 138 | + | ||
| 139 | + if (i < length && decimals[i] >= '5') // Rounding | ||
| 140 | + significand++; | ||
| 141 | + | ||
| 142 | + size_t remaining = length - i; | ||
| 143 | + const int kUlpShift = 3; | ||
| 144 | + const int kUlp = 1 << kUlpShift; | ||
| 145 | + int64_t error = (remaining == 0) ? 0 : kUlp / 2; | ||
| 146 | + | ||
| 147 | + DiyFp v(significand, 0); | ||
| 148 | + v = v.Normalize(); | ||
| 149 | + error <<= -v.e; | ||
| 150 | + | ||
| 151 | + const int dExp = static_cast<int>(decimalPosition) - static_cast<int>(i) + exp; | ||
| 152 | + | ||
| 153 | + int actualExp; | ||
| 154 | + DiyFp cachedPower = GetCachedPower10(dExp, &actualExp); | ||
| 155 | + if (actualExp != dExp) { | ||
| 156 | + static const DiyFp kPow10[] = { | ||
| 157 | + DiyFp(RAPIDJSON_UINT64_C2(0xa0000000, 00000000), -60), // 10^1 | ||
| 158 | + DiyFp(RAPIDJSON_UINT64_C2(0xc8000000, 00000000), -57), // 10^2 | ||
| 159 | + DiyFp(RAPIDJSON_UINT64_C2(0xfa000000, 00000000), -54), // 10^3 | ||
| 160 | + DiyFp(RAPIDJSON_UINT64_C2(0x9c400000, 00000000), -50), // 10^4 | ||
| 161 | + DiyFp(RAPIDJSON_UINT64_C2(0xc3500000, 00000000), -47), // 10^5 | ||
| 162 | + DiyFp(RAPIDJSON_UINT64_C2(0xf4240000, 00000000), -44), // 10^6 | ||
| 163 | + DiyFp(RAPIDJSON_UINT64_C2(0x98968000, 00000000), -40) // 10^7 | ||
| 164 | + }; | ||
| 165 | + int adjustment = dExp - actualExp - 1; | ||
| 166 | + RAPIDJSON_ASSERT(adjustment >= 0 && adjustment < 7); | ||
| 167 | + v = v * kPow10[adjustment]; | ||
| 168 | + if (length + static_cast<unsigned>(adjustment)> 19u) // has more digits than decimal digits in 64-bit | ||
| 169 | + error += kUlp / 2; | ||
| 170 | + } | ||
| 171 | + | ||
| 172 | + v = v * cachedPower; | ||
| 173 | + | ||
| 174 | + error += kUlp + (error == 0 ? 0 : 1); | ||
| 175 | + | ||
| 176 | + const int oldExp = v.e; | ||
| 177 | + v = v.Normalize(); | ||
| 178 | + error <<= oldExp - v.e; | ||
| 179 | + | ||
| 180 | + const int effectiveSignificandSize = Double::EffectiveSignificandSize(64 + v.e); | ||
| 181 | + int precisionSize = 64 - effectiveSignificandSize; | ||
| 182 | + if (precisionSize + kUlpShift >= 64) { | ||
| 183 | + int scaleExp = (precisionSize + kUlpShift) - 63; | ||
| 184 | + v.f >>= scaleExp; | ||
| 185 | + v.e += scaleExp; | ||
| 186 | + error = (error >> scaleExp) + 1 + kUlp; | ||
| 187 | + precisionSize -= scaleExp; | ||
| 188 | + } | ||
| 189 | + | ||
| 190 | + DiyFp rounded(v.f >> precisionSize, v.e + precisionSize); | ||
| 191 | + const uint64_t precisionBits = (v.f & ((uint64_t(1) << precisionSize) - 1)) * kUlp; | ||
| 192 | + const uint64_t halfWay = (uint64_t(1) << (precisionSize - 1)) * kUlp; | ||
| 193 | + if (precisionBits >= halfWay + static_cast<unsigned>(error)) { | ||
| 194 | + rounded.f++; | ||
| 195 | + if (rounded.f & (DiyFp::kDpHiddenBit << 1)) { // rounding overflows mantissa (issue #340) | ||
| 196 | + rounded.f >>= 1; | ||
| 197 | + rounded.e++; | ||
| 198 | + } | ||
| 199 | + } | ||
| 200 | + | ||
| 201 | + *result = rounded.ToDouble(); | ||
| 202 | + | ||
| 203 | + return halfWay - static_cast<unsigned>(error) >= precisionBits || precisionBits >= halfWay + static_cast<unsigned>(error); | ||
| 204 | +} | ||
| 205 | + | ||
| 206 | +inline double StrtodBigInteger(double approx, const char* decimals, size_t length, size_t decimalPosition, int exp) { | ||
| 207 | + const BigInteger dInt(decimals, length); | ||
| 208 | + const int dExp = static_cast<int>(decimalPosition) - static_cast<int>(length) + exp; | ||
| 209 | + Double a(approx); | ||
| 210 | + int cmp = CheckWithinHalfULP(a.Value(), dInt, dExp); | ||
| 211 | + if (cmp < 0) | ||
| 212 | + return a.Value(); // within half ULP | ||
| 213 | + else if (cmp == 0) { | ||
| 214 | + // Round towards even | ||
| 215 | + if (a.Significand() & 1) | ||
| 216 | + return a.NextPositiveDouble(); | ||
| 217 | + else | ||
| 218 | + return a.Value(); | ||
| 219 | + } | ||
| 220 | + else // adjustment | ||
| 221 | + return a.NextPositiveDouble(); | ||
| 222 | +} | ||
| 223 | + | ||
| 224 | +inline double StrtodFullPrecision(double d, int p, const char* decimals, size_t length, size_t decimalPosition, int exp) { | ||
| 225 | + RAPIDJSON_ASSERT(d >= 0.0); | ||
| 226 | + RAPIDJSON_ASSERT(length >= 1); | ||
| 227 | + | ||
| 228 | + double result; | ||
| 229 | + if (StrtodFast(d, p, &result)) | ||
| 230 | + return result; | ||
| 231 | + | ||
| 232 | + // Trim leading zeros | ||
| 233 | + while (*decimals == '0' && length > 1) { | ||
| 234 | + length--; | ||
| 235 | + decimals++; | ||
| 236 | + decimalPosition--; | ||
| 237 | + } | ||
| 238 | + | ||
| 239 | + // Trim trailing zeros | ||
| 240 | + while (decimals[length - 1] == '0' && length > 1) { | ||
| 241 | + length--; | ||
| 242 | + decimalPosition--; | ||
| 243 | + exp++; | ||
| 244 | + } | ||
| 245 | + | ||
| 246 | + // Trim right-most digits | ||
| 247 | + const int kMaxDecimalDigit = 780; | ||
| 248 | + if (static_cast<int>(length) > kMaxDecimalDigit) { | ||
| 249 | + int delta = (static_cast<int>(length) - kMaxDecimalDigit); | ||
| 250 | + exp += delta; | ||
| 251 | + decimalPosition -= static_cast<unsigned>(delta); | ||
| 252 | + length = kMaxDecimalDigit; | ||
| 253 | + } | ||
| 254 | + | ||
| 255 | + // If too small, underflow to zero | ||
| 256 | + if (int(length) + exp < -324) | ||
| 257 | + return 0.0; | ||
| 258 | + | ||
| 259 | + if (StrtodDiyFp(decimals, length, decimalPosition, exp, &result)) | ||
| 260 | + return result; | ||
| 261 | + | ||
| 262 | + // Use approximation from StrtodDiyFp and make adjustment with BigInteger comparison | ||
| 263 | + return StrtodBigInteger(result, decimals, length, decimalPosition, exp); | ||
| 264 | +} | ||
| 265 | + | ||
| 266 | +} // namespace internal | ||
| 267 | +RAPIDJSON_NAMESPACE_END | ||
| 268 | + | ||
| 269 | +#endif // RAPIDJSON_STRTOD_ |
PaperGraph/include/rapidjson/internal/swap.h
0 → 100644
| 1 | +// Tencent is pleased to support the open source community by making RapidJSON available. | ||
| 2 | +// | ||
| 3 | +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. | ||
| 4 | +// | ||
| 5 | +// Licensed under the MIT License (the "License"); you may not use this file except | ||
| 6 | +// in compliance with the License. You may obtain a copy of the License at | ||
| 7 | +// | ||
| 8 | +// http://opensource.org/licenses/MIT | ||
| 9 | +// | ||
| 10 | +// Unless required by applicable law or agreed to in writing, software distributed | ||
| 11 | +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR | ||
| 12 | +// CONDITIONS OF ANY KIND, either express or implied. See the License for the | ||
| 13 | +// specific language governing permissions and limitations under the License. | ||
| 14 | + | ||
| 15 | +#ifndef RAPIDJSON_INTERNAL_SWAP_H_ | ||
| 16 | +#define RAPIDJSON_INTERNAL_SWAP_H_ | ||
| 17 | + | ||
| 18 | +#include "../rapidjson.h" | ||
| 19 | + | ||
| 20 | +#if defined(__clang__) | ||
| 21 | +RAPIDJSON_DIAG_PUSH | ||
| 22 | +RAPIDJSON_DIAG_OFF(c++98-compat) | ||
| 23 | +#endif | ||
| 24 | + | ||
| 25 | +RAPIDJSON_NAMESPACE_BEGIN | ||
| 26 | +namespace internal { | ||
| 27 | + | ||
| 28 | +//! Custom swap() to avoid dependency on C++ <algorithm> header | ||
| 29 | +/*! \tparam T Type of the arguments to swap, should be instantiated with primitive C++ types only. | ||
| 30 | + \note This has the same semantics as std::swap(). | ||
| 31 | +*/ | ||
| 32 | +template <typename T> | ||
| 33 | +inline void Swap(T& a, T& b) RAPIDJSON_NOEXCEPT { | ||
| 34 | + T tmp = a; | ||
| 35 | + a = b; | ||
| 36 | + b = tmp; | ||
| 37 | +} | ||
| 38 | + | ||
| 39 | +} // namespace internal | ||
| 40 | +RAPIDJSON_NAMESPACE_END | ||
| 41 | + | ||
| 42 | +#if defined(__clang__) | ||
| 43 | +RAPIDJSON_DIAG_POP | ||
| 44 | +#endif | ||
| 45 | + | ||
| 46 | +#endif // RAPIDJSON_INTERNAL_SWAP_H_ |
| 1 | +// Tencent is pleased to support the open source community by making RapidJSON available. | ||
| 2 | +// | ||
| 3 | +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. | ||
| 4 | +// | ||
| 5 | +// Licensed under the MIT License (the "License"); you may not use this file except | ||
| 6 | +// in compliance with the License. You may obtain a copy of the License at | ||
| 7 | +// | ||
| 8 | +// http://opensource.org/licenses/MIT | ||
| 9 | +// | ||
| 10 | +// Unless required by applicable law or agreed to in writing, software distributed | ||
| 11 | +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR | ||
| 12 | +// CONDITIONS OF ANY KIND, either express or implied. See the License for the | ||
| 13 | +// specific language governing permissions and limitations under the License. | ||
| 14 | + | ||
| 15 | +#ifndef RAPIDJSON_ISTREAMWRAPPER_H_ | ||
| 16 | +#define RAPIDJSON_ISTREAMWRAPPER_H_ | ||
| 17 | + | ||
| 18 | +#include "stream.h" | ||
| 19 | +#include <iosfwd> | ||
| 20 | + | ||
| 21 | +#ifdef __clang__ | ||
| 22 | +RAPIDJSON_DIAG_PUSH | ||
| 23 | +RAPIDJSON_DIAG_OFF(padded) | ||
| 24 | +#endif | ||
| 25 | + | ||
| 26 | +#ifdef _MSC_VER | ||
| 27 | +RAPIDJSON_DIAG_PUSH | ||
| 28 | +RAPIDJSON_DIAG_OFF(4351) // new behavior: elements of array 'array' will be default initialized | ||
| 29 | +#endif | ||
| 30 | + | ||
| 31 | +RAPIDJSON_NAMESPACE_BEGIN | ||
| 32 | + | ||
| 33 | +//! Wrapper of \c std::basic_istream into RapidJSON's Stream concept. | ||
| 34 | +/*! | ||
| 35 | + The classes can be wrapped including but not limited to: | ||
| 36 | + | ||
| 37 | + - \c std::istringstream | ||
| 38 | + - \c std::stringstream | ||
| 39 | + - \c std::wistringstream | ||
| 40 | + - \c std::wstringstream | ||
| 41 | + - \c std::ifstream | ||
| 42 | + - \c std::fstream | ||
| 43 | + - \c std::wifstream | ||
| 44 | + - \c std::wfstream | ||
| 45 | + | ||
| 46 | + \tparam StreamType Class derived from \c std::basic_istream. | ||
| 47 | +*/ | ||
| 48 | + | ||
| 49 | +template <typename StreamType> | ||
| 50 | +class BasicIStreamWrapper { | ||
| 51 | +public: | ||
| 52 | + typedef typename StreamType::char_type Ch; | ||
| 53 | + BasicIStreamWrapper(StreamType& stream) : stream_(stream), count_(), peekBuffer_() {} | ||
| 54 | + | ||
| 55 | + Ch Peek() const { | ||
| 56 | + typename StreamType::int_type c = stream_.peek(); | ||
| 57 | + return RAPIDJSON_LIKELY(c != StreamType::traits_type::eof()) ? static_cast<Ch>(c) : static_cast<Ch>('\0'); | ||
| 58 | + } | ||
| 59 | + | ||
| 60 | + Ch Take() { | ||
| 61 | + typename StreamType::int_type c = stream_.get(); | ||
| 62 | + if (RAPIDJSON_LIKELY(c != StreamType::traits_type::eof())) { | ||
| 63 | + count_++; | ||
| 64 | + return static_cast<Ch>(c); | ||
| 65 | + } | ||
| 66 | + else | ||
| 67 | + return '\0'; | ||
| 68 | + } | ||
| 69 | + | ||
| 70 | + // tellg() may return -1 when failed. So we count by ourself. | ||
| 71 | + size_t Tell() const { return count_; } | ||
| 72 | + | ||
| 73 | + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } | ||
| 74 | + void Put(Ch) { RAPIDJSON_ASSERT(false); } | ||
| 75 | + void Flush() { RAPIDJSON_ASSERT(false); } | ||
| 76 | + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } | ||
| 77 | + | ||
| 78 | + // For encoding detection only. | ||
| 79 | + const Ch* Peek4() const { | ||
| 80 | + RAPIDJSON_ASSERT(sizeof(Ch) == 1); // Only usable for byte stream. | ||
| 81 | + int i; | ||
| 82 | + bool hasError = false; | ||
| 83 | + for (i = 0; i < 4; ++i) { | ||
| 84 | + typename StreamType::int_type c = stream_.get(); | ||
| 85 | + if (c == StreamType::traits_type::eof()) { | ||
| 86 | + hasError = true; | ||
| 87 | + stream_.clear(); | ||
| 88 | + break; | ||
| 89 | + } | ||
| 90 | + peekBuffer_[i] = static_cast<Ch>(c); | ||
| 91 | + } | ||
| 92 | + for (--i; i >= 0; --i) | ||
| 93 | + stream_.putback(peekBuffer_[i]); | ||
| 94 | + return !hasError ? peekBuffer_ : 0; | ||
| 95 | + } | ||
| 96 | + | ||
| 97 | +private: | ||
| 98 | + BasicIStreamWrapper(const BasicIStreamWrapper&); | ||
| 99 | + BasicIStreamWrapper& operator=(const BasicIStreamWrapper&); | ||
| 100 | + | ||
| 101 | + StreamType& stream_; | ||
| 102 | + size_t count_; //!< Number of characters read. Note: | ||
| 103 | + mutable Ch peekBuffer_[4]; | ||
| 104 | +}; | ||
| 105 | + | ||
| 106 | +typedef BasicIStreamWrapper<std::istream> IStreamWrapper; | ||
| 107 | +typedef BasicIStreamWrapper<std::wistream> WIStreamWrapper; | ||
| 108 | + | ||
| 109 | +#if defined(__clang__) || defined(_MSC_VER) | ||
| 110 | +RAPIDJSON_DIAG_POP | ||
| 111 | +#endif | ||
| 112 | + | ||
| 113 | +RAPIDJSON_NAMESPACE_END | ||
| 114 | + | ||
| 115 | +#endif // RAPIDJSON_ISTREAMWRAPPER_H_ |
PaperGraph/include/rapidjson/memorybuffer.h
0 → 100644
| 1 | +// Tencent is pleased to support the open source community by making RapidJSON available. | ||
| 2 | +// | ||
| 3 | +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. | ||
| 4 | +// | ||
| 5 | +// Licensed under the MIT License (the "License"); you may not use this file except | ||
| 6 | +// in compliance with the License. You may obtain a copy of the License at | ||
| 7 | +// | ||
| 8 | +// http://opensource.org/licenses/MIT | ||
| 9 | +// | ||
| 10 | +// Unless required by applicable law or agreed to in writing, software distributed | ||
| 11 | +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR | ||
| 12 | +// CONDITIONS OF ANY KIND, either express or implied. See the License for the | ||
| 13 | +// specific language governing permissions and limitations under the License. | ||
| 14 | + | ||
| 15 | +#ifndef RAPIDJSON_MEMORYBUFFER_H_ | ||
| 16 | +#define RAPIDJSON_MEMORYBUFFER_H_ | ||
| 17 | + | ||
| 18 | +#include "stream.h" | ||
| 19 | +#include "internal/stack.h" | ||
| 20 | + | ||
| 21 | +RAPIDJSON_NAMESPACE_BEGIN | ||
| 22 | + | ||
| 23 | +//! Represents an in-memory output byte stream. | ||
| 24 | +/*! | ||
| 25 | + This class is mainly for being wrapped by EncodedOutputStream or AutoUTFOutputStream. | ||
| 26 | + | ||
| 27 | + It is similar to FileWriteBuffer but the destination is an in-memory buffer instead of a file. | ||
| 28 | + | ||
| 29 | + Differences between MemoryBuffer and StringBuffer: | ||
| 30 | + 1. StringBuffer has Encoding but MemoryBuffer is only a byte buffer. | ||
| 31 | + 2. StringBuffer::GetString() returns a null-terminated string. MemoryBuffer::GetBuffer() returns a buffer without terminator. | ||
| 32 | + | ||
| 33 | + \tparam Allocator type for allocating memory buffer. | ||
| 34 | + \note implements Stream concept | ||
| 35 | +*/ | ||
| 36 | +template <typename Allocator = CrtAllocator> | ||
| 37 | +struct GenericMemoryBuffer { | ||
| 38 | + typedef char Ch; // byte | ||
| 39 | + | ||
| 40 | + GenericMemoryBuffer(Allocator* allocator = 0, size_t capacity = kDefaultCapacity) : stack_(allocator, capacity) {} | ||
| 41 | + | ||
| 42 | + void Put(Ch c) { *stack_.template Push<Ch>() = c; } | ||
| 43 | + void Flush() {} | ||
| 44 | + | ||
| 45 | + void Clear() { stack_.Clear(); } | ||
| 46 | + void ShrinkToFit() { stack_.ShrinkToFit(); } | ||
| 47 | + Ch* Push(size_t count) { return stack_.template Push<Ch>(count); } | ||
| 48 | + void Pop(size_t count) { stack_.template Pop<Ch>(count); } | ||
| 49 | + | ||
| 50 | + const Ch* GetBuffer() const { | ||
| 51 | + return stack_.template Bottom<Ch>(); | ||
| 52 | + } | ||
| 53 | + | ||
| 54 | + size_t GetSize() const { return stack_.GetSize(); } | ||
| 55 | + | ||
| 56 | + static const size_t kDefaultCapacity = 256; | ||
| 57 | + mutable internal::Stack<Allocator> stack_; | ||
| 58 | +}; | ||
| 59 | + | ||
| 60 | +typedef GenericMemoryBuffer<> MemoryBuffer; | ||
| 61 | + | ||
| 62 | +//! Implement specialized version of PutN() with memset() for better performance. | ||
| 63 | +template<> | ||
| 64 | +inline void PutN(MemoryBuffer& memoryBuffer, char c, size_t n) { | ||
| 65 | + std::memset(memoryBuffer.stack_.Push<char>(n), c, n * sizeof(c)); | ||
| 66 | +} | ||
| 67 | + | ||
| 68 | +RAPIDJSON_NAMESPACE_END | ||
| 69 | + | ||
| 70 | +#endif // RAPIDJSON_MEMORYBUFFER_H_ |
PaperGraph/include/rapidjson/memorystream.h
0 → 100644
| 1 | +// Tencent is pleased to support the open source community by making RapidJSON available. | ||
| 2 | +// | ||
| 3 | +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. | ||
| 4 | +// | ||
| 5 | +// Licensed under the MIT License (the "License"); you may not use this file except | ||
| 6 | +// in compliance with the License. You may obtain a copy of the License at | ||
| 7 | +// | ||
| 8 | +// http://opensource.org/licenses/MIT | ||
| 9 | +// | ||
| 10 | +// Unless required by applicable law or agreed to in writing, software distributed | ||
| 11 | +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR | ||
| 12 | +// CONDITIONS OF ANY KIND, either express or implied. See the License for the | ||
| 13 | +// specific language governing permissions and limitations under the License. | ||
| 14 | + | ||
| 15 | +#ifndef RAPIDJSON_MEMORYSTREAM_H_ | ||
| 16 | +#define RAPIDJSON_MEMORYSTREAM_H_ | ||
| 17 | + | ||
| 18 | +#include "stream.h" | ||
| 19 | + | ||
| 20 | +#ifdef __clang__ | ||
| 21 | +RAPIDJSON_DIAG_PUSH | ||
| 22 | +RAPIDJSON_DIAG_OFF(unreachable-code) | ||
| 23 | +RAPIDJSON_DIAG_OFF(missing-noreturn) | ||
| 24 | +#endif | ||
| 25 | + | ||
| 26 | +RAPIDJSON_NAMESPACE_BEGIN | ||
| 27 | + | ||
| 28 | +//! Represents an in-memory input byte stream. | ||
| 29 | +/*! | ||
| 30 | + This class is mainly for being wrapped by EncodedInputStream or AutoUTFInputStream. | ||
| 31 | + | ||
| 32 | + It is similar to FileReadBuffer but the source is an in-memory buffer instead of a file. | ||
| 33 | + | ||
| 34 | + Differences between MemoryStream and StringStream: | ||
| 35 | + 1. StringStream has encoding but MemoryStream is a byte stream. | ||
| 36 | + 2. MemoryStream needs size of the source buffer and the buffer don't need to be null terminated. StringStream assume null-terminated string as source. | ||
| 37 | + 3. MemoryStream supports Peek4() for encoding detection. StringStream is specified with an encoding so it should not have Peek4(). | ||
| 38 | + \note implements Stream concept | ||
| 39 | +*/ | ||
| 40 | +struct MemoryStream { | ||
| 41 | + typedef char Ch; // byte | ||
| 42 | + | ||
| 43 | + MemoryStream(const Ch *src, size_t size) : src_(src), begin_(src), end_(src + size), size_(size) {} | ||
| 44 | + | ||
| 45 | + Ch Peek() const { return RAPIDJSON_UNLIKELY(src_ == end_) ? '\0' : *src_; } | ||
| 46 | + Ch Take() { return RAPIDJSON_UNLIKELY(src_ == end_) ? '\0' : *src_++; } | ||
| 47 | + size_t Tell() const { return static_cast<size_t>(src_ - begin_); } | ||
| 48 | + | ||
| 49 | + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } | ||
| 50 | + void Put(Ch) { RAPIDJSON_ASSERT(false); } | ||
| 51 | + void Flush() { RAPIDJSON_ASSERT(false); } | ||
| 52 | + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } | ||
| 53 | + | ||
| 54 | + // For encoding detection only. | ||
| 55 | + const Ch* Peek4() const { | ||
| 56 | + return Tell() + 4 <= size_ ? src_ : 0; | ||
| 57 | + } | ||
| 58 | + | ||
| 59 | + const Ch* src_; //!< Current read position. | ||
| 60 | + const Ch* begin_; //!< Original head of the string. | ||
| 61 | + const Ch* end_; //!< End of stream. | ||
| 62 | + size_t size_; //!< Size of the stream. | ||
| 63 | +}; | ||
| 64 | + | ||
| 65 | +RAPIDJSON_NAMESPACE_END | ||
| 66 | + | ||
| 67 | +#ifdef __clang__ | ||
| 68 | +RAPIDJSON_DIAG_POP | ||
| 69 | +#endif | ||
| 70 | + | ||
| 71 | +#endif // RAPIDJSON_MEMORYBUFFER_H_ |
| 1 | +// ISO C9x compliant inttypes.h for Microsoft Visual Studio | ||
| 2 | +// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 | ||
| 3 | +// | ||
| 4 | +// Copyright (c) 2006-2013 Alexander Chemeris | ||
| 5 | +// | ||
| 6 | +// Redistribution and use in source and binary forms, with or without | ||
| 7 | +// modification, are permitted provided that the following conditions are met: | ||
| 8 | +// | ||
| 9 | +// 1. Redistributions of source code must retain the above copyright notice, | ||
| 10 | +// this list of conditions and the following disclaimer. | ||
| 11 | +// | ||
| 12 | +// 2. Redistributions in binary form must reproduce the above copyright | ||
| 13 | +// notice, this list of conditions and the following disclaimer in the | ||
| 14 | +// documentation and/or other materials provided with the distribution. | ||
| 15 | +// | ||
| 16 | +// 3. Neither the name of the product nor the names of its contributors may | ||
| 17 | +// be used to endorse or promote products derived from this software | ||
| 18 | +// without specific prior written permission. | ||
| 19 | +// | ||
| 20 | +// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED | ||
| 21 | +// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
| 22 | +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO | ||
| 23 | +// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| 24 | +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| 25 | +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; | ||
| 26 | +// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | ||
| 27 | +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR | ||
| 28 | +// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF | ||
| 29 | +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 30 | +// | ||
| 31 | +/////////////////////////////////////////////////////////////////////////////// | ||
| 32 | + | ||
| 33 | +// The above software in this distribution may have been modified by | ||
| 34 | +// THL A29 Limited ("Tencent Modifications"). | ||
| 35 | +// All Tencent Modifications are Copyright (C) 2015 THL A29 Limited. | ||
| 36 | + | ||
| 37 | +#ifndef _MSC_VER // [ | ||
| 38 | +#error "Use this header only with Microsoft Visual C++ compilers!" | ||
| 39 | +#endif // _MSC_VER ] | ||
| 40 | + | ||
| 41 | +#ifndef _MSC_INTTYPES_H_ // [ | ||
| 42 | +#define _MSC_INTTYPES_H_ | ||
| 43 | + | ||
| 44 | +#if _MSC_VER > 1000 | ||
| 45 | +#pragma once | ||
| 46 | +#endif | ||
| 47 | + | ||
| 48 | +#include "stdint.h" | ||
| 49 | + | ||
| 50 | +// miloyip: VC supports inttypes.h since VC2013 | ||
| 51 | +#if _MSC_VER >= 1800 | ||
| 52 | +#include <inttypes.h> | ||
| 53 | +#else | ||
| 54 | + | ||
| 55 | +// 7.8 Format conversion of integer types | ||
| 56 | + | ||
| 57 | +typedef struct { | ||
| 58 | + intmax_t quot; | ||
| 59 | + intmax_t rem; | ||
| 60 | +} imaxdiv_t; | ||
| 61 | + | ||
| 62 | +// 7.8.1 Macros for format specifiers | ||
| 63 | + | ||
| 64 | +#if !defined(__cplusplus) || defined(__STDC_FORMAT_MACROS) // [ See footnote 185 at page 198 | ||
| 65 | + | ||
| 66 | +// The fprintf macros for signed integers are: | ||
| 67 | +#define PRId8 "d" | ||
| 68 | +#define PRIi8 "i" | ||
| 69 | +#define PRIdLEAST8 "d" | ||
| 70 | +#define PRIiLEAST8 "i" | ||
| 71 | +#define PRIdFAST8 "d" | ||
| 72 | +#define PRIiFAST8 "i" | ||
| 73 | + | ||
| 74 | +#define PRId16 "hd" | ||
| 75 | +#define PRIi16 "hi" | ||
| 76 | +#define PRIdLEAST16 "hd" | ||
| 77 | +#define PRIiLEAST16 "hi" | ||
| 78 | +#define PRIdFAST16 "hd" | ||
| 79 | +#define PRIiFAST16 "hi" | ||
| 80 | + | ||
| 81 | +#define PRId32 "I32d" | ||
| 82 | +#define PRIi32 "I32i" | ||
| 83 | +#define PRIdLEAST32 "I32d" | ||
| 84 | +#define PRIiLEAST32 "I32i" | ||
| 85 | +#define PRIdFAST32 "I32d" | ||
| 86 | +#define PRIiFAST32 "I32i" | ||
| 87 | + | ||
| 88 | +#define PRId64 "I64d" | ||
| 89 | +#define PRIi64 "I64i" | ||
| 90 | +#define PRIdLEAST64 "I64d" | ||
| 91 | +#define PRIiLEAST64 "I64i" | ||
| 92 | +#define PRIdFAST64 "I64d" | ||
| 93 | +#define PRIiFAST64 "I64i" | ||
| 94 | + | ||
| 95 | +#define PRIdMAX "I64d" | ||
| 96 | +#define PRIiMAX "I64i" | ||
| 97 | + | ||
| 98 | +#define PRIdPTR "Id" | ||
| 99 | +#define PRIiPTR "Ii" | ||
| 100 | + | ||
| 101 | +// The fprintf macros for unsigned integers are: | ||
| 102 | +#define PRIo8 "o" | ||
| 103 | +#define PRIu8 "u" | ||
| 104 | +#define PRIx8 "x" | ||
| 105 | +#define PRIX8 "X" | ||
| 106 | +#define PRIoLEAST8 "o" | ||
| 107 | +#define PRIuLEAST8 "u" | ||
| 108 | +#define PRIxLEAST8 "x" | ||
| 109 | +#define PRIXLEAST8 "X" | ||
| 110 | +#define PRIoFAST8 "o" | ||
| 111 | +#define PRIuFAST8 "u" | ||
| 112 | +#define PRIxFAST8 "x" | ||
| 113 | +#define PRIXFAST8 "X" | ||
| 114 | + | ||
| 115 | +#define PRIo16 "ho" | ||
| 116 | +#define PRIu16 "hu" | ||
| 117 | +#define PRIx16 "hx" | ||
| 118 | +#define PRIX16 "hX" | ||
| 119 | +#define PRIoLEAST16 "ho" | ||
| 120 | +#define PRIuLEAST16 "hu" | ||
| 121 | +#define PRIxLEAST16 "hx" | ||
| 122 | +#define PRIXLEAST16 "hX" | ||
| 123 | +#define PRIoFAST16 "ho" | ||
| 124 | +#define PRIuFAST16 "hu" | ||
| 125 | +#define PRIxFAST16 "hx" | ||
| 126 | +#define PRIXFAST16 "hX" | ||
| 127 | + | ||
| 128 | +#define PRIo32 "I32o" | ||
| 129 | +#define PRIu32 "I32u" | ||
| 130 | +#define PRIx32 "I32x" | ||
| 131 | +#define PRIX32 "I32X" | ||
| 132 | +#define PRIoLEAST32 "I32o" | ||
| 133 | +#define PRIuLEAST32 "I32u" | ||
| 134 | +#define PRIxLEAST32 "I32x" | ||
| 135 | +#define PRIXLEAST32 "I32X" | ||
| 136 | +#define PRIoFAST32 "I32o" | ||
| 137 | +#define PRIuFAST32 "I32u" | ||
| 138 | +#define PRIxFAST32 "I32x" | ||
| 139 | +#define PRIXFAST32 "I32X" | ||
| 140 | + | ||
| 141 | +#define PRIo64 "I64o" | ||
| 142 | +#define PRIu64 "I64u" | ||
| 143 | +#define PRIx64 "I64x" | ||
| 144 | +#define PRIX64 "I64X" | ||
| 145 | +#define PRIoLEAST64 "I64o" | ||
| 146 | +#define PRIuLEAST64 "I64u" | ||
| 147 | +#define PRIxLEAST64 "I64x" | ||
| 148 | +#define PRIXLEAST64 "I64X" | ||
| 149 | +#define PRIoFAST64 "I64o" | ||
| 150 | +#define PRIuFAST64 "I64u" | ||
| 151 | +#define PRIxFAST64 "I64x" | ||
| 152 | +#define PRIXFAST64 "I64X" | ||
| 153 | + | ||
| 154 | +#define PRIoMAX "I64o" | ||
| 155 | +#define PRIuMAX "I64u" | ||
| 156 | +#define PRIxMAX "I64x" | ||
| 157 | +#define PRIXMAX "I64X" | ||
| 158 | + | ||
| 159 | +#define PRIoPTR "Io" | ||
| 160 | +#define PRIuPTR "Iu" | ||
| 161 | +#define PRIxPTR "Ix" | ||
| 162 | +#define PRIXPTR "IX" | ||
| 163 | + | ||
| 164 | +// The fscanf macros for signed integers are: | ||
| 165 | +#define SCNd8 "d" | ||
| 166 | +#define SCNi8 "i" | ||
| 167 | +#define SCNdLEAST8 "d" | ||
| 168 | +#define SCNiLEAST8 "i" | ||
| 169 | +#define SCNdFAST8 "d" | ||
| 170 | +#define SCNiFAST8 "i" | ||
| 171 | + | ||
| 172 | +#define SCNd16 "hd" | ||
| 173 | +#define SCNi16 "hi" | ||
| 174 | +#define SCNdLEAST16 "hd" | ||
| 175 | +#define SCNiLEAST16 "hi" | ||
| 176 | +#define SCNdFAST16 "hd" | ||
| 177 | +#define SCNiFAST16 "hi" | ||
| 178 | + | ||
| 179 | +#define SCNd32 "ld" | ||
| 180 | +#define SCNi32 "li" | ||
| 181 | +#define SCNdLEAST32 "ld" | ||
| 182 | +#define SCNiLEAST32 "li" | ||
| 183 | +#define SCNdFAST32 "ld" | ||
| 184 | +#define SCNiFAST32 "li" | ||
| 185 | + | ||
| 186 | +#define SCNd64 "I64d" | ||
| 187 | +#define SCNi64 "I64i" | ||
| 188 | +#define SCNdLEAST64 "I64d" | ||
| 189 | +#define SCNiLEAST64 "I64i" | ||
| 190 | +#define SCNdFAST64 "I64d" | ||
| 191 | +#define SCNiFAST64 "I64i" | ||
| 192 | + | ||
| 193 | +#define SCNdMAX "I64d" | ||
| 194 | +#define SCNiMAX "I64i" | ||
| 195 | + | ||
| 196 | +#ifdef _WIN64 // [ | ||
| 197 | +# define SCNdPTR "I64d" | ||
| 198 | +# define SCNiPTR "I64i" | ||
| 199 | +#else // _WIN64 ][ | ||
| 200 | +# define SCNdPTR "ld" | ||
| 201 | +# define SCNiPTR "li" | ||
| 202 | +#endif // _WIN64 ] | ||
| 203 | + | ||
| 204 | +// The fscanf macros for unsigned integers are: | ||
| 205 | +#define SCNo8 "o" | ||
| 206 | +#define SCNu8 "u" | ||
| 207 | +#define SCNx8 "x" | ||
| 208 | +#define SCNX8 "X" | ||
| 209 | +#define SCNoLEAST8 "o" | ||
| 210 | +#define SCNuLEAST8 "u" | ||
| 211 | +#define SCNxLEAST8 "x" | ||
| 212 | +#define SCNXLEAST8 "X" | ||
| 213 | +#define SCNoFAST8 "o" | ||
| 214 | +#define SCNuFAST8 "u" | ||
| 215 | +#define SCNxFAST8 "x" | ||
| 216 | +#define SCNXFAST8 "X" | ||
| 217 | + | ||
| 218 | +#define SCNo16 "ho" | ||
| 219 | +#define SCNu16 "hu" | ||
| 220 | +#define SCNx16 "hx" | ||
| 221 | +#define SCNX16 "hX" | ||
| 222 | +#define SCNoLEAST16 "ho" | ||
| 223 | +#define SCNuLEAST16 "hu" | ||
| 224 | +#define SCNxLEAST16 "hx" | ||
| 225 | +#define SCNXLEAST16 "hX" | ||
| 226 | +#define SCNoFAST16 "ho" | ||
| 227 | +#define SCNuFAST16 "hu" | ||
| 228 | +#define SCNxFAST16 "hx" | ||
| 229 | +#define SCNXFAST16 "hX" | ||
| 230 | + | ||
| 231 | +#define SCNo32 "lo" | ||
| 232 | +#define SCNu32 "lu" | ||
| 233 | +#define SCNx32 "lx" | ||
| 234 | +#define SCNX32 "lX" | ||
| 235 | +#define SCNoLEAST32 "lo" | ||
| 236 | +#define SCNuLEAST32 "lu" | ||
| 237 | +#define SCNxLEAST32 "lx" | ||
| 238 | +#define SCNXLEAST32 "lX" | ||
| 239 | +#define SCNoFAST32 "lo" | ||
| 240 | +#define SCNuFAST32 "lu" | ||
| 241 | +#define SCNxFAST32 "lx" | ||
| 242 | +#define SCNXFAST32 "lX" | ||
| 243 | + | ||
| 244 | +#define SCNo64 "I64o" | ||
| 245 | +#define SCNu64 "I64u" | ||
| 246 | +#define SCNx64 "I64x" | ||
| 247 | +#define SCNX64 "I64X" | ||
| 248 | +#define SCNoLEAST64 "I64o" | ||
| 249 | +#define SCNuLEAST64 "I64u" | ||
| 250 | +#define SCNxLEAST64 "I64x" | ||
| 251 | +#define SCNXLEAST64 "I64X" | ||
| 252 | +#define SCNoFAST64 "I64o" | ||
| 253 | +#define SCNuFAST64 "I64u" | ||
| 254 | +#define SCNxFAST64 "I64x" | ||
| 255 | +#define SCNXFAST64 "I64X" | ||
| 256 | + | ||
| 257 | +#define SCNoMAX "I64o" | ||
| 258 | +#define SCNuMAX "I64u" | ||
| 259 | +#define SCNxMAX "I64x" | ||
| 260 | +#define SCNXMAX "I64X" | ||
| 261 | + | ||
| 262 | +#ifdef _WIN64 // [ | ||
| 263 | +# define SCNoPTR "I64o" | ||
| 264 | +# define SCNuPTR "I64u" | ||
| 265 | +# define SCNxPTR "I64x" | ||
| 266 | +# define SCNXPTR "I64X" | ||
| 267 | +#else // _WIN64 ][ | ||
| 268 | +# define SCNoPTR "lo" | ||
| 269 | +# define SCNuPTR "lu" | ||
| 270 | +# define SCNxPTR "lx" | ||
| 271 | +# define SCNXPTR "lX" | ||
| 272 | +#endif // _WIN64 ] | ||
| 273 | + | ||
| 274 | +#endif // __STDC_FORMAT_MACROS ] | ||
| 275 | + | ||
| 276 | +// 7.8.2 Functions for greatest-width integer types | ||
| 277 | + | ||
| 278 | +// 7.8.2.1 The imaxabs function | ||
| 279 | +#define imaxabs _abs64 | ||
| 280 | + | ||
| 281 | +// 7.8.2.2 The imaxdiv function | ||
| 282 | + | ||
| 283 | +// This is modified version of div() function from Microsoft's div.c found | ||
| 284 | +// in %MSVC.NET%\crt\src\div.c | ||
| 285 | +#ifdef STATIC_IMAXDIV // [ | ||
| 286 | +static | ||
| 287 | +#else // STATIC_IMAXDIV ][ | ||
| 288 | +_inline | ||
| 289 | +#endif // STATIC_IMAXDIV ] | ||
| 290 | +imaxdiv_t __cdecl imaxdiv(intmax_t numer, intmax_t denom) | ||
| 291 | +{ | ||
| 292 | + imaxdiv_t result; | ||
| 293 | + | ||
| 294 | + result.quot = numer / denom; | ||
| 295 | + result.rem = numer % denom; | ||
| 296 | + | ||
| 297 | + if (numer < 0 && result.rem > 0) { | ||
| 298 | + // did division wrong; must fix up | ||
| 299 | + ++result.quot; | ||
| 300 | + result.rem -= denom; | ||
| 301 | + } | ||
| 302 | + | ||
| 303 | + return result; | ||
| 304 | +} | ||
| 305 | + | ||
| 306 | +// 7.8.2.3 The strtoimax and strtoumax functions | ||
| 307 | +#define strtoimax _strtoi64 | ||
| 308 | +#define strtoumax _strtoui64 | ||
| 309 | + | ||
| 310 | +// 7.8.2.4 The wcstoimax and wcstoumax functions | ||
| 311 | +#define wcstoimax _wcstoi64 | ||
| 312 | +#define wcstoumax _wcstoui64 | ||
| 313 | + | ||
| 314 | +#endif // _MSC_VER >= 1800 | ||
| 315 | + | ||
| 316 | +#endif // _MSC_INTTYPES_H_ ] |
| 1 | +// ISO C9x compliant stdint.h for Microsoft Visual Studio | ||
| 2 | +// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 | ||
| 3 | +// | ||
| 4 | +// Copyright (c) 2006-2013 Alexander Chemeris | ||
| 5 | +// | ||
| 6 | +// Redistribution and use in source and binary forms, with or without | ||
| 7 | +// modification, are permitted provided that the following conditions are met: | ||
| 8 | +// | ||
| 9 | +// 1. Redistributions of source code must retain the above copyright notice, | ||
| 10 | +// this list of conditions and the following disclaimer. | ||
| 11 | +// | ||
| 12 | +// 2. Redistributions in binary form must reproduce the above copyright | ||
| 13 | +// notice, this list of conditions and the following disclaimer in the | ||
| 14 | +// documentation and/or other materials provided with the distribution. | ||
| 15 | +// | ||
| 16 | +// 3. Neither the name of the product nor the names of its contributors may | ||
| 17 | +// be used to endorse or promote products derived from this software | ||
| 18 | +// without specific prior written permission. | ||
| 19 | +// | ||
| 20 | +// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED | ||
| 21 | +// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
| 22 | +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO | ||
| 23 | +// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| 24 | +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| 25 | +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; | ||
| 26 | +// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | ||
| 27 | +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR | ||
| 28 | +// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF | ||
| 29 | +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 30 | +// | ||
| 31 | +/////////////////////////////////////////////////////////////////////////////// | ||
| 32 | + | ||
| 33 | +// The above software in this distribution may have been modified by | ||
| 34 | +// THL A29 Limited ("Tencent Modifications"). | ||
| 35 | +// All Tencent Modifications are Copyright (C) 2015 THL A29 Limited. | ||
| 36 | + | ||
| 37 | +#ifndef _MSC_VER // [ | ||
| 38 | +#error "Use this header only with Microsoft Visual C++ compilers!" | ||
| 39 | +#endif // _MSC_VER ] | ||
| 40 | + | ||
| 41 | +#ifndef _MSC_STDINT_H_ // [ | ||
| 42 | +#define _MSC_STDINT_H_ | ||
| 43 | + | ||
| 44 | +#if _MSC_VER > 1000 | ||
| 45 | +#pragma once | ||
| 46 | +#endif | ||
| 47 | + | ||
| 48 | +// miloyip: Originally Visual Studio 2010 uses its own stdint.h. However it generates warning with INT64_C(), so change to use this file for vs2010. | ||
| 49 | +#if _MSC_VER >= 1600 // [ | ||
| 50 | +#include <stdint.h> | ||
| 51 | + | ||
| 52 | +#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260 | ||
| 53 | + | ||
| 54 | +#undef INT8_C | ||
| 55 | +#undef INT16_C | ||
| 56 | +#undef INT32_C | ||
| 57 | +#undef INT64_C | ||
| 58 | +#undef UINT8_C | ||
| 59 | +#undef UINT16_C | ||
| 60 | +#undef UINT32_C | ||
| 61 | +#undef UINT64_C | ||
| 62 | + | ||
| 63 | +// 7.18.4.1 Macros for minimum-width integer constants | ||
| 64 | + | ||
| 65 | +#define INT8_C(val) val##i8 | ||
| 66 | +#define INT16_C(val) val##i16 | ||
| 67 | +#define INT32_C(val) val##i32 | ||
| 68 | +#define INT64_C(val) val##i64 | ||
| 69 | + | ||
| 70 | +#define UINT8_C(val) val##ui8 | ||
| 71 | +#define UINT16_C(val) val##ui16 | ||
| 72 | +#define UINT32_C(val) val##ui32 | ||
| 73 | +#define UINT64_C(val) val##ui64 | ||
| 74 | + | ||
| 75 | +// 7.18.4.2 Macros for greatest-width integer constants | ||
| 76 | +// These #ifndef's are needed to prevent collisions with <boost/cstdint.hpp>. | ||
| 77 | +// Check out Issue 9 for the details. | ||
| 78 | +#ifndef INTMAX_C // [ | ||
| 79 | +# define INTMAX_C INT64_C | ||
| 80 | +#endif // INTMAX_C ] | ||
| 81 | +#ifndef UINTMAX_C // [ | ||
| 82 | +# define UINTMAX_C UINT64_C | ||
| 83 | +#endif // UINTMAX_C ] | ||
| 84 | + | ||
| 85 | +#endif // __STDC_CONSTANT_MACROS ] | ||
| 86 | + | ||
| 87 | +#else // ] _MSC_VER >= 1700 [ | ||
| 88 | + | ||
| 89 | +#include <limits.h> | ||
| 90 | + | ||
| 91 | +// For Visual Studio 6 in C++ mode and for many Visual Studio versions when | ||
| 92 | +// compiling for ARM we have to wrap <wchar.h> include with 'extern "C++" {}' | ||
| 93 | +// or compiler would give many errors like this: | ||
| 94 | +// error C2733: second C linkage of overloaded function 'wmemchr' not allowed | ||
| 95 | +#if defined(__cplusplus) && !defined(_M_ARM) | ||
| 96 | +extern "C" { | ||
| 97 | +#endif | ||
| 98 | +# include <wchar.h> | ||
| 99 | +#if defined(__cplusplus) && !defined(_M_ARM) | ||
| 100 | +} | ||
| 101 | +#endif | ||
| 102 | + | ||
| 103 | +// Define _W64 macros to mark types changing their size, like intptr_t. | ||
| 104 | +#ifndef _W64 | ||
| 105 | +# if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300 | ||
| 106 | +# define _W64 __w64 | ||
| 107 | +# else | ||
| 108 | +# define _W64 | ||
| 109 | +# endif | ||
| 110 | +#endif | ||
| 111 | + | ||
| 112 | + | ||
| 113 | +// 7.18.1 Integer types | ||
| 114 | + | ||
| 115 | +// 7.18.1.1 Exact-width integer types | ||
| 116 | + | ||
| 117 | +// Visual Studio 6 and Embedded Visual C++ 4 doesn't | ||
| 118 | +// realize that, e.g. char has the same size as __int8 | ||
| 119 | +// so we give up on __intX for them. | ||
| 120 | +#if (_MSC_VER < 1300) | ||
| 121 | + typedef signed char int8_t; | ||
| 122 | + typedef signed short int16_t; | ||
| 123 | + typedef signed int int32_t; | ||
| 124 | + typedef unsigned char uint8_t; | ||
| 125 | + typedef unsigned short uint16_t; | ||
| 126 | + typedef unsigned int uint32_t; | ||
| 127 | +#else | ||
| 128 | + typedef signed __int8 int8_t; | ||
| 129 | + typedef signed __int16 int16_t; | ||
| 130 | + typedef signed __int32 int32_t; | ||
| 131 | + typedef unsigned __int8 uint8_t; | ||
| 132 | + typedef unsigned __int16 uint16_t; | ||
| 133 | + typedef unsigned __int32 uint32_t; | ||
| 134 | +#endif | ||
| 135 | +typedef signed __int64 int64_t; | ||
| 136 | +typedef unsigned __int64 uint64_t; | ||
| 137 | + | ||
| 138 | + | ||
| 139 | +// 7.18.1.2 Minimum-width integer types | ||
| 140 | +typedef int8_t int_least8_t; | ||
| 141 | +typedef int16_t int_least16_t; | ||
| 142 | +typedef int32_t int_least32_t; | ||
| 143 | +typedef int64_t int_least64_t; | ||
| 144 | +typedef uint8_t uint_least8_t; | ||
| 145 | +typedef uint16_t uint_least16_t; | ||
| 146 | +typedef uint32_t uint_least32_t; | ||
| 147 | +typedef uint64_t uint_least64_t; | ||
| 148 | + | ||
| 149 | +// 7.18.1.3 Fastest minimum-width integer types | ||
| 150 | +typedef int8_t int_fast8_t; | ||
| 151 | +typedef int16_t int_fast16_t; | ||
| 152 | +typedef int32_t int_fast32_t; | ||
| 153 | +typedef int64_t int_fast64_t; | ||
| 154 | +typedef uint8_t uint_fast8_t; | ||
| 155 | +typedef uint16_t uint_fast16_t; | ||
| 156 | +typedef uint32_t uint_fast32_t; | ||
| 157 | +typedef uint64_t uint_fast64_t; | ||
| 158 | + | ||
| 159 | +// 7.18.1.4 Integer types capable of holding object pointers | ||
| 160 | +#ifdef _WIN64 // [ | ||
| 161 | + typedef signed __int64 intptr_t; | ||
| 162 | + typedef unsigned __int64 uintptr_t; | ||
| 163 | +#else // _WIN64 ][ | ||
| 164 | + typedef _W64 signed int intptr_t; | ||
| 165 | + typedef _W64 unsigned int uintptr_t; | ||
| 166 | +#endif // _WIN64 ] | ||
| 167 | + | ||
| 168 | +// 7.18.1.5 Greatest-width integer types | ||
| 169 | +typedef int64_t intmax_t; | ||
| 170 | +typedef uint64_t uintmax_t; | ||
| 171 | + | ||
| 172 | + | ||
| 173 | +// 7.18.2 Limits of specified-width integer types | ||
| 174 | + | ||
| 175 | +#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [ See footnote 220 at page 257 and footnote 221 at page 259 | ||
| 176 | + | ||
| 177 | +// 7.18.2.1 Limits of exact-width integer types | ||
| 178 | +#define INT8_MIN ((int8_t)_I8_MIN) | ||
| 179 | +#define INT8_MAX _I8_MAX | ||
| 180 | +#define INT16_MIN ((int16_t)_I16_MIN) | ||
| 181 | +#define INT16_MAX _I16_MAX | ||
| 182 | +#define INT32_MIN ((int32_t)_I32_MIN) | ||
| 183 | +#define INT32_MAX _I32_MAX | ||
| 184 | +#define INT64_MIN ((int64_t)_I64_MIN) | ||
| 185 | +#define INT64_MAX _I64_MAX | ||
| 186 | +#define UINT8_MAX _UI8_MAX | ||
| 187 | +#define UINT16_MAX _UI16_MAX | ||
| 188 | +#define UINT32_MAX _UI32_MAX | ||
| 189 | +#define UINT64_MAX _UI64_MAX | ||
| 190 | + | ||
| 191 | +// 7.18.2.2 Limits of minimum-width integer types | ||
| 192 | +#define INT_LEAST8_MIN INT8_MIN | ||
| 193 | +#define INT_LEAST8_MAX INT8_MAX | ||
| 194 | +#define INT_LEAST16_MIN INT16_MIN | ||
| 195 | +#define INT_LEAST16_MAX INT16_MAX | ||
| 196 | +#define INT_LEAST32_MIN INT32_MIN | ||
| 197 | +#define INT_LEAST32_MAX INT32_MAX | ||
| 198 | +#define INT_LEAST64_MIN INT64_MIN | ||
| 199 | +#define INT_LEAST64_MAX INT64_MAX | ||
| 200 | +#define UINT_LEAST8_MAX UINT8_MAX | ||
| 201 | +#define UINT_LEAST16_MAX UINT16_MAX | ||
| 202 | +#define UINT_LEAST32_MAX UINT32_MAX | ||
| 203 | +#define UINT_LEAST64_MAX UINT64_MAX | ||
| 204 | + | ||
| 205 | +// 7.18.2.3 Limits of fastest minimum-width integer types | ||
| 206 | +#define INT_FAST8_MIN INT8_MIN | ||
| 207 | +#define INT_FAST8_MAX INT8_MAX | ||
| 208 | +#define INT_FAST16_MIN INT16_MIN | ||
| 209 | +#define INT_FAST16_MAX INT16_MAX | ||
| 210 | +#define INT_FAST32_MIN INT32_MIN | ||
| 211 | +#define INT_FAST32_MAX INT32_MAX | ||
| 212 | +#define INT_FAST64_MIN INT64_MIN | ||
| 213 | +#define INT_FAST64_MAX INT64_MAX | ||
| 214 | +#define UINT_FAST8_MAX UINT8_MAX | ||
| 215 | +#define UINT_FAST16_MAX UINT16_MAX | ||
| 216 | +#define UINT_FAST32_MAX UINT32_MAX | ||
| 217 | +#define UINT_FAST64_MAX UINT64_MAX | ||
| 218 | + | ||
| 219 | +// 7.18.2.4 Limits of integer types capable of holding object pointers | ||
| 220 | +#ifdef _WIN64 // [ | ||
| 221 | +# define INTPTR_MIN INT64_MIN | ||
| 222 | +# define INTPTR_MAX INT64_MAX | ||
| 223 | +# define UINTPTR_MAX UINT64_MAX | ||
| 224 | +#else // _WIN64 ][ | ||
| 225 | +# define INTPTR_MIN INT32_MIN | ||
| 226 | +# define INTPTR_MAX INT32_MAX | ||
| 227 | +# define UINTPTR_MAX UINT32_MAX | ||
| 228 | +#endif // _WIN64 ] | ||
| 229 | + | ||
| 230 | +// 7.18.2.5 Limits of greatest-width integer types | ||
| 231 | +#define INTMAX_MIN INT64_MIN | ||
| 232 | +#define INTMAX_MAX INT64_MAX | ||
| 233 | +#define UINTMAX_MAX UINT64_MAX | ||
| 234 | + | ||
| 235 | +// 7.18.3 Limits of other integer types | ||
| 236 | + | ||
| 237 | +#ifdef _WIN64 // [ | ||
| 238 | +# define PTRDIFF_MIN _I64_MIN | ||
| 239 | +# define PTRDIFF_MAX _I64_MAX | ||
| 240 | +#else // _WIN64 ][ | ||
| 241 | +# define PTRDIFF_MIN _I32_MIN | ||
| 242 | +# define PTRDIFF_MAX _I32_MAX | ||
| 243 | +#endif // _WIN64 ] | ||
| 244 | + | ||
| 245 | +#define SIG_ATOMIC_MIN INT_MIN | ||
| 246 | +#define SIG_ATOMIC_MAX INT_MAX | ||
| 247 | + | ||
| 248 | +#ifndef SIZE_MAX // [ | ||
| 249 | +# ifdef _WIN64 // [ | ||
| 250 | +# define SIZE_MAX _UI64_MAX | ||
| 251 | +# else // _WIN64 ][ | ||
| 252 | +# define SIZE_MAX _UI32_MAX | ||
| 253 | +# endif // _WIN64 ] | ||
| 254 | +#endif // SIZE_MAX ] | ||
| 255 | + | ||
| 256 | +// WCHAR_MIN and WCHAR_MAX are also defined in <wchar.h> | ||
| 257 | +#ifndef WCHAR_MIN // [ | ||
| 258 | +# define WCHAR_MIN 0 | ||
| 259 | +#endif // WCHAR_MIN ] | ||
| 260 | +#ifndef WCHAR_MAX // [ | ||
| 261 | +# define WCHAR_MAX _UI16_MAX | ||
| 262 | +#endif // WCHAR_MAX ] | ||
| 263 | + | ||
| 264 | +#define WINT_MIN 0 | ||
| 265 | +#define WINT_MAX _UI16_MAX | ||
| 266 | + | ||
| 267 | +#endif // __STDC_LIMIT_MACROS ] | ||
| 268 | + | ||
| 269 | + | ||
| 270 | +// 7.18.4 Limits of other integer types | ||
| 271 | + | ||
| 272 | +#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260 | ||
| 273 | + | ||
| 274 | +// 7.18.4.1 Macros for minimum-width integer constants | ||
| 275 | + | ||
| 276 | +#define INT8_C(val) val##i8 | ||
| 277 | +#define INT16_C(val) val##i16 | ||
| 278 | +#define INT32_C(val) val##i32 | ||
| 279 | +#define INT64_C(val) val##i64 | ||
| 280 | + | ||
| 281 | +#define UINT8_C(val) val##ui8 | ||
| 282 | +#define UINT16_C(val) val##ui16 | ||
| 283 | +#define UINT32_C(val) val##ui32 | ||
| 284 | +#define UINT64_C(val) val##ui64 | ||
| 285 | + | ||
| 286 | +// 7.18.4.2 Macros for greatest-width integer constants | ||
| 287 | +// These #ifndef's are needed to prevent collisions with <boost/cstdint.hpp>. | ||
| 288 | +// Check out Issue 9 for the details. | ||
| 289 | +#ifndef INTMAX_C // [ | ||
| 290 | +# define INTMAX_C INT64_C | ||
| 291 | +#endif // INTMAX_C ] | ||
| 292 | +#ifndef UINTMAX_C // [ | ||
| 293 | +# define UINTMAX_C UINT64_C | ||
| 294 | +#endif // UINTMAX_C ] | ||
| 295 | + | ||
| 296 | +#endif // __STDC_CONSTANT_MACROS ] | ||
| 297 | + | ||
| 298 | +#endif // _MSC_VER >= 1600 ] | ||
| 299 | + | ||
| 300 | +#endif // _MSC_STDINT_H_ ] |
| 1 | +// Tencent is pleased to support the open source community by making RapidJSON available. | ||
| 2 | +// | ||
| 3 | +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. | ||
| 4 | +// | ||
| 5 | +// Licensed under the MIT License (the "License"); you may not use this file except | ||
| 6 | +// in compliance with the License. You may obtain a copy of the License at | ||
| 7 | +// | ||
| 8 | +// http://opensource.org/licenses/MIT | ||
| 9 | +// | ||
| 10 | +// Unless required by applicable law or agreed to in writing, software distributed | ||
| 11 | +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR | ||
| 12 | +// CONDITIONS OF ANY KIND, either express or implied. See the License for the | ||
| 13 | +// specific language governing permissions and limitations under the License. | ||
| 14 | + | ||
| 15 | +#ifndef RAPIDJSON_OSTREAMWRAPPER_H_ | ||
| 16 | +#define RAPIDJSON_OSTREAMWRAPPER_H_ | ||
| 17 | + | ||
| 18 | +#include "stream.h" | ||
| 19 | +#include <iosfwd> | ||
| 20 | + | ||
| 21 | +#ifdef __clang__ | ||
| 22 | +RAPIDJSON_DIAG_PUSH | ||
| 23 | +RAPIDJSON_DIAG_OFF(padded) | ||
| 24 | +#endif | ||
| 25 | + | ||
| 26 | +RAPIDJSON_NAMESPACE_BEGIN | ||
| 27 | + | ||
| 28 | +//! Wrapper of \c std::basic_ostream into RapidJSON's Stream concept. | ||
| 29 | +/*! | ||
| 30 | + The classes can be wrapped including but not limited to: | ||
| 31 | + | ||
| 32 | + - \c std::ostringstream | ||
| 33 | + - \c std::stringstream | ||
| 34 | + - \c std::wpstringstream | ||
| 35 | + - \c std::wstringstream | ||
| 36 | + - \c std::ifstream | ||
| 37 | + - \c std::fstream | ||
| 38 | + - \c std::wofstream | ||
| 39 | + - \c std::wfstream | ||
| 40 | + | ||
| 41 | + \tparam StreamType Class derived from \c std::basic_ostream. | ||
| 42 | +*/ | ||
| 43 | + | ||
| 44 | +template <typename StreamType> | ||
| 45 | +class BasicOStreamWrapper { | ||
| 46 | +public: | ||
| 47 | + typedef typename StreamType::char_type Ch; | ||
| 48 | + BasicOStreamWrapper(StreamType& stream) : stream_(stream) {} | ||
| 49 | + | ||
| 50 | + void Put(Ch c) { | ||
| 51 | + stream_.put(c); | ||
| 52 | + } | ||
| 53 | + | ||
| 54 | + void Flush() { | ||
| 55 | + stream_.flush(); | ||
| 56 | + } | ||
| 57 | + | ||
| 58 | + // Not implemented | ||
| 59 | + char Peek() const { RAPIDJSON_ASSERT(false); return 0; } | ||
| 60 | + char Take() { RAPIDJSON_ASSERT(false); return 0; } | ||
| 61 | + size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; } | ||
| 62 | + char* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } | ||
| 63 | + size_t PutEnd(char*) { RAPIDJSON_ASSERT(false); return 0; } | ||
| 64 | + | ||
| 65 | +private: | ||
| 66 | + BasicOStreamWrapper(const BasicOStreamWrapper&); | ||
| 67 | + BasicOStreamWrapper& operator=(const BasicOStreamWrapper&); | ||
| 68 | + | ||
| 69 | + StreamType& stream_; | ||
| 70 | +}; | ||
| 71 | + | ||
| 72 | +typedef BasicOStreamWrapper<std::ostream> OStreamWrapper; | ||
| 73 | +typedef BasicOStreamWrapper<std::wostream> WOStreamWrapper; | ||
| 74 | + | ||
| 75 | +#ifdef __clang__ | ||
| 76 | +RAPIDJSON_DIAG_POP | ||
| 77 | +#endif | ||
| 78 | + | ||
| 79 | +RAPIDJSON_NAMESPACE_END | ||
| 80 | + | ||
| 81 | +#endif // RAPIDJSON_OSTREAMWRAPPER_H_ |
PaperGraph/include/rapidjson/pointer.h
0 → 100644
| 1 | +// Tencent is pleased to support the open source community by making RapidJSON available. | ||
| 2 | +// | ||
| 3 | +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. | ||
| 4 | +// | ||
| 5 | +// Licensed under the MIT License (the "License"); you may not use this file except | ||
| 6 | +// in compliance with the License. You may obtain a copy of the License at | ||
| 7 | +// | ||
| 8 | +// http://opensource.org/licenses/MIT | ||
| 9 | +// | ||
| 10 | +// Unless required by applicable law or agreed to in writing, software distributed | ||
| 11 | +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR | ||
| 12 | +// CONDITIONS OF ANY KIND, either express or implied. See the License for the | ||
| 13 | +// specific language governing permissions and limitations under the License. | ||
| 14 | + | ||
| 15 | +#ifndef RAPIDJSON_POINTER_H_ | ||
| 16 | +#define RAPIDJSON_POINTER_H_ | ||
| 17 | + | ||
| 18 | +#include "document.h" | ||
| 19 | +#include "internal/itoa.h" | ||
| 20 | + | ||
| 21 | +#ifdef __clang__ | ||
| 22 | +RAPIDJSON_DIAG_PUSH | ||
| 23 | +RAPIDJSON_DIAG_OFF(switch-enum) | ||
| 24 | +#endif | ||
| 25 | + | ||
| 26 | +#ifdef _MSC_VER | ||
| 27 | +RAPIDJSON_DIAG_PUSH | ||
| 28 | +RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated | ||
| 29 | +#endif | ||
| 30 | + | ||
| 31 | +RAPIDJSON_NAMESPACE_BEGIN | ||
| 32 | + | ||
| 33 | +static const SizeType kPointerInvalidIndex = ~SizeType(0); //!< Represents an invalid index in GenericPointer::Token | ||
| 34 | + | ||
| 35 | +//! Error code of parsing. | ||
| 36 | +/*! \ingroup RAPIDJSON_ERRORS | ||
| 37 | + \see GenericPointer::GenericPointer, GenericPointer::GetParseErrorCode | ||
| 38 | +*/ | ||
| 39 | +enum PointerParseErrorCode { | ||
| 40 | + kPointerParseErrorNone = 0, //!< The parse is successful | ||
| 41 | + | ||
| 42 | + kPointerParseErrorTokenMustBeginWithSolidus, //!< A token must begin with a '/' | ||
| 43 | + kPointerParseErrorInvalidEscape, //!< Invalid escape | ||
| 44 | + kPointerParseErrorInvalidPercentEncoding, //!< Invalid percent encoding in URI fragment | ||
| 45 | + kPointerParseErrorCharacterMustPercentEncode //!< A character must percent encoded in URI fragment | ||
| 46 | +}; | ||
| 47 | + | ||
| 48 | +/////////////////////////////////////////////////////////////////////////////// | ||
| 49 | +// GenericPointer | ||
| 50 | + | ||
| 51 | +//! Represents a JSON Pointer. Use Pointer for UTF8 encoding and default allocator. | ||
| 52 | +/*! | ||
| 53 | + This class implements RFC 6901 "JavaScript Object Notation (JSON) Pointer" | ||
| 54 | + (https://tools.ietf.org/html/rfc6901). | ||
| 55 | + | ||
| 56 | + A JSON pointer is for identifying a specific value in a JSON document | ||
| 57 | + (GenericDocument). It can simplify coding of DOM tree manipulation, because it | ||
| 58 | + can access multiple-level depth of DOM tree with single API call. | ||
| 59 | + | ||
| 60 | + After it parses a string representation (e.g. "/foo/0" or URI fragment | ||
| 61 | + representation (e.g. "#/foo/0") into its internal representation (tokens), | ||
| 62 | + it can be used to resolve a specific value in multiple documents, or sub-tree | ||
| 63 | + of documents. | ||
| 64 | + | ||
| 65 | + Contrary to GenericValue, Pointer can be copy constructed and copy assigned. | ||
| 66 | + Apart from assignment, a Pointer cannot be modified after construction. | ||
| 67 | + | ||
| 68 | + Although Pointer is very convenient, please aware that constructing Pointer | ||
| 69 | + involves parsing and dynamic memory allocation. A special constructor with user- | ||
| 70 | + supplied tokens eliminates these. | ||
| 71 | + | ||
| 72 | + GenericPointer depends on GenericDocument and GenericValue. | ||
| 73 | + | ||
| 74 | + \tparam ValueType The value type of the DOM tree. E.g. GenericValue<UTF8<> > | ||
| 75 | + \tparam Allocator The allocator type for allocating memory for internal representation. | ||
| 76 | + | ||
| 77 | + \note GenericPointer uses same encoding of ValueType. | ||
| 78 | + However, Allocator of GenericPointer is independent of Allocator of Value. | ||
| 79 | +*/ | ||
| 80 | +template <typename ValueType, typename Allocator = CrtAllocator> | ||
| 81 | +class GenericPointer { | ||
| 82 | +public: | ||
| 83 | + typedef typename ValueType::EncodingType EncodingType; //!< Encoding type from Value | ||
| 84 | + typedef typename ValueType::Ch Ch; //!< Character type from Value | ||
| 85 | + | ||
| 86 | + //! A token is the basic units of internal representation. | ||
| 87 | + /*! | ||
| 88 | + A JSON pointer string representation "/foo/123" is parsed to two tokens: | ||
| 89 | + "foo" and 123. 123 will be represented in both numeric form and string form. | ||
| 90 | + They are resolved according to the actual value type (object or array). | ||
| 91 | + | ||
| 92 | + For token that are not numbers, or the numeric value is out of bound | ||
| 93 | + (greater than limits of SizeType), they are only treated as string form | ||
| 94 | + (i.e. the token's index will be equal to kPointerInvalidIndex). | ||
| 95 | + | ||
| 96 | + This struct is public so that user can create a Pointer without parsing and | ||
| 97 | + allocation, using a special constructor. | ||
| 98 | + */ | ||
| 99 | + struct Token { | ||
| 100 | + const Ch* name; //!< Name of the token. It has null character at the end but it can contain null character. | ||
| 101 | + SizeType length; //!< Length of the name. | ||
| 102 | + SizeType index; //!< A valid array index, if it is not equal to kPointerInvalidIndex. | ||
| 103 | + }; | ||
| 104 | + | ||
| 105 | + //!@name Constructors and destructor. | ||
| 106 | + //@{ | ||
| 107 | + | ||
| 108 | + //! Default constructor. | ||
| 109 | + GenericPointer(Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) {} | ||
| 110 | + | ||
| 111 | + //! Constructor that parses a string or URI fragment representation. | ||
| 112 | + /*! | ||
| 113 | + \param source A null-terminated, string or URI fragment representation of JSON pointer. | ||
| 114 | + \param allocator User supplied allocator for this pointer. If no allocator is provided, it creates a self-owned one. | ||
| 115 | + */ | ||
| 116 | + explicit GenericPointer(const Ch* source, Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { | ||
| 117 | + Parse(source, internal::StrLen(source)); | ||
| 118 | + } | ||
| 119 | + | ||
| 120 | +#if RAPIDJSON_HAS_STDSTRING | ||
| 121 | + //! Constructor that parses a string or URI fragment representation. | ||
| 122 | + /*! | ||
| 123 | + \param source A string or URI fragment representation of JSON pointer. | ||
| 124 | + \param allocator User supplied allocator for this pointer. If no allocator is provided, it creates a self-owned one. | ||
| 125 | + \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. | ||
| 126 | + */ | ||
| 127 | + explicit GenericPointer(const std::basic_string<Ch>& source, Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { | ||
| 128 | + Parse(source.c_str(), source.size()); | ||
| 129 | + } | ||
| 130 | +#endif | ||
| 131 | + | ||
| 132 | + //! Constructor that parses a string or URI fragment representation, with length of the source string. | ||
| 133 | + /*! | ||
| 134 | + \param source A string or URI fragment representation of JSON pointer. | ||
| 135 | + \param length Length of source. | ||
| 136 | + \param allocator User supplied allocator for this pointer. If no allocator is provided, it creates a self-owned one. | ||
| 137 | + \note Slightly faster than the overload without length. | ||
| 138 | + */ | ||
| 139 | + GenericPointer(const Ch* source, size_t length, Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { | ||
| 140 | + Parse(source, length); | ||
| 141 | + } | ||
| 142 | + | ||
| 143 | + //! Constructor with user-supplied tokens. | ||
| 144 | + /*! | ||
| 145 | + This constructor let user supplies const array of tokens. | ||
| 146 | + This prevents the parsing process and eliminates allocation. | ||
| 147 | + This is preferred for memory constrained environments. | ||
| 148 | + | ||
| 149 | + \param tokens An constant array of tokens representing the JSON pointer. | ||
| 150 | + \param tokenCount Number of tokens. | ||
| 151 | + | ||
| 152 | + \b Example | ||
| 153 | + \code | ||
| 154 | + #define NAME(s) { s, sizeof(s) / sizeof(s[0]) - 1, kPointerInvalidIndex } | ||
| 155 | + #define INDEX(i) { #i, sizeof(#i) - 1, i } | ||
| 156 | + | ||
| 157 | + static const Pointer::Token kTokens[] = { NAME("foo"), INDEX(123) }; | ||
| 158 | + static const Pointer p(kTokens, sizeof(kTokens) / sizeof(kTokens[0])); | ||
| 159 | + // Equivalent to static const Pointer p("/foo/123"); | ||
| 160 | + | ||
| 161 | + #undef NAME | ||
| 162 | + #undef INDEX | ||
| 163 | + \endcode | ||
| 164 | + */ | ||
| 165 | + GenericPointer(const Token* tokens, size_t tokenCount) : allocator_(), ownAllocator_(), nameBuffer_(), tokens_(const_cast<Token*>(tokens)), tokenCount_(tokenCount), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) {} | ||
| 166 | + | ||
| 167 | + //! Copy constructor. | ||
| 168 | + GenericPointer(const GenericPointer& rhs, Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { | ||
| 169 | + *this = rhs; | ||
| 170 | + } | ||
| 171 | + | ||
| 172 | + //! Destructor. | ||
| 173 | + ~GenericPointer() { | ||
| 174 | + if (nameBuffer_) // If user-supplied tokens constructor is used, nameBuffer_ is nullptr and tokens_ are not deallocated. | ||
| 175 | + Allocator::Free(tokens_); | ||
| 176 | + RAPIDJSON_DELETE(ownAllocator_); | ||
| 177 | + } | ||
| 178 | + | ||
| 179 | + //! Assignment operator. | ||
| 180 | + GenericPointer& operator=(const GenericPointer& rhs) { | ||
| 181 | + if (this != &rhs) { | ||
| 182 | + // Do not delete ownAllcator | ||
| 183 | + if (nameBuffer_) | ||
| 184 | + Allocator::Free(tokens_); | ||
| 185 | + | ||
| 186 | + tokenCount_ = rhs.tokenCount_; | ||
| 187 | + parseErrorOffset_ = rhs.parseErrorOffset_; | ||
| 188 | + parseErrorCode_ = rhs.parseErrorCode_; | ||
| 189 | + | ||
| 190 | + if (rhs.nameBuffer_) | ||
| 191 | + CopyFromRaw(rhs); // Normally parsed tokens. | ||
| 192 | + else { | ||
| 193 | + tokens_ = rhs.tokens_; // User supplied const tokens. | ||
| 194 | + nameBuffer_ = 0; | ||
| 195 | + } | ||
| 196 | + } | ||
| 197 | + return *this; | ||
| 198 | + } | ||
| 199 | + | ||
| 200 | + //@} | ||
| 201 | + | ||
| 202 | + //!@name Append token | ||
| 203 | + //@{ | ||
| 204 | + | ||
| 205 | + //! Append a token and return a new Pointer | ||
| 206 | + /*! | ||
| 207 | + \param token Token to be appended. | ||
| 208 | + \param allocator Allocator for the newly return Pointer. | ||
| 209 | + \return A new Pointer with appended token. | ||
| 210 | + */ | ||
| 211 | + GenericPointer Append(const Token& token, Allocator* allocator = 0) const { | ||
| 212 | + GenericPointer r; | ||
| 213 | + r.allocator_ = allocator; | ||
| 214 | + Ch *p = r.CopyFromRaw(*this, 1, token.length + 1); | ||
| 215 | + std::memcpy(p, token.name, (token.length + 1) * sizeof(Ch)); | ||
| 216 | + r.tokens_[tokenCount_].name = p; | ||
| 217 | + r.tokens_[tokenCount_].length = token.length; | ||
| 218 | + r.tokens_[tokenCount_].index = token.index; | ||
| 219 | + return r; | ||
| 220 | + } | ||
| 221 | + | ||
| 222 | + //! Append a name token with length, and return a new Pointer | ||
| 223 | + /*! | ||
| 224 | + \param name Name to be appended. | ||
| 225 | + \param length Length of name. | ||
| 226 | + \param allocator Allocator for the newly return Pointer. | ||
| 227 | + \return A new Pointer with appended token. | ||
| 228 | + */ | ||
| 229 | + GenericPointer Append(const Ch* name, SizeType length, Allocator* allocator = 0) const { | ||
| 230 | + Token token = { name, length, kPointerInvalidIndex }; | ||
| 231 | + return Append(token, allocator); | ||
| 232 | + } | ||
| 233 | + | ||
| 234 | + //! Append a name token without length, and return a new Pointer | ||
| 235 | + /*! | ||
| 236 | + \param name Name (const Ch*) to be appended. | ||
| 237 | + \param allocator Allocator for the newly return Pointer. | ||
| 238 | + \return A new Pointer with appended token. | ||
| 239 | + */ | ||
| 240 | + template <typename T> | ||
| 241 | + RAPIDJSON_DISABLEIF_RETURN((internal::NotExpr<internal::IsSame<typename internal::RemoveConst<T>::Type, Ch> >), (GenericPointer)) | ||
| 242 | + Append(T* name, Allocator* allocator = 0) const { | ||
| 243 | + return Append(name, internal::StrLen(name), allocator); | ||
| 244 | + } | ||
| 245 | + | ||
| 246 | +#if RAPIDJSON_HAS_STDSTRING | ||
| 247 | + //! Append a name token, and return a new Pointer | ||
| 248 | + /*! | ||
| 249 | + \param name Name to be appended. | ||
| 250 | + \param allocator Allocator for the newly return Pointer. | ||
| 251 | + \return A new Pointer with appended token. | ||
| 252 | + */ | ||
| 253 | + GenericPointer Append(const std::basic_string<Ch>& name, Allocator* allocator = 0) const { | ||
| 254 | + return Append(name.c_str(), static_cast<SizeType>(name.size()), allocator); | ||
| 255 | + } | ||
| 256 | +#endif | ||
| 257 | + | ||
| 258 | + //! Append a index token, and return a new Pointer | ||
| 259 | + /*! | ||
| 260 | + \param index Index to be appended. | ||
| 261 | + \param allocator Allocator for the newly return Pointer. | ||
| 262 | + \return A new Pointer with appended token. | ||
| 263 | + */ | ||
| 264 | + GenericPointer Append(SizeType index, Allocator* allocator = 0) const { | ||
| 265 | + char buffer[21]; | ||
| 266 | + char* end = sizeof(SizeType) == 4 ? internal::u32toa(index, buffer) : internal::u64toa(index, buffer); | ||
| 267 | + SizeType length = static_cast<SizeType>(end - buffer); | ||
| 268 | + buffer[length] = '\0'; | ||
| 269 | + | ||
| 270 | + if (sizeof(Ch) == 1) { | ||
| 271 | + Token token = { reinterpret_cast<Ch*>(buffer), length, index }; | ||
| 272 | + return Append(token, allocator); | ||
| 273 | + } | ||
| 274 | + else { | ||
| 275 | + Ch name[21]; | ||
| 276 | + for (size_t i = 0; i <= length; i++) | ||
| 277 | + name[i] = static_cast<Ch>(buffer[i]); | ||
| 278 | + Token token = { name, length, index }; | ||
| 279 | + return Append(token, allocator); | ||
| 280 | + } | ||
| 281 | + } | ||
| 282 | + | ||
| 283 | + //! Append a token by value, and return a new Pointer | ||
| 284 | + /*! | ||
| 285 | + \param token token to be appended. | ||
| 286 | + \param allocator Allocator for the newly return Pointer. | ||
| 287 | + \return A new Pointer with appended token. | ||
| 288 | + */ | ||
| 289 | + GenericPointer Append(const ValueType& token, Allocator* allocator = 0) const { | ||
| 290 | + if (token.IsString()) | ||
| 291 | + return Append(token.GetString(), token.GetStringLength(), allocator); | ||
| 292 | + else { | ||
| 293 | + RAPIDJSON_ASSERT(token.IsUint64()); | ||
| 294 | + RAPIDJSON_ASSERT(token.GetUint64() <= SizeType(~0)); | ||
| 295 | + return Append(static_cast<SizeType>(token.GetUint64()), allocator); | ||
| 296 | + } | ||
| 297 | + } | ||
| 298 | + | ||
| 299 | + //!@name Handling Parse Error | ||
| 300 | + //@{ | ||
| 301 | + | ||
| 302 | + //! Check whether this is a valid pointer. | ||
| 303 | + bool IsValid() const { return parseErrorCode_ == kPointerParseErrorNone; } | ||
| 304 | + | ||
| 305 | + //! Get the parsing error offset in code unit. | ||
| 306 | + size_t GetParseErrorOffset() const { return parseErrorOffset_; } | ||
| 307 | + | ||
| 308 | + //! Get the parsing error code. | ||
| 309 | + PointerParseErrorCode GetParseErrorCode() const { return parseErrorCode_; } | ||
| 310 | + | ||
| 311 | + //@} | ||
| 312 | + | ||
| 313 | + //! Get the allocator of this pointer. | ||
| 314 | + Allocator& GetAllocator() { return *allocator_; } | ||
| 315 | + | ||
| 316 | + //!@name Tokens | ||
| 317 | + //@{ | ||
| 318 | + | ||
| 319 | + //! Get the token array (const version only). | ||
| 320 | + const Token* GetTokens() const { return tokens_; } | ||
| 321 | + | ||
| 322 | + //! Get the number of tokens. | ||
| 323 | + size_t GetTokenCount() const { return tokenCount_; } | ||
| 324 | + | ||
| 325 | + //@} | ||
| 326 | + | ||
| 327 | + //!@name Equality/inequality operators | ||
| 328 | + //@{ | ||
| 329 | + | ||
| 330 | + //! Equality operator. | ||
| 331 | + /*! | ||
| 332 | + \note When any pointers are invalid, always returns false. | ||
| 333 | + */ | ||
| 334 | + bool operator==(const GenericPointer& rhs) const { | ||
| 335 | + if (!IsValid() || !rhs.IsValid() || tokenCount_ != rhs.tokenCount_) | ||
| 336 | + return false; | ||
| 337 | + | ||
| 338 | + for (size_t i = 0; i < tokenCount_; i++) { | ||
| 339 | + if (tokens_[i].index != rhs.tokens_[i].index || | ||
| 340 | + tokens_[i].length != rhs.tokens_[i].length || | ||
| 341 | + (tokens_[i].length != 0 && std::memcmp(tokens_[i].name, rhs.tokens_[i].name, sizeof(Ch)* tokens_[i].length) != 0)) | ||
| 342 | + { | ||
| 343 | + return false; | ||
| 344 | + } | ||
| 345 | + } | ||
| 346 | + | ||
| 347 | + return true; | ||
| 348 | + } | ||
| 349 | + | ||
| 350 | + //! Inequality operator. | ||
| 351 | + /*! | ||
| 352 | + \note When any pointers are invalid, always returns true. | ||
| 353 | + */ | ||
| 354 | + bool operator!=(const GenericPointer& rhs) const { return !(*this == rhs); } | ||
| 355 | + | ||
| 356 | + //@} | ||
| 357 | + | ||
| 358 | + //!@name Stringify | ||
| 359 | + //@{ | ||
| 360 | + | ||
| 361 | + //! Stringify the pointer into string representation. | ||
| 362 | + /*! | ||
| 363 | + \tparam OutputStream Type of output stream. | ||
| 364 | + \param os The output stream. | ||
| 365 | + */ | ||
| 366 | + template<typename OutputStream> | ||
| 367 | + bool Stringify(OutputStream& os) const { | ||
| 368 | + return Stringify<false, OutputStream>(os); | ||
| 369 | + } | ||
| 370 | + | ||
| 371 | + //! Stringify the pointer into URI fragment representation. | ||
| 372 | + /*! | ||
| 373 | + \tparam OutputStream Type of output stream. | ||
| 374 | + \param os The output stream. | ||
| 375 | + */ | ||
| 376 | + template<typename OutputStream> | ||
| 377 | + bool StringifyUriFragment(OutputStream& os) const { | ||
| 378 | + return Stringify<true, OutputStream>(os); | ||
| 379 | + } | ||
| 380 | + | ||
| 381 | + //@} | ||
| 382 | + | ||
| 383 | + //!@name Create value | ||
| 384 | + //@{ | ||
| 385 | + | ||
| 386 | + //! Create a value in a subtree. | ||
| 387 | + /*! | ||
| 388 | + If the value is not exist, it creates all parent values and a JSON Null value. | ||
| 389 | + So it always succeed and return the newly created or existing value. | ||
| 390 | + | ||
| 391 | + Remind that it may change types of parents according to tokens, so it | ||
| 392 | + potentially removes previously stored values. For example, if a document | ||
| 393 | + was an array, and "/foo" is used to create a value, then the document | ||
| 394 | + will be changed to an object, and all existing array elements are lost. | ||
| 395 | + | ||
| 396 | + \param root Root value of a DOM subtree to be resolved. It can be any value other than document root. | ||
| 397 | + \param allocator Allocator for creating the values if the specified value or its parents are not exist. | ||
| 398 | + \param alreadyExist If non-null, it stores whether the resolved value is already exist. | ||
| 399 | + \return The resolved newly created (a JSON Null value), or already exists value. | ||
| 400 | + */ | ||
| 401 | + ValueType& Create(ValueType& root, typename ValueType::AllocatorType& allocator, bool* alreadyExist = 0) const { | ||
| 402 | + RAPIDJSON_ASSERT(IsValid()); | ||
| 403 | + ValueType* v = &root; | ||
| 404 | + bool exist = true; | ||
| 405 | + for (const Token *t = tokens_; t != tokens_ + tokenCount_; ++t) { | ||
| 406 | + if (v->IsArray() && t->name[0] == '-' && t->length == 1) { | ||
| 407 | + v->PushBack(ValueType().Move(), allocator); | ||
| 408 | + v = &((*v)[v->Size() - 1]); | ||
| 409 | + exist = false; | ||
| 410 | + } | ||
| 411 | + else { | ||
| 412 | + if (t->index == kPointerInvalidIndex) { // must be object name | ||
| 413 | + if (!v->IsObject()) | ||
| 414 | + v->SetObject(); // Change to Object | ||
| 415 | + } | ||
| 416 | + else { // object name or array index | ||
| 417 | + if (!v->IsArray() && !v->IsObject()) | ||
| 418 | + v->SetArray(); // Change to Array | ||
| 419 | + } | ||
| 420 | + | ||
| 421 | + if (v->IsArray()) { | ||
| 422 | + if (t->index >= v->Size()) { | ||
| 423 | + v->Reserve(t->index + 1, allocator); | ||
| 424 | + while (t->index >= v->Size()) | ||
| 425 | + v->PushBack(ValueType().Move(), allocator); | ||
| 426 | + exist = false; | ||
| 427 | + } | ||
| 428 | + v = &((*v)[t->index]); | ||
| 429 | + } | ||
| 430 | + else { | ||
| 431 | + typename ValueType::MemberIterator m = v->FindMember(GenericStringRef<Ch>(t->name, t->length)); | ||
| 432 | + if (m == v->MemberEnd()) { | ||
| 433 | + v->AddMember(ValueType(t->name, t->length, allocator).Move(), ValueType().Move(), allocator); | ||
| 434 | + v = &(--v->MemberEnd())->value; // Assumes AddMember() appends at the end | ||
| 435 | + exist = false; | ||
| 436 | + } | ||
| 437 | + else | ||
| 438 | + v = &m->value; | ||
| 439 | + } | ||
| 440 | + } | ||
| 441 | + } | ||
| 442 | + | ||
| 443 | + if (alreadyExist) | ||
| 444 | + *alreadyExist = exist; | ||
| 445 | + | ||
| 446 | + return *v; | ||
| 447 | + } | ||
| 448 | + | ||
| 449 | + //! Creates a value in a document. | ||
| 450 | + /*! | ||
| 451 | + \param document A document to be resolved. | ||
| 452 | + \param alreadyExist If non-null, it stores whether the resolved value is already exist. | ||
| 453 | + \return The resolved newly created, or already exists value. | ||
| 454 | + */ | ||
| 455 | + template <typename stackAllocator> | ||
| 456 | + ValueType& Create(GenericDocument<EncodingType, typename ValueType::AllocatorType, stackAllocator>& document, bool* alreadyExist = 0) const { | ||
| 457 | + return Create(document, document.GetAllocator(), alreadyExist); | ||
| 458 | + } | ||
| 459 | + | ||
| 460 | + //@} | ||
| 461 | + | ||
| 462 | + //!@name Query value | ||
| 463 | + //@{ | ||
| 464 | + | ||
| 465 | + //! Query a value in a subtree. | ||
| 466 | + /*! | ||
| 467 | + \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. | ||
| 468 | + \param unresolvedTokenIndex If the pointer cannot resolve a token in the pointer, this parameter can obtain the index of unresolved token. | ||
| 469 | + \return Pointer to the value if it can be resolved. Otherwise null. | ||
| 470 | + | ||
| 471 | + \note | ||
| 472 | + There are only 3 situations when a value cannot be resolved: | ||
| 473 | + 1. A value in the path is not an array nor object. | ||
| 474 | + 2. An object value does not contain the token. | ||
| 475 | + 3. A token is out of range of an array value. | ||
| 476 | + | ||
| 477 | + Use unresolvedTokenIndex to retrieve the token index. | ||
| 478 | + */ | ||
| 479 | + ValueType* Get(ValueType& root, size_t* unresolvedTokenIndex = 0) const { | ||
| 480 | + RAPIDJSON_ASSERT(IsValid()); | ||
| 481 | + ValueType* v = &root; | ||
| 482 | + for (const Token *t = tokens_; t != tokens_ + tokenCount_; ++t) { | ||
| 483 | + switch (v->GetType()) { | ||
| 484 | + case kObjectType: | ||
| 485 | + { | ||
| 486 | + typename ValueType::MemberIterator m = v->FindMember(GenericStringRef<Ch>(t->name, t->length)); | ||
| 487 | + if (m == v->MemberEnd()) | ||
| 488 | + break; | ||
| 489 | + v = &m->value; | ||
| 490 | + } | ||
| 491 | + continue; | ||
| 492 | + case kArrayType: | ||
| 493 | + if (t->index == kPointerInvalidIndex || t->index >= v->Size()) | ||
| 494 | + break; | ||
| 495 | + v = &((*v)[t->index]); | ||
| 496 | + continue; | ||
| 497 | + default: | ||
| 498 | + break; | ||
| 499 | + } | ||
| 500 | + | ||
| 501 | + // Error: unresolved token | ||
| 502 | + if (unresolvedTokenIndex) | ||
| 503 | + *unresolvedTokenIndex = static_cast<size_t>(t - tokens_); | ||
| 504 | + return 0; | ||
| 505 | + } | ||
| 506 | + return v; | ||
| 507 | + } | ||
| 508 | + | ||
| 509 | + //! Query a const value in a const subtree. | ||
| 510 | + /*! | ||
| 511 | + \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. | ||
| 512 | + \return Pointer to the value if it can be resolved. Otherwise null. | ||
| 513 | + */ | ||
| 514 | + const ValueType* Get(const ValueType& root, size_t* unresolvedTokenIndex = 0) const { | ||
| 515 | + return Get(const_cast<ValueType&>(root), unresolvedTokenIndex); | ||
| 516 | + } | ||
| 517 | + | ||
| 518 | + //@} | ||
| 519 | + | ||
| 520 | + //!@name Query a value with default | ||
| 521 | + //@{ | ||
| 522 | + | ||
| 523 | + //! Query a value in a subtree with default value. | ||
| 524 | + /*! | ||
| 525 | + Similar to Get(), but if the specified value do not exists, it creates all parents and clone the default value. | ||
| 526 | + So that this function always succeed. | ||
| 527 | + | ||
| 528 | + \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. | ||
| 529 | + \param defaultValue Default value to be cloned if the value was not exists. | ||
| 530 | + \param allocator Allocator for creating the values if the specified value or its parents are not exist. | ||
| 531 | + \see Create() | ||
| 532 | + */ | ||
| 533 | + ValueType& GetWithDefault(ValueType& root, const ValueType& defaultValue, typename ValueType::AllocatorType& allocator) const { | ||
| 534 | + bool alreadyExist; | ||
| 535 | + Value& v = Create(root, allocator, &alreadyExist); | ||
| 536 | + return alreadyExist ? v : v.CopyFrom(defaultValue, allocator); | ||
| 537 | + } | ||
| 538 | + | ||
| 539 | + //! Query a value in a subtree with default null-terminated string. | ||
| 540 | + ValueType& GetWithDefault(ValueType& root, const Ch* defaultValue, typename ValueType::AllocatorType& allocator) const { | ||
| 541 | + bool alreadyExist; | ||
| 542 | + Value& v = Create(root, allocator, &alreadyExist); | ||
| 543 | + return alreadyExist ? v : v.SetString(defaultValue, allocator); | ||
| 544 | + } | ||
| 545 | + | ||
| 546 | +#if RAPIDJSON_HAS_STDSTRING | ||
| 547 | + //! Query a value in a subtree with default std::basic_string. | ||
| 548 | + ValueType& GetWithDefault(ValueType& root, const std::basic_string<Ch>& defaultValue, typename ValueType::AllocatorType& allocator) const { | ||
| 549 | + bool alreadyExist; | ||
| 550 | + Value& v = Create(root, allocator, &alreadyExist); | ||
| 551 | + return alreadyExist ? v : v.SetString(defaultValue, allocator); | ||
| 552 | + } | ||
| 553 | +#endif | ||
| 554 | + | ||
| 555 | + //! Query a value in a subtree with default primitive value. | ||
| 556 | + /*! | ||
| 557 | + \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool | ||
| 558 | + */ | ||
| 559 | + template <typename T> | ||
| 560 | + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T>, internal::IsGenericValue<T> >), (ValueType&)) | ||
| 561 | + GetWithDefault(ValueType& root, T defaultValue, typename ValueType::AllocatorType& allocator) const { | ||
| 562 | + return GetWithDefault(root, ValueType(defaultValue).Move(), allocator); | ||
| 563 | + } | ||
| 564 | + | ||
| 565 | + //! Query a value in a document with default value. | ||
| 566 | + template <typename stackAllocator> | ||
| 567 | + ValueType& GetWithDefault(GenericDocument<EncodingType, typename ValueType::AllocatorType, stackAllocator>& document, const ValueType& defaultValue) const { | ||
| 568 | + return GetWithDefault(document, defaultValue, document.GetAllocator()); | ||
| 569 | + } | ||
| 570 | + | ||
| 571 | + //! Query a value in a document with default null-terminated string. | ||
| 572 | + template <typename stackAllocator> | ||
| 573 | + ValueType& GetWithDefault(GenericDocument<EncodingType, typename ValueType::AllocatorType, stackAllocator>& document, const Ch* defaultValue) const { | ||
| 574 | + return GetWithDefault(document, defaultValue, document.GetAllocator()); | ||
| 575 | + } | ||
| 576 | + | ||
| 577 | +#if RAPIDJSON_HAS_STDSTRING | ||
| 578 | + //! Query a value in a document with default std::basic_string. | ||
| 579 | + template <typename stackAllocator> | ||
| 580 | + ValueType& GetWithDefault(GenericDocument<EncodingType, typename ValueType::AllocatorType, stackAllocator>& document, const std::basic_string<Ch>& defaultValue) const { | ||
| 581 | + return GetWithDefault(document, defaultValue, document.GetAllocator()); | ||
| 582 | + } | ||
| 583 | +#endif | ||
| 584 | + | ||
| 585 | + //! Query a value in a document with default primitive value. | ||
| 586 | + /*! | ||
| 587 | + \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool | ||
| 588 | + */ | ||
| 589 | + template <typename T, typename stackAllocator> | ||
| 590 | + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T>, internal::IsGenericValue<T> >), (ValueType&)) | ||
| 591 | + GetWithDefault(GenericDocument<EncodingType, typename ValueType::AllocatorType, stackAllocator>& document, T defaultValue) const { | ||
| 592 | + return GetWithDefault(document, defaultValue, document.GetAllocator()); | ||
| 593 | + } | ||
| 594 | + | ||
| 595 | + //@} | ||
| 596 | + | ||
| 597 | + //!@name Set a value | ||
| 598 | + //@{ | ||
| 599 | + | ||
| 600 | + //! Set a value in a subtree, with move semantics. | ||
| 601 | + /*! | ||
| 602 | + It creates all parents if they are not exist or types are different to the tokens. | ||
| 603 | + So this function always succeeds but potentially remove existing values. | ||
| 604 | + | ||
| 605 | + \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. | ||
| 606 | + \param value Value to be set. | ||
| 607 | + \param allocator Allocator for creating the values if the specified value or its parents are not exist. | ||
| 608 | + \see Create() | ||
| 609 | + */ | ||
| 610 | + ValueType& Set(ValueType& root, ValueType& value, typename ValueType::AllocatorType& allocator) const { | ||
| 611 | + return Create(root, allocator) = value; | ||
| 612 | + } | ||
| 613 | + | ||
| 614 | + //! Set a value in a subtree, with copy semantics. | ||
| 615 | + ValueType& Set(ValueType& root, const ValueType& value, typename ValueType::AllocatorType& allocator) const { | ||
| 616 | + return Create(root, allocator).CopyFrom(value, allocator); | ||
| 617 | + } | ||
| 618 | + | ||
| 619 | + //! Set a null-terminated string in a subtree. | ||
| 620 | + ValueType& Set(ValueType& root, const Ch* value, typename ValueType::AllocatorType& allocator) const { | ||
| 621 | + return Create(root, allocator) = ValueType(value, allocator).Move(); | ||
| 622 | + } | ||
| 623 | + | ||
| 624 | +#if RAPIDJSON_HAS_STDSTRING | ||
| 625 | + //! Set a std::basic_string in a subtree. | ||
| 626 | + ValueType& Set(ValueType& root, const std::basic_string<Ch>& value, typename ValueType::AllocatorType& allocator) const { | ||
| 627 | + return Create(root, allocator) = ValueType(value, allocator).Move(); | ||
| 628 | + } | ||
| 629 | +#endif | ||
| 630 | + | ||
| 631 | + //! Set a primitive value in a subtree. | ||
| 632 | + /*! | ||
| 633 | + \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool | ||
| 634 | + */ | ||
| 635 | + template <typename T> | ||
| 636 | + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T>, internal::IsGenericValue<T> >), (ValueType&)) | ||
| 637 | + Set(ValueType& root, T value, typename ValueType::AllocatorType& allocator) const { | ||
| 638 | + return Create(root, allocator) = ValueType(value).Move(); | ||
| 639 | + } | ||
| 640 | + | ||
| 641 | + //! Set a value in a document, with move semantics. | ||
| 642 | + template <typename stackAllocator> | ||
| 643 | + ValueType& Set(GenericDocument<EncodingType, typename ValueType::AllocatorType, stackAllocator>& document, ValueType& value) const { | ||
| 644 | + return Create(document) = value; | ||
| 645 | + } | ||
| 646 | + | ||
| 647 | + //! Set a value in a document, with copy semantics. | ||
| 648 | + template <typename stackAllocator> | ||
| 649 | + ValueType& Set(GenericDocument<EncodingType, typename ValueType::AllocatorType, stackAllocator>& document, const ValueType& value) const { | ||
| 650 | + return Create(document).CopyFrom(value, document.GetAllocator()); | ||
| 651 | + } | ||
| 652 | + | ||
| 653 | + //! Set a null-terminated string in a document. | ||
| 654 | + template <typename stackAllocator> | ||
| 655 | + ValueType& Set(GenericDocument<EncodingType, typename ValueType::AllocatorType, stackAllocator>& document, const Ch* value) const { | ||
| 656 | + return Create(document) = ValueType(value, document.GetAllocator()).Move(); | ||
| 657 | + } | ||
| 658 | + | ||
| 659 | +#if RAPIDJSON_HAS_STDSTRING | ||
| 660 | + //! Sets a std::basic_string in a document. | ||
| 661 | + template <typename stackAllocator> | ||
| 662 | + ValueType& Set(GenericDocument<EncodingType, typename ValueType::AllocatorType, stackAllocator>& document, const std::basic_string<Ch>& value) const { | ||
| 663 | + return Create(document) = ValueType(value, document.GetAllocator()).Move(); | ||
| 664 | + } | ||
| 665 | +#endif | ||
| 666 | + | ||
| 667 | + //! Set a primitive value in a document. | ||
| 668 | + /*! | ||
| 669 | + \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool | ||
| 670 | + */ | ||
| 671 | + template <typename T, typename stackAllocator> | ||
| 672 | + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T>, internal::IsGenericValue<T> >), (ValueType&)) | ||
| 673 | + Set(GenericDocument<EncodingType, typename ValueType::AllocatorType, stackAllocator>& document, T value) const { | ||
| 674 | + return Create(document) = value; | ||
| 675 | + } | ||
| 676 | + | ||
| 677 | + //@} | ||
| 678 | + | ||
| 679 | + //!@name Swap a value | ||
| 680 | + //@{ | ||
| 681 | + | ||
| 682 | + //! Swap a value with a value in a subtree. | ||
| 683 | + /*! | ||
| 684 | + It creates all parents if they are not exist or types are different to the tokens. | ||
| 685 | + So this function always succeeds but potentially remove existing values. | ||
| 686 | + | ||
| 687 | + \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. | ||
| 688 | + \param value Value to be swapped. | ||
| 689 | + \param allocator Allocator for creating the values if the specified value or its parents are not exist. | ||
| 690 | + \see Create() | ||
| 691 | + */ | ||
| 692 | + ValueType& Swap(ValueType& root, ValueType& value, typename ValueType::AllocatorType& allocator) const { | ||
| 693 | + return Create(root, allocator).Swap(value); | ||
| 694 | + } | ||
| 695 | + | ||
| 696 | + //! Swap a value with a value in a document. | ||
| 697 | + template <typename stackAllocator> | ||
| 698 | + ValueType& Swap(GenericDocument<EncodingType, typename ValueType::AllocatorType, stackAllocator>& document, ValueType& value) const { | ||
| 699 | + return Create(document).Swap(value); | ||
| 700 | + } | ||
| 701 | + | ||
| 702 | + //@} | ||
| 703 | + | ||
| 704 | + //! Erase a value in a subtree. | ||
| 705 | + /*! | ||
| 706 | + \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. | ||
| 707 | + \return Whether the resolved value is found and erased. | ||
| 708 | + | ||
| 709 | + \note Erasing with an empty pointer \c Pointer(""), i.e. the root, always fail and return false. | ||
| 710 | + */ | ||
| 711 | + bool Erase(ValueType& root) const { | ||
| 712 | + RAPIDJSON_ASSERT(IsValid()); | ||
| 713 | + if (tokenCount_ == 0) // Cannot erase the root | ||
| 714 | + return false; | ||
| 715 | + | ||
| 716 | + ValueType* v = &root; | ||
| 717 | + const Token* last = tokens_ + (tokenCount_ - 1); | ||
| 718 | + for (const Token *t = tokens_; t != last; ++t) { | ||
| 719 | + switch (v->GetType()) { | ||
| 720 | + case kObjectType: | ||
| 721 | + { | ||
| 722 | + typename ValueType::MemberIterator m = v->FindMember(GenericStringRef<Ch>(t->name, t->length)); | ||
| 723 | + if (m == v->MemberEnd()) | ||
| 724 | + return false; | ||
| 725 | + v = &m->value; | ||
| 726 | + } | ||
| 727 | + break; | ||
| 728 | + case kArrayType: | ||
| 729 | + if (t->index == kPointerInvalidIndex || t->index >= v->Size()) | ||
| 730 | + return false; | ||
| 731 | + v = &((*v)[t->index]); | ||
| 732 | + break; | ||
| 733 | + default: | ||
| 734 | + return false; | ||
| 735 | + } | ||
| 736 | + } | ||
| 737 | + | ||
| 738 | + switch (v->GetType()) { | ||
| 739 | + case kObjectType: | ||
| 740 | + return v->EraseMember(GenericStringRef<Ch>(last->name, last->length)); | ||
| 741 | + case kArrayType: | ||
| 742 | + if (last->index == kPointerInvalidIndex || last->index >= v->Size()) | ||
| 743 | + return false; | ||
| 744 | + v->Erase(v->Begin() + last->index); | ||
| 745 | + return true; | ||
| 746 | + default: | ||
| 747 | + return false; | ||
| 748 | + } | ||
| 749 | + } | ||
| 750 | + | ||
| 751 | +private: | ||
| 752 | + //! Clone the content from rhs to this. | ||
| 753 | + /*! | ||
| 754 | + \param rhs Source pointer. | ||
| 755 | + \param extraToken Extra tokens to be allocated. | ||
| 756 | + \param extraNameBufferSize Extra name buffer size (in number of Ch) to be allocated. | ||
| 757 | + \return Start of non-occupied name buffer, for storing extra names. | ||
| 758 | + */ | ||
| 759 | + Ch* CopyFromRaw(const GenericPointer& rhs, size_t extraToken = 0, size_t extraNameBufferSize = 0) { | ||
| 760 | + if (!allocator_) // allocator is independently owned. | ||
| 761 | + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); | ||
| 762 | + | ||
| 763 | + size_t nameBufferSize = rhs.tokenCount_; // null terminators for tokens | ||
| 764 | + for (Token *t = rhs.tokens_; t != rhs.tokens_ + rhs.tokenCount_; ++t) | ||
| 765 | + nameBufferSize += t->length; | ||
| 766 | + | ||
| 767 | + tokenCount_ = rhs.tokenCount_ + extraToken; | ||
| 768 | + tokens_ = static_cast<Token *>(allocator_->Malloc(tokenCount_ * sizeof(Token) + (nameBufferSize + extraNameBufferSize) * sizeof(Ch))); | ||
| 769 | + nameBuffer_ = reinterpret_cast<Ch *>(tokens_ + tokenCount_); | ||
| 770 | + if (rhs.tokenCount_ > 0) { | ||
| 771 | + std::memcpy(tokens_, rhs.tokens_, rhs.tokenCount_ * sizeof(Token)); | ||
| 772 | + } | ||
| 773 | + if (nameBufferSize > 0) { | ||
| 774 | + std::memcpy(nameBuffer_, rhs.nameBuffer_, nameBufferSize * sizeof(Ch)); | ||
| 775 | + } | ||
| 776 | + | ||
| 777 | + // Adjust pointers to name buffer | ||
| 778 | + std::ptrdiff_t diff = nameBuffer_ - rhs.nameBuffer_; | ||
| 779 | + for (Token *t = tokens_; t != tokens_ + rhs.tokenCount_; ++t) | ||
| 780 | + t->name += diff; | ||
| 781 | + | ||
| 782 | + return nameBuffer_ + nameBufferSize; | ||
| 783 | + } | ||
| 784 | + | ||
| 785 | + //! Check whether a character should be percent-encoded. | ||
| 786 | + /*! | ||
| 787 | + According to RFC 3986 2.3 Unreserved Characters. | ||
| 788 | + \param c The character (code unit) to be tested. | ||
| 789 | + */ | ||
| 790 | + bool NeedPercentEncode(Ch c) const { | ||
| 791 | + return !((c >= '0' && c <= '9') || (c >= 'A' && c <='Z') || (c >= 'a' && c <= 'z') || c == '-' || c == '.' || c == '_' || c =='~'); | ||
| 792 | + } | ||
| 793 | + | ||
| 794 | + //! Parse a JSON String or its URI fragment representation into tokens. | ||
| 795 | +#ifndef __clang__ // -Wdocumentation | ||
| 796 | + /*! | ||
| 797 | + \param source Either a JSON Pointer string, or its URI fragment representation. Not need to be null terminated. | ||
| 798 | + \param length Length of the source string. | ||
| 799 | + \note Source cannot be JSON String Representation of JSON Pointer, e.g. In "/\u0000", \u0000 will not be unescaped. | ||
| 800 | + */ | ||
| 801 | +#endif | ||
| 802 | + void Parse(const Ch* source, size_t length) { | ||
| 803 | + RAPIDJSON_ASSERT(source != NULL); | ||
| 804 | + RAPIDJSON_ASSERT(nameBuffer_ == 0); | ||
| 805 | + RAPIDJSON_ASSERT(tokens_ == 0); | ||
| 806 | + | ||
| 807 | + // Create own allocator if user did not supply. | ||
| 808 | + if (!allocator_) | ||
| 809 | + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); | ||
| 810 | + | ||
| 811 | + // Count number of '/' as tokenCount | ||
| 812 | + tokenCount_ = 0; | ||
| 813 | + for (const Ch* s = source; s != source + length; s++) | ||
| 814 | + if (*s == '/') | ||
| 815 | + tokenCount_++; | ||
| 816 | + | ||
| 817 | + Token* token = tokens_ = static_cast<Token *>(allocator_->Malloc(tokenCount_ * sizeof(Token) + length * sizeof(Ch))); | ||
| 818 | + Ch* name = nameBuffer_ = reinterpret_cast<Ch *>(tokens_ + tokenCount_); | ||
| 819 | + size_t i = 0; | ||
| 820 | + | ||
| 821 | + // Detect if it is a URI fragment | ||
| 822 | + bool uriFragment = false; | ||
| 823 | + if (source[i] == '#') { | ||
| 824 | + uriFragment = true; | ||
| 825 | + i++; | ||
| 826 | + } | ||
| 827 | + | ||
| 828 | + if (i != length && source[i] != '/') { | ||
| 829 | + parseErrorCode_ = kPointerParseErrorTokenMustBeginWithSolidus; | ||
| 830 | + goto error; | ||
| 831 | + } | ||
| 832 | + | ||
| 833 | + while (i < length) { | ||
| 834 | + RAPIDJSON_ASSERT(source[i] == '/'); | ||
| 835 | + i++; // consumes '/' | ||
| 836 | + | ||
| 837 | + token->name = name; | ||
| 838 | + bool isNumber = true; | ||
| 839 | + | ||
| 840 | + while (i < length && source[i] != '/') { | ||
| 841 | + Ch c = source[i]; | ||
| 842 | + if (uriFragment) { | ||
| 843 | + // Decoding percent-encoding for URI fragment | ||
| 844 | + if (c == '%') { | ||
| 845 | + PercentDecodeStream is(&source[i], source + length); | ||
| 846 | + GenericInsituStringStream<EncodingType> os(name); | ||
| 847 | + Ch* begin = os.PutBegin(); | ||
| 848 | + if (!Transcoder<UTF8<>, EncodingType>().Validate(is, os) || !is.IsValid()) { | ||
| 849 | + parseErrorCode_ = kPointerParseErrorInvalidPercentEncoding; | ||
| 850 | + goto error; | ||
| 851 | + } | ||
| 852 | + size_t len = os.PutEnd(begin); | ||
| 853 | + i += is.Tell() - 1; | ||
| 854 | + if (len == 1) | ||
| 855 | + c = *name; | ||
| 856 | + else { | ||
| 857 | + name += len; | ||
| 858 | + isNumber = false; | ||
| 859 | + i++; | ||
| 860 | + continue; | ||
| 861 | + } | ||
| 862 | + } | ||
| 863 | + else if (NeedPercentEncode(c)) { | ||
| 864 | + parseErrorCode_ = kPointerParseErrorCharacterMustPercentEncode; | ||
| 865 | + goto error; | ||
| 866 | + } | ||
| 867 | + } | ||
| 868 | + | ||
| 869 | + i++; | ||
| 870 | + | ||
| 871 | + // Escaping "~0" -> '~', "~1" -> '/' | ||
| 872 | + if (c == '~') { | ||
| 873 | + if (i < length) { | ||
| 874 | + c = source[i]; | ||
| 875 | + if (c == '0') c = '~'; | ||
| 876 | + else if (c == '1') c = '/'; | ||
| 877 | + else { | ||
| 878 | + parseErrorCode_ = kPointerParseErrorInvalidEscape; | ||
| 879 | + goto error; | ||
| 880 | + } | ||
| 881 | + i++; | ||
| 882 | + } | ||
| 883 | + else { | ||
| 884 | + parseErrorCode_ = kPointerParseErrorInvalidEscape; | ||
| 885 | + goto error; | ||
| 886 | + } | ||
| 887 | + } | ||
| 888 | + | ||
| 889 | + // First check for index: all of characters are digit | ||
| 890 | + if (c < '0' || c > '9') | ||
| 891 | + isNumber = false; | ||
| 892 | + | ||
| 893 | + *name++ = c; | ||
| 894 | + } | ||
| 895 | + token->length = static_cast<SizeType>(name - token->name); | ||
| 896 | + if (token->length == 0) | ||
| 897 | + isNumber = false; | ||
| 898 | + *name++ = '\0'; // Null terminator | ||
| 899 | + | ||
| 900 | + // Second check for index: more than one digit cannot have leading zero | ||
| 901 | + if (isNumber && token->length > 1 && token->name[0] == '0') | ||
| 902 | + isNumber = false; | ||
| 903 | + | ||
| 904 | + // String to SizeType conversion | ||
| 905 | + SizeType n = 0; | ||
| 906 | + if (isNumber) { | ||
| 907 | + for (size_t j = 0; j < token->length; j++) { | ||
| 908 | + SizeType m = n * 10 + static_cast<SizeType>(token->name[j] - '0'); | ||
| 909 | + if (m < n) { // overflow detection | ||
| 910 | + isNumber = false; | ||
| 911 | + break; | ||
| 912 | + } | ||
| 913 | + n = m; | ||
| 914 | + } | ||
| 915 | + } | ||
| 916 | + | ||
| 917 | + token->index = isNumber ? n : kPointerInvalidIndex; | ||
| 918 | + token++; | ||
| 919 | + } | ||
| 920 | + | ||
| 921 | + RAPIDJSON_ASSERT(name <= nameBuffer_ + length); // Should not overflow buffer | ||
| 922 | + parseErrorCode_ = kPointerParseErrorNone; | ||
| 923 | + return; | ||
| 924 | + | ||
| 925 | + error: | ||
| 926 | + Allocator::Free(tokens_); | ||
| 927 | + nameBuffer_ = 0; | ||
| 928 | + tokens_ = 0; | ||
| 929 | + tokenCount_ = 0; | ||
| 930 | + parseErrorOffset_ = i; | ||
| 931 | + return; | ||
| 932 | + } | ||
| 933 | + | ||
| 934 | + //! Stringify to string or URI fragment representation. | ||
| 935 | + /*! | ||
| 936 | + \tparam uriFragment True for stringifying to URI fragment representation. False for string representation. | ||
| 937 | + \tparam OutputStream type of output stream. | ||
| 938 | + \param os The output stream. | ||
| 939 | + */ | ||
| 940 | + template<bool uriFragment, typename OutputStream> | ||
| 941 | + bool Stringify(OutputStream& os) const { | ||
| 942 | + RAPIDJSON_ASSERT(IsValid()); | ||
| 943 | + | ||
| 944 | + if (uriFragment) | ||
| 945 | + os.Put('#'); | ||
| 946 | + | ||
| 947 | + for (Token *t = tokens_; t != tokens_ + tokenCount_; ++t) { | ||
| 948 | + os.Put('/'); | ||
| 949 | + for (size_t j = 0; j < t->length; j++) { | ||
| 950 | + Ch c = t->name[j]; | ||
| 951 | + if (c == '~') { | ||
| 952 | + os.Put('~'); | ||
| 953 | + os.Put('0'); | ||
| 954 | + } | ||
| 955 | + else if (c == '/') { | ||
| 956 | + os.Put('~'); | ||
| 957 | + os.Put('1'); | ||
| 958 | + } | ||
| 959 | + else if (uriFragment && NeedPercentEncode(c)) { | ||
| 960 | + // Transcode to UTF8 sequence | ||
| 961 | + GenericStringStream<typename ValueType::EncodingType> source(&t->name[j]); | ||
| 962 | + PercentEncodeStream<OutputStream> target(os); | ||
| 963 | + if (!Transcoder<EncodingType, UTF8<> >().Validate(source, target)) | ||
| 964 | + return false; | ||
| 965 | + j += source.Tell() - 1; | ||
| 966 | + } | ||
| 967 | + else | ||
| 968 | + os.Put(c); | ||
| 969 | + } | ||
| 970 | + } | ||
| 971 | + return true; | ||
| 972 | + } | ||
| 973 | + | ||
| 974 | + //! A helper stream for decoding a percent-encoded sequence into code unit. | ||
| 975 | + /*! | ||
| 976 | + This stream decodes %XY triplet into code unit (0-255). | ||
| 977 | + If it encounters invalid characters, it sets output code unit as 0 and | ||
| 978 | + mark invalid, and to be checked by IsValid(). | ||
| 979 | + */ | ||
| 980 | + class PercentDecodeStream { | ||
| 981 | + public: | ||
| 982 | + typedef typename ValueType::Ch Ch; | ||
| 983 | + | ||
| 984 | + //! Constructor | ||
| 985 | + /*! | ||
| 986 | + \param source Start of the stream | ||
| 987 | + \param end Past-the-end of the stream. | ||
| 988 | + */ | ||
| 989 | + PercentDecodeStream(const Ch* source, const Ch* end) : src_(source), head_(source), end_(end), valid_(true) {} | ||
| 990 | + | ||
| 991 | + Ch Take() { | ||
| 992 | + if (*src_ != '%' || src_ + 3 > end_) { // %XY triplet | ||
| 993 | + valid_ = false; | ||
| 994 | + return 0; | ||
| 995 | + } | ||
| 996 | + src_++; | ||
| 997 | + Ch c = 0; | ||
| 998 | + for (int j = 0; j < 2; j++) { | ||
| 999 | + c = static_cast<Ch>(c << 4); | ||
| 1000 | + Ch h = *src_; | ||
| 1001 | + if (h >= '0' && h <= '9') c = static_cast<Ch>(c + h - '0'); | ||
| 1002 | + else if (h >= 'A' && h <= 'F') c = static_cast<Ch>(c + h - 'A' + 10); | ||
| 1003 | + else if (h >= 'a' && h <= 'f') c = static_cast<Ch>(c + h - 'a' + 10); | ||
| 1004 | + else { | ||
| 1005 | + valid_ = false; | ||
| 1006 | + return 0; | ||
| 1007 | + } | ||
| 1008 | + src_++; | ||
| 1009 | + } | ||
| 1010 | + return c; | ||
| 1011 | + } | ||
| 1012 | + | ||
| 1013 | + size_t Tell() const { return static_cast<size_t>(src_ - head_); } | ||
| 1014 | + bool IsValid() const { return valid_; } | ||
| 1015 | + | ||
| 1016 | + private: | ||
| 1017 | + const Ch* src_; //!< Current read position. | ||
| 1018 | + const Ch* head_; //!< Original head of the string. | ||
| 1019 | + const Ch* end_; //!< Past-the-end position. | ||
| 1020 | + bool valid_; //!< Whether the parsing is valid. | ||
| 1021 | + }; | ||
| 1022 | + | ||
| 1023 | + //! A helper stream to encode character (UTF-8 code unit) into percent-encoded sequence. | ||
| 1024 | + template <typename OutputStream> | ||
| 1025 | + class PercentEncodeStream { | ||
| 1026 | + public: | ||
| 1027 | + PercentEncodeStream(OutputStream& os) : os_(os) {} | ||
| 1028 | + void Put(char c) { // UTF-8 must be byte | ||
| 1029 | + unsigned char u = static_cast<unsigned char>(c); | ||
| 1030 | + static const char hexDigits[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; | ||
| 1031 | + os_.Put('%'); | ||
| 1032 | + os_.Put(static_cast<typename OutputStream::Ch>(hexDigits[u >> 4])); | ||
| 1033 | + os_.Put(static_cast<typename OutputStream::Ch>(hexDigits[u & 15])); | ||
| 1034 | + } | ||
| 1035 | + private: | ||
| 1036 | + OutputStream& os_; | ||
| 1037 | + }; | ||
| 1038 | + | ||
| 1039 | + Allocator* allocator_; //!< The current allocator. It is either user-supplied or equal to ownAllocator_. | ||
| 1040 | + Allocator* ownAllocator_; //!< Allocator owned by this Pointer. | ||
| 1041 | + Ch* nameBuffer_; //!< A buffer containing all names in tokens. | ||
| 1042 | + Token* tokens_; //!< A list of tokens. | ||
| 1043 | + size_t tokenCount_; //!< Number of tokens in tokens_. | ||
| 1044 | + size_t parseErrorOffset_; //!< Offset in code unit when parsing fail. | ||
| 1045 | + PointerParseErrorCode parseErrorCode_; //!< Parsing error code. | ||
| 1046 | +}; | ||
| 1047 | + | ||
| 1048 | +//! GenericPointer for Value (UTF-8, default allocator). | ||
| 1049 | +typedef GenericPointer<Value> Pointer; | ||
| 1050 | + | ||
| 1051 | +//!@name Helper functions for GenericPointer | ||
| 1052 | +//@{ | ||
| 1053 | + | ||
| 1054 | +////////////////////////////////////////////////////////////////////////////// | ||
| 1055 | + | ||
| 1056 | +template <typename T> | ||
| 1057 | +typename T::ValueType& CreateValueByPointer(T& root, const GenericPointer<typename T::ValueType>& pointer, typename T::AllocatorType& a) { | ||
| 1058 | + return pointer.Create(root, a); | ||
| 1059 | +} | ||
| 1060 | + | ||
| 1061 | +template <typename T, typename CharType, size_t N> | ||
| 1062 | +typename T::ValueType& CreateValueByPointer(T& root, const CharType(&source)[N], typename T::AllocatorType& a) { | ||
| 1063 | + return GenericPointer<typename T::ValueType>(source, N - 1).Create(root, a); | ||
| 1064 | +} | ||
| 1065 | + | ||
| 1066 | +// No allocator parameter | ||
| 1067 | + | ||
| 1068 | +template <typename DocumentType> | ||
| 1069 | +typename DocumentType::ValueType& CreateValueByPointer(DocumentType& document, const GenericPointer<typename DocumentType::ValueType>& pointer) { | ||
| 1070 | + return pointer.Create(document); | ||
| 1071 | +} | ||
| 1072 | + | ||
| 1073 | +template <typename DocumentType, typename CharType, size_t N> | ||
| 1074 | +typename DocumentType::ValueType& CreateValueByPointer(DocumentType& document, const CharType(&source)[N]) { | ||
| 1075 | + return GenericPointer<typename DocumentType::ValueType>(source, N - 1).Create(document); | ||
| 1076 | +} | ||
| 1077 | + | ||
| 1078 | +////////////////////////////////////////////////////////////////////////////// | ||
| 1079 | + | ||
| 1080 | +template <typename T> | ||
| 1081 | +typename T::ValueType* GetValueByPointer(T& root, const GenericPointer<typename T::ValueType>& pointer, size_t* unresolvedTokenIndex = 0) { | ||
| 1082 | + return pointer.Get(root, unresolvedTokenIndex); | ||
| 1083 | +} | ||
| 1084 | + | ||
| 1085 | +template <typename T> | ||
| 1086 | +const typename T::ValueType* GetValueByPointer(const T& root, const GenericPointer<typename T::ValueType>& pointer, size_t* unresolvedTokenIndex = 0) { | ||
| 1087 | + return pointer.Get(root, unresolvedTokenIndex); | ||
| 1088 | +} | ||
| 1089 | + | ||
| 1090 | +template <typename T, typename CharType, size_t N> | ||
| 1091 | +typename T::ValueType* GetValueByPointer(T& root, const CharType (&source)[N], size_t* unresolvedTokenIndex = 0) { | ||
| 1092 | + return GenericPointer<typename T::ValueType>(source, N - 1).Get(root, unresolvedTokenIndex); | ||
| 1093 | +} | ||
| 1094 | + | ||
| 1095 | +template <typename T, typename CharType, size_t N> | ||
| 1096 | +const typename T::ValueType* GetValueByPointer(const T& root, const CharType(&source)[N], size_t* unresolvedTokenIndex = 0) { | ||
| 1097 | + return GenericPointer<typename T::ValueType>(source, N - 1).Get(root, unresolvedTokenIndex); | ||
| 1098 | +} | ||
| 1099 | + | ||
| 1100 | +////////////////////////////////////////////////////////////////////////////// | ||
| 1101 | + | ||
| 1102 | +template <typename T> | ||
| 1103 | +typename T::ValueType& GetValueByPointerWithDefault(T& root, const GenericPointer<typename T::ValueType>& pointer, const typename T::ValueType& defaultValue, typename T::AllocatorType& a) { | ||
| 1104 | + return pointer.GetWithDefault(root, defaultValue, a); | ||
| 1105 | +} | ||
| 1106 | + | ||
| 1107 | +template <typename T> | ||
| 1108 | +typename T::ValueType& GetValueByPointerWithDefault(T& root, const GenericPointer<typename T::ValueType>& pointer, const typename T::Ch* defaultValue, typename T::AllocatorType& a) { | ||
| 1109 | + return pointer.GetWithDefault(root, defaultValue, a); | ||
| 1110 | +} | ||
| 1111 | + | ||
| 1112 | +#if RAPIDJSON_HAS_STDSTRING | ||
| 1113 | +template <typename T> | ||
| 1114 | +typename T::ValueType& GetValueByPointerWithDefault(T& root, const GenericPointer<typename T::ValueType>& pointer, const std::basic_string<typename T::Ch>& defaultValue, typename T::AllocatorType& a) { | ||
| 1115 | + return pointer.GetWithDefault(root, defaultValue, a); | ||
| 1116 | +} | ||
| 1117 | +#endif | ||
| 1118 | + | ||
| 1119 | +template <typename T, typename T2> | ||
| 1120 | +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T2>, internal::IsGenericValue<T2> >), (typename T::ValueType&)) | ||
| 1121 | +GetValueByPointerWithDefault(T& root, const GenericPointer<typename T::ValueType>& pointer, T2 defaultValue, typename T::AllocatorType& a) { | ||
| 1122 | + return pointer.GetWithDefault(root, defaultValue, a); | ||
| 1123 | +} | ||
| 1124 | + | ||
| 1125 | +template <typename T, typename CharType, size_t N> | ||
| 1126 | +typename T::ValueType& GetValueByPointerWithDefault(T& root, const CharType(&source)[N], const typename T::ValueType& defaultValue, typename T::AllocatorType& a) { | ||
| 1127 | + return GenericPointer<typename T::ValueType>(source, N - 1).GetWithDefault(root, defaultValue, a); | ||
| 1128 | +} | ||
| 1129 | + | ||
| 1130 | +template <typename T, typename CharType, size_t N> | ||
| 1131 | +typename T::ValueType& GetValueByPointerWithDefault(T& root, const CharType(&source)[N], const typename T::Ch* defaultValue, typename T::AllocatorType& a) { | ||
| 1132 | + return GenericPointer<typename T::ValueType>(source, N - 1).GetWithDefault(root, defaultValue, a); | ||
| 1133 | +} | ||
| 1134 | + | ||
| 1135 | +#if RAPIDJSON_HAS_STDSTRING | ||
| 1136 | +template <typename T, typename CharType, size_t N> | ||
| 1137 | +typename T::ValueType& GetValueByPointerWithDefault(T& root, const CharType(&source)[N], const std::basic_string<typename T::Ch>& defaultValue, typename T::AllocatorType& a) { | ||
| 1138 | + return GenericPointer<typename T::ValueType>(source, N - 1).GetWithDefault(root, defaultValue, a); | ||
| 1139 | +} | ||
| 1140 | +#endif | ||
| 1141 | + | ||
| 1142 | +template <typename T, typename CharType, size_t N, typename T2> | ||
| 1143 | +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T2>, internal::IsGenericValue<T2> >), (typename T::ValueType&)) | ||
| 1144 | +GetValueByPointerWithDefault(T& root, const CharType(&source)[N], T2 defaultValue, typename T::AllocatorType& a) { | ||
| 1145 | + return GenericPointer<typename T::ValueType>(source, N - 1).GetWithDefault(root, defaultValue, a); | ||
| 1146 | +} | ||
| 1147 | + | ||
| 1148 | +// No allocator parameter | ||
| 1149 | + | ||
| 1150 | +template <typename DocumentType> | ||
| 1151 | +typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const GenericPointer<typename DocumentType::ValueType>& pointer, const typename DocumentType::ValueType& defaultValue) { | ||
| 1152 | + return pointer.GetWithDefault(document, defaultValue); | ||
| 1153 | +} | ||
| 1154 | + | ||
| 1155 | +template <typename DocumentType> | ||
| 1156 | +typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const GenericPointer<typename DocumentType::ValueType>& pointer, const typename DocumentType::Ch* defaultValue) { | ||
| 1157 | + return pointer.GetWithDefault(document, defaultValue); | ||
| 1158 | +} | ||
| 1159 | + | ||
| 1160 | +#if RAPIDJSON_HAS_STDSTRING | ||
| 1161 | +template <typename DocumentType> | ||
| 1162 | +typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const GenericPointer<typename DocumentType::ValueType>& pointer, const std::basic_string<typename DocumentType::Ch>& defaultValue) { | ||
| 1163 | + return pointer.GetWithDefault(document, defaultValue); | ||
| 1164 | +} | ||
| 1165 | +#endif | ||
| 1166 | + | ||
| 1167 | +template <typename DocumentType, typename T2> | ||
| 1168 | +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T2>, internal::IsGenericValue<T2> >), (typename DocumentType::ValueType&)) | ||
| 1169 | +GetValueByPointerWithDefault(DocumentType& document, const GenericPointer<typename DocumentType::ValueType>& pointer, T2 defaultValue) { | ||
| 1170 | + return pointer.GetWithDefault(document, defaultValue); | ||
| 1171 | +} | ||
| 1172 | + | ||
| 1173 | +template <typename DocumentType, typename CharType, size_t N> | ||
| 1174 | +typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const CharType(&source)[N], const typename DocumentType::ValueType& defaultValue) { | ||
| 1175 | + return GenericPointer<typename DocumentType::ValueType>(source, N - 1).GetWithDefault(document, defaultValue); | ||
| 1176 | +} | ||
| 1177 | + | ||
| 1178 | +template <typename DocumentType, typename CharType, size_t N> | ||
| 1179 | +typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const CharType(&source)[N], const typename DocumentType::Ch* defaultValue) { | ||
| 1180 | + return GenericPointer<typename DocumentType::ValueType>(source, N - 1).GetWithDefault(document, defaultValue); | ||
| 1181 | +} | ||
| 1182 | + | ||
| 1183 | +#if RAPIDJSON_HAS_STDSTRING | ||
| 1184 | +template <typename DocumentType, typename CharType, size_t N> | ||
| 1185 | +typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const CharType(&source)[N], const std::basic_string<typename DocumentType::Ch>& defaultValue) { | ||
| 1186 | + return GenericPointer<typename DocumentType::ValueType>(source, N - 1).GetWithDefault(document, defaultValue); | ||
| 1187 | +} | ||
| 1188 | +#endif | ||
| 1189 | + | ||
| 1190 | +template <typename DocumentType, typename CharType, size_t N, typename T2> | ||
| 1191 | +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T2>, internal::IsGenericValue<T2> >), (typename DocumentType::ValueType&)) | ||
| 1192 | +GetValueByPointerWithDefault(DocumentType& document, const CharType(&source)[N], T2 defaultValue) { | ||
| 1193 | + return GenericPointer<typename DocumentType::ValueType>(source, N - 1).GetWithDefault(document, defaultValue); | ||
| 1194 | +} | ||
| 1195 | + | ||
| 1196 | +////////////////////////////////////////////////////////////////////////////// | ||
| 1197 | + | ||
| 1198 | +template <typename T> | ||
| 1199 | +typename T::ValueType& SetValueByPointer(T& root, const GenericPointer<typename T::ValueType>& pointer, typename T::ValueType& value, typename T::AllocatorType& a) { | ||
| 1200 | + return pointer.Set(root, value, a); | ||
| 1201 | +} | ||
| 1202 | + | ||
| 1203 | +template <typename T> | ||
| 1204 | +typename T::ValueType& SetValueByPointer(T& root, const GenericPointer<typename T::ValueType>& pointer, const typename T::ValueType& value, typename T::AllocatorType& a) { | ||
| 1205 | + return pointer.Set(root, value, a); | ||
| 1206 | +} | ||
| 1207 | + | ||
| 1208 | +template <typename T> | ||
| 1209 | +typename T::ValueType& SetValueByPointer(T& root, const GenericPointer<typename T::ValueType>& pointer, const typename T::Ch* value, typename T::AllocatorType& a) { | ||
| 1210 | + return pointer.Set(root, value, a); | ||
| 1211 | +} | ||
| 1212 | + | ||
| 1213 | +#if RAPIDJSON_HAS_STDSTRING | ||
| 1214 | +template <typename T> | ||
| 1215 | +typename T::ValueType& SetValueByPointer(T& root, const GenericPointer<typename T::ValueType>& pointer, const std::basic_string<typename T::Ch>& value, typename T::AllocatorType& a) { | ||
| 1216 | + return pointer.Set(root, value, a); | ||
| 1217 | +} | ||
| 1218 | +#endif | ||
| 1219 | + | ||
| 1220 | +template <typename T, typename T2> | ||
| 1221 | +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T2>, internal::IsGenericValue<T2> >), (typename T::ValueType&)) | ||
| 1222 | +SetValueByPointer(T& root, const GenericPointer<typename T::ValueType>& pointer, T2 value, typename T::AllocatorType& a) { | ||
| 1223 | + return pointer.Set(root, value, a); | ||
| 1224 | +} | ||
| 1225 | + | ||
| 1226 | +template <typename T, typename CharType, size_t N> | ||
| 1227 | +typename T::ValueType& SetValueByPointer(T& root, const CharType(&source)[N], typename T::ValueType& value, typename T::AllocatorType& a) { | ||
| 1228 | + return GenericPointer<typename T::ValueType>(source, N - 1).Set(root, value, a); | ||
| 1229 | +} | ||
| 1230 | + | ||
| 1231 | +template <typename T, typename CharType, size_t N> | ||
| 1232 | +typename T::ValueType& SetValueByPointer(T& root, const CharType(&source)[N], const typename T::ValueType& value, typename T::AllocatorType& a) { | ||
| 1233 | + return GenericPointer<typename T::ValueType>(source, N - 1).Set(root, value, a); | ||
| 1234 | +} | ||
| 1235 | + | ||
| 1236 | +template <typename T, typename CharType, size_t N> | ||
| 1237 | +typename T::ValueType& SetValueByPointer(T& root, const CharType(&source)[N], const typename T::Ch* value, typename T::AllocatorType& a) { | ||
| 1238 | + return GenericPointer<typename T::ValueType>(source, N - 1).Set(root, value, a); | ||
| 1239 | +} | ||
| 1240 | + | ||
| 1241 | +#if RAPIDJSON_HAS_STDSTRING | ||
| 1242 | +template <typename T, typename CharType, size_t N> | ||
| 1243 | +typename T::ValueType& SetValueByPointer(T& root, const CharType(&source)[N], const std::basic_string<typename T::Ch>& value, typename T::AllocatorType& a) { | ||
| 1244 | + return GenericPointer<typename T::ValueType>(source, N - 1).Set(root, value, a); | ||
| 1245 | +} | ||
| 1246 | +#endif | ||
| 1247 | + | ||
| 1248 | +template <typename T, typename CharType, size_t N, typename T2> | ||
| 1249 | +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T2>, internal::IsGenericValue<T2> >), (typename T::ValueType&)) | ||
| 1250 | +SetValueByPointer(T& root, const CharType(&source)[N], T2 value, typename T::AllocatorType& a) { | ||
| 1251 | + return GenericPointer<typename T::ValueType>(source, N - 1).Set(root, value, a); | ||
| 1252 | +} | ||
| 1253 | + | ||
| 1254 | +// No allocator parameter | ||
| 1255 | + | ||
| 1256 | +template <typename DocumentType> | ||
| 1257 | +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const GenericPointer<typename DocumentType::ValueType>& pointer, typename DocumentType::ValueType& value) { | ||
| 1258 | + return pointer.Set(document, value); | ||
| 1259 | +} | ||
| 1260 | + | ||
| 1261 | +template <typename DocumentType> | ||
| 1262 | +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const GenericPointer<typename DocumentType::ValueType>& pointer, const typename DocumentType::ValueType& value) { | ||
| 1263 | + return pointer.Set(document, value); | ||
| 1264 | +} | ||
| 1265 | + | ||
| 1266 | +template <typename DocumentType> | ||
| 1267 | +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const GenericPointer<typename DocumentType::ValueType>& pointer, const typename DocumentType::Ch* value) { | ||
| 1268 | + return pointer.Set(document, value); | ||
| 1269 | +} | ||
| 1270 | + | ||
| 1271 | +#if RAPIDJSON_HAS_STDSTRING | ||
| 1272 | +template <typename DocumentType> | ||
| 1273 | +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const GenericPointer<typename DocumentType::ValueType>& pointer, const std::basic_string<typename DocumentType::Ch>& value) { | ||
| 1274 | + return pointer.Set(document, value); | ||
| 1275 | +} | ||
| 1276 | +#endif | ||
| 1277 | + | ||
| 1278 | +template <typename DocumentType, typename T2> | ||
| 1279 | +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T2>, internal::IsGenericValue<T2> >), (typename DocumentType::ValueType&)) | ||
| 1280 | +SetValueByPointer(DocumentType& document, const GenericPointer<typename DocumentType::ValueType>& pointer, T2 value) { | ||
| 1281 | + return pointer.Set(document, value); | ||
| 1282 | +} | ||
| 1283 | + | ||
| 1284 | +template <typename DocumentType, typename CharType, size_t N> | ||
| 1285 | +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const CharType(&source)[N], typename DocumentType::ValueType& value) { | ||
| 1286 | + return GenericPointer<typename DocumentType::ValueType>(source, N - 1).Set(document, value); | ||
| 1287 | +} | ||
| 1288 | + | ||
| 1289 | +template <typename DocumentType, typename CharType, size_t N> | ||
| 1290 | +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const CharType(&source)[N], const typename DocumentType::ValueType& value) { | ||
| 1291 | + return GenericPointer<typename DocumentType::ValueType>(source, N - 1).Set(document, value); | ||
| 1292 | +} | ||
| 1293 | + | ||
| 1294 | +template <typename DocumentType, typename CharType, size_t N> | ||
| 1295 | +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const CharType(&source)[N], const typename DocumentType::Ch* value) { | ||
| 1296 | + return GenericPointer<typename DocumentType::ValueType>(source, N - 1).Set(document, value); | ||
| 1297 | +} | ||
| 1298 | + | ||
| 1299 | +#if RAPIDJSON_HAS_STDSTRING | ||
| 1300 | +template <typename DocumentType, typename CharType, size_t N> | ||
| 1301 | +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const CharType(&source)[N], const std::basic_string<typename DocumentType::Ch>& value) { | ||
| 1302 | + return GenericPointer<typename DocumentType::ValueType>(source, N - 1).Set(document, value); | ||
| 1303 | +} | ||
| 1304 | +#endif | ||
| 1305 | + | ||
| 1306 | +template <typename DocumentType, typename CharType, size_t N, typename T2> | ||
| 1307 | +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T2>, internal::IsGenericValue<T2> >), (typename DocumentType::ValueType&)) | ||
| 1308 | +SetValueByPointer(DocumentType& document, const CharType(&source)[N], T2 value) { | ||
| 1309 | + return GenericPointer<typename DocumentType::ValueType>(source, N - 1).Set(document, value); | ||
| 1310 | +} | ||
| 1311 | + | ||
| 1312 | +////////////////////////////////////////////////////////////////////////////// | ||
| 1313 | + | ||
| 1314 | +template <typename T> | ||
| 1315 | +typename T::ValueType& SwapValueByPointer(T& root, const GenericPointer<typename T::ValueType>& pointer, typename T::ValueType& value, typename T::AllocatorType& a) { | ||
| 1316 | + return pointer.Swap(root, value, a); | ||
| 1317 | +} | ||
| 1318 | + | ||
| 1319 | +template <typename T, typename CharType, size_t N> | ||
| 1320 | +typename T::ValueType& SwapValueByPointer(T& root, const CharType(&source)[N], typename T::ValueType& value, typename T::AllocatorType& a) { | ||
| 1321 | + return GenericPointer<typename T::ValueType>(source, N - 1).Swap(root, value, a); | ||
| 1322 | +} | ||
| 1323 | + | ||
| 1324 | +template <typename DocumentType> | ||
| 1325 | +typename DocumentType::ValueType& SwapValueByPointer(DocumentType& document, const GenericPointer<typename DocumentType::ValueType>& pointer, typename DocumentType::ValueType& value) { | ||
| 1326 | + return pointer.Swap(document, value); | ||
| 1327 | +} | ||
| 1328 | + | ||
| 1329 | +template <typename DocumentType, typename CharType, size_t N> | ||
| 1330 | +typename DocumentType::ValueType& SwapValueByPointer(DocumentType& document, const CharType(&source)[N], typename DocumentType::ValueType& value) { | ||
| 1331 | + return GenericPointer<typename DocumentType::ValueType>(source, N - 1).Swap(document, value); | ||
| 1332 | +} | ||
| 1333 | + | ||
| 1334 | +////////////////////////////////////////////////////////////////////////////// | ||
| 1335 | + | ||
| 1336 | +template <typename T> | ||
| 1337 | +bool EraseValueByPointer(T& root, const GenericPointer<typename T::ValueType>& pointer) { | ||
| 1338 | + return pointer.Erase(root); | ||
| 1339 | +} | ||
| 1340 | + | ||
| 1341 | +template <typename T, typename CharType, size_t N> | ||
| 1342 | +bool EraseValueByPointer(T& root, const CharType(&source)[N]) { | ||
| 1343 | + return GenericPointer<typename T::ValueType>(source, N - 1).Erase(root); | ||
| 1344 | +} | ||
| 1345 | + | ||
| 1346 | +//@} | ||
| 1347 | + | ||
| 1348 | +RAPIDJSON_NAMESPACE_END | ||
| 1349 | + | ||
| 1350 | +#ifdef __clang__ | ||
| 1351 | +RAPIDJSON_DIAG_POP | ||
| 1352 | +#endif | ||
| 1353 | + | ||
| 1354 | +#ifdef _MSC_VER | ||
| 1355 | +RAPIDJSON_DIAG_POP | ||
| 1356 | +#endif | ||
| 1357 | + | ||
| 1358 | +#endif // RAPIDJSON_POINTER_H_ |
PaperGraph/include/rapidjson/prettywriter.h
0 → 100644
| 1 | +// Tencent is pleased to support the open source community by making RapidJSON available. | ||
| 2 | +// | ||
| 3 | +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. | ||
| 4 | +// | ||
| 5 | +// Licensed under the MIT License (the "License"); you may not use this file except | ||
| 6 | +// in compliance with the License. You may obtain a copy of the License at | ||
| 7 | +// | ||
| 8 | +// http://opensource.org/licenses/MIT | ||
| 9 | +// | ||
| 10 | +// Unless required by applicable law or agreed to in writing, software distributed | ||
| 11 | +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR | ||
| 12 | +// CONDITIONS OF ANY KIND, either express or implied. See the License for the | ||
| 13 | +// specific language governing permissions and limitations under the License. | ||
| 14 | + | ||
| 15 | +#ifndef RAPIDJSON_PRETTYWRITER_H_ | ||
| 16 | +#define RAPIDJSON_PRETTYWRITER_H_ | ||
| 17 | + | ||
| 18 | +#include "writer.h" | ||
| 19 | + | ||
| 20 | +#ifdef __GNUC__ | ||
| 21 | +RAPIDJSON_DIAG_PUSH | ||
| 22 | +RAPIDJSON_DIAG_OFF(effc++) | ||
| 23 | +#endif | ||
| 24 | + | ||
| 25 | +#if defined(__clang__) | ||
| 26 | +RAPIDJSON_DIAG_PUSH | ||
| 27 | +RAPIDJSON_DIAG_OFF(c++98-compat) | ||
| 28 | +#endif | ||
| 29 | + | ||
| 30 | +RAPIDJSON_NAMESPACE_BEGIN | ||
| 31 | + | ||
| 32 | +//! Combination of PrettyWriter format flags. | ||
| 33 | +/*! \see PrettyWriter::SetFormatOptions | ||
| 34 | + */ | ||
| 35 | +enum PrettyFormatOptions { | ||
| 36 | + kFormatDefault = 0, //!< Default pretty formatting. | ||
| 37 | + kFormatSingleLineArray = 1 //!< Format arrays on a single line. | ||
| 38 | +}; | ||
| 39 | + | ||
| 40 | +//! Writer with indentation and spacing. | ||
| 41 | +/*! | ||
| 42 | + \tparam OutputStream Type of ouptut os. | ||
| 43 | + \tparam SourceEncoding Encoding of source string. | ||
| 44 | + \tparam TargetEncoding Encoding of output stream. | ||
| 45 | + \tparam StackAllocator Type of allocator for allocating memory of stack. | ||
| 46 | +*/ | ||
| 47 | +template<typename OutputStream, typename SourceEncoding = UTF8<>, typename TargetEncoding = UTF8<>, typename StackAllocator = CrtAllocator, unsigned writeFlags = kWriteDefaultFlags> | ||
| 48 | +class PrettyWriter : public Writer<OutputStream, SourceEncoding, TargetEncoding, StackAllocator, writeFlags> { | ||
| 49 | +public: | ||
| 50 | + typedef Writer<OutputStream, SourceEncoding, TargetEncoding, StackAllocator, writeFlags> Base; | ||
| 51 | + typedef typename Base::Ch Ch; | ||
| 52 | + | ||
| 53 | + //! Constructor | ||
| 54 | + /*! \param os Output stream. | ||
| 55 | + \param allocator User supplied allocator. If it is null, it will create a private one. | ||
| 56 | + \param levelDepth Initial capacity of stack. | ||
| 57 | + */ | ||
| 58 | + explicit PrettyWriter(OutputStream& os, StackAllocator* allocator = 0, size_t levelDepth = Base::kDefaultLevelDepth) : | ||
| 59 | + Base(os, allocator, levelDepth), indentChar_(' '), indentCharCount_(4), formatOptions_(kFormatDefault) {} | ||
| 60 | + | ||
| 61 | + | ||
| 62 | + explicit PrettyWriter(StackAllocator* allocator = 0, size_t levelDepth = Base::kDefaultLevelDepth) : | ||
| 63 | + Base(allocator, levelDepth), indentChar_(' '), indentCharCount_(4) {} | ||
| 64 | + | ||
| 65 | +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS | ||
| 66 | + PrettyWriter(PrettyWriter&& rhs) : | ||
| 67 | + Base(std::forward<PrettyWriter>(rhs)), indentChar_(rhs.indentChar_), indentCharCount_(rhs.indentCharCount_), formatOptions_(rhs.formatOptions_) {} | ||
| 68 | +#endif | ||
| 69 | + | ||
| 70 | + //! Set custom indentation. | ||
| 71 | + /*! \param indentChar Character for indentation. Must be whitespace character (' ', '\\t', '\\n', '\\r'). | ||
| 72 | + \param indentCharCount Number of indent characters for each indentation level. | ||
| 73 | + \note The default indentation is 4 spaces. | ||
| 74 | + */ | ||
| 75 | + PrettyWriter& SetIndent(Ch indentChar, unsigned indentCharCount) { | ||
| 76 | + RAPIDJSON_ASSERT(indentChar == ' ' || indentChar == '\t' || indentChar == '\n' || indentChar == '\r'); | ||
| 77 | + indentChar_ = indentChar; | ||
| 78 | + indentCharCount_ = indentCharCount; | ||
| 79 | + return *this; | ||
| 80 | + } | ||
| 81 | + | ||
| 82 | + //! Set pretty writer formatting options. | ||
| 83 | + /*! \param options Formatting options. | ||
| 84 | + */ | ||
| 85 | + PrettyWriter& SetFormatOptions(PrettyFormatOptions options) { | ||
| 86 | + formatOptions_ = options; | ||
| 87 | + return *this; | ||
| 88 | + } | ||
| 89 | + | ||
| 90 | + /*! @name Implementation of Handler | ||
| 91 | + \see Handler | ||
| 92 | + */ | ||
| 93 | + //@{ | ||
| 94 | + | ||
| 95 | + bool Null() { PrettyPrefix(kNullType); return Base::WriteNull(); } | ||
| 96 | + bool Bool(bool b) { PrettyPrefix(b ? kTrueType : kFalseType); return Base::WriteBool(b); } | ||
| 97 | + bool Int(int i) { PrettyPrefix(kNumberType); return Base::WriteInt(i); } | ||
| 98 | + bool Uint(unsigned u) { PrettyPrefix(kNumberType); return Base::WriteUint(u); } | ||
| 99 | + bool Int64(int64_t i64) { PrettyPrefix(kNumberType); return Base::WriteInt64(i64); } | ||
| 100 | + bool Uint64(uint64_t u64) { PrettyPrefix(kNumberType); return Base::WriteUint64(u64); } | ||
| 101 | + bool Double(double d) { PrettyPrefix(kNumberType); return Base::WriteDouble(d); } | ||
| 102 | + | ||
| 103 | + bool RawNumber(const Ch* str, SizeType length, bool copy = false) { | ||
| 104 | + RAPIDJSON_ASSERT(str != 0); | ||
| 105 | + (void)copy; | ||
| 106 | + PrettyPrefix(kNumberType); | ||
| 107 | + return Base::WriteString(str, length); | ||
| 108 | + } | ||
| 109 | + | ||
| 110 | + bool String(const Ch* str, SizeType length, bool copy = false) { | ||
| 111 | + RAPIDJSON_ASSERT(str != 0); | ||
| 112 | + (void)copy; | ||
| 113 | + PrettyPrefix(kStringType); | ||
| 114 | + return Base::WriteString(str, length); | ||
| 115 | + } | ||
| 116 | + | ||
| 117 | +#if RAPIDJSON_HAS_STDSTRING | ||
| 118 | + bool String(const std::basic_string<Ch>& str) { | ||
| 119 | + return String(str.data(), SizeType(str.size())); | ||
| 120 | + } | ||
| 121 | +#endif | ||
| 122 | + | ||
| 123 | + bool StartObject() { | ||
| 124 | + PrettyPrefix(kObjectType); | ||
| 125 | + new (Base::level_stack_.template Push<typename Base::Level>()) typename Base::Level(false); | ||
| 126 | + return Base::WriteStartObject(); | ||
| 127 | + } | ||
| 128 | + | ||
| 129 | + bool Key(const Ch* str, SizeType length, bool copy = false) { return String(str, length, copy); } | ||
| 130 | + | ||
| 131 | +#if RAPIDJSON_HAS_STDSTRING | ||
| 132 | + bool Key(const std::basic_string<Ch>& str) { | ||
| 133 | + return Key(str.data(), SizeType(str.size())); | ||
| 134 | + } | ||
| 135 | +#endif | ||
| 136 | + | ||
| 137 | + bool EndObject(SizeType memberCount = 0) { | ||
| 138 | + (void)memberCount; | ||
| 139 | + RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level)); // not inside an Object | ||
| 140 | + RAPIDJSON_ASSERT(!Base::level_stack_.template Top<typename Base::Level>()->inArray); // currently inside an Array, not Object | ||
| 141 | + RAPIDJSON_ASSERT(0 == Base::level_stack_.template Top<typename Base::Level>()->valueCount % 2); // Object has a Key without a Value | ||
| 142 | + | ||
| 143 | + bool empty = Base::level_stack_.template Pop<typename Base::Level>(1)->valueCount == 0; | ||
| 144 | + | ||
| 145 | + if (!empty) { | ||
| 146 | + Base::os_->Put('\n'); | ||
| 147 | + WriteIndent(); | ||
| 148 | + } | ||
| 149 | + bool ret = Base::WriteEndObject(); | ||
| 150 | + (void)ret; | ||
| 151 | + RAPIDJSON_ASSERT(ret == true); | ||
| 152 | + if (Base::level_stack_.Empty()) // end of json text | ||
| 153 | + Base::Flush(); | ||
| 154 | + return true; | ||
| 155 | + } | ||
| 156 | + | ||
| 157 | + bool StartArray() { | ||
| 158 | + PrettyPrefix(kArrayType); | ||
| 159 | + new (Base::level_stack_.template Push<typename Base::Level>()) typename Base::Level(true); | ||
| 160 | + return Base::WriteStartArray(); | ||
| 161 | + } | ||
| 162 | + | ||
| 163 | + bool EndArray(SizeType memberCount = 0) { | ||
| 164 | + (void)memberCount; | ||
| 165 | + RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level)); | ||
| 166 | + RAPIDJSON_ASSERT(Base::level_stack_.template Top<typename Base::Level>()->inArray); | ||
| 167 | + bool empty = Base::level_stack_.template Pop<typename Base::Level>(1)->valueCount == 0; | ||
| 168 | + | ||
| 169 | + if (!empty && !(formatOptions_ & kFormatSingleLineArray)) { | ||
| 170 | + Base::os_->Put('\n'); | ||
| 171 | + WriteIndent(); | ||
| 172 | + } | ||
| 173 | + bool ret = Base::WriteEndArray(); | ||
| 174 | + (void)ret; | ||
| 175 | + RAPIDJSON_ASSERT(ret == true); | ||
| 176 | + if (Base::level_stack_.Empty()) // end of json text | ||
| 177 | + Base::Flush(); | ||
| 178 | + return true; | ||
| 179 | + } | ||
| 180 | + | ||
| 181 | + //@} | ||
| 182 | + | ||
| 183 | + /*! @name Convenience extensions */ | ||
| 184 | + //@{ | ||
| 185 | + | ||
| 186 | + //! Simpler but slower overload. | ||
| 187 | + bool String(const Ch* str) { return String(str, internal::StrLen(str)); } | ||
| 188 | + bool Key(const Ch* str) { return Key(str, internal::StrLen(str)); } | ||
| 189 | + | ||
| 190 | + //@} | ||
| 191 | + | ||
| 192 | + //! Write a raw JSON value. | ||
| 193 | + /*! | ||
| 194 | + For user to write a stringified JSON as a value. | ||
| 195 | + | ||
| 196 | + \param json A well-formed JSON value. It should not contain null character within [0, length - 1] range. | ||
| 197 | + \param length Length of the json. | ||
| 198 | + \param type Type of the root of json. | ||
| 199 | + \note When using PrettyWriter::RawValue(), the result json may not be indented correctly. | ||
| 200 | + */ | ||
| 201 | + bool RawValue(const Ch* json, size_t length, Type type) { | ||
| 202 | + RAPIDJSON_ASSERT(json != 0); | ||
| 203 | + PrettyPrefix(type); | ||
| 204 | + return Base::WriteRawValue(json, length); | ||
| 205 | + } | ||
| 206 | + | ||
| 207 | +protected: | ||
| 208 | + void PrettyPrefix(Type type) { | ||
| 209 | + (void)type; | ||
| 210 | + if (Base::level_stack_.GetSize() != 0) { // this value is not at root | ||
| 211 | + typename Base::Level* level = Base::level_stack_.template Top<typename Base::Level>(); | ||
| 212 | + | ||
| 213 | + if (level->inArray) { | ||
| 214 | + if (level->valueCount > 0) { | ||
| 215 | + Base::os_->Put(','); // add comma if it is not the first element in array | ||
| 216 | + if (formatOptions_ & kFormatSingleLineArray) | ||
| 217 | + Base::os_->Put(' '); | ||
| 218 | + } | ||
| 219 | + | ||
| 220 | + if (!(formatOptions_ & kFormatSingleLineArray)) { | ||
| 221 | + Base::os_->Put('\n'); | ||
| 222 | + WriteIndent(); | ||
| 223 | + } | ||
| 224 | + } | ||
| 225 | + else { // in object | ||
| 226 | + if (level->valueCount > 0) { | ||
| 227 | + if (level->valueCount % 2 == 0) { | ||
| 228 | + Base::os_->Put(','); | ||
| 229 | + Base::os_->Put('\n'); | ||
| 230 | + } | ||
| 231 | + else { | ||
| 232 | + Base::os_->Put(':'); | ||
| 233 | + Base::os_->Put(' '); | ||
| 234 | + } | ||
| 235 | + } | ||
| 236 | + else | ||
| 237 | + Base::os_->Put('\n'); | ||
| 238 | + | ||
| 239 | + if (level->valueCount % 2 == 0) | ||
| 240 | + WriteIndent(); | ||
| 241 | + } | ||
| 242 | + if (!level->inArray && level->valueCount % 2 == 0) | ||
| 243 | + RAPIDJSON_ASSERT(type == kStringType); // if it's in object, then even number should be a name | ||
| 244 | + level->valueCount++; | ||
| 245 | + } | ||
| 246 | + else { | ||
| 247 | + RAPIDJSON_ASSERT(!Base::hasRoot_); // Should only has one and only one root. | ||
| 248 | + Base::hasRoot_ = true; | ||
| 249 | + } | ||
| 250 | + } | ||
| 251 | + | ||
| 252 | + void WriteIndent() { | ||
| 253 | + size_t count = (Base::level_stack_.GetSize() / sizeof(typename Base::Level)) * indentCharCount_; | ||
| 254 | + PutN(*Base::os_, static_cast<typename OutputStream::Ch>(indentChar_), count); | ||
| 255 | + } | ||
| 256 | + | ||
| 257 | + Ch indentChar_; | ||
| 258 | + unsigned indentCharCount_; | ||
| 259 | + PrettyFormatOptions formatOptions_; | ||
| 260 | + | ||
| 261 | +private: | ||
| 262 | + // Prohibit copy constructor & assignment operator. | ||
| 263 | + PrettyWriter(const PrettyWriter&); | ||
| 264 | + PrettyWriter& operator=(const PrettyWriter&); | ||
| 265 | +}; | ||
| 266 | + | ||
| 267 | +RAPIDJSON_NAMESPACE_END | ||
| 268 | + | ||
| 269 | +#if defined(__clang__) | ||
| 270 | +RAPIDJSON_DIAG_POP | ||
| 271 | +#endif | ||
| 272 | + | ||
| 273 | +#ifdef __GNUC__ | ||
| 274 | +RAPIDJSON_DIAG_POP | ||
| 275 | +#endif | ||
| 276 | + | ||
| 277 | +#endif // RAPIDJSON_RAPIDJSON_H_ |
PaperGraph/include/rapidjson/rapidjson.h
0 → 100644
| 1 | +// Tencent is pleased to support the open source community by making RapidJSON available. | ||
| 2 | +// | ||
| 3 | +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. | ||
| 4 | +// | ||
| 5 | +// Licensed under the MIT License (the "License"); you may not use this file except | ||
| 6 | +// in compliance with the License. You may obtain a copy of the License at | ||
| 7 | +// | ||
| 8 | +// http://opensource.org/licenses/MIT | ||
| 9 | +// | ||
| 10 | +// Unless required by applicable law or agreed to in writing, software distributed | ||
| 11 | +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR | ||
| 12 | +// CONDITIONS OF ANY KIND, either express or implied. See the License for the | ||
| 13 | +// specific language governing permissions and limitations under the License. | ||
| 14 | + | ||
| 15 | +#ifndef RAPIDJSON_RAPIDJSON_H_ | ||
| 16 | +#define RAPIDJSON_RAPIDJSON_H_ | ||
| 17 | + | ||
| 18 | +/*!\file rapidjson.h | ||
| 19 | + \brief common definitions and configuration | ||
| 20 | + | ||
| 21 | + \see RAPIDJSON_CONFIG | ||
| 22 | + */ | ||
| 23 | + | ||
| 24 | +/*! \defgroup RAPIDJSON_CONFIG RapidJSON configuration | ||
| 25 | + \brief Configuration macros for library features | ||
| 26 | + | ||
| 27 | + Some RapidJSON features are configurable to adapt the library to a wide | ||
| 28 | + variety of platforms, environments and usage scenarios. Most of the | ||
| 29 | + features can be configured in terms of overriden or predefined | ||
| 30 | + preprocessor macros at compile-time. | ||
| 31 | + | ||
| 32 | + Some additional customization is available in the \ref RAPIDJSON_ERRORS APIs. | ||
| 33 | + | ||
| 34 | + \note These macros should be given on the compiler command-line | ||
| 35 | + (where applicable) to avoid inconsistent values when compiling | ||
| 36 | + different translation units of a single application. | ||
| 37 | + */ | ||
| 38 | + | ||
| 39 | +#include <cstdlib> // malloc(), realloc(), free(), size_t | ||
| 40 | +#include <cstring> // memset(), memcpy(), memmove(), memcmp() | ||
| 41 | + | ||
| 42 | +/////////////////////////////////////////////////////////////////////////////// | ||
| 43 | +// RAPIDJSON_VERSION_STRING | ||
| 44 | +// | ||
| 45 | +// ALWAYS synchronize the following 3 macros with corresponding variables in /CMakeLists.txt. | ||
| 46 | +// | ||
| 47 | + | ||
| 48 | +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN | ||
| 49 | +// token stringification | ||
| 50 | +#define RAPIDJSON_STRINGIFY(x) RAPIDJSON_DO_STRINGIFY(x) | ||
| 51 | +#define RAPIDJSON_DO_STRINGIFY(x) #x | ||
| 52 | + | ||
| 53 | +// token concatenation | ||
| 54 | +#define RAPIDJSON_JOIN(X, Y) RAPIDJSON_DO_JOIN(X, Y) | ||
| 55 | +#define RAPIDJSON_DO_JOIN(X, Y) RAPIDJSON_DO_JOIN2(X, Y) | ||
| 56 | +#define RAPIDJSON_DO_JOIN2(X, Y) X##Y | ||
| 57 | +//!@endcond | ||
| 58 | + | ||
| 59 | +/*! \def RAPIDJSON_MAJOR_VERSION | ||
| 60 | + \ingroup RAPIDJSON_CONFIG | ||
| 61 | + \brief Major version of RapidJSON in integer. | ||
| 62 | +*/ | ||
| 63 | +/*! \def RAPIDJSON_MINOR_VERSION | ||
| 64 | + \ingroup RAPIDJSON_CONFIG | ||
| 65 | + \brief Minor version of RapidJSON in integer. | ||
| 66 | +*/ | ||
| 67 | +/*! \def RAPIDJSON_PATCH_VERSION | ||
| 68 | + \ingroup RAPIDJSON_CONFIG | ||
| 69 | + \brief Patch version of RapidJSON in integer. | ||
| 70 | +*/ | ||
| 71 | +/*! \def RAPIDJSON_VERSION_STRING | ||
| 72 | + \ingroup RAPIDJSON_CONFIG | ||
| 73 | + \brief Version of RapidJSON in "<major>.<minor>.<patch>" string format. | ||
| 74 | +*/ | ||
| 75 | +#define RAPIDJSON_MAJOR_VERSION 1 | ||
| 76 | +#define RAPIDJSON_MINOR_VERSION 1 | ||
| 77 | +#define RAPIDJSON_PATCH_VERSION 0 | ||
| 78 | +#define RAPIDJSON_VERSION_STRING \ | ||
| 79 | + RAPIDJSON_STRINGIFY(RAPIDJSON_MAJOR_VERSION.RAPIDJSON_MINOR_VERSION.RAPIDJSON_PATCH_VERSION) | ||
| 80 | + | ||
| 81 | +/////////////////////////////////////////////////////////////////////////////// | ||
| 82 | +// RAPIDJSON_NAMESPACE_(BEGIN|END) | ||
| 83 | +/*! \def RAPIDJSON_NAMESPACE | ||
| 84 | + \ingroup RAPIDJSON_CONFIG | ||
| 85 | + \brief provide custom rapidjson namespace | ||
| 86 | + | ||
| 87 | + In order to avoid symbol clashes and/or "One Definition Rule" errors | ||
| 88 | + between multiple inclusions of (different versions of) RapidJSON in | ||
| 89 | + a single binary, users can customize the name of the main RapidJSON | ||
| 90 | + namespace. | ||
| 91 | + | ||
| 92 | + In case of a single nesting level, defining \c RAPIDJSON_NAMESPACE | ||
| 93 | + to a custom name (e.g. \c MyRapidJSON) is sufficient. If multiple | ||
| 94 | + levels are needed, both \ref RAPIDJSON_NAMESPACE_BEGIN and \ref | ||
| 95 | + RAPIDJSON_NAMESPACE_END need to be defined as well: | ||
| 96 | + | ||
| 97 | + \code | ||
| 98 | + // in some .cpp file | ||
| 99 | + #define RAPIDJSON_NAMESPACE my::rapidjson | ||
| 100 | + #define RAPIDJSON_NAMESPACE_BEGIN namespace my { namespace rapidjson { | ||
| 101 | + #define RAPIDJSON_NAMESPACE_END } } | ||
| 102 | + #include "rapidjson/..." | ||
| 103 | + \endcode | ||
| 104 | + | ||
| 105 | + \see rapidjson | ||
| 106 | + */ | ||
| 107 | +/*! \def RAPIDJSON_NAMESPACE_BEGIN | ||
| 108 | + \ingroup RAPIDJSON_CONFIG | ||
| 109 | + \brief provide custom rapidjson namespace (opening expression) | ||
| 110 | + \see RAPIDJSON_NAMESPACE | ||
| 111 | +*/ | ||
| 112 | +/*! \def RAPIDJSON_NAMESPACE_END | ||
| 113 | + \ingroup RAPIDJSON_CONFIG | ||
| 114 | + \brief provide custom rapidjson namespace (closing expression) | ||
| 115 | + \see RAPIDJSON_NAMESPACE | ||
| 116 | +*/ | ||
| 117 | +#ifndef RAPIDJSON_NAMESPACE | ||
| 118 | +#define RAPIDJSON_NAMESPACE rapidjson | ||
| 119 | +#endif | ||
| 120 | +#ifndef RAPIDJSON_NAMESPACE_BEGIN | ||
| 121 | +#define RAPIDJSON_NAMESPACE_BEGIN namespace RAPIDJSON_NAMESPACE { | ||
| 122 | +#endif | ||
| 123 | +#ifndef RAPIDJSON_NAMESPACE_END | ||
| 124 | +#define RAPIDJSON_NAMESPACE_END } | ||
| 125 | +#endif | ||
| 126 | + | ||
| 127 | +/////////////////////////////////////////////////////////////////////////////// | ||
| 128 | +// RAPIDJSON_HAS_STDSTRING | ||
| 129 | + | ||
| 130 | +#ifndef RAPIDJSON_HAS_STDSTRING | ||
| 131 | +#ifdef RAPIDJSON_DOXYGEN_RUNNING | ||
| 132 | +#define RAPIDJSON_HAS_STDSTRING 1 // force generation of documentation | ||
| 133 | +#else | ||
| 134 | +#define RAPIDJSON_HAS_STDSTRING 0 // no std::string support by default | ||
| 135 | +#endif | ||
| 136 | +/*! \def RAPIDJSON_HAS_STDSTRING | ||
| 137 | + \ingroup RAPIDJSON_CONFIG | ||
| 138 | + \brief Enable RapidJSON support for \c std::string | ||
| 139 | + | ||
| 140 | + By defining this preprocessor symbol to \c 1, several convenience functions for using | ||
| 141 | + \ref rapidjson::GenericValue with \c std::string are enabled, especially | ||
| 142 | + for construction and comparison. | ||
| 143 | + | ||
| 144 | + \hideinitializer | ||
| 145 | +*/ | ||
| 146 | +#endif // !defined(RAPIDJSON_HAS_STDSTRING) | ||
| 147 | + | ||
| 148 | +#if RAPIDJSON_HAS_STDSTRING | ||
| 149 | +#include <string> | ||
| 150 | +#endif // RAPIDJSON_HAS_STDSTRING | ||
| 151 | + | ||
| 152 | +/////////////////////////////////////////////////////////////////////////////// | ||
| 153 | +// RAPIDJSON_NO_INT64DEFINE | ||
| 154 | + | ||
| 155 | +/*! \def RAPIDJSON_NO_INT64DEFINE | ||
| 156 | + \ingroup RAPIDJSON_CONFIG | ||
| 157 | + \brief Use external 64-bit integer types. | ||
| 158 | + | ||
| 159 | + RapidJSON requires the 64-bit integer types \c int64_t and \c uint64_t types | ||
| 160 | + to be available at global scope. | ||
| 161 | + | ||
| 162 | + If users have their own definition, define RAPIDJSON_NO_INT64DEFINE to | ||
| 163 | + prevent RapidJSON from defining its own types. | ||
| 164 | +*/ | ||
| 165 | +#ifndef RAPIDJSON_NO_INT64DEFINE | ||
| 166 | +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN | ||
| 167 | +#if defined(_MSC_VER) && (_MSC_VER < 1800) // Visual Studio 2013 | ||
| 168 | +#include "msinttypes/stdint.h" | ||
| 169 | +#include "msinttypes/inttypes.h" | ||
| 170 | +#else | ||
| 171 | +// Other compilers should have this. | ||
| 172 | +#include <stdint.h> | ||
| 173 | +#include <inttypes.h> | ||
| 174 | +#endif | ||
| 175 | +//!@endcond | ||
| 176 | +#ifdef RAPIDJSON_DOXYGEN_RUNNING | ||
| 177 | +#define RAPIDJSON_NO_INT64DEFINE | ||
| 178 | +#endif | ||
| 179 | +#endif // RAPIDJSON_NO_INT64TYPEDEF | ||
| 180 | + | ||
| 181 | +/////////////////////////////////////////////////////////////////////////////// | ||
| 182 | +// RAPIDJSON_FORCEINLINE | ||
| 183 | + | ||
| 184 | +#ifndef RAPIDJSON_FORCEINLINE | ||
| 185 | +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN | ||
| 186 | +#if defined(_MSC_VER) && defined(NDEBUG) | ||
| 187 | +#define RAPIDJSON_FORCEINLINE __forceinline | ||
| 188 | +#elif defined(__GNUC__) && __GNUC__ >= 4 && defined(NDEBUG) | ||
| 189 | +#define RAPIDJSON_FORCEINLINE __attribute__((always_inline)) | ||
| 190 | +#else | ||
| 191 | +#define RAPIDJSON_FORCEINLINE | ||
| 192 | +#endif | ||
| 193 | +//!@endcond | ||
| 194 | +#endif // RAPIDJSON_FORCEINLINE | ||
| 195 | + | ||
| 196 | +/////////////////////////////////////////////////////////////////////////////// | ||
| 197 | +// RAPIDJSON_ENDIAN | ||
| 198 | +#define RAPIDJSON_LITTLEENDIAN 0 //!< Little endian machine | ||
| 199 | +#define RAPIDJSON_BIGENDIAN 1 //!< Big endian machine | ||
| 200 | + | ||
| 201 | +//! Endianness of the machine. | ||
| 202 | +/*! | ||
| 203 | + \def RAPIDJSON_ENDIAN | ||
| 204 | + \ingroup RAPIDJSON_CONFIG | ||
| 205 | + | ||
| 206 | + GCC 4.6 provided macro for detecting endianness of the target machine. But other | ||
| 207 | + compilers may not have this. User can define RAPIDJSON_ENDIAN to either | ||
| 208 | + \ref RAPIDJSON_LITTLEENDIAN or \ref RAPIDJSON_BIGENDIAN. | ||
| 209 | + | ||
| 210 | + Default detection implemented with reference to | ||
| 211 | + \li https://gcc.gnu.org/onlinedocs/gcc-4.6.0/cpp/Common-Predefined-Macros.html | ||
| 212 | + \li http://www.boost.org/doc/libs/1_42_0/boost/detail/endian.hpp | ||
| 213 | +*/ | ||
| 214 | +#ifndef RAPIDJSON_ENDIAN | ||
| 215 | +// Detect with GCC 4.6's macro | ||
| 216 | +# ifdef __BYTE_ORDER__ | ||
| 217 | +# if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ | ||
| 218 | +# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN | ||
| 219 | +# elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ | ||
| 220 | +# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN | ||
| 221 | +# else | ||
| 222 | +# error Unknown machine endianess detected. User needs to define RAPIDJSON_ENDIAN. | ||
| 223 | +# endif // __BYTE_ORDER__ | ||
| 224 | +// Detect with GLIBC's endian.h | ||
| 225 | +# elif defined(__GLIBC__) | ||
| 226 | +# include <endian.h> | ||
| 227 | +# if (__BYTE_ORDER == __LITTLE_ENDIAN) | ||
| 228 | +# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN | ||
| 229 | +# elif (__BYTE_ORDER == __BIG_ENDIAN) | ||
| 230 | +# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN | ||
| 231 | +# else | ||
| 232 | +# error Unknown machine endianess detected. User needs to define RAPIDJSON_ENDIAN. | ||
| 233 | +# endif // __GLIBC__ | ||
| 234 | +// Detect with _LITTLE_ENDIAN and _BIG_ENDIAN macro | ||
| 235 | +# elif defined(_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN) | ||
| 236 | +# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN | ||
| 237 | +# elif defined(_BIG_ENDIAN) && !defined(_LITTLE_ENDIAN) | ||
| 238 | +# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN | ||
| 239 | +// Detect with architecture macros | ||
| 240 | +# elif defined(__sparc) || defined(__sparc__) || defined(_POWER) || defined(__powerpc__) || defined(__ppc__) || defined(__hpux) || defined(__hppa) || defined(_MIPSEB) || defined(_POWER) || defined(__s390__) | ||
| 241 | +# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN | ||
| 242 | +# elif defined(__i386__) || defined(__alpha__) || defined(__ia64) || defined(__ia64__) || defined(_M_IX86) || defined(_M_IA64) || defined(_M_ALPHA) || defined(__amd64) || defined(__amd64__) || defined(_M_AMD64) || defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || defined(__bfin__) | ||
| 243 | +# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN | ||
| 244 | +# elif defined(_MSC_VER) && defined(_M_ARM) | ||
| 245 | +# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN | ||
| 246 | +# elif defined(RAPIDJSON_DOXYGEN_RUNNING) | ||
| 247 | +# define RAPIDJSON_ENDIAN | ||
| 248 | +# else | ||
| 249 | +# error Unknown machine endianess detected. User needs to define RAPIDJSON_ENDIAN. | ||
| 250 | +# endif | ||
| 251 | +#endif // RAPIDJSON_ENDIAN | ||
| 252 | + | ||
| 253 | +/////////////////////////////////////////////////////////////////////////////// | ||
| 254 | +// RAPIDJSON_64BIT | ||
| 255 | + | ||
| 256 | +//! Whether using 64-bit architecture | ||
| 257 | +#ifndef RAPIDJSON_64BIT | ||
| 258 | +#if defined(__LP64__) || (defined(__x86_64__) && defined(__ILP32__)) || defined(_WIN64) || defined(__EMSCRIPTEN__) | ||
| 259 | +#define RAPIDJSON_64BIT 1 | ||
| 260 | +#else | ||
| 261 | +#define RAPIDJSON_64BIT 0 | ||
| 262 | +#endif | ||
| 263 | +#endif // RAPIDJSON_64BIT | ||
| 264 | + | ||
| 265 | +/////////////////////////////////////////////////////////////////////////////// | ||
| 266 | +// RAPIDJSON_ALIGN | ||
| 267 | + | ||
| 268 | +//! Data alignment of the machine. | ||
| 269 | +/*! \ingroup RAPIDJSON_CONFIG | ||
| 270 | + \param x pointer to align | ||
| 271 | + | ||
| 272 | + Some machines require strict data alignment. Currently the default uses 4 bytes | ||
| 273 | + alignment on 32-bit platforms and 8 bytes alignment for 64-bit platforms. | ||
| 274 | + User can customize by defining the RAPIDJSON_ALIGN function macro. | ||
| 275 | +*/ | ||
| 276 | +#ifndef RAPIDJSON_ALIGN | ||
| 277 | +#if RAPIDJSON_64BIT == 1 | ||
| 278 | +#define RAPIDJSON_ALIGN(x) (((x) + static_cast<uint64_t>(7u)) & ~static_cast<uint64_t>(7u)) | ||
| 279 | +#else | ||
| 280 | +#define RAPIDJSON_ALIGN(x) (((x) + 3u) & ~3u) | ||
| 281 | +#endif | ||
| 282 | +#endif | ||
| 283 | + | ||
| 284 | +/////////////////////////////////////////////////////////////////////////////// | ||
| 285 | +// RAPIDJSON_UINT64_C2 | ||
| 286 | + | ||
| 287 | +//! Construct a 64-bit literal by a pair of 32-bit integer. | ||
| 288 | +/*! | ||
| 289 | + 64-bit literal with or without ULL suffix is prone to compiler warnings. | ||
| 290 | + UINT64_C() is C macro which cause compilation problems. | ||
| 291 | + Use this macro to define 64-bit constants by a pair of 32-bit integer. | ||
| 292 | +*/ | ||
| 293 | +#ifndef RAPIDJSON_UINT64_C2 | ||
| 294 | +#define RAPIDJSON_UINT64_C2(high32, low32) ((static_cast<uint64_t>(high32) << 32) | static_cast<uint64_t>(low32)) | ||
| 295 | +#endif | ||
| 296 | + | ||
| 297 | +/////////////////////////////////////////////////////////////////////////////// | ||
| 298 | +// RAPIDJSON_48BITPOINTER_OPTIMIZATION | ||
| 299 | + | ||
| 300 | +//! Use only lower 48-bit address for some pointers. | ||
| 301 | +/*! | ||
| 302 | + \ingroup RAPIDJSON_CONFIG | ||
| 303 | + | ||
| 304 | + This optimization uses the fact that current X86-64 architecture only implement lower 48-bit virtual address. | ||
| 305 | + The higher 16-bit can be used for storing other data. | ||
| 306 | + \c GenericValue uses this optimization to reduce its size form 24 bytes to 16 bytes in 64-bit architecture. | ||
| 307 | +*/ | ||
| 308 | +#ifndef RAPIDJSON_48BITPOINTER_OPTIMIZATION | ||
| 309 | +#if defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || defined(_M_X64) || defined(_M_AMD64) | ||
| 310 | +#define RAPIDJSON_48BITPOINTER_OPTIMIZATION 1 | ||
| 311 | +#else | ||
| 312 | +#define RAPIDJSON_48BITPOINTER_OPTIMIZATION 0 | ||
| 313 | +#endif | ||
| 314 | +#endif // RAPIDJSON_48BITPOINTER_OPTIMIZATION | ||
| 315 | + | ||
| 316 | +#if RAPIDJSON_48BITPOINTER_OPTIMIZATION == 1 | ||
| 317 | +#if RAPIDJSON_64BIT != 1 | ||
| 318 | +#error RAPIDJSON_48BITPOINTER_OPTIMIZATION can only be set to 1 when RAPIDJSON_64BIT=1 | ||
| 319 | +#endif | ||
| 320 | +#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)))) | ||
| 321 | +#define RAPIDJSON_GETPOINTER(type, p) (reinterpret_cast<type *>(reinterpret_cast<uintptr_t>(p) & static_cast<uintptr_t>(RAPIDJSON_UINT64_C2(0x0000FFFF, 0xFFFFFFFF)))) | ||
| 322 | +#else | ||
| 323 | +#define RAPIDJSON_SETPOINTER(type, p, x) (p = (x)) | ||
| 324 | +#define RAPIDJSON_GETPOINTER(type, p) (p) | ||
| 325 | +#endif | ||
| 326 | + | ||
| 327 | +/////////////////////////////////////////////////////////////////////////////// | ||
| 328 | +// RAPIDJSON_SSE2/RAPIDJSON_SSE42/RAPIDJSON_NEON/RAPIDJSON_SIMD | ||
| 329 | + | ||
| 330 | +/*! \def RAPIDJSON_SIMD | ||
| 331 | + \ingroup RAPIDJSON_CONFIG | ||
| 332 | + \brief Enable SSE2/SSE4.2/Neon optimization. | ||
| 333 | + | ||
| 334 | + RapidJSON supports optimized implementations for some parsing operations | ||
| 335 | + based on the SSE2, SSE4.2 or NEon SIMD extensions on modern Intel | ||
| 336 | + or ARM compatible processors. | ||
| 337 | + | ||
| 338 | + To enable these optimizations, three different symbols can be defined; | ||
| 339 | + \code | ||
| 340 | + // Enable SSE2 optimization. | ||
| 341 | + #define RAPIDJSON_SSE2 | ||
| 342 | + | ||
| 343 | + // Enable SSE4.2 optimization. | ||
| 344 | + #define RAPIDJSON_SSE42 | ||
| 345 | + \endcode | ||
| 346 | + | ||
| 347 | + // Enable ARM Neon optimization. | ||
| 348 | + #define RAPIDJSON_NEON | ||
| 349 | + \endcode | ||
| 350 | + | ||
| 351 | + \c RAPIDJSON_SSE42 takes precedence over SSE2, if both are defined. | ||
| 352 | + | ||
| 353 | + If any of these symbols is defined, RapidJSON defines the macro | ||
| 354 | + \c RAPIDJSON_SIMD to indicate the availability of the optimized code. | ||
| 355 | +*/ | ||
| 356 | +#if defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42) \ | ||
| 357 | + || defined(RAPIDJSON_NEON) || defined(RAPIDJSON_DOXYGEN_RUNNING) | ||
| 358 | +#define RAPIDJSON_SIMD | ||
| 359 | +#endif | ||
| 360 | + | ||
| 361 | +/////////////////////////////////////////////////////////////////////////////// | ||
| 362 | +// RAPIDJSON_NO_SIZETYPEDEFINE | ||
| 363 | + | ||
| 364 | +#ifndef RAPIDJSON_NO_SIZETYPEDEFINE | ||
| 365 | +/*! \def RAPIDJSON_NO_SIZETYPEDEFINE | ||
| 366 | + \ingroup RAPIDJSON_CONFIG | ||
| 367 | + \brief User-provided \c SizeType definition. | ||
| 368 | + | ||
| 369 | + In order to avoid using 32-bit size types for indexing strings and arrays, | ||
| 370 | + define this preprocessor symbol and provide the type rapidjson::SizeType | ||
| 371 | + before including RapidJSON: | ||
| 372 | + \code | ||
| 373 | + #define RAPIDJSON_NO_SIZETYPEDEFINE | ||
| 374 | + namespace rapidjson { typedef ::std::size_t SizeType; } | ||
| 375 | + #include "rapidjson/..." | ||
| 376 | + \endcode | ||
| 377 | + | ||
| 378 | + \see rapidjson::SizeType | ||
| 379 | +*/ | ||
| 380 | +#ifdef RAPIDJSON_DOXYGEN_RUNNING | ||
| 381 | +#define RAPIDJSON_NO_SIZETYPEDEFINE | ||
| 382 | +#endif | ||
| 383 | +RAPIDJSON_NAMESPACE_BEGIN | ||
| 384 | +//! Size type (for string lengths, array sizes, etc.) | ||
| 385 | +/*! RapidJSON uses 32-bit array/string indices even on 64-bit platforms, | ||
| 386 | + instead of using \c size_t. Users may override the SizeType by defining | ||
| 387 | + \ref RAPIDJSON_NO_SIZETYPEDEFINE. | ||
| 388 | +*/ | ||
| 389 | +typedef unsigned SizeType; | ||
| 390 | +RAPIDJSON_NAMESPACE_END | ||
| 391 | +#endif | ||
| 392 | + | ||
| 393 | +// always import std::size_t to rapidjson namespace | ||
| 394 | +RAPIDJSON_NAMESPACE_BEGIN | ||
| 395 | +using std::size_t; | ||
| 396 | +RAPIDJSON_NAMESPACE_END | ||
| 397 | + | ||
| 398 | +/////////////////////////////////////////////////////////////////////////////// | ||
| 399 | +// RAPIDJSON_ASSERT | ||
| 400 | + | ||
| 401 | +//! Assertion. | ||
| 402 | +/*! \ingroup RAPIDJSON_CONFIG | ||
| 403 | + By default, rapidjson uses C \c assert() for internal assertions. | ||
| 404 | + User can override it by defining RAPIDJSON_ASSERT(x) macro. | ||
| 405 | + | ||
| 406 | + \note Parsing errors are handled and can be customized by the | ||
| 407 | + \ref RAPIDJSON_ERRORS APIs. | ||
| 408 | +*/ | ||
| 409 | +#ifndef RAPIDJSON_ASSERT | ||
| 410 | +#include <cassert> | ||
| 411 | +#define RAPIDJSON_ASSERT(x) assert(x) | ||
| 412 | +#endif // RAPIDJSON_ASSERT | ||
| 413 | + | ||
| 414 | +/////////////////////////////////////////////////////////////////////////////// | ||
| 415 | +// RAPIDJSON_STATIC_ASSERT | ||
| 416 | + | ||
| 417 | +// Prefer C++11 static_assert, if available | ||
| 418 | +#ifndef RAPIDJSON_STATIC_ASSERT | ||
| 419 | +#if __cplusplus >= 201103L || ( defined(_MSC_VER) && _MSC_VER >= 1800 ) | ||
| 420 | +#define RAPIDJSON_STATIC_ASSERT(x) \ | ||
| 421 | + static_assert(x, RAPIDJSON_STRINGIFY(x)) | ||
| 422 | +#endif // C++11 | ||
| 423 | +#endif // RAPIDJSON_STATIC_ASSERT | ||
| 424 | + | ||
| 425 | +// Adopt C++03 implementation from boost | ||
| 426 | +#ifndef RAPIDJSON_STATIC_ASSERT | ||
| 427 | +#ifndef __clang__ | ||
| 428 | +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN | ||
| 429 | +#endif | ||
| 430 | +RAPIDJSON_NAMESPACE_BEGIN | ||
| 431 | +template <bool x> struct STATIC_ASSERTION_FAILURE; | ||
| 432 | +template <> struct STATIC_ASSERTION_FAILURE<true> { enum { value = 1 }; }; | ||
| 433 | +template <size_t x> struct StaticAssertTest {}; | ||
| 434 | +RAPIDJSON_NAMESPACE_END | ||
| 435 | + | ||
| 436 | +#if defined(__GNUC__) | ||
| 437 | +#define RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE __attribute__((unused)) | ||
| 438 | +#else | ||
| 439 | +#define RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE | ||
| 440 | +#endif | ||
| 441 | +#ifndef __clang__ | ||
| 442 | +//!@endcond | ||
| 443 | +#endif | ||
| 444 | + | ||
| 445 | +/*! \def RAPIDJSON_STATIC_ASSERT | ||
| 446 | + \brief (Internal) macro to check for conditions at compile-time | ||
| 447 | + \param x compile-time condition | ||
| 448 | + \hideinitializer | ||
| 449 | + */ | ||
| 450 | +#define RAPIDJSON_STATIC_ASSERT(x) \ | ||
| 451 | + typedef ::RAPIDJSON_NAMESPACE::StaticAssertTest< \ | ||
| 452 | + sizeof(::RAPIDJSON_NAMESPACE::STATIC_ASSERTION_FAILURE<bool(x) >)> \ | ||
| 453 | + RAPIDJSON_JOIN(StaticAssertTypedef, __LINE__) RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE | ||
| 454 | +#endif // RAPIDJSON_STATIC_ASSERT | ||
| 455 | + | ||
| 456 | +/////////////////////////////////////////////////////////////////////////////// | ||
| 457 | +// RAPIDJSON_LIKELY, RAPIDJSON_UNLIKELY | ||
| 458 | + | ||
| 459 | +//! Compiler branching hint for expression with high probability to be true. | ||
| 460 | +/*! | ||
| 461 | + \ingroup RAPIDJSON_CONFIG | ||
| 462 | + \param x Boolean expression likely to be true. | ||
| 463 | +*/ | ||
| 464 | +#ifndef RAPIDJSON_LIKELY | ||
| 465 | +#if defined(__GNUC__) || defined(__clang__) | ||
| 466 | +#define RAPIDJSON_LIKELY(x) __builtin_expect(!!(x), 1) | ||
| 467 | +#else | ||
| 468 | +#define RAPIDJSON_LIKELY(x) (x) | ||
| 469 | +#endif | ||
| 470 | +#endif | ||
| 471 | + | ||
| 472 | +//! Compiler branching hint for expression with low probability to be true. | ||
| 473 | +/*! | ||
| 474 | + \ingroup RAPIDJSON_CONFIG | ||
| 475 | + \param x Boolean expression unlikely to be true. | ||
| 476 | +*/ | ||
| 477 | +#ifndef RAPIDJSON_UNLIKELY | ||
| 478 | +#if defined(__GNUC__) || defined(__clang__) | ||
| 479 | +#define RAPIDJSON_UNLIKELY(x) __builtin_expect(!!(x), 0) | ||
| 480 | +#else | ||
| 481 | +#define RAPIDJSON_UNLIKELY(x) (x) | ||
| 482 | +#endif | ||
| 483 | +#endif | ||
| 484 | + | ||
| 485 | +/////////////////////////////////////////////////////////////////////////////// | ||
| 486 | +// Helpers | ||
| 487 | + | ||
| 488 | +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN | ||
| 489 | + | ||
| 490 | +#define RAPIDJSON_MULTILINEMACRO_BEGIN do { | ||
| 491 | +#define RAPIDJSON_MULTILINEMACRO_END \ | ||
| 492 | +} while((void)0, 0) | ||
| 493 | + | ||
| 494 | +// adopted from Boost | ||
| 495 | +#define RAPIDJSON_VERSION_CODE(x,y,z) \ | ||
| 496 | + (((x)*100000) + ((y)*100) + (z)) | ||
| 497 | + | ||
| 498 | +/////////////////////////////////////////////////////////////////////////////// | ||
| 499 | +// RAPIDJSON_DIAG_PUSH/POP, RAPIDJSON_DIAG_OFF | ||
| 500 | + | ||
| 501 | +#if defined(__GNUC__) | ||
| 502 | +#define RAPIDJSON_GNUC \ | ||
| 503 | + RAPIDJSON_VERSION_CODE(__GNUC__,__GNUC_MINOR__,__GNUC_PATCHLEVEL__) | ||
| 504 | +#endif | ||
| 505 | + | ||
| 506 | +#if defined(__clang__) || (defined(RAPIDJSON_GNUC) && RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,2,0)) | ||
| 507 | + | ||
| 508 | +#define RAPIDJSON_PRAGMA(x) _Pragma(RAPIDJSON_STRINGIFY(x)) | ||
| 509 | +#define RAPIDJSON_DIAG_PRAGMA(x) RAPIDJSON_PRAGMA(GCC diagnostic x) | ||
| 510 | +#define RAPIDJSON_DIAG_OFF(x) \ | ||
| 511 | + RAPIDJSON_DIAG_PRAGMA(ignored RAPIDJSON_STRINGIFY(RAPIDJSON_JOIN(-W,x))) | ||
| 512 | + | ||
| 513 | +// push/pop support in Clang and GCC>=4.6 | ||
| 514 | +#if defined(__clang__) || (defined(RAPIDJSON_GNUC) && RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0)) | ||
| 515 | +#define RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_PRAGMA(push) | ||
| 516 | +#define RAPIDJSON_DIAG_POP RAPIDJSON_DIAG_PRAGMA(pop) | ||
| 517 | +#else // GCC >= 4.2, < 4.6 | ||
| 518 | +#define RAPIDJSON_DIAG_PUSH /* ignored */ | ||
| 519 | +#define RAPIDJSON_DIAG_POP /* ignored */ | ||
| 520 | +#endif | ||
| 521 | + | ||
| 522 | +#elif defined(_MSC_VER) | ||
| 523 | + | ||
| 524 | +// pragma (MSVC specific) | ||
| 525 | +#define RAPIDJSON_PRAGMA(x) __pragma(x) | ||
| 526 | +#define RAPIDJSON_DIAG_PRAGMA(x) RAPIDJSON_PRAGMA(warning(x)) | ||
| 527 | + | ||
| 528 | +#define RAPIDJSON_DIAG_OFF(x) RAPIDJSON_DIAG_PRAGMA(disable: x) | ||
| 529 | +#define RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_PRAGMA(push) | ||
| 530 | +#define RAPIDJSON_DIAG_POP RAPIDJSON_DIAG_PRAGMA(pop) | ||
| 531 | + | ||
| 532 | +#else | ||
| 533 | + | ||
| 534 | +#define RAPIDJSON_DIAG_OFF(x) /* ignored */ | ||
| 535 | +#define RAPIDJSON_DIAG_PUSH /* ignored */ | ||
| 536 | +#define RAPIDJSON_DIAG_POP /* ignored */ | ||
| 537 | + | ||
| 538 | +#endif // RAPIDJSON_DIAG_* | ||
| 539 | + | ||
| 540 | +/////////////////////////////////////////////////////////////////////////////// | ||
| 541 | +// C++11 features | ||
| 542 | + | ||
| 543 | +#ifndef RAPIDJSON_HAS_CXX11_RVALUE_REFS | ||
| 544 | +#if defined(__clang__) | ||
| 545 | +#if __has_feature(cxx_rvalue_references) && \ | ||
| 546 | + (defined(_LIBCPP_VERSION) || defined(__GLIBCXX__) && __GLIBCXX__ >= 20080306) | ||
| 547 | +#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 1 | ||
| 548 | +#else | ||
| 549 | +#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 0 | ||
| 550 | +#endif | ||
| 551 | +#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,3,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \ | ||
| 552 | + (defined(_MSC_VER) && _MSC_VER >= 1600) | ||
| 553 | + | ||
| 554 | +#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 1 | ||
| 555 | +#else | ||
| 556 | +#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 0 | ||
| 557 | +#endif | ||
| 558 | +#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS | ||
| 559 | + | ||
| 560 | +#ifndef RAPIDJSON_HAS_CXX11_NOEXCEPT | ||
| 561 | +#if defined(__clang__) | ||
| 562 | +#define RAPIDJSON_HAS_CXX11_NOEXCEPT __has_feature(cxx_noexcept) | ||
| 563 | +#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) | ||
| 564 | +// (defined(_MSC_VER) && _MSC_VER >= ????) // not yet supported | ||
| 565 | +#define RAPIDJSON_HAS_CXX11_NOEXCEPT 1 | ||
| 566 | +#else | ||
| 567 | +#define RAPIDJSON_HAS_CXX11_NOEXCEPT 0 | ||
| 568 | +#endif | ||
| 569 | +#endif | ||
| 570 | +#if RAPIDJSON_HAS_CXX11_NOEXCEPT | ||
| 571 | +#define RAPIDJSON_NOEXCEPT noexcept | ||
| 572 | +#else | ||
| 573 | +#define RAPIDJSON_NOEXCEPT /* noexcept */ | ||
| 574 | +#endif // RAPIDJSON_HAS_CXX11_NOEXCEPT | ||
| 575 | + | ||
| 576 | +// no automatic detection, yet | ||
| 577 | +#ifndef RAPIDJSON_HAS_CXX11_TYPETRAITS | ||
| 578 | +#define RAPIDJSON_HAS_CXX11_TYPETRAITS 0 | ||
| 579 | +#endif | ||
| 580 | + | ||
| 581 | +#ifndef RAPIDJSON_HAS_CXX11_RANGE_FOR | ||
| 582 | +#if defined(__clang__) | ||
| 583 | +#define RAPIDJSON_HAS_CXX11_RANGE_FOR __has_feature(cxx_range_for) | ||
| 584 | +#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \ | ||
| 585 | + (defined(_MSC_VER) && _MSC_VER >= 1700) | ||
| 586 | +#define RAPIDJSON_HAS_CXX11_RANGE_FOR 1 | ||
| 587 | +#else | ||
| 588 | +#define RAPIDJSON_HAS_CXX11_RANGE_FOR 0 | ||
| 589 | +#endif | ||
| 590 | +#endif // RAPIDJSON_HAS_CXX11_RANGE_FOR | ||
| 591 | + | ||
| 592 | +//!@endcond | ||
| 593 | + | ||
| 594 | +/////////////////////////////////////////////////////////////////////////////// | ||
| 595 | +// new/delete | ||
| 596 | + | ||
| 597 | +#ifndef RAPIDJSON_NEW | ||
| 598 | +///! customization point for global \c new | ||
| 599 | +#define RAPIDJSON_NEW(TypeName) new TypeName | ||
| 600 | +#endif | ||
| 601 | +#ifndef RAPIDJSON_DELETE | ||
| 602 | +///! customization point for global \c delete | ||
| 603 | +#define RAPIDJSON_DELETE(x) delete x | ||
| 604 | +#endif | ||
| 605 | + | ||
| 606 | +/////////////////////////////////////////////////////////////////////////////// | ||
| 607 | +// Type | ||
| 608 | + | ||
| 609 | +/*! \namespace rapidjson | ||
| 610 | + \brief main RapidJSON namespace | ||
| 611 | + \see RAPIDJSON_NAMESPACE | ||
| 612 | +*/ | ||
| 613 | +RAPIDJSON_NAMESPACE_BEGIN | ||
| 614 | + | ||
| 615 | +//! Type of JSON value | ||
| 616 | +enum Type { | ||
| 617 | + kNullType = 0, //!< null | ||
| 618 | + kFalseType = 1, //!< false | ||
| 619 | + kTrueType = 2, //!< true | ||
| 620 | + kObjectType = 3, //!< object | ||
| 621 | + kArrayType = 4, //!< array | ||
| 622 | + kStringType = 5, //!< string | ||
| 623 | + kNumberType = 6 //!< number | ||
| 624 | +}; | ||
| 625 | + | ||
| 626 | +RAPIDJSON_NAMESPACE_END | ||
| 627 | + | ||
| 628 | +#endif // RAPIDJSON_RAPIDJSON_H_ |
PaperGraph/include/rapidjson/reader.h
0 → 100644
| 1 | +// Tencent is pleased to support the open source community by making RapidJSON available. | ||
| 2 | +// | ||
| 3 | +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. | ||
| 4 | +// | ||
| 5 | +// Licensed under the MIT License (the "License"); you may not use this file except | ||
| 6 | +// in compliance with the License. You may obtain a copy of the License at | ||
| 7 | +// | ||
| 8 | +// http://opensource.org/licenses/MIT | ||
| 9 | +// | ||
| 10 | +// Unless required by applicable law or agreed to in writing, software distributed | ||
| 11 | +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR | ||
| 12 | +// CONDITIONS OF ANY KIND, either express or implied. See the License for the | ||
| 13 | +// specific language governing permissions and limitations under the License. | ||
| 14 | + | ||
| 15 | +#ifndef RAPIDJSON_READER_H_ | ||
| 16 | +#define RAPIDJSON_READER_H_ | ||
| 17 | + | ||
| 18 | +/*! \file reader.h */ | ||
| 19 | + | ||
| 20 | +#include "allocators.h" | ||
| 21 | +#include "stream.h" | ||
| 22 | +#include "encodedstream.h" | ||
| 23 | +#include "internal/meta.h" | ||
| 24 | +#include "internal/stack.h" | ||
| 25 | +#include "internal/strtod.h" | ||
| 26 | +#include <limits> | ||
| 27 | + | ||
| 28 | +#if defined(RAPIDJSON_SIMD) && defined(_MSC_VER) | ||
| 29 | +#include <intrin.h> | ||
| 30 | +#pragma intrinsic(_BitScanForward) | ||
| 31 | +#endif | ||
| 32 | +#ifdef RAPIDJSON_SSE42 | ||
| 33 | +#include <nmmintrin.h> | ||
| 34 | +#elif defined(RAPIDJSON_SSE2) | ||
| 35 | +#include <emmintrin.h> | ||
| 36 | +#elif defined(RAPIDJSON_NEON) | ||
| 37 | +#include <arm_neon.h> | ||
| 38 | +#endif | ||
| 39 | + | ||
| 40 | +#ifdef _MSC_VER | ||
| 41 | +RAPIDJSON_DIAG_PUSH | ||
| 42 | +RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant | ||
| 43 | +RAPIDJSON_DIAG_OFF(4702) // unreachable code | ||
| 44 | +#endif | ||
| 45 | + | ||
| 46 | +#ifdef __clang__ | ||
| 47 | +RAPIDJSON_DIAG_PUSH | ||
| 48 | +RAPIDJSON_DIAG_OFF(old-style-cast) | ||
| 49 | +RAPIDJSON_DIAG_OFF(padded) | ||
| 50 | +RAPIDJSON_DIAG_OFF(switch-enum) | ||
| 51 | +#endif | ||
| 52 | + | ||
| 53 | +#ifdef __GNUC__ | ||
| 54 | +RAPIDJSON_DIAG_PUSH | ||
| 55 | +RAPIDJSON_DIAG_OFF(effc++) | ||
| 56 | +#endif | ||
| 57 | + | ||
| 58 | +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN | ||
| 59 | +#define RAPIDJSON_NOTHING /* deliberately empty */ | ||
| 60 | +#ifndef RAPIDJSON_PARSE_ERROR_EARLY_RETURN | ||
| 61 | +#define RAPIDJSON_PARSE_ERROR_EARLY_RETURN(value) \ | ||
| 62 | + RAPIDJSON_MULTILINEMACRO_BEGIN \ | ||
| 63 | + if (RAPIDJSON_UNLIKELY(HasParseError())) { return value; } \ | ||
| 64 | + RAPIDJSON_MULTILINEMACRO_END | ||
| 65 | +#endif | ||
| 66 | +#define RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID \ | ||
| 67 | + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(RAPIDJSON_NOTHING) | ||
| 68 | +//!@endcond | ||
| 69 | + | ||
| 70 | +/*! \def RAPIDJSON_PARSE_ERROR_NORETURN | ||
| 71 | + \ingroup RAPIDJSON_ERRORS | ||
| 72 | + \brief Macro to indicate a parse error. | ||
| 73 | + \param parseErrorCode \ref rapidjson::ParseErrorCode of the error | ||
| 74 | + \param offset position of the error in JSON input (\c size_t) | ||
| 75 | + | ||
| 76 | + This macros can be used as a customization point for the internal | ||
| 77 | + error handling mechanism of RapidJSON. | ||
| 78 | + | ||
| 79 | + A common usage model is to throw an exception instead of requiring the | ||
| 80 | + caller to explicitly check the \ref rapidjson::GenericReader::Parse's | ||
| 81 | + return value: | ||
| 82 | + | ||
| 83 | + \code | ||
| 84 | + #define RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode,offset) \ | ||
| 85 | + throw ParseException(parseErrorCode, #parseErrorCode, offset) | ||
| 86 | + | ||
| 87 | + #include <stdexcept> // std::runtime_error | ||
| 88 | + #include "rapidjson/error/error.h" // rapidjson::ParseResult | ||
| 89 | + | ||
| 90 | + struct ParseException : std::runtime_error, rapidjson::ParseResult { | ||
| 91 | + ParseException(rapidjson::ParseErrorCode code, const char* msg, size_t offset) | ||
| 92 | + : std::runtime_error(msg), ParseResult(code, offset) {} | ||
| 93 | + }; | ||
| 94 | + | ||
| 95 | + #include "rapidjson/reader.h" | ||
| 96 | + \endcode | ||
| 97 | + | ||
| 98 | + \see RAPIDJSON_PARSE_ERROR, rapidjson::GenericReader::Parse | ||
| 99 | + */ | ||
| 100 | +#ifndef RAPIDJSON_PARSE_ERROR_NORETURN | ||
| 101 | +#define RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode, offset) \ | ||
| 102 | + RAPIDJSON_MULTILINEMACRO_BEGIN \ | ||
| 103 | + RAPIDJSON_ASSERT(!HasParseError()); /* Error can only be assigned once */ \ | ||
| 104 | + SetParseError(parseErrorCode, offset); \ | ||
| 105 | + RAPIDJSON_MULTILINEMACRO_END | ||
| 106 | +#endif | ||
| 107 | + | ||
| 108 | +/*! \def RAPIDJSON_PARSE_ERROR | ||
| 109 | + \ingroup RAPIDJSON_ERRORS | ||
| 110 | + \brief (Internal) macro to indicate and handle a parse error. | ||
| 111 | + \param parseErrorCode \ref rapidjson::ParseErrorCode of the error | ||
| 112 | + \param offset position of the error in JSON input (\c size_t) | ||
| 113 | + | ||
| 114 | + Invokes RAPIDJSON_PARSE_ERROR_NORETURN and stops the parsing. | ||
| 115 | + | ||
| 116 | + \see RAPIDJSON_PARSE_ERROR_NORETURN | ||
| 117 | + \hideinitializer | ||
| 118 | + */ | ||
| 119 | +#ifndef RAPIDJSON_PARSE_ERROR | ||
| 120 | +#define RAPIDJSON_PARSE_ERROR(parseErrorCode, offset) \ | ||
| 121 | + RAPIDJSON_MULTILINEMACRO_BEGIN \ | ||
| 122 | + RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode, offset); \ | ||
| 123 | + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; \ | ||
| 124 | + RAPIDJSON_MULTILINEMACRO_END | ||
| 125 | +#endif | ||
| 126 | + | ||
| 127 | +#include "error/error.h" // ParseErrorCode, ParseResult | ||
| 128 | + | ||
| 129 | +RAPIDJSON_NAMESPACE_BEGIN | ||
| 130 | + | ||
| 131 | +/////////////////////////////////////////////////////////////////////////////// | ||
| 132 | +// ParseFlag | ||
| 133 | + | ||
| 134 | +/*! \def RAPIDJSON_PARSE_DEFAULT_FLAGS | ||
| 135 | + \ingroup RAPIDJSON_CONFIG | ||
| 136 | + \brief User-defined kParseDefaultFlags definition. | ||
| 137 | + | ||
| 138 | + User can define this as any \c ParseFlag combinations. | ||
| 139 | +*/ | ||
| 140 | +#ifndef RAPIDJSON_PARSE_DEFAULT_FLAGS | ||
| 141 | +#define RAPIDJSON_PARSE_DEFAULT_FLAGS kParseNoFlags | ||
| 142 | +#endif | ||
| 143 | + | ||
| 144 | +//! Combination of parseFlags | ||
| 145 | +/*! \see Reader::Parse, Document::Parse, Document::ParseInsitu, Document::ParseStream | ||
| 146 | + */ | ||
| 147 | +enum ParseFlag { | ||
| 148 | + kParseNoFlags = 0, //!< No flags are set. | ||
| 149 | + kParseInsituFlag = 1, //!< In-situ(destructive) parsing. | ||
| 150 | + kParseValidateEncodingFlag = 2, //!< Validate encoding of JSON strings. | ||
| 151 | + kParseIterativeFlag = 4, //!< Iterative(constant complexity in terms of function call stack size) parsing. | ||
| 152 | + 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. | ||
| 153 | + kParseFullPrecisionFlag = 16, //!< Parse number in full precision (but slower). | ||
| 154 | + kParseCommentsFlag = 32, //!< Allow one-line (//) and multi-line (/**/) comments. | ||
| 155 | + kParseNumbersAsStringsFlag = 64, //!< Parse all numbers (ints/doubles) as strings. | ||
| 156 | + kParseTrailingCommasFlag = 128, //!< Allow trailing commas at the end of objects and arrays. | ||
| 157 | + kParseNanAndInfFlag = 256, //!< Allow parsing NaN, Inf, Infinity, -Inf and -Infinity as doubles. | ||
| 158 | + kParseDefaultFlags = RAPIDJSON_PARSE_DEFAULT_FLAGS //!< Default parse flags. Can be customized by defining RAPIDJSON_PARSE_DEFAULT_FLAGS | ||
| 159 | +}; | ||
| 160 | + | ||
| 161 | +/////////////////////////////////////////////////////////////////////////////// | ||
| 162 | +// Handler | ||
| 163 | + | ||
| 164 | +/*! \class rapidjson::Handler | ||
| 165 | + \brief Concept for receiving events from GenericReader upon parsing. | ||
| 166 | + The functions return true if no error occurs. If they return false, | ||
| 167 | + the event publisher should terminate the process. | ||
| 168 | +\code | ||
| 169 | +concept Handler { | ||
| 170 | + typename Ch; | ||
| 171 | + | ||
| 172 | + bool Null(); | ||
| 173 | + bool Bool(bool b); | ||
| 174 | + bool Int(int i); | ||
| 175 | + bool Uint(unsigned i); | ||
| 176 | + bool Int64(int64_t i); | ||
| 177 | + bool Uint64(uint64_t i); | ||
| 178 | + bool Double(double d); | ||
| 179 | + /// enabled via kParseNumbersAsStringsFlag, string is not null-terminated (use length) | ||
| 180 | + bool RawNumber(const Ch* str, SizeType length, bool copy); | ||
| 181 | + bool String(const Ch* str, SizeType length, bool copy); | ||
| 182 | + bool StartObject(); | ||
| 183 | + bool Key(const Ch* str, SizeType length, bool copy); | ||
| 184 | + bool EndObject(SizeType memberCount); | ||
| 185 | + bool StartArray(); | ||
| 186 | + bool EndArray(SizeType elementCount); | ||
| 187 | +}; | ||
| 188 | +\endcode | ||
| 189 | +*/ | ||
| 190 | +/////////////////////////////////////////////////////////////////////////////// | ||
| 191 | +// BaseReaderHandler | ||
| 192 | + | ||
| 193 | +//! Default implementation of Handler. | ||
| 194 | +/*! This can be used as base class of any reader handler. | ||
| 195 | + \note implements Handler concept | ||
| 196 | +*/ | ||
| 197 | +template<typename Encoding = UTF8<>, typename Derived = void> | ||
| 198 | +struct BaseReaderHandler { | ||
| 199 | + typedef typename Encoding::Ch Ch; | ||
| 200 | + | ||
| 201 | + typedef typename internal::SelectIf<internal::IsSame<Derived, void>, BaseReaderHandler, Derived>::Type Override; | ||
| 202 | + | ||
| 203 | + bool Default() { return true; } | ||
| 204 | + bool Null() { return static_cast<Override&>(*this).Default(); } | ||
| 205 | + bool Bool(bool) { return static_cast<Override&>(*this).Default(); } | ||
| 206 | + bool Int(int) { return static_cast<Override&>(*this).Default(); } | ||
| 207 | + bool Uint(unsigned) { return static_cast<Override&>(*this).Default(); } | ||
| 208 | + bool Int64(int64_t) { return static_cast<Override&>(*this).Default(); } | ||
| 209 | + bool Uint64(uint64_t) { return static_cast<Override&>(*this).Default(); } | ||
| 210 | + bool Double(double) { return static_cast<Override&>(*this).Default(); } | ||
| 211 | + /// enabled via kParseNumbersAsStringsFlag, string is not null-terminated (use length) | ||
| 212 | + bool RawNumber(const Ch* str, SizeType len, bool copy) { return static_cast<Override&>(*this).String(str, len, copy); } | ||
| 213 | + bool String(const Ch*, SizeType, bool) { return static_cast<Override&>(*this).Default(); } | ||
| 214 | + bool StartObject() { return static_cast<Override&>(*this).Default(); } | ||
| 215 | + bool Key(const Ch* str, SizeType len, bool copy) { return static_cast<Override&>(*this).String(str, len, copy); } | ||
| 216 | + bool EndObject(SizeType) { return static_cast<Override&>(*this).Default(); } | ||
| 217 | + bool StartArray() { return static_cast<Override&>(*this).Default(); } | ||
| 218 | + bool EndArray(SizeType) { return static_cast<Override&>(*this).Default(); } | ||
| 219 | +}; | ||
| 220 | + | ||
| 221 | +/////////////////////////////////////////////////////////////////////////////// | ||
| 222 | +// StreamLocalCopy | ||
| 223 | + | ||
| 224 | +namespace internal { | ||
| 225 | + | ||
| 226 | +template<typename Stream, int = StreamTraits<Stream>::copyOptimization> | ||
| 227 | +class StreamLocalCopy; | ||
| 228 | + | ||
| 229 | +//! Do copy optimization. | ||
| 230 | +template<typename Stream> | ||
| 231 | +class StreamLocalCopy<Stream, 1> { | ||
| 232 | +public: | ||
| 233 | + StreamLocalCopy(Stream& original) : s(original), original_(original) {} | ||
| 234 | + ~StreamLocalCopy() { original_ = s; } | ||
| 235 | + | ||
| 236 | + Stream s; | ||
| 237 | + | ||
| 238 | +private: | ||
| 239 | + StreamLocalCopy& operator=(const StreamLocalCopy&) /* = delete */; | ||
| 240 | + | ||
| 241 | + Stream& original_; | ||
| 242 | +}; | ||
| 243 | + | ||
| 244 | +//! Keep reference. | ||
| 245 | +template<typename Stream> | ||
| 246 | +class StreamLocalCopy<Stream, 0> { | ||
| 247 | +public: | ||
| 248 | + StreamLocalCopy(Stream& original) : s(original) {} | ||
| 249 | + | ||
| 250 | + Stream& s; | ||
| 251 | + | ||
| 252 | +private: | ||
| 253 | + StreamLocalCopy& operator=(const StreamLocalCopy&) /* = delete */; | ||
| 254 | +}; | ||
| 255 | + | ||
| 256 | +} // namespace internal | ||
| 257 | + | ||
| 258 | +/////////////////////////////////////////////////////////////////////////////// | ||
| 259 | +// SkipWhitespace | ||
| 260 | + | ||
| 261 | +//! Skip the JSON white spaces in a stream. | ||
| 262 | +/*! \param is A input stream for skipping white spaces. | ||
| 263 | + \note This function has SSE2/SSE4.2 specialization. | ||
| 264 | +*/ | ||
| 265 | +template<typename InputStream> | ||
| 266 | +void SkipWhitespace(InputStream& is) { | ||
| 267 | + internal::StreamLocalCopy<InputStream> copy(is); | ||
| 268 | + InputStream& s(copy.s); | ||
| 269 | + | ||
| 270 | + typename InputStream::Ch c; | ||
| 271 | + while ((c = s.Peek()) == ' ' || c == '\n' || c == '\r' || c == '\t') | ||
| 272 | + s.Take(); | ||
| 273 | +} | ||
| 274 | + | ||
| 275 | +inline const char* SkipWhitespace(const char* p, const char* end) { | ||
| 276 | + while (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')) | ||
| 277 | + ++p; | ||
| 278 | + return p; | ||
| 279 | +} | ||
| 280 | + | ||
| 281 | +#ifdef RAPIDJSON_SSE42 | ||
| 282 | +//! Skip whitespace with SSE 4.2 pcmpistrm instruction, testing 16 8-byte characters at once. | ||
| 283 | +inline const char *SkipWhitespace_SIMD(const char* p) { | ||
| 284 | + // Fast return for single non-whitespace | ||
| 285 | + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') | ||
| 286 | + ++p; | ||
| 287 | + else | ||
| 288 | + return p; | ||
| 289 | + | ||
| 290 | + // 16-byte align to the next boundary | ||
| 291 | + const char* nextAligned = reinterpret_cast<const char*>((reinterpret_cast<size_t>(p) + 15) & static_cast<size_t>(~15)); | ||
| 292 | + while (p != nextAligned) | ||
| 293 | + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') | ||
| 294 | + ++p; | ||
| 295 | + else | ||
| 296 | + return p; | ||
| 297 | + | ||
| 298 | + // The rest of string using SIMD | ||
| 299 | + static const char whitespace[16] = " \n\r\t"; | ||
| 300 | + const __m128i w = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&whitespace[0])); | ||
| 301 | + | ||
| 302 | + for (;; p += 16) { | ||
| 303 | + const __m128i s = _mm_load_si128(reinterpret_cast<const __m128i *>(p)); | ||
| 304 | + const int r = _mm_cmpistri(w, s, _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_LEAST_SIGNIFICANT | _SIDD_NEGATIVE_POLARITY); | ||
| 305 | + if (r != 16) // some of characters is non-whitespace | ||
| 306 | + return p + r; | ||
| 307 | + } | ||
| 308 | +} | ||
| 309 | + | ||
| 310 | +inline const char *SkipWhitespace_SIMD(const char* p, const char* end) { | ||
| 311 | + // Fast return for single non-whitespace | ||
| 312 | + if (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')) | ||
| 313 | + ++p; | ||
| 314 | + else | ||
| 315 | + return p; | ||
| 316 | + | ||
| 317 | + // The middle of string using SIMD | ||
| 318 | + static const char whitespace[16] = " \n\r\t"; | ||
| 319 | + const __m128i w = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&whitespace[0])); | ||
| 320 | + | ||
| 321 | + for (; p <= end - 16; p += 16) { | ||
| 322 | + const __m128i s = _mm_loadu_si128(reinterpret_cast<const __m128i *>(p)); | ||
| 323 | + const int r = _mm_cmpistri(w, s, _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_LEAST_SIGNIFICANT | _SIDD_NEGATIVE_POLARITY); | ||
| 324 | + if (r != 16) // some of characters is non-whitespace | ||
| 325 | + return p + r; | ||
| 326 | + } | ||
| 327 | + | ||
| 328 | + return SkipWhitespace(p, end); | ||
| 329 | +} | ||
| 330 | + | ||
| 331 | +#elif defined(RAPIDJSON_SSE2) | ||
| 332 | + | ||
| 333 | +//! Skip whitespace with SSE2 instructions, testing 16 8-byte characters at once. | ||
| 334 | +inline const char *SkipWhitespace_SIMD(const char* p) { | ||
| 335 | + // Fast return for single non-whitespace | ||
| 336 | + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') | ||
| 337 | + ++p; | ||
| 338 | + else | ||
| 339 | + return p; | ||
| 340 | + | ||
| 341 | + // 16-byte align to the next boundary | ||
| 342 | + const char* nextAligned = reinterpret_cast<const char*>((reinterpret_cast<size_t>(p) + 15) & static_cast<size_t>(~15)); | ||
| 343 | + while (p != nextAligned) | ||
| 344 | + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') | ||
| 345 | + ++p; | ||
| 346 | + else | ||
| 347 | + return p; | ||
| 348 | + | ||
| 349 | + // The rest of string | ||
| 350 | + #define C16(c) { c, c, c, c, c, c, c, c, c, c, c, c, c, c, c, c } | ||
| 351 | + static const char whitespaces[4][16] = { C16(' '), C16('\n'), C16('\r'), C16('\t') }; | ||
| 352 | + #undef C16 | ||
| 353 | + | ||
| 354 | + const __m128i w0 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&whitespaces[0][0])); | ||
| 355 | + const __m128i w1 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&whitespaces[1][0])); | ||
| 356 | + const __m128i w2 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&whitespaces[2][0])); | ||
| 357 | + const __m128i w3 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&whitespaces[3][0])); | ||
| 358 | + | ||
| 359 | + for (;; p += 16) { | ||
| 360 | + const __m128i s = _mm_load_si128(reinterpret_cast<const __m128i *>(p)); | ||
| 361 | + __m128i x = _mm_cmpeq_epi8(s, w0); | ||
| 362 | + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w1)); | ||
| 363 | + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w2)); | ||
| 364 | + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w3)); | ||
| 365 | + unsigned short r = static_cast<unsigned short>(~_mm_movemask_epi8(x)); | ||
| 366 | + if (r != 0) { // some of characters may be non-whitespace | ||
| 367 | +#ifdef _MSC_VER // Find the index of first non-whitespace | ||
| 368 | + unsigned long offset; | ||
| 369 | + _BitScanForward(&offset, r); | ||
| 370 | + return p + offset; | ||
| 371 | +#else | ||
| 372 | + return p + __builtin_ffs(r) - 1; | ||
| 373 | +#endif | ||
| 374 | + } | ||
| 375 | + } | ||
| 376 | +} | ||
| 377 | + | ||
| 378 | +inline const char *SkipWhitespace_SIMD(const char* p, const char* end) { | ||
| 379 | + // Fast return for single non-whitespace | ||
| 380 | + if (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')) | ||
| 381 | + ++p; | ||
| 382 | + else | ||
| 383 | + return p; | ||
| 384 | + | ||
| 385 | + // The rest of string | ||
| 386 | + #define C16(c) { c, c, c, c, c, c, c, c, c, c, c, c, c, c, c, c } | ||
| 387 | + static const char whitespaces[4][16] = { C16(' '), C16('\n'), C16('\r'), C16('\t') }; | ||
| 388 | + #undef C16 | ||
| 389 | + | ||
| 390 | + const __m128i w0 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&whitespaces[0][0])); | ||
| 391 | + const __m128i w1 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&whitespaces[1][0])); | ||
| 392 | + const __m128i w2 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&whitespaces[2][0])); | ||
| 393 | + const __m128i w3 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&whitespaces[3][0])); | ||
| 394 | + | ||
| 395 | + for (; p <= end - 16; p += 16) { | ||
| 396 | + const __m128i s = _mm_loadu_si128(reinterpret_cast<const __m128i *>(p)); | ||
| 397 | + __m128i x = _mm_cmpeq_epi8(s, w0); | ||
| 398 | + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w1)); | ||
| 399 | + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w2)); | ||
| 400 | + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w3)); | ||
| 401 | + unsigned short r = static_cast<unsigned short>(~_mm_movemask_epi8(x)); | ||
| 402 | + if (r != 0) { // some of characters may be non-whitespace | ||
| 403 | +#ifdef _MSC_VER // Find the index of first non-whitespace | ||
| 404 | + unsigned long offset; | ||
| 405 | + _BitScanForward(&offset, r); | ||
| 406 | + return p + offset; | ||
| 407 | +#else | ||
| 408 | + return p + __builtin_ffs(r) - 1; | ||
| 409 | +#endif | ||
| 410 | + } | ||
| 411 | + } | ||
| 412 | + | ||
| 413 | + return SkipWhitespace(p, end); | ||
| 414 | +} | ||
| 415 | + | ||
| 416 | +#elif defined(RAPIDJSON_NEON) | ||
| 417 | + | ||
| 418 | +//! Skip whitespace with ARM Neon instructions, testing 16 8-byte characters at once. | ||
| 419 | +inline const char *SkipWhitespace_SIMD(const char* p) { | ||
| 420 | + // Fast return for single non-whitespace | ||
| 421 | + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') | ||
| 422 | + ++p; | ||
| 423 | + else | ||
| 424 | + return p; | ||
| 425 | + | ||
| 426 | + // 16-byte align to the next boundary | ||
| 427 | + const char* nextAligned = reinterpret_cast<const char*>((reinterpret_cast<size_t>(p) + 15) & static_cast<size_t>(~15)); | ||
| 428 | + while (p != nextAligned) | ||
| 429 | + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') | ||
| 430 | + ++p; | ||
| 431 | + else | ||
| 432 | + return p; | ||
| 433 | + | ||
| 434 | + const uint8x16_t w0 = vmovq_n_u8(' '); | ||
| 435 | + const uint8x16_t w1 = vmovq_n_u8('\n'); | ||
| 436 | + const uint8x16_t w2 = vmovq_n_u8('\r'); | ||
| 437 | + const uint8x16_t w3 = vmovq_n_u8('\t'); | ||
| 438 | + | ||
| 439 | + for (;; p += 16) { | ||
| 440 | + const uint8x16_t s = vld1q_u8(reinterpret_cast<const uint8_t *>(p)); | ||
| 441 | + uint8x16_t x = vceqq_u8(s, w0); | ||
| 442 | + x = vorrq_u8(x, vceqq_u8(s, w1)); | ||
| 443 | + x = vorrq_u8(x, vceqq_u8(s, w2)); | ||
| 444 | + x = vorrq_u8(x, vceqq_u8(s, w3)); | ||
| 445 | + | ||
| 446 | + x = vmvnq_u8(x); // Negate | ||
| 447 | + x = vrev64q_u8(x); // Rev in 64 | ||
| 448 | + uint64_t low = vgetq_lane_u64(reinterpret_cast<uint64x2_t>(x), 0); // extract | ||
| 449 | + uint64_t high = vgetq_lane_u64(reinterpret_cast<uint64x2_t>(x), 1); // extract | ||
| 450 | + | ||
| 451 | + if (low == 0) { | ||
| 452 | + if (high != 0) { | ||
| 453 | + int lz =__builtin_clzll(high);; | ||
| 454 | + return p + 8 + (lz >> 3); | ||
| 455 | + } | ||
| 456 | + } else { | ||
| 457 | + int lz = __builtin_clzll(low);; | ||
| 458 | + return p + (lz >> 3); | ||
| 459 | + } | ||
| 460 | + } | ||
| 461 | +} | ||
| 462 | + | ||
| 463 | +inline const char *SkipWhitespace_SIMD(const char* p, const char* end) { | ||
| 464 | + // Fast return for single non-whitespace | ||
| 465 | + if (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')) | ||
| 466 | + ++p; | ||
| 467 | + else | ||
| 468 | + return p; | ||
| 469 | + | ||
| 470 | + const uint8x16_t w0 = vmovq_n_u8(' '); | ||
| 471 | + const uint8x16_t w1 = vmovq_n_u8('\n'); | ||
| 472 | + const uint8x16_t w2 = vmovq_n_u8('\r'); | ||
| 473 | + const uint8x16_t w3 = vmovq_n_u8('\t'); | ||
| 474 | + | ||
| 475 | + for (; p <= end - 16; p += 16) { | ||
| 476 | + const uint8x16_t s = vld1q_u8(reinterpret_cast<const uint8_t *>(p)); | ||
| 477 | + uint8x16_t x = vceqq_u8(s, w0); | ||
| 478 | + x = vorrq_u8(x, vceqq_u8(s, w1)); | ||
| 479 | + x = vorrq_u8(x, vceqq_u8(s, w2)); | ||
| 480 | + x = vorrq_u8(x, vceqq_u8(s, w3)); | ||
| 481 | + | ||
| 482 | + x = vmvnq_u8(x); // Negate | ||
| 483 | + x = vrev64q_u8(x); // Rev in 64 | ||
| 484 | + uint64_t low = vgetq_lane_u64(reinterpret_cast<uint64x2_t>(x), 0); // extract | ||
| 485 | + uint64_t high = vgetq_lane_u64(reinterpret_cast<uint64x2_t>(x), 1); // extract | ||
| 486 | + | ||
| 487 | + if (low == 0) { | ||
| 488 | + if (high != 0) { | ||
| 489 | + int lz = __builtin_clzll(high); | ||
| 490 | + return p + 8 + (lz >> 3); | ||
| 491 | + } | ||
| 492 | + } else { | ||
| 493 | + int lz = __builtin_clzll(low); | ||
| 494 | + return p + (lz >> 3); | ||
| 495 | + } | ||
| 496 | + } | ||
| 497 | + | ||
| 498 | + return SkipWhitespace(p, end); | ||
| 499 | +} | ||
| 500 | + | ||
| 501 | +#endif // RAPIDJSON_NEON | ||
| 502 | + | ||
| 503 | +#ifdef RAPIDJSON_SIMD | ||
| 504 | +//! Template function specialization for InsituStringStream | ||
| 505 | +template<> inline void SkipWhitespace(InsituStringStream& is) { | ||
| 506 | + is.src_ = const_cast<char*>(SkipWhitespace_SIMD(is.src_)); | ||
| 507 | +} | ||
| 508 | + | ||
| 509 | +//! Template function specialization for StringStream | ||
| 510 | +template<> inline void SkipWhitespace(StringStream& is) { | ||
| 511 | + is.src_ = SkipWhitespace_SIMD(is.src_); | ||
| 512 | +} | ||
| 513 | + | ||
| 514 | +template<> inline void SkipWhitespace(EncodedInputStream<UTF8<>, MemoryStream>& is) { | ||
| 515 | + is.is_.src_ = SkipWhitespace_SIMD(is.is_.src_, is.is_.end_); | ||
| 516 | +} | ||
| 517 | +#endif // RAPIDJSON_SIMD | ||
| 518 | + | ||
| 519 | +/////////////////////////////////////////////////////////////////////////////// | ||
| 520 | +// GenericReader | ||
| 521 | + | ||
| 522 | +//! SAX-style JSON parser. Use \ref Reader for UTF8 encoding and default allocator. | ||
| 523 | +/*! GenericReader parses JSON text from a stream, and send events synchronously to an | ||
| 524 | + object implementing Handler concept. | ||
| 525 | + | ||
| 526 | + It needs to allocate a stack for storing a single decoded string during | ||
| 527 | + non-destructive parsing. | ||
| 528 | + | ||
| 529 | + For in-situ parsing, the decoded string is directly written to the source | ||
| 530 | + text string, no temporary buffer is required. | ||
| 531 | + | ||
| 532 | + A GenericReader object can be reused for parsing multiple JSON text. | ||
| 533 | + | ||
| 534 | + \tparam SourceEncoding Encoding of the input stream. | ||
| 535 | + \tparam TargetEncoding Encoding of the parse output. | ||
| 536 | + \tparam StackAllocator Allocator type for stack. | ||
| 537 | +*/ | ||
| 538 | +template <typename SourceEncoding, typename TargetEncoding, typename StackAllocator = CrtAllocator> | ||
| 539 | +class GenericReader { | ||
| 540 | +public: | ||
| 541 | + typedef typename SourceEncoding::Ch Ch; //!< SourceEncoding character type | ||
| 542 | + | ||
| 543 | + //! Constructor. | ||
| 544 | + /*! \param stackAllocator Optional allocator for allocating stack memory. (Only use for non-destructive parsing) | ||
| 545 | + \param stackCapacity stack capacity in bytes for storing a single decoded string. (Only use for non-destructive parsing) | ||
| 546 | + */ | ||
| 547 | + GenericReader(StackAllocator* stackAllocator = 0, size_t stackCapacity = kDefaultStackCapacity) : stack_(stackAllocator, stackCapacity), parseResult_() {} | ||
| 548 | + | ||
| 549 | + //! Parse JSON text. | ||
| 550 | + /*! \tparam parseFlags Combination of \ref ParseFlag. | ||
| 551 | + \tparam InputStream Type of input stream, implementing Stream concept. | ||
| 552 | + \tparam Handler Type of handler, implementing Handler concept. | ||
| 553 | + \param is Input stream to be parsed. | ||
| 554 | + \param handler The handler to receive events. | ||
| 555 | + \return Whether the parsing is successful. | ||
| 556 | + */ | ||
| 557 | + template <unsigned parseFlags, typename InputStream, typename Handler> | ||
| 558 | + ParseResult Parse(InputStream& is, Handler& handler) { | ||
| 559 | + if (parseFlags & kParseIterativeFlag) | ||
| 560 | + return IterativeParse<parseFlags>(is, handler); | ||
| 561 | + | ||
| 562 | + parseResult_.Clear(); | ||
| 563 | + | ||
| 564 | + ClearStackOnExit scope(*this); | ||
| 565 | + | ||
| 566 | + SkipWhitespaceAndComments<parseFlags>(is); | ||
| 567 | + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); | ||
| 568 | + | ||
| 569 | + if (RAPIDJSON_UNLIKELY(is.Peek() == '\0')) { | ||
| 570 | + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentEmpty, is.Tell()); | ||
| 571 | + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); | ||
| 572 | + } | ||
| 573 | + else { | ||
| 574 | + ParseValue<parseFlags>(is, handler); | ||
| 575 | + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); | ||
| 576 | + | ||
| 577 | + if (!(parseFlags & kParseStopWhenDoneFlag)) { | ||
| 578 | + SkipWhitespaceAndComments<parseFlags>(is); | ||
| 579 | + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); | ||
| 580 | + | ||
| 581 | + if (RAPIDJSON_UNLIKELY(is.Peek() != '\0')) { | ||
| 582 | + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentRootNotSingular, is.Tell()); | ||
| 583 | + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); | ||
| 584 | + } | ||
| 585 | + } | ||
| 586 | + } | ||
| 587 | + | ||
| 588 | + return parseResult_; | ||
| 589 | + } | ||
| 590 | + | ||
| 591 | + //! Parse JSON text (with \ref kParseDefaultFlags) | ||
| 592 | + /*! \tparam InputStream Type of input stream, implementing Stream concept | ||
| 593 | + \tparam Handler Type of handler, implementing Handler concept. | ||
| 594 | + \param is Input stream to be parsed. | ||
| 595 | + \param handler The handler to receive events. | ||
| 596 | + \return Whether the parsing is successful. | ||
| 597 | + */ | ||
| 598 | + template <typename InputStream, typename Handler> | ||
| 599 | + ParseResult Parse(InputStream& is, Handler& handler) { | ||
| 600 | + return Parse<kParseDefaultFlags>(is, handler); | ||
| 601 | + } | ||
| 602 | + | ||
| 603 | + //! Initialize JSON text token-by-token parsing | ||
| 604 | + /*! | ||
| 605 | + */ | ||
| 606 | + void IterativeParseInit() { | ||
| 607 | + parseResult_.Clear(); | ||
| 608 | + state_ = IterativeParsingStartState; | ||
| 609 | + } | ||
| 610 | + | ||
| 611 | + //! Parse one token from JSON text | ||
| 612 | + /*! \tparam InputStream Type of input stream, implementing Stream concept | ||
| 613 | + \tparam Handler Type of handler, implementing Handler concept. | ||
| 614 | + \param is Input stream to be parsed. | ||
| 615 | + \param handler The handler to receive events. | ||
| 616 | + \return Whether the parsing is successful. | ||
| 617 | + */ | ||
| 618 | + template <unsigned parseFlags, typename InputStream, typename Handler> | ||
| 619 | + bool IterativeParseNext(InputStream& is, Handler& handler) { | ||
| 620 | + while (RAPIDJSON_LIKELY(is.Peek() != '\0')) { | ||
| 621 | + SkipWhitespaceAndComments<parseFlags>(is); | ||
| 622 | + | ||
| 623 | + Token t = Tokenize(is.Peek()); | ||
| 624 | + IterativeParsingState n = Predict(state_, t); | ||
| 625 | + IterativeParsingState d = Transit<parseFlags>(state_, t, n, is, handler); | ||
| 626 | + | ||
| 627 | + // If we've finished or hit an error... | ||
| 628 | + if (RAPIDJSON_UNLIKELY(IsIterativeParsingCompleteState(d))) { | ||
| 629 | + // Report errors. | ||
| 630 | + if (d == IterativeParsingErrorState) { | ||
| 631 | + HandleError(state_, is); | ||
| 632 | + return false; | ||
| 633 | + } | ||
| 634 | + | ||
| 635 | + // Transition to the finish state. | ||
| 636 | + RAPIDJSON_ASSERT(d == IterativeParsingFinishState); | ||
| 637 | + state_ = d; | ||
| 638 | + | ||
| 639 | + // If StopWhenDone is not set... | ||
| 640 | + if (!(parseFlags & kParseStopWhenDoneFlag)) { | ||
| 641 | + // ... and extra non-whitespace data is found... | ||
| 642 | + SkipWhitespaceAndComments<parseFlags>(is); | ||
| 643 | + if (is.Peek() != '\0') { | ||
| 644 | + // ... this is considered an error. | ||
| 645 | + HandleError(state_, is); | ||
| 646 | + return false; | ||
| 647 | + } | ||
| 648 | + } | ||
| 649 | + | ||
| 650 | + // Success! We are done! | ||
| 651 | + return true; | ||
| 652 | + } | ||
| 653 | + | ||
| 654 | + // Transition to the new state. | ||
| 655 | + state_ = d; | ||
| 656 | + | ||
| 657 | + // If we parsed anything other than a delimiter, we invoked the handler, so we can return true now. | ||
| 658 | + if (!IsIterativeParsingDelimiterState(n)) | ||
| 659 | + return true; | ||
| 660 | + } | ||
| 661 | + | ||
| 662 | + // We reached the end of file. | ||
| 663 | + stack_.Clear(); | ||
| 664 | + | ||
| 665 | + if (state_ != IterativeParsingFinishState) { | ||
| 666 | + HandleError(state_, is); | ||
| 667 | + return false; | ||
| 668 | + } | ||
| 669 | + | ||
| 670 | + return true; | ||
| 671 | + } | ||
| 672 | + | ||
| 673 | + //! Check if token-by-token parsing JSON text is complete | ||
| 674 | + /*! \return Whether the JSON has been fully decoded. | ||
| 675 | + */ | ||
| 676 | + RAPIDJSON_FORCEINLINE bool IterativeParseComplete() { | ||
| 677 | + return IsIterativeParsingCompleteState(state_); | ||
| 678 | + } | ||
| 679 | + | ||
| 680 | + //! Whether a parse error has occured in the last parsing. | ||
| 681 | + bool HasParseError() const { return parseResult_.IsError(); } | ||
| 682 | + | ||
| 683 | + //! Get the \ref ParseErrorCode of last parsing. | ||
| 684 | + ParseErrorCode GetParseErrorCode() const { return parseResult_.Code(); } | ||
| 685 | + | ||
| 686 | + //! Get the position of last parsing error in input, 0 otherwise. | ||
| 687 | + size_t GetErrorOffset() const { return parseResult_.Offset(); } | ||
| 688 | + | ||
| 689 | +protected: | ||
| 690 | + void SetParseError(ParseErrorCode code, size_t offset) { parseResult_.Set(code, offset); } | ||
| 691 | + | ||
| 692 | +private: | ||
| 693 | + // Prohibit copy constructor & assignment operator. | ||
| 694 | + GenericReader(const GenericReader&); | ||
| 695 | + GenericReader& operator=(const GenericReader&); | ||
| 696 | + | ||
| 697 | + void ClearStack() { stack_.Clear(); } | ||
| 698 | + | ||
| 699 | + // clear stack on any exit from ParseStream, e.g. due to exception | ||
| 700 | + struct ClearStackOnExit { | ||
| 701 | + explicit ClearStackOnExit(GenericReader& r) : r_(r) {} | ||
| 702 | + ~ClearStackOnExit() { r_.ClearStack(); } | ||
| 703 | + private: | ||
| 704 | + GenericReader& r_; | ||
| 705 | + ClearStackOnExit(const ClearStackOnExit&); | ||
| 706 | + ClearStackOnExit& operator=(const ClearStackOnExit&); | ||
| 707 | + }; | ||
| 708 | + | ||
| 709 | + template<unsigned parseFlags, typename InputStream> | ||
| 710 | + void SkipWhitespaceAndComments(InputStream& is) { | ||
| 711 | + SkipWhitespace(is); | ||
| 712 | + | ||
| 713 | + if (parseFlags & kParseCommentsFlag) { | ||
| 714 | + while (RAPIDJSON_UNLIKELY(Consume(is, '/'))) { | ||
| 715 | + if (Consume(is, '*')) { | ||
| 716 | + while (true) { | ||
| 717 | + if (RAPIDJSON_UNLIKELY(is.Peek() == '\0')) | ||
| 718 | + RAPIDJSON_PARSE_ERROR(kParseErrorUnspecificSyntaxError, is.Tell()); | ||
| 719 | + else if (Consume(is, '*')) { | ||
| 720 | + if (Consume(is, '/')) | ||
| 721 | + break; | ||
| 722 | + } | ||
| 723 | + else | ||
| 724 | + is.Take(); | ||
| 725 | + } | ||
| 726 | + } | ||
| 727 | + else if (RAPIDJSON_LIKELY(Consume(is, '/'))) | ||
| 728 | + while (is.Peek() != '\0' && is.Take() != '\n') {} | ||
| 729 | + else | ||
| 730 | + RAPIDJSON_PARSE_ERROR(kParseErrorUnspecificSyntaxError, is.Tell()); | ||
| 731 | + | ||
| 732 | + SkipWhitespace(is); | ||
| 733 | + } | ||
| 734 | + } | ||
| 735 | + } | ||
| 736 | + | ||
| 737 | + // Parse object: { string : value, ... } | ||
| 738 | + template<unsigned parseFlags, typename InputStream, typename Handler> | ||
| 739 | + void ParseObject(InputStream& is, Handler& handler) { | ||
| 740 | + RAPIDJSON_ASSERT(is.Peek() == '{'); | ||
| 741 | + is.Take(); // Skip '{' | ||
| 742 | + | ||
| 743 | + if (RAPIDJSON_UNLIKELY(!handler.StartObject())) | ||
| 744 | + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); | ||
| 745 | + | ||
| 746 | + SkipWhitespaceAndComments<parseFlags>(is); | ||
| 747 | + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; | ||
| 748 | + | ||
| 749 | + if (Consume(is, '}')) { | ||
| 750 | + if (RAPIDJSON_UNLIKELY(!handler.EndObject(0))) // empty object | ||
| 751 | + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); | ||
| 752 | + return; | ||
| 753 | + } | ||
| 754 | + | ||
| 755 | + for (SizeType memberCount = 0;;) { | ||
| 756 | + if (RAPIDJSON_UNLIKELY(is.Peek() != '"')) | ||
| 757 | + RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissName, is.Tell()); | ||
| 758 | + | ||
| 759 | + ParseString<parseFlags>(is, handler, true); | ||
| 760 | + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; | ||
| 761 | + | ||
| 762 | + SkipWhitespaceAndComments<parseFlags>(is); | ||
| 763 | + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; | ||
| 764 | + | ||
| 765 | + if (RAPIDJSON_UNLIKELY(!Consume(is, ':'))) | ||
| 766 | + RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissColon, is.Tell()); | ||
| 767 | + | ||
| 768 | + SkipWhitespaceAndComments<parseFlags>(is); | ||
| 769 | + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; | ||
| 770 | + | ||
| 771 | + ParseValue<parseFlags>(is, handler); | ||
| 772 | + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; | ||
| 773 | + | ||
| 774 | + SkipWhitespaceAndComments<parseFlags>(is); | ||
| 775 | + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; | ||
| 776 | + | ||
| 777 | + ++memberCount; | ||
| 778 | + | ||
| 779 | + switch (is.Peek()) { | ||
| 780 | + case ',': | ||
| 781 | + is.Take(); | ||
| 782 | + SkipWhitespaceAndComments<parseFlags>(is); | ||
| 783 | + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; | ||
| 784 | + break; | ||
| 785 | + case '}': | ||
| 786 | + is.Take(); | ||
| 787 | + if (RAPIDJSON_UNLIKELY(!handler.EndObject(memberCount))) | ||
| 788 | + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); | ||
| 789 | + return; | ||
| 790 | + default: | ||
| 791 | + RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, is.Tell()); break; // This useless break is only for making warning and coverage happy | ||
| 792 | + } | ||
| 793 | + | ||
| 794 | + if (parseFlags & kParseTrailingCommasFlag) { | ||
| 795 | + if (is.Peek() == '}') { | ||
| 796 | + if (RAPIDJSON_UNLIKELY(!handler.EndObject(memberCount))) | ||
| 797 | + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); | ||
| 798 | + is.Take(); | ||
| 799 | + return; | ||
| 800 | + } | ||
| 801 | + } | ||
| 802 | + } | ||
| 803 | + } | ||
| 804 | + | ||
| 805 | + // Parse array: [ value, ... ] | ||
| 806 | + template<unsigned parseFlags, typename InputStream, typename Handler> | ||
| 807 | + void ParseArray(InputStream& is, Handler& handler) { | ||
| 808 | + RAPIDJSON_ASSERT(is.Peek() == '['); | ||
| 809 | + is.Take(); // Skip '[' | ||
| 810 | + | ||
| 811 | + if (RAPIDJSON_UNLIKELY(!handler.StartArray())) | ||
| 812 | + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); | ||
| 813 | + | ||
| 814 | + SkipWhitespaceAndComments<parseFlags>(is); | ||
| 815 | + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; | ||
| 816 | + | ||
| 817 | + if (Consume(is, ']')) { | ||
| 818 | + if (RAPIDJSON_UNLIKELY(!handler.EndArray(0))) // empty array | ||
| 819 | + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); | ||
| 820 | + return; | ||
| 821 | + } | ||
| 822 | + | ||
| 823 | + for (SizeType elementCount = 0;;) { | ||
| 824 | + ParseValue<parseFlags>(is, handler); | ||
| 825 | + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; | ||
| 826 | + | ||
| 827 | + ++elementCount; | ||
| 828 | + SkipWhitespaceAndComments<parseFlags>(is); | ||
| 829 | + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; | ||
| 830 | + | ||
| 831 | + if (Consume(is, ',')) { | ||
| 832 | + SkipWhitespaceAndComments<parseFlags>(is); | ||
| 833 | + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; | ||
| 834 | + } | ||
| 835 | + else if (Consume(is, ']')) { | ||
| 836 | + if (RAPIDJSON_UNLIKELY(!handler.EndArray(elementCount))) | ||
| 837 | + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); | ||
| 838 | + return; | ||
| 839 | + } | ||
| 840 | + else | ||
| 841 | + RAPIDJSON_PARSE_ERROR(kParseErrorArrayMissCommaOrSquareBracket, is.Tell()); | ||
| 842 | + | ||
| 843 | + if (parseFlags & kParseTrailingCommasFlag) { | ||
| 844 | + if (is.Peek() == ']') { | ||
| 845 | + if (RAPIDJSON_UNLIKELY(!handler.EndArray(elementCount))) | ||
| 846 | + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); | ||
| 847 | + is.Take(); | ||
| 848 | + return; | ||
| 849 | + } | ||
| 850 | + } | ||
| 851 | + } | ||
| 852 | + } | ||
| 853 | + | ||
| 854 | + template<unsigned parseFlags, typename InputStream, typename Handler> | ||
| 855 | + void ParseNull(InputStream& is, Handler& handler) { | ||
| 856 | + RAPIDJSON_ASSERT(is.Peek() == 'n'); | ||
| 857 | + is.Take(); | ||
| 858 | + | ||
| 859 | + if (RAPIDJSON_LIKELY(Consume(is, 'u') && Consume(is, 'l') && Consume(is, 'l'))) { | ||
| 860 | + if (RAPIDJSON_UNLIKELY(!handler.Null())) | ||
| 861 | + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); | ||
| 862 | + } | ||
| 863 | + else | ||
| 864 | + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); | ||
| 865 | + } | ||
| 866 | + | ||
| 867 | + template<unsigned parseFlags, typename InputStream, typename Handler> | ||
| 868 | + void ParseTrue(InputStream& is, Handler& handler) { | ||
| 869 | + RAPIDJSON_ASSERT(is.Peek() == 't'); | ||
| 870 | + is.Take(); | ||
| 871 | + | ||
| 872 | + if (RAPIDJSON_LIKELY(Consume(is, 'r') && Consume(is, 'u') && Consume(is, 'e'))) { | ||
| 873 | + if (RAPIDJSON_UNLIKELY(!handler.Bool(true))) | ||
| 874 | + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); | ||
| 875 | + } | ||
| 876 | + else | ||
| 877 | + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); | ||
| 878 | + } | ||
| 879 | + | ||
| 880 | + template<unsigned parseFlags, typename InputStream, typename Handler> | ||
| 881 | + void ParseFalse(InputStream& is, Handler& handler) { | ||
| 882 | + RAPIDJSON_ASSERT(is.Peek() == 'f'); | ||
| 883 | + is.Take(); | ||
| 884 | + | ||
| 885 | + if (RAPIDJSON_LIKELY(Consume(is, 'a') && Consume(is, 'l') && Consume(is, 's') && Consume(is, 'e'))) { | ||
| 886 | + if (RAPIDJSON_UNLIKELY(!handler.Bool(false))) | ||
| 887 | + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); | ||
| 888 | + } | ||
| 889 | + else | ||
| 890 | + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); | ||
| 891 | + } | ||
| 892 | + | ||
| 893 | + template<typename InputStream> | ||
| 894 | + RAPIDJSON_FORCEINLINE static bool Consume(InputStream& is, typename InputStream::Ch expect) { | ||
| 895 | + if (RAPIDJSON_LIKELY(is.Peek() == expect)) { | ||
| 896 | + is.Take(); | ||
| 897 | + return true; | ||
| 898 | + } | ||
| 899 | + else | ||
| 900 | + return false; | ||
| 901 | + } | ||
| 902 | + | ||
| 903 | + // Helper function to parse four hexidecimal digits in \uXXXX in ParseString(). | ||
| 904 | + template<typename InputStream> | ||
| 905 | + unsigned ParseHex4(InputStream& is, size_t escapeOffset) { | ||
| 906 | + unsigned codepoint = 0; | ||
| 907 | + for (int i = 0; i < 4; i++) { | ||
| 908 | + Ch c = is.Peek(); | ||
| 909 | + codepoint <<= 4; | ||
| 910 | + codepoint += static_cast<unsigned>(c); | ||
| 911 | + if (c >= '0' && c <= '9') | ||
| 912 | + codepoint -= '0'; | ||
| 913 | + else if (c >= 'A' && c <= 'F') | ||
| 914 | + codepoint -= 'A' - 10; | ||
| 915 | + else if (c >= 'a' && c <= 'f') | ||
| 916 | + codepoint -= 'a' - 10; | ||
| 917 | + else { | ||
| 918 | + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorStringUnicodeEscapeInvalidHex, escapeOffset); | ||
| 919 | + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(0); | ||
| 920 | + } | ||
| 921 | + is.Take(); | ||
| 922 | + } | ||
| 923 | + return codepoint; | ||
| 924 | + } | ||
| 925 | + | ||
| 926 | + template <typename CharType> | ||
| 927 | + class StackStream { | ||
| 928 | + public: | ||
| 929 | + typedef CharType Ch; | ||
| 930 | + | ||
| 931 | + StackStream(internal::Stack<StackAllocator>& stack) : stack_(stack), length_(0) {} | ||
| 932 | + RAPIDJSON_FORCEINLINE void Put(Ch c) { | ||
| 933 | + *stack_.template Push<Ch>() = c; | ||
| 934 | + ++length_; | ||
| 935 | + } | ||
| 936 | + | ||
| 937 | + RAPIDJSON_FORCEINLINE void* Push(SizeType count) { | ||
| 938 | + length_ += count; | ||
| 939 | + return stack_.template Push<Ch>(count); | ||
| 940 | + } | ||
| 941 | + | ||
| 942 | + size_t Length() const { return length_; } | ||
| 943 | + | ||
| 944 | + Ch* Pop() { | ||
| 945 | + return stack_.template Pop<Ch>(length_); | ||
| 946 | + } | ||
| 947 | + | ||
| 948 | + private: | ||
| 949 | + StackStream(const StackStream&); | ||
| 950 | + StackStream& operator=(const StackStream&); | ||
| 951 | + | ||
| 952 | + internal::Stack<StackAllocator>& stack_; | ||
| 953 | + SizeType length_; | ||
| 954 | + }; | ||
| 955 | + | ||
| 956 | + // Parse string and generate String event. Different code paths for kParseInsituFlag. | ||
| 957 | + template<unsigned parseFlags, typename InputStream, typename Handler> | ||
| 958 | + void ParseString(InputStream& is, Handler& handler, bool isKey = false) { | ||
| 959 | + internal::StreamLocalCopy<InputStream> copy(is); | ||
| 960 | + InputStream& s(copy.s); | ||
| 961 | + | ||
| 962 | + RAPIDJSON_ASSERT(s.Peek() == '\"'); | ||
| 963 | + s.Take(); // Skip '\"' | ||
| 964 | + | ||
| 965 | + bool success = false; | ||
| 966 | + if (parseFlags & kParseInsituFlag) { | ||
| 967 | + typename InputStream::Ch *head = s.PutBegin(); | ||
| 968 | + ParseStringToStream<parseFlags, SourceEncoding, SourceEncoding>(s, s); | ||
| 969 | + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; | ||
| 970 | + size_t length = s.PutEnd(head) - 1; | ||
| 971 | + RAPIDJSON_ASSERT(length <= 0xFFFFFFFF); | ||
| 972 | + const typename TargetEncoding::Ch* const str = reinterpret_cast<typename TargetEncoding::Ch*>(head); | ||
| 973 | + success = (isKey ? handler.Key(str, SizeType(length), false) : handler.String(str, SizeType(length), false)); | ||
| 974 | + } | ||
| 975 | + else { | ||
| 976 | + StackStream<typename TargetEncoding::Ch> stackStream(stack_); | ||
| 977 | + ParseStringToStream<parseFlags, SourceEncoding, TargetEncoding>(s, stackStream); | ||
| 978 | + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; | ||
| 979 | + SizeType length = static_cast<SizeType>(stackStream.Length()) - 1; | ||
| 980 | + const typename TargetEncoding::Ch* const str = stackStream.Pop(); | ||
| 981 | + success = (isKey ? handler.Key(str, length, true) : handler.String(str, length, true)); | ||
| 982 | + } | ||
| 983 | + if (RAPIDJSON_UNLIKELY(!success)) | ||
| 984 | + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, s.Tell()); | ||
| 985 | + } | ||
| 986 | + | ||
| 987 | + // Parse string to an output is | ||
| 988 | + // This function handles the prefix/suffix double quotes, escaping, and optional encoding validation. | ||
| 989 | + template<unsigned parseFlags, typename SEncoding, typename TEncoding, typename InputStream, typename OutputStream> | ||
| 990 | + RAPIDJSON_FORCEINLINE void ParseStringToStream(InputStream& is, OutputStream& os) { | ||
| 991 | +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN | ||
| 992 | +#define Z16 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 | ||
| 993 | + static const char escape[256] = { | ||
| 994 | + Z16, Z16, 0, 0,'\"', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'/', | ||
| 995 | + Z16, Z16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'\\', 0, 0, 0, | ||
| 996 | + 0, 0,'\b', 0, 0, 0,'\f', 0, 0, 0, 0, 0, 0, 0,'\n', 0, | ||
| 997 | + 0, 0,'\r', 0,'\t', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 998 | + Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16 | ||
| 999 | + }; | ||
| 1000 | +#undef Z16 | ||
| 1001 | +//!@endcond | ||
| 1002 | + | ||
| 1003 | + for (;;) { | ||
| 1004 | + // Scan and copy string before "\\\"" or < 0x20. This is an optional optimzation. | ||
| 1005 | + if (!(parseFlags & kParseValidateEncodingFlag)) | ||
| 1006 | + ScanCopyUnescapedString(is, os); | ||
| 1007 | + | ||
| 1008 | + Ch c = is.Peek(); | ||
| 1009 | + if (RAPIDJSON_UNLIKELY(c == '\\')) { // Escape | ||
| 1010 | + size_t escapeOffset = is.Tell(); // For invalid escaping, report the inital '\\' as error offset | ||
| 1011 | + is.Take(); | ||
| 1012 | + Ch e = is.Peek(); | ||
| 1013 | + if ((sizeof(Ch) == 1 || unsigned(e) < 256) && RAPIDJSON_LIKELY(escape[static_cast<unsigned char>(e)])) { | ||
| 1014 | + is.Take(); | ||
| 1015 | + os.Put(static_cast<typename TEncoding::Ch>(escape[static_cast<unsigned char>(e)])); | ||
| 1016 | + } | ||
| 1017 | + else if (RAPIDJSON_LIKELY(e == 'u')) { // Unicode | ||
| 1018 | + is.Take(); | ||
| 1019 | + unsigned codepoint = ParseHex4(is, escapeOffset); | ||
| 1020 | + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; | ||
| 1021 | + if (RAPIDJSON_UNLIKELY(codepoint >= 0xD800 && codepoint <= 0xDBFF)) { | ||
| 1022 | + // Handle UTF-16 surrogate pair | ||
| 1023 | + if (RAPIDJSON_UNLIKELY(!Consume(is, '\\') || !Consume(is, 'u'))) | ||
| 1024 | + RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, escapeOffset); | ||
| 1025 | + unsigned codepoint2 = ParseHex4(is, escapeOffset); | ||
| 1026 | + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; | ||
| 1027 | + if (RAPIDJSON_UNLIKELY(codepoint2 < 0xDC00 || codepoint2 > 0xDFFF)) | ||
| 1028 | + RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, escapeOffset); | ||
| 1029 | + codepoint = (((codepoint - 0xD800) << 10) | (codepoint2 - 0xDC00)) + 0x10000; | ||
| 1030 | + } | ||
| 1031 | + TEncoding::Encode(os, codepoint); | ||
| 1032 | + } | ||
| 1033 | + else | ||
| 1034 | + RAPIDJSON_PARSE_ERROR(kParseErrorStringEscapeInvalid, escapeOffset); | ||
| 1035 | + } | ||
| 1036 | + else if (RAPIDJSON_UNLIKELY(c == '"')) { // Closing double quote | ||
| 1037 | + is.Take(); | ||
| 1038 | + os.Put('\0'); // null-terminate the string | ||
| 1039 | + return; | ||
| 1040 | + } | ||
| 1041 | + else if (RAPIDJSON_UNLIKELY(static_cast<unsigned>(c) < 0x20)) { // RFC 4627: unescaped = %x20-21 / %x23-5B / %x5D-10FFFF | ||
| 1042 | + if (c == '\0') | ||
| 1043 | + RAPIDJSON_PARSE_ERROR(kParseErrorStringMissQuotationMark, is.Tell()); | ||
| 1044 | + else | ||
| 1045 | + RAPIDJSON_PARSE_ERROR(kParseErrorStringInvalidEncoding, is.Tell()); | ||
| 1046 | + } | ||
| 1047 | + else { | ||
| 1048 | + size_t offset = is.Tell(); | ||
| 1049 | + if (RAPIDJSON_UNLIKELY((parseFlags & kParseValidateEncodingFlag ? | ||
| 1050 | + !Transcoder<SEncoding, TEncoding>::Validate(is, os) : | ||
| 1051 | + !Transcoder<SEncoding, TEncoding>::Transcode(is, os)))) | ||
| 1052 | + RAPIDJSON_PARSE_ERROR(kParseErrorStringInvalidEncoding, offset); | ||
| 1053 | + } | ||
| 1054 | + } | ||
| 1055 | + } | ||
| 1056 | + | ||
| 1057 | + template<typename InputStream, typename OutputStream> | ||
| 1058 | + static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(InputStream&, OutputStream&) { | ||
| 1059 | + // Do nothing for generic version | ||
| 1060 | + } | ||
| 1061 | + | ||
| 1062 | +#if defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42) | ||
| 1063 | + // StringStream -> StackStream<char> | ||
| 1064 | + static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(StringStream& is, StackStream<char>& os) { | ||
| 1065 | + const char* p = is.src_; | ||
| 1066 | + | ||
| 1067 | + // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) | ||
| 1068 | + const char* nextAligned = reinterpret_cast<const char*>((reinterpret_cast<size_t>(p) + 15) & static_cast<size_t>(~15)); | ||
| 1069 | + while (p != nextAligned) | ||
| 1070 | + if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast<unsigned>(*p) < 0x20)) { | ||
| 1071 | + is.src_ = p; | ||
| 1072 | + return; | ||
| 1073 | + } | ||
| 1074 | + else | ||
| 1075 | + os.Put(*p++); | ||
| 1076 | + | ||
| 1077 | + // The rest of string using SIMD | ||
| 1078 | + static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; | ||
| 1079 | + static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; | ||
| 1080 | + static const char space[16] = { 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F }; | ||
| 1081 | + const __m128i dq = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&dquote[0])); | ||
| 1082 | + const __m128i bs = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&bslash[0])); | ||
| 1083 | + const __m128i sp = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&space[0])); | ||
| 1084 | + | ||
| 1085 | + for (;; p += 16) { | ||
| 1086 | + const __m128i s = _mm_load_si128(reinterpret_cast<const __m128i *>(p)); | ||
| 1087 | + const __m128i t1 = _mm_cmpeq_epi8(s, dq); | ||
| 1088 | + const __m128i t2 = _mm_cmpeq_epi8(s, bs); | ||
| 1089 | + const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x1F) == 0x1F | ||
| 1090 | + const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3); | ||
| 1091 | + unsigned short r = static_cast<unsigned short>(_mm_movemask_epi8(x)); | ||
| 1092 | + if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped | ||
| 1093 | + SizeType length; | ||
| 1094 | + #ifdef _MSC_VER // Find the index of first escaped | ||
| 1095 | + unsigned long offset; | ||
| 1096 | + _BitScanForward(&offset, r); | ||
| 1097 | + length = offset; | ||
| 1098 | + #else | ||
| 1099 | + length = static_cast<SizeType>(__builtin_ffs(r) - 1); | ||
| 1100 | + #endif | ||
| 1101 | + if (length != 0) { | ||
| 1102 | + char* q = reinterpret_cast<char*>(os.Push(length)); | ||
| 1103 | + for (size_t i = 0; i < length; i++) | ||
| 1104 | + q[i] = p[i]; | ||
| 1105 | + | ||
| 1106 | + p += length; | ||
| 1107 | + } | ||
| 1108 | + break; | ||
| 1109 | + } | ||
| 1110 | + _mm_storeu_si128(reinterpret_cast<__m128i *>(os.Push(16)), s); | ||
| 1111 | + } | ||
| 1112 | + | ||
| 1113 | + is.src_ = p; | ||
| 1114 | + } | ||
| 1115 | + | ||
| 1116 | + // InsituStringStream -> InsituStringStream | ||
| 1117 | + static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(InsituStringStream& is, InsituStringStream& os) { | ||
| 1118 | + RAPIDJSON_ASSERT(&is == &os); | ||
| 1119 | + (void)os; | ||
| 1120 | + | ||
| 1121 | + if (is.src_ == is.dst_) { | ||
| 1122 | + SkipUnescapedString(is); | ||
| 1123 | + return; | ||
| 1124 | + } | ||
| 1125 | + | ||
| 1126 | + char* p = is.src_; | ||
| 1127 | + char *q = is.dst_; | ||
| 1128 | + | ||
| 1129 | + // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) | ||
| 1130 | + const char* nextAligned = reinterpret_cast<const char*>((reinterpret_cast<size_t>(p) + 15) & static_cast<size_t>(~15)); | ||
| 1131 | + while (p != nextAligned) | ||
| 1132 | + if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast<unsigned>(*p) < 0x20)) { | ||
| 1133 | + is.src_ = p; | ||
| 1134 | + is.dst_ = q; | ||
| 1135 | + return; | ||
| 1136 | + } | ||
| 1137 | + else | ||
| 1138 | + *q++ = *p++; | ||
| 1139 | + | ||
| 1140 | + // The rest of string using SIMD | ||
| 1141 | + static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; | ||
| 1142 | + static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; | ||
| 1143 | + static const char space[16] = { 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F }; | ||
| 1144 | + const __m128i dq = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&dquote[0])); | ||
| 1145 | + const __m128i bs = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&bslash[0])); | ||
| 1146 | + const __m128i sp = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&space[0])); | ||
| 1147 | + | ||
| 1148 | + for (;; p += 16, q += 16) { | ||
| 1149 | + const __m128i s = _mm_load_si128(reinterpret_cast<const __m128i *>(p)); | ||
| 1150 | + const __m128i t1 = _mm_cmpeq_epi8(s, dq); | ||
| 1151 | + const __m128i t2 = _mm_cmpeq_epi8(s, bs); | ||
| 1152 | + const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x1F) == 0x1F | ||
| 1153 | + const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3); | ||
| 1154 | + unsigned short r = static_cast<unsigned short>(_mm_movemask_epi8(x)); | ||
| 1155 | + if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped | ||
| 1156 | + size_t length; | ||
| 1157 | +#ifdef _MSC_VER // Find the index of first escaped | ||
| 1158 | + unsigned long offset; | ||
| 1159 | + _BitScanForward(&offset, r); | ||
| 1160 | + length = offset; | ||
| 1161 | +#else | ||
| 1162 | + length = static_cast<size_t>(__builtin_ffs(r) - 1); | ||
| 1163 | +#endif | ||
| 1164 | + for (const char* pend = p + length; p != pend; ) | ||
| 1165 | + *q++ = *p++; | ||
| 1166 | + break; | ||
| 1167 | + } | ||
| 1168 | + _mm_storeu_si128(reinterpret_cast<__m128i *>(q), s); | ||
| 1169 | + } | ||
| 1170 | + | ||
| 1171 | + is.src_ = p; | ||
| 1172 | + is.dst_ = q; | ||
| 1173 | + } | ||
| 1174 | + | ||
| 1175 | + // When read/write pointers are the same for insitu stream, just skip unescaped characters | ||
| 1176 | + static RAPIDJSON_FORCEINLINE void SkipUnescapedString(InsituStringStream& is) { | ||
| 1177 | + RAPIDJSON_ASSERT(is.src_ == is.dst_); | ||
| 1178 | + char* p = is.src_; | ||
| 1179 | + | ||
| 1180 | + // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) | ||
| 1181 | + const char* nextAligned = reinterpret_cast<const char*>((reinterpret_cast<size_t>(p) + 15) & static_cast<size_t>(~15)); | ||
| 1182 | + for (; p != nextAligned; p++) | ||
| 1183 | + if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast<unsigned>(*p) < 0x20)) { | ||
| 1184 | + is.src_ = is.dst_ = p; | ||
| 1185 | + return; | ||
| 1186 | + } | ||
| 1187 | + | ||
| 1188 | + // The rest of string using SIMD | ||
| 1189 | + static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; | ||
| 1190 | + static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; | ||
| 1191 | + static const char space[16] = { 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F }; | ||
| 1192 | + const __m128i dq = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&dquote[0])); | ||
| 1193 | + const __m128i bs = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&bslash[0])); | ||
| 1194 | + const __m128i sp = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&space[0])); | ||
| 1195 | + | ||
| 1196 | + for (;; p += 16) { | ||
| 1197 | + const __m128i s = _mm_load_si128(reinterpret_cast<const __m128i *>(p)); | ||
| 1198 | + const __m128i t1 = _mm_cmpeq_epi8(s, dq); | ||
| 1199 | + const __m128i t2 = _mm_cmpeq_epi8(s, bs); | ||
| 1200 | + const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x1F) == 0x1F | ||
| 1201 | + const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3); | ||
| 1202 | + unsigned short r = static_cast<unsigned short>(_mm_movemask_epi8(x)); | ||
| 1203 | + if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped | ||
| 1204 | + size_t length; | ||
| 1205 | +#ifdef _MSC_VER // Find the index of first escaped | ||
| 1206 | + unsigned long offset; | ||
| 1207 | + _BitScanForward(&offset, r); | ||
| 1208 | + length = offset; | ||
| 1209 | +#else | ||
| 1210 | + length = static_cast<size_t>(__builtin_ffs(r) - 1); | ||
| 1211 | +#endif | ||
| 1212 | + p += length; | ||
| 1213 | + break; | ||
| 1214 | + } | ||
| 1215 | + } | ||
| 1216 | + | ||
| 1217 | + is.src_ = is.dst_ = p; | ||
| 1218 | + } | ||
| 1219 | +#elif defined(RAPIDJSON_NEON) | ||
| 1220 | + // StringStream -> StackStream<char> | ||
| 1221 | + static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(StringStream& is, StackStream<char>& os) { | ||
| 1222 | + const char* p = is.src_; | ||
| 1223 | + | ||
| 1224 | + // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) | ||
| 1225 | + const char* nextAligned = reinterpret_cast<const char*>((reinterpret_cast<size_t>(p) + 15) & static_cast<size_t>(~15)); | ||
| 1226 | + while (p != nextAligned) | ||
| 1227 | + if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast<unsigned>(*p) < 0x20)) { | ||
| 1228 | + is.src_ = p; | ||
| 1229 | + return; | ||
| 1230 | + } | ||
| 1231 | + else | ||
| 1232 | + os.Put(*p++); | ||
| 1233 | + | ||
| 1234 | + // The rest of string using SIMD | ||
| 1235 | + const uint8x16_t s0 = vmovq_n_u8('"'); | ||
| 1236 | + const uint8x16_t s1 = vmovq_n_u8('\\'); | ||
| 1237 | + const uint8x16_t s2 = vmovq_n_u8('\b'); | ||
| 1238 | + const uint8x16_t s3 = vmovq_n_u8(32); | ||
| 1239 | + | ||
| 1240 | + for (;; p += 16) { | ||
| 1241 | + const uint8x16_t s = vld1q_u8(reinterpret_cast<const uint8_t *>(p)); | ||
| 1242 | + uint8x16_t x = vceqq_u8(s, s0); | ||
| 1243 | + x = vorrq_u8(x, vceqq_u8(s, s1)); | ||
| 1244 | + x = vorrq_u8(x, vceqq_u8(s, s2)); | ||
| 1245 | + x = vorrq_u8(x, vcltq_u8(s, s3)); | ||
| 1246 | + | ||
| 1247 | + x = vrev64q_u8(x); // Rev in 64 | ||
| 1248 | + uint64_t low = vgetq_lane_u64(reinterpret_cast<uint64x2_t>(x), 0); // extract | ||
| 1249 | + uint64_t high = vgetq_lane_u64(reinterpret_cast<uint64x2_t>(x), 1); // extract | ||
| 1250 | + | ||
| 1251 | + SizeType length = 0; | ||
| 1252 | + bool escaped = false; | ||
| 1253 | + if (low == 0) { | ||
| 1254 | + if (high != 0) { | ||
| 1255 | + unsigned lz = (unsigned)__builtin_clzll(high);; | ||
| 1256 | + length = 8 + (lz >> 3); | ||
| 1257 | + escaped = true; | ||
| 1258 | + } | ||
| 1259 | + } else { | ||
| 1260 | + unsigned lz = (unsigned)__builtin_clzll(low);; | ||
| 1261 | + length = lz >> 3; | ||
| 1262 | + escaped = true; | ||
| 1263 | + } | ||
| 1264 | + if (RAPIDJSON_UNLIKELY(escaped)) { // some of characters is escaped | ||
| 1265 | + if (length != 0) { | ||
| 1266 | + char* q = reinterpret_cast<char*>(os.Push(length)); | ||
| 1267 | + for (size_t i = 0; i < length; i++) | ||
| 1268 | + q[i] = p[i]; | ||
| 1269 | + | ||
| 1270 | + p += length; | ||
| 1271 | + } | ||
| 1272 | + break; | ||
| 1273 | + } | ||
| 1274 | + vst1q_u8(reinterpret_cast<uint8_t *>(os.Push(16)), s); | ||
| 1275 | + } | ||
| 1276 | + | ||
| 1277 | + is.src_ = p; | ||
| 1278 | + } | ||
| 1279 | + | ||
| 1280 | + // InsituStringStream -> InsituStringStream | ||
| 1281 | + static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(InsituStringStream& is, InsituStringStream& os) { | ||
| 1282 | + RAPIDJSON_ASSERT(&is == &os); | ||
| 1283 | + (void)os; | ||
| 1284 | + | ||
| 1285 | + if (is.src_ == is.dst_) { | ||
| 1286 | + SkipUnescapedString(is); | ||
| 1287 | + return; | ||
| 1288 | + } | ||
| 1289 | + | ||
| 1290 | + char* p = is.src_; | ||
| 1291 | + char *q = is.dst_; | ||
| 1292 | + | ||
| 1293 | + // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) | ||
| 1294 | + const char* nextAligned = reinterpret_cast<const char*>((reinterpret_cast<size_t>(p) + 15) & static_cast<size_t>(~15)); | ||
| 1295 | + while (p != nextAligned) | ||
| 1296 | + if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast<unsigned>(*p) < 0x20)) { | ||
| 1297 | + is.src_ = p; | ||
| 1298 | + is.dst_ = q; | ||
| 1299 | + return; | ||
| 1300 | + } | ||
| 1301 | + else | ||
| 1302 | + *q++ = *p++; | ||
| 1303 | + | ||
| 1304 | + // The rest of string using SIMD | ||
| 1305 | + const uint8x16_t s0 = vmovq_n_u8('"'); | ||
| 1306 | + const uint8x16_t s1 = vmovq_n_u8('\\'); | ||
| 1307 | + const uint8x16_t s2 = vmovq_n_u8('\b'); | ||
| 1308 | + const uint8x16_t s3 = vmovq_n_u8(32); | ||
| 1309 | + | ||
| 1310 | + for (;; p += 16, q += 16) { | ||
| 1311 | + const uint8x16_t s = vld1q_u8(reinterpret_cast<uint8_t *>(p)); | ||
| 1312 | + uint8x16_t x = vceqq_u8(s, s0); | ||
| 1313 | + x = vorrq_u8(x, vceqq_u8(s, s1)); | ||
| 1314 | + x = vorrq_u8(x, vceqq_u8(s, s2)); | ||
| 1315 | + x = vorrq_u8(x, vcltq_u8(s, s3)); | ||
| 1316 | + | ||
| 1317 | + x = vrev64q_u8(x); // Rev in 64 | ||
| 1318 | + uint64_t low = vgetq_lane_u64(reinterpret_cast<uint64x2_t>(x), 0); // extract | ||
| 1319 | + uint64_t high = vgetq_lane_u64(reinterpret_cast<uint64x2_t>(x), 1); // extract | ||
| 1320 | + | ||
| 1321 | + SizeType length = 0; | ||
| 1322 | + bool escaped = false; | ||
| 1323 | + if (low == 0) { | ||
| 1324 | + if (high != 0) { | ||
| 1325 | + unsigned lz = (unsigned)__builtin_clzll(high); | ||
| 1326 | + length = 8 + (lz >> 3); | ||
| 1327 | + escaped = true; | ||
| 1328 | + } | ||
| 1329 | + } else { | ||
| 1330 | + unsigned lz = (unsigned)__builtin_clzll(low); | ||
| 1331 | + length = lz >> 3; | ||
| 1332 | + escaped = true; | ||
| 1333 | + } | ||
| 1334 | + if (RAPIDJSON_UNLIKELY(escaped)) { // some of characters is escaped | ||
| 1335 | + for (const char* pend = p + length; p != pend; ) { | ||
| 1336 | + *q++ = *p++; | ||
| 1337 | + } | ||
| 1338 | + break; | ||
| 1339 | + } | ||
| 1340 | + vst1q_u8(reinterpret_cast<uint8_t *>(q), s); | ||
| 1341 | + } | ||
| 1342 | + | ||
| 1343 | + is.src_ = p; | ||
| 1344 | + is.dst_ = q; | ||
| 1345 | + } | ||
| 1346 | + | ||
| 1347 | + // When read/write pointers are the same for insitu stream, just skip unescaped characters | ||
| 1348 | + static RAPIDJSON_FORCEINLINE void SkipUnescapedString(InsituStringStream& is) { | ||
| 1349 | + RAPIDJSON_ASSERT(is.src_ == is.dst_); | ||
| 1350 | + char* p = is.src_; | ||
| 1351 | + | ||
| 1352 | + // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) | ||
| 1353 | + const char* nextAligned = reinterpret_cast<const char*>((reinterpret_cast<size_t>(p) + 15) & static_cast<size_t>(~15)); | ||
| 1354 | + for (; p != nextAligned; p++) | ||
| 1355 | + if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast<unsigned>(*p) < 0x20)) { | ||
| 1356 | + is.src_ = is.dst_ = p; | ||
| 1357 | + return; | ||
| 1358 | + } | ||
| 1359 | + | ||
| 1360 | + // The rest of string using SIMD | ||
| 1361 | + const uint8x16_t s0 = vmovq_n_u8('"'); | ||
| 1362 | + const uint8x16_t s1 = vmovq_n_u8('\\'); | ||
| 1363 | + const uint8x16_t s2 = vmovq_n_u8('\b'); | ||
| 1364 | + const uint8x16_t s3 = vmovq_n_u8(32); | ||
| 1365 | + | ||
| 1366 | + for (;; p += 16) { | ||
| 1367 | + const uint8x16_t s = vld1q_u8(reinterpret_cast<uint8_t *>(p)); | ||
| 1368 | + uint8x16_t x = vceqq_u8(s, s0); | ||
| 1369 | + x = vorrq_u8(x, vceqq_u8(s, s1)); | ||
| 1370 | + x = vorrq_u8(x, vceqq_u8(s, s2)); | ||
| 1371 | + x = vorrq_u8(x, vcltq_u8(s, s3)); | ||
| 1372 | + | ||
| 1373 | + x = vrev64q_u8(x); // Rev in 64 | ||
| 1374 | + uint64_t low = vgetq_lane_u64(reinterpret_cast<uint64x2_t>(x), 0); // extract | ||
| 1375 | + uint64_t high = vgetq_lane_u64(reinterpret_cast<uint64x2_t>(x), 1); // extract | ||
| 1376 | + | ||
| 1377 | + if (low == 0) { | ||
| 1378 | + if (high != 0) { | ||
| 1379 | + int lz = __builtin_clzll(high); | ||
| 1380 | + p += 8 + (lz >> 3); | ||
| 1381 | + break; | ||
| 1382 | + } | ||
| 1383 | + } else { | ||
| 1384 | + int lz = __builtin_clzll(low); | ||
| 1385 | + p += lz >> 3; | ||
| 1386 | + break; | ||
| 1387 | + } | ||
| 1388 | + } | ||
| 1389 | + | ||
| 1390 | + is.src_ = is.dst_ = p; | ||
| 1391 | + } | ||
| 1392 | +#endif // RAPIDJSON_NEON | ||
| 1393 | + | ||
| 1394 | + template<typename InputStream, bool backup, bool pushOnTake> | ||
| 1395 | + class NumberStream; | ||
| 1396 | + | ||
| 1397 | + template<typename InputStream> | ||
| 1398 | + class NumberStream<InputStream, false, false> { | ||
| 1399 | + public: | ||
| 1400 | + typedef typename InputStream::Ch Ch; | ||
| 1401 | + | ||
| 1402 | + NumberStream(GenericReader& reader, InputStream& s) : is(s) { (void)reader; } | ||
| 1403 | + | ||
| 1404 | + RAPIDJSON_FORCEINLINE Ch Peek() const { return is.Peek(); } | ||
| 1405 | + RAPIDJSON_FORCEINLINE Ch TakePush() { return is.Take(); } | ||
| 1406 | + RAPIDJSON_FORCEINLINE Ch Take() { return is.Take(); } | ||
| 1407 | + RAPIDJSON_FORCEINLINE void Push(char) {} | ||
| 1408 | + | ||
| 1409 | + size_t Tell() { return is.Tell(); } | ||
| 1410 | + size_t Length() { return 0; } | ||
| 1411 | + const char* Pop() { return 0; } | ||
| 1412 | + | ||
| 1413 | + protected: | ||
| 1414 | + NumberStream& operator=(const NumberStream&); | ||
| 1415 | + | ||
| 1416 | + InputStream& is; | ||
| 1417 | + }; | ||
| 1418 | + | ||
| 1419 | + template<typename InputStream> | ||
| 1420 | + class NumberStream<InputStream, true, false> : public NumberStream<InputStream, false, false> { | ||
| 1421 | + typedef NumberStream<InputStream, false, false> Base; | ||
| 1422 | + public: | ||
| 1423 | + NumberStream(GenericReader& reader, InputStream& is) : Base(reader, is), stackStream(reader.stack_) {} | ||
| 1424 | + | ||
| 1425 | + RAPIDJSON_FORCEINLINE Ch TakePush() { | ||
| 1426 | + stackStream.Put(static_cast<char>(Base::is.Peek())); | ||
| 1427 | + return Base::is.Take(); | ||
| 1428 | + } | ||
| 1429 | + | ||
| 1430 | + RAPIDJSON_FORCEINLINE void Push(char c) { | ||
| 1431 | + stackStream.Put(c); | ||
| 1432 | + } | ||
| 1433 | + | ||
| 1434 | + size_t Length() { return stackStream.Length(); } | ||
| 1435 | + | ||
| 1436 | + const char* Pop() { | ||
| 1437 | + stackStream.Put('\0'); | ||
| 1438 | + return stackStream.Pop(); | ||
| 1439 | + } | ||
| 1440 | + | ||
| 1441 | + private: | ||
| 1442 | + StackStream<char> stackStream; | ||
| 1443 | + }; | ||
| 1444 | + | ||
| 1445 | + template<typename InputStream> | ||
| 1446 | + class NumberStream<InputStream, true, true> : public NumberStream<InputStream, true, false> { | ||
| 1447 | + typedef NumberStream<InputStream, true, false> Base; | ||
| 1448 | + public: | ||
| 1449 | + NumberStream(GenericReader& reader, InputStream& is) : Base(reader, is) {} | ||
| 1450 | + | ||
| 1451 | + RAPIDJSON_FORCEINLINE Ch Take() { return Base::TakePush(); } | ||
| 1452 | + }; | ||
| 1453 | + | ||
| 1454 | + template<unsigned parseFlags, typename InputStream, typename Handler> | ||
| 1455 | + void ParseNumber(InputStream& is, Handler& handler) { | ||
| 1456 | + internal::StreamLocalCopy<InputStream> copy(is); | ||
| 1457 | + NumberStream<InputStream, | ||
| 1458 | + ((parseFlags & kParseNumbersAsStringsFlag) != 0) ? | ||
| 1459 | + ((parseFlags & kParseInsituFlag) == 0) : | ||
| 1460 | + ((parseFlags & kParseFullPrecisionFlag) != 0), | ||
| 1461 | + (parseFlags & kParseNumbersAsStringsFlag) != 0 && | ||
| 1462 | + (parseFlags & kParseInsituFlag) == 0> s(*this, copy.s); | ||
| 1463 | + | ||
| 1464 | + size_t startOffset = s.Tell(); | ||
| 1465 | + double d = 0.0; | ||
| 1466 | + bool useNanOrInf = false; | ||
| 1467 | + | ||
| 1468 | + // Parse minus | ||
| 1469 | + bool minus = Consume(s, '-'); | ||
| 1470 | + | ||
| 1471 | + // Parse int: zero / ( digit1-9 *DIGIT ) | ||
| 1472 | + unsigned i = 0; | ||
| 1473 | + uint64_t i64 = 0; | ||
| 1474 | + bool use64bit = false; | ||
| 1475 | + int significandDigit = 0; | ||
| 1476 | + if (RAPIDJSON_UNLIKELY(s.Peek() == '0')) { | ||
| 1477 | + i = 0; | ||
| 1478 | + s.TakePush(); | ||
| 1479 | + } | ||
| 1480 | + else if (RAPIDJSON_LIKELY(s.Peek() >= '1' && s.Peek() <= '9')) { | ||
| 1481 | + i = static_cast<unsigned>(s.TakePush() - '0'); | ||
| 1482 | + | ||
| 1483 | + if (minus) | ||
| 1484 | + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { | ||
| 1485 | + if (RAPIDJSON_UNLIKELY(i >= 214748364)) { // 2^31 = 2147483648 | ||
| 1486 | + if (RAPIDJSON_LIKELY(i != 214748364 || s.Peek() > '8')) { | ||
| 1487 | + i64 = i; | ||
| 1488 | + use64bit = true; | ||
| 1489 | + break; | ||
| 1490 | + } | ||
| 1491 | + } | ||
| 1492 | + i = i * 10 + static_cast<unsigned>(s.TakePush() - '0'); | ||
| 1493 | + significandDigit++; | ||
| 1494 | + } | ||
| 1495 | + else | ||
| 1496 | + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { | ||
| 1497 | + if (RAPIDJSON_UNLIKELY(i >= 429496729)) { // 2^32 - 1 = 4294967295 | ||
| 1498 | + if (RAPIDJSON_LIKELY(i != 429496729 || s.Peek() > '5')) { | ||
| 1499 | + i64 = i; | ||
| 1500 | + use64bit = true; | ||
| 1501 | + break; | ||
| 1502 | + } | ||
| 1503 | + } | ||
| 1504 | + i = i * 10 + static_cast<unsigned>(s.TakePush() - '0'); | ||
| 1505 | + significandDigit++; | ||
| 1506 | + } | ||
| 1507 | + } | ||
| 1508 | + // Parse NaN or Infinity here | ||
| 1509 | + else if ((parseFlags & kParseNanAndInfFlag) && RAPIDJSON_LIKELY((s.Peek() == 'I' || s.Peek() == 'N'))) { | ||
| 1510 | + if (Consume(s, 'N')) { | ||
| 1511 | + if (Consume(s, 'a') && Consume(s, 'N')) { | ||
| 1512 | + d = std::numeric_limits<double>::quiet_NaN(); | ||
| 1513 | + useNanOrInf = true; | ||
| 1514 | + } | ||
| 1515 | + } | ||
| 1516 | + else if (RAPIDJSON_LIKELY(Consume(s, 'I'))) { | ||
| 1517 | + if (Consume(s, 'n') && Consume(s, 'f')) { | ||
| 1518 | + d = (minus ? -std::numeric_limits<double>::infinity() : std::numeric_limits<double>::infinity()); | ||
| 1519 | + useNanOrInf = true; | ||
| 1520 | + | ||
| 1521 | + if (RAPIDJSON_UNLIKELY(s.Peek() == 'i' && !(Consume(s, 'i') && Consume(s, 'n') | ||
| 1522 | + && Consume(s, 'i') && Consume(s, 't') && Consume(s, 'y')))) { | ||
| 1523 | + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell()); | ||
| 1524 | + } | ||
| 1525 | + } | ||
| 1526 | + } | ||
| 1527 | + | ||
| 1528 | + if (RAPIDJSON_UNLIKELY(!useNanOrInf)) { | ||
| 1529 | + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell()); | ||
| 1530 | + } | ||
| 1531 | + } | ||
| 1532 | + else | ||
| 1533 | + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell()); | ||
| 1534 | + | ||
| 1535 | + // Parse 64bit int | ||
| 1536 | + bool useDouble = false; | ||
| 1537 | + if (use64bit) { | ||
| 1538 | + if (minus) | ||
| 1539 | + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { | ||
| 1540 | + if (RAPIDJSON_UNLIKELY(i64 >= RAPIDJSON_UINT64_C2(0x0CCCCCCC, 0xCCCCCCCC))) // 2^63 = 9223372036854775808 | ||
| 1541 | + if (RAPIDJSON_LIKELY(i64 != RAPIDJSON_UINT64_C2(0x0CCCCCCC, 0xCCCCCCCC) || s.Peek() > '8')) { | ||
| 1542 | + d = static_cast<double>(i64); | ||
| 1543 | + useDouble = true; | ||
| 1544 | + break; | ||
| 1545 | + } | ||
| 1546 | + i64 = i64 * 10 + static_cast<unsigned>(s.TakePush() - '0'); | ||
| 1547 | + significandDigit++; | ||
| 1548 | + } | ||
| 1549 | + else | ||
| 1550 | + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { | ||
| 1551 | + if (RAPIDJSON_UNLIKELY(i64 >= RAPIDJSON_UINT64_C2(0x19999999, 0x99999999))) // 2^64 - 1 = 18446744073709551615 | ||
| 1552 | + if (RAPIDJSON_LIKELY(i64 != RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) || s.Peek() > '5')) { | ||
| 1553 | + d = static_cast<double>(i64); | ||
| 1554 | + useDouble = true; | ||
| 1555 | + break; | ||
| 1556 | + } | ||
| 1557 | + i64 = i64 * 10 + static_cast<unsigned>(s.TakePush() - '0'); | ||
| 1558 | + significandDigit++; | ||
| 1559 | + } | ||
| 1560 | + } | ||
| 1561 | + | ||
| 1562 | + // Force double for big integer | ||
| 1563 | + if (useDouble) { | ||
| 1564 | + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { | ||
| 1565 | + if (RAPIDJSON_UNLIKELY(d >= 1.7976931348623157e307)) // DBL_MAX / 10.0 | ||
| 1566 | + RAPIDJSON_PARSE_ERROR(kParseErrorNumberTooBig, startOffset); | ||
| 1567 | + d = d * 10 + (s.TakePush() - '0'); | ||
| 1568 | + } | ||
| 1569 | + } | ||
| 1570 | + | ||
| 1571 | + // Parse frac = decimal-point 1*DIGIT | ||
| 1572 | + int expFrac = 0; | ||
| 1573 | + size_t decimalPosition; | ||
| 1574 | + if (Consume(s, '.')) { | ||
| 1575 | + decimalPosition = s.Length(); | ||
| 1576 | + | ||
| 1577 | + if (RAPIDJSON_UNLIKELY(!(s.Peek() >= '0' && s.Peek() <= '9'))) | ||
| 1578 | + RAPIDJSON_PARSE_ERROR(kParseErrorNumberMissFraction, s.Tell()); | ||
| 1579 | + | ||
| 1580 | + if (!useDouble) { | ||
| 1581 | +#if RAPIDJSON_64BIT | ||
| 1582 | + // Use i64 to store significand in 64-bit architecture | ||
| 1583 | + if (!use64bit) | ||
| 1584 | + i64 = i; | ||
| 1585 | + | ||
| 1586 | + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { | ||
| 1587 | + if (i64 > RAPIDJSON_UINT64_C2(0x1FFFFF, 0xFFFFFFFF)) // 2^53 - 1 for fast path | ||
| 1588 | + break; | ||
| 1589 | + else { | ||
| 1590 | + i64 = i64 * 10 + static_cast<unsigned>(s.TakePush() - '0'); | ||
| 1591 | + --expFrac; | ||
| 1592 | + if (i64 != 0) | ||
| 1593 | + significandDigit++; | ||
| 1594 | + } | ||
| 1595 | + } | ||
| 1596 | + | ||
| 1597 | + d = static_cast<double>(i64); | ||
| 1598 | +#else | ||
| 1599 | + // Use double to store significand in 32-bit architecture | ||
| 1600 | + d = static_cast<double>(use64bit ? i64 : i); | ||
| 1601 | +#endif | ||
| 1602 | + useDouble = true; | ||
| 1603 | + } | ||
| 1604 | + | ||
| 1605 | + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { | ||
| 1606 | + if (significandDigit < 17) { | ||
| 1607 | + d = d * 10.0 + (s.TakePush() - '0'); | ||
| 1608 | + --expFrac; | ||
| 1609 | + if (RAPIDJSON_LIKELY(d > 0.0)) | ||
| 1610 | + significandDigit++; | ||
| 1611 | + } | ||
| 1612 | + else | ||
| 1613 | + s.TakePush(); | ||
| 1614 | + } | ||
| 1615 | + } | ||
| 1616 | + else | ||
| 1617 | + decimalPosition = s.Length(); // decimal position at the end of integer. | ||
| 1618 | + | ||
| 1619 | + // Parse exp = e [ minus / plus ] 1*DIGIT | ||
| 1620 | + int exp = 0; | ||
| 1621 | + if (Consume(s, 'e') || Consume(s, 'E')) { | ||
| 1622 | + if (!useDouble) { | ||
| 1623 | + d = static_cast<double>(use64bit ? i64 : i); | ||
| 1624 | + useDouble = true; | ||
| 1625 | + } | ||
| 1626 | + | ||
| 1627 | + bool expMinus = false; | ||
| 1628 | + if (Consume(s, '+')) | ||
| 1629 | + ; | ||
| 1630 | + else if (Consume(s, '-')) | ||
| 1631 | + expMinus = true; | ||
| 1632 | + | ||
| 1633 | + if (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { | ||
| 1634 | + exp = static_cast<int>(s.Take() - '0'); | ||
| 1635 | + if (expMinus) { | ||
| 1636 | + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { | ||
| 1637 | + exp = exp * 10 + static_cast<int>(s.Take() - '0'); | ||
| 1638 | + if (exp >= 214748364) { // Issue #313: prevent overflow exponent | ||
| 1639 | + while (RAPIDJSON_UNLIKELY(s.Peek() >= '0' && s.Peek() <= '9')) // Consume the rest of exponent | ||
| 1640 | + s.Take(); | ||
| 1641 | + } | ||
| 1642 | + } | ||
| 1643 | + } | ||
| 1644 | + else { // positive exp | ||
| 1645 | + int maxExp = 308 - expFrac; | ||
| 1646 | + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { | ||
| 1647 | + exp = exp * 10 + static_cast<int>(s.Take() - '0'); | ||
| 1648 | + if (RAPIDJSON_UNLIKELY(exp > maxExp)) | ||
| 1649 | + RAPIDJSON_PARSE_ERROR(kParseErrorNumberTooBig, startOffset); | ||
| 1650 | + } | ||
| 1651 | + } | ||
| 1652 | + } | ||
| 1653 | + else | ||
| 1654 | + RAPIDJSON_PARSE_ERROR(kParseErrorNumberMissExponent, s.Tell()); | ||
| 1655 | + | ||
| 1656 | + if (expMinus) | ||
| 1657 | + exp = -exp; | ||
| 1658 | + } | ||
| 1659 | + | ||
| 1660 | + // Finish parsing, call event according to the type of number. | ||
| 1661 | + bool cont = true; | ||
| 1662 | + | ||
| 1663 | + if (parseFlags & kParseNumbersAsStringsFlag) { | ||
| 1664 | + if (parseFlags & kParseInsituFlag) { | ||
| 1665 | + s.Pop(); // Pop stack no matter if it will be used or not. | ||
| 1666 | + typename InputStream::Ch* head = is.PutBegin(); | ||
| 1667 | + const size_t length = s.Tell() - startOffset; | ||
| 1668 | + RAPIDJSON_ASSERT(length <= 0xFFFFFFFF); | ||
| 1669 | + // unable to insert the \0 character here, it will erase the comma after this number | ||
| 1670 | + const typename TargetEncoding::Ch* const str = reinterpret_cast<typename TargetEncoding::Ch*>(head); | ||
| 1671 | + cont = handler.RawNumber(str, SizeType(length), false); | ||
| 1672 | + } | ||
| 1673 | + else { | ||
| 1674 | + SizeType numCharsToCopy = static_cast<SizeType>(s.Length()); | ||
| 1675 | + StringStream srcStream(s.Pop()); | ||
| 1676 | + StackStream<typename TargetEncoding::Ch> dstStream(stack_); | ||
| 1677 | + while (numCharsToCopy--) { | ||
| 1678 | + Transcoder<UTF8<>, TargetEncoding>::Transcode(srcStream, dstStream); | ||
| 1679 | + } | ||
| 1680 | + dstStream.Put('\0'); | ||
| 1681 | + const typename TargetEncoding::Ch* str = dstStream.Pop(); | ||
| 1682 | + const SizeType length = static_cast<SizeType>(dstStream.Length()) - 1; | ||
| 1683 | + cont = handler.RawNumber(str, SizeType(length), true); | ||
| 1684 | + } | ||
| 1685 | + } | ||
| 1686 | + else { | ||
| 1687 | + size_t length = s.Length(); | ||
| 1688 | + const char* decimal = s.Pop(); // Pop stack no matter if it will be used or not. | ||
| 1689 | + | ||
| 1690 | + if (useDouble) { | ||
| 1691 | + int p = exp + expFrac; | ||
| 1692 | + if (parseFlags & kParseFullPrecisionFlag) | ||
| 1693 | + d = internal::StrtodFullPrecision(d, p, decimal, length, decimalPosition, exp); | ||
| 1694 | + else | ||
| 1695 | + d = internal::StrtodNormalPrecision(d, p); | ||
| 1696 | + | ||
| 1697 | + cont = handler.Double(minus ? -d : d); | ||
| 1698 | + } | ||
| 1699 | + else if (useNanOrInf) { | ||
| 1700 | + cont = handler.Double(d); | ||
| 1701 | + } | ||
| 1702 | + else { | ||
| 1703 | + if (use64bit) { | ||
| 1704 | + if (minus) | ||
| 1705 | + cont = handler.Int64(static_cast<int64_t>(~i64 + 1)); | ||
| 1706 | + else | ||
| 1707 | + cont = handler.Uint64(i64); | ||
| 1708 | + } | ||
| 1709 | + else { | ||
| 1710 | + if (minus) | ||
| 1711 | + cont = handler.Int(static_cast<int32_t>(~i + 1)); | ||
| 1712 | + else | ||
| 1713 | + cont = handler.Uint(i); | ||
| 1714 | + } | ||
| 1715 | + } | ||
| 1716 | + } | ||
| 1717 | + if (RAPIDJSON_UNLIKELY(!cont)) | ||
| 1718 | + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, startOffset); | ||
| 1719 | + } | ||
| 1720 | + | ||
| 1721 | + // Parse any JSON value | ||
| 1722 | + template<unsigned parseFlags, typename InputStream, typename Handler> | ||
| 1723 | + void ParseValue(InputStream& is, Handler& handler) { | ||
| 1724 | + switch (is.Peek()) { | ||
| 1725 | + case 'n': ParseNull <parseFlags>(is, handler); break; | ||
| 1726 | + case 't': ParseTrue <parseFlags>(is, handler); break; | ||
| 1727 | + case 'f': ParseFalse <parseFlags>(is, handler); break; | ||
| 1728 | + case '"': ParseString<parseFlags>(is, handler); break; | ||
| 1729 | + case '{': ParseObject<parseFlags>(is, handler); break; | ||
| 1730 | + case '[': ParseArray <parseFlags>(is, handler); break; | ||
| 1731 | + default : | ||
| 1732 | + ParseNumber<parseFlags>(is, handler); | ||
| 1733 | + break; | ||
| 1734 | + | ||
| 1735 | + } | ||
| 1736 | + } | ||
| 1737 | + | ||
| 1738 | + // Iterative Parsing | ||
| 1739 | + | ||
| 1740 | + // States | ||
| 1741 | + enum IterativeParsingState { | ||
| 1742 | + IterativeParsingFinishState = 0, // sink states at top | ||
| 1743 | + IterativeParsingErrorState, // sink states at top | ||
| 1744 | + IterativeParsingStartState, | ||
| 1745 | + | ||
| 1746 | + // Object states | ||
| 1747 | + IterativeParsingObjectInitialState, | ||
| 1748 | + IterativeParsingMemberKeyState, | ||
| 1749 | + IterativeParsingMemberValueState, | ||
| 1750 | + IterativeParsingObjectFinishState, | ||
| 1751 | + | ||
| 1752 | + // Array states | ||
| 1753 | + IterativeParsingArrayInitialState, | ||
| 1754 | + IterativeParsingElementState, | ||
| 1755 | + IterativeParsingArrayFinishState, | ||
| 1756 | + | ||
| 1757 | + // Single value state | ||
| 1758 | + IterativeParsingValueState, | ||
| 1759 | + | ||
| 1760 | + // Delimiter states (at bottom) | ||
| 1761 | + IterativeParsingElementDelimiterState, | ||
| 1762 | + IterativeParsingMemberDelimiterState, | ||
| 1763 | + IterativeParsingKeyValueDelimiterState, | ||
| 1764 | + | ||
| 1765 | + cIterativeParsingStateCount | ||
| 1766 | + }; | ||
| 1767 | + | ||
| 1768 | + // Tokens | ||
| 1769 | + enum Token { | ||
| 1770 | + LeftBracketToken = 0, | ||
| 1771 | + RightBracketToken, | ||
| 1772 | + | ||
| 1773 | + LeftCurlyBracketToken, | ||
| 1774 | + RightCurlyBracketToken, | ||
| 1775 | + | ||
| 1776 | + CommaToken, | ||
| 1777 | + ColonToken, | ||
| 1778 | + | ||
| 1779 | + StringToken, | ||
| 1780 | + FalseToken, | ||
| 1781 | + TrueToken, | ||
| 1782 | + NullToken, | ||
| 1783 | + NumberToken, | ||
| 1784 | + | ||
| 1785 | + kTokenCount | ||
| 1786 | + }; | ||
| 1787 | + | ||
| 1788 | + RAPIDJSON_FORCEINLINE Token Tokenize(Ch c) { | ||
| 1789 | + | ||
| 1790 | +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN | ||
| 1791 | +#define N NumberToken | ||
| 1792 | +#define N16 N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N | ||
| 1793 | + // Maps from ASCII to Token | ||
| 1794 | + static const unsigned char tokenMap[256] = { | ||
| 1795 | + N16, // 00~0F | ||
| 1796 | + N16, // 10~1F | ||
| 1797 | + N, N, StringToken, N, N, N, N, N, N, N, N, N, CommaToken, N, N, N, // 20~2F | ||
| 1798 | + N, N, N, N, N, N, N, N, N, N, ColonToken, N, N, N, N, N, // 30~3F | ||
| 1799 | + N16, // 40~4F | ||
| 1800 | + N, N, N, N, N, N, N, N, N, N, N, LeftBracketToken, N, RightBracketToken, N, N, // 50~5F | ||
| 1801 | + N, N, N, N, N, N, FalseToken, N, N, N, N, N, N, N, NullToken, N, // 60~6F | ||
| 1802 | + N, N, N, N, TrueToken, N, N, N, N, N, N, LeftCurlyBracketToken, N, RightCurlyBracketToken, N, N, // 70~7F | ||
| 1803 | + N16, N16, N16, N16, N16, N16, N16, N16 // 80~FF | ||
| 1804 | + }; | ||
| 1805 | +#undef N | ||
| 1806 | +#undef N16 | ||
| 1807 | +//!@endcond | ||
| 1808 | + | ||
| 1809 | + if (sizeof(Ch) == 1 || static_cast<unsigned>(c) < 256) | ||
| 1810 | + return static_cast<Token>(tokenMap[static_cast<unsigned char>(c)]); | ||
| 1811 | + else | ||
| 1812 | + return NumberToken; | ||
| 1813 | + } | ||
| 1814 | + | ||
| 1815 | + RAPIDJSON_FORCEINLINE IterativeParsingState Predict(IterativeParsingState state, Token token) { | ||
| 1816 | + // current state x one lookahead token -> new state | ||
| 1817 | + static const char G[cIterativeParsingStateCount][kTokenCount] = { | ||
| 1818 | + // Finish(sink state) | ||
| 1819 | + { | ||
| 1820 | + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, | ||
| 1821 | + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, | ||
| 1822 | + IterativeParsingErrorState | ||
| 1823 | + }, | ||
| 1824 | + // Error(sink state) | ||
| 1825 | + { | ||
| 1826 | + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, | ||
| 1827 | + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, | ||
| 1828 | + IterativeParsingErrorState | ||
| 1829 | + }, | ||
| 1830 | + // Start | ||
| 1831 | + { | ||
| 1832 | + IterativeParsingArrayInitialState, // Left bracket | ||
| 1833 | + IterativeParsingErrorState, // Right bracket | ||
| 1834 | + IterativeParsingObjectInitialState, // Left curly bracket | ||
| 1835 | + IterativeParsingErrorState, // Right curly bracket | ||
| 1836 | + IterativeParsingErrorState, // Comma | ||
| 1837 | + IterativeParsingErrorState, // Colon | ||
| 1838 | + IterativeParsingValueState, // String | ||
| 1839 | + IterativeParsingValueState, // False | ||
| 1840 | + IterativeParsingValueState, // True | ||
| 1841 | + IterativeParsingValueState, // Null | ||
| 1842 | + IterativeParsingValueState // Number | ||
| 1843 | + }, | ||
| 1844 | + // ObjectInitial | ||
| 1845 | + { | ||
| 1846 | + IterativeParsingErrorState, // Left bracket | ||
| 1847 | + IterativeParsingErrorState, // Right bracket | ||
| 1848 | + IterativeParsingErrorState, // Left curly bracket | ||
| 1849 | + IterativeParsingObjectFinishState, // Right curly bracket | ||
| 1850 | + IterativeParsingErrorState, // Comma | ||
| 1851 | + IterativeParsingErrorState, // Colon | ||
| 1852 | + IterativeParsingMemberKeyState, // String | ||
| 1853 | + IterativeParsingErrorState, // False | ||
| 1854 | + IterativeParsingErrorState, // True | ||
| 1855 | + IterativeParsingErrorState, // Null | ||
| 1856 | + IterativeParsingErrorState // Number | ||
| 1857 | + }, | ||
| 1858 | + // MemberKey | ||
| 1859 | + { | ||
| 1860 | + IterativeParsingErrorState, // Left bracket | ||
| 1861 | + IterativeParsingErrorState, // Right bracket | ||
| 1862 | + IterativeParsingErrorState, // Left curly bracket | ||
| 1863 | + IterativeParsingErrorState, // Right curly bracket | ||
| 1864 | + IterativeParsingErrorState, // Comma | ||
| 1865 | + IterativeParsingKeyValueDelimiterState, // Colon | ||
| 1866 | + IterativeParsingErrorState, // String | ||
| 1867 | + IterativeParsingErrorState, // False | ||
| 1868 | + IterativeParsingErrorState, // True | ||
| 1869 | + IterativeParsingErrorState, // Null | ||
| 1870 | + IterativeParsingErrorState // Number | ||
| 1871 | + }, | ||
| 1872 | + // MemberValue | ||
| 1873 | + { | ||
| 1874 | + IterativeParsingErrorState, // Left bracket | ||
| 1875 | + IterativeParsingErrorState, // Right bracket | ||
| 1876 | + IterativeParsingErrorState, // Left curly bracket | ||
| 1877 | + IterativeParsingObjectFinishState, // Right curly bracket | ||
| 1878 | + IterativeParsingMemberDelimiterState, // Comma | ||
| 1879 | + IterativeParsingErrorState, // Colon | ||
| 1880 | + IterativeParsingErrorState, // String | ||
| 1881 | + IterativeParsingErrorState, // False | ||
| 1882 | + IterativeParsingErrorState, // True | ||
| 1883 | + IterativeParsingErrorState, // Null | ||
| 1884 | + IterativeParsingErrorState // Number | ||
| 1885 | + }, | ||
| 1886 | + // ObjectFinish(sink state) | ||
| 1887 | + { | ||
| 1888 | + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, | ||
| 1889 | + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, | ||
| 1890 | + IterativeParsingErrorState | ||
| 1891 | + }, | ||
| 1892 | + // ArrayInitial | ||
| 1893 | + { | ||
| 1894 | + IterativeParsingArrayInitialState, // Left bracket(push Element state) | ||
| 1895 | + IterativeParsingArrayFinishState, // Right bracket | ||
| 1896 | + IterativeParsingObjectInitialState, // Left curly bracket(push Element state) | ||
| 1897 | + IterativeParsingErrorState, // Right curly bracket | ||
| 1898 | + IterativeParsingErrorState, // Comma | ||
| 1899 | + IterativeParsingErrorState, // Colon | ||
| 1900 | + IterativeParsingElementState, // String | ||
| 1901 | + IterativeParsingElementState, // False | ||
| 1902 | + IterativeParsingElementState, // True | ||
| 1903 | + IterativeParsingElementState, // Null | ||
| 1904 | + IterativeParsingElementState // Number | ||
| 1905 | + }, | ||
| 1906 | + // Element | ||
| 1907 | + { | ||
| 1908 | + IterativeParsingErrorState, // Left bracket | ||
| 1909 | + IterativeParsingArrayFinishState, // Right bracket | ||
| 1910 | + IterativeParsingErrorState, // Left curly bracket | ||
| 1911 | + IterativeParsingErrorState, // Right curly bracket | ||
| 1912 | + IterativeParsingElementDelimiterState, // Comma | ||
| 1913 | + IterativeParsingErrorState, // Colon | ||
| 1914 | + IterativeParsingErrorState, // String | ||
| 1915 | + IterativeParsingErrorState, // False | ||
| 1916 | + IterativeParsingErrorState, // True | ||
| 1917 | + IterativeParsingErrorState, // Null | ||
| 1918 | + IterativeParsingErrorState // Number | ||
| 1919 | + }, | ||
| 1920 | + // ArrayFinish(sink state) | ||
| 1921 | + { | ||
| 1922 | + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, | ||
| 1923 | + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, | ||
| 1924 | + IterativeParsingErrorState | ||
| 1925 | + }, | ||
| 1926 | + // Single Value (sink state) | ||
| 1927 | + { | ||
| 1928 | + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, | ||
| 1929 | + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, | ||
| 1930 | + IterativeParsingErrorState | ||
| 1931 | + }, | ||
| 1932 | + // ElementDelimiter | ||
| 1933 | + { | ||
| 1934 | + IterativeParsingArrayInitialState, // Left bracket(push Element state) | ||
| 1935 | + IterativeParsingArrayFinishState, // Right bracket | ||
| 1936 | + IterativeParsingObjectInitialState, // Left curly bracket(push Element state) | ||
| 1937 | + IterativeParsingErrorState, // Right curly bracket | ||
| 1938 | + IterativeParsingErrorState, // Comma | ||
| 1939 | + IterativeParsingErrorState, // Colon | ||
| 1940 | + IterativeParsingElementState, // String | ||
| 1941 | + IterativeParsingElementState, // False | ||
| 1942 | + IterativeParsingElementState, // True | ||
| 1943 | + IterativeParsingElementState, // Null | ||
| 1944 | + IterativeParsingElementState // Number | ||
| 1945 | + }, | ||
| 1946 | + // MemberDelimiter | ||
| 1947 | + { | ||
| 1948 | + IterativeParsingErrorState, // Left bracket | ||
| 1949 | + IterativeParsingErrorState, // Right bracket | ||
| 1950 | + IterativeParsingErrorState, // Left curly bracket | ||
| 1951 | + IterativeParsingObjectFinishState, // Right curly bracket | ||
| 1952 | + IterativeParsingErrorState, // Comma | ||
| 1953 | + IterativeParsingErrorState, // Colon | ||
| 1954 | + IterativeParsingMemberKeyState, // String | ||
| 1955 | + IterativeParsingErrorState, // False | ||
| 1956 | + IterativeParsingErrorState, // True | ||
| 1957 | + IterativeParsingErrorState, // Null | ||
| 1958 | + IterativeParsingErrorState // Number | ||
| 1959 | + }, | ||
| 1960 | + // KeyValueDelimiter | ||
| 1961 | + { | ||
| 1962 | + IterativeParsingArrayInitialState, // Left bracket(push MemberValue state) | ||
| 1963 | + IterativeParsingErrorState, // Right bracket | ||
| 1964 | + IterativeParsingObjectInitialState, // Left curly bracket(push MemberValue state) | ||
| 1965 | + IterativeParsingErrorState, // Right curly bracket | ||
| 1966 | + IterativeParsingErrorState, // Comma | ||
| 1967 | + IterativeParsingErrorState, // Colon | ||
| 1968 | + IterativeParsingMemberValueState, // String | ||
| 1969 | + IterativeParsingMemberValueState, // False | ||
| 1970 | + IterativeParsingMemberValueState, // True | ||
| 1971 | + IterativeParsingMemberValueState, // Null | ||
| 1972 | + IterativeParsingMemberValueState // Number | ||
| 1973 | + }, | ||
| 1974 | + }; // End of G | ||
| 1975 | + | ||
| 1976 | + return static_cast<IterativeParsingState>(G[state][token]); | ||
| 1977 | + } | ||
| 1978 | + | ||
| 1979 | + // Make an advance in the token stream and state based on the candidate destination state which was returned by Transit(). | ||
| 1980 | + // May return a new state on state pop. | ||
| 1981 | + template <unsigned parseFlags, typename InputStream, typename Handler> | ||
| 1982 | + RAPIDJSON_FORCEINLINE IterativeParsingState Transit(IterativeParsingState src, Token token, IterativeParsingState dst, InputStream& is, Handler& handler) { | ||
| 1983 | + (void)token; | ||
| 1984 | + | ||
| 1985 | + switch (dst) { | ||
| 1986 | + case IterativeParsingErrorState: | ||
| 1987 | + return dst; | ||
| 1988 | + | ||
| 1989 | + case IterativeParsingObjectInitialState: | ||
| 1990 | + case IterativeParsingArrayInitialState: | ||
| 1991 | + { | ||
| 1992 | + // Push the state(Element or MemeberValue) if we are nested in another array or value of member. | ||
| 1993 | + // In this way we can get the correct state on ObjectFinish or ArrayFinish by frame pop. | ||
| 1994 | + IterativeParsingState n = src; | ||
| 1995 | + if (src == IterativeParsingArrayInitialState || src == IterativeParsingElementDelimiterState) | ||
| 1996 | + n = IterativeParsingElementState; | ||
| 1997 | + else if (src == IterativeParsingKeyValueDelimiterState) | ||
| 1998 | + n = IterativeParsingMemberValueState; | ||
| 1999 | + // Push current state. | ||
| 2000 | + *stack_.template Push<SizeType>(1) = n; | ||
| 2001 | + // Initialize and push the member/element count. | ||
| 2002 | + *stack_.template Push<SizeType>(1) = 0; | ||
| 2003 | + // Call handler | ||
| 2004 | + bool hr = (dst == IterativeParsingObjectInitialState) ? handler.StartObject() : handler.StartArray(); | ||
| 2005 | + // On handler short circuits the parsing. | ||
| 2006 | + if (!hr) { | ||
| 2007 | + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorTermination, is.Tell()); | ||
| 2008 | + return IterativeParsingErrorState; | ||
| 2009 | + } | ||
| 2010 | + else { | ||
| 2011 | + is.Take(); | ||
| 2012 | + return dst; | ||
| 2013 | + } | ||
| 2014 | + } | ||
| 2015 | + | ||
| 2016 | + case IterativeParsingMemberKeyState: | ||
| 2017 | + ParseString<parseFlags>(is, handler, true); | ||
| 2018 | + if (HasParseError()) | ||
| 2019 | + return IterativeParsingErrorState; | ||
| 2020 | + else | ||
| 2021 | + return dst; | ||
| 2022 | + | ||
| 2023 | + case IterativeParsingKeyValueDelimiterState: | ||
| 2024 | + RAPIDJSON_ASSERT(token == ColonToken); | ||
| 2025 | + is.Take(); | ||
| 2026 | + return dst; | ||
| 2027 | + | ||
| 2028 | + case IterativeParsingMemberValueState: | ||
| 2029 | + // Must be non-compound value. Or it would be ObjectInitial or ArrayInitial state. | ||
| 2030 | + ParseValue<parseFlags>(is, handler); | ||
| 2031 | + if (HasParseError()) { | ||
| 2032 | + return IterativeParsingErrorState; | ||
| 2033 | + } | ||
| 2034 | + return dst; | ||
| 2035 | + | ||
| 2036 | + case IterativeParsingElementState: | ||
| 2037 | + // Must be non-compound value. Or it would be ObjectInitial or ArrayInitial state. | ||
| 2038 | + ParseValue<parseFlags>(is, handler); | ||
| 2039 | + if (HasParseError()) { | ||
| 2040 | + return IterativeParsingErrorState; | ||
| 2041 | + } | ||
| 2042 | + return dst; | ||
| 2043 | + | ||
| 2044 | + case IterativeParsingMemberDelimiterState: | ||
| 2045 | + case IterativeParsingElementDelimiterState: | ||
| 2046 | + is.Take(); | ||
| 2047 | + // Update member/element count. | ||
| 2048 | + *stack_.template Top<SizeType>() = *stack_.template Top<SizeType>() + 1; | ||
| 2049 | + return dst; | ||
| 2050 | + | ||
| 2051 | + case IterativeParsingObjectFinishState: | ||
| 2052 | + { | ||
| 2053 | + // Transit from delimiter is only allowed when trailing commas are enabled | ||
| 2054 | + if (!(parseFlags & kParseTrailingCommasFlag) && src == IterativeParsingMemberDelimiterState) { | ||
| 2055 | + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorObjectMissName, is.Tell()); | ||
| 2056 | + return IterativeParsingErrorState; | ||
| 2057 | + } | ||
| 2058 | + // Get member count. | ||
| 2059 | + SizeType c = *stack_.template Pop<SizeType>(1); | ||
| 2060 | + // If the object is not empty, count the last member. | ||
| 2061 | + if (src == IterativeParsingMemberValueState) | ||
| 2062 | + ++c; | ||
| 2063 | + // Restore the state. | ||
| 2064 | + IterativeParsingState n = static_cast<IterativeParsingState>(*stack_.template Pop<SizeType>(1)); | ||
| 2065 | + // Transit to Finish state if this is the topmost scope. | ||
| 2066 | + if (n == IterativeParsingStartState) | ||
| 2067 | + n = IterativeParsingFinishState; | ||
| 2068 | + // Call handler | ||
| 2069 | + bool hr = handler.EndObject(c); | ||
| 2070 | + // On handler short circuits the parsing. | ||
| 2071 | + if (!hr) { | ||
| 2072 | + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorTermination, is.Tell()); | ||
| 2073 | + return IterativeParsingErrorState; | ||
| 2074 | + } | ||
| 2075 | + else { | ||
| 2076 | + is.Take(); | ||
| 2077 | + return n; | ||
| 2078 | + } | ||
| 2079 | + } | ||
| 2080 | + | ||
| 2081 | + case IterativeParsingArrayFinishState: | ||
| 2082 | + { | ||
| 2083 | + // Transit from delimiter is only allowed when trailing commas are enabled | ||
| 2084 | + if (!(parseFlags & kParseTrailingCommasFlag) && src == IterativeParsingElementDelimiterState) { | ||
| 2085 | + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorValueInvalid, is.Tell()); | ||
| 2086 | + return IterativeParsingErrorState; | ||
| 2087 | + } | ||
| 2088 | + // Get element count. | ||
| 2089 | + SizeType c = *stack_.template Pop<SizeType>(1); | ||
| 2090 | + // If the array is not empty, count the last element. | ||
| 2091 | + if (src == IterativeParsingElementState) | ||
| 2092 | + ++c; | ||
| 2093 | + // Restore the state. | ||
| 2094 | + IterativeParsingState n = static_cast<IterativeParsingState>(*stack_.template Pop<SizeType>(1)); | ||
| 2095 | + // Transit to Finish state if this is the topmost scope. | ||
| 2096 | + if (n == IterativeParsingStartState) | ||
| 2097 | + n = IterativeParsingFinishState; | ||
| 2098 | + // Call handler | ||
| 2099 | + bool hr = handler.EndArray(c); | ||
| 2100 | + // On handler short circuits the parsing. | ||
| 2101 | + if (!hr) { | ||
| 2102 | + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorTermination, is.Tell()); | ||
| 2103 | + return IterativeParsingErrorState; | ||
| 2104 | + } | ||
| 2105 | + else { | ||
| 2106 | + is.Take(); | ||
| 2107 | + return n; | ||
| 2108 | + } | ||
| 2109 | + } | ||
| 2110 | + | ||
| 2111 | + default: | ||
| 2112 | + // This branch is for IterativeParsingValueState actually. | ||
| 2113 | + // Use `default:` rather than | ||
| 2114 | + // `case IterativeParsingValueState:` is for code coverage. | ||
| 2115 | + | ||
| 2116 | + // The IterativeParsingStartState is not enumerated in this switch-case. | ||
| 2117 | + // It is impossible for that case. And it can be caught by following assertion. | ||
| 2118 | + | ||
| 2119 | + // The IterativeParsingFinishState is not enumerated in this switch-case either. | ||
| 2120 | + // It is a "derivative" state which cannot triggered from Predict() directly. | ||
| 2121 | + // Therefore it cannot happen here. And it can be caught by following assertion. | ||
| 2122 | + RAPIDJSON_ASSERT(dst == IterativeParsingValueState); | ||
| 2123 | + | ||
| 2124 | + // Must be non-compound value. Or it would be ObjectInitial or ArrayInitial state. | ||
| 2125 | + ParseValue<parseFlags>(is, handler); | ||
| 2126 | + if (HasParseError()) { | ||
| 2127 | + return IterativeParsingErrorState; | ||
| 2128 | + } | ||
| 2129 | + return IterativeParsingFinishState; | ||
| 2130 | + } | ||
| 2131 | + } | ||
| 2132 | + | ||
| 2133 | + template <typename InputStream> | ||
| 2134 | + void HandleError(IterativeParsingState src, InputStream& is) { | ||
| 2135 | + if (HasParseError()) { | ||
| 2136 | + // Error flag has been set. | ||
| 2137 | + return; | ||
| 2138 | + } | ||
| 2139 | + | ||
| 2140 | + switch (src) { | ||
| 2141 | + case IterativeParsingStartState: RAPIDJSON_PARSE_ERROR(kParseErrorDocumentEmpty, is.Tell()); return; | ||
| 2142 | + case IterativeParsingFinishState: RAPIDJSON_PARSE_ERROR(kParseErrorDocumentRootNotSingular, is.Tell()); return; | ||
| 2143 | + case IterativeParsingObjectInitialState: | ||
| 2144 | + case IterativeParsingMemberDelimiterState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissName, is.Tell()); return; | ||
| 2145 | + case IterativeParsingMemberKeyState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissColon, is.Tell()); return; | ||
| 2146 | + case IterativeParsingMemberValueState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, is.Tell()); return; | ||
| 2147 | + case IterativeParsingKeyValueDelimiterState: | ||
| 2148 | + case IterativeParsingArrayInitialState: | ||
| 2149 | + case IterativeParsingElementDelimiterState: RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); return; | ||
| 2150 | + default: RAPIDJSON_ASSERT(src == IterativeParsingElementState); RAPIDJSON_PARSE_ERROR(kParseErrorArrayMissCommaOrSquareBracket, is.Tell()); return; | ||
| 2151 | + } | ||
| 2152 | + } | ||
| 2153 | + | ||
| 2154 | + RAPIDJSON_FORCEINLINE bool IsIterativeParsingDelimiterState(IterativeParsingState s) { | ||
| 2155 | + return s >= IterativeParsingElementDelimiterState; | ||
| 2156 | + } | ||
| 2157 | + | ||
| 2158 | + RAPIDJSON_FORCEINLINE bool IsIterativeParsingCompleteState(IterativeParsingState s) { | ||
| 2159 | + return s <= IterativeParsingErrorState; | ||
| 2160 | + } | ||
| 2161 | + | ||
| 2162 | + template <unsigned parseFlags, typename InputStream, typename Handler> | ||
| 2163 | + ParseResult IterativeParse(InputStream& is, Handler& handler) { | ||
| 2164 | + parseResult_.Clear(); | ||
| 2165 | + ClearStackOnExit scope(*this); | ||
| 2166 | + IterativeParsingState state = IterativeParsingStartState; | ||
| 2167 | + | ||
| 2168 | + SkipWhitespaceAndComments<parseFlags>(is); | ||
| 2169 | + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); | ||
| 2170 | + while (is.Peek() != '\0') { | ||
| 2171 | + Token t = Tokenize(is.Peek()); | ||
| 2172 | + IterativeParsingState n = Predict(state, t); | ||
| 2173 | + IterativeParsingState d = Transit<parseFlags>(state, t, n, is, handler); | ||
| 2174 | + | ||
| 2175 | + if (d == IterativeParsingErrorState) { | ||
| 2176 | + HandleError(state, is); | ||
| 2177 | + break; | ||
| 2178 | + } | ||
| 2179 | + | ||
| 2180 | + state = d; | ||
| 2181 | + | ||
| 2182 | + // Do not further consume streams if a root JSON has been parsed. | ||
| 2183 | + if ((parseFlags & kParseStopWhenDoneFlag) && state == IterativeParsingFinishState) | ||
| 2184 | + break; | ||
| 2185 | + | ||
| 2186 | + SkipWhitespaceAndComments<parseFlags>(is); | ||
| 2187 | + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); | ||
| 2188 | + } | ||
| 2189 | + | ||
| 2190 | + // Handle the end of file. | ||
| 2191 | + if (state != IterativeParsingFinishState) | ||
| 2192 | + HandleError(state, is); | ||
| 2193 | + | ||
| 2194 | + return parseResult_; | ||
| 2195 | + } | ||
| 2196 | + | ||
| 2197 | + static const size_t kDefaultStackCapacity = 256; //!< Default stack capacity in bytes for storing a single decoded string. | ||
| 2198 | + internal::Stack<StackAllocator> stack_; //!< A stack for storing decoded string temporarily during non-destructive parsing. | ||
| 2199 | + ParseResult parseResult_; | ||
| 2200 | + IterativeParsingState state_; | ||
| 2201 | +}; // class GenericReader | ||
| 2202 | + | ||
| 2203 | +//! Reader with UTF8 encoding and default allocator. | ||
| 2204 | +typedef GenericReader<UTF8<>, UTF8<> > Reader; | ||
| 2205 | + | ||
| 2206 | +RAPIDJSON_NAMESPACE_END | ||
| 2207 | + | ||
| 2208 | +#ifdef __clang__ | ||
| 2209 | +RAPIDJSON_DIAG_POP | ||
| 2210 | +#endif | ||
| 2211 | + | ||
| 2212 | + | ||
| 2213 | +#ifdef __GNUC__ | ||
| 2214 | +RAPIDJSON_DIAG_POP | ||
| 2215 | +#endif | ||
| 2216 | + | ||
| 2217 | +#ifdef _MSC_VER | ||
| 2218 | +RAPIDJSON_DIAG_POP | ||
| 2219 | +#endif | ||
| 2220 | + | ||
| 2221 | +#endif // RAPIDJSON_READER_H_ |
PaperGraph/include/rapidjson/schema.h
0 → 100644
| 1 | +// Tencent is pleased to support the open source community by making RapidJSON available-> | ||
| 2 | +// | ||
| 3 | +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip-> All rights reserved-> | ||
| 4 | +// | ||
| 5 | +// Licensed under the MIT License (the "License"); you may not use this file except | ||
| 6 | +// in compliance with the License-> You may obtain a copy of the License at | ||
| 7 | +// | ||
| 8 | +// http://opensource->org/licenses/MIT | ||
| 9 | +// | ||
| 10 | +// Unless required by applicable law or agreed to in writing, software distributed | ||
| 11 | +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR | ||
| 12 | +// CONDITIONS OF ANY KIND, either express or implied-> See the License for the | ||
| 13 | +// specific language governing permissions and limitations under the License-> | ||
| 14 | + | ||
| 15 | +#ifndef RAPIDJSON_SCHEMA_H_ | ||
| 16 | +#define RAPIDJSON_SCHEMA_H_ | ||
| 17 | + | ||
| 18 | +#include "document.h" | ||
| 19 | +#include "pointer.h" | ||
| 20 | +#include <cmath> // abs, floor | ||
| 21 | + | ||
| 22 | +#if !defined(RAPIDJSON_SCHEMA_USE_INTERNALREGEX) | ||
| 23 | +#define RAPIDJSON_SCHEMA_USE_INTERNALREGEX 1 | ||
| 24 | +#else | ||
| 25 | +#define RAPIDJSON_SCHEMA_USE_INTERNALREGEX 0 | ||
| 26 | +#endif | ||
| 27 | + | ||
| 28 | +#if !RAPIDJSON_SCHEMA_USE_INTERNALREGEX && !defined(RAPIDJSON_SCHEMA_USE_STDREGEX) && (__cplusplus >=201103L || (defined(_MSC_VER) && _MSC_VER >= 1800)) | ||
| 29 | +#define RAPIDJSON_SCHEMA_USE_STDREGEX 1 | ||
| 30 | +#else | ||
| 31 | +#define RAPIDJSON_SCHEMA_USE_STDREGEX 0 | ||
| 32 | +#endif | ||
| 33 | + | ||
| 34 | +#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX | ||
| 35 | +#include "internal/regex.h" | ||
| 36 | +#elif RAPIDJSON_SCHEMA_USE_STDREGEX | ||
| 37 | +#include <regex> | ||
| 38 | +#endif | ||
| 39 | + | ||
| 40 | +#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX || RAPIDJSON_SCHEMA_USE_STDREGEX | ||
| 41 | +#define RAPIDJSON_SCHEMA_HAS_REGEX 1 | ||
| 42 | +#else | ||
| 43 | +#define RAPIDJSON_SCHEMA_HAS_REGEX 0 | ||
| 44 | +#endif | ||
| 45 | + | ||
| 46 | +#ifndef RAPIDJSON_SCHEMA_VERBOSE | ||
| 47 | +#define RAPIDJSON_SCHEMA_VERBOSE 0 | ||
| 48 | +#endif | ||
| 49 | + | ||
| 50 | +#if RAPIDJSON_SCHEMA_VERBOSE | ||
| 51 | +#include "stringbuffer.h" | ||
| 52 | +#endif | ||
| 53 | + | ||
| 54 | +RAPIDJSON_DIAG_PUSH | ||
| 55 | + | ||
| 56 | +#if defined(__GNUC__) | ||
| 57 | +RAPIDJSON_DIAG_OFF(effc++) | ||
| 58 | +#endif | ||
| 59 | + | ||
| 60 | +#ifdef __clang__ | ||
| 61 | +RAPIDJSON_DIAG_OFF(weak-vtables) | ||
| 62 | +RAPIDJSON_DIAG_OFF(exit-time-destructors) | ||
| 63 | +RAPIDJSON_DIAG_OFF(c++98-compat-pedantic) | ||
| 64 | +RAPIDJSON_DIAG_OFF(variadic-macros) | ||
| 65 | +#endif | ||
| 66 | + | ||
| 67 | +#ifdef _MSC_VER | ||
| 68 | +RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated | ||
| 69 | +#endif | ||
| 70 | + | ||
| 71 | +RAPIDJSON_NAMESPACE_BEGIN | ||
| 72 | + | ||
| 73 | +/////////////////////////////////////////////////////////////////////////////// | ||
| 74 | +// Verbose Utilities | ||
| 75 | + | ||
| 76 | +#if RAPIDJSON_SCHEMA_VERBOSE | ||
| 77 | + | ||
| 78 | +namespace internal { | ||
| 79 | + | ||
| 80 | +inline void PrintInvalidKeyword(const char* keyword) { | ||
| 81 | + printf("Fail keyword: %s\n", keyword); | ||
| 82 | +} | ||
| 83 | + | ||
| 84 | +inline void PrintInvalidKeyword(const wchar_t* keyword) { | ||
| 85 | + wprintf(L"Fail keyword: %ls\n", keyword); | ||
| 86 | +} | ||
| 87 | + | ||
| 88 | +inline void PrintInvalidDocument(const char* document) { | ||
| 89 | + printf("Fail document: %s\n\n", document); | ||
| 90 | +} | ||
| 91 | + | ||
| 92 | +inline void PrintInvalidDocument(const wchar_t* document) { | ||
| 93 | + wprintf(L"Fail document: %ls\n\n", document); | ||
| 94 | +} | ||
| 95 | + | ||
| 96 | +inline void PrintValidatorPointers(unsigned depth, const char* s, const char* d) { | ||
| 97 | + printf("S: %*s%s\nD: %*s%s\n\n", depth * 4, " ", s, depth * 4, " ", d); | ||
| 98 | +} | ||
| 99 | + | ||
| 100 | +inline void PrintValidatorPointers(unsigned depth, const wchar_t* s, const wchar_t* d) { | ||
| 101 | + wprintf(L"S: %*ls%ls\nD: %*ls%ls\n\n", depth * 4, L" ", s, depth * 4, L" ", d); | ||
| 102 | +} | ||
| 103 | + | ||
| 104 | +} // namespace internal | ||
| 105 | + | ||
| 106 | +#endif // RAPIDJSON_SCHEMA_VERBOSE | ||
| 107 | + | ||
| 108 | +/////////////////////////////////////////////////////////////////////////////// | ||
| 109 | +// RAPIDJSON_INVALID_KEYWORD_RETURN | ||
| 110 | + | ||
| 111 | +#if RAPIDJSON_SCHEMA_VERBOSE | ||
| 112 | +#define RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword) internal::PrintInvalidKeyword(keyword) | ||
| 113 | +#else | ||
| 114 | +#define RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword) | ||
| 115 | +#endif | ||
| 116 | + | ||
| 117 | +#define RAPIDJSON_INVALID_KEYWORD_RETURN(keyword)\ | ||
| 118 | +RAPIDJSON_MULTILINEMACRO_BEGIN\ | ||
| 119 | + context.invalidKeyword = keyword.GetString();\ | ||
| 120 | + RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword.GetString());\ | ||
| 121 | + return false;\ | ||
| 122 | +RAPIDJSON_MULTILINEMACRO_END | ||
| 123 | + | ||
| 124 | +/////////////////////////////////////////////////////////////////////////////// | ||
| 125 | +// Forward declarations | ||
| 126 | + | ||
| 127 | +template <typename ValueType, typename Allocator> | ||
| 128 | +class GenericSchemaDocument; | ||
| 129 | + | ||
| 130 | +namespace internal { | ||
| 131 | + | ||
| 132 | +template <typename SchemaDocumentType> | ||
| 133 | +class Schema; | ||
| 134 | + | ||
| 135 | +/////////////////////////////////////////////////////////////////////////////// | ||
| 136 | +// ISchemaValidator | ||
| 137 | + | ||
| 138 | +class ISchemaValidator { | ||
| 139 | +public: | ||
| 140 | + virtual ~ISchemaValidator() {} | ||
| 141 | + virtual bool IsValid() const = 0; | ||
| 142 | +}; | ||
| 143 | + | ||
| 144 | +/////////////////////////////////////////////////////////////////////////////// | ||
| 145 | +// ISchemaStateFactory | ||
| 146 | + | ||
| 147 | +template <typename SchemaType> | ||
| 148 | +class ISchemaStateFactory { | ||
| 149 | +public: | ||
| 150 | + virtual ~ISchemaStateFactory() {} | ||
| 151 | + virtual ISchemaValidator* CreateSchemaValidator(const SchemaType&) = 0; | ||
| 152 | + virtual void DestroySchemaValidator(ISchemaValidator* validator) = 0; | ||
| 153 | + virtual void* CreateHasher() = 0; | ||
| 154 | + virtual uint64_t GetHashCode(void* hasher) = 0; | ||
| 155 | + virtual void DestroryHasher(void* hasher) = 0; | ||
| 156 | + virtual void* MallocState(size_t size) = 0; | ||
| 157 | + virtual void FreeState(void* p) = 0; | ||
| 158 | +}; | ||
| 159 | + | ||
| 160 | +/////////////////////////////////////////////////////////////////////////////// | ||
| 161 | +// Hasher | ||
| 162 | + | ||
| 163 | +// For comparison of compound value | ||
| 164 | +template<typename Encoding, typename Allocator> | ||
| 165 | +class Hasher { | ||
| 166 | +public: | ||
| 167 | + typedef typename Encoding::Ch Ch; | ||
| 168 | + | ||
| 169 | + Hasher(Allocator* allocator = 0, size_t stackCapacity = kDefaultSize) : stack_(allocator, stackCapacity) {} | ||
| 170 | + | ||
| 171 | + bool Null() { return WriteType(kNullType); } | ||
| 172 | + bool Bool(bool b) { return WriteType(b ? kTrueType : kFalseType); } | ||
| 173 | + bool Int(int i) { Number n; n.u.i = i; n.d = static_cast<double>(i); return WriteNumber(n); } | ||
| 174 | + bool Uint(unsigned u) { Number n; n.u.u = u; n.d = static_cast<double>(u); return WriteNumber(n); } | ||
| 175 | + bool Int64(int64_t i) { Number n; n.u.i = i; n.d = static_cast<double>(i); return WriteNumber(n); } | ||
| 176 | + bool Uint64(uint64_t u) { Number n; n.u.u = u; n.d = static_cast<double>(u); return WriteNumber(n); } | ||
| 177 | + bool Double(double d) { | ||
| 178 | + Number n; | ||
| 179 | + if (d < 0) n.u.i = static_cast<int64_t>(d); | ||
| 180 | + else n.u.u = static_cast<uint64_t>(d); | ||
| 181 | + n.d = d; | ||
| 182 | + return WriteNumber(n); | ||
| 183 | + } | ||
| 184 | + | ||
| 185 | + bool RawNumber(const Ch* str, SizeType len, bool) { | ||
| 186 | + WriteBuffer(kNumberType, str, len * sizeof(Ch)); | ||
| 187 | + return true; | ||
| 188 | + } | ||
| 189 | + | ||
| 190 | + bool String(const Ch* str, SizeType len, bool) { | ||
| 191 | + WriteBuffer(kStringType, str, len * sizeof(Ch)); | ||
| 192 | + return true; | ||
| 193 | + } | ||
| 194 | + | ||
| 195 | + bool StartObject() { return true; } | ||
| 196 | + bool Key(const Ch* str, SizeType len, bool copy) { return String(str, len, copy); } | ||
| 197 | + bool EndObject(SizeType memberCount) { | ||
| 198 | + uint64_t h = Hash(0, kObjectType); | ||
| 199 | + uint64_t* kv = stack_.template Pop<uint64_t>(memberCount * 2); | ||
| 200 | + for (SizeType i = 0; i < memberCount; i++) | ||
| 201 | + h ^= Hash(kv[i * 2], kv[i * 2 + 1]); // Use xor to achieve member order insensitive | ||
| 202 | + *stack_.template Push<uint64_t>() = h; | ||
| 203 | + return true; | ||
| 204 | + } | ||
| 205 | + | ||
| 206 | + bool StartArray() { return true; } | ||
| 207 | + bool EndArray(SizeType elementCount) { | ||
| 208 | + uint64_t h = Hash(0, kArrayType); | ||
| 209 | + uint64_t* e = stack_.template Pop<uint64_t>(elementCount); | ||
| 210 | + for (SizeType i = 0; i < elementCount; i++) | ||
| 211 | + h = Hash(h, e[i]); // Use hash to achieve element order sensitive | ||
| 212 | + *stack_.template Push<uint64_t>() = h; | ||
| 213 | + return true; | ||
| 214 | + } | ||
| 215 | + | ||
| 216 | + bool IsValid() const { return stack_.GetSize() == sizeof(uint64_t); } | ||
| 217 | + | ||
| 218 | + uint64_t GetHashCode() const { | ||
| 219 | + RAPIDJSON_ASSERT(IsValid()); | ||
| 220 | + return *stack_.template Top<uint64_t>(); | ||
| 221 | + } | ||
| 222 | + | ||
| 223 | +private: | ||
| 224 | + static const size_t kDefaultSize = 256; | ||
| 225 | + struct Number { | ||
| 226 | + union U { | ||
| 227 | + uint64_t u; | ||
| 228 | + int64_t i; | ||
| 229 | + }u; | ||
| 230 | + double d; | ||
| 231 | + }; | ||
| 232 | + | ||
| 233 | + bool WriteType(Type type) { return WriteBuffer(type, 0, 0); } | ||
| 234 | + | ||
| 235 | + bool WriteNumber(const Number& n) { return WriteBuffer(kNumberType, &n, sizeof(n)); } | ||
| 236 | + | ||
| 237 | + bool WriteBuffer(Type type, const void* data, size_t len) { | ||
| 238 | + // FNV-1a from http://isthe.com/chongo/tech/comp/fnv/ | ||
| 239 | + uint64_t h = Hash(RAPIDJSON_UINT64_C2(0x84222325, 0xcbf29ce4), type); | ||
| 240 | + const unsigned char* d = static_cast<const unsigned char*>(data); | ||
| 241 | + for (size_t i = 0; i < len; i++) | ||
| 242 | + h = Hash(h, d[i]); | ||
| 243 | + *stack_.template Push<uint64_t>() = h; | ||
| 244 | + return true; | ||
| 245 | + } | ||
| 246 | + | ||
| 247 | + static uint64_t Hash(uint64_t h, uint64_t d) { | ||
| 248 | + static const uint64_t kPrime = RAPIDJSON_UINT64_C2(0x00000100, 0x000001b3); | ||
| 249 | + h ^= d; | ||
| 250 | + h *= kPrime; | ||
| 251 | + return h; | ||
| 252 | + } | ||
| 253 | + | ||
| 254 | + Stack<Allocator> stack_; | ||
| 255 | +}; | ||
| 256 | + | ||
| 257 | +/////////////////////////////////////////////////////////////////////////////// | ||
| 258 | +// SchemaValidationContext | ||
| 259 | + | ||
| 260 | +template <typename SchemaDocumentType> | ||
| 261 | +struct SchemaValidationContext { | ||
| 262 | + typedef Schema<SchemaDocumentType> SchemaType; | ||
| 263 | + typedef ISchemaStateFactory<SchemaType> SchemaValidatorFactoryType; | ||
| 264 | + typedef typename SchemaType::ValueType ValueType; | ||
| 265 | + typedef typename ValueType::Ch Ch; | ||
| 266 | + | ||
| 267 | + enum PatternValidatorType { | ||
| 268 | + kPatternValidatorOnly, | ||
| 269 | + kPatternValidatorWithProperty, | ||
| 270 | + kPatternValidatorWithAdditionalProperty | ||
| 271 | + }; | ||
| 272 | + | ||
| 273 | + SchemaValidationContext(SchemaValidatorFactoryType& f, const SchemaType* s) : | ||
| 274 | + factory(f), | ||
| 275 | + schema(s), | ||
| 276 | + valueSchema(), | ||
| 277 | + invalidKeyword(), | ||
| 278 | + hasher(), | ||
| 279 | + arrayElementHashCodes(), | ||
| 280 | + validators(), | ||
| 281 | + validatorCount(), | ||
| 282 | + patternPropertiesValidators(), | ||
| 283 | + patternPropertiesValidatorCount(), | ||
| 284 | + patternPropertiesSchemas(), | ||
| 285 | + patternPropertiesSchemaCount(), | ||
| 286 | + valuePatternValidatorType(kPatternValidatorOnly), | ||
| 287 | + propertyExist(), | ||
| 288 | + inArray(false), | ||
| 289 | + valueUniqueness(false), | ||
| 290 | + arrayUniqueness(false) | ||
| 291 | + { | ||
| 292 | + } | ||
| 293 | + | ||
| 294 | + ~SchemaValidationContext() { | ||
| 295 | + if (hasher) | ||
| 296 | + factory.DestroryHasher(hasher); | ||
| 297 | + if (validators) { | ||
| 298 | + for (SizeType i = 0; i < validatorCount; i++) | ||
| 299 | + factory.DestroySchemaValidator(validators[i]); | ||
| 300 | + factory.FreeState(validators); | ||
| 301 | + } | ||
| 302 | + if (patternPropertiesValidators) { | ||
| 303 | + for (SizeType i = 0; i < patternPropertiesValidatorCount; i++) | ||
| 304 | + factory.DestroySchemaValidator(patternPropertiesValidators[i]); | ||
| 305 | + factory.FreeState(patternPropertiesValidators); | ||
| 306 | + } | ||
| 307 | + if (patternPropertiesSchemas) | ||
| 308 | + factory.FreeState(patternPropertiesSchemas); | ||
| 309 | + if (propertyExist) | ||
| 310 | + factory.FreeState(propertyExist); | ||
| 311 | + } | ||
| 312 | + | ||
| 313 | + SchemaValidatorFactoryType& factory; | ||
| 314 | + const SchemaType* schema; | ||
| 315 | + const SchemaType* valueSchema; | ||
| 316 | + const Ch* invalidKeyword; | ||
| 317 | + void* hasher; // Only validator access | ||
| 318 | + void* arrayElementHashCodes; // Only validator access this | ||
| 319 | + ISchemaValidator** validators; | ||
| 320 | + SizeType validatorCount; | ||
| 321 | + ISchemaValidator** patternPropertiesValidators; | ||
| 322 | + SizeType patternPropertiesValidatorCount; | ||
| 323 | + const SchemaType** patternPropertiesSchemas; | ||
| 324 | + SizeType patternPropertiesSchemaCount; | ||
| 325 | + PatternValidatorType valuePatternValidatorType; | ||
| 326 | + PatternValidatorType objectPatternValidatorType; | ||
| 327 | + SizeType arrayElementIndex; | ||
| 328 | + bool* propertyExist; | ||
| 329 | + bool inArray; | ||
| 330 | + bool valueUniqueness; | ||
| 331 | + bool arrayUniqueness; | ||
| 332 | +}; | ||
| 333 | + | ||
| 334 | +/////////////////////////////////////////////////////////////////////////////// | ||
| 335 | +// Schema | ||
| 336 | + | ||
| 337 | +template <typename SchemaDocumentType> | ||
| 338 | +class Schema { | ||
| 339 | +public: | ||
| 340 | + typedef typename SchemaDocumentType::ValueType ValueType; | ||
| 341 | + typedef typename SchemaDocumentType::AllocatorType AllocatorType; | ||
| 342 | + typedef typename SchemaDocumentType::PointerType PointerType; | ||
| 343 | + typedef typename ValueType::EncodingType EncodingType; | ||
| 344 | + typedef typename EncodingType::Ch Ch; | ||
| 345 | + typedef SchemaValidationContext<SchemaDocumentType> Context; | ||
| 346 | + typedef Schema<SchemaDocumentType> SchemaType; | ||
| 347 | + typedef GenericValue<EncodingType, AllocatorType> SValue; | ||
| 348 | + friend class GenericSchemaDocument<ValueType, AllocatorType>; | ||
| 349 | + | ||
| 350 | + Schema(SchemaDocumentType* schemaDocument, const PointerType& p, const ValueType& value, const ValueType& document, AllocatorType* allocator) : | ||
| 351 | + allocator_(allocator), | ||
| 352 | + typeless_(schemaDocument->GetTypeless()), | ||
| 353 | + enum_(), | ||
| 354 | + enumCount_(), | ||
| 355 | + not_(), | ||
| 356 | + type_((1 << kTotalSchemaType) - 1), // typeless | ||
| 357 | + validatorCount_(), | ||
| 358 | + properties_(), | ||
| 359 | + additionalPropertiesSchema_(), | ||
| 360 | + patternProperties_(), | ||
| 361 | + patternPropertyCount_(), | ||
| 362 | + propertyCount_(), | ||
| 363 | + minProperties_(), | ||
| 364 | + maxProperties_(SizeType(~0)), | ||
| 365 | + additionalProperties_(true), | ||
| 366 | + hasDependencies_(), | ||
| 367 | + hasRequired_(), | ||
| 368 | + hasSchemaDependencies_(), | ||
| 369 | + additionalItemsSchema_(), | ||
| 370 | + itemsList_(), | ||
| 371 | + itemsTuple_(), | ||
| 372 | + itemsTupleCount_(), | ||
| 373 | + minItems_(), | ||
| 374 | + maxItems_(SizeType(~0)), | ||
| 375 | + additionalItems_(true), | ||
| 376 | + uniqueItems_(false), | ||
| 377 | + pattern_(), | ||
| 378 | + minLength_(0), | ||
| 379 | + maxLength_(~SizeType(0)), | ||
| 380 | + exclusiveMinimum_(false), | ||
| 381 | + exclusiveMaximum_(false) | ||
| 382 | + { | ||
| 383 | + typedef typename SchemaDocumentType::ValueType ValueType; | ||
| 384 | + typedef typename ValueType::ConstValueIterator ConstValueIterator; | ||
| 385 | + typedef typename ValueType::ConstMemberIterator ConstMemberIterator; | ||
| 386 | + | ||
| 387 | + if (!value.IsObject()) | ||
| 388 | + return; | ||
| 389 | + | ||
| 390 | + if (const ValueType* v = GetMember(value, GetTypeString())) { | ||
| 391 | + type_ = 0; | ||
| 392 | + if (v->IsString()) | ||
| 393 | + AddType(*v); | ||
| 394 | + else if (v->IsArray()) | ||
| 395 | + for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr) | ||
| 396 | + AddType(*itr); | ||
| 397 | + } | ||
| 398 | + | ||
| 399 | + if (const ValueType* v = GetMember(value, GetEnumString())) | ||
| 400 | + if (v->IsArray() && v->Size() > 0) { | ||
| 401 | + enum_ = static_cast<uint64_t*>(allocator_->Malloc(sizeof(uint64_t) * v->Size())); | ||
| 402 | + for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr) { | ||
| 403 | + typedef Hasher<EncodingType, MemoryPoolAllocator<> > EnumHasherType; | ||
| 404 | + char buffer[256 + 24]; | ||
| 405 | + MemoryPoolAllocator<> hasherAllocator(buffer, sizeof(buffer)); | ||
| 406 | + EnumHasherType h(&hasherAllocator, 256); | ||
| 407 | + itr->Accept(h); | ||
| 408 | + enum_[enumCount_++] = h.GetHashCode(); | ||
| 409 | + } | ||
| 410 | + } | ||
| 411 | + | ||
| 412 | + if (schemaDocument) { | ||
| 413 | + AssignIfExist(allOf_, *schemaDocument, p, value, GetAllOfString(), document); | ||
| 414 | + AssignIfExist(anyOf_, *schemaDocument, p, value, GetAnyOfString(), document); | ||
| 415 | + AssignIfExist(oneOf_, *schemaDocument, p, value, GetOneOfString(), document); | ||
| 416 | + } | ||
| 417 | + | ||
| 418 | + if (const ValueType* v = GetMember(value, GetNotString())) { | ||
| 419 | + schemaDocument->CreateSchema(¬_, p.Append(GetNotString(), allocator_), *v, document); | ||
| 420 | + notValidatorIndex_ = validatorCount_; | ||
| 421 | + validatorCount_++; | ||
| 422 | + } | ||
| 423 | + | ||
| 424 | + // Object | ||
| 425 | + | ||
| 426 | + const ValueType* properties = GetMember(value, GetPropertiesString()); | ||
| 427 | + const ValueType* required = GetMember(value, GetRequiredString()); | ||
| 428 | + const ValueType* dependencies = GetMember(value, GetDependenciesString()); | ||
| 429 | + { | ||
| 430 | + // Gather properties from properties/required/dependencies | ||
| 431 | + SValue allProperties(kArrayType); | ||
| 432 | + | ||
| 433 | + if (properties && properties->IsObject()) | ||
| 434 | + for (ConstMemberIterator itr = properties->MemberBegin(); itr != properties->MemberEnd(); ++itr) | ||
| 435 | + AddUniqueElement(allProperties, itr->name); | ||
| 436 | + | ||
| 437 | + if (required && required->IsArray()) | ||
| 438 | + for (ConstValueIterator itr = required->Begin(); itr != required->End(); ++itr) | ||
| 439 | + if (itr->IsString()) | ||
| 440 | + AddUniqueElement(allProperties, *itr); | ||
| 441 | + | ||
| 442 | + if (dependencies && dependencies->IsObject()) | ||
| 443 | + for (ConstMemberIterator itr = dependencies->MemberBegin(); itr != dependencies->MemberEnd(); ++itr) { | ||
| 444 | + AddUniqueElement(allProperties, itr->name); | ||
| 445 | + if (itr->value.IsArray()) | ||
| 446 | + for (ConstValueIterator i = itr->value.Begin(); i != itr->value.End(); ++i) | ||
| 447 | + if (i->IsString()) | ||
| 448 | + AddUniqueElement(allProperties, *i); | ||
| 449 | + } | ||
| 450 | + | ||
| 451 | + if (allProperties.Size() > 0) { | ||
| 452 | + propertyCount_ = allProperties.Size(); | ||
| 453 | + properties_ = static_cast<Property*>(allocator_->Malloc(sizeof(Property) * propertyCount_)); | ||
| 454 | + for (SizeType i = 0; i < propertyCount_; i++) { | ||
| 455 | + new (&properties_[i]) Property(); | ||
| 456 | + properties_[i].name = allProperties[i]; | ||
| 457 | + properties_[i].schema = typeless_; | ||
| 458 | + } | ||
| 459 | + } | ||
| 460 | + } | ||
| 461 | + | ||
| 462 | + if (properties && properties->IsObject()) { | ||
| 463 | + PointerType q = p.Append(GetPropertiesString(), allocator_); | ||
| 464 | + for (ConstMemberIterator itr = properties->MemberBegin(); itr != properties->MemberEnd(); ++itr) { | ||
| 465 | + SizeType index; | ||
| 466 | + if (FindPropertyIndex(itr->name, &index)) | ||
| 467 | + schemaDocument->CreateSchema(&properties_[index].schema, q.Append(itr->name, allocator_), itr->value, document); | ||
| 468 | + } | ||
| 469 | + } | ||
| 470 | + | ||
| 471 | + if (const ValueType* v = GetMember(value, GetPatternPropertiesString())) { | ||
| 472 | + PointerType q = p.Append(GetPatternPropertiesString(), allocator_); | ||
| 473 | + patternProperties_ = static_cast<PatternProperty*>(allocator_->Malloc(sizeof(PatternProperty) * v->MemberCount())); | ||
| 474 | + patternPropertyCount_ = 0; | ||
| 475 | + | ||
| 476 | + for (ConstMemberIterator itr = v->MemberBegin(); itr != v->MemberEnd(); ++itr) { | ||
| 477 | + new (&patternProperties_[patternPropertyCount_]) PatternProperty(); | ||
| 478 | + patternProperties_[patternPropertyCount_].pattern = CreatePattern(itr->name); | ||
| 479 | + schemaDocument->CreateSchema(&patternProperties_[patternPropertyCount_].schema, q.Append(itr->name, allocator_), itr->value, document); | ||
| 480 | + patternPropertyCount_++; | ||
| 481 | + } | ||
| 482 | + } | ||
| 483 | + | ||
| 484 | + if (required && required->IsArray()) | ||
| 485 | + for (ConstValueIterator itr = required->Begin(); itr != required->End(); ++itr) | ||
| 486 | + if (itr->IsString()) { | ||
| 487 | + SizeType index; | ||
| 488 | + if (FindPropertyIndex(*itr, &index)) { | ||
| 489 | + properties_[index].required = true; | ||
| 490 | + hasRequired_ = true; | ||
| 491 | + } | ||
| 492 | + } | ||
| 493 | + | ||
| 494 | + if (dependencies && dependencies->IsObject()) { | ||
| 495 | + PointerType q = p.Append(GetDependenciesString(), allocator_); | ||
| 496 | + hasDependencies_ = true; | ||
| 497 | + for (ConstMemberIterator itr = dependencies->MemberBegin(); itr != dependencies->MemberEnd(); ++itr) { | ||
| 498 | + SizeType sourceIndex; | ||
| 499 | + if (FindPropertyIndex(itr->name, &sourceIndex)) { | ||
| 500 | + if (itr->value.IsArray()) { | ||
| 501 | + properties_[sourceIndex].dependencies = static_cast<bool*>(allocator_->Malloc(sizeof(bool) * propertyCount_)); | ||
| 502 | + std::memset(properties_[sourceIndex].dependencies, 0, sizeof(bool)* propertyCount_); | ||
| 503 | + for (ConstValueIterator targetItr = itr->value.Begin(); targetItr != itr->value.End(); ++targetItr) { | ||
| 504 | + SizeType targetIndex; | ||
| 505 | + if (FindPropertyIndex(*targetItr, &targetIndex)) | ||
| 506 | + properties_[sourceIndex].dependencies[targetIndex] = true; | ||
| 507 | + } | ||
| 508 | + } | ||
| 509 | + else if (itr->value.IsObject()) { | ||
| 510 | + hasSchemaDependencies_ = true; | ||
| 511 | + schemaDocument->CreateSchema(&properties_[sourceIndex].dependenciesSchema, q.Append(itr->name, allocator_), itr->value, document); | ||
| 512 | + properties_[sourceIndex].dependenciesValidatorIndex = validatorCount_; | ||
| 513 | + validatorCount_++; | ||
| 514 | + } | ||
| 515 | + } | ||
| 516 | + } | ||
| 517 | + } | ||
| 518 | + | ||
| 519 | + if (const ValueType* v = GetMember(value, GetAdditionalPropertiesString())) { | ||
| 520 | + if (v->IsBool()) | ||
| 521 | + additionalProperties_ = v->GetBool(); | ||
| 522 | + else if (v->IsObject()) | ||
| 523 | + schemaDocument->CreateSchema(&additionalPropertiesSchema_, p.Append(GetAdditionalPropertiesString(), allocator_), *v, document); | ||
| 524 | + } | ||
| 525 | + | ||
| 526 | + AssignIfExist(minProperties_, value, GetMinPropertiesString()); | ||
| 527 | + AssignIfExist(maxProperties_, value, GetMaxPropertiesString()); | ||
| 528 | + | ||
| 529 | + // Array | ||
| 530 | + if (const ValueType* v = GetMember(value, GetItemsString())) { | ||
| 531 | + PointerType q = p.Append(GetItemsString(), allocator_); | ||
| 532 | + if (v->IsObject()) // List validation | ||
| 533 | + schemaDocument->CreateSchema(&itemsList_, q, *v, document); | ||
| 534 | + else if (v->IsArray()) { // Tuple validation | ||
| 535 | + itemsTuple_ = static_cast<const Schema**>(allocator_->Malloc(sizeof(const Schema*) * v->Size())); | ||
| 536 | + SizeType index = 0; | ||
| 537 | + for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr, index++) | ||
| 538 | + schemaDocument->CreateSchema(&itemsTuple_[itemsTupleCount_++], q.Append(index, allocator_), *itr, document); | ||
| 539 | + } | ||
| 540 | + } | ||
| 541 | + | ||
| 542 | + AssignIfExist(minItems_, value, GetMinItemsString()); | ||
| 543 | + AssignIfExist(maxItems_, value, GetMaxItemsString()); | ||
| 544 | + | ||
| 545 | + if (const ValueType* v = GetMember(value, GetAdditionalItemsString())) { | ||
| 546 | + if (v->IsBool()) | ||
| 547 | + additionalItems_ = v->GetBool(); | ||
| 548 | + else if (v->IsObject()) | ||
| 549 | + schemaDocument->CreateSchema(&additionalItemsSchema_, p.Append(GetAdditionalItemsString(), allocator_), *v, document); | ||
| 550 | + } | ||
| 551 | + | ||
| 552 | + AssignIfExist(uniqueItems_, value, GetUniqueItemsString()); | ||
| 553 | + | ||
| 554 | + // String | ||
| 555 | + AssignIfExist(minLength_, value, GetMinLengthString()); | ||
| 556 | + AssignIfExist(maxLength_, value, GetMaxLengthString()); | ||
| 557 | + | ||
| 558 | + if (const ValueType* v = GetMember(value, GetPatternString())) | ||
| 559 | + pattern_ = CreatePattern(*v); | ||
| 560 | + | ||
| 561 | + // Number | ||
| 562 | + if (const ValueType* v = GetMember(value, GetMinimumString())) | ||
| 563 | + if (v->IsNumber()) | ||
| 564 | + minimum_.CopyFrom(*v, *allocator_); | ||
| 565 | + | ||
| 566 | + if (const ValueType* v = GetMember(value, GetMaximumString())) | ||
| 567 | + if (v->IsNumber()) | ||
| 568 | + maximum_.CopyFrom(*v, *allocator_); | ||
| 569 | + | ||
| 570 | + AssignIfExist(exclusiveMinimum_, value, GetExclusiveMinimumString()); | ||
| 571 | + AssignIfExist(exclusiveMaximum_, value, GetExclusiveMaximumString()); | ||
| 572 | + | ||
| 573 | + if (const ValueType* v = GetMember(value, GetMultipleOfString())) | ||
| 574 | + if (v->IsNumber() && v->GetDouble() > 0.0) | ||
| 575 | + multipleOf_.CopyFrom(*v, *allocator_); | ||
| 576 | + } | ||
| 577 | + | ||
| 578 | + ~Schema() { | ||
| 579 | + AllocatorType::Free(enum_); | ||
| 580 | + if (properties_) { | ||
| 581 | + for (SizeType i = 0; i < propertyCount_; i++) | ||
| 582 | + properties_[i].~Property(); | ||
| 583 | + AllocatorType::Free(properties_); | ||
| 584 | + } | ||
| 585 | + if (patternProperties_) { | ||
| 586 | + for (SizeType i = 0; i < patternPropertyCount_; i++) | ||
| 587 | + patternProperties_[i].~PatternProperty(); | ||
| 588 | + AllocatorType::Free(patternProperties_); | ||
| 589 | + } | ||
| 590 | + AllocatorType::Free(itemsTuple_); | ||
| 591 | +#if RAPIDJSON_SCHEMA_HAS_REGEX | ||
| 592 | + if (pattern_) { | ||
| 593 | + pattern_->~RegexType(); | ||
| 594 | + AllocatorType::Free(pattern_); | ||
| 595 | + } | ||
| 596 | +#endif | ||
| 597 | + } | ||
| 598 | + | ||
| 599 | + bool BeginValue(Context& context) const { | ||
| 600 | + if (context.inArray) { | ||
| 601 | + if (uniqueItems_) | ||
| 602 | + context.valueUniqueness = true; | ||
| 603 | + | ||
| 604 | + if (itemsList_) | ||
| 605 | + context.valueSchema = itemsList_; | ||
| 606 | + else if (itemsTuple_) { | ||
| 607 | + if (context.arrayElementIndex < itemsTupleCount_) | ||
| 608 | + context.valueSchema = itemsTuple_[context.arrayElementIndex]; | ||
| 609 | + else if (additionalItemsSchema_) | ||
| 610 | + context.valueSchema = additionalItemsSchema_; | ||
| 611 | + else if (additionalItems_) | ||
| 612 | + context.valueSchema = typeless_; | ||
| 613 | + else | ||
| 614 | + RAPIDJSON_INVALID_KEYWORD_RETURN(GetItemsString()); | ||
| 615 | + } | ||
| 616 | + else | ||
| 617 | + context.valueSchema = typeless_; | ||
| 618 | + | ||
| 619 | + context.arrayElementIndex++; | ||
| 620 | + } | ||
| 621 | + return true; | ||
| 622 | + } | ||
| 623 | + | ||
| 624 | + RAPIDJSON_FORCEINLINE bool EndValue(Context& context) const { | ||
| 625 | + if (context.patternPropertiesValidatorCount > 0) { | ||
| 626 | + bool otherValid = false; | ||
| 627 | + SizeType count = context.patternPropertiesValidatorCount; | ||
| 628 | + if (context.objectPatternValidatorType != Context::kPatternValidatorOnly) | ||
| 629 | + otherValid = context.patternPropertiesValidators[--count]->IsValid(); | ||
| 630 | + | ||
| 631 | + bool patternValid = true; | ||
| 632 | + for (SizeType i = 0; i < count; i++) | ||
| 633 | + if (!context.patternPropertiesValidators[i]->IsValid()) { | ||
| 634 | + patternValid = false; | ||
| 635 | + break; | ||
| 636 | + } | ||
| 637 | + | ||
| 638 | + if (context.objectPatternValidatorType == Context::kPatternValidatorOnly) { | ||
| 639 | + if (!patternValid) | ||
| 640 | + RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString()); | ||
| 641 | + } | ||
| 642 | + else if (context.objectPatternValidatorType == Context::kPatternValidatorWithProperty) { | ||
| 643 | + if (!patternValid || !otherValid) | ||
| 644 | + RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString()); | ||
| 645 | + } | ||
| 646 | + else if (!patternValid && !otherValid) // kPatternValidatorWithAdditionalProperty) | ||
| 647 | + RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString()); | ||
| 648 | + } | ||
| 649 | + | ||
| 650 | + if (enum_) { | ||
| 651 | + const uint64_t h = context.factory.GetHashCode(context.hasher); | ||
| 652 | + for (SizeType i = 0; i < enumCount_; i++) | ||
| 653 | + if (enum_[i] == h) | ||
| 654 | + goto foundEnum; | ||
| 655 | + RAPIDJSON_INVALID_KEYWORD_RETURN(GetEnumString()); | ||
| 656 | + foundEnum:; | ||
| 657 | + } | ||
| 658 | + | ||
| 659 | + if (allOf_.schemas) | ||
| 660 | + for (SizeType i = allOf_.begin; i < allOf_.begin + allOf_.count; i++) | ||
| 661 | + if (!context.validators[i]->IsValid()) | ||
| 662 | + RAPIDJSON_INVALID_KEYWORD_RETURN(GetAllOfString()); | ||
| 663 | + | ||
| 664 | + if (anyOf_.schemas) { | ||
| 665 | + for (SizeType i = anyOf_.begin; i < anyOf_.begin + anyOf_.count; i++) | ||
| 666 | + if (context.validators[i]->IsValid()) | ||
| 667 | + goto foundAny; | ||
| 668 | + RAPIDJSON_INVALID_KEYWORD_RETURN(GetAnyOfString()); | ||
| 669 | + foundAny:; | ||
| 670 | + } | ||
| 671 | + | ||
| 672 | + if (oneOf_.schemas) { | ||
| 673 | + bool oneValid = false; | ||
| 674 | + for (SizeType i = oneOf_.begin; i < oneOf_.begin + oneOf_.count; i++) | ||
| 675 | + if (context.validators[i]->IsValid()) { | ||
| 676 | + if (oneValid) | ||
| 677 | + RAPIDJSON_INVALID_KEYWORD_RETURN(GetOneOfString()); | ||
| 678 | + else | ||
| 679 | + oneValid = true; | ||
| 680 | + } | ||
| 681 | + if (!oneValid) | ||
| 682 | + RAPIDJSON_INVALID_KEYWORD_RETURN(GetOneOfString()); | ||
| 683 | + } | ||
| 684 | + | ||
| 685 | + if (not_ && context.validators[notValidatorIndex_]->IsValid()) | ||
| 686 | + RAPIDJSON_INVALID_KEYWORD_RETURN(GetNotString()); | ||
| 687 | + | ||
| 688 | + return true; | ||
| 689 | + } | ||
| 690 | + | ||
| 691 | + bool Null(Context& context) const { | ||
| 692 | + if (!(type_ & (1 << kNullSchemaType))) | ||
| 693 | + RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); | ||
| 694 | + return CreateParallelValidator(context); | ||
| 695 | + } | ||
| 696 | + | ||
| 697 | + bool Bool(Context& context, bool) const { | ||
| 698 | + if (!(type_ & (1 << kBooleanSchemaType))) | ||
| 699 | + RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); | ||
| 700 | + return CreateParallelValidator(context); | ||
| 701 | + } | ||
| 702 | + | ||
| 703 | + bool Int(Context& context, int i) const { | ||
| 704 | + if (!CheckInt(context, i)) | ||
| 705 | + return false; | ||
| 706 | + return CreateParallelValidator(context); | ||
| 707 | + } | ||
| 708 | + | ||
| 709 | + bool Uint(Context& context, unsigned u) const { | ||
| 710 | + if (!CheckUint(context, u)) | ||
| 711 | + return false; | ||
| 712 | + return CreateParallelValidator(context); | ||
| 713 | + } | ||
| 714 | + | ||
| 715 | + bool Int64(Context& context, int64_t i) const { | ||
| 716 | + if (!CheckInt(context, i)) | ||
| 717 | + return false; | ||
| 718 | + return CreateParallelValidator(context); | ||
| 719 | + } | ||
| 720 | + | ||
| 721 | + bool Uint64(Context& context, uint64_t u) const { | ||
| 722 | + if (!CheckUint(context, u)) | ||
| 723 | + return false; | ||
| 724 | + return CreateParallelValidator(context); | ||
| 725 | + } | ||
| 726 | + | ||
| 727 | + bool Double(Context& context, double d) const { | ||
| 728 | + if (!(type_ & (1 << kNumberSchemaType))) | ||
| 729 | + RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); | ||
| 730 | + | ||
| 731 | + if (!minimum_.IsNull() && !CheckDoubleMinimum(context, d)) | ||
| 732 | + return false; | ||
| 733 | + | ||
| 734 | + if (!maximum_.IsNull() && !CheckDoubleMaximum(context, d)) | ||
| 735 | + return false; | ||
| 736 | + | ||
| 737 | + if (!multipleOf_.IsNull() && !CheckDoubleMultipleOf(context, d)) | ||
| 738 | + return false; | ||
| 739 | + | ||
| 740 | + return CreateParallelValidator(context); | ||
| 741 | + } | ||
| 742 | + | ||
| 743 | + bool String(Context& context, const Ch* str, SizeType length, bool) const { | ||
| 744 | + if (!(type_ & (1 << kStringSchemaType))) | ||
| 745 | + RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); | ||
| 746 | + | ||
| 747 | + if (minLength_ != 0 || maxLength_ != SizeType(~0)) { | ||
| 748 | + SizeType count; | ||
| 749 | + if (internal::CountStringCodePoint<EncodingType>(str, length, &count)) { | ||
| 750 | + if (count < minLength_) | ||
| 751 | + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinLengthString()); | ||
| 752 | + if (count > maxLength_) | ||
| 753 | + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxLengthString()); | ||
| 754 | + } | ||
| 755 | + } | ||
| 756 | + | ||
| 757 | + if (pattern_ && !IsPatternMatch(pattern_, str, length)) | ||
| 758 | + RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternString()); | ||
| 759 | + | ||
| 760 | + return CreateParallelValidator(context); | ||
| 761 | + } | ||
| 762 | + | ||
| 763 | + bool StartObject(Context& context) const { | ||
| 764 | + if (!(type_ & (1 << kObjectSchemaType))) | ||
| 765 | + RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); | ||
| 766 | + | ||
| 767 | + if (hasDependencies_ || hasRequired_) { | ||
| 768 | + context.propertyExist = static_cast<bool*>(context.factory.MallocState(sizeof(bool) * propertyCount_)); | ||
| 769 | + std::memset(context.propertyExist, 0, sizeof(bool) * propertyCount_); | ||
| 770 | + } | ||
| 771 | + | ||
| 772 | + if (patternProperties_) { // pre-allocate schema array | ||
| 773 | + SizeType count = patternPropertyCount_ + 1; // extra for valuePatternValidatorType | ||
| 774 | + context.patternPropertiesSchemas = static_cast<const SchemaType**>(context.factory.MallocState(sizeof(const SchemaType*) * count)); | ||
| 775 | + context.patternPropertiesSchemaCount = 0; | ||
| 776 | + std::memset(context.patternPropertiesSchemas, 0, sizeof(SchemaType*) * count); | ||
| 777 | + } | ||
| 778 | + | ||
| 779 | + return CreateParallelValidator(context); | ||
| 780 | + } | ||
| 781 | + | ||
| 782 | + bool Key(Context& context, const Ch* str, SizeType len, bool) const { | ||
| 783 | + if (patternProperties_) { | ||
| 784 | + context.patternPropertiesSchemaCount = 0; | ||
| 785 | + for (SizeType i = 0; i < patternPropertyCount_; i++) | ||
| 786 | + if (patternProperties_[i].pattern && IsPatternMatch(patternProperties_[i].pattern, str, len)) { | ||
| 787 | + context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = patternProperties_[i].schema; | ||
| 788 | + context.valueSchema = typeless_; | ||
| 789 | + } | ||
| 790 | + } | ||
| 791 | + | ||
| 792 | + SizeType index; | ||
| 793 | + if (FindPropertyIndex(ValueType(str, len).Move(), &index)) { | ||
| 794 | + if (context.patternPropertiesSchemaCount > 0) { | ||
| 795 | + context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = properties_[index].schema; | ||
| 796 | + context.valueSchema = typeless_; | ||
| 797 | + context.valuePatternValidatorType = Context::kPatternValidatorWithProperty; | ||
| 798 | + } | ||
| 799 | + else | ||
| 800 | + context.valueSchema = properties_[index].schema; | ||
| 801 | + | ||
| 802 | + if (context.propertyExist) | ||
| 803 | + context.propertyExist[index] = true; | ||
| 804 | + | ||
| 805 | + return true; | ||
| 806 | + } | ||
| 807 | + | ||
| 808 | + if (additionalPropertiesSchema_) { | ||
| 809 | + if (additionalPropertiesSchema_ && context.patternPropertiesSchemaCount > 0) { | ||
| 810 | + context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = additionalPropertiesSchema_; | ||
| 811 | + context.valueSchema = typeless_; | ||
| 812 | + context.valuePatternValidatorType = Context::kPatternValidatorWithAdditionalProperty; | ||
| 813 | + } | ||
| 814 | + else | ||
| 815 | + context.valueSchema = additionalPropertiesSchema_; | ||
| 816 | + return true; | ||
| 817 | + } | ||
| 818 | + else if (additionalProperties_) { | ||
| 819 | + context.valueSchema = typeless_; | ||
| 820 | + return true; | ||
| 821 | + } | ||
| 822 | + | ||
| 823 | + if (context.patternPropertiesSchemaCount == 0) // patternProperties are not additional properties | ||
| 824 | + RAPIDJSON_INVALID_KEYWORD_RETURN(GetAdditionalPropertiesString()); | ||
| 825 | + | ||
| 826 | + return true; | ||
| 827 | + } | ||
| 828 | + | ||
| 829 | + bool EndObject(Context& context, SizeType memberCount) const { | ||
| 830 | + if (hasRequired_) | ||
| 831 | + for (SizeType index = 0; index < propertyCount_; index++) | ||
| 832 | + if (properties_[index].required) | ||
| 833 | + if (!context.propertyExist[index]) | ||
| 834 | + RAPIDJSON_INVALID_KEYWORD_RETURN(GetRequiredString()); | ||
| 835 | + | ||
| 836 | + if (memberCount < minProperties_) | ||
| 837 | + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinPropertiesString()); | ||
| 838 | + | ||
| 839 | + if (memberCount > maxProperties_) | ||
| 840 | + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxPropertiesString()); | ||
| 841 | + | ||
| 842 | + if (hasDependencies_) { | ||
| 843 | + for (SizeType sourceIndex = 0; sourceIndex < propertyCount_; sourceIndex++) | ||
| 844 | + if (context.propertyExist[sourceIndex]) { | ||
| 845 | + if (properties_[sourceIndex].dependencies) { | ||
| 846 | + for (SizeType targetIndex = 0; targetIndex < propertyCount_; targetIndex++) | ||
| 847 | + if (properties_[sourceIndex].dependencies[targetIndex] && !context.propertyExist[targetIndex]) | ||
| 848 | + RAPIDJSON_INVALID_KEYWORD_RETURN(GetDependenciesString()); | ||
| 849 | + } | ||
| 850 | + else if (properties_[sourceIndex].dependenciesSchema) | ||
| 851 | + if (!context.validators[properties_[sourceIndex].dependenciesValidatorIndex]->IsValid()) | ||
| 852 | + RAPIDJSON_INVALID_KEYWORD_RETURN(GetDependenciesString()); | ||
| 853 | + } | ||
| 854 | + } | ||
| 855 | + | ||
| 856 | + return true; | ||
| 857 | + } | ||
| 858 | + | ||
| 859 | + bool StartArray(Context& context) const { | ||
| 860 | + if (!(type_ & (1 << kArraySchemaType))) | ||
| 861 | + RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); | ||
| 862 | + | ||
| 863 | + context.arrayElementIndex = 0; | ||
| 864 | + context.inArray = true; | ||
| 865 | + | ||
| 866 | + return CreateParallelValidator(context); | ||
| 867 | + } | ||
| 868 | + | ||
| 869 | + bool EndArray(Context& context, SizeType elementCount) const { | ||
| 870 | + context.inArray = false; | ||
| 871 | + | ||
| 872 | + if (elementCount < minItems_) | ||
| 873 | + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinItemsString()); | ||
| 874 | + | ||
| 875 | + if (elementCount > maxItems_) | ||
| 876 | + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxItemsString()); | ||
| 877 | + | ||
| 878 | + return true; | ||
| 879 | + } | ||
| 880 | + | ||
| 881 | + // Generate functions for string literal according to Ch | ||
| 882 | +#define RAPIDJSON_STRING_(name, ...) \ | ||
| 883 | + static const ValueType& Get##name##String() {\ | ||
| 884 | + static const Ch s[] = { __VA_ARGS__, '\0' };\ | ||
| 885 | + static const ValueType v(s, static_cast<SizeType>(sizeof(s) / sizeof(Ch) - 1));\ | ||
| 886 | + return v;\ | ||
| 887 | + } | ||
| 888 | + | ||
| 889 | + RAPIDJSON_STRING_(Null, 'n', 'u', 'l', 'l') | ||
| 890 | + RAPIDJSON_STRING_(Boolean, 'b', 'o', 'o', 'l', 'e', 'a', 'n') | ||
| 891 | + RAPIDJSON_STRING_(Object, 'o', 'b', 'j', 'e', 'c', 't') | ||
| 892 | + RAPIDJSON_STRING_(Array, 'a', 'r', 'r', 'a', 'y') | ||
| 893 | + RAPIDJSON_STRING_(String, 's', 't', 'r', 'i', 'n', 'g') | ||
| 894 | + RAPIDJSON_STRING_(Number, 'n', 'u', 'm', 'b', 'e', 'r') | ||
| 895 | + RAPIDJSON_STRING_(Integer, 'i', 'n', 't', 'e', 'g', 'e', 'r') | ||
| 896 | + RAPIDJSON_STRING_(Type, 't', 'y', 'p', 'e') | ||
| 897 | + RAPIDJSON_STRING_(Enum, 'e', 'n', 'u', 'm') | ||
| 898 | + RAPIDJSON_STRING_(AllOf, 'a', 'l', 'l', 'O', 'f') | ||
| 899 | + RAPIDJSON_STRING_(AnyOf, 'a', 'n', 'y', 'O', 'f') | ||
| 900 | + RAPIDJSON_STRING_(OneOf, 'o', 'n', 'e', 'O', 'f') | ||
| 901 | + RAPIDJSON_STRING_(Not, 'n', 'o', 't') | ||
| 902 | + RAPIDJSON_STRING_(Properties, 'p', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') | ||
| 903 | + RAPIDJSON_STRING_(Required, 'r', 'e', 'q', 'u', 'i', 'r', 'e', 'd') | ||
| 904 | + RAPIDJSON_STRING_(Dependencies, 'd', 'e', 'p', 'e', 'n', 'd', 'e', 'n', 'c', 'i', 'e', 's') | ||
| 905 | + RAPIDJSON_STRING_(PatternProperties, 'p', 'a', 't', 't', 'e', 'r', 'n', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') | ||
| 906 | + RAPIDJSON_STRING_(AdditionalProperties, 'a', 'd', 'd', 'i', 't', 'i', 'o', 'n', 'a', 'l', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') | ||
| 907 | + RAPIDJSON_STRING_(MinProperties, 'm', 'i', 'n', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') | ||
| 908 | + RAPIDJSON_STRING_(MaxProperties, 'm', 'a', 'x', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') | ||
| 909 | + RAPIDJSON_STRING_(Items, 'i', 't', 'e', 'm', 's') | ||
| 910 | + RAPIDJSON_STRING_(MinItems, 'm', 'i', 'n', 'I', 't', 'e', 'm', 's') | ||
| 911 | + RAPIDJSON_STRING_(MaxItems, 'm', 'a', 'x', 'I', 't', 'e', 'm', 's') | ||
| 912 | + RAPIDJSON_STRING_(AdditionalItems, 'a', 'd', 'd', 'i', 't', 'i', 'o', 'n', 'a', 'l', 'I', 't', 'e', 'm', 's') | ||
| 913 | + RAPIDJSON_STRING_(UniqueItems, 'u', 'n', 'i', 'q', 'u', 'e', 'I', 't', 'e', 'm', 's') | ||
| 914 | + RAPIDJSON_STRING_(MinLength, 'm', 'i', 'n', 'L', 'e', 'n', 'g', 't', 'h') | ||
| 915 | + RAPIDJSON_STRING_(MaxLength, 'm', 'a', 'x', 'L', 'e', 'n', 'g', 't', 'h') | ||
| 916 | + RAPIDJSON_STRING_(Pattern, 'p', 'a', 't', 't', 'e', 'r', 'n') | ||
| 917 | + RAPIDJSON_STRING_(Minimum, 'm', 'i', 'n', 'i', 'm', 'u', 'm') | ||
| 918 | + RAPIDJSON_STRING_(Maximum, 'm', 'a', 'x', 'i', 'm', 'u', 'm') | ||
| 919 | + RAPIDJSON_STRING_(ExclusiveMinimum, 'e', 'x', 'c', 'l', 'u', 's', 'i', 'v', 'e', 'M', 'i', 'n', 'i', 'm', 'u', 'm') | ||
| 920 | + RAPIDJSON_STRING_(ExclusiveMaximum, 'e', 'x', 'c', 'l', 'u', 's', 'i', 'v', 'e', 'M', 'a', 'x', 'i', 'm', 'u', 'm') | ||
| 921 | + RAPIDJSON_STRING_(MultipleOf, 'm', 'u', 'l', 't', 'i', 'p', 'l', 'e', 'O', 'f') | ||
| 922 | + | ||
| 923 | +#undef RAPIDJSON_STRING_ | ||
| 924 | + | ||
| 925 | +private: | ||
| 926 | + enum SchemaValueType { | ||
| 927 | + kNullSchemaType, | ||
| 928 | + kBooleanSchemaType, | ||
| 929 | + kObjectSchemaType, | ||
| 930 | + kArraySchemaType, | ||
| 931 | + kStringSchemaType, | ||
| 932 | + kNumberSchemaType, | ||
| 933 | + kIntegerSchemaType, | ||
| 934 | + kTotalSchemaType | ||
| 935 | + }; | ||
| 936 | + | ||
| 937 | +#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX | ||
| 938 | + typedef internal::GenericRegex<EncodingType> RegexType; | ||
| 939 | +#elif RAPIDJSON_SCHEMA_USE_STDREGEX | ||
| 940 | + typedef std::basic_regex<Ch> RegexType; | ||
| 941 | +#else | ||
| 942 | + typedef char RegexType; | ||
| 943 | +#endif | ||
| 944 | + | ||
| 945 | + struct SchemaArray { | ||
| 946 | + SchemaArray() : schemas(), count() {} | ||
| 947 | + ~SchemaArray() { AllocatorType::Free(schemas); } | ||
| 948 | + const SchemaType** schemas; | ||
| 949 | + SizeType begin; // begin index of context.validators | ||
| 950 | + SizeType count; | ||
| 951 | + }; | ||
| 952 | + | ||
| 953 | + template <typename V1, typename V2> | ||
| 954 | + void AddUniqueElement(V1& a, const V2& v) { | ||
| 955 | + for (typename V1::ConstValueIterator itr = a.Begin(); itr != a.End(); ++itr) | ||
| 956 | + if (*itr == v) | ||
| 957 | + return; | ||
| 958 | + V1 c(v, *allocator_); | ||
| 959 | + a.PushBack(c, *allocator_); | ||
| 960 | + } | ||
| 961 | + | ||
| 962 | + static const ValueType* GetMember(const ValueType& value, const ValueType& name) { | ||
| 963 | + typename ValueType::ConstMemberIterator itr = value.FindMember(name); | ||
| 964 | + return itr != value.MemberEnd() ? &(itr->value) : 0; | ||
| 965 | + } | ||
| 966 | + | ||
| 967 | + static void AssignIfExist(bool& out, const ValueType& value, const ValueType& name) { | ||
| 968 | + if (const ValueType* v = GetMember(value, name)) | ||
| 969 | + if (v->IsBool()) | ||
| 970 | + out = v->GetBool(); | ||
| 971 | + } | ||
| 972 | + | ||
| 973 | + static void AssignIfExist(SizeType& out, const ValueType& value, const ValueType& name) { | ||
| 974 | + if (const ValueType* v = GetMember(value, name)) | ||
| 975 | + if (v->IsUint64() && v->GetUint64() <= SizeType(~0)) | ||
| 976 | + out = static_cast<SizeType>(v->GetUint64()); | ||
| 977 | + } | ||
| 978 | + | ||
| 979 | + void AssignIfExist(SchemaArray& out, SchemaDocumentType& schemaDocument, const PointerType& p, const ValueType& value, const ValueType& name, const ValueType& document) { | ||
| 980 | + if (const ValueType* v = GetMember(value, name)) { | ||
| 981 | + if (v->IsArray() && v->Size() > 0) { | ||
| 982 | + PointerType q = p.Append(name, allocator_); | ||
| 983 | + out.count = v->Size(); | ||
| 984 | + out.schemas = static_cast<const Schema**>(allocator_->Malloc(out.count * sizeof(const Schema*))); | ||
| 985 | + memset(out.schemas, 0, sizeof(Schema*)* out.count); | ||
| 986 | + for (SizeType i = 0; i < out.count; i++) | ||
| 987 | + schemaDocument.CreateSchema(&out.schemas[i], q.Append(i, allocator_), (*v)[i], document); | ||
| 988 | + out.begin = validatorCount_; | ||
| 989 | + validatorCount_ += out.count; | ||
| 990 | + } | ||
| 991 | + } | ||
| 992 | + } | ||
| 993 | + | ||
| 994 | +#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX | ||
| 995 | + template <typename ValueType> | ||
| 996 | + RegexType* CreatePattern(const ValueType& value) { | ||
| 997 | + if (value.IsString()) { | ||
| 998 | + RegexType* r = new (allocator_->Malloc(sizeof(RegexType))) RegexType(value.GetString()); | ||
| 999 | + if (!r->IsValid()) { | ||
| 1000 | + r->~RegexType(); | ||
| 1001 | + AllocatorType::Free(r); | ||
| 1002 | + r = 0; | ||
| 1003 | + } | ||
| 1004 | + return r; | ||
| 1005 | + } | ||
| 1006 | + return 0; | ||
| 1007 | + } | ||
| 1008 | + | ||
| 1009 | + static bool IsPatternMatch(const RegexType* pattern, const Ch *str, SizeType) { | ||
| 1010 | + GenericRegexSearch<RegexType> rs(*pattern); | ||
| 1011 | + return rs.Search(str); | ||
| 1012 | + } | ||
| 1013 | +#elif RAPIDJSON_SCHEMA_USE_STDREGEX | ||
| 1014 | + template <typename ValueType> | ||
| 1015 | + RegexType* CreatePattern(const ValueType& value) { | ||
| 1016 | + if (value.IsString()) | ||
| 1017 | + try { | ||
| 1018 | + return new (allocator_->Malloc(sizeof(RegexType))) RegexType(value.GetString(), std::size_t(value.GetStringLength()), std::regex_constants::ECMAScript); | ||
| 1019 | + } | ||
| 1020 | + catch (const std::regex_error&) { | ||
| 1021 | + } | ||
| 1022 | + return 0; | ||
| 1023 | + } | ||
| 1024 | + | ||
| 1025 | + static bool IsPatternMatch(const RegexType* pattern, const Ch *str, SizeType length) { | ||
| 1026 | + std::match_results<const Ch*> r; | ||
| 1027 | + return std::regex_search(str, str + length, r, *pattern); | ||
| 1028 | + } | ||
| 1029 | +#else | ||
| 1030 | + template <typename ValueType> | ||
| 1031 | + RegexType* CreatePattern(const ValueType&) { return 0; } | ||
| 1032 | + | ||
| 1033 | + static bool IsPatternMatch(const RegexType*, const Ch *, SizeType) { return true; } | ||
| 1034 | +#endif // RAPIDJSON_SCHEMA_USE_STDREGEX | ||
| 1035 | + | ||
| 1036 | + void AddType(const ValueType& type) { | ||
| 1037 | + if (type == GetNullString() ) type_ |= 1 << kNullSchemaType; | ||
| 1038 | + else if (type == GetBooleanString()) type_ |= 1 << kBooleanSchemaType; | ||
| 1039 | + else if (type == GetObjectString() ) type_ |= 1 << kObjectSchemaType; | ||
| 1040 | + else if (type == GetArrayString() ) type_ |= 1 << kArraySchemaType; | ||
| 1041 | + else if (type == GetStringString() ) type_ |= 1 << kStringSchemaType; | ||
| 1042 | + else if (type == GetIntegerString()) type_ |= 1 << kIntegerSchemaType; | ||
| 1043 | + else if (type == GetNumberString() ) type_ |= (1 << kNumberSchemaType) | (1 << kIntegerSchemaType); | ||
| 1044 | + } | ||
| 1045 | + | ||
| 1046 | + bool CreateParallelValidator(Context& context) const { | ||
| 1047 | + if (enum_ || context.arrayUniqueness) | ||
| 1048 | + context.hasher = context.factory.CreateHasher(); | ||
| 1049 | + | ||
| 1050 | + if (validatorCount_) { | ||
| 1051 | + RAPIDJSON_ASSERT(context.validators == 0); | ||
| 1052 | + context.validators = static_cast<ISchemaValidator**>(context.factory.MallocState(sizeof(ISchemaValidator*) * validatorCount_)); | ||
| 1053 | + context.validatorCount = validatorCount_; | ||
| 1054 | + | ||
| 1055 | + if (allOf_.schemas) | ||
| 1056 | + CreateSchemaValidators(context, allOf_); | ||
| 1057 | + | ||
| 1058 | + if (anyOf_.schemas) | ||
| 1059 | + CreateSchemaValidators(context, anyOf_); | ||
| 1060 | + | ||
| 1061 | + if (oneOf_.schemas) | ||
| 1062 | + CreateSchemaValidators(context, oneOf_); | ||
| 1063 | + | ||
| 1064 | + if (not_) | ||
| 1065 | + context.validators[notValidatorIndex_] = context.factory.CreateSchemaValidator(*not_); | ||
| 1066 | + | ||
| 1067 | + if (hasSchemaDependencies_) { | ||
| 1068 | + for (SizeType i = 0; i < propertyCount_; i++) | ||
| 1069 | + if (properties_[i].dependenciesSchema) | ||
| 1070 | + context.validators[properties_[i].dependenciesValidatorIndex] = context.factory.CreateSchemaValidator(*properties_[i].dependenciesSchema); | ||
| 1071 | + } | ||
| 1072 | + } | ||
| 1073 | + | ||
| 1074 | + return true; | ||
| 1075 | + } | ||
| 1076 | + | ||
| 1077 | + void CreateSchemaValidators(Context& context, const SchemaArray& schemas) const { | ||
| 1078 | + for (SizeType i = 0; i < schemas.count; i++) | ||
| 1079 | + context.validators[schemas.begin + i] = context.factory.CreateSchemaValidator(*schemas.schemas[i]); | ||
| 1080 | + } | ||
| 1081 | + | ||
| 1082 | + // O(n) | ||
| 1083 | + bool FindPropertyIndex(const ValueType& name, SizeType* outIndex) const { | ||
| 1084 | + SizeType len = name.GetStringLength(); | ||
| 1085 | + const Ch* str = name.GetString(); | ||
| 1086 | + for (SizeType index = 0; index < propertyCount_; index++) | ||
| 1087 | + if (properties_[index].name.GetStringLength() == len && | ||
| 1088 | + (std::memcmp(properties_[index].name.GetString(), str, sizeof(Ch) * len) == 0)) | ||
| 1089 | + { | ||
| 1090 | + *outIndex = index; | ||
| 1091 | + return true; | ||
| 1092 | + } | ||
| 1093 | + return false; | ||
| 1094 | + } | ||
| 1095 | + | ||
| 1096 | + bool CheckInt(Context& context, int64_t i) const { | ||
| 1097 | + if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType)))) | ||
| 1098 | + RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); | ||
| 1099 | + | ||
| 1100 | + if (!minimum_.IsNull()) { | ||
| 1101 | + if (minimum_.IsInt64()) { | ||
| 1102 | + if (exclusiveMinimum_ ? i <= minimum_.GetInt64() : i < minimum_.GetInt64()) | ||
| 1103 | + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); | ||
| 1104 | + } | ||
| 1105 | + else if (minimum_.IsUint64()) { | ||
| 1106 | + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); // i <= max(int64_t) < minimum.GetUint64() | ||
| 1107 | + } | ||
| 1108 | + else if (!CheckDoubleMinimum(context, static_cast<double>(i))) | ||
| 1109 | + return false; | ||
| 1110 | + } | ||
| 1111 | + | ||
| 1112 | + if (!maximum_.IsNull()) { | ||
| 1113 | + if (maximum_.IsInt64()) { | ||
| 1114 | + if (exclusiveMaximum_ ? i >= maximum_.GetInt64() : i > maximum_.GetInt64()) | ||
| 1115 | + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); | ||
| 1116 | + } | ||
| 1117 | + else if (maximum_.IsUint64()) { } | ||
| 1118 | + /* do nothing */ // i <= max(int64_t) < maximum_.GetUint64() | ||
| 1119 | + else if (!CheckDoubleMaximum(context, static_cast<double>(i))) | ||
| 1120 | + return false; | ||
| 1121 | + } | ||
| 1122 | + | ||
| 1123 | + if (!multipleOf_.IsNull()) { | ||
| 1124 | + if (multipleOf_.IsUint64()) { | ||
| 1125 | + if (static_cast<uint64_t>(i >= 0 ? i : -i) % multipleOf_.GetUint64() != 0) | ||
| 1126 | + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString()); | ||
| 1127 | + } | ||
| 1128 | + else if (!CheckDoubleMultipleOf(context, static_cast<double>(i))) | ||
| 1129 | + return false; | ||
| 1130 | + } | ||
| 1131 | + | ||
| 1132 | + return true; | ||
| 1133 | + } | ||
| 1134 | + | ||
| 1135 | + bool CheckUint(Context& context, uint64_t i) const { | ||
| 1136 | + if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType)))) | ||
| 1137 | + RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); | ||
| 1138 | + | ||
| 1139 | + if (!minimum_.IsNull()) { | ||
| 1140 | + if (minimum_.IsUint64()) { | ||
| 1141 | + if (exclusiveMinimum_ ? i <= minimum_.GetUint64() : i < minimum_.GetUint64()) | ||
| 1142 | + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); | ||
| 1143 | + } | ||
| 1144 | + else if (minimum_.IsInt64()) | ||
| 1145 | + /* do nothing */; // i >= 0 > minimum.Getint64() | ||
| 1146 | + else if (!CheckDoubleMinimum(context, static_cast<double>(i))) | ||
| 1147 | + return false; | ||
| 1148 | + } | ||
| 1149 | + | ||
| 1150 | + if (!maximum_.IsNull()) { | ||
| 1151 | + if (maximum_.IsUint64()) { | ||
| 1152 | + if (exclusiveMaximum_ ? i >= maximum_.GetUint64() : i > maximum_.GetUint64()) | ||
| 1153 | + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); | ||
| 1154 | + } | ||
| 1155 | + else if (maximum_.IsInt64()) | ||
| 1156 | + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); // i >= 0 > maximum_ | ||
| 1157 | + else if (!CheckDoubleMaximum(context, static_cast<double>(i))) | ||
| 1158 | + return false; | ||
| 1159 | + } | ||
| 1160 | + | ||
| 1161 | + if (!multipleOf_.IsNull()) { | ||
| 1162 | + if (multipleOf_.IsUint64()) { | ||
| 1163 | + if (i % multipleOf_.GetUint64() != 0) | ||
| 1164 | + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString()); | ||
| 1165 | + } | ||
| 1166 | + else if (!CheckDoubleMultipleOf(context, static_cast<double>(i))) | ||
| 1167 | + return false; | ||
| 1168 | + } | ||
| 1169 | + | ||
| 1170 | + return true; | ||
| 1171 | + } | ||
| 1172 | + | ||
| 1173 | + bool CheckDoubleMinimum(Context& context, double d) const { | ||
| 1174 | + if (exclusiveMinimum_ ? d <= minimum_.GetDouble() : d < minimum_.GetDouble()) | ||
| 1175 | + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); | ||
| 1176 | + return true; | ||
| 1177 | + } | ||
| 1178 | + | ||
| 1179 | + bool CheckDoubleMaximum(Context& context, double d) const { | ||
| 1180 | + if (exclusiveMaximum_ ? d >= maximum_.GetDouble() : d > maximum_.GetDouble()) | ||
| 1181 | + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); | ||
| 1182 | + return true; | ||
| 1183 | + } | ||
| 1184 | + | ||
| 1185 | + bool CheckDoubleMultipleOf(Context& context, double d) const { | ||
| 1186 | + double a = std::abs(d), b = std::abs(multipleOf_.GetDouble()); | ||
| 1187 | + double q = std::floor(a / b); | ||
| 1188 | + double r = a - q * b; | ||
| 1189 | + if (r > 0.0) | ||
| 1190 | + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString()); | ||
| 1191 | + return true; | ||
| 1192 | + } | ||
| 1193 | + | ||
| 1194 | + struct Property { | ||
| 1195 | + Property() : schema(), dependenciesSchema(), dependenciesValidatorIndex(), dependencies(), required(false) {} | ||
| 1196 | + ~Property() { AllocatorType::Free(dependencies); } | ||
| 1197 | + SValue name; | ||
| 1198 | + const SchemaType* schema; | ||
| 1199 | + const SchemaType* dependenciesSchema; | ||
| 1200 | + SizeType dependenciesValidatorIndex; | ||
| 1201 | + bool* dependencies; | ||
| 1202 | + bool required; | ||
| 1203 | + }; | ||
| 1204 | + | ||
| 1205 | + struct PatternProperty { | ||
| 1206 | + PatternProperty() : schema(), pattern() {} | ||
| 1207 | + ~PatternProperty() { | ||
| 1208 | + if (pattern) { | ||
| 1209 | + pattern->~RegexType(); | ||
| 1210 | + AllocatorType::Free(pattern); | ||
| 1211 | + } | ||
| 1212 | + } | ||
| 1213 | + const SchemaType* schema; | ||
| 1214 | + RegexType* pattern; | ||
| 1215 | + }; | ||
| 1216 | + | ||
| 1217 | + AllocatorType* allocator_; | ||
| 1218 | + const SchemaType* typeless_; | ||
| 1219 | + uint64_t* enum_; | ||
| 1220 | + SizeType enumCount_; | ||
| 1221 | + SchemaArray allOf_; | ||
| 1222 | + SchemaArray anyOf_; | ||
| 1223 | + SchemaArray oneOf_; | ||
| 1224 | + const SchemaType* not_; | ||
| 1225 | + unsigned type_; // bitmask of kSchemaType | ||
| 1226 | + SizeType validatorCount_; | ||
| 1227 | + SizeType notValidatorIndex_; | ||
| 1228 | + | ||
| 1229 | + Property* properties_; | ||
| 1230 | + const SchemaType* additionalPropertiesSchema_; | ||
| 1231 | + PatternProperty* patternProperties_; | ||
| 1232 | + SizeType patternPropertyCount_; | ||
| 1233 | + SizeType propertyCount_; | ||
| 1234 | + SizeType minProperties_; | ||
| 1235 | + SizeType maxProperties_; | ||
| 1236 | + bool additionalProperties_; | ||
| 1237 | + bool hasDependencies_; | ||
| 1238 | + bool hasRequired_; | ||
| 1239 | + bool hasSchemaDependencies_; | ||
| 1240 | + | ||
| 1241 | + const SchemaType* additionalItemsSchema_; | ||
| 1242 | + const SchemaType* itemsList_; | ||
| 1243 | + const SchemaType** itemsTuple_; | ||
| 1244 | + SizeType itemsTupleCount_; | ||
| 1245 | + SizeType minItems_; | ||
| 1246 | + SizeType maxItems_; | ||
| 1247 | + bool additionalItems_; | ||
| 1248 | + bool uniqueItems_; | ||
| 1249 | + | ||
| 1250 | + RegexType* pattern_; | ||
| 1251 | + SizeType minLength_; | ||
| 1252 | + SizeType maxLength_; | ||
| 1253 | + | ||
| 1254 | + SValue minimum_; | ||
| 1255 | + SValue maximum_; | ||
| 1256 | + SValue multipleOf_; | ||
| 1257 | + bool exclusiveMinimum_; | ||
| 1258 | + bool exclusiveMaximum_; | ||
| 1259 | +}; | ||
| 1260 | + | ||
| 1261 | +template<typename Stack, typename Ch> | ||
| 1262 | +struct TokenHelper { | ||
| 1263 | + RAPIDJSON_FORCEINLINE static void AppendIndexToken(Stack& documentStack, SizeType index) { | ||
| 1264 | + *documentStack.template Push<Ch>() = '/'; | ||
| 1265 | + char buffer[21]; | ||
| 1266 | + size_t length = static_cast<size_t>((sizeof(SizeType) == 4 ? u32toa(index, buffer) : u64toa(index, buffer)) - buffer); | ||
| 1267 | + for (size_t i = 0; i < length; i++) | ||
| 1268 | + *documentStack.template Push<Ch>() = static_cast<Ch>(buffer[i]); | ||
| 1269 | + } | ||
| 1270 | +}; | ||
| 1271 | + | ||
| 1272 | +// Partial specialized version for char to prevent buffer copying. | ||
| 1273 | +template <typename Stack> | ||
| 1274 | +struct TokenHelper<Stack, char> { | ||
| 1275 | + RAPIDJSON_FORCEINLINE static void AppendIndexToken(Stack& documentStack, SizeType index) { | ||
| 1276 | + if (sizeof(SizeType) == 4) { | ||
| 1277 | + char *buffer = documentStack.template Push<char>(1 + 10); // '/' + uint | ||
| 1278 | + *buffer++ = '/'; | ||
| 1279 | + const char* end = internal::u32toa(index, buffer); | ||
| 1280 | + documentStack.template Pop<char>(static_cast<size_t>(10 - (end - buffer))); | ||
| 1281 | + } | ||
| 1282 | + else { | ||
| 1283 | + char *buffer = documentStack.template Push<char>(1 + 20); // '/' + uint64 | ||
| 1284 | + *buffer++ = '/'; | ||
| 1285 | + const char* end = internal::u64toa(index, buffer); | ||
| 1286 | + documentStack.template Pop<char>(static_cast<size_t>(20 - (end - buffer))); | ||
| 1287 | + } | ||
| 1288 | + } | ||
| 1289 | +}; | ||
| 1290 | + | ||
| 1291 | +} // namespace internal | ||
| 1292 | + | ||
| 1293 | +/////////////////////////////////////////////////////////////////////////////// | ||
| 1294 | +// IGenericRemoteSchemaDocumentProvider | ||
| 1295 | + | ||
| 1296 | +template <typename SchemaDocumentType> | ||
| 1297 | +class IGenericRemoteSchemaDocumentProvider { | ||
| 1298 | +public: | ||
| 1299 | + typedef typename SchemaDocumentType::Ch Ch; | ||
| 1300 | + | ||
| 1301 | + virtual ~IGenericRemoteSchemaDocumentProvider() {} | ||
| 1302 | + virtual const SchemaDocumentType* GetRemoteDocument(const Ch* uri, SizeType length) = 0; | ||
| 1303 | +}; | ||
| 1304 | + | ||
| 1305 | +/////////////////////////////////////////////////////////////////////////////// | ||
| 1306 | +// GenericSchemaDocument | ||
| 1307 | + | ||
| 1308 | +//! JSON schema document. | ||
| 1309 | +/*! | ||
| 1310 | + A JSON schema document is a compiled version of a JSON schema. | ||
| 1311 | + It is basically a tree of internal::Schema. | ||
| 1312 | + | ||
| 1313 | + \note This is an immutable class (i.e. its instance cannot be modified after construction). | ||
| 1314 | + \tparam ValueT Type of JSON value (e.g. \c Value ), which also determine the encoding. | ||
| 1315 | + \tparam Allocator Allocator type for allocating memory of this document. | ||
| 1316 | +*/ | ||
| 1317 | +template <typename ValueT, typename Allocator = CrtAllocator> | ||
| 1318 | +class GenericSchemaDocument { | ||
| 1319 | +public: | ||
| 1320 | + typedef ValueT ValueType; | ||
| 1321 | + typedef IGenericRemoteSchemaDocumentProvider<GenericSchemaDocument> IRemoteSchemaDocumentProviderType; | ||
| 1322 | + typedef Allocator AllocatorType; | ||
| 1323 | + typedef typename ValueType::EncodingType EncodingType; | ||
| 1324 | + typedef typename EncodingType::Ch Ch; | ||
| 1325 | + typedef internal::Schema<GenericSchemaDocument> SchemaType; | ||
| 1326 | + typedef GenericPointer<ValueType, Allocator> PointerType; | ||
| 1327 | + friend class internal::Schema<GenericSchemaDocument>; | ||
| 1328 | + template <typename, typename, typename> | ||
| 1329 | + friend class GenericSchemaValidator; | ||
| 1330 | + | ||
| 1331 | + //! Constructor. | ||
| 1332 | + /*! | ||
| 1333 | + Compile a JSON document into schema document. | ||
| 1334 | + | ||
| 1335 | + \param document A JSON document as source. | ||
| 1336 | + \param remoteProvider An optional remote schema document provider for resolving remote reference. Can be null. | ||
| 1337 | + \param allocator An optional allocator instance for allocating memory. Can be null. | ||
| 1338 | + */ | ||
| 1339 | + explicit GenericSchemaDocument(const ValueType& document, IRemoteSchemaDocumentProviderType* remoteProvider = 0, Allocator* allocator = 0) : | ||
| 1340 | + remoteProvider_(remoteProvider), | ||
| 1341 | + allocator_(allocator), | ||
| 1342 | + ownAllocator_(), | ||
| 1343 | + root_(), | ||
| 1344 | + typeless_(), | ||
| 1345 | + schemaMap_(allocator, kInitialSchemaMapSize), | ||
| 1346 | + schemaRef_(allocator, kInitialSchemaRefSize) | ||
| 1347 | + { | ||
| 1348 | + if (!allocator_) | ||
| 1349 | + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); | ||
| 1350 | + | ||
| 1351 | + typeless_ = static_cast<SchemaType*>(allocator_->Malloc(sizeof(SchemaType))); | ||
| 1352 | + new (typeless_) SchemaType(this, PointerType(), ValueType(kObjectType).Move(), ValueType(kObjectType).Move(), 0); | ||
| 1353 | + | ||
| 1354 | + // Generate root schema, it will call CreateSchema() to create sub-schemas, | ||
| 1355 | + // And call AddRefSchema() if there are $ref. | ||
| 1356 | + CreateSchemaRecursive(&root_, PointerType(), document, document); | ||
| 1357 | + | ||
| 1358 | + // Resolve $ref | ||
| 1359 | + while (!schemaRef_.Empty()) { | ||
| 1360 | + SchemaRefEntry* refEntry = schemaRef_.template Pop<SchemaRefEntry>(1); | ||
| 1361 | + if (const SchemaType* s = GetSchema(refEntry->target)) { | ||
| 1362 | + if (refEntry->schema) | ||
| 1363 | + *refEntry->schema = s; | ||
| 1364 | + | ||
| 1365 | + // Create entry in map if not exist | ||
| 1366 | + if (!GetSchema(refEntry->source)) { | ||
| 1367 | + new (schemaMap_.template Push<SchemaEntry>()) SchemaEntry(refEntry->source, const_cast<SchemaType*>(s), false, allocator_); | ||
| 1368 | + } | ||
| 1369 | + } | ||
| 1370 | + else if (refEntry->schema) | ||
| 1371 | + *refEntry->schema = typeless_; | ||
| 1372 | + | ||
| 1373 | + refEntry->~SchemaRefEntry(); | ||
| 1374 | + } | ||
| 1375 | + | ||
| 1376 | + RAPIDJSON_ASSERT(root_ != 0); | ||
| 1377 | + | ||
| 1378 | + schemaRef_.ShrinkToFit(); // Deallocate all memory for ref | ||
| 1379 | + } | ||
| 1380 | + | ||
| 1381 | +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS | ||
| 1382 | + //! Move constructor in C++11 | ||
| 1383 | + GenericSchemaDocument(GenericSchemaDocument&& rhs) RAPIDJSON_NOEXCEPT : | ||
| 1384 | + remoteProvider_(rhs.remoteProvider_), | ||
| 1385 | + allocator_(rhs.allocator_), | ||
| 1386 | + ownAllocator_(rhs.ownAllocator_), | ||
| 1387 | + root_(rhs.root_), | ||
| 1388 | + typeless_(rhs.typeless_), | ||
| 1389 | + schemaMap_(std::move(rhs.schemaMap_)), | ||
| 1390 | + schemaRef_(std::move(rhs.schemaRef_)) | ||
| 1391 | + { | ||
| 1392 | + rhs.remoteProvider_ = 0; | ||
| 1393 | + rhs.allocator_ = 0; | ||
| 1394 | + rhs.ownAllocator_ = 0; | ||
| 1395 | + rhs.typeless_ = 0; | ||
| 1396 | + } | ||
| 1397 | +#endif | ||
| 1398 | + | ||
| 1399 | + //! Destructor | ||
| 1400 | + ~GenericSchemaDocument() { | ||
| 1401 | + while (!schemaMap_.Empty()) | ||
| 1402 | + schemaMap_.template Pop<SchemaEntry>(1)->~SchemaEntry(); | ||
| 1403 | + | ||
| 1404 | + if (typeless_) { | ||
| 1405 | + typeless_->~SchemaType(); | ||
| 1406 | + Allocator::Free(typeless_); | ||
| 1407 | + } | ||
| 1408 | + | ||
| 1409 | + RAPIDJSON_DELETE(ownAllocator_); | ||
| 1410 | + } | ||
| 1411 | + | ||
| 1412 | + //! Get the root schema. | ||
| 1413 | + const SchemaType& GetRoot() const { return *root_; } | ||
| 1414 | + | ||
| 1415 | +private: | ||
| 1416 | + //! Prohibit copying | ||
| 1417 | + GenericSchemaDocument(const GenericSchemaDocument&); | ||
| 1418 | + //! Prohibit assignment | ||
| 1419 | + GenericSchemaDocument& operator=(const GenericSchemaDocument&); | ||
| 1420 | + | ||
| 1421 | + struct SchemaRefEntry { | ||
| 1422 | + SchemaRefEntry(const PointerType& s, const PointerType& t, const SchemaType** outSchema, Allocator *allocator) : source(s, allocator), target(t, allocator), schema(outSchema) {} | ||
| 1423 | + PointerType source; | ||
| 1424 | + PointerType target; | ||
| 1425 | + const SchemaType** schema; | ||
| 1426 | + }; | ||
| 1427 | + | ||
| 1428 | + struct SchemaEntry { | ||
| 1429 | + SchemaEntry(const PointerType& p, SchemaType* s, bool o, Allocator* allocator) : pointer(p, allocator), schema(s), owned(o) {} | ||
| 1430 | + ~SchemaEntry() { | ||
| 1431 | + if (owned) { | ||
| 1432 | + schema->~SchemaType(); | ||
| 1433 | + Allocator::Free(schema); | ||
| 1434 | + } | ||
| 1435 | + } | ||
| 1436 | + PointerType pointer; | ||
| 1437 | + SchemaType* schema; | ||
| 1438 | + bool owned; | ||
| 1439 | + }; | ||
| 1440 | + | ||
| 1441 | + void CreateSchemaRecursive(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document) { | ||
| 1442 | + if (schema) | ||
| 1443 | + *schema = typeless_; | ||
| 1444 | + | ||
| 1445 | + if (v.GetType() == kObjectType) { | ||
| 1446 | + const SchemaType* s = GetSchema(pointer); | ||
| 1447 | + if (!s) | ||
| 1448 | + CreateSchema(schema, pointer, v, document); | ||
| 1449 | + | ||
| 1450 | + for (typename ValueType::ConstMemberIterator itr = v.MemberBegin(); itr != v.MemberEnd(); ++itr) | ||
| 1451 | + CreateSchemaRecursive(0, pointer.Append(itr->name, allocator_), itr->value, document); | ||
| 1452 | + } | ||
| 1453 | + else if (v.GetType() == kArrayType) | ||
| 1454 | + for (SizeType i = 0; i < v.Size(); i++) | ||
| 1455 | + CreateSchemaRecursive(0, pointer.Append(i, allocator_), v[i], document); | ||
| 1456 | + } | ||
| 1457 | + | ||
| 1458 | + void CreateSchema(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document) { | ||
| 1459 | + RAPIDJSON_ASSERT(pointer.IsValid()); | ||
| 1460 | + if (v.IsObject()) { | ||
| 1461 | + if (!HandleRefSchema(pointer, schema, v, document)) { | ||
| 1462 | + SchemaType* s = new (allocator_->Malloc(sizeof(SchemaType))) SchemaType(this, pointer, v, document, allocator_); | ||
| 1463 | + new (schemaMap_.template Push<SchemaEntry>()) SchemaEntry(pointer, s, true, allocator_); | ||
| 1464 | + if (schema) | ||
| 1465 | + *schema = s; | ||
| 1466 | + } | ||
| 1467 | + } | ||
| 1468 | + } | ||
| 1469 | + | ||
| 1470 | + bool HandleRefSchema(const PointerType& source, const SchemaType** schema, const ValueType& v, const ValueType& document) { | ||
| 1471 | + static const Ch kRefString[] = { '$', 'r', 'e', 'f', '\0' }; | ||
| 1472 | + static const ValueType kRefValue(kRefString, 4); | ||
| 1473 | + | ||
| 1474 | + typename ValueType::ConstMemberIterator itr = v.FindMember(kRefValue); | ||
| 1475 | + if (itr == v.MemberEnd()) | ||
| 1476 | + return false; | ||
| 1477 | + | ||
| 1478 | + if (itr->value.IsString()) { | ||
| 1479 | + SizeType len = itr->value.GetStringLength(); | ||
| 1480 | + if (len > 0) { | ||
| 1481 | + const Ch* s = itr->value.GetString(); | ||
| 1482 | + SizeType i = 0; | ||
| 1483 | + while (i < len && s[i] != '#') // Find the first # | ||
| 1484 | + i++; | ||
| 1485 | + | ||
| 1486 | + if (i > 0) { // Remote reference, resolve immediately | ||
| 1487 | + if (remoteProvider_) { | ||
| 1488 | + if (const GenericSchemaDocument* remoteDocument = remoteProvider_->GetRemoteDocument(s, i)) { | ||
| 1489 | + PointerType pointer(&s[i], len - i, allocator_); | ||
| 1490 | + if (pointer.IsValid()) { | ||
| 1491 | + if (const SchemaType* sc = remoteDocument->GetSchema(pointer)) { | ||
| 1492 | + if (schema) | ||
| 1493 | + *schema = sc; | ||
| 1494 | + return true; | ||
| 1495 | + } | ||
| 1496 | + } | ||
| 1497 | + } | ||
| 1498 | + } | ||
| 1499 | + } | ||
| 1500 | + else if (s[i] == '#') { // Local reference, defer resolution | ||
| 1501 | + PointerType pointer(&s[i], len - i, allocator_); | ||
| 1502 | + if (pointer.IsValid()) { | ||
| 1503 | + if (const ValueType* nv = pointer.Get(document)) | ||
| 1504 | + if (HandleRefSchema(source, schema, *nv, document)) | ||
| 1505 | + return true; | ||
| 1506 | + | ||
| 1507 | + new (schemaRef_.template Push<SchemaRefEntry>()) SchemaRefEntry(source, pointer, schema, allocator_); | ||
| 1508 | + return true; | ||
| 1509 | + } | ||
| 1510 | + } | ||
| 1511 | + } | ||
| 1512 | + } | ||
| 1513 | + return false; | ||
| 1514 | + } | ||
| 1515 | + | ||
| 1516 | + const SchemaType* GetSchema(const PointerType& pointer) const { | ||
| 1517 | + for (const SchemaEntry* target = schemaMap_.template Bottom<SchemaEntry>(); target != schemaMap_.template End<SchemaEntry>(); ++target) | ||
| 1518 | + if (pointer == target->pointer) | ||
| 1519 | + return target->schema; | ||
| 1520 | + return 0; | ||
| 1521 | + } | ||
| 1522 | + | ||
| 1523 | + PointerType GetPointer(const SchemaType* schema) const { | ||
| 1524 | + for (const SchemaEntry* target = schemaMap_.template Bottom<SchemaEntry>(); target != schemaMap_.template End<SchemaEntry>(); ++target) | ||
| 1525 | + if (schema == target->schema) | ||
| 1526 | + return target->pointer; | ||
| 1527 | + return PointerType(); | ||
| 1528 | + } | ||
| 1529 | + | ||
| 1530 | + const SchemaType* GetTypeless() const { return typeless_; } | ||
| 1531 | + | ||
| 1532 | + static const size_t kInitialSchemaMapSize = 64; | ||
| 1533 | + static const size_t kInitialSchemaRefSize = 64; | ||
| 1534 | + | ||
| 1535 | + IRemoteSchemaDocumentProviderType* remoteProvider_; | ||
| 1536 | + Allocator *allocator_; | ||
| 1537 | + Allocator *ownAllocator_; | ||
| 1538 | + const SchemaType* root_; //!< Root schema. | ||
| 1539 | + SchemaType* typeless_; | ||
| 1540 | + internal::Stack<Allocator> schemaMap_; // Stores created Pointer -> Schemas | ||
| 1541 | + internal::Stack<Allocator> schemaRef_; // Stores Pointer from $ref and schema which holds the $ref | ||
| 1542 | +}; | ||
| 1543 | + | ||
| 1544 | +//! GenericSchemaDocument using Value type. | ||
| 1545 | +typedef GenericSchemaDocument<Value> SchemaDocument; | ||
| 1546 | +//! IGenericRemoteSchemaDocumentProvider using SchemaDocument. | ||
| 1547 | +typedef IGenericRemoteSchemaDocumentProvider<SchemaDocument> IRemoteSchemaDocumentProvider; | ||
| 1548 | + | ||
| 1549 | +/////////////////////////////////////////////////////////////////////////////// | ||
| 1550 | +// GenericSchemaValidator | ||
| 1551 | + | ||
| 1552 | +//! JSON Schema Validator. | ||
| 1553 | +/*! | ||
| 1554 | + A SAX style JSON schema validator. | ||
| 1555 | + It uses a \c GenericSchemaDocument to validate SAX events. | ||
| 1556 | + It delegates the incoming SAX events to an output handler. | ||
| 1557 | + The default output handler does nothing. | ||
| 1558 | + It can be reused multiple times by calling \c Reset(). | ||
| 1559 | + | ||
| 1560 | + \tparam SchemaDocumentType Type of schema document. | ||
| 1561 | + \tparam OutputHandler Type of output handler. Default handler does nothing. | ||
| 1562 | + \tparam StateAllocator Allocator for storing the internal validation states. | ||
| 1563 | +*/ | ||
| 1564 | +template < | ||
| 1565 | + typename SchemaDocumentType, | ||
| 1566 | + typename OutputHandler = BaseReaderHandler<typename SchemaDocumentType::SchemaType::EncodingType>, | ||
| 1567 | + typename StateAllocator = CrtAllocator> | ||
| 1568 | +class GenericSchemaValidator : | ||
| 1569 | + public internal::ISchemaStateFactory<typename SchemaDocumentType::SchemaType>, | ||
| 1570 | + public internal::ISchemaValidator | ||
| 1571 | +{ | ||
| 1572 | +public: | ||
| 1573 | + typedef typename SchemaDocumentType::SchemaType SchemaType; | ||
| 1574 | + typedef typename SchemaDocumentType::PointerType PointerType; | ||
| 1575 | + typedef typename SchemaType::EncodingType EncodingType; | ||
| 1576 | + typedef typename EncodingType::Ch Ch; | ||
| 1577 | + | ||
| 1578 | + //! Constructor without output handler. | ||
| 1579 | + /*! | ||
| 1580 | + \param schemaDocument The schema document to conform to. | ||
| 1581 | + \param allocator Optional allocator for storing internal validation states. | ||
| 1582 | + \param schemaStackCapacity Optional initial capacity of schema path stack. | ||
| 1583 | + \param documentStackCapacity Optional initial capacity of document path stack. | ||
| 1584 | + */ | ||
| 1585 | + GenericSchemaValidator( | ||
| 1586 | + const SchemaDocumentType& schemaDocument, | ||
| 1587 | + StateAllocator* allocator = 0, | ||
| 1588 | + size_t schemaStackCapacity = kDefaultSchemaStackCapacity, | ||
| 1589 | + size_t documentStackCapacity = kDefaultDocumentStackCapacity) | ||
| 1590 | + : | ||
| 1591 | + schemaDocument_(&schemaDocument), | ||
| 1592 | + root_(schemaDocument.GetRoot()), | ||
| 1593 | + stateAllocator_(allocator), | ||
| 1594 | + ownStateAllocator_(0), | ||
| 1595 | + schemaStack_(allocator, schemaStackCapacity), | ||
| 1596 | + documentStack_(allocator, documentStackCapacity), | ||
| 1597 | + outputHandler_(CreateNullHandler()), | ||
| 1598 | + valid_(true) | ||
| 1599 | +#if RAPIDJSON_SCHEMA_VERBOSE | ||
| 1600 | + , depth_(0) | ||
| 1601 | +#endif | ||
| 1602 | + { | ||
| 1603 | + } | ||
| 1604 | + | ||
| 1605 | + //! Constructor with output handler. | ||
| 1606 | + /*! | ||
| 1607 | + \param schemaDocument The schema document to conform to. | ||
| 1608 | + \param allocator Optional allocator for storing internal validation states. | ||
| 1609 | + \param schemaStackCapacity Optional initial capacity of schema path stack. | ||
| 1610 | + \param documentStackCapacity Optional initial capacity of document path stack. | ||
| 1611 | + */ | ||
| 1612 | + GenericSchemaValidator( | ||
| 1613 | + const SchemaDocumentType& schemaDocument, | ||
| 1614 | + OutputHandler& outputHandler, | ||
| 1615 | + StateAllocator* allocator = 0, | ||
| 1616 | + size_t schemaStackCapacity = kDefaultSchemaStackCapacity, | ||
| 1617 | + size_t documentStackCapacity = kDefaultDocumentStackCapacity) | ||
| 1618 | + : | ||
| 1619 | + schemaDocument_(&schemaDocument), | ||
| 1620 | + root_(schemaDocument.GetRoot()), | ||
| 1621 | + stateAllocator_(allocator), | ||
| 1622 | + ownStateAllocator_(0), | ||
| 1623 | + schemaStack_(allocator, schemaStackCapacity), | ||
| 1624 | + documentStack_(allocator, documentStackCapacity), | ||
| 1625 | + outputHandler_(outputHandler), | ||
| 1626 | + nullHandler_(0), | ||
| 1627 | + valid_(true) | ||
| 1628 | +#if RAPIDJSON_SCHEMA_VERBOSE | ||
| 1629 | + , depth_(0) | ||
| 1630 | +#endif | ||
| 1631 | + { | ||
| 1632 | + } | ||
| 1633 | + | ||
| 1634 | + //! Destructor. | ||
| 1635 | + ~GenericSchemaValidator() { | ||
| 1636 | + Reset(); | ||
| 1637 | + if (nullHandler_) { | ||
| 1638 | + nullHandler_->~OutputHandler(); | ||
| 1639 | + StateAllocator::Free(nullHandler_); | ||
| 1640 | + } | ||
| 1641 | + RAPIDJSON_DELETE(ownStateAllocator_); | ||
| 1642 | + } | ||
| 1643 | + | ||
| 1644 | + //! Reset the internal states. | ||
| 1645 | + void Reset() { | ||
| 1646 | + while (!schemaStack_.Empty()) | ||
| 1647 | + PopSchema(); | ||
| 1648 | + documentStack_.Clear(); | ||
| 1649 | + valid_ = true; | ||
| 1650 | + } | ||
| 1651 | + | ||
| 1652 | + //! Checks whether the current state is valid. | ||
| 1653 | + // Implementation of ISchemaValidator | ||
| 1654 | + virtual bool IsValid() const { return valid_; } | ||
| 1655 | + | ||
| 1656 | + //! Gets the JSON pointer pointed to the invalid schema. | ||
| 1657 | + PointerType GetInvalidSchemaPointer() const { | ||
| 1658 | + return schemaStack_.Empty() ? PointerType() : schemaDocument_->GetPointer(&CurrentSchema()); | ||
| 1659 | + } | ||
| 1660 | + | ||
| 1661 | + //! Gets the keyword of invalid schema. | ||
| 1662 | + const Ch* GetInvalidSchemaKeyword() const { | ||
| 1663 | + return schemaStack_.Empty() ? 0 : CurrentContext().invalidKeyword; | ||
| 1664 | + } | ||
| 1665 | + | ||
| 1666 | + //! Gets the JSON pointer pointed to the invalid value. | ||
| 1667 | + PointerType GetInvalidDocumentPointer() const { | ||
| 1668 | + return documentStack_.Empty() ? PointerType() : PointerType(documentStack_.template Bottom<Ch>(), documentStack_.GetSize() / sizeof(Ch)); | ||
| 1669 | + } | ||
| 1670 | + | ||
| 1671 | +#if RAPIDJSON_SCHEMA_VERBOSE | ||
| 1672 | +#define RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_() \ | ||
| 1673 | +RAPIDJSON_MULTILINEMACRO_BEGIN\ | ||
| 1674 | + *documentStack_.template Push<Ch>() = '\0';\ | ||
| 1675 | + documentStack_.template Pop<Ch>(1);\ | ||
| 1676 | + internal::PrintInvalidDocument(documentStack_.template Bottom<Ch>());\ | ||
| 1677 | +RAPIDJSON_MULTILINEMACRO_END | ||
| 1678 | +#else | ||
| 1679 | +#define RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_() | ||
| 1680 | +#endif | ||
| 1681 | + | ||
| 1682 | +#define RAPIDJSON_SCHEMA_HANDLE_BEGIN_(method, arg1)\ | ||
| 1683 | + if (!valid_) return false; \ | ||
| 1684 | + if (!BeginValue() || !CurrentSchema().method arg1) {\ | ||
| 1685 | + RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_();\ | ||
| 1686 | + return valid_ = false;\ | ||
| 1687 | + } | ||
| 1688 | + | ||
| 1689 | +#define RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(method, arg2)\ | ||
| 1690 | + for (Context* context = schemaStack_.template Bottom<Context>(); context != schemaStack_.template End<Context>(); context++) {\ | ||
| 1691 | + if (context->hasher)\ | ||
| 1692 | + static_cast<HasherType*>(context->hasher)->method arg2;\ | ||
| 1693 | + if (context->validators)\ | ||
| 1694 | + for (SizeType i_ = 0; i_ < context->validatorCount; i_++)\ | ||
| 1695 | + static_cast<GenericSchemaValidator*>(context->validators[i_])->method arg2;\ | ||
| 1696 | + if (context->patternPropertiesValidators)\ | ||
| 1697 | + for (SizeType i_ = 0; i_ < context->patternPropertiesValidatorCount; i_++)\ | ||
| 1698 | + static_cast<GenericSchemaValidator*>(context->patternPropertiesValidators[i_])->method arg2;\ | ||
| 1699 | + } | ||
| 1700 | + | ||
| 1701 | +#define RAPIDJSON_SCHEMA_HANDLE_END_(method, arg2)\ | ||
| 1702 | + return valid_ = EndValue() && outputHandler_.method arg2 | ||
| 1703 | + | ||
| 1704 | +#define RAPIDJSON_SCHEMA_HANDLE_VALUE_(method, arg1, arg2) \ | ||
| 1705 | + RAPIDJSON_SCHEMA_HANDLE_BEGIN_ (method, arg1);\ | ||
| 1706 | + RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(method, arg2);\ | ||
| 1707 | + RAPIDJSON_SCHEMA_HANDLE_END_ (method, arg2) | ||
| 1708 | + | ||
| 1709 | + bool Null() { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Null, (CurrentContext() ), ( )); } | ||
| 1710 | + bool Bool(bool b) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Bool, (CurrentContext(), b), (b)); } | ||
| 1711 | + bool Int(int i) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Int, (CurrentContext(), i), (i)); } | ||
| 1712 | + bool Uint(unsigned u) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Uint, (CurrentContext(), u), (u)); } | ||
| 1713 | + bool Int64(int64_t i) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Int64, (CurrentContext(), i), (i)); } | ||
| 1714 | + bool Uint64(uint64_t u) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Uint64, (CurrentContext(), u), (u)); } | ||
| 1715 | + bool Double(double d) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Double, (CurrentContext(), d), (d)); } | ||
| 1716 | + bool RawNumber(const Ch* str, SizeType length, bool copy) | ||
| 1717 | + { RAPIDJSON_SCHEMA_HANDLE_VALUE_(String, (CurrentContext(), str, length, copy), (str, length, copy)); } | ||
| 1718 | + bool String(const Ch* str, SizeType length, bool copy) | ||
| 1719 | + { RAPIDJSON_SCHEMA_HANDLE_VALUE_(String, (CurrentContext(), str, length, copy), (str, length, copy)); } | ||
| 1720 | + | ||
| 1721 | + bool StartObject() { | ||
| 1722 | + RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartObject, (CurrentContext())); | ||
| 1723 | + RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartObject, ()); | ||
| 1724 | + return valid_ = outputHandler_.StartObject(); | ||
| 1725 | + } | ||
| 1726 | + | ||
| 1727 | + bool Key(const Ch* str, SizeType len, bool copy) { | ||
| 1728 | + if (!valid_) return false; | ||
| 1729 | + AppendToken(str, len); | ||
| 1730 | + if (!CurrentSchema().Key(CurrentContext(), str, len, copy)) return valid_ = false; | ||
| 1731 | + RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(Key, (str, len, copy)); | ||
| 1732 | + return valid_ = outputHandler_.Key(str, len, copy); | ||
| 1733 | + } | ||
| 1734 | + | ||
| 1735 | + bool EndObject(SizeType memberCount) { | ||
| 1736 | + if (!valid_) return false; | ||
| 1737 | + RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndObject, (memberCount)); | ||
| 1738 | + if (!CurrentSchema().EndObject(CurrentContext(), memberCount)) return valid_ = false; | ||
| 1739 | + RAPIDJSON_SCHEMA_HANDLE_END_(EndObject, (memberCount)); | ||
| 1740 | + } | ||
| 1741 | + | ||
| 1742 | + bool StartArray() { | ||
| 1743 | + RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartArray, (CurrentContext())); | ||
| 1744 | + RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartArray, ()); | ||
| 1745 | + return valid_ = outputHandler_.StartArray(); | ||
| 1746 | + } | ||
| 1747 | + | ||
| 1748 | + bool EndArray(SizeType elementCount) { | ||
| 1749 | + if (!valid_) return false; | ||
| 1750 | + RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndArray, (elementCount)); | ||
| 1751 | + if (!CurrentSchema().EndArray(CurrentContext(), elementCount)) return valid_ = false; | ||
| 1752 | + RAPIDJSON_SCHEMA_HANDLE_END_(EndArray, (elementCount)); | ||
| 1753 | + } | ||
| 1754 | + | ||
| 1755 | +#undef RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_ | ||
| 1756 | +#undef RAPIDJSON_SCHEMA_HANDLE_BEGIN_ | ||
| 1757 | +#undef RAPIDJSON_SCHEMA_HANDLE_PARALLEL_ | ||
| 1758 | +#undef RAPIDJSON_SCHEMA_HANDLE_VALUE_ | ||
| 1759 | + | ||
| 1760 | + // Implementation of ISchemaStateFactory<SchemaType> | ||
| 1761 | + virtual ISchemaValidator* CreateSchemaValidator(const SchemaType& root) { | ||
| 1762 | + return new (GetStateAllocator().Malloc(sizeof(GenericSchemaValidator))) GenericSchemaValidator(*schemaDocument_, root, | ||
| 1763 | +#if RAPIDJSON_SCHEMA_VERBOSE | ||
| 1764 | + depth_ + 1, | ||
| 1765 | +#endif | ||
| 1766 | + &GetStateAllocator()); | ||
| 1767 | + } | ||
| 1768 | + | ||
| 1769 | + virtual void DestroySchemaValidator(ISchemaValidator* validator) { | ||
| 1770 | + GenericSchemaValidator* v = static_cast<GenericSchemaValidator*>(validator); | ||
| 1771 | + v->~GenericSchemaValidator(); | ||
| 1772 | + StateAllocator::Free(v); | ||
| 1773 | + } | ||
| 1774 | + | ||
| 1775 | + virtual void* CreateHasher() { | ||
| 1776 | + return new (GetStateAllocator().Malloc(sizeof(HasherType))) HasherType(&GetStateAllocator()); | ||
| 1777 | + } | ||
| 1778 | + | ||
| 1779 | + virtual uint64_t GetHashCode(void* hasher) { | ||
| 1780 | + return static_cast<HasherType*>(hasher)->GetHashCode(); | ||
| 1781 | + } | ||
| 1782 | + | ||
| 1783 | + virtual void DestroryHasher(void* hasher) { | ||
| 1784 | + HasherType* h = static_cast<HasherType*>(hasher); | ||
| 1785 | + h->~HasherType(); | ||
| 1786 | + StateAllocator::Free(h); | ||
| 1787 | + } | ||
| 1788 | + | ||
| 1789 | + virtual void* MallocState(size_t size) { | ||
| 1790 | + return GetStateAllocator().Malloc(size); | ||
| 1791 | + } | ||
| 1792 | + | ||
| 1793 | + virtual void FreeState(void* p) { | ||
| 1794 | + StateAllocator::Free(p); | ||
| 1795 | + } | ||
| 1796 | + | ||
| 1797 | +private: | ||
| 1798 | + typedef typename SchemaType::Context Context; | ||
| 1799 | + typedef GenericValue<UTF8<>, StateAllocator> HashCodeArray; | ||
| 1800 | + typedef internal::Hasher<EncodingType, StateAllocator> HasherType; | ||
| 1801 | + | ||
| 1802 | + GenericSchemaValidator( | ||
| 1803 | + const SchemaDocumentType& schemaDocument, | ||
| 1804 | + const SchemaType& root, | ||
| 1805 | +#if RAPIDJSON_SCHEMA_VERBOSE | ||
| 1806 | + unsigned depth, | ||
| 1807 | +#endif | ||
| 1808 | + StateAllocator* allocator = 0, | ||
| 1809 | + size_t schemaStackCapacity = kDefaultSchemaStackCapacity, | ||
| 1810 | + size_t documentStackCapacity = kDefaultDocumentStackCapacity) | ||
| 1811 | + : | ||
| 1812 | + schemaDocument_(&schemaDocument), | ||
| 1813 | + root_(root), | ||
| 1814 | + stateAllocator_(allocator), | ||
| 1815 | + ownStateAllocator_(0), | ||
| 1816 | + schemaStack_(allocator, schemaStackCapacity), | ||
| 1817 | + documentStack_(allocator, documentStackCapacity), | ||
| 1818 | + outputHandler_(CreateNullHandler()), | ||
| 1819 | + valid_(true) | ||
| 1820 | +#if RAPIDJSON_SCHEMA_VERBOSE | ||
| 1821 | + , depth_(depth) | ||
| 1822 | +#endif | ||
| 1823 | + { | ||
| 1824 | + } | ||
| 1825 | + | ||
| 1826 | + StateAllocator& GetStateAllocator() { | ||
| 1827 | + if (!stateAllocator_) | ||
| 1828 | + stateAllocator_ = ownStateAllocator_ = RAPIDJSON_NEW(StateAllocator)(); | ||
| 1829 | + return *stateAllocator_; | ||
| 1830 | + } | ||
| 1831 | + | ||
| 1832 | + bool BeginValue() { | ||
| 1833 | + if (schemaStack_.Empty()) | ||
| 1834 | + PushSchema(root_); | ||
| 1835 | + else { | ||
| 1836 | + if (CurrentContext().inArray) | ||
| 1837 | + internal::TokenHelper<internal::Stack<StateAllocator>, Ch>::AppendIndexToken(documentStack_, CurrentContext().arrayElementIndex); | ||
| 1838 | + | ||
| 1839 | + if (!CurrentSchema().BeginValue(CurrentContext())) | ||
| 1840 | + return false; | ||
| 1841 | + | ||
| 1842 | + SizeType count = CurrentContext().patternPropertiesSchemaCount; | ||
| 1843 | + const SchemaType** sa = CurrentContext().patternPropertiesSchemas; | ||
| 1844 | + typename Context::PatternValidatorType patternValidatorType = CurrentContext().valuePatternValidatorType; | ||
| 1845 | + bool valueUniqueness = CurrentContext().valueUniqueness; | ||
| 1846 | + RAPIDJSON_ASSERT(CurrentContext().valueSchema); | ||
| 1847 | + PushSchema(*CurrentContext().valueSchema); | ||
| 1848 | + | ||
| 1849 | + if (count > 0) { | ||
| 1850 | + CurrentContext().objectPatternValidatorType = patternValidatorType; | ||
| 1851 | + ISchemaValidator**& va = CurrentContext().patternPropertiesValidators; | ||
| 1852 | + SizeType& validatorCount = CurrentContext().patternPropertiesValidatorCount; | ||
| 1853 | + va = static_cast<ISchemaValidator**>(MallocState(sizeof(ISchemaValidator*) * count)); | ||
| 1854 | + for (SizeType i = 0; i < count; i++) | ||
| 1855 | + va[validatorCount++] = CreateSchemaValidator(*sa[i]); | ||
| 1856 | + } | ||
| 1857 | + | ||
| 1858 | + CurrentContext().arrayUniqueness = valueUniqueness; | ||
| 1859 | + } | ||
| 1860 | + return true; | ||
| 1861 | + } | ||
| 1862 | + | ||
| 1863 | + bool EndValue() { | ||
| 1864 | + if (!CurrentSchema().EndValue(CurrentContext())) | ||
| 1865 | + return false; | ||
| 1866 | + | ||
| 1867 | +#if RAPIDJSON_SCHEMA_VERBOSE | ||
| 1868 | + GenericStringBuffer<EncodingType> sb; | ||
| 1869 | + schemaDocument_->GetPointer(&CurrentSchema()).Stringify(sb); | ||
| 1870 | + | ||
| 1871 | + *documentStack_.template Push<Ch>() = '\0'; | ||
| 1872 | + documentStack_.template Pop<Ch>(1); | ||
| 1873 | + internal::PrintValidatorPointers(depth_, sb.GetString(), documentStack_.template Bottom<Ch>()); | ||
| 1874 | +#endif | ||
| 1875 | + | ||
| 1876 | + uint64_t h = CurrentContext().arrayUniqueness ? static_cast<HasherType*>(CurrentContext().hasher)->GetHashCode() : 0; | ||
| 1877 | + | ||
| 1878 | + PopSchema(); | ||
| 1879 | + | ||
| 1880 | + if (!schemaStack_.Empty()) { | ||
| 1881 | + Context& context = CurrentContext(); | ||
| 1882 | + if (context.valueUniqueness) { | ||
| 1883 | + HashCodeArray* a = static_cast<HashCodeArray*>(context.arrayElementHashCodes); | ||
| 1884 | + if (!a) | ||
| 1885 | + CurrentContext().arrayElementHashCodes = a = new (GetStateAllocator().Malloc(sizeof(HashCodeArray))) HashCodeArray(kArrayType); | ||
| 1886 | + for (typename HashCodeArray::ConstValueIterator itr = a->Begin(); itr != a->End(); ++itr) | ||
| 1887 | + if (itr->GetUint64() == h) | ||
| 1888 | + RAPIDJSON_INVALID_KEYWORD_RETURN(SchemaType::GetUniqueItemsString()); | ||
| 1889 | + a->PushBack(h, GetStateAllocator()); | ||
| 1890 | + } | ||
| 1891 | + } | ||
| 1892 | + | ||
| 1893 | + // Remove the last token of document pointer | ||
| 1894 | + while (!documentStack_.Empty() && *documentStack_.template Pop<Ch>(1) != '/') | ||
| 1895 | + ; | ||
| 1896 | + | ||
| 1897 | + return true; | ||
| 1898 | + } | ||
| 1899 | + | ||
| 1900 | + void AppendToken(const Ch* str, SizeType len) { | ||
| 1901 | + documentStack_.template Reserve<Ch>(1 + len * 2); // worst case all characters are escaped as two characters | ||
| 1902 | + *documentStack_.template PushUnsafe<Ch>() = '/'; | ||
| 1903 | + for (SizeType i = 0; i < len; i++) { | ||
| 1904 | + if (str[i] == '~') { | ||
| 1905 | + *documentStack_.template PushUnsafe<Ch>() = '~'; | ||
| 1906 | + *documentStack_.template PushUnsafe<Ch>() = '0'; | ||
| 1907 | + } | ||
| 1908 | + else if (str[i] == '/') { | ||
| 1909 | + *documentStack_.template PushUnsafe<Ch>() = '~'; | ||
| 1910 | + *documentStack_.template PushUnsafe<Ch>() = '1'; | ||
| 1911 | + } | ||
| 1912 | + else | ||
| 1913 | + *documentStack_.template PushUnsafe<Ch>() = str[i]; | ||
| 1914 | + } | ||
| 1915 | + } | ||
| 1916 | + | ||
| 1917 | + RAPIDJSON_FORCEINLINE void PushSchema(const SchemaType& schema) { new (schemaStack_.template Push<Context>()) Context(*this, &schema); } | ||
| 1918 | + | ||
| 1919 | + RAPIDJSON_FORCEINLINE void PopSchema() { | ||
| 1920 | + Context* c = schemaStack_.template Pop<Context>(1); | ||
| 1921 | + if (HashCodeArray* a = static_cast<HashCodeArray*>(c->arrayElementHashCodes)) { | ||
| 1922 | + a->~HashCodeArray(); | ||
| 1923 | + StateAllocator::Free(a); | ||
| 1924 | + } | ||
| 1925 | + c->~Context(); | ||
| 1926 | + } | ||
| 1927 | + | ||
| 1928 | + const SchemaType& CurrentSchema() const { return *schemaStack_.template Top<Context>()->schema; } | ||
| 1929 | + Context& CurrentContext() { return *schemaStack_.template Top<Context>(); } | ||
| 1930 | + const Context& CurrentContext() const { return *schemaStack_.template Top<Context>(); } | ||
| 1931 | + | ||
| 1932 | + OutputHandler& CreateNullHandler() { | ||
| 1933 | + return *(nullHandler_ = new (GetStateAllocator().Malloc(sizeof(OutputHandler))) OutputHandler); | ||
| 1934 | + } | ||
| 1935 | + | ||
| 1936 | + static const size_t kDefaultSchemaStackCapacity = 1024; | ||
| 1937 | + static const size_t kDefaultDocumentStackCapacity = 256; | ||
| 1938 | + const SchemaDocumentType* schemaDocument_; | ||
| 1939 | + const SchemaType& root_; | ||
| 1940 | + StateAllocator* stateAllocator_; | ||
| 1941 | + StateAllocator* ownStateAllocator_; | ||
| 1942 | + internal::Stack<StateAllocator> schemaStack_; //!< stack to store the current path of schema (BaseSchemaType *) | ||
| 1943 | + internal::Stack<StateAllocator> documentStack_; //!< stack to store the current path of validating document (Ch) | ||
| 1944 | + OutputHandler& outputHandler_; | ||
| 1945 | + OutputHandler* nullHandler_; | ||
| 1946 | + bool valid_; | ||
| 1947 | +#if RAPIDJSON_SCHEMA_VERBOSE | ||
| 1948 | + unsigned depth_; | ||
| 1949 | +#endif | ||
| 1950 | +}; | ||
| 1951 | + | ||
| 1952 | +typedef GenericSchemaValidator<SchemaDocument> SchemaValidator; | ||
| 1953 | + | ||
| 1954 | +/////////////////////////////////////////////////////////////////////////////// | ||
| 1955 | +// SchemaValidatingReader | ||
| 1956 | + | ||
| 1957 | +//! A helper class for parsing with validation. | ||
| 1958 | +/*! | ||
| 1959 | + This helper class is a functor, designed as a parameter of \ref GenericDocument::Populate(). | ||
| 1960 | + | ||
| 1961 | + \tparam parseFlags Combination of \ref ParseFlag. | ||
| 1962 | + \tparam InputStream Type of input stream, implementing Stream concept. | ||
| 1963 | + \tparam SourceEncoding Encoding of the input stream. | ||
| 1964 | + \tparam SchemaDocumentType Type of schema document. | ||
| 1965 | + \tparam StackAllocator Allocator type for stack. | ||
| 1966 | +*/ | ||
| 1967 | +template < | ||
| 1968 | + unsigned parseFlags, | ||
| 1969 | + typename InputStream, | ||
| 1970 | + typename SourceEncoding, | ||
| 1971 | + typename SchemaDocumentType = SchemaDocument, | ||
| 1972 | + typename StackAllocator = CrtAllocator> | ||
| 1973 | +class SchemaValidatingReader { | ||
| 1974 | +public: | ||
| 1975 | + typedef typename SchemaDocumentType::PointerType PointerType; | ||
| 1976 | + typedef typename InputStream::Ch Ch; | ||
| 1977 | + | ||
| 1978 | + //! Constructor | ||
| 1979 | + /*! | ||
| 1980 | + \param is Input stream. | ||
| 1981 | + \param sd Schema document. | ||
| 1982 | + */ | ||
| 1983 | + SchemaValidatingReader(InputStream& is, const SchemaDocumentType& sd) : is_(is), sd_(sd), invalidSchemaKeyword_(), isValid_(true) {} | ||
| 1984 | + | ||
| 1985 | + template <typename Handler> | ||
| 1986 | + bool operator()(Handler& handler) { | ||
| 1987 | + GenericReader<SourceEncoding, typename SchemaDocumentType::EncodingType, StackAllocator> reader; | ||
| 1988 | + GenericSchemaValidator<SchemaDocumentType, Handler> validator(sd_, handler); | ||
| 1989 | + parseResult_ = reader.template Parse<parseFlags>(is_, validator); | ||
| 1990 | + | ||
| 1991 | + isValid_ = validator.IsValid(); | ||
| 1992 | + if (isValid_) { | ||
| 1993 | + invalidSchemaPointer_ = PointerType(); | ||
| 1994 | + invalidSchemaKeyword_ = 0; | ||
| 1995 | + invalidDocumentPointer_ = PointerType(); | ||
| 1996 | + } | ||
| 1997 | + else { | ||
| 1998 | + invalidSchemaPointer_ = validator.GetInvalidSchemaPointer(); | ||
| 1999 | + invalidSchemaKeyword_ = validator.GetInvalidSchemaKeyword(); | ||
| 2000 | + invalidDocumentPointer_ = validator.GetInvalidDocumentPointer(); | ||
| 2001 | + } | ||
| 2002 | + | ||
| 2003 | + return parseResult_; | ||
| 2004 | + } | ||
| 2005 | + | ||
| 2006 | + const ParseResult& GetParseResult() const { return parseResult_; } | ||
| 2007 | + bool IsValid() const { return isValid_; } | ||
| 2008 | + const PointerType& GetInvalidSchemaPointer() const { return invalidSchemaPointer_; } | ||
| 2009 | + const Ch* GetInvalidSchemaKeyword() const { return invalidSchemaKeyword_; } | ||
| 2010 | + const PointerType& GetInvalidDocumentPointer() const { return invalidDocumentPointer_; } | ||
| 2011 | + | ||
| 2012 | +private: | ||
| 2013 | + InputStream& is_; | ||
| 2014 | + const SchemaDocumentType& sd_; | ||
| 2015 | + | ||
| 2016 | + ParseResult parseResult_; | ||
| 2017 | + PointerType invalidSchemaPointer_; | ||
| 2018 | + const Ch* invalidSchemaKeyword_; | ||
| 2019 | + PointerType invalidDocumentPointer_; | ||
| 2020 | + bool isValid_; | ||
| 2021 | +}; | ||
| 2022 | + | ||
| 2023 | +RAPIDJSON_NAMESPACE_END | ||
| 2024 | +RAPIDJSON_DIAG_POP | ||
| 2025 | + | ||
| 2026 | +#endif // RAPIDJSON_SCHEMA_H_ |
PaperGraph/include/rapidjson/stream.h
0 → 100644
| 1 | +// Tencent is pleased to support the open source community by making RapidJSON available. | ||
| 2 | +// | ||
| 3 | +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. | ||
| 4 | +// | ||
| 5 | +// Licensed under the MIT License (the "License"); you may not use this file except | ||
| 6 | +// in compliance with the License. You may obtain a copy of the License at | ||
| 7 | +// | ||
| 8 | +// http://opensource.org/licenses/MIT | ||
| 9 | +// | ||
| 10 | +// Unless required by applicable law or agreed to in writing, software distributed | ||
| 11 | +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR | ||
| 12 | +// CONDITIONS OF ANY KIND, either express or implied. See the License for the | ||
| 13 | +// specific language governing permissions and limitations under the License. | ||
| 14 | + | ||
| 15 | +#include "rapidjson.h" | ||
| 16 | + | ||
| 17 | +#ifndef RAPIDJSON_STREAM_H_ | ||
| 18 | +#define RAPIDJSON_STREAM_H_ | ||
| 19 | + | ||
| 20 | +#include "encodings.h" | ||
| 21 | + | ||
| 22 | +RAPIDJSON_NAMESPACE_BEGIN | ||
| 23 | + | ||
| 24 | +/////////////////////////////////////////////////////////////////////////////// | ||
| 25 | +// Stream | ||
| 26 | + | ||
| 27 | +/*! \class rapidjson::Stream | ||
| 28 | + \brief Concept for reading and writing characters. | ||
| 29 | + | ||
| 30 | + For read-only stream, no need to implement PutBegin(), Put(), Flush() and PutEnd(). | ||
| 31 | + | ||
| 32 | + For write-only stream, only need to implement Put() and Flush(). | ||
| 33 | + | ||
| 34 | +\code | ||
| 35 | +concept Stream { | ||
| 36 | + typename Ch; //!< Character type of the stream. | ||
| 37 | + | ||
| 38 | + //! Read the current character from stream without moving the read cursor. | ||
| 39 | + Ch Peek() const; | ||
| 40 | + | ||
| 41 | + //! Read the current character from stream and moving the read cursor to next character. | ||
| 42 | + Ch Take(); | ||
| 43 | + | ||
| 44 | + //! Get the current read cursor. | ||
| 45 | + //! \return Number of characters read from start. | ||
| 46 | + size_t Tell(); | ||
| 47 | + | ||
| 48 | + //! Begin writing operation at the current read pointer. | ||
| 49 | + //! \return The begin writer pointer. | ||
| 50 | + Ch* PutBegin(); | ||
| 51 | + | ||
| 52 | + //! Write a character. | ||
| 53 | + void Put(Ch c); | ||
| 54 | + | ||
| 55 | + //! Flush the buffer. | ||
| 56 | + void Flush(); | ||
| 57 | + | ||
| 58 | + //! End the writing operation. | ||
| 59 | + //! \param begin The begin write pointer returned by PutBegin(). | ||
| 60 | + //! \return Number of characters written. | ||
| 61 | + size_t PutEnd(Ch* begin); | ||
| 62 | +} | ||
| 63 | +\endcode | ||
| 64 | +*/ | ||
| 65 | + | ||
| 66 | +//! Provides additional information for stream. | ||
| 67 | +/*! | ||
| 68 | + By using traits pattern, this type provides a default configuration for stream. | ||
| 69 | + For custom stream, this type can be specialized for other configuration. | ||
| 70 | + See TEST(Reader, CustomStringStream) in readertest.cpp for example. | ||
| 71 | +*/ | ||
| 72 | +template<typename Stream> | ||
| 73 | +struct StreamTraits { | ||
| 74 | + //! Whether to make local copy of stream for optimization during parsing. | ||
| 75 | + /*! | ||
| 76 | + By default, for safety, streams do not use local copy optimization. | ||
| 77 | + Stream that can be copied fast should specialize this, like StreamTraits<StringStream>. | ||
| 78 | + */ | ||
| 79 | + enum { copyOptimization = 0 }; | ||
| 80 | +}; | ||
| 81 | + | ||
| 82 | +//! Reserve n characters for writing to a stream. | ||
| 83 | +template<typename Stream> | ||
| 84 | +inline void PutReserve(Stream& stream, size_t count) { | ||
| 85 | + (void)stream; | ||
| 86 | + (void)count; | ||
| 87 | +} | ||
| 88 | + | ||
| 89 | +//! Write character to a stream, presuming buffer is reserved. | ||
| 90 | +template<typename Stream> | ||
| 91 | +inline void PutUnsafe(Stream& stream, typename Stream::Ch c) { | ||
| 92 | + stream.Put(c); | ||
| 93 | +} | ||
| 94 | + | ||
| 95 | +//! Put N copies of a character to a stream. | ||
| 96 | +template<typename Stream, typename Ch> | ||
| 97 | +inline void PutN(Stream& stream, Ch c, size_t n) { | ||
| 98 | + PutReserve(stream, n); | ||
| 99 | + for (size_t i = 0; i < n; i++) | ||
| 100 | + PutUnsafe(stream, c); | ||
| 101 | +} | ||
| 102 | + | ||
| 103 | +/////////////////////////////////////////////////////////////////////////////// | ||
| 104 | +// StringStream | ||
| 105 | + | ||
| 106 | +//! Read-only string stream. | ||
| 107 | +/*! \note implements Stream concept | ||
| 108 | +*/ | ||
| 109 | +template <typename Encoding> | ||
| 110 | +struct GenericStringStream { | ||
| 111 | + typedef typename Encoding::Ch Ch; | ||
| 112 | + | ||
| 113 | + GenericStringStream(const Ch *src) : src_(src), head_(src) {} | ||
| 114 | + | ||
| 115 | + Ch Peek() const { return *src_; } | ||
| 116 | + Ch Take() { return *src_++; } | ||
| 117 | + size_t Tell() const { return static_cast<size_t>(src_ - head_); } | ||
| 118 | + | ||
| 119 | + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } | ||
| 120 | + void Put(Ch) { RAPIDJSON_ASSERT(false); } | ||
| 121 | + void Flush() { RAPIDJSON_ASSERT(false); } | ||
| 122 | + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } | ||
| 123 | + | ||
| 124 | + const Ch* src_; //!< Current read position. | ||
| 125 | + const Ch* head_; //!< Original head of the string. | ||
| 126 | +}; | ||
| 127 | + | ||
| 128 | +template <typename Encoding> | ||
| 129 | +struct StreamTraits<GenericStringStream<Encoding> > { | ||
| 130 | + enum { copyOptimization = 1 }; | ||
| 131 | +}; | ||
| 132 | + | ||
| 133 | +//! String stream with UTF8 encoding. | ||
| 134 | +typedef GenericStringStream<UTF8<> > StringStream; | ||
| 135 | + | ||
| 136 | +/////////////////////////////////////////////////////////////////////////////// | ||
| 137 | +// InsituStringStream | ||
| 138 | + | ||
| 139 | +//! A read-write string stream. | ||
| 140 | +/*! This string stream is particularly designed for in-situ parsing. | ||
| 141 | + \note implements Stream concept | ||
| 142 | +*/ | ||
| 143 | +template <typename Encoding> | ||
| 144 | +struct GenericInsituStringStream { | ||
| 145 | + typedef typename Encoding::Ch Ch; | ||
| 146 | + | ||
| 147 | + GenericInsituStringStream(Ch *src) : src_(src), dst_(0), head_(src) {} | ||
| 148 | + | ||
| 149 | + // Read | ||
| 150 | + Ch Peek() { return *src_; } | ||
| 151 | + Ch Take() { return *src_++; } | ||
| 152 | + size_t Tell() { return static_cast<size_t>(src_ - head_); } | ||
| 153 | + | ||
| 154 | + // Write | ||
| 155 | + void Put(Ch c) { RAPIDJSON_ASSERT(dst_ != 0); *dst_++ = c; } | ||
| 156 | + | ||
| 157 | + Ch* PutBegin() { return dst_ = src_; } | ||
| 158 | + size_t PutEnd(Ch* begin) { return static_cast<size_t>(dst_ - begin); } | ||
| 159 | + void Flush() {} | ||
| 160 | + | ||
| 161 | + Ch* Push(size_t count) { Ch* begin = dst_; dst_ += count; return begin; } | ||
| 162 | + void Pop(size_t count) { dst_ -= count; } | ||
| 163 | + | ||
| 164 | + Ch* src_; | ||
| 165 | + Ch* dst_; | ||
| 166 | + Ch* head_; | ||
| 167 | +}; | ||
| 168 | + | ||
| 169 | +template <typename Encoding> | ||
| 170 | +struct StreamTraits<GenericInsituStringStream<Encoding> > { | ||
| 171 | + enum { copyOptimization = 1 }; | ||
| 172 | +}; | ||
| 173 | + | ||
| 174 | +//! Insitu string stream with UTF8 encoding. | ||
| 175 | +typedef GenericInsituStringStream<UTF8<> > InsituStringStream; | ||
| 176 | + | ||
| 177 | +RAPIDJSON_NAMESPACE_END | ||
| 178 | + | ||
| 179 | +#endif // RAPIDJSON_STREAM_H_ |
PaperGraph/include/rapidjson/stringbuffer.h
0 → 100644
| 1 | +// Tencent is pleased to support the open source community by making RapidJSON available. | ||
| 2 | +// | ||
| 3 | +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. | ||
| 4 | +// | ||
| 5 | +// Licensed under the MIT License (the "License"); you may not use this file except | ||
| 6 | +// in compliance with the License. You may obtain a copy of the License at | ||
| 7 | +// | ||
| 8 | +// http://opensource.org/licenses/MIT | ||
| 9 | +// | ||
| 10 | +// Unless required by applicable law or agreed to in writing, software distributed | ||
| 11 | +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR | ||
| 12 | +// CONDITIONS OF ANY KIND, either express or implied. See the License for the | ||
| 13 | +// specific language governing permissions and limitations under the License. | ||
| 14 | + | ||
| 15 | +#ifndef RAPIDJSON_STRINGBUFFER_H_ | ||
| 16 | +#define RAPIDJSON_STRINGBUFFER_H_ | ||
| 17 | + | ||
| 18 | +#include "stream.h" | ||
| 19 | +#include "internal/stack.h" | ||
| 20 | + | ||
| 21 | +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS | ||
| 22 | +#include <utility> // std::move | ||
| 23 | +#endif | ||
| 24 | + | ||
| 25 | +#include "internal/stack.h" | ||
| 26 | + | ||
| 27 | +#if defined(__clang__) | ||
| 28 | +RAPIDJSON_DIAG_PUSH | ||
| 29 | +RAPIDJSON_DIAG_OFF(c++98-compat) | ||
| 30 | +#endif | ||
| 31 | + | ||
| 32 | +RAPIDJSON_NAMESPACE_BEGIN | ||
| 33 | + | ||
| 34 | +//! Represents an in-memory output stream. | ||
| 35 | +/*! | ||
| 36 | + \tparam Encoding Encoding of the stream. | ||
| 37 | + \tparam Allocator type for allocating memory buffer. | ||
| 38 | + \note implements Stream concept | ||
| 39 | +*/ | ||
| 40 | +template <typename Encoding, typename Allocator = CrtAllocator> | ||
| 41 | +class GenericStringBuffer { | ||
| 42 | +public: | ||
| 43 | + typedef typename Encoding::Ch Ch; | ||
| 44 | + | ||
| 45 | + GenericStringBuffer(Allocator* allocator = 0, size_t capacity = kDefaultCapacity) : stack_(allocator, capacity) {} | ||
| 46 | + | ||
| 47 | +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS | ||
| 48 | + GenericStringBuffer(GenericStringBuffer&& rhs) : stack_(std::move(rhs.stack_)) {} | ||
| 49 | + GenericStringBuffer& operator=(GenericStringBuffer&& rhs) { | ||
| 50 | + if (&rhs != this) | ||
| 51 | + stack_ = std::move(rhs.stack_); | ||
| 52 | + return *this; | ||
| 53 | + } | ||
| 54 | +#endif | ||
| 55 | + | ||
| 56 | + void Put(Ch c) { *stack_.template Push<Ch>() = c; } | ||
| 57 | + void PutUnsafe(Ch c) { *stack_.template PushUnsafe<Ch>() = c; } | ||
| 58 | + void Flush() {} | ||
| 59 | + | ||
| 60 | + void Clear() { stack_.Clear(); } | ||
| 61 | + void ShrinkToFit() { | ||
| 62 | + // Push and pop a null terminator. This is safe. | ||
| 63 | + *stack_.template Push<Ch>() = '\0'; | ||
| 64 | + stack_.ShrinkToFit(); | ||
| 65 | + stack_.template Pop<Ch>(1); | ||
| 66 | + } | ||
| 67 | + | ||
| 68 | + void Reserve(size_t count) { stack_.template Reserve<Ch>(count); } | ||
| 69 | + Ch* Push(size_t count) { return stack_.template Push<Ch>(count); } | ||
| 70 | + Ch* PushUnsafe(size_t count) { return stack_.template PushUnsafe<Ch>(count); } | ||
| 71 | + void Pop(size_t count) { stack_.template Pop<Ch>(count); } | ||
| 72 | + | ||
| 73 | + const Ch* GetString() const { | ||
| 74 | + // Push and pop a null terminator. This is safe. | ||
| 75 | + *stack_.template Push<Ch>() = '\0'; | ||
| 76 | + stack_.template Pop<Ch>(1); | ||
| 77 | + | ||
| 78 | + return stack_.template Bottom<Ch>(); | ||
| 79 | + } | ||
| 80 | + | ||
| 81 | + //! Get the size of string in bytes in the string buffer. | ||
| 82 | + size_t GetSize() const { return stack_.GetSize(); } | ||
| 83 | + | ||
| 84 | + //! Get the length of string in Ch in the string buffer. | ||
| 85 | + size_t GetLength() const { return stack_.GetSize() / sizeof(Ch); } | ||
| 86 | + | ||
| 87 | + static const size_t kDefaultCapacity = 256; | ||
| 88 | + mutable internal::Stack<Allocator> stack_; | ||
| 89 | + | ||
| 90 | +private: | ||
| 91 | + // Prohibit copy constructor & assignment operator. | ||
| 92 | + GenericStringBuffer(const GenericStringBuffer&); | ||
| 93 | + GenericStringBuffer& operator=(const GenericStringBuffer&); | ||
| 94 | +}; | ||
| 95 | + | ||
| 96 | +//! String buffer with UTF8 encoding | ||
| 97 | +typedef GenericStringBuffer<UTF8<> > StringBuffer; | ||
| 98 | + | ||
| 99 | +template<typename Encoding, typename Allocator> | ||
| 100 | +inline void PutReserve(GenericStringBuffer<Encoding, Allocator>& stream, size_t count) { | ||
| 101 | + stream.Reserve(count); | ||
| 102 | +} | ||
| 103 | + | ||
| 104 | +template<typename Encoding, typename Allocator> | ||
| 105 | +inline void PutUnsafe(GenericStringBuffer<Encoding, Allocator>& stream, typename Encoding::Ch c) { | ||
| 106 | + stream.PutUnsafe(c); | ||
| 107 | +} | ||
| 108 | + | ||
| 109 | +//! Implement specialized version of PutN() with memset() for better performance. | ||
| 110 | +template<> | ||
| 111 | +inline void PutN(GenericStringBuffer<UTF8<> >& stream, char c, size_t n) { | ||
| 112 | + std::memset(stream.stack_.Push<char>(n), c, n * sizeof(c)); | ||
| 113 | +} | ||
| 114 | + | ||
| 115 | +RAPIDJSON_NAMESPACE_END | ||
| 116 | + | ||
| 117 | +#if defined(__clang__) | ||
| 118 | +RAPIDJSON_DIAG_POP | ||
| 119 | +#endif | ||
| 120 | + | ||
| 121 | +#endif // RAPIDJSON_STRINGBUFFER_H_ |
PaperGraph/include/rapidjson/writer.h
0 → 100644
| 1 | +// Tencent is pleased to support the open source community by making RapidJSON available. | ||
| 2 | +// | ||
| 3 | +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. | ||
| 4 | +// | ||
| 5 | +// Licensed under the MIT License (the "License"); you may not use this file except | ||
| 6 | +// in compliance with the License. You may obtain a copy of the License at | ||
| 7 | +// | ||
| 8 | +// http://opensource.org/licenses/MIT | ||
| 9 | +// | ||
| 10 | +// Unless required by applicable law or agreed to in writing, software distributed | ||
| 11 | +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR | ||
| 12 | +// CONDITIONS OF ANY KIND, either express or implied. See the License for the | ||
| 13 | +// specific language governing permissions and limitations under the License. | ||
| 14 | + | ||
| 15 | +#ifndef RAPIDJSON_WRITER_H_ | ||
| 16 | +#define RAPIDJSON_WRITER_H_ | ||
| 17 | + | ||
| 18 | +#include "stream.h" | ||
| 19 | +#include "internal/meta.h" | ||
| 20 | +#include "internal/stack.h" | ||
| 21 | +#include "internal/strfunc.h" | ||
| 22 | +#include "internal/dtoa.h" | ||
| 23 | +#include "internal/itoa.h" | ||
| 24 | +#include "stringbuffer.h" | ||
| 25 | +#include <new> // placement new | ||
| 26 | + | ||
| 27 | +#if defined(RAPIDJSON_SIMD) && defined(_MSC_VER) | ||
| 28 | +#include <intrin.h> | ||
| 29 | +#pragma intrinsic(_BitScanForward) | ||
| 30 | +#endif | ||
| 31 | +#ifdef RAPIDJSON_SSE42 | ||
| 32 | +#include <nmmintrin.h> | ||
| 33 | +#elif defined(RAPIDJSON_SSE2) | ||
| 34 | +#include <emmintrin.h> | ||
| 35 | +#elif defined(RAPIDJSON_NEON) | ||
| 36 | +#include <arm_neon.h> | ||
| 37 | +#endif | ||
| 38 | + | ||
| 39 | +#ifdef _MSC_VER | ||
| 40 | +RAPIDJSON_DIAG_PUSH | ||
| 41 | +RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant | ||
| 42 | +#endif | ||
| 43 | + | ||
| 44 | +#ifdef __clang__ | ||
| 45 | +RAPIDJSON_DIAG_PUSH | ||
| 46 | +RAPIDJSON_DIAG_OFF(padded) | ||
| 47 | +RAPIDJSON_DIAG_OFF(unreachable-code) | ||
| 48 | +RAPIDJSON_DIAG_OFF(c++98-compat) | ||
| 49 | +#endif | ||
| 50 | + | ||
| 51 | +RAPIDJSON_NAMESPACE_BEGIN | ||
| 52 | + | ||
| 53 | +/////////////////////////////////////////////////////////////////////////////// | ||
| 54 | +// WriteFlag | ||
| 55 | + | ||
| 56 | +/*! \def RAPIDJSON_WRITE_DEFAULT_FLAGS | ||
| 57 | + \ingroup RAPIDJSON_CONFIG | ||
| 58 | + \brief User-defined kWriteDefaultFlags definition. | ||
| 59 | + | ||
| 60 | + User can define this as any \c WriteFlag combinations. | ||
| 61 | +*/ | ||
| 62 | +#ifndef RAPIDJSON_WRITE_DEFAULT_FLAGS | ||
| 63 | +#define RAPIDJSON_WRITE_DEFAULT_FLAGS kWriteNoFlags | ||
| 64 | +#endif | ||
| 65 | + | ||
| 66 | +//! Combination of writeFlags | ||
| 67 | +enum WriteFlag { | ||
| 68 | + kWriteNoFlags = 0, //!< No flags are set. | ||
| 69 | + kWriteValidateEncodingFlag = 1, //!< Validate encoding of JSON strings. | ||
| 70 | + kWriteNanAndInfFlag = 2, //!< Allow writing of Infinity, -Infinity and NaN. | ||
| 71 | + kWriteDefaultFlags = RAPIDJSON_WRITE_DEFAULT_FLAGS //!< Default write flags. Can be customized by defining RAPIDJSON_WRITE_DEFAULT_FLAGS | ||
| 72 | +}; | ||
| 73 | + | ||
| 74 | +//! JSON writer | ||
| 75 | +/*! Writer implements the concept Handler. | ||
| 76 | + It generates JSON text by events to an output os. | ||
| 77 | + | ||
| 78 | + User may programmatically calls the functions of a writer to generate JSON text. | ||
| 79 | + | ||
| 80 | + On the other side, a writer can also be passed to objects that generates events, | ||
| 81 | + | ||
| 82 | + for example Reader::Parse() and Document::Accept(). | ||
| 83 | + | ||
| 84 | + \tparam OutputStream Type of output stream. | ||
| 85 | + \tparam SourceEncoding Encoding of source string. | ||
| 86 | + \tparam TargetEncoding Encoding of output stream. | ||
| 87 | + \tparam StackAllocator Type of allocator for allocating memory of stack. | ||
| 88 | + \note implements Handler concept | ||
| 89 | +*/ | ||
| 90 | +template<typename OutputStream, typename SourceEncoding = UTF8<>, typename TargetEncoding = UTF8<>, typename StackAllocator = CrtAllocator, unsigned writeFlags = kWriteDefaultFlags> | ||
| 91 | +class Writer { | ||
| 92 | +public: | ||
| 93 | + typedef typename SourceEncoding::Ch Ch; | ||
| 94 | + | ||
| 95 | + static const int kDefaultMaxDecimalPlaces = 324; | ||
| 96 | + | ||
| 97 | + //! Constructor | ||
| 98 | + /*! \param os Output stream. | ||
| 99 | + \param stackAllocator User supplied allocator. If it is null, it will create a private one. | ||
| 100 | + \param levelDepth Initial capacity of stack. | ||
| 101 | + */ | ||
| 102 | + explicit | ||
| 103 | + Writer(OutputStream& os, StackAllocator* stackAllocator = 0, size_t levelDepth = kDefaultLevelDepth) : | ||
| 104 | + os_(&os), level_stack_(stackAllocator, levelDepth * sizeof(Level)), maxDecimalPlaces_(kDefaultMaxDecimalPlaces), hasRoot_(false) {} | ||
| 105 | + | ||
| 106 | + explicit | ||
| 107 | + Writer(StackAllocator* allocator = 0, size_t levelDepth = kDefaultLevelDepth) : | ||
| 108 | + os_(0), level_stack_(allocator, levelDepth * sizeof(Level)), maxDecimalPlaces_(kDefaultMaxDecimalPlaces), hasRoot_(false) {} | ||
| 109 | + | ||
| 110 | +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS | ||
| 111 | + Writer(Writer&& rhs) : | ||
| 112 | + os_(rhs.os_), level_stack_(std::move(rhs.level_stack_)), maxDecimalPlaces_(rhs.maxDecimalPlaces_), hasRoot_(rhs.hasRoot_) { | ||
| 113 | + rhs.os_ = 0; | ||
| 114 | + } | ||
| 115 | +#endif | ||
| 116 | + | ||
| 117 | + //! Reset the writer with a new stream. | ||
| 118 | + /*! | ||
| 119 | + This function reset the writer with a new stream and default settings, | ||
| 120 | + in order to make a Writer object reusable for output multiple JSONs. | ||
| 121 | + | ||
| 122 | + \param os New output stream. | ||
| 123 | + \code | ||
| 124 | + Writer<OutputStream> writer(os1); | ||
| 125 | + writer.StartObject(); | ||
| 126 | + // ... | ||
| 127 | + writer.EndObject(); | ||
| 128 | + | ||
| 129 | + writer.Reset(os2); | ||
| 130 | + writer.StartObject(); | ||
| 131 | + // ... | ||
| 132 | + writer.EndObject(); | ||
| 133 | + \endcode | ||
| 134 | + */ | ||
| 135 | + void Reset(OutputStream& os) { | ||
| 136 | + os_ = &os; | ||
| 137 | + hasRoot_ = false; | ||
| 138 | + level_stack_.Clear(); | ||
| 139 | + } | ||
| 140 | + | ||
| 141 | + //! Checks whether the output is a complete JSON. | ||
| 142 | + /*! | ||
| 143 | + A complete JSON has a complete root object or array. | ||
| 144 | + */ | ||
| 145 | + bool IsComplete() const { | ||
| 146 | + return hasRoot_ && level_stack_.Empty(); | ||
| 147 | + } | ||
| 148 | + | ||
| 149 | + int GetMaxDecimalPlaces() const { | ||
| 150 | + return maxDecimalPlaces_; | ||
| 151 | + } | ||
| 152 | + | ||
| 153 | + //! Sets the maximum number of decimal places for double output. | ||
| 154 | + /*! | ||
| 155 | + This setting truncates the output with specified number of decimal places. | ||
| 156 | + | ||
| 157 | + For example, | ||
| 158 | + | ||
| 159 | + \code | ||
| 160 | + writer.SetMaxDecimalPlaces(3); | ||
| 161 | + writer.StartArray(); | ||
| 162 | + writer.Double(0.12345); // "0.123" | ||
| 163 | + writer.Double(0.0001); // "0.0" | ||
| 164 | + writer.Double(1.234567890123456e30); // "1.234567890123456e30" (do not truncate significand for positive exponent) | ||
| 165 | + writer.Double(1.23e-4); // "0.0" (do truncate significand for negative exponent) | ||
| 166 | + writer.EndArray(); | ||
| 167 | + \endcode | ||
| 168 | + | ||
| 169 | + The default setting does not truncate any decimal places. You can restore to this setting by calling | ||
| 170 | + \code | ||
| 171 | + writer.SetMaxDecimalPlaces(Writer::kDefaultMaxDecimalPlaces); | ||
| 172 | + \endcode | ||
| 173 | + */ | ||
| 174 | + void SetMaxDecimalPlaces(int maxDecimalPlaces) { | ||
| 175 | + maxDecimalPlaces_ = maxDecimalPlaces; | ||
| 176 | + } | ||
| 177 | + | ||
| 178 | + /*!@name Implementation of Handler | ||
| 179 | + \see Handler | ||
| 180 | + */ | ||
| 181 | + //@{ | ||
| 182 | + | ||
| 183 | + bool Null() { Prefix(kNullType); return EndValue(WriteNull()); } | ||
| 184 | + bool Bool(bool b) { Prefix(b ? kTrueType : kFalseType); return EndValue(WriteBool(b)); } | ||
| 185 | + bool Int(int i) { Prefix(kNumberType); return EndValue(WriteInt(i)); } | ||
| 186 | + bool Uint(unsigned u) { Prefix(kNumberType); return EndValue(WriteUint(u)); } | ||
| 187 | + bool Int64(int64_t i64) { Prefix(kNumberType); return EndValue(WriteInt64(i64)); } | ||
| 188 | + bool Uint64(uint64_t u64) { Prefix(kNumberType); return EndValue(WriteUint64(u64)); } | ||
| 189 | + | ||
| 190 | + //! Writes the given \c double value to the stream | ||
| 191 | + /*! | ||
| 192 | + \param d The value to be written. | ||
| 193 | + \return Whether it is succeed. | ||
| 194 | + */ | ||
| 195 | + bool Double(double d) { Prefix(kNumberType); return EndValue(WriteDouble(d)); } | ||
| 196 | + | ||
| 197 | + bool RawNumber(const Ch* str, SizeType length, bool copy = false) { | ||
| 198 | + RAPIDJSON_ASSERT(str != 0); | ||
| 199 | + (void)copy; | ||
| 200 | + Prefix(kNumberType); | ||
| 201 | + return EndValue(WriteString(str, length)); | ||
| 202 | + } | ||
| 203 | + | ||
| 204 | + bool String(const Ch* str, SizeType length, bool copy = false) { | ||
| 205 | + RAPIDJSON_ASSERT(str != 0); | ||
| 206 | + (void)copy; | ||
| 207 | + Prefix(kStringType); | ||
| 208 | + return EndValue(WriteString(str, length)); | ||
| 209 | + } | ||
| 210 | + | ||
| 211 | +#if RAPIDJSON_HAS_STDSTRING | ||
| 212 | + bool String(const std::basic_string<Ch>& str) { | ||
| 213 | + return String(str.data(), SizeType(str.size())); | ||
| 214 | + } | ||
| 215 | +#endif | ||
| 216 | + | ||
| 217 | + bool StartObject() { | ||
| 218 | + Prefix(kObjectType); | ||
| 219 | + new (level_stack_.template Push<Level>()) Level(false); | ||
| 220 | + return WriteStartObject(); | ||
| 221 | + } | ||
| 222 | + | ||
| 223 | + bool Key(const Ch* str, SizeType length, bool copy = false) { return String(str, length, copy); } | ||
| 224 | + | ||
| 225 | +#if RAPIDJSON_HAS_STDSTRING | ||
| 226 | + bool Key(const std::basic_string<Ch>& str) | ||
| 227 | + { | ||
| 228 | + return Key(str.data(), SizeType(str.size())); | ||
| 229 | + } | ||
| 230 | +#endif | ||
| 231 | + | ||
| 232 | + bool EndObject(SizeType memberCount = 0) { | ||
| 233 | + (void)memberCount; | ||
| 234 | + RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level)); // not inside an Object | ||
| 235 | + RAPIDJSON_ASSERT(!level_stack_.template Top<Level>()->inArray); // currently inside an Array, not Object | ||
| 236 | + RAPIDJSON_ASSERT(0 == level_stack_.template Top<Level>()->valueCount % 2); // Object has a Key without a Value | ||
| 237 | + level_stack_.template Pop<Level>(1); | ||
| 238 | + return EndValue(WriteEndObject()); | ||
| 239 | + } | ||
| 240 | + | ||
| 241 | + bool StartArray() { | ||
| 242 | + Prefix(kArrayType); | ||
| 243 | + new (level_stack_.template Push<Level>()) Level(true); | ||
| 244 | + return WriteStartArray(); | ||
| 245 | + } | ||
| 246 | + | ||
| 247 | + bool EndArray(SizeType elementCount = 0) { | ||
| 248 | + (void)elementCount; | ||
| 249 | + RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level)); | ||
| 250 | + RAPIDJSON_ASSERT(level_stack_.template Top<Level>()->inArray); | ||
| 251 | + level_stack_.template Pop<Level>(1); | ||
| 252 | + return EndValue(WriteEndArray()); | ||
| 253 | + } | ||
| 254 | + //@} | ||
| 255 | + | ||
| 256 | + /*! @name Convenience extensions */ | ||
| 257 | + //@{ | ||
| 258 | + | ||
| 259 | + //! Simpler but slower overload. | ||
| 260 | + bool String(const Ch* const& str) { return String(str, internal::StrLen(str)); } | ||
| 261 | + bool Key(const Ch* const& str) { return Key(str, internal::StrLen(str)); } | ||
| 262 | + | ||
| 263 | + //@} | ||
| 264 | + | ||
| 265 | + //! Write a raw JSON value. | ||
| 266 | + /*! | ||
| 267 | + For user to write a stringified JSON as a value. | ||
| 268 | + | ||
| 269 | + \param json A well-formed JSON value. It should not contain null character within [0, length - 1] range. | ||
| 270 | + \param length Length of the json. | ||
| 271 | + \param type Type of the root of json. | ||
| 272 | + */ | ||
| 273 | + bool RawValue(const Ch* json, size_t length, Type type) { | ||
| 274 | + RAPIDJSON_ASSERT(json != 0); | ||
| 275 | + Prefix(type); | ||
| 276 | + return EndValue(WriteRawValue(json, length)); | ||
| 277 | + } | ||
| 278 | + | ||
| 279 | + //! Flush the output stream. | ||
| 280 | + /*! | ||
| 281 | + Allows the user to flush the output stream immediately. | ||
| 282 | + */ | ||
| 283 | + void Flush() { | ||
| 284 | + os_->Flush(); | ||
| 285 | + } | ||
| 286 | + | ||
| 287 | +protected: | ||
| 288 | + //! Information for each nested level | ||
| 289 | + struct Level { | ||
| 290 | + Level(bool inArray_) : valueCount(0), inArray(inArray_) {} | ||
| 291 | + size_t valueCount; //!< number of values in this level | ||
| 292 | + bool inArray; //!< true if in array, otherwise in object | ||
| 293 | + }; | ||
| 294 | + | ||
| 295 | + static const size_t kDefaultLevelDepth = 32; | ||
| 296 | + | ||
| 297 | + bool WriteNull() { | ||
| 298 | + PutReserve(*os_, 4); | ||
| 299 | + PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'u'); PutUnsafe(*os_, 'l'); PutUnsafe(*os_, 'l'); return true; | ||
| 300 | + } | ||
| 301 | + | ||
| 302 | + bool WriteBool(bool b) { | ||
| 303 | + if (b) { | ||
| 304 | + PutReserve(*os_, 4); | ||
| 305 | + PutUnsafe(*os_, 't'); PutUnsafe(*os_, 'r'); PutUnsafe(*os_, 'u'); PutUnsafe(*os_, 'e'); | ||
| 306 | + } | ||
| 307 | + else { | ||
| 308 | + PutReserve(*os_, 5); | ||
| 309 | + PutUnsafe(*os_, 'f'); PutUnsafe(*os_, 'a'); PutUnsafe(*os_, 'l'); PutUnsafe(*os_, 's'); PutUnsafe(*os_, 'e'); | ||
| 310 | + } | ||
| 311 | + return true; | ||
| 312 | + } | ||
| 313 | + | ||
| 314 | + bool WriteInt(int i) { | ||
| 315 | + char buffer[11]; | ||
| 316 | + const char* end = internal::i32toa(i, buffer); | ||
| 317 | + PutReserve(*os_, static_cast<size_t>(end - buffer)); | ||
| 318 | + for (const char* p = buffer; p != end; ++p) | ||
| 319 | + PutUnsafe(*os_, static_cast<typename OutputStream::Ch>(*p)); | ||
| 320 | + return true; | ||
| 321 | + } | ||
| 322 | + | ||
| 323 | + bool WriteUint(unsigned u) { | ||
| 324 | + char buffer[10]; | ||
| 325 | + const char* end = internal::u32toa(u, buffer); | ||
| 326 | + PutReserve(*os_, static_cast<size_t>(end - buffer)); | ||
| 327 | + for (const char* p = buffer; p != end; ++p) | ||
| 328 | + PutUnsafe(*os_, static_cast<typename OutputStream::Ch>(*p)); | ||
| 329 | + return true; | ||
| 330 | + } | ||
| 331 | + | ||
| 332 | + bool WriteInt64(int64_t i64) { | ||
| 333 | + char buffer[21]; | ||
| 334 | + const char* end = internal::i64toa(i64, buffer); | ||
| 335 | + PutReserve(*os_, static_cast<size_t>(end - buffer)); | ||
| 336 | + for (const char* p = buffer; p != end; ++p) | ||
| 337 | + PutUnsafe(*os_, static_cast<typename OutputStream::Ch>(*p)); | ||
| 338 | + return true; | ||
| 339 | + } | ||
| 340 | + | ||
| 341 | + bool WriteUint64(uint64_t u64) { | ||
| 342 | + char buffer[20]; | ||
| 343 | + char* end = internal::u64toa(u64, buffer); | ||
| 344 | + PutReserve(*os_, static_cast<size_t>(end - buffer)); | ||
| 345 | + for (char* p = buffer; p != end; ++p) | ||
| 346 | + PutUnsafe(*os_, static_cast<typename OutputStream::Ch>(*p)); | ||
| 347 | + return true; | ||
| 348 | + } | ||
| 349 | + | ||
| 350 | + bool WriteDouble(double d) { | ||
| 351 | + if (internal::Double(d).IsNanOrInf()) { | ||
| 352 | + if (!(writeFlags & kWriteNanAndInfFlag)) | ||
| 353 | + return false; | ||
| 354 | + if (internal::Double(d).IsNan()) { | ||
| 355 | + PutReserve(*os_, 3); | ||
| 356 | + PutUnsafe(*os_, 'N'); PutUnsafe(*os_, 'a'); PutUnsafe(*os_, 'N'); | ||
| 357 | + return true; | ||
| 358 | + } | ||
| 359 | + if (internal::Double(d).Sign()) { | ||
| 360 | + PutReserve(*os_, 9); | ||
| 361 | + PutUnsafe(*os_, '-'); | ||
| 362 | + } | ||
| 363 | + else | ||
| 364 | + PutReserve(*os_, 8); | ||
| 365 | + PutUnsafe(*os_, 'I'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'f'); | ||
| 366 | + PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 't'); PutUnsafe(*os_, 'y'); | ||
| 367 | + return true; | ||
| 368 | + } | ||
| 369 | + | ||
| 370 | + char buffer[25]; | ||
| 371 | + char* end = internal::dtoa(d, buffer, maxDecimalPlaces_); | ||
| 372 | + PutReserve(*os_, static_cast<size_t>(end - buffer)); | ||
| 373 | + for (char* p = buffer; p != end; ++p) | ||
| 374 | + PutUnsafe(*os_, static_cast<typename OutputStream::Ch>(*p)); | ||
| 375 | + return true; | ||
| 376 | + } | ||
| 377 | + | ||
| 378 | + bool WriteString(const Ch* str, SizeType length) { | ||
| 379 | + static const typename OutputStream::Ch hexDigits[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; | ||
| 380 | + static const char escape[256] = { | ||
| 381 | +#define Z16 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 | ||
| 382 | + //0 1 2 3 4 5 6 7 8 9 A B C D E F | ||
| 383 | + 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'b', 't', 'n', 'u', 'f', 'r', 'u', 'u', // 00 | ||
| 384 | + 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', // 10 | ||
| 385 | + 0, 0, '"', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20 | ||
| 386 | + Z16, Z16, // 30~4F | ||
| 387 | + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'\\', 0, 0, 0, // 50 | ||
| 388 | + Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16 // 60~FF | ||
| 389 | +#undef Z16 | ||
| 390 | + }; | ||
| 391 | + | ||
| 392 | + if (TargetEncoding::supportUnicode) | ||
| 393 | + PutReserve(*os_, 2 + length * 6); // "\uxxxx..." | ||
| 394 | + else | ||
| 395 | + PutReserve(*os_, 2 + length * 12); // "\uxxxx\uyyyy..." | ||
| 396 | + | ||
| 397 | + PutUnsafe(*os_, '\"'); | ||
| 398 | + GenericStringStream<SourceEncoding> is(str); | ||
| 399 | + while (ScanWriteUnescapedString(is, length)) { | ||
| 400 | + const Ch c = is.Peek(); | ||
| 401 | + if (!TargetEncoding::supportUnicode && static_cast<unsigned>(c) >= 0x80) { | ||
| 402 | + // Unicode escaping | ||
| 403 | + unsigned codepoint; | ||
| 404 | + if (RAPIDJSON_UNLIKELY(!SourceEncoding::Decode(is, &codepoint))) | ||
| 405 | + return false; | ||
| 406 | + PutUnsafe(*os_, '\\'); | ||
| 407 | + PutUnsafe(*os_, 'u'); | ||
| 408 | + if (codepoint <= 0xD7FF || (codepoint >= 0xE000 && codepoint <= 0xFFFF)) { | ||
| 409 | + PutUnsafe(*os_, hexDigits[(codepoint >> 12) & 15]); | ||
| 410 | + PutUnsafe(*os_, hexDigits[(codepoint >> 8) & 15]); | ||
| 411 | + PutUnsafe(*os_, hexDigits[(codepoint >> 4) & 15]); | ||
| 412 | + PutUnsafe(*os_, hexDigits[(codepoint ) & 15]); | ||
| 413 | + } | ||
| 414 | + else { | ||
| 415 | + RAPIDJSON_ASSERT(codepoint >= 0x010000 && codepoint <= 0x10FFFF); | ||
| 416 | + // Surrogate pair | ||
| 417 | + unsigned s = codepoint - 0x010000; | ||
| 418 | + unsigned lead = (s >> 10) + 0xD800; | ||
| 419 | + unsigned trail = (s & 0x3FF) + 0xDC00; | ||
| 420 | + PutUnsafe(*os_, hexDigits[(lead >> 12) & 15]); | ||
| 421 | + PutUnsafe(*os_, hexDigits[(lead >> 8) & 15]); | ||
| 422 | + PutUnsafe(*os_, hexDigits[(lead >> 4) & 15]); | ||
| 423 | + PutUnsafe(*os_, hexDigits[(lead ) & 15]); | ||
| 424 | + PutUnsafe(*os_, '\\'); | ||
| 425 | + PutUnsafe(*os_, 'u'); | ||
| 426 | + PutUnsafe(*os_, hexDigits[(trail >> 12) & 15]); | ||
| 427 | + PutUnsafe(*os_, hexDigits[(trail >> 8) & 15]); | ||
| 428 | + PutUnsafe(*os_, hexDigits[(trail >> 4) & 15]); | ||
| 429 | + PutUnsafe(*os_, hexDigits[(trail ) & 15]); | ||
| 430 | + } | ||
| 431 | + } | ||
| 432 | + else if ((sizeof(Ch) == 1 || static_cast<unsigned>(c) < 256) && RAPIDJSON_UNLIKELY(escape[static_cast<unsigned char>(c)])) { | ||
| 433 | + is.Take(); | ||
| 434 | + PutUnsafe(*os_, '\\'); | ||
| 435 | + PutUnsafe(*os_, static_cast<typename OutputStream::Ch>(escape[static_cast<unsigned char>(c)])); | ||
| 436 | + if (escape[static_cast<unsigned char>(c)] == 'u') { | ||
| 437 | + PutUnsafe(*os_, '0'); | ||
| 438 | + PutUnsafe(*os_, '0'); | ||
| 439 | + PutUnsafe(*os_, hexDigits[static_cast<unsigned char>(c) >> 4]); | ||
| 440 | + PutUnsafe(*os_, hexDigits[static_cast<unsigned char>(c) & 0xF]); | ||
| 441 | + } | ||
| 442 | + } | ||
| 443 | + else if (RAPIDJSON_UNLIKELY(!(writeFlags & kWriteValidateEncodingFlag ? | ||
| 444 | + Transcoder<SourceEncoding, TargetEncoding>::Validate(is, *os_) : | ||
| 445 | + Transcoder<SourceEncoding, TargetEncoding>::TranscodeUnsafe(is, *os_)))) | ||
| 446 | + return false; | ||
| 447 | + } | ||
| 448 | + PutUnsafe(*os_, '\"'); | ||
| 449 | + return true; | ||
| 450 | + } | ||
| 451 | + | ||
| 452 | + bool ScanWriteUnescapedString(GenericStringStream<SourceEncoding>& is, size_t length) { | ||
| 453 | + return RAPIDJSON_LIKELY(is.Tell() < length); | ||
| 454 | + } | ||
| 455 | + | ||
| 456 | + bool WriteStartObject() { os_->Put('{'); return true; } | ||
| 457 | + bool WriteEndObject() { os_->Put('}'); return true; } | ||
| 458 | + bool WriteStartArray() { os_->Put('['); return true; } | ||
| 459 | + bool WriteEndArray() { os_->Put(']'); return true; } | ||
| 460 | + | ||
| 461 | + bool WriteRawValue(const Ch* json, size_t length) { | ||
| 462 | + PutReserve(*os_, length); | ||
| 463 | + for (size_t i = 0; i < length; i++) { | ||
| 464 | + RAPIDJSON_ASSERT(json[i] != '\0'); | ||
| 465 | + PutUnsafe(*os_, json[i]); | ||
| 466 | + } | ||
| 467 | + return true; | ||
| 468 | + } | ||
| 469 | + | ||
| 470 | + void Prefix(Type type) { | ||
| 471 | + (void)type; | ||
| 472 | + if (RAPIDJSON_LIKELY(level_stack_.GetSize() != 0)) { // this value is not at root | ||
| 473 | + Level* level = level_stack_.template Top<Level>(); | ||
| 474 | + if (level->valueCount > 0) { | ||
| 475 | + if (level->inArray) | ||
| 476 | + os_->Put(','); // add comma if it is not the first element in array | ||
| 477 | + else // in object | ||
| 478 | + os_->Put((level->valueCount % 2 == 0) ? ',' : ':'); | ||
| 479 | + } | ||
| 480 | + if (!level->inArray && level->valueCount % 2 == 0) | ||
| 481 | + RAPIDJSON_ASSERT(type == kStringType); // if it's in object, then even number should be a name | ||
| 482 | + level->valueCount++; | ||
| 483 | + } | ||
| 484 | + else { | ||
| 485 | + RAPIDJSON_ASSERT(!hasRoot_); // Should only has one and only one root. | ||
| 486 | + hasRoot_ = true; | ||
| 487 | + } | ||
| 488 | + } | ||
| 489 | + | ||
| 490 | + // Flush the value if it is the top level one. | ||
| 491 | + bool EndValue(bool ret) { | ||
| 492 | + if (RAPIDJSON_UNLIKELY(level_stack_.Empty())) // end of json text | ||
| 493 | + Flush(); | ||
| 494 | + return ret; | ||
| 495 | + } | ||
| 496 | + | ||
| 497 | + OutputStream* os_; | ||
| 498 | + internal::Stack<StackAllocator> level_stack_; | ||
| 499 | + int maxDecimalPlaces_; | ||
| 500 | + bool hasRoot_; | ||
| 501 | + | ||
| 502 | +private: | ||
| 503 | + // Prohibit copy constructor & assignment operator. | ||
| 504 | + Writer(const Writer&); | ||
| 505 | + Writer& operator=(const Writer&); | ||
| 506 | +}; | ||
| 507 | + | ||
| 508 | +// Full specialization for StringStream to prevent memory copying | ||
| 509 | + | ||
| 510 | +template<> | ||
| 511 | +inline bool Writer<StringBuffer>::WriteInt(int i) { | ||
| 512 | + char *buffer = os_->Push(11); | ||
| 513 | + const char* end = internal::i32toa(i, buffer); | ||
| 514 | + os_->Pop(static_cast<size_t>(11 - (end - buffer))); | ||
| 515 | + return true; | ||
| 516 | +} | ||
| 517 | + | ||
| 518 | +template<> | ||
| 519 | +inline bool Writer<StringBuffer>::WriteUint(unsigned u) { | ||
| 520 | + char *buffer = os_->Push(10); | ||
| 521 | + const char* end = internal::u32toa(u, buffer); | ||
| 522 | + os_->Pop(static_cast<size_t>(10 - (end - buffer))); | ||
| 523 | + return true; | ||
| 524 | +} | ||
| 525 | + | ||
| 526 | +template<> | ||
| 527 | +inline bool Writer<StringBuffer>::WriteInt64(int64_t i64) { | ||
| 528 | + char *buffer = os_->Push(21); | ||
| 529 | + const char* end = internal::i64toa(i64, buffer); | ||
| 530 | + os_->Pop(static_cast<size_t>(21 - (end - buffer))); | ||
| 531 | + return true; | ||
| 532 | +} | ||
| 533 | + | ||
| 534 | +template<> | ||
| 535 | +inline bool Writer<StringBuffer>::WriteUint64(uint64_t u) { | ||
| 536 | + char *buffer = os_->Push(20); | ||
| 537 | + const char* end = internal::u64toa(u, buffer); | ||
| 538 | + os_->Pop(static_cast<size_t>(20 - (end - buffer))); | ||
| 539 | + return true; | ||
| 540 | +} | ||
| 541 | + | ||
| 542 | +template<> | ||
| 543 | +inline bool Writer<StringBuffer>::WriteDouble(double d) { | ||
| 544 | + if (internal::Double(d).IsNanOrInf()) { | ||
| 545 | + // Note: This code path can only be reached if (RAPIDJSON_WRITE_DEFAULT_FLAGS & kWriteNanAndInfFlag). | ||
| 546 | + if (!(kWriteDefaultFlags & kWriteNanAndInfFlag)) | ||
| 547 | + return false; | ||
| 548 | + if (internal::Double(d).IsNan()) { | ||
| 549 | + PutReserve(*os_, 3); | ||
| 550 | + PutUnsafe(*os_, 'N'); PutUnsafe(*os_, 'a'); PutUnsafe(*os_, 'N'); | ||
| 551 | + return true; | ||
| 552 | + } | ||
| 553 | + if (internal::Double(d).Sign()) { | ||
| 554 | + PutReserve(*os_, 9); | ||
| 555 | + PutUnsafe(*os_, '-'); | ||
| 556 | + } | ||
| 557 | + else | ||
| 558 | + PutReserve(*os_, 8); | ||
| 559 | + PutUnsafe(*os_, 'I'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'f'); | ||
| 560 | + PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 't'); PutUnsafe(*os_, 'y'); | ||
| 561 | + return true; | ||
| 562 | + } | ||
| 563 | + | ||
| 564 | + char *buffer = os_->Push(25); | ||
| 565 | + char* end = internal::dtoa(d, buffer, maxDecimalPlaces_); | ||
| 566 | + os_->Pop(static_cast<size_t>(25 - (end - buffer))); | ||
| 567 | + return true; | ||
| 568 | +} | ||
| 569 | + | ||
| 570 | +#if defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42) | ||
| 571 | +template<> | ||
| 572 | +inline bool Writer<StringBuffer>::ScanWriteUnescapedString(StringStream& is, size_t length) { | ||
| 573 | + if (length < 16) | ||
| 574 | + return RAPIDJSON_LIKELY(is.Tell() < length); | ||
| 575 | + | ||
| 576 | + if (!RAPIDJSON_LIKELY(is.Tell() < length)) | ||
| 577 | + return false; | ||
| 578 | + | ||
| 579 | + const char* p = is.src_; | ||
| 580 | + const char* end = is.head_ + length; | ||
| 581 | + const char* nextAligned = reinterpret_cast<const char*>((reinterpret_cast<size_t>(p) + 15) & static_cast<size_t>(~15)); | ||
| 582 | + const char* endAligned = reinterpret_cast<const char*>(reinterpret_cast<size_t>(end) & static_cast<size_t>(~15)); | ||
| 583 | + if (nextAligned > end) | ||
| 584 | + return true; | ||
| 585 | + | ||
| 586 | + while (p != nextAligned) | ||
| 587 | + if (*p < 0x20 || *p == '\"' || *p == '\\') { | ||
| 588 | + is.src_ = p; | ||
| 589 | + return RAPIDJSON_LIKELY(is.Tell() < length); | ||
| 590 | + } | ||
| 591 | + else | ||
| 592 | + os_->PutUnsafe(*p++); | ||
| 593 | + | ||
| 594 | + // The rest of string using SIMD | ||
| 595 | + static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; | ||
| 596 | + static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; | ||
| 597 | + static const char space[16] = { 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F }; | ||
| 598 | + const __m128i dq = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&dquote[0])); | ||
| 599 | + const __m128i bs = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&bslash[0])); | ||
| 600 | + const __m128i sp = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&space[0])); | ||
| 601 | + | ||
| 602 | + for (; p != endAligned; p += 16) { | ||
| 603 | + const __m128i s = _mm_load_si128(reinterpret_cast<const __m128i *>(p)); | ||
| 604 | + const __m128i t1 = _mm_cmpeq_epi8(s, dq); | ||
| 605 | + const __m128i t2 = _mm_cmpeq_epi8(s, bs); | ||
| 606 | + const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x1F) == 0x1F | ||
| 607 | + const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3); | ||
| 608 | + unsigned short r = static_cast<unsigned short>(_mm_movemask_epi8(x)); | ||
| 609 | + if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped | ||
| 610 | + SizeType len; | ||
| 611 | +#ifdef _MSC_VER // Find the index of first escaped | ||
| 612 | + unsigned long offset; | ||
| 613 | + _BitScanForward(&offset, r); | ||
| 614 | + len = offset; | ||
| 615 | +#else | ||
| 616 | + len = static_cast<SizeType>(__builtin_ffs(r) - 1); | ||
| 617 | +#endif | ||
| 618 | + char* q = reinterpret_cast<char*>(os_->PushUnsafe(len)); | ||
| 619 | + for (size_t i = 0; i < len; i++) | ||
| 620 | + q[i] = p[i]; | ||
| 621 | + | ||
| 622 | + p += len; | ||
| 623 | + break; | ||
| 624 | + } | ||
| 625 | + _mm_storeu_si128(reinterpret_cast<__m128i *>(os_->PushUnsafe(16)), s); | ||
| 626 | + } | ||
| 627 | + | ||
| 628 | + is.src_ = p; | ||
| 629 | + return RAPIDJSON_LIKELY(is.Tell() < length); | ||
| 630 | +} | ||
| 631 | +#elif defined(RAPIDJSON_NEON) | ||
| 632 | +template<> | ||
| 633 | +inline bool Writer<StringBuffer>::ScanWriteUnescapedString(StringStream& is, size_t length) { | ||
| 634 | + if (length < 16) | ||
| 635 | + return RAPIDJSON_LIKELY(is.Tell() < length); | ||
| 636 | + | ||
| 637 | + if (!RAPIDJSON_LIKELY(is.Tell() < length)) | ||
| 638 | + return false; | ||
| 639 | + | ||
| 640 | + const char* p = is.src_; | ||
| 641 | + const char* end = is.head_ + length; | ||
| 642 | + const char* nextAligned = reinterpret_cast<const char*>((reinterpret_cast<size_t>(p) + 15) & static_cast<size_t>(~15)); | ||
| 643 | + const char* endAligned = reinterpret_cast<const char*>(reinterpret_cast<size_t>(end) & static_cast<size_t>(~15)); | ||
| 644 | + if (nextAligned > end) | ||
| 645 | + return true; | ||
| 646 | + | ||
| 647 | + while (p != nextAligned) | ||
| 648 | + if (*p < 0x20 || *p == '\"' || *p == '\\') { | ||
| 649 | + is.src_ = p; | ||
| 650 | + return RAPIDJSON_LIKELY(is.Tell() < length); | ||
| 651 | + } | ||
| 652 | + else | ||
| 653 | + os_->PutUnsafe(*p++); | ||
| 654 | + | ||
| 655 | + // The rest of string using SIMD | ||
| 656 | + const uint8x16_t s0 = vmovq_n_u8('"'); | ||
| 657 | + const uint8x16_t s1 = vmovq_n_u8('\\'); | ||
| 658 | + const uint8x16_t s2 = vmovq_n_u8('\b'); | ||
| 659 | + const uint8x16_t s3 = vmovq_n_u8(32); | ||
| 660 | + | ||
| 661 | + for (; p != endAligned; p += 16) { | ||
| 662 | + const uint8x16_t s = vld1q_u8(reinterpret_cast<const uint8_t *>(p)); | ||
| 663 | + uint8x16_t x = vceqq_u8(s, s0); | ||
| 664 | + x = vorrq_u8(x, vceqq_u8(s, s1)); | ||
| 665 | + x = vorrq_u8(x, vceqq_u8(s, s2)); | ||
| 666 | + x = vorrq_u8(x, vcltq_u8(s, s3)); | ||
| 667 | + | ||
| 668 | + x = vrev64q_u8(x); // Rev in 64 | ||
| 669 | + uint64_t low = vgetq_lane_u64(reinterpret_cast<uint64x2_t>(x), 0); // extract | ||
| 670 | + uint64_t high = vgetq_lane_u64(reinterpret_cast<uint64x2_t>(x), 1); // extract | ||
| 671 | + | ||
| 672 | + SizeType len = 0; | ||
| 673 | + bool escaped = false; | ||
| 674 | + if (low == 0) { | ||
| 675 | + if (high != 0) { | ||
| 676 | + unsigned lz = (unsigned)__builtin_clzll(high); | ||
| 677 | + len = 8 + (lz >> 3); | ||
| 678 | + escaped = true; | ||
| 679 | + } | ||
| 680 | + } else { | ||
| 681 | + unsigned lz = (unsigned)__builtin_clzll(low); | ||
| 682 | + len = lz >> 3; | ||
| 683 | + escaped = true; | ||
| 684 | + } | ||
| 685 | + if (RAPIDJSON_UNLIKELY(escaped)) { // some of characters is escaped | ||
| 686 | + char* q = reinterpret_cast<char*>(os_->PushUnsafe(len)); | ||
| 687 | + for (size_t i = 0; i < len; i++) | ||
| 688 | + q[i] = p[i]; | ||
| 689 | + | ||
| 690 | + p += len; | ||
| 691 | + break; | ||
| 692 | + } | ||
| 693 | + vst1q_u8(reinterpret_cast<uint8_t *>(os_->PushUnsafe(16)), s); | ||
| 694 | + } | ||
| 695 | + | ||
| 696 | + is.src_ = p; | ||
| 697 | + return RAPIDJSON_LIKELY(is.Tell() < length); | ||
| 698 | +} | ||
| 699 | +#endif // RAPIDJSON_NEON | ||
| 700 | + | ||
| 701 | +RAPIDJSON_NAMESPACE_END | ||
| 702 | + | ||
| 703 | +#ifdef _MSC_VER | ||
| 704 | +RAPIDJSON_DIAG_POP | ||
| 705 | +#endif | ||
| 706 | + | ||
| 707 | +#ifdef __clang__ | ||
| 708 | +RAPIDJSON_DIAG_POP | ||
| 709 | +#endif | ||
| 710 | + | ||
| 711 | +#endif // RAPIDJSON_RAPIDJSON_H_ |
| ... | @@ -2,50 +2,26 @@ | ... | @@ -2,50 +2,26 @@ |
| 2 | #include "PaperGraphWidget.h" | 2 | #include "PaperGraphWidget.h" |
| 3 | #include "MainWindow.h" | 3 | #include "MainWindow.h" |
| 4 | 4 | ||
| 5 | -size_t write_callback(void *contents, size_t size, | 5 | +int main(int argc, char *argv[]) { |
| 6 | - size_t nmemb, void *userp) { | ||
| 7 | - std::string* p_str = (std::string*)userp; | ||
| 8 | - | ||
| 9 | - //clear | ||
| 10 | - if (!p_str->empty()) | ||
| 11 | - p_str->clear(); | ||
| 12 | - | ||
| 13 | - //write | ||
| 14 | - p_str->append((char*)contents, size*nmemb); | ||
| 15 | - return size*nmemb; | ||
| 16 | -} | ||
| 17 | - | ||
| 18 | -int main(int argc, char *argv[]) | ||
| 19 | -{ | ||
| 20 | if (1) { | 6 | if (1) { |
| 21 | - CURL *curl; | 7 | + _curl_processor.set_url("http://dblp.uni-trier.de/rec/bib1/conf/sbrn/WedemannCD06"); |
| 22 | - CURLcode res; | 8 | + _curl_processor.perform(); |
| 23 | - std::string read_buffer; | 9 | + printf("%s", _curl_processor.get_buffer().c_str()); |
| 24 | - | 10 | + |
| 25 | - curl = curl_easy_init(); | 11 | + _bibtex_processor.read(_curl_processor.get_buffer()); |
| 26 | - if (curl) { | 12 | + std::string doi; |
| 27 | - curl_easy_setopt(curl, CURLOPT_URL, "http://dblp.uni-trier.de/rec/bib/conf/sbrn/WedemannCD06"); | 13 | + _bibtex_processor.get_value("doi", doi); |
| 28 | - /* example.com is redirected, so we tell libcurl to follow redirection */ | ||
| 29 | - | ||
| 30 | - //write option | ||
| 31 | - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback); | ||
| 32 | - curl_easy_setopt(curl, CURLOPT_WRITEDATA, &read_buffer); | ||
| 33 | - | ||
| 34 | - /* Perform the request, res will get the return code */ | ||
| 35 | - res = curl_easy_perform(curl); | ||
| 36 | - res = curl_easy_perform(curl); | ||
| 37 | 14 | ||
| 15 | + std::string address = std::string("http://api.crossref.org/works/")+doi; | ||
| 16 | + _curl_processor.set_url(address.c_str()); | ||
| 17 | + _curl_processor.perform(); | ||
| 38 | 18 | ||
| 39 | - printf("%s\n", read_buffer.c_str()); | 19 | + printf("json: %s\n", _curl_processor.get_buffer().c_str()); |
| 40 | 20 | ||
| 41 | - /* Check for errors */ | 21 | + //rapidjson test |
| 42 | - if (res != CURLE_OK) | 22 | + rapidjson::Document d; |
| 43 | - fprintf(stderr, "curl_easy_perform() failed: %s\n", | 23 | + d.Parse(_curl_processor.get_buffer().c_str()); |
| 44 | - curl_easy_strerror(res)); | ||
| 45 | 24 | ||
| 46 | - /* always cleanup */ | ||
| 47 | - curl_easy_cleanup(curl); | ||
| 48 | - } | ||
| 49 | return 0; | 25 | return 0; |
| 50 | } | 26 | } |
| 51 | 27 | ... | ... |
| 1 | #include "stdafx.h" | 1 | #include "stdafx.h" |
| 2 | + | ||
| 3 | +size_t write_callback(void *contents, size_t size, size_t nmemb, void *userp) { | ||
| 4 | + //copy ptr | ||
| 5 | + std::string* p_str = (std::string*)userp; | ||
| 6 | + | ||
| 7 | + //clear | ||
| 8 | + if (!p_str->empty()) | ||
| 9 | + p_str->clear(); | ||
| 10 | + | ||
| 11 | + //write | ||
| 12 | + p_str->append((char*)contents, size*nmemb); | ||
| 13 | + return size*nmemb; | ||
| 14 | +} | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
| 1 | #pragma once | 1 | #pragma once |
| 2 | +#define _CRT_SECURE_NO_WARNINGS | ||
| 3 | + | ||
| 2 | #include <QColor> | 4 | #include <QColor> |
| 3 | #include <QComboBox> | 5 | #include <QComboBox> |
| 4 | #include <QDebug> | 6 | #include <QDebug> |
| ... | @@ -47,17 +49,32 @@ | ... | @@ -47,17 +49,32 @@ |
| 47 | #include <utility> | 49 | #include <utility> |
| 48 | #include <vector> | 50 | #include <vector> |
| 49 | 51 | ||
| 52 | +#include <bibtexreader.hpp> | ||
| 53 | + | ||
| 54 | +#include <rapidjson/document.h> | ||
| 55 | +#include <rapidjson/writer.h> | ||
| 56 | +#include <rapidjson/stringbuffer.h> | ||
| 57 | + | ||
| 58 | +#include "bibtex_processor.h" | ||
| 59 | +#include "curl_processor.h" | ||
| 60 | + | ||
| 61 | +using bibtex::BibTeXEntry; | ||
| 50 | using namespace boost; | 62 | using namespace boost; |
| 51 | using namespace std; | 63 | using namespace std; |
| 52 | 64 | ||
| 65 | +/* function declaration */ | ||
| 66 | +size_t write_callback(void *contents, size_t size, size_t nmemb, void *userp); | ||
| 67 | + | ||
| 53 | /* enums */ | 68 | /* enums */ |
| 54 | enum vertex_position_t { vertex_position }; | 69 | enum vertex_position_t { vertex_position }; |
| 55 | enum vertex_type_t { vertex_type }; | 70 | enum vertex_type_t { vertex_type }; |
| 56 | enum vertex_record_t { vertex_record }; | 71 | enum vertex_record_t { vertex_record }; |
| 72 | +enum vertex_citation_t { vertex_citation }; | ||
| 57 | namespace boost { | 73 | namespace boost { |
| 58 | BOOST_INSTALL_PROPERTY(vertex, position); | 74 | BOOST_INSTALL_PROPERTY(vertex, position); |
| 59 | BOOST_INSTALL_PROPERTY(vertex, type); | 75 | BOOST_INSTALL_PROPERTY(vertex, type); |
| 60 | BOOST_INSTALL_PROPERTY(vertex, record); | 76 | BOOST_INSTALL_PROPERTY(vertex, record); |
| 77 | + BOOST_INSTALL_PROPERTY(vertex, citation); | ||
| 61 | } | 78 | } |
| 62 | enum NODE_TYPE { | 79 | enum NODE_TYPE { |
| 63 | NODE_PAPER, | 80 | NODE_PAPER, |
| ... | @@ -75,9 +92,11 @@ typedef boost::bimap<string, int> bm_type; | ... | @@ -75,9 +92,11 @@ typedef boost::bimap<string, int> bm_type; |
| 75 | typedef square_topology<>::point_type point; | 92 | typedef square_topology<>::point_type point; |
| 76 | typedef boost::property<vertex_index_t, int, | 93 | typedef boost::property<vertex_index_t, int, |
| 77 | boost::property<vertex_name_t, std::string, | 94 | boost::property<vertex_name_t, std::string, |
| 78 | - boost::property<vertex_position_t, point, | 95 | + boost::property<vertex_position_t, point, //좌표값 |
| 79 | - boost::property<vertex_type_t, int, | 96 | + boost::property<vertex_type_t, int, //타입. enum NODE_TYPE에 정의됨 |
| 80 | - boost::property<vertex_record_t, int>>>> | 97 | + boost::property<vertex_record_t, int, //이웃 노드 개수 |
| 98 | + boost::property<vertex_citation_t, int> | ||
| 99 | + >>>> | ||
| 81 | > VertexProperties; | 100 | > VertexProperties; |
| 82 | typedef boost::adjacency_list< | 101 | typedef boost::adjacency_list< |
| 83 | listS, //outEdgeList | 102 | listS, //outEdgeList |
| ... | @@ -108,11 +127,11 @@ namespace { | ... | @@ -108,11 +127,11 @@ namespace { |
| 108 | //const int READ_LINE_UNIT = 20; //한 번에 몇 라인을 읽을지 | 127 | //const int READ_LINE_UNIT = 20; //한 번에 몇 라인을 읽을지 |
| 109 | const int READ_LINE_UNIT = 100; | 128 | const int READ_LINE_UNIT = 100; |
| 110 | 129 | ||
| 111 | - /* topK */ | 130 | + /* curl processor */ |
| 112 | - //const int TOP_K = 5; //상위 몇 개 아이템에 대해 highlight 할 지 | 131 | + curl_processor _curl_processor; |
| 113 | 132 | ||
| 114 | - /* a research you might know */ | 133 | + /* bibtex processor */ |
| 115 | - //const char* TARGET_AUTHOR_NAME = "Shuichi Itoh"; | 134 | + bibtex_processor _bibtex_processor; |
| 116 | } | 135 | } |
| 117 | 136 | ||
| 118 | /* boost */ | 137 | /* boost */ |
| ... | @@ -204,4 +223,4 @@ public: | ... | @@ -204,4 +223,4 @@ public: |
| 204 | } | 223 | } |
| 205 | int getSize() {return size;} | 224 | int getSize() {return size;} |
| 206 | bool isFull() {return k==size;} | 225 | bool isFull() {return k==size;} |
| 207 | -}; | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
| 226 | +}; | ... | ... |
-
Please register or login to post a comment