Redis/NewLife.Redis/Buffers/BufferedReader.cs

147 lines
4.4 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using NewLife.Data;
namespace NewLife.Caching.Buffers;
/// <summary>缓存的读取器</summary>
public ref struct BufferedReader
{
#region
private ReadOnlySpan<Byte> _span;
/// <summary>数据片段</summary>
public ReadOnlySpan<Byte> Span => _span;
private Int32 _index;
/// <summary>已读取字节数</summary>
public Int32 Position { get => _index; set => _index = value; }
/// <summary>总容量</summary>
public Int32 Capacity => _span.Length;
/// <summary>空闲容量</summary>
public Int32 FreeCapacity => _span.Length - _index;
private readonly Stream _stream;
private readonly Int32 _bufferSize;
private IPacket _data;
#endregion
#region
/// <summary>实例化Span读取器</summary>
/// <param name="stream"></param>
/// <param name="data"></param>
/// <param name="bufferSize"></param>
public BufferedReader(Stream stream, IPacket data, Int32 bufferSize = 8192)
{
_stream = stream;
_bufferSize = bufferSize;
_data = data;
_span = data.GetSpan();
}
#endregion
#region
/// <summary>告知有多少数据已从缓冲区读取</summary>
/// <param name="count"></param>
public void Advance(Int32 count)
{
if (count < 0) throw new ArgumentOutOfRangeException(nameof(count));
if (_index + count > _span.Length) throw new ArgumentOutOfRangeException(nameof(count));
_index += count;
}
/// <summary>返回要写入到的Span其大小按 sizeHint 参数指定至少为所请求的大小</summary>
/// <param name="sizeHint"></param>
/// <returns></returns>
/// <exception cref="ArgumentOutOfRangeException"></exception>
public ReadOnlySpan<Byte> GetSpan(Int32 sizeHint = 0)
{
if (sizeHint > FreeCapacity) throw new ArgumentOutOfRangeException(nameof(sizeHint));
return _span[_index..];
}
#endregion
#region
/// <summary>确保缓冲区中有足够的空间。</summary>
/// <param name="size">需要的字节数。</param>
/// <exception cref="InvalidOperationException"></exception>
public void EnsureSpace(Int32 size)
{
// 检查剩余空间大小不足时再从数据流中读取。此时需要注意创建新的OwnerPacket后需要先把之前剩余的一点数据拷贝过去然后再读取Stream
var remain = FreeCapacity;
if (remain < size)
{
var idx = 0;
var pk = new OwnerPacket(_bufferSize);
if (_data != null && remain > 0)
{
if (!_data.TryGetArray(out var arr)) throw new NotSupportedException();
arr.AsSpan(_index, remain).CopyTo(pk.Buffer);
idx += remain;
}
_data.TryDispose();
_data = pk;
_index = 0;
// 多次读取,直到满足需求
//var n = _stream.ReadExactly(pk.Buffer, pk.Offset + idx, pk.Length - idx);
while (idx < size)
{
var n = _stream.Read(pk.Buffer, pk.Offset + idx, pk.Length - idx);
if (n < 0) break;
idx += n;
}
if (idx < size)
throw new InvalidOperationException("Not enough data to read.");
pk.Resize(idx);
_span = pk.GetSpan();
}
if (_index + size > _span.Length)
throw new InvalidOperationException("Not enough data to read.");
}
/// <summary>读取单个字节</summary>
/// <returns></returns>
public Byte ReadByte()
{
var size = sizeof(Byte);
EnsureSpace(size);
var result = _span[_index];
_index += size;
return result;
}
/// <summary>读取字节数组</summary>
/// <param name="length"></param>
/// <returns></returns>
/// <exception cref="InvalidOperationException"></exception>
public ReadOnlySpan<Byte> ReadBytes(Int32 length)
{
EnsureSpace(length);
var result = _span.Slice(_index, length);
_index += length;
return result;
}
/// <summary>读取数据包</summary>
/// <param name="length"></param>
/// <returns></returns>
public IPacket ReadPacket(Int32 length)
{
EnsureSpace(length);
var result = _data.Slice(_index, length);
_index += length;
return result;
}
#endregion
}