C # non sicuro tipo di matrice per conversioni di array di byte

Io uso un metodo di estensione per convertire gli array float in matrici di byte:

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; } 

Comprendo che un array è un puntatore alla memoria associato alle informazioni sul tipo e sul numero di elementi. Inoltre, mi sembra che non ci sia modo di fare una conversione da e verso un array di byte senza copiare i dati come sopra.

Ho capito questo? Sarebbe persino imansible scrivere IL per creare un array da un puntatore, un tipo e una lunghezza senza copiare i dati?

EDIT: Grazie per le risposte, ho imparato alcuni fondamenti e ho dovuto provare nuovi trucchi!

Dopo aver inizialmente accettato la risposta di Davy Landman, ho scoperto che mentre il suo geniale StructLayout hack converte gli array di byte in array mobili, non funziona nel contrario. Dimostrare:

 [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]); } 

Sembra che CLR consideri entrambi gli array come aventi la stessa lunghezza. Se la struttura viene creata da dati float, la lunghezza dell’array di byte è troppo breve.

Sì, le informazioni sul tipo e i dati si trovano nello stesso blocco di memoria, quindi è imansible a meno che non si sovrascriva le informazioni sul tipo in un array float per ingannare il sistema che si tratta di array di byte. Sarebbe davvero un brutto scherzo e potrebbe facilmente saltare in aria …

Ecco come puoi convertire i float senza codice non sicuro se ti piace:

 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; } 

Puoi usare un trucco davvero brutto per modificare temporaneamente la tua matrice in byte [] usando la manipolazione della memoria.

Questo è veramente veloce ed efficiente in quanto non richiede la clonazione dei dati e l’iterazione su di esso.

Ho provato questo hack in entrambi i sistemi operativi a 32 e 64 bit, quindi dovrebbe essere portatile.

L’utilizzo del campione + di esempio viene mantenuto su https://gist.github.com/1050703 , ma per maggiore praticità lo incollo anche qui:

 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 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 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(this TSrc[] array, Action 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)); } } } 

E l’utilizzo è:

 var floats = new float[] {0, 1, 0, 1}; floats.AsByteArray(bytes => { foreach (var b in bytes) { Console.WriteLine(b); } }); 

Questa domanda è il contrario di Qual è il modo più veloce per convertire un float [] in un byte []? .

Ho risposto con un tipo di unione di hack per saltare l’intera copia dei dati. Si potrebbe facilmente invertire questo (lunghezza = lunghezza * sizeof (doppio).

Ho scritto qualcosa di simile per una rapida conversione tra array. È fondamentalmente un brutto proof-of-concept più che una bella soluzione. 😉

 public static TDest[] ConvertArray(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; } 

questo può aiutarti a convertire un object in un array di byte.

Dovresti controllare la mia risposta a una domanda simile: qual è il modo più veloce per convertire un float [] in un byte []? .

In esso troverai un codice portatile (compatibile a 32/64 bit) che ti consente di visualizzare un array mobile come array di byte o viceversa, senza copiare i dati. È il modo più veloce che conosco per fare una cosa del genere.

Se sei solo interessato al codice, viene mantenuto su https://gist.github.com/1050703 .

Bene – se sei ancora interessato a questo hack – dai un’occhiata a questo codice modificato – funziona come un incantesimo e costa ~ 0 volte, ma potrebbe non funzionare in futuro dato che è un hack che permette di ottenere pieno accesso all’intero spazio degli indirizzi del processo senza requisiti di fiducia e marchi non sicuri.

  [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; }