Char[4] to UInt32

Started by
15 comments, last by LostAndConfused 17 years, 9 months ago
Quote:Original post by samuraicrow
If you're using SDL then use its built-in endian swapper/reader:

*** Source Snippet Removed ***

or something similar. SDL's endian swapping routines are optimized for maximum performance under most processors and use inline assembly to accomplish this feat.


That's quite broken. For one thing, you're C-style casting. For another, you're casting the array address (char*) to the int, rather than a pointer thereto. You'll be swapping the bytes of *the location in memory* of the array, rather than the content bytes.

Anyway.

#ifdef SWAP_ENDIANtemplate <typename POD>void fill_from_buffer(char* buffer, POD& target) {  typedef reverse_iterator<char*, char> ri;  std::fill(ri(buffer), ri(buffer + sizeof(POD)),             reinterpret_cast<char*>(&target));}#elsetemplate <typename POD>void fill_from_buffer(char* buffer, POD& target) {  std::fill(buffer, buffer + sizeof(POD), reinterpret_cast<char*>(&target));}#endiftemplate <typename POD>POD create_from_buffer(char* buffer) {  POD result;  fill_from_buffer(buffer, result);  return result;}


That is, instead of trying to interpret the few bytes as an int, interpret the location of the int as a place where bytes can be written.
Advertisement
You can do this for copying from buffer at x.

	union {		char c[4];		Uint32 i;	} swap;		swap.c[3] = buffer[x];	swap.c[2] = buffer[x + 1];	swap.c[1] = buffer[x + 2];	swap.c[0] = buffer[x + 3];		fprintf(stderr, "%d\n", swap.i);


And you can add some defines so it works for both endian types on compile. It's standard C???????
The union is a common way about this, too, but there is a big surprise in the standard:

You are allowed ONLY to read from the union member you have written into, otherwise undefined behaviour is invoked.

There was a thread recently in, I think, comp.std.c++ titled "irrational rules" where this was discussed.

Google (groups):
"irrational rules" union
Reinterpreting bits is always tricky in terms of getting it right for multiple platforms. Doing this:

char foo[234];
char *bar = foo[30 + (rand() % 100)];
int *baz = (int *)bar;
int ick = *baz;

May work. It may fault on some processors. It may work, but be slow on others. As some others have mentioned, if the endian-ness of the CPU doesn't match the endian-ness of the CPU that wrote out the bytes, you can have problems.

I'd recommend avoiding this approach entirely. I'd say it's better to use complete structures where possible. For example:

struct rPackedFile
{
u8 signature[4];
rDirectory *directory;
};

struct rFile
{
u8 *fileName;
u8 *fileContents;
u32 fileLength;
};

struct rDirectory
{
u32 fileCount;
rFile files[];
};

Then, you'd read your file in, convert a bunch of offsets to pointers, do one cast at the beginning of the file, and then never have to do nasty casting again - just follow your type pointers around. You can also push any endian issues into your writer, so your reader code never has to think about it. You can manually insert any padding necessary into your structures, and have inter-structure padding happen automatically by your writer.

This also has the advantage of being wicked fast at runtime.
why not just use atol() ??

unsigned int iTmp = (unsigned int) atol( &buffer );

Is this to much "old timer"? lol
Quote:Original post by LostAndConfused
why not just use atol() ??

unsigned int iTmp = (unsigned int) atol( &buffer );

Is this to much "old timer"? lol


Wrong data conversion - he wasn't asking how to convert a string of 4 characters representing a number (like "165") to an integral value. He was asking how to reinterpret bits that were in a stream of bytes into a fixed-size integral value.
Ahhhhh !!! -10 for the old timer... LOL

This topic is closed to new replies.

Advertisement