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

100.0% Lines (111/111) 100.0% Functions (2/2)
include/boost/url/rfc/impl/ipv6_address_rule.hpp
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 65 maybe_octet(
35 unsigned char const* p) noexcept
36 {
37 65 unsigned short word =
38 static_cast<unsigned short>(
39 65 p[0]) * 256 +
40 static_cast<unsigned short>(
41 65 p[1]);
42 65 if(word > 0x255)
43 7 return false;
44 58 if(((word >> 4) & 0xf) > 9)
45 1 return false;
46 57 if((word & 0xf) > 9)
47 2 return false;
48 55 return true;
49 }
50
51 } // detail
52
53 BOOST_URL_CXX20_CONSTEXPR_OR_INLINE
54 auto
55 292 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 292 int n = 8; // words needed
63 292 int b = -1; // value of n
64 // when '::' seen
65 292 bool c = false; // need colon
66 292 auto prev = it;
67 ipv6_address::bytes_type bytes;
68 292 system::result<detail::h16_rule_t::value_type> rv;
69 for(;;)
70 {
71 1358 if(it == end)
72 {
73 91 if(b != -1)
74 {
75 // end in "::"
76 85 break;
77 }
78 6 BOOST_ASSERT(n > 0);
79 // not enough words
80 6 BOOST_URL_CONSTEXPR_RETURN_EC(
81 grammar::error::invalid);
82 }
83 1267 if(*it == ':')
84 {
85 804 ++it;
86 804 if(it == end)
87 {
88 // expected ':'
89 5 BOOST_URL_CONSTEXPR_RETURN_EC(
90 grammar::error::invalid);
91 }
92 799 if(*it == ':')
93 {
94 196 if(b == -1)
95 {
96 // first "::"
97 193 ++it;
98 193 --n;
99 193 b = n;
100 193 if(n == 0)
101 2 break;
102 191 c = false;
103 191 continue;
104 }
105 // extra "::" found
106 3 BOOST_URL_CONSTEXPR_RETURN_EC(
107 grammar::error::invalid);
108 }
109 603 if(c)
110 {
111 597 prev = it;
112 597 rv = grammar::parse(
113 it, end,
114 detail::h16_rule);
115 597 if(! rv)
116 5 return rv.error();
117 592 bytes[2*(8-n)+0] = rv->hi;
118 592 bytes[2*(8-n)+1] = rv->lo;
119 592 --n;
120 592 if(n == 0)
121 51 break;
122 541 continue;
123 }
124 // expected h16
125 6 BOOST_URL_CONSTEXPR_RETURN_EC(
126 grammar::error::invalid);
127 }
128 463 if(*it == '.')
129 {
130 75 if(b == -1 && n > 1)
131 {
132 // not enough h16
133 10 BOOST_URL_CONSTEXPR_RETURN_EC(
134 grammar::error::invalid);
135 }
136 65 if(! detail::maybe_octet(
137 65 &bytes[2*(7-n)]))
138 {
139 // invalid octet
140 10 BOOST_URL_CONSTEXPR_RETURN_EC(
141 grammar::error::invalid);
142 }
143 // rewind the h16 and
144 // parse it as ipv4
145 55 it = prev;
146 55 auto rv1 = grammar::parse(
147 it, end, ipv4_address_rule);
148 55 if(! rv1)
149 22 return rv1.error();
150 33 auto v4 = *rv1;
151 auto const b4 =
152 33 v4.to_bytes();
153 33 bytes[2*(7-n)+0] = b4[0];
154 33 bytes[2*(7-n)+1] = b4[1];
155 33 bytes[2*(7-n)+2] = b4[2];
156 33 bytes[2*(7-n)+3] = b4[3];
157 33 --n;
158 33 break;
159 }
160 auto d =
161 388 grammar::hexdig_value(*it);
162 388 if( b != -1 &&
163 d < 0)
164 {
165 // ends in "::"
166 33 break;
167 }
168 355 if(! c)
169 {
170 351 prev = it;
171 351 rv = grammar::parse(
172 it, end,
173 detail::h16_rule);
174 351 if(! rv)
175 16 return rv.error();
176 335 bytes[2*(8-n)+0] = rv->hi;
177 335 bytes[2*(8-n)+1] = rv->lo;
178 335 --n;
179 335 if(n == 0)
180 1 break;
181 334 c = true;
182 334 continue;
183 }
184 // ':' divides a word
185 4 BOOST_URL_CONSTEXPR_RETURN_EC(
186 grammar::error::invalid);
187 1066 }
188 205 if(b == -1)
189 50 return ipv6_address{bytes};
190 155 if(b == n)
191 {
192 // "::" last
193 34 auto const i =
194 34 2 * (7 - n);
195 34 std::memset(
196 34 &bytes[i],
197 34 0, 16 - i);
198 }
199 121 else if(b == 7)
200 {
201 // "::" first
202 45 auto const i =
203 45 2 * (b - n);
204 90 std::memmove(
205 45 &bytes[16 - i],
206 45 &bytes[2],
207 i);
208 45 std::memset(
209 45 &bytes[0],
210 45 0, 16 - i);
211 }
212 else
213 {
214 // "::" in middle
215 76 auto const i0 =
216 76 2 * (7 - b);
217 76 auto const i1 =
218 76 2 * (b - n);
219 152 std::memmove(
220 76 &bytes[16 - i1],
221 76 &bytes[i0 + 2],
222 i1);
223 76 std::memset(
224 76 &bytes[i0],
225 76 0, 16 - (i0 + i1));
226 }
227 155 return ipv6_address{bytes};
228 }
229
230 } // urls
231 } // boost
232
233
234 #endif
235