/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef Utils_h
#define Utils_h

#include <stdint.h>
#include <stddef.h>
#include <sys/mman.h>
#include <unistd.h>

/**
 * On architectures that are little endian and that support unaligned reads,
 * we can use direct type, but on others, we want to have a special class
 * to handle conversion and alignment issues.
 */
#if defined(__i386__) || defined(__x86_64__)
typedef uint16_t le_uint16;
typedef uint32_t le_uint32;
#else

/**
 * Template that allows to find an unsigned int type from a (computed) bit size
 */
template <int s> struct UInt { };
template <> struct UInt<16> { typedef uint16_t Type; };
template <> struct UInt<32> { typedef uint32_t Type; };

/**
 * Template to read 2 n-bit sized words as a 2*n-bit sized word, doing
 * conversion from little endian and avoiding alignment issues.
 */
template <typename T>
class le_to_cpu
{
public:
  operator typename UInt<16 * sizeof(T)>::Type() const
  {
    return (b << (sizeof(T) * 8)) | a;
  }
private:
  T a, b;
};

/**
 * Type definitions
 */
typedef le_to_cpu<unsigned char> le_uint16;
typedef le_to_cpu<le_uint16> le_uint32;
#endif

/**
 * AutoClean is a helper to create RAII wrappers
 * The Traits class is expected to look like the following:
 *   struct Traits {
 *     // Define the type of the value stored in the wrapper
 *     typedef value_type type;
 *     // Returns the value corresponding to the uninitialized or freed state
 *     const static type None();
 *     // Cleans up resources corresponding to the wrapped value
 *     const static void clean(type);
 *   }
 */
template <typename Traits>
class AutoClean
{
  typedef typename Traits::type T;
public:
  AutoClean(): value(Traits::None()) { }
  AutoClean(const T& value): value(value) { }
  ~AutoClean()
  {
    if (value != Traits::None())
      Traits::clean(value);
  }

  operator const T&() const { return value; }
  const T& operator->() const { return value; }
  const T& get() const { return value; }

  T forget()
  {
    T _value = value;
    value = Traits::None();
    return _value;
  }

  bool operator ==(T other) const
  {
    return value == other;
  }

  AutoClean& operator =(T other)
  {
    if (value != Traits::None())
      Traits::clean(value);
    value = other;
    return *this;
  }

private:
  T value;
};

/**
 * AUTOCLEAN_TEMPLATE defines a templated class derived from AutoClean
 * This allows to implement templates such as AutoFreePtr.
 */
#define AUTOCLEAN_TEMPLATE(name, Traits) \
template <typename T> \
struct name: public AutoClean<Traits<T> > \
{ \
  using AutoClean<Traits<T> >::operator =; \
  name(): AutoClean<Traits<T> >() { } \
  name(typename Traits<T>::type ptr): AutoClean<Traits<T> >(ptr) { } \
}

/**
 * AutoCloseFD is a RAII wrapper for POSIX file descriptors
 */
struct AutoCloseFDTraits
{
  typedef int type;
  static int None() { return -1; }
  static void clean(int fd) { close(fd); }
};
typedef AutoClean<AutoCloseFDTraits> AutoCloseFD;

/**
 * AutoFreePtr is a RAII wrapper for pointers that need to be free()d.
 *
 *   struct S { ... };
 *   AutoFreePtr<S> foo = malloc(sizeof(S));
 *   AutoFreePtr<char> bar = strdup(str);
 */
template <typename T>
struct AutoFreePtrTraits
{
  typedef T *type;
  static T *None() { return NULL; }
  static void clean(T *ptr) { free(ptr); }
};
AUTOCLEAN_TEMPLATE(AutoFreePtr, AutoFreePtrTraits);

/**
 * AutoDeletePtr is a RAII wrapper for pointers that need to be deleted.
 *
 *   struct S { ... };
 *   AutoDeletePtr<S> foo = new S();
 */
template <typename T>
struct AutoDeletePtrTraits: public AutoFreePtrTraits<T>
{
  static void clean(T *ptr) { delete ptr; }
};
AUTOCLEAN_TEMPLATE(AutoDeletePtr, AutoDeletePtrTraits);

/**
 * AutoDeleteArray is a RAII wrapper for pointers that need to be delete[]ed.
 *
 *   struct S { ... };
 *   AutoDeleteArray<S> foo = new S[42];
 */
template <typename T>
struct AutoDeleteArrayTraits: public AutoFreePtrTraits<T>
{
  static void clean(T *ptr) { delete [] ptr; }
};
AUTOCLEAN_TEMPLATE(AutoDeleteArray, AutoDeleteArrayTraits);

/**
 * MappedPtr is a RAII wrapper for mmap()ed memory. It can be used as
 * a simple void * or unsigned char *.
 *
 * It is defined as a derivative of a template that allows to use a
 * different unmapping strategy.
 */
template <typename T>
class GenericMappedPtr
{
public:
  GenericMappedPtr(void *buf, size_t length): buf(buf), length(length) { }
  GenericMappedPtr(): buf(MAP_FAILED), length(0) { }

  void Assign(void *b, size_t len) {
    if (buf != MAP_FAILED)
      static_cast<T *>(this)->munmap(buf, length);
    buf = b;
    length = len;
  }

  ~GenericMappedPtr()
  {
    if (buf != MAP_FAILED)
      static_cast<T *>(this)->munmap(buf, length);
  }

  operator void *() const
  {
    return buf;
  }

  operator unsigned char *() const
  {
    return reinterpret_cast<unsigned char *>(buf);
  }

  bool operator ==(void *ptr) const {
    return buf == ptr;
  }

  bool operator ==(unsigned char *ptr) const {
    return buf == ptr;
  }

  void *operator +(off_t offset) const
  {
    return reinterpret_cast<char *>(buf) + offset;
  }

  /**
   * Returns whether the given address is within the mapped range
   */
  bool Contains(void *ptr) const
  {
    return (ptr >= buf) && (ptr < reinterpret_cast<char *>(buf) + length);
  }

  /**
   * Returns the length of the mapped range
   */
  size_t GetLength() const
  {
    return length;
  }

private:
  void *buf;
  size_t length;
};

struct MappedPtr: public GenericMappedPtr<MappedPtr>
{
  MappedPtr(void *buf, size_t length)
  : GenericMappedPtr<MappedPtr>(buf, length) { }
  MappedPtr(): GenericMappedPtr<MappedPtr>() { }

  void munmap(void *buf, size_t length)
  {
    ::munmap(buf, length);
  }
};

/**
 * UnsizedArray is a way to access raw arrays of data in memory.
 *
 *   struct S { ... };
 *   UnsizedArray<S> a(buf);
 *   UnsizedArray<S> b; b.Init(buf);
 *
 * This is roughly equivalent to
 *   const S *a = reinterpret_cast<const S *>(buf);
 *   const S *b = NULL; b = reinterpret_cast<const S *>(buf);
 *
 * An UnsizedArray has no known length, and it's up to the caller to make
 * sure the accessed memory is mapped and makes sense.
 */
template <typename T>
class UnsizedArray
{
public:
  typedef size_t idx_t;

  /**
   * Constructors and Initializers
   */
  UnsizedArray(): contents(NULL) { }
  UnsizedArray(const void *buf): contents(reinterpret_cast<const T *>(buf)) { }

  void Init(const void *buf)
  {
    // ASSERT(operator bool())
    contents = reinterpret_cast<const T *>(buf);
  }

  /**
   * Returns the nth element of the array
   */
  const T &operator[](const idx_t index) const
  {
    // ASSERT(operator bool())
    return contents[index];
  }

  /**
   * Returns whether the array points somewhere
   */
  operator bool() const
  {
    return contents != NULL;
  }
private:
  const T *contents;
};

/**
 * Array, like UnsizedArray, is a way to access raw arrays of data in memory.
 * Unlike UnsizedArray, it has a known length, and is enumerable with an
 * iterator.
 *
 *   struct S { ... };
 *   Array<S> a(buf, len);
 *   UnsizedArray<S> b; b.Init(buf, len);
 *
 * In the above examples, len is the number of elements in the array. It is
 * also possible to initialize an Array with the buffer size:
 *
 *   Array<S> c; c.InitSize(buf, size);
 *
 * It is also possible to initialize an Array in two steps, only providing
 * one data at a time:
 *
 *   Array<S> d;
 *   d.Init(buf);
 *   d.Init(len); // or d.InitSize(size);
 *
 */
template <typename T>
class Array: public UnsizedArray<T>
{
public:
  typedef typename UnsizedArray<T>::idx_t idx_t;

  /**
   * Constructors and Initializers
   */
  Array(): UnsizedArray<T>(), length(0) { }
  Array(const void *buf, const idx_t length)
  : UnsizedArray<T>(buf), length(length) { }

  void Init(const void *buf)
  {
    UnsizedArray<T>::Init(buf);
  }

  void Init(const idx_t len)
  {
    // ASSERT(length != 0)
    length = len;
  }

  void InitSize(const idx_t size)
  {
    Init(size / sizeof(T));
  }

  void Init(const void *buf, const idx_t len)
  {
    UnsizedArray<T>::Init(buf);
    Init(len);
  }

  void InitSize(const void *buf, const idx_t size)
  {
    UnsizedArray<T>::Init(buf);
    InitSize(size);
  }

  /**
   * Returns the nth element of the array
   */
  const T &operator[](const idx_t index) const
  {
    // ASSERT(index < length)
    // ASSERT(operator bool())
    return UnsizedArray<T>::operator[](index);
  }

  /**
   * Returns the number of elements in the array
   */
  idx_t numElements() const
  {
    return length;
  }

  /**
   * Returns whether the array points somewhere and has at least one element.
   */
  operator bool() const
  {
    return (length > 0) && UnsizedArray<T>::operator bool();
  }

  /**
   * Iterator for an Array. Use is similar to that of STL const_iterators:
   *
   *   struct S { ... };
   *   Array<S> a(buf, len);
   *   for (Array<S>::iterator it = a.begin(); it < a.end(); ++it) {
   *     // Do something with *it.
   *   }
   */
  class iterator
  {
  public:
    iterator(): item(NULL) { }

    const T &operator *() const
    {
      return *item;
    }

    const T *operator ->() const
    {
      return item;
    }

    const T &operator ++()
    {
      return *(++item);
    }

    bool operator<(const iterator &other) const
    {
      return item < other.item;
    }
  protected:
    friend class Array<T>;
    iterator(const T &item): item(&item) { }

  private:
    const T *item;
  };

  /**
   * Returns an iterator pointing at the beginning of the Array
   */
  iterator begin() const {
    if (length)
      return iterator(UnsizedArray<T>::operator[](0));
    return iterator();
  }

  /**
   * Returns an iterator pointing past the end of the Array
   */
  iterator end() const {
    if (length)
      return iterator(UnsizedArray<T>::operator[](length));
    return iterator();
  }
private:
  idx_t length;
};

/**
 * Transforms a pointer-to-function to a pointer-to-object pointing at the
 * same address.
 */
template <typename T>
void *FunctionPtr(T func)
{
  union {
    void *ptr;
    T func;
  } f;
  f.func = func;
  return f.ptr;
}

#endif /* Utils_h */
 
