.NET Pointer Artimetic
Dear Microsoft,
Please add the methods:
IntPtr Offset(int offset);
IntPtr Offset(long offset);
to the IntPtr class. In the meantime, here's how you might implement them in a static utility method:
public static IntPtr Offset(IntPtr src, int offset)
{
switch (IntPtr.Size) {
case 4:
return new IntPtr(src.ToInt32() + offset);
case 8:
return new IntPtr(src.ToInt64() + offset);
default:
throw new NotSupportedException("Surprise! This is running on a machine where pointers are " + IntPtr.Size + " bytes and arithmetic doesn't work in C# on them.");
}
}
public static IntPtr Offset(IntPtr src, long offset)
{
switch (IntPtr.Size) {
case 4: // may generate funny results
return new IntPtr(src.ToInt32() + offset);
case 8:
return new IntPtr(src.ToInt64() + offset);
default:
throw new NotSupportedException("Surprise! This is running on
a machine where pointers are " + IntPtr.Size + " bytes and arithmetic
doesn't work in C# on them.");
}
}Arguably, you could just use
return new IntPtr(src.ToInt64() + offset);
and rely on IntPtr truncating the 64 bit value to a reasonable 32 bit value.
The real issue I have is that building in pointer arithmetic into the data type itself should be more efficient than what I can get the compiler to do since it should be able to always to the best case and it eliminates the possibility of a user screwing up by using convenient 32 bit math and having that code break randomly on a 64 bit machine.
For fun (and, no really, it is fun), I rewrote this in 32 bit math and 64 bit math alone and they take pretty much the same amount of code in both IL and the final JIT compiled code, so it's not much of a loss one way or the other. If implemented with a switch statement, like above, this will do be slightly better than if you use a series of if/else statements, unless you use a local variable to hold IntPtr.Size.
You could, of course, do this as well:
public static IntPtr Offset(IntPtr src, int offset)
{
unsafe { return new IntPtr((byte*)src + offset); }
}
public static IntPtr Offset(IntPtr src, long offset)
{
unsafe { return new IntPtr((byte*)src + offset); }
}which is nearly identical to the safe code version, but doesn't need to check the size of the pointer type - so if you can live with unsafe code, this should always work with any pointer size.
This also begs the question as to how unsafe this code is. I'm not dereferencing anything - but real pointers are considered unsafe, so I lose.