~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to mysys/my_alloc.cc

Removed protocol field flags.

Show diffs side-by-side

added added

removed removed

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