aboutsummaryrefslogblamecommitdiffstats
path: root/types-message.h
blob: 5d479b744156aa6404da22a787fad282ab510725 (plain) (tree)




































































































































                                                                                            
/* See LICENSE file for copyright and license details. */
#ifndef TYPES_MESSAGE_H
#define TYPES_MESSAGE_H

#include <stddef.h>
#include <limits.h>

#ifndef GCC_ONLY
# if defined(__GNUC__) && !defined(__clang__)
#  define GCC_ONLY(...) __VA_ARGS__
# else
#  define GCC_ONLY(...) /* nothing */
# endif
#endif

/**
 * Message passed between a server and a client
 */
struct message {
	/**
	 * The headers in the message, each element in this list
	 * as an unparsed header, it consists of both the header
	 * name and its associated value, joined by ": ". A header
	 * cannot be `NULL` (unless its memory allocation failed,)
	 * but `headers` itself is `NULL` if there are no headers.
	 * The "Length" header should be included in this list.
	 */
	char **restrict headers;

	/**
	 * The number of headers in the message
	 */
	size_t header_count;

	/**
	 * The payload of the message, `NULL` if none (of zero-length)
	 */
	char *restrict payload;

	/**
	 * The size of the payload
	 */
	size_t payload_size;

	/**
	 * How much of the payload that has been stored (internal data)
	 */
	size_t payload_ptr;

	/**
	 * Internal buffer for the reading function (internal data)
	 */
	char *restrict buffer;

	/**
	 * The size allocated to `buffer` (internal data)
	 */
	size_t buffer_size;

	/**
	 * The number of bytes used in `buffer` (internal data)
	 */
	size_t buffer_ptr;

	/**
	 * 0 while reading headers, 1 while reading payload, and 2 when done (internal data)
	 */
	int stage;

#if INT_MAX != LONG_MAX
	int padding__;
#endif
};

/**
 * Initialise a message slot so that it can
 * be used by to read messages
 * 
 * @param   this  Memory slot in which to store the new message
 * @return        Non-zero on error, `errno` will be set accordingly
 */
GCC_ONLY(__attribute__((__nonnull__)))
int message_initialise(struct message *restrict this);

/**
 * Release all resources in a message, should
 * be done even if initialisation fails
 * 
 * @param  this  The message
 */
GCC_ONLY(__attribute__((__nonnull__)))
void message_destroy(struct message *restrict this);

/**
 * Marshal a message for state serialisation
 * 
 * @param  this  The message
 * @param  buf   Output buffer for the marshalled data,
 *               `NULL` just measure how large the buffers
 *               needs to be
 * @return       The number of marshalled byte
 */
GCC_ONLY(__attribute__((__nonnull__(1))))
size_t message_marshal(const struct message *restrict this, void *restrict buf);

/**
 * Unmarshal a message for state deserialisation
 * 
 * @param   this  Memory slot in which to store the new message
 * @param   buf   In buffer with the marshalled data
 * @return        The number of unmarshalled bytes, 0 on error
 */
GCC_ONLY(__attribute__((__nonnull__)))
size_t message_unmarshal(struct message *restrict this, const void *restrict buf);

/**
 * Read the next message from a file descriptor
 * 
 * @param   this  Memory slot in which to store the new message
 * @param   fd    The file descriptor
 * @return        0:  At least one message is available
 *                -1: Exceptional connection:
 *                  EINTR:        System call interrupted
 *                  EAGAIN:       No message is available
 *                  EWOULDBLOCK:  No message is available
 *                  ECONNRESET:   Connection closed
 *                  Other:        Failure
 *                -2: Corrupt message (unrecoverable)
 */
GCC_ONLY(__attribute__((__nonnull__)))
int message_read(struct message *restrict this, int fd);

#endif