/* * Copyright 2005-2017 Haiku, Inc. All rights reserved. * Distributed under the terms of the MIT License. * * Authors: * Michael Lotz, mmlr@mlotz.ch */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "tracing_config.h" // kernel tracing configuration //#define VERBOSE_DEBUG_OUTPUT #ifdef VERBOSE_DEBUG_OUTPUT #define DEBUG_FUNCTION_ENTER \ debug_printf("msg thread: %ld; this: %p; header: %p; fields: %p;" \ " data: %p; what: 0x%08lx '%.4s'; line: %d; func: %s\n", \ find_thread(NULL), this, fHeader, fFields, fData, what, (char*)&what, \ __LINE__, __PRETTY_FUNCTION__); #define DEBUG_FUNCTION_ENTER2 \ debug_printf("msg thread: %ld; line: %d: func: %s\n", find_thread(NULL), \ __LINE__, __PRETTY_FUNCTION__); #else #define DEBUG_FUNCTION_ENTER /* nothing */ #define DEBUG_FUNCTION_ENTER2 /* nothing */ #endif #if BMESSAGE_TRACING # define KTRACE(format...) ktrace_printf(format) #else # define KTRACE(format...) ; #endif 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); extern "C" { // private os function to set the owning team of an area status_t _kern_transfer_area(area_id area, void** _address, uint32 addressSpec, team_id target); } BBlockCache* BMessage::sMsgCache = NULL; port_id BMessage::sReplyPorts[sNumReplyPorts]; int32 BMessage::sReplyPortInUse[sNumReplyPorts]; template static void print_to_stream_type(uint8* pointer) { Type* item = (Type*)pointer; item->PrintToStream(); } template static void print_type(const char* format, uint8* pointer) { Type* item = (Type*)pointer; printf(format,* item,* item); } template static void print_type3(const char* format, uint8* pointer) { Type* item = (Type*)pointer; printf(format, *item, *item, *item); } static status_t handle_reply(port_id replyPort, int32* _code, bigtime_t timeout, BMessage* reply) { DEBUG_FUNCTION_ENTER2; ssize_t size; do { size = port_buffer_size_etc(replyPort, B_RELATIVE_TIMEOUT, timeout); } while (size == B_INTERRUPTED); if (size < 0) return size; status_t result; char* buffer = (char*)malloc(size); if (buffer == NULL) return B_NO_MEMORY; do { result = read_port(replyPort, _code, buffer, size); } while (result == B_INTERRUPTED); if (result < 0 || *_code != kPortMessageCode) { free(buffer); return result < 0 ? result : B_ERROR; } result = reply->Unflatten(buffer); free(buffer); return result; } // #pragma mark - BMessage::BMessage() { DEBUG_FUNCTION_ENTER; _InitCommon(true); } BMessage::BMessage(BMessage* other) { DEBUG_FUNCTION_ENTER; _InitCommon(false); *this = *other; } BMessage::BMessage(uint32 _what) { DEBUG_FUNCTION_ENTER; if (_InitCommon(true)) fHeader->what = _what; what = _what; } BMessage::BMessage(const BMessage& other) { DEBUG_FUNCTION_ENTER; _InitCommon(false); *this = other; } BMessage::~BMessage() { DEBUG_FUNCTION_ENTER; _Clear(); } BMessage& BMessage::operator=(const BMessage& other) { DEBUG_FUNCTION_ENTER; if (this == &other) return *this; _Clear(); fHeader = (message_header*)malloc(sizeof(message_header)); if (fHeader == NULL) return *this; if (other.fHeader == NULL) return *this; memcpy(fHeader, other.fHeader, sizeof(message_header)); // Clear some header flags inherited from the original message that don't // apply to the clone. fHeader->flags &= ~(MESSAGE_FLAG_REPLY_REQUIRED | MESSAGE_FLAG_REPLY_DONE | MESSAGE_FLAG_IS_REPLY | MESSAGE_FLAG_WAS_DELIVERED | MESSAGE_FLAG_PASS_BY_AREA); // Note, that BeOS R5 seems to keep the reply info. if (fHeader->field_count > 0) { size_t fieldsSize = fHeader->field_count * sizeof(field_header); if (other.fFields != NULL) fFields = (field_header*)malloc(fieldsSize); if (fFields == NULL) { fHeader->field_count = 0; fHeader->data_size = 0; } else if (other.fFields != NULL) memcpy(fFields, other.fFields, fieldsSize); } if (fHeader->data_size > 0) { if (other.fData != NULL) fData = (uint8*)malloc(fHeader->data_size); if (fData == NULL) { fHeader->field_count = 0; free(fFields); fFields = NULL; } else if (other.fData != NULL) memcpy(fData, other.fData, fHeader->data_size); } fHeader->what = what = other.what; fHeader->message_area = -1; fFieldsAvailable = 0; fDataAvailable = 0; return *this; } void* BMessage::operator new(size_t size) { DEBUG_FUNCTION_ENTER2; return sMsgCache->Get(size); } void* BMessage::operator new(size_t size, const std::nothrow_t& noThrow) { DEBUG_FUNCTION_ENTER2; return sMsgCache->Get(size); } void* BMessage::operator new(size_t, void* pointer) { DEBUG_FUNCTION_ENTER2; return pointer; } void BMessage::operator delete(void* pointer, size_t size) { DEBUG_FUNCTION_ENTER2; if (pointer == NULL) return; sMsgCache->Save(pointer, size); } bool BMessage::HasSameData(const BMessage& other, bool ignoreFieldOrder, bool deep) const { if (this == &other) return true; if (fHeader == NULL) return other.fHeader == NULL; if (fHeader->field_count != other.fHeader->field_count) return false; for (uint32 i = 0; i < fHeader->field_count; i++) { field_header* field = &fFields[i]; field_header* otherField = NULL; const char* name = (const char*)fData + field->offset; if (ignoreFieldOrder) { if (other._FindField(name, B_ANY_TYPE, &otherField) != B_OK) return false; } else { otherField = &other.fFields[i]; if (otherField->name_length != field->name_length) return false; const char* otherName = (const char*)other.fData + otherField->offset; if (strncmp(name, otherName, field->name_length) != 0) return false; } if (otherField->type != field->type || otherField->count != field->count) { return false; } uint8* data = fData + field->offset + field->name_length; uint8* otherData = other.fData + otherField->offset + otherField->name_length; bool needsMemCompare = true; if (deep && field->type == B_MESSAGE_TYPE) { BMessage message, otherMessage; if (message.Unflatten((const char*)data) == B_OK && otherMessage.Unflatten((const char*)otherData) == B_OK) { if (!message.HasSameData(ignoreFieldOrder, deep)) return false; needsMemCompare = false; } } if (needsMemCompare) { if (otherField->data_size != field->data_size) return false; if (memcmp(data, otherData, field->data_size) != 0) return false; } } return true; } status_t BMessage::_InitCommon(bool initHeader) { DEBUG_FUNCTION_ENTER; what = 0; fHeader = NULL; fFields = NULL; fData = NULL; fFieldsAvailable = 0; fDataAvailable = 0; fOriginal = NULL; fQueueLink = NULL; fArchivingPointer = NULL; if (initHeader) return _InitHeader(); return B_OK; } status_t BMessage::_InitHeader() { DEBUG_FUNCTION_ENTER; if (fHeader == NULL) { fHeader = (message_header*)malloc(sizeof(message_header)); if (fHeader == NULL) return B_NO_MEMORY; } memset(fHeader, 0, sizeof(message_header) - sizeof(fHeader->hash_table)); fHeader->format = MESSAGE_FORMAT_HAIKU; fHeader->flags = MESSAGE_FLAG_VALID; fHeader->what = what; fHeader->current_specifier = -1; fHeader->message_area = -1; fHeader->target = B_NULL_TOKEN; fHeader->reply_target = B_NULL_TOKEN; fHeader->reply_port = -1; fHeader->reply_team = -1; // initializing the hash table to -1 because 0 is a valid index fHeader->hash_table_size = MESSAGE_BODY_HASH_TABLE_SIZE; memset(&fHeader->hash_table, 255, sizeof(fHeader->hash_table)); return B_OK; } status_t BMessage::_Clear() { DEBUG_FUNCTION_ENTER; if (fHeader != NULL) { // We're going to destroy all information of this message. If there's // still someone waiting for a reply to this message, we have to send // one now. if (IsSourceWaiting()) SendReply(B_NO_REPLY); if (fHeader->message_area >= 0) _Dereference(); free(fHeader); fHeader = NULL; } free(fFields); fFields = NULL; free(fData); fData = NULL; fArchivingPointer = NULL; fFieldsAvailable = 0; fDataAvailable = 0; delete fOriginal; fOriginal = NULL; return B_OK; } status_t BMessage::GetInfo(type_code typeRequested, int32 index, char** nameFound, type_code* typeFound, int32* countFound) const { DEBUG_FUNCTION_ENTER; if (fHeader == NULL) return B_NO_INIT; if (index < 0 || (uint32)index >= fHeader->field_count) return B_BAD_INDEX; if (typeRequested == B_ANY_TYPE) { if (nameFound != NULL) *nameFound = (char*)fData + fFields[index].offset; if (typeFound != NULL) *typeFound = fFields[index].type; if (countFound != NULL) *countFound = fFields[index].count; return B_OK; } int32 counter = -1; field_header* field = fFields; for (uint32 i = 0; i < fHeader->field_count; i++, field++) { if (field->type == typeRequested) counter++; if (counter == index) { if (nameFound != NULL) *nameFound = (char*)fData + field->offset; if (typeFound != NULL) *typeFound = field->type; if (countFound != NULL) *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 != NULL) *countFound = 0; field_header* field = NULL; status_t result = _FindField(name, B_ANY_TYPE, &field); if (result != B_OK) return result; if (typeFound != NULL) *typeFound = field->type; if (countFound != NULL) *countFound = field->count; return B_OK; } status_t BMessage::GetInfo(const char* name, type_code* typeFound, bool* fixedSize) const { DEBUG_FUNCTION_ENTER; field_header* field = NULL; status_t result = _FindField(name, B_ANY_TYPE, &field); if (result != B_OK) return result; if (typeFound != NULL) *typeFound = field->type; if (fixedSize != NULL) *fixedSize = (field->flags & FIELD_FLAG_FIXED_SIZE) != 0; return B_OK; } status_t BMessage::GetInfo(const char* name, type_code* typeFound, int32* countFound, bool* fixedSize) const { DEBUG_FUNCTION_ENTER; field_header* field = NULL; status_t result = _FindField(name, B_ANY_TYPE, &field); if (result != B_OK) return result; if (typeFound != NULL) *typeFound = field->type; if (countFound != NULL) *countFound = field->count; if (fixedSize != NULL) *fixedSize = (field->flags & FIELD_FLAG_FIXED_SIZE) != 0; return B_OK; } int32 BMessage::CountNames(type_code type) const { DEBUG_FUNCTION_ENTER; if (fHeader == NULL) return 0; if (type == B_ANY_TYPE) return fHeader->field_count; int32 count = 0; field_header* field = fFields; for (uint32 i = 0; i < fHeader->field_count; i++, field++) { if (field->type == type) count++; } return count; } bool BMessage::IsEmpty() const { DEBUG_FUNCTION_ENTER; return fHeader == NULL || fHeader->field_count == 0; } bool BMessage::IsSystem() const { DEBUG_FUNCTION_ENTER; char a = char(what >> 24); char b = char(what >> 16); char c = char(what >> 8); char d = char(what); // The BeBook says: // ... we've adopted a strict convention for assigning values to all // Be-defined constants. The value assigned will always be formed by // combining four characters into a multicharacter constant, with the // characters limited to uppercase letters and the underbar // Between that and what's in AppDefs.h, this algo seems like a safe bet: if (a == '_' && isupper(b) && isupper(c) && isupper(d)) return true; return false; } bool BMessage::IsReply() const { DEBUG_FUNCTION_ENTER; return fHeader != NULL && (fHeader->flags & MESSAGE_FLAG_IS_REPLY) != 0; } void BMessage::PrintToStream() const { _PrintToStream(""); printf("}\n"); } void BMessage::_PrintToStream(const char* indent) const { DEBUG_FUNCTION_ENTER; int32 value = B_BENDIAN_TO_HOST_INT32(what); printf("BMessage("); if (isprint(*(char*)&value)) printf("'%.4s'", (char*)&value); else printf("0x%" B_PRIx32, what); printf(") {\n"); if (fHeader == NULL || fFields == NULL || fData == NULL) return; field_header* field = fFields; for (uint32 i = 0; i < fHeader->field_count; i++, field++) { value = B_BENDIAN_TO_HOST_INT32(field->type); ssize_t size = 0; if ((field->flags & FIELD_FLAG_FIXED_SIZE) != 0 && field->count > 0) size = field->data_size / field->count; uint8* pointer = fData + field->offset + field->name_length; for (uint32 j = 0; j < field->count; j++) { if (field->count == 1) { printf("%s %s = ", indent, (char*)(fData + field->offset)); } else { printf("%s %s[%" B_PRIu32 "] = ", indent, (char*)(fData + field->offset), j); } if ((field->flags & FIELD_FLAG_FIXED_SIZE) == 0) { size = *(uint32*)pointer; pointer += sizeof(uint32); } switch (field->type) { case B_RECT_TYPE: print_to_stream_type(pointer); break; case B_POINT_TYPE: print_to_stream_type(pointer); break; case B_STRING_TYPE: printf("string(\"%.*s\", %ld bytes)\n", (int)size, (char*)pointer, (long)size); break; case B_INT8_TYPE: print_type3("int8(0x%hx or %d or '%c')\n", pointer); break; case B_UINT8_TYPE: print_type3("uint8(0x%hx or %u or '%c')\n", pointer); break; case B_INT16_TYPE: print_type("int16(0x%x or %d)\n", pointer); break; case B_UINT16_TYPE: print_type("uint16(0x%x or %u)\n", pointer); break; case B_INT32_TYPE: print_type("int32(0x%lx or %ld)\n", pointer); break; case B_UINT32_TYPE: print_type("uint32(0x%lx or %lu)\n", pointer); break; case B_INT64_TYPE: print_type("int64(0x%Lx or %lld)\n", pointer); break; case B_UINT64_TYPE: print_type("uint64(0x%Lx or %lld)\n", pointer); break; case B_BOOL_TYPE: printf("bool(%s)\n", *((bool*)pointer) != 0 ? "true" : "false"); break; case B_FLOAT_TYPE: print_type("float(%.4f)\n", pointer); break; case B_DOUBLE_TYPE: print_type("double(%.8f)\n", pointer); break; case B_REF_TYPE: { entry_ref ref; BPrivate::entry_ref_unflatten(&ref, (char*)pointer, size); printf("entry_ref(device=%d, directory=%" B_PRIdINO ", name=\"%s\", ", (int)ref.device, ref.directory, ref.name); BPath path(&ref); printf("path=\"%s\")\n", path.Path()); break; } case B_NODE_REF_TYPE: { node_ref ref; BPrivate::node_ref_unflatten(&ref, (char*)pointer, size); printf("node_ref(device=%d, node=%" B_PRIdINO ", ", (int)ref.device, ref.node); break; } case B_MESSAGE_TYPE: { char buffer[1024]; snprintf(buffer, sizeof(buffer), "%s ", indent); BMessage message; status_t result = message.Unflatten((const char*)pointer); if (result != B_OK) { printf("failed unflatten: %s\n", strerror(result)); break; } message._PrintToStream(buffer); printf("%s }\n", indent); break; } case B_RGB_32_BIT_TYPE: { rgb_color* color = (rgb_color*)pointer; printf("rgb_color(%u, %u, %u, %u)\n", color->red, color->green, color->blue, color->alpha); break; } default: { printf("(type = '%.4s')(size = %ld)\n", (char*)&value, (long)size); break; } } pointer += size; } } } status_t BMessage::Rename(const char* oldEntry, const char* newEntry) { DEBUG_FUNCTION_ENTER; if (oldEntry == NULL || newEntry == NULL) return B_BAD_VALUE; if (fHeader == NULL) return B_NO_INIT; status_t result; if (fHeader->message_area >= 0) { result = _CopyForWrite(); if (result != B_OK) return result; } uint32 hash = _HashName(oldEntry) % fHeader->hash_table_size; int32* nextField = &fHeader->hash_table[hash]; while (*nextField >= 0) { field_header* field = &fFields[*nextField]; if (strncmp((const char*)(fData + field->offset), oldEntry, field->name_length) == 0) { // nextField points to the field for oldEntry, save it and unlink int32 index = *nextField; *nextField = field->next_field; field->next_field = -1; hash = _HashName(newEntry) % fHeader->hash_table_size; nextField = &fHeader->hash_table[hash]; while (*nextField >= 0) nextField = &fFields[*nextField].next_field; *nextField = index; int32 newLength = strlen(newEntry) + 1; result = _ResizeData(field->offset + 1, newLength - field->name_length); if (result != B_OK) return result; memcpy(fData + field->offset, newEntry, newLength); field->name_length = newLength; return B_OK; } nextField = &field->next_field; } return B_NAME_NOT_FOUND; } bool BMessage::WasDelivered() const { DEBUG_FUNCTION_ENTER; return fHeader != NULL && (fHeader->flags & MESSAGE_FLAG_WAS_DELIVERED) != 0; } bool BMessage::IsSourceWaiting() const { DEBUG_FUNCTION_ENTER; return fHeader != NULL && (fHeader->flags & MESSAGE_FLAG_REPLY_REQUIRED) != 0 && (fHeader->flags & MESSAGE_FLAG_REPLY_DONE) == 0; } bool BMessage::IsSourceRemote() const { DEBUG_FUNCTION_ENTER; return fHeader != NULL && (fHeader->flags & MESSAGE_FLAG_WAS_DELIVERED) != 0 && fHeader->reply_team != BPrivate::current_team(); } BMessenger BMessage::ReturnAddress() const { DEBUG_FUNCTION_ENTER; if (fHeader == NULL || (fHeader->flags & MESSAGE_FLAG_WAS_DELIVERED) == 0) return BMessenger(); BMessenger messenger; BMessenger::Private(messenger).SetTo(fHeader->reply_team, fHeader->reply_port, fHeader->reply_target); return messenger; } const BMessage* BMessage::Previous() const { DEBUG_FUNCTION_ENTER; /* ToDo: test if the "_previous_" field is used in R5 */ if (fOriginal == NULL) { fOriginal = new BMessage(); if (FindMessage("_previous_", fOriginal) != B_OK) { delete fOriginal; fOriginal = NULL; } } return fOriginal; } bool BMessage::WasDropped() const { DEBUG_FUNCTION_ENTER; return fHeader != NULL && (fHeader->flags & MESSAGE_FLAG_WAS_DROPPED) != 0; } BPoint BMessage::DropPoint(BPoint* offset) const { DEBUG_FUNCTION_ENTER; if (offset != NULL) *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; if (fHeader == NULL) return B_NO_INIT; BMessenger messenger; BMessenger::Private messengerPrivate(messenger); messengerPrivate.SetTo(fHeader->reply_team, fHeader->reply_port, fHeader->reply_target); if ((fHeader->flags & MESSAGE_FLAG_REPLY_AS_KMESSAGE) != 0) reply->fHeader->flags |= MESSAGE_FLAG_REPLY_AS_KMESSAGE; if ((fHeader->flags & MESSAGE_FLAG_REPLY_REQUIRED) != 0) { if ((fHeader->flags & MESSAGE_FLAG_REPLY_DONE) != 0) 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 && 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) == 0) 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; if (fHeader == NULL) return B_NO_INIT; BMessenger messenger; BMessenger::Private messengerPrivate(messenger); messengerPrivate.SetTo(fHeader->reply_team, fHeader->reply_port, fHeader->reply_target); if ((fHeader->flags & MESSAGE_FLAG_REPLY_REQUIRED) != 0) { if ((fHeader->flags & MESSAGE_FLAG_REPLY_DONE) != 0) 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) == 0) return B_BAD_REPLY; reply->AddMessage("_previous_", this); reply->fHeader->flags |= MESSAGE_FLAG_IS_REPLY | (fHeader->flags & MESSAGE_FLAG_REPLY_AS_KMESSAGE); 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; if (fHeader == NULL) return B_NO_INIT; return sizeof(message_header) + fHeader->field_count * sizeof(field_header) + fHeader->data_size; } status_t BMessage::Flatten(char* buffer, ssize_t size) const { DEBUG_FUNCTION_ENTER; if (buffer == NULL || size < 0) return B_BAD_VALUE; if (fHeader == NULL) return B_NO_INIT; if (size < FlattenedSize()) return B_BUFFER_OVERFLOW; /* we have to sync the what code as it is a public member */ fHeader->what = what; memcpy(buffer, fHeader, sizeof(message_header)); buffer += sizeof(message_header); size_t fieldsSize = fHeader->field_count * sizeof(field_header); memcpy(buffer, fFields, fieldsSize); buffer += fieldsSize; memcpy(buffer, fData, fHeader->data_size); return B_OK; } status_t BMessage::Flatten(BDataIO* stream, ssize_t* size) const { DEBUG_FUNCTION_ENTER; if (stream == NULL) return B_BAD_VALUE; if (fHeader == NULL) 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(message_header)); if (result1 != sizeof(message_header)) return result1 < 0 ? result1 : B_ERROR; ssize_t result2 = 0; if (fHeader->field_count > 0) { ssize_t fieldsSize = fHeader->field_count * sizeof(field_header); result2 = stream->Write(fFields, fieldsSize); if (result2 != fieldsSize) return result2 < 0 ? result2 : B_ERROR; } ssize_t result3 = 0; if (fHeader->data_size > 0) { result3 = stream->Write(fData, fHeader->data_size); if (result3 != (ssize_t)fHeader->data_size) return result3 < 0 ? result3 : B_ERROR; } if (size) *size = result1 + result2 + result3; return B_OK; } /* The concept of message sending by area: The traditional way of sending a message is to send it by flattening it to a buffer, pushing it through a port, reading it into the outputbuffer and unflattening it from there (copying the data again). While this works ok for small messages it does not make any sense for larger ones and may even hit some port capacity limit. Often in the life of a BMessage, it will be sent to someone. Almost as often the one receiving the message will not need to change the message in any way, but uses it "read only" to get information from it. This means that all that copying is pretty pointless in the first place since we could simply pass the original buffers on. It's obviously not exactly as simple as this, since we cannot just use the memory of one application in another - but we can share areas with eachother. Therefore instead of flattening into a buffer, we copy the message data into an area, put this information into the message header and only push this through the port. The receiving looper then builds a BMessage from the header, that only references the data in the area (not copying it), allowing read only access to it. Only if write access is necessary the message will be copyed from the area to its own buffers (like in the unflatten step before). The double copying is reduced to a single copy in most cases and we safe the slower route of moving the data through a port. Additionally we save us the reference counting with the use of areas that are reference counted internally. So we don't have to worry about leaving an area behind or deleting one that is still in use. */ status_t BMessage::_FlattenToArea(message_header** _header) const { DEBUG_FUNCTION_ENTER; if (fHeader == NULL) return B_NO_INIT; message_header* header = (message_header*)malloc(sizeof(message_header)); if (header == NULL) return B_NO_MEMORY; memcpy(header, fHeader, sizeof(message_header)); header->what = what; header->message_area = -1; *_header = header; if (header->field_count == 0 && header->data_size == 0) return B_OK; char* address = NULL; size_t fieldsSize = header->field_count * sizeof(field_header); size_t size = fieldsSize + header->data_size; size = (size + B_PAGE_SIZE) & ~(B_PAGE_SIZE - 1); area_id area = create_area("BMessage data", (void**)&address, B_ANY_ADDRESS, size, B_NO_LOCK, B_READ_AREA | B_WRITE_AREA); if (area < 0) { free(header); *_header = NULL; return area; } memcpy(address, fFields, fieldsSize); memcpy(address + fieldsSize, fData, fHeader->data_size); header->flags |= MESSAGE_FLAG_PASS_BY_AREA; header->message_area = area; return B_OK; } status_t BMessage::_Reference() { DEBUG_FUNCTION_ENTER; if (fHeader == NULL) return B_NO_INIT; fHeader->flags &= ~MESSAGE_FLAG_PASS_BY_AREA; /* if there is no data at all we don't need the area */ if (fHeader->field_count == 0 && fHeader->data_size == 0) return B_OK; area_info areaInfo; status_t result = get_area_info(fHeader->message_area, &areaInfo); if (result != B_OK) return result; if (areaInfo.team != BPrivate::current_team()) return B_BAD_VALUE; set_area_protection(fHeader->message_area, B_READ_AREA); uint8* address = (uint8*)areaInfo.address; fFields = (field_header*)address; fData = address + fHeader->field_count * sizeof(field_header); return B_OK; } status_t BMessage::_Dereference() { DEBUG_FUNCTION_ENTER; if (fHeader == NULL) return B_NO_INIT; delete_area(fHeader->message_area); fHeader->message_area = -1; fFields = NULL; fData = NULL; return B_OK; } status_t BMessage::_CopyForWrite() { DEBUG_FUNCTION_ENTER; if (fHeader == NULL) return B_NO_INIT; field_header* newFields = NULL; uint8* newData = NULL; if (fHeader->field_count > 0) { size_t fieldsSize = fHeader->field_count * sizeof(field_header); newFields = (field_header*)malloc(fieldsSize); if (newFields == NULL) return B_NO_MEMORY; memcpy(newFields, fFields, fieldsSize); } if (fHeader->data_size > 0) { newData = (uint8*)malloc(fHeader->data_size); if (newData == NULL) { free(newFields); return B_NO_MEMORY; } memcpy(newData, fData, fHeader->data_size); } _Dereference(); fFieldsAvailable = 0; fDataAvailable = 0; fFields = newFields; fData = newData; return B_OK; } status_t BMessage::_ValidateMessage() { DEBUG_FUNCTION_ENTER; if (fHeader == NULL) return B_NO_INIT; if (fHeader->field_count == 0) return B_OK; if (fFields == NULL) return B_NO_INIT; for (uint32 i = 0; i < fHeader->field_count; i++) { field_header* field = &fFields[i]; if ((field->next_field >= 0 && (uint32)field->next_field > fHeader->field_count) || (field->offset + field->name_length + field->data_size > fHeader->data_size)) { // the message is corrupt MakeEmpty(); return B_BAD_VALUE; } } return B_OK; } status_t BMessage::Unflatten(const char* flatBuffer) { DEBUG_FUNCTION_ENTER; if (flatBuffer == NULL) return B_BAD_VALUE; uint32 format = *(uint32*)flatBuffer; if (format != MESSAGE_FORMAT_HAIKU) return BPrivate::MessageAdapter::Unflatten(format, this, flatBuffer); BMemoryIO io(flatBuffer, SSIZE_MAX); return Unflatten(&io); } status_t BMessage::Unflatten(BDataIO* stream) { DEBUG_FUNCTION_ENTER; if (stream == NULL) return B_BAD_VALUE; uint32 format = 0; stream->Read(&format, sizeof(uint32)); if (format != MESSAGE_FORMAT_HAIKU) return BPrivate::MessageAdapter::Unflatten(format, this, stream); // native message unflattening _Clear(); fHeader = (message_header*)malloc(sizeof(message_header)); if (fHeader == NULL) return B_NO_MEMORY; fHeader->format = format; uint8* header = (uint8*)fHeader; ssize_t result = stream->Read(header + sizeof(uint32), sizeof(message_header) - sizeof(uint32)); if (result != sizeof(message_header) - sizeof(uint32) || (fHeader->flags & MESSAGE_FLAG_VALID) == 0) { _InitHeader(); return result < 0 ? result : B_BAD_VALUE; } what = fHeader->what; if ((fHeader->flags & MESSAGE_FLAG_PASS_BY_AREA) != 0 && fHeader->message_area >= 0) { status_t result = _Reference(); if (result != B_OK) { _InitHeader(); return result; } } else { fHeader->message_area = -1; if (fHeader->field_count > 0) { ssize_t fieldsSize = fHeader->field_count * sizeof(field_header); fFields = (field_header*)malloc(fieldsSize); if (fFields == NULL) { _InitHeader(); return B_NO_MEMORY; } result = stream->Read(fFields, fieldsSize); if (result != fieldsSize) return result < 0 ? result : B_BAD_VALUE; } if (fHeader->data_size > 0) { fData = (uint8*)malloc(fHeader->data_size); if (fData == NULL) { free(fFields); fFields = NULL; _InitHeader(); return B_NO_MEMORY; } result = stream->Read(fData, fHeader->data_size); if (result != (ssize_t)fHeader->data_size) return result < 0 ? result : B_BAD_VALUE; } } return _ValidateMessage(); } status_t BMessage::AddSpecifier(const char* property) { DEBUG_FUNCTION_ENTER; BMessage message(B_DIRECT_SPECIFIER); status_t result = message.AddString(B_PROPERTY_ENTRY, property); if (result != B_OK) return result; return AddSpecifier(&message); } status_t BMessage::AddSpecifier(const char* property, int32 index) { DEBUG_FUNCTION_ENTER; BMessage message(B_INDEX_SPECIFIER); status_t result = message.AddString(B_PROPERTY_ENTRY, property); if (result != B_OK) return result; result = message.AddInt32("index", index); if (result != B_OK) return result; return AddSpecifier(&message); } status_t BMessage::AddSpecifier(const char* property, int32 index, int32 range) { DEBUG_FUNCTION_ENTER; if (range < 0) return B_BAD_VALUE; BMessage message(B_RANGE_SPECIFIER); status_t result = message.AddString(B_PROPERTY_ENTRY, property); if (result != B_OK) return result; result = message.AddInt32("index", index); if (result != B_OK) return result; result = message.AddInt32("range", range); if (result != B_OK) return result; return AddSpecifier(&message); } status_t BMessage::AddSpecifier(const char* property, const char* name) { DEBUG_FUNCTION_ENTER; BMessage message(B_NAME_SPECIFIER); status_t result = message.AddString(B_PROPERTY_ENTRY, property); if (result != B_OK) return result; result = message.AddString(B_PROPERTY_NAME_ENTRY, name); if (result != B_OK) return result; return AddSpecifier(&message); } status_t BMessage::AddSpecifier(const BMessage* specifier) { DEBUG_FUNCTION_ENTER; status_t result = AddMessage(B_SPECIFIER_ENTRY, specifier); if (result != B_OK) return result; fHeader->current_specifier++; fHeader->flags |= MESSAGE_FLAG_HAS_SPECIFIERS; return B_OK; } status_t BMessage::SetCurrentSpecifier(int32 index) { DEBUG_FUNCTION_ENTER; if (index < 0) return B_BAD_INDEX; type_code type; int32 count; status_t result = GetInfo(B_SPECIFIER_ENTRY, &type, &count); if (result != B_OK) return result; if (index >= count) return B_BAD_INDEX; fHeader->current_specifier = index; return B_OK; } status_t BMessage::GetCurrentSpecifier(int32* index, BMessage* specifier, int32* _what, const char** property) const { DEBUG_FUNCTION_ENTER; if (fHeader == NULL) return B_NO_INIT; if (index != NULL) *index = fHeader->current_specifier; if (fHeader->current_specifier < 0 || (fHeader->flags & MESSAGE_FLAG_WAS_DELIVERED) == 0) return B_BAD_SCRIPT_SYNTAX; if (specifier) { if (FindMessage(B_SPECIFIER_ENTRY, fHeader->current_specifier, specifier) != B_OK) return B_BAD_SCRIPT_SYNTAX; if (_what != NULL) *_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 != NULL && (fHeader->flags & MESSAGE_FLAG_HAS_SPECIFIERS) != 0; } status_t BMessage::PopSpecifier() { DEBUG_FUNCTION_ENTER; if (fHeader == NULL) return B_NO_INIT; if (fHeader->current_specifier < 0 || (fHeader->flags & MESSAGE_FLAG_WAS_DELIVERED) == 0) return B_BAD_VALUE; if (fHeader->current_specifier >= 0) fHeader->current_specifier--; return B_OK; } void BMessage::_UpdateOffsets(uint32 offset, int32 change) { // Update the header to match the new position of the fields if (offset < fHeader->data_size) { field_header* field = fFields; for (uint32 i = 0; i < fHeader->field_count; i++, field++) { if (field->offset >= offset) field->offset += change; } } } status_t BMessage::_ResizeData(uint32 offset, int32 change) { if (change == 0) return B_OK; /* optimize for the most usual case: appending data */ if (change > 0) { // We need to make the field bigger // check if there is enough free space allocated if (fDataAvailable >= (uint32)change) { // In this case, we just need to move the data after the growing // field to get the space at the right place if (offset < fHeader->data_size) { memmove(fData + offset + change, fData + offset, fHeader->data_size - offset); } _UpdateOffsets(offset, change); fDataAvailable -= change; fHeader->data_size += change; return B_OK; } // We need to grow the buffer. We try to optimize reallocations by // preallocating space for more fields. size_t size = fHeader->data_size * 2; size = min_c(size, fHeader->data_size + MAX_DATA_PREALLOCATION); size = max_c(size, fHeader->data_size + change); uint8* newData = (uint8*)realloc(fData, size); if (size > 0 && newData == NULL) return B_NO_MEMORY; fData = newData; if (offset < fHeader->data_size) { memmove(fData + offset + change, fData + offset, fHeader->data_size - offset); } fHeader->data_size += change; fDataAvailable = size - fHeader->data_size; } else { ssize_t length = fHeader->data_size - offset + change; if (length > 0) memmove(fData + offset, fData + offset - change, length); // change is negative fHeader->data_size += change; fDataAvailable -= change; if (fDataAvailable > MAX_DATA_PREALLOCATION) { ssize_t available = MAX_DATA_PREALLOCATION / 2; ssize_t size = fHeader->data_size + available; uint8* newData = (uint8*)realloc(fData, size); if (size > 0 && newData == NULL) { // this is strange, but not really fatal _UpdateOffsets(offset, change); return B_OK; } fData = newData; fDataAvailable = available; } } _UpdateOffsets(offset, change); return B_OK; } uint32 BMessage::_HashName(const char* name) const { char ch; uint32 result = 0; while ((ch = *name++) != 0) { result = (result << 7) ^ (result >> 24); result ^= ch; } result ^= result << 12; return result; } status_t BMessage::_FindField(const char* name, type_code type, field_header** result) const { if (name == NULL) return B_BAD_VALUE; if (fHeader == NULL) return B_NO_INIT; if (fHeader->field_count == 0 || fFields == NULL || fData == NULL) return B_NAME_NOT_FOUND; uint32 hash = _HashName(name) % fHeader->hash_table_size; int32 nextField = fHeader->hash_table[hash]; while (nextField >= 0) { field_header* field = &fFields[nextField]; if ((field->flags & FIELD_FLAG_VALID) == 0) break; if (strncmp((const char*)(fData + field->offset), name, field->name_length) == 0) { if (type != B_ANY_TYPE && field->type != type) return B_BAD_TYPE; *result = field; return B_OK; } nextField = field->next_field; } return B_NAME_NOT_FOUND; } status_t BMessage::_AddField(const char* name, type_code type, bool isFixedSize, field_header** result) { if (fHeader == NULL) return B_NO_INIT; if (fFieldsAvailable <= 0) { uint32 count = fHeader->field_count * 2 + 1; count = min_c(count, fHeader->field_count + MAX_FIELD_PREALLOCATION); field_header* newFields = (field_header*)realloc(fFields, count * sizeof(field_header)); if (count > 0 && newFields == NULL) return B_NO_MEMORY; fFields = newFields; fFieldsAvailable = count - fHeader->field_count; } uint32 hash = _HashName(name) % fHeader->hash_table_size; int32* nextField = &fHeader->hash_table[hash]; while (*nextField >= 0) nextField = &fFields[*nextField].next_field; *nextField = fHeader->field_count; field_header* field = &fFields[fHeader->field_count]; field->type = type; field->count = 0; field->data_size = 0; field->next_field = -1; field->offset = fHeader->data_size; field->name_length = strlen(name) + 1; status_t status = _ResizeData(field->offset, field->name_length); if (status != B_OK) return status; memcpy(fData + field->offset, name, field->name_length); field->flags = FIELD_FLAG_VALID; if (isFixedSize) field->flags |= FIELD_FLAG_FIXED_SIZE; fFieldsAvailable--; fHeader->field_count++; *result = field; return B_OK; } status_t BMessage::_RemoveField(field_header* field) { status_t result = _ResizeData(field->offset, -(field->data_size + field->name_length)); if (result != B_OK) return result; int32 index = ((uint8*)field - (uint8*)fFields) / sizeof(field_header); int32 nextField = field->next_field; if (nextField > index) nextField--; int32* value = fHeader->hash_table; for (uint32 i = 0; i < fHeader->hash_table_size; i++, value++) { if (*value > index) *value -= 1; else if (*value == index) *value = nextField; } field_header* other = fFields; for (uint32 i = 0; i < fHeader->field_count; i++, other++) { if (other->next_field > index) other->next_field--; else if (other->next_field == index) other->next_field = nextField; } size_t size = (fHeader->field_count - index - 1) * sizeof(field_header); memmove(fFields + index, fFields + index + 1, size); fHeader->field_count--; fFieldsAvailable++; if (fFieldsAvailable > MAX_FIELD_PREALLOCATION) { ssize_t available = MAX_FIELD_PREALLOCATION / 2; size = (fHeader->field_count + available) * sizeof(field_header); field_header* newFields = (field_header*)realloc(fFields, size); if (size > 0 && newFields == NULL) { // this is strange, but not really fatal return B_OK; } fFields = newFields; fFieldsAvailable = available; } return B_OK; } status_t BMessage::AddData(const char* name, type_code type, const void* data, ssize_t numBytes, bool isFixedSize, int32 count) { // Note that the "count" argument is only a hint at how many items // the caller expects to add to this field. Since we do no item pre- // allocation, we ignore this argument. DEBUG_FUNCTION_ENTER; if (numBytes <= 0 || data == NULL) return B_BAD_VALUE; if (fHeader == NULL) return B_NO_INIT; status_t result; if (fHeader->message_area >= 0) { result = _CopyForWrite(); if (result != B_OK) return result; } field_header* field = NULL; result = _FindField(name, type, &field); if (result == B_NAME_NOT_FOUND) result = _AddField(name, type, isFixedSize, &field); if (result != B_OK) return result; if (field == NULL) return B_ERROR; uint32 offset = field->offset + field->name_length + field->data_size; if ((field->flags & FIELD_FLAG_FIXED_SIZE) != 0) { if (field->count) { ssize_t size = field->data_size / field->count; if (size != numBytes) return B_BAD_VALUE; } result = _ResizeData(offset, numBytes); if (result != B_OK) { if (field->count == 0) _RemoveField(field); return result; } memcpy(fData + offset, data, numBytes); field->data_size += numBytes; } else { int32 change = numBytes + sizeof(uint32); result = _ResizeData(offset, change); if (result != B_OK) { if (field->count == 0) _RemoveField(field); return result; } uint32 size = (uint32)numBytes; memcpy(fData + offset, &size, sizeof(uint32)); memcpy(fData + offset + sizeof(uint32), data, size); field->data_size += change; } field->count++; return B_OK; } status_t BMessage::RemoveData(const char* name, int32 index) { DEBUG_FUNCTION_ENTER; if (index < 0) return B_BAD_INDEX; if (fHeader == NULL) return B_NO_INIT; status_t result; if (fHeader->message_area >= 0) { result = _CopyForWrite(); if (result != B_OK) return result; } field_header* field = NULL; result = _FindField(name, B_ANY_TYPE, &field); if (result != B_OK) return result; if ((uint32)index >= field->count) return B_BAD_INDEX; if (field->count == 1) return _RemoveField(field); uint32 offset = field->offset + field->name_length; if ((field->flags & FIELD_FLAG_FIXED_SIZE) != 0) { ssize_t size = field->data_size / field->count; result = _ResizeData(offset + index * size, -size); if (result != B_OK) return result; field->data_size -= size; } else { uint8* pointer = fData + offset; for (int32 i = 0; i < index; i++) { offset += *(uint32*)pointer + sizeof(uint32); pointer = fData + offset; } size_t currentSize = *(uint32*)pointer + sizeof(uint32); result = _ResizeData(offset, -currentSize); if (result != B_OK) return result; field->data_size -= currentSize; } field->count--; return B_OK; } status_t BMessage::RemoveName(const char* name) { DEBUG_FUNCTION_ENTER; if (fHeader == NULL) return B_NO_INIT; status_t result; if (fHeader->message_area >= 0) { result = _CopyForWrite(); if (result != B_OK) return result; } field_header* field = NULL; result = _FindField(name, B_ANY_TYPE, &field); if (result != B_OK) return result; return _RemoveField(field); } status_t BMessage::MakeEmpty() { DEBUG_FUNCTION_ENTER; _Clear(); return _InitHeader(); } status_t BMessage::FindData(const char* name, type_code type, int32 index, const void** data, ssize_t* numBytes) const { DEBUG_FUNCTION_ENTER; if (data == NULL) return B_BAD_VALUE; *data = NULL; field_header* field = NULL; status_t result = _FindField(name, type, &field); if (result != B_OK) return result; if (index < 0 || (uint32)index >= field->count) return B_BAD_INDEX; if ((field->flags & FIELD_FLAG_FIXED_SIZE) != 0) { size_t bytes = field->data_size / field->count; *data = fData + field->offset + field->name_length + index * bytes; if (numBytes != NULL) *numBytes = bytes; } else { uint8* pointer = fData + field->offset + field->name_length; for (int32 i = 0; i < index; i++) pointer += *(uint32*)pointer + sizeof(uint32); *data = pointer + sizeof(uint32); if (numBytes != NULL) *numBytes = *(uint32*)pointer; } 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 == NULL) return B_BAD_VALUE; status_t result; if (fHeader->message_area >= 0) { result = _CopyForWrite(); if (result != B_OK) return result; } field_header* field = NULL; result = _FindField(name, type, &field); if (result != B_OK) return result; if (index < 0 || (uint32)index >= field->count) return B_BAD_INDEX; if ((field->flags & FIELD_FLAG_FIXED_SIZE) != 0) { ssize_t size = field->data_size / field->count; if (size != numBytes) return B_BAD_VALUE; memcpy(fData + field->offset + field->name_length + index * size, data, size); } else { uint32 offset = field->offset + field->name_length; uint8* pointer = fData + offset; for (int32 i = 0; i < index; i++) { offset += *(uint32*)pointer + sizeof(uint32); pointer = fData + offset; } size_t currentSize = *(uint32*)pointer; int32 change = numBytes - currentSize; result = _ResizeData(offset, change); if (result != B_OK) return result; uint32 newSize = (uint32)numBytes; memcpy(fData + offset, &newSize, sizeof(uint32)); memcpy(fData + offset + sizeof(uint32), data, newSize); field->data_size += change; } return B_OK; } bool BMessage::HasData(const char* name, type_code type, int32 index) const { DEBUG_FUNCTION_ENTER; field_header* field = NULL; status_t result = _FindField(name, type, &field); if (result != B_OK) return false; if (index < 0 || (uint32)index >= field->count) return false; return true; } /* Static functions for cache initialization and cleanup */ void BMessage::_StaticInit() { DEBUG_FUNCTION_ENTER2; sReplyPorts[0] = create_port(1, "tmp_rport0"); sReplyPorts[1] = create_port(1, "tmp_rport1"); sReplyPorts[2] = create_port(1, "tmp_rport2"); sReplyPortInUse[0] = 0; sReplyPortInUse[1] = 0; sReplyPortInUse[2] = 0; sMsgCache = new BBlockCache(20, sizeof(BMessage), B_OBJECT_CACHE); } void BMessage::_StaticReInitForkedChild() { DEBUG_FUNCTION_ENTER2; // overwrite the inherited ports with a set of our own 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; } void BMessage::_StaticCleanup() { DEBUG_FUNCTION_ENTER2; delete_port(sReplyPorts[0]); sReplyPorts[0] = -1; delete_port(sReplyPorts[1]); sReplyPorts[1] = -1; delete_port(sReplyPorts[2]); sReplyPorts[2] = -1; } void BMessage::_StaticCacheCleanup() { DEBUG_FUNCTION_ENTER2; delete sMsgCache; sMsgCache = NULL; } int32 BMessage::_StaticGetCachedReplyPort() { DEBUG_FUNCTION_ENTER2; int index = -1; for (int32 i = 0; i < sNumReplyPorts; i++) { int32 old = atomic_add(&(sReplyPortInUse[i]), 1); if (old == 0) { // This entry is free index = i; break; } else { // This entry is being used. atomic_add(&(sReplyPortInUse[i]), -1); } } return index; } status_t BMessage::_SendMessage(port_id port, team_id portOwner, int32 token, bigtime_t timeout, bool replyRequired, BMessenger& replyTo) const { DEBUG_FUNCTION_ENTER; ssize_t size = 0; char* buffer = NULL; message_header* header = NULL; status_t result = B_OK; BPrivate::BDirectMessageTarget* direct = NULL; BMessage* copy = NULL; if (portOwner == BPrivate::current_team()) BPrivate::gDefaultTokens.AcquireHandlerTarget(token, &direct); if (direct != NULL) { // We have a direct local message target - we can just enqueue the // message in its message queue. This will also prevent possible // deadlocks when the queue is full. copy = new BMessage(*this); if (copy != NULL) { header = copy->fHeader; header->flags = fHeader->flags; } else { direct->Release(); return B_NO_MEMORY; } #ifndef HAIKU_TARGET_PLATFORM_LIBBE_TEST } else if ((fHeader->flags & MESSAGE_FLAG_REPLY_AS_KMESSAGE) != 0) { KMessage toMessage; result = BPrivate::MessageAdapter::ConvertToKMessage(this, toMessage); if (result != B_OK) return result; return toMessage.SendTo(port, token); } else if (fHeader->data_size > B_PAGE_SIZE * 10) { // ToDo: bind the above size to the max port message size // use message passing by area for such a large message result = _FlattenToArea(&header); if (result != B_OK) return result; buffer = (char*)header; size = sizeof(message_header); if (header->message_area >= 0) { team_id target = portOwner; if (target < 0) { port_info info; result = get_port_info(port, &info); if (result != B_OK) { free(header); return result; } target = info.team; } void* address = NULL; area_id transfered = _kern_transfer_area(header->message_area, &address, B_ANY_ADDRESS, target); if (transfered < 0) { delete_area(header->message_area); free(header); return transfered; } header->message_area = transfered; } #endif } else { size = FlattenedSize(); buffer = (char*)malloc(size); if (buffer == NULL) return B_NO_MEMORY; result = Flatten(buffer, size); if (result != B_OK) { free(buffer); return result; } header = (message_header*)buffer; } if (!replyTo.IsValid()) { BMessenger::Private(replyTo).SetTo(fHeader->reply_team, fHeader->reply_port, fHeader->reply_target); if (!replyTo.IsValid()) replyTo = be_app_messenger; } BMessenger::Private replyToPrivate(replyTo); if (replyRequired) { header->flags |= MESSAGE_FLAG_REPLY_REQUIRED; header->flags &= ~MESSAGE_FLAG_REPLY_DONE; } header->target = token; header->reply_team = replyToPrivate.Team(); header->reply_port = replyToPrivate.Port(); header->reply_target = replyToPrivate.Token(); header->flags |= MESSAGE_FLAG_WAS_DELIVERED; if (direct == NULL) { KTRACE("BMessage send remote: team: %ld, port: %ld, token: %ld, " "message: '%c%c%c%c'", portOwner, port, token, char(what >> 24), char(what >> 16), char(what >> 8), (char)what); do { result = write_port_etc(port, kPortMessageCode, (void*)buffer, size, B_RELATIVE_TIMEOUT, timeout); } while (result == B_INTERRUPTED); } if (result == B_OK && IsSourceWaiting()) { // the forwarded message will handle the reply - we must not do // this anymore fHeader->flags |= MESSAGE_FLAG_REPLY_DONE; } // we need to do this last because it is possible our // message might be destroyed after it's enqueued in the // target looper. Thus we don't want to do any ops that depend on // members of this after the enqueue. if (direct != NULL) { KTRACE("BMessage send direct: port: %ld, token: %ld, " "message: '%c%c%c%c'", port, token, char(what >> 24), char(what >> 16), char(what >> 8), (char)what); // this is a local message transmission direct->AddMessage(copy); if (direct->Queue()->IsNextMessage(copy) && port_count(port) <= 0) { // there is currently no message waiting, and we need to wakeup the // looper write_port_etc(port, 0, NULL, 0, B_RELATIVE_TIMEOUT, 0); } direct->Release(); } free(buffer); return result; } // Sends a message and waits synchronously for a reply. status_t BMessage::_SendMessage(port_id port, team_id portOwner, int32 token, BMessage* reply, bigtime_t sendTimeout, bigtime_t replyTimeout) const { if (IsSourceWaiting()) { // we can't forward this message synchronously when it's already // waiting for a reply return B_ERROR; } DEBUG_FUNCTION_ENTER; const int32 cachedReplyPort = _StaticGetCachedReplyPort(); port_id replyPort = B_BAD_PORT_ID; status_t result = B_OK; if (cachedReplyPort < 0) { // All the cached reply ports are in use; create a new one replyPort = create_port(1 /* for one message */, "tmp_reply_port"); if (replyPort < 0) return replyPort; } else { assert(cachedReplyPort < sNumReplyPorts); replyPort = sReplyPorts[cachedReplyPort]; } bool recreateCachedPort = false; 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; // tests if the queue of the reply port is really empty #if 0 port_info portInfo; if (get_port_info(replyPort, &portInfo) == B_OK && portInfo.queue_count > 0) { debugger("reply port not empty!"); printf(" reply port not empty! %ld message(s) in queue\n", portInfo.queue_count); // fetch and print the messages for (int32 i = 0; i < portInfo.queue_count; i++) { char buffer[1024]; int32 code; ssize_t size = read_port(replyPort, &code, buffer, sizeof(buffer)); if (size < 0) { printf("failed to read message from reply port\n"); continue; } if (size >= (ssize_t)sizeof(buffer)) { printf("message from reply port too big\n"); continue; } BMemoryIO stream(buffer, size); BMessage reply; if (reply.Unflatten(&stream) != B_OK) { printf("failed to unflatten message from reply port\n"); continue; } printf("message %ld from reply port:\n", i); reply.PrintToStream(); } } #endif { BMessenger replyTarget; BMessenger::Private(replyTarget).SetTo(team, replyPort, B_PREFERRED_TOKEN); // TODO: replying could also use a BDirectMessageTarget like mechanism // for local targets result = _SendMessage(port, -1, token, sendTimeout, true, replyTarget); } if (result != B_OK) goto error; int32 code; result = handle_reply(replyPort, &code, replyTimeout, reply); if (result != B_OK && cachedReplyPort >= 0) { delete_port(replyPort); recreateCachedPort = true; } error: if (cachedReplyPort >= 0) { // Reclaim ownership of cached port, if possible if (!recreateCachedPort && set_port_owner(replyPort, team) == B_OK) { // Flag as available atomic_add(&sReplyPortInUse[cachedReplyPort], -1); } else sReplyPorts[cachedReplyPort] = create_port(1, "tmp_rport"); return result; } delete_port(replyPort); return result; } status_t BMessage::_SendFlattenedMessage(void* data, int32 size, port_id port, int32 token, bigtime_t timeout) { DEBUG_FUNCTION_ENTER2; if (data == NULL) return B_BAD_VALUE; uint32 magic = *(uint32*)data; if (magic == MESSAGE_FORMAT_HAIKU || magic == MESSAGE_FORMAT_HAIKU_SWAPPED) { message_header* header = (message_header*)data; header->target = token; header->flags |= MESSAGE_FLAG_WAS_DELIVERED; } else if (magic == MESSAGE_FORMAT_R5) { uint8* header = (uint8*)data; header += sizeof(uint32) /* magic */ + sizeof(uint32) /* checksum */ + sizeof(ssize_t) /* flattenedSize */ + sizeof(int32) /* what */ + sizeof(uint8) /* flags */; *(int32*)header = token; } else if (((KMessage::Header*)data)->magic == KMessage::kMessageHeaderMagic) { KMessage::Header* header = (KMessage::Header*)data; header->targetToken = token; } else { return B_NOT_A_MESSAGE; } // send the message status_t result; do { result = write_port_etc(port, kPortMessageCode, data, size, B_RELATIVE_TIMEOUT, timeout); } while (result == B_INTERRUPTED); return result; } void BMessage::_ReservedMessage1() {} void BMessage::_ReservedMessage2() {} void BMessage::_ReservedMessage3() {} // #pragma mark - Macro definitions for data access methods /* 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 \ { \ return Find##typeName(name, 0, p); \ } \ \ \ status_t \ BMessage::Find##typeName(const char* name, int32 index, type* p) const \ { \ type* 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((void *)p, ptr, sizeof(type)); \ \ return error; \ } \ \ \ status_t \ BMessage::Replace##typeName(const char* name, type value) \ { \ return ReplaceData(name, typeCode, 0, &value, sizeof(type)); \ } \ \ \ status_t \ BMessage::Replace##typeName(const char* name, int32 index, type value) \ { \ return ReplaceData(name, typeCode, index, &value, 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(BSize, Size, B_SIZE_TYPE); DEFINE_FUNCTIONS(int8, Int8, B_INT8_TYPE); DEFINE_FUNCTIONS(uint8, UInt8, B_UINT8_TYPE); DEFINE_FUNCTIONS(int16, Int16, B_INT16_TYPE); DEFINE_FUNCTIONS(uint16, UInt16, B_UINT16_TYPE); DEFINE_FUNCTIONS(int32, Int32, B_INT32_TYPE); DEFINE_FUNCTIONS(uint32, UInt32, B_UINT32_TYPE); DEFINE_FUNCTIONS(int64, Int64, B_INT64_TYPE); DEFINE_FUNCTIONS(uint64, UInt64, B_UINT64_TYPE); DEFINE_FUNCTIONS(bool, Bool, B_BOOL_TYPE); DEFINE_FUNCTIONS(float, Float, B_FLOAT_TYPE); DEFINE_FUNCTIONS(double, Double, B_DOUBLE_TYPE); DEFINE_FUNCTIONS(rgb_color, Color, B_RGB_32_BIT_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(Alignment, B_ALIGNMENT_TYPE); 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(NodeRef, B_NODE_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 #define DEFINE_SET_GET_FUNCTIONS(type, typeName, typeCode) \ type \ BMessage::Get##typeName(const char* name, type defaultValue) const \ { \ return Get##typeName(name, 0, defaultValue); \ } \ \ \ type \ BMessage::Get##typeName(const char* name, int32 index, \ type defaultValue) const \ { \ type value; \ if (Find##typeName(name, index, &value) == B_OK) \ return value; \ \ return defaultValue; \ } \ \ \ status_t \ BMessage::Set##typeName(const char* name, type value) \ { \ return SetData(name, typeCode, &value, sizeof(type)); \ } \ DEFINE_SET_GET_FUNCTIONS(int8, Int8, B_INT8_TYPE); DEFINE_SET_GET_FUNCTIONS(uint8, UInt8, B_UINT8_TYPE); DEFINE_SET_GET_FUNCTIONS(int16, Int16, B_INT16_TYPE); DEFINE_SET_GET_FUNCTIONS(uint16, UInt16, B_UINT16_TYPE); DEFINE_SET_GET_FUNCTIONS(int32, Int32, B_INT32_TYPE); DEFINE_SET_GET_FUNCTIONS(uint32, UInt32, B_UINT32_TYPE); DEFINE_SET_GET_FUNCTIONS(int64, Int64, B_INT64_TYPE); DEFINE_SET_GET_FUNCTIONS(uint64, UInt64, B_UINT64_TYPE); DEFINE_SET_GET_FUNCTIONS(bool, Bool, B_BOOL_TYPE); DEFINE_SET_GET_FUNCTIONS(float, Float, B_FLOAT_TYPE); DEFINE_SET_GET_FUNCTIONS(double, Double, B_DOUBLE_TYPE); DEFINE_SET_GET_FUNCTIONS(rgb_color, Color, B_RGB_32_BIT_TYPE); #undef DEFINE_SET_GET_FUNCTION const void* BMessage::GetPointer(const char* name, const void* defaultValue) const { return GetPointer(name, 0, defaultValue); } const void* BMessage::GetPointer(const char* name, int32 index, const void* defaultValue) const { void* value; if (FindPointer(name, index, &value) == B_OK) return value; return defaultValue; } status_t BMessage::SetPointer(const char* name, const void* value) { return SetData(name, B_POINTER_TYPE, &value, sizeof(void*)); } #define DEFINE_SET_GET_BY_REFERENCE_FUNCTIONS(type, typeName, typeCode) \ type \ BMessage::Get##typeName(const char* name, const type& defaultValue) const \ { \ return Get##typeName(name, 0, defaultValue); \ } \ \ \ type \ BMessage::Get##typeName(const char* name, int32 index, \ const type& defaultValue) const \ { \ type value; \ if (Find##typeName(name, index, &value) == B_OK) \ return value; \ \ return defaultValue; \ } \ \ \ status_t \ BMessage::Set##typeName(const char* name, const type& value) \ { \ return SetData(name, typeCode, &value, sizeof(type)); \ } \ DEFINE_SET_GET_BY_REFERENCE_FUNCTIONS(BPoint, Point, B_POINT_TYPE); DEFINE_SET_GET_BY_REFERENCE_FUNCTIONS(BRect, Rect, B_RECT_TYPE); DEFINE_SET_GET_BY_REFERENCE_FUNCTIONS(BSize, Size, B_SIZE_TYPE); DEFINE_SET_GET_BY_REFERENCE_FUNCTIONS(BAlignment, Alignment, B_ALIGNMENT_TYPE); #undef DEFINE_SET_GET_BY_REFERENCE_FUNCTIONS status_t BMessage::AddAlignment(const char* name, const BAlignment& alignment) { int32 data[2] = { alignment.horizontal, alignment.vertical }; return AddData(name, B_ALIGNMENT_TYPE, data, sizeof(data)); } status_t BMessage::AddString(const char* name, const char* string) { return AddData(name, B_STRING_TYPE, string, string ? strlen(string) + 1 : 0, false); } status_t BMessage::AddString(const char* name, const BString& string) { return AddData(name, B_STRING_TYPE, string.String(), string.Length() + 1, false); } status_t BMessage::AddStrings(const char* name, const BStringList& list) { int32 count = list.CountStrings(); for (int32 i = 0; i < count; i++) { status_t error = AddString(name, list.StringAt(i)); if (error != B_OK) return error; } return B_OK; } 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::AddNodeRef(const char* name, const node_ref* ref) { size_t size = sizeof(node_ref); char buffer[size]; status_t error = BPrivate::node_ref_flatten(buffer, &size, ref); if (error >= B_OK) error = AddData(name, B_NODE_REF_TYPE, buffer, size, false); return error; } status_t BMessage::AddMessage(const char* name, const BMessage* message) { if (message == NULL) return B_BAD_VALUE; // 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. char stackBuffer[16384]; ssize_t size = message->FlattenedSize(); char* buffer; if (size > (ssize_t)sizeof(stackBuffer)) { buffer = (char*)malloc(size); if (buffer == NULL) return B_NO_MEMORY; } else buffer = stackBuffer; status_t error = message->Flatten(buffer, size); if (error >= B_OK) error = AddData(name, B_MESSAGE_TYPE, buffer, size, false); if (buffer != stackBuffer) free(buffer); return error; } status_t BMessage::AddFlat(const char* name, BFlattenable* object, int32 count) { return AddFlat(name, (const BFlattenable*)object, count); } status_t BMessage::AddFlat(const char* name, const BFlattenable* object, int32 count) { if (object == NULL) return B_BAD_VALUE; char stackBuffer[16384]; ssize_t size = object->FlattenedSize(); char* buffer; if (size > (ssize_t)sizeof(stackBuffer)) { buffer = (char*)malloc(size); if (buffer == NULL) return B_NO_MEMORY; } else buffer = stackBuffer; status_t error = object->Flatten(buffer, size); if (error >= B_OK) error = AddData(name, object->TypeCode(), buffer, size, false); if (buffer != stackBuffer) free(buffer); return error; } status_t BMessage::Append(const BMessage& other) { field_header* field = other.fFields; for (uint32 i = 0; i < other.fHeader->field_count; i++, field++) { const char* name = (const char*)(other.fData + field->offset); const void* data = (const void*)(other.fData + field->offset + field->name_length); bool isFixed = (field->flags & FIELD_FLAG_FIXED_SIZE) != 0; size_t size = field->data_size / field->count; for (uint32 j = 0; j < field->count; j++) { if (!isFixed) { size = *(uint32*)data; data = (const void*)((const char*)data + sizeof(uint32)); } status_t status = AddData(name, field->type, data, size, isFixed, 1); if (status != B_OK) return status; data = (const void*)((const char*)data + size); } } return B_OK; } status_t BMessage::FindAlignment(const char* name, BAlignment* alignment) const { return FindAlignment(name, 0, alignment); } status_t BMessage::FindAlignment(const char* name, int32 index, BAlignment* alignment) const { if (!alignment) return B_BAD_VALUE; int32* data; ssize_t bytes; status_t err = FindData(name, B_ALIGNMENT_TYPE, index, (const void**)&data, &bytes); if (err == B_OK) { if (bytes != sizeof(int32[2])) return B_ERROR; alignment->horizontal = (enum alignment)(*data); alignment->vertical = (vertical_alignment)*(data + 1); } return err; } 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 { if (string == NULL) return B_BAD_VALUE; const char* value; status_t error = FindString(name, index, &value); // Find*() clobbers the object even on failure string->SetTo(value); return error; } status_t BMessage::FindStrings(const char* name, BStringList* list) const { if (list == NULL) return B_BAD_VALUE; list->MakeEmpty(); // get the number of items type_code type; int32 count; if (GetInfo(name, &type, &count) != B_OK) return B_NAME_NOT_FOUND; if (type != B_STRING_TYPE) return B_BAD_DATA; for (int32 i = 0; i < count; i++) { BString string; status_t error = FindString(name, i, &string); if (error != B_OK) return error; if (!list->Add(string)) return B_NO_MEMORY; } 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 { if (pointer == NULL) return B_BAD_VALUE; 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 { if (messenger == NULL) return B_BAD_VALUE; BMessenger* data = NULL; ssize_t size = 0; status_t error = FindData(name, B_MESSENGER_TYPE, index, (const void**)&data, &size); if (error == B_OK) *messenger = *data; 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 { if (ref == NULL) return B_BAD_VALUE; 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::FindNodeRef(const char* name, node_ref* ref) const { return FindNodeRef(name, 0, ref); } status_t BMessage::FindNodeRef(const char* name, int32 index, node_ref* ref) const { if (ref == NULL) return B_BAD_VALUE; void* data = NULL; ssize_t size = 0; status_t error = FindData(name, B_NODE_REF_TYPE, index, (const void**)&data, &size); if (error == B_OK) error = BPrivate::node_ref_unflatten(ref, (char*)data, size); else *ref = node_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 { if (message == NULL) return B_BAD_VALUE; 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 { if (object == NULL) return B_BAD_VALUE; 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::ReplaceAlignment(const char* name, const BAlignment& alignment) { int32 data[2] = {alignment.horizontal, alignment.vertical}; return ReplaceData(name, B_ALIGNMENT_TYPE, 0, data, sizeof(data)); } status_t BMessage::ReplaceAlignment(const char* name, int32 index, const BAlignment& alignment) { int32 data[2] = {alignment.horizontal, alignment.vertical}; return ReplaceData(name, B_ALIGNMENT_TYPE, index, data, sizeof(data)); } status_t BMessage::ReplaceString(const char* name, const char* string) { if (string == NULL) return B_BAD_VALUE; return ReplaceData(name, B_STRING_TYPE, 0, string, strlen(string) + 1); } status_t BMessage::ReplaceString(const char* name, int32 index, const char* string) { if (string == NULL) return B_BAD_VALUE; 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::ReplaceNodeRef(const char* name, const node_ref* ref) { return ReplaceNodeRef(name, 0, ref); } status_t BMessage::ReplaceNodeRef(const char* name, int32 index, const node_ref* ref) { size_t size = sizeof(node_ref) + B_PATH_NAME_LENGTH; char buffer[size]; status_t error = BPrivate::node_ref_flatten(buffer, &size, ref); if (error >= B_OK) error = ReplaceData(name, B_NODE_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) { if (message == NULL) return B_BAD_VALUE; ssize_t size = message->FlattenedSize(); if (size < 0) return B_BAD_VALUE; 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) { if (object == NULL) return B_BAD_VALUE; ssize_t size = object->FlattenedSize(); if (size < 0) return B_BAD_VALUE; 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); } const char* BMessage::GetString(const char* name, const char* defaultValue) const { return GetString(name, 0, defaultValue); } const char* BMessage::GetString(const char* name, int32 index, const char* defaultValue) const { const char* value; if (FindString(name, index, &value) == B_OK) return value; return defaultValue; } status_t BMessage::SetString(const char* name, const BString& value) { return SetData(name, B_STRING_TYPE, value.String(), value.Length() + 1, false); } status_t BMessage::SetString(const char* name, const char* value) { return SetData(name, B_STRING_TYPE, value, strlen(value) + 1, false); } status_t BMessage::SetData(const char* name, type_code type, const void* data, ssize_t numBytes, bool fixedSize, int count) { if (numBytes <= 0 || data == NULL) return B_BAD_VALUE; if (ReplaceData(name, type, data, numBytes) == B_OK) return B_OK; return AddData(name, type, data, numBytes, fixedSize, count); }