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

#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_ALLOC		0xbada110c //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 function
void 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 align
template <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_t		difference_type;
	typedef size_t			size_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 down
template <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>
void
malloc_alloc_template<T>::deallocate(typename alloc_template<T>::pointer p)
{
	free(p);
}

template <typename T>
typename alloc_template<T>::pointer
malloc_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>
void
oom_malloc_alloc_template<T>::deallocate(typename alloc_template<T>::pointer p)
{
	free(p);
}

template <typename T>
typename alloc_template<T>::pointer
oom_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_Handler
oom_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 enough
template<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 have
char* 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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值