NetMQFrame.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
  1. using System;
  2. using System.Text;
  3. namespace NetMQ
  4. {
  5. /// <summary>
  6. /// Objects of class NetMQFrame serve to hold a Buffer (that consists of a byte-array containing a unit of a message-queue message)
  7. /// and provide methods to construct it given a string and an encoding.
  8. /// </summary>
  9. public class NetMQFrame : IEquatable<NetMQFrame>, IEquatable<byte[]>
  10. {
  11. /// <summary>
  12. /// This is the length of the byte-array data buffer.
  13. /// </summary>
  14. private int m_messageSize;
  15. /// <summary>
  16. /// This holds the computed hash-code for this object.
  17. /// </summary>
  18. private int m_hash;
  19. /// <summary>
  20. /// Create a new NetMQFrame containing the given byte-array data.
  21. /// </summary>
  22. /// <param name="buffer">a byte-array to hold as the frame's data</param>
  23. public NetMQFrame(byte[]? buffer)
  24. {
  25. buffer ??= EmptyArray<byte>.Instance;
  26. Buffer = buffer;
  27. MessageSize = buffer.Length;
  28. }
  29. /// <summary>
  30. /// Instantiates a frame from the provided byte array, considering only the specified number of bytes.
  31. /// </summary>
  32. /// <remarks>This constructor may be useful to avoid copying data into a smaller array when a buffer is oversized.</remarks>
  33. /// <param name="buffer">The content of the frame.</param>
  34. /// <param name="length">The number bytes from <paramref name="buffer"/> to consider as part of the frame.</param>
  35. public NetMQFrame(byte[] buffer, int length)
  36. {
  37. if (buffer == null)
  38. throw new ArgumentNullException(nameof(buffer));
  39. if (length > buffer.Length)
  40. throw new ArgumentOutOfRangeException(nameof(length), length, "Must be less than or equal to the provided buffer's length.");
  41. Buffer = buffer;
  42. MessageSize = length;
  43. }
  44. /// <summary>
  45. /// Create a new NetMQFrame containing the given string-message,
  46. /// using the default ASCII encoding.
  47. /// </summary>
  48. /// <param name="message">a string containing the message-data of the frame</param>
  49. public NetMQFrame(string message)
  50. : this(Encoding.ASCII.GetBytes(message))
  51. {}
  52. /// <summary>
  53. /// Create a new NetMQFrame containing the given string-message,
  54. /// using the given encoding to convert it into a byte-array.
  55. /// </summary>
  56. /// <param name="message">a string containing the message-data of the frame</param>
  57. /// <param name="encoding">the Encoding to use to convert the given string-message into the internal byte-array</param>
  58. public NetMQFrame(string message, Encoding encoding)
  59. : this(encoding.GetBytes(message))
  60. {}
  61. /// <summary>
  62. /// Create a new NetMQFrame with a data-buffer pre-sized to the given length.
  63. /// </summary>
  64. /// <param name="length">the number of bytes to allocate for the data-buffer</param>
  65. /// <exception cref="ArgumentOutOfRangeException">length must be non-negative (zero or positive).</exception>
  66. public NetMQFrame(int length)
  67. {
  68. if (length < 0)
  69. {
  70. throw new ArgumentOutOfRangeException(nameof(length), "A non-negative value is expected.");
  71. }
  72. Buffer = new byte[length];
  73. MessageSize = length;
  74. }
  75. /// <summary>
  76. /// Get or set the size of the message data contained in the frame, which here represents the number of bytes.
  77. /// </summary>
  78. /// <exception cref="ArgumentOutOfRangeException">The value must be between zero and BufferSize.</exception>
  79. public int MessageSize
  80. {
  81. get => m_messageSize;
  82. set
  83. {
  84. if (value < 0 || value > BufferSize)
  85. {
  86. throw new ArgumentOutOfRangeException(nameof(value), "Expecting a non-negative value less than or equal to the buffer size.");
  87. }
  88. m_messageSize = value;
  89. }
  90. }
  91. /// <summary>
  92. /// Get the underlying frame-data buffer, which is an array of bytes.
  93. /// </summary>
  94. public byte[] Buffer { get; }
  95. /// <summary>
  96. /// Get the maximum size of the frame-data buffer (ie, the number of bytes of the array).
  97. /// </summary>
  98. public int BufferSize => Buffer.Length;
  99. /// <summary>
  100. /// Get a new empty <see cref="NetMQFrame"/> that may be used as message separators.
  101. /// </summary>
  102. public static NetMQFrame Empty => new NetMQFrame(0);
  103. /// <summary>
  104. /// Get whether this NetMQFrame is empty - that is, has a Buffer of zero-length.
  105. /// </summary>
  106. public bool IsEmpty => MessageSize == 0;
  107. /// <summary>
  108. /// Create and return a new NetMQFrame with a copy of the supplied byte-array buffer.
  109. /// </summary>
  110. /// <param name="buffer">the byte-array to copy into the new NetMQFrame</param>
  111. /// <returns>a new <see cref="NetMQFrame"/> containing a copy of the supplied byte-array</returns>
  112. /// <exception cref="ArgumentNullException"><paramref name="buffer"/> is null.</exception>
  113. public static NetMQFrame Copy(byte[] buffer)
  114. {
  115. if (buffer == null)
  116. {
  117. throw new ArgumentNullException(nameof(buffer));
  118. }
  119. var copy = new NetMQFrame(buffer.Length);
  120. System.Buffer.BlockCopy(buffer, 0, copy.Buffer, 0, buffer.Length);
  121. return copy;
  122. }
  123. /// <summary>
  124. /// Return this frame's data-buffer converted into a string, using the default ASCII encoding.
  125. /// </summary>
  126. /// <returns>the data buffer converted to a string</returns>
  127. public string ConvertToString()
  128. {
  129. return Encoding.ASCII.GetString(Buffer, 0, MessageSize);
  130. }
  131. /// <summary>
  132. /// Return this frame's data-buffer converted into a string using the given encoding.
  133. /// </summary>
  134. /// <param name="encoding">the Encoding to use to convert the internal byte-array buffer into a string</param>
  135. /// <returns>the data buffer converted to a string</returns>
  136. public string ConvertToString(Encoding encoding)
  137. {
  138. return encoding.GetString(Buffer, 0, MessageSize);
  139. }
  140. /// <summary>
  141. /// Convert the buffer to integer in network byte order (big-endian)
  142. /// </summary>
  143. /// <returns></returns>
  144. public int ConvertToInt32()
  145. {
  146. return NetworkOrderBitsConverter.ToInt32(Buffer);
  147. }
  148. /// <summary>
  149. /// Convert the buffer to long in network byte order (big-endian)
  150. /// </summary>
  151. /// <returns></returns>
  152. public long ConvertToInt64()
  153. {
  154. return NetworkOrderBitsConverter.ToInt64(Buffer);
  155. }
  156. /// <summary>
  157. /// Create a deep-copy of the supplied <see cref="NetMQFrame"/>.
  158. /// </summary>
  159. /// <param name="frame">the <see cref="NetMQFrame"/> to copy</param>
  160. /// <returns>a <see cref="NetMQFrame"/> containing a copy of <paramref name="frame"/></returns>
  161. /// <exception cref="ArgumentNullException"><paramref name="frame"/> is null.</exception>
  162. public static NetMQFrame Copy(NetMQFrame frame)
  163. {
  164. if (frame == null)
  165. {
  166. throw new ArgumentNullException(nameof(frame));
  167. }
  168. var copy = new NetMQFrame(new byte[frame.BufferSize]) { MessageSize = frame.MessageSize };
  169. System.Buffer.BlockCopy(frame.Buffer, 0, copy.Buffer, 0, frame.BufferSize);
  170. return copy;
  171. }
  172. /// <summary>
  173. /// Create a deep-copy of this NetMQFrame and return it.
  174. /// </summary>
  175. /// <returns>a new NetMQFrame containing a copy of this one's buffer data</returns>
  176. public NetMQFrame Duplicate()
  177. {
  178. return Copy(this);
  179. }
  180. /// <summary>
  181. /// Return true if the buffer of this NetMQFrame is equal to the given byte-array.
  182. /// </summary>
  183. /// <param name="other">a byte-array buffer to compare this frame against</param>
  184. /// <returns></returns>
  185. public bool Equals(byte[]? other)
  186. {
  187. if (other?.Length != MessageSize)
  188. return false;
  189. if (ReferenceEquals(Buffer, other))
  190. return true;
  191. for (int i = 0; i < MessageSize; i++)
  192. {
  193. if (Buffer[i] != other[i])
  194. return false;
  195. }
  196. return true;
  197. }
  198. /// <summary>
  199. /// Determine whether the specified <see cref="NetMQFrame"/> is equal to the current <see cref="NetMQFrame"/>.
  200. /// </summary>
  201. /// <param name="other">the <see cref="NetMQFrame"/> to compare with the current <see cref="NetMQFrame"/>.</param>
  202. /// <returns>true if the specified NetMQFrame is equal to this one; otherwise, false</returns>
  203. public bool Equals(NetMQFrame? other)
  204. {
  205. if (other == null)
  206. return false;
  207. if (ReferenceEquals(this, other))
  208. return true;
  209. if (MessageSize > other.BufferSize || MessageSize != other.MessageSize)
  210. {
  211. return false;
  212. }
  213. for (int i = 0; i < MessageSize; i++)
  214. {
  215. if (Buffer[i] != other.Buffer[i])
  216. {
  217. return false;
  218. }
  219. }
  220. return true;
  221. }
  222. bool IEquatable<NetMQFrame>.Equals(NetMQFrame other)
  223. {
  224. return Equals(other);
  225. }
  226. /// <summary>
  227. /// Return true if the given Object is a NetMQFrame which has a Buffer that is identical to that of this one.
  228. /// </summary>
  229. /// <param name="obj">the Object to compare this to</param>
  230. /// <returns>true only if the given Object is a NetMQFrame equal to this one</returns>
  231. public override bool Equals(object obj)
  232. {
  233. return Equals(obj as NetMQFrame);
  234. }
  235. /// <summary>
  236. /// Return true if this one and the other NetMQFrame are equal, or both are null.
  237. /// </summary>
  238. /// <param name="one">one frame to compare against the other</param>
  239. /// <param name="other">the other frame to compare</param>
  240. /// <returns>true if both frames are equal</returns>
  241. public static bool operator ==(NetMQFrame? one, NetMQFrame? other)
  242. {
  243. // NOTE use of ReferenceEquals here to avoid recurrence and stack overflow exception
  244. if (one is null && other is null)
  245. return true;
  246. return one is object && one.Equals(other!);
  247. }
  248. /// <summary>
  249. /// Return true if this one and the other NetMQFrame NOT are equal.
  250. /// </summary>
  251. /// <param name="one">one frame to compare against the other</param>
  252. /// <param name="other">the other frame to compare</param>
  253. /// <returns>false if both frames are equal</returns>
  254. public static bool operator !=(NetMQFrame? one, NetMQFrame? other)
  255. {
  256. return !(one == other);
  257. }
  258. /// <summary>
  259. /// Override the Object.GetHashCode method to return a hash-code derived from the content of the Buffer.
  260. /// That is only computed the first time this method is called.
  261. /// </summary>
  262. /// <returns>an integer that represents the computed hash-code</returns>
  263. [System.Diagnostics.CodeAnalysis.SuppressMessage("ReSharper", "NonReadonlyMemberInGetHashCode")]
  264. public override int GetHashCode()
  265. {
  266. if (m_hash == 0)
  267. {
  268. foreach (var b in Buffer)
  269. {
  270. m_hash = (31*m_hash) ^ b;
  271. }
  272. }
  273. return m_hash;
  274. }
  275. /// <summary>
  276. /// Return an array of bytes that carries the content of this NetMQFrames Buffer.
  277. /// </summary>
  278. /// <param name="copy">if this argument is true - a new copy is made if BufferSize is not equal to MessageSize</param>
  279. /// <returns>the Buffer as a byte-array, either newly-allocated or else (if copy is false) simply a reference to the actual Buffer</returns>
  280. public byte[] ToByteArray(bool copy = false)
  281. {
  282. if (!copy || MessageSize == BufferSize)
  283. {
  284. return Buffer;
  285. }
  286. var byteArray = new byte[MessageSize];
  287. System.Buffer.BlockCopy(Buffer, 0, byteArray, 0, MessageSize);
  288. return byteArray;
  289. }
  290. /// <summary>
  291. /// Return an readonly span of bytes that carries the content of this NetMQFrames Buffer.
  292. /// </summary>
  293. /// <returns>the Buffer as a readonly span</returns>
  294. public ReadOnlySpan<byte> AsSpan()
  295. {
  296. return new ReadOnlySpan<byte>(Buffer, 0, MessageSize);
  297. }
  298. }
  299. }