LCOV - code coverage report
Current view: top level - libs/url/src/rfc - ipv6_address_rule.cpp (source / functions) Hit Total Coverage
Test: coverage_filtered.info Lines: 111 111 100.0 %
Date: 2024-02-29 20:02:55 Functions: 2 2 100.0 %

          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             : #ifndef BOOST_URL_RFC_IMPL_IPV6_ADDRESS_RULE_IPP
      11             : #define BOOST_URL_RFC_IMPL_IPV6_ADDRESS_RULE_IPP
      12             : 
      13             : #include <boost/url/detail/config.hpp>
      14             : #include <boost/url/rfc/ipv6_address_rule.hpp>
      15             : #include <boost/url/rfc/ipv4_address_rule.hpp>
      16             : #include "detail/h16_rule.hpp"
      17             : #include <boost/url/grammar/charset.hpp>
      18             : #include <boost/url/grammar/hexdig_chars.hpp>
      19             : #include <boost/url/grammar/parse.hpp>
      20             : #include <boost/assert.hpp>
      21             : #include <cstring>
      22             : 
      23             : namespace boost {
      24             : namespace urls {
      25             : 
      26             : namespace detail {
      27             : 
      28             : // return `true` if the hex
      29             : // word could be 0..255 if
      30             : // interpreted as decimal
      31             : static
      32             : bool
      33          65 : maybe_octet(
      34             :     unsigned char const* p) noexcept
      35             : {
      36          65 :     unsigned short word =
      37             :         static_cast<unsigned short>(
      38          65 :             p[0]) * 256 +
      39             :         static_cast<unsigned short>(
      40          65 :             p[1]);
      41          65 :     if(word > 0x255)
      42           5 :         return false;
      43          60 :     if(((word >>  4) & 0xf) > 9)
      44           3 :         return false;
      45          57 :     if((word & 0xf) > 9)
      46           2 :         return false;
      47          55 :     return true;
      48             : }
      49             : 
      50             : } // detail
      51             : 
      52             : auto
      53         288 : ipv6_address_rule_t::
      54             : parse(
      55             :     char const*& it,
      56             :     char const* const end
      57             :         ) const noexcept ->
      58             :     system::result<ipv6_address>
      59             : {
      60         288 :     int n = 8;      // words needed
      61         288 :     int b = -1;     // value of n
      62             :                     // when '::' seen
      63         288 :     bool c = false; // need colon
      64         288 :     auto prev = it;
      65             :     ipv6_address::bytes_type bytes;
      66         288 :     system::result<detail::h16_rule_t::value_type> rv;
      67             :     for(;;)
      68             :     {
      69        1324 :         if(it == end)
      70             :         {
      71          91 :             if(b != -1)
      72             :             {
      73             :                 // end in "::"
      74          83 :                 break;
      75             :             }
      76           8 :             BOOST_ASSERT(n > 0);
      77             :             // not enough words
      78           8 :             BOOST_URL_RETURN_EC(
      79             :                 grammar::error::invalid);
      80             :         }
      81        1233 :         if(*it == ':')
      82             :         {
      83         794 :             ++it;
      84         794 :             if(it == end)
      85             :             {
      86             :                 // expected ':'
      87           5 :                 BOOST_URL_RETURN_EC(
      88             :                     grammar::error::invalid);
      89             :             }
      90         789 :             if(*it == ':')
      91             :             {
      92         186 :                 if(b == -1)
      93             :                 {
      94             :                     // first "::"
      95         183 :                     ++it;
      96         183 :                     --n;
      97         183 :                     b = n;
      98         183 :                     if(n == 0)
      99           2 :                         break;
     100         181 :                     c = false;
     101         181 :                     continue;
     102             :                 }
     103             :                 // extra "::" found
     104           3 :                 BOOST_URL_RETURN_EC(
     105             :                     grammar::error::invalid);
     106             :             }
     107         603 :             if(c)
     108             :             {
     109         597 :                 prev = it;
     110             :                 rv = grammar::parse(
     111             :                     it, end,
     112         597 :                     detail::h16_rule);
     113         597 :                 if(! rv)
     114           5 :                     return rv.error();
     115         592 :                 bytes[2*(8-n)+0] = rv->hi;
     116         592 :                 bytes[2*(8-n)+1] = rv->lo;
     117         592 :                 --n;
     118         592 :                 if(n == 0)
     119          51 :                     break;
     120         541 :                 continue;
     121             :             }
     122             :             // expected h16
     123           6 :             BOOST_URL_RETURN_EC(
     124             :                 grammar::error::invalid);
     125             :         }
     126         439 :         if(*it == '.')
     127             :         {
     128          75 :             if(b == -1 && n > 1)
     129             :             {
     130             :                 // not enough h16
     131          10 :                 BOOST_URL_RETURN_EC(
     132             :                     grammar::error::invalid);
     133             :             }
     134          65 :             if(! detail::maybe_octet(
     135          65 :                 &bytes[2*(7-n)]))
     136             :             {
     137             :                 // invalid octet
     138          10 :                 BOOST_URL_RETURN_EC(
     139             :                     grammar::error::invalid);
     140             :             }
     141             :             // rewind the h16 and
     142             :             // parse it as ipv4
     143          55 :             it = prev;
     144             :             auto rv1 = grammar::parse(
     145          55 :                 it, end, ipv4_address_rule);
     146          55 :             if(! rv1)
     147          22 :                 return rv1.error();
     148          33 :             auto v4 = *rv1;
     149             :             auto const b4 =
     150          33 :                 v4.to_bytes();
     151          33 :             bytes[2*(7-n)+0] = b4[0];
     152          33 :             bytes[2*(7-n)+1] = b4[1];
     153          33 :             bytes[2*(7-n)+2] = b4[2];
     154          33 :             bytes[2*(7-n)+3] = b4[3];
     155          33 :             --n;
     156          33 :             break;
     157             :         }
     158             :         auto d =
     159         364 :             grammar::hexdig_value(*it);
     160         364 :         if( b != -1 &&
     161             :             d < 0)
     162             :         {
     163             :             // ends in "::"
     164          25 :             break;
     165             :         }
     166         339 :         if(! c)
     167             :         {
     168         335 :             prev = it;
     169             :             rv = grammar::parse(
     170             :                 it, end,
     171         335 :                 detail::h16_rule);
     172         335 :             if(! rv)
     173          20 :                 return rv.error();
     174         315 :             bytes[2*(8-n)+0] = rv->hi;
     175         315 :             bytes[2*(8-n)+1] = rv->lo;
     176         315 :             --n;
     177         315 :             if(n == 0)
     178           1 :                 break;
     179         314 :             c = true;
     180         314 :             continue;
     181             :         }
     182             :         // ':' divides a word
     183           4 :         BOOST_URL_RETURN_EC(
     184             :             grammar::error::invalid);
     185        1036 :     }
     186         195 :     if(b == -1)
     187          50 :         return ipv6_address{bytes};
     188         145 :     if(b == n)
     189             :     {
     190             :         // "::" last
     191          34 :         auto const i =
     192          34 :             2 * (7 - n);
     193          34 :         std::memset(
     194          34 :             &bytes[i],
     195          34 :             0, 16 - i);
     196             :     }
     197         111 :     else if(b == 7)
     198             :     {
     199             :         // "::" first
     200          45 :         auto const i =
     201          45 :             2 * (b - n);
     202          90 :         std::memmove(
     203          45 :             &bytes[16 - i],
     204          45 :             &bytes[2],
     205             :             i);
     206          45 :         std::memset(
     207          45 :             &bytes[0],
     208          45 :             0, 16 - i);
     209             :     }
     210             :     else
     211             :     {
     212             :         // "::" in middle
     213          66 :         auto const i0 =
     214          66 :             2 * (7 - b);
     215          66 :         auto const i1 =
     216          66 :             2 * (b - n);
     217         132 :         std::memmove(
     218          66 :             &bytes[16 - i1],
     219          66 :             &bytes[i0 + 2],
     220             :             i1);
     221          66 :         std::memset(
     222          66 :             &bytes[i0],
     223          66 :             0, 16 - (i0 + i1));
     224             :     }
     225         145 :     return ipv6_address{bytes};
     226             : }
     227             : 
     228             : } // urls
     229             : } // boost
     230             : 
     231             : #endif

Generated by: LCOV version 1.15