os_alloc.c 9.28 KB
Newer Older
1 2 3
/*-
 * See the file LICENSE for redistribution information.
 *
jimw@mysql.com's avatar
jimw@mysql.com committed
4
 * Copyright (c) 1997-2005
5
 *	Sleepycat Software.  All rights reserved.
jimw@mysql.com's avatar
jimw@mysql.com committed
6
 *
jimw@mysql.com's avatar
jimw@mysql.com committed
7
 * $Id: os_alloc.c,v 12.2 2005/06/16 20:23:23 bostic Exp $
8 9 10 11 12 13 14 15 16 17 18 19 20 21
 */

#include "db_config.h"

#ifndef NO_SYSTEM_INCLUDES
#include <sys/types.h>

#include <stdlib.h>
#include <string.h>
#endif

#include "db_int.h"

#ifdef DIAGNOSTIC
ram@mysql.r18.ru's avatar
ram@mysql.r18.ru committed
22 23
static void __os_guard __P((DB_ENV *));

jimw@mysql.com's avatar
jimw@mysql.com committed
24
union __db_allocinfo {
ram@mysql.r18.ru's avatar
ram@mysql.r18.ru committed
25 26 27
	size_t size;
	double align;
};
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
#endif

/*
 * !!!
 * Correct for systems that return NULL when you allocate 0 bytes of memory.
 * There are several places in DB where we allocate the number of bytes held
 * by the key/data item, and it can be 0.  Correct here so that malloc never
 * returns a NULL for that reason (which behavior is permitted by ANSI).  We
 * could make these calls macros on non-Alpha architectures (that's where we
 * saw the problem), but it's probably not worth the autoconf complexity.
 *
 * !!!
 * Correct for systems that don't set errno when malloc and friends fail.
 *
 *	Out of memory.
 *	We wish to hold the whole sky,
 *	But we never will.
 */

ram@mysql.r18.ru's avatar
ram@mysql.r18.ru committed
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
/*
 * __os_umalloc --
 *	A malloc(3) function that will use, in order of preference,
 *	the allocation function specified to the DB handle, the DB_ENV
 *	handle, or __os_malloc.
 *
 * PUBLIC: int __os_umalloc __P((DB_ENV *, size_t, void *));
 */
int
__os_umalloc(dbenv, size, storep)
	DB_ENV *dbenv;
	size_t size;
	void *storep;
{
	int ret;

	/* Never allocate 0 bytes -- some C libraries don't like it. */
	if (size == 0)
		++size;

	if (dbenv == NULL || dbenv->db_malloc == NULL) {
		if (DB_GLOBAL(j_malloc) != NULL)
			*(void **)storep = DB_GLOBAL(j_malloc)(size);
		else
			*(void **)storep = malloc(size);
		if (*(void **)storep == NULL) {
			/*
			 *  Correct error return, see __os_malloc.
			 */
jimw@mysql.com's avatar
jimw@mysql.com committed
76
			if ((ret = __os_get_errno_ret_zero()) == 0) {
ram@mysql.r18.ru's avatar
ram@mysql.r18.ru committed
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
				ret = ENOMEM;
				__os_set_errno(ENOMEM);
			}
			__db_err(dbenv,
			    "malloc: %s: %lu", strerror(ret), (u_long)size);
			return (ret);
		}
		return (0);
	}

	if ((*(void **)storep = dbenv->db_malloc(size)) == NULL) {
		__db_err(dbenv, "User-specified malloc function returned NULL");
		return (ENOMEM);
	}

	return (0);
}

/*
 * __os_urealloc --
 *	realloc(3) counterpart to __os_umalloc.
 *
 * PUBLIC: int __os_urealloc __P((DB_ENV *, size_t, void *));
 */
int
__os_urealloc(dbenv, size, storep)
	DB_ENV *dbenv;
	size_t size;
	void *storep;
{
	int ret;
	void *ptr;

	ptr = *(void **)storep;

	/* Never allocate 0 bytes -- some C libraries don't like it. */
	if (size == 0)
		++size;

	if (dbenv == NULL || dbenv->db_realloc == NULL) {
		if (ptr == NULL)
			return (__os_umalloc(dbenv, size, storep));

		if (DB_GLOBAL(j_realloc) != NULL)
			*(void **)storep = DB_GLOBAL(j_realloc)(ptr, size);
		else
			*(void **)storep = realloc(ptr, size);
		if (*(void **)storep == NULL) {
			/*
			 * Correct errno, see __os_realloc.
			 */
jimw@mysql.com's avatar
jimw@mysql.com committed
128
			if ((ret = __os_get_errno_ret_zero()) == 0) {
ram@mysql.r18.ru's avatar
ram@mysql.r18.ru committed
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
				ret = ENOMEM;
				__os_set_errno(ENOMEM);
			}
			__db_err(dbenv,
			    "realloc: %s: %lu", strerror(ret), (u_long)size);
			return (ret);
		}
		return (0);
	}

	if ((*(void **)storep = dbenv->db_realloc(ptr, size)) == NULL) {
		__db_err(dbenv,
		    "User-specified realloc function returned NULL");
		return (ENOMEM);
	}

	return (0);
}

/*
 * __os_ufree --
 *	free(3) counterpart to __os_umalloc.
 *
jimw@mysql.com's avatar
jimw@mysql.com committed
152
 * PUBLIC: void __os_ufree __P((DB_ENV *, void *));
ram@mysql.r18.ru's avatar
ram@mysql.r18.ru committed
153
 */
jimw@mysql.com's avatar
jimw@mysql.com committed
154
void
ram@mysql.r18.ru's avatar
ram@mysql.r18.ru committed
155 156 157 158 159 160 161 162 163 164 165 166
__os_ufree(dbenv, ptr)
	DB_ENV *dbenv;
	void *ptr;
{
	if (dbenv != NULL && dbenv->db_free != NULL)
		dbenv->db_free(ptr);
	else if (DB_GLOBAL(j_free) != NULL)
		DB_GLOBAL(j_free)(ptr);
	else
		free(ptr);
}

167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185
/*
 * __os_strdup --
 *	The strdup(3) function for DB.
 *
 * PUBLIC: int __os_strdup __P((DB_ENV *, const char *, void *));
 */
int
__os_strdup(dbenv, str, storep)
	DB_ENV *dbenv;
	const char *str;
	void *storep;
{
	size_t size;
	int ret;
	void *p;

	*(void **)storep = NULL;

	size = strlen(str) + 1;
ram@mysql.r18.ru's avatar
ram@mysql.r18.ru committed
186
	if ((ret = __os_malloc(dbenv, size, &p)) != 0)
187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210
		return (ret);

	memcpy(p, str, size);

	*(void **)storep = p;
	return (0);
}

/*
 * __os_calloc --
 *	The calloc(3) function for DB.
 *
 * PUBLIC: int __os_calloc __P((DB_ENV *, size_t, size_t, void *));
 */
int
__os_calloc(dbenv, num, size, storep)
	DB_ENV *dbenv;
	size_t num, size;
	void *storep;
{
	void *p;
	int ret;

	size *= num;
ram@mysql.r18.ru's avatar
ram@mysql.r18.ru committed
211
	if ((ret = __os_malloc(dbenv, size, &p)) != 0)
212 213 214 215 216 217 218 219 220 221 222 223
		return (ret);

	memset(p, 0, size);

	*(void **)storep = p;
	return (0);
}

/*
 * __os_malloc --
 *	The malloc(3) function for DB.
 *
ram@mysql.r18.ru's avatar
ram@mysql.r18.ru committed
224
 * PUBLIC: int __os_malloc __P((DB_ENV *, size_t, void *));
225 226
 */
int
ram@mysql.r18.ru's avatar
ram@mysql.r18.ru committed
227
__os_malloc(dbenv, size, storep)
228 229
	DB_ENV *dbenv;
	size_t size;
ram@mysql.r18.ru's avatar
ram@mysql.r18.ru committed
230
	void *storep;
231 232 233 234 235 236 237 238 239
{
	int ret;
	void *p;

	*(void **)storep = NULL;

	/* Never allocate 0 bytes -- some C libraries don't like it. */
	if (size == 0)
		++size;
ram@mysql.r18.ru's avatar
ram@mysql.r18.ru committed
240

241
#ifdef DIAGNOSTIC
ram@mysql.r18.ru's avatar
ram@mysql.r18.ru committed
242
	/* Add room for size and a guard byte. */
jimw@mysql.com's avatar
jimw@mysql.com committed
243
	size += sizeof(union __db_allocinfo) + 1;
244 245
#endif

ram@mysql.r18.ru's avatar
ram@mysql.r18.ru committed
246 247
	if (DB_GLOBAL(j_malloc) != NULL)
		p = DB_GLOBAL(j_malloc)(size);
248 249 250
	else
		p = malloc(size);
	if (p == NULL) {
ram@mysql.r18.ru's avatar
ram@mysql.r18.ru committed
251 252 253 254 255 256
		/*
		 * Some C libraries don't correctly set errno when malloc(3)
		 * fails.  We'd like to 0 out errno before calling malloc,
		 * but it turns out that setting errno is quite expensive on
		 * Windows/NT in an MT environment.
		 */
jimw@mysql.com's avatar
jimw@mysql.com committed
257
		if ((ret = __os_get_errno_ret_zero()) == 0) {
258
			ret = ENOMEM;
ram@mysql.r18.ru's avatar
ram@mysql.r18.ru committed
259
			__os_set_errno(ENOMEM);
260 261 262 263 264 265 266
		}
		__db_err(dbenv,
		    "malloc: %s: %lu", strerror(ret), (u_long)size);
		return (ret);
	}

#ifdef DIAGNOSTIC
jimw@mysql.com's avatar
jimw@mysql.com committed
267 268 269
	/* Overwrite memory. */
	memset(p, CLEAR_BYTE, size);

270 271 272
	/*
	 * Guard bytes: if #DIAGNOSTIC is defined, we allocate an additional
	 * byte after the memory and set it to a special value that we check
ram@mysql.r18.ru's avatar
ram@mysql.r18.ru committed
273
	 * for when the memory is free'd.
274
	 */
ram@mysql.r18.ru's avatar
ram@mysql.r18.ru committed
275 276
	((u_int8_t *)p)[size - 1] = CLEAR_BYTE;

jimw@mysql.com's avatar
jimw@mysql.com committed
277 278
	((union __db_allocinfo *)p)->size = size;
	p = &((union __db_allocinfo *)p)[1];
279 280 281 282 283 284 285 286 287 288
#endif
	*(void **)storep = p;

	return (0);
}

/*
 * __os_realloc --
 *	The realloc(3) function for DB.
 *
ram@mysql.r18.ru's avatar
ram@mysql.r18.ru committed
289
 * PUBLIC: int __os_realloc __P((DB_ENV *, size_t, void *));
290 291
 */
int
ram@mysql.r18.ru's avatar
ram@mysql.r18.ru committed
292
__os_realloc(dbenv, size, storep)
293 294
	DB_ENV *dbenv;
	size_t size;
ram@mysql.r18.ru's avatar
ram@mysql.r18.ru committed
295
	void *storep;
296 297 298 299 300 301 302 303 304
{
	int ret;
	void *p, *ptr;

	ptr = *(void **)storep;

	/* Never allocate 0 bytes -- some C libraries don't like it. */
	if (size == 0)
		++size;
ram@mysql.r18.ru's avatar
ram@mysql.r18.ru committed
305 306 307 308 309

	/* If we haven't yet allocated anything yet, simply call malloc. */
	if (ptr == NULL)
		return (__os_malloc(dbenv, size, storep));

310
#ifdef DIAGNOSTIC
ram@mysql.r18.ru's avatar
ram@mysql.r18.ru committed
311
	/* Add room for size and a guard byte. */
jimw@mysql.com's avatar
jimw@mysql.com committed
312
	size += sizeof(union __db_allocinfo) + 1;
ram@mysql.r18.ru's avatar
ram@mysql.r18.ru committed
313

jimw@mysql.com's avatar
jimw@mysql.com committed
314 315
	/* Back up to the real beginning */
	ptr = &((union __db_allocinfo *)ptr)[-1];
jimw@mysql.com's avatar
jimw@mysql.com committed
316 317 318 319 320 321 322 323

	{
		size_t s;

		s = ((union __db_allocinfo *)ptr)->size;
		if (((u_int8_t *)ptr)[s - 1] != CLEAR_BYTE)
			 __os_guard(dbenv);
	}
324 325 326 327 328 329
#endif

	/*
	 * Don't overwrite the original pointer, there are places in DB we
	 * try to continue after realloc fails.
	 */
ram@mysql.r18.ru's avatar
ram@mysql.r18.ru committed
330 331
	if (DB_GLOBAL(j_realloc) != NULL)
		p = DB_GLOBAL(j_realloc)(ptr, size);
332 333 334
	else
		p = realloc(ptr, size);
	if (p == NULL) {
ram@mysql.r18.ru's avatar
ram@mysql.r18.ru committed
335 336 337 338 339 340
		/*
		 * Some C libraries don't correctly set errno when malloc(3)
		 * fails.  We'd like to 0 out errno before calling malloc,
		 * but it turns out that setting errno is quite expensive on
		 * Windows/NT in an MT environment.
		 */
jimw@mysql.com's avatar
jimw@mysql.com committed
341
		if ((ret = __os_get_errno_ret_zero()) == 0) {
342 343 344 345 346 347 348 349 350
			ret = ENOMEM;
			__os_set_errno(ENOMEM);
		}
		__db_err(dbenv,
		    "realloc: %s: %lu", strerror(ret), (u_long)size);
		return (ret);
	}
#ifdef DIAGNOSTIC
	((u_int8_t *)p)[size - 1] = CLEAR_BYTE;	/* Initialize guard byte. */
ram@mysql.r18.ru's avatar
ram@mysql.r18.ru committed
351

jimw@mysql.com's avatar
jimw@mysql.com committed
352 353
	((union __db_allocinfo *)p)->size = size;
	p = &((union __db_allocinfo *)p)[1];
354 355 356 357 358 359 360 361 362 363 364
#endif

	*(void **)storep = p;

	return (0);
}

/*
 * __os_free --
 *	The free(3) function for DB.
 *
ram@mysql.r18.ru's avatar
ram@mysql.r18.ru committed
365
 * PUBLIC: void __os_free __P((DB_ENV *, void *));
366 367
 */
void
ram@mysql.r18.ru's avatar
ram@mysql.r18.ru committed
368 369
__os_free(dbenv, ptr)
	DB_ENV *dbenv;
370 371 372
	void *ptr;
{
#ifdef DIAGNOSTIC
jimw@mysql.com's avatar
jimw@mysql.com committed
373
	size_t size;
374 375 376 377
	/*
	 * Check that the guard byte (one past the end of the memory) is
	 * still CLEAR_BYTE.
	 */
ram@mysql.r18.ru's avatar
ram@mysql.r18.ru committed
378 379 380
	if (ptr == NULL)
		return;

jimw@mysql.com's avatar
jimw@mysql.com committed
381 382
	ptr = &((union __db_allocinfo *)ptr)[-1];
	size = ((union __db_allocinfo *)ptr)->size;
ram@mysql.r18.ru's avatar
ram@mysql.r18.ru committed
383 384
	if (((u_int8_t *)ptr)[size - 1] != CLEAR_BYTE)
		 __os_guard(dbenv);
385

jimw@mysql.com's avatar
jimw@mysql.com committed
386
	/* Overwrite memory. */
ram@mysql.r18.ru's avatar
ram@mysql.r18.ru committed
387 388
	if (size != 0)
		memset(ptr, CLEAR_BYTE, size);
389
#endif
ram@mysql.r18.ru's avatar
ram@mysql.r18.ru committed
390
	COMPQUIET(dbenv, NULL);
391

ram@mysql.r18.ru's avatar
ram@mysql.r18.ru committed
392 393
	if (DB_GLOBAL(j_free) != NULL)
		DB_GLOBAL(j_free)(ptr);
394 395 396 397 398 399 400 401 402 403
	else
		free(ptr);
}

#ifdef DIAGNOSTIC
/*
 * __os_guard --
 *	Complain and abort.
 */
static void
ram@mysql.r18.ru's avatar
ram@mysql.r18.ru committed
404 405
__os_guard(dbenv)
	DB_ENV *dbenv;
406
{
ram@mysql.r18.ru's avatar
ram@mysql.r18.ru committed
407
	__db_err(dbenv, "Guard byte incorrect during free");
408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449
	abort();
	/* NOTREACHED */
}
#endif

/*
 * __ua_memcpy --
 *	Copy memory to memory without relying on any kind of alignment.
 *
 *	There are places in DB that we have unaligned data, for example,
 *	when we've stored a structure in a log record as a DBT, and now
 *	we want to look at it.  Unfortunately, if you have code like:
 *
 *		struct a {
 *			int x;
 *		} *p;
 *
 *		void *func_argument;
 *		int local;
 *
 *		p = (struct a *)func_argument;
 *		memcpy(&local, p->x, sizeof(local));
 *
 *	compilers optimize to use inline instructions requiring alignment,
 *	and records in the log don't have any particular alignment.  (This
 *	isn't a compiler bug, because it's a structure they're allowed to
 *	assume alignment.)
 *
 *	Casting the memcpy arguments to (u_int8_t *) appears to work most
 *	of the time, but we've seen examples where it wasn't sufficient
 *	and there's nothing in ANSI C that requires that work.
 *
 * PUBLIC: void *__ua_memcpy __P((void *, const void *, size_t));
 */
void *
__ua_memcpy(dst, src, len)
	void *dst;
	const void *src;
	size_t len;
{
	return ((void *)memcpy(dst, src, len));
}