text: /**
   * Copyright (c) 2006-2018 Apple Inc. All rights reserved.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   **/
  
  #include "base64.h"
  
  #include <stdlib.h>
  #include <string.h>
  
  // base64 tables
  static char basis_64[] =
      "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
  static signed char index_64[128] =
  {
      -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
      -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
      -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63,
      52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1,-1,-1,-1,
      -1, 0, 1, 2,  3, 4, 5, 6,  7, 8, 9,10, 11,12,13,14,
      15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1,
      -1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
      41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1
  };
  #define CHAR64(c)  (((c) < 0 || (c) > 127) ? -1 : index_64[(c)])
  
  // base64_encode    :    base64 encode
  //
  // value            :    data to encode
  // vlen             :    length of data
  // (result)         :    new char[] - c-str of result
  char *base64_encode(const unsigned char *value, size_t vlen)
  {
      char *result = (char *)malloc((vlen * 4) / 3 + 5);
      if (result == NULL)
      {
          return NULL;
      }
      char *out = result;
      while (vlen >= 3)
      {
          *out++ = basis_64[value[0] >> 2];
          *out++ = basis_64[((value[0] << 4) & 0x30) | (value[1] >> 4)];
          *out++ = basis_64[((value[1] << 2) & 0x3C) | (value[2] >> 6)];
          *out++ = basis_64[value[2] & 0x3F];
          value += 3;
          vlen -= 3;
      }
      if (vlen > 0)
      {
          *out++ = basis_64[value[0] >> 2];
          unsigned char oval = (value[0] << 4) & 0x30;
          if (vlen > 1) oval |= value[1] >> 4;
          *out++ = basis_64[oval];
          *out++ = (vlen < 2) ? '=' : basis_64[(value[1] << 2) & 0x3C];
          *out++ = '=';
      }
      *out = '\0';
  
      return result;
  }
  
  // base64_decode    :    base64 decode
  //
  // value            :    c-str to decode
  // rlen             :    length of decoded result
  // (result)         :    new unsigned char[] - decoded result
  unsigned char *base64_decode(const char *value, size_t *rlen)
  {
      *rlen = 0;
      int c1, c2, c3, c4;
  
      size_t vlen = strlen(value);
      unsigned char *result =(unsigned char *)malloc((vlen * 3) / 4 + 1);
      if (result == NULL)
      {
          return NULL;
      }
      unsigned char *out = result;
  
      while (1) {
          if (value[0]==0) {
              return result;
          }
          c1 = value[0];
          if (CHAR64(c1) == -1) {
              goto base64_decode_error;;
          }
          c2 = value[1];
          if (CHAR64(c2) == -1) {
              goto base64_decode_error;;
          }
          c3 = value[2];
          if ((c3 != '=') && (CHAR64(c3) == -1)) {
              goto base64_decode_error;;
          }
          c4 = value[3];
          if ((c4 != '=') && (CHAR64(c4) == -1)) {
              goto base64_decode_error;;
          }
  
          value += 4;
          *out++ = (CHAR64(c1) << 2) | (CHAR64(c2) >> 4);
          *rlen += 1;
  
          if (c3 != '=') {
              *out++ = ((CHAR64(c2) << 4) & 0xf0) | (CHAR64(c3) >> 2);
              *rlen += 1;
  
              if (c4 != '=') {
                  *out++ = ((CHAR64(c3) << 6) & 0xc0) | CHAR64(c4);
                  *rlen += 1;
              }
          }
      }
  
  base64_decode_error:
      *result = 0;
      *rlen = 0;
  
      return result;
  }
  