//
//<止まらないalignの再現コード> for 3.51
//
//  1)最初に先頭が偶然8KBにアライメントが合っていたとします
//    +----------------+----------------+----------------+----------------+
//    | ADDRESS        |0x2000(top_)    |0x3000          |0x4000          |
//    +----------------+----------------+----------------+----------------+
//    | DATA           |                <                |                |
//    +----------------+----------------+----------------+----------------+
//
//  2)1バイト書き込んでからalign(8192)を呼び出します。
//    +----------------+----------------+----------------+----------------+
//    | ADDRESS        |0x2000(top_)    |0x3000          |0x4000          |
//    +----------------+----------------+----------------+----------------+
//    | DATA           |*               |                |                |
//    +----------------+----------------+----------------+----------------+
//
//  3)4096バイト目の書き込み時にgrowかおこり、
//    今度は先頭が4KBにアライメントが合ったとします。(8KBには合ってない)
//    +----------------+----------------+----------------+----------------+
//    | ADDRESS        |0x5000(top_)    |0x6000          |0x7000          |
//    +----------------+----------------+----------------+----------------+
//    | DATA           |****************|                <                |
//    +----------------+----------------+----------------+----------------+
//
//  4)すると書き込もうとしている位置は8KBにアライメントが
//    合っているにもかかわらず1バイト書き込んでしまいます。(db内でgrow直後に書き込んでいるため)
//    +----------------+----------------+----------------+----------------+
//    | ADDRESS        |0x5000(top_)    |0x6000          |0x7000          |
//    +----------------+----------------+----------------+----------------+
//    | DATA           |****************|*               <                |
//    +----------------+----------------+----------------+----------------+
//
//  5)8196バイト目にgrowがおこり、先頭が偶然8KBにアライメントが合ったとします。
//    +----------------+----------------+----------------+----------------+
//    | ADDRESS        |0x8000(top_)    |0x9000          |0xA000          |
//    +----------------+----------------+----------------+----------------+
//    | DATA           |****************|****************|                <
//    +----------------+----------------+----------------+----------------+
//
//  6)すると書き込もうとしている位置は8KBにアライメントが
//    合っているにもかかわらず1バイト書き込んでしまいます。
//    +----------------+----------------+----------------+----------------+
//    | ADDRESS        |0x8000(top_)    |0x9000          |0xA000          |
//    +----------------+----------------+----------------+----------------+
//    | DATA           |****************|****************|*               <
//    +----------------+----------------+----------------+----------------+
//  (以降繰り返し)
//
//
//<対処法>
// 
//  CodeArrayのメンバ変数にtop_align_を用意する
//    private:
//      size_t top_align_;
//
//  CodeArrayのコンストラクタでtop_align_をALIGN_PAGE_SIZEで初期化する
//    , top_align_( ALIGN_PAGE_SIZE)
//
//  growMemory内の２行を以下の様に変更
//    uint8 *newAllocPtr = reinterpret_cast<uint8*>(alloc_->alloc(newSize + top_align_));
//    uint8 *newTop = getAlignedAddress(newAllocPtr, top_align_);
//
//  CodeArray内にアライメントの要求を行う関数を用意しalign()の先頭で呼び出す。
//    protected:
//      void request_align( size_t align) // 名前は適当です
//      {
//        if(align < 1 || (align & (align - 1))) throw ERR_BAD_ALIGN;
//        if(isAutoGrow() && align > top_align_) {
//          // ザイズを変えずにアライメントだけ変更する
//          top_align_ = align;
//          uint8 *newAllocPtr = reinterpret_cast<uint8*>(alloc_->alloc(maxSize_ + top_align_));
//          if (newAllocPtr == 0) throw ERR_CANT_ALLOC;
//          uint8 *newTop = getAlignedAddress(newAllocPtr, top_align_);
//          for (size_t i = 0; i < size_; i++) newTop[i] = top_[i];
//          alloc_->free(allocPtr_);
//          allocPtr_ = newAllocPtr;
//          top_ = newTop;
//        }
//      }
//
//  コレで大丈夫だと思います。
//  この関数は継承先から呼び出し可能であると嬉しいです。
//  Nバイトnop等、分割できないバイト列でもアライメントしてみたいので。
//
#include <stdint.h>
#include <iostream>
#include <iomanip>
#include <xbyak/xbyak.h>

namespace {

//----------------
// 例外クラス
//
class InfiniteAllocateException {};

//----------------
// 8KB/4KBアライメントのメモリを交互に返すアロケータ
//
class Alloc8K4K : public Xbyak::Allocator {
  int alloc_count_;
  static int const max_alloc = 15;
public:

  Alloc8K4K() : alloc_count_( 0) {}

  Xbyak::uint8 *alloc( size_t size) {
    if( alloc_count_ > max_alloc) throw InfiniteAllocateException();

    intptr_t nap, ap;
    if( alloc_count_ % 2) {
      // 4K
      nap = reinterpret_cast< intptr_t>(new Xbyak::uint8[size + sizeof(intptr_t) + 4096 + 8192 -1]);
       ap = ((nap + sizeof(intptr_t) + 8192 - 1) & ~(8192-1)) + 4096;
    } else {
      // 8K
      nap = reinterpret_cast< intptr_t>( new Xbyak::uint8[size + sizeof(intptr_t) + 8192 -1]);
       ap = (nap + sizeof(intptr_t) + 8192 - 1) & ~(8192-1);
    }
    *reinterpret_cast< intptr_t*>(ap - sizeof(intptr_t)) = nap; // 保管
    ++alloc_count_;

    std::cout << std::right << std::dec;
    std::cout << "alloc(" << std::setw(2) << alloc_count_ << ")";
    std::cout << std::hex;
    std::cout << std::setw(16) << ap;
    std::cout << std::left << std::dec;
    std::cout << "  size = " << std::setw(16) << size;
    std::cout << std::endl;

    return reinterpret_cast< Xbyak::uint8*>( ap);
  }

  void free( Xbyak::uint8*p) {
    Xbyak::uint8 *nap = reinterpret_cast< Xbyak::uint8*>( *reinterpret_cast< intptr_t*>(reinterpret_cast< intptr_t>(p) - sizeof(intptr_t)));
    delete[] nap;
  }
};

//----------------
// テストコード
//
class TestCode1 : Xbyak::CodeGenerator {
public:
  TestCode1( Xbyak::Allocator *alc) : CodeGenerator( 4096, Xbyak::AutoGrow, alc) {
    nop();
    align( 8192);
    std::cout << std::left << std::dec;
    std::cout << "getSize()=" << getSize() << std::endl;
  }
};

} // namespace

//----------------
// main
//
using namespace std;
int main() {

  Alloc8K4K alc;

  try {
    TestCode1 *t1 = new TestCode1(&alc);
    delete t1;
  } catch( InfiniteAllocateException) {
    cout << "Stop : Detect infinite allocation." << endl;
  } catch( bad_alloc) {
    cout << "Stop : Allocate error." << endl;
  } catch( ...) {
    cout << "Stop : Unknown error." << endl;
  }

  {
    cout << "\nwait." << endl;
    int x; cin >> x;
  }
}
