event.c 8.79 KB
#include <scot/memory.h>
#include <scot/stream.h>
#include <scot/event.h>
#include <scot/scot_int.h>
#include <scot/scot_types.h>

/*
 * !!! REALLY IMPORTANT !!!
 * If there is extra data to an event, and the event sould be serializable,
 * one has to ensure that ed points to already network byteorder 
 * converted data. Else there might occur problems when sending the data
 * to another machine with a different byteorder.
 */
struct scot_event *
scot_event_new (struct scot_event * e, SCOT_EVENT_NO no, void * ed, SIZE_T eds)
{
	if (e == NULL)
	{
		e = SCOT_MEM_GET (sizeof (struct scot_event));
		SCOT_MEM_ZERO (e, sizeof (struct scot_event));
	}

	e->event = no;

	e->size  = sizeof (SCOT_EVENT_NO);
	e->size += sizeof (SIZE_T);

	if (ed != NULL)
	{
		e->extra_data = SCOT_MEM_GET (eds);
		SCOT_MEM_COPY (e->extra_data, ed, eds);
		e->ed_size = eds;
		e->size += eds;
	}

	e->size += sizeof (SIZE_T);

	return e;
}

struct scot_stream_pool_event * 
scot_stream_pool_event_new (
		struct scot_stream_pool_event *  e,
		SCOT_EVENT_NO                    no, 
		void *                           ed, 
		SIZE_T                           eds,
		struct scot_stream *             st)
{
	if (e == NULL)
	{
		e = SCOT_MEM_GET (sizeof (struct scot_stream_pool_event));
		SCOT_MEM_ZERO (e, sizeof (struct scot_stream_pool_event));
	}

	e = (struct scot_stream_pool_event *) scot_event_new (
			(struct scot_event *) e, no, ed, eds);

	e->st = st;

	e->event.size += sizeof (struct scot_stream);

	return e;
}

struct scot_fs_watcher_event *
scot_fs_watcher_event_new (
		struct scot_fs_watcher_event *  e,
		SCOT_EVENT_NO                   no, 
		void *                          ed, 
		SIZE_T                          eds,
		const char *                    path,
		const char *                    name,
		const char *                    oldname)
{
	if (e == NULL)
	{
		e = SCOT_MEM_GET (sizeof (struct scot_fs_watcher_event));
		SCOT_MEM_ZERO (e, sizeof (struct scot_fs_watcher_event));
	}

	e = (struct scot_fs_watcher_event *) scot_event_new (
			(struct scot_event *) e, no, ed, eds);

	e->path = SCOT_MEM_GET (SCOT_STR_LENGTH (path) + 1);
	SCOT_STR_COPY (e->path, path);

	e->event.size += SCOT_STR_LENGTH (path) + 1;

	e->name = SCOT_MEM_GET (SCOT_STR_LENGTH (name) + 1);
	SCOT_STR_COPY (e->name, name);

	e->event.size += SCOT_STR_LENGTH (name) + 1;

	if (oldname != NULL)
	{
		e->oldname = SCOT_MEM_GET (SCOT_STR_LENGTH (oldname) + 1);
		SCOT_STR_COPY (e->oldname, oldname);

		e->event.size += SCOT_STR_LENGTH (oldname) + 1;
	}
	else
		e->event.size += 1;

	return e;
}

static
void
scot_stream_pool_event_free (struct scot_stream_pool_event * e)
{}

static
void
scot_fs_watcher_event_free (struct scot_fs_watcher_event * e)
{
	if (e->path != NULL)
		SCOT_MEM_FREE (e->path);

	if (e->name != NULL)
		SCOT_MEM_FREE (e->name);

	if (e->oldname != NULL)
		SCOT_MEM_FREE (e->oldname);
}

/*
 * scot_event_free identifies the given event and call appropriate
 * free methods for it. Thus only scot_event_free is exported to 
 * free any type of event.
 * The same is true for serialize and deserialize.
 */
void
scot_event_free (struct scot_event * e)
{
	if (e != NULL)
	{
		if (SCOT_EVENT_CHK_GROUP (e->event, SCOT_EG_STREAM_POOL))
			scot_stream_pool_event_free ((struct scot_stream_pool_event *) e);
		if (SCOT_EVENT_CHK_GROUP (e->event, SCOT_EG_FS_WATCHER))
			scot_fs_watcher_event_free ((struct scot_fs_watcher_event *) e);

		if (e->extra_data != NULL)
			SCOT_MEM_FREE (e->extra_data);

		SCOT_MEM_FREE (e);
	}
}

/*
 * Eine Sache die man unbedingt beachten sollte ist, das die serialisierten
 * Daten unter umständen auf einer Maschine mit unterschiedlicher
 * Byte-Order landen. Daher muessen alle Daten in Network Byteorder
 * umgewandelt werden und beim deserialisieren wieder zurück.
 */
static
void
scot_stream_pool_event_serialize (struct scot_stream_pool_event * e, char ** s)
{
	char * fill = *s;

	/*
	 * serialize the struct scot_stream item of a stream_pool_event.
	 * well, normally we don't send stream_pool_event to other machines,
	 * so it might look like bloat to convert every part of it to
	 * network byte order. But as we do it we are ready to send the if
	 * the need araises in future.
	 * But at least to send this information to other machines is kind of
	 * senseless, because handle is something completely different on
	 * windows and linux.
	 *
	 * Well, the htonl approch does only work with sockets on windows.
	 * Under linux it is unimportant what part of the union is used, as
	 * all are uint32_t. Serialization of the stream handle makes no
	 * sense at all, as it couldn't be used by the receiver anyway.
	 */
	* (int32_t *)fill = htonl (e->st->handle.sock);
	fill += 4;
	SCOT_MEM_COPY (fill, e->st->rbuf, SCOT_STREAM_BUF_SIZE);
	fill += SCOT_STREAM_BUF_SIZE;
	SCOT_MEM_COPY (fill, e->st->wbuf, SCOT_STREAM_BUF_SIZE);
	fill += SCOT_STREAM_BUF_SIZE;
	* (uint32_t *)fill = htonl (e->st->rbuf_idx);
	fill += 4;
	* (uint32_t *)fill = htonl (e->st->wbuf_idx);
	fill += 4;
	* (uint32_t *)fill = htonl (e->st->s_type);
	fill += 4;

	*s = fill;
}

static
void
scot_fs_watcher_event_serialize (struct scot_fs_watcher_event * e, char ** s)
{
	char * fill = *s;

	/*
	 * zuerst fswi serialisieren....
	 */
	SCOT_STR_COPY (fill, e->path);
	fill += SCOT_STR_LENGTH (e->path) + 1;
	SCOT_STR_COPY (fill, e->name);
	fill += SCOT_STR_LENGTH (e->name) + 1;
	if (e->oldname != NULL)
		SCOT_STR_COPY (fill, e->oldname);
	else
		SCOT_STR_COPY (fill, "");
	fill += SCOT_STR_LENGTH (fill) + 1;

	*s = fill;
}

static
struct scot_event *
scot_stream_pool_event_deserialize (
		struct scot_stream_pool_event * e, const char * s)
{
	e->st = SCOT_MEM_GET (sizeof (struct scot_stream));

	e->st->handle.sock = ntohl (* (int32_t *)s);
	s += 4;
	SCOT_MEM_COPY (e->st->rbuf, s, SCOT_STREAM_BUF_SIZE);
	s += SCOT_STREAM_BUF_SIZE;
	SCOT_MEM_COPY (e->st->wbuf, s, SCOT_STREAM_BUF_SIZE);
	s += SCOT_STREAM_BUF_SIZE;
	e->st->rbuf_idx = ntohl (* (uint32_t *)s);
	s += 4;
	e->st->wbuf_idx = ntohl (* (uint32_t *)s);
	s += 4;
	e->st->s_type = ntohl (* (uint32_t *)s);

	SCOT_MEM_COPY (e->st, s, sizeof (struct scot_stream));

	return (struct scot_event *) e;
}

static
struct scot_event *
scot_fs_watcher_event_deserialize (
		struct scot_fs_watcher_event * e, const char * s)
{
	e->path = SCOT_MEM_GET (SCOT_STR_LENGTH (s) + 1);
	SCOT_STR_COPY (e->path, s);
	s += SCOT_STR_LENGTH (s) + 1;

	e->name = SCOT_MEM_GET (SCOT_STR_LENGTH (s) + 1);
	SCOT_STR_COPY (e->name, s);
	s += SCOT_STR_LENGTH (s) + 1;

	e->oldname = SCOT_MEM_GET (SCOT_STR_LENGTH (s) + 1);
	SCOT_STR_COPY (e->oldname, s);
	
	return (struct scot_event *) e;
}

SIZE_T
scot_event_serialize (struct scot_event * e, char ** s)
{
	char * fill;

	*s = fill = SCOT_MEM_GET (e->size);
	SCOT_MEM_ZERO (fill, e->size);

	* (SCOT_EVENT_NO *)fill = htons (e->event);
	fill += 2;
	* (uint32_t *)fill = htonl (e->size);
	fill += 4;
	* (uint32_t *)fill = htonl (e->ed_size);
	fill += 4;

	/*
	 * every extra data must be available in Network Byteorder if the
	 * event should be serializable.
	 * This is important at an ..._event_new function, as the void * for
	 * extra_data should always point to data that is already converted
	 * to network byteorder.
	 */
	if (e->extra_data != NULL)
	{
		SCOT_MEM_COPY (fill, &e->extra_data, e->ed_size);
		fill += e->ed_size;
	}

	if (SCOT_EVENT_CHK_GROUP (e->event, SCOT_EG_STREAM_POOL))
		scot_stream_pool_event_serialize (
				(struct scot_stream_pool_event *) e, &fill);
	if (SCOT_EVENT_CHK_GROUP (e->event, SCOT_EG_FS_WATCHER))
		scot_fs_watcher_event_serialize (
				(struct scot_fs_watcher_event *) e, &fill);

	return e->size;
}

/*
 * GANZ WICHTIG....für diese Funktion muß sichergestellt sein, daß
 * s mindestens sizeof (SCOT_EVENT_NO) + sizeof (SIZE_T) bytes
 * vom anfang einer gültigen event-serialisierung enthält.
 * Sonst kann die größe logischerweise nicht ermittelt werden.
 */
SIZE_T
scot_event_size_serial (struct scot_event * e, const char * s)
{
	return ntohl (* (uint32_t *)(s + sizeof (SCOT_EVENT_NO)));
}

SCOT_EVENT_NO
scot_event_no_serial (struct scot_event * e, const char * s)
{
	return ntohs (* (SCOT_EVENT_NO *)s);
}

/*
 * Hier sollte sichergestellt sein, das s ein komplettes serialisiertes
 * event enthält.
 */
struct scot_event *
scot_event_deserialize (struct scot_event * e, const char * s)
{
	SIZE_T size;

	size = scot_event_size_serial (e, s);

	e = SCOT_MEM_GET (size);
	SCOT_MEM_ZERO (e, size);

	e->event = ntohs (* (SCOT_EVENT_NO *)s);
	s += 2;
	e->size = ntohl (* (uint32_t *)s);
	s += 4;
	e->ed_size = ntohl (* (uint32_t *)s);
	s += 4;

	if (e->ed_size != 0)
	{
		SCOT_MEM_COPY (&e->extra_data, s, e->ed_size);
		s += e->ed_size;
	}

	if (SCOT_EVENT_CHK_GROUP (e->event, SCOT_EG_STREAM_POOL))
		e = scot_stream_pool_event_deserialize (
				(struct scot_stream_pool_event *) e, s);
	if (SCOT_EVENT_CHK_GROUP (e->event, SCOT_EG_FS_WATCHER))
		e = scot_fs_watcher_event_deserialize (
				(struct scot_fs_watcher_event *) e, s);

	return e;
}