I write an encryption library which uses AES256 CBC based on openssl (libcrypto.so). My library is written in C++ (C++11, actually) Profiling with valgrind shows, that the encryption/decryption is very fast, but my encoding/decoding of the resulting bytes to a hex string is slow.
When encrypting, encode_bytes2hex needs 50% of the total program runtime. When decrypting decode_hex2bytes needs even 75% of the total runtime. I want at least have decode_hex2bytes to be as fast as encode_bytes2hex. But it would also be nice to decrease their runtime by factor 10 or so. I expect these "little" functions to use only insignificant parts of the total runtime.
Here are my implementations:
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;
}
I guess removing std::stringstream somehow could speed-up everything a lot. But how?
Thanks to the answers of @Toby Speight and @Pepijn Kramer I could create much faster functions. This is my solution for C++11
constexpr char to_hex1(unsigned char c) {
return "0123456789abcdef"[c/16];
};
constexpr char to_hex2(unsigned char c) {
return "0123456789abcdef"[c%16];
};
std::string encode_bytes2hex(const std::vector<unsigned char>& rData)
{
std::string result;
result.reserve(rData.size() * 2);
for (auto c: rData) {
result.push_back(to_hex1(c));
result.push_back(to_hex2(c));
}
return result;
}
constexpr unsigned char from_hex(char nC) {
return (nC >= '0' && nC <= '9') ? (nC - '0') :
((nC >= 'a' && nC <= 'f') ? (nC - 'a' +10u) :
(nC >= 'A' && nC <= 'F') ? (nC - 'A' +10u) :
nC
);
};
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> oResult;
oResult.reserve(rHex.size()/2);
for (std::size_t i = 0; i < rHex.size(); i += 2) {
oResult.push_back(from_hex(rHex[i]) * 16u + from_hex(rHex[i+1]));
}
return oResult;
}
I had to write from_hex
in a disgusting way, because C++11 does only allow constexpr
functions with nothing than a return statement. For C++14 and newer it is possible to write cleaner code.
But I must say, even with this code these functions are much much much slower than the AES encryption/decryption routines in openssl itsself. (In my problem I encrypt a string, then encode the bytes to hex).
Including the -O3
parameter for gcc I could speed up my decryption throughput from approx 80k per second to 475k per second. This is a 6x speed-up. The encryption is now up from 175k to 475k which is a speed-up of approx 3x.