Variable-length messages.

Dave Jones djones at megatest.UUCP
Sun Oct 23 11:24:49 AEST 1988


#define SLM 1  /* silly little macro */

Shall we define a message packet as follows?

struct msg
  { int size;
    enum msg_type type;
    char contents[SLM];
  };

Or with a header?

struct hdr
  { int size;
    enum msg_type type;
  };

And then we package the contents separately?

It is quite likely that I would choose neither. It depends
partly on whether the solution is to be tuned for one program,
and thus can have "direct knowledge" of the data-types being
transmitted, or whether it is to be a general "library" solution.
Is it supposed to transfer messages between machines of different 
types or between programs compiled with different compilers? 
(Apparently not.)  Approximately how many different message types 
will there be?  Will they differ significantly in size?  Will they all
have a fixed-size format?  Etc..

But we still have the question as to whether or not the first declaration
is even feasible.  So, for now, let's go along with the gag, and look 
at the first declaration.

How does one malloc such a thing, and how do you write bytes into
it?  What you've got to watch out for is alignment restrictions
and conventions, and "holes" in structures.  Some of the "solutions"
which have been posted so far, and which purport to solve the
problem don't.  To begin with, the compiler may not align that 
one-char-array on a boundary suitable for just any kind of data.
So lets back off and try again. 

/* The following is supposed to have the most general
** alignment of all types.
*/
typedef union
 { void* ptr; char c; short s; int i; long l;
   float f; double d;
 }max_align;

struct msg
  { int size;
    enum msg_type type;
    max_align contents[1];
  };

Now the "contents" field should be aligned on a boundary suitable
for any data-type. (Did I miss something?)

Now, I think the following will always work; But remember, I
didn't recommend this approach, anyway.

  extern int errno;
  extern int pipe_out;

  int
  send(size,  type, contents)
     int size;
     void* contents;
     enum msg_type type;
  {
      int packet_size = 

            /* header overhead (including any padding or "holes") */
            sizeof(struct msg) - sizeof(max_align) 
      
            /* and add to that... */
            + size;

      struct msg* msg;

      if((msg = (struct msg*)malloc(packet_size)) == 0)
         return errno;

      /* Fill out the header. */
      msg->size = size;
      msg->type = type;

      /* Copy the message. */
      bcopy(contents, (void*)(msg->contents), size);

      { int written = write(pipe_out, (void*)msg, packet_size);
        free(msg);
	return (written == packet_size)?0: errno;
      }
  }

   



More information about the Comp.lang.c mailing list