diff --git a/NewLife.Core/Data/Packet.cs b/NewLife.Core/Data/Packet.cs index 504466522..2183ecd9d 100644 --- a/NewLife.Core/Data/Packet.cs +++ b/NewLife.Core/Data/Packet.cs @@ -1,4 +1,5 @@ using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; using System.Text; using NewLife.Collections; @@ -81,6 +82,24 @@ public Packet(Stream stream) // 必须确保数据流位置不变 if (count > 0) stream.Seek(-count, SeekOrigin.Current); } + + /// 从Span实例化 + /// + public Packet(Span span) => Set(span.ToArray()); + + /// 从Memory实例化 + /// + public Packet(Memory memory) + { + if (MemoryMarshal.TryGetArray(memory, out var segment)) + { + Set(segment.Array!, segment.Offset, segment.Count); + } + else + { + Set(memory.ToArray()); + } + } #endregion #region 索引 @@ -347,7 +366,6 @@ public IList> ToSegments() return list; } -#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP /// 转为Span /// public Span AsSpan() @@ -365,7 +383,6 @@ public Memory AsMemory() return new Memory(ToArray()); } -#endif /// 获取封包的数据流形式 /// diff --git a/NewLife.Core/Extension/SpanHelper.cs b/NewLife.Core/Extension/SpanHelper.cs new file mode 100644 index 000000000..190a55e05 --- /dev/null +++ b/NewLife.Core/Extension/SpanHelper.cs @@ -0,0 +1,63 @@ +using System.Buffers; +using System.Runtime.InteropServices; +using System.Text; + +namespace NewLife; + +/// Span帮助类 +public static class SpanHelper +{ + /// 获取字符串的字节数组 + public static unsafe Int32 GetBytes(this Encoding encoding, ReadOnlySpan chars, Span bytes) + { + fixed (Char* chars2 = &MemoryMarshal.GetReference(chars)) + { + fixed (Byte* bytes2 = &MemoryMarshal.GetReference(bytes)) + { + return encoding.GetBytes(chars2, chars.Length, bytes2, bytes.Length); + } + } + } + + /// 获取字节数组的字符串 + public static unsafe String GetString(this Encoding encoding, ReadOnlySpan 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 + } + + /// 写入Memory到数据流 + /// + /// + /// + /// + public static Task WriteAsync(this Stream stream, ReadOnlyMemory buffer, CancellationToken cancellationToken = default) + { + if (MemoryMarshal.TryGetArray(buffer, out var segment)) + return stream.WriteAsync(segment.Array!, segment.Offset, segment.Count, cancellationToken); + + var array = ArrayPool.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.Shared.Return(array); + } + }); + } +} \ No newline at end of file diff --git a/NewLife.Core/NewLife.Core.csproj b/NewLife.Core/NewLife.Core.csproj index 07b4576da..f3ac9caf8 100644 --- a/NewLife.Core/NewLife.Core.csproj +++ b/NewLife.Core/NewLife.Core.csproj @@ -20,6 +20,7 @@ True ..\Doc\newlife.snk 1701;1702;NU5104;NU1505;NETSDK1138;CS7035 + True $(AssemblyName) @@ -61,8 +62,8 @@ - - + + diff --git a/NewLife.Core/Serialization/SpanReader.cs b/NewLife.Core/Serialization/SpanReader.cs new file mode 100644 index 000000000..0f9a7407c --- /dev/null +++ b/NewLife.Core/Serialization/SpanReader.cs @@ -0,0 +1,214 @@ +using System.Buffers.Binary; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Text; + +namespace NewLife.Serialization; + +/// Span读取器 +/// +public ref struct SpanReader(Span span) +{ + #region 属性 + private readonly Span _span = span; + + private Int32 _index; + /// 已写入字节数 + public Int32 WrittenCount => _index; + + /// 总容量 + public Int32 Capacity => _span.Length; + + /// 空闲容量 + public Int32 FreeCapacity => _span.Length - _index; + + /// 是否小端字节序。默认true + public Boolean IsLittleEndian { get; set; } = true; + #endregion + + #region 基础方法 + /// 告知有多少数据已写入缓冲区 + /// + 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; + } + + /// 返回要写入到的Span,其大小按 sizeHint 参数指定至少为所请求的大小 + /// + /// + /// + public Span GetSpan(Int32 sizeHint = 0) + { + if (sizeHint > FreeCapacity) throw new ArgumentOutOfRangeException(nameof(sizeHint)); + + return _span[.._index]; + } + #endregion + + #region 读取方法 + /// 确保缓冲区中有足够的空间。 + /// 需要的字节数。 + private void EnsureSpace(Int32 size) + { + if (_index + size > _span.Length) + throw new InvalidOperationException("Not enough data to read."); + } + + /// 读取单个字节 + /// + public Byte ReadByte() + { + var size = sizeof(Byte); + EnsureSpace(size); + var result = _span[_index]; + _index += size; + return result; + } + + /// 读取Int16整数 + /// + 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; + } + + /// 读取UInt16整数 + /// + 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; + } + + /// 读取Int32整数 + /// + 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; + } + + /// 读取UInt32整数 + /// + 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; + } + + /// 读取Int64整数 + /// + 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; + } + + /// 读取UInt64整数 + /// + 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; + } + + /// 读取单精度浮点数 + /// + public unsafe Single ReadSingle() + { +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP + return BitConverter.Int32BitsToSingle(ReadInt32()); +#else + var result = ReadInt32(); + return Unsafe.ReadUnaligned(ref Unsafe.As(ref result)); +#endif + } + + /// 读取双精度浮点数 + /// + public Double ReadDouble() + { +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP + return BitConverter.Int64BitsToDouble(ReadInt64()); +#else + var result = ReadInt64(); + return Unsafe.ReadUnaligned(ref Unsafe.As(ref result)); +#endif + } + + /// 读取字符串 + /// + /// + /// + /// + public String ReadString(Int32 length, Encoding encoding) + { + EnsureSpace(length); + + var result = encoding.GetString(_span.Slice(_index, length)); + _index += length; + return result; + } + + /// 读取字节数组 + /// + /// + /// + public Span ReadBytes(Int32 length) + { + EnsureSpace(length); + + var result = _span.Slice(_index, length); + _index += length; + return result; + } + + /// 读取结构体 + /// + /// + public T Read() where T : struct + { + var size = Unsafe.SizeOf(); + EnsureSpace(size); + + var result = MemoryMarshal.Read(_span.Slice(_index)); + _index += size; + return result; + } + #endregion +} diff --git a/NewLife.Core/Serialization/SpanWriter.cs b/NewLife.Core/Serialization/SpanWriter.cs new file mode 100644 index 000000000..6d0f5630b --- /dev/null +++ b/NewLife.Core/Serialization/SpanWriter.cs @@ -0,0 +1,241 @@ +using System.Buffers; +using System.Buffers.Binary; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Text; + +namespace NewLife.Serialization; + +/// Span写入器 +/// +public ref struct SpanWriter(Span buffer) +{ + #region 属性 + private readonly Span _span = buffer; + + private Int32 _index; + /// 已写入字节数 + public Int32 WrittenCount => _index; + + /// 总容量 + public Int32 Capacity => _span.Length; + + /// 空闲容量 + public Int32 FreeCapacity => _span.Length - _index; + + /// 是否小端字节序。默认true + public Boolean IsLittleEndian { get; set; } = true; + #endregion + + #region 基础方法 + /// 告知有多少数据已写入缓冲区 + /// + 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; + } + + //public Memory GetMemory(Int32 sizeHint = 0) + //{ + // if (sizeHint > FreeCapacity) throw new ArgumentOutOfRangeException(nameof(sizeHint)); + + // return _buffer[.._index]; + //} + + /// 返回要写入到的Span,其大小按 sizeHint 参数指定至少为所请求的大小 + /// + /// + /// + public Span GetSpan(Int32 sizeHint = 0) + { + if (sizeHint > FreeCapacity) throw new ArgumentOutOfRangeException(nameof(sizeHint)); + + return _span[.._index]; + } + + //public Span GetRemain() => _buffer[_index..]; + #endregion + + #region 写入方法 + /// 确保缓冲区中有足够的空间。 + /// 需要的字节数。 + private void EnsureSpace(Int32 size) + { + if (_index + size > _span.Length) + throw new InvalidOperationException("Not enough data to write."); + } + + /// 写入字节。 + /// 要写入的字节值。 + public Int32 Write(Byte value) + { + var size = sizeof(Byte); + EnsureSpace(size); + _span[_index] = value; + _index += size; + return size; + } + + /// 写入 16 位整数。 + /// 要写入的整数值。 + public Int32 Write(Int16 value) + { + var size = sizeof(Int16); + EnsureSpace(size); + if (IsLittleEndian) + BinaryPrimitives.WriteInt16LittleEndian(_span.Slice(_index), value); + else + BinaryPrimitives.WriteInt16BigEndian(_span.Slice(_index), value); + _index += size; + return size; + } + + /// 写入无符号 16 位整数。 + /// 要写入的无符号整数值。 + public Int32 Write(UInt16 value) + { + var size = sizeof(UInt16); + EnsureSpace(size); + if (IsLittleEndian) + BinaryPrimitives.WriteUInt16LittleEndian(_span.Slice(_index), value); + else + BinaryPrimitives.WriteUInt16BigEndian(_span.Slice(_index), value); + _index += size; + return size; + } + + /// 写入 32 位整数。 + /// 要写入的整数值。 + public Int32 Write(Int32 value) + { + var size = sizeof(Int32); + EnsureSpace(size); + if (IsLittleEndian) + BinaryPrimitives.WriteInt32LittleEndian(_span.Slice(_index), value); + else + BinaryPrimitives.WriteInt32BigEndian(_span.Slice(_index), value); + _index += size; + return size; + } + + /// 写入无符号 32 位整数。 + /// 要写入的无符号整数值。 + public Int32 Write(UInt32 value) + { + var size = sizeof(UInt32); + EnsureSpace(size); + if (IsLittleEndian) + BinaryPrimitives.WriteUInt32LittleEndian(_span.Slice(_index), value); + else + BinaryPrimitives.WriteUInt32BigEndian(_span.Slice(_index), value); + _index += size; + return size; + } + + /// 写入 64 位整数。 + /// 要写入的整数值。 + public Int32 Write(Int64 value) + { + var size = sizeof(Int64); + EnsureSpace(size); + if (IsLittleEndian) + BinaryPrimitives.WriteInt64LittleEndian(_span.Slice(_index), value); + else + BinaryPrimitives.WriteInt64BigEndian(_span.Slice(_index), value); + _index += size; + return size; + } + + /// 写入无符号 64 位整数。 + /// 要写入的无符号整数值。 + public Int32 Write(UInt64 value) + { + var size = sizeof(UInt64); + EnsureSpace(size); + if (IsLittleEndian) + BinaryPrimitives.WriteUInt64LittleEndian(_span.Slice(_index), value); + else + BinaryPrimitives.WriteUInt64BigEndian(_span.Slice(_index), value); + _index += size; + return size; + } + + /// 写入单精度浮点数。 + /// 要写入的浮点值。 + public unsafe Int32 Write(Single value) + { +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP + return Write(BitConverter.SingleToInt32Bits(value)); +#else + return Write(*(Int32*)&value); +#endif + } + + /// 写入双精度浮点数。 + /// 要写入的浮点值。 + public unsafe Int32 Write(Double value) + { +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP + return Write(BitConverter.DoubleToInt64Bits(value)); +#else + return Write(*(Int64*)&value); +#endif + } + + /// 写入字符串 + /// + /// + /// + public Int32 Write(String value) + { + if (value == null) throw new ArgumentNullException(nameof(value)); + + var count = Encoding.UTF8.GetBytes(value.AsSpan(), _span.Slice(_index)); + _index += count; + + return count; + } + + /// 写入字节数组 + /// + /// + /// + public Int32 Write(Byte[] value) + { + if (value == null) + throw new ArgumentNullException(nameof(value)); + + value.CopyTo(_span.Slice(_index)); + _index += value.Length; + + return value.Length; + } + + /// 写入Span + /// + /// + public Int32 Write(ReadOnlySpan span) + { + span.CopyTo(_span.Slice(_index)); + _index += span.Length; + + return span.Length; + } + + /// 写入结构体 + /// + /// + /// + public Int32 Write(T value) where T : struct + { + var size = Unsafe.SizeOf(); + EnsureSpace(size); + MemoryMarshal.Write(_span.Slice(_index, size), ref value); + _index += size; + return size; + } + #endregion +}