Re: [dev] network protocol packing
See also: https://kentonv.github.io/capnproto/
Re: [dev] network protocol packing
Rob wrote: You've got alignment issues here - msg will be aligned to support any type (as malloc's interface specifies) so msg+1 will most likely be on an odd address, one byte off a highly aligned address. This means if your struct contains anything other than chars, you'll have UB. This is fine on x86, which allows unaligned access with a performance penalty but on something like an ARM machine you'll have issues. You probably want to memcpy the struct in from an existing one. Heyho Rop, thanks for your feedback. What about declaring a struct for each message-type: struct msg_signed_data { unsigned int op; struct foo data; struct bar signature; }; This should also solve the alignment issues, or am I mistaken here? It also reduces the amount of memcpy. --Markus
Re: [dev] network protocol packing
On Tue, Jul 1, 2014 at 2:56 PM, Markus Teich markus.te...@stusta.mhn.de wrote: thanks for your feedback. What about declaring a struct for each message-type: struct msg_signed_data { unsigned int op; struct foo data; struct bar signature; }; This should also solve the alignment issues, or am I mistaken here? It also reduces the amount of memcpy. Is this skipping the padding or including it now? Including it would mean there would be more to memcpy? if you want to look at struct paddings, here we go... [0] cheers! mar77i [0] https://gist.github.com/mar77i/b2cbe1a6c0c2194d7804
Re: [dev] network protocol packing
On Tue, Jul 01, 2014 at 01:56:04PM +0200, Markus Teich wrote: struct msg_signed_data { unsigned int op; struct foo data; struct bar signature; }; If this is data that goes across the network then instead of directly mapping a struct on that data I'd simply have functions that create a struct out of that data. The format of the data on the wire would be specified somewhere. So the data would arrive as a series of bytes and whenever my packet parsing function would indicate that a valid packet has been seen, it would extract data from that ring buffer and map it onto a struct. The particular layout of that struct could be different across different architectures/ABIs in order to meet alignment requirements etc. Parsing valid packets for further processing could be done with a table of function pointers indexed by the packet type. You'd expect to access the struct via functions in the above case. This is not specific to data across a network, that's just used as an example.
Re: [dev] network protocol packing
Rob wrote: You've got alignment issues here - msg will be aligned to support any type (as malloc's interface specifies) so msg+1 will most likely be on an odd address, one byte off a highly aligned address. This means if your struct contains anything other than chars, you'll have UB. This is fine on x86, which allows unaligned access with a performance penalty but on something like an ARM machine you'll have issues. Heyho, so if every field in the message is a multiple of 4 bytes long, this should fix the problem? In this case I would just make op an uint32_t and htonl it before sending, ntohl after receiving. --Markus
Re: [dev] network protocol packing
On Tue, Jul 01, 2014 at 05:01:43PM +0200, Markus Teich wrote: Rob wrote: You've got alignment issues here - msg will be aligned to support any type (as malloc's interface specifies) so msg+1 will most likely be on an odd address, one byte off a highly aligned address. This means if your struct contains anything other than chars, you'll have UB. This is fine on x86, which allows unaligned access with a performance penalty but on something like an ARM machine you'll have issues. Heyho, so if every field in the message is a multiple of 4 bytes long, this should fix the problem? In this case I would just make op an uint32_t and htonl it before sending, ntohl after receiving. Do not assume that. It is wrong in the general case.
Re: [dev] network protocol packing
On Tue, Jul 1, 2014 at 8:01 AM, Markus Teich markus.te...@stusta.mhn.de wrote: Rob wrote: You've got alignment issues here - msg will be aligned to support any type (as malloc's interface specifies) so msg+1 will most likely be on an odd address, one byte off a highly aligned address. This means if your struct contains anything other than chars, you'll have UB. This is fine on x86, which allows unaligned access with a performance penalty but on something like an ARM machine you'll have issues. Heyho, so if every field in the message is a multiple of 4 bytes long, this should fix the problem? In this case I would just make op an uint32_t and htonl it before sending, ntohl after receiving. It may be worth taking a look at capnproto as I mentioned above if you haven't yet. It's taken a very similar approach to the problem, and come up with a rather low-suck solution. Messages are all aligned to 64-bit words and sent that way over the wire, with an optional lightweight packing format to reduce message sizes. There are C bindings in a mostly-working state. You don't need to touch C++ except to compile their protocol format, which you shouldn't have to do most of the time. The only thing from your strawman that capnproto doesn't do directly in a similarly simple manner is message signing, and it's not hard to imagine adding it. That said, I think your strawman is great, and I'm not trying to shoot it down -- just pointing in the direction of similar work that might be inspiring or might save you some effort. --Markus
Re: [dev] network protocol packing
On Mon, Jun 30, 2014 at 08:54:52PM +0200, Markus Teich wrote: Heyho, since I did not find any suckless project regarding this issue, I would like to ask you guys for some feedback: unsigned char *msg; size_t msg_size; struct foo *msg_data; struct bar *msg_signature; msg_size = sizeof(unsigned char)// op + sizeof(struct foo)// data + sizeof(struct bar)// signature msg = malloc(msg_size); *msg = MSG_OP_SIGNED_DATA; msg_data = (struct foo *)(msg + 1); msg_data-field0 = bla; msg_data-field1 = blub; msg_signature = (struct bar *)(msg_data + 1); create_signature(msg, msg_signature); sendmsg(msg); free(msg); I feel it is pretty good already compared to other message packing I've seen, but do you know a way to make it suck even less? --Markus The alignment issue was already mentioned (there's nothing like your program working at home and then suddenly dying of SIGBUS in the field to hammer that point home), but unless all your data is single bytes, you also have byte order problems. Solving these requires some suck. There are basically only two ways to resolve these problems: Either copy the data into the buffer and flip it later, or flip it on the way. I'd personally go with flipping on the way: unsigned char *p = msg; *p++ = MSG_OP_SIGNED_DATA; align32(p, msg); write_be32(p, bla); write_be16(p, blub); /* and so on */ Then implement them like: void write_be32(unsigned char **p, uint32_t i) { *(*p)++ = i 24; *(*p)++ = i 16; *(*p)++ = i 8; *(*p)++ = i; } Then you are even independent of the host processor's byte order. HTH, Markus
[dev] network protocol packing
Heyho, since I did not find any suckless project regarding this issue, I would like to ask you guys for some feedback: unsigned char *msg; size_t msg_size; struct foo *msg_data; struct bar *msg_signature; msg_size = sizeof(unsigned char)// op + sizeof(struct foo)// data + sizeof(struct bar)// signature msg = malloc(msg_size); *msg = MSG_OP_SIGNED_DATA; msg_data = (struct foo *)(msg + 1); msg_data-field0 = bla; msg_data-field1 = blub; msg_signature = (struct bar *)(msg_data + 1); create_signature(msg, msg_signature); sendmsg(msg); free(msg); I feel it is pretty good already compared to other message packing I've seen, but do you know a way to make it suck even less? --Markus
Re: [dev] network protocol packing
On 30/06/14, Markus Teich wrote: Heyho, Hello there, since I did not find any suckless project regarding this issue, I would like to ask you guys for some feedback: unsigned char *msg; size_t msg_size; struct foo *msg_data; struct bar *msg_signature; msg_size = sizeof(unsigned char)// op + sizeof(struct foo)// data + sizeof(struct bar)// signature msg = malloc(msg_size); *msg = MSG_OP_SIGNED_DATA; msg_data = (struct foo *)(msg + 1); You've got alignment issues here - msg will be aligned to support any type (as malloc's interface specifies) so msg+1 will most likely be on an odd address, one byte off a highly aligned address. This means if your struct contains anything other than chars, you'll have UB. This is fine on x86, which allows unaligned access with a performance penalty but on something like an ARM machine you'll have issues. You probably want to memcpy the struct in from an existing one. msg_data-field0 = bla; msg_data-field1 = blub; msg_signature = (struct bar *)(msg_data + 1); create_signature(msg, msg_signature); sendmsg(msg); free(msg); I feel it is pretty good already compared to other message packing I've seen, but do you know a way to make it suck even less? Seems pretty straightforward otherwise :) Cheers, Rob