using System; using System.Reflection; using System.Runtime.InteropServices; using System.Text; using SabreTools.Numerics; namespace SabreTools.IO.Extensions { /// /// Extensions for byte arrays /// public static class ByteArrayWriterExtensions { /// /// Write a UInt8 and increment the pointer to an array /// public static bool Write(this byte[] content, ref int offset, byte value) => WriteFromBuffer(content, ref offset, [value]); /// /// Write a UInt8 and increment the pointer to an array /// /// Writes in both-endian format public static bool WriteBothEndian(this byte[] content, ref int offset, BothUInt8 value) { bool actual = content.Write(ref offset, value.LittleEndian); actual &= content.Write(ref offset, value.BigEndian); return actual; } /// /// Write a UInt8[] and increment the pointer to an array /// public static bool Write(this byte[] content, ref int offset, byte[] value) => WriteFromBuffer(content, ref offset, value); /// /// Write a UInt8[] and increment the pointer to an array /// /// Writes in big-endian format public static bool WriteBigEndian(this byte[] content, ref int offset, byte[] value) { Array.Reverse(value); return WriteFromBuffer(content, ref offset, value); } /// /// Write an Int8 and increment the pointer to an array /// public static bool Write(this byte[] content, ref int offset, sbyte value) => WriteFromBuffer(content, ref offset, [(byte)value]); /// /// Write a Int8 and increment the pointer to an array /// /// Writes in both-endian format public static bool WriteBothEndian(this byte[] content, ref int offset, BothInt8 value) { bool actual = content.Write(ref offset, value.LittleEndian); actual &= content.Write(ref offset, value.BigEndian); return actual; } /// /// Write a Char and increment the pointer to an array /// public static bool Write(this byte[] content, ref int offset, char value) { byte[] buffer = BitConverter.GetBytes(value); return WriteFromBuffer(content, ref offset, buffer); } /// /// Write a Char with an Encoding and increment the pointer to an array /// public static bool Write(this byte[] content, ref int offset, char value, Encoding encoding) { byte[] buffer = encoding.GetBytes($"{value}"); return WriteFromBuffer(content, ref offset, buffer); } /// /// Write an Int16 and increment the pointer to an array /// /// Writes in machine native format public static bool Write(this byte[] content, ref int offset, short value) { if (BitConverter.IsLittleEndian) return content.WriteLittleEndian(ref offset, value); else return content.WriteBigEndian(ref offset, value); } /// /// Write an Int16 and increment the pointer to an array /// /// Writes in big-endian format public static bool WriteBigEndian(this byte[] content, ref int offset, short value) { byte[] buffer = value.GetBytesBigEndian(); return WriteFromBuffer(content, ref offset, buffer); } /// /// Write an Int16 and increment the pointer to an array /// /// Writes in little-endian format public static bool WriteLittleEndian(this byte[] content, ref int offset, short value) { byte[] buffer = value.GetBytesLittleEndian(); return WriteFromBuffer(content, ref offset, buffer); } /// /// Write a Int16 and increment the pointer to an array /// /// Writes in both-endian format public static bool WriteBothEndian(this byte[] content, ref int offset, BothInt16 value) { bool actual = content.WriteLittleEndian(ref offset, value.LittleEndian); actual &= content.WriteBigEndian(ref offset, value.BigEndian); return actual; } /// /// Write a UInt16 and increment the pointer to an array /// /// Writes in machine native format public static bool Write(this byte[] content, ref int offset, ushort value) { if (BitConverter.IsLittleEndian) return content.WriteLittleEndian(ref offset, value); else return content.WriteBigEndian(ref offset, value); } /// /// Write a UInt16 and increment the pointer to an array /// /// Writes in big-endian format public static bool WriteBigEndian(this byte[] content, ref int offset, ushort value) { byte[] buffer = value.GetBytesBigEndian(); return WriteFromBuffer(content, ref offset, buffer); } /// /// Write a UInt16 and increment the pointer to an array /// /// Writes in little-endian format public static bool WriteLittleEndian(this byte[] content, ref int offset, ushort value) { byte[] buffer = value.GetBytesLittleEndian(); return WriteFromBuffer(content, ref offset, buffer); } /// /// Write a UInt16 and increment the pointer to an array /// /// Writes in both-endian format public static bool WriteBothEndian(this byte[] content, ref int offset, BothUInt16 value) { bool actual = content.WriteLittleEndian(ref offset, value.LittleEndian); actual &= content.WriteBigEndian(ref offset, value.BigEndian); return actual; } #if NET5_0_OR_GREATER /// /// Write a Half and increment the pointer to an array /// /// Writes in machine native format public static bool Write(this byte[] content, ref int offset, Half value) { if (BitConverter.IsLittleEndian) return content.WriteLittleEndian(ref offset, value); else return content.WriteBigEndian(ref offset, value); } /// /// Write a Half and increment the pointer to an array /// /// Writes in big-endian format public static bool WriteBigEndian(this byte[] content, ref int offset, Half value) { byte[] buffer = value.GetBytesBigEndian(); return WriteFromBuffer(content, ref offset, buffer); } /// /// Write a Half and increment the pointer to an array /// /// Writes in little-endian format public static bool WriteLittleEndian(this byte[] content, ref int offset, Half value) { byte[] buffer = value.GetBytesLittleEndian(); return WriteFromBuffer(content, ref offset, buffer); } #endif /// /// Write an Int24 and increment the pointer to an array /// /// Writes in machine native format public static bool Write(this byte[] content, ref int offset, Int24 value) { if (BitConverter.IsLittleEndian) return content.WriteLittleEndian(ref offset, value); else return content.WriteBigEndian(ref offset, value); } /// /// Write an Int24 and increment the pointer to an array /// /// Writes in big-endian format public static bool WriteBigEndian(this byte[] content, ref int offset, Int24 value) { byte[] buffer = value.GetBytesBigEndian(); return WriteFromBuffer(content, ref offset, buffer); } /// /// Write an Int24 and increment the pointer to an array /// /// Writes in little-endian format public static bool WriteLittleEndian(this byte[] content, ref int offset, Int24 value) { byte[] buffer = value.GetBytesLittleEndian(); return WriteFromBuffer(content, ref offset, buffer); } /// /// Write a UInt24 and increment the pointer to an array /// /// Writes in machine native format public static bool Write(this byte[] content, ref int offset, UInt24 value) { if (BitConverter.IsLittleEndian) return content.WriteLittleEndian(ref offset, value); else return content.WriteBigEndian(ref offset, value); } /// /// Write a UInt24 and increment the pointer to an array /// /// Writes in big-endian format public static bool WriteBigEndian(this byte[] content, ref int offset, UInt24 value) { byte[] buffer = value.GetBytesBigEndian(); return WriteFromBuffer(content, ref offset, buffer); } /// /// Write a UInt24 and increment the pointer to an array /// /// Writes in little-endian format public static bool WriteLittleEndian(this byte[] content, ref int offset, UInt24 value) { byte[] buffer = value.GetBytesLittleEndian(); return WriteFromBuffer(content, ref offset, buffer); } /// /// Write an Int32 and increment the pointer to an array /// /// Writes in machine native format public static bool Write(this byte[] content, ref int offset, int value) { if (BitConverter.IsLittleEndian) return content.WriteLittleEndian(ref offset, value); else return content.WriteBigEndian(ref offset, value); } /// /// Write an Int32 and increment the pointer to an array /// /// Writes in big-endian format public static bool WriteBigEndian(this byte[] content, ref int offset, int value) { byte[] buffer = value.GetBytesBigEndian(); return WriteFromBuffer(content, ref offset, buffer); } /// /// Write an Int32 and increment the pointer to an array /// /// Writes in little-endian format public static bool WriteLittleEndian(this byte[] content, ref int offset, int value) { byte[] buffer = value.GetBytesLittleEndian(); return WriteFromBuffer(content, ref offset, buffer); } /// /// Write a Int32 and increment the pointer to an array /// /// Writes in both-endian format public static bool WriteBothEndian(this byte[] content, ref int offset, BothInt32 value) { bool actual = content.WriteLittleEndian(ref offset, value.LittleEndian); actual &= content.WriteBigEndian(ref offset, value.BigEndian); return actual; } /// /// Write a UInt32 and increment the pointer to an array /// /// Writes in machine native format public static bool Write(this byte[] content, ref int offset, uint value) { if (BitConverter.IsLittleEndian) return content.WriteLittleEndian(ref offset, value); else return content.WriteBigEndian(ref offset, value); } /// /// Write a UInt32 and increment the pointer to an array /// /// Writes in big-endian format public static bool WriteBigEndian(this byte[] content, ref int offset, uint value) { byte[] buffer = value.GetBytesBigEndian(); return WriteFromBuffer(content, ref offset, buffer); } /// /// Write a UInt32 and increment the pointer to an array /// /// Writes in little-endian format public static bool WriteLittleEndian(this byte[] content, ref int offset, uint value) { byte[] buffer = value.GetBytesLittleEndian(); return WriteFromBuffer(content, ref offset, buffer); } /// /// Write a UInt32 and increment the pointer to an array /// /// Writes in both-endian format public static bool WriteBothEndian(this byte[] content, ref int offset, BothUInt32 value) { bool actual = content.WriteLittleEndian(ref offset, value.LittleEndian); actual &= content.WriteBigEndian(ref offset, value.BigEndian); return actual; } /// /// Write a Single and increment the pointer to an array /// /// Writes in machine native format public static bool Write(this byte[] content, ref int offset, float value) { if (BitConverter.IsLittleEndian) return content.WriteLittleEndian(ref offset, value); else return content.WriteBigEndian(ref offset, value); } /// /// Write a Single and increment the pointer to an array /// /// Writes in big-endian format public static bool WriteBigEndian(this byte[] content, ref int offset, float value) { byte[] buffer = value.GetBytesBigEndian(); return WriteFromBuffer(content, ref offset, buffer); } /// /// Write a Single and increment the pointer to an array /// /// Writes in little-endian format public static bool WriteLittleEndian(this byte[] content, ref int offset, float value) { byte[] buffer = value.GetBytesLittleEndian(); return WriteFromBuffer(content, ref offset, buffer); } /// /// Write an Int48 and increment the pointer to an array /// /// Writes in machine native format public static bool Write(this byte[] content, ref int offset, Int48 value) { if (BitConverter.IsLittleEndian) return content.WriteLittleEndian(ref offset, value); else return content.WriteBigEndian(ref offset, value); } /// /// Write an Int48 and increment the pointer to an array /// /// Writes in big-endian format public static bool WriteBigEndian(this byte[] content, ref int offset, Int48 value) { byte[] buffer = value.GetBytesBigEndian(); return WriteFromBuffer(content, ref offset, buffer); } /// /// Write an Int48 and increment the pointer to an array /// /// Writes in little-endian format public static bool WriteLittleEndian(this byte[] content, ref int offset, Int48 value) { byte[] buffer = value.GetBytesLittleEndian(); return WriteFromBuffer(content, ref offset, buffer); } /// /// Write a UInt48 and increment the pointer to an array /// /// Writes in machine native format public static bool Write(this byte[] content, ref int offset, UInt48 value) { if (BitConverter.IsLittleEndian) return content.WriteLittleEndian(ref offset, value); else return content.WriteBigEndian(ref offset, value); } /// /// Write a UInt48 and increment the pointer to an array /// /// Writes in big-endian format public static bool WriteBigEndian(this byte[] content, ref int offset, UInt48 value) { byte[] buffer = value.GetBytesBigEndian(); return WriteFromBuffer(content, ref offset, buffer); } /// /// Write a UInt48 and increment the pointer to an array /// /// Writes in little-endian format public static bool WriteLittleEndian(this byte[] content, ref int offset, UInt48 value) { byte[] buffer = value.GetBytesLittleEndian(); return WriteFromBuffer(content, ref offset, buffer); } /// /// Write an Int64 and increment the pointer to an array /// /// Writes in machine native format public static bool Write(this byte[] content, ref int offset, long value) { if (BitConverter.IsLittleEndian) return content.WriteLittleEndian(ref offset, value); else return content.WriteBigEndian(ref offset, value); } /// /// Write an Int64 and increment the pointer to an array /// /// Writes in big-endian format public static bool WriteBigEndian(this byte[] content, ref int offset, long value) { byte[] buffer = value.GetBytesBigEndian(); return WriteFromBuffer(content, ref offset, buffer); } /// /// Write an Int64 and increment the pointer to an array /// /// Writes in little-endian format public static bool WriteLittleEndian(this byte[] content, ref int offset, long value) { byte[] buffer = value.GetBytesLittleEndian(); return WriteFromBuffer(content, ref offset, buffer); } /// /// Write a Int64 and increment the pointer to an array /// /// Writes in both-endian format public static bool WriteBothEndian(this byte[] content, ref int offset, BothInt64 value) { bool actual = content.WriteLittleEndian(ref offset, value.LittleEndian); actual &= content.WriteBigEndian(ref offset, value.BigEndian); return actual; } /// /// Write a UInt64 and increment the pointer to an array /// /// Writes in machine native format public static bool Write(this byte[] content, ref int offset, ulong value) { if (BitConverter.IsLittleEndian) return content.WriteLittleEndian(ref offset, value); else return content.WriteBigEndian(ref offset, value); } /// /// Write a UInt64 and increment the pointer to an array /// /// Writes in big-endian format public static bool WriteBigEndian(this byte[] content, ref int offset, ulong value) { byte[] buffer = value.GetBytesBigEndian(); return WriteFromBuffer(content, ref offset, buffer); } /// /// Write a UInt64 and increment the pointer to an array /// /// Writes in little-endian format public static bool WriteLittleEndian(this byte[] content, ref int offset, ulong value) { byte[] buffer = value.GetBytesLittleEndian(); return WriteFromBuffer(content, ref offset, buffer); } /// /// Write a UInt64 and increment the pointer to an array /// /// Writes in both-endian format public static bool WriteBothEndian(this byte[] content, ref int offset, BothUInt64 value) { bool actual = content.WriteLittleEndian(ref offset, value.LittleEndian); actual &= content.WriteBigEndian(ref offset, value.BigEndian); return actual; } /// /// Write a Double and increment the pointer to an array /// /// Writes in machine native format public static bool Write(this byte[] content, ref int offset, double value) { if (BitConverter.IsLittleEndian) return content.WriteLittleEndian(ref offset, value); else return content.WriteBigEndian(ref offset, value); } /// /// Write a Double and increment the pointer to an array /// /// Writes in big-endian format public static bool WriteBigEndian(this byte[] content, ref int offset, double value) { byte[] buffer = value.GetBytesBigEndian(); return WriteFromBuffer(content, ref offset, buffer); } /// /// Write a Double and increment the pointer to an array /// /// Writes in little-endian format public static bool WriteLittleEndian(this byte[] content, ref int offset, double value) { byte[] buffer = value.GetBytesLittleEndian(); return WriteFromBuffer(content, ref offset, buffer); } /// /// Write a Guid and increment the pointer to an array /// /// Writes in machine native format public static bool Write(this byte[] content, ref int offset, Guid value) { if (BitConverter.IsLittleEndian) return content.WriteLittleEndian(ref offset, value); else return content.WriteBigEndian(ref offset, value); } /// /// Write a Guid and increment the pointer to an array /// /// Writes in big-endian format public static bool WriteBigEndian(this byte[] content, ref int offset, Guid value) { byte[] buffer = value.GetBytesBigEndian(); return WriteFromBuffer(content, ref offset, buffer); } /// /// Write a Guid and increment the pointer to an array /// /// Writes in little-endian format public static bool WriteLittleEndian(this byte[] content, ref int offset, Guid value) { byte[] buffer = value.GetBytesLittleEndian(); return WriteFromBuffer(content, ref offset, buffer); } #if NET7_0_OR_GREATER /// /// Write an Int128 and increment the pointer to an array /// /// Writes in machine native format public static bool Write(this byte[] content, ref int offset, Int128 value) { if (BitConverter.IsLittleEndian) return content.WriteLittleEndian(ref offset, value); else return content.WriteBigEndian(ref offset, value); } /// /// Write an Int128 and increment the pointer to an array /// /// Writes in big-endian format public static bool WriteBigEndian(this byte[] content, ref int offset, Int128 value) { byte[] buffer = value.GetBytesBigEndian(); return WriteFromBuffer(content, ref offset, buffer); } /// /// Write an Int128 and increment the pointer to an array /// /// Writes in little-endian format public static bool WriteLittleEndian(this byte[] content, ref int offset, Int128 value) { byte[] buffer = value.GetBytesLittleEndian(); return WriteFromBuffer(content, ref offset, buffer); } /// /// Write a UInt128 and increment the pointer to an array /// /// Writes in machine native format public static bool Write(this byte[] content, ref int offset, UInt128 value) { if (BitConverter.IsLittleEndian) return content.WriteLittleEndian(ref offset, value); else return content.WriteBigEndian(ref offset, value); } /// /// Write a UInt128 and increment the pointer to an array /// /// Writes in big-endian format public static bool WriteBigEndian(this byte[] content, ref int offset, UInt128 value) { byte[] buffer = value.GetBytesBigEndian(); return WriteFromBuffer(content, ref offset, buffer); } /// /// Write a UInt128 and increment the pointer to an array /// /// Writes in little-endian format public static bool WriteLittleEndian(this byte[] content, ref int offset, UInt128 value) { byte[] buffer = value.GetBytesLittleEndian(); return WriteFromBuffer(content, ref offset, buffer); } #endif /// /// Write a Decimal and increment the pointer to an array /// /// Writes in machine native format public static bool Write(this byte[] content, ref int offset, decimal value) { if (BitConverter.IsLittleEndian) return content.WriteLittleEndian(ref offset, value); else return content.WriteBigEndian(ref offset, value); } /// /// Write a Decimal and increment the pointer to an array /// /// Writes in big-endian format public static bool WriteBigEndian(this byte[] content, ref int offset, decimal value) { byte[] buffer = value.GetBytesBigEndian(); return WriteFromBuffer(content, ref offset, buffer); } /// /// Write a Decimal and increment the pointer to an array /// /// Writes in little-endian format public static bool WriteLittleEndian(this byte[] content, ref int offset, decimal value) { byte[] buffer = value.GetBytesLittleEndian(); return WriteFromBuffer(content, ref offset, buffer); } /// /// Write a null-terminated string to the array /// public static bool WriteNullTerminatedString(this byte[] content, ref int offset, string? value, Encoding encoding) { // If the value is null if (value is null) return false; // Add the null terminator and write value += "\0"; byte[] buffer = encoding.GetBytes(value); return WriteFromBuffer(content, ref offset, buffer); } /// /// Write a null-terminated ASCII string to the byte array /// public static bool WriteNullTerminatedAnsiString(this byte[] content, ref int offset, string? value) => content.WriteNullTerminatedString(ref offset, value, Encoding.ASCII); #if NET5_0_OR_GREATER /// /// Write a null-terminated Latin1 string to the byte array /// public static bool WriteNullTerminatedLatin1String(this byte[] content, ref int offset, string? value) => content.WriteNullTerminatedString(ref offset, value, Encoding.Latin1); #endif /// /// Write a null-terminated UTF-8 string to the byte array /// public static bool WriteNullTerminatedUTF8String(this byte[] content, ref int offset, string? value) => content.WriteNullTerminatedString(ref offset, value, Encoding.UTF8); /// /// Write a null-terminated UTF-16 (Unicode) string to the byte array /// public static bool WriteNullTerminatedUnicodeString(this byte[] content, ref int offset, string? value) => content.WriteNullTerminatedString(ref offset, value, Encoding.Unicode); /// /// Write a null-terminated UTF-16 (Unicode) string to the byte array /// public static bool WriteNullTerminatedBigEndianUnicodeString(this byte[] content, ref int offset, string? value) => content.WriteNullTerminatedString(ref offset, value, Encoding.BigEndianUnicode); /// /// Write a null-terminated UTF-32 string to the byte array /// public static bool WriteNullTerminatedUTF32String(this byte[] content, ref int offset, string? value) => content.WriteNullTerminatedString(ref offset, value, Encoding.UTF32); /// /// Write a byte-prefixed ASCII string to the byte array /// public static bool WritePrefixedAnsiString(this byte[] content, ref int offset, string? value) { // If the value is null if (value is null) return false; // Get the buffer byte[] buffer = Encoding.ASCII.GetBytes(value); // Write the length as a byte if (!content.Write(ref offset, (byte)value.Length)) return false; // Write the buffer return WriteFromBuffer(content, ref offset, buffer); } #if NET5_0_OR_GREATER /// /// Write a byte-prefixed Latin1 string to the byte array /// public static bool WritePrefixedLatin1String(this byte[] content, ref int offset, string? value) { // If the value is null if (value is null) return false; // Get the buffer byte[] buffer = Encoding.Latin1.GetBytes(value); // Write the length as a byte if (!content.Write(ref offset, (byte)value.Length)) return false; // Write the buffer return WriteFromBuffer(content, ref offset, buffer); } #endif /// /// Write a ushort-prefixed Unicode string to the byte array /// public static bool WritePrefixedUnicodeString(this byte[] content, ref int offset, string? value) { // If the value is null if (value is null) return false; // Get the buffer byte[] buffer = Encoding.Unicode.GetBytes(value); // Write the length as a ushort if (!content.Write(ref offset, (ushort)value.Length)) return false; // Write the buffer return WriteFromBuffer(content, ref offset, buffer); } /// /// Write a ushort-prefixed Unicode string to the byte array /// public static bool WritePrefixedBigEndianUnicodeString(this byte[] content, ref int offset, string? value) { // If the value is null if (value is null) return false; // Get the buffer byte[] buffer = Encoding.BigEndianUnicode.GetBytes(value); // Write the length as a ushort if (!content.Write(ref offset, (ushort)value.Length)) return false; // Write the buffer return WriteFromBuffer(content, ref offset, buffer); } /// /// Write a to the byte array /// /// /// This method is different than standard marshalling in a few notable ways: /// - Strings are written by value, not by reference /// - Complex objects are written by value, not by reference /// - Enumeration values are written by the underlying value type /// - Arrays of the above are handled sequentially as above /// - Inherited fields from parents are serialized BEFORE fields in the child /// public static bool WriteType(this byte[] content, ref int offset, T? value) => content.WriteType(ref offset, value, typeof(T)); /// /// Write a to the byte array /// /// /// This method is different than standard marshalling in a few notable ways: /// - Strings are written by value, not by reference /// - Complex objects are written by value, not by reference /// - Enumeration values are written by the underlying value type /// - Arrays of the above are handled sequentially as above /// - Inherited fields from parents are serialized BEFORE fields in the child /// public static bool WriteType(this byte[] content, ref int offset, object? value, Type type) { // Null values cannot be written if (value is null) return true; // Handle special struct cases if (type == typeof(Guid)) return content.Write(ref offset, (Guid)value); #if NET5_0_OR_GREATER else if (type == typeof(Half)) return content.Write(ref offset, (Half)value); #endif #if NET7_0_OR_GREATER else if (type == typeof(Int128)) return content.Write(ref offset, (Int128)value); else if (type == typeof(UInt128)) return content.Write(ref offset, (UInt128)value); #endif if (type.IsClass || (type.IsValueType && !type.IsEnum && !type.IsPrimitive)) return WriteComplexType(content, ref offset, value, type); else if (type.IsValueType && type.IsEnum) return WriteNormalType(content, ref offset, value, Enum.GetUnderlyingType(type)); else return WriteNormalType(content, ref offset, value, type); } /// /// Read a from the stream /// private static bool WriteNormalType(byte[] content, ref int offset, object? value, Type type) { try { // Null values cannot be written if (value is null) return true; int typeSize = Marshal.SizeOf(type); if (value.GetType() != type) value = Convert.ChangeType(value, type); var buffer = new byte[typeSize]; var handle = GCHandle.Alloc(buffer, GCHandleType.Pinned); Marshal.StructureToPtr(value, handle.AddrOfPinnedObject(), false); handle.Free(); return WriteFromBuffer(content, ref offset, buffer); } catch { return false; } } /// /// Read a from the stream /// private static bool WriteComplexType(byte[] content, ref int offset, object? value, Type type) { try { // Null values cannot be written if (value is null) return true; // Get the layout information var layoutAttr = MarshalHelpers.GetAttribute(type); LayoutKind layoutKind = MarshalHelpers.DetermineLayoutKind(layoutAttr, type); Encoding encoding = MarshalHelpers.DetermineEncoding(layoutAttr); // Cache the current offset int currentOffset = offset; // Generate the fields by parent first var fields = MarshalHelpers.GetFields(type); // Loop through the fields and set them foreach (var fi in fields) { // If we have an explicit layout, move accordingly if (layoutKind == LayoutKind.Explicit) { var fieldOffset = MarshalHelpers.GetAttribute(fi); offset = currentOffset + (fieldOffset?.Value ?? 0); } if (!GetField(content, ref offset, encoding, fields, value, fi)) return false; } return true; } catch { return false; } } /// /// Write a single field from an object /// private static bool GetField(byte[] content, ref int offset, Encoding encoding, FieldInfo[] fields, object instance, FieldInfo fi) { if (fi.FieldType.IsAssignableFrom(typeof(string))) { return WriteStringType(content, ref offset, encoding, instance, fi); } else if (fi.FieldType.IsArray) { return WriteArrayType(content, ref offset, fields, instance, fi); } else { var value = fi.GetValue(instance); return content.WriteType(ref offset, value, fi.FieldType); } } /// /// Write an array type field from an object /// private static bool WriteArrayType(byte[] content, ref int offset, FieldInfo[] fields, object instance, FieldInfo fi) { var marshalAsAttr = MarshalHelpers.GetAttribute(fi); if (marshalAsAttr is null) return false; // Get the array if (fi.GetValue(instance) is not Array arr) return false; // Get the number of elements expected int elementCount = MarshalHelpers.GetArrayElementCount(marshalAsAttr, fields, instance); if (elementCount < 0) return false; // Get the item type for the array Type elementType = fi.FieldType.GetElementType() ?? typeof(object); // Loop through and write the array for (int i = 0; i < elementCount; i++) { var value = arr.GetValue(i); if (!WriteType(content, ref offset, value, elementType)) return false; } return true; } /// /// Write a string type field from an object /// private static bool WriteStringType(byte[] content, ref int offset, Encoding encoding, object instance, FieldInfo fi) { var marshalAsAttr = MarshalHelpers.GetAttribute(fi); if (fi.GetValue(instance) is not string fieldValue) return true; #pragma warning disable IDE0010 switch (marshalAsAttr?.Value) { case UnmanagedType.AnsiBStr: return content.WritePrefixedAnsiString(ref offset, fieldValue); case UnmanagedType.BStr: case UnmanagedType.TBStr: // Technically distinct; returns char[] instead return content.WritePrefixedUnicodeString(ref offset, fieldValue); case UnmanagedType.ByValTStr: int byvalLength = marshalAsAttr!.SizeConst; byte[] byvalBytes = encoding.GetBytes(fieldValue); byte[] byvalSizedBytes = new byte[byvalLength]; Array.Copy(byvalBytes, byvalSizedBytes, Math.Min(byvalBytes.Length, byvalSizedBytes.Length)); return content.Write(ref offset, byvalSizedBytes); case UnmanagedType.LPStr: case UnmanagedType.LPTStr: // Technically distinct; possibly not null-terminated case null: return content.WriteNullTerminatedAnsiString(ref offset, fieldValue); #if NET472_OR_GREATER || NETCOREAPP || NETSTANDARD2_1_OR_GREATER case UnmanagedType.LPUTF8Str: return content.WriteNullTerminatedUTF8String(ref offset, fieldValue); #endif case UnmanagedType.LPWStr: return content.WriteNullTerminatedUnicodeString(ref offset, fieldValue); // No other string types are recognized default: return false; } #pragma warning restore IDE0010 } /// /// Write an array of bytes to the byte array /// /// /// Thrown if into /// would not accomodate . /// private static bool WriteFromBuffer(byte[] content, ref int offset, byte[] value) { // Handle the 0-byte case if (value.Length == 0) return true; // If there are not enough bytes if (offset + value.Length > content.Length) throw new System.IO.EndOfStreamException(nameof(content)); // Handle the general case, forcing a write of the correct length Array.Copy(value, 0, content, offset, value.Length); offset += value.Length; return true; } } }