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

boostorg / url / #438

14 May 2025 09:06PM UTC coverage: 99.164% (-0.04%) from 99.206%
#438

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

8422 of 8493 relevant lines covered (99.16%)

3171.41 hits per line

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

99.12
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
        bool (*takes_one_char)(CS const& allowed, char) =
376✔
73
            allowed('+') ?
752✔
74
                (allowed(' ') ?
376✔
75
                     [](CS const& allowed, char c){ return allowed(c) && c != '+'; } :
42✔
76
                     [](CS const& allowed, char c){ return (allowed(c) || c == ' ') && c != '+'; }) :
2,898✔
77
                (allowed(' ') ?
×
78
                     [](CS const& allowed, char c){ return allowed(c); } :
42✔
79
                     [](CS const& allowed, char c){ return allowed(c) || c == ' '; });
42✔
80
        while (it != last)
3,232✔
81
        {
82
            char const c = *it;
2,856✔
83
            if (takes_one_char(allowed, c))
2,856✔
84
            {
85
                ++n;
2,772✔
86
            }
87
            else
88
            {
89
                n += 3;
84✔
90
            }
91
            ++it;
2,856✔
92
        }
93
    }
94
    return n;
1,734✔
95
}
96

97
//------------------------------------------------
98

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

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

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

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

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

188
//------------------------------------------------
189

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

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

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

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

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

271
//------------------------------------------------
272

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

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

295
} // urls
296
} // boost
297

298
#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