diff --git a/SharpNBT/BufferedTagWriter.cs b/SharpNBT/BufferedTagWriter.cs index 99564e1..b3be8f8 100644 --- a/SharpNBT/BufferedTagWriter.cs +++ b/SharpNBT/BufferedTagWriter.cs @@ -3,116 +3,114 @@ using System.IO.Compression; using System.Threading.Tasks; using JetBrains.Annotations; -using SharpNBT.ZLib; -namespace SharpNBT +namespace SharpNBT; + +/// +/// Provides a object that writes to an internal buffer instead of a object, which then can be retrieved as +/// an array of bytes or written directly to a stream. This is especially convenient when creating packets to be sent over a network, where the size of +/// the packet must be pre-determined before sending. +/// +[PublicAPI] +public class BufferedTagWriter : TagWriter { + private readonly MemoryStream buffer; + /// - /// Provides a object that writes to an internal buffer instead of a object, which then can be retrieved as - /// an array of bytes or written directly to a stream. This is especially convenient when creating packets to be sent over a network, where the size of - /// the packet must be pre-determined before sending. + /// Creates a new instance of the class. /// - [PublicAPI] - public class BufferedTagWriter : TagWriter + /// Indicates the compression algorithm used to compress the file. + /// Bitwise flags to configure how data should be handled for compatibility between different specifications. + /// A newly created instance. + /// Thrown when an invalid compression type is specified. + public static BufferedTagWriter Create(CompressionType compression, FormatOptions options) { - private readonly MemoryStream buffer; - - /// - /// Creates a new instance of the class. - /// - /// Indicates the compression algorithm used to compress the file. - /// Bitwise flags to configure how data should be handled for compatibility between different specifications. - /// A newly created instance. - /// Thrown when an invalid compression type is specified. - public static BufferedTagWriter Create(CompressionType compression, FormatOptions options) - { - // ReSharper disable once IntroduceOptionalParameters.Global - return Create(compression, options, 4096); - } + // ReSharper disable once IntroduceOptionalParameters.Global + return Create(compression, options, 4096); + } - /// - /// Creates a new instance of the class. - /// - /// Indicates the compression algorithm used to compress the file. - /// Bitwise flags to configure how data should be handled for compatibility between different specifications. - /// The initial capacity of the buffer. - /// A newly created instance. - /// Thrown when an invalid compression type is specified. - public static BufferedTagWriter Create(CompressionType compression, FormatOptions options, int capacity) + /// + /// Creates a new instance of the class. + /// + /// Indicates the compression algorithm used to compress the file. + /// Bitwise flags to configure how data should be handled for compatibility between different specifications. + /// The initial capacity of the buffer. + /// A newly created instance. + /// Thrown when an invalid compression type is specified. + public static BufferedTagWriter Create(CompressionType compression, FormatOptions options, int capacity) + { + var buffer = new MemoryStream(capacity); + Stream stream = compression switch { - var buffer = new MemoryStream(capacity); - Stream stream = compression switch - { - CompressionType.None => buffer, - CompressionType.GZip => new GZipStream(buffer, CompressionMode.Compress, false), - CompressionType.ZLib => new ZLibStream(buffer, CompressionMode.Compress), - _ => throw new ArgumentOutOfRangeException(nameof(compression), compression, null) - }; + CompressionType.None => buffer, + CompressionType.GZip => new GZipStream(buffer, CompressionMode.Compress, false), + CompressionType.ZLib => new ZLibStream(buffer, CompressionMode.Compress), + _ => throw new ArgumentOutOfRangeException(nameof(compression), compression, null) + }; - return new BufferedTagWriter(stream, buffer, options); - } + return new BufferedTagWriter(stream, buffer, options); + } - private BufferedTagWriter([NotNull] Stream stream, MemoryStream buffer, FormatOptions options) : base(stream, options, false) - { - this.buffer = buffer; - } + private BufferedTagWriter(Stream stream, MemoryStream buffer, FormatOptions options) : base(stream, options, false) + { + this.buffer = buffer; + } - /// - /// Gets the number of bytes in the internal buffer. - /// - public long Length + /// + /// Gets the number of bytes in the internal buffer. + /// + public long Length + { + get { - get - { - BaseStream.Flush(); - return buffer.Length; - } + BaseStream.Flush(); + return buffer.Length; } + } - /// - /// Gets the capacity of the internal buffer. - /// - /// The capacity will expand automatically as-needed. - public long Capacity => buffer.Capacity; + /// + /// Gets the capacity of the internal buffer. + /// + /// The capacity will expand automatically as-needed. + public long Capacity => buffer.Capacity; - /// - /// Gets the internal buffer as an array of bytes containing the NBT data written so far. - /// - /// An array of bytes containing the NBT data. - [Pure] - public byte[] ToArray() - { - BaseStream.Flush(); - return buffer.ToArray(); - } + /// + /// Gets the internal buffer as an array of bytes containing the NBT data written so far. + /// + /// An array of bytes containing the NBT data. + [Pure] + public byte[] ToArray() + { + BaseStream.Flush(); + return buffer.ToArray(); + } - /// - /// Copies the internal buffer to the specified ; - /// - /// A instance to write to. - public void CopyTo([NotNull] Stream stream) - { - BaseStream.Flush(); - buffer.CopyTo(stream); - } + /// + /// Copies the internal buffer to the specified ; + /// + /// A instance to write to. + public void CopyTo(Stream stream) + { + BaseStream.Flush(); + buffer.CopyTo(stream); + } - /// - /// Asynchronously copies the internal buffer to the specified ; - /// - /// A instance to write to. - public async Task CopyToAsync([NotNull] Stream stream) - { - await BaseStream.FlushAsync(); - await buffer.CopyToAsync(stream); - } + /// + /// Asynchronously copies the internal buffer to the specified ; + /// + /// A instance to write to. + public async Task CopyToAsync(Stream stream) + { + await BaseStream.FlushAsync(); + await buffer.CopyToAsync(stream); + } - /// - /// Implicit conversion of to a . - /// - public static implicit operator ReadOnlySpan(BufferedTagWriter writer) - { - writer.BaseStream.Flush(); - return writer.buffer.ToArray(); - } + /// + /// Implicit conversion of to a . + /// + public static implicit operator ReadOnlySpan(BufferedTagWriter writer) + { + writer.BaseStream.Flush(); + return writer.buffer.ToArray(); } } \ No newline at end of file diff --git a/SharpNBT/CompressionType.cs b/SharpNBT/CompressionType.cs index bfc81c7..4b979de 100644 --- a/SharpNBT/CompressionType.cs +++ b/SharpNBT/CompressionType.cs @@ -1,33 +1,31 @@ using JetBrains.Annotations; -namespace SharpNBT +namespace SharpNBT; + +/// +/// Describes compression formats supported by the NBT specification. +/// +[PublicAPI] +public enum CompressionType : byte { /// - /// Describes compression formats supported by the NBT specification. + /// No compression. /// - [PublicAPI] - public enum CompressionType : byte - { - /// - /// No compression. - /// - None, + None, - /// - /// GZip compression - /// - GZip, + /// + /// GZip compression + /// + GZip, - /// - /// ZLib compression - /// - ZLib, + /// + /// ZLib compression + /// + ZLib, - /// - /// Automatically detect compression using magic numbers. - /// - /// This is not a valid value when specifying a compression type for writing. - AutoDetect = 0xFF - } - + /// + /// Automatically detect compression using magic numbers. + /// + /// This is not a valid value when specifying a compression type for writing. + AutoDetect = 0xFF } \ No newline at end of file diff --git a/SharpNBT/EndianExtensions.cs b/SharpNBT/EndianExtensions.cs index 8e91f26..8436ca8 100644 --- a/SharpNBT/EndianExtensions.cs +++ b/SharpNBT/EndianExtensions.cs @@ -2,74 +2,73 @@ using System.Runtime.CompilerServices; using JetBrains.Annotations; -namespace SharpNBT +namespace SharpNBT; + +/// +/// Contains extension methods dealing with endianness of numeric types. +/// +[PublicAPI] +public static class EndianExtensions { /// - /// Contains extension methods dealing with endianness of numeric types. + /// Swap the endian of the given . /// - [PublicAPI] - public static class EndianExtensions + /// The value to swap endian of. + /// The value with bytes in opposite format. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static short SwapEndian(this short value) { - /// - /// Swap the endian of the given . - /// - /// The value to swap endian of. - /// The value with bytes in opposite format. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static short SwapEndian(this short value) - { - return (short) ((value << 8) | ((value >> 8) & 0xFF)); - } + return (short) ((value << 8) | ((value >> 8) & 0xFF)); + } - /// - [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ushort SwapEndian(this ushort value) - { - return (ushort)((value << 8) | (value >> 8 )); - } + /// + [CLSCompliant(false)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ushort SwapEndian(this ushort value) + { + return (ushort)((value << 8) | (value >> 8 )); + } - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int SwapEndian(this int value) => unchecked((int) SwapEndian(unchecked((uint)value))); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int SwapEndian(this int value) => unchecked((int) SwapEndian(unchecked((uint)value))); - /// - [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static uint SwapEndian(this uint value) - { - value = ((value << 8) & 0xFF00FF00 ) | ((value >> 8) & 0xFF00FF ); - return (value << 16) | (value >> 16); - } + /// + [CLSCompliant(false)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static uint SwapEndian(this uint value) + { + value = ((value << 8) & 0xFF00FF00 ) | ((value >> 8) & 0xFF00FF ); + return (value << 16) | (value >> 16); + } - /// - [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ulong SwapEndian(this ulong value) - { - value = ((value << 8) & 0xFF00FF00FF00FF00UL) | ((value >> 8) & 0x00FF00FF00FF00FFUL); - value = ((value << 16) & 0xFFFF0000FFFF0000UL) | ((value >> 16) & 0x0000FFFF0000FFFFUL); - return (value << 32) | (value >> 32); - } + /// + [CLSCompliant(false)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ulong SwapEndian(this ulong value) + { + value = ((value << 8) & 0xFF00FF00FF00FF00UL) | ((value >> 8) & 0x00FF00FF00FF00FFUL); + value = ((value << 16) & 0xFFFF0000FFFF0000UL) | ((value >> 16) & 0x0000FFFF0000FFFFUL); + return (value << 32) | (value >> 32); + } - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static long SwapEndian(this long value) => unchecked((long) SwapEndian(unchecked((ulong)value))); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static long SwapEndian(this long value) => unchecked((long) SwapEndian(unchecked((ulong)value))); - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float SwapEndian(this float value) - { - var n = BitConverter.SingleToInt32Bits(value); - return BitConverter.Int32BitsToSingle(n.SwapEndian()); - } + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float SwapEndian(this float value) + { + var n = BitConverter.SingleToInt32Bits(value); + return BitConverter.Int32BitsToSingle(n.SwapEndian()); + } - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static double SwapEndian(this double value) - { - var n = BitConverter.DoubleToInt64Bits(value); - return BitConverter.Int64BitsToDouble(n.SwapEndian()); - } + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double SwapEndian(this double value) + { + var n = BitConverter.DoubleToInt64Bits(value); + return BitConverter.Int64BitsToDouble(n.SwapEndian()); } } \ No newline at end of file diff --git a/SharpNBT/Events/TagEventArgs.cs b/SharpNBT/Events/TagEventArgs.cs index a104fde..5cf0e94 100644 --- a/SharpNBT/Events/TagEventArgs.cs +++ b/SharpNBT/Events/TagEventArgs.cs @@ -1,35 +1,33 @@ using System; using JetBrains.Annotations; -namespace SharpNBT +namespace SharpNBT; + +/// +/// Arguments supplied with tag-related events. +/// +[PublicAPI] +public class TagEventArgs : EventArgs { /// - /// Arguments supplied with tag-related events. + /// Gets a constant describing the basic NBT type of the tag. /// - [PublicAPI] - public class TagEventArgs : EventArgs - { - /// - /// Gets a constant describing the basic NBT type of the tag. - /// - public TagType Type { get; } + public TagType Type { get; } - /// - /// Gets the parsed instance. - /// - [NotNull] - public Tag Tag { get; } + /// + /// Gets the parsed instance. + /// + public Tag Tag { get; } - /// - /// Creates a new instance of the class. - /// - /// A constant describing the basic NBT type of the tag. - /// The parsed instance. - /// Thrown when is . - public TagEventArgs(TagType type, [NotNull] Tag tag) - { - Type = type; - Tag = tag ?? throw new ArgumentNullException(nameof(tag)); - } + /// + /// Creates a new instance of the class. + /// + /// A constant describing the basic NBT type of the tag. + /// The parsed instance. + /// Thrown when is . + public TagEventArgs(TagType type, Tag tag) + { + Type = type; + Tag = tag ?? throw new ArgumentNullException(nameof(tag)); } } \ No newline at end of file diff --git a/SharpNBT/Events/TagHandledEventArgs.cs b/SharpNBT/Events/TagHandledEventArgs.cs index e842893..53760e8 100644 --- a/SharpNBT/Events/TagHandledEventArgs.cs +++ b/SharpNBT/Events/TagHandledEventArgs.cs @@ -3,51 +3,48 @@ using System.IO; using JetBrains.Annotations; -namespace SharpNBT +namespace SharpNBT; + +/// +/// Arguments supplied when an event that can be handled by an event subscriber. +/// +[PublicAPI] +public class TagHandledEventArgs : HandledEventArgs { /// - /// Arguments supplied when an event that can be handled by an event subscriber. + /// Gets a constant describing the basic NBT type of the tag. /// - [PublicAPI] - public class TagHandledEventArgs : HandledEventArgs - { - /// - /// Gets a constant describing the basic NBT type of the tag. - /// - public TagType Type { get; } + public TagType Type { get; } - /// - /// Gets flag indicating if this tag is named, only when a tag is a direct child of a . - /// - public bool IsNamed { get; } + /// + /// Gets flag indicating if this tag is named, only when a tag is a direct child of a . + /// + public bool IsNamed { get; } - /// - /// Gets the stream being read from, positioned at the beginning of the tag payload. - /// - /// When handling this event, the stream position must be moved to the end of the payload, ready for the next tag to be parsed. - /// - [NotNull] - public Stream Stream { get; } + /// + /// Gets the stream being read from, positioned at the beginning of the tag payload. + /// + /// When handling this event, the stream position must be moved to the end of the payload, ready for the next tag to be parsed. + /// + public Stream Stream { get; } - /// - /// Gets or sets the resulting tag from this event being handled. - /// - /// This property must set to a non-null value when is . - [CanBeNull] - public Tag Result { get; set; } + /// + /// Gets or sets the resulting tag from this event being handled. + /// + /// This property must set to a non-null value when is . + public Tag? Result { get; set; } - /// - /// Creates a new instance of the class. - /// - /// A constant describing the basic NBT type of the tag. - /// Flag indicating if this tag is named, only when a tag is a direct child of a . - /// The stream being read from, positioned at the beginning of the tag payload. - /// Thrown when is . - public TagHandledEventArgs(TagType type, bool isNamed, [NotNull] Stream stream) - { - Type = type; - IsNamed = isNamed; - Stream = stream ?? throw new ArgumentNullException(nameof(stream)); - } + /// + /// Creates a new instance of the class. + /// + /// A constant describing the basic NBT type of the tag. + /// Flag indicating if this tag is named, only when a tag is a direct child of a . + /// The stream being read from, positioned at the beginning of the tag payload. + /// Thrown when is . + public TagHandledEventArgs(TagType type, bool isNamed, Stream stream) + { + Type = type; + IsNamed = isNamed; + Stream = stream ?? throw new ArgumentNullException(nameof(stream)); } } \ No newline at end of file diff --git a/SharpNBT/Events/TagReaderCallback.cs b/SharpNBT/Events/TagReaderCallback.cs index 4f7b7b6..389aa97 100644 --- a/SharpNBT/Events/TagReaderCallback.cs +++ b/SharpNBT/Events/TagReaderCallback.cs @@ -1,12 +1,11 @@ using System; -namespace SharpNBT -{ - /// - /// Handler for events used with the class. - /// - /// A type derived from . - /// The instance invoking the event. - /// Any relevant args to be supplied with the callback, - public delegate void TagReaderCallback(TagReader reader, T args) where T : EventArgs; -} \ No newline at end of file +namespace SharpNBT; + +/// +/// Handler for events used with the class. +/// +/// A type derived from . +/// The instance invoking the event. +/// Any relevant args to be supplied with the callback, +public delegate void TagReaderCallback(TagReader reader, T args) where T : EventArgs; \ No newline at end of file diff --git a/SharpNBT/FormatOptions.cs b/SharpNBT/FormatOptions.cs index 99ebfb2..d35b820 100644 --- a/SharpNBT/FormatOptions.cs +++ b/SharpNBT/FormatOptions.cs @@ -1,60 +1,59 @@ using System; using JetBrains.Annotations; -namespace SharpNBT +namespace SharpNBT; + +/// +/// Describes the specification to use for reading/writing. +/// +/// +/// There are some major changes between the original Java version, and the Bedrock editions of Minecraft that makes them incompatible with one another. +/// Furthermore, the Bedrock editions use a different specification depending on whether it is writing to disk or sending over a network. +/// +/// +[PublicAPI][Flags] +public enum FormatOptions { /// - /// Describes the specification to use for reading/writing. - /// - /// - /// There are some major changes between the original Java version, and the Bedrock editions of Minecraft that makes them incompatible with one another. - /// Furthermore, the Bedrock editions use a different specification depending on whether it is writing to disk or sending over a network. - /// - /// - [PublicAPI][Flags] - public enum FormatOptions - { - /// - /// None/invalid option flags. - /// - None = 0, + /// None/invalid option flags. + /// + None = 0, - /// - /// Numeric values will be read/written in big-endian format. - /// - /// This is the default for the Java edition of Minecraft. - BigEndian = 0x01, + /// + /// Numeric values will be read/written in big-endian format. + /// + /// This is the default for the Java edition of Minecraft. + BigEndian = 0x01, - /// - /// Numeric values will be read/written in little-endian format. - /// - /// This is the default for Bedrock editions of Minecraft. - LittleEndian = 0x02, + /// + /// Numeric values will be read/written in little-endian format. + /// + /// This is the default for Bedrock editions of Minecraft. + LittleEndian = 0x02, - /// - /// Integer types will be read/written as variable-length integers. - /// - VarIntegers = 0x04, + /// + /// Integer types will be read/written as variable-length integers. + /// + VarIntegers = 0x04, - /// - /// Variable-length integers will be written using ZigZag encoding. - /// - /// - ZigZagEncoding = 0x08, + /// + /// Variable-length integers will be written using ZigZag encoding. + /// + /// + ZigZagEncoding = 0x08, - /// - /// Flags for using a format compatible with Java editions of Minecraft. - /// - Java = BigEndian, + /// + /// Flags for using a format compatible with Java editions of Minecraft. + /// + Java = BigEndian, - /// - /// Flags for using a format compatible with Bedrock editions of Minecraft when writing to a file. - /// - BedrockFile = LittleEndian, + /// + /// Flags for using a format compatible with Bedrock editions of Minecraft when writing to a file. + /// + BedrockFile = LittleEndian, - /// - /// Flags for using a format compatible with Bedrock editions of Minecraft when transporting across a network.. - /// - BedrockNetwork = LittleEndian | VarIntegers | ZigZagEncoding - } + /// + /// Flags for using a format compatible with Bedrock editions of Minecraft when transporting across a network.. + /// + BedrockNetwork = LittleEndian | VarIntegers | ZigZagEncoding } \ No newline at end of file diff --git a/SharpNBT/JetBrains.Annotations.cs b/SharpNBT/JetBrains.Annotations.cs index a7a2fd4..05d7e26 100644 --- a/SharpNBT/JetBrains.Annotations.cs +++ b/SharpNBT/JetBrains.Annotations.cs @@ -32,1324 +32,1323 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // ReSharper disable MemberCanBeProtected.Global // ReSharper disable InconsistentNaming -namespace JetBrains.Annotations -{ - /// - /// Indicates that the value of the marked element could be null sometimes, - /// so checking for null is required before its usage. - /// - /// - /// [CanBeNull] object Test() => null; - /// - /// void UseTest() { - /// var p = Test(); - /// var s = p.ToString(); // Warning: Possible 'System.NullReferenceException' - /// } - /// - [AttributeUsage( - AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | - AttributeTargets.Delegate | AttributeTargets.Field | AttributeTargets.Event | - AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.GenericParameter)] +namespace JetBrains.Annotations; + +/// +/// Indicates that the value of the marked element could be null sometimes, +/// so checking for null is required before its usage. +/// +/// +/// [CanBeNull] object Test() => null; +/// +/// void UseTest() { +/// var p = Test(); +/// var s = p.ToString(); // Warning: Possible 'System.NullReferenceException' +/// } +/// +[AttributeUsage( + AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | + AttributeTargets.Delegate | AttributeTargets.Field | AttributeTargets.Event | + AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.GenericParameter)] internal sealed class CanBeNullAttribute : Attribute { } - /// - /// Indicates that the value of the marked element can never be null. - /// - /// - /// [NotNull] object Foo() { - /// return null; // Warning: Possible 'null' assignment - /// } - /// - [AttributeUsage( - AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | - AttributeTargets.Delegate | AttributeTargets.Field | AttributeTargets.Event | - AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.GenericParameter)] +/// +/// Indicates that the value of the marked element can never be null. +/// +/// +/// [NotNull] object Foo() { +/// return null; // Warning: Possible 'null' assignment +/// } +/// +[AttributeUsage( + AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | + AttributeTargets.Delegate | AttributeTargets.Field | AttributeTargets.Event | + AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.GenericParameter)] internal sealed class NotNullAttribute : Attribute { } - /// - /// Can be applied to symbols of types derived from IEnumerable as well as to symbols of Task - /// and Lazy classes to indicate that the value of a collection item, of the Task.Result property - /// or of the Lazy.Value property can never be null. - /// - /// - /// public void Foo([ItemNotNull]List<string> books) - /// { - /// foreach (var book in books) { - /// if (book != null) // Warning: Expression is always true - /// Console.WriteLine(book.ToUpper()); - /// } - /// } - /// - [AttributeUsage( - AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | - AttributeTargets.Delegate | AttributeTargets.Field)] +/// +/// Can be applied to symbols of types derived from IEnumerable as well as to symbols of Task +/// and Lazy classes to indicate that the value of a collection item, of the Task.Result property +/// or of the Lazy.Value property can never be null. +/// +/// +/// public void Foo([ItemNotNull]List<string> books) +/// { +/// foreach (var book in books) { +/// if (book != null) // Warning: Expression is always true +/// Console.WriteLine(book.ToUpper()); +/// } +/// } +/// +[AttributeUsage( + AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | + AttributeTargets.Delegate | AttributeTargets.Field)] internal sealed class ItemNotNullAttribute : Attribute { } - /// - /// Can be applied to symbols of types derived from IEnumerable as well as to symbols of Task - /// and Lazy classes to indicate that the value of a collection item, of the Task.Result property - /// or of the Lazy.Value property can be null. - /// - /// - /// public void Foo([ItemCanBeNull]List<string> books) - /// { - /// foreach (var book in books) - /// { - /// // Warning: Possible 'System.NullReferenceException' - /// Console.WriteLine(book.ToUpper()); - /// } - /// } - /// - [AttributeUsage( - AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | - AttributeTargets.Delegate | AttributeTargets.Field)] +/// +/// Can be applied to symbols of types derived from IEnumerable as well as to symbols of Task +/// and Lazy classes to indicate that the value of a collection item, of the Task.Result property +/// or of the Lazy.Value property can be null. +/// +/// +/// public void Foo([ItemCanBeNull]List<string> books) +/// { +/// foreach (var book in books) +/// { +/// // Warning: Possible 'System.NullReferenceException' +/// Console.WriteLine(book.ToUpper()); +/// } +/// } +/// +[AttributeUsage( + AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | + AttributeTargets.Delegate | AttributeTargets.Field)] internal sealed class ItemCanBeNullAttribute : Attribute { } - /// - /// Indicates that the marked method builds string by the format pattern and (optional) arguments. - /// The parameter, which contains the format string, should be given in the constructor. The format string - /// should be in -like form. - /// - /// - /// [StringFormatMethod("message")] - /// void ShowError(string message, params object[] args) { /* do something */ } - /// - /// void Foo() { - /// ShowError("Failed: {0}"); // Warning: Non-existing argument in format string - /// } - /// - [AttributeUsage( - AttributeTargets.Constructor | AttributeTargets.Method | - AttributeTargets.Property | AttributeTargets.Delegate)] +/// +/// Indicates that the marked method builds string by the format pattern and (optional) arguments. +/// The parameter, which contains the format string, should be given in the constructor. The format string +/// should be in -like form. +/// +/// +/// [StringFormatMethod("message")] +/// void ShowError(string message, params object[] args) { /* do something */ } +/// +/// void Foo() { +/// ShowError("Failed: {0}"); // Warning: Non-existing argument in format string +/// } +/// +[AttributeUsage( + AttributeTargets.Constructor | AttributeTargets.Method | + AttributeTargets.Property | AttributeTargets.Delegate)] internal sealed class StringFormatMethodAttribute : Attribute +{ + /// + /// Specifies which parameter of an annotated method should be treated as the format string + /// + public StringFormatMethodAttribute(string formatParameterName) { - /// - /// Specifies which parameter of an annotated method should be treated as the format string - /// - public StringFormatMethodAttribute([NotNull] string formatParameterName) - { - FormatParameterName = formatParameterName; - } - - [NotNull] public string FormatParameterName { get; } + FormatParameterName = formatParameterName; } - /// - /// Indicates that the marked parameter is a message template where placeholders are to be replaced by the following arguments - /// in the order in which they appear - /// - /// - /// void LogInfo([StructuredMessageTemplate]string message, params object[] args) { /* do something */ } - /// - /// void Foo() { - /// LogInfo("User created: {username}"); // Warning: Non-existing argument in format string - /// } - /// - [AttributeUsage(AttributeTargets.Parameter)] + public string FormatParameterName { get; } +} + +/// +/// Indicates that the marked parameter is a message template where placeholders are to be replaced by the following arguments +/// in the order in which they appear +/// +/// +/// void LogInfo([StructuredMessageTemplate]string message, params object[] args) { /* do something */ } +/// +/// void Foo() { +/// LogInfo("User created: {username}"); // Warning: Non-existing argument in format string +/// } +/// +[AttributeUsage(AttributeTargets.Parameter)] internal sealed class StructuredMessageTemplateAttribute : Attribute {} - /// - /// Use this annotation to specify a type that contains static or const fields - /// with values for the annotated property/field/parameter. - /// The specified type will be used to improve completion suggestions. - /// - /// - /// namespace TestNamespace - /// { - /// public class Constants - /// { - /// public static int INT_CONST = 1; - /// public const string STRING_CONST = "1"; - /// } - /// - /// public class Class1 - /// { - /// [ValueProvider("TestNamespace.Constants")] public int myField; - /// public void Foo([ValueProvider("TestNamespace.Constants")] string str) { } - /// - /// public void Test() - /// { - /// Foo(/*try completion here*/);// - /// myField = /*try completion here*/ - /// } - /// } - /// } - /// - [AttributeUsage( - AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.Field, - AllowMultiple = true)] +/// +/// Use this annotation to specify a type that contains static or const fields +/// with values for the annotated property/field/parameter. +/// The specified type will be used to improve completion suggestions. +/// +/// +/// namespace TestNamespace +/// { +/// public class Constants +/// { +/// public static int INT_CONST = 1; +/// public const string STRING_CONST = "1"; +/// } +/// +/// public class Class1 +/// { +/// [ValueProvider("TestNamespace.Constants")] public int myField; +/// public void Foo([ValueProvider("TestNamespace.Constants")] string str) { } +/// +/// public void Test() +/// { +/// Foo(/*try completion here*/);// +/// myField = /*try completion here*/ +/// } +/// } +/// } +/// +[AttributeUsage( + AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.Field, + AllowMultiple = true)] internal sealed class ValueProviderAttribute : Attribute +{ + public ValueProviderAttribute(string name) { - public ValueProviderAttribute([NotNull] string name) - { - Name = name; - } - - [NotNull] public string Name { get; } + Name = name; } - /// - /// Indicates that the integral value falls into the specified interval. - /// It's allowed to specify multiple non-intersecting intervals. - /// Values of interval boundaries are inclusive. - /// - /// - /// void Foo([ValueRange(0, 100)] int value) { - /// if (value == -1) { // Warning: Expression is always 'false' - /// ... - /// } - /// } - /// - [AttributeUsage( - AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property | - AttributeTargets.Method | AttributeTargets.Delegate, - AllowMultiple = true)] + public string Name { get; } +} + +/// +/// Indicates that the integral value falls into the specified interval. +/// It's allowed to specify multiple non-intersecting intervals. +/// Values of interval boundaries are inclusive. +/// +/// +/// void Foo([ValueRange(0, 100)] int value) { +/// if (value == -1) { // Warning: Expression is always 'false' +/// ... +/// } +/// } +/// +[AttributeUsage( + AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property | + AttributeTargets.Method | AttributeTargets.Delegate, + AllowMultiple = true)] internal sealed class ValueRangeAttribute : Attribute +{ + public object From { get; } + public object To { get; } + + public ValueRangeAttribute(long from, long to) { - public object From { get; } - public object To { get; } - - public ValueRangeAttribute(long from, long to) - { - From = from; - To = to; - } - - public ValueRangeAttribute(ulong from, ulong to) - { - From = from; - To = to; - } - - public ValueRangeAttribute(long value) - { - From = To = value; - } - - public ValueRangeAttribute(ulong value) - { - From = To = value; - } + From = from; + To = to; } - /// - /// Indicates that the integral value never falls below zero. - /// - /// - /// void Foo([NonNegativeValue] int value) { - /// if (value == -1) { // Warning: Expression is always 'false' - /// ... - /// } - /// } - /// - [AttributeUsage( - AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property | - AttributeTargets.Method | AttributeTargets.Delegate)] + public ValueRangeAttribute(ulong from, ulong to) + { + From = from; + To = to; + } + + public ValueRangeAttribute(long value) + { + From = To = value; + } + + public ValueRangeAttribute(ulong value) + { + From = To = value; + } +} + +/// +/// Indicates that the integral value never falls below zero. +/// +/// +/// void Foo([NonNegativeValue] int value) { +/// if (value == -1) { // Warning: Expression is always 'false' +/// ... +/// } +/// } +/// +[AttributeUsage( + AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property | + AttributeTargets.Method | AttributeTargets.Delegate)] internal sealed class NonNegativeValueAttribute : Attribute { } - /// - /// Indicates that the function argument should be a string literal and match one - /// of the parameters of the caller function. For example, ReSharper annotates - /// the parameter of . - /// - /// - /// void Foo(string param) { - /// if (param == null) - /// throw new ArgumentNullException("par"); // Warning: Cannot resolve symbol - /// } - /// - [AttributeUsage(AttributeTargets.Parameter)] +/// +/// Indicates that the function argument should be a string literal and match one +/// of the parameters of the caller function. For example, ReSharper annotates +/// the parameter of . +/// +/// +/// void Foo(string param) { +/// if (param == null) +/// throw new ArgumentNullException("par"); // Warning: Cannot resolve symbol +/// } +/// +[AttributeUsage(AttributeTargets.Parameter)] internal sealed class InvokerParameterNameAttribute : Attribute { } - /// - /// Indicates that the method is contained in a type that implements - /// System.ComponentModel.INotifyPropertyChanged interface and this method - /// is used to notify that some property value changed. - /// - /// - /// The method should be non-static and conform to one of the supported signatures: - /// - /// NotifyChanged(string) - /// NotifyChanged(params string[]) - /// NotifyChanged{T}(Expression{Func{T}}) - /// NotifyChanged{T,U}(Expression{Func{T,U}}) - /// SetProperty{T}(ref T, T, string) - /// - /// - /// - /// public class Foo : INotifyPropertyChanged { - /// public event PropertyChangedEventHandler PropertyChanged; - /// - /// [NotifyPropertyChangedInvocator] - /// protected virtual void NotifyChanged(string propertyName) { ... } - /// - /// string _name; - /// - /// public string Name { - /// get { return _name; } - /// set { _name = value; NotifyChanged("LastName"); /* Warning */ } - /// } - /// } - /// - /// Examples of generated notifications: - /// - /// NotifyChanged("Property") - /// NotifyChanged(() => Property) - /// NotifyChanged((VM x) => x.Property) - /// SetProperty(ref myField, value, "Property") - /// - /// - [AttributeUsage(AttributeTargets.Method)] +/// +/// Indicates that the method is contained in a type that implements +/// System.ComponentModel.INotifyPropertyChanged interface and this method +/// is used to notify that some property value changed. +/// +/// +/// The method should be non-static and conform to one of the supported signatures: +/// +/// NotifyChanged(string) +/// NotifyChanged(params string[]) +/// NotifyChanged{T}(Expression{Func{T}}) +/// NotifyChanged{T,U}(Expression{Func{T,U}}) +/// SetProperty{T}(ref T, T, string) +/// +/// +/// +/// public class Foo : INotifyPropertyChanged { +/// public event PropertyChangedEventHandler PropertyChanged; +/// +/// [NotifyPropertyChangedInvocator] +/// protected virtual void NotifyChanged(string propertyName) { ... } +/// +/// string _name; +/// +/// public string Name { +/// get { return _name; } +/// set { _name = value; NotifyChanged("LastName"); /* Warning */ } +/// } +/// } +/// +/// Examples of generated notifications: +/// +/// NotifyChanged("Property") +/// NotifyChanged(() => Property) +/// NotifyChanged((VM x) => x.Property) +/// SetProperty(ref myField, value, "Property") +/// +/// +[AttributeUsage(AttributeTargets.Method)] internal sealed class NotifyPropertyChangedInvocatorAttribute : Attribute +{ + public NotifyPropertyChangedInvocatorAttribute() { } + public NotifyPropertyChangedInvocatorAttribute(string parameterName) { - public NotifyPropertyChangedInvocatorAttribute() { } - public NotifyPropertyChangedInvocatorAttribute([NotNull] string parameterName) - { - ParameterName = parameterName; - } - - [CanBeNull] public string ParameterName { get; } + ParameterName = parameterName; } - /// - /// Describes dependency between method input and output. - /// - /// - ///

Function Definition Table syntax:

- /// - /// FDT ::= FDTRow [;FDTRow]* - /// FDTRow ::= Input => Output | Output <= Input - /// Input ::= ParameterName: Value [, Input]* - /// Output ::= [ParameterName: Value]* {halt|stop|void|nothing|Value} - /// Value ::= true | false | null | notnull | canbenull - /// - /// If the method has a single input parameter, its name could be omitted.
- /// Using halt (or void/nothing, which is the same) for the method output - /// means that the method doesn't return normally (throws or terminates the process).
- /// Value canbenull is only applicable for output parameters.
- /// You can use multiple [ContractAnnotation] for each FDT row, or use single attribute - /// with rows separated by the semicolon. There is no notion of order rows, all rows are checked - /// for applicability and applied per each program state tracked by the analysis engine.
- ///
- /// - /// - /// [ContractAnnotation("=> halt")] - /// public void TerminationMethod() - /// - /// - /// [ContractAnnotation("null <= param:null")] // reverse condition syntax - /// public string GetName(string surname) - /// - /// - /// [ContractAnnotation("s:null => true")] - /// public bool IsNullOrEmpty(string s) // string.IsNullOrEmpty() - /// - /// - /// // A method that returns null if the parameter is null, - /// // and not null if the parameter is not null - /// [ContractAnnotation("null => null; notnull => notnull")] - /// public object Transform(object data) - /// - /// - /// [ContractAnnotation("=> true, result: notnull; => false, result: null")] - /// public bool TryParse(string s, out Person result) - /// - /// - [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] + public string? ParameterName { get; } +} + +/// +/// Describes dependency between method input and output. +/// +/// +///

Function Definition Table syntax:

+/// +/// FDT ::= FDTRow [;FDTRow]* +/// FDTRow ::= Input => Output | Output <= Input +/// Input ::= ParameterName: Value [, Input]* +/// Output ::= [ParameterName: Value]* {halt|stop|void|nothing|Value} +/// Value ::= true | false | null | notnull | canbenull +/// +/// If the method has a single input parameter, its name could be omitted.
+/// Using halt (or void/nothing, which is the same) for the method output +/// means that the method doesn't return normally (throws or terminates the process).
+/// Value canbenull is only applicable for output parameters.
+/// You can use multiple [ContractAnnotation] for each FDT row, or use single attribute +/// with rows separated by the semicolon. There is no notion of order rows, all rows are checked +/// for applicability and applied per each program state tracked by the analysis engine.
+///
+/// +/// +/// [ContractAnnotation("=> halt")] +/// public void TerminationMethod() +/// +/// +/// [ContractAnnotation("null <= param:null")] // reverse condition syntax +/// public string GetName(string surname) +/// +/// +/// [ContractAnnotation("s:null => true")] +/// public bool IsNullOrEmpty(string s) // string.IsNullOrEmpty() +/// +/// +/// // A method that returns null if the parameter is null, +/// // and not null if the parameter is not null +/// [ContractAnnotation("null => null; notnull => notnull")] +/// public object Transform(object data) +/// +/// +/// [ContractAnnotation("=> true, result: notnull; => false, result: null")] +/// public bool TryParse(string s, out Person result) +/// +/// +[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] internal sealed class ContractAnnotationAttribute : Attribute - { - public ContractAnnotationAttribute([NotNull] string contract) - : this(contract, false) { } +{ + public ContractAnnotationAttribute(string contract) + : this(contract, false) { } - public ContractAnnotationAttribute([NotNull] string contract, bool forceFullStates) - { - Contract = contract; - ForceFullStates = forceFullStates; - } + public ContractAnnotationAttribute(string contract, bool forceFullStates) + { + Contract = contract; + ForceFullStates = forceFullStates; + } - [NotNull] public string Contract { get; } + public string Contract { get; } - public bool ForceFullStates { get; } - } + public bool ForceFullStates { get; } +} - /// - /// Indicates whether the marked element should be localized. - /// - /// - /// [LocalizationRequiredAttribute(true)] - /// class Foo { - /// string str = "my string"; // Warning: Localizable string - /// } - /// - [AttributeUsage(AttributeTargets.All)] +/// +/// Indicates whether the marked element should be localized. +/// +/// +/// [LocalizationRequiredAttribute(true)] +/// class Foo { +/// string str = "my string"; // Warning: Localizable string +/// } +/// +[AttributeUsage(AttributeTargets.All)] internal sealed class LocalizationRequiredAttribute : Attribute - { - public LocalizationRequiredAttribute() : this(true) { } - - public LocalizationRequiredAttribute(bool required) - { - Required = required; - } +{ + public LocalizationRequiredAttribute() : this(true) { } - public bool Required { get; } + public LocalizationRequiredAttribute(bool required) + { + Required = required; } - /// - /// Indicates that the value of the marked type (or its derivatives) - /// cannot be compared using '==' or '!=' operators and Equals() - /// should be used instead. However, using '==' or '!=' for comparison - /// with null is always permitted. - /// - /// - /// [CannotApplyEqualityOperator] - /// class NoEquality { } - /// - /// class UsesNoEquality { - /// void Test() { - /// var ca1 = new NoEquality(); - /// var ca2 = new NoEquality(); - /// if (ca1 != null) { // OK - /// bool condition = ca1 == ca2; // Warning - /// } - /// } - /// } - /// - [AttributeUsage(AttributeTargets.Interface | AttributeTargets.Class | AttributeTargets.Struct)] + public bool Required { get; } +} + +/// +/// Indicates that the value of the marked type (or its derivatives) +/// cannot be compared using '==' or '!=' operators and Equals() +/// should be used instead. However, using '==' or '!=' for comparison +/// with null is always permitted. +/// +/// +/// [CannotApplyEqualityOperator] +/// class NoEquality { } +/// +/// class UsesNoEquality { +/// void Test() { +/// var ca1 = new NoEquality(); +/// var ca2 = new NoEquality(); +/// if (ca1 != null) { // OK +/// bool condition = ca1 == ca2; // Warning +/// } +/// } +/// } +/// +[AttributeUsage(AttributeTargets.Interface | AttributeTargets.Class | AttributeTargets.Struct)] internal sealed class CannotApplyEqualityOperatorAttribute : Attribute { } - /// - /// When applied to a target attribute, specifies a requirement for any type marked - /// with the target attribute to implement or inherit specific type or types. - /// - /// - /// [BaseTypeRequired(typeof(IComponent)] // Specify requirement - /// class ComponentAttribute : Attribute { } - /// - /// [Component] // ComponentAttribute requires implementing IComponent interface - /// class MyComponent : IComponent { } - /// - [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] - [BaseTypeRequired(typeof(Attribute))] +/// +/// When applied to a target attribute, specifies a requirement for any type marked +/// with the target attribute to implement or inherit specific type or types. +/// +/// +/// [BaseTypeRequired(typeof(IComponent)] // Specify requirement +/// class ComponentAttribute : Attribute { } +/// +/// [Component] // ComponentAttribute requires implementing IComponent interface +/// class MyComponent : IComponent { } +/// +[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] +[BaseTypeRequired(typeof(Attribute))] internal sealed class BaseTypeRequiredAttribute : Attribute +{ + public BaseTypeRequiredAttribute(Type baseType) { - public BaseTypeRequiredAttribute([NotNull] Type baseType) - { - BaseType = baseType; - } - - [NotNull] public Type BaseType { get; } + BaseType = baseType; } - /// - /// Indicates that the marked symbol is used implicitly (e.g. via reflection, in external library), - /// so this symbol will be ignored by usage-checking inspections.
- /// You can use and - /// to configure how this attribute is applied. - ///
- /// - /// [UsedImplicitly] - /// public class TypeConverter {} - /// - /// public class SummaryData - /// { - /// [UsedImplicitly(ImplicitUseKindFlags.InstantiatedWithFixedConstructorSignature)] - /// public SummaryData() {} - /// } - /// - /// [UsedImplicitly(ImplicitUseTargetFlags.WithInheritors | ImplicitUseTargetFlags.Default)] - /// public interface IService {} - /// - [AttributeUsage(AttributeTargets.All)] + public Type BaseType { get; } +} + +/// +/// Indicates that the marked symbol is used implicitly (e.g. via reflection, in external library), +/// so this symbol will be ignored by usage-checking inspections.
+/// You can use and +/// to configure how this attribute is applied. +///
+/// +/// [UsedImplicitly] +/// public class TypeConverter {} +/// +/// public class SummaryData +/// { +/// [UsedImplicitly(ImplicitUseKindFlags.InstantiatedWithFixedConstructorSignature)] +/// public SummaryData() {} +/// } +/// +/// [UsedImplicitly(ImplicitUseTargetFlags.WithInheritors | ImplicitUseTargetFlags.Default)] +/// public interface IService {} +/// +[AttributeUsage(AttributeTargets.All)] internal sealed class UsedImplicitlyAttribute : Attribute - { - public UsedImplicitlyAttribute() - : this(ImplicitUseKindFlags.Default, ImplicitUseTargetFlags.Default) { } +{ + public UsedImplicitlyAttribute() + : this(ImplicitUseKindFlags.Default, ImplicitUseTargetFlags.Default) { } - public UsedImplicitlyAttribute(ImplicitUseKindFlags useKindFlags) - : this(useKindFlags, ImplicitUseTargetFlags.Default) { } + public UsedImplicitlyAttribute(ImplicitUseKindFlags useKindFlags) + : this(useKindFlags, ImplicitUseTargetFlags.Default) { } - public UsedImplicitlyAttribute(ImplicitUseTargetFlags targetFlags) - : this(ImplicitUseKindFlags.Default, targetFlags) { } + public UsedImplicitlyAttribute(ImplicitUseTargetFlags targetFlags) + : this(ImplicitUseKindFlags.Default, targetFlags) { } - public UsedImplicitlyAttribute(ImplicitUseKindFlags useKindFlags, ImplicitUseTargetFlags targetFlags) - { - UseKindFlags = useKindFlags; - TargetFlags = targetFlags; - } + public UsedImplicitlyAttribute(ImplicitUseKindFlags useKindFlags, ImplicitUseTargetFlags targetFlags) + { + UseKindFlags = useKindFlags; + TargetFlags = targetFlags; + } - public ImplicitUseKindFlags UseKindFlags { get; } + public ImplicitUseKindFlags UseKindFlags { get; } - public ImplicitUseTargetFlags TargetFlags { get; } - } + public ImplicitUseTargetFlags TargetFlags { get; } +} - /// - /// Can be applied to attributes, type parameters, and parameters of a type assignable from . - /// When applied to an attribute, the decorated attribute behaves the same as . - /// When applied to a type parameter or to a parameter of type , - /// indicates that the corresponding type is used implicitly. - /// - [AttributeUsage(AttributeTargets.Class | AttributeTargets.GenericParameter | AttributeTargets.Parameter)] +/// +/// Can be applied to attributes, type parameters, and parameters of a type assignable from . +/// When applied to an attribute, the decorated attribute behaves the same as . +/// When applied to a type parameter or to a parameter of type , +/// indicates that the corresponding type is used implicitly. +/// +[AttributeUsage(AttributeTargets.Class | AttributeTargets.GenericParameter | AttributeTargets.Parameter)] internal sealed class MeansImplicitUseAttribute : Attribute - { - public MeansImplicitUseAttribute() - : this(ImplicitUseKindFlags.Default, ImplicitUseTargetFlags.Default) { } +{ + public MeansImplicitUseAttribute() + : this(ImplicitUseKindFlags.Default, ImplicitUseTargetFlags.Default) { } - public MeansImplicitUseAttribute(ImplicitUseKindFlags useKindFlags) - : this(useKindFlags, ImplicitUseTargetFlags.Default) { } + public MeansImplicitUseAttribute(ImplicitUseKindFlags useKindFlags) + : this(useKindFlags, ImplicitUseTargetFlags.Default) { } - public MeansImplicitUseAttribute(ImplicitUseTargetFlags targetFlags) - : this(ImplicitUseKindFlags.Default, targetFlags) { } + public MeansImplicitUseAttribute(ImplicitUseTargetFlags targetFlags) + : this(ImplicitUseKindFlags.Default, targetFlags) { } - public MeansImplicitUseAttribute(ImplicitUseKindFlags useKindFlags, ImplicitUseTargetFlags targetFlags) - { - UseKindFlags = useKindFlags; - TargetFlags = targetFlags; - } + public MeansImplicitUseAttribute(ImplicitUseKindFlags useKindFlags, ImplicitUseTargetFlags targetFlags) + { + UseKindFlags = useKindFlags; + TargetFlags = targetFlags; + } - [UsedImplicitly] public ImplicitUseKindFlags UseKindFlags { get; } + [UsedImplicitly] public ImplicitUseKindFlags UseKindFlags { get; } - [UsedImplicitly] public ImplicitUseTargetFlags TargetFlags { get; } - } + [UsedImplicitly] public ImplicitUseTargetFlags TargetFlags { get; } +} - /// - /// Specifies the details of implicitly used symbol when it is marked - /// with or . - /// - [Flags] +/// +/// Specifies the details of implicitly used symbol when it is marked +/// with or . +/// +[Flags] internal enum ImplicitUseKindFlags - { - Default = Access | Assign | InstantiatedWithFixedConstructorSignature, - /// Only entity marked with attribute considered used. - Access = 1, - /// Indicates implicit assignment to a member. - Assign = 2, - /// - /// Indicates implicit instantiation of a type with fixed constructor signature. - /// That means any unused constructor parameters won't be reported as such. - /// - InstantiatedWithFixedConstructorSignature = 4, - /// Indicates implicit instantiation of a type. - InstantiatedNoFixedConstructorSignature = 8, - } - +{ + Default = Access | Assign | InstantiatedWithFixedConstructorSignature, + /// Only entity marked with attribute considered used. + Access = 1, + /// Indicates implicit assignment to a member. + Assign = 2, /// - /// Specifies what is considered to be used implicitly when marked - /// with or . + /// Indicates implicit instantiation of a type with fixed constructor signature. + /// That means any unused constructor parameters won't be reported as such. /// - [Flags] + InstantiatedWithFixedConstructorSignature = 4, + /// Indicates implicit instantiation of a type. + InstantiatedNoFixedConstructorSignature = 8, +} + +/// +/// Specifies what is considered to be used implicitly when marked +/// with or . +/// +[Flags] internal enum ImplicitUseTargetFlags - { - Default = Itself, - Itself = 1, - /// Members of the type marked with the attribute are considered used. - Members = 2, - /// Inherited entities are considered used. - WithInheritors = 4, - /// Entity marked with the attribute and all its members considered used. - WithMembers = Itself | Members - } +{ + Default = Itself, + Itself = 1, + /// Members of the type marked with the attribute are considered used. + Members = 2, + /// Inherited entities are considered used. + WithInheritors = 4, + /// Entity marked with the attribute and all its members considered used. + WithMembers = Itself | Members +} - /// - /// This attribute is intended to mark publicly available API, - /// which should not be removed and so is treated as used. - /// - [MeansImplicitUse(ImplicitUseTargetFlags.WithMembers)] - [AttributeUsage(AttributeTargets.All, Inherited = false)] +/// +/// This attribute is intended to mark publicly available API, +/// which should not be removed and so is treated as used. +/// +[MeansImplicitUse(ImplicitUseTargetFlags.WithMembers)] +[AttributeUsage(AttributeTargets.All, Inherited = false)] internal sealed class PublicAPIAttribute : Attribute - { - public PublicAPIAttribute() { } - - public PublicAPIAttribute([NotNull] string comment) - { - Comment = comment; - } +{ + public PublicAPIAttribute() { } - [CanBeNull] public string Comment { get; } + public PublicAPIAttribute(string comment) + { + Comment = comment; } - /// - /// Tells the code analysis engine if the parameter is completely handled when the invoked method is on stack. - /// If the parameter is a delegate, indicates that delegate is executed while the method is executed. - /// If the parameter is an enumerable, indicates that it is enumerated while the method is executed. - /// - [AttributeUsage(AttributeTargets.Parameter)] + public string? Comment { get; } +} + +/// +/// Tells the code analysis engine if the parameter is completely handled when the invoked method is on stack. +/// If the parameter is a delegate, indicates that delegate is executed while the method is executed. +/// If the parameter is an enumerable, indicates that it is enumerated while the method is executed. +/// +[AttributeUsage(AttributeTargets.Parameter)] internal sealed class InstantHandleAttribute : Attribute { } - /// - /// Indicates that a method does not make any observable state changes. - /// The same as System.Diagnostics.Contracts.PureAttribute. - /// - /// - /// [Pure] int Multiply(int x, int y) => x * y; - /// - /// void M() { - /// Multiply(123, 42); // Warning: Return value of pure method is not used - /// } - /// - [AttributeUsage(AttributeTargets.Method)] +/// +/// Indicates that a method does not make any observable state changes. +/// The same as System.Diagnostics.Contracts.PureAttribute. +/// +/// +/// [Pure] int Multiply(int x, int y) => x * y; +/// +/// void M() { +/// Multiply(123, 42); // Warning: Return value of pure method is not used +/// } +/// +[AttributeUsage(AttributeTargets.Method)] internal sealed class PureAttribute : Attribute { } - /// - /// Indicates that the return value of the method invocation must be used. - /// - /// - /// Methods decorated with this attribute (in contrast to pure methods) might change state, - /// but make no sense without using their return value.
- /// Similarly to , this attribute - /// will help to detect usages of the method when the return value is not used. - /// Optionally, you can specify a message to use when showing warnings, e.g. - /// [MustUseReturnValue("Use the return value to...")]. - ///
- [AttributeUsage(AttributeTargets.Method)] +/// +/// Indicates that the return value of the method invocation must be used. +/// +/// +/// Methods decorated with this attribute (in contrast to pure methods) might change state, +/// but make no sense without using their return value.
+/// Similarly to , this attribute +/// will help to detect usages of the method when the return value is not used. +/// Optionally, you can specify a message to use when showing warnings, e.g. +/// [MustUseReturnValue("Use the return value to...")]. +///
+[AttributeUsage(AttributeTargets.Method)] internal sealed class MustUseReturnValueAttribute : Attribute - { - public MustUseReturnValueAttribute() { } - - public MustUseReturnValueAttribute([NotNull] string justification) - { - Justification = justification; - } +{ + public MustUseReturnValueAttribute() { } - [CanBeNull] public string Justification { get; } + public MustUseReturnValueAttribute(string justification) + { + Justification = justification; } - /// - /// This annotation allows to enforce allocation-less usage patterns of delegates for performance-critical APIs. - /// When this annotation is applied to the parameter of delegate type, IDE checks the input argument of this parameter: - /// * When lambda expression or anonymous method is passed as an argument, IDE verifies that the passed closure - /// has no captures of the containing local variables and the compiler is able to cache the delegate instance - /// to avoid heap allocations. Otherwise the warning is produced. - /// * IDE warns when method name or local function name is passed as an argument as this always results - /// in heap allocation of the delegate instance. - /// - /// - /// In C# 9.0 code IDE would also suggest to annotate the anonymous function with 'static' modifier - /// to make use of the similar analysis provided by the language/compiler. - /// - [AttributeUsage(AttributeTargets.Parameter)] + public string? Justification { get; } +} + +/// +/// This annotation allows to enforce allocation-less usage patterns of delegates for performance-critical APIs. +/// When this annotation is applied to the parameter of delegate type, IDE checks the input argument of this parameter: +/// * When lambda expression or anonymous method is passed as an argument, IDE verifies that the passed closure +/// has no captures of the containing local variables and the compiler is able to cache the delegate instance +/// to avoid heap allocations. Otherwise the warning is produced. +/// * IDE warns when method name or local function name is passed as an argument as this always results +/// in heap allocation of the delegate instance. +/// +/// +/// In C# 9.0 code IDE would also suggest to annotate the anonymous function with 'static' modifier +/// to make use of the similar analysis provided by the language/compiler. +/// +[AttributeUsage(AttributeTargets.Parameter)] internal class RequireStaticDelegateAttribute : Attribute - { - public bool IsError { get; set; } - } +{ + public bool IsError { get; set; } +} - /// - /// Indicates the type member or parameter of some type, that should be used instead of all other ways - /// to get the value of that type. This annotation is useful when you have some "context" value evaluated - /// and stored somewhere, meaning that all other ways to get this value must be consolidated with existing one. - /// - /// - /// class Foo { - /// [ProvidesContext] IBarService _barService = ...; - /// - /// void ProcessNode(INode node) { - /// DoSomething(node, node.GetGlobalServices().Bar); - /// // ^ Warning: use value of '_barService' field - /// } - /// } - /// - [AttributeUsage( - AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Parameter | AttributeTargets.Method | - AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct | AttributeTargets.GenericParameter)] +/// +/// Indicates the type member or parameter of some type, that should be used instead of all other ways +/// to get the value of that type. This annotation is useful when you have some "context" value evaluated +/// and stored somewhere, meaning that all other ways to get this value must be consolidated with existing one. +/// +/// +/// class Foo { +/// [ProvidesContext] IBarService _barService = ...; +/// +/// void ProcessNode(INode node) { +/// DoSomething(node, node.GetGlobalServices().Bar); +/// // ^ Warning: use value of '_barService' field +/// } +/// } +/// +[AttributeUsage( + AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Parameter | AttributeTargets.Method | + AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct | AttributeTargets.GenericParameter)] internal sealed class ProvidesContextAttribute : Attribute { } - /// - /// Indicates that a parameter is a path to a file or a folder within a web project. - /// Path can be relative or absolute, starting from web root (~). - /// - [AttributeUsage(AttributeTargets.Parameter)] +/// +/// Indicates that a parameter is a path to a file or a folder within a web project. +/// Path can be relative or absolute, starting from web root (~). +/// +[AttributeUsage(AttributeTargets.Parameter)] internal sealed class PathReferenceAttribute : Attribute +{ + public PathReferenceAttribute() { } + + public PathReferenceAttribute([PathReference] string basePath) { - public PathReferenceAttribute() { } + BasePath = basePath; + } - public PathReferenceAttribute([NotNull, PathReference] string basePath) - { - BasePath = basePath; - } + public string? BasePath { get; } +} - [CanBeNull] public string BasePath { get; } - } +/// +/// An extension method marked with this attribute is processed by code completion +/// as a 'Source Template'. When the extension method is completed over some expression, its source code +/// is automatically expanded like a template at call site. +/// +/// +/// Template method body can contain valid source code and/or special comments starting with '$'. +/// Text inside these comments is added as source code when the template is applied. Template parameters +/// can be used either as additional method parameters or as identifiers wrapped in two '$' signs. +/// Use the attribute to specify macros for parameters. +/// +/// +/// In this example, the 'forEach' method is a source template available over all values +/// of enumerable types, producing ordinary C# 'foreach' statement and placing caret inside block: +/// +/// [SourceTemplate] +/// public static void forEach<T>(this IEnumerable<T> xs) { +/// foreach (var x in xs) { +/// //$ $END$ +/// } +/// } +/// +/// +[AttributeUsage(AttributeTargets.Method)] +internal sealed class SourceTemplateAttribute : Attribute { } +/// +/// Allows specifying a macro for a parameter of a source template. +/// +/// +/// You can apply the attribute on the whole method or on any of its additional parameters. The macro expression +/// is defined in the property. When applied on a method, the target +/// template parameter is defined in the property. To apply the macro silently +/// for the parameter, set the property value = -1. +/// +/// +/// Applying the attribute on a source template method: +/// +/// [SourceTemplate, Macro(Target = "item", Expression = "suggestVariableName()")] +/// public static void forEach<T>(this IEnumerable<T> collection) { +/// foreach (var item in collection) { +/// //$ $END$ +/// } +/// } +/// +/// Applying the attribute on a template method parameter: +/// +/// [SourceTemplate] +/// public static void something(this Entity x, [Macro(Expression = "guid()", Editable = -1)] string newguid) { +/// /*$ var $x$Id = "$newguid$" + x.ToString(); +/// x.DoSomething($x$Id); */ +/// } +/// +/// +[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method, AllowMultiple = true)] +internal sealed class MacroAttribute : Attribute +{ /// - /// An extension method marked with this attribute is processed by code completion - /// as a 'Source Template'. When the extension method is completed over some expression, its source code - /// is automatically expanded like a template at call site. + /// Allows specifying a macro that will be executed for a source template + /// parameter when the template is expanded. /// - /// - /// Template method body can contain valid source code and/or special comments starting with '$'. - /// Text inside these comments is added as source code when the template is applied. Template parameters - /// can be used either as additional method parameters or as identifiers wrapped in two '$' signs. - /// Use the attribute to specify macros for parameters. - /// - /// - /// In this example, the 'forEach' method is a source template available over all values - /// of enumerable types, producing ordinary C# 'foreach' statement and placing caret inside block: - /// - /// [SourceTemplate] - /// public static void forEach<T>(this IEnumerable<T> xs) { - /// foreach (var x in xs) { - /// //$ $END$ - /// } - /// } - /// - /// - [AttributeUsage(AttributeTargets.Method)] -internal sealed class SourceTemplateAttribute : Attribute { } + public string? Expression { get; set; } /// - /// Allows specifying a macro for a parameter of a source template. + /// Allows specifying which occurrence of the target parameter becomes editable when the template is deployed. /// /// - /// You can apply the attribute on the whole method or on any of its additional parameters. The macro expression - /// is defined in the property. When applied on a method, the target - /// template parameter is defined in the property. To apply the macro silently - /// for the parameter, set the property value = -1. + /// If the target parameter is used several times in the template, only one occurrence becomes editable; + /// other occurrences are changed synchronously. To specify the zero-based index of the editable occurrence, + /// use values >= 0. To make the parameter non-editable when the template is expanded, use -1. /// - /// - /// Applying the attribute on a source template method: - /// - /// [SourceTemplate, Macro(Target = "item", Expression = "suggestVariableName()")] - /// public static void forEach<T>(this IEnumerable<T> collection) { - /// foreach (var item in collection) { - /// //$ $END$ - /// } - /// } - /// - /// Applying the attribute on a template method parameter: - /// - /// [SourceTemplate] - /// public static void something(this Entity x, [Macro(Expression = "guid()", Editable = -1)] string newguid) { - /// /*$ var $x$Id = "$newguid$" + x.ToString(); - /// x.DoSomething($x$Id); */ - /// } - /// - /// - [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method, AllowMultiple = true)] -internal sealed class MacroAttribute : Attribute - { - /// - /// Allows specifying a macro that will be executed for a source template - /// parameter when the template is expanded. - /// - [CanBeNull] public string Expression { get; set; } - - /// - /// Allows specifying which occurrence of the target parameter becomes editable when the template is deployed. - /// - /// - /// If the target parameter is used several times in the template, only one occurrence becomes editable; - /// other occurrences are changed synchronously. To specify the zero-based index of the editable occurrence, - /// use values >= 0. To make the parameter non-editable when the template is expanded, use -1. - /// - public int Editable { get; set; } - - /// - /// Identifies the target parameter of a source template if the - /// is applied on a template method. - /// - [CanBeNull] public string Target { get; set; } - } + public int Editable { get; set; } - [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] + /// + /// Identifies the target parameter of a source template if the + /// is applied on a template method. + /// + public string? Target { get; set; } +} + +[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] internal sealed class AspMvcAreaMasterLocationFormatAttribute : Attribute +{ + public AspMvcAreaMasterLocationFormatAttribute(string format) { - public AspMvcAreaMasterLocationFormatAttribute([NotNull] string format) - { - Format = format; - } - - [NotNull] public string Format { get; } + Format = format; } - [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] + public string Format { get; } +} + +[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] internal sealed class AspMvcAreaPartialViewLocationFormatAttribute : Attribute +{ + public AspMvcAreaPartialViewLocationFormatAttribute(string format) { - public AspMvcAreaPartialViewLocationFormatAttribute([NotNull] string format) - { - Format = format; - } - - [NotNull] public string Format { get; } + Format = format; } - [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] + public string Format { get; } +} + +[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] internal sealed class AspMvcAreaViewLocationFormatAttribute : Attribute +{ + public AspMvcAreaViewLocationFormatAttribute(string format) { - public AspMvcAreaViewLocationFormatAttribute([NotNull] string format) - { - Format = format; - } - - [NotNull] public string Format { get; } + Format = format; } - [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] + public string Format { get; } +} + +[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] internal sealed class AspMvcMasterLocationFormatAttribute : Attribute +{ + public AspMvcMasterLocationFormatAttribute(string format) { - public AspMvcMasterLocationFormatAttribute([NotNull] string format) - { - Format = format; - } - - [NotNull] public string Format { get; } + Format = format; } - [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] + public string Format { get; } +} + +[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] internal sealed class AspMvcPartialViewLocationFormatAttribute : Attribute +{ + public AspMvcPartialViewLocationFormatAttribute(string format) { - public AspMvcPartialViewLocationFormatAttribute([NotNull] string format) - { - Format = format; - } - - [NotNull] public string Format { get; } + Format = format; } - [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] + public string Format { get; } +} + +[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] internal sealed class AspMvcViewLocationFormatAttribute : Attribute +{ + public AspMvcViewLocationFormatAttribute(string format) { - public AspMvcViewLocationFormatAttribute([NotNull] string format) - { - Format = format; - } - - [NotNull] public string Format { get; } + Format = format; } - /// - /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter - /// is an MVC action. If applied to a method, the MVC action name is calculated - /// implicitly from the context. Use this attribute for custom wrappers similar to - /// System.Web.Mvc.Html.ChildActionExtensions.RenderAction(HtmlHelper, String). - /// - [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)] -internal sealed class AspMvcActionAttribute : Attribute - { - public AspMvcActionAttribute() { } + public string Format { get; } +} - public AspMvcActionAttribute([NotNull] string anonymousProperty) - { - AnonymousProperty = anonymousProperty; - } +/// +/// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter +/// is an MVC action. If applied to a method, the MVC action name is calculated +/// implicitly from the context. Use this attribute for custom wrappers similar to +/// System.Web.Mvc.Html.ChildActionExtensions.RenderAction(HtmlHelper, String). +/// +[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)] +internal sealed class AspMvcActionAttribute : Attribute +{ + public AspMvcActionAttribute() { } - [CanBeNull] public string AnonymousProperty { get; } + public AspMvcActionAttribute(string anonymousProperty) + { + AnonymousProperty = anonymousProperty; } - /// - /// ASP.NET MVC attribute. Indicates that the marked parameter is an MVC area. - /// Use this attribute for custom wrappers similar to - /// System.Web.Mvc.Html.ChildActionExtensions.RenderAction(HtmlHelper, String). - /// - [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] -internal sealed class AspMvcAreaAttribute : Attribute - { - public AspMvcAreaAttribute() { } + public string? AnonymousProperty { get; } +} - public AspMvcAreaAttribute([NotNull] string anonymousProperty) - { - AnonymousProperty = anonymousProperty; - } +/// +/// ASP.NET MVC attribute. Indicates that the marked parameter is an MVC area. +/// Use this attribute for custom wrappers similar to +/// System.Web.Mvc.Html.ChildActionExtensions.RenderAction(HtmlHelper, String). +/// +[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] +internal sealed class AspMvcAreaAttribute : Attribute +{ + public AspMvcAreaAttribute() { } - [CanBeNull] public string AnonymousProperty { get; } + public AspMvcAreaAttribute(string anonymousProperty) + { + AnonymousProperty = anonymousProperty; } - /// - /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter is - /// an MVC controller. If applied to a method, the MVC controller name is calculated - /// implicitly from the context. Use this attribute for custom wrappers similar to - /// System.Web.Mvc.Html.ChildActionExtensions.RenderAction(HtmlHelper, String, String). - /// - [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)] -internal sealed class AspMvcControllerAttribute : Attribute - { - public AspMvcControllerAttribute() { } + public string? AnonymousProperty { get; } +} - public AspMvcControllerAttribute([NotNull] string anonymousProperty) - { - AnonymousProperty = anonymousProperty; - } +/// +/// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter is +/// an MVC controller. If applied to a method, the MVC controller name is calculated +/// implicitly from the context. Use this attribute for custom wrappers similar to +/// System.Web.Mvc.Html.ChildActionExtensions.RenderAction(HtmlHelper, String, String). +/// +[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)] +internal sealed class AspMvcControllerAttribute : Attribute +{ + public AspMvcControllerAttribute() { } - [CanBeNull] public string AnonymousProperty { get; } + public AspMvcControllerAttribute(string anonymousProperty) + { + AnonymousProperty = anonymousProperty; } - /// - /// ASP.NET MVC attribute. Indicates that the marked parameter is an MVC Master. Use this attribute - /// for custom wrappers similar to System.Web.Mvc.Controller.View(String, String). - /// - [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] + public string? AnonymousProperty { get; } +} + +/// +/// ASP.NET MVC attribute. Indicates that the marked parameter is an MVC Master. Use this attribute +/// for custom wrappers similar to System.Web.Mvc.Controller.View(String, String). +/// +[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] internal sealed class AspMvcMasterAttribute : Attribute { } - /// - /// ASP.NET MVC attribute. Indicates that the marked parameter is an MVC model type. Use this attribute - /// for custom wrappers similar to System.Web.Mvc.Controller.View(String, Object). - /// - [AttributeUsage(AttributeTargets.Parameter)] +/// +/// ASP.NET MVC attribute. Indicates that the marked parameter is an MVC model type. Use this attribute +/// for custom wrappers similar to System.Web.Mvc.Controller.View(String, Object). +/// +[AttributeUsage(AttributeTargets.Parameter)] internal sealed class AspMvcModelTypeAttribute : Attribute { } - /// - /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter is an MVC - /// partial view. If applied to a method, the MVC partial view name is calculated implicitly - /// from the context. Use this attribute for custom wrappers similar to - /// System.Web.Mvc.Html.RenderPartialExtensions.RenderPartial(HtmlHelper, String). - /// - [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)] +/// +/// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter is an MVC +/// partial view. If applied to a method, the MVC partial view name is calculated implicitly +/// from the context. Use this attribute for custom wrappers similar to +/// System.Web.Mvc.Html.RenderPartialExtensions.RenderPartial(HtmlHelper, String). +/// +[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)] internal sealed class AspMvcPartialViewAttribute : Attribute { } - /// - /// ASP.NET MVC attribute. Allows disabling inspections for MVC views within a class or a method. - /// - [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] +/// +/// ASP.NET MVC attribute. Allows disabling inspections for MVC views within a class or a method. +/// +[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] internal sealed class AspMvcSuppressViewErrorAttribute : Attribute { } - /// - /// ASP.NET MVC attribute. Indicates that a parameter is an MVC display template. - /// Use this attribute for custom wrappers similar to - /// System.Web.Mvc.Html.DisplayExtensions.DisplayForModel(HtmlHelper, String). - /// - [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] +/// +/// ASP.NET MVC attribute. Indicates that a parameter is an MVC display template. +/// Use this attribute for custom wrappers similar to +/// System.Web.Mvc.Html.DisplayExtensions.DisplayForModel(HtmlHelper, String). +/// +[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] internal sealed class AspMvcDisplayTemplateAttribute : Attribute { } - /// - /// ASP.NET MVC attribute. Indicates that the marked parameter is an MVC editor template. - /// Use this attribute for custom wrappers similar to - /// System.Web.Mvc.Html.EditorExtensions.EditorForModel(HtmlHelper, String). - /// - [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] +/// +/// ASP.NET MVC attribute. Indicates that the marked parameter is an MVC editor template. +/// Use this attribute for custom wrappers similar to +/// System.Web.Mvc.Html.EditorExtensions.EditorForModel(HtmlHelper, String). +/// +[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] internal sealed class AspMvcEditorTemplateAttribute : Attribute { } - /// - /// ASP.NET MVC attribute. Indicates that the marked parameter is an MVC template. - /// Use this attribute for custom wrappers similar to - /// System.ComponentModel.DataAnnotations.UIHintAttribute(System.String). - /// - [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] +/// +/// ASP.NET MVC attribute. Indicates that the marked parameter is an MVC template. +/// Use this attribute for custom wrappers similar to +/// System.ComponentModel.DataAnnotations.UIHintAttribute(System.String). +/// +[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] internal sealed class AspMvcTemplateAttribute : Attribute { } - /// - /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter - /// is an MVC view component. If applied to a method, the MVC view name is calculated implicitly - /// from the context. Use this attribute for custom wrappers similar to - /// System.Web.Mvc.Controller.View(Object). - /// - [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)] +/// +/// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter +/// is an MVC view component. If applied to a method, the MVC view name is calculated implicitly +/// from the context. Use this attribute for custom wrappers similar to +/// System.Web.Mvc.Controller.View(Object). +/// +[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)] internal sealed class AspMvcViewAttribute : Attribute { } - /// - /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter - /// is an MVC view component name. - /// - [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] +/// +/// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter +/// is an MVC view component name. +/// +[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] internal sealed class AspMvcViewComponentAttribute : Attribute { } - /// - /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter - /// is an MVC view component view. If applied to a method, the MVC view component view name is default. - /// - [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)] +/// +/// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter +/// is an MVC view component view. If applied to a method, the MVC view component view name is default. +/// +[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)] internal sealed class AspMvcViewComponentViewAttribute : Attribute { } - /// - /// ASP.NET MVC attribute. When applied to a parameter of an attribute, - /// indicates that this parameter is an MVC action name. - /// - /// - /// [ActionName("Foo")] - /// public ActionResult Login(string returnUrl) { - /// ViewBag.ReturnUrl = Url.Action("Foo"); // OK - /// return RedirectToAction("Bar"); // Error: Cannot resolve action - /// } - /// - [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property)] +/// +/// ASP.NET MVC attribute. When applied to a parameter of an attribute, +/// indicates that this parameter is an MVC action name. +/// +/// +/// [ActionName("Foo")] +/// public ActionResult Login(string returnUrl) { +/// ViewBag.ReturnUrl = Url.Action("Foo"); // OK +/// return RedirectToAction("Bar"); // Error: Cannot resolve action +/// } +/// +[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property)] internal sealed class AspMvcActionSelectorAttribute : Attribute { } - [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.Field)] +[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.Field)] internal sealed class HtmlElementAttributesAttribute : Attribute - { - public HtmlElementAttributesAttribute() { } - - public HtmlElementAttributesAttribute([NotNull] string name) - { - Name = name; - } +{ + public HtmlElementAttributesAttribute() { } - [CanBeNull] public string Name { get; } + public HtmlElementAttributesAttribute(string name) + { + Name = name; } - [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] + public string? Name { get; } +} + +[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] internal sealed class HtmlAttributeValueAttribute : Attribute +{ + public HtmlAttributeValueAttribute(string name) { - public HtmlAttributeValueAttribute([NotNull] string name) - { - Name = name; - } - - [NotNull] public string Name { get; } + Name = name; } - /// - /// Razor attribute. Indicates that the marked parameter or method is a Razor section. - /// Use this attribute for custom wrappers similar to - /// System.Web.WebPages.WebPageBase.RenderSection(String). - /// - [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)] + public string Name { get; } +} + +/// +/// Razor attribute. Indicates that the marked parameter or method is a Razor section. +/// Use this attribute for custom wrappers similar to +/// System.Web.WebPages.WebPageBase.RenderSection(String). +/// +[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)] internal sealed class RazorSectionAttribute : Attribute { } - /// - /// Indicates how method, constructor invocation, or property access - /// over collection type affects the contents of the collection. - /// Use to specify the access type. - /// - /// - /// Using this attribute only makes sense if all collection methods are marked with this attribute. - /// - /// - /// public class MyStringCollection : List<string> - /// { - /// [CollectionAccess(CollectionAccessType.Read)] - /// public string GetFirstString() - /// { - /// return this.ElementAt(0); - /// } - /// } - /// class Test - /// { - /// public void Foo() - /// { - /// // Warning: Contents of the collection is never updated - /// var col = new MyStringCollection(); - /// string x = col.GetFirstString(); - /// } - /// } - /// - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property)] +/// +/// Indicates how method, constructor invocation, or property access +/// over collection type affects the contents of the collection. +/// Use to specify the access type. +/// +/// +/// Using this attribute only makes sense if all collection methods are marked with this attribute. +/// +/// +/// public class MyStringCollection : List<string> +/// { +/// [CollectionAccess(CollectionAccessType.Read)] +/// public string GetFirstString() +/// { +/// return this.ElementAt(0); +/// } +/// } +/// class Test +/// { +/// public void Foo() +/// { +/// // Warning: Contents of the collection is never updated +/// var col = new MyStringCollection(); +/// string x = col.GetFirstString(); +/// } +/// } +/// +[AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property)] internal sealed class CollectionAccessAttribute : Attribute +{ + public CollectionAccessAttribute(CollectionAccessType collectionAccessType) { - public CollectionAccessAttribute(CollectionAccessType collectionAccessType) - { - CollectionAccessType = collectionAccessType; - } - - public CollectionAccessType CollectionAccessType { get; } + CollectionAccessType = collectionAccessType; } - /// - /// Provides a value for the to define - /// how the collection method invocation affects the contents of the collection. - /// - [Flags] + public CollectionAccessType CollectionAccessType { get; } +} + +/// +/// Provides a value for the to define +/// how the collection method invocation affects the contents of the collection. +/// +[Flags] internal enum CollectionAccessType - { - /// Method does not use or modify content of the collection. - None = 0, - /// Method only reads content of the collection but does not modify it. - Read = 1, - /// Method can change content of the collection but does not add new elements. - ModifyExistingContent = 2, - /// Method can add new elements to the collection. - UpdatedContent = ModifyExistingContent | 4 - } +{ + /// Method does not use or modify content of the collection. + None = 0, + /// Method only reads content of the collection but does not modify it. + Read = 1, + /// Method can change content of the collection but does not add new elements. + ModifyExistingContent = 2, + /// Method can add new elements to the collection. + UpdatedContent = ModifyExistingContent | 4 +} - /// - /// Indicates that the marked method is assertion method, i.e. it halts the control flow if - /// one of the conditions is satisfied. To set the condition, mark one of the parameters with - /// attribute. - /// - [AttributeUsage(AttributeTargets.Method)] +/// +/// Indicates that the marked method is assertion method, i.e. it halts the control flow if +/// one of the conditions is satisfied. To set the condition, mark one of the parameters with +/// attribute. +/// +[AttributeUsage(AttributeTargets.Method)] internal sealed class AssertionMethodAttribute : Attribute { } - /// - /// Indicates the condition parameter of the assertion method. The method itself should be - /// marked by attribute. The mandatory argument of - /// the attribute is the assertion type. - /// - [AttributeUsage(AttributeTargets.Parameter)] +/// +/// Indicates the condition parameter of the assertion method. The method itself should be +/// marked by attribute. The mandatory argument of +/// the attribute is the assertion type. +/// +[AttributeUsage(AttributeTargets.Parameter)] internal sealed class AssertionConditionAttribute : Attribute +{ + public AssertionConditionAttribute(AssertionConditionType conditionType) { - public AssertionConditionAttribute(AssertionConditionType conditionType) - { - ConditionType = conditionType; - } - - public AssertionConditionType ConditionType { get; } + ConditionType = conditionType; } - /// - /// Specifies assertion type. If the assertion method argument satisfies the condition, - /// then the execution continues. Otherwise, execution is assumed to be halted. - /// + public AssertionConditionType ConditionType { get; } +} + +/// +/// Specifies assertion type. If the assertion method argument satisfies the condition, +/// then the execution continues. Otherwise, execution is assumed to be halted. +/// internal enum AssertionConditionType - { - /// Marked parameter should be evaluated to true. - IS_TRUE = 0, - /// Marked parameter should be evaluated to false. - IS_FALSE = 1, - /// Marked parameter should be evaluated to null value. - IS_NULL = 2, - /// Marked parameter should be evaluated to not null value. - IS_NOT_NULL = 3, - } +{ + /// Marked parameter should be evaluated to true. + IS_TRUE = 0, + /// Marked parameter should be evaluated to false. + IS_FALSE = 1, + /// Marked parameter should be evaluated to null value. + IS_NULL = 2, + /// Marked parameter should be evaluated to not null value. + IS_NOT_NULL = 3, +} - /// - /// Indicates that the marked method unconditionally terminates control flow execution. - /// For example, it could unconditionally throw exception. - /// - [Obsolete("Use [ContractAnnotation('=> halt')] instead")] - [AttributeUsage(AttributeTargets.Method)] +/// +/// Indicates that the marked method unconditionally terminates control flow execution. +/// For example, it could unconditionally throw exception. +/// +[Obsolete("Use [ContractAnnotation('=> halt')] instead")] +[AttributeUsage(AttributeTargets.Method)] internal sealed class TerminatesProgramAttribute : Attribute { } - /// - /// Indicates that the method is a pure LINQ method, with postponed enumeration (like Enumerable.Select, - /// .Where). This annotation allows inference of [InstantHandle] annotation for parameters - /// of delegate type by analyzing LINQ method chains. - /// - [AttributeUsage(AttributeTargets.Method)] +/// +/// Indicates that the method is a pure LINQ method, with postponed enumeration (like Enumerable.Select, +/// .Where). This annotation allows inference of [InstantHandle] annotation for parameters +/// of delegate type by analyzing LINQ method chains. +/// +[AttributeUsage(AttributeTargets.Method)] internal sealed class LinqTunnelAttribute : Attribute { } - /// - /// Indicates that IEnumerable passed as a parameter is not enumerated. - /// Use this annotation to suppress the 'Possible multiple enumeration of IEnumerable' inspection. - /// - /// - /// static void ThrowIfNull<T>([NoEnumeration] T v, string n) where T : class - /// { - /// // custom check for null but no enumeration - /// } - /// - /// void Foo(IEnumerable<string> values) - /// { - /// ThrowIfNull(values, nameof(values)); - /// var x = values.ToList(); // No warnings about multiple enumeration - /// } - /// - [AttributeUsage(AttributeTargets.Parameter)] +/// +/// Indicates that IEnumerable passed as a parameter is not enumerated. +/// Use this annotation to suppress the 'Possible multiple enumeration of IEnumerable' inspection. +/// +/// +/// static void ThrowIfNull<T>([NoEnumeration] T v, string n) where T : class +/// { +/// // custom check for null but no enumeration +/// } +/// +/// void Foo(IEnumerable<string> values) +/// { +/// ThrowIfNull(values, nameof(values)); +/// var x = values.ToList(); // No warnings about multiple enumeration +/// } +/// +[AttributeUsage(AttributeTargets.Parameter)] internal sealed class NoEnumerationAttribute : Attribute { } - /// - /// Indicates that the marked parameter, field, or property is a regular expression pattern. - /// - [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] +/// +/// Indicates that the marked parameter, field, or property is a regular expression pattern. +/// +[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] internal sealed class RegexPatternAttribute : Attribute { } - /// - /// Prevents the Member Reordering feature from tossing members of the marked class. - /// - /// - /// The attribute must be mentioned in your member reordering patterns. - /// - [AttributeUsage( - AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct | AttributeTargets.Enum)] +/// +/// Prevents the Member Reordering feature from tossing members of the marked class. +/// +/// +/// The attribute must be mentioned in your member reordering patterns. +/// +[AttributeUsage( + AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct | AttributeTargets.Enum)] internal sealed class NoReorderAttribute : Attribute { } - /// - /// XAML attribute. Indicates the type that has ItemsSource property and should be treated - /// as ItemsControl-derived type, to enable inner items DataContext type resolve. - /// - [AttributeUsage(AttributeTargets.Class)] +/// +/// XAML attribute. Indicates the type that has ItemsSource property and should be treated +/// as ItemsControl-derived type, to enable inner items DataContext type resolve. +/// +[AttributeUsage(AttributeTargets.Class)] internal sealed class XamlItemsControlAttribute : Attribute { } - /// - /// XAML attribute. Indicates the property of some BindingBase-derived type, that - /// is used to bind some item of ItemsControl-derived type. This annotation will - /// enable the DataContext type resolve for XAML bindings for such properties. - /// - /// - /// Property should have the tree ancestor of the ItemsControl type or - /// marked with the attribute. - /// - [AttributeUsage(AttributeTargets.Property)] +/// +/// XAML attribute. Indicates the property of some BindingBase-derived type, that +/// is used to bind some item of ItemsControl-derived type. This annotation will +/// enable the DataContext type resolve for XAML bindings for such properties. +/// +/// +/// Property should have the tree ancestor of the ItemsControl type or +/// marked with the attribute. +/// +[AttributeUsage(AttributeTargets.Property)] internal sealed class XamlItemBindingOfItemsControlAttribute : Attribute { } - /// - /// XAML attribute. Indicates the property of some Style-derived type, that - /// is used to style items of ItemsControl-derived type. This annotation will - /// enable the DataContext type resolve for XAML bindings for such properties. - /// - /// - /// Property should have the tree ancestor of the ItemsControl type or - /// marked with the attribute. - /// - [AttributeUsage(AttributeTargets.Property)] +/// +/// XAML attribute. Indicates the property of some Style-derived type, that +/// is used to style items of ItemsControl-derived type. This annotation will +/// enable the DataContext type resolve for XAML bindings for such properties. +/// +/// +/// Property should have the tree ancestor of the ItemsControl type or +/// marked with the attribute. +/// +[AttributeUsage(AttributeTargets.Property)] internal sealed class XamlItemStyleOfItemsControlAttribute : Attribute { } - /// - /// XAML attribute. Indicates that DependencyProperty has OneWay binding mode by default. - /// - /// - /// This attribute must be applied to DependencyProperty's CLR accessor property if it is present, to DependencyProperty descriptor field otherwise. - /// - [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] +/// +/// XAML attribute. Indicates that DependencyProperty has OneWay binding mode by default. +/// +/// +/// This attribute must be applied to DependencyProperty's CLR accessor property if it is present, to DependencyProperty descriptor field otherwise. +/// +[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] internal sealed class XamlOneWayBindingModeByDefaultAttribute : Attribute { } - /// - /// XAML attribute. Indicates that DependencyProperty has TwoWay binding mode by default. - /// - /// - /// This attribute must be applied to DependencyProperty's CLR accessor property if it is present, to DependencyProperty descriptor field otherwise. - /// - [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] +/// +/// XAML attribute. Indicates that DependencyProperty has TwoWay binding mode by default. +/// +/// +/// This attribute must be applied to DependencyProperty's CLR accessor property if it is present, to DependencyProperty descriptor field otherwise. +/// +[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] internal sealed class XamlTwoWayBindingModeByDefaultAttribute : Attribute { } - [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] +[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] internal sealed class AspChildControlTypeAttribute : Attribute +{ + public AspChildControlTypeAttribute(string tagName, Type controlType) { - public AspChildControlTypeAttribute([NotNull] string tagName, [NotNull] Type controlType) - { - TagName = tagName; - ControlType = controlType; - } + TagName = tagName; + ControlType = controlType; + } - [NotNull] public string TagName { get; } + public string TagName { get; } - [NotNull] public Type ControlType { get; } - } + public Type ControlType { get; } +} - [AttributeUsage(AttributeTargets.Property | AttributeTargets.Method)] +[AttributeUsage(AttributeTargets.Property | AttributeTargets.Method)] internal sealed class AspDataFieldAttribute : Attribute { } - [AttributeUsage(AttributeTargets.Property | AttributeTargets.Method)] +[AttributeUsage(AttributeTargets.Property | AttributeTargets.Method)] internal sealed class AspDataFieldsAttribute : Attribute { } - [AttributeUsage(AttributeTargets.Property)] +[AttributeUsage(AttributeTargets.Property)] internal sealed class AspMethodPropertyAttribute : Attribute { } - [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] +[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] internal sealed class AspRequiredAttributeAttribute : Attribute +{ + public AspRequiredAttributeAttribute(string attribute) { - public AspRequiredAttributeAttribute([NotNull] string attribute) - { - Attribute = attribute; - } - - [NotNull] public string Attribute { get; } + Attribute = attribute; } - [AttributeUsage(AttributeTargets.Property)] + public string Attribute { get; } +} + +[AttributeUsage(AttributeTargets.Property)] internal sealed class AspTypePropertyAttribute : Attribute - { - public bool CreateConstructorReferences { get; } +{ + public bool CreateConstructorReferences { get; } - public AspTypePropertyAttribute(bool createConstructorReferences) - { - CreateConstructorReferences = createConstructorReferences; - } + public AspTypePropertyAttribute(bool createConstructorReferences) + { + CreateConstructorReferences = createConstructorReferences; } +} - [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] +[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class RazorImportNamespaceAttribute : Attribute +{ + public RazorImportNamespaceAttribute(string name) { - public RazorImportNamespaceAttribute([NotNull] string name) - { - Name = name; - } - - [NotNull] public string Name { get; } + Name = name; } - [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] + public string Name { get; } +} + +[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class RazorInjectionAttribute : Attribute +{ + public RazorInjectionAttribute(string type, string fieldName) { - public RazorInjectionAttribute([NotNull] string type, [NotNull] string fieldName) - { - Type = type; - FieldName = fieldName; - } + Type = type; + FieldName = fieldName; + } - [NotNull] public string Type { get; } + public string Type { get; } - [NotNull] public string FieldName { get; } - } + public string FieldName { get; } +} - [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] +[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class RazorDirectiveAttribute : Attribute +{ + public RazorDirectiveAttribute(string directive) { - public RazorDirectiveAttribute([NotNull] string directive) - { - Directive = directive; - } - - [NotNull] public string Directive { get; } + Directive = directive; } - [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] + public string Directive { get; } +} + +[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class RazorPageBaseTypeAttribute : Attribute +{ + public RazorPageBaseTypeAttribute(string baseType) { - public RazorPageBaseTypeAttribute([NotNull] string baseType) - { - BaseType = baseType; - } - public RazorPageBaseTypeAttribute([NotNull] string baseType, string pageName) - { - BaseType = baseType; - PageName = pageName; - } - - [NotNull] public string BaseType { get; } - [CanBeNull] public string PageName { get; } + BaseType = baseType; + } + public RazorPageBaseTypeAttribute(string baseType, string pageName) + { + BaseType = baseType; + PageName = pageName; } - [AttributeUsage(AttributeTargets.Method)] + public string BaseType { get; } + public string? PageName { get; } +} + +[AttributeUsage(AttributeTargets.Method)] internal sealed class RazorHelperCommonAttribute : Attribute { } - [AttributeUsage(AttributeTargets.Property)] +[AttributeUsage(AttributeTargets.Property)] internal sealed class RazorLayoutAttribute : Attribute { } - [AttributeUsage(AttributeTargets.Method)] +[AttributeUsage(AttributeTargets.Method)] internal sealed class RazorWriteLiteralMethodAttribute : Attribute { } - [AttributeUsage(AttributeTargets.Method)] +[AttributeUsage(AttributeTargets.Method)] internal sealed class RazorWriteMethodAttribute : Attribute { } - [AttributeUsage(AttributeTargets.Parameter)] +[AttributeUsage(AttributeTargets.Parameter)] internal sealed class RazorWriteMethodParameterAttribute : Attribute { } - /// - /// Indicates that the marked parameter, field, or property is a route template. - /// - /// - /// This attribute allows IDE to recognize the use of web frameworks' route templates - /// to enable syntax highlighting, code completion, navigation, rename and other features in string literals. - /// - [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] +/// +/// Indicates that the marked parameter, field, or property is a route template. +/// +/// +/// This attribute allows IDE to recognize the use of web frameworks' route templates +/// to enable syntax highlighting, code completion, navigation, rename and other features in string literals. +/// +[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] internal sealed class RouteTemplateAttribute : Attribute { } - /// - /// Indicates that the marked type is custom route parameter constraint, - /// which is registered in application's Startup with name ConstraintName - /// - /// - /// You can specify ProposedType if target constraint matches only route parameters of specific type, - /// it will allow IDE to create method's parameter from usage in route template - /// with specified type instead of default System.String - /// and check if constraint's proposed type conflicts with matched parameter's type - /// - [AttributeUsage(AttributeTargets.Class)] +/// +/// Indicates that the marked type is custom route parameter constraint, +/// which is registered in application's Startup with name ConstraintName +/// +/// +/// You can specify ProposedType if target constraint matches only route parameters of specific type, +/// it will allow IDE to create method's parameter from usage in route template +/// with specified type instead of default System.String +/// and check if constraint's proposed type conflicts with matched parameter's type +/// +[AttributeUsage(AttributeTargets.Class)] internal sealed class RouteParameterConstraintAttribute : Attribute - { - [NotNull] public string ConstraintName { get; } - [CanBeNull] public Type ProposedType { get; set; } +{ + public string ConstraintName { get; } + public Type? ProposedType { get; set; } - public RouteParameterConstraintAttribute([NotNull] string constraintName) - { - ConstraintName = constraintName; - } + public RouteParameterConstraintAttribute(string constraintName) + { + ConstraintName = constraintName; } +} - /// - /// Indicates that the marked parameter, field, or property is an URI string. - /// - /// - /// This attribute enables code completion, navigation, rename and other features - /// in URI string literals assigned to annotated parameter, field or property. - /// - [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] +/// +/// Indicates that the marked parameter, field, or property is an URI string. +/// +/// +/// This attribute enables code completion, navigation, rename and other features +/// in URI string literals assigned to annotated parameter, field or property. +/// +[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] internal sealed class UriStringAttribute : Attribute - { - public UriStringAttribute() { } - - public UriStringAttribute(string httpVerb) - { - HttpVerb = httpVerb; - } +{ + public UriStringAttribute() { } - [CanBeNull] public string HttpVerb { get; } + public UriStringAttribute(string httpVerb) + { + HttpVerb = httpVerb; } -} + + public string? HttpVerb { get; } +} \ No newline at end of file diff --git a/SharpNBT/NbtFile.cs b/SharpNBT/NbtFile.cs index 901ea09..36606a4 100644 --- a/SharpNBT/NbtFile.cs +++ b/SharpNBT/NbtFile.cs @@ -3,183 +3,181 @@ using System.IO.Compression; using System.Threading.Tasks; using JetBrains.Annotations; -using SharpNBT.ZLib; -namespace SharpNBT +namespace SharpNBT; + +/// +/// Provides static convenience methods for working with NBT-formatted files, including both reading and writing. +/// +[PublicAPI] +public static class NbtFile { + /// - /// Provides static convenience methods for working with NBT-formatted files, including both reading and writing. + /// Reads a file at the given and deserializes the top-level contained in the file. /// - [PublicAPI] - public static class NbtFile + /// The path to the file to be read. + /// Indicates the compression algorithm used to compress the file. + /// Bitwise flags to configure how data should be handled for compatibility between different specifications. + /// The type of tag to deserialize. + /// The deserialized instance. + public static T Read(string path, FormatOptions options, CompressionType compression = CompressionType.AutoDetect) where T : TagContainer { - - /// - /// Reads a file at the given and deserializes the top-level contained in the file. - /// - /// The path to the file to be read. - /// Indicates the compression algorithm used to compress the file. - /// Bitwise flags to configure how data should be handled for compatibility between different specifications. - /// The type of tag to deserialize. - /// The deserialized instance. - public static T Read([NotNull] string path, FormatOptions options, CompressionType compression = CompressionType.AutoDetect) where T : TagContainer - { - using var reader = new TagReader(GetReadStream(path, compression), options); - return reader.ReadTag(); - } + using var reader = new TagReader(GetReadStream(path, compression), options); + return reader.ReadTag(); + } - /// - /// Reads a file at the given and deserializes the top-level contained in the file. - /// - /// The path to the file to be read. - /// Indicates the compression algorithm used to compress the file. - /// Bitwise flags to configure how data should be handled for compatibility between different specifications. - /// The deserialized instance. - public static CompoundTag Read([NotNull] string path, FormatOptions options, CompressionType compression = CompressionType.AutoDetect) - { - using var reader = new TagReader(GetReadStream(path, compression), options); - return reader.ReadTag(); - } + /// + /// Reads a file at the given and deserializes the top-level contained in the file. + /// + /// The path to the file to be read. + /// Indicates the compression algorithm used to compress the file. + /// Bitwise flags to configure how data should be handled for compatibility between different specifications. + /// The deserialized instance. + public static CompoundTag Read(string path, FormatOptions options, CompressionType compression = CompressionType.AutoDetect) + { + using var reader = new TagReader(GetReadStream(path, compression), options); + return reader.ReadTag(); + } - /// - /// Asynchronously reads a file at the given and deserializes the top-level contained in the file. - /// - /// The path to the file to be read. - /// Indicates the compression algorithm used to compress the file. - /// Bitwise flags to configure how data should be handled for compatibility between different specifications. - /// The deserialized instance. - public static async Task ReadAsync([NotNull] string path, FormatOptions options, CompressionType compression = CompressionType.AutoDetect) where T : TagContainer - { - await using var reader = new TagReader(GetReadStream(path, compression), options); - return await reader.ReadTagAsync(); - } + /// + /// Asynchronously reads a file at the given and deserializes the top-level contained in the file. + /// + /// The path to the file to be read. + /// Indicates the compression algorithm used to compress the file. + /// Bitwise flags to configure how data should be handled for compatibility between different specifications. + /// The deserialized instance. + public static async Task ReadAsync(string path, FormatOptions options, CompressionType compression = CompressionType.AutoDetect) where T : TagContainer + { + await using var reader = new TagReader(GetReadStream(path, compression), options); + return await reader.ReadTagAsync(); + } - /// - /// Asynchronously reads a file at the given and deserializes the top-level contained in the file. - /// - /// The path to the file to be read. - /// Indicates the compression algorithm used to compress the file. - /// Bitwise flags to configure how data should be handled for compatibility between different specifications. - /// The deserialized instance. - public static async Task ReadAsync([NotNull] string path, FormatOptions options, CompressionType compression = CompressionType.AutoDetect) - { - await using var reader = new TagReader(GetReadStream(path, compression), options); - return await reader.ReadTagAsync(); - } + /// + /// Asynchronously reads a file at the given and deserializes the top-level contained in the file. + /// + /// The path to the file to be read. + /// Indicates the compression algorithm used to compress the file. + /// Bitwise flags to configure how data should be handled for compatibility between different specifications. + /// The deserialized instance. + public static async Task ReadAsync(string path, FormatOptions options, CompressionType compression = CompressionType.AutoDetect) + { + await using var reader = new TagReader(GetReadStream(path, compression), options); + return await reader.ReadTagAsync(); + } - /// - /// Writes the given to a file at the specified . - /// - /// The path to the file to be written to. - /// The top-level instance to be serialized. - /// Bitwise flags to configure how data should be handled for compatibility between different specifications. - /// A flag indicating the type of compression to use. - /// Indicates a compression strategy to be used, if any. - public static void Write([NotNull] string path, [NotNull] CompoundTag tag, FormatOptions options, CompressionType type = CompressionType.GZip, CompressionLevel level = CompressionLevel.Fastest) - { - using var stream = File.OpenWrite(path); - using var writer = new TagWriter(GetWriteStream(stream, type, level), options); - writer.WriteTag(tag); - } + /// + /// Writes the given to a file at the specified . + /// + /// The path to the file to be written to. + /// The top-level instance to be serialized. + /// Bitwise flags to configure how data should be handled for compatibility between different specifications. + /// A flag indicating the type of compression to use. + /// Indicates a compression strategy to be used, if any. + public static void Write(string path, CompoundTag tag, FormatOptions options, CompressionType type = CompressionType.GZip, CompressionLevel level = CompressionLevel.Fastest) + { + using var stream = File.OpenWrite(path); + using var writer = new TagWriter(GetWriteStream(stream, type, level), options); + writer.WriteTag(tag); + } - /// - public static void Write([NotNull] string path, [NotNull] ListTag tag, FormatOptions options, CompressionType type = CompressionType.GZip, CompressionLevel level = CompressionLevel.Fastest) - { - using var stream = File.OpenWrite(path); - using var writer = new TagWriter(GetWriteStream(stream, type, level), options); - writer.WriteTag(tag); - } + /// + public static void Write(string path, ListTag tag, FormatOptions options, CompressionType type = CompressionType.GZip, CompressionLevel level = CompressionLevel.Fastest) + { + using var stream = File.OpenWrite(path); + using var writer = new TagWriter(GetWriteStream(stream, type, level), options); + writer.WriteTag(tag); + } - /// - /// Asynchronously writes the given to a file at the specified . - /// - /// The path to the file to be written to. - /// The top-level instance to be serialized. - /// Bitwise flags to configure how data should be handled for compatibility between different specifications. - /// A flag indicating the type of compression to use. - /// Indicates a compression strategy to be used, if any. - public static async Task WriteAsync([NotNull] string path, [NotNull] CompoundTag tag, FormatOptions options, CompressionType type = CompressionType.GZip, CompressionLevel level = CompressionLevel.Fastest) - { - await using var stream = File.OpenWrite(path); - await using var writer = new TagWriter(GetWriteStream(stream, type, level), options); - await writer.WriteTagAsync(tag); - } + /// + /// Asynchronously writes the given to a file at the specified . + /// + /// The path to the file to be written to. + /// The top-level instance to be serialized. + /// Bitwise flags to configure how data should be handled for compatibility between different specifications. + /// A flag indicating the type of compression to use. + /// Indicates a compression strategy to be used, if any. + public static async Task WriteAsync(string path, CompoundTag tag, FormatOptions options, CompressionType type = CompressionType.GZip, CompressionLevel level = CompressionLevel.Fastest) + { + await using var stream = File.OpenWrite(path); + await using var writer = new TagWriter(GetWriteStream(stream, type, level), options); + await writer.WriteTagAsync(tag); + } - /// - public static async Task WriteAsync([NotNull] string path, [NotNull] ListTag tag, FormatOptions options, CompressionType type = CompressionType.GZip, CompressionLevel level = CompressionLevel.Fastest) - { - await using var stream = File.OpenWrite(path); - await using var writer = new TagWriter(GetWriteStream(stream, type, level), options); - await writer.WriteTagAsync(tag); - } + /// + public static async Task WriteAsync(string path, ListTag tag, FormatOptions options, CompressionType type = CompressionType.GZip, CompressionLevel level = CompressionLevel.Fastest) + { + await using var stream = File.OpenWrite(path); + await using var writer = new TagWriter(GetWriteStream(stream, type, level), options); + await writer.WriteTagAsync(tag); + } - /// - /// Opens an existing NBT file for reading, and returns a instance for it. - /// - /// The path of the file to query write. - /// Bitwise flags to configure how data should be handled for compatibility between different specifications. - /// Indicates the compression algorithm used to compress the file. - /// A instance for the file stream. - /// File compression will be automatically detected and used handled when necessary. - public static TagReader OpenRead([NotNull] string path, FormatOptions options, CompressionType compression = CompressionType.AutoDetect) - { - return new TagReader(GetReadStream(path, compression), options); - } + /// + /// Opens an existing NBT file for reading, and returns a instance for it. + /// + /// The path of the file to query write. + /// Bitwise flags to configure how data should be handled for compatibility between different specifications. + /// Indicates the compression algorithm used to compress the file. + /// A instance for the file stream. + /// File compression will be automatically detected and used handled when necessary. + public static TagReader OpenRead(string path, FormatOptions options, CompressionType compression = CompressionType.AutoDetect) + { + return new TagReader(GetReadStream(path, compression), options); + } - /// - /// Opens an existing NBT file or creates a new one if one if it does not exist, and returns a instance for it. - /// - /// The path of the file to query write. - /// Bitwise flags to configure how data should be handled for compatibility between different specifications. - /// A flag indicating the type of compression to use. - /// A flag indicating the compression strategy that will be used, if any. - /// A instance for the file stream. - public static TagWriter OpenWrite([NotNull] string path, FormatOptions options, CompressionType type = CompressionType.GZip, CompressionLevel level = CompressionLevel.Fastest) - { - var stream = GetWriteStream(File.OpenWrite(path), type, level); - return new TagWriter(stream, options); - } + /// + /// Opens an existing NBT file or creates a new one if one if it does not exist, and returns a instance for it. + /// + /// The path of the file to query write. + /// Bitwise flags to configure how data should be handled for compatibility between different specifications. + /// A flag indicating the type of compression to use. + /// A flag indicating the compression strategy that will be used, if any. + /// A instance for the file stream. + public static TagWriter OpenWrite(string path, FormatOptions options, CompressionType type = CompressionType.GZip, CompressionLevel level = CompressionLevel.Fastest) + { + var stream = GetWriteStream(File.OpenWrite(path), type, level); + return new TagWriter(stream, options); + } - private static Stream GetWriteStream(Stream stream, CompressionType type, CompressionLevel level) + private static Stream GetWriteStream(Stream stream, CompressionType type, CompressionLevel level) + { + switch (type) { - switch (type) - { - case CompressionType.None: return stream; - case CompressionType.GZip: return new GZipStream(stream, level, false); - case CompressionType.ZLib: return new ZLibStream(stream, level); - case CompressionType.AutoDetect: - throw new ArgumentOutOfRangeException(nameof(type), Strings.AutoDetectNotValid); - default: - throw new ArgumentOutOfRangeException(nameof(type), type, null); - } + case CompressionType.None: return stream; + case CompressionType.GZip: return new GZipStream(stream, level, false); + case CompressionType.ZLib: return new ZLibStream(stream, level); + case CompressionType.AutoDetect: + throw new ArgumentOutOfRangeException(nameof(type), Strings.AutoDetectNotValid); + default: + throw new ArgumentOutOfRangeException(nameof(type), type, null); } + } - private static Stream GetReadStream(string path, CompressionType compression) + private static Stream GetReadStream(string path, CompressionType compression) + { + var stream = File.OpenRead(path); + if (compression == CompressionType.AutoDetect) { - var stream = File.OpenRead(path); - if (compression == CompressionType.AutoDetect) - { - var firstByte = (byte) stream.ReadByte(); - stream.Seek(0, SeekOrigin.Begin); - - compression = firstByte switch - { - 0x78 => CompressionType.ZLib, - 0x1F => CompressionType.GZip, - 0x08 => CompressionType.None, // ListTag (valid in Bedrock) - 0x0A => CompressionType.None, // CompoundTag - _ => throw new FormatException(Strings.CannotDetectCompression) - }; - } + var firstByte = (byte) stream.ReadByte(); + stream.Seek(0, SeekOrigin.Begin); - return compression switch + compression = firstByte switch { - CompressionType.None => stream, - CompressionType.GZip => new GZipStream(stream, CompressionMode.Decompress, false), - CompressionType.ZLib => new ZLibStream(stream, CompressionMode.Decompress), - _ => throw new ArgumentOutOfRangeException(nameof(compression), compression, null) + 0x78 => CompressionType.ZLib, + 0x1F => CompressionType.GZip, + 0x08 => CompressionType.None, // ListTag (valid in Bedrock) + 0x0A => CompressionType.None, // CompoundTag + _ => throw new FormatException(Strings.CannotDetectCompression) }; } - + + return compression switch + { + CompressionType.None => stream, + CompressionType.GZip => new GZipStream(stream, CompressionMode.Decompress, false), + CompressionType.ZLib => new ZLibStream(stream, CompressionMode.Decompress), + _ => throw new ArgumentOutOfRangeException(nameof(compression), compression, null) + }; } + } \ No newline at end of file diff --git a/SharpNBT/SNBT/Lexer.cs b/SharpNBT/SNBT/Lexer.cs index 03fb767..7fc40f7 100644 --- a/SharpNBT/SNBT/Lexer.cs +++ b/SharpNBT/SNBT/Lexer.cs @@ -1,50 +1,49 @@ using System.Collections.Generic; using System.Data; -namespace SharpNBT.SNBT +namespace SharpNBT.SNBT; + +internal sealed class Lexer { - internal sealed class Lexer - { - private readonly List ruleList; + private readonly List ruleList; - public Lexer() - { - ruleList = new List(); - } + public Lexer() + { + ruleList = new List(); + } - public void AddRule(TokenType type, string pattern, bool skipped = false) => ruleList.Add(new LexerRule(type, pattern, null, skipped)); + public void AddRule(TokenType type, string pattern, bool skipped = false) => ruleList.Add(new LexerRule(type, pattern, null, skipped)); - public void AddRule(TokenType type, string pattern, ResultHandler handler, bool skipped = false) - { - ruleList.Add(new LexerRule(type, pattern, handler, skipped)); - } + public void AddRule(TokenType type, string pattern, ResultHandler handler, bool skipped = false) + { + ruleList.Add(new LexerRule(type, pattern, handler, skipped)); + } - public IEnumerable Tokenize(string source) + public IEnumerable Tokenize(string source) + { + var index = 0; + while (index < source.Length) { - var index = 0; - while (index < source.Length) - { - var success = false; + var success = false; - foreach (var rule in ruleList) - { - var match = rule.Pattern.Match(source, index); - if (!match.Success || match.Index - index != 0) - continue; + foreach (var rule in ruleList) + { + var match = rule.Pattern.Match(source, index); + if (!match.Success || match.Index - index != 0) + continue; - if (!rule.IsSkipped) - yield return new Token(rule.Type, rule.Process(source, index, match)); + if (!rule.IsSkipped) + yield return new Token(rule.Type, rule.Process(source, index, match)); - index += match.Length; - success = true; - break; - } - - if (!success) - throw new SyntaxErrorException($"Unrecognized sequence at index {index}: '{source[index]}'"); + index += match.Length; + success = true; + break; } + + if (!success) + throw new SyntaxErrorException($"Unrecognized sequence at index {index}: '{source[index]}'"); } + } - } } \ No newline at end of file diff --git a/SharpNBT/SNBT/LexerRule.cs b/SharpNBT/SNBT/LexerRule.cs index f282754..d1d4e3f 100644 --- a/SharpNBT/SNBT/LexerRule.cs +++ b/SharpNBT/SNBT/LexerRule.cs @@ -1,37 +1,35 @@ using System; using System.Text.RegularExpressions; -namespace SharpNBT.SNBT -{ +namespace SharpNBT.SNBT; - internal delegate string ResultHandler(Match match); +internal delegate string ResultHandler(Match match); - internal class LexerRule - { - private readonly ResultHandler processResult; +internal class LexerRule +{ + private readonly ResultHandler processResult; - public TokenType Type { get; } + public TokenType Type { get; } - public Regex Pattern { get; } + public Regex Pattern { get; } - public bool IsSkipped { get; } + public bool IsSkipped { get; } - public LexerRule(TokenType type, string pattern, bool skipped = false) : this(type, pattern, null, skipped) - { - } + public LexerRule(TokenType type, string pattern, bool skipped = false) : this(type, pattern, null, skipped) + { + } - public LexerRule(TokenType type, string pattern, ResultHandler handler, bool skipped = false) - { - Type = type; - Pattern = new Regex(pattern, RegexOptions.Compiled); - IsSkipped = skipped; - processResult = handler; - } + public LexerRule(TokenType type, string pattern, ResultHandler handler, bool skipped = false) + { + Type = type; + Pattern = new Regex(pattern, RegexOptions.Compiled); + IsSkipped = skipped; + processResult = handler; + } - public string Process(string source, int index, Match match) - { - return processResult is null ? source.Substring(index, match.Length) : processResult.Invoke(match); - } + public string Process(string source, int index, Match match) + { + return processResult is null ? source.Substring(index, match.Length) : processResult.Invoke(match); } } \ No newline at end of file diff --git a/SharpNBT/SNBT/StringNbt.cs b/SharpNBT/SNBT/StringNbt.cs index d8fe429..7d562ff 100644 --- a/SharpNBT/SNBT/StringNbt.cs +++ b/SharpNBT/SNBT/StringNbt.cs @@ -7,244 +7,239 @@ using System.Threading.Tasks; using JetBrains.Annotations; -namespace SharpNBT.SNBT +namespace SharpNBT.SNBT; + +/// +/// Provides static methods for parsing string-NBT (SNBT) source text into a complete . +/// +[PublicAPI] +public static class StringNbt { + private static readonly Lexer lexer; + + static StringNbt() + { + lexer = new Lexer(); + lexer.AddRule(TokenType.Whitespace, @"(\r|\t|\v|\f|\s)+?", true); + lexer.AddRule(TokenType.Separator, ",", true); + lexer.AddRule(TokenType.Compound, @"{"); + lexer.AddRule(TokenType.EndCompound, @"}"); + lexer.AddRule(TokenType.Identifier, "\"(.*?)\"\\s*(?>:)", FirstGroupValue); + lexer.AddRule(TokenType.Identifier, "'(.*?)'\\s*(?>:)", FirstGroupValue); + lexer.AddRule(TokenType.Identifier, "([A-Za-z0-9_-]+)\\s*(?>:)", FirstGroupValue); + lexer.AddRule(TokenType.String, "\"(.*?)\"", FirstGroupValue); + lexer.AddRule(TokenType.String, "'(.*?)'", FirstGroupValue); + lexer.AddRule(TokenType.ByteArray, @"\[B;"); + lexer.AddRule(TokenType.IntArray, @"\[I;"); + lexer.AddRule(TokenType.LongArray, @"\[L;"); + lexer.AddRule(TokenType.List, @"\["); + lexer.AddRule(TokenType.EndArray, @"\]"); + lexer.AddRule(TokenType.Float, @"(-?[0-9]*\.[0-9]+)[Ff]", FirstGroupValue); + lexer.AddRule(TokenType.Double, @"(-?[0-9]*\.[0-9]+)[Dd]?", FirstGroupValue); + lexer.AddRule(TokenType.Bool, "(true|false)", FirstGroupValue); + lexer.AddRule(TokenType.Byte, "(-?[0-9]+)[Bb]", FirstGroupValue); + lexer.AddRule(TokenType.Short, "(-?[0-9]+)[Ss]", FirstGroupValue); + lexer.AddRule(TokenType.Long, "(-?[0-9]+)[Ll]", FirstGroupValue); + lexer.AddRule(TokenType.Int, "(-?[0-9]+)", FirstGroupValue); + } + /// - /// Provides static methods for parsing string-NBT (SNBT) source text into a complete . + /// Parse the text in the given into a . /// - [PublicAPI] - public static class StringNbt + /// A containing the SNBT data. + /// The number of bytes to read from the , advancing its position. + /// The instance described in the source text. + /// When is . + /// When is not opened for reading. + /// When is negative. + /// When contains invalid SNBT code. + public static CompoundTag Parse(Stream stream, int length) { - private static readonly Lexer lexer; - - static StringNbt() - { - lexer = new Lexer(); - lexer.AddRule(TokenType.Whitespace, @"(\r|\t|\v|\f|\s)+?", true); - lexer.AddRule(TokenType.Separator, ",", true); - lexer.AddRule(TokenType.Compound, @"{"); - lexer.AddRule(TokenType.EndCompound, @"}"); - lexer.AddRule(TokenType.Identifier, "\"(.*?)\"\\s*(?>:)", FirstGroupValue); - lexer.AddRule(TokenType.Identifier, "'(.*?)'\\s*(?>:)", FirstGroupValue); - lexer.AddRule(TokenType.Identifier, "([A-Za-z0-9_-]+)\\s*(?>:)", FirstGroupValue); - lexer.AddRule(TokenType.String, "\"(.*?)\"", FirstGroupValue); - lexer.AddRule(TokenType.String, "'(.*?)'", FirstGroupValue); - lexer.AddRule(TokenType.ByteArray, @"\[B;"); - lexer.AddRule(TokenType.IntArray, @"\[I;"); - lexer.AddRule(TokenType.LongArray, @"\[L;"); - lexer.AddRule(TokenType.List, @"\["); - lexer.AddRule(TokenType.EndArray, @"\]"); - lexer.AddRule(TokenType.Float, @"(-?[0-9]*\.[0-9]+)[Ff]", FirstGroupValue); - lexer.AddRule(TokenType.Double, @"(-?[0-9]*\.[0-9]+)[Dd]?", FirstGroupValue); - lexer.AddRule(TokenType.Bool, "(true|false)", FirstGroupValue); - lexer.AddRule(TokenType.Byte, "(-?[0-9]+)[Bb]", FirstGroupValue); - lexer.AddRule(TokenType.Short, "(-?[0-9]+)[Ss]", FirstGroupValue); - lexer.AddRule(TokenType.Long, "(-?[0-9]+)[Ll]", FirstGroupValue); - lexer.AddRule(TokenType.Int, "(-?[0-9]+)", FirstGroupValue); - } - - /// - /// Parse the text in the given into a . - /// - /// A containing the SNBT data. - /// The number of bytes to read from the , advancing its position. - /// The instance described in the source text. - /// When is . - /// When is not opened for reading. - /// When is negative. - /// When contains invalid SNBT code. - [NotNull] - public static CompoundTag Parse(Stream stream, int length) - { - Validate(stream, length); - if (length == 0) - return new CompoundTag(null); + Validate(stream, length); + if (length == 0) + return new CompoundTag(null); - var buffer = new byte[length]; - stream.Read(buffer, 0, length); - var str = Encoding.UTF8.GetString(buffer, 0, buffer.Length); + var buffer = new byte[length]; + stream.Read(buffer, 0, length); + var str = Encoding.UTF8.GetString(buffer, 0, buffer.Length); - return Parse(str); - } + return Parse(str); + } - /// - /// Asynchronously parses the text in the given into a . - /// - /// A containing the SNBT data. - /// The number of bytes to read from the , advancing its position. - /// The instance described in the source text. - /// When is . - /// When is not opened for reading. - /// When is negative. - /// When contains invalid SNBT code. - [NotNull] - public static async Task ParseAsync(Stream stream, int length) - { - Validate(stream, length); - if (length == 0) - return new CompoundTag(null); + /// + /// Asynchronously parses the text in the given into a . + /// + /// A containing the SNBT data. + /// The number of bytes to read from the , advancing its position. + /// The instance described in the source text. + /// When is . + /// When is not opened for reading. + /// When is negative. + /// When contains invalid SNBT code. + public static async Task ParseAsync(Stream stream, int length) + { + Validate(stream, length); + if (length == 0) + return new CompoundTag(null); - var buffer = new byte[length]; - await stream.ReadAsync(buffer, 0, length); - var str = Encoding.UTF8.GetString(buffer, 0, buffer.Length); + var buffer = new byte[length]; + await stream.ReadAsync(buffer, 0, length); + var str = Encoding.UTF8.GetString(buffer, 0, buffer.Length); - return Parse(str); - } + return Parse(str); + } - private static void Validate(Stream stream, int length) - { - if (stream is null) - throw new ArgumentNullException(nameof(stream)); - if (!stream.CanRead) - throw new IOException("Stream is not opened for reading."); + private static void Validate(Stream stream, int length) + { + if (stream is null) + throw new ArgumentNullException(nameof(stream)); + if (!stream.CanRead) + throw new IOException("Stream is not opened for reading."); - if (length < 0) - throw new ArgumentException(Strings.NegativeLengthSpecified, nameof(length)); - } + if (length < 0) + throw new ArgumentException(Strings.NegativeLengthSpecified, nameof(length)); + } - /// - /// Parse the given text into a . - /// - /// A string containing the SNBT code to parse. - /// The instance described in the source text. - /// When is . - /// When is invalid SNBT code. - [NotNull] - public static CompoundTag Parse([NotNull] string source) - { - if (source is null) - throw new ArgumentNullException(nameof(source)); + /// + /// Parse the given text into a . + /// + /// A string containing the SNBT code to parse. + /// The instance described in the source text. + /// When is . + /// When is invalid SNBT code. + public static CompoundTag Parse(string source) + { + if (source is null) + throw new ArgumentNullException(nameof(source)); - if (string.IsNullOrWhiteSpace(source)) - return new CompoundTag(null); + if (string.IsNullOrWhiteSpace(source)) + return new CompoundTag(null); - var queue = new Queue(lexer.Tokenize(source)); - return Parse(queue); - } + var queue = new Queue(lexer.Tokenize(source)); + return Parse(queue); + } - private static T Parse(Queue queue) where T : Tag => (T)Parse(queue); + private static T Parse(Queue queue) where T : Tag => (T)Parse(queue); - private static Tag Parse(Queue queue) - { - string name = null; - var token = MoveNext(queue); - - if (token.Type == TokenType.Identifier) - { - name = token.Value; - token = MoveNext(queue); - } + private static Tag Parse(Queue queue) + { + string name = null; + var token = MoveNext(queue); - return token.Type switch - { - TokenType.Compound => ParseCompound(name, queue), - TokenType.String => new StringTag(name, token.Value), - TokenType.ByteArray => ParseByteArray(name, queue), - TokenType.IntArray => ParseIntArray(name, queue), - TokenType.LongArray => ParseLongArray(name, queue), - TokenType.List => ParseList(name, queue), - TokenType.Bool => new BoolTag(name, bool.Parse(token.Value)), - TokenType.Byte => new ByteTag(name, sbyte.Parse(token.Value)), - TokenType.Short => new ShortTag(name, short.Parse(token.Value)), - TokenType.Int => new IntTag(name, int.Parse(token.Value)), - TokenType.Long => new LongTag(name, long.Parse(token.Value)), - TokenType.Float => new FloatTag(name, float.Parse(token.Value)), - TokenType.Double => new DoubleTag(name, double.Parse(token.Value)), - _ => throw new SyntaxErrorException() - }; + if (token.Type == TokenType.Identifier) + { + name = token.Value; + token = MoveNext(queue); } - - [NotNull] - private static Token MoveNext(Queue queue) + + return token.Type switch { - if (queue.TryDequeue(out var token)) - return token; + TokenType.Compound => ParseCompound(name, queue), + TokenType.String => new StringTag(name, token.Value), + TokenType.ByteArray => ParseByteArray(name, queue), + TokenType.IntArray => ParseIntArray(name, queue), + TokenType.LongArray => ParseLongArray(name, queue), + TokenType.List => ParseList(name, queue), + TokenType.Bool => new BoolTag(name, bool.Parse(token.Value)), + TokenType.Byte => new ByteTag(name, sbyte.Parse(token.Value)), + TokenType.Short => new ShortTag(name, short.Parse(token.Value)), + TokenType.Int => new IntTag(name, int.Parse(token.Value)), + TokenType.Long => new LongTag(name, long.Parse(token.Value)), + TokenType.Float => new FloatTag(name, float.Parse(token.Value)), + TokenType.Double => new DoubleTag(name, double.Parse(token.Value)), + _ => throw new SyntaxErrorException() + }; + } + + private static Token MoveNext(Queue queue) + { + if (queue.TryDequeue(out var token)) + return token; - throw new SyntaxErrorException("Unexpected end-of-input"); - } + throw new SyntaxErrorException("Unexpected end-of-input"); + } - private static void MoveNext(Queue queue, TokenType assertType) - { - var token = MoveNext(queue); - if (token.Type != assertType) - throw new SyntaxErrorException($"Expected token of type {assertType}, but encountered {token.Type}."); - } + private static void MoveNext(Queue queue, TokenType assertType) + { + var token = MoveNext(queue); + if (token.Type != assertType) + throw new SyntaxErrorException($"Expected token of type {assertType}, but encountered {token.Type}."); + } - private static CompoundTag ParseCompound(string name, Queue queue) + private static CompoundTag ParseCompound(string name, Queue queue) + { + var compound = new CompoundTag(name); + while (queue.TryPeek(out var token) && token.Type != TokenType.EndCompound) { - var compound = new CompoundTag(name); - while (queue.TryPeek(out var token) && token.Type != TokenType.EndCompound) - { - compound.Add(Parse(queue)); - } - MoveNext(queue, TokenType.EndCompound); - return compound; + compound.Add(Parse(queue)); } + MoveNext(queue, TokenType.EndCompound); + return compound; + } - private static ListTag ParseList(string name, Queue queue) + private static ListTag ParseList(string name, Queue queue) + { + var values = new List(); + while (queue.TryPeek(out var token) && token.Type != TokenType.EndArray) { - var values = new List(); - while (queue.TryPeek(out var token) && token.Type != TokenType.EndArray) - { - values.Add(Parse(queue)); - } + values.Add(Parse(queue)); + } - MoveNext(queue, TokenType.EndArray); - if (values.Count > 0) - { - var type = values[0].Type; - return new ListTag(name, type, values); - } - return new ListTag(name, TagType.End); + MoveNext(queue, TokenType.EndArray); + if (values.Count > 0) + { + var type = values[0].Type; + return new ListTag(name, type, values); } + return new ListTag(name, TagType.End); + } - private static ByteArrayTag ParseByteArray(string name, Queue queue) + private static ByteArrayTag ParseByteArray(string name, Queue queue) + { + var values = new List(); + foreach (var token in DequeueUntil(queue, TokenType.EndArray)) { - var values = new List(); - foreach (var token in DequeueUntil(queue, TokenType.EndArray)) - { - if (token.Type != TokenType.Byte) - throw new SyntaxErrorException($"Invalid token type in array, expected {TokenType.Byte}, got {token.Type}."); - values.Add(unchecked((byte) sbyte.Parse(token.Value))); - } - return new ByteArrayTag(name, values); + if (token.Type != TokenType.Byte) + throw new SyntaxErrorException($"Invalid token type in array, expected {TokenType.Byte}, got {token.Type}."); + values.Add(unchecked((byte) sbyte.Parse(token.Value))); } + return new ByteArrayTag(name, values); + } - private static IntArrayTag ParseIntArray(string name, Queue queue) + private static IntArrayTag ParseIntArray(string name, Queue queue) + { + var values = new List(); + foreach (var token in DequeueUntil(queue, TokenType.EndArray)) { - var values = new List(); - foreach (var token in DequeueUntil(queue, TokenType.EndArray)) - { - if (token.Type != TokenType.Int) - throw new SyntaxErrorException($"Invalid token type in array, expected {TokenType.Int}, got {token.Type}."); - values.Add(int.Parse(token.Value)); - } - return new IntArrayTag(name, values); + if (token.Type != TokenType.Int) + throw new SyntaxErrorException($"Invalid token type in array, expected {TokenType.Int}, got {token.Type}."); + values.Add(int.Parse(token.Value)); } + return new IntArrayTag(name, values); + } - private static LongArrayTag ParseLongArray(string name, Queue queue) + private static LongArrayTag ParseLongArray(string name, Queue queue) + { + var values = new List(); + foreach (var token in DequeueUntil(queue, TokenType.EndArray)) { - var values = new List(); - foreach (var token in DequeueUntil(queue, TokenType.EndArray)) - { - if (token.Type != TokenType.Long) - throw new SyntaxErrorException($"Invalid token type in array, expected {TokenType.Long}, got {token.Type}."); - values.Add(long.Parse(token.Value)); - } - return new LongArrayTag(name, values); + if (token.Type != TokenType.Long) + throw new SyntaxErrorException($"Invalid token type in array, expected {TokenType.Long}, got {token.Type}."); + values.Add(long.Parse(token.Value)); } + return new LongArrayTag(name, values); + } - private static IEnumerable DequeueUntil(Queue queue, TokenType type) + private static IEnumerable DequeueUntil(Queue queue, TokenType type) + { + while (true) { - while (true) - { - var token = MoveNext(queue); - if (token.Type == type) - yield break; - yield return token; - } + var token = MoveNext(queue); + if (token.Type == type) + yield break; + yield return token; } - - private static string FirstGroupValue(Match match) => match.Groups[1].Value; } + + private static string FirstGroupValue(Match match) => match.Groups[1].Value; } \ No newline at end of file diff --git a/SharpNBT/SNBT/Token.cs b/SharpNBT/SNBT/Token.cs index c915f5d..53d4e03 100644 --- a/SharpNBT/SNBT/Token.cs +++ b/SharpNBT/SNBT/Token.cs @@ -1,35 +1,34 @@ using JetBrains.Annotations; -namespace SharpNBT.SNBT +namespace SharpNBT.SNBT; + +/// +/// An object emitted by the lexer to describe a logical fragment of code that can be parsed. +/// +[PublicAPI] +public sealed class Token { /// - /// An object emitted by the lexer to describe a logical fragment of code that can be parsed. + /// Gets a value describing the general type code fragment this represents. /// - [PublicAPI] - public sealed class Token - { - /// - /// Gets a value describing the general type code fragment this represents. - /// - public TokenType Type { get; } - - /// - /// Gets a value of this fragment, which can vary depending on context and the . - /// - public string Value { get; } + public TokenType Type { get; } - /// - /// Creates a new instance of the class. - /// - /// A value describing the general type code fragment this represents. - /// Ahe value of this code fragment. - public Token(TokenType type, [NotNull] string value) - { - Type = type; - Value = value; - } + /// + /// Gets a value of this fragment, which can vary depending on context and the . + /// + public string Value { get; } - /// - public override string ToString() => $"[{Type}] \"{Value}\""; + /// + /// Creates a new instance of the class. + /// + /// A value describing the general type code fragment this represents. + /// Ahe value of this code fragment. + public Token(TokenType type, string value) + { + Type = type; + Value = value; } + + /// + public override string ToString() => $"[{Type}] \"{Value}\""; } \ No newline at end of file diff --git a/SharpNBT/SNBT/TokenType.cs b/SharpNBT/SNBT/TokenType.cs index 568c2db..541057d 100644 --- a/SharpNBT/SNBT/TokenType.cs +++ b/SharpNBT/SNBT/TokenType.cs @@ -1,103 +1,102 @@ using JetBrains.Annotations; -namespace SharpNBT.SNBT +namespace SharpNBT.SNBT; + +/// +/// Describes types of tokens that the SNBT lexer can emit. +/// +[PublicAPI] +public enum TokenType { /// - /// Describes types of tokens that the SNBT lexer can emit. - /// - [PublicAPI] - public enum TokenType - { - /// - /// Any whitespace/newline not found within a string or identifier. - /// - /// This type is not yielded during tokenization. - Whitespace, - - /// - /// A separator between objects and array elements. - /// - /// This type is not yielded during tokenization. - Separator, - - /// - /// The beginning of new object. - /// - Compound, - - /// - /// The end of a . - /// - EndCompound, - - /// - /// The name of an tag. - /// - Identifier, - - /// - /// A value, which may be contain escaped quotes. - /// - String, - - /// - /// The beginning of a . - /// - ByteArray, - - /// - /// The beginning of a . - /// - IntArray, - - /// - /// The beginning of a . - /// - LongArray, - - /// - /// The beginning of a . - /// - List, - - /// - /// The end of a , , or . - /// - EndArray, - - /// - /// A value or element of a depending on context. - /// - Byte, - - /// - /// A value. - /// - Bool, - - /// - /// A value. - /// - Short, - - /// - /// A value or element of a depending on context. - /// - Int, - - /// - /// A value or element of a depending on context. - /// - Long, - - /// - /// A value. - /// - Float, - - /// - /// A value. - /// - Double - } + /// Any whitespace/newline not found within a string or identifier. + /// + /// This type is not yielded during tokenization. + Whitespace, + + /// + /// A separator between objects and array elements. + /// + /// This type is not yielded during tokenization. + Separator, + + /// + /// The beginning of new object. + /// + Compound, + + /// + /// The end of a . + /// + EndCompound, + + /// + /// The name of an tag. + /// + Identifier, + + /// + /// A value, which may be contain escaped quotes. + /// + String, + + /// + /// The beginning of a . + /// + ByteArray, + + /// + /// The beginning of a . + /// + IntArray, + + /// + /// The beginning of a . + /// + LongArray, + + /// + /// The beginning of a . + /// + List, + + /// + /// The end of a , , or . + /// + EndArray, + + /// + /// A value or element of a depending on context. + /// + Byte, + + /// + /// A value. + /// + Bool, + + /// + /// A value. + /// + Short, + + /// + /// A value or element of a depending on context. + /// + Int, + + /// + /// A value or element of a depending on context. + /// + Long, + + /// + /// A value. + /// + Float, + + /// + /// A value. + /// + Double } \ No newline at end of file diff --git a/SharpNBT/SharpNBT.csproj b/SharpNBT/SharpNBT.csproj index 8c84bfc..bc18fdb 100644 --- a/SharpNBT/SharpNBT.csproj +++ b/SharpNBT/SharpNBT.csproj @@ -1,7 +1,7 @@ - netstandard2.1 + net7.0 SharpNBT ForeverZer0 MIT @@ -16,6 +16,8 @@ 1.2.0 1.2.0 1.2.0 + latestmajor + enable diff --git a/SharpNBT/TagBuilder.cs b/SharpNBT/TagBuilder.cs index 67397bf..71d5c90 100644 --- a/SharpNBT/TagBuilder.cs +++ b/SharpNBT/TagBuilder.cs @@ -4,432 +4,430 @@ using System.Runtime.InteropServices; using JetBrains.Annotations; -namespace SharpNBT +namespace SharpNBT; + +/// +/// Provides a mechanism for easily building a tree of NBT objects by handling the intermediate step of creating tags, allowing the direct adding of their +/// equivalent values. +/// +/// All methods return the instance itself, allowing for easily chaining calls to build a document. +/// +[PublicAPI] +public class TagBuilder { + private readonly CompoundTag root; + private readonly Stack tree; + /// - /// Provides a mechanism for easily building a tree of NBT objects by handling the intermediate step of creating tags, allowing the direct adding of their - /// equivalent values. - /// - /// All methods return the instance itself, allowing for easily chaining calls to build a document. + /// Gets the zero-based depth of the current node, indicating how deeply nested it is within other tags. /// - [PublicAPI] - public class TagBuilder - { - private readonly CompoundTag root; - private readonly Stack tree; - - /// - /// Gets the zero-based depth of the current node, indicating how deeply nested it is within other tags. - /// - /// The implicit top-level is not factored into this value. - public int Depth => tree.Count - 1; + /// The implicit top-level is not factored into this value. + public int Depth => tree.Count - 1; - /// - /// Creates a new instance of the class, optionally with a to assign the top-level - /// of the final result. - /// - /// - public TagBuilder([CanBeNull] string name = null) - { - root = new CompoundTag(name); - tree = new Stack(); - tree.Push(root); - } + /// + /// Creates a new instance of the class, optionally with a to assign the top-level + /// of the final result. + /// + /// + public TagBuilder(string? name = null) + { + root = new CompoundTag(name); + tree = new Stack(); + tree.Push(root); + } - /// - /// Adds a new with the specified and to the tree at the current depth. - /// - /// The name of the node to add. - /// The value of the tag. - /// Returns this instance for chaining. - public TagBuilder AddBool([CanBeNull] string name, bool value) => AddTag(new BoolTag(name, value)); + /// + /// Adds a new with the specified and to the tree at the current depth. + /// + /// 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)); - /// - /// Adds a new unnamed with the specified to the tree at the current depth. - /// - /// The value of the tag. - /// Returns this instance for chaining. - public TagBuilder AddBool(bool value) => AddBool(null, value); + /// + /// Adds a new unnamed with the specified to the tree at the current depth. + /// + /// The value of the tag. + /// Returns this instance for chaining. + public TagBuilder AddBool(bool value) => AddBool(null, value); - /// - /// Adds a new with the specified and to the tree at the current depth. - /// - /// The name of the node to add. - /// The value of the tag. - /// Returns this instance for chaining. - public TagBuilder AddByte([CanBeNull] string name, byte value) => AddTag(new ByteTag(name, value)); + /// + /// Adds a new with the specified and to the tree at the current depth. + /// + /// The name of the node to add. + /// The value of the tag. + /// Returns this instance for chaining. + public TagBuilder AddByte(string? name, byte value) => AddTag(new ByteTag(name, value)); - /// - [CLSCompliant(false)] - public TagBuilder AddByte([CanBeNull] string name, sbyte value) => AddByte(name, unchecked((byte)value)); + /// + [CLSCompliant(false)] + public TagBuilder AddByte(string? name, sbyte value) => AddByte(name, unchecked((byte)value)); - /// - /// Adds a new unnamed with the specified to the tree at the current depth. - /// - /// The value of the tag. - /// Returns this instance for chaining. - public TagBuilder AddByte(byte value) => AddByte(null, value); + /// + /// Adds a new unnamed with the specified to the tree at the current depth. + /// + /// The value of the tag. + /// Returns this instance for chaining. + public TagBuilder AddByte(byte value) => AddByte(null, value); - /// - [CLSCompliant(false)] - public TagBuilder AddByte(sbyte value) => AddByte(null, unchecked((byte)value)); + /// + [CLSCompliant(false)] + public TagBuilder AddByte(sbyte value) => AddByte(null, unchecked((byte)value)); - /// - /// Adds a new with the specified and to the tree at the current depth. - /// - /// The name of the node to add. - /// The value of the tag. - /// Returns this instance for chaining. - public TagBuilder AddShort([CanBeNull]string name, short value) => AddTag(new ShortTag(name, value)); + /// + /// Adds a new with the specified and to the tree at the current depth. + /// + /// The name of the node to add. + /// The value of the tag. + /// Returns this instance for chaining. + public TagBuilder AddShort(string? name, short value) => AddTag(new ShortTag(name, value)); - /// - [CLSCompliant(false)] - public TagBuilder AddShort([CanBeNull] string name, ushort value) => AddShort(name, unchecked((short)value)); + /// + [CLSCompliant(false)] + public TagBuilder AddShort(string? name, ushort value) => AddShort(name, unchecked((short)value)); - /// - /// Adds a new unnamed with the specified to the tree at the current depth. - /// - /// The value of the tag. - /// Returns this instance for chaining. - public TagBuilder AddShort(short value) => AddShort(null, value); + /// + /// Adds a new unnamed with the specified to the tree at the current depth. + /// + /// The value of the tag. + /// Returns this instance for chaining. + public TagBuilder AddShort(short value) => AddShort(null, value); - /// - [CLSCompliant(false)] - public TagBuilder AddShort(ushort value) => AddShort(null, unchecked((short)value)); + /// + [CLSCompliant(false)] + public TagBuilder AddShort(ushort value) => AddShort(null, unchecked((short)value)); - /// - /// Adds a new with the specified and to the tree at the current depth. - /// - /// The name of the node to add. - /// The value of the tag. - /// Returns this instance for chaining. - public TagBuilder AddInt([CanBeNull]string name, int value) => AddTag(new IntTag(name, value)); + /// + /// Adds a new with the specified and to the tree at the current depth. + /// + /// The name of the node to add. + /// The value of the tag. + /// Returns this instance for chaining. + public TagBuilder AddInt(string? name, int value) => AddTag(new IntTag(name, value)); - /// - [CLSCompliant(false)] - public TagBuilder AddInt([CanBeNull] string name, uint value) => AddInt(name, unchecked((int)value)); + /// + [CLSCompliant(false)] + public TagBuilder AddInt(string? name, uint value) => AddInt(name, unchecked((int)value)); - /// - /// Adds a new unnamed with the specified to the tree at the current depth. - /// - /// The value of the tag. - /// Returns this instance for chaining. - public TagBuilder AddInt(int value) => AddInt(null, value); + /// + /// Adds a new unnamed with the specified to the tree at the current depth. + /// + /// The value of the tag. + /// Returns this instance for chaining. + public TagBuilder AddInt(int value) => AddInt(null, value); - /// - [CLSCompliant(false)] - public TagBuilder AddInt(uint value) => AddInt(null, unchecked((int)value)); + /// + [CLSCompliant(false)] + public TagBuilder AddInt(uint value) => AddInt(null, unchecked((int)value)); - /// - /// Adds a new with the specified and to the tree at the current depth. - /// - /// The name of the node to add. - /// The value of the tag. - /// Returns this instance for chaining. - public TagBuilder AddLong([CanBeNull]string name, long value) => AddTag(new LongTag(name, value)); + /// + /// Adds a new with the specified and to the tree at the current depth. + /// + /// The name of the node to add. + /// The value of the tag. + /// Returns this instance for chaining. + public TagBuilder AddLong(string? name, long value) => AddTag(new LongTag(name, value)); - /// - [CLSCompliant(false)] - public TagBuilder AddLong([CanBeNull] string name, ulong value) => AddLong(name, unchecked((long)value)); + /// + [CLSCompliant(false)] + public TagBuilder AddLong(string? name, ulong value) => AddLong(name, unchecked((long)value)); - /// - /// Adds a new unnamed with the specified to the tree at the current depth. - /// - /// The value of the tag. - /// Returns this instance for chaining. - public TagBuilder AddLong(long value) => AddLong(null, value); + /// + /// Adds a new unnamed with the specified to the tree at the current depth. + /// + /// The value of the tag. + /// Returns this instance for chaining. + public TagBuilder AddLong(long value) => AddLong(null, value); - /// - [CLSCompliant(false)] - public TagBuilder AddLong(ulong value) => AddLong(null, unchecked((long)value)); + /// + [CLSCompliant(false)] + public TagBuilder AddLong(ulong value) => AddLong(null, unchecked((long)value)); - /// - /// Adds a new with the specified and to the tree at the current depth. - /// - /// The name of the node to add. - /// The value of the tag. - /// Returns this instance for chaining. - public TagBuilder AddFloat([CanBeNull]string name, float value) => AddTag(new FloatTag(name, value)); + /// + /// Adds a new with the specified and to the tree at the current depth. + /// + /// The name of the node to add. + /// The value of the tag. + /// Returns this instance for chaining. + public TagBuilder AddFloat(string? name, float value) => AddTag(new FloatTag(name, value)); - /// - /// Adds a new unnamed with the specified to the tree at the current depth. - /// - /// The value of the tag. - /// Returns this instance for chaining. - public TagBuilder AddFloat(float value) => AddFloat(null, value); + /// + /// Adds a new unnamed with the specified to the tree at the current depth. + /// + /// The value of the tag. + /// Returns this instance for chaining. + public TagBuilder AddFloat(float value) => AddFloat(null, value); - /// - /// Adds a new with the specified and to the tree at the current depth. - /// - /// The name of the node to add. - /// The value of the tag. - /// Returns this instance for chaining. - public TagBuilder AddDouble([CanBeNull]string name, double value) => AddTag(new DoubleTag(name, value)); + /// + /// Adds a new with the specified and to the tree at the current depth. + /// + /// The name of the node to add. + /// The value of the tag. + /// Returns this instance for chaining. + public TagBuilder AddDouble(string? name, double value) => AddTag(new DoubleTag(name, value)); - /// - /// Adds a new unnamed with the specified to the tree at the current depth. - /// - /// The value of the tag. - /// Returns this instance for chaining. - public TagBuilder AddDouble(double value) => AddDouble(null, value); + /// + /// Adds a new unnamed with the specified to the tree at the current depth. + /// + /// The value of the tag. + /// Returns this instance for chaining. + public TagBuilder AddDouble(double value) => AddDouble(null, value); - /// - /// Adds a new with the specified and to the tree at the current depth. - /// - /// The name of the node to add. - /// The value of the tag. - public TagBuilder AddString([CanBeNull]string name, [CanBeNull] string value) => AddTag(new StringTag(name, value)); + /// + /// Adds a new with the specified and to the tree at the current depth. + /// + /// The name of the node to add. + /// The value of the tag. + public TagBuilder AddString(string? name, string? value) => AddTag(new StringTag(name, value)); - /// - /// Adds a new unnamed with the specified to the tree at the current depth. - /// - /// The value of the tag. - /// Returns this instance for chaining. - public TagBuilder AddString([CanBeNull] string value) => AddString(null, value); + /// + /// Adds a new unnamed with the specified to the tree at the current depth. + /// + /// The value of the tag. + /// Returns this instance for chaining. + public TagBuilder AddString(string? value) => AddString(null, value); - /// - /// Adds a new with the specified to the tree at the current depth. - /// - /// The name of the node to add. - /// The value(s) that will be included in the tag. - /// Returns this instance for chaining. - public TagBuilder AddByteArray([CanBeNull] string name, params byte[] values) => AddTag(new ByteArrayTag(name, new ReadOnlySpan(values))); + /// + /// Adds a new with the specified to the tree at the current depth. + /// + /// The name of the node to add. + /// The value(s) that will be included in the tag. + /// Returns this instance for chaining. + public TagBuilder AddByteArray(string? name, params byte[] values) => AddTag(new ByteArrayTag(name, new ReadOnlySpan(values))); - /// - public TagBuilder AddByteArray([CanBeNull] string name, [NotNull] IEnumerable values) => AddByteArray(name, values.ToArray()); + /// + public TagBuilder AddByteArray(string? name, IEnumerable values) => AddByteArray(name, values.ToArray()); - /// - /// Adds a new unnamed with the specified to the tree at the current depth. - /// - /// The value(s) that will be included in the tag. - /// Returns this instance for chaining. - public TagBuilder AddByteArray(params byte[] values) => AddByteArray(null, values); + /// + /// Adds a new unnamed with the specified to the tree at the current depth. + /// + /// The value(s) that will be included in the tag. + /// Returns this instance for chaining. + public TagBuilder AddByteArray(params byte[] values) => AddByteArray(null, values); - /// - public TagBuilder AddByteArray([NotNull] IEnumerable values) => AddByteArray(null, values.ToArray()); + /// + public TagBuilder AddByteArray(IEnumerable values) => AddByteArray(null, values.ToArray()); - /// - /// Adds a new with the specified to the tree at the current depth. - /// - /// The name of the node to add. - /// The value(s) that will be included in the tag. - /// Returns this instance for chaining. - [CLSCompliant(false)] - public TagBuilder AddByteArray([CanBeNull] string name, params sbyte[] values) - { - var span = new ReadOnlySpan(values); - return AddTag(new ByteArrayTag(name, MemoryMarshal.Cast(span))); - } + /// + /// Adds a new with the specified to the tree at the current depth. + /// + /// The name of the node to add. + /// The value(s) that will be included in the tag. + /// Returns this instance for chaining. + [CLSCompliant(false)] + public TagBuilder AddByteArray(string? name, params sbyte[] values) + { + var span = new ReadOnlySpan(values); + return AddTag(new ByteArrayTag(name, MemoryMarshal.Cast(span))); + } - /// - [CLSCompliant(false)] - public TagBuilder AddByteArray([CanBeNull] string name, [NotNull] IEnumerable values) => AddByteArray(name, values.ToArray()); + /// + [CLSCompliant(false)] + public TagBuilder AddByteArray(string? name, IEnumerable values) => AddByteArray(name, values.ToArray()); - /// - /// Adds a new unnamed with the specified to the tree at the current depth. - /// - /// The value(s) that will be included in the tag. - /// Returns this instance for chaining. - [CLSCompliant(false)] - public TagBuilder AddByteArray(params sbyte[] values) => AddByteArray(null, values); - - /// - [CLSCompliant(false)] - public TagBuilder AddByteArray([NotNull] IEnumerable values) => AddByteArray(null, values.ToArray()); + /// + /// Adds a new unnamed with the specified to the tree at the current depth. + /// + /// The value(s) that will be included in the tag. + /// Returns this instance for chaining. + [CLSCompliant(false)] + public TagBuilder AddByteArray(params sbyte[] values) => AddByteArray(null, values); + + /// + [CLSCompliant(false)] + public TagBuilder AddByteArray(IEnumerable values) => AddByteArray(null, values.ToArray()); - /// - /// Adds a new with the specified to the tree at the current depth. - /// - /// The name of the node to add. - /// The value(s) that will be included in the tag. - /// Returns this instance for chaining. - public TagBuilder AddIntArray([CanBeNull] string name, params int[] values) => AddTag(new IntArrayTag(name, values as IEnumerable)); + /// + /// Adds a new with the specified to the tree at the current depth. + /// + /// The name of the node to add. + /// The value(s) that will be included in the tag. + /// Returns this instance for chaining. + public TagBuilder AddIntArray(string? name, params int[] values) => AddTag(new IntArrayTag(name, values as IEnumerable)); - /// - public TagBuilder AddIntArray([CanBeNull] string name, [NotNull] IEnumerable values) => AddIntArray(name, values.ToArray()); + /// + public TagBuilder AddIntArray(string? name, IEnumerable values) => AddIntArray(name, values.ToArray()); - /// - /// Adds a new unnamed with the specified to the tree at the current depth. - /// - /// The value(s) that will be included in the tag. - /// Returns this instance for chaining. - public TagBuilder AddIntArray(params int[] values) => AddIntArray(null, values); + /// + /// Adds a new unnamed with the specified to the tree at the current depth. + /// + /// The value(s) that will be included in the tag. + /// Returns this instance for chaining. + public TagBuilder AddIntArray(params int[] values) => AddIntArray(null, values); - /// - public TagBuilder AddIntArray([NotNull] IEnumerable values) => AddIntArray(null, values.ToArray()); + /// + public TagBuilder AddIntArray(IEnumerable values) => AddIntArray(null, values.ToArray()); - /// - /// Adds a new with the specified to the tree at the current depth. - /// - /// The name of the node to add. - /// The value(s) that will be included in the tag. - /// Returns this instance for chaining. - public TagBuilder AddLongArray([CanBeNull] string name, params long[] values) => AddTag(new LongArrayTag(name, new ReadOnlySpan(values))); + /// + /// Adds a new with the specified to the tree at the current depth. + /// + /// The name of the node to add. + /// The value(s) that will be included in the tag. + /// Returns this instance for chaining. + public TagBuilder AddLongArray(string? name, params long[] values) => AddTag(new LongArrayTag(name, new ReadOnlySpan(values))); - /// - public TagBuilder AddLongArray([CanBeNull] string name, IEnumerable values) => AddLongArray(name, values.ToArray()); + /// + public TagBuilder AddLongArray(string? name, IEnumerable values) => AddLongArray(name, values.ToArray()); - /// - /// Adds a new unnamed with the specified to the tree at the current depth. - /// - /// The value(s) that will be included in the tag. - /// Returns this instance for chaining. - public TagBuilder AddLongArray(params long[] values) => AddLongArray(null, values); + /// + /// Adds a new unnamed with the specified to the tree at the current depth. + /// + /// The value(s) that will be included in the tag. + /// Returns this instance for chaining. + public TagBuilder AddLongArray(params long[] values) => AddLongArray(null, values); - /// - public TagBuilder AddLongArray([NotNull] IEnumerable values) => AddLongArray(null, values.ToArray()); + /// + public TagBuilder AddLongArray(IEnumerable values) => AddLongArray(null, values.ToArray()); - /// - /// Adds an existing object to the tree at the current depth. - /// - /// The instance to add. - /// Returns this instance for chaining. - /// Thrown if adding to a node, and the type does not match. - /// Thrown when is . - public TagBuilder AddTag([NotNull] Tag tag) - { - var top = tree.Peek(); - top.Add(tag ?? throw new ArgumentNullException(nameof(tag))); - return this; - } + /// + /// Adds an existing object to the tree at the current depth. + /// + /// The instance to add. + /// Returns this instance for chaining. + /// Thrown if adding to a node, and the type does not match. + /// Thrown when is . + public TagBuilder AddTag(Tag tag) + { + var top = tree.Peek(); + top.Add(tag ?? throw new ArgumentNullException(nameof(tag))); + return this; + } - /// - /// Opens a new section, increasing the current depth level by one. - /// - /// The of the child items this list will contain. - /// The name to apply to the , or to omit a name. - /// Returns this instance for chaining. - /// - public TagBuilder BeginList(TagType childType, [CanBeNull] string name = null) - { - var list = new ListTag(name, childType); - AddTag(list); - tree.Push(list); - return this; - } + /// + /// Opens a new section, increasing the current depth level by one. + /// + /// The of the child items this list will contain. + /// The name to apply to the , or to omit a name. + /// Returns this instance for chaining. + /// + public TagBuilder BeginList(TagType childType, string? name = null) + { + var list = new ListTag(name, childType); + AddTag(list); + tree.Push(list); + return this; + } - /// - /// Closes the current section and decreases the by one. Does nothing if the the current node does not - /// represent a . - /// - /// Returns this instance for chaining. - /// - public TagBuilder EndList() - { - if (tree.TryPeek(out var result) && result is ListTag) - tree.Pop(); + /// + /// Closes the current section and decreases the by one. Does nothing if the the current node does not + /// represent a . + /// + /// Returns this instance for chaining. + /// + public TagBuilder EndList() + { + if (tree.TryPeek(out var result) && result is ListTag) + tree.Pop(); - return this; - } + return this; + } - /// - /// Opens a new section, increasing the current depth level by one. - /// - /// The name to apply to the , or to omit a name. - /// Returns this instance for chaining. - /// - public TagBuilder BeginCompound([CanBeNull] string name = null) - { - var compound = new CompoundTag(name); - AddTag(compound); - tree.Push(compound); - return this; - } + /// + /// Opens a new section, increasing the current depth level by one. + /// + /// The name to apply to the , or to omit a name. + /// Returns this instance for chaining. + /// + public TagBuilder BeginCompound(string? name = null) + { + var compound = new CompoundTag(name); + AddTag(compound); + tree.Push(compound); + return this; + } - /// - /// Closes the current section and decreases the by one. Does nothing if the the current node does not - /// represent a . - /// - /// Returns this instance for chaining. - /// - public TagBuilder EndCompound() - { - if (tree.Count > 1 && tree.TryPeek(out var result) && result is CompoundTag) - tree.Pop(); + /// + /// Closes the current section and decreases the by one. Does nothing if the the current node does not + /// represent a . + /// + /// Returns this instance for chaining. + /// + public TagBuilder EndCompound() + { + if (tree.Count > 1 && tree.TryPeek(out var result) && result is CompoundTag) + tree.Pop(); - return this; - } + return this; + } - /// - /// Closes the current or section and decreases the by one. - /// - /// Returns this instance for chaining. - /// This method does nothing if the current location is already at the top-level. - public TagBuilder End() - { - if ((tree.Peek() is ListTag) || (tree.Count > 1 && tree.Peek() is CompoundTag)) - tree.Pop(); - return this; - } + /// + /// Closes the current or section and decreases the by one. + /// + /// Returns this instance for chaining. + /// This method does nothing if the current location is already at the top-level. + public TagBuilder End() + { + if ((tree.Peek() is ListTag) || (tree.Count > 1 && tree.Peek() is CompoundTag)) + tree.Pop(); + return this; + } - /// - /// Closes any open compound/list sections, and returns the result as a . - /// - /// Invoking this method moves the current back to the top-level. - /// A representing the result of this tree. - public CompoundTag Create() - { - tree.Clear(); - tree.Push(root); - return root; - } + /// + /// Closes any open compound/list sections, and returns the result as a . + /// + /// Invoking this method moves the current back to the top-level. + /// A representing the result of this tree. + public CompoundTag Create() + { + tree.Clear(); + tree.Push(root); + return root; + } - /// - /// Creates a new and pushes it to the current scope level, returning a object that pulls the current - /// scope back out one level when disposed. - /// - /// The name to apply to the , or to omit a name. - /// A that will close the when disposed. - /// This is essentially no different than and but can use `using` blocks to distinguish scope. - public Context NewCompound([CanBeNull] string name) - { - BeginCompound(name); - return new Context(tree.Peek(), EndCompound); - } + /// + /// Creates a new and pushes it to the current scope level, returning a object that pulls the current + /// scope back out one level when disposed. + /// + /// The name to apply to the , or to omit a name. + /// A that will close the when disposed. + /// This is essentially no different than and but can use `using` blocks to distinguish scope. + public Context NewCompound(string? name) + { + BeginCompound(name); + return new Context(tree.Peek(), EndCompound); + } + /// + /// Creates a new and pushes it to the current scope level, returning a object that pulls the current + /// scope back out one level when disposed. + /// + /// The of the child items this list will contain. + /// The name to apply to the , or to omit a name. + /// A that will close the when disposed. + /// This is essentially no different than and but can use `using` blocks to distinguish scope. + public Context NewList(TagType childType, string? name) + { + BeginList(childType, name); + return new Context(tree.Peek(), EndList); + } + + /// + /// Represents the context of a single "level" of depth into a AST. + /// + /// Implements to that each node can used with using statements for easily distinguishable scope. + [PublicAPI] + public class Context: IDisposable + { + internal delegate TagBuilder CloseHandler(); + private readonly CloseHandler closeHandler; + /// - /// Creates a new and pushes it to the current scope level, returning a object that pulls the current - /// scope back out one level when disposed. + /// Gets the top-level tag for this context. /// - /// The of the child items this list will contain. - /// The name to apply to the , or to omit a name. - /// A that will close the when disposed. - /// This is essentially no different than and but can use `using` blocks to distinguish scope. - public Context NewList(TagType childType, [CanBeNull] string name) + public TagContainer Tag { get; } + + internal Context(TagContainer tag, CloseHandler handler) { - BeginList(childType, name); - return new Context(tree.Peek(), EndList); + Tag = tag; + closeHandler = handler; } - /// - /// Represents the context of a single "level" of depth into a AST. - /// - /// Implements to that each node can used with using statements for easily distinguishable scope. - [PublicAPI] - public class Context: IDisposable + /// Closes this context. + public void Dispose() { - internal delegate TagBuilder CloseHandler(); - private readonly CloseHandler closeHandler; - - /// - /// Gets the top-level tag for this context. - /// - [NotNull] - public TagContainer Tag { get; } - - internal Context([NotNull] TagContainer tag, [NotNull] CloseHandler handler) - { - Tag = tag; - closeHandler = handler; - } - - /// Closes this context. - public void Dispose() - { - Console.WriteLine(Tag); - closeHandler.Invoke(); - } + Console.WriteLine(Tag); + closeHandler.Invoke(); } } } \ No newline at end of file diff --git a/SharpNBT/TagIO.cs b/SharpNBT/TagIO.cs index 537071a..a07372c 100644 --- a/SharpNBT/TagIO.cs +++ b/SharpNBT/TagIO.cs @@ -3,70 +3,68 @@ using System.Threading.Tasks; using JetBrains.Annotations; -namespace SharpNBT +namespace SharpNBT; + +/// +/// Abstract base class for the and classes, providing shared functionality. +/// +[PublicAPI] +public abstract class TagIO : IDisposable { /// - /// Abstract base class for the and classes, providing shared functionality. + /// Gets the underlying stream this instance is operating on. /// - [PublicAPI] - public abstract class TagIO : IDisposable - { - /// - /// Gets the underlying stream this instance is operating on. - /// - [NotNull] - protected Stream BaseStream { get; } + protected Stream BaseStream { get; } - /// - /// Gets a flag indicating if byte swapping is required for numeric values, accounting for both the endianness of the host machine and the - /// specified . - /// - protected bool SwapEndian { get; } + /// + /// Gets a flag indicating if byte swapping is required for numeric values, accounting for both the endianness of the host machine and the + /// specified . + /// + protected bool SwapEndian { get; } - /// - /// Gets a flag indicating if variable-length integers should be used in applicable places. - /// - protected bool UseVarInt { get; } + /// + /// Gets a flag indicating if variable-length integers should be used in applicable places. + /// + protected bool UseVarInt { get; } - /// - /// Gets a flag indicating if variable-length integers will be "ZigZag encoded". - /// - /// - public bool ZigZagEncoding { get; } + /// + /// Gets a flag indicating if variable-length integers will be "ZigZag encoded". + /// + /// + public bool ZigZagEncoding { get; } - /// - /// Gets the format to be followed for compatibility. - /// - public FormatOptions FormatOptions { get; } + /// + /// Gets the format to be followed for compatibility. + /// + public FormatOptions FormatOptions { get; } - /// - /// Initializes a new instance of the class. - /// - /// A instance that the writer will be writing to. - /// Bitwise flags to configure how data should be handled for compatibility between different specifications. - /// Thrown when is - protected TagIO([NotNull] Stream stream, FormatOptions options) - { - BaseStream = stream ?? throw new ArgumentNullException(nameof(stream)); + /// + /// Initializes a new instance of the class. + /// + /// A instance that the writer will be writing to. + /// Bitwise flags to configure how data should be handled for compatibility between different specifications. + /// Thrown when is + protected TagIO(Stream stream, FormatOptions options) + { + BaseStream = stream ?? throw new ArgumentNullException(nameof(stream)); - if (options.HasFlag(FormatOptions.BigEndian)) - SwapEndian = BitConverter.IsLittleEndian; - else if (options.HasFlag(FormatOptions.LittleEndian)) - SwapEndian = !BitConverter.IsLittleEndian; + if (options.HasFlag(FormatOptions.BigEndian)) + SwapEndian = BitConverter.IsLittleEndian; + else if (options.HasFlag(FormatOptions.LittleEndian)) + SwapEndian = !BitConverter.IsLittleEndian; - UseVarInt = options.HasFlag(FormatOptions.VarIntegers); - ZigZagEncoding = options.HasFlag(FormatOptions.ZigZagEncoding); - } + UseVarInt = options.HasFlag(FormatOptions.VarIntegers); + ZigZagEncoding = options.HasFlag(FormatOptions.ZigZagEncoding); + } - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - /// - public abstract void Dispose(); + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public abstract void Dispose(); - /// - /// Asynchronously releases the unmanaged resources used by the instance. - /// - public abstract ValueTask DisposeAsync(); + /// + /// Asynchronously releases the unmanaged resources used by the instance. + /// + public abstract ValueTask DisposeAsync(); - } } \ No newline at end of file diff --git a/SharpNBT/TagReader.cs b/SharpNBT/TagReader.cs index 42366d2..dc2837a 100644 --- a/SharpNBT/TagReader.cs +++ b/SharpNBT/TagReader.cs @@ -5,507 +5,503 @@ using System.Threading.Tasks; using JetBrains.Annotations; -namespace SharpNBT +namespace SharpNBT; + +/// +/// Provides methods for reading NBT data from a stream. +/// +[PublicAPI] +public class TagReader : TagIO { /// - /// Provides methods for reading NBT data from a stream. + /// Occurs when a tag has been fully deserialized from the stream. + /// + public event TagReaderCallback TagRead; + + /// + /// Occurs when a tag has been encountered in the stream, after reading the first byte to determine its . /// - [PublicAPI] - public class TagReader : TagIO + public event TagReaderCallback TagEncountered; + + private readonly bool leaveOpen; + + /// + /// Creates a new instance of the class from the given . + /// + /// A instance that the reader will be reading from. + /// Bitwise flags to configure how data should be handled for compatibility between different specifications. + /// + /// to leave the object open after disposing the + /// object; otherwise, . + public TagReader(Stream stream, FormatOptions options, bool leaveOpen = false) : base(stream, options) { - /// - /// Occurs when a tag has been fully deserialized from the stream. - /// - public event TagReaderCallback TagRead; + if (!stream.CanRead) + throw new IOException(Strings.CannotReadStream); + this.leaveOpen = leaveOpen; + } - /// - /// Occurs when a tag has been encountered in the stream, after reading the first byte to determine its . - /// - public event TagReaderCallback TagEncountered; + /// + /// Reads a from the stream. + /// + /// Flag indicating if this tag is named, only when a tag is a direct child of a . + /// It is assumed that the stream is positioned at the beginning of the tag payload. + /// The deserialized instance. + public ByteTag ReadByte(bool named = true) + { + var name = named ? ReadUTF8String() : null; + return new ByteTag(name, (byte) BaseStream.ReadByte()); + } - private readonly bool leaveOpen; + /// + /// Reads a from the stream. + /// + /// Flag indicating if this tag is named, only when a tag is a direct child of a . + /// It is assumed that the stream is positioned at the beginning of the tag payload. + /// The deserialized instance. + public ShortTag ReadShort(bool named = true) + { + var name = named ? ReadUTF8String() : null; + short value; - /// - /// Creates a new instance of the class from the given . - /// - /// A instance that the reader will be reading from. - /// Bitwise flags to configure how data should be handled for compatibility between different specifications. - /// - /// to leave the object open after disposing the - /// object; otherwise, . - public TagReader([NotNull] Stream stream, FormatOptions options, bool leaveOpen = false) : base(stream, options) + if (UseVarInt) { - if (!stream.CanRead) - throw new IOException(Strings.CannotReadStream); - this.leaveOpen = leaveOpen; + value = (short)VarInt.Read(BaseStream, ZigZagEncoding); } - - /// - /// Reads a from the stream. - /// - /// Flag indicating if this tag is named, only when a tag is a direct child of a . - /// It is assumed that the stream is positioned at the beginning of the tag payload. - /// The deserialized instance. - public ByteTag ReadByte(bool named = true) + else { - var name = named ? ReadUTF8String() : null; - return new ByteTag(name, (byte) BaseStream.ReadByte()); + Span buffer = stackalloc byte[sizeof(short)]; + ReadToFixSizedBuffer(buffer); + value = BitConverter.ToInt16(buffer); + if (SwapEndian) + value = value.SwapEndian(); } - - /// - /// Reads a from the stream. - /// - /// Flag indicating if this tag is named, only when a tag is a direct child of a . - /// It is assumed that the stream is positioned at the beginning of the tag payload. - /// The deserialized instance. - public ShortTag ReadShort(bool named = true) - { - var name = named ? ReadUTF8String() : null; - short value; - - if (UseVarInt) - { - value = (short)VarInt.Read(BaseStream, ZigZagEncoding); - } - else - { - Span buffer = stackalloc byte[sizeof(short)]; - ReadToFixSizedBuffer(buffer); - value = BitConverter.ToInt16(buffer); - if (SwapEndian) - value = value.SwapEndian(); - } - return new ShortTag(name, value); - } + return new ShortTag(name, value); + } - /// - /// Reads a from the stream. - /// - /// Flag indicating if this tag is named, only when a tag is a direct child of a . - /// It is assumed that the stream is positioned at the beginning of the tag payload. - /// The deserialized instance. - public IntTag ReadInt(bool named = true) + /// + /// Reads a from the stream. + /// + /// Flag indicating if this tag is named, only when a tag is a direct child of a . + /// It is assumed that the stream is positioned at the beginning of the tag payload. + /// The deserialized instance. + public IntTag ReadInt(bool named = true) + { + var name = named ? ReadUTF8String() : null; + return new IntTag(name, UseVarInt ? VarInt.Read(BaseStream, ZigZagEncoding) : ReadInt32()); + } + + /// + /// Reads a from the stream. + /// + /// Flag indicating if this tag is named, only when a tag is a direct child of a . + /// It is assumed that the stream is positioned at the beginning of the tag payload. + /// The deserialized instance. + public LongTag ReadLong(bool named = true) + { + var name = named ? ReadUTF8String() : null; + long value; + + if (UseVarInt) { - var name = named ? ReadUTF8String() : null; - return new IntTag(name, UseVarInt ? VarInt.Read(BaseStream, ZigZagEncoding) : ReadInt32()); + value = VarLong.Read(BaseStream, ZigZagEncoding); } - - /// - /// Reads a from the stream. - /// - /// Flag indicating if this tag is named, only when a tag is a direct child of a . - /// It is assumed that the stream is positioned at the beginning of the tag payload. - /// The deserialized instance. - public LongTag ReadLong(bool named = true) + else { - var name = named ? ReadUTF8String() : null; - long value; - - if (UseVarInt) - { - value = VarLong.Read(BaseStream, ZigZagEncoding); - } - else - { - Span buffer = stackalloc byte[sizeof(long)]; - ReadToFixSizedBuffer(buffer); - value = BitConverter.ToInt64(buffer); - if (SwapEndian) - value = value.SwapEndian(); - } - - return new LongTag(name, value); + Span buffer = stackalloc byte[sizeof(long)]; + ReadToFixSizedBuffer(buffer); + value = BitConverter.ToInt64(buffer); + if (SwapEndian) + value = value.SwapEndian(); } + + return new LongTag(name, value); + } - /// - /// Reads a from the stream. - /// - /// Flag indicating if this tag is named, only when a tag is a direct child of a . - /// It is assumed that the stream is positioned at the beginning of the tag payload. - /// The deserialized instance. - public FloatTag ReadFloat(bool named = true) - { - var name = named ? ReadUTF8String() : null; + /// + /// Reads a from the stream. + /// + /// Flag indicating if this tag is named, only when a tag is a direct child of a . + /// It is assumed that the stream is positioned at the beginning of the tag payload. + /// The deserialized instance. + public FloatTag ReadFloat(bool named = true) + { + var name = named ? ReadUTF8String() : null; - var buffer = new byte[sizeof(float)]; - ReadToFixSizedBuffer(buffer, 0, sizeof(float)); - if (SwapEndian) - Array.Reverse(buffer); + var buffer = new byte[sizeof(float)]; + ReadToFixSizedBuffer(buffer, 0, sizeof(float)); + if (SwapEndian) + Array.Reverse(buffer); - return new FloatTag( name, BitConverter.ToSingle(buffer)); - } + return new FloatTag( name, BitConverter.ToSingle(buffer)); + } - /// - /// Reads a from the stream. - /// - /// Flag indicating if this tag is named, only when a tag is a direct child of a . - /// It is assumed that the stream is positioned at the beginning of the tag payload. - /// The deserialized instance. - public DoubleTag ReadDouble(bool named = true) - { - var name = named ? ReadUTF8String() : null; - var buffer = new byte[sizeof(double)]; - ReadToFixSizedBuffer(buffer, 0, buffer.Length); - if (SwapEndian) - Array.Reverse(buffer); + /// + /// Reads a from the stream. + /// + /// Flag indicating if this tag is named, only when a tag is a direct child of a . + /// It is assumed that the stream is positioned at the beginning of the tag payload. + /// The deserialized instance. + public DoubleTag ReadDouble(bool named = true) + { + var name = named ? ReadUTF8String() : null; + var buffer = new byte[sizeof(double)]; + ReadToFixSizedBuffer(buffer, 0, buffer.Length); + if (SwapEndian) + Array.Reverse(buffer); - return new DoubleTag( name, BitConverter.ToDouble(buffer, 0)); - } + return new DoubleTag( name, BitConverter.ToDouble(buffer, 0)); + } - /// - /// Reads a from the stream. - /// - /// Flag indicating if this tag is named, only when a tag is a direct child of a . - /// It is assumed that the stream is positioned at the beginning of the tag payload. - /// The deserialized instance. - public StringTag ReadString(bool named = true) - { - var name = named ? ReadUTF8String() : null; - var value = ReadUTF8String(); - return new StringTag(name, value); - } + /// + /// Reads a from the stream. + /// + /// Flag indicating if this tag is named, only when a tag is a direct child of a . + /// It is assumed that the stream is positioned at the beginning of the tag payload. + /// The deserialized instance. + public StringTag ReadString(bool named = true) + { + var name = named ? ReadUTF8String() : null; + var value = ReadUTF8String(); + return new StringTag(name, value); + } - /// - /// Reads a from the stream. - /// - /// Flag indicating if this tag is named, only when a tag is a direct child of a . - /// It is assumed that the stream is positioned at the beginning of the tag payload. - /// The deserialized instance. - public ByteArrayTag ReadByteArray(bool named = true) - { - var name = named ? ReadUTF8String() : null; - var count = ReadCount(); - var buffer = new byte[count]; - ReadToFixSizedBuffer(buffer, 0, count); - return new ByteArrayTag(name, buffer); - } + /// + /// Reads a from the stream. + /// + /// Flag indicating if this tag is named, only when a tag is a direct child of a . + /// It is assumed that the stream is positioned at the beginning of the tag payload. + /// The deserialized instance. + public ByteArrayTag ReadByteArray(bool named = true) + { + var name = named ? ReadUTF8String() : null; + var count = ReadCount(); + var buffer = new byte[count]; + ReadToFixSizedBuffer(buffer, 0, count); + return new ByteArrayTag(name, buffer); + } - /// - /// Reads a from the stream. - /// - /// Flag indicating if this tag is named, only when a tag is a direct child of a . - /// It is assumed that the stream is positioned at the beginning of the tag payload. - /// The deserialized instance. - public IntArrayTag ReadIntArray(bool named = true) - { - const int INT_SIZE = sizeof(int); + /// + /// Reads a from the stream. + /// + /// Flag indicating if this tag is named, only when a tag is a direct child of a . + /// It is assumed that the stream is positioned at the beginning of the tag payload. + /// The deserialized instance. + public IntArrayTag ReadIntArray(bool named = true) + { + const int INT_SIZE = sizeof(int); - var name = named ? ReadUTF8String() : null; - var count = ReadCount(); + var name = named ? ReadUTF8String() : null; + var count = ReadCount(); - if (UseVarInt) - { - var array = new int[count]; - for (var i = 0; i < count; i++) - array[i] = VarInt.Read(BaseStream, ZigZagEncoding); - return new IntArrayTag(name, array); - } + if (UseVarInt) + { + var array = new int[count]; + for (var i = 0; i < count; i++) + array[i] = VarInt.Read(BaseStream, ZigZagEncoding); + return new IntArrayTag(name, array); + } - var buffer = new byte[count * INT_SIZE]; - ReadToFixSizedBuffer(buffer, 0, count * INT_SIZE); + var buffer = new byte[count * INT_SIZE]; + ReadToFixSizedBuffer(buffer, 0, count * INT_SIZE); - Span values = MemoryMarshal.Cast(buffer); - if (SwapEndian) - { - for (var i = 0; i < count; i++) - values[i] = values[i].SwapEndian(); - } - return new IntArrayTag(name, values); + Span values = MemoryMarshal.Cast(buffer); + if (SwapEndian) + { + for (var i = 0; i < count; i++) + values[i] = values[i].SwapEndian(); } + return new IntArrayTag(name, values); + } - /// - /// Reads a from the stream. - /// - /// Flag indicating if this tag is named, only when a tag is a direct child of a . - /// It is assumed that the stream is positioned at the beginning of the tag payload. - /// The deserialized instance. - public LongArrayTag ReadLongArray(bool named = true) - { - const int LONG_SIZE = sizeof(long); + /// + /// Reads a from the stream. + /// + /// Flag indicating if this tag is named, only when a tag is a direct child of a . + /// It is assumed that the stream is positioned at the beginning of the tag payload. + /// The deserialized instance. + public LongArrayTag ReadLongArray(bool named = true) + { + const int LONG_SIZE = sizeof(long); - var name = named ? ReadUTF8String() : null; - var count = ReadCount(); + var name = named ? ReadUTF8String() : null; + var count = ReadCount(); - if (UseVarInt) - { - var array = new long[count]; - for (var i = 0; i < count; i++) - array[i] = VarLong.Read(BaseStream, ZigZagEncoding); - return new LongArrayTag(name, array); - } + if (UseVarInt) + { + var array = new long[count]; + for (var i = 0; i < count; i++) + array[i] = VarLong.Read(BaseStream, ZigZagEncoding); + return new LongArrayTag(name, array); + } - var buffer = new byte[count * LONG_SIZE]; - ReadToFixSizedBuffer(buffer, 0, count * LONG_SIZE); + var buffer = new byte[count * LONG_SIZE]; + ReadToFixSizedBuffer(buffer, 0, count * LONG_SIZE); - Span values = MemoryMarshal.Cast(buffer); - if (SwapEndian) - { - for (var i = 0; i < count; i++) - values[i] = values[i].SwapEndian(); - } - return new LongArrayTag(name, values); + Span values = MemoryMarshal.Cast(buffer); + if (SwapEndian) + { + for (var i = 0; i < count; i++) + values[i] = values[i].SwapEndian(); } + return new LongArrayTag(name, values); + } - /// - /// Reads a from the stream. - /// - /// Flag indicating if this tag is named, only when a tag is a direct child of a . - /// It is assumed that the stream is positioned at the beginning of the tag payload. - /// The deserialized instance. - public ListTag ReadList(bool named = true) - { - var name = named ? ReadUTF8String() : null; - var childType = ReadType(); - var count = ReadCount(); + /// + /// Reads a from the stream. + /// + /// Flag indicating if this tag is named, only when a tag is a direct child of a . + /// It is assumed that the stream is positioned at the beginning of the tag payload. + /// The deserialized instance. + public ListTag ReadList(bool named = true) + { + var name = named ? ReadUTF8String() : null; + var childType = ReadType(); + var count = ReadCount(); - if (childType == TagType.End && count > 0) - throw new FormatException(Strings.InvalidEndTagChild); + if (childType == TagType.End && count > 0) + throw new FormatException(Strings.InvalidEndTagChild); - var list = new ListTag(name, childType); - while (count-- > 0) - { - list.Add(ReadTag(childType, false)); - } - return list; + var list = new ListTag(name, childType); + while (count-- > 0) + { + list.Add(ReadTag(childType, false)); } + return list; + } - /// - /// Reads a from the stream. - /// - /// Flag indicating if this tag is named, only when a tag is a direct child of a . - /// It is assumed that the stream is positioned at the beginning of the tag payload. - /// The deserialized instance. - public CompoundTag ReadCompound(bool named = true) - { - var name = named ? ReadUTF8String() : null; - var compound = new CompoundTag(name); + /// + /// Reads a from the stream. + /// + /// Flag indicating if this tag is named, only when a tag is a direct child of a . + /// It is assumed that the stream is positioned at the beginning of the tag payload. + /// The deserialized instance. + public CompoundTag ReadCompound(bool named = true) + { + var name = named ? ReadUTF8String() : null; + var compound = new CompoundTag(name); - while (true) - { - var type = ReadType(); - if (type == TagType.End) - break; - - compound.Add(ReadTag(type, true)); - } - - return compound; - } - - /// - /// Reads a from the current position in the stream. - /// - /// Flag indicating if this tag is named, only when a tag is a direct child of a . - /// The tag instance that was read from the stream. - public Tag ReadTag(bool named = true) + while (true) { var type = ReadType(); - return ReadTag(type, named); + if (type == TagType.End) + break; + + compound.Add(ReadTag(type, true)); } - /// - /// Asynchronously reads a from the current position in the stream. - /// - /// Flag indicating if this tag is named, only when a tag is a direct child of a . - /// The tag instance that was read from the stream. - public async Task ReadTagAsync(bool named = true) - { - return await Task.Run(() => ReadTag(named)); - } + return compound; + } + + /// + /// Reads a from the current position in the stream. + /// + /// Flag indicating if this tag is named, only when a tag is a direct child of a . + /// The tag instance that was read from the stream. + public Tag ReadTag(bool named = true) + { + var type = ReadType(); + return ReadTag(type, named); + } + + /// + /// Asynchronously reads a from the current position in the stream. + /// + /// Flag indicating if this tag is named, only when a tag is a direct child of a . + /// The tag instance that was read from the stream. + public async Task ReadTagAsync(bool named = true) + { + return await Task.Run(() => ReadTag(named)); + } - /// - /// Convenience method to read a tag and cast it automatically. - /// - /// Flag indicating if this tag is named, only when a tag is a direct child of a . - /// The tag type that is being read from the stream. - /// The tag instance that was read from the stream. - /// This is typically only used when reading the top-level of a document where the type is already known. - public T ReadTag(bool named = true) where T : Tag + /// + /// Convenience method to read a tag and cast it automatically. + /// + /// Flag indicating if this tag is named, only when a tag is a direct child of a . + /// The tag type that is being read from the stream. + /// The tag instance that was read from the stream. + /// This is typically only used when reading the top-level of a document where the type is already known. + public T ReadTag(bool named = true) where T : Tag + { + return (T)ReadTag(named); + } + + /// + /// Convenience method to asynchronously read a tag and cast it automatically. + /// + /// Flag indicating if this tag is named, only when a tag is a direct child of a . + /// The tag type that is being read from the stream. + /// The tag instance that was read from the stream. + /// This is typically only used when reading the top-level of a document where the type is already known. + public async Task ReadTagAsync(bool named = true) where T : Tag + { + var tag = await ReadTagAsync(named); + return (T)tag; + } + + private Tag ReadTag(TagType type, bool named) + { + var result = OnTagEncountered(type, named); + if (result != null) { - return (T)ReadTag(named); + OnTagRead(result); + return result; } - /// - /// Convenience method to asynchronously read a tag and cast it automatically. - /// - /// Flag indicating if this tag is named, only when a tag is a direct child of a . - /// The tag type that is being read from the stream. - /// The tag instance that was read from the stream. - /// This is typically only used when reading the top-level of a document where the type is already known. - public async Task ReadTagAsync(bool named = true) where T : Tag + Tag tag = type switch { - var tag = await ReadTagAsync(named); - return (T)tag; - } + TagType.End => new EndTag(), + TagType.Byte => ReadByte(named), + TagType.Short => ReadShort(named), + TagType.Int => ReadInt(named), + TagType.Long => ReadLong(named), + TagType.Float => ReadFloat(named), + TagType.Double => ReadDouble(named), + TagType.ByteArray => ReadByteArray(named), + TagType.String => ReadString(named), + TagType.List => ReadList(named), + TagType.Compound => ReadCompound(named), + TagType.IntArray => ReadIntArray(named), + TagType.LongArray => ReadLongArray(named), + _ => throw new ArgumentOutOfRangeException(nameof(type), type, null) + }; + OnTagRead(tag); + return tag; + } + - [NotNull] - private Tag ReadTag(TagType type, bool named) + private TagType ReadType() + { + try { - var result = OnTagEncountered(type, named); - if (result != null) - { - OnTagRead(result); - return result; - } - - Tag tag = type switch - { - TagType.End => new EndTag(), - TagType.Byte => ReadByte(named), - TagType.Short => ReadShort(named), - TagType.Int => ReadInt(named), - TagType.Long => ReadLong(named), - TagType.Float => ReadFloat(named), - TagType.Double => ReadDouble(named), - TagType.ByteArray => ReadByteArray(named), - TagType.String => ReadString(named), - TagType.List => ReadList(named), - TagType.Compound => ReadCompound(named), - TagType.IntArray => ReadIntArray(named), - TagType.LongArray => ReadLongArray(named), - _ => throw new ArgumentOutOfRangeException(nameof(type), type, null) - }; - OnTagRead(tag); - return tag; + return (TagType)BaseStream.ReadByte(); } - - - private TagType ReadType() + catch (EndOfStreamException) { - try - { - return (TagType)BaseStream.ReadByte(); - } - catch (EndOfStreamException) - { - return TagType.End; - } + return TagType.End; } + } - /// - /// Reads a length-prefixed UTF-8 string from the stream. - /// - /// The deserialized string instance. - [CanBeNull] - protected string ReadUTF8String() + /// + /// Reads a length-prefixed UTF-8 string from the stream. + /// + /// The deserialized string instance. + protected string? ReadUTF8String() + { + int length; + if (UseVarInt) + length = VarInt.Read(BaseStream); + else { - int length; - if (UseVarInt) - length = VarInt.Read(BaseStream); - else - { - Span buffer = stackalloc byte[sizeof(ushort)]; - ReadToFixSizedBuffer(buffer); - var uint16 = BitConverter.ToUInt16(buffer); - length = SwapEndian ? uint16.SwapEndian() : uint16; - } + Span buffer = stackalloc byte[sizeof(ushort)]; + ReadToFixSizedBuffer(buffer); + var uint16 = BitConverter.ToUInt16(buffer); + length = SwapEndian ? uint16.SwapEndian() : uint16; + } - if (length == 0) - return null; + if (length == 0) + return null; - var utf8 = new byte[length]; - ReadToFixSizedBuffer(utf8, 0, length); - return Encoding.UTF8.GetString(utf8); - } + var utf8 = new byte[length]; + ReadToFixSizedBuffer(utf8, 0, length); + return Encoding.UTF8.GetString(utf8); + } - private int ReadCount() => UseVarInt ? VarInt.Read(BaseStream, ZigZagEncoding) : ReadInt32(); + private int ReadCount() => UseVarInt ? VarInt.Read(BaseStream, ZigZagEncoding) : ReadInt32(); - /// - /// Reads a 64-bit signed (big-endian) integer from the stream, converting to native endian when necessary. - /// - /// The deserialized value. - private int ReadInt32() - { - Span buffer = stackalloc byte[sizeof(int)]; - ReadToFixSizedBuffer(buffer); - var value = BitConverter.ToInt32(buffer); - return SwapEndian ? value.SwapEndian() : value; - } + /// + /// Reads a 64-bit signed (big-endian) integer from the stream, converting to native endian when necessary. + /// + /// The deserialized value. + private int ReadInt32() + { + Span buffer = stackalloc byte[sizeof(int)]; + ReadToFixSizedBuffer(buffer); + var value = BitConverter.ToInt32(buffer); + return SwapEndian ? value.SwapEndian() : value; + } - /// - /// Reads bytes from the streams and stores them into the . - /// The number of read bytes is dictated by the size of the buffer. - /// This method ensures that all requested bytes are read. - /// - /// - /// Use this instead of BaseStream.Read(buffer). - /// There was a breaking change in .NET 6 where the can read less bytes than requested for certain streams. - /// Read more here: https://docs.microsoft.com/en-us/dotnet/core/compatibility/core-libraries/6.0/partial-byte-reads-in-streams - /// - /// The buffer where the read bytes are written to. the buffer size defines the number of bytes to read. - /// Throws if no more bytes could be read from the stream, but the buffer wasn't completely filled yet. - protected void ReadToFixSizedBuffer(Span buffer) + /// + /// Reads bytes from the streams and stores them into the . + /// The number of read bytes is dictated by the size of the buffer. + /// This method ensures that all requested bytes are read. + /// + /// + /// Use this instead of BaseStream.Read(buffer). + /// There was a breaking change in .NET 6 where the can read less bytes than requested for certain streams. + /// Read more here: https://docs.microsoft.com/en-us/dotnet/core/compatibility/core-libraries/6.0/partial-byte-reads-in-streams + /// + /// The buffer where the read bytes are written to. the buffer size defines the number of bytes to read. + /// Throws if no more bytes could be read from the stream, but the buffer wasn't completely filled yet. + protected void ReadToFixSizedBuffer(Span buffer) + { + var totalBytes = 0; + while (totalBytes < buffer.Length) { - var totalBytes = 0; - while (totalBytes < buffer.Length) - { - var readBytes = BaseStream.Read(buffer.Slice(totalBytes)); - if (readBytes == 0) - throw new EndOfStreamException(); - totalBytes += readBytes; - } + var readBytes = BaseStream.Read(buffer.Slice(totalBytes)); + if (readBytes == 0) + throw new EndOfStreamException(); + totalBytes += readBytes; } + } - /// - /// Reads bytes from the streams and stores them into the . - /// This method ensures that all requested bytes are read. - /// - /// - /// Use this instead of BaseStream.Read(buffer, offset, count). - /// There was a breaking change in .NET 6 where the can read less bytes than requested for certain streams. - /// Read more here: https://docs.microsoft.com/en-us/dotnet/core/compatibility/core-libraries/6.0/partial-byte-reads-in-streams - /// - /// The buffer where the read bytes are written to. The data will be stored starting at to + - 1. - /// The offset in where the read data is stored. - /// The number of bytes to read. Must be positive. - /// Throws if no more bytes could be read from the stream, but the buffer wasn't completely filled yet. - protected void ReadToFixSizedBuffer(byte[] buffer, int offset, int count) - { - ReadToFixSizedBuffer(new Span(buffer, offset, count)); - } + /// + /// Reads bytes from the streams and stores them into the . + /// This method ensures that all requested bytes are read. + /// + /// + /// Use this instead of BaseStream.Read(buffer, offset, count). + /// There was a breaking change in .NET 6 where the can read less bytes than requested for certain streams. + /// Read more here: https://docs.microsoft.com/en-us/dotnet/core/compatibility/core-libraries/6.0/partial-byte-reads-in-streams + /// + /// The buffer where the read bytes are written to. The data will be stored starting at to + - 1. + /// The offset in where the read data is stored. + /// The number of bytes to read. Must be positive. + /// Throws if no more bytes could be read from the stream, but the buffer wasn't completely filled yet. + protected void ReadToFixSizedBuffer(byte[] buffer, int offset, int count) + { + ReadToFixSizedBuffer(new Span(buffer, offset, count)); + } - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - /// - public override void Dispose() - { - if (!leaveOpen) - BaseStream.Dispose(); - } + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public override void Dispose() + { + if (!leaveOpen) + BaseStream.Dispose(); + } - /// - /// Asynchronously releases the unmanaged resources used by the . - /// - public override async ValueTask DisposeAsync() - { - if (!leaveOpen) - await BaseStream.DisposeAsync(); - } + /// + /// Asynchronously releases the unmanaged resources used by the . + /// + public override async ValueTask DisposeAsync() + { + if (!leaveOpen) + await BaseStream.DisposeAsync(); + } - /// - /// Invokes the event when a tag has been fully deserialized from the . - /// - /// The deserialized instance. - protected virtual void OnTagRead(Tag tag) => TagRead?.Invoke(this, new TagEventArgs(tag.Type, tag)); + /// + /// Invokes the event when a tag has been fully deserialized from the . + /// + /// The deserialized instance. + protected virtual void OnTagRead(Tag tag) => TagRead?.Invoke(this, new TagEventArgs(tag.Type, tag)); - /// - /// Invokes the event when the stream is positioned at the beginning of a an unread tag. - /// - /// The type of tag next to be read from the stream. - /// Flag indicating if this tag is named. - /// When handled by an event subscriber, returns a parsed instance, otherwise returns . - [CanBeNull] - protected virtual Tag OnTagEncountered(TagType type, bool named) - { - // Early out if no subscribers. - if (TagEncountered is null) - return null; + /// + /// Invokes the event when the stream is positioned at the beginning of a an unread tag. + /// + /// The type of tag next to be read from the stream. + /// Flag indicating if this tag is named. + /// When handled by an event subscriber, returns a parsed instance, otherwise returns . + protected virtual Tag? OnTagEncountered(TagType type, bool named) + { + // Early out if no subscribers. + if (TagEncountered is null) + return null; - var args = new TagHandledEventArgs(type, named, BaseStream); - TagEncountered.Invoke(this, args); - return args.Handled ? args.Result : null; - } + var args = new TagHandledEventArgs(type, named, BaseStream); + TagEncountered.Invoke(this, args); + return args.Handled ? args.Result : null; } } \ No newline at end of file diff --git a/SharpNBT/TagWriter.cs b/SharpNBT/TagWriter.cs index b9cfaa0..9f05a99 100644 --- a/SharpNBT/TagWriter.cs +++ b/SharpNBT/TagWriter.cs @@ -7,382 +7,381 @@ using System.Threading.Tasks; using JetBrains.Annotations; -namespace SharpNBT +namespace SharpNBT; + +/// +/// Provides methods for writing NBT tags to a stream. +/// +[PublicAPI] +public class TagWriter : TagIO { + private readonly bool leaveOpen; + /// - /// Provides methods for writing NBT tags to a stream. + /// Creates a new instance of the class from the given . /// - [PublicAPI] - public class TagWriter : TagIO + /// A instance that the writer will be writing to. + /// Bitwise flags to configure how data should be handled for compatibility between different specifications. + /// + /// to leave the object open after disposing the + /// object; otherwise, . + public TagWriter(Stream stream, FormatOptions options, bool leaveOpen = false) : base(stream, options) { - private readonly bool leaveOpen; - - /// - /// Creates a new instance of the class from the given . - /// - /// A instance that the writer will be writing to. - /// Bitwise flags to configure how data should be handled for compatibility between different specifications. - /// - /// to leave the object open after disposing the - /// object; otherwise, . - public TagWriter([NotNull] Stream stream, FormatOptions options, bool leaveOpen = false) : base(stream, options) - { - if (!stream.CanWrite) - throw new IOException(Strings.CannotWriteStream); - this.leaveOpen = leaveOpen; - } + if (!stream.CanWrite) + throw new IOException(Strings.CannotWriteStream); + this.leaveOpen = leaveOpen; + } - /// - /// Writes a to the stream. - /// - /// The instance to write. - public virtual void WriteByte(ByteTag tag) - { - WriteTypeAndName(tag); - BaseStream.WriteByte(tag.Value); - } + /// + /// Writes a to the stream. + /// + /// The instance to write. + public virtual void WriteByte(ByteTag tag) + { + WriteTypeAndName(tag); + BaseStream.WriteByte(tag.Value); + } - /// - /// Writes a to the stream. - /// - /// The instance to write. - public virtual void WriteShort(ShortTag tag) - { - WriteTypeAndName(tag); - if (UseVarInt) - VarInt.Write(BaseStream, tag.Value, ZigZagEncoding); - else - BaseStream.Write(GetBytes(tag.Value), 0, sizeof(short)); - } + /// + /// Writes a to the stream. + /// + /// The instance to write. + public virtual void WriteShort(ShortTag tag) + { + WriteTypeAndName(tag); + if (UseVarInt) + VarInt.Write(BaseStream, tag.Value, ZigZagEncoding); + else + BaseStream.Write(GetBytes(tag.Value), 0, sizeof(short)); + } - /// - /// Writes a to the stream. - /// - /// The instance to write. - public virtual void WriteInt(IntTag tag) - { - WriteTypeAndName(tag); - if (UseVarInt) - VarInt.Write(BaseStream, tag.Value, ZigZagEncoding); - else - BaseStream.Write(GetBytes(tag.Value), 0, sizeof(int)); - } + /// + /// Writes a to the stream. + /// + /// The instance to write. + public virtual void WriteInt(IntTag tag) + { + WriteTypeAndName(tag); + if (UseVarInt) + VarInt.Write(BaseStream, tag.Value, ZigZagEncoding); + else + BaseStream.Write(GetBytes(tag.Value), 0, sizeof(int)); + } - /// - /// Writes a to the stream. - /// - /// The instance to write. - public virtual void WriteLong(LongTag tag) - { - WriteTypeAndName(tag); - if (UseVarInt) - VarLong.Write(BaseStream, tag.Value, ZigZagEncoding); - else - BaseStream.Write(GetBytes(tag.Value), 0, sizeof(long)); - } + /// + /// Writes a to the stream. + /// + /// The instance to write. + public virtual void WriteLong(LongTag tag) + { + WriteTypeAndName(tag); + if (UseVarInt) + VarLong.Write(BaseStream, tag.Value, ZigZagEncoding); + else + BaseStream.Write(GetBytes(tag.Value), 0, sizeof(long)); + } - /// - /// Writes a to the stream. - /// - /// The instance to write. - public virtual void WriteFloat(FloatTag tag) - { - WriteTypeAndName(tag); - BaseStream.Write(GetBytes(tag.Value), 0, sizeof(float)); - } + /// + /// Writes a to the stream. + /// + /// The instance to write. + public virtual void WriteFloat(FloatTag tag) + { + WriteTypeAndName(tag); + BaseStream.Write(GetBytes(tag.Value), 0, sizeof(float)); + } - /// - /// Writes a to the stream. - /// - /// The instance to write. - public virtual void WriteDouble(DoubleTag tag) - { - WriteTypeAndName(tag); - BaseStream.Write(GetBytes(tag.Value), 0, sizeof(double)); - } + /// + /// Writes a to the stream. + /// + /// The instance to write. + public virtual void WriteDouble(DoubleTag tag) + { + WriteTypeAndName(tag); + BaseStream.Write(GetBytes(tag.Value), 0, sizeof(double)); + } - /// - /// Writes a to the stream. - /// - /// The instance to write. - public virtual void WriteString(StringTag tag) - { - WriteTypeAndName(tag); - WriteUTF8String(tag.Value); - } + /// + /// Writes a to the stream. + /// + /// The instance to write. + public virtual void WriteString(StringTag tag) + { + WriteTypeAndName(tag); + WriteUTF8String(tag.Value); + } - /// - /// Writes a to the stream. - /// - /// The instance to write. - public virtual void WriteByteArray(ByteArrayTag tag) + /// + /// Writes a to the stream. + /// + /// The instance to write. + public virtual void WriteByteArray(ByteArrayTag tag) + { + WriteTypeAndName(tag); + WriteCount(tag); + BaseStream.Write(tag.ToArray(), 0, tag.Count); + } + + /// + /// Writes a to the stream. + /// + /// The instance to write. + public virtual void WriteIntArray(IntArrayTag tag) + { + WriteTypeAndName(tag); + WriteCount(tag); + + var values = new Span(tag.ToArray()); + if (UseVarInt) { - WriteTypeAndName(tag); - WriteCount(tag); - BaseStream.Write(tag.ToArray(), 0, tag.Count); + // VarInt is effectively always little-endian + foreach (var n in values) + VarInt.Write(BaseStream, n, ZigZagEncoding); + return; } - - /// - /// Writes a to the stream. - /// - /// The instance to write. - public virtual void WriteIntArray(IntArrayTag tag) + if (SwapEndian) { - WriteTypeAndName(tag); - WriteCount(tag); - - var values = new Span(tag.ToArray()); - if (UseVarInt) - { - // VarInt is effectively always little-endian - foreach (var n in values) - VarInt.Write(BaseStream, n, ZigZagEncoding); - return; - } - if (SwapEndian) - { - for (var i = 0; i < values.Length; i++) - values[i] = values[i].SwapEndian(); - } - BaseStream.Write(MemoryMarshal.AsBytes(values)); + for (var i = 0; i < values.Length; i++) + values[i] = values[i].SwapEndian(); } + BaseStream.Write(MemoryMarshal.AsBytes(values)); + } - /// - /// Writes a to the stream. - /// - /// The instance to write. - public virtual void WriteLongArray(LongArrayTag tag) - { + /// + /// Writes a to the stream. + /// + /// The instance to write. + public virtual void WriteLongArray(LongArrayTag tag) + { - WriteTypeAndName(tag); - WriteCount(tag); + WriteTypeAndName(tag); + WriteCount(tag); - var values = new Span(tag.ToArray()); - if (UseVarInt) - { - // VarLong is effectively always little-endian - foreach (var n in values) - VarLong.Write(BaseStream, n, ZigZagEncoding); - return; - } - if (SwapEndian) - { - for (var i = 0; i < values.Length; i++) - values[i] = values[i].SwapEndian(); + var values = new Span(tag.ToArray()); + if (UseVarInt) + { + // VarLong is effectively always little-endian + foreach (var n in values) + VarLong.Write(BaseStream, n, ZigZagEncoding); + return; + } + if (SwapEndian) + { + for (var i = 0; i < values.Length; i++) + values[i] = values[i].SwapEndian(); - } - BaseStream.Write(MemoryMarshal.AsBytes(values)); } + BaseStream.Write(MemoryMarshal.AsBytes(values)); + } - /// - /// Writes a to the stream. - /// - /// The instance to write. - public virtual void WriteList(ListTag tag) - { - WriteTypeAndName(tag); - BaseStream.WriteByte((byte) tag.ChildType); - WriteCount(tag); + /// + /// Writes a to the stream. + /// + /// The instance to write. + public virtual void WriteList(ListTag tag) + { + WriteTypeAndName(tag); + BaseStream.WriteByte((byte) tag.ChildType); + WriteCount(tag); - foreach (var child in tag) - WriteTag(child); - } + foreach (var child in tag) + WriteTag(child); + } - /// - /// Writes a to the stream. - /// - /// The instance to write. - public virtual void WriteCompound(CompoundTag tag) + /// + /// Writes a to the stream. + /// + /// The instance to write. + public virtual void WriteCompound(CompoundTag tag) + { + WriteTypeAndName(tag); + foreach (var child in tag) { - WriteTypeAndName(tag); - foreach (var child in tag) - { - if (tag.Type == TagType.End) - break; + if (tag.Type == TagType.End) + break; - child.Parent = tag; - WriteTag(child); - } - - BaseStream.WriteByte((byte) TagType.End); + child.Parent = tag; + WriteTag(child); } + + BaseStream.WriteByte((byte) TagType.End); + } - /// - /// Convenience method to build and write a instance to the underlying stream. - /// - /// A instance to write. - public virtual void WriteBuilder([NotNull] TagBuilder builder) => WriteCompound(builder.Create()); + /// + /// Convenience method to build and write a instance to the underlying stream. + /// + /// A instance to write. + public virtual void WriteBuilder(TagBuilder builder) => WriteCompound(builder.Create()); - /// - /// - /// - /// - public virtual void WriteEndTag([CanBeNull] EndTag tag = null) => BaseStream.WriteByte(0); + /// + /// + /// + /// + public virtual void WriteEndTag(EndTag? tag = null) => BaseStream.WriteByte(0); - /// - /// Writes the given to the stream. - /// - /// The instance to be written. - /// Thrown when the tag type is unrecognized. - public void WriteTag(Tag tag) + /// + /// Writes the given to the stream. + /// + /// The instance to be written. + /// Thrown when the tag type is unrecognized. + public void WriteTag(Tag tag) + { + switch (tag.Type) { - switch (tag.Type) - { - case TagType.End: - WriteEndTag((EndTag) tag); - break; - case TagType.Byte: - WriteByte((ByteTag) tag); - break; - case TagType.Short: - WriteShort((ShortTag) tag); - break; - case TagType.Int: - WriteInt((IntTag) tag); - break; - case TagType.Long: - WriteLong((LongTag)tag); - break; - case TagType.Float: - WriteFloat((FloatTag)tag); - break; - case TagType.Double: - WriteDouble((DoubleTag)tag); - break; - case TagType.ByteArray: - WriteByteArray((ByteArrayTag)tag); - break; - case TagType.String: - WriteString((StringTag)tag); - break; - case TagType.List: - WriteList((ListTag)tag); - break; - case TagType.Compound: - WriteCompound((CompoundTag)tag); - break; - case TagType.IntArray: - WriteIntArray((IntArrayTag)tag); - break; - case TagType.LongArray: - WriteLongArray((LongArrayTag)tag); - break; - default: - throw new ArgumentOutOfRangeException(nameof(tag.Type), Strings.UnknownTagType); - } + case TagType.End: + WriteEndTag((EndTag) tag); + break; + case TagType.Byte: + WriteByte((ByteTag) tag); + break; + case TagType.Short: + WriteShort((ShortTag) tag); + break; + case TagType.Int: + WriteInt((IntTag) tag); + break; + case TagType.Long: + WriteLong((LongTag)tag); + break; + case TagType.Float: + WriteFloat((FloatTag)tag); + break; + case TagType.Double: + WriteDouble((DoubleTag)tag); + break; + case TagType.ByteArray: + WriteByteArray((ByteArrayTag)tag); + break; + case TagType.String: + WriteString((StringTag)tag); + break; + case TagType.List: + WriteList((ListTag)tag); + break; + case TagType.Compound: + WriteCompound((CompoundTag)tag); + break; + case TagType.IntArray: + WriteIntArray((IntArrayTag)tag); + break; + case TagType.LongArray: + WriteLongArray((LongArrayTag)tag); + break; + default: + throw new ArgumentOutOfRangeException(nameof(tag.Type), Strings.UnknownTagType); } + } - /// - /// Asynchronously writes the given to the stream. - /// - /// The instance to be written. - /// Thrown when the tag type is unrecognized. - public async Task WriteTagAsync(Tag tag) - { - await Task.Run(() => WriteTag(tag)); - } + /// + /// Asynchronously writes the given to the stream. + /// + /// The instance to be written. + /// Thrown when the tag type is unrecognized. + public async Task WriteTagAsync(Tag tag) + { + await Task.Run(() => WriteTag(tag)); + } - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - /// - public override void Dispose() - { - BaseStream.Flush(); - if (!leaveOpen) - BaseStream.Dispose(); - } + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public override void Dispose() + { + BaseStream.Flush(); + if (!leaveOpen) + BaseStream.Dispose(); + } - /// - /// Asynchronously releases the unmanaged resources used by the . - /// - public override async ValueTask DisposeAsync() - { - await BaseStream.FlushAsync(); - if (!leaveOpen) - await BaseStream.DisposeAsync(); - } + /// + /// Asynchronously releases the unmanaged resources used by the . + /// + public override async ValueTask DisposeAsync() + { + await BaseStream.FlushAsync(); + if (!leaveOpen) + await BaseStream.DisposeAsync(); + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void WriteTypeAndName(Tag tag) - { - if (tag.Parent is ListTag) - return; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void WriteTypeAndName(Tag tag) + { + if (tag.Parent is ListTag) + return; - BaseStream.WriteByte((byte) tag.Type); - WriteUTF8String(tag.Name); - } + BaseStream.WriteByte((byte) tag.Type); + WriteUTF8String(tag.Name); + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void WriteUTF8String(string value) - { - // String length prefixes never use ZigZag encoding + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void WriteUTF8String(string value) + { + // String length prefixes never use ZigZag encoding - if (string.IsNullOrEmpty(value)) - { - if (UseVarInt) - VarInt.Write(BaseStream, 0); - else - BaseStream.Write(GetBytes((ushort) 0), 0, sizeof(ushort)); - } + if (string.IsNullOrEmpty(value)) + { + if (UseVarInt) + VarInt.Write(BaseStream, 0); else - { - var utf8 = Encoding.UTF8.GetBytes(value); - if (UseVarInt) - VarInt.Write(BaseStream, utf8.Length); - else - BaseStream.Write(GetBytes((ushort) utf8.Length), 0, sizeof(ushort)); + BaseStream.Write(GetBytes((ushort) 0), 0, sizeof(ushort)); + } + else + { + var utf8 = Encoding.UTF8.GetBytes(value); + if (UseVarInt) + VarInt.Write(BaseStream, utf8.Length); + else + BaseStream.Write(GetBytes((ushort) utf8.Length), 0, sizeof(ushort)); - BaseStream.Write(utf8, 0, utf8.Length); - } + BaseStream.Write(utf8, 0, utf8.Length); } + } - /// - /// Gets the bytes for this number, accounting for the host machine endianness and target format. - /// - /// The value to convert. - /// An array of bytes representing the value in compatible format. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private byte[] GetBytes(short n) => BitConverter.GetBytes(SwapEndian ? n.SwapEndian() : n); + /// + /// Gets the bytes for this number, accounting for the host machine endianness and target format. + /// + /// The value to convert. + /// An array of bytes representing the value in compatible format. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private byte[] GetBytes(short n) => BitConverter.GetBytes(SwapEndian ? n.SwapEndian() : n); - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private byte[] GetBytes(int n) => BitConverter.GetBytes(SwapEndian ? n.SwapEndian() : n); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private byte[] GetBytes(int n) => BitConverter.GetBytes(SwapEndian ? n.SwapEndian() : n); - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private byte[] GetBytes(long n) => BitConverter.GetBytes(SwapEndian ? n.SwapEndian() : n); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private byte[] GetBytes(long n) => BitConverter.GetBytes(SwapEndian ? n.SwapEndian() : n); - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private byte[] GetBytes(ushort n) => BitConverter.GetBytes(SwapEndian ? n.SwapEndian() : n); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private byte[] GetBytes(ushort n) => BitConverter.GetBytes(SwapEndian ? n.SwapEndian() : n); - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private byte[] GetBytes(float n) - { - var bytes = BitConverter.GetBytes(n); - if (SwapEndian) - Array.Reverse(bytes); - return bytes; - } + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private byte[] GetBytes(float n) + { + var bytes = BitConverter.GetBytes(n); + if (SwapEndian) + Array.Reverse(bytes); + return bytes; + } - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private byte[] GetBytes(double n) - { - var bytes = BitConverter.GetBytes(n); - if (SwapEndian) - Array.Reverse(bytes); - return bytes; - } + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private byte[] GetBytes(double n) + { + var bytes = BitConverter.GetBytes(n); + if (SwapEndian) + Array.Reverse(bytes); + return bytes; + } - private void WriteCount(EnumerableTag tag) - { - if (UseVarInt) - VarInt.Write(BaseStream, tag.Count, ZigZagEncoding); - else - BaseStream.Write(GetBytes(tag.Count), 0, sizeof(int)); - } + private void WriteCount(EnumerableTag tag) + { + if (UseVarInt) + VarInt.Write(BaseStream, tag.Count, ZigZagEncoding); + else + BaseStream.Write(GetBytes(tag.Count), 0, sizeof(int)); } } \ No newline at end of file diff --git a/SharpNBT/Tags/BoolTag.cs b/SharpNBT/Tags/BoolTag.cs index f5ea46d..1f23fb7 100644 --- a/SharpNBT/Tags/BoolTag.cs +++ b/SharpNBT/Tags/BoolTag.cs @@ -2,55 +2,54 @@ using System.Runtime.Serialization; using JetBrains.Annotations; -namespace SharpNBT +namespace SharpNBT; + +/// +/// A tag that contains a single 8-bit integer value. +/// +/// +/// This tag type does not exist in the NBT specification, and is included for convenience to differentiate it from the that it is +/// actually serialized as. +/// +[PublicAPI][Serializable] +public class BoolTag : Tag { + private const string TRUE = "true"; + private const string FALSE = "false"; + /// - /// A tag that contains a single 8-bit integer value. + /// Creates a new instance of the class with the specified . /// - /// - /// This tag type does not exist in the NBT specification, and is included for convenience to differentiate it from the that it is - /// actually serialized as. - /// - [PublicAPI][Serializable] - public class BoolTag : Tag + /// 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) { - private const string TRUE = "true"; - private const string FALSE = "false"; - - /// - /// 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([CanBeNull] string name, bool value) : base(TagType.Byte, name, value) - { - } + } - /// - /// Required constructor for ISerializable implementation. - /// - /// The to describing this instance. - /// The destination (see ) for this serialization. - protected BoolTag(SerializationInfo info, StreamingContext context) : base(info, context) - { - } + /// + /// Required constructor for ISerializable implementation. + /// + /// The to describing this instance. + /// The destination (see ) for this serialization. + protected BoolTag(SerializationInfo info, StreamingContext context) : base(info, context) + { + } - /// - public override string ToString() => $"TAG_Bool({PrettyName}): {(Value ? TRUE : FALSE)}"; + /// + public override string ToString() => $"TAG_Bool({PrettyName}): {(Value ? TRUE : FALSE)}"; - /// - /// Implicit conversion of this tag to a . - /// - /// The tag to convert. - /// The tag represented as a . - public static implicit operator bool(BoolTag tag) => tag.Value; + /// + /// Implicit conversion of this tag to a . + /// + /// The tag to convert. + /// The tag represented as a . + public static implicit operator bool(BoolTag tag) => tag.Value; - /// - /// Gets the string representation of this NBT tag (SNBT). - /// - /// This NBT tag in SNBT format. - /// - public override string Stringify() => $"{StringifyName}{(Value ? TRUE : FALSE)}"; + /// + /// Gets the string representation of this NBT tag (SNBT). + /// + /// This NBT tag in SNBT format. + /// + 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 4638e7f..e964df2 100644 --- a/SharpNBT/Tags/ByteArrayTag.cs +++ b/SharpNBT/Tags/ByteArrayTag.cs @@ -3,72 +3,71 @@ using System.Runtime.Serialization; using JetBrains.Annotations; -namespace SharpNBT +namespace SharpNBT; + +/// +/// A tag that whose value is a contiguous sequence of 8-bit integers. +/// +/// +/// While this class uses the CLS compliant (0..255), the NBT specification uses a signed value with a range of -128..127, so ensure +/// the bits are equivalent for your values. +/// +[PublicAPI][Serializable] +public class ByteArrayTag : EnumerableTag { /// - /// A tag that whose value is a contiguous sequence of 8-bit integers. + /// Initializes a new instance of the . /// - /// - /// While this class uses the CLS compliant (0..255), the NBT specification uses a signed value with a range of -128..127, so ensure - /// the bits are equivalent for your values. - /// - [PublicAPI][Serializable] - 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, byte[] values) : base(TagType.ByteArray, name, values) { - /// - /// Initializes a new instance of the . - /// - /// The name of the tag, or if tag has no name. - /// A collection of values to include in this tag. - public ByteArrayTag([CanBeNull] string name, byte[] values) : base(TagType.ByteArray, name, values) - { - } + } - /// - /// Initializes a new instance of the with the specified . - /// - /// The name of the tag, or if tag has no name. - /// A collection of values to include in this tag. - public ByteArrayTag([CanBeNull] string name, [NotNull] IEnumerable values) : base(TagType.ByteArray, name, values) - { - } + /// + /// Initializes a new instance of the with the specified . + /// + /// 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) + { + } - /// - /// Initializes a new instance of the with the specified . - /// - /// The name of the tag, or if tag has no name. - /// A collection of values to include in this tag. - public ByteArrayTag([CanBeNull] string name, ReadOnlySpan values) : base(TagType.ByteArray, name, values) - { - } + /// + /// Initializes a new instance of the with the specified . + /// + /// 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) + { + } - /// - /// Required constructor for ISerializable implementation. - /// - /// The to describing this instance. - /// The destination (see ) for this serialization. - protected ByteArrayTag(SerializationInfo info, StreamingContext context) : base(info, context) - { - } + /// + /// Required constructor for ISerializable implementation. + /// + /// The to describing this instance. + /// The destination (see ) for this serialization. + protected ByteArrayTag(SerializationInfo info, StreamingContext context) : base(info, context) + { + } - /// - public override string ToString() - { - var word = Count == 1 ? Strings.WordElement : Strings.WordElements; - return $"TAG_Byte_Array({PrettyName}): [{Count} {word}]"; - } + /// + public override string ToString() + { + var word = Count == 1 ? Strings.WordElement : Strings.WordElements; + return $"TAG_Byte_Array({PrettyName}): [{Count} {word}]"; + } - /// - /// Gets the string representation of this NBT tag (SNBT). - /// - /// 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)}]"; - } + /// + /// Gets the string representation of this NBT tag (SNBT). + /// + /// 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)}]"; } } \ No newline at end of file diff --git a/SharpNBT/Tags/ByteTag.cs b/SharpNBT/Tags/ByteTag.cs index de1eb85..9c1c8e1 100644 --- a/SharpNBT/Tags/ByteTag.cs +++ b/SharpNBT/Tags/ByteTag.cs @@ -3,79 +3,78 @@ using System.Text; using JetBrains.Annotations; -namespace SharpNBT +namespace SharpNBT; + +/// +/// A tag that contains a single 8-bit integer value. +/// +/// +/// While this class uses the CLS compliant (0..255), the NBT specification uses a signed value with a range of -128..127. It is +/// recommended to use the property if your language supports a signed 8-bit value, otherwise simply ensure the bits are +/// equivalent. +/// +[PublicAPI][Serializable] +public class ByteTag : Tag { /// - /// A tag that contains a single 8-bit integer value. + /// Gets or sets the value of this tag as an unsigned value. /// /// - /// While this class uses the CLS compliant (0..255), the NBT specification uses a signed value with a range of -128..127. It is - /// recommended to use the property if your language supports a signed 8-bit value, otherwise simply ensure the bits are - /// equivalent. + /// This is only a reinterpretation of the bytes, no actual conversion is performed. /// - [PublicAPI][Serializable] - public class ByteTag : Tag + [CLSCompliant(false)] + public sbyte SignedValue { - /// - /// Gets or sets the value of this tag as an unsigned value. - /// - /// - /// This is only a reinterpretation of the bytes, no actual conversion is performed. - /// - [CLSCompliant(false)] - public sbyte SignedValue - { - get => unchecked((sbyte)Value); - set => Value = unchecked((byte)value); - } + get => unchecked((sbyte)Value); + set => Value = unchecked((byte)value); + } - /// - /// 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 ByteTag([CanBeNull] string name, byte value) : base(TagType.Byte, name, value) - { - } + /// + /// 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 ByteTag(string? name, byte value) : base(TagType.Byte, name, value) + { + } - /// - [CLSCompliant(false)] - public ByteTag([CanBeNull] string name, sbyte value) : base(TagType.Byte, name, unchecked((byte) value)) - { - } + /// + [CLSCompliant(false)] + public ByteTag(string? name, sbyte value) : base(TagType.Byte, name, unchecked((byte) value)) + { + } - /// - /// Required constructor for ISerializable implementation. - /// - /// The to describing this instance. - /// The destination (see ) for this serialization. - protected ByteTag(SerializationInfo info, StreamingContext context) : base(info, context) - { - } + /// + /// Required constructor for ISerializable implementation. + /// + /// The to describing this instance. + /// The destination (see ) for this serialization. + protected ByteTag(SerializationInfo info, StreamingContext context) : base(info, context) + { + } - /// - public override string ToString() => $"TAG_Byte({PrettyName}): {Value}"; + /// + public override string ToString() => $"TAG_Byte({PrettyName}): {Value}"; - /// - /// Implicit conversion of this tag to a . - /// - /// The tag to convert. - /// The tag represented as a . - public static implicit operator byte(ByteTag tag) => tag.Value; + /// + /// Implicit conversion of this tag to a . + /// + /// The tag to convert. + /// The tag represented as a . + public static implicit operator byte(ByteTag tag) => tag.Value; - /// - /// Implicit conversion of this tag to a . - /// - /// The tag to convert. - /// The tag represented as a . - [CLSCompliant(false)] - public static implicit operator sbyte(ByteTag tag) => unchecked((sbyte)tag.Value); + /// + /// Implicit conversion of this tag to a . + /// + /// The tag to convert. + /// The tag represented as a . + [CLSCompliant(false)] + public static implicit operator sbyte(ByteTag tag) => unchecked((sbyte)tag.Value); - /// - /// Gets the string representation of this NBT tag (SNBT). - /// - /// This NBT tag in SNBT format. - /// - public override string Stringify() => $"{StringifyName}{Value}B"; - } + /// + /// Gets the string representation of this NBT tag (SNBT). + /// + /// This NBT tag in SNBT format. + /// + 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 a0aeb4e..b3f2941 100644 --- a/SharpNBT/Tags/CompoundTag.cs +++ b/SharpNBT/Tags/CompoundTag.cs @@ -5,139 +5,135 @@ using System.Text; using JetBrains.Annotations; -namespace SharpNBT +namespace SharpNBT; + +/// +/// Top-level tag that acts as a container for other named tags. +/// +/// +/// This along with the class define the structure of the NBT format. Children are not order-dependent, nor is order guaranteed. The +/// closing does not require to be explicitly added, it will be added automatically during serialization. +/// +[PublicAPI][Serializable] +public class CompoundTag : TagContainer { /// - /// Top-level tag that acts as a container for other named tags. + /// Creates a new instance of the class. /// - /// - /// This along with the class define the structure of the NBT format. Children are not order-dependent, nor is order guaranteed. The - /// closing does not require to be explicitly added, it will be added automatically during serialization. - /// - [PublicAPI][Serializable] - public class CompoundTag : TagContainer + /// The name of the tag, or if tag has no name. + public CompoundTag(string? name) : base(TagType.Compound, name) { - /// - /// Creates a new instance of the class. - /// - /// The name of the tag, or if tag has no name. - public CompoundTag([CanBeNull] string name) : base(TagType.Compound, name) - { - NamedChildren = true; - RequiredType = null; - } + NamedChildren = true; + RequiredType = null; + } - /// - /// Creates a new instance of the class. - /// - /// The name of the tag, or if tag has no name. - /// A collection objects that are children of this object. - public CompoundTag([CanBeNull] string name, [NotNull] IEnumerable values) : this(name) - { - AddRange(values); - } + /// + /// Creates a new instance of the class. + /// + /// The name of the tag, or if tag has no name. + /// A collection objects that are children of this object. + public CompoundTag(string? name, IEnumerable values) : this(name) + { + AddRange(values); + } - /// - /// Required constructor for ISerializable implementation. - /// - /// The to describing this instance. - /// The destination (see ) for this serialization. - protected CompoundTag(SerializationInfo info, StreamingContext context) : base(info, context) - { - } + /// + /// Required constructor for ISerializable implementation. + /// + /// The to describing this instance. + /// The destination (see ) for this serialization. + protected CompoundTag(SerializationInfo info, StreamingContext context) : base(info, context) + { + } - /// Returns a string that represents the current object. - /// A string that represents the current object. - /// - public override string ToString() - { - var word = Count == 1 ? Strings.WordEntry : Strings.WordEntries; - return $"TAG_Compound({PrettyName}): [{Count} {word}]"; - } + /// Returns a string that represents the current object. + /// A string that represents the current object. + /// + public override string ToString() + { + var word = Count == 1 ? Strings.WordEntry : Strings.WordEntries; + return $"TAG_Compound({PrettyName}): [{Count} {word}]"; + } - /// - /// Retrieves a "pretty-printed" multiline string representing the complete tree structure of the tag. - /// - /// The prefix that will be applied to each indent-level of nested nodes in the tree structure. - /// The pretty-printed string. - [NotNull] - public string PrettyPrinted([CanBeNull] string indent = " ") - { - var buffer = new StringBuilder(); - PrettyPrinted(buffer, 0, indent ?? string.Empty); - return buffer.ToString(); - } + /// + /// Retrieves a "pretty-printed" multiline string representing the complete tree structure of the tag. + /// + /// The prefix that will be applied to each indent-level of nested nodes in the tree structure. + /// The pretty-printed string. + public string PrettyPrinted(string? indent = " ") + { + var buffer = new StringBuilder(); + PrettyPrinted(buffer, 0, indent ?? string.Empty); + return buffer.ToString(); + } - /// - /// Searches the children of this tag, returning the first child with the specified . - /// - /// The name of the tag to search for. - /// to recursively search children, otherwise to only search direct descendants. - /// The first tag found with , otherwise if none was found. - [CanBeNull] - public Tag Find([NotNull] string name, bool deep) + /// + /// Searches the children of this tag, returning the first child with the specified . + /// + /// The name of the tag to search for. + /// to recursively search children, otherwise to only search direct descendants. + /// The first tag found with , otherwise if none was found. + public Tag? Find(string name, bool deep) + { + foreach (var tag in this) { - foreach (var tag in this) - { - if (string.CompareOrdinal(name, tag.Name) == 0) - return tag; + if (string.CompareOrdinal(name, tag.Name) == 0) + return tag; - if (deep && tag is CompoundTag child) - { - var result = child.Find(name, true); - if (result != null) - return result; - } + if (deep && tag is CompoundTag child) + { + var result = child.Find(name, true); + if (result != null) + return result; } - - return null; } - /// - /// Retrieves a child tag with the specified , or if no match was found. - /// - /// The name of the tag to retrieve. - [CanBeNull] - public Tag this[[NotNull] string name] => Find(name, false); + return null; + } - /// - protected internal override void PrettyPrinted(StringBuilder buffer, int level, string indent) - { - var space = new StringBuilder(); - for (var i = 0; i < level; i++) - space.Append(indent); + /// + /// Retrieves a child tag with the specified , or if no match was found. + /// + /// The name of the tag to retrieve. + public Tag? this[string name] => Find(name, false); + + /// + protected internal override void PrettyPrinted(StringBuilder buffer, int level, string indent) + { + var space = new StringBuilder(); + for (var i = 0; i < level; i++) + space.Append(indent); - buffer.AppendLine(space + ToString()); - buffer.AppendLine(space + "{"); - foreach (var tag in this) - tag.PrettyPrinted(buffer, level + 1, indent); - buffer.AppendLine(space + "}"); - } + buffer.AppendLine(space + ToString()); + buffer.AppendLine(space + "{"); + foreach (var tag in this) + tag.PrettyPrinted(buffer, level + 1, indent); + buffer.AppendLine(space + "}"); + } - /// - /// Gets the string representation of this NBT tag (SNBT). - /// - /// This NBT tag in SNBT format. - /// - public override string Stringify() - { - var strings = new string[Count]; - for (var i = 0; i < strings.Length; i++) - strings[i] = this[i].Stringify(); + /// + /// Gets the string representation of this NBT tag (SNBT). + /// + /// This NBT tag in SNBT format. + /// + public override string Stringify() + { + var strings = new string[Count]; + for (var i = 0; i < strings.Length; i++) + strings[i] = this[i].Stringify(); - return $"{StringifyName}{{{string.Join(',', strings)}}}"; - } + return $"{StringifyName}{{{string.Join(',', strings)}}}"; + } - /// - /// Gets the string representation of this NBT tag (SNBT). - /// - /// Flag indicating if this is the top-level tag that should be wrapped in braces. - /// This NBT tag in SNBT format. - /// - public string Stringify(bool topLevel) - { - var str = Stringify(); - return topLevel ? $"{{{str}}}" : str; - } + /// + /// Gets the string representation of this NBT tag (SNBT). + /// + /// Flag indicating if this is the top-level tag that should be wrapped in braces. + /// This NBT tag in SNBT format. + /// + public string Stringify(bool topLevel) + { + var str = Stringify(); + return topLevel ? $"{{{str}}}" : str; } } \ No newline at end of file diff --git a/SharpNBT/Tags/DoubleTag.cs b/SharpNBT/Tags/DoubleTag.cs index f745c66..2dde1be 100644 --- a/SharpNBT/Tags/DoubleTag.cs +++ b/SharpNBT/Tags/DoubleTag.cs @@ -2,47 +2,46 @@ using System.Runtime.Serialization; using JetBrains.Annotations; -namespace SharpNBT +namespace SharpNBT; + +/// +/// A tag that contains a single IEEE-754 double-precision floating point number. +/// +[PublicAPI][Serializable] +public class DoubleTag : Tag { /// - /// A tag that contains a single IEEE-754 double-precision floating point number. + /// Creates a new instance of the class with the specified . /// - [PublicAPI][Serializable] - public class DoubleTag : Tag + /// The name of the tag, or if tag has no name. + /// The value to assign to this tag. + public DoubleTag(string? name, double value) : base(TagType.Double, name, value) { - /// - /// 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 DoubleTag([CanBeNull] string name, double value) : base(TagType.Double, name, value) - { - } + } - /// - /// Required constructor for ISerializable implementation. - /// - /// The to describing this instance. - /// The destination (see ) for this serialization. - protected DoubleTag(SerializationInfo info, StreamingContext context) : base(info, context) - { - } + /// + /// Required constructor for ISerializable implementation. + /// + /// The to describing this instance. + /// The destination (see ) for this serialization. + protected DoubleTag(SerializationInfo info, StreamingContext context) : base(info, context) + { + } - /// - public override string ToString() => $"TAG_Double({PrettyName}): {Value:0.0}"; + /// + public override string ToString() => $"TAG_Double({PrettyName}): {Value:0.0}"; - /// - /// Implicit conversion of this tag to a . - /// - /// The tag to convert. - /// The tag represented as a . - public static implicit operator double(DoubleTag tag) => tag.Value; + /// + /// Implicit conversion of this tag to a . + /// + /// The tag to convert. + /// The tag represented as a . + public static implicit operator double(DoubleTag tag) => tag.Value; - /// - /// Gets the string representation of this NBT tag (SNBT). - /// - /// This NBT tag in SNBT format. - /// - public override string Stringify() => $"{StringifyName}{Value:0.0}D"; - } + /// + /// Gets the string representation of this NBT tag (SNBT). + /// + /// This NBT tag in SNBT format. + /// + public override string Stringify() => $"{StringifyName}{Value:0.0}D"; } \ No newline at end of file diff --git a/SharpNBT/Tags/EndTag.cs b/SharpNBT/Tags/EndTag.cs index cdd7a75..5ab9c82 100644 --- a/SharpNBT/Tags/EndTag.cs +++ b/SharpNBT/Tags/EndTag.cs @@ -1,35 +1,34 @@ using System.Text; using JetBrains.Annotations; -namespace SharpNBT +namespace SharpNBT; + +/// +/// Represents the end of . +/// +[PublicAPI] +public sealed class EndTag : Tag { /// - /// Represents the end of . + /// Creates a new instance of the class. /// - [PublicAPI] - public sealed class EndTag : Tag + public EndTag() : base(TagType.End, null) { - /// - /// Creates a new instance of the class. - /// - public EndTag() : base(TagType.End, null) - { - } + } - /// - public override string ToString() => $"TAG_End"; + /// + public override string ToString() => $"TAG_End"; - /// - protected internal override void PrettyPrinted(StringBuilder buffer, int level, string indent) - { - // Do nothing - } - - /// - /// Gets the string representation of this NBT tag (SNBT). - /// - /// This NBT tag in SNBT format. - /// - public override string Stringify() => string.Empty; + /// + protected internal override void PrettyPrinted(StringBuilder buffer, int level, string indent) + { + // Do nothing } + + /// + /// Gets the string representation of this NBT tag (SNBT). + /// + /// This NBT tag in SNBT format. + /// + public override string Stringify() => string.Empty; } \ No newline at end of file diff --git a/SharpNBT/Tags/EnumerableTag.cs b/SharpNBT/Tags/EnumerableTag.cs index 3ac1640..3c6db0e 100644 --- a/SharpNBT/Tags/EnumerableTag.cs +++ b/SharpNBT/Tags/EnumerableTag.cs @@ -6,198 +6,196 @@ using JetBrains.Annotations; using SuppressMessageAttribute = System.Diagnostics.CodeAnalysis.SuppressMessageAttribute; -namespace SharpNBT +namespace SharpNBT; + +/// +/// Base class for tags that contain a collection of values and can be enumerated. +/// +/// The type of the item the tag contains. +[PublicAPI][Serializable] +public abstract class EnumerableTag : Tag, IList { /// - /// Base class for tags that contain a collection of values and can be enumerated. + /// Internal list implementation. /// - /// The type of the item the tag contains. - [PublicAPI][Serializable] - public abstract class EnumerableTag : Tag, IList + private readonly List internalList = new List(); + + /// + /// Initializes a new instance of the . + /// + /// A constant describing the NBT type for this tag. + /// The name of the tag, or if tag has no name. + protected EnumerableTag(TagType type, string? name) : base(type, name) { - /// - /// Internal list implementation. - /// - private readonly List internalList = new List(); - - /// - /// Initializes a new instance of the . - /// - /// A constant describing the NBT type for this tag. - /// The name of the tag, or if tag has no name. - protected EnumerableTag(TagType type, [CanBeNull] string name) : base(type, name) - { - } + } - /// - /// Initializes a new instance of the with the specified . - /// - /// A constant describing the NBT type for this tag. - /// The name of the tag, or if tag has no name. - /// A collection of values to include in this tag. - protected EnumerableTag(TagType type, [CanBeNull] string name, [NotNull] T[] values) : base(type, name) - { - internalList.AddRange(values); - } + /// + /// Initializes a new instance of the with the specified . + /// + /// A constant describing the NBT type for this tag. + /// The name of the tag, or if tag has no name. + /// A collection of values to include in this tag. + protected EnumerableTag(TagType type, string? name, T[] values) : base(type, name) + { + internalList.AddRange(values); + } - /// - /// Initializes a new instance of the with the specified . - /// - /// A constant describing the NBT type for this tag. - /// The name of the tag, or if tag has no name. - /// A collection of values to include in this tag. - protected EnumerableTag(TagType type, [CanBeNull] string name, [NotNull] IEnumerable values) : base(type, name) - { - internalList.AddRange(values); - } + /// + /// Initializes a new instance of the with the specified . + /// + /// A constant describing the NBT type for this tag. + /// The name of the tag, or if tag has no name. + /// A collection of values to include in this tag. + protected EnumerableTag(TagType type, string? name, IEnumerable values) : base(type, name) + { + internalList.AddRange(values); + } - /// - /// Initializes a new instance of the with the specified . - /// - /// A constant describing the NBT type for this tag. - /// The name of the tag, or if tag has no name. - /// A collection of values to include in this tag. - protected EnumerableTag(TagType type, [CanBeNull] string name, ReadOnlySpan values) : base(type, name) - { - internalList.AddRange(values.ToArray()); - } - - /// - /// Required constructor for ISerializable implementation. - /// - /// The to describing this instance. - /// The destination (see ) for this serialization. - protected EnumerableTag(SerializationInfo info, StreamingContext context) : base(info, context) - { - var dummy = info.GetInt32("count"); - internalList.AddRange((T[]) info.GetValue("values", 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("count", Count); - info.AddValue("values", internalList.ToArray(), typeof(T[])); - } - - /// Returns an enumerator that iterates through the collection. - /// An enumerator that can be used to iterate through the collection. - /// - public IEnumerator GetEnumerator() => internalList.GetEnumerator(); - - /// Returns an enumerator that iterates through a collection. - /// An object that can be used to iterate through the collection. - /// - IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)internalList).GetEnumerator(); - - /// Adds an item to the . - /// The object to add to the . - /// The is read-only. - /// - [SuppressMessage("ReSharper", "AnnotationConflictInHierarchy")] - public virtual void Add([NotNull] T item) => internalList.Add(item); - - /// - /// Adds the elements of the specified collection to the . - /// - /// A collection containing the items to add. - public void AddRange([NotNull] [ItemNotNull] IEnumerable items) - { - foreach (var item in items) - Add(item); - } - - /// Inserts an item to the at the specified index. - /// The zero-based index at which should be inserted. - /// The object to insert into the . - /// - /// is not a valid index in the . - /// The is read-only. - /// - [SuppressMessage("ReSharper", "AnnotationConflictInHierarchy")] - public virtual void Insert(int index, [NotNull] 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. - /// - /// is not a valid index in the . - /// The property is set and the is read-only. - /// The element at the specified index. - /// - [NotNull] - public virtual T this[int index] - { - get => internalList[index]; - set => internalList[index] = value; - } - - /// Removes all items from the . - /// The is read-only. - /// - public virtual void Clear() => internalList.Clear(); - - /// Determines whether the contains a specific value. - /// The object to locate in the . - /// - /// if is found in the ; otherwise, . - /// - public bool Contains(T item) => internalList.Contains(item); - - /// Copies the elements of the to an , starting at a particular index. - /// The one-dimensional that is the destination of the elements copied from . The must have zero-based indexing. - /// The zero-based index in at which copying begins. - /// - /// is . - /// - /// is less than 0. - /// The number of elements in the source is greater than the available space from to the end of the destination . - /// - public void CopyTo(T[] array, int arrayIndex) => internalList.CopyTo(array, arrayIndex); - - /// Removes the first occurrence of a specific object from the . - /// The object to remove from the . - /// The is read-only. - /// - /// if was successfully removed from the ; otherwise, . This method also returns if is not found in the original . - /// - public virtual bool Remove(T item) => internalList.Remove(item); - - /// Gets the number of elements contained in the . - /// The number of elements contained in the . - /// - public int Count => internalList.Count; - - /// Gets a value indicating whether the is read-only. - /// - /// if the is read-only; otherwise, . - /// - public bool IsReadOnly => false; - - /// Determines the index of a specific item in the . - /// The object to locate in the . - /// The index of if found in the list; otherwise, -1. - /// - public int IndexOf(T item) => internalList.IndexOf(item); - - /// Removes the item at the specified index. - /// The zero-based index of the item to remove. - /// - /// is not a valid index in the . - /// The is read-only. - /// - public virtual void RemoveAt(int index) => internalList.RemoveAt(index); - - /// - protected internal override void PrettyPrinted(StringBuilder buffer, int level, string indent) - { - for (var i = 0; i < level; i++) - buffer.Append(indent); - buffer.AppendLine(ToString()); - } + /// + /// Initializes a new instance of the with the specified . + /// + /// A constant describing the NBT type for this tag. + /// The name of the tag, or if tag has no name. + /// A collection of values to include in this tag. + protected EnumerableTag(TagType type, string? name, ReadOnlySpan values) : base(type, name) + { + internalList.AddRange(values.ToArray()); + } + + /// + /// Required constructor for ISerializable implementation. + /// + /// The to describing this instance. + /// The destination (see ) for this serialization. + protected EnumerableTag(SerializationInfo info, StreamingContext context) : base(info, context) + { + var dummy = info.GetInt32("count"); + internalList.AddRange((T[]) info.GetValue("values", 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("count", Count); + info.AddValue("values", internalList.ToArray(), typeof(T[])); + } + + /// Returns an enumerator that iterates through the collection. + /// An enumerator that can be used to iterate through the collection. + /// + public IEnumerator GetEnumerator() => internalList.GetEnumerator(); + + /// Returns an enumerator that iterates through a collection. + /// An object that can be used to iterate through the collection. + /// + IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)internalList).GetEnumerator(); + + /// Adds an item to the . + /// The object to add to the . + /// The is read-only. + /// + [SuppressMessage("ReSharper", "AnnotationConflictInHierarchy")] + public virtual void Add([NotNull] T item) => internalList.Add(item); + + /// + /// Adds the elements of the specified collection to the . + /// + /// A collection containing the items to add. + public void AddRange([ItemNotNull] IEnumerable items) + { + foreach (var item in items) + Add(item); } + /// Inserts an item to the at the specified index. + /// The zero-based index at which should be inserted. + /// The object to insert into the . + /// + /// is not a valid index in the . + /// The is read-only. + /// + [SuppressMessage("ReSharper", "AnnotationConflictInHierarchy")] + public virtual void Insert(int index, [NotNull] 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. + /// + /// is not a valid index in the . + /// The property is set and the is read-only. + /// The element at the specified index. + /// + [NotNull] + public virtual T this[int index] + { + get => internalList[index]; + set => internalList[index] = value; + } + + /// Removes all items from the . + /// The is read-only. + /// + public virtual void Clear() => internalList.Clear(); + + /// Determines whether the contains a specific value. + /// The object to locate in the . + /// + /// if is found in the ; otherwise, . + /// + public bool Contains(T item) => internalList.Contains(item); + + /// Copies the elements of the to an , starting at a particular index. + /// The one-dimensional that is the destination of the elements copied from . The must have zero-based indexing. + /// The zero-based index in at which copying begins. + /// + /// is . + /// + /// is less than 0. + /// The number of elements in the source is greater than the available space from to the end of the destination . + /// + public void CopyTo(T[] array, int arrayIndex) => internalList.CopyTo(array, arrayIndex); + + /// Removes the first occurrence of a specific object from the . + /// The object to remove from the . + /// The is read-only. + /// + /// if was successfully removed from the ; otherwise, . This method also returns if is not found in the original . + /// + public virtual bool Remove(T item) => internalList.Remove(item); + + /// Gets the number of elements contained in the . + /// The number of elements contained in the . + /// + public int Count => internalList.Count; + + /// Gets a value indicating whether the is read-only. + /// + /// if the is read-only; otherwise, . + /// + public bool IsReadOnly => false; + + /// Determines the index of a specific item in the . + /// The object to locate in the . + /// The index of if found in the list; otherwise, -1. + /// + public int IndexOf(T item) => internalList.IndexOf(item); + + /// Removes the item at the specified index. + /// The zero-based index of the item to remove. + /// + /// is not a valid index in the . + /// The is read-only. + /// + public virtual void RemoveAt(int index) => internalList.RemoveAt(index); + + /// + protected internal override void PrettyPrinted(StringBuilder buffer, int level, string indent) + { + for (var i = 0; i < level; i++) + buffer.Append(indent); + buffer.AppendLine(ToString()); + } } \ No newline at end of file diff --git a/SharpNBT/Tags/FloatTag.cs b/SharpNBT/Tags/FloatTag.cs index a7025ea..b7319a7 100644 --- a/SharpNBT/Tags/FloatTag.cs +++ b/SharpNBT/Tags/FloatTag.cs @@ -2,46 +2,45 @@ using System.Runtime.Serialization; using JetBrains.Annotations; -namespace SharpNBT +namespace SharpNBT; + +/// +/// A tag that contains a single IEEE-754 single-precision floating point number. +/// +[PublicAPI][Serializable] +public class FloatTag : Tag { /// - /// A tag that contains a single IEEE-754 single-precision floating point number. + /// 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 FloatTag(string? name, float value) : base(TagType.Float, name, value) + { + } + /// + /// Required constructor for ISerializable implementation. /// - [PublicAPI][Serializable] - public class FloatTag : Tag + /// The to describing this instance. + /// The destination (see ) for this serialization. + protected FloatTag(SerializationInfo info, StreamingContext context) : base(info, context) { - /// - /// 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 FloatTag([CanBeNull] string name, float value) : base(TagType.Float, name, value) - { - } - /// - /// Required constructor for ISerializable implementation. - /// - /// The to describing this instance. - /// The destination (see ) for this serialization. - protected FloatTag(SerializationInfo info, StreamingContext context) : base(info, context) - { - } + } - /// - public override string ToString() => $"TAG_Float({PrettyName}): {Value:0.0}"; + /// + public override string ToString() => $"TAG_Float({PrettyName}): {Value:0.0}"; - /// - /// Implicit conversion of this tag to a . - /// - /// The tag to convert. - /// The tag represented as a . - public static implicit operator float(FloatTag tag) => tag.Value; + /// + /// Implicit conversion of this tag to a . + /// + /// The tag to convert. + /// The tag represented as a . + public static implicit operator float(FloatTag tag) => tag.Value; - /// - /// Gets the string representation of this NBT tag (SNBT). - /// - /// This NBT tag in SNBT format. - /// - public override string Stringify() => $"{StringifyName}{Value:0.0}F"; - } + /// + /// Gets the string representation of this NBT tag (SNBT). + /// + /// This NBT tag in SNBT format. + /// + 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 1709d33..33a1382 100644 --- a/SharpNBT/Tags/IntArrayTag.cs +++ b/SharpNBT/Tags/IntArrayTag.cs @@ -3,70 +3,69 @@ using System.Runtime.Serialization; using JetBrains.Annotations; -namespace SharpNBT +namespace SharpNBT; + +/// +/// A tag that whose value is a contiguous sequence of 32-bit integers. +/// +[PublicAPI][Serializable] +public class IntArrayTag : EnumerableTag { /// - /// A tag that whose value is a contiguous sequence of 32-bit integers. + /// 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) + { + } + /// + /// Initializes a new instance of the with the specified . /// - [PublicAPI][Serializable] - 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, int[] values) : base(TagType.IntArray, name, values) { - /// - /// Initializes a new instance of the . - /// - /// The name of the tag, or if tag has no name. - public IntArrayTag([CanBeNull] string name) : base(TagType.IntArray, name) - { - } - /// - /// Initializes a new instance of the with the specified . - /// - /// The name of the tag, or if tag has no name. - /// A collection of values to include in this tag. - public IntArrayTag([CanBeNull] string name, [NotNull] int[] values) : base(TagType.IntArray, name, values) - { - } + } - /// - /// Initializes a new instance of the with the specified . - /// - /// The name of the tag, or if tag has no name. - /// A collection of values to include in this tag. - public IntArrayTag([CanBeNull] string name, [NotNull] IEnumerable values) : base(TagType.IntArray, name, values) - { - } + /// + /// Initializes a new instance of the with the specified . + /// + /// 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) + { + } - /// - /// Initializes a new instance of the . - /// - /// The name of the tag, or if tag has no name. - /// A collection of values to include in this tag. - public IntArrayTag([CanBeNull] string name, ReadOnlySpan values) : base(TagType.IntArray, name, values) - { - } - - /// - /// Required constructor for ISerializable implementation. - /// - /// The to describing this instance. - /// The destination (see ) for this serialization. - protected IntArrayTag(SerializationInfo info, StreamingContext context) : base(info, context) - { - } + /// + /// Initializes a new instance of the . + /// + /// 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 override string ToString() - { - var word = Count == 1 ? Strings.WordElement : Strings.WordElements; - return $"TAG_Int_Array({PrettyName}): [{Count} {word}]"; - } + /// + /// Required constructor for ISerializable implementation. + /// + /// The to describing this instance. + /// The destination (see ) for this serialization. + protected IntArrayTag(SerializationInfo info, StreamingContext context) : base(info, context) + { + } - /// - /// Gets the string representation of this NBT tag (SNBT). - /// - /// This NBT tag in SNBT format. - /// - public override string Stringify() => $"{StringifyName}[I;{string.Join(',', this)}]"; + /// + public override string ToString() + { + var word = Count == 1 ? Strings.WordElement : Strings.WordElements; + return $"TAG_Int_Array({PrettyName}): [{Count} {word}]"; } + + /// + /// Gets the string representation of this NBT tag (SNBT). + /// + /// This NBT tag in SNBT format. + /// + public override string Stringify() => $"{StringifyName}[I;{string.Join(',', this)}]"; } \ No newline at end of file diff --git a/SharpNBT/Tags/IntTag.cs b/SharpNBT/Tags/IntTag.cs index 0538c97..aa49a3c 100644 --- a/SharpNBT/Tags/IntTag.cs +++ b/SharpNBT/Tags/IntTag.cs @@ -2,74 +2,73 @@ using System.Runtime.Serialization; using JetBrains.Annotations; -namespace SharpNBT +namespace SharpNBT; + +/// +/// A tag that contains a single 32-bit integer value. +/// +[PublicAPI][Serializable] +public class IntTag : Tag { /// - /// A tag that contains a single 32-bit integer value. + /// Gets or sets the value of this tag as an unsigned value. /// - [PublicAPI][Serializable] - public class IntTag : Tag + /// + /// This is only a reinterpretation of the bytes, no actual conversion is performed. + /// + [CLSCompliant(false)] + public uint UnsignedValue { - /// - /// Gets or sets the value of this tag as an unsigned value. - /// - /// - /// This is only a reinterpretation of the bytes, no actual conversion is performed. - /// - [CLSCompliant(false)] - public uint UnsignedValue - { - get => unchecked((uint)Value); - set => Value = unchecked((int)value); - } + get => unchecked((uint)Value); + set => Value = unchecked((int)value); + } - /// - /// 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 IntTag([CanBeNull] string name, int value) : base(TagType.Int, name, value) - { - } + /// + /// 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 IntTag(string? name, int value) : base(TagType.Int, name, value) + { + } - /// - [CLSCompliant(false)] - public IntTag([CanBeNull] Tag parent, [CanBeNull] string name, uint value) : base(TagType.Int, name, unchecked((int) value)) - { - } + /// + [CLSCompliant(false)] + public IntTag(Tag? parent, string? name, uint value) : base(TagType.Int, name, unchecked((int) value)) + { + } - /// - /// Required constructor for ISerializable implementation. - /// - /// The to describing this instance. - /// The destination (see ) for this serialization. - protected IntTag(SerializationInfo info, StreamingContext context) : base(info, context) - { - } + /// + /// Required constructor for ISerializable implementation. + /// + /// The to describing this instance. + /// The destination (see ) for this serialization. + protected IntTag(SerializationInfo info, StreamingContext context) : base(info, context) + { + } - /// - public override string ToString() => $"TAG_Int({PrettyName}): {Value}"; + /// + public override string ToString() => $"TAG_Int({PrettyName}): {Value}"; - /// - /// Implicit conversion of this tag to a . - /// - /// The tag to convert. - /// The tag represented as a . - public static implicit operator int(IntTag tag) => tag.Value; + /// + /// Implicit conversion of this tag to a . + /// + /// The tag to convert. + /// The tag represented as a . + public static implicit operator int(IntTag tag) => tag.Value; - /// - /// Implicit conversion of this tag to a . - /// - /// The tag to convert. - /// The tag represented as a . - [CLSCompliant(false)] - public static implicit operator uint(IntTag tag) => unchecked((uint)tag.Value); + /// + /// Implicit conversion of this tag to a . + /// + /// The tag to convert. + /// The tag represented as a . + [CLSCompliant(false)] + public static implicit operator uint(IntTag tag) => unchecked((uint)tag.Value); - /// - /// Gets the string representation of this NBT tag (SNBT). - /// - /// This NBT tag in SNBT format. - /// - public override string Stringify() => $"{StringifyName}{Value}"; - } + /// + /// Gets the string representation of this NBT tag (SNBT). + /// + /// This NBT tag in SNBT format. + /// + 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 5a51db6..faa9205 100644 --- a/SharpNBT/Tags/ListTag.cs +++ b/SharpNBT/Tags/ListTag.cs @@ -4,100 +4,98 @@ using System.Text; using JetBrains.Annotations; -namespace SharpNBT +namespace SharpNBT; + +/// +/// Represents a collection of a tags. +/// +/// +/// All child tags must be have the same value, and their value will be omitted during serialization. +/// +[PublicAPI][Serializable] +public class ListTag : TagContainer { /// - /// Represents a collection of a tags. + /// Gets the NBT type of this tag's children. /// - /// - /// All child tags must be have the same value, and their value will be omitted during serialization. - /// - [PublicAPI][Serializable] - public class ListTag : TagContainer - { - /// - /// Gets the NBT type of this tag's children. - /// - public TagType ChildType { get; private set; } + public TagType ChildType { get; private set; } - /// - /// Creates a new instance of the class. - /// - /// The name of the tag, or if tag has no name. - /// A constant describing the NBT type for children in this tag. - public ListTag([CanBeNull] string name, TagType childType) : base(TagType.List, name) - { - RequiredType = childType; - NamedChildren = false; - ChildType = childType; - } + /// + /// Creates a new instance of the class. + /// + /// The name of the tag, or if tag has no name. + /// A constant describing the NBT type for children in this tag. + public ListTag(string? name, TagType childType) : base(TagType.List, name) + { + RequiredType = childType; + NamedChildren = false; + ChildType = childType; + } - /// - /// Creates a new instance of the with the specified . - /// - /// The name of the tag, or if tag has no name. - /// A constant describing the NBT type for children in this tag. - /// A collection of values to include in this tag. - public ListTag([CanBeNull] string name, TagType childType, [NotNull][ItemNotNull] IEnumerable children) : this(name, childType) - { - AddRange(children); - } + /// + /// Creates a new instance of the with the specified . + /// + /// The name of the tag, or if tag has no name. + /// A constant describing the NBT type for children in this tag. + /// A collection of values to include in this tag. + public ListTag(string? name, TagType childType, IEnumerable children) : this(name, childType) + { + AddRange(children); + } - /// - /// Required constructor for ISerializable implementation. - /// - /// The to describing this instance. - /// The destination (see ) for this serialization. - protected ListTag(SerializationInfo info, StreamingContext context) : base(info, context) - { - } + /// + /// Required constructor for ISerializable implementation. + /// + /// The to describing this instance. + /// The destination (see ) for this serialization. + protected ListTag(SerializationInfo info, StreamingContext context) : base(info, context) + { + } - /// - public override string ToString() - { - var word = Count == 1 ? Strings.WordEntry : Strings.WordEntries; - return $"TAG_List({PrettyName}): [{Count} {word}]"; - } + /// + public override string ToString() + { + var word = Count == 1 ? Strings.WordEntry : Strings.WordEntries; + return $"TAG_List({PrettyName}): [{Count} {word}]"; + } - /// - protected internal override void PrettyPrinted(StringBuilder buffer, int level, string indent) - { - var space = new StringBuilder(); - for (var i = 0; i < level; i++) - space.Append(indent); + /// + protected internal override void PrettyPrinted(StringBuilder buffer, int level, string indent) + { + var space = new StringBuilder(); + for (var i = 0; i < level; i++) + space.Append(indent); - buffer.AppendLine(space + ToString()); - buffer.AppendLine(space + "{"); - foreach (var tag in this) - tag.PrettyPrinted(buffer, level + 1, indent); - buffer.AppendLine(space + "}"); - } + buffer.AppendLine(space + ToString()); + buffer.AppendLine(space + "{"); + foreach (var tag in this) + tag.PrettyPrinted(buffer, level + 1, indent); + buffer.AppendLine(space + "}"); + } - /// - /// Retrieves a "pretty-printed" multiline string representing the complete tree structure of the tag. - /// - /// The prefix that will be applied to each indent-level of nested nodes in the tree structure. - /// The pretty-printed string. - [NotNull] - public string PrettyPrinted([NotNull] string indent = " ") - { - var buffer = new StringBuilder(); - PrettyPrinted(buffer, 0, indent); - return buffer.ToString(); - } + /// + /// Retrieves a "pretty-printed" multiline string representing the complete tree structure of the tag. + /// + /// The prefix that will be applied to each indent-level of nested nodes in the tree structure. + /// The pretty-printed string. + public string PrettyPrinted(string indent = " ") + { + var buffer = new StringBuilder(); + PrettyPrinted(buffer, 0, indent); + return buffer.ToString(); + } - /// - /// Gets the string representation of this NBT tag (SNBT). - /// - /// This NBT tag in SNBT format. - /// - public override string Stringify() - { - var strings = new string[Count]; - for (var i = 0; i < strings.Length; i++) - strings[i] = this[i].Stringify(); + /// + /// Gets the string representation of this NBT tag (SNBT). + /// + /// This NBT tag in SNBT format. + /// + public override string Stringify() + { + var strings = new string[Count]; + for (var i = 0; i < strings.Length; i++) + strings[i] = this[i].Stringify(); - return $"{StringifyName}[{string.Join(',', strings)}]"; - } + return $"{StringifyName}[{string.Join(',', strings)}]"; } } \ No newline at end of file diff --git a/SharpNBT/Tags/LongArrayTag.cs b/SharpNBT/Tags/LongArrayTag.cs index a6d0f09..4c249ec 100644 --- a/SharpNBT/Tags/LongArrayTag.cs +++ b/SharpNBT/Tags/LongArrayTag.cs @@ -3,75 +3,74 @@ using System.Runtime.Serialization; using JetBrains.Annotations; -namespace SharpNBT +namespace SharpNBT; + +/// +/// A tag that whose value is a contiguous sequence of 64-bit integers. +/// +[PublicAPI][Serializable] +public class LongArrayTag : EnumerableTag { /// - /// A tag that whose value is a contiguous sequence of 64-bit integers. + /// 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) + { + } + /// + /// Initializes a new instance of the with the specified . /// - [PublicAPI][Serializable] - 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) { - /// - /// Initializes a new instance of the . - /// - /// The name of the tag, or if tag has no name. - public LongArrayTag([CanBeNull] string name) : base(TagType.LongArray, name) - { - } - /// - /// Initializes a new instance of the with the specified . - /// - /// The name of the tag, or if tag has no name. - /// A collection of values to include in this tag. - public LongArrayTag([CanBeNull] string name, [NotNull] long[] values) : base(TagType.LongArray, name, values) - { - } + } - /// - /// Required constructor for ISerializable implementation. - /// - /// The to describing this instance. - /// The destination (see ) for this serialization. - protected LongArrayTag(SerializationInfo info, StreamingContext context) : base(info, context) - { - } + /// + /// Required constructor for ISerializable implementation. + /// + /// The to describing this instance. + /// The destination (see ) for this serialization. + protected LongArrayTag(SerializationInfo info, StreamingContext context) : base(info, context) + { + } - /// - /// Initializes a new instance of the with the specified . - /// - /// The name of the tag, or if tag has no name. - /// A collection of values to include in this tag. - public LongArrayTag([CanBeNull] string name, [NotNull] IEnumerable values) : base(TagType.LongArray, name, values) - { - } + /// + /// Initializes a new instance of the with the specified . + /// + /// 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) + { + } - /// - /// Initializes a new instance of the . - /// - /// The name of the tag, or if tag has no name. - /// A collection of values to include in this tag. - public LongArrayTag([CanBeNull] string name, ReadOnlySpan values) : base(TagType.LongArray, name, values) - { - } + /// + /// Initializes a new instance of the . + /// + /// 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 override string ToString() - { - var word = Count == 1 ? Strings.WordElement : Strings.WordElements; - return $"TAG_Long_Array({PrettyName}): [{Count} {word}]"; - } + /// + public override string ToString() + { + var word = Count == 1 ? Strings.WordElement : Strings.WordElements; + return $"TAG_Long_Array({PrettyName}): [{Count} {word}]"; + } - /// - /// Gets the string representation of this NBT tag (SNBT). - /// - /// 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)}]"; - } + /// + /// Gets the string representation of this NBT tag (SNBT). + /// + /// 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)}]"; } } \ No newline at end of file diff --git a/SharpNBT/Tags/LongTag.cs b/SharpNBT/Tags/LongTag.cs index 0e34115..978062e 100644 --- a/SharpNBT/Tags/LongTag.cs +++ b/SharpNBT/Tags/LongTag.cs @@ -2,74 +2,73 @@ using System.Runtime.Serialization; using JetBrains.Annotations; -namespace SharpNBT +namespace SharpNBT; + +/// +/// A tag that contains a single 64-bit integer value. +/// +[PublicAPI][Serializable] +public class LongTag : Tag { /// - /// A tag that contains a single 64-bit integer value. + /// Gets or sets the value of this tag as an unsigned value. /// - [PublicAPI][Serializable] - public class LongTag : Tag + /// + /// This is only a reinterpretation of the bytes, no actual conversion is performed. + /// + [CLSCompliant(false)] + public ulong UnsignedValue { - /// - /// Gets or sets the value of this tag as an unsigned value. - /// - /// - /// This is only a reinterpretation of the bytes, no actual conversion is performed. - /// - [CLSCompliant(false)] - public ulong UnsignedValue - { - get => unchecked((ulong)Value); - set => Value = unchecked((long)value); - } + get => unchecked((ulong)Value); + set => Value = unchecked((long)value); + } - /// - /// 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 LongTag([CanBeNull] string name, long value) : base(TagType.Long, name, value) - { - } + /// + /// 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 LongTag(string? name, long value) : base(TagType.Long, name, value) + { + } - /// - [CLSCompliant(false)] - public LongTag([CanBeNull] string name, ulong value) : base(TagType.Long, name, unchecked((long) value)) - { - } + /// + [CLSCompliant(false)] + public LongTag(string? name, ulong value) : base(TagType.Long, name, unchecked((long) value)) + { + } - /// - /// Required constructor for ISerializable implementation. - /// - /// The to describing this instance. - /// The destination (see ) for this serialization. - protected LongTag(SerializationInfo info, StreamingContext context) : base(info, context) - { - } + /// + /// Required constructor for ISerializable implementation. + /// + /// The to describing this instance. + /// The destination (see ) for this serialization. + protected LongTag(SerializationInfo info, StreamingContext context) : base(info, context) + { + } - /// - public override string ToString() => $"TAG_Long({PrettyName}): {Value}"; + /// + public override string ToString() => $"TAG_Long({PrettyName}): {Value}"; - /// - /// Implicit conversion of this tag to a . - /// - /// The tag to convert. - /// The tag represented as a . - public static implicit operator long(LongTag tag) => tag.Value; + /// + /// Implicit conversion of this tag to a . + /// + /// The tag to convert. + /// The tag represented as a . + public static implicit operator long(LongTag tag) => tag.Value; - /// - /// Implicit conversion of this tag to a . - /// - /// The tag to convert. - /// The tag represented as a . - [CLSCompliant(false)] - public static implicit operator ulong(LongTag tag) => unchecked((ulong)tag.Value); + /// + /// Implicit conversion of this tag to a . + /// + /// The tag to convert. + /// The tag represented as a . + [CLSCompliant(false)] + public static implicit operator ulong(LongTag tag) => unchecked((ulong)tag.Value); - /// - /// Gets the string representation of this NBT tag (SNBT). - /// - /// This NBT tag in SNBT format. - /// - public override string Stringify() => $"{StringifyName}{Value}L"; - } + /// + /// Gets the string representation of this NBT tag (SNBT). + /// + /// This NBT tag in SNBT format. + /// + public override string Stringify() => $"{StringifyName}{Value}L"; } \ No newline at end of file diff --git a/SharpNBT/Tags/ShortTag.cs b/SharpNBT/Tags/ShortTag.cs index 2e404e7..1ad6dc6 100644 --- a/SharpNBT/Tags/ShortTag.cs +++ b/SharpNBT/Tags/ShortTag.cs @@ -2,74 +2,73 @@ using System.Runtime.Serialization; using JetBrains.Annotations; -namespace SharpNBT +namespace SharpNBT; + +/// +/// A tag that contains a single 16-bit integer value. +/// +[PublicAPI][Serializable] +public class ShortTag : Tag { /// - /// A tag that contains a single 16-bit integer value. + /// Gets or sets the value of this tag as an unsigned value. /// - [PublicAPI][Serializable] - public class ShortTag : Tag + /// + /// This is only a reinterpretation of the bytes, no actual conversion is performed. + /// + [CLSCompliant(false)] + public ushort UnsignedValue { - /// - /// Gets or sets the value of this tag as an unsigned value. - /// - /// - /// This is only a reinterpretation of the bytes, no actual conversion is performed. - /// - [CLSCompliant(false)] - public ushort UnsignedValue - { - get => unchecked((ushort)Value); - set => Value = unchecked((short)value); - } + get => unchecked((ushort)Value); + set => Value = unchecked((short)value); + } - /// - /// 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 ShortTag([CanBeNull] string name, short value) : base(TagType.Short, name, value) - { - } + /// + /// 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 ShortTag(string? name, short value) : base(TagType.Short, name, value) + { + } - /// - [CLSCompliant(false)] - public ShortTag([CanBeNull] string name, ushort value) : base(TagType.Short, name, unchecked((short) value)) - { - } + /// + [CLSCompliant(false)] + public ShortTag(string? name, ushort value) : base(TagType.Short, name, unchecked((short) value)) + { + } - /// - /// Required constructor for ISerializable implementation. - /// - /// The to describing this instance. - /// The destination (see ) for this serialization. - protected ShortTag(SerializationInfo info, StreamingContext context) : base(info, context) - { - } + /// + /// Required constructor for ISerializable implementation. + /// + /// The to describing this instance. + /// The destination (see ) for this serialization. + protected ShortTag(SerializationInfo info, StreamingContext context) : base(info, context) + { + } - /// - public override string ToString() => $"TAG_Short({PrettyName}): {Value}"; + /// + public override string ToString() => $"TAG_Short({PrettyName}): {Value}"; - /// - /// Implicit conversion of this tag to a . - /// - /// The tag to convert. - /// The tag represented as a . - public static implicit operator short(ShortTag tag) => tag.Value; + /// + /// Implicit conversion of this tag to a . + /// + /// The tag to convert. + /// The tag represented as a . + public static implicit operator short(ShortTag tag) => tag.Value; - /// - /// Implicit conversion of this tag to a . - /// - /// The tag to convert. - /// The tag represented as a . - [CLSCompliant(false)] - public static implicit operator ushort(ShortTag tag) => unchecked((ushort)tag.Value); + /// + /// Implicit conversion of this tag to a . + /// + /// The tag to convert. + /// The tag represented as a . + [CLSCompliant(false)] + public static implicit operator ushort(ShortTag tag) => unchecked((ushort)tag.Value); - /// - /// Gets the string representation of this NBT tag (SNBT). - /// - /// This NBT tag in SNBT format. - /// - public override string Stringify() => $"{StringifyName}{Value}S"; - } + /// + /// Gets the string representation of this NBT tag (SNBT). + /// + /// This NBT tag in SNBT format. + /// + 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 4c53992..8c93a07 100644 --- a/SharpNBT/Tags/StringTag.cs +++ b/SharpNBT/Tags/StringTag.cs @@ -2,47 +2,46 @@ using System.Runtime.Serialization; using JetBrains.Annotations; -namespace SharpNBT +namespace SharpNBT; + +/// +/// A tag the contains a UTF-8 string. +/// +[PublicAPI][Serializable] +public class StringTag : Tag { /// - /// A tag the contains a UTF-8 string. + /// Creates a new instance of the class with the specified . /// - [PublicAPI][Serializable] - public class StringTag : Tag + /// 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) { - /// - /// 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([CanBeNull] string name, [CanBeNull] string value) : base(TagType.String, name, value) - { - } + } - /// - /// 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) - { - } + /// + /// 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) + { + } - /// - public override string ToString() => $"TAG_String({PrettyName}): \"{Value}\""; + /// + public override string ToString() => $"TAG_String({PrettyName}): \"{Value}\""; - /// - /// Implicit conversion of this tag to a . - /// - /// The tag to convert. - /// The tag represented as a . - public static implicit operator string(StringTag tag) => tag.Value; + /// + /// Implicit conversion of this tag to a . + /// + /// The tag to convert. + /// The tag represented as a . + public static implicit operator string(StringTag tag) => tag.Value; - /// - /// Gets the string representation of this NBT tag (SNBT). - /// - /// This NBT tag in SNBT format. - /// - public override string Stringify() => $"{StringifyName}\"{Value}\""; - } + /// + /// Gets the string representation of this NBT tag (SNBT). + /// + /// This NBT tag in SNBT format. + /// + public override string Stringify() => $"{StringifyName}\"{Value}\""; } \ No newline at end of file diff --git a/SharpNBT/Tags/Tag.cs b/SharpNBT/Tags/Tag.cs index cb838a8..2773778 100644 --- a/SharpNBT/Tags/Tag.cs +++ b/SharpNBT/Tags/Tag.cs @@ -11,340 +11,336 @@ [assembly: CLSCompliant(true)] [assembly: InternalsVisibleTo("SharpNBT.Tests")] -namespace SharpNBT +namespace SharpNBT; + +/// +/// Abstract base class that all NBT tags inherit from. +/// +[PublicAPI][Serializable] +public abstract class Tag : IEquatable, ISerializable, ICloneable { - /// - /// Abstract base class that all NBT tags inherit from. - /// - [PublicAPI][Serializable] - public abstract class Tag : IEquatable, ISerializable, ICloneable - { - private static Regex simpleNameMatcher; + private static Regex simpleNameMatcher; - static Tag() - { - simpleNameMatcher = new Regex(@"^[A-Ba-z0-9_-]+$", RegexOptions.Compiled); - } + static Tag() + { + simpleNameMatcher = new Regex(@"^[A-Ba-z0-9_-]+$", RegexOptions.Compiled); + } - private static IEnumerable GetKnownTypes() + private static IEnumerable GetKnownTypes() + { + return new[] { - return new[] - { - typeof(TagType), - typeof(Tag<>), - typeof(Tag[]), - typeof(EnumerableTag<>), - typeof(TagContainer), - typeof(ByteTag), - typeof(ShortTag), - typeof(IntTag), - typeof(LongTag), - typeof(FloatTag), - typeof(DoubleTag), - typeof(StringTag), - typeof(ByteArrayTag), - typeof(IntArrayTag), - typeof(LongArrayTag), - typeof(ListTag), - typeof(CompoundTag) - }; - } + typeof(TagType), + typeof(Tag<>), + typeof(Tag[]), + typeof(EnumerableTag<>), + typeof(TagContainer), + typeof(ByteTag), + typeof(ShortTag), + typeof(IntTag), + typeof(LongTag), + typeof(FloatTag), + typeof(DoubleTag), + typeof(StringTag), + typeof(ByteArrayTag), + typeof(IntArrayTag), + typeof(LongArrayTag), + typeof(ListTag), + typeof(CompoundTag) + }; + } - /// - /// Text applied in a pretty-print sting when a tag has no defined value. - /// - protected const string NO_NAME = "None"; + /// + /// Text applied in a pretty-print sting when a tag has no defined value. + /// + protected const string NoName = "None"; - /// - /// Gets a constant describing the NBT type this object represents. - /// - public TagType Type { get; private set; } + /// + /// Gets a constant describing the NBT type this object represents. + /// + public TagType Type { get; private set; } - /// - /// Gets the parent this object is a child of. - /// - [CanBeNull] - public Tag Parent { get; internal set; } + /// + /// Gets the parent this object is a child of. + /// + public Tag? Parent { get; internal set; } - /// - /// Gets the name assigned to this . - /// - [CanBeNull] - public string Name { get; set; } + /// + /// Gets the name assigned to this . + /// + public string? Name { get; set; } - /// - /// Initialized a new instance of the class. - /// - /// A constant describing the NBT type for this tag. - /// The name of the tag, or if tag has no name. - protected Tag(TagType type, [CanBeNull] string name) - { - Type = type; - Name = name; - } + /// + /// Initialized a new instance of the class. + /// + /// A constant describing the NBT type for this tag. + /// The name of the tag, or if tag has no name. + protected Tag(TagType type, string? name) + { + 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([NotNull] StringBuilder buffer, int level, [NotNull] string indent); + /// + /// 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); - /// - /// Gets the name of the object as a human-readable quoted string, or a default name to indicate it has no name when applicable. - /// - protected internal string PrettyName => Name is null ? "None" : $"\"{Name}\""; + /// + /// Gets the name of the object as a human-readable quoted string, or a default name to indicate it has no name when applicable. + /// + protected internal string PrettyName => Name is null ? "None" : $"\"{Name}\""; - /// - /// Gets a representation of this as a JSON string. - /// - /// Flag indicating if formatting should be applied to make the string human-readable. - /// When is , indicates the indent characters(s) to use. - /// A JSON string describing this object. - public string ToJsonString(bool pretty = false, string indent = " ") + /// + /// Gets a representation of this as a JSON string. + /// + /// Flag indicating if formatting should be applied to make the string human-readable. + /// When is , indicates the indent characters(s) to use. + /// A JSON string describing this object. + public string ToJsonString(bool pretty = false, string indent = " ") + { + var settings = new DataContractJsonSerializerSettings { - var settings = new DataContractJsonSerializerSettings - { - UseSimpleDictionaryFormat = true, - EmitTypeInformation = EmitTypeInformation.Never, - KnownTypes = GetKnownTypes() - }; - var serializer = new DataContractJsonSerializer(typeof(Tag), settings); - using var stream = new MemoryStream(); - if (pretty) - { - using var writer = JsonReaderWriterFactory.CreateJsonWriter(stream, Encoding.UTF8, false, true, indent); - serializer.WriteObject(writer, this); - writer.Flush(); - } - else - { - serializer.WriteObject(stream, this); - } - stream.Flush(); - return Encoding.UTF8.GetString(stream.ToArray()); - } - - /// 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) + UseSimpleDictionaryFormat = true, + EmitTypeInformation = EmitTypeInformation.Never, + KnownTypes = GetKnownTypes() + }; + var serializer = new DataContractJsonSerializer(typeof(Tag), settings); + using var stream = new MemoryStream(); + if (pretty) { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; - return Type == other.Type && Name == other.Name; + using var writer = JsonReaderWriterFactory.CreateJsonWriter(stream, Encoding.UTF8, false, true, indent); + serializer.WriteObject(writer, this); + writer.Flush(); } - - /// 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) + else { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != this.GetType()) return false; - return Equals((Tag)obj); + serializer.WriteObject(stream, this); } + stream.Flush(); + return Encoding.UTF8.GetString(stream.ToArray()); + } + + /// 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 Type == other.Type && Name == other.Name; + } - /// Serves as the default hash function. - /// A hash code for the current object. - /// - public override int GetHashCode() + /// 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() != this.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 { - unchecked - { - // ReSharper disable NonReadonlyMemberInGetHashCode - return ((int)Type * 373) ^ (Name != null ? Name.GetHashCode() : 0); - // ReSharper restore NonReadonlyMemberInGetHashCode - } + // ReSharper disable NonReadonlyMemberInGetHashCode + return ((int)Type * 373) ^ (Name != null ? Name.GetHashCode() : 0); + // ReSharper restore NonReadonlyMemberInGetHashCode } + } - /// Creates a new object that is a copy of the current instance. - /// A new object that is a copy of this instance. - public object Clone() - { - // Serialize then deserialize to make a deep-copy - using var stream = new MemoryStream(); + /// Creates a new object that is a copy of the current instance. + /// A new object that is a copy of this instance. + public object Clone() + { + // Serialize then deserialize to make a deep-copy + using var stream = new MemoryStream(); - // Might as well not worry about swapping bits, just use native endian - var opts = BitConverter.IsLittleEndian ? FormatOptions.LittleEndian : FormatOptions.BigEndian; - using var writer = new TagWriter(stream, opts, true); - using var reader = new TagReader(stream, opts, true); + // Might as well not worry about swapping bits, just use native endian + var opts = BitConverter.IsLittleEndian ? FormatOptions.LittleEndian : FormatOptions.BigEndian; + using var writer = new TagWriter(stream, opts, true); + using var reader = new TagReader(stream, opts, true); - writer.WriteTag(this); - stream.Seek(0, SeekOrigin.Begin); + writer.WriteTag(this); + stream.Seek(0, SeekOrigin.Begin); - return reader.ReadTag(!(Parent is ListTag)); - } + return reader.ReadTag(!(Parent is ListTag)); + } - /// - /// Required constructor for ISerializable implementation. - /// - /// The to describing this instance. - /// The destination (see ) for this serialization. - protected Tag(SerializationInfo info, StreamingContext context) - { - Type = (TagType) info.GetByte("type"); - Name = info.GetString("name"); - } + /// + /// Required constructor for ISerializable implementation. + /// + /// The to describing this instance. + /// The destination (see ) for this serialization. + protected Tag(SerializationInfo info, StreamingContext context) + { + Type = (TagType) info.GetByte("type"); + Name = info.GetString("name"); + } - /// 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 virtual void GetObjectData(SerializationInfo info, StreamingContext context) - { - info.AddValue("type", (byte) Type); - info.AddValue("name", Name); - } + /// 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 virtual void GetObjectData(SerializationInfo info, StreamingContext context) + { + info.AddValue("type", (byte) Type); + info.AddValue("name", Name); + } - /// - /// 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 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); + /// + /// 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); - /// - /// Gets the string representation of this NBT tag (SNBT). - /// - /// This NBT tag in SNBT format. - /// - public abstract string Stringify(); + /// + /// Gets the string representation of this NBT tag (SNBT). + /// + /// This NBT tag in SNBT format. + /// + public abstract string Stringify(); - /// - /// Gets the name in a formatted properly for SNBT. - /// - [NotNull] - protected internal string StringifyName + /// + /// Gets the name in a formatted properly for SNBT. + /// + protected internal string StringifyName + { + get { - get - { - if (string.IsNullOrEmpty(Name)) - return string.Empty; - return simpleNameMatcher.IsMatch(Name) ? $"{Name}: " : $"\"{Name}\": "; - } + if (string.IsNullOrEmpty(Name)) + return string.Empty; + 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> +{ /// - /// Abstract base class for types that contain a single primitive value. + /// Gets or sets the value of the tag. /// - /// 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; } + 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)); - } + /// + /// 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)); - } + /// 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, [CanBeNull] string name, T value) : base(type, name) - { - Value = value; - } + /// + /// 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()); - } + /// + 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); - } + /// 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); - } + /// 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() + /// Serves as the default hash function. + /// A hash code for the current object. + /// + public override int GetHashCode() + { + unchecked { - unchecked - { - // ReSharper disable NonReadonlyMemberInGetHashCode - return (base.GetHashCode() * 421) ^ EqualityComparer.Default.GetHashCode(Value); - // ReSharper restore NonReadonlyMemberInGetHashCode - } + // 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 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); - } + /// + /// 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/TagContainer.cs b/SharpNBT/Tags/TagContainer.cs index 8b2bfd0..5bce5fc 100644 --- a/SharpNBT/Tags/TagContainer.cs +++ b/SharpNBT/Tags/TagContainer.cs @@ -5,170 +5,169 @@ using JetBrains.Annotations; using SuppressMessageAttribute = System.Diagnostics.CodeAnalysis.SuppressMessageAttribute; -namespace SharpNBT +namespace SharpNBT; + +/// +/// Base class for tags that contain a collection of other objects and can be enumerated. +/// +[PublicAPI][Serializable] +public abstract class TagContainer : EnumerableTag { /// - /// Base class for tags that contain a collection of other objects and can be enumerated. + /// A value indicating if children of this container are required to have be named. /// - [PublicAPI][Serializable] - public abstract class TagContainer : EnumerableTag - { - /// - /// A value indicating if children of this container are required to have be named. - /// - protected bool NamedChildren; + protected bool NamedChildren; - /// - /// When not , indicates that a child must be of a specific type to be added. - /// - protected TagType? RequiredType; + /// + /// When not , indicates that a child must be of a specific type to be added. + /// + protected TagType? RequiredType; - /// - /// Initializes a new instance of the . - /// - /// A constant describing the NBT type for this tag. - /// The name of the tag, or if tag has no name. - protected TagContainer(TagType type, [CanBeNull] string name) : base(type, name) - { - } + /// + /// Initializes a new instance of the . + /// + /// A constant describing the NBT type for this tag. + /// The name of the tag, or if tag has no name. + protected TagContainer(TagType type, string? name) : base(type, name) + { + } - /// - /// Initializes a new instance of the with the specified . - /// - /// A constant describing the NBT type for this tag. - /// The name of the tag, or if tag has no name. - /// A collection of values to include in this tag. - protected TagContainer(TagType type, [CanBeNull] string name, [NotNull][ItemNotNull] IEnumerable values) : base(type, name, values) - { - } + /// + /// Initializes a new instance of the with the specified . + /// + /// A constant describing the NBT type for this tag. + /// The name of the tag, or if tag has no name. + /// A collection of values to include in this tag. + protected TagContainer(TagType type, string? name, IEnumerable values) : base(type, name, values) + { + } - /// - /// Required constructor for ISerializable implementation. - /// - /// The to describing this instance. - /// The destination (see ) for this serialization. - protected TagContainer(SerializationInfo info, StreamingContext context) : base(info, context) - { - RequiredType = (TagType?) info.GetValue("child_type", typeof(TagType?)); - NamedChildren = !RequiredType.HasValue; - } + /// + /// Required constructor for ISerializable implementation. + /// + /// The to describing this instance. + /// The destination (see ) for this serialization. + protected TagContainer(SerializationInfo info, StreamingContext context) : base(info, context) + { + RequiredType = (TagType?) info.GetValue("child_type", typeof(TagType?)); + NamedChildren = !RequiredType.HasValue; + } - /// 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); - if (RequiredType.HasValue) - info.AddValue("child_type", RequiredType.Value); - } + /// 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); + if (RequiredType.HasValue) + info.AddValue("child_type", RequiredType.Value); + } - /// Adds an item to the . - /// The object to add to the . - /// The is read-only. - /// - [SuppressMessage("ReSharper", "AnnotationConflictInHierarchy")] - public sealed override void Add(Tag item) - { - base.Add(AssertConventions(item)); - item.Parent = this; - } + /// Adds an item to the . + /// The object to add to the . + /// The is read-only. + /// + [SuppressMessage("ReSharper", "AnnotationConflictInHierarchy")] + public sealed override void Add(Tag item) + { + base.Add(AssertConventions(item)); + item.Parent = this; + } - /// Inserts an item to the at the specified index. - /// The zero-based index at which should be inserted. - /// The object to insert into the . - /// - /// is not a valid index in the . - /// The is read-only. - /// - [SuppressMessage("ReSharper", "AnnotationConflictInHierarchy")] - public sealed override void Insert(int index, Tag item) - { - base.Insert(index, AssertConventions(item)); - item.Parent = this; - } + /// Inserts an item to the at the specified index. + /// The zero-based index at which should be inserted. + /// The object to insert into the . + /// + /// is not a valid index in the . + /// The is read-only. + /// + [SuppressMessage("ReSharper", "AnnotationConflictInHierarchy")] + public sealed override void Insert(int index, Tag item) + { + base.Insert(index, AssertConventions(item)); + item.Parent = this; + } - /// Gets or sets the element at the specified index. - /// The zero-based index of the element to get or set. - /// - /// is not a valid index in the . - /// The property is set and the is read-only. - /// The element at the specified index. - /// - public sealed override Tag this[int index] + /// Gets or sets the element at the specified index. + /// The zero-based index of the element to get or set. + /// + /// is not a valid index in the . + /// The property is set and the is read-only. + /// The element at the specified index. + /// + public sealed override Tag this[int index] + { + get => base[index]; + set { - get => base[index]; - set - { - base[index] = AssertConventions(value); - value.Parent = this; - } + base[index] = AssertConventions(value); + value.Parent = this; } + } - /// Removes all items from the . - /// The is read-only. - /// - public sealed override void Clear() - { - foreach (var item in this) - item.Parent = null; - base.Clear(); - } + /// Removes all items from the . + /// The is read-only. + /// + public sealed override void Clear() + { + foreach (var item in this) + item.Parent = null; + base.Clear(); + } - /// Removes the first occurrence of a specific object from the . - /// The object to remove from the . - /// The is read-only. - /// - /// if was successfully removed from the ; otherwise, . This method also returns if is not found in the original . - /// - public sealed override bool Remove(Tag item) - { - if (item is null || !base.Remove(item)) - return false; + /// Removes the first occurrence of a specific object from the . + /// The object to remove from the . + /// The is read-only. + /// + /// if was successfully removed from the ; otherwise, . This method also returns if is not found in the original . + /// + public sealed override bool Remove(Tag item) + { + if (item is null || !base.Remove(item)) + return false; - item.Parent = null; - return true; - } + item.Parent = null; + return true; + } - /// Removes the item at the specified index. - /// The zero-based index of the item to remove. - /// - /// is not a valid index in the . - /// The is read-only. - /// - public sealed override void RemoveAt(int index) - { - this[index].Parent = null; - base.RemoveAt(index); - } + /// Removes the item at the specified index. + /// The zero-based index of the item to remove. + /// + /// is not a valid index in the . + /// The is read-only. + /// + public sealed override void RemoveAt(int index) + { + this[index].Parent = null; + base.RemoveAt(index); + } - /// - /// Performs routine checks to ensure that the given complies with the NBT standard for this collection type. - /// - /// A instance to validate. - /// Returns the instance. - /// - /// - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - protected Tag AssertConventions([CanBeNull] Tag tag) - { - if (tag is null) - throw new ArgumentNullException(nameof(tag), Strings.ChildCannotBeNull); + /// + /// Performs routine checks to ensure that the given complies with the NBT standard for this collection type. + /// + /// A instance to validate. + /// Returns the instance. + /// + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected Tag AssertConventions(Tag? tag) + { + if (tag is null) + throw new ArgumentNullException(nameof(tag), Strings.ChildCannotBeNull); - switch (NamedChildren) - { - case true when tag.Name is null: - throw new FormatException(Strings.ChildrenMustBeNamed); - case false when tag.Name != null: - throw new FormatException(Strings.ChildrenMustNotBeNamed); - } + switch (NamedChildren) + { + case true when tag.Name is null: + throw new FormatException(Strings.ChildrenMustBeNamed); + case false when tag.Name != null: + throw new FormatException(Strings.ChildrenMustNotBeNamed); + } - if (RequiredType.HasValue && RequiredType.Value != tag.Type) - throw new ArrayTypeMismatchException(Strings.ChildWrongType); + if (RequiredType.HasValue && RequiredType.Value != tag.Type) + throw new ArrayTypeMismatchException(Strings.ChildWrongType); - return tag; - } + return tag; } } \ No newline at end of file diff --git a/SharpNBT/Tags/TagType.cs b/SharpNBT/Tags/TagType.cs index 607e69e..285322b 100644 --- a/SharpNBT/Tags/TagType.cs +++ b/SharpNBT/Tags/TagType.cs @@ -1,77 +1,76 @@ using System; using JetBrains.Annotations; -namespace SharpNBT +namespace SharpNBT; + +/// +/// Strongly-typed numerical constants that are prefixed to tags to denote their type. +/// +[PublicAPI][Serializable] +public enum TagType : byte { /// - /// Strongly-typed numerical constants that are prefixed to tags to denote their type. + /// Signifies the end of a . /// - [PublicAPI][Serializable] - public enum TagType : byte - { - /// - /// Signifies the end of a . - /// - End = 0x00, + End = 0x00, - /// - /// A single signed byte, - /// - Byte = 0x01, + /// + /// A single signed byte, + /// + Byte = 0x01, - /// - /// A single signed 16-bit integer. - /// - Short = 0x02, + /// + /// A single signed 16-bit integer. + /// + Short = 0x02, - /// - /// A single signed 32-bit integer. - /// - Int = 0x03, + /// + /// A single signed 32-bit integer. + /// + Int = 0x03, - /// - /// A single signed 64-bit integer. - /// - Long = 0x04, + /// + /// A single signed 64-bit integer. + /// + Long = 0x04, - /// - /// A single IEEE-754 single-precision floating point number. - /// - Float = 0x05, + /// + /// A single IEEE-754 single-precision floating point number. + /// + Float = 0x05, - /// - /// A single IEEE-754 double-precision floating point number. - /// - Double = 0x06, + /// + /// A single IEEE-754 double-precision floating point number. + /// + Double = 0x06, - /// - /// A length-prefixed array of bytes. - /// - ByteArray = 0x07, + /// + /// A length-prefixed array of bytes. + /// + ByteArray = 0x07, - /// - /// A length-prefixed UTF-8 string. - /// - String = 0x08, + /// + /// A length-prefixed UTF-8 string. + /// + String = 0x08, - /// - /// A list of nameless tags, all of the same type. - /// - List = 0x09, + /// + /// A list of nameless tags, all of the same type. + /// + List = 0x09, - /// - /// A set of named tags. - /// - Compound = 0x0a, + /// + /// A set of named tags. + /// + Compound = 0x0a, - /// - /// A length-prefixed array of signed 32-bit integers. - /// - IntArray = 0x0b, + /// + /// A length-prefixed array of signed 32-bit integers. + /// + IntArray = 0x0b, - /// - /// A length-prefixed array of signed 64-bit integers. - /// - LongArray = 0x0c - } + /// + /// A length-prefixed array of signed 64-bit integers. + /// + LongArray = 0x0c } \ No newline at end of file diff --git a/SharpNBT/VarInt.cs b/SharpNBT/VarInt.cs index 42eebd2..7acd518 100644 --- a/SharpNBT/VarInt.cs +++ b/SharpNBT/VarInt.cs @@ -2,116 +2,115 @@ using System.IO; using JetBrains.Annotations; -namespace SharpNBT +namespace SharpNBT; + +/// +/// Provides static methods for reading and writing variable-length integers that are up to 5 bytes from both streams and buffers. +/// +[PublicAPI] +public static class VarInt { /// - /// Provides static methods for reading and writing variable-length integers that are up to 5 bytes from both streams and buffers. + /// Encodes the given to a variable-length integer up to 5 bytes long, and writes it to the . /// - [PublicAPI] - public static class VarInt + /// A instance to write the value to. + /// The value to encode and write. + /// Flag indicating if the value will be ZigZag encoded. + /// The number of bytes written to the . + public static int Write(Stream stream, int value, bool zigzag = false) { - /// - /// Encodes the given to a variable-length integer up to 5 bytes long, and writes it to the . - /// - /// A instance to write the value to. - /// The value to encode and write. - /// Flag indicating if the value will be ZigZag encoded. - /// The number of bytes written to the . - public static int Write([NotNull] Stream stream, int value, bool zigzag = false) - { - var buffer = Encode(value, zigzag); - stream.Write(buffer, 0, buffer.Length); - return buffer.Length; - } + var buffer = Encode(value, zigzag); + stream.Write(buffer, 0, buffer.Length); + return buffer.Length; + } - /// - /// Reads up to 5 bytes from the given and returns the VarInt value as a 32-bit integer. - /// - /// A instance to read from. - /// Flag indicating if the value is ZigZag encoded. - /// The parsed value read from the . - public static int Read([NotNull] Stream stream, bool zigzag = false) - { - var value = VarIntUtil.Decode(stream, 32, out var dummy); - return zigzag ? (int) VarIntUtil.DecodeZigZag(value) : unchecked((int)value); - } + /// + /// Reads up to 5 bytes from the given and returns the VarInt value as a 32-bit integer. + /// + /// A instance to read from. + /// Flag indicating if the value is ZigZag encoded. + /// The parsed value read from the . + public static int Read(Stream stream, bool zigzag = false) + { + var value = VarIntUtil.Decode(stream, 32, out var dummy); + return zigzag ? (int) VarIntUtil.DecodeZigZag(value) : unchecked((int)value); + } - /// - /// Reads up to 5 bytes from the given and returns the VarInt value as a 32-bit integer. - /// - /// A instance to read from. - /// A variable to store the number of bytes read from the . - /// Flag indicating if the value is ZigZag encoded. - /// The parsed value read from the . - public static int Read([NotNull] Stream stream, out int size, bool zigzag = false) - { - var value = VarIntUtil.Decode(stream, 32, out size); - return zigzag ? (int) VarIntUtil.DecodeZigZag(value) : unchecked((int)value); - } + /// + /// Reads up to 5 bytes from the given and returns the VarInt value as a 32-bit integer. + /// + /// A instance to read from. + /// A variable to store the number of bytes read from the . + /// Flag indicating if the value is ZigZag encoded. + /// The parsed value read from the . + public static int Read(Stream stream, out int size, bool zigzag = false) + { + var value = VarIntUtil.Decode(stream, 32, out size); + return zigzag ? (int) VarIntUtil.DecodeZigZag(value) : unchecked((int)value); + } - /// - /// Encodes the given and returns an array of bytes that represent it. - /// - /// The value to encode. - /// Flag indicating if the value will be ZigZag encoded. - /// An array of bytes representing the as a variable length integer. - public static byte[] Encode(int value, bool zigzag = false) - { - if (zigzag) - return VarIntUtil.Encode(unchecked((ulong)VarIntUtil.EncodeZigZag(value, 32))); - return VarIntUtil.Encode(unchecked((uint)value)); - } + /// + /// Encodes the given and returns an array of bytes that represent it. + /// + /// The value to encode. + /// Flag indicating if the value will be ZigZag encoded. + /// An array of bytes representing the as a variable length integer. + public static byte[] Encode(int value, bool zigzag = false) + { + if (zigzag) + return VarIntUtil.Encode(unchecked((ulong)VarIntUtil.EncodeZigZag(value, 32))); + return VarIntUtil.Encode(unchecked((uint)value)); + } - /// - /// Decodes a buffer of bytes that represent a variable-length integer up to 5 bytes long. - /// - /// A buffer containing the data to be decoded. - /// The offset into the to begin reading. - /// The maximum number of bytes that should be read from the . - /// A variable to store the actual number of bytes read from the . - /// Flag indicating if the value is ZigZag encoded. - /// The decoded value. - public static long Decode([NotNull] byte[] buffer, int offset, int count, out int size, bool zigzag = false) - { - return Decode(new ReadOnlySpan(buffer, offset, count), out size, zigzag); - } + /// + /// Decodes a buffer of bytes that represent a variable-length integer up to 5 bytes long. + /// + /// A buffer containing the data to be decoded. + /// The offset into the to begin reading. + /// The maximum number of bytes that should be read from the . + /// A variable to store the actual number of bytes read from the . + /// Flag indicating if the value is ZigZag encoded. + /// The decoded value. + public static long Decode(byte[] buffer, int offset, int count, out int size, bool zigzag = false) + { + return Decode(new ReadOnlySpan(buffer, offset, count), out size, zigzag); + } - /// - /// Decodes a buffer of bytes that represent a variable-length integer up to 5 bytes long. - /// - /// A buffer containing the data to be decoded. - /// The offset into the to begin reading. - /// The maximum number of bytes that should be read from the . - /// Flag indicating if the value is ZigZag encoded. - /// The decoded value. - public static long Decode([NotNull] byte[] buffer, int offset, int count, bool zigzag = false) - { - return Decode(new ReadOnlySpan(buffer, offset, count), out var dummy, zigzag); - } + /// + /// Decodes a buffer of bytes that represent a variable-length integer up to 5 bytes long. + /// + /// A buffer containing the data to be decoded. + /// The offset into the to begin reading. + /// The maximum number of bytes that should be read from the . + /// Flag indicating if the value is ZigZag encoded. + /// The decoded value. + public static long Decode(byte[] buffer, int offset, int count, bool zigzag = false) + { + return Decode(new ReadOnlySpan(buffer, offset, count), out var dummy, zigzag); + } - /// - /// Decodes a buffer of bytes that represent a variable-length integer up to 5 bytes long. - /// - /// A buffer containing the data to be decoded. - /// A variable to store the actual number of bytes read from the . - /// Flag indicating if the value is ZigZag encoded. - /// The decoded value. - public static int Decode(ReadOnlySpan buffer, out int size, bool zigzag = false) - { - var value = VarIntUtil.Decode(buffer, 32, out size); - return zigzag ? (int) VarIntUtil.DecodeZigZag(value) : unchecked((int)value); - } + /// + /// Decodes a buffer of bytes that represent a variable-length integer up to 5 bytes long. + /// + /// A buffer containing the data to be decoded. + /// A variable to store the actual number of bytes read from the . + /// Flag indicating if the value is ZigZag encoded. + /// The decoded value. + public static int Decode(ReadOnlySpan buffer, out int size, bool zigzag = false) + { + var value = VarIntUtil.Decode(buffer, 32, out size); + return zigzag ? (int) VarIntUtil.DecodeZigZag(value) : unchecked((int)value); + } - /// - /// Decodes a buffer of bytes that represent a variable-length integer up to 5 bytes long. - /// - /// A buffer containing the data to be decoded. - /// Flag indicating if the value is ZigZag encoded. - /// The decoded value. - public static int Decode(ReadOnlySpan buffer, bool zigzag = false) - { - var value = VarIntUtil.Decode(buffer, 32, out var dummy); - return zigzag ? (int) VarIntUtil.DecodeZigZag(value) : unchecked((int)value); - } + /// + /// Decodes a buffer of bytes that represent a variable-length integer up to 5 bytes long. + /// + /// A buffer containing the data to be decoded. + /// Flag indicating if the value is ZigZag encoded. + /// The decoded value. + public static int Decode(ReadOnlySpan buffer, bool zigzag = false) + { + var value = VarIntUtil.Decode(buffer, 32, out var dummy); + return zigzag ? (int) VarIntUtil.DecodeZigZag(value) : unchecked((int)value); } } \ No newline at end of file diff --git a/SharpNBT/VarIntUtil.cs b/SharpNBT/VarIntUtil.cs index d33e171..dc57d2e 100644 --- a/SharpNBT/VarIntUtil.cs +++ b/SharpNBT/VarIntUtil.cs @@ -2,105 +2,104 @@ using System.IO; using System.Runtime.CompilerServices; -namespace SharpNBT +namespace SharpNBT; + +internal static class VarIntUtil { - internal static class VarIntUtil + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static long EncodeZigZag(long value, int bitLength) { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static long EncodeZigZag(long value, int bitLength) - { - return (value << 1) ^ (value >> (bitLength - 1)); - } + return (value << 1) ^ (value >> (bitLength - 1)); + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static long DecodeZigZag(ulong value) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static long DecodeZigZag(ulong value) + { + if ((value & 0x1) == 0x1) { - if ((value & 0x1) == 0x1) - { - return (-1 * ((long)(value >> 1) + 1)); - } - return (long)(value >> 1); + return (-1 * ((long)(value >> 1) + 1)); } + return (long)(value >> 1); + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static byte[] Encode(uint value) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static byte[] Encode(uint value) + { + Span buffer = stackalloc byte[5]; + var pos = 0; + do { - Span buffer = stackalloc byte[5]; - var pos = 0; - do - { - var byteVal = value & 0x7f; - value >>= 7; - if (value != 0) - byteVal |= 0x80; - buffer[pos++] = (byte) byteVal; + var byteVal = value & 0x7f; + value >>= 7; + if (value != 0) + byteVal |= 0x80; + buffer[pos++] = (byte) byteVal; - } while (value != 0); + } while (value != 0); - return buffer[..pos].ToArray(); - } + return buffer[..pos].ToArray(); + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static byte[] Encode(ulong value) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static byte[] Encode(ulong value) + { + Span buffer = stackalloc byte[10]; + var pos = 0; + do { - Span buffer = stackalloc byte[10]; - var pos = 0; - do - { - var byteVal = value & 0x7f; - value >>= 7; - if (value != 0) - byteVal |= 0x80; - buffer[pos++] = (byte) byteVal; + var byteVal = value & 0x7f; + value >>= 7; + if (value != 0) + byteVal |= 0x80; + buffer[pos++] = (byte) byteVal; - } while (value != 0); + } while (value != 0); - return buffer[..pos].ToArray(); - } + return buffer[..pos].ToArray(); + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ulong Decode(ReadOnlySpan buffer, int bits, out int size) - { - var shift = 0; - ulong result = 0; - size = 0; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ulong Decode(ReadOnlySpan buffer, int bits, out int size) + { + var shift = 0; + ulong result = 0; + size = 0; - foreach (ulong byteValue in buffer) - { - ulong tmp = byteValue & 0x7f; - result |= tmp << shift; - if (shift > bits) - throw new OverflowException(string.Format(Strings.VarIntTooMuchData, bits)); - size++; - if ((byteValue & 0x80) != 0x80) - return result; + foreach (ulong byteValue in buffer) + { + ulong tmp = byteValue & 0x7f; + result |= tmp << shift; + if (shift > bits) + throw new OverflowException(string.Format(Strings.VarIntTooMuchData, bits)); + size++; + if ((byteValue & 0x80) != 0x80) + return result; - shift += 7; - } - throw new FormatException(Strings.VarIntCannotDecode); + shift += 7; } + throw new FormatException(Strings.VarIntCannotDecode); + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ulong Decode(Stream stream, int bits, out int size) - { - var shift = 0; - ulong result = 0; - size = 0; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ulong Decode(Stream stream, int bits, out int size) + { + var shift = 0; + ulong result = 0; + size = 0; - while (true) - { - var byteValue = (ulong) stream.ReadByte(); - size++; + while (true) + { + var byteValue = (ulong) stream.ReadByte(); + size++; - ulong tmp = byteValue & 0x7f; - result |= tmp << shift; - if (shift > bits) - throw new OverflowException(string.Format(Strings.VarIntTooMuchData, bits)); - if ((byteValue & 0x80) != 0x80) - return result; + ulong tmp = byteValue & 0x7f; + result |= tmp << shift; + if (shift > bits) + throw new OverflowException(string.Format(Strings.VarIntTooMuchData, bits)); + if ((byteValue & 0x80) != 0x80) + return result; - shift += 7; - } + shift += 7; } } } \ No newline at end of file diff --git a/SharpNBT/VarLong.cs b/SharpNBT/VarLong.cs index bfd5866..a76e3ad 100644 --- a/SharpNBT/VarLong.cs +++ b/SharpNBT/VarLong.cs @@ -2,118 +2,115 @@ using System.IO; using JetBrains.Annotations; -namespace SharpNBT -{ +namespace SharpNBT; +/// +/// Provides static methods for reading and writing variable-length integers that are up to 10 bytes from both streams and buffers. +/// +[PublicAPI] +public static class VarLong +{ /// - /// Provides static methods for reading and writing variable-length integers that are up to 10 bytes from both streams and buffers. + /// Encodes the given to a variable-length integer up to 10 bytes long, and writes it to the . /// - [PublicAPI] - public static class VarLong + /// A instance to write the value to. + /// The value to encode and write. + /// Flag indicating if the value will be ZigZag encoded. + /// The number of bytes written to the . + public static int Write(Stream stream, long value, bool zigzag = false) { - /// - /// Encodes the given to a variable-length integer up to 10 bytes long, and writes it to the . - /// - /// A instance to write the value to. - /// The value to encode and write. - /// Flag indicating if the value will be ZigZag encoded. - /// The number of bytes written to the . - public static int Write([NotNull] Stream stream, long value, bool zigzag = false) - { - var buffer = Encode(value, zigzag); - stream.Write(buffer, 0, buffer.Length); - return buffer.Length; - } + var buffer = Encode(value, zigzag); + stream.Write(buffer, 0, buffer.Length); + return buffer.Length; + } - /// - /// Reads up to 10 bytes from the given and returns the VarLong value as a 64-bit integer. - /// - /// A instance to read from. - /// Flag indicating if the value is ZigZag encoded. - /// The parsed value read from the . - public static long Read([NotNull] Stream stream, bool zigzag = false) - { - var value = VarIntUtil.Decode(stream, 64, out var dummy); - return zigzag ? VarIntUtil.DecodeZigZag(value) : unchecked((long) value); - } + /// + /// Reads up to 10 bytes from the given and returns the VarLong value as a 64-bit integer. + /// + /// A instance to read from. + /// Flag indicating if the value is ZigZag encoded. + /// The parsed value read from the . + public static long Read(Stream stream, bool zigzag = false) + { + var value = VarIntUtil.Decode(stream, 64, out var dummy); + return zigzag ? VarIntUtil.DecodeZigZag(value) : unchecked((long) value); + } - /// - /// Reads up to 10 bytes from the given and returns the VarLong value as a 64-bit integer. - /// - /// A instance to read from. - /// A variable to store the number of bytes read from the . - /// Flag indicating if the value is ZigZag encoded. - /// The parsed value read from the . - public static long Read([NotNull] Stream stream, out int size, bool zigzag = false) - { - var value = VarIntUtil.Decode(stream, 64, out size); - return zigzag ? VarIntUtil.DecodeZigZag(value) : unchecked((long) value); - } + /// + /// Reads up to 10 bytes from the given and returns the VarLong value as a 64-bit integer. + /// + /// A instance to read from. + /// A variable to store the number of bytes read from the . + /// Flag indicating if the value is ZigZag encoded. + /// The parsed value read from the . + public static long Read(Stream stream, out int size, bool zigzag = false) + { + var value = VarIntUtil.Decode(stream, 64, out size); + return zigzag ? VarIntUtil.DecodeZigZag(value) : unchecked((long) value); + } - /// - /// Encodes the given and returns an array of bytes that represent it. - /// - /// The value to encode. - /// Flag indicating if the value will be ZigZag encoded. - /// An array of bytes representing the as a variable length integer. - public static byte[] Encode(long value, bool zigzag = false) - { - if (zigzag) - return VarIntUtil.Encode(unchecked((ulong)VarIntUtil.EncodeZigZag(value, 64))); - return VarIntUtil.Encode(unchecked((ulong)value)); - } + /// + /// Encodes the given and returns an array of bytes that represent it. + /// + /// The value to encode. + /// Flag indicating if the value will be ZigZag encoded. + /// An array of bytes representing the as a variable length integer. + public static byte[] Encode(long value, bool zigzag = false) + { + if (zigzag) + return VarIntUtil.Encode(unchecked((ulong)VarIntUtil.EncodeZigZag(value, 64))); + return VarIntUtil.Encode(unchecked((ulong)value)); + } - /// - /// Decodes a buffer of bytes that represent a variable-length integer up to 10 bytes long. - /// - /// A buffer containing the data to be decoded. - /// The offset into the to begin reading. - /// The maximum number of bytes that should be read from the . - /// A variable to store the actual number of bytes read from the . - /// Flag indicating if the value is ZigZag encoded. - /// The decoded value. - public static long Decode([NotNull] byte[] buffer, int offset, int count, out int size, bool zigzag = false) - { - return Decode(new ReadOnlySpan(buffer, offset, count), out size, zigzag); - } + /// + /// Decodes a buffer of bytes that represent a variable-length integer up to 10 bytes long. + /// + /// A buffer containing the data to be decoded. + /// The offset into the to begin reading. + /// The maximum number of bytes that should be read from the . + /// A variable to store the actual number of bytes read from the . + /// Flag indicating if the value is ZigZag encoded. + /// The decoded value. + public static long Decode(byte[] buffer, int offset, int count, out int size, bool zigzag = false) + { + return Decode(new ReadOnlySpan(buffer, offset, count), out size, zigzag); + } - /// - /// Decodes a buffer of bytes that represent a variable-length integer up to 10 bytes long. - /// - /// A buffer containing the data to be decoded. - /// The offset into the to begin reading. - /// The maximum number of bytes that should be read from the . - /// Flag indicating if the value is ZigZag encoded. - /// The decoded value. - public static long Decode([NotNull] byte[] buffer, int offset, int count, bool zigzag = false) - { - return Decode(new ReadOnlySpan(buffer, offset, count), out var dummy, zigzag); - } + /// + /// Decodes a buffer of bytes that represent a variable-length integer up to 10 bytes long. + /// + /// A buffer containing the data to be decoded. + /// The offset into the to begin reading. + /// The maximum number of bytes that should be read from the . + /// Flag indicating if the value is ZigZag encoded. + /// The decoded value. + public static long Decode(byte[] buffer, int offset, int count, bool zigzag = false) + { + return Decode(new ReadOnlySpan(buffer, offset, count), out var dummy, zigzag); + } - /// - /// Decodes a buffer of bytes that represent a variable-length integer up to 10 bytes long. - /// - /// A buffer containing the data to be decoded. - /// A variable to store the actual number of bytes read from the . - /// Flag indicating if the value is ZigZag encoded. - /// The decoded value. - public static long Decode(ReadOnlySpan buffer, out int size, bool zigzag = false) - { - var value = VarIntUtil.Decode(buffer, 64, out size); - return zigzag ? VarIntUtil.DecodeZigZag(value) : unchecked((long)value); - } + /// + /// Decodes a buffer of bytes that represent a variable-length integer up to 10 bytes long. + /// + /// A buffer containing the data to be decoded. + /// A variable to store the actual number of bytes read from the . + /// Flag indicating if the value is ZigZag encoded. + /// The decoded value. + public static long Decode(ReadOnlySpan buffer, out int size, bool zigzag = false) + { + var value = VarIntUtil.Decode(buffer, 64, out size); + return zigzag ? VarIntUtil.DecodeZigZag(value) : unchecked((long)value); + } - /// - /// Decodes a buffer of bytes that represent a variable-length integer up to 10 bytes long. - /// - /// A buffer containing the data to be decoded. - /// Flag indicating if the value is ZigZag encoded. - /// The decoded value. - public static long Decode(ReadOnlySpan buffer, bool zigzag = false) - { - var value = VarIntUtil.Decode(buffer, 64, out var dummy); - return zigzag ? VarIntUtil.DecodeZigZag(value) : unchecked((long)value); - } + /// + /// Decodes a buffer of bytes that represent a variable-length integer up to 10 bytes long. + /// + /// A buffer containing the data to be decoded. + /// Flag indicating if the value is ZigZag encoded. + /// The decoded value. + public static long Decode(ReadOnlySpan buffer, bool zigzag = false) + { + var value = VarIntUtil.Decode(buffer, 64, out var dummy); + return zigzag ? VarIntUtil.DecodeZigZag(value) : unchecked((long)value); } - } \ No newline at end of file diff --git a/SharpNBT/ZLib/Adler32.cs b/SharpNBT/ZLib/Adler32.cs deleted file mode 100644 index f119ade..0000000 --- a/SharpNBT/ZLib/Adler32.cs +++ /dev/null @@ -1,115 +0,0 @@ -using System; -using System.Runtime.CompilerServices; -using JetBrains.Annotations; - -namespace SharpNBT.ZLib -{ - /// - /// An Adler-32 checksum implementation for ZLib streams. - /// - /// - [PublicAPI] - public sealed class Adler32 - { - private uint a = 1; - private uint b; - private const int BASE = 65521; - private const int MAX = 5550; - private int pending; - - /// - /// Update the checksum value with the specified . - /// - /// A single value to calculate into the checksum. - public void Update(byte data) - { - if (pending >= MAX) - UpdateModulus(); - a += data; - b += a; - pending++; - } - - /// - /// Update the checksum value with the specified . - /// - /// A buffer containing the values to calculate into the checksum. - public void Update(byte[] data) => Update(new ReadOnlySpan(data, 0, data.Length)); - - /// - /// Update the checksum value with the specified . - /// - /// A buffer containing the values to calculate into the checksum. - public void Update(ReadOnlySpan data) - { - unchecked - { - var nextCompute = MAX - pending; - for (var i = 0; i < data.Length; i++) - { - if (i == nextCompute) - { - UpdateModulus(); - nextCompute = i + MAX; - } - a += data[i]; - b += a; - pending++; - } - } - } - - /// - /// Update the checksum value with the specified . - /// - /// A buffer containing the values to calculate into the checksum. - /// An offset into the to begin adding from. - /// The number of bytes in to calculate. - public void Update(byte[] data, int offset, int length) => Update(new ReadOnlySpan(data, offset, length)); - - /// - /// Reset the checksum back to the initial state. - /// - public void Reset() - { - a = 1; - b = 0; - pending = 0; - } - - /// - /// Gets the current calculated checksum value as a signed 32-bit integer. - /// - public int Value - { - get - { - if (pending > 0) - UpdateModulus(); - return unchecked((int)((b << 16) | a)); - } - } - - /// - /// Gets the current calculated checksum value as the original unsigned 32-bit integer value. - /// - [CLSCompliant(false)] - public uint UnsignedValue - { - get - { - if (pending > 0) - UpdateModulus(); - return (b << 16) | a; - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void UpdateModulus() - { - a %= BASE; - b %= BASE; - pending = 0; - } - } -} \ No newline at end of file diff --git a/SharpNBT/ZLib/ZLibHeader.cs b/SharpNBT/ZLib/ZLibHeader.cs deleted file mode 100644 index 288fa6b..0000000 --- a/SharpNBT/ZLib/ZLibHeader.cs +++ /dev/null @@ -1,115 +0,0 @@ -using System; -using System.IO.Compression; -using JetBrains.Annotations; - -namespace SharpNBT.ZLib -{ - /// - /// Provides methods for the creation of a ZLib header as outlined by RFC-1950. - /// - /// - public sealed class ZLibHeader - { - private byte compressionMethod; - private byte compressionInfo; - private byte fCheck; - private byte fLevel; - private byte fDict; - - /// - /// Gets a flag indicating if this represents a valid and supported ZLib format. - /// - public bool IsSupported { get; private set; } - - /// - /// Creates a new instance of the class using the specified compression strategy. - /// - /// The desired level of compression. - public ZLibHeader(CompressionLevel compressionLevel = CompressionLevel.Fastest) - { - const byte FASTER = 0; - const byte DEFAULT = 2; - const byte OPTIMAL = 3; - - compressionMethod = 8; // Deflate algorithm - compressionInfo = 7; // Window size - fDict = 0; // false - - fLevel = compressionLevel switch - { - CompressionLevel.NoCompression => FASTER, - CompressionLevel.Fastest => DEFAULT, - CompressionLevel.Optimal => OPTIMAL, - _ => throw new ArgumentOutOfRangeException(nameof(compressionLevel)) - }; - } - - private void RefreshFCheck() - { - var flg = (byte) (Convert.ToByte(fLevel) << 1); - flg |= Convert.ToByte(fDict); - - fCheck = Convert.ToByte(31 - Convert.ToByte((CMF * 256 + flg) % 31)); - if (fCheck > 31) - throw new ArgumentOutOfRangeException(nameof(fCheck), Strings.ZLibValueGreater31); - } - - /// - /// Gets the computed "compression method and flags" (CMF) value of the header. - /// - // ReSharper disable once InconsistentNaming - private byte CMF => (byte)((compressionInfo << 4) | compressionMethod); - - /// - /// Gets the computed "flags" (FLG) value of the header. - /// - // ReSharper disable once InconsistentNaming - private byte FLG => (byte)((fLevel << 6) | (fDict << 5) | fCheck); - - /// - /// Computes and returns the CMF and FLG magic numbers associated with a ZLib header. - /// - /// A two element byte array containing the CMF and FLG values. - [NotNull] - public byte[] Encode() - { - var result = new byte[2]; - RefreshFCheck(); - - result[0] = CMF; - result[1] = FLG; - - return result; - } - - /// - /// Calculates and returns a new instance from the specified CMF and FLG magic bytes read from a ZLib header. - /// - /// The first byte of a ZLib header. - /// The second byte of a ZLib header. - /// The decoded instance. - [NotNull] - public static ZLibHeader Decode(int cmf, int flg) - { - var result = new ZLibHeader(); - cmf = cmf & 0x0FF; - flg = flg & 0x0FF; - - result.compressionInfo = Convert.ToByte((cmf & 0xF0) >> 4); - if (result.compressionInfo > 15) - throw new ArgumentOutOfRangeException(nameof(result.compressionInfo), Strings.ZLibValueGreater15); - - result.compressionMethod = Convert.ToByte(cmf & 0x0F); - if (result.compressionInfo > 15) - throw new ArgumentOutOfRangeException(nameof(result.compressionMethod), Strings.ZLibValueGreater15); - - result.fCheck = Convert.ToByte(flg & 0x1F); - result.fDict = Convert.ToByte((flg & 0x20) >> 5); - result.fLevel = Convert.ToByte((flg & 0xC0) >> 6); - - result.IsSupported = (result.compressionMethod == 8) && (result.compressionInfo == 7) && (((cmf * 256 + flg) % 31 == 0)) && (result.fDict == 0); - - return result; - } - } -} diff --git a/SharpNBT/ZLib/ZLibStream.cs b/SharpNBT/ZLib/ZLibStream.cs deleted file mode 100644 index 692e5c3..0000000 --- a/SharpNBT/ZLib/ZLibStream.cs +++ /dev/null @@ -1,410 +0,0 @@ -using System; -using System.IO; -using System.IO.Compression; -using System.Threading; -using System.Threading.Tasks; -using JetBrains.Annotations; - -namespace SharpNBT.ZLib -{ - /// - /// ZLib stream implementation for reading/writing. - /// - /// Generally speaking, the ZLib format is merely a DEFLATE stream, prefixed with a header, and performs a cyclic redundancy check to ensure data integrity - /// by storing an Adler-32 checksum after the compressed payload. - /// - [PublicAPI] - public class ZLibStream : Stream - { - /// - /// The internal DEFLATE stream used for compression/decompression. - /// - protected readonly DeflateStream DeflateStream; - - /// - /// The base stream the ZlibStream is wrapping. - /// - protected readonly Stream BaseStream; - - private readonly CompressionMode compressionMode; - private readonly bool leaveOpen; - private readonly Adler32 adler32 = new Adler32(); - private bool isClosed; - private byte[] checksum; - - /// - /// Initializes a new instance of the class using the specified compression and . - /// - /// A instance to be compressed. - /// The level of compression to use. - public ZLibStream([NotNull] Stream stream, CompressionLevel level) : this(stream, level, false) - { - } - - /// - /// Initializes a new instance of the class using the specified compression and . - /// - /// A instance to be compressed or uncompressed. - /// The type of compression to use. - public ZLibStream([NotNull] Stream stream, CompressionMode mode) : this(stream, mode, false) - { - } - - /// - /// Initializes a new instance of the class using the specified compression and , - /// and optionally leaves the stream open. - /// - /// A instance to be compressed. - /// The level of compression to use. - /// Indicates if the should be left open after this is closed. - public ZLibStream([NotNull] Stream stream, CompressionLevel level, bool leaveOpen) - { - compressionMode = CompressionMode.Compress; - this.leaveOpen = leaveOpen; - BaseStream = stream; - DeflateStream = CreateStream(level); - } - - /// - /// Initializes a new instance of the class using the specified compression and , - /// and optionally leaves the stream open. - /// - /// A instance to be compressed or uncompressed. - /// The type of compression to use. - /// Indicates if the should be left open after this is closed. - public ZLibStream([NotNull] Stream stream, CompressionMode mode, bool leaveOpen) - { - compressionMode = mode; - this.leaveOpen = leaveOpen; - BaseStream = stream; - DeflateStream = CreateStream(CompressionLevel.Fastest); - } - - /// Gets a value indicating whether the current stream supports reading. - /// - /// if the stream supports reading; otherwise, . - public override bool CanRead => compressionMode == CompressionMode.Decompress && !isClosed; - - /// Gets a value indicating whether the current stream supports writing. - /// - /// if the stream supports writing; otherwise, . - public override bool CanWrite => compressionMode == CompressionMode.Compress && !isClosed; - - /// Gets a value indicating whether the current stream supports seeking. - /// - /// if the stream supports seeking; otherwise, . - public override bool CanSeek => false; - - /// Gets the length in bytes of the stream. - /// A long value representing the length of the stream in bytes. - /// This property is not supported and will always throw an exception. - /// Methods were called after the stream was closed. - public override long Length => throw new NotSupportedException("Stream does not support this function."); - - /// Gets or sets the position within the current stream. - /// The current position within the stream. - /// An I/O error occurs. - /// This property is not supported and will always throw an exception. - /// Methods were called after the stream was closed. - public override long Position - { - get => throw new NotSupportedException("Stream does not support getting/setting position."); - set => throw new NotSupportedException("Stream does not support getting/setting position."); - } - - /// Reads a byte from the stream and advances the position within the stream by one byte, or returns -1 if at the end of the stream. - /// The unsigned byte cast to an , or -1 if at the end of the stream. - /// The stream does not support reading. - /// Methods were called after the stream was closed. - public override int ReadByte() - { - var n = DeflateStream.ReadByte(); - if (n == -1) // EOF - ReadCrc(); - else - adler32.Update(Convert.ToByte(n)); - - return n; - } - - /// Reads a sequence of bytes from the current stream and advances the position within the stream by the number of bytes read. - /// A region of memory. When this method returns, the contents of this region are replaced by the bytes read from the current source. - /// The total number of bytes read into the buffer. This can be less than the number of bytes allocated in the buffer if that many bytes are not currently available, or zero (0) if the end of the stream has been reached. - public override int Read(Span buffer) - { - var read = DeflateStream.Read(buffer); - if (read < 1 && buffer.Length > 0) - ReadCrc(); - else - adler32.Update(buffer[..read]); - - return read; - } - - /// Reads a sequence of bytes from the current stream and advances the position within the stream by the number of bytes read. - /// An array of bytes. When this method returns, the buffer contains the specified byte array with the values between and ( + - 1) replaced by the bytes read from the current source. - /// The zero-based byte offset in at which to begin storing the data read from the current stream. - /// The maximum number of bytes to be read from the current stream. - /// The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many bytes are not currently available, or zero (0) if the end of the stream has been reached. - /// The sum of and is larger than the buffer length. - /// - /// is . - /// - /// or is negative. - /// An I/O error occurs. - /// The stream does not support reading. - /// Methods were called after the stream was closed. - public override int Read(byte[] buffer, int offset, int count) => Read(new Span(buffer, offset, count)); - - /// Asynchronously reads a sequence of bytes from the current stream, advances the position within the stream by the number of bytes read, and monitors cancellation requests. - /// The region of memory to write the data into. - /// The token to monitor for cancellation requests. The default value is . - /// A task that represents the asynchronous read operation. The value of its property contains the total number of bytes read into the buffer. The result value can be less than the number of bytes allocated in the buffer if that many bytes are not currently available, or it can be 0 (zero) if the end of the stream has been reached. - public override async ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default) - { - var read = await DeflateStream.ReadAsync(buffer, cancellationToken); - adler32.Update(buffer.Slice(0, read).Span); - return read; - } - - /// Asynchronously reads a sequence of bytes from the current stream, advances the position within the stream by the number of bytes read, and monitors cancellation requests. - /// The buffer to write the data into. - /// The byte offset in at which to begin writing data from the stream. - /// The maximum number of bytes to read. - /// The token to monitor for cancellation requests. The default value is . - /// A task that represents the asynchronous read operation. The value of the task parameter contains the total number of bytes read into the buffer. The result value can be less than the number of bytes requested if the number of bytes currently available is less than the requested number, or it can be 0 (zero) if the end of the stream has been reached. - /// - /// is . - /// - /// or is negative. - /// The sum of and is larger than the buffer length. - /// The stream does not support reading. - /// The stream has been disposed. - /// The stream is currently in use by a previous read operation. - public override async Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { - var read = await DeflateStream.ReadAsync(buffer, offset, count, cancellationToken); - adler32.Update(new ReadOnlySpan(buffer, offset, read)); - return read; - } - - /// Writes a byte to the current position in the stream and advances the position within the stream by one byte. - /// The byte to write to the stream. - /// An I/O error occurs. - /// The stream does not support writing, or the stream is already closed. - /// Methods were called after the stream was closed. - public override void WriteByte(byte value) - { - DeflateStream.WriteByte(value); - adler32.Update(value); - } - - /// Writes a sequence of bytes to the current stream and advances the current position within this stream by the number of bytes written. - /// An array of bytes. This method copies bytes from to the current stream. - /// The zero-based byte offset in at which to begin copying bytes to the current stream. - /// The number of bytes to be written to the current stream. - /// The sum of and is greater than the buffer length. - /// - /// is . - /// - /// or is negative. - /// An I/O error occurred, such as the specified file cannot be found. - /// The stream does not support writing. - /// - /// was called after the stream was closed. - public override void Write(byte[] buffer, int offset, int count) - { - DeflateStream.Write(buffer, offset, count); - adler32.Update(buffer, offset, count); - } - - /// Writes a sequence of bytes to the current stream and advances the current position within this stream by the number of bytes written. - /// A region of memory. This method copies the contents of this region to the current stream. - public override void Write(ReadOnlySpan buffer) - { - DeflateStream.Write(buffer); - adler32.Update(buffer); - } - - /// Asynchronously writes a sequence of bytes to the current stream, advances the current position within this stream by the number of bytes written, and monitors cancellation requests. - /// The buffer to write data from. - /// The zero-based byte offset in from which to begin copying bytes to the stream. - /// The maximum number of bytes to write. - /// The token to monitor for cancellation requests. The default value is . - /// A task that represents the asynchronous write operation. - /// - /// is . - /// - /// or is negative. - /// The sum of and is larger than the buffer length. - /// The stream does not support writing. - /// The stream has been disposed. - /// The stream is currently in use by a previous write operation. - public override async Task WriteAsync([NotNull] byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { - await DeflateStream.WriteAsync(buffer, offset, count, cancellationToken); - adler32.Update(new ReadOnlySpan(buffer, offset, count)); - } - - /// Asynchronously writes a sequence of bytes to the current stream, advances the current position within this stream by the number of bytes written, and monitors cancellation requests. - /// The region of memory to write data from. - /// The token to monitor for cancellation requests. The default value is . - /// A task that represents the asynchronous write operation. - public override async ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) - { - await DeflateStream.WriteAsync(buffer, cancellationToken); - adler32.Update(buffer.Span); - } - - /// - /// Closes the current stream and releases any resources (such as sockets and file handles) associated with the current stream. - /// Instead of calling this method, ensure that the stream is properly disposed. - /// - public override void Close() - { - if (isClosed) - return; - - isClosed = true; - if (compressionMode == CompressionMode.Compress) - { - Flush(); - DeflateStream.Close(); - - checksum = BitConverter.GetBytes(adler32.Value); - if (BitConverter.IsLittleEndian) - Array.Reverse(checksum); - BaseStream.Write(checksum, 0, checksum.Length); - } - else - { - DeflateStream.Close(); - if (checksum == null) - ReadCrc(); - } - - if (!leaveOpen) - BaseStream.Close(); - } - - /// Clears all buffers for this stream and causes any buffered data to be written to the underlying device. - /// An I/O error occurs. - public override void Flush() - { - DeflateStream?.Flush(); - BaseStream?.Flush(); - } - - /// Sets the position within the current stream. - /// A byte offset relative to the parameter. - /// A value of type indicating the reference point used to obtain the new position. - /// The new position within the current stream. - /// An I/O error occurs. - /// The stream does not support seeking, such as if the stream is constructed from a pipe or console output. - /// Methods were called after the stream was closed. - public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException(); - - /// Sets the length of the current stream. - /// The desired length of the current stream in bytes. - /// An I/O error occurs. - /// The stream does not support both writing and seeking, such as if the stream is constructed from a pipe or console output. - /// Methods were called after the stream was closed. - public override void SetLength(long value) => throw new NotSupportedException(); - - /// - /// Checks if the given is in ZLib format. - /// - /// A instance to query. - /// is is in a supported ZLib format, otherwise if not or an error occured. - public static bool IsSupported(Stream stream) - { - int cmf; - int flag; - - if (!stream.CanRead) - return false; - - if (stream.Position != 0) - { - var pos = stream.Position; - stream.Seek(0, SeekOrigin.Begin); - cmf = stream.ReadByte(); - flag = stream.ReadByte(); - stream.Seek(pos, SeekOrigin.Begin); - } - else - { - cmf = stream.ReadByte(); - flag = stream.ReadByte(); - } - - try - { - var header = ZLibHeader.Decode(cmf, flag); - return header.IsSupported; - } - catch - { - return false; - } - } - - /// - /// Reads the last 4 bytes of the stream where the CRC is stored. - /// - /// Thrown when the stream is cannot seek to the checksum location to read. - /// Thrown when the checksum comparison does not match. - private void ReadCrc() - { - checksum = new byte[4]; - BaseStream.Seek(-4, SeekOrigin.End); - if (BaseStream.Read(checksum, 0, 4) < 4) - throw new EndOfStreamException(); - - if (BitConverter.IsLittleEndian) - Array.Reverse(checksum); - - var crcAdler = adler32.Value; - var crcStream = BitConverter.ToInt32(checksum, 0); - - if (crcStream != crcAdler) - throw new InvalidDataException(Strings.CRCFail); - } - - /// - /// Initializes the underlying instance. - /// - private DeflateStream CreateStream(CompressionLevel compressionLevel) - { - switch (compressionMode) - { - case CompressionMode.Compress: - { - WriteHeader(compressionLevel); - return new DeflateStream(BaseStream, compressionLevel, true); - } - case CompressionMode.Decompress: - { - if (!IsSupported(BaseStream)) - throw new InvalidDataException(Strings.ZlibUnsupported); - - return new DeflateStream(BaseStream, CompressionMode.Decompress, true); - } - default: - throw new ArgumentOutOfRangeException(nameof(compressionMode)); - } - } - - /// - /// Writes the ZLib header to the stream. - /// - /// The compression level being used. - protected void WriteHeader(CompressionLevel compressionLevel) - { - var header = new ZLibHeader(compressionLevel); - var magicNumber = header.Encode(); - BaseStream.WriteByte(magicNumber[0]); - BaseStream.WriteByte(magicNumber[1]); - } - } -}