// Copyright (c) 2022-2023, NVIDIA CORPORATION. All rights reserved.
//
// NVIDIA CORPORATION and its licensors retain all intellectual property
// and proprietary rights in and to this software, related documentation
// and any modifications thereto. Any use, reproduction, disclosure or
// distribution of this software and related documentation without an express
// license agreement from NVIDIA CORPORATION is strictly prohibited.
//

//! @file Span.h
//!
//! @brief Defines @ref omni::graph::exec::unstable::Span.
#pragma once

#include <cstdint>
#include <type_traits>

namespace omni
{
namespace graph
{
namespace exec
{
namespace unstable
{

//! A pointer along with the number of items the pointer points to.
//!
//! This object is ABI-safe.
template <typename T>
class Span
{
public:
    //! Constructor.
    Span(T* buffer, uint64_t count) noexcept : m_buffer(buffer), m_count(count)
    {
        static_assert(offsetof(Span<T>, m_buffer) == 0, "unexpected buffer offset");
        static_assert(offsetof(Span<T>, m_count) == 8, "unexpected count offset");
        static_assert(16 == sizeof(Span<T>), "Span is an unexpected size");
        static_assert(std::is_standard_layout<Span<T>>::value, "Span is expected to be abi safe");
    }

    //! Returns a pointer to the beginning of the array.
    T* begin() noexcept
    {
        return m_buffer;
    }

    //! Returns a const pointer to the beginning of the array.
    const T* begin() const noexcept
    {
        return m_buffer;
    }

    //! Returns a pointer to one past the end of the array.
    T* end() noexcept
    {
        return m_buffer + m_count;
    }

    //! Returns a const pointer to one past the end of the array.
    const T* end() const noexcept
    {
        return m_buffer + m_count;
    }

    //! Return @c true if the span is empty.
    bool empty() const noexcept
    {
        return (0 == m_count);
    }

    //! Returns a reference to the first element.
    //!
    //! Calling when the span is empty is undefined behavior.
    T& front() noexcept
    {
        return *(begin());
    }

    //! Returns a const reference to the first element.
    //!
    //! Calling when the span is empty is undefined behavior.
    const T& front() const noexcept
    {
        return *(begin());
    }

    //! Returns a reference to the last element.
    //!
    //! Calling when the span is empty is undefined behavior.
    T& back() noexcept
    {
        return *(end() - 1);
    }

    //! Returns a const reference to the last element.
    //!
    //! Calling when the span is empty is undefined behavior.
    const T& back() const noexcept
    {
        return *(end() - 1);
    }

    //! Returns a pointer to the beginning of the array.
    T* data() noexcept
    {
        return m_buffer;
    }

    //! Returns a pointer to the beginning of the array.
    const T* data() const noexcept
    {
        return m_buffer;
    }

    //! Returns the number of items in the array.
    uint64_t size() const noexcept
    {
        return m_count;
    }

private:
    T* m_buffer;
    uint64_t m_count;
};

} // namespace unstable
} // namespace exec
} // namespace graph
} // namespace omni
