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(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