Converting Bytes to Base64 and Back Again

The JavaScript code shown below converts an array of bytes to a string Base64 digits, converts that string back into bytes, and converts that second set of bytes into another, hopefully identical string of Base64 digits. It displays all of these values in the web browser window so that the user can verify that the values match.

To see the code in action, copy it into an .html file and open that file in a web browser that runs JavaScript.

<html>
<body>
<script type='text/javascript'>

// main

function main()
{
	var newline = "<br />";

	var dataToEncodeAsBytes = 
	[
		65, 66, 67, 68, 69, 70,
	];

	document.write
	(
		"dataToEncodeAsBytes = " 
		+ ByteHelper.convertBytesToString(dataToEncodeAsBytes)
		+ newline
	);

	var dataEncodedAsBase64String = Base64Encoder.convertBytesToBase64String
	(
		dataToEncodeAsBytes						
	);

	document.write
	(
		"dataEncodedAsBase64String = "
		+ dataEncodedAsBase64String
		+ newline
	);

	var dataDecodedFromBase64StringAsBytes = Base64Encoder.convertBase64StringToBytes
	(
		dataEncodedAsBase64String
	);

	document.write
	(
		"dataDecodedFromBase64StringAsBytes = " 
		+ ByteHelper.convertBytesToString(dataDecodedFromBase64StringAsBytes)
		+ newline
	);

	var dataReencodedAsBase64String = Base64Encoder.convertBytesToBase64String
	(
		dataDecodedFromBase64StringAsBytes
	);

	document.write
	(
		"dataReencodedAsBase64String = "
		+ dataReencodedAsBase64String 
		+ newline
	);
}

function Base64Encoder()
{
	// do nothing
}
{
	// constants

	Base64Encoder.Base64DigitsAsString = 
		"ABCDEFGHIJKLMNOPQRSTUVWXYZ" 
		+ "abcdefghijklmnopqrstuvwxyz"
		+ "0123456789"
		+ "+/";

	// static methods

	Base64Encoder.convertBase64StringToBytes = function(base64StringToConvert)
	{
		// Convert each four sets of six bits (sextets, or Base 64 digits)
		// into three sets of eight bits (octets, or bytes)

		var returnBytes = [];

		var bytesPerSet = 3;
		var base64DigitsPerSet = 4;
		var base64DigitsAll = Base64Encoder.Base64DigitsAsString;

		var indexOfEqualsSign = base64StringToConvert.indexOf("=");

		if (indexOfEqualsSign >= 0)
		{
			base64StringToConvert = base64StringToConvert.substring
			(
				0, 
				indexOfEqualsSign
			);
		}

		var numberOfBase64DigitsToConvert = base64StringToConvert.length;

		var numberOfFullSets = Math.floor
		(
			numberOfBase64DigitsToConvert 
			/ base64DigitsPerSet
		);

		var numberOfBase64DigitsInFullSets = 
			numberOfFullSets * base64DigitsPerSet;

		var numberOfBase64DigitsLeftAtEnd = 
			numberOfBase64DigitsToConvert - numberOfBase64DigitsInFullSets;

		for (var s = 0; s < numberOfFullSets; s++)
		{
			var d = s * base64DigitsPerSet;

			var valueToEncode = 
				(base64DigitsAll.indexOf(base64StringToConvert[d]) << 18)
				| (base64DigitsAll.indexOf(base64StringToConvert[d + 1]) << 12)
				| (base64DigitsAll.indexOf(base64StringToConvert[d + 2]) << 6)
				| (base64DigitsAll.indexOf(base64StringToConvert[d + 3]));

			returnBytes.push((valueToEncode >> 16) & 0xFF);
			returnBytes.push((valueToEncode >> 8) & 0xFF);
			returnBytes.push((valueToEncode) & 0xFF);
		}	

		var d = numberOfFullSets * base64DigitsPerSet;

		if (numberOfBase64DigitsLeftAtEnd > 0)
		{
			var valueToEncode = 0;

			for (var i = 0; i < numberOfBase64DigitsLeftAtEnd; i++)
			{
				var digit = base64StringToConvert[d + i];
				var digitValue = base64DigitsAll.indexOf(digit);
				var bitsToShift = (18 - 6 * i);
				var digitValueShifted = digitValue << bitsToShift;

				valueToEncode = 
					valueToEncode
					| digitValueShifted;
			}


			for (var b = 0; b < numberOfBase64DigitsLeftAtEnd; b++)
			{
				var byteValue = (valueToEncode >> (16 - 8 * b)) & 0xFF;
				if (byteValue > 0)
				{
					returnBytes.push(byteValue);
				}
			}
		}

		return returnBytes;
	}

	Base64Encoder.convertBytesToBase64String = function(bytesToEncode)
	{
		// Encode each three sets of eight bits (octets, or bytes)
		// as four sets of six bits (sextets, or Base 64 digits)

		var returnString = "";

		var bytesPerSet = 3;
		var base64DigitsPerSet = 4;
		var base64DigitsAsString = Base64Encoder.Base64DigitsAsString;

		var numberOfBytesToEncode = bytesToEncode.length;
		var numberOfFullSets = Math.floor(numberOfBytesToEncode / bytesPerSet);
		var numberOfBytesInFullSets = numberOfFullSets * bytesPerSet;
		var numberOfBytesLeftAtEnd = numberOfBytesToEncode - numberOfBytesInFullSets;

		for (var s = 0; s < numberOfFullSets; s++)
		{
			var b = s * bytesPerSet;

			var valueToEncode = 
				(bytesToEncode[b] << 16)
				| (bytesToEncode[b + 1] << 8)
				| (bytesToEncode[b + 2]);

			returnString += base64DigitsAsString[((valueToEncode & 0xFC0000) >>> 18)];
			returnString += base64DigitsAsString[((valueToEncode & 0x03F000) >>> 12)];
			returnString += base64DigitsAsString[((valueToEncode & 0x000FC0) >>> 6)];
			returnString += base64DigitsAsString[((valueToEncode & 0x00003F))];
		}	

		var b = numberOfFullSets * bytesPerSet;

		if (numberOfBytesLeftAtEnd == 1)
		{
			var valueToEncode = (bytesToEncode[b] << 16);

			returnString += base64DigitsAsString[((valueToEncode & 0xFC0000) >>> 18)];
			returnString += base64DigitsAsString[((valueToEncode & 0x03F000) >>> 12)];
			returnString += "==";
		}		
		else if (numberOfBytesLeftAtEnd == 2)
		{
			var valueToEncode = 
				(bytesToEncode[b] << 16)
				| (bytesToEncode[b + 1] << 8);

			returnString += base64DigitsAsString[((valueToEncode & 0xFC0000) >>> 18)];
			returnString += base64DigitsAsString[((valueToEncode & 0x03F000) >>> 12)];
			returnString += base64DigitsAsString[((valueToEncode & 0x000FC0) >>> 6)];
			returnString += "=";
		}

		return returnString;
	}
}

function ByteHelper()
{
	// do nothing
}
{
	// static methods

	ByteHelper.convertBytesToString = function(bytesToConvert)
	{
		var returnString = "[";

		for (var b = 0; b < bytesToConvert.length; b++)
		{
			var byteToConvert = bytesToConvert[b];
			returnString += byteToConvert + ","
		}

		returnString += "]";

		return returnString;
	}
}

//run

main();

</script>
</body>
</html>
This entry was posted in Uncategorized and tagged , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s