Drizzled Public API Documentation

root.cc
Go to the documentation of this file.
00001 /* Copyright (C) 2000 MySQL AB
00002 
00003    This program is free software; you can redistribute it and/or modify
00004    it under the terms of the GNU General Public License as published by
00005    the Free Software Foundation; version 2 of the License.
00006 
00007    This program is distributed in the hope that it will be useful,
00008    but WITHOUT ANY WARRANTY; without even the implied warranty of
00009    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00010    GNU General Public License for more details.
00011 
00012    You should have received a copy of the GNU General Public License
00013    along with this program; if not, write to the Free Software
00014    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA */
00015 
00021 #include <config.h>
00022 
00023 #include <drizzled/definitions.h>
00024 #include <drizzled/memory/root.h>
00025 #include <drizzled/internal/my_sys.h>
00026 #include <drizzled/internal/m_string.h>
00027 #include <drizzled/sql_string.h>
00028 
00029 #include <algorithm>
00030 
00031 using namespace std;
00032 
00033 namespace drizzled {
00034 namespace memory {
00035 
00036 static const unsigned int MAX_BLOCK_TO_DROP= 4096;
00037 static const unsigned int MAX_BLOCK_USAGE_BEFORE_DROP= 10;
00038 
00057 void Root::init(size_t block_size_arg)
00058 {
00059   free= used= pre_alloc= 0;
00060   min_malloc= 32;
00061   block_size= block_size_arg - ROOT_MIN_BLOCK_SIZE;
00062   block_num= 4;     /* We shift this with >>2 */
00063   first_block_usage= 0;
00064 }
00065 
00081 void Root::reset_defaults(size_t block_size_arg, size_t pre_alloc_size)
00082 {
00083   block_size= block_size_arg - ROOT_MIN_BLOCK_SIZE;
00084   if (pre_alloc_size)
00085   {
00086     size_t size= pre_alloc_size + ALIGN_SIZE(sizeof(internal::UsedMemory));
00087     if (not pre_alloc || pre_alloc->size != size)
00088     {
00089       internal::UsedMemory *mem, **prev= &this->free;
00090       /*
00091         Free unused blocks, so that consequent calls
00092         to reset_root_defaults won't eat away memory.
00093       */
00094       while (*prev)
00095       {
00096         mem= *prev;
00097         if (mem->size == size)
00098         {
00099           /* We found a suitable block, no need to do anything else */
00100           pre_alloc= mem;
00101           return;
00102         }
00103         if (mem->left + ALIGN_SIZE(sizeof(internal::UsedMemory)) == mem->size)
00104         {
00105           /* remove block from the list and free it */
00106           *prev= mem->next;
00107           std::free(mem);
00108         }
00109         else
00110           prev= &mem->next;
00111       }
00112       /* Allocate new prealloc block and add it to the end of free list */
00113       mem= static_cast<internal::UsedMemory *>(malloc(size));
00114       mem->size= size;
00115       mem->left= pre_alloc_size;
00116       mem->next= *prev;
00117       *prev= pre_alloc= mem;
00118     }
00119   }
00120   else
00121   {
00122     pre_alloc= 0;
00123   }
00124 }
00125 
00140 unsigned char* Root::alloc(size_t length)
00141 {
00142   internal::UsedMemory *next= NULL;
00143   assert(alloc_root_inited());
00144 
00145   length= ALIGN_SIZE(length);
00146   internal::UsedMemory **prev= &this->free;
00147   if (*prev)
00148   {
00149     if ((*prev)->left < length &&
00150   this->first_block_usage++ >= MAX_BLOCK_USAGE_BEFORE_DROP &&
00151   (*prev)->left < MAX_BLOCK_TO_DROP)
00152     {
00153       next= *prev;
00154       *prev= next->next;      /* Remove block from list */
00155       next->next= this->used;
00156       this->used= next;
00157       this->first_block_usage= 0;
00158     }
00159     for (next= *prev; next && next->left < length; next= next->next)
00160       prev= &next->next;
00161   }
00162   if (! next)
00163   {           /* Time to alloc new block */
00164     size_t tmp_block_size= this->block_size * (this->block_num >> 2);
00165     size_t get_size= length+ALIGN_SIZE(sizeof(internal::UsedMemory));
00166     get_size= max(get_size, tmp_block_size);
00167 
00168     next = static_cast<internal::UsedMemory *>(malloc(get_size));
00169     this->block_num++;
00170     next->next= *prev;
00171     next->size= get_size;
00172     next->left= get_size-ALIGN_SIZE(sizeof(internal::UsedMemory));
00173     *prev=next;
00174   }
00175 
00176   unsigned char* point= (unsigned char*) ((char*) next+ (next->size-next->left));
00178   if ((next->left-= length) < this->min_malloc)
00179   {           /* Full block */
00180     *prev= next->next;        /* Remove block from list */
00181     next->next= this->used;
00182     this->used= next;
00183     this->first_block_usage= 0;
00184   }
00185 
00186   return point;
00187 }
00188 
00189 
00212 void* Root::multi_alloc(int unused, ...)
00213 {
00214   va_list args;
00215   char* *ptr, *start, *res;
00216   size_t tot_length, length;
00217 
00218   (void)unused; // For some reason Sun Studio registers unused as not used.
00219   va_start(args, unused);
00220   tot_length= 0;
00221   while ((ptr= va_arg(args, char* *)))
00222   {
00223     length= va_arg(args, uint);
00224     tot_length+= ALIGN_SIZE(length);
00225   }
00226   va_end(args);
00227 
00228   start= (char*) this->alloc(tot_length);
00229 
00230   va_start(args, unused);
00231   res= start;
00232   while ((ptr= va_arg(args, char* *)))
00233   {
00234     *ptr= res;
00235     length= va_arg(args, uint);
00236     res+= ALIGN_SIZE(length);
00237   }
00238   va_end(args);
00239   return((void*) start);
00240 }
00241 
00246 void Root::mark_blocks_free()
00247 {
00248   internal::UsedMemory *next;
00249   internal::UsedMemory **last;
00250 
00251   /* iterate through (partially) free blocks, mark them free */
00252   last= &free;
00253   for (next= free; next; next= *(last= &next->next))
00254   {
00255     next->left= next->size - ALIGN_SIZE(sizeof(internal::UsedMemory));
00256   }
00257 
00258   /* Combine the free and the used list */
00259   *last= next= used;
00260 
00261   /* now go through the used blocks and mark them free */
00262   for (; next; next= next->next)
00263   {
00264     next->left= next->size - ALIGN_SIZE(sizeof(internal::UsedMemory));
00265   }
00266 
00267   /* Now everything is set; Indicate that nothing is used anymore */
00268   used= 0;
00269   first_block_usage= 0;
00270 }
00271 
00288 void Root::free_root(myf MyFlags)
00289 {
00290   if (MyFlags & MARK_BLOCKS_FREE)
00291   {
00292     this->mark_blocks_free();
00293     return;
00294   }
00295   if (!(MyFlags & KEEP_PREALLOC))
00296     this->pre_alloc=0;
00297 
00298   for (internal::UsedMemory* next= this->used; next;)
00299   {
00300     internal::UsedMemory* old =next; 
00301     next= next->next;
00302     if (old != this->pre_alloc)
00303       std::free(old);
00304   }
00305   for (internal::UsedMemory* next=this->free; next;)
00306   {
00307     internal::UsedMemory* old= next; 
00308     next= next->next;
00309     if (old != this->pre_alloc)
00310       std::free(old);
00311   }
00312   this->used=this->free=0;
00313   if (this->pre_alloc)
00314   {
00315     this->free=this->pre_alloc;
00316     this->free->left=this->pre_alloc->size-ALIGN_SIZE(sizeof(internal::UsedMemory));
00317     this->free->next=0;
00318   }
00319   this->block_num= 4;
00320   this->first_block_usage= 0;
00321 }
00322 
00328 char* Root::strdup(const char* str)
00329 {
00330   return strdup(str, strlen(str));
00331 }
00332 
00344 char* Root::strdup(const char* str, size_t len)
00345 {
00346   char* pos= (char*)alloc(len + 1);
00347   memcpy(pos, str, len);
00348   pos[len]= 0;
00349   return pos;
00350 }
00351 
00352 char* Root::strdup(str_ref v)
00353 {
00354   return strdup(v.data(), v.size());
00355 }
00356 
00366 void* Root::memdup(const void* str, size_t len)
00367 {
00368   void* pos= alloc(len);
00369   memcpy(pos, str, len);
00370   return pos;
00371 }
00372 
00373 }
00374 } /* namespace drizzled */