// Copyright Epic Games, Inc. All Rights Reserved. using System; using System.Numerics; namespace EpicGames.Core { /// /// Methods for reading VarUInt values /// public static class VarInt { /// /// Read a variable-length unsigned integer. /// /// A variable-length encoding of an unsigned integer /// [Obsolete("Call ReadSigned or ReadUnsigned instead")] public static ulong Read(ReadOnlySpan buffer) => ReadUnsigned(buffer, out _); /// /// Read a variable-length unsigned integer. /// /// A variable-length encoding of an unsigned integer /// The number of bytes consumed from the input /// [Obsolete("Call ReadSigned or ReadUnsigned instead")] public static ulong Read(ReadOnlySpan buffer, out int bytesRead) => ReadUnsigned(buffer, out bytesRead); /// /// Read a variable-length signed integer. /// /// A variable-length encoding of an unsigned integer /// public static long ReadSigned(ReadOnlySpan buffer) => ReadSigned(buffer, out _); /// /// Read a variable-length signed integer. /// /// A variable-length encoding of an unsigned integer /// The number of bytes consumed from the input /// public static long ReadSigned(ReadOnlySpan buffer, out int bytesRead) { ulong unsignedValue = ReadUnsigned(buffer, out bytesRead); return DecodeSigned(unsignedValue); } /// /// Read a variable-length unsigned integer. /// /// A variable-length encoding of an unsigned integer /// public static ulong ReadUnsigned(ReadOnlySpan buffer) => ReadUnsigned(buffer, out _); /// /// Read a variable-length unsigned integer. /// /// A variable-length encoding of an unsigned integer /// The number of bytes consumed from the input /// public static ulong ReadUnsigned(ReadOnlySpan buffer, out int bytesRead) { bytesRead = (int)Measure(buffer); return ReadUnsignedKnownSize(buffer, bytesRead); } /// /// Read a variable-length unsigned integer. /// /// A variable-length encoding of an unsigned integer /// The number of bytes consumed from the input /// internal static ulong ReadUnsignedKnownSize(ReadOnlySpan buffer, int numBytes) { ulong value = (ulong)(buffer[0] & (0xff >> numBytes)); for (int i = 1; i < numBytes; i++) { value <<= 8; value |= buffer[i]; } return value; } /// /// Measure the length in bytes (1-9) of an encoded variable-length integer. /// /// A variable-length encoding of an(signed or unsigned) integer. /// The number of bytes used to encode the integer, in the range 1-9. public static int Measure(ReadOnlySpan buffer) { byte b = buffer[0]; b = (byte)~b; return BitOperations.LeadingZeroCount(b) - 23; } /// /// Measure the length in bytes (1-9) of an encoded variable-length integer. /// /// First byte of the encoded integer. /// The number of bytes used to encode the integer, in the range 1-9. public static int Measure(byte b) { b = (byte)~b; return BitOperations.LeadingZeroCount(b) - 23; } /// /// Measure the number of bytes required to encode the input. /// /// /// [Obsolete("Use MeasureSigned or MeasureUnsigned instead")] public static int Measure(uint value) => MeasureUnsigned(value); /// /// Measure the number of bytes required to encode the input. /// /// /// [Obsolete("Use MeasureSigned or MeasureUnsigned instead")] public static int Measure(ulong value) => MeasureUnsigned(value); /// /// Measure the number of bytes (1-9) required to encode the input. /// /// /// public static int MeasureSigned(long value) { return MeasureUnsigned(EncodeSigned(value)); } /// /// Measure the number of bytes (1-5) required to encode the 32-bit input. /// /// /// public static int MeasureUnsigned(int value) { return MeasureUnsigned((ulong)(long)value); } /// /// Measure the number of bytes (1-5) required to encode the 32-bit input. /// /// /// public static int MeasureUnsigned(uint value) { return BitOperations.Log2(value) / 7 + 1; } /// /// Measure the number of bytes (1-9) required to encode the 64-bit input. /// /// /// public static int MeasureUnsigned(ulong value) { return Math.Min(BitOperations.Log2(value) / 7 + 1, 9); } /// /// Write a variable-length unsigned integer. /// /// An unsigned integer to encode /// A buffer of at least 9 bytes to write the output to. /// The number of bytes used in the output [Obsolete("Use WriteUnsigned or WriteSigned instead")] public static int Write(Span buffer, long value) => WriteUnsigned(buffer, (ulong)value); /// /// Write a variable-length unsigned integer. /// /// An unsigned integer to encode /// A buffer of at least 9 bytes to write the output to. /// The number of bytes used in the output [Obsolete("Use WriteUnsigned or WriteSigned instead")] public static int Write(Span buffer, ulong value) => WriteUnsigned(buffer, value); /// /// Write a variable-length unsigned integer. /// /// An unsigned integer to encode /// A buffer of at least 9 bytes to write the output to. /// The number of bytes used in the output public static int WriteSigned(Span buffer, long value) { ulong unsignedValue = EncodeSigned(value); return WriteUnsigned(buffer, unsignedValue); } /// /// Write a variable-length unsigned integer. /// /// An unsigned integer to encode /// A buffer of at least 9 bytes to write the output to. /// The number of bytes used in the output public static int WriteUnsigned(Span buffer, int value) => WriteUnsigned(buffer, (ulong)value); /// /// Write a variable-length unsigned integer. /// /// An unsigned integer to encode /// A buffer of at least 9 bytes to write the output to. /// The number of bytes used in the output public static int WriteUnsigned(Span buffer, long value) => WriteUnsigned(buffer, (ulong)value); /// /// Write a variable-length unsigned integer. /// /// An unsigned integer to encode /// A buffer of at least 9 bytes to write the output to. /// The number of bytes used in the output public static int WriteUnsigned(Span buffer, ulong value) { int byteCount = MeasureUnsigned(value); for (int idx = 1; idx < byteCount; idx++) { buffer[byteCount - idx] = (byte)value; value >>= 8; } buffer[0] = (byte)((0xff << (9 - (int)byteCount)) | (byte)value); return byteCount; } /// /// Decode a signed VarInt value from an unsigned value /// /// Value to decode /// Decoded value public static long DecodeSigned(ulong value) { return -(long)(value & 1) ^ (long)(value >> 1); } /// /// Encode a signed VarInt value into an unsigned value /// /// Value to encode /// Encoded value public static ulong EncodeSigned(long value) { return (ulong)((value >> 63) ^ (value << 1)); } } /// /// Extension methods for writing VarInt values /// public static class VarIntExtensions { /// /// Read an unsigned VarInt from the given reader /// /// Reader to deserialize from /// The deserialized value public static long ReadSignedVarInt(this IMemoryReader reader) { ulong value = ReadUnsignedVarInt(reader); return VarInt.DecodeSigned(value); } /// /// Read an unsigned VarInt from the given reader /// /// Reader to deserialize from /// The deserialized value public static ulong ReadUnsignedVarInt(this IMemoryReader reader) { int length = VarInt.Measure(reader.GetSpan(1)); ReadOnlySpan span = reader.GetSpan(length); reader.Advance(length); return VarInt.ReadUnsignedKnownSize(span, length); } /// /// Writes a signed VarInt to a byte array /// /// Writer to serialize to /// Value to write public static void WriteSignedVarInt(this IMemoryWriter writer, int value) { WriteUnsignedVarInt(writer, VarInt.EncodeSigned(value)); } /// /// Writes a signed VarInt to a byte array /// /// Writer to serialize to /// Value to write public static void WriteSignedVarInt(this IMemoryWriter writer, long value) { WriteUnsignedVarInt(writer, VarInt.EncodeSigned(value)); } /// /// Writes a unsigned VarInt to a byte array /// /// Writer to serialize to /// Value to write public static void WriteUnsignedVarInt(this IMemoryWriter writer, int value) => WriteUnsignedVarInt(writer, (ulong)value); /// /// Writes a unsigned VarInt to a byte array /// /// Writer to serialize to /// Value to write public static void WriteUnsignedVarInt(this IMemoryWriter writer, ulong value) { int length = VarInt.MeasureUnsigned(value); VarInt.WriteUnsigned(writer.GetSpan(length), value); writer.Advance(length); } } }