Skip to content

Instantly share code, notes, and snippets.

@ForeverZer0
Last active May 3, 2022 07:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ForeverZer0/91770b6027cf549a39cadd06fd751064 to your computer and use it in GitHub Desktop.
Save ForeverZer0/91770b6027cf549a39cadd06fd751064 to your computer and use it in GitHub Desktop.
Static helper classes for reading/writing Minecraft-compatible VarInt and VarLong values to a Stream or arbitrary array of bytes.
using System.Runtime.CompilerServices;
namespace MyNamespace;
/// <summary>
/// Provides static functions for encoding/decoding variable-length integers represented as a <see cref="int"/>.
/// </summary>
public class VarInt
{
private const int SEGMENT_BITS = 0x7F;
private const int CONTINUE_BIT = 0x80;
/// <summary>
/// The maximum number of bytes that a serialized <see cref="VarInt"/> can encompass.
/// </summary>
public const int MaxSize = 5;
/// <summary>
/// Encodes a <see cref="int"/> as a variable-length integer into the specified <paramref name="buffer"/>.
/// </summary>
/// <param name="value">The value to encode.</param>
/// <param name="buffer">A <see cref="Span{T}"/> to receive the encoded value.</param>
/// <returns>The number of written into the <paramref name="buffer"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static int Encode(int value, Span<byte> buffer)
{
var unsigned = Unsafe.As<int, uint>(ref value);
var pos = 0;
unchecked
{
do
{
var byteVal = unsigned & SEGMENT_BITS;
unsigned >>= 7;
if (unsigned != 0)
byteVal |= CONTINUE_BIT;
buffer[pos++] = (byte) byteVal;
} while (unsigned != 0);
}
return pos;
}
/// <summary>
/// Encodes a <see cref="int"/> as a variable-length integer into the specified <paramref name="buffer"/>.
/// </summary>
/// <param name="value">The value to encode.</param>
/// <param name="buffer">A buffer to receive the encoded value.</param>
/// <param name="start">The offset into the <paramref name="buffer"/> to begin writing into.</param>
/// <param name="length">The maximum number of bytes that can be written to the <paramref name="buffer"/>.</param>
/// <returns>The number of written into the <paramref name="buffer"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static int Encode(int value, byte[] buffer, int start, int length)
{
var span = new Span<byte>(buffer, start, length);
return Encode(value, span);
}
/// <summary>
/// Encodes a <see cref="int"/> as a variable-length integer into the specified <paramref name="stream"/>.
/// </summary>
/// <param name="value">The value to encode.</param>
/// <param name="stream">A <see cref="Stream"/> to receive the encoded value.</param>
/// <returns>The number of written into the <paramref name="stream"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static int Encode(int value, Stream stream)
{
var unsigned = Unsafe.As<int, uint>(ref value);
var pos = 0;
do
{
var byteVal = unsigned & SEGMENT_BITS;
unsigned >>= 7;
if (unsigned != 0)
byteVal |= CONTINUE_BIT;
stream.WriteByte((byte) byteVal);
pos++;
} while (unsigned != 0);
return pos;
}
/// <summary>
/// Decodes a variable-length integer from the specified <paramref name="buffer"/> into a <see cref="int"/> value.
/// </summary>
/// <param name="buffer">A <see cref="ReadOnlySpan{T}"/> containing the encoded value.</param>
/// <param name="size">The number of bytes read from the <paramref name="buffer"/>.</param>
/// <returns>The decoded value as a <see cref="int"/>.</returns>
/// <exception cref="OverflowException">The value exceeds 32-bits.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static int Decode(ReadOnlySpan<byte> buffer, out int size)
{
size = 0;
var value = 0;
var position = 0;
unchecked
{
while (true)
{
var currentByte = buffer[size++];
value |= (currentByte & SEGMENT_BITS) << position;
if ((currentByte & CONTINUE_BIT) == 0)
break;
position += 7;
if (position >= 32)
throw new OverflowException("VarInt exceeds 32-bits.");
}
}
return value;
}
/// <summary>
/// Decodes a variable-length integer from the specified <paramref name="buffer"/> into a <see cref="int"/> value.
/// </summary>
/// <param name="buffer">A buffer containing the encoded value.</param>
/// <param name="start">The index into the <paramref name="buffer"/> to begin reading.</param>
/// <param name="length">The maximum number of bytes that can be read from the <paramref name="buffer"/>.</param>
/// <param name="size">The number of bytes read from the <paramref name="buffer"/>.</param>
/// <returns>The decoded value as a <see cref="int"/>.</returns>
/// <exception cref="OverflowException">The value exceeds 32-bits.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static int Decode(byte[] buffer, int start, int length, out int size)
{
var span = new ReadOnlySpan<byte>(buffer, start, length);
return Decode(span, out size);
}
/// <summary>
/// Decodes a variable-length integer from the specified <paramref name="stream"/> into a <see cref="int"/> value.
/// </summary>
/// <param name="stream">A <see cref="Stream"/> containing the encoded value.</param>
/// <param name="size">The number of bytes read from the <paramref name="stream"/>.</param>
/// <returns>The decoded value as a <see cref="int"/>.</returns>
/// <exception cref="OverflowException">The value exceeds 32-bits.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static int Decode(Stream stream, out int size)
{
size = 0;
var value = 0;
var position = 0;
unchecked
{
while (true)
{
var currentByte = (byte) stream.ReadByte();
size++;
value |= (currentByte & SEGMENT_BITS) << position;
if ((currentByte & CONTINUE_BIT) == 0)
break;
position += 7;
if (position >= 32)
throw new OverflowException("VarInt exceeds 32-bits.");
}
}
return value;
}
/// <summary>
/// Decodes a variable-length integer from the specified <paramref name="buffer"/> into a <see cref="int"/> value.
/// </summary>
/// <param name="buffer">A <see cref="ReadOnlySpan{T}"/> containing the encoded value.</param>
/// <returns>The decoded value as a <see cref="int"/>.</returns>
/// <exception cref="OverflowException">The value exceeds 32-bits.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static int Decode(ReadOnlySpan<byte> buffer)
{
var size = 0;
var value = 0;
var position = 0;
unchecked
{
while (true)
{
var currentByte = buffer[size++];
value |= (currentByte & SEGMENT_BITS) << position;
if ((currentByte & CONTINUE_BIT) == 0)
break;
position += 7;
if (position >= 32)
throw new OverflowException("VarInt exceeds 32-bits.");
}
}
return value;
}
/// <summary>
/// Decodes a variable-length integer from the specified <paramref name="buffer"/> into a <see cref="int"/> value.
/// </summary>
/// <param name="buffer">A buffer containing the encoded value.</param>
/// <param name="start">The index into the <paramref name="buffer"/> to begin reading.</param>
/// <param name="length">The maximum number of bytes that can be read from the <paramref name="buffer"/>.</param>
/// <returns>The decoded value as a <see cref="int"/>.</returns>
/// <exception cref="OverflowException">The value exceeds 32-bits.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static int Decode(byte[] buffer, int start, int length)
{
var span = new ReadOnlySpan<byte>(buffer, start, length);
return Decode(span);
}
/// <summary>
/// Decodes a variable-length integer from the specified <paramref name="stream"/> into a <see cref="int"/> value.
/// </summary>
/// <param name="stream">A <see cref="Stream"/> containing the encoded value.</param>
/// <returns>The decoded value as a <see cref="int"/>.</returns>
/// <exception cref="OverflowException">The value exceeds 32-bits.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static int Decode(Stream stream)
{
var value = 0;
var position = 0;
unchecked
{
while (true)
{
var currentByte = (byte) stream.ReadByte();
value |= (currentByte & SEGMENT_BITS) << position;
if ((currentByte & CONTINUE_BIT) == 0)
break;
position += 7;
if (position >= 32)
throw new OverflowException("VarInt exceeds 32-bits.");
}
}
return value;
}
/// <summary>
/// Encodes a <see cref="int"/> as a variable-length integer into the specified <paramref name="buffer"/>.
/// </summary>
/// <param name="value">The value to encode.</param>
/// <param name="buffer">A <see cref="Span{T}"/> to receive the encoded value.</param>
/// <typeparam name="TEnum32">An <see cref="Enum"/> type that is backed by a 32-bit integer.</typeparam>
/// <returns>The number of written into the <paramref name="buffer"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static int Encode<TEnum32>(TEnum32 value, Span<byte> buffer) where TEnum32 : unmanaged, Enum
{
var unsigned = Unsafe.As<TEnum32, uint>(ref value);
var pos = 0;
unchecked
{
do
{
var byteVal = unsigned & SEGMENT_BITS;
unsigned >>= 7;
if (unsigned != 0)
byteVal |= CONTINUE_BIT;
buffer[pos++] = (byte) byteVal;
} while (unsigned != 0);
}
return pos;
}
/// <summary>
/// Encodes a <see cref="int"/> as a variable-length integer into the specified <paramref name="buffer"/>.
/// </summary>
/// <param name="value">The value to encode.</param>
/// <param name="buffer">A buffer to receive the encoded value.</param>
/// <param name="start">The offset into the <paramref name="buffer"/> to begin writing into.</param>
/// <param name="length">The maximum number of bytes that can be written to the <paramref name="buffer"/>.</param>
/// <typeparam name="TEnum32">An <see cref="Enum"/> type that is backed by a 32-bit integer.</typeparam>
/// <returns>The number of written into the <paramref name="buffer"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static int Encode<TEnum32>(TEnum32 value, byte[] buffer, int start, int length) where TEnum32 : unmanaged, Enum
{
var span = new Span<byte>(buffer, start, length);
return Encode(value, span);
}
/// <summary>
/// Encodes a <see cref="int"/> as a variable-length integer into the specified <paramref name="stream"/>.
/// </summary>
/// <param name="value">The value to encode.</param>
/// <param name="stream">A <see cref="Stream"/> to receive the encoded value.</param>
/// <typeparam name="TEnum32">An <see cref="Enum"/> type that is backed by a 32-bit integer.</typeparam>
/// <returns>The number of written into the <paramref name="stream"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static int Encode<TEnum32>(TEnum32 value, Stream stream) where TEnum32 : unmanaged, Enum
{
var unsigned = Unsafe.As<TEnum32, uint>(ref value);
var pos = 0;
do
{
var byteVal = unsigned & SEGMENT_BITS;
unsigned >>= 7;
if (unsigned != 0)
byteVal |= CONTINUE_BIT;
stream.WriteByte((byte) byteVal);
pos++;
} while (unsigned != 0);
return pos;
}
/// <summary>
/// Decodes a variable-length integer from the specified <paramref name="buffer"/> into a <see cref="Enum"/> value.
/// </summary>
/// <param name="buffer">A <see cref="ReadOnlySpan{T}"/> containing the encoded value.</param>
/// <param name="size">The number of bytes read from the <paramref name="buffer"/>.</param>
/// <typeparam name="TEnum32">An <see cref="Enum"/> type that is backed by a 32-bit integer.</typeparam>
/// <returns>The decoded value as an enumeration type specified by <typeparamref name="TEnum32"/>.</returns>
/// <exception cref="OverflowException">The value exceeds 32-bits.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static TEnum32 Decode<TEnum32>(ReadOnlySpan<byte> buffer, out int size)
{
size = 0;
var value = 0;
var position = 0;
unchecked
{
while (true)
{
var currentByte = buffer[size++];
value |= (currentByte & SEGMENT_BITS) << position;
if ((currentByte & CONTINUE_BIT) == 0)
break;
position += 7;
if (position >= 32)
throw new OverflowException("VarInt exceeds 32-bits.");
}
}
return Unsafe.As<int, TEnum32>(ref value);
}
/// <summary>
/// Decodes a variable-length integer from the specified <paramref name="buffer"/> into a <see cref="Enum"/> value.
/// </summary>
/// <param name="buffer">A buffer containing the encoded value.</param>
/// <param name="start">The index into the <paramref name="buffer"/> to begin reading.</param>
/// <param name="length">The maximum number of bytes that can be read from the <paramref name="buffer"/>.</param>
/// <param name="size">The number of bytes read from the <paramref name="buffer"/>.</param>
/// <typeparam name="TEnum32">An <see cref="Enum"/> type that is backed by a 32-bit integer.</typeparam>
/// <returns>The decoded value as an enumeration type specified by <typeparamref name="TEnum32"/>.</returns>
/// <exception cref="OverflowException">The value exceeds 32-bits.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static TEnum32 Decode<TEnum32>(byte[] buffer, int start, int length, out int size)
{
var span = new ReadOnlySpan<byte>(buffer, start, length);
return Decode<TEnum32>(span, out size);
}
/// <summary>
/// Decodes a variable-length integer from the specified <paramref name="stream"/> into a <see cref="Enum"/> value.
/// </summary>
/// <param name="stream">A <see cref="Stream"/> containing the encoded value.</param>
/// <param name="size">The number of bytes read from the <paramref name="stream"/>.</param>
/// <typeparam name="TEnum32">An <see cref="Enum"/> type that is backed by a 32-bit integer.</typeparam>
/// <returns>The decoded value as an enumeration type specified by <typeparamref name="TEnum32"/>.</returns>
/// <exception cref="OverflowException">The value exceeds 32-bits.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static TEnum32 Decode<TEnum32>(Stream stream, out int size)
{
size = 0;
var value = 0;
var position = 0;
unchecked
{
while (true)
{
var currentByte = (byte) stream.ReadByte();
size++;
value |= (currentByte & SEGMENT_BITS) << position;
if ((currentByte & CONTINUE_BIT) == 0)
break;
position += 7;
if (position >= 32)
throw new OverflowException("VarInt exceeds 32-bits.");
}
}
return Unsafe.As<int, TEnum32>(ref value);
}
/// <summary>
/// Decodes a variable-length integer from the specified <paramref name="buffer"/> into a <see cref="Enum"/> value.
/// </summary>
/// <param name="buffer">A <see cref="ReadOnlySpan{T}"/> containing the encoded value.</param>
/// <typeparam name="TEnum32">An <see cref="Enum"/> type that is backed by a 32-bit integer.</typeparam>
/// <returns>The decoded value as an enumeration type specified by <typeparamref name="TEnum32"/>.</returns>
/// <exception cref="OverflowException">The value exceeds 32-bits.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static TEnum32 Decode<TEnum32>(ReadOnlySpan<byte> buffer)
{
var size = 0;
var value = 0;
var position = 0;
unchecked
{
while (true)
{
var currentByte = buffer[size++];
value |= (currentByte & SEGMENT_BITS) << position;
if ((currentByte & CONTINUE_BIT) == 0)
break;
position += 7;
if (position >= 32)
throw new OverflowException("VarInt exceeds 32-bits.");
}
}
return Unsafe.As<int, TEnum32>(ref value);
}
/// <summary>
/// Decodes a variable-length integer from the specified <paramref name="buffer"/> into a <see cref="Enum"/> value.
/// </summary>
/// <param name="buffer">A buffer containing the encoded value.</param>
/// <param name="start">The index into the <paramref name="buffer"/> to begin reading.</param>
/// <param name="length">The maximum number of bytes that can be read from the <paramref name="buffer"/>.</param>
/// <typeparam name="TEnum32">An <see cref="Enum"/> type that is backed by a 32-bit integer.</typeparam>
/// <returns>The decoded value as an enumeration type specified by <typeparamref name="TEnum32"/>.</returns>
/// <exception cref="OverflowException">The value exceeds 32-bits.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static TEnum32 Decode<TEnum32>(byte[] buffer, int start, int length)
{
var span = new ReadOnlySpan<byte>(buffer, start, length);
return Decode<TEnum32>(span);
}
/// <summary>
/// Decodes a variable-length integer from the specified <paramref name="stream"/> into a <see cref="Enum"/> value.
/// </summary>
/// <param name="stream">A <see cref="Stream"/> containing the encoded value.</param>
/// <typeparam name="TEnum32">An <see cref="Enum"/> type that is backed by a 32-bit integer.</typeparam>
/// <returns>The decoded value as an enumeration type specified by <typeparamref name="TEnum32"/>.</returns>
/// <exception cref="OverflowException">The value exceeds 32-bits.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static TEnum32 Decode<TEnum32>(Stream stream)
{
var value = 0;
var position = 0;
unchecked
{
while (true)
{
var currentByte = (byte) stream.ReadByte();
value |= (currentByte & SEGMENT_BITS) << position;
if ((currentByte & CONTINUE_BIT) == 0)
break;
position += 7;
if (position >= 32)
throw new OverflowException("VarInt exceeds 32-bits.");
}
}
return Unsafe.As<int, TEnum32>(ref value);
}
}
using System.Runtime.CompilerServices;
namespace MyNamespace;
/// <summary>
/// Provides static functions for encoding/decoding variable-length integers represented as a <see cref="long"/>.
/// </summary>
public class VarLong
{
private const uint SEGMENT_BITS = 0x7F;
private const uint CONTINUE_BIT = 0x80;
/// <summary>
/// The maximum number of bytes that a serialized <see cref="VarLong"/> can encompass.
/// </summary>
public const int MaxSize = 10;
/// <summary>
/// Encodes a <see cref="long"/> as a variable-length integer into the specified <paramref name="buffer"/>.
/// </summary>
/// <param name="value">The value to encode.</param>
/// <param name="buffer">A <see cref="Span{T}"/> to receive the encoded value.</param>
/// <returns>The number of written into the <paramref name="buffer"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static int Encode(long value, Span<byte> buffer)
{
var unsigned = Unsafe.As<long, ulong>(ref value);
var pos = 0;
do
{
var byteVal = unsigned & SEGMENT_BITS;
unsigned >>= 7;
if (unsigned != 0)
byteVal |= CONTINUE_BIT;
buffer[pos++] = (byte) byteVal;
} while (unsigned != 0);
return pos;
}
/// <summary>
/// Encodes a <see cref="long"/> as a variable-length integer into the specified <paramref name="buffer"/>.
/// </summary>
/// <param name="value">The value to encode.</param>
/// <param name="buffer">A buffer to receive the encoded value.</param>
/// <param name="start">The offset into the <paramref name="buffer"/> to begin writing into.</param>
/// <param name="length">The maximum number of bytes that can be written to the <paramref name="buffer"/>.</param>
/// <returns>The number of written into the <paramref name="buffer"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static int Encode(long value, byte[] buffer, int start, int length)
{
var span = new Span<byte>(buffer, start, length);
return Encode(value, span);
}
/// <summary>
/// Encodes a <see cref="long"/> as a variable-length integer into the specified <paramref name="stream"/>.
/// </summary>
/// <param name="value">The value to encode.</param>
/// <param name="stream">A <see cref="Stream"/> to receive the encoded value.</param>
/// <returns>The number of written into the <paramref name="stream"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static int Encode(long value, Stream stream)
{
var unsigned = Unsafe.As<long, ulong>(ref value);
var pos = 0;
do
{
var byteVal = unsigned & SEGMENT_BITS;
unsigned >>= 7;
if (unsigned != 0)
byteVal |= CONTINUE_BIT;
stream.WriteByte((byte) byteVal);
pos++;
} while (unsigned != 0);
return pos;
}
/// <summary>
/// Decodes a variable-length integer from the specified <paramref name="buffer"/> into a <see cref="long"/> value.
/// </summary>
/// <param name="buffer">A <see cref="ReadOnlySpan{T}"/> containing the encoded value.</param>
/// <param name="size">The number of bytes read from the <paramref name="buffer"/>.</param>
/// <returns>The decoded value as a <see cref="long"/>.</returns>
/// <exception cref="OverflowException">The value exceeds 64-bits.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static long Decode(ReadOnlySpan<byte> buffer, out int size)
{
size = 0;
var value = 0L;
var position = 0;
unchecked
{
while (true)
{
var currentByte = buffer[size++];
value |= (currentByte & SEGMENT_BITS) << position;
if ((currentByte & CONTINUE_BIT) == 0)
break;
position += 7;
if (position >= 64)
throw new OverflowException("VarLong exceeds 64-bits.");
}
}
return value;
}
/// <summary>
/// Decodes a variable-length integer from the specified <paramref name="buffer"/> into a <see cref="long"/> value.
/// </summary>
/// <param name="buffer">A buffer containing the encoded value.</param>
/// <param name="start">The index into the <paramref name="buffer"/> to begin reading.</param>
/// <param name="length">The maximum number of bytes that can be read from the <paramref name="buffer"/>.</param>
/// <param name="size">The number of bytes read from the <paramref name="buffer"/>.</param>
/// <returns>The decoded value as a <see cref="long"/>.</returns>
/// <exception cref="OverflowException">The value exceeds 64-bits.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static long Decode(byte[] buffer, int start, int length, out int size)
{
var span = new ReadOnlySpan<byte>(buffer, start, length);
return Decode(span, out size);
}
/// <summary>
/// Decodes a variable-length integer from the specified <paramref name="stream"/> into a <see cref="long"/> value.
/// </summary>
/// <param name="stream">A <see cref="Stream"/> containing the encoded value.</param>
/// <param name="size">The number of bytes read from the <paramref name="stream"/>.</param>
/// <returns>The decoded value as a <see cref="long"/>.</returns>
/// <exception cref="OverflowException">The value exceeds 64-bits.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static long Decode(Stream stream, out int size)
{
size = 0;
var value = 0L;
var position = 0;
unchecked
{
while (true)
{
var currentByte = (byte) stream.ReadByte();
size++;
value |= (currentByte & SEGMENT_BITS) << position;
if ((currentByte & CONTINUE_BIT) == 0)
break;
position += 7;
if (position >= 64)
throw new OverflowException("VarLong exceeds 64-bits.");
}
}
return value;
}
/// <summary>
/// Decodes a variable-length integer from the specified <paramref name="buffer"/> into a <see cref="long"/> value.
/// </summary>
/// <param name="buffer">A <see cref="ReadOnlySpan{T}"/> containing the encoded value.</param>
/// <returns>The decoded value as a <see cref="long"/>.</returns>
/// <exception cref="OverflowException">The value exceeds 64-bits.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static long Decode(ReadOnlySpan<byte> buffer)
{
var size = 0;
var value = 0L;
var position = 0;
unchecked
{
while (true)
{
var currentByte = buffer[size++];
value |= (currentByte & SEGMENT_BITS) << position;
if ((currentByte & CONTINUE_BIT) == 0)
break;
position += 7;
if (position >= 64)
throw new OverflowException("VarLong exceeds 64-bits.");
}
}
return value;
}
/// <summary>
/// Decodes a variable-length integer from the specified <paramref name="buffer"/> into a <see cref="long"/> value.
/// </summary>
/// <param name="buffer">A buffer containing the encoded value.</param>
/// <param name="start">The index into the <paramref name="buffer"/> to begin reading.</param>
/// <param name="length">The maximum number of bytes that can be read from the <paramref name="buffer"/>.</param>
/// <returns>The decoded value as a <see cref="long"/>.</returns>
/// <exception cref="OverflowException">The value exceeds 64-bits.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static long Decode(byte[] buffer, int start, int length)
{
var span = new ReadOnlySpan<byte>(buffer, start, length);
return Decode(span);
}
/// <summary>
/// Decodes a variable-length integer from the specified <paramref name="stream"/> into a <see cref="long"/> value.
/// </summary>
/// <param name="stream">A <see cref="Stream"/> containing the encoded value.</param>
/// <returns>The decoded value as a <see cref="long"/>.</returns>
/// <exception cref="OverflowException">The value exceeds 64-bits.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static long Decode(Stream stream)
{
var value = 0L;
var position = 0;
unchecked
{
while (true)
{
var currentByte = (byte) stream.ReadByte();
value |= (currentByte & SEGMENT_BITS) << position;
if ((currentByte & CONTINUE_BIT) == 0)
break;
position += 7;
if (position >= 64)
throw new OverflowException("VarLong exceeds 64-bits.");
}
}
return value;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment