What should I pin when working on arrays?

送分小仙女□ 提交于 2019-12-01 22:29:58

I believe that your usage of pinned local variables is correct.

You don't need to pin anything in this method, if you want to pin then pin your array before input to this method. You don't need to pin any pointer because address of an element alway same unless you restart your program, you can even stock it into intptr type without any problem.

.maxstack 3
ldarg.0
ldarg.1
ldelema int8

ldarg.2
ldarg.3
ldelema int8

ldarg.s 4
cpblk

ret

void cpblk<T>(ref T src, ref T dst, int c_elem)

Copies c_elem elements of type T from src to dst using the cpblk IL instruction. Note that c_elem indicates the number of elements, not the number of bytes. Tested with C#7 and .NET 4.7. See usage example below.

public static class IL<T>
{
    public delegate void _cpblk_del(ref T src, ref T dst, int c_elem);
    public static readonly _cpblk_del cpblk;

    static IL()
    {
        var dm = new DynamicMethod("cpblk+" + typeof(T).FullName,
            typeof(void),
            new[] { typeof(T).MakeByRefType(), typeof(T).MakeByRefType(), typeof(int) },
            typeof(T),
            true);

        var il = dm.GetILGenerator();
        il.Emit(OpCodes.Ldarg_1);
        il.Emit(OpCodes.Ldarg_0);
        il.Emit(OpCodes.Ldarg_2);

        int cb = Marshal.SizeOf<T>();
        if (cb > 1)
        {
            il.Emit(OpCodes.Ldc_I4, cb);
            il.Emit(OpCodes.Mul);
        }

        byte align;
        if ((cb & (align = 1)) != 0 ||
            (cb & (align = 2)) != 0 ||
            (cb & (align = 4)) != 0)
            il.Emit(OpCodes.Unaligned, align);

        il.Emit(OpCodes.Cpblk);
        il.Emit(OpCodes.Ret);
        cpblk = (_cpblk_del)dm.CreateDelegate(typeof(_cpblk_del));
    }
}

Note that this code assumes that the elements are byte-packed (i.e., no padding between individual elements) and aligned according to their size. Specifically, the source and destination addresses should be divisible by 1 << floor(log₂(sizeof(T) & 0xF)) Said another way, if sizeof(T) % 8 is non-zero, then OpCodes.Unaligned prefix is emitted specifying the highest divisor of that remainder amongst {1, 2, or 4}. For 8-byte alignment, no prefix is needed.

As an example, a 11-byte struct requires alignment prefix 1 because even if the first element in the range happens to be quad-aligned, byte-packing means the adjacent ones won't be. Normally, the CLR arranges arrays this way and you don't have to worry about these issues.

Usage:

var src = new[] { 1, 2, 3, 4, 5, 6 };
var dst = new int[6];

IL<int>.cpblk(ref src[2], ref dst[3], 2);      // dst => { 0, 0, 0, 3, 4, 0 }

Automatic type inference (optional):

For automatic type inference, you can include the following class as well:

public static class IL
{
    public static void cpblk<T>(ref T src, ref T dst, int c_elem) 
        => IL<T>.cpblk(ref src, ref dst, c_elem);
}

With this, you don't need to specify the type arguments and the previous example becomes simply:

IL.cpblk(ref src[2], ref dst[3], 2);
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!