From 4ef82a9c6c4e968f291a65d71fdeae8b1a35c2a3 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Fri, 1 Apr 2016 10:39:54 +1100 Subject: [PATCH] Add EndianBinaryWriter Former-commit-id: 17cd44fdcf56bd26a2a22ddecc156c9c35ae6478 Former-commit-id: c978bc8b4b361b67559b28f71eb1c351ff2b5246 Former-commit-id: 9a242b89981c266e38e6e79bf55e4750e0e3ef73 --- .../IO/EndianBinaryWriter.cs | 385 ++++++++++++++++++ 1 file changed, 385 insertions(+) create mode 100644 src/ImageProcessorCore/IO/EndianBinaryWriter.cs diff --git a/src/ImageProcessorCore/IO/EndianBinaryWriter.cs b/src/ImageProcessorCore/IO/EndianBinaryWriter.cs new file mode 100644 index 000000000..0f37b9a13 --- /dev/null +++ b/src/ImageProcessorCore/IO/EndianBinaryWriter.cs @@ -0,0 +1,385 @@ +// +// 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(); + } + } + } +} \ No newline at end of file