LCOV - code coverage report
Current view: top level - src - ipv4_address.cpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 99.0 % 102 101
Test Date: 2026-02-04 14:16:13 Functions: 100.0 % 15 15

            Line data    Source code
       1              : //
       2              : // Copyright (c) 2026 Vinnie Falco (vinnie dot falco at gmail dot 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/cppalliance/corosio
       8              : //
       9              : 
      10              : #include <boost/corosio/ipv4_address.hpp>
      11              : 
      12              : #include <ostream>
      13              : #include <stdexcept>
      14              : 
      15              : namespace boost::corosio {
      16              : 
      17         4848 : ipv4_address::ipv4_address(uint_type u) noexcept
      18         4848 :     : addr_(u)
      19              : {
      20         4848 : }
      21              : 
      22        14298 : ipv4_address::ipv4_address(bytes_type const& bytes) noexcept
      23              : {
      24        14298 :     addr_ =
      25        14298 :         (static_cast<std::uint32_t>(bytes[0]) << 24) |
      26        14298 :         (static_cast<std::uint32_t>(bytes[1]) << 16) |
      27        14298 :         (static_cast<std::uint32_t>(bytes[2]) <<  8) |
      28        14298 :         (static_cast<std::uint32_t>(bytes[3]));
      29        14298 : }
      30              : 
      31            4 : ipv4_address::ipv4_address(std::string_view s)
      32              : {
      33            4 :     auto ec = parse_ipv4_address(s, *this);
      34            4 :     if (ec)
      35            3 :         throw std::invalid_argument("invalid IPv4 address");
      36            1 : }
      37              : 
      38              : auto
      39         4845 : ipv4_address::to_bytes() const noexcept -> bytes_type
      40              : {
      41              :     bytes_type bytes;
      42         4845 :     bytes[0] = static_cast<unsigned char>((addr_ >> 24) & 0xff);
      43         4845 :     bytes[1] = static_cast<unsigned char>((addr_ >> 16) & 0xff);
      44         4845 :     bytes[2] = static_cast<unsigned char>((addr_ >>  8) & 0xff);
      45         4845 :     bytes[3] = static_cast<unsigned char>( addr_        & 0xff);
      46         4845 :     return bytes;
      47              : }
      48              : 
      49              : auto
      50           10 : ipv4_address::to_uint() const noexcept -> uint_type
      51              : {
      52           10 :     return addr_;
      53              : }
      54              : 
      55              : std::string
      56           11 : ipv4_address::to_string() const
      57              : {
      58              :     char buf[max_str_len];
      59           11 :     auto n = print_impl(buf);
      60           22 :     return std::string(buf, n);
      61              : }
      62              : 
      63              : std::string_view
      64            4 : ipv4_address::to_buffer(char* dest, std::size_t dest_size) const
      65              : {
      66            4 :     if (dest_size < max_str_len)
      67            0 :         throw std::length_error("buffer too small for IPv4 address");
      68            4 :     auto n = print_impl(dest);
      69            4 :     return std::string_view(dest, n);
      70              : }
      71              : 
      72              : bool
      73            5 : ipv4_address::is_loopback() const noexcept
      74              : {
      75            5 :     return (addr_ & 0xFF000000) == 0x7F000000;
      76              : }
      77              : 
      78              : bool
      79            4 : ipv4_address::is_unspecified() const noexcept
      80              : {
      81            4 :     return addr_ == 0;
      82              : }
      83              : 
      84              : bool
      85            4 : ipv4_address::is_multicast() const noexcept
      86              : {
      87            4 :     return (addr_ & 0xF0000000) == 0xE0000000;
      88              : }
      89              : 
      90              : std::ostream&
      91            1 : operator<<(std::ostream& os, ipv4_address const& addr)
      92              : {
      93              :     char buf[ipv4_address::max_str_len];
      94            1 :     os << addr.to_buffer(buf, sizeof(buf));
      95            1 :     return os;
      96              : }
      97              : 
      98              : std::size_t
      99           15 : ipv4_address::print_impl(char* dest) const noexcept
     100              : {
     101           15 :     auto const start = dest;
     102           60 :     auto const write = [](char*& dest, unsigned char v)
     103              :     {
     104           60 :         if (v >= 100)
     105              :         {
     106           19 :             *dest++ = '0' + v / 100;
     107           19 :             v %= 100;
     108           19 :             *dest++ = '0' + v / 10;
     109           19 :             v %= 10;
     110              :         }
     111           41 :         else if (v >= 10)
     112              :         {
     113            5 :             *dest++ = '0' + v / 10;
     114            5 :             v %= 10;
     115              :         }
     116           60 :         *dest++ = '0' + v;
     117           60 :     };
     118           15 :     write(dest, static_cast<unsigned char>((addr_ >> 24) & 0xff));
     119           15 :     *dest++ = '.';
     120           15 :     write(dest, static_cast<unsigned char>((addr_ >> 16) & 0xff));
     121           15 :     *dest++ = '.';
     122           15 :     write(dest, static_cast<unsigned char>((addr_ >>  8) & 0xff));
     123           15 :     *dest++ = '.';
     124           15 :     write(dest, static_cast<unsigned char>( addr_        & 0xff));
     125           15 :     return static_cast<std::size_t>(dest - start);
     126              : }
     127              : 
     128              : //------------------------------------------------
     129              : 
     130              : namespace {
     131              : 
     132              : // Parse a decimal octet (0-255), no leading zeros except "0"
     133              : // Returns true on success, advances `it`
     134              : bool
     135          193 : parse_dec_octet(
     136              :     char const*& it,
     137              :     char const* end,
     138              :     unsigned char& octet) noexcept
     139              : {
     140          193 :     if (it == end)
     141            2 :         return false;
     142              : 
     143          191 :     char c = *it;
     144          191 :     if (c < '0' || c > '9')
     145            6 :         return false;
     146              : 
     147          185 :     unsigned v = static_cast<unsigned>(c - '0');
     148          185 :     ++it;
     149              : 
     150          185 :     if (v == 0)
     151              :     {
     152              :         // "0" is valid, but "00", "01", etc. are not
     153           29 :         if (it != end && *it >= '0' && *it <= '9')
     154            3 :             return false;
     155           26 :         octet = 0;
     156           26 :         return true;
     157              :     }
     158              : 
     159              :     // First digit was 1-9, can have more
     160          156 :     if (it != end && *it >= '0' && *it <= '9')
     161              :     {
     162           42 :         v = v * 10 + static_cast<unsigned>(*it - '0');
     163           42 :         ++it;
     164              : 
     165           42 :         if (it != end && *it >= '0' && *it <= '9')
     166              :         {
     167           39 :             v = v * 10 + static_cast<unsigned>(*it - '0');
     168           39 :             ++it;
     169              : 
     170              :             // Can't have more than 3 digits
     171           39 :             if (it != end && *it >= '0' && *it <= '9')
     172            1 :                 return false;
     173              :         }
     174              :     }
     175              : 
     176          155 :     if (v > 255)
     177            7 :         return false;
     178              : 
     179          148 :     octet = static_cast<unsigned char>(v);
     180          148 :     return true;
     181              : }
     182              : 
     183              : } // namespace
     184              : 
     185              : std::error_code
     186           62 : parse_ipv4_address(
     187              :     std::string_view s,
     188              :     ipv4_address& addr) noexcept
     189              : {
     190           62 :     auto it = s.data();
     191           62 :     auto const end = it + s.size();
     192              : 
     193              :     unsigned char octets[4];
     194              : 
     195              :     // Parse first octet
     196           62 :     if (!parse_dec_octet(it, end, octets[0]))
     197           13 :         return std::make_error_code(std::errc::invalid_argument);
     198              : 
     199              :     // Parse remaining octets
     200          174 :     for (int i = 1; i < 4; ++i)
     201              :     {
     202          137 :         if (it == end || *it != '.')
     203            6 :             return std::make_error_code(std::errc::invalid_argument);
     204          131 :         ++it; // skip '.'
     205              : 
     206          131 :         if (!parse_dec_octet(it, end, octets[i]))
     207            6 :             return std::make_error_code(std::errc::invalid_argument);
     208              :     }
     209              : 
     210              :     // Must have consumed entire string
     211           37 :     if (it != end)
     212            5 :         return std::make_error_code(std::errc::invalid_argument);
     213              : 
     214           64 :     addr = ipv4_address(ipv4_address::bytes_type{{
     215           32 :         octets[0], octets[1], octets[2], octets[3]}});
     216              : 
     217           32 :     return {};
     218              : }
     219              : 
     220              : } // namespace boost::corosio
        

Generated by: LCOV version 2.3