Reputation: 3333
I need to read/write a bunch of structs to/from native memory. And I am trying to figure out whether or not should I bother with struct alignment. Here is a simple code I wrote to test things out. It writes a packed struct to unaligned pointer, and then reads the struct back:
public static unsafe class Program
{
public static void Main()
{
Console.WriteLine(sizeof(MyStructPacked)); //returns 6, as expected (no padding)
var nativeMemory = Marshal.AllocHGlobal(33);
var ptr = (byte*)nativeMemory.ToPointer();
Console.WriteLine((int)ptr % 8 == 0); //true, pointer is aligned to 8 bytes
Console.WriteLine((int)ptr % 16 == 0); //true, pointer is also aligned to 16 bytes
Unsafe.InitBlock(ptr, 0, 33);
//!!! aligned write to unaligned pointer
Unsafe.Write(ptr + 3, new MyStructPacked{Short = ushort.MaxValue / 2, Int = UInt32.MaxValue});
for (int i = 0; i < 10; i++)
{
byte b = *(ptr + i);
//returns 0 0 0 255 127 255 255 255 255 0 (which is correct)
Console.WriteLine(b);
}
//!!! aligned read from unaligned pointer
var read1 = Unsafe.Read<MyStructPacked>(ptr + 3);
//unaligned read from unaligned pointer
var read2 = Unsafe.ReadUnaligned<MyStructPacked>(ptr + 3);
//!!! dereferencing unaligned pointer
var read3 = *(MyStructPacked*)(ptr + 3);
//all produce the same (correct) result.
Console.WriteLine(read1.Int + " " + read1.Short);
Console.WriteLine(read2.Int + " " + read2.Short);
Console.WriteLine(read3.Int + " " + read3.Short);
//!!! aligned read from unaligned pointer
var readInt1 = Unsafe.Read<uint>(ptr + 5);
//unaligned read from unaligned pointer
var readInt2 = Unsafe.ReadUnaligned<uint>(ptr + 5);
//!!! dereferencing unaligned pointer
var readInt3 = *(uint*)(ptr + 5);
//all return uint.MaxValue (also correct)
Console.WriteLine(readInt1);
Console.WriteLine(readInt2);
Console.WriteLine(readInt3);
Marshal.FreeHGlobal(nativeMemory);
}
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct MyStructPacked
{
public ushort Short;
public uint Int;
}
I tested this on a couple of systems I have at hand (with .Net 6.0), and it works on them without a problem. The spec says value types have to be aligned a certain way, but I can't find any info on what actually happens if pointers aren't aligned the way they should according to spec. The best I could find is that it is considered "undefined behavior" (mentioned here), which is not very helpful.
So my question is, how safe is above code? Can I expect it to work on other .Net-supported systems and platforms? Or is it working purely by chance? If it won't work on some systems than what is the worst that can happen? Can this code crash? Is there anything I can do to reproduce potential problems (maybe run it on specific system?)? Any advice here is welcome, thanks.
P.S. I am aware of performance implications of unaligned access, but those are of secondary concern. At the moment the main concern is execution safety.
Upvotes: 3
Views: 777
Reputation: 3333
I ran into issues on armv7 processors, those are relatively common in low-end sector. They do in fact throw actual exceptions if you try to de-reference unaligned pointer (using safe ref
operator or unsafe *
). The issue is easy to reproduce and exception message is fairly explicit.
So in the end I had to manually align all unmanaged memory which had to be accessed by reference. It was tedious but, well... manageable. If you do not need to access the memory by reference, Unsafe.ReadUnaligned
and Unsafe.WriteUnaligned
methods prevent exceptions as well (at the cost of access speed, obviously).
Upvotes: 1