//==================================================================================
// BSD 2-Clause License
//
// Copyright (c) 2014-2022, NJIT, Duality Technologies Inc. and other contributors
//
// All rights reserved.
//
// Author TPOC: contact@openfhe.org
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
//    list of conditions and the following disclaimer.
//
// 2. 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.
//
// 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 THE COPYRIGHT HOLDER OR 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 _STL_ALLOCATOR_H
#define _STL_ALLOCATOR_H

// See
// http://www.codeproject.com/Articles/1089905/A-Custom-STL-std-allocator-Replacement-Improves-Performance-

#include "xallocator.h"

template <typename T>
class stl_allocator;
template <>
class stl_allocator<void> {
public:
    typedef void* pointer;
    typedef const void* const_pointer;
    // reference to void members are impossible.
    typedef void value_type;

    template <class U>
    struct rebind {
        typedef stl_allocator<U> other;
    };
};

/// @brief stl_allocator is STL-compatible allocator used to provide fixed
/// block allocations.
/// @details The default allocator for the STL is the global heap. The
/// stl_allocator is custom allocator where xmalloc/xfree is used to obtain
/// and release memory.
template <typename T>
class stl_allocator {
public:
    typedef size_t size_type;
    typedef ptrdiff_t difference_type;
    typedef T* pointer;
    typedef const T* const_pointer;
    typedef T& reference;
    typedef const T& const_reference;
    typedef T value_type;

    /// Constructor
    stl_allocator() {}

    /// Destructor
    ~stl_allocator() {}

    /// Copy constructor
    template <class U>
    stl_allocator(const stl_allocator<U>&) {}

    template <class U>
    struct rebind {
        typedef stl_allocator<U> other;
    };

    /// Return reference address.
    /// @return  Pointer to T memory.
    pointer address(reference x) const {
        return &x;
    }

    /// Return reference address.
    /// @return  Const pointer to T memory.
    const_pointer address(const_reference x) const {
        return &x;
    }

    /// Get the maximum size of memory.
    /// @return  Max memory size in bytes.
    size_type max_size() const throw() {
        return size_t(-1) / sizeof(value_type);
    }

    /// Allocates a fixed block of memory
    /// @param[in] n - size of memory to allocate in bytes
    /// @param[in] hint
    /// @return  Pointer to the allocated memory.
    pointer allocate(size_type n, stl_allocator<void>::const_pointer hint = nullptr) {
        return static_cast<pointer>(xmalloc(n * sizeof(T)));
    }

    /// Deallocate a previously allocated fixed memory block.
    /// @param[in] p - pointer to the memory block
    /// @param[in] n - size of memory in bytes
    void deallocate(pointer p, size_type n) {
        xfree(p);
    }

    /// Constructs a new instance.
    /// @param[in] p - pointer to the memory where the instance is constructed
    ///    using placement new.
    /// @param[in] val - instance of object to copy construct.
    void construct(pointer p, const T& val) {
        new (static_cast<void*>(p)) T(val);
    }

    /// Create a new object instance using placement new.
    /// @param[in] p - pointer to the memory where the instance is constructed
    ///    using placement new.
    void construct(pointer p) {
        new (static_cast<void*>(p)) T();
    }

    /// Destroys an instance. Objects created with placement new must
    ///  explicitly call the destructor.
    /// @param[in] p - pointer to object instance.
    void destroy(pointer p) {
        p->~T();
    }
};

template <typename T, typename U>
inline bool operator==(const stl_allocator<T>&, const stl_allocator<U>) {
    return true;
}

template <typename T, typename U>
inline bool operator!=(const stl_allocator<T>&, const stl_allocator<U>) {
    return false;
}

// For VC6/STLPort 4-5-3 see /stl/_alloc.h, line 464
// "If custom allocators are being used without member template classes support
// : user (on purpose) is forced to define rebind/get operations !!!"
#ifdef _WIN32
    #define STD_ALLOC_CDECL __cdecl
#else
    #define STD_ALLOC_CDECL
#endif

namespace std {
template <class _Tp1, class _Tp2>
inline stl_allocator<_Tp2>& STD_ALLOC_CDECL __stl_alloc_rebind(stl_allocator<_Tp1>& __a, const _Tp2*) {
    return (stl_allocator<_Tp2>&)(__a);
}

template <class _Tp1, class _Tp2>
inline stl_allocator<_Tp2> STD_ALLOC_CDECL __stl_alloc_create(const stl_allocator<_Tp1>&, const _Tp2*) {
    return stl_allocator<_Tp2>();
}
}  // namespace std

#endif
