su_alloc.c 42.8 KB
Newer Older
Pekka Pessi's avatar
Pekka Pessi committed
1 2 3 4 5 6 7
/*
 * This file is part of the Sofia-SIP package
 *
 * Copyright (C) 2005 Nokia Corporation.
 *
 * Contact: Pekka Pessi <pekka.pessi@nokia.com>
 *
8
 * This library is free software; you can redistribute it and/or
Pekka Pessi's avatar
Pekka Pessi committed
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA
 *
 */

#include "config.h"

/**@defgroup su_alloc Memory Management Tutorial
 *
 * This page gives a short overview of home-based memory management used
 * with Sofia. Such home-based memory management is useful when a lot of
 * memory blocks are allocated for given task. The allocations are done via
32 33 34
 * the @e memory @e home, which keeps a reference to each block. When the
 * memory home is then freed, it will free all blocks to which it has
 * reference.
Pekka Pessi's avatar
Pekka Pessi committed
35 36
 *
 * Typically, there is a @e home @e object which contains a su_home_t
37 38
 * structure in the beginning of the object (sort of inheritance from
 * su_home_t):
Pekka Pessi's avatar
Pekka Pessi committed
39 40 41 42 43 44 45
 * @code
 * struct context {
 *   su_home_t ctx_home[1];
 *   other_t  *ctx_stuff;
 *   ...
 * }
 * @endcode
46
 *
47 48 49 50 51 52 53 54
 * A new home memory pool can be created with su_home_new():
 * @code
 * struct context *ctx = su_home_new(sizeof (struct context));
 * @endcode
 *
 * It is also possible to create a secondary memory pool that can be
 * released separately:
 *
Pekka Pessi's avatar
Pekka Pessi committed
55 56 57 58
 * @code
 * struct context *ctx = su_home_clone(tophome, sizeof (struct context));
 * @endcode
 *
59 60
 * Note that the tophome has a reference to @a ctx structure; whenever
 * tophome is freed, the @a ctx is also freed.
61
 *
62
 * You can also create an independent home object by passing NULL as @a
63
 * tophome argument. This is identical to the call to su_home_new().
Pekka Pessi's avatar
Pekka Pessi committed
64
 *
65
 * The memory allocations using @a ctx proceed then as follows:
Pekka Pessi's avatar
Pekka Pessi committed
66
 * @code
67
 *    zeroblock = su_zalloc(ctx->ctx_home, sizeof (*zeroblock));
Pekka Pessi's avatar
Pekka Pessi committed
68 69 70
 * @endcode
 *
 * The home memory pool - the home object and all the memory blocks
71
 * allocated using it - are freed when su_home_unref() is called:
Pekka Pessi's avatar
Pekka Pessi committed
72 73
 *
 * @code
74
 *    su_home_unref(ctx->ctx_home).
Pekka Pessi's avatar
Pekka Pessi committed
75 76
 * @endcode
 *
77 78 79 80 81
 * @note For historical reasons, su_home_unref() is also known as
 * su_home_zap().
 *
 * As you might have guessed, it is also possible to use reference counting
 * with home objects. The function su_home_ref() increases the reference
82 83
 * count, su_home_unref() decreases it. A newly allocated or initialized
 * home object has reference count of 1.
84
 *
85 86 87 88
 * @note Please note that while it is possible to create new references to
 * secondary home objects which have a parent home, the secondary home
 * objects will always be destroyed when the parent home is destroyed even
 * if there are other references left to them.
89 90 91
 *
 * The memory blocks in a cloned home object are freed when the object with
 * home itself is freed:
Pekka Pessi's avatar
Pekka Pessi committed
92 93 94
 * @code
 *    su_free(tophome, ctx);
 * @endcode
95
 *
96 97 98
 * @note
 *
 * The su_home_destroy() function is deprecated as it does not free the home
99 100
 * object itself. Like su_home_deinit(), it should be called only on home
 * objects with reference count of 1.
101
 *
102 103 104 105
 * The function su_home_init() initializes a home object structure. When the
 * initialized home object is destroyed or deinitialized or its reference
 * count reaches zero, the memory allocate thorugh it reclaimed but the home
 * object structure itself is not freed.
Pekka Pessi's avatar
Pekka Pessi committed
106
 *
107
 * @section su_home_destructor_usage Destructors
108 109 110 111 112 113
 *
 * It is possible to give a destructor function to a home object. The
 * destructor releases other resources associated with the home object
 * besides memory. The destructor function will be called when the reference
 * count of home reaches zero (upon calling su_home_unref()) or the home
 * object is otherwise deinitialized (calling su_home_deinit() on
114
 * objects allocated from stack).
115
 *
Pekka Pessi's avatar
Pekka Pessi committed
116 117 118 119 120 121 122 123 124
 * @section su_home_move_example Combining Allocations
 *
 * In some cases, an operation that makes multiple memory allocations may
 * fail, making those allocations redundant. If the allocations are made
 * through a temporary home, they can be conveniently freed by calling
 * su_home_deinit(), for instance. If, however, the operation is successful,
 * and one wants to keep the allocations, the allocations can be combined
 * into an existing home with su_home_move(). For example,
 * @code
125
 * int example(su_home_t *home, ...)
Pekka Pessi's avatar
Pekka Pessi committed
126 127
 * {
 *   su_home_t temphome[1] = { SU_HOME_INIT(temphome) };
128
 *
Pekka Pessi's avatar
Pekka Pessi committed
129
 *   ... do lot of allocations with temphome ...
130 131
 *
 *   if (success)
Pekka Pessi's avatar
Pekka Pessi committed
132 133 134 135 136 137 138 139 140 141 142 143 144 145
 *     su_home_move(home, temphome);
 *   su_home_deinit(temphome);
 *
 *   return success;
 * }
 * @endcode
 *
 * Note that the @a temphome is deinitialized in every case, but when
 * operation is successful, the allocations are moved from @a temphome to @a
 * home.
 *
 * @section su_alloc_threadsafe Threadsafe Operation
 *
 * If multiple threads need to access same home object, it must be marked as
146
 * @e threadsafe by calling su_home_threadsafe() with the home pointer as
147
 * argument. The threadsafeness is not inherited by clones.
148 149
 *
 * The threadsafe home objects can be locked and unlocked with
150 151
 * su_home_mutex_lock() and su_home_mutex_unlock(). These operations are
 * no-op on home object that is not threadsafe.
152 153 154
 *
 * @section su_alloc_preloading Preloading a Memory Home
 *
155 156
 * In some situations there is quite heavy overhead if the global heap
 * allocator is used. The overhead caused by the large number of small
157 158
 * allocations can be reduced by using su_home_preload(): it allocates or
 * preloads some a memory to home to be used as a kind of private heap. The
159
 * preloaded memory area is then used to satisfy small enough allocations.
160 161 162 163 164 165 166 167 168 169 170
 * For instance, the SIP parser typically preloads some 2K of memory when it
 * starts to parse the message.
 *
 * @section su_alloc_stack Using Stack
 *
 * In some situation, it is sensible to use memory allocated from stack for
 * some operations. The su_home_auto() function can be used for that
 * purpose. The memory area from stack is used to satisfy the allocations as
 * far as possible; if it is not enough, allocation is made from heap.
 *
 * The word @e auto refers to the automatic scope; however, the home object
171 172 173
 * that was initialized with su_home_auto() must be explicitly deinitialized
 * with su_home_deinit() or su_home_unref() when the program exits the scope
 * where the stack frame used in su_home_auto() was allocated.
Pekka Pessi's avatar
Pekka Pessi committed
174 175
 */

Pekka Pessi's avatar
Pekka Pessi committed
176 177 178 179
/**@ingroup su_alloc
 * @CFILE su_alloc.c  Home-based memory management.
 *
 * @author Pekka Pessi <Pekka.Pessi@nokia.com>.
180
 *
Pekka Pessi's avatar
Pekka Pessi committed
181 182 183
 * @date Created: Thu Aug 19 01:12:25 1999 ppessi
 */

184 185 186 187
#include <sofia-sip/su_config.h>
#include "sofia-sip/su_alloc.h"
#include "sofia-sip/su_alloc_stat.h"
#include "sofia-sip/su_errno.h"
188

189
#include <stdio.h>
Pekka Pessi's avatar
Pekka Pessi committed
190 191 192 193 194 195 196
#include <stdlib.h>
#include <stddef.h>
#include <memory.h>
#include <limits.h>

#include <assert.h>

197 198
int (*_su_home_locker)(void *mutex);
int (*_su_home_unlocker)(void *mutex);
Pekka Pessi's avatar
Pekka Pessi committed
199

200 201 202
int (*_su_home_mutex_locker)(void *mutex);
int (*_su_home_mutex_trylocker)(void *mutex);
int (*_su_home_mutex_unlocker)(void *mutex);
203

204
void (*_su_home_destroy_mutexes)(void *mutex);
205

206 207 208 209 210 211
#if HAVE_FREE_NULL
#define safefree(x) free((x))
#else
su_inline void safefree(void *b) { b ? free(b) : (void)0; }
#endif

212
#define MEMLOCK(h)   \
213 214
  ((void)((h) && (h)->suh_lock ? _su_home_locker((h)->suh_lock) : 0), (h)->suh_blocks)
#define UNLOCK(h) ((void)((h) && (h)->suh_lock ? _su_home_unlocker((h)->suh_lock) : 0), NULL)
Pekka Pessi's avatar
Pekka Pessi committed
215 216 217 218 219 220 221 222 223

#ifdef NDEBUG
#define MEMCHECK 0
#define MEMCHECK_EXTRA 0
#elif !defined(MEMCHECK)
/* Default settings for valgrinding */
#define MEMCHECK 1
#define MEMCHECK_EXTRA 0
#elif !defined(MEMCHECK_EXTRA)
224
#define MEMCHECK_EXTRA sizeof (size_t)
Pekka Pessi's avatar
Pekka Pessi committed
225 226
#endif

227
enum {
Pekka Pessi's avatar
Pekka Pessi committed
228
  SUB_N = 31,			/**< Initial size */
229
  SUB_N_AUTO = 7,		/**< Initial size for autohome */
Pekka Pessi's avatar
Pekka Pessi committed
230
  SUB_P = 29			/**< Secondary probe.
231
				 * Secondary probe must be relative prime
Pekka Pessi's avatar
Pekka Pessi committed
232
				 * with all sub_n values */
233
};
Pekka Pessi's avatar
Pekka Pessi committed
234 235

#define ALIGNMENT (8)
236
#define ALIGN(n) (size_t)(((n) + (ALIGNMENT - 1)) & (size_t)~(ALIGNMENT - 1))
237
#define SIZEBITS (sizeof (unsigned) * 8 - 1)
Pekka Pessi's avatar
Pekka Pessi committed
238 239

typedef struct {
240
  unsigned sua_size:SIZEBITS;	/**< Size of the block */
241
  unsigned sua_home:1;		/**< Is this another home? */
242
  unsigned :0;
243
  void    *sua_data;		/**< Data pointer */
Pekka Pessi's avatar
Pekka Pessi committed
244 245 246
} su_alloc_t;

struct su_block_s {
247
  su_home_t  *sub_parent;	/**< Parent home */
248 249
  char       *sub_preload;	/**< Preload area */
  su_home_stat_t *sub_stats;	/**< Statistics.. */
250
  void      (*sub_destructor)(void *); /**< Destructor function */
251
  size_t      sub_ref;		/**< Reference count */
252
#define REF_MAX SIZE_MAX
253 254
  size_t      sub_used;		/**< Number of blocks allocated */
  size_t      sub_n;		/**< Size of hash table  */
Pekka Pessi's avatar
Pekka Pessi committed
255 256 257

  unsigned    sub_prsize:16;	/**< Preload size */
  unsigned    sub_prused:16;	/**< Used from preload */
258 259 260
  unsigned    sub_hauto:1;      /**< "Home" is not from malloc */
  unsigned    sub_auto:1;	/**< struct su_block_s is not from malloc */
  unsigned    sub_preauto:1;	/**< Preload is not from malloc */
Pekka Pessi's avatar
Pekka Pessi committed
261 262
  unsigned    sub_auto_all:1;	/**< Everything is from stack! */
  unsigned :0;
263

Pekka Pessi's avatar
Pekka Pessi committed
264 265 266 267 268
  su_alloc_t  sub_nodes[SUB_N];	/**< Pointers to data/lower blocks */
};

static void su_home_check_blocks(su_block_t const *b);

269
static void su_home_stats_alloc(su_block_t *, void *p, void *preload,
270
				size_t size, int zero);
271
static void su_home_stats_free(su_block_t *sub, void *p, void *preload,
272
			       unsigned size);
Pekka Pessi's avatar
Pekka Pessi committed
273

274 275
static void _su_home_deinit(su_home_t *home);

Pekka Pessi's avatar
Pekka Pessi committed
276 277 278
#define SU_ALLOC_STATS 1

#if SU_ALLOC_STATS
279 280 281
size_t count_su_block_find, count_su_block_find_loop;
size_t size_su_block_find, used_su_block_find;
size_t max_size_su_block_find, max_used_su_block_find;
282
size_t su_block_find_collision, su_block_find_collision_used,
Pekka Pessi's avatar
Pekka Pessi committed
283 284 285
  su_block_find_collision_size;
#endif

286
su_inline su_alloc_t *su_block_find(su_block_t const *b, void const *p)
Pekka Pessi's avatar
Pekka Pessi committed
287
{
288
  size_t h, h0, probe;
Pekka Pessi's avatar
Pekka Pessi committed
289

290
#if SU_ALLOC_STATS
291
  size_t collision = 0;
Pekka Pessi's avatar
Pekka Pessi committed
292 293 294 295 296 297 298 299 300 301 302 303

  count_su_block_find++;
  size_su_block_find += b->sub_n;
  used_su_block_find += b->sub_used;
  if (b->sub_n > max_size_su_block_find)
    max_size_su_block_find = b->sub_n;
  if (b->sub_used > max_used_su_block_find)
    max_used_su_block_find = b->sub_used;
#endif

  assert(p != NULL);

304
  h = h0 = (size_t)((uintptr_t)p % b->sub_n);
Pekka Pessi's avatar
Pekka Pessi committed
305

306 307
  probe = (b->sub_n > SUB_P) ? SUB_P : 1;

Pekka Pessi's avatar
Pekka Pessi committed
308
  do {
309 310 311 312
    if (b->sub_nodes[h].sua_data == p) {
      su_alloc_t const *retval = &b->sub_nodes[h];
      return (su_alloc_t *)retval; /* discard const */
    }
313
    h += probe;
Pekka Pessi's avatar
Pekka Pessi committed
314 315 316 317
    if (h >= b->sub_n)
      h -= b->sub_n;
#if SU_ALLOC_STATS
    if (++collision > su_block_find_collision)
318
      su_block_find_collision = collision,
Pekka Pessi's avatar
Pekka Pessi committed
319 320 321 322 323 324 325 326 327
	su_block_find_collision_used = b->sub_used,
	su_block_find_collision_size = b->sub_n;
    count_su_block_find_loop++;
#endif
  } while (h != h0);

  return NULL;
}

328
su_inline su_alloc_t *su_block_add(su_block_t *b, void *p)
Pekka Pessi's avatar
Pekka Pessi committed
329
{
330
  size_t h, probe;
Pekka Pessi's avatar
Pekka Pessi committed
331 332 333

  assert(p != NULL);

334
  h = (size_t)((uintptr_t)p % b->sub_n);
Pekka Pessi's avatar
Pekka Pessi committed
335

336 337
  probe = (b->sub_n > SUB_P) ? SUB_P : 1;

Pekka Pessi's avatar
Pekka Pessi committed
338
  while (b->sub_nodes[h].sua_data) {
339
    h += probe;
Pekka Pessi's avatar
Pekka Pessi committed
340 341 342 343 344 345 346 347 348 349
    if (h >= b->sub_n)
      h -= b->sub_n;
  }

  b->sub_used++;
  b->sub_nodes[h].sua_data = p;

  return &b->sub_nodes[h];
}

350
su_inline int su_is_preloaded(su_block_t const *sub, char *data)
Pekka Pessi's avatar
Pekka Pessi committed
351 352
{
  return
353 354
    sub->sub_preload &&
    sub->sub_preload <= data &&
Pekka Pessi's avatar
Pekka Pessi committed
355 356 357
    sub->sub_preload + sub->sub_prsize > data;
}

358
su_inline int su_alloc_check(su_block_t const *sub, su_alloc_t const *sua)
Pekka Pessi's avatar
Pekka Pessi committed
359 360
{
#if MEMCHECK_EXTRA
361
  size_t size, term;
Pekka Pessi's avatar
Pekka Pessi committed
362 363
  assert(sua);
  if (sua) {
364
    size = (size_t)sua->sua_size;
365
    memcpy(&term, (char *)sua->sua_data + size, sizeof (term));
366 367
    assert(size - term == 0);
    return size - term == 0;
Pekka Pessi's avatar
Pekka Pessi committed
368 369 370 371 372 373 374 375 376
  }
  else
    return 0;
#endif
  return sua != NULL;
}

/** Allocate the block hash table.
 *
377 378 379
 * @internal
 *
 * Allocate a block hash table of @a n elements.
Pekka Pessi's avatar
Pekka Pessi committed
380 381 382 383 384 385 386 387
 *
 * @param home  pointer to home object
 * @param n     number of buckets in hash table
 *
 * @return
 *   This function returns a pointer to the allocated hash table or
 *   NULL if an error occurred.
 */
388
su_inline su_block_t *su_hash_alloc(size_t n)
Pekka Pessi's avatar
Pekka Pessi committed
389
{
390
  su_block_t *b = calloc(1, offsetof(su_block_t, sub_nodes[n]));
Pekka Pessi's avatar
Pekka Pessi committed
391

392 393 394 395
  if (b) {
    /* Implicit su_home_init(); */
    b->sub_ref = 1;
    b->sub_hauto = 1;
Pekka Pessi's avatar
Pekka Pessi committed
396
    b->sub_n = n;
397
  }
Pekka Pessi's avatar
Pekka Pessi committed
398 399 400 401

  return b;
}

402 403
enum sub_zero { do_malloc, do_calloc, do_clone };

Pekka Pessi's avatar
Pekka Pessi committed
404
/** Allocate a memory block.
405 406
 *
 * @internal
Pekka Pessi's avatar
Pekka Pessi committed
407
 *
408
 * Precondition: locked home
Pekka Pessi's avatar
Pekka Pessi committed
409
 *
410 411 412 413 414
 * @param home home to allocate
 * @param sub  block structure used to allocate
 * @param size
 * @param zero if true, zero allocated block;
 *             if > 1, allocate a subhome
Pekka Pessi's avatar
Pekka Pessi committed
415 416
 *
 */
417 418
static
void *sub_alloc(su_home_t *home,
419
		su_block_t *sub,
420
		size_t size,
421
		enum sub_zero zero)
Pekka Pessi's avatar
Pekka Pessi committed
422
{
Pekka Pessi's avatar
Pekka Pessi committed
423
  void *data, *preload = NULL;
424

425 426 427 428
  assert (size < (((size_t)1) << SIZEBITS));

  if (size >= ((size_t)1) << SIZEBITS)
    return (void)(errno = ENOMEM), NULL;
Pekka Pessi's avatar
Pekka Pessi committed
429 430

  if (sub == NULL || 3 * sub->sub_used > 2 * sub->sub_n) {
431
    /* Resize the hash table */
432
    size_t i, n, n2, used;
Pekka Pessi's avatar
Pekka Pessi committed
433 434 435 436 437 438 439
    su_block_t *b2;

    if (sub)
      n = home->suh_blocks->sub_n, n2 = 4 * n + 3, used = sub->sub_used;
    else
      n = 0, n2 = SUB_N, used = 0;

Pekka Pessi's avatar
Pekka Pessi committed
440 441 442
#if 0
    printf("su_alloc(home = %p): realloc block hash of size %d\n", home, n2);
#endif
Pekka Pessi's avatar
Pekka Pessi committed
443

444 445
    if (!(b2 = su_hash_alloc(n2)))
      return NULL;
Pekka Pessi's avatar
Pekka Pessi committed
446 447

    for (i = 0; i < n; i++) {
448
      if (sub->sub_nodes[i].sua_data)
Pekka Pessi's avatar
Pekka Pessi committed
449 450 451 452 453
	su_block_add(b2, sub->sub_nodes[i].sua_data)[0] = sub->sub_nodes[i];
    }

    if (sub) {
      b2->sub_parent = sub->sub_parent;
454
      b2->sub_ref = sub->sub_ref;
Pekka Pessi's avatar
Pekka Pessi committed
455 456 457
      b2->sub_preload = sub->sub_preload;
      b2->sub_prsize = sub->sub_prsize;
      b2->sub_prused = sub->sub_prused;
458
      b2->sub_hauto = sub->sub_hauto;
Pekka Pessi's avatar
Pekka Pessi committed
459
      b2->sub_preauto = sub->sub_preauto;
460
      b2->sub_destructor = sub->sub_destructor;
461
      /* auto_all is not copied! */
Pekka Pessi's avatar
Pekka Pessi committed
462 463 464
      b2->sub_stats = sub->sub_stats;
    }

465 466
    home->suh_blocks = b2;

Pekka Pessi's avatar
Pekka Pessi committed
467
    if (sub && !sub->sub_auto)
468
      free(sub);
Pekka Pessi's avatar
Pekka Pessi committed
469
    sub = b2;
Pekka Pessi's avatar
Pekka Pessi committed
470 471
  }

472
  if (size && sub && zero < do_clone &&
473
      sub->sub_preload && size <= sub->sub_prsize) {
474
    /* Use preloaded memory */
475
    size_t prused = sub->sub_prused + size + MEMCHECK_EXTRA;
476 477
    prused = ALIGN(prused);
    if (prused <= sub->sub_prsize) {
Pekka Pessi's avatar
Pekka Pessi committed
478
      preload = (char *)sub->sub_preload + sub->sub_prused;
479
      sub->sub_prused = (unsigned)prused;
Pekka Pessi's avatar
Pekka Pessi committed
480 481
    }
  }
482

483
  if (preload && zero)
Pekka Pessi's avatar
Pekka Pessi committed
484
    data = memset(preload, 0, size);
485
  else if (preload)
Pekka Pessi's avatar
Pekka Pessi committed
486
    data = preload;
487 488
  else if (zero)
    data = calloc(1, size + MEMCHECK_EXTRA);
Pekka Pessi's avatar
Pekka Pessi committed
489 490 491 492
  else
    data = malloc(size + MEMCHECK_EXTRA);

  if (data) {
493 494
    su_alloc_t *sua;

Pekka Pessi's avatar
Pekka Pessi committed
495
#if MEMCHECK_EXTRA
496
    size_t term = 0 - size;
Pekka Pessi's avatar
Pekka Pessi committed
497 498 499
    memcpy((char *)data + size, &term, sizeof (term));
#endif

500 501 502
    if (!preload)
      sub->sub_auto_all = 0;

503
    if (zero >= do_clone) {
504
      /* Prepare cloned home */
505 506 507 508 509
      su_home_t *subhome = data;

      assert(preload == 0);

      subhome->suh_blocks = su_hash_alloc(SUB_N);
510
      if (!subhome->suh_blocks)
511
	return (void)safefree(data), NULL;
512

513
      subhome->suh_size = (unsigned)size;
514
      subhome->suh_blocks->sub_parent = home;
515
      subhome->suh_blocks->sub_hauto = 0;
516 517
    }

518 519
    /* OK, add the block to the hash table. */

520
    sua = su_block_add(sub, data); assert(sua);
521
    sua->sua_size = (unsigned)size;
522
    sua->sua_home = zero > 1;
Pekka Pessi's avatar
Pekka Pessi committed
523 524

    if (sub->sub_stats)
525
      su_home_stats_alloc(sub, data, preload, size, zero);
Pekka Pessi's avatar
Pekka Pessi committed
526 527
  }

528 529 530 531 532 533 534
  return data;
}

/**Create a new su_home_t object.
 *
 * Create a home object used to collect multiple memory allocations under
 * one handle. The memory allocations made using this home object is freed
535
 * either when this home is destroyed.
536 537
 *
 * The maximum @a size of a home object is INT_MAX (2 gigabytes).
538 539 540
 *
 * @param size    size of home object
 *
541
 * The memory home object allocated with su_home_new() can be reclaimed with
542 543 544 545 546 547
 * su_home_unref().
 *
 * @return
 * This function returns a pointer to an su_home_t object, or NULL upon
 * an error.
 */
548
void *su_home_new(isize_t size)
549 550 551 552 553 554
{
  su_home_t *home;

  assert(size >= sizeof (*home));

  if (size < sizeof (*home))
555
    return (void)(errno = EINVAL), NULL;
556
  else if (size > INT_MAX)
557
    return (void)(errno = ENOMEM), NULL;
558 559 560

  home = calloc(1, size);
  if (home) {
561
    home->suh_size = (int)size;
562 563
    home->suh_blocks = su_hash_alloc(SUB_N);
    if (home->suh_blocks)
564
      home->suh_blocks->sub_hauto = 0;
565
    else
566
      safefree(home), home = NULL;
567 568 569 570 571 572
  }

  return home;
}

/** Create a new reference to a home object. */
573
void *su_home_ref(su_home_t const *home)
574 575 576 577 578 579 580 581 582
{
  if (home) {
    su_block_t *sub = MEMLOCK(home);

    if (sub == NULL || sub->sub_ref == 0) {
      assert(sub && sub->sub_ref != 0);
      UNLOCK(home);
      return NULL;
    }
583

584
    if (sub->sub_ref != REF_MAX)
585 586 587
      sub->sub_ref++;
    UNLOCK(home);
  }
588 589
  else
    su_seterrno(EFAULT);
590

591
  return (void *)home;
592 593
}

594 595 596 597 598 599 600 601 602 603
/** Set destructor function.
 *
 * The destructor function is called after the reference count of a
 * #su_home_t object reaches zero or a home object is deinitialized, but
 * before any of the memory areas within the home object are freed.
 *
 * @since New in @VERSION_1_12_4.
 * Earlier versions had su_home_desctructor() (spelling).
 */
int su_home_destructor(su_home_t *home, void (*destructor)(void *))
604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620
{
  int retval = -1;

  if (home) {
    su_block_t *sub = MEMLOCK(home);
    if (sub && sub->sub_destructor == NULL) {
      sub->sub_destructor = destructor;
      retval = 0;
    }
    UNLOCK(home);
  }
  else
    su_seterrno(EFAULT);

  return retval;
}

621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638
#undef su_home_desctructor

/** Set destructor function.
 *
 * @deprecated The su_home_destructor() was added in @VERSION_1_12_4. The
 * su_home_desctructor() is now defined as a macro expanding as
 * su_home_destructor(). If you want to compile an application as binary
 * compatible with earlier versions, you have to define su_home_desctructor
 * as itself, e.g.,
 * @code
 * #define su_home_desctructor su_home_desctructor
 * #include <sofia-sip/su_alloc.h>
 * @endcode
 */
int su_home_desctructor(su_home_t *home, void (*destructor)(void *))
{
  return su_home_destructor(home, destructor);
}
639

640 641
/**Unreference a su_home_t object.
 *
642 643
 * Decrements the reference count on home object and destroys and frees it
 * and the memory allocations using it if the reference count reaches 0.
644
 *
645
 * @param home memory pool object to be unreferenced
646 647 648
 *
 * @retval 1 if object was freed
 * @retval 0 if object is still alive
649
 */
650
int su_home_unref(su_home_t *home)
651 652 653 654
{
  su_block_t *sub;

  if (home == NULL)
655
    return 0;
656 657 658 659 660

  sub = MEMLOCK(home);

  if (sub == NULL) {
    /* Xyzzy */
661
    return 0;
662
  }
663
  else if (sub->sub_ref == REF_MAX) {
664
    UNLOCK(home);
665
    return 0;
666
  }
667
  else if (--sub->sub_ref > 0) {
668
    UNLOCK(home);
669 670
    return 0;
  }
671
  else if (sub->sub_parent) {
672 673 674 675
    su_home_t *parent = sub->sub_parent;
    UNLOCK(home);
    su_free(parent, home);
    return 1;
676 677
  }
  else {
678
    int hauto = sub->sub_hauto;
679
    _su_home_deinit(home);
680
    if (!hauto)
681
      safefree(home);
682
    /* UNLOCK(home); */
683
    return 1;
684 685 686
  }
}

687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702
/** Return reference count of home. */
size_t su_home_refcount(su_home_t *home)
{
  size_t count = 0;

  if (home) {
    su_block_t *sub = MEMLOCK(home);

    if (sub)
      count = sub->sub_ref;

    UNLOCK(home);
  }

  return count;
}
703 704 705 706 707

/**Clone a su_home_t object.
 *
 * Clone a secondary home object used to collect multiple memoryf
 * allocations under one handle. The memory is freed either when the cloned
708
 * home is destroyed or when the parent home is destroyed.
709 710 711 712 713 714 715
 *
 * An independent
 * home object is created if NULL is passed as @a parent argument.
 *
 * @param parent  a parent object (may be NULL)
 * @param size    size of home object
 *
716
 * The memory home object allocated with su_home_clone() can be freed with
717 718 719 720 721 722
 * su_home_unref().
 *
 * @return
 * This function returns a pointer to an su_home_t object, or NULL upon
 * an error.
 */
723
void *su_home_clone(su_home_t *parent, isize_t size)
724 725 726 727 728 729
{
  su_home_t *home;

  assert(size >= sizeof (*home));

  if (size < sizeof (*home))
730 731 732
    return (void)(errno = EINVAL), NULL;
  else if (size > INT_MAX)
    return (void)(errno = ENOMEM), NULL;
733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748

  if (parent) {
    su_block_t *sub = MEMLOCK(parent);
    home = sub_alloc(parent, sub, size, 2);
    UNLOCK(parent);
  }
  else {
    home = su_home_new(size);
  }

  return home;
}

/** Return true if home is a clone. */
int su_home_has_parent(su_home_t const *home)
{
749
  return home && !home->suh_lock &&
750 751 752 753 754
    home->suh_blocks && home->suh_blocks->sub_parent;
}

/** Allocate a memory block.
 *
755
 * Allocates a memory block of a given @a size.
756 757 758 759 760 761 762 763 764 765
 *
 * If @a home is NULL, this function behaves exactly like malloc().
 *
 * @param home  pointer to home object
 * @param size  size of the memory block to be allocated
 *
 * @return
 * This function returns a pointer to the allocated memory block or
 * NULL if an error occurred.
 */
766
void *su_alloc(su_home_t *home, isize_t size)
767 768 769 770
{
  void *data;

  if (home) {
771
    data = sub_alloc(home, MEMLOCK(home), size, 0);
772 773 774 775
    UNLOCK(home);
  }
  else
    data = malloc(size);
Pekka Pessi's avatar
Pekka Pessi committed
776 777 778 779 780 781

  return data;
}

/**Free a memory block.
 *
782 783 784
 * Frees a single memory block. The @a home must be the owner of the memory
 * block (usually the memory home used to allocate the memory block, or NULL
 * if no home was used).
Pekka Pessi's avatar
Pekka Pessi committed
785 786 787 788 789 790
 *
 * @param home  pointer to home object
 * @param data  pointer to the memory block to be freed
 */
void su_free(su_home_t *home, void *data)
{
791 792 793 794
  if (!data)
    return;

  if (home) {
Pekka Pessi's avatar
Pekka Pessi committed
795
    su_alloc_t *allocation;
796
    su_block_t *sub = MEMLOCK(home);
Pekka Pessi's avatar
Pekka Pessi committed
797 798 799 800 801 802

    assert(sub);
    allocation = su_block_find(sub, data);
    assert(allocation);

    if (su_alloc_check(sub, allocation)) {
803 804 805 806 807
      void *preloaded = NULL;

      /* Is this preloaded data? */
      if (su_is_preloaded(sub, data))
	preloaded = data;
Pekka Pessi's avatar
Pekka Pessi committed
808 809

      if (sub->sub_stats)
810
	su_home_stats_free(sub, data, preloaded, allocation->sua_size);
Pekka Pessi's avatar
Pekka Pessi committed
811

812 813 814 815
      if (allocation->sua_home) {
	su_home_t *subhome = data;
	su_block_t *sub = MEMLOCK(subhome);

816
	assert(sub->sub_ref != REF_MAX);
817 818 819 820 821 822
	/* assert(sub->sub_ref > 0); */

	sub->sub_ref = 0;	/* Zap all references */

	_su_home_deinit(subhome);
      }
Pekka Pessi's avatar
Pekka Pessi committed
823 824

#if MEMCHECK != 0
825
      memset(data, 0xaa, (size_t)allocation->sua_size);
Pekka Pessi's avatar
Pekka Pessi committed
826 827 828 829
#endif

      memset(allocation, 0, sizeof (*allocation));
      sub->sub_used--;
830 831 832

      if (preloaded)
	data = NULL;
Pekka Pessi's avatar
Pekka Pessi committed
833 834 835 836 837
    }

    UNLOCK(home);
  }

838
  safefree(data);
Pekka Pessi's avatar
Pekka Pessi committed
839 840
}

841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861
/** Check if pointer has been allocated through home.
 *
 * @param home   pointer to a memory home
 * @param data   pointer to a memory area possibly allocated though home
 */
int su_home_check_alloc(su_home_t const *home, void const *data)
{
  int retval = 0;

  if (home && data) {
    su_block_t const *sub = MEMLOCK(home);
    su_alloc_t *allocation = su_block_find(sub, data);

    retval = allocation != NULL;

    UNLOCK(home);
  }

  return retval;
}

Pekka Pessi's avatar
Pekka Pessi committed
862 863
/** Check home consistency.
 *
864 865 866
 * Ensures that the home structure and all memory blocks allocated through
 * it are consistent. It can be used to catch memory allocation and usage
 * errors.
Pekka Pessi's avatar
Pekka Pessi committed
867 868 869 870 871 872
 *
 * @param home Pointer to a memory home.
 */
void su_home_check(su_home_t const *home)
{
#if MEMCHECK != 0
873
  su_block_t const *b = MEMLOCK(home);
Pekka Pessi's avatar
Pekka Pessi committed
874 875 876 877 878 879 880 881 882 883 884 885 886

  su_home_check_blocks(b);

  UNLOCK(home);
#endif
}

/** Check home blocks. */
static
void su_home_check_blocks(su_block_t const *b)
{
#if MEMCHECK != 0
  if (b) {
887
    size_t i, used;
Pekka Pessi's avatar
Pekka Pessi committed
888 889 890 891 892
    assert(b->sub_used <= b->sub_n);

    for (i = 0, used = 0; i < b->sub_n; i++)
      if (b->sub_nodes[i].sua_data) {
	su_alloc_check(b, &b->sub_nodes[i]), used++;
893
	if (b->sub_nodes[i].sua_home)
Pekka Pessi's avatar
Pekka Pessi committed
894 895 896 897 898 899 900 901 902 903 904
	  su_home_check((su_home_t *)b->sub_nodes[i].sua_data);
      }

    assert(used == b->sub_used);
  }
#endif
}

/**
 * Create an su_home_t object.
 *
905 906
 * Creates a home object. A home object is used to collect multiple memory
 * allocations, so that they all can be freed by calling su_home_unref().
Pekka Pessi's avatar
Pekka Pessi committed
907
 *
908 909
 * @return This function returns a pointer to an #su_home_t object, or
 * NULL upon an error.
Pekka Pessi's avatar
Pekka Pessi committed
910 911 912
 */
su_home_t *su_home_create(void)
{
913
  return su_home_new(sizeof(su_home_t));
Pekka Pessi's avatar
Pekka Pessi committed
914 915
}

916
/** Destroy a home object
Pekka Pessi's avatar
Pekka Pessi committed
917
 *
918 919
 * Frees all memory blocks associated with a home object. Note that the home
 * object structure is not freed.
Pekka Pessi's avatar
Pekka Pessi committed
920
 *
921
 * @param home pointer to a home object
Pekka Pessi's avatar
Pekka Pessi committed
922 923
 *
 * @deprecated
924 925
 * su_home_destroy() is included for backwards compatibility only. Use
 * su_home_unref() instead of su_home_destroy().
Pekka Pessi's avatar
Pekka Pessi committed
926 927 928
 */
void su_home_destroy(su_home_t *home)
{
929 930 931 932 933 934 935 936 937
  if (MEMLOCK(home)) {
    assert(home->suh_blocks);
    assert(home->suh_blocks->sub_ref == 1);
    if (!home->suh_blocks->sub_hauto)
      /* should warn user */;
    home->suh_blocks->sub_hauto = 1;
    _su_home_deinit(home);
    /* UNLOCK(home); */
  }
Pekka Pessi's avatar
Pekka Pessi committed
938 939
}

940
/** Initialize an su_home_t struct.
Pekka Pessi's avatar
Pekka Pessi committed
941
 *
942 943 944
 * Initializes an su_home_t structure. It can be used when the home
 * structure is allocated from stack or when the home structure is part of
 * an another object.
Pekka Pessi's avatar
Pekka Pessi committed
945 946 947
 *
 * @param home pointer to home object
 *
948 949 950 951
 * @retval 0 when successful
 * @retval -1 upon an error.
 *
 * @sa SU_HOME_INIT(), su_home_deinit(), su_home_new(), su_home_clone()
952
 *
953
 * @bug
954 955 956
 * Prior to @VERSION_1_12_8 the su_home_t structure should have been
 * initialized with SU_HOME_INIT() or otherwise zeroed before calling
 * su_home_init().
Pekka Pessi's avatar
Pekka Pessi committed
957 958 959
 */
int su_home_init(su_home_t *home)
{
960
  su_block_t *sub;
961

Pekka Pessi's avatar
Pekka Pessi committed
962 963 964
  if (home == NULL)
    return -1;

965
  home->suh_blocks = sub = su_hash_alloc(SUB_N);
966 967
  home->suh_lock = NULL;

968 969
  if (!sub)
    return -1;
Pekka Pessi's avatar
Pekka Pessi committed
970

971
  return 0;
Pekka Pessi's avatar
Pekka Pessi committed
972 973 974 975 976 977
}

/** Internal deinitialization */
static
void _su_home_deinit(su_home_t *home)
{
978
  if (home->suh_blocks) {
979
    size_t i;
Pekka Pessi's avatar
Pekka Pessi committed
980 981
    su_block_t *b;

982
     if (home->suh_blocks->sub_destructor) {
983 984 985 986 987
      void (*destructor)(void *) = home->suh_blocks->sub_destructor;
      home->suh_blocks->sub_destructor = NULL;
      destructor(home);
    }

Pekka Pessi's avatar
Pekka Pessi committed
988 989 990 991 992 993
    b = home->suh_blocks;

    su_home_check_blocks(b);

    for (i = 0; i < b->sub_n; i++) {
      if (b->sub_nodes[i].sua_data) {
994 995
	if (b->sub_nodes[i].sua_home) {
	  su_home_t *subhome = b->sub_nodes[i].sua_data;
996 997 998 999 1000 1001 1002 1003 1004 1005
	  su_block_t *subb = MEMLOCK(subhome);

	  assert(subb); assert(subb->sub_ref >= 1);
#if 0
	  if (subb->sub_ref > 0)
	    SU_DEBUG_7(("su_home_unref: subhome %p with destructor %p has still %u refs\n",
			subhome, subb->sub_destructor, subb->sub_ref));
#endif
	  subb->sub_ref = 0;	/* zap them all */
	  _su_home_deinit(subhome);
1006 1007 1008
	}
	else if (su_is_preloaded(b, b->sub_nodes[i].sua_data))
	  continue;
1009
	safefree(b->sub_nodes[i].sua_data);
Pekka Pessi's avatar
Pekka Pessi committed
1010 1011 1012
      }
    }

Pekka Pessi's avatar
Pekka Pessi committed
1013
    if (b->sub_preload && !b->sub_preauto)
Pekka Pessi's avatar
Pekka Pessi committed
1014 1015 1016
      free(b->sub_preload);
    if (b->sub_stats)
      free(b->sub_stats);
Pekka Pessi's avatar
Pekka Pessi committed
1017 1018
    if (!b->sub_auto)
      free(b);
Pekka Pessi's avatar
Pekka Pessi committed
1019 1020

    home->suh_blocks = NULL;
1021

1022 1023 1024 1025 1026 1027 1028
    if (home->suh_lock) {
#ifdef WIN32
      UNLOCK(home); /* we must unlock here or windows leaks handles on the next call because the mutex is locked */
#endif
/* "In the LinuxThreads implementation, no resources are associated with mutex objects,
   thus pthread_mutex_destroy actually does nothing except checking that the mutex is unlocked. "
   In the Windows pthread implementation we must free the handles that are allocated */
1029
      _su_home_destroy_mutexes(home->suh_lock);
1030
    }
Pekka Pessi's avatar
Pekka Pessi committed
1031 1032
  }

1033
  home->suh_lock = NULL;
Pekka Pessi's avatar
Pekka Pessi committed
1034 1035 1036 1037
}

/** Free memory blocks allocated through home.
 *
1038 1039 1040
 * Frees the memory blocks associated with the home object allocated. It
 * does not free the home object itself. Use su_home_unref() to free the
 * home object.
Pekka Pessi's avatar
Pekka Pessi committed
1041 1042
 *
 * @param home pointer to home object
1043 1044
 *
 * @sa su_home_init()
Pekka Pessi's avatar
Pekka Pessi committed
1045 1046 1047
 */
void su_home_deinit(su_home_t *home)
{
1048
  if (MEMLOCK(home)) {
1049 1050 1051
    assert(home->suh_blocks);
    assert(home->suh_blocks->sub_ref == 1);
    assert(home->suh_blocks->sub_hauto);
Pekka Pessi's avatar
Pekka Pessi committed
1052
    _su_home_deinit(home);
1053 1054
    /* UNLOCK(home); */
  }
Pekka Pessi's avatar
Pekka Pessi committed
1055 1056 1057 1058
}

/**Move allocations from a su_home_t object to another.
 *
1059 1060 1061 1062 1063
 * Moves allocations made through the @a src home object under the @a dst
 * home object. It is handy, for example, if an operation allocates some
 * number of blocks that should be freed upon an error. It uses a temporary
 * home and moves the blocks from temporary to a proper home when
 * successful, but frees the temporary home upon an error.
Pekka Pessi's avatar
Pekka Pessi committed
1064
 *
1065
 * If @a src has destructor, it is called before starting to move.
1066
 *
Pekka Pessi's avatar
Pekka Pessi committed
1067 1068 1069 1070 1071 1072 1073 1074
 * @param dst destination home
 * @param src source home
 *
 * @retval 0 if succesful
 * @retval -1 upon an error
 */
int su_home_move(su_home_t *dst, su_home_t *src)
{
1075
  size_t i, n, n2, used;
Pekka Pessi's avatar
Pekka Pessi committed
1076 1077 1078 1079 1080 1081
  su_block_t *s, *d, *d2;

  if (src == NULL || dst == src)
    return 0;

  if (dst) {
1082
    s = MEMLOCK(src); d = MEMLOCK(dst);
Pekka Pessi's avatar
Pekka Pessi committed
1083 1084

    if (s && s->sub_n) {
1085 1086 1087 1088 1089 1090 1091

      if (s->sub_destructor) {
	void (*destructor)(void *) = s->sub_destructor;
	s->sub_destructor = NULL;
	destructor(src);
      }

1092
      if (d)
Pekka Pessi's avatar
Pekka Pessi committed
1093 1094 1095 1096
	used = s->sub_used + d->sub_used;
      else
	used = s->sub_used;

1097
      if (used && (d == NULL || 3 * used > 2 * d->sub_n)) {
Pekka Pessi's avatar
Pekka Pessi committed
1098 1099 1100 1101 1102
	if (d)
	  for (n = n2 = d->sub_n; 3 * used > 2 * n2; n2 = 4 * n2 + 3)
	    ;
	else
	  n = 0, n2 = s->sub_n;
1103 1104

	if (!(d2 = su_hash_alloc(n2))) {
Pekka Pessi's avatar
Pekka Pessi committed
1105 1106 1107 1108 1109 1110 1111
	  UNLOCK(dst); UNLOCK(src);
	  return -1;
	}

	dst->suh_blocks = d2;

      	for (i = 0; i < n; i++)
1112
	  if (d->sub_nodes[i].sua_data)
Pekka Pessi's avatar
Pekka Pessi committed
1113 1114 1115 1116
	    su_block_add(d2, d->sub_nodes[i].sua_data)[0] = d->sub_nodes[i];

	if (d) {
	  d2->sub_parent = d->sub_parent;
1117
	  d2->sub_ref = d->sub_ref;
Pekka Pessi's avatar
Pekka Pessi committed
1118 1119 1120
	  d2->sub_preload = d->sub_preload;
	  d2->sub_prsize = d->sub_prsize;
	  d2->sub_prused = d->sub_prused;
Pekka Pessi's avatar
Pekka Pessi committed
1121
	  d2->sub_preauto = d->sub_preauto;
Pekka Pessi's avatar
Pekka Pessi committed
1122 1123 1124
	  d2->sub_stats = d->sub_stats;
	}

Pekka Pessi's avatar
Pekka Pessi committed
1125 1126 1127 1128
	if (d && !d->sub_auto)
	  free(d);

	d = d2;
Pekka Pessi's avatar
Pekka Pessi committed
1129 1130
      }

1131 1132 1133
      if (s->sub_used) {
	n = s->sub_n;

Pekka Pessi's avatar
Pekka Pessi committed
1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144
	for (i = 0; i < n; i++)
	  if (s->sub_nodes[i].sua_data) {
	    su_block_add(d, s->sub_nodes[i].sua_data)[0] = s->sub_nodes[i];
	  }

	s->sub_used = 0;

	memset(s->sub_nodes, 0, n * sizeof (s->sub_nodes[0]));
      }

      if (s->sub_stats) {
1145
				/* XXX */
Pekka Pessi's avatar
Pekka Pessi committed
1146 1147 1148 1149 1150 1151
      }
    }

    UNLOCK(dst); UNLOCK(src);
  }
  else {
1152
    s = MEMLOCK(src);
Pekka Pessi's avatar
Pekka Pessi committed
1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166

    if (s && s->sub_used) {
      s->sub_used = 0;
      memset(s->sub_nodes, 0, s->sub_n * sizeof (s->sub_nodes[0]));
    }

    UNLOCK(src);
  }

  return 0;
}

/** Preload a memory home.
 *
1167
 * The function su_home_preload() preloads a memory home.
Pekka Pessi's avatar
Pekka Pessi committed
1168
 */
1169
void su_home_preload(su_home_t *home, isize_t n, isize_t isize)
Pekka Pessi's avatar
Pekka Pessi committed
1170 1171 1172 1173 1174 1175
{
  su_block_t *sub;

  if (home == NULL)
    return;

1176
  if (home->suh_blocks == NULL)
Pekka Pessi's avatar
Pekka Pessi committed
1177 1178
    su_home_init(home);

1179
  sub = MEMLOCK(home);
Pekka Pessi's avatar
Pekka Pessi committed
1180
  if (!sub->sub_preload) {
1181
    size_t size;
Pekka Pessi's avatar
Pekka Pessi committed
1182 1183 1184
    void *preload;

    size = n * ALIGN(isize);
1185 1186
    if (size > 65535)		/* We have 16 bits... */
      size = 65535 & (ALIGNMENT - 1);
Pekka Pessi's avatar
Pekka Pessi committed
1187 1188 1189 1190

    preload = malloc(size);

    home->suh_blocks->sub_preload = preload;
1191
    home->suh_blocks->sub_prsize = (unsigned)size;
Pekka Pessi's avatar
Pekka Pessi committed
1192 1193 1194 1195
  }
  UNLOCK(home);
}

Pekka Pessi's avatar
Pekka Pessi committed
1196 1197
/** Preload a memory home from stack.
 *
1198 1199
 * Initializes a memory home using an area allocated from stack. Poor man's
 * alloca().
Pekka Pessi's avatar
Pekka Pessi committed
1200
 */
1201
su_home_t *su_home_auto(void *area, isize_t size)
Pekka Pessi's avatar
Pekka Pessi committed
1202 1203 1204 1205
{
  su_home_t *home;
  su_block_t *sub;
  size_t homesize = ALIGN(sizeof *home);
1206
  size_t subsize = ALIGN(offsetof(su_block_t, sub_nodes[SUB_N_AUTO]));
1207
  size_t prepsize;
Pekka Pessi's avatar
Pekka Pessi committed
1208 1209 1210

  char *p = area;

1211 1212 1213
  prepsize = homesize + subsize + (ALIGN((intptr_t)p) - (intptr_t)p);

  if (area == NULL || size < prepsize)
Pekka Pessi's avatar
Pekka Pessi committed
1214 1215
    return NULL;

1216 1217 1218
  if (size > INT_MAX)
    size = INT_MAX;

Pekka Pessi's avatar
Pekka Pessi committed
1219
  home = memset(p, 0, homesize);
1220
  home->suh_size = (int)size;
Pekka Pessi's avatar
Pekka Pessi committed
1221 1222 1223 1224

  sub = memset(p + homesize, 0, subsize);
  home->suh_blocks = sub;

1225 1226 1227
  if (size > prepsize + 65535)
    size = prepsize + 65535;

1228
  sub->sub_n = SUB_N_AUTO;
1229
  sub->sub_ref = 1;
1230
  sub->sub_preload = p + prepsize;
1231
  sub->sub_prsize = (unsigned)(size - prepsize);
1232
  sub->sub_hauto = 1;
Pekka Pessi's avatar
Pekka Pessi committed
1233
  sub->sub_auto = 1;
1234
  sub->sub_preauto = 1;
Pekka Pessi's avatar
Pekka Pessi committed
1235 1236 1237 1238 1239 1240
  sub->sub_auto_all = 1;

  return home;
}


Pekka Pessi's avatar
Pekka Pessi committed
1241 1242
/** Reallocate a memory block.
 *
1243
 *   Allocates a memory block of @a size bytes.
Pekka Pessi's avatar
Pekka Pessi committed
1244 1245 1246 1247 1248 1249 1250 1251
 *   It copies the old block contents to the new block and frees the old
 *   block.
 *
 *   If @a home is NULL, this function behaves exactly like realloc().
 *
 *   @param home  pointer to memory pool object
 *   @param data  pointer to old memory block
 *   @param size  size of the memory block to be allocated
1252
 *
Pekka Pessi's avatar
Pekka Pessi committed
1253
 * @return
1254
 *   A pointer to the allocated memory block or
Pekka Pessi's avatar
Pekka Pessi committed
1255 1256
 *   NULL if an error occurred.
 */
1257
void *su_realloc(su_home_t *home, void *data, isize_t size)
Pekka Pessi's avatar
Pekka Pessi committed
1258 1259 1260 1261
{
  void *ndata;
  su_alloc_t *sua;
  su_block_t *sub;
1262
  size_t p;
1263
  size_t term = 0 - size;
Pekka Pessi's avatar
Pekka Pessi committed
1264

1265
  if (!home)
Pekka Pessi's avatar
Pekka Pessi committed
1266 1267 1268 1269 1270 1271 1272 1273
    return realloc(data, size);

  if (size == 0) {
    if (data)
      su_free(home, data);
    return NULL;
  }

1274
  sub = MEMLOCK(home);
1275 1276 1277 1278 1279 1280
  if (!data) {
    data = sub_alloc(home, sub, size, 0);
    UNLOCK(home);
    return data;
  }

Pekka Pessi's avatar
Pekka Pessi committed
1281 1282 1283
  sua = su_block_find(sub, data);

  if (!su_alloc_check(sub, sua))
1284
    return UNLOCK(home);
1285

1286 1287
  assert(!sua->sua_home);
  if (sua->sua_home)
1288
    return UNLOCK(home);
1289

Pekka Pessi's avatar
Pekka Pessi committed
1290 1291 1292 1293
  if (!su_is_preloaded(sub, data)) {
    ndata = realloc(data, size + MEMCHECK_EXTRA);
    if (ndata) {
      if (sub->sub_stats) {
1294 1295
	su_home_stats_free(sub, data, 0, sua->sua_size);
	su_home_stats_alloc(sub, data, 0, size, 1);
Pekka Pessi's avatar
Pekka Pessi committed
1296 1297 1298 1299 1300 1301 1302 1303 1304
      }

#if MEMCHECK_EXTRA
      memcpy((char *)ndata + size, &term, sizeof (term));
#else
      (void)term;
#endif
      memset(sua, 0, sizeof *sua);
      sub->sub_used--;
1305
      su_block_add(sub, ndata)->sua_size = (unsigned)size;
Pekka Pessi's avatar
Pekka Pessi committed
1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316
    }
    UNLOCK(home);

    return ndata;
  }

  p = (char *)data - home->suh_blocks->sub_preload;
  p += sua->sua_size + MEMCHECK_EXTRA;
  p = ALIGN(p);

  if (p == sub->sub_prused) {
1317
    size_t p2 = (char *)data - sub->sub_preload + size + MEMCHECK_EXTRA;
Pekka Pessi's avatar
Pekka Pessi committed
1318 1319 1320
    p2 = ALIGN(p2);
    if (p2 <= sub->sub_prsize) {
      /* Extend/reduce existing preload */
1321 1322 1323 1324 1325
      if (sub->sub_stats) {
	su_home_stats_free(sub, data, data, sua->sua_size);
	su_home_stats_alloc(sub, data, data, size, 0);
      }

1326
      sub->sub_prused = (unsigned)p2;
1327
      sua->sua_size = (unsigned)size;
1328

Pekka Pessi's avatar
Pekka Pessi committed
1329 1330 1331 1332 1333 1334 1335
#if MEMCHECK_EXTRA
      memcpy((char *)data + size, &term, sizeof (term));
#endif
      UNLOCK(home);
      return data;
    }
  }
1336
  else if (size < (size_t)sua->sua_size) {
Pekka Pessi's avatar
Pekka Pessi committed
1337
    /* Reduce existing preload */
1338 1339 1340 1341
    if (sub->sub_stats) {
      su_home_stats_free(sub, data, data, sua->sua_size);
      su_home_stats_alloc(sub, data, data, size, 0);
    }
Pekka Pessi's avatar
Pekka Pessi committed
1342 1343 1344
#if MEMCHECK_EXTRA
    memcpy((char *)data + size, &term, sizeof (term));
#endif
1345
    sua->sua_size = (unsigned)size;
Pekka Pessi's avatar
Pekka Pessi committed
1346 1347 1348 1349 1350 1351 1352
    UNLOCK(home);
    return data;
  }

  ndata = malloc(size + MEMCHECK_EXTRA);

  if (ndata) {
1353
    if (p == sub->sub_prused) {
Pekka Pessi's avatar
Pekka Pessi committed
1354 1355
      /* Free preload */
      sub->sub_prused = (char *)data - home->suh_blocks->sub_preload;
1356 1357 1358
      if (sub->sub_stats)
	su_home_stats_free(sub, data, data, sua->sua_size);
    }
1359

1360 1361 1362 1363
    memcpy(ndata, data,
	   (size_t)sua->sua_size < size
	   ? (size_t)sua->sua_size
	   : size);
Pekka Pessi's avatar
Pekka Pessi committed
1364 1365 1366 1367
#if MEMCHECK_EXTRA
    memcpy((char *)ndata + size, &term, sizeof (term));
#endif