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

View File

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

View File

@ -13,21 +13,15 @@ namespace SharpNBT
public partial class NbtStream : Stream public partial class NbtStream : Stream
{ {
protected readonly Stream BaseStream; protected readonly Stream BaseStream;
private readonly Stack<Tag> topLevel; private readonly Stack<bool> nameStack;
// /// <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));
/// <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) public static NbtStream OpenRead(string path)
{ {
var compressed = false; var compressed = false;
@ -42,6 +36,14 @@ namespace SharpNBT
return compressed ? new NbtStream(File.OpenRead(path), CompressionMode.Decompress) : new NbtStream(File.OpenRead(path)); 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) public static NbtStream OpenWrite(string path, CompressionLevel level = CompressionLevel.NoCompression)
{ {
if (level != CompressionLevel.NoCompression) if (level != CompressionLevel.NoCompression)
@ -55,17 +57,14 @@ namespace SharpNBT
public NbtStream(Stream stream, bool leaveOpen = false) public NbtStream(Stream stream, bool leaveOpen = false)
{ {
BaseStream = stream ?? throw new ArgumentNullException(nameof(stream)); 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(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> /// <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> /// <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> /// <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> <TargetFramework>netstandard2.1</TargetFramework>
<Title>SharpNBT</Title> <Title>SharpNBT</Title>
<Authors>ForeverZer0</Authors> <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> </PropertyGroup>
<ItemGroup>
<None Include="icon.png" Pack="true" Visible="true" PackagePath=""/>
</ItemGroup>
</Project> </Project>

View File

@ -34,6 +34,11 @@ namespace SharpNBT
[CanBeNull] [CanBeNull]
public string Name { get; set; } 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) protected Tag(TagType type, [CanBeNull] string name)
{ {
Type = type; Type = type;

BIN
SharpNBT/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 204 KiB