From b50d5ebbd61ba611b0b69e1a159c009ab3546236 Mon Sep 17 00:00:00 2001 From: Eric Freed Date: Sun, 27 Aug 2023 03:13:05 -0400 Subject: [PATCH] refactoring and optimization --- SharpNBT/TagBuilder.cs | 19 ++++- SharpNBT/TagWriter.cs | 14 ++-- SharpNBT/Tags/ArrayTag.cs | 138 ++++++++++++++++++++++++++++++++- SharpNBT/Tags/BoolTag.cs | 16 ++-- SharpNBT/Tags/ByteArrayTag.cs | 24 +++--- SharpNBT/Tags/ByteTag.cs | 12 ++- SharpNBT/Tags/CompoundTag.cs | 4 +- SharpNBT/Tags/DoubleTag.cs | 4 +- SharpNBT/Tags/EnumerableTag.cs | 10 +-- SharpNBT/Tags/FloatTag.cs | 4 +- SharpNBT/Tags/IntArrayTag.cs | 13 ++-- SharpNBT/Tags/IntTag.cs | 4 +- SharpNBT/Tags/ListTag.cs | 4 +- SharpNBT/Tags/LongArrayTag.cs | 20 ++--- SharpNBT/Tags/LongTag.cs | 4 +- SharpNBT/Tags/NumericTag.cs | 72 ++++++++++++++++- SharpNBT/Tags/ShortTag.cs | 4 +- SharpNBT/Tags/StringTag.cs | 68 ++++++++++++++-- SharpNBT/Tags/Tag.cs | 122 +++-------------------------- SharpNBT/Tags/TagType.cs | 1 + 20 files changed, 369 insertions(+), 188 deletions(-) diff --git a/SharpNBT/TagBuilder.cs b/SharpNBT/TagBuilder.cs index 71d5c90..f60c13a 100644 --- a/SharpNBT/TagBuilder.cs +++ b/SharpNBT/TagBuilder.cs @@ -42,7 +42,7 @@ public class TagBuilder /// The name of the node to add. /// The value of the tag. /// Returns this instance for chaining. - public TagBuilder AddBool(string? name, bool value) => AddTag(new BoolTag(name, value)); + public TagBuilder AddBool(string? name, bool value) => AddTag(new ByteTag(name, value)); /// /// Adds a new unnamed with the specified to the tree at the current depth. @@ -58,7 +58,10 @@ public class TagBuilder /// The value of the tag. /// Returns this instance for chaining. public TagBuilder AddByte(string? name, byte value) => AddTag(new ByteTag(name, value)); - + + /// + public TagBuilder AddByte(string? name, int value) => AddByte(name, unchecked((byte)(value & 0xFF))); + /// [CLSCompliant(false)] public TagBuilder AddByte(string? name, sbyte value) => AddByte(name, unchecked((byte)value)); @@ -70,6 +73,9 @@ public class TagBuilder /// Returns this instance for chaining. public TagBuilder AddByte(byte value) => AddByte(null, value); + /// + public TagBuilder AddByte(int value) => AddByte(null, unchecked((byte)(value & 0xFF))); + /// [CLSCompliant(false)] public TagBuilder AddByte(sbyte value) => AddByte(null, unchecked((byte)value)); @@ -82,6 +88,9 @@ public class TagBuilder /// Returns this instance for chaining. public TagBuilder AddShort(string? name, short value) => AddTag(new ShortTag(name, value)); + /// + public TagBuilder AddShort(string? name, int value) => AddShort(name, unchecked((short)(value & 0xFFFF))); + /// [CLSCompliant(false)] public TagBuilder AddShort(string? name, ushort value) => AddShort(name, unchecked((short)value)); @@ -93,6 +102,9 @@ public class TagBuilder /// Returns this instance for chaining. public TagBuilder AddShort(short value) => AddShort(null, value); + /// + public TagBuilder AddShort(int value) => AddShort(null, unchecked((short)(value & 0xFFFF))); + /// [CLSCompliant(false)] public TagBuilder AddShort(ushort value) => AddShort(null, unchecked((short)value)); @@ -236,8 +248,7 @@ public class TagBuilder /// [CLSCompliant(false)] public TagBuilder AddByteArray(IEnumerable values) => AddByteArray(null, values.ToArray()); - - + /// /// Adds a new with the specified to the tree at the current depth. /// diff --git a/SharpNBT/TagWriter.cs b/SharpNBT/TagWriter.cs index 9f05a99..67f6bef 100644 --- a/SharpNBT/TagWriter.cs +++ b/SharpNBT/TagWriter.cs @@ -118,7 +118,7 @@ public class TagWriter : TagIO public virtual void WriteByteArray(ByteArrayTag tag) { WriteTypeAndName(tag); - WriteCount(tag); + WriteCount(tag.Count); BaseStream.Write(tag.ToArray(), 0, tag.Count); } @@ -129,7 +129,7 @@ public class TagWriter : TagIO public virtual void WriteIntArray(IntArrayTag tag) { WriteTypeAndName(tag); - WriteCount(tag); + WriteCount(tag.Count); var values = new Span(tag.ToArray()); if (UseVarInt) @@ -155,7 +155,7 @@ public class TagWriter : TagIO { WriteTypeAndName(tag); - WriteCount(tag); + WriteCount(tag.Count); var values = new Span(tag.ToArray()); if (UseVarInt) @@ -182,7 +182,7 @@ public class TagWriter : TagIO { WriteTypeAndName(tag); BaseStream.WriteByte((byte) tag.ChildType); - WriteCount(tag); + WriteCount(tag.Count); foreach (var child in tag) WriteTag(child); @@ -377,11 +377,11 @@ public class TagWriter : TagIO return bytes; } - private void WriteCount(EnumerableTag tag) + private void WriteCount(int count) { if (UseVarInt) - VarInt.Write(BaseStream, tag.Count, ZigZagEncoding); + VarInt.Write(BaseStream, count, ZigZagEncoding); else - BaseStream.Write(GetBytes(tag.Count), 0, sizeof(int)); + BaseStream.Write(GetBytes(count), 0, sizeof(int)); } } \ No newline at end of file diff --git a/SharpNBT/Tags/ArrayTag.cs b/SharpNBT/Tags/ArrayTag.cs index c520689..71f3ec6 100644 --- a/SharpNBT/Tags/ArrayTag.cs +++ b/SharpNBT/Tags/ArrayTag.cs @@ -1,6 +1,142 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.Serialization; +using System.Text; +using JetBrains.Annotations; + namespace SharpNBT; -public class ArrayTag +/// +/// Base class for NBT tags that contain a fixed-size array of numeric types. +/// +/// A value type that implements . +[PublicAPI][Serializable] +public abstract class ArrayTag : Tag, IReadOnlyList where T : unmanaged, INumber { + /// + /// Gets a over the tag data. + /// + public Span Span => new(array); + + /// + /// Gets a over the tag data. + /// + public Memory Memory => new(array); + /// + /// The value of the tag. + // ReSharper disable InvalidXmlDocComment + protected ArrayTag(TagType type, string? name, T[] value) : base(type, name) + // ReSharper restore InvalidXmlDocComment + { + array = value; + } + + /// + protected ArrayTag(SerializationInfo info, StreamingContext context) : base(info, context) + { + var _ = info.GetInt32("count"); + var value = info.GetValue("values", typeof(T[])) as T[]; + array = value ?? Array.Empty(); + } + + /// + public override void GetObjectData(SerializationInfo info, StreamingContext context) + { + base.GetObjectData(info, context); + info.AddValue("count", array.Length); + info.AddValue("values", array); + } + + /// + public IEnumerator GetEnumerator() + { + // ReSharper disable ForCanBeConvertedToForeach + for (var i = 0; i < array.Length; i++) + yield return array[i]; + // ReSharper restore ForCanBeConvertedToForeach + } + + /// + IEnumerator IEnumerable.GetEnumerator() => array.GetEnumerator(); + + /// + // ReSharper disable once ParameterHidesMember + public void CopyTo(T[] array, int arrayIndex) => this.array.CopyTo(array, arrayIndex); + + /// + public int Count => array.Length; + + /// + public int IndexOf(T item) + { + for (var i = 0; i < array.Length; i++) + { + if (array[i] == item) + return i; + } + + return -1; + } + + /// + /// This method being defined provides Range indexers for the class. + public Span Slice(int start, int length) => new(array, start, length); + + /// + public T this[int index] + { + get => array[index]; + set => array[index] = value; + } + + /// + /// Returns a reference to the underlying memory of this object that is be pinned using the + /// statement. + /// + /// A reference to the first value in the underlying array. + public ref T GetPinnableReference() => ref array[0] ; + + private protected string Stringify(char prefix, char? suffix) + { + var sb = new StringBuilder(32 + array.Length * 4); + sb.Append($"{StringifyName}:[{prefix};"); + + for (var i = 0; i < array.Length; i++) + { + if (i > 0) + sb.Append(','); + sb.Append(array[i]); + if (suffix != null) + sb.Append(suffix.Value); + } + sb.Append(']'); + return sb.ToString(); + } + + /// + /// Implicit conversion of a an to an array of . + /// + /// The to be converted. + /// The value of as an array of . + public static implicit operator T[](ArrayTag tag) => tag.array; + + /// + /// Implicit conversion of a an to a . + /// + /// The to be converted. + /// The value of as a . + public static implicit operator Span(ArrayTag tag) => new(tag.array); + + /// + /// Implicit conversion of a an to a . + /// + /// The to be converted. + /// The value of as a . + public static implicit operator Memory(ArrayTag tag) => new(tag.array); + + private readonly T[] array; } \ No newline at end of file diff --git a/SharpNBT/Tags/BoolTag.cs b/SharpNBT/Tags/BoolTag.cs index 2e98395..2bf0591 100644 --- a/SharpNBT/Tags/BoolTag.cs +++ b/SharpNBT/Tags/BoolTag.cs @@ -12,19 +12,19 @@ namespace SharpNBT; /// actually serialized as. /// [PublicAPI][Serializable] -[Obsolete("Use the IsBool and Bool properties of ByteTag. This class will be removed in a future release.")] -public class BoolTag : Tag +[Obsolete("Use the IsBool and Bool properties of ByteTag. This class will be removed in a future version.")] +public class BoolTag : Tag { - private const string TRUE = "true"; - private const string FALSE = "false"; - + public bool Value { get; set; } + /// /// Creates a new instance of the class with the specified . /// /// The name of the tag, or if tag has no name. /// The value to assign to this tag. - public BoolTag(string? name, bool value) : base(TagType.Byte, name, value) + public BoolTag(string? name, bool value) : base(TagType.Byte, name) { + Value = value; } /// @@ -37,7 +37,7 @@ public class BoolTag : Tag } /// - public override string ToString() => $"TAG_Bool({PrettyName}): {(Value ? TRUE : FALSE)}"; + public override string ToString() => $"TAG_Byte({PrettyName}): {(Value ? "true" : "false")}"; /// /// Implicit conversion of this tag to a . @@ -51,6 +51,6 @@ public class BoolTag : Tag /// /// This NBT tag in SNBT format. /// - public override string Stringify() => $"{StringifyName}{(Value ? TRUE : FALSE)}"; + public override string Stringify() => $"{StringifyName}:{(Value ? "true" : "false")}"; } \ No newline at end of file diff --git a/SharpNBT/Tags/ByteArrayTag.cs b/SharpNBT/Tags/ByteArrayTag.cs index e964df2..0d27a75 100644 --- a/SharpNBT/Tags/ByteArrayTag.cs +++ b/SharpNBT/Tags/ByteArrayTag.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Runtime.Serialization; using JetBrains.Annotations; @@ -13,8 +14,17 @@ namespace SharpNBT; /// the bits are equivalent for your values. /// [PublicAPI][Serializable] -public class ByteArrayTag : EnumerableTag +public class ByteArrayTag : ArrayTag { + /// + /// Initializes a new instance of the . + /// + /// The name of the tag, or if tag has no name. + /// The capacity of the array. + public ByteArrayTag(string? name, int capacity) : base(TagType.IntArray, name, new byte[capacity]) + { + } + /// /// Initializes a new instance of the . /// @@ -29,7 +39,7 @@ public class ByteArrayTag : EnumerableTag /// /// The name of the tag, or if tag has no name. /// A collection of values to include in this tag. - public ByteArrayTag(string? name, IEnumerable values) : base(TagType.ByteArray, name, values) + public ByteArrayTag(string? name, IEnumerable values) : base(TagType.ByteArray, name, values.ToArray()) { } @@ -38,7 +48,7 @@ public class ByteArrayTag : EnumerableTag /// /// The name of the tag, or if tag has no name. /// A collection of values to include in this tag. - public ByteArrayTag(string? name, ReadOnlySpan values) : base(TagType.ByteArray, name, values) + public ByteArrayTag(string? name, ReadOnlySpan values) : base(TagType.ByteArray, name, values.ToArray()) { } @@ -63,11 +73,5 @@ public class ByteArrayTag : EnumerableTag /// /// This NBT tag in SNBT format. /// - public override string Stringify() - { - var values = new string[Count]; - for (var i = 0; i < Count; i++) - values[i] = $"{this[i]}b"; - return $"{StringifyName}[B;{string.Join(',', values)}]"; - } + public override string Stringify() => Stringify('B', 'b'); } \ No newline at end of file diff --git a/SharpNBT/Tags/ByteTag.cs b/SharpNBT/Tags/ByteTag.cs index c70124d..6c1360b 100644 --- a/SharpNBT/Tags/ByteTag.cs +++ b/SharpNBT/Tags/ByteTag.cs @@ -13,7 +13,7 @@ namespace SharpNBT; /// equivalent. /// [PublicAPI][Serializable] -public class ByteTag : Tag +public class ByteTag : NumericTag { /// /// Gets a flag indicating if this was assigned a value. @@ -95,9 +95,13 @@ public class ByteTag : Tag protected ByteTag(SerializationInfo info, StreamingContext context) : base(info, context) { } - + /// - public override string ToString() => $"TAG_Byte({PrettyName}): {Value}"; + public override string ToString() + { + object obj = IsBool ? Bool : Value; + return $"TAG_Byte({PrettyName}): {obj}"; + } /// /// Implicit conversion of this tag to a . @@ -126,5 +130,5 @@ public class ByteTag : Tag /// /// This NBT tag in SNBT format. /// - public override string Stringify() => $"{StringifyName}{Value}B"; + public override string Stringify() => $"{StringifyName}:{Value}B"; } \ No newline at end of file diff --git a/SharpNBT/Tags/CompoundTag.cs b/SharpNBT/Tags/CompoundTag.cs index b3f2941..ad6f90c 100644 --- a/SharpNBT/Tags/CompoundTag.cs +++ b/SharpNBT/Tags/CompoundTag.cs @@ -122,7 +122,9 @@ public class CompoundTag : TagContainer for (var i = 0; i < strings.Length; i++) strings[i] = this[i].Stringify(); - return $"{StringifyName}{{{string.Join(',', strings)}}}"; + // TODO: Use StringBuilder + + return $"{StringifyName}:{{{string.Join(',', strings)}}}"; } /// diff --git a/SharpNBT/Tags/DoubleTag.cs b/SharpNBT/Tags/DoubleTag.cs index 2dde1be..08559f9 100644 --- a/SharpNBT/Tags/DoubleTag.cs +++ b/SharpNBT/Tags/DoubleTag.cs @@ -8,7 +8,7 @@ namespace SharpNBT; /// A tag that contains a single IEEE-754 double-precision floating point number. /// [PublicAPI][Serializable] -public class DoubleTag : Tag +public class DoubleTag : NumericTag { /// /// Creates a new instance of the class with the specified . @@ -43,5 +43,5 @@ public class DoubleTag : Tag /// /// This NBT tag in SNBT format. /// - public override string Stringify() => $"{StringifyName}{Value:0.0}D"; + public override string Stringify() => $"{StringifyName}:{Value:0.0}D"; } \ No newline at end of file diff --git a/SharpNBT/Tags/EnumerableTag.cs b/SharpNBT/Tags/EnumerableTag.cs index c54b89a..3ba7e64 100644 --- a/SharpNBT/Tags/EnumerableTag.cs +++ b/SharpNBT/Tags/EnumerableTag.cs @@ -1,7 +1,6 @@ using System; using System.Collections; using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; using System.Runtime.Serialization; using System.Text; using JetBrains.Annotations; @@ -72,7 +71,9 @@ public abstract class EnumerableTag : Tag, IList protected EnumerableTag(SerializationInfo info, StreamingContext context) : base(info, context) { var dummy = info.GetInt32("count"); - internalList.AddRange((T[]) info.GetValue("values", typeof(T[]))); + var obj = info.GetValue("values", typeof(T[])) as T[]; + if (obj is IEnumerable e) + internalList.AddRange(e); } /// Populates a with the data needed to serialize the target object. @@ -101,7 +102,7 @@ public abstract class EnumerableTag : Tag, IList /// The is read-only. /// [SuppressMessage("ReSharper", "AnnotationConflictInHierarchy")] - public virtual void Add([DisallowNull] T item) => internalList.Add(item); + public virtual void Add(T item) => internalList.Add(item); /// /// Adds the elements of the specified collection to the . @@ -121,7 +122,7 @@ public abstract class EnumerableTag : Tag, IList /// The is read-only. /// [SuppressMessage("ReSharper", "AnnotationConflictInHierarchy")] - public virtual void Insert(int index, [DisallowNull] T item) => internalList.Insert(index, item); + public virtual void Insert(int index, T item) => internalList.Insert(index, item); /// Gets or sets the element at the specified index. /// The zero-based index of the element to get or set. @@ -130,7 +131,6 @@ public abstract class EnumerableTag : Tag, IList /// The property is set and the is read-only. /// The element at the specified index. /// - [DisallowNull] public virtual T this[int index] { get => internalList[index]; diff --git a/SharpNBT/Tags/FloatTag.cs b/SharpNBT/Tags/FloatTag.cs index b7319a7..813d8b5 100644 --- a/SharpNBT/Tags/FloatTag.cs +++ b/SharpNBT/Tags/FloatTag.cs @@ -8,7 +8,7 @@ namespace SharpNBT; /// A tag that contains a single IEEE-754 single-precision floating point number. /// [PublicAPI][Serializable] -public class FloatTag : Tag +public class FloatTag : NumericTag { /// /// Creates a new instance of the class with the specified . @@ -42,5 +42,5 @@ public class FloatTag : Tag /// /// This NBT tag in SNBT format. /// - public override string Stringify() => $"{StringifyName}{Value:0.0}F"; + public override string Stringify() => $"{StringifyName}:{Value:0.0}F"; } \ No newline at end of file diff --git a/SharpNBT/Tags/IntArrayTag.cs b/SharpNBT/Tags/IntArrayTag.cs index 33a1382..fc9b965 100644 --- a/SharpNBT/Tags/IntArrayTag.cs +++ b/SharpNBT/Tags/IntArrayTag.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Runtime.Serialization; using JetBrains.Annotations; @@ -9,15 +10,17 @@ namespace SharpNBT; /// A tag that whose value is a contiguous sequence of 32-bit integers. /// [PublicAPI][Serializable] -public class IntArrayTag : EnumerableTag +public class IntArrayTag : ArrayTag { /// /// Initializes a new instance of the . /// /// The name of the tag, or if tag has no name. - public IntArrayTag(string? name) : base(TagType.IntArray, name) + /// The capacity of the array. + public IntArrayTag(string? name, int capacity) : base(TagType.IntArray, name, new int[capacity]) { } + /// /// Initializes a new instance of the with the specified . /// @@ -33,7 +36,7 @@ public class IntArrayTag : EnumerableTag /// /// The name of the tag, or if tag has no name. /// A collection of values to include in this tag. - public IntArrayTag(string? name, IEnumerable values) : base(TagType.IntArray, name, values) + public IntArrayTag(string? name, IEnumerable values) : base(TagType.IntArray, name, values.ToArray()) { } @@ -42,7 +45,7 @@ public class IntArrayTag : EnumerableTag /// /// The name of the tag, or if tag has no name. /// A collection of values to include in this tag. - public IntArrayTag(string? name, ReadOnlySpan values) : base(TagType.IntArray, name, values) + public IntArrayTag(string? name, ReadOnlySpan values) : base(TagType.IntArray, name, values.ToArray()) { } @@ -67,5 +70,5 @@ public class IntArrayTag : EnumerableTag /// /// This NBT tag in SNBT format. /// - public override string Stringify() => $"{StringifyName}[I;{string.Join(',', this)}]"; + public override string Stringify() => Stringify('I', null); } \ No newline at end of file diff --git a/SharpNBT/Tags/IntTag.cs b/SharpNBT/Tags/IntTag.cs index aa49a3c..a62a117 100644 --- a/SharpNBT/Tags/IntTag.cs +++ b/SharpNBT/Tags/IntTag.cs @@ -8,7 +8,7 @@ namespace SharpNBT; /// A tag that contains a single 32-bit integer value. /// [PublicAPI][Serializable] -public class IntTag : Tag +public class IntTag : NumericTag { /// /// Gets or sets the value of this tag as an unsigned value. @@ -70,5 +70,5 @@ public class IntTag : Tag /// /// This NBT tag in SNBT format. /// - public override string Stringify() => $"{StringifyName}{Value}"; + public override string Stringify() => $"{StringifyName}:{Value}"; } \ No newline at end of file diff --git a/SharpNBT/Tags/ListTag.cs b/SharpNBT/Tags/ListTag.cs index faa9205..a6a12c1 100644 --- a/SharpNBT/Tags/ListTag.cs +++ b/SharpNBT/Tags/ListTag.cs @@ -96,6 +96,8 @@ public class ListTag : TagContainer for (var i = 0; i < strings.Length; i++) strings[i] = this[i].Stringify(); - return $"{StringifyName}[{string.Join(',', strings)}]"; + // TODO: Use StringBuilder + + return $"{StringifyName}:[{string.Join(',', strings)}]"; } } \ No newline at end of file diff --git a/SharpNBT/Tags/LongArrayTag.cs b/SharpNBT/Tags/LongArrayTag.cs index 4c249ec..609e4da 100644 --- a/SharpNBT/Tags/LongArrayTag.cs +++ b/SharpNBT/Tags/LongArrayTag.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Runtime.Serialization; using JetBrains.Annotations; @@ -9,13 +10,14 @@ namespace SharpNBT; /// A tag that whose value is a contiguous sequence of 64-bit integers. /// [PublicAPI][Serializable] -public class LongArrayTag : EnumerableTag +public class LongArrayTag : ArrayTag { /// /// Initializes a new instance of the . /// /// The name of the tag, or if tag has no name. - public LongArrayTag(string? name) : base(TagType.LongArray, name) + /// The capacity of the array. + public LongArrayTag(string? name, int capacity) : base(TagType.LongArray, name, new long[capacity]) { } /// @@ -23,7 +25,7 @@ public class LongArrayTag : EnumerableTag /// /// The name of the tag, or if tag has no name. /// A collection of values to include in this tag. - public LongArrayTag(string? name, long[] values) : base(TagType.LongArray, name, values) + public LongArrayTag(string? name, long[] values) : base(TagType.LongArray, name, values.ToArray()) { } @@ -41,7 +43,7 @@ public class LongArrayTag : EnumerableTag /// /// The name of the tag, or if tag has no name. /// A collection of values to include in this tag. - public LongArrayTag(string? name, IEnumerable values) : base(TagType.LongArray, name, values) + public LongArrayTag(string? name, IEnumerable values) : base(TagType.LongArray, name, values.ToArray()) { } @@ -50,7 +52,7 @@ public class LongArrayTag : EnumerableTag /// /// The name of the tag, or if tag has no name. /// A collection of values to include in this tag. - public LongArrayTag(string? name, ReadOnlySpan values) : base(TagType.LongArray, name, values) + public LongArrayTag(string? name, ReadOnlySpan values) : base(TagType.LongArray, name, values.ToArray()) { } @@ -66,11 +68,5 @@ public class LongArrayTag : EnumerableTag /// /// This NBT tag in SNBT format. /// - public override string Stringify() - { - var values = new string[Count]; - for (var i = 0; i < Count; i++) - values[i] = $"{this[i]}l"; - return $"{StringifyName}[L;{string.Join(',', values)}]"; - } + public override string Stringify() => Stringify('L', 'l'); } \ No newline at end of file diff --git a/SharpNBT/Tags/LongTag.cs b/SharpNBT/Tags/LongTag.cs index 978062e..c118130 100644 --- a/SharpNBT/Tags/LongTag.cs +++ b/SharpNBT/Tags/LongTag.cs @@ -8,7 +8,7 @@ namespace SharpNBT; /// A tag that contains a single 64-bit integer value. /// [PublicAPI][Serializable] -public class LongTag : Tag +public class LongTag : NumericTag { /// /// Gets or sets the value of this tag as an unsigned value. @@ -70,5 +70,5 @@ public class LongTag : Tag /// /// This NBT tag in SNBT format. /// - public override string Stringify() => $"{StringifyName}{Value}L"; + public override string Stringify() => $"{StringifyName}:{Value}L"; } \ No newline at end of file diff --git a/SharpNBT/Tags/NumericTag.cs b/SharpNBT/Tags/NumericTag.cs index a97f1f0..22d023a 100644 --- a/SharpNBT/Tags/NumericTag.cs +++ b/SharpNBT/Tags/NumericTag.cs @@ -13,19 +13,32 @@ namespace SharpNBT; [PublicAPI][Serializable] public abstract class NumericTag : Tag, IEquatable>, IComparable>, IComparable where T : unmanaged, INumber { - public T Value { get; set; } + /// + /// Gets or sets the value of the tag. + /// + public T Value { get; [Obsolete("Numeric tag types will be made immutable in a future version.")] set; } + /// protected NumericTag(TagType type, string? name, T value) : base(type, name) { Value = value; } + /// protected NumericTag(SerializationInfo info, StreamingContext context) : base(info, context) { var value = info.GetValue("value", typeof(T)); Value = value is null ? default : (T)value; } + + /// + public override void GetObjectData(SerializationInfo info, StreamingContext context) + { + base.GetObjectData(info, context); + info.AddValue("value", Value, typeof(T)); + } + /// public bool Equals(NumericTag? other) { if (ReferenceEquals(null, other)) return false; @@ -33,6 +46,7 @@ public abstract class NumericTag : Tag, IEquatable>, IComparabl return base.Equals(other) && Value.Equals(other.Value); } + /// public override bool Equals(object? obj) { if (ReferenceEquals(null, obj)) return false; @@ -41,8 +55,10 @@ public abstract class NumericTag : Tag, IEquatable>, IComparabl return Equals((NumericTag)obj); } + /// public override int GetHashCode() => base.GetHashCode(); + /// public int CompareTo(NumericTag? other) { if (ReferenceEquals(this, other)) return 0; @@ -50,6 +66,7 @@ public abstract class NumericTag : Tag, IEquatable>, IComparabl return Value.CompareTo(other.Value); } + /// public int CompareTo(object? obj) { if (ReferenceEquals(null, obj)) return 1; @@ -57,27 +74,80 @@ public abstract class NumericTag : Tag, IEquatable>, IComparabl return obj is NumericTag other ? CompareTo(other) : throw new ArgumentException($"Object must be of type {nameof(NumericTag)}"); } + /// + /// Compares two values to determine equality. + /// + /// The value to compare with . + /// The value to compare with . + /// + /// if is equal to ; otherwise, + /// . + /// public static bool operator ==(NumericTag? left, NumericTag? right) => Equals(left, right); + /// + /// Compares two values to determine inequality. + /// + /// The value to compare with . + /// The value to compare with . + /// + /// if is not equal to ; otherwise, + /// . + /// public static bool operator !=(NumericTag? left, NumericTag? right) => !Equals(left, right); + /// Compares two values to determine which is less. + /// The value to compare with . + /// The value to compare with . + /// + /// if is less than ; otherwise, + /// . + /// public static bool operator <(NumericTag? left, NumericTag? right) { return Comparer>.Default.Compare(left, right) < 0; } + /// Compares two values to determine which is greater. + /// The value to compare with . + /// The value to compare with . + /// + /// if is greater than ; otherwise, + /// . + /// public static bool operator >(NumericTag? left, NumericTag? right) { return Comparer>.Default.Compare(left, right) > 0; } + /// Compares two values to determine which is less or equal. + /// The value to compare with . + /// The value to compare with . + /// + /// if is less than or equal to ; + /// otherwise, . + /// public static bool operator <=(NumericTag? left, NumericTag? right) { return Comparer>.Default.Compare(left, right) <= 0; } + /// Compares two values to determine which is greater or equal. + /// The value to compare with . + /// The value to compare with . + /// + /// if is greater than or equal to ; + /// otherwise, . + /// public static bool operator >=(NumericTag? left, NumericTag? right) { return Comparer>.Default.Compare(left, right) >= 0; } + + /// + /// Implicit conversion of a an to a . + /// + /// The to be converted. + /// The value of as a . + public static implicit operator T(NumericTag tag) => tag.Value; } \ No newline at end of file diff --git a/SharpNBT/Tags/ShortTag.cs b/SharpNBT/Tags/ShortTag.cs index 6cb95d7..7a6a1ae 100644 --- a/SharpNBT/Tags/ShortTag.cs +++ b/SharpNBT/Tags/ShortTag.cs @@ -8,7 +8,7 @@ namespace SharpNBT; /// A tag that contains a single 16-bit integer value. /// [PublicAPI][Serializable] -public class ShortTag : Tag +public class ShortTag : NumericTag { /// /// Gets or sets the value of this tag as an unsigned value. @@ -76,5 +76,5 @@ public class ShortTag : Tag /// /// This NBT tag in SNBT format. /// - public override string Stringify() => $"{StringifyName}{Value}S"; + public override string Stringify() => $"{StringifyName}:{Value}S"; } \ No newline at end of file diff --git a/SharpNBT/Tags/StringTag.cs b/SharpNBT/Tags/StringTag.cs index 8c93a07..7e5c05c 100644 --- a/SharpNBT/Tags/StringTag.cs +++ b/SharpNBT/Tags/StringTag.cs @@ -1,4 +1,5 @@ using System; +using System.Numerics; using System.Runtime.Serialization; using JetBrains.Annotations; @@ -8,26 +9,36 @@ namespace SharpNBT; /// A tag the contains a UTF-8 string. /// [PublicAPI][Serializable] -public class StringTag : Tag +public class StringTag : Tag, IEquatable { + /// + /// Gets or sets the value of the tag. + /// + public string Value { get; [Obsolete("String tag type will be made immutable in a future version.")] set; } + /// /// Creates a new instance of the class with the specified . /// /// The name of the tag, or if tag has no name. /// The value to assign to this tag. - public StringTag(string? name, string? value) : base(TagType.String, name, value) + public StringTag(string? name, string? value) : base(TagType.String, name) { + Value = value ?? string.Empty; } - /// - /// Required constructor for ISerializable implementation. - /// - /// The to describing this instance. - /// The destination (see ) for this serialization. + /// protected StringTag(SerializationInfo info, StreamingContext context) : base(info, context) { + Value = info.GetString("value") ?? string.Empty; } + /// + public override void GetObjectData(SerializationInfo info, StreamingContext context) + { + base.GetObjectData(info, context); + info.AddValue("value", Value); + } + /// public override string ToString() => $"TAG_String({PrettyName}): \"{Value}\""; @@ -43,5 +54,46 @@ public class StringTag : Tag /// /// This NBT tag in SNBT format. /// - public override string Stringify() => $"{StringifyName}\"{Value}\""; + public override string Stringify() => $"{StringifyName}:\"{Value}\""; // TODO: Does this get properly escaped? + + /// + public bool Equals(StringTag? other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return base.Equals(other) && string.CompareOrdinal(Value, other.Value) == 0; + } + + /// + public override bool Equals(object? obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + return obj.GetType() == GetType() && Equals((StringTag)obj); + } + + /// + public override int GetHashCode() => base.GetHashCode(); // TODO: Add Value once immutable + + /// + /// Compares two values to determine equality. + /// + /// The value to compare with . + /// The value to compare with . + /// + /// if is equal to ; otherwise, + /// . + /// + public static bool operator ==(StringTag? left, StringTag? right) => Equals(left, right); + + /// + /// Compares two values to determine inequality. + /// + /// The value to compare with . + /// The value to compare with . + /// + /// if is not equal to ; otherwise, + /// . + /// + public static bool operator !=(StringTag? left, StringTag? right) => !Equals(left, right); } \ No newline at end of file diff --git a/SharpNBT/Tags/Tag.cs b/SharpNBT/Tags/Tag.cs index 2773778..fb4984f 100644 --- a/SharpNBT/Tags/Tag.cs +++ b/SharpNBT/Tags/Tag.cs @@ -32,7 +32,8 @@ public abstract class Tag : IEquatable, ISerializable, ICloneable return new[] { typeof(TagType), - typeof(Tag<>), + typeof(NumericTag<>), + typeof(ArrayTag<>), typeof(Tag[]), typeof(EnumerableTag<>), typeof(TagContainer), @@ -81,15 +82,20 @@ public abstract class Tag : IEquatable, ISerializable, ICloneable Type = type; Name = name; } - + /// /// Writes this tag as a formatted string to the given . /// /// A instance to write to. /// The current indent depth to write at. /// The string to use for indents. - protected internal abstract void PrettyPrinted(StringBuilder buffer, int level, string indent); - + protected internal virtual void PrettyPrinted(StringBuilder buffer, int level, string indent) + { + for (var i = 0; i < level; i++) + buffer.Append(indent); + buffer.AppendLine(ToString()); + } + /// /// Gets the name of the object as a human-readable quoted string, or a default name to indicate it has no name when applicable. /// @@ -234,113 +240,7 @@ public abstract class Tag : IEquatable, ISerializable, ICloneable { if (string.IsNullOrEmpty(Name)) return string.Empty; - return simpleNameMatcher.IsMatch(Name) ? $"{Name}: " : $"\"{Name}\": "; + return simpleNameMatcher.IsMatch(Name) ? Name : $"\"{Name}\""; } } -} - -/// -/// Abstract base class for types that contain a single primitive value. -/// -/// The type of the value the tag represents. -[PublicAPI][Serializable] -public abstract class Tag : Tag, IEquatable> -{ - /// - /// Gets or sets the value of the tag. - /// - public T Value { get; set; } - - /// - /// Required constructor for ISerializable implementation. - /// - /// The to describing this instance. - /// The destination (see ) for this serialization. - protected Tag(SerializationInfo info, StreamingContext context) : base(info, context) - { - Value = (T)info.GetValue("value", typeof(T)); - } - - /// Populates a with the data needed to serialize the target object. - /// The to populate with data. - /// The destination (see ) for this serialization. - /// The caller does not have the required permission. - public override void GetObjectData(SerializationInfo info, StreamingContext context) - { - base.GetObjectData(info, context); - info.AddValue("value", Value, typeof(T)); - } - - /// - /// Creates a new instance of the class with the specified . - /// - /// A constant describing the NBT type for this tag. - /// The name of the tag, or if tag has no name. - /// The value to assign to this tag. - protected Tag(TagType type, string? name, T value) : base(type, name) - { - Value = value; - } - - /// - protected internal override void PrettyPrinted(StringBuilder buffer, int level, string indent) - { - for (var i = 0; i < level; i++) - buffer.Append(indent); - buffer.AppendLine(ToString()); - } - - /// Indicates whether the current object is equal to another object of the same type. - /// An object to compare with this object. - /// - /// if the current object is equal to the parameter; otherwise, . - /// - public bool Equals(Tag? other) - { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; - return base.Equals(other) && EqualityComparer.Default.Equals(Value, other.Value); - } - - /// Determines whether the specified object is equal to the current object. - /// The object to compare with the current object. - /// - /// if the specified object is equal to the current object; otherwise, . - /// - public override bool Equals(object? obj) - { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != GetType()) return false; - return Equals((Tag)obj); - } - - /// Serves as the default hash function. - /// A hash code for the current object. - /// - public override int GetHashCode() - { - unchecked - { - // ReSharper disable NonReadonlyMemberInGetHashCode - return (base.GetHashCode() * 421) ^ EqualityComparer.Default.GetHashCode(Value); - // ReSharper restore NonReadonlyMemberInGetHashCode - } - } - - /// - /// Tests for equality of this object with another instance. - /// - /// First value to compare. - /// Second value to compare. - /// Result of comparison. - public static bool operator ==(Tag left, Tag right) => Equals(left, right); - - /// - /// Tests for inequality of this object with another instance. - /// - /// First value to compare. - /// Second value to compare. - /// Result of comparison. - public static bool operator !=(Tag left, Tag right) => !Equals(left, right); } \ No newline at end of file diff --git a/SharpNBT/Tags/TagType.cs b/SharpNBT/Tags/TagType.cs index 285322b..0fdbc9e 100644 --- a/SharpNBT/Tags/TagType.cs +++ b/SharpNBT/Tags/TagType.cs @@ -12,6 +12,7 @@ public enum TagType : byte /// /// Signifies the end of a . /// + /// Some implementation may also use as the child type for an empty list. End = 0x00, ///