aboutsummaryrefslogtreecommitdiffstats
path: root/util/system/hi_lo.h
blob: f86870534fb4dccc09f85cd527264cf453245a22 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
#pragma once

#include "unaligned_mem.h"

#include <utility>

#ifndef _little_endian_
    #error "Not implemented"
#endif

namespace NHiLoPrivate {
    template <class TRepr>
    class TConstIntRef {
    public:
        explicit TConstIntRef(const char* ptr)
            : Ptr(ptr)
        {
        }

        TRepr Get() const {
            return ReadUnaligned<TRepr>(Ptr);
        }
        operator TRepr() const {
            return Get();
        }

        const char* GetPtr() const {
            return Ptr;
        }

    protected:
        const char* Ptr;
    };

    template <class TRepr>
    class TIntRef: public TConstIntRef<TRepr> {
    public:
        explicit TIntRef(char* ptr)
            : TConstIntRef<TRepr>(ptr)
        {
        }

        TIntRef& operator=(TRepr value) {
            WriteUnaligned<TRepr>(GetPtr(), value);
            return *this;
        }

        char* GetPtr() const {
            return const_cast<char*>(this->Ptr);
        }
    };

    template <class T>
    struct TReferenceType {
        using TType = T;
    };

    template <class T>
    struct TReferenceType<TConstIntRef<T>> {
        using TType = T;
    };

    template <class T>
    struct TReferenceType<TIntRef<T>> {
        using TType = T;
    };

    template <class TRepr>
    auto MakeIntRef(const char* ptr) {
        return TConstIntRef<TRepr>(ptr);
    }

    template <class TRepr>
    auto MakeIntRef(char* ptr) {
        return TIntRef<TRepr>(ptr);
    }

    template <class T>
    const char* CharPtrOf(const T& value) {
        return reinterpret_cast<const char*>(&value);
    }

    template <class T>
    char* CharPtrOf(T& value) {
        return reinterpret_cast<char*>(&value);
    }

    template <class T>
    const char* CharPtrOf(TConstIntRef<T> value) {
        return value.GetPtr();
    }

    template <class T>
    char* CharPtrOf(TIntRef<T> value) {
        return value.GetPtr();
    }

    template <bool IsLow, class TRepr, class T>
    auto MakeIntRef(T&& value) {
        using TRef = typename TReferenceType<typename std::decay<T>::type>::TType;
        static_assert(
            std::is_scalar<TRef>::value,
            "Hi* and Lo* functions can be applied only to scalar values");
        static_assert(sizeof(TRef) >= sizeof(TRepr), "Requested bit range is not within provided value");
        constexpr size_t offset = IsLow ? 0 : sizeof(TRef) - sizeof(TRepr);

        return MakeIntRef<TRepr>(CharPtrOf(std::forward<T>(value)) + offset);
    }
}

/**
 * Return manipulator object that allows to get and set lower or higher bits of the value.
 *
 * @param value Must be a scalar value of sufficient size or a manipulator object obtained by
 * calling any of the other Hi/Lo functions.
 *
 * @{
 */
template <class T>
auto Lo32(T&& value) {
    return NHiLoPrivate::MakeIntRef<true, ui32>(std::forward<T>(value));
}

template <class T>
auto Hi32(T&& value) {
    return NHiLoPrivate::MakeIntRef<false, ui32>(std::forward<T>(value));
}

template <class T>
auto Lo16(T&& value) {
    return NHiLoPrivate::MakeIntRef<true, ui16>(std::forward<T>(value));
}

template <class T>
auto Hi16(T&& value) {
    return NHiLoPrivate::MakeIntRef<false, ui16>(std::forward<T>(value));
}

template <class T>
auto Lo8(T&& value) {
    return NHiLoPrivate::MakeIntRef<true, ui8>(std::forward<T>(value));
}

template <class T>
auto Hi8(T&& value) {
    return NHiLoPrivate::MakeIntRef<false, ui8>(std::forward<T>(value));
}

/** @} */