C# unsafe value type array to byte array conversions

后端 未结 7 754
梦毁少年i
梦毁少年i 2020-11-30 08:20

I use an extension method to convert float arrays into byte arrays:

public static unsafe byte[] ToByteArray(this float[] floatArray, int count)
{
    int arr         


        
相关标签:
7条回答
  • 2020-11-30 08:39

    I've written something similar for quick conversion between arrays. It's basically an ugly proof-of-concept more than a handsome 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;
        }
    }
    
    0 讨论(0)
  • 2020-11-30 08:41

    Well - if you still interested in that hack - check out this modified code - it works like a charm and costs ~0 time, but it may not work in future since it's a hack allowing to gain full access to the whole process address space without trust requirements and unsafe marks.

        [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;
        }
    
    0 讨论(0)
  • 2020-11-30 08:43

    This question is the reverse of What is the fastest way to convert a float[] to a byte[]?.

    I've answered with a union kind of hack to skip the whole copying of the data. You could easily reverse this (length = length *sizeof(Double).

    0 讨论(0)
  • 2020-11-30 08:45

    Yes, the type information and data is in the same memory block, so that is impossible unless you overwrite the type information in a float array to fool the system that it's byte array. That would be a really ugly hack, and could easily blow up...

    Here's how you can convert the floats without unsafe code if you like:

    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;
    }
    
    0 讨论(0)
  • 2020-11-30 08:46
        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;
        }
    

    this may help you to convert an object to a byte array.

    0 讨论(0)
  • 2020-11-30 08:47

    You can use a really ugly hack to temporary change your array to byte[] using memory manipulation.

    This is really fast and efficient as it doesn't require cloning the data and iterating on it.

    I tested this hack in both 32 & 64 bit OS, so it should be portable.

    The source + sample usage is maintained at https://gist.github.com/1050703 , but for your convenience I'll paste it here as well:

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

    And the usage is:

    var floats = new float[] {0, 1, 0, 1};
    floats.AsByteArray(bytes =>
    {
        foreach (var b in bytes)
        {
            Console.WriteLine(b);
        }
    });
    
    0 讨论(0)
提交回复
热议问题