Guid is a 128bits structure, long is a Int64 so 64 bits structure, therefore Guid can be used to represent two long and two long can be stored in a Guid.
I have been searching several times for a reliable way to perform the transformation of a Guid to 2 longs and the way around, mainly to get a simple way to provide a tracking id to external services.
The objective is to get a reversable way to pass in a single parameter 2 longs, and decode it back later (of course it is not intended to be used "decoded" on the other side). It is like a session id for the external service.
Warning: these solutions do not take endianness into consideration, and the result may therefore differ from one platform to another
Taking advantage of new features of C# 7, I came out with the following tools class, which transforms long, ulong, int, uint to Guid and reverse:
public static class GuidTools
{
public static Guid GuidFromLongs(long a, long b)
{
byte[] guidData = new byte[16];
Array.Copy(BitConverter.GetBytes(a), guidData, 8);
Array.Copy(BitConverter.GetBytes(b), 0, guidData, 8, 8);
return new Guid(guidData);
}
public static (long, long) ToLongs(this Guid guid)
{
var bytes = guid.ToByteArray();
var long1 = BitConverter.ToInt64(bytes, 0);
var long2 = BitConverter.ToInt64(bytes, 8);
return (long1, long2);
}
public static Guid GuidFromULongs(ulong a, ulong b)
{
byte[] guidData = new byte[16];
Array.Copy(BitConverter.GetBytes(a), guidData, 8);
Array.Copy(BitConverter.GetBytes(b), 0, guidData, 8, 8);
return new Guid(guidData);
}
public static (ulong, ulong) ToULongs(this Guid guid)
{
var bytes = guid.ToByteArray();
var ulong1 = BitConverter.ToUInt64(bytes, 0);
var ulong2 = BitConverter.ToUInt64(bytes, 8);
return (ulong1, ulong2);
}
public static Guid GuidFromInts(int a, int b, int c, int d)
{
byte[] guidData = new byte[16];
Array.Copy(BitConverter.GetBytes(a), guidData, 4);
Array.Copy(BitConverter.GetBytes(b), 0, guidData, 4, 4);
Array.Copy(BitConverter.GetBytes(c), 0, guidData, 8, 4);
Array.Copy(BitConverter.GetBytes(d), 0, guidData, 12, 4);
return new Guid(guidData);
}
public static (int, int , int, int) ToInts(this Guid guid)
{
var bytes = guid.ToByteArray();
var a = BitConverter.ToInt32(bytes, 0);
var b = BitConverter.ToInt32(bytes, 4);
var c = BitConverter.ToInt32(bytes, 8);
var d = BitConverter.ToInt32(bytes, 12);
return (a, b, c, d);
}
public static Guid GuidFromUInts(uint a, uint b, uint c, uint d)
{
byte[] guidData = new byte[16];
Array.Copy(BitConverter.GetBytes(a), guidData, 4);
Array.Copy(BitConverter.GetBytes(b), 0, guidData, 4, 4);
Array.Copy(BitConverter.GetBytes(c), 0, guidData, 8, 4);
Array.Copy(BitConverter.GetBytes(d), 0, guidData, 12, 4);
return new Guid(guidData);
}
public static (uint, uint, uint, uint) ToUInts(this Guid guid)
{
var bytes = guid.ToByteArray();
var a = BitConverter.ToUInt32(bytes, 0);
var b = BitConverter.ToUInt32(bytes, 4);
var c = BitConverter.ToUInt32(bytes, 8);
var d = BitConverter.ToUInt32(bytes, 12);
return (a, b, c, d);
}
}
Also found another solution inspired from there: Converting System.Decimal to System.Guid
[StructLayout(LayoutKind.Explicit)]
struct GuidConverter
{
[FieldOffset(0)]
public decimal Decimal;
[FieldOffset(0)]
public Guid Guid;
[FieldOffset(0)]
public long Long1;
[FieldOffset(8)]
public long Long2;
}
private static GuidConverter _converter;
public static (long, long) FastGuidToLongs(this Guid guid)
{
_converter.Guid = guid;
return (_converter.Long1, _converter.Long2);
}
public static Guid FastLongsToGuid(long a, long b)
{
_converter.Long1 = a;
_converter.Long2 = b;
return _converter.Guid;
}
As an unsafe but very efficient version (no byte[] allocations, via BitConverter):
static void Main()
{
var g = Guid.NewGuid();
Console.WriteLine(g);
GuidToInt64(g, out var x, out var y);
Console.WriteLine(x);
Console.WriteLine(y);
var g2 = GuidFromInt64(x, y);
Console.WriteLine(g2);
}
public static unsafe void GuidToInt64(Guid value, out long x, out long y)
{
long* ptr = (long*)&value;
x = *ptr++;
y = *ptr;
}
public static unsafe Guid GuidFromInt64(long x, long y)
{
long* ptr = stackalloc long[2];
ptr[0] = x;
ptr[1] = y;
return *(Guid*)ptr;
}
You could actually do the same thing with a union struct, if you don't like using the unsafe keyword, but: it is more code, and a union struct is still fundamentally unverifiable, so this doesn't gain you much at the IL level (it just means you don't need the "allow unsafe code" flag):
static void Main()
{
var g = Guid.NewGuid();
Console.WriteLine(g);
var val = new GuidInt64(g);
var x = val.X;
var y = val.Y;
Console.WriteLine(x);
Console.WriteLine(y);
var val2 = new GuidInt64(x, y);
var g2 = val2.Guid;
Console.WriteLine(g2);
}
[StructLayout(LayoutKind.Explicit)]
struct GuidInt64
{
[FieldOffset(0)]
private Guid _guid;
[FieldOffset(0)]
private long _x;
[FieldOffset(8)]
private long _y;
public Guid Guid => _guid;
public long X => _x;
public long Y => _y;
public GuidInt64(Guid guid)
{
_x = _y = 0; // to make the compiler happy
_guid = guid;
}
public GuidInt64(long x, long y)
{
_guid = Guid.Empty;// to make the compiler happy
_x = x;
_y = y;
}
}
Following pair of methods could do what you need:
public static void GuidToInt16(Guid guidToConvert, out long guidAsLong1, out long guidAsLong2)
{
byte[] guidByteArray = guidToConvert.ToByteArray();
var segment1 = new ArraySegment<byte>(guidByteArray, 0, 8);
var segment2 = new ArraySegment<byte>(guidByteArray, 8, 8);
guidAsLong1 = BitConverter.ToInt64(segment1.ToArray(), 0);
guidAsLong2 = BitConverter.ToInt64(segment2.ToArray(), 0);
}
public static Guid Int16ToGuid(long guidAsLong1, long guidAsLong2)
{
var segment1 = BitConverter.GetBytes(guidAsLong1);
var segment2 = BitConverter.GetBytes(guidAsLong2);
return new Guid(segment1.Concat(segment2).ToArray());
}
And possible usage:
Guid guidToConvert = new Guid("cbd5bb87-a249-49ac-8b06-87c124205b99");
long guidAsLong1, guidAsLong2;
GuidToInt16(guidToConvert, out guidAsLong1, out guidAsLong2);
Console.WriteLine(guidAsLong1 + " " + guidAsLong2);
Guid guidConvertedBack = Int16ToGuid(guidAsLong1, guidAsLong2);
Console.WriteLine(guidConvertedBack);
Console.ReadKey();
My solution should help understand whole process with binary operations:
class Program
{
public static Guid LongsToGuid(long l1, long l2)
{
var a = (int)l1;
var b = (short)(l1 >> 32);
var c = (short)(l1 >> 48);
var d = (byte)l2;
var e = (byte)(l2 >> 8);
var f = (byte)(l2 >> 16);
var g = (byte)(l2 >> 24);
var h = (byte)(l2 >> 32);
var i = (byte)(l2 >> 40);
var j = (byte)(l2 >> 48);
var k = (byte)(l2 >> 56);
return new Guid(a, b, c, d, e, f, g, h, i, j, k);
}
public static long BytesToLong(byte[] bytes, int start, int end)
{
long toReturn = 0;
for (var i = start; i < end; i++)
{
toReturn |= ((long)bytes[i]) << (8 * i);
}
return toReturn;
}
static void Main(string[] args)
{
var l1 = long.MinValue;
var l2 = long.MaxValue;
var guid = LongsToGuid(l1, l2);
var guidBytes = guid.ToByteArray();
var readL1 = BytesToLong(guidBytes, 0, 8);
var readL2 = BytesToLong(guidBytes, 8, 16);
Console.WriteLine(l1 == readL1);
Console.WriteLine(l2 == readL2);
Console.ReadKey();
}
}
Related
So basically here is how I do it with C++:
enum ServerOpcode : uint16_t{
SERVER_AUTH_CONNECTION_RESPONSE = 0x001,
SERVER_LOGIN_REQUEST_RESPONSE = 0x002,
SERVER_NUM_MSG_TYPES = 0x003,
};
uint8_t* Vibranium::Packet::PreparePacket(ServerOpcode& serverOpcode, flatbuffers::FlatBufferBuilder& builder) {
size_t size = builder.GetSize();
uint8_t *buf = builder.GetBufferPointer();
uint8_t *actualBuffer = new uint8_t[size + 2];
actualBuffer[1] = (serverOpcode >> 8);
actualBuffer[0] = (serverOpcode&0xFF);
memcpy(actualBuffer + 2, buf, size);
return actualBuffer;
}
I know that uint16_t is exactly 2 bytes and that is why i add +2.
Can someone give example in C# of how can I cast the ByteBuffer to byte[] and than prefix it with:
public enum ServerOpcode : ushort
{
SERVER_AUTH_CONNECTION_RESPONSE = 0x001,
SERVER_LOGIN_REQUEST_RESPONSE = 0x002,
SERVER_NUM_MSG_TYPES = 0x003,
}
In C# I found out that the equivalent of uint16_t is ushort.
So my question is how can I convert ByteBuffer into byte[] and prefix it with ushort?
Can anyone make an answer showing and equivalent of PreparePacket in C# ?
P.S.
Note that I am familiar with the file_identifier but I would like to do that manually. Hope someone can provide an example in C#
Following is the solution:
public static Byte[] PrependServerOpcode(ByteBuffer byteBuffer, ServerOpcode code)
{
var originalArray = byteBuffer.ToArray(0, byteBuffer.Length);
byte[] buffer = new byte[originalArray.Length + 2];
buffer[0] = (byte)((ushort)code / 0x0100);
buffer[1] = (byte)code;
Array.Copy(originalArray, 0, buffer, 2, originalArray.Length);
return buffer;
}
public enum ServerOpcode : ushort
{
SERVER_AUTH_CONNECTION_RESPONSE = 0x001,
SERVER_LOGIN_REQUEST_RESPONSE = 0x002,
SERVER_NUM_MSG_TYPES = 0x003
}
Or alternative:
public static ByteBuffer PrependServerOpcode(ByteBuffer byteBuffer, ServerOpcode code)
{
var originalArray = byteBuffer.ToArray(0, byteBuffer.Length);
byte[] buffer = new byte[originalArray.Length + 2];
buffer[0] = (byte)((ushort)code / 0x0100);
buffer[1] = (byte)code;
Array.Copy(originalArray, 0, buffer, 2, originalArray.Length);
return new ByteBuffer(buffer);
}
Usage:
static void Main(string[] args)
{
var bb = new ByteBuffer(new byte[] { 0x01 });
var result = PrependServerOpcode(bb, ServerOpcode.SERVER_NUM_MSG_TYPES);
}
Function that generates timestamp based guid
public Guid GenerateId(
byte[] machineProcessId,
int increment)
{
var tickCount = DateTime.UtcNow.Ticks;
var a = (int)(tickCount >> 32);
var b = (short)(tickCount >> 16);
var c = (short)(tickCount);
var d = new byte[8];
Array.Copy(machineProcessId, d, 5);
d[5] = (byte)(increment >> 16);
d[6] = (byte)(increment >> 8);
d[7] = (byte)(increment);
var guid= new Guid(a, b, c, d);
return guid;
}
Sample GUID based on above function: 08d855c4-987c-51df-70db-ff1d6c000001
Mongo-C#-Driver
As said in the comments by the others, this is technically not a GUID. Meanwhile, you can redo the bitwise operator this way:
public static long GetTicks(Guid target)
{
var parts = target.ToByteArray();
var result = new byte[8];
Array.Copy(parts, 0, result, 4, 2);
Array.Copy(parts, 2, result, 6, 2);
Array.Copy(parts, 4, result, 2, 2);
Array.Copy(parts, 6, result, 0, 2);
var ticks = BitConverter.ToInt64(result,0);
return ticks;
}
And then get the DateTime by new DateTime(ticks), the sample "guid" in your question get converted to "9/10/2020 20:03:33" which looks correct...
Before anybody mentions it, I refered to this link to find out how I needed to copy the backbuffer to a bitmap.
Current situation
I am injected to the target process
Target process' FeatureLevel = Level_11_0
Target SwapChain is being made with DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH flag.
SwapChain::Present function is hooked.
Screenshot turns out black and target process crashes. without screenshot process runs fine.
Desired situation
Make the screenshot properly and let the target process continue with its normal execution.
Code
NOTE Hook class is the same as in the link. I only added an UnmodifiableHook version of it which does what its name says. I left out all unimportant bits.
TestSwapChainHook.cs
using System;
using System.Runtime.InteropServices;
namespace Test
{
public sealed class TestSwapChainHook : IDisposable
{
private enum IDXGISwapChainVirtualTable
{
QueryInterface = 0,
AddRef = 1,
Release = 2,
SetPrivateData = 3,
SetPrivateDataInterface = 4,
GetPrivateData = 5,
GetParent = 6,
GetDevice = 7,
Present = 8,
GetBuffer = 9,
SetFullscreenState = 10,
GetFullscreenState = 11,
GetDesc = 12,
ResizeBuffers = 13,
ResizeTarget = 14,
GetContainingOutput = 15,
GetFrameStatistics = 16,
GetLastPresentCount = 17,
}
public static readonly int VIRTUAL_METHOD_COUNT_LEVEL_DEFAULT = 18;
private static IntPtr[] SWAP_CHAIN_VIRTUAL_TABLE_ADDRESSES;
[UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode, SetLastError = true)]
public delegate int DXGISwapChainPresentDelegate(IntPtr thisPtr, uint syncInterval, SharpDX.DXGI.PresentFlags flags);
public delegate int DXGISwapChainPresentHookDelegate(UnmodifiableHook<DXGISwapChainPresentDelegate> hook, IntPtr thisPtr, uint syncInterval, SharpDX.DXGI.PresentFlags flags);
private DXGISwapChainPresentHookDelegate _present;
private Hook<DXGISwapChainPresentDelegate> presentHook;
static TestSwapChainHook()
{
SharpDX.DXGI.Rational rational = new SharpDX.DXGI.Rational(60, 1);
SharpDX.DXGI.ModeDescription modeDescription = new SharpDX.DXGI.ModeDescription(100, 100, rational, SharpDX.DXGI.Format.R8G8B8A8_UNorm);
SharpDX.DXGI.SampleDescription sampleDescription = new SharpDX.DXGI.SampleDescription(1, 0);
using (SharpDX.Windows.RenderForm renderForm = new SharpDX.Windows.RenderForm())
{
SharpDX.DXGI.SwapChainDescription swapChainDescription = new SharpDX.DXGI.SwapChainDescription();
swapChainDescription.BufferCount = 1;
swapChainDescription.Flags = SharpDX.DXGI.SwapChainFlags.None;
swapChainDescription.IsWindowed = true;
swapChainDescription.ModeDescription = modeDescription;
swapChainDescription.OutputHandle = renderForm.Handle;
swapChainDescription.SampleDescription = sampleDescription;
swapChainDescription.SwapEffect = SharpDX.DXGI.SwapEffect.Discard;
swapChainDescription.Usage = SharpDX.DXGI.Usage.RenderTargetOutput;
SharpDX.Direct3D11.Device device = null;
SharpDX.DXGI.SwapChain swapChain = null;
SharpDX.Direct3D11.Device.CreateWithSwapChain(SharpDX.Direct3D.DriverType.Hardware, SharpDX.Direct3D11.DeviceCreationFlags.BgraSupport, swapChainDescription, out device, out swapChain);
try
{
IntPtr swapChainVirtualTable = Marshal.ReadIntPtr(swapChain.NativePointer);
SWAP_CHAIN_VIRTUAL_TABLE_ADDRESSES = new IntPtr[VIRTUAL_METHOD_COUNT_LEVEL_DEFAULT];
for (int x = 0; x < VIRTUAL_METHOD_COUNT_LEVEL_DEFAULT; x++)
{
SWAP_CHAIN_VIRTUAL_TABLE_ADDRESSES[x] = Marshal.ReadIntPtr(swapChainVirtualTable, x * IntPtr.Size);
}
device.Dispose();
swapChain.Dispose();
}
catch (Exception)
{
if (device != null)
{
device.Dispose();
}
if (swapChain != null)
{
swapChain.Dispose();
}
throw;
}
}
}
public TestSwapChainHook()
{
this._present = null;
this.presentHook = new Hook<DXGISwapChainPresentDelegate>(
SWAP_CHAIN_VIRTUAL_TABLE_ADDRESSES[(int)IDXGISwapChainVirtualTable.Present],
new DXGISwapChainPresentDelegate(hookPresent),
this);
}
public void activate()
{
this.presentHook.activate();
}
public void deactivate()
{
this.presentHook.deactivate();
}
private int hookPresent(IntPtr thisPtr, uint syncInterval, SharpDX.DXGI.PresentFlags flags)
{
lock (this.presentHook)
{
if (this._present == null)
{
return this.presentHook.original(thisPtr, syncInterval, flags);
}
else
{
return this._present(new UnmodifiableHook<DXGISwapChainPresentDelegate>(this.presentHook), thisPtr, syncInterval, flags);
}
}
}
public DXGISwapChainPresentHookDelegate present
{
get
{
lock (this.presentHook)
{
return this._present;
}
}
set
{
lock (this.presentHook)
{
this._present = value;
}
}
}
}
}
Using code
initialization
private TestSwapChain swapChainHook;
private bool capture = false;
private object captureLock = new object();
this.swapChainHook = new TestSwapChainHook();
this.swapChainHook.present = presentHook;
this.swapChainHook.activate();
EDIT
I used a different method to capture a screenshot described in this link. However my screenshot turns out like this:
Now this seems to be a problem with my conversion settings or whatever but I'm unable to find out what exactly I need to do to fix it. I know that the surface I'm converting to a bitmap uses the DXGI_FORMAT_R10G10B10A2_UNORM format (32-bits, 10 bits per color and 2 for alpha I think?). But I'm not sure how this even works in the for loops (skipping bytes and stuff). I just plain copy pasted it.
new hook function
private int presentHook(UnmodifiableHook<IDXGISwapChainHook.DXGISwapChainPresentDelegate> hook, IntPtr thisPtr, uint syncInterval, SharpDX.DXGI.PresentFlags flags)
{
try
{
lock (this.captureLock)
{
if (this.capture)
{
SharpDX.DXGI.SwapChain swapChain = (SharpDX.DXGI.SwapChain)thisPtr;
using (SharpDX.Direct3D11.Texture2D backBuffer = swapChain.GetBackBuffer<SharpDX.Direct3D11.Texture2D>(0))
{
SharpDX.Direct3D11.Texture2DDescription texture2DDescription = backBuffer.Description;
texture2DDescription.CpuAccessFlags = SharpDX.Direct3D11.CpuAccessFlags.Read;
texture2DDescription.Usage = SharpDX.Direct3D11.ResourceUsage.Staging;
texture2DDescription.OptionFlags = SharpDX.Direct3D11.ResourceOptionFlags.None;
texture2DDescription.BindFlags = SharpDX.Direct3D11.BindFlags.None;
using (SharpDX.Direct3D11.Texture2D texture = new SharpDX.Direct3D11.Texture2D(backBuffer.Device, texture2DDescription))
{
//DXGI_FORMAT_R10G10B10A2_UNORM
backBuffer.Device.ImmediateContext.CopyResource(backBuffer, texture);
using (SharpDX.DXGI.Surface surface = texture.QueryInterface<SharpDX.DXGI.Surface>())
{
SharpDX.DataStream dataStream;
SharpDX.DataRectangle map = surface.Map(SharpDX.DXGI.MapFlags.Read, out dataStream);
try
{
byte[] pixelData = new byte[surface.Description.Width * surface.Description.Height * 4];
int lines = (int)(dataStream.Length / map.Pitch);
int dataCounter = 0;
int actualWidth = surface.Description.Width * 4;
for (int y = 0; y < lines; y++)
{
for (int x = 0; x < map.Pitch; x++)
{
if (x < actualWidth)
{
pixelData[dataCounter++] = dataStream.Read<byte>();
}
else
{
dataStream.Read<byte>();
}
}
}
GCHandle handle = GCHandle.Alloc(pixelData, GCHandleType.Pinned);
try
{
using (Bitmap bitmap = new Bitmap(surface.Description.Width, surface.Description.Height, map.Pitch, PixelFormat.Format32bppArgb, handle.AddrOfPinnedObject()))
{
bitmap.Save(#"C:\Users\SOMEUSERNAME\Desktop\test.bmp");
}
}
finally
{
if (handle.IsAllocated)
{
handle.Free();
}
}
}
finally
{
surface.Unmap();
dataStream.Dispose();
}
}
}
}
this.capture = false;
}
}
}
catch(Exception ex)
{
MessageBox.Show(ex.ToString());
}
return hook.original(thisPtr, syncInterval, flags);
}
Answer
Turns out the DXGI_FORMAT_R10G10B10A2_UNORM format is in this bit format:
A=alpha
B=blue
G=green
R=red
AABBBBBB BBBBGGGG GGGGGGRR RRRRRRRR
And Format32bppArgb is in this byte order:
BGRA
So the final loop code would be:
while (pixelIndex < pixelData.Length)
{
uint currentPixel = dataStream.Read<uint>();
uint r = (currentPixel & 0x3FF);
uint g = (currentPixel & 0xFFC00) >> 10;
uint b = (currentPixel & 0x3FF00000) >> 20;
uint a = (currentPixel & 0xC0000000) >> 30;
pixelData[pixelIndex++] = (byte)(b >> 2);
pixelData[pixelIndex++] = (byte)(g >> 2);
pixelData[pixelIndex++] = (byte)(r >> 2);
pixelData[pixelIndex++] = (byte)(a << 6);
while ((pixelIndex % map.Pitch) >= actualWidth)
{
dataStream.Read<byte>();
pixelIndex++;
}
}
That screenshot does look like R10G10B10A2 is getting stuffed into R8G8B8A8. I haven't tested your code but we should have this bit layout
xxxxxxxx yyyyyyyy zzzzzzzz wwwwwwww
RRRRRRRR RRGGGGGG GGGGBBBB BBBBBBAA
and you can extract them as follows
byte x = data[ptr++];
byte y = data[ptr++];
byte z = data[ptr++];
byte w = data[ptr++];
int r = x << 2 | y >> 6;
int g = (y & 0x3F) << 4 | z >> 4;
int b = (z & 0xF) << 6 | w >> 2;
int a = w & 0x3;
where r, g, b now have 10 bit resolution. If you want to scale them back to bytes you can do that with (byte)(r >> 2).
Update
This would replace your double for loop. I have no way of testing this so I don't want to push it further, but I believe the idea is correct. The last check should skip the padding bytes in each row.
while(dataCounter < pixelData.Length)
{
byte x = dataStream.Read<byte>();
byte y = dataStream.Read<byte>();
byte z = dataStream.Read<byte>();
byte w = dataStream.Read<byte>();
int r = x << 2 | y >> 6;
int g = (y & 0x3F) << 4 | z >> 4;
int b = (z & 0xF) << 6 | w >> 2;
int a = w & 0x3;
pixelData[dataCounter++] = (byte)(r >> 2);
pixelData[dataCounter++] = (byte)(g >> 2);
pixelData[dataCounter++] = (byte)(b >> 2);
pixelData[dataCounter++] = (byte)(a << 6);
while((dataCounter % map.Pitch) >= actualWidth)
{
dataStream.Read<byte>();
dataCounter++;
}
}
I have something like that:
[StructLayout(LayoutKind.Explicit)]
public struct PixelColorRGBA
{
[FieldOffset(0)]
public UInt32 ColorBGRA;
[FieldOffset(0)]
public byte Blue;
[FieldOffset(1)]
public byte Green;
[FieldOffset(2)]
public byte Red;
[FieldOffset(3)]
public byte Alpha;
}
What is the fastest way to copy PixelColorRGBA[w, h] to byte[w * h * 4] and vise versa?
I end up with the following code:
var source = new PixelColorRGBA[1000, 1000];
var destination = new byte[4000000];
{
var start = DateTime.Now;
unsafe
{
for (var q = 0; q < 100; q++)
fixed (PixelColorRGBA* tmpSourcePtr = &source[0, 0])
{
var sourcePtr = (IntPtr) tmpSourcePtr;
Marshal.Copy(sourcePtr, destination, 0, 4000000);
}
}
Console.WriteLine("MS: " + DateTime.Now.Subtract(start).TotalMilliseconds);
}
It takes 62 ms on my computer.
Try the following
PixelColorRGBA[,] source = ...;
byte[] dest = source
.Cast<PixelColorRGBA>()
.SelectMany(x => new byte[] { x.Blue, x.Green, x.Red, x.Alpha })
.ToArray();
You can use
System.Buffer.BlockCopy(myArray, 0, byteArray, 0, length)
or
System.Runtime.InteropServices.Marshal.Copy(myArray, 0, destPtr, length);
I use an extension method to convert float arrays into byte arrays:
public static unsafe byte[] ToByteArray(this float[] floatArray, int count)
{
int arrayLength = floatArray.Length > count ? count : floatArray.Length;
byte[] byteArray = new byte[4 * arrayLength];
fixed (float* floatPointer = floatArray)
{
fixed (byte* bytePointer = byteArray)
{
float* read = floatPointer;
float* write = (float*)bytePointer;
for (int i = 0; i < arrayLength; i++)
{
*write++ = *read++;
}
}
}
return byteArray;
}
I understand that an array is a pointer to memory associated with information on the type and number of elements. Also, it seems to me that there is no way of doing a conversion from and to a byte array without copying the data as above.
Have I understood this? Would it even be impossible to write IL to create an array from a pointer, type and length without copying data?
EDIT: Thanks for the answers, I learned some fundamentals and got to try out new tricks!
After initially accepting Davy Landman's answer I found out that while his brilliant StructLayout hack does convert byte arrays into float arrays, it does not work the other way around. To demonstrate:
[StructLayout(LayoutKind.Explicit)]
struct UnionArray
{
[FieldOffset(0)]
public Byte[] Bytes;
[FieldOffset(0)]
public float[] Floats;
}
static void Main(string[] args)
{
// From bytes to floats - works
byte[] bytes = { 0, 1, 2, 4, 8, 16, 32, 64 };
UnionArray arry = new UnionArray { Bytes = bytes };
for (int i = 0; i < arry.Bytes.Length / 4; i++)
Console.WriteLine(arry.Floats[i]);
// From floats to bytes - index out of range
float[] floats = { 0.1f, 0.2f, 0.3f };
arry = new UnionArray { Floats = floats };
for (int i = 0; i < arry.Floats.Length * 4; i++)
Console.WriteLine(arry.Bytes[i]);
}
It seems that the CLR sees both arrays as having the same length. If the struct is created from float data, the byte array's length is just too short.
You can use a really ugly hack to temporary change your array to byte[] using memory manipulation.
This is really fast and efficient as it doesn't require cloning the data and iterating on it.
I tested this hack in both 32 & 64 bit OS, so it should be portable.
The source + sample usage is maintained at https://gist.github.com/1050703 , but for your convenience I'll paste it here as well:
public static unsafe class FastArraySerializer
{
[StructLayout(LayoutKind.Explicit)]
private struct Union
{
[FieldOffset(0)] public byte[] bytes;
[FieldOffset(0)] public float[] floats;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
private struct ArrayHeader
{
public UIntPtr type;
public UIntPtr length;
}
private static readonly UIntPtr BYTE_ARRAY_TYPE;
private static readonly UIntPtr FLOAT_ARRAY_TYPE;
static FastArraySerializer()
{
fixed (void* pBytes = new byte[1])
fixed (void* pFloats = new float[1])
{
BYTE_ARRAY_TYPE = getHeader(pBytes)->type;
FLOAT_ARRAY_TYPE = getHeader(pFloats)->type;
}
}
public static void AsByteArray(this float[] floats, Action<byte[]> action)
{
if (floats.handleNullOrEmptyArray(action))
return;
var union = new Union {floats = floats};
union.floats.toByteArray();
try
{
action(union.bytes);
}
finally
{
union.bytes.toFloatArray();
}
}
public static void AsFloatArray(this byte[] bytes, Action<float[]> action)
{
if (bytes.handleNullOrEmptyArray(action))
return;
var union = new Union {bytes = bytes};
union.bytes.toFloatArray();
try
{
action(union.floats);
}
finally
{
union.floats.toByteArray();
}
}
public static bool handleNullOrEmptyArray<TSrc,TDst>(this TSrc[] array, Action<TDst[]> action)
{
if (array == null)
{
action(null);
return true;
}
if (array.Length == 0)
{
action(new TDst[0]);
return true;
}
return false;
}
private static ArrayHeader* getHeader(void* pBytes)
{
return (ArrayHeader*)pBytes - 1;
}
private static void toFloatArray(this byte[] bytes)
{
fixed (void* pArray = bytes)
{
var pHeader = getHeader(pArray);
pHeader->type = FLOAT_ARRAY_TYPE;
pHeader->length = (UIntPtr)(bytes.Length / sizeof(float));
}
}
private static void toByteArray(this float[] floats)
{
fixed(void* pArray = floats)
{
var pHeader = getHeader(pArray);
pHeader->type = BYTE_ARRAY_TYPE;
pHeader->length = (UIntPtr)(floats.Length * sizeof(float));
}
}
}
And the usage is:
var floats = new float[] {0, 1, 0, 1};
floats.AsByteArray(bytes =>
{
foreach (var b in bytes)
{
Console.WriteLine(b);
}
});
Yes, the type information and data is in the same memory block, so that is impossible unless you overwrite the type information in a float array to fool the system that it's byte array. That would be a really ugly hack, and could easily blow up...
Here's how you can convert the floats without unsafe code if you like:
public static byte[] ToByteArray(this float[] floatArray) {
int len = floatArray.Length * 4;
byte[] byteArray = new byte[len];
int pos = 0;
foreach (float f in floatArray) {
byte[] data = BitConverter.GetBytes(f);
Array.Copy(data, 0, byteArray, pos, 4);
pos += 4;
}
return byteArray;
}
This question is the reverse of What is the fastest way to convert a float[] to a byte[]?.
I've answered with a union kind of hack to skip the whole copying of the data. You could easily reverse this (length = length *sizeof(Double).
I've written something similar for quick conversion between arrays. It's basically an ugly proof-of-concept more than a handsome solution. ;)
public static TDest[] ConvertArray<TSource, TDest>(TSource[] source)
where TSource : struct
where TDest : struct {
if (source == null)
throw new ArgumentNullException("source");
var sourceType = typeof(TSource);
var destType = typeof(TDest);
if (sourceType == typeof(char) || destType == typeof(char))
throw new NotSupportedException(
"Can not convert from/to a char array. Char is special " +
"in a somewhat unknown way (like enums can't be based on " +
"char either), and Marshal.SizeOf returns 1 even when the " +
"values held by a char can be above 255."
);
var sourceByteSize = Buffer.ByteLength(source);
var destTypeSize = Marshal.SizeOf(destType);
if (sourceByteSize % destTypeSize != 0)
throw new Exception(
"The source array is " + sourceByteSize + " bytes, which can " +
"not be transfered to chunks of " + destTypeSize + ", the size " +
"of type " + typeof(TDest).Name + ". Change destination type or " +
"pad the source array with additional values."
);
var destCount = sourceByteSize / destTypeSize;
var destArray = new TDest[destCount];
Buffer.BlockCopy(source, 0, destArray, 0, sourceByteSize);
return destArray;
}
}
public byte[] ToByteArray(object o)
{
int size = Marshal.SizeOf(o);
byte[] buffer = new byte[size];
IntPtr p = Marshal.AllocHGlobal(size);
try
{
Marshal.StructureToPtr(o, p, false);
Marshal.Copy(p, buffer, 0, size);
}
finally
{
Marshal.FreeHGlobal(p);
}
return buffer;
}
this may help you to convert an object to a byte array.
You should check my answer to a similar question: What is the fastest way to convert a float[] to a byte[]?.
In it you'll find portable code (32/64 bit compatible) to let you view a float array as a byte array or vice-versa, without copying the data. It's the fastest way that I know of to do such thing.
If you're just interested in the code, it's maintained at https://gist.github.com/1050703 .
Well - if you still interested in that hack - check out this modified code - it works like a charm and costs ~0 time, but it may not work in future since it's a hack allowing to gain full access to the whole process address space without trust requirements and unsafe marks.
[StructLayout(LayoutKind.Explicit)]
struct ArrayConvert
{
public static byte[] GetBytes(float[] floats)
{
ArrayConvert ar = new ArrayConvert();
ar.floats = floats;
ar.length.val = floats.Length * 4;
return ar.bytes;
}
public static float[] GetFloats(byte[] bytes)
{
ArrayConvert ar = new ArrayConvert();
ar.bytes = bytes;
ar.length.val = bytes.Length / 4;
return ar.floats;
}
public static byte[] GetTop4BytesFrom(object obj)
{
ArrayConvert ar = new ArrayConvert();
ar.obj = obj;
return new byte[]
{
ar.top4bytes.b0,
ar.top4bytes.b1,
ar.top4bytes.b2,
ar.top4bytes.b3
};
}
public static byte[] GetBytesFrom(object obj, int size)
{
ArrayConvert ar = new ArrayConvert();
ar.obj = obj;
ar.length.val = size;
return ar.bytes;
}
class ArrayLength
{
public int val;
}
class Top4Bytes
{
public byte b0;
public byte b1;
public byte b2;
public byte b3;
}
[FieldOffset(0)]
private Byte[] bytes;
[FieldOffset(0)]
private object obj;
[FieldOffset(0)]
private float[] floats;
[FieldOffset(0)]
private ArrayLength length;
[FieldOffset(0)]
private Top4Bytes top4bytes;
}