1/*
2 * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6#include "ancillary_data.h"
7
8#include <stdlib.h>
9#include <string.h>
10
11#include <new>
12
13#include <util/DoublyLinkedList.h>
14
15
16#define MAX_ANCILLARY_DATA_SIZE	128
17
18
19struct ancillary_data : DoublyLinkedListLinkImpl<ancillary_data> {
20	void* Data()
21	{
22		return (char*)this + _ALIGN(sizeof(ancillary_data));
23	}
24
25	static ancillary_data* FromData(void* data)
26	{
27		return (ancillary_data*)((char*)data - _ALIGN(sizeof(ancillary_data)));
28	}
29
30	ancillary_data_header	header;
31	void (*destructor)(const ancillary_data_header*, void*);
32};
33
34typedef DoublyLinkedList<ancillary_data> ancillary_data_list;
35
36struct ancillary_data_container {
37	ancillary_data_list	data_list;
38};
39
40
41ancillary_data_container*
42create_ancillary_data_container()
43{
44	return new(std::nothrow) ancillary_data_container;
45}
46
47
48void
49delete_ancillary_data_container(ancillary_data_container* container)
50{
51	if (container == NULL)
52		return;
53
54	while (ancillary_data* data = container->data_list.RemoveHead()) {
55		if (data->destructor != NULL)
56			data->destructor(&data->header, data->Data());
57		free(data);
58	}
59}
60
61
62/*!
63	Adds ancillary data to the given container.
64
65	\param container The container.
66	\param header Description of the data.
67	\param data If not \c NULL, the data are copied into the allocated storage.
68	\param destructor If not \c NULL, this function will be invoked with the
69		data as parameter when the container is destroyed.
70	\param _allocatedData Will be set to the storage allocated for the data.
71	\return \c B_OK when everything goes well, another error code otherwise.
72*/
73status_t
74add_ancillary_data(ancillary_data_container* container,
75	const ancillary_data_header* header, const void* data,
76	void (*destructor)(const ancillary_data_header*, void*),
77	void** _allocatedData)
78{
79	// check parameters
80	if (header == NULL)
81		return B_BAD_VALUE;
82
83	if (header->len > MAX_ANCILLARY_DATA_SIZE)
84		return ENOBUFS;
85
86	// allocate buffer
87	void *dataBuffer = malloc(_ALIGN(sizeof(ancillary_data)) + header->len);
88	if (dataBuffer == NULL)
89		return B_NO_MEMORY;
90
91	// init and attach the structure
92	ancillary_data *ancillaryData = new(dataBuffer) ancillary_data;
93	ancillaryData->header = *header;
94	ancillaryData->destructor = destructor;
95
96	container->data_list.Add(ancillaryData);
97
98	if (data != NULL)
99		memcpy(ancillaryData->Data(), data, header->len);
100
101	if (_allocatedData != NULL)
102		*_allocatedData = ancillaryData->Data();
103
104	return B_OK;
105}
106
107
108/*!
109	Removes ancillary data from the given container. The associated memory is
110	freed, i.e. the \a data pointer must no longer be used after calling this
111	function. Depending on \a destroy, the destructor is invoked before freeing
112	the data.
113
114	\param container The container.
115	\param data Pointer to the data to be removed (as returned by
116		add_ancillary_data() or next_ancillary_data()).
117	\param destroy If \c true, the destructor, if one was passed to
118		add_ancillary_data(), is invoked for the data.
119	\return \c B_OK when everything goes well, another error code otherwise.
120*/
121status_t
122remove_ancillary_data(ancillary_data_container* container, void* data,
123	bool destroy)
124{
125	if (data == NULL)
126		return B_BAD_VALUE;
127
128	ancillary_data *ancillaryData = ancillary_data::FromData(data);
129
130	container->data_list.Remove(ancillaryData);
131
132	if (destroy && ancillaryData->destructor != NULL) {
133		ancillaryData->destructor(&ancillaryData->header,
134			ancillaryData->Data());
135	}
136
137	free(ancillaryData);
138
139	return B_OK;
140}
141
142
143/*!
144	Moves all ancillary data from container \c from to the end of the list of
145	ancillary data of container \c to.
146
147	\param from The container from which to remove the ancillary data.
148	\param to The container to which to add the ancillary data.
149	\return A pointer to the first of the moved ancillary data, if any, \c NULL
150		otherwise.
151*/
152void *
153move_ancillary_data(ancillary_data_container* from,
154	ancillary_data_container* to)
155{
156	if (from == NULL || to == NULL)
157		return NULL;
158
159	ancillary_data *ancillaryData = from->data_list.Head();
160	to->data_list.MoveFrom(&from->data_list);
161
162	return ancillaryData != NULL ? ancillaryData->Data() : NULL;
163}
164
165
166/*!
167	Returns the next ancillary data. When iterating through the data, initially
168	a \c NULL pointer shall be passed as \a previousData, subsequently the
169	previously returned data pointer. After the last item, \c NULL is returned.
170
171	Note, that it is not safe to call remove_ancillary_data() for a data item
172	and then pass that pointer to this function. First get the next item, then
173	remove the previous one.
174
175	\param container The container.
176	\param previousData The pointer to the previous data returned by this
177		function. Initially \c NULL shall be passed.
178	\param header Pointer to allocated storage into which the data description
179		is written. May be \c NULL.
180	\return A pointer to the next ancillary data in the container. \c NULL after
181		the last one.
182*/
183void*
184next_ancillary_data(ancillary_data_container* container, void* previousData,
185	ancillary_data_header* _header)
186{
187	ancillary_data *ancillaryData;
188
189	if (previousData == NULL) {
190		ancillaryData = container->data_list.Head();
191	} else {
192		ancillaryData = ancillary_data::FromData(previousData);
193		ancillaryData = container->data_list.GetNext(ancillaryData);
194	}
195
196	if (ancillaryData == NULL)
197		return NULL;
198
199	if (_header != NULL)
200		*_header = ancillaryData->header;
201
202	return ancillaryData->Data();
203}
204