1 /* $NetBSD: pool-fast.c,v 1.1.1.2 2009/12/02 00:26:09 haad Exp $ */ 2 3 /* 4 * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved. 5 * Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved. 6 * 7 * This file is part of the device-mapper userspace tools. 8 * 9 * This copyrighted material is made available to anyone wishing to use, 10 * modify, copy, or redistribute it subject to the terms and conditions 11 * of the GNU Lesser General Public License v.2.1. 12 * 13 * You should have received a copy of the GNU Lesser General Public License 14 * along with this program; if not, write to the Free Software Foundation, 15 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 16 */ 17 18 #include "dmlib.h" 19 20 struct chunk { 21 char *begin, *end; 22 struct chunk *prev; 23 }; 24 25 struct dm_pool { 26 struct dm_list list; 27 struct chunk *chunk, *spare_chunk; /* spare_chunk is a one entry free 28 list to stop 'bobbling' */ 29 size_t chunk_size; 30 size_t object_len; 31 unsigned object_alignment; 32 }; 33 34 void _align_chunk(struct chunk *c, unsigned alignment); 35 struct chunk *_new_chunk(struct dm_pool *p, size_t s); 36 37 /* by default things come out aligned for doubles */ 38 #define DEFAULT_ALIGNMENT __alignof__ (double) 39 40 struct dm_pool *dm_pool_create(const char *name, size_t chunk_hint) 41 { 42 size_t new_size = 1024; 43 struct dm_pool *p = dm_malloc(sizeof(*p)); 44 45 if (!p) { 46 log_error("Couldn't create memory pool %s (size %" 47 PRIsize_t ")", name, sizeof(*p)); 48 return 0; 49 } 50 memset(p, 0, sizeof(*p)); 51 52 /* round chunk_hint up to the next power of 2 */ 53 p->chunk_size = chunk_hint + sizeof(struct chunk); 54 while (new_size < p->chunk_size) 55 new_size <<= 1; 56 p->chunk_size = new_size; 57 dm_list_add(&_dm_pools, &p->list); 58 return p; 59 } 60 61 void dm_pool_destroy(struct dm_pool *p) 62 { 63 struct chunk *c, *pr; 64 dm_free(p->spare_chunk); 65 c = p->chunk; 66 while (c) { 67 pr = c->prev; 68 dm_free(c); 69 c = pr; 70 } 71 72 dm_list_del(&p->list); 73 dm_free(p); 74 } 75 76 void *dm_pool_alloc(struct dm_pool *p, size_t s) 77 { 78 return dm_pool_alloc_aligned(p, s, DEFAULT_ALIGNMENT); 79 } 80 81 void *dm_pool_alloc_aligned(struct dm_pool *p, size_t s, unsigned alignment) 82 { 83 struct chunk *c = p->chunk; 84 void *r; 85 86 /* realign begin */ 87 if (c) 88 _align_chunk(c, alignment); 89 90 /* have we got room ? */ 91 if (!c || (c->begin > c->end) || (c->end - c->begin < s)) { 92 /* allocate new chunk */ 93 size_t needed = s + alignment + sizeof(struct chunk); 94 c = _new_chunk(p, (needed > p->chunk_size) ? 95 needed : p->chunk_size); 96 97 if (!c) 98 return NULL; 99 100 _align_chunk(c, alignment); 101 } 102 103 r = c->begin; 104 c->begin += s; 105 return r; 106 } 107 108 void dm_pool_empty(struct dm_pool *p) 109 { 110 struct chunk *c; 111 112 for (c = p->chunk; c && c->prev; c = c->prev) 113 ; 114 115 if (c) 116 dm_pool_free(p, (char *) (c + 1)); 117 } 118 119 void dm_pool_free(struct dm_pool *p, void *ptr) 120 { 121 struct chunk *c = p->chunk; 122 123 while (c) { 124 if (((char *) c < (char *) ptr) && 125 ((char *) c->end > (char *) ptr)) { 126 c->begin = ptr; 127 break; 128 } 129 130 if (p->spare_chunk) 131 dm_free(p->spare_chunk); 132 p->spare_chunk = c; 133 c = c->prev; 134 } 135 136 if (!c) 137 log_error("Internal error: pool_free asked to free pointer " 138 "not in pool"); 139 else 140 p->chunk = c; 141 } 142 143 int dm_pool_begin_object(struct dm_pool *p, size_t hint) 144 { 145 struct chunk *c = p->chunk; 146 const size_t align = DEFAULT_ALIGNMENT; 147 148 p->object_len = 0; 149 p->object_alignment = align; 150 151 if (c) 152 _align_chunk(c, align); 153 154 if (!c || (c->begin > c->end) || (c->end - c->begin < hint)) { 155 /* allocate a new chunk */ 156 c = _new_chunk(p, 157 hint > (p->chunk_size - sizeof(struct chunk)) ? 158 hint + sizeof(struct chunk) + align : 159 p->chunk_size); 160 161 if (!c) 162 return 0; 163 164 _align_chunk(c, align); 165 } 166 167 return 1; 168 } 169 170 int dm_pool_grow_object(struct dm_pool *p, const void *extra, size_t delta) 171 { 172 struct chunk *c = p->chunk, *nc; 173 174 if (!delta) 175 delta = strlen(extra); 176 177 if (c->end - (c->begin + p->object_len) < delta) { 178 /* move into a new chunk */ 179 if (p->object_len + delta > (p->chunk_size / 2)) 180 nc = _new_chunk(p, (p->object_len + delta) * 2); 181 else 182 nc = _new_chunk(p, p->chunk_size); 183 184 if (!nc) 185 return 0; 186 187 _align_chunk(p->chunk, p->object_alignment); 188 memcpy(p->chunk->begin, c->begin, p->object_len); 189 c = p->chunk; 190 } 191 192 memcpy(c->begin + p->object_len, extra, delta); 193 p->object_len += delta; 194 return 1; 195 } 196 197 void *dm_pool_end_object(struct dm_pool *p) 198 { 199 struct chunk *c = p->chunk; 200 void *r = c->begin; 201 c->begin += p->object_len; 202 p->object_len = 0u; 203 p->object_alignment = DEFAULT_ALIGNMENT; 204 return r; 205 } 206 207 void dm_pool_abandon_object(struct dm_pool *p) 208 { 209 p->object_len = 0; 210 p->object_alignment = DEFAULT_ALIGNMENT; 211 } 212 213 void _align_chunk(struct chunk *c, unsigned alignment) 214 { 215 c->begin += alignment - ((unsigned long) c->begin & (alignment - 1)); 216 } 217 218 struct chunk *_new_chunk(struct dm_pool *p, size_t s) 219 { 220 struct chunk *c; 221 222 if (p->spare_chunk && 223 ((p->spare_chunk->end - (char *) p->spare_chunk) >= s)) { 224 /* reuse old chunk */ 225 c = p->spare_chunk; 226 p->spare_chunk = 0; 227 } else { 228 if (!(c = dm_malloc(s))) { 229 log_error("Out of memory. Requested %" PRIsize_t 230 " bytes.", s); 231 return NULL; 232 } 233 234 c->end = (char *) c + s; 235 } 236 237 c->prev = p->chunk; 238 c->begin = (char *) (c + 1); 239 p->chunk = c; 240 241 return c; 242 } 243