using System; using System.IO; using System.Text; using MiscUtil.Conversion; namespace MiscUtil.IO { /// /// Equivalent of System.IO.BinaryWriter, but with either endianness, depending on /// the EndianBitConverter it is constructed with. /// public class EndianBinaryWriter : IDisposable { #region Fields not directly related to properties /// /// Whether or not this writer has been disposed yet. /// bool disposed=false; /// /// Buffer used for temporary storage during conversion from primitives /// byte[] buffer = new byte[16]; /// /// Buffer used for Write(char) /// char[] charBuffer = new char[1]; #endregion #region Constructors /// /// Constructs a new binary writer with the given bit converter, writing /// to the given stream, using UTF-8 encoding. /// /// Converter to use when writing data /// Stream to write data to public EndianBinaryWriter (EndianBitConverter bitConverter, Stream stream) : this (bitConverter, stream, Encoding.UTF8) { } /// /// Constructs a new binary writer with the given bit converter, writing /// to the given stream, using the given encoding. /// /// Converter to use when writing data /// Stream to write data to /// Encoding to use when writing character data public EndianBinaryWriter (EndianBitConverter bitConverter, Stream stream, Encoding encoding) { if (bitConverter==null) { throw new ArgumentNullException("bitConverter"); } if (stream==null) { throw new ArgumentNullException("stream"); } if (encoding==null) { throw new ArgumentNullException("encoding"); } if (!stream.CanWrite) { throw new ArgumentException("Stream isn't writable", "stream"); } this.stream = stream; this.bitConverter = bitConverter; this.encoding = encoding; } #endregion #region Properties EndianBitConverter bitConverter; /// /// The bit converter used to write values to the stream /// public EndianBitConverter BitConverter { get { return bitConverter; } } Encoding encoding; /// /// The encoding used to write strings /// public Encoding Encoding { get { return encoding; } } Stream stream; /// /// Gets the underlying stream of the EndianBinaryWriter. /// public Stream BaseStream { get { return stream; } } #endregion #region Public methods /// /// Closes the writer, including the underlying stream. /// public void Close() { Dispose(); } /// /// Flushes the underlying stream. /// public void Flush() { CheckDisposed(); stream.Flush(); } /// /// Seeks within the stream. /// /// Offset to seek to. /// Origin of seek operation. public void Seek (int offset, SeekOrigin origin) { CheckDisposed(); stream.Seek (offset, origin); } /// /// Writes a boolean value to the stream. 1 byte is written. /// /// The value to write public void Write (bool value) { bitConverter.CopyBytes(value, buffer, 0); WriteInternal(buffer, 1); } /// /// Writes a 16-bit signed integer to the stream, using the bit converter /// for this writer. 2 bytes are written. /// /// The value to write public void Write (short value) { bitConverter.CopyBytes(value, buffer, 0); WriteInternal(buffer, 2); } /// /// Writes a 32-bit signed integer to the stream, using the bit converter /// for this writer. 4 bytes are written. /// /// The value to write public void Write (int value) { bitConverter.CopyBytes(value, buffer, 0); WriteInternal(buffer, 4); } /// /// Writes a 64-bit signed integer to the stream, using the bit converter /// for this writer. 8 bytes are written. /// /// The value to write public void Write (long value) { bitConverter.CopyBytes(value, buffer, 0); WriteInternal(buffer, 8); } /// /// Writes a 16-bit unsigned integer to the stream, using the bit converter /// for this writer. 2 bytes are written. /// /// The value to write public void Write (ushort value) { bitConverter.CopyBytes(value, buffer, 0); WriteInternal(buffer, 2); } /// /// Writes a 32-bit unsigned integer to the stream, using the bit converter /// for this writer. 4 bytes are written. /// /// The value to write public void Write (uint value) { bitConverter.CopyBytes(value, buffer, 0); WriteInternal(buffer, 4); } /// /// Writes a 64-bit unsigned integer to the stream, using the bit converter /// for this writer. 8 bytes are written. /// /// The value to write public void Write (ulong value) { bitConverter.CopyBytes(value, buffer, 0); WriteInternal(buffer, 8); } /// /// Writes a single-precision floating-point value to the stream, using the bit converter /// for this writer. 4 bytes are written. /// /// The value to write public void Write (float value) { bitConverter.CopyBytes(value, buffer, 0); WriteInternal(buffer, 4); } /// /// Writes a double-precision floating-point value to the stream, using the bit converter /// for this writer. 8 bytes are written. /// /// The value to write public void Write (double value) { bitConverter.CopyBytes(value, buffer, 0); WriteInternal(buffer, 8); } /// /// Writes a decimal value to the stream, using the bit converter for this writer. /// 16 bytes are written. /// /// The value to write public void Write (decimal value) { bitConverter.CopyBytes(value, buffer, 0); WriteInternal(buffer, 16); } /// /// Writes a signed byte to the stream. /// /// The value to write public void Write (byte value) { buffer[0] = value; WriteInternal(buffer, 1); } /// /// Writes an unsigned byte to the stream. /// /// The value to write public void Write (sbyte value) { buffer[0] = unchecked((byte)value); WriteInternal(buffer, 1); } /// /// Writes an array of bytes to the stream. /// /// The values to write public void Write (byte[] value) { if (value == null) { throw (new System.ArgumentNullException("value")); } WriteInternal(value, value.Length); } /// /// Writes a portion of an array of bytes to the stream. /// /// An array containing the bytes to write /// The index of the first byte to write within the array /// The number of bytes to write public void Write (byte[] value, int offset, int count) { CheckDisposed(); stream.Write(value, offset, count); } /// /// Writes a single character to the stream, using the encoding for this writer. /// /// The value to write public void Write(char value) { charBuffer[0] = value; Write(charBuffer); } /// /// Writes an array of characters to the stream, using the encoding for this writer. /// /// An array containing the characters to write public void Write(char[] value) { if (value==null) { throw new ArgumentNullException("value"); } CheckDisposed(); byte[] data = Encoding.GetBytes(value, 0, value.Length); WriteInternal(data, data.Length); } /// /// Writes a string to the stream, using the encoding for this writer. /// /// The value to write. Must not be null. /// value is null public void Write(string value) { if (value==null) { throw new ArgumentNullException("value"); } CheckDisposed(); byte[] data = Encoding.GetBytes(value); Write7BitEncodedInt(data.Length); WriteInternal(data, data.Length); } /// /// Writes a 7-bit encoded integer from the stream. This is stored with the least significant /// information first, with 7 bits of information per byte of value, and the top /// bit as a continuation flag. /// /// The 7-bit encoded integer to write to the stream public void Write7BitEncodedInt(int value) { CheckDisposed(); if (value < 0) { throw new ArgumentOutOfRangeException("value", "Value must be greater than or equal to 0."); } int index=0; while (value >= 128) { buffer[index++]= (byte)((value&0x7f) | 0x80); value = value >> 7; index++; } buffer[index++]=(byte)value; stream.Write(buffer, 0, index); } #endregion #region Private methods /// /// Checks whether or not the writer has been disposed, throwing an exception if so. /// void CheckDisposed() { if (disposed) { throw new ObjectDisposedException("EndianBinaryWriter"); } } /// /// Writes the specified number of bytes from the start of the given byte array, /// after checking whether or not the writer has been disposed. /// /// The array of bytes to write from /// The number of bytes to write void WriteInternal (byte[] bytes, int length) { CheckDisposed(); stream.Write(bytes, 0, length); } #endregion #region IDisposable Members /// /// Disposes of the underlying stream. /// public void Dispose() { if (!disposed) { Flush(); disposed = true; ((IDisposable)stream).Dispose(); } } #endregion } }