snippetcMinor
Convert hex to base64, part 2
Viewed 0 times
converthexpartbase64
Problem
Original question
Reimplemented solution following JS1's answer.
Changed bit string hack to bitwise operations to get the corresponding base64 index values.
```
#include
#include
void get_b64_quads(char hex, char b64quads, int pad)
{
char *dec_2_base64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
char b1 = hex[0] >> 2;
char b2 = ((hex[0] & 0x03) > 4);
char b3 = ((hex[1] & 0x0f) > 6);
char b4 = hex[2] & 0x3f;
b64quads[0] = dec_2_base64[b1];
b64quads[1] = dec_2_base64[b2];
b64quads[2] = pad == 1 && !b3 ? '=' : dec_2_base64[b3];
b64quads[3] = pad && !b4 ? '=' : dec_2_base64[b4];
return;
}
char hex_2_base64(char _hex)
{
int hexstring_size = strlen(_hex);
if (!hexstring_size)
return NULL;
if (hexstring_size % 2 != 0)
return NULL;
//remove whitespaces from hex string
char *moveto = NULL;
char *p = _hex;
while (*p)
{
if (*p == ' ')
{
if (moveto == NULL)
moveto = p;
}
else
{
if (moveto)
{
memmove(moveto, p, strlen(p) + 1);
moveto = NULL;
}
}
p++;
}
//converts hex string to byte array
int bytearray_size = hexstring_size / 2; //each pair of hex chars is a byte
char *bytearray = malloc(bytearray_size);
p = _hex;
char *bytearray_p = bytearray;
char hexbytebuffer[3] = { 0 };
int temp = hexstring_size;
int ibytearray = 0;
while (temp > 0)
{
memcpy(hexbytebuffer, p, 2);
bytearray[ibytearray] = strtol(hexbytebuffer, NULL, 16);
ibytearray++;
p += 2;
temp -= 2;
}
//allocate memory for base64 output (must be freed by whoever calls this function)
int b64length = (((hexstring_size + 2) / 3) * 4) + 1; //each 3 hex bytes will become 4 base64 chars
char *base64 = malloc(b64length + 1);
memset(base64, 0, b64length + 1);
//walk through byte array, converting each 3 bytes to 4 base64 characters
temp = bytearray_size;
char *bytearrayp = bytearray;
char bytes[3] = { 0 };
ch
Reimplemented solution following JS1's answer.
Changed bit string hack to bitwise operations to get the corresponding base64 index values.
```
#include
#include
void get_b64_quads(char hex, char b64quads, int pad)
{
char *dec_2_base64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
char b1 = hex[0] >> 2;
char b2 = ((hex[0] & 0x03) > 4);
char b3 = ((hex[1] & 0x0f) > 6);
char b4 = hex[2] & 0x3f;
b64quads[0] = dec_2_base64[b1];
b64quads[1] = dec_2_base64[b2];
b64quads[2] = pad == 1 && !b3 ? '=' : dec_2_base64[b3];
b64quads[3] = pad && !b4 ? '=' : dec_2_base64[b4];
return;
}
char hex_2_base64(char _hex)
{
int hexstring_size = strlen(_hex);
if (!hexstring_size)
return NULL;
if (hexstring_size % 2 != 0)
return NULL;
//remove whitespaces from hex string
char *moveto = NULL;
char *p = _hex;
while (*p)
{
if (*p == ' ')
{
if (moveto == NULL)
moveto = p;
}
else
{
if (moveto)
{
memmove(moveto, p, strlen(p) + 1);
moveto = NULL;
}
}
p++;
}
//converts hex string to byte array
int bytearray_size = hexstring_size / 2; //each pair of hex chars is a byte
char *bytearray = malloc(bytearray_size);
p = _hex;
char *bytearray_p = bytearray;
char hexbytebuffer[3] = { 0 };
int temp = hexstring_size;
int ibytearray = 0;
while (temp > 0)
{
memcpy(hexbytebuffer, p, 2);
bytearray[ibytearray] = strtol(hexbytebuffer, NULL, 16);
ibytearray++;
p += 2;
temp -= 2;
}
//allocate memory for base64 output (must be freed by whoever calls this function)
int b64length = (((hexstring_size + 2) / 3) * 4) + 1; //each 3 hex bytes will become 4 base64 chars
char *base64 = malloc(b64length + 1);
memset(base64, 0, b64length + 1);
//walk through byte array, converting each 3 bytes to 4 base64 characters
temp = bytearray_size;
char *bytearrayp = bytearray;
char bytes[3] = { 0 };
ch
Solution
Temporary buffers not needed for the most part
I see a lot of places where you copy a few bytes to/from a temporary buffer. I think you could avoid the temporary buffer in most cases and just use the actual source/destination buffer instead. For example, take a look at this main loop in your function:
There are a few things I would change here. First of all, I take the special case of less than 3 bytes out of the main loop, like this:
Second of all, you don't actually need to use the temporary buffers in the main loop. All you are doing in unnecessary copying back and forth. In the special case of less than 3 bytes left, you do need a temporary input buffer because you need to zero out the bytes that don't exit. But you don't need the temporary output buffer. So the code now turns into this:
The same thing can be said about the loop that converts the hex string to a byte array:
Here, I would change to this:
where
Go directly from hex to base64
If you want to be more efficient, you can skip the step where you convert the hex string into a byte array. You can do the base64 conversion directly from the hex string if you want. Right now your last step converts 3 bytes to 4 base64 digits. If you operated on the hex string directly, you would be converting 6 hex characters to 4 base64 digits instead. You'd just need to modify your
Sample reimplementation
Here is how it would look like rewritten:
I see a lot of places where you copy a few bytes to/from a temporary buffer. I think you could avoid the temporary buffer in most cases and just use the actual source/destination buffer instead. For example, take a look at this main loop in your function:
while (temp > 0)
{
if (temp >= 3)
{
memcpy(bytes, bytearrayp, 3);
get_b64_quads(bytes, b64chars, 0);
temp -= 3;
bytearrayp += 3;
}
else
{
//needs to pad with '='
memset(bytes, 0, 3);
memcpy(bytes, bytearrayp, temp);
get_b64_quads(bytes, b64chars, temp);
temp = 0;
}
memcpy(base64p, b64chars, 4);
base64p += 4;
}There are a few things I would change here. First of all, I take the special case of less than 3 bytes out of the main loop, like this:
while (temp > 3)
{
memcpy(bytes, bytearrayp, 3);
get_b64_quads(bytes, b64chars, 0);
memcpy(base64p, b64chars, 4);
temp -= 3;
bytearrayp += 3;
base64p += 4;
}
if (temp > 0)
{
//needs to pad with '='
memset(bytes, 0, 3);
memcpy(bytes, bytearrayp, temp);
get_b64_quads(bytes, b64chars, temp);
memcpy(base64p, b64chars, 4);
}Second of all, you don't actually need to use the temporary buffers in the main loop. All you are doing in unnecessary copying back and forth. In the special case of less than 3 bytes left, you do need a temporary input buffer because you need to zero out the bytes that don't exit. But you don't need the temporary output buffer. So the code now turns into this:
while (temp > 3)
{
get_b64_quads(bytearrayp, base64p, 0);
temp -= 3;
bytearrayp += 3;
base64p += 4;
}
if (temp > 0)
{
//needs to pad with '='
uint8_t bytes[3] = {0};
memcpy(bytes, bytearrayp, temp);
get_b64_quads(bytes, base64p, temp);
}The same thing can be said about the loop that converts the hex string to a byte array:
while (temp > 0)
{
memcpy(hexbytebuffer, p, 2);
bytearray[ibytearray] = strtol(hexbytebuffer, NULL, 16);
ibytearray++;
p += 2;
temp -= 2;
}Here, I would change to this:
while (temp > 0)
{
bytearray[ibytearray++] = (hexval(p[0]) << 4) | hexval(p[1]);
p += 2;
temp -= 2;
}where
hexval() is a helper function that converts a hex character to a value from 0..15.Go directly from hex to base64
If you want to be more efficient, you can skip the step where you convert the hex string into a byte array. You can do the base64 conversion directly from the hex string if you want. Right now your last step converts 3 bytes to 4 base64 digits. If you operated on the hex string directly, you would be converting 6 hex characters to 4 base64 digits instead. You'd just need to modify your
get_b64_quads() function appropriately.Sample reimplementation
Here is how it would look like rewritten:
#include
#include
#include
#include
static inline int hexVal(char c)
{
if (c >= 'a' && c = 'A' && c = '0' && c > 2;
char b2 = ((h0 & 0x03) > 4);
char b3 = ((h1 & 0x0f) > 6);
char b4 = h2 & 0x3f;
b64quads[0] = dec_2_base64[b1];
b64quads[1] = dec_2_base64[b2];
b64quads[2] = pad == 2 && !b3 ? '=' : dec_2_base64[b3];
b64quads[3] = pad && !b4 ? '=' : dec_2_base64[b4];
return;
}
char* hex_2_base64(char *_hex)
{
int hexstring_size = strlen(_hex);
if (!hexstring_size)
return NULL;
if (hexstring_size % 2 != 0)
return NULL;
//remove whitespaces from hex string
char *moveto = NULL;
char *p = _hex;
while (*p)
{
if (*p == ' ')
{
if (moveto == NULL)
moveto = p;
}
else
{
if (moveto)
{
memmove(moveto, p, strlen(p) + 1);
moveto = NULL;
}
}
p++;
}
//allocate memory for base64 output (must be freed by whoever calls this function)
int b64length = (((hexstring_size + 2) / 3) * 4) + 1; //each 3 hex bytes will become 4 base64 chars
char *base64 = calloc(1, b64length + 1);
//walk through byte array, converting each 6 hex chars to 4 base64 characters
char *base64p = base64;
while (hexstring_size > 6)
{
get_b64_quads(_hex, base64p, 0);
hexstring_size -= 6;
_hex += 6;
base64p += 4;
}
if (hexstring_size > 0)
{
char temphex[6] = { '0', '0', '0', '0', '0', '0' };
memcpy(temphex, _hex, hexstring_size);
get_b64_quads(temphex, base64p, hexstring_size);
}
return base64;
}Code Snippets
while (temp > 0)
{
if (temp >= 3)
{
memcpy(bytes, bytearrayp, 3);
get_b64_quads(bytes, b64chars, 0);
temp -= 3;
bytearrayp += 3;
}
else
{
//needs to pad with '='
memset(bytes, 0, 3);
memcpy(bytes, bytearrayp, temp);
get_b64_quads(bytes, b64chars, temp);
temp = 0;
}
memcpy(base64p, b64chars, 4);
base64p += 4;
}while (temp > 3)
{
memcpy(bytes, bytearrayp, 3);
get_b64_quads(bytes, b64chars, 0);
memcpy(base64p, b64chars, 4);
temp -= 3;
bytearrayp += 3;
base64p += 4;
}
if (temp > 0)
{
//needs to pad with '='
memset(bytes, 0, 3);
memcpy(bytes, bytearrayp, temp);
get_b64_quads(bytes, b64chars, temp);
memcpy(base64p, b64chars, 4);
}while (temp > 3)
{
get_b64_quads(bytearrayp, base64p, 0);
temp -= 3;
bytearrayp += 3;
base64p += 4;
}
if (temp > 0)
{
//needs to pad with '='
uint8_t bytes[3] = {0};
memcpy(bytes, bytearrayp, temp);
get_b64_quads(bytes, base64p, temp);
}while (temp > 0)
{
memcpy(hexbytebuffer, p, 2);
bytearray[ibytearray] = strtol(hexbytebuffer, NULL, 16);
ibytearray++;
p += 2;
temp -= 2;
}while (temp > 0)
{
bytearray[ibytearray++] = (hexval(p[0]) << 4) | hexval(p[1]);
p += 2;
temp -= 2;
}Context
StackExchange Code Review Q#144812, answer score: 3
Revisions (0)
No revisions yet.