/**
 * @file Alignment.h
 * @author apio (cloudapio.eu)
 * @brief Functions to align addresses and sizes to specified boundaries.
 *
 * @copyright Copyright (c) 2022-2023, the Luna authors.
 *
 */

#pragma once
#include <luna/Types.h>

/**
 * @brief Check whether a value is aligned to a specific alignment.
 *
 * @tparam alignment The alignment to use. It must be provided as a template parameter to help the compiler with
 * optimizations. For best results, use a power of two.
 * @tparam T The type of the value.
 * @param value The value to check.
 * @return constexpr bool Whether the value is aligned.
 */
template <usize alignment, typename T> constexpr inline bool is_aligned(T value)
{
    return (value % alignment == 0);
}

/**
 * @brief Find the closest aligned value that is smaller than the value provided.
 *
 * @tparam alignment The alignment to use. It must be provided as a template parameter to help the compiler with
 * optimizations. For best results, use a power of two.
 * @tparam T The type of the value.
 * @param value The value to use.
 * @return constexpr T The aligned value.
 */
template <usize alignment, typename T> constexpr inline T align_down(T value)
{
    return value - value % alignment;
}

/**
 * @brief Find the closest aligned value that is bigger than the value provided.
 *
 * @tparam alignment The alignment to use. It must be provided as a template parameter to help the compiler with
 * optimizations. For best results, use a power of two.
 * @tparam T The type of the value.
 * @param value The value to use.
 * @return constexpr T The aligned value.
 */
template <usize alignment, typename T> constexpr inline T align_up(T value)
{
    if (is_aligned<alignment>(value)) return value;
    return align_down<alignment>(value) + alignment;
}

/**
 * @brief Offset a pointer by exactly <offset> bytes, no matter the type.
 *
 * Generally only useful for pointers to void (or char), because breaking other pointers' alignments is not a good
 * idea...
 *
 * @tparam T The underlying type of the pointer.
 * @tparam Offset The type of the offset.
 * @param ptr The pointer to offset.
 * @param offset The offset to use (depending on the type, this could be negative).
 * @return constexpr T* The new pointer.
 */
template <typename T, typename Offset> constexpr inline T* offset_ptr(T* ptr, Offset offset)
{
    return (T*)((u8*)ptr + offset);
}