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

10  

11  
#ifndef BOOST_URL_GRAMMAR_CI_STRING_HPP
11  
#ifndef BOOST_URL_GRAMMAR_CI_STRING_HPP
12  
#define BOOST_URL_GRAMMAR_CI_STRING_HPP
12  
#define BOOST_URL_GRAMMAR_CI_STRING_HPP
13  

13  

14  
#include <boost/url/detail/config.hpp>
14  
#include <boost/url/detail/config.hpp>
15  
#include <boost/core/detail/string_view.hpp>
15  
#include <boost/core/detail/string_view.hpp>
16  
#include <boost/url/grammar/detail/ci_string.hpp>
16  
#include <boost/url/grammar/detail/ci_string.hpp>
17  
#include <cstdlib>
17  
#include <cstdlib>
18  

18  

19  
namespace boost {
19  
namespace boost {
20  
namespace urls {
20  
namespace urls {
21  
namespace grammar {
21  
namespace grammar {
22  

22  

23  
// Algorithms for interacting with low-ASCII
23  
// Algorithms for interacting with low-ASCII
24  
// characters and strings, for implementing
24  
// characters and strings, for implementing
25  
// semantics in RFCs. These routines do not
25  
// semantics in RFCs. These routines do not
26  
// use std::locale.
26  
// use std::locale.
27  

27  

28  
//------------------------------------------------
28  
//------------------------------------------------
29  

29  

30  
/** Return c converted to lowercase
30  
/** Return c converted to lowercase
31  

31  

32  
    This function returns the character,
32  
    This function returns the character,
33  
    converting it to lowercase if it is
33  
    converting it to lowercase if it is
34  
    uppercase.
34  
    uppercase.
35  
    The function is defined only for
35  
    The function is defined only for
36  
    low-ASCII characters.
36  
    low-ASCII characters.
37  

37  

38  
    @par Example
38  
    @par Example
39  
    @code
39  
    @code
40  
    assert( to_lower( 'A' ) == 'a' );
40  
    assert( to_lower( 'A' ) == 'a' );
41  
    @endcode
41  
    @endcode
42  

42  

43  
    @par Exception Safety
43  
    @par Exception Safety
44  
    Throws nothing.
44  
    Throws nothing.
45  

45  

46  
    @return The converted character
46  
    @return The converted character
47  

47  

48  
    @param c The character to convert
48  
    @param c The character to convert
49  

49  

50  
    @see
50  
    @see
51  
        @ref to_upper.
51  
        @ref to_upper.
52  
*/
52  
*/
53  
constexpr
53  
constexpr
54  
char
54  
char
55  
to_lower(char c) noexcept
55  
to_lower(char c) noexcept
56  
{
56  
{
57  
    return detail::to_lower(c);
57  
    return detail::to_lower(c);
58  
}
58  
}
59  

59  

60  
/** Return c converted to uppercase
60  
/** Return c converted to uppercase
61  

61  

62  
    This function returns the character,
62  
    This function returns the character,
63  
    converting it to uppercase if it is
63  
    converting it to uppercase if it is
64  
    lowercase.
64  
    lowercase.
65  
    The function is defined only for
65  
    The function is defined only for
66  
    low-ASCII characters.
66  
    low-ASCII characters.
67  

67  

68  
    @par Example
68  
    @par Example
69  
    @code
69  
    @code
70  
    assert( to_upper( 'a' ) == 'A' );
70  
    assert( to_upper( 'a' ) == 'A' );
71  
    @endcode
71  
    @endcode
72  

72  

73  
    @par Exception Safety
73  
    @par Exception Safety
74  
    Throws nothing.
74  
    Throws nothing.
75  

75  

76  
    @return The converted character
76  
    @return The converted character
77  

77  

78  
    @param c The character to convert
78  
    @param c The character to convert
79  

79  

80  
    @see
80  
    @see
81  
        @ref to_lower.
81  
        @ref to_lower.
82  
*/
82  
*/
83  
constexpr
83  
constexpr
84  
char
84  
char
85  
to_upper(char c) noexcept
85  
to_upper(char c) noexcept
86  
{
86  
{
87  
    return detail::to_upper(c);
87  
    return detail::to_upper(c);
88  
}
88  
}
89  

89  

90  
//------------------------------------------------
90  
//------------------------------------------------
91  

91  

92  
/** Return the case-insensitive comparison of s0 and s1
92  
/** Return the case-insensitive comparison of s0 and s1
93  

93  

94  
    This returns the lexicographical comparison
94  
    This returns the lexicographical comparison
95  
    of two strings, ignoring case.
95  
    of two strings, ignoring case.
96  
    The function is defined only for strings
96  
    The function is defined only for strings
97  
    containing low-ASCII characters.
97  
    containing low-ASCII characters.
98  

98  

99  
    @par Example
99  
    @par Example
100  
    @code
100  
    @code
101  
    assert( ci_compare( "boost", "Boost" ) == 0 );
101  
    assert( ci_compare( "boost", "Boost" ) == 0 );
102  
    @endcode
102  
    @endcode
103  

103  

104  
    @par Exception Safety
104  
    @par Exception Safety
105  
    Throws nothing.
105  
    Throws nothing.
106  

106  

107  
    @return 0 if the strings are equal, -1 if
107  
    @return 0 if the strings are equal, -1 if
108  
    `s0` is less than `s1`, or 1 if `s0` is
108  
    `s0` is less than `s1`, or 1 if `s0` is
109  
    greater than s1.
109  
    greater than s1.
110  

110  

111  
    @param s0 The first string
111  
    @param s0 The first string
112  

112  

113  
    @param s1 The second string
113  
    @param s1 The second string
114  

114  

115  
    @see
115  
    @see
116  
        @ref ci_is_equal,
116  
        @ref ci_is_equal,
117  
        @ref ci_is_less.
117  
        @ref ci_is_less.
118  
*/
118  
*/
119  
BOOST_URL_DECL
119  
BOOST_URL_DECL
120  
int
120  
int
121  
ci_compare(
121  
ci_compare(
122  
    core::string_view s0,
122  
    core::string_view s0,
123  
    core::string_view s1) noexcept;
123  
    core::string_view s1) noexcept;
124  

124  

125  
/** Return the case-insensitive digest of a string
125  
/** Return the case-insensitive digest of a string
126  

126  

127  
    The hash function is non-cryptographic and
127  
    The hash function is non-cryptographic and
128  
    not hardened against algorithmic complexity
128  
    not hardened against algorithmic complexity
129  
    attacks.
129  
    attacks.
130  
    Returned digests are suitable for usage in
130  
    Returned digests are suitable for usage in
131  
    unordered containers.
131  
    unordered containers.
132  
    The function is defined only for strings
132  
    The function is defined only for strings
133  
    containing low-ASCII characters.
133  
    containing low-ASCII characters.
134  

134  

135  
    @return The digest
135  
    @return The digest
136  

136  

137  
    @param s The string
137  
    @param s The string
138  
*/
138  
*/
139  
BOOST_URL_DECL
139  
BOOST_URL_DECL
140  
std::size_t
140  
std::size_t
141  
ci_digest(
141  
ci_digest(
142  
    core::string_view s) noexcept;
142  
    core::string_view s) noexcept;
143  

143  

144  
//------------------------------------------------
144  
//------------------------------------------------
145  

145  

146  
/** Return true if s0 equals s1 using case-insensitive comparison
146  
/** Return true if s0 equals s1 using case-insensitive comparison
147  

147  

148  
    The function is defined only for strings
148  
    The function is defined only for strings
149  
    containing low-ASCII characters.
149  
    containing low-ASCII characters.
150  

150  

151  
    @param s0 The first string
151  
    @param s0 The first string
152  
    @param s1 The second string
152  
    @param s1 The second string
153  
    @return `true` if `s0` case-insensitively equals `s1`, otherwise `false`
153  
    @return `true` if `s0` case-insensitively equals `s1`, otherwise `false`
154  

154  

155  
    @par Example
155  
    @par Example
156  
    @code
156  
    @code
157  
    assert( ci_is_equal( "Boost", "boost" ) );
157  
    assert( ci_is_equal( "Boost", "boost" ) );
158  
    @endcode
158  
    @endcode
159  

159  

160  
    @see
160  
    @see
161  
        @ref ci_compare,
161  
        @ref ci_compare,
162  
        @ref ci_is_less.
162  
        @ref ci_is_less.
163  
*/
163  
*/
164  
template<
164  
template<
165  
    class String0,
165  
    class String0,
166  
    class String1>
166  
    class String1>
167  
auto
167  
auto
168  
ci_is_equal(
168  
ci_is_equal(
169  
    String0 const& s0,
169  
    String0 const& s0,
170  
    String1 const& s1) ->
170  
    String1 const& s1) ->
171  
        typename std::enable_if<
171  
        typename std::enable_if<
172  
            ! std::is_convertible<
172  
            ! std::is_convertible<
173  
                String0, core::string_view>::value ||
173  
                String0, core::string_view>::value ||
174  
            ! std::is_convertible<
174  
            ! std::is_convertible<
175  
                String1, core::string_view>::value,
175  
                String1, core::string_view>::value,
176  
        bool>::type
176  
        bool>::type
177  
{
177  
{
178  
    // this overload supports forward iterators and
178  
    // this overload supports forward iterators and
179  
    // does not assume the existence core::string_view::size
179  
    // does not assume the existence core::string_view::size
180  
    if( detail::type_id<String0>() >
180  
    if( detail::type_id<String0>() >
181  
        detail::type_id<String1>())
181  
        detail::type_id<String1>())
182  
        return detail::ci_is_equal(s1, s0);
182  
        return detail::ci_is_equal(s1, s0);
183  
    return detail::ci_is_equal(s0, s1);
183  
    return detail::ci_is_equal(s0, s1);
184  
}
184  
}
185  

185  

186  
/** Return true if s0 equals s1 using case-insensitive comparison
186  
/** Return true if s0 equals s1 using case-insensitive comparison
187  

187  

188  
    The function is defined only for strings
188  
    The function is defined only for strings
189  
    containing low-ASCII characters.
189  
    containing low-ASCII characters.
190  

190  

191  
    @param s0 The first string
191  
    @param s0 The first string
192  
    @param s1 The second string
192  
    @param s1 The second string
193  
    @return `true` if `s0` case-insensitively equals `s1`, otherwise `false`
193  
    @return `true` if `s0` case-insensitively equals `s1`, otherwise `false`
194  

194  

195  
    @par Example
195  
    @par Example
196  
    @code
196  
    @code
197  
    assert( ci_is_equal( "Boost", "boost" ) );
197  
    assert( ci_is_equal( "Boost", "boost" ) );
198  
    @endcode
198  
    @endcode
199  

199  

200  
    @see
200  
    @see
201  
        @ref ci_compare,
201  
        @ref ci_compare,
202  
        @ref ci_is_less.
202  
        @ref ci_is_less.
203  
*/
203  
*/
204  
inline
204  
inline
205  
bool
205  
bool
206  
ci_is_equal(
206  
ci_is_equal(
207  
    core::string_view s0,
207  
    core::string_view s0,
208  
    core::string_view s1) noexcept
208  
    core::string_view s1) noexcept
209  
{
209  
{
210  
    // this overload is faster as it makes use of
210  
    // this overload is faster as it makes use of
211  
    // core::string_view::size
211  
    // core::string_view::size
212  
    if(s0.size() != s1.size())
212  
    if(s0.size() != s1.size())
213  
        return false;
213  
        return false;
214  
    return detail::ci_is_equal(s0, s1);
214  
    return detail::ci_is_equal(s0, s1);
215  
}
215  
}
216  

216  

217  
/** Return true if s0 is less than s1 using case-insensitive comparison 
217  
/** Return true if s0 is less than s1 using case-insensitive comparison 
218  

218  

219  
    The comparison algorithm implements a
219  
    The comparison algorithm implements a
220  
    case-insensitive total order on the set
220  
    case-insensitive total order on the set
221  
    of all strings; however, it is not a
221  
    of all strings; however, it is not a
222  
    lexicographical comparison.
222  
    lexicographical comparison.
223  
    The function is defined only for strings
223  
    The function is defined only for strings
224  
    containing low-ASCII characters.
224  
    containing low-ASCII characters.
225  

225  

226  
    @param s0 The first string
226  
    @param s0 The first string
227  
    @param s1 The second string
227  
    @param s1 The second string
228  
    @return `true` if `s0` is case-insensitively less than `s1`, otherwise `false`
228  
    @return `true` if `s0` is case-insensitively less than `s1`, otherwise `false`
229  

229  

230  
    @par Example
230  
    @par Example
231  
    @code
231  
    @code
232  
    assert( ! ci_is_less( "Boost", "boost" ) );
232  
    assert( ! ci_is_less( "Boost", "boost" ) );
233  
    @endcode
233  
    @endcode
234  

234  

235  
    @see
235  
    @see
236  
        @ref ci_compare,
236  
        @ref ci_compare,
237  
        @ref ci_is_equal.
237  
        @ref ci_is_equal.
238  
*/
238  
*/
239  
inline
239  
inline
240  
bool
240  
bool
241  
ci_is_less(
241  
ci_is_less(
242  
    core::string_view s0,
242  
    core::string_view s0,
243  
    core::string_view s1) noexcept
243  
    core::string_view s1) noexcept
244  
{
244  
{
245  
    if(s0.size() != s1.size())
245  
    if(s0.size() != s1.size())
246  
        return s0.size() < s1.size();
246  
        return s0.size() < s1.size();
247  
    return detail::ci_is_less(s0, s1);
247  
    return detail::ci_is_less(s0, s1);
248  
}
248  
}
249  

249  

250  
//------------------------------------------------
250  
//------------------------------------------------
251  

251  

252  
namespace implementation_defined {
252  
namespace implementation_defined {
253  
struct ci_hash
253  
struct ci_hash
254  
{
254  
{
255  
    using is_transparent = void;
255  
    using is_transparent = void;
256  

256  

257  
    std::size_t
257  
    std::size_t
258  
    operator()(
258  
    operator()(
259  
        core::string_view s) const noexcept
259  
        core::string_view s) const noexcept
260  
    {
260  
    {
261  
        return ci_digest(s);
261  
        return ci_digest(s);
262  
    }
262  
    }
263  
};
263  
};
264  
}
264  
}
265  

265  

266  
/** A case-insensitive hash function object for strings
266  
/** A case-insensitive hash function object for strings
267  

267  

268  
    The hash function is non-cryptographic and
268  
    The hash function is non-cryptographic and
269  
    not hardened against algorithmic complexity
269  
    not hardened against algorithmic complexity
270  
    attacks.
270  
    attacks.
271  
    This is a suitable hash function for
271  
    This is a suitable hash function for
272  
    unordered containers.
272  
    unordered containers.
273  
    The function is defined only for strings
273  
    The function is defined only for strings
274  
    containing low-ASCII characters.
274  
    containing low-ASCII characters.
275  

275  

276  
    @par Example
276  
    @par Example
277  
    @code
277  
    @code
278  
    boost::unordered_map< std::string, std::string, ci_hash, ci_equal > m1;
278  
    boost::unordered_map< std::string, std::string, ci_hash, ci_equal > m1;
279  

279  

280  
    std::unordered_map  < std::string, std::string, ci_hash, ci_equal > m2; // (since C++20)
280  
    std::unordered_map  < std::string, std::string, ci_hash, ci_equal > m2; // (since C++20)
281  
    @endcode
281  
    @endcode
282  

282  

283  
    @see
283  
    @see
284  
        @ref ci_equal,
284  
        @ref ci_equal,
285  
        @ref ci_less.
285  
        @ref ci_less.
286  
*/
286  
*/
287  
using ci_hash = implementation_defined::ci_hash;
287  
using ci_hash = implementation_defined::ci_hash;
288  

288  

289  
namespace implementation_defined {
289  
namespace implementation_defined {
290  
struct ci_equal
290  
struct ci_equal
291  
{
291  
{
292  
    using is_transparent = void;
292  
    using is_transparent = void;
293  

293  

294  
    template<
294  
    template<
295  
        class String0, class String1>
295  
        class String0, class String1>
296  
    bool
296  
    bool
297  
    operator()(
297  
    operator()(
298  
        String0 s0,
298  
        String0 s0,
299  
        String1 s1) const noexcept
299  
        String1 s1) const noexcept
300  
    {
300  
    {
301  
        return ci_is_equal(s0, s1);
301  
        return ci_is_equal(s0, s1);
302  
    }
302  
    }
303  
};
303  
};
304  
} // implementation_defined
304  
} // implementation_defined
305  

305  

306  
/** A case-insensitive equals predicate for strings
306  
/** A case-insensitive equals predicate for strings
307  

307  

308  
    The function object returns `true` when
308  
    The function object returns `true` when
309  
    two strings are equal, ignoring case.
309  
    two strings are equal, ignoring case.
310  
    This is a suitable equality predicate for
310  
    This is a suitable equality predicate for
311  
    unordered containers.
311  
    unordered containers.
312  
    The function is defined only for strings
312  
    The function is defined only for strings
313  
    containing low-ASCII characters.
313  
    containing low-ASCII characters.
314  

314  

315  
    @par Example
315  
    @par Example
316  
    @code
316  
    @code
317  
    boost::unordered_map< std::string, std::string, ci_hash, ci_equal > m1;
317  
    boost::unordered_map< std::string, std::string, ci_hash, ci_equal > m1;
318  

318  

319  
    std::unordered_map  < std::string, std::string, ci_hash, ci_equal > m2; // (since C++20)
319  
    std::unordered_map  < std::string, std::string, ci_hash, ci_equal > m2; // (since C++20)
320  
    @endcode
320  
    @endcode
321  

321  

322  
    @see
322  
    @see
323  
        @ref ci_hash,
323  
        @ref ci_hash,
324  
        @ref ci_less.
324  
        @ref ci_less.
325  
*/
325  
*/
326  
using ci_equal = implementation_defined::ci_equal;
326  
using ci_equal = implementation_defined::ci_equal;
327  

327  

328  
namespace implementation_defined {
328  
namespace implementation_defined {
329  
struct ci_less
329  
struct ci_less
330  
{
330  
{
331  
    using is_transparent = void;
331  
    using is_transparent = void;
332  

332  

333  
    std::size_t
333  
    std::size_t
334  
    operator()(
334  
    operator()(
335  
        core::string_view s0,
335  
        core::string_view s0,
336  
        core::string_view s1) const noexcept
336  
        core::string_view s1) const noexcept
337  
    {
337  
    {
338  
        return ci_is_less(s0, s1);
338  
        return ci_is_less(s0, s1);
339  
    }
339  
    }
340  
};
340  
};
341  
}
341  
}
342  

342  

343  
/** A case-insensitive less predicate for strings
343  
/** A case-insensitive less predicate for strings
344  

344  

345  
    The comparison algorithm implements a
345  
    The comparison algorithm implements a
346  
    case-insensitive total order on the set
346  
    case-insensitive total order on the set
347  
    of all ASCII strings; however, it is
347  
    of all ASCII strings; however, it is
348  
    not a lexicographical comparison.
348  
    not a lexicographical comparison.
349  
    This is a suitable predicate for
349  
    This is a suitable predicate for
350  
    ordered containers.
350  
    ordered containers.
351  
    The function is defined only for strings
351  
    The function is defined only for strings
352  
    containing low-ASCII characters.
352  
    containing low-ASCII characters.
353  

353  

354  
    @par Example
354  
    @par Example
355  
    @code
355  
    @code
356  
    boost::container::map< std::string, std::string, ci_less > m1;
356  
    boost::container::map< std::string, std::string, ci_less > m1;
357  

357  

358  
    std::map< std::string, std::string, ci_less > m2; // (since C++14)
358  
    std::map< std::string, std::string, ci_less > m2; // (since C++14)
359  
    @endcode
359  
    @endcode
360  

360  

361  
    @see
361  
    @see
362  
        @ref ci_equal,
362  
        @ref ci_equal,
363  
        @ref ci_hash.
363  
        @ref ci_hash.
364  
*/
364  
*/
365  
using ci_less = implementation_defined::ci_less;
365  
using ci_less = implementation_defined::ci_less;
366  

366  

367  
} // grammar
367  
} // grammar
368  
} // urls
368  
} // urls
369  
} // boost
369  
} // boost
370  

370  

371  
#endif
371  
#endif