【试着自己写一个STL 】Knife_STL —— alloc.h

来源:互联网 发布:淘宝6.0官网下载安装 编辑:程序博客网 时间:2024/06/06 09:27
#ifndef KNIFE_ALLOC_H#define KNIFE_ALLOC_H#include "config.h"#include "knifeio.h"#include <cstdlib>#include <cstddef>// 通常写注释会中英混杂,其实我英文也不是很好,主要是懒得切换输入法了,就当练练口语交际// 这次实现的就是STL的重要部分了:allocator// 仍旧是SGI_STL 203的实现方法,略微有一些不一样,加上了自己觉得合理的部分// 整体框架是这样的,首先将alloc的使用情况分为release和debug两种,于是// 具体实现还是得看后面的代码注释和侯捷老师的书哦// Release: User -> simple_alloc -> if (n > 128) malloc_alloc / oom_malloc_alloc_template : alloc_template//else   list_alloc : alloc_template / //   hash_alloc : alloc_template ///   bitmap_alloc : alloc_template// Debug: User -> simple_alloc -> debug_alloc// 在这里有一个包装的问题,原文中使用simple_aloc将所有的template问题给解决了// 每个实现只需要保持返回void*就可以了,但我的设计有点弄巧成拙的意思,看来还要多学习_STL_NAMESPACE_BEGIN#define __EXIT_BAD_ALLOC0xbada110c //badalloc#define _THROW_BAD_ALLOC knife::printf("Out of memory while alloc"); exit(__EXIT_BAD_ALLOC)#define _THROW_BAD_REALLOC knife::printf("Out of memory while realloc"); exit(__EXIT_BAD_ALLOC)// default global out_of_memory handler functionvoid def_oom_handler(){knife::printf("Out of memory");}// This function is used to fill the buffer memory// in some system, if we do NOT initialize the buffer memory// OS would NOT 'really' assign the memory to us// Really we should use double or long to acculate, // but we can not assume that the size is 4 aligntemplate <typename T, typename _Size>void memfill(T* ptr, _Size n){char* p = (char*) ptr;while (n--) *p++ = 0;}// 这个malloc是一个借助C库的非常“正常”的allocator// 比较独特的地方在于,他使用一个函数指针,处理out_of_memory的情况// 同时允许用户指定这个指针,并使用用户指定的函数指针去处理这个error// 因为这是类库,并不负责处理用户的内存问题,那种情况太多太复杂了// 我们需要懂得将这种“太过”普适性的问题延迟到用户端,即使用者端// 提供给使用者解决这个问题的接口,让他们能够解决这个问题就可以了// 而且我们可以通过包装这个接口,对用户提供的方案设计的范围加以限制// 但这里还要考虑到用户使用接口的延时性问题,即用户会在可能碰到这个问题// 的地方之后几条语句再使用这个接口去处理问题,这会导致“当时”接口还是空的class malloc_alloc {private:static void* oom_malloc(size_t);static void* oom_realloc(void*, size_t);static void (* _malloc_oom_handler)();public:static void* allocate(size_t n);static void deallocate(void* p, size_t /*n*/);static void* reallocate(void* p, size_t/*old_sz*/, size_t new_sz);static void (* set_malloc_handler(void (*f)())) ();};// Loop the oom_handler until we can malloc the n size memory// 原文因为考虑到true的版本问题,使用了for(;;), 为了可读性// 这里还是使用了while (true)void* malloc_alloc::oom_malloc(size_t n){void* pointer = NULL;while (true){if (NULL == _malloc_oom_handler) _THROW_BAD_ALLOC;_malloc_oom_handler();pointer = malloc(n);if (NULL != pointer) return pointer;}}void* malloc_alloc::oom_realloc(void* p, size_t new_sz){void* pointer = NULL;while (true){if (NULL == _malloc_oom_handler) _THROW_BAD_ALLOC;_malloc_oom_handler();pointer = realloc(p, new_sz);if (NULL != pointer) return pointer;}}void (* malloc_alloc::_malloc_oom_handler)() = 0;void* malloc_alloc::allocate(size_t n){void* pointer = malloc(n);knife::printf(n, pointer);if (NULL == pointer) oom_malloc(n);return pointer;}void malloc_alloc::deallocate(void* p, size_t /* n */){free(p);}void* malloc_alloc::reallocate(void* p, size_t/*old_sz*/, size_t new_sz){void* pointer = realloc(p, new_sz);if (NULL == pointer) oom_realloc(p, new_sz);return pointer;}void(* malloc_alloc::set_malloc_handler(void (*f)())) (){void (*old)() = _malloc_oom_handler;_malloc_oom_handler = f;return old;}template <typename T>class alloc_template {public:typedef T value_type;typedef T*pointer;typedef const T*const_pointer;typedef T&reference;typedef const T& const_reference;typedef ptrdiff_tdifference_type;typedef size_tsize_type;};// 应该还会有malloc的template版本,我加上了// This version is sooo simple that you should not// suppose what will it do while something wrong happened// If some bad things real happened while the user // use allocator of this version, The things we can only do// is to throw a exception or just report this error// and shut downtemplate <typename T>class malloc_alloc_template : public alloc_template<T>{public:static typename alloc_template<T>::pointer allocate(typename alloc_template<T>::size_type);static void deallocate(typename alloc_template<T>::pointer);static typename alloc_template<T>::pointer reallocate(typename alloc_template<T>::pointer,    typename alloc_template<T>::size_type);};template <typename T>typename alloc_template<T>::pointer malloc_alloc_template<T>::allocate(typename alloc_template<T>::size_type n){typename alloc_template<T>::pointer p = (typename alloc_template<T>::pointer) malloc(n);if (NULL != p) return p;else _THROW_BAD_ALLOC;}template <typename T>voidmalloc_alloc_template<T>::deallocate(typename alloc_template<T>::pointer p){free(p);}template <typename T>typename alloc_template<T>::pointermalloc_alloc_template<T>::reallocate(typename alloc_template<T>::pointer p, typename alloc_template<T>::size_type new_sz){typename alloc_template<T>::pointer tmp = (typename alloc_template<T>::pointer) realloc(p, new_sz);if (NULL != tmp) return tmp;else _THROW_BAD_ALLOC;}template <typename T>class oom_malloc_alloc_template : alloc_template<T>{public:typedef void(*OOM_Handler)();static typename alloc_template<T>::pointer allocate(typename alloc_template<T>::size_type);static void deallocate(typename alloc_template<T>::pointer);static typename alloc_template<T>::pointer reallocate(typename alloc_template<T>::pointer,   typename alloc_template<T>::size_type);static OOM_Handler set_oom_handler(OOM_Handler);protected:static void oom_malloc(typename alloc_template<T>::pointer);static void oom_realloc(typename alloc_template<T>::pointer, typename alloc_template<T>::size_type);private:static OOM_Handler CUR_HANDLER;};template <typename T>typename oom_malloc_alloc_template<T>::OOM_Handler oom_malloc_alloc_template<T>::CUR_HANDLER = def_oom_handler;template <typename T>typename alloc_template<T>::pointer oom_malloc_alloc_template<T>::allocate(typename alloc_template<T>::size_type n){typename alloc_template<T>::pointer p = NULL;p = (typename alloc_template<T>::pointer) malloc(n);if (NULL == p) oom_malloc_alloc_template<T>::oom_malloc(p);return p;}template <typename T>voidoom_malloc_alloc_template<T>::deallocate(typename alloc_template<T>::pointer p){free(p);}template <typename T>typename alloc_template<T>::pointeroom_malloc_alloc_template<T>::reallocate(typename alloc_template<T>::pointer p,   typename alloc_template<T>::size_type n){typename alloc_template<T>::pointer tmp_pointer = NULL;tmp_pointer = (typename alloc_template<T>::pointer) realloc(p, n);if (NULL == tmp_pointer) oom_malloc_alloc_template<T>::oom_realloc(p, n);return tmp_pointer;}template <typename T>typename oom_malloc_alloc_template<T>::OOM_Handleroom_malloc_alloc_template<T>::set_oom_handler(OOM_Handler new_handler){OOM_Handler old_handler = CUR_HANDLER;CUR_HANDLER = new_handler;return old_handler;}template <typename T>void oom_malloc_alloc_template<T>::oom_malloc(typename alloc_template<T>::pointer p){typename alloc_template<T>::pointer tmp_pointer;while (true){CUR_HANDLER();tmp_pointer = allocate(p);if (tmp_pointer != NULL) return;}}template <typename T>void oom_malloc_alloc_template<T>::oom_realloc(typename alloc_template<T>::pointer p,   typename alloc_template<T>::size_type new_sz){typename alloc_template<T>::pointer tmp_pointer;while (true){CUR_HANDLER();tmp_pointer = reallocate(p, new_sz);if (tmp_pointer != NULL) return;}}// This is just a copy of the original one,// cause this simple_alloc is simple enoughtemplate<typename T, typename Alloc>class simple_alloc {public:    static T *allocate(size_t n)                { return 0 == n? 0 : (T*) Alloc::allocate(n * sizeof (T)); }    static T *allocate(void)                { return (T*) Alloc::allocate(sizeof (T)); }    static void deallocate(T *p, size_t n)                { if (0 != n) Alloc::deallocate(p, n * sizeof (T)); }    static void deallocate(T *p)                { Alloc::deallocate(p, sizeof (T)); }};// 实际上我之前对alloc的看法是错误的,准确的说,是太简单了// 我以为alloc只是使用了嵌入式指针,既减少内存占用,又同时// 让内存池能够用链表连接起来,后来发现不是的,其实他是将// 小于_MAX_BYTES即128的内存分配视作小内存分配,这种分配容易// 造成内存碎片,他给出的解决方法是,用8 bytes做一个align,// 每个小内存都会被align成一整块(一个ROUND_UP(N)),然后分配出去// 这样就减少内存占用了,同时优化的还有一次性malloc一大块内存// 这样也会减少内存碎片(既优化操作系统内存调度的速度,又减少了// 内存资源的小号),当然在SGI的作者看来,最少是320一次才不容// 易造成碎片class list_alloc {public:static void* allocate(size_t);static void deallocate(void*, size_t);static void* reallocate(void*, size_t);private:union obj {union obj* list_link;char data[1];};  // 最小支持8bytes,更小的部分不会分配static const size_t __ALIGN = 8;static const size_t __MAX_BYTES = 128;static const int __LIST_SIZE = __MAX_BYTES / __ALIGN;static obj* _LIST_POOL[__LIST_SIZE];  static char* list_begin;  static char* list_end;  static size_t heap_size;static size_t ROUND_UP(size_t);static size_t LIST_POS(size_t);  static char* chunk_alloc(size_t, int&);  static void* refill(size_t);};list_alloc::obj* list_alloc::_LIST_POOL[__LIST_SIZE] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};char* list_alloc::list_begin = 0;char* list_alloc::list_end   = 0;size_t list_alloc::heap_size = 0;void* list_alloc::allocate(size_t n){if (n > __MAX_BYTES){return malloc_alloc::allocate(n);}// 获得要分配的list上存储的指针obj** list  = _LIST_POOL + LIST_POS(n);obj* result = *list;if (result == 0) {return refill(ROUND_UP(n));}*list = result->list_link;return result;}void list_alloc::deallocate(void* ptr, size_t n){  obj* q = (obj*) ptr;  obj** list_ptr = NULL;  if (n > __MAX_BYTES)  {    malloc_alloc::deallocate(ptr, n);    return;  }  list_ptr = _LIST_POOL + LIST_POS(n);  q->list_link = *list_ptr;  *list_ptr = q;}size_t list_alloc::ROUND_UP(size_t n){return (n + __ALIGN - 1) & ~(__ALIGN + 1);}// 虽然我们会将ROUND_UP之后的数传入进来// 但这个函数不应该将ROUND_UP作为前提// 这是违反实现原则的size_t list_alloc::LIST_POS(size_t n){return (n + __ALIGN - 1) / __ALIGN - 1;}// alloc a chunk of memory which costs n * nobjs bytes// Before we alloc the mem, we will check the mem we havechar* list_alloc::chunk_alloc(size_t bytes, int& nobjs){  char* result = NULL;  size_t bytes_need = bytes * nobjs;  size_t bytes_left = list_end - list_begin;  // 如果剩下的空间比需要的大,那么直接返回list_begin  // 并且修改list_begin即可  if (bytes_left > bytes_need)  {    result = list_begin;    list_begin += bytes_need;    return result;  }  // 如果剩下的空间必须要的小,但是剩下的空间又够分配  // 一个以上的bytes,那么修改nobjs的值为剩下空间够分  // 配的bytes数量,并且返回list_begin,并修改list_begin  // 的值  else if (bytes_left > bytes)  {    nobjs      = bytes_left / bytes;    bytes_need = bytes * nobjs;    result     = list_begin;    list_begin += bytes_need;    return result;  }  // 如果不剩下任何空间了(或者是剩下的空间不够一个bytes)  // 那么我们重新malloc一块内存,大小为2倍于需求的大小  // 并调整list_begin list_end heap_size的值,让list_begin  // 和list_end分别指向重新非配出来的值,再重新调用一次  // chunk_alloc  else  {    size_t bytes_to_alloc = 2 * bytes_need + ROUND_UP(heap_size);    // 还会剩下一些空间,为防止内存碎片,    // 我们把它们分配到适合的位置去    if (bytes_left > 0)    {      obj** list_ptr = _LIST_POOL + LIST_POS(bytes_left);      ((obj*) list_begin)->list_link = *list_ptr;      *list_ptr = (obj*) list_begin;    }    list_begin = (char*) malloc(bytes_to_alloc);    if (0 == list_begin)    {      int i = bytes;      obj** list_ptr;      obj* p;      for (; i <= __MAX_BYTES; i += __ALIGN)      {        list_ptr = _LIST_POOL + LIST_POS(i);        p = *list_ptr;        if (0 != p)        {          *list_ptr = p -> list_link;          list_begin = (char*) p;          list_end = list_begin + i;          return (chunk_alloc(bytes, nobjs));        }      }    }    list_end   = list_begin + bytes_to_alloc;    heap_size  += bytes_to_alloc;    return chunk_alloc(bytes, nobjs);  }}// 如何在list中分配memory的逻辑是由chunk_alloc// 负责的,当nobjs == 1时,代表只分配了一个obj// 这正是refill所要做的,////void* list_alloc::refill(size_t n){  static int OBJ_NUM = 20;  int nobjs = OBJ_NUM;  char* chunk = chunk_alloc(n, nobjs);  obj **list_ptr, *result;  obj *curr_obj, *next_obj;  if (1 == nobjs) return chunk;  list_ptr = _LIST_POOL + LIST_POS(n);  result = (obj*) chunk;  *list_ptr = next_obj = (obj*) (chunk + n);  for (int i = 1; ;++i)  {    curr_obj = next_obj;    next_obj = (obj*) ((char*)next_obj + n);    if (nobjs - 1 == i)    {      curr_obj->list_link = 0;      break;    }    else    {      curr_obj->list_link = next_obj;    }  }  return result;}// 利用bitmap作出的alloc,等实现了bitmap再说吧。。。template <class T>class bitmap_alloc : public alloc_template<T> {public:};// 利用hash_table的技术,将内存池做成hash_table// 可能在有时会比较有效吧,不过如果要用的话尽可以都试试// 做个试验,就知道最适合自己项目的版本了template <class T>class hash_alloc : public alloc_template<T> {public:};_STL_NAMESPACE_END#endif // KNIFE_ALLOC_H

0 0
原创粉丝点击