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 346 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 346 int n = 8; // words needed
63 346 int b = -1; // value of n
64 // when '::' seen
65 346 bool c = false; // need colon
66 346 auto prev = it;
67 ipv6_address::bytes_type bytes;
68 346 system::result<detail::h16_rule_t::value_type> rv;
69 for(;;)
70 {
71 1746 if(it == end)
72 {
73 92 if(b != -1)
74 {
75 // end in "::"
76 86 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 1654 if(*it == ':')
84 {
85 1129 ++it;
86 1129 if(it == end)
87 {
88 // expected ':'
89 5 BOOST_URL_CONSTEXPR_RETURN_EC(
90 grammar::error::invalid);
91 }
92 1124 if(*it == ':')
93 {
94 205 if(b == -1)
95 {
96 // first "::"
97 202 ++it;
98 202 --n;
99 202 b = n;
100 202 if(n == 0)
101 2 break;
102 200 c = false;
103 200 continue;
104 }
105 // extra "::" found
106 3 BOOST_URL_CONSTEXPR_RETURN_EC(
107 grammar::error::invalid);
108 }
109 919 if(c)
110 {
111 913 prev = it;
112 913 rv = grammar::parse(
113 it, end,
114 detail::h16_rule);
115 913 if(! rv)
116 5 return rv.error();
117 908 bytes[2*(8-n)+0] = rv->hi;
118 908 bytes[2*(8-n)+1] = rv->lo;
119 908 --n;
120 908 if(n == 0)
121 96 break;
122 812 continue;
123 }
124 // expected h16
125 6 BOOST_URL_CONSTEXPR_RETURN_EC(
126 grammar::error::invalid);
127 }
128 525 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 450 grammar::hexdig_value(*it);
162 450 if( b != -1 &&
163 d < 0)
164 {
165 // ends in "::"
166 41 break;
167 }
168 409 if(! c)
169 {
170 405 prev = it;
171 405 rv = grammar::parse(
172 it, end,
173 detail::h16_rule);
174 405 if(! rv)
175 16 return rv.error();
176 389 bytes[2*(8-n)+0] = rv->hi;
177 389 bytes[2*(8-n)+1] = rv->lo;
178 389 --n;
179 389 if(n == 0)
180 1 break;
181 388 c = true;
182 388 continue;
183 }
184 // ':' divides a word
185 4 BOOST_URL_CONSTEXPR_RETURN_EC(
186 grammar::error::invalid);
187 1400 }
188 259 if(b == -1)
189 95 return ipv6_address{bytes};
190 164 if(b == n)
191 {
192 // "::" last
193 35 auto const i =
194 35 2 * (7 - n);
195 35 std::memset(
196 35 &bytes[i],
197 35 0, 16 - i);
198 }
199 129 else if(b == 7)
200 {
201 // "::" first
202 52 auto const i =
203 52 2 * (b - n);
204 104 std::memmove(
205 52 &bytes[16 - i],
206 52 &bytes[2],
207 i);
208 52 std::memset(
209 52 &bytes[0],
210 52 0, 16 - i);
211 }
212 else
213 {
214 // "::" in middle
215 77 auto const i0 =
216 77 2 * (7 - b);
217 77 auto const i1 =
218 77 2 * (b - n);
219 154 std::memmove(
220 77 &bytes[16 - i1],
221 77 &bytes[i0 + 2],
222 i1);
223 77 std::memset(
224 77 &bytes[i0],
225 77 0, 16 - (i0 + i1));
226 }
227 164 return ipv6_address{bytes};
228 }
229
230 } // urls
231 } // boost
232
233
234 #endif
235