― Steven Redhead, Life Is Simply A Game
The following routine is from a real-world project. It’s supposed to convert binary data into a printable C-string of hexadecimal digits. Even though the developer diligently wrote some unit tests, he got complaints from his fellow coders a few days later. Can you find what’s wrong with it?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
const char* to_hexstring(const void* data, size_t data_len, char* out, size_t out_len) { if (data == NULL || data_len == 0 || out == NULL || out_len == 0) { return NULL; } // Only proceed if output buffer is large enough: // Two chars per byte + trailing string terminator. if (out_len < (data_len * 2) + 1) { return NULL; } const char* p = (const char*) data; for (size_t i = 0; i < data_len; ++i) { char hexbyte[2 + 1]; snprintf(hexbyte, sizeof(hexbyte), "%02X", *p++); strcat(out, hexbyte); } return out; } |
Unit tests:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
TEST(to_hexstring, some_tests) { const unsigned char TEST_DATA[] = { 0x00, 0x11, 0x22, 0x33, 0x1F }; char hexstring[sizeof(TEST_DATA) * 2 + 1]; // Typical case, output buffer large enough. hexstring[0] = '\0'; EXPECT_STREQ("001122331F", to_hexstring(TEST_DATA, sizeof(TEST_DATA), hexstring, sizeof(hexstring))); // Convert just a single byte. hexstring[0] = '\0'; EXPECT_STREQ("00", to_hexstring(TEST_DATA, 1, hexstring, sizeof(hexstring))); // Convert two bytes. hexstring[0] = '\0'; EXPECT_STREQ("0011", to_hexstring(TEST_DATA, 2, hexstring, sizeof(hexstring))); // Error: output buffer too small. hexstring[0] = '\0'; EXPECT_STREQ(NULL, to_hexstring(TEST_DATA, sizeof(TEST_DATA), hexstring, 1)); // Error: output buffer too small. hexstring[0] = '\0'; EXPECT_STREQ(NULL, to_hexstring(TEST_DATA, 1, hexstring, 1)); // Error: output buffer still too small. hexstring[0] = '\0'; EXPECT_STREQ(NULL, to_hexstring(TEST_DATA, 1, hexstring, 1 + 1)); } |