// -*- Mode: C++ -*-
// vim:ft=cpp
/**
 * \file
 * \brief  Bitmap capability allocator
 */
/*
 * (c) 2008-2009 Adam Lackorzynski <adam@os.inf.tu-dresden.de>,
 *               Alexander Warg <warg@os.inf.tu-dresden.de>
 *     economic rights: Technische Universität Dresden (Germany)
 *
 * License: see LICENSE.spdx (in this directory or the directories above)
 */

#pragma once

#include <l4/re/util/item_alloc>
#include <l4/sys/assert.h>
#include <l4/sys/capability>
#include <l4/sys/task.h>

namespace L4Re { namespace Util {

/**
 * \brief Capability allocator.
 * \ingroup api_l4re_util
 */
class Cap_alloc_base
{
private:
  long _bias;
  Item_alloc_base _items;

public:
  template <unsigned COUNT>
  struct Storage
  {
    typename Bitmap_base::Word<COUNT>::Type _bits[Bitmap_base::Word<COUNT>::Size];
  };

  enum State { Free = 0, Allocated, Unknown };
  Cap_alloc_base(long max, void *mem, long bias = 0, void * = 0)
    noexcept : _bias(bias), _items(max, mem) {}

  L4::Cap<void> alloc() noexcept
  {
    long cap = _items.alloc();
    if (cap < 0)
      return L4::Cap<void>::Invalid;

    return L4::Cap<void>((cap + _bias) << L4_CAP_SHIFT);
  }

  long hint() const { return _items.hint(); }

  /**
   * \brief Allocate a capability slot.
   */
  template< typename T >
  L4::Cap<T> alloc() noexcept
  { return L4::Cap<T>(alloc().cap()); }

  State is_allocated(L4::Cap<void> c) const noexcept
  {
    long idx = (c.cap() >> L4_CAP_SHIFT);

    if (idx < _bias)
      return Unknown;

    idx -= _bias;
    if (idx >= _items.size())
      return Unknown;

    return _items.is_allocated(idx) ? Allocated : Free;
  }

  /**
   * \brief Free a capability slot.
   */
  template< typename T>
  void free(L4::Cap<T> const &cap, l4_cap_idx_t task = L4_INVALID_CAP,
            l4_umword_t unmap_flags = L4_FP_ALL_SPACES) noexcept
  {
    long idx = (cap.cap() >> L4_CAP_SHIFT);
    if (idx < _bias)
      return;

    idx -= _bias;
    if (idx >= _items.size())
      return;

    l4_assert(_items.is_allocated(idx));

    if (l4_is_valid_cap(task))
      l4_task_unmap(task, cap.fpage(), unmap_flags | 2);

    _items.free(idx);
  }

  // since we have no counters assume counter always > 0
  void take(L4::Cap<void>) noexcept {}
  bool release(L4::Cap<void>, l4_cap_idx_t /*task*/ = L4_INVALID_CAP,
               unsigned /*unmap_flags*/ = L4_FP_ALL_SPACES) noexcept
  { return false; }

  long last() noexcept
  {
    return _items.size() + _bias - 1;
  }
};

template< long Size >
class Cap_alloc : public Cap_alloc_base
{
private:
  typename Bitmap_base::Word<Size>::Type _bits[Bitmap_base::Word<Size>::Size];

public:
  explicit Cap_alloc(long bias = 0) noexcept
    : Cap_alloc_base(Size, _bits, bias) {}

};

}
}
