// %BANNER_BEGIN% // --------------------------------------------------------------------- // %COPYRIGHT_BEGIN% // Copyright (c) 2022-2023 Magic Leap, Inc. All Rights Reserved. // Use of this file is governed by the Magic Leap 2 Software License Agreement, located here: https://www.magicleap.com/software-license-agreement-ml2 // Terms and conditions applicable to third-party materials accompanying this distribution may also be found in the top-level NOTICE file appearing herein. // %COPYRIGHT_END% // --------------------------------------------------------------------- // %BANNER_END% using System; using System.Buffers; using System.IO; using System.Net.Security; using System.Runtime.InteropServices; namespace MagicLeap.Spectator { public class Message : IDisposable { #region Static private static ArrayPool bufferPool = ArrayPool.Create(); public static implicit operator Message(Guid guid) { return new Message(guid, null, 0); } #endregion #region Public properties public Guid guid { get; private set; } = Guid.Empty; public byte[] data { get; private set; } = null; public int size { get; private set; } = 0; #endregion #region Private variables private bool fromArrayPool = false; #endregion #region Constructors / Destructor public Message(Guid guid, byte[] data, int size) { this.guid = guid; this.data = data; this.size = size; fromArrayPool = false; } public Message(Guid guid, byte[] data) { this.guid = guid; this.data = data; size = data.Length; fromArrayPool = false; } public Message(Guid guid, int size) { this.guid = guid; this.size = size; // Get a buffer from our buffer pool data = bufferPool.Rent(size); fromArrayPool = true; } public Message(SslStream stream, SafeBool Connected, SafeBool Closed) { // Function to make sure we get all the data we need void Read(byte[] buffer, int size, bool block) { for (int r = 0, read = 0; Connected && read < size;) { try { r = stream.Read(buffer, read, size - read); } catch (Exception ex) { if (!block) throw (ex); } if (r < 0) { read = r; break; } read += r; } } byte[] sizeBytes = new byte[sizeof(int)]; byte[] guidBytes = new byte[Marshal.SizeOf()]; // Read our message size Read(sizeBytes, sizeBytes.Length, true); size = BitConverter.ToInt32(sizeBytes); // If our size is less than zero, we have received an exit command if (size < 0) { Closed.Set(true); Connected.Set(false); return; } // Read our message guid Read(guidBytes, guidBytes.Length, false); guid = new Guid(guidBytes); // If we have a payload if (size > 0) { // Get a buffer from our buffer pool data = bufferPool.Rent(size); fromArrayPool = true; // Read our payload into our buffer Read(data, size, false); } } ~Message() { Dispose(); } #endregion #region Public methods // Messages received via TcpConnection are disposed of after all callbacks are invoked // If you wish to hold onto information from a message you must copy it yourself public void Dispose() { if (data == null) return; if (fromArrayPool) { bufferPool.Return(data); data = null; } } public static byte[] ToByteArray(Message msg) { using (MemoryStream stream = new()) { StreamHelper.Write(stream, msg.guid); StreamHelper.Write(stream, msg.size); if (msg.size > 0) StreamHelper.Write(stream, msg.data, msg.size); return stream.ToArray(); } } public static Message FromByteArray(byte[] bytes) { using (MemoryStream stream = new()) { stream.Write(bytes, 0, bytes.Length); stream.Seek(0, SeekOrigin.Begin); Guid guid = StreamHelper.ReadGuid(stream); int size = StreamHelper.ReadInt32(stream); byte[] data = null; if (size > 0) data = StreamHelper.ReadBytes(stream, size); return new Message(guid, data, size); } } #endregion } }