xaizek / tos (License: GPLv3 only) (since 2018-12-07)
This is an alternative version of sources presented as part of Write Your Own OS video tutorial by Viktor Engelmann.
<root> / src / utils / struct.hpp (2b5005c273ed033e56dde40d618879c48fb56a76) (5,700B) (mode 100644) [raw]
#ifndef TOS__UTILS__STRUCT_HPP__
#define TOS__UTILS__STRUCT_HPP__

#include <limits>
#include <type_traits>

#include "utils/conv.hpp"

template <typename T>
struct NetOrder
{
    static_assert(std::is_integral<T>::value,
                  "NetOrder accepts only integral types!");

    constexpr NetOrder() = default;

    explicit constexpr NetOrder(T value) : BE(value)
    {
    }

    friend constexpr bool operator==(NetOrder lhs, NetOrder rhs)
    {
        return (lhs.BE == rhs.BE);
    }

    T BE;
};

template <typename T>
constexpr NetOrder<T>
netOrder(T value)
{
    return NetOrder<T>(value);
}

template <typename T>
constexpr NetOrder<T>
toNetOrder(T value)
{
    return NetOrder<T>(hton(value));
}

template <std::size_t O, std::size_t N, typename T>
class NetField
{
    static_assert(std::is_integral<T>::value,
                  "NetField accepts only integral types!");
    static_assert(!std::is_signed<T>::value,
                  "NetField accepts only unsigned types!");
    static_assert(N <= sizeof(T),
                  "NetField width can't be bigger than size of the type!");
    static_assert(N > 0U,
                  "NetField can't handle field of zero width!");

public:
    static constexpr std::size_t offset = O;
    static constexpr std::size_t size = N;

public:
    explicit NetField(std::uint8_t data[]) : ptr(data + O), cached({})
    {
        for (std::size_t i = N; i != 0U; --i) {
            cached.BE = (cached.BE << 8) | ptr[i - 1U];
        }
    }

    // Fields aren't supposed to be copied or assigned in any way.
    NetField(NetField &&rhs) = delete;
    NetField & operator=(NetField &&rhs) = delete;

public:
    NetField & operator=(NetOrder<T> value)
    {
        cached = value;
        for (std::size_t i = 0; i < N; ++i) {
            ptr[i] = value.BE & 0xff;
            value.BE >>= 8;
        }
        return *this;
    }

    operator NetOrder<T>() const
    {
        return cached;
    }

    const T &BE = cached.BE;

private:
    std::uint8_t *ptr;
    NetOrder<T> cached;
};

template <typename T, std::size_t N = sizeof(T)>
class Field
{
    static_assert(std::is_integral<T>::value,
                  "Field accepts only integral types!");
    static_assert(!std::is_signed<T>::value,
                  "Field accepts only unsigned types!");
    static_assert(N <= sizeof(T),
                  "Field width can't be bigger than size of the type!");
    static_assert(N > 0U,
                  "Field can't handle field of zero width!");

public:
    static constexpr std::size_t size = N;

public:
    explicit Field(std::uint8_t *&data) : ptr(data), cached()
    {
        for (std::size_t i = N; i != 0U; --i) {
            cached = (cached << 8) | ptr[i - 1U];
        }
        data += N;
    }

    // Fields aren't supposed to be copied or assigned in any way.
    Field(Field &&rhs) = delete;
    Field & operator=(Field &&rhs) = delete;

public:
    Field & operator=(T value)
    {
        cached = value;
        for (std::size_t i = 0; i < N; ++i) {
            ptr[i] = value & 0xff;
            value >>= 8;
        }
        return *this;
    }

    operator T() const
    {
        return cached;
    }

private:
    std::uint8_t *ptr;
    T cached;
};

template <typename T, bool = std::is_integral<T>::value>
class ArrayField
{
public:
    static constexpr std::size_t N = T::size();
    static constexpr std::size_t size = N;

public:
    explicit ArrayField(std::uint8_t *&data) : field(data)
    {
    }

    // Fields aren't supposed to be copied or assigned in any way.
    ArrayField(ArrayField &&rhs) = delete;
    ArrayField & operator=(ArrayField &&rhs) = delete;

public:
    operator T&()
    {
        return field;
    }

    T * operator->()
    {
        return &field;
    }

private:
    T field;
};

template <typename T>
class ArrayField<T, true>
{
    static_assert(!std::is_signed<T>::value,
                  "ArrayField accepts only unsigned types!");

public:
    static constexpr std::size_t N = sizeof(T);
    static constexpr std::size_t size = N;

public:
    explicit ArrayField(std::uint8_t *&data) : ptr(data), cached()
    {
        for (std::size_t i = N; i != 0U; --i) {
            cached = (cached << 8) | ptr[i - 1U];
        }
        data += N;
    }

    // Fields aren't supposed to be copied or assigned in any way.
    ArrayField(ArrayField &&rhs) = delete;
    ArrayField & operator=(ArrayField &&rhs) = delete;

public:
    ArrayField & operator=(T value)
    {
        cached = value;
        for (std::size_t i = 0; i < N; ++i) {
            ptr[i] = value & 0xff;
            value >>= 8;
        }
        return *this;
    }

    operator T() const
    {
        return cached;
    }

private:
    std::uint8_t *ptr;
    T cached;
};

template <typename T, std::size_t C>
class Fields
{
    static_assert(C > 0U, "Fields can't have zero size!");

public:
    static constexpr std::size_t size = C*ArrayField<T>::size();

public:
    explicit Fields(std::uint8_t *&data)
    {
        for (std::size_t i = 0U; i < C; ++i) {
            fields[i] = new(&datas[i].field) ArrayField<T>(data);
        }
    }

    ~Fields()
    {
        for (Data &field : datas) {
            field.field.~ArrayField();
        }
    }

    // Fields aren't supposed to be copied or assigned in any way.
    Fields(Fields &&rhs) = delete;
    Fields & operator=(Fields &&rhs) = delete;

public:
    ArrayField<T> & operator[](std::size_t idx)
    {
        return *fields[idx];
    }

private:
    union Data {
        ArrayField<T> field;

        Data() {}
        ~Data() {}
    };
    Data datas[C];

    ArrayField<T> *fields[C];
};

#endif // TOS__UTILS__STRUCT_HPP__
Hints

Before first commit, do not forget to setup your git environment:
git config --global user.name "your_name_here"
git config --global user.email "your@email_here"

Clone this repository using HTTP(S):
git clone https://code.reversed.top/user/xaizek/tos

Clone this repository using ssh (do not forget to upload a key first):
git clone ssh://rocketgit@code.reversed.top/user/xaizek/tos

You are allowed to anonymously push to this repository.
This means that your pushed commits will automatically be transformed into a pull request:
... clone the repository ...
... make some changes and some commits ...
git push origin master