Comment remettre à zéro la mémoire allouée par le Maréchal.AllocHGlobal?

Je suis à consacrer une mémoire non managée dans mon application via Marshal.AllocHGlobal. Je suis alors la copie d'un ensemble d'octets à cet endroit, et la conversion de l'résultant segment de mémoire pour un struct avant de libérer la mémoire à nouveau via Marshal.FreeHGlobal.

Voici la méthode:

public static T Deserialize<T>(byte[] messageBytes, int start, int length)
    where T : struct
{
    if (start + length > messageBytes.Length)
        throw new ArgumentOutOfRangeException();

    int typeSize = Marshal.SizeOf(typeof(T));
    int bytesToCopy = Math.Min(typeSize, length);

    IntPtr targetBytes = Marshal.AllocHGlobal(typeSize);
    Marshal.Copy(messageBytes, start, targetBytes, bytesToCopy);

    if (length < typeSize)
    {
        //Zero out additional bytes at the end of the struct
    }

    T item = (T)Marshal.PtrToStructure(targetBytes, typeof(T));
    Marshal.FreeHGlobal(targetBytes);
    return item;
}

Cela fonctionne pour la plupart, cependant, si j'ai moins d'octets que la taille de la struct exige, puis "aléatoire" des valeurs sont affectées à la dernière champs (je suis en utilisant LayoutKind.Sequential sur la cible struct). Je tiens à zéro ces pendaison des domaines aussi efficacement que possible.

Pour le contexte, ce code est la désérialisation d'à haute fréquence de multidiffusion les messages envoyés à partir de C++ sur Linux.

Ici est une faute de cas de test:

//Give only one byte, which is too few for the struct
var s3 = MessageSerializer.Deserialize<S3>(new[] { (byte)0x21 });
Assert.AreEqual(0x21, s3.Byte);
Assert.AreEqual(0x0000, s3.Int); //hanging field should be zero, but isn't

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
private struct S3
{
    public byte Byte;
    public int Int;
}

L'exécution de ce test à plusieurs reprises les causes de la seconde affirmer l'échec avec une valeur différente à chaque fois.


MODIFIER

En fin de compte, j'ai utilisé leppie la suggestion de d'aller unsafe et à l'aide de stackalloc. Cette alloué un tableau d'octets qui a été remis à zéro en tant que de besoin, et de l'amélioration du débit entre 50% et 100%, selon la taille des messages (les messages les plus grands a vu le plus grand bénéfice).

La dernière méthode a fini ressemblant à:

public static T Deserialize<T>(byte[] messageBytes, int startIndex, int length)
    where T : struct
{
    if (length <= 0)
        throw new ArgumentOutOfRangeException("length", length, "Must be greater than zero.");
    if (startIndex < 0)
        throw new ArgumentOutOfRangeException("startIndex", startIndex, "Must be greater than or equal to zero.");
    if (startIndex + length > messageBytes.Length)
        throw new ArgumentOutOfRangeException("length", length, "startIndex + length must be <= messageBytes.Length");

    int typeSize = Marshal.SizeOf(typeof(T));
    unsafe
    {
        byte* basePtr = stackalloc byte[typeSize];
        byte* b = basePtr;
        int end = startIndex + Math.Min(length, typeSize);
        for (int srcPos = startIndex; srcPos < end; srcPos++)
            *b++ = messageBytes[srcPos];
        return (T)Marshal.PtrToStructure(new IntPtr(basePtr), typeof(T));
    }   
}

Malheureusement, cela nécessite encore un appel à Marshal.PtrToStructure pour convertir les octets dans le type de cible.

OriginalL'auteur Drew Noakes | 2009-09-28