~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to mysys/my_alloc.c

  • Committer: brian
  • Date: 2008-06-25 05:29:13 UTC
  • Revision ID: brian@localhost.localdomain-20080625052913-6upwo0jsrl4lnapl
clean slate

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Copyright (C) 2000 MySQL AB
 
2
 
 
3
   This program is free software; you can redistribute it and/or modify
 
4
   it under the terms of the GNU General Public License as published by
 
5
   the Free Software Foundation; version 2 of the License.
 
6
 
 
7
   This program is distributed in the hope that it will be useful,
 
8
   but WITHOUT ANY WARRANTY; without even the implied warranty of
 
9
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
10
   GNU General Public License for more details.
 
11
 
 
12
   You should have received a copy of the GNU General Public License
 
13
   along with this program; if not, write to the Free Software
 
14
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
 
15
 
 
16
/* Routines to handle mallocing of results which will be freed the same time */
 
17
 
 
18
#include <my_global.h>
 
19
#include <my_sys.h>
 
20
#include <m_string.h>
 
21
#undef EXTRA_DEBUG
 
22
#define EXTRA_DEBUG
 
23
 
 
24
 
 
25
/*
 
26
  Initialize memory root
 
27
 
 
28
  SYNOPSIS
 
29
    init_alloc_root()
 
30
      mem_root       - memory root to initialize
 
31
      block_size     - size of chunks (blocks) used for memory allocation
 
32
                       (It is external size of chunk i.e. it should include
 
33
                        memory required for internal structures, thus it
 
34
                        should be no less than ALLOC_ROOT_MIN_BLOCK_SIZE)
 
35
      pre_alloc_size - if non-0, then size of block that should be
 
36
                       pre-allocated during memory root initialization.
 
37
 
 
38
  DESCRIPTION
 
39
    This function prepares memory root for further use, sets initial size of
 
40
    chunk for memory allocation and pre-allocates first block if specified.
 
41
    Altough error can happen during execution of this function if
 
42
    pre_alloc_size is non-0 it won't be reported. Instead it will be
 
43
    reported as error in first alloc_root() on this memory root.
 
44
*/
 
45
 
 
46
void init_alloc_root(MEM_ROOT *mem_root, size_t block_size,
 
47
                     size_t pre_alloc_size __attribute__((unused)))
 
48
{
 
49
  DBUG_ENTER("init_alloc_root");
 
50
  DBUG_PRINT("enter",("root: 0x%lx", (long) mem_root));
 
51
 
 
52
  mem_root->free= mem_root->used= mem_root->pre_alloc= 0;
 
53
  mem_root->min_malloc= 32;
 
54
  mem_root->block_size= block_size - ALLOC_ROOT_MIN_BLOCK_SIZE;
 
55
  mem_root->error_handler= 0;
 
56
  mem_root->block_num= 4;                       /* We shift this with >>2 */
 
57
  mem_root->first_block_usage= 0;
 
58
 
 
59
#if !(defined(HAVE_purify) && defined(EXTRA_DEBUG))
 
60
  if (pre_alloc_size)
 
61
  {
 
62
    if ((mem_root->free= mem_root->pre_alloc=
 
63
         (USED_MEM*) my_malloc(pre_alloc_size+ ALIGN_SIZE(sizeof(USED_MEM)),
 
64
                               MYF(0))))
 
65
    {
 
66
      mem_root->free->size= pre_alloc_size+ALIGN_SIZE(sizeof(USED_MEM));
 
67
      mem_root->free->left= pre_alloc_size;
 
68
      mem_root->free->next= 0;
 
69
    }
 
70
  }
 
71
#endif
 
72
  DBUG_VOID_RETURN;
 
73
}
 
74
 
 
75
 
 
76
/*
 
77
  SYNOPSIS
 
78
    reset_root_defaults()
 
79
    mem_root        memory root to change defaults of
 
80
    block_size      new value of block size. Must be greater or equal
 
81
                    than ALLOC_ROOT_MIN_BLOCK_SIZE (this value is about
 
82
                    68 bytes and depends on platform and compilation flags)
 
83
    pre_alloc_size  new size of preallocated block. If not zero,
 
84
                    must be equal to or greater than block size,
 
85
                    otherwise means 'no prealloc'.
 
86
  DESCRIPTION
 
87
    Function aligns and assigns new value to block size; then it tries to
 
88
    reuse one of existing blocks as prealloc block, or malloc new one of
 
89
    requested size. If no blocks can be reused, all unused blocks are freed
 
90
    before allocation.
 
91
*/
 
92
 
 
93
void reset_root_defaults(MEM_ROOT *mem_root, size_t block_size,
 
94
                         size_t pre_alloc_size __attribute__((unused)))
 
95
{
 
96
  DBUG_ASSERT(alloc_root_inited(mem_root));
 
97
 
 
98
  mem_root->block_size= block_size - ALLOC_ROOT_MIN_BLOCK_SIZE;
 
99
#if !(defined(HAVE_purify) && defined(EXTRA_DEBUG))
 
100
  if (pre_alloc_size)
 
101
  {
 
102
    size_t size= pre_alloc_size + ALIGN_SIZE(sizeof(USED_MEM));
 
103
    if (!mem_root->pre_alloc || mem_root->pre_alloc->size != size)
 
104
    {
 
105
      USED_MEM *mem, **prev= &mem_root->free;
 
106
      /*
 
107
        Free unused blocks, so that consequent calls
 
108
        to reset_root_defaults won't eat away memory.
 
109
      */
 
110
      while (*prev)
 
111
      {
 
112
        mem= *prev;
 
113
        if (mem->size == size)
 
114
        {
 
115
          /* We found a suitable block, no need to do anything else */
 
116
          mem_root->pre_alloc= mem;
 
117
          return;
 
118
        }
 
119
        if (mem->left + ALIGN_SIZE(sizeof(USED_MEM)) == mem->size)
 
120
        {
 
121
          /* remove block from the list and free it */
 
122
          *prev= mem->next;
 
123
          my_free(mem, MYF(0));
 
124
        }
 
125
        else
 
126
          prev= &mem->next;
 
127
      }
 
128
      /* Allocate new prealloc block and add it to the end of free list */
 
129
      if ((mem= (USED_MEM *) my_malloc(size, MYF(0))))
 
130
      {
 
131
        mem->size= size; 
 
132
        mem->left= pre_alloc_size;
 
133
        mem->next= *prev;
 
134
        *prev= mem_root->pre_alloc= mem; 
 
135
      }
 
136
      else
 
137
      {
 
138
        mem_root->pre_alloc= 0;
 
139
      }
 
140
    }
 
141
  }
 
142
  else
 
143
#endif
 
144
    mem_root->pre_alloc= 0;
 
145
}
 
146
 
 
147
 
 
148
void *alloc_root(MEM_ROOT *mem_root, size_t length)
 
149
{
 
150
#if defined(HAVE_purify) && defined(EXTRA_DEBUG)
 
151
  register USED_MEM *next;
 
152
  DBUG_ENTER("alloc_root");
 
153
  DBUG_PRINT("enter",("root: 0x%lx", (long) mem_root));
 
154
 
 
155
  DBUG_ASSERT(alloc_root_inited(mem_root));
 
156
 
 
157
  length+=ALIGN_SIZE(sizeof(USED_MEM));
 
158
  if (!(next = (USED_MEM*) my_malloc(length,MYF(MY_WME | ME_FATALERROR))))
 
159
  {
 
160
    if (mem_root->error_handler)
 
161
      (*mem_root->error_handler)();
 
162
    DBUG_RETURN((uchar*) 0);                    /* purecov: inspected */
 
163
  }
 
164
  next->next= mem_root->used;
 
165
  next->size= length;
 
166
  mem_root->used= next;
 
167
  DBUG_PRINT("exit",("ptr: 0x%lx", (long) (((char*) next)+
 
168
                                           ALIGN_SIZE(sizeof(USED_MEM)))));
 
169
  DBUG_RETURN((uchar*) (((char*) next)+ALIGN_SIZE(sizeof(USED_MEM))));
 
170
#else
 
171
  size_t get_size, block_size;
 
172
  uchar* point;
 
173
  register USED_MEM *next= 0;
 
174
  register USED_MEM **prev;
 
175
  DBUG_ENTER("alloc_root");
 
176
  DBUG_PRINT("enter",("root: 0x%lx", (long) mem_root));
 
177
  DBUG_ASSERT(alloc_root_inited(mem_root));
 
178
 
 
179
  length= ALIGN_SIZE(length);
 
180
  if ((*(prev= &mem_root->free)) != NULL)
 
181
  {
 
182
    if ((*prev)->left < length &&
 
183
        mem_root->first_block_usage++ >= ALLOC_MAX_BLOCK_USAGE_BEFORE_DROP &&
 
184
        (*prev)->left < ALLOC_MAX_BLOCK_TO_DROP)
 
185
    {
 
186
      next= *prev;
 
187
      *prev= next->next;                        /* Remove block from list */
 
188
      next->next= mem_root->used;
 
189
      mem_root->used= next;
 
190
      mem_root->first_block_usage= 0;
 
191
    }
 
192
    for (next= *prev ; next && next->left < length ; next= next->next)
 
193
      prev= &next->next;
 
194
  }
 
195
  if (! next)
 
196
  {                                             /* Time to alloc new block */
 
197
    block_size= mem_root->block_size * (mem_root->block_num >> 2);
 
198
    get_size= length+ALIGN_SIZE(sizeof(USED_MEM));
 
199
    get_size= max(get_size, block_size);
 
200
 
 
201
    if (!(next = (USED_MEM*) my_malloc(get_size,MYF(MY_WME | ME_FATALERROR))))
 
202
    {
 
203
      if (mem_root->error_handler)
 
204
        (*mem_root->error_handler)();
 
205
      DBUG_RETURN((void*) 0);                      /* purecov: inspected */
 
206
    }
 
207
    mem_root->block_num++;
 
208
    next->next= *prev;
 
209
    next->size= get_size;
 
210
    next->left= get_size-ALIGN_SIZE(sizeof(USED_MEM));
 
211
    *prev=next;
 
212
  }
 
213
 
 
214
  point= (uchar*) ((char*) next+ (next->size-next->left));
 
215
  /*TODO: next part may be unneded due to mem_root->first_block_usage counter*/
 
216
  if ((next->left-= length) < mem_root->min_malloc)
 
217
  {                                             /* Full block */
 
218
    *prev= next->next;                          /* Remove block from list */
 
219
    next->next= mem_root->used;
 
220
    mem_root->used= next;
 
221
    mem_root->first_block_usage= 0;
 
222
  }
 
223
  DBUG_PRINT("exit",("ptr: 0x%lx", (ulong) point));
 
224
  DBUG_RETURN((void*) point);
 
225
#endif
 
226
}
 
227
 
 
228
 
 
229
/*
 
230
  Allocate many pointers at the same time.
 
231
 
 
232
  DESCRIPTION
 
233
    ptr1, ptr2, etc all point into big allocated memory area.
 
234
 
 
235
  SYNOPSIS
 
236
    multi_alloc_root()
 
237
      root               Memory root
 
238
      ptr1, length1      Multiple arguments terminated by a NULL pointer
 
239
      ptr2, length2      ...
 
240
      ...
 
241
      NULL
 
242
 
 
243
  RETURN VALUE
 
244
    A pointer to the beginning of the allocated memory block
 
245
    in case of success or NULL if out of memory.
 
246
*/
 
247
 
 
248
void *multi_alloc_root(MEM_ROOT *root, ...)
 
249
{
 
250
  va_list args;
 
251
  char **ptr, *start, *res;
 
252
  size_t tot_length, length;
 
253
  DBUG_ENTER("multi_alloc_root");
 
254
 
 
255
  va_start(args, root);
 
256
  tot_length= 0;
 
257
  while ((ptr= va_arg(args, char **)))
 
258
  {
 
259
    length= va_arg(args, uint);
 
260
    tot_length+= ALIGN_SIZE(length);
 
261
  }
 
262
  va_end(args);
 
263
 
 
264
  if (!(start= (char*) alloc_root(root, tot_length)))
 
265
    DBUG_RETURN(0);                            /* purecov: inspected */
 
266
 
 
267
  va_start(args, root);
 
268
  res= start;
 
269
  while ((ptr= va_arg(args, char **)))
 
270
  {
 
271
    *ptr= res;
 
272
    length= va_arg(args, uint);
 
273
    res+= ALIGN_SIZE(length);
 
274
  }
 
275
  va_end(args);
 
276
  DBUG_RETURN((void*) start);
 
277
}
 
278
 
 
279
#define TRASH_MEM(X) TRASH(((char*)(X) + ((X)->size-(X)->left)), (X)->left)
 
280
 
 
281
/* Mark all data in blocks free for reusage */
 
282
 
 
283
static inline void mark_blocks_free(MEM_ROOT* root)
 
284
{
 
285
  register USED_MEM *next;
 
286
  register USED_MEM **last;
 
287
 
 
288
  /* iterate through (partially) free blocks, mark them free */
 
289
  last= &root->free;
 
290
  for (next= root->free; next; next= *(last= &next->next))
 
291
  {
 
292
    next->left= next->size - ALIGN_SIZE(sizeof(USED_MEM));
 
293
    TRASH_MEM(next);
 
294
  }
 
295
 
 
296
  /* Combine the free and the used list */
 
297
  *last= next=root->used;
 
298
 
 
299
  /* now go through the used blocks and mark them free */
 
300
  for (; next; next= next->next)
 
301
  {
 
302
    next->left= next->size - ALIGN_SIZE(sizeof(USED_MEM));
 
303
    TRASH_MEM(next);
 
304
  }
 
305
 
 
306
  /* Now everything is set; Indicate that nothing is used anymore */
 
307
  root->used= 0;
 
308
  root->first_block_usage= 0;
 
309
}
 
310
 
 
311
 
 
312
/*
 
313
  Deallocate everything used by alloc_root or just move
 
314
  used blocks to free list if called with MY_USED_TO_FREE
 
315
 
 
316
  SYNOPSIS
 
317
    free_root()
 
318
      root              Memory root
 
319
      MyFlags           Flags for what should be freed:
 
320
 
 
321
        MY_MARK_BLOCKS_FREED    Don't free blocks, just mark them free
 
322
        MY_KEEP_PREALLOC        If this is not set, then free also the
 
323
                                preallocated block
 
324
 
 
325
  NOTES
 
326
    One can call this function either with root block initialised with
 
327
    init_alloc_root() or with a bzero()-ed block.
 
328
    It's also safe to call this multiple times with the same mem_root.
 
329
*/
 
330
 
 
331
void free_root(MEM_ROOT *root, myf MyFlags)
 
332
{
 
333
  register USED_MEM *next,*old;
 
334
  DBUG_ENTER("free_root");
 
335
  DBUG_PRINT("enter",("root: 0x%lx  flags: %u", (long) root, (uint) MyFlags));
 
336
 
 
337
  if (MyFlags & MY_MARK_BLOCKS_FREE)
 
338
  {
 
339
    mark_blocks_free(root);
 
340
    DBUG_VOID_RETURN;
 
341
  }
 
342
  if (!(MyFlags & MY_KEEP_PREALLOC))
 
343
    root->pre_alloc=0;
 
344
 
 
345
  for (next=root->used; next ;)
 
346
  {
 
347
    old=next; next= next->next ;
 
348
    if (old != root->pre_alloc)
 
349
      my_free(old,MYF(0));
 
350
  }
 
351
  for (next=root->free ; next ;)
 
352
  {
 
353
    old=next; next= next->next;
 
354
    if (old != root->pre_alloc)
 
355
      my_free(old,MYF(0));
 
356
  }
 
357
  root->used=root->free=0;
 
358
  if (root->pre_alloc)
 
359
  {
 
360
    root->free=root->pre_alloc;
 
361
    root->free->left=root->pre_alloc->size-ALIGN_SIZE(sizeof(USED_MEM));
 
362
    TRASH_MEM(root->pre_alloc);
 
363
    root->free->next=0;
 
364
  }
 
365
  root->block_num= 4;
 
366
  root->first_block_usage= 0;
 
367
  DBUG_VOID_RETURN;
 
368
}
 
369
 
 
370
/*
 
371
  Find block that contains an object and set the pre_alloc to it
 
372
*/
 
373
 
 
374
void set_prealloc_root(MEM_ROOT *root, char *ptr)
 
375
{
 
376
  USED_MEM *next;
 
377
  for (next=root->used; next ; next=next->next)
 
378
  {
 
379
    if ((char*) next <= ptr && (char*) next + next->size > ptr)
 
380
    {
 
381
      root->pre_alloc=next;
 
382
      return;
 
383
    }
 
384
  }
 
385
  for (next=root->free ; next ; next=next->next)
 
386
  {
 
387
    if ((char*) next <= ptr && (char*) next + next->size > ptr)
 
388
    {
 
389
      root->pre_alloc=next;
 
390
      return;
 
391
    }
 
392
  }
 
393
}
 
394
 
 
395
 
 
396
char *strdup_root(MEM_ROOT *root, const char *str)
 
397
{
 
398
  return strmake_root(root, str, strlen(str));
 
399
}
 
400
 
 
401
 
 
402
char *strmake_root(MEM_ROOT *root, const char *str, size_t len)
 
403
{
 
404
  char *pos;
 
405
  if ((pos=alloc_root(root,len+1)))
 
406
  {
 
407
    memcpy(pos,str,len);
 
408
    pos[len]=0;
 
409
  }
 
410
  return pos;
 
411
}
 
412
 
 
413
 
 
414
void *memdup_root(MEM_ROOT *root, const void *str, size_t len)
 
415
{
 
416
  char *pos;
 
417
  if ((pos=alloc_root(root,len)))
 
418
    memcpy(pos,str,len);
 
419
  return pos;
 
420
}