Files
haiku-beta6/src/kits/app/Message.cpp
T
Michael Lotz 2bf8fb3a96 Putting Message4 into place to become the new BMessage implementation. Will cleanup USING_MESSAGE4 now.
git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@16709 a95241bf-73f2-0310-859d-f6bbb57e9c96
2006-03-11 17:55:47 +00:00

2597 lines
59 KiB
C++

/*
* Copyright 2005, Haiku Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Michael Lotz <[email protected]>
*/
#include "dano_message.h"
#include "r5_message.h"
#include <Message.h>
#include <MessagePrivate.h>
#include <MessageUtils.h>
#include <Application.h>
#include <AppMisc.h>
#include <BlockCache.h>
#include <Entry.h>
#include <KMessage.h>
#include <Messenger.h>
#include <MessengerPrivate.h>
#include <Path.h>
#include <Point.h>
#include <Rect.h>
#include <String.h>
#include <TokenSpace.h>
#include <ctype.h>
#include <malloc.h>
#include <stdio.h>
#include <string.h>
#define DEBUG_FUNCTION_ENTER //debug_printf("thread: 0x%x; this: 0x%08x; header: 0x%08x; fields: 0x%08x; data: 0x%08x; line: %04ld; func: %s\n", find_thread(NULL), this, fHeader, fFields, fData, __LINE__, __PRETTY_FUNCTION__);
#define DEBUG_FUNCTION_ENTER2 //debug_printf("thread: 0x%x; line: %04ld: func: %s\n", find_thread(NULL), __LINE__, __PRETTY_FUNCTION__);
static const uint32 kMessageMagicR5 = 'FOB1';
static const uint32 kMessageMagicR5Swapped = '1BOF';
static const uint32 kMessageMagicDano = 'FOB2';
static const uint32 kMessageMagicDanoSwapped = '2BOF';
static const uint32 kMessageMagic4 = '4GSM';
static const uint32 kMessageMagic4Swapped = 'MSG4';
const char *B_SPECIFIER_ENTRY = "specifiers";
const char *B_PROPERTY_ENTRY = "property";
const char *B_PROPERTY_NAME_ENTRY = "name";
static status_t handle_reply(port_id replyPort, int32 *pCode,
bigtime_t timeout, BMessage *reply);
static status_t convert_message(const KMessage *fromMessage,
BMessage *toMessage);
extern "C" {
// private os function to set the owning team of an area
status_t _kern_transfer_area(area_id area, void **_address,
uint32 addressSpec, team_id target);
}
BBlockCache *BMessage::sMsgCache = NULL;
port_id BMessage::sReplyPorts[sNumReplyPorts];
long BMessage::sReplyPortInUse[sNumReplyPorts];
BMessage::BMessage()
{
DEBUG_FUNCTION_ENTER;
_InitCommon();
_InitHeader();
}
BMessage::BMessage(uint32 _what)
{
DEBUG_FUNCTION_ENTER;
_InitCommon();
_InitHeader();
fHeader->what = what = _what;
}
BMessage::BMessage(const BMessage &other)
{
DEBUG_FUNCTION_ENTER;
_InitCommon();
_InitHeader();
*this = other;
}
BMessage::~BMessage()
{
DEBUG_FUNCTION_ENTER;
if (IsSourceWaiting())
SendReply(B_NO_REPLY);
_Clear();
}
BMessage &
BMessage::operator=(const BMessage &other)
{
DEBUG_FUNCTION_ENTER;
_Clear();
fHeader = (message_header *)malloc(sizeof(message_header));
memcpy(fHeader, other.fHeader, sizeof(message_header));
if (fHeader->fields_size > 0) {
fFields = (field_header *)malloc(fHeader->fields_size);
memcpy(fFields, other.fFields, fHeader->fields_size);
}
if (fHeader->data_size > 0) {
fData = (uint8 *)malloc(fHeader->data_size);
memcpy(fData, other.fData, fHeader->data_size);
}
fHeader->shared_area = -1;
fHeader->fields_available = 0;
fHeader->data_available = 0;
fHeader->what = what = other.what;
fQueueLink = other.fQueueLink;
return *this;
}
void *
BMessage::operator new(size_t size)
{
DEBUG_FUNCTION_ENTER2;
void *pointer = sMsgCache->Get(size);
return pointer;
}
void *
BMessage::operator new(size_t, void *pointer)
{
DEBUG_FUNCTION_ENTER2;
return pointer;
}
void
BMessage::operator delete(void *pointer, size_t size)
{
DEBUG_FUNCTION_ENTER2;
sMsgCache->Save(pointer, size);
}
status_t
BMessage::_InitCommon()
{
DEBUG_FUNCTION_ENTER;
what = 0;
fHeader = NULL;
fFields = NULL;
fData = NULL;
fClonedArea = -1;
fOriginal = NULL;
fQueueLink = NULL;
return B_OK;
}
status_t
BMessage::_InitHeader()
{
DEBUG_FUNCTION_ENTER;
fHeader = (message_header *)malloc(sizeof(message_header));
memset(fHeader, 0, sizeof(message_header) - sizeof(message_header::hash_table));
fHeader->format = kMessageMagic4;
fHeader->flags = MESSAGE_FLAG_VALID;
fHeader->what = what;
fHeader->current_specifier = -1;
fHeader->shared_area = -1;
fHeader->target = B_NULL_TOKEN;
fHeader->reply_target = B_NULL_TOKEN;
fHeader->reply_port = -1;
fHeader->reply_team = -1;
// initializing the hash table to -1 because 0 is a valid index
fHeader->hash_table_size = MESSAGE_BODY_HASH_TABLE_SIZE;
memset(&fHeader->hash_table, 255, sizeof(fHeader->hash_table));
return B_OK;
}
status_t
BMessage::_Clear()
{
DEBUG_FUNCTION_ENTER;
if (fClonedArea >= B_OK)
_Dereference();
free(fHeader);
fHeader = NULL;
free(fFields);
fFields = NULL;
free(fData);
fData = NULL;
delete fOriginal;
fOriginal = NULL;
fQueueLink = NULL;
return B_OK;
}
status_t
BMessage::GetInfo(type_code typeRequested, int32 index, char **nameFound,
type_code *typeFound, int32 *countFound = NULL) const
{
DEBUG_FUNCTION_ENTER;
if (typeRequested == B_ANY_TYPE) {
if (index >= fHeader->field_count)
return B_BAD_INDEX;
if (nameFound)
*nameFound = (char *)fData + fFields[index].offset;
if (typeFound)
*typeFound = fFields[index].type;
if (countFound)
*countFound = fFields[index].count;
return B_OK;
}
int32 counter = -1;
field_header *field = fFields;
for (int32 i = 0; i < fHeader->field_count; i++, field++) {
if (field->type == typeRequested)
counter++;
if (counter == index) {
if (nameFound)
*nameFound = (char *)fData + field->offset;
if (typeFound)
*typeFound = field->type;
if (countFound)
*countFound = field->count;
return B_OK;
}
}
if (counter == -1)
return B_BAD_TYPE;
return B_BAD_INDEX;
}
status_t
BMessage::GetInfo(const char *name, type_code *typeFound, int32 *countFound)
const
{
DEBUG_FUNCTION_ENTER;
if (countFound)
*countFound = 0;
field_header *field = NULL;
status_t result = _FindField(name, B_ANY_TYPE, &field);
if (result < B_OK || !field)
return result;
if (typeFound)
*typeFound = field->type;
if (countFound)
*countFound = field->count;
return B_OK;
}
status_t
BMessage::GetInfo(const char *name, type_code *typeFound, bool *fixedSize)
const
{
DEBUG_FUNCTION_ENTER;
field_header *field = NULL;
status_t result = _FindField(name, B_ANY_TYPE, &field);
if (result < B_OK || !field)
return result;
if (typeFound)
*typeFound = field->type;
if (fixedSize)
*fixedSize = field->flags & FIELD_FLAG_FIXED_SIZE;
return B_OK;
}
int32
BMessage::CountNames(type_code type) const
{
DEBUG_FUNCTION_ENTER;
if (type == B_ANY_TYPE)
return fHeader->field_count;
int32 count = 0;
field_header *field = fFields;
for (int32 i = 0; i < fHeader->field_count; i++, field++) {
if (field->type == type)
count++;
}
return count;
}
bool
BMessage::IsEmpty() const
{
DEBUG_FUNCTION_ENTER;
return fHeader->field_count == 0;
}
bool
BMessage::IsSystem() const
{
DEBUG_FUNCTION_ENTER;
char a = char(what >> 24);
char b = char(what >> 16);
char c = char(what >> 8);
char d = char(what);
// The BeBook says:
// ... we've adopted a strict convention for assigning values to all
// Be-defined constants. The value assigned will always be formed by
// combining four characters into a multicharacter constant, with the
// characters limited to uppercase letters and the underbar
// Between that and what's in AppDefs.h, this algo seems like a safe bet:
if (a == '_' && isupper(b) && isupper(c) && isupper(d))
return true;
return false;
}
bool
BMessage::IsReply() const
{
DEBUG_FUNCTION_ENTER;
return fHeader->flags & MESSAGE_FLAG_IS_REPLY;
}
template<typename Type> static void
print_to_stream_type(char* buffer, Type* item, int32 count,
uint32 size)
{
sprintf(buffer + strlen(buffer), "size=%2ld, ", size);
printf("%s", buffer);
memset(buffer, ' ', strlen(buffer));
for (int32 i = 0; i < count; i++, item++) {
if (i > 0) /* indent */
printf(buffer);
printf("data[%ld]: ", i);
item->PrintToStream();
}
}
template<typename Type> static void
print_type(char* buffer, const char* format, Type* item, int32 count,
uint32 size, type_code typeCode)
{
sprintf(buffer + strlen(buffer), "size=%2ld, ", size);
printf("%s", buffer);
memset(buffer, ' ', strlen(buffer));
for (int32 i = 0; i < count; i++, item++) {
if (i > 0) /* indent */
printf(buffer);
printf("data[%ld]: ", i);
Type value = *item;
if (typeCode == B_BOOL_TYPE) {
printf("%s (%d)\n", value != 0 ? "true" : "false", (int8)value);
} else {
if (typeCode == B_INT8_TYPE && value < ' ')
value = ' ';
swap_data(typeCode, &value, sizeof(Type), B_SWAP_BENDIAN_TO_HOST);
printf(format, *item, *item, &value);
}
}
}
void
BMessage::PrintToStream() const
{
DEBUG_FUNCTION_ENTER;
int32 value = B_BENDIAN_TO_HOST_INT32(what);
printf("BMessage: what = '%.4s' (0x%lx, or %ld)\n",
(char *)&value, what, what);
char buffer[1024];
field_header *field = fFields;
for (int32 i = 0; i < fHeader->field_count; i++, field++) {
value = B_BENDIAN_TO_HOST_INT32(field->type);
sprintf(buffer, " entry %14s, type='%.4s', c=%ld, ",
(char *)(fData + field->offset), (char *)&value, field->count);
ssize_t size = 0;
if (field->flags & FIELD_FLAG_FIXED_SIZE)
size = field->data_size / field->count;
switch (field->type) {
case B_RECT_TYPE:
print_to_stream_type<BRect>(buffer,
(BRect *)(fData + field->offset + field->name_length),
field->count, size);
break;
case B_POINT_TYPE:
print_to_stream_type<BPoint>(buffer,
(BPoint *)(fData + field->offset + field->name_length),
field->count, size);
break;
case B_STRING_TYPE:
{
printf("%s", buffer);
memset(buffer, ' ', strlen(buffer));
uint8 *pointer = fData + field->offset + field->name_length;
for (int32 i = 0; i < field->count; i++) {
if (i > 0) /* indent */
printf("%s", buffer);
ssize_t size = *(ssize_t *)pointer;
pointer += sizeof(ssize_t);
printf("size=%2ld, data[%ld]: ", size, i);
printf("\"%s\"\n", (char *)pointer);
pointer += size;
}
break;
}
case B_INT8_TYPE:
print_type<int8>(buffer, "0x%hx (%d \'%.1s\')\n",
(int8 *)(fData + field->offset + field->name_length),
field->count, size, B_INT8_TYPE);
break;
case B_INT16_TYPE:
print_type<int16>(buffer, "0x%x (%d \'%.2s\')\n",
(int16 *)(fData + field->offset + field->name_length),
field->count, size, B_INT16_TYPE);
break;
case B_INT32_TYPE:
print_type<int32>(buffer, "0x%lx (%ld \'%.4s\')\n",
(int32 *)(fData + field->offset + field->name_length),
field->count, size, B_INT32_TYPE);
break;
case B_INT64_TYPE:
print_type<int64>(buffer, "0x%Lx (%Ld \'%.8s\')\n",
(int64 *)(fData + field->offset + field->name_length),
field->count, size, B_INT64_TYPE);
break;
case B_BOOL_TYPE:
print_type<int8>(buffer, NULL,
(int8 *)(fData + field->offset + field->name_length),
field->count, size, B_BOOL_TYPE);
break;
case B_FLOAT_TYPE:
print_type<float>(buffer, "%.4f\n",
(float *)(fData + field->offset + field->name_length),
field->count, size, B_FLOAT_TYPE);
break;
case B_DOUBLE_TYPE:
print_type<double>(buffer, "%.8f\n",
(double *)(fData + field->offset + field->name_length),
field->count, size, B_DOUBLE_TYPE);
break;
case B_REF_TYPE: {
printf("%s", buffer);
memset(buffer, ' ', strlen(buffer));
uint8 *pointer = fData + field->offset + field->name_length;
for (int32 i = 0; i < field->count; i++) {
if (i > 0) /* indent */
printf("%s", buffer);
ssize_t size = *(ssize_t *)pointer;
pointer += sizeof(ssize_t);
entry_ref ref;
BPrivate::entry_ref_unflatten(&ref, (char *)pointer, size);
printf("size=%2ld, data[%ld]: ", size, i);
printf("device=%ld, directory=%lld, name=\"%s\", ",
ref.device, ref.directory, ref.name);
BPath path(&ref);
printf("path=\"%s\"\n", path.Path());
pointer += size;
}
break;
}
default: {
sprintf(buffer + strlen(buffer), "size=%2ld, \n", size);
printf("%s", buffer);
}
}
}
}
status_t
BMessage::Rename(const char *oldEntry, const char *newEntry)
{
DEBUG_FUNCTION_ENTER;
if (!oldEntry || !newEntry)
return B_BAD_VALUE;
if (fClonedArea >= B_OK)
_CopyForWrite();
uint32 hash = _HashName(oldEntry) % fHeader->hash_table_size;
int32 *nextField = &fHeader->hash_table[hash];
while (*nextField >= 0) {
field_header *field = &fFields[*nextField];
if (strncmp((const char *)(fData + field->offset), oldEntry,
field->name_length) == 0) {
// nextField points to the field for oldEntry, save it and unlink
int32 index = *nextField;
*nextField = field->next_field;
field->next_field = -1;
hash = _HashName(newEntry) % fHeader->hash_table_size;
nextField = &fHeader->hash_table[hash];
while (*nextField >= 0)
nextField = &fFields[*nextField].next_field;
*nextField = index;
int32 oldLength = field->name_length;
field->name_length = strlen(newEntry) + 1;
_ResizeData(field->offset, field->name_length - oldLength);
memcpy(fData + field->offset, newEntry, field->name_length);
return B_OK;
}
nextField = &field->next_field;
}
return B_NAME_NOT_FOUND;
}
bool
BMessage::WasDelivered() const
{
DEBUG_FUNCTION_ENTER;
return fHeader->flags & MESSAGE_FLAG_WAS_DELIVERED;
}
bool
BMessage::IsSourceWaiting() const
{
DEBUG_FUNCTION_ENTER;
return (fHeader->flags & MESSAGE_FLAG_REPLY_REQUIRED)
&& !(fHeader->flags & MESSAGE_FLAG_REPLY_DONE);
}
bool
BMessage::IsSourceRemote() const
{
DEBUG_FUNCTION_ENTER;
return (fHeader->flags & MESSAGE_FLAG_WAS_DELIVERED)
&& (fHeader->reply_team != BPrivate::current_team());
}
BMessenger
BMessage::ReturnAddress() const
{
DEBUG_FUNCTION_ENTER;
if (fHeader->flags & MESSAGE_FLAG_WAS_DELIVERED) {
BMessenger messenger;
BMessenger::Private(messenger).SetTo(fHeader->reply_team,
fHeader->reply_port, fHeader->reply_target);
return messenger;
}
return BMessenger();
}
const BMessage *
BMessage::Previous() const
{
DEBUG_FUNCTION_ENTER;
/* ToDo: test if the "_previous_" field is used in R5 */
if (!fOriginal) {
fOriginal = new BMessage();
if (FindMessage("_previous_", fOriginal) != B_OK) {
delete fOriginal;
fOriginal = NULL;
}
}
return fOriginal;
}
bool
BMessage::WasDropped() const
{
DEBUG_FUNCTION_ENTER;
return fHeader->flags & MESSAGE_FLAG_WAS_DROPPED;
}
BPoint
BMessage::DropPoint(BPoint *offset = NULL) const
{
DEBUG_FUNCTION_ENTER;
if (offset)
*offset = FindPoint("_drop_offset_");
return FindPoint("_drop_point_");
}
status_t
BMessage::SendReply(uint32 command, BHandler *replyTo)
{
DEBUG_FUNCTION_ENTER;
BMessage message(command);
return SendReply(&message, replyTo);
}
status_t
BMessage::SendReply(BMessage *reply, BHandler *replyTo, bigtime_t timeout)
{
DEBUG_FUNCTION_ENTER;
BMessenger messenger(replyTo);
return SendReply(reply, messenger, timeout);
}
status_t
BMessage::SendReply(BMessage *reply, BMessenger replyTo, bigtime_t timeout)
{
DEBUG_FUNCTION_ENTER;
BMessenger messenger;
BMessenger::Private messengerPrivate(messenger);
messengerPrivate.SetTo(fHeader->reply_team, fHeader->reply_port,
fHeader->reply_target);
if (fHeader->flags & MESSAGE_FLAG_REPLY_REQUIRED) {
if (fHeader->flags & MESSAGE_FLAG_REPLY_DONE)
return B_DUPLICATE_REPLY;
fHeader->flags |= MESSAGE_FLAG_REPLY_DONE;
reply->fHeader->flags |= MESSAGE_FLAG_IS_REPLY;
status_t result = messenger.SendMessage(reply, replyTo, timeout);
reply->fHeader->flags &= ~MESSAGE_FLAG_IS_REPLY;
if (result != B_OK) {
if (set_port_owner(messengerPrivate.Port(),
messengerPrivate.Team()) == B_BAD_TEAM_ID) {
delete_port(messengerPrivate.Port());
}
}
return result;
}
// no reply required
if (!(fHeader->flags & MESSAGE_FLAG_WAS_DELIVERED))
return B_BAD_REPLY;
reply->AddMessage("_previous_", this);
reply->fHeader->flags |= MESSAGE_FLAG_IS_REPLY;
status_t result = messenger.SendMessage(reply, replyTo, timeout);
reply->fHeader->flags &= ~MESSAGE_FLAG_IS_REPLY;
reply->RemoveName("_previous_");
return result;
}
status_t
BMessage::SendReply(uint32 command, BMessage *replyToReply)
{
DEBUG_FUNCTION_ENTER;
BMessage message(command);
return SendReply(&message, replyToReply);
}
status_t
BMessage::SendReply(BMessage *reply, BMessage *replyToReply,
bigtime_t sendTimeout, bigtime_t replyTimeout)
{
DEBUG_FUNCTION_ENTER;
BMessenger messenger;
BMessenger::Private messengerPrivate(messenger);
messengerPrivate.SetTo(fHeader->reply_team, fHeader->reply_port,
fHeader->reply_target);
if (fHeader->flags & MESSAGE_FLAG_REPLY_REQUIRED) {
if (fHeader->flags & MESSAGE_FLAG_REPLY_DONE)
return B_DUPLICATE_REPLY;
fHeader->flags |= MESSAGE_FLAG_REPLY_DONE;
reply->fHeader->flags |= MESSAGE_FLAG_IS_REPLY;
status_t result = messenger.SendMessage(reply, replyToReply,
sendTimeout, replyTimeout);
reply->fHeader->flags &= ~MESSAGE_FLAG_IS_REPLY;
if (result != B_OK) {
if (set_port_owner(messengerPrivate.Port(),
messengerPrivate.Team()) == B_BAD_TEAM_ID) {
delete_port(messengerPrivate.Port());
}
}
return result;
}
// no reply required
if (!(fHeader->flags & MESSAGE_FLAG_WAS_DELIVERED))
return B_BAD_REPLY;
reply->AddMessage("_previous_", this);
reply->fHeader->flags |= MESSAGE_FLAG_IS_REPLY;
status_t result = messenger.SendMessage(reply, replyToReply, sendTimeout,
replyTimeout);
reply->fHeader->flags &= ~MESSAGE_FLAG_IS_REPLY;
reply->RemoveName("_previous_");
return result;
}
ssize_t
BMessage::FlattenedSize() const
{
DEBUG_FUNCTION_ENTER;
return BPrivate::r5_message_flattened_size(this);
}
status_t
BMessage::Flatten(char *buffer, ssize_t size) const
{
DEBUG_FUNCTION_ENTER;
return BPrivate::flatten_r5_message(this, buffer, size);
}
status_t
BMessage::Flatten(BDataIO *stream, ssize_t *size) const
{
DEBUG_FUNCTION_ENTER;
return BPrivate::flatten_r5_message(this, stream, size);
}
ssize_t
BMessage::_NativeFlattenedSize() const
{
DEBUG_FUNCTION_ENTER;
return sizeof(message_header) + fHeader->fields_size + fHeader->data_size;
}
status_t
BMessage::_NativeFlatten(char *buffer, ssize_t size) const
{
DEBUG_FUNCTION_ENTER;
if (!buffer)
return B_BAD_VALUE;
if (!fHeader)
return B_NO_INIT;
/* we have to sync the what code as it is a public member */
fHeader->what = what;
//fHeader->fields_checksum = BPrivate::CalculateChecksum((uint8 *)fFields, fHeader->fields_size);
//fHeader->data_checksum = BPrivate::CalculateChecksum((uint8 *)fData, fHeader->data_size);
memcpy(buffer, fHeader, min_c(sizeof(message_header), (size_t)size));
buffer += sizeof(message_header);
size -= sizeof(message_header);
memcpy(buffer, fFields, min_c(fHeader->fields_size, size));
buffer += fHeader->fields_size;
size -= fHeader->fields_size;
memcpy(buffer, fData, min_c(fHeader->data_size, size));
if (size >= fHeader->data_size)
return B_OK;
return B_NO_MEMORY;
}
status_t
BMessage::_NativeFlatten(BDataIO *stream, ssize_t *size) const
{
DEBUG_FUNCTION_ENTER;
if (!stream)
return B_BAD_VALUE;
if (!fHeader)
return B_NO_INIT;
/* we have to sync the what code as it is a public member */
fHeader->what = what;
//fHeader->fields_checksum = BPrivate::CalculateChecksum((uint8 *)fFields, fHeader->fields_size);
//fHeader->data_checksum = BPrivate::CalculateChecksum((uint8 *)fData, fHeader->data_size);
ssize_t result1 = stream->Write(fHeader, sizeof(message_header));
if (result1 != sizeof(message_header))
return (result1 >= 0 ? B_ERROR : result1);
ssize_t result2 = 0;
if (fHeader->fields_size > 0) {
stream->Write(fFields, fHeader->fields_size);
if (result2 != fHeader->fields_size)
return (result2 >= 0 ? B_ERROR : result2);
}
ssize_t result3 = 0;
if (fHeader->data_size > 0) {
stream->Write(fData, fHeader->data_size);
if (result3 != fHeader->data_size)
return (result3 >= 0 ? B_ERROR : result3);
}
if (size)
*size = result1 + result2 + result3;
return B_OK;
}
/* The concept of message sending by area:
The traditional way of sending a message is to send it by flattening it to
a buffer, pushing it through a port, reading it into the outputbuffer and
unflattening it from there (copying the data again). While this works ok
for small messages it does not make any sense for larger ones and may even
hit some port capacity limit.
Often in the life of a BMessage, it will be sent to someone. Almost as
often the one receiving the message will not need to change the message
in any way, but uses it "read only" to get information from it. This means
that all that copying is pretty pointless in the first place since we
could simply pass the original buffers on.
It's obviously not exactly as simple as this, since we cannot just use the
memory of one application in another - but we can share areas with
eachother.
Therefore instead of flattening into a buffer, we copy the message data
into an area, put this information into the message header and only push
this through the port. The receiving looper then builds a BMessage from
the header, that only references the data in the area (not copying it),
allowing read only access to it.
Only if write access is necessary the message will be copyed from the area
to its own buffers (like in the unflatten step before).
The double copying is reduced to a single copy in most cases and we safe
the slower route of moving the data through a port.
Additionally we save us the reference counting with the use of areas that
are reference counted internally. So we don't have to worry about leaving
an area behind or deleting one that is still in use.
*/
status_t
BMessage::_FlattenToArea(message_header **_header) const
{
DEBUG_FUNCTION_ENTER;
message_header *header = (message_header *)malloc(sizeof(message_header));
memcpy(header, fHeader, sizeof(message_header));
header->what = what;
header->fields_available = 0;
header->data_available = 0;
header->flags |= MESSAGE_FLAG_PASS_BY_AREA;
*_header = header;
if (header->shared_area >= B_OK)
return B_OK;
if (header->fields_size == 0 && header->data_size == 0)
return B_OK;
uint8 *address = NULL;
ssize_t size = header->fields_size + header->data_size;
size = (size + B_PAGE_SIZE) & ~(B_PAGE_SIZE - 1);
area_id area = create_area("Shared BMessage data", (void **)&address,
B_ANY_ADDRESS, size, B_NO_LOCK, B_READ_AREA | B_WRITE_AREA);
if (area < B_OK) {
free(header);
*_header = NULL;
return area;
}
if (header->fields_size > 0) {
memcpy(address, fFields, header->fields_size);
header->fields_checksum = BPrivate::CalculateChecksum((uint8 *)address, header->fields_size);
address += header->fields_size;
}
if (header->data_size > 0) {
memcpy(address, fData, header->data_size);
header->data_checksum = BPrivate::CalculateChecksum((uint8 *)address, header->data_size);
}
header->shared_area = area;
return B_OK;
}
status_t
BMessage::_Reference(message_header *header)
{
DEBUG_FUNCTION_ENTER;
fHeader = header;
fHeader->flags &= ~MESSAGE_FLAG_PASS_BY_AREA;
/* if there is no data at all we don't need the area */
if (fHeader->fields_size == 0 && header->data_size == 0)
return B_OK;
uint8 *address = NULL;
area_id clone = clone_area("Cloned BMessage data", (void **)&address,
B_ANY_ADDRESS, B_READ_AREA, fHeader->shared_area);
if (clone < B_OK) {
free(fHeader);
fHeader = NULL;
_InitHeader();
return clone;
}
fClonedArea = clone;
fFields = (field_header *)address;
address += fHeader->fields_size;
fData = address;
return B_OK;
}
status_t
BMessage::_Dereference()
{
DEBUG_FUNCTION_ENTER;
delete_area(fClonedArea);
fClonedArea = -1;
fFields = NULL;
fData = NULL;
return B_OK;
}
status_t
BMessage::_CopyForWrite()
{
DEBUG_FUNCTION_ENTER;
if (fClonedArea < B_OK)
return B_OK;
field_header *newFields = NULL;
uint8 *newData = NULL;
if (fHeader->fields_size > 0) {
newFields = (field_header *)malloc(fHeader->fields_size);
memcpy(newFields, fFields, fHeader->fields_size);
}
if (fHeader->data_size > 0) {
newData = (uint8 *)malloc(fHeader->data_size);
memcpy(newData, fData, fHeader->data_size);
}
_Dereference();
fHeader->fields_available = 0;
fHeader->data_available = 0;
fFields = newFields;
fData = newData;
return B_OK;
}
status_t
BMessage::Unflatten(const char *flatBuffer)
{
DEBUG_FUNCTION_ENTER;
if (!flatBuffer)
return B_BAD_VALUE;
uint32 format = *(uint32 *)flatBuffer;
if (format != kMessageMagic4) {
if (format == KMessage::kMessageHeaderMagic) {
KMessage message;
status_t result = message.SetTo(flatBuffer,
((KMessage::Header*)flatBuffer)->size);
if (result != B_OK)
return result;
return convert_message(&message, this);
}
if (format == kMessageMagicR5 || format == kMessageMagicR5Swapped)
return BPrivate::unflatten_r5_message(format, this, flatBuffer);
if (format == kMessageMagicDano || format == kMessageMagicDanoSwapped) {
BMemoryIO stream(flatBuffer + sizeof(uint32),
BPrivate::dano_message_flattened_size(flatBuffer));
return BPrivate::unflatten_dano_message(format, stream, *this);
}
return B_NOT_A_MESSAGE;
}
// native message unflattening
_Clear();
fHeader = (message_header *)malloc(sizeof(message_header));
if (!fHeader)
return B_NO_MEMORY;
memcpy(fHeader, flatBuffer, sizeof(message_header));
flatBuffer += sizeof(message_header);
if (fHeader->format != kMessageMagic4
|| !(fHeader->flags & MESSAGE_FLAG_VALID)) {
free(fHeader);
fHeader = NULL;
_InitHeader();
return B_BAD_VALUE;
}
fHeader->fields_available = 0;
fHeader->data_available = 0;
what = fHeader->what;
if (fHeader->flags & MESSAGE_FLAG_PASS_BY_AREA) {
status_t result = _Reference(fHeader);
if (result < B_OK)
return result;
} else {
fHeader->shared_area = -1;
if (fHeader->fields_size > 0) {
fFields = (field_header *)malloc(fHeader->fields_size);
if (!fFields)
return B_NO_MEMORY;
memcpy(fFields, flatBuffer, fHeader->fields_size);
flatBuffer += fHeader->fields_size;
}
if (fHeader->data_size > 0) {
fData = (uint8 *)malloc(fHeader->data_size);
if (!fData)
return B_NO_MEMORY;
memcpy(fData, flatBuffer, fHeader->data_size);
}
}
/*if (fHeader->fields_checksum != BPrivate::CalculateChecksum((uint8 *)fFields, fHeader->fields_size)
|| fHeader->data_checksum != BPrivate::CalculateChecksum((uint8 *)fData, fHeader->data_size)) {
debug_printf("checksum mismatch 1\n");
_Clear();
_InitHeader();
return B_BAD_VALUE;
}*/
return B_OK;
}
status_t
BMessage::Unflatten(BDataIO *stream)
{
DEBUG_FUNCTION_ENTER;
if (!stream)
return B_BAD_VALUE;
uint32 format = 0;
stream->Read(&format, sizeof(uint32));
if (format != kMessageMagic4) {
if (format == kMessageMagicR5 || format == kMessageMagicR5Swapped)
return BPrivate::unflatten_r5_message(format, this, stream);
if (format == kMessageMagicDano || format == kMessageMagicDanoSwapped)
return BPrivate::unflatten_dano_message(format, *stream, *this);
return B_NOT_A_MESSAGE;
}
// native message unflattening
_Clear();
fHeader = (message_header *)malloc(sizeof(message_header));
if (!fHeader)
return B_NO_MEMORY;
fHeader->format = format;
uint8 *header = (uint8 *)fHeader;
ssize_t result = stream->Read(header + sizeof(uint32),
sizeof(message_header) - sizeof(uint32));
result -= sizeof(message_header) - sizeof(uint32);
if (result != B_OK || fHeader->format != kMessageMagic4
|| !(fHeader->flags & MESSAGE_FLAG_VALID)) {
free(fHeader);
fHeader = NULL;
_InitHeader();
return B_BAD_VALUE;
}
fHeader->fields_available = 0;
fHeader->data_available = 0;
what = fHeader->what;
if (fHeader->flags & MESSAGE_FLAG_PASS_BY_AREA) {
result = _Reference(fHeader);
if (result < B_OK)
return result;
} else {
fHeader->shared_area = -1;
if (result == B_OK && fHeader->fields_size > 0) {
fFields = (field_header *)malloc(fHeader->fields_size);
if (!fFields)
return B_NO_MEMORY;
result = stream->Read(fFields, fHeader->fields_size);
result -= fHeader->fields_size;
}
if (result == B_OK && fHeader->data_size > 0) {
fData = (uint8 *)malloc(fHeader->data_size);
if (!fData)
return B_NO_MEMORY;
result = stream->Read(fData, fHeader->data_size);
result -= fHeader->data_size;
}
if (result < B_OK)
return B_BAD_VALUE;
}
/*if (fHeader->fields_checksum != BPrivate::CalculateChecksum((uint8 *)fFields, fHeader->fields_size)
|| fHeader->data_checksum != BPrivate::CalculateChecksum((uint8 *)fData, fHeader->data_size)) {
debug_printf("checksum mismatch 2\n");
_Clear();
_InitHeader();
return B_BAD_VALUE;
}*/
return B_OK;
}
status_t
BMessage::AddSpecifier(const char *property)
{
DEBUG_FUNCTION_ENTER;
BMessage message(B_DIRECT_SPECIFIER);
status_t result = message.AddString(B_PROPERTY_ENTRY, property);
if (result < B_OK)
return result;
return AddSpecifier(&message);
}
status_t
BMessage::AddSpecifier(const char *property, int32 index)
{
DEBUG_FUNCTION_ENTER;
BMessage message(B_INDEX_SPECIFIER);
status_t result = message.AddString(B_PROPERTY_ENTRY, property);
if (result < B_OK)
return result;
result = message.AddInt32("index", index);
if (result < B_OK)
return result;
return AddSpecifier(&message);
}
status_t
BMessage::AddSpecifier(const char *property, int32 index, int32 range)
{
DEBUG_FUNCTION_ENTER;
if (range < 0)
return B_BAD_VALUE;
BMessage message(B_RANGE_SPECIFIER);
status_t result = message.AddString(B_PROPERTY_ENTRY, property);
if (result < B_OK)
return result;
result = message.AddInt32("index", index);
if (result < B_OK)
return result;
result = message.AddInt32("range", range);
if (result < B_OK)
return result;
return AddSpecifier(&message);
}
status_t
BMessage::AddSpecifier(const char *property, const char *name)
{
DEBUG_FUNCTION_ENTER;
BMessage message(B_NAME_SPECIFIER);
status_t result = message.AddString(B_PROPERTY_ENTRY, property);
if (result < B_OK)
return result;
result = message.AddString(B_PROPERTY_NAME_ENTRY, name);
if (result < B_OK)
return result;
return AddSpecifier(&message);
}
status_t
BMessage::AddSpecifier(const BMessage *specifier)
{
DEBUG_FUNCTION_ENTER;
status_t result = AddMessage(B_SPECIFIER_ENTRY, specifier);
if (result < B_OK)
return result;
fHeader->current_specifier++;
fHeader->flags |= MESSAGE_FLAG_HAS_SPECIFIERS;
return B_OK;
}
status_t
BMessage::SetCurrentSpecifier(int32 index)
{
DEBUG_FUNCTION_ENTER;
if (index < 0)
return B_BAD_INDEX;
type_code type;
int32 count;
status_t result = GetInfo(B_SPECIFIER_ENTRY, &type, &count);
if (result < B_OK)
return result;
if (index > count)
return B_BAD_INDEX;
fHeader->current_specifier = index;
return B_OK;
}
status_t
BMessage::GetCurrentSpecifier(int32 *index, BMessage *specifier, int32 *what,
const char **property) const
{
DEBUG_FUNCTION_ENTER;
if (fHeader->current_specifier < 0
|| !(fHeader->flags & MESSAGE_FLAG_WAS_DELIVERED))
return B_BAD_SCRIPT_SYNTAX;
if (index)
*index = fHeader->current_specifier;
if (specifier) {
if (FindMessage(B_SPECIFIER_ENTRY, fHeader->current_specifier,
specifier) < B_OK)
return B_BAD_SCRIPT_SYNTAX;
if (what)
*what = specifier->what;
if (property) {
if (specifier->FindString(B_PROPERTY_ENTRY, property) < B_OK)
return B_BAD_SCRIPT_SYNTAX;
}
}
return B_OK;
}
bool
BMessage::HasSpecifiers() const
{
DEBUG_FUNCTION_ENTER;
return fHeader->flags & MESSAGE_FLAG_HAS_SPECIFIERS;
}
status_t
BMessage::PopSpecifier()
{
DEBUG_FUNCTION_ENTER;
if (fHeader->current_specifier < 0 ||
!(fHeader->flags & MESSAGE_FLAG_WAS_DELIVERED))
return B_BAD_VALUE;
if (fHeader->current_specifier >= 0)
fHeader->current_specifier--;
return B_OK;
}
status_t
BMessage::_ResizeData(int32 offset, int32 change)
{
if (change == 0)
return B_OK;
/* optimize for the most usual case: appending data */
if (offset < fHeader->data_size) {
field_header *field = fFields;
for (int32 i = 0; i < fHeader->field_count; i++, field++) {
if (field->offset >= offset)
field->offset += change;
}
}
if (change > 0) {
if (fHeader->data_available >= change) {
if (offset < fHeader->data_size) {
ssize_t length = fHeader->data_size - offset;
memmove(fData + offset + change, fData + offset, length);
}
fHeader->data_available -= change;
fHeader->data_size += change;
return B_OK;
}
ssize_t size = fHeader->data_size * 2;
size = min_c(size, fHeader->data_size + MAX_DATA_PREALLOCATION);
size = max_c(size, fHeader->data_size + change);
fData = (uint8 *)realloc(fData, size);
if (!fData)
return B_NO_MEMORY;
if (offset < fHeader->data_size) {
memmove(fData + offset + change, fData + offset,
fHeader->data_size - offset - change);
}
fHeader->data_size += change;
fHeader->data_available = size - fHeader->data_size;
} else {
ssize_t size = fHeader->data_size;
memmove(fData + offset, fData + offset - change, size - offset + change);
fHeader->data_size += change;
fHeader->data_available -= change;
if (fHeader->data_available > MAX_DATA_PREALLOCATION) {
ssize_t available = MAX_DATA_PREALLOCATION / 2;
fData = (uint8 *)realloc(fData, fHeader->data_size + available);
if (!fData)
return B_NO_MEMORY;
fHeader->data_available = available;
}
}
return B_OK;
}
uint32
BMessage::_HashName(const char *name) const
{
char ch;
uint32 result = 0;
while ((ch = *name++) != 0) {
result = (result << 7) ^ (result >> 24);
result ^= ch;
}
result ^= result << 12;
return result;
}
status_t
BMessage::_FindField(const char *name, type_code type, field_header **result) const
{
if (!name)
return B_BAD_VALUE;
if (!fHeader || !fFields || !fData)
return B_NAME_NOT_FOUND;
uint32 hash = _HashName(name) % fHeader->hash_table_size;
int32 nextField = fHeader->hash_table[hash];
while (nextField >= 0) {
field_header *field = &fFields[nextField];
if (strncmp((const char *)(fData + field->offset), name,
field->name_length) == 0) {
if (type != B_ANY_TYPE && field->type != type)
return B_BAD_TYPE;
*result = field;
return B_OK;
}
nextField = field->next_field;
}
return B_NAME_NOT_FOUND;
}
status_t
BMessage::_AddField(const char *name, type_code type, bool isFixedSize,
field_header **result)
{
if (!fHeader)
return B_ERROR;
if (fHeader->fields_available <= 0) {
int32 count = fHeader->field_count * 2 + 1;
count = min_c(count, fHeader->field_count + MAX_FIELD_PREALLOCATION);
fFields = (field_header *)realloc(fFields, count * sizeof(field_header));
if (!fFields)
return B_NO_MEMORY;
fHeader->fields_available = count - fHeader->field_count;
}
uint32 hash = _HashName(name) % fHeader->hash_table_size;
int32 *nextField = &fHeader->hash_table[hash];
while (*nextField >= 0)
nextField = &fFields[*nextField].next_field;
*nextField = fHeader->field_count;
field_header *field = &fFields[fHeader->field_count];
field->type = type;
field->flags = FIELD_FLAG_VALID;
if (isFixedSize)
field->flags |= FIELD_FLAG_FIXED_SIZE;
field->count = 0;
field->data_size = 0;
field->allocated = 0;
field->next_field = -1;
field->offset = fHeader->data_size;
field->name_length = strlen(name) + 1;
_ResizeData(field->offset, field->name_length);
memcpy(fData + field->offset, name, field->name_length);
fHeader->fields_available--;
fHeader->fields_size += sizeof(field_header);
fHeader->field_count++;
*result = field;
return B_OK;
}
status_t
BMessage::_RemoveField(field_header *field)
{
int32 index = ((uint8 *)field - (uint8 *)fFields) / sizeof(field_header);
int32 nextField = field->next_field;
if (nextField > index)
nextField--;
int32 *value = fHeader->hash_table;
for (int32 i = 0; i < fHeader->hash_table_size; i++, value++) {
if (*value > index)
*value -= 1;
else if (*value == index)
*value = nextField;
}
field_header *other = fFields;
for (int32 i = 0; i < fHeader->field_count; i++, other++) {
if (other->next_field > index)
other->next_field--;
else if (other->next_field == index)
other->next_field = nextField;
}
_ResizeData(field->offset, -(field->data_size + field->name_length));
ssize_t size = fHeader->fields_size - (index + 1) * sizeof(field_header);
memmove(fFields + index, fFields + index + 1, size);
fHeader->fields_size -= sizeof(field_header);
fHeader->field_count--;
fHeader->fields_available++;
if (fHeader->fields_available > MAX_FIELD_PREALLOCATION) {
ssize_t available = MAX_FIELD_PREALLOCATION / 2;
fFields = (field_header *)realloc(fFields, fHeader->fields_size
+ available * sizeof(field_header));
if (!fFields)
return B_NO_MEMORY;
fHeader->fields_available = available;
}
return B_OK;
}
status_t
BMessage::AddData(const char *name, type_code type, const void *data,
ssize_t numBytes, bool isFixedSize, int32 count)
{
DEBUG_FUNCTION_ENTER;
if (numBytes <= 0 || !data)
return B_BAD_VALUE;
if (fClonedArea >= B_OK)
_CopyForWrite();
field_header *field = NULL;
status_t result = _FindField(name, type, &field);
if (result == B_NAME_NOT_FOUND)
result = _AddField(name, type, isFixedSize, &field);
if (result < B_OK || !field)
return result;
uint32 offset = field->offset + field->name_length + field->data_size;
if (field->flags & FIELD_FLAG_FIXED_SIZE) {
if (field->count) {
ssize_t size = field->data_size / field->count;
if (size != numBytes)
return B_BAD_VALUE;
}
_ResizeData(offset, numBytes);
memcpy(fData + offset, data, numBytes);
field->data_size += numBytes;
} else {
int32 change = numBytes + sizeof(numBytes);
_ResizeData(offset, change);
memcpy(fData + offset, &numBytes, sizeof(numBytes));
memcpy(fData + offset + sizeof(numBytes), data, numBytes);
field->data_size += change;
}
field->count++;
return B_OK;
}
status_t
BMessage::RemoveData(const char *name, int32 index)
{
DEBUG_FUNCTION_ENTER;
if (index < 0)
return B_BAD_VALUE;
if (fClonedArea >= B_OK)
_CopyForWrite();
field_header *field = NULL;
status_t result = _FindField(name, B_ANY_TYPE, &field);
if (result < B_OK)
return result;
if (!field)
return B_ERROR;
if (index >= field->count)
return B_BAD_INDEX;
if (field->count == 1)
return _RemoveField(field);
uint32 offset = field->offset + field->name_length;
if (field->flags & FIELD_FLAG_FIXED_SIZE) {
ssize_t size = field->data_size / field->count;
_ResizeData(offset + index * size, -size);
field->data_size -= size;
} else {
uint8 *pointer = fData + offset;
for (int32 i = 0; i < index; i++) {
offset += *(ssize_t *)pointer + sizeof(ssize_t);
pointer = fData + offset;
}
ssize_t currentSize = *(ssize_t *)pointer + sizeof(ssize_t);
_ResizeData(offset, -currentSize);
field->data_size -= currentSize;
}
field->count--;
return B_OK;
}
status_t
BMessage::RemoveName(const char *name)
{
DEBUG_FUNCTION_ENTER;
if (fClonedArea >= B_OK)
_CopyForWrite();
field_header *field = NULL;
status_t result = _FindField(name, B_ANY_TYPE, &field);
if (result < B_OK)
return result;
if (!field)
return B_ERROR;
return _RemoveField(field);
}
status_t
BMessage::MakeEmpty()
{
DEBUG_FUNCTION_ENTER;
_Clear();
_InitHeader();
return B_OK;
}
status_t
BMessage::FindData(const char *name, type_code type, int32 index,
const void **data, ssize_t *numBytes) const
{
DEBUG_FUNCTION_ENTER;
if (!data || !numBytes)
return B_BAD_VALUE;
*data = NULL;
field_header *field = NULL;
status_t result = _FindField(name, type, &field);
if (result < B_OK)
return result;
if (!field)
return B_ERROR;
if (index >= field->count)
return B_BAD_INDEX;
if (field->flags & FIELD_FLAG_FIXED_SIZE) {
*numBytes = field->data_size / field->count;
*data = fData + field->offset + field->name_length + index * *numBytes;
} else {
uint8 *pointer = fData + field->offset + field->name_length;
for (int32 i = 0; i < index; i++)
pointer += *(ssize_t *)pointer + sizeof(ssize_t);
*numBytes = *(ssize_t *)pointer;
*data = pointer + sizeof(ssize_t);
}
return B_OK;
}
status_t
BMessage::ReplaceData(const char *name, type_code type, int32 index,
const void *data, ssize_t numBytes)
{
DEBUG_FUNCTION_ENTER;
if (numBytes <= 0 || !data)
return B_BAD_VALUE;
if (fClonedArea >= B_OK)
_CopyForWrite();
field_header *field = NULL;
status_t result = _FindField(name, type, &field);
if (result < B_OK)
return result;
if (!field)
return B_ERROR;
if (index >= field->count)
return B_BAD_INDEX;
if (field->flags & FIELD_FLAG_FIXED_SIZE) {
ssize_t size = field->data_size / field->count;
if (size != numBytes)
return B_BAD_VALUE;
memcpy(fData + field->offset + field->name_length + index * size, data,
size);
} else {
uint32 offset = field->offset + field->name_length;
uint8 *pointer = fData + offset;
for (int32 i = 0; i < index; i++) {
offset += *(ssize_t *)pointer + sizeof(ssize_t);
pointer = fData + offset;
}
ssize_t currentSize = *(ssize_t *)pointer;
int32 change = numBytes - currentSize;
_ResizeData(offset, change);
memcpy(fData + offset, &numBytes, sizeof(numBytes));
memcpy(fData + offset + sizeof(numBytes), data, numBytes);
field->data_size += change;
}
return B_OK;
}
bool
BMessage::HasData(const char *name, type_code type, int32 index) const
{
DEBUG_FUNCTION_ENTER;
field_header *field = NULL;
status_t result = _FindField(name, type, &field);
if (result < B_OK)
return false;
if (!field)
return false;
if (index >= field->count)
return false;
return true;
}
/* Static functions for cache initialization and cleanup */
void
BMessage::_StaticInit()
{
DEBUG_FUNCTION_ENTER2;
sReplyPorts[0] = create_port(1, "tmp_rport0");
sReplyPorts[1] = create_port(1, "tmp_rport1");
sReplyPorts[2] = create_port(1, "tmp_rport2");
sReplyPortInUse[0] = 0;
sReplyPortInUse[1] = 0;
sReplyPortInUse[2] = 0;
sMsgCache = new BBlockCache(20, sizeof(BMessage), B_OBJECT_CACHE);
}
void
BMessage::_StaticCleanup()
{
DEBUG_FUNCTION_ENTER2;
delete_port(sReplyPorts[0]);
sReplyPorts[0] = -1;
delete_port(sReplyPorts[1]);
sReplyPorts[1] = -1;
delete_port(sReplyPorts[2]);
sReplyPorts[2] = -1;
}
void
BMessage::_StaticCacheCleanup()
{
DEBUG_FUNCTION_ENTER2;
delete sMsgCache;
sMsgCache = NULL;
}
int32
BMessage::_StaticGetCachedReplyPort()
{
DEBUG_FUNCTION_ENTER2;
int index = -1;
for (int32 i = 0; i < sNumReplyPorts; i++) {
int32 old = atomic_add(&(sReplyPortInUse[i]), 1);
if (old == 0) {
// This entry is free
index = i;
break;
} else {
// This entry is being used.
atomic_add(&(sReplyPortInUse[i]), -1);
}
}
return index;
}
status_t
BMessage::_SendMessage(port_id port, int32 token, bigtime_t timeout,
bool replyRequired, BMessenger &replyTo) const
{
DEBUG_FUNCTION_ENTER;
ssize_t size = 0;
char *buffer = NULL;
message_header *header = NULL;
status_t result;
if (/*fHeader->fields_size + fHeader->data_size > B_PAGE_SIZE*/false) {
result = _FlattenToArea(&header);
buffer = (char *)header;
size = sizeof(message_header);
port_info info;
get_port_info(port, &info);
void *address = NULL;
_kern_transfer_area(header->shared_area, &address, B_ANY_ADDRESS,
info.team);
} else {
size = _NativeFlattenedSize();
buffer = (char *)malloc(size);
result = _NativeFlatten(buffer, size);
header = (message_header *)buffer;
}
if (result < B_OK)
return result;
if (!replyTo.IsValid()) {
BMessenger::Private(replyTo).SetTo(header->reply_team,
header->reply_port, header->reply_target);
if (!replyTo.IsValid())
replyTo = be_app_messenger;
}
BMessenger::Private replyToPrivate(replyTo);
if (replyRequired)
header->flags |= MESSAGE_FLAG_REPLY_REQUIRED;
else
header->flags &= ~MESSAGE_FLAG_REPLY_REQUIRED;
header->target = token;
header->reply_team = replyToPrivate.Team();
header->reply_port = replyToPrivate.Port();
header->reply_target = replyToPrivate.Token();
header->flags |= MESSAGE_FLAG_WAS_DELIVERED;
do {
result = write_port_etc(port, kPortMessageCode, (void *)buffer, size,
B_RELATIVE_TIMEOUT, timeout);
} while (result == B_INTERRUPTED);
free(buffer);
return result;
}
status_t
BMessage::_SendMessage(port_id port, team_id portOwner, int32 token,
BMessage *reply, bigtime_t sendTimeout, bigtime_t replyTimeout) const
{
DEBUG_FUNCTION_ENTER;
const int32 cachedReplyPort = _StaticGetCachedReplyPort();
port_id replyPort = B_BAD_PORT_ID;
status_t result = B_OK;
if (cachedReplyPort < B_OK) {
// All the cached reply ports are in use; create a new one
replyPort = create_port(1 /* for one message */, "tmp_reply_port");
if (replyPort < B_OK)
return replyPort;
} else {
assert(cachedReplyPort < sNumReplyPorts);
replyPort = sReplyPorts[cachedReplyPort];
}
team_id team = B_BAD_TEAM_ID;
if (be_app != NULL)
team = be_app->Team();
else {
port_info portInfo;
result = get_port_info(replyPort, &portInfo);
if (result < B_OK)
goto error;
team = portInfo.team;
}
result = set_port_owner(replyPort, portOwner);
if (result < B_OK)
goto error;
{
BMessenger messenger;
BMessenger::Private(messenger).SetTo(team, replyPort,
B_PREFERRED_TOKEN);
result = _SendMessage(port, token, sendTimeout, true,
messenger);
}
if (result < B_OK)
goto error;
int32 code;
result = handle_reply(replyPort, &code, replyTimeout, reply);
if (result < B_OK && cachedReplyPort >= 0) {
delete_port(replyPort);
sReplyPorts[cachedReplyPort] = create_port(1, "tmp_rport");
}
error:
if (cachedReplyPort >= 0) {
// Reclaim ownership of cached port
set_port_owner(replyPort, team);
// Flag as available
atomic_add(&sReplyPortInUse[cachedReplyPort], -1);
return result;
}
delete_port(replyPort);
return result;
}
status_t
BMessage::_SendFlattenedMessage(void *data, int32 size, port_id port,
int32 token, bigtime_t timeout)
{
DEBUG_FUNCTION_ENTER2;
if (!data)
return B_BAD_VALUE;
uint32 magic = *(uint32 *)data;
if (magic == kMessageMagic4 || magic == kMessageMagic4Swapped) {
message_header *header = (message_header *)data;
header->target = token;
} else if (magic == kMessageMagicR5) {
uint8 *header = (uint8 *)data;
header += sizeof(uint32) /* magic */ + sizeof(uint32) /* checksum */
+ sizeof(ssize_t) /* flattenedSize */ + sizeof(int32) /* what */
+ sizeof(uint8) /* flags */;
*(int32 *)header = token;
} else if (((KMessage::Header *)data)->magic == KMessage::kMessageHeaderMagic) {
KMessage::Header *header = (KMessage::Header *)data;
header->targetToken = token;
} else {
return B_NOT_A_MESSAGE;
}
// send the message
status_t result;
do {
result = write_port_etc(port, kPortMessageCode, data, size,
B_RELATIVE_TIMEOUT, timeout);
} while (result == B_INTERRUPTED);
return result;
}
static status_t
handle_reply(port_id replyPort, int32 *pCode, bigtime_t timeout,
BMessage *reply)
{
DEBUG_FUNCTION_ENTER2;
ssize_t size;
do {
size = port_buffer_size_etc(replyPort, B_RELATIVE_TIMEOUT, timeout);
} while (size == B_INTERRUPTED);
if (size < B_OK)
return size;
status_t result;
char *buffer = (char *)malloc(size);
do {
result = read_port(replyPort, pCode, buffer, size);
} while (result == B_INTERRUPTED);
if (result < B_OK || *pCode != kPortMessageCode) {
free(buffer);
return (result < B_OK ? result : B_ERROR);
}
result = reply->Unflatten(buffer);
free(buffer);
return result;
}
static status_t
convert_message(const KMessage *fromMessage, BMessage *toMessage)
{
DEBUG_FUNCTION_ENTER2;
if (!fromMessage || !toMessage)
return B_BAD_VALUE;
// make empty and init what of the target message
toMessage->MakeEmpty();
toMessage->what = fromMessage->What();
// iterate through the fields and import them in the target message
KMessageField field;
while (fromMessage->GetNextField(&field) == B_OK) {
int32 elementCount = field.CountElements();
if (elementCount > 0) {
for (int32 i = 0; i < elementCount; i++) {
int32 size;
const void *data = field.ElementAt(i, &size);
status_t result;
if (field.TypeCode() == B_MESSAGE_TYPE) {
// message type: if it's a KMessage, convert it
KMessage message;
if (message.SetTo(data, size) == B_OK) {
BMessage bMessage;
result = convert_message(&message, &bMessage);
if (result < B_OK)
return result;
result = toMessage->AddMessage(field.Name(), &bMessage);
} else {
// just add it
result = toMessage->AddData(field.Name(),
field.TypeCode(), data, size,
field.HasFixedElementSize(), 1);
}
} else {
result = toMessage->AddData(field.Name(), field.TypeCode(),
data, size, field.HasFixedElementSize(), 1);
}
if (result < B_OK)
return result;
}
}
}
return B_OK;
}
void BMessage::_ReservedMessage1(void) {};
void BMessage::_ReservedMessage2(void) {};
void BMessage::_ReservedMessage3(void) {};
/* Relay functions from here on (Add... -> AddData, Find... -> FindData) */
#define DEFINE_FUNCTIONS(type, typeName, typeCode) \
status_t \
BMessage::Add##typeName(const char *name, type val) \
{ \
return AddData(name, typeCode, &val, sizeof(type), true); \
} \
\
status_t \
BMessage::Find##typeName(const char *name, type *p) const \
{ \
void *ptr = NULL; \
ssize_t bytes = 0; \
status_t error = B_OK; \
\
*p = type(); \
error = FindData(name, typeCode, 0, (const void **)&ptr, &bytes); \
\
if (error == B_OK) \
memcpy(p, ptr, sizeof(type)); \
\
return error; \
} \
\
status_t \
BMessage::Find##typeName(const char *name, int32 index, type *p) const \
{ \
void *ptr = NULL; \
ssize_t bytes = 0; \
status_t error = B_OK; \
\
*p = type(); \
error = FindData(name, typeCode, index, (const void **)&ptr, &bytes); \
\
if (error == B_OK) \
memcpy(p, ptr, sizeof(type)); \
\
return error; \
} \
\
status_t \
BMessage::Replace##typeName(const char *name, type val) \
{ \
return ReplaceData(name, typeCode, 0, &val, sizeof(type)); \
} \
\
status_t \
BMessage::Replace##typeName(const char *name, int32 index, type val) \
{ \
return ReplaceData(name, typeCode, index, &val, sizeof(type)); \
} \
\
bool \
BMessage::Has##typeName(const char *name, int32 index) const \
{ \
return HasData(name, typeCode, index); \
}
DEFINE_FUNCTIONS(BPoint, Point, B_POINT_TYPE);
DEFINE_FUNCTIONS(BRect, Rect, B_RECT_TYPE);
DEFINE_FUNCTIONS(int8, Int8, B_INT8_TYPE);
DEFINE_FUNCTIONS(int16, Int16, B_INT16_TYPE);
DEFINE_FUNCTIONS(int32, Int32, B_INT32_TYPE);
DEFINE_FUNCTIONS(int64, Int64, B_INT64_TYPE);
DEFINE_FUNCTIONS(bool, Bool, B_BOOL_TYPE);
DEFINE_FUNCTIONS(float, Float, B_FLOAT_TYPE);
DEFINE_FUNCTIONS(double, Double, B_DOUBLE_TYPE);
#undef DEFINE_FUNCTIONS
#define DEFINE_HAS_FUNCTION(typeName, typeCode) \
bool \
BMessage::Has##typeName(const char *name, int32 index) const \
{ \
return HasData(name, typeCode, index); \
}
DEFINE_HAS_FUNCTION(String, B_STRING_TYPE);
DEFINE_HAS_FUNCTION(Pointer, B_POINTER_TYPE);
DEFINE_HAS_FUNCTION(Messenger, B_MESSENGER_TYPE);
DEFINE_HAS_FUNCTION(Ref, B_REF_TYPE);
DEFINE_HAS_FUNCTION(Message, B_MESSAGE_TYPE);
#undef DEFINE_HAS_FUNCTION
#define DEFINE_LAZY_FIND_FUNCTION(type, typeName, initialize) \
type \
BMessage::Find##typeName(const char *name, int32 index) const \
{ \
type val = initialize; \
Find##typeName(name, index, &val); \
return val; \
}
DEFINE_LAZY_FIND_FUNCTION(BRect, Rect, BRect());
DEFINE_LAZY_FIND_FUNCTION(BPoint, Point, BPoint());
DEFINE_LAZY_FIND_FUNCTION(const char *, String, NULL);
DEFINE_LAZY_FIND_FUNCTION(int8, Int8, 0);
DEFINE_LAZY_FIND_FUNCTION(int16, Int16, 0);
DEFINE_LAZY_FIND_FUNCTION(int32, Int32, 0);
DEFINE_LAZY_FIND_FUNCTION(int64, Int64, 0);
DEFINE_LAZY_FIND_FUNCTION(bool, Bool, false);
DEFINE_LAZY_FIND_FUNCTION(float, Float, 0);
DEFINE_LAZY_FIND_FUNCTION(double, Double, 0);
#undef DEFINE_LAZY_FIND_FUNCTION
status_t
BMessage::AddString(const char *name, const char *string)
{
return AddData(name, B_STRING_TYPE, string, string ? strlen(string) + 1 : 0, false);
}
status_t
BMessage::AddString(const char *name, const BString &string)
{
return AddData(name, B_STRING_TYPE, string.String(), string.Length() + 1, false);
}
status_t
BMessage::AddPointer(const char *name, const void *pointer)
{
return AddData(name, B_POINTER_TYPE, &pointer, sizeof(pointer), true);
}
status_t
BMessage::AddMessenger(const char *name, BMessenger messenger)
{
return AddData(name, B_MESSENGER_TYPE, &messenger, sizeof(messenger), true);
}
status_t
BMessage::AddRef(const char *name, const entry_ref *ref)
{
size_t size = sizeof(entry_ref) + B_PATH_NAME_LENGTH;
char buffer[size];
status_t error = BPrivate::entry_ref_flatten(buffer, &size, ref);
if (error >= B_OK)
error = AddData(name, B_REF_TYPE, buffer, size, false);
return error;
}
status_t
BMessage::AddMessage(const char *name, const BMessage *message)
{
/* ToDo: This and the following functions waste time by allocating and
copying an extra buffer. Functions can be added that return a direct
pointer into the message. */
ssize_t size = message->_NativeFlattenedSize();
char buffer[size];
status_t error = message->_NativeFlatten(buffer, size);
if (error >= B_OK)
error = AddData(name, B_MESSAGE_TYPE, &buffer, size, false);
return error;
}
status_t
BMessage::AddFlat(const char *name, BFlattenable *object, int32 count)
{
ssize_t size = object->FlattenedSize();
char buffer[size];
status_t error = object->Flatten(buffer, size);
if (error >= B_OK)
error = AddData(name, object->TypeCode(), &buffer, size, false);
return error;
}
status_t
BMessage::FindString(const char *name, const char **string) const
{
return FindString(name, 0, string);
}
status_t
BMessage::FindString(const char *name, int32 index, const char **string) const
{
ssize_t bytes;
return FindData(name, B_STRING_TYPE, index, (const void **)string, &bytes);
}
status_t
BMessage::FindString(const char *name, BString *string) const
{
return FindString(name, 0, string);
}
status_t
BMessage::FindString(const char *name, int32 index, BString *string) const
{
const char *cstr;
status_t error = FindString(name, index, &cstr);
if (error < B_OK)
return error;
*string = cstr;
return B_OK;
}
status_t
BMessage::FindPointer(const char *name, void **pointer) const
{
return FindPointer(name, 0, pointer);
}
status_t
BMessage::FindPointer(const char *name, int32 index, void **pointer) const
{
void **data = NULL;
ssize_t size = 0;
status_t error = FindData(name, B_POINTER_TYPE, index,
(const void **)&data, &size);
if (error == B_OK)
*pointer = *data;
else
*pointer = NULL;
return error;
}
status_t
BMessage::FindMessenger(const char *name, BMessenger *messenger) const
{
return FindMessenger(name, 0, messenger);
}
status_t
BMessage::FindMessenger(const char *name, int32 index, BMessenger *messenger)
const
{
void *data = NULL;
ssize_t size = 0;
status_t error = FindData(name, B_MESSENGER_TYPE, index,
(const void **)&data, &size);
if (error == B_OK)
memcpy(messenger, data, sizeof(BMessenger));
else
*messenger = BMessenger();
return error;
}
status_t
BMessage::FindRef(const char *name, entry_ref *ref) const
{
return FindRef(name, 0, ref);
}
status_t
BMessage::FindRef(const char *name, int32 index, entry_ref *ref) const
{
void *data = NULL;
ssize_t size = 0;
status_t error = FindData(name, B_REF_TYPE, index,
(const void **)&data, &size);
if (error == B_OK)
error = BPrivate::entry_ref_unflatten(ref, (char *)data, size);
else
*ref = entry_ref();
return error;
}
status_t
BMessage::FindMessage(const char *name, BMessage *message) const
{
return FindMessage(name, 0, message);
}
status_t
BMessage::FindMessage(const char *name, int32 index, BMessage *message) const
{
void *data = NULL;
ssize_t size = 0;
status_t error = FindData(name, B_MESSAGE_TYPE, index,
(const void **)&data, &size);
if (error == B_OK)
error = message->Unflatten((const char *)data);
else
*message = BMessage();
return error;
}
status_t
BMessage::FindFlat(const char *name, BFlattenable *object) const
{
return FindFlat(name, 0, object);
}
status_t
BMessage::FindFlat(const char *name, int32 index, BFlattenable *object) const
{
void *data = NULL;
ssize_t numBytes = 0;
status_t error = FindData(name, object->TypeCode(), index,
(const void **)&data, &numBytes);
if (error == B_OK)
error = object->Unflatten(object->TypeCode(), data, numBytes);
return error;
}
status_t
BMessage::FindData(const char *name, type_code type, const void **data,
ssize_t *numBytes) const
{
return FindData(name, type, 0, data, numBytes);
}
status_t
BMessage::ReplaceString(const char *name, const char *string)
{
return ReplaceData(name, B_STRING_TYPE, 0, string, strlen(string) + 1);
}
status_t
BMessage::ReplaceString(const char *name, int32 index, const char *string)
{
return ReplaceData(name, B_STRING_TYPE, index, string, strlen(string) + 1);
}
status_t
BMessage::ReplaceString(const char *name, const BString &string)
{
return ReplaceData(name, B_STRING_TYPE, 0, string.String(),
string.Length() + 1);
}
status_t
BMessage::ReplaceString(const char *name, int32 index, const BString &string)
{
return ReplaceData(name, B_STRING_TYPE, index, string.String(),
string.Length() + 1);
}
status_t
BMessage::ReplacePointer(const char *name, const void *pointer)
{
return ReplaceData(name, B_POINTER_TYPE, 0, &pointer, sizeof(pointer));
}
status_t
BMessage::ReplacePointer(const char *name, int32 index, const void *pointer)
{
return ReplaceData(name, B_POINTER_TYPE, index, &pointer, sizeof(pointer));
}
status_t
BMessage::ReplaceMessenger(const char *name, BMessenger messenger)
{
return ReplaceData(name, B_MESSENGER_TYPE, 0, &messenger,
sizeof(BMessenger));
}
status_t
BMessage::ReplaceMessenger(const char *name, int32 index, BMessenger messenger)
{
return ReplaceData(name, B_MESSENGER_TYPE, index, &messenger,
sizeof(BMessenger));
}
status_t
BMessage::ReplaceRef(const char *name, const entry_ref *ref)
{
return ReplaceRef(name, 0, ref);
}
status_t
BMessage::ReplaceRef(const char *name, int32 index, const entry_ref *ref)
{
size_t size = sizeof(entry_ref) + B_PATH_NAME_LENGTH;
char buffer[size];
status_t error = BPrivate::entry_ref_flatten(buffer, &size, ref);
if (error >= B_OK)
error = ReplaceData(name, B_REF_TYPE, index, &buffer, size);
return error;
}
status_t
BMessage::ReplaceMessage(const char *name, const BMessage *message)
{
return ReplaceMessage(name, 0, message);
}
status_t
BMessage::ReplaceMessage(const char *name, int32 index, const BMessage *message)
{
ssize_t size = message->_NativeFlattenedSize();
char buffer[size];
status_t error = message->_NativeFlatten(buffer, size);
if (error >= B_OK)
error = ReplaceData(name, B_MESSAGE_TYPE, index, &buffer, size);
return error;
}
status_t
BMessage::ReplaceFlat(const char *name, BFlattenable *object)
{
return ReplaceFlat(name, 0, object);
}
status_t
BMessage::ReplaceFlat(const char *name, int32 index, BFlattenable *object)
{
ssize_t size = object->FlattenedSize();
char buffer[size];
status_t error = object->Flatten(buffer, size);
if (error >= B_OK)
error = ReplaceData(name, object->TypeCode(), index, &buffer, size);
return error;
}
status_t
BMessage::ReplaceData(const char *name, type_code type, const void *data,
ssize_t numBytes)
{
return ReplaceData(name, type, 0, data, numBytes);
}
bool
BMessage::HasFlat(const char *name, const BFlattenable *object) const
{
return HasFlat(name, 0, object);
}
bool
BMessage::HasFlat(const char *name, int32 index, const BFlattenable *object)
const
{
return HasData(name, object->TypeCode(), index);
}