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