我编写了一个基于openssl(libcrypto.so)使用AES256 CBC的加密库。我的库是用 C++ 编写的(实际上是 C++11) 使用 valgrind 进行分析表明,加密/解密非常快,但我将结果字节编码/解码为十六进制字符串的速度很慢。
加密时,encode_bytes2hex需要总程序运行时间的50%。解密 decode_hex2bytes 甚至需要总运行时间的 75%。 我希望至少让 decode_hex2bytes 和 encode_bytes2hex 一样快。但将运行时间减少 10 倍左右也不错。我希望这些“小”函数仅使用总运行时间中无关紧要的部分。
这是我的实现:
std::string encode_bytes2hex(const std::vector<unsigned char>& rData){
//encode as hex
std::stringstream ss;
ss << std::hex << std::setfill('0');
for (auto& c : rData)
{
ss << std::setw(2) << static_cast<int>(c);
}
return ss.str();
}
std::vector<unsigned char> decode_hex2bytes(const std::string& rHex){
std::vector<unsigned char> oBytes(rHex.length()/2);
std::stringstream ssConv;
for(size_t n = 0; n < rHex.length(); n+=2)
{
ssConv << std::hex << rHex.substr(n,2);
int byte;
ssConv >> byte;
oBytes[n/2] = byte & 0xFF;
ssConv.str(std::string());
ssConv.clear();
}
return oBytes;
}
我想删除 std::stringstream 可以大大加快一切速度。但如何呢?
当您通过
std::stringstream
时,您会产生大量不必要的开销,并且当您往返于 std::string
时,会产生额外的副本。
直接使用字符串,并在自己的循环中读/写每个半字节:
#include <sstream>
#include <iomanip>
std::string encode_bytes2hex(const std::vector<unsigned char>& rData)
{
std::string result;
result.reserve(rData.size() * 2);
static auto const to_hex = [](unsigned char c) {
return "0123456789abcdef"[c];
};
for (auto c: rData) {
result.push_back(to_hex(c / 16));
result.push_back(to_hex(c % 16));
}
return result;
}
std::vector<unsigned char> decode_hex2bytes(const std::string& rHex)
{
if (rHex.size() % 2) {
throw std::invalid_argument("hex string of odd length");
}
std::vector<unsigned char> result;
result.reserve(rHex.size()/2);
static auto const from_hex = [](char c) {
switch (c) {
case '0': return 0; case '1': return 1; case '2': return 2; case '3': return 3;
case '4': return 4; case '5': return 5; case '6': return 6; case '7': return 7;
case '8': return 8; case '9': return 9; case 'a': return 10; case 'b': return 11;
case 'c': return 12; case 'd': return 13; case 'e': return 14; case 'f': return 15;
case 'A': return 10; case 'B': return 11; case 'C': return 12;
case 'D': return 13; case 'E': return 14; case 'F': return 15;
}
throw std::invalid_argument("not a hex digit");
};
for (std::size_t i = 0; i < rHex.size(); i += 2) {
result.push_back(static_cast<unsigned char>(from_hex(rHex[i]) * 16u + from_hex(rHex[i+1])));
}
return result;
}
这通过了相同的测试:
int main()
{
const auto v = std::vector<unsigned char>{ 0xFF, 0x0a, 0x00, 0x12 };
const auto s = std::string{"ff0a0012"};
return encode_bytes2hex(v) != s
|| decode_hex2bytes(s) != v;
}