-1

I am working on code to get USB device details into single String, and have following code,

    struct usb_bus *bus;
    struct usb_device *dev;

    usb_init();
    usb_find_busses();
    usb_find_devices();

    for (bus = usb_busses; bus; bus = bus->next)
        for (dev = bus->devices; dev; dev = dev->next)
    {

            // working outputs
            printf("Trying device %s/%s\n", bus->dirname, dev->filename);
            printf("Trying device2 %0x\n", dev->descriptor.idVendor);
            printf("Trying device3 %0x\n", dev->descriptor.idProduct);

        char deviceDetailsStr[150]; 
        sprintf(deviceDetailsStr, "%s_%s_%0x_%0x", bus->dirname, 
           dev->filename,dev->descriptor.idVendor,dev->descriptor.idProduct);

        ... have other code here that works on "deviceDetailsStr"
    }

Been reading thatt "sprintf" has performance issues, since it supports lots of transforms.

Can you please suggest what is better alternative to using "sprintf", so that all 4 variables data gets read into variable "deviceDetailsStr"

End goal is "deviceDetailsStr" char array needs to have all 4 entires as single string.

Thanks

Mark
  • 11
  • 5
  • 4
    Are performance issues really the reason you want to replace `sprintf`? Are you sure this is your bottleneck? –  Jul 29 '16 at 17:12
  • `snprintf` is probably better than `sprintf`. –  Jul 29 '16 at 17:16
  • You could use strcat() multiple times. – FredK Jul 29 '16 at 17:40
  • 2
    `sprintf` (or, better, `snprintf`) is likely a bit slower than some alternatives because it has to parse and interpret the format string -- but that's likely to be orders of magnitude faster than communicating with the device. – Keith Thompson Jul 29 '16 at 18:05

2 Answers2

0

Step 1: Determine the maximum buffer size needs.

Assume bus->dirname, dev->filename are arrays.

#define Mark_SZ ((sizeof bus->dirname - 1) + 1 + \
    (sizeof dev->filename - 1) + 1 + \
    ((sizeof dev->descriptor.idVendor * CHAR_BIT + 3) /4) + 1 + \
    ((sizeof dev->descriptor.idProduct * CHAR_BIT + 3) /4) + 1)

#defined Extra (depends on: "other code here that works on "deviceDetailsStr"")
char deviceDetailsStr[Mark_SZ + Extra];

Step 2: Copy in each part

// Some untested code to give you an idea.

char *p = deviceDetailsStr;
size_t n = strlen(bus->dirname); 
memcpy(p, bus->dirname, n);
p += n;

*p++ = '_';

n = strlen(dev->filename); 
memcpy(p, dev->filename, n);
p += n;

*p++ = '_';

p += sprintf(p, "%x", dev->descriptor.idVendor);

*p++ = '_';

sprintf(p, "%x", dev->descriptor.idProduct);

I coded sprintf(p, "%x", dev->descriptor.idVendor) and sprintf(p, "%x", dev->descriptor.idProduct) by themselves as hoping even a modest compiler will recognize this and replace with the equivalent itoa() like function calls. Otherwise, simply code up a replacement unsigned to string.

I see no value with "0" in "%0x"

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
0

If you want the best performance, I would say you need to write something custom. Here's an example for your specific requirements.

uint32_t printHex( char * buffer, uint32_t value ) {
    uint32_t sz = value <= 0xF ? 1 :
                  value <= 0xFF ? 2 :
                  value <= 0xFFF ? 3 :
                  value <= 0xFFFF ? 4 :
                  value <= 0xFFFFF ? 5 :
                  value <= 0xFFFFFF ? 6 :
                  value <= 0xFFFFFFF ? 7 : 8;
    for( uint32_t i=sz-1; i; i-- ) {
        buffer[ i ] = ((value & 0xF) <= 9 ? '0' : 'a'-10 ) + (value & 0xF);
        value=value>>4;
    }
    return sz;
}

char buffer[150];
unsigned bi=0;
for( char * ptr = bus->dirname ; *ptr; ptr++ ) buffer[bi++] = *ptr;
buffer[bi++] = '_';
for( char * ptr = bus->filename; *ptr; ptr++ ) buffer[bi++] = *ptr;
buffer[bi++] = '_';
bi += printHex( buffer + bi, dev->descriptor.idVendor );
buffer[bi++] = '_';
bi += printHex( buffer + bi, dev->descriptor.idProduct );
buffer[bi] = '\0';

Note: There are no size checks (just like sprintf). Again if you want best performance, this is a trade off.

flu
  • 546
  • 4
  • 11
  • Thanks for the example. This is exactly what i was looking for. Just a small change. The output from printHex does not seem to match with output from printHex function. The first char seems to be printing wrong. **1) First Run:** **From sprintf :** |004_004_45e_750| **From New printHex Code :** |004_004_`5e_q50| 2) Second Run: From sprintf : |004_004_45e_750| From New printHex Code : |004_004_`5e_�50| Also printing special chars instead of regular ascii randomly for for first char. Can you please help fix this. Will mark this as selected answer. – Mark Jul 30 '16 at 05:26
  • Hi @Mark, I tried to reproduce the issue you're seeing with idVendor=1118 (0x45E) and idProduct=1872 (0x750). However, I wasn't able to, as the code above works every time, no random characters. I suspect maybe something else is causing the corruption. To verify, you can just call hexPrint() with any uint32_t, and it should match printf("%x"). – flu Jul 30 '16 at 22:49