├── Makefile ├── README.md ├── halloc.h └── src ├── align.h ├── halloc.c ├── hlist.h └── macros.h /Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS = -I. -ansi -Wall 2 | 3 | LIBNAME = libhalloc.a 4 | OBJS = src/halloc.o 5 | 6 | $(LIBNAME): $(OBJS) 7 | ar rcs $(LIBNAME) $(OBJS) 8 | 9 | install: $(LIBNAME) 10 | cp halloc.h /usr/local/include 11 | cp $(LIBNAME) /usr/local/lib 12 | 13 | clean: 14 | rm -f $(LIBNAME) $(OBJS) 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ============================================================================= 2 | 3 | halloc 1.2.3 4 | 5 | === 6 | 7 | Hierarchical memory allocation interface - an extension 8 | to standard malloc/free interface that simplifies tasks 9 | of memory disposal in cases when allocated structures 10 | exhibit hierarchical properties. 11 | 12 | http://swapped.cc/halloc 13 | 14 | === 15 | 16 | Distributed under terms of the BSD license. 17 | 18 | === 19 | 20 | To build libhalloc.a with GNU tools run 21 | make 22 | 23 | To install in /usr/include and /usr/lib 24 | make install 25 | 26 | To cleanup the build files 27 | make clean 28 | 29 | === 30 | 31 | halloc-1.2.3 32 | 33 | * added an integer overflow checks to halloc() as per 34 | derrek's comment 35 | 36 | * added a multiplication overflow check to h_calloc() 37 | while at it 38 | 39 | * pruned unused, commented out code 40 | 41 | halloc-1.2.2 42 | 43 | * fixed an issue with hlist_relink in halloc when 44 | realloc'ed block is not on a sibling list, as per 45 | github/abergeron comments 46 | 47 | * changed 'make install' from /usr/ to /usr/local/ as per 48 | github/ttilley comment 49 | 50 | halloc-1.2.1 51 | 52 | * fixed a double-free bug in _set_allocator() as per 53 | Matthew Gregan comments 54 | 55 | * switched to using NULL instead of 0 where applicable 56 | 57 | halloc-1.2.0 58 | 59 | * added missing include to halloc.c 60 | 61 | * improved standard compliance thanks to the feedback 62 | received from Stan Tobias. Two things were fixed - 63 | 64 | - hblock_t structure no longer uses zero-sized 'data' 65 | array, which happened to be common, but non-standard 66 | extension; 67 | 68 | - secondly, added the code to test the behaviour of 69 | realloc(ptr, 0). Standard allows it NOT to act as 70 | free(), in which case halloc will use its own version 71 | of allocator calling free() when neccessary. 72 | 73 | halloc-1.1.0 74 | 75 | * initial public release (rewrite of hhmalloc library) 76 | 77 | ============================================================================= 78 | Copyright (c) 2004-2011, Alex Pankratov (ap@swapped.cc). All rights reserved. 79 | -------------------------------------------------------------------------------- /halloc.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of Hierarchical Allocator library. 3 | * Copyright (c) 2004-2011 Alex Pankratov. All rights reserved. 4 | * 5 | * http://swapped.cc/halloc 6 | */ 7 | 8 | /* 9 | * The program is distributed under terms of BSD license. 10 | * You can obtain the copy of the license by visiting: 11 | * 12 | * http://www.opensource.org/licenses/bsd-license.php 13 | */ 14 | 15 | #ifndef _LIBP_HALLOC_H_ 16 | #define _LIBP_HALLOC_H_ 17 | 18 | #include /* size_t */ 19 | 20 | /* 21 | * Core API 22 | */ 23 | void * halloc (void * block, size_t len); 24 | void hattach(void * block, void * parent); 25 | 26 | /* 27 | * standard malloc/free api 28 | */ 29 | void * h_malloc (size_t len); 30 | void * h_calloc (size_t n, size_t len); 31 | void * h_realloc(void * p, size_t len); 32 | void h_free (void * p); 33 | char * h_strdup (const char * str); 34 | 35 | /* 36 | * the underlying allocator 37 | */ 38 | typedef void * (* realloc_t)(void * ptr, size_t len); 39 | 40 | extern realloc_t halloc_allocator; 41 | 42 | #endif 43 | 44 | -------------------------------------------------------------------------------- /src/align.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of Hierarchical Allocator library. 3 | * Copyright (c) 2004-2011 Alex Pankratov. All rights reserved. 4 | * 5 | * http://swapped.cc/halloc 6 | */ 7 | 8 | /* 9 | * The program is distributed under terms of BSD license. 10 | * You can obtain the copy of the license by visiting: 11 | * 12 | * http://www.opensource.org/licenses/bsd-license.php 13 | */ 14 | 15 | #ifndef _LIBP_ALIGN_H_ 16 | #define _LIBP_ALIGN_H_ 17 | 18 | /* 19 | * a type with the most strict alignment requirements 20 | */ 21 | union max_align 22 | { 23 | char c; 24 | short s; 25 | long l; 26 | int i; 27 | float f; 28 | double d; 29 | void * v; 30 | void (*q)(void); 31 | }; 32 | 33 | typedef union max_align max_align_t; 34 | 35 | #endif 36 | 37 | -------------------------------------------------------------------------------- /src/halloc.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of Hierarchical Allocator library. 3 | * Copyright (c) 2004-2011 Alex Pankratov. All rights reserved. 4 | * 5 | * http://swapped.cc/halloc 6 | */ 7 | 8 | /* 9 | * The program is distributed under terms of BSD license. 10 | * You can obtain the copy of the license by visiting: 11 | * 12 | * http://www.opensource.org/licenses/bsd-license.php 13 | */ 14 | 15 | #include /* realloc */ 16 | #include /* memset & co */ 17 | 18 | #include "halloc.h" 19 | #include "align.h" 20 | #include "hlist.h" 21 | 22 | /* 23 | * block control header 24 | */ 25 | typedef struct hblock 26 | { 27 | #ifndef NDEBUG 28 | #define HH_MAGIC 0x20040518L 29 | long magic; 30 | #endif 31 | hlist_item_t siblings; /* 2 pointers */ 32 | hlist_head_t children; /* 1 pointer */ 33 | max_align_t data[1]; /* not allocated, see below */ 34 | 35 | } hblock_t; 36 | 37 | #define sizeof_hblock offsetof(hblock_t, data) 38 | 39 | /* 40 | * 41 | */ 42 | realloc_t halloc_allocator = NULL; 43 | 44 | #define allocator halloc_allocator 45 | 46 | /* 47 | * static methods 48 | */ 49 | static int _ok_to_multiply(size_t a, size_t b); 50 | 51 | static void _set_allocator(void); 52 | static void * _realloc(void * ptr, size_t n); 53 | 54 | static int _relate(hblock_t * b, hblock_t * p); 55 | static void _free_children(hblock_t * p); 56 | 57 | /* 58 | * Core API 59 | */ 60 | void * halloc(void * ptr, size_t len) 61 | { 62 | hblock_t * p; 63 | 64 | /* set up default allocator */ 65 | if (! allocator) 66 | { 67 | _set_allocator(); 68 | assert(allocator); 69 | } 70 | 71 | /* a quick overflow check */ 72 | if (len + sizeof_hblock < sizeof_hblock) 73 | return NULL; 74 | 75 | /* calloc */ 76 | if (! ptr) 77 | { 78 | if (! len) 79 | return NULL; 80 | 81 | p = allocator(0, len + sizeof_hblock); 82 | if (! p) 83 | return NULL; 84 | #ifndef NDEBUG 85 | p->magic = HH_MAGIC; 86 | #endif 87 | hlist_init(&p->children); 88 | hlist_init_item(&p->siblings); 89 | 90 | return p->data; 91 | } 92 | 93 | p = structof(ptr, hblock_t, data); 94 | assert(p->magic == HH_MAGIC); 95 | 96 | /* realloc */ 97 | if (len) 98 | { 99 | int listed = hlist_item_listed(&p->siblings); 100 | 101 | p = allocator(p, len + sizeof_hblock); 102 | if (! p) 103 | return NULL; 104 | 105 | hlist_relink_head(&p->children); 106 | 107 | if (listed) hlist_relink(&p->siblings); 108 | else hlist_init_item(&p->siblings); 109 | 110 | return p->data; 111 | } 112 | 113 | /* free */ 114 | _free_children(p); 115 | hlist_del(&p->siblings); 116 | allocator(p, 0); 117 | 118 | return NULL; 119 | } 120 | 121 | void hattach(void * block, void * parent) 122 | { 123 | hblock_t * b, * p; 124 | 125 | if (! block) 126 | { 127 | assert(! parent); 128 | return; 129 | } 130 | 131 | /* detach */ 132 | b = structof(block, hblock_t, data); 133 | assert(b->magic == HH_MAGIC); 134 | 135 | hlist_del(&b->siblings); 136 | 137 | if (! parent) 138 | return; 139 | 140 | /* attach */ 141 | p = structof(parent, hblock_t, data); 142 | assert(p->magic == HH_MAGIC); 143 | 144 | /* sanity checks */ 145 | assert(b != p); /* trivial */ 146 | assert(! _relate(p, b)); /* heavy ! */ 147 | 148 | hlist_add(&p->children, &b->siblings); 149 | } 150 | 151 | /* 152 | * malloc/free api 153 | */ 154 | void * h_malloc(size_t len) 155 | { 156 | return halloc(0, len); 157 | } 158 | 159 | void * h_calloc(size_t n, size_t len) 160 | { 161 | void * ptr; 162 | 163 | if (! _ok_to_multiply(n, len)) 164 | return NULL; 165 | 166 | ptr = halloc(0, len *= n); 167 | return ptr ? memset(ptr, 0, len) : NULL; 168 | } 169 | 170 | void * h_realloc(void * ptr, size_t len) 171 | { 172 | return halloc(ptr, len); 173 | } 174 | 175 | void h_free(void * ptr) 176 | { 177 | halloc(ptr, 0); 178 | } 179 | 180 | char * h_strdup(const char * str) 181 | { 182 | size_t len = strlen(str); 183 | char * ptr = halloc(0, len + 1); 184 | return ptr ? (ptr[len] = 0, memcpy(ptr, str, len)) : NULL; 185 | } 186 | 187 | /* 188 | * static stuff 189 | */ 190 | #define SIZE_T_MAX ((size_t)-1) 191 | #define SIZE_T_HALF (((size_t)1) << 4*sizeof(size_t)) 192 | 193 | static int _ok_to_multiply(size_t a, size_t b) 194 | { 195 | return (a < SIZE_T_HALF && b < SIZE_T_HALF) || 196 | (a == 0) || 197 | (SIZE_T_MAX / a < b); 198 | } 199 | 200 | static void _set_allocator(void) 201 | { 202 | void * p; 203 | assert(! allocator); 204 | 205 | /* 206 | * the purpose of the test below is to check the behaviour 207 | * of realloc(ptr, 0), which is defined in the standard 208 | * as an implementation-specific. if it returns zero, 209 | * then it's equivalent to free(). it can however return 210 | * non-zero, in which case it cannot be used for freeing 211 | * memory blocks and we'll need to supply our own version 212 | * 213 | * Thanks to Stan Tobias for pointing this tricky part out. 214 | */ 215 | allocator = realloc; 216 | if (! (p = malloc(1))) 217 | /* hmm */ 218 | return; 219 | 220 | if ((p = realloc(p, 0))) 221 | { 222 | /* realloc cannot be used as free() */ 223 | allocator = _realloc; 224 | free(p); 225 | } 226 | } 227 | 228 | static void * _realloc(void * ptr, size_t n) 229 | { 230 | /* 231 | * free'ing realloc() 232 | */ 233 | if (n) 234 | return realloc(ptr, n); 235 | free(ptr); 236 | return NULL; 237 | } 238 | 239 | static int _relate(hblock_t * b, hblock_t * p) 240 | { 241 | hlist_item_t * i; 242 | 243 | if (!b || !p) 244 | return 0; 245 | 246 | /* 247 | * since there is no 'parent' pointer, which would've allowed 248 | * O(log(n)) upward traversal, the check must use O(n) downward 249 | * iteration of the entire hierarchy; and this can be VERY SLOW 250 | */ 251 | hlist_for_each(i, &p->children) 252 | { 253 | hblock_t * q = structof(i, hblock_t, siblings); 254 | if (q == b || _relate(b, q)) 255 | return 1; 256 | } 257 | return 0; 258 | } 259 | 260 | static void _free_children(hblock_t * p) 261 | { 262 | hlist_item_t * i, * tmp; 263 | 264 | #ifndef NDEBUG 265 | /* 266 | * this catches loops in hierarchy with almost zero 267 | * overhead (compared to _relate() running time) 268 | */ 269 | assert(p && p->magic == HH_MAGIC); 270 | p->magic = 0; 271 | #endif 272 | hlist_for_each_safe(i, tmp, &p->children) 273 | { 274 | hblock_t * q = structof(i, hblock_t, siblings); 275 | _free_children(q); 276 | allocator(q, 0); 277 | } 278 | } 279 | 280 | -------------------------------------------------------------------------------- /src/hlist.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of Hierarchical Allocator library. 3 | * Copyright (c) 2004-2011 Alex Pankratov. All rights reserved. 4 | * 5 | * http://swapped.cc/halloc 6 | */ 7 | 8 | /* 9 | * The program is distributed under terms of BSD license. 10 | * You can obtain the copy of the license by visiting: 11 | * 12 | * http://www.opensource.org/licenses/bsd-license.php 13 | */ 14 | 15 | #ifndef _LIBP_HLIST_H_ 16 | #define _LIBP_HLIST_H_ 17 | 18 | #include 19 | #include "macros.h" /* static_inline */ 20 | 21 | /* 22 | * weak double-linked list w/ tail sentinel 23 | */ 24 | typedef struct hlist_head hlist_head_t; 25 | typedef struct hlist_item hlist_item_t; 26 | 27 | /* 28 | * 29 | */ 30 | struct hlist_head 31 | { 32 | hlist_item_t * next; 33 | }; 34 | 35 | struct hlist_item 36 | { 37 | hlist_item_t * next; 38 | hlist_item_t ** prev; 39 | }; 40 | 41 | /* 42 | * shared tail sentinel 43 | */ 44 | struct hlist_item hlist_null; 45 | 46 | /* 47 | * 48 | */ 49 | #define __hlist_init(h) { &hlist_null } 50 | #define __hlist_init_item(i) { &hlist_null, &(i).next } 51 | 52 | static_inline void hlist_init(hlist_head_t * h); 53 | static_inline void hlist_init_item(hlist_item_t * i); 54 | 55 | static_inline int hlist_item_listed(hlist_item_t * i); 56 | 57 | static_inline void hlist_add(hlist_head_t * h, hlist_item_t * i); 58 | static_inline void hlist_del(hlist_item_t * i); 59 | 60 | static_inline void hlist_relink(hlist_item_t * i); 61 | static_inline void hlist_relink_head(hlist_head_t * h); 62 | 63 | #define hlist_for_each(i, h) \ 64 | for (i = (h)->next; i != &hlist_null; i = i->next) 65 | 66 | #define hlist_for_each_safe(i, tmp, h) \ 67 | for (i = (h)->next, tmp = i->next; \ 68 | i!= &hlist_null; \ 69 | i = tmp, tmp = i->next) 70 | 71 | /* 72 | * static 73 | */ 74 | static_inline void hlist_init(hlist_head_t * h) 75 | { 76 | assert(h); 77 | h->next = &hlist_null; 78 | } 79 | 80 | static_inline void hlist_init_item(hlist_item_t * i) 81 | { 82 | assert(i); 83 | i->prev = &i->next; 84 | i->next = &hlist_null; 85 | } 86 | 87 | static_inline int hlist_item_listed(hlist_item_t * i) 88 | { 89 | assert(i); 90 | return i->prev != &i->next; 91 | } 92 | 93 | static_inline void hlist_add(hlist_head_t * h, hlist_item_t * i) 94 | { 95 | hlist_item_t * next; 96 | assert(h && i); 97 | 98 | next = i->next = h->next; 99 | next->prev = &i->next; 100 | h->next = i; 101 | i->prev = &h->next; 102 | } 103 | 104 | static_inline void hlist_del(hlist_item_t * i) 105 | { 106 | hlist_item_t * next; 107 | assert(i); 108 | 109 | next = i->next; 110 | next->prev = i->prev; 111 | *i->prev = next; 112 | 113 | hlist_init_item(i); 114 | } 115 | 116 | static_inline void hlist_relink(hlist_item_t * i) 117 | { 118 | assert(i); 119 | *i->prev = i; 120 | i->next->prev = &i->next; 121 | } 122 | 123 | static_inline void hlist_relink_head(hlist_head_t * h) 124 | { 125 | assert(h); 126 | h->next->prev = &h->next; 127 | } 128 | 129 | #endif 130 | 131 | -------------------------------------------------------------------------------- /src/macros.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of Hierarchical Allocator library. 3 | * Copyright (c) 2004-2011 Alex Pankratov. All rights reserved. 4 | * 5 | * http://swapped.cc/halloc 6 | */ 7 | 8 | /* 9 | * The program is distributed under terms of BSD license. 10 | * You can obtain the copy of the license by visiting: 11 | * 12 | * http://www.opensource.org/licenses/bsd-license.php 13 | */ 14 | 15 | #ifndef _LIBP_MACROS_H_ 16 | #define _LIBP_MACROS_H_ 17 | 18 | #include /* offsetof */ 19 | 20 | /* 21 | restore pointer to the structure by a pointer to its field 22 | */ 23 | #define structof(p,t,f) ((t*)(- offsetof(t,f) + (void*)(p))) 24 | 25 | /* 26 | * redefine for the target compiler 27 | */ 28 | #define static_inline static __inline__ 29 | 30 | 31 | #endif 32 | 33 | --------------------------------------------------------------------------------