root/src/kits/app/MessageAdapter.cpp
/*
 * Copyright 2005-2015, Haiku Inc. All rights reserved.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *              Axel Dörfler, axeld@pinc-software.de
 *              Michael Lotz <mmlr@mlotz.ch>
 */


#include <MessageAdapter.h>
#include <MessagePrivate.h>
#include <MessageUtils.h>

#include <stdlib.h>


namespace BPrivate {

#define R5_MESSAGE_FLAG_VALID                   0x01
#define R5_MESSAGE_FLAG_INCLUDE_TARGET  0x02
#define R5_MESSAGE_FLAG_INCLUDE_REPLY   0x04
#define R5_MESSAGE_FLAG_SCRIPT_MESSAGE  0x08

#define R5_FIELD_FLAG_VALID                             0x01
#define R5_FIELD_FLAG_MINI_DATA                 0x02
#define R5_FIELD_FLAG_FIXED_SIZE                0x04
#define R5_FIELD_FLAG_SINGLE_ITEM               0x08


enum {
        SECTION_MESSAGE_HEADER = 'FOB2',
        SECTION_OFFSET_TABLE = 'STof',
        SECTION_TARGET_INFORMATION = 'ENwh',
        SECTION_SINGLE_ITEM_DATA = 'SGDa',
        SECTION_FIXED_SIZE_ARRAY_DATA = 'FADa',
        SECTION_VARIABLE_SIZE_ARRAY_DATA = 'VADa',
        SECTION_SORTED_INDEX_TABLE = 'DXIn',
        SECTION_END_OF_DATA = 'DDEn'
};


struct r5_message_header {
        uint32  magic;
        uint32  checksum;
        int32   flattened_size;
        int32   what;
        uint8   flags;
} _PACKED;


struct dano_section_header {
        uint32          code;
        int32           size;
        uint8           data[0];
} _PACKED;


struct dano_message_header {
        int32           what;
        int32           padding;
} _PACKED;


typedef struct offset_table_s {
        int32           indexTable;
        int32           endOfData;
        int64           padding;
} OffsetTable;


struct dano_single_item {
        type_code       type;
        int32           item_size;
        uint8           name_length;
        char            name[0];
} _PACKED;


struct dano_fixed_size_array {
        type_code       type;
        int32           size_per_item;
        uint8           name_length;
        char            name[0];
} _PACKED;


struct dano_variable_size_array {
        type_code       type;
        int32           padding;
        uint8           name_length;
        char            name[0];
} _PACKED;


inline int32
pad_to_8(int32 value)
{
        return (value + 7) & ~7;
}


/*static*/ ssize_t
MessageAdapter::FlattenedSize(uint32 format, const BMessage *from)
{
        switch (format) {
                case MESSAGE_FORMAT_R5:
                case MESSAGE_FORMAT_R5_SWAPPED:
                        return _R5FlattenedSize(from);
        }

        return -1;
}


/*static*/ status_t
MessageAdapter::Flatten(uint32 format, const BMessage *from, char *buffer,
        ssize_t *size)
{
        switch (format) {
                case MESSAGE_FORMAT_R5:
                case MESSAGE_FORMAT_R5_SWAPPED:
                        return _FlattenR5Message(format, from, buffer, size);
        }

        return B_ERROR;
}


/*static*/ status_t
MessageAdapter::Flatten(uint32 format, const BMessage *from, BDataIO *stream,
        ssize_t *size)
{
        switch (format) {
                case MESSAGE_FORMAT_R5:
                case MESSAGE_FORMAT_R5_SWAPPED:
                {
                        ssize_t flattenedSize = _R5FlattenedSize(from);
                        char *buffer = (char *)malloc(flattenedSize);
                        if (!buffer)
                                return B_NO_MEMORY;

                        status_t result = _FlattenR5Message(format, from, buffer,
                                &flattenedSize);
                        if (result < B_OK) {
                                free(buffer);
                                return result;
                        }

                        ssize_t written = stream->Write(buffer, flattenedSize);
                        if (written != flattenedSize) {
                                free(buffer);
                                return (written >= 0 ? B_ERROR : written);
                        }

                        if (size)
                                *size = flattenedSize;

                        free(buffer);
                        return B_OK;
                }
        }

        return B_ERROR;
}


/*static*/ status_t
MessageAdapter::Unflatten(uint32 format, BMessage *into, const char *buffer)
{
        if (format == KMessage::kMessageHeaderMagic) {
                KMessage message;
                status_t result = message.SetTo(buffer,
                        ((KMessage::Header *)buffer)->size);
                if (result != B_OK)
                        return result;

                return _ConvertFromKMessage(&message, into);
        }

        try {
                switch (format) {
                        case MESSAGE_FORMAT_R5:
                        {
                                r5_message_header *header = (r5_message_header *)buffer;
                                BMemoryIO stream(buffer + sizeof(uint32),
                                        header->flattened_size - sizeof(uint32));
                                return _UnflattenR5Message(format, into, &stream);
                        }

                        case MESSAGE_FORMAT_R5_SWAPPED:
                        {
                                r5_message_header *header = (r5_message_header *)buffer;
                                BMemoryIO stream(buffer + sizeof(uint32),
                                        __swap_int32(header->flattened_size) - sizeof(uint32));
                                return _UnflattenR5Message(format, into, &stream);
                        }

                        case MESSAGE_FORMAT_DANO:
                        case MESSAGE_FORMAT_DANO_SWAPPED:
                        {
                                dano_section_header *header = (dano_section_header *)buffer;
                                ssize_t size = header->size;
                                if (header->code == MESSAGE_FORMAT_DANO_SWAPPED)
                                        size = __swap_int32(size);

                                BMemoryIO stream(buffer + sizeof(uint32), size - sizeof(uint32));
                                return _UnflattenDanoMessage(format, into, &stream);
                        }
                }
        } catch (status_t error) {
                into->MakeEmpty();
                return error;
        }

        return B_NOT_A_MESSAGE;
}


/*static*/ status_t
MessageAdapter::Unflatten(uint32 format, BMessage *into, BDataIO *stream)
{
        try {
                switch (format) {
                        case MESSAGE_FORMAT_R5:
                        case MESSAGE_FORMAT_R5_SWAPPED:
                                return _UnflattenR5Message(format, into, stream);

                        case MESSAGE_FORMAT_DANO:
                        case MESSAGE_FORMAT_DANO_SWAPPED:
                                return _UnflattenDanoMessage(format, into, stream);
                }
        } catch (status_t error) {
                into->MakeEmpty();
                return error;
        }

        return B_NOT_A_MESSAGE;
}


/*static*/ status_t
MessageAdapter::ConvertToKMessage(const BMessage* from, KMessage& to)
{
        if (from == NULL)
                return B_BAD_VALUE;

        BMessage::Private fromPrivate(const_cast<BMessage*>(from));
        BMessage::message_header* header = fromPrivate.GetMessageHeader();
        uint8* data = fromPrivate.GetMessageData();

        // Iterate through the fields and import them in the target message
        BMessage::field_header* field = fromPrivate.GetMessageFields();
        for (uint32 i = 0; i < header->field_count; i++, field++) {
                const char* name = (const char*)data + field->offset;
                const uint8* fieldData = data + field->offset + field->name_length;
                bool fixedSize = (field->flags & FIELD_FLAG_FIXED_SIZE) != 0;

                if (fixedSize) {
                        status_t status = to.AddArray(name, field->type, fieldData,
                                field->data_size / field->count, field->count);
                        if (status != B_OK)
                                return status;
                } else {
                        for (uint32 i = 0; i < field->count; i++) {
                                uint32 itemSize = *(uint32*)fieldData;
                                fieldData += sizeof(uint32);
                                status_t status = to.AddData(name, field->type, fieldData,
                                        itemSize, false);
                                if (status != B_OK)
                                        return status;
                                fieldData += itemSize;
                        }
                }
        }
        return B_OK;
}


/*static*/ status_t
MessageAdapter::_ConvertFromKMessage(const KMessage *fromMessage,
        BMessage *toMessage)
{
        if (!fromMessage || !toMessage)
                return B_BAD_VALUE;

        // make empty and init what of the target message
        toMessage->MakeEmpty();
        toMessage->what = fromMessage->What();

        BMessage::Private toPrivate(toMessage);
        toPrivate.SetTarget(fromMessage->TargetToken());
        toPrivate.SetReply(B_SYSTEM_TEAM, fromMessage->ReplyPort(),
                fromMessage->ReplyToken());
        if (fromMessage->ReplyPort() >= 0) {
                toPrivate.GetMessageHeader()->flags |= MESSAGE_FLAG_REPLY_AS_KMESSAGE
                        | MESSAGE_FLAG_REPLY_REQUIRED;
        }

        // Iterate through the fields and import them in the target message
        KMessageField field;
        while (fromMessage->GetNextField(&field) == B_OK) {
                int32 elementCount = field.CountElements();
                if (elementCount > 0) {
                        for (int32 i = 0; i < elementCount; i++) {
                                int32 size;
                                const void *data = field.ElementAt(i, &size);
                                status_t result;

                                if (field.TypeCode() == B_MESSAGE_TYPE) {
                                        // message type: if it's a KMessage, convert it
                                        KMessage message;
                                        if (message.SetTo(data, size) == B_OK) {
                                                BMessage bMessage;
                                                result = _ConvertFromKMessage(&message, &bMessage);
                                                if (result < B_OK)
                                                        return result;

                                                result = toMessage->AddMessage(field.Name(), &bMessage);
                                        } else {
                                                // just add it
                                                result = toMessage->AddData(field.Name(),
                                                        field.TypeCode(), data, size,
                                                        field.HasFixedElementSize(), 1);
                                        }
                                } else {
                                        result = toMessage->AddData(field.Name(), field.TypeCode(),
                                                data, size, field.HasFixedElementSize(), 1);
                                }

                                if (result < B_OK)
                                        return result;
                        }
                }
        }

        return B_OK;
}


/*static*/ ssize_t
MessageAdapter::_R5FlattenedSize(const BMessage *from)
{
        BMessage::Private messagePrivate((BMessage *)from);
        BMessage::message_header* header = messagePrivate.GetMessageHeader();

        // header size (variable, depending on the flags)

        ssize_t flattenedSize = sizeof(r5_message_header);

        if (header->target != B_NULL_TOKEN)
                flattenedSize += sizeof(int32);

        if (header->reply_port >= 0 && header->reply_target != B_NULL_TOKEN
                && header->reply_team >= 0) {
                // reply info + big flags
                flattenedSize += sizeof(port_id) + sizeof(int32) + sizeof(team_id) + 4;
        }

        // field size

        uint8 *data = messagePrivate.GetMessageData();
        BMessage::field_header *field = messagePrivate.GetMessageFields();
        for (uint32 i = 0; i < header->field_count; i++, field++) {
                // flags and type
                flattenedSize += 1 + sizeof(type_code);

#if 0
                bool miniData = field->dataSize <= 255 && field->count <= 255;
#else
                // TODO: we don't know the R5 dataSize yet (padding)
                bool miniData = false;
#endif

                // item count
                if (field->count > 1)
                        flattenedSize += (miniData ? sizeof(uint8) : sizeof(uint32));

                // data size
                flattenedSize += (miniData ? sizeof(uint8) : sizeof(size_t));

                // name length and name
                flattenedSize += 1 + min_c(field->name_length - 1, 255);

                // data
                if (field->flags & FIELD_FLAG_FIXED_SIZE)
                        flattenedSize += field->data_size;
                else {
                        uint8 *source = data + field->offset + field->name_length;

                        for (uint32 i = 0; i < field->count; i++) {
                                ssize_t itemSize = *(ssize_t *)source + sizeof(ssize_t);
                                flattenedSize += pad_to_8(itemSize);
                                source += itemSize;
                        }
                }
        }

        // pseudo field with flags 0
        return flattenedSize + 1;
}


/*static*/ status_t
MessageAdapter::_FlattenR5Message(uint32 format, const BMessage *from,
        char *buffer, ssize_t *size)
{
        BMessage::Private messagePrivate((BMessage *)from);
        BMessage::message_header *header = messagePrivate.GetMessageHeader();
        uint8 *data = messagePrivate.GetMessageData();

        r5_message_header *r5header = (r5_message_header *)buffer;
        uint8 *pointer = (uint8 *)buffer + sizeof(r5_message_header);

        r5header->magic = MESSAGE_FORMAT_R5;
        r5header->what = from->what;
        r5header->checksum = 0;

        uint8 flags = R5_MESSAGE_FLAG_VALID;
        if (header->target != B_NULL_TOKEN) {
                *(int32 *)pointer = header->target;
                pointer += sizeof(int32);
                flags |= R5_MESSAGE_FLAG_INCLUDE_TARGET;
        }

        if (header->reply_port >= 0 && header->reply_target != B_NULL_TOKEN
                && header->reply_team >= 0) {
                // reply info
                *(port_id *)pointer = header->reply_port;
                pointer += sizeof(port_id);
                *(int32 *)pointer = header->reply_target;
                pointer += sizeof(int32);
                *(team_id *)pointer = header->reply_team;
                pointer += sizeof(team_id);

                // big flags
                *pointer = (header->reply_target == B_PREFERRED_TOKEN ? 1 : 0);
                pointer++;

                *pointer = (header->flags & MESSAGE_FLAG_REPLY_REQUIRED ? 1 : 0);
                pointer++;

                *pointer = (header->flags & MESSAGE_FLAG_REPLY_DONE ? 1 : 0);
                pointer++;

                *pointer = (header->flags & MESSAGE_FLAG_IS_REPLY ? 1 : 0);
                pointer++;

                flags |= R5_MESSAGE_FLAG_INCLUDE_REPLY;
        }

        if (header->flags & MESSAGE_FLAG_HAS_SPECIFIERS)
                flags |= R5_MESSAGE_FLAG_SCRIPT_MESSAGE;

        r5header->flags = flags;

        // store the header size - used for the checksum later
        ssize_t headerSize = (addr_t)pointer - (addr_t)buffer;

        // collect and add the data
        BMessage::field_header *field = messagePrivate.GetMessageFields();
        for (uint32 i = 0; i < header->field_count; i++, field++) {
                flags = R5_FIELD_FLAG_VALID;

                if (field->count == 1)
                        flags |= R5_FIELD_FLAG_SINGLE_ITEM;
                // TODO: we don't really know the data size now (padding missing)
//              if (field->data_size <= 255 && field->count <= 255)
//                      flags |= R5_FIELD_FLAG_MINI_DATA;
                if (field->flags & FIELD_FLAG_FIXED_SIZE)
                        flags |= R5_FIELD_FLAG_FIXED_SIZE;

                *pointer = flags;
                pointer++;

                *(type_code *)pointer = field->type;
                pointer += sizeof(type_code);

                if (!(flags & R5_FIELD_FLAG_SINGLE_ITEM)) {
                        if (flags & R5_FIELD_FLAG_MINI_DATA) {
                                *pointer = (uint8)field->count;
                                pointer++;
                        } else {
                                *(int32 *)pointer = field->count;
                                pointer += sizeof(int32);
                        }
                }

                // we may have to adjust this to account for padding later
                uint8 *fieldSize = pointer;
                if (flags & R5_FIELD_FLAG_MINI_DATA) {
                        *pointer = (uint8)field->data_size;
                        pointer++;
                } else {
                        *(ssize_t *)pointer = field->data_size;
                        pointer += sizeof(ssize_t);
                }

                // name
                int32 nameLength = min_c(field->name_length - 1, 255);
                *pointer = (uint8)nameLength;
                pointer++;

                strncpy((char *)pointer, (char *)data + field->offset, nameLength);
                pointer += nameLength;

                // data
                uint8 *source = data + field->offset + field->name_length;
                if (flags & R5_FIELD_FLAG_FIXED_SIZE) {
                        memcpy(pointer, source, field->data_size);
                        pointer += field->data_size;
                } else {
                        uint8 *previous = pointer;
                        for (uint32 i = 0; i < field->count; i++) {
                                ssize_t itemSize = *(ssize_t *)source + sizeof(ssize_t);
                                memcpy(pointer, source, itemSize);
                                ssize_t paddedSize = pad_to_8(itemSize);
                                memset(pointer + itemSize, 0, paddedSize - itemSize);
                                pointer += paddedSize;
                                source += itemSize;
                        }

                        // adjust the field size to the padded value
                        if (flags & R5_FIELD_FLAG_MINI_DATA)
                                *fieldSize = (uint8)(pointer - previous);
                        else
                                *(ssize_t *)fieldSize = (pointer - previous);
                }
        }

        // terminate the fields with a pseudo field with flags 0 (not valid)
        *pointer = 0;
        pointer++;

        // calculate the flattened size from the pointers
        r5header->flattened_size = (addr_t)pointer - (addr_t)buffer;
        r5header->checksum = CalculateChecksum((uint8 *)(buffer + 8),
                headerSize - 8);

        if (size)
                *size = r5header->flattened_size;

        return B_OK;
}


/*static*/ status_t
MessageAdapter::_UnflattenR5Message(uint32 format, BMessage *into,
        BDataIO *stream)
{
        into->MakeEmpty();

        BMessage::Private messagePrivate(into);
        BMessage::message_header *header = messagePrivate.GetMessageHeader();

        TReadHelper reader(stream);
        if (format == MESSAGE_FORMAT_R5_SWAPPED)
                reader.SetSwap(true);

        // the stream is already advanced by the size of the "format"
        r5_message_header r5header;
        reader(((uint8 *)&r5header) + sizeof(uint32),
                sizeof(r5header) - sizeof(uint32));

        header->what = into->what = r5header.what;
        if (r5header.flags & R5_MESSAGE_FLAG_INCLUDE_TARGET)
                reader(&header->target, sizeof(header->target));

        if (r5header.flags & R5_MESSAGE_FLAG_INCLUDE_REPLY) {
                // reply info
                reader(&header->reply_port, sizeof(header->reply_port));
                reader(&header->reply_target, sizeof(header->reply_target));
                reader(&header->reply_team, sizeof(header->reply_team));

                // big flags
                uint8 bigFlag;
                reader(bigFlag);
                if (bigFlag)
                        header->reply_target = B_PREFERRED_TOKEN;

                reader(bigFlag);
                if (bigFlag)
                        header->flags |= MESSAGE_FLAG_REPLY_REQUIRED;

                reader(bigFlag);
                if (bigFlag)
                        header->flags |= MESSAGE_FLAG_REPLY_DONE;

                reader(bigFlag);
                if (bigFlag)
                        header->flags |= MESSAGE_FLAG_IS_REPLY;
        }

        if (r5header.flags & R5_MESSAGE_FLAG_SCRIPT_MESSAGE)
                header->flags |= MESSAGE_FLAG_HAS_SPECIFIERS;

        uint8 flags;
        reader(flags);
        while ((flags & R5_FIELD_FLAG_VALID) != 0) {
                bool fixedSize = flags & R5_FIELD_FLAG_FIXED_SIZE;
                bool miniData = flags & R5_FIELD_FLAG_MINI_DATA;
                bool singleItem = flags & R5_FIELD_FLAG_SINGLE_ITEM;

                type_code type;
                reader(type);

                int32 itemCount;
                if (!singleItem) {
                        if (miniData) {
                                uint8 miniCount;
                                reader(miniCount);
                                itemCount = miniCount;
                        } else
                                reader(itemCount);
                } else
                        itemCount = 1;

                int32 dataSize;
                if (miniData) {
                        uint8 miniSize;
                        reader(miniSize);
                        dataSize = miniSize;
                } else
                        reader(dataSize);

                if (dataSize <= 0)
                        return B_ERROR;

                // name
                uint8 nameLength;
                reader(nameLength);

                char nameBuffer[256];
                reader(nameBuffer, nameLength);
                nameBuffer[nameLength] = '\0';

                uint8 *buffer = (uint8 *)malloc(dataSize);
                uint8 *pointer = buffer;
                reader(buffer, dataSize);

                status_t result = B_OK;
                int32 itemSize = 0;
                if (fixedSize)
                        itemSize = dataSize / itemCount;

                if (format == MESSAGE_FORMAT_R5) {
                        for (int32 i = 0; i < itemCount; i++) {
                                if (!fixedSize) {
                                        itemSize = *(int32 *)pointer;
                                        pointer += sizeof(int32);
                                }

                                result = into->AddData(nameBuffer, type, pointer, itemSize,
                                        fixedSize, itemCount);

                                if (result < B_OK) {
                                        free(buffer);
                                        return result;
                                }

                                if (fixedSize)
                                        pointer += itemSize;
                                else {
                                        pointer += pad_to_8(itemSize + sizeof(int32))
                                                - sizeof(int32);
                                }
                        }
                } else {
                        for (int32 i = 0; i < itemCount; i++) {
                                if (!fixedSize) {
                                        itemSize = __swap_int32(*(int32 *)pointer);
                                        pointer += sizeof(int32);
                                }

                                swap_data(type, pointer, itemSize, B_SWAP_ALWAYS);
                                result = into->AddData(nameBuffer, type, pointer, itemSize,
                                        fixedSize, itemCount);

                                if (result < B_OK) {
                                        free(buffer);
                                        return result;
                                }

                                if (fixedSize)
                                        pointer += itemSize;
                                else {
                                        pointer += pad_to_8(itemSize + sizeof(int32))
                                                - sizeof(int32);
                                }
                        }
                }

                free(buffer);

                // flags of next field or termination byte
                reader(flags);
        }

        return B_OK;
}


/*static*/ status_t
MessageAdapter::_UnflattenDanoMessage(uint32 format, BMessage *into,
        BDataIO *stream)
{
        into->MakeEmpty();

        TReadHelper reader(stream);
        if (format == MESSAGE_FORMAT_DANO_SWAPPED)
                reader.SetSwap(true);

        ssize_t size;
        reader(size);

        dano_message_header header;
        reader(header);
        into->what = header.what;

        size -= sizeof(dano_section_header) + sizeof(dano_message_header);
        int32 offset = 0;

        while (offset < size) {
                dano_section_header sectionHeader;
                reader(sectionHeader);

                // be safe. this shouldn't be necessary but in some testcases it was.
                sectionHeader.size = pad_to_8(sectionHeader.size);

                if (offset + sectionHeader.size > size || sectionHeader.size < 0)
                        return B_BAD_DATA;

                ssize_t fieldSize = sectionHeader.size - sizeof(dano_section_header);
                uint8 *fieldBuffer = NULL;
                if (fieldSize <= 0) {
                        // there may be no data. we shouldn't fail because of that
                        offset += sectionHeader.size;
                        continue;
                }

                fieldBuffer = (uint8 *)malloc(fieldSize);
                if (fieldBuffer == NULL)
                        throw (status_t)B_NO_MEMORY;

                reader(fieldBuffer, fieldSize);

                switch (sectionHeader.code) {
                        case SECTION_OFFSET_TABLE:
                        case SECTION_TARGET_INFORMATION:
                        case SECTION_SORTED_INDEX_TABLE:
                        case SECTION_END_OF_DATA:
                                // discard
                                break;

                        case SECTION_SINGLE_ITEM_DATA:
                        {
                                dano_single_item *field = (dano_single_item *)fieldBuffer;

                                int32 dataOffset = sizeof(dano_single_item)
                                        + field->name_length + 1;
                                dataOffset = pad_to_8(dataOffset);

                                if (offset + dataOffset + field->item_size > size)
                                        return B_BAD_DATA;

                                // support for fixed size is not possible with a single item
                                bool fixedSize = false;
                                switch (field->type) {
                                        case B_RECT_TYPE:
                                        case B_POINT_TYPE:
                                        case B_INT8_TYPE:
                                        case B_INT16_TYPE:
                                        case B_INT32_TYPE:
                                        case B_INT64_TYPE:
                                        case B_BOOL_TYPE:
                                        case B_FLOAT_TYPE:
                                        case B_DOUBLE_TYPE:
                                        case B_POINTER_TYPE:
                                        case B_MESSENGER_TYPE:
                                                fixedSize = true;
                                                break;
                                        default:
                                                break;
                                }

                                status_t result = into->AddData(field->name, field->type,
                                        fieldBuffer + dataOffset, field->item_size, fixedSize);

                                if (result != B_OK) {
                                        free(fieldBuffer);
                                        throw result;
                                }
                                break;
                        }

                        case SECTION_FIXED_SIZE_ARRAY_DATA: {
                                dano_fixed_size_array *field
                                        = (dano_fixed_size_array *)fieldBuffer;

                                int32 dataOffset = sizeof(dano_fixed_size_array)
                                        + field->name_length + 1;
                                dataOffset = pad_to_8(dataOffset);
                                int32 count = *(int32 *)(fieldBuffer + dataOffset);
                                dataOffset += 8; /* count and padding */

                                if (offset + dataOffset + count * field->size_per_item > size)
                                        return B_BAD_DATA;

                                status_t result = B_OK;
                                for (int32 i = 0; i < count; i++) {
                                        result = into->AddData(field->name, field->type,
                                                fieldBuffer + dataOffset, field->size_per_item, true,
                                                count);

                                        if (result != B_OK) {
                                                free(fieldBuffer);
                                                throw result;
                                        }

                                        dataOffset += field->size_per_item;
                                }
                                break;
                        }

                        case SECTION_VARIABLE_SIZE_ARRAY_DATA: {
                                dano_variable_size_array *field
                                        = (dano_variable_size_array *)fieldBuffer;

                                int32 dataOffset = sizeof(dano_variable_size_array)
                                        + field->name_length + 1;
                                dataOffset = pad_to_8(dataOffset);
                                int32 count = *(int32 *)(fieldBuffer + dataOffset);
                                dataOffset += sizeof(int32);
                                ssize_t totalSize = *(ssize_t *)(fieldBuffer + dataOffset);
                                dataOffset += sizeof(ssize_t);

                                int32 *endPoints = (int32 *)(fieldBuffer + dataOffset
                                        + totalSize);

                                status_t result = B_OK;
                                for (int32 i = 0; i < count; i++) {
                                        int32 itemOffset = (i > 0 ? pad_to_8(endPoints[i - 1]) : 0);

                                        result = into->AddData(field->name, field->type,
                                                fieldBuffer + dataOffset + itemOffset,
                                                endPoints[i] - itemOffset, false, count);

                                        if (result != B_OK) {
                                                free(fieldBuffer);
                                                throw result;
                                        }
                                }
                                break;
                        }
                }

                free(fieldBuffer);
                offset += sectionHeader.size;
        }

        return B_OK;
}


} // namespace BPrivate