#include "ancillary_data.h"
#include <stdlib.h>
#include <string.h>
#include <new>
#include <util/DoublyLinkedList.h>
#define MAX_ANCILLARY_DATA_SIZE (32 * sizeof(void*))
struct ancillary_data : DoublyLinkedListLinkImpl<ancillary_data> {
void* Data()
{
return (char*)this + _ALIGN(sizeof(ancillary_data));
}
static ancillary_data* FromData(void* data)
{
return (ancillary_data*)((char*)data - _ALIGN(sizeof(ancillary_data)));
}
ancillary_data_header header;
void (*destructor)(const ancillary_data_header*, void*);
void (*clone)(const ancillary_data_header*, void*);
};
typedef DoublyLinkedList<ancillary_data> ancillary_data_list;
struct ancillary_data_container {
ancillary_data_list data_list;
};
ancillary_data_container*
create_ancillary_data_container()
{
return new(std::nothrow) ancillary_data_container;
}
void
delete_ancillary_data_container(ancillary_data_container* container)
{
if (container == NULL)
return;
while (ancillary_data* data = container->data_list.RemoveHead()) {
if (data->destructor != NULL)
data->destructor(&data->header, data->Data());
free(data);
}
delete container;
}
status_t
add_ancillary_data(ancillary_data_container* container,
const ancillary_data_header* header, const void* data,
void (*destructor)(const ancillary_data_header*, void*),
void (*clone)(const ancillary_data_header*, void*),
void** _allocatedData)
{
if (header == NULL)
return B_BAD_VALUE;
if (header->len > MAX_ANCILLARY_DATA_SIZE)
return ENOBUFS;
void *dataBuffer = malloc(_ALIGN(sizeof(ancillary_data)) + header->len);
if (dataBuffer == NULL)
return B_NO_MEMORY;
ancillary_data *ancillaryData = new(dataBuffer) ancillary_data;
ancillaryData->header = *header;
ancillaryData->destructor = destructor;
ancillaryData->clone = clone;
container->data_list.Add(ancillaryData);
if (data != NULL)
memcpy(ancillaryData->Data(), data, header->len);
if (_allocatedData != NULL)
*_allocatedData = ancillaryData->Data();
return B_OK;
}
status_t
remove_ancillary_data(ancillary_data_container* container, void* data,
bool destroy)
{
if (data == NULL)
return B_BAD_VALUE;
ancillary_data *ancillaryData = ancillary_data::FromData(data);
container->data_list.Remove(ancillaryData);
if (destroy && ancillaryData->destructor != NULL) {
ancillaryData->destructor(&ancillaryData->header,
ancillaryData->Data());
}
free(ancillaryData);
return B_OK;
}
void *
move_ancillary_data(ancillary_data_container* from,
ancillary_data_container* to)
{
if (from == NULL || to == NULL)
return NULL;
ancillary_data *ancillaryData = from->data_list.Head();
to->data_list.TakeFrom(&from->data_list);
return ancillaryData != NULL ? ancillaryData->Data() : NULL;
}
void*
next_ancillary_data(const ancillary_data_container* container, void* previousData,
ancillary_data_header* _header)
{
ancillary_data *ancillaryData;
if (previousData == NULL) {
ancillaryData = container->data_list.Head();
} else {
ancillaryData = ancillary_data::FromData(previousData);
ancillaryData = container->data_list.GetNext(ancillaryData);
}
if (ancillaryData == NULL)
return NULL;
if (_header != NULL)
*_header = ancillaryData->header;
return ancillaryData->Data();
}
status_t
clone_ancillary_data(const ancillary_data_container* from,
ancillary_data_container* to)
{
ancillary_data *ancillaryData = from->data_list.Head();
while (ancillaryData != NULL) {
void *dataBuffer = malloc(_ALIGN(sizeof(ancillary_data)) + ancillaryData->header.len);
if (dataBuffer == NULL)
return B_NO_MEMORY;
ancillary_data *clone = new(dataBuffer) ancillary_data;
clone->header = ancillaryData->header;
if (ancillaryData->clone != NULL)
ancillaryData->clone(&ancillaryData->header, ancillaryData->Data());
clone->destructor = ancillaryData->destructor;
clone->clone = ancillaryData->clone;
to->data_list.Add(clone);
if (ancillaryData->Data() != NULL)
memcpy(clone->Data(), ancillaryData->Data(), ancillaryData->header.len);
ancillaryData = from->data_list.GetNext(ancillaryData);
}
return B_OK;
}