// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // namespace ImageProcessorCore.IO { using System; using System.IO; using System.Text; /// /// Equivalent of , but with either endianness, depending on /// the it is constructed with. /// internal class EndianBinaryWriter : IDisposable { /// /// Buffer used for temporary storage during conversion from primitives /// private readonly byte[] buffer = new byte[16]; /// /// Buffer used for Write(char) /// private readonly char[] charBuffer = new char[1]; /// /// Whether or not this writer has been disposed yet. /// private bool disposed; /// /// Initializes a new instance of the class /// 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) { } /// /// Initializes a new instance of the class /// 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) { // TODO: Use Guard 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.BaseStream = stream; this.BitConverter = bitConverter; this.Encoding = encoding; } /// /// Gets the bit converter used to write values to the stream /// public EndianBitConverter BitConverter { get; } /// /// Gets the encoding used to write strings /// public Encoding Encoding { get; } /// /// Gets the underlying stream of the EndianBinaryWriter. /// public Stream BaseStream { get; } /// /// Closes the writer, including the underlying stream. /// public void Close() { this.Dispose(); } /// /// Flushes the underlying stream. /// public void Flush() { this.CheckDisposed(); this.BaseStream.Flush(); } /// /// Seeks within the stream. /// /// Offset to seek to. /// Origin of seek operation. public void Seek(int offset, SeekOrigin origin) { this.CheckDisposed(); this.BaseStream.Seek(offset, origin); } /// /// Writes a boolean value to the stream. 1 byte is written. /// /// The value to write public void Write(bool value) { this.BitConverter.CopyBytes(value, this.buffer, 0); this.WriteInternal(this.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) { this.BitConverter.CopyBytes(value, this.buffer, 0); this.WriteInternal(this.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) { this.BitConverter.CopyBytes(value, this.buffer, 0); this.WriteInternal(this.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) { this.BitConverter.CopyBytes(value, this.buffer, 0); this.WriteInternal(this.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) { this.BitConverter.CopyBytes(value, this.buffer, 0); this.WriteInternal(this.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) { this.BitConverter.CopyBytes(value, this.buffer, 0); this.WriteInternal(this.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) { this.BitConverter.CopyBytes(value, this.buffer, 0); this.WriteInternal(this.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) { this.BitConverter.CopyBytes(value, this.buffer, 0); this.WriteInternal(this.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) { this.BitConverter.CopyBytes(value, this.buffer, 0); this.WriteInternal(this.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) { this.BitConverter.CopyBytes(value, this.buffer, 0); this.WriteInternal(this.buffer, 16); } /// /// Writes a signed byte to the stream. /// /// The value to write public void Write(byte value) { this.buffer[0] = value; this.WriteInternal(this.buffer, 1); } /// /// Writes an unsigned byte to the stream. /// /// The value to write public void Write(sbyte value) { this.buffer[0] = unchecked((byte)value); this.WriteInternal(this.buffer, 1); } /// /// Writes an array of bytes to the stream. /// /// The values to write public void Write(byte[] value) { if (value == null) { throw new ArgumentNullException(nameof(value)); } this.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) { this.CheckDisposed(); this.BaseStream.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) { this.charBuffer[0] = value; this.Write(this.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(nameof(value)); } this.CheckDisposed(); byte[] data = this.Encoding.GetBytes(value, 0, value.Length); this.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(nameof(value)); } this.CheckDisposed(); byte[] data = this.Encoding.GetBytes(value); this.Write7BitEncodedInt(data.Length); this.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) { this.CheckDisposed(); if (value < 0) { throw new ArgumentOutOfRangeException(nameof(value), "Value must be greater than or equal to 0."); } int index = 0; while (value >= 128) { this.buffer[index++] = (byte)((value & 0x7f) | 0x80); value = value >> 7; index++; } this.buffer[index++] = (byte)value; this.BaseStream.Write(this.buffer, 0, index); } /// /// Checks whether or not the writer has been disposed, throwing an exception if so. /// private void CheckDisposed() { if (this.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 private void WriteInternal(byte[] bytes, int length) { this.CheckDisposed(); this.BaseStream.Write(bytes, 0, length); } /// /// Disposes of the underlying stream. /// public void Dispose() { if (!this.disposed) { this.Flush(); this.disposed = true; ((IDisposable)this.BaseStream).Dispose(); } } } }