/************************************************************************* * ModernUO * * Copyright (C) 2019-2020 - ModernUO Development Team * * Email: hi@modernuo.com * * File: SpanWriter.cs - Created: 2019/08/05 - Updated: 2019/12/24 * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * *************************************************************************/ using System; using System.Text; namespace Server.Buffers { /// /// Provides functionality for writing primitive binary data. /// public ref struct SpanWriter { private int m_Position; /// /// Underlying Span. /// public Span RawSpan { get; } /// /// Underlying Span up to the bytes written. /// public Span Span => RawSpan.Slice(0, WrittenCount); /// /// Gets the total length of the span. /// public int Length => RawSpan.Length; /// /// Total bytes written to the span. /// public int WrittenCount { get; private set; } /// /// Current position in the the span. /// public int Position { get => m_Position; set { m_Position = value; if (value > WrittenCount) WrittenCount = value; } } /// /// Instantiates a new SpanWriter instance. /// public SpanWriter(Span span) { RawSpan = span; m_Position = 0; WrittenCount = 0; } /// /// Writes a 1-byte boolean value to the span. /// public unsafe void Write(bool value) { RawSpan[Position++] = *(byte*)&value; } /// /// Writes a 1-byte unsigned integer value to the span. /// public void Write(byte value) { RawSpan[Position++] = value; } /// /// Writes a 1-byte signed integer value to the span. /// public void Write(sbyte value) { RawSpan[Position++] = (byte)value; } /// /// Writes a 2-byte signed integer value to the span. /// public void Write(short value) { Write((byte)(value >> 8)); Write((byte)value); } /// /// Writes a 2-byte unsigned integer value to the span. /// public void Write(ushort value) { Write((byte)(value >> 8)); Write((byte)value); } /// /// Writes a 4-byte signed integer value to the span. /// public void Write(int value) { Write((byte)(value >> 24)); Write((byte)(value >> 16)); Write((byte)(value >> 8)); Write((byte)value); } /// /// Writes a 4-byte unsigned integer value to the span. /// public void Write(uint value) { Write((byte)(value >> 24)); Write((byte)(value >> 16)); Write((byte)(value >> 8)); Write((byte)value); } /// /// Writes an 8-byte signed integer value to the span. /// public void Write(long value) { Write((byte)(value >> 56)); Write((byte)(value >> 48)); Write((byte)(value >> 40)); Write((byte)(value >> 32)); Write((byte)(value >> 24)); Write((byte)(value >> 16)); Write((byte)(value >> 8)); Write((byte)value); } /// /// Writes an 8-byte unsigned integer value to the span. /// public void Write(ulong value) { Write((byte)(value >> 56)); Write((byte)(value >> 48)); Write((byte)(value >> 40)); Write((byte)(value >> 32)); Write((byte)(value >> 24)); Write((byte)(value >> 16)); Write((byte)(value >> 8)); Write((byte)value); } /// /// Writes a sequence of bytes to the span. /// public void Write(ReadOnlySpan input) { var size = Math.Min(input.Length, Length - Position); input.Slice(0, size).CopyTo(RawSpan.Slice(Position)); Position += size; } /// /// Writes an ASCII-encoded string value to the span. /// public void WriteAscii(string value) { Position += Encoding.ASCII.GetBytes(value ?? "", RawSpan.Slice(Position)); } /// /// Writes a fixed-length ASCII-encoded string value to the span. /// public void WriteAsciiFixed(string value, int size, bool zero = false) { value ??= ""; var length = Math.Min(size, value.Length); Encoding.ASCII.GetBytes(value.AsSpan(0, length), RawSpan.Slice(Position)); if (zero) { Position += length; Fill(size - length); } else { Position += size; } } /// /// Writes a dynamic-length ASCII-encoded string value to the span, followed by a 1-byte null character. /// public void WriteAsciiNull(string value) { Position += Encoding.ASCII.GetBytes(value ?? "", RawSpan.Slice(Position)); Write((byte)0); } /// /// Writes a dynamic-length ASCII-encoded string value to the span, followed by a 1-byte null character. /// public void WriteAsciiNull(string value, int size) { value ??= ""; size = Math.Min(size, value.Length); Position += Encoding.ASCII.GetBytes(value.AsSpan(0, size), RawSpan.Slice(Position)); Write((byte)0); } /// /// Writes a dynamic-length little-endian unicode string value to the span. /// public void WriteLittleUni(string value) { Position += Encoding.Unicode.GetBytes(value ?? "", RawSpan.Slice(Position)); } /// /// Writes a dynamic-length little-endian unicode string value to the span, followed by a 2-byte null character. /// public void WriteLittleUniNull(string value) { WriteLittleUni(value); Write((ushort)0); } /// /// Writes a dynamic-length big-endian unicode string value to the span. /// public void WriteBigUni(string value) { Position += Encoding.BigEndianUnicode.GetBytes(value ?? "", RawSpan.Slice(Position)); } /// /// Writes a dynamic-length big-endian unicode string value to the span, followed by a 2-byte null character. /// public void WriteBigUniNull(string value, bool zero = false) { WriteBigUni(value); if (zero) Fill(2); else Position += 2; } /// /// Writes a dynamic-length utf-8 string value, followed by a 1-byte null character. /// public void WriteUTF8Null(string value) { Position += Encoding.UTF8.GetBytes(value ?? "", RawSpan.Slice(Position)) + 1; } /// /// Copies the span to the destination. /// public void CopyTo(Span destination) { RawSpan.CopyTo(destination); } /// /// Fills the buffer with zeroes up to count /// public void Fill(int count) { count = Math.Min(count, RawSpan.Length - Position); RawSpan.Slice(Position, count).Clear(); Position += count; } } }