// Copyright (C) 2006-2014 David Sugar, Tycho Softworks.
// Copyright (C) 2015-2020 Cherokees of Idaho.
//
// This file is part of GNU uCommon C++.
//
// GNU uCommon C++ is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// GNU uCommon C++ 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 Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with GNU uCommon C++.  If not, see <http://www.gnu.org/licenses/>.

/**
 * Support for memory mapped objects.
 * Memory mapped objects can be used to publish information so that it may be
 * accessible directly by external programs.  The mapped memory objects will
 * usually be built as a vector vector or reusable type factory, in the latter
 * case using the allocated shared memory block itself as a local heap.  A
 * simple template can be used to view the mapped contents that have been
 * published by another process.
 * @file ucommon/mapped.h
 */

#ifndef _UCOMMON_MAPPED_H_
#define _UCOMMON_MAPPED_H_

#ifndef _UCOMMON_LINKED_H_
#include <ucommon/linked.h>
#endif

#ifndef _UCOMMON_THREAD_H_
#include <ucommon/thread.h>
#endif

#ifndef _UCOMMON_STRING_H_
#include <ucommon/string.h>
#endif

#ifndef _MSWINDOWS_
#include <signal.h>
#endif

namespace ucommon {

/**
 * Construct or access a named section of memory.  A logical name is used
 * which might map to something that is invoked from a call like shm_open
 * or a named w32 mapped swap segment.  This is meant to support mapping a
 * vector onto shared memory and is often used as a supporting class for our
 * shared memory access templates.
 * @author David Sugar <dyfet@gnutelephony.org>
 */
class __EXPORT MappedMemory
{
private:
    size_t mapsize;
    caddr_t map;
    fd_t fd;

    __DELETE_COPY(MappedMemory);

protected:
    size_t size, used;
    char idname[65];
    bool erase;

    MappedMemory();

    /**
     * Supporting function to construct a new or access an existing
     * shared memory segment.  Used by primary constructors.
     * @param name of segment to create or access.
     * @param size of segment if creating new.  Use 0 for read-only access.
     */
    void create(const char *name, size_t size = (size_t)0);

public:
    /**
     * Construct a read/write access mapped shared segment of memory of a
     * known size.  This constructs a new memory segment.
     * @param name of segment.
     * @param size of segment.
     */
    MappedMemory(const char *name, size_t size);

    /**
     * Provide read-only mapped access to an existing named shared memory
     * segment.  The size of the map is found by the size of the already
     * existing segment.
     * @param name of existing segment.
     */
    MappedMemory(const char *name);

    /**
     * Unmap memory segment.
     */
    virtual ~MappedMemory();

    /**
     * Unmap memory segment.
     */
    void release(void);

    /**
     * Destroy a previously existing memory segment under the specified name.
     * This is used both before creating a new one, and after a publishing
     * process unmaps the segment it created.
     * @param name of segment to remove.
     */
    static  void remove(const char *name);

    /**
     * Test if map active.
     * @return true if active map.
     */
    inline operator bool() const
        {return (size != 0);}

    /**
     * Test if map is inactive.
     * @return true if map inactive.
     */
    inline bool operator!() const
        {return (size == 0);}

    /**
     * Extend size of managed heap on shared memory segment.  This does not
     * change the size of the mapped segment in any way, only that of any
     * heap space that is being allocated and used from the mapped segment.
     * @return start of space from map.
     * @param size of space requested.  Will fault if past end of segment.
     */
    void *sbrk(size_t size);

    /**
     * Get memory from a specific offset within the mapped memory segment.
     * @param offset from start of segment.  Will fault if past end.
     * @return address of offset.
     */
    void *offset(size_t offset) const;

    /**
     * Copy memory from specific offset within the mapped memory segment.
     * This function assures the copy is not in the middle of being modified.
     * @param offset from start of segment.
     * @param buffer to copy into.
     * @param size of object to copy.
     * @return true on success.
     */
    bool copy(size_t offset, void *buffer, size_t size) const;

    /**
     * Get size of mapped segment.
     * @return size of mapped segment.
     */
    inline size_t len(void) const
        {return size;}

    /**
     * Get starting address of mapped segment.
     * @return starting address of mapped segment.
     */
    inline caddr_t addr(void)
        {return map;}

    /**
     * An API that allows "disabling" of publishing shared memory maps.
     * This may be useful when an app doesn't want to use shared memory
     * as a runtime or build option, but does not want to have to be "recoded"
     * explicitly for non-shared memory either.  Basically it substitutes a
     * dummy map running on the local heap.
     */
    static void disable(void);
};

/**
 * Map a reusable allocator over a named shared memory segment.  This may be
 * used to form a resource bound fixed size managed heap in shared memory.
 * The request can either be fulfilled from the object reuse pool or from a
 * new section of memory, and if all memory in the segment has been exhausted,
 * it can wait until more objects are returned by another thread to the reuse
 * pool.
 * @author David Sugar <dyfet@gnutelephony.org>
 */
class __EXPORT MappedReuse : protected ReusableAllocator, protected MappedMemory
{
private:
    unsigned objsize;
    unsigned reading;
    mutex_t mutex;

    __DELETE_DEFAULTS(MappedReuse);

protected:
    MappedReuse(size_t osize);

    inline void create(const char *fname, unsigned count)
        {MappedMemory::create(fname, count * objsize);}

public:
    /**
     * Construct a named memory segment for use with managed fixed size
     * reusable objects.  The segment is created as writable.  There is no
     * read-only version of mapped reuse since the mapped segment can be read
     * by another process directly as a mapped read-only vector.  The actual
     * mapped type will be derived from ReusableObject to meet the needs of
     * the reusable allocator.  The template version should be used to
     * assure type correctness rather than using this class directly.
     * @param name of shared memory segment.
     * @param size of the object type being mapped.
     * @param count of the maximum number of active mapped objects.
     */
    MappedReuse(const char *name, size_t size, unsigned count);

    /**
     * Check whether there are objects available to be allocated.
     * @return true if objects are available.
     */
    bool avail(void) const;

    /**
     * Request a reusable object from the free list or mapped space.
     * @return free object or NULL if pool is exhausted.
     */
    ReusableObject *request(void);

    /**
     * Request a reusable object from the free list or mapped space.
     * This method blocks until an object becomes available.
     * @return free object.
     */
    ReusableObject *get(void);

    /**
     * Request a reusable object from the free list or mapped space.
     * This method blocks until an object becomes available or the
     * timeout has expired.
     * @param timeout to wait in milliseconds.
     * @return free object or NULL if timeout.
     */
    ReusableObject *getTimed(timeout_t timeout);

    /**
     * Used to get an object from the reuse pool when the mutex lock is
     * already held.
     * @return object from pool or NULL if exhausted.
     */
    ReusableObject *getLocked(void);

    /**
     * Used to return an object to the reuse pool when the mutex lock is
     * already held.
     * @param object being returned.
     */
    void removeLocked(ReusableObject *object);
};

/**
 * Template class to map typed vector into shared memory.  This is used to
 * construct a typed read/write vector of objects that are held in a named
 * shared memory segment.
 * @author David Sugar <dyfet@gnutelephony.org>
 */
template <class T>
class mapped_array : public MappedMemory
{
private:
    __DELETE_COPY(mapped_array);

protected:
    inline mapped_array() : MappedMemory() {}

    inline void create(const char *fn, unsigned members)
        {MappedMemory::create(fn, members * sizeof(T));}

public:
    /**
     * Construct mapped vector array of typed objects.  This is constructed
     * for read/write access.  mapped_view is used in all cases for read-only
     * access to mapped data.  Member objects are linearly allocated from
     * the shared memory segment, or may simply be directly accessed by offset.
     * @param name of mapped segment to construct.
     * @param number of objects in the mapped vector.
     */
    inline mapped_array(const char *name, unsigned number) :
        MappedMemory(name, number * sizeof(T)) {}

    /**
     * Initialize typed data in mapped array.  Assumes default constructor
     * for type.
     */
    inline void initialize(void)
        {new((caddr_t)offset(0)) T[size / sizeof(T)];}

    /**
     * Add mapped space while holding lock for one object.
     * @return address of object.
     */
    inline void *addLock(void)
        {return sbrk(sizeof(T));}

    /**
     * Get typed pointer to member object of vector in mapped segment.
     * @param member to access.
     * @return typed pointer or NULL if past end of array.
     */
    inline T *operator()(unsigned member)
        {return static_cast<T*>(offset(member * sizeof(T)));}

    /**
     * Allocate mapped space for one object.
     * @return address of object.
     */
    inline T *operator()(void)
        {return static_cast<T*>(sbrk(sizeof(T)));}

    /**
     * Reference typed object of vector in mapped segment.
     * @param member to access.
     * @return typed reference.
     */
    inline T& operator[](unsigned member)
        {return *(operator()(member));}

    /**
     * Get member size of typed objects that can be held in mapped vector.
     * @return members mapped in segment.
     */
    inline unsigned max(void) const
        {return (unsigned)(size / sizeof(T));}
};

/**
 * Template class to map typed reusable objects into shared memory heap.
 * This is used to construct a read/write heap of objects that are held in a
 * named shared memory segment.  Member objects are allocated from a reusable
 * heap but are stored in the shared memory segment as a vector.
 * @author David Sugar <dyfet@gnutelephony.org>
 */
template <class T>
class mapped_reuse : public MappedReuse
{
private:
    __DELETE_COPY(mapped_reuse);

protected:
    inline mapped_reuse() :
        MappedReuse(sizeof(T)) {}

public:
    /**
     * Construct mapped reuse array of typed objects.  This is constructed
     * for read/write access.  mapped_view is used in all cases for read-only
     * access to mapped data.
     * @param name of mapped segment to construct.
     * @param number of objects in the mapped vector.
     */
    inline mapped_reuse(const char *name, unsigned number) :
        MappedReuse(name, sizeof(T), number) {}

    /**
     * Initialize typed data in mapped array.  Assumes default constructor
     * for type.
     */
    inline void initialize(void)
        {new((caddr_t)pos(0)) T[size / sizeof(T)];}

    /**
     * Check whether there are typed objects available to be allocated.
     * @return true if objects are available.
     */
    inline operator bool() const
        {return MappedReuse::avail();}

    /**
     * Check whether there are typed objects available to be allocated.
     * @return true if no more typed objects are available.
     */
    inline bool operator!() const
        {return !MappedReuse::avail();}

    /**
     * Request a typed reusable object from the free list or mapped space.
     * This method blocks until an object becomes available.
     * @return free object.
     */
    inline operator T*()
        {return mapped_reuse::get();}

    /**
     * Request a typed reusable object from the free list or mapped space by
     * pointer reference.  This method blocks until an object becomes available.
     * @return free object.
     */
    inline T* operator*()
        {return mapped_reuse::get();}

    /**
     * Get typed object from a specific member offset within the mapped segment.
     * @param member offset from start of segment.  Will fault if past end.
     * @return typed object pointer.
     */
    inline T *pos(size_t member)
        {return static_cast<T*>(MappedReuse::offset(member * sizeof(T)));}

    /**
     * Request a typed reusable object from the free list or mapped space.
     * This method blocks until an object becomes available.
     * @return free typed object.
     */
    inline T *get(void)
        {return static_cast<T*>(MappedReuse::get());}

    /**
     * Request a typed reusable object from the free list or mapped space.
     * This method blocks until an object becomes available from another
     * thread or the timeout expires.
     * @param timeout in milliseconds.
     * @return free typed object.
     */
    inline T *getTimed(timeout_t timeout)
        {return static_cast<T*>(MappedReuse::getTimed(timeout));}

    /**
     * Request a typed reusable object from the free list or mapped space.
     * This method does not block or wait.
     * @return free typed object if available or NULL.
     */
    inline T *request(void)
        {return static_cast<T*>(MappedReuse::request());}

    /**
     * Used to return a typed object to the reuse pool when the mutex lock is
     * already held.
     * @param object being returned.
     */
    inline void removeLocked(T *object)
        {MappedReuse::removeLocked(object);}

    /**
     * Used to get a typed object from the reuse pool when the mutex lock is
     * already held.
     * @return typed object from pool or NULL if exhausted.
     */
    inline T *getLocked(void)
        {return static_cast<T*>(MappedReuse::getLocked());}

    /**
     * Used to release a typed object back to the reuse typed object pool.
     * @param object being released.
     */
    inline void release(T *object)
        {ReusableAllocator::release(object);}
};

/**
 * Class to access a named mapped segment published from another process.
 * This offers a simple typed vector interface to access the shared memory
 * segment in read-only mode.
 * @author David Sugar <dyfet@gnutelephony.org>
 */
template <class T>
class mapped_view : protected MappedMemory
{
private:
    __DELETE_DEFAULTS(mapped_view);

public:
    /**
     * Map existing named memory segment.  The size of the map is derived
     * from the existing map alone.
     * @param name of memory segment to map.
     */
    inline mapped_view(const char *name) :
        MappedMemory(name) {}

    /**
     * Access typed member object in the mapped segment.
     * @param member to access.
     * @return typed object pointer.
     */
    inline volatile const T *operator()(unsigned member)
        {return static_cast<const T*>(offset(member * sizeof(T)));}

    /**
     * Reference typed member object in the mapped segment.
     * @param member to access.
     * @return typed object reference.
     */
    inline volatile const T &operator[](unsigned member)
        {return *(operator()(member));}

    inline volatile const T *get(unsigned member)
        {return static_cast<const T*>(offset(member * sizeof(T)));}

    inline void copy(unsigned member, T& buffer)
        {MappedMemory::copy(member * sizeof(T), &buffer, sizeof(T));}

    /**
     * Get count of typed member objects held in this map.
     * @return count of typed member objects.
     */
    inline unsigned count(void) const
        {return (unsigned)(size / sizeof(T));}
};

} // namespace ucommon

#endif
