#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(char *memory_pointer, size_t size) :
    memory_pointer_{memory_pointer}, // MUST be aligned
    max_offset_{size / base::system_details::CACHE_LINE_SIZE},
    current_offset_{0} {
  PLS_ASSERT((pointer_t) memory_pointer_ % base::system_details::CACHE_LINE_SIZE != 0,
             "Must initialize an aligned_stack with a properly aligned memory region!")
}

aligned_stack::aligned_stack(char *unaligned_memory_pointer, size_t size, size_t unaligned_size) :
    unaligned_memory_pointer_{unaligned_memory_pointer},
    memory_pointer_{base::alignment::next_alignment(unaligned_memory_pointer)},
    max_offset_{unaligned_size / base::system_details::CACHE_LINE_SIZE} {
  PLS_ASSERT(size == base::alignment::previous_alignment(unaligned_size),
             "Initialized aligned stack with invalid memory configuration!")
}

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 *>(memory_pointer_ + 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;
  PLS_ASSERT(current_offset_ > max_offset_,
             "Tried to allocate object on alligned_stack without sufficient memory!");

  return result;
}

}
}
}
