mem.c 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. /* Memory handling, malloc wrapers, etc */
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include <stdio.h>
  5. #include "mem.h"
  6. /* Calls exit(EXIT_FAILURE) if ptr is NULL. Is message is not null, prints this on stderr. */
  7. void * exit_if_null(void * ptr, char * message) {
  8. if(!ptr) {
  9. if(message) {
  10. fprintf(stderr, "%s\n", message);
  11. }
  12. exit(EXIT_FAILURE);
  13. }
  14. return ptr;
  15. }
  16. #define EXIT_ON_NOMEMORY(PTR) exit_if_null(PTR, "Out of memory!")
  17. /** Wrapper for calloc/malloc */
  18. void * mem_calloc(size_t amount, size_t size) {
  19. void * ptr = calloc(amount, size);
  20. return EXIT_ON_NOMEMORY(ptr);
  21. }
  22. /** Wrapper for calloc/malloc */
  23. void * mem_alloc(size_t size) {
  24. void * ptr = mem_calloc(1, size);
  25. return EXIT_ON_NOMEMORY(ptr);
  26. }
  27. /** Wrapper for free */
  28. void * mem_free(void * ptr) {
  29. free(ptr);
  30. return NULL;
  31. }
  32. /** Wrapper for realloc */
  33. void * mem_realloc(void *ptr, size_t newsize) {
  34. void * aid = realloc(ptr, newsize);
  35. return EXIT_ON_NOMEMORY(aid);
  36. }
  37. /** Resizes memory, taking care not to lose ptr* if the reallocation
  38. fails. */
  39. void * mem_resize(void ** ptr, size_t newsize) {
  40. if(*ptr) {
  41. void * res = mem_realloc((*ptr), newsize);
  42. if(res) {
  43. (*ptr) = res;
  44. return res;
  45. }
  46. }
  47. return NULL;
  48. }
  49. /** Wrapper for memmove, for consistency */
  50. void * mem_move(void * dest, void * src, size_t size) {
  51. return memmove(dest , src, size);
  52. }
  53. /* Thoughts on hierarchical data structures.
  54. * Often, in C data is allocatedin a hierarchical way.
  55. * It's useful to have functions that take this into consideration...
  56. */
  57. typedef struct Memtree_ Memtree;
  58. struct Memtree_ {
  59. void * memory;
  60. size_t size;
  61. Memtree * next, * parent, * child;
  62. };
  63. #ifdef COMMENT_
  64. /* Tree memory allocator. */
  65. void * mem_tree_alloc(void * vparent, size_t size) {
  66. char * aid;
  67. Memtree * me, * next;
  68. Memtree * parent = vparent;
  69. aid = mem_calloc(1, size + sizeof(Memtree));
  70. if (!aid) return NULL;
  71. me = (Memtree *) aid;
  72. me->memory = aid + sizeof(Memtree);
  73. me->size = size;
  74. if (parent) {
  75. me->parent = parent;
  76. if (parent->child) {
  77. next = parent->child;
  78. } else {
  79. next = NULL
  80. }
  81. parent->child = me;
  82. me->next = next;
  83. } else {
  84. me->parent = NULL;
  85. me->next = NULL;
  86. me->child = NULL;
  87. }
  88. return me->memory;
  89. }
  90. void mem_tree_free(void * vparent) {
  91. char * aid;
  92. Memtree * me, * next;
  93. Memtree * parent = vparent;
  94. aid = vparent;
  95. if (!aid) return;
  96. me = (Memtree *) (aid + sizeof(Memtree));
  97. if (!me) return;
  98. me->memory = NULL;
  99. me->size = 0;
  100. if (me->child) {
  101. mem_tree_free(me->child);
  102. } else if (me->next) {
  103. for (next = me->next; next; next = next->next) {
  104. mem_tree_free(next);
  105. }
  106. } else {
  107. next = NULL
  108. }
  109. mem_free(me);
  110. }
  111. #endif
  112. /*
  113. * Thoughts on memory allocation in C.
  114. *
  115. * In theory, memory allocations could just use malloc an free and
  116. * that would be enough, however, in practise there are several factors that
  117. * mean that just calling free() on any pointer won't free all allocated
  118. * memory and resources associated to it. In general, when a pointer to
  119. * a resource needs to be cleaned up, there are 3 concens to take care of:
  120. *
  121. * 1) Cleaning up of non-memory resources like OS handles, API haandles,
  122. * FILEs, etc. that must be closed or cleaned up before the pointer/struct
  123. * that refers to them may be deallocated. In general, this could be
  124. * called deinitialization.
  125. *
  126. * 2) The second problem is that the pointer may point to a complex data
  127. * structure that may have been allocated my several mallocs. In this case
  128. * multiple frees are needed. This could be called deconstruction.
  129. *
  130. * 3) Finally the pointer will usually point to allocated memory that must
  131. * be deallocated. For this, free could suffice, but not in all cases.
  132. * This could be deallocation.
  133. *
  134. * These 3 steps are of course mirrored when the resource is created, as can
  135. * be seen in the diagram below.
  136. *
  137. * step state of resource
  138. *
  139. * Before creation: : VOID
  140. *
  141. * /- allocation : ALLOCATED
  142. * Creation {-- construction : CONSTRUCTED
  143. * \- initialization : INITIALIZED
  144. *
  145. * Use of the resource....
  146. *
  147. * /- deinitialization : CONSTRUCTED
  148. * Destruction {-- deconstruction : ALLLOCATED
  149. * \- deallocation : VOID
  150. *
  151. * It should be noticed that with proper care an object that has not been
  152. * intialized, or that merely has been deinitialized (i.e. in the CONSTRUCTED
  153. * state) could be used or reused simply by initializing or reinitializing it.
  154. * It could be useful for performance or practical reasons or to enable such
  155. * recycling of already allocated and constructed resources.
  156. *
  157. * On the other hand a resource that has been deallocated or not allocated
  158. * cannnot be used correctly at all. What then about a resource that has not
  159. * been constructed yet or one that has already been deconstructed? The barrier
  160. * is more fluid and uncrear here. However, in many progaming languages, these
  161. * 3 steps get simplified ito 2 or even 1 step. However, experience shows that
  162. * having only one step is too gross, especially in the face of reference
  163. * counting or on-GC deinitialization. Therefore, it's best to have 2
  164. * steps on construction and 2 steps on deconstruction namely
  165. *
  166. * construction by XXX_new : XXX_build XXX_init.
  167. * destruction by XXX_free : XXX_done XXX_demolish
  168. *
  169. * The rule is that XXX_done and XXX_init should be complementary,
  170. * any resource RES that has XXX_init(XXX_done(RES), params...) called
  171. * will be usable again. Furthemore XXX_done should be idempotent,
  172. * that is XXX_done(XXX_done(RES)), etc is allowed.
  173. * XXX_init need not be idempotent as long as it can undo XXX_done correctly.
  174. * XXX_free is guaranteed to call XXX_done and XXX_demolish.
  175. * XXX_demolish must not neccesarily be exposed on the public API or
  176. * even exist.
  177. * XXX_new calls XXX_build and XXX_init in turn.
  178. *
  179. *
  180. * The problem remains then : how to know which destructor to call?
  181. * In case it's known at compile-time which other destructors will be needed
  182. * it's simple. However, some containers may want to store objects whose type
  183. * is only known at run time. For them it is neccesary to either not accept
  184. * ownership and let some other routine deallocate them) which is
  185. * bothersome, certainy for, say, hash tables, or to properly devise a system
  186. * where the correct destructors can be passed at run time.
  187. *
  188. * And then we arrive at interfaces that use and/or consist of virtual tables,
  189. * that is tablew with function pointers. For the destructor a single pointer
  190. * could suffice, but while we're going virtual, allowing interface/class like
  191. * tables will be better since it alllows more generality and saves space.
  192. *
  193. *
  194. * Another point is what to do if memory allocation fails. While for a critical
  195. * application, it woule make sense to try to free ups some space and try
  196. * again, for non-critical ones like Eruta, the best thing to do is to
  197. * simply end the program, since there's nothing sensible that could be done,
  198. * apart from trying to restore the sceen to it's previous resolution if possible.
  199. *
  200. *
  201. */
  202. /** Thoughts on memory management and garbage collection.
  203. * The probelm in C is that it is not always clear who is the owner
  204. * for an object, and and who is just using a reference.
  205. * If ownership is well defined, then correct cleanup is easier to arrange.
  206. *
  207. * Here's an idea on reference linking for shared ownership:
  208. */
  209. typedef struct Memref_ Memref;
  210. struct Memref_ {
  211. void * pointer;
  212. Memref * next;
  213. Memref * previous;
  214. };
  215. Memref memref(void * pointer) {
  216. Memref ref = { pointer, 0, 0 };
  217. return ref;
  218. }