void ToHexManual( BYTE* pSrc, int nSrcLen, CString& sDest )
{
sDest="";
for ( int j=0; j<nSrcLen; j++ ) {
BYTE b1= *pSrc >> 4; // hi nybble
BYTE b2= *pSrc & 0x0f; // lo nybble
b1+='0'; if (b1>'9') b1 += 7; // gap between '9' and 'A'
b2+='0'; if (b2>'9') b2 += 7;
sDest += b1;
sDest += b2;
pSrc++;
}
}
Here's a more concise (but slower) version that uses a printf-type tool:
void ToHexSlow( BYTE* pSrc, int nSrcLen, CString& sDest )
{
sDest="";
for ( int j=0; j<nSrcLen; j++ ) {
sDest.AppendFormat( "%02x", int(*pSrc) );
pSrc++;
}
}
Both of these are usable, but not as efficient as I'd like. The former requires calculations and conditional tests twice for each byte. The latter makes a function call for each byte. Both versions append the output to an ever-growing CString object, and that can be a
significant problem when the output is large. Let me explain...
void FromHexManual( LPCSTR pSrc, int nSrcLen, BYTE* pDest )
{
for ( int j=0; j<nSrcLen; j+=2 ) {
BYTE b1= pSrc[j] -'0'; if (b1>9) b1 -= 7;
BYTE b2= pSrc[j+1] -'0'; if (b2>9) b2 -= 7;
*pDest++ = (b1<<4) + b2; // <<4 multiplies by 16
}
}
Here's another try that uses sscanf from the C runtime library:
void FromHexSlow( LPCSTR pSrc, int nSrcLen, BYTE* pDest )
{
char sTmp[3]="xx";
int n;
for ( int j=0; j<nSrcLen; j += 2 ) {
sTmp[0]= pSrc[0];
sTmp[1]= pSrc[1];
sscanf( sTmp, "%x", &n );
pSrc += 2;
*pDest++ = (BYTE)n;
}
}
The CryptoAPI Hex Conversion Functions
#pragma comment( lib, "Crypt32.lib" )
#include <Wincrypt.h>
void ToHexCryptoAPI( BYTE* pSrc, int nSrcLen, CString& sDest )
{
DWORD nOutLen= (nSrcLen*3)+4; // xx<space> + etc/
char* pBuf= new char[nOutLen];
BOOL fRet= CryptBinaryToString( pSrc,nSrcLen, CRYPT_STRING_HEX, pBuf, &nOutLen );
sDest= pBuf;
delete pBuf;
}
int FromHexCryptoAPI( LPCSTR pSrc, int nSrcLen, BYTE* pDest )
{
DWORD nOutLen= nSrcLen/2;
BOOL fRet= CryptStringToBinary( pSrc, nSrcLen, CRYPT_STRING_HEX, pDest, &nOutLen, 0, 0);
return( (int)nOutLen );
}
Oddly, the only useful option is CRYPT_STRING_HEX -- the CRYPT_STRING_HEXRAW option is no longer supported. The output from
CryptBinaryToString appears to be formatted for screen output -- spaces between each pair of hex digits, embedded tab characters, CRLF at the end, etc. The two functions above
do work, and there certainly is value in using standard API functions, but the performance was lackluster, and I didn't want the overhead of the extra embedded characters.
BYTE HexLookup[513]= {
"000102030405060708090a0b0c0d0e0f"
"101112131415161718191a1b1c1d1e1f"
"202122232425262728292a2b2c2d2e2f"
"303132333435363738393a3b3c3d3e3f"
"404142434445464748494a4b4c4d4e4f"
"505152535455565758595a5b5c5d5e5f"
"606162636465666768696a6b6c6d6e6f"
"707172737475767778797a7b7c7d7e7f"
"808182838485868788898a8b8c8d8e8f"
"909192939495969798999a9b9c9d9e9f"
"a0a1a2a3a4a5a6a7a8a9aaabacadaeaf"
"b0b1b2b3b4b5b6b7b8b9babbbcbdbebf"
"c0c1c2c3c4c5c6c7c8c9cacbcccdcecf"
"d0d1d2d3d4d5d6d7d8d9dadbdcdddedf"
"e0e1e2e3e4e5e6e7e8e9eaebecedeeef"
"f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff"
};
void ToHex( BYTE* pSrc, int nSrcLen, CString& sDest )
{
WORD* pwHex= (WORD*)HexLookup;
WORD* pwDest= (WORD*)sDest.GetBuffer((nSrcLen*2)+1);
for (int j=0; j<nSrcLen; j++ ) {
*pwDest= pwHex[*pSrc];
pwDest++; pSrc++;
}
*((BYTE*)pwDest)= 0; // terminate the string
sDest.ReleaseBuffer((nSrcLen*2)+1);
}
BYTE DecLookup[] = {
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // gap before first hex digit
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,1,2,3,4,5,6,7,8,9, // 0123456789
0,0,0,0,0,0,0, // :;<=>?@ (gap)
10,11,12,13,14,15, // ABCDEF
0,0,0,0,0,0,0,0,0,0,0,0,0, // GHIJKLMNOPQRS (gap)
0,0,0,0,0,0,0,0,0,0,0,0,0, // TUVWXYZ[/]^_` (gap)
10,11,12,13,14,15 // abcdef
};
void FromHex( BYTE* pSrc, int nSrcLen, BYTE* pDest )
{
for (int j=0; j<nSrcLen; j += 2 ) {
int d = DecLookup[*pSrc++ ] << 4;
d |= DecLookup[*pSrc++ ];
*pDest++ = (BYTE)d;
}
}
Have a question about something in this article? You can receive help directly from the article author. Sign up for a free trial to get started.
Comments (5)
Commented:
Coming from the old days of 3.5KB "PCs" and programmable calculators, I never would have considered the lookup table approach to be that speedy and hence useful. I reckon I should start thinking in bigger figures (i.e. GB) :-)
Author
Commented:Re: Lookup tables -- I used a 512-byte lookup table, but in that "hex challenge" link, one of the participants had a go at pre-calculating a 390KB table in order to lookup the output of two bytes at one time. I called it "...either ridculous or sublime or both." As I recall, the performance gain was less than stellar... once the table gets that large, you lose the L1 cache advantage. But it can be fun to try that kind of thing -- just to find out.
Commented:
MIT? GNU?
Author
Commented:I know of no prohibition or limitation on use of any source code provided in an EE article or Q/A response. Frankly, the whole point of this site is to provide usable solutions, so it would be strange if there were prohibitions on usage. As a practical matter, the above code can be used in any way you want. I'd expect that if you use the code in an article or publication, that you would properly attribute the author and the site.
-- Dan
Commented:
can be unrolled and rumors are that duff's device achieves for example 30% faster with gcc. I believe its definitely worth a try.
Open in new window