Как назначить byte[] в качестве указателя в C#
Вопрос
У меня есть функция, которая генерирует проверочный байт CRC на основе содержимого любого пакета. Проблема заключается в переводе функции с C++ на C#.
Код С++:
unsigned char GenerateCheckByte( char* packet, int length, unsigned long seed )
{
if( !packet ) return 0;
unsigned long checksum = 0xFFFFFFFF;
length &= 0x7FFF;
char* ptr = packet;
unsigned long moddedseed = seed << 8;
for( int i = 0; i < length; i++ )
checksum = ( checksum >> 8 ) ^ table[moddedseed + ( ( *(ptr++) ^ checksum ) & 0xFF )];
unsigned char result = ( (checksum>>24)&0xFF ) + ( (checksum>>8)&0xFF ) + ( (checksum>>16)&0xFF ) + ( checksum&0xFF );
return result;
}
char*(пакет) также может быть определен как LPBYTE. Идея состоит в том, что значение, назначенное *packet, присваивается *ptr, и, как вы видите, *ptr увеличивается. Это означает, что передается массив байтов и увеличивается значение указатель, он переходит к следующему байту.
Я пытался сделать это на C#, но много раз терпел неудачу. После упорной работы я нашел код, но не могу его выполнить :?
код С#
public static unsafe byte GenerateCheckByte(byte *packet, int length, UInt32 seed )
{
if (*packet == 0)
return 0;
UInt32 checksum = 0xFFFFFFFF;
length &= 0x7FFF;
byte *ptr = packet;
UInt32 moddedseed = seed << 8;
for (int i = 0; i < length; i++)
checksum = ( checksum >> 8 ) ^ Table.table[moddedseed + ( ( *(ptr++) ^ checksum ) & 0xFF )];
byte result = (byte)(( (checksum>>24)&0xFF ) + ( (checksum>>8)&0xFF ) + ( (checksum>>16)&0xFF ) + ( checksum&0xFF ));
return result;
}
Выглядит не так уж и плохо, но я не могу это назвать
unsafe
{
packetBuffer[5] = Functions.GenerateCheckByte(&packetBuffer[0], 18, packet.seedCRC);
}
ошибка:«Вы можете использовать только адрес незафиксированного выражения внутри фиксированного инициализатора оператора»
Пожалуйста, обрати внимание
packagebuffer как в приложении C++, так и в приложении C# равен byte[] packageBuffer = new byte[18];
Решение
Вы можете заставить метод принимать массив байтов:
public static unsafe byte GenerateCheckByte(byte[] packetArray, int length, UInt32 seed)
{
fixed(byte *packet = packetArray)
{
... etc
}
}
Лучше спрятать небезопасные вещи как можно дальше за управляемыми интерфейсами.
Тогда вызвать его было бы легко:
packetBuffer[5] = Functions.GenerateCheckByte(packetBuffer, 18, ...
На самом деле, лучше было бы написать GenerateCheckByte
в любом случае работать с массивом, вместо того, чтобы углубляться в unsafe
методы:
public static unsafe byte GenerateCheckByte(byte[] packet, int length, UInt32 seed )
{
if (packet == null)
throw new ArgumentNullException("packet"); // the right way in C#
UInt32 checksum = 0xFFFFFFFF;
length &= 0x7FFF;
UInt32 moddedseed = seed << 8;
for (int i = 0; i < length; i++)
checksum = ( checksum >> 8 ) ^ Table.table[moddedseed + ( ( packet[i] ^ checksum ) & 0xFF )];
byte result = (byte)(( (checksum>>24)&0xFF ) + ( (checksum>>8)&0xFF ) + ( (checksum>>16)&0xFF ) + ( checksum&0xFF ));
return result;
}
Напишите самую простую и безопасную реализацию, какую только можете, и возитесь с указателями только в том случае, если обнаружите узкое место в профилировании.
Вы просто переводите большую часть существующего C/C++ на C#?В этом нет особого смысла, если вы не получите от этого новых возможностей безопасности и ремонтопригодности.:)
Другие советы
Вам вообще не обязательно использовать небезопасный код.Если вы отправите в функцию массив байтов, она сможет получить к нему доступ без использования указателей.
Я не проверял код, но должно быть примерно так:
byte GenerateCheckByte(byte[] packet, ulong seed) {
if (packet == null) return 0;
int length = packet.Length & 0x7FFF;
ulong checksum = 0xFFFFFFFF;
ulong moddedseed = seed << 8;
for (int i = 0; i < length; i++) {
checksum = (checksum >> 8) ^ table[moddedseed + ((packet[i] ^ checksum) & 0xFF)];
}
return (byte)(
((checksum >> 24) & 0xFF) +
((checksum >> 16) & 0xFF) +
((checksum >> 8) & 0xFF) +
(checksum & 0xFF)
);
}
Вам необходимо «закрепить» массив байтов в памяти, чтобы использовать его как байт*.
byte checksum;
fixed(byte* pPacketBuffer = packetBuffer)
{
checksum = Functions.GenerateCheckByte(pPacketBuffer, 18, packet.seedCRC)
}
packetBuffer[5] = checksum
Использованная литература: