root/src/system/kernel/messaging/KMessage.cpp
/*
 * Copyright 2005-2010, Ingo Weinhold, ingo_weinhold@gmx.de.
 * Distributed under the terms of the MIT License.
 */


#include <util/KMessage.h>

#include <stdlib.h>
#include <string.h>

#include <ByteOrder.h>
#include <Debug.h>
#include <KernelExport.h>
#include <TypeConstants.h>


#if defined(_BOOT_MODE) || defined(_LOADER_MODE)
#       include <util/kernel_cpp.h>
#else
#       include <new>
#endif


// TODO: Add a field index using a hash map, so that lookup improves to O(1)
// (is now O(n)).


// define the PANIC macro
#ifndef PANIC
#       if defined(_KERNEL_MODE) || defined(_BOOT_MODE)
#               define PANIC(str)       panic(str)
#       else
#               define PANIC(str)       debugger(str)
#       endif
#endif


#if !defined(HAIKU_TARGET_PLATFORM_HAIKU) || defined(_BOOT_MODE) \
        || defined(_LOADER_MODE)
#       define MEMALIGN(alignment, size)        malloc(size)
        // Built as part of a build tool or the boot or runtime loader.
#else
#       include <malloc.h>
#       define MEMALIGN(alignment, size)        memalign(alignment, size)
        // Built as part of the kernel or userland. Using memalign allows use of
        // special heap implementations that might otherwise return unaligned
        // buffers for debugging purposes.
#endif


static const int32 kMessageReallocChunkSize = 64;
static const size_t kMessageBufferAlignment = 4;

const uint32 KMessage::kMessageHeaderMagic = 'kMsG';


// #pragma mark - Helper Functions

static inline int32
_Align(int32 offset)
{
        return (offset + kMessageBufferAlignment - 1)
                & ~(kMessageBufferAlignment - 1);
}


static inline void*
_Align(void* address, int32 offset = 0)
{
        return (void*)(((addr_t)address + offset + kMessageBufferAlignment - 1)
                & ~(kMessageBufferAlignment - 1));
}


// #pragma mark - FieldValueHeader


struct KMessage::FieldValueHeader {
        int32           size;

        void* Data()
        {
                return _Align(this, sizeof(FieldValueHeader));
        }

        FieldValueHeader* NextFieldValueHeader()
        {
                return (FieldValueHeader*)_Align(Data(), size);
        }
};


// #pragma mark - FieldHeader


struct KMessage::FieldHeader {
        type_code       type;
        int32           elementSize;    // if < 0: non-fixed size
        int32           elementCount;
        int32           fieldSize;
        int16           headerSize;
        char            name[1];

        void* Data()
        {
                return (uint8*)this + headerSize;
        }

        bool HasFixedElementSize()
        {
                return elementSize >= 0;
        }

        void* ElementAt(int32 index, int32* size)
        {
                if (index < 0 || index >= elementCount)
                        return NULL;
                uint8* data = (uint8*)this + headerSize;
                if (HasFixedElementSize()) {
                        *size = elementSize;
                        return data + elementSize * index;
                }
                // non-fixed element size: we need to iterate
                FieldValueHeader* valueHeader = (FieldValueHeader*)data;
                for (int i = 0; i < index; i++)
                        valueHeader = valueHeader->NextFieldValueHeader();
                *size = valueHeader->size;
                return valueHeader->Data();
        }

        FieldHeader* NextFieldHeader()
        {
                return (FieldHeader*)_Align(this, fieldSize);
        }
};


// #pragma mark - KMessage


KMessage::KMessage()
        :
        fBuffer(NULL),
        fBufferCapacity(0),
        fFlags(0),
        fLastFieldOffset(0)
{
        Unset();
}


KMessage::KMessage(uint32 what)
        :
        fBuffer(NULL),
        fBufferCapacity(0),
        fFlags(0),
        fLastFieldOffset(0)
{
        Unset();
        SetWhat(what);
}


KMessage::~KMessage()
{
        Unset();
}


status_t
KMessage::SetTo(uint32 what, uint32 flags)
{
        // There are no flags interesting in this case at the moment.
        Unset();
        SetWhat(what);
        return B_OK;
}


status_t
KMessage::SetTo(void* buffer, int32 bufferSize, uint32 what, uint32 flags)
{
        Unset();

        if (!buffer)
                return B_BAD_VALUE;

        if (bufferSize < 0) {
                if (!(flags & KMESSAGE_INIT_FROM_BUFFER))
                        return B_BAD_VALUE;
        } else if (bufferSize < (int)sizeof(Header))
                return B_BAD_VALUE;

        // if read-only, we need to init from the buffer, too
        if ((flags & KMESSAGE_READ_ONLY) != 0
                && (flags & KMESSAGE_INIT_FROM_BUFFER) == 0) {
                return B_BAD_VALUE;
        }

        // if not initializing from the given buffer, cloning it doesn't make sense
        if ((flags & KMESSAGE_INIT_FROM_BUFFER) == 0
                && (flags & KMESSAGE_CLONE_BUFFER) != 0) {
                return B_BAD_VALUE;
        }

        fBuffer = buffer;
        fBufferCapacity = bufferSize;
        fFlags = flags;

        status_t error = B_OK;
        if (flags & KMESSAGE_INIT_FROM_BUFFER)
                error = _InitFromBuffer(bufferSize < 0);
        else
                _InitBuffer(what);

        if (error != B_OK)
                Unset();

        return error;
}


status_t
KMessage::SetTo(const void* buffer, int32 bufferSize, uint32 flags)
{
        return SetTo(const_cast<void*>(buffer), bufferSize, 0,
                KMESSAGE_INIT_FROM_BUFFER | KMESSAGE_READ_ONLY | flags);
}


void
KMessage::Unset()
{
        // free buffer
        if (fBuffer && fBuffer != &fHeader && (fFlags & KMESSAGE_OWNS_BUFFER))
                free(fBuffer);
        fBuffer = &fHeader;
        fBufferCapacity = sizeof(Header);
        _InitBuffer(0);
}


void
KMessage::SetWhat(uint32 what)
{
        _Header()->what = what;
}


uint32
KMessage::What() const
{
        return _Header()->what;
}


const void*
KMessage::Buffer() const
{
        return fBuffer;
}


int32
KMessage::BufferCapacity() const
{
        return fBufferCapacity;
}


int32
KMessage::ContentSize() const
{
        return _Header()->size;
}


status_t
KMessage::AddField(const char* name, type_code type, int32 elementSize,
        KMessageField* field)
{
        if (!name || type == B_ANY_TYPE)
                return B_BAD_VALUE;
        KMessageField existingField;
        if (FindField(name, &existingField) == B_OK)
                return B_NAME_IN_USE;
        return _AddField(name, type, elementSize, field);
}


status_t
KMessage::FindField(const char* name, KMessageField* field) const
{
        return FindField(name, B_ANY_TYPE, field);
}


status_t
KMessage::FindField(const char* name, type_code type,
        KMessageField* field) const
{
        if (!name)
                return B_BAD_VALUE;
        KMessageField stackField;
        if (field)
                field->Unset();
        else
                field = &stackField;
        while (GetNextField(field) == B_OK) {
                if ((type == B_ANY_TYPE || field->TypeCode() == type)
                        && strcmp(name, field->Name()) == 0) {
                        return B_OK;
                }
        }
        return B_NAME_NOT_FOUND;
}


status_t
KMessage::GetNextField(KMessageField* field) const
{
        if (!field || (field->Message() != NULL && field->Message() != this))
                return B_BAD_VALUE;
        FieldHeader* fieldHeader = field->_Header();
        FieldHeader* lastField = _LastFieldHeader();
        if (!lastField)
                return B_NAME_NOT_FOUND;
        if (fieldHeader == NULL) {
                fieldHeader = _FirstFieldHeader();
        } else {
                if ((uint8*)fieldHeader < (uint8*)_FirstFieldHeader()
                        || (uint8*)fieldHeader > (uint8*)lastField) {
                        return B_BAD_VALUE;
                }
                if (fieldHeader == lastField)
                        return B_NAME_NOT_FOUND;
                fieldHeader = fieldHeader->NextFieldHeader();
        }
        field->SetTo(const_cast<KMessage*>(this), _BufferOffsetFor(fieldHeader));
        return B_OK;
}


bool
KMessage::IsEmpty() const
{
        return _LastFieldHeader() == NULL;
}


status_t
KMessage::AddData(const char* name, type_code type, const void* data,
        int32 numBytes, bool isFixedSize)
{
        if (!name || type == B_ANY_TYPE || !data || numBytes < 0)
                return B_BAD_VALUE;
        KMessageField field;
        if (FindField(name, &field) == B_OK) {
                // field with that name already exists: check its type
                if (field.TypeCode() != type)
                        return B_BAD_TYPE;
        } else {
                // no such field yet: add it
                status_t error = _AddField(name, type, (isFixedSize ? numBytes : -1),
                        &field);
                if (error != B_OK)
                        return error;
        }
        return _AddFieldData(&field, data, numBytes, 1);
}


status_t
KMessage::AddArray(const char* name, type_code type, const void* data,
        int32 elementSize, int32 elementCount)
{
        if (!name || type == B_ANY_TYPE || !data || elementSize < 0
                || elementCount < 0) {
                return B_BAD_VALUE;
        }
        KMessageField field;
        if (FindField(name, &field) == B_OK) {
                // field with that name already exists: check its type
                if (field.TypeCode() != type)
                        return B_BAD_TYPE;
        } else {
                // no such field yet: add it
                status_t error = _AddField(name, type, elementSize, &field);
                if (error != B_OK)
                        return error;
        }
        return _AddFieldData(&field, data, elementSize, elementCount);
}


status_t
KMessage::SetData(const char* name, type_code type, const void* data,
        int32 numBytes)
{
        if (fBuffer != &fHeader && (fFlags & KMESSAGE_READ_ONLY))
                return B_NOT_ALLOWED;

        KMessageField field;

        if (FindField(name, &field) == B_OK) {
                // field already known
                if (field.TypeCode() != type || !field.HasFixedElementSize()
                        || field.ElementSize() != numBytes) {
                        return B_BAD_VALUE;
                }

                // if it has an element, just replace its value
                if (field.CountElements() > 0) {
                        const void* element = field.ElementAt(0);
                        memcpy(const_cast<void*>(element), data, numBytes);
                        return B_OK;
                }
        } else {
                // no such field yet -- add it
                status_t error = _AddField(name, type, numBytes, &field);
                if (error != B_OK)
                        return error;
        }

        // we've got an empty field -- add the element
        return _AddFieldData(&field, data, numBytes, 1);
}


status_t
KMessage::FindData(const char* name, type_code type, const void** data,
        int32* numBytes) const
{
        return FindData(name, type, 0, data, numBytes);
}


status_t
KMessage::FindData(const char* name, type_code type, int32 index,
        const void** data, int32* numBytes) const
{
        if (!name || !data || !numBytes)
                return B_BAD_VALUE;
        KMessageField field;
        status_t error = FindField(name, type, &field);
        if (error != B_OK)
                return error;
        const void* foundData = field.ElementAt(index, numBytes);
        if (!foundData)
                return B_BAD_INDEX;
        if (data)
                *data = foundData;
        return B_OK;
}


team_id
KMessage::Sender() const
{
        return _Header()->sender;
}


int32
KMessage::TargetToken() const
{
        return _Header()->targetToken;
}


port_id
KMessage::ReplyPort() const
{
        return _Header()->replyPort;
}


int32
KMessage::ReplyToken() const
{
        return _Header()->replyToken;
}


void
KMessage::SetDeliveryInfo(int32 targetToken, port_id replyPort,
        int32 replyToken, team_id senderTeam)
{
        Header* header = _Header();
        header->sender = senderTeam;
        header->targetToken = targetToken;
        header->replyPort = replyPort;
        header->replyToken = replyToken;
        header->sender = senderTeam;
}


#ifndef KMESSAGE_CONTAINER_ONLY


status_t
KMessage::SendTo(port_id targetPort, int32 targetToken, port_id replyPort,
        int32 replyToken, bigtime_t timeout, team_id senderTeam)
{
        // get the sender team
        if (senderTeam < 0) {
                thread_info info;
                status_t error = get_thread_info(find_thread(NULL), &info);
                if (error != B_OK)
                        return error;

                senderTeam = info.team;
        }

        SetDeliveryInfo(targetToken, replyPort, replyToken, senderTeam);

        // send the message
        if (timeout < 0)
                return write_port(targetPort, 'KMSG', fBuffer, ContentSize());

        return write_port_etc(targetPort, 'KMSG', fBuffer, ContentSize(),
                B_RELATIVE_TIMEOUT, timeout);
}


status_t
KMessage::SendTo(port_id targetPort, int32 targetToken, KMessage* reply,
        bigtime_t deliveryTimeout, bigtime_t replyTimeout, team_id senderTeam)
{
        // get the team the target port belongs to
        port_info portInfo;
        status_t error = get_port_info(targetPort, &portInfo);
        if (error != B_OK)
                return error;
        team_id targetTeam = portInfo.team;
        // allocate a reply port, if a reply is desired
        port_id replyPort = -1;
        if (reply) {
                // get our team
                team_id ourTeam = B_SYSTEM_TEAM;
                #ifndef _KERNEL_MODE
                        if (targetTeam != B_SYSTEM_TEAM) {
                                thread_info threadInfo;
                                error = get_thread_info(find_thread(NULL), &threadInfo);
                                if (error != B_OK)
                                        return error;
                                ourTeam = threadInfo.team;
                        }
                #endif
                // create the port
                replyPort = create_port(1, "KMessage reply port");
                if (replyPort < 0)
                        return replyPort;
                // If the target team is not our team and not the kernel team either,
                // we transfer the ownership of the port to it, so we will not block
                if (targetTeam != ourTeam && targetTeam != B_SYSTEM_TEAM)
                        set_port_owner(replyPort, targetTeam);
        }
        struct PortDeleter {
                PortDeleter(port_id port) : port(port) {}
                ~PortDeleter()
                {
                        if (port >= 0)
                                delete_port(port);
                }

                port_id port;
        } replyPortDeleter(replyPort);
        // send the message
        error = SendTo(targetPort, targetToken, replyPort, 0,
                deliveryTimeout, senderTeam);
        if (error != B_OK)
                return error;
        // get the reply
        if (reply)
                return reply->ReceiveFrom(replyPort, replyTimeout);
        return B_OK;
}


status_t
KMessage::SendReply(KMessage* message, port_id replyPort, int32 replyToken,
        bigtime_t timeout, team_id senderTeam)
{
        if (!message)
                return B_BAD_VALUE;
        return message->SendTo(ReplyPort(), ReplyToken(), replyPort, replyToken,
                timeout, senderTeam);
}


status_t
KMessage::SendReply(KMessage* message, KMessage* reply,
        bigtime_t deliveryTimeout, bigtime_t replyTimeout, team_id senderTeam)
{
        if (!message)
                return B_BAD_VALUE;
        return message->SendTo(ReplyPort(), ReplyToken(), reply, deliveryTimeout,
                replyTimeout, senderTeam);
}


status_t
KMessage::ReceiveFrom(port_id fromPort, bigtime_t timeout,
        port_message_info* messageInfo)
{
        port_message_info _messageInfo;
        if (messageInfo == NULL)
                messageInfo = &_messageInfo;

        // get the port buffer size
        status_t error;
        if (timeout < 0) {
                error = get_port_message_info_etc(fromPort, messageInfo, 0, 0);
        } else {
                error = get_port_message_info_etc(fromPort, messageInfo,
                        B_RELATIVE_TIMEOUT, timeout);
        }
        if (error != B_OK)
                return error;

        // allocate a buffer
        uint8* buffer = (uint8*)MEMALIGN(kMessageBufferAlignment,
                messageInfo->size);
        if (!buffer)
                return B_NO_MEMORY;

        // read the message
        int32 what;
        ssize_t realSize = read_port_etc(fromPort, &what, buffer, messageInfo->size,
                B_RELATIVE_TIMEOUT, 0);
        if (realSize < 0) {
                free(buffer);
                return realSize;
        }
        if (messageInfo->size != (size_t)realSize) {
                free(buffer);
                return B_ERROR;
        }

        // init the message
        return SetTo(buffer, messageInfo->size, 0,
                KMESSAGE_OWNS_BUFFER | KMESSAGE_INIT_FROM_BUFFER);
}


#endif  // !KMESSAGE_CONTAINER_ONLY


void
KMessage::Dump(void (*printFunc)(const char*, ...)) const
{
        Header* header = _Header();
        printFunc("KMessage: buffer: %p (size/capacity: %ld/%ld), flags: %#"
                B_PRIx32 "\n", fBuffer, header->size, fBufferCapacity, fFlags);

        KMessageField field;
        while (GetNextField(&field) == B_OK) {
                type_code type = field.TypeCode();
                uint32 bigEndianType = B_HOST_TO_BENDIAN_INT32(type);
                int nameSpacing = 17 - strlen(field.Name());
                if (nameSpacing < 0)
                        nameSpacing = 0;

                printFunc("  field: \"%s\"%*s (%.4s): ", field.Name(), nameSpacing, "",
                        (char*)&bigEndianType);

                if (field.CountElements() != 1)
                        printFunc("\n");

                int32 size;
                for (int i = 0; const void* data = field.ElementAt(i, &size); i++) {
                        if (field.CountElements() != 1)
                                printFunc("    [%2d] ", i);

                        bool isIntType = false;
                        int64 intData = 0;
                        switch (type) {
                                case B_BOOL_TYPE:
                                        printFunc("%s\n", (*(bool*)data ? "true" : "false"));
                                        break;
                                case B_INT8_TYPE:
                                        isIntType = true;
                                        intData = *(int8*)data;
                                        break;
                                case B_INT16_TYPE:
                                        isIntType = true;
                                        intData = *(int16*)data;
                                        break;
                                case B_INT32_TYPE:
                                        isIntType = true;
                                        intData = *(int32*)data;
                                        break;
                                case B_INT64_TYPE:
                                        isIntType = true;
                                        intData = *(int64*)data;
                                        break;
                                case B_STRING_TYPE:
                                        printFunc("\"%s\"\n", (char*)data);
                                        break;
                                default:
                                        printFunc("data at %p, %ld bytes\n", (char*)data, size);
                                        break;
                        }
                        if (isIntType)
                                printFunc("%lld (0x%llx)\n", intData, intData);
                }
        }
}


KMessage::Header*
KMessage::_Header() const
{
        return (Header*)fBuffer;
}


int32
KMessage::_BufferOffsetFor(const void* data) const
{
        if (!data)
                return -1;
        return (uint8*)data - (uint8*)fBuffer;
}


KMessage::FieldHeader*
KMessage::_FirstFieldHeader() const
{
        return (FieldHeader*)_Align(fBuffer, sizeof(Header));
}


KMessage::FieldHeader*
KMessage::_LastFieldHeader() const
{
        return _FieldHeaderForOffset(fLastFieldOffset);
}


KMessage::FieldHeader*
KMessage::_FieldHeaderForOffset(int32 offset) const
{
        if (offset <= 0 || offset >= _Header()->size)
                return NULL;
        return (FieldHeader*)((uint8*)fBuffer + offset);
}


status_t
KMessage::_AddField(const char* name, type_code type, int32 elementSize,
        KMessageField* field)
{
        FieldHeader* fieldHeader;
        int32 alignedSize;
        status_t error = _AllocateSpace(sizeof(FieldHeader) + strlen(name), true,
                true, (void**)&fieldHeader, &alignedSize);
        if (error != B_OK)
                return error;
        fieldHeader->type = type;
        fieldHeader->elementSize = elementSize;
        fieldHeader->elementCount = 0;
        fieldHeader->fieldSize = alignedSize;
        fieldHeader->headerSize = alignedSize;
        strcpy(fieldHeader->name, name);
        fLastFieldOffset = _BufferOffsetFor(fieldHeader);
        if (field)
                field->SetTo(this, _BufferOffsetFor(fieldHeader));
        return B_OK;
}


status_t
KMessage::_AddFieldData(KMessageField* field, const void* data,
        int32 elementSize, int32 elementCount)
{
        if (!field)
                return B_BAD_VALUE;
        FieldHeader* fieldHeader = field->_Header();
        FieldHeader* lastField = _LastFieldHeader();
        if (!fieldHeader || fieldHeader != lastField || !data
                || elementSize < 0 || elementCount < 0) {
                return B_BAD_VALUE;
        }
        if (elementCount == 0)
                return B_OK;
        // fixed size values
        if (fieldHeader->HasFixedElementSize()) {
                if (elementSize != fieldHeader->elementSize)
                        return B_BAD_VALUE;
                void* address;
                int32 alignedSize;
                status_t error = _AllocateSpace(elementSize * elementCount,
                        (fieldHeader->elementCount == 0), false, &address, &alignedSize);
                if (error != B_OK)
                        return error;
                fieldHeader = field->_Header(); // might have been relocated
                memcpy(address, data, elementSize * elementCount);
                fieldHeader->elementCount += elementCount;
                fieldHeader->fieldSize = (uint8*)address + alignedSize
                        - (uint8*)fieldHeader;
                return B_OK;
        }
        // non-fixed size values
        // add the elements individually (TODO: Optimize!)
        int32 valueHeaderSize = _Align(sizeof(FieldValueHeader));
        int32 entrySize = valueHeaderSize + elementSize;
        for (int32 i = 0; i < elementCount; i++) {
                void* address;
                int32 alignedSize;
                status_t error = _AllocateSpace(entrySize, true, false, &address,
                        &alignedSize);
                if (error != B_OK)
                        return error;
                fieldHeader = field->_Header(); // might have been relocated
                FieldValueHeader* valueHeader = (FieldValueHeader*)address;
                valueHeader->size = elementSize;
                memcpy(valueHeader->Data(), (const uint8*)data + i * elementSize,
                        elementSize);
                fieldHeader->elementCount++;
                fieldHeader->fieldSize = (uint8*)address + alignedSize
                        - (uint8*)fieldHeader;
        }
        return B_OK;
}


status_t
KMessage::_InitFromBuffer(bool sizeFromBuffer)
{
        if (fBuffer == NULL)
                return B_BAD_DATA;

        // clone the buffer, if requested
        if ((fFlags & KMESSAGE_CLONE_BUFFER) != 0 || _Align(fBuffer) != fBuffer) {
                if (sizeFromBuffer) {
                        int32 size = fBufferCapacity;
                        memcpy(&size, &_Header()->size, 4);
                        fBufferCapacity = size;
                }

                void* buffer = MEMALIGN(kMessageBufferAlignment, fBufferCapacity);
                if (buffer == NULL)
                        return B_NO_MEMORY;

                memcpy(buffer, fBuffer, fBufferCapacity);

                if ((fFlags & KMESSAGE_OWNS_BUFFER) != 0)
                        free(fBuffer);

                fBuffer = buffer;
                fFlags &= ~(uint32)(KMESSAGE_READ_ONLY | KMESSAGE_CLONE_BUFFER);
                fFlags |= KMESSAGE_OWNS_BUFFER;
        }

        if (_Align(fBuffer) != fBuffer)
                return B_BAD_DATA;

        Header* header = _Header();

        if (sizeFromBuffer)
                fBufferCapacity = header->size;

        if (fBufferCapacity < (int)sizeof(Header))
                return B_BAD_DATA;

        // check header
        if (header->magic != kMessageHeaderMagic)
                return B_BAD_DATA;
        if (header->size < (int)sizeof(Header) || header->size > fBufferCapacity)
                return B_BAD_DATA;

        // check the fields
        FieldHeader* fieldHeader = NULL;
        uint8* data = (uint8*)_FirstFieldHeader();
        int32 remainingBytes = (uint8*)fBuffer + header->size - data;
        while (remainingBytes > 0) {
                if (remainingBytes < (int)sizeof(FieldHeader))
                        return B_BAD_DATA;
                fieldHeader = (FieldHeader*)data;
                // check field header
                if (fieldHeader->type == B_ANY_TYPE)
                        return B_BAD_DATA;
                if (fieldHeader->elementCount < 0)
                        return B_BAD_DATA;
                if (fieldHeader->fieldSize < (int)sizeof(FieldHeader)
                        || fieldHeader->fieldSize > remainingBytes) {
                        return B_BAD_DATA;
                }
                if (fieldHeader->headerSize < (int)sizeof(FieldHeader)
                        || fieldHeader->headerSize > fieldHeader->fieldSize) {
                        return B_BAD_DATA;
                }
                int32 maxNameLen = data + fieldHeader->headerSize
                        - (uint8*)fieldHeader->name;
                int32 nameLen = strnlen(fieldHeader->name, maxNameLen);
                if (nameLen == maxNameLen || nameLen == 0)
                        return B_BAD_DATA;
                int32 fieldSize =  fieldHeader->headerSize;
                if (fieldHeader->HasFixedElementSize()) {
                        // fixed element size
                        int32 dataSize = fieldHeader->elementSize
                                * fieldHeader->elementCount;
                        fieldSize = (uint8*)fieldHeader->Data() + dataSize - data;
                } else {
                        // non-fixed element size
                        FieldValueHeader* valueHeader
                                = (FieldValueHeader*)fieldHeader->Data();
                        for (int32 i = 0; i < fieldHeader->elementCount; i++) {
                                remainingBytes = (uint8*)fBuffer + header->size
                                        - (uint8*)valueHeader;
                                if (remainingBytes < (int)sizeof(FieldValueHeader))
                                        return B_BAD_DATA;
                                uint8* value = (uint8*)valueHeader->Data();
                                remainingBytes = (uint8*)fBuffer + header->size - (uint8*)value;
                                if (remainingBytes < valueHeader->size)
                                        return B_BAD_DATA;
                                fieldSize = value + valueHeader->size - data;
                                valueHeader = valueHeader->NextFieldValueHeader();
                        }
                        if (fieldSize > fieldHeader->fieldSize)
                                return B_BAD_DATA;
                }
                data = (uint8*)fieldHeader->NextFieldHeader();
                remainingBytes = (uint8*)fBuffer + header->size - data;
        }
        fLastFieldOffset = _BufferOffsetFor(fieldHeader);
        return B_OK;
}


void
KMessage::_InitBuffer(uint32 what)
{
        Header* header = _Header();
        header->magic = kMessageHeaderMagic;
        header->size = sizeof(Header);
        header->what = what;
        header->sender = -1;
        header->targetToken = -1;
        header->replyPort = -1;
        header->replyToken = -1;
        fLastFieldOffset = 0;
}


void
KMessage::_CheckBuffer()
{
        int32 lastFieldOffset = fLastFieldOffset;
        if (_InitFromBuffer(false) != B_OK) {
                PANIC("internal data mangled");
        }
        if (fLastFieldOffset != lastFieldOffset) {
                PANIC("fLastFieldOffset changed during KMessage::_CheckBuffer()");
        }
}


status_t
KMessage::_AllocateSpace(int32 size, bool alignAddress, bool alignSize,
        void** address, int32* alignedSize)
{
        if (fBuffer != &fHeader && (fFlags & KMESSAGE_READ_ONLY))
                return B_NOT_ALLOWED;

        int32 offset = ContentSize();
        if (alignAddress)
                offset = _Align(offset);
        int32 newSize = offset + size;
        if (alignSize)
                newSize = _Align(newSize);
        // reallocate if necessary
        if (fBuffer == &fHeader) {
                int32 newCapacity = _CapacityFor(newSize);
                void* newBuffer = MEMALIGN(kMessageBufferAlignment, newCapacity);
                if (!newBuffer)
                        return B_NO_MEMORY;
                fBuffer = newBuffer;
                fBufferCapacity = newCapacity;
                fFlags |= KMESSAGE_OWNS_BUFFER;
                memcpy(fBuffer, &fHeader, sizeof(fHeader));
        } else {
                if (newSize > fBufferCapacity) {
                        // if we don't own the buffer, we can't resize it
                        if (!(fFlags & KMESSAGE_OWNS_BUFFER)) {
#if defined(_KERNEL_MODE) && 0
                                // optional debugging to find insufficiently sized KMessage
                                // buffers (e.g. for in-kernel notifications)
                                panic("KMessage: out of space: available: %" B_PRId32
                                        ", needed: %" B_PRId32 "\n", fBufferCapacity, newSize);
#endif
                                return B_BUFFER_OVERFLOW;
                        }

                        int32 newCapacity = _CapacityFor(newSize);
                        void* newBuffer = realloc(fBuffer, newCapacity);
                        if (!newBuffer)
                                return B_NO_MEMORY;
                        fBuffer = newBuffer;
                        fBufferCapacity = newCapacity;
                }
        }
        _Header()->size = newSize;
        *address = (char*)fBuffer + offset;
        *alignedSize = newSize - offset;
        return B_OK;
}


int32
KMessage::_CapacityFor(int32 size)
{
        return (size + kMessageReallocChunkSize - 1) / kMessageReallocChunkSize
                * kMessageReallocChunkSize;
}


// #pragma mark - KMessageField


KMessageField::KMessageField()
        :
        fMessage(NULL),
        fHeaderOffset(0)
{
}


void
KMessageField::Unset()
{
        fMessage = NULL;
        fHeaderOffset = 0;
}


KMessage*
KMessageField::Message() const
{
        return fMessage;
}


const char*
KMessageField::Name() const
{
        KMessage::FieldHeader* header = _Header();
        return header ? header->name : NULL;
}


type_code
KMessageField::TypeCode() const
{
        KMessage::FieldHeader* header = _Header();
        return header ? header->type : 0;
}


bool
KMessageField::HasFixedElementSize() const
{
        KMessage::FieldHeader* header = _Header();
        return header ? header->HasFixedElementSize() : false;
}


int32
KMessageField::ElementSize() const
{
        KMessage::FieldHeader* header = _Header();
        return header ? header->elementSize : -1;
}


status_t
KMessageField::AddElement(const void* data, int32 size)
{
        KMessage::FieldHeader* header = _Header();
        if (!header || !data)
                return B_BAD_VALUE;
        if (size < 0) {
                size = ElementSize();
                if (size < 0)
                        return B_BAD_VALUE;
        }
        return fMessage->_AddFieldData(this, data, size, 1);
}


status_t
KMessageField::AddElements(const void* data, int32 count, int32 elementSize)
{
        KMessage::FieldHeader* header = _Header();
        if (!header || !data || count < 0)
                return B_BAD_VALUE;
        if (elementSize < 0) {
                elementSize = ElementSize();
                if (elementSize < 0)
                        return B_BAD_VALUE;
        }
        return fMessage->_AddFieldData(this, data, elementSize, count);
}


const void*
KMessageField::ElementAt(int32 index, int32* size) const
{
        KMessage::FieldHeader* header = _Header();
        return header ? header->ElementAt(index, size) : NULL;
}


int32
KMessageField::CountElements() const
{
        KMessage::FieldHeader* header = _Header();
        return header ? header->elementCount : 0;
}


void
KMessageField::SetTo(KMessage* message, int32 headerOffset)
{
        fMessage = message;
        fHeaderOffset = headerOffset;
}


KMessage::FieldHeader*
KMessageField::_Header() const
{
        return fMessage ? fMessage->_FieldHeaderForOffset(fHeaderOffset) : NULL;
}