
#ifndef PLS_ALIGNMENT_H
#define PLS_ALIGNMENT_H

#include <cstdint>
#include <cstdlib>
#include <array>

#include "system_details.h"

namespace pls {
namespace internal {
namespace base {
namespace alignment {

system_details::pointer_t next_alignment(system_details::pointer_t size);
char *next_alignment(char *pointer);
system_details::pointer_t previous_alignment(system_details::pointer_t size);

/**
 * Forces alignment requirements on a type equal to a cache line size.
 * This can be useful to store the elements aligned in an array or to allocate them using new.
 *
 * The object is constructed using perfect forwarding. Thus initialization looks identical to the
 * wrapped object (can be used in containers), access to the elements has to be done through
 * the pointer returned by pointer() or moved out using object().
 *
 * (This is required, as C++11 does not allow 'over_aligned' types, meaning alignments higher
 * than the max_align_t (most times 16 bytes) wont be respected properly.)
 */
template<typename T>
struct alignas(system_details::CACHE_LINE_SIZE) cache_alignment_wrapper {
 private:
  std::array<char, sizeof(T) + system_details::CACHE_LINE_SIZE> memory_;
  char *data_;
 public:
  template<typename ...ARGS>
  explicit cache_alignment_wrapper(ARGS &&...args): memory_{}, data_{next_alignment(memory_.data())} {
    new(data_) T(std::forward<ARGS>(args)...);
  }

  T &object() { return *reinterpret_cast<T *>(data_); }
  T *pointer() { return reinterpret_cast<T *>(data_); }
};

void *allocate_aligned(size_t size);

#include "alignment_impl.h"
}
}
}
}

#endif //PLS_ALIGNMENT_H
