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