Horus Messages

This document is part of the online Horus Documentation, under Horus Utilities.


--MUTS provides a convenient structure to store and manipulate messages. Messages go through three stages. First they are written, then they can be read (but not written), and then they are released. In the write stage, data is always prepended rather than appended. You will find this convenient most of the time, as you add headers to a message. Concurrent writing is not allowed (and not really useful). Messages are usually read from the front to the back. However, if you wish to push part of a message onto another message, the data is taken of the back rather than the front. Although hard to explain why this is convenient, just believe us for the moment. Once you start reading a message, you are no longer allowed to write it. Concurrent reading is allowed. For this, a message may have multiple pointers, each with its own read (and concatenation) offset. Moreover, each pointer can have some private data attached to it, so different layers can maintain control information with a message.

SOURCES

include/muts/xxx_message.h
src/muts/xxx_message.c

INCLUDE

#include "muts.h"

SWITCHES

#define MSG_PURIFY	/* enable for better purification */

INTERFACE

xxx_message_t *xxx_message_alloc(
	struct mem_chan *mc,
	char  *name
);
This routine allocates a message of the given memory channel. The message gets a name for debugging purposes. The xxx_message_t structure contains a field, "void *ctl", which may be used to maintain private data associated with this message.
xxx_message_t *xxx_message_clone(
	 xxx_message_t *msg
);
A message may be cloned. Cloned messages share the same message data, but each have their own offsets into the message, and each have their own ctl pointer. This routine clones a message and returns a new pointer. The reference count to the message is *not* incremented, that is, it is as if the original message was released. So if you want to keep accessing the old pointer, you first have to dup it. The offsets and ctl pointer in the cloned message are initially the same as those of the original.
xxx_message_dup(
	 xxx_message_t *msg
);
This routine increments the reference counter to a message. It is typically called when a message pointer is saved in some data structure.
xxx_message_release(
	 xxx_message_t *msg
);
This routine decrements the reference counter of a message, and releases the message when it reaches zero.
void xxx_message_push(
	 xxx_message_t *msg,
	 unsigned int options,
	 void *data,
	 int size,
	 void (*free)(void *env, void *data),
	 void *env
);
Prepend the given data of the given size to the message. If size is -1, then the data is a null-terminated string, and everything but the null byte is pushed onto the message. The data is copied into internal storage unless the XXX_MSG_NO_COPY option is specified. In that case, the given free routine is called upon final release. Free is called with the given env and a pointer to the given data. This function may be null if the data need not be released. Otherwise, this free routine is often mc_free, to release data previously allocated using mc_alloc.
void xxx_message_add(
	 xxx_message_t *msg,
	 unsigned int options,
	 int size,
	 void **dataptr
);
Like xxx_message_push, except that data is allocated automatically and a pointer to that data is returned through dataptr. The data may be updated until another operation is executed on the message. The data is automatically released upon final release of the message. The returned pointer is normally aligned to some safe alignment unless the XXX_MSG_NO_ALIGN option is specified. If dataptr is null, zero bytes are added to the message
int xxx_message_tail(
	 xxx_message_t *msg
);
	This routine returns the remaining length of the
	message, from the current offset to the end of
	the message.
int xxx_message_total(
	 xxx_message_t *msg
);
This routine returns the total length of the given message. Avoid this routine. Use xxx_message_tail instead.
int xxx_message_read(
	 xxx_message_t *msg,
	 unsigned int options,
	 void *buf,
	 int size
);
This function reads size bytes into the buffer buf. It may read less than that if the message is not long enough, therefore the number of bytes really transfered is returned.
void *xxx_message_map(
	 xxx_message_t *msg,
	 unsigned int options,
	 int length,
	 xxx_msg_map_t *map
);
Map part of a message (from current offset for the given length) in the current address space. This map may not be updated, and must be released after usage using xxx_message_map_release. The message may not be released while a map is outstanding. Zero-length maps are allowed. Normally the map will be aligned conservatively unless the XXX_MSG_NO_ALIGN option has been specified. Returns null if the remaining length in the message is less than the requested length.
	void xxx_message_map_release(
	 xxx_msg_map_t *map
);
This function releases a map previously acquired with xxx_message_map.
int xxx_message_seek(
	 xxx_message_t *msg,
	 int offset,
	 unsigned int options
);
This function updates one of the offsets of a message. If options contains XXX_MSG_LEFT, it's the read offset (default), and if options contains XXX_MSG_RIGHT, it's the concat offset relative to the end of the message. If options contains XXX_MSG_SET, the offset is the absolute position. If options contains XXX_MSG_CUR, the offset is relative to the current position. If options is XXX_MSG_END, offset is subtracted from the other end of the message (which is the concat or the read offset). In all cases, the resulting offset is returned. You are allowed to seek outside the bounds of the message, but read operations there are undefined.
error_t xxx_message_error(
	 xxx_message_t *msg
);
To avoid having to test each and every message operation for errors, messages remember the first thing that goes wrong. If something goes wrong, subsequent operations are still executed so that data release can still be performed. This function returns the error description.
int xxx_message_concat(
	 xxx_message_t *msg1,
	 xxx_message_t *msg2,
	 int bytes
);
Push the specified number of bytes of msg2, below its current concat offset, onto msg1. The concat offset of msg2 is automatically updated. If there is not enough data left in msg2, the data is truncated and less than the specified number of bytes are transfered. The actual number of transfered bytes is returned. Msg1 will maintain a reference to msg2, so msg2 will not be released before msg1 is.
void *xxx_message_mem_alloc(
	 xxx_message_t *msg,
	 unsigned int options,
	 int size,
	 void (*upcall)(void *env, void *mem),
	 void *env
);
A convenient feature of messages is that they allow efficient memory allocation for data that is to be associated with the message. This memory is automatically released upon final release of the message. For example, this memory may be used for the private data of the message. A pointer to the allocated data is returned. Normally this pointer is aligned conservatively, unless the XXX_MSG_NO_ALIGN option is specified. Concurrent access is normally allowed at some extra overhead. If no concurrent memory allocation is done, the XXX_MSG_NO_LOCK option turns off the extra overhead. Just before the memory is released, the given upcall function is invoked with the env pointer and a pointer to the memory itself. This upcall can be used to release any data pointed to in the memory itself.

EXAMPLE

not available yet

FUTURE WORK

We are currently in the process of combining the xxx_event and the xxx_message structure into one single structure. We will support adding new fields onto this structure at run-time, using the layout module support.


This document is part of the online Horus Documentation, under Horus Utilities.