text: /*
   *  Base64Transcoder.c
   *  Base64Test
   *
   *  Created by Jonathan Wight on Tue Mar 18 2003.
   *  Copyright (c) 2003 Toxic Software. All rights reserved.
   *
   *  Permission is hereby granted, free of charge, to any person obtaining a copy
   *  of this software and associated documentation files (the "Software"), to deal
   *  in the Software without restriction, including without limitation the rights
   *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   *  copies of the Software, and to permit persons to whom the Software is
   *  furnished to do so, subject to the following conditions:
   *
   *  The above copyright notice and this permission notice shall be included in
   *  all copies or substantial portions of the Software.
   *
   *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
   *  THE SOFTWARE.
   *
   */
  
  #include "Base64Transcoder.h"
  
  #include <math.h>
  #include <string.h>
  
  const u_int8_t kBase64EncodeTable[64] = {
  /*  0 */ 'A',	/*  1 */ 'B',	/*  2 */ 'C',	/*  3 */ 'D', 
  /*  4 */ 'E',	/*  5 */ 'F',	/*  6 */ 'G',	/*  7 */ 'H', 
  /*  8 */ 'I',	/*  9 */ 'J',	/* 10 */ 'K',	/* 11 */ 'L', 
  /* 12 */ 'M',	/* 13 */ 'N',	/* 14 */ 'O',	/* 15 */ 'P', 
  /* 16 */ 'Q',	/* 17 */ 'R',	/* 18 */ 'S',	/* 19 */ 'T', 
  /* 20 */ 'U',	/* 21 */ 'V',	/* 22 */ 'W',	/* 23 */ 'X', 
  /* 24 */ 'Y',	/* 25 */ 'Z',	/* 26 */ 'a',	/* 27 */ 'b', 
  /* 28 */ 'c',	/* 29 */ 'd',	/* 30 */ 'e',	/* 31 */ 'f', 
  /* 32 */ 'g',	/* 33 */ 'h',	/* 34 */ 'i',	/* 35 */ 'j', 
  /* 36 */ 'k',	/* 37 */ 'l',	/* 38 */ 'm',	/* 39 */ 'n', 
  /* 40 */ 'o',	/* 41 */ 'p',	/* 42 */ 'q',	/* 43 */ 'r', 
  /* 44 */ 's',	/* 45 */ 't',	/* 46 */ 'u',	/* 47 */ 'v', 
  /* 48 */ 'w',	/* 49 */ 'x',	/* 50 */ 'y',	/* 51 */ 'z', 
  /* 52 */ '0',	/* 53 */ '1',	/* 54 */ '2',	/* 55 */ '3', 
  /* 56 */ '4',	/* 57 */ '5',	/* 58 */ '6',	/* 59 */ '7', 
  /* 60 */ '8',	/* 61 */ '9',	/* 62 */ '+',	/* 63 */ '/'
  };
  
  /*
   -1 = Base64 end of data marker.
   -2 = White space (tabs, cr, lf, space)
   -3 = Noise (all non whitespace, non-base64 characters) 
   -4 = Dangerous noise
   -5 = Illegal noise (null byte)
   */
  
  const int8_t kBase64DecodeTable[128] = {
  /* 0x00 */ -5, 	/* 0x01 */ -3, 	/* 0x02 */ -3, 	/* 0x03 */ -3,
  /* 0x04 */ -3, 	/* 0x05 */ -3, 	/* 0x06 */ -3, 	/* 0x07 */ -3,
  /* 0x08 */ -3, 	/* 0x09 */ -2, 	/* 0x0a */ -2, 	/* 0x0b */ -2,
  /* 0x0c */ -2, 	/* 0x0d */ -2, 	/* 0x0e */ -3, 	/* 0x0f */ -3,
  /* 0x10 */ -3, 	/* 0x11 */ -3, 	/* 0x12 */ -3, 	/* 0x13 */ -3,
  /* 0x14 */ -3, 	/* 0x15 */ -3, 	/* 0x16 */ -3, 	/* 0x17 */ -3,
  /* 0x18 */ -3, 	/* 0x19 */ -3, 	/* 0x1a */ -3, 	/* 0x1b */ -3,
  /* 0x1c */ -3, 	/* 0x1d */ -3, 	/* 0x1e */ -3, 	/* 0x1f */ -3,
  /* ' ' */ -2,	/* '!' */ -3,	/* '"' */ -3,	/* '#' */ -3,
  /* '$' */ -3,	/* '%' */ -3,	/* '&' */ -3,	/* ''' */ -3,
  /* '(' */ -3,	/* ')' */ -3,	/* '*' */ -3,	/* '+' */ 62,
  /* ',' */ -3,	/* '-' */ -3,	/* '.' */ -3,	/* '/' */ 63,
  /* '0' */ 52,	/* '1' */ 53,	/* '2' */ 54,	/* '3' */ 55,
  /* '4' */ 56,	/* '5' */ 57,	/* '6' */ 58,	/* '7' */ 59,
  /* '8' */ 60,	/* '9' */ 61,	/* ':' */ -3,	/* ';' */ -3,
  /* '<' */ -3,	/* '=' */ -1,	/* '>' */ -3,	/* '?' */ -3,
  /* '@' */ -3,	/* 'A' */ 0,	/* 'B' */  1,	/* 'C' */  2,
  /* 'D' */  3,	/* 'E' */  4,	/* 'F' */  5,	/* 'G' */  6,
  /* 'H' */  7,	/* 'I' */  8,	/* 'J' */  9,	/* 'K' */ 10,
  /* 'L' */ 11,	/* 'M' */ 12,	/* 'N' */ 13,	/* 'O' */ 14,
  /* 'P' */ 15,	/* 'Q' */ 16,	/* 'R' */ 17,	/* 'S' */ 18,
  /* 'T' */ 19,	/* 'U' */ 20,	/* 'V' */ 21,	/* 'W' */ 22,
  /* 'X' */ 23,	/* 'Y' */ 24,	/* 'Z' */ 25,	/* '[' */ -3,
  /* '\' */ -3,	/* ']' */ -3,	/* '^' */ -3,	/* '_' */ -3,
  /* '`' */ -3,	/* 'a' */ 26,	/* 'b' */ 27,	/* 'c' */ 28,
  /* 'd' */ 29,	/* 'e' */ 30,	/* 'f' */ 31,	/* 'g' */ 32,
  /* 'h' */ 33,	/* 'i' */ 34,	/* 'j' */ 35,	/* 'k' */ 36,
  /* 'l' */ 37,	/* 'm' */ 38,	/* 'n' */ 39,	/* 'o' */ 40,
  /* 'p' */ 41,	/* 'q' */ 42,	/* 'r' */ 43,	/* 's' */ 44,
  /* 't' */ 45,	/* 'u' */ 46,	/* 'v' */ 47,	/* 'w' */ 48,
  /* 'x' */ 49,	/* 'y' */ 50,	/* 'z' */ 51,	/* '{' */ -3,
  /* '|' */ -3,	/* '}' */ -3,	/* '~' */ -3,	/* 0x7f */ -3
  };
  
  const u_int8_t kBits_00000011 = 0x03;
  const u_int8_t kBits_00001111 = 0x0F;
  const u_int8_t kBits_00110000 = 0x30;
  const u_int8_t kBits_00111100 = 0x3C;
  const u_int8_t kBits_00111111 = 0x3F;
  const u_int8_t kBits_11000000 = 0xC0;
  const u_int8_t kBits_11110000 = 0xF0;
  const u_int8_t kBits_11111100 = 0xFC;
  
  size_t EstimateBas64EncodedDataSize(size_t inDataSize)
  {
  	size_t theEncodedDataSize = (int)ceil(inDataSize / 3.0) * 4;
  	theEncodedDataSize = theEncodedDataSize / 72 * 74 + theEncodedDataSize % 72;
  	return(theEncodedDataSize);
  }
  
  size_t EstimateBas64DecodedDataSize(size_t inDataSize)
  {
  	size_t theDecodedDataSize = (int)ceil(inDataSize / 4.0) * 3;
  	//theDecodedDataSize = theDecodedDataSize / 72 * 74 + theDecodedDataSize % 72;
  	return(theDecodedDataSize);
  }
  
  bool Base64EncodeData(const void *inInputData, size_t inInputDataSize, char *outOutputData, size_t *ioOutputDataSize)
  {
  	size_t theEncodedDataSize = EstimateBas64EncodedDataSize(inInputDataSize);
  	if (*ioOutputDataSize < theEncodedDataSize)
  		return(false);
  	*ioOutputDataSize = theEncodedDataSize;
  	const u_int8_t *theInPtr = (const u_int8_t *)inInputData;
  	u_int32_t theInIndex = 0, theOutIndex = 0;
  	for (; theInIndex < (inInputDataSize / 3) * 3; theInIndex += 3)
  	{
  		outOutputData[theOutIndex++] = kBase64EncodeTable[(theInPtr[theInIndex] & kBits_11111100) >> 2];
  		outOutputData[theOutIndex++] = kBase64EncodeTable[(theInPtr[theInIndex] & kBits_00000011) << 4 | (theInPtr[theInIndex + 1] & kBits_11110000) >> 4];
  		outOutputData[theOutIndex++] = kBase64EncodeTable[(theInPtr[theInIndex + 1] & kBits_00001111) << 2 | (theInPtr[theInIndex + 2] & kBits_11000000) >> 6];
  		outOutputData[theOutIndex++] = kBase64EncodeTable[(theInPtr[theInIndex + 2] & kBits_00111111) >> 0];
  		if (theOutIndex % 74 == 72)
  		{
  			outOutputData[theOutIndex++] = '\r';
  			outOutputData[theOutIndex++] = '\n';
  		}
  	}
  	const size_t theRemainingBytes = inInputDataSize - theInIndex;
  	if (theRemainingBytes == 1)
  	{
  		outOutputData[theOutIndex++] = kBase64EncodeTable[(theInPtr[theInIndex] & kBits_11111100) >> 2];
  		outOutputData[theOutIndex++] = kBase64EncodeTable[(theInPtr[theInIndex] & kBits_00000011) << 4 | (0 & kBits_11110000) >> 4];
  		outOutputData[theOutIndex++] = '=';
  		outOutputData[theOutIndex++] = '=';
  		if (theOutIndex % 74 == 72)
  		{
  			outOutputData[theOutIndex++] = '\r';
  			outOutputData[theOutIndex++] = '\n';
  		}
  	}
  	else if (theRemainingBytes == 2)
  	{
  		outOutputData[theOutIndex++] = kBase64EncodeTable[(theInPtr[theInIndex] & kBits_11111100) >> 2];
  		outOutputData[theOutIndex++] = kBase64EncodeTable[(theInPtr[theInIndex] & kBits_00000011) << 4 | (theInPtr[theInIndex + 1] & kBits_11110000) >> 4];
  		outOutputData[theOutIndex++] = kBase64EncodeTable[(theInPtr[theInIndex + 1] & kBits_00001111) << 2 | (0 & kBits_11000000) >> 6];
  		outOutputData[theOutIndex++] = '=';
  		if (theOutIndex % 74 == 72)
  		{
  			outOutputData[theOutIndex++] = '\r';
  			outOutputData[theOutIndex++] = '\n';
  		}
  	}
  	return(true);
  }
  
  bool Base64DecodeData(const void *inInputData, size_t inInputDataSize, void *ioOutputData, size_t *ioOutputDataSize)
  {
  	memset(ioOutputData, '.', *ioOutputDataSize);
  	
  	size_t theDecodedDataSize = EstimateBas64DecodedDataSize(inInputDataSize);
  	if (*ioOutputDataSize < theDecodedDataSize)
  		return(false);
  	*ioOutputDataSize = 0;
  	const u_int8_t *theInPtr = (const u_int8_t *)inInputData;
  	u_int8_t *theOutPtr = (u_int8_t *)ioOutputData;
  	size_t theInIndex = 0, theOutIndex = 0;
  	u_int8_t theOutputOctet = '\0';
  	size_t theSequence = 0;
  	for (; theInIndex < inInputDataSize; )
  	{
  		int8_t theSextet = 0;
  		
  		int8_t theCurrentInputOctet = theInPtr[theInIndex];
  		theSextet = kBase64DecodeTable[theCurrentInputOctet];
  		if (theSextet == -1)
  			break;
  		while (theSextet == -2)
  		{
  			theCurrentInputOctet = theInPtr[++theInIndex];
  			theSextet = kBase64DecodeTable[theCurrentInputOctet];
  		}
  		while (theSextet == -3)
  		{
  			theCurrentInputOctet = theInPtr[++theInIndex];
  			theSextet = kBase64DecodeTable[theCurrentInputOctet];
  		}
  		if (theSequence == 0)
  		{
  			theOutputOctet = (theSextet >= 0 ? theSextet : 0) << 2 & kBits_11111100;
  		}
  		else if (theSequence == 1)
  		{
  			theOutputOctet |= (theSextet >- 0 ? theSextet : 0) >> 4 & kBits_00000011;
  			theOutPtr[theOutIndex++] = theOutputOctet;
  		}
  		else if (theSequence == 2)
  		{
  			theOutputOctet = (theSextet >= 0 ? theSextet : 0) << 4 & kBits_11110000;
  		}
  		else if (theSequence == 3)
  		{
  			theOutputOctet |= (theSextet >= 0 ? theSextet : 0) >> 2 & kBits_00001111;
  			theOutPtr[theOutIndex++] = theOutputOctet;
  		}
  		else if (theSequence == 4)
  		{
  			theOutputOctet = (theSextet >= 0 ? theSextet : 0) << 6 & kBits_11000000;
  		}
  		else if (theSequence == 5)
  		{
  			theOutputOctet |= (theSextet >= 0 ? theSextet : 0) >> 0 & kBits_00111111;
  			theOutPtr[theOutIndex++] = theOutputOctet;
  		}
  		theSequence = (theSequence + 1) % 6;
  		if (theSequence != 2 && theSequence != 4)
  			theInIndex++;
  	}
  	*ioOutputDataSize = theOutIndex;
  	return(true);
  }
  