/*-
 * Copyright 2005 Guram Dukashvili
 * All rights reserved.
 *
 * 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 AUTHOR 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 AUTHOR 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 _shmem_H_
#define _shmem_H_
//---------------------------------------------------------------------------
namespace ksys {

//---------------------------------------------------------------------------
/////////////////////////////////////////////////////////////////////////////
//---------------------------------------------------------------------------
class SharedMemory {
  public:
    ~SharedMemory();
    SharedMemory(const utf8::String & name = utf8::String(), uintptr_t len = 0, const void * address = NULL,
#if HAVE_SYS_MMAN_H
      uintptr_t mode = S_IRWXU
#elif defined(__WIN32__) || defined(__WIN64__)
                    uintptr_t mode = 0   // not used only for compatability
#endif
    );
    static void     unlink(const utf8::String & name);
    void *&         memory();
    void * const &  memory() const;
    const bool &    creator() const;
  protected:
    union {
        void *      memory_;
        char *      c_;
        int8_t *    i8_;
        uint8_t *   u8_;
        int16_t *   i16_;
        uint16_t *  u16_;
        int32_t *   i32_;
        uint32_t *  u32_;
        int64_t *   i64_;
        uint64_t *  u64_;
        intptr_t *  ip_;
        uintptr_t * up_;
    };
    uintptr_t         length_;
#if HAVE_SYS_MMAN_H
    int               file_;
    utf8::AnsiString  name_;
#elif defined(__WIN32__) || defined(__WIN64__)
    HANDLE            file_;
    HANDLE            map_;
#endif
    SharedSemaphore   semaphore_;
  private:
    SharedMemory(const SharedMemory &);
    void operator =(const SharedMemory &);
};
//---------------------------------------------------------------------------
inline void *& SharedMemory::memory()
{
  return memory_;
}
//---------------------------------------------------------------------------
inline void * const & SharedMemory::memory() const
{
  return memory_;
}
//---------------------------------------------------------------------------
inline const bool & SharedMemory::creator() const
{
  return semaphore_.creator();
}
//---------------------------------------------------------------------------
/////////////////////////////////////////////////////////////////////////////
//---------------------------------------------------------------------------
class ClassSMQRDLCK {
};
class ClassSMQRDULK {
};
class ClassSMQWRLCK {
};
class ClassSMQWRULK {
};

extern ClassSMQRDLCK  SMQ_RDL;
extern ClassSMQRDULK  SMQ_RDU;
extern ClassSMQWRLCK  SMQ_WRL;
extern ClassSMQWRULK  SMQ_WRU;
//---------------------------------------------------------------------------
class SharedMemoryQueue : public SharedMemory {
  public:
    ~SharedMemoryQueue();
    SharedMemoryQueue(const utf8::String & name = utf8::String(), uintptr_t len = 0, const void * address = NULL,
#if HAVE_SYS_MMAN_H
      uintptr_t mode = S_IRWXU
#elif defined(__WIN32__) || defined(__WIN64__)
                        uintptr_t mode = 0   // not used only for compatability
#endif
    );

    static void         unlink(const utf8::String & name);
    SharedMemoryQueue & ref();

    SharedMemoryQueue & rdQueueRDLock();
    SharedMemoryQueue & rdQueueRDUnlock();
    SharedMemoryQueue & rdQueueWRLock();
    SharedMemoryQueue & rdQueueWRUnlock();
    SharedMemoryQueue & wrQueueRDLock();
    SharedMemoryQueue & wrQueueRDUnlock();
    SharedMemoryQueue & wrQueueWRLock();
    SharedMemoryQueue & wrQueueWRUnlock();
    SharedMemoryQueue & swap();

    SharedMemoryQueue & read(void * buf, uintptr_t len);
    SharedMemoryQueue & write(const void * buf, uintptr_t len);

    template< typename T> SharedMemoryQueue & operator <<(const T & a)
    {
      return write(&a, sizeof(a));
    }
    template< typename T> SharedMemoryQueue & operator >>(T & a)
    {
      return read(&a, sizeof(a));
    }

    SharedMemoryQueue & operator <<(const ClassSMQRDLCK &);
    SharedMemoryQueue & operator <<(const ClassSMQRDULK &);
    SharedMemoryQueue & operator <<(const ClassSMQWRLCK &);
    SharedMemoryQueue & operator <<(const ClassSMQWRULK &);

    SharedMemoryQueue & operator <<(int8_t a);
    SharedMemoryQueue & operator <<(uint8_t a);
    SharedMemoryQueue & operator <<(int16_t a);
    SharedMemoryQueue & operator <<(uint16_t a);
    SharedMemoryQueue & operator <<(int32_t a);
    SharedMemoryQueue & operator <<(uint32_t a);
    SharedMemoryQueue & operator <<(int64_t a);
    SharedMemoryQueue & operator <<(uint64_t a);
#if !HAVE_INT_AS_INT32_T && !HAVE_INT32_T_AS_INT
    SharedMemoryQueue & operator <<(int a);
    SharedMemoryQueue & operator <<(unsigned int a);
#endif
#if !HAVE_INTPTR_T_AS_INT && !HAVE_INTPTR_T_AS_INT64_T
    SharedMemoryQueue & operator <<(intptr_t a);
    SharedMemoryQueue & operator <<(uintptr_t a);
#endif
#if !HAVE_INTMAX_T_AS_INTPTR_T && !HAVE_INTMAX_T_AS_INT64_T && !HAVE_INT64_T_AS_INTMAX_T
    SharedMemoryQueue & operator <<(intmax_t a);
    SharedMemoryQueue & operator <<(uintmax_t a);
#endif
    SharedMemoryQueue & operator <<(float a);
    SharedMemoryQueue & operator <<(double a);
#if HAVE_LONG_DOUBLE
    SharedMemoryQueue & operator <<(long double a);
#endif
    SharedMemoryQueue & operator <<(const utf8::String & a);

    SharedMemoryQueue & operator >>(int8_t & a);
    SharedMemoryQueue & operator >>(uint8_t & a);
    SharedMemoryQueue & operator >>(int16_t & a);
    SharedMemoryQueue & operator >>(uint16_t & a);
    SharedMemoryQueue & operator >>(int32_t & a);
    SharedMemoryQueue & operator >>(uint32_t & a);
    SharedMemoryQueue & operator >>(int64_t & a);
    SharedMemoryQueue & operator >>(uint64_t & a);
#if !HAVE_INT_AS_INT32_T && !HAVE_INT32_T_AS_INT
    SharedMemoryQueue & operator >>(int & a);
    SharedMemoryQueue & operator >>(unsigned int & a);
#endif
#if !HAVE_INTPTR_T_AS_INT && !HAVE_INTPTR_T_AS_INT64_T
    SharedMemoryQueue & operator >>(intptr_t & a);
    SharedMemoryQueue & operator >>(uintptr_t & a);
#endif
#if !HAVE_INTMAX_T_AS_INTPTR_T && !HAVE_INTMAX_T_AS_INT64_T && !HAVE_INT64_T_AS_INTMAX_T
    SharedMemoryQueue & operator >>(intmax_t & a);
    SharedMemoryQueue & operator >>(uintmax_t & a);
#endif
    SharedMemoryQueue & operator >>(float & a);
    SharedMemoryQueue & operator >>(double & a);
#if HAVE_LONG_DOUBLE
    SharedMemoryQueue & operator >>(long double & a);
#endif
    SharedMemoryQueue & operator >>(utf8::String & a);
  protected:
  private:
    SharedMemoryQueue(const SharedMemory &);
    SharedMemoryQueue(const SharedMemoryQueue &);
    SharedMemoryQueue & operator =(const SharedMemory &);
    SharedMemoryQueue & operator =(const SharedMemoryQueue &);

    SharedSemaphore rdQueueRDLockObject_;
    SharedSemaphore rdQueueWRLockObject_;
    SharedSemaphore wrQueueRDLockObject_;
    SharedSemaphore wrQueueWRLockObject_;
    SharedSemaphore rdQueueMutexObject_;
    SharedSemaphore wrQueueMutexObject_;
    SharedSemaphore rdQueueRDIndicatorObject_;
    SharedSemaphore rdQueueWRIndicatorObject_;
    SharedSemaphore wrQueueRDIndicatorObject_;
    SharedSemaphore wrQueueWRIndicatorObject_;
    bool            rdQueueRDLockedV_;
    bool            rdQueueWRLockedV_;
    bool            wrQueueRDLockedV_;
    bool            wrQueueWRLockedV_;
    struct Queue {
      intptr_t  pid_;
      uintptr_t head_;
      uintptr_t tail_;
      uintptr_t bound_;
#if _MSC_VER
#pragma warning(disable:4200)
#endif
      uint8_t   data_[EMPTY_ARRAY_SIZE];
#if _MSC_VER
#pragma warning(default:4200)
#endif

      SharedMemoryQueue & read(SharedMemoryQueue * queue, void * buf, uintptr_t len);
      SharedMemoryQueue & write(SharedMemoryQueue * queue, const void * buf, uintptr_t len);
    };
    friend struct Queue;
    SharedSemaphore * rdQueueRDLock_;
    SharedSemaphore * rdQueueWRLock_;
    SharedSemaphore * wrQueueRDLock_;
    SharedSemaphore * wrQueueWRLock_;
    SharedSemaphore * wrQueueLock_;
    SharedSemaphore * rdQueueMutex_;
    SharedSemaphore * wrQueueMutex_;
    SharedSemaphore * rdQueueRDIndicator_;
    SharedSemaphore * rdQueueWRIndicator_;
    SharedSemaphore * wrQueueRDIndicator_;
    SharedSemaphore * wrQueueWRIndicator_;
    Queue *           rdQueue_;
    Queue *           wrQueue_;
    bool *            rdQueueRDLocked_;
    bool *            rdQueueWRLocked_;
    bool *            wrQueueRDLocked_;
    bool *            wrQueueWRLocked_;
};
//---------------------------------------------------------------------------
inline SharedMemoryQueue & SharedMemoryQueue::ref()
{
  return *this;
}
//---------------------------------------------------------------------------
inline SharedMemoryQueue & SharedMemoryQueue::rdQueueRDLock()
{
  rdQueueRDLock_->wait();
  *rdQueueRDLocked_ = true;
  return *this;
}
//---------------------------------------------------------------------------
inline SharedMemoryQueue & SharedMemoryQueue::rdQueueRDUnlock()
{
  rdQueueRDLock_->post();
  *rdQueueRDLocked_ = false;
  return *this;
}
//---------------------------------------------------------------------------
inline SharedMemoryQueue & SharedMemoryQueue::rdQueueWRLock()
{
  rdQueueWRLock_->wait();
  *rdQueueWRLocked_ = true;
  return *this;
}
//---------------------------------------------------------------------------
inline SharedMemoryQueue & SharedMemoryQueue::rdQueueWRUnlock()
{
  rdQueueWRLock_->post();
  *rdQueueWRLocked_ = false;
  return *this;
}
//---------------------------------------------------------------------------
inline SharedMemoryQueue & SharedMemoryQueue::wrQueueRDLock()
{
  wrQueueRDLock_->wait();
  *wrQueueRDLocked_ = true;
  return *this;
}
//---------------------------------------------------------------------------
inline SharedMemoryQueue & SharedMemoryQueue::wrQueueRDUnlock()
{
  wrQueueRDLock_->post();
  *wrQueueRDLocked_ = false;
  return *this;
}
//---------------------------------------------------------------------------
inline SharedMemoryQueue & SharedMemoryQueue::wrQueueWRLock()
{
  wrQueueWRLock_->wait();
  *wrQueueWRLocked_ = true;
  return *this;
}
//---------------------------------------------------------------------------
inline SharedMemoryQueue & SharedMemoryQueue::wrQueueWRUnlock()
{
  wrQueueWRLock_->post();
  *wrQueueWRLocked_ = false;
  return *this;
}
//---------------------------------------------------------------------------
inline SharedMemoryQueue & SharedMemoryQueue::read(void * buf, uintptr_t len)
{
  return rdQueue_->read(this, buf, len);
}
//---------------------------------------------------------------------------
inline SharedMemoryQueue & SharedMemoryQueue::write(const void * buf, uintptr_t len)
{
  return wrQueue_->write(this, buf, len);
}
//---------------------------------------------------------------------------
inline SharedMemoryQueue & SharedMemoryQueue::operator <<(const ClassSMQRDLCK &)
{
  return rdQueueRDLock();
}
//---------------------------------------------------------------------------
inline SharedMemoryQueue & SharedMemoryQueue::operator <<(const ClassSMQRDULK &)
{
  return rdQueueRDUnlock();
}
//---------------------------------------------------------------------------
inline SharedMemoryQueue & SharedMemoryQueue::operator <<(const ClassSMQWRLCK &)
{
  return wrQueueWRLock();
}
//---------------------------------------------------------------------------
inline SharedMemoryQueue & SharedMemoryQueue::operator <<(const ClassSMQWRULK &)
{
  return wrQueueWRUnlock();
}
//---------------------------------------------------------------------------
inline SharedMemoryQueue & SharedMemoryQueue::operator <<(int8_t a)
{
  return write(&a, sizeof(a));
}
//---------------------------------------------------------------------------
inline SharedMemoryQueue & SharedMemoryQueue::operator <<(uint8_t a)
{
  return write(&a, sizeof(a));
}
//---------------------------------------------------------------------------
inline SharedMemoryQueue & SharedMemoryQueue::operator <<(int16_t a)
{
  return write(&a, sizeof(a));
}
//---------------------------------------------------------------------------
inline SharedMemoryQueue & SharedMemoryQueue::operator <<(uint16_t a)
{
  return write(&a, sizeof(a));
}
//---------------------------------------------------------------------------
inline SharedMemoryQueue & SharedMemoryQueue::operator <<(int32_t a)
{
  return write(&a, sizeof(a));
}
//---------------------------------------------------------------------------
inline SharedMemoryQueue & SharedMemoryQueue::operator <<(uint32_t a)
{
  return write(&a, sizeof(a));
}
//---------------------------------------------------------------------------
inline SharedMemoryQueue & SharedMemoryQueue::operator <<(int64_t a)
{
  return write(&a, sizeof(a));
}
//---------------------------------------------------------------------------
inline SharedMemoryQueue & SharedMemoryQueue::operator <<(uint64_t a)
{
  return write(&a, sizeof(a));
}
//---------------------------------------------------------------------------
#if !HAVE_INT_AS_INT32_T && !HAVE_INT32_T_AS_INT
//---------------------------------------------------------------------------
inline SharedMemoryQueue & SharedMemoryQueue::operator <<(int a)
{
  return write(&a, sizeof(a));
}
//---------------------------------------------------------------------------
inline SharedMemoryQueue & SharedMemoryQueue::operator <<(unsigned int a)
{
  return write(&a, sizeof(a));
}
//---------------------------------------------------------------------------
#endif
//---------------------------------------------------------------------------
#if !HAVE_INTPTR_T_AS_INT && !HAVE_INTPTR_T_AS_INT64_T
//---------------------------------------------------------------------------
inline SharedMemoryQueue & SharedMemoryQueue::operator <<(intptr_t a)
{
  return write(&a, sizeof(a));
}
//---------------------------------------------------------------------------
inline SharedMemoryQueue & SharedMemoryQueue::operator <<(uintptr_t a)
{
  return write(&a, sizeof(a));
}
//---------------------------------------------------------------------------
#endif
//---------------------------------------------------------------------------
#if !HAVE_INTMAX_T_AS_INTPTR_T && !HAVE_INTMAX_T_AS_INT64_T && !HAVE_INT64_T_AS_INTMAX_T
//---------------------------------------------------------------------------
inline SharedMemoryQueue & SharedMemoryQueue::operator <<(intmax_t a)
{
  return write(&a, sizeof(a));
}
//---------------------------------------------------------------------------
inline SharedMemoryQueue & SharedMemoryQueue::operator <<(uintmax_t a)
{
  return write(&a, sizeof(a));
}
//---------------------------------------------------------------------------
#endif
//---------------------------------------------------------------------------
inline SharedMemoryQueue & SharedMemoryQueue::operator <<(float a)
{
  return write(&a, sizeof(a));
}
//---------------------------------------------------------------------------
inline SharedMemoryQueue & SharedMemoryQueue::operator <<(double a)
{
  return write(&a, sizeof(a));
}
//---------------------------------------------------------------------------
#if HAVE_LONG_DOUBLE
//---------------------------------------------------------------------------
inline SharedMemoryQueue & SharedMemoryQueue::operator <<(long double a)
{
  return write(&a, sizeof(a));
}
//---------------------------------------------------------------------------
#endif
//---------------------------------------------------------------------------
inline SharedMemoryQueue & SharedMemoryQueue::operator <<(const utf8::String & a)
{
  uintptr_t l = a.size();
  return write(&l, sizeof(l)).write(a.c_str(), l);
}
//---------------------------------------------------------------------------
inline SharedMemoryQueue & SharedMemoryQueue::operator >>(int8_t & a)
{
  return read(&a, sizeof(a));
}
//---------------------------------------------------------------------------
inline SharedMemoryQueue & SharedMemoryQueue::operator >>(uint8_t & a)
{
  return read(&a, sizeof(a));
}
//---------------------------------------------------------------------------
inline SharedMemoryQueue & SharedMemoryQueue::operator >>(int16_t & a)
{
  return read(&a, sizeof(a));
}
//---------------------------------------------------------------------------
inline SharedMemoryQueue & SharedMemoryQueue::operator >>(uint16_t & a)
{
  return read(&a, sizeof(a));
}
//---------------------------------------------------------------------------
inline SharedMemoryQueue & SharedMemoryQueue::operator >>(int32_t & a)
{
  return read(&a, sizeof(a));
}
//---------------------------------------------------------------------------
inline SharedMemoryQueue & SharedMemoryQueue::operator >>(uint32_t & a)
{
  return read(&a, sizeof(a));
}
//---------------------------------------------------------------------------
inline SharedMemoryQueue & SharedMemoryQueue::operator >>(int64_t & a)
{
  return read(&a, sizeof(a));
}
//---------------------------------------------------------------------------
inline SharedMemoryQueue & SharedMemoryQueue::operator >>(uint64_t & a)
{
  return read(&a, sizeof(a));
}
//---------------------------------------------------------------------------
#if !HAVE_INT_AS_INT32_T && !HAVE_INT32_T_AS_INT
//---------------------------------------------------------------------------
inline SharedMemoryQueue & SharedMemoryQueue::operator >>(int & a)
{
  return read(&a, sizeof(a));
}
//---------------------------------------------------------------------------
inline SharedMemoryQueue & SharedMemoryQueue::operator >>(unsigned int & a)
{
  return read(&a, sizeof(a));
}
//---------------------------------------------------------------------------
#endif
//---------------------------------------------------------------------------
#if !HAVE_INTPTR_T_AS_INT && !HAVE_INTPTR_T_AS_INT64_T
//---------------------------------------------------------------------------
inline SharedMemoryQueue & SharedMemoryQueue::operator >>(intptr_t & a)
{
  return read(&a, sizeof(a));
}
//---------------------------------------------------------------------------
inline SharedMemoryQueue & SharedMemoryQueue::operator >>(uintptr_t & a)
{
  return read(&a, sizeof(a));
}
//---------------------------------------------------------------------------
#endif
//---------------------------------------------------------------------------
#if !HAVE_INTMAX_T_AS_INTPTR_T && !HAVE_INTMAX_T_AS_INT64_T && !HAVE_INT64_T_AS_INTMAX_T
//---------------------------------------------------------------------------
inline SharedMemoryQueue & SharedMemoryQueue::operator >>(intmax_t & a)
{
  return read(&a, sizeof(a));
}
//---------------------------------------------------------------------------
inline SharedMemoryQueue & SharedMemoryQueue::operator >>(uintmax_t & a)
{
  return read(&a, sizeof(a));
}
//---------------------------------------------------------------------------
#endif
//---------------------------------------------------------------------------
inline SharedMemoryQueue & SharedMemoryQueue::operator >>(float & a)
{
  return read(&a, sizeof(a));
}
//---------------------------------------------------------------------------
inline SharedMemoryQueue & SharedMemoryQueue::operator >>(double & a)
{
  return read(&a, sizeof(a));
}
//---------------------------------------------------------------------------
#if HAVE_LONG_DOUBLE
//---------------------------------------------------------------------------
inline SharedMemoryQueue & SharedMemoryQueue::operator >>(long double & a)
{
  return read(&a, sizeof(a));
}
//---------------------------------------------------------------------------
#endif
//---------------------------------------------------------------------------
} // namespace ksys
//---------------------------------------------------------------------------
#endif /* _shmem_H_ */
//---------------------------------------------------------------------------
