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