include/boost/url/rfc/impl/ipv6_address_rule.hpp

100.0% Lines (113/113) 100.0% List of functions (2/2)
ipv6_address_rule.hpp
f(x) Functions (2)
Line TLA Hits Source Code
1 //
2 // Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
3 // Copyright (c) 2024 Alan de Freitas (alandefreitas@gmail.com)
4 //
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)
7 //
8 // Official repository: https://github.com/boostorg/url
9 //
10
11 #ifndef BOOST_URL_RFC_IMPL_IPV6_ADDRESS_RULE_HPP
12 #define BOOST_URL_RFC_IMPL_IPV6_ADDRESS_RULE_HPP
13
14 #include <boost/url/detail/config.hpp>
15 #include <boost/url/rfc/ipv4_address_rule.hpp>
16 #include <boost/url/rfc/detail/h16_rule.hpp>
17 #include <boost/url/grammar/charset.hpp>
18 #include <boost/url/grammar/hexdig_chars.hpp>
19 #include <boost/url/grammar/error.hpp>
20 #include <boost/url/grammar/parse.hpp>
21 #include <boost/assert.hpp>
22 #include <cstring>
23
24 namespace boost {
25 namespace urls {
26
27 namespace detail {
28
29 // return `true` if the hex
30 // word could be 0..255 if
31 // interpreted as decimal
32 BOOST_URL_CXX20_CONSTEXPR_OR_INLINE
33 bool
34 61x maybe_octet(
35 unsigned char const* p) noexcept
36 {
37 61x unsigned short word =
38 static_cast<unsigned short>(
39 61x p[0]) * 256 +
40 static_cast<unsigned short>(
41 61x p[1]);
42 61x if(word > 0x255)
43 3x return false;
44 58x if(((word >> 4) & 0xf) > 9)
45 1x return false;
46 57x if((word & 0xf) > 9)
47 2x return false;
48 55x return true;
49 }
50
51 } // detail
52
53 BOOST_URL_CXX20_CONSTEXPR_OR_INLINE
54 auto
55 352x implementation_defined::ipv6_address_rule_t::
56 parse(
57 char const*& it,
58 char const* const end
59 ) const noexcept ->
60 system::result<ipv6_address>
61 {
62 352x int n = 8; // words needed
63 352x int b = -1; // value of n
64 // when '::' seen
65 352x bool c = false; // need colon
66 352x auto prev = it;
67 ipv6_address::bytes_type bytes;
68 352x system::result<detail::h16_rule_t::value_type> rv;
69 for(;;)
70 {
71 1760x if(it == end)
72 {
73 92x if(b != -1)
74 {
75 // end in "::"
76 86x break;
77 }
78 6x BOOST_ASSERT(n > 0);
79 // not enough words
80 6x BOOST_URL_CONSTEXPR_RETURN_EC(
81 grammar::error::invalid);
82 }
83 1668x if(*it == ':')
84 {
85 1135x ++it;
86 1135x if(it == end)
87 {
88 // expected ':'
89 5x BOOST_URL_CONSTEXPR_RETURN_EC(
90 grammar::error::invalid);
91 }
92 1130x if(*it == ':')
93 {
94 210x if(b == -1)
95 {
96 // first "::"
97 207x ++it;
98 207x --n;
99 207x b = n;
100 207x if(n == 0)
101 2x break;
102 205x c = false;
103 205x continue;
104 }
105 // extra "::" found
106 3x BOOST_URL_CONSTEXPR_RETURN_EC(
107 grammar::error::invalid);
108 }
109 920x if(c)
110 {
111 914x prev = it;
112 914x rv = grammar::parse(
113 it, end,
114 detail::h16_rule);
115 914x if(! rv)
116 5x return rv.error();
117 909x bytes[2*(8-n)+0] = rv->hi;
118 909x bytes[2*(8-n)+1] = rv->lo;
119 909x --n;
120 909x if(n == 0)
121 96x break;
122 813x continue;
123 }
124 // expected h16
125 6x BOOST_URL_CONSTEXPR_RETURN_EC(
126 grammar::error::invalid);
127 }
128 533x if(*it == '.')
129 {
130 81x if(b == -1 && n > 1)
131 {
132 // not enough h16
133 11x BOOST_URL_CONSTEXPR_RETURN_EC(
134 grammar::error::invalid);
135 }
136 70x if(! c)
137 {
138 // missing h16 before "."
139 9x BOOST_URL_CONSTEXPR_RETURN_EC(
140 grammar::error::invalid);
141 }
142 61x if(! detail::maybe_octet(
143 61x &bytes[2*(7-n)]))
144 {
145 // invalid octet
146 6x BOOST_URL_CONSTEXPR_RETURN_EC(
147 grammar::error::invalid);
148 }
149 // rewind the h16 and
150 // parse it as ipv4
151 55x it = prev;
152 55x auto rv1 = grammar::parse(
153 it, end, ipv4_address_rule);
154 55x if(! rv1)
155 22x return rv1.error();
156 33x auto v4 = *rv1;
157 auto const b4 =
158 33x v4.to_bytes();
159 33x bytes[2*(7-n)+0] = b4[0];
160 33x bytes[2*(7-n)+1] = b4[1];
161 33x bytes[2*(7-n)+2] = b4[2];
162 33x bytes[2*(7-n)+3] = b4[3];
163 33x --n;
164 33x break;
165 }
166 auto d =
167 452x grammar::hexdig_value(*it);
168 452x if( b != -1 &&
169 d < 0)
170 {
171 // ends in "::"
172 41x break;
173 }
174 411x if(! c)
175 {
176 407x prev = it;
177 407x rv = grammar::parse(
178 it, end,
179 detail::h16_rule);
180 407x if(! rv)
181 16x return rv.error();
182 391x bytes[2*(8-n)+0] = rv->hi;
183 391x bytes[2*(8-n)+1] = rv->lo;
184 391x --n;
185 391x if(n == 0)
186 1x break;
187 390x c = true;
188 390x continue;
189 }
190 // ':' divides a word
191 4x BOOST_URL_CONSTEXPR_RETURN_EC(
192 grammar::error::invalid);
193 1408x }
194 259x if(b == -1)
195 95x return ipv6_address{bytes};
196 164x if(b == n)
197 {
198 // "::" last
199 35x auto const i =
200 35x 2 * (7 - n);
201 35x std::memset(
202 35x &bytes[i],
203 35x 0, 16 - i);
204 }
205 129x else if(b == 7)
206 {
207 // "::" first
208 52x auto const i =
209 52x 2 * (b - n);
210 104x std::memmove(
211 52x &bytes[16 - i],
212 52x &bytes[2],
213 i);
214 52x std::memset(
215 52x &bytes[0],
216 52x 0, 16 - i);
217 }
218 else
219 {
220 // "::" in middle
221 77x auto const i0 =
222 77x 2 * (7 - b);
223 77x auto const i1 =
224 77x 2 * (b - n);
225 154x std::memmove(
226 77x &bytes[16 - i1],
227 77x &bytes[i0 + 2],
228 i1);
229 77x std::memset(
230 77x &bytes[i0],
231 77x 0, 16 - (i0 + i1));
232 }
233 164x return ipv6_address{bytes};
234 }
235
236 } // urls
237 } // boost
238
239
240 #endif
241