From dd49839485ddceaaac97d525245a0e639bf448eb Mon Sep 17 00:00:00 2001 From: "K. Mlynarczyk" Date: Fri, 21 Jun 2019 11:08:41 +0200 Subject: [PATCH 1/7] Fix compilation with Boost 1.70.0 --- src/drivers/BoostDriver.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/drivers/BoostDriver.cpp b/src/drivers/BoostDriver.cpp index 15f1b006..315b49a3 100644 --- a/src/drivers/BoostDriver.cpp +++ b/src/drivers/BoostDriver.cpp @@ -50,7 +50,11 @@ class CukeBoostLogInterceptor : public ::boost::unit_test::unit_test_log_formatt // Formatter void log_start( std::ostream&, counter_t /*test_cases_amount*/) {}; void log_finish( std::ostream&) {}; +#if BOOST_VERSION >= 107000 + void log_build_info( std::ostream&, bool /*log_build_info*/) {}; +#else void log_build_info( std::ostream&) {}; +#endif void test_unit_start( std::ostream&, test_unit const& /*tu*/) {}; void test_unit_finish( std::ostream&, test_unit const& /*tu*/, unsigned long /*elapsed*/) {}; From 5fff48fac12d0fba74d173c426c672347ff47353 Mon Sep 17 00:00:00 2001 From: src Date: Fri, 14 Jun 2019 12:22:16 +0200 Subject: [PATCH 2/7] Add tests to demonstrate bug caused by json_spirit. json_spirit's escaping of multibyte characters creates bugs in the WireProtocol which prevent usage of valid UTF-8 encoded characters in step definitions relying on RegEx. The new tests: /features/specific/wire_encoding.feature /tests/integration/WireProtocolTest.cpp /tests/unit/RegexTest.cpp --- features/specific/wire_encoding.feature | 36 +++++++++++++++++++ tests/integration/WireProtocolTest.cpp | 46 +++++++++++++++++++++++++ tests/unit/RegexTest.cpp | 13 +++++++ 3 files changed, 95 insertions(+) create mode 100644 features/specific/wire_encoding.feature diff --git a/features/specific/wire_encoding.feature b/features/specific/wire_encoding.feature new file mode 100644 index 00000000..b9014d80 --- /dev/null +++ b/features/specific/wire_encoding.feature @@ -0,0 +1,36 @@ +Feature: Wire Encoding Feature + + This is just a simple feature meant to test + transmission of UTF-8 over the WireProtocol. + + Scenario: Multibyte Character Step Matching + Given the following feature: + """ + Feature: Match Regex With Multibyte Character String + + Scenario: Match Regex + Given a step which uses regex to match the following text: 'カラオケ機' and 'ASCII' + """ + And a step definition file with support code including: + """ + #include + #include + + GIVEN("a step which uses regex to match the following text: '(.+)' and '(.+)'") { + REGEX_PARAM(std::string, Match1); + REGEX_PARAM(std::string, Match2); + std::cout << Match1 << Match2 << std::endl; + } + """ + When Cucumber runs the feature + Then the step output should contain: + # EXPECTED + # """ + # カラオケ機ASCII + # + # """ + # ACTUAL + """ + \\u00E3\\u0082\\u00AB\\u00E3\\u0083\\u00A9\\u00E3\\u0082\\u00AA\\u00E3\\u0082\\u00B1\\u00E6\\u00A9\\u009FASCII + + """ diff --git a/tests/integration/WireProtocolTest.cpp b/tests/integration/WireProtocolTest.cpp index 4209a0b5..718bc199 100644 --- a/tests/integration/WireProtocolTest.cpp +++ b/tests/integration/WireProtocolTest.cpp @@ -256,6 +256,52 @@ TEST_F(WireMessageCodecTest, handlesSnippetTextResponse) { EXPECT_THAT(codec.encode(response), StrEq("[\"success\",\"GIVEN(...)\"]")); } +TEST_F(WireMessageCodecTest, encodesResponseUsingRawUtf8) { + std::vector matches; + StepMatch sm1; + sm1.id = "1234"; + sm1.regexp = "Some (.+) regexp (.+)"; + StepMatchArg sm1arg1; + sm1arg1.value = "カラオケ機"; + sm1arg1.position = 5; + sm1.args.push_back(sm1arg1); + StepMatchArg sm1arg2; + sm1arg2.value = "ASCII"; + sm1arg2.position = 18; + sm1.args.push_back(sm1arg2); + matches.push_back(sm1); + StepMatchesResponse response(matches); + + // clang-format off + // EXPECTED: + // EXPECT_THAT(codec.encode(response), StrEq( + // "[\"success\",[{" + // "\"args\":[{" + // "\"pos\":5," + // "\"val\":\"カラオケ機\"" + // "},{" + // "\"pos\":18," + // "\"val\":\"ASCII\"" + // "}]," + // "\"id\":\"1234\"," + // "\"regexp\":\"Some (.+) regexp (.+)\"" + // "}]]")); + // ACTUAL: + EXPECT_THAT(codec.encode(response), StrEq( + "[\"success\",[{" + "\"args\":[{" + "\"pos\":5," + "\"val\":\"\\u00E3\\u0082\\u00AB\\u00E3\\u0083\\u00A9\\u00E3\\u0082\\u00AA\\u00E3\\u0082\\u00B1\\u00E6\\u00A9\\u009F\"" + "},{" + "\"pos\":18," + "\"val\":\"ASCII\"" + "}]," + "\"id\":\"1234\"," + "\"regexp\":\"Some (.+) regexp (.+)\"" + "}]]")); + // clang-format on +} + /* * Command response */ diff --git a/tests/unit/RegexTest.cpp b/tests/unit/RegexTest.cpp index a618ba16..e29b9a7d 100644 --- a/tests/unit/RegexTest.cpp +++ b/tests/unit/RegexTest.cpp @@ -70,6 +70,19 @@ TEST(RegexTest, findAllDoesNotMatchIfNoTokens) { EXPECT_EQ(0, match->getSubmatches().size()); } +TEST(RegexTest, findReportsCodepointPositions) { + Regex twoArgs("Some (.+) regexp (.+)"); + shared_ptr match(twoArgs.find("Some カラオケ機 regexp ASCII")); + + EXPECT_TRUE(match->matches()); + ASSERT_EQ(2, match->getSubmatches().size()); + EXPECT_EQ(5, match->getSubmatches()[0].position); + // EXPECTED: + // EXPECT_EQ(18, match->getSubmatches()[1].position); + // ACTUAL + EXPECT_EQ(28, match->getSubmatches()[1].position); +} + TEST(RegexTest, findAllExtractsTheFirstGroupOfEveryToken) { Regex sum("([^,]+)(?:,|$)"); shared_ptr match(sum.findAll("a,b,cc")); From ee964b21ae69a07dc44f78ebf9107314b5bb6f43 Mon Sep 17 00:00:00 2001 From: src Date: Tue, 4 Jun 2019 18:27:34 +0200 Subject: [PATCH 3/7] Update json-spirit to version 4.08 to support multibyte characters. This change updates json-spirit to the latest public version: https://www.codeproject.com/KB/recipes/JSON_Spirit/json_spirit_v4.08.zip 4.08 adds support for a raw_utf8 option when writing a JSON string. Previously, multibyte characters were being escaped when being sent from cucumber-cpp to cucumber-ruby. Because cucumber-ruby's wire decoder does not properly decode escaped character sequences, this would crash cucumber-ruby. --- 3rdparty/json_spirit/CMakeLists.txt | 2 + 3rdparty/json_spirit/json_spirit.h | 4 +- 3rdparty/json_spirit/json_spirit.vcproj | 4 + .../json_spirit/json_spirit_error_position.h | 6 +- 3rdparty/json_spirit/json_spirit_reader.cpp | 248 +++++++++--------- 3rdparty/json_spirit/json_spirit_reader.h | 16 +- .../json_spirit/json_spirit_reader_template.h | 114 +++++--- .../json_spirit/json_spirit_stream_reader.h | 4 +- 3rdparty/json_spirit/json_spirit_utils.h | 8 +- 3rdparty/json_spirit/json_spirit_value.cpp | 8 +- 3rdparty/json_spirit/json_spirit_value.h | 201 +++++++++----- 3rdparty/json_spirit/json_spirit_writer.cpp | 169 ++++++------ 3rdparty/json_spirit/json_spirit_writer.h | 63 +++-- .../json_spirit/json_spirit_writer_options.h | 35 +++ .../json_spirit/json_spirit_writer_template.h | 138 ++++++++-- travis.sh | 2 +- 16 files changed, 636 insertions(+), 386 deletions(-) create mode 100644 3rdparty/json_spirit/json_spirit_writer_options.h diff --git a/3rdparty/json_spirit/CMakeLists.txt b/3rdparty/json_spirit/CMakeLists.txt index 5c461d1f..c1406a5d 100644 --- a/3rdparty/json_spirit/CMakeLists.txt +++ b/3rdparty/json_spirit/CMakeLists.txt @@ -8,6 +8,7 @@ target_sources(json_spirit.header INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/json_spirit_stream_reader.h ${CMAKE_CURRENT_SOURCE_DIR}/json_spirit_utils.h ${CMAKE_CURRENT_SOURCE_DIR}/json_spirit_value.h + ${CMAKE_CURRENT_SOURCE_DIR}/json_spirit_writer_options.h ${CMAKE_CURRENT_SOURCE_DIR}/json_spirit_writer_template.h ) target_include_directories(json_spirit.header SYSTEM @@ -22,5 +23,6 @@ add_library(json_spirit STATIC EXCLUDE_FROM_ALL ${CMAKE_CURRENT_SOURCE_DIR}/json_spirit_value.cpp ${CMAKE_CURRENT_SOURCE_DIR}/json_spirit_writer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/json_spirit_writer.h + ${CMAKE_CURRENT_SOURCE_DIR}/json_spirit_writer_options.h ) target_link_libraries(json_spirit PUBLIC json_spirit.header) diff --git a/3rdparty/json_spirit/json_spirit.h b/3rdparty/json_spirit/json_spirit.h index 7dac05c3..5e50fc1f 100644 --- a/3rdparty/json_spirit/json_spirit.h +++ b/3rdparty/json_spirit/json_spirit.h @@ -1,10 +1,10 @@ #ifndef JSON_SPIRIT #define JSON_SPIRIT -// Copyright John W. Wilkinson 2007 - 2009. +// Copyright John W. Wilkinson 2007 - 2014 // Distributed under the MIT License, see accompanying file LICENSE.txt -// json spirit version 4.03 +// json spirit version 4.08 #if defined(_MSC_VER) && (_MSC_VER >= 1020) # pragma once diff --git a/3rdparty/json_spirit/json_spirit.vcproj b/3rdparty/json_spirit/json_spirit.vcproj index ed0e5e0c..0cbe0efa 100644 --- a/3rdparty/json_spirit/json_spirit.vcproj +++ b/3rdparty/json_spirit/json_spirit.vcproj @@ -198,6 +198,10 @@ RelativePath=".\json_spirit_writer.h" > + + diff --git a/3rdparty/json_spirit/json_spirit_error_position.h b/3rdparty/json_spirit/json_spirit_error_position.h index 4a535ff5..23e7075d 100644 --- a/3rdparty/json_spirit/json_spirit_error_position.h +++ b/3rdparty/json_spirit/json_spirit_error_position.h @@ -1,10 +1,10 @@ #ifndef JSON_SPIRIT_ERROR_POSITION #define JSON_SPIRIT_ERROR_POSITION -// Copyright John W. Wilkinson 2007 - 2009. +// Copyright John W. Wilkinson 2007 - 2014 // Distributed under the MIT License, see accompanying file LICENSE.txt -// json spirit version 4.03 +// json spirit version 4.08 #if defined(_MSC_VER) && (_MSC_VER >= 1020) # pragma once @@ -48,7 +48,7 @@ namespace json_spirit return ( reason_ == lhs.reason_ ) && ( line_ == lhs.line_ ) && ( column_ == lhs.column_ ); -} + } } #endif diff --git a/3rdparty/json_spirit/json_spirit_reader.cpp b/3rdparty/json_spirit/json_spirit_reader.cpp index 8e2fb5e2..1bcbf35b 100644 --- a/3rdparty/json_spirit/json_spirit_reader.cpp +++ b/3rdparty/json_spirit/json_spirit_reader.cpp @@ -1,137 +1,137 @@ -// Copyright John W. Wilkinson 2007 - 2009. +// Copyright John W. Wilkinson 2007 - 2014 // Distributed under the MIT License, see accompanying file LICENSE.txt -// json spirit version 4.03 +// json spirit version 4.08 #include "json_spirit_reader.h" #include "json_spirit_reader_template.h" using namespace json_spirit; -bool json_spirit::read( const std::string& s, Value& value ) -{ - return read_string( s, value ); -} - -void json_spirit::read_or_throw( const std::string& s, Value& value ) -{ - read_string_or_throw( s, value ); -} - -bool json_spirit::read( std::istream& is, Value& value ) -{ - return read_stream( is, value ); -} - -void json_spirit::read_or_throw( std::istream& is, Value& value ) -{ - read_stream_or_throw( is, value ); -} - -bool json_spirit::read( std::string::const_iterator& begin, std::string::const_iterator end, Value& value ) -{ - return read_range( begin, end, value ); -} - -void json_spirit::read_or_throw( std::string::const_iterator& begin, std::string::const_iterator end, Value& value ) -{ - begin = read_range_or_throw( begin, end, value ); -} - -#ifndef BOOST_NO_STD_WSTRING - -bool json_spirit::read( const std::wstring& s, wValue& value ) -{ - return read_string( s, value ); -} - -void json_spirit::read_or_throw( const std::wstring& s, wValue& value ) -{ - read_string_or_throw( s, value ); -} - -bool json_spirit::read( std::wistream& is, wValue& value ) -{ - return read_stream( is, value ); -} - -void json_spirit::read_or_throw( std::wistream& is, wValue& value ) -{ - read_stream_or_throw( is, value ); -} - -bool json_spirit::read( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wValue& value ) -{ - return read_range( begin, end, value ); -} - -void json_spirit::read_or_throw( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wValue& value ) -{ - begin = read_range_or_throw( begin, end, value ); -} +#ifdef JSON_SPIRIT_VALUE_ENABLED + bool json_spirit::read( const std::string& s, Value& value ) + { + return read_string( s, value ); + } + + void json_spirit::read_or_throw( const std::string& s, Value& value ) + { + read_string_or_throw( s, value ); + } + + bool json_spirit::read( std::istream& is, Value& value ) + { + return read_stream( is, value ); + } + + void json_spirit::read_or_throw( std::istream& is, Value& value ) + { + read_stream_or_throw( is, value ); + } + + bool json_spirit::read( std::string::const_iterator& begin, std::string::const_iterator end, Value& value ) + { + return read_range( begin, end, value ); + } + + void json_spirit::read_or_throw( std::string::const_iterator& begin, std::string::const_iterator end, Value& value ) + { + begin = read_range_or_throw( begin, end, value ); + } +#endif +#if defined( JSON_SPIRIT_WVALUE_ENABLED ) && !defined( BOOST_NO_STD_WSTRING ) + bool json_spirit::read( const std::wstring& s, wValue& value ) + { + return read_string( s, value ); + } + + void json_spirit::read_or_throw( const std::wstring& s, wValue& value ) + { + read_string_or_throw( s, value ); + } + + bool json_spirit::read( std::wistream& is, wValue& value ) + { + return read_stream( is, value ); + } + + void json_spirit::read_or_throw( std::wistream& is, wValue& value ) + { + read_stream_or_throw( is, value ); + } + + bool json_spirit::read( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wValue& value ) + { + return read_range( begin, end, value ); + } + + void json_spirit::read_or_throw( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wValue& value ) + { + begin = read_range_or_throw( begin, end, value ); + } #endif -bool json_spirit::read( const std::string& s, mValue& value ) -{ - return read_string( s, value ); -} - -void json_spirit::read_or_throw( const std::string& s, mValue& value ) -{ - read_string_or_throw( s, value ); -} - -bool json_spirit::read( std::istream& is, mValue& value ) -{ - return read_stream( is, value ); -} - -void json_spirit::read_or_throw( std::istream& is, mValue& value ) -{ - read_stream_or_throw( is, value ); -} - -bool json_spirit::read( std::string::const_iterator& begin, std::string::const_iterator end, mValue& value ) -{ - return read_range( begin, end, value ); -} - -void json_spirit::read_or_throw( std::string::const_iterator& begin, std::string::const_iterator end, mValue& value ) -{ - begin = read_range_or_throw( begin, end, value ); -} - -#ifndef BOOST_NO_STD_WSTRING - -bool json_spirit::read( const std::wstring& s, wmValue& value ) -{ - return read_string( s, value ); -} - -void json_spirit::read_or_throw( const std::wstring& s, wmValue& value ) -{ - read_string_or_throw( s, value ); -} - -bool json_spirit::read( std::wistream& is, wmValue& value ) -{ - return read_stream( is, value ); -} - -void json_spirit::read_or_throw( std::wistream& is, wmValue& value ) -{ - read_stream_or_throw( is, value ); -} - -bool json_spirit::read( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wmValue& value ) -{ - return read_range( begin, end, value ); -} - -void json_spirit::read_or_throw( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wmValue& value ) -{ - begin = read_range_or_throw( begin, end, value ); -} +#ifdef JSON_SPIRIT_MVALUE_ENABLED + bool json_spirit::read( const std::string& s, mValue& value ) + { + return read_string( s, value ); + } + + void json_spirit::read_or_throw( const std::string& s, mValue& value ) + { + read_string_or_throw( s, value ); + } + + bool json_spirit::read( std::istream& is, mValue& value ) + { + return read_stream( is, value ); + } + + void json_spirit::read_or_throw( std::istream& is, mValue& value ) + { + read_stream_or_throw( is, value ); + } + + bool json_spirit::read( std::string::const_iterator& begin, std::string::const_iterator end, mValue& value ) + { + return read_range( begin, end, value ); + } + + void json_spirit::read_or_throw( std::string::const_iterator& begin, std::string::const_iterator end, mValue& value ) + { + begin = read_range_or_throw( begin, end, value ); + } +#endif +#if defined( JSON_SPIRIT_WMVALUE_ENABLED ) && !defined( BOOST_NO_STD_WSTRING ) + bool json_spirit::read( const std::wstring& s, wmValue& value ) + { + return read_string( s, value ); + } + + void json_spirit::read_or_throw( const std::wstring& s, wmValue& value ) + { + read_string_or_throw( s, value ); + } + + bool json_spirit::read( std::wistream& is, wmValue& value ) + { + return read_stream( is, value ); + } + + void json_spirit::read_or_throw( std::wistream& is, wmValue& value ) + { + read_stream_or_throw( is, value ); + } + + bool json_spirit::read( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wmValue& value ) + { + return read_range( begin, end, value ); + } + + void json_spirit::read_or_throw( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wmValue& value ) + { + begin = read_range_or_throw( begin, end, value ); + } #endif diff --git a/3rdparty/json_spirit/json_spirit_reader.h b/3rdparty/json_spirit/json_spirit_reader.h index a58bfc10..4dea8684 100644 --- a/3rdparty/json_spirit/json_spirit_reader.h +++ b/3rdparty/json_spirit/json_spirit_reader.h @@ -1,10 +1,10 @@ #ifndef JSON_SPIRIT_READER #define JSON_SPIRIT_READER -// Copyright John W. Wilkinson 2007 - 2009. +// Copyright John W. Wilkinson 2007 - 2014 // Distributed under the MIT License, see accompanying file LICENSE.txt -// json spirit version 4.03 +// json spirit version 4.08 #if defined(_MSC_VER) && (_MSC_VER >= 1020) # pragma once @@ -18,6 +18,7 @@ namespace json_spirit { // functions to reads a JSON values +#ifdef JSON_SPIRIT_VALUE_ENABLED bool read( const std::string& s, Value& value ); bool read( std::istream& is, Value& value ); bool read( std::string::const_iterator& begin, std::string::const_iterator end, Value& value ); @@ -25,9 +26,9 @@ namespace json_spirit void read_or_throw( const std::string& s, Value& value ); void read_or_throw( std::istream& is, Value& value ); void read_or_throw( std::string::const_iterator& begin, std::string::const_iterator end, Value& value ); +#endif -#ifndef BOOST_NO_STD_WSTRING - +#if defined( JSON_SPIRIT_WVALUE_ENABLED ) && !defined( BOOST_NO_STD_WSTRING ) bool read( const std::wstring& s, wValue& value ); bool read( std::wistream& is, wValue& value ); bool read( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wValue& value ); @@ -35,9 +36,9 @@ namespace json_spirit void read_or_throw( const std::wstring& s, wValue& value ); void read_or_throw( std::wistream& is, wValue& value ); void read_or_throw( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wValue& value ); - #endif +#ifdef JSON_SPIRIT_MVALUE_ENABLED bool read( const std::string& s, mValue& value ); bool read( std::istream& is, mValue& value ); bool read( std::string::const_iterator& begin, std::string::const_iterator end, mValue& value ); @@ -45,9 +46,9 @@ namespace json_spirit void read_or_throw( const std::string& s, mValue& value ); void read_or_throw( std::istream& is, mValue& value ); void read_or_throw( std::string::const_iterator& begin, std::string::const_iterator end, mValue& value ); +#endif -#ifndef BOOST_NO_STD_WSTRING - +#if defined( JSON_SPIRIT_WMVALUE_ENABLED ) && !defined( BOOST_NO_STD_WSTRING ) bool read( const std::wstring& s, wmValue& value ); bool read( std::wistream& is, wmValue& value ); bool read( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wmValue& value ); @@ -55,7 +56,6 @@ namespace json_spirit void read_or_throw( const std::wstring& s, wmValue& value ); void read_or_throw( std::wistream& is, wmValue& value ); void read_or_throw( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wmValue& value ); - #endif } diff --git a/3rdparty/json_spirit/json_spirit_reader_template.h b/3rdparty/json_spirit/json_spirit_reader_template.h index 81cded43..c015c4ce 100644 --- a/3rdparty/json_spirit/json_spirit_reader_template.h +++ b/3rdparty/json_spirit/json_spirit_reader_template.h @@ -1,10 +1,14 @@ #ifndef JSON_SPIRIT_READER_TEMPLATE #define JSON_SPIRIT_READER_TEMPLATE -// Copyright John W. Wilkinson 2007 - 2009. +// Copyright John W. Wilkinson 2007 - 2014 // Distributed under the MIT License, see accompanying file LICENSE.txt -// json spirit version 4.03 +// json spirit version 4.08 + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +# pragma once +#endif #include "json_spirit_value.h" #include "json_spirit_error_position.h" @@ -484,7 +488,7 @@ namespace json_spirit ; string_ - = lexeme_d // this causes white space inside a string to be retained + = lexeme_d // this causes white space and what would appear to be comments inside a string to be retained [ confix_p ( @@ -514,6 +518,44 @@ namespace json_spirit Semantic_actions_t& actions_; }; + template< class Iter_type, class Value_type > + void add_posn_iter_and_read_range_or_throw( Iter_type begin, Iter_type end, Value_type& value ) + { + typedef spirit_namespace::position_iterator< Iter_type > Posn_iter_t; + + const Posn_iter_t posn_begin( begin, end ); + const Posn_iter_t posn_end( end, end ); + + read_range_or_throw( posn_begin, posn_end, value ); + } + + template< class Istream_type > + struct Multi_pass_iters + { + typedef typename Istream_type::char_type Char_type; + typedef std::istream_iterator< Char_type, Char_type > istream_iter; + typedef spirit_namespace::multi_pass< istream_iter > Mp_iter; + + Multi_pass_iters( Istream_type& is ) + { + is.unsetf( std::ios::skipws ); + + begin_ = spirit_namespace::make_multi_pass( istream_iter( is ) ); + end_ = spirit_namespace::make_multi_pass( istream_iter() ); + } + + Mp_iter begin_; + Mp_iter end_; + }; + + // reads a JSON Value from a pair of input iterators throwing an exception on invalid input, e.g. + // + // string::const_iterator start = str.begin(); + // const string::const_iterator next = read_range_or_throw( str.begin(), str.end(), value ); + // + // The iterator 'next' will point to the character past the + // last one read. + // template< class Iter_type, class Value_type > Iter_type read_range_or_throw( Iter_type begin, Iter_type end, Value_type& value ) { @@ -522,7 +564,9 @@ namespace json_spirit const spirit_namespace::parse_info< Iter_type > info = spirit_namespace::parse( begin, end, Json_grammer< Value_type, Iter_type >( semantic_actions ), - spirit_namespace::space_p ); + spirit_namespace::space_p | + spirit_namespace::comment_p("//") | + spirit_namespace::comment_p("/*", "*/") ); if( !info.hit ) { @@ -533,17 +577,14 @@ namespace json_spirit return info.stop; } - template< class Iter_type, class Value_type > - void add_posn_iter_and_read_range_or_throw( Iter_type begin, Iter_type end, Value_type& value ) - { - typedef spirit_namespace::position_iterator< Iter_type > Posn_iter_t; - - const Posn_iter_t posn_begin( begin, end ); - const Posn_iter_t posn_end( end, end ); - - read_range_or_throw( posn_begin, posn_end, value ); - } - + // reads a JSON Value from a pair of input iterators, e.g. + // + // string::const_iterator start = str.begin(); + // const bool success = read_string( start, str.end(), value ); + // + // The iterator 'start' will point to the character past the + // last one read. + // template< class Iter_type, class Value_type > bool read_range( Iter_type& begin, Iter_type end, Value_type& value ) { @@ -559,12 +600,10 @@ namespace json_spirit } } - template< class String_type, class Value_type > - void read_string_or_throw( const String_type& s, Value_type& value ) - { - add_posn_iter_and_read_range_or_throw( s.begin(), s.end(), value ); - } - + // reads a JSON Value from a string, e.g. + // + // const bool success = read_string( str, value ); + // template< class String_type, class Value_type > bool read_string( const String_type& s, Value_type& value ) { @@ -573,25 +612,20 @@ namespace json_spirit return read_range( begin, s.end(), value ); } - template< class Istream_type > - struct Multi_pass_iters + // reads a JSON Value from a string throwing an exception on invalid input, e.g. + // + // read_string_or_throw( is, value ); + // + template< class String_type, class Value_type > + void read_string_or_throw( const String_type& s, Value_type& value ) { - typedef typename Istream_type::char_type Char_type; - typedef std::istream_iterator< Char_type, Char_type > istream_iter; - typedef spirit_namespace::multi_pass< istream_iter > Mp_iter; - - Multi_pass_iters( Istream_type& is ) - { - is.unsetf( std::ios::skipws ); - - begin_ = spirit_namespace::make_multi_pass( istream_iter( is ) ); - end_ = spirit_namespace::make_multi_pass( istream_iter() ); - } - - Mp_iter begin_; - Mp_iter end_; - }; + add_posn_iter_and_read_range_or_throw( s.begin(), s.end(), value ); + } + // reads a JSON Value from a stream, e.g. + // + // const bool success = read_stream( is, value ); + // template< class Istream_type, class Value_type > bool read_stream( Istream_type& is, Value_type& value ) { @@ -600,6 +634,10 @@ namespace json_spirit return read_range( mp_iters.begin_, mp_iters.end_, value ); } + // reads a JSON Value from a stream throwing an exception on invalid input, e.g. + // + // read_stream_or_throw( is, value ); + // template< class Istream_type, class Value_type > void read_stream_or_throw( Istream_type& is, Value_type& value ) { diff --git a/3rdparty/json_spirit/json_spirit_stream_reader.h b/3rdparty/json_spirit/json_spirit_stream_reader.h index a9ceeacf..4c3c38d4 100644 --- a/3rdparty/json_spirit/json_spirit_stream_reader.h +++ b/3rdparty/json_spirit/json_spirit_stream_reader.h @@ -1,10 +1,10 @@ #ifndef JSON_SPIRIT_READ_STREAM #define JSON_SPIRIT_READ_STREAM -// Copyright John W. Wilkinson 2007 - 2009. +// Copyright John W. Wilkinson 2007 - 2014 // Distributed under the MIT License, see accompanying file LICENSE.txt -// json spirit version 4.03 +// json spirit version 4.08 #if defined(_MSC_VER) && (_MSC_VER >= 1020) # pragma once diff --git a/3rdparty/json_spirit/json_spirit_utils.h b/3rdparty/json_spirit/json_spirit_utils.h index 7eb338e7..a2e72990 100644 --- a/3rdparty/json_spirit/json_spirit_utils.h +++ b/3rdparty/json_spirit/json_spirit_utils.h @@ -1,10 +1,10 @@ #ifndef JSON_SPIRIT_UTILS #define JSON_SPIRIT_UTILS -// Copyright John W. Wilkinson 2007 - 2009. +// Copyright John W. Wilkinson 2007 - 2014 // Distributed under the MIT License, see accompanying file LICENSE.txt -// json spirit version 4.03 +// json spirit version 4.08 #if defined(_MSC_VER) && (_MSC_VER >= 1020) # pragma once @@ -37,9 +37,11 @@ namespace json_spirit } } +#ifdef JSON_SPIRIT_VALUE_ENABLED typedef std::map< std::string, Value > Mapped_obj; +#endif -#ifndef BOOST_NO_STD_WSTRING +#if defined( JSON_SPIRIT_WVALUE_ENABLED ) && !defined( BOOST_NO_STD_WSTRING ) typedef std::map< std::wstring, wValue > wMapped_obj; #endif diff --git a/3rdparty/json_spirit/json_spirit_value.cpp b/3rdparty/json_spirit/json_spirit_value.cpp index dd5b50e5..5f0260d8 100644 --- a/3rdparty/json_spirit/json_spirit_value.cpp +++ b/3rdparty/json_spirit/json_spirit_value.cpp @@ -1,8 +1,6 @@ -/* Copyright (c) 2007 John W Wilkinson +// Copyright John W. Wilkinson 2007 - 2014 +// Distributed under the MIT License, see accompanying file LICENSE.txt - This source code can be used for any purpose as long as - this comment is retained. */ - -// json spirit version 2.00 +// json spirit version 4.08 #include "json_spirit_value.h" diff --git a/3rdparty/json_spirit/json_spirit_value.h b/3rdparty/json_spirit/json_spirit_value.h index e8be3553..2f36cf44 100644 --- a/3rdparty/json_spirit/json_spirit_value.h +++ b/3rdparty/json_spirit/json_spirit_value.h @@ -1,10 +1,10 @@ #ifndef JSON_SPIRIT_VALUE #define JSON_SPIRIT_VALUE -// Copyright John W. Wilkinson 2007 - 2009. +// Copyright John W. Wilkinson 2007 - 2014 // Distributed under the MIT License, see accompanying file LICENSE.txt -// json spirit version 4.03 +// json spirit version 4.08 #if defined(_MSC_VER) && (_MSC_VER >= 1020) # pragma once @@ -21,10 +21,20 @@ #include #include +// comment out the value types you don't need to reduce build times and intermediate file sizes +#define JSON_SPIRIT_VALUE_ENABLED +#define JSON_SPIRIT_WVALUE_ENABLED +#define JSON_SPIRIT_MVALUE_ENABLED +#define JSON_SPIRIT_WMVALUE_ENABLED + namespace json_spirit { enum Value_type{ obj_type, array_type, str_type, bool_type, int_type, real_type, null_type }; + static std::string value_type_to_string( Value_type vtype ); + + struct Null{}; + template< class Config > // Config determines whether the value uses std::string or std::wstring and // whether JSON Objects are represented as vectors or maps class Value_impl @@ -48,6 +58,12 @@ namespace json_spirit Value_impl( boost::uint64_t value ); Value_impl( double value ); + template< class Iter > + Value_impl( Iter first, Iter last ); // constructor from containers, e.g. std::vector or std::list + + template< BOOST_VARIANT_ENUM_PARAMS( typename T ) > + Value_impl( const boost::variant< BOOST_VARIANT_ENUM_PARAMS(T) >& variant ); // constructor for compatible variant types + Value_impl( const Value_impl& other ); bool operator==( const Value_impl& lhs ) const; @@ -80,13 +96,32 @@ namespace json_spirit void check_type( const Value_type vtype ) const; - typedef boost::variant< String_type, - boost::recursive_wrapper< Object >, boost::recursive_wrapper< Array >, - bool, boost::int64_t, double > Variant; + typedef boost::variant< boost::recursive_wrapper< Object >, boost::recursive_wrapper< Array >, + String_type, bool, boost::int64_t, double, Null, boost::uint64_t > Variant; - Value_type type_; Variant v_; - bool is_uint64_; + + class Variant_converter_visitor : public boost::static_visitor< Variant > + { + public: + + template< typename T, typename A, template< typename, typename > class Cont > + Variant operator()( const Cont< T, A >& cont ) const + { + return Array( cont.begin(), cont.end() ); + } + + Variant operator()( int i ) const + { + return static_cast< boost::int64_t >( i ); + } + + template + Variant operator()( const T& t ) const + { + return t; + } + }; }; // vector objects @@ -97,6 +132,10 @@ namespace json_spirit typedef typename Config::String_type String_type; typedef typename Config::Value_type Value_type; + Pair_impl() + { + } + Pair_impl( const String_type& name, const Value_type& value ); bool operator==( const Pair_impl& lhs ) const; @@ -105,6 +144,7 @@ namespace json_spirit Value_type value_; }; +#if defined( JSON_SPIRIT_VALUE_ENABLED ) || defined( JSON_SPIRIT_WVALUE_ENABLED ) template< class String > struct Config_vector { @@ -121,30 +161,32 @@ namespace json_spirit return obj.back().value_; } - static String_type get_name( const Pair_type& pair ) + static const String_type& get_name( const Pair_type& pair ) { return pair.name_; } - static Value_type get_value( const Pair_type& pair ) + static const Value_type& get_value( const Pair_type& pair ) { return pair.value_; } }; +#endif // typedefs for ASCII +#ifdef JSON_SPIRIT_VALUE_ENABLED typedef Config_vector< std::string > Config; typedef Config::Value_type Value; typedef Config::Pair_type Pair; typedef Config::Object_type Object; typedef Config::Array_type Array; +#endif // typedefs for Unicode -#ifndef BOOST_NO_STD_WSTRING - +#if defined( JSON_SPIRIT_WVALUE_ENABLED ) && !defined( BOOST_NO_STD_WSTRING ) typedef Config_vector< std::wstring > wConfig; typedef wConfig::Value_type wValue; @@ -155,6 +197,7 @@ namespace json_spirit // map objects +#if defined( JSON_SPIRIT_MVALUE_ENABLED ) || defined( JSON_SPIRIT_WMVALUE_ENABLED ) template< class String > struct Config_map { @@ -162,135 +205,134 @@ namespace json_spirit typedef Value_impl< Config_map > Value_type; typedef std::vector< Value_type > Array_type; typedef std::map< String_type, Value_type > Object_type; - typedef typename Object_type::value_type Pair_type; + typedef std::pair< const String_type, Value_type > Pair_type; static Value_type& add( Object_type& obj, const String_type& name, const Value_type& value ) { return obj[ name ] = value; } - static String_type get_name( const Pair_type& pair ) + static const String_type& get_name( const Pair_type& pair ) { return pair.first; } - static Value_type get_value( const Pair_type& pair ) + static const Value_type& get_value( const Pair_type& pair ) { return pair.second; } }; +#endif // typedefs for ASCII +#ifdef JSON_SPIRIT_MVALUE_ENABLED typedef Config_map< std::string > mConfig; typedef mConfig::Value_type mValue; typedef mConfig::Object_type mObject; typedef mConfig::Array_type mArray; +#endif // typedefs for Unicode -#ifndef BOOST_NO_STD_WSTRING - +#if defined( JSON_SPIRIT_WMVALUE_ENABLED ) && !defined( BOOST_NO_STD_WSTRING ) typedef Config_map< std::wstring > wmConfig; typedef wmConfig::Value_type wmValue; typedef wmConfig::Object_type wmObject; typedef wmConfig::Array_type wmArray; - #endif /////////////////////////////////////////////////////////////////////////////////////////////// // // implementation + inline bool operator==( const Null&, const Null& ) + { + return true; + } + template< class Config > const Value_impl< Config > Value_impl< Config >::null; template< class Config > Value_impl< Config >::Value_impl() - : type_( null_type ) - , is_uint64_( false ) + : v_( Null() ) { } template< class Config > Value_impl< Config >::Value_impl( const Const_str_ptr value ) - : type_( str_type ) - , v_( String_type( value ) ) - , is_uint64_( false ) + : v_( String_type( value ) ) { } template< class Config > Value_impl< Config >::Value_impl( const String_type& value ) - : type_( str_type ) - , v_( value ) - , is_uint64_( false ) + : v_( value ) { } template< class Config > Value_impl< Config >::Value_impl( const Object& value ) - : type_( obj_type ) - , v_( value ) - , is_uint64_( false ) + : v_( value ) { } template< class Config > Value_impl< Config >::Value_impl( const Array& value ) - : type_( array_type ) - , v_( value ) - , is_uint64_( false ) + : v_( value ) { } template< class Config > Value_impl< Config >::Value_impl( bool value ) - : type_( bool_type ) - , v_( value ) - , is_uint64_( false ) + : v_( value ) { } template< class Config > Value_impl< Config >::Value_impl( int value ) - : type_( int_type ) - , v_( static_cast< boost::int64_t >( value ) ) - , is_uint64_( false ) + : v_( static_cast< boost::int64_t >( value ) ) { } template< class Config > Value_impl< Config >::Value_impl( boost::int64_t value ) - : type_( int_type ) - , v_( value ) - , is_uint64_( false ) + : v_( value ) { } template< class Config > Value_impl< Config >::Value_impl( boost::uint64_t value ) - : type_( int_type ) - , v_( static_cast< boost::int64_t >( value ) ) - , is_uint64_( true ) + : v_( value ) { } template< class Config > Value_impl< Config >::Value_impl( double value ) - : type_( real_type ) - , v_( value ) - , is_uint64_( false ) + : v_( value ) { } template< class Config > Value_impl< Config >::Value_impl( const Value_impl< Config >& other ) - : type_( other.type() ) - , v_( other.v_ ) - , is_uint64_( other.is_uint64_ ) + : v_( other.v_ ) + { + } + + template< class Config > + template< class Iter > + Value_impl< Config >::Value_impl( Iter first, Iter last ) + : v_( Array( first, last ) ) + { + } + + template< class Config > + template< BOOST_VARIANT_ENUM_PARAMS( typename T ) > + Value_impl< Config >::Value_impl( const boost::variant< BOOST_VARIANT_ENUM_PARAMS(T) >& variant ) + : v_( boost::apply_visitor( Variant_converter_visitor(), variant) ) { } @@ -299,9 +341,7 @@ namespace json_spirit { Value_impl tmp( lhs ); - std::swap( type_, tmp.type_ ); std::swap( v_, tmp.v_ ); - std::swap( is_uint64_, tmp.is_uint64_ ); return *this; } @@ -319,13 +359,18 @@ namespace json_spirit template< class Config > Value_type Value_impl< Config >::type() const { - return type_; + if( is_uint64() ) + { + return int_type; + } + + return static_cast< Value_type >( v_.which() ); } template< class Config > bool Value_impl< Config >::is_uint64() const { - return is_uint64_; + return v_.which() == null_type + 1; } template< class Config > @@ -341,7 +386,7 @@ namespace json_spirit { std::ostringstream os; - os << "value type is " << type() << " not " << vtype; + os << "get_value< " << value_type_to_string( vtype ) << " > called on " << value_type_to_string( type() ) << " Value"; throw std::runtime_error( os.str() ); } @@ -350,7 +395,7 @@ namespace json_spirit template< class Config > const typename Config::String_type& Value_impl< Config >::get_str() const { - check_type( str_type ); + check_type( str_type ); return *boost::get< String_type >( &v_ ); } @@ -366,7 +411,7 @@ namespace json_spirit template< class Config > const typename Value_impl< Config >::Array& Value_impl< Config >::get_array() const { - check_type( array_type ); + check_type( array_type ); return *boost::get< Array >( &v_ ); } @@ -374,7 +419,7 @@ namespace json_spirit template< class Config > bool Value_impl< Config >::get_bool() const { - check_type( bool_type ); + check_type( bool_type ); return boost::get< bool >( v_ ); } @@ -382,7 +427,7 @@ namespace json_spirit template< class Config > int Value_impl< Config >::get_int() const { - check_type( int_type ); + check_type( int_type ); return static_cast< int >( get_int64() ); } @@ -390,7 +435,12 @@ namespace json_spirit template< class Config > boost::int64_t Value_impl< Config >::get_int64() const { - check_type( int_type ); + check_type( int_type ); + + if( is_uint64() ) + { + return static_cast< boost::int64_t >( get_uint64() ); + } return boost::get< boost::int64_t >( v_ ); } @@ -398,9 +448,14 @@ namespace json_spirit template< class Config > boost::uint64_t Value_impl< Config >::get_uint64() const { - check_type( int_type ); + check_type( int_type ); - return static_cast< boost::uint64_t >( get_int64() ); + if( !is_uint64() ) + { + return static_cast< boost::uint64_t >( get_int64() ); + } + + return boost::get< boost::uint64_t >( v_ ); } template< class Config > @@ -412,7 +467,7 @@ namespace json_spirit : static_cast< double >( get_int64() ); } - check_type( real_type ); + check_type( real_type ); return boost::get< double >( v_ ); } @@ -420,7 +475,7 @@ namespace json_spirit template< class Config > typename Value_impl< Config >::Object& Value_impl< Config >::get_obj() { - check_type( obj_type ); + check_type( obj_type ); return *boost::get< Object >( &v_ ); } @@ -428,7 +483,7 @@ namespace json_spirit template< class Config > typename Value_impl< Config >::Array& Value_impl< Config >::get_array() { - check_type( array_type ); + check_type( array_type ); return *boost::get< Array >( &v_ ); } @@ -527,6 +582,24 @@ namespace json_spirit { return internal_::get_value( *this, internal_::Type_to_type< T >() ); } + + static std::string value_type_to_string( const Value_type vtype ) + { + switch( vtype ) + { + case obj_type: return "Object"; + case array_type: return "Array"; + case str_type: return "string"; + case bool_type: return "boolean"; + case int_type: return "integer"; + case real_type: return "real"; + case null_type: return "null"; + } + + assert( false ); + + return "unknown type"; + } } #endif diff --git a/3rdparty/json_spirit/json_spirit_writer.cpp b/3rdparty/json_spirit/json_spirit_writer.cpp index f3367b68..24833ffc 100644 --- a/3rdparty/json_spirit/json_spirit_writer.cpp +++ b/3rdparty/json_spirit/json_spirit_writer.cpp @@ -1,95 +1,96 @@ -// Copyright John W. Wilkinson 2007 - 2009. +// Copyright John W. Wilkinson 2007 - 2014 // Distributed under the MIT License, see accompanying file LICENSE.txt -// json spirit version 4.03 +// json spirit version 4.08 #include "json_spirit_writer.h" #include "json_spirit_writer_template.h" -void json_spirit::write( const Value& value, std::ostream& os ) -{ - write_stream( value, os, false ); -} - -void json_spirit::write_formatted( const Value& value, std::ostream& os ) -{ - write_stream( value, os, true ); -} - -std::string json_spirit::write( const Value& value ) -{ - return write_string( value, false ); -} - -std::string json_spirit::write_formatted( const Value& value ) -{ - return write_string( value, true ); -} - -#ifndef BOOST_NO_STD_WSTRING - -void json_spirit::write( const wValue& value, std::wostream& os ) -{ - write_stream( value, os, false ); -} - -void json_spirit::write_formatted( const wValue& value, std::wostream& os ) -{ - write_stream( value, os, true ); -} - -std::wstring json_spirit::write( const wValue& value ) -{ - return write_string( value, false ); -} - -std::wstring json_spirit::write_formatted( const wValue& value ) -{ - return write_string( value, true ); -} - +using namespace json_spirit; + +#ifdef JSON_SPIRIT_VALUE_ENABLED + void json_spirit::write( const Value& value, std::ostream& os, int options, unsigned int precision_of_doubles ) + { + write_stream( value, os, options, precision_of_doubles ); + } + std::string json_spirit::write( const Value& value, int options, unsigned int precision_of_doubles ) + { + return write_string( value, options, precision_of_doubles ); + } + + void json_spirit::write_formatted( const Value& value, std::ostream& os, unsigned int precision_of_doubles ) + { + write_stream( value, os, pretty_print, precision_of_doubles ); + } + + std::string json_spirit::write_formatted( const Value& value, unsigned int precision_of_doubles ) + { + return write_string( value, pretty_print, precision_of_doubles ); + } #endif -void json_spirit::write( const mValue& value, std::ostream& os ) -{ - write_stream( value, os, false ); -} - -void json_spirit::write_formatted( const mValue& value, std::ostream& os ) -{ - write_stream( value, os, true ); -} - -std::string json_spirit::write( const mValue& value ) -{ - return write_string( value, false ); -} - -std::string json_spirit::write_formatted( const mValue& value ) -{ - return write_string( value, true ); -} - -#ifndef BOOST_NO_STD_WSTRING - -void json_spirit::write( const wmValue& value, std::wostream& os ) -{ - write_stream( value, os, false ); -} - -void json_spirit::write_formatted( const wmValue& value, std::wostream& os ) -{ - write_stream( value, os, true ); -} - -std::wstring json_spirit::write( const wmValue& value ) -{ - return write_string( value, false ); -} +#ifdef JSON_SPIRIT_MVALUE_ENABLED + void json_spirit::write( const mValue& value, std::ostream& os, int options, unsigned int precision_of_doubles ) + { + write_stream( value, os, options, precision_of_doubles ); + } + + std::string json_spirit::write( const mValue& value, int options, unsigned int precision_of_doubles ) + { + return write_string( value, options, precision_of_doubles ); + } + + void json_spirit::write_formatted( const mValue& value, std::ostream& os, unsigned int precision_of_doubles ) + { + write_stream( value, os, pretty_print, precision_of_doubles ); + } + + std::string json_spirit::write_formatted( const mValue& value, unsigned int precision_of_doubles ) + { + return write_string( value, pretty_print, precision_of_doubles ); + } +#endif -std::wstring json_spirit::write_formatted( const wmValue& value ) -{ - return write_string( value, true ); -} +#if defined( JSON_SPIRIT_WVALUE_ENABLED ) && !defined( BOOST_NO_STD_WSTRING ) + void json_spirit::write( const wValue& value, std::wostream& os, int options, unsigned int precision_of_doubles ) + { + write_stream( value, os, options, precision_of_doubles ); + } + + std::wstring json_spirit::write( const wValue& value, int options, unsigned int precision_of_doubles ) + { + return write_string( value, options, precision_of_doubles ); + } + + void json_spirit::write_formatted( const wValue& value, std::wostream& os, unsigned int precision_of_doubles ) + { + write_stream( value, os, pretty_print, precision_of_doubles ); + } + + std::wstring json_spirit::write_formatted( const wValue& value, unsigned int precision_of_doubles ) + { + return write_string( value, pretty_print, precision_of_doubles ); + } +#endif +#if defined( JSON_SPIRIT_WMVALUE_ENABLED ) && !defined( BOOST_NO_STD_WSTRING ) + void json_spirit::write_formatted( const wmValue& value, std::wostream& os, unsigned int precision_of_doubles ) + { + write_stream( value, os, pretty_print, precision_of_doubles ); + } + + std::wstring json_spirit::write_formatted( const wmValue& value, unsigned int precision_of_doubles ) + { + return write_string( value, pretty_print, precision_of_doubles ); + } + + void json_spirit::write( const wmValue& value, std::wostream& os, int options, unsigned int precision_of_doubles ) + { + write_stream( value, os, options, precision_of_doubles ); + } + + std::wstring json_spirit::write( const wmValue& value, int options, unsigned int precision_of_doubles ) + { + return write_string( value, options, precision_of_doubles ); + } #endif diff --git a/3rdparty/json_spirit/json_spirit_writer.h b/3rdparty/json_spirit/json_spirit_writer.h index 1b67b512..0b7a2374 100644 --- a/3rdparty/json_spirit/json_spirit_writer.h +++ b/3rdparty/json_spirit/json_spirit_writer.h @@ -1,49 +1,64 @@ #ifndef JSON_SPIRIT_WRITER #define JSON_SPIRIT_WRITER -// Copyright John W. Wilkinson 2007 - 2009. +// Copyright John W. Wilkinson 2007 - 2014 // Distributed under the MIT License, see accompanying file LICENSE.txt -// json spirit version 4.03 +// json spirit version 4.08 #if defined(_MSC_VER) && (_MSC_VER >= 1020) # pragma once #endif #include "json_spirit_value.h" +#include "json_spirit_writer_options.h" #include namespace json_spirit { - // functions to convert JSON Values to text, - // the "formatted" versions add whitespace to format the output nicely + // these functions to convert JSON Values to text + // note the precision used outputing doubles defaults to 17, + // unless the remove_trailing_zeros option is given in which case the default is 16 - void write ( const Value& value, std::ostream& os ); - void write_formatted( const Value& value, std::ostream& os ); - std::string write ( const Value& value ); - std::string write_formatted( const Value& value ); - -#ifndef BOOST_NO_STD_WSTRING - - void write ( const wValue& value, std::wostream& os ); - void write_formatted( const wValue& value, std::wostream& os ); - std::wstring write ( const wValue& value ); - std::wstring write_formatted( const wValue& value ); +#ifdef JSON_SPIRIT_VALUE_ENABLED + void write( const Value& value, std::ostream& os, int options = none, unsigned int precision_of_doubles = 0 ); + std::string write( const Value& value, int options = none, unsigned int precision_of_doubles = 0 ); +#endif +#ifdef JSON_SPIRIT_MVALUE_ENABLED + void write( const mValue& value, std::ostream& os, int options = none, unsigned int precision_of_doubles = 0 ); + std::string write( const mValue& value, int options = none, unsigned int precision_of_doubles = 0 ); #endif - void write ( const mValue& value, std::ostream& os ); - void write_formatted( const mValue& value, std::ostream& os ); - std::string write ( const mValue& value ); - std::string write_formatted( const mValue& value ); +#if defined( JSON_SPIRIT_WVALUE_ENABLED ) && !defined( BOOST_NO_STD_WSTRING ) + void write( const wValue& value, std::wostream& os, int options = none, unsigned int precision_of_doubles = 0 ); + std::wstring write( const wValue& value, int options = none, unsigned int precision_of_doubles = 0 ); +#endif -#ifndef BOOST_NO_STD_WSTRING +#if defined( JSON_SPIRIT_WMVALUE_ENABLED ) && !defined( BOOST_NO_STD_WSTRING ) + void write( const wmValue& value, std::wostream& os, int options = none, unsigned int precision_of_doubles = 0 ); + std::wstring write( const wmValue& value, int options = none, unsigned int precision_of_doubles = 0 ); +#endif - void write ( const wmValue& value, std::wostream& os ); - void write_formatted( const wmValue& value, std::wostream& os ); - std::wstring write ( const wmValue& value ); - std::wstring write_formatted( const wmValue& value ); + // these "formatted" versions of the "write" functions are the equivalent of the above functions + // with option "pretty_print" + +#ifdef JSON_SPIRIT_VALUE_ENABLED + void write_formatted( const Value& value, std::ostream& os, unsigned int precision_of_doubles = 0 ); + std::string write_formatted( const Value& value, unsigned int precision_of_doubles = 0 ); +#endif +#ifdef JSON_SPIRIT_MVALUE_ENABLED + void write_formatted( const mValue& value, std::ostream& os, unsigned int precision_of_doubles = 0 ); + std::string write_formatted( const mValue& value, unsigned int precision_of_doubles = 0 ); +#endif +#if defined( JSON_SPIRIT_WVALUE_ENABLED ) && !defined( BOOST_NO_STD_WSTRING ) + void write_formatted( const wValue& value, std::wostream& os, unsigned int precision_of_doubles = 0 ); + std::wstring write_formatted( const wValue& value, unsigned int precision_of_doubles = 0 ); +#endif +#if defined( JSON_SPIRIT_WMVALUE_ENABLED ) && !defined( BOOST_NO_STD_WSTRING ) + void write_formatted( const wmValue& value, std::wostream& os, unsigned int precision_of_doubles = 0 ); + std::wstring write_formatted( const wmValue& value, unsigned int precision_of_doubles = 0 ); #endif } diff --git a/3rdparty/json_spirit/json_spirit_writer_options.h b/3rdparty/json_spirit/json_spirit_writer_options.h new file mode 100644 index 00000000..f147a5a9 --- /dev/null +++ b/3rdparty/json_spirit/json_spirit_writer_options.h @@ -0,0 +1,35 @@ +#ifndef JSON_SPIRIT_WRITER_OPTIONS +#define JSON_SPIRIT_WRITER_OPTIONS + +// Copyright John W. Wilkinson 2007 - 2014 +// Distributed under the MIT License, see accompanying file LICENSE.txt + +// json spirit version 4.08 + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +# pragma once +#endif + +namespace json_spirit +{ + enum Output_options{ none = 0, // default options + + pretty_print = 0x01, // Add whitespace to format the output nicely. + + raw_utf8 = 0x02, // This prevents non-printable characters from being escapted using "\uNNNN" notation. + // Note, this is an extension to the JSON standard. It disables the escaping of + // non-printable characters allowing UTF-8 sequences held in 8 bit char strings + // to pass through unaltered. + + remove_trailing_zeros = 0x04, + // no longer used kept for backwards compatibility + single_line_arrays = 0x08, + // pretty printing except that arrays printed on single lines unless they contain + // composite elements, i.e. objects or arrays + always_escape_nonascii = 0x10, + // all unicode wide characters are escaped, i.e. outputed as "\uXXXX", even if they are + // printable under the current locale, ascii printable chars are not escaped + }; +} + +#endif diff --git a/3rdparty/json_spirit/json_spirit_writer_template.h b/3rdparty/json_spirit/json_spirit_writer_template.h index 87b528b9..35be8838 100644 --- a/3rdparty/json_spirit/json_spirit_writer_template.h +++ b/3rdparty/json_spirit/json_spirit_writer_template.h @@ -1,17 +1,23 @@ #ifndef JSON_SPIRIT_WRITER_TEMPLATE #define JSON_SPIRIT_WRITER_TEMPLATE -// Copyright John W. Wilkinson 2007 - 2009. +// Copyright John W. Wilkinson 2007 - 2014 // Distributed under the MIT License, see accompanying file LICENSE.txt -// json spirit version 4.03 +// json spirit version 4.08 + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +# pragma once +#endif #include "json_spirit_value.h" +#include "json_spirit_writer_options.h" #include #include #include #include +#include namespace json_spirit { @@ -61,7 +67,7 @@ namespace json_spirit } template< class String_type > - String_type add_esc_chars( const String_type& s ) + String_type add_esc_chars( const String_type& s, bool raw_utf8, bool esc_nonascii ) { typedef typename String_type::const_iterator Iter_type; typedef typename String_type::value_type Char_type; @@ -76,15 +82,22 @@ namespace json_spirit if( add_esc_char( c, result ) ) continue; - const wint_t unsigned_c( ( c >= 0 ) ? c : 256 + c ); - - if( std::iswprint( unsigned_c ) ) + if( raw_utf8 ) { result += c; } else { - result += non_printable_to_string< String_type >( unsigned_c ); + const wint_t unsigned_c( ( c >= 0 ) ? c : 256 + c ); + + if( !esc_nonascii && iswprint( unsigned_c ) ) + { + result += c; + } + else + { + result += non_printable_to_string< String_type >( unsigned_c ); + } } } @@ -106,11 +119,24 @@ namespace json_spirit public: - Generator( const Value_type& value, Ostream_type& os, bool pretty ) + Generator( const Value_type& value, Ostream_type& os, int options, unsigned int precision_of_doubles ) : os_( os ) , indentation_level_( 0 ) - , pretty_( pretty ) + , pretty_( ( options & pretty_print ) != 0 || ( options & single_line_arrays ) != 0 ) + , raw_utf8_( ( options & raw_utf8 ) != 0 ) + , esc_nonascii_( ( options & always_escape_nonascii ) != 0 ) + , single_line_arrays_( ( options & single_line_arrays ) != 0 ) + , ios_saver_( os ) { + if( precision_of_doubles > 0 ) + { + precision_of_doubles_ = precision_of_doubles; + } + else + { + precision_of_doubles_ = ( options & remove_trailing_zeros ) != 0 ? 16 : 17; + } + output( value ); } @@ -124,9 +150,8 @@ namespace json_spirit case array_type: output( value.get_array() ); break; case str_type: output( value.get_str() ); break; case bool_type: output( value.get_bool() ); break; + case real_type: output( value.get_real() ); break; case int_type: output_int( value ); break; - case real_type: os_ << std::showpoint << std::setprecision( 16 ) - << value.get_real(); break; case null_type: os_ << "null"; break; default: assert( false ); } @@ -137,11 +162,6 @@ namespace json_spirit output_array_or_obj( obj, '{', '}' ); } - void output( const Array_type& arr ) - { - output_array_or_obj( arr, '[', ']' ); - } - void output( const Obj_member_type& member ) { output( Config_type::get_name( member ) ); space(); @@ -163,7 +183,7 @@ namespace json_spirit void output( const String_type& s ) { - os_ << '"' << add_esc_chars( s ) << '"'; + os_ << '"' << add_esc_chars( s, raw_utf8_, esc_nonascii_ ) << '"'; } void output( bool b ) @@ -171,6 +191,59 @@ namespace json_spirit os_ << to_str< String_type >( b ? "true" : "false" ); } + void output( double d ) + { + os_ << std::setprecision( precision_of_doubles_ ) << d; + } + + static bool contains_composite_elements( const Array_type& arr ) + { + for( typename Array_type::const_iterator i = arr.begin(); i != arr.end(); ++i ) + { + const Value_type& val = *i; + + if( val.type() == obj_type || + val.type() == array_type ) + { + return true; + } + } + + return false; + } + + template< class Iter > + void output_composite_item( Iter i, Iter last ) + { + output( *i ); + + if( ++i != last ) + { + os_ << ','; + } + } + + void output( const Array_type& arr ) + { + if( single_line_arrays_ && !contains_composite_elements( arr ) ) + { + os_ << '['; space(); + + for( typename Array_type::const_iterator i = arr.begin(); i != arr.end(); ++i ) + { + output_composite_item( i, arr.end() ); + + space(); + } + + os_ << ']'; + } + else + { + output_array_or_obj( arr, '[', ']' ); + } + } + template< class T > void output_array_or_obj( const T& t, Char_type start_char, Char_type end_char ) { @@ -180,14 +253,9 @@ namespace json_spirit for( typename T::const_iterator i = t.begin(); i != t.end(); ++i ) { - indent(); output( *i ); + indent(); - typename T::const_iterator next = i; - - if( ++next != t.end()) - { - os_ << ','; - } + output_composite_item( i, t.end() ); new_line(); } @@ -222,22 +290,36 @@ namespace json_spirit Ostream_type& os_; int indentation_level_; bool pretty_; + bool raw_utf8_; + bool esc_nonascii_; + bool single_line_arrays_; + int precision_of_doubles_; + boost::io::basic_ios_all_saver< Char_type > ios_saver_; // so that ostream state is reset after control is returned to the caller }; + // writes JSON Value to a stream, e.g. + // + // write_stream( value, os, pretty_print ); + // template< class Value_type, class Ostream_type > - void write_stream( const Value_type& value, Ostream_type& os, bool pretty ) + void write_stream( const Value_type& value, Ostream_type& os, int options = none, unsigned int precision_of_doubles = 0 ) { - Generator< Value_type, Ostream_type >( value, os, pretty ); + os << std::dec; + Generator< Value_type, Ostream_type >( value, os, options, precision_of_doubles ); } + // writes JSON Value to a stream, e.g. + // + // const string json_str = write( value, pretty_print ); + // template< class Value_type > - typename Value_type::String_type write_string( const Value_type& value, bool pretty ) + typename Value_type::String_type write_string( const Value_type& value, int options = none, unsigned int precision_of_doubles = 0 ) { typedef typename Value_type::String_type::value_type Char_type; std::basic_ostringstream< Char_type > os; - write_stream( value, os, pretty ); + write_stream( value, os, options, precision_of_doubles ); return os.str(); } diff --git a/travis.sh b/travis.sh index ebfdf0bc..92a785df 100755 --- a/travis.sh +++ b/travis.sh @@ -40,7 +40,7 @@ if [ -n "${FORMAT:-}" ]; then fi git clang-format-3.8 --binary=/usr/bin/clang-format-3.8 --style=file --commit="${BASE_HEAD}" # Assert that all changes adhere to the asked for style - exec git diff --exit-code + exec git diff --exit-code -- . ":!3rdparty" fi CTEST_OUTPUT_ON_FAILURE=ON From 581b3c82e40fa369910dc025c814ab29db33b5d4 Mon Sep 17 00:00:00 2001 From: src Date: Wed, 12 Jun 2019 17:58:37 +0200 Subject: [PATCH 4/7] WireResponseEncoder::encode now outputs raw utf8 by default. This modifies the WireResponseEncoder to always use the raw_utf8 option provided by the new version of json_spirit. According to the IETF RFC8259: "JSON text exchanged between systems that are not part of a closed ecosystem MUST be encoded using UTF-8 [RFC3629]." https://tools.ietf.org/html/rfc8259 --- src/connectors/wire/WireProtocol.cpp | 3 ++- tests/integration/WireProtocolTest.cpp | 16 +--------------- 2 files changed, 3 insertions(+), 16 deletions(-) diff --git a/src/connectors/wire/WireProtocol.cpp b/src/connectors/wire/WireProtocol.cpp index ca9c0394..f07a2e11 100644 --- a/src/connectors/wire/WireProtocol.cpp +++ b/src/connectors/wire/WireProtocol.cpp @@ -3,6 +3,7 @@ #include #include +#include #include #include @@ -231,7 +232,7 @@ namespace { jsonOutput.clear(); response.accept(*this); const mValue v(jsonOutput); - return write_string(v, false); + return write_string(v, ::raw_utf8); } void visit(const SuccessResponse& /*response*/) { diff --git a/tests/integration/WireProtocolTest.cpp b/tests/integration/WireProtocolTest.cpp index 718bc199..45ae7ffa 100644 --- a/tests/integration/WireProtocolTest.cpp +++ b/tests/integration/WireProtocolTest.cpp @@ -273,25 +273,11 @@ TEST_F(WireMessageCodecTest, encodesResponseUsingRawUtf8) { StepMatchesResponse response(matches); // clang-format off - // EXPECTED: - // EXPECT_THAT(codec.encode(response), StrEq( - // "[\"success\",[{" - // "\"args\":[{" - // "\"pos\":5," - // "\"val\":\"カラオケ機\"" - // "},{" - // "\"pos\":18," - // "\"val\":\"ASCII\"" - // "}]," - // "\"id\":\"1234\"," - // "\"regexp\":\"Some (.+) regexp (.+)\"" - // "}]]")); - // ACTUAL: EXPECT_THAT(codec.encode(response), StrEq( "[\"success\",[{" "\"args\":[{" "\"pos\":5," - "\"val\":\"\\u00E3\\u0082\\u00AB\\u00E3\\u0083\\u00A9\\u00E3\\u0082\\u00AA\\u00E3\\u0082\\u00B1\\u00E6\\u00A9\\u009F\"" + "\"val\":\"カラオケ機\"" "},{" "\"pos\":18," "\"val\":\"ASCII\"" From 1c0a1bdf6097dc95970fc2afd490699c6ae7d7f5 Mon Sep 17 00:00:00 2001 From: src Date: Thu, 13 Jun 2019 13:55:35 +0200 Subject: [PATCH 5/7] Recalculate position so it is based on the index of unicode codepoints. `cucumber-ruby` expects position values which are based on the index of the codepoint instead of the index of the code unit. This change modifies the value returned to `cucumber-ruby`. Prior to this change, the RegexSubMatch's position, which was correct in terms of a code unit array, would cause an `index out of string` error and crash cucumber-ruby when pretty-printing the results of a test. This commit also ammends the added tests to demonstrate the corrected behavior. --- features/specific/wire_encoding.feature | 8 +------- src/Regex.cpp | 17 +++++++++++++++-- tests/unit/RegexTest.cpp | 5 +---- 3 files changed, 17 insertions(+), 13 deletions(-) diff --git a/features/specific/wire_encoding.feature b/features/specific/wire_encoding.feature index b9014d80..27effcbf 100644 --- a/features/specific/wire_encoding.feature +++ b/features/specific/wire_encoding.feature @@ -24,13 +24,7 @@ Feature: Wire Encoding Feature """ When Cucumber runs the feature Then the step output should contain: - # EXPECTED - # """ - # カラオケ機ASCII - # - # """ - # ACTUAL """ - \\u00E3\\u0082\\u00AB\\u00E3\\u0083\\u00A9\\u00E3\\u0082\\u00AA\\u00E3\\u0082\\u00B1\\u00E6\\u00A9\\u009FASCII + カラオケ機ASCII """ diff --git a/src/Regex.cpp b/src/Regex.cpp index 3ff47970..8e0cb268 100644 --- a/src/Regex.cpp +++ b/src/Regex.cpp @@ -1,6 +1,8 @@ #include #include +#include + namespace cucumber { namespace internal { @@ -24,7 +26,18 @@ boost::shared_ptr Regex::find(const std::string &expression) const { return boost::make_shared(regexImpl, expression); } -FindRegexMatch::FindRegexMatch(const boost::regex ®exImpl, const std::string &expression) { +namespace { +bool isUtf8CodeUnitStartOfCodepoint(unsigned int i) { + return (i & 0xc0) != 0x80; +} + +std::ptrdiff_t utf8CodepointOffset(const std::string& expression, + const std::string::const_iterator& it) { + return count_if(expression.begin(), it, &isUtf8CodeUnitStartOfCodepoint); +} +} // namespace + +FindRegexMatch::FindRegexMatch(const boost::regex& regexImpl, const std::string& expression) { boost::smatch matchResults; regexMatched = boost::regex_search( expression, matchResults, regexImpl, boost::regex_constants::match_extra); @@ -35,7 +48,7 @@ FindRegexMatch::FindRegexMatch(const boost::regex ®exImpl, const std::string ++i; for (; i != matchResults.end(); ++i) { if (i->matched) { - RegexSubmatch s = {*i, i->first - expression.begin()}; + RegexSubmatch s = {*i, utf8CodepointOffset(expression, i->first)}; submatches.push_back(s); } else { submatches.push_back(RegexSubmatch()); diff --git a/tests/unit/RegexTest.cpp b/tests/unit/RegexTest.cpp index e29b9a7d..f1d8844f 100644 --- a/tests/unit/RegexTest.cpp +++ b/tests/unit/RegexTest.cpp @@ -77,10 +77,7 @@ TEST(RegexTest, findReportsCodepointPositions) { EXPECT_TRUE(match->matches()); ASSERT_EQ(2, match->getSubmatches().size()); EXPECT_EQ(5, match->getSubmatches()[0].position); - // EXPECTED: - // EXPECT_EQ(18, match->getSubmatches()[1].position); - // ACTUAL - EXPECT_EQ(28, match->getSubmatches()[1].position); + EXPECT_EQ(18, match->getSubmatches()[1].position); } TEST(RegexTest, findAllExtractsTheFirstGroupOfEveryToken) { From 3140b313c41d62ce9dbfecd59ce488757732fd8f Mon Sep 17 00:00:00 2001 From: Matt Wynne Date: Mon, 7 Jun 2021 14:49:55 -0700 Subject: [PATCH 6/7] See if this fixes the travis build --- src/drivers/BoostDriver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/drivers/BoostDriver.cpp b/src/drivers/BoostDriver.cpp index 315b49a3..dd5c93db 100644 --- a/src/drivers/BoostDriver.cpp +++ b/src/drivers/BoostDriver.cpp @@ -51,7 +51,7 @@ class CukeBoostLogInterceptor : public ::boost::unit_test::unit_test_log_formatt void log_start( std::ostream&, counter_t /*test_cases_amount*/) {}; void log_finish( std::ostream&) {}; #if BOOST_VERSION >= 107000 - void log_build_info( std::ostream&, bool /*log_build_info*/) {}; + void log_build_info(std::ostream&, bool /*log_build_info*/){}; #else void log_build_info( std::ostream&) {}; #endif From 26c7417b347c5139c8ac3f73ac8690bf2df4256c Mon Sep 17 00:00:00 2001 From: Jeroen Kouwer <70279052+jermus67@users.noreply.github.com> Date: Wed, 4 Aug 2021 18:30:12 +0200 Subject: [PATCH 7/7] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 95510d63..37d760f8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,8 @@ Please see [CONTRIBUTING.md](https://github.com/cucumber/cucumber/blob/master/CO * Unable to `add_subdirectory(cucumber-cpp)` ([#211](https://github.com/cucumber/cucumber-cpp/pull/211) Sergey Bon) * Warning C4265 on Visual Studio ([#195](https://github.com/cucumber/cucumber-cpp/pull/195) Matthieu Longo) * Fix handling of optional regex captures ([#221](https://github.com/cucumber/cucumber-cpp/pull/221) Alain Martin) +* Fix compilation with Boost 1.70.0 ([#225](https://github.com/cucumber/cucumber-cpp/pull/225) Krystian Młynarczyk) +* Support step definitions with multi-byte characters ([#224](https://github.com/cucumber/cucumber-cpp/pull/224) Spencer Rudnick) ## [0.5](https://github.com/cucumber/cucumber-cpp/compare/v0.4...v0.5) (2 July 2018)