my_alloc.c 9.94 KB
Newer Older
unknown's avatar
unknown committed
1 2 3 4 5 6 7 8
/* Copyright (C) 2000 MySQL AB

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
unknown's avatar
unknown committed
9
   but WITHOUT ANY WARRANTY; without even the implied warranty of
unknown's avatar
unknown committed
10 11 12 13 14 15
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
unknown's avatar
unknown committed
16 17 18

/* Routines to handle mallocing of results which will be freed the same time */

unknown's avatar
unknown committed
19
#include <my_global.h>
unknown's avatar
unknown committed
20 21
#include <my_sys.h>
#include <m_string.h>
unknown's avatar
unknown committed
22
#undef EXTRA_DEBUG
unknown's avatar
unknown committed
23
#define EXTRA_DEBUG
unknown's avatar
unknown committed
24

25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45

/*
  Initialize memory root

  SYNOPSIS
    init_alloc_root()
      mem_root       - memory root to initialize
      block_size     - size of chunks (blocks) used for memory allocation
                       (It is external size of chunk i.e. it should include
                        memory required for internal structures, thus it
                        should be no less than ALLOC_ROOT_MIN_BLOCK_SIZE)
      pre_alloc_size - if non-0, then size of block that should be
                       pre-allocated during memory root initialization.

  DESCRIPTION
    This function prepares memory root for further use, sets initial size of
    chunk for memory allocation and pre-allocates first block if specified.
    Altough error can happen during execution of this function if pre_alloc_size
    is non-0 it won't be reported. Instead it will be reported as error in first
    alloc_root() on this memory root.
*/
unknown's avatar
unknown committed
46 47
void init_alloc_root(MEM_ROOT *mem_root, uint block_size,
		     uint pre_alloc_size __attribute__((unused)))
unknown's avatar
unknown committed
48
{
49
  DBUG_ENTER("init_alloc_root");
50
  DBUG_PRINT("enter",("root: 0x%lx", mem_root));
51
  mem_root->free= mem_root->used= mem_root->pre_alloc= 0;
52
  mem_root->min_malloc= 32;
53
  mem_root->block_size= block_size - ALLOC_ROOT_MIN_BLOCK_SIZE;
54
  mem_root->error_handler= 0;
unknown's avatar
unknown committed
55
  mem_root->block_num= 4;			/* We shift this with >>2 */
56
  mem_root->first_block_usage= 0;
57

58 59 60
#if !(defined(HAVE_purify) && defined(EXTRA_DEBUG))
  if (pre_alloc_size)
  {
61
    if ((mem_root->free= mem_root->pre_alloc=
62 63 64
	 (USED_MEM*) my_malloc(pre_alloc_size+ ALIGN_SIZE(sizeof(USED_MEM)),
			       MYF(0))))
    {
65 66 67
      mem_root->free->size= pre_alloc_size+ALIGN_SIZE(sizeof(USED_MEM));
      mem_root->free->left= pre_alloc_size;
      mem_root->free->next= 0;
68 69 70
    }
  }
#endif
71
  DBUG_VOID_RETURN;
unknown's avatar
unknown committed
72 73
}

74 75 76 77
/*
  SYNOPSIS
    reset_root_defaults()
    mem_root        memory root to change defaults of
78 79 80
    block_size      new value of block size. Must be greater or equal
                    than ALLOC_ROOT_MIN_BLOCK_SIZE (this value is about
                    68 bytes and depends on platform and compilation flags)
81 82 83 84 85 86 87 88 89 90 91
    pre_alloc_size  new size of preallocated block. If not zero,
                    must be equal to or greater than block size,
                    otherwise means 'no prealloc'.
  DESCRIPTION
    Function aligns and assigns new value to block size; then it tries to
    reuse one of existing blocks as prealloc block, or malloc new one of
    requested size. If no blocks can be reused, all unused blocks are freed
    before allocation.
 */

void reset_root_defaults(MEM_ROOT *mem_root, uint block_size,
92
                         uint pre_alloc_size __attribute__((unused)))
93
{
94 95 96
  DBUG_ASSERT(alloc_root_inited(mem_root));

  mem_root->block_size= block_size - ALLOC_ROOT_MIN_BLOCK_SIZE;
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 128 129 130 131 132 133 134 135 136 137 138 139 140 141
#if !(defined(HAVE_purify) && defined(EXTRA_DEBUG))
  if (pre_alloc_size)
  {
    uint size= pre_alloc_size + ALIGN_SIZE(sizeof(USED_MEM));
    if (!mem_root->pre_alloc || mem_root->pre_alloc->size != size)
    {
      USED_MEM *mem, **prev= &mem_root->free;
      /*
        Free unused blocks, so that consequent calls
        to reset_root_defaults won't eat away memory.
      */
      while (*prev)
      {
        mem= *prev;
        if (mem->size == size)
        {
          /* We found a suitable block, no need to do anything else */
          mem_root->pre_alloc= mem;
          return;
        }
        if (mem->left + ALIGN_SIZE(sizeof(USED_MEM)) == mem->size)
        {
          /* remove block from the list and free it */
          *prev= mem->next;
          my_free((gptr) mem, MYF(0));
        }
        else
          prev= &mem->next;
      }
      /* Allocate new prealloc block and add it to the end of free list */
      if ((mem= (USED_MEM *) my_malloc(size, MYF(0))))
      {
        mem->size= size; 
        mem->left= pre_alloc_size;
        mem->next= *prev;
        *prev= mem_root->pre_alloc= mem; 
      }
    }
  }
  else
#endif
    mem_root->pre_alloc= 0;
}


unknown's avatar
unknown committed
142 143 144 145
gptr alloc_root(MEM_ROOT *mem_root,unsigned int Size)
{
#if defined(HAVE_purify) && defined(EXTRA_DEBUG)
  reg1 USED_MEM *next;
146
  DBUG_ENTER("alloc_root");
147
  DBUG_PRINT("enter",("root: 0x%lx", mem_root));
unknown's avatar
unknown committed
148

149 150
  DBUG_ASSERT(alloc_root_inited(mem_root));

151
  Size+=ALIGN_SIZE(sizeof(USED_MEM));
unknown's avatar
unknown committed
152 153 154 155
  if (!(next = (USED_MEM*) my_malloc(Size,MYF(MY_WME))))
  {
    if (mem_root->error_handler)
      (*mem_root->error_handler)();
156
    DBUG_RETURN((gptr) 0);			/* purecov: inspected */
unknown's avatar
unknown committed
157
  }
158
  next->next= mem_root->used;
unknown's avatar
unknown committed
159
  next->size= Size;
160
  mem_root->used= next;
161 162
  DBUG_PRINT("exit",("ptr: 0x%lx", (((char*) next)+
                                    ALIGN_SIZE(sizeof(USED_MEM)))));
163
  DBUG_RETURN((gptr) (((char*) next)+ALIGN_SIZE(sizeof(USED_MEM))));
unknown's avatar
unknown committed
164
#else
165
  uint get_size, block_size;
unknown's avatar
unknown committed
166
  gptr point;
167
  reg1 USED_MEM *next= 0;
unknown's avatar
unknown committed
168 169
  reg2 USED_MEM **prev;

170 171
  DBUG_ASSERT(alloc_root_inited(mem_root));

unknown's avatar
unknown committed
172
  Size= ALIGN_SIZE(Size);
unknown's avatar
unknown committed
173
  if ((*(prev= &mem_root->free)) != NULL)
174
  {
unknown's avatar
unknown committed
175 176 177
    if ((*prev)->left < Size &&
	mem_root->first_block_usage++ >= ALLOC_MAX_BLOCK_USAGE_BEFORE_DROP &&
	(*prev)->left < ALLOC_MAX_BLOCK_TO_DROP)
178 179 180 181 182 183 184 185 186 187
    {
      next= *prev;
      *prev= next->next;			/* Remove block from list */
      next->next= mem_root->used;
      mem_root->used= next;
      mem_root->first_block_usage= 0;
    }
    for (next= *prev ; next && next->left < Size ; next= next->next)
      prev= &next->next;
  }
unknown's avatar
unknown committed
188 189
  if (! next)
  {						/* Time to alloc new block */
unknown's avatar
unknown committed
190
    block_size= mem_root->block_size * (mem_root->block_num >> 2);
unknown's avatar
unknown committed
191
    get_size= Size+ALIGN_SIZE(sizeof(USED_MEM));
192
    get_size= max(get_size, block_size);
unknown's avatar
unknown committed
193 194 195 196 197 198 199

    if (!(next = (USED_MEM*) my_malloc(get_size,MYF(MY_WME))))
    {
      if (mem_root->error_handler)
	(*mem_root->error_handler)();
      return((gptr) 0);				/* purecov: inspected */
    }
200
    mem_root->block_num++;
unknown's avatar
unknown committed
201 202 203 204 205
    next->next= *prev;
    next->size= get_size;
    next->left= get_size-ALIGN_SIZE(sizeof(USED_MEM));
    *prev=next;
  }
206

unknown's avatar
unknown committed
207
  point= (gptr) ((char*) next+ (next->size-next->left));
208
  /*TODO: next part may be unneded due to mem_root->first_block_usage counter*/
unknown's avatar
unknown committed
209 210
  if ((next->left-= Size) < mem_root->min_malloc)
  {						/* Full block */
211 212 213 214
    *prev= next->next;				/* Remove block from list */
    next->next= mem_root->used;
    mem_root->used= next;
    mem_root->first_block_usage= 0;
unknown's avatar
unknown committed
215 216 217 218 219
  }
  return(point);
#endif
}

220 221 222 223 224 225
#ifdef SAFEMALLOC
#define TRASH(X) bfill(((char*)(X) + ((X)->size-(X)->left)), (X)->left, 0xa5)
#else
#define TRASH /* no-op */
#endif

unknown's avatar
unknown committed
226 227
/* Mark all data in blocks free for reusage */

228 229
static inline void mark_blocks_free(MEM_ROOT* root)
{
unknown's avatar
unknown committed
230 231
  reg1 USED_MEM *next;
  reg2 USED_MEM **last;
232

unknown's avatar
unknown committed
233 234 235
  /* iterate through (partially) free blocks, mark them free */
  last= &root->free;
  for (next= root->free; next; next= *(last= &next->next))
236
  {
unknown's avatar
unknown committed
237
    next->left= next->size - ALIGN_SIZE(sizeof(USED_MEM));
238 239
    TRASH(next);
  }
unknown's avatar
unknown committed
240 241 242 243 244 245

  /* Combine the free and the used list */
  *last= next=root->used;

  /* now go through the used blocks and mark them free */
  for (; next; next= next->next)
246
  {
unknown's avatar
unknown committed
247
    next->left= next->size - ALIGN_SIZE(sizeof(USED_MEM));
248 249
    TRASH(next);
  }
unknown's avatar
unknown committed
250 251 252

  /* Now everything is set; Indicate that nothing is used anymore */
  root->used= 0;
253
  root->first_block_usage= 0;
254 255
}

unknown's avatar
unknown committed
256 257 258 259 260

/*
  Deallocate everything used by alloc_root or just move
  used blocks to free list if called with MY_USED_TO_FREE
*/
unknown's avatar
unknown committed
261

262
void free_root(MEM_ROOT *root, myf MyFlags)
unknown's avatar
unknown committed
263 264 265
{
  reg1 USED_MEM *next,*old;
  DBUG_ENTER("free_root");
266
  DBUG_PRINT("enter",("root: 0x%lx  flags: %u", root, (uint) MyFlags));
unknown's avatar
unknown committed
267

268
  if (!root)					/* QQ: Should be deleted */
unknown's avatar
unknown committed
269
    DBUG_VOID_RETURN; /* purecov: inspected */
unknown's avatar
unknown committed
270 271 272 273 274
  if (MyFlags & MY_MARK_BLOCKS_FREE)
  {
    mark_blocks_free(root);
    DBUG_VOID_RETURN;
  }
275 276 277
  if (!(MyFlags & MY_KEEP_PREALLOC))
    root->pre_alloc=0;

unknown's avatar
unknown committed
278
  for (next=root->used; next ;)
unknown's avatar
unknown committed
279 280
  {
    old=next; next= next->next ;
281 282
    if (old != root->pre_alloc)
      my_free((gptr) old,MYF(0));
unknown's avatar
unknown committed
283
  }
unknown's avatar
unknown committed
284
  for (next=root->free ; next ;)
unknown's avatar
unknown committed
285
  {
unknown's avatar
unknown committed
286
    old=next; next= next->next;
287 288
    if (old != root->pre_alloc)
      my_free((gptr) old,MYF(0));
unknown's avatar
unknown committed
289 290
  }
  root->used=root->free=0;
291 292 293 294
  if (root->pre_alloc)
  {
    root->free=root->pre_alloc;
    root->free->left=root->pre_alloc->size-ALIGN_SIZE(sizeof(USED_MEM));
295
    TRASH(root->pre_alloc);
296 297
    root->free->next=0;
  }
unknown's avatar
unknown committed
298
  root->block_num= 4;
299
  root->first_block_usage= 0;
unknown's avatar
unknown committed
300 301 302
  DBUG_VOID_RETURN;
}

unknown's avatar
unknown committed
303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325
/*
  Find block that contains an object and set the pre_alloc to it
*/

void set_prealloc_root(MEM_ROOT *root, char *ptr)
{
  USED_MEM *next;
  for (next=root->used; next ; next=next->next)
  {
    if ((char*) next <= ptr && (char*) next + next->size > ptr)
    {
      root->pre_alloc=next;
      return;
    }
  }
  for (next=root->free ; next ; next=next->next)
  {
    if ((char*) next <= ptr && (char*) next + next->size > ptr)
    {
      root->pre_alloc=next;
      return;
    }
  }
unknown's avatar
unknown committed
326
}
unknown's avatar
unknown committed
327

328

unknown's avatar
unknown committed
329 330
char *strdup_root(MEM_ROOT *root,const char *str)
{
331 332 333 334 335
  return strmake_root(root, str, strlen(str));
}

char *strmake_root(MEM_ROOT *root,const char *str, uint len)
{
unknown's avatar
unknown committed
336
  char *pos;
337 338
  if ((pos=alloc_root(root,len+1)))
  {
unknown's avatar
unknown committed
339
    memcpy(pos,str,len);
340 341
    pos[len]=0;
  }
unknown's avatar
unknown committed
342 343 344 345 346 347 348 349 350 351 352
  return pos;
}


char *memdup_root(MEM_ROOT *root,const char *str,uint len)
{
  char *pos;
  if ((pos=alloc_root(root,len)))
    memcpy(pos,str,len);
  return pos;
}