#region Apache License // // Licensed to the Apache Software Foundation (ASF) under one or more // contributor license agreements. See the NOTICE file distributed with // this work for additional information regarding copyright ownership. // The ASF licenses this file to you under the Apache License, Version 2.0 // (the "License"); you may not use this file except in compliance with // the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // #endregion using System; using log4net.Core; namespace log4net.Util { /// /// A fixed size rolling buffer of logging events. /// /// /// /// An array backed fixed size leaky bucket. /// /// /// Nicko Cadell /// Gert Driesen public class CyclicBuffer { #region Public Instance Constructors /// /// Constructor /// /// The maximum number of logging events in the buffer. /// /// /// Initializes a new instance of the class with /// the specified maximum number of buffered logging events. /// /// /// The argument is not a positive integer. public CyclicBuffer(int maxSize) { if (maxSize < 1) { throw SystemInfo.CreateArgumentOutOfRangeException("maxSize", (object)maxSize, "Parameter: maxSize, Value: [" + maxSize + "] out of range. Non zero positive integer required"); } m_maxSize = maxSize; m_events = new LoggingEvent[maxSize]; m_first = 0; m_last = 0; m_numElems = 0; } #endregion Public Instance Constructors #region Public Instance Methods /// /// Appends a to the buffer. /// /// The event to append to the buffer. /// The event discarded from the buffer, if the buffer is full, otherwise null. /// /// /// Append an event to the buffer. If the buffer still contains free space then /// null is returned. If the buffer is full then an event will be dropped /// to make space for the new event, the event dropped is returned. /// /// public LoggingEvent Append(LoggingEvent loggingEvent) { if (loggingEvent == null) { throw new ArgumentNullException("loggingEvent"); } lock(this) { // save the discarded event LoggingEvent discardedLoggingEvent = m_events[m_last]; // overwrite the last event position m_events[m_last] = loggingEvent; if (++m_last == m_maxSize) { m_last = 0; } if (m_numElems < m_maxSize) { m_numElems++; } else if (++m_first == m_maxSize) { m_first = 0; } if (m_numElems < m_maxSize) { // Space remaining return null; } else { // Buffer is full and discarding an event return discardedLoggingEvent; } } } /// /// Get and remove the oldest event in the buffer. /// /// The oldest logging event in the buffer /// /// /// Gets the oldest (first) logging event in the buffer and removes it /// from the buffer. /// /// public LoggingEvent PopOldest() { lock(this) { LoggingEvent ret = null; if (m_numElems > 0) { m_numElems--; ret = m_events[m_first]; m_events[m_first] = null; if (++m_first == m_maxSize) { m_first = 0; } } return ret; } } /// /// Pops all the logging events from the buffer into an array. /// /// An array of all the logging events in the buffer. /// /// /// Get all the events in the buffer and clear the buffer. /// /// public LoggingEvent[] PopAll() { lock(this) { LoggingEvent[] ret = new LoggingEvent[m_numElems]; if (m_numElems > 0) { if (m_first < m_last) { Array.Copy(m_events, m_first, ret, 0, m_numElems); } else { Array.Copy(m_events, m_first, ret, 0, m_maxSize - m_first); Array.Copy(m_events, 0, ret, m_maxSize - m_first, m_last); } } Clear(); return ret; } } /// /// Clear the buffer /// /// /// /// Clear the buffer of all events. The events in the buffer are lost. /// /// public void Clear() { lock(this) { // Set all the elements to null Array.Clear(m_events, 0, m_events.Length); m_first = 0; m_last = 0; m_numElems = 0; } } #if RESIZABLE_CYCLIC_BUFFER /// /// Resizes the cyclic buffer to . /// /// The new size of the buffer. /// /// /// Resize the cyclic buffer. Events in the buffer are copied into /// the newly sized buffer. If the buffer is shrunk and there are /// more events currently in the buffer than the new size of the /// buffer then the newest events will be dropped from the buffer. /// /// /// The argument is not a positive integer. public void Resize(int newSize) { lock(this) { if (newSize < 0) { throw log4net.Util.SystemInfo.CreateArgumentOutOfRangeException("newSize", (object)newSize, "Parameter: newSize, Value: [" + newSize + "] out of range. Non zero positive integer required"); } if (newSize == m_numElems) { return; // nothing to do } LoggingEvent[] temp = new LoggingEvent[newSize]; int loopLen = (newSize < m_numElems) ? newSize : m_numElems; for(int i = 0; i < loopLen; i++) { temp[i] = m_events[m_first]; m_events[m_first] = null; if (++m_first == m_numElems) { m_first = 0; } } m_events = temp; m_first = 0; m_numElems = loopLen; m_maxSize = newSize; if (loopLen == newSize) { m_last = 0; } else { m_last = loopLen; } } } #endif #endregion Public Instance Methods #region Public Instance Properties /// /// Gets the th oldest event currently in the buffer. /// /// The th oldest event currently in the buffer. /// /// /// If is outside the range 0 to the number of events /// currently in the buffer, then null is returned. /// /// public LoggingEvent this[int i] { get { lock(this) { if (i < 0 || i >= m_numElems) { return null; } return m_events[(m_first + i) % m_maxSize]; } } } /// /// Gets the maximum size of the buffer. /// /// The maximum size of the buffer. /// /// /// Gets the maximum size of the buffer /// /// public int MaxSize { get { lock(this) { return m_maxSize; } } #if RESIZABLE_CYCLIC_BUFFER set { /// Setting the MaxSize will cause the buffer to resize. Resize(value); } #endif } /// /// Gets the number of logging events in the buffer. /// /// The number of logging events in the buffer. /// /// /// This number is guaranteed to be in the range 0 to /// (inclusive). /// /// public int Length { get { lock(this) { return m_numElems; } } } #endregion Public Instance Properties #region Private Instance Fields private LoggingEvent[] m_events; private int m_first; private int m_last; private int m_numElems; private int m_maxSize; #endregion Private Instance Fields } }