CyclicBuffer.cs 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355
  1. #region Apache License
  2. //
  3. // Licensed to the Apache Software Foundation (ASF) under one or more
  4. // contributor license agreements. See the NOTICE file distributed with
  5. // this work for additional information regarding copyright ownership.
  6. // The ASF licenses this file to you under the Apache License, Version 2.0
  7. // (the "License"); you may not use this file except in compliance with
  8. // the License. You may obtain a copy of the License at
  9. //
  10. // http://www.apache.org/licenses/LICENSE-2.0
  11. //
  12. // Unless required by applicable law or agreed to in writing, software
  13. // distributed under the License is distributed on an "AS IS" BASIS,
  14. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. // See the License for the specific language governing permissions and
  16. // limitations under the License.
  17. //
  18. #endregion
  19. using System;
  20. using log4net.Core;
  21. namespace log4net.Util
  22. {
  23. /// <summary>
  24. /// A fixed size rolling buffer of logging events.
  25. /// </summary>
  26. /// <remarks>
  27. /// <para>
  28. /// An array backed fixed size leaky bucket.
  29. /// </para>
  30. /// </remarks>
  31. /// <author>Nicko Cadell</author>
  32. /// <author>Gert Driesen</author>
  33. public class CyclicBuffer
  34. {
  35. #region Public Instance Constructors
  36. /// <summary>
  37. /// Constructor
  38. /// </summary>
  39. /// <param name="maxSize">The maximum number of logging events in the buffer.</param>
  40. /// <remarks>
  41. /// <para>
  42. /// Initializes a new instance of the <see cref="CyclicBuffer" /> class with
  43. /// the specified maximum number of buffered logging events.
  44. /// </para>
  45. /// </remarks>
  46. /// <exception cref="ArgumentOutOfRangeException">The <paramref name="maxSize"/> argument is not a positive integer.</exception>
  47. public CyclicBuffer(int maxSize)
  48. {
  49. if (maxSize < 1)
  50. {
  51. throw SystemInfo.CreateArgumentOutOfRangeException("maxSize", (object)maxSize, "Parameter: maxSize, Value: [" + maxSize + "] out of range. Non zero positive integer required");
  52. }
  53. m_maxSize = maxSize;
  54. m_events = new LoggingEvent[maxSize];
  55. m_first = 0;
  56. m_last = 0;
  57. m_numElems = 0;
  58. }
  59. #endregion Public Instance Constructors
  60. #region Public Instance Methods
  61. /// <summary>
  62. /// Appends a <paramref name="loggingEvent"/> to the buffer.
  63. /// </summary>
  64. /// <param name="loggingEvent">The event to append to the buffer.</param>
  65. /// <returns>The event discarded from the buffer, if the buffer is full, otherwise <c>null</c>.</returns>
  66. /// <remarks>
  67. /// <para>
  68. /// Append an event to the buffer. If the buffer still contains free space then
  69. /// <c>null</c> is returned. If the buffer is full then an event will be dropped
  70. /// to make space for the new event, the event dropped is returned.
  71. /// </para>
  72. /// </remarks>
  73. public LoggingEvent Append(LoggingEvent loggingEvent)
  74. {
  75. if (loggingEvent == null)
  76. {
  77. throw new ArgumentNullException("loggingEvent");
  78. }
  79. lock(this)
  80. {
  81. // save the discarded event
  82. LoggingEvent discardedLoggingEvent = m_events[m_last];
  83. // overwrite the last event position
  84. m_events[m_last] = loggingEvent;
  85. if (++m_last == m_maxSize)
  86. {
  87. m_last = 0;
  88. }
  89. if (m_numElems < m_maxSize)
  90. {
  91. m_numElems++;
  92. }
  93. else if (++m_first == m_maxSize)
  94. {
  95. m_first = 0;
  96. }
  97. if (m_numElems < m_maxSize)
  98. {
  99. // Space remaining
  100. return null;
  101. }
  102. else
  103. {
  104. // Buffer is full and discarding an event
  105. return discardedLoggingEvent;
  106. }
  107. }
  108. }
  109. /// <summary>
  110. /// Get and remove the oldest event in the buffer.
  111. /// </summary>
  112. /// <returns>The oldest logging event in the buffer</returns>
  113. /// <remarks>
  114. /// <para>
  115. /// Gets the oldest (first) logging event in the buffer and removes it
  116. /// from the buffer.
  117. /// </para>
  118. /// </remarks>
  119. public LoggingEvent PopOldest()
  120. {
  121. lock(this)
  122. {
  123. LoggingEvent ret = null;
  124. if (m_numElems > 0)
  125. {
  126. m_numElems--;
  127. ret = m_events[m_first];
  128. m_events[m_first] = null;
  129. if (++m_first == m_maxSize)
  130. {
  131. m_first = 0;
  132. }
  133. }
  134. return ret;
  135. }
  136. }
  137. /// <summary>
  138. /// Pops all the logging events from the buffer into an array.
  139. /// </summary>
  140. /// <returns>An array of all the logging events in the buffer.</returns>
  141. /// <remarks>
  142. /// <para>
  143. /// Get all the events in the buffer and clear the buffer.
  144. /// </para>
  145. /// </remarks>
  146. public LoggingEvent[] PopAll()
  147. {
  148. lock(this)
  149. {
  150. LoggingEvent[] ret = new LoggingEvent[m_numElems];
  151. if (m_numElems > 0)
  152. {
  153. if (m_first < m_last)
  154. {
  155. Array.Copy(m_events, m_first, ret, 0, m_numElems);
  156. }
  157. else
  158. {
  159. Array.Copy(m_events, m_first, ret, 0, m_maxSize - m_first);
  160. Array.Copy(m_events, 0, ret, m_maxSize - m_first, m_last);
  161. }
  162. }
  163. Clear();
  164. return ret;
  165. }
  166. }
  167. /// <summary>
  168. /// Clear the buffer
  169. /// </summary>
  170. /// <remarks>
  171. /// <para>
  172. /// Clear the buffer of all events. The events in the buffer are lost.
  173. /// </para>
  174. /// </remarks>
  175. public void Clear()
  176. {
  177. lock(this)
  178. {
  179. // Set all the elements to null
  180. Array.Clear(m_events, 0, m_events.Length);
  181. m_first = 0;
  182. m_last = 0;
  183. m_numElems = 0;
  184. }
  185. }
  186. #if RESIZABLE_CYCLIC_BUFFER
  187. /// <summary>
  188. /// Resizes the cyclic buffer to <paramref name="newSize"/>.
  189. /// </summary>
  190. /// <param name="newSize">The new size of the buffer.</param>
  191. /// <remarks>
  192. /// <para>
  193. /// Resize the cyclic buffer. Events in the buffer are copied into
  194. /// the newly sized buffer. If the buffer is shrunk and there are
  195. /// more events currently in the buffer than the new size of the
  196. /// buffer then the newest events will be dropped from the buffer.
  197. /// </para>
  198. /// </remarks>
  199. /// <exception cref="ArgumentOutOfRangeException">The <paramref name="newSize"/> argument is not a positive integer.</exception>
  200. public void Resize(int newSize)
  201. {
  202. lock(this)
  203. {
  204. if (newSize < 0)
  205. {
  206. throw log4net.Util.SystemInfo.CreateArgumentOutOfRangeException("newSize", (object)newSize, "Parameter: newSize, Value: [" + newSize + "] out of range. Non zero positive integer required");
  207. }
  208. if (newSize == m_numElems)
  209. {
  210. return; // nothing to do
  211. }
  212. LoggingEvent[] temp = new LoggingEvent[newSize];
  213. int loopLen = (newSize < m_numElems) ? newSize : m_numElems;
  214. for(int i = 0; i < loopLen; i++)
  215. {
  216. temp[i] = m_events[m_first];
  217. m_events[m_first] = null;
  218. if (++m_first == m_numElems)
  219. {
  220. m_first = 0;
  221. }
  222. }
  223. m_events = temp;
  224. m_first = 0;
  225. m_numElems = loopLen;
  226. m_maxSize = newSize;
  227. if (loopLen == newSize)
  228. {
  229. m_last = 0;
  230. }
  231. else
  232. {
  233. m_last = loopLen;
  234. }
  235. }
  236. }
  237. #endif
  238. #endregion Public Instance Methods
  239. #region Public Instance Properties
  240. /// <summary>
  241. /// Gets the <paramref name="i"/>th oldest event currently in the buffer.
  242. /// </summary>
  243. /// <value>The <paramref name="i"/>th oldest event currently in the buffer.</value>
  244. /// <remarks>
  245. /// <para>
  246. /// If <paramref name="i"/> is outside the range 0 to the number of events
  247. /// currently in the buffer, then <c>null</c> is returned.
  248. /// </para>
  249. /// </remarks>
  250. public LoggingEvent this[int i]
  251. {
  252. get
  253. {
  254. lock(this)
  255. {
  256. if (i < 0 || i >= m_numElems)
  257. {
  258. return null;
  259. }
  260. return m_events[(m_first + i) % m_maxSize];
  261. }
  262. }
  263. }
  264. /// <summary>
  265. /// Gets the maximum size of the buffer.
  266. /// </summary>
  267. /// <value>The maximum size of the buffer.</value>
  268. /// <remarks>
  269. /// <para>
  270. /// Gets the maximum size of the buffer
  271. /// </para>
  272. /// </remarks>
  273. public int MaxSize
  274. {
  275. get
  276. {
  277. lock(this)
  278. {
  279. return m_maxSize;
  280. }
  281. }
  282. #if RESIZABLE_CYCLIC_BUFFER
  283. set
  284. {
  285. /// Setting the MaxSize will cause the buffer to resize.
  286. Resize(value);
  287. }
  288. #endif
  289. }
  290. /// <summary>
  291. /// Gets the number of logging events in the buffer.
  292. /// </summary>
  293. /// <value>The number of logging events in the buffer.</value>
  294. /// <remarks>
  295. /// <para>
  296. /// This number is guaranteed to be in the range 0 to <see cref="MaxSize"/>
  297. /// (inclusive).
  298. /// </para>
  299. /// </remarks>
  300. public int Length
  301. {
  302. get
  303. {
  304. lock(this)
  305. {
  306. return m_numElems;
  307. }
  308. }
  309. }
  310. #endregion Public Instance Properties
  311. #region Private Instance Fields
  312. private LoggingEvent[] m_events;
  313. private int m_first;
  314. private int m_last;
  315. private int m_numElems;
  316. private int m_maxSize;
  317. #endregion Private Instance Fields
  318. }
  319. }