diff --git a/README.md b/README.md
index 4a814bf..e2a5414 100644
--- a/README.md
+++ b/README.md
@@ -63,32 +63,32 @@ class MyBasicObj
And assume these are our input bytes (in little endian):
### Input Bytes (Little Endian):
```cs
-0x00, 0x08,
-0xFF, 0x01,
-0x00, 0x00, 0x4A, 0x7A, 0x9E, 0x01, 0xC0, 0x08,
-
-0x00, 0x00, 0x00, 0x00,
-0x01, 0x00, 0x00, 0x00,
-0x02, 0x00, 0x00, 0x00,
-0x03, 0x00, 0x00, 0x00,
-0x04, 0x00, 0x00, 0x00,
-0x05, 0x00, 0x00, 0x00,
-0x06, 0x00, 0x00, 0x00,
-0x07, 0x00, 0x00, 0x00,
-0x08, 0x00, 0x00, 0x00,
-0x09, 0x00, 0x00, 0x00,
-0x0A, 0x00, 0x00, 0x00,
-0x0B, 0x00, 0x00, 0x00,
-0x0C, 0x00, 0x00, 0x00,
-0x0D, 0x00, 0x00, 0x00,
-0x0E, 0x00, 0x00, 0x00,
-0x0F, 0x00, 0x00, 0x00,
-
-0x00, 0x00, 0x00, 0x00,
-
-0x45, 0x6E, 0x64, 0x69, 0x61, 0x6E, 0x42, 0x69, 0x6E, 0x61, 0x72, 0x79, 0x49, 0x4F, 0x00,
-
-0x4B, 0x00, 0x65, 0x00, 0x72, 0x00, 0x6D, 0x00, 0x61, 0x00, 0x6C, 0x00, 0x69, 0x00, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00
+0x00, 0x08, // ShortSizedEnum.Val2
+0xFF, 0x01, // (short)0x1FF
+0x00, 0x00, 0x4A, 0x7A, 0x9E, 0x01, 0xC0, 0x08, // (DateTime)Dec. 30, 1998
+
+0x00, 0x00, 0x00, 0x00, // (uint)0
+0x01, 0x00, 0x00, 0x00, // (uint)1
+0x02, 0x00, 0x00, 0x00, // (uint)2
+0x03, 0x00, 0x00, 0x00, // (uint)3
+0x04, 0x00, 0x00, 0x00, // (uint)4
+0x05, 0x00, 0x00, 0x00, // (uint)5
+0x06, 0x00, 0x00, 0x00, // (uint)6
+0x07, 0x00, 0x00, 0x00, // (uint)7
+0x08, 0x00, 0x00, 0x00, // (uint)8
+0x09, 0x00, 0x00, 0x00, // (uint)9
+0x0A, 0x00, 0x00, 0x00, // (uint)10
+0x0B, 0x00, 0x00, 0x00, // (uint)11
+0x0C, 0x00, 0x00, 0x00, // (uint)12
+0x0D, 0x00, 0x00, 0x00, // (uint)13
+0x0E, 0x00, 0x00, 0x00, // (uint)14
+0x0F, 0x00, 0x00, 0x00, // (uint)15
+
+0x00, 0x00, 0x00, 0x00, // (bool32)false
+
+0x45, 0x6E, 0x64, 0x69, 0x61, 0x6E, 0x42, 0x69, 0x6E, 0x61, 0x72, 0x79, 0x49, 0x4F, 0x00, // "EndianBinaryIO\0"
+
+0x4B, 0x00, 0x65, 0x00, 0x72, 0x00, 0x6D, 0x00, 0x61, 0x00, 0x6C, 0x00, 0x69, 0x00, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, // "Kermalis\0\0"
```
We can read/write the object manually or automatically (with reflection):
diff --git a/Source/Attributes.cs b/Source/Attributes.cs
index 092e936..74cd135 100644
--- a/Source/Attributes.cs
+++ b/Source/Attributes.cs
@@ -49,7 +49,7 @@ public sealed class BinaryArrayFixedLengthAttribute : Attribute
public BinaryArrayFixedLengthAttribute(int length)
{
- if (length <= 0)
+ if (length < 0)
{
throw new ArgumentOutOfRangeException(nameof(length));
}
diff --git a/Source/EndianBinaryIO.csproj b/Source/EndianBinaryIO.csproj
index ca31666..ad29e8f 100644
--- a/Source/EndianBinaryIO.csproj
+++ b/Source/EndianBinaryIO.csproj
@@ -9,7 +9,7 @@
EndianBinaryIO
EndianBinaryIO
EndianBinaryIO
- 1.0.2.0
+ 1.0.3.0
https://github.com/Kermalis/EndianBinaryIO
git
diff --git a/Source/EndianBinaryReader.cs b/Source/EndianBinaryReader.cs
index 3f73d1e..886a3fb 100644
--- a/Source/EndianBinaryReader.cs
+++ b/Source/EndianBinaryReader.cs
@@ -40,6 +40,21 @@ public void Dispose()
}
}
+ // Returns true if count is 0
+ private static bool ValidateArraySize(int count, out T[] array)
+ {
+ if (count < 0)
+ {
+ throw new ArgumentOutOfRangeException($"Invalid array length ({count})");
+ }
+ if (count == 0)
+ {
+ array = Array.Empty();
+ return true;
+ }
+ array = null;
+ return false;
+ }
private void ReadBytesIntoBuffer(int primitiveCount, int primitiveSize)
{
int byteCount = primitiveCount * primitiveSize;
@@ -150,10 +165,13 @@ public bool[] ReadBooleans(int count, long offset)
}
public bool[] ReadBooleans(int count, BooleanSize size)
{
- bool[] array = new bool[count];
- for (int i = 0; i < count; i++)
+ if (!ValidateArraySize(count, out bool[] array))
{
- array[i] = ReadBoolean(size);
+ array = new bool[count];
+ for (int i = 0; i < count; i++)
+ {
+ array[i] = ReadBoolean(size);
+ }
}
return array;
}
@@ -174,11 +192,14 @@ public byte ReadByte(long offset)
}
public byte[] ReadBytes(int count)
{
- ReadBytesIntoBuffer(count, 1);
- byte[] array = new byte[count];
- for (int i = 0; i < count; i++)
+ if (!ValidateArraySize(count, out byte[] array))
{
- array[i] = _buffer[i];
+ ReadBytesIntoBuffer(count, 1);
+ array = new byte[count];
+ for (int i = 0; i < count; i++)
+ {
+ array[i] = _buffer[i];
+ }
}
return array;
}
@@ -199,11 +220,14 @@ public sbyte ReadSByte(long offset)
}
public sbyte[] ReadSBytes(int count)
{
- ReadBytesIntoBuffer(count, 1);
- sbyte[] array = new sbyte[count];
- for (int i = 0; i < count; i++)
+ if (!ValidateArraySize(count, out sbyte[] array))
{
- array[i] = (sbyte)_buffer[i];
+ ReadBytesIntoBuffer(count, 1);
+ array = new sbyte[count];
+ for (int i = 0; i < count; i++)
+ {
+ array[i] = (sbyte)_buffer[i];
+ }
}
return array;
}
@@ -244,6 +268,10 @@ public char[] ReadChars(int count, long offset)
}
public char[] ReadChars(int count, EncodingType encodingType)
{
+ if (ValidateArraySize(count, out char[] array))
+ {
+ return array;
+ }
Encoding encoding = Utils.EncodingFromEnum(encodingType);
int encodingSize = Utils.EncodingSize(encoding);
ReadBytesIntoBuffer(count, encodingSize);
@@ -300,6 +328,58 @@ public string ReadString(int charCount, EncodingType encodingType, long offset)
BaseStream.Position = offset;
return ReadString(charCount, encodingType);
}
+ public string[] ReadStringsNullTerminated(int count)
+ {
+ return ReadStringsNullTerminated(count, Encoding);
+ }
+ public string[] ReadStringsNullTerminated(int count, long offset)
+ {
+ BaseStream.Position = offset;
+ return ReadStringsNullTerminated(count);
+ }
+ public string[] ReadStringsNullTerminated(int count, EncodingType encodingType)
+ {
+ if (!ValidateArraySize(count, out string[] array))
+ {
+ array = new string[count];
+ for (int i = 0; i < count; i++)
+ {
+ array[i] = ReadStringNullTerminated(encodingType);
+ }
+ }
+ return array;
+ }
+ public string[] ReadStringsNullTerminated(int count, EncodingType encodingType, long offset)
+ {
+ BaseStream.Position = offset;
+ return ReadStringsNullTerminated(count, encodingType);
+ }
+ public string[] ReadStrings(int count, int charCount)
+ {
+ return ReadStrings(count, charCount, Encoding);
+ }
+ public string[] ReadStrings(int count, int charCount, long offset)
+ {
+ BaseStream.Position = offset;
+ return ReadStrings(count, charCount);
+ }
+ public string[] ReadStrings(int count, int charCount, EncodingType encodingType)
+ {
+ if (!ValidateArraySize(count, out string[] array))
+ {
+ array = new string[count];
+ for (int i = 0; i < count; i++)
+ {
+ array[i] = ReadString(charCount, encodingType);
+ }
+ }
+ return array;
+ }
+ public string[] ReadStrings(int count, int charCount, EncodingType encodingType, long offset)
+ {
+ BaseStream.Position = offset;
+ return ReadStrings(count, charCount, encodingType);
+ }
public short ReadInt16()
{
ReadBytesIntoBuffer(1, 2);
@@ -312,11 +392,14 @@ public short ReadInt16(long offset)
}
public short[] ReadInt16s(int count)
{
- ReadBytesIntoBuffer(count, 2);
- short[] array = new short[count];
- for (int i = 0; i < count; i++)
+ if (!ValidateArraySize(count, out short[] array))
{
- array[i] = Utils.BytesToInt16(_buffer, 2 * i);
+ ReadBytesIntoBuffer(count, 2);
+ array = new short[count];
+ for (int i = 0; i < count; i++)
+ {
+ array[i] = Utils.BytesToInt16(_buffer, 2 * i);
+ }
}
return array;
}
@@ -337,11 +420,14 @@ public ushort ReadUInt16(long offset)
}
public ushort[] ReadUInt16s(int count)
{
- ReadBytesIntoBuffer(count, 2);
- ushort[] array = new ushort[count];
- for (int i = 0; i < count; i++)
+ if (!ValidateArraySize(count, out ushort[] array))
{
- array[i] = (ushort)Utils.BytesToInt16(_buffer, 2 * i);
+ ReadBytesIntoBuffer(count, 2);
+ array = new ushort[count];
+ for (int i = 0; i < count; i++)
+ {
+ array[i] = (ushort)Utils.BytesToInt16(_buffer, 2 * i);
+ }
}
return array;
}
@@ -362,11 +448,14 @@ public int ReadInt32(long offset)
}
public int[] ReadInt32s(int count)
{
- ReadBytesIntoBuffer(count, 4);
- int[] array = new int[count];
- for (int i = 0; i < count; i++)
+ if (!ValidateArraySize(count, out int[] array))
{
- array[i] = Utils.BytesToInt32(_buffer, 4 * i);
+ ReadBytesIntoBuffer(count, 4);
+ array = new int[count];
+ for (int i = 0; i < count; i++)
+ {
+ array[i] = Utils.BytesToInt32(_buffer, 4 * i);
+ }
}
return array;
}
@@ -387,11 +476,14 @@ public uint ReadUInt32(long offset)
}
public uint[] ReadUInt32s(int count)
{
- ReadBytesIntoBuffer(count, 4);
- uint[] array = new uint[count];
- for (int i = 0; i < count; i++)
+ if (!ValidateArraySize(count, out uint[] array))
{
- array[i] = (uint)Utils.BytesToInt32(_buffer, 4 * i);
+ ReadBytesIntoBuffer(count, 4);
+ array = new uint[count];
+ for (int i = 0; i < count; i++)
+ {
+ array[i] = (uint)Utils.BytesToInt32(_buffer, 4 * i);
+ }
}
return array;
}
@@ -412,11 +504,14 @@ public long ReadInt64(long offset)
}
public long[] ReadInt64s(int count)
{
- ReadBytesIntoBuffer(count, 8);
- long[] array = new long[count];
- for (int i = 0; i < count; i++)
+ if (!ValidateArraySize(count, out long[] array))
{
- array[i] = Utils.BytesToInt64(_buffer, 8 * i);
+ ReadBytesIntoBuffer(count, 8);
+ array = new long[count];
+ for (int i = 0; i < count; i++)
+ {
+ array[i] = Utils.BytesToInt64(_buffer, 8 * i);
+ }
}
return array;
}
@@ -437,11 +532,14 @@ public ulong ReadUInt64(long offset)
}
public ulong[] ReadUInt64s(int count)
{
- ReadBytesIntoBuffer(count, 8);
- ulong[] array = new ulong[count];
- for (int i = 0; i < count; i++)
+ if (!ValidateArraySize(count, out ulong[] array))
{
- array[i] = (ulong)Utils.BytesToInt64(_buffer, 8 * i);
+ ReadBytesIntoBuffer(count, 8);
+ array = new ulong[count];
+ for (int i = 0; i < count; i++)
+ {
+ array[i] = (ulong)Utils.BytesToInt64(_buffer, 8 * i);
+ }
}
return array;
}
@@ -462,11 +560,14 @@ public float ReadSingle(long offset)
}
public float[] ReadSingles(int count)
{
- ReadBytesIntoBuffer(count, 4);
- float[] array = new float[count];
- for (int i = 0; i < count; i++)
+ if (!ValidateArraySize(count, out float[] array))
{
- array[i] = Utils.BytesToSingle(_buffer, 4 * i);
+ ReadBytesIntoBuffer(count, 4);
+ array = new float[count];
+ for (int i = 0; i < count; i++)
+ {
+ array[i] = Utils.BytesToSingle(_buffer, 4 * i);
+ }
}
return array;
}
@@ -487,11 +588,14 @@ public double ReadDouble(long offset)
}
public double[] ReadDoubles(int count)
{
- ReadBytesIntoBuffer(count, 8);
- double[] array = new double[count];
- for (int i = 0; i < count; i++)
+ if (!ValidateArraySize(count, out double[] array))
{
- array[i] = Utils.BytesToDouble(_buffer, 8 * i);
+ ReadBytesIntoBuffer(count, 8);
+ array = new double[count];
+ for (int i = 0; i < count; i++)
+ {
+ array[i] = Utils.BytesToDouble(_buffer, 8 * i);
+ }
}
return array;
}
@@ -512,11 +616,14 @@ public decimal ReadDecimal(long offset)
}
public decimal[] ReadDecimals(int count)
{
- ReadBytesIntoBuffer(count, 16);
- decimal[] array = new decimal[count];
- for (int i = 0; i < count; i++)
+ if (!ValidateArraySize(count, out decimal[] array))
{
- array[i] = Utils.BytesToDecimal(_buffer, 16 * i);
+ ReadBytesIntoBuffer(count, 16);
+ array = new decimal[count];
+ for (int i = 0; i < count; i++)
+ {
+ array[i] = Utils.BytesToDecimal(_buffer, 16 * i);
+ }
}
return array;
}
@@ -554,10 +661,13 @@ public decimal[] ReadDecimals(int count, long offset)
}
public TEnum[] ReadEnums(int count) where TEnum : struct, Enum
{
- var array = new TEnum[count];
- for (int i = 0; i < count; i++)
+ if (!ValidateArraySize(count, out TEnum[] array))
{
- array[i] = ReadEnum();
+ array = new TEnum[count];
+ for (int i = 0; i < count; i++)
+ {
+ array[i] = ReadEnum();
+ }
}
return array;
}
@@ -578,10 +688,13 @@ public DateTime ReadDateTime(long offset)
}
public DateTime[] ReadDateTimes(int count)
{
- var array = new DateTime[count];
- for (int i = 0; i < count; i++)
+ if (!ValidateArraySize(count, out DateTime[] array))
{
- array[i] = ReadDateTime();
+ array = new DateTime[count];
+ for (int i = 0; i < count; i++)
+ {
+ array[i] = ReadDateTime();
+ }
}
return array;
}
@@ -656,79 +769,80 @@ public void ReadIntoObject(object obj)
int arrayLength = Utils.GetArrayLength(obj, objType, propertyInfo);
// Get array type
Type elementType = propertyType.GetElementType();
- if (elementType.IsEnum)
+ if (arrayLength == 0)
{
- elementType = Enum.GetUnderlyingType(elementType);
+ value = Array.CreateInstance(elementType, 0); // Create 0 length array regardless of type
}
- switch (Type.GetTypeCode(elementType))
+ else
{
- case TypeCode.Boolean:
+ if (elementType.IsEnum)
{
- BooleanSize booleanSize = Utils.AttributeValueOrDefault(propertyInfo, typeof(BinaryBooleanSizeAttribute), BooleanSize);
- value = ReadBooleans(arrayLength, booleanSize);
- break;
+ elementType = Enum.GetUnderlyingType(elementType);
}
- case TypeCode.Byte: value = ReadBytes(arrayLength); break;
- case TypeCode.SByte: value = ReadSBytes(arrayLength); break;
- case TypeCode.Char:
+ switch (Type.GetTypeCode(elementType))
{
- EncodingType encodingType = Utils.AttributeValueOrDefault(propertyInfo, typeof(BinaryEncodingAttribute), Encoding);
- value = ReadChars(arrayLength, encodingType);
- break;
- }
- case TypeCode.Int16: value = ReadInt16s(arrayLength); break;
- case TypeCode.UInt16: value = ReadUInt16s(arrayLength); break;
- case TypeCode.Int32: value = ReadInt32s(arrayLength); break;
- case TypeCode.UInt32: value = ReadUInt32s(arrayLength); break;
- case TypeCode.Int64: value = ReadInt64s(arrayLength); break;
- case TypeCode.UInt64: value = ReadUInt64s(arrayLength); break;
- case TypeCode.Single: value = ReadSingles(arrayLength); break;
- case TypeCode.Double: value = ReadDoubles(arrayLength); break;
- case TypeCode.Decimal: value = ReadDecimals(arrayLength); break;
- case TypeCode.DateTime: value = ReadDateTimes(arrayLength); break;
- case TypeCode.String:
- {
- Utils.GetStringLength(obj, objType, propertyInfo, true, out bool? nullTerminated, out int stringLength);
- EncodingType encodingType = Utils.AttributeValueOrDefault(propertyInfo, typeof(BinaryEncodingAttribute), Encoding);
- value = Array.CreateInstance(elementType, arrayLength);
- for (int i = 0; i < arrayLength; i++)
+ case TypeCode.Boolean:
{
- string str;
+ BooleanSize booleanSize = Utils.AttributeValueOrDefault(propertyInfo, typeof(BinaryBooleanSizeAttribute), BooleanSize);
+ value = ReadBooleans(arrayLength, booleanSize);
+ break;
+ }
+ case TypeCode.Byte: value = ReadBytes(arrayLength); break;
+ case TypeCode.SByte: value = ReadSBytes(arrayLength); break;
+ case TypeCode.Char:
+ {
+ EncodingType encodingType = Utils.AttributeValueOrDefault(propertyInfo, typeof(BinaryEncodingAttribute), Encoding);
+ value = ReadChars(arrayLength, encodingType);
+ break;
+ }
+ case TypeCode.Int16: value = ReadInt16s(arrayLength); break;
+ case TypeCode.UInt16: value = ReadUInt16s(arrayLength); break;
+ case TypeCode.Int32: value = ReadInt32s(arrayLength); break;
+ case TypeCode.UInt32: value = ReadUInt32s(arrayLength); break;
+ case TypeCode.Int64: value = ReadInt64s(arrayLength); break;
+ case TypeCode.UInt64: value = ReadUInt64s(arrayLength); break;
+ case TypeCode.Single: value = ReadSingles(arrayLength); break;
+ case TypeCode.Double: value = ReadDoubles(arrayLength); break;
+ case TypeCode.Decimal: value = ReadDecimals(arrayLength); break;
+ case TypeCode.DateTime: value = ReadDateTimes(arrayLength); break;
+ case TypeCode.String:
+ {
+ Utils.GetStringLength(obj, objType, propertyInfo, true, out bool? nullTerminated, out int stringLength);
+ EncodingType encodingType = Utils.AttributeValueOrDefault(propertyInfo, typeof(BinaryEncodingAttribute), Encoding);
if (nullTerminated == true)
{
- str = ReadStringNullTerminated(encodingType);
+ value = ReadStringsNullTerminated(arrayLength, encodingType);
}
else
{
- str = ReadString(stringLength, encodingType);
+ value = ReadStrings(arrayLength, stringLength, encodingType);
}
- ((Array)value).SetValue(str, i);
+ break;
}
- break;
- }
- case TypeCode.Object:
- {
- value = Array.CreateInstance(elementType, arrayLength);
- if (typeof(IBinarySerializable).IsAssignableFrom(elementType))
+ case TypeCode.Object:
{
- for (int i = 0; i < arrayLength; i++)
+ value = Array.CreateInstance(elementType, arrayLength);
+ if (typeof(IBinarySerializable).IsAssignableFrom(elementType))
{
- var serializable = (IBinarySerializable)Activator.CreateInstance(elementType);
- serializable.Read(this);
- ((Array)value).SetValue(serializable, i);
+ for (int i = 0; i < arrayLength; i++)
+ {
+ var serializable = (IBinarySerializable)Activator.CreateInstance(elementType);
+ serializable.Read(this);
+ ((Array)value).SetValue(serializable, i);
+ }
}
- }
- else // Element's type is not supported so try to read the array's objects
- {
- for (int i = 0; i < arrayLength; i++)
+ else // Element's type is not supported so try to read the array's objects
{
- object elementObj = ReadObject(elementType);
- ((Array)value).SetValue(elementObj, i);
+ for (int i = 0; i < arrayLength; i++)
+ {
+ object elementObj = ReadObject(elementType);
+ ((Array)value).SetValue(elementObj, i);
+ }
}
+ break;
}
- break;
+ default: throw new ArgumentOutOfRangeException(nameof(elementType));
}
- default: throw new ArgumentOutOfRangeException(nameof(elementType));
}
}
else
diff --git a/Source/EndianBinaryWriter.cs b/Source/EndianBinaryWriter.cs
index 023ad8f..6abf474 100644
--- a/Source/EndianBinaryWriter.cs
+++ b/Source/EndianBinaryWriter.cs
@@ -40,6 +40,15 @@ public void Dispose()
}
}
+ // Returns true if count is 0
+ private static bool ValidateArraySize(int count)
+ {
+ if (count < 0)
+ {
+ throw new ArgumentOutOfRangeException($"Invalid array length ({count})");
+ }
+ return count == 0;
+ }
private void SetBufferSize(int size)
{
if (_buffer is null || _buffer.Length < size)
@@ -135,10 +144,7 @@ public void Write(bool[] value, BooleanSize booleanSize, long offset)
}
public void Write(bool[] value, int index, int count)
{
- for (int i = index; i < count; i++)
- {
- Write(value[i], BooleanSize);
- }
+ Write(value, index, count, BooleanSize);
}
public void Write(bool[] value, int index, int count, long offset)
{
@@ -147,6 +153,10 @@ public void Write(bool[] value, int index, int count, long offset)
}
public void Write(bool[] value, int index, int count, BooleanSize booleanSize)
{
+ if (ValidateArraySize(count))
+ {
+ return;
+ }
for (int i = index; i < count; i++)
{
Write(value[i], booleanSize);
@@ -179,6 +189,10 @@ public void Write(byte[] value, long offset)
}
public void Write(byte[] value, int index, int count)
{
+ if (ValidateArraySize(count))
+ {
+ return;
+ }
SetBufferSize(count);
for (int i = 0; i < count; i++)
{
@@ -213,6 +227,10 @@ public void Write(sbyte[] value, long offset)
}
public void Write(sbyte[] value, int index, int count)
{
+ if (ValidateArraySize(count))
+ {
+ return;
+ }
SetBufferSize(count);
for (int i = 0; i < count; i++)
{
@@ -280,6 +298,10 @@ public void Write(char[] value, int index, int count, long offset)
}
public void Write(char[] value, int index, int count, EncodingType encodingType)
{
+ if (ValidateArraySize(count))
+ {
+ return;
+ }
Encoding encoding = Utils.EncodingFromEnum(encodingType);
int encodingSize = Utils.EncodingSize(encoding);
SetBufferSize(encodingSize * count);
@@ -336,6 +358,56 @@ public void Write(string value, int charCount, EncodingType encodingType, long o
BaseStream.Position = offset;
Write(value, charCount, encodingType);
}
+ public void Write(string[] value, int index, int count, bool nullTerminated)
+ {
+ Write(value, index, count, nullTerminated, Encoding);
+ }
+ public void Write(string[] value, int index, int count, bool nullTerminated, long offset)
+ {
+ BaseStream.Position = offset;
+ Write(value, index, count, nullTerminated, Encoding);
+ }
+ public void Write(string[] value, int index, int count, bool nullTerminated, EncodingType encodingType)
+ {
+ if (ValidateArraySize(count))
+ {
+ return;
+ }
+ for (int i = 0; i < count; i++)
+ {
+ Write(value[i + index], nullTerminated, encodingType);
+ }
+ }
+ public void Write(string[] value, int index, int count, bool nullTerminated, EncodingType encodingType, long offset)
+ {
+ BaseStream.Position = offset;
+ Write(value, index, count, nullTerminated, encodingType);
+ }
+ public void Write(string[] value, int index, int count, int charCount)
+ {
+ Write(value, index, count, charCount, Encoding);
+ }
+ public void Write(string[] value, int index, int count, int charCount, long offset)
+ {
+ BaseStream.Position = offset;
+ Write(value, index, count, charCount, Encoding);
+ }
+ public void Write(string[] value, int index, int count, int charCount, EncodingType encodingType)
+ {
+ if (ValidateArraySize(count))
+ {
+ return;
+ }
+ for (int i = 0; i < count; i++)
+ {
+ Write(value[i + index], charCount, encodingType);
+ }
+ }
+ public void Write(string[] value, int index, int count, int charCount, EncodingType encodingType, long offset)
+ {
+ BaseStream.Position = offset;
+ Write(value, index, count, charCount, encodingType);
+ }
public void Write(short value)
{
SetBufferSize(2);
@@ -362,6 +434,10 @@ public void Write(short[] value, long offset)
}
public void Write(short[] value, int index, int count)
{
+ if (ValidateArraySize(count))
+ {
+ return;
+ }
SetBufferSize(2 * count);
for (int i = 0; i < count; i++)
{
@@ -404,6 +480,10 @@ public void Write(ushort[] value, long offset)
}
public void Write(ushort[] value, int index, int count)
{
+ if (ValidateArraySize(count))
+ {
+ return;
+ }
SetBufferSize(2 * count);
for (int i = 0; i < count; i++)
{
@@ -446,6 +526,10 @@ public void Write(int[] value, long offset)
}
public void Write(int[] value, int index, int count)
{
+ if (ValidateArraySize(count))
+ {
+ return;
+ }
SetBufferSize(4 * count);
for (int i = 0; i < count; i++)
{
@@ -488,6 +572,10 @@ public void Write(uint[] value, long offset)
}
public void Write(uint[] value, int index, int count)
{
+ if (ValidateArraySize(count))
+ {
+ return;
+ }
SetBufferSize(4 * count);
for (int i = 0; i < count; i++)
{
@@ -530,6 +618,10 @@ public void Write(long[] value, long offset)
}
public void Write(long[] value, int index, int count)
{
+ if (ValidateArraySize(count))
+ {
+ return;
+ }
SetBufferSize(8 * count);
for (int i = 0; i < count; i++)
{
@@ -572,6 +664,10 @@ public void Write(ulong[] value, long offset)
}
public void Write(ulong[] value, int index, int count)
{
+ if (ValidateArraySize(count))
+ {
+ return;
+ }
SetBufferSize(8 * count);
for (int i = 0; i < count; i++)
{
@@ -614,6 +710,10 @@ public void Write(float[] value, long offset)
}
public void Write(float[] value, int index, int count)
{
+ if (ValidateArraySize(count))
+ {
+ return;
+ }
SetBufferSize(4 * count);
for (int i = 0; i < count; i++)
{
@@ -656,6 +756,10 @@ public void Write(double[] value, long offset)
}
public void Write(double[] value, int index, int count)
{
+ if (ValidateArraySize(count))
+ {
+ return;
+ }
SetBufferSize(8 * count);
for (int i = 0; i < count; i++)
{
@@ -698,6 +802,10 @@ public void Write(decimal[] value, long offset)
}
public void Write(decimal[] value, int index, int count)
{
+ if (ValidateArraySize(count))
+ {
+ return;
+ }
SetBufferSize(16 * count);
for (int i = 0; i < count; i++)
{
@@ -750,6 +858,10 @@ public void Write(decimal[] value, int index, int count, long offset)
}
public void Write(TEnum[] value, int index, int count) where TEnum : Enum
{
+ if (ValidateArraySize(count))
+ {
+ return;
+ }
for (int i = 0; i < count; i++)
{
Write(value[i + index]);
@@ -781,6 +893,10 @@ public void Write(DateTime[] value, long offset)
}
public void Write(DateTime[] value, int index, int count)
{
+ if (ValidateArraySize(count))
+ {
+ return;
+ }
for (int i = 0; i < count; i++)
{
Write(value[i + index]);
@@ -834,76 +950,76 @@ public void Write(object obj)
if (propertyType.IsArray)
{
int arrayLength = Utils.GetArrayLength(obj, objType, propertyInfo);
- // Get array type
- Type elementType = propertyType.GetElementType();
- if (elementType.IsEnum)
+ if (arrayLength != 0) // Do not need to do anything for length 0
{
- elementType = Enum.GetUnderlyingType(elementType);
- }
- switch (Type.GetTypeCode(elementType))
- {
- case TypeCode.Boolean:
- {
- BooleanSize booleanSize = Utils.AttributeValueOrDefault(propertyInfo, typeof(BinaryBooleanSizeAttribute), BooleanSize);
- Write((bool[])value, 0, arrayLength, booleanSize);
- break;
- }
- case TypeCode.Byte: Write((byte[])value, 0, arrayLength); break;
- case TypeCode.SByte: Write((sbyte[])value, 0, arrayLength); break;
- case TypeCode.Char:
+ // Get array type
+ Type elementType = propertyType.GetElementType();
+ if (elementType.IsEnum)
{
- EncodingType encodingType = Utils.AttributeValueOrDefault(propertyInfo, typeof(BinaryEncodingAttribute), Encoding);
- Write((char[])value, 0, arrayLength, encodingType);
- break;
+ elementType = Enum.GetUnderlyingType(elementType);
}
- case TypeCode.Int16: Write((short[])value, 0, arrayLength); break;
- case TypeCode.UInt16: Write((ushort[])value, 0, arrayLength); break;
- case TypeCode.Int32: Write((int[])value, 0, arrayLength); break;
- case TypeCode.UInt32: Write((uint[])value, 0, arrayLength); break;
- case TypeCode.Int64: Write((long[])value, 0, arrayLength); break;
- case TypeCode.UInt64: Write((ulong[])value, 0, arrayLength); break;
- case TypeCode.Single: Write((float[])value, 0, arrayLength); break;
- case TypeCode.Double: Write((double[])value, 0, arrayLength); break;
- case TypeCode.Decimal: Write((decimal[])value, 0, arrayLength); break;
- case TypeCode.DateTime: Write((DateTime[])value, 0, arrayLength); break;
- case TypeCode.String:
+ switch (Type.GetTypeCode(elementType))
{
- Utils.GetStringLength(obj, objType, propertyInfo, false, out bool? nullTerminated, out int stringLength);
- EncodingType encodingType = Utils.AttributeValueOrDefault(propertyInfo, typeof(BinaryEncodingAttribute), Encoding);
- for (int i = 0; i < arrayLength; i++)
+ case TypeCode.Boolean:
+ {
+ BooleanSize booleanSize = Utils.AttributeValueOrDefault(propertyInfo, typeof(BinaryBooleanSizeAttribute), BooleanSize);
+ Write((bool[])value, 0, arrayLength, booleanSize);
+ break;
+ }
+ case TypeCode.Byte: Write((byte[])value, 0, arrayLength); break;
+ case TypeCode.SByte: Write((sbyte[])value, 0, arrayLength); break;
+ case TypeCode.Char:
{
- string str = (string)((Array)value).GetValue(i);
+ EncodingType encodingType = Utils.AttributeValueOrDefault(propertyInfo, typeof(BinaryEncodingAttribute), Encoding);
+ Write((char[])value, 0, arrayLength, encodingType);
+ break;
+ }
+ case TypeCode.Int16: Write((short[])value, 0, arrayLength); break;
+ case TypeCode.UInt16: Write((ushort[])value, 0, arrayLength); break;
+ case TypeCode.Int32: Write((int[])value, 0, arrayLength); break;
+ case TypeCode.UInt32: Write((uint[])value, 0, arrayLength); break;
+ case TypeCode.Int64: Write((long[])value, 0, arrayLength); break;
+ case TypeCode.UInt64: Write((ulong[])value, 0, arrayLength); break;
+ case TypeCode.Single: Write((float[])value, 0, arrayLength); break;
+ case TypeCode.Double: Write((double[])value, 0, arrayLength); break;
+ case TypeCode.Decimal: Write((decimal[])value, 0, arrayLength); break;
+ case TypeCode.DateTime: Write((DateTime[])value, 0, arrayLength); break;
+ case TypeCode.String:
+ {
+ Utils.GetStringLength(obj, objType, propertyInfo, false, out bool? nullTerminated, out int stringLength);
+ EncodingType encodingType = Utils.AttributeValueOrDefault(propertyInfo, typeof(BinaryEncodingAttribute), Encoding);
if (nullTerminated.HasValue)
{
- Write(str, nullTerminated.Value, encodingType);
+ Write((string[])value, 0, arrayLength, nullTerminated.Value, encodingType);
}
else
{
- Write(str, stringLength, encodingType);
+ Write((string[])value, 0, arrayLength, stringLength, encodingType);
}
+ break;
}
- break;
- }
- case TypeCode.Object:
- {
- if (typeof(IBinarySerializable).IsAssignableFrom(elementType))
+ case TypeCode.Object:
{
- for (int i = 0; i < arrayLength; i++)
+ if (typeof(IBinarySerializable).IsAssignableFrom(elementType))
{
- var serializable = (IBinarySerializable)((Array)value).GetValue(i);
- serializable.Write(this);
+ for (int i = 0; i < arrayLength; i++)
+ {
+ var serializable = (IBinarySerializable)((Array)value).GetValue(i);
+ serializable.Write(this);
+ }
}
- }
- else // Element's type is not supported so try to write the array's objects
- {
- for (int i = 0; i < arrayLength; i++)
+ else // Element's type is not supported so try to write the array's objects
{
- Write(((Array)value).GetValue(i));
+ for (int i = 0; i < arrayLength; i++)
+ {
+ object elementObj = ((Array)value).GetValue(i);
+ Write(elementObj);
+ }
}
+ break;
}
- break;
+ default: throw new ArgumentOutOfRangeException(nameof(elementType));
}
- default: throw new ArgumentOutOfRangeException(nameof(elementType));
}
}
else
diff --git a/Source/Utils.cs b/Source/Utils.cs
index e13bb82..b90a4d5 100644
--- a/Source/Utils.cs
+++ b/Source/Utils.cs
@@ -210,7 +210,7 @@ public static int GetArrayLength(object obj, Type objType, PropertyInfo property
{
int Validate(int value)
{
- if (value <= 0)
+ if (value < 0)
{
throw new ArgumentOutOfRangeException($"An array property in \"{objType.FullName}\" has an invalid length attribute ({value})");
}
diff --git a/Testing/BasicTests.cs b/Testing/BasicTests.cs
index f3e4a91..fc00c52 100644
--- a/Testing/BasicTests.cs
+++ b/Testing/BasicTests.cs
@@ -40,34 +40,34 @@ private sealed class MyBasicObj
public string UTF16String { get; set; }
}
- private static readonly byte[] _bytes = new byte[115]
+ private static readonly byte[] _bytes = new byte[]
{
- 0x00, 0x08,
- 0xFF, 0x01,
- 0x00, 0x00, 0x4A, 0x7A, 0x9E, 0x01, 0xC0, 0x08,
-
- 0x00, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x00, 0x00,
- 0x02, 0x00, 0x00, 0x00,
- 0x03, 0x00, 0x00, 0x00,
- 0x04, 0x00, 0x00, 0x00,
- 0x05, 0x00, 0x00, 0x00,
- 0x06, 0x00, 0x00, 0x00,
- 0x07, 0x00, 0x00, 0x00,
- 0x08, 0x00, 0x00, 0x00,
- 0x09, 0x00, 0x00, 0x00,
- 0x0A, 0x00, 0x00, 0x00,
- 0x0B, 0x00, 0x00, 0x00,
- 0x0C, 0x00, 0x00, 0x00,
- 0x0D, 0x00, 0x00, 0x00,
- 0x0E, 0x00, 0x00, 0x00,
- 0x0F, 0x00, 0x00, 0x00,
-
- 0x00, 0x00, 0x00, 0x00,
-
- 0x45, 0x6E, 0x64, 0x69, 0x61, 0x6E, 0x42, 0x69, 0x6E, 0x61, 0x72, 0x79, 0x49, 0x4F, 0x00,
-
- 0x4B, 0x00, 0x65, 0x00, 0x72, 0x00, 0x6D, 0x00, 0x61, 0x00, 0x6C, 0x00, 0x69, 0x00, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00
+ 0x00, 0x08, // ShortSizedEnum.Val2
+ 0xFF, 0x01, // (short)0x1FF
+ 0x00, 0x00, 0x4A, 0x7A, 0x9E, 0x01, 0xC0, 0x08, // (DateTime)Dec. 30, 1998
+
+ 0x00, 0x00, 0x00, 0x00, // (uint)0
+ 0x01, 0x00, 0x00, 0x00, // (uint)1
+ 0x02, 0x00, 0x00, 0x00, // (uint)2
+ 0x03, 0x00, 0x00, 0x00, // (uint)3
+ 0x04, 0x00, 0x00, 0x00, // (uint)4
+ 0x05, 0x00, 0x00, 0x00, // (uint)5
+ 0x06, 0x00, 0x00, 0x00, // (uint)6
+ 0x07, 0x00, 0x00, 0x00, // (uint)7
+ 0x08, 0x00, 0x00, 0x00, // (uint)8
+ 0x09, 0x00, 0x00, 0x00, // (uint)9
+ 0x0A, 0x00, 0x00, 0x00, // (uint)10
+ 0x0B, 0x00, 0x00, 0x00, // (uint)11
+ 0x0C, 0x00, 0x00, 0x00, // (uint)12
+ 0x0D, 0x00, 0x00, 0x00, // (uint)13
+ 0x0E, 0x00, 0x00, 0x00, // (uint)14
+ 0x0F, 0x00, 0x00, 0x00, // (uint)15
+
+ 0x00, 0x00, 0x00, 0x00, // (bool32)false
+
+ 0x45, 0x6E, 0x64, 0x69, 0x61, 0x6E, 0x42, 0x69, 0x6E, 0x61, 0x72, 0x79, 0x49, 0x4F, 0x00, // "EndianBinaryIO\0"
+
+ 0x4B, 0x00, 0x65, 0x00, 0x72, 0x00, 0x6D, 0x00, 0x61, 0x00, 0x6C, 0x00, 0x69, 0x00, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, // "Kermalis\0\0"
};
[Fact]
diff --git a/Testing/LengthsTests.cs b/Testing/LengthsTests.cs
index 1710da6..3e38c46 100644
--- a/Testing/LengthsTests.cs
+++ b/Testing/LengthsTests.cs
@@ -1,4 +1,5 @@
using Kermalis.EndianBinaryIO;
+using System;
using System.IO;
using System.Linq;
using Xunit;
@@ -23,27 +24,40 @@ private sealed class MyLengthyObj
[BinaryArrayVariableLength(nameof(VariableLengthProperty))]
public ShortSizedEnum[] VariableSizedArray { get; set; }
}
+ private sealed class ZeroLenArrayObj
+ {
+ [BinaryArrayFixedLength(0)]
+ public byte[] SizedArray { get; set; }
+
+ public byte VariableLength { get; set; }
+ [BinaryArrayVariableLength(nameof(VariableLength))]
+ public byte[] VariableArray { get; set; }
+ }
- private static readonly byte[] _bytes = new byte[34]
+ private static readonly byte[] _lengthyObjBytes = new byte[]
{
- 0x48, 0x69, 0x00,
- 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x00,
- 0x48, 0x6F, 0x6C, 0x61, 0x00,
+ 0x48, 0x69, 0x00, // "Hi\0"
+ 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x00, // "Hello\0"
+ 0x48, 0x6F, 0x6C, 0x61, 0x00, // "Hola\0"
- 0x53, 0x65, 0x65, 0x79, 0x61,
- 0x42, 0x79, 0x65, 0x00, 0x00,
- 0x41, 0x64, 0x69, 0x6F, 0x73,
+ 0x53, 0x65, 0x65, 0x79, 0x61, // "Seeya"
+ 0x42, 0x79, 0x65, 0x00, 0x00, // "Bye\0\0"
+ 0x41, 0x64, 0x69, 0x6F, 0x73, // "Adios"
- 0x02,
- 0x40, 0x00,
- 0x00, 0x08
+ 0x02, // (byte)2
+ 0x40, 0x00, // ShortSizedEnum.Val1
+ 0x00, 0x08, // ShortSizedEnum.Val2
+ };
+ private static readonly byte[] _zeroLenArrayObjBytes = new byte[]
+ {
+ 0x00, // (byte)0
};
[Fact]
- public void ReadObject()
+ public void ReadLengthyObject()
{
MyLengthyObj obj;
- using (var stream = new MemoryStream(_bytes))
+ using (var stream = new MemoryStream(_lengthyObjBytes))
using (var reader = new EndianBinaryReader(stream, Endianness.LittleEndian))
{
obj = reader.ReadObject();
@@ -66,9 +80,9 @@ public void ReadObject()
}
[Fact]
- public void WriteObject()
+ public void WriteLengthyObject()
{
- byte[] bytes = new byte[_bytes.Length];
+ byte[] bytes = new byte[_lengthyObjBytes.Length];
using (var stream = new MemoryStream(bytes))
using (var writer = new EndianBinaryWriter(stream, Endianness.LittleEndian))
{
@@ -91,7 +105,41 @@ public void WriteObject()
}
});
}
- Assert.True(bytes.SequenceEqual(_bytes));
+ Assert.True(bytes.SequenceEqual(_lengthyObjBytes));
+ }
+
+ [Fact]
+ public void ReadZeroLenArrayObject()
+ {
+ ZeroLenArrayObj obj;
+ using (var stream = new MemoryStream(_zeroLenArrayObjBytes))
+ using (var reader = new EndianBinaryReader(stream, Endianness.LittleEndian))
+ {
+ obj = reader.ReadObject();
+ }
+
+ Assert.True(obj.SizedArray.Length == 0); // Fixed size array works
+
+ Assert.True(obj.VariableLength == 0); // This determines how long the following array is
+ Assert.True(obj.VariableArray.Length == 0); // Retrieves the proper size
+ }
+
+ [Fact]
+ public void WriteZeroLenArrayObject()
+ {
+ byte[] bytes = new byte[_zeroLenArrayObjBytes.Length];
+ using (var stream = new MemoryStream(bytes))
+ using (var writer = new EndianBinaryWriter(stream, Endianness.LittleEndian))
+ {
+ writer.Write(new ZeroLenArrayObj
+ {
+ SizedArray = Array.Empty(),
+
+ VariableLength = 0,
+ VariableArray = Array.Empty()
+ });
+ }
+ Assert.True(bytes.SequenceEqual(_zeroLenArrayObjBytes));
}
}
}