using System.Collections.Generic; using System.Runtime.CompilerServices; using System; using System.IO; using System.Text; namespace Unity.MLAgents.SideChannels { /// /// Utility class for reading the data sent to the SideChannel. /// public class IncomingMessage : IDisposable { byte[] m_Data; Stream m_Stream; BinaryReader m_Reader; /// /// Construct an IncomingMessage from the byte array. /// /// Byte array public IncomingMessage(byte[] data) { m_Data = data; m_Stream = new MemoryStream(data); m_Reader = new BinaryReader(m_Stream); } /// /// Read a boolean value from the message. /// /// Default value to use if the end of the message is reached. /// True if boolean was read by the reader, False if not. public bool ReadBoolean(bool defaultValue = false) { return CanReadMore() ? m_Reader.ReadBoolean() : defaultValue; } /// /// Read an integer value from the message. /// /// Default value to use if the end of the message is reached. /// True if int32 was read by the reader, False if not. public int ReadInt32(int defaultValue = 0) { return CanReadMore() ? m_Reader.ReadInt32() : defaultValue; } /// /// Read a float value from the message. /// /// Default value to use if the end of the message is reached. /// True if float32 was read by the reader, False if not. public float ReadFloat32(float defaultValue = 0.0f) { return CanReadMore() ? m_Reader.ReadSingle() : defaultValue; } /// /// Read a string value from the message. /// /// Default value to use if the end of the message is reached. /// True if string was read by the reader, False if not. public string ReadString(string defaultValue = default) { if (!CanReadMore()) { return defaultValue; } var strLength = ReadInt32(); var str = Encoding.ASCII.GetString(m_Reader.ReadBytes(strLength)); return str; } /// /// Reads a list of floats from the message. The length of the list is stored in the message. /// /// Default value to use if the end of the message is reached. /// True if list of float was read by the reader, False if not. public IList ReadFloatList(IList defaultValue = default) { if (!CanReadMore()) { return defaultValue; } var len = ReadInt32(); var output = new float[len]; for (var i = 0; i < len; i++) { output[i] = ReadFloat32(); } return output; } /// /// Gets the original data of the message. Note that this will return all of the data, /// even if part of it has already been read. /// /// Original data of the message. public byte[] GetRawBytes() { return m_Data; } /// /// Clean up the internal storage. /// public void Dispose() { m_Reader?.Dispose(); m_Stream?.Dispose(); } /// /// Whether or not there is more data left in the stream that can be read. /// /// True if there is still data left in the stream that can be read, False if not. [MethodImpl(MethodImplOptions.AggressiveInlining)] bool CanReadMore() { return m_Stream.Position < m_Stream.Length; } } }