Question

J'utilise une méthode d'extension pour convertir des tableaux à flotteurs en tableaux d'octets:

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

Je comprends qu'un tableau est un pointeur sur la mémoire associé à des informations sur le type et le nombre d'éléments. De plus, il me semble qu’il n’ya aucun moyen de convertir un tableau en octets sans copier les données comme ci-dessus.

Ai-je compris cela? Serait-il même impossible d'écrire IL pour créer un tableau à partir d'un pointeur, d'un type et d'une longueur sans copier les données?

EDIT: Merci pour les réponses, j'ai appris quelques notions de base et j'ai pu essayer de nouvelles astuces!

Après avoir initialement accepté la réponse de Davy Landman, j’ai découvert que, même si son génial StructLayout bidouille convertit les tableaux d’octets en tableaux flottants, ils ne fonctionnent pas dans l’inverse. Pour démontrer:

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

Il semble que le CLR considère que les deux tableaux ont la même longueur. Si la structure est créée à partir de données flottantes, la longueur du tableau d'octets est trop courte.

Était-ce utile?

La solution

Oui, les informations de type et les données se trouvent dans le même bloc de mémoire. Il est donc impossible de remplacer les informations de type dans un tableau float afin de tromper le système en lui indiquant qu'il s'agit d'un tableau d'octets. Ce serait un bidule vraiment moche, et pourrait facilement exploser ...

Voici comment vous pouvez convertir les flottants sans code dangereux si vous aimez:

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

Autres conseils

Vous pouvez utiliser un hack vraiment moche pour changer temporairement votre tableau en octets [] en utilisant la manipulation de la mémoire.

C’est très rapide et efficace, car il n’est pas nécessaire de cloner les données et de les itérer.

J'ai testé ce piratage dans 32 & amp; Le système d'exploitation 64 bits devrait donc être portable.

L'utilisation de source + sample est conservée à l'adresse https://gist.github.com/1050703 . mais pour votre commodité, je le collerai ici aussi:

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

Et l'utilisation est:

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

Cette question est l'inverse de Quel est le moyen le plus rapide de convertir un float [] en octet []? .

J'ai répondu avec un type de piratage de l'union pour ignorer la copie complète des données. Vous pouvez facilement l’inverser (longueur = longueur * sizeof (Double).

J'ai écrit quelque chose de similaire pour une conversion rapide entre des tableaux. Il s’agit essentiellement d’une vaine preuve de concept plutôt que d’une belle 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;
    }

cela peut vous aider à convertir un objet en tableau d'octets.

Vous devriez vérifier ma réponse à une question similaire: Quel est le moyen le plus rapide de convertir un float [] en octet []? .

Vous y trouverez du code portable (compatible 32/64 bits) vous permettant de visualiser un tableau flottant sous forme de tableau d'octets ou inversement, sans copier les données. C'est le moyen le plus rapide que je connaisse pour faire une telle chose.

Si le code vous intéresse, il est disponible à l'adresse https://gist.github.com/. 1050703 .

Eh bien - si ce hack vous intéresse toujours - consultez ce code modifié - il fonctionne comme un charme et coûte environ 0 $, mais il risque de ne pas fonctionner à l'avenir car il s'agit d'un hack permettant d'avoir un accès complet à l'ensemble du processus espace d'adressage sans exigences de confiance et marques non sécurisées.

    [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;
    }
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top