#include "pls/internal/data_structures/aligned_stack.h"
#include "pls/internal/base/system_details.h"

namespace pls {
namespace internal {
namespace data_structures {

aligned_stack::aligned_stack(pointer_t memory_region, const std::size_t size) :
    aligned_memory_start_{base::alignment::next_alignment(memory_region)},
    aligned_memory_end_{base::alignment::previous_alignment(memory_region + size)},
    max_offset_{(aligned_memory_end_ - aligned_memory_start_) / base::system_details::CACHE_LINE_SIZE},
    current_offset_{0} {}

aligned_stack::aligned_stack(char *memory_region, const std::size_t size) :
    aligned_stack((pointer_t) memory_region, size) {}

void *aligned_stack::memory_at_offset(stack_offset offset) const {
  const auto byte_offset = offset * base::system_details::CACHE_LINE_SIZE;
  return reinterpret_cast<void *>(aligned_memory_start_ + byte_offset);
}

void *aligned_stack::push_bytes(size_t size) {
  size_t round_up_size = base::alignment::next_alignment(size);
  size_t num_cache_lines = round_up_size / base::system_details::CACHE_LINE_SIZE;

  void *result = memory_at_offset(current_offset_);

  // Move head to next aligned position after new object
  current_offset_ += num_cache_lines;
  if (current_offset_ > max_offset_) {
    PLS_ERROR("Tried to allocate object on alligned_stack without sufficient memory!");
  }

  return result;
}

}
}
}
