Msg.cs 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761
  1. /*
  2. Copyright (c) 2007-2012 iMatix Corporation
  3. Copyright (c) 2009-2011 250bpm s.r.o.
  4. Copyright (c) 2007-2015 Other contributors as noted in the AUTHORS file
  5. This file is part of 0MQ.
  6. 0MQ is free software; you can redistribute it and/or modify it under
  7. the terms of the GNU Lesser General Public License as published by
  8. the Free Software Foundation; either version 3 of the License, or
  9. (at your option) any later version.
  10. 0MQ is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. GNU Lesser General Public License for more details.
  14. You should have received a copy of the GNU Lesser General Public License
  15. along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. */
  17. using System;
  18. using System.Collections;
  19. using System.Collections.Generic;
  20. using System.Runtime.CompilerServices;
  21. using System.Text;
  22. using NetMQ.Core.Utils;
  23. namespace NetMQ
  24. {
  25. /// <summary>Defines a set of flags applicable to a <see cref="Msg"/> instance: None (default), More, Identity, Shared</summary>
  26. [Flags]
  27. public enum MsgFlags : byte
  28. {
  29. /// <summary>Indicates no flags are set (the default).</summary>
  30. None = 0,
  31. /// <summary>Indicates that more frames of the same message follow.</summary>
  32. More = 1,
  33. /// <summary>
  34. /// Command frame (see ZMTP spec)
  35. /// Command types, use only bits 2-5 and compare with ==, not bitwise,
  36. /// a command can never be of more that one type at the same time
  37. /// </summary>
  38. Command = 2,
  39. /// <summary>Indicates that this frame conveys the identity of a connected peer.</summary>
  40. Identity = 64,
  41. /// <summary>Indicates that this frame's internal data is shared with other <see cref="Msg"/> objects.</summary>
  42. Shared = 128,
  43. }
  44. /// <summary>Enumeration of possible <see cref="Msg"/> types: Uninitialised, GC, Pool, Delimiter.</summary>
  45. public enum MsgType : byte
  46. {
  47. /// <summary>The <see cref="Msg"/> has not yet been initialised (default value).</summary>
  48. Uninitialised = 0,
  49. /// <summary>The <see cref="Msg"/> is empty.</summary>
  50. Empty = 101,
  51. /// <summary>The <see cref="Msg"/> data will be garbage collected when no longer needed.</summary>
  52. GC = 102,
  53. /// <summary>
  54. /// Join message for radio-dish
  55. /// </summary>
  56. Join = 106,
  57. /// <summary>
  58. /// Leave message for radio_dish
  59. /// </summary>
  60. Leave = 107,
  61. /// <summary>
  62. /// The <see cref="Msg"/> data was allocated by <see cref="BufferPool"/>, and must be released back
  63. /// to this pool when no longer needed. This happens when <see cref="Msg.Close"/> is called.
  64. /// </summary>
  65. Pool = 103,
  66. /// <summary>The <see cref="Msg"/> is a delimiter frame and doesn't contain any data.</summary>
  67. /// <remarks>Delimiters are commonly used to mark a boundary between groups frames.</remarks>
  68. Delimiter = 104
  69. }
  70. /// <summary>
  71. /// A Msg struct is the lowest-level interpretation of a ZeroMQ message, and simply contains byte-array data
  72. /// and MsgType and MsgFlags properties.
  73. /// It supports message buffer pooling.
  74. /// </summary>
  75. /// <remarks>
  76. /// Many users will not use this class directly. However in high-performance situations it
  77. /// may be useful. When used correctly it's possible to have zero-copy and zero-allocation
  78. /// behaviour.
  79. /// </remarks>
  80. public struct Msg
  81. {
  82. /// <summary>
  83. /// The maximum length of a group (Radio/Dish)
  84. /// </summary>
  85. public const int MaxGroupLength = 255;
  86. /// <summary>An atomic reference count for knowing when to release a pooled data buffer back to the <see cref="BufferPool"/>.</summary>
  87. /// <remarks>Will be <c>null</c> unless <see cref="MsgType"/> equals <see cref="NetMQ.MsgType.Pool"/>.</remarks>
  88. private AtomicCounter? m_refCount;
  89. private byte[]? m_data;
  90. private int m_offset;
  91. private uint m_routingId;
  92. private string m_group;
  93. /// <summary>
  94. /// Get the number of bytes within the Data property.
  95. /// </summary>
  96. public int Size { get; private set; }
  97. /// <summary>
  98. /// Returns true if msg is empty
  99. /// </summary>
  100. public bool IsEmpty => m_data == null || Size == 0;
  101. /// <summary>
  102. /// Returns true if the msg is join message
  103. /// </summary>
  104. public bool IsJoin => MsgType == MsgType.Join;
  105. /// <summary>
  106. /// Returns true if the msg is leave message
  107. /// </summary>
  108. public bool IsLeave => MsgType == MsgType.Leave;
  109. /// <summary>
  110. /// Gets the position of the first element in the Data property delimited by the message,
  111. /// relative to the start of the original array.
  112. /// Deprecated: use <see cref="Slice()"/> or implicit casting to Span
  113. /// </summary>
  114. [Obsolete("Use implicit casting to Span or Slice instead")]
  115. public int Offset => m_offset;
  116. #region MsgType
  117. /// <summary>Get the type of this message, from the MsgType enum.</summary>
  118. public MsgType MsgType { get; private set; }
  119. /// <summary>
  120. /// Get whether the Delimiter bit is set on the Flags property,
  121. /// which would indicate that this message is intended for use simply to mark a boundary
  122. /// between other parts of some unit of communication.
  123. /// </summary>
  124. public bool IsDelimiter => MsgType == MsgType.Delimiter;
  125. /// <summary>Get whether this <see cref="Msg"/> is initialised and ready for use.</summary>
  126. /// <remarks>A newly constructed <see cref="Msg"/> is uninitialised, and can be initialised via one
  127. /// of <see cref="InitEmpty"/>, <see cref="InitDelimiter"/>, <see cref="InitGC(byte[],int)"/>, <see cref="InitGC(byte[],int,int)"/>, or <see cref="InitPool"/>.
  128. /// Calling <see cref="Close"/> will cause the <see cref="Msg"/> to become uninitialised again.</remarks>
  129. /// <returns><c>true</c> if the <see cref="Msg"/> is initialised, otherwise <c>false</c>.</returns>
  130. public bool IsInitialised => MsgType != MsgType.Uninitialised;
  131. #endregion
  132. #region MsgFlags
  133. /// <summary>
  134. /// Get the flags-enum MsgFlags value, which indicates which of the More, Identity, or Shared bits are set.
  135. /// </summary>
  136. public MsgFlags Flags { get; private set; }
  137. /// <summary>
  138. /// Get the "Has-More" flag, which when set on a message-queue frame indicates that there are more frames to follow.
  139. /// </summary>
  140. public bool HasMore => (Flags & MsgFlags.More) == MsgFlags.More;
  141. internal bool HasCommand => (Flags & MsgFlags.Command) == MsgFlags.Command;
  142. /// <summary>
  143. /// Get whether the <see cref="Data"/> buffer of this <see cref="Msg"/> is shared with another instance.
  144. /// Only applies to pooled message types.
  145. /// </summary>
  146. public bool IsShared => (Flags & MsgFlags.Shared) != 0;
  147. /// <summary>
  148. /// Get whether the Identity bit is set on the Flags property.
  149. /// </summary>
  150. public bool IsIdentity => (Flags & MsgFlags.Identity) != 0;
  151. /// <summary>
  152. /// Set the indicated Flags bits.
  153. /// </summary>
  154. /// <param name="flags">which Flags bits to set (More, Identity, or Shared)</param>
  155. public void SetFlags(MsgFlags flags)
  156. {
  157. Flags |= flags;
  158. }
  159. /// <summary>
  160. /// Clear the indicated Flags bits.
  161. /// </summary>
  162. /// <param name="flags">which Flags bits to clear (More, Identity, or Shared)</param>
  163. public void ResetFlags(MsgFlags flags)
  164. {
  165. Flags &= ~flags;
  166. }
  167. #endregion
  168. /// <summary>
  169. /// Routing id of the message, for SERVER and PEER sockets only.
  170. /// </summary>
  171. public uint RoutingId
  172. {
  173. get => m_routingId;
  174. set
  175. {
  176. if (value == 0)
  177. throw new ArgumentOutOfRangeException("RoutingId cannot be zero.");
  178. m_routingId = value;
  179. }
  180. }
  181. /// <summary>
  182. /// Reset routing id back to zero
  183. /// </summary>
  184. internal void ResetRoutingId()
  185. {
  186. m_routingId = 0;
  187. }
  188. /// <summary>
  189. /// Set or retrieve the group for RADIO/DISH sockets
  190. /// </summary>
  191. /// <exception cref="InvalidException">Value is larger than 255.</exception>
  192. public string Group
  193. {
  194. get => m_group;
  195. set
  196. {
  197. if (value.Length > MaxGroupLength)
  198. throw new ArgumentOutOfRangeException("Group maximum length is 255");
  199. m_group = value;
  200. }
  201. }
  202. /// <summary>
  203. /// Get the byte-array that represents the data payload of this <see cref="Msg"/>.
  204. /// Deprecated: use <see cref="Slice()" /> or implicit casting to Span
  205. /// </summary>
  206. /// <remarks>
  207. /// This value will be <c>null</c> if <see cref="MsgType"/> is <see cref="NetMQ.MsgType.Uninitialised"/>,
  208. /// <see cref="NetMQ.MsgType.Empty"/> or <see cref="NetMQ.MsgType.Delimiter"/>.
  209. /// </remarks>
  210. [Obsolete("Use implicit casting to Span or Slice instead")]
  211. public byte[]? Data => m_data;
  212. /// <summary>
  213. /// Return the internal buffer as Span
  214. /// </summary>
  215. /// <returns>The span</returns>
  216. public Span<byte> Slice()
  217. {
  218. if (m_data == null)
  219. return Span<byte>.Empty;
  220. return new Span<byte>(m_data, m_offset, Size);
  221. }
  222. /// <summary>
  223. /// Return the internal buffer as Memory
  224. /// </summary>
  225. /// <returns>The memory</returns>
  226. public Memory<byte> SliceAsMemory()
  227. {
  228. if (m_data == null)
  229. return Memory<byte>.Empty;
  230. return new Memory<byte>(m_data, m_offset, Size);
  231. }
  232. /// <summary>
  233. /// Returns a slice of the internal buffer.
  234. /// </summary>
  235. /// <param name="offset">The offset to take the span from</param>
  236. public Span<byte> Slice(int offset)
  237. {
  238. if (m_data == null && offset > 0)
  239. throw new ArgumentOutOfRangeException(nameof(offset));
  240. if (m_offset + offset > Size)
  241. throw new ArgumentOutOfRangeException(nameof(offset));
  242. return new Span<byte>(m_data, m_offset + offset, Size - offset);
  243. }
  244. /// <summary>
  245. /// Returns a slice of the internal buffer.
  246. /// </summary>
  247. /// <param name="offset">The offset to take the span from</param>
  248. /// <param name="count">The size of the slice</param>
  249. public Span<byte> Slice(int offset, int count)
  250. {
  251. if (m_data == null && offset > 0)
  252. throw new ArgumentOutOfRangeException(nameof(offset));
  253. if (m_offset + offset > Size)
  254. throw new ArgumentOutOfRangeException(nameof(offset));
  255. if (m_offset + offset + count > Size)
  256. throw new ArgumentOutOfRangeException(nameof(offset));
  257. return new Span<byte>(m_data, m_offset + offset, count);
  258. }
  259. /// <summary>
  260. /// Copy the content of the message into a Span
  261. /// </summary>
  262. /// <param name="span">The span to copy content to</param>
  263. public void CopyTo(Span<byte> span)
  264. {
  265. ((Span<byte>) this).CopyTo(span);
  266. }
  267. /// <summary>
  268. /// Return a copy of the internal buffer as byte array
  269. /// </summary>
  270. /// <returns>Byte array</returns>
  271. public byte[] ToArray()
  272. {
  273. var data = new byte[Size];
  274. if (Size > 0)
  275. Buffer.BlockCopy(m_data, m_offset, data, 0, Size);
  276. return data;
  277. }
  278. /// <summary>
  279. /// Implicit case of of Msg to Span
  280. /// </summary>
  281. /// <returns>Span</returns>
  282. public static implicit operator Span<byte>(Msg msg)
  283. {
  284. return new Span<byte>(msg.m_data, msg.m_offset, msg.Size);
  285. }
  286. /// <summary>
  287. /// Implicit case of of Msg to ReadOnlySpan
  288. /// </summary>
  289. /// <returns>Span</returns>
  290. public static implicit operator ReadOnlySpan<byte>(Msg msg)
  291. {
  292. return new Span<byte>(msg.m_data, msg.m_offset, msg.Size);
  293. }
  294. /// <summary>
  295. /// Returns span enumerator, to iterate of the Msg
  296. /// </summary>
  297. /// <returns>Span Enumerator</returns>
  298. public Span<byte>.Enumerator GetEnumerator()
  299. {
  300. return ((Span<byte>) this).GetEnumerator();
  301. }
  302. #region Initialisation
  303. /// <summary>
  304. /// Clear this Msg to empty - ie, set MsgFlags to None, MsgType to Empty, and clear the Data.
  305. /// </summary>
  306. public void InitEmpty()
  307. {
  308. MsgType = MsgType.Empty;
  309. Flags = MsgFlags.None;
  310. Size = 0;
  311. m_offset = 0;
  312. m_data = null;
  313. EnsureAtomicCounterNull();
  314. }
  315. /// <summary>
  316. /// Initialise this Msg to be of MsgType.Pool, with a data-buffer of the given number of bytes.
  317. /// </summary>
  318. /// <param name="size">the number of bytes to allocate in the data-buffer</param>
  319. public void InitPool(int size)
  320. {
  321. MsgType = MsgType.Pool;
  322. Flags = MsgFlags.None;
  323. m_data = BufferPool.Take(size);
  324. Size = size;
  325. m_offset = 0;
  326. EnsureAtomicCounterNull();
  327. m_refCount = AtomicCounterPool.Take();
  328. }
  329. /// <summary>
  330. /// Initialise this Msg to be of MsgType.GC with the given data-buffer value.
  331. /// </summary>
  332. /// <param name="data">the byte-array of data to assign to the Msg's Data property</param>
  333. /// <param name="size">the number of bytes that are in the data byte-array</param>
  334. public void InitGC(byte[] data, int size)
  335. {
  336. InitGC(data, 0, size);
  337. }
  338. /// <summary>
  339. /// Initialise this Msg to be of MsgType.GC with the given data-buffer value.
  340. /// </summary>
  341. /// <param name="data">the byte-array of data to assign to the Msg's Data property</param>
  342. /// <param name="offset">first byte in the data array</param>
  343. /// <param name="size">the number of bytes that are in the data byte-array</param>
  344. public void InitGC(byte[] data, int offset, int size)
  345. {
  346. MsgType = MsgType.GC;
  347. Flags = MsgFlags.None;
  348. m_data = data;
  349. Size = size;
  350. m_offset = offset;
  351. EnsureAtomicCounterNull();
  352. }
  353. /// <summary>
  354. /// Set this Msg to be of type MsgType.Delimiter with no bits set within MsgFlags.
  355. /// </summary>
  356. public void InitDelimiter()
  357. {
  358. MsgType = MsgType.Delimiter;
  359. Flags = MsgFlags.None;
  360. }
  361. internal void InitJoin()
  362. {
  363. MsgType = MsgType.Join;
  364. Flags = MsgFlags.None;
  365. }
  366. internal void InitLeave()
  367. {
  368. MsgType = MsgType.Leave;
  369. Flags = MsgFlags.None;
  370. }
  371. #endregion
  372. /// <summary>
  373. /// Clear the <see cref="Data"/> and set the MsgType to Invalid.
  374. /// If this is not a shared-data Msg (MsgFlags.Shared is not set), or it is shared but the reference-counter has dropped to zero,
  375. /// then return the data back to the BufferPool.
  376. /// </summary>
  377. /// <exception cref="FaultException">The object is not initialised.</exception>
  378. public void Close()
  379. {
  380. if (!IsInitialised)
  381. throw new FaultException("Cannot close an uninitialised Msg.");
  382. if (MsgType == MsgType.Pool)
  383. {
  384. Assumes.NotNull(m_refCount);
  385. Assumes.NotNull(m_data);
  386. // if not shared or reference counter drop to zero
  387. if (!IsShared || m_refCount.Decrement() == 0)
  388. BufferPool.Return(m_data);
  389. EnsureAtomicCounterNull();
  390. }
  391. // Uninitialise the frame
  392. m_data = null;
  393. MsgType = MsgType.Uninitialised;
  394. }
  395. /// <summary>
  396. /// If this Msg is of MsgType.Pool, then - add the given amount number to the reference-counter
  397. /// and set the shared-data Flags bit.
  398. /// If this is not a Pool Msg, this does nothing.
  399. /// </summary>
  400. /// <param name="amount">the number to add to the internal reference-counter</param>
  401. public void AddReferences(int amount)
  402. {
  403. if (amount == 0)
  404. {
  405. return;
  406. }
  407. if (MsgType == MsgType.Pool)
  408. {
  409. Assumes.NotNull(m_refCount);
  410. if (IsShared)
  411. {
  412. m_refCount.Increase(amount);
  413. }
  414. else
  415. {
  416. // Because msg is not yet shared we add 1 to the amount to represent the current copy of the msg.
  417. // We can set the amount in a none thread-safe way because the message is not yet shared and
  418. // therefore only being used by one thread
  419. m_refCount.Set(amount + 1);
  420. Flags |= MsgFlags.Shared;
  421. }
  422. }
  423. }
  424. /// <summary>
  425. /// If this Msg is of MsgType.Pool and is marked as Shared, then - subtract the given amount number from the reference-counter
  426. /// and, if that reaches zero - return the data to the shared-data pool.
  427. /// If this is not both a Pool Msg and also marked as Shared, this simply Closes this Msg.
  428. /// </summary>
  429. /// <param name="amount">the number to subtract from the internal reference-counter</param>
  430. public void RemoveReferences(int amount)
  431. {
  432. if (amount == 0)
  433. return;
  434. if (MsgType != MsgType.Pool || !IsShared)
  435. {
  436. Close();
  437. return;
  438. }
  439. Assumes.NotNull(m_refCount);
  440. if (m_refCount.Decrement(amount) == 0)
  441. {
  442. Assumes.NotNull(m_data);
  443. EnsureAtomicCounterNull();
  444. BufferPool.Return(m_data);
  445. // TODO shouldn't we set the type to uninitialised, or call clear, here? the object has a null refCount, but other methods may try to use it
  446. }
  447. }
  448. /// <summary>
  449. /// Override the Object ToString method to show the object-type, and values of the MsgType, Size, and Flags properties.
  450. /// </summary>
  451. /// <returns>a string that provides some detail about this Msg's state</returns>
  452. public override string ToString()
  453. {
  454. return base.ToString() + "[" + MsgType + "," + Size + "," + Flags + "]";
  455. }
  456. /// <summary>
  457. /// Convert the Msg to string
  458. /// </summary>
  459. /// <param name="encoding">The encoding to use for the conversion</param>
  460. /// <returns>The string</returns>
  461. public string GetString(Encoding encoding)
  462. {
  463. return encoding.GetString(m_data, m_offset, Size);
  464. }
  465. /// <summary>
  466. /// Convert the Msg to string
  467. /// </summary>
  468. /// <param name="encoding">The encoding to use for the conversion</param>
  469. /// <param name="offset">Offset to start conversion from</param>
  470. /// <param name="count">Number of bytes to convert</param>
  471. /// <returns>The string</returns>
  472. public string GetString(Encoding encoding, int offset, int count)
  473. {
  474. return encoding.GetString(m_data, m_offset + offset, count);
  475. }
  476. /// <summary>
  477. /// Copy the given byte-array data to this Msg's Data buffer.
  478. /// </summary>
  479. /// <param name="src">the source byte-array to copy from</param>
  480. /// <param name="dstOffset">index within the internal Data array to copy that byte to</param>
  481. /// <param name="len">the number of bytes to copy</param>
  482. public void Put(byte[]? src, int dstOffset, int len)
  483. {
  484. if (len == 0 || src == null)
  485. return;
  486. Buffer.BlockCopy(src, 0, m_data, dstOffset, len);
  487. }
  488. /// <summary>
  489. /// Copy the given byte-array data to this Msg's Data buffer.
  490. /// </summary>
  491. /// <param name="src">the source byte-array to copy from</param>
  492. /// <param name="srcOffset">first byte in the source byte-array</param>
  493. /// <param name="dstOffset">index within the internal Data array to copy that byte to</param>
  494. /// <param name="len">the number of bytes to copy</param>
  495. public void Put(byte[]? src, int srcOffset, int dstOffset, int len) {
  496. if (len == 0 || src == null)
  497. return;
  498. Buffer.BlockCopy(src, srcOffset, m_data, dstOffset, len);
  499. }
  500. /// <summary>
  501. /// Copy the given single byte to this Msg's Data buffer.
  502. /// </summary>
  503. /// <param name="b">the source byte to copy from</param>
  504. public void Put(byte b)
  505. {
  506. Assumes.NotNull(m_data);
  507. m_data[m_offset] = b;
  508. }
  509. /// <summary>
  510. /// Copy the given single byte to this Msg's Data buffer at the given array-index.
  511. /// </summary>
  512. /// <param name="b">the source byte to copy from</param>
  513. /// <param name="i">index within the internal Data array to copy that byte to</param>
  514. public void Put(byte b, int i)
  515. {
  516. Assumes.NotNull(m_data);
  517. m_data[m_offset + i] = b;
  518. }
  519. /// <summary>
  520. /// Write a string into msg at the specified index
  521. /// </summary>
  522. /// <param name="encoding">The encoding to use for the writing</param>
  523. /// <param name="str">The string to write</param>
  524. /// <param name="index">The index to write the string to</param>
  525. public void Put(Encoding encoding, string str, int index)
  526. {
  527. encoding.GetBytes(str, 0, str.Length, m_data, m_offset + index);
  528. }
  529. /// <summary>
  530. /// Copy the given byte-array data to this Msg's Data buffer.
  531. /// </summary>
  532. /// <param name="src">the source byte-array to copy from</param>
  533. /// <param name="offset">index within the internal Data array to copy that byte to</param>
  534. public void Put(Span<byte> src, int offset)
  535. {
  536. src.CopyTo(Slice(offset));
  537. }
  538. /// <summary>
  539. /// Get and set the byte value in the <see cref="Data"/> buffer at a specific index.
  540. /// </summary>
  541. /// <param name="index">The index to access</param>
  542. /// <returns></returns>
  543. public byte this[int index]
  544. {
  545. get
  546. {
  547. Assumes.NotNull(m_data);
  548. return m_data[m_offset + index];
  549. }
  550. set
  551. {
  552. Assumes.NotNull(m_data);
  553. m_data[m_offset + index] = value;
  554. }
  555. }
  556. /// <summary>
  557. /// Close this Msg, and effectively make this Msg a copy of the given source Msg
  558. /// by simply setting it to point to the given source Msg.
  559. /// If this is a Pool Msg, then this also increases the reference-counter and sets the Shared bit.
  560. /// </summary>
  561. /// <param name="src">the source Msg to copy from</param>
  562. /// <exception cref="FaultException">The object is not initialised.</exception>
  563. public void Copy(ref Msg src)
  564. {
  565. // Check the validity of the source.
  566. if (!src.IsInitialised)
  567. throw new FaultException("Cannot copy an uninitialised Msg.");
  568. if (IsInitialised)
  569. Close();
  570. if (src.MsgType == MsgType.Pool)
  571. {
  572. Assumes.NotNull(src.m_refCount);
  573. // One reference is added to shared messages. Non-shared messages
  574. // are turned into shared messages and reference count is set to 2.
  575. if (IsShared)
  576. {
  577. src.m_refCount.Increase(1);
  578. }
  579. else
  580. {
  581. src.Flags |= MsgFlags.Shared;
  582. src.m_refCount.Set(2);
  583. }
  584. }
  585. // Populate this instance via a memberwise-copy from the 'src' instance.
  586. this = src;
  587. }
  588. /// <summary>
  589. /// Increase Offset and decrease Size by the given count.
  590. /// </summary>
  591. /// <param name="count">Number of bytes to remove from a message</param>
  592. public void TrimPrefix(int count)
  593. {
  594. if (count > Size || count < 0)
  595. throw new ArgumentOutOfRangeException(nameof(count), "Count should be between 0 and size");
  596. m_offset = m_offset + count;
  597. Size = Size - count;
  598. }
  599. /// <summary>
  600. /// Close this Msg and make it reference the given source Msg, and then clear the Msg to empty.
  601. /// </summary>
  602. /// <param name="src">the source-Msg to become</param>
  603. /// <exception cref="FaultException">The object is not initialised.</exception>
  604. public void Move(ref Msg src)
  605. {
  606. // Check the validity of the source.
  607. if (!src.IsInitialised)
  608. throw new FaultException("Cannot move an uninitialised Msg.");
  609. if (IsInitialised)
  610. Close();
  611. this = src;
  612. src.InitEmpty();
  613. }
  614. /// <summary>
  615. /// Returns a new array containing the first <see cref="Size"/> bytes of <see cref="Data"/>.
  616. /// Deprecated: use <see cref="ToArray()"/>
  617. /// </summary>
  618. public byte[] CloneData()
  619. {
  620. var data = new byte[Size];
  621. if (Size > 0)
  622. Buffer.BlockCopy(m_data, m_offset, data, 0, Size);
  623. return data;
  624. }
  625. private void EnsureAtomicCounterNull()
  626. {
  627. if(m_refCount != null)
  628. {
  629. AtomicCounterPool.Return(m_refCount);
  630. m_refCount = null;
  631. }
  632. }
  633. #region Internal unsafe methods
  634. internal byte[] UnsafeToArray()
  635. {
  636. Assumes.NotNull(m_data);
  637. if (m_offset == 0 && m_data.Length == Size)
  638. return m_data;
  639. return CloneData();
  640. }
  641. internal byte[]? UnsafeData => m_data;
  642. internal int UnsafeOffset => m_offset;
  643. #endregion
  644. }
  645. }