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