#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
}
}