
#ifndef PLS_ALIGNED_STACK_H
#define PLS_ALIGNED_STACK_H

#include <cstdint>
#include <cstdlib>

#include "pls/internal/base/error_handling.h"
#include "pls/internal/base/alignment.h"

namespace pls {
namespace internal {
namespace data_structures {

using base::system_details::pointer_t;

/**
 * Generic stack-like data structure that allows to allocate arbitrary objects in a given memory region.
 * The objects will be stored aligned in the stack, making the storage cache friendly and very fast
 * (as long as one can live with the stack restrictions).
 *
 * IMPORTANT: Does not call destructors on stored objects! Do not allocate resources in the objects!
 *
 * Usage:
 * aligned_stack stack{pointer_to_memory, size_of_memory};
 * T* pointer = stack.push<T>(constructor_arguments); // Perfect-Forward-Construct the object on top of stack
 * stack.pop<T>(); // Remove the top object of type T
 */
class aligned_stack {
  // Keep bounds of our memory block
  pointer_t memory_start_;
  pointer_t memory_end_;

  // Current head will always be aligned to cache lines
  pointer_t head_;
 public:
  typedef pointer_t state;

  aligned_stack() : memory_start_{0}, memory_end_{0}, head_{0} {};
  aligned_stack(pointer_t memory_region, std::size_t size);
  aligned_stack(char *memory_region, std::size_t size);

  template<typename T, typename ...ARGS>
  T *push(ARGS &&... args);
  template<typename T>
  void *push_bytes();
  void *push_bytes(size_t size);
  template<typename T>
  T pop();

  state save_state() const { return head_; }
  void reset_state(state new_state) { head_ = new_state; }
};

}
}
}
#include "aligned_stack_impl.h"

#endif //PLS_ALIGNED_STACK_H
