atl_alloc.h

atl_alloc.h

/*
 * Copyright (c) 1996-1997
 * Silicon Graphics Computer Systems, Inc.
 *
 * Permission to use, copy, modify, distribute and sell this software
 * and its documentation for any purpose is hereby granted without fee,
 * provided that the above copyright notice appear in all copies and
 * that both that copyright notice and this permission notice appear
 * in supporting documentation.  Silicon Graphics makes no
 * representations about the suitability of this software for any
 * purpose.  It is provided "as is" without express or implied warranty.
 
*/

/*  NOTE: This is an internal header file, included by other STL headers.
 *   You should not attempt to use it directly.
 
*/

#ifndef __SGI_STL_INTERNAL_ALLOC_H
#define __SGI_STL_INTERNAL_ALLOC_H

#ifdef __SUNPRO_CC
#  define __PRIVATE  public
    //  Extra access restrictions prevent us from really making some things
   
//  private.
#else
#  define __PRIVATE  private
#endif

#ifdef __STL_STATIC_TEMPLATE_MEMBER_BUG
#  define __USE_MALLOC
#endif


//  This implements some standard node allocators.  These are
//  NOT the same as the allocators in the C++ draft standard or in
//  in the original STL.  They do not encapsulate different pointer
//  types; indeed we assume that there is only one pointer type.
//  The allocation primitives are intended to allocate individual objects,
//  not larger arenas as with the original STL allocators.

#ifndef __THROW_BAD_ALLOC
#   if defined(__STL_NO_BAD_ALLOC) || !defined(__STL_USE_EXCEPTIONS)
#    include <stdio.h>
#    include <stdlib.h>
#    define __THROW_BAD_ALLOC fprintf(stderr, "out of memory\n"); exit(1)
#   else  /*  Standard conforming out-of-memory handling  */
#    include < new>
#    define __THROW_BAD_ALLOC  throw std::bad_alloc()
#  endif
#endif

#include <stddef.h>
#include <stdlib.h>
#include < string.h>
#include <assert.h>
#ifndef __RESTRICT
#  define __RESTRICT
#endif






//  与多线程有关系的锁控制

#ifdef __STL_THREADS
# include <stl_threads.h>
# define __NODE_ALLOCATOR_THREADS  true
# ifdef __STL_SGI_THREADS
   //  We test whether threads are in use before locking.
  
//  Perhaps this should be moved into stl_threads.h, but that
  
//  probably makes it harder to avoid the procedure call when
  
//  it isn't needed.
     extern "C" {
       extern  int __us_rsthread_malloc;
    }
     //  The above is copied from malloc.h.  Including <malloc.h>
    
//  would be cleaner but fails with certain levels of standard
    
//  conformance.
#   define __NODE_ALLOCATOR_LOCK  if (threads && __us_rsthread_malloc) \
                { _S_node_allocator_lock._M_acquire_lock(); }
#   define __NODE_ALLOCATOR_UNLOCK  if (threads && __us_rsthread_malloc) \
                { _S_node_allocator_lock._M_release_lock(); }
else  /*  !__STL_SGI_THREADS  */
#   define __NODE_ALLOCATOR_LOCK \
        {  if (threads) _S_node_allocator_lock._M_acquire_lock(); }
#   define __NODE_ALLOCATOR_UNLOCK \
        {  if (threads) _S_node_allocator_lock._M_release_lock(); }
# endif
#else
//   Thread-unsafe
#   define __NODE_ALLOCATOR_LOCK
#   define __NODE_ALLOCATOR_UNLOCK
#   define __NODE_ALLOCATOR_THREADS  false
#endif














__STL_BEGIN_NAMESPACE

#if defined(__sgi) && !defined(__GNUC__) && (_MIPS_SIM != _MIPS_SIM_ABI32)
#pragma  set woff 1174
#endif

//  Malloc-based allocator.  Typically slower than default alloc below.
//  Typically thread-safe and more storage efficient.
#ifdef __STL_STATIC_TEMPLATE_MEMBER_BUG
# ifdef __DECLARE_GLOBALS_HERE
     void (* __malloc_alloc_oom_handler)() = 0;
     //  g++ 2.7.2 does not handle static template data members.
else
     extern  void (* __malloc_alloc_oom_handler)();
# endif
#endif

////////////////////////////////////////////////
//  第一级配置器,分配大于 _MAX_BYTES = 128 的空间
////////////////////////////////////////////////

template < int __inst>  //  非类型参数,根本没用上
class __malloc_alloc_template { 

private:

     //  错误处理定义,在这里不断尝试分配空间
   static  void* _S_oom_malloc(size_t);
   static  void* _S_oom_realloc( void*, size_t);

     //  这用于处理内存不足时的回调函数指针
    
//  OOM : out of memory.

#ifndef __STL_STATIC_TEMPLATE_MEMBER_BUG  //  STL 静态成员,在早期某些编译器上有BUG : )
   static  void (* __malloc_alloc_oom_handler)(); 
#endif

public:

   static  void* allocate(size_t __n)
  {
     void* __result = malloc(__n);  //  第一级配置器,直接使用了 malloc

    
//  这是标准 malloc 处理失败以后,给 oom_malloc 处理的机会
     if (0 == __result) __result = _S_oom_malloc(__n);
     return __result;
  }

   static  void deallocate( void* __p, size_t  /*  __n  */)
  {
    free(__p);  //  第一级配置器,直接使用了 free
  }

   static  void* reallocate( void* __p, size_t  /*  old_sz  */, size_t __new_sz)
  {
     void* __result = realloc(__p, __new_sz);  // 第一级配置器,直接使用了 realloc

    
//  这是标准 malloc 处理失败以后,给 oom_malloc 处理的机会
     if (0 == __result) __result = _S_oom_realloc(__p, __new_sz);
     return __result;
  }


   //  设置处理内存不足的情况,指定自已的错误回调处理函数地址

   static  void (* __set_malloc_handler( void (*__f)()))() 
  {
     void (* __old)() = __malloc_alloc_oom_handler;
    __malloc_alloc_oom_handler = __f;
     return(__old);
  }

};

//  静态成员的初始化,保存内存不足时的回调函数指针,初始化为 0
//  malloc_alloc out-of-memory handling

#ifndef __STL_STATIC_TEMPLATE_MEMBER_BUG  //
template < int __inst> 
void (* __malloc_alloc_template<__inst>::__malloc_alloc_oom_handler)() = 0; 
#endif



template < int __inst>
void*
__malloc_alloc_template<__inst>::_S_oom_malloc(size_t __n)
{
     void (* __my_malloc_handler)();
     void* __result;

     for (;;) {
         //  不断尝试分配空间,直到 得到非空指针
        __my_malloc_handler = __malloc_alloc_oom_handler;
         if (0 == __my_malloc_handler) { __THROW_BAD_ALLOC; }

         //  尝试执行自己的回调函数,这样有机会处理分配内存失败的原因。
        
//  一般这里的回调函数,内容是加入对可移动内存块的整理
        (*__my_malloc_handler)();

         //  在处理分配失败原因后,继续分配
        __result = malloc(__n);
         if (__result)  return(__result);
    }
}

template < int __inst>
void* __malloc_alloc_template<__inst>::_S_oom_realloc( void* __p, size_t __n)
{
     void (* __my_malloc_handler)();
     void* __result;

     for (;;) {
         //  这里是对 realloc 的处理,同 oom_alloc 方式一致
        __my_malloc_handler = __malloc_alloc_oom_handler;
         if (0 == __my_malloc_handler) { __THROW_BAD_ALLOC; }
        (*__my_malloc_handler)();

         //  继续尝试分配
        __result = realloc(__p, __n);
         if (__result)  return(__result);
    }
}




//  重新对模板类的声明,这个 0 实际上没有任何意思,好像就是为了让这个类是个模板类
//  这里定义了第一分配器的名字 malloc_alloc

typedef __malloc_alloc_template<0> malloc_alloc;



//  simple_alloc 提供符合 STL  标准的访问函数名称

template< class _Tp,  class _Alloc>
class simple_alloc {

public:
     static _Tp* allocate(size_t __n)
      {  return 0 == __n ? 0 : (_Tp*) _Alloc::allocate(__n *  sizeof (_Tp)); }
     static _Tp* allocate( void)
      {  return (_Tp*) _Alloc::allocate( sizeof (_Tp)); }
     static  void deallocate(_Tp* __p, size_t __n)
      {  if (0 != __n) _Alloc::deallocate(__p, __n *  sizeof (_Tp)); }
     static  void deallocate(_Tp* __p)
      { _Alloc::deallocate(__p,  sizeof (_Tp)); }
};




//  Allocator adaptor to check size arguments for debugging.
//  Reports errors using assert.  Checking can be disabled with
//  NDEBUG, but it's far better to just use the underlying allocator
//  instead when no checking is desired.
//  There is some evidence that this can confuse Purify.
template < class _Alloc>
class debug_alloc {

private:

   enum {_S_extra = 8};   //  Size of space used to store size.  Note
                        
//  that this must be large enough to preserve
                        
//  alignment.

public:

   static  void* allocate(size_t __n)
  {
     char* __result = ( char*)_Alloc::allocate(__n + ( int) _S_extra);
    *(size_t*)__result = __n;
     return __result + ( int) _S_extra;
  }

   static  void deallocate( void* __p, size_t __n)
  {
     char* __real_p = ( char*)__p - ( int) _S_extra;
    assert(*(size_t*)__real_p == __n);
    _Alloc::deallocate(__real_p, __n + ( int) _S_extra);
  }

   static  void* reallocate( void* __p, size_t __old_sz, size_t __new_sz)
  {
     char* __real_p = ( char*)__p - ( int) _S_extra;
    assert(*(size_t*)__real_p == __old_sz);
     char* __result = ( char*)
      _Alloc::reallocate(__real_p, __old_sz + ( int) _S_extra,
                                   __new_sz + ( int) _S_extra);
    *(size_t*)__result = __new_sz;
     return __result + ( int) _S_extra;
  }

};








////////////////////////////////////////////////////////////////////////////////////////////////
//  第二级配置器  //////////////////////////// / 第二级配置器  ////////////////////////// /
///  小型对象分配技术 : )  /////////////////////////////////////////////////////////////// //



//  如果定义了 __USE_MALLOC, 是指第二分配器直接使用 第一分配器,其实就是 malloc  
//  __USE_MALLOC 不影响第一分配器

# ifdef __USE_MALLOC

typedef malloc_alloc alloc;
typedef malloc_alloc single_client_alloc;

else 



//  Default node allocator.
//  With a reasonable compiler, this should be roughly as fast as the
//  original STL class-specific allocators, but with less fragmentation.
//  Default_alloc_template parameters are experimental and MAY
//  DISAPPEAR in the future.  Clients should just use alloc for now.
//
//  Important implementation properties:
//  1. If the client request an object of size > _MAX_BYTES, the resulting
//     object will be obtained directly from malloc.
//  2. In all other cases, we allocate an object of size exactly
//     _S_round_up(requested_size).  Thus the client has enough size
//     information that we can return the object to the proper free list
//     without permanently losing part of the object.
//

//  The first template parameter specifies whether more than one thread
//  may use this allocator.  It is safe to allocate an object from
//  one instance of a default_alloc and deallocate it with another
//  one.  This effectively transfers its ownership to the second one.
//  This may have undesirable effects on reference locality.
//  The second parameter is unreferenced and serves only to allow the
//  creation of multiple default_alloc instances.
//  Node that containers built on different allocator instances have
//  different types, limiting the utility of this approach.



//  这是定义小块内存的大小


#if defined(__SUNPRO_CC) || defined(__GNUC__)
//  breaks if we make these template class members:
   enum {_ALIGN = 8};
   enum {_MAX_BYTES = 128};
   enum {_NFREELISTS = 16};  //  _MAX_BYTES/_ALIGN
#endif



template < bool threads,  int inst>
class __default_alloc_template {

private:
   //  Really we should use static const int x = N
  
//  instead of enum { x = N }, but few compilers accept the former.
#if ! (defined(__SUNPRO_CC) || defined(__GNUC__))
     enum {_ALIGN = 8};
     enum {_MAX_BYTES = 128};
     enum {_NFREELISTS = 16};  //  _MAX_BYTES/_ALIGN
# endif

     //  在分配小块内存时,总是以 _ALIGN 作为边界对齐
    
//  不满 _ALIGN 大小时,要分配至少保存的容量
    
//  这里是计算公式
   static size_t
  _S_round_up(size_t __bytes) 
    {  return (((__bytes) + (size_t) _ALIGN-1) & ~((size_t) _ALIGN - 1)); }



   //  分配内存空间的技术,事实上和 loki 的小型对象分配技术一致
__PRIVATE:
  union _Obj {
        union _Obj* _M_free_list_link;   //  下次 free 的地址
         char _M_client_data[1];     /*  The client sees this.         */
  };


private:

     //  定义了 _NFREELISTS = 16 个小型内存块的指针
    
//  对应大小为 8, 16, 24, 32, 40, 48, 56, 64,  128
    
//  为什么不能支持 4 字节的呢?? 可能是考虑有些机器指针是 64 位,即 8 字节
if defined(__SUNPRO_CC) || defined(__GNUC__) || defined(__HP_aCC)
     static _Obj* __STL_VOLATILE _S_free_list[]; 
         //  Specifying a size results in duplicate def for 4.1
else
     static _Obj* __STL_VOLATILE _S_free_list[_NFREELISTS]; 
# endif

//  这是根据字节长度,决定使用哪个内存块作为分配对象
   static  size_t _S_freelist_index(size_t __bytes) {
         return (((__bytes) + (size_t)_ALIGN-1)/(size_t)_ALIGN - 1);
  }

   //  Returns an object of size __n, and optionally adds to size __n free list.
   static  void* _S_refill(size_t __n);
   //  Allocates a chunk for nobjs of size size.  nobjs may be reduced
  
//  if it is inconvenient to allocate the requested number.
   static  char* _S_chunk_alloc(size_t __size,  int& __nobjs);

   // 内存起始和结束位置

  
//  Chunk allocation state.
   static  char* _S_start_free;  //  这个 _S 好像是 STL 为了表示它是“静态”的成员
   static  char* _S_end_free;
   static size_t _S_heap_size;

# ifdef __STL_THREADS
     static _STL_mutex_lock _S_node_allocator_lock;
# endif

     //  It would be nice to use _STL_auto_lock here.  But we
    
//  don't need the NULL check.  And we do need a test whether
    
//  threads have actually been started.
     class _Lock;
    friend  class _Lock;  //  内部同步锁
     class _Lock {
         public:
            _Lock() { __NODE_ALLOCATOR_LOCK; }
            ~_Lock() { __NODE_ALLOCATOR_UNLOCK; }
    };

public:

   /*  __n must be > 0       */
   static  void* allocate(size_t __n)
  {
     void* __ret = 0;
    
     //  大于 _MAX_BYTES = 128 字节,用一级分配器

     if (__n > (size_t) _MAX_BYTES) {
      __ret = malloc_alloc::allocate(__n);
    }
     else {

         //  得到小型对象分配空间,在 _S_free_list(16个小型区块)中的索引
        
      _Obj* __STL_VOLATILE* __my_free_list
          = _S_free_list 
            + _S_freelist_index(__n);  //  计算出符合条件的区块,这里用了 _ALIGN 对齐方案,最小也要分配 _ALIGN 个字节

      
//  Acquire the lock here with a constructor call.
      
//  This ensures that it is released in exit or during stack
      
//  unwinding.
#     ifndef _NOTHREADS
       /* REFERENCED */
      _Lock __lock_instance;  //  多线程锁
#     endif

       //  先保存上次的分配区块中
      _Obj* __RESTRICT __result = *__my_free_list;

       //  这里有个 if : 0 判断是否还有自由空间,防止为空
       if (__result == 0)
        __ret = _S_refill(_S_round_up(__n));
       else {
           //  前面有个 if : 0 判断,防止为空
        
//  重置记录的下个节点的分配位置,这被放在了 _M_free_list_link 中
        *__my_free_list = __result -> _M_free_list_link;
        __ret = __result;
      }
    }

     return __ret;
  };

   /*  __p may not be 0  */
   static  void deallocate( void* __p, size_t __n)
  {
       //  对于大于 _MAX_BYTES 个数据的,直接由第一级分配器回收
     if (__n > (size_t) _MAX_BYTES)
      malloc_alloc::deallocate(__p, __n);


     else {

         //  打出当前字节数的内容,到底在哪一个小型数据区块中
      _Obj* __STL_VOLATILE*  __my_free_list
          = _S_free_list + _S_freelist_index(__n);

       //  只是为了强制转换指针类型
      _Obj* __q = (_Obj*)__p;

       //  acquire lock
#       ifndef _NOTHREADS
       /* REFERENCED */
      _Lock __lock_instance;
#       endif  /*  _NOTHREADS  */

     //  以当前要释放的区作为空闲区域的首地址,把之前的首地址
      
//  作为 _M_free_list_link的成员量
      
//  这样的结果是,当前释放的地址,最先被重新利用,分配空闲的地址空间被打乱,但我们不关心它是否整齐
      __q -> _M_free_list_link = *__my_free_list;
      *__my_free_list = __q;
       //  lock is released here
    }
  }

   static  void* reallocate( void* __p, size_t __old_sz, size_t __new_sz);

} ;

//  这里把 alloc 定义成了第二分配器
typedef __default_alloc_template<__NODE_ALLOCATOR_THREADS, 0> alloc;
typedef __default_alloc_template< false, 0> single_client_alloc;

template < bool __threads,  int __inst>
inline  bool  operator==( const __default_alloc_template<__threads, __inst>&,
                        const __default_alloc_template<__threads, __inst>&)
{
   return  true;
}

# ifdef __STL_FUNCTION_TMPL_PARTIAL_ORDER
template < bool __threads,  int __inst>
inline  bool  operator!=( const __default_alloc_template<__threads, __inst>&,
                        const __default_alloc_template<__threads, __inst>&)
{
   return  false;
}
# endif  /*  __STL_FUNCTION_TMPL_PARTIAL_ORDER  */



/*  We allocate memory in large chunks in order to avoid fragmenting      */
/*  the malloc heap too much.                                             */
/*  We assume that size is properly aligned.                              */
/*  We hold the allocation lock.                                          */
//  从记忆池中,取数据空间,给 freelist 用
template < bool __threads,  int __inst>
char*
__default_alloc_template<__threads, __inst>::_S_chunk_alloc(size_t __size, 
                                                             int& __nobjs)
{
     char* __result;
    size_t __total_bytes = __size * __nobjs;  //  本次请求的内存空间总量
    size_t __bytes_left = _S_end_free - _S_start_free;  //  这是计算内存池中,剩余的空间数据量

    
//  剩余未分配的内存空间,够此次分配
     if (__bytes_left >= __total_bytes) {
        __result = _S_start_free;  //  把空闲内存空间起始地址 _S_start_free, 作为 返回值
        _S_start_free += __total_bytes;  //  调整空闲区的指针位置 
         return(__result);


     //  虽然不够分配满足 __nobjs 个自由空间,但至少还够满足一个
        
//  这时不按照申请的区块个数,而尝试按当前最大能分配的容量进行分配
    }  else  if (__bytes_left >= __size) {
        __nobjs = ( int)(__bytes_left/__size);  //  计算到底还够满足多少个
        __total_bytes = __size * __nobjs;  //  重新计算实际占用的空间
        __result = _S_start_free;  //  把首地址返回
        _S_start_free += __total_bytes;  //  调整空闲区的指针位置 
         return(__result); 


    }  else {
        size_t __bytes_to_get =   //  准备申请的内存数据量大小
      2 * __total_bytes 
      + _S_round_up(_S_heap_size >> 4);  //  这是一个随分配次数在不断增长的附加量


        
//  如果有没有分配完的小空间,尝试把它加入接近它的那个空间列表中,榨干最后一点剩余价值
        
//  Try to make use of the left-over piece.
         if (__bytes_left > 0) {
            _Obj* __STL_VOLATILE* __my_free_list =
                        _S_free_list + _S_freelist_index(__bytes_left);

             //  这是把内存池中未分配空间的起始位置,链入空闲链表
            ((_Obj*)_S_start_free) -> _M_free_list_link = *__my_free_list;
            *__my_free_list = (_Obj*)_S_start_free;  //  修正空闲链表的起始指针为当前内存池
        }

         //  用最原始的分配器,分配空间:这里有池的概念,每次分配的空间都是原来空间的 2 倍,然后由第二分配器自己管理
        _S_start_free = ( char*)malloc(__bytes_to_get);

         if (0 == _S_start_free) {
             //  竟然会堆空间不足了!!!, 没办法搞了吗??不,尝试在已经拿到的空闲空间中找找看

            size_t __i;
            _Obj* __STL_VOLATILE* __my_free_list;
            _Obj* __p;
             //  Try to make do with what we have.  That can't
            
//  hurt.  We do not try smaller requests, since that tends
            
//  to result in disaster on multi-process machines.

            
//  尝试从已经分配了,但还没有使用的 freelist 链表空间中,“借”一个
            
//  而且查找的时候,只在比它大的表中查,不查比它小的
            
//  因为小的可能地址不连续,大的一块就可以满足,大的地址是肯定连续的

             for (__i = __size;  //  __size 是和 _ALIGN 对齐了的,肯定是它的整数倍
                 __i <= (size_t) _MAX_BYTES;
                 __i += (size_t) _ALIGN) 
            {
                __my_free_list = _S_free_list + _S_freelist_index(__i);  //  计算合适的区块
                __p = *__my_free_list;  //  记录空闲区块的头指针 
                 if (0 != __p) { 
                     //  如果空闲区块不为空,就算找到了一个
                    *__my_free_list = __p -> _M_free_list_link;  //  把这个空闲块从链表中拆出来
                    _S_start_free = ( char*)__p;  //  起始的空闲池位置设置为 从链表中拆出来的空间
                    _S_end_free = _S_start_free + __i;  //  这是结束位置

                    
//  因为从链表中拆了一块出来,所以已经有空闲的内存了
                    
//  递归调用,按先前的流程尝试分配空间
                     return(_S_chunk_alloc(__size, __nobjs)); 
                     //  Any leftover piece will eventually make it to the
                    
//  right free list.
                }
            }
        
             //  二级分配器一点办法也没有了,让一级分配器再想想法子
            
//  一级分配器有一个回调函数,指定分配空间异常的回调用户处理 _S_oom_malloc

        _S_end_free = 0;     //  In case of exception.
            _S_start_free = ( char*)malloc_alloc::allocate(__bytes_to_get);
             //  如果一级分配器也没有办法解决,那么它会锁死在那个尝试释放内存的函数里
            
//  根本不会走到这里来

            
//  This should either throw an
            
//  exception or remedy the situation.  Thus we assume it
            
//  succeeded.
        }

         //  如果代码能走到这里,它肯定是由 malloc 得到了有效空间
        
//  PS: 由空闲链表中拆出来的空间,不是在这里在返回的, 它直接在中间就结束了
        
//  因为拆出来的内存空间大小,与 __bytes_to_get 是不一样的
        _S_heap_size += __bytes_to_get;
        _S_end_free = _S_start_free + __bytes_to_get;
         return(_S_chunk_alloc(__size, __nobjs));  //  已经得到了可分配的空间,递归调用,按先前的流程尝试分配空间
    }
}


/*  Returns an object of size __n, and optionally adds to size __n free list. */
/*  We assume that __n is properly aligned.                                 */
/*  We hold the allocation lock.                                          */
template < bool __threads,  int __inst>
void*
__default_alloc_template<__threads, __inst>::_S_refill(size_t __n)
{
     int __nobjs = 20;  //  试图分配 20 个空间,大小为 __n        
                    
//  这里的 __n 是对齐以后的,也就是在 freelist 中一定有它的存在位置

     char* __chunk = _S_chunk_alloc(__n, __nobjs);  //  当心,__nobjs 是引用传送
    _Obj* __STL_VOLATILE* __my_free_list;
    _Obj* __result;
    _Obj* __current_obj;
    _Obj* __next_obj;
     int __i;
    
     //  如果只分配到一个小内存块,这一小块将成为自由的,它不在其它的连续空间内
    
//  但这种算法,在回收时,又不并心它的指向位置,或者说它只是用链表接在一起的方式回收
    
//  不在一起,并没有关系
     if (1 == __nobjs)  return(__chunk);
    __my_free_list = _S_free_list + _S_freelist_index(__n);

     /*  Build free list in chunk  */
     //  调整数据空间之前的准备
      __result = (_Obj*)__chunk;  //  指针声明,assume __chunk 分配了 _Obj* 的类型

      
//  初始化 __my_free_list(局部变量)为空闲区块中,下一次可分配空间的位置
      
//  因为一次从内存中分配的空间是连续多个的(最多为20个,空闲19个)
      *__my_free_list = __next_obj = (_Obj*)(__chunk + __n); 
    
       //  把连续的空间,用链表连接在一起
       for (__i = 1; ; __i++) {  //  这里的 for 循环没有直接写结束条件,是因为在没有最后一个链表内容时,要补下次可分配的空间地址为 0 
        __current_obj = __next_obj;
        __next_obj = (_Obj*)(( char*)__next_obj + __n);  //  调整下次分配空间的地址,实际上这里是在 (char*)__next_obj += n;
         if (__nobjs - 1 == __i) {
            __current_obj -> _M_free_list_link = 0;   //  0 是表示没有空闲空间了
             break;
        }  else {
            __current_obj -> _M_free_list_link = __next_obj;  //  把空闲数据串接在一起
        }
      }
     return(__result);  //  返回了分配空间的首地址,恰好是一个没有链接在空闲空间中的,大小为 n 的对象
}


//  第二分配器的 reallocate 实现,没什么
template < bool threads,  int inst>
void*
__default_alloc_template<threads, inst>::reallocate( void* __p,
                                                    size_t __old_sz,
                                                    size_t __new_sz)
{
     void* __result;
    size_t __copy_sz;

     if (__old_sz > (size_t) _MAX_BYTES && __new_sz > (size_t) _MAX_BYTES) {
         return(realloc(__p, __new_sz));
    }
     if (_S_round_up(__old_sz) == _S_round_up(__new_sz))  return(__p);
    __result = allocate(__new_sz);
    __copy_sz = __new_sz > __old_sz? __old_sz : __new_sz;
    memcpy(__result, __p, __copy_sz);
    deallocate(__p, __old_sz);
     return(__result);
}

#ifdef __STL_THREADS
    template < bool __threads,  int __inst>
    _STL_mutex_lock
    __default_alloc_template<__threads, __inst>::_S_node_allocator_lock
        __STL_MUTEX_INITIALIZER;
#endif

/////////////////////////////////////////////////////////////////////////////////////// /
///  分配器二,初始化静态的成员变量  ////////////  分配器二,初始化静态的成员变量 ///  
/////////////////////////////////////////////////////////////////////////////////////// /

template < bool __threads,  int __inst>
char* __default_alloc_template<__threads, __inst>::_S_start_free = 0;  //  记忆池的起始位置

template < bool __threads,  int __inst>
char* __default_alloc_template<__threads, __inst>::_S_end_free = 0;  //  终止位置

template < bool __threads,  int __inst>
size_t __default_alloc_template<__threads, __inst>::_S_heap_size = 0;  //  堆大小

//  这里累了半死写这么长
//  实际上是 _NFREELISTS 的 freelist 指针列表初始化的写法,与编译器BUG有关
template < bool __threads,  int __inst>
typename __default_alloc_template<__threads, __inst>::_Obj* __STL_VOLATILE
__default_alloc_template<__threads, __inst> ::_S_free_list[
if defined(__SUNPRO_CC) || defined(__GNUC__) || defined(__HP_aCC)
    _NFREELISTS  //  这里应用上了文件域定义的   enum {_NFREELISTS = 16};  //  _MAX_BYTES/_ALIGN
else
    __default_alloc_template<__threads, __inst>::_NFREELISTS   //  这是直接用了类内部的 enum { _NFREELISTS = 16};
# endif
] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, };
//  The 16 zeros are necessary to make version 4.1 of the SunPro
//  compiler happy.  Otherwise it appears to allocate too little
//  space for the array.

#endif /* ! __USE_MALLOC */


















//  This implements allocators as specified in the C++ standard.  
//
//  Note that standard-conforming allocators use many language features
//  that are not yet widely implemented.  In particular, they rely on
//  member templates, partial specialization, partial ordering of function
//  templates, the typename keyword, and the use of the template keyword
//  to refer to a template member of a dependent type.

#ifdef __STL_USE_STD_ALLOCATORS

template < class _Tp>
class allocator {
  typedef alloc _Alloc;           //  The underlying allocator.
public:
  typedef size_t     size_type;
  typedef ptrdiff_t  difference_type;
  typedef _Tp*       pointer;
  typedef  const _Tp* const_pointer;
  typedef _Tp&       reference;
  typedef  const _Tp& const_reference;
  typedef _Tp        value_type;

  template < class _Tp1>  struct rebind {
    typedef allocator<_Tp1> other;
  };

  allocator() __STL_NOTHROW {}
  allocator( const allocator&) __STL_NOTHROW {}
  template < class _Tp1> allocator( const allocator<_Tp1>&) __STL_NOTHROW {}
  ~allocator() __STL_NOTHROW {}

  pointer address(reference __x)  const {  return &__x; }
  const_pointer address(const_reference __x)  const {  return &__x; }

   //  __n is permitted to be 0.  The C++ standard says nothing about what
  
//  the return value is when __n == 0.
  _Tp* allocate(size_type __n,  const  void* = 0) {
     return __n != 0 ? static_cast<_Tp*>(_Alloc::allocate(__n *  sizeof(_Tp))) 
                    : 0;
  }

   //  __p is not permitted to be a null pointer.
   void deallocate(pointer __p, size_type __n)
    { _Alloc::deallocate(__p, __n *  sizeof(_Tp)); }

  size_type max_size()  const __STL_NOTHROW 
    {  return size_t(-1) /  sizeof(_Tp); }

   void construct(pointer __p,  const _Tp& __val) {  new(__p) _Tp(__val); }
   void destroy(pointer __p) { __p->~_Tp(); }
};

template<>
class allocator< void> {
public:
  typedef size_t      size_type;
  typedef ptrdiff_t   difference_type;
  typedef  void*       pointer;
  typedef  const  void* const_pointer;
  typedef  void        value_type;

  template < class _Tp1>  struct rebind {
    typedef allocator<_Tp1> other;
  };
};


template < class _T1,  class _T2>
inline  bool  operator==( const allocator<_T1>&,  const allocator<_T2>&) 
{
   return  true;
}

template < class _T1,  class _T2>
inline  bool  operator!=( const allocator<_T1>&,  const allocator<_T2>&)
{
   return  false;
}

//  Allocator adaptor to turn an SGI-style allocator (e.g. alloc, malloc_alloc)
//  into a standard-conforming allocator.   Note that this adaptor does
//  *not* assume that all objects of the underlying alloc class are
//  identical, nor does it assume that all of the underlying alloc's
//  member functions are static member functions.  Note, also, that 
//  __allocator<_Tp, alloc> is essentially the same thing as allocator<_Tp>.

template < class _Tp,  class _Alloc>
struct __allocator {
  _Alloc __underlying_alloc;

  typedef size_t    size_type;
  typedef ptrdiff_t difference_type;
  typedef _Tp*       pointer;
  typedef  const _Tp* const_pointer;
  typedef _Tp&       reference;
  typedef  const _Tp& const_reference;
  typedef _Tp        value_type;

  template < class _Tp1>  struct rebind {
    typedef __allocator<_Tp1, _Alloc> other;
  };

  __allocator() __STL_NOTHROW {}
  __allocator( const __allocator& __a) __STL_NOTHROW
    : __underlying_alloc(__a.__underlying_alloc) {}
  template < class _Tp1> 
  __allocator( const __allocator<_Tp1, _Alloc>& __a) __STL_NOTHROW
    : __underlying_alloc(__a.__underlying_alloc) {}
  ~__allocator() __STL_NOTHROW {}

  pointer address(reference __x)  const {  return &__x; }
  const_pointer address(const_reference __x)  const {  return &__x; }

   //  __n is permitted to be 0.
  _Tp* allocate(size_type __n,  const  void* = 0) {
     return __n != 0 
        ? static_cast<_Tp*>(__underlying_alloc.allocate(__n *  sizeof(_Tp))) 
        : 0;
  }

   //  __p is not permitted to be a null pointer.
   void deallocate(pointer __p, size_type __n)
    { __underlying_alloc.deallocate(__p, __n *  sizeof(_Tp)); }

  size_type max_size()  const __STL_NOTHROW 
    {  return size_t(-1) /  sizeof(_Tp); }

   void construct(pointer __p,  const _Tp& __val) {  new(__p) _Tp(__val); }
   void destroy(pointer __p) { __p->~_Tp(); }
};

template < class _Alloc>
class __allocator< void, _Alloc> {
  typedef size_t      size_type;
  typedef ptrdiff_t   difference_type;
  typedef  void*       pointer;
  typedef  const  void* const_pointer;
  typedef  void        value_type;

  template < class _Tp1>  struct rebind {
    typedef __allocator<_Tp1, _Alloc> other;
  };
};

template < class _Tp,  class _Alloc>
inline  bool  operator==( const __allocator<_Tp, _Alloc>& __a1,
                        const __allocator<_Tp, _Alloc>& __a2)
{
   return __a1.__underlying_alloc == __a2.__underlying_alloc;
}

#ifdef __STL_FUNCTION_TMPL_PARTIAL_ORDER
template < class _Tp,  class _Alloc>
inline  bool  operator!=( const __allocator<_Tp, _Alloc>& __a1,
                        const __allocator<_Tp, _Alloc>& __a2)
{
   return __a1.__underlying_alloc != __a2.__underlying_alloc;
}
#endif /* __STL_FUNCTION_TMPL_PARTIAL_ORDER */

//  Comparison operators for all of the predifined SGI-style allocators.
//  This ensures that __allocator<malloc_alloc> (for example) will
//  work correctly.

template < int inst>
inline  bool  operator==( const __malloc_alloc_template<inst>&,
                        const __malloc_alloc_template<inst>&)
{
   return  true;
}

#ifdef __STL_FUNCTION_TMPL_PARTIAL_ORDER
template < int __inst>
inline  bool  operator!=( const __malloc_alloc_template<__inst>&,
                        const __malloc_alloc_template<__inst>&)
{
   return  false;
}
#endif /* __STL_FUNCTION_TMPL_PARTIAL_ORDER */


template < class _Alloc>
inline  bool  operator==( const debug_alloc<_Alloc>&,
                        const debug_alloc<_Alloc>&) {
   return  true;
}

#ifdef __STL_FUNCTION_TMPL_PARTIAL_ORDER
template < class _Alloc>
inline  bool  operator!=( const debug_alloc<_Alloc>&,
                        const debug_alloc<_Alloc>&) {
   return  false;
}
#endif /* __STL_FUNCTION_TMPL_PARTIAL_ORDER */

//  Another allocator adaptor: _Alloc_traits.  This serves two
//  purposes.  First, make it possible to write containers that can use
//  either SGI-style allocators or standard-conforming allocator.
//  Second, provide a mechanism so that containers can query whether or
//  not the allocator has distinct instances.  If not, the container
//  can avoid wasting a word of memory to store an empty object.

//  This adaptor uses partial specialization.  The general case of
//  _Alloc_traits<_Tp, _Alloc> assumes that _Alloc is a
//  standard-conforming allocator, possibly with non-equal instances
//  and non-static members.  (It still behaves correctly even if _Alloc
//  has static member and if all instances are equal.  Refinements
//  affect performance, not correctness.)

//  There are always two members: allocator_type, which is a standard-
//  conforming allocator type for allocating objects of type _Tp, and
//  _S_instanceless, a static const member of type bool.  If
//  _S_instanceless is true, this means that there is no difference
//  between any two instances of type allocator_type.  Furthermore, if
//  _S_instanceless is true, then _Alloc_traits has one additional
//  member: _Alloc_type.  This type encapsulates allocation and
//  deallocation of objects of type _Tp through a static interface; it
//  has two member functions, whose signatures are
//     static _Tp* allocate(size_t)
//     static void deallocate(_Tp*, size_t)

//  The fully general version.

template < class _Tp,  class _Allocator>
struct _Alloc_traits
{
   static  const  bool _S_instanceless =  false;
  typedef typename _Allocator::__STL_TEMPLATE rebind<_Tp>::other 
          allocator_type;
};

template < class _Tp,  class _Allocator>
const  bool _Alloc_traits<_Tp, _Allocator>::_S_instanceless;

//  The version for the default allocator.

template < class _Tp,  class _Tp1>
struct _Alloc_traits<_Tp, allocator<_Tp1> >
{
   static  const  bool _S_instanceless =  true;
  typedef simple_alloc<_Tp, alloc> _Alloc_type;
  typedef allocator<_Tp> allocator_type;
};

//  Versions for the predefined SGI-style allocators.

template < class _Tp,  int __inst>
struct _Alloc_traits<_Tp, __malloc_alloc_template<__inst> >
{
   static  const  bool _S_instanceless =  true;
  typedef simple_alloc<_Tp, __malloc_alloc_template<__inst> > _Alloc_type;
  typedef __allocator<_Tp, __malloc_alloc_template<__inst> > allocator_type;
};

template < class _Tp,  bool __threads,  int __inst>
struct _Alloc_traits<_Tp, __default_alloc_template<__threads, __inst> >
{
   static  const  bool _S_instanceless =  true;
  typedef simple_alloc<_Tp, __default_alloc_template<__threads, __inst> > 
          _Alloc_type;
  typedef __allocator<_Tp, __default_alloc_template<__threads, __inst> > 
          allocator_type;
};

template < class _Tp,  class _Alloc>
struct _Alloc_traits<_Tp, debug_alloc<_Alloc> >
{
   static  const  bool _S_instanceless =  true;
  typedef simple_alloc<_Tp, debug_alloc<_Alloc> > _Alloc_type;
  typedef __allocator<_Tp, debug_alloc<_Alloc> > allocator_type;
};

//  Versions for the __allocator adaptor used with the predefined
//  SGI-style allocators.

template < class _Tp,  class _Tp1,  int __inst>
struct _Alloc_traits<_Tp, 
                     __allocator<_Tp1, __malloc_alloc_template<__inst> > >
{
   static  const  bool _S_instanceless =  true;
  typedef simple_alloc<_Tp, __malloc_alloc_template<__inst> > _Alloc_type;
  typedef __allocator<_Tp, __malloc_alloc_template<__inst> > allocator_type;
};

template < class _Tp,  class _Tp1,  bool __thr,  int __inst>
struct _Alloc_traits<_Tp, 
                      __allocator<_Tp1, 
                                  __default_alloc_template<__thr, __inst> > >
{
   static  const  bool _S_instanceless =  true;
  typedef simple_alloc<_Tp, __default_alloc_template<__thr,__inst> > 
          _Alloc_type;
  typedef __allocator<_Tp, __default_alloc_template<__thr,__inst> > 
          allocator_type;
};

template < class _Tp,  class _Tp1,  class _Alloc>
struct _Alloc_traits<_Tp, __allocator<_Tp1, debug_alloc<_Alloc> > >
{
   static  const  bool _S_instanceless =  true;
  typedef simple_alloc<_Tp, debug_alloc<_Alloc> > _Alloc_type;
  typedef __allocator<_Tp, debug_alloc<_Alloc> > allocator_type;
};


#endif /* __STL_USE_STD_ALLOCATORS */

#if defined(__sgi) && !defined(__GNUC__) && (_MIPS_SIM != _MIPS_SIM_ABI32)
#pragma reset woff 1174
#endif

__STL_END_NAMESPACE

#undef __PRIVATE

#endif /* __SGI_STL_INTERNAL_ALLOC_H */

//  Local Variables:
//  mode:C++
//  End:

你可能感兴趣的:(atl_alloc.h)