text: #include <assert.h>
  #include <err.h>
  #include <errno.h>
  #include <limits.h>
  #include <stddef.h>
  #include <stdint.h>
  #include <stdlib.h>
  #include <string.h>
  
  #include "base64.h"
  
  /* Base64 routines adapted from https://www.libsodium.org */
  
  /*
   * ISC License
   *
   * Copyright (c) 2013-2017
   * Frank Denis <j at pureftpd dot org>
   *
   * Permission to use, copy, modify, and/or distribute this software for any
   * purpose with or without fee is hereby granted, provided that the above
   * copyright notice and this permission notice appear in all copies.
   *
   * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
   * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
   * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
   * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
   * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
   * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
   * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
   */
  
  /* Derived from original code by CodesInChaos */
  static char *bin2hex(
    char* const                hex,
    const size_t               hex_maxlen,
    const unsigned char* const bin,
    const size_t               bin_len) {
  
    size_t i = (size_t) 0U;
    unsigned int x;
    int b;
    int c;
  
    if ((bin_len >= SIZE_MAX / 2) || (hex_maxlen <= bin_len * 2U) ) {
      //errx(2, "bin2hex length wrong");
      return 0;
    }
    while (i < bin_len) {
      c = bin[i] & 0xf;
      b = bin[i] >> 4;
      x = (unsigned char) (87U + c + (((c - 10U) >> 8) & ~38U)) << 8
          | (unsigned char) (87U + b + (((b - 10U) >> 8) & ~38U));
      hex[i * 2U] = (char) x;
      x >>= 8;
      hex[i * 2U + 1U] = (char) x;
      i++;
    }
    hex[i * 2U] = 0U;
  
    return hex;
  }
  
  static int hex2bin(
    unsigned char* const bin,
    const size_t         bin_maxlen,
    const char* const    hex,
    const size_t         hex_len,
    const char* const    ignore,
    size_t* const        bin_len,
    const char** const   hex_end) {
  
    size_t bin_pos = (size_t) 0U;
    size_t hex_pos = (size_t) 0U;
    int ret = 0;
    unsigned char c;
    unsigned char c_acc = 0U;
    unsigned char c_alpha0, c_alpha;
    unsigned char c_num0, c_num;
    unsigned char c_val;
    unsigned char state = 0U;
  
    while (hex_pos < hex_len) {
      c = (unsigned char) hex[hex_pos];
      c_num = c ^ 48U;
      c_num0 = (c_num - 10U) >> 8;
      c_alpha = (c & ~32U) - 55U;
      c_alpha0 = ((c_alpha - 10U) ^ (c_alpha - 16U)) >> 8;
      if ((c_num0 | c_alpha0) == 0U) {
        if ((ignore != 0) && (state == 0U) && (strchr(ignore, c) != 0) ) {
          hex_pos++;
          continue;
        }
        break;
      }
      c_val = (c_num0 & c_num) | (c_alpha0 & c_alpha);
      if (bin_pos >= bin_maxlen) {
        ret = -1;
        errno = ERANGE;
        break;
      }
      if (state == 0U) {
        c_acc = c_val * 16U;
      } else {
        bin[bin_pos++] = c_acc | c_val;
      }
      state = ~state;
      hex_pos++;
    }
    if (state != 0U) {
      hex_pos--;
      errno = EINVAL;
      ret = -1;
    }
    if (ret != 0) {
      bin_pos = (size_t) 0U;
    }
    if (hex_end != 0) {
      *hex_end = &hex[hex_pos];
    } else if (hex_pos != hex_len) {
      errno = EINVAL;
      ret = -1;
    }
    if (bin_len != 0) {
      *bin_len = bin_pos;
    }
    return ret;
  }
  
  /*
   * Some macros for constant-time comparisons. These work over values in
   * the 0..255 range. Returned value is 0x00 on "false", 0xFF on "true".
   *
   * Original code by Thomas Pornin.
   */
  #define EQ(x, y) \
    ((((0U - ((unsigned int) (x) ^ (unsigned int) (y))) >> 8) & 0xFF) ^ 0xFF)
  #define GT(x, y) ((((unsigned int) (y) - (unsigned int) (x)) >> 8) & 0xFF)
  #define GE(x, y) (GT(y, x) ^ 0xFF)
  #define LT(x, y) GT(y, x)
  #define LE(x, y) GE(y, x)
  
  static int b64_byte_to_char(unsigned int x) {
    return (LT(x, 26) & (x + 'A'))
           | (GE(x, 26) & LT(x, 52) & (x + ('a' - 26)))
           | (GE(x, 52) & LT(x, 62) & (x + ('0' - 52))) | (EQ(x, 62) & '+')
           | (EQ(x, 63) & '/');
  }
  
  static unsigned int b64_char_to_byte(int c) {
    const unsigned int x
      = (GE(c, 'A') & LE(c, 'Z') & (c - 'A'))
        | (GE(c, 'a') & LE(c, 'z') & (c - ('a' - 26)))
        | (GE(c, '0') & LE(c, '9') & (c - ('0' - 52))) | (EQ(c, '+') & 62)
        | (EQ(c, '/') & 63);
  
    return x | (EQ(x, 0) & (EQ(c, 'A') ^ 0xFF));
  }
  
  static int b64_byte_to_urlsafe_char(unsigned int x) {
    return (LT(x, 26) & (x + 'A'))
           | (GE(x, 26) & LT(x, 52) & (x + ('a' - 26)))
           | (GE(x, 52) & LT(x, 62) & (x + ('0' - 52))) | (EQ(x, 62) & '-')
           | (EQ(x, 63) & '_');
  }
  
  static unsigned int b64_urlsafe_char_to_byte(int c) {
    const unsigned x
      = (GE(c, 'A') & LE(c, 'Z') & (c - 'A'))
        | (GE(c, 'a') & LE(c, 'z') & (c - ('a' - 26)))
        | (GE(c, '0') & LE(c, '9') & (c - ('0' - 52))) | (EQ(c, '-') & 62)
        | (EQ(c, '_') & 63);
  
    return x | (EQ(x, 0) & (EQ(c, 'A') ^ 0xFF));
  }
  
  #define VARIANT_NO_PADDING_MASK 0x2U
  #define VARIANT_URLSAFE_MASK    0x4U
  
  static int base64_check_variant(const int variant) {
    if ((((unsigned int) variant) & ~0x6U) != 0x1U) {
      //errx(2, "base64_check_variant: invalid variant");
      return 0;
    }
    return 1;
  }
  
  size_t iwn_base64_encoded_len(const size_t bin_len, const int variant) {
    if (!base64_check_variant(variant)) {
      return 0;
    }
    return base64_ENCODED_LEN(bin_len, variant);
  }
  
  char *iwn_base64_encode(
    char* const                b64,
    const size_t               b64_maxlen,
    size_t                    *out_b64_len,
    const unsigned char* const bin,
    const size_t               bin_len,
    const int                  variant) {
  
    size_t acc_len = (size_t) 0;
    size_t b64_pos = (size_t) 0;
    size_t bin_pos = (size_t) 0;
    unsigned int acc = 0U;
  
    size_t b64_len = iwn_base64_encoded_len(bin_len, variant);
    if (b64_maxlen < b64_len) {
      *out_b64_len = 0;
      return 0;
    }
    if ((((unsigned int) variant) & VARIANT_URLSAFE_MASK) != 0U) {
      while (bin_pos < bin_len) {
        acc = (acc << 8) + bin[bin_pos++];
        acc_len += 8;
        while (acc_len >= 6) {
          acc_len -= 6;
          b64[b64_pos++] = (char) b64_byte_to_urlsafe_char((acc >> acc_len) & 0x3F);
        }
      }
      if (acc_len > 0) {
        b64[b64_pos++] = (char) b64_byte_to_urlsafe_char((acc << (6 - acc_len)) & 0x3F);
      }
    } else {
      while (bin_pos < bin_len) {
        acc = (acc << 8) + bin[bin_pos++];
        acc_len += 8;
        while (acc_len >= 6) {
          acc_len -= 6;
          b64[b64_pos++] = (char) b64_byte_to_char((acc >> acc_len) & 0x3F);
        }
      }
      if (acc_len > 0) {
        b64[b64_pos++] = (char) b64_byte_to_char((acc << (6 - acc_len)) & 0x3F);
      }
    }
    while (b64_pos < b64_len - 1) {
      b64[b64_pos++] = '=';
    }
    b64[b64_len - 1] = 0U;
    *out_b64_len = b64_len;
    return b64;
  }
  
  static int _base642bin_skip_padding(
    const char* const b64,
    const size_t b64_len, size_t* const b64_pos_p,
    const char* const ignore, size_t padding_len) {
    int c;
  
    while (padding_len > 0) {
      if (*b64_pos_p >= b64_len) {
        errno = ERANGE;
        return -1;
      }
      c = b64[*b64_pos_p];
      if (c == '=') {
        padding_len--;
      } else if ((ignore == 0) || (strchr(ignore, c) == 0) ) {
        errno = EINVAL;
        return -1;
      }
      (*b64_pos_p)++;
    }
    return 0;
  }
  
  int iwn_base64_decode(
    unsigned char* const bin, const size_t bin_maxlen,
    const char* const b64, const size_t b64_len,
    const char* const ignore, size_t* const bin_len,
    const char** const b64_end, const int variant) {
  
    size_t acc_len = (size_t) 0;
    size_t b64_pos = (size_t) 0;
    size_t bin_pos = (size_t) 0;
  
    unsigned int is_urlsafe;
    int ret = 0;
    unsigned int acc = 0U;
    unsigned int d;
    char c;
  
    if (!base64_check_variant(variant)) {
      return -1;
    }
    is_urlsafe = ((unsigned int) variant) & VARIANT_URLSAFE_MASK;
    while (b64_pos < b64_len) {
      c = b64[b64_pos];
      if (is_urlsafe) {
        d = b64_urlsafe_char_to_byte(c);
      } else {
        d = b64_char_to_byte(c);
      }
      if (d == 0xFF) {
        if ((ignore != 0) && (strchr(ignore, c) != 0) ) {
          b64_pos++;
          continue;
        }
        break;
      }
      acc = (acc << 6) + d;
      acc_len += 6;
      if (acc_len >= 8) {
        acc_len -= 8;
        if (bin_pos >= bin_maxlen) {
          errno = ERANGE;
          ret = -1;
          break;
        }
        bin[bin_pos++] = (acc >> acc_len) & 0xFF;
      }
      b64_pos++;
    }
    if ((acc_len > 4U) || ((acc & ((1U << acc_len) - 1U)) != 0U) ) {
      ret = -1;
    } else if (  (ret == 0)
              && ((((unsigned int) variant) & VARIANT_NO_PADDING_MASK) == 0U) ) {
      ret = _base642bin_skip_padding(b64, b64_len, &b64_pos, ignore,
                                     acc_len / 2);
    }
    if (ret != 0) {
      bin_pos = (size_t) 0U;
    } else if (ignore != 0) {
      while (b64_pos < b64_len && strchr(ignore, b64[b64_pos]) != 0) {
        b64_pos++;
      }
    }
    if (b64_end != 0) {
      *b64_end = &b64[b64_pos];
    } else if (b64_pos != b64_len) {
      errno = EINVAL;
      ret = -1;
    }
    if (bin_len != 0) {
      *bin_len = bin_pos;
    }
    return ret;
  }
  
  char *iwn_base64_encode_url(const void *buf, size_t buf_len, size_t *out_len) {
    size_t encoded_len = base64_ENCODED_LEN(buf_len, base64_VARIANT_URLSAFE_NO_PADDING);
    char *encoded = calloc(1, encoded_len);
    if (!encoded) {
      return 0;
    }
    return iwn_base64_encode(encoded, encoded_len, out_len, buf, buf_len, base64_VARIANT_URLSAFE_NO_PADDING);
  }
  