using System;
using System.Buffers;
namespace ZeroLevel.Services.Network
{
///
/// Управляет адаптивным буфером для сетевых операций
///
public class AdaptiveBufferManager : IDisposable
{
private byte[] _buffer;
private int _currentSize;
private readonly int _minSize;
private readonly int _maxSize;
// Счетчики для адаптивного изменения размера
private int _consecutiveSmallReads = 0;
private int _consecutiveLargeReads = 0;
// Настройки адаптации
private readonly double _increaseThreshold;
private readonly double _decreaseThreshold;
private readonly int _increaseAfterReads;
private readonly int _decreaseAfterReads;
public byte[] Buffer => _buffer;
public int CurrentSize => _currentSize;
public AdaptiveBufferManager(
int minSize = 4096,
int maxSize = 65536,
double increaseThreshold = 0.9,
double decreaseThreshold = 0.25,
int increaseAfterReads = 3,
int decreaseAfterReads = 10)
{
_minSize = minSize;
_maxSize = maxSize;
_currentSize = minSize;
_increaseThreshold = increaseThreshold;
_decreaseThreshold = decreaseThreshold;
_increaseAfterReads = increaseAfterReads;
_decreaseAfterReads = decreaseAfterReads;
AllocateBuffer(_currentSize);
}
///
/// Обрабатывает результат чтения и адаптирует размер буфера
///
public void ProcessReadResult(int bytesRead)
{
AdjustBufferSize(bytesRead);
}
///
/// Принудительно изменить размер буфера
///
public void ResizeBuffer(int newSize)
{
if (newSize < _minSize || newSize > _maxSize)
{
throw new ArgumentOutOfRangeException(nameof(newSize),
$"Size must be between {_minSize} and {_maxSize}");
}
AllocateBuffer(newSize);
}
private void AllocateBuffer(int size)
{
// Возвращаем старый буфер если есть
if (_buffer != null)
{
ArrayPool.Shared.Return(_buffer, clearArray: false);
}
// Арендуем новый буфер нужного размера
_buffer = ArrayPool.Shared.Rent(size);
_currentSize = _buffer.Length; // ArrayPool может вернуть буфер большего размера
}
private void AdjustBufferSize(int bytesRead)
{
// Если прочитали почти весь буфер, возможно нужен больший размер
if (bytesRead >= _currentSize * _increaseThreshold)
{
_consecutiveLargeReads++;
_consecutiveSmallReads = 0;
// После N больших чтений подряд увеличиваем буфер
if (_consecutiveLargeReads >= _increaseAfterReads && _currentSize < _maxSize)
{
var newSize = Math.Min(_currentSize * 2, _maxSize);
Log.Debug($"[AdaptiveBuffer] Increasing size from {_currentSize} to {newSize}");
AllocateBuffer(newSize);
_consecutiveLargeReads = 0;
}
}
// Если прочитали мало данных, возможно можно уменьшить буфер
else if (bytesRead < _currentSize * _decreaseThreshold)
{
_consecutiveSmallReads++;
_consecutiveLargeReads = 0;
// После N маленьких чтений подряд уменьшаем буфер
if (_consecutiveSmallReads >= _decreaseAfterReads && _currentSize > _minSize)
{
var newSize = Math.Max(_currentSize / 2, _minSize);
Log.Debug($"[AdaptiveBuffer] Decreasing size from {_currentSize} to {newSize}");
AllocateBuffer(newSize);
_consecutiveSmallReads = 0;
}
}
else
{
// Сброс счетчиков при среднем использовании
_consecutiveSmallReads = 0;
_consecutiveLargeReads = 0;
}
}
public void Dispose()
{
if (_buffer != null)
{
ArrayPool.Shared.Return(_buffer, clearArray: false);
_buffer = null!;
}
}
}
}