diff --git a/headers/os/app/Message4.h b/headers/os/app/Message4.h new file mode 100644 index 0000000000..37dbe8987b --- /dev/null +++ b/headers/os/app/Message4.h @@ -0,0 +1,330 @@ +/* + * Copyright 2005, Haiku Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Michael Lotz + */ + +#ifndef _MESSAGE_H +#define _MESSAGE_H + +#define USING_MESSAGE4 1 + +#include +#include +#include +#include +#include + +#include /* For convenience */ +#include /* For convenience */ + +class BBlockCache; +class BMessenger; +class BHandler; +class BString; + +struct message_header_s; +struct field_header_s; + +// Private or reserved --------------------------------------------------------- +extern "C" void _msg_cache_cleanup_(); +extern "C" int _init_message_(); +extern "C" int _delete_message_(); +//------------------------------------------------------------------------------ + + +// Name lengths and Scripting specifiers --------------------------------------- +#define B_FIELD_NAME_LENGTH 255 +#define B_PROPERTY_NAME_LENGTH 255 + +enum +{ + B_NO_SPECIFIER = 0, + B_DIRECT_SPECIFIER = 1, + B_INDEX_SPECIFIER, + B_REVERSE_INDEX_SPECIFIER, + B_RANGE_SPECIFIER, + B_REVERSE_RANGE_SPECIFIER, + B_NAME_SPECIFIER, + B_ID_SPECIFIER, + + B_SPECIFIERS_END = 128 + // app-defined specifiers start at B_SPECIFIERS_END + 1 +}; +//------------------------------------------------------------------------------ + + +class BMessage { + +public: + uint32 what; + + BMessage(); + BMessage(uint32 what); + BMessage(const BMessage &other); +virtual ~BMessage(); + + BMessage &operator=(const BMessage &other); + +// Statistics and misc info + status_t GetInfo(type_code typeRequested, int32 index, + char **nameFound, type_code *typeFound, + int32 *countFound = NULL) const; + status_t GetInfo(const char *name, type_code *typeFound, + int32 *countFound = NULL) const; + status_t GetInfo(const char *name, type_code *typeFound, + bool *fixedSize) const; + + int32 CountNames(type_code type) const; + bool IsEmpty() const; + bool IsSystem() const; + bool IsReply() const; + void PrintToStream() const; + + status_t Rename(const char *oldEntry, const char *newEntry); + +// Delivery info + bool WasDelivered() const; + bool IsSourceWaiting() const; + bool IsSourceRemote() const; + BMessenger ReturnAddress() const; + const BMessage *Previous() const; + bool WasDropped() const; + BPoint DropPoint(BPoint *offset = NULL) const; + +// Replying + status_t SendReply(uint32 command, BHandler *replyTo = NULL); + status_t SendReply(BMessage *reply, BHandler *replyTo = NULL, + bigtime_t timeout = B_INFINITE_TIMEOUT); + status_t SendReply(BMessage *reply, BMessenger replyTo, + bigtime_t timeout = B_INFINITE_TIMEOUT); + + status_t SendReply(uint32 command, BMessage *replyToReply); + status_t SendReply(BMessage *the_reply, BMessage *replyToReply, + bigtime_t sendTimeout = B_INFINITE_TIMEOUT, + bigtime_t replyTimeout = B_INFINITE_TIMEOUT); + +// Flattening data + ssize_t FlattenedSize() const; + status_t Flatten(char *buffer, ssize_t size) const; + status_t Flatten(BDataIO *stream, ssize_t *size = NULL) const; + status_t Unflatten(const char *flatBuffer); + status_t Unflatten(BDataIO *stream); + +// Specifiers (scripting) + status_t AddSpecifier(const char *property); + status_t AddSpecifier(const char *property, int32 index); + status_t AddSpecifier(const char *property, int32 index, int32 range); + status_t AddSpecifier(const char *property, const char *name); + status_t AddSpecifier(const BMessage *specifier); + + status_t SetCurrentSpecifier(int32 index); + status_t GetCurrentSpecifier(int32 *index, + BMessage *specifier = NULL, int32 *what = NULL, + const char **property = NULL) const; + bool HasSpecifiers() const; + status_t PopSpecifier(); + +// Adding data + status_t AddRect(const char *name, BRect aRect); + status_t AddPoint(const char *name, BPoint aPoint); + status_t AddString(const char *name, const char *aString); + status_t AddString(const char *name, const BString &aString); + status_t AddInt8(const char *name, int8 value); + status_t AddInt16(const char *name, int16 value); + status_t AddInt32(const char *name, int32 value); + status_t AddInt64(const char *name, int64 value); + status_t AddBool(const char *name, bool aBoolean); + status_t AddFloat(const char *name, float aFloat); + status_t AddDouble(const char *name, double aDouble); + status_t AddPointer(const char *name, const void *aPointer); + status_t AddMessenger(const char *name, BMessenger messenger); + status_t AddRef(const char *name, const entry_ref *ref); + status_t AddMessage(const char *name, const BMessage *message); + status_t AddFlat(const char *name, BFlattenable *object, + int32 count = 1); + status_t AddData(const char *name, type_code type, + const void *data, ssize_t numBytes, + bool isFixedSize = true, int32 count = 1); + +// Removing data + status_t RemoveData(const char *name, int32 index = 0); + status_t RemoveName(const char *name); + status_t MakeEmpty(); + +// Finding data + status_t FindRect(const char *name, BRect *rect) const; + status_t FindRect(const char *name, int32 index, BRect *rect) const; + status_t FindPoint(const char *name, BPoint *point) const; + status_t FindPoint(const char *name, int32 index, BPoint *point) const; + status_t FindString(const char *name, const char **string) const; + status_t FindString(const char *name, int32 index, const char **string) const; + status_t FindString(const char *name, BString *string) const; + status_t FindString(const char *name, int32 index, BString *string) const; + status_t FindInt8(const char *name, int8 *value) const; + status_t FindInt8(const char *name, int32 index, int8 *value) const; + status_t FindInt16(const char *name, int16 *value) const; + status_t FindInt16(const char *name, int32 index, int16 *value) const; + status_t FindInt32(const char *name, int32 *value) const; + status_t FindInt32(const char *name, int32 index, int32 *value) const; + status_t FindInt64(const char *name, int64 *value) const; + status_t FindInt64(const char *name, int32 index, int64 *value) const; + status_t FindBool(const char *name, bool *value) const; + status_t FindBool(const char *name, int32 index, bool *value) const; + status_t FindFloat(const char *name, float *value) const; + status_t FindFloat(const char *name, int32 index, float *value) const; + status_t FindDouble(const char *name, double *value) const; + status_t FindDouble(const char *name, int32 index, double *value) const; + status_t FindPointer(const char *name, void **pointer) const; + status_t FindPointer(const char *name, int32 index, void **pointer) const; + status_t FindMessenger(const char *name, BMessenger *messenger) const; + status_t FindMessenger(const char *name, int32 index, BMessenger *messenger) const; + status_t FindRef(const char *name, entry_ref *ref) const; + status_t FindRef(const char *name, int32 index, entry_ref *ref) const; + status_t FindMessage(const char *name, BMessage *message) const; + status_t FindMessage(const char *name, int32 index, BMessage *message) const; + status_t FindFlat(const char *name, BFlattenable *object) const; + status_t FindFlat(const char *name, int32 index, BFlattenable *object) const; + status_t FindData(const char *name, type_code type, + const void **data, ssize_t *numBytes) const; + status_t FindData(const char *name, type_code type, int32 index, + const void **data, ssize_t *numBytes) const; + +// Replacing data + status_t ReplaceRect(const char *name, BRect aRect); + status_t ReplaceRect(const char *name, int32 index, BRect aRect); + status_t ReplacePoint(const char *name, BPoint aPoint); + status_t ReplacePoint(const char *name, int32 index, BPoint aPoint); + status_t ReplaceString(const char *name, const char *aString); + status_t ReplaceString(const char *name, int32 index, const char *aString); + status_t ReplaceString(const char *name, const BString &aString); + status_t ReplaceString(const char *name, int32 index, const BString &aString); + status_t ReplaceInt8(const char *name, int8 value); + status_t ReplaceInt8(const char *name, int32 index, int8 value); + status_t ReplaceInt16(const char *name, int16 value); + status_t ReplaceInt16(const char *name, int32 index, int16 value); + status_t ReplaceInt32(const char *name, int32 value); + status_t ReplaceInt32(const char *name, int32 index, int32 value); + status_t ReplaceInt64(const char *name, int64 value); + status_t ReplaceInt64(const char *name, int32 index, int64 value); + status_t ReplaceBool(const char *name, bool aBoolean); + status_t ReplaceBool(const char *name, int32 index, bool aBoolean); + status_t ReplaceFloat(const char *name, float aFloat); + status_t ReplaceFloat(const char *name, int32 index, float aFloat); + status_t ReplaceDouble(const char *name, double aDouble); + status_t ReplaceDouble(const char *name, int32 index, double aDouble); + status_t ReplacePointer(const char *name, const void *pointer); + status_t ReplacePointer(const char *name,int32 index,const void *pointer); + status_t ReplaceMessenger(const char *name, BMessenger messenger); + status_t ReplaceMessenger(const char *name, int32 index, BMessenger messenger); + status_t ReplaceRef( const char *name,const entry_ref *ref); + status_t ReplaceRef( const char *name, int32 index, const entry_ref *ref); + status_t ReplaceMessage(const char *name, const BMessage *message); + status_t ReplaceMessage(const char *name, int32 index, const BMessage *message); + status_t ReplaceFlat(const char *name, BFlattenable *object); + status_t ReplaceFlat(const char *name, int32 index, BFlattenable *object); + status_t ReplaceData(const char *name, type_code type, + const void *data, ssize_t numBytes); + status_t ReplaceData(const char *name, type_code type, int32 index, + const void *data, ssize_t numBytes); + + void *operator new(size_t size); + void *operator new(size_t, void *pointer); + void operator delete(void *pointer, size_t size); + +// Private, reserved, or obsolete ---------------------------------------------- + bool HasRect(const char *, int32 n = 0) const; + bool HasPoint(const char *, int32 n = 0) const; + bool HasString(const char *, int32 n = 0) const; + bool HasInt8(const char *, int32 n = 0) const; + bool HasInt16(const char *, int32 n = 0) const; + bool HasInt32(const char *, int32 n = 0) const; + bool HasInt64(const char *, int32 n = 0) const; + bool HasBool(const char *, int32 n = 0) const; + bool HasFloat(const char *, int32 n = 0) const; + bool HasDouble(const char *, int32 n = 0) const; + bool HasPointer(const char *, int32 n = 0) const; + bool HasMessenger(const char *, int32 n = 0) const; + bool HasRef(const char *, int32 n = 0) const; + bool HasMessage(const char *, int32 n = 0) const; + bool HasFlat(const char *, const BFlattenable *) const; + bool HasFlat(const char *, int32 n, const BFlattenable *) const; + bool HasData(const char *, type_code , int32 n = 0) const; + BRect FindRect(const char *, int32 n = 0) const; + BPoint FindPoint(const char *, int32 n = 0) const; + const char *FindString(const char *, int32 n = 0) const; + int8 FindInt8(const char *, int32 n = 0) const; + int16 FindInt16(const char *, int32 n = 0) const; + int32 FindInt32(const char *, int32 n = 0) const; + int64 FindInt64(const char *, int32 n = 0) const; + bool FindBool(const char *, int32 n = 0) const; + float FindFloat(const char *, int32 n = 0) const; + double FindDouble(const char *, int32 n = 0) const; + + class Private; + +private: +friend class Private; +friend class BMessageQueue; + + status_t _InitCommon(); + status_t _InitHeader(); + status_t _Clear(); + + status_t _ResizeData(int32 offset, int32 change); + + uint32 _HashName(const char *name) const; + status_t _FindField(const char *name, type_code type, + field_header_s **result) const; + status_t _AddField(const char *name, type_code type, + bool isFixedSize, field_header_s **result); + status_t _RemoveField(field_header_s *field); + + ssize_t _NativeFlattenedSize() const; + status_t _NativeFlatten(char *buffer, ssize_t size) const; + status_t _NativeFlatten(BDataIO *stream, ssize_t *size = NULL) const; + + message_header_s*fHeader; + field_header_s *fFields; + uint8 *fData; + +mutable BMessage *fOriginal; + + // fQueueLink is used by BMessageQueue to build a linked list + BMessage *fQueueLink; + + uint32 fReserved[11]; + + // deprecated + BMessage(BMessage *a_message); + +virtual void _ReservedMessage1(); +virtual void _ReservedMessage2(); +virtual void _ReservedMessage3(); + + status_t _SendMessage(port_id port, int32 token, bool preferred, + bigtime_t timeout, bool replyRequired, + BMessenger &replyTo) const; + status_t _SendMessage(port_id port, team_id portOwner, + int32 token, bool preferred, BMessage *reply, + bigtime_t sendTimeout, bigtime_t replyTimeout) const; +static status_t _SendFlattenedMessage(void *data, int32 size, + port_id port, int32 token, bool preferred, + bigtime_t timeout); + +static void _StaticInit(); +static void _StaticCleanup(); +static void _StaticCacheCleanup(); +static int32 _StaticGetCachedReplyPort(); + + enum { sNumReplyPorts = 3 }; +static port_id sReplyPorts[sNumReplyPorts]; +static long sReplyPortInUse[sNumReplyPorts]; +static int32 sGetCachedReplyPort(); + +static BBlockCache *sMsgCache; +}; + +#endif // _MESSAGE_H diff --git a/headers/private/app/MessagePrivate4.h b/headers/private/app/MessagePrivate4.h new file mode 100644 index 0000000000..d8e8f5ecff --- /dev/null +++ b/headers/private/app/MessagePrivate4.h @@ -0,0 +1,201 @@ +#ifndef _MESSAGE_PRIVATE_H_ +#define _MESSAGE_PRIVATE_H_ + +#include +#include +#include +#include + + +#define MESSAGE_BODY_HASH_TABLE_SIZE 10 +#define MAX_DATA_PREALLOCATION B_PAGE_SIZE * 10 +#define MAX_FIELD_PREALLOCATION 50 +#define MAX_ITEM_PREALLOCATION B_PAGE_SIZE + + +enum { + MESSAGE_FLAG_VALID = 0x0001, + MESSAGE_FLAG_REPLY_REQUIRED = 0x0002, + MESSAGE_FLAG_REPLY_DONE = 0x0004, + MESSAGE_FLAG_IS_REPLY = 0x0008, + MESSAGE_FLAG_WAS_DELIVERED = 0x0010, + MESSAGE_FLAG_HAS_SPECIFIERS = 0x0020, + MESSAGE_FLAG_READ_ONLY = 0x0040 +}; + + +enum { + FIELD_FLAG_VALID = 0x0001, + FIELD_FLAG_FIXED_SIZE = 0x0002, +}; + + +typedef struct field_header_s { + uint32 flags; + type_code type; + int32 nameLength; + int32 count; + ssize_t dataSize; + ssize_t allocated; + int32 offset; + int32 nextField; +} FieldHeader; + + +typedef struct message_header_s { + uint32 format; + uint32 what; + uint32 flags; + + ssize_t fieldsSize; + ssize_t dataSize; + ssize_t fieldsAvailable; + ssize_t dataAvailable; + + int32 target; + int32 currentSpecifier; + + // reply info + port_id replyPort; + int32 replyTarget; + team_id replyTeam; + + // body info + int32 fieldCount; + int32 hashTableSize; + int32 hashTable[MESSAGE_BODY_HASH_TABLE_SIZE]; + + /* The hash table does contain indexes into the field list and + not direct offsets to the fields. This has the advantage + of not needing to update offsets in two locations. + The hash table must be reevaluated when we remove a field + though. + */ +} MessageHeader; + + +class BMessage::Private { + +public: + Private(BMessage *msg) + : fMessage(msg) + { + } + + Private(BMessage &msg) + : fMessage(&msg) + { + } + +inline void SetTarget(int32 token, bool preferred) + { + fMessage->fHeader->target = (preferred + ? B_PREFERRED_TOKEN : token); + } + +inline void SetReply(BMessenger messenger) + { + BMessenger::Private messengerPrivate(messenger); + fMessage->fHeader->replyPort = messengerPrivate.Port(); + fMessage->fHeader->replyTarget = messengerPrivate.Token(); + fMessage->fHeader->replyTeam = messengerPrivate.Team(); + } + +inline int32 GetTarget() + { + return fMessage->fHeader->target; + } + +inline bool UsePreferredTarget() + { + return fMessage->fHeader->target == B_PREFERRED_TOKEN; + } + +inline status_t Clear() + { + return fMessage->_Clear(); + } + +inline status_t InitHeader() + { + return fMessage->_InitHeader(); + } + +inline MessageHeader *GetMessageHeader() + { + return fMessage->fHeader; + } + +inline FieldHeader *GetMessageFields() + { + return fMessage->fFields; + } + +inline uint8 *GetMessageData() + { + return fMessage->fData; + } + +inline ssize_t NativeFlattenedSize() const + { + return fMessage->_NativeFlattenedSize(); + } + +inline status_t NativeFlatten(char *buffer, ssize_t size) const + { + return fMessage->_NativeFlatten(buffer, size); + } + +inline status_t NativeFlatten(BDataIO *stream, ssize_t *size) const + { + return fMessage->_NativeFlatten(stream, size); + } + +inline status_t SendMessage(port_id port, int32 token, bool preferred, + bigtime_t timeout, bool replyRequired, + BMessenger &replyTo) const + { + return fMessage->_SendMessage(port, token, + preferred, timeout, replyRequired, replyTo); + } + +inline status_t SendMessage(port_id port, team_id portOwner, + int32 token, bool preferred, BMessage *reply, + bigtime_t sendTimeout, bigtime_t replyTimeout) const + { + return fMessage->_SendMessage(port, portOwner, token, + preferred, reply, sendTimeout, replyTimeout); + } + +static +inline status_t SendFlattenedMessage(void *data, int32 size, + port_id port, int32 token, bool preferred, + bigtime_t timeout) + { + return BMessage::_SendFlattenedMessage(data, size, + port, token, preferred, timeout); + } + +static +inline void StaticInit() + { + BMessage::_StaticInit(); + } + +static +inline void StaticCleanup() + { + BMessage::_StaticCleanup(); + } + +static +inline void StaticCacheCleanup() + { + BMessage::_StaticCacheCleanup(); + } + +private: + BMessage *fMessage; +}; + +#endif // _MESSAGE_PRIVATE_H_ diff --git a/headers/private/app/MessageUtils4.h b/headers/private/app/MessageUtils4.h new file mode 100644 index 0000000000..f96864fd55 --- /dev/null +++ b/headers/private/app/MessageUtils4.h @@ -0,0 +1,173 @@ +#ifndef _MESSAGE_UTILS_H_ +#define _MESSAGE_UTILS_H_ + +#include +#include +#include +#include +#include + + +namespace BPrivate { // Only putting these here because Be did + +status_t entry_ref_flatten(char* buffer, size_t* size, const entry_ref* ref); +status_t entry_ref_unflatten(entry_ref* ref, const char* buffer, size_t size); +status_t entry_ref_swap(char* buffer, size_t size); + +uint32 CalculateChecksum(const uint8 *buffer, int32 size); + +} // namespace BPrivate + + +template +inline void +byte_swap(T &/*data*/) +{ + // Specialize for data types which actually swap +} + + +inline void +write_helper(BDataIO *stream, const void *data, size_t size) +{ + status_t error = stream->Write(data, size); + if (error < B_OK) + throw error; +} + + +class TReadHelper { +public: + TReadHelper(BDataIO *stream) + : fStream(stream), + fError(B_OK), + fSwap(false) + { + } + + TReadHelper(BDataIO *stream, bool swap) + : fStream(stream), + fError(B_OK), + fSwap(swap) + { + } + + template + inline void operator()(T &data) + { + fError = fStream->Read((void *)&data, sizeof(T)); + if (fError > B_OK) { + if (IsSwapping()) + byte_swap(data); + return; + } + + if (fError == 0) + throw B_ERROR; + throw fError; + } + + template + inline void operator()(T data, size_t len) + { + fError = fStream->Read((void *)data, len); + if (fError > B_OK) + return; + + if (fError == 0) + throw B_ERROR; + throw fError; + } + + status_t Status() { return fError; }; + + void SetSwap(bool yesNo) { fSwap = yesNo; }; + bool IsSwapping() { return fSwap; }; + +private: + BDataIO *fStream; + status_t fError; + bool fSwap; +}; + + +class TChecksumHelper { +public: + TChecksumHelper(uchar* buffer) + : fBuffer(buffer), + fBufPtr(buffer) + { + } + + template + inline void Cache(const T &data) + { + *((T*)fBufPtr) = data; + fBufPtr += sizeof (T); + } + + int32 CheckSum(); + +private: + uchar *fBuffer; + uchar *fBufPtr; +}; + + +template +inline status_t +read_helper(BDataIO *stream, T &data) +{ + return normalize_err(stream->Read((void *)&data, sizeof(T))); +} + + +template<> +inline void +byte_swap(double &data) +{ + data = __swap_double(data); +} + + +template<> +inline void +byte_swap(float &data) +{ + data = __swap_float(data); +} + + +template<> +inline void +byte_swap(int64 &data) +{ + data = __swap_int64(data); +} + + +template<> +inline void +byte_swap(int32 &data) +{ + data = __swap_int32(data); +} + + +template<> +inline void +byte_swap(int16 &data) +{ + data = __swap_int16(data); +} + + +template<> +inline void +byte_swap(entry_ref &data) +{ + byte_swap(data.device); + byte_swap(data.directory); +} + +#endif // _MESSAGE_UTILS_H_ diff --git a/src/kits/app/Looper.cpp b/src/kits/app/Looper.cpp index 129eaeb743..ba68f38e2a 100644 --- a/src/kits/app/Looper.cpp +++ b/src/kits/app/Looper.cpp @@ -60,8 +60,24 @@ using BPrivate::BObjectLocker; using BPrivate::BLooperList; port_id _get_looper_port_(const BLooper* looper); +#ifndef USING_MESSAGE4 bool _use_preferred_target_(BMessage* msg) { return msg->fPreferred; } int32 _get_message_target_(BMessage* msg) { return msg->fTarget; } +#else +#include + +inline bool +_use_preferred_target_(BMessage *message) +{ + return BMessage::Private(message).UsePreferredTarget(); +} + +inline int32 +_get_message_target_(BMessage *message) +{ + return BMessage::Private(message).GetTarget(); +} +#endif uint32 BLooper::sLooperID = (uint32)B_ERROR; team_id BLooper::sTeamID = (team_id)B_ERROR; diff --git a/src/kits/app/Message4.cpp b/src/kits/app/Message4.cpp new file mode 100644 index 0000000000..ad405b28df --- /dev/null +++ b/src/kits/app/Message4.cpp @@ -0,0 +1,2379 @@ +/* + * Copyright 2005, Haiku Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Michael Lotz + */ + +#include +#include +#include +#include "dano_message.h" +#include "r5_message.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define DEBUG_FUNCTION_ENTER //debug_printf("%ld: %s\n", __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); + + +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(); + *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 = (MessageHeader *)malloc(sizeof(MessageHeader)); + memcpy(fHeader, other.fHeader, sizeof(MessageHeader)); + + if (fHeader->fieldsSize > 0) { + fFields = (FieldHeader *)malloc(fHeader->fieldsSize); + memcpy(fFields, other.fFields, fHeader->fieldsSize); + } + + if (fHeader->dataSize > 0) { + fData = (uint8 *)malloc(other.fHeader->dataSize); + memcpy(fData, other.fData, other.fHeader->dataSize); + } + + fHeader->fieldsAvailable = 0; + fHeader->dataAvailable = 0; + what = fHeader->what; + fQueueLink = other.fQueueLink; + return *this; +} + + +void * +BMessage::operator new(size_t size) +{ + DEBUG_FUNCTION_ENTER; + if (!sMsgCache) + sMsgCache = new BBlockCache(10, size, B_OBJECT_CACHE); + + return sMsgCache->Get(size); +} + + +void * +BMessage::operator new(size_t, void *pointer) +{ + DEBUG_FUNCTION_ENTER; + return pointer; +} + + +void +BMessage::operator delete(void *pointer, size_t size) +{ + DEBUG_FUNCTION_ENTER; + sMsgCache->Save(pointer, size); +} + + +status_t +BMessage::_InitCommon() +{ + DEBUG_FUNCTION_ENTER; + what = 0; + + fHeader = NULL; + fFields = NULL; + fData = NULL; + + fOriginal = NULL; + fQueueLink = NULL; + return B_OK; +} + + +status_t +BMessage::_InitHeader() +{ + DEBUG_FUNCTION_ENTER; + fHeader = (MessageHeader *)malloc(sizeof(MessageHeader)); + memset(fHeader, 0, sizeof(MessageHeader)); + + fHeader->format = kMessageMagic4; + fHeader->flags = MESSAGE_FLAG_VALID; + fHeader->currentSpecifier = -1; + + fHeader->target = B_NULL_TOKEN; + fHeader->replyTarget = B_NULL_TOKEN; + fHeader->replyPort = -1; + fHeader->replyTeam = -1; + + // initializing the hash table to -1 because 0 is a valid index + fHeader->hashTableSize = MESSAGE_BODY_HASH_TABLE_SIZE; + memset(&fHeader->hashTable, 255, sizeof(fHeader->hashTable)); + return B_OK; +} + + +status_t +BMessage::_Clear() +{ + delete fOriginal; + fOriginal = NULL; + fQueueLink = NULL; + + free(fHeader); + fHeader = NULL; + free(fFields); + fFields = NULL; + free(fData); + fData = 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->fieldCount) + return B_BAD_INDEX; + + *nameFound = (char *)fData + fFields[index].offset; + *typeFound = fFields[index].type; + if (countFound) + *countFound = fFields[index].count; + return B_OK; + } + + int32 counter = -1; + FieldHeader *field = fFields; + for (int32 i = 0; i < fHeader->fieldCount; i++, field++) { + if (field->type == typeRequested) + counter++; + + if (counter == index) { + *nameFound = (char *)fData + field->offset; + *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; + + FieldHeader *field = NULL; + status_t result = _FindField(name, B_ANY_TYPE, &field); + if (result < B_OK || !field) + return result; + + *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; + FieldHeader *field = NULL; + status_t result = _FindField(name, B_ANY_TYPE, &field); + if (result < B_OK || !field) + return result; + + *typeFound = field->type; + *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->fieldCount; + + int32 count = 0; + FieldHeader *field = fFields; + for (int32 i = 0; i < fHeader->fieldCount; i++, field++) { + if (field->type == type) + count++; + } + + return count; +} + + +bool +BMessage::IsEmpty() const +{ + DEBUG_FUNCTION_ENTER; + return fHeader->fieldCount == 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; +} + + +#define PRINT_SOME_TYPE(type, typeCode) \ + case typeCode: { \ + sprintf(buffer + strlen(buffer), "size=%2ld, ", size); \ + printf("%s", buffer); \ + memset(buffer, ' ', strlen(buffer)); \ + \ + type *item = (type *)(fData + field->offset + field->nameLength); \ + for (int32 i = 0; i < field->count; i++, item++) { \ + if (i > 0) /* indent */ \ + printf(buffer); \ + \ + printf("data[%ld]: ", i); \ + item->PrintToStream(); \ + } \ + } break + +#define PRINT_INT_TYPE(type, typeCode, format, swap) \ + case typeCode: { \ + sprintf(buffer + strlen(buffer), "size=%2ld, ", size); \ + printf("%s", buffer); \ + memset(buffer, ' ', strlen(buffer)); \ + \ + type *item = (type *)(fData + field->offset + field->nameLength); \ + for (int32 i = 0; i < field->count; i++, item++) { \ + if (i > 0) /* indent */ \ + printf(buffer); \ + \ + printf("data[%ld]: ", i); \ + type value = swap(*item); \ + printf(format, *item, *item, &value); \ + } \ + } break + + +#define PRINT_FLOAT_TYPE(type, typeCode, format) \ + case typeCode: { \ + sprintf(buffer + strlen(buffer), "size=%2ld, ", size); \ + printf("%s", buffer); \ + memset(buffer, ' ', strlen(buffer)); \ + \ + type *item = (type *)(fData + field->offset + field->nameLength); \ + for (int32 i = 0; i < field->count; i++, item++) { \ + if (i > 0) /* indent */ \ + printf(buffer); \ + \ + printf("data[%ld]: ", i); \ + printf(format, *item); \ + } \ + } break + + +#define REMOVE_BELOW_20(x) (x >= 0x20 ? x : 0x20) + + +void +BMessage::PrintToStream() const +{ + DEBUG_FUNCTION_ENTER; + printf("BMessage: what = "); + + int32 value = B_BENDIAN_TO_HOST_INT32(what); + printf("%.4s", (char *)&value); + printf(" (0x%lx, or %ld)\n", what, what); + + char buffer[1024]; + FieldHeader *field = fFields; + for (int32 i = 0; i < fHeader->fieldCount; 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->dataSize / field->count; + + switch (field->type) { + PRINT_SOME_TYPE(BRect, B_RECT_TYPE); + PRINT_SOME_TYPE(BPoint, B_POINT_TYPE); + + case B_STRING_TYPE: { + printf("%s", buffer); + memset(buffer, ' ', strlen(buffer)); + + uint8 *pointer = fData + field->offset + field->nameLength; + 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=%ld, data[%ld]: ", size, i); + printf("\"%s\"\n", (char *)pointer); + pointer += size; + } + } break; + + PRINT_INT_TYPE(int8, B_INT8_TYPE, "0x%hx (%d \'%.1s\')\n", REMOVE_BELOW_20); + PRINT_INT_TYPE(int16, B_INT16_TYPE, "0x%lx (%d, \'%.2s\')\n", B_BENDIAN_TO_HOST_INT16); + PRINT_INT_TYPE(int32, B_INT32_TYPE, "0x%lx (%ld, \'%.4s\')\n", B_BENDIAN_TO_HOST_INT32); + PRINT_INT_TYPE(int64, B_INT64_TYPE, "0x%Lx (%lld, \'%.4s\')\n", B_BENDIAN_TO_HOST_INT64); + + PRINT_FLOAT_TYPE(bool, B_BOOL_TYPE, "%d\n"); + PRINT_FLOAT_TYPE(float, B_FLOAT_TYPE, "%.4f\n"); + PRINT_FLOAT_TYPE(double, B_DOUBLE_TYPE, "%.8f\n"); + + case B_REF_TYPE: { + printf("%s", buffer); + memset(buffer, ' ', strlen(buffer)); + + uint8 *pointer = fData + field->offset + field->nameLength; + 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=%ld, 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); + } + } + } +} + + +#undef PRINT_FLOAT_TYPE +#undef PRINT_INT_TYPE +#undef PRINT_SOME_TYPE + + +status_t +BMessage::Rename(const char *oldEntry, const char *newEntry) +{ + DEBUG_FUNCTION_ENTER; + if (!oldEntry || !newEntry) + return B_BAD_VALUE; + + uint32 hash = _HashName(oldEntry) % fHeader->hashTableSize; + int32 *nextField = &fHeader->hashTable[hash]; + + while (*nextField >= 0) { + FieldHeader *field = &fFields[*nextField]; + + if (strncmp((const char *)(fData + field->offset), oldEntry, + field->nameLength) == 0) { + // nextField points to the field for oldEntry, save it and unlink + int32 index = *nextField; + *nextField = field->nextField; + field->nextField = -1; + + hash = _HashName(newEntry) % fHeader->hashTableSize; + nextField = &fHeader->hashTable[hash]; + while (*nextField >= 0) + nextField = &fFields[*nextField].nextField; + *nextField = index; + + int32 oldLength = field->nameLength; + field->nameLength = strlen(newEntry) + 1; + _ResizeData(field->offset, field->nameLength - oldLength); + memcpy(fData + field->offset, newEntry, field->nameLength); + return B_OK; + } + + nextField = &field->nextField; + } + + 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->replyTeam != BPrivate::current_team()); +} + + +BMessenger +BMessage::ReturnAddress() const +{ + DEBUG_FUNCTION_ENTER; + if (fHeader->flags & MESSAGE_FLAG_WAS_DELIVERED) { + BMessenger messenger; + BMessenger::Private(messenger).SetTo(fHeader->replyTeam, + fHeader->replyPort, fHeader->replyTarget, + fHeader->replyTarget == B_PREFERRED_TOKEN); + return messenger; + } + + return BMessenger(); +} + + +const BMessage * +BMessage::Previous() const +{ + DEBUG_FUNCTION_ENTER; + /* ToDo: test if the "_previous_" field is used in R5 */ + if (!fOriginal) { + delete 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_READ_ONLY; +} + + +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->replyTeam, fHeader->replyPort, + fHeader->replyTarget, fHeader->replyTarget == B_PREFERRED_TOKEN); + + 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->replyTeam, fHeader->replyPort, + fHeader->replyTarget, fHeader->replyTarget == B_PREFERRED_TOKEN); + + 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 _NativeFlattenedSize(); + return BPrivate::R5MessageFlattenedSize(this); +} + + +status_t +BMessage::Flatten(char *buffer, ssize_t size) const +{ + DEBUG_FUNCTION_ENTER; + //return _NativeFlatten(buffer, size); + return BPrivate::R5MessageFlatten(this, buffer, size); +} + + +status_t +BMessage::Flatten(BDataIO *stream, ssize_t *size) const +{ + DEBUG_FUNCTION_ENTER; + //return _NativeFlatten(stream, size); + return BPrivate::R5MessageFlatten(this, stream, size); +} + + +ssize_t +BMessage::_NativeFlattenedSize() const +{ + DEBUG_FUNCTION_ENTER; + return sizeof(MessageHeader) + fHeader->fieldsSize + fHeader->dataSize; +} + + +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; + + memcpy(buffer, fHeader, min_c(sizeof(MessageHeader), (size_t)size)); + buffer += sizeof(MessageHeader); + size -= sizeof(MessageHeader); + + memcpy(buffer, fFields, min_c(fHeader->fieldsSize, size)); + buffer += fHeader->fieldsSize; + size -= fHeader->fieldsSize; + + memcpy(buffer, fData, min_c(fHeader->dataSize, size)); + if (size >= fHeader->dataSize) + 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; + + ssize_t result1 = stream->Write(fHeader, sizeof(MessageHeader)); + if (result1 != sizeof(MessageHeader)) + return (result1 >= 0 ? B_ERROR : result1); + + ssize_t result2 = 0; + if (fHeader->fieldsSize > 0) { + stream->Write(fFields, fHeader->fieldsSize); + if (result2 != fHeader->fieldsSize) + return (result2 >= 0 ? B_ERROR : result2); + } + + ssize_t result3 = 0; + if (fHeader->dataSize > 0) { + stream->Write(fData, fHeader->dataSize); + if (result3 != fHeader->dataSize) + return (result3 >= 0 ? B_ERROR : result3); + } + + if (size) + *size = result1 + result2 + result3; + + 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) + return BPrivate::R5MessageUnflatten(this, flatBuffer); + + if (format == kMessageMagicDano) { + BMemoryIO stream(flatBuffer, BPrivate::dano_message_size(flatBuffer)); + return BPrivate::unflatten_dano_message(format, stream, *this); + } + } + + free(fHeader); + fHeader = NULL; + fHeader = (MessageHeader *)malloc(sizeof(MessageHeader)); + if (!fHeader) + return B_NO_MEMORY; + + memcpy(fHeader, flatBuffer, sizeof(MessageHeader)); + flatBuffer += sizeof(MessageHeader); + + /* ToDo: better message validation (checksum) */ + if (fHeader->format != kMessageMagic4 || + !(fHeader->flags & MESSAGE_FLAG_VALID)) { + _Clear(); + _InitHeader(); + return B_BAD_VALUE; + } + + free(fFields); + fFields = NULL; + if (fHeader->fieldsSize > 0) { + fFields = (FieldHeader *)malloc(fHeader->fieldsSize); + if (!fFields) + return B_NO_MEMORY; + + memcpy(fFields, flatBuffer, fHeader->fieldsSize); + flatBuffer += fHeader->fieldsSize; + } + + free(fData); + fData = NULL; + if (fHeader->dataSize > 0) { + fData = (uint8 *)malloc(fHeader->dataSize); + if (!fData) + return B_NO_MEMORY; + + memcpy(fData, flatBuffer, fHeader->dataSize); + } + + fHeader->fieldsAvailable = 0; + fHeader->dataAvailable = 0; + what = fHeader->what; + 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) + return BPrivate::R5MessageUnflatten(this, stream); + + if (format == kMessageMagicDano) + return BPrivate::unflatten_dano_message(format, *stream, *this); + } + + free(fHeader); + fHeader = (MessageHeader *)malloc(sizeof(MessageHeader)); + if (!fHeader) + return B_NO_MEMORY; + + fHeader->format = format; + uint8 *header = (uint8 *)fHeader; + ssize_t result = stream->Read(header + sizeof(uint32), + sizeof(MessageHeader) - sizeof(uint32)); + if (result != sizeof(MessageHeader) - sizeof(uint32)) { + _Clear(); + _InitHeader(); + return (result >= 0 ? B_ERROR : result); + } + + /* ToDo: better message validation (checksum) */ + if (fHeader->format != kMessageMagic4 || + !(fHeader->flags & MESSAGE_FLAG_VALID)) { + _Clear(); + _InitHeader(); + return B_BAD_VALUE; + } + + free(fFields); + fFields = NULL; + if (fHeader->fieldsSize > 0) { + fFields = (FieldHeader *)malloc(fHeader->fieldsSize); + if (!fFields) + return B_NO_MEMORY; + + result = stream->Read(fFields, fHeader->fieldsSize); + if (result != fHeader->fieldsSize) { + _Clear(); + _InitHeader(); + return (result >= 0 ? B_ERROR : result); + } + } + + free(fData); + fData = NULL; + if (fHeader->dataSize > 0) { + fData = (uint8 *)malloc(fHeader->dataSize); + if (!fData) + return B_NO_MEMORY; + + result = stream->Read(fData, fHeader->dataSize); + if (result != fHeader->dataSize) { + _Clear(); + _InitHeader(); + return (result >= 0 ? B_ERROR : result); + } + } + + fHeader->fieldsAvailable = 0; + fHeader->dataAvailable = 0; + what = fHeader->what; + 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; + + 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->currentSpecifier++; + 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->currentSpecifier = index; + return B_OK; +} + + +status_t +BMessage::GetCurrentSpecifier(int32 *index, BMessage *specifier, int32 *what, + const char **property) const +{ + DEBUG_FUNCTION_ENTER; + if (fHeader->currentSpecifier < 0 + || !(fHeader->flags & MESSAGE_FLAG_WAS_DELIVERED)) + return B_BAD_SCRIPT_SYNTAX; + + if (index) + *index = fHeader->currentSpecifier; + + if (specifier) { + if (FindMessage(B_SPECIFIER_ENTRY, fHeader->currentSpecifier, + 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->currentSpecifier < 0 || + !(fHeader->flags & MESSAGE_FLAG_WAS_DELIVERED)) + return B_BAD_VALUE; + + if (fHeader->currentSpecifier >= 0) + fHeader->currentSpecifier--; + + 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->dataSize) { + FieldHeader *field = fFields; + for (int32 i = 0; i < fHeader->fieldCount; i++, field++) { + if (field->offset >= offset) + field->offset += change; + } + } + + if (change > 0) { + if (fHeader->dataAvailable >= change) { + if (offset < fHeader->dataSize) { + ssize_t length = fHeader->dataSize - offset; + memmove(fData + offset + change, fData + offset, length); + } + + fHeader->dataAvailable -= change; + fHeader->dataSize += change; + return B_OK; + } + + ssize_t size = fHeader->dataSize * 2; + size = min_c(size, fHeader->dataSize + MAX_DATA_PREALLOCATION); + size = max_c(size, fHeader->dataSize + change); + + fData = (uint8 *)realloc(fData, size); + if (!fData) + return B_NO_MEMORY; + + if (offset < fHeader->dataSize) { + memmove(fData + offset + change, fData + offset, + fHeader->dataSize - offset - change); + } + + fHeader->dataSize += change; + fHeader->dataAvailable = size - fHeader->dataSize; + } else { + ssize_t size = fHeader->dataSize; + memmove(fData + offset, fData + offset - change, size - offset + change); + fHeader->dataSize += change; + fHeader->dataAvailable -= change; + + if (fHeader->dataAvailable > MAX_DATA_PREALLOCATION) { + ssize_t available = MAX_DATA_PREALLOCATION / 2; + fData = (uint8 *)realloc(fData, fHeader->dataSize + available); + if (!fData) + return B_NO_MEMORY; + + fHeader->dataAvailable = 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, FieldHeader **result) const +{ + if (!name) + return B_BAD_VALUE; + + if (!fHeader || !fFields || !fData) + return B_NAME_NOT_FOUND; + + uint32 hash = _HashName(name) % fHeader->hashTableSize; + int32 nextField = fHeader->hashTable[hash]; + + while (nextField >= 0) { + FieldHeader *field = &fFields[nextField]; + + if (strncmp((const char *)(fData + field->offset), name, + field->nameLength) == 0) { + if (type != B_ANY_TYPE && field->type != type) + return B_BAD_TYPE; + + *result = field; + return B_OK; + } + + nextField = field->nextField; + } + + return B_NAME_NOT_FOUND; +} + + +status_t +BMessage::_AddField(const char *name, type_code type, bool isFixedSize, + FieldHeader **result) +{ + if (!fHeader) + return B_ERROR; + + if (fHeader->fieldsAvailable <= 0) { + int32 count = fHeader->fieldCount * 2 + 1; + count = min_c(count, fHeader->fieldCount + MAX_FIELD_PREALLOCATION); + + fFields = (FieldHeader *)realloc(fFields, count * sizeof(FieldHeader)); + if (!fFields) + return B_NO_MEMORY; + + fHeader->fieldsAvailable = count - fHeader->fieldCount; + } + + uint32 hash = _HashName(name) % fHeader->hashTableSize; + + int32 *nextField = &fHeader->hashTable[hash]; + while (*nextField >= 0) + nextField = &fFields[*nextField].nextField; + *nextField = fHeader->fieldCount; + + FieldHeader *field = &fFields[fHeader->fieldCount]; + field->type = type; + field->flags = FIELD_FLAG_VALID; + if (isFixedSize) + field->flags |= FIELD_FLAG_FIXED_SIZE; + + field->count = 0; + field->dataSize = 0; + field->allocated = 0; + field->nextField = -1; + field->offset = fHeader->dataSize; + field->nameLength = strlen(name) + 1; + _ResizeData(field->offset, field->nameLength); + memcpy(fData + field->offset, name, field->nameLength); + + fHeader->fieldsAvailable--; + fHeader->fieldsSize += sizeof(FieldHeader); + fHeader->fieldCount++; + *result = field; + return B_OK; +} + + +status_t +BMessage::_RemoveField(FieldHeader *field) +{ + int32 index = ((uint8 *)field - (uint8 *)fFields) / sizeof(FieldHeader); + int32 nextField = field->nextField; + if (nextField > index) + nextField--; + + int32 *value = fHeader->hashTable; + for (int32 i = 0; i < fHeader->hashTableSize; i++, value++) { + if (*value > index) + *value -= 1; + else if (*value == index) + *value = nextField; + } + + FieldHeader *other = fFields; + for (int32 i = 0; i < fHeader->fieldCount; i++, other++) { + if (other->nextField > index) + other->nextField--; + else if (other->nextField == index) + other->nextField = nextField; + } + + _ResizeData(field->offset, -(field->dataSize + field->nameLength)); + + ssize_t size = fHeader->fieldsSize - (index + 1) * sizeof(FieldHeader); + memmove(fFields + index, fFields + index + 1, size); + fHeader->fieldsSize -= sizeof(FieldHeader); + fHeader->fieldCount--; + fHeader->fieldsAvailable++; + + if (fHeader->fieldsAvailable > MAX_FIELD_PREALLOCATION) { + ssize_t available = MAX_FIELD_PREALLOCATION / 2; + fFields = (FieldHeader *)realloc(fFields, fHeader->fieldsSize + + available * sizeof(FieldHeader)); + if (!fFields) + return B_NO_MEMORY; + + fHeader->fieldsAvailable = 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; + + FieldHeader *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->nameLength + field->dataSize; + if (field->flags & FIELD_FLAG_FIXED_SIZE) { + if (field->count) { + ssize_t size = field->dataSize / field->count; + if (size != numBytes) + return B_BAD_VALUE; + } + + _ResizeData(offset, numBytes); + memcpy(fData + offset, data, numBytes); + field->dataSize += numBytes; + } else { + int32 change = numBytes + sizeof(numBytes); + _ResizeData(offset, change); + memcpy(fData + offset, &numBytes, sizeof(numBytes)); + memcpy(fData + offset + sizeof(numBytes), data, numBytes); + field->dataSize += 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; + + FieldHeader *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->nameLength; + if (field->flags & FIELD_FLAG_FIXED_SIZE) { + ssize_t size = field->dataSize / field->count; + _ResizeData(offset + index * size, -size); + field->dataSize -= 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->dataSize -= currentSize; + } + + field->count--; + return B_OK; +} + + +status_t +BMessage::RemoveName(const char *name) +{ + DEBUG_FUNCTION_ENTER; + FieldHeader *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; + + free(fFields); + fFields = NULL; + free(fData); + fData = NULL; + + fHeader->fieldsSize = 0; + fHeader->dataSize = 0; + fHeader->fieldsAvailable = 0; + fHeader->dataAvailable = 0; + fHeader->currentSpecifier = -1; + fHeader->fieldCount = 0; + + fHeader->flags &= ~MESSAGE_FLAG_HAS_SPECIFIERS; + + // initializing the hash table to -1 because 0 is a valid index + memset(&fHeader->hashTable, 255, sizeof(fHeader->hashTable)); + 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; + FieldHeader *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->dataSize / field->count; + *data = fData + field->offset + field->nameLength + index * *numBytes; + } else { + uint8 *pointer = fData + field->offset + field->nameLength; + + 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; + + FieldHeader *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->dataSize / field->count; + if (size != numBytes) + return B_BAD_VALUE; + + memcpy(fData + field->offset + field->nameLength + index * size, data, + size); + } else { + uint32 offset = field->offset + field->nameLength; + 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->dataSize += change; + } + + return B_OK; +} + + +bool +BMessage::HasData(const char *name, type_code type, int32 index) const +{ + DEBUG_FUNCTION_ENTER; + FieldHeader *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_ENTER; + 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 = NULL; +} + + +void +BMessage::_StaticCleanup() +{ + DEBUG_FUNCTION_ENTER; + 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_ENTER; + delete sMsgCache; + sMsgCache = NULL; +} + + +int32 +BMessage::_StaticGetCachedReplyPort() +{ + DEBUG_FUNCTION_ENTER; + 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, bool preferred, + bigtime_t timeout, bool replyRequired, BMessenger &replyTo) const +{ + DEBUG_FUNCTION_ENTER; + uint32 oldFlags = fHeader->flags; + int32 oldTarget = fHeader->target; + port_id oldReplyPort = fHeader->replyPort; + int32 oldReplyTarget = fHeader->replyTarget; + team_id oldReplyTeam = fHeader->replyTeam; + + if (!replyTo.IsValid()) { + BMessenger::Private(replyTo).SetTo(fHeader->replyTeam, + fHeader->replyPort, fHeader->replyTarget, + fHeader->replyTarget == B_PREFERRED_TOKEN); + + if (!replyTo.IsValid()) + replyTo = be_app_messenger; + } + + BMessenger::Private replyToPrivate(replyTo); + + if (replyRequired) + fHeader->flags |= MESSAGE_FLAG_REPLY_REQUIRED; + else + fHeader->flags &= ~MESSAGE_FLAG_REPLY_REQUIRED; + + fHeader->target = (preferred ? B_PREFERRED_TOKEN : token); + fHeader->replyTeam = replyToPrivate.Team(); + fHeader->replyPort = replyToPrivate.Port(); + fHeader->replyTarget = (replyToPrivate.IsPreferredTarget() + ? B_PREFERRED_TOKEN : replyToPrivate.Token()); + fHeader->flags |= MESSAGE_FLAG_WAS_DELIVERED; + + /* ToDo: we can use _kern_writev_port to send the three parts directly: + iovec vectors[3]; + vectors[0].iov_base = fHeader; + vectors[0].iov_len = sizeof(MessageHeader); + vectors[1].iov_base = fFields; + vectors[1].iov_len = fHeader->fieldsSize; + vectors[2].iov_base = fData; + vectors[2].iov_len = fHeader->dataSize; + status_t result; + + do { + result = _kern_writev_port_etc(port, 'pjpp', vectors, 3, + _NativeFlattenedSize(), B_RELATIVE_TIMEOUT, timeout); + } while (result == B_INTERRUPTED); + */ + + ssize_t size = _NativeFlattenedSize(); + char *buffer = new char[size]; + status_t result = _NativeFlatten(buffer, size); + if (result < B_OK) + goto error; + + do { + result = write_port_etc(port, 'pjpp', buffer, size, + B_RELATIVE_TIMEOUT, timeout); + } while (result == B_INTERRUPTED); + +error: + fHeader->flags = oldFlags; + fHeader->target = oldTarget; + fHeader->replyPort = oldReplyPort; + fHeader->replyTarget = oldReplyTarget; + fHeader->replyTeam = oldReplyTeam; + delete[] buffer; + return result; +} + + +status_t +BMessage::_SendMessage(port_id port, team_id portOwner, int32 token, + bool preferred, 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, true); + result = _SendMessage(port, token, preferred, 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, bool preferred, bigtime_t timeout) +{ + DEBUG_FUNCTION_ENTER; + if (!data) + return B_BAD_VALUE; + + uint32 magic = *(uint32*)data; + + if (magic == kMessageMagic4 || magic == kMessageMagic4Swapped) { + MessageHeader *header = (MessageHeader *)data; + header->target = (preferred ? B_PREFERRED_TOKEN : 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 = (preferred ? B_PREFERRED_TOKEN : token); + } else if (((KMessage::Header *)data)->magic == KMessage::kMessageHeaderMagic) { + KMessage::Header *header = (KMessage::Header *)data; + header->targetToken = (preferred ? B_PREFERRED_TOKEN : token); + } else { + return B_NOT_A_MESSAGE; + } + + // send the message + status_t result; + + do { + result = write_port_etc(port, 'pjpp', 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_ENTER; + status_t result; + do { + result = port_buffer_size_etc(replyPort, B_RELATIVE_TIMEOUT, timeout); + } while (result == B_INTERRUPTED); + + if (result < B_OK) + return result; + + // The API lied. It really isn't an error code, but the message size... + char *buffer = new char[result]; + + do { + result = read_port(replyPort, pCode, buffer, result); + } while (result == B_INTERRUPTED); + + if (result < B_OK || *pCode != 'pjpp') { + delete[] buffer; + return (result < B_OK ? result : B_ERROR); + } + + result = reply->Unflatten(buffer); + delete[] buffer; + return result; +} + + +static status_t +convert_message(const KMessage *fromMessage, BMessage *toMessage) +{ + DEBUG_FUNCTION_ENTER; + 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, strlen(string) + 1, 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->FlattenedSize(); + char buffer[size]; + + status_t error = message->Flatten(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->FlattenedSize(); + char buffer[size]; + + status_t error = message->Flatten(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); +} diff --git a/src/kits/app/MessageQueue.cpp b/src/kits/app/MessageQueue.cpp index 083ec6e5ae..376894d057 100644 --- a/src/kits/app/MessageQueue.cpp +++ b/src/kits/app/MessageQueue.cpp @@ -71,7 +71,11 @@ BMessageQueue::~BMessageQueue() BMessage *theMessage = fTheQueue; while (theMessage != NULL) { BMessage *messageToDelete = theMessage; +#ifndef USING_MESSAGE4 theMessage = theMessage->link; +#else + theMessage = theMessage->fQueueLink; +#endif delete messageToDelete; } } @@ -112,7 +116,11 @@ BMessageQueue::AddMessage(BMessage *message) // The message passed in will be the last message on the queue so its // link member should be set to null. +#ifndef USING_MESSAGE4 message->link = NULL; +#else + message->fQueueLink = NULL; +#endif // We now have one more BMessage on the queue. fMessageCount++; @@ -127,7 +135,11 @@ BMessageQueue::AddMessage(BMessage *message) // BMessage at the end. The last BMessage prior to this AddMessage() // is fQueueTail. The BMessage at fQueueTail needs to point to the // new last message, the one being added. +#ifndef USING_MESSAGE4 fQueueTail->link = message; +#else + fQueueTail->fQueueLink = message; +#endif // Now update the fQueueTail to point to this new last message. fQueueTail = message; @@ -165,7 +177,11 @@ BMessageQueue::RemoveMessage(BMessage *message) if (fTheQueue == message) { // We need to special case the handling of removing the first element. // First, the new front element will be the next one. +#ifndef USING_MESSAGE4 fTheQueue = fTheQueue->link; +#else + fTheQueue = fTheQueue->fQueueLink; +#endif // Must decrement the count of elements since the front one is being // removed. @@ -191,7 +207,11 @@ BMessageQueue::RemoveMessage(BMessage *message) while (messageIter != NULL) { // If the next message after this (ie second, then third etc) is // the one we are looking for. +#ifndef USING_MESSAGE4 if (messageIter->link == message) { +#else + if (messageIter->fQueueLink == message) { +#endif // At this point, this is what we have: // messageIter - the BMessage in the queue just before the // match @@ -202,13 +222,21 @@ BMessageQueue::RemoveMessage(BMessage *message) // The next step is to link the BMessage just before the match // to the one just after the match. This removes the match from // the queue. +#ifndef USING_MESSAGE4 messageIter->link = message->link; +#else + messageIter->fQueueLink = message->fQueueLink; +#endif // One less element on the queue. fMessageCount--; // If there is no BMessage after the match is the +#ifndef USING_MESSAGE4 if (message->link == NULL) { +#else + if (message->fQueueLink == NULL) { +#endif // That means that we just removed the last element from the // queue. The new last element then must be messageIter. fQueueTail = messageIter; @@ -219,7 +247,11 @@ BMessageQueue::RemoveMessage(BMessage *message) } // No match yet, go to the next element in the list. +#ifndef USING_MESSAGE4 messageIter = messageIter->link; +#else + messageIter = messageIter->fQueueLink; +#endif } } } @@ -290,7 +322,11 @@ BMessageQueue::FindMessage(int32 index) const // reaches zero. index--; // Increment the messageIter to the next BMessage on the queue. +#ifndef USING_MESSAGE4 messageIter = messageIter->link; +#else + messageIter = messageIter->fQueueLink; +#endif } // If no match was found, messageIter will be NULL since that is the only @@ -347,7 +383,11 @@ BMessageQueue::FindMessage(uint32 what, index--; } // Increment the messageIter to the next BMessage on the queue. +#ifndef USING_MESSAGE4 messageIter = messageIter->link; +#else + messageIter = messageIter->fQueueLink; +#endif } // If no match was found, messageIter will be NULL since that is the only @@ -422,7 +462,11 @@ BMessageQueue::NextMessage(void) fMessageCount--; // The new front of the list is moved forward thereby removing the // first element from the queue. +#ifndef USING_MESSAGE4 fTheQueue = fTheQueue->link; +#else + fTheQueue = fTheQueue->fQueueLink; +#endif // If the queue is empty after removing the front element. if (fTheQueue == NULL) { // We need to set the tail of the queue to NULL since the queue diff --git a/src/kits/app/MessageUtils4.cpp b/src/kits/app/MessageUtils4.cpp new file mode 100644 index 0000000000..d9f6115c5b --- /dev/null +++ b/src/kits/app/MessageUtils4.cpp @@ -0,0 +1,106 @@ +/* + * Copyright 2001-2005, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Erik Jaesler (erik@cgsoftware.com) + */ + +/** Extra messaging utility functions */ + +#include +#include + +#include + +namespace BPrivate { + +uint32 +CalculateChecksum(const uint8 *buffer, int32 size) +{ + uint32 sum = 0; + uint32 temp = 0; + + while (size > 3) { +#if defined(__INTEL__) + sum += B_SWAP_INT32(*(int32 *)buffer); +#else + sum += *(int32 *)buffer; +#endif + buffer += 4; + size -= 4; + } + + while (size > 0) { + temp = (temp << 8) + *buffer++; + size -= 1; + } + + return sum + temp; +} + + +/* entry_ref support functions */ +status_t +entry_ref_flatten(char *buffer, size_t *size, const entry_ref *ref) +{ + memcpy((void *)buffer, (const void *)&ref->device, sizeof(ref->device)); + buffer += sizeof(ref->device); + memcpy((void *)buffer, (const void *)&ref->directory, sizeof(ref->directory)); + buffer += sizeof (ref->directory); + + size_t nameLength = 0; + if (ref->name) { + nameLength = strlen(ref->name) + 1; + memcpy((void *)buffer, (const void *)ref->name, nameLength); + } + + *size = sizeof(ref->device) + sizeof(ref->directory) + nameLength; + return B_OK; +} + + +status_t +entry_ref_unflatten(entry_ref *ref, const char *buffer, size_t size) +{ + if (size < sizeof(ref->device) + sizeof(ref->directory)) { + *ref = entry_ref(); + return B_BAD_VALUE; + } + + memcpy((void *)&ref->device, (const void *)buffer, sizeof(ref->device)); + buffer += sizeof (ref->device); + memcpy((void *)&ref->directory, (const void *)buffer, sizeof(ref->directory)); + buffer += sizeof(ref->directory); + + if (ref->device != -1 && size > sizeof(ref->device) + + sizeof(ref->directory)) { + ref->set_name(buffer); + if (ref->name == NULL) { + *ref = entry_ref(); + return B_NO_MEMORY; + } + } else + ref->set_name(NULL); + + return B_OK; +} + + +status_t +entry_ref_swap(char *buffer, size_t size) +{ + if (size < sizeof(dev_t) + sizeof(ino_t)) + return B_BAD_VALUE; + + dev_t *dev = (dev_t *)buffer; + *dev = B_SWAP_INT32(*dev); + buffer += sizeof(dev_t); + + ino_t *ino = (ino_t *)buffer; + *ino = B_SWAP_INT64(*ino); + + return B_OK; +} + +} // namespace BPrivate diff --git a/src/kits/app/Messenger.cpp b/src/kits/app/Messenger.cpp index 2294ee34d0..e4ba708a25 100644 --- a/src/kits/app/Messenger.cpp +++ b/src/kits/app/Messenger.cpp @@ -51,6 +51,10 @@ #include "ObjectLocker.h" #include "TokenSpace.h" +#ifdef USING_MESSAGE4 +#include +#endif + // Local Includes -------------------------------------------------------------- // Local Defines --------------------------------------------------------------- @@ -353,8 +357,13 @@ BMessenger::SendMessage(BMessage *message, BMessenger replyTo, if (!message) return B_BAD_VALUE; +#ifndef USING_MESSAGE4 return message->_send_(fPort, fHandlerToken, fPreferredTarget, timeout, false, replyTo); +#else + return BMessage::Private(message).SendMessage(fPort, fHandlerToken, + fPreferredTarget, timeout, false, replyTo); +#endif } // SendMessage @@ -413,9 +422,15 @@ BMessenger::SendMessage(BMessage *message, BMessage *reply, { status_t error = (message && reply ? B_OK : B_BAD_VALUE); if (error == B_OK) { +#ifndef USING_MESSAGE4 error = message->send_message(fPort, fTeam, fHandlerToken, fPreferredTarget, reply, deliveryTimeout, replyTimeout); +#else + error = BMessage::Private(message).SendMessage(fPort, fTeam, + fHandlerToken, fPreferredTarget, reply, deliveryTimeout, + replyTimeout); +#endif // Map this error for now: if (error == B_BAD_TEAM_ID) error = B_BAD_PORT_ID; diff --git a/src/kits/app/r5_message.cpp b/src/kits/app/r5_message.cpp new file mode 100644 index 0000000000..77238a1d44 --- /dev/null +++ b/src/kits/app/r5_message.cpp @@ -0,0 +1,396 @@ +#include +#include +#include +#include +#include "r5_message.h" + +#define R5_MESSAGE_FLAG_VALID 0x01 +#define R5_MESSAGE_FLAG_INCLUDE_TARGET 0x02 +#define R5_MESSAGE_FLAG_INCLUDE_REPLY 0x04 +#define R5_MESSAGE_FLAG_SCRIPT_MESSAGE 0x08 + +#define R5_FIELD_FLAG_VALID 0x01 +#define R5_FIELD_FLAG_MINI_DATA 0x02 +#define R5_FIELD_FLAG_FIXED_SIZE 0x04 +#define R5_FIELD_FLAG_SINGLE_ITEM 0x08 + +namespace BPrivate { + +static const uint32 kR5MessageMagic = 'FOB1'; +static const uint32 kR5MessageMagicSwapped = '1BOF'; + + +typedef struct r5_message_header_s { + uint32 magic; // kR5MessageMagic + uint32 checksum; + ssize_t flattenedSize; + int32 what; + uint8 flags; +} _PACKED R5MessageHeader; + + +inline int32 +pad_to_8(int32 value) +{ + return (value + 7) & ~7; +} + + +ssize_t +R5MessageFlattenedSize(const BMessage *message) +{ + BMessage::Private messagePrivate((BMessage *)message); + MessageHeader *header = messagePrivate.GetMessageHeader(); + ssize_t flattenedSize = sizeof(R5MessageHeader); + + if (header->target != B_NULL_TOKEN) + flattenedSize += sizeof(int32); + + if (header->replyPort >= 0 && header->replyTarget != B_NULL_TOKEN + && header->replyTeam >= 0) { + + // reply info + flattenedSize += sizeof(port_id) + sizeof(int32) + sizeof(team_id); + + // big flags + flattenedSize += 4; + } + + uint8 *data = messagePrivate.GetMessageData(); + FieldHeader *field = messagePrivate.GetMessageFields(); + for (int32 i = 0; i < header->fieldCount; i++, field++) { + // flags and type + flattenedSize += 1 + sizeof(type_code); + + // item count +#if 0 + bool miniData = field->dataSize <= 255 && field->count <= 255; +#else + // ToDo: we don't know the R5 dataSize yet (padding) + bool miniData = false; +#endif + if (field->count > 1) + flattenedSize += (miniData ? sizeof(uint8) : sizeof(int32)); + + // data size + flattenedSize += (miniData ? sizeof(uint8) : sizeof(ssize_t)); + + // name length and name + flattenedSize += 1 + min_c(field->nameLength - 1, 255); + + // data + if (field->flags & FIELD_FLAG_FIXED_SIZE) + flattenedSize += field->dataSize; + else { + uint8 *source = data + field->offset + field->nameLength; + + for (int32 i = 0; i < field->count; i++) { + ssize_t itemSize = *(ssize_t *)source + sizeof(ssize_t); + flattenedSize += pad_to_8(itemSize); + source += itemSize; + } + } + } + + // pseudo field with flags 0 + return flattenedSize + 1; +} + + +status_t +R5MessageFlatten(const BMessage *message, char *buffer, ssize_t size) +{ + BMessage::Private messagePrivate((BMessage *)message); + MessageHeader *header = messagePrivate.GetMessageHeader(); + uint8 *data = messagePrivate.GetMessageData(); + + R5MessageHeader *r5header = (R5MessageHeader *)buffer; + uint8 *pointer = (uint8 *)buffer + sizeof(R5MessageHeader); + + r5header->magic = kR5MessageMagic; + r5header->what = message->what; + r5header->checksum = 0; + + uint8 flags = R5_MESSAGE_FLAG_VALID; + if (header->target != B_NULL_TOKEN) { + *(int32 *)pointer = header->target; + pointer += sizeof(int32); + flags |= R5_MESSAGE_FLAG_INCLUDE_TARGET; + } + + if (header->replyPort >= 0 && header->replyTarget != B_NULL_TOKEN + && header->replyTeam >= 0) { + // reply info + *(port_id *)pointer = header->replyPort; + pointer += sizeof(port_id); + *(int32 *)pointer = header->replyTarget; + pointer += sizeof(int32); + *(team_id *)pointer = header->replyTeam; + pointer += sizeof(team_id); + + // big flags + *pointer = (header->replyTarget == B_PREFERRED_TOKEN ? 1 : 0); + pointer++; + + *pointer = (header->flags & MESSAGE_FLAG_REPLY_REQUIRED ? 1 : 0); + pointer++; + + *pointer = (header->flags & MESSAGE_FLAG_REPLY_DONE ? 1 : 0); + pointer++; + + *pointer = (header->flags & MESSAGE_FLAG_IS_REPLY ? 1 : 0); + pointer++; + + flags |= R5_MESSAGE_FLAG_INCLUDE_REPLY; + } + + if (header->flags & MESSAGE_FLAG_HAS_SPECIFIERS) + flags |= R5_MESSAGE_FLAG_SCRIPT_MESSAGE; + + r5header->flags = flags; + + // store the header size - used for the checksum later + ssize_t headerSize = (uint32)pointer - (uint32)buffer; + + // collect and add the data + FieldHeader *field = messagePrivate.GetMessageFields(); + for (int32 i = 0; i < header->fieldCount; i++, field++) { + flags = R5_FIELD_FLAG_VALID; + + if (field->count == 1) + flags |= R5_FIELD_FLAG_SINGLE_ITEM; + // ToDo: we don't really know the data size now (padding missing) + if (field->dataSize <= 255 && field->count <= 255) + ;//flags |= R5_FIELD_FLAG_MINI_DATA; + if (field->flags & FIELD_FLAG_FIXED_SIZE) + flags |= R5_FIELD_FLAG_FIXED_SIZE; + + *pointer = flags; + pointer++; + + *(type_code *)pointer = field->type; + pointer += sizeof(type_code); + + if (!(flags & R5_FIELD_FLAG_SINGLE_ITEM)) { + if (flags & R5_FIELD_FLAG_MINI_DATA) { + *pointer = (uint8)field->count; + pointer++; + } else { + *(int32 *)pointer = field->count; + pointer += sizeof(int32); + } + } + + // we may have to adjust this to account for padding later + uint8 *fieldSize = pointer; + if (flags & R5_FIELD_FLAG_MINI_DATA) { + *pointer = (uint8)field->dataSize; + pointer++; + } else { + *(ssize_t *)pointer = field->dataSize; + pointer += sizeof(ssize_t); + } + + // name + int32 nameLength = min_c(field->nameLength - 1, 255); + *pointer = (uint8)nameLength; + pointer++; + + strncpy((char *)pointer, (char *)data + field->offset, nameLength); + pointer += nameLength; + + // data + uint8 *source = data + field->offset + field->nameLength; + if (flags & R5_FIELD_FLAG_FIXED_SIZE) { + memcpy(pointer, source, field->dataSize); + pointer += field->dataSize; + } else { + uint8 *previous = pointer; + for (int32 i = 0; i < field->count; i++) { + ssize_t itemSize = *(ssize_t *)source + sizeof(ssize_t); + memcpy(pointer, source, itemSize); + pointer += pad_to_8(itemSize); + source += itemSize; + } + + // adjust the field size to the padded value + if (flags & R5_FIELD_FLAG_MINI_DATA) + *fieldSize = (uint8)(pointer - previous); + else + *(ssize_t *)fieldSize = (pointer - previous); + } + } + + // terminate the fields with a pseudo field with flags 0 (not valid) + *pointer = 0; + pointer++; + + // calculate the flattened size from the pointers + r5header->flattenedSize = (uint32)pointer - (uint32)buffer; + r5header->checksum = BPrivate::CalculateChecksum((uint8 *)(buffer + 8), + headerSize - 8); + return B_OK; +} + + +status_t +R5MessageFlatten(const BMessage *message, BDataIO *stream, ssize_t *_size) +{ + // ToDo: This is not very cheap... + ssize_t size = R5MessageFlattenedSize(message); + char *buffer = (char *)malloc(size); + if (!buffer) + return B_NO_MEMORY; + + status_t result = R5MessageFlatten(message, buffer, size); + if (result < B_OK) { + free(buffer); + return result; + } + + ssize_t written = stream->Write(buffer, size); + if (written != size) { + free(buffer); + return (written >= 0 ? B_ERROR : written); + } + + if (_size) + *_size = size; + + free(buffer); + return B_OK; +} + + +status_t +R5MessageUnflatten(BMessage *message, const char *flatBuffer) +{ + R5MessageHeader *r5header = (R5MessageHeader *)flatBuffer; + BMemoryIO stream(flatBuffer + 4, r5header->flattenedSize - 4); + return R5MessageUnflatten(message, &stream); +} + + +status_t +R5MessageUnflatten(BMessage *message, BDataIO *stream) +{ + TReadHelper reader(stream); + BMessage::Private messagePrivate(message); + MessageHeader *header = messagePrivate.GetMessageHeader(); + + // the stream is already advanced by the size of the "format" + R5MessageHeader r5header; + reader(((uint8 *)&r5header) + sizeof(uint32), + sizeof(r5header) - sizeof(uint32)); + + messagePrivate.Clear(); + messagePrivate.InitHeader(); + + header->what = message->what = r5header.what; + if (r5header.flags & R5_MESSAGE_FLAG_INCLUDE_TARGET) + reader(header->target); + + if (r5header.flags & R5_MESSAGE_FLAG_INCLUDE_REPLY) { + // reply info + reader(header->replyPort); + reader(header->replyTarget); + reader(header->replyTeam); + + // big flags + uint8 bigFlag; + reader(bigFlag); + if (bigFlag) + header->replyTarget = B_PREFERRED_TOKEN; + + reader(bigFlag); + if (bigFlag) + header->flags |= MESSAGE_FLAG_REPLY_REQUIRED; + + reader(bigFlag); + if (bigFlag) + header->flags |= MESSAGE_FLAG_REPLY_DONE; + + reader(bigFlag); + if (bigFlag) + header->flags |= MESSAGE_FLAG_IS_REPLY; + } + + if (r5header.flags & R5_MESSAGE_FLAG_SCRIPT_MESSAGE) + header->flags |= MESSAGE_FLAG_HAS_SPECIFIERS; + + uint8 flags; + reader(flags); + while (flags & R5_FIELD_FLAG_VALID) { + bool fixedSize = flags & R5_FIELD_FLAG_FIXED_SIZE; + bool miniData = flags & R5_FIELD_FLAG_MINI_DATA; + bool singleItem = flags & R5_FIELD_FLAG_SINGLE_ITEM; + + type_code type; + reader(type); + + int32 itemCount; + if (!singleItem) { + if (miniData) { + uint8 miniCount; + reader(miniCount); + itemCount = miniCount; + } else + reader(itemCount); + } else + itemCount = 1; + + ssize_t dataSize; + if (miniData) { + uint8 miniSize; + reader(miniSize); + dataSize = miniSize; + } else + reader(dataSize); + + // name + uint8 nameLength; + reader(nameLength); + + char nameBuffer[256]; + reader(nameBuffer, nameLength); + nameBuffer[nameLength] = 0; + + uint8 *buffer = (uint8 *)malloc(dataSize); + uint8 *pointer = buffer; + reader(buffer, dataSize); + + status_t result = B_OK; + ssize_t itemSize = 0; + if (fixedSize) + itemSize = dataSize / itemCount; + + for (int32 i = 0; i < itemCount; i++) { + if (!fixedSize) { + itemSize = *(ssize_t *)pointer; + pointer += sizeof(ssize_t); + } + + result = message->AddData(nameBuffer, type, pointer, itemSize, + fixedSize, itemCount); + + if (result < B_OK) { + free(buffer); + return result; + } + + if (fixedSize) + pointer += itemSize; + else + pointer += pad_to_8(itemSize + sizeof(ssize_t)) - sizeof(ssize_t); + } + + free(buffer); + + // flags of next field or termination byte + reader(flags); + } + + return B_OK; +} + +} // namespace BPrivate diff --git a/src/kits/app/r5_message.h b/src/kits/app/r5_message.h new file mode 100644 index 0000000000..a6d28e7ed1 --- /dev/null +++ b/src/kits/app/r5_message.h @@ -0,0 +1,17 @@ +#ifndef _R5_MESSAGE_H_ +#define _R5_MESSAGE_H_ + +class BMessage; +class BDataIO; + +namespace BPrivate { + +ssize_t R5MessageFlattenedSize(const BMessage *message); +status_t R5MessageFlatten(const BMessage *message, char *buffer, ssize_t size); +status_t R5MessageFlatten(const BMessage *message, BDataIO *stream, ssize_t *size); +status_t R5MessageUnflatten(BMessage *message, const char *flatBuffer); +status_t R5MessageUnflatten(BMessage *message, BDataIO *stream); + +} + +#endif diff --git a/src/kits/interface/View.cpp b/src/kits/interface/View.cpp index 4fe0528892..c5a846400b 100644 --- a/src/kits/interface/View.cpp +++ b/src/kits/interface/View.cpp @@ -42,6 +42,10 @@ #include #include +#ifdef USING_MESSAGE4 +#include +#endif + #include @@ -1261,7 +1265,11 @@ BView::DragMessage(BMessage *message, BRect dragRect, BHandler *replyTo) message->AddInt32("buttons", buttons); } +#ifndef USING_MESSAGE4 _set_message_reply_(message, BMessenger(replyTo, replyTo->Looper())); +#else + BMessage::Private(message).SetReply(BMessenger(replyTo, replyTo->Looper())); +#endif int32 bufferSize = message->FlattenedSize(); char *buffer = new char[bufferSize]; @@ -1314,7 +1322,11 @@ BView::DragMessage(BMessage *message, BBitmap *image, message->AddInt32("buttons", buttons); } +#ifndef USING_MESSAGE4 _set_message_reply_(message, BMessenger(replyTo, replyTo->Looper())); +#else + BMessage::Private(message).SetReply(BMessenger(replyTo, replyTo->Looper())); +#endif int32 bufferSize = message->FlattenedSize(); char *buffer = new char[bufferSize]; diff --git a/src/kits/interface/Window.cpp b/src/kits/interface/Window.cpp index 4324a1fe29..95567fb2df 100644 --- a/src/kits/interface/Window.cpp +++ b/src/kits/interface/Window.cpp @@ -34,6 +34,10 @@ #include #include +#ifdef USING_MESSAGE4 +#include +#endif + #include #include #include @@ -2249,15 +2253,29 @@ BWindow::task_looper() dispatchNextMessage = false; } else { // Get the target handler +#ifdef USING_MESSAGE4 + // Use the private BMessage accessor to determine if we are + // using the preferred handler, or if a target has been + // specified + BHandler *handler; + BMessage::Private messagePrivate(fLastMessage); + bool usePreferred = messagePrivate.UsePreferredTarget(); +#else // Use BMessage friend functions to determine if we are using the // preferred handler, or if a target has been specified BHandler* handler; bool usePreferred = _use_preferred_target_(fLastMessage); +#endif if (usePreferred) { handler = PreferredHandler(); } else { +#ifndef USING_MESSAGE4 gDefaultTokens.GetToken(_get_message_target_(fLastMessage), B_HANDLER_TOKEN, (void **)&handler); +#else + gDefaultTokens.GetToken(messagePrivate.GetTarget(), + B_HANDLER_TOKEN, (void **)&handler); +#endif } // if a target was given, and we should not use the preferred diff --git a/src/servers/app/ServerWindow.cpp b/src/servers/app/ServerWindow.cpp index adf09969b1..27d9d34066 100644 --- a/src/servers/app/ServerWindow.cpp +++ b/src/servers/app/ServerWindow.cpp @@ -2094,6 +2094,7 @@ ServerWindow::SendMessageToClient(const BMessage* msg, int32 target, bool usePre char* buffer = new(nothrow) char[size]; status_t ret; +#ifndef USING_MESSAGE4 if ((ret = msg->Flatten(buffer, size)) == B_OK) { ret = BMessage::Private::SendFlattenedMessage(buffer, size, fClientLooperPort, target, usePreferred, 100000); @@ -2101,6 +2102,12 @@ ServerWindow::SendMessageToClient(const BMessage* msg, int32 target, bool usePre fprintf(stderr, "ServerWindow::SendMessageToClient(): %s\n", strerror(ret)); } else printf("PANIC: ServerWindow %s: can't flatten message in 'SendMessageToClient()'\n", fTitle); +#else + BMessenger reply; + BMessage::Private messagePrivate((BMessage *)msg); + ret = messagePrivate.SendMessage(fClientLooperPort, target, usePreferred, + 100000, false, reply); +#endif delete[] buffer; diff --git a/src/servers/app/Utils.cpp b/src/servers/app/Utils.cpp index df0e46cf2c..b5d7b56da8 100644 --- a/src/servers/app/Utils.cpp +++ b/src/servers/app/Utils.cpp @@ -31,10 +31,14 @@ SendMessage(port_id port, BMessage *message, int32 target) if (!message) return; +#ifndef USING_MESSAGE4 if (target == -1) _set_message_target_(message, target, true); else _set_message_target_(message, target, false); +#else + BMessage::Private(message).SetTarget(target, target == -1); +#endif ssize_t size = message->FlattenedSize(); char *buffer = new char[size]; @@ -47,6 +51,7 @@ SendMessage(port_id port, BMessage *message, int32 target) } +#ifndef USING_MESSAGE4 /* Below are friend functions for BMessage which currently are not in the Message.cpp that we need to send messages to BLoopers and such. Placed here to allow compilation. @@ -79,6 +84,7 @@ _use_preferred_target_(BMessage *msg) { return msg->fPreferred; } +#endif const char * diff --git a/src/servers/app/Utils.h b/src/servers/app/Utils.h index 974e1ff543..569f71d040 100644 --- a/src/servers/app/Utils.h +++ b/src/servers/app/Utils.h @@ -31,6 +31,10 @@ #include #include +#ifdef USING_MESSAGE4 +#include +#endif + void SendMessage(port_id port, BMessage *message, int32 target=-1); const char *MsgCodeToString(int32 code); BString MsgCodeToBString(int32 code); diff --git a/src/servers/registrar/MessageDeliverer.cpp b/src/servers/registrar/MessageDeliverer.cpp index 66cc7dc0f5..257d932fa0 100644 --- a/src/servers/registrar/MessageDeliverer.cpp +++ b/src/servers/registrar/MessageDeliverer.cpp @@ -605,6 +605,7 @@ MessageDeliverer::DeliverMessage(BMessage *message, MessagingTargetSet &targets, if (!message) return B_BAD_VALUE; +#ifndef USING_MESSAGE4 // Set the token now, so that the header contains room for it. // It will be set when sending the message anyway, but if it is not set // before flattening, the header will not contain room for it, and it @@ -616,6 +617,13 @@ MessageDeliverer::DeliverMessage(BMessage *message, MessagingTargetSet &targets, status_t error = message->Flatten(&mallocIO); if (error != B_OK) return error; +#else + // flatten the message + BMallocIO mallocIO; + status_t error = BMessage::Private(message).NativeFlatten(&mallocIO, NULL); + if (error < B_OK) + return error; +#endif return DeliverMessage(mallocIO.Buffer(), mallocIO.BufferLength(), targets, timeout); diff --git a/src/tests/apps/fake_app_server/Utils.cpp b/src/tests/apps/fake_app_server/Utils.cpp index f33a6b7d16..a59cfa6392 100644 --- a/src/tests/apps/fake_app_server/Utils.cpp +++ b/src/tests/apps/fake_app_server/Utils.cpp @@ -42,10 +42,14 @@ void SendMessage(port_id port, BMessage *message, int32 target) if(!message) return; +#ifndef USING_MESSAGE4 if(target==-1) _set_message_target_(message,target,true); else _set_message_target_(message,target,false); +#else + BMessage::Private(message).SetTarget(target, target == -1); +#endif ssize_t flatsize=message->FlattenedSize(); char *buffer=new char[flatsize]; @@ -57,6 +61,7 @@ void SendMessage(port_id port, BMessage *message, int32 target) delete message; } +#ifndef USING_MESSAGE4 /* Below are friend functions for BMessage which currently are not in the Message.cpp that we need to send messages to BLoopers and such. Placed here to allow compilation. @@ -87,6 +92,7 @@ bool _use_preferred_target_(BMessage *msg) { return msg->fPreferred; } +#endif // !USING_MESSAGE4 const char *MsgCodeToString(int32 code) { diff --git a/src/tests/apps/fake_app_server/Utils.h b/src/tests/apps/fake_app_server/Utils.h index 974e1ff543..569f71d040 100644 --- a/src/tests/apps/fake_app_server/Utils.h +++ b/src/tests/apps/fake_app_server/Utils.h @@ -31,6 +31,10 @@ #include #include +#ifdef USING_MESSAGE4 +#include +#endif + void SendMessage(port_id port, BMessage *message, int32 target=-1); const char *MsgCodeToString(int32 code); BString MsgCodeToBString(int32 code);