xref: /haiku/src/add-ons/kernel/network/stack/ancillary_data.cpp (revision 9a6a20d4689307142a7ed26a1437ba47e244e73f)
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> {
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 
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*
42 create_ancillary_data_container()
43 {
44 	return new(std::nothrow) ancillary_data_container;
45 }
46 
47 
48 void
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
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
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 *
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*
186 next_ancillary_data(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