//---------------------------------------------------------
// For conditions of distribution and use, see
// https://github.com/preshing/cpp11-on-multicore/blob/master/LICENSE
//---------------------------------------------------------

#ifndef __CPP11OM_BITFIELD_H__
#define __CPP11OM_BITFIELD_H__

#include <cassert>

//---------------------------------------------------------
// BitFieldMember<>: Used internally by ADD_BITFIELD_MEMBER macro.
// All members are public to simplify compliance with sections 9.0.7 and
// 9.5.1 of the C++11 standard, thereby avoiding undefined behavior.
//---------------------------------------------------------
template <typename T, int Offset, int Bits>
struct BitFieldMember
{
    T value;

    static_assert(Offset + Bits <= (int)sizeof(T) * 8, "Member exceeds bitfield boundaries");
    static_assert(Bits < (int)sizeof(T) * 8, "Can't fill entire bitfield with one member");

    static const T Maximum = (T(1) << Bits) - 1;
    static const T Mask = Maximum << Offset;
    T maximum() const { return Maximum; }
    T one() const { return T(1) << Offset; }

    operator T() const { return (value >> Offset) & Maximum; }

    BitFieldMember &operator=(T v)
    {
        assert(v <= Maximum); // v must fit inside the bitfield member
        value = (value & ~Mask) | (v << Offset);
        return *this;
    }

    BitFieldMember &operator+=(T v)
    {
        assert(T(*this) + v <= Maximum); // result must fit inside the bitfield member
        value += v << Offset;
        return *this;
    }

    BitFieldMember &operator-=(T v)
    {
        assert(T(*this) >= v); // result must not underflow
        value -= v << Offset;
        return *this;
    }

    BitFieldMember &operator++() { return *this += 1; }
    BitFieldMember &operator++(int) { return *this += 1; } // postfix form
    BitFieldMember &operator--() { return *this -= 1; }
    BitFieldMember &operator--(int) { return *this -= 1; } // postfix form
};

//---------------------------------------------------------
// BitFieldArray<>: Used internally by ADD_BITFIELD_ARRAY macro.
// All members are public to simplify compliance with sections 9.0.7 and
// 9.5.1 of the C++11 standard, thereby avoiding undefined behavior.
//---------------------------------------------------------
template <typename T, int BaseOffset, int BitsPerItem, int NumItems>
struct BitFieldArray
{
    T value;

    static_assert(BaseOffset + BitsPerItem * NumItems <= (int)sizeof(T) * 8,
                  "Array exceeds bitfield boundaries");
    static_assert(BitsPerItem < (int)sizeof(T) * 8,
                  "Can't fill entire bitfield with one array element");

    static const T Maximum = (T(1) << BitsPerItem) - 1;
    T maximum() const { return Maximum; }
    int numItems() const { return NumItems; }

    class Element
    {
    private:
        T &value;
        int offset;

    public:
        Element(T &value, int offset) : value(value), offset(offset) {}
        T mask() const { return Maximum << offset; }

        operator T() const { return (value >> offset) & Maximum; }

        Element &operator=(T v)
        {
            assert(v <= Maximum); // v must fit inside the bitfield member
            value = (value & ~mask()) | (v << offset);
            return *this;
        }

        Element &operator+=(T v)
        {
            assert(T(*this) + v <= Maximum); // result must fit inside the bitfield member
            value += v << offset;
            return *this;
        }

        Element &operator-=(T v)
        {
            assert(T(*this) >= v); // result must not underflow
            value -= v << offset;
            return *this;
        }

        Element &operator++() { return *this += 1; }
        Element &operator++(int) { return *this += 1; } // postfix form
        Element &operator--() { return *this -= 1; }
        Element &operator--(int) { return *this -= 1; } // postfix form
    };

    Element operator[](int i)
    {
        assert(i >= 0 && i < NumItems); // array index must be in range
        return Element(value, BaseOffset + BitsPerItem * i);
    }

    const Element operator[](int i) const
    {
        assert(i >= 0 && i < NumItems); // array index must be in range
        return Element(value, BaseOffset + BitsPerItem * i);
    }
};

//---------------------------------------------------------
// Bitfield definition macros.
// For usage examples, see RWLock and LockReducedDiningPhilosophers.
// All members are public to simplify compliance with sections 9.0.7 and
// 9.5.1 of the C++11 standard, thereby avoiding undefined behavior.
//---------------------------------------------------------
#define BEGIN_BITFIELD_TYPE(typeName, T)                                                           \
    union typeName                                                                                 \
    {                                                                                              \
        struct Wrapper                                                                             \
        {                                                                                          \
            T value;                                                                               \
        };                                                                                         \
        Wrapper wrapper;                                                                           \
        typeName(T v = 0) { wrapper.value = v; }                                                   \
        typeName &operator=(T v)                                                                   \
        {                                                                                          \
            wrapper.value = v;                                                                     \
            return *this;                                                                          \
        }                                                                                          \
        operator T &() { return wrapper.value; }                                                   \
        operator T() const { return wrapper.value; }                                               \
        typedef T StorageType;

#define ADD_BITFIELD_MEMBER(memberName, offset, bits)                                              \
    BitFieldMember<StorageType, offset, bits> memberName;

#define ADD_BITFIELD_ARRAY(memberName, offset, bits, numItems)                                     \
    BitFieldArray<StorageType, offset, bits, numItems> memberName;

#define END_BITFIELD_TYPE()                                                                        \
    }                                                                                              \
    ;

#endif // __CPP11OM_BITFIELD_H__
