Tuesday 7 August 2018

Swapping variables in C# Unmanaged / Managed

This article will shortly present two ways of swapping variables in C#. Specifically, these two ways are only both available for value types and structs and unmanaged types, while the managed swap by using ref is available for values, structs and managed structs. The method UnsafeSwap swaps two variables by using unsafe code and pointers. By passing in the address of the two variables to swap, it is possible to use the dereference operator * to not only copy into a temporary variable but also use the syntax *a = *b to exchange the address the variable is pointing to, effectively we swap to variables with their content, here int is used. Another way is do a SafeSwap where we pass in the ref of the variables and just change their contents. Note that if you want to exchange two strings, you must pin the pointer of the chars using the fixed keyword.

unsafe void Main()
{
 int one = 20;
 int two = 30;

 Console.WriteLine("Before swap, one: {0}, two: {1}", one, two);

 UnsafeSwap(&one, &two);
 
 Console.WriteLine("Unsafe swap, one: {0}, two: {1}", one, two); 
 
 SafeSwap(ref one, ref two);

 Console.WriteLine("Safe swap back again, one: {0}, two: {1}", one, two);

}

unsafe void UnsafeSwap(int* a, int* b){
 int temp = *a;
 *a = *b;
 *b = temp; 
}

void SafeSwap(ref int a, ref int b){
 int temp = a;
 a = b;
 b = temp;
}


To exchange two string variables, we must use the fixed keyword and of course only be able to exchange the characters from the char array consisting the first variable to be exchanged with the char array of the second variable. In addition, the string in C# is at a low level actually a character array null terminated with the '\0' sequence, at least when a string is treated as a character array.. The following method will exchange two string variables using unsafe / unmanaged code. Note that the two strings differ in length, so the last character is not exchanged. That is - our string is actually a character array and consists of multiple memory addresses.
unsafe void Main()
{
 fixed (char* one = "hello??")
 {
  fixed (char* two = "world!")
  {
   char* ptrone = one;
   char* ptrtwo = two;

   while (*ptrone != '\0' && *ptrtwo != '\0')
   {
    Swap(ptrone, ptrtwo);
    Console.WriteLine("one: {0}", GetString(one));
    Console.WriteLine("two: {0}", GetString(two));
    ++ptrone;
    ++ptrtwo;
   }

  }
 }
 
}

unsafe void Swap(char* first, char* second){
 char temp = *first;
 *first = *second;
 *second = temp;
}

unsafe string GetString(char* input){
 char* ptr = input; 
 var sw = new StringBuilder();
 while (*ptr != '\0')
 {
  sw.Append(ptr->ToString());
  ++ptr;
 }
 return sw.ToString();
}

The output in Linqpad gives as the exchange of the string or actually a null terminated character array progresses:

one: wello?? two: horld! one: wollo?? two: herld! one: worlo?? two: helld! one: worlo?? two: helld! one: world?? two: hello! one: world!? two: hello?

Monday 6 August 2018

Simple Base64 encoder in C#

Just read a bit about Base64 encoding and decided to try out writing my own in Linqpad for strings! The way you Base64 encode is to treat each char in the input string as a byte value with groups of six bytes (this byte value is zero padded left) and then mapping the 2^6 values into a Base64 table and outputting the corresponding values into a resulting string. Note that you also pad the Base64 string with '=' char to ensure the entire bit length is divisible with three. That is why Base64 strings in .NET always have 0,1 or 2 '=' chars at the end. The characters used in the Base64 encoding is in .NET the chars [A-z] and [0-9] plus + and slash /.

void Main()
{
 string wordToBase64Encode = "Hello world from base64 encoder. This is a sample input string, does it work?"; 
 string wordBase64EncodedAccordingToNet = Convert.ToBase64String(Encoding.ASCII.GetBytes(wordToBase64Encode)).Dump();
 string wordBase64EncodedAccordingToCustomBase64 = wordToBase64Encode.ToBase64String().Dump();
 (("Are the two strings equal?: ") + string.Equals(wordBase64EncodedAccordingToNet, wordBase64EncodedAccordingToCustomBase64)).Dump();
 
}

public static class LowLevelBase64Extensions {

    private static readonly char[] _base64Table =
 {
  'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 
  'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
  'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 
  'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
  '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/',
 };

 public static string ToBase64String(this string toBeEncoded){
     byte[] toBeEncodedByteArray = Encoding.ASCII.GetBytes(toBeEncoded);
  string toBeEncodedBinaryString = 
  string.Join("", 
   toBeEncodedByteArray.Select(b => (Convert.ToString(b, 2).PadLeft(8, '0'))));
   
  //toBeEncodedBinaryString.Length.Dump();
   
  int padCharacters = toBeEncodedBinaryString.Length % 3;
  //toBeEncoded.Dump();
       
  StringWriter sw = new StringWriter();

  for (var i = 0; i < toBeEncodedBinaryString.Length; i = i + 6)
  {
   string encodingToMap =
    toBeEncodedBinaryString.Substring(i, Math.Min(toBeEncodedBinaryString.Length - i, 6))
    .PadRight(6, '0');
   //encodingToMap.Dump();
   byte encodingToMapByte = Convert.ToByte(encodingToMap, 2);
   char encodingMapped = _base64Table[(int)encodingToMapByte];
   sw.Write(encodingMapped);
  }
  
  sw.Write(new String('=', padCharacters));
  
  //Check 24 bits / 3 byte padding 
  return sw.ToString();
 }
}

Note - the casting to int of the encodingToMapByte also works if you cast the byte representation to short or byte. I have compared this custom Base64Encoder with .NET's own Convert.ToBase64String(), and they give equal strings in the use cases I have tried out. Make note that this is just playing around, .NET's own method is optimized for speed. This is more to show what happens with the input string to generate a Base64 encoded string. You can use this code as a basis for custom encoding shemes like Base32. I have used Encoding.ASCII.GetBytes to look up the ASCII code. So this implementation primarily supports input strings in languages using ASCII and Extended ASCII characters, at least that is what I have tested it with. The output in Linqpad shows the following corresponding results:

Sunday 5 August 2018

Consuming video from WCF as a Base64 encoded strings

This article follows up our quest of loading up video byte arrays from WCF efficiently, but now the byte array is converted into a Base64 encoded string. You can start by cloning the repository I have prepared from here:
git clone https://toreaurstad@bitbucket.org/toreaurstad/wcfaudiostreamdemo.git git fetch && git checkout VideBase64String_05082018
Base64 encoded strings take six bits from the byte array and designates is as a char where the char can be one of 64 characters, in MIME implementation it is [A-z][0-9] and + and /, which is 64 different characters that the six bytes are encoded into. This means that 24 bits can be represented as three Base64 encodeded characters, that is 3 bytes in our byte array can be represented as FOUR base64 encoded chars. This 3:4 ratio is the rationale behind the MTOM optimization in WCF (to be discussed later). However, recap from the previous article where we downloaded a video sizing 4 MB on disk and Fiddler reporting it to be about 3 times larger. This is bloated data that I want to explore if we can fix up a bit. The bloated data is because I have managed not to truly enforce the XMLHttpRequest to send the data through the WCF REST binding (webHttpBinding) as true binary data - binary data is still sent as a string object to Javascript. This is sad, and I want to try to speed this up and avoid bloated data. To get this size down, we can start by using a Base64 encoded string instead in our XmlHttpRequest. First off, we define a service contract operation to return our string with Base64 data:
        [OperationContract]
        [WebGet(UriTemplate = "mediabytes/{videofile}")]
        string GetVideoAsBase64(string videofile);
Now our Service Implementation looks like this:

using System;
using System.IO;
using System.ServiceModel.Activation;
using System.ServiceModel.Web;
using System.Web;
using WcfStreamAudioDemo.Common;

namespace WcfStreamAudioDemo.Host
{

    [AspNetCompatibilityRequirements (RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
    public class VideoService : IVideoServiceContract
    {

        public Stream GetVideo(string videofile)
        {
            return GetVideoStream(videofile, ".mp4");
        }

        public string GetVideoAsBase64(string videofile)
        {
            Stream videoStream = GetVideoStream(videofile, ".mp4");

            byte[] buffer = new byte[32*1024];
            int count;
            var memoryStream = new MemoryStream();

            while ((count = videoStream.Read(buffer, 0, buffer.Length)) > 0)
            {
                memoryStream.Write(buffer, 0, count);
            }

            return Convert.ToBase64String(memoryStream.ToArray()); 
        }

        private Stream GetVideoStream(string videofile, string extension)
        {
            string mediaFolder = HttpContext.Current.Server.MapPath("~/media");
            string videoFileFullPath = Path.Combine(mediaFolder, videofile) + extension;
            return File.OpenRead(videoFileFullPath);
        }

    }
}


The client side code will now retrieve a Base64Encoded string. The following client side scripts loads up our video:

 <script type="text/javascript">

        window.onload = init;
      
        var source; //Video buffer 

        function init() {
            loadByteArray('http://myserver/WcfStreamAudioDemo.Host/VideoService.svc/mediabytes/sintel_trailer-480p');
        }

        function base64ToArrayBuffer(base64) {
            var binaryString =  window.atob(base64);
            var len = binaryString.length;
            var bytes = new Uint8Array( len );
            for (var i = 0; i < len; i++)        {
                bytes[i] = binaryString.charCodeAt(i);
            }
            return bytes.buffer;
        }

        function loadByteArray(url) {
            
            var request = new XMLHttpRequest();

            //request.overrideMimeType('text\/plain; charset=x-user-defined');
            //request.responseType = 'blob';

            request.open('GET', url, true);
            //request.setRequestHeader("Content-Type", "video/mp4");


            request.onload = function() {
                //console.log(request.response);
                debugger;

                var responseData = request.response;
                if (responseData === undefined || responseData === null)
                    return;

                responseData = responseData.slice(0, -9);
                responseData = responseData.substring(68);

                var videoByteArrayView = base64ToArrayBuffer(responseData);
            
                var blob = new Blob([videoByteArrayView], { type: "text/plain;charset=utf-8" });

                var blobUrl = URL.createObjectURL(blob);

                var videoCtrlFedByByteArray = document.getElementById("videoCtrlFedByByteArray");

                videoCtrlFedByByteArray.setAttribute("src", blobUrl);


            } //request.onload 

            request.send();

        }

    </script>

There is still a lot of manual Js scripting here to prepare the video here, I will try to explain. We have now managed to drastically reduce the bloated data of our original 4.4 MB video down from 15+ MB to just 5.8 MB using Base64 encoded string. The retrieved Base64 encoded string is first decoded with the Js atob() function ("ASCII to BINARY"). A Uint8Array is initialized with the length of this decoded binary string and the binary string is iterated by using the charCodeAt method, returning the ArrayBuffer inside the constructed Uint8Array. Note that I got the Base64 encoded string as a XML first, which is why I do some chopping off the string using slice and substring. Anyways, after getting an ArrayBuffer from the Uint8Array's buffer property, we can construct a Blob object (Binary large object) and create an object url and then set the "src" attribute of our HTML5 Video control to this blobUrl. This is still not very elegant, I have seen examples using "arraybuffer" as responseType of the XmlHttpRequest object in Js to retrieve binary data, but I got garbled data back trying to use it with WCF REST. So in this article you have seen a way to send binary data from a WCF method by using a Base64 encoded string as an intermediary between the Server and the client. We got the file size of the request according to Fiddler inspection down to much less than the bloated binary array transfer that actually sent the byte array as a text string back to the client.