diff --git a/doc/qbk/0.main.qbk b/doc/qbk/0.main.qbk index dcc96bfd..e4638c1b 100644 --- a/doc/qbk/0.main.qbk +++ b/doc/qbk/0.main.qbk @@ -52,6 +52,8 @@ [def __Rule1__ [link url.concepts.rule ['Rule1]]] [def __Rule2__ [link url.concepts.rule ['Rule2]]] [def __Rules__ [link url.concepts.rule ['Rules]]] +[def __StringToken__ [link url.concepts.stringtoken ['StringToken]]] +[def __deduced__ [link url.concepts.stringtoken ['DEDUCED]]] [def __std_swap__ [@https://en.cppreference.com/w/cpp/algorithm/swap `std::swap`]] [def __std_string__ [@https://en.cppreference.com/w/cpp/string/basic_string `std::string`]] diff --git a/doc/qbk/5.0.concepts.qbk b/doc/qbk/5.0.concepts.qbk index 525ee38c..482e2e67 100644 --- a/doc/qbk/5.0.concepts.qbk +++ b/doc/qbk/5.0.concepts.qbk @@ -14,5 +14,6 @@ This section describes all of the concepts defined by the library. [include 5.1.CharSet.qbk] [include 5.2.MutableString.qbk] [include 5.3.Rule.qbk] +[include 5.4.StringToken.qbk] [endsect] diff --git a/doc/qbk/5.4.StringToken.qbk b/doc/qbk/5.4.StringToken.qbk new file mode 100644 index 00000000..34f78e6b --- /dev/null +++ b/doc/qbk/5.4.StringToken.qbk @@ -0,0 +1,14 @@ +[/ + Copyright (c) 2022 Vinnie Falco (vinnie.falco@gmail.com) + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + + Official repository: https://github.com/CPPAlliance/url +] + +[section StringToken] + +A ['StringToken]... + +[endsect] diff --git a/doc/qbk/quickref.xml b/doc/qbk/quickref.xml index eec54b06..0b6af7b5 100644 --- a/doc/qbk/quickref.xml +++ b/doc/qbk/quickref.xml @@ -62,16 +62,16 @@ params_view params_encoded_view pct_string_view - segments - segments_encoded + segments_const_view + segments_const_encoded_view segments_encoded_view + segments_view Types (2/2) - segments_view static_url url url_view @@ -199,6 +199,7 @@ is_charset is_mutable_string is_rule + is_string_token @@ -217,6 +218,15 @@ unsigned_rule + StringToken + + arg + append_to + assign_to + preserve_size + returned + + diff --git a/doc/xsl/custom-overrides.xsl b/doc/xsl/custom-overrides.xsl index 869431d5..8f101294 100644 --- a/doc/xsl/custom-overrides.xsl +++ b/doc/xsl/custom-overrides.xsl @@ -27,7 +27,8 @@ 'Allocator', 'CharSet', 'MutableString', - 'Rule', 'Rule1', 'Rule2', 'Rules' + 'Rule', 'Rule1', 'Rule2', 'Rules', + 'StringToken' "/> diff --git a/include/boost/url/detail/config.hpp b/include/boost/url/detail/config.hpp index cf9e294f..4eff1214 100644 --- a/include/boost/url/detail/config.hpp +++ b/include/boost/url/detail/config.hpp @@ -73,6 +73,16 @@ # define BOOST_URL_POS BOOST_CURRENT_LOCATION #endif +#ifndef BOOST_URL_STRTOK_TPARAM +#define BOOST_URL_STRTOK_TPARAM(T) class T = string_token::return_string +#endif +#ifndef BOOST_URL_STRTOK_RETURN +#define BOOST_URL_STRTOK_RETURN(T) typename T::result_type +#endif +#ifndef BOOST_URL_STRTOK_ARG +#define BOOST_URL_STRTOK_ARG(T, name) T&& name = {} +#endif + #ifndef BOOST_URL_STACK_BYTES #define BOOST_URL_STACK_BYTES 4096 #endif diff --git a/include/boost/url/grammar.hpp b/include/boost/url/grammar.hpp index 18b01306..f576434a 100644 --- a/include/boost/url/grammar.hpp +++ b/include/boost/url/grammar.hpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include diff --git a/include/boost/url/grammar/range_rule.hpp b/include/boost/url/grammar/range_rule.hpp index b595e838..7fa12f6b 100644 --- a/include/boost/url/grammar/range_rule.hpp +++ b/include/boost/url/grammar/range_rule.hpp @@ -38,6 +38,18 @@ namespace grammar { the lifetime of the buffer extends until it is no longer referenced by the range. + @note + + The implementation may use temporary, + recycled storage for type-erasure. Objects + of type `range` are intended to be used + ephemerally. That is, for short durations + such as within a function scope. If it is + necessary to store the range for a long + period of time or with static storage + duration, it is necessary to copy the + contents to an object of a different type. + @tparam T The value type of the range @see diff --git a/include/boost/url/grammar/string_token.hpp b/include/boost/url/grammar/string_token.hpp new file mode 100644 index 00000000..5a19914f --- /dev/null +++ b/include/boost/url/grammar/string_token.hpp @@ -0,0 +1,348 @@ +// +// Copyright (c) 2021 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/CPPAlliance/url +// + +#ifndef BOOST_URL_GRAMMAR_STRING_TOKEN_HPP +#define BOOST_URL_GRAMMAR_STRING_TOKEN_HPP + +#include +#include +#include +#include +#include +#include + +namespace boost { +namespace urls { + +namespace grammar { +namespace string_token { + +/** Base class for string tokens, and algorithm parameters + + This abstract interface provides a means + for an algorithm to generically obtain a + modifiable, contiguous character buffer + of prescribed size. As the author of an + algorithm simply declare an rvalue + reference as a parameter type. + +
+ + Instances of this type are intended only + to be used once and then destroyed. + + @par Example + The declared function will accept any + temporary instance of `arg` to be + used for writing: + @code + void algorithm( string_token::arg&& dest ); + @endcode + + To implement the interface for your type + or use-case, derive from the class and + implement the prepare function. +*/ +struct arg +{ + /** Return a modifiable character buffer + + This function attempts to obtain a + character buffer with space for at + least `n` characters. Upon success, + a pointer to the beginning of the + buffer is returned. Ownership is not + transferred; the caller should not + attempt to free the storage. The + buffer shall remain valid until + `this` is destroyed. + + @note + This function may only be called once. + After invoking the function, the only + valid operation is destruction. + */ + virtual char* prepare(std::size_t n) = 0; + + // prevent misuse + arg() = default; + arg(arg&&) = default; + arg(arg const&) = delete; + arg& operator=(arg&&) = delete; + arg& operator=(arg const&) = delete; +}; + +//------------------------------------------------ + +/** Metafunction returning true if T is a StringToken +*/ +#ifdef BOOST_URL_DOCS +template +using is_token = __see_below__; +#else +template +struct is_token : std::false_type {}; + +template +struct is_token().prepare( + std::declval())), + decltype(std::declval().result()) + > > : std::integral_constant().result()), + typename T::result_type>::value && + std::is_same().prepare(0)), + char*>::value && + std::is_base_of::value && + std::is_convertible::value + > +{ +}; +#endif + +//------------------------------------------------ + +/** A token for returning a plain string +*/ +#ifdef BOOST_URL_DOCS +using return_string = __implementation_defined__; +#else +struct return_string + : arg +{ + using result_type = std::string; + + char* + prepare(std::size_t n) override + { + s_.resize(n); + return &s_[0]; + } + + result_type + result() noexcept + { + return std::move(s_); + } + +private: + result_type s_; +}; +#endif + +//------------------------------------------------ + +/** A token for appending to a plain string +*/ +#ifdef BOOST_URL_DOCS +template< + class Allocator = + std::allocator> +__implementation_defined__ +append_to( + std::basic_string< + char, + std::char_traits, + Allocator>& s); +#else +template +struct append_to_t + : arg +{ + using string_type = std::basic_string< + char, std::char_traits, + Alloc>; + + using result_type = string_type&; + + explicit + append_to_t( + string_type& s) noexcept + : s_(s) + { + } + + char* + prepare(std::size_t n) override + { + std::size_t n0 = s_.size(); + if(n > s_.max_size() - n0) + urls::detail::throw_bad_alloc(); + s_.resize(n0 + n); + return &s_[n0]; + } + + result_type + result() noexcept + { + return s_; + } + +private: + string_type& s_; +}; + +template< + class Alloc = + std::allocator> +append_to_t +append_to( + std::basic_string< + char, + std::char_traits, + Alloc>& s) +{ + return append_to_t(s); +} +#endif + +//------------------------------------------------ + +/** A token for assigning to a plain string +*/ +#ifdef BOOST_URL_DOCS +template< + class Allocator = + std::allocator> +__implementation_defined__ +assign( + std::basic_string< + char, + std::char_traits, + Allocator>& s); +#else +template +struct assign_to_t + : arg +{ + using string_type = std::basic_string< + char, std::char_traits, + Alloc>; + + using result_type = string_type&; + + explicit + assign_to_t( + string_type& s) noexcept + : s_(s) + { + } + + char* + prepare(std::size_t n) override + { + s_.resize(n); + return &s_[0]; + } + + result_type + result() noexcept + { + return s_; + } + +private: + string_type& s_; +}; + +template< + class Alloc = + std::allocator> +assign_to_t +assign_to( + std::basic_string< + char, + std::char_traits, + Alloc>& s) +{ + return assign_to_t(s); +} +#endif + +//------------------------------------------------ + +/** A token for producing a durable string_view from a temporary string +*/ +#ifdef BOOST_URL_DOCS +template< + class Allocator = + std::allocator> +__implementation_defined__ +preserve_size( + std::basic_string< + char, + std::char_traits, + Allocator>& s); +#else +template +struct preserve_size_t + : arg +{ + using result_type = string_view; + + using string_type = std::basic_string< + char, std::char_traits, + Alloc>; + + explicit + preserve_size_t( + string_type& s) noexcept + : s_(s) + { + } + + char* + prepare(std::size_t n) override + { + n_ = n; + // preserve size() to + // avoid value-init + if(s_.size() < n) + s_.resize(n); + return &s_[0]; + } + + result_type + result() noexcept + { + return string_view( + s_.data(), n_); + } + +private: + string_type& s_; + std::size_t n_ = 0; +}; + +template< + class Alloc = + std::allocator> +preserve_size_t +preserve_size( + std::basic_string< + char, + std::char_traits, + Alloc>& s) +{ + return preserve_size_t(s); +} +#endif + +} // string_token +} // grammar + +namespace string_token = grammar::string_token; + +} // urls +} // boost + +#endif diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt index 08a05361..4edd21ce 100644 --- a/test/unit/CMakeLists.txt +++ b/test/unit/CMakeLists.txt @@ -71,6 +71,7 @@ set(BOOST_URL_TESTS_FILES grammar/parse.cpp grammar/range_rule.cpp grammar/recycled.cpp + grammar/string_token.cpp grammar/token_rule.cpp grammar/tuple_rule.cpp grammar/type_traits.cpp diff --git a/test/unit/Jamfile b/test/unit/Jamfile index 6fef624e..e8f5d24a 100644 --- a/test/unit/Jamfile +++ b/test/unit/Jamfile @@ -73,6 +73,7 @@ local SOURCES = grammar/parse.cpp grammar/range_rule.cpp grammar/recycled.cpp + grammar/string_token.cpp grammar/token_rule.cpp grammar/tuple_rule.cpp grammar/type_traits.cpp diff --git a/test/unit/grammar/string_token.cpp b/test/unit/grammar/string_token.cpp new file mode 100644 index 00000000..0cf93488 --- /dev/null +++ b/test/unit/grammar/string_token.cpp @@ -0,0 +1,87 @@ +// +// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/CPPAlliance/url +// + +// Test that header file is self-contained. +#include + +#include "test_suite.hpp" + +namespace boost { +namespace urls { +namespace grammar { + +struct string_token_test +{ + void + f_impl( + string_token::arg& dest, + string_view v) + { + char* p = dest.prepare(v.size()); + v.copy(p, v.size()); + } + + template< + class StringToken = string_token::return_string> + typename StringToken::result_type + f( StringToken&& st = {}, + string_view v = "test") + { + f_impl(st, v); + return st.result(); + } + + void + run() + { + // return_string + { + BOOST_TEST_EQ(f(), "test"); + } + + // append_string + { + std::string s("url"); + std::string& r(f(string_token::append_to(s))); + BOOST_TEST_EQ(r, "urltest"); + } + + // assign_string + { + std::string s("url"); + std::string& r(f(string_token::assign_to(s))); + BOOST_TEST_EQ(r, "test"); + } + + // temp_string + { + std::string s("url"); + string_view sv; + + sv = f(string_token::preserve_size(s)); + BOOST_TEST_EQ(sv, "test"); + + sv = f(string_token::preserve_size(s), + "supercalifragilisticexpialidocious" ); + BOOST_TEST_EQ(sv, + "supercalifragilisticexpialidocious"); + + sv = f(string_token::preserve_size(s)); + BOOST_TEST_EQ(sv, "test"); + } + } +}; + +TEST_SUITE( + string_token_test, + "boost.url.grammar.string_token"); + +} // grammar +} // urls +} // boost