Я пишу сервер веб-сокетов, и мне приходится иметь дело с замаскированными данными, которые мне нужно разоблачить.
Маска имеет тип unsigned char [4], а данные также являются буфером без знака char *.
Я не хочу XOR байт за байтом, я бы предпочел XOR 4 байта за раз.
uint32_t * const end = reinterpret_cast<uint32_t *>(data_+length);
for(uint32_t *i = reinterpret_cast<uint32_t *>(data_); i != end; ++i) {
*i ^= mask_;
}
Есть ли что-то не так с использованием reinterpret_cast в этой ситуации?
Альтернативой может быть следующий код, который не так понятен и не так быстр:
uint64_t j = 0;
uint8_t *end = data_+length;
for(uint8_t *i = data_; i != end; ++i,++j) {
*i ^= mask_[j % 4];
}
Я все уши за альтернативы, в том числе те, которые зависят от функций C ++ 11.
Вот несколько потенциальных проблем с опубликованным подходом:
char
должен быть правильно выровнен, чтобы быть доступным. Типичное требование для uint32_t
заключается в том, что объект выровнен по адресу, кратному четырем.length / sizeof(uint32_t) != 0
цикл может никогда не закончиться.mask
должен содержать разные значения. Если mask
производится *reinterpret_cast<uint32_t>(char_mask)
подходящего массива это не должно быть массивом.Если об этих проблемах позаботятся, reinterpret_cast<...>(...)
может быть использован в вашей ситуации. Переосмысление значения указателей является одной из причин, по которой эта операция выполняется, а иногда и необходима. Я хотел бы создать подходящий тестовый пример, чтобы убедиться, что он работает должным образом, чтобы избежать необходимости выявлять проблемы при переносе кода на другую платформу.
Лично я бы использовал другой подход, пока профилирование не покажет, что это слишком медленно:
char* it(data);
if (4 < length) {
for (char* end(data + length - 4); it < end; it += 4) {
it[0] ^= mask_[0];
it[1] ^= mask_[1];
it[2] ^= mask_[2];
it[3] ^= mask_[3];
}
}
it != data + length && *it++ ^= mask_[0];
it != data + length && *it++ ^= mask_[1];
it != data + length && *it++ ^= mask_[2];
it != data + length && *it++ ^= mask_[3];
Я определенно использую ряд аналогичных подходов в программном обеспечении, которые должны были быть действительно более быстрыми и не находили их заметной проблемой производительности.
Там нет ничего особенно плохого в reinterpret_cast
в этом случае. Но будь осторожен.
32-битный цикл в его нынешнем виде некорректен, потому что он не подходит для случая, когда полезная нагрузка не кратна 32-битному размеру. Полагаю, два возможных решения:
!=
с <
в проверке цикла (есть причина, почему люди используют <
, и это не потому, что они тупые …) и делают завершающие 1-3 байта побайтноКроме того, в зависимости от того, как структурирован код, вам также может понадобиться справиться с неправильным доступом к данным для некоторых процессоров. Если у вас есть весь буфер в буфере, заголовок и все, в буфере, который выровнен на 32 бита, и если длина полезной нагрузки <126 байт или> 65 535 байт, тогда и ключ маскирования, и полезная нагрузка будут смещены.
Как бы то ни было, мой сервер использует что-то вроде первого цикла:
for(int i=0;i<n;++i)
payload[i]^=key[i&3];
В отличие от 32-битного варианта, это в принципе невозможно ошибиться.