ext-boost/boost/url/impl/ipv6_address.ipp
2023-05-22 18:45:02 +10:00

253 lines
5.0 KiB
C++

//
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/url
//
#ifndef BOOST_URL_IMPL_IPV6_ADDRESS_IPP
#define BOOST_URL_IMPL_IPV6_ADDRESS_IPP
#include <boost/url/ipv6_address.hpp>
#include <boost/url/rfc/ipv6_address_rule.hpp>
#include <boost/url/detail/except.hpp>
#include <boost/url/grammar/parse.hpp>
#include <cstring>
namespace boost {
namespace urls {
ipv6_address::
ipv6_address(
bytes_type const& bytes) noexcept
{
std::memcpy(&addr_,
bytes.data(), 16);
}
ipv6_address::
ipv6_address(
ipv4_address const& addr) noexcept
{
auto const v = addr.to_bytes();
ipv6_address::bytes_type bytes = {
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0xff, 0xff, v[0], v[1], v[2], v[3] } };
std::memcpy(&addr_, bytes.data(), 16);
}
ipv6_address::
ipv6_address(
string_view s)
: ipv6_address(
parse_ipv6_address(s
).value(BOOST_URL_POS))
{
}
string_view
ipv6_address::
to_buffer(
char* dest,
std::size_t dest_size) const
{
if(dest_size < max_str_len)
detail::throw_length_error();
auto n = print_impl(dest);
return string_view(dest, n);
}
bool
ipv6_address::
is_loopback() const noexcept
{
return *this == loopback();
}
bool
ipv6_address::
is_unspecified() const noexcept
{
return *this == ipv6_address();
}
bool
ipv6_address::
is_v4_mapped() const noexcept
{
return
addr_[ 0] == 0 && addr_[ 1] == 0 &&
addr_[ 2] == 0 && addr_[ 3] == 0 &&
addr_[ 4] == 0 && addr_[ 5] == 0 &&
addr_[ 6] == 0 && addr_[ 7] == 0 &&
addr_[ 8] == 0 && addr_[ 9] == 0 &&
addr_[10] == 0xff &&
addr_[11] == 0xff;
}
ipv6_address
ipv6_address::
loopback() noexcept
{
ipv6_address a;
a.addr_[15] = 1;
return a;
}
std::size_t
ipv6_address::
print_impl(
char* dest) const noexcept
{
auto const count_zeroes =
[]( unsigned char const* first,
unsigned char const* const last)
{
std::size_t n = 0;
while(first != last)
{
if( first[0] != 0 ||
first[1] != 0)
break;
n += 2;
first += 2;
}
return n;
};
auto const print_hex =
[]( char* dest,
unsigned short v)
{
char const* const dig =
"0123456789abcdef";
if(v >= 0x1000)
{
*dest++ = dig[v>>12];
v &= 0x0fff;
*dest++ = dig[v>>8];
v &= 0x0ff;
*dest++ = dig[v>>4];
v &= 0x0f;
*dest++ = dig[v];
}
else if(v >= 0x100)
{
*dest++ = dig[v>>8];
v &= 0x0ff;
*dest++ = dig[v>>4];
v &= 0x0f;
*dest++ = dig[v];
}
else if(v >= 0x10)
{
*dest++ = dig[v>>4];
v &= 0x0f;
*dest++ = dig[v];
}
else
{
*dest++ = dig[v];
}
return dest;
};
auto const dest0 = dest;
// find longest run of zeroes
std::size_t best_len = 0;
int best_pos = -1;
auto it = addr_.data();
auto const v4 =
is_v4_mapped();
auto const end = v4 ?
(it + addr_.size() - 4)
: it + addr_.size();
while(it != end)
{
auto n = count_zeroes(
it, end);
if(n == 0)
{
it += 2;
continue;
}
if(n > best_len)
{
best_pos = static_cast<
int>(it - addr_.data());
best_len = n;
}
it += n;
}
it = addr_.data();
if(best_pos != 0)
{
unsigned short v =
(it[0] * 256U) + it[1];
dest = print_hex(dest, v);
it += 2;
}
else
{
*dest++ = ':';
it += best_len;
if(it == end)
*dest++ = ':';
}
while(it != end)
{
*dest++ = ':';
if(it - addr_.data() ==
best_pos)
{
it += best_len;
if(it == end)
*dest++ = ':';
continue;
}
unsigned short v =
(it[0] * 256U) + it[1];
dest = print_hex(dest, v);
it += 2;
}
if(v4)
{
ipv4_address::bytes_type bytes;
bytes[0] = it[0];
bytes[1] = it[1];
bytes[2] = it[2];
bytes[3] = it[3];
ipv4_address a(bytes);
*dest++ = ':';
dest += a.print_impl(dest);
}
return dest - dest0;
}
void
ipv6_address::
to_string_impl(
string_token::arg& t) const
{
char buf[max_str_len];
auto const n = print_impl(buf);
char* dest = t.prepare(n);
std::memcpy(dest, buf, n);
}
//------------------------------------------------
auto
parse_ipv6_address(
string_view s) noexcept ->
result<ipv6_address>
{
return grammar::parse(
s, ipv6_address_rule);
}
} // urls
} // boost
#endif