1  
//
1  
//
2  
// Copyright (c) 2022 Alan de Freitas (alandefreitas@gmail.com)
2  
// Copyright (c) 2022 Alan de Freitas (alandefreitas@gmail.com)
3  
//
3  
//
4  
// Distributed under the Boost Software License, Version 1.0. (See accompanying
4  
// Distributed under the Boost Software License, Version 1.0. (See accompanying
5  
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
5  
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6  
//
6  
//
7  
// Official repository: https://github.com/boostorg/url
7  
// Official repository: https://github.com/boostorg/url
8  
//
8  
//
9  

9  

10  

10  

11  
#include <boost/url/detail/config.hpp>
11  
#include <boost/url/detail/config.hpp>
12  
#include "pct_format.hpp"
12  
#include "pct_format.hpp"
13  
#include <boost/url/grammar/parse.hpp>
13  
#include <boost/url/grammar/parse.hpp>
14  
#include <boost/url/grammar/unsigned_rule.hpp>
14  
#include <boost/url/grammar/unsigned_rule.hpp>
15  

15  

16  
namespace boost {
16  
namespace boost {
17  
namespace urls {
17  
namespace urls {
18  
namespace detail {
18  
namespace detail {
19  

19  

20  
std::size_t
20  
std::size_t
21  
pct_vmeasure(
21  
pct_vmeasure(
22  
    grammar::lut_chars const& cs,
22  
    grammar::lut_chars const& cs,
23  
    format_parse_context& pctx,
23  
    format_parse_context& pctx,
24  
    measure_context& mctx)
24  
    measure_context& mctx)
25  
{
25  
{
26  
    auto it0 = pctx.begin();
26  
    auto it0 = pctx.begin();
27  
    auto end = pctx.end();
27  
    auto end = pctx.end();
28  
    while( it0 != end )
28  
    while( it0 != end )
29  
    {
29  
    {
30  
        // look for replacement id
30  
        // look for replacement id
31  
        char const* it1 = it0;
31  
        char const* it1 = it0;
32  
        while(
32  
        while(
33  
            it1 != end &&
33  
            it1 != end &&
34  
            *it1 != '{' )
34  
            *it1 != '{' )
35  
        {
35  
        {
36  
            ++it1;
36  
            ++it1;
37  
        }
37  
        }
38  

38  

39  
        // output literal prefix
39  
        // output literal prefix
40  
        if( it0 != it1 )
40  
        if( it0 != it1 )
41  
        {
41  
        {
42  
            for (char const* i = it0; i != it1; ++i)
42  
            for (char const* i = it0; i != it1; ++i)
43  
                mctx.advance_to( mctx.out() + measure_one(*i, cs));
43  
                mctx.advance_to( mctx.out() + measure_one(*i, cs));
44  
        }
44  
        }
45  

45  

46  
        // over
46  
        // over
47  
        if( it1 == end )
47  
        if( it1 == end )
48  
        {
48  
        {
49  
            break;
49  
            break;
50  
        }
50  
        }
51  

51  

52  
        // enter replacement id
52  
        // enter replacement id
53  
        ++it1;
53  
        ++it1;
54  
        BOOST_ASSERT(it1 != end);
54  
        BOOST_ASSERT(it1 != end);
55  

55  

56  
        // handle escaped replacement (second '{')
56  
        // handle escaped replacement (second '{')
57  
        // there's no "{{" in URL templates because
57  
        // there's no "{{" in URL templates because
58  
        // '{'s are not allowed in URLs
58  
        // '{'s are not allowed in URLs
59  
        BOOST_ASSERT(*it1 != '{');
59  
        BOOST_ASSERT(*it1 != '{');
60  
        /*
60  
        /*
61  
        if( *it1 == '{' )
61  
        if( *it1 == '{' )
62  
        {
62  
        {
63  
            mctx.advance_to( mctx.out() + measure_one('{', cs));
63  
            mctx.advance_to( mctx.out() + measure_one('{', cs));
64  
            ++it1;
64  
            ++it1;
65  
            // this was not a real replacement,
65  
            // this was not a real replacement,
66  
            // so we just keep moving
66  
            // so we just keep moving
67  
            continue;
67  
            continue;
68  
        }
68  
        }
69  
        */
69  
        */
70  

70  

71  

71  

72  
        // parse {id} or {id:specs}
72  
        // parse {id} or {id:specs}
73  
        char const* id_start = it1;
73  
        char const* id_start = it1;
74  
        while (it1 != end &&
74  
        while (it1 != end &&
75  
               *it1 != ':' &&
75  
               *it1 != ':' &&
76  
               *it1 != '}')
76  
               *it1 != '}')
77  
        {
77  
        {
78  
            ++it1;
78  
            ++it1;
79  
        }
79  
        }
80  
        core::string_view id(id_start, it1);
80  
        core::string_view id(id_start, it1);
81  

81  

82  
        // move to specs start
82  
        // move to specs start
83  
        if (it1 != end &&
83  
        if (it1 != end &&
84  
            *it1 == ':')
84  
            *it1 == ':')
85  
            ++it1;
85  
            ++it1;
86  
        pctx.advance_to( it1 );
86  
        pctx.advance_to( it1 );
87  

87  

88  
        // get format_arg to use
88  
        // get format_arg to use
89  
        auto idv = grammar::parse(
89  
        auto idv = grammar::parse(
90  
            id, grammar::unsigned_rule<std::size_t>{});
90  
            id, grammar::unsigned_rule<std::size_t>{});
91  
        if (idv)
91  
        if (idv)
92  
        {
92  
        {
93  
            mctx.arg( *idv ).measure( pctx, mctx, cs );
93  
            mctx.arg( *idv ).measure( pctx, mctx, cs );
94  
        }
94  
        }
95  
        else if (!id.empty())
95  
        else if (!id.empty())
96  
        {
96  
        {
97  
            mctx.arg( id ).measure( pctx, mctx, cs );
97  
            mctx.arg( id ).measure( pctx, mctx, cs );
98  
        }
98  
        }
99  
        else
99  
        else
100  
        {
100  
        {
101  
            std::size_t arg_id = pctx.next_arg_id();
101  
            std::size_t arg_id = pctx.next_arg_id();
102  
            mctx.arg( arg_id ).measure( pctx, mctx, cs );
102  
            mctx.arg( arg_id ).measure( pctx, mctx, cs );
103  
        }
103  
        }
104  

104  

105  

105  

106  
        it1 = pctx.begin();
106  
        it1 = pctx.begin();
107  
        BOOST_ASSERT(*it1 == '}');
107  
        BOOST_ASSERT(*it1 == '}');
108  
        it0 = it1 + 1;
108  
        it0 = it1 + 1;
109  
    }
109  
    }
110  

110  

111  
    return mctx.out();
111  
    return mctx.out();
112  
}
112  
}
113  

113  

114  
char*
114  
char*
115  
pct_vformat(
115  
pct_vformat(
116  
    grammar::lut_chars const& cs,
116  
    grammar::lut_chars const& cs,
117  
    format_parse_context& pctx,
117  
    format_parse_context& pctx,
118  
    format_context& fctx)
118  
    format_context& fctx)
119  
{
119  
{
120  
    auto it0 = pctx.begin();
120  
    auto it0 = pctx.begin();
121  
    auto end = pctx.end();
121  
    auto end = pctx.end();
122  
    while( it0 != end )
122  
    while( it0 != end )
123  
    {
123  
    {
124  
        // look for replacement id
124  
        // look for replacement id
125  
        char const* it1 = it0;
125  
        char const* it1 = it0;
126  
        while(
126  
        while(
127  
            it1 != end &&
127  
            it1 != end &&
128  
            *it1 != '{' )
128  
            *it1 != '{' )
129  
        {
129  
        {
130  
            ++it1;
130  
            ++it1;
131  
        }
131  
        }
132  

132  

133  
        // output literal prefix
133  
        // output literal prefix
134  
        if( it0 != it1 )
134  
        if( it0 != it1 )
135  
        {
135  
        {
136  
            for (char const* i = it0; i != it1; ++i)
136  
            for (char const* i = it0; i != it1; ++i)
137  
            {
137  
            {
138  
                char* o = fctx.out();
138  
                char* o = fctx.out();
139  
                encode_one(o, *i, cs);
139  
                encode_one(o, *i, cs);
140  
                fctx.advance_to(o);
140  
                fctx.advance_to(o);
141  
            }
141  
            }
142  
        }
142  
        }
143  

143  

144  
        // over
144  
        // over
145  
        if( it1 == end )
145  
        if( it1 == end )
146  
        {
146  
        {
147  
            break;
147  
            break;
148  
        }
148  
        }
149  

149  

150  
        // enter replacement id
150  
        // enter replacement id
151  
        ++it1;
151  
        ++it1;
152  
        BOOST_ASSERT(it1 != end);
152  
        BOOST_ASSERT(it1 != end);
153  

153  

154  
        // handle escaped replacement (second '{')
154  
        // handle escaped replacement (second '{')
155  
        // there's no "{{" in URL templates because
155  
        // there's no "{{" in URL templates because
156  
        // '{'s are not allowed in URLs
156  
        // '{'s are not allowed in URLs
157  
        BOOST_ASSERT(*it1 != '{');
157  
        BOOST_ASSERT(*it1 != '{');
158  
        /*
158  
        /*
159  
        if( *it1 == '{' )
159  
        if( *it1 == '{' )
160  
        {
160  
        {
161  
            char* o = fctx.out();
161  
            char* o = fctx.out();
162  
            encode_one(o, '{', cs);
162  
            encode_one(o, '{', cs);
163  
            fctx.advance_to(o);
163  
            fctx.advance_to(o);
164  
            ++it1;
164  
            ++it1;
165  
            // this was not a real replacement,
165  
            // this was not a real replacement,
166  
            // so we just keep moving
166  
            // so we just keep moving
167  
            continue;
167  
            continue;
168  
        }
168  
        }
169  
        */
169  
        */
170  

170  

171  
        // parse {id} or {id:specs}
171  
        // parse {id} or {id:specs}
172  
        char const* id_start = it1;
172  
        char const* id_start = it1;
173  
        while (it1 != end &&
173  
        while (it1 != end &&
174  
               *it1 != ':' &&
174  
               *it1 != ':' &&
175  
               *it1 != '}')
175  
               *it1 != '}')
176  
        {
176  
        {
177  
            ++it1;
177  
            ++it1;
178  
        }
178  
        }
179  
        core::string_view id(id_start, it1);
179  
        core::string_view id(id_start, it1);
180  

180  

181  
        // move to specs part
181  
        // move to specs part
182  
        if (it1 != end &&
182  
        if (it1 != end &&
183  
            *it1 == ':')
183  
            *it1 == ':')
184  
            ++it1;
184  
            ++it1;
185  
        pctx.advance_to( it1 );
185  
        pctx.advance_to( it1 );
186  

186  

187  
        // get format_arg to use
187  
        // get format_arg to use
188  
        auto idv = grammar::parse(
188  
        auto idv = grammar::parse(
189  
            id, grammar::unsigned_rule<std::size_t>{});
189  
            id, grammar::unsigned_rule<std::size_t>{});
190  
        if (idv)
190  
        if (idv)
191  
        {
191  
        {
192  
            fctx.arg( *idv ).format( pctx, fctx, cs );
192  
            fctx.arg( *idv ).format( pctx, fctx, cs );
193  
        }
193  
        }
194  
        else if (!id.empty())
194  
        else if (!id.empty())
195  
        {
195  
        {
196  
            fctx.arg( id ).format( pctx, fctx, cs );
196  
            fctx.arg( id ).format( pctx, fctx, cs );
197  
        }
197  
        }
198  
        else
198  
        else
199  
        {
199  
        {
200  
            std::size_t arg_id = pctx.next_arg_id();
200  
            std::size_t arg_id = pctx.next_arg_id();
201  
            fctx.arg( arg_id ).format( pctx, fctx, cs );
201  
            fctx.arg( arg_id ).format( pctx, fctx, cs );
202  
        }
202  
        }
203  

203  

204  
        it1 = pctx.begin();
204  
        it1 = pctx.begin();
205  
        BOOST_ASSERT(*it1 == '}');
205  
        BOOST_ASSERT(*it1 == '}');
206  
        it0 = it1 + 1;
206  
        it0 = it1 + 1;
207  
    }
207  
    }
208  

208  

209  
    return fctx.out();
209  
    return fctx.out();
210  
}
210  
}
211  

211  

212  
} // detail
212  
} // detail
213  
} // urls
213  
} // urls
214  
} // boost
214  
} // boost
215  

215