From f3c814f6ecd8a2e3c7cb0b8385277883980ba4bd Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Wed, 16 Sep 2020 15:12:43 -0600 Subject: [PATCH] add XSigUtility from Troy Garner https://github.com/bitm0de/XSigUtilityLibrary --- .../Pepperdash Core/PepperDash_Core.csproj | 10 + .../Serialization/IXSigSerialization.cs | 14 + .../XSigSerializationException.cs | 11 + .../XSigUtility/Tokens/XSigAnalogToken.cs | 55 ++++ .../XSigUtility/Tokens/XSigDigitalToken.cs | 53 ++++ .../XSigUtility/Tokens/XSigSerialToken.cs | 53 ++++ .../XSigUtility/Tokens/XSigToken.cs | 45 ++++ .../XSigUtility/Tokens/XSigTokenType.cs | 23 ++ .../XSigUtility/XSigHelpers.cs | 239 ++++++++++++++++++ .../XSigUtility/XSigTokenStreamReader.cs | 147 +++++++++++ .../XSigUtility/XSigTokenStreamWriter.cs | 136 ++++++++++ 11 files changed, 786 insertions(+) create mode 100644 Pepperdash Core/Pepperdash Core/XSigUtility/Serialization/IXSigSerialization.cs create mode 100644 Pepperdash Core/Pepperdash Core/XSigUtility/Serialization/XSigSerializationException.cs create mode 100644 Pepperdash Core/Pepperdash Core/XSigUtility/Tokens/XSigAnalogToken.cs create mode 100644 Pepperdash Core/Pepperdash Core/XSigUtility/Tokens/XSigDigitalToken.cs create mode 100644 Pepperdash Core/Pepperdash Core/XSigUtility/Tokens/XSigSerialToken.cs create mode 100644 Pepperdash Core/Pepperdash Core/XSigUtility/Tokens/XSigToken.cs create mode 100644 Pepperdash Core/Pepperdash Core/XSigUtility/Tokens/XSigTokenType.cs create mode 100644 Pepperdash Core/Pepperdash Core/XSigUtility/XSigHelpers.cs create mode 100644 Pepperdash Core/Pepperdash Core/XSigUtility/XSigTokenStreamReader.cs create mode 100644 Pepperdash Core/Pepperdash Core/XSigUtility/XSigTokenStreamWriter.cs diff --git a/Pepperdash Core/Pepperdash Core/PepperDash_Core.csproj b/Pepperdash Core/Pepperdash Core/PepperDash_Core.csproj index d4fbf18..f746202 100644 --- a/Pepperdash Core/Pepperdash Core/PepperDash_Core.csproj +++ b/Pepperdash Core/Pepperdash Core/PepperDash_Core.csproj @@ -122,6 +122,16 @@ + + + + + + + + + + diff --git a/Pepperdash Core/Pepperdash Core/XSigUtility/Serialization/IXSigSerialization.cs b/Pepperdash Core/Pepperdash Core/XSigUtility/Serialization/IXSigSerialization.cs new file mode 100644 index 0000000..074d8a5 --- /dev/null +++ b/Pepperdash Core/Pepperdash Core/XSigUtility/Serialization/IXSigSerialization.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; +using PepperDash.Core.Intersystem.Tokens; + +namespace PepperDash.Core.Intersystem.Serialization +{ + /// + /// Interface to determine XSig serialization for an object. + /// + public interface IXSigSerialization + { + IEnumerable Serialize(); + T Deserialize(IEnumerable tokens) where T : class, IXSigSerialization; + } +} \ No newline at end of file diff --git a/Pepperdash Core/Pepperdash Core/XSigUtility/Serialization/XSigSerializationException.cs b/Pepperdash Core/Pepperdash Core/XSigUtility/Serialization/XSigSerializationException.cs new file mode 100644 index 0000000..f1c50f5 --- /dev/null +++ b/Pepperdash Core/Pepperdash Core/XSigUtility/Serialization/XSigSerializationException.cs @@ -0,0 +1,11 @@ +using System; + +namespace PepperDash.Core.Intersystem.Serialization +{ + public class XSigSerializationException : Exception + { + public XSigSerializationException() { } + public XSigSerializationException(string message) : base(message) { } + public XSigSerializationException(string message, Exception inner) : base(message, inner) { } + } +} \ No newline at end of file diff --git a/Pepperdash Core/Pepperdash Core/XSigUtility/Tokens/XSigAnalogToken.cs b/Pepperdash Core/Pepperdash Core/XSigUtility/Tokens/XSigAnalogToken.cs new file mode 100644 index 0000000..41427fe --- /dev/null +++ b/Pepperdash Core/Pepperdash Core/XSigUtility/Tokens/XSigAnalogToken.cs @@ -0,0 +1,55 @@ +using System; + +namespace PepperDash.Core.Intersystem.Tokens +{ + public sealed class XSigAnalogToken : XSigToken, IFormattable + { + private readonly ushort _value; + + public XSigAnalogToken(int index, ushort value) + : base(index) + { + // 10-bits available for analog encoded data + if (index >= 1024 || index < 0) + throw new ArgumentOutOfRangeException("index"); + + _value = value; + } + + public ushort Value + { + get { return _value; } + } + + public override XSigTokenType TokenType + { + get { return XSigTokenType.Analog; } + } + + public override byte[] GetBytes() + { + return new[] { + (byte)(0xC0 | ((Value & 0xC000) >> 10) | (Index >> 7)), + (byte)((Index - 1) & 0x7F), + (byte)((Value & 0x3F80) >> 7), + (byte)(Value & 0x7F) + }; + } + + public override XSigToken GetTokenWithOffset(int offset) + { + if (offset == 0) return this; + return new XSigAnalogToken(Index + offset, Value); + } + + public override string ToString() + { + return Index + " = 0x" + Value.ToString("X4"); + } + + public string ToString(string format, IFormatProvider formatProvider) + { + return Value.ToString(format, formatProvider); + } + } +} \ No newline at end of file diff --git a/Pepperdash Core/Pepperdash Core/XSigUtility/Tokens/XSigDigitalToken.cs b/Pepperdash Core/Pepperdash Core/XSigUtility/Tokens/XSigDigitalToken.cs new file mode 100644 index 0000000..f721387 --- /dev/null +++ b/Pepperdash Core/Pepperdash Core/XSigUtility/Tokens/XSigDigitalToken.cs @@ -0,0 +1,53 @@ +using System; + +namespace PepperDash.Core.Intersystem.Tokens +{ + public sealed class XSigDigitalToken : XSigToken + { + private readonly bool _value; + + public XSigDigitalToken(int index, bool value) + : base(index) + { + // 12-bits available for digital encoded data + if (index >= 4096 || index < 0) + throw new ArgumentOutOfRangeException("index"); + + _value = value; + } + + public bool Value + { + get { return _value; } + } + + public override XSigTokenType TokenType + { + get { return XSigTokenType.Digital; } + } + + public override byte[] GetBytes() + { + return new[] { + (byte)(0x80 | (Value ? 0 : 0x20) | (Index >> 7)), + (byte)((Index - 1) & 0x7F) + }; + } + + public override XSigToken GetTokenWithOffset(int offset) + { + if (offset == 0) return this; + return new XSigDigitalToken(Index + offset, Value); + } + + public override string ToString() + { + return Index + " = " + (Value ? "High" : "Low"); + } + + public string ToString(IFormatProvider formatProvider) + { + return Value.ToString(formatProvider); + } + } +} \ No newline at end of file diff --git a/Pepperdash Core/Pepperdash Core/XSigUtility/Tokens/XSigSerialToken.cs b/Pepperdash Core/Pepperdash Core/XSigUtility/Tokens/XSigSerialToken.cs new file mode 100644 index 0000000..845b297 --- /dev/null +++ b/Pepperdash Core/Pepperdash Core/XSigUtility/Tokens/XSigSerialToken.cs @@ -0,0 +1,53 @@ +using System; +using System.Text; + +namespace PepperDash.Core.Intersystem.Tokens +{ + public sealed class XSigSerialToken : XSigToken + { + private readonly string _value; + + public XSigSerialToken(int index, string value) + : base(index) + { + // 10-bits available for serial encoded data + if (index >= 1024 || index < 0) + throw new ArgumentOutOfRangeException("index"); + + _value = value; + } + + public string Value + { + get { return _value; } + } + + public override XSigTokenType TokenType + { + get { return XSigTokenType.Serial; } + } + + public override byte[] GetBytes() + { + var serialBytes = Encoding.GetEncoding(28591).GetBytes(Value); + var xsig = new byte[serialBytes.Length + 3]; + xsig[0] = (byte)(0xC8 | (Index >> 7)); + xsig[1] = (byte)((Index - 1) & 0x7F); + xsig[xsig.Length - 1] = 0xFF; + + Buffer.BlockCopy(serialBytes, 0, xsig, 2, serialBytes.Length); + return xsig; + } + + public override XSigToken GetTokenWithOffset(int offset) + { + if (offset == 0) return this; + return new XSigSerialToken(Index + offset, Value); + } + + public override string ToString() + { + return Index + " = \"" + Value + "\""; + } + } +} \ No newline at end of file diff --git a/Pepperdash Core/Pepperdash Core/XSigUtility/Tokens/XSigToken.cs b/Pepperdash Core/Pepperdash Core/XSigUtility/Tokens/XSigToken.cs new file mode 100644 index 0000000..4c00a2e --- /dev/null +++ b/Pepperdash Core/Pepperdash Core/XSigUtility/Tokens/XSigToken.cs @@ -0,0 +1,45 @@ +namespace PepperDash.Core.Intersystem.Tokens +{ + /// + /// Represents the base class for all XSig datatypes. + /// + public abstract class XSigToken + { + private readonly int _index; + + /// + /// Constructs an XSigToken with the specified index. + /// + /// Index for the data. + protected XSigToken(int index) + { + _index = index; + } + + /// + /// XSig 1-based index. + /// + public int Index + { + get { return _index; } + } + + /// + /// XSigToken type. + /// + public abstract XSigTokenType TokenType { get; } + + /// + /// Generates the XSig bytes for the corresponding token. + /// + /// XSig byte array. + public abstract byte[] GetBytes(); + + /// + /// Returns a new token if necessary with an updated index based on the specified offset. + /// + /// Offset to adjust the index with. + /// XSigToken + public abstract XSigToken GetTokenWithOffset(int offset); + } +} \ No newline at end of file diff --git a/Pepperdash Core/Pepperdash Core/XSigUtility/Tokens/XSigTokenType.cs b/Pepperdash Core/Pepperdash Core/XSigUtility/Tokens/XSigTokenType.cs new file mode 100644 index 0000000..26d6c12 --- /dev/null +++ b/Pepperdash Core/Pepperdash Core/XSigUtility/Tokens/XSigTokenType.cs @@ -0,0 +1,23 @@ +namespace PepperDash.Core.Intersystem.Tokens +{ + /// + /// XSig token types. + /// + public enum XSigTokenType + { + /// + /// Digital signal datatype. + /// + Digital, + + /// + /// Analog signal datatype. + /// + Analog, + + /// + /// Serial signal datatype. + /// + Serial + } +} \ No newline at end of file diff --git a/Pepperdash Core/Pepperdash Core/XSigUtility/XSigHelpers.cs b/Pepperdash Core/Pepperdash Core/XSigUtility/XSigHelpers.cs new file mode 100644 index 0000000..4ea6f63 --- /dev/null +++ b/Pepperdash Core/Pepperdash Core/XSigUtility/XSigHelpers.cs @@ -0,0 +1,239 @@ +using System; +using System.Linq; +using Crestron.SimplSharp.CrestronIO; +using PepperDash.Core.Intersystem.Serialization; +using PepperDash.Core.Intersystem.Tokens; + +/* + Digital (2 bytes) + 10C##### 0####### (mask = 11000000_10000000b -> 0xC080) + + Analog (4 bytes) + 11aa0### 0####### (mask = 11001000_10000000b -> 0xC880) + 0aaaaaaa 0aaaaaaa + + Serial (Variable length) + 11001### 0####### (mask = 11111000_10000000b -> 0xF880) + dddddddd ........ <- up to 252 bytes of serial data (255 - 3) + 11111111 <- denotes end of data +*/ + +namespace PepperDash.Core.Intersystem +{ + /// + /// Helper methods for creating XSig byte sequences compatible with the Intersystem Communications (ISC) symbol. + /// + /// + /// Indexing is not from the start of each signal type but rather from the beginning of the first defined signal + /// the Intersystem Communications (ISC) symbol. + /// + public static class XSigHelpers + { + /// + /// Forces all outputs to 0. + /// + /// Bytes in XSig format for clear outputs trigger. + public static byte[] ClearOutputs() + { + return new byte[] { 0xFC }; + } + + /// + /// Evaluate all inputs and re-transmit any digital, analog, and permanent serail signals not set to 0. + /// + /// Bytes in XSig format for send status trigger. + public static byte[] SendStatus() + { + return new byte[] { 0xFD }; + } + + /// + /// Get bytes for an IXSigStateResolver object. + /// + /// XSig state resolver. + /// Bytes in XSig format for each token within the state representation. + public static byte[] GetBytes(IXSigSerialization xSigSerialization) + { + return GetBytes(xSigSerialization, 0); + } + + /// + /// Get bytes for an IXSigStateResolver object, with a specified offset. + /// + /// XSig state resolver. + /// Offset to which the data will be aligned. + /// Bytes in XSig format for each token within the state representation. + public static byte[] GetBytes(IXSigSerialization xSigSerialization, int offset) + { + var tokens = xSigSerialization.Serialize(); + if (tokens == null) return new byte[0]; + using (var memoryStream = new MemoryStream()) + { + using (var tokenWriter = new XSigTokenStreamWriter(memoryStream)) + tokenWriter.WriteXSigData(xSigSerialization, offset); + + return memoryStream.ToArray(); + } + } + + /// + /// Get bytes for a single digital signal. + /// + /// 1-based digital index + /// Digital data to be encoded + /// Bytes in XSig format for digtial information. + public static byte[] GetBytes(int index, bool value) + { + return GetBytes(index, 0, value); + } + + /// + /// Get bytes for a single digital signal. + /// + /// 1-based digital index + /// Index offset. + /// Digital data to be encoded + /// Bytes in XSig format for digtial information. + public static byte[] GetBytes(int index, int offset, bool value) + { + return new XSigDigitalToken(index + offset, value).GetBytes(); + } + + /// + /// Get byte sequence for multiple digital signals. + /// + /// Starting index of the sequence. + /// Digital signal value array. + /// Byte sequence in XSig format for digital signal information. + public static byte[] GetBytes(int startIndex, bool[] values) + { + return GetBytes(startIndex, 0, values); + } + + /// + /// Get byte sequence for multiple digital signals. + /// + /// Starting index of the sequence. + /// Index offset. + /// Digital signal value array. + /// Byte sequence in XSig format for digital signal information. + public static byte[] GetBytes(int startIndex, int offset, bool[] values) + { + // Digital XSig data is 2 bytes per value + const int fixedLength = 2; + var bytes = new byte[values.Length * fixedLength]; + for (var i = 0; i < values.Length; i++) + Buffer.BlockCopy(GetBytes(startIndex++, offset, values[i]), 0, bytes, i * fixedLength, fixedLength); + + return bytes; + } + + /// + /// Get bytes for a single analog signal. + /// + /// 1-based analog index + /// Analog data to be encoded + /// Bytes in XSig format for analog signal information. + public static byte[] GetBytes(int index, ushort value) + { + return GetBytes(index, 0, value); + } + + /// + /// Get bytes for a single analog signal. + /// + /// 1-based analog index + /// Index offset. + /// Analog data to be encoded + /// Bytes in XSig format for analog signal information. + public static byte[] GetBytes(int index, int offset, ushort value) + { + return new XSigAnalogToken(index + offset, value).GetBytes(); + } + + /// + /// Get byte sequence for multiple analog signals. + /// + /// Starting index of the sequence. + /// Analog signal value array. + /// Byte sequence in XSig format for analog signal information. + public static byte[] GetBytes(int startIndex, ushort[] values) + { + return GetBytes(startIndex, 0, values); + } + + /// + /// Get byte sequence for multiple analog signals. + /// + /// Starting index of the sequence. + /// Index offset. + /// Analog signal value array. + /// Byte sequence in XSig format for analog signal information. + public static byte[] GetBytes(int startIndex, int offset, ushort[] values) + { + // Analog XSig data is 4 bytes per value + const int fixedLength = 4; + var bytes = new byte[values.Length * fixedLength]; + for (var i = 0; i < values.Length; i++) + Buffer.BlockCopy(GetBytes(startIndex++, offset, values[i]), 0, bytes, i * fixedLength, fixedLength); + + return bytes; + } + + /// + /// Get bytes for a single serial signal. + /// + /// 1-based serial index + /// Serial data to be encoded + /// Bytes in XSig format for serial signal information. + public static byte[] GetBytes(int index, string value) + { + return GetBytes(index, 0, value); + } + + /// + /// Get bytes for a single serial signal. + /// + /// 1-based serial index + /// Index offset. + /// Serial data to be encoded + /// Bytes in XSig format for serial signal information. + public static byte[] GetBytes(int index, int offset, string value) + { + return new XSigSerialToken(index + offset, value).GetBytes(); + } + + /// + /// Get byte sequence for multiple serial signals. + /// + /// Starting index of the sequence. + /// Serial signal value array. + /// Byte sequence in XSig format for serial signal information. + public static byte[] GetBytes(int startIndex, string[] values) + { + return GetBytes(startIndex, 0, values); + } + + /// + /// Get byte sequence for multiple serial signals. + /// + /// Starting index of the sequence. + /// Index offset. + /// Serial signal value array. + /// Byte sequence in XSig format for serial signal information. + public static byte[] GetBytes(int startIndex, int offset, string[] values) + { + // Serial XSig data is not fixed-length like the other formats + var dstOffset = 0; + var bytes = new byte[values.Sum(v => v.Length + 3)]; + for (var i = 0; i < values.Length; i++) + { + var data = GetBytes(startIndex++, offset, values[i]); + Buffer.BlockCopy(data, 0, bytes, dstOffset, data.Length); + dstOffset += data.Length; + } + + return bytes; + } + } +} \ No newline at end of file diff --git a/Pepperdash Core/Pepperdash Core/XSigUtility/XSigTokenStreamReader.cs b/Pepperdash Core/Pepperdash Core/XSigUtility/XSigTokenStreamReader.cs new file mode 100644 index 0000000..9d70d02 --- /dev/null +++ b/Pepperdash Core/Pepperdash Core/XSigUtility/XSigTokenStreamReader.cs @@ -0,0 +1,147 @@ +using System; +using System.Collections.Generic; +using Crestron.SimplSharp.CrestronIO; +using PepperDash.Core.Intersystem.Serialization; +using PepperDash.Core.Intersystem.Tokens; + +namespace PepperDash.Core.Intersystem +{ + /// + /// XSigToken stream reader. + /// + public sealed class XSigTokenStreamReader : IDisposable + { + private readonly Stream _stream; + private readonly bool _leaveOpen; + + /// + /// + /// XSigToken stream reader constructor. + /// + /// Input stream to read from. + /// Stream is null. + /// Stream cannot be read from. + public XSigTokenStreamReader(Stream stream) + : this(stream, false) { } + + /// + /// XSigToken stream reader constructor. + /// + /// Input stream to read from. + /// Determines whether to leave the stream open or not. + /// Stream is null. + /// Stream cannot be read from. + public XSigTokenStreamReader(Stream stream, bool leaveOpen) + { + if (stream == null) + throw new ArgumentNullException("stream"); + if (!stream.CanRead) + throw new ArgumentException("The specified stream cannot be read from."); + + _stream = stream; + _leaveOpen = leaveOpen; + } + + /// + /// Reads a 16-bit unsigned integer from the specified stream using Big Endian byte order. + /// + /// Input stream + /// Result + /// True if successful, otherwise false. + public static bool TryReadUInt16BE(Stream stream, out ushort value) + { + value = 0; + if (stream.Length < 2) + return false; + + var buffer = new byte[2]; + stream.Read(buffer, 0, 2); + value = (ushort)((buffer[0] << 8) | buffer[1]); + return true; + } + + /// + /// Read XSig token from the stream. + /// + /// XSigToken + /// Offset is less than 0. + public XSigToken ReadXSigToken() + { + ushort prefix; + if (!TryReadUInt16BE(_stream, out prefix)) + return null; + + if ((prefix & 0xF880) == 0xC800) // Serial data + { + var index = ((prefix & 0x0700) >> 1) | (prefix & 0x7F); + var n = 0; + const int maxSerialDataLength = 252; + var chars = new char[maxSerialDataLength]; + int ch; + while ((ch = _stream.ReadByte()) != 0xFF) + { + if (ch == -1) // Reached end of stream without end of data marker + return null; + + chars[n++] = (char)ch; + } + + return new XSigSerialToken((ushort)(index + 1), new string(chars, 0, n)); + } + + if ((prefix & 0xC880) == 0xC000) // Analog data + { + ushort data; + if (!TryReadUInt16BE(_stream, out data)) + return null; + + var index = ((prefix & 0x0700) >> 1) | (prefix & 0x7F); + var value = ((prefix & 0x3000) << 2) | ((data & 0x7F00) >> 1) | (data & 0x7F); + return new XSigAnalogToken((ushort)(index + 1), (ushort)value); + } + + if ((prefix & 0xC080) == 0x8000) // Digital data + { + var index = ((prefix & 0x1F00) >> 1) | (prefix & 0x7F); + var value = (prefix & 0x2000) == 0; + return new XSigDigitalToken((ushort)(index + 1), value); + } + + return null; + } + + /// + /// Reads all available XSig tokens from the stream. + /// + /// XSigToken collection. + public IEnumerable ReadAllXSigTokens() + { + var tokens = new List(); + XSigToken token; + while ((token = ReadXSigToken()) != null) + tokens.Add(token); + + return tokens; + } + + /// + /// Attempts to deserialize all XSig data within the stream from the current position. + /// + /// Type to deserialize the information to. + /// Deserialized object. + public T DeserializeStream() + where T : class, IXSigSerialization, new() + { + return new T().Deserialize(ReadAllXSigTokens()); + } + + /// + /// Disposes of the internal stream if specified to not leave open. + /// + public void Dispose() + { + if (!_leaveOpen) + _stream.Dispose(); + } + } +} \ No newline at end of file diff --git a/Pepperdash Core/Pepperdash Core/XSigUtility/XSigTokenStreamWriter.cs b/Pepperdash Core/Pepperdash Core/XSigUtility/XSigTokenStreamWriter.cs new file mode 100644 index 0000000..934f2c2 --- /dev/null +++ b/Pepperdash Core/Pepperdash Core/XSigUtility/XSigTokenStreamWriter.cs @@ -0,0 +1,136 @@ +using System; +using System.Linq; +using System.Collections.Generic; +using Crestron.SimplSharp.CrestronIO; +using PepperDash.Core.Intersystem.Serialization; +using PepperDash.Core.Intersystem.Tokens; + +namespace PepperDash.Core.Intersystem +{ + /// + /// XSigToken stream writer. + /// + public sealed class XSigTokenStreamWriter : IDisposable + { + private readonly Stream _stream; + private readonly bool _leaveOpen; + + /// + /// + /// XSigToken stream writer constructor. + /// + /// Input stream to write to. + /// Stream is null. + /// Stream cannot be written to. + public XSigTokenStreamWriter(Stream stream) + : this(stream, false) { } + + /// + /// XSigToken stream writer constructor. + /// + /// Input stream to write to. + /// Determines whether to leave the stream open or not. + /// Stream is null. + /// Stream cannot be written to. + public XSigTokenStreamWriter(Stream stream, bool leaveOpen) + { + if (stream == null) + throw new ArgumentNullException("stream"); + if (!stream.CanWrite) + throw new ArgumentException("The specified stream cannot be written to."); + + _stream = stream; + _leaveOpen = leaveOpen; + } + + /// + /// Write XSig data gathered from an IXSigStateResolver to the stream. + /// + /// IXSigStateResolver object. + public void WriteXSigData(IXSigSerialization xSigSerialization) + { + WriteXSigData(xSigSerialization, 0); + } + + /// + /// Write XSig data gathered from an IXSigStateResolver to the stream. + /// + /// IXSigStateResolver object. + /// Index offset for each XSigToken. + public void WriteXSigData(IXSigSerialization xSigSerialization, int offset) + { + if (xSigSerialization == null) + throw new ArgumentNullException("xSigSerialization"); + + var tokens = xSigSerialization.Serialize(); + WriteXSigData(tokens, offset); + } + + /// + /// Write XSigToken to the stream. + /// + /// XSigToken object. + public void WriteXSigData(XSigToken token) + { + WriteXSigData(token, 0); + } + + /// + /// Write XSigToken to the stream. + /// + /// XSigToken object. + /// Index offset for each XSigToken. + public void WriteXSigData(XSigToken token, int offset) + { + WriteXSigData(new[] { token }, offset); + } + + /// + /// Writes an array of XSigTokens to the stream. + /// + /// XSigToken objects. + public void WriteXSigData(XSigToken[] tokens) + { + WriteXSigData(tokens.AsEnumerable()); + } + + /// + /// Write an enumerable collection of XSigTokens to the stream. + /// + /// XSigToken objects. + public void WriteXSigData(IEnumerable tokens) + { + WriteXSigData(tokens, 0); + } + + /// + /// Write an enumerable collection of XSigTokens to the stream. + /// + /// XSigToken objects. + /// Index offset for each XSigToken. + public void WriteXSigData(IEnumerable tokens, int offset) + { + if (offset < 0) + throw new ArgumentOutOfRangeException("offset", "Offset must be greater than or equal to 0."); + + if (tokens != null) + { + foreach (var token in tokens) + { + if (token == null) continue; + var bytes = token.GetTokenWithOffset(offset).GetBytes(); + _stream.Write(bytes, 0, bytes.Length); + } + } + } + + /// + /// Disposes of the internal stream if specified to not leave open. + /// + public void Dispose() + { + if (!_leaveOpen) + _stream.Dispose(); + } + } +} \ No newline at end of file