// SPDX-License-Identifier: AGPL-3.0-or-later // Copyright (C) 2022-2026 Aykut Alparslan KOC using System; using System.Buffers; using System.IO; using System.IO.Pipelines; using System.Runtime.CompilerServices; using System.Text; using DotNext.Buffers; using DotNext.IO; using DotNext.IO.Pipelines; namespace Ferrite.Utils; public static class BufferExtensions { [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ReadOnlySpan ToSpan(in this ReadOnlySequence buffer) { if (buffer.IsSingleSegment) { return buffer.FirstSpan; } return buffer.ToArray(); } public static string ReadTLString(this ref SequenceReader reader) { return Encoding.UTF8.GetString(reader.ReadTLBytes()); } public static Span ReadTLBytes(this ref SequenceReader reader) { int len; byte b = reader.ReadByte(); len = b; int rem = (4 - ((len + 1) % 4)) % 4; if (len == 254) { Span len_bytes = stackalloc byte[3]; reader.Read(len_bytes); len = ((int)len_bytes[0]) | ((int)len_bytes[1] << 8) | ((int)len_bytes[2] << 16); rem = (4 - ((len + 4) % 4)) % 4; } Span result = new byte[len]; reader.Read(result); reader.Skip(rem); return result; } public static async Task ReadTLBytesLength(this PipeReader reader) { var firstByte = new byte[1]; await reader.ReadExactlyAsync(firstByte); byte b = firstByte[0]; int len = b; if (len == 254) { var lenBytes = new byte[3]; await reader.ReadExactlyAsync(lenBytes); len = ((int)lenBytes[0]) | ((int)lenBytes[1] << 8) | ((int)lenBytes[2] << 16); } return len; } public static async ValueTask ReadToMemoryAsync(this PipeReader reader, Memory destination, CancellationToken cancellationToken = default) { int copied = 0; while (copied < destination.Length) { var result = await reader.ReadAsync(cancellationToken); var buffer = result.Buffer; int toCopy = (int)Math.Min(buffer.Length, destination.Length - copied); if (toCopy > 0) { buffer.Slice(0, toCopy).CopyTo(destination.Span.Slice(copied, toCopy)); copied += toCopy; reader.AdvanceTo(buffer.GetPosition(toCopy)); } else { reader.AdvanceTo(buffer.Start, buffer.End); } if (copied == destination.Length) { return; } if (result.IsCompleted) { throw new EndOfStreamException(); } } } public static void WriteTLString(this IBufferWriter writer, string value) { byte[] bytes = Encoding.UTF8.GetBytes(value); writer.WriteTLBytes(bytes); } public static void WriteTLString(this SparseBufferWriter writer, string value) { byte[] bytes = Encoding.UTF8.GetBytes(value); ((IBufferWriter)writer).WriteTLBytes(bytes); } public static void WriteTLBytes(this SparseBufferWriter writer, in byte[] bytes) { ((IBufferWriter)writer).WriteTLBytes(in bytes); } public static void WriteTLBytes(this SparseBufferWriter writer, ReadOnlySpan bytes) { ((IBufferWriter)writer).WriteTLBytes(bytes); } public static void WriteTLBytes(this SparseBufferWriter writer, in ReadOnlySequence bytes, bool copymemory = true) { const byte zero = 0; int rem; if (bytes.Length < 254) { writer.Write((byte)bytes.Length); rem = (int)((4 - ((bytes.Length + 1) % 4)) % 4); } else { writer.Write((byte)254); writer.Write((byte)(bytes.Length & 0xff)); writer.Write((byte)((bytes.Length >> 8) & 0xff)); writer.Write((byte)((bytes.Length >> 16) & 0xff)); rem = (int)((4 - ((bytes.Length + 4) % 4)) % 4); } writer.Write(bytes, copymemory); for (int i = 0; i < rem; i++) { writer.Write(zero); } } public static void WriteTLBytes(this IBufferWriter writer, in ReadOnlySequence bytes) { const byte zero = 0; int rem; if (bytes.Length < 254) { writer.Write((byte)bytes.Length); rem = (int)((4 - ((bytes.Length + 1) % 4)) % 4); } else { writer.Write((byte)254); writer.Write((byte)(bytes.Length & 0xff)); writer.Write((byte)((bytes.Length >> 8) & 0xff)); writer.Write((byte)((bytes.Length >> 16) & 0xff)); rem = (int)((4 - ((bytes.Length + 4) % 4)) % 4); } writer.Write(bytes); for (int i = 0; i < rem; i++) { writer.Write(zero); } } public static void WriteTLBytes(this IBufferWriter writer, ReadOnlySpan bytes) { const byte zero = 0; int rem; if (bytes.Length < 254) { writer.Write((byte)bytes.Length); rem = (4 - ((bytes.Length + 1) % 4)) % 4; } else { writer.Write((byte)254); writer.Write((byte)(bytes.Length & 0xff)); writer.Write((byte)((bytes.Length >> 8) & 0xff)); writer.Write((byte)((bytes.Length >> 16) & 0xff)); rem = (4 - ((bytes.Length + 4) % 4)) % 4; } writer.Write(bytes); for (int i = 0; i < rem; i++) { writer.Write(zero); } } public static void WriteTLBytes(this IBufferWriter writer, in byte[] bytes) { const byte zero = 0; int rem; if (bytes.Length < 254) { writer.Write((byte)bytes.Length); rem = (4 - ((bytes.Length + 1) % 4)) % 4; } else { writer.Write((byte)254); writer.Write((byte)(bytes.Length & 0xff)); writer.Write((byte)((bytes.Length >> 8) & 0xff)); writer.Write((byte)((bytes.Length >> 16) & 0xff)); rem = (4 - ((bytes.Length + 4) % 4)) % 4; } writer.Write(bytes); for (int i = 0; i < rem; i++) { writer.Write(zero); } } }