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

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