Return Styles: Pseud0ch, Terminal, Valhalla, NES, Geocities, Blue Moon. Entire thread

struct sockaddr rant

Name: Anonymous 2012-12-27 1:50

When I look in netinet/in.h on my system it's defined with the type uint32_t. Over here (http://www.beej.us/guide/bgnet/output/html/multipage/sockaddr_inman.html) it's defined as an unsigned long. Why wouldn't they do what they did with IPv6 addresses and just using an array of chars? If they're already using the assumption that CHAR_BIT is 8 for IPv6, why carry the extra, unnecessary assumption that the implementation provides a 32-bit integer type?

The only reason I can really think of is to ensure portability to platforms that use larger chars, but since they aren't going to work with IPv6 structs, why not redesign IPv4 structs?

And if we're fixing that, why not change the following functions:

uint32_t htonl(uint32_t hostlong);
uint32_t ntohl(uint32_t netlong);


to something like:

void *htonl(void *dest, unsigned long val);
unsigned long ntohl(const void *src);


/end rant

Name: Anonymous 2012-12-27 3:30

Because all programs that have something to do with network depend on these APIs.

Name: Cudder !MhMRSATORI!fR8duoqGZdD/iE5 2012-12-27 5:04

Pronounced 'sock-adder'...

Why wouldn't they do what they did with IPv6 addresses and just using an array of chars?
Because you'd have to rely on the compiler somehow being able to figure out that it can manipulate the whole thing as a unit; it's also easier to do netmasking etc. IPv6 was not designed as well, they should've used two 64-bit pieces.

Name: Anonymous 2012-12-27 5:05

So they fucking made a mistake when they defined the API for IPV4.

Have a peanut, don't stick it too far up your rectum.

Name: Anonymous 2012-12-27 7:05

>>3

Well, if you want to write fast code for an implementation that supports uint32_t, you could simply cast it to a pointer of that type then dereference it. A portable method would be to convert the array of four chars to an unsigned long in the host format (which has a minimum guaranteed range that allows a 32-bit integer to fit nicely into it) then to convert it back before you push it down the wire.

Even with the minor benefits of being able to treat it as a single arithmetic type, it requires any C implementation providing the same library to support uint32_t (or uint64_t if they were to define the IPv6 address as two objects of that type).

Name: Anonymous 2012-12-27 7:38

The ``Pimpl'' idiom

Name: Cudder !MhMRSATORI!fR8duoqGZdD/iE5 2012-12-27 8:11

>>5
An array of char can be aligned on any address, so you're not guaranteed that a cast-and-dereference will work (fuck architectures that don't support unaligned access, but just for the sake of argument...)

it requires any C implementation providing the same library to support uint32_t
uint32_t is defined in C99, and C89 has long which must be at least 32 bits.

Name: Anonymous 2012-12-27 8:18

>>7
uint32_t is defined in C99, and C89 has long which must be at least 32 bits.
The fixed width types in stdint.h are optional.

Name: Anonymous 2012-12-27 11:00

>>7
An array of char can be aligned on any address, so you're not guaranteed that a cast-and-dereference will work
I guess, but if it does you can always use that under the assumption that it will be faster. If you stick with the portable method (where you convert to the host format if you need to perform operations on the type as a whole), the only assumption required for an implementation of htonl/ntohl that works with void *s is CHAR_BIT == 8.

Name: Anonymous 2012-12-27 11:32

>>1
assuming void* is 32 bit or wider
Also you're shit at designing APIs.

Name: Anonymous 2012-12-27 11:50

If the language of choice doesn't sort out platform dependent shit, and you feel you need to sort this shit out, why the fuck do you still use that language?

Name: Anonymous 2012-12-27 11:55

>>10
Where do I assume that? The implementation for both of those functions would probably be something like this:


void *htonl(void *dest, unsigned long val)
{
    unsigned char *ptr = dest;

    ptr[0] = val >> 24;
    ptr[1] = val >> 16;
    ptr[2] = val >>  8;
    ptr[0] = val;
    return dest;
}

unsigned long ntohl(const void *src)
{
    const unsigned char *ptr = src;

    return ptr[0] << 24 | ptr[1] << 16 | ptr[2] << 8 | ptr[3];
}

As I mentioned in >>9, the only assumption made about the host environment is CHAR_BIT == 8.

Name: 12 2012-12-27 11:56

ptr[3] = val;*

Name: Anonymous 2012-12-28 10:38

Congratulations, your pointless pointer bullshit has just turned a function that cannot fail into a potential crash source.

Name: Anonymous 2012-12-28 10:52

>>14
More importantly, it allows you to store 32-bit integers in the big-endian byte order on systems that don't provide a 32-bit type.

If you're too useless to call the function correctly then you should probably go back to using Java or VB.

Name: Anonymous 2012-12-28 11:11

>>12
You're making size assumptions, and you can't dereference void pointers. Your solution will not work. Second, if you look in bits/byteswap.h (included from netinet/in.h) you'll see that on i486 and AMD64 processors it's implemented with single instruction inline assembly.

Name: Anonymous 2012-12-28 11:25

>>16
You're making size assumptions
The only assumption I'm making is that chars are 8 bits. If that assumption is true, then you can portably send a 32-bit integer with the network byte order like so:

unsigned char buf[4];

send(fd, htonl(buf, 123456), sizeof buf, 0);


This method makes absolutely no assumptions about the size of short, int, or long, except for the minimum guaranteed limits specified by the C standard.

and you can't dereference void pointers.
That's why the value is converted to a pointer to unsigned char within the body of the function (which is safe since both types are guaranteed to have the same internal representation). Functions like memcpy are designed in a similar way.

Name: Anonymous 2012-12-28 11:25

>>15
An unsigned long int must be at least 32 bits wide. Furthermore, a conforming hosted C99 compiler implementation must provide the uint_least32_t and uint_fast32_t types.

Name: Anonymous 2012-12-28 11:53

>>18
None of those types are guaranteed to be 32-bit types. They could all be 128-bit types, for instance. If they were, then the method I'm suggesting would work (if chars were 8 bits), and the current definitions of ntohl and htonl would be broken since you'd be unable to reliably send out a 32-bit integer. This is basically my main argument.

Name: Anonymous 2012-12-28 12:17

int32_t should be 32 bit long always, ne

Name: Anonymous 2012-12-28 12:31

>>20
ballpoint pen might be tree trunk hat never, en?

Name: Anonymous 2012-12-28 12:39

>>17
Sorry, just woke up when I wrote that and I overlooked the pointer conversion. Regardless, you're taking something that's a one-register inline assembly instruction and turning it into memory laden garbage. Imagine if you're missing cache the first time through as well; now you've replaced a 1 cycle instruction with several multicycle cache fetches AND the initial 400+ cycles of memory latency. Way to fucking go. Also, considering that htonl/ntohl and htons/ntohs are defined in terms of uint32_t and uint16_t, where the hell is the precision problem?

Name: Anonymous 2012-12-28 13:39

>>22
Those are implementation issues. When you're writing low level code you can sacrifice portability for speed in many situations. That's a not a very good sacrifice to make when you're writing interfaces for these libraries that are included by programs which are meant to be portable.

Also, considering that htonl/ntohl and htons/ntohs are defined in terms of uint32_t and uint16_t, where the hell is the precision problem?
I'm not sure what you mean by that. The point is that uint32_t and uint16_t needn't be supported by the C implementation. In >>19 I was assuming those functions would be implemented with unsigned long and unsigned short. Obviously they couldn't be implemented without a 32-bit or 16-bit integer type.

Name: Anonymous 2012-12-28 13:42

>>23
Obviously they couldn't be implemented without a 32-bit or 16-bit integer type.
Why not?

Name: Anonymous 2012-12-28 13:48

>>23
The hton? and ntoh? "functions" are in reality implemented as C preprocessor macros in most implementations. If you're dissatisfied with the old arpa/ junk, why not just use the BSD stuff?
#define _BSD_SOURCE             /* See feature_test_macros(7) */
#include <endian.h>

uint16_t htobe16(uint16_t host_16bits);
uint16_t htole16(uint16_t host_16bits);
uint16_t be16toh(uint16_t big_endian_16bits);
uint16_t le16toh(uint16_t little_endian_16bits);

uint32_t htobe32(uint32_t host_32bits);
uint32_t htole32(uint32_t host_32bits);
uint32_t be32toh(uint32_t big_endian_32bits);
uint32_t le32toh(uint32_t little_endian_32bits);

uint64_t htobe64(uint64_t host_64bits);
uint64_t htole64(uint64_t host_64bits);
uint64_t be64toh(uint64_t big_endian_64bits);
uint64_t le64toh(uint64_t little_endian_64bits);

Name: Anonymous 2012-12-28 13:51

uint64_t htonl(uint64_t);
If you change the types to uint64_t, you'd be sending out 8 bytes, like so:

uint64_t n = htonl(123456);

send(fd, &n, sizeof n, 0);


If you change the call to send to this:

send(fd, &n, 4, 0);

Then how do you know the data was moved to the beginning of the object?

Name: Anonymous 2012-12-28 13:57

>>25
Those have the same problem though. The only difference is that it adds functions for handling data in the little endian byte order, as well as 64-bit integers. I'll challenge you to write functions that pack and unpack a 24-bit integer in the same manner as those functions do.

Name: 27 2012-12-28 13:58

(without padding)

Name: Anonymous 2012-12-28 14:22

>>27,28
But... you can't necessarily write generic endianness correction functions, even when you do know the size of the integer type. For big/little endian it's easy (when you know the size), but using a byte array to return the corrected integer will still put it at an offset if you're trying to use smaller types than the one provided. e.g. you convert an LE 24-bit integer to BE and the unsigned long is 32 bits wide, your data will start at byte_array[1] with byte_array[0]=0. Example:
uint24_t u = 0xAABBCC;
uint8_t netbuf[512];
htonl(netbuf, u);
/*
Memory seen as LTR

Memory layout of u: CCBBAA
Memory layout of u when converted to ulong: CCBBAA00
Data in netbuf: 00AABBCC
*/

And now you have zero padding that you need to remove. If you think you can overcome this, please explain how you can get any endianness to conform, no matter how silly (see middle endian for retarded stuff that sadly needs support).

Personally I use tightly packed (not standard, I know) structs and unions when preparing data for sending. My problem is entirely different but solvable. There are no signed conversion functions, but those can be made.

Name: Anonymous 2012-12-28 14:34

And now you have zero padding that you need to remove. If you think you can overcome this, please explain how you can get any endianness to conform, no matter how silly (see middle endian for retarded stuff that sadly needs support).

Uhh, it's actually quite trivial with the method I suggested. If you want to change it to a middle endian format, just switch the array indices around in the hton* and ntoh* functions.


#include <stdio.h>

void *hton24(void *dest, unsigned long val)
{
    unsigned char *ptr = dest;

    ptr[0] = val >> 16;
    ptr[1] = val >> 8;
    ptr[2] = val;
    return dest;
}

unsigned long ntoh24(const void *src)
{
    const unsigned char *ptr = src;

    return ptr[0] << 16 | ptr[1] << 8 | ptr[2];
}

void printbyte(const void *ptr, size_t n)
{
    const unsigned char *bp;

    for (bp = ptr; n--; bp++)
        printf("%02x ", *bp);
    puts("");
}

int main(void)
{
    unsigned long int x = 0xaabbcc;
    unsigned char buf[3];

    printf("host: ");
    printbyte(&x, sizeof x);
    printf("net: ");
    printbyte(hton24(buf, x), sizeof buf);
    x = ntoh24(buf);
    printf("host: ");
    printbyte(&x, sizeof x);
}

Name: Anonymous 2012-12-28 14:50

>>29
you convert an LE 24-bit integer to BE and the unsigned long is 32 bits wide
I think you're under the impression that the C environment takes the endianness of the host environment into account. It doesn't since '1 << 1' will yield the value '2' in every single conforming implementation. Think of it like this:

ptr[0] = val / 65536;
ptr[1] = val / 256;
ptr[0] = val;


ptr[0] * 65536 + ptr[1] * 256 + ptr[0]

This is exactly the same as what I wrote in the previous post. a << x is equivalent to a dividied by (two raised to the power of x).

Name: Anonymous 2012-12-28 14:51

>>31
s/divided by/multiplied by/

Name: Anonymous 2012-12-28 14:53

>>31
Oh, and change the last indices in both examples to 2. I don't know why I keep messing that up.

Name: Anonymous 2012-12-28 15:17

>>30-33
Stop making it sound like you can magically conjure up arbitrary endianness switching functions if all you really mean is that there aren't integer types for every possible integer size.

I'm perfectly well aware of how C handles integers when stored as integer variables. We're talking about memory layouts, not registers. Your integers do however have an associated memory layout. If you alias your types a little, i.e. (uint8_t *)&my32bitint, you'll see the byte order.

It's not hard to make a 24-bit integer conversion though. Do the exact same thing as >>30 but return it in a 32-bit integer aligned to the byte index 0. Then again, if you really want these functions I suggest you write them in whatever way you find most convenient, but use the provided endianness functions from endian.h to do the conversion, because, and I can't stress this enough, THEY ARE SINGLE CYCLE INSTRUCTIONS. Also, use uint8_t instead of unsigned char, or typedef your own.

Name: Anonymous 2012-12-28 15:50

>>34
Stop making it sound like you can magically conjure up arbitrary endianness switching functions if all you really mean is that there aren't integer types for every possible integer size.

That function, hton24, converts an unsigned long value to a 24-bit big-endian representation, suitable for sending across a network or storing into a file. It does nothing more than that. If you want to treat it as a single unit, then you can convert it back with ntoh24. If you consider that magic, then I suppose it's magical. Otherwise, it was never my intention to claim that it does things it doesn't.

Do the exact same thing as >>30 but return it in a 32-bit integer aligned to the byte index 0.

I'm quite certain that any values stored in padding bits may be freely discarded in assignments by the C implementation.

and I can't stress this enough, THEY ARE SINGLE CYCLE INSTRUCTIONS

I'll keep that in mind if those functions happen to produce a significant bottleneck in any of my programs.

Also, use uint8_t instead of unsigned char, or typedef your own.

uint8_t was introduced in C99. If unsigned char isn't 8 bits, uint8_t isn't available. If uint8_t is available, unsigned char is 8 bits. It's impractical to use it; you have to include stdint.h every time you want to, and there's a chance your compiler won't support it if they don't support the later standards. I write code so that unsigned char implies an 8-bit quantity. That really is just a matter of preference.

Name: Anonymous 2012-12-28 15:56

wow op i fap so hard my dick bigger 2 inches great vid

Name: Anonymous 2012-12-28 15:59

>>36
o yeaa

Name: Anonymous 2012-12-28 17:00

>>35
So for you who is clearly incapable of reading and comprehending, here's how endianness works: It's only well defined for byte streams that have a power of two length. First you complain that the network order functions don't store their shit into a byte array because "what if the implementation doesn't have a 32-bit integer type".
Barring that 32-bit integer types are available, or you're on a system that doesn't have any reason to communicate over your average network protocol, it then turns into: "oh but you can't do it for arbitrary sizes with the arpa functions", which is an asinine statement as you might not even be able to write generic functions to switch endianness, as endianness can be whatever the ISA designer wanted it to be. However this just seemed to be a misunderstanding. What you actually meant was "there are no 24-bit integer types (for example), so a byte array would be better".
Given that endianness is still only well defined for 2^n length sequences of bytes, and unless you have a system where you have defined your own conventions you won't see these, why the living fuck should the absolutely ancient functions hton? and ntoh? support your system?

If you have definitions for non-pow2 endianness I'd like to have them though. I'm especially curious of how middle endian looks.

And it's not about those functions being a bottleneck. It's about using that ridiculous CISC processor of yours the way it was intended to be used.

Name: Anonymous 2012-12-28 19:26

Fuck it, every platform of significance these days is little-endian anyway.

Name: Anonymous 2012-12-28 19:42

>>35
Your problem is that you're taking a function designed for endian conversion and shoehorning it into serializing data.

>>39
Power.

Newer Posts
Don't change these.
Name: Email:
Entire Thread Thread List