1
0

Begin implementing async IO

This commit is contained in:
2024-03-18 19:32:07 +01:00
parent a8819905dc
commit be45164f90
16 changed files with 1908 additions and 1158 deletions

617
Nbt/Serialization/NbtReader.cs Executable file → Normal file
View File

@@ -1,249 +1,368 @@
using System.Buffers;
using System.Text;
using Nbt.Tag;
namespace Nbt.Serialization;
public interface INbtReader : IDisposable
{
INbtTag ReadTag();
NamedTag ReadNamedTag();
NbtTagType ReadTagType();
sbyte ReadSByte();
short ReadShort();
int ReadInt();
long ReadLong();
float ReadFloat();
double ReadDouble();
string ReadString();
}
public sealed class NbtReader(Stream stream, bool leaveOpen = false) : INbtReader
{
private readonly Stream stream = stream;
private readonly bool leaveOpen = leaveOpen;
public Stream BaseStream => stream;
public static NbtReader Create(Stream stream, Compression compression = Compression.Auto, bool leaveOpen = false) => compression switch
{
Compression.Auto => CreateInflater(stream, leaveOpen),
Compression.GZip => new(Utils.CreateGZipInflater(stream, leaveOpen), false),
Compression.ZLib => new(Utils.CreateZLibInflater(stream, leaveOpen), false),
Compression.LZ4 => new(Utils.CreateLZ4Deflater(stream, leaveOpen), false),
_ => new(stream, leaveOpen)
};
private static NbtReader CreateInflater(Stream stream, bool leaveOpen)
{
var cmf = stream.ReadByte();
if (cmf < 0)
{
throw new EndOfStreamException();
}
stream.Seek(-1, SeekOrigin.Current);
return cmf switch
{
0x1F => new(Utils.CreateGZipInflater(stream, leaveOpen), false),
0x78 => new(Utils.CreateZLibInflater(stream, leaveOpen), false),
_ => throw new UnknownCompressionSchemeException(cmf)
};
}
private int ReadBytes(byte[] buffer, int offset, int count) => stream.Read(buffer, offset, count);
private int ReadBytes(byte[] buffer) => ReadBytes(buffer, 0, buffer.Length);
private int ReadBytes(Span<byte> buffer) => stream.Read(buffer);
private void ReadAllBytes(byte[] buffer, int offset, int count)
{
if (count == 0)
{
return;
}
var bytesLeft = count;
do
{
var bytesRead = ReadBytes(buffer, offset + count - bytesLeft, bytesLeft);
if (bytesRead == 0)
{
throw new EndOfStreamException();
}
bytesLeft -= bytesRead;
} while (bytesLeft > 0);
}
private void ReadAllBytes(byte[] buffer) => ReadAllBytes(buffer, 0, buffer.Length);
private void ReadAllBytes(Span<byte> buffer)
{
var count = buffer.Length;
if (count == 0)
{
return;
}
var bytesLeft = count;
do
{
var bytesRead = ReadBytes(buffer.Slice(count - bytesLeft, bytesLeft));
if (bytesRead == 0)
{
throw new EndOfStreamException();
}
bytesLeft -= bytesRead;
} while (bytesLeft > 0);
}
private int Read() => stream.ReadByte();
private int ReadAndThrowIfEndOfStream()
{
var b = Read();
return b < 0 ? throw new EndOfStreamException() : b;
}
private void ReadEndian(Span<byte> buffer)
{
ReadAllBytes(buffer);
if (BitConverter.IsLittleEndian)
{
buffer.Reverse();
}
}
private byte ReadByte() => (byte)ReadAndThrowIfEndOfStream();
public sbyte ReadSByte() => (sbyte)ReadAndThrowIfEndOfStream();
private ushort ReadUShort()
{
Span<byte> buffer = stackalloc byte[sizeof(ushort)];
ReadEndian(buffer);
return BitConverter.ToUInt16(buffer);
}
public short ReadShort()
{
Span<byte> buffer = stackalloc byte[sizeof(short)];
ReadEndian(buffer);
return BitConverter.ToInt16(buffer);
}
public int ReadInt()
{
Span<byte> buffer = stackalloc byte[sizeof(int)];
ReadEndian(buffer);
return BitConverter.ToInt32(buffer);
}
public long ReadLong()
{
Span<byte> buffer = stackalloc byte[sizeof(long)];
ReadEndian(buffer);
return BitConverter.ToInt64(buffer);
}
public float ReadFloat()
{
Span<byte> buffer = stackalloc byte[sizeof(float)];
ReadEndian(buffer);
return BitConverter.ToSingle(buffer);
}
public double ReadDouble()
{
Span<byte> buffer = stackalloc byte[sizeof(double)];
ReadEndian(buffer);
return BitConverter.ToDouble(buffer);
}
public string ReadString()
{
var len = ReadUShort();
if (len == 0)
{
return string.Empty;
}
var buffer = ArrayPool<byte>.Shared.Rent(len);
try
{
ReadAllBytes(buffer, 0, len);
return Encoding.UTF8.GetString(buffer, 0, len);
}
finally
{
Array.Fill(buffer, (byte)0, 0, len);
ArrayPool<byte>.Shared.Return(buffer);
}
}
public NbtTagType ReadTagType() => (NbtTagType)ReadByte();
public INbtTag ReadTag()
{
var tagType = NbtUtils.GetTagType(ReadTagType());
return tagType.Read(this);
}
public NamedTag ReadNamedTag()
{
var tagType = NbtUtils.GetTagType(ReadTagType());
var tagName = ReadString();
var tag = tagType.Read(this);
return new(tagName, tag);
}
#region IDisposable
private bool _disposedValue;
private void Dispose(bool disposing)
{
if (!_disposedValue)
{
if (disposing)
{
if (!leaveOpen)
{
stream.Dispose();
}
}
_disposedValue = true;
}
}
public void Dispose()
{
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
#endregion
}
using System.Buffers;
using System.Text;
using Nbt.Tag;
namespace Nbt.Serialization;
public interface INbtReader : IDisposable
{
INbtTag ReadTag();
NamedTag ReadNamedTag();
NbtTagType ReadTagType();
sbyte ReadSByte();
short ReadShort();
int ReadInt();
long ReadLong();
float ReadFloat();
double ReadDouble();
string ReadString();
}
public interface INbtAsyncReader : IAsyncDisposable
{
ValueTask<INbtTag> ReadTagAsync(CancellationToken ct = default);
ValueTask<NamedTag> ReadNamedTagAsync(CancellationToken ct = default);
ValueTask<NbtTagType> ReadTagTypeAsync(CancellationToken ct = default);
ValueTask<sbyte> ReadSByteAsync(CancellationToken ct = default);
ValueTask<short> ReadShortAsync(CancellationToken ct = default);
ValueTask<int> ReadIntAsync(CancellationToken ct = default);
ValueTask<long> ReadLongAsync(CancellationToken ct = default);
ValueTask<float> ReadFloatAsync(CancellationToken ct = default);
ValueTask<double> ReadDoubleAsync(CancellationToken ct = default);
ValueTask<string> ReadStringAsync(CancellationToken ct = default);
}
public sealed class NbtReader(Stream stream, bool leaveOpen = false) : INbtReader, INbtAsyncReader
{
private readonly Stream stream = stream;
private readonly bool leaveOpen = leaveOpen;
public Stream BaseStream => stream;
public static NbtReader Create(Stream stream, Compression compression = Compression.Auto, bool leaveOpen = false) => compression switch
{
Compression.Auto => CreateInflater(stream, leaveOpen),
Compression.GZip => new(Utils.CreateGZipInflater(stream, leaveOpen), false),
Compression.ZLib => new(Utils.CreateZLibInflater(stream, leaveOpen), false),
Compression.LZ4 => new(Utils.CreateLZ4Deflater(stream, leaveOpen), false),
_ => new(stream, leaveOpen)
};
private static NbtReader CreateInflater(Stream stream, bool leaveOpen)
{
if (!stream.CanSeek)
{
return new(stream, leaveOpen);
}
var cmf = stream.ReadByte();
if (cmf < 0)
{
throw new EndOfStreamException();
}
stream.Seek(-1, SeekOrigin.Current);
return cmf switch
{
0x1F => new(Utils.CreateGZipInflater(stream, leaveOpen), false),
0x78 => new(Utils.CreateZLibInflater(stream, leaveOpen), false),
_ => new(stream, leaveOpen)
};
}
#region Sync
private int Read(byte[] buffer, int offset, int count) => stream.Read(buffer, offset, count);
private int Read(Span<byte> buffer) => stream.Read(buffer);
private void ReadExactly(byte[] buffer, int offset, int count) => stream.ReadExactly(buffer, offset, count);
private void ReadExactly(Span<byte> buffer) => stream.ReadExactly(buffer);
private byte ReadByte()
{
var b = new byte[1];
ReadExactly(b);
return b[0];
}
private void ReadEndian(Span<byte> buffer)
{
ReadExactly(buffer);
if (BitConverter.IsLittleEndian)
{
buffer.Reverse();
}
}
public sbyte ReadSByte() => (sbyte)ReadByte();
private ushort ReadUShort()
{
Span<byte> buffer = stackalloc byte[sizeof(ushort)];
ReadEndian(buffer);
return BitConverter.ToUInt16(buffer);
}
public short ReadShort()
{
Span<byte> buffer = stackalloc byte[sizeof(short)];
ReadEndian(buffer);
return BitConverter.ToInt16(buffer);
}
public int ReadInt()
{
Span<byte> buffer = stackalloc byte[sizeof(int)];
ReadEndian(buffer);
return BitConverter.ToInt32(buffer);
}
public long ReadLong()
{
Span<byte> buffer = stackalloc byte[sizeof(long)];
ReadEndian(buffer);
return BitConverter.ToInt64(buffer);
}
public float ReadFloat()
{
Span<byte> buffer = stackalloc byte[sizeof(float)];
ReadEndian(buffer);
return BitConverter.ToSingle(buffer);
}
public double ReadDouble()
{
Span<byte> buffer = stackalloc byte[sizeof(double)];
ReadEndian(buffer);
return BitConverter.ToDouble(buffer);
}
public string ReadString()
{
var len = ReadUShort();
if (len == 0)
{
return string.Empty;
}
var buffer = ArrayPool<byte>.Shared.Rent(len);
try
{
ReadExactly(buffer, 0, len);
return Encoding.UTF8.GetString(buffer, 0, len);
}
finally
{
Array.Fill(buffer, (byte)0, 0, len);
ArrayPool<byte>.Shared.Return(buffer);
}
}
public NbtTagType ReadTagType() => (NbtTagType)ReadByte();
public INbtTag ReadTag()
{
var tagType = NbtUtils.GetTagType(ReadTagType());
return tagType.Read(this);
}
public NamedTag ReadNamedTag()
{
var tagType = NbtUtils.GetTagType(ReadTagType());
var tagName = ReadString();
var tag = tagType.Read(this);
return new(tagName, tag);
}
#endregion
#region Async
private ValueTask<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken ct) => new(stream.ReadAsync(buffer, offset, count, ct));
private ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken ct) => stream.ReadAsync(buffer, ct);
private ValueTask ReadExactlyAsync(byte[] buffer, int offset, int count, CancellationToken ct) => stream.ReadExactlyAsync(buffer, offset, count, ct);
private ValueTask ReadExactlyAsync(Memory<byte> buffer, CancellationToken ct) => stream.ReadExactlyAsync(buffer, ct);
private async ValueTask<int> ReadByteAsync(CancellationToken ct)
{
var b = new byte[1];
await ReadExactlyAsync(b, ct);
return b[0];
}
private async ValueTask ReadEndianAsync(Memory<byte> buffer, CancellationToken ct)
{
await ReadExactlyAsync(buffer, ct);
if (BitConverter.IsLittleEndian)
{
buffer.Span.Reverse();
}
}
public async ValueTask<sbyte> ReadSByteAsync(CancellationToken ct = default) => (sbyte)await ReadByteAsync(ct);
public async ValueTask<ushort> ReadUShortAsync(CancellationToken ct)
{
var buffer = new byte[sizeof(ushort)];
await ReadEndianAsync(buffer, ct);
return BitConverter.ToUInt16(buffer);
}
public async ValueTask<short> ReadShortAsync(CancellationToken ct = default)
{
var buffer = new byte[sizeof(short)];
await ReadEndianAsync(buffer, ct);
return BitConverter.ToInt16(buffer);
}
public async ValueTask<int> ReadIntAsync(CancellationToken ct = default)
{
var buffer = new byte[sizeof(int)];
await ReadEndianAsync(buffer, ct);
return BitConverter.ToInt32(buffer);
}
public async ValueTask<long> ReadLongAsync(CancellationToken ct = default)
{
var buffer = new byte[sizeof(long)];
await ReadEndianAsync(buffer, ct);
return BitConverter.ToInt64(buffer);
}
public async ValueTask<float> ReadFloatAsync(CancellationToken ct = default)
{
var buffer = new byte[sizeof(float)];
await ReadEndianAsync(buffer, ct);
return BitConverter.ToSingle(buffer);
}
public async ValueTask<double> ReadDoubleAsync(CancellationToken ct = default)
{
var buffer = new byte[sizeof(double)];
await ReadEndianAsync(buffer, ct);
return BitConverter.ToDouble(buffer);
}
public async ValueTask<string> ReadStringAsync(CancellationToken ct = default)
{
var len = await ReadUShortAsync(ct);
if (len == 0)
{
return string.Empty;
}
var buffer = ArrayPool<byte>.Shared.Rent(len);
try
{
await ReadExactlyAsync(buffer, 0, len, ct);
return Encoding.UTF8.GetString(buffer, 0, len);
}
finally
{
Array.Fill(buffer, (byte)0, 0, len);
ArrayPool<byte>.Shared.Return(buffer);
}
}
public async ValueTask<NbtTagType> ReadTagTypeAsync(CancellationToken ct = default) => (NbtTagType)await ReadByteAsync(ct);
public async ValueTask<INbtTag> ReadTagAsync(CancellationToken ct = default)
{
var tagType = await ReadTagTypeAsync(ct);
var type = NbtUtils.GetTagType(tagType);
return await type.ReadAsync(this, ct);
}
public async ValueTask<NamedTag> ReadNamedTagAsync(CancellationToken ct = default)
{
var tagType = await ReadTagTypeAsync(ct);
var type = NbtUtils.GetTagType(tagType);
var tagName = await ReadStringAsync(ct);
var tag = await type.ReadAsync(this, ct);
return new(tagName, tag);
}
#endregion
#region IDisposable
private bool _disposedValue;
private void Dispose(bool disposing)
{
if (!_disposedValue)
{
if (disposing)
{
if (!leaveOpen)
{
stream.Dispose();
}
}
_disposedValue = true;
}
}
public void Dispose()
{
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
private async ValueTask DisposeAsync(bool disposing)
{
if (!_disposedValue)
{
if (disposing)
{
if (!leaveOpen)
{
await stream.DisposeAsync();
}
}
_disposedValue = true;
}
}
public async ValueTask DisposeAsync()
{
await DisposeAsync(disposing: true);
GC.SuppressFinalize(this);
}
#endregion
}

524
Nbt/Serialization/NbtWriter.cs Executable file → Normal file
View File

@@ -1,185 +1,341 @@
using System.Text;
using Nbt.Tag;
namespace Nbt.Serialization;
public interface INbtWriter : IDisposable
{
void WriteNamedTag(NamedTag namedTag);
void WriteTag(INbtTag tag);
void WriteTagType(NbtTagType tagType);
void WriteSByte(sbyte b);
void WriteShort(short s);
void WriteInt(int i);
void WriteLong(long l);
void WriteFloat(float f);
void WriteDouble(double d);
void WriteString(string s);
}
public sealed class NbtWriter(Stream stream, bool leaveOpen = false) : INbtWriter
{
private readonly Stream stream = stream;
private readonly bool leaveOpen = leaveOpen;
public Stream BaseStream => stream;
public static NbtWriter Create(Stream stream, Compression compression = Compression.None, bool leaveOpen = false) => compression switch
{
Compression.GZip => new(Utils.CreateGZipDeflater(stream, leaveOpen), false),
Compression.ZLib => new(Utils.CreateZLibDeflater(stream, leaveOpen), false),
Compression.LZ4 => new(Utils.CreateLZ4Deflater(stream, leaveOpen), false),
Compression.Auto => throw new ArgumentException($"{nameof(Compression.Auto)} is invalid for a writer", nameof(compression)),
_ => new(stream, leaveOpen)
};
public void Flush() => stream.Flush();
public void WriteBytes(byte[] buffer, int offset, int count) => stream.Write(buffer, offset, count);
public void WriteBytes(byte[] buffer) => stream.Write(buffer, 0, buffer.Length);
public void WriteBytes(ReadOnlySpan<byte> buffer) => stream.Write(buffer);
public void WriteByte(byte b) => stream.WriteByte(b);
private void WriteEndian(Span<byte> buffer)
{
if (BitConverter.IsLittleEndian)
{
for (var i = buffer.Length - 1; i >= 0; i--)
{
WriteByte(buffer[i]);
}
}
else
{
WriteBytes(buffer);
}
}
public void WriteSByte(sbyte b)
{
WriteByte((byte)b);
}
public void WriteUShort(ushort s)
{
Span<byte> buffer = stackalloc byte[sizeof(ushort)];
BitConverter.TryWriteBytes(buffer, s);
WriteEndian(buffer);
}
public void WriteShort(short s)
{
Span<byte> buffer = stackalloc byte[sizeof(short)];
BitConverter.TryWriteBytes(buffer, s);
WriteEndian(buffer);
}
public void WriteUInt(uint i)
{
Span<byte> buffer = stackalloc byte[sizeof(uint)];
BitConverter.TryWriteBytes(buffer, i);
WriteEndian(buffer);
}
public void WriteInt(int i)
{
Span<byte> buffer = stackalloc byte[sizeof(int)];
BitConverter.TryWriteBytes(buffer, i);
WriteEndian(buffer);
}
public void WriteULong(ulong l)
{
Span<byte> buffer = stackalloc byte[sizeof(ulong)];
BitConverter.TryWriteBytes(buffer, l);
WriteEndian(buffer);
}
public void WriteLong(long l)
{
Span<byte> buffer = stackalloc byte[sizeof(long)];
BitConverter.TryWriteBytes(buffer, l);
WriteEndian(buffer);
}
public void WriteFloat(float f)
{
Span<byte> buffer = stackalloc byte[sizeof(float)];
BitConverter.TryWriteBytes(buffer, f);
WriteEndian(buffer);
}
public void WriteDouble(double d)
{
Span<byte> buffer = stackalloc byte[sizeof(double)];
BitConverter.TryWriteBytes(buffer, d);
WriteEndian(buffer);
}
public void WriteString(string s)
{
WriteUShort((ushort)s.Length);
var buffer = Encoding.UTF8.GetBytes(s);
WriteBytes(buffer);
}
public void WriteNamedTag(NamedTag namedTag) => WriteNamedTag(namedTag.Name, namedTag.Tag);
public void WriteNamedTag(string tagName, INbtTag tag)
{
var tagType = NbtUtils.GetTagType(tag.Type);
WriteTagType(tag.Type);
WriteString(tagName);
tagType.Write(this, tag);
}
public void WriteTag(INbtTag tag)
{
var tagType = NbtUtils.GetTagType(tag.Type);
WriteTagType(tag.Type);
tagType.Write(this, tag);
}
public void WriteTagType(NbtTagType tagType) => WriteByte((byte)tagType);
#region IDisposable
private bool _disposedValue;
private void Dispose(bool disposing)
{
if (!_disposedValue)
{
if (disposing)
{
if (!leaveOpen)
{
stream.Dispose();
}
}
_disposedValue = true;
}
}
public void Dispose()
{
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
#endregion
using System.Text;
using Nbt.Tag;
namespace Nbt.Serialization;
public interface INbtWriter : IDisposable
{
void WriteNamedTag(NamedTag namedTag);
void WriteTag(INbtTag tag);
void WriteTagType(NbtTagType tagType);
void WriteSByte(sbyte b);
void WriteShort(short s);
void WriteInt(int i);
void WriteLong(long l);
void WriteFloat(float f);
void WriteDouble(double d);
void WriteString(string s);
}
public interface INbtAsyncWriter : IAsyncDisposable
{
ValueTask WriteNamedTagAsync(NamedTag namedTag, CancellationToken ct = default);
ValueTask WriteTagAsync(INbtTag tag, CancellationToken ct = default);
ValueTask WriteTagTypeAsync(NbtTagType tagType, CancellationToken ct = default);
ValueTask WriteSByteAsync(sbyte b, CancellationToken ct = default);
ValueTask WriteShortAsync(short s, CancellationToken ct = default);
ValueTask WriteIntAsync(int i, CancellationToken ct = default);
ValueTask WriteLongAsync(long l, CancellationToken ct = default);
ValueTask WriteFloatAsync(float f, CancellationToken ct = default);
ValueTask WriteDoubleAsync(double d, CancellationToken ct = default);
ValueTask WriteStringAsync(string s, CancellationToken ct = default);
}
public sealed class NbtWriter(Stream stream, bool leaveOpen = false) : INbtWriter, INbtAsyncWriter
{
private readonly Stream stream = stream;
private readonly bool leaveOpen = leaveOpen;
public Stream BaseStream => stream;
public static NbtWriter Create(Stream stream, Compression compression = Compression.None, bool leaveOpen = false) => compression switch
{
Compression.GZip => new(Utils.CreateGZipDeflater(stream, leaveOpen), false),
Compression.ZLib => new(Utils.CreateZLibDeflater(stream, leaveOpen), false),
Compression.LZ4 => new(Utils.CreateLZ4Deflater(stream, leaveOpen), false),
Compression.Auto => throw new ArgumentException($"{nameof(Compression.Auto)} is invalid for a writer", nameof(compression)),
_ => new(stream, leaveOpen)
};
#region Sync
private void Flush() => stream.Flush();
private void Write(byte[] buffer, int offset, int count) => stream.Write(buffer, offset, count);
private void Write(ReadOnlySpan<byte> buffer) => stream.Write(buffer);
private void WriteByte(byte b) => stream.WriteByte(b);
private void WriteEndian(Span<byte> buffer)
{
if (BitConverter.IsLittleEndian)
{
for (var i = buffer.Length - 1; i >= 0; i--)
{
WriteByte(buffer[i]);
}
}
else
{
Write(buffer);
}
}
public void WriteSByte(sbyte b) => WriteByte((byte)b);
public void WriteUShort(ushort s)
{
Span<byte> buffer = stackalloc byte[sizeof(ushort)];
BitConverter.TryWriteBytes(buffer, s);
WriteEndian(buffer);
}
public void WriteShort(short s)
{
Span<byte> buffer = stackalloc byte[sizeof(short)];
BitConverter.TryWriteBytes(buffer, s);
WriteEndian(buffer);
}
public void WriteUInt(uint i)
{
Span<byte> buffer = stackalloc byte[sizeof(uint)];
BitConverter.TryWriteBytes(buffer, i);
WriteEndian(buffer);
}
public void WriteInt(int i)
{
Span<byte> buffer = stackalloc byte[sizeof(int)];
BitConverter.TryWriteBytes(buffer, i);
WriteEndian(buffer);
}
public void WriteULong(ulong l)
{
Span<byte> buffer = stackalloc byte[sizeof(ulong)];
BitConverter.TryWriteBytes(buffer, l);
WriteEndian(buffer);
}
public void WriteLong(long l)
{
Span<byte> buffer = stackalloc byte[sizeof(long)];
BitConverter.TryWriteBytes(buffer, l);
WriteEndian(buffer);
}
public void WriteFloat(float f)
{
Span<byte> buffer = stackalloc byte[sizeof(float)];
BitConverter.TryWriteBytes(buffer, f);
WriteEndian(buffer);
}
public void WriteDouble(double d)
{
Span<byte> buffer = stackalloc byte[sizeof(double)];
BitConverter.TryWriteBytes(buffer, d);
WriteEndian(buffer);
}
public void WriteString(string s)
{
WriteUShort((ushort)s.Length);
var buffer = Encoding.UTF8.GetBytes(s);
Write(buffer);
}
public void WriteNamedTag(NamedTag namedTag) => WriteNamedTag(namedTag.Name, namedTag.Tag);
public void WriteNamedTag(string tagName, INbtTag tag)
{
var tagType = NbtUtils.GetTagType(tag.Type);
WriteTagType(tag.Type);
WriteString(tagName);
tagType.Write(this, tag);
}
public void WriteTag(INbtTag tag)
{
var tagType = NbtUtils.GetTagType(tag.Type);
WriteTagType(tag.Type);
tagType.Write(this, tag);
}
public void WriteTagType(NbtTagType tagType) => WriteByte((byte)tagType);
#endregion
#region Async
private ValueTask FlushAsync(CancellationToken ct) => new(stream.FlushAsync(ct));
private ValueTask WriteAsync(byte[] buffer, int offset, int count, CancellationToken ct) => new(stream.WriteAsync(buffer, offset, count, ct));
private ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken ct) => stream.WriteAsync(buffer, ct);
private ValueTask WriteByteAsync(byte b, CancellationToken ct) => new(stream.WriteAsync([b], 0, 1, ct));
private async ValueTask WriteEndianAsync(Memory<byte> buffer, CancellationToken ct)
{
if (BitConverter.IsLittleEndian)
{
for (var i = buffer.Length - 1; i >= 0; i--)
{
await WriteAsync(buffer.Slice(i, 1), ct);
}
}
else
{
await WriteAsync(buffer, ct);
}
}
public ValueTask WriteSByteAsync(sbyte b, CancellationToken ct = default) => WriteByteAsync((byte)b, ct);
public async ValueTask WriteUShortAsync(ushort s, CancellationToken ct = default)
{
var buffer = new byte[sizeof(ushort)];
BitConverter.TryWriteBytes(buffer, s);
await WriteEndianAsync(buffer, ct);
}
public async ValueTask WriteShortAsync(short s, CancellationToken ct = default)
{
var buffer = new byte[sizeof(short)];
BitConverter.TryWriteBytes(buffer, s);
await WriteEndianAsync(buffer, ct);
}
public async ValueTask WriteUIntAsync(uint i, CancellationToken ct = default)
{
var buffer = new byte[sizeof(uint)];
BitConverter.TryWriteBytes(buffer, i);
await WriteEndianAsync(buffer, ct);
}
public async ValueTask WriteIntAsync(int i, CancellationToken ct = default)
{
var buffer = new byte[sizeof(int)];
BitConverter.TryWriteBytes(buffer, i);
await WriteEndianAsync(buffer, ct);
}
public async ValueTask WriteULongAsync(ulong l, CancellationToken ct = default)
{
var buffer = new byte[sizeof(ulong)];
BitConverter.TryWriteBytes(buffer, l);
await WriteEndianAsync(buffer, ct);
}
public async ValueTask WriteLongAsync(long l, CancellationToken ct = default)
{
var buffer = new byte[sizeof(long)];
BitConverter.TryWriteBytes(buffer, l);
await WriteEndianAsync(buffer, ct);
}
public async ValueTask WriteFloatAsync(float f, CancellationToken ct = default)
{
var buffer = new byte[sizeof(float)];
BitConverter.TryWriteBytes(buffer, f);
await WriteEndianAsync(buffer, ct);
}
public async ValueTask WriteDoubleAsync(double d, CancellationToken ct = default)
{
var buffer = new byte[sizeof(double)];
BitConverter.TryWriteBytes(buffer, d);
await WriteEndianAsync(buffer, ct);
}
public async ValueTask WriteStringAsync(string s, CancellationToken ct = default)
{
await WriteUShortAsync((ushort)s.Length, ct);
var buffer = Encoding.UTF8.GetBytes(s);
await WriteAsync(buffer, ct);
}
public ValueTask WriteNamedTagAsync(NamedTag namedTag, CancellationToken ct = default) => WriteNamedTagAsync(namedTag.Name, namedTag.Tag, ct);
public async ValueTask WriteNamedTagAsync(string tagName, INbtTag tag, CancellationToken ct = default)
{
var tagType = NbtUtils.GetTagType(tag.Type);
await WriteTagTypeAsync(tag.Type, ct);
await WriteStringAsync(tagName, ct);
await tagType.WriteAsync(this, tag, ct);
}
public async ValueTask WriteTagAsync(INbtTag tag, CancellationToken ct = default)
{
var tagType = NbtUtils.GetTagType(tag.Type);
await WriteTagTypeAsync(tag.Type, ct);
await tagType.WriteAsync(this, tag, ct);
}
public ValueTask WriteTagTypeAsync(NbtTagType tagType, CancellationToken ct = default) => WriteByteAsync((byte)tagType, ct);
#endregion
#region IDisposable
private bool _disposedValue;
private void Dispose(bool disposing)
{
if (!_disposedValue)
{
if (disposing)
{
if (!leaveOpen)
{
stream.Dispose();
}
}
_disposedValue = true;
}
}
public void Dispose()
{
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
private async ValueTask DisposeAsync(bool disposing)
{
if (!_disposedValue)
{
if (disposing)
{
if (!leaveOpen)
{
await stream.DisposeAsync();
}
}
_disposedValue = true;
}
}
public async ValueTask DisposeAsync()
{
await DisposeAsync(disposing: true);
GC.SuppressFinalize(this);
}
#endregion
}

1155
Nbt/Serialization/SNbt.cs Executable file → Normal file

File diff suppressed because it is too large Load Diff