-
Notifications
You must be signed in to change notification settings - Fork 525
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[feat]新增SpanWriter/SpanReader,用于Span结构的读写,逐步替代Binary二进制序列化,实现零GC
- Loading branch information
Showing
5 changed files
with
540 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
using System.Buffers; | ||
using System.Runtime.InteropServices; | ||
using System.Text; | ||
|
||
namespace NewLife; | ||
|
||
/// <summary>Span帮助类</summary> | ||
public static class SpanHelper | ||
{ | ||
/// <summary>获取字符串的字节数组</summary> | ||
public static unsafe Int32 GetBytes(this Encoding encoding, ReadOnlySpan<Char> chars, Span<Byte> bytes) | ||
{ | ||
fixed (Char* chars2 = &MemoryMarshal.GetReference(chars)) | ||
{ | ||
fixed (Byte* bytes2 = &MemoryMarshal.GetReference(bytes)) | ||
{ | ||
return encoding.GetBytes(chars2, chars.Length, bytes2, bytes.Length); | ||
} | ||
} | ||
} | ||
|
||
/// <summary>获取字节数组的字符串</summary> | ||
public static unsafe String GetString(this Encoding encoding, ReadOnlySpan<Byte> bytes) | ||
{ | ||
if (bytes.IsEmpty) return String.Empty; | ||
|
||
#if NET45 | ||
return encoding.GetString(bytes.ToArray()); | ||
#else | ||
fixed (Byte* bytes2 = &MemoryMarshal.GetReference(bytes)) | ||
{ | ||
return encoding.GetString(bytes2, bytes.Length); | ||
} | ||
#endif | ||
} | ||
|
||
/// <summary>写入Memory到数据流</summary> | ||
/// <param name="stream"></param> | ||
/// <param name="buffer"></param> | ||
/// <param name="cancellationToken"></param> | ||
/// <returns></returns> | ||
public static Task WriteAsync(this Stream stream, ReadOnlyMemory<Byte> buffer, CancellationToken cancellationToken = default) | ||
{ | ||
if (MemoryMarshal.TryGetArray(buffer, out var segment)) | ||
return stream.WriteAsync(segment.Array!, segment.Offset, segment.Count, cancellationToken); | ||
|
||
var array = ArrayPool<Byte>.Shared.Rent(buffer.Length); | ||
buffer.Span.CopyTo(array); | ||
|
||
var writeTask = stream.WriteAsync(array, 0, buffer.Length, cancellationToken); | ||
return Task.Run(async () => | ||
{ | ||
try | ||
{ | ||
await writeTask.ConfigureAwait(false); | ||
} | ||
finally | ||
{ | ||
ArrayPool<Byte>.Shared.Return(array); | ||
} | ||
}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,214 @@ | ||
using System.Buffers.Binary; | ||
using System.Runtime.CompilerServices; | ||
using System.Runtime.InteropServices; | ||
using System.Text; | ||
|
||
namespace NewLife.Serialization; | ||
|
||
/// <summary>Span读取器</summary> | ||
/// <param name="span"></param> | ||
public ref struct SpanReader(Span<Byte> span) | ||
{ | ||
#region 属性 | ||
private readonly Span<Byte> _span = span; | ||
|
||
private Int32 _index; | ||
/// <summary>已写入字节数</summary> | ||
public Int32 WrittenCount => _index; | ||
|
||
/// <summary>总容量</summary> | ||
public Int32 Capacity => _span.Length; | ||
|
||
/// <summary>空闲容量</summary> | ||
public Int32 FreeCapacity => _span.Length - _index; | ||
|
||
/// <summary>是否小端字节序。默认true</summary> | ||
public Boolean IsLittleEndian { get; set; } = true; | ||
#endregion | ||
|
||
#region 基础方法 | ||
/// <summary>告知有多少数据已写入缓冲区</summary> | ||
/// <param name="count"></param> | ||
public void Advance(Int32 count) | ||
{ | ||
if (count < 0) throw new ArgumentOutOfRangeException(nameof(count)); | ||
if (_index > _span.Length - count) throw new ArgumentOutOfRangeException(nameof(count)); | ||
|
||
_index += count; | ||
} | ||
|
||
/// <summary>返回要写入到的Span,其大小按 sizeHint 参数指定至少为所请求的大小</summary> | ||
/// <param name="sizeHint"></param> | ||
/// <returns></returns> | ||
/// <exception cref="ArgumentOutOfRangeException"></exception> | ||
public Span<Byte> GetSpan(Int32 sizeHint = 0) | ||
{ | ||
if (sizeHint > FreeCapacity) throw new ArgumentOutOfRangeException(nameof(sizeHint)); | ||
|
||
return _span[.._index]; | ||
} | ||
#endregion | ||
|
||
#region 读取方法 | ||
/// <summary>确保缓冲区中有足够的空间。</summary> | ||
/// <param name="size">需要的字节数。</param> | ||
private void EnsureSpace(Int32 size) | ||
{ | ||
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>读取Int16整数</summary> | ||
/// <returns></returns> | ||
public Int16 ReadInt16() | ||
{ | ||
var size = sizeof(Int16); | ||
EnsureSpace(size); | ||
var result = IsLittleEndian ? | ||
BinaryPrimitives.ReadInt16LittleEndian(_span.Slice(_index, size)) : | ||
BinaryPrimitives.ReadInt16BigEndian(_span.Slice(_index, size)); | ||
_index += size; | ||
return result; | ||
} | ||
|
||
/// <summary>读取UInt16整数</summary> | ||
/// <returns></returns> | ||
public UInt16 ReadUInt16() | ||
{ | ||
var size = sizeof(UInt16); | ||
EnsureSpace(size); | ||
var result = IsLittleEndian ? | ||
BinaryPrimitives.ReadUInt16LittleEndian(_span.Slice(_index, size)) : | ||
BinaryPrimitives.ReadUInt16BigEndian(_span.Slice(_index, size)); | ||
_index += size; | ||
return result; | ||
} | ||
|
||
/// <summary>读取Int32整数</summary> | ||
/// <returns></returns> | ||
public Int32 ReadInt32() | ||
{ | ||
var size = sizeof(Int32); | ||
EnsureSpace(size); | ||
var result = IsLittleEndian ? | ||
BinaryPrimitives.ReadInt32LittleEndian(_span.Slice(_index, size)) : | ||
BinaryPrimitives.ReadInt32BigEndian(_span.Slice(_index, size)); | ||
_index += size; | ||
return result; | ||
} | ||
|
||
/// <summary>读取UInt32整数</summary> | ||
/// <returns></returns> | ||
public UInt32 ReadUInt32() | ||
{ | ||
var size = sizeof(UInt32); | ||
EnsureSpace(size); | ||
var result = IsLittleEndian ? | ||
BinaryPrimitives.ReadUInt32LittleEndian(_span.Slice(_index, size)) : | ||
BinaryPrimitives.ReadUInt32BigEndian(_span.Slice(_index, size)); | ||
_index += size; | ||
return result; | ||
} | ||
|
||
/// <summary>读取Int64整数</summary> | ||
/// <returns></returns> | ||
public Int64 ReadInt64() | ||
{ | ||
var size = sizeof(Int64); | ||
EnsureSpace(size); | ||
var result = IsLittleEndian ? | ||
BinaryPrimitives.ReadInt64LittleEndian(_span.Slice(_index, size)) : | ||
BinaryPrimitives.ReadInt64BigEndian(_span.Slice(_index, size)); | ||
_index += size; | ||
return result; | ||
} | ||
|
||
/// <summary>读取UInt64整数</summary> | ||
/// <returns></returns> | ||
public UInt64 ReadUInt64() | ||
{ | ||
var size = sizeof(UInt64); | ||
EnsureSpace(size); | ||
var result = IsLittleEndian ? | ||
BinaryPrimitives.ReadUInt64LittleEndian(_span.Slice(_index, size)) : | ||
BinaryPrimitives.ReadUInt64BigEndian(_span.Slice(_index, size)); | ||
_index += size; | ||
return result; | ||
} | ||
|
||
/// <summary>读取单精度浮点数</summary> | ||
/// <returns></returns> | ||
public unsafe Single ReadSingle() | ||
{ | ||
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP | ||
return BitConverter.Int32BitsToSingle(ReadInt32()); | ||
#else | ||
var result = ReadInt32(); | ||
return Unsafe.ReadUnaligned<Single>(ref Unsafe.As<Int32, Byte>(ref result)); | ||
#endif | ||
} | ||
|
||
/// <summary>读取双精度浮点数</summary> | ||
/// <returns></returns> | ||
public Double ReadDouble() | ||
{ | ||
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP | ||
return BitConverter.Int64BitsToDouble(ReadInt64()); | ||
#else | ||
var result = ReadInt64(); | ||
return Unsafe.ReadUnaligned<Double>(ref Unsafe.As<Int64, Byte>(ref result)); | ||
#endif | ||
} | ||
|
||
/// <summary>读取字符串</summary> | ||
/// <param name="length"></param> | ||
/// <param name="encoding"></param> | ||
/// <returns></returns> | ||
/// <exception cref="InvalidOperationException"></exception> | ||
public String ReadString(Int32 length, Encoding encoding) | ||
{ | ||
EnsureSpace(length); | ||
|
||
var result = encoding.GetString(_span.Slice(_index, length)); | ||
_index += length; | ||
return result; | ||
} | ||
|
||
/// <summary>读取字节数组</summary> | ||
/// <param name="length"></param> | ||
/// <returns></returns> | ||
/// <exception cref="InvalidOperationException"></exception> | ||
public Span<Byte> ReadBytes(Int32 length) | ||
{ | ||
EnsureSpace(length); | ||
|
||
var result = _span.Slice(_index, length); | ||
_index += length; | ||
return result; | ||
} | ||
|
||
/// <summary>读取结构体</summary> | ||
/// <typeparam name="T"></typeparam> | ||
/// <returns></returns> | ||
public T Read<T>() where T : struct | ||
{ | ||
var size = Unsafe.SizeOf<T>(); | ||
EnsureSpace(size); | ||
|
||
var result = MemoryMarshal.Read<T>(_span.Slice(_index)); | ||
_index += size; | ||
return result; | ||
} | ||
#endregion | ||
} |
Oops, something went wrong.