LCOV - code coverage report
Current view: top level - spb/json - base64.h (source / functions) Coverage Total Hit
Test: coverage.info Lines: 98.9 % 95 94
Test Date: 2025-05-23 14:18:13 Functions: 100.0 % 8 8

            Line data    Source code
       1              : /***************************************************************************\
       2              : * Name        : base64 library for json                                     *
       3              : * Description : RFC 4648 base64 decoder and encoder                         *
       4              : * Author      : antonin.kriz@gmail.com                                      *
       5              : * ------------------------------------------------------------------------- *
       6              : * This is free software; you can redistribute it and/or modify it under the *
       7              : * terms of the MIT license. A copy of the license can be found in the file  *
       8              : * "LICENSE" at the root of this distribution.                               *
       9              : \***************************************************************************/
      10              : 
      11              : #pragma once
      12              : 
      13              : #include "../concepts.h"
      14              : #include <cstddef>
      15              : #include <cstdint>
      16              : #include <span>
      17              : #include <stdexcept>
      18              : 
      19              : namespace spb::json::detail
      20              : {
      21              : template < typename ostream >
      22         5241 : static inline void base64_encode( ostream & output, std::span< const std::byte > input )
      23              : {
      24              :     static constexpr char encode_table[] =
      25              :         "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
      26              : 
      27         5241 :     const auto * p_char = reinterpret_cast< const uint8_t * >( input.data( ) );
      28              :     //
      29              :     //- +3 means 3 bytes are being processed in one iteration (3 * 8 = 24 bits)
      30              :     //
      31      2189072 :     for( size_t idx = 3; idx <= input.size( ); idx += 3 )
      32              :     {
      33      2183831 :         auto temp = uint32_t( *p_char++ ) << 16U;
      34      2183831 :         temp += uint32_t( *p_char++ ) << 8U;
      35      2183831 :         temp += ( *p_char++ );
      36      2183831 :         output.write( encode_table[ ( temp & 0x00FC0000U ) >> 18U ] );
      37      2183831 :         output.write( encode_table[ ( temp & 0x0003F000U ) >> 12U ] );
      38      2183831 :         output.write( encode_table[ ( temp & 0x00000FC0U ) >> 6U ] );
      39      2183831 :         output.write( encode_table[ ( temp & 0x0000003FU ) ] );
      40              :     }
      41         5241 :     switch( input.size( ) % 3 )
      42              :     {
      43         1706 :     case 1:
      44              :     {
      45         1706 :         auto temp = uint32_t( *p_char++ ) << 16U;
      46         1706 :         output.write( encode_table[ ( temp & 0x00FC0000U ) >> 18U ] );
      47         1706 :         output.write( encode_table[ ( temp & 0x0003F000U ) >> 12U ] );
      48         1706 :         output.write( '=' );
      49         1706 :         output.write( '=' );
      50              :     }
      51         1706 :     break;
      52         1806 :     case 2:
      53              :     {
      54         1806 :         auto temp = uint32_t( *p_char++ ) << 16U;
      55         1806 :         temp += uint32_t( *p_char++ ) << 8U;
      56         1806 :         output.write( encode_table[ ( temp & 0x00FC0000 ) >> 18 ] );
      57         1806 :         output.write( encode_table[ ( temp & 0x0003F000 ) >> 12 ] );
      58         1806 :         output.write( encode_table[ ( temp & 0x00000FC0 ) >> 6 ] );
      59         1806 :         output.write( '=' );
      60              :     }
      61         1806 :     break;
      62              :     }
      63         5241 : }
      64              : 
      65              : template < typename istream >
      66         2635 : static inline void base64_decode_string( spb::detail::proto_field_bytes auto & output,
      67              :                                          istream & stream )
      68              : {
      69              :     static constexpr uint8_t decode_table[ 256 ] = {
      70              :         128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
      71              :         128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
      72              :         128, 128, 128, 128, 128, 128, 128, 62,  128, 128, 128, 63,  52,  53,  54,  55,  56,  57,
      73              :         58,  59,  60,  61,  128, 128, 128, 128, 128, 128, 128, 0,   1,   2,   3,   4,   5,   6,
      74              :         7,   8,   9,   10,  11,  12,  13,  14,  15,  16,  17,  18,  19,  20,  21,  22,  23,  24,
      75              :         25,  128, 128, 128, 128, 128, 128, 26,  27,  28,  29,  30,  31,  32,  33,  34,  35,  36,
      76              :         37,  38,  39,  40,  41,  42,  43,  44,  45,  46,  47,  48,  49,  50,  51,  128, 128, 128,
      77              :         128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
      78              :         128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
      79              :         128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
      80              :         128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
      81              :         128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
      82              :         128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
      83              :         128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
      84              :         128, 128, 128, 128
      85              :     };
      86              : 
      87              :     /*static constexpr uint8_t decode_table2[] = {
      88              :         62, 128, 128, 128, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 128, 128, 128, 128, 128, 128,
      89              :         128, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
      90              :     24, 25, 128, 128, 128, 128, 128, 128, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
      91              :     40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
      92              :     };*/
      93              : 
      94              :     if constexpr( spb::detail::proto_field_bytes_resizable< decltype( output ) > )
      95              :     {
      96         2617 :         output.clear( );
      97              :     }
      98         2635 :     if( stream.current_char( ) != '"' ) [[unlikely]]
      99              :     {
     100            9 :         throw std::runtime_error( "expecting '\"'" );
     101              :     }
     102              : 
     103         2626 :     stream.consume_current_char( false );
     104         2626 :     if( stream.consume( '"' ) )
     105              :     {
     106            5 :         return;
     107              :     }
     108         2621 :     auto mask = uint8_t( 0 );
     109              : 
     110        18715 :     for( auto out_index = size_t( 0 );; )
     111              :     {
     112        18715 :         auto view      = stream.view( UINT32_MAX );
     113        18714 :         auto length    = view.find( '"' );
     114        18714 :         auto end_found = length < view.npos;
     115        18714 :         if( ( end_found && length % 4 != 0 ) || view.size( ) <= 4 ) [[unlikely]]
     116              :         {
     117            7 :             throw std::runtime_error( "invalid base64" );
     118              :         }
     119        18707 :         length = std::min( length, view.size( ) );
     120              : 
     121              :         //- align to 4 bytes
     122        18707 :         auto aligned_length = length & ~3;
     123        18707 :         if( aligned_length > 4 ) [[likely]]
     124              :         {
     125        18659 :             auto out_length = ( ( aligned_length - 4 ) / 4 ) * 3;
     126        18659 :             view            = view.substr( 0, aligned_length );
     127              : 
     128              :             if constexpr( spb::detail::proto_field_bytes_resizable< decltype( output ) > )
     129              :             {
     130        18628 :                 output.resize( output.size( ) + out_length );
     131              :             }
     132              :             else
     133              :             {
     134           31 :                 if( out_length > ( output.size( ) - out_index ) )
     135              :                 {
     136            0 :                     throw std::runtime_error( "too large base64" );
     137              :                 }
     138              :             }
     139              : 
     140        18659 :             auto * p_out      = output.data( ) + out_index;
     141        18659 :             const auto * p_in = reinterpret_cast< const uint8_t * >( view.data( ) );
     142        18659 :             const auto * p_end =
     143        18659 :                 p_in + aligned_length - 4;//- exclude the last 4 chars (possible padding)
     144              : 
     145      1110557 :             while( p_in < p_end ) [[likely]]
     146              :             {
     147      1091898 :                 uint8_t v0 = decode_table[ *p_in++ ];
     148      1091898 :                 uint8_t v1 = decode_table[ *p_in++ ];
     149      1091898 :                 uint8_t v2 = decode_table[ *p_in++ ];
     150      1091898 :                 uint8_t v3 = decode_table[ *p_in++ ];
     151      1091898 :                 mask |= ( v0 | v1 | v2 | v3 );
     152              : 
     153      1091898 :                 *p_out++ = std::byte( ( v0 << 2 ) | ( v1 >> 4 ) );
     154      1091898 :                 *p_out++ = std::byte( ( v1 << 4 ) | ( v2 >> 2 ) );
     155      1091898 :                 *p_out++ = std::byte( ( v2 << 6 ) | ( v3 ) );
     156              : 
     157      1091898 :                 out_index += 3;
     158              :             }
     159        18659 :             auto consumed_bytes = p_in - reinterpret_cast< const uint8_t * >( view.data( ) );
     160        18659 :             view.remove_prefix( consumed_bytes );
     161        18659 :             stream.skip( consumed_bytes );
     162              :         }
     163              : 
     164        18707 :         if( end_found )
     165              :         {
     166              :             //- handle padding
     167         2613 :             const auto * p_in = reinterpret_cast< const uint8_t * >( view.data( ) );
     168              : 
     169         2613 :             uint8_t v0 = decode_table[ *p_in++ ];
     170         2613 :             uint8_t v1 = decode_table[ *p_in++ ];
     171         2613 :             auto i1    = *p_in++;
     172         2613 :             uint8_t v2 = i1 == '=' ? 0 : decode_table[ i1 ];
     173         2613 :             auto i2    = *p_in++;
     174         2613 :             uint8_t v3 = i2 == '=' ? 0 : decode_table[ i2 ];
     175         2613 :             mask |= ( v0 | v1 | v2 | v3 );
     176         2613 :             mask |= ( ( i1 == '=' ) & ( i2 != '=' ) ) ? 128 : 0;
     177         2613 :             if( mask & 128 ) [[unlikely]]
     178              :             {
     179            4 :                 throw std::runtime_error( "invalid base64" );
     180              :             }
     181              : 
     182         2609 :             auto padding_size   = ( i1 == '=' ? 1 : 0 ) + ( i2 == '=' ? 1 : 0 );
     183         2609 :             auto consumed_bytes = 3 - padding_size;
     184              :             //- +1 is for "
     185         2609 :             stream.skip( 5 );
     186              :             if constexpr( spb::detail::proto_field_bytes_resizable< decltype( output ) > )
     187              :             {
     188         2591 :                 output.resize( output.size( ) + consumed_bytes );
     189              :             }
     190              :             else
     191              :             {
     192           18 :                 if( output.size( ) != out_index + consumed_bytes )
     193              :                 {
     194            3 :                     throw std::runtime_error( "too large base64" );
     195              :                 }
     196              :             }
     197         2606 :             auto * p_out = output.data( ) + out_index;
     198         2606 :             if( padding_size == 0 )
     199              :             {
     200          860 :                 *p_out++ = std::byte( ( v0 << 2 ) | ( v1 >> 4 ) );
     201          860 :                 *p_out++ = std::byte( ( v1 << 4 ) | ( v2 >> 2 ) );
     202          860 :                 *p_out++ = std::byte( ( v2 << 6 ) | ( v3 ) );
     203              :             }
     204         1746 :             else if( padding_size == 1 )
     205              :             {
     206          892 :                 *p_out++ = std::byte( ( v0 << 2 ) | ( v1 >> 4 ) );
     207          892 :                 *p_out++ = std::byte( ( v1 << 4 ) | ( v2 >> 2 ) );
     208              :             }
     209          854 :             else if( padding_size == 2 )
     210              :             {
     211          854 :                 *p_out++ = std::byte( ( v0 << 2 ) | ( v1 >> 4 ) );
     212              :             }
     213         2606 :             return;
     214              :         }
     215              :     }
     216              : }
     217              : }// namespace spb::json::detail
        

Generated by: LCOV version 2.0-1