1/**
2 * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#ifndef LIBPANDABASE_UTILS_BIT_FIELD_H
17#define LIBPANDABASE_UTILS_BIT_FIELD_H
18
19#include <limits>
20#include "macros.h"
21
22namespace panda {
23
24/*
25 * Auxiliary static class that provides access to bits range within an integer value.
26 */
27template <typename T, size_t start, size_t bits_num = 1>
28class BitField {
29    static constexpr unsigned BITS_PER_BYTE = 8;
30
31    static_assert(start < sizeof(uint64_t) * BITS_PER_BYTE, "Invalid position");
32    static_assert(bits_num != 0U, "Invalid size");
33    static_assert(bits_num <= sizeof(uint64_t) * BITS_PER_BYTE, "Invalid size");
34    static_assert(bits_num + start <= sizeof(uint64_t) * BITS_PER_BYTE, "Invalid position + size");
35
36public:
37    using ValueType = T;
38    static constexpr unsigned START_BIT = start;
39    static constexpr unsigned END_BIT = start + bits_num;
40    static constexpr unsigned SIZE = bits_num;
41
42    /*
43     * This is static class and should not be instantiated.
44     */
45    BitField() = delete;
46
47    virtual ~BitField() = delete;
48
49    NO_COPY_SEMANTIC(BitField);
50    NO_MOVE_SEMANTIC(BitField);
51
52    /*
53     * Make BitField type that follows right after current bit range.
54     *
55     *  If we have
56     *   BitField<T, 0, 9>
57     * then
58     *   BitField<T, 0, 9>::NextField<T,3>
59     * will be equal to
60     *   BitField<T, 9, 3>
61     *
62     * It is helpful when we need to specify chain of fields.
63     */
64    template <typename T2, unsigned bits_num2>
65    using NextField = BitField<T2, start + bits_num, bits_num2>;
66
67    /*
68     * Make Flag field that follows right after current bit range.
69     * Same as NextField, but no need to specify number of bits, it is always 1.
70     */
71    using NextFlag = BitField<bool, start + bits_num, 1>;
72
73public:
74    /*
75     * Return maximum value that fits bit range [START_BIT : START_BIT+END_BIT]
76     */
77    static constexpr uint64_t MaxValue()
78    {
79        return (1LLU << bits_num) - 1;
80    }
81
82    /*
83     * Return mask of bit range, f.e. 0b1110 for BitField<T, 1, 3>
84     */
85    static constexpr uint64_t Mask()
86    {
87        return MaxValue() << start;
88    }
89
90    /*
91     * Check if given value fits into the bit field
92     */
93    static constexpr bool IsValid(T value)
94    {
95        return (static_cast<uint64_t>(value) & ~MaxValue()) == 0;
96    }
97
98    /*
99     * Set 'value' to current bit range [START_BIT : START_BIT+END_BIT] within the 'stor' parameter.
100     */
101    template <typename Stor>
102    static constexpr void Set(T value, Stor *stor)
103    {
104        static_assert(END_BIT <= std::numeric_limits<Stor>::digits);
105        *stor = (*stor & ~Mask()) | Encode(value);
106    }
107
108    /*
109     * Return bit range [START_BIT : START_BIT+END_BIT] value from given integer 'value'
110     */
111    static constexpr T Get(uint64_t value)
112    {
113        return static_cast<T>((value >> start) & MaxValue());
114    }
115
116    /*
117     * Encode 'value' to current bit range [START_BIT : START_BIT+END_BIT] and return it
118     */
119    static constexpr uint64_t Encode(T value)
120    {
121        ASSERT(IsValid(value));
122        return (static_cast<uint64_t>(value) << start);
123    }
124
125    /*
126     * Update 'value' to current bit range [START_BIT : START_BIT+END_BIT] and return it
127     */
128    static constexpr uint64_t Update(uint64_t old_value, T value)
129    {
130        return (old_value & ~Mask()) | Encode(value);
131    }
132
133    /*
134     * Decode from value
135     */
136    static constexpr T Decode(uint64_t value)
137    {
138        return Get(value);
139    }
140};
141
142}  // namespace panda
143
144#endif  // LIBPANDABASE_UTILS_BIT_FIELD_H
145