dbus/dbus/dbus-message.c
Havoc Pennington 17fbe2b702 2002-12-24 Havoc Pennington <hp@pobox.com>
* glib/dbus-gthread.c: fix include

	* glib/dbus-glib.h: rename DBusMessageHandler for now.
	I think glib API needs to change, though, as you don't
	want to use DBusMessageFunction, you want to use the
	DBusMessageHandler object. Probably
	dbus_connection_open_with_g_main_loop()
	and dbus_connection_setup_g_main_loop() or something like that
	(but think of better names...) that just create a connection
	that has watch/timeout functions etc. already set up.

	* dbus/dbus-connection.c
	(dbus_connection_send_message_with_reply): new function just to
	show how the message handler helps us deal with replies.

	* dbus/dbus-list.c (_dbus_list_remove_last): new function

	* dbus/dbus-string.c (_dbus_string_test): free a string that
	wasn't

	* dbus/dbus-hash.c: use memory pools for the hash entries
	(rebuild_table): be more paranoid about overflow, and
	shrink table when we can
	(_dbus_hash_test): reduce number of sprintfs and write
	valid C89. Add tests for case where we grow and then
	shrink the hash table.

	* dbus/dbus-mempool.h, dbus/dbus-mempool.c: memory pools

	* dbus/dbus-connection.c (dbus_connection_register_handler)
	(dbus_connection_unregister_handler): new functions

	* dbus/dbus-message.c (dbus_message_get_name): new

	* dbus/dbus-list.c: fix docs typo

	* dbus/dbus-message-handler.h, dbus/dbus-message-handler.c:
	an object representing a handler for messages.
2002-12-24 06:37:33 +00:00

410 lines
11 KiB
C

/* -*- mode: C; c-file-style: "gnu" -*- */
/* dbus-message.c DBusMessage object
*
* Copyright (C) 2002 Red Hat Inc.
*
* Licensed under the Academic Free License version 1.2
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include "dbus-internals.h"
#include "dbus-message.h"
#include "dbus-message-internal.h"
#include "dbus-memory.h"
#include "dbus-list.h"
#include <string.h>
/**
* @defgroup DBusMessageInternals DBusMessage implementation details
* @ingroup DBusInternals
* @brief DBusMessage private implementation details.
*
* The guts of DBusMessage and its methods.
*
* @{
*/
/**
* The largest-length message we allow
*
* @todo match this up with whatever the protocol spec says.
*/
#define _DBUS_MAX_MESSAGE_LENGTH (_DBUS_INT_MAX/16)
/**
* @brief Internals of DBusMessage
*
* Object representing a message received from or to be sent to
* another application. This is an opaque object, all members
* are private.
*/
struct DBusMessage
{
int refcount; /**< Reference count */
DBusString header; /**< Header network data, stored
* separately from body so we can
* independently realloc it.
*/
DBusString body; /**< Body network data. */
unsigned int locked : 1; /**< Message being sent, no modifications allowed. */
};
/**
* Gets the data to be sent over the network for this message.
* The header and then the body should be written out.
* This function is guaranteed to always return the same
* data once a message is locked (with _dbus_message_lock()).
*
* @param message the message.
* @param header return location for message header data.
* @param body return location for message body data.
*/
void
_dbus_message_get_network_data (DBusMessage *message,
const DBusString **header,
const DBusString **body)
{
_dbus_assert (message->locked);
*header = &message->header;
*body = &message->body;
}
/**
* Locks a message. Allows checking that applications don't keep a
* reference to a message in the outgoing queue and change it
* underneath us. Messages are locked when they enter the outgoing
* queue (dbus_connection_send_message()), and the library complains
* if the message is modified while locked.
*
* @param message the message to lock.
*/
void
_dbus_message_lock (DBusMessage *message)
{
message->locked = TRUE;
}
/** @} */
/**
* @defgroup DBusMessage DBusMessage
* @ingroup DBus
* @brief Message to be sent or received over a DBusConnection.
*
* A DBusMessage is the most basic unit of communication over a
* DBusConnection. A DBusConnection represents a stream of messages
* received from a remote application, and a stream of messages
* sent to a remote application.
*
* @{
*/
/**
* @typedef DBusMessage
*
* Opaque data type representing a message received from or to be
* sent to another application.
*/
/**
* Constructs a new message. Returns #NULL if memory
* can't be allocated for the message.
*
* @return a new DBusMessage, free with dbus_message_unref()
* @see dbus_message_unref()
*/
DBusMessage*
dbus_message_new (void)
{
DBusMessage *message;
message = dbus_new0 (DBusMessage, 1);
if (message == NULL)
return NULL;
message->refcount = 1;
if (!_dbus_string_init (&message->header, _DBUS_MAX_MESSAGE_LENGTH))
{
dbus_free (message);
return NULL;
}
if (!_dbus_string_init (&message->body, _DBUS_MAX_MESSAGE_LENGTH))
{
_dbus_string_free (&message->header);
dbus_free (message);
return NULL;
}
/* We need to decide what a message contains. ;-) */
/* (not bothering to check failure of these appends) */
_dbus_string_append (&message->header, "H");
_dbus_string_append_byte (&message->header, '\0');
_dbus_string_append (&message->body, "Body");
_dbus_string_append_byte (&message->body, '\0');
return message;
}
/**
* Increments the reference count of a DBusMessage.
*
* @param message The message
* @see dbus_message_unref
*/
void
dbus_message_ref (DBusMessage *message)
{
message->refcount += 1;
}
/**
* Decrements the reference count of a DBusMessage.
*
* @param message The message
* @see dbus_message_ref
*/
void
dbus_message_unref (DBusMessage *message)
{
_dbus_assert (message != NULL);
_dbus_assert (message->refcount > 0);
message->refcount -= 1;
if (message->refcount == 0)
{
_dbus_string_free (&message->header);
_dbus_string_free (&message->body);
dbus_free (message);
}
}
/**
* Gets the name of a message.
* @param message the message
* @returns the message name (should not be freed)
*/
const char*
dbus_message_get_name (DBusMessage *message)
{
/* FIXME */
return NULL;
}
/** @} */
/**
* @addtogroup DBusMessageInternals
*
* @{
*/
/**
* @typedef DBusMessageLoader
*
* The DBusMessageLoader object encapsulates the process of converting
* a byte stream into a series of DBusMessage. It buffers the incoming
* bytes as efficiently as possible, and generates a queue of
* messages. DBusMessageLoader is typically used as part of a
* DBusTransport implementation. The DBusTransport then hands off
* the loaded messages to a DBusConnection, making the messages
* visible to the application.
*
*/
/**
* Implementation details of DBusMessageLoader.
* All members are private.
*/
struct DBusMessageLoader
{
int refcount; /**< Reference count. */
DBusString data; /**< Buffered data */
DBusList *messages; /**< Complete messages. */
unsigned int buffer_outstanding : 1; /**< Someone is using the buffer to read */
};
/**
* The initial buffer size of the message loader.
*
* @todo this should be based on min header size plus some average
* body size, or something. Or rather, the min header size only, if we
* want to try to read only the header, store that in a DBusMessage,
* then read only the body and store that, etc., depends on
* how we optimize _dbus_message_loader_get_buffer() and what
* the exact message format is.
*/
#define INITIAL_LOADER_DATA_LEN 32
/**
* Creates a new message loader. Returns #NULL if memory can't
* be allocated.
*
* @returns new loader, or #NULL.
*/
DBusMessageLoader*
_dbus_message_loader_new (void)
{
DBusMessageLoader *loader;
loader = dbus_new0 (DBusMessageLoader, 1);
if (loader == NULL)
return NULL;
loader->refcount = 1;
if (!_dbus_string_init (&loader->data, _DBUS_INT_MAX))
{
dbus_free (loader);
return NULL;
}
/* preallocate the buffer for speed, ignore failure */
(void) _dbus_string_set_length (&loader->data, INITIAL_LOADER_DATA_LEN);
return loader;
}
/**
* Increments the reference count of the loader.
*
* @param loader the loader.
*/
void
_dbus_message_loader_ref (DBusMessageLoader *loader)
{
loader->refcount += 1;
}
/**
* Decrements the reference count of the loader and finalizes the
* loader when the count reaches zero.
*
* @param loader the loader.
*/
void
_dbus_message_loader_unref (DBusMessageLoader *loader)
{
loader->refcount -= 1;
if (loader->refcount == 0)
{
_dbus_list_foreach (&loader->messages,
(DBusForeachFunction) dbus_message_unref,
NULL);
_dbus_list_clear (&loader->messages);
_dbus_string_free (&loader->data);
dbus_free (loader);
}
}
/**
* Gets the buffer to use for reading data from the network. Network
* data is read directly into an allocated buffer, which is then used
* in the DBusMessage, to avoid as many extra memcpy's as possible.
* The buffer must always be returned immediately using
* _dbus_message_loader_return_buffer(), even if no bytes are
* successfully read.
*
* @todo this function can be a lot more clever. For example
* it can probably always return a buffer size to read exactly
* the body of the next message, thus avoiding any memory wastage
* or reallocs.
*
* @param loader the message loader.
* @param buffer the buffer
*/
void
_dbus_message_loader_get_buffer (DBusMessageLoader *loader,
DBusString **buffer)
{
_dbus_assert (!loader->buffer_outstanding);
*buffer = &loader->data;
loader->buffer_outstanding = TRUE;
}
/**
* Returns a buffer obtained from _dbus_message_loader_get_buffer(),
* indicating to the loader how many bytes of the buffer were filled
* in. This function must always be called, even if no bytes were
* successfully read.
*
* @param loader the loader.
* @param buffer the buffer.
* @param bytes_read number of bytes that were read into the buffer.
*/
void
_dbus_message_loader_return_buffer (DBusMessageLoader *loader,
DBusString *buffer,
int bytes_read)
{
_dbus_assert (loader->buffer_outstanding);
_dbus_assert (buffer == &loader->data);
/* FIXME fake implementation just creates a message for every 7
* bytes. The real implementation will pass ownership of
* loader->data bytes to new messages, to avoid memcpy. We can also
* smart-realloc loader->data to shrink it if it's too big, though
* _dbus_message_loader_get_buffer() could strategically arrange for
* that to usually not happen.
*/
loader->buffer_outstanding = FALSE;
while (_dbus_string_get_length (&loader->data) >= 7)
{
DBusMessage *message;
message = dbus_message_new ();
if (message == NULL)
break; /* ugh, postpone this I guess. */
_dbus_list_append (&loader->messages, message);
_dbus_string_delete (&loader->data,
0, 7);
_dbus_verbose ("Loaded message %p\n", message);
}
}
/**
* Pops a loaded message (passing ownership of the message
* to the caller). Returns #NULL if no messages have been
* loaded.
*
* @param loader the loader.
* @returns the next message, or #NULL if none.
*/
DBusMessage*
_dbus_message_loader_pop_message (DBusMessageLoader *loader)
{
return _dbus_list_pop_first (&loader->messages);
}
/** @} */