468,765 Members | 1,183 Online

# Endian Reversal?

What is the fastest way to convert __int64 type from BigEndian To Little
Endian?
I wrote this function:
---
uint64 EndianReverse(uint64 number) {
typedef unsigned __int8 uint8;
const int size = sizeof(uint64);
uint8 polje[size] = {0};
uint8 *ptr = reinterpret_cast<uint8*>(&number);
int index = 0;
for (int i=size-1 ; i>=0 ; i--) {
polje[index] = *(ptr+i);
index++;
}
uint64 *retPtr = reinterpret_cast<uint64*>(polje);
return *retPtr;
}
---

but i know there is faster way with logical and shift opertors, so if anyone
knows it i would be appreciated.
Mar 16 '06 #1
11 3835
Tosha wrote:
What is the fastest way to convert __int64 type from BigEndian To Little
Endian?
I wrote this function:
---
uint64 EndianReverse(uint64 number) {
typedef unsigned __int8 uint8;
const int size = sizeof(uint64);
uint8 polje[size] = {0};
uint8 *ptr = reinterpret_cast<uint8*>(&number);
int index = 0;
for (int i=size-1 ; i>=0 ; i--) {
polje[index] = *(ptr+i);
index++;
}
uint64 *retPtr = reinterpret_cast<uint64*>(polje);
return *retPtr;
}
---

but i know there is faster way with logical and shift opertors, so if anyone
knows it i would be appreciated.

The "fastness" has to be _measured_, not deduced from the types of
operations used. That said, I'd expect this to be a bit faster:

template<class T> T EndianReverse(T t)
{
unsigned char uc[sizeof t];
memcpy(uc, &t, sizeof t);
for (unsigned char *b = uc, *e = uc + sizeof(T) - 1; b < e; ++b, --e)
std::swap(*b, *e);
memcpy(&t, uc, sizeof t);
return t;
}

.... and it is (just measured), by about 20%, on my machine.

V
--
Mar 16 '06 #2
Tosha wrote:
What is the fastest way to convert __int64 type from BigEndian To Little
Endian?

<snip code>

Since it looks like you're on visual studio, how about trying the
_byteswap_uint64 intrinsic:

http://msdn2.microsoft.com/en-us/lib...77(VS.80).aspx

Mar 17 '06 #3

"Me" <an*****************@yahoo.com> wrote in message
Tosha wrote:
What is the fastest way to convert __int64 type from BigEndian To Little
Endian?

<snip code>

Since it looks like you're on visual studio, how about trying the
_byteswap_uint64 intrinsic:

http://msdn2.microsoft.com/en-us/lib...77(VS.80).aspx

Thank you very much.
Mar 17 '06 #4
Victor Bazarov wrote:
Tosha wrote:
What is the fastest way to convert __int64 type from BigEndian To Little
Endian?
The "fastness" has to be _measured_, not deduced from the types of
operations used. That said, I'd expect this to be a bit faster:

template<class T> T EndianReverse(T t)
{
unsigned char uc[sizeof t];
memcpy(uc, &t, sizeof t);
for (unsigned char *b = uc, *e = uc + sizeof(T) - 1; b < e; ++b, --e)
std::swap(*b, *e);
memcpy(&t, uc, sizeof t);
return t;
}

Why not do it inplace?

template<class T> void InplaceEndianReverse(T& t)
{
unsigned char* tp = reinterpret_cast<unsigned char *>(&t);
for (unsigned char *b = tp, *e = tp + sizeof(T) - 1; b < e; ++b, --e)
std::swap(*b, *e);
return;
}

Or saving at least one of the memcpy's in the out of place syntax?

template<class T> T EndianReverse(T t)
{
T result=t; // "hides" a memcpy.
unsigned char* tp = reinterpret_cast<unsigned char *>(&result);
for (unsigned char *b = tp, *e = tp + sizeof(T) - 1; b < e; ++b, --e)
std::swap(*b, *e);
return result;
}

Shouldn't this make a difference, especially if it is inlined?

Fabio

Mar 18 '06 #5
Fabio Fracassi wrote:
Victor Bazarov wrote:
Tosha wrote:
What is the fastest way to convert __int64 type from BigEndian To
Little Endian?
The "fastness" has to be _measured_, not deduced from the types of
operations used. That said, I'd expect this to be a bit faster:

template<class T> T EndianReverse(T t)
{
unsigned char uc[sizeof t];
memcpy(uc, &t, sizeof t);
for (unsigned char *b = uc, *e = uc + sizeof(T) - 1; b < e;
++b, --e) std::swap(*b, *e);
memcpy(&t, uc, sizeof t);
return t;
}

Why not do it inplace?

Because the behaviour in _your_ case is undefined. You're *not*
allowed to use the pointer obtained from 'reiterpret_cast' for
anything _except_ converting back to what it was.
template<class T> void InplaceEndianReverse(T& t)
{
unsigned char* tp = reinterpret_cast<unsigned char *>(&t);
for (unsigned char *b = tp, *e = tp + sizeof(T) - 1; b < e; ++b,
--e) std::swap(*b, *e);
return;
}

Or saving at least one of the memcpy's in the out of place syntax?

template<class T> T EndianReverse(T t)
{
T result=t; // "hides" a memcpy.
unsigned char* tp = reinterpret_cast<unsigned char *>(&result);
for (unsigned char *b = tp, *e = tp + sizeof(T) - 1; b < e; ++b,
--e) std::swap(*b, *e);
return result;
}

Shouldn't this make a difference, especially if it is inlined?

Oh yes, I'll be it does, but in 'comp.lang.c++' it's customary to
suggest only _proper_ solutions, whose behaviour is well-defined
according to the International Standard of the C++ language. Both

V
--
Mar 18 '06 #6
Victor Bazarov wrote:
Fabio Fracassi wrote:
Victor Bazarov wrote:
template<class T> T EndianReverse(T t)
{
unsigned char uc[sizeof t];
memcpy(uc, &t, sizeof t);
for (unsigned char *b = uc, *e = uc + sizeof(T) - 1; b < e;
++b, --e) std::swap(*b, *e);
memcpy(&t, uc, sizeof t);
return t;
}

Why not do it inplace?

Because the behaviour in _your_ case is undefined. You're *not*
allowed to use the pointer obtained from 'reiterpret_cast' for
anything _except_ converting back to what it was.

And what exactly is the point in converting it back and forth, without doing
anything in between?
Besides, the call to memcpy does two implicit casts, one from (T*) to
(void*) and one from (void*) to (unsigned char*), which is what allows you
to interpret the data as a series of bytes. Effectively your doing an
implicit cast from (T*) to (unsigned char*) with the memcpy. AFAIK,
reinterpret_cast does the same, albeit without the copying.
template<class T> void InplaceEndianReverse(T& t)
{
unsigned char* tp = reinterpret_cast<unsigned char *>(&t);
for (unsigned char *b = tp, *e = tp + sizeof(T) - 1; b < e; ++b,
--e) std::swap(*b, *e);
return;
}
[sniped one simmilar example] Shouldn't this make a difference, especially if it is inlined?

Oh yes, I'll be it does, but in 'comp.lang.c++' it's customary to
suggest only _proper_ solutions, whose behaviour is well-defined
according to the International Standard of the C++ language. Both

I think my example's have exactly the same behaviour as yours. We both let
the compiler interpret a typed pointer as another typed pointer. This might
be implementational defined behaviour, but I don't see a way around this.

Fabio

Mar 19 '06 #7
Fabio Fracassi wrote:
Victor Bazarov wrote:
Fabio Fracassi wrote:
Victor Bazarov wrote:

template<class T> T EndianReverse(T t)
{
unsigned char uc[sizeof t];
memcpy(uc, &t, sizeof t);
for (unsigned char *b = uc, *e = uc + sizeof(T) - 1; b < e;
++b, --e) std::swap(*b, *e);
memcpy(&t, uc, sizeof t);
return t;
}

Why not do it inplace? Because the behaviour in _your_ case is undefined. You're *not*
allowed to use the pointer obtained from 'reiterpret_cast' for
anything _except_ converting back to what it was.

And what exactly is the point in converting it back and forth, without doing
anything in between?

If there's no point, then do not do it.
Besides, the call to memcpy does two implicit casts, one from (T*) to
(void*) and one from (void*) to (unsigned char*),
No. The call converts to void* only.
Anything else happens inside the implementation of memcpy.
And memcpy is an implementation problem.

Sure, most implementations will do what you say. But it is not guaranteed.
to interpret the data as a series of bytes. Effectively your doing an
implicit cast from (T*) to (unsigned char*) with the memcpy. AFAIK,
reinterpret_cast does the same, albeit without the copying.
That is what happens on most implementations, and on most
implementations your code actually does work.

Still there's nothing in the language definition that makes it
guaranteed. If I write a compiler that will do things differently, it
will still be a perfectly legal compiler.
I think my example's have exactly the same behaviour as yours. We both let
the compiler interpret a typed pointer as another typed pointer. This might
be implementational defined behaviour, but I don't see a way around this.

No. That is what your code tries to do. And is not legal.
His code converts to void* and calls memcpy. What happens inside memcpy
is that memory content is copyed. Without any type cast. The cast inside
memcpy is that of a magic spell.
Mar 19 '06 #8
AnalogFile wrote:
Fabio Fracassi wrote:
Victor Bazarov wrote:
Fabio Fracassi wrote:
Victor Bazarov wrote:

> template<class T> T EndianReverse(T t)
> {
> unsigned char uc[sizeof t];
> memcpy(uc, &t, sizeof t);
> for (unsigned char *b = uc, *e = uc + sizeof(T) - 1; b < e;
> ++b, --e) std::swap(*b, *e);
> memcpy(&t, uc, sizeof t);
> return t;
> }
>
Why not do it inplace?
Because the behaviour in _your_ case is undefined. You're *not*
allowed to use the pointer obtained from 'reiterpret_cast' for
anything _except_ converting back to what it was.

And what exactly is the point in converting it back and forth, without
doing anything in between?

If there's no point, then do not do it.

What is the point of having it in the language then? And how could I solve
such problems without relying on undefined behaviour?
Besides, the call to memcpy does two implicit casts, one from (T*) to
(void*) and one from (void*) to (unsigned char*),

No. The call converts to void* only.
Anything else happens inside the implementation of memcpy.

I don't think so. memcpy doesn't know anything about types, and does no
casting whatsoever. Its signature is "void* memcpy(void* dest, const void*
src, size_t n);". So if someone uses a typed pointer in memcpy's dest
parameter there is an implicit cast from void* to the type which was used.

As long as the cast doesn't change the bit pattern of the pointed to values
this operation is save.

Fabio

Mar 20 '06 #9
Fabio Fracassi wrote:
AnalogFile wrote:
Fabio Fracassi wrote:
Victor Bazarov wrote:

Fabio Fracassi wrote:
> Victor Bazarov wrote:
>
>> template<class T> T EndianReverse(T t)
>> {
>> unsigned char uc[sizeof t];
>> memcpy(uc, &t, sizeof t);
>> for (unsigned char *b = uc, *e = uc + sizeof(T) - 1; b < e;
>> ++b, --e) std::swap(*b, *e);
>> memcpy(&t, uc, sizeof t);
>> return t;
>> }
>>
> Why not do it inplace?
Because the behaviour in _your_ case is undefined. You're *not*
allowed to use the pointer obtained from 'reiterpret_cast' for
anything _except_ converting back to what it was.
And what exactly is the point in converting it back and forth, without
doing anything in between? If there's no point, then do not do it.

What is the point of having it in the language then?

I wrote "if" there's no point. It's in the language for those cases
where there actually is a point.
And how could I solve
such problems without relying on undefined behaviour?
Use the system library.
Besides, the call to memcpy does two implicit casts, one from (T*) to
(void*) and one from (void*) to (unsigned char*),

No. The call converts to void* only.
Anything else happens inside the implementation of memcpy.

I don't think so. memcpy doesn't know anything about types, and does no
casting whatsoever.

memcpy is part of the system and what it does or does not you cannot
say, except for the documented side effect of copying underlying bytes
from source to destination.
Its signature is "void* memcpy(void* dest, const void*
casting whatsoever. Its signature is "void* memcpy(void* dest, const void*
src, size_t n);". So if someone uses a typed pointer in memcpy's dest
parameter there is an implicit cast from void* to the type which was used.

sorry. I do not understand the above.

Mar 20 '06 #10
AnalogFile wrote:
Fabio Fracassi wrote:
memcpy is part of the system and what it does or does not you cannot
say, except for the documented side effect of copying underlying bytes
from source to destination.
Its signature is "void* memcpy(void* dest, const void*
casting whatsoever. Its signature is "void* memcpy(void* dest, const
void* src, size_t n);". So if someone uses a typed pointer in memcpy's
dest parameter there is an implicit cast from void* to the type which was
used.

sorry. I do not understand the above.

I'm just saying that memcpy is not resposible for the reinterpreting of the
pointer types, but that it happens implicitly at the function call, i.e

T1* t1p;
T2* t2p;
memcpy (t2p, t1p, sizeof(T1));

is equivalent to:

T1* t1p;
T2* t2p;

void * v1p = (void*) t1p;
void * v2p = (void*) t2p;
memcpy (v2p, v1p, sizeof(T1));

After writeing this I see the subtle difference between this way an what I
have written, because in this case the "backward" cast from (void*) to

Now does the standard really not guarantee that the pointed to memory is
left alone when I do an reinterpret_cast? Could you point me the right
section where I could look this up?

I'm not trying to be argumentative, but I can't belive that something as
basic as an inplace endianess reversal can't be done without resorting to
undefined behaviour or system specific libraries. If it is I consider this
a Defect.

Fabio

Mar 21 '06 #11
Fabio Fracassi wrote:
Its signature is "void* memcpy(void* dest, const void*
casting whatsoever. Its signature is "void* memcpy(void* dest, const
void* src, size_t n);". So if someone uses a typed pointer in memcpy's
dest parameter there is an implicit cast from void* to the type which was
used. sorry. I do not understand the above.

I'm just saying that memcpy is not resposible for the reinterpreting of the
pointer types, but that it happens implicitly at the function call, i.e

T1* t1p;
T2* t2p;
memcpy (t2p, t1p, sizeof(T1));

is equivalent to:

T1* t1p;
T2* t2p;

void * v1p = (void*) t1p;
void * v2p = (void*) t2p;
memcpy (v2p, v1p, sizeof(T1));

After writeing this I see the subtle difference between this way an what I
have written, because in this case the "backward" cast from (void*) to

Neither of the above sequences ever does any backward cast from void*
They in fact are perfectly equivalent.
Now does the standard really not guarantee that the pointed to memory is
left alone when I do an reinterpret_cast? Could you point me the right
section where I could look this up?
I'm again not sure I understand you.
If I get it right then, the "pointed to" memory does not change. Not at
the time of the cast.
I'm not trying to be argumentative, but I can't belive that something as
basic as an inplace endianess reversal can't be done without resorting to
undefined behaviour or system specific libraries. If it is I consider this
a Defect.

If you think hard at it you realize that the very concept of "endianness
reversal" is totally system dependent.

You are assuming that the system does have some sort of endianness to
begin with. Like a 4 bytes value is either stored as 1234 or 4321. And
probably you also assume values to be 2s complement. But as far as the
language is concerned it may be 1423 and not be 2s complement!

Mar 22 '06 #12

### This discussion thread is closed

Replies have been disabled for this discussion.