Dear all:

I've created TCPsocket in linux using C++. It receives an array of unsigned characters. I need the corresponding hex values in character format.
i.e. "ABC" in hex it is "414243". Now I want to treat each of "414243" as character.

So I have done the coding as follow ( taking into cosideration that the TCP socket is established and the data is received in an unsigned character pointer m_cpSockData sized same to Buffer Size of the socket. And the number of byte read is m_iReadLen).


char* m_cHexvalue = new char[2*BufSize +1];
char *m_cCurrCharVal=new char[3];

*(m_cCurrCharVal+2)=0X00;
memset(m_cHexvalue ,0,2);

for(int count=0;count<BufSize ;count++)
{
     memset(m_cCurrCharVal,0,2);
     sprintf(m_cCurrCharVal,"%x",*(m_cpSockData+count));
     strcat(m_cHexvalue ,m_cCurrCharVal);

}

<< moderator edit: added [code][/code] tags >>


I also have similar thing while writing back to the socket.

But this reduces the efficiency of the code..

Can any body help to provide a mechanism where efficiency can be increased.

Dani AI

Generated

Good topic, . The main cost in your original loop is repeated sprintf + strcat (O(n^2) growth) and treating binary data as C-strings. Network payloads can contain '\0', so always work with explicit lengths (m_iReadLen), not BufSize, and never rely on while (*p) termination. Also, only hex-encode if the protocol actually requires ASCII hex; if peers accept raw bytes, skip the conversion entirely.

Below is a branch-light, allocation-safe pair that avoids libc formatting, pre-sizes outputs, and handles both upper- and lowercase hex. It also addresses ’s note: values outside 0-9/A-F are rejected cleanly.

#include <string>
#include <vector>
#include <cstdint>

static inline std::string hex_encode(const uint8_t* data, size_t n) {
    static const char hexd[] = "0123456789ABCDEF";
    std::string out;
    out.resize(n * 2);
    for (size_t i = 0; i < n; ++i) {
        uint8_t b = data[i];
        out[2*i]     = hexd[b >> 4];
        out[2*i + 1] = hexd[b & 0x0F];
    }
    return out;
}

static inline uint8_t hex_nibble(unsigned char c) {
    if (c >= '0' && c <= '9') return uint8_t(c - '0');
    c |= 0x20; // fold to lowercase
    if (c >= 'a' && c <= 'f') return uint8_t(c - 'a' + 10);
    return 0xFF; // invalid
}

static inline bool hex_decode(const char* s, size_t len, std::vector<uint8_t>& out) {
    if (len % 2) return false;
    out.resize(len / 2);
    for (size_t i = 0, j = 0; i < len; i += 2, ++j) {
        uint8_t hi = hex_nibble((unsigned char)s[i]);
        uint8_t lo = hex_nibble((unsigned char)s[i+1]);
        if ((hi | lo) == 0xFF) return false;
        out[j] = uint8_t((hi << 4) | lo);
    }
    return true;
}

Tips:

  • Use reinterpret_cast<const uint8_t*>(m_cpSockData) and m_iReadLen when encoding.
  • For decoding, validate input first and fail fast; do not write partial bytes to your socket buffer.
  • If you need even more speed, keep the hexdigit table in L1 by marking functions inline, compile with -O3 -march=native, and avoid branches inside the hot loop (as above).

Recommended Answers

All 7 Replies

This is pretty much the same, but it's all I've got at the moment.

#include <stdio.h>

char *foo(char *dst, const char *src)
{
   char *start = dst;
   while ( *src )
   {
      dst += sprintf(dst, "%x", (unsigned)*src++);
   }
   return start;
}

char *bar(char *dst, const char *src)
{
   char *start = dst;
   while ( *src )
   {
      unsigned value;
      if ( sscanf(src, "%2x", &value) == 1 )
      {
         src += 2;
         *dst++ = value;
      }
   }
   *dst = '\0';
   return start;
}

int main(void)
{
   const char abc[] = "ABC";
   char over [ sizeof abc * 2  - 1 ], back [ sizeof abc ];
   printf("\"%s\" -> \"%s\"\n", abc,  foo(over, abc));
   printf("\"%s\" -> \"%s\"\n", over, bar(back, over));
   return 0;
}

/* my output
"ABC" -> "414243"
"414243" -> "ABC"
*/

Thanks for the reply.

I understand you have removed some operations. That reduces quite a big time.

I think this can further be minimized. Do you have any idea of doing this with the help of asm codes in C.

I've not used so far but I think that may help the roundtrip time.

Thanks again.
Sutanu

Perhaps discard the standard routines and roll your own.

#include <stdio.h>
#include <ctype.h>

unsigned char *foo(unsigned char *dst, const unsigned char *src)
{
   static const unsigned char nibble[] = 
   {
      '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'
   };
   unsigned char *start = dst;
   while ( *src )
   {
      *dst++ = nibble [ *src >> 4    ];
      *dst++ = nibble [ *src++ & 0xF ];
   }
   *dst = '\0';
   return start;
}

unsigned char *bar(unsigned char *dst, const unsigned char *src)
{
   unsigned char *start = dst;
   while ( *src )
   {
      if ( isdigit(*src) )
      {
         *dst = *src++ - '0';
      }
      else
      {
         *dst = *src++ - 'A' + 10;
      }
      *dst <<= 4;
      if ( isdigit(*src) )
      {
         *dst++ += *src++ - '0';
      }
      else
      {
         *dst++ += *src++ - 'A' + 10;
      }
   }
   *dst = '\0';
   return start;
}

int main(void)
{
   const unsigned char abc[] = "ABCMNO";
   unsigned char over [ sizeof abc * 2  - 1 ], back [ sizeof abc ];
   printf("\"%s\" -> \"%s\"\n", abc,  foo(over, abc));
   printf("\"%s\" -> \"%s\"\n", over, bar(back, over));
   return 0;
}

/* my output
"ABCMNO" -> "4142434D4E4F"
"4142434D4E4F" -> "ABCMNO"
*/

This is for ASCII, and I think it is right. But I was wrong with it a minute ago.

Thanks again.

I performed a dry run. I think it would be better to use

if (*src <= '9')

rather than

if (isdigit(*src))

What do you think...

Sutanu

I performed a dry run. I think it would be better to use

if (*src <= '9')

rather than

if (isdigit(*src))

What do you think...

Sutanu

If that works better, go ahead. Many times the standard functions are implemented faster than writing an equivalent, but not always.

if( *src <= '9' )

what if *src < '0' ? isdigit() will catch that, but your if statement won't.

Thanks for the concern.

The scenario I'm talking about doesn't permit you to have any values other than 0-9 and A-F.

In other scenarios or to have a generic one I think using a isdigit() is a better option. But then also you can see what happens if any character as * ( ASCII value is 42) comes . Then you probably will have a negative value (42 - 64 +10 = -12).

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.