Line data Source code
1 : //
2 : // Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
3 : //
4 : // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 : // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 : //
7 : // Official repository: https://github.com/boostorg/url
8 : //
9 :
10 :
11 : #include <boost/url/detail/config.hpp>
12 : #include <boost/url/ipv6_address.hpp>
13 : #include <boost/url/ipv4_address.hpp>
14 : #include <boost/url/rfc/ipv6_address_rule.hpp>
15 : #include <boost/url/detail/except.hpp>
16 : #include <boost/url/grammar/parse.hpp>
17 : #include <cstring>
18 :
19 : namespace boost {
20 : namespace urls {
21 :
22 226 : ipv6_address::
23 : ipv6_address(
24 226 : bytes_type const& bytes) noexcept
25 : {
26 226 : std::memcpy(&addr_,
27 226 : bytes.data(), 16);
28 226 : }
29 :
30 4 : ipv6_address::
31 : ipv6_address(
32 4 : ipv4_address const& addr) noexcept
33 : {
34 4 : auto const v = addr.to_bytes();
35 4 : ipv6_address::bytes_type bytes = {
36 : { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
37 4 : 0xff, 0xff, v[0], v[1], v[2], v[3] } };
38 4 : std::memcpy(&addr_, bytes.data(), 16);
39 4 : }
40 :
41 60 : ipv6_address::
42 : ipv6_address(
43 60 : core::string_view s)
44 : : ipv6_address(
45 60 : parse_ipv6_address(s
46 60 : ).value(BOOST_URL_POS))
47 : {
48 59 : }
49 :
50 : core::string_view
51 27 : ipv6_address::
52 : to_buffer(
53 : char* dest,
54 : std::size_t dest_size) const
55 : {
56 27 : if(dest_size < max_str_len)
57 1 : detail::throw_length_error();
58 26 : auto n = print_impl(dest);
59 26 : return core::string_view(dest, n);
60 : }
61 :
62 : bool
63 3 : ipv6_address::
64 : is_loopback() const noexcept
65 : {
66 3 : return *this == loopback();
67 : }
68 :
69 : bool
70 3 : ipv6_address::
71 : is_unspecified() const noexcept
72 : {
73 3 : return *this == ipv6_address();
74 : }
75 :
76 : bool
77 70 : ipv6_address::
78 : is_v4_mapped() const noexcept
79 : {
80 : return
81 119 : addr_[ 0] == 0 && addr_[ 1] == 0 &&
82 33 : addr_[ 2] == 0 && addr_[ 3] == 0 &&
83 31 : addr_[ 4] == 0 && addr_[ 5] == 0 &&
84 29 : addr_[ 6] == 0 && addr_[ 7] == 0 &&
85 27 : addr_[ 8] == 0 && addr_[ 9] == 0 &&
86 131 : addr_[10] == 0xff &&
87 82 : addr_[11] == 0xff;
88 : }
89 :
90 : ipv6_address
91 5 : ipv6_address::
92 : loopback() noexcept
93 : {
94 5 : ipv6_address a;
95 5 : a.addr_[15] = 1;
96 5 : return a;
97 : }
98 :
99 : void
100 1 : ipv6_address::
101 : write_ostream(
102 : std::ostream& os) const
103 : {
104 : char buf[ipv6_address::max_str_len];
105 1 : auto const s = to_buffer(buf, sizeof(buf));
106 1 : os << s;
107 1 : }
108 :
109 : std::size_t
110 65 : ipv6_address::
111 : print_impl(
112 : char* dest) const noexcept
113 : {
114 : auto const count_zeroes =
115 207 : []( unsigned char const* first,
116 : unsigned char const* const last)
117 : {
118 207 : std::size_t n = 0;
119 574 : while(first != last)
120 : {
121 549 : if( first[0] != 0 ||
122 476 : first[1] != 0)
123 : break;
124 367 : n += 2;
125 367 : first += 2;
126 : }
127 207 : return n;
128 : };
129 : auto const print_hex =
130 159 : []( char* dest,
131 : unsigned short v)
132 : {
133 159 : char const* const dig =
134 : "0123456789abcdef";
135 159 : if(v >= 0x1000)
136 : {
137 60 : *dest++ = dig[v>>12];
138 60 : v &= 0x0fff;
139 60 : *dest++ = dig[v>>8];
140 60 : v &= 0x0ff;
141 60 : *dest++ = dig[v>>4];
142 60 : v &= 0x0f;
143 60 : *dest++ = dig[v];
144 : }
145 99 : else if(v >= 0x100)
146 : {
147 2 : *dest++ = dig[v>>8];
148 2 : v &= 0x0ff;
149 2 : *dest++ = dig[v>>4];
150 2 : v &= 0x0f;
151 2 : *dest++ = dig[v];
152 : }
153 97 : else if(v >= 0x10)
154 : {
155 1 : *dest++ = dig[v>>4];
156 1 : v &= 0x0f;
157 1 : *dest++ = dig[v];
158 : }
159 : else
160 : {
161 96 : *dest++ = dig[v];
162 : }
163 159 : return dest;
164 : };
165 65 : auto const dest0 = dest;
166 : // find longest run of zeroes
167 65 : std::size_t best_len = 0;
168 65 : int best_pos = -1;
169 65 : auto it = addr_.data();
170 : auto const v4 =
171 65 : is_v4_mapped();
172 65 : auto const end = v4 ?
173 18 : (it + addr_.size() - 4)
174 112 : : it + addr_.size();
175 272 : while(it != end)
176 : {
177 207 : auto n = count_zeroes(
178 : it, end);
179 207 : if(n == 0)
180 : {
181 135 : it += 2;
182 135 : continue;
183 : }
184 72 : if(n > best_len)
185 : {
186 66 : best_pos = static_cast<
187 66 : int>(it - addr_.data());
188 66 : best_len = n;
189 : }
190 72 : it += n;
191 : }
192 65 : it = addr_.data();
193 65 : if(best_pos != 0)
194 : {
195 42 : unsigned short v =
196 42 : (it[0] * 256U) + it[1];
197 42 : dest = print_hex(dest, v);
198 42 : it += 2;
199 : }
200 : else
201 : {
202 23 : *dest++ = ':';
203 23 : it += best_len;
204 23 : if(it == end)
205 4 : *dest++ = ':';
206 : }
207 219 : while(it != end)
208 : {
209 154 : *dest++ = ':';
210 154 : if(it - addr_.data() ==
211 154 : best_pos)
212 : {
213 37 : it += best_len;
214 37 : if(it == end)
215 15 : *dest++ = ':';
216 37 : continue;
217 : }
218 117 : unsigned short v =
219 117 : (it[0] * 256U) + it[1];
220 117 : dest = print_hex(dest, v);
221 117 : it += 2;
222 : }
223 65 : if(v4)
224 : {
225 : ipv4_address::bytes_type bytes;
226 9 : bytes[0] = it[0];
227 9 : bytes[1] = it[1];
228 9 : bytes[2] = it[2];
229 9 : bytes[3] = it[3];
230 9 : ipv4_address a(bytes);
231 9 : *dest++ = ':';
232 9 : dest += a.print_impl(dest);
233 : }
234 65 : return dest - dest0;
235 : }
236 :
237 : void
238 39 : ipv6_address::
239 : to_string_impl(
240 : string_token::arg& t) const
241 : {
242 : char buf[max_str_len];
243 39 : auto const n = print_impl(buf);
244 39 : char* dest = t.prepare(n);
245 39 : std::memcpy(dest, buf, n);
246 39 : }
247 :
248 : //------------------------------------------------
249 :
250 : auto
251 172 : parse_ipv6_address(
252 : core::string_view s) noexcept ->
253 : system::result<ipv6_address>
254 : {
255 172 : return grammar::parse(
256 172 : s, ipv6_address_rule);
257 : }
258 :
259 : } // urls
260 : } // boost
261 :
|