문제

확장 방법을 사용하여 플로트 어레이를 바이트 어레이로 변환합니다.

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

배열은 요소 유형 및 수에 대한 정보와 관련된 메모리에 대한 포인터라는 것을 이해합니다. 또한 위와 같이 데이터를 복사하지 않고 바이트 배열에서 전환 할 방법이없는 것 같습니다.

내가 이것을 이해 했습니까? 데이터를 복사하지 않고 포인터, 유형 및 길이에서 배열을 만들기 위해 IL을 작성하는 것이 불가능합니까?

편집하다: 답변 주셔서 감사합니다. 나는 몇 가지 기본 사항을 배웠고 새로운 트릭을 시험해 보았습니다!

Davy Landman의 답변을 처음 수락 한 후 그의 화려한 구조적 해킹이 바이트 어레이로 변환되는 반면 다른 방식으로 작동하지 않는다는 것을 알았습니다. 시연하려면 :

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

CLR은 두 배열의 길이를 동일한 것으로 본 것 같습니다. 플로트 데이터에서 구조물이 생성되면 바이트 배열의 길이가 너무 짧습니다.

도움이 되었습니까?

해결책

예, 유형 정보와 데이터는 동일한 메모리 블록에 있으므로 바이트 배열 인 시스템을 속이기 위해 플로트 배열에서 유형 정보를 덮어 쓰지 않으면 불가능합니다. 그것은 정말 추악한 해킹 일 것이며 쉽게 폭발 할 수 있습니다 ...

원하는 경우 안전하지 않은 코드없이 플로트를 변환하는 방법은 다음과 같습니다.

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

다른 팁

정말 못생긴 해킹을 사용하여 메모리 조작을 사용하여 배열을 바이트로 임시로 변경할 수 있습니다.

데이터를 복제하고 반복 할 필요가 없기 때문에 이것은 매우 빠르고 효율적입니다.

32 & 64 비트 OS 에서이 해킹을 테스트 했으므로 휴대용이어야합니다.

소스 + 샘플 사용량이 유지됩니다 https://gist.github.com/1050703 , 그러나 당신의 편의를 위해서도 여기에 붙여 넣을 것입니다.

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

그리고 사용법은 다음과 같습니다.

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

이 질문은 반대입니다 float []를 바이트 []로 변환하는 가장 빠른 방법은 무엇입니까?.

나는 대답했다 노조의 종류의 해킹 데이터의 전체 복사를 건너 뜁니다. 이것 (길이 = 길이 *sizeof (double)를 쉽게 뒤집을 수 있습니다.

배열 간의 빠른 변환을 위해 비슷한 것을 썼습니다. 기본적으로 잘 생긴 솔루션 이상의 추악한 개념 증명입니다. ;)

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

이를 통해 개체를 바이트 어레이로 변환하는 데 도움이 될 수 있습니다.

비슷한 질문에 대한 내 대답을 확인해야합니다. float []를 바이트 []로 변환하는 가장 빠른 방법은 무엇입니까?.

그것에는 데이터를 복사하지 않고 플로트 어레이를 바이트 어레이 또는 그 반대로 볼 수 있도록 휴대용 코드 (32/64 비트 호환)를 찾을 수 있습니다. 그런 일을하는 것이 가장 빠른 방법입니다.

코드에 관심이 있다면 유지 관리됩니다. https://gist.github.com/1050703 .

글쎄 - 여전히 해킹에 관심이 있다면 -이 수정 된 코드를 확인하십시오 - 매력과 비용 ~ 0 시간처럼 작동하지만 해킹이기 때문에 전체 프로세스 주소 공간에 대한 전체 액세스를 허용하기 때문에 미래에 작동하지 않을 수 있습니다. 신뢰 요구 사항 및 안전하지 않은 마크.

    [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;
    }
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top