public class ByteArrayBuilder : IDisposable { #region Constants ////// True in a byte form of the Line /// const byte streamTrue = (byte)1; ////// False in the byte form of a line /// const byte streamFalse = (byte)0; #endregion #region Fields #region Internal ////// Holds the actual bytes. /// MemoryStream store = new MemoryStream(); ////// Is Little Endian /// True - little endian /// False - big endian /// bool isLittleEndian; #endregion #region Property bases #endregion #endregion #region Properties ////// Bytes in the store. /// public int Length { get { return (int)store.Length; } } #endregion #region Regular Expressions #endregion #region Enums #endregion #region Constructors ////// Create a new, empty builder ready to be filled. /// public ByteArrayBuilder(bool isLittleEndian = true) { this.isLittleEndian = isLittleEndian; } ////// Create a new builder from a set of data /// /// Data to preset the builder from public ByteArrayBuilder(byte[] data, bool isLittleEndian = true) { store.Close(); store.Dispose(); store = new MemoryStream(data); this.isLittleEndian = isLittleEndian; } ////// Create a new builder from the Base64 string representation of an /// existing instance. /// The Base64 representation can be retrieved using the ToString override /// /// Base64 string representation of an /// existing instance. public ByteArrayBuilder(string base64, bool isLittleEndian = true) { store.Close(); store.Dispose(); store = new MemoryStream(Convert.FromBase64String(base64)); this.isLittleEndian = isLittleEndian; } #endregion #region Events #region Event Constructors #endregion #region Event Handlers #endregion #endregion #region Public Methods #region Append overloads ////// Adds a bool to an array /// /// Value to append to existing builder data public void Append(bool b) { store.WriteByte(b ? streamTrue : streamFalse); } ////// Adds a byte to an array /// /// Value to append to existing builder data public void Append(byte b) { store.WriteByte(b); } ////// Adds an array of bytes to an array /// /// Value to append to existing builder data /// /// If true, the length is added before the value. /// This allows extraction of individual elements back to the original input form. /// public void Append(byte[] b, bool addLength = false) { if (b != null) { if (addLength) Append(b.Length); AddBytes(b); } } ////// Adds a char to an array /// /// Value to append to existing builder data public void Append(char c) { store.WriteByte((byte)c); } ////// Adds an array of characters to an array /// /// Value to append to existing builder data /// /// If true, the length is added before the value. /// This allows extraction of individual elements back to the original input form. /// public void Append(char[] c, bool addLength = false) { if (c != null) { if (addLength) Append(c.Length); Append(System.Text.Encoding.Unicode.GetBytes(c)); } } ////// Adds a DateTime to an array /// /// Value to append to existing builder data public void Append(DateTime dt) { Append(dt.Ticks); } ////// Adds a decimal value to an array /// /// Value to append to existing builder data public void Append(decimal d) { // GetBits always returns four ints. // We store them in a specific order so that they can be recovered later. int[] bits = decimal.GetBits(d); if (isLittleEndian) { Append(bits[0]); Append(bits[1]); Append(bits[2]); Append(bits[3]); } else { Append(bits[3]); Append(bits[2]); Append(bits[1]); Append(bits[0]); } } ////// Adds a double to an array /// /// Value to append to existing builder data public void Append(double d) { byte[] data = BitConverter.GetBytes(d); if (!isLittleEndian) { Array.Reverse(data); } AddBytes(data); } ////// Adds a float to an array /// /// Value to append to existing builder data public void Append(float f) { byte[] data = BitConverter.GetBytes(f); if (!isLittleEndian) { Array.Reverse(data); } AddBytes(data); } ////// Adds a Guid to an array /// /// Value to append to existing builder data public void Append(Guid g) { Append(g.ToByteArray()); } ////// Adds an integer to an array /// /// Value to append to existing builder data public void Append(int i) { byte[] data = BitConverter.GetBytes(i); if (!isLittleEndian) { Array.Reverse(data); } AddBytes(data); } ////// Adds a long integer to an array /// /// Value to append to existing builder data public void Append(long l) { byte[] data = BitConverter.GetBytes(l); if (!isLittleEndian) { Array.Reverse(data); } AddBytes(data); } ////// Adds a short integer to an array /// /// Value to append to existing builder data public void Append(short s) { byte[] data = BitConverter.GetBytes(s); if (!isLittleEndian) { Array.Reverse(data); } AddBytes(data); } ////// Adds a string to an array /// /// Value to append to existing builder data /// /// If true, the length is added before the value. /// This allows extraction of individual elements back to the original input form. /// public void Append(string s, bool addLength = false) { if (!string.IsNullOrEmpty(s)) { byte[] data = System.Text.Encoding.Unicode.GetBytes(s); if (addLength) Append(data.Length); AddBytes(data); } } ////// Adds an unsigned integer to an array /// /// Value to append to existing builder data public void Append(uint ui) { byte[] data = BitConverter.GetBytes(ui); if (!isLittleEndian) { Array.Reverse(data); } AddBytes(data); } ////// Adds a unsigned long integer to an array /// /// Value to append to existing builder data public void Append(ulong ul) { byte[] data = BitConverter.GetBytes(ul); if (!isLittleEndian) { Array.Reverse(data); } AddBytes(data); } ////// Adds a unsigned short integer to an array /// /// Value to append to existing builder data public void Append(ushort us) { byte[] data = BitConverter.GetBytes(us); if (!isLittleEndian) { Array.Reverse(data); } AddBytes(data); } #endregion #region Extraction ////// Gets a bool from an array /// ///public bool GetBool() { return store.ReadByte() == streamTrue; } /// /// Gets a byte from an array /// ///public byte GetByte() { return (byte)store.ReadByte(); } /// /// Gets an array of bytes from an array /// ///public byte[] GetByteArray() { int length = GetInt(); return GetBytes(length); } /// /// Gets a char from an array /// ///public char GetChar() { return (char)store.ReadByte(); } /// /// Gets an array of characters from an array /// ///public char[] GetCharArray() { int length = GetInt(); return System.Text.Encoding.Unicode.GetChars(GetBytes(length)); } /// /// Gets a DateTime value from an array /// ///public DateTime GetDateTime() { return new DateTime(GetLong()); } /// /// Gets a decimal value from an array /// ///public decimal GetDecimal() { // GetBits always returns four ints. // We store them in a specific order so that they can be recovered later. int[] bits = new int[] { GetInt(), GetInt(), GetInt(), GetInt() }; if(!isLittleEndian) { Array.Reverse(bits); } return new decimal(bits); } /// /// Gets a double from an array /// ///public double GetDouble() { byte[] data = GetBytes(8); if (!isLittleEndian) { Array.Reverse(data); } return BitConverter.ToDouble(data, 0); } /// /// Gets a float from an array /// ///public float GetFloat() { byte[] data = GetBytes(4); if (!isLittleEndian) { Array.Reverse(data); } return BitConverter.ToSingle(data, 0); } /// /// Gets a Guid from an array /// ///public Guid GetGuid() { return new Guid(GetByteArray()); } /// /// Gets an integer from an array /// ///public int GetInt() { byte[] data = GetBytes(4); if (!isLittleEndian) { Array.Reverse(data); } return BitConverter.ToInt32(data, 0); } /// /// Gets a long integer from an array /// ///public long GetLong() { byte[] data = GetBytes(8); if (!isLittleEndian) { Array.Reverse(data); } return BitConverter.ToInt64(data, 0); } /// /// Gets a short integer from an array /// ///public short GetShort() { byte[] data = GetBytes(2); if (!isLittleEndian) { Array.Reverse(data); } return BitConverter.ToInt16(data, 0); } /// /// Gets a string from an array /// ///public string GetString() { int length = GetInt(); return System.Text.Encoding.Unicode.GetString(GetBytes(length)); } /// /// Gets an unsigned integer from an array /// ///public uint GetUint() { byte[] data = GetBytes(4); if (!isLittleEndian) { Array.Reverse(data); } return BitConverter.ToUInt32(data, 0); } /// /// Gets a unsigned long integer from an array /// ///public ulong GetUlong() { byte[] data = GetBytes(8); if (!isLittleEndian) { Array.Reverse(data); } return BitConverter.ToUInt64(data, 0); } /// /// Gets a unsigned short integer from an array /// ///public ushort GetUshort() { byte[] data = GetBytes(2); if (!isLittleEndian) { Array.Reverse(data); } return BitConverter.ToUInt16(data, 0); } #endregion #region Interaction /// /// Clear all content from the builder /// public void Clear() { store.Close(); store.Dispose(); store = new MemoryStream(); } ////// Rewind the builder ready to read data /// public void Rewind() { store.Seek(0, SeekOrigin.Begin); } ////// Set an absolute position in the builder. /// **WARNING** /// If you add any variable size objects to the builder, the results of /// reading after a Seek to a non-zero value are unpredictable. /// A builder does not store just objects - for some it stores additional /// information as well. /// /// public void Seek(int position) { store.Seek((long)position, SeekOrigin.Begin); } ////// Returns the builder as an array of bytes /// ///public byte[] ToArray() { byte[] data = new byte[Length]; Array.Copy(store.GetBuffer(), data, Length); return data; } #endregion #endregion #region Overrides /// /// Returns a text based (Base64) string version of the current content /// ///public override string ToString() { return Convert.ToBase64String(ToArray()); } #endregion #region Private Methods /// /// Add a string of raw bytes to the store /// /// private void AddBytes(byte[] b) { if (b != null) { store.Write(b, 0, b.Length); } } ////// Reads a specific number of bytes from the store /// /// ///private byte[] GetBytes(int length) { byte[] data = new byte[length]; if (length > 0) { int read = store.Read(data, 0, length); if (read != length) { throw new ApplicationException("Buffer did not contain " + length + " bytes"); } } return data; } #endregion #region IDisposable Implememntation /// /// Dispose of this builder and it's resources /// public void Dispose() { store.Close(); store.Dispose(); } #endregion }