LCOV - code coverage report
Current view: top level - src - base64.c (source / functions) Hit Total Coverage
Test: Code coverage Lines: 54 54 100.0 %
Date: 2026-07-14 21:28:07 Functions: 5 5 100.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 35 36 97.2 %

           Branch data     Line data    Source code
       1                 :            : // Copyright 2011-2026 David Robillard <d@drobilla.net>
       2                 :            : // SPDX-License-Identifier: ISC
       3                 :            : 
       4                 :            : #include "char_utils.h"
       5                 :            : #include "macros.h"
       6                 :            : #include "read_utils.h"
       7                 :            : #include "result.h"
       8                 :            : #include "write_utils.h"
       9                 :            : 
      10                 :            : #include <exess/exess.h>
      11                 :            : 
      12                 :            : #include <assert.h>
      13                 :            : #include <stdint.h>
      14                 :            : #include <string.h>
      15                 :            : 
      16                 :            : // Map a 6-bit base64 group to a base64 digit
      17                 :            : EXESS_PURE_FUNC static inline uint8_t
      18                 :      43647 : map(const unsigned group)
      19                 :            : {
      20         [ -  + ]:      43647 :   assert(group < 64);
      21                 :            : 
      22                 :            :   // See <http://tools.ietf.org/html/rfc3548#section-3>.
      23                 :      43647 :   EXESS_CONSTEXPR uint8_t b64_map[] =
      24                 :            :     "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
      25                 :            : 
      26                 :      43647 :   return b64_map[group];
      27                 :            : }
      28                 :            : 
      29                 :            : // Unmap a base64 digit to the numeric value used for decoding
      30                 :            : static inline uint8_t
      31                 :      65490 : unmap(const uint8_t in)
      32                 :            : {
      33                 :            :   /* Table indexed by encoded characters that contains the numeric value used
      34                 :            :      for decoding, shifted up by 47 to be in the range of printable ASCII.  A
      35                 :            :      '$' is a placeholder for characters not in the base64 alphabet. */
      36                 :      65490 :   EXESS_CONSTEXPR uint8_t b64_unmap[] =
      37                 :            :     "$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$m$$$ncdefghijkl$$$$$$"
      38                 :            :     "$/0123456789:;<=>?@ABCDEFGH$$$$$$IJKLMNOPQRSTUVWXYZ[\\]^_`ab$$$$"
      39                 :            :     "$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$"
      40                 :            :     "$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$";
      41                 :            : 
      42                 :      65490 :   return (uint8_t)(b64_unmap[in] - 47U);
      43                 :            : }
      44                 :            : 
      45                 :            : EXESS_NONBLOCKING size_t
      46                 :         13 : exess_decoded_base64_size(const size_t length)
      47                 :            : {
      48                 :         13 :   return ((length * 3) / 4) + 2;
      49                 :            : }
      50                 :            : 
      51                 :            : EXESS_NONBLOCKING ExessVariableResult
      52                 :        296 : exess_read_base64(const char* const str, const size_t out_size, void* const out)
      53                 :            : {
      54                 :        296 :   uint8_t* const       uout = (uint8_t*)out;
      55                 :        296 :   const uint8_t* const ustr = (const uint8_t*)str;
      56                 :        296 :   size_t               i    = skip_whitespace(str);
      57                 :        296 :   size_t               o    = 0U;
      58                 :            : 
      59         [ +  + ]:      11307 :   while (str[i]) {
      60                 :            :     // Read next chunk of 4 input characters
      61                 :      11026 :     uint8_t in[] = {'=', '=', '=', '='};
      62         [ +  + ]:      55100 :     for (size_t j = 0; j < 4; ++j) {
      63         [ +  + ]:      44086 :       if (!is_base64(str[i])) {
      64                 :         12 :         return VRESULT(EXESS_EXPECTED_BASE64, i, o);
      65                 :            :       }
      66                 :            : 
      67                 :      44074 :       in[j] = ustr[i++];
      68                 :      44074 :       i += skip_whitespace(str + i);
      69                 :            :     }
      70                 :            : 
      71   [ +  +  +  +  :      11014 :     if (in[0] == '=' || in[1] == '=' || (in[2] == '=' && in[3] != '=')) {
             +  +  +  + ]
      72                 :          3 :       return VRESULT(EXESS_BAD_VALUE, i, o);
      73                 :            :     }
      74                 :            : 
      75         [ +  + ]:      11011 :     const size_t n_bytes = 1U + (in[2] != '=') + (in[3] != '=');
      76         [ +  + ]:      11011 :     if (o + n_bytes > out_size) {
      77                 :          9 :       o += n_bytes;
      78                 :            :     } else {
      79                 :      11002 :       const uint8_t a1 = (uint8_t)(unmap(in[0]) << 2U);
      80                 :      11002 :       const uint8_t a2 = unmap(in[1]) >> 4U;
      81                 :            : 
      82                 :      11002 :       uout[o++] = a1 | a2;
      83                 :            : 
      84         [ +  + ]:      11002 :       if (in[2] != '=') {
      85                 :      10916 :         const uint8_t b1 = (uint8_t)(((unsigned)unmap(in[1]) << 4U) & 0xF0U);
      86                 :      10916 :         const uint8_t b2 = unmap(in[2]) >> 2U;
      87                 :            : 
      88                 :      10916 :         uout[o++] = b1 | b2;
      89                 :            :       }
      90                 :            : 
      91         [ +  + ]:      11002 :       if (in[3] != '=') {
      92                 :      10827 :         const uint8_t c1 = (uint8_t)(((unsigned)unmap(in[2]) << 6U) & 0xC0U);
      93                 :      10827 :         const uint8_t c2 = unmap(in[3]);
      94                 :            : 
      95                 :      10827 :         uout[o++] = c1 | c2;
      96                 :            :       }
      97                 :            :     }
      98                 :            :   }
      99                 :            : 
     100         [ +  + ]:        281 :   return VRESULT(o <= out_size ? EXESS_SUCCESS : EXESS_NO_SPACE, i, o);
     101                 :            : }
     102                 :            : 
     103                 :            : EXESS_NONBLOCKING ExessResult
     104                 :        523 : exess_write_base64(const size_t      data_size,
     105                 :            :                    const void* const data,
     106                 :            :                    const size_t      buf_size,
     107                 :            :                    char* const       buf)
     108                 :            : {
     109                 :        523 :   const size_t length = (data_size + 2) / 3 * 4;
     110   [ +  +  +  + ]:        523 :   if (!buf || length >= buf_size) {
     111                 :        259 :     return end_write(EXESS_NO_SPACE, buf_size, buf, length);
     112                 :            :   }
     113                 :            : 
     114                 :        264 :   uint8_t* const out = (uint8_t*)buf;
     115                 :            : 
     116                 :        264 :   size_t o = 0;
     117         [ +  + ]:      11241 :   for (size_t i = 0; i < data_size; i += 3, o += 4) {
     118                 :      10977 :     uint8_t      in[4] = {0, 0, 0, 0};
     119                 :      10977 :     const size_t n_in  = MIN(3, data_size - i);
     120                 :      10977 :     memcpy(in, (const uint8_t*)data + i, n_in);
     121                 :            : 
     122                 :      10977 :     out[o]     = map(in[0] >> 2U);
     123                 :      10977 :     out[o + 1] = map(((in[0] & 0x03U) << 4U) | ((in[1] & 0xF0U) >> 4U));
     124         [ +  + ]:      10977 :     out[o + 2] =
     125                 :      10890 :       ((n_in > 1U) ? map(((in[1] & 0x0FU) << 2U) | ((in[2] & 0xC0U) >> 6U))
     126                 :            :                    : '=');
     127                 :            : 
     128         [ +  + ]:      10977 :     out[o + 3] = ((n_in > 2U) ? map(in[2] & 0x3FU) : '=');
     129                 :            :   }
     130                 :            : 
     131                 :        264 :   return end_write(EXESS_SUCCESS, buf_size, buf, o);
     132                 :            : }

Generated by: LCOV version 1.16