using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Novacoin { public class VarInt { /// /// Encodes unsigned integer value into compact representation. /// /// See https://bitcoin.org/en/developer-reference#compactsize-unsigned-integers for additional information. /// /// Unsigned integer value /// Byte sequence public static IList EncodeVarInt(ulong n) { List resultBytes = new List(); if (n <= 0xfc) { // Values up to 0xfc are stored directly without any prefix resultBytes.Add((byte)n); } else { byte prefix; byte[] valueBytes; if (n <= ushort.MaxValue) { // ushort flag prefix = 0xfd; valueBytes = Interop.LEBytes((ushort)n); } else if (n <= uint.MaxValue) { // uint flag prefix = 0xfe; valueBytes = Interop.LEBytes((uint)n); } else { // ulong flag prefix = 0xff; valueBytes = Interop.LEBytes(n); } resultBytes.Add(prefix); resultBytes.AddRange(valueBytes); } return resultBytes; } /// /// Encodes integer value into compact representation. /// /// See https://bitcoin.org/en/developer-reference#compactsize-unsigned-integers for additional information. /// /// Integer value /// Byte sequence public static IList EncodeVarInt(long n) { return EncodeVarInt((ulong)n); } /// /// Decodes integer value from compact representation /// /// See https://bitcoin.org/en/developer-reference#compactsize-unsigned-integers for additional information. /// /// Byte sequence /// Integer value public static ulong DecodeVarInt(IList bytes) { byte prefix = bytes[0]; bytes.RemoveAt(0); // Remove prefix byte[] bytesArray = bytes.ToArray(); if (BitConverter.IsLittleEndian) { switch (prefix) { case 0xfd: // ushort flag return BitConverter.ToUInt16(bytesArray, 0); case 0xfe: // uint flag return BitConverter.ToUInt32(bytesArray, 0); case 0xff: // ulong flag return BitConverter.ToUInt64(bytesArray, 0); default: return prefix; } } else { // Values are stored in little-endian order switch (prefix) { case 0xfd: // ushort flag Array.Resize(ref bytesArray, 2); Array.Reverse(bytesArray); return BitConverter.ToUInt16(bytesArray, 0); case 0xfe: // uint flag Array.Resize(ref bytesArray, 4); Array.Reverse(bytesArray); return BitConverter.ToUInt32(bytesArray, 0); case 0xff: // ulong flag Array.Resize(ref bytesArray, 8); Array.Reverse(bytesArray); return BitConverter.ToUInt64(bytesArray, 0); default: return prefix; } } } /// /// Read and decode variable integer from wrapped list object. /// /// Note: Should be used only if there is some variable integer data at current position. Otherwise you will get undefined behavior, so make sure that you know what you are doing. /// /// /// public static ulong ReadVarInt(ref WrappedList wBytes) { byte prefix = wBytes.GetItem(); switch (prefix) { case 0xfd: // ushort return Interop.LEBytesToUInt16(wBytes.GetItems(2)); case 0xfe: // uint return Interop.LEBytesToUInt32(wBytes.GetItems(4)); case 0xff: // ulong return Interop.LEBytesToUInt64(wBytes.GetItems(8)); default: return prefix; } } } }