gpt4 book ai didi

C# 不安全值类型数组到字节数组的转换

转载 作者:可可西里 更新时间:2023-11-01 08:55:02 25 4
gpt4 key购买 nike

我使用扩展方法将 float 组转换为字节数组:

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 hack 确实可以将字节数组转换为 float 组,但反过来却行不通。演示:

[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 认为两个数组具有相同的长度。如果结构是从 float 据创建的,字节数组的长度就太短了。

最佳答案

你可以使用一个非常丑陋的 hack,通过内存操作将你的数组临时更改为 byte[]。

这非常快速且高效,因为它不需要克隆数据并对其进行迭代。

我在 32 位和 64 位操作系统中测试了这个 hack,所以它应该是可移植的。

源代码+示例使用维护在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);
}
});

关于C# 不安全值类型数组到字节数组的转换,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/621493/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com