Frage

Ich verwende eine Erweiterungsmethode float Arrays in Byte-Arrays zu konvertieren:

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

Ich verstehe, dass ein Array ein Zeiger auf dem Speicher mit Informationen über die Art und Anzahl der Elemente zugeordnet ist. Auch scheint es mir, dass es keine Möglichkeit gibt, eine Konvertierung von und zu einem Byte-Array zu tun, ohne die Daten, wie oben zu kopieren.

Habe ich das verstanden? Wäre es sogar unmöglich sein, IL zu schreiben ein Array aus einer Zeiger, die Art und Länge zu erstellen, ohne Daten zu kopieren?

EDIT: Danke für die Antworten, habe ich gelernt, einige Grundlagen und bekam neue Tricks auszuprobieren

Nach anfänglich Davy Landman Antwort zu akzeptieren fand ich heraus, dass während seiner brillanten StructLayout Hack tut Bytefeldgruppen in float Arrays konvertieren, ist es nicht umgekehrt funktioniert. Um zu zeigen:

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

Es scheint, dass die CLR beide Arrays sieht die gleiche Länge haben. Wenn die Struktur von Float-Daten erstellt wird, die Länge des Byte-Array ist einfach zu kurz.

War es hilfreich?

Lösung

Ja, die Art von Informationen und Daten im selben Speicherblock, so dass ist unmöglich, es sei denn, Sie die Typinformationen in einem Float-Array überschreiben, das System zu täuschen, dass es Byte-Array. Das wäre ein wirklich hässlicher Hack, und könnte leicht sprengen ...

Hier ist, wie Sie den Schwimmer ohne unsicheren Code umwandeln können, wenn Sie mögen:

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

Andere Tipps

Sie können einen wirklich hässlichen Hack vorübergehende Änderung Ihrer array [zum Byte verwenden] Speicherbearbeitung verwendet wird.

Das ist wirklich schnell und effizient, da es nicht die Daten Klonen erfordert und Iterieren auf sich.

Ich testete diesen Hack in 32 & 64-Bit-Betriebssystem, so sollte es tragbar sein.

Die Quelle + Probe Nutzung ist unter https://gist.github.com/1050703 , aber für Ihre Bequemlichkeit ich werde es füge auch hier:

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

Und die Nutzung ist:

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

Diese Frage ist die Umkehrung der Was ist der schnellste Weg, um einen Schwimmer [] zu einem byte []? .

konvertieren

Ich habe mit einem Vereinigung Art von hack die ganze Kopieren der Daten zu überspringen. Man könnte leicht umgekehrte Reihenfolge (Länge = Länge * sizeof (Doppel).

Ich habe etwas ähnliches für die schnelle Konvertierung zwischen Arrays geschrieben. Es ist im Grunde einen hässlichen Proof-of-concept mehr als eine gut aussehende Lösung. ;)

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

Dies können Ihnen helfen, ein Objekt in einer Byte-Array zu konvertieren.

Sie sollten meine Antwort auf eine ähnliche Frage prüfen: Was ist der schnellste Weg einen Schwimmer [] zu einem byte [] konvertieren? .

In ihm finden Sie portablen Code (32/64 Bit-kompatibel) finden Sie lassen einen Schwimmer Array als Byte-Array oder umgekehrt zu sehen, ohne die Daten zu kopieren. Es ist der schnellste Weg, die ich kenne so etwas zu tun.

Wenn Sie nur Interesse an dem Code ist, ist es gehalten bei https://gist.github.com/ 1050703 .

Nun - wenn Sie Interesse noch in diesem Hack - diesen modifizierten Code überprüfen - es wirkt wie ein Zauber und Kosten ~ 0 Zeit, aber es kann, da es nicht in Zukunft arbeitet ein Hack ist vollen Zugriff auf den gesamten Prozess zu gewinnen, so dass Adressraum ohne Vertrauen Anforderungen und unsichere Marken.

    [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;
    }
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top