Implemented a stack getting naming strategy during serialization

This commit is contained in:
ForeverZer0 2021-08-21 18:45:14 -04:00
parent 7bb273b262
commit bf700464fa
7 changed files with 96 additions and 57 deletions

View File

@ -0,0 +1,29 @@
using System;
using Xunit;
namespace SharpNBT.Tests
{
public class CompressionTest : IDisposable
{
public void Dispose()
{
}
[Fact]
public void ReadUncompressed()
{
using var stream = NbtStream.OpenRead("./Data/hello_world.nbt");
var compound = stream.ReadTag<CompoundTag>();
Assert.Equal("hello world", compound.Name);
}
[Fact]
public void ReadCompressed()
{
using var stream = NbtStream.OpenRead("./Data/bigtest.nbt");
var compound = stream.ReadTag<CompoundTag>();
Assert.Equal("Level", compound.Name);
}
}
}

View File

@ -15,7 +15,7 @@ namespace SharpNBT
/// <returns>The deserialized <see cref="ByteTag"/> instance.</returns>
public new ByteTag ReadByte()
{
var name = named ? ReadPrefixedString() : null;
var name = nameStack.Peek() ? ReadPrefixedString() : null;
return new ByteTag(name, (byte)BaseStream.ReadByte());
}
@ -26,7 +26,7 @@ namespace SharpNBT
/// <returns>The deserialized <see cref="ShortTag"/> instance.</returns>
public ShortTag ReadShort()
{
var name = named ? ReadPrefixedString() : null;
var name = nameStack.Peek() ? ReadPrefixedString() : null;
return new ShortTag(name, BitConverter.ToInt16(ReadNumber(sizeof(short))));
}
@ -37,7 +37,7 @@ namespace SharpNBT
/// <returns>The deserialized <see cref="IntTag"/> instance.</returns>
public IntTag ReadInt()
{
var name = named ? ReadPrefixedString() : null;
var name = nameStack.Peek() ? ReadPrefixedString() : null;
return new IntTag(name, BitConverter.ToInt32(ReadNumber(sizeof(int))));
}
@ -48,7 +48,7 @@ namespace SharpNBT
/// <returns>The deserialized <see cref="LongTag"/> instance.</returns>
public LongTag ReadLong()
{
var name = named ? ReadPrefixedString() : null;
var name = nameStack.Peek() ? ReadPrefixedString() : null;
return new LongTag(name, BitConverter.ToInt64(ReadNumber(sizeof(long))));
}
@ -59,7 +59,7 @@ namespace SharpNBT
/// <returns>The deserialized <see cref="FloatTag"/> instance.</returns>
public FloatTag ReadFloat()
{
var name = named ? ReadPrefixedString() : null;
var name = nameStack.Peek() ? ReadPrefixedString() : null;
return new FloatTag(name, BitConverter.ToSingle(ReadNumber(sizeof(float))));
}
@ -70,7 +70,7 @@ namespace SharpNBT
/// <returns>The deserialized <see cref="DoubleTag"/> instance.</returns>
public DoubleTag ReadDouble()
{
var name = named ? ReadPrefixedString() : null;
var name = nameStack.Peek() ? ReadPrefixedString() : null;
return new DoubleTag( name, BitConverter.ToDouble(ReadNumber(sizeof(double))));
}
@ -81,7 +81,7 @@ namespace SharpNBT
/// <returns>The deserialized <see cref="StringTag"/> instance.</returns>
public StringTag ReadString()
{
var name = named ? ReadPrefixedString() : null;
var name = nameStack.Peek() ? ReadPrefixedString() : null;
var value = ReadPrefixedString();
return new StringTag(name, value);
}
@ -93,7 +93,7 @@ namespace SharpNBT
/// <returns>The deserialized <see cref="ByteArrayTag"/> instance.</returns>
public ByteArrayTag ReadByteArray()
{
var name = named ? ReadPrefixedString() : null;
var name = nameStack.Peek() ? ReadPrefixedString() : null;
var count = BitConverter.ToInt32(ReadNumber(sizeof(int)));
var buffer = new byte[count];
BaseStream.Read(buffer, 0, count);
@ -109,7 +109,7 @@ namespace SharpNBT
{
const int INT_SIZE = sizeof(int);
var name = named ? ReadPrefixedString() : null;
var name = nameStack.Peek() ? ReadPrefixedString() : null;
var count = BitConverter.ToInt32(ReadNumber(sizeof(int)));
var buffer = new byte[count * INT_SIZE];
BaseStream.Read(buffer, 0, count * INT_SIZE);
@ -143,7 +143,7 @@ namespace SharpNBT
{
const int LONG_SIZE = sizeof(long);
var name = named ? ReadPrefixedString() : null;
var name = nameStack.Peek() ? ReadPrefixedString() : null;
var count = BitConverter.ToInt32(ReadNumber(sizeof(int)));
var buffer = new byte[count * LONG_SIZE];
BaseStream.Read(buffer, 0, count * LONG_SIZE);
@ -177,19 +177,18 @@ namespace SharpNBT
/// <returns>The deserialized <see cref="ListTag"/> instance.</returns>
public ListTag ReadList()
{
var name = named ? ReadPrefixedString() : null;
var name = nameStack.Peek() ? ReadPrefixedString() : null;
var childType = ReadType();
var count = BitConverter.ToInt32(ReadNumber(sizeof(int)));
var list = new ListTag(name, childType);
var previous = named;
named = false;
nameStack.Push(false);
while (count-- > 0)
{
list.Add(ReadTag(childType));
}
named = previous;
nameStack.Pop();
return list;
}
@ -200,10 +199,9 @@ namespace SharpNBT
/// <returns>The deserialized <see cref="CompoundTag"/> instance.</returns>
public CompoundTag ReadCompound()
{
var name = named ? ReadPrefixedString() : null;
var name = nameStack.Peek() ? ReadPrefixedString() : null;
var compound = new CompoundTag(name);
var previous = named;
named = true;
nameStack.Push(true);
while (true)
{
var type = ReadType();
@ -211,8 +209,7 @@ namespace SharpNBT
break;
compound.Add(ReadTag(type));
}
named = previous;
nameStack.Pop();
return compound;
}

View File

@ -7,9 +7,6 @@ namespace SharpNBT
{
public partial class NbtStream
{
private bool named = true;
public void WriteType(Tag tag)
{
BaseStream.WriteByte((byte) tag.Type);
@ -18,7 +15,7 @@ namespace SharpNBT
public void WriteByte(ByteTag tag)
{
BaseStream.WriteByte((byte) tag.Type);
if (named)
if (nameStack.Peek())
WriteString(tag.Name);
BaseStream.WriteByte(tag.Value);
}
@ -26,7 +23,7 @@ namespace SharpNBT
public void WriteShort(ShortTag tag)
{
BaseStream.WriteByte((byte) tag.Type);
if (named)
if (nameStack.Peek())
WriteString(tag.Name);
WriteNumber(BitConverter.GetBytes(tag.Value));
}
@ -34,7 +31,7 @@ namespace SharpNBT
public void WriteInt(IntTag tag)
{
BaseStream.WriteByte((byte) tag.Type);
if (named)
if (nameStack.Peek())
WriteString(tag.Name);
WriteNumber(BitConverter.GetBytes(tag.Value));
}
@ -42,7 +39,7 @@ namespace SharpNBT
public void WriteLong(LongTag tag)
{
BaseStream.WriteByte((byte) tag.Type);
if (named)
if (nameStack.Peek())
WriteString(tag.Name);
WriteNumber(BitConverter.GetBytes(tag.Value));
}
@ -50,7 +47,7 @@ namespace SharpNBT
public void WriteFloat(FloatTag tag)
{
BaseStream.WriteByte((byte) tag.Type);
if (named)
if (nameStack.Peek())
WriteString(tag.Name);
WriteNumber(BitConverter.GetBytes(tag.Value));
}
@ -58,7 +55,7 @@ namespace SharpNBT
public void WriteDouble(DoubleTag tag)
{
BaseStream.WriteByte((byte) tag.Type);
if (named)
if (nameStack.Peek())
WriteString(tag.Name);
WriteNumber(BitConverter.GetBytes(tag.Value));
}
@ -66,7 +63,7 @@ namespace SharpNBT
public void WriteString(StringTag tag)
{
BaseStream.WriteByte((byte) tag.Type);
if (named)
if (nameStack.Peek())
WriteString(tag.Name);
WriteString(tag.Value);
}
@ -74,7 +71,7 @@ namespace SharpNBT
public void WriteByteArray(ByteArrayTag tag)
{
BaseStream.WriteByte((byte) tag.Type);
if (named)
if (nameStack.Peek())
WriteString(tag.Name);
WriteNumber(BitConverter.GetBytes(tag.Count));
@ -86,7 +83,7 @@ namespace SharpNBT
const int INT_SIZE = sizeof(int);
BaseStream.WriteByte((byte) tag.Type);
if (named)
if (nameStack.Peek())
WriteString(tag.Name);
WriteNumber(BitConverter.GetBytes(tag.Count));
@ -121,7 +118,7 @@ namespace SharpNBT
const int LONG_SIZE = sizeof(long);
BaseStream.WriteByte((byte) tag.Type);
if (named)
if (nameStack.Peek())
WriteString(tag.Name);
WriteNumber(BitConverter.GetBytes(tag.Count));
@ -154,29 +151,31 @@ namespace SharpNBT
public void WriteList(ListTag tag)
{
BaseStream.WriteByte((byte) tag.Type);
if (named)
if (nameStack.Peek())
WriteString(tag.Name);
BaseStream.WriteByte((byte) tag.ChildType);
WriteNumber(BitConverter.GetBytes(tag.Count));
named = false;
nameStack.Push(false);
foreach (var child in tag)
WriteTag(child);
named = true;
nameStack.Pop();
}
public void WriteCompound(CompoundTag tag)
{
BaseStream.WriteByte((byte) tag.Type);
if (named)
if (nameStack.Peek())
WriteString(tag.Name);
nameStack.Push(true);
foreach (var child in tag)
{
child.Parent = tag;
WriteTag(child);
}
nameStack.Pop();
WriteEndTag();
}

View File

@ -13,21 +13,15 @@ namespace SharpNBT
public partial class NbtStream : Stream
{
protected readonly Stream BaseStream;
private readonly Stack<Tag> topLevel;
// /// <summary>
// /// Opens a <see cref="NbtStream"/> on the specified <paramref name="path"/> with read/write access.
// /// </summary>
// /// <param name="path">The path to a file to open.</param>
// /// <param name="mode">
// /// A value that specified whether a file is created if one does not exist, and determines
// /// whether the contents of an existing file are retained or overwritten.
// /// </param>
// /// <returns>A <see cref="NbtStream"/> opened in the specified mode and path, with read/write access.</returns>
// public static NbtStream Open(string path, FileMode mode) => new(File.Open(path, mode));
//
// public static NbtStream Open(string path, FileMode mode, FileAccess access) => new(File.Open(path, mode, access));
private readonly Stack<bool> nameStack;
/// <summary>
/// Opens a <see cref="NbtStream"/> on the specified <paramref name="path"/> with read access.
/// <para/>
/// GZip compressed files will be detected and handled automatically.
/// </summary>
/// <param name="path">The path to a file to open.</param>
///<returns>A <see cref="NbtStream"/> opened on the specified path with read access.</returns>
public static NbtStream OpenRead(string path)
{
var compressed = false;
@ -42,6 +36,14 @@ namespace SharpNBT
return compressed ? new NbtStream(File.OpenRead(path), CompressionMode.Decompress) : new NbtStream(File.OpenRead(path));
}
/// <summary>
/// Opens a <see cref="NbtStream"/> on the specified <paramref name="path"/> with write access.
/// <para/>
/// GZip compressed files will be detected and handled automatically.
/// </summary>
/// <param name="path">The path to a file to open.</param>
/// <param name="level">Specify a compression strategy to emphasise either size or speed, or no compression at all.</param>
/// <returns>A <see cref="NbtStream"/> opened on the specified path with write access.</returns>
public static NbtStream OpenWrite(string path, CompressionLevel level = CompressionLevel.NoCompression)
{
if (level != CompressionLevel.NoCompression)
@ -55,17 +57,14 @@ namespace SharpNBT
public NbtStream(Stream stream, bool leaveOpen = false)
{
BaseStream = stream ?? throw new ArgumentNullException(nameof(stream));
topLevel = new Stack<Tag>();
nameStack = new Stack<bool>();
nameStack.Push(true);
}
public NbtStream(Stream stream, CompressionMode compression, bool leaveOpen = false) : this(new GZipStream(stream, compression, leaveOpen), leaveOpen)
{
}
public NbtStream([NotNull] byte[] buffer) : this(new MemoryStream(buffer), false)
{
}
/// <summary>Clears all buffers for this stream and causes any buffered data to be written to the underlying device.</summary>
/// <exception cref="T:System.IO.IOException">An I/O error occurs.</exception>
/// <footer><a href="https://docs.microsoft.com/en-us/dotnet/api/System.IO.Stream.Flush?view=netcore-5.0">`Stream.Flush` on docs.microsoft.com</a></footer>

View File

@ -4,6 +4,16 @@
<TargetFramework>netstandard2.1</TargetFramework>
<Title>SharpNBT</Title>
<Authors>ForeverZer0</Authors>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<Description>A pure CLS-compliant C# implementation of the Named Binary Tag (NBT) format specification commonly used with Minecraft applications, allowing easy reading/writing streams and serialization to other formats.</Description>
<PackageProjectUrl>https://github.com/ForeverZer0/SharpNBT</PackageProjectUrl>
<RepositoryUrl>https://github.com/ForeverZer0/SharpNBT</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<PackageIcon>icon.png</PackageIcon>
<PackageTags>nbt;named binary tag;minecraft;serialization</PackageTags>
</PropertyGroup>
<ItemGroup>
<None Include="icon.png" Pack="true" Visible="true" PackagePath=""/>
</ItemGroup>
</Project>

View File

@ -34,6 +34,11 @@ namespace SharpNBT
[CanBeNull]
public string Name { get; set; }
/// <summary>
/// Initialized a new instance of the <see cref="Tag"/> class.
/// </summary>
/// <param name="type">A constant describing the NBT type for this tag.</param>
/// <param name="name">The name of the tag, or <see langword="null"/> if tag has no name.</param>
protected Tag(TagType type, [CanBeNull] string name)
{
Type = type;

BIN
SharpNBT/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 204 KiB