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. /// /// 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. /// 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. /// 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. /// 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. /// 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. /// 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. /// /// 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. /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] bool CanReadMore() { return m_Stream.Position < m_Stream.Length; } } }