/****
* memory.h
*
* Interface to the Boehm Garbage Collector.
*****/

#ifndef MEMORY_H
#define MEMORY_H

#include <list>
#include <vector>
#include <stack>
#include <map>
#include <deque>
#include <string>
#include <sstream>
#include <unordered_map>

#ifdef USEGC

#define GC_THREADS
#ifdef __clang__
#define GC_ATTR_EXPLICIT
#define GC_NOEXCEPT
#endif
#include <gc.h>

#ifdef GC_DEBUG
extern "C" {
#include <gc_backptr.h>
}
#endif

void* asy_malloc(size_t n);
void* asy_malloc_atomic(size_t n);

#undef GC_MALLOC
#undef GC_MALLOC_ATOMIC

#define GC_MALLOC(sz) asy_malloc(sz)
#define GC_MALLOC_ATOMIC(sz) asy_malloc_atomic(sz)

#include <gc_allocator.h>
#include <gc_cpp.h>

#define gc_allocator gc_allocator_ignore_off_page

#else // USEGC

using std::allocator;
#define gc_allocator allocator

class gc {};
class gc_cleanup {};

enum GCPlacement {UseGC, NoGC, PointerFreeGC};

inline void* operator new(size_t size, GCPlacement) {
 return operator new(size);
}

inline void* operator new[](size_t size, GCPlacement) {
                          return operator new(size);
                        }

template<class T>
struct GC_type_traits {};

#define GC_DECLARE_PTRFREE(T)                   \
 template<> struct GC_type_traits<T> {}

#endif // USEGC

namespace mem {

#define GC_CONTAINER(KIND)                                              \
 template <typename T>                                                 \
 struct KIND : public std::KIND<T, gc_allocator<T> >, public gc {      \
   KIND() : std::KIND<T, gc_allocator<T> >() {}                        \
   KIND(size_t n) : std::KIND<T, gc_allocator<T> >(n) {}               \
   KIND(size_t n, const T& t) : std::KIND<T, gc_allocator<T> >(n,t) {} \
 }

GC_CONTAINER(list);
GC_CONTAINER(vector);
GC_CONTAINER(deque);

template <typename T, typename Container = vector<T> >
struct stack : public std::stack<T, Container>, public gc {};

template <typename T, typename S >
struct pair : public std::pair<T, S>, public gc {
 pair(T t, S s) : std::pair<T,S>(t,s) {}
};

#define PAIR_ALLOC gc_allocator<std::pair<const Key,T> > /* space */

#undef GC_CONTAINER

#define GC_CONTAINER(KIND)                                              \
 template <typename Key,                                               \
           typename T,                                                 \
           typename Compare = std::less<Key> >                         \
 struct KIND : public std::KIND<Key,T,Compare,PAIR_ALLOC>, public gc   \
 {                                                                     \
   KIND() : std::KIND<Key,T,Compare,PAIR_ALLOC> () {}                  \
 }

GC_CONTAINER(map);
GC_CONTAINER(multimap);

#undef GC_CONTAINER

#define GC_CONTAINER(KIND)                              \
 template <typename Key, typename T,                   \
           typename Hash = std::hash<Key>,             \
           typename Eq = std::equal_to<Key> >          \
 struct KIND : public                                  \
 std::KIND<Key,T,Hash,Eq,PAIR_ALLOC>, public gc {      \
   KIND() : std::KIND<Key,T,Hash,Eq,PAIR_ALLOC> () {}  \
   KIND(size_t n)                                      \
     : std::KIND<Key,T,Hash,Eq,PAIR_ALLOC> (n) {}      \
 }

GC_CONTAINER(unordered_map);
GC_CONTAINER(unordered_multimap);

#undef GC_CONTAINER

#undef PAIR_ALLOC

#ifdef USEGC
typedef std::basic_string<char,std::char_traits<char>,
                         gc_allocator<char> > string;
typedef std::basic_stringstream<char,std::char_traits<char>,
                               gc_allocator<char> > stringstream;
typedef std::basic_istringstream<char,std::char_traits<char>,
                                gc_allocator<char> > istringstream;
typedef std::basic_ostringstream<char,std::char_traits<char>,
                                gc_allocator<char> > ostringstream;
typedef std::basic_stringbuf<char,std::char_traits<char>,
                            gc_allocator<char> > stringbuf;

void compact(int x);
#else
inline void compact(int) {}
typedef std::string string;
typedef std::stringstream stringstream;
typedef std::istringstream istringstream;
typedef std::ostringstream ostringstream;
typedef std::stringbuf stringbuf;
#endif // USEGC

std::string stdString(string s);

} // namespace mem

#endif