// vim:set ft=cpp: -*- Mode: C++ -*-
/**
 * \file
 * Shared_cap / Shared_del_cap
 */
/*
 * (c) 2017 Alexander Warg <alexander.warg@kernkonzept.com>
 *
 * License: see LICENSE.spdx (in this directory or the directories above)
 */

#pragma once

#include <l4/re/util/cap_alloc>
#include <l4/sys/cxx/smart_capability_1x>
#include <l4/sys/cxx/types>

namespace L4Re { namespace Util {

/**
 * Shared capability that implements automatic free and unmap of the capability
 * selector.
 *
 * \tparam T  Type of the object the capability refers to.
 *
 * This shared capability implements a counted reference to a capability
 * selector. The capability shall be unmapped and freed when the reference
 * count in the allocator goes to zero.
 *
 * Usage:
 *
 *     L4Re::Util::Shared_cap<L4Re::Dataspace> global_ds_cap;
 *
 *     {
 *       L4Re::Util::Shared_cap<L4Re::Dataspace>
 *         ds_cap = make_shared_cap<L4Re::Dataspace>();
 *       // reference count for the allocated cap selector is now 1
 *
 *       // use the dataspace cap
 *       L4Re::chksys(mem_alloc->alloc(4096, ds_cap.get()));
 *
 *       global_ds_cap = ds_cap;
 *       // reference count is now 2
 *       ...
 *     }
 *     // reference count dropped to 1 (ds_cap is no longer existing).
 */
template< typename T >
using Shared_cap
  = L4::Detail::Shared_cap_impl<T, L4Re::Util::Smart_count_cap<L4_FP_ALL_SPACES>>;
/// Deprecated; use L4Re::Util::Shared_cap. \deprecated Use L4Re::Util::Shared_cap.
template< typename T >
using shared_cap [[deprecated("Use L4Re::Util::Shared_cap.")]]
  = L4::Detail::Shared_cap_impl<T, L4Re::Util::Smart_count_cap<L4_FP_ALL_SPACES>>;

/**
 * Allocate a capability slot and wrap it in a Shared_cap.
 *
 * \tparam T  Type of the object the capability refers to.
 */
template< typename T >
Shared_cap<T>
make_shared_cap()
{ return Shared_cap<T>(cap_alloc.alloc<T>()); }

/**
 * Create a new shared capability by an explicit cast from another shared
 * capability.
 *
 * Type `U` must be convertible to type `T`.
 *
 * \see L4::cap_cast
 *
 * \tparam T    Type of the object the capability refers to.
 * \tparam U    Type of the source shared capability.
 * \param from  Source shared capability.
 */
template<typename T, typename U>
Shared_cap<T> shared_cap_cast(Shared_cap<U> const &from) noexcept
{
  auto to = L4::cap_cast<T>(from.get());
  return Shared_cap<T>(from, to);
}

/**
 * Create a new shared capability by an explicit cast from another shared
 * capability with move semantics.
 *
 * Type `U` must be convertible to type `T`. After the call, `from` is empty.
 *
 * \see L4::cap_cast
 *
 * \tparam T    Type of the object the capability refers to.
 * \tparam U    Type of the source shared capability.
 * \param from  Source shared capability.
 */
template<typename T, typename U>
Shared_cap<T> shared_cap_cast(Shared_cap<U> &&from) noexcept
{
  auto to = L4::cap_cast<T>(from.get());
  return Shared_cap<T>(L4::Types::move(from), to);
}

/**
 * Create a new shared capability by a reinterpret cast from another shared
 * capability.
 *
 * \see L4::cap_reinterpret_cast
 *
 * \tparam T    Type of the object the capability refers to.
 * \tparam U    Type of the source shared capability.
 * \param from  Source shared capability.
 */
template<typename T, typename U>
Shared_cap<T> shared_cap_reinterpret_cast(Shared_cap<U> const &from) noexcept
{
  auto to = L4::cap_reinterpret_cast<T>(from.get());
  return Shared_cap<T>(from, to);
}

/**
 * Create a new shared capability by a reinterpret cast from another shared
 * capability with move semantics.
 *
 * After the call, `from` is empty.
 *
 * \see L4::cap_reinterpret_cast
 *
 * \tparam T    Type of the object the capability refers to.
 * \tparam U    Type of the source shared capability.
 * \param from  Source shared capability.
 */
template<typename T, typename U>
Shared_cap<T> shared_cap_reinterpret_cast(Shared_cap<U> &&from) noexcept
{
  auto to = L4::cap_reinterpret_cast<T>(from.get());
  return Shared_cap<T>(L4::Types::move(from), to);
}

/**
 * Create a new shared capability by a dynamic cast from another shared
 * capability.
 *
 * \see L4::cap_dynamic_cast
 *
 * \tparam T    Type of the object the capability refers to.
 * \tparam U    Type of the source shared capability.
 * \param from  Source shared capability.
 */
template<typename T, typename U>
Shared_cap<T> shared_cap_dynamic_cast(Shared_cap<U> const &from) noexcept
{
  if (auto to = L4::cap_dynamic_cast<T>(from.get()))
    return Shared_cap<T>(from, to);
  else
    return Shared_cap<T>();
}

/**
 * Create a new shared capability by an dynamic cast from another shared
 * capability with move semantics.
 *
 * \see L4::cap_dynamic_cast
 *
 * After the call, `from` is empty, unless the L4::cap_dynamic_cast fails.
 *
 * \tparam T    Type of the object the capability refers to.
 * \tparam U    Type of the source shared capability.
 * \param from  Source shared capability.
 */
template<typename T, typename U>
Shared_cap<T> shared_cap_dynamic_cast(Shared_cap<U> &&from) noexcept
{
  if (auto to = L4::cap_dynamic_cast<T>(from.get()))
    return Shared_cap<T>(L4::Types::move(from), to);
  else
    return Shared_cap<T>();
}

/**
 * Shared capability that implements automatic free and unmap+delete of the
 * capability selector.
 *
 * \tparam T  Type of the object the capability refers to.
 *
 * This shared capability implements a counted reference to a capability
 * selector. The capability shall be unmapped and freed when the reference
 * count in the allocator goes to zero.
 * The main difference to Shared_cap is that the unmap is done with the
 * deletion flag enabled and this leads to the deletion of the object
 * if the current task holds appropriate deletion rights.
 *
 * Usage:
 *
 *     L4Re::Util::Shared_del_cap<L4Re::Dataspace> global_ds_cap;
 *
 *     {
 *       L4Re::Util::Shared_del_cap<L4Re::Dataspace>
 *         ds_cap = make_shared_del_cap<L4Re::Dataspace>();
 *       // reference count for the allocated cap selector is now 1
 *
 *       // use the dataspace cap
 *       L4Re::chksys(mem_alloc->alloc(4096, ds_cap.get()));
 *
 *       global_ds_cap = ds_cap;
 *       // reference count is now 2
 *       ...
 *     }
 *     // reference count dropped to 1 (ds_cap is no longer existing).
 *     ...
 *     global_ds_cap = L4_INVALID_CAP;
 *     // reference count dropped to 0 (data space shall be deleted).
 */
template< typename T >
using Shared_del_cap
  = L4::Detail::Shared_cap_impl<T, L4Re::Util::Smart_count_cap<L4_FP_DELETE_OBJ>>;
/// Deprecated; use L4Re::Util::Shared_del_cap. \deprecated Use L4Re::Util::Shared_del_cap.
template< typename T >
using shared_del_cap [[deprecated("Use L4Re::Util::Shared_del_cap.")]]
  = L4::Detail::Shared_cap_impl<T, L4Re::Util::Smart_count_cap<L4_FP_DELETE_OBJ>>;

/**
 * Allocate a capability slot and wrap it in a Shared_del_cap.
 *
 * \tparam T  Type of the object the capability refers to.
 */
template< typename T >
Shared_del_cap<T>
make_shared_del_cap()
{ return Shared_del_cap<T>(cap_alloc.alloc<T>()); }

/**
 * Create a new shared capability by an explicit cast from another shared
 * capability.
 *
 * Type `U` must be convertible to type `T`.
 *
 * \see L4::cap_cast
 *
 * \tparam T    Type of the object the capability refers to.
 * \tparam U    Type of the source shared capability.
 * \param from  Source shared capability.
 */
template<typename T, typename U>
Shared_del_cap<T> shared_del_cap_cast(Shared_del_cap<U> const &from) noexcept
{
  auto to = L4::cap_cast<T>(from.get());
  return Shared_del_cap<T>(from, to);
}

/**
 * Create a new shared capability by an explicit cast from another shared
 * capability with move semantics.
 *
 * Type `U` must be convertible to type `T`. After the call, `from` is empty.
 *
 * \see L4::cap_cast
 *
 * \tparam T    Type of the object the capability refers to.
 * \tparam U    Type of the source shared capability.
 * \param from  Source shared capability.
 */
template<typename T, typename U>
Shared_del_cap<T> shared_del_cap_cast(Shared_del_cap<U> &&from) noexcept
{
  auto to = L4::cap_cast<T>(from.get());
  return Shared_del_cap<T>(L4::Types::move(from), to);
}

/**
 * Create a new shared capability by a reinterpret cast from another shared
 * capability.
 *
 * \see L4::cap_reinterpret_cast
 *
 * \tparam T    Type of the object the capability refers to.
 * \tparam U    Type of the source shared capability.
 * \param from  Source shared capability.
 */
template<typename T, typename U>
Shared_del_cap<T>
shared_del_cap_reinterpret_cast(Shared_del_cap<U> const &from) noexcept
{
  auto to = L4::cap_reinterpret_cast<T>(from.get());
  return Shared_del_cap<T>(from, to);
}

/**
 * Create a new shared capability by a reinterpret cast from another shared
 * capability with move semantics.
 *
 * After the call, `from` is empty.
 *
 * \see L4::cap_reinterpret_cast
 *
 * \tparam T    Type of the object the capability refers to.
 * \tparam U    Type of the source shared capability.
 * \param from  Source shared capability.
 */
template<typename T, typename U>
Shared_del_cap<T>
shared_del_cap_reinterpret_cast(Shared_del_cap<U> &&from) noexcept
{
  auto to = L4::cap_reinterpret_cast<T>(from.get());
  return Shared_del_cap<T>(L4::Types::move(from), to);
}

/**
 * Create a new shared capability by a dynamic cast from another shared
 * capability.
 *
 * \see L4::cap_dynamic_cast
 *
 * \tparam T    Type of the object the capability refers to.
 * \tparam U    Type of the source shared capability.
 * \param from  Source shared capability.
 */
template<typename T, typename U>
Shared_del_cap<T>
shared_del_cap_dynamic_cast(Shared_del_cap<U> const &from) noexcept
{
  if (auto to = L4::cap_dynamic_cast<T>(from.get()))
    return Shared_del_cap<T>(from, to);
  else
    return Shared_del_cap<T>();
}

/**
 * Create a new shared capability by an dynamic cast from another shared
 * capability with move semantics.
 *
 * \see L4::cap_dynamic_cast
 *
 * After the call, `from` is empty, unless the L4::cap_dynamic_cast fails.
 *
 * \tparam T    Type of the object the capability refers to.
 * \tparam U    Type of the source shared capability.
 * \param from  Source shared capability.
 */
template<typename T, typename U>
Shared_del_cap<T>
shared_del_cap_dynamic_cast(Shared_del_cap<U> &&from) noexcept
{
  if (auto to = L4::cap_dynamic_cast<T>(from.get()))
    return Shared_del_cap<T>(L4::Types::move(from), to);
  else
    return Shared_del_cap<T>();
}

}} // namespace L4Re::Util

