/*
 *      Author: alexrayne <alexraynepe196@gmail.com>
  ------------------------------------------------------------------------
    Copyright (c) alexrayne

   All rights reserved.
   Redistribution and use in source and binary forms, with or without
   modification, are permitted provided that the following conditions are met:
   - Redistributions of source code must retain the above copyright
     notice, this list of conditions and the following disclaimer.
   - Redistributions in binary form must reproduce the above copyright
     notice, this list of conditions and the following disclaimer in the
     documentation and/or other materials provided with the distribution.
   - Neither the name of ARM nor the names of its contributors may be used
     to endorse or promote products derived from this software without
     specific prior written permission.
   *
   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
   AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE
   LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
   CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   POSSIBILITY OF SUCH DAMAGE. *
*/
/* 
 * Определение глобального аллокатора в системном хипе, используемом для С++ объектов.
 * Этот аллокатор - прослойка к системному менегеру памяти
*/
#ifndef V12_ALLOCATOR_H
#define V12_ALLOCATOR_H

// provide: HEAP_STYLE
#include <project-conf.h>

// еслине задан стиль своего хипа HEAP_STYLE, использую аллокатор в стандартном хипе
#ifndef HEAP_STYLE
#include <memory>

template<class _Ty>
class SystemHeap : public std::allocator<_Ty>{

};

#else //HEAP_STYLE

#include "stddef.h"
#include <c_compat.h>
#include <assert.h>
#include <new>



#ifndef PRE_ALLOC_MEMORY_SIZE
#define PRE_ALLOC_MEMORY_SIZE 15000
#endif


#ifdef __cplusplus
extern "C" {
#endif

void *allocMe(size_t s) __cpp_decls;
void deallocMe(void *p, size_t s) __cpp_decls;
size_t availMe(void) __cpp_decls;
void initMe(void) __cpp_decls;

#ifdef __cplusplus
}
#endif

#ifdef __cplusplus


#ifndef __unix__ //
#else
    #include <assert.h>
    #include <iostream>
#endif

#include <cstring>
#include <limits>
#include <cstddef>
#include <string.h>



template <class T>
class SystemHeap
//    : public std::allocator<T>
{
 public:
   // type definitions
   typedef T        value_type;
   typedef T*       pointer;
   typedef const T* const_pointer;
   typedef T&       reference;
   typedef const T& const_reference;
   typedef std::size_t    size_type;
   typedef std::ptrdiff_t difference_type;

   // rebind allocator to type U
   template <class U>
   struct rebind {
       typedef SystemHeap<U> other;
   };

   // return address of values
   pointer address (reference value) const {
       return &value;
   }
   const_pointer address (const_reference value) const {
       return &value;
   }

   /* constructors and destructor
    * - nothing to do because the allocator has no state
    */
   SystemHeap() __noexcept{
       initMe();
   }
   SystemHeap(const SystemHeap&) __noexcept {
   }
   template <class U>
     SystemHeap (const SystemHeap<U>&) __noexcept {
   }
   ~SystemHeap() throw() {
   }

   // return maximum number of elements that can be allocated
   size_type max_size () const __noexcept {
       return availMe() / sizeof(T);
   }

   // allocate but don't initialize num elements of type T
   pointer allocate (size_type num, const void* = 0) __noexcept
   {
       // print message and allocate memory with global new
    #ifdef __unix__
       std::cerr << "allocate " << num << " element(s)"
                 << " of size " << sizeof(T) << std::endl;
    #endif
    #ifdef HWDEBUG
        FPrintUsrLog(putcharx_hw, "\t\tAllocate %d element(s) of size %d\r\n", num, sizeof(T));
    #endif

       size_t n = num*sizeof(T);
       pointer ret = (pointer) allocMe(n);
       assert(ret != NULL);


    #ifdef __unix__
       std::cerr << " allocated at: " << (void*)ret << std::endl;
    #endif

    #ifdef HWDEBUG
        FPrintUsrLog(putcharx_hw, " allocated at: 0x%X\r\n", (void*)ret);
    #endif
       return ret;
   }

#if 1  // use standart stl placement new

#if __cplusplus >= 201103L
      template<typename _Up, typename... _Args>
        void
        construct(_Up* __p, _Args&&... __args)
    { ::new((void *)__p) _Up(std::forward<_Args>(__args)...); }

      template<typename _Up>
        void
        destroy(_Up* __p) { __p->~_Up(); }
#else
      // _GLIBCXX_RESOLVE_LIB_DEFECTS
      // 402. wrong new expression in [some_] allocator::construct
      void
      construct(pointer __p, const T& __val)
      { ::new((void *)__p) T(__val); }

      void
      destroy(pointer __p) { __p->~T(); }
#endif

#else // use standart stl placement new
   // initialize elements of allocated storage p with value value
   void construct (pointer p, const T& value) __noexcept {
        // initialize memory with placement new
        #ifdef __unix__
        std::cerr << "-------initialize memory with placement new" << std::endl;
        std::cerr << "\t\tpointer: " << (void *)p << std::endl; //(T) value << std::endl;
        #endif
        #ifdef HWDEBUG
            FPrintUsrLog(putcharx_hw, "-------initialize memory with placement new\r\n");
            FPrintUsrLog(putcharx_hw, "\t\tpointer: 0x%X\r\n", (void *)p);
        #endif
        memcpy(p, &value, sizeof(T));
   }

   // destroy elements of initialized storage p
   void destroy (pointer p) __noexcept {
       // destroy objects by calling their destructor
        #ifdef __unix__
        std::cerr << "-------destroy objects by calling their destructor" << std::endl;
        std::cerr << "\t\tpointer: " << (void *)p << std::endl;
        #endif
        #ifdef HWDEBUG
            FPrintUsrLog(putcharx_hw, "-------destroy objects by calling their destructor\r\n");
            FPrintUsrLog(putcharx_hw, "\t\tpointer: 0x%X\r\n", (void *)p);
        #endif
        p->~T();
   }
#endif // use standart stl placement new

   // deallocate storage p of deleted elements
   void deallocate (pointer p, size_type num) __noexcept {
       // print message and deallocate memory with global delete
    #ifdef __unix__
       std::cerr << "deallocate " << num << " element(s)"
                 << " of size " << sizeof(T)
                 << " at: " << (void*)p << std::endl;
    #endif
    #ifdef HWDEBUG
        FPrintUsrLog(putcharx_hw, "\t\tDeallocate %d element(s) of size %d at 0x%X\r\n", num, sizeof(T), (void*)p);
    #endif

       //::operator delete((void*)p);
       deallocMe((void*)p, num*sizeof(T));
   }
};

// return that all specializations of this allocator are interchangeable
template <class T1, class T2>
bool operator== (const SystemHeap<T1>&,
                const SystemHeap<T2>&) {
   return true;
}
template <class T1, class T2>
bool operator!= (const SystemHeap<T1>&,
                const SystemHeap<T2>&) {
   return false;
}

#endif //HEAP_STYLE


#endif //#ifdef __cplusplus

#endif // V12_ALLOCATOR_H
