mirror of
https://github.com/MariaDB/server.git
synced 2025-01-17 20:42:30 +01:00
1d1dc31a06
symbols. Use it for all definitions of non-static variables and functions. lexyy.c, make_flex.sh: Declare yylex as UNIV_INTERN, not static. It is referenced from pars0grm.c. Actually, according to nm .libs/ha_innodb.so|grep -w '[ABCE-TVXYZ]' the following symbols are still global: * The vtable for class ha_innodb * pars0grm.c: The function yyparse() and the variables yychar, yylval, yynerrs The required changes to the Bison-generated file pars0grm.c will be addressed in a separate commit, which will add a script similar to make_flex.sh. The class ha_innodb is renamed from class ha_innobase by a #define. Thus, there will be no clash with the builtin InnoDB. However, there will be some overhead for invoking virtual methods of class ha_innodb. Ideas for making the vtable hidden are welcome. -fvisibility=hidden is not available in GCC 3.
630 lines
16 KiB
Text
630 lines
16 KiB
Text
/************************************************************************
|
|
The memory management
|
|
|
|
(c) 1994, 1995 Innobase Oy
|
|
|
|
Created 6/8/1994 Heikki Tuuri
|
|
*************************************************************************/
|
|
|
|
#include "mem0dbg.ic"
|
|
|
|
#include "mem0pool.h"
|
|
|
|
/*******************************************************************
|
|
Creates a memory heap block where data can be allocated. */
|
|
UNIV_INTERN
|
|
mem_block_t*
|
|
mem_heap_create_block(
|
|
/*==================*/
|
|
/* out, own: memory heap block, NULL if
|
|
did not succeed (only possible for
|
|
MEM_HEAP_BTR_SEARCH type heaps) */
|
|
mem_heap_t* heap, /* in: memory heap or NULL if first block
|
|
should be created */
|
|
ulint n, /* in: number of bytes needed for user data */
|
|
ulint type, /* in: type of heap: MEM_HEAP_DYNAMIC or
|
|
MEM_HEAP_BUFFER */
|
|
const char* file_name,/* in: file name where created */
|
|
ulint line); /* in: line where created */
|
|
/**********************************************************************
|
|
Frees a block from a memory heap. */
|
|
UNIV_INTERN
|
|
void
|
|
mem_heap_block_free(
|
|
/*================*/
|
|
mem_heap_t* heap, /* in: heap */
|
|
mem_block_t* block); /* in: block to free */
|
|
/**********************************************************************
|
|
Frees the free_block field from a memory heap. */
|
|
UNIV_INTERN
|
|
void
|
|
mem_heap_free_block_free(
|
|
/*=====================*/
|
|
mem_heap_t* heap); /* in: heap */
|
|
/*******************************************************************
|
|
Adds a new block to a memory heap. */
|
|
UNIV_INTERN
|
|
mem_block_t*
|
|
mem_heap_add_block(
|
|
/*===============*/
|
|
/* out: created block, NULL if did not
|
|
succeed (only possible for
|
|
MEM_HEAP_BTR_SEARCH type heaps)*/
|
|
mem_heap_t* heap, /* in: memory heap */
|
|
ulint n); /* in: number of bytes user needs */
|
|
|
|
UNIV_INLINE
|
|
void
|
|
mem_block_set_len(mem_block_t* block, ulint len)
|
|
{
|
|
ut_ad(len > 0);
|
|
|
|
block->len = len;
|
|
}
|
|
|
|
UNIV_INLINE
|
|
ulint
|
|
mem_block_get_len(mem_block_t* block)
|
|
{
|
|
return(block->len);
|
|
}
|
|
|
|
UNIV_INLINE
|
|
void
|
|
mem_block_set_type(mem_block_t* block, ulint type)
|
|
{
|
|
ut_ad((type == MEM_HEAP_DYNAMIC) || (type == MEM_HEAP_BUFFER)
|
|
|| (type == MEM_HEAP_BUFFER + MEM_HEAP_BTR_SEARCH));
|
|
|
|
block->type = type;
|
|
}
|
|
|
|
UNIV_INLINE
|
|
ulint
|
|
mem_block_get_type(mem_block_t* block)
|
|
{
|
|
return(block->type);
|
|
}
|
|
|
|
UNIV_INLINE
|
|
void
|
|
mem_block_set_free(mem_block_t* block, ulint free)
|
|
{
|
|
ut_ad(free > 0);
|
|
ut_ad(free <= mem_block_get_len(block));
|
|
|
|
block->free = free;
|
|
}
|
|
|
|
UNIV_INLINE
|
|
ulint
|
|
mem_block_get_free(mem_block_t* block)
|
|
{
|
|
return(block->free);
|
|
}
|
|
|
|
UNIV_INLINE
|
|
void
|
|
mem_block_set_start(mem_block_t* block, ulint start)
|
|
{
|
|
ut_ad(start > 0);
|
|
|
|
block->start = start;
|
|
}
|
|
|
|
UNIV_INLINE
|
|
ulint
|
|
mem_block_get_start(mem_block_t* block)
|
|
{
|
|
return(block->start);
|
|
}
|
|
|
|
/*******************************************************************
|
|
Allocates and zero-fills n bytes of memory from a memory heap. */
|
|
UNIV_INLINE
|
|
void*
|
|
mem_heap_zalloc(
|
|
/*============*/
|
|
/* out: allocated, zero-filled storage */
|
|
mem_heap_t* heap, /* in: memory heap */
|
|
ulint n) /* in: number of bytes; if the heap is allowed
|
|
to grow into the buffer pool, this must be
|
|
<= MEM_MAX_ALLOC_IN_BUF */
|
|
{
|
|
ut_ad(heap);
|
|
ut_ad(!(heap->type & MEM_HEAP_BTR_SEARCH));
|
|
return(memset(mem_heap_alloc(heap, n), 0, n));
|
|
}
|
|
|
|
/*******************************************************************
|
|
Allocates n bytes of memory from a memory heap. */
|
|
UNIV_INLINE
|
|
void*
|
|
mem_heap_alloc(
|
|
/*===========*/
|
|
/* out: allocated storage, NULL if did not
|
|
succeed (only possible for
|
|
MEM_HEAP_BTR_SEARCH type heaps) */
|
|
mem_heap_t* heap, /* in: memory heap */
|
|
ulint n) /* in: number of bytes; if the heap is allowed
|
|
to grow into the buffer pool, this must be
|
|
<= MEM_MAX_ALLOC_IN_BUF */
|
|
{
|
|
mem_block_t* block;
|
|
void* buf;
|
|
ulint free;
|
|
|
|
ut_ad(mem_heap_check(heap));
|
|
|
|
block = UT_LIST_GET_LAST(heap->base);
|
|
|
|
ut_ad(!(block->type & MEM_HEAP_BUFFER) || (n <= MEM_MAX_ALLOC_IN_BUF));
|
|
|
|
/* Check if there is enough space in block. If not, create a new
|
|
block to the heap */
|
|
|
|
if (mem_block_get_len(block)
|
|
< mem_block_get_free(block) + MEM_SPACE_NEEDED(n)) {
|
|
|
|
block = mem_heap_add_block(heap, n);
|
|
|
|
if (block == NULL) {
|
|
|
|
return(NULL);
|
|
}
|
|
}
|
|
|
|
free = mem_block_get_free(block);
|
|
|
|
buf = (byte*)block + free;
|
|
|
|
mem_block_set_free(block, free + MEM_SPACE_NEEDED(n));
|
|
|
|
#ifdef UNIV_MEM_DEBUG
|
|
UNIV_MEM_ALLOC(buf,
|
|
n + MEM_FIELD_HEADER_SIZE + MEM_FIELD_TRAILER_SIZE);
|
|
|
|
/* In the debug version write debugging info to the field */
|
|
mem_field_init((byte*)buf, n);
|
|
|
|
/* Advance buf to point at the storage which will be given to the
|
|
caller */
|
|
buf = (byte*)buf + MEM_FIELD_HEADER_SIZE;
|
|
|
|
#endif
|
|
#ifdef UNIV_SET_MEM_TO_ZERO
|
|
UNIV_MEM_ALLOC(buf, n);
|
|
memset(buf, '\0', n);
|
|
#endif
|
|
UNIV_MEM_ALLOC(buf, n);
|
|
return(buf);
|
|
}
|
|
|
|
/*********************************************************************
|
|
Returns a pointer to the heap top. */
|
|
UNIV_INLINE
|
|
byte*
|
|
mem_heap_get_heap_top(
|
|
/*==================*/
|
|
/* out: pointer to the heap top */
|
|
mem_heap_t* heap) /* in: memory heap */
|
|
{
|
|
mem_block_t* block;
|
|
byte* buf;
|
|
|
|
ut_ad(mem_heap_check(heap));
|
|
|
|
block = UT_LIST_GET_LAST(heap->base);
|
|
|
|
buf = (byte*)block + mem_block_get_free(block);
|
|
|
|
return(buf);
|
|
}
|
|
|
|
/*********************************************************************
|
|
Frees the space in a memory heap exceeding the pointer given. The
|
|
pointer must have been acquired from mem_heap_get_heap_top. The first
|
|
memory block of the heap is not freed. */
|
|
UNIV_INLINE
|
|
void
|
|
mem_heap_free_heap_top(
|
|
/*===================*/
|
|
mem_heap_t* heap, /* in: heap from which to free */
|
|
byte* old_top)/* in: pointer to old top of heap */
|
|
{
|
|
mem_block_t* block;
|
|
mem_block_t* prev_block;
|
|
#ifdef UNIV_MEM_DEBUG
|
|
ibool error;
|
|
ulint total_size;
|
|
ulint size;
|
|
#endif
|
|
|
|
ut_ad(mem_heap_check(heap));
|
|
|
|
#ifdef UNIV_MEM_DEBUG
|
|
|
|
/* Validate the heap and get its total allocated size */
|
|
mem_heap_validate_or_print(heap, NULL, FALSE, &error, &total_size,
|
|
NULL, NULL);
|
|
ut_a(!error);
|
|
|
|
/* Get the size below top pointer */
|
|
mem_heap_validate_or_print(heap, old_top, FALSE, &error, &size, NULL,
|
|
NULL);
|
|
ut_a(!error);
|
|
|
|
#endif
|
|
|
|
block = UT_LIST_GET_LAST(heap->base);
|
|
|
|
while (block != NULL) {
|
|
if (((byte*)block + mem_block_get_free(block) >= old_top)
|
|
&& ((byte*)block <= old_top)) {
|
|
/* Found the right block */
|
|
|
|
break;
|
|
}
|
|
|
|
/* Store prev_block value before freeing the current block
|
|
(the current block will be erased in freeing) */
|
|
|
|
prev_block = UT_LIST_GET_PREV(list, block);
|
|
|
|
mem_heap_block_free(heap, block);
|
|
|
|
block = prev_block;
|
|
}
|
|
|
|
ut_ad(block);
|
|
|
|
/* Set the free field of block */
|
|
mem_block_set_free(block, old_top - (byte*)block);
|
|
|
|
#ifdef UNIV_MEM_DEBUG
|
|
ut_ad(mem_block_get_start(block) <= mem_block_get_free(block));
|
|
|
|
/* In the debug version erase block from top up */
|
|
mem_erase_buf(old_top, (byte*)block + block->len - old_top);
|
|
|
|
/* Update allocated memory count */
|
|
mutex_enter(&mem_hash_mutex);
|
|
mem_current_allocated_memory -= (total_size - size);
|
|
mutex_exit(&mem_hash_mutex);
|
|
#else /* UNIV_MEM_DEBUG */
|
|
UNIV_MEM_ASSERT_W(old_top, (byte*)block + block->len - old_top);
|
|
#endif /* UNIV_MEM_DEBUG */
|
|
UNIV_MEM_ALLOC(old_top, (byte*)block + block->len - old_top);
|
|
|
|
/* If free == start, we may free the block if it is not the first
|
|
one */
|
|
|
|
if ((heap != block) && (mem_block_get_free(block)
|
|
== mem_block_get_start(block))) {
|
|
mem_heap_block_free(heap, block);
|
|
}
|
|
}
|
|
|
|
/*********************************************************************
|
|
Empties a memory heap. The first memory block of the heap is not freed. */
|
|
UNIV_INLINE
|
|
void
|
|
mem_heap_empty(
|
|
/*===========*/
|
|
mem_heap_t* heap) /* in: heap to empty */
|
|
{
|
|
mem_heap_free_heap_top(heap, (byte*)heap + mem_block_get_start(heap));
|
|
|
|
if (heap->free_block) {
|
|
mem_heap_free_block_free(heap);
|
|
}
|
|
}
|
|
|
|
/*********************************************************************
|
|
Returns a pointer to the topmost element in a memory heap. The size of the
|
|
element must be given. */
|
|
UNIV_INLINE
|
|
void*
|
|
mem_heap_get_top(
|
|
/*=============*/
|
|
/* out: pointer to the topmost element */
|
|
mem_heap_t* heap, /* in: memory heap */
|
|
ulint n) /* in: size of the topmost element */
|
|
{
|
|
mem_block_t* block;
|
|
void* buf;
|
|
|
|
ut_ad(mem_heap_check(heap));
|
|
|
|
block = UT_LIST_GET_LAST(heap->base);
|
|
|
|
buf = (byte*)block + mem_block_get_free(block) - MEM_SPACE_NEEDED(n);
|
|
|
|
#ifdef UNIV_MEM_DEBUG
|
|
ut_ad(mem_block_get_start(block) <=(ulint)((byte*)buf - (byte*)block));
|
|
|
|
/* In the debug version, advance buf to point at the storage which
|
|
was given to the caller in the allocation*/
|
|
|
|
buf = (byte*)buf + MEM_FIELD_HEADER_SIZE;
|
|
|
|
/* Check that the field lengths agree */
|
|
ut_ad(n == (ulint)mem_field_header_get_len(buf));
|
|
#endif
|
|
|
|
return(buf);
|
|
}
|
|
|
|
/*********************************************************************
|
|
Frees the topmost element in a memory heap. The size of the element must be
|
|
given. */
|
|
UNIV_INLINE
|
|
void
|
|
mem_heap_free_top(
|
|
/*==============*/
|
|
mem_heap_t* heap, /* in: memory heap */
|
|
ulint n) /* in: size of the topmost element */
|
|
{
|
|
mem_block_t* block;
|
|
|
|
ut_ad(mem_heap_check(heap));
|
|
|
|
block = UT_LIST_GET_LAST(heap->base);
|
|
|
|
/* Subtract the free field of block */
|
|
mem_block_set_free(block, mem_block_get_free(block)
|
|
- MEM_SPACE_NEEDED(n));
|
|
UNIV_MEM_ASSERT_W((byte*) block + mem_block_get_free(block), n);
|
|
#ifdef UNIV_MEM_DEBUG
|
|
|
|
ut_ad(mem_block_get_start(block) <= mem_block_get_free(block));
|
|
|
|
/* In the debug version check the consistency, and erase field */
|
|
mem_field_erase((byte*)block + mem_block_get_free(block), n);
|
|
#endif
|
|
|
|
/* If free == start, we may free the block if it is not the first
|
|
one */
|
|
|
|
if ((heap != block) && (mem_block_get_free(block)
|
|
== mem_block_get_start(block))) {
|
|
mem_heap_block_free(heap, block);
|
|
} else {
|
|
/* Avoid a bogus UNIV_MEM_ASSERT_W() warning in a
|
|
subsequent invocation of mem_heap_free_top().
|
|
Originally, this was UNIV_MEM_FREE(), to catch writes
|
|
to freed memory. */
|
|
UNIV_MEM_ALLOC((byte*) block + mem_block_get_free(block), n);
|
|
}
|
|
}
|
|
|
|
/*********************************************************************
|
|
NOTE: Use the corresponding macros instead of this function. Creates a
|
|
memory heap. For debugging purposes, takes also the file name and line as
|
|
argument. */
|
|
UNIV_INLINE
|
|
mem_heap_t*
|
|
mem_heap_create_func(
|
|
/*=================*/
|
|
/* out, own: memory heap, NULL if
|
|
did not succeed (only possible for
|
|
MEM_HEAP_BTR_SEARCH type heaps)*/
|
|
ulint n, /* in: desired start block size,
|
|
this means that a single user buffer
|
|
of size n will fit in the block,
|
|
0 creates a default size block */
|
|
ulint type, /* in: heap type */
|
|
const char* file_name, /* in: file name where created */
|
|
ulint line) /* in: line where created */
|
|
{
|
|
mem_block_t* block;
|
|
|
|
if (!n) {
|
|
n = MEM_BLOCK_START_SIZE;
|
|
}
|
|
|
|
block = mem_heap_create_block(NULL, n, type, file_name, line);
|
|
|
|
if (block == NULL) {
|
|
|
|
return(NULL);
|
|
}
|
|
|
|
UT_LIST_INIT(block->base);
|
|
|
|
/* Add the created block itself as the first block in the list */
|
|
UT_LIST_ADD_FIRST(list, block->base, block);
|
|
|
|
#ifdef UNIV_MEM_DEBUG
|
|
|
|
mem_hash_insert(block, file_name, line);
|
|
|
|
#endif
|
|
|
|
return(block);
|
|
}
|
|
|
|
/*********************************************************************
|
|
NOTE: Use the corresponding macro instead of this function. Frees the space
|
|
occupied by a memory heap. In the debug version erases the heap memory
|
|
blocks. */
|
|
UNIV_INLINE
|
|
void
|
|
mem_heap_free_func(
|
|
/*===============*/
|
|
mem_heap_t* heap, /* in, own: heap to be freed */
|
|
const char* file_name __attribute__((unused)),
|
|
/* in: file name where freed */
|
|
ulint line __attribute__((unused)))
|
|
{
|
|
mem_block_t* block;
|
|
mem_block_t* prev_block;
|
|
|
|
ut_ad(mem_heap_check(heap));
|
|
|
|
block = UT_LIST_GET_LAST(heap->base);
|
|
|
|
#ifdef UNIV_MEM_DEBUG
|
|
|
|
/* In the debug version remove the heap from the hash table of heaps
|
|
and check its consistency */
|
|
|
|
mem_hash_remove(heap, file_name, line);
|
|
|
|
#endif
|
|
|
|
if (heap->free_block) {
|
|
mem_heap_free_block_free(heap);
|
|
}
|
|
|
|
while (block != NULL) {
|
|
/* Store the contents of info before freeing current block
|
|
(it is erased in freeing) */
|
|
|
|
prev_block = UT_LIST_GET_PREV(list, block);
|
|
|
|
mem_heap_block_free(heap, block);
|
|
|
|
block = prev_block;
|
|
}
|
|
}
|
|
|
|
/*******************************************************************
|
|
NOTE: Use the corresponding macro instead of this function.
|
|
Allocates a single buffer of memory from the dynamic memory of
|
|
the C compiler. Is like malloc of C. The buffer must be freed
|
|
with mem_free. */
|
|
UNIV_INLINE
|
|
void*
|
|
mem_alloc_func(
|
|
/*===========*/
|
|
/* out, own: free storage */
|
|
ulint n, /* in: desired number of bytes */
|
|
ulint* size, /* out: allocated size in bytes,
|
|
or NULL */
|
|
const char* file_name, /* in: file name where created */
|
|
ulint line) /* in: line where created */
|
|
{
|
|
mem_heap_t* heap;
|
|
void* buf;
|
|
|
|
heap = mem_heap_create_func(n, MEM_HEAP_DYNAMIC, file_name, line);
|
|
|
|
/* Note that as we created the first block in the heap big enough
|
|
for the buffer requested by the caller, the buffer will be in the
|
|
first block and thus we can calculate the pointer to the heap from
|
|
the pointer to the buffer when we free the memory buffer. */
|
|
|
|
if (UNIV_LIKELY_NULL(size)) {
|
|
/* Adjust the allocation to the actual size of the
|
|
memory block. */
|
|
ulint m = mem_block_get_len(heap)
|
|
- mem_block_get_free(heap);
|
|
#ifdef UNIV_MEM_DEBUG
|
|
m -= MEM_FIELD_HEADER_SIZE + MEM_FIELD_TRAILER_SIZE;
|
|
#endif /* UNIV_MEM_DEBUG */
|
|
ut_ad(m >= n);
|
|
*size = n = m;
|
|
}
|
|
|
|
buf = mem_heap_alloc(heap, n);
|
|
|
|
ut_a((byte*)heap == (byte*)buf - MEM_BLOCK_HEADER_SIZE
|
|
- MEM_FIELD_HEADER_SIZE);
|
|
return(buf);
|
|
}
|
|
|
|
/*******************************************************************
|
|
NOTE: Use the corresponding macro instead of this function. Frees a single
|
|
buffer of storage from the dynamic memory of the C compiler. Similar to the
|
|
free of C. */
|
|
UNIV_INLINE
|
|
void
|
|
mem_free_func(
|
|
/*==========*/
|
|
void* ptr, /* in, own: buffer to be freed */
|
|
const char* file_name, /* in: file name where created */
|
|
ulint line /* in: line where created */
|
|
)
|
|
{
|
|
mem_heap_t* heap;
|
|
|
|
heap = (mem_heap_t*)((byte*)ptr - MEM_BLOCK_HEADER_SIZE
|
|
- MEM_FIELD_HEADER_SIZE);
|
|
mem_heap_free_func(heap, file_name, line);
|
|
}
|
|
|
|
/*********************************************************************
|
|
Returns the space in bytes occupied by a memory heap. */
|
|
UNIV_INLINE
|
|
ulint
|
|
mem_heap_get_size(
|
|
/*==============*/
|
|
mem_heap_t* heap) /* in: heap */
|
|
{
|
|
mem_block_t* block;
|
|
ulint size = 0;
|
|
|
|
ut_ad(mem_heap_check(heap));
|
|
|
|
block = heap;
|
|
|
|
while (block != NULL) {
|
|
|
|
size += mem_block_get_len(block);
|
|
block = UT_LIST_GET_NEXT(list, block);
|
|
}
|
|
|
|
if (heap->free_block) {
|
|
size += UNIV_PAGE_SIZE;
|
|
}
|
|
|
|
return(size);
|
|
}
|
|
|
|
/**************************************************************************
|
|
Duplicates a NUL-terminated string. */
|
|
UNIV_INLINE
|
|
char*
|
|
mem_strdup(
|
|
/*=======*/
|
|
/* out, own: a copy of the string,
|
|
must be deallocated with mem_free */
|
|
const char* str) /* in: string to be copied */
|
|
{
|
|
ulint len = strlen(str) + 1;
|
|
return((char*) memcpy(mem_alloc(len), str, len));
|
|
}
|
|
|
|
/**************************************************************************
|
|
Makes a NUL-terminated copy of a nonterminated string. */
|
|
UNIV_INLINE
|
|
char*
|
|
mem_strdupl(
|
|
/*========*/
|
|
/* out, own: a copy of the string,
|
|
must be deallocated with mem_free */
|
|
const char* str, /* in: string to be copied */
|
|
ulint len) /* in: length of str, in bytes */
|
|
{
|
|
char* s = (char*) mem_alloc(len + 1);
|
|
s[len] = 0;
|
|
return((char*) memcpy(s, str, len));
|
|
}
|
|
|
|
/**************************************************************************
|
|
Makes a NUL-terminated copy of a nonterminated string,
|
|
allocated from a memory heap. */
|
|
UNIV_INLINE
|
|
char*
|
|
mem_heap_strdupl(
|
|
/*=============*/
|
|
/* out, own: a copy of the string */
|
|
mem_heap_t* heap, /* in: memory heap where string is allocated */
|
|
const char* str, /* in: string to be copied */
|
|
ulint len) /* in: length of str, in bytes */
|
|
{
|
|
char* s = (char*) mem_heap_alloc(heap, len + 1);
|
|
s[len] = 0;
|
|
return((char*) memcpy(s, str, len));
|
|
}
|