// -*- Mode: C++ -*-
// vim:ft=cpp
/*!
 * \file
 * \brief  Item 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/cxx/bitmap>

namespace L4Re { namespace Util {

using cxx::Bitmap_base;
using cxx::Bitmap;

/**
 * \brief Item allocator.
 */
class Item_alloc_base
{
private:
  long _capacity;
  long _free_hint;
  Bitmap_base _bits;

  void hint(long hint)
  { __atomic_store_n(&_free_hint, hint, __ATOMIC_RELAXED); }

public:
  bool is_allocated(long item) const noexcept
  { return _bits[item]; }

  long hint() const { return __atomic_load_n(&_free_hint, __ATOMIC_RELAXED); }

  bool alloc(long item) noexcept
  {
    return !_bits.atomic_get_and_set(item);
  }

  void free(long item) noexcept
  {
    if (item < hint())
      hint(item);

    _bits.atomic_clear_bit(item);
  }

  Item_alloc_base(long size, void *mem) noexcept
    : _capacity(size), _free_hint(0), _bits(mem)
  {}

  long alloc() noexcept
  {
    long free_hint = hint();

    for (long i = free_hint; i < _capacity; ++i)
      if (alloc(i))
        {
          hint(i + 1);
          return i;
        }

    // _free_hint is not necessarily correct in case of multi-threading! Make
    // sure we don't miss any potentially free slots.
    for (long i = 0; i < free_hint && i < _capacity; ++i)
      if (alloc(i))
        {
          hint(i + 1);
          return i;
        }

    return -1;
  }

  long size() const noexcept
  {
    return _capacity;
  }
};

template< long Bits >
class Item_alloc : public Item_alloc_base
{
private:
  typename Bitmap_base::Word<Bits>::Type _bits[Bitmap_base::Word<Bits>::Size];

public:
  Item_alloc() noexcept : Item_alloc_base(Bits, _bits) {}
};

}}
