/* Copyright (c) 1997-2010
   Ewgenij Gawrilow, Michael Joswig (Technische Universitaet Darmstadt, Germany)
   http://www.polymake.de

   This program is free software; you can redistribute it and/or modify it
   under the terms of the GNU General Public License as published by the
   Free Software Foundation; either version 2, or (at your option) any
   later version: http://www.gnu.org/licenses/gpl.txt.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
--------------------------------------------------------------------------------
   $Project: polymake $$Id: lib_init.cc 9991 2010-12-12 23:36:28Z gawrilow $
*/

#include "polymake/internal/lib_init.h"

#include <cstring>
#include <memory>
#include <cstdio>
#include <gmp.h>

#if defined(__GNUC__)
#if __GNUC__==3 && __GNUC_MINOR__==4 || __GNUC__==4
namespace {

# if __GNUC__==3 && __GNUC_PATCHLEVEL__ == 0
typedef __gnu_cxx::__pool_alloc<true,0> raw_alloc;

void* pm_gmp_allocate(size_t n)
{
   return __builtin_expect(n==0,0) ? 0 : raw_alloc::allocate(n);
}

void pm_gmp_deallocate(void* p, size_t n) { raw_alloc::deallocate(p,n); }
# else  // >3.4.0
__gnu_cxx::__pool_alloc<char> gmp_allocator;

void* pm_gmp_allocate(size_t n) { return gmp_allocator.allocate(n); }
void pm_gmp_deallocate(void* p, size_t n) { gmp_allocator.deallocate(reinterpret_cast<char*>(p), n); }
# endif

void* pm_gmp_reallocate(void* p, size_t old_sz, size_t new_sz)
{
   static const bool use_new=getenv("GLIBCPP_FORCE_NEW") || getenv("GLIBCXX_FORCE_NEW");
   const size_t align=8, limit=128;
   if (!use_new && ((old_sz+align-1)&~(align-1)) == ((new_sz+align-1)&~(align-1)) && new_sz < limit)
      return p;
   void* new_p=pm_gmp_allocate(new_sz);
   if (new_p) {
      std::memcpy(new_p, p, old_sz < new_sz ? old_sz : new_sz);
      pm_gmp_deallocate(p,old_sz);
   }
   return new_p;
}

}
#endif // gcc >=3.4
#elif defined(__INTEL_COMPILER)
namespace {

std::allocator<char> gmp_allocator;
void* pm_gmp_allocate(size_t n) { return gmp_allocator.allocate(n); }
void pm_gmp_deallocate(void* p, size_t n) { gmp_allocator.deallocate(reinterpret_cast<char*>(p), n); }
void* pm_gmp_reallocate(void* p, size_t old_sz, size_t new_sz)
{
   void* new_p=pm_gmp_allocate(new_sz);
   if (new_p) {
      std::memcpy(new_p, p, old_sz < new_sz ? old_sz : new_sz);
      pm_gmp_deallocate(p,old_sz);
   }
   return new_p;
}

}
#endif // __INTEL_COMPILER

namespace pm {

void init_gmp_memory_management()
{
   mp_set_memory_functions(pm_gmp_allocate, pm_gmp_reallocate, pm_gmp_deallocate);
}

}

// Local Variables:
// mode:C++
// c-basic-offset:3
// indent-tabs-mode:nil
// End:
