1923 lines
97 KiB
C#
1923 lines
97 KiB
C#
// 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;
|
|
using Riptide.Utils;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Runtime.CompilerServices;
|
|
using System.Text;
|
|
|
|
namespace Riptide
|
|
{
|
|
/// <summary>The send mode of a <see cref="Message"/>.</summary>
|
|
public enum MessageSendMode : byte
|
|
{
|
|
/// <summary>Guarantees order but not delivery. Notifies the sender of what happened via the <see cref="Connection.NotifyDelivered"/> and <see cref="Connection.NotifyLost"/>
|
|
/// events. The receiver must handle notify messages via the <see cref="Connection.NotifyReceived"/> event, <i>which is different from the other two send modes</i>.</summary>
|
|
Notify = MessageHeader.Notify,
|
|
/// <summary>Guarantees neither delivery nor order.</summary>
|
|
Unreliable = MessageHeader.Unreliable,
|
|
/// <summary>Guarantees delivery but not order.</summary>
|
|
Reliable = MessageHeader.Reliable,
|
|
}
|
|
|
|
/// <summary>Provides functionality for converting data to bytes and vice versa.</summary>
|
|
public class Message
|
|
{
|
|
/// <summary>The maximum number of bits required for a message's header.</summary>
|
|
public const int MaxHeaderSize = NotifyHeaderBits;
|
|
/// <summary>The number of bits used by the <see cref="MessageHeader"/>.</summary>
|
|
internal const int HeaderBits = 4;
|
|
/// <summary>A bitmask that, when applied, only keeps the bits corresponding to the <see cref="MessageHeader"/> value.</summary>
|
|
internal const byte HeaderBitmask = (1 << HeaderBits) - 1;
|
|
/// <summary>The header size for unreliable messages. Does not count the 2 bytes used for the message ID.</summary>
|
|
/// <remarks>4 bits - header.</remarks>
|
|
internal const int UnreliableHeaderBits = HeaderBits;
|
|
/// <summary>The header size for reliable messages. Does not count the 2 bytes used for the message ID.</summary>
|
|
/// <remarks>4 bits - header, 16 bits - sequence ID.</remarks>
|
|
internal const int ReliableHeaderBits = HeaderBits + 2 * BitsPerByte;
|
|
/// <summary>The header size for notify messages.</summary>
|
|
/// <remarks>4 bits - header, 24 bits - ack, 16 bits - sequence ID.</remarks>
|
|
internal const int NotifyHeaderBits = HeaderBits + 5 * BitsPerByte;
|
|
/// <summary>The minimum number of bytes contained in an unreliable message.</summary>
|
|
internal const int MinUnreliableBytes = UnreliableHeaderBits / BitsPerByte + (UnreliableHeaderBits % BitsPerByte == 0 ? 0 : 1);
|
|
/// <summary>The minimum number of bytes contained in a reliable message.</summary>
|
|
internal const int MinReliableBytes = ReliableHeaderBits / BitsPerByte + (ReliableHeaderBits % BitsPerByte == 0 ? 0 : 1);
|
|
/// <summary>The minimum number of bytes contained in a notify message.</summary>
|
|
internal const int MinNotifyBytes = NotifyHeaderBits / BitsPerByte + (NotifyHeaderBits % BitsPerByte == 0 ? 0 : 1);
|
|
/// <summary>The number of bits in a byte.</summary>
|
|
private const int BitsPerByte = Converter.BitsPerByte;
|
|
/// <summary>The number of bits in each data segment.</summary>
|
|
private const int BitsPerSegment = Converter.BitsPerULong;
|
|
|
|
/// <summary>The maximum number of bytes that a message can contain, including the <see cref="MaxHeaderSize"/>.</summary>
|
|
public static int MaxSize { get; private set; }
|
|
/// <summary>The maximum number of bytes of payload data that a message can contain. This value represents how many bytes can be added to a message <i>on top of</i> the <see cref="MaxHeaderSize"/>.</summary>
|
|
public static int MaxPayloadSize
|
|
{
|
|
get => MaxSize - (MaxHeaderSize / BitsPerByte + (MaxHeaderSize % BitsPerByte == 0 ? 0 : 1));
|
|
set
|
|
{
|
|
if (Peer.ActiveCount > 0)
|
|
throw new InvalidOperationException($"Changing the '{nameof(MaxPayloadSize)}' is not allowed while a {nameof(Server)} or {nameof(Client)} is running!");
|
|
|
|
if (value < 0)
|
|
throw new ArgumentOutOfRangeException(nameof(value), $"'{nameof(MaxPayloadSize)}' cannot be negative!");
|
|
|
|
MaxSize = MaxHeaderSize / BitsPerByte + (MaxHeaderSize % BitsPerByte == 0 ? 0 : 1) + value;
|
|
maxBitCount = MaxSize * BitsPerByte;
|
|
maxArraySize = MaxSize / sizeof(ulong) + (MaxSize % sizeof(ulong) == 0 ? 0 : 1);
|
|
ByteBuffer = new byte[MaxSize];
|
|
TrimPool(); // When ActiveSocketCount is 0, this clears the pool
|
|
PendingMessage.ClearPool();
|
|
}
|
|
}
|
|
/// <summary>An intermediary buffer to help convert <see cref="data"/> to a byte array when sending.</summary>
|
|
internal static byte[] ByteBuffer;
|
|
/// <summary>The maximum number of bits a message can contain.</summary>
|
|
private static int maxBitCount;
|
|
/// <summary>The maximum size of the <see cref="data"/> array.</summary>
|
|
private static int maxArraySize;
|
|
|
|
/// <summary>How many messages to add to the pool for each <see cref="Server"/> or <see cref="Client"/> instance that is started.</summary>
|
|
/// <remarks>Changes will not affect <see cref="Server"/> and <see cref="Client"/> instances which are already running until they are restarted.</remarks>
|
|
public static byte InstancesPerPeer { get; set; } = 4;
|
|
/// <summary>A pool of reusable message instances.</summary>
|
|
private static readonly List<Message> pool = new List<Message>(InstancesPerPeer * 2);
|
|
|
|
static Message()
|
|
{
|
|
MaxSize = MaxHeaderSize / BitsPerByte + (MaxHeaderSize % BitsPerByte == 0 ? 0 : 1) + 1225;
|
|
maxBitCount = MaxSize * BitsPerByte;
|
|
maxArraySize = MaxSize / sizeof(ulong) + (MaxSize % sizeof(ulong) == 0 ? 0 : 1);
|
|
ByteBuffer = new byte[MaxSize];
|
|
}
|
|
|
|
/// <summary>The message's send mode.</summary>
|
|
public MessageSendMode SendMode { get; private set; }
|
|
/// <summary>How many bits have been retrieved from the message.</summary>
|
|
public int ReadBits => readBit;
|
|
/// <summary>How many unretrieved bits remain in the message.</summary>
|
|
public int UnreadBits => writeBit - readBit;
|
|
/// <summary>How many bits have been added to the message.</summary>
|
|
public int WrittenBits => writeBit;
|
|
/// <summary>How many more bits can be added to the message.</summary>
|
|
public int UnwrittenBits => maxBitCount - writeBit;
|
|
/// <summary>How many of this message's bytes are in use. Rounds up to the next byte because only whole bytes can be sent.</summary>
|
|
public int BytesInUse => writeBit / BitsPerByte + (writeBit % BitsPerByte == 0 ? 0 : 1);
|
|
/// <summary>How many bytes have been retrieved from the message.</summary>
|
|
[Obsolete("Use ReadBits instead.")] public int ReadLength => ReadBits / BitsPerByte + (ReadBits % BitsPerByte == 0 ? 0 : 1);
|
|
/// <summary>How many more bytes can be retrieved from the message.</summary>
|
|
[Obsolete("Use UnreadBits instead.")] public int UnreadLength => UnreadBits / BitsPerByte + (UnreadBits % BitsPerByte == 0 ? 0 : 1);
|
|
/// <summary>How many bytes have been added to the message.</summary>
|
|
[Obsolete("Use WrittenBits instead.")] public int WrittenLength => WrittenBits / BitsPerByte + (WrittenBits % BitsPerByte == 0 ? 0 : 1);
|
|
/// <inheritdoc cref="data"/>
|
|
internal ulong[] Data => data;
|
|
|
|
/// <summary>The message's data.</summary>
|
|
private readonly ulong[] data;
|
|
/// <summary>The next bit to be read.</summary>
|
|
private int readBit;
|
|
/// <summary>The next bit to be written.</summary>
|
|
private int writeBit;
|
|
|
|
/// <summary>Initializes a reusable <see cref="Message"/> instance.</summary>
|
|
private Message() => data = new ulong[maxArraySize];
|
|
|
|
/// <summary>Gets a completely empty message instance with no header.</summary>
|
|
/// <returns>An empty message instance.</returns>
|
|
public static Message Create()
|
|
{
|
|
Message message = RetrieveFromPool();
|
|
message.readBit = 0;
|
|
message.writeBit = 0;
|
|
return message;
|
|
}
|
|
/// <summary>Gets a message instance that can be used for sending.</summary>
|
|
/// <param name="sendMode">The mode in which the message should be sent.</param>
|
|
/// <returns>A message instance ready to be sent.</returns>
|
|
/// <remarks>This method is primarily intended for use with <see cref="MessageSendMode.Notify"/> as notify messages don't have a built-in message ID, and unlike
|
|
/// <see cref="Create(MessageSendMode, ushort)"/> and <see cref="Create(MessageSendMode, Enum)"/>, this overload does not add a message ID to the message.</remarks>
|
|
public static Message Create(MessageSendMode sendMode)
|
|
{
|
|
return RetrieveFromPool().Init((MessageHeader)sendMode);
|
|
}
|
|
/// <summary>Gets a message instance that can be used for sending.</summary>
|
|
/// <param name="sendMode">The mode in which the message should be sent.</param>
|
|
/// <param name="id">The message ID.</param>
|
|
/// <returns>A message instance ready to be sent.</returns>
|
|
public static Message Create(MessageSendMode sendMode, ushort id)
|
|
{
|
|
return RetrieveFromPool().Init((MessageHeader)sendMode).AddVarULong(id);
|
|
}
|
|
/// <inheritdoc cref="Create(MessageSendMode, ushort)"/>
|
|
/// <remarks>NOTE: <paramref name="id"/> will be cast to a <see cref="ushort"/>. You should ensure that its value never exceeds that of <see cref="ushort.MaxValue"/>, otherwise you'll encounter unexpected behaviour when handling messages.</remarks>
|
|
public static Message Create(MessageSendMode sendMode, Enum id)
|
|
{
|
|
return Create(sendMode, (ushort)(object)id);
|
|
}
|
|
/// <summary>Gets a message instance that can be used for sending.</summary>
|
|
/// <param name="header">The message's header type.</param>
|
|
/// <returns>A message instance ready to be sent.</returns>
|
|
internal static Message Create(MessageHeader header)
|
|
{
|
|
return RetrieveFromPool().Init(header);
|
|
}
|
|
|
|
#region Pooling
|
|
/// <summary>Trims the message pool to a more appropriate size for how many <see cref="Server"/> and/or <see cref="Client"/> instances are currently running.</summary>
|
|
public static void TrimPool()
|
|
{
|
|
if (Peer.ActiveCount == 0)
|
|
{
|
|
// No Servers or Clients are running, empty the list and reset the capacity
|
|
pool.Clear();
|
|
pool.Capacity = InstancesPerPeer * 2; // x2 so there's some buffer room for extra Message instances in the event that more are needed
|
|
}
|
|
else
|
|
{
|
|
// Reset the pool capacity and number of Message instances in the pool to what is appropriate for how many Servers & Clients are active
|
|
int idealInstanceAmount = Peer.ActiveCount * InstancesPerPeer;
|
|
if (pool.Count > idealInstanceAmount)
|
|
{
|
|
pool.RemoveRange(Peer.ActiveCount * InstancesPerPeer, pool.Count - idealInstanceAmount);
|
|
pool.Capacity = idealInstanceAmount * 2;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>Retrieves a message instance from the pool. If none is available, a new instance is created.</summary>
|
|
/// <returns>A message instance ready to be used for sending or handling.</returns>
|
|
private static Message RetrieveFromPool()
|
|
{
|
|
Message message;
|
|
if (pool.Count > 0)
|
|
{
|
|
message = pool[0];
|
|
pool.RemoveAt(0);
|
|
}
|
|
else
|
|
message = new Message();
|
|
|
|
return message;
|
|
}
|
|
|
|
/// <summary>Returns the message instance to the internal pool so it can be reused.</summary>
|
|
public void Release()
|
|
{
|
|
if (pool.Count < pool.Capacity)
|
|
{
|
|
// Pool exists and there's room
|
|
if (!pool.Contains(this))
|
|
pool.Add(this); // Only add it if it's not already in the list, otherwise this method being called twice in a row for whatever reason could cause *serious* issues
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Functions
|
|
/// <summary>Initializes the message so that it can be used for sending.</summary>
|
|
/// <param name="header">The message's header type.</param>
|
|
/// <returns>The message, ready to be used for sending.</returns>
|
|
private Message Init(MessageHeader header)
|
|
{
|
|
data[0] = (byte)header;
|
|
SetHeader(header);
|
|
return this;
|
|
}
|
|
/// <summary>Initializes the message so that it can be used for receiving/handling.</summary>
|
|
/// <param name="firstByte">The first byte of the received data.</param>
|
|
/// <param name="header">The message's header type.</param>
|
|
/// <param name="contentLength">The number of bytes which this message will contain.</param>
|
|
/// <returns>The message, ready to be used for handling.</returns>
|
|
internal Message Init(byte firstByte, int contentLength, out MessageHeader header)
|
|
{
|
|
data[0] = firstByte;
|
|
header = (MessageHeader)(firstByte & HeaderBitmask);
|
|
SetHeader(header);
|
|
writeBit = contentLength * BitsPerByte;
|
|
return this;
|
|
}
|
|
|
|
/// <summary>Sets the message's header bits to the given <paramref name="header"/> and determines the appropriate <see cref="MessageSendMode"/> and read/write positions.</summary>
|
|
/// <param name="header">The header to use for this message.</param>
|
|
private void SetHeader(MessageHeader header)
|
|
{
|
|
if (header == MessageHeader.Notify)
|
|
{
|
|
readBit = NotifyHeaderBits;
|
|
writeBit = NotifyHeaderBits;
|
|
SendMode = MessageSendMode.Notify;
|
|
}
|
|
else if (header >= MessageHeader.Reliable)
|
|
{
|
|
readBit = ReliableHeaderBits;
|
|
writeBit = ReliableHeaderBits;
|
|
SendMode = MessageSendMode.Reliable;
|
|
}
|
|
else
|
|
{
|
|
readBit = UnreliableHeaderBits;
|
|
writeBit = UnreliableHeaderBits;
|
|
SendMode = MessageSendMode.Unreliable;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Add & Retrieve Data
|
|
#region Message
|
|
/// <summary>Adds <paramref name="message"/>'s unread bits to the message.</summary>
|
|
/// <param name="message">The message whose unread bits to add.</param>
|
|
/// <returns>The message that the bits were added to.</returns>
|
|
/// <remarks>This method does not move <paramref name="message"/>'s internal read position!</remarks>
|
|
public Message AddMessage(Message message) => AddMessage(message, message.UnreadBits, message.readBit);
|
|
/// <summary>Adds a range of bits from <paramref name="message"/> to the message.</summary>
|
|
/// <param name="message">The message whose bits to add.</param>
|
|
/// <param name="amount">The number of bits to add.</param>
|
|
/// <param name="startBit">The position in <paramref name="message"/> from which to add the bits.</param>
|
|
/// <returns>The message that the bits were added to.</returns>
|
|
/// <remarks>This method does not move <paramref name="message"/>'s internal read position!</remarks>
|
|
public Message AddMessage(Message message, int amount, int startBit)
|
|
{
|
|
if (UnwrittenBits < amount)
|
|
throw new InsufficientCapacityException(this, nameof(Message), amount);
|
|
|
|
int sourcePos = startBit / BitsPerSegment;
|
|
int sourceBit = startBit % BitsPerSegment;
|
|
int destPos = writeBit / BitsPerSegment;
|
|
int destBit = writeBit % BitsPerSegment;
|
|
int bitOffset = destBit - sourceBit;
|
|
int destSegments = (writeBit + amount) / BitsPerSegment - destPos + 1;
|
|
|
|
if (bitOffset == 0)
|
|
{
|
|
// Source doesn't need to be shifted, source and dest bits span the same number of segments
|
|
ulong firstSegment = message.data[sourcePos];
|
|
if (destBit == 0)
|
|
data[destPos] = firstSegment;
|
|
else
|
|
data[destPos] |= firstSegment & ~((1ul << sourceBit) - 1);
|
|
|
|
for (int i = 1; i < destSegments; i++)
|
|
data[destPos + i] = message.data[sourcePos + i];
|
|
}
|
|
else if (bitOffset > 0)
|
|
{
|
|
// Source needs to be shifted left, dest bits may span more segments than source bits
|
|
ulong firstSegment = message.data[sourcePos] & ~((1ul << sourceBit) - 1);
|
|
firstSegment <<= bitOffset;
|
|
if (destBit == 0)
|
|
data[destPos] = firstSegment;
|
|
else
|
|
data[destPos] |= firstSegment;
|
|
|
|
for (int i = 1; i < destSegments; i++)
|
|
data[destPos + i] = (message.data[sourcePos + i - 1] >> (BitsPerSegment - bitOffset)) | (message.data[sourcePos + i] << bitOffset);
|
|
}
|
|
else
|
|
{
|
|
// Source needs to be shifted right, source bits may span more segments than dest bits
|
|
bitOffset = -bitOffset;
|
|
ulong firstSegment = message.data[sourcePos] & ~((1ul << sourceBit) - 1);
|
|
firstSegment >>= bitOffset;
|
|
if (destBit == 0)
|
|
data[destPos] = firstSegment;
|
|
else
|
|
data[destPos] |= firstSegment;
|
|
|
|
int sourceSegments = (startBit + amount) / BitsPerSegment - sourcePos + 1;
|
|
for (int i = 1; i < sourceSegments; i++)
|
|
{
|
|
data[destPos + i - 1] |= message.data[sourcePos + i] << (BitsPerSegment - bitOffset);
|
|
data[destPos + i ] = message.data[sourcePos + i] >> bitOffset;
|
|
}
|
|
}
|
|
|
|
writeBit += amount;
|
|
data[destPos + destSegments - 1] &= (1ul << (writeBit % BitsPerSegment)) - 1;
|
|
return this;
|
|
}
|
|
#endregion
|
|
|
|
#region Bits
|
|
/// <summary>Moves the message's internal write position by the given <paramref name="amount"/> of bits, reserving them so they can be set at a later time.</summary>
|
|
/// <param name="amount">The number of bits to reserve.</param>
|
|
/// <returns>The message instance.</returns>
|
|
public Message ReserveBits(int amount)
|
|
{
|
|
if (UnwrittenBits < amount)
|
|
throw new InsufficientCapacityException(this, amount);
|
|
|
|
int bit = writeBit % BitsPerSegment;
|
|
writeBit += amount;
|
|
|
|
// Reset the last segment that the reserved range touches, unless it's also the first one, in which case it may already contain data which we don't want to overwrite
|
|
if (bit + amount >= BitsPerSegment)
|
|
data[writeBit / BitsPerSegment] = 0;
|
|
|
|
return this;
|
|
}
|
|
|
|
/// <summary>Moves the message's internal read position by the given <paramref name="amount"/> of bits, skipping over them.</summary>
|
|
/// <param name="amount">The number of bits to skip.</param>
|
|
/// <returns>The message instance.</returns>
|
|
public Message SkipBits(int amount)
|
|
{
|
|
if (UnreadBits < amount)
|
|
RiptideLogger.Log(LogType.Error, $"Message only contains {UnreadBits} unread {Helper.CorrectForm(UnreadBits, "bit")}, which is not enough to skip {amount}!");
|
|
|
|
readBit += amount;
|
|
return this;
|
|
}
|
|
|
|
/// <summary>Sets up to 64 bits at the specified position in the message.</summary>
|
|
/// <param name="bitfield">The bits to write into the message.</param>
|
|
/// <param name="amount">The number of bits to set.</param>
|
|
/// <param name="startBit">The bit position in the message at which to start writing.</param>
|
|
/// <returns>The message instance.</returns>
|
|
/// <remarks>This method can be used to directly set a range of bits anywhere in the message without moving its internal write position. Data which was previously added to
|
|
/// the message and which falls within the range of bits being set will be <i>overwritten</i>, meaning that improper use of this method will likely corrupt the message!</remarks>
|
|
public Message SetBits(ulong bitfield, int amount, int startBit)
|
|
{
|
|
if (amount > sizeof(ulong) * BitsPerByte)
|
|
throw new ArgumentOutOfRangeException(nameof(amount), $"Cannot set more than {sizeof(ulong) * BitsPerByte} bits at a time!");
|
|
|
|
Converter.SetBits(bitfield, amount, data, startBit);
|
|
return this;
|
|
}
|
|
|
|
/// <summary>Retrieves up to 8 bits from the specified position in the message.</summary>
|
|
/// <param name="amount">The number of bits to peek.</param>
|
|
/// <param name="startBit">The bit position in the message at which to start peeking.</param>
|
|
/// <param name="bitfield">The bits that were retrieved.</param>
|
|
/// <returns>The message instance.</returns>
|
|
/// <remarks>This method can be used to retrieve a range of bits from anywhere in the message without moving its internal read position.</remarks>
|
|
public Message PeekBits(int amount, int startBit, out byte bitfield)
|
|
{
|
|
if (amount > BitsPerByte)
|
|
throw new ArgumentOutOfRangeException(nameof(amount), $"This '{nameof(PeekBits)}' overload cannot be used to peek more than {BitsPerByte} bits at a time!");
|
|
|
|
Converter.GetBits(amount, data, startBit, out bitfield);
|
|
return this;
|
|
}
|
|
/// <summary>Retrieves up to 16 bits from the specified position in the message.</summary>
|
|
/// <inheritdoc cref="PeekBits(int, int, out byte)"/>
|
|
public Message PeekBits(int amount, int startBit, out ushort bitfield)
|
|
{
|
|
if (amount > sizeof(ushort) * BitsPerByte)
|
|
throw new ArgumentOutOfRangeException(nameof(amount), $"This '{nameof(PeekBits)}' overload cannot be used to peek more than {sizeof(ushort) * BitsPerByte} bits at a time!");
|
|
|
|
Converter.GetBits(amount, data, startBit, out bitfield);
|
|
return this;
|
|
}
|
|
/// <summary>Retrieves up to 32 bits from the specified position in the message.</summary>
|
|
/// <inheritdoc cref="PeekBits(int, int, out byte)"/>
|
|
public Message PeekBits(int amount, int startBit, out uint bitfield)
|
|
{
|
|
if (amount > sizeof(uint) * BitsPerByte)
|
|
throw new ArgumentOutOfRangeException(nameof(amount), $"This '{nameof(PeekBits)}' overload cannot be used to peek more than {sizeof(uint) * BitsPerByte} bits at a time!");
|
|
|
|
Converter.GetBits(amount, data, startBit, out bitfield);
|
|
return this;
|
|
}
|
|
/// <summary>Retrieves up to 64 bits from the specified position in the message.</summary>
|
|
/// <inheritdoc cref="PeekBits(int, int, out byte)"/>
|
|
public Message PeekBits(int amount, int startBit, out ulong bitfield)
|
|
{
|
|
if (amount > sizeof(ulong) * BitsPerByte)
|
|
throw new ArgumentOutOfRangeException(nameof(amount), $"This '{nameof(PeekBits)}' overload cannot be used to peek more than {sizeof(ulong) * BitsPerByte} bits at a time!");
|
|
|
|
Converter.GetBits(amount, data, startBit, out bitfield);
|
|
return this;
|
|
}
|
|
|
|
/// <summary>Adds up to 8 of the given bits to the message.</summary>
|
|
/// <param name="bitfield">The bits to add.</param>
|
|
/// <param name="amount">The number of bits to add.</param>
|
|
/// <returns>The message that the bits were added to.</returns>
|
|
public Message AddBits(byte bitfield, int amount)
|
|
{
|
|
if (amount > BitsPerByte)
|
|
throw new ArgumentOutOfRangeException(nameof(amount), $"This '{nameof(AddBits)}' overload cannot be used to add more than {BitsPerByte} bits at a time!");
|
|
|
|
bitfield &= (byte)((1 << amount) - 1); // Discard any bits that are set beyond the ones we're setting
|
|
Converter.ByteToBits(bitfield, data, writeBit);
|
|
writeBit += amount;
|
|
return this;
|
|
}
|
|
/// <summary>Adds up to 16 of the given bits to the message.</summary>
|
|
/// <inheritdoc cref="AddBits(byte, int)"/>
|
|
public Message AddBits(ushort bitfield, int amount)
|
|
{
|
|
if (amount > sizeof(ushort) * BitsPerByte)
|
|
throw new ArgumentOutOfRangeException(nameof(amount), $"This '{nameof(AddBits)}' overload cannot be used to add more than {sizeof(ushort) * BitsPerByte} bits at a time!");
|
|
|
|
bitfield &= (ushort)((1 << amount) - 1); // Discard any bits that are set beyond the ones we're adding
|
|
Converter.UShortToBits(bitfield, data, writeBit);
|
|
writeBit += amount;
|
|
return this;
|
|
}
|
|
/// <summary>Adds up to 32 of the given bits to the message.</summary>
|
|
/// <inheritdoc cref="AddBits(byte, int)"/>
|
|
public Message AddBits(uint bitfield, int amount)
|
|
{
|
|
if (amount > sizeof(uint) * BitsPerByte)
|
|
throw new ArgumentOutOfRangeException(nameof(amount), $"This '{nameof(AddBits)}' overload cannot be used to add more than {sizeof(uint) * BitsPerByte} bits at a time!");
|
|
|
|
bitfield &= (1u << (amount - 1) << 1) - 1; // Discard any bits that are set beyond the ones we're adding
|
|
Converter.UIntToBits(bitfield, data, writeBit);
|
|
writeBit += amount;
|
|
return this;
|
|
}
|
|
/// <summary>Adds up to 64 of the given bits to the message.</summary>
|
|
/// <inheritdoc cref="AddBits(byte, int)"/>
|
|
public Message AddBits(ulong bitfield, int amount)
|
|
{
|
|
if (amount > sizeof(ulong) * BitsPerByte)
|
|
throw new ArgumentOutOfRangeException(nameof(amount), $"This '{nameof(AddBits)}' overload cannot be used to add more than {sizeof(ulong) * BitsPerByte} bits at a time!");
|
|
|
|
bitfield &= (1ul << (amount - 1) << 1) - 1; // Discard any bits that are set beyond the ones we're adding
|
|
Converter.ULongToBits(bitfield, data, writeBit);
|
|
writeBit += amount;
|
|
return this;
|
|
}
|
|
|
|
/// <summary>Retrieves the next <paramref name="amount"/> bits (up to 8) from the message.</summary>
|
|
/// <param name="amount">The number of bits to retrieve.</param>
|
|
/// <param name="bitfield">The bits that were retrieved.</param>
|
|
/// <returns>The messages that the bits were retrieved from.</returns>
|
|
public Message GetBits(int amount, out byte bitfield)
|
|
{
|
|
PeekBits(amount, readBit, out bitfield);
|
|
readBit += amount;
|
|
return this;
|
|
}
|
|
/// <summary>Retrieves the next <paramref name="amount"/> bits (up to 16) from the message.</summary>
|
|
/// <inheritdoc cref="GetBits(int, out byte)"/>
|
|
public Message GetBits(int amount, out ushort bitfield)
|
|
{
|
|
PeekBits(amount, readBit, out bitfield);
|
|
readBit += amount;
|
|
return this;
|
|
}
|
|
/// <summary>Retrieves the next <paramref name="amount"/> bits (up to 32) from the message.</summary>
|
|
/// <inheritdoc cref="GetBits(int, out byte)"/>
|
|
public Message GetBits(int amount, out uint bitfield)
|
|
{
|
|
PeekBits(amount, readBit, out bitfield);
|
|
readBit += amount;
|
|
return this;
|
|
}
|
|
/// <summary>Retrieves the next <paramref name="amount"/> bits (up to 64) from the message.</summary>
|
|
/// <inheritdoc cref="GetBits(int, out byte)"/>
|
|
public Message GetBits(int amount, out ulong bitfield)
|
|
{
|
|
PeekBits(amount, readBit, out bitfield);
|
|
readBit += amount;
|
|
return this;
|
|
}
|
|
#endregion
|
|
|
|
#region Varint
|
|
/// <summary>Adds a positive or negative number to the message, using fewer bits for smaller values.</summary>
|
|
/// <inheritdoc cref="AddVarULong(ulong)"/>
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public Message AddVarLong(long value) => AddVarULong((ulong)Converter.ZigZagEncode(value));
|
|
/// <summary>Adds a positive number to the message, using fewer bits for smaller values.</summary>
|
|
/// <param name="value">The value to add.</param>
|
|
/// <returns>The message that the value was added to.</returns>
|
|
/// <remarks>The value is added in segments of 8 bits, 1 of which is used to indicate whether or not another segment follows. As a result, small values are
|
|
/// added to the message using fewer bits, while large values will require a few more bits than they would if they were added via <see cref="AddByte(byte)"/>,
|
|
/// <see cref="AddUShort(ushort)"/>, <see cref="AddUInt"/>, or <see cref="AddULong(ulong)"/> (or their signed counterparts).</remarks>
|
|
public Message AddVarULong(ulong value)
|
|
{
|
|
do
|
|
{
|
|
byte byteValue = (byte)(value & 0b01111111);
|
|
value >>= 7;
|
|
if (value != 0) // There's more to write
|
|
byteValue |= 0b10000000;
|
|
|
|
AddByte(byteValue);
|
|
}
|
|
while (value != 0);
|
|
|
|
return this;
|
|
}
|
|
|
|
/// <summary>Retrieves a positive or negative number from the message, using fewer bits for smaller values.</summary>
|
|
/// <inheritdoc cref="GetVarULong()"/>
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public long GetVarLong() => Converter.ZigZagDecode((long)GetVarULong());
|
|
/// <summary>Retrieves a positive number from the message, using fewer bits for smaller values.</summary>
|
|
/// <returns>The value that was retrieved.</returns>
|
|
/// <remarks>The value is retrieved in segments of 8 bits, 1 of which is used to indicate whether or not another segment follows. As a result, small values are
|
|
/// retrieved from the message using fewer bits, while large values will require a few more bits than they would if they were retrieved via <see cref="GetByte"/>,
|
|
/// <see cref="GetUShort"/>, <see cref="GetUInt"/>, or <see cref="GetULong"/> (or their signed counterparts).</remarks>
|
|
public ulong GetVarULong()
|
|
{
|
|
ulong byteValue;
|
|
ulong value = 0;
|
|
int shift = 0;
|
|
|
|
do
|
|
{
|
|
byteValue = GetByte();
|
|
value |= (byteValue & 0b01111111) << shift;
|
|
shift += 7;
|
|
}
|
|
while ((byteValue & 0b10000000) != 0);
|
|
|
|
return value;
|
|
}
|
|
#endregion
|
|
|
|
#region Byte & SByte
|
|
/// <summary>Adds a <see cref="byte"/> to the message.</summary>
|
|
/// <param name="value">The <see cref="byte"/> to add.</param>
|
|
/// <returns>The message that the <see cref="byte"/> was added to.</returns>
|
|
public Message AddByte(byte value)
|
|
{
|
|
if (UnwrittenBits < BitsPerByte)
|
|
throw new InsufficientCapacityException(this, ByteName, BitsPerByte);
|
|
|
|
Converter.ByteToBits(value, data, writeBit);
|
|
writeBit += BitsPerByte;
|
|
return this;
|
|
}
|
|
|
|
/// <summary>Adds an <see cref="sbyte"/> to the message.</summary>
|
|
/// <param name="value">The <see cref="sbyte"/> to add.</param>
|
|
/// <returns>The message that the <see cref="sbyte"/> was added to.</returns>
|
|
public Message AddSByte(sbyte value)
|
|
{
|
|
if (UnwrittenBits < BitsPerByte)
|
|
throw new InsufficientCapacityException(this, SByteName, BitsPerByte);
|
|
|
|
Converter.SByteToBits(value, data, writeBit);
|
|
writeBit += BitsPerByte;
|
|
return this;
|
|
}
|
|
|
|
/// <summary>Retrieves a <see cref="byte"/> from the message.</summary>
|
|
/// <returns>The <see cref="byte"/> that was retrieved.</returns>
|
|
public byte GetByte()
|
|
{
|
|
if (UnreadBits < BitsPerByte)
|
|
{
|
|
RiptideLogger.Log(LogType.Error, NotEnoughBitsError(ByteName, $"{default(byte)}"));
|
|
return default(byte);
|
|
}
|
|
|
|
byte value = Converter.ByteFromBits(data, readBit);
|
|
readBit += BitsPerByte;
|
|
return value;
|
|
}
|
|
|
|
/// <summary>Retrieves an <see cref="sbyte"/> from the message.</summary>
|
|
/// <returns>The <see cref="sbyte"/> that was retrieved.</returns>
|
|
public sbyte GetSByte()
|
|
{
|
|
if (UnreadBits < BitsPerByte)
|
|
{
|
|
RiptideLogger.Log(LogType.Error, NotEnoughBitsError(SByteName, $"{default(sbyte)}"));
|
|
return default(sbyte);
|
|
}
|
|
|
|
sbyte value = Converter.SByteFromBits(data, readBit);
|
|
readBit += BitsPerByte;
|
|
return value;
|
|
}
|
|
|
|
/// <summary>Adds a <see cref="byte"/> array to the message.</summary>
|
|
/// <param name="array">The array to add.</param>
|
|
/// <param name="includeLength">Whether or not to include the length of the array in the message.</param>
|
|
/// <returns>The message that the array was added to.</returns>
|
|
public Message AddBytes(byte[] array, bool includeLength = true)
|
|
{
|
|
if (includeLength)
|
|
AddVarULong((uint)array.Length);
|
|
|
|
if (UnwrittenBits < array.Length * BitsPerByte)
|
|
throw new InsufficientCapacityException(this, array.Length, ByteName, BitsPerByte);
|
|
|
|
if (writeBit % BitsPerByte == 0)
|
|
{
|
|
Buffer.BlockCopy(array, 0, data, writeBit / BitsPerByte, array.Length);
|
|
writeBit += array.Length * BitsPerByte;
|
|
}
|
|
else
|
|
{
|
|
for (int i = 0; i < array.Length; i++)
|
|
{
|
|
Converter.ByteToBits(array[i], data, writeBit);
|
|
writeBit += BitsPerByte;
|
|
}
|
|
}
|
|
|
|
return this;
|
|
}
|
|
|
|
/// <summary>Adds an <see cref="sbyte"/> array to the message.</summary>
|
|
/// <param name="array">The array to add.</param>
|
|
/// <param name="includeLength">Whether or not to include the length of the array in the message.</param>
|
|
/// <returns>The message that the array was added to.</returns>
|
|
public Message AddSBytes(sbyte[] array, bool includeLength = true)
|
|
{
|
|
if (includeLength)
|
|
AddVarULong((uint)array.Length);
|
|
|
|
if (UnwrittenBits < array.Length * BitsPerByte)
|
|
throw new InsufficientCapacityException(this, array.Length, SByteName, BitsPerByte);
|
|
|
|
for (int i = 0; i < array.Length; i++)
|
|
{
|
|
Converter.SByteToBits(array[i], data, writeBit);
|
|
writeBit += BitsPerByte;
|
|
}
|
|
|
|
return this;
|
|
}
|
|
|
|
/// <summary>Retrieves a <see cref="byte"/> array from the message.</summary>
|
|
/// <returns>The array that was retrieved.</returns>
|
|
public byte[] GetBytes() => GetBytes((int)GetVarULong());
|
|
/// <summary>Retrieves a <see cref="byte"/> array from the message.</summary>
|
|
/// <param name="amount">The amount of bytes to retrieve.</param>
|
|
/// <returns>The array that was retrieved.</returns>
|
|
public byte[] GetBytes(int amount)
|
|
{
|
|
byte[] array = new byte[amount];
|
|
ReadBytes(amount, array);
|
|
return array;
|
|
}
|
|
/// <summary>Populates a <see cref="byte"/> array with bytes retrieved from the message.</summary>
|
|
/// <param name="amount">The amount of bytes to retrieve.</param>
|
|
/// <param name="intoArray">The array to populate.</param>
|
|
/// <param name="startIndex">The position at which to start populating the array.</param>
|
|
public void GetBytes(int amount, byte[] intoArray, int startIndex = 0)
|
|
{
|
|
if (startIndex + amount > intoArray.Length)
|
|
throw new ArgumentException(nameof(amount), ArrayNotLongEnoughError(amount, intoArray.Length, startIndex, ByteName));
|
|
|
|
ReadBytes(amount, intoArray, startIndex);
|
|
}
|
|
|
|
/// <summary>Retrieves an <see cref="sbyte"/> array from the message.</summary>
|
|
/// <returns>The array that was retrieved.</returns>
|
|
public sbyte[] GetSBytes() => GetSBytes((int)GetVarULong());
|
|
/// <summary>Retrieves an <see cref="sbyte"/> array from the message.</summary>
|
|
/// <param name="amount">The amount of sbytes to retrieve.</param>
|
|
/// <returns>The array that was retrieved.</returns>
|
|
public sbyte[] GetSBytes(int amount)
|
|
{
|
|
sbyte[] array = new sbyte[amount];
|
|
ReadSBytes(amount, array);
|
|
return array;
|
|
}
|
|
/// <summary>Populates a <see cref="sbyte"/> array with bytes retrieved from the message.</summary>
|
|
/// <param name="amount">The amount of sbytes to retrieve.</param>
|
|
/// <param name="intArray">The array to populate.</param>
|
|
/// <param name="startIndex">The position at which to start populating <paramref name="intArray"/>.</param>
|
|
public void GetSBytes(int amount, sbyte[] intArray, int startIndex = 0)
|
|
{
|
|
if (startIndex + amount > intArray.Length)
|
|
throw new ArgumentException(nameof(amount), ArrayNotLongEnoughError(amount, intArray.Length, startIndex, SByteName));
|
|
|
|
ReadSBytes(amount, intArray, startIndex);
|
|
}
|
|
|
|
/// <summary>Reads a number of bytes from the message and writes them into the given array.</summary>
|
|
/// <param name="amount">The amount of bytes to read.</param>
|
|
/// <param name="intoArray">The array to write the bytes into.</param>
|
|
/// <param name="startIndex">The position at which to start writing into the array.</param>
|
|
private void ReadBytes(int amount, byte[] intoArray, int startIndex = 0)
|
|
{
|
|
if (UnreadBits < amount * BitsPerByte)
|
|
{
|
|
RiptideLogger.Log(LogType.Error, NotEnoughBitsError(amount, ByteName));
|
|
amount = UnreadBits / BitsPerByte;
|
|
}
|
|
|
|
if (readBit % BitsPerByte == 0)
|
|
{
|
|
Buffer.BlockCopy(data, readBit / BitsPerByte, intoArray, startIndex, amount);
|
|
readBit += amount * BitsPerByte;
|
|
}
|
|
else
|
|
{
|
|
for (int i = 0; i < amount; i++)
|
|
{
|
|
intoArray[startIndex + i] = Converter.ByteFromBits(data, readBit);
|
|
readBit += BitsPerByte;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>Reads a number of sbytes from the message and writes them into the given array.</summary>
|
|
/// <param name="amount">The amount of sbytes to read.</param>
|
|
/// <param name="intoArray">The array to write the sbytes into.</param>
|
|
/// <param name="startIndex">The position at which to start writing into the array.</param>
|
|
private void ReadSBytes(int amount, sbyte[] intoArray, int startIndex = 0)
|
|
{
|
|
if (UnreadBits < amount * BitsPerByte)
|
|
{
|
|
RiptideLogger.Log(LogType.Error, NotEnoughBitsError(amount, SByteName));
|
|
amount = UnreadBits / BitsPerByte;
|
|
}
|
|
|
|
for (int i = 0; i < amount; i++)
|
|
{
|
|
intoArray[startIndex + i] = Converter.SByteFromBits(data, readBit);
|
|
readBit += BitsPerByte;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Bool
|
|
/// <summary>Adds a <see cref="bool"/> to the message.</summary>
|
|
/// <param name="value">The <see cref="bool"/> to add.</param>
|
|
/// <returns>The message that the <see cref="bool"/> was added to.</returns>
|
|
public Message AddBool(bool value)
|
|
{
|
|
if (UnwrittenBits < 1)
|
|
throw new InsufficientCapacityException(this, BoolName, 1);
|
|
|
|
Converter.BoolToBit(value, data, writeBit++);
|
|
return this;
|
|
}
|
|
|
|
/// <summary>Retrieves a <see cref="bool"/> from the message.</summary>
|
|
/// <returns>The <see cref="bool"/> that was retrieved.</returns>
|
|
public bool GetBool()
|
|
{
|
|
if (UnreadBits < 1)
|
|
{
|
|
RiptideLogger.Log(LogType.Error, NotEnoughBitsError(BoolName, $"{default(bool)}"));
|
|
return default(bool);
|
|
}
|
|
|
|
return Converter.BoolFromBit(data, readBit++);
|
|
}
|
|
|
|
/// <summary>Adds a <see cref="bool"/> array to the message.</summary>
|
|
/// <param name="array">The array to add.</param>
|
|
/// <param name="includeLength">Whether or not to include the length of the array in the message.</param>
|
|
/// <returns>The message that the array was added to.</returns>
|
|
public Message AddBools(bool[] array, bool includeLength = true)
|
|
{
|
|
if (includeLength)
|
|
AddVarULong((uint)array.Length);
|
|
|
|
if (UnwrittenBits < array.Length)
|
|
throw new InsufficientCapacityException(this, array.Length, BoolName, 1);
|
|
|
|
for (int i = 0; i < array.Length; i++)
|
|
Converter.BoolToBit(array[i], data, writeBit++);
|
|
|
|
return this;
|
|
}
|
|
|
|
/// <summary>Retrieves a <see cref="bool"/> array from the message.</summary>
|
|
/// <returns>The array that was retrieved.</returns>
|
|
public bool[] GetBools() => GetBools((int)GetVarULong());
|
|
/// <summary>Retrieves a <see cref="bool"/> array from the message.</summary>
|
|
/// <param name="amount">The amount of bools to retrieve.</param>
|
|
/// <returns>The array that was retrieved.</returns>
|
|
public bool[] GetBools(int amount)
|
|
{
|
|
bool[] array = new bool[amount];
|
|
ReadBools(amount, array);
|
|
return array;
|
|
}
|
|
/// <summary>Populates a <see cref="bool"/> array with bools retrieved from the message.</summary>
|
|
/// <param name="amount">The amount of bools to retrieve.</param>
|
|
/// <param name="intoArray">The array to populate.</param>
|
|
/// <param name="startIndex">The position at which to start populating the array.</param>
|
|
public void GetBools(int amount, bool[] intoArray, int startIndex = 0)
|
|
{
|
|
if (startIndex + amount > intoArray.Length)
|
|
throw new ArgumentException(nameof(amount), ArrayNotLongEnoughError(amount, intoArray.Length, startIndex, BoolName));
|
|
|
|
ReadBools(amount, intoArray, startIndex);
|
|
}
|
|
|
|
/// <summary>Reads a number of bools from the message and writes them into the given array.</summary>
|
|
/// <param name="amount">The amount of bools to read.</param>
|
|
/// <param name="intoArray">The array to write the bools into.</param>
|
|
/// <param name="startIndex">The position at which to start writing into the array.</param>
|
|
private void ReadBools(int amount, bool[] intoArray, int startIndex = 0)
|
|
{
|
|
if (UnreadBits < amount)
|
|
{
|
|
RiptideLogger.Log(LogType.Error, NotEnoughBitsError(amount, BoolName));
|
|
amount = UnreadBits;
|
|
}
|
|
|
|
for (int i = 0; i < amount; i++)
|
|
intoArray[startIndex + i] = Converter.BoolFromBit(data, readBit++);
|
|
}
|
|
#endregion
|
|
|
|
#region Short & UShort
|
|
/// <summary>Adds a <see cref="short"/> to the message.</summary>
|
|
/// <param name="value">The <see cref="short"/> to add.</param>
|
|
/// <returns>The message that the <see cref="short"/> was added to.</returns>
|
|
public Message AddShort(short value)
|
|
{
|
|
if (UnwrittenBits < sizeof(short) * BitsPerByte)
|
|
throw new InsufficientCapacityException(this, ShortName, sizeof(short) * BitsPerByte);
|
|
|
|
Converter.ShortToBits(value, data, writeBit);
|
|
writeBit += sizeof(short) * BitsPerByte;
|
|
return this;
|
|
}
|
|
|
|
/// <summary>Adds a <see cref="ushort"/> to the message.</summary>
|
|
/// <param name="value">The <see cref="ushort"/> to add.</param>
|
|
/// <returns>The message that the <see cref="ushort"/> was added to.</returns>
|
|
public Message AddUShort(ushort value)
|
|
{
|
|
if (UnwrittenBits < sizeof(ushort) * BitsPerByte)
|
|
throw new InsufficientCapacityException(this, UShortName, sizeof(ushort) * BitsPerByte);
|
|
|
|
Converter.UShortToBits(value, data, writeBit);
|
|
writeBit += sizeof(ushort) * BitsPerByte;
|
|
return this;
|
|
}
|
|
|
|
/// <summary>Retrieves a <see cref="short"/> from the message.</summary>
|
|
/// <returns>The <see cref="short"/> that was retrieved.</returns>
|
|
public short GetShort()
|
|
{
|
|
if (UnreadBits < sizeof(short) * BitsPerByte)
|
|
{
|
|
RiptideLogger.Log(LogType.Error, NotEnoughBitsError(ShortName, $"{default(short)}"));
|
|
return default(short);
|
|
}
|
|
|
|
short value = Converter.ShortFromBits(data, readBit);
|
|
readBit += sizeof(short) * BitsPerByte;
|
|
return value;
|
|
}
|
|
|
|
/// <summary>Retrieves a <see cref="ushort"/> from the message.</summary>
|
|
/// <returns>The <see cref="ushort"/> that was retrieved.</returns>
|
|
public ushort GetUShort()
|
|
{
|
|
if (UnreadBits < sizeof(ushort) * BitsPerByte)
|
|
{
|
|
RiptideLogger.Log(LogType.Error, NotEnoughBitsError(UShortName, $"{default(ushort)}"));
|
|
return default(ushort);
|
|
}
|
|
|
|
ushort value = Converter.UShortFromBits(data, readBit);
|
|
readBit += sizeof(ushort) * BitsPerByte;
|
|
return value;
|
|
}
|
|
|
|
/// <summary>Adds a <see cref="short"/> array to the message.</summary>
|
|
/// <param name="array">The array to add.</param>
|
|
/// <param name="includeLength">Whether or not to include the length of the array in the message.</param>
|
|
/// <returns>The message that the array was added to.</returns>
|
|
public Message AddShorts(short[] array, bool includeLength = true)
|
|
{
|
|
if (includeLength)
|
|
AddVarULong((uint)array.Length);
|
|
|
|
if (UnwrittenBits < array.Length * sizeof(short) * BitsPerByte)
|
|
throw new InsufficientCapacityException(this, array.Length, ShortName, sizeof(short) * BitsPerByte);
|
|
|
|
for (int i = 0; i < array.Length; i++)
|
|
{
|
|
array[i] = Converter.ShortFromBits(data, readBit);
|
|
readBit += sizeof(short) * BitsPerByte;
|
|
}
|
|
|
|
return this;
|
|
}
|
|
|
|
/// <summary>Adds a <see cref="ushort"/> array to the message.</summary>
|
|
/// <param name="array">The array to add.</param>
|
|
/// <param name="includeLength">Whether or not to include the length of the array in the message.</param>
|
|
/// <returns>The message that the array was added to.</returns>
|
|
public Message AddUShorts(ushort[] array, bool includeLength = true)
|
|
{
|
|
if (includeLength)
|
|
AddVarULong((uint)array.Length);
|
|
|
|
if (UnwrittenBits < array.Length * sizeof(ushort) * BitsPerByte)
|
|
throw new InsufficientCapacityException(this, array.Length, UShortName, sizeof(ushort) * BitsPerByte);
|
|
|
|
for (int i = 0; i < array.Length; i++)
|
|
{
|
|
array[i] = Converter.UShortFromBits(data, readBit);
|
|
readBit += sizeof(ushort) * BitsPerByte;
|
|
}
|
|
|
|
return this;
|
|
}
|
|
|
|
/// <summary>Retrieves a <see cref="short"/> array from the message.</summary>
|
|
/// <returns>The array that was retrieved.</returns>
|
|
public short[] GetShorts() => GetShorts((int)GetVarULong());
|
|
/// <summary>Retrieves a <see cref="short"/> array from the message.</summary>
|
|
/// <param name="amount">The amount of shorts to retrieve.</param>
|
|
/// <returns>The array that was retrieved.</returns>
|
|
public short[] GetShorts(int amount)
|
|
{
|
|
short[] array = new short[amount];
|
|
ReadShorts(amount, array);
|
|
return array;
|
|
}
|
|
/// <summary>Populates a <see cref="short"/> array with shorts retrieved from the message.</summary>
|
|
/// <param name="amount">The amount of shorts to retrieve.</param>
|
|
/// <param name="intoArray">The array to populate.</param>
|
|
/// <param name="startIndex">The position at which to start populating the array.</param>
|
|
public void GetShorts(int amount, short[] intoArray, int startIndex = 0)
|
|
{
|
|
if (startIndex + amount > intoArray.Length)
|
|
throw new ArgumentException(nameof(amount), ArrayNotLongEnoughError(amount, intoArray.Length, startIndex, ShortName));
|
|
|
|
ReadShorts(amount, intoArray, startIndex);
|
|
}
|
|
|
|
/// <summary>Retrieves a <see cref="ushort"/> array from the message.</summary>
|
|
/// <returns>The array that was retrieved.</returns>
|
|
public ushort[] GetUShorts() => GetUShorts((int)GetVarULong());
|
|
/// <summary>Retrieves a <see cref="ushort"/> array from the message.</summary>
|
|
/// <param name="amount">The amount of ushorts to retrieve.</param>
|
|
/// <returns>The array that was retrieved.</returns>
|
|
public ushort[] GetUShorts(int amount)
|
|
{
|
|
ushort[] array = new ushort[amount];
|
|
ReadUShorts(amount, array);
|
|
return array;
|
|
}
|
|
/// <summary>Populates a <see cref="ushort"/> array with ushorts retrieved from the message.</summary>
|
|
/// <param name="amount">The amount of ushorts to retrieve.</param>
|
|
/// <param name="intoArray">The array to populate.</param>
|
|
/// <param name="startIndex">The position at which to start populating the array.</param>
|
|
public void GetUShorts(int amount, ushort[] intoArray, int startIndex = 0)
|
|
{
|
|
if (startIndex + amount > intoArray.Length)
|
|
throw new ArgumentException(nameof(amount), ArrayNotLongEnoughError(amount, intoArray.Length, startIndex, UShortName));
|
|
|
|
ReadUShorts(amount, intoArray, startIndex);
|
|
}
|
|
|
|
/// <summary>Reads a number of shorts from the message and writes them into the given array.</summary>
|
|
/// <param name="amount">The amount of shorts to read.</param>
|
|
/// <param name="intoArray">The array to write the shorts into.</param>
|
|
/// <param name="startIndex">The position at which to start writing into the array.</param>
|
|
private void ReadShorts(int amount, short[] intoArray, int startIndex = 0)
|
|
{
|
|
if (UnreadBits < amount * sizeof(short) * BitsPerByte)
|
|
{
|
|
RiptideLogger.Log(LogType.Error, NotEnoughBitsError(amount, ShortName));
|
|
amount = UnreadBits / (sizeof(short) * BitsPerByte);
|
|
}
|
|
|
|
for (int i = 0; i < amount; i++)
|
|
{
|
|
intoArray[startIndex + i] = Converter.ShortFromBits(data, readBit);
|
|
readBit += sizeof(short) * BitsPerByte;
|
|
}
|
|
}
|
|
|
|
/// <summary>Reads a number of ushorts from the message and writes them into the given array.</summary>
|
|
/// <param name="amount">The amount of ushorts to read.</param>
|
|
/// <param name="intoArray">The array to write the ushorts into.</param>
|
|
/// <param name="startIndex">The position at which to start writing into the array.</param>
|
|
private void ReadUShorts(int amount, ushort[] intoArray, int startIndex = 0)
|
|
{
|
|
if (UnreadBits < amount * sizeof(ushort) * BitsPerByte)
|
|
{
|
|
RiptideLogger.Log(LogType.Error, NotEnoughBitsError(amount, UShortName));
|
|
amount = UnreadBits / (sizeof(ushort) * BitsPerByte);
|
|
}
|
|
|
|
for (int i = 0; i < amount; i++)
|
|
{
|
|
intoArray[startIndex + i] = Converter.UShortFromBits(data, readBit);
|
|
readBit += sizeof(ushort) * BitsPerByte;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Int & UInt
|
|
/// <summary>Adds an <see cref="int"/> to the message.</summary>
|
|
/// <param name="value">The <see cref="int"/> to add.</param>
|
|
/// <returns>The message that the <see cref="int"/> was added to.</returns>
|
|
public Message AddInt(int value)
|
|
{
|
|
if (UnwrittenBits < sizeof(int) * BitsPerByte)
|
|
throw new InsufficientCapacityException(this, IntName, sizeof(int) * BitsPerByte);
|
|
|
|
Converter.IntToBits(value, data, writeBit);
|
|
writeBit += sizeof(int) * BitsPerByte;
|
|
return this;
|
|
}
|
|
|
|
/// <summary>Adds a <see cref="uint"/> to the message.</summary>
|
|
/// <param name="value">The <see cref="uint"/> to add.</param>
|
|
/// <returns>The message that the <see cref="uint"/> was added to.</returns>
|
|
public Message AddUInt(uint value)
|
|
{
|
|
if (UnwrittenBits < sizeof(uint) * BitsPerByte)
|
|
throw new InsufficientCapacityException(this, UIntName, sizeof(uint) * BitsPerByte);
|
|
|
|
Converter.UIntToBits(value, data, writeBit);
|
|
writeBit += sizeof(uint) * BitsPerByte;
|
|
return this;
|
|
}
|
|
|
|
/// <summary>Retrieves an <see cref="int"/> from the message.</summary>
|
|
/// <returns>The <see cref="int"/> that was retrieved.</returns>
|
|
public int GetInt()
|
|
{
|
|
if (UnreadBits < sizeof(int) * BitsPerByte)
|
|
{
|
|
RiptideLogger.Log(LogType.Error, NotEnoughBitsError(IntName, $"{default(int)}"));
|
|
return default(int);
|
|
}
|
|
|
|
int value = Converter.IntFromBits(data, readBit);
|
|
readBit += sizeof(int) * BitsPerByte;
|
|
return value;
|
|
}
|
|
|
|
/// <summary>Retrieves a <see cref="uint"/> from the message.</summary>
|
|
/// <returns>The <see cref="uint"/> that was retrieved.</returns>
|
|
public uint GetUInt()
|
|
{
|
|
if (UnreadBits < sizeof(uint) * BitsPerByte)
|
|
{
|
|
RiptideLogger.Log(LogType.Error, NotEnoughBitsError(UIntName, $"{default(uint)}"));
|
|
return default(uint);
|
|
}
|
|
|
|
uint value = Converter.UIntFromBits(data, readBit);
|
|
readBit += sizeof(uint) * BitsPerByte;
|
|
return value;
|
|
}
|
|
|
|
/// <summary>Adds an <see cref="int"/> array message.</summary>
|
|
/// <param name="array">The array to add.</param>
|
|
/// <param name="includeLength">Whether or not to include the length of the array in the message.</param>
|
|
/// <returns>The message that the array was added to.</returns>
|
|
public Message AddInts(int[] array, bool includeLength = true)
|
|
{
|
|
if (includeLength)
|
|
AddVarULong((uint)array.Length);
|
|
|
|
if (UnwrittenBits < array.Length * sizeof(int) * BitsPerByte)
|
|
throw new InsufficientCapacityException(this, array.Length, IntName, sizeof(int) * BitsPerByte);
|
|
|
|
for (int i = 0; i < array.Length; i++)
|
|
{
|
|
Converter.IntToBits(array[i], data, writeBit);
|
|
writeBit += sizeof(int) * BitsPerByte;
|
|
}
|
|
|
|
return this;
|
|
}
|
|
|
|
/// <summary>Adds a <see cref="uint"/> array to the message.</summary>
|
|
/// <param name="array">The array to add.</param>
|
|
/// <param name="includeLength">Whether or not to include the length of the array in the message.</param>
|
|
/// <returns>The message that the array was added to.</returns>
|
|
public Message AddUInts(uint[] array, bool includeLength = true)
|
|
{
|
|
if (includeLength)
|
|
AddVarULong((uint)array.Length);
|
|
|
|
if (UnwrittenBits < array.Length * sizeof(uint) * BitsPerByte)
|
|
throw new InsufficientCapacityException(this, array.Length, UIntName, sizeof(uint) * BitsPerByte);
|
|
|
|
for (int i = 0; i < array.Length; i++)
|
|
{
|
|
Converter.UIntToBits(array[i], data, writeBit);
|
|
writeBit += sizeof(uint) * BitsPerByte;
|
|
}
|
|
|
|
return this;
|
|
}
|
|
|
|
/// <summary>Retrieves an <see cref="int"/> array from the message.</summary>
|
|
/// <returns>The array that was retrieved.</returns>
|
|
public int[] GetInts() => GetInts((int)GetVarULong());
|
|
/// <summary>Retrieves an <see cref="int"/> array from the message.</summary>
|
|
/// <param name="amount">The amount of ints to retrieve.</param>
|
|
/// <returns>The array that was retrieved.</returns>
|
|
public int[] GetInts(int amount)
|
|
{
|
|
int[] array = new int[amount];
|
|
ReadInts(amount, array);
|
|
return array;
|
|
}
|
|
/// <summary>Populates an <see cref="int"/> array with ints retrieved from the message.</summary>
|
|
/// <param name="amount">The amount of ints to retrieve.</param>
|
|
/// <param name="intoArray">The array to populate.</param>
|
|
/// <param name="startIndex">The position at which to start populating the array.</param>
|
|
public void GetInts(int amount, int[] intoArray, int startIndex = 0)
|
|
{
|
|
if (startIndex + amount > intoArray.Length)
|
|
throw new ArgumentException(nameof(amount), ArrayNotLongEnoughError(amount, intoArray.Length, startIndex, IntName));
|
|
|
|
ReadInts(amount, intoArray, startIndex);
|
|
}
|
|
|
|
/// <summary>Retrieves a <see cref="uint"/> array from the message.</summary>
|
|
/// <returns>The array that was retrieved.</returns>
|
|
public uint[] GetUInts() => GetUInts((int)GetVarULong());
|
|
/// <summary>Retrieves a <see cref="uint"/> array from the message.</summary>
|
|
/// <param name="amount">The amount of uints to retrieve.</param>
|
|
/// <returns>The array that was retrieved.</returns>
|
|
public uint[] GetUInts(int amount)
|
|
{
|
|
uint[] array = new uint[amount];
|
|
ReadUInts(amount, array);
|
|
return array;
|
|
}
|
|
/// <summary>Populates a <see cref="uint"/> array with uints retrieved from the message.</summary>
|
|
/// <param name="amount">The amount of uints to retrieve.</param>
|
|
/// <param name="intoArray">The array to populate.</param>
|
|
/// <param name="startIndex">The position at which to start populating the array.</param>
|
|
public void GetUInts(int amount, uint[] intoArray, int startIndex = 0)
|
|
{
|
|
if (startIndex + amount > intoArray.Length)
|
|
throw new ArgumentException(nameof(amount), ArrayNotLongEnoughError(amount, intoArray.Length, startIndex, UIntName));
|
|
|
|
ReadUInts(amount, intoArray, startIndex);
|
|
}
|
|
|
|
/// <summary>Reads a number of ints from the message and writes them into the given array.</summary>
|
|
/// <param name="amount">The amount of ints to read.</param>
|
|
/// <param name="intoArray">The array to write the ints into.</param>
|
|
/// <param name="startIndex">The position at which to start writing into the array.</param>
|
|
private void ReadInts(int amount, int[] intoArray, int startIndex = 0)
|
|
{
|
|
if (UnreadBits < amount * sizeof(int) * BitsPerByte)
|
|
{
|
|
RiptideLogger.Log(LogType.Error, NotEnoughBitsError(amount, IntName));
|
|
amount = UnreadBits / (sizeof(int) * BitsPerByte);
|
|
}
|
|
|
|
for (int i = 0; i < amount; i++)
|
|
{
|
|
intoArray[startIndex + i] = Converter.IntFromBits(data, readBit);
|
|
readBit += sizeof(int) * BitsPerByte;
|
|
}
|
|
}
|
|
|
|
/// <summary>Reads a number of uints from the message and writes them into the given array.</summary>
|
|
/// <param name="amount">The amount of uints to read.</param>
|
|
/// <param name="intoArray">The array to write the uints into.</param>
|
|
/// <param name="startIndex">The position at which to start writing into the array.</param>
|
|
private void ReadUInts(int amount, uint[] intoArray, int startIndex = 0)
|
|
{
|
|
if (UnreadBits < amount * sizeof(uint) * BitsPerByte)
|
|
{
|
|
RiptideLogger.Log(LogType.Error, NotEnoughBitsError(amount, UIntName));
|
|
amount = UnreadBits / (sizeof(uint) * BitsPerByte);
|
|
}
|
|
|
|
for (int i = 0; i < amount; i++)
|
|
{
|
|
intoArray[startIndex + i] = Converter.UIntFromBits(data, readBit);
|
|
readBit += sizeof(uint) * BitsPerByte;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Long & ULong
|
|
/// <summary>Adds a <see cref="long"/> to the message.</summary>
|
|
/// <param name="value">The <see cref="long"/> to add.</param>
|
|
/// <returns>The message that the <see cref="long"/> was added to.</returns>
|
|
public Message AddLong(long value)
|
|
{
|
|
if (UnwrittenBits < sizeof(long) * BitsPerByte)
|
|
throw new InsufficientCapacityException(this, LongName, sizeof(long) * BitsPerByte);
|
|
|
|
Converter.LongToBits(value, data, writeBit);
|
|
writeBit += sizeof(long) * BitsPerByte;
|
|
return this;
|
|
}
|
|
|
|
/// <summary>Adds a <see cref="ulong"/> to the message.</summary>
|
|
/// <param name="value">The <see cref="ulong"/> to add.</param>
|
|
/// <returns>The message that the <see cref="ulong"/> was added to.</returns>
|
|
public Message AddULong(ulong value)
|
|
{
|
|
if (UnwrittenBits < sizeof(ulong) * BitsPerByte)
|
|
throw new InsufficientCapacityException(this, ULongName, sizeof(ulong) * BitsPerByte);
|
|
|
|
Converter.ULongToBits(value, data, writeBit);
|
|
writeBit += sizeof(ulong) * BitsPerByte;
|
|
return this;
|
|
}
|
|
|
|
/// <summary>Retrieves a <see cref="long"/> from the message.</summary>
|
|
/// <returns>The <see cref="long"/> that was retrieved.</returns>
|
|
public long GetLong()
|
|
{
|
|
if (UnreadBits < sizeof(long) * BitsPerByte)
|
|
{
|
|
RiptideLogger.Log(LogType.Error, NotEnoughBitsError(LongName, $"{default(long)}"));
|
|
return default(long);
|
|
}
|
|
|
|
long value = Converter.LongFromBits(data, readBit);
|
|
readBit += sizeof(long) * BitsPerByte;
|
|
return value;
|
|
}
|
|
|
|
/// <summary>Retrieves a <see cref="ulong"/> from the message.</summary>
|
|
/// <returns>The <see cref="ulong"/> that was retrieved.</returns>
|
|
public ulong GetULong()
|
|
{
|
|
if (UnreadBits < sizeof(ulong) * BitsPerByte)
|
|
{
|
|
RiptideLogger.Log(LogType.Error, NotEnoughBitsError(ULongName, $"{default(ulong)}"));
|
|
return default(ulong);
|
|
}
|
|
|
|
ulong value = Converter.ULongFromBits(data, readBit);
|
|
readBit += sizeof(ulong) * BitsPerByte;
|
|
return value;
|
|
}
|
|
|
|
/// <summary>Adds a <see cref="long"/> array to the message.</summary>
|
|
/// <param name="array">The array to add.</param>
|
|
/// <param name="includeLength">Whether or not to include the length of the array in the message.</param>
|
|
/// <returns>The message that the array was added to.</returns>
|
|
public Message AddLongs(long[] array, bool includeLength = true)
|
|
{
|
|
if (includeLength)
|
|
AddVarULong((uint)array.Length);
|
|
|
|
if (UnwrittenBits < array.Length * sizeof(long) * BitsPerByte)
|
|
throw new InsufficientCapacityException(this, array.Length, LongName, sizeof(long) * BitsPerByte);
|
|
|
|
for (int i = 0; i < array.Length; i++)
|
|
{
|
|
Converter.LongToBits(array[i], data, writeBit);
|
|
writeBit += sizeof(long) * BitsPerByte;
|
|
}
|
|
|
|
return this;
|
|
}
|
|
|
|
/// <summary>Adds a <see cref="ulong"/> array to the message.</summary>
|
|
/// <param name="array">The array to add.</param>
|
|
/// <param name="includeLength">Whether or not to include the length of the array in the message.</param>
|
|
/// <returns>The message that the array was added to.</returns>
|
|
public Message AddULongs(ulong[] array, bool includeLength = true)
|
|
{
|
|
if (includeLength)
|
|
AddVarULong((uint)array.Length);
|
|
|
|
if (UnwrittenBits < array.Length * sizeof(ulong) * BitsPerByte)
|
|
throw new InsufficientCapacityException(this, array.Length, ULongName, sizeof(ulong) * BitsPerByte);
|
|
|
|
for (int i = 0; i < array.Length; i++)
|
|
{
|
|
Converter.ULongToBits(array[i], data, writeBit);
|
|
writeBit += sizeof(ulong) * BitsPerByte;
|
|
}
|
|
|
|
return this;
|
|
}
|
|
|
|
/// <summary>Retrieves a <see cref="long"/> array from the message.</summary>
|
|
/// <returns>The array that was retrieved.</returns>
|
|
public long[] GetLongs() => GetLongs((int)GetVarULong());
|
|
/// <summary>Retrieves a <see cref="long"/> array from the message.</summary>
|
|
/// <param name="amount">The amount of longs to retrieve.</param>
|
|
/// <returns>The array that was retrieved.</returns>
|
|
public long[] GetLongs(int amount)
|
|
{
|
|
long[] array = new long[amount];
|
|
ReadLongs(amount, array);
|
|
return array;
|
|
}
|
|
/// <summary>Populates a <see cref="long"/> array with longs retrieved from the message.</summary>
|
|
/// <param name="amount">The amount of longs to retrieve.</param>
|
|
/// <param name="intoArray">The array to populate.</param>
|
|
/// <param name="startIndex">The position at which to start populating the array.</param>
|
|
public void GetLongs(int amount, long[] intoArray, int startIndex = 0)
|
|
{
|
|
if (startIndex + amount > intoArray.Length)
|
|
throw new ArgumentException(nameof(amount), ArrayNotLongEnoughError(amount, intoArray.Length, startIndex, LongName));
|
|
|
|
ReadLongs(amount, intoArray, startIndex);
|
|
}
|
|
|
|
/// <summary>Retrieves a <see cref="ulong"/> array from the message.</summary>
|
|
/// <returns>The array that was retrieved.</returns>
|
|
public ulong[] GetULongs() => GetULongs((int)GetVarULong());
|
|
/// <summary>Retrieves a <see cref="ulong"/> array from the message.</summary>
|
|
/// <param name="amount">The amount of ulongs to retrieve.</param>
|
|
/// <returns>The array that was retrieved.</returns>
|
|
public ulong[] GetULongs(int amount)
|
|
{
|
|
ulong[] array = new ulong[amount];
|
|
ReadULongs(amount, array);
|
|
return array;
|
|
}
|
|
/// <summary>Populates a <see cref="ulong"/> array with ulongs retrieved from the message.</summary>
|
|
/// <param name="amount">The amount of ulongs to retrieve.</param>
|
|
/// <param name="intoArray">The array to populate.</param>
|
|
/// <param name="startIndex">The position at which to start populating the array.</param>
|
|
public void GetULongs(int amount, ulong[] intoArray, int startIndex = 0)
|
|
{
|
|
if (startIndex + amount > intoArray.Length)
|
|
throw new ArgumentException(nameof(amount), ArrayNotLongEnoughError(amount, intoArray.Length, startIndex, ULongName));
|
|
|
|
ReadULongs(amount, intoArray, startIndex);
|
|
}
|
|
|
|
/// <summary>Reads a number of longs from the message and writes them into the given array.</summary>
|
|
/// <param name="amount">The amount of longs to read.</param>
|
|
/// <param name="intoArray">The array to write the longs into.</param>
|
|
/// <param name="startIndex">The position at which to start writing into the array.</param>
|
|
private void ReadLongs(int amount, long[] intoArray, int startIndex = 0)
|
|
{
|
|
if (UnreadBits < amount * sizeof(long) * BitsPerByte)
|
|
{
|
|
RiptideLogger.Log(LogType.Error, NotEnoughBitsError(amount, LongName));
|
|
amount = UnreadBits / (sizeof(long) * BitsPerByte);
|
|
}
|
|
|
|
for (int i = 0; i < amount; i++)
|
|
{
|
|
intoArray[startIndex + i] = Converter.LongFromBits(data, readBit);
|
|
readBit += sizeof(long) * BitsPerByte;
|
|
}
|
|
}
|
|
|
|
/// <summary>Reads a number of ulongs from the message and writes them into the given array.</summary>
|
|
/// <param name="amount">The amount of ulongs to read.</param>
|
|
/// <param name="intoArray">The array to write the ulongs into.</param>
|
|
/// <param name="startIndex">The position at which to start writing into the array.</param>
|
|
private void ReadULongs(int amount, ulong[] intoArray, int startIndex = 0)
|
|
{
|
|
if (UnreadBits < amount * sizeof(ulong) * BitsPerByte)
|
|
{
|
|
RiptideLogger.Log(LogType.Error, NotEnoughBitsError(amount, ULongName));
|
|
amount = UnreadBits / (sizeof(ulong) * BitsPerByte);
|
|
}
|
|
|
|
for (int i = 0; i < amount; i++)
|
|
{
|
|
intoArray[startIndex + i] = Converter.ULongFromBits(data, readBit);
|
|
readBit += sizeof(ulong) * BitsPerByte;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Float
|
|
/// <summary>Adds a <see cref="float"/> to the message.</summary>
|
|
/// <param name="value">The <see cref="float"/> to add.</param>
|
|
/// <returns>The message that the <see cref="float"/> was added to.</returns>
|
|
public Message AddFloat(float value)
|
|
{
|
|
if (UnwrittenBits < sizeof(float) * BitsPerByte)
|
|
throw new InsufficientCapacityException(this, FloatName, sizeof(float) * BitsPerByte);
|
|
|
|
Converter.FloatToBits(value, data, writeBit);
|
|
writeBit += sizeof(float) * BitsPerByte;
|
|
return this;
|
|
}
|
|
|
|
/// <summary>Retrieves a <see cref="float"/> from the message.</summary>
|
|
/// <returns>The <see cref="float"/> that was retrieved.</returns>
|
|
public float GetFloat()
|
|
{
|
|
if (UnreadBits < sizeof(float) * BitsPerByte)
|
|
{
|
|
RiptideLogger.Log(LogType.Error, NotEnoughBitsError(FloatName, $"{default(float)}"));
|
|
return default(float);
|
|
}
|
|
|
|
float value = Converter.FloatFromBits(data, readBit);
|
|
readBit += sizeof(float) * BitsPerByte;
|
|
return value;
|
|
}
|
|
|
|
/// <summary>Adds a <see cref="float"/> array to the message.</summary>
|
|
/// <param name="array">The array to add.</param>
|
|
/// <param name="includeLength">Whether or not to include the length of the array in the message.</param>
|
|
/// <returns>The message that the array was added to.</returns>
|
|
public Message AddFloats(float[] array, bool includeLength = true)
|
|
{
|
|
if (includeLength)
|
|
AddVarULong((uint)array.Length);
|
|
|
|
if (UnwrittenBits < array.Length * sizeof(float) * BitsPerByte)
|
|
throw new InsufficientCapacityException(this, array.Length, FloatName, sizeof(float) * BitsPerByte);
|
|
|
|
for (int i = 0; i < array.Length; i++)
|
|
{
|
|
Converter.FloatToBits(array[i], data, writeBit);
|
|
writeBit += sizeof(float) * BitsPerByte;
|
|
}
|
|
|
|
return this;
|
|
}
|
|
|
|
/// <summary>Retrieves a <see cref="float"/> array from the message.</summary>
|
|
/// <returns>The array that was retrieved.</returns>
|
|
public float[] GetFloats() => GetFloats((int)GetVarULong());
|
|
/// <summary>Retrieves a <see cref="float"/> array from the message.</summary>
|
|
/// <param name="amount">The amount of floats to retrieve.</param>
|
|
/// <returns>The array that was retrieved.</returns>
|
|
public float[] GetFloats(int amount)
|
|
{
|
|
float[] array = new float[amount];
|
|
ReadFloats(amount, array);
|
|
return array;
|
|
}
|
|
/// <summary>Populates a <see cref="float"/> array with floats retrieved from the message.</summary>
|
|
/// <param name="amount">The amount of floats to retrieve.</param>
|
|
/// <param name="intoArray">The array to populate.</param>
|
|
/// <param name="startIndex">The position at which to start populating the array.</param>
|
|
public void GetFloats(int amount, float[] intoArray, int startIndex = 0)
|
|
{
|
|
if (startIndex + amount > intoArray.Length)
|
|
throw new ArgumentException(nameof(amount), ArrayNotLongEnoughError(amount, intoArray.Length, startIndex, FloatName));
|
|
|
|
ReadFloats(amount, intoArray, startIndex);
|
|
}
|
|
|
|
/// <summary>Reads a number of floats from the message and writes them into the given array.</summary>
|
|
/// <param name="amount">The amount of floats to read.</param>
|
|
/// <param name="intoArray">The array to write the floats into.</param>
|
|
/// <param name="startIndex">The position at which to start writing into the array.</param>
|
|
private void ReadFloats(int amount, float[] intoArray, int startIndex = 0)
|
|
{
|
|
if (UnreadBits < amount * sizeof(float) * BitsPerByte)
|
|
{
|
|
RiptideLogger.Log(LogType.Error, NotEnoughBitsError(amount, FloatName));
|
|
amount = UnreadBits / (sizeof(float) * BitsPerByte);
|
|
}
|
|
|
|
for (int i = 0; i < amount; i++)
|
|
{
|
|
intoArray[startIndex + i] = Converter.FloatFromBits(data, readBit);
|
|
readBit += sizeof(float) * BitsPerByte;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Double
|
|
/// <summary>Adds a <see cref="double"/> to the message.</summary>
|
|
/// <param name="value">The <see cref="double"/> to add.</param>
|
|
/// <returns>The message that the <see cref="double"/> was added to.</returns>
|
|
public Message AddDouble(double value)
|
|
{
|
|
if (UnwrittenBits < sizeof(double) * BitsPerByte)
|
|
throw new InsufficientCapacityException(this, DoubleName, sizeof(double) * BitsPerByte);
|
|
|
|
Converter.DoubleToBits(value, data, writeBit);
|
|
writeBit += sizeof(double) * BitsPerByte;
|
|
return this;
|
|
}
|
|
|
|
/// <summary>Retrieves a <see cref="double"/> from the message.</summary>
|
|
/// <returns>The <see cref="double"/> that was retrieved.</returns>
|
|
public double GetDouble()
|
|
{
|
|
if (UnreadBits < sizeof(double) * BitsPerByte)
|
|
{
|
|
RiptideLogger.Log(LogType.Error, NotEnoughBitsError(DoubleName, $"{default(double)}"));
|
|
return default(double);
|
|
}
|
|
|
|
double value = Converter.DoubleFromBits(data, readBit);
|
|
readBit += sizeof(double) * BitsPerByte;
|
|
return value;
|
|
}
|
|
|
|
/// <summary>Adds a <see cref="double"/> array to the message.</summary>
|
|
/// <param name="array">The array to add.</param>
|
|
/// <param name="includeLength">Whether or not to include the length of the array in the message.</param>
|
|
/// <returns>The message that the array was added to.</returns>
|
|
public Message AddDoubles(double[] array, bool includeLength = true)
|
|
{
|
|
if (includeLength)
|
|
AddVarULong((uint)array.Length);
|
|
|
|
if (UnwrittenBits < array.Length * sizeof(double) * BitsPerByte)
|
|
throw new InsufficientCapacityException(this, array.Length, DoubleName, sizeof(double) * BitsPerByte);
|
|
|
|
for (int i = 0; i < array.Length; i++)
|
|
{
|
|
Converter.DoubleToBits(array[i], data, writeBit);
|
|
writeBit += sizeof(double) * BitsPerByte;
|
|
}
|
|
|
|
return this;
|
|
}
|
|
|
|
/// <summary>Retrieves a <see cref="double"/> array from the message.</summary>
|
|
/// <returns>The array that was retrieved.</returns>
|
|
public double[] GetDoubles() => GetDoubles((int)GetVarULong());
|
|
/// <summary>Retrieves a <see cref="double"/> array from the message.</summary>
|
|
/// <param name="amount">The amount of doubles to retrieve.</param>
|
|
/// <returns>The array that was retrieved.</returns>
|
|
public double[] GetDoubles(int amount)
|
|
{
|
|
double[] array = new double[amount];
|
|
ReadDoubles(amount, array);
|
|
return array;
|
|
}
|
|
/// <summary>Populates a <see cref="double"/> array with doubles retrieved from the message.</summary>
|
|
/// <param name="amount">The amount of doubles to retrieve.</param>
|
|
/// <param name="intoArray">The array to populate.</param>
|
|
/// <param name="startIndex">The position at which to start populating the array.</param>
|
|
public void GetDoubles(int amount, double[] intoArray, int startIndex = 0)
|
|
{
|
|
if (startIndex + amount > intoArray.Length)
|
|
throw new ArgumentException(nameof(amount), ArrayNotLongEnoughError(amount, intoArray.Length, startIndex, DoubleName));
|
|
|
|
ReadDoubles(amount, intoArray, startIndex);
|
|
}
|
|
|
|
/// <summary>Reads a number of doubles from the message and writes them into the given array.</summary>
|
|
/// <param name="amount">The amount of doubles to read.</param>
|
|
/// <param name="intoArray">The array to write the doubles into.</param>
|
|
/// <param name="startIndex">The position at which to start writing into the array.</param>
|
|
private void ReadDoubles(int amount, double[] intoArray, int startIndex = 0)
|
|
{
|
|
if (UnreadBits < amount * sizeof(double) * BitsPerByte)
|
|
{
|
|
RiptideLogger.Log(LogType.Error, NotEnoughBitsError(amount, DoubleName));
|
|
amount = UnreadBits / (sizeof(double) * BitsPerByte);
|
|
}
|
|
|
|
for (int i = 0; i < amount; i++)
|
|
{
|
|
intoArray[startIndex + i] = Converter.DoubleFromBits(data, readBit);
|
|
readBit += sizeof(double) * BitsPerByte;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region String
|
|
/// <summary>Adds a <see cref="string"/> to the message.</summary>
|
|
/// <param name="value">The <see cref="string"/> to add.</param>
|
|
/// <returns>The message that the <see cref="string"/> was added to.</returns>
|
|
public Message AddString(string value)
|
|
{
|
|
AddBytes(Encoding.UTF8.GetBytes(value));
|
|
return this;
|
|
}
|
|
|
|
/// <summary>Retrieves a <see cref="string"/> from the message.</summary>
|
|
/// <returns>The <see cref="string"/> that was retrieved.</returns>
|
|
public string GetString()
|
|
{
|
|
int length = (int)GetVarULong(); // Get the length of the string (in bytes, NOT characters)
|
|
if (UnreadBits < length * BitsPerByte)
|
|
{
|
|
RiptideLogger.Log(LogType.Error, NotEnoughBitsError(StringName, "shortened string"));
|
|
length = UnreadBits / BitsPerByte;
|
|
}
|
|
|
|
string value = Encoding.UTF8.GetString(GetBytes(length), 0, length);
|
|
return value;
|
|
}
|
|
|
|
/// <summary>Adds a <see cref="string"/> array to the message.</summary>
|
|
/// <param name="array">The array to add.</param>
|
|
/// <param name="includeLength">Whether or not to include the length of the array in the message.</param>
|
|
/// <returns>The message that the array was added to.</returns>
|
|
public Message AddStrings(string[] array, bool includeLength = true)
|
|
{
|
|
if (includeLength)
|
|
AddVarULong((uint)array.Length);
|
|
|
|
// It'd be ideal to throw an exception here (instead of in AddString) if the entire array isn't going to fit, but since each string could
|
|
// be (and most likely is) a different length and some characters use more than a single byte, the only way of doing that would be to loop
|
|
// through the whole array here and convert each string to bytes ahead of time, just to get the required byte count. Then if they all fit
|
|
// into the message, they would all be converted again when actually being written into the byte array, which is obviously inefficient.
|
|
|
|
for (int i = 0; i < array.Length; i++)
|
|
AddString(array[i]);
|
|
|
|
return this;
|
|
}
|
|
|
|
/// <summary>Retrieves a <see cref="string"/> array from the message.</summary>
|
|
/// <returns>The array that was retrieved.</returns>
|
|
public string[] GetStrings() => GetStrings((int)GetVarULong());
|
|
/// <summary>Retrieves a <see cref="string"/> array from the message.</summary>
|
|
/// <param name="amount">The amount of strings to retrieve.</param>
|
|
/// <returns>The array that was retrieved.</returns>
|
|
public string[] GetStrings(int amount)
|
|
{
|
|
string[] array = new string[amount];
|
|
for (int i = 0; i < array.Length; i++)
|
|
array[i] = GetString();
|
|
|
|
return array;
|
|
}
|
|
/// <summary>Populates a <see cref="string"/> array with strings retrieved from the message.</summary>
|
|
/// <param name="amount">The amount of strings to retrieve.</param>
|
|
/// <param name="intoArray">The array to populate.</param>
|
|
/// <param name="startIndex">The position at which to start populating the array.</param>
|
|
public void GetStrings(int amount, string[] intoArray, int startIndex = 0)
|
|
{
|
|
if (startIndex + amount > intoArray.Length)
|
|
throw new ArgumentException(nameof(amount), ArrayNotLongEnoughError(amount, intoArray.Length, startIndex, StringName));
|
|
|
|
for (int i = 0; i < amount; i++)
|
|
intoArray[startIndex + i] = GetString();
|
|
}
|
|
#endregion
|
|
|
|
#region IMessageSerializable Types
|
|
/// <summary>Adds a serializable to the message.</summary>
|
|
/// <param name="value">The serializable to add.</param>
|
|
/// <returns>The message that the serializable was added to.</returns>
|
|
public Message AddSerializable<T>(T value) where T : IMessageSerializable
|
|
{
|
|
value.Serialize(this);
|
|
return this;
|
|
}
|
|
|
|
/// <summary>Retrieves a serializable from the message.</summary>
|
|
/// <returns>The serializable that was retrieved.</returns>
|
|
public T GetSerializable<T>() where T : IMessageSerializable, new()
|
|
{
|
|
T t = new T();
|
|
t.Deserialize(this);
|
|
return t;
|
|
}
|
|
|
|
/// <summary>Adds an array of serializables to the message.</summary>
|
|
/// <param name="array">The array to add.</param>
|
|
/// <param name="includeLength">Whether or not to include the length of the array in the message.</param>
|
|
/// <returns>The message that the array was added to.</returns>
|
|
public Message AddSerializables<T>(T[] array, bool includeLength = true) where T : IMessageSerializable
|
|
{
|
|
if (includeLength)
|
|
AddVarULong((uint)array.Length);
|
|
|
|
for (int i = 0; i < array.Length; i++)
|
|
AddSerializable(array[i]);
|
|
|
|
return this;
|
|
}
|
|
|
|
/// <summary>Retrieves an array of serializables from the message.</summary>
|
|
/// <returns>The array that was retrieved.</returns>
|
|
public T[] GetSerializables<T>() where T : IMessageSerializable, new() => GetSerializables<T>((int)GetVarULong());
|
|
/// <summary>Retrieves an array of serializables from the message.</summary>
|
|
/// <param name="amount">The amount of serializables to retrieve.</param>
|
|
/// <returns>The array that was retrieved.</returns>
|
|
public T[] GetSerializables<T>(int amount) where T : IMessageSerializable, new()
|
|
{
|
|
T[] array = new T[amount];
|
|
ReadSerializables(amount, array);
|
|
return array;
|
|
}
|
|
/// <summary>Populates an array of serializables retrieved from the message.</summary>
|
|
/// <param name="amount">The amount of serializables to retrieve.</param>
|
|
/// <param name="intoArray">The array to populate.</param>
|
|
/// <param name="startIndex">The position at which to start populating the array.</param>
|
|
public void GetSerializables<T>(int amount, T[] intoArray, int startIndex = 0) where T : IMessageSerializable, new()
|
|
{
|
|
if (startIndex + amount > intoArray.Length)
|
|
throw new ArgumentException(nameof(amount), ArrayNotLongEnoughError(amount, intoArray.Length, startIndex, typeof(T).Name));
|
|
|
|
ReadSerializables(amount, intoArray, startIndex);
|
|
}
|
|
|
|
/// <summary>Reads a number of serializables from the message and writes them into the given array.</summary>
|
|
/// <param name="amount">The amount of serializables to read.</param>
|
|
/// <param name="intArray">The array to write the serializables into.</param>
|
|
/// <param name="startIndex">The position at which to start writing into <paramref name="intArray"/>.</param>
|
|
private void ReadSerializables<T>(int amount, T[] intArray, int startIndex = 0) where T : IMessageSerializable, new()
|
|
{
|
|
for (int i = 0; i < amount; i++)
|
|
intArray[startIndex + i] = GetSerializable<T>();
|
|
}
|
|
#endregion
|
|
|
|
#region Overload Versions
|
|
/// <inheritdoc cref="AddByte(byte)"/>
|
|
/// <remarks>This method is simply an alternative way of calling <see cref="AddByte(byte)"/>.</remarks>
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public Message Add(byte value) => AddByte(value);
|
|
/// <inheritdoc cref="AddSByte(sbyte)"/>
|
|
/// <remarks>This method is simply an alternative way of calling <see cref="AddSByte(sbyte)"/>.</remarks>
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public Message Add(sbyte value) => AddSByte(value);
|
|
/// <inheritdoc cref="AddBool(bool)"/>
|
|
/// <remarks>This method is simply an alternative way of calling <see cref="AddBool(bool)"/>.</remarks>
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public Message Add(bool value) => AddBool(value);
|
|
/// <inheritdoc cref="AddShort(short)"/>
|
|
/// <remarks>This method is simply an alternative way of calling <see cref="AddShort(short)"/>.</remarks>
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public Message Add(short value) => AddShort(value);
|
|
/// <inheritdoc cref="AddUShort(ushort)"/>
|
|
/// <remarks>This method is simply an alternative way of calling <see cref="AddUShort(ushort)"/>.</remarks>
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public Message Add(ushort value) => AddUShort(value);
|
|
/// <inheritdoc cref="AddInt(int)"/>
|
|
/// <remarks>This method is simply an alternative way of calling <see cref="AddInt(int)"/>.</remarks>
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public Message Add(int value) => AddInt(value);
|
|
/// <inheritdoc cref="AddUInt(uint)"/>
|
|
/// <remarks>This method is simply an alternative way of calling <see cref="AddUInt(uint)"/>.</remarks>
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public Message Add(uint value) => AddUInt(value);
|
|
/// <inheritdoc cref="AddLong(long)"/>
|
|
/// <remarks>This method is simply an alternative way of calling <see cref="AddLong(long)"/>.</remarks>
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public Message Add(long value) => AddLong(value);
|
|
/// <inheritdoc cref="AddULong(ulong)"/>
|
|
/// <remarks>This method is simply an alternative way of calling <see cref="AddULong(ulong)"/>.</remarks>
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public Message Add(ulong value) => AddULong(value);
|
|
/// <inheritdoc cref="AddFloat(float)"/>
|
|
/// <remarks>This method is simply an alternative way of calling <see cref="AddFloat(float)"/>.</remarks>
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public Message Add(float value) => AddFloat(value);
|
|
/// <inheritdoc cref="AddDouble(double)"/>
|
|
/// <remarks>This method is simply an alternative way of calling <see cref="AddDouble(double)"/>.</remarks>
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public Message Add(double value) => AddDouble(value);
|
|
/// <inheritdoc cref="AddString(string)"/>
|
|
/// <remarks>This method is simply an alternative way of calling <see cref="AddString(string)"/>.</remarks>
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public Message Add(string value) => AddString(value);
|
|
/// <inheritdoc cref="AddSerializable{T}(T)"/>
|
|
/// <remarks>This method is simply an alternative way of calling <see cref="AddSerializable{T}(T)"/>.</remarks>
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public Message Add<T>(T value) where T : IMessageSerializable => AddSerializable(value);
|
|
|
|
/// <inheritdoc cref="AddBytes(byte[], bool)"/>
|
|
/// <remarks>This method is simply an alternative way of calling <see cref="AddBytes(byte[], bool)"/>.</remarks>
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public Message Add(byte[] array, bool includeLength = true) => AddBytes(array, includeLength);
|
|
/// <inheritdoc cref="AddSBytes(sbyte[], bool)"/>
|
|
/// <remarks>This method is simply an alternative way of calling <see cref="AddSBytes(sbyte[], bool)"/>.</remarks>
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public Message Add(sbyte[] array, bool includeLength = true) => AddSBytes(array, includeLength);
|
|
/// <inheritdoc cref="AddBools(bool[], bool)"/>
|
|
/// <remarks>This method is simply an alternative way of calling <see cref="AddBools(bool[], bool)"/>.</remarks>
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public Message Add(bool[] array, bool includeLength = true) => AddBools(array, includeLength);
|
|
/// <inheritdoc cref="AddShorts(short[], bool)"/>
|
|
/// <remarks>This method is simply an alternative way of calling <see cref="AddShorts(short[], bool)"/>.</remarks>
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public Message Add(short[] array, bool includeLength = true) => AddShorts(array, includeLength);
|
|
/// <inheritdoc cref="AddUShorts(ushort[], bool)"/>
|
|
/// <remarks>This method is simply an alternative way of calling <see cref="AddUShorts(ushort[], bool)"/>.</remarks>
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public Message Add(ushort[] array, bool includeLength = true) => AddUShorts(array, includeLength);
|
|
/// <inheritdoc cref="AddInts(int[], bool)"/>
|
|
/// <remarks>This method is simply an alternative way of calling <see cref="AddInts(int[], bool)"/>.</remarks>
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public Message Add(int[] array, bool includeLength = true) => AddInts(array, includeLength);
|
|
/// <inheritdoc cref="AddUInts(uint[], bool)"/>
|
|
/// <remarks>This method is simply an alternative way of calling <see cref="AddUInts(uint[], bool)"/>.</remarks>
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public Message Add(uint[] array, bool includeLength = true) => AddUInts(array, includeLength);
|
|
/// <inheritdoc cref="AddLongs(long[], bool)"/>
|
|
/// <remarks>This method is simply an alternative way of calling <see cref="AddLongs(long[], bool)"/>.</remarks>
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public Message Add(long[] array, bool includeLength = true) => AddLongs(array, includeLength);
|
|
/// <inheritdoc cref="AddULongs(ulong[], bool)"/>
|
|
/// <remarks>This method is simply an alternative way of calling <see cref="AddULongs(ulong[], bool)"/>.</remarks>
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public Message Add(ulong[] array, bool includeLength = true) => AddULongs(array, includeLength);
|
|
/// <inheritdoc cref="AddFloats(float[], bool)"/>
|
|
/// <remarks>This method is simply an alternative way of calling <see cref="AddFloats(float[], bool)"/>.</remarks>
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public Message Add(float[] array, bool includeLength = true) => AddFloats(array, includeLength);
|
|
/// <inheritdoc cref="AddDoubles(double[], bool)"/>
|
|
/// <remarks>This method is simply an alternative way of calling <see cref="AddDoubles(double[], bool)"/>.</remarks>
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public Message Add(double[] array, bool includeLength = true) => AddDoubles(array, includeLength);
|
|
/// <inheritdoc cref="AddStrings(string[], bool)"/>
|
|
/// <remarks>This method is simply an alternative way of calling <see cref="AddStrings(string[], bool)"/>.</remarks>
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public Message Add(string[] array, bool includeLength = true) => AddStrings(array, includeLength);
|
|
/// <inheritdoc cref="AddSerializables{T}(T[], bool)"/>
|
|
/// <remarks>This method is simply an alternative way of calling <see cref="AddSerializables{T}(T[], bool)"/>.</remarks>
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public Message Add<T>(T[] array, bool includeLength = true) where T : IMessageSerializable, new() => AddSerializables(array, includeLength);
|
|
#endregion
|
|
#endregion
|
|
|
|
#region Error Messaging
|
|
/// <summary>The name of a <see cref="byte"/> value.</summary>
|
|
private const string ByteName = "byte";
|
|
/// <summary>The name of a <see cref="sbyte"/> value.</summary>
|
|
private const string SByteName = "sbyte";
|
|
/// <summary>The name of a <see cref="bool"/> value.</summary>
|
|
private const string BoolName = "bool";
|
|
/// <summary>The name of a <see cref="short"/> value.</summary>
|
|
private const string ShortName = "short";
|
|
/// <summary>The name of a <see cref="ushort"/> value.</summary>
|
|
private const string UShortName = "ushort";
|
|
/// <summary>The name of an <see cref="int"/> value.</summary>
|
|
private const string IntName = "int";
|
|
/// <summary>The name of a <see cref="uint"/> value.</summary>
|
|
private const string UIntName = "uint";
|
|
/// <summary>The name of a <see cref="long"/> value.</summary>
|
|
private const string LongName = "long";
|
|
/// <summary>The name of a <see cref="ulong"/> value.</summary>
|
|
private const string ULongName = "ulong";
|
|
/// <summary>The name of a <see cref="float"/> value.</summary>
|
|
private const string FloatName = "float";
|
|
/// <summary>The name of a <see cref="double"/> value.</summary>
|
|
private const string DoubleName = "double";
|
|
/// <summary>The name of a <see cref="string"/> value.</summary>
|
|
private const string StringName = "string";
|
|
/// <summary>The name of an array length value.</summary>
|
|
private const string ArrayLengthName = "array length";
|
|
|
|
/// <summary>Constructs an error message for when a message contains insufficient unread bits to retrieve a certain value.</summary>
|
|
/// <param name="valueName">The name of the value type for which the retrieval attempt failed.</param>
|
|
/// <param name="defaultReturn">Text describing the value which will be returned.</param>
|
|
/// <returns>The error message.</returns>
|
|
private string NotEnoughBitsError(string valueName, string defaultReturn)
|
|
{
|
|
return $"Message only contains {UnreadBits} unread {Helper.CorrectForm(UnreadBits, "bit")}, which is not enough to retrieve a value of type '{valueName}'! Returning {defaultReturn}.";
|
|
}
|
|
/// <summary>Constructs an error message for when a message contains insufficient unread bits to retrieve an array of values.</summary>
|
|
/// <param name="arrayLength">The expected length of the array.</param>
|
|
/// <param name="valueName">The name of the value type for which the retrieval attempt failed.</param>
|
|
/// <returns>The error message.</returns>
|
|
private string NotEnoughBitsError(int arrayLength, string valueName)
|
|
{
|
|
return $"Message only contains {UnreadBits} unread {Helper.CorrectForm(UnreadBits, "bit")}, which is not enough to retrieve {arrayLength} {Helper.CorrectForm(arrayLength, valueName)}! Returned array will contain default elements.";
|
|
}
|
|
|
|
/// <summary>Constructs an error message for when a number of retrieved values do not fit inside the bounds of the provided array.</summary>
|
|
/// <param name="amount">The number of values being retrieved.</param>
|
|
/// <param name="arrayLength">The length of the provided array.</param>
|
|
/// <param name="startIndex">The position in the array at which to begin writing values.</param>
|
|
/// <param name="valueName">The name of the value type which is being retrieved.</param>
|
|
/// <param name="pluralValueName">The name of the value type in plural form. If left empty, this will be set to <paramref name="valueName"/> with an <c>s</c> appended to it.</param>
|
|
/// <returns>The error message.</returns>
|
|
private string ArrayNotLongEnoughError(int amount, int arrayLength, int startIndex, string valueName, string pluralValueName = "")
|
|
{
|
|
if (string.IsNullOrEmpty(pluralValueName))
|
|
pluralValueName = $"{valueName}s";
|
|
|
|
return $"The amount of {pluralValueName} to retrieve ({amount}) is greater than the number of elements from the start index ({startIndex}) to the end of the given array (length: {arrayLength})!";
|
|
}
|
|
#endregion
|
|
}
|
|
}
|