• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

boostorg / url / #440

14 May 2025 10:31PM UTC coverage: 99.129% (-0.08%) from 99.206%
#440

push

travis-ci

alandefreitas
fix: consistent behavior for space-as-plus option

This commit refactors all functions so they have consistent behavior for the space-as-plus encoding option.

- any_params_iter objects store and apply the appropriate option when measuring and copying
- when the option is enabled, encoding functions encode space-as-plus and plus as %2B regardless of the charset
- normalization and comparison algorithms take into consideration special query chars whose meaning changes depending on encoding
- all params_view objects created with default options enable space-as-plus encoding

fix #903

8419 of 8493 relevant lines covered (99.13%)

3171.46 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

96.46
include/boost/url/impl/encode.hpp
1
//
2
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
3
//
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)
6
//
7
// Official repository: https://github.com/boostorg/url
8
//
9

10
#ifndef BOOST_URL_IMPL_ENCODE_HPP
11
#define BOOST_URL_IMPL_ENCODE_HPP
12

13
#include "boost/url/grammar/token_rule.hpp"
14
#include <boost/assert.hpp>
15
#include <boost/static_assert.hpp>
16
#include <boost/url/detail/encode.hpp>
17
#include <boost/url/detail/except.hpp>
18
#include <boost/url/encoding_opts.hpp>
19
#include <boost/url/grammar/charset.hpp>
20
#include <boost/url/grammar/hexdig_chars.hpp>
21
#include <boost/url/grammar/string_token.hpp>
22
#include <boost/url/grammar/type_traits.hpp>
23

24
namespace boost {
25
namespace urls {
26

27
//------------------------------------------------
28

29
template<BOOST_URL_CONSTRAINT(grammar::CharSet) CS>
30
std::size_t
31
encoded_size(
1,734✔
32
    core::string_view s,
33
    CS const& allowed,
34
    encoding_opts opt) noexcept
35
{
36
    /*
37
        If you get a compilation error here, it
38
        means that the value you passed does
39
        not meet the requirements stated in
40
        the documentation.
41
    */
42
    BOOST_STATIC_ASSERT(
43
        grammar::is_charset<CS>::value);
44

45
    std::size_t n = 0;
1,734✔
46
    auto it = s.data();
1,734✔
47
    auto const last = it + s.size();
1,734✔
48

49
    if (!opt.space_as_plus)
1,734✔
50
    {
51
        while (it != last)
6,532✔
52
        {
53
            char const c = *it;
5,174✔
54
            if (allowed(c))
5,174✔
55
            {
56
                ++n;
4,926✔
57
            }
58
            else
59
            {
60
                n += 3;
248✔
61
            }
62
            ++it;
5,174✔
63
        }
64
    }
65
    else
66
    {
67
        // '+' is always encoded (thus
68
        // spending 3 chars) even if
69
        // allowed because "%2B" and
70
        // "+" have different meanings
71
        // when space as plus is enabled
72
        using FNT = bool (*)(CS const& allowed, char);
73
        FNT takes_one_char =
376✔
74
            allowed('+') ?
752✔
75
                (allowed(' ') ?
376✔
76
                     FNT([](CS const& allowed, char c){ return allowed(c) && c != '+'; }) :
×
77
                     FNT([](CS const& allowed, char c){ return (allowed(c) || c == ' ') && c != '+'; })) :
3,232✔
78
                (allowed(' ') ?
×
79
                     FNT([](CS const& allowed, char c){ return allowed(c); }) :
×
80
                     FNT([](CS const& allowed, char c){ return allowed(c) || c == ' '; }));
×
81
        while (it != last)
3,232✔
82
        {
83
            char const c = *it;
2,856✔
84
            if (takes_one_char(allowed, c))
2,856✔
85
            {
86
                ++n;
2,772✔
87
            }
88
            else
89
            {
90
                n += 3;
84✔
91
            }
92
            ++it;
2,856✔
93
        }
94
    }
95
    return n;
1,734✔
96
}
97

98
//------------------------------------------------
99

100
template<BOOST_URL_CONSTRAINT(grammar::CharSet) CS>
101
std::size_t
102
encode(
1,110✔
103
    char* dest,
104
    std::size_t size,
105
    core::string_view s,
106
    CS const& allowed,
107
    encoding_opts opt)
108
{
109
/*  If you get a compilation error here, it
110
    means that the value you passed does
111
    not meet the requirements stated in
112
    the documentation.
113
*/
114
    BOOST_STATIC_ASSERT(
115
        grammar::is_charset<CS>::value);
116

117
    // '%' must be reserved
118
    BOOST_ASSERT(!allowed('%'));
1,110✔
119

120
    char const* const hex =
1,110✔
121
        detail::hexdigs[opt.lower_case];
1,110✔
122
    auto const encode = [hex](
1,340✔
123
        char*& dest,
124
        unsigned char c) noexcept
125
    {
126
        *dest++ = '%';
230✔
127
        *dest++ = hex[c>>4];
230✔
128
        *dest++ = hex[c&0xf];
230✔
129
    };
130

131
    auto it = s.data();
1,110✔
132
    auto const end = dest + size;
1,110✔
133
    auto const last = it + s.size();
1,110✔
134
    auto const dest0 = dest;
1,110✔
135
    auto const end3 = end - 3;
1,110✔
136

137
    if (!opt.space_as_plus)
1,110✔
138
    {
139
        while(it != last)
3,608✔
140
        {
141
            char const c = *it;
2,918✔
142
            if (allowed(c))
2,918✔
143
            {
144
                if(dest == end)
2,734✔
145
                    return dest - dest0;
6✔
146
                *dest++ = c;
2,728✔
147
                ++it;
2,728✔
148
                continue;
2,728✔
149
            }
150
            if (dest > end3)
184✔
151
                return dest - dest0;
30✔
152
            encode(dest, c);
154✔
153
            ++it;
154✔
154
        }
155
        return dest - dest0;
690✔
156
    }
157
    else
158
    {
159
        while (it != last)
3,204✔
160
        {
161
            char const c = *it;
2,848✔
162
            if (c == ' ')
2,848✔
163
            {
164
                if(dest == end)
50✔
165
                    return dest - dest0;
4✔
166
                *dest++ = '+';
46✔
167
                ++it;
46✔
168
                continue;
46✔
169
            }
170
            else if (
5,496✔
171
                allowed(c) &&
2,798✔
172
                c != '+')
173
            {
174
                if(dest == end)
2,704✔
175
                    return dest - dest0;
6✔
176
                *dest++ = c;
2,698✔
177
                ++it;
2,698✔
178
                continue;
2,698✔
179
            }
180
            if(dest > end3)
94✔
181
                return dest - dest0;
18✔
182
            encode(dest, c);
76✔
183
            ++it;
76✔
184
        }
185
    }
186
    return dest - dest0;
356✔
187
}
188

189
//------------------------------------------------
190

191
// unsafe encode just
192
// asserts on the output buffer
193
//
194
template<BOOST_URL_CONSTRAINT(grammar::CharSet) CS>
195
std::size_t
196
encode_unsafe(
350✔
197
    char* dest,
198
    std::size_t size,
199
    core::string_view s,
200
    CS const& allowed,
201
    encoding_opts opt)
202
{
203
    BOOST_STATIC_ASSERT(
204
        grammar::is_charset<CS>::value);
205

206
    // '%' must be reserved
207
    BOOST_ASSERT(!allowed('%'));
350✔
208

209
    auto it = s.data();
350✔
210
    auto const last = it + s.size();
350✔
211
    auto const end = dest + size;
350✔
212
    ignore_unused(end);
213

214
    char const* const hex =
350✔
215
        detail::hexdigs[opt.lower_case];
350✔
216
    auto const encode = [end, hex](
726✔
217
        char*& dest,
218
        unsigned char c) noexcept
219
    {
220
        ignore_unused(end);
94✔
221
        *dest++ = '%';
94✔
222
        BOOST_ASSERT(dest != end);
94✔
223
        *dest++ = hex[c>>4];
94✔
224
        BOOST_ASSERT(dest != end);
94✔
225
        *dest++ = hex[c&0xf];
94✔
226
    };
227

228
    auto const dest0 = dest;
350✔
229
    if (!opt.space_as_plus)
350✔
230
    {
231
        while(it != last)
1,210✔
232
        {
233
            BOOST_ASSERT(dest != end);
878✔
234
            char const c = *it;
878✔
235
            if(allowed(c))
878✔
236
            {
237
                *dest++ = c;
792✔
238
            }
239
            else
240
            {
241
                encode(dest, c);
86✔
242
            }
243
            ++it;
878✔
244
        }
245
    }
246
    else
247
    {
248
        while(it != last)
74✔
249
        {
250
            BOOST_ASSERT(dest != end);
56✔
251
            char const c = *it;
56✔
252
            if (c == ' ')
56✔
253
            {
254
                *dest++ = '+';
10✔
255
            }
256
            else if (
46✔
257
                allowed(c) &&
46✔
258
                c != '+')
259
            {
260
                *dest++ = c;
38✔
261
            }
262
            else
263
            {
264
                encode(dest, c);
8✔
265
            }
266
            ++it;
56✔
267
        }
268
    }
269
    return dest - dest0;
350✔
270
}
271

272
//------------------------------------------------
273

274
template<
275
    BOOST_URL_CONSTRAINT(string_token::StringToken) StringToken,
276
    BOOST_URL_CONSTRAINT(grammar::CharSet) CS>
277
BOOST_URL_STRTOK_RETURN
278
encode(
48✔
279
    core::string_view s,
280
    CS const& allowed,
281
    encoding_opts opt,
282
    StringToken&& token) noexcept
283
{
284
    BOOST_STATIC_ASSERT(
285
        grammar::is_charset<CS>::value);
286

287
    auto const n = encoded_size(
48✔
288
        s, allowed, opt);
289
    auto p = token.prepare(n);
48✔
290
    if(n > 0)
48✔
291
        encode_unsafe(
44✔
292
            p, n, s, allowed, opt);
293
    return token.result();
48✔
294
}
295

296
} // urls
297
} // boost
298

299
#endif
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc