tags:

views:

47

answers:

3

Hey,

I need to generate a path string from a number (in C)

e.g:

53431453 -> 0003/2F4/C9D

what I have so far is this:

char *id_to_path(long long int id, char *d)
{
    char t[MAX_PATH_LEN];
    sprintf(t, "%010llX", id);

    memcpy(d,   t,   4);
    memcpy(d+5, t+4, 3);
    memcpy(d+9, t+7, 4);

    d[4] = d[8] = '/';

    return d;
}

I'm wondering if there's a better way, e.g to generate the final string in one step instead of doing sprintf and then moving the bytes around.

Thanks

Edit:

I benchmarked the given solutions

results in operations per second (higher is better):

(1) sprintf + memcpy  : 3383005
(2) single sprintf    : 2219253 
(3) not using sprintf : 10917996

when compiling with -O3 the difference is even greater:

(1) 4422101
(2) 2207157
(3) 178756551

Since this function will be called a lot, I'll use the fastest solution even though the single sprintf is the shortest and most readable.

Thanks for your answers!

+2  A: 

Not tested, but you can split the int into three then print it:

char *id_to_path(long long int id, char *d)
{
    sprintf(d, "%04llX/%03llX/%03llX", ( id >> 24 ) & 0xffff, ( id >> 12 ) & 0xfff, id & 0xfff);

    return d;
}
Pete Kirkham
+1, but everybody here seems to assume that `long long` always is 32 bit. One probably shouldn't do that, or, if there is such an assumption use `int32t` for the prototype. Or even better use `uint32_t` since it seems that the semantic of the `id` is to be unsigned, just a bit pattern.
Jens Gustedt
@Jens you won't get 10 hex digits out of a 32 bit number. The assumption is that it's at least 40 bits, otherwise a 1 in the msb will be sign extended on the shift right. I would tend to use uint64_t to make it clear myself, but that's not the prototype given.
Pete Kirkham
A: 

Since the string uses hex, it can be quite easily done using shift and bit operators.

Getting the 4 highest bits from the value can be done like this:

id >> 28

Converting this to a digit simply means adding the character '0' to it, like this:

'0' + (id >> 28)

However, since A, B, C, ... don't immediately follow the character 9, we have to perform an additional check, something like:

if (c > '9') c = c - '9' - 1 'A'

If we want the next 4 bits, we should only shift 24 bits, but then we still have the highest 4 bits left, so we should mask them out, like this:

(id >> 24) & 0xf

If we pour this into your function, we get this:

char convert (int value)
{
char c = value + '0';
if (c > '9') c = c - '9' - 1 + 'A';
return c;
}

void main()
{
long id = 53431453;
char buffer[20];

buffer[0] = convert(id >> 28);
buffer[1] = convert((id >> 24) & 0xf);
buffer[2] = convert((id >> 20) & 0xf);
buffer[3] = convert((id >> 16) & 0xf);
buffer[4] = convert((id >> 12) & 0xf);
buffer[5] = convert((id >>  8) & 0xf);
buffer[6] = convert((id >>  4) & 0xf);
buffer[7] = convert((id >>  0) & 0xf);
buffer[8] = '\0';
}

Now adjust this to add the slashes in between, the extra zeroes in the beginning, ...

EDIT: I know this is not in one step, but it is better extensible if you later want to change the places of the slashes, ...

Patrick
A: 

Did you try this option yet?

typedef struct {
    unsigned f7 : 4;
    unsigned f6 : 4;
    unsigned f5 : 4;
    unsigned f4 : 4;
    unsigned f3 : 4;
    unsigned f2 : 4;
    unsigned f1 : 4;
    unsigned f0 : 4;
} lubf;

#define convert(a) ( a > 9 ? a + 'A' - 10 : a + '0' )

int main()
{
    lubf bf;
    unsigned long a = 0xABCDE123;
    memcpy(&bf, &a, sizeof(a));
    char arr[9];
    arr[0] = convert(bf.f0);
    arr[1] = convert(bf.f1);
    arr[2] = convert(bf.f2);
    arr[3] = convert(bf.f3);
    arr[4] = convert(bf.f4);
    arr[5] = convert(bf.f5);
    arr[6] = convert(bf.f6);
    arr[7] = convert(bf.f7);
    arr[8] = '\0';
    printf("%lX : %s\n", a, arr);
};
bergundy
yes, the memcpy made it a lot slower than the bitwise operations. a better way to do this is to use a union that has the bitfielded struct and the unsigned long, you assign the long, but can access the same data via the bitfielded struct. but again, the performance was still worse than the bitwise operations option.
miedwar
also, i'm not sure, but i think this solution has a portability problem because of bit ordering (endianness), and on a 64bit box sizeof(a) is 8, which is too big to copy to lubf.
miedwar