Advertisement
Guest User

Untitled

a guest
Feb 8th, 2016
49
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 9.19 KB | None | 0 0
  1. #ifndef DOUBLY_LINKED_LIST_H
  2. #define DOUBLY_LINKED_LIST_H
  3.  
  4. #include <stdlib.h>
  5. #include <assert.h>
  6.  
  7. /* When reading through this code, you may encounter functions not being
  8. * strictly const-correct. This is because I wanted to make const-correctness
  9. * to depict the CONCEPTUAL const-ness, not const-ness due to some random
  10. * implementation-detail.
  11. */
  12.  
  13. /* From this implementation's perspective, this is not a pointer to the data
  14. * but really the data itsself.
  15. */
  16. typedef void* data_t;
  17.  
  18. /* I second that the name "dll" for "doubly-linked list" is ambiguous
  19. * with the Windows DLL (Dynamic Link Library) but I don't think this'll
  20. * lead to any serious confusion.
  21. */
  22.  
  23. typedef struct dll_node {
  24. struct dll_node* prev;
  25. struct dll_node* next;
  26.  
  27. data_t data;
  28. } dll_node_t;
  29.  
  30. typedef int (*cleanup_op_t)(dll_node_t* node);
  31.  
  32. typedef struct dll {
  33. dll_node_t* head;
  34. dll_node_t* tail;
  35.  
  36. cleanup_op_t cleanup_op;
  37.  
  38. size_t size;
  39. } dll_t;
  40.  
  41. /* Initializes a list with zero nodes.
  42. * `list' - The list to initialize
  43. * `cleanup_op' - The operation to apply to every datum when it is
  44. * removed from the list. This includes the destruction of
  45. * the list using `dll_destroy'.
  46. */
  47. void dll_init(dll_t* list, cleanup_op_t cleanup_op);
  48.  
  49. /* Data definition with macros - Linux kernel style. C ain't got no
  50. * constructors - we use ugly macros to make up for that. AFAIK, GCC does
  51. * offer aping constructors using the attribute "constructor" but that's
  52. * compiler-specific and therefore not eligible for usage.
  53. * Use it if you find it useful. The big fat drawback is that the actually
  54. * hidden stuff going on in this macro needs to be exposed for further
  55. * operations. Or, when assigning dll_t*'s, you need to know the type too.
  56. * That completely defeats the purpose of this macro.
  57. * NOTE: LACK OF DO-WHILE LOOP IS ON PURPOSE TO MAKE THE VARIABLE VISIBLE
  58. * IN THE OUTER SCOPE!
  59. */
  60. #define DEFINE_DLL(identifier, cleanup_op)
  61. dll_t identifier;
  62. dll_init(&identifier, (cleanup_op));
  63.  
  64. /* Destructs the list, that is, all its nodes and the data they contain.
  65. * Complexity: O(n)
  66. * `list' - The list to destruct
  67. */
  68. void dll_destroy(dll_t* list);
  69.  
  70. /* The return value indicates whether the traversing function should continue
  71. * or if the required condition has been met. This enables searching for
  72. * items in the list and other, crazy stuff the user of this API can think of.
  73. */
  74. typedef int (*dll_traverse_op_t)(dll_node_t* node);
  75.  
  76. /* Traverses through a list, starting at the head and going forward to the next
  77. * nodes, and performs an operation on every node.
  78. * Complexity: O(n)
  79. * `list' - The list to traverse through
  80. * `op' - The operation to perform
  81. */
  82. void dll_traverse(dll_t* list, dll_traverse_op_t op);
  83.  
  84. /* Traverses through a list, starting at the tail and going backward to the
  85. * previous nodes, and performs an operation on every node.
  86. * Complexity: O(n)
  87. * `list' - The list to traverse through
  88. * `op' - The operation to perform
  89. */
  90. void dll_traverse_rev(dll_t* list, const dll_traverse_op_t op);
  91.  
  92. /* TODO: is it reasonable to return the created node from the dll_add_* and
  93. * dll_insert_* functions? Think about that!
  94. */
  95.  
  96. /* TODO: THE BELOW COMMENT SERVES AS A TEMPLATE FOR ALL DOCUMENTING COMMENTS
  97. * IN THIS IMPLEMENTATION. FORM THE OTHER ONES WITH THIS ONE HERE IN MIND!
  98. */
  99. /* Inserts a node right before another one.
  100. * Complexity: O(1)
  101. * `list' - The list to insert into
  102. * `node' - The node before which to insert the new node
  103. * `data' - The data stored by the new node
  104. * Notes:
  105. * If the list doesn't contain any nodes, a new node is created and added to
  106. * the list, INDEPENDENT OF THE VALUE OF `node'.
  107. */
  108. dll_node_t* dll_insert_before(dll_t* list, dll_node_t* node, data_t data);
  109.  
  110. /* TODO: THE BELOW COMMENT SERVES AS A TEMPLATE FOR ALL DOCUMENTING COMMENTS
  111. * IN THIS IMPLEMENTATION. FORM THE OTHER ONES WITH THIS ONE HERE IN MIND!
  112. */
  113. /* Inserts a node right after another one.
  114. * Complexity: O(1)
  115. * `list' - The list to insert into
  116. * `node' - The node after which to insert the new node
  117. * `data' - The data stored by the new node
  118. * Notes:
  119. * If the list doesn't contain any nodes, a new node is created and added to
  120. * the list, INDEPENDENT OF THE VALUE OF `node'.
  121. */
  122. dll_node_t* dll_insert_after(dll_t* list, dll_node_t* node, data_t data);
  123.  
  124. /* The dll_add_* and dll_insert_* functions are very similar, so I wrote the
  125. * actual, generic implementation in the dll_insert_* functions and built
  126. * the dll_add_* ones on top of them.
  127. * Similarly done with the dll_rm and dll_rm_* functions.
  128. */
  129.  
  130. /* Add a node to the head of the list.
  131. * Complexity: O(1)
  132. * `list' - The list to prepend the node to
  133. * `data' - The data to be stored in the new node
  134. */
  135. dll_node_t* dll_add_front(dll_t* list, data_t data);
  136.  
  137. /* Add a node to the tail of the list.
  138. * Complexity: O(1)
  139. * `list' - The list to append the node to
  140. * `data' - The data to be stored in the new node
  141. */
  142. dll_node_t* dll_add_back(dll_t* list, data_t data);
  143.  
  144. /* Removes a node.
  145. * Complexity: O(1)
  146. * `list' - The list to remove the node from
  147. * `node' - The node to remove
  148. */
  149. void dll_rm(dll_t* list, const dll_node_t* node);
  150.  
  151. /* Remove the node at the head of the list.
  152. * Complexity: O(1)
  153. * `list' - The list to remove the node from
  154. */
  155. void dll_rm_front(dll_t* list);
  156.  
  157. /* Remove the node at the tail of the list.
  158. * Complexity: O(1)
  159. * `list' - The list to remove the node from
  160. */
  161. void dll_rm_back(dll_t* list);
  162.  
  163. #endif
  164.  
  165. #include <dll.h>
  166.  
  167. void dll_init(dll_t* list, cleanup_op_t cleanup_op) {
  168. assert(list);
  169.  
  170. list->head = list->tail = NULL;
  171. list->cleanup_op = cleanup_op;
  172. list->size = 0;
  173. }
  174.  
  175. void dll_destroy(dll_t* list) {
  176. assert(list);
  177.  
  178. if (list->size == 0)
  179. return;
  180.  
  181. dll_node_t* it = list->head;
  182.  
  183. while (it) {
  184. if (list->cleanup_op) /* fare well, branch predictor */
  185. list->cleanup_op(it->data);
  186.  
  187. dll_node_t* tmp = it;
  188. it = it->next;
  189. free(tmp);
  190. }
  191. }
  192.  
  193. void dll_traverse(dll_t* list, dll_traverse_op_t op) {
  194. assert(list);
  195.  
  196. if (list->size == 0)
  197. return;
  198.  
  199. dll_node_t* it = list->head;
  200.  
  201. while (it) {
  202. op(it);
  203. it = it->next;
  204. }
  205. }
  206.  
  207. void dll_traverse_rev(dll_t* list, const dll_traverse_op_t op) {
  208. if (list->size == 0)
  209. return;
  210.  
  211. dll_node_t* it = list->tail;
  212.  
  213. while (it) {
  214. op(it);
  215. it = it->prev;
  216. }
  217. }
  218.  
  219. dll_node_t* dll_insert_before(dll_t* list, dll_node_t* node, data_t data) {
  220. assert(list);
  221.  
  222. if (list->size == 0)
  223. assert(node);
  224.  
  225. dll_node_t* prev_node = list->size == 0 ? NULL : node->prev;
  226.  
  227. dll_node_t* new_node = malloc(sizeof(dll_node_t));
  228. assert(new_node);
  229. new_node->data = data;
  230. new_node->next = list->size == 0 ? NULL : node;
  231. new_node->prev = prev_node;
  232.  
  233. if (list->size == 0)
  234. list->head = list->tail = new_node;
  235. else {
  236. if (node->prev)
  237. prev_node->next = new_node;
  238. else {
  239. assert(node == list->head);
  240. list->head = new_node;
  241. }
  242. node->prev = new_node;
  243. }
  244.  
  245. ++(list->size);
  246.  
  247. return new_node;
  248. }
  249.  
  250. dll_node_t* dll_insert_after(dll_t* list, dll_node_t* node, data_t data) {
  251. assert(list);
  252.  
  253. if (list->size != 0)
  254. assert(node);
  255.  
  256. dll_node_t* next_node = list->size == 0 ? NULL : node->next;
  257.  
  258. dll_node_t* new_node = malloc(sizeof(dll_node_t));
  259. assert(new_node);
  260. new_node->data = data;
  261. new_node->next = next_node;
  262. new_node->prev = list->size == 0 ? NULL : node;
  263.  
  264. if (list->size == 0)
  265. list->head = list->tail = new_node;
  266. else {
  267. if (node->next)
  268. next_node->prev = new_node;
  269. else {
  270. assert(node == list->tail);
  271. list->tail = new_node;
  272. }
  273. node->next = new_node;
  274. }
  275.  
  276. ++(list->size);
  277.  
  278. return new_node;
  279. }
  280.  
  281. dll_node_t* dll_add_front(dll_t* list, data_t data) {
  282. return dll_insert_before(list, list->head, data);
  283. }
  284.  
  285. dll_node_t* dll_add_back(dll_t* list, data_t data) {
  286. return dll_insert_after(list, list->tail, data);
  287. }
  288.  
  289. void dll_rm(dll_t* list, const dll_node_t* node) {
  290. assert(list);
  291. assert(node);
  292. assert(list->size != 0);
  293.  
  294. if (list->cleanup_op)
  295. list->cleanup_op(node->data);
  296.  
  297. if (node->prev)
  298. node->prev->next = node->next;
  299. else {
  300. assert(node == list->head);
  301. list->head = node->next;
  302. }
  303. if (node->next)
  304. node->next->prev = node->prev;
  305. else {
  306. assert(node == list->tail);
  307. list->tail = node->prev;
  308. }
  309.  
  310. /* Casting away const-ness is bad but it's just a necessary
  311. * implementation detail here. I hope it's not undefined behavior...
  312. * in C++, it is, if the object the casted pointer is pointing to is
  313. * const, AFAIK. Only if it is non-const but pointed to by a const
  314. * pointer casting away const-ness is well-defined.
  315. * But I think the rules differ in C. (C != C++!)
  316. */
  317. free((dll_node_t*) node);
  318.  
  319. --(list->size);
  320. }
  321.  
  322. void dll_rm_front(dll_t* list) {
  323. dll_rm(list, list->head);
  324. }
  325.  
  326. void dll_rm_back(dll_t* list) {
  327. dll_rm(list, list->tail);
  328. }
  329.  
  330. /* When reading through this code, you may encounter functions not being
  331. * strictly const-correct. This is because I wanted to make const-correctness
  332. * to depict the CONCEPTUAL const-ness, not const-ness due to some random
  333. * implementation-detail. */
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement