~drizzle-trunk/drizzle/development

1 by brian
clean slate
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
}