save
This commit is contained in:
@@ -1,150 +0,0 @@
|
||||
// This file is provided under The MIT License as part of RiptideNetworking.
|
||||
// Copyright (c) Tom Weiland
|
||||
// For additional information please see the included LICENSE.md file or view it on GitHub:
|
||||
// https://github.com/RiptideNetworking/Riptide/blob/main/LICENSE.md
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Riptide.Utils
|
||||
{
|
||||
/// <summary>Provides functionality for managing and manipulating a collection of bits.</summary>
|
||||
internal class Bitfield
|
||||
{
|
||||
/// <summary>The first 8 bits stored in the bitfield.</summary>
|
||||
internal byte First8 => (byte)segments[0];
|
||||
/// <summary>The first 16 bits stored in the bitfield.</summary>
|
||||
internal ushort First16 => (ushort)segments[0];
|
||||
|
||||
/// <summary>The number of bits which fit into a single segment.</summary>
|
||||
private const int SegmentSize = sizeof(uint) * 8;
|
||||
/// <summary>The segments of the bitfield.</summary>
|
||||
private readonly List<uint> segments;
|
||||
/// <summary>Whether or not the bitfield's capacity should dynamically adjust when shifting.</summary>
|
||||
private readonly bool isDynamicCapacity;
|
||||
/// <summary>The current number of bits being stored.</summary>
|
||||
private int count;
|
||||
/// <summary>The current capacity.</summary>
|
||||
private int capacity;
|
||||
|
||||
/// <summary>Creates a bitfield.</summary>
|
||||
/// <param name="isDynamicCapacity">Whether or not the bitfield's capacity should dynamically adjust when shifting.</param>
|
||||
internal Bitfield(bool isDynamicCapacity = true)
|
||||
{
|
||||
segments = new List<uint>(4) { 0 };
|
||||
capacity = segments.Count * SegmentSize;
|
||||
this.isDynamicCapacity = isDynamicCapacity;
|
||||
}
|
||||
|
||||
/// <summary>Checks if the bitfield has capacity for the given number of bits.</summary>
|
||||
/// <param name="amount">The number of bits for which to check if there is capacity.</param>
|
||||
/// <param name="overflow">The number of bits from <paramref name="amount"/> which there is no capacity for.</param>
|
||||
/// <returns>Whether or not there is sufficient capacity.</returns>
|
||||
internal bool HasCapacityFor(int amount, out int overflow)
|
||||
{
|
||||
overflow = count + amount - capacity;
|
||||
return overflow < 0;
|
||||
}
|
||||
|
||||
/// <summary>Shifts the bitfield by the given amount.</summary>
|
||||
/// <param name="amount">How much to shift by.</param>
|
||||
internal void ShiftBy(int amount)
|
||||
{
|
||||
int segmentShift = amount / SegmentSize; // How many WHOLE segments we have to shift by
|
||||
int bitShift = amount % SegmentSize; // How many bits we have to shift by
|
||||
|
||||
if (!isDynamicCapacity)
|
||||
count = Math.Min(count + amount, SegmentSize);
|
||||
else if (!HasCapacityFor(amount, out int _))
|
||||
{
|
||||
Trim();
|
||||
count += amount;
|
||||
|
||||
if (count > capacity)
|
||||
{
|
||||
int increaseBy = segmentShift + 1;
|
||||
for (int i = 0; i < increaseBy; i++)
|
||||
segments.Add(0);
|
||||
|
||||
capacity = segments.Count * SegmentSize;
|
||||
}
|
||||
}
|
||||
else
|
||||
count += amount;
|
||||
|
||||
int s = segments.Count - 1;
|
||||
segments[s] <<= bitShift;
|
||||
s -= 1 + segmentShift;
|
||||
while (s > -1)
|
||||
{
|
||||
ulong shiftedBits = (ulong)segments[s] << bitShift;
|
||||
segments[s] = (uint)shiftedBits;
|
||||
|
||||
segments[s + 1 + segmentShift] |= (uint)(shiftedBits >> SegmentSize);
|
||||
s--;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Checks the last bit in the bitfield, and trims it if it is set to 1.</summary>
|
||||
/// <param name="checkedPosition">The checked bit's position in the bitfield.</param>
|
||||
/// <returns>Whether or not the checked bit was set.</returns>
|
||||
internal bool CheckAndTrimLast(out int checkedPosition)
|
||||
{
|
||||
checkedPosition = count;
|
||||
uint bitToCheck = (uint)(1 << ((count - 1) % SegmentSize));
|
||||
bool isSet = (segments[segments.Count - 1] & bitToCheck) != 0;
|
||||
count--;
|
||||
return isSet;
|
||||
}
|
||||
|
||||
/// <summary>Trims all bits from the end of the bitfield until an unset bit is encountered.</summary>
|
||||
private void Trim()
|
||||
{
|
||||
while (count > 0 && IsSet(count))
|
||||
count--;
|
||||
}
|
||||
|
||||
/// <summary>Sets the given bit to 1.</summary>
|
||||
/// <param name="bit">The bit to set.</param>
|
||||
/// <exception cref="ArgumentOutOfRangeException"><paramref name="bit"/> is less than 1.</exception>
|
||||
internal void Set(int bit)
|
||||
{
|
||||
if (bit < 1)
|
||||
throw new ArgumentOutOfRangeException(nameof(bit), $"'{nameof(bit)}' must be greater than zero!");
|
||||
|
||||
bit--;
|
||||
int s = bit / SegmentSize;
|
||||
uint bitToSet = (uint)(1 << (bit % SegmentSize));
|
||||
if (s < segments.Count)
|
||||
segments[s] |= bitToSet;
|
||||
}
|
||||
|
||||
/// <summary>Checks if the given bit is set to 1.</summary>
|
||||
/// <param name="bit">The bit to check.</param>
|
||||
/// <returns>Whether or not the bit is set.</returns>
|
||||
/// <exception cref="ArgumentOutOfRangeException"><paramref name="bit"/> is less than 1.</exception>
|
||||
internal bool IsSet(int bit)
|
||||
{
|
||||
if (bit > count)
|
||||
return true;
|
||||
|
||||
if (bit < 1)
|
||||
throw new ArgumentOutOfRangeException(nameof(bit), $"'{nameof(bit)}' must be greater than zero!");
|
||||
|
||||
bit--;
|
||||
int s = bit / SegmentSize;
|
||||
uint bitToCheck = (uint)(1 << (bit % SegmentSize));
|
||||
if (s < segments.Count)
|
||||
return (segments[s] & bitToCheck) != 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>Combines this bitfield with the given bits.</summary>
|
||||
/// <param name="other">The bits to OR into the bitfield.</param>
|
||||
internal void Combine(ushort other)
|
||||
{
|
||||
segments[0] |= other;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,202 +0,0 @@
|
||||
// This file is provided under The MIT License as part of RiptideNetworking.
|
||||
// Copyright (c) Tom Weiland
|
||||
// For additional information please see the included LICENSE.md file or view it on GitHub:
|
||||
// https://github.com/RiptideNetworking/Riptide/blob/main/LICENSE.md
|
||||
|
||||
namespace Riptide.Utils
|
||||
{
|
||||
/// <summary>Tracks and manages various metrics of a <see cref="Connection"/>.</summary>
|
||||
public class ConnectionMetrics
|
||||
{
|
||||
/// <summary>The total number of bytes received across all send modes since the last <see cref="Reset"/> call, including those in duplicate and, in
|
||||
/// the case of notify messages, out-of-order packets. Does <i>not</i> include packet header bytes, which may vary by transport.</summary>
|
||||
public int BytesIn => UnreliableBytesIn + NotifyBytesIn + ReliableBytesIn;
|
||||
/// <summary>The total number of bytes sent across all send modes since the last <see cref="Reset"/> call, including those in automatic resends.
|
||||
/// Does <i>not</i> include packet header bytes, which may vary by transport.</summary>
|
||||
public int BytesOut => UnreliableBytesOut + NotifyBytesOut + ReliableBytesOut;
|
||||
/// <summary>The total number of messages received across all send modes since the last <see cref="Reset"/> call, including duplicate and out-of-order notify messages.</summary>
|
||||
public int MessagesIn => UnreliableIn + NotifyIn + ReliableIn;
|
||||
/// <summary>The total number of messages sent across all send modes since the last <see cref="Reset"/> call, including automatic resends.</summary>
|
||||
public int MessagesOut => UnreliableOut + NotifyOut + ReliableOut;
|
||||
|
||||
/// <summary>The total number of bytes received in unreliable messages since the last <see cref="Reset"/> call. Does <i>not</i> include packet header bytes, which may vary by transport.</summary>
|
||||
public int UnreliableBytesIn { get; private set; }
|
||||
/// <summary>The total number of bytes sent in unreliable messages since the last <see cref="Reset"/> call. Does <i>not</i> include packet header bytes, which may vary by transport.</summary>
|
||||
public int UnreliableBytesOut { get; internal set; }
|
||||
/// <summary>The number of unreliable messages received since the last <see cref="Reset"/> call.</summary>
|
||||
public int UnreliableIn { get; private set; }
|
||||
/// <summary>The number of unreliable messages sent since the last <see cref="Reset"/> call.</summary>
|
||||
public int UnreliableOut { get; internal set; }
|
||||
|
||||
/// <summary>The total number of bytes received in notify messages since the last <see cref="Reset"/> call, including those in duplicate and out-of-order packets.
|
||||
/// Does <i>not</i> include packet header bytes, which may vary by transport.</summary>
|
||||
public int NotifyBytesIn { get; private set; }
|
||||
/// <summary>The total number of bytes sent in notify messages since the last <see cref="Reset"/> call. Does <i>not</i> include packet header bytes, which may vary by transport.</summary>
|
||||
public int NotifyBytesOut { get; internal set; }
|
||||
/// <summary>The number of notify messages received since the last <see cref="Reset"/> call, including duplicate and out-of-order ones.</summary>
|
||||
public int NotifyIn { get; private set; }
|
||||
/// <summary>The number of notify messages sent since the last <see cref="Reset"/> call.</summary>
|
||||
public int NotifyOut { get; internal set; }
|
||||
/// <summary>The number of duplicate or out-of-order notify messages which were received, but discarded (not handled) since the last <see cref="Reset"/> call.</summary>
|
||||
public int NotifyDiscarded { get; internal set; }
|
||||
/// <summary>The number of notify messages lost since the last <see cref="Reset"/> call.</summary>
|
||||
public int NotifyLost { get; private set; }
|
||||
/// <summary>The number of notify messages delivered since the last <see cref="Reset"/> call.</summary>
|
||||
public int NotifyDelivered { get; private set; }
|
||||
/// <summary>The number of notify messages lost of the last 64 notify messages to be lost or delivered.</summary>
|
||||
public int RollingNotifyLost { get; private set; }
|
||||
/// <summary>The number of notify messages delivered of the last 64 notify messages to be lost or delivered.</summary>
|
||||
public int RollingNotifyDelivered { get; private set; }
|
||||
/// <summary>The loss rate (0-1) among the last 64 notify messages.</summary>
|
||||
public float RollingNotifyLossRate => RollingNotifyLost / 64f;
|
||||
|
||||
/// <summary>The total number of bytes received in reliable messages since the last <see cref="Reset"/> call, including those in duplicate packets.
|
||||
/// Does <i>not</i> include packet header bytes, which may vary by transport.</summary>
|
||||
public int ReliableBytesIn { get; private set; }
|
||||
/// <summary>The total number of bytes sent in reliable messages since the last <see cref="Reset"/> call, including those in automatic resends.
|
||||
/// Does <i>not</i> include packet header bytes, which may vary by transport.</summary>
|
||||
public int ReliableBytesOut { get; internal set; }
|
||||
/// <summary>The number of reliable messages received since the last <see cref="Reset"/> call, including duplicates.</summary>
|
||||
public int ReliableIn { get; private set; }
|
||||
/// <summary>The number of reliable messages sent since the last <see cref="Reset"/> call, including automatic resends (each resend adds to this value).</summary>
|
||||
public int ReliableOut { get; internal set; }
|
||||
/// <summary>The number of duplicate reliable messages which were received, but discarded (and not handled) since the last <see cref="Reset"/> call.</summary>
|
||||
public int ReliableDiscarded { get; internal set; }
|
||||
/// <summary>The number of unique reliable messages sent since the last <see cref="Reset"/> call.
|
||||
/// A message only counts towards this the first time it is sent—subsequent resends are not counted.</summary>
|
||||
public int ReliableUniques { get; internal set; }
|
||||
/// <summary>The number of send attempts that were required to deliver recent reliable messages.</summary>
|
||||
public readonly RollingStat RollingReliableSends;
|
||||
|
||||
/// <summary>The left-most bit of a <see cref="ulong"/>, used to store the oldest value in the <see cref="notifyLossTracker"/>.</summary>
|
||||
private const ulong ULongLeftBit = 1ul << 63;
|
||||
/// <summary>Which recent notify messages were lost. Each bit corresponds to a message.</summary>
|
||||
private ulong notifyLossTracker;
|
||||
/// <summary>How many of the <see cref="notifyLossTracker"/>'s bits are in use.</summary>
|
||||
private int notifyBufferCount;
|
||||
|
||||
/// <summary>Initializes metrics.</summary>
|
||||
public ConnectionMetrics()
|
||||
{
|
||||
Reset();
|
||||
RollingNotifyDelivered = 0;
|
||||
RollingNotifyLost = 0;
|
||||
notifyLossTracker = 0;
|
||||
notifyBufferCount = 0;
|
||||
RollingReliableSends = new RollingStat(64);
|
||||
}
|
||||
|
||||
/// <summary>Resets all non-rolling metrics to 0.</summary>
|
||||
public void Reset()
|
||||
{
|
||||
UnreliableBytesIn = 0;
|
||||
UnreliableBytesOut = 0;
|
||||
UnreliableIn = 0;
|
||||
UnreliableOut = 0;
|
||||
|
||||
NotifyBytesIn = 0;
|
||||
NotifyBytesOut = 0;
|
||||
NotifyIn = 0;
|
||||
NotifyOut = 0;
|
||||
NotifyDiscarded = 0;
|
||||
NotifyLost = 0;
|
||||
NotifyDelivered = 0;
|
||||
|
||||
ReliableBytesIn = 0;
|
||||
ReliableBytesOut = 0;
|
||||
ReliableIn = 0;
|
||||
ReliableOut = 0;
|
||||
ReliableDiscarded = 0;
|
||||
ReliableUniques = 0;
|
||||
}
|
||||
|
||||
/// <summary>Updates the metrics associated with receiving an unreliable message.</summary>
|
||||
/// <param name="byteCount">The number of bytes that were received.</param>
|
||||
internal void ReceivedUnreliable(int byteCount)
|
||||
{
|
||||
UnreliableBytesIn += byteCount;
|
||||
UnreliableIn++;
|
||||
}
|
||||
|
||||
/// <summary>Updates the metrics associated with sending an unreliable message.</summary>
|
||||
/// <param name="byteCount">The number of bytes that were sent.</param>
|
||||
internal void SentUnreliable(int byteCount)
|
||||
{
|
||||
UnreliableBytesOut += byteCount;
|
||||
UnreliableOut++;
|
||||
}
|
||||
|
||||
/// <summary>Updates the metrics associated with receiving a notify message.</summary>
|
||||
/// <param name="byteCount">The number of bytes that were received.</param>
|
||||
internal void ReceivedNotify(int byteCount)
|
||||
{
|
||||
NotifyBytesIn += byteCount;
|
||||
NotifyIn++;
|
||||
}
|
||||
|
||||
/// <summary>Updates the metrics associated with sending a notify message.</summary>
|
||||
/// <param name="byteCount">The number of bytes that were sent.</param>
|
||||
internal void SentNotify(int byteCount)
|
||||
{
|
||||
NotifyBytesOut += byteCount;
|
||||
NotifyOut++;
|
||||
}
|
||||
|
||||
/// <summary>Updates the metrics associated with delivering a notify message.</summary>
|
||||
internal void DeliveredNotify()
|
||||
{
|
||||
NotifyDelivered++;
|
||||
|
||||
if (notifyBufferCount < 64)
|
||||
{
|
||||
RollingNotifyDelivered++;
|
||||
notifyBufferCount++;
|
||||
}
|
||||
else if ((notifyLossTracker & ULongLeftBit) == 0)
|
||||
{
|
||||
// The one being removed from the buffer was not delivered
|
||||
RollingNotifyDelivered++;
|
||||
RollingNotifyLost--;
|
||||
}
|
||||
|
||||
notifyLossTracker <<= 1;
|
||||
notifyLossTracker |= 1;
|
||||
}
|
||||
|
||||
/// <summary>Updates the metrics associated with losing a notify message.</summary>
|
||||
internal void LostNotify()
|
||||
{
|
||||
NotifyLost++;
|
||||
|
||||
if (notifyBufferCount < 64)
|
||||
{
|
||||
RollingNotifyLost++;
|
||||
notifyBufferCount++;
|
||||
}
|
||||
else if ((notifyLossTracker & ULongLeftBit) != 0)
|
||||
{
|
||||
// The one being removed from the buffer was delivered
|
||||
RollingNotifyDelivered--;
|
||||
RollingNotifyLost++;
|
||||
}
|
||||
|
||||
notifyLossTracker <<= 1;
|
||||
}
|
||||
|
||||
/// <summary>Updates the metrics associated with receiving a reliable message.</summary>
|
||||
/// <param name="byteCount">The number of bytes that were received.</param>
|
||||
internal void ReceivedReliable(int byteCount)
|
||||
{
|
||||
ReliableBytesIn += byteCount;
|
||||
ReliableIn++;
|
||||
}
|
||||
|
||||
/// <summary>Updates the metrics associated with sending a reliable message.</summary>
|
||||
/// <param name="byteCount">The number of bytes that were sent.</param>
|
||||
internal void SentReliable(int byteCount)
|
||||
{
|
||||
ReliableBytesOut += byteCount;
|
||||
ReliableOut++;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,954 +0,0 @@
|
||||
// This file is provided under The MIT License as part of RiptideNetworking.
|
||||
// Copyright (c) Tom Weiland
|
||||
// For additional information please see the included LICENSE.md file or view it on GitHub:
|
||||
// https://github.com/RiptideNetworking/Riptide/blob/main/LICENSE.md
|
||||
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Riptide.Utils
|
||||
{
|
||||
/// <summary>Provides functionality for converting bits and bytes to various value types and vice versa.</summary>
|
||||
public class Converter
|
||||
{
|
||||
/// <summary>The number of bits in a byte.</summary>
|
||||
public const int BitsPerByte = 8;
|
||||
/// <summary>The number of bits in a ulong.</summary>
|
||||
public const int BitsPerULong = sizeof(ulong) * BitsPerByte;
|
||||
|
||||
#region Zig Zag Encoding
|
||||
/// <summary>Zig zag encodes <paramref name="value"/>.</summary>
|
||||
/// <param name="value">The value to encode.</param>
|
||||
/// <returns>The zig zag-encoded value.</returns>
|
||||
/// <remarks>Zig zag encoding allows small negative numbers to be represented as small positive numbers. All positive numbers are doubled and become even numbers,
|
||||
/// while all negative numbers become positive odd numbers. In contrast, simply casting a negative value to its unsigned counterpart would result in a large positive
|
||||
/// number which uses the high bit, rendering compression via <see cref="Message.AddVarULong(ulong)"/> and <see cref="Message.GetVarULong"/> ineffective.</remarks>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int ZigZagEncode(int value)
|
||||
{
|
||||
return (value >> 31) ^ (value << 1);
|
||||
}
|
||||
/// <inheritdoc cref="ZigZagEncode(int)"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static long ZigZagEncode(long value)
|
||||
{
|
||||
return (value >> 63) ^ (value << 1);
|
||||
}
|
||||
|
||||
/// <summary>Zig zag decodes <paramref name="value"/>.</summary>
|
||||
/// <param name="value">The value to decode.</param>
|
||||
/// <returns>The zig zag-decoded value.</returns>
|
||||
/// <inheritdoc cref="ZigZagEncode(int)"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int ZigZagDecode(int value)
|
||||
{
|
||||
return (value >> 1) ^ -(value & 1);
|
||||
}
|
||||
/// <inheritdoc cref="ZigZagDecode(int)"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static long ZigZagDecode(long value)
|
||||
{
|
||||
return (value >> 1) ^ -(value & 1);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Bits
|
||||
/// <summary>Takes <paramref name="amount"/> bits from <paramref name="bitfield"/> and writes them into <paramref name="array"/>, starting at <paramref name="startBit"/>.</summary>
|
||||
/// <param name="bitfield">The bitfield from which to write the bits into the array.</param>
|
||||
/// <param name="amount">The number of bits to write.</param>
|
||||
/// <param name="array">The array to write the bits into.</param>
|
||||
/// <param name="startBit">The bit position in the array at which to start writing.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void SetBits(byte bitfield, int amount, byte[] array, int startBit)
|
||||
{
|
||||
byte mask = (byte)((1 << amount) - 1);
|
||||
bitfield &= mask; // Discard any bits that are set beyond the ones we're setting
|
||||
int inverseMask = ~mask;
|
||||
int pos = startBit / BitsPerByte;
|
||||
int bit = startBit % BitsPerByte;
|
||||
if (bit == 0)
|
||||
array[pos] = (byte)(bitfield | (array[pos] & inverseMask));
|
||||
else
|
||||
{
|
||||
array[pos ] = (byte)((bitfield << bit) | (array[pos] & ~(mask << bit)));
|
||||
array[pos + 1] = (byte)((bitfield >> (8 - bit)) | (array[pos + 1] & (inverseMask >> (8 - bit))));
|
||||
}
|
||||
}
|
||||
/// <inheritdoc cref="SetBits(byte, int, byte[], int)"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void SetBits(ushort bitfield, int amount, byte[] array, int startBit)
|
||||
{
|
||||
ushort mask = (ushort)((1 << amount) - 1);
|
||||
bitfield &= mask; // Discard any bits that are set beyond the ones we're setting
|
||||
int inverseMask = ~mask;
|
||||
int pos = startBit / BitsPerByte;
|
||||
int bit = startBit % BitsPerByte;
|
||||
if (bit == 0)
|
||||
{
|
||||
array[pos ] = (byte)(bitfield | (array[pos] & inverseMask));
|
||||
array[pos + 1] = (byte)((bitfield >> 8) | (array[pos + 1] & (inverseMask >> 8)));
|
||||
}
|
||||
else
|
||||
{
|
||||
array[pos ] = (byte)((bitfield << bit) | (array[pos] & ~(mask << bit)));
|
||||
bitfield >>= 8 - bit;
|
||||
inverseMask >>= 8 - bit;
|
||||
array[pos + 1] = (byte)(bitfield | (array[pos + 1] & inverseMask));
|
||||
array[pos + 2] = (byte)((bitfield >> 8) | (array[pos + 2] & (inverseMask >> 8)));
|
||||
}
|
||||
}
|
||||
/// <inheritdoc cref="SetBits(byte, int, byte[], int)"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void SetBits(uint bitfield, int amount, byte[] array, int startBit)
|
||||
{
|
||||
uint mask = (1u << (amount - 1) << 1) - 1; // Perform 2 shifts, doing it in 1 doesn't cause the value to wrap properly
|
||||
bitfield &= mask; // Discard any bits that are set beyond the ones we're setting
|
||||
uint inverseMask = ~mask;
|
||||
int pos = startBit / BitsPerByte;
|
||||
int bit = startBit % BitsPerByte;
|
||||
if (bit == 0)
|
||||
{
|
||||
array[pos ] = (byte)(bitfield | (array[pos] & inverseMask));
|
||||
array[pos + 1] = (byte)((bitfield >> 8) | (array[pos + 1] & (inverseMask >> 8)));
|
||||
array[pos + 2] = (byte)((bitfield >> 16) | (array[pos + 2] & (inverseMask >> 16)));
|
||||
array[pos + 3] = (byte)((bitfield >> 24) | (array[pos + 3] & (inverseMask >> 24)));
|
||||
}
|
||||
else
|
||||
{
|
||||
array[pos ] = (byte)((bitfield << bit) | (array[pos] & ~(mask << bit)));
|
||||
bitfield >>= 8 - bit;
|
||||
inverseMask >>= 8 - bit;
|
||||
array[pos + 1] = (byte)(bitfield | (array[pos + 1] & inverseMask));
|
||||
array[pos + 2] = (byte)((bitfield >> 8) | (array[pos + 2] & (inverseMask >> 8)));
|
||||
array[pos + 3] = (byte)((bitfield >> 16) | (array[pos + 3] & (inverseMask >> 16)));
|
||||
array[pos + 4] = (byte)((bitfield >> 24) | (array[pos + 4] & ~(mask >> (32 - bit)))); // This one can't use inverseMask because it would have incorrectly zeroed bits
|
||||
}
|
||||
}
|
||||
/// <inheritdoc cref="SetBits(byte, int, byte[], int)"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void SetBits(ulong bitfield, int amount, byte[] array, int startBit)
|
||||
{
|
||||
ulong mask = (1ul << (amount - 1) << 1) - 1; // Perform 2 shifts, doing it in 1 doesn't cause the value to wrap properly
|
||||
bitfield &= mask; // Discard any bits that are set beyond the ones we're setting
|
||||
ulong inverseMask = ~mask;
|
||||
int pos = startBit / BitsPerByte;
|
||||
int bit = startBit % BitsPerByte;
|
||||
if (bit == 0)
|
||||
{
|
||||
array[pos ] = (byte)(bitfield | (array[pos] & inverseMask));
|
||||
array[pos + 1] = (byte)((bitfield >> 8) | (array[pos + 1] & (inverseMask >> 8)));
|
||||
array[pos + 2] = (byte)((bitfield >> 16) | (array[pos + 2] & (inverseMask >> 16)));
|
||||
array[pos + 3] = (byte)((bitfield >> 24) | (array[pos + 3] & (inverseMask >> 24)));
|
||||
array[pos + 4] = (byte)((bitfield >> 32) | (array[pos + 4] & (inverseMask >> 32)));
|
||||
array[pos + 5] = (byte)((bitfield >> 40) | (array[pos + 5] & (inverseMask >> 40)));
|
||||
array[pos + 6] = (byte)((bitfield >> 48) | (array[pos + 6] & (inverseMask >> 48)));
|
||||
array[pos + 7] = (byte)((bitfield >> 56) | (array[pos + 7] & (inverseMask >> 56)));
|
||||
}
|
||||
else
|
||||
{
|
||||
array[pos ] = (byte)((bitfield << bit) | (array[pos] & ~(mask << bit)));
|
||||
bitfield >>= 8 - bit;
|
||||
inverseMask >>= 8 - bit;
|
||||
array[pos + 1] = (byte)(bitfield | (array[pos + 1] & inverseMask));
|
||||
array[pos + 2] = (byte)((bitfield >> 8) | (array[pos + 2] & (inverseMask >> 8)));
|
||||
array[pos + 3] = (byte)((bitfield >> 16) | (array[pos + 3] & (inverseMask >> 16)));
|
||||
array[pos + 4] = (byte)((bitfield >> 24) | (array[pos + 4] & (inverseMask >> 24)));
|
||||
array[pos + 5] = (byte)((bitfield >> 32) | (array[pos + 5] & (inverseMask >> 32)));
|
||||
array[pos + 6] = (byte)((bitfield >> 40) | (array[pos + 6] & (inverseMask >> 40)));
|
||||
array[pos + 7] = (byte)((bitfield >> 48) | (array[pos + 7] & (inverseMask >> 48)));
|
||||
array[pos + 8] = (byte)((bitfield >> 56) | (array[pos + 8] & ~(mask >> (64 - bit)))); // This one can't use inverseMask because it would have incorrectly zeroed bits
|
||||
}
|
||||
}
|
||||
/// <inheritdoc cref="SetBits(byte, int, byte[], int)"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void SetBits(ulong bitfield, int amount, ulong[] array, int startBit)
|
||||
{
|
||||
ulong mask = (1ul << (amount - 1) << 1) - 1; // Perform 2 shifts, doing it in 1 doesn't cause the value to wrap properly
|
||||
bitfield &= mask; // Discard any bits that are set beyond the ones we're setting
|
||||
int pos = startBit / BitsPerULong;
|
||||
int bit = startBit % BitsPerULong;
|
||||
if (bit == 0)
|
||||
array[pos] = bitfield | array[pos] & ~mask;
|
||||
else
|
||||
{
|
||||
array[pos] = (bitfield << bit) | (array[pos] & ~(mask << bit));
|
||||
if (bit + amount >= BitsPerULong)
|
||||
array[pos + 1] = (bitfield >> (64 - bit)) | (array[pos + 1] & ~(mask >> (64 - bit)));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Starting at <paramref name="startBit"/>, reads <paramref name="amount"/> bits from <paramref name="array"/> into <paramref name="bitfield"/>.</summary>
|
||||
/// <param name="amount">The number of bits to read.</param>
|
||||
/// <param name="array">The array to read the bits from.</param>
|
||||
/// <param name="startBit">The bit position in the array at which to start reading.</param>
|
||||
/// <param name="bitfield">The bitfield into which to write the bits from the array.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void GetBits(int amount, byte[] array, int startBit, out byte bitfield)
|
||||
{
|
||||
bitfield = ByteFromBits(array, startBit);
|
||||
bitfield &= (byte)((1 << amount) - 1); // Discard any bits that are set beyond the ones we're reading
|
||||
}
|
||||
/// <inheritdoc cref="GetBits(int, byte[], int, out byte)"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void GetBits(int amount, byte[] array, int startBit, out ushort bitfield)
|
||||
{
|
||||
bitfield = UShortFromBits(array, startBit);
|
||||
bitfield &= (ushort)((1 << amount) - 1); // Discard any bits that are set beyond the ones we're reading
|
||||
}
|
||||
/// <inheritdoc cref="GetBits(int, byte[], int, out byte)"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void GetBits(int amount, byte[] array, int startBit, out uint bitfield)
|
||||
{
|
||||
bitfield = UIntFromBits(array, startBit);
|
||||
bitfield &= (1u << (amount - 1) << 1) - 1; // Discard any bits that are set beyond the ones we're reading
|
||||
}
|
||||
/// <inheritdoc cref="GetBits(int, byte[], int, out byte)"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void GetBits(int amount, byte[] array, int startBit, out ulong bitfield)
|
||||
{
|
||||
bitfield = ULongFromBits(array, startBit);
|
||||
bitfield &= (1ul << (amount - 1) << 1) - 1; // Discard any bits that are set beyond the ones we're reading
|
||||
}
|
||||
/// <inheritdoc cref="GetBits(int, byte[], int, out byte)"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void GetBits(int amount, ulong[] array, int startBit, out byte bitfield)
|
||||
{
|
||||
bitfield = ByteFromBits(array, startBit);
|
||||
bitfield &= (byte)((1 << amount) - 1); // Discard any bits that are set beyond the ones we're reading
|
||||
}
|
||||
/// <inheritdoc cref="GetBits(int, byte[], int, out byte)"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void GetBits(int amount, ulong[] array, int startBit, out ushort bitfield)
|
||||
{
|
||||
bitfield = UShortFromBits(array, startBit);
|
||||
bitfield &= (ushort)((1 << amount) - 1); // Discard any bits that are set beyond the ones we're reading
|
||||
}
|
||||
/// <inheritdoc cref="GetBits(int, byte[], int, out byte)"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void GetBits(int amount, ulong[] array, int startBit, out uint bitfield)
|
||||
{
|
||||
bitfield = UIntFromBits(array, startBit);
|
||||
bitfield &= (1u << (amount - 1) << 1) - 1; // Discard any bits that are set beyond the ones we're reading
|
||||
}
|
||||
/// <inheritdoc cref="GetBits(int, byte[], int, out byte)"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void GetBits(int amount, ulong[] array, int startBit, out ulong bitfield)
|
||||
{
|
||||
bitfield = ULongFromBits(array, startBit);
|
||||
bitfield &= (1ul << (amount - 1) << 1) - 1; // Discard any bits that are set beyond the ones we're reading
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Byte/SByte
|
||||
/// <summary>Converts <paramref name="value"/> to 8 bits and writes them into <paramref name="array"/> at <paramref name="startBit"/>.</summary>
|
||||
/// <param name="value">The <see cref="sbyte"/> to convert.</param>
|
||||
/// <param name="array">The array to write the bits into.</param>
|
||||
/// <param name="startBit">The position in the array at which to write the bits.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void SByteToBits(sbyte value, byte[] array, int startBit) => ByteToBits((byte)value, array, startBit);
|
||||
/// <inheritdoc cref="SByteToBits(sbyte, byte[], int)"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void SByteToBits(sbyte value, ulong[] array, int startBit) => ByteToBits((byte)value, array, startBit);
|
||||
/// <summary>Converts <paramref name="value"/> to 8 bits and writes them into <paramref name="array"/> at <paramref name="startBit"/>.</summary>
|
||||
/// <param name="value">The <see cref="byte"/> to convert.</param>
|
||||
/// <param name="array">The array to write the bits into.</param>
|
||||
/// <param name="startBit">The position in the array at which to write the bits.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void ByteToBits(byte value, byte[] array, int startBit)
|
||||
{
|
||||
int pos = startBit / BitsPerByte;
|
||||
int bit = startBit % BitsPerByte;
|
||||
if (bit == 0)
|
||||
array[pos] = value;
|
||||
else
|
||||
{
|
||||
array[pos ] |= (byte)(value << bit);
|
||||
array[pos + 1] = (byte)(value >> (8 - bit));
|
||||
}
|
||||
}
|
||||
/// <inheritdoc cref="ByteToBits(byte, byte[], int)"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void ByteToBits(byte value, ulong[] array, int startBit) => ToBits(value, BitsPerByte, array, startBit);
|
||||
|
||||
/// <summary>Converts the 8 bits at <paramref name="startBit"/> in <paramref name="array"/> to an <see cref="sbyte"/>.</summary>
|
||||
/// <param name="array">The array to convert the bits from.</param>
|
||||
/// <param name="startBit">The position in the array from which to read the bits.</param>
|
||||
/// <returns>The converted <see cref="sbyte"/>.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static sbyte SByteFromBits(byte[] array, int startBit) => (sbyte)ByteFromBits(array, startBit);
|
||||
/// <inheritdoc cref="SByteFromBits(byte[], int)"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static sbyte SByteFromBits(ulong[] array, int startBit) => (sbyte)ByteFromBits(array, startBit);
|
||||
/// <summary>Converts the 8 bits at <paramref name="startBit"/> in <paramref name="array"/> to a <see cref="byte"/>.</summary>
|
||||
/// <param name="array">The array to convert the bits from.</param>
|
||||
/// <param name="startBit">The position in the array from which to read the bits.</param>
|
||||
/// <returns>The converted <see cref="byte"/>.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static byte ByteFromBits(byte[] array, int startBit)
|
||||
{
|
||||
int pos = startBit / BitsPerByte;
|
||||
int bit = startBit % BitsPerByte;
|
||||
byte value = array[pos];
|
||||
if (bit == 0)
|
||||
return value;
|
||||
|
||||
value >>= bit;
|
||||
return (byte)(value | (array[pos + 1] << (8 - bit)));
|
||||
}
|
||||
/// <inheritdoc cref="ByteFromBits(byte[], int)"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static byte ByteFromBits(ulong[] array, int startBit) => (byte)FromBits(BitsPerByte, array, startBit);
|
||||
#endregion
|
||||
|
||||
#region Bool
|
||||
/// <summary>Converts <paramref name="value"/> to a bit and writes it into <paramref name="array"/> at <paramref name="startBit"/>.</summary>
|
||||
/// <param name="value">The <see cref="bool"/> to convert.</param>
|
||||
/// <param name="array">The array to write the bit into.</param>
|
||||
/// <param name="startBit">The position in the array at which to write the bit.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void BoolToBit(bool value, byte[] array, int startBit)
|
||||
{
|
||||
int pos = startBit / BitsPerByte;
|
||||
int bit = startBit % BitsPerByte;
|
||||
if (bit == 0)
|
||||
array[pos] = 0;
|
||||
|
||||
if (value)
|
||||
array[pos] |= (byte)(1 << bit);
|
||||
}
|
||||
/// <inheritdoc cref="BoolToBit(bool, byte[], int)"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void BoolToBit(bool value, ulong[] array, int startBit)
|
||||
{
|
||||
int pos = startBit / BitsPerULong;
|
||||
int bit = startBit % BitsPerULong;
|
||||
if (bit == 0)
|
||||
array[pos] = 0;
|
||||
|
||||
if (value)
|
||||
array[pos] |= 1ul << bit;
|
||||
}
|
||||
|
||||
/// <summary>Converts the bit at <paramref name="startBit"/> in <paramref name="array"/> to a <see cref="bool"/>.</summary>
|
||||
/// <param name="array">The array to convert the bit from.</param>
|
||||
/// <param name="startBit">The position in the array from which to read the bit.</param>
|
||||
/// <returns>The converted <see cref="bool"/>.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool BoolFromBit(byte[] array, int startBit)
|
||||
{
|
||||
int pos = startBit / BitsPerByte;
|
||||
int bit = startBit % BitsPerByte;
|
||||
return (array[pos] & (1 << bit)) != 0;
|
||||
}
|
||||
/// <inheritdoc cref="BoolFromBit(byte[], int)"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool BoolFromBit(ulong[] array, int startBit)
|
||||
{
|
||||
int pos = startBit / BitsPerULong;
|
||||
int bit = startBit % BitsPerULong;
|
||||
return (array[pos] & (1ul << bit)) != 0;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Short/UShort
|
||||
/// <summary>Converts a given <see cref="short"/> to bytes and writes them into the given array.</summary>
|
||||
/// <param name="value">The <see cref="short"/> to convert.</param>
|
||||
/// <param name="array">The array to write the bytes into.</param>
|
||||
/// <param name="startIndex">The position in the array at which to write the bytes.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void FromShort(short value, byte[] array, int startIndex) => FromUShort((ushort)value, array, startIndex);
|
||||
/// <summary>Converts a given <see cref="ushort"/> to bytes and writes them into the given array.</summary>
|
||||
/// <param name="value">The <see cref="ushort"/> to convert.</param>
|
||||
/// <param name="array">The array to write the bytes into.</param>
|
||||
/// <param name="startIndex">The position in the array at which to write the bytes.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void FromUShort(ushort value, byte[] array, int startIndex)
|
||||
{
|
||||
#if BIG_ENDIAN
|
||||
array[startIndex + 1] = (byte)value;
|
||||
array[startIndex ] = (byte)(value >> 8);
|
||||
#else
|
||||
array[startIndex ] = (byte)value;
|
||||
array[startIndex + 1] = (byte)(value >> 8);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>Converts the 2 bytes in the array at <paramref name="startIndex"/> to a <see cref="short"/>.</summary>
|
||||
/// <param name="array">The array to read the bytes from.</param>
|
||||
/// <param name="startIndex">The position in the array at which to read the bytes.</param>
|
||||
/// <returns>The converted <see cref="short"/>.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static short ToShort(byte[] array, int startIndex) => (short)ToUShort(array, startIndex);
|
||||
/// <summary>Converts the 2 bytes in the array at <paramref name="startIndex"/> to a <see cref="ushort"/>.</summary>
|
||||
/// <param name="array">The array to read the bytes from.</param>
|
||||
/// <param name="startIndex">The position in the array at which to read the bytes.</param>
|
||||
/// <returns>The converted <see cref="ushort"/>.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ushort ToUShort(byte[] array, int startIndex)
|
||||
{
|
||||
#if BIG_ENDIAN
|
||||
return (ushort)(array[startIndex + 1] | (array[startIndex ] << 8));
|
||||
#else
|
||||
return (ushort)(array[startIndex ] | (array[startIndex + 1] << 8));
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>Converts <paramref name="value"/> to 16 bits and writes them into <paramref name="array"/> at <paramref name="startBit"/>.</summary>
|
||||
/// <param name="value">The <see cref="short"/> to convert.</param>
|
||||
/// <param name="array">The array to write the bits into.</param>
|
||||
/// <param name="startBit">The position in the array at which to write the bits.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void ShortToBits(short value, byte[] array, int startBit) => UShortToBits((ushort)value, array, startBit);
|
||||
/// <inheritdoc cref="ShortToBits(short, byte[], int)"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void ShortToBits(short value, ulong[] array, int startBit) => UShortToBits((ushort)value, array, startBit);
|
||||
/// <summary>Converts <paramref name="value"/> to 16 bits and writes them into <paramref name="array"/> at <paramref name="startBit"/>.</summary>
|
||||
/// <param name="value">The <see cref="ushort"/> to convert.</param>
|
||||
/// <param name="array">The array to write the bits into.</param>
|
||||
/// <param name="startBit">The position in the array at which to write the bits.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void UShortToBits(ushort value, byte[] array, int startBit)
|
||||
{
|
||||
int pos = startBit / BitsPerByte;
|
||||
int bit = startBit % BitsPerByte;
|
||||
if (bit == 0)
|
||||
{
|
||||
array[pos] = (byte)value;
|
||||
array[pos + 1] = (byte)(value >> 8);
|
||||
}
|
||||
else
|
||||
{
|
||||
array[pos ] |= (byte)(value << bit);
|
||||
value >>= 8 - bit;
|
||||
array[pos + 1] = (byte)value;
|
||||
array[pos + 2] = (byte)(value >> 8);
|
||||
}
|
||||
}
|
||||
/// <inheritdoc cref="UShortToBits(ushort, byte[], int)"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void UShortToBits(ushort value, ulong[] array, int startBit) => ToBits(value, sizeof(ushort) * BitsPerByte, array, startBit);
|
||||
|
||||
/// <summary>Converts the 16 bits at <paramref name="startBit"/> in <paramref name="array"/> to a <see cref="short"/>.</summary>
|
||||
/// <param name="array">The array to convert the bits from.</param>
|
||||
/// <param name="startBit">The position in the array from which to read the bits.</param>
|
||||
/// <returns>The converted <see cref="short"/>.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static short ShortFromBits(byte[] array, int startBit) => (short)UShortFromBits(array, startBit);
|
||||
/// <inheritdoc cref="ShortFromBits(byte[], int)"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static short ShortFromBits(ulong[] array, int startBit) => (short)UShortFromBits(array, startBit);
|
||||
/// <summary>Converts the 16 bits at <paramref name="startBit"/> in <paramref name="array"/> to a <see cref="ushort"/>.</summary>
|
||||
/// <param name="array">The array to convert the bits from.</param>
|
||||
/// <param name="startBit">The position in the array from which to read the bits.</param>
|
||||
/// <returns>The converted <see cref="ushort"/>.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ushort UShortFromBits(byte[] array, int startBit)
|
||||
{
|
||||
int pos = startBit / BitsPerByte;
|
||||
int bit = startBit % BitsPerByte;
|
||||
ushort value = (ushort)(array[pos] | (array[pos + 1] << 8));
|
||||
if (bit == 0)
|
||||
return value;
|
||||
|
||||
value >>= bit;
|
||||
return (ushort)(value | (array[pos + 2] << (16 - bit)));
|
||||
}
|
||||
/// <inheritdoc cref="UShortFromBits(byte[], int)"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ushort UShortFromBits(ulong[] array, int startBit) => (ushort)FromBits(sizeof(ushort) * BitsPerByte, array, startBit);
|
||||
#endregion
|
||||
|
||||
#region Int/UInt
|
||||
/// <summary>Converts a given <see cref="int"/> to bytes and writes them into the given array.</summary>
|
||||
/// <param name="value">The <see cref="int"/> to convert.</param>
|
||||
/// <param name="array">The array to write the bytes into.</param>
|
||||
/// <param name="startIndex">The position in the array at which to write the bytes.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void FromInt(int value, byte[] array, int startIndex) => FromUInt((uint)value, array, startIndex);
|
||||
/// <summary>Converts a given <see cref="uint"/> to bytes and writes them into the given array.</summary>
|
||||
/// <param name="value">The <see cref="uint"/> to convert.</param>
|
||||
/// <param name="array">The array to write the bytes into.</param>
|
||||
/// <param name="startIndex">The position in the array at which to write the bytes.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void FromUInt(uint value, byte[] array, int startIndex)
|
||||
{
|
||||
#if BIG_ENDIAN
|
||||
array[startIndex + 3] = (byte)value;
|
||||
array[startIndex + 2] = (byte)(value >> 8);
|
||||
array[startIndex + 1] = (byte)(value >> 16);
|
||||
array[startIndex ] = (byte)(value >> 24);
|
||||
#else
|
||||
array[startIndex ] = (byte)value;
|
||||
array[startIndex + 1] = (byte)(value >> 8);
|
||||
array[startIndex + 2] = (byte)(value >> 16);
|
||||
array[startIndex + 3] = (byte)(value >> 24);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>Converts the 4 bytes in the array at <paramref name="startIndex"/> to a <see cref="int"/>.</summary>
|
||||
/// <param name="array">The array to read the bytes from.</param>
|
||||
/// <param name="startIndex">The position in the array at which to read the bytes.</param>
|
||||
/// <returns>The converted <see cref="int"/>.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int ToInt(byte[] array, int startIndex) => (int)ToUInt(array, startIndex);
|
||||
/// <summary>Converts the 4 bytes in the array at <paramref name="startIndex"/> to a <see cref="uint"/>.</summary>
|
||||
/// <param name="array">The array to read the bytes from.</param>
|
||||
/// <param name="startIndex">The position in the array at which to read the bytes.</param>
|
||||
/// <returns>The converted <see cref="uint"/>.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static uint ToUInt(byte[] array, int startIndex)
|
||||
{
|
||||
#if BIG_ENDIAN
|
||||
return (uint)(array[startIndex + 3] | (array[startIndex + 2] << 8) | (array[startIndex + 1] << 16) | (array[startIndex ] << 24));
|
||||
#else
|
||||
return (uint)(array[startIndex ] | (array[startIndex + 1] << 8) | (array[startIndex + 2] << 16) | (array[startIndex + 3] << 24));
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>Converts <paramref name="value"/> to 32 bits and writes them into <paramref name="array"/> at <paramref name="startBit"/>.</summary>
|
||||
/// <param name="value">The <see cref="int"/> to convert.</param>
|
||||
/// <param name="array">The array to write the bits into.</param>
|
||||
/// <param name="startBit">The position in the array at which to write the bits.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void IntToBits(int value, byte[] array, int startBit) => UIntToBits((uint)value, array, startBit);
|
||||
/// <inheritdoc cref="IntToBits(int, byte[], int)"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void IntToBits(int value, ulong[] array, int startBit) => UIntToBits((uint)value, array, startBit);
|
||||
/// <summary>Converts <paramref name="value"/> to 32 bits and writes them into <paramref name="array"/> at <paramref name="startBit"/>.</summary>
|
||||
/// <param name="value">The <see cref="uint"/> to convert.</param>
|
||||
/// <param name="array">The array to write the bits into.</param>
|
||||
/// <param name="startBit">The position in the array at which to write the bits.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void UIntToBits(uint value, byte[] array, int startBit)
|
||||
{
|
||||
int pos = startBit / BitsPerByte;
|
||||
int bit = startBit % BitsPerByte;
|
||||
if (bit == 0)
|
||||
{
|
||||
array[pos ] = (byte)value;
|
||||
array[pos + 1] = (byte)(value >> 8);
|
||||
array[pos + 2] = (byte)(value >> 16);
|
||||
array[pos + 3] = (byte)(value >> 24);
|
||||
}
|
||||
else
|
||||
{
|
||||
array[pos ] |= (byte)(value << bit);
|
||||
value >>= 8 - bit;
|
||||
array[pos + 1] = (byte)value;
|
||||
array[pos + 2] = (byte)(value >> 8);
|
||||
array[pos + 3] = (byte)(value >> 16);
|
||||
array[pos + 4] = (byte)(value >> 24);
|
||||
}
|
||||
}
|
||||
/// <inheritdoc cref="UIntToBits(uint, byte[], int)"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void UIntToBits(uint value, ulong[] array, int startBit) => ToBits(value, sizeof(uint) * BitsPerByte, array, startBit);
|
||||
|
||||
/// <summary>Converts the 32 bits at <paramref name="startBit"/> in <paramref name="array"/> to an <see cref="int"/>.</summary>
|
||||
/// <param name="array">The array to convert the bits from.</param>
|
||||
/// <param name="startBit">The position in the array from which to read the bits.</param>
|
||||
/// <returns>The converted <see cref="int"/>.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int IntFromBits(byte[] array, int startBit) => (int)UIntFromBits(array, startBit);
|
||||
/// <inheritdoc cref="IntFromBits(byte[], int)"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int IntFromBits(ulong[] array, int startBit) => (int)UIntFromBits(array, startBit);
|
||||
/// <summary>Converts the 32 bits at <paramref name="startBit"/> in <paramref name="array"/> to a <see cref="uint"/>.</summary>
|
||||
/// <param name="array">The array to convert the bits from.</param>
|
||||
/// <param name="startBit">The position in the array from which to read the bits.</param>
|
||||
/// <returns>The converted <see cref="uint"/>.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static uint UIntFromBits(byte[] array, int startBit)
|
||||
{
|
||||
int pos = startBit / BitsPerByte;
|
||||
int bit = startBit % BitsPerByte;
|
||||
uint value = (uint)(array[pos] | (array[pos + 1] << 8) | (array[pos + 2] << 16) | (array[pos + 3] << 24));
|
||||
if (bit == 0)
|
||||
return value;
|
||||
|
||||
value >>= bit;
|
||||
return value | (uint)(array[pos + 4] << (32 - bit));
|
||||
}
|
||||
/// <inheritdoc cref="UIntFromBits(byte[], int)"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static uint UIntFromBits(ulong[] array, int startBit) => (uint)FromBits(sizeof(uint) * BitsPerByte, array, startBit);
|
||||
#endregion
|
||||
|
||||
#region Long/ULong
|
||||
/// <summary>Converts a given <see cref="long"/> to bytes and writes them into the given array.</summary>
|
||||
/// <param name="value">The <see cref="long"/> to convert.</param>
|
||||
/// <param name="array">The array to write the bytes into.</param>
|
||||
/// <param name="startIndex">The position in the array at which to write the bytes.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void FromLong(long value, byte[] array, int startIndex) => FromULong((ulong)value, array, startIndex);
|
||||
/// <summary>Converts a given <see cref="ulong"/> to bytes and writes them into the given array.</summary>
|
||||
/// <param name="value">The <see cref="ulong"/> to convert.</param>
|
||||
/// <param name="array">The array to write the bytes into.</param>
|
||||
/// <param name="startIndex">The position in the array at which to write the bytes.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void FromULong(ulong value, byte[] array, int startIndex)
|
||||
{
|
||||
#if BIG_ENDIAN
|
||||
array[startIndex + 7] = (byte)value;
|
||||
array[startIndex + 6] = (byte)(value >> 8);
|
||||
array[startIndex + 5] = (byte)(value >> 16);
|
||||
array[startIndex + 4] = (byte)(value >> 24);
|
||||
array[startIndex + 3] = (byte)(value >> 32);
|
||||
array[startIndex + 2] = (byte)(value >> 40);
|
||||
array[startIndex + 1] = (byte)(value >> 48);
|
||||
array[startIndex ] = (byte)(value >> 56);
|
||||
#else
|
||||
array[startIndex ] = (byte)value;
|
||||
array[startIndex + 1] = (byte)(value >> 8);
|
||||
array[startIndex + 2] = (byte)(value >> 16);
|
||||
array[startIndex + 3] = (byte)(value >> 24);
|
||||
array[startIndex + 4] = (byte)(value >> 32);
|
||||
array[startIndex + 5] = (byte)(value >> 40);
|
||||
array[startIndex + 6] = (byte)(value >> 48);
|
||||
array[startIndex + 7] = (byte)(value >> 56);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>Converts the 8 bytes in the array at <paramref name="startIndex"/> to a <see cref="long"/>.</summary>
|
||||
/// <param name="array">The array to read the bytes from.</param>
|
||||
/// <param name="startIndex">The position in the array at which to read the bytes.</param>
|
||||
/// <returns>The converted <see cref="long"/>.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static long ToLong(byte[] array, int startIndex)
|
||||
{
|
||||
#if BIG_ENDIAN
|
||||
Array.Reverse(array, startIndex, longLength);
|
||||
#endif
|
||||
return BitConverter.ToInt64(array, startIndex);
|
||||
}
|
||||
/// <summary>Converts the 8 bytes in the array at <paramref name="startIndex"/> to a <see cref="ulong"/>.</summary>
|
||||
/// <param name="array">The array to read the bytes from.</param>
|
||||
/// <param name="startIndex">The position in the array at which to read the bytes.</param>
|
||||
/// <returns>The converted <see cref="ulong"/>.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ulong ToULong(byte[] array, int startIndex)
|
||||
{
|
||||
#if BIG_ENDIAN
|
||||
Array.Reverse(array, startIndex, ulongLength);
|
||||
#endif
|
||||
return BitConverter.ToUInt64(array, startIndex);
|
||||
}
|
||||
|
||||
/// <summary>Converts <paramref name="value"/> to 64 bits and writes them into <paramref name="array"/> at <paramref name="startBit"/>.</summary>
|
||||
/// <param name="value">The <see cref="long"/> to convert.</param>
|
||||
/// <param name="array">The array to write the bits into.</param>
|
||||
/// <param name="startBit">The position in the array at which to write the bits.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void LongToBits(long value, byte[] array, int startBit) => ULongToBits((ulong)value, array, startBit);
|
||||
/// <inheritdoc cref="LongToBits(long, byte[], int)"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void LongToBits(long value, ulong[] array, int startBit) => ULongToBits((ulong)value, array, startBit);
|
||||
/// <summary>Converts <paramref name="value"/> to 64 bits and writes them into <paramref name="array"/> at <paramref name="startBit"/>.</summary>
|
||||
/// <param name="value">The <see cref="ulong"/> to convert.</param>
|
||||
/// <param name="array">The array to write the bits into.</param>
|
||||
/// <param name="startBit">The position in the array at which to write the bits.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void ULongToBits(ulong value, byte[] array, int startBit)
|
||||
{
|
||||
int pos = startBit / BitsPerByte;
|
||||
int bit = startBit % BitsPerByte;
|
||||
if (bit == 0)
|
||||
{
|
||||
array[pos ] = (byte)value;
|
||||
array[pos + 1] = (byte)(value >> 8);
|
||||
array[pos + 2] = (byte)(value >> 16);
|
||||
array[pos + 3] = (byte)(value >> 24);
|
||||
array[pos + 4] = (byte)(value >> 32);
|
||||
array[pos + 5] = (byte)(value >> 40);
|
||||
array[pos + 6] = (byte)(value >> 48);
|
||||
array[pos + 7] = (byte)(value >> 56);
|
||||
}
|
||||
else
|
||||
{
|
||||
array[pos ] |= (byte)(value << bit);
|
||||
value >>= 8 - bit;
|
||||
array[pos + 1] = (byte)value;
|
||||
array[pos + 2] = (byte)(value >> 8);
|
||||
array[pos + 3] = (byte)(value >> 16);
|
||||
array[pos + 4] = (byte)(value >> 24);
|
||||
array[pos + 5] = (byte)(value >> 32);
|
||||
array[pos + 6] = (byte)(value >> 40);
|
||||
array[pos + 7] = (byte)(value >> 48);
|
||||
array[pos + 8] = (byte)(value >> 56);
|
||||
}
|
||||
}
|
||||
/// <inheritdoc cref="ULongToBits(ulong, byte[], int)"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void ULongToBits(ulong value, ulong[] array, int startBit)
|
||||
{
|
||||
int pos = startBit / BitsPerULong;
|
||||
int bit = startBit % BitsPerULong;
|
||||
if (bit == 0)
|
||||
array[pos] = value;
|
||||
else
|
||||
{
|
||||
array[pos ] |= value << bit;
|
||||
array[pos + 1] = value >> (BitsPerULong - bit);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Converts the 64 bits at <paramref name="startBit"/> in <paramref name="array"/> to a <see cref="long"/>.</summary>
|
||||
/// <param name="array">The array to convert the bits from.</param>
|
||||
/// <param name="startBit">The position in the array from which to read the bits.</param>
|
||||
/// <returns>The converted <see cref="long"/>.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static long LongFromBits(byte[] array, int startBit) => (long)ULongFromBits(array, startBit);
|
||||
/// <inheritdoc cref="LongFromBits(byte[], int)"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static long LongFromBits(ulong[] array, int startBit) => (long)ULongFromBits(array, startBit);
|
||||
/// <summary>Converts the 64 bits at <paramref name="startBit"/> in <paramref name="array"/> to a <see cref="ulong"/>.</summary>
|
||||
/// <param name="array">The array to convert the bits from.</param>
|
||||
/// <param name="startBit">The position in the array from which to read the bits.</param>
|
||||
/// <returns>The converted <see cref="ulong"/>.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ulong ULongFromBits(byte[] array, int startBit)
|
||||
{
|
||||
int pos = startBit / BitsPerByte;
|
||||
int bit = startBit % BitsPerByte;
|
||||
ulong value = BitConverter.ToUInt64(array, pos);
|
||||
if (bit == 0)
|
||||
return value;
|
||||
|
||||
value >>= bit;
|
||||
return value | ((ulong)array[pos + 8] << (64 - bit));
|
||||
}
|
||||
/// <inheritdoc cref="ULongFromBits(byte[], int)"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ulong ULongFromBits(ulong[] array, int startBit)
|
||||
{
|
||||
int pos = startBit / BitsPerULong;
|
||||
int bit = startBit % BitsPerULong;
|
||||
ulong value = array[pos];
|
||||
if (bit == 0)
|
||||
return value;
|
||||
|
||||
value >>= bit;
|
||||
return value | (array[pos + 1] << (BitsPerULong - bit));
|
||||
}
|
||||
|
||||
/// <summary>Converts <paramref name="value"/> to <paramref name="valueSize"/> bits and writes them into <paramref name="array"/> at <paramref name="startBit"/>.
|
||||
/// Meant for values which fit into a <see cref="ulong"/>, not for <see cref="ulong"/>s themselves.</summary>
|
||||
/// <param name="value">The value to convert.</param>
|
||||
/// <param name="valueSize">The size in bits of the value being converted.</param>
|
||||
/// <param name="array">The array to write the bits into.</param>
|
||||
/// <param name="startBit">The position in the array at which to write the bits.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static void ToBits(ulong value, int valueSize, ulong[] array, int startBit)
|
||||
{
|
||||
int pos = startBit / BitsPerULong;
|
||||
int bit = startBit % BitsPerULong;
|
||||
if (bit == 0)
|
||||
array[pos] = value;
|
||||
else if (bit + valueSize < BitsPerULong)
|
||||
array[pos] |= value << bit;
|
||||
else
|
||||
{
|
||||
array[pos] |= value << bit;
|
||||
array[pos + 1] = value >> (BitsPerULong - bit);
|
||||
}
|
||||
}
|
||||
/// <summary>Converts the <paramref name="valueSize"/> bits at <paramref name="startBit"/> in <paramref name="array"/> to a <see cref="ulong"/>.
|
||||
/// Meant for values which fit into a <see cref="ulong"/>, not for <see cref="ulong"/>s themselves.</summary>
|
||||
/// <param name="valueSize">The size in bits of the value being converted.</param>
|
||||
/// <param name="array">The array to convert the bits from.</param>
|
||||
/// <param name="startBit">The position in the array from which to read the bits.</param>
|
||||
/// <returns>The converted <see cref="ulong"/>.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static ulong FromBits(int valueSize, ulong[] array, int startBit)
|
||||
{
|
||||
int pos = startBit / BitsPerULong;
|
||||
int bit = startBit % BitsPerULong;
|
||||
ulong value = array[pos];
|
||||
if (bit == 0)
|
||||
return value;
|
||||
|
||||
value >>= bit;
|
||||
if (bit + valueSize < BitsPerULong)
|
||||
return value;
|
||||
|
||||
return value | (array[pos + 1] << (BitsPerULong - bit));
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Float
|
||||
/// <summary>Converts a given <see cref="float"/> to bytes and writes them into the given array.</summary>
|
||||
/// <param name="value">The <see cref="float"/> to convert.</param>
|
||||
/// <param name="array">The array to write the bytes into.</param>
|
||||
/// <param name="startIndex">The position in the array at which to write the bytes.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void FromFloat(float value, byte[] array, int startIndex)
|
||||
{
|
||||
FloatConverter converter = new FloatConverter { FloatValue = value };
|
||||
#if BIG_ENDIAN
|
||||
array[startIndex + 3] = converter.Byte0;
|
||||
array[startIndex + 2] = converter.Byte1;
|
||||
array[startIndex + 1] = converter.Byte2;
|
||||
array[startIndex ] = converter.Byte3;
|
||||
#else
|
||||
array[startIndex ] = converter.Byte0;
|
||||
array[startIndex + 1] = converter.Byte1;
|
||||
array[startIndex + 2] = converter.Byte2;
|
||||
array[startIndex + 3] = converter.Byte3;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>Converts the 4 bytes in the array at <paramref name="startIndex"/> to a <see cref="float"/>.</summary>
|
||||
/// <param name="array">The array to read the bytes from.</param>
|
||||
/// <param name="startIndex">The position in the array at which to read the bytes.</param>
|
||||
/// <returns>The converted <see cref="float"/>.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static float ToFloat(byte[] array, int startIndex)
|
||||
{
|
||||
#if BIG_ENDIAN
|
||||
return new FloatConverter { Byte3 = array[startIndex], Byte2 = array[startIndex + 1], Byte1 = array[startIndex + 2], Byte0 = array[startIndex + 3] }.FloatValue;
|
||||
#else
|
||||
return new FloatConverter { Byte0 = array[startIndex], Byte1 = array[startIndex + 1], Byte2 = array[startIndex + 2], Byte3 = array[startIndex + 3] }.FloatValue;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>Converts <paramref name="value"/> to 32 bits and writes them into <paramref name="array"/> at <paramref name="startBit"/>.</summary>
|
||||
/// <param name="value">The <see cref="float"/> to convert.</param>
|
||||
/// <param name="array">The array to write the bits into.</param>
|
||||
/// <param name="startBit">The position in the array at which to write the bits.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void FloatToBits(float value, byte[] array, int startBit)
|
||||
{
|
||||
UIntToBits(new FloatConverter { FloatValue = value }.UIntValue, array, startBit);
|
||||
}
|
||||
/// <inheritdoc cref="FloatToBits(float, byte[], int)"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void FloatToBits(float value, ulong[] array, int startBit)
|
||||
{
|
||||
UIntToBits(new FloatConverter { FloatValue = value }.UIntValue, array, startBit);
|
||||
}
|
||||
|
||||
/// <summary>Converts the 32 bits at <paramref name="startBit"/> in <paramref name="array"/> to a <see cref="float"/>.</summary>
|
||||
/// <param name="array">The array to convert the bits from.</param>
|
||||
/// <param name="startBit">The position in the array from which to read the bits.</param>
|
||||
/// <returns>The converted <see cref="float"/>.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static float FloatFromBits(byte[] array, int startBit)
|
||||
{
|
||||
return new FloatConverter { UIntValue = UIntFromBits(array, startBit) }.FloatValue;
|
||||
}
|
||||
/// <inheritdoc cref="FloatFromBits(byte[], int)"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static float FloatFromBits(ulong[] array, int startBit)
|
||||
{
|
||||
return new FloatConverter { UIntValue = UIntFromBits(array, startBit) }.FloatValue;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Double
|
||||
/// <summary>Converts a given <see cref="double"/> to bytes and writes them into the given array.</summary>
|
||||
/// <param name="value">The <see cref="double"/> to convert.</param>
|
||||
/// <param name="array">The array to write the bytes into.</param>
|
||||
/// <param name="startIndex">The position in the array at which to write the bytes.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void FromDouble(double value, byte[] array, int startIndex)
|
||||
{
|
||||
DoubleConverter converter = new DoubleConverter { DoubleValue = value };
|
||||
#if BIG_ENDIAN
|
||||
array[startIndex + 7] = converter.Byte0;
|
||||
array[startIndex + 6] = converter.Byte1;
|
||||
array[startIndex + 5] = converter.Byte2;
|
||||
array[startIndex + 4] = converter.Byte3;
|
||||
array[startIndex + 3] = converter.Byte4;
|
||||
array[startIndex + 2] = converter.Byte5;
|
||||
array[startIndex + 1] = converter.Byte6;
|
||||
array[startIndex ] = converter.Byte7;
|
||||
#else
|
||||
array[startIndex ] = converter.Byte0;
|
||||
array[startIndex + 1] = converter.Byte1;
|
||||
array[startIndex + 2] = converter.Byte2;
|
||||
array[startIndex + 3] = converter.Byte3;
|
||||
array[startIndex + 4] = converter.Byte4;
|
||||
array[startIndex + 5] = converter.Byte5;
|
||||
array[startIndex + 6] = converter.Byte6;
|
||||
array[startIndex + 7] = converter.Byte7;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>Converts the 8 bytes in the array at <paramref name="startIndex"/> to a <see cref="double"/>.</summary>
|
||||
/// <param name="array">The array to read the bytes from.</param>
|
||||
/// <param name="startIndex">The position in the array at which to read the bytes.</param>
|
||||
/// <returns>The converted <see cref="double"/>.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static double ToDouble(byte[] array, int startIndex)
|
||||
{
|
||||
#if BIG_ENDIAN
|
||||
Array.Reverse(array, startIndex, doubleLength);
|
||||
#endif
|
||||
return BitConverter.ToDouble(array, startIndex);
|
||||
}
|
||||
|
||||
/// <summary>Converts <paramref name="value"/> to 64 bits and writes them into <paramref name="array"/> at <paramref name="startBit"/>.</summary>
|
||||
/// <param name="value">The <see cref="double"/> to convert.</param>
|
||||
/// <param name="array">The array to write the bits into.</param>
|
||||
/// <param name="startBit">The position in the array at which to write the bits.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void DoubleToBits(double value, byte[] array, int startBit)
|
||||
{
|
||||
ULongToBits(new DoubleConverter { DoubleValue = value }.ULongValue, array, startBit);
|
||||
}
|
||||
/// <inheritdoc cref="DoubleToBits(double, byte[], int)"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void DoubleToBits(double value, ulong[] array, int startBit)
|
||||
{
|
||||
ULongToBits(new DoubleConverter { DoubleValue = value }.ULongValue, array, startBit);
|
||||
}
|
||||
|
||||
/// <summary>Converts the 64 bits at <paramref name="startBit"/> in <paramref name="array"/> to a <see cref="double"/>.</summary>
|
||||
/// <param name="array">The array to convert the bits from.</param>
|
||||
/// <param name="startBit">The position in the array from which to read the bits.</param>
|
||||
/// <returns>The converted <see cref="double"/>.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static double DoubleFromBits(byte[] array, int startBit)
|
||||
{
|
||||
return new DoubleConverter { ULongValue = ULongFromBits(array, startBit) }.DoubleValue;
|
||||
}
|
||||
/// <inheritdoc cref="DoubleFromBits(byte[], int)"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static double DoubleFromBits(ulong[] array, int startBit)
|
||||
{
|
||||
return new DoubleConverter { ULongValue = ULongFromBits(array, startBit) }.DoubleValue;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
internal struct FloatConverter
|
||||
{
|
||||
[FieldOffset(0)] public byte Byte0;
|
||||
[FieldOffset(1)] public byte Byte1;
|
||||
[FieldOffset(2)] public byte Byte2;
|
||||
[FieldOffset(3)] public byte Byte3;
|
||||
|
||||
[FieldOffset(0)] public float FloatValue;
|
||||
|
||||
[FieldOffset(0)] public uint UIntValue;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
internal struct DoubleConverter
|
||||
{
|
||||
[FieldOffset(0)] public byte Byte0;
|
||||
[FieldOffset(1)] public byte Byte1;
|
||||
[FieldOffset(2)] public byte Byte2;
|
||||
[FieldOffset(3)] public byte Byte3;
|
||||
[FieldOffset(4)] public byte Byte4;
|
||||
[FieldOffset(5)] public byte Byte5;
|
||||
[FieldOffset(6)] public byte Byte6;
|
||||
[FieldOffset(7)] public byte Byte7;
|
||||
|
||||
[FieldOffset(0)] public double DoubleValue;
|
||||
|
||||
[FieldOffset(0)] public ulong ULongValue;
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
// This file is provided under The MIT License as part of RiptideNetworking.
|
||||
// Copyright (c) Tom Weiland
|
||||
// For additional information please see the included LICENSE.md file or view it on GitHub:
|
||||
// https://github.com/RiptideNetworking/Riptide/blob/main/LICENSE.md
|
||||
|
||||
using Riptide.Transports;
|
||||
|
||||
namespace Riptide.Utils
|
||||
{
|
||||
/// <summary>Executes an action when invoked.</summary>
|
||||
internal abstract class DelayedEvent
|
||||
{
|
||||
/// <summary>Executes the action.</summary>
|
||||
public abstract void Invoke();
|
||||
}
|
||||
|
||||
/// <summary>Resends a <see cref="PendingMessage"/> when invoked.</summary>
|
||||
internal class ResendEvent : DelayedEvent
|
||||
{
|
||||
/// <summary>The message to resend.</summary>
|
||||
private readonly PendingMessage message;
|
||||
/// <summary>The time at which the resend event was queued.</summary>
|
||||
private readonly long initiatedAtTime;
|
||||
|
||||
/// <summary>Initializes the event.</summary>
|
||||
/// <param name="message">The message to resend.</param>
|
||||
/// <param name="initiatedAtTime">The time at which the resend event was queued.</param>
|
||||
public ResendEvent(PendingMessage message, long initiatedAtTime)
|
||||
{
|
||||
this.message = message;
|
||||
this.initiatedAtTime = initiatedAtTime;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Invoke()
|
||||
{
|
||||
if (initiatedAtTime == message.LastSendTime) // If this isn't the case then the message has been resent already
|
||||
message.RetrySend();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Executes a heartbeat when invoked.</summary>
|
||||
internal class HeartbeatEvent : DelayedEvent
|
||||
{
|
||||
/// <summary>The peer whose heart to beat.</summary>
|
||||
private readonly Peer peer;
|
||||
|
||||
/// <summary>Initializes the event.</summary>
|
||||
/// <param name="peer">The peer whose heart to beat.</param>
|
||||
public HeartbeatEvent(Peer peer)
|
||||
{
|
||||
this.peer = peer;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Invoke()
|
||||
{
|
||||
peer.Heartbeat();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
// This file is provided under The MIT License as part of RiptideNetworking.
|
||||
// Copyright (c) Tom Weiland
|
||||
// For additional information please see the included LICENSE.md file or view it on GitHub:
|
||||
// https://github.com/RiptideNetworking/Riptide/blob/main/LICENSE.md
|
||||
|
||||
using System.Net;
|
||||
|
||||
namespace Riptide.Utils
|
||||
{
|
||||
/// <summary>Contains extension methods for various classes.</summary>
|
||||
public static class Extensions
|
||||
{
|
||||
/// <summary>Takes the <see cref="IPEndPoint"/>'s IP address and port number and converts it to a string, accounting for whether the address is an IPv4 or IPv6 address.</summary>
|
||||
/// <returns>A string containing the IP address and port number of the endpoint.</returns>
|
||||
public static string ToStringBasedOnIPFormat(this IPEndPoint endPoint)
|
||||
{
|
||||
if (endPoint.Address.IsIPv4MappedToIPv6)
|
||||
return $"{endPoint.Address.MapToIPv4()}:{endPoint.Port}";
|
||||
|
||||
return endPoint.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,113 +0,0 @@
|
||||
// This file is provided under The MIT License as part of RiptideNetworking.
|
||||
// Copyright (c) Tom Weiland
|
||||
// For additional information please see the included LICENSE.md file or view it on GitHub:
|
||||
// https://github.com/RiptideNetworking/Riptide/blob/main/LICENSE.md
|
||||
|
||||
using System;
|
||||
|
||||
namespace Riptide.Utils
|
||||
{
|
||||
/// <summary>Contains miscellaneous helper methods.</summary>
|
||||
internal class Helper
|
||||
{
|
||||
/// <summary>The text to log when disconnected due to <see cref="DisconnectReason.NeverConnected"/>.</summary>
|
||||
private const string DCNeverConnected = "Never connected";
|
||||
/// <summary>The text to log when disconnected due to <see cref="DisconnectReason.TransportError"/>.</summary>
|
||||
private const string DCTransportError = "Transport error";
|
||||
/// <summary>The text to log when disconnected due to <see cref="DisconnectReason.TimedOut"/>.</summary>
|
||||
private const string DCTimedOut = "Timed out";
|
||||
/// <summary>The text to log when disconnected due to <see cref="DisconnectReason.Kicked"/>.</summary>
|
||||
private const string DCKicked = "Kicked";
|
||||
/// <summary>The text to log when disconnected due to <see cref="DisconnectReason.ServerStopped"/>.</summary>
|
||||
private const string DCServerStopped = "Server stopped";
|
||||
/// <summary>The text to log when disconnected due to <see cref="DisconnectReason.Disconnected"/>.</summary>
|
||||
private const string DCDisconnected = "Disconnected";
|
||||
/// <summary>The text to log when disconnected due to <see cref="DisconnectReason.PoorConnection"/>.</summary>
|
||||
private const string DCPoorConnection = "Poor connection";
|
||||
/// <summary>The text to log when disconnected or rejected due to an unknown reason.</summary>
|
||||
private const string UnknownReason = "Unknown reason";
|
||||
/// <summary>The text to log when the connection failed due to <see cref="RejectReason.NoConnection"/>.</summary>
|
||||
private const string CRNoConnection = "No connection";
|
||||
/// <summary>The text to log when the connection failed due to <see cref="RejectReason.AlreadyConnected"/>.</summary>
|
||||
private const string CRAlreadyConnected = "This client is already connected";
|
||||
/// <summary>The text to log when the connection failed due to <see cref="RejectReason.ServerFull"/>.</summary>
|
||||
private const string CRServerFull = "Server is full";
|
||||
/// <summary>The text to log when the connection failed due to <see cref="RejectReason.Rejected"/>.</summary>
|
||||
private const string CRRejected = "Rejected";
|
||||
/// <summary>The text to log when the connection failed due to <see cref="RejectReason.Custom"/>.</summary>
|
||||
private const string CRCustom = "Rejected (with custom data)";
|
||||
|
||||
/// <summary>Determines whether <paramref name="singular"/> or <paramref name="plural"/> form should be used based on the <paramref name="amount"/>.</summary>
|
||||
/// <param name="amount">The amount that <paramref name="singular"/> and <paramref name="plural"/> refer to.</param>
|
||||
/// <param name="singular">The singular form.</param>
|
||||
/// <param name="plural">The plural form.</param>
|
||||
/// <returns><paramref name="singular"/> if <paramref name="amount"/> is 1; otherwise <paramref name="plural"/>.</returns>
|
||||
internal static string CorrectForm(int amount, string singular, string plural = "")
|
||||
{
|
||||
if (string.IsNullOrEmpty(plural))
|
||||
plural = $"{singular}s";
|
||||
|
||||
return amount == 1 ? singular : plural;
|
||||
}
|
||||
|
||||
/// <summary>Calculates the signed gap between sequence IDs, accounting for wrapping.</summary>
|
||||
/// <param name="seqId1">The new sequence ID.</param>
|
||||
/// <param name="seqId2">The previous sequence ID.</param>
|
||||
/// <returns>The signed gap between the two given sequence IDs. A positive gap means <paramref name="seqId1"/> is newer than <paramref name="seqId2"/>. A negative gap means <paramref name="seqId1"/> is older than <paramref name="seqId2"/>.</returns>
|
||||
internal static int GetSequenceGap(ushort seqId1, ushort seqId2)
|
||||
{
|
||||
int gap = seqId1 - seqId2;
|
||||
if (Math.Abs(gap) <= 32768) // Difference is small, meaning sequence IDs are close together
|
||||
return gap;
|
||||
else // Difference is big, meaning sequence IDs are far apart
|
||||
return (seqId1 <= 32768 ? ushort.MaxValue + 1 + seqId1 : seqId1) - (seqId2 <= 32768 ? ushort.MaxValue + 1 + seqId2 : seqId2);
|
||||
}
|
||||
|
||||
/// <summary>Retrieves the appropriate reason string for the given <see cref="DisconnectReason"/>.</summary>
|
||||
/// <param name="forReason">The <see cref="DisconnectReason"/> to retrieve the string for.</param>
|
||||
/// <returns>The appropriate reason string.</returns>
|
||||
internal static string GetReasonString(DisconnectReason forReason)
|
||||
{
|
||||
switch (forReason)
|
||||
{
|
||||
case DisconnectReason.NeverConnected:
|
||||
return DCNeverConnected;
|
||||
case DisconnectReason.TransportError:
|
||||
return DCTransportError;
|
||||
case DisconnectReason.TimedOut:
|
||||
return DCTimedOut;
|
||||
case DisconnectReason.Kicked:
|
||||
return DCKicked;
|
||||
case DisconnectReason.ServerStopped:
|
||||
return DCServerStopped;
|
||||
case DisconnectReason.Disconnected:
|
||||
return DCDisconnected;
|
||||
case DisconnectReason.PoorConnection:
|
||||
return DCPoorConnection;
|
||||
default:
|
||||
return $"{UnknownReason} '{forReason}'";
|
||||
}
|
||||
}
|
||||
/// <summary>Retrieves the appropriate reason string for the given <see cref="RejectReason"/>.</summary>
|
||||
/// <param name="forReason">The <see cref="RejectReason"/> to retrieve the string for.</param>
|
||||
/// <returns>The appropriate reason string.</returns>
|
||||
internal static string GetReasonString(RejectReason forReason)
|
||||
{
|
||||
switch (forReason)
|
||||
{
|
||||
case RejectReason.NoConnection:
|
||||
return CRNoConnection;
|
||||
case RejectReason.AlreadyConnected:
|
||||
return CRAlreadyConnected;
|
||||
case RejectReason.ServerFull:
|
||||
return CRServerFull;
|
||||
case RejectReason.Rejected:
|
||||
return CRRejected;
|
||||
case RejectReason.Custom:
|
||||
return CRCustom;
|
||||
default:
|
||||
return $"{UnknownReason} '{forReason}'";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,158 +0,0 @@
|
||||
// This file is provided under The MIT License as part of RiptideNetworking.
|
||||
// Copyright (c) Tom Weiland
|
||||
// For additional information please see the included LICENSE.md file or view it on GitHub:
|
||||
// https://github.com/RiptideNetworking/Riptide/blob/main/LICENSE.md
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Riptide.Utils
|
||||
{
|
||||
// PriorityQueue unfortunately doesn't exist in .NET Standard 2.1
|
||||
/// <summary>Represents a collection of items that have a value and a priority. On dequeue, the item with the lowest priority value is removed.</summary>
|
||||
/// <typeparam name="TElement">Specifies the type of elements in the queue.</typeparam>
|
||||
/// <typeparam name="TPriority">Specifies the type of priority associated with enqueued elements.</typeparam>
|
||||
public class PriorityQueue<TElement, TPriority>
|
||||
{
|
||||
/// <summary>Gets the number of elements contained in the <see cref="PriorityQueue{TElement, TPriority}"/>.</summary>
|
||||
public int Count { get; private set; }
|
||||
|
||||
private const int DefaultCapacity = 8;
|
||||
private Entry<TElement, TPriority>[] heap;
|
||||
private readonly IComparer<TPriority> comparer;
|
||||
|
||||
/// <summary>Initializes a new instance of the <see cref="PriorityQueue{TElement, TPriority}"/> class.</summary>
|
||||
/// <param name="capacity">Initial capacity to allocate for the underlying heap array.</param>
|
||||
public PriorityQueue(int capacity = DefaultCapacity)
|
||||
{
|
||||
heap = new Entry<TElement, TPriority>[capacity];
|
||||
comparer = Comparer<TPriority>.Default;
|
||||
}
|
||||
|
||||
/// <summary>Initializes a new instance of the <see cref="PriorityQueue{TElement, TPriority}"/> class with the specified custom priority comparer.</summary>
|
||||
/// <param name="comparer">Custom comparer dictating the ordering of elements.</param>
|
||||
/// <param name="capacity">Initial capacity to allocate for the underlying heap array.</param>
|
||||
public PriorityQueue(IComparer<TPriority> comparer, int capacity = DefaultCapacity)
|
||||
{
|
||||
heap = new Entry<TElement, TPriority>[capacity];
|
||||
this.comparer = comparer;
|
||||
}
|
||||
|
||||
/// <summary>Adds the specified element and associated priority to the <see cref="PriorityQueue{TElement, TPriority}"/>.</summary>
|
||||
/// <param name="element">The element to add.</param>
|
||||
/// <param name="priority">The priority with which to associate the new element.</param>
|
||||
public void Enqueue(TElement element, TPriority priority)
|
||||
{
|
||||
if (Count == heap.Length)
|
||||
{
|
||||
// Resizing is necessary
|
||||
Entry<TElement, TPriority>[] temp = new Entry<TElement, TPriority>[Count * 2];
|
||||
Array.Copy(heap, temp, heap.Length);
|
||||
heap = temp;
|
||||
}
|
||||
|
||||
int index = Count;
|
||||
while (index > 0)
|
||||
{
|
||||
int parentIndex = GetParentIndex(index);
|
||||
if (comparer.Compare(priority, heap[parentIndex].Priority) < 0)
|
||||
{
|
||||
heap[index] = heap[parentIndex];
|
||||
index = parentIndex;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
heap[index] = new Entry<TElement, TPriority>(element, priority);
|
||||
Count++;
|
||||
}
|
||||
|
||||
/// <summary>Removes and returns the lowest priority element.</summary>
|
||||
public TElement Dequeue()
|
||||
{
|
||||
TElement returnValue = heap[0].Element;
|
||||
|
||||
if (Count > 1)
|
||||
{
|
||||
int parent = 0;
|
||||
int leftChild = GetLeftChildIndex(parent);
|
||||
|
||||
while (leftChild < Count)
|
||||
{
|
||||
int rightChild = leftChild + 1;
|
||||
int bestChild = (rightChild < Count && comparer.Compare(heap[rightChild].Priority, heap[leftChild].Priority) < 0) ? rightChild : leftChild;
|
||||
|
||||
heap[parent] = heap[bestChild];
|
||||
parent = bestChild;
|
||||
leftChild = GetLeftChildIndex(parent);
|
||||
}
|
||||
|
||||
heap[parent] = heap[Count - 1];
|
||||
}
|
||||
|
||||
Count--;
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
/// <summary>Removes the lowest priority element from the <see cref="PriorityQueue{TElement, TPriority}"/> and copies it and its associated priority to the <paramref name="element"/> and <paramref name="priority"/> arguments.</summary>
|
||||
/// <param name="element">When this method returns, contains the removed element.</param>
|
||||
/// <param name="priority">When this method returns, contains the priority associated with the removed element.</param>
|
||||
/// <returns>true if the element is successfully removed; false if the <see cref="PriorityQueue{TElement, TPriority}"/> is empty.</returns>
|
||||
public bool TryDequeue(out TElement element, out TPriority priority)
|
||||
{
|
||||
if (Count > 0)
|
||||
{
|
||||
priority = heap[0].Priority;
|
||||
element = Dequeue();
|
||||
return true;
|
||||
}
|
||||
{
|
||||
element = default(TElement);
|
||||
priority = default(TPriority);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Returns the lowest priority element.</summary>
|
||||
public TElement Peek()
|
||||
{
|
||||
return heap[0].Element;
|
||||
}
|
||||
|
||||
/// <summary>Returns the priority of the lowest priority element.</summary>
|
||||
public TPriority PeekPriority()
|
||||
{
|
||||
return heap[0].Priority;
|
||||
}
|
||||
|
||||
/// <summary>Removes all elements from the <see cref="PriorityQueue{TElement, TPriority}"/>.</summary>
|
||||
public void Clear()
|
||||
{
|
||||
Array.Clear(heap, 0, heap.Length);
|
||||
Count = 0;
|
||||
}
|
||||
|
||||
private static int GetParentIndex(int index)
|
||||
{
|
||||
return (index - 1) / 2;
|
||||
}
|
||||
|
||||
private static int GetLeftChildIndex(int index)
|
||||
{
|
||||
return (index * 2) + 1;
|
||||
}
|
||||
|
||||
private struct Entry<TEle, TPrio>
|
||||
{
|
||||
internal readonly TEle Element;
|
||||
internal readonly TPrio Priority;
|
||||
|
||||
public Entry(TEle element, TPrio priority)
|
||||
{
|
||||
Element = element;
|
||||
Priority = priority;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,126 +0,0 @@
|
||||
// This file is provided under The MIT License as part of RiptideNetworking.
|
||||
// Copyright (c) Tom Weiland
|
||||
// For additional information please see the included LICENSE.md file or view it on GitHub:
|
||||
// https://github.com/RiptideNetworking/Riptide/blob/main/LICENSE.md
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Riptide.Utils
|
||||
{
|
||||
/// <summary>Defines log message types.</summary>
|
||||
public enum LogType
|
||||
{
|
||||
/// <summary>Logs that are used for investigation during development.</summary>
|
||||
Debug,
|
||||
/// <summary>Logs that provide general information about application flow.</summary>
|
||||
Info,
|
||||
/// <summary>Logs that highlight abnormal or unexpected events in the application flow.</summary>
|
||||
Warning,
|
||||
/// <summary>Logs that highlight problematic events in the application flow which will cause unexpected behavior if not planned for.</summary>
|
||||
Error
|
||||
}
|
||||
|
||||
/// <summary>Provides functionality for logging messages.</summary>
|
||||
public class RiptideLogger
|
||||
{
|
||||
/// <summary>Whether or not <see cref="LogType.Debug"/> messages will be logged.</summary>
|
||||
public static bool IsDebugLoggingEnabled => logMethods.ContainsKey(LogType.Debug);
|
||||
/// <summary>Whether or not <see cref="LogType.Info"/> messages will be logged.</summary>
|
||||
public static bool IsInfoLoggingEnabled => logMethods.ContainsKey(LogType.Info);
|
||||
/// <summary>Whether or not <see cref="LogType.Warning"/> messages will be logged.</summary>
|
||||
public static bool IsWarningLoggingEnabled => logMethods.ContainsKey(LogType.Warning);
|
||||
/// <summary>Whether or not <see cref="LogType.Error"/> messages will be logged.</summary>
|
||||
public static bool IsErrorLoggingEnabled => logMethods.ContainsKey(LogType.Error);
|
||||
/// <summary>Encapsulates a method used to log messages.</summary>
|
||||
/// <param name="log">The message to log.</param>
|
||||
public delegate void LogMethod(string log);
|
||||
|
||||
/// <summary>Log methods, accessible by their <see cref="LogType"/></summary>
|
||||
private static readonly Dictionary<LogType, LogMethod> logMethods = new Dictionary<LogType, LogMethod>(4);
|
||||
/// <summary>Whether or not to include timestamps when logging messages.</summary>
|
||||
private static bool includeTimestamps;
|
||||
/// <summary>The format to use for timestamps.</summary>
|
||||
private static string timestampFormat;
|
||||
|
||||
/// <summary>Initializes <see cref="RiptideLogger"/> with all log types enabled.</summary>
|
||||
/// <param name="logMethod">The method to use when logging all types of messages.</param>
|
||||
/// <param name="includeTimestamps">Whether or not to include timestamps when logging messages.</param>
|
||||
/// <param name="timestampFormat">The format to use for timestamps.</param>
|
||||
public static void Initialize(LogMethod logMethod, bool includeTimestamps, string timestampFormat = "HH:mm:ss") => Initialize(logMethod, logMethod, logMethod, logMethod, includeTimestamps, timestampFormat);
|
||||
/// <summary>Initializes <see cref="RiptideLogger"/> with the supplied log methods.</summary>
|
||||
/// <param name="debugMethod">The method to use when logging debug messages. Set to <see langword="null"/> to disable debug logs.</param>
|
||||
/// <param name="infoMethod">The method to use when logging info messages. Set to <see langword="null"/> to disable info logs.</param>
|
||||
/// <param name="warningMethod">The method to use when logging warning messages. Set to <see langword="null"/> to disable warning logs.</param>
|
||||
/// <param name="errorMethod">The method to use when logging error messages. Set to <see langword="null"/> to disable error logs.</param>
|
||||
/// <param name="includeTimestamps">Whether or not to include timestamps when logging messages.</param>
|
||||
/// <param name="timestampFormat">The format to use for timestamps.</param>
|
||||
public static void Initialize(LogMethod debugMethod, LogMethod infoMethod, LogMethod warningMethod, LogMethod errorMethod, bool includeTimestamps, string timestampFormat = "HH:mm:ss")
|
||||
{
|
||||
logMethods.Clear();
|
||||
|
||||
if (debugMethod != null)
|
||||
logMethods.Add(LogType.Debug, debugMethod);
|
||||
if (infoMethod != null)
|
||||
logMethods.Add(LogType.Info, infoMethod);
|
||||
if (warningMethod != null)
|
||||
logMethods.Add(LogType.Warning, warningMethod);
|
||||
if (errorMethod != null)
|
||||
logMethods.Add(LogType.Error, errorMethod);
|
||||
|
||||
RiptideLogger.includeTimestamps = includeTimestamps;
|
||||
RiptideLogger.timestampFormat = timestampFormat;
|
||||
}
|
||||
|
||||
/// <summary>Enables logging for messages of the given <see cref="LogType"/>.</summary>
|
||||
/// <param name="logType">The type of message to enable logging for.</param>
|
||||
/// <param name="logMethod">The method to use when logging this type of message.</param>
|
||||
public static void EnableLoggingFor(LogType logType, LogMethod logMethod)
|
||||
{
|
||||
if (logMethods.ContainsKey(logType))
|
||||
logMethods[logType] = logMethod;
|
||||
else
|
||||
logMethods.Add(logType, logMethod);
|
||||
}
|
||||
|
||||
/// <summary>Disables logging for messages of the given <see cref="LogType"/>.</summary>
|
||||
/// <param name="logType">The type of message to enable logging for.</param>
|
||||
public static void DisableLoggingFor(LogType logType) => logMethods.Remove(logType);
|
||||
|
||||
/// <summary>Logs a message.</summary>
|
||||
/// <param name="logType">The type of log message that is being logged.</param>
|
||||
/// <param name="message">The message to log.</param>
|
||||
public static void Log(LogType logType, string message)
|
||||
{
|
||||
if (logMethods.TryGetValue(logType, out LogMethod logMethod))
|
||||
{
|
||||
if (includeTimestamps)
|
||||
logMethod($"[{GetTimestamp(DateTime.Now)}]: {message}");
|
||||
else
|
||||
logMethod(message);
|
||||
}
|
||||
}
|
||||
/// <summary>Logs a message.</summary>
|
||||
/// <param name="logType">The type of log message that is being logged.</param>
|
||||
/// <param name="logName">Who is logging this message.</param>
|
||||
/// <param name="message">The message to log.</param>
|
||||
public static void Log(LogType logType, string logName, string message)
|
||||
{
|
||||
if (logMethods.TryGetValue(logType, out LogMethod logMethod))
|
||||
{
|
||||
if (includeTimestamps)
|
||||
logMethod($"[{GetTimestamp(DateTime.Now)}] ({logName}): {message}");
|
||||
else
|
||||
logMethod($"({logName}): {message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Converts a <see cref="DateTime"/> object to a formatted timestamp string.</summary>
|
||||
/// <param name="time">The time to format.</param>
|
||||
/// <returns>The formatted timestamp.</returns>
|
||||
private static string GetTimestamp(DateTime time)
|
||||
{
|
||||
return time.ToString(timestampFormat);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
// This file is provided under The MIT License as part of RiptideNetworking.
|
||||
// Copyright (c) Tom Weiland
|
||||
// For additional information please see the included LICENSE.md file or view it on GitHub:
|
||||
// https://github.com/RiptideNetworking/Riptide/blob/main/LICENSE.md
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace Riptide.Utils
|
||||
{
|
||||
/// <summary>Represents a rolling series of numbers.</summary>
|
||||
public class RollingStat
|
||||
{
|
||||
/// <summary>The position in the array of the latest item.</summary>
|
||||
private int index;
|
||||
/// <summary>How many of the array's slots are in use.</summary>
|
||||
private int slotsFilled;
|
||||
/// <inheritdoc cref="Mean"/>
|
||||
private double mean;
|
||||
/// <summary>The sum of the mean subtracted from each value in the array.</summary>
|
||||
private double sumOfSquares;
|
||||
/// <summary>The array used to store the values.</summary>
|
||||
private readonly double[] array;
|
||||
|
||||
/// <summary>The mean of the stat's values.</summary>
|
||||
public double Mean => mean;
|
||||
/// <summary>The variance of the stat's values.</summary>
|
||||
public double Variance => slotsFilled > 1 ? sumOfSquares / (slotsFilled - 1) : 0;
|
||||
/// <summary>The standard deviation of the stat's values.</summary>
|
||||
public double StandardDev
|
||||
{
|
||||
get
|
||||
{
|
||||
double variance = Variance;
|
||||
if (variance >= double.Epsilon)
|
||||
{
|
||||
double root = Math.Sqrt(variance);
|
||||
return double.IsNaN(root) ? 0 : root;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Initializes the stat.</summary>
|
||||
/// <param name="sampleSize">The number of values to store.</param>
|
||||
public RollingStat(int sampleSize)
|
||||
{
|
||||
index = 0;
|
||||
slotsFilled = 0;
|
||||
mean = 0;
|
||||
sumOfSquares = 0;
|
||||
array = new double[sampleSize];
|
||||
}
|
||||
|
||||
/// <summary>Adds a new value to the stat.</summary>
|
||||
/// <param name="value">The value to add.</param>
|
||||
public void Add(double value)
|
||||
{
|
||||
if (double.IsNaN(value) || double.IsInfinity(value))
|
||||
return;
|
||||
|
||||
index %= array.Length;
|
||||
double oldMean = mean;
|
||||
double oldValue = array[index];
|
||||
array[index] = value;
|
||||
index++;
|
||||
|
||||
if (slotsFilled == array.Length)
|
||||
{
|
||||
double delta = value - oldValue;
|
||||
mean += delta / slotsFilled;
|
||||
sumOfSquares += delta * (value - mean + (oldValue - oldMean));
|
||||
}
|
||||
else
|
||||
{
|
||||
slotsFilled++;
|
||||
double delta = value - oldMean;
|
||||
mean += delta / slotsFilled;
|
||||
sumOfSquares += delta * (value - mean);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString()
|
||||
{
|
||||
if (slotsFilled == array.Length)
|
||||
return string.Join(",", array);
|
||||
|
||||
return string.Join(",", array.Take(slotsFilled));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user