fix: Fixes BitArray serialization (#1027)
Fixes bit array serialization. This may cause objects that were serialized by bit array to fail to deserialize. I am sorry, please accept my condolences. It is probably easiest to just delete those objects. If it becomes a major problem, contact me and I'll help with a hacky per-case solution.
This commit is contained in:
parent
6ea5508f5f
commit
6b3617b08f
4 changed files with 207 additions and 167 deletions
29
Projects/Server.Tests/Tests/Collections/BitArrayTests.cs
Normal file
29
Projects/Server.Tests/Tests/Collections/BitArrayTests.cs
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
using Server.Collections;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Server.Tests;
|
||||||
|
|
||||||
|
public class BitArrayTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void TestBitArray()
|
||||||
|
{
|
||||||
|
var bitArray = new BitArray(700); // Restricted Spells;
|
||||||
|
bitArray.Set(5, true);
|
||||||
|
bitArray.Set(39, true);
|
||||||
|
bitArray.Set(125, true);
|
||||||
|
|
||||||
|
// Simulate World Saving
|
||||||
|
var writer = new BufferWriter(1024, false);
|
||||||
|
writer.Write(bitArray); // Save it to a file
|
||||||
|
|
||||||
|
// Simulate World Loading
|
||||||
|
var reader = new BufferReader(writer.Buffer);
|
||||||
|
var bitArrayTest = reader.ReadBitArray();
|
||||||
|
Assert.Equal(700, bitArrayTest.Length);
|
||||||
|
for (var i = 0; i < bitArrayTest.Length; i++)
|
||||||
|
{
|
||||||
|
Assert.Equal(i is 5 or 39 or 125, bitArrayTest.Get(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -63,7 +63,7 @@ public sealed class BitArray : ICollection, ICloneable
|
||||||
**
|
**
|
||||||
** Exceptions: ArgumentException if bytes == null.
|
** Exceptions: ArgumentException if bytes == null.
|
||||||
=========================================================================*/
|
=========================================================================*/
|
||||||
public BitArray(byte[] bytes)
|
public BitArray(ReadOnlySpan<byte> bytes, int length = -1)
|
||||||
{
|
{
|
||||||
if (bytes == null)
|
if (bytes == null)
|
||||||
{
|
{
|
||||||
|
|
@ -79,7 +79,7 @@ public sealed class BitArray : ICollection, ICloneable
|
||||||
}
|
}
|
||||||
|
|
||||||
m_array = new int[GetInt32ArrayLengthFromByteLength(bytes.Length)];
|
m_array = new int[GetInt32ArrayLengthFromByteLength(bytes.Length)];
|
||||||
m_length = bytes.Length * BitsPerByte;
|
m_length = length == -1 ? bytes.Length * BitsPerByte : length;
|
||||||
|
|
||||||
uint totalCount = (uint)bytes.Length / 4;
|
uint totalCount = (uint)bytes.Length / 4;
|
||||||
|
|
||||||
|
|
@ -96,16 +96,22 @@ public sealed class BitArray : ICollection, ICloneable
|
||||||
switch (byteSpan.Length)
|
switch (byteSpan.Length)
|
||||||
{
|
{
|
||||||
case 3:
|
case 3:
|
||||||
last = byteSpan[2] << 16;
|
{
|
||||||
goto case 2;
|
last = byteSpan[2] << 16;
|
||||||
|
goto case 2;
|
||||||
|
}
|
||||||
// fall through
|
// fall through
|
||||||
case 2:
|
case 2:
|
||||||
last |= byteSpan[1] << 8;
|
{
|
||||||
goto case 1;
|
last |= byteSpan[1] << 8;
|
||||||
|
goto case 1;
|
||||||
|
}
|
||||||
// fall through
|
// fall through
|
||||||
case 1:
|
case 1:
|
||||||
m_array[totalCount] = last | byteSpan[0];
|
{
|
||||||
break;
|
m_array[totalCount] = last | byteSpan[0];
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_version = 0;
|
_version = 0;
|
||||||
|
|
@ -119,63 +125,7 @@ public sealed class BitArray : ICollection, ICloneable
|
||||||
**
|
**
|
||||||
** Exceptions: ArgumentException if bytes == null.
|
** Exceptions: ArgumentException if bytes == null.
|
||||||
=========================================================================*/
|
=========================================================================*/
|
||||||
public BitArray(ReadOnlySpan<byte> bytes)
|
public BitArray(BinaryReader reader, int bitLength)
|
||||||
{
|
|
||||||
if (bytes == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(bytes));
|
|
||||||
}
|
|
||||||
|
|
||||||
// this value is chosen to prevent overflow when computing m_length.
|
|
||||||
// m_length is of type int32 and is exposed as a property, so
|
|
||||||
// type of m_length can't be changed to accommodate.
|
|
||||||
if (bytes.Length > int.MaxValue / BitsPerByte)
|
|
||||||
{
|
|
||||||
throw new ArgumentException(string.Format(CollectionThrowStrings.Argument_ArrayTooLarge, BitsPerByte), nameof(bytes));
|
|
||||||
}
|
|
||||||
|
|
||||||
m_array = new int[GetInt32ArrayLengthFromByteLength(bytes.Length)];
|
|
||||||
m_length = bytes.Length * BitsPerByte;
|
|
||||||
|
|
||||||
uint totalCount = (uint)bytes.Length / 4;
|
|
||||||
|
|
||||||
ReadOnlySpan<byte> byteSpan = bytes;
|
|
||||||
for (int i = 0; i < totalCount; i++)
|
|
||||||
{
|
|
||||||
m_array[i] = BinaryPrimitives.ReadInt32LittleEndian(byteSpan);
|
|
||||||
byteSpan = byteSpan[4..];
|
|
||||||
}
|
|
||||||
|
|
||||||
Debug.Assert(byteSpan.Length >= 0 && byteSpan.Length < 4);
|
|
||||||
|
|
||||||
int last = 0;
|
|
||||||
switch (byteSpan.Length)
|
|
||||||
{
|
|
||||||
case 3:
|
|
||||||
last = byteSpan[2] << 16;
|
|
||||||
goto case 2;
|
|
||||||
// fall through
|
|
||||||
case 2:
|
|
||||||
last |= byteSpan[1] << 8;
|
|
||||||
goto case 1;
|
|
||||||
// fall through
|
|
||||||
case 1:
|
|
||||||
m_array[totalCount] = last | byteSpan[0];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
_version = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*=========================================================================
|
|
||||||
** Allocates space to hold the bit values in bytes. bytes[0] represents
|
|
||||||
** bits 0 - 7, bytes[1] represents bits 8 - 15, etc. The LSB of each byte
|
|
||||||
** represents the lowest index value; bytes[0] & 1 represents bit 0,
|
|
||||||
** bytes[0] & 2 represents bit 1, bytes[0] & 4 represents bit 2, etc.
|
|
||||||
**
|
|
||||||
** Exceptions: ArgumentException if bytes == null.
|
|
||||||
=========================================================================*/
|
|
||||||
public BitArray(BinaryReader reader, int length)
|
|
||||||
{
|
{
|
||||||
if (reader == null)
|
if (reader == null)
|
||||||
{
|
{
|
||||||
|
|
@ -185,13 +135,15 @@ public sealed class BitArray : ICollection, ICloneable
|
||||||
// this value is chosen to prevent overflow when computing m_length.
|
// this value is chosen to prevent overflow when computing m_length.
|
||||||
// m_length is of type int32 and is exposed as a property, so
|
// m_length is of type int32 and is exposed as a property, so
|
||||||
// type of m_length can't be changed to accommodate.
|
// type of m_length can't be changed to accommodate.
|
||||||
if (length > int.MaxValue / BitsPerByte)
|
if (bitLength > int.MaxValue / BitsPerByte)
|
||||||
{
|
{
|
||||||
throw new ArgumentException(string.Format(CollectionThrowStrings.Argument_ArrayTooLarge, BitsPerByte), nameof(reader));
|
throw new ArgumentException(string.Format(CollectionThrowStrings.Argument_ArrayTooLarge, BitsPerByte), nameof(reader));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var length = GetByteArrayLengthFromBitLength(bitLength);
|
||||||
|
|
||||||
m_array = new int[GetInt32ArrayLengthFromByteLength(length)];
|
m_array = new int[GetInt32ArrayLengthFromByteLength(length)];
|
||||||
m_length = length * BitsPerByte;
|
m_length = length;
|
||||||
|
|
||||||
uint totalCount = (uint)length / 4;
|
uint totalCount = (uint)length / 4;
|
||||||
|
|
||||||
|
|
@ -207,16 +159,22 @@ public sealed class BitArray : ICollection, ICloneable
|
||||||
switch (length)
|
switch (length)
|
||||||
{
|
{
|
||||||
case 3:
|
case 3:
|
||||||
last = reader.ReadInt16();
|
{
|
||||||
goto case 2;
|
last = reader.ReadInt16();
|
||||||
|
goto case 2;
|
||||||
|
}
|
||||||
// fall through
|
// fall through
|
||||||
case 2:
|
case 2:
|
||||||
last |= reader.ReadByte();
|
{
|
||||||
goto case 1;
|
last |= reader.ReadByte();
|
||||||
|
goto case 1;
|
||||||
|
}
|
||||||
// fall through
|
// fall through
|
||||||
case 1:
|
case 1:
|
||||||
m_array[totalCount] = last | reader.ReadByte();
|
{
|
||||||
break;
|
m_array[totalCount] = last | reader.ReadByte();
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_version = 0;
|
_version = 0;
|
||||||
|
|
@ -499,14 +457,38 @@ public sealed class BitArray : ICollection, ICloneable
|
||||||
// Unroll loop for count less than Vector256 size.
|
// Unroll loop for count less than Vector256 size.
|
||||||
switch (count)
|
switch (count)
|
||||||
{
|
{
|
||||||
case 7: thisArray[6] &= valueArray[6]; goto case 6;
|
case 7:
|
||||||
case 6: thisArray[5] &= valueArray[5]; goto case 5;
|
{
|
||||||
case 5: thisArray[4] &= valueArray[4]; goto case 4;
|
thisArray[6] &= valueArray[6]; goto case 6;
|
||||||
case 4: thisArray[3] &= valueArray[3]; goto case 3;
|
}
|
||||||
case 3: thisArray[2] &= valueArray[2]; goto case 2;
|
case 6:
|
||||||
case 2: thisArray[1] &= valueArray[1]; goto case 1;
|
{
|
||||||
case 1: thisArray[0] &= valueArray[0]; goto Done;
|
thisArray[5] &= valueArray[5]; goto case 5;
|
||||||
case 0: goto Done;
|
}
|
||||||
|
case 5:
|
||||||
|
{
|
||||||
|
thisArray[4] &= valueArray[4]; goto case 4;
|
||||||
|
}
|
||||||
|
case 4:
|
||||||
|
{
|
||||||
|
thisArray[3] &= valueArray[3]; goto case 3;
|
||||||
|
}
|
||||||
|
case 3:
|
||||||
|
{
|
||||||
|
thisArray[2] &= valueArray[2]; goto case 2;
|
||||||
|
}
|
||||||
|
case 2:
|
||||||
|
{
|
||||||
|
thisArray[1] &= valueArray[1]; goto case 1;
|
||||||
|
}
|
||||||
|
case 1:
|
||||||
|
{
|
||||||
|
thisArray[0] &= valueArray[0]; goto Done;
|
||||||
|
}
|
||||||
|
case 0:
|
||||||
|
{
|
||||||
|
goto Done;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint i = 0;
|
uint i = 0;
|
||||||
|
|
@ -597,14 +579,38 @@ public sealed class BitArray : ICollection, ICloneable
|
||||||
// Unroll loop for count less than Vector256 size.
|
// Unroll loop for count less than Vector256 size.
|
||||||
switch (count)
|
switch (count)
|
||||||
{
|
{
|
||||||
case 7: thisArray[6] |= valueArray[6]; goto case 6;
|
case 7:
|
||||||
case 6: thisArray[5] |= valueArray[5]; goto case 5;
|
{
|
||||||
case 5: thisArray[4] |= valueArray[4]; goto case 4;
|
thisArray[6] |= valueArray[6]; goto case 6;
|
||||||
case 4: thisArray[3] |= valueArray[3]; goto case 3;
|
}
|
||||||
case 3: thisArray[2] |= valueArray[2]; goto case 2;
|
case 6:
|
||||||
case 2: thisArray[1] |= valueArray[1]; goto case 1;
|
{
|
||||||
case 1: thisArray[0] |= valueArray[0]; goto Done;
|
thisArray[5] |= valueArray[5]; goto case 5;
|
||||||
case 0: goto Done;
|
}
|
||||||
|
case 5:
|
||||||
|
{
|
||||||
|
thisArray[4] |= valueArray[4]; goto case 4;
|
||||||
|
}
|
||||||
|
case 4:
|
||||||
|
{
|
||||||
|
thisArray[3] |= valueArray[3]; goto case 3;
|
||||||
|
}
|
||||||
|
case 3:
|
||||||
|
{
|
||||||
|
thisArray[2] |= valueArray[2]; goto case 2;
|
||||||
|
}
|
||||||
|
case 2:
|
||||||
|
{
|
||||||
|
thisArray[1] |= valueArray[1]; goto case 1;
|
||||||
|
}
|
||||||
|
case 1:
|
||||||
|
{
|
||||||
|
thisArray[0] |= valueArray[0]; goto Done;
|
||||||
|
}
|
||||||
|
case 0:
|
||||||
|
{
|
||||||
|
goto Done;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint i = 0;
|
uint i = 0;
|
||||||
|
|
@ -695,14 +701,38 @@ public sealed class BitArray : ICollection, ICloneable
|
||||||
// Unroll loop for count less than Vector256 size.
|
// Unroll loop for count less than Vector256 size.
|
||||||
switch (count)
|
switch (count)
|
||||||
{
|
{
|
||||||
case 7: thisArray[6] ^= valueArray[6]; goto case 6;
|
case 7:
|
||||||
case 6: thisArray[5] ^= valueArray[5]; goto case 5;
|
{
|
||||||
case 5: thisArray[4] ^= valueArray[4]; goto case 4;
|
thisArray[6] ^= valueArray[6]; goto case 6;
|
||||||
case 4: thisArray[3] ^= valueArray[3]; goto case 3;
|
}
|
||||||
case 3: thisArray[2] ^= valueArray[2]; goto case 2;
|
case 6:
|
||||||
case 2: thisArray[1] ^= valueArray[1]; goto case 1;
|
{
|
||||||
case 1: thisArray[0] ^= valueArray[0]; goto Done;
|
thisArray[5] ^= valueArray[5]; goto case 5;
|
||||||
case 0: goto Done;
|
}
|
||||||
|
case 5:
|
||||||
|
{
|
||||||
|
thisArray[4] ^= valueArray[4]; goto case 4;
|
||||||
|
}
|
||||||
|
case 4:
|
||||||
|
{
|
||||||
|
thisArray[3] ^= valueArray[3]; goto case 3;
|
||||||
|
}
|
||||||
|
case 3:
|
||||||
|
{
|
||||||
|
thisArray[2] ^= valueArray[2]; goto case 2;
|
||||||
|
}
|
||||||
|
case 2:
|
||||||
|
{
|
||||||
|
thisArray[1] ^= valueArray[1]; goto case 1;
|
||||||
|
}
|
||||||
|
case 1:
|
||||||
|
{
|
||||||
|
thisArray[0] ^= valueArray[0]; goto Done;
|
||||||
|
}
|
||||||
|
case 0:
|
||||||
|
{
|
||||||
|
goto Done;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint i = 0;
|
uint i = 0;
|
||||||
|
|
@ -781,14 +811,38 @@ public sealed class BitArray : ICollection, ICloneable
|
||||||
// Unroll loop for count less than Vector256 size.
|
// Unroll loop for count less than Vector256 size.
|
||||||
switch (count)
|
switch (count)
|
||||||
{
|
{
|
||||||
case 7: thisArray[6] = ~thisArray[6]; goto case 6;
|
case 7:
|
||||||
case 6: thisArray[5] = ~thisArray[5]; goto case 5;
|
{
|
||||||
case 5: thisArray[4] = ~thisArray[4]; goto case 4;
|
thisArray[6] = ~thisArray[6]; goto case 6;
|
||||||
case 4: thisArray[3] = ~thisArray[3]; goto case 3;
|
}
|
||||||
case 3: thisArray[2] = ~thisArray[2]; goto case 2;
|
case 6:
|
||||||
case 2: thisArray[1] = ~thisArray[1]; goto case 1;
|
{
|
||||||
case 1: thisArray[0] = ~thisArray[0]; goto Done;
|
thisArray[5] = ~thisArray[5]; goto case 5;
|
||||||
case 0: goto Done;
|
}
|
||||||
|
case 5:
|
||||||
|
{
|
||||||
|
thisArray[4] = ~thisArray[4]; goto case 4;
|
||||||
|
}
|
||||||
|
case 4:
|
||||||
|
{
|
||||||
|
thisArray[3] = ~thisArray[3]; goto case 3;
|
||||||
|
}
|
||||||
|
case 3:
|
||||||
|
{
|
||||||
|
thisArray[2] = ~thisArray[2]; goto case 2;
|
||||||
|
}
|
||||||
|
case 2:
|
||||||
|
{
|
||||||
|
thisArray[1] = ~thisArray[1]; goto case 1;
|
||||||
|
}
|
||||||
|
case 1:
|
||||||
|
{
|
||||||
|
thisArray[0] = ~thisArray[0]; goto Done;
|
||||||
|
}
|
||||||
|
case 0:
|
||||||
|
{
|
||||||
|
goto Done;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint i = 0;
|
uint i = 0;
|
||||||
|
|
@ -964,10 +1018,7 @@ public sealed class BitArray : ICollection, ICloneable
|
||||||
|
|
||||||
public int Length
|
public int Length
|
||||||
{
|
{
|
||||||
get
|
get => m_length;
|
||||||
{
|
|
||||||
return m_length;
|
|
||||||
}
|
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (value < 0)
|
if (value < 0)
|
||||||
|
|
@ -1035,16 +1086,22 @@ public sealed class BitArray : ICollection, ICloneable
|
||||||
switch (remainder)
|
switch (remainder)
|
||||||
{
|
{
|
||||||
case 3:
|
case 3:
|
||||||
span[2] = (byte)(m_array[quotient] >> 16);
|
{
|
||||||
goto case 2;
|
span[2] = (byte)(m_array[quotient] >> 16);
|
||||||
|
goto case 2;
|
||||||
|
}
|
||||||
// fall through
|
// fall through
|
||||||
case 2:
|
case 2:
|
||||||
span[1] = (byte)(m_array[quotient] >> 8);
|
{
|
||||||
goto case 1;
|
span[1] = (byte)(m_array[quotient] >> 8);
|
||||||
|
goto case 1;
|
||||||
|
}
|
||||||
// fall through
|
// fall through
|
||||||
case 1:
|
case 1:
|
||||||
span[0] = (byte)m_array[quotient];
|
{
|
||||||
break;
|
span[0] = (byte)m_array[quotient];
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1084,54 +1141,6 @@ public sealed class BitArray : ICollection, ICloneable
|
||||||
intArray[index + last] = m_array[last] & unchecked((1 << extraBits) - 1);
|
intArray[index + last] = m_array[last] & unchecked((1 << extraBits) - 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (array is byte[] byteArray)
|
|
||||||
{
|
|
||||||
int arrayLength = GetByteArrayLengthFromBitLength(m_length);
|
|
||||||
if (array.Length - index < arrayLength)
|
|
||||||
{
|
|
||||||
throw new ArgumentException(CollectionThrowStrings.Argument_InvalidOffLen);
|
|
||||||
}
|
|
||||||
|
|
||||||
// equivalent to m_length % BitsPerByte, since BitsPerByte is a power of 2
|
|
||||||
uint extraBits = (uint)m_length & (BitsPerByte - 1);
|
|
||||||
if (extraBits > 0)
|
|
||||||
{
|
|
||||||
// last byte is not aligned, we will directly copy one less byte
|
|
||||||
arrayLength -= 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
Span<byte> span = byteArray.AsSpan(index);
|
|
||||||
|
|
||||||
int quotient = Div4Rem(arrayLength, out int remainder);
|
|
||||||
for (int i = 0; i < quotient; i++)
|
|
||||||
{
|
|
||||||
BinaryPrimitives.WriteInt32LittleEndian(span, m_array[i]);
|
|
||||||
span = span[4..];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (extraBits > 0)
|
|
||||||
{
|
|
||||||
Debug.Assert(span.Length > 0);
|
|
||||||
Debug.Assert(m_array.Length > quotient);
|
|
||||||
// mask the final byte
|
|
||||||
span[remainder] = (byte)((m_array[quotient] >> (remainder * 8)) & ((1 << (int)extraBits) - 1));
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (remainder)
|
|
||||||
{
|
|
||||||
case 3:
|
|
||||||
span[2] = (byte)(m_array[quotient] >> 16);
|
|
||||||
goto case 2;
|
|
||||||
// fall through
|
|
||||||
case 2:
|
|
||||||
span[1] = (byte)(m_array[quotient] >> 8);
|
|
||||||
goto case 1;
|
|
||||||
// fall through
|
|
||||||
case 1:
|
|
||||||
span[0] = (byte)m_array[quotient];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (array is bool[] boolArray)
|
else if (array is bool[] boolArray)
|
||||||
{
|
{
|
||||||
if (array.Length - index < m_length)
|
if (array.Length - index < m_length)
|
||||||
|
|
|
||||||
|
|
@ -159,13 +159,15 @@ namespace Server
|
||||||
|
|
||||||
public BitArray ReadBitArray()
|
public BitArray ReadBitArray()
|
||||||
{
|
{
|
||||||
var length = ((IGenericReader)this).ReadEncodedInt();
|
var bitLength = ((IGenericReader)this).ReadEncodedInt();
|
||||||
|
var length = BitArray.GetByteArrayLengthFromBitLength(bitLength);
|
||||||
|
|
||||||
if (length > _buffer.Length - _position)
|
if (length > _buffer.Length - _position)
|
||||||
{
|
{
|
||||||
throw new OutOfMemoryException();
|
throw new OutOfMemoryException();
|
||||||
}
|
}
|
||||||
|
|
||||||
var bitArray = new BitArray(_buffer.AsSpan(_position, length));
|
var bitArray = new BitArray(_buffer.AsSpan(_position, length), bitLength);
|
||||||
_position += length;
|
_position += length;
|
||||||
return bitArray;
|
return bitArray;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -138,9 +138,9 @@ namespace Server
|
||||||
public void Write(BitArray bitArray)
|
public void Write(BitArray bitArray)
|
||||||
{
|
{
|
||||||
var byteLength = BitArray.GetByteArrayLengthFromBitLength(bitArray.Length);
|
var byteLength = BitArray.GetByteArrayLengthFromBitLength(bitArray.Length);
|
||||||
FlushIfNeeded(byteLength + 4);
|
|
||||||
|
|
||||||
((IGenericWriter)this).WriteEncodedInt(byteLength);
|
((IGenericWriter)this).WriteEncodedInt(bitArray.Length);
|
||||||
|
FlushIfNeeded(byteLength);
|
||||||
bitArray.CopyTo(_buffer.AsSpan((int)Index, byteLength));
|
bitArray.CopyTo(_buffer.AsSpan((int)Index, byteLength));
|
||||||
Index += byteLength;
|
Index += byteLength;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue