#include "stdafx.h"
#include "memory.h"
using namespace ki;
//=========================================================================
#ifdef SUPERTINY
static HANDLE g_heap;
#ifndef _DEBUG
void* __cdecl operator new( size_t siz )
{
return ::HeapAlloc( g_heap, HEAP_GENERATE_EXCEPTIONS, siz );
}
void __cdecl operator delete( void* ptr )
{
::HeapFree( g_heap, 0, ptr );
}
#else
// デバッグ用に回数計測版(^^;
static int allocCounter = 0;
void* __cdecl operator new( size_t siz )
{
++allocCounter;
return ::HeapAlloc( g_heap, HEAP_GENERATE_EXCEPTIONS, siz );
}
void __cdecl operator delete( void* ptr )
{
::HeapFree( g_heap, 0, ptr );
if( ptr != NULL )
--allocCounter;
}
#endif
extern "C"
void* __cdecl memset( void* buf, int ch, size_t n )
{
BYTE v = BYTE(ch&0xff);
BYTE* ptr = (BYTE*)buf;
DWORD vvvv = (v<<24) | (v<<16) | (v<<8) | v;
for(;n>3;n-=4,ptr+=4) *(DWORD*)ptr = vvvv;
for(;n;--n,++ptr) *ptr = v;
return buf;
}
void* __cdecl memmove( void* dst, const void* src, size_t cnt )
{
__asm {
mov esi, [src] ;U esi = const void* src
mov edx, [cnt] ;V edx = void* cnt
mov edi, [dst] ;U edi = ulong dst
mov ebx, edx ;V
mov eax, 03h ;U eax = const ulong 3 (for masking)
add ebx, esi ;V ebx = const void* src+cnt
cmp edi, esi ;
jbe CopyUp ;
cmp edi, ebx ; if( src < dst < src+cnt )
jb CopyDown ; downward copy
CopyUp:
cmp edx, eax ; if( cnt<=3 )
jbe MiniCopy ; byte by byte copy
mov ebx, edi ;U
mov ecx, eax ;V
and ebx, eax ;U ebx = (dst&3)
inc ecx ;V
sub ecx, ebx ; ecx = (4-(dst&3))
and ecx, eax ; ecx = {dst%4 0->0 1->3 2->2 3->1}
sub edx, ecx ;
rep movsb ;N BYTE MOVE (align dst)
mov ecx, edx ;
shr ecx, 2 ; ecx = [rest bytes]/4
and edx, eax ; edx = [rest bytes]%4
rep movsd ;N DWORD MOVE
jmp MiniCopy ;
CopyDown:
std ;
lea esi,[esi+edx-1] ;
lea edi,[edi+edx-1] ;
cmp edx, 4 ; if( cnt<=4 )
jbe MiniCopy ; byte by byte copy
mov ecx, edi ;
and ecx, eax ;
inc ecx ; ecx = {dst%4 0->1 1->2 2->3 3->4}
sub edx, ecx ;
rep movsb ;N BYTE MOVE (align dst @ dword)
sub edi, eax ;U
mov ecx, edx ;V
sub esi, eax ;U
shr ecx, 2 ;V ecx = [rest bytes]/4
and edx, eax ; edx = [rest bytes]%4
rep movsd ;N DWORD MOVE
add edi, eax ;U
add esi, eax ;V
MiniCopy:
mov ecx, edx ;
rep movsb ;N BYTE MOVE
cld ;U
mov eax, [dst] ;V return dst
}
return dst;
}
#endif
#ifdef USE_ORIGINAL_MEMMAN
//=========================================================================
// 効率的なメモリ管理を目指すアロケータ
//=========================================================================
//
// メモリブロック
// 「sizバイト * num個」分の領域を一括確保するのが仕事
//
// 空きブロックには、先頭バイトに [次の空きブロックのindex] を格納。
// これを用いて、先頭への出し入れのみが可能な単方向リストとして扱う。
//
struct ki::MemBlock
{
public:
void Construct( byte siz, byte num );
void Destruct();
void* Alloc( byte siz );
void DeAlloc( void* ptr, byte siz );
bool isAvail();
bool isEmpty( byte num );
bool hasThisPtr( void* ptr, size_t len );
private:
byte* buf_;
byte first_, avail_;
};
void MemBlock::Construct( byte siz, byte num )
{
// 確保
buf_ = ::new byte[siz*num];
first_ = 0;
avail_ = num;
// 連結リスト初期化
for( byte i=0,*p=buf_; i<num; p+=siz )
*p = ++i;
}
inline void MemBlock::Destruct()
{
// 解放
::delete [] buf_;
}
void* MemBlock::Alloc( byte siz )
{
// メモリ切り出し
// ( avail==0 等のチェックは上位層に任せる )
byte* blk = buf_ + siz*first_;
first_ = *blk;
--avail_;
return blk;
}
void MemBlock::DeAlloc( void* ptr, byte siz )
{
// メモリ戻す
// ( 変なポインタ渡されたらだ〜め〜 )
byte* blk = static_cast<byte*>(ptr);
*blk = first_;
first_ = static_cast<byte>((blk-buf_)/siz);
++avail_;
}
inline bool MemBlock::isAvail()
{
// 空きがある?
return (avail_ != 0);
}
inline bool MemBlock::isEmpty( byte num )
{
// 完全に空?
return (avail_ == num);
}
inline bool MemBlock::hasThisPtr( void* ptr, size_t len )
{
// このブロックのポインタ?
return ( buf_<=ptr && ptr<buf_+len );
}
//-------------------------------------------------------------------------
//
// 固定サイズ確保人
// 「sizバイト」の領域を毎回確保するのが仕事
//
// メモリブロックのリストを保持し、空いているブロックを使って
// メモリ要求に応えていく。空きがなくなったら新しくMemBlockを
// 作ってリストに加える。
//
// 最後にメモリ割り当て/解放を行ったBlockをそれぞれ記憶しておき、
// 最初にそこを調べることで高速化を図る。
//
void MemoryManager::FixedSizeMemBlockPool::Construct( byte siz )
{
// メモリマネージャが初期化されるまでは、
// 普通のauto_ptrも使わない方が無難…
struct AutoDeleter
{
AutoDeleter( MemBlock* p ) : ptr_(p) {}
~AutoDeleter() { ::delete [] ptr_; }
void Release() { ptr_ = NULL; }
MemBlock* ptr_;
};
// メモリブロック情報域をちょこっと確保
AutoDeleter a( blocks_ = ::new MemBlock[4] );
// ブロックサイズ等計算
int npb = BLOCK_SIZ/siz;
numPerBlock_ = static_cast<byte>( Min( npb, 255 ) );
fixedSize_ = siz;
// 一個だけブロック作成
blocks_[0].Construct( fixedSize_, numPerBlock_ );
a.Release();
lastA_ = 0;
lastDA_ = 0;
blockNum_ = 1;
blockNumReserved_ = 4;
}
void MemoryManager::FixedSizeMemBlockPool::Destruct()
{
// 各ブロックを解放
for( int i=0; i<blockNum_; ++i )
blocks_[i].Destruct();
// ブロック情報保持領域のメモリも解放
::delete [] blocks_;
blockNum_ = 0;
}
void* MemoryManager::FixedSizeMemBlockPool::Alloc()
{
// ここでlastA_がValidかどうかチェックしないとまずい。
// DeAllocされてなくなってるかもしらないので。
// 前回メモリを切り出したブロックに
// まだ空きがあるかどうかチェック
if( !blocks_[lastA_].isAvail() )
{
// 無かった場合、リストの末尾から順に線形探索
for( int i=blockNum_;; )
{
if( blocks_[--i].isAvail() )
{
// 空きブロック発見〜!
lastA_ = i;
break;
}
if( i == 0 )
{
// 全部埋まってた...
if( blockNum_ == blockNumReserved_ )
{
// しかも作業領域も満杯なので拡張
MemBlock* nb = ::new MemBlock[ blockNum_*2 ];
memmove( nb, blocks_, sizeof(MemBlock)*(blockNum_) );
::delete [] blocks_;
blocks_ = nb;
blockNumReserved_ *= 2;
}
// 新しくブロック構築
blocks_[ blockNum_ ].Construct( fixedSize_, numPerBlock_ );
lastA_ = blockNum_++;
break;
}
}
}
// ブロックから切り出し割り当て
return blocks_[lastA_].Alloc( fixedSize_ );
}
void MemoryManager::FixedSizeMemBlockPool::DeAlloc( void* ptr )
{
// 該当ブロックを探索
const int mx=blockNum_, ln=fixedSize_*numPerBlock_;
for( int u=lastDA_, d=lastDA_-1;; )
{
if( u>=0 )
if( blocks_[u].hasThisPtr(ptr,ln) )
{
lastDA_ = u;
break;
}
else if( u==mx )
{
u = -1;
}
else
{
++u;
}
if( d>=0 )
if( blocks_[d].hasThisPtr(ptr,ln) )
{
lastDA_ = d;
break;
}
else
{
--d;
}
}
// 解放を実行
blocks_[lastDA_].DeAlloc( ptr, fixedSize_ );
// この削除でブロックが完全に空になった場合
if( blocks_[lastDA_].isEmpty( numPerBlock_ ) )
{
// しかも一番後ろのブロックでなかったら
int end = blockNum_-1;
if( lastDA_ != end )
{
// 一番後ろが空だったら解放
if( blocks_[end].isEmpty( numPerBlock_ ) )
{
blocks_[end].Destruct();
--blockNum_;
if( lastA_ > --end )
lastA_ = end;
}
// 後ろと交換
MemBlock tmp( blocks_[lastDA_] );
blocks_[lastDA_] = blocks_[end];
blocks_[end] = tmp;
}
}
}
inline bool MemoryManager::FixedSizeMemBlockPool::isValid()
{
// 既に使用開始されているか?
return (blockNum_ != 0);
}
//-------------------------------------------------------------------------
//
// 最上位層
// 指定サイズにあった FixedSizeMemBlockPool に処理をまわす
//
// lokiの実装では固定サイズアロケータも、必要に応じて
// 動的確保していたが、それは面倒なのでやめました。(^^;
// 最初に64個確保したからと言って、そんなにメモリも喰わないし…。
//
MemoryManager* MemoryManager::pUniqueInstance_;
MemoryManager::MemoryManager()
{
g_heap = ::GetProcessHeap();
// メモリプールをZEROクリア
mem00( pools_, sizeof(pools_) );
// 唯一のインスタンスは私です
pUniqueInstance_ = this;
}
MemoryManager::~MemoryManager()
{
// 構築済みメモリプールを全て解放
for( int i=0; i<SMALL_MAX; ++i )
if( pools_[i].isValid() )
pools_[i].Destruct();
#if defined(SUPERTINY) && defined(_DEBUG)
// リーク検出用
if( allocCounter != 0 )
::MessageBox( NULL, TEXT("MemoryLeak!"), NULL, MB_OK );
#endif
}
void* MemoryManager::Alloc( size_t siz )
{
#if defined(SUPERTINY) && defined(_DEBUG)
++allocCounter;
#endif
// サイズが零か大きすぎるなら
// デフォルトの new 演算子に任せる
uint i = static_cast<uint>( siz-1 );
if( i >= SMALL_MAX )
return ::operator new( siz );
// マルチスレッド対応
AutoLock al(this);
// このサイズのメモリ確保が初めてなら
// ここでメモリプールを作成する。
if( !pools_[i].isValid() )
pools_[i].Construct( static_cast<byte>(siz) );
// ここで割り当て
return pools_[i].Alloc();
}
void MemoryManager::DeAlloc( void* ptr, size_t siz )
{
#if defined(SUPERTINY) && defined(_DEBUG)
--allocCounter;
#endif
// サイズが零か大きすぎるなら
// デフォルトの delete 演算子に任せる
uint i = static_cast<uint>( siz-1 );
if( i >= SMALL_MAX )
{
::operator delete( ptr );
return; // VCで return void が出来ないとは…
}
// マルチスレッド対応
AutoLock al(this);
// ここで解放
pools_[i].DeAlloc( ptr );
}
#else // USE_ORIGNAL_MEMMAN
MemoryManager* MemoryManager::pUniqueInstance_;
MemoryManager::MemoryManager()
{
g_heap = ::GetProcessHeap();
// 唯一のインスタンスは私です
pUniqueInstance_ = this;
}
MemoryManager::~MemoryManager()
{
#if defined(SUPERTINY) && defined(_DEBUG)
// リーク検出用
if( allocCounter != 0 )
::MessageBox( NULL, TEXT("MemoryLeak!"), NULL, MB_OK );
#endif
}
void* MemoryManager::Alloc( size_t siz )
{
return ::operator new(siz);
}
void MemoryManager::DeAlloc( void* ptr, size_t siz )
{
::operator delete(ptr);
}
#endif