neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

base64.c (6247B)


      1 #include <assert.h>
      2 #include <stddef.h>
      3 #include <stdint.h>
      4 #include <string.h>
      5 
      6 #include "auto/config.h"  // IWYU pragma: keep
      7 #include "nvim/ascii_defs.h"
      8 #include "nvim/base64.h"
      9 #include "nvim/memory.h"
     10 
     11 #ifdef HAVE_BE64TOH
     12 # include ENDIAN_INCLUDE_FILE
     13 #endif
     14 
     15 #include "base64.c.generated.h"
     16 
     17 static const char alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
     18 
     19 // Indices are 1-based because we use 0 to indicate a letter that is not part of the alphabet
     20 static const uint8_t char_to_index[256] = {
     21  ['A'] = 1,  ['B'] = 2,  ['C'] = 3, ['D'] = 4,  ['E'] = 5,  ['F'] = 6,  ['G'] = 7,  ['H'] = 8,
     22  ['I'] = 9,  ['J'] = 10, ['K'] = 11, ['L'] = 12, ['M'] = 13, ['N'] = 14, ['O'] = 15, ['P'] = 16,
     23  ['Q'] = 17, ['R'] = 18, ['S'] = 19, ['T'] = 20, ['U'] = 21, ['V'] = 22, ['W'] = 23, ['X'] = 24,
     24  ['Y'] = 25, ['Z'] = 26, ['a'] = 27, ['b'] = 28, ['c'] = 29, ['d'] = 30, ['e'] = 31, ['f'] = 32,
     25  ['g'] = 33, ['h'] = 34, ['i'] = 35, ['j'] = 36, ['k'] = 37, ['l'] = 38, ['m'] = 39, ['n'] = 40,
     26  ['o'] = 41, ['p'] = 42, ['q'] = 43, ['r'] = 44, ['s'] = 45, ['t'] = 46, ['u'] = 47, ['v'] = 48,
     27  ['w'] = 49, ['x'] = 50, ['y'] = 51, ['z'] = 52, ['0'] = 53, ['1'] = 54, ['2'] = 55, ['3'] = 56,
     28  ['4'] = 57, ['5'] = 58, ['6'] = 59, ['7'] = 60, ['8'] = 61, ['9'] = 62, ['+'] = 63, ['/'] = 64,
     29 };
     30 
     31 #ifndef HAVE_BE64TOH
     32 static inline uint64_t vim_htobe64(uint64_t host_64bits)
     33 {
     34 # ifdef ORDER_BIG_ENDIAN
     35  return host_64bits;
     36 # else
     37  uint8_t *buf = (uint8_t *)&host_64bits;
     38  uint64_t ret = 0;
     39  for (size_t i = 8; i; i--) {
     40    ret |= ((uint64_t)buf[i - 1]) << ((8 - i) * 8);
     41  }
     42  return ret;
     43 # endif
     44 }
     45 
     46 static inline uint32_t vim_htobe32(uint32_t host_32bits)
     47 {
     48 # ifdef ORDER_BIG_ENDIAN
     49  return host_32bits;
     50 # else
     51  uint8_t *buf = (uint8_t *)&host_32bits;
     52  uint32_t ret = 0;
     53  for (size_t i = 4; i; i--) {
     54    ret |= ((uint32_t)buf[i - 1]) << ((4 - i) * 8);
     55  }
     56  return ret;
     57 # endif
     58 }
     59 # define htobe64 vim_htobe64
     60 # define htobe32 vim_htobe32
     61 #endif
     62 
     63 /// Encode a string using Base64.
     64 ///
     65 /// @param src String to encode
     66 /// @param src_len Length of the string
     67 /// @return Base64 encoded string
     68 char *base64_encode(const char *src, size_t src_len)
     69  FUNC_ATTR_NONNULL_ALL
     70 {
     71  assert(src != NULL);
     72 
     73  const size_t out_len = ((src_len + 2) / 3) * 4;
     74  char *dest = xmalloc(out_len + 1);
     75 
     76  size_t src_i = 0;
     77  size_t out_i = 0;
     78 
     79  const uint8_t *s = (const uint8_t *)src;
     80 
     81  // Read 8 bytes at a time as much as we can
     82  for (; src_i + 7 < src_len; src_i += 6) {
     83    uint64_t bits_h;
     84    memcpy(&bits_h, &s[src_i], sizeof(uint64_t));
     85    const uint64_t bits_be = htobe64(bits_h);
     86    dest[out_i + 0] = alphabet[(bits_be >> 58) & 0x3F];
     87    dest[out_i + 1] = alphabet[(bits_be >> 52) & 0x3F];
     88    dest[out_i + 2] = alphabet[(bits_be >> 46) & 0x3F];
     89    dest[out_i + 3] = alphabet[(bits_be >> 40) & 0x3F];
     90    dest[out_i + 4] = alphabet[(bits_be >> 34) & 0x3F];
     91    dest[out_i + 5] = alphabet[(bits_be >> 28) & 0x3F];
     92    dest[out_i + 6] = alphabet[(bits_be >> 22) & 0x3F];
     93    dest[out_i + 7] = alphabet[(bits_be >> 16) & 0x3F];
     94    out_i += sizeof(uint64_t);
     95  }
     96 
     97  for (; src_i + 3 < src_len; src_i += 3) {
     98    uint32_t bits_h;
     99    memcpy(&bits_h, &s[src_i], sizeof(uint32_t));
    100    const uint32_t bits_be = htobe32(bits_h);
    101    dest[out_i + 0] = alphabet[(bits_be >> 26) & 0x3F];
    102    dest[out_i + 1] = alphabet[(bits_be >> 20) & 0x3F];
    103    dest[out_i + 2] = alphabet[(bits_be >> 14) & 0x3F];
    104    dest[out_i + 3] = alphabet[(bits_be >> 8) & 0x3F];
    105    out_i += sizeof(uint32_t);
    106  }
    107 
    108  if (src_i + 2 < src_len) {
    109    dest[out_i + 0] = alphabet[s[src_i] >> 2];
    110    dest[out_i + 1] = alphabet[((s[src_i] & 0x3) << 4) | (s[src_i + 1] >> 4)];
    111    dest[out_i + 2] = alphabet[(s[src_i + 1] & 0xF) << 2 | (s[src_i + 2] >> 6)];
    112    dest[out_i + 3] = alphabet[(s[src_i + 2] & 0x3F)];
    113    out_i += 4;
    114  } else if (src_i + 1 < src_len) {
    115    dest[out_i + 0] = alphabet[s[src_i] >> 2];
    116    dest[out_i + 1] = alphabet[((s[src_i] & 0x3) << 4) | (s[src_i + 1] >> 4)];
    117    dest[out_i + 2] = alphabet[(s[src_i + 1] & 0xF) << 2];
    118    out_i += 3;
    119  } else if (src_i < src_len) {
    120    dest[out_i + 0] = alphabet[s[src_i] >> 2];
    121    dest[out_i + 1] = alphabet[(s[src_i] & 0x3) << 4];
    122    out_i += 2;
    123  }
    124 
    125  for (; out_i < out_len; out_i++) {
    126    dest[out_i] = '=';
    127  }
    128 
    129  dest[out_len] = NUL;
    130 
    131  return dest;
    132 }
    133 
    134 /// Decode a Base64 encoded string.
    135 ///
    136 /// The returned string is NOT null-terminated, because the decoded string may
    137 /// contain embedded NULLs. Use the output parameter out_lenp to determine the
    138 /// length of the returned string.
    139 ///
    140 /// @param src Base64 encoded string
    141 /// @param src_len Length of {src}
    142 /// @param [out] out_lenp Returns the length of the decoded string
    143 /// @return Decoded string
    144 char *base64_decode(const char *src, size_t src_len, size_t *out_lenp)
    145  FUNC_ATTR_NONNULL_ALL
    146 {
    147  assert(src != NULL);
    148  assert(out_lenp != NULL);
    149 
    150  char *dest = NULL;
    151 
    152  if (src_len % 4 != 0) {
    153    goto invalid;
    154  }
    155 
    156  size_t out_len = (src_len / 4) * 3;
    157  if (src_len >= 1 && src[src_len - 1] == '=') {
    158    out_len--;
    159  }
    160  if (src_len >= 2 && src[src_len - 2] == '=') {
    161    out_len--;
    162  }
    163 
    164  const uint8_t *s = (const uint8_t *)src;
    165 
    166  dest = xmalloc(out_len);
    167 
    168  int acc = 0;
    169  int acc_len = 0;
    170  size_t out_i = 0;
    171  size_t src_i = 0;
    172  int leftover_i = -1;
    173 
    174  for (; src_i < src_len; src_i++) {
    175    const uint8_t c = s[src_i];
    176    const uint8_t d = char_to_index[c];
    177    if (d == 0) {
    178      if (c == '=') {
    179        leftover_i = (int)src_i;
    180        break;
    181      }
    182      goto invalid;
    183    }
    184 
    185    acc = ((acc << 6) & 0xFFF) + (d - 1);
    186    acc_len += 6;
    187    if (acc_len >= 8) {
    188      acc_len -= 8;
    189      dest[out_i] = (char)(acc >> acc_len);
    190      out_i += 1;
    191    }
    192  }
    193 
    194  if (acc_len > 4 || ((acc & ((1 << acc_len) - 1)) != 0)) {
    195    goto invalid;
    196  }
    197 
    198  if (leftover_i > -1) {
    199    int padding_len = acc_len / 2;
    200    int padding_chars = 0;
    201    for (; (size_t)leftover_i < src_len; leftover_i++) {
    202      const uint8_t c = s[leftover_i];
    203      if (c != '=') {
    204        goto invalid;
    205      }
    206      padding_chars += 1;
    207    }
    208 
    209    if (padding_chars != padding_len) {
    210      goto invalid;
    211    }
    212  }
    213 
    214  *out_lenp = out_len;
    215 
    216  return dest;
    217 
    218 invalid:
    219  if (dest) {
    220    xfree((void *)dest);
    221  }
    222 
    223  *out_lenp = 0;
    224 
    225  return NULL;
    226 }