diff --git a/SharpNBT/EndianUtils.cs b/SharpNBT/EndianUtils.cs
index 08dd72c..c76b5ba 100644
--- a/SharpNBT/EndianUtils.cs
+++ b/SharpNBT/EndianUtils.cs
@@ -16,19 +16,20 @@ namespace SharpNBT
/// The value to convert.
/// An array of bytes representing the value in big-endian format.
/// The endianness of the host machine is accounted for.
- internal static byte[] GetBytes(this short n) => BitConverter.GetBytes(BitConverter.IsLittleEndian ? SwapEndian(n) : n);
+ public static byte[] BigEndianBytes(this short n) => BitConverter.GetBytes(BitConverter.IsLittleEndian ? SwapEndian(n) : n);
- ///
- internal static byte[] GetBytes(this int n) => BitConverter.GetBytes(BitConverter.IsLittleEndian ? SwapEndian(n) : n);
+ ///
+ public static byte[] BigEndianBytes(this int n) => BitConverter.GetBytes(BitConverter.IsLittleEndian ? SwapEndian(n) : n);
- ///
- internal static byte[] GetBytes(this long n) => BitConverter.GetBytes(BitConverter.IsLittleEndian ? SwapEndian(n) : n);
+ ///
+ public static byte[] BigEndianBytes(this long n) => BitConverter.GetBytes(BitConverter.IsLittleEndian ? SwapEndian(n) : n);
- ///
- internal static byte[] GetBytes(this ushort n) => BitConverter.GetBytes(BitConverter.IsLittleEndian ? SwapEndian(n) : n);
+ ///
+ [CLSCompliant(false)]
+ internal static byte[] BigEndianBytes(this ushort n) => BitConverter.GetBytes(BitConverter.IsLittleEndian ? SwapEndian(n) : n);
- ///
- internal static byte[] GetBytes(this float n)
+ ///
+ internal static byte[] BigEndianBytes(this float n)
{
var bytes = BitConverter.GetBytes(n);
if (BitConverter.IsLittleEndian)
@@ -36,8 +37,8 @@ namespace SharpNBT
return bytes;
}
- ///
- internal static byte[] GetBytes(this double n)
+ ///
+ internal static byte[] BigEndianBytes(this double n)
{
var bytes = BitConverter.GetBytes(n);
if (BitConverter.IsLittleEndian)
@@ -57,6 +58,7 @@ namespace SharpNBT
}
///
+ [CLSCompliant(false)]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ushort SwapEndian(this ushort value)
{
@@ -68,6 +70,7 @@ namespace SharpNBT
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)
{
@@ -76,6 +79,7 @@ namespace SharpNBT
}
///
+ [CLSCompliant(false)]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ulong SwapEndian(this ulong val)
{
diff --git a/SharpNBT/NbtFile.cs b/SharpNBT/NbtFile.cs
index 2565b93..2698b06 100644
--- a/SharpNBT/NbtFile.cs
+++ b/SharpNBT/NbtFile.cs
@@ -60,7 +60,7 @@ namespace SharpNBT
///
/// The path of the file to query.
/// if GZip compression was detected, otherwise .
- public static bool IsCompressed(string path)
+ public static bool IsCompressed([NotNull] string path)
{
using var str = File.OpenRead(path);
return str.ReadByte() == 0x1F && str.ReadByte() == 0x8B;
@@ -72,7 +72,7 @@ namespace SharpNBT
/// The path of the file to query write.
/// A instance for the file stream.
/// File compression will be automatically detected and used handled when necessary.
- public static TagReader OpenRead(string path)
+ public static TagReader OpenRead([NotNull] string path)
{
var compressed = IsCompressed(path);
var stream = File.OpenRead(path);
@@ -85,7 +85,7 @@ namespace SharpNBT
/// The path of the file to query write.
/// A flag indicating the compression strategy that will be used, if any.
/// A instance for the file stream.
- public static TagWriter OpenWrite(string path, CompressionLevel compression = CompressionLevel.NoCompression)
+ public static TagWriter OpenWrite([NotNull] string path, CompressionLevel compression = CompressionLevel.NoCompression)
{
var stream = File.OpenWrite(path);
if (compression == CompressionLevel.NoCompression)
diff --git a/SharpNBT/TagWriter.cs b/SharpNBT/TagWriter.cs
index d0a7982..dd2cf66 100644
--- a/SharpNBT/TagWriter.cs
+++ b/SharpNBT/TagWriter.cs
@@ -70,7 +70,7 @@ namespace SharpNBT
public virtual void WriteShort(ShortTag tag)
{
WriteTypeAndName(tag);
- BaseStream.Write(tag.Value.GetBytes(), 0, sizeof(short));
+ BaseStream.Write(tag.Value.BigEndianBytes(), 0, sizeof(short));
}
///
@@ -80,7 +80,7 @@ namespace SharpNBT
public virtual void WriteInt(IntTag tag)
{
WriteTypeAndName(tag);
- BaseStream.Write(tag.Value.GetBytes(), 0, sizeof(int));
+ BaseStream.Write(tag.Value.BigEndianBytes(), 0, sizeof(int));
}
///
@@ -90,7 +90,7 @@ namespace SharpNBT
public virtual void WriteLong(LongTag tag)
{
WriteTypeAndName(tag);
- BaseStream.Write(tag.Value.GetBytes(), 0, sizeof(long));
+ BaseStream.Write(tag.Value.BigEndianBytes(), 0, sizeof(long));
}
///
@@ -100,7 +100,7 @@ namespace SharpNBT
public virtual void WriteFloat(FloatTag tag)
{
WriteTypeAndName(tag);
- BaseStream.Write(tag.Value.GetBytes(), 0, sizeof(float));
+ BaseStream.Write(tag.Value.BigEndianBytes(), 0, sizeof(float));
}
///
@@ -110,7 +110,7 @@ namespace SharpNBT
public virtual void WriteDouble(DoubleTag tag)
{
WriteTypeAndName(tag);
- BaseStream.Write(tag.Value.GetBytes(), 0, sizeof(double));
+ BaseStream.Write(tag.Value.BigEndianBytes(), 0, sizeof(double));
}
///
@@ -130,7 +130,7 @@ namespace SharpNBT
public virtual void WriteByteArray(ByteArrayTag tag)
{
WriteTypeAndName(tag);
- BaseStream.Write(tag.Count.GetBytes(), 0, sizeof(int));
+ BaseStream.Write(tag.Count.BigEndianBytes(), 0, sizeof(int));
BaseStream.Write(tag.ToArray(), 0, tag.Count);
}
@@ -141,7 +141,7 @@ namespace SharpNBT
public virtual void WriteIntArray(IntArrayTag tag)
{
WriteTypeAndName(tag);
- BaseStream.Write(tag.Count.GetBytes(), 0, sizeof(int));
+ BaseStream.Write(tag.Count.BigEndianBytes(), 0, sizeof(int));
var values = new Span(tag.ToArray());
if (BitConverter.IsLittleEndian)
@@ -161,7 +161,7 @@ namespace SharpNBT
{
WriteTypeAndName(tag);
- BaseStream.Write(tag.Count.GetBytes(), 0, sizeof(int));
+ BaseStream.Write(tag.Count.BigEndianBytes(), 0, sizeof(int));
var values = new Span(tag.ToArray());
if (BitConverter.IsLittleEndian)
@@ -181,7 +181,7 @@ namespace SharpNBT
{
WriteTypeAndName(tag);
BaseStream.WriteByte((byte) tag.ChildType);
- BaseStream.Write(tag.Count.GetBytes(), 0, sizeof(int));
+ BaseStream.Write(tag.Count.BigEndianBytes(), 0, sizeof(int));
foreach (var child in tag)
WriteTag(child);
@@ -311,12 +311,12 @@ namespace SharpNBT
{
if (string.IsNullOrEmpty(value))
{
- BaseStream.Write(((ushort) 0).GetBytes(), 0, sizeof(ushort));
+ BaseStream.Write(((ushort) 0).BigEndianBytes(), 0, sizeof(ushort));
}
else
{
var utf8 = Encoding.UTF8.GetBytes(value);
- BaseStream.Write(((ushort) utf8.Length).GetBytes(), 0, sizeof(ushort));
+ BaseStream.Write(((ushort) utf8.Length).BigEndianBytes(), 0, sizeof(ushort));
BaseStream.Write(utf8, 0, utf8.Length);
}
}
diff --git a/SharpNBT/VarInt.cs b/SharpNBT/VarInt.cs
new file mode 100644
index 0000000..dd9edfa
--- /dev/null
+++ b/SharpNBT/VarInt.cs
@@ -0,0 +1,181 @@
+using System;
+using System.IO;
+using JetBrains.Annotations;
+
+namespace SharpNBT
+{
+ ///
+ /// Provides static methods for reading/writing the VarInt implementation commonly used by Minecraft to/from streams and buffers.
+ ///
+ /// This is not part of the NBT API, and is merely included for convenience.
+ [PublicAPI]
+ public class VarInt
+ {
+ private const int INT_SIZE = sizeof(int);
+
+ ///
+ /// Reads a VarInt from the given .
+ ///
+ /// The object to read from.
+ /// The VarInt value as a 32-bit integer.
+ public static int Read([NotNull] Stream stream) => Read(stream, out var dummy);
+
+ ///
+ /// Reads a VarInt from the given .
+ ///
+ /// A buffer containing the VarInt bytes.
+ /// The VarInt value as a 32-bit integer.
+ public static int Read(ReadOnlySpan buffer) => Read(buffer, out var dummy);
+
+ ///
+ /// Reads a VarInt from the given .
+ ///
+ /// A buffer containing the VarInt bytes.
+ /// The start index of the to begin reading at.
+ /// The maximum number of bytes to read from the .
+ /// The VarInt value as a 32-bit integer.
+ public static int Read([NotNull] byte[] buffer, int start, int length) => Read(new ReadOnlySpan(buffer, start, length), out var dummy);
+
+ ///
+ /// Reads a VarInt from the given .
+ ///
+ /// A buffer containing the VarInt bytes.
+ /// The start index of the to begin reading at.
+ /// The maximum number of bytes to read from the .
+ /// A variable to store the number of bytes read from the .
+ /// The VarInt value as a 32-bit integer.
+ public static int Read([NotNull] byte[] buffer, int start, int length, out int count) => Read(new ReadOnlySpan(buffer, start, length), out count);
+
+ ///
+ /// Reads a VarInt from the given .
+ ///
+ /// The object to read from.
+ /// A variable to store the number of bytes read from the .
+ /// The VarInt value as a 32-bit integer.
+ public static int Read([NotNull] Stream stream, out int count)
+ {
+ uint value = 0;
+ count = 0;
+
+ while (true)
+ {
+ if (count == INT_SIZE)
+ throw new OverflowException($"A VarInt cannot exceed {INT_SIZE} bytes.");
+
+ var currentByte = (byte)stream.ReadByte();
+ value |= unchecked((uint) (currentByte & 0x7F) << (count * 7));
+ if ((currentByte & 0x80) == 0)
+ break;
+ count++;
+ }
+
+ return unchecked((int) value);
+ }
+
+ ///
+ /// Reads a VarInt from the given .
+ ///
+ /// A buffer containing the VarInt bytes.
+ /// A variable to store the number of bytes read from the .
+ /// The VarInt value as a 32-bit integer.
+ public static int Read(ReadOnlySpan buffer, out int count)
+ {
+ uint value = 0;
+ count = 0;
+
+ while (true)
+ {
+ if (count == INT_SIZE)
+ throw new OverflowException($"A VarInt cannot exceed {INT_SIZE} bytes.");
+ if (count >= buffer.Length)
+ throw new ArgumentException("Not enough data provided in buffer.", nameof(buffer));
+
+ var currentByte = buffer[count];
+ value |= unchecked((uint) (currentByte & 0x7F) << (count * 7));
+ if ((currentByte & 0x80) == 0)
+ break;
+ count++;
+ }
+
+ return unchecked((int) value);
+ }
+
+ ///
+ /// Writes the given to the as a VarInt.
+ ///
+ /// The object to write to.
+ /// The value to be written.
+ /// The number of bytes that were written to the .
+ /// Thrown when the is too large for a VarInt.
+ public static int Write([NotNull] Stream stream, int value)
+ {
+ Span buffer = stackalloc byte[INT_SIZE];
+ var count = 0;
+
+ var unsigned = unchecked((uint)value);
+ while (true)
+ {
+ if (count == INT_SIZE)
+ throw new OverflowException("Int32 value exceeds the size of a VarInt.");
+
+ var currentByte = (byte) (unsigned & 0x7F);
+ unsigned >>= 7;
+ if (unsigned != 0)
+ currentByte |= 0x80;
+ buffer[count] = currentByte;
+
+ if (unsigned == 0)
+ break;
+ count++;
+ }
+
+ stream.Write(buffer[..count]);
+ return count;
+ }
+
+ ///
+ /// Writes the given to the as a VarInt.
+ ///
+ /// A buffer to write to..
+ /// The value to be written.
+ /// The number of bytes that were written to the .
+ /// Thrown when the is too large for a VarInt.
+ /// Thrown when the is not large enough to contain the data.
+ public static int Write(Span buffer, int value)
+ {
+ var count = 0;
+ var unsigned = unchecked((uint)value);
+ while (true)
+ {
+ if (count == INT_SIZE)
+ throw new OverflowException("Int32 value exceeds the size of a VarInt.");
+ if (count >= buffer.Length)
+ throw new ArgumentException("Buffer is not large enough to contain data.", nameof(buffer));
+
+ var currentByte = (byte) (unsigned & 0x7F);
+ unsigned >>= 7;
+ if (unsigned != 0)
+ currentByte |= 0x80;
+ buffer[count] = currentByte;
+
+ if (unsigned == 0)
+ break;
+ count++;
+ }
+
+ return count;
+ }
+
+ ///
+ /// Writes the given to the as a VarInt.
+ ///
+ /// A buffer to write to..
+ /// The start index of the to begin writing to.
+ /// The maximum number of bytes to write to the .
+ /// The value to be written.
+ /// The number of bytes that were written to the .
+ /// Thrown when the is too large for a VarInt.
+ /// Thrown when the is not large enough to contain the data.
+ public static int Write([NotNull] byte[] buffer, int start, int length, int value) => Write(new Span(buffer, start, length), value);
+ }
+}
\ No newline at end of file