xref: /haiku/src/add-ons/kernel/network/stack/ancillary_data.cpp (revision 1e60bdeab63fa7a57bc9a55b032052e95a18bd2c)
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 
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 
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 */
73 status_t
74 add_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 */
121 status_t
122 remove_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 */
152 void *
153 move_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 */
183 void*
184 next_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