I am using the below prog to check the htonl and ntohl functions and in both the cases I am getting in little endian format ?
Why htonl is not converting the val to big endian format ?
the o/p of the below program is
LE
cdab
cdab
int main()
{
int val = 1;
char *c = (char*)&val;
short int a = 0xabcd;
if (*c)
printf("LE\n");
else
printf("BE\n");
printf("%x\n",htons(a));
printf("%x\n",ntohs(a));
return 0;
}
Related
Upgraded the gcc version from 4.6.3 to 8.2.0. The following function doesn't work without the memory barrier.
static u32 checksumPsedoHeader(u16 size, u32 src_addr, u32 dest_addr)
{
const u16 protocol_udp = 17;
u32 checksum = 0;
// Add UDP pseudo header.
struct PseudoHeader
{
unsigned int src;
unsigned int dst;
unsigned char zero;
unsigned char proto;
u16 length;
} pseudoHeader;
// Initalize the array. Using an uninitialized
// array is not acceptable by newer compiler versions.
memset((void*)&pseudoHeader, 0, sizeof(pseudoHeader));
pseudoHeader.dst = htonl(dest_addr);
pseudoHeader.src = htonl(src_addr);
pseudoHeader.zero = 0;
pseudoHeader.proto = protocol_udp;
pseudoHeader.length = htons(size);
u16 *pseudo_header_p = (u16*)&pseudoHeader;
asm volatile("" ::: "memory"); // The function doesn't work without this line after GCC upgration.
for (u32 i = 0; i < sizeof(pseudoHeader); i += 2)
{
checksum = checksum + *pseudo_header_p;
pseudo_header_p++;
}
return checksum;
}
Why does it need a memory barrier here? The pointer type conversion make the compiler did the wrong optimization(after the memory barrier, the for loop fetch the values from memories but all the values have not been flushed to the memories)?
I have a few for loops that does saturated arithmetic operations.
For instance:
Implementation of saturated add in my case is as follows:
static void addsat(Vector &R, Vector &A, Vector &B)
{
int32_t a, b, r;
int32_t max_add;
int32_t min_add;
const int32_t SAT_VALUE = (1<<(16-1))-1;
const int32_t SAT_VALUE2 = (-SAT_VALUE - 1);
const int32_t sat_cond = (SAT_VALUE <= 0x7fffffff);
const uint32_t SAT = 0xffffffff >> 16;
for (int i=0; i<R.length; i++)
{
a = static_cast<uint32_t>(A.data[i]);
b = static_cast<uint32_t>(B.data[i]);
max_add = (int32_t)0x7fffffff - a;
min_add = (int32_t)0x80000000 - a;
r = (a>0 && b>max_add) ? 0x7fffffff : a + b;
r = (a<0 && b<min_add) ? 0x80000000 : a + b;
if ( sat_cond == 1)
{
std_max(r,r,SAT_VALUE2);
std_min(r,r,SAT_VALUE);
}
else
{
r = static_cast<uint16_t> (static_cast<int32_t> (r));
}
R.data[i] = static_cast<uint16_t>(r);
}
}
I see that there is paddsat intrinsic in x86 that could have been the perfect solution to this loop. I do get the code auto vectorized but with a combination of multiple operations according to my code. I would like to know what could be the best way to write this loop that auto-vectorizer finds the addsat operation match right.
Vector structure is:
struct V {
static constexpr int length = 32;
unsigned short data[32];
};
Compiler used is clang 3.8 and code was compiled for AVX2 Haswell x86-64 architecture.
to convert a byte array from another machine which is big-endian, we can use:
long long convert(unsigned char data[]) {
long long res;
res = 0;
for( int i=0;i < DATA_SIZE; ++i)
res = (res << 8) + data[i];
return res;
}
if another machine is little-endian, we can use
long long convert(unsigned char data[]) {
long long res;
res = 0;
for( int i=DATA_SIZE-1;i >=0 ; --i)
res = (res << 8) + data[i];
return res;
}
why do we need the above functions? shouldn't we use hton at sender and ntoh when receiving? Is it because hton/nton is to convert integer while this convert() is for char array?
The hton/ntoh functions convert between network order and host order. If these two are the same (i.e., on big-endian machines) these functions do nothing. So they cannot be portably relied upon to swap endianness. Also, as you pointed out, they are only defined for 16-bit (htons) and 32-bit (htonl) integers; your code can handle up to the sizeof(long long) depending on how DATA_SIZE is set.
Through the network you always receive a series of bytes (octets), which you can't directly pass to ntohs or ntohl. Supposing the incoming bytes are buffered in the (unsigned) char array buf, you could do
short x = ntohs(*(short *)(buf+offset));
but this is not portable unless buf+offset is always even, so that you read with correct alignment. Similarly, to do
long y = ntohl(*(long *)(buf+offset));
you have to make sure that 4 divides buf+offset. Your convert() functions, though, don't have this limitation, they can process byte series at arbitrary (unaligned) memory address.
following is code of function
void printf(char *ch,void *num,...)
{
int i;
va_list ptr; //to store variable length argument list
va_start(ptr,num); // initialise ptr
for(i=0;ch[i]!='\0';i++)
{
if(ch[i]=='%') // check for % sign in print statement
{ i++;
if( ch[i]=='d')
{
int *no = (int *)va_arg(ptr,int * );
int value=*no; // just used for nothing
printno(value); //print int number
}
if( ch[i]=='u')
{
unsigned long *no =(unsigned long *) va_arg(ptr,unsigned long *);
unsigned long value=*no;
printuno(value); //print unsigned long
}
}
else // if not % sign then its regular character so print it
{
printchar(ch[i]);
}
}
}
this my code for printf() to print integer value and uint values
It is working fine for string portion in arguments but for %d %u it shows the same
values for all variables. This value is 405067 - even though the values of the variables are different.
Please tell me how to fix this.
Why are you interpreting the argument as a pointer? I'm surprised you aren't crashing. You should just be using
int num = va_arg(ptr,int);
printno(num);
and
unsigned int num = va_arg(ptr,unsigned int);
printuno(value);
(note, unsigned int, not unsigned long, because that would actually be %lu)
Also, get rid of the num parameter. It's wrong. Your va_list should be initialized as
`va_start(ptr, ch);`
va_start() takes the last argument before the varargs, not the first argument.
As noted in a comment, the C99 prototype for printf() is:
int printf(const char * restrict format, ...);
Therefore, if you're calling your function printf(), you should probably follow its design. I'm going to ignore flags, field width, precision and length modifiers, assuming that the conversion specifiers are simply two characters each, such as %d or %%.
int printf(const char * restrict format, ...)
{
va_list args;
va_start(args, format);
char c;
int len = 0;
while ((c = *format++) != '\0')
{
if (c != '%')
{
putchar(c);
len++;
}
else if ((c = *format++) == '%')
{
putchar(c);
len++;
}
else if (c == 'd')
{
int value = va_arg(args, int);
len += printno(value);
}
else if (c == 'u')
{
unsigned value = va_arg(args, unsigned);
len += printuno(value);
}
else
{
/* Print unrecognized formats verbatim */
putchar('%');
putchar(c);
len += 2;
}
}
return len;
}
Dealing with the full set of format specifiers (especially if you add the POSIX n$ notation as well as flags, field width, precision and length modifiers) is much harder, but this should get you moving in the correct direction. Note that I assume the printno() and printuno() functions both report how many characters were written for the conversion specifier. The function returns the total number of characters written. Note, too, that production code would need to allow for the called functions to fail, and would therefore probably not use the len += printno(value); notation, but would capture the return from printno() into a separate variable that could be tested for an error before adding it to the total length output.
Is there efficient way to do this?
That's something you could be using a union for:
union {
UINT64 ui64;
struct {
DWORD d0;
DWORD d1;
} y;
} un;
un.ui64 = 27;
// Use un.y.d0 and un.y.d1
An example (under Linix so using different types):
#include <stdio.h>
union {
long ui64;
struct {
int d0;
int d1;
} y;
} un;
int main (void) {
un.ui64 = 27;
printf ("%d %d\n", un.y.d0, un.y.d1);
return 0;
}
This produces:
27 0
Thought I would provide an example using LARGE_INTEGER FOR the windows platform.
If I have a variable called "value" that is 64 bit, then I do:
LARGE_INTEGER li;
li.QuadPart = value;
DWORD low = li.LowPart;
DWORD high = li.HighPart;
Yes, this copies it, but I like the readability of it.
Keep in mind that 64-bit integers have alignment restrictions at least as great as 32-bit integers on all platforms. Therefore, it's perfectly safe to cast a pointer to a 64-bit integer as a pointer to a 32-bit.
ULONGLONG largeInt;
printf( "%u %u\n", ((DWORD *)&largeInt)[ 0 ], ((DWORD *)&largeInt)[ 1 ] );
Obviously, Pax's solution is a lot cleaner, but this is technically more efficient since it doesn't require any data copying.