Francois Grieu
5/5/2011 1:13:00 PM
Le 05/05/2011 15:01, Ben Bacarisse wrote:
> Francois Grieu<fgrieu@gmail.com> writes:
>
>> I have system X needing to call procedures on a system Y
>> acting as server (say, "system Y, please decipher this data
>> using the key of index n in your keystore and return me
>> the result, or an error code if the key is not found").
>>
>> I can exchange variable-size 8-bit-byte blocks between
>> X (acting as client) and Y (acting as server). My problem
>> is conversion of parameters and result to/from 8-bit-byte
>> blocks, for a significant and evolving set of functions,
>> often with variable-size parameters. Y must be highly
>> resistant to deliberate injection of mis-formatted requests,
>> same for X on mis-formatted responses. Speed matters to a
>> degree since Y may use a PowerPC 603 and X may be a
>> multi-gigahertz Amd64 CPU connected thru PCIe.
>>
>> The C code for X and Y must be highly portable across systems.
>> They do not necessary share the same data type size and
>> endianness, and some crash on unaligned memory access.
>> I'm willing to assume CHAR_BIT==8, but not much more.
>>
>> The current code is rather ad-hoc: for each new function the
>> parameters and result are serialized on the originating side,
>> and parsed on the receiving side (wich is the harder part),
>> with no real method. On the parsing side, we have torrents of
>>
>> unsigned char* inData;
>> long vIdx = inData[4]<<8 | inData[5];
>> (sometime the index is an enum) or
>> long vIdx ;
>> vIdx = inData[j++]<<8;
>> vIdx |= inData[j++];
>> (I'm not making the use of "long" for 2-byte index)
>> and this is typically interleaved with error checking code
>> (although optimizing for error cases is pointless).
>>
>> This is error prone, in particular it is hard to avoid parsing
>> beyond the end of the received data (I have spotted cases where
>> the test for length is off-by-one, especially with the second
>> parsing method) and not introduce practically untestable
>> dependencies on basic type size.
>>
>> I have the feeling (did not benchmark) that calling a parsing
>> function for each argument would incur a significant speed penalty
>> (especially if we want to avoid globals, which is desirable since
>> Y is multi-threaded with no support for thread-local globals)
>> and I do not want to depend on if the compiler for Y supports
>> inline functions. Thus I'm leaning towards
>> - a general parsing function parsing all the arguments into
>> a struct, returning a pointer for variable-size for long
>> arguments, and doing that according to a description of
>> the expected input and/or struct interpreted at runtime
>> - some clever (but readable and robust) use of macros
>> - some simple C code generating scheme for the serialization
>> and de-serialization.
>>
>> Any idea/reference/pointer?
>
> This is not really a C question. The key term to search for is "remote
> procedure call" or RPC. Many moons ago I wrote a portable RPC mechanism
> for C programs, but there are now lots of these and some are widely
> available (Sun RPC being one of the most widely distributed).
My problem is clean, robust, portable, lightweight, efficient
serialization/de-serialization of C arguments and result in
the context of an (assumed working) RPC framework where a remote
procedure has a single argument and result: a variable size
8-bit-byte buffer.
I see it as largely C-specific, because C has lax rules on type sizes,
arithmetic, packing, alignment. And C has a relatively precisely
defined preprocessor that may help.
Francois Grieu