Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 16 additions & 12 deletions SharpNBT.Tests/StringifiedTest.cs
Original file line number Diff line number Diff line change
@@ -1,23 +1,13 @@
using SharpNBT.SNBT;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using SharpNBT.SNBT;
using Xunit;
using Xunit.Abstractions;

namespace SharpNBT.Tests
{
public class StringifiedTest
public class StringifiedTest(ITestOutputHelper output)
{
private readonly ITestOutputHelper output;


public StringifiedTest(ITestOutputHelper output)
{
this.output = output;
}

[Fact]
public void BigOutput()
{
Expand Down Expand Up @@ -58,5 +48,19 @@ public void ParseBig()
var tag = StringNbt.Parse(testString);
output.WriteLine(tag.PrettyPrinted());
}

[Fact]
public void ParseIntArrayWithNegativeNumbers()
{
const string testString = "{uuid:[I; 123, 0, 2147483647, -1]}";
var tag = StringNbt.Parse(testString);

var array = tag.Get<ArrayTag<int>>("uuid");
Assert.Equal(4, array.Count);
Assert.Equal(123, array[0]);
Assert.Equal(0, array[1]);
Assert.Equal(2147483647, array[2]);
Assert.Equal(-1, array[3]);
}
}
}
55 changes: 28 additions & 27 deletions SharpNBT/SNBT/StringNbt.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
using JetBrains.Annotations;
using System;
using System.Collections.Generic;
using System.Data;
using System.Globalization;
using System.Numerics;
using System.Text;
using JetBrains.Annotations;

namespace SharpNBT.SNBT;

Expand Down Expand Up @@ -55,7 +55,7 @@ public static CompoundTag Parse(ReadOnlySpan<byte> source, Encoding? encoding =
scanner.AssertChar('{');
return ParseCompound(null, ref scanner);
}

/// <summary>
/// Parse the given <paramref name="source"/> text into a <see cref="ListTag"/>.
/// </summary>
Expand All @@ -71,11 +71,11 @@ public static ListTag ParseList(ReadOnlySpan<byte> source, Encoding? encoding =
scanner.AssertChar('[');
return ParseList(null, ref scanner);
}

private static CompoundTag ParseCompound(string? name, ref Scanner scanner)
{
scanner.MoveNext(true, true);

// For the case of "{}", return empty compound tag.
var result = new CompoundTag(name);
if (scanner.Current == '}')
Expand All @@ -85,17 +85,17 @@ private static CompoundTag ParseCompound(string? name, ref Scanner scanner)
{
// Read the name of the tag
var childName = ParseString(ref scanner, out _);

// Move to and asser the next significant character is a deliminator.
scanner.MoveNext(true, true);
scanner.AssertChar(':');

// Move to and parse the tag value
scanner.MoveNext(true, true);
var tag = ParseTag(childName, ref scanner);
result.Add(tag);
scanner.MoveNext(true, true);

// Comma encountered, read another tag.
if (scanner.Current == ',')
{
Expand All @@ -109,7 +109,7 @@ private static CompoundTag ParseCompound(string? name, ref Scanner scanner)
// scanner.MoveNext(true, false);
break;
}

// Invalid character
scanner.SyntaxError($"Expected ',' or '}}', got '{scanner.Current}'.");
}
Expand Down Expand Up @@ -139,7 +139,7 @@ private static string ParseString(ref Scanner scanner, out bool quoted)
var escape = false;
var closed = false;
var sb = new StringBuilder();

while (scanner.MoveNext(false, false))
{
if (escape)
Expand Down Expand Up @@ -169,7 +169,7 @@ private static string ParseString(ref Scanner scanner, out bool quoted)
scanner.SyntaxError("Improperly terminated string.");
return sb.ToString();
}

private static string ParseUnquotedString(ref Scanner scanner)
{
var start = scanner.Position;
Expand Down Expand Up @@ -202,7 +202,7 @@ private static Tag ParseLiteral(string? name, ref Scanner scanner)
var value = ParseString(ref scanner, out var quoted);
if (quoted || value.Length == 0)
return new StringTag(name, value);

// Early out for true/false values
if (bool.TryParse(value, out var boolean))
return new ByteTag(name, boolean);
Expand All @@ -216,7 +216,7 @@ private static Tag ParseLiteral(string? name, ref Scanner scanner)

if (int.TryParse(value, NumberStyles.Integer, NumberFormatInfo.InvariantInfo, out var i32))
return new IntTag(name, i32);
}
}
else if (TryParseNumber(name, value, suffix, out var tag))
{
return tag;
Expand All @@ -228,17 +228,17 @@ private static Tag ParseLiteral(string? name, ref Scanner scanner)
if (int.TryParse(value[2..], NumberStyles.HexNumber, NumberFormatInfo.InvariantInfo, out var hex))
return new IntTag(name, hex);
}

// When all else fails, is could only have been an unquoted string
return new StringTag(name, value);
}

private static bool TryParseNumber(string? name, string value, char suffix, out Tag tag)
{
// A much less complicated char.ToLower()
if (suffix >= 'a')
suffix -= (char) 32;
suffix -= (char)32;

switch (suffix)
{
case 'B':
Expand Down Expand Up @@ -277,21 +277,21 @@ private static bool TryParseNumber(string? name, string value, char suffix, out
}
break;
}

tag = null!;
return false;
}

private static Tag ParseArray(string? name, ref Scanner scanner)
{
scanner.MoveNext(true, true);
if (scanner.Current == ']')
return new ListTag(name, TagType.End);

// No type-prefix, must be a ListTag
if (scanner.Peek() != ';')
if (scanner.Peek() != ';')
return ParseList(name, ref scanner);

// This is an array of integral values
var prefix = scanner.Current;
scanner.Position += 2;
Expand All @@ -311,9 +311,9 @@ private static ListTag ParseList(string? name, ref Scanner scanner)
{
var child = ParseTag(null, ref scanner);
list.Add(child);

scanner.MoveNext(true, true);

// Comma encountered, read another tag.
if (scanner.Current == ',')
{
Expand All @@ -326,7 +326,7 @@ private static ListTag ParseList(string? name, ref Scanner scanner)
{
break;
}

// Invalid character
scanner.SyntaxError($"Expected ',' or ']', got '{scanner.Current}'.");
}
Expand All @@ -349,22 +349,23 @@ private static T[] ParseArrayValues<T>(ref Scanner scanner) where T : INumber<T>
var c = char.ToLowerInvariant(scanner.Current);
if (c == ']')
break;
if (char.IsNumber(c) || c == ',')
if (char.IsNumber(c) || c == '-' || c == ',')
//if (char.IsNumber(c) || c == ',')
continue;
if (c is not ('b' or 'l'))
scanner.SyntaxError($"Invalid character '{c}' in integer array.");
}

var span = scanner.Source.Slice(start, scanner.Position - start);
var strings = new string(span).Split(SplitSeparators, SplitOpts);

var values = new T[strings.Length];
for (var i = 0; i < values.Length; i++)
values[i] = T.Parse(strings[i], NumberStyles.Integer, NumberFormatInfo.InvariantInfo);

return values;
}

private const StringSplitOptions SplitOpts = StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries;
private static readonly char[] SplitSeparators = new[] { ',', 'b', 'B', 'l', 'L' };
}
Loading