Implement some operations on variable-length quantities, at least including conversions from a normal number in the language to the binary representation of the variable-length quantity for that number, and vice versa. Any variants are acceptable.



Task

With above operations,

convert these two numbers 0x200000 (2097152 in decimal) and 0x1fffff (2097151 in decimal) into sequences of octets (an eight-bit byte); 
display these sequences of octets;
convert these sequences of octets back to numbers, and check that they are equal to original numbers.


namespace Vlq
{
  using System;
  using System.Collections.Generic;
  using System.Linq;
 
  public static class VarLenQuantity
  {
    public static ulong ToVlq(ulong integer)
    {
      var array = new byte[8];
      var buffer = ToVlqCollection(integer)
        .SkipWhile(b => b == 0)
        .Reverse()
        .ToArray();
      Array.Copy(buffer, array, buffer.Length);
      return BitConverter.ToUInt64(array, 0);
    }
 
    public static ulong FromVlq(ulong integer)
    {
      var collection = BitConverter.GetBytes(integer).Reverse();
      return FromVlqCollection(collection);
    }
 
    public static IEnumerable<byte> ToVlqCollection(ulong integer)
    {
      if (integer > Math.Pow(2, 56))
        throw new OverflowException("Integer exceeds max value.");
 
      var index = 7;
      var significantBitReached = false;
      var mask = 0x7fUL << (index * 7);
      while (index >= 0)
      {
        var buffer = (mask & integer);
        if (buffer > 0 || significantBitReached)
        {
          significantBitReached = true;
          buffer >>= index * 7;
          if (index > 0)
            buffer |= 0x80;
          yield return (byte)buffer;
        }
        mask >>= 7;
        index--;
      }
    }
 
 
    public static ulong FromVlqCollection(IEnumerable<byte> vlq)
    {
      ulong integer = 0;
      var significantBitReached = false;
 
      using (var enumerator = vlq.GetEnumerator())
      {
        int index = 0;
        while (enumerator.MoveNext())
        {
          var buffer = enumerator.Current;
          if (buffer > 0 || significantBitReached)
          {
            significantBitReached = true;
            integer <<= 7;
            integer |= (buffer & 0x7fUL);
          }
 
          if (++index == 8 || (significantBitReached && (buffer & 0x80) != 0x80))
            break;
        }
      }
      return integer;
    }
 
    public static void Main()
    {
      var integers = new ulong[] { 0x7fUL << 7 * 7, 0x80, 0x2000, 0x3FFF, 0x4000, 0x200000, 0x1fffff };
 
      foreach (var original in integers)
      {
        Console.WriteLine("Original: 0x{0:X}", original);
 
        //collection
        var seq = ToVlqCollection(original);
        Console.WriteLine("Sequence: 0x{0}", seq.Select(b => b.ToString("X2")).Aggregate(string.Concat));
 
        var decoded = FromVlqCollection(seq);
        Console.WriteLine("Decoded: 0x{0:X}", decoded);
 
        //ints
        var encoded = ToVlq(original);
        Console.WriteLine("Encoded: 0x{0:X}", encoded);
 
        decoded = FromVlq(encoded);
        Console.WriteLine("Decoded: 0x{0:X}", decoded);
 
        Console.WriteLine();
      }
      Console.WriteLine("Press any key to continue...");
      Console.ReadKey();
    }
  }
}