├── .clang-format ├── .gitignore ├── LICENSE ├── README.md ├── advanced_bitfield.hpp ├── bit_ops.hpp ├── bitcast.hpp ├── examples ├── overlapping_parts.cpp ├── simple.cpp └── x86_segment_descriptor.cpp ├── field_ref.hpp ├── get_int_storage.hpp ├── helper_macro.hpp ├── holder.hpp ├── storage_ops.hpp └── tests ├── CMakeLists.txt ├── FetchDoctest.cmake ├── README.md ├── tests ├── overlapping_parts.cpp ├── simple.cpp └── x86_segment_descriptor.cpp └── tests_main.cpp /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | # BasedOnStyle: Mozilla 4 | AccessModifierOffset: -2 5 | AlignAfterOpenBracket: AlwaysBreak 6 | AlignArrayOfStructures: None 7 | AlignConsecutiveAssignments: 8 | Enabled: false 9 | AcrossEmptyLines: false 10 | AcrossComments: false 11 | AlignCompound: false 12 | AlignFunctionPointers: false 13 | PadOperators: true 14 | AlignConsecutiveBitFields: 15 | Enabled: false 16 | AcrossEmptyLines: false 17 | AcrossComments: false 18 | AlignCompound: false 19 | AlignFunctionPointers: false 20 | PadOperators: false 21 | AlignConsecutiveDeclarations: 22 | Enabled: false 23 | AcrossEmptyLines: false 24 | AcrossComments: false 25 | AlignCompound: false 26 | AlignFunctionPointers: false 27 | PadOperators: false 28 | AlignConsecutiveMacros: 29 | Enabled: false 30 | AcrossEmptyLines: false 31 | AcrossComments: false 32 | AlignCompound: false 33 | AlignFunctionPointers: false 34 | PadOperators: false 35 | AlignConsecutiveShortCaseStatements: 36 | Enabled: false 37 | AcrossEmptyLines: false 38 | AcrossComments: false 39 | AlignCaseColons: false 40 | AlignEscapedNewlines: Right 41 | AlignOperands: Align 42 | AlignTrailingComments: 43 | Kind: Always 44 | OverEmptyLines: 0 45 | AllowAllArgumentsOnNextLine: true 46 | AllowAllParametersOfDeclarationOnNextLine: false 47 | AllowBreakBeforeNoexceptSpecifier: Never 48 | AllowShortBlocksOnASingleLine: Never 49 | AllowShortCaseLabelsOnASingleLine: false 50 | AllowShortCompoundRequirementOnASingleLine: true 51 | AllowShortEnumsOnASingleLine: true 52 | AllowShortFunctionsOnASingleLine: Inline 53 | AllowShortIfStatementsOnASingleLine: Never 54 | AllowShortLambdasOnASingleLine: All 55 | AllowShortLoopsOnASingleLine: false 56 | AlwaysBreakAfterDefinitionReturnType: None 57 | AlwaysBreakAfterReturnType: None 58 | AlwaysBreakBeforeMultilineStrings: false 59 | AlwaysBreakTemplateDeclarations: Yes 60 | AttributeMacros: 61 | - __capability 62 | BinPackArguments: false 63 | BinPackParameters: false 64 | BitFieldColonSpacing: Both 65 | BraceWrapping: 66 | AfterCaseLabel: false 67 | AfterClass: true 68 | AfterControlStatement: Never 69 | AfterEnum: true 70 | AfterExternBlock: true 71 | AfterFunction: true 72 | AfterNamespace: false 73 | AfterObjCDeclaration: false 74 | AfterStruct: true 75 | AfterUnion: true 76 | BeforeCatch: false 77 | BeforeElse: false 78 | BeforeLambdaBody: false 79 | BeforeWhile: false 80 | IndentBraces: false 81 | SplitEmptyFunction: true 82 | SplitEmptyRecord: false 83 | SplitEmptyNamespace: true 84 | BreakAdjacentStringLiterals: true 85 | BreakAfterAttributes: Leave 86 | BreakAfterJavaFieldAnnotations: false 87 | BreakArrays: true 88 | BreakBeforeBinaryOperators: None 89 | BreakBeforeConceptDeclarations: Always 90 | BreakBeforeBraces: Allman 91 | BreakBeforeInlineASMColon: OnlyMultiline 92 | BreakBeforeTernaryOperators: true 93 | BreakConstructorInitializers: BeforeComma 94 | BreakInheritanceList: BeforeComma 95 | BreakStringLiterals: true 96 | ColumnLimit: 80 97 | CommentPragmas: '^ IWYU pragma:' 98 | CompactNamespaces: true 99 | ConstructorInitializerIndentWidth: 2 100 | ContinuationIndentWidth: 2 101 | Cpp11BracedListStyle: false 102 | DerivePointerAlignment: false 103 | DisableFormat: false 104 | EmptyLineAfterAccessModifier: Never 105 | EmptyLineBeforeAccessModifier: LogicalBlock 106 | ExperimentalAutoDetectBinPacking: false 107 | FixNamespaceComments: false 108 | ForEachMacros: 109 | - foreach 110 | - Q_FOREACH 111 | - BOOST_FOREACH 112 | IfMacros: 113 | - KJ_IF_MAYBE 114 | IncludeBlocks: Preserve 115 | IncludeCategories: 116 | - Regex: '^"(llvm|llvm-c|clang|clang-c)/' 117 | Priority: 2 118 | SortPriority: 0 119 | CaseSensitive: false 120 | - Regex: '^(<|"(gtest|gmock|isl|json)/)' 121 | Priority: 3 122 | SortPriority: 0 123 | CaseSensitive: false 124 | - Regex: '.*' 125 | Priority: 1 126 | SortPriority: 0 127 | CaseSensitive: false 128 | IncludeIsMainRegex: '(Test)?$' 129 | IncludeIsMainSourceRegex: '' 130 | IndentAccessModifiers: false 131 | IndentCaseBlocks: false 132 | IndentCaseLabels: true 133 | IndentExternBlock: AfterExternBlock 134 | IndentGotoLabels: true 135 | IndentPPDirectives: None 136 | IndentRequiresClause: true 137 | IndentWidth: 2 138 | IndentWrappedFunctionNames: false 139 | InsertBraces: false 140 | InsertNewlineAtEOF: false 141 | InsertTrailingCommas: None 142 | IntegerLiteralSeparator: 143 | Binary: 0 144 | BinaryMinDigits: 0 145 | Decimal: 0 146 | DecimalMinDigits: 0 147 | Hex: 0 148 | HexMinDigits: 0 149 | JavaScriptQuotes: Leave 150 | JavaScriptWrapImports: true 151 | KeepEmptyLinesAtTheStartOfBlocks: true 152 | KeepEmptyLinesAtEOF: false 153 | LambdaBodyIndentation: Signature 154 | LineEnding: DeriveLF 155 | MacroBlockBegin: '' 156 | MacroBlockEnd: '' 157 | MaxEmptyLinesToKeep: 1 158 | NamespaceIndentation: None 159 | ObjCBinPackProtocolList: Auto 160 | ObjCBlockIndentWidth: 2 161 | ObjCBreakBeforeNestedBlockParam: true 162 | ObjCSpaceAfterProperty: true 163 | ObjCSpaceBeforeProtocolList: false 164 | PackConstructorInitializers: BinPack 165 | PenaltyBreakAssignment: 2 166 | PenaltyBreakBeforeFirstCallParameter: 19 167 | PenaltyBreakComment: 300 168 | PenaltyBreakFirstLessLess: 120 169 | PenaltyBreakOpenParenthesis: 0 170 | PenaltyBreakScopeResolution: 500 171 | PenaltyBreakString: 1000 172 | PenaltyBreakTemplateDeclaration: 10 173 | PenaltyExcessCharacter: 1000000 174 | PenaltyIndentedWhitespace: 0 175 | PenaltyReturnTypeOnItsOwnLine: 200 176 | PointerAlignment: Left 177 | PPIndentWidth: -1 178 | QualifierAlignment: Leave 179 | ReferenceAlignment: Pointer 180 | ReflowComments: true 181 | RemoveBracesLLVM: false 182 | RemoveParentheses: Leave 183 | RemoveSemicolon: false 184 | RequiresClausePosition: OwnLine 185 | RequiresExpressionIndentation: OuterScope 186 | SeparateDefinitionBlocks: Leave 187 | ShortNamespaceLines: 1 188 | SkipMacroDefinitionBody: false 189 | SortIncludes: Never 190 | SortJavaStaticImport: Before 191 | SortUsingDeclarations: LexicographicNumeric 192 | SpaceAfterCStyleCast: false 193 | SpaceAfterLogicalNot: false 194 | SpaceAfterTemplateKeyword: false 195 | SpaceAroundPointerQualifiers: Default 196 | SpaceBeforeAssignmentOperators: true 197 | SpaceBeforeCaseColon: false 198 | SpaceBeforeCpp11BracedList: false 199 | SpaceBeforeCtorInitializerColon: true 200 | SpaceBeforeInheritanceColon: true 201 | SpaceBeforeJsonColon: false 202 | SpaceBeforeParens: ControlStatements 203 | SpaceBeforeParensOptions: 204 | AfterControlStatements: true 205 | AfterForeachMacros: true 206 | AfterFunctionDefinitionName: false 207 | AfterFunctionDeclarationName: false 208 | AfterIfMacros: true 209 | AfterOverloadedOperator: false 210 | AfterPlacementOperator: true 211 | AfterRequiresInClause: false 212 | AfterRequiresInExpression: false 213 | BeforeNonEmptyParentheses: false 214 | SpaceBeforeRangeBasedForLoopColon: true 215 | SpaceBeforeSquareBrackets: false 216 | SpaceInEmptyBlock: false 217 | SpacesBeforeTrailingComments: 1 218 | SpacesInAngles: Never 219 | SpacesInContainerLiterals: true 220 | SpacesInLineCommentPrefix: 221 | Minimum: 1 222 | Maximum: -1 223 | SpacesInParens: Never 224 | SpacesInParensOptions: 225 | InCStyleCasts: false 226 | InConditionalStatements: false 227 | InEmptyParentheses: false 228 | Other: false 229 | SpacesInSquareBrackets: false 230 | Standard: Latest 231 | StatementAttributeLikeMacros: 232 | - Q_EMIT 233 | StatementMacros: 234 | - Q_UNUSED 235 | - QT_REQUIRE_VERSION 236 | TabWidth: 8 237 | UseTab: Never 238 | VerilogBreakBetweenInstancePorts: true 239 | WhitespaceSensitiveMacros: 240 | - BOOST_PP_STRINGIZE 241 | - CF_SWIFT_NAME 242 | - NS_SWIFT_NAME 243 | - PP_STRINGIZE 244 | - STRINGIZE 245 | - ABF_FIELD 246 | - ABF_CONST_FIELD 247 | ... 248 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | .cache/ 3 | compile_commands.json 4 | a.out 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 SanyaNya 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Advanced-bitfield 2 | Library for complex bitfields, useful for things like x86 segment descriptor 3 | 4 | ## Requirements: C++11 5 | 6 | ## Installation: just include advanced_bitfield.hpp 7 | 8 | ## Features: 9 | * Header only 10 | * Constexpr 11 | * Fields with multiple unordered parts 12 | * Overlapped parts 13 | * Cast bitfield to and from integral 14 | 15 | ## Simple example 16 | ~~~cpp 17 | #include 18 | 19 | //Include library header 20 | #include "../advanced_bitfield.hpp" 21 | 22 | //First of all inherit from abf::bitfield 23 | class S : public abf::bitfield 24 | { 25 | using bf = abf::bitfield; //Convenient alias for base 26 | using bf::bf; //Using base constructors(default, from integral) 27 | 28 | public: 29 | //Declaring fields 30 | //Bits indexed from lsb to msb 31 | //ABF_FIELD(BASE_BITFIELD_CLASS, FIELD_TYPE, FIELD_NAME, PART0_BEGIN_BIT, PART0_END_BIT, [PART1_BEGIN_BIT, PART1_END_BIT]...) 32 | ABF_FIELD(bf, bool, f1, 0, 1 ) 33 | ABF_FIELD(bf, unsigned short, n1, 1, 17) 34 | ABF_FIELD(bf, unsigned short, n2, 17, 31) 35 | ABF_FIELD(bf, bool, f2, 31, 32) 36 | 37 | //If you want custom constructor: 38 | //ABF_CTOR(BASE_BITFIELD_CLASS, FIELD_NAME_b(VALUE)...) 39 | constexpr S(f1_b f1_, n1_b n1_, n2_b n2_, f2_b f2_) noexcept : 40 | ABF_CTOR(bf, f1_, n1_, n2_, f2_) {} 41 | }; 42 | 43 | //Using in constexpr 44 | constexpr S get_s() 45 | { 46 | return S(1, 0xffff, 0x1bcd, 1); 47 | } 48 | 49 | int main() 50 | { 51 | //To access fields(get/set) use OBJECT.FIELD_NAME() 52 | 53 | constexpr S s = get_s(); 54 | 55 | std::cout << std::hex 56 | << "s representation: " << static_cast(s) << "\n" 57 | << "s.f1() = " << s.f1() << "\n" 58 | << "s.n1() = " << s.n1() << "\n" 59 | << "s.n2() = " << s.n2() << "\n" 60 | << "s.f2() = " << s.f2() << "\n"; 61 | } 62 | ~~~ 63 | -------------------------------------------------------------------------------- /advanced_bitfield.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "helper_macro.hpp" 4 | 5 | namespace abf 6 | { 7 | 8 | template 9 | class bitfield 10 | { 11 | TAlias storage; 12 | 13 | friend TImpl; 14 | 15 | template 16 | struct init_storage 17 | { 18 | static constexpr TAlias f(TAlias s, Holder h, Tail... tail) noexcept 19 | { 20 | return init_storage::f(h.template set(s), tail...); 21 | } 22 | }; 23 | 24 | template 25 | struct init_storage 26 | { 27 | static constexpr TAlias f(TAlias s, Holder h) noexcept 28 | { 29 | return h.template set(s); 30 | } 31 | }; 32 | 33 | struct ctor_tag 34 | { 35 | }; 36 | 37 | template 38 | constexpr bitfield(ctor_tag, Holders... holders) noexcept 39 | : storage(init_storage::f(0, holders...)) 40 | { 41 | } 42 | 43 | public: 44 | using Alias = TAlias; 45 | 46 | constexpr bitfield() noexcept 47 | : storage(0) 48 | { 49 | static_assert( 50 | sizeof(TImpl) == sizeof(Alias), 51 | "Size of bitfield must be equal to size of Alias"); 52 | } 53 | 54 | constexpr bitfield(TAlias t) noexcept 55 | : storage(t) 56 | { 57 | static_assert( 58 | sizeof(TImpl) == sizeof(Alias), 59 | "Size of bitfield must be equal to size of Alias"); 60 | } 61 | 62 | CONSTEXPR_IF_NOT_CPP11 TImpl& operator=(Alias t) noexcept 63 | { 64 | static_assert( 65 | sizeof(TImpl) == sizeof(Alias), 66 | "Size of bitfield must be equal to size of Alias"); 67 | 68 | storage = t; 69 | return *static_cast(this); 70 | } 71 | 72 | constexpr operator Alias() const noexcept 73 | { 74 | static_assert( 75 | sizeof(TImpl) == sizeof(Alias), 76 | "Size of bitfield must be equal to size of Alias"); 77 | 78 | return storage; 79 | } 80 | }; 81 | 82 | } // namespace abf 83 | -------------------------------------------------------------------------------- /bit_ops.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // LSB - least significant bit 4 | // MSB - most significant bit 5 | // 6 | // Numbering goes from LSB to MSB 7 | // 8 | // MSB LSB 9 | // | | 10 | // Number - 0 1 0 1 0 1 1 0 11 | // Bit position - 7 6 5 4 3 2 1 0 12 | // 13 | // Shift left (<<) moves bits towards MSB 14 | // Shift right(>>) moves bits towards LSB 15 | 16 | #include 17 | #include 18 | 19 | namespace abf 20 | { 21 | namespace detail 22 | { 23 | 24 | // Get left mask starting from "begin" position 25 | // begin 26 | // | 27 | // Example - 11111000 28 | template 29 | constexpr T lmask(std::size_t begin) noexcept 30 | { 31 | return std::numeric_limits::max() << begin; 32 | } 33 | 34 | // Get right mask ending in "end" position 35 | // end 36 | // | 37 | // Example - 00000111 38 | template 39 | constexpr T rmask(std::size_t end) noexcept 40 | { 41 | return std::numeric_limits::max() >> 42 | (std::numeric_limits::digits - end); 43 | } 44 | 45 | // Combination of lmask and rmask 46 | // end begin 47 | // | | 48 | // Example 00111100 49 | template 50 | constexpr T lrmask(std::size_t begin, std::size_t end) noexcept 51 | { 52 | return lmask(begin) & rmask(end); 53 | } 54 | 55 | // Recursive merge of multiple masks 56 | template 57 | struct mask 58 | { 59 | static constexpr T f() noexcept 60 | { 61 | return lrmask(BEGIN, END) | mask::f(); 62 | } 63 | }; 64 | 65 | // End of recursion 66 | template 67 | struct mask 68 | { 69 | static constexpr T f() noexcept { return lrmask(BEGIN, END); } 70 | }; 71 | 72 | // Shift integer from "SRC_POS" to "DEST_POS" 73 | template 74 | constexpr T shift_to(T t) noexcept 75 | { 76 | return static_cast( 77 | SRC_POS < DEST_POS ? t << (DEST_POS - SRC_POS) : t >> (SRC_POS - DEST_POS)); 78 | } 79 | 80 | } 81 | } // namespace abf::detail 82 | -------------------------------------------------------------------------------- /bitcast.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Implementation of std::bit_cast with c++11 support(partially constexpr) 4 | 5 | #if __cplusplus >= 202002L 6 | #include 7 | #else 8 | #include 9 | #endif 10 | 11 | #include 12 | 13 | namespace abf 14 | { 15 | namespace detail 16 | { 17 | 18 | template 19 | struct impl_switch 20 | : std::integral_constant< 21 | bool, 22 | std::is_convertible::value || 23 | ((std::is_enum::value || std::is_integral::value) && 24 | (std::is_enum::value || std::is_integral::value))> 25 | { 26 | }; 27 | 28 | template 29 | #if __cplusplus >= 202002L 30 | constexpr 31 | #endif 32 | typename std::enable_if::value, To>::type 33 | bit_cast__(const From& from) noexcept 34 | { 35 | #if __cplusplus >= 202002L 36 | return std::bit_cast(from); 37 | #else 38 | static_assert( 39 | sizeof(To) == sizeof(From), "Size of To must be equal to size of From"); 40 | static_assert( 41 | std::is_trivially_copyable::value, "To must be trivially copyable"); 42 | static_assert( 43 | std::is_trivially_copyable::value, "From must be trivially copyable"); 44 | 45 | To to; 46 | std::memcpy(&to, &from, sizeof(To)); 47 | 48 | return to; 49 | #endif 50 | } 51 | 52 | template 53 | constexpr typename std::enable_if::value, To>::type 54 | bit_cast__(const From& from) noexcept 55 | { 56 | static_assert(noexcept(static_cast(from)), "Conversion must be noexcept"); 57 | 58 | return static_cast(from); 59 | } 60 | 61 | } 62 | } // namespace abf::detail 63 | -------------------------------------------------------------------------------- /examples/overlapping_parts.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "../advanced_bitfield.hpp" 3 | 4 | class S : public abf::bitfield 5 | { 6 | using bf = abf::bitfield; 7 | using bf::bf; 8 | 9 | public: 10 | ABF_FIELD(bf, std::uint16_t, base, 0, 16) 11 | ABF_FIELD(bf, std::uint8_t, offset1, 16, 24) 12 | ABF_FIELD(bf, std::uint8_t, offset2, 24, 32) 13 | 14 | ABF_CONST_FIELD(bf, 15 | std::uint32_t, addr1, 16, 24, //(base << 8) | offset1 16 | 0, 16) // 17 | 18 | ABF_CONST_FIELD(bf, 19 | std::uint32_t, addr2, 24, 32, //(base << 8) | offset2 20 | 0, 16) 21 | 22 | ABF_CONST_FIELD(bf, std::uint32_t, is_odd1, 16, 17); 23 | ABF_CONST_FIELD(bf, std::uint32_t, is_odd2, 24, 25); 24 | }; 25 | 26 | inline void print(S s) 27 | { 28 | std::cout << std::hex << "s:\n" 29 | << "s.base() = " << s.base() << "\n" 30 | << "s.offset1() = " << static_cast(s.offset1()) << "\n" 31 | << "s.offset2() = " << static_cast(s.offset2()) << "\n" 32 | << "s.addr1() = " << s.addr1() << "\n" 33 | << "s.addr2() = " << s.addr2() << "\n" 34 | << "s.is_odd1() = " << s.is_odd1() << "\n" 35 | << "s.is_odd2() = " << s.is_odd2() << "\n"; 36 | } 37 | 38 | int main() 39 | { 40 | S s; 41 | print(s); 42 | 43 | std::cout << "\nset s.base() = 0x8000\n"; 44 | s.base() = 0x8000; 45 | 46 | std::cout << "set s.offset1() = 0xa0\n"; 47 | s.offset1() = 0xa0; 48 | 49 | std::cout << "set s.offset2() = 0x41\n\n"; 50 | s.offset2() = 0x41; 51 | 52 | print(s); 53 | } 54 | -------------------------------------------------------------------------------- /examples/simple.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // Include library header 4 | #include "../advanced_bitfield.hpp" 5 | 6 | // First of all inherit from abf::bitfield 7 | class S : public abf::bitfield 8 | { 9 | using bf = abf::bitfield; // Convenient alias for base 10 | using bf::bf; // Using base constructors(default, from integral) 11 | 12 | public: 13 | // Declaring fields 14 | // Bits indexed from lsb to msb 15 | // ABF_FIELD(BASE_BITFIELD_CLASS, FIELD_TYPE, FIELD_NAME, PART0_BEGIN_BIT, 16 | // PART0_END_BIT, [PART1_BEGIN_BIT, PART1_END_BIT]...) 17 | ABF_FIELD(bf, bool, f1, 0, 1 ) 18 | ABF_FIELD(bf, unsigned short, n1, 1, 17) 19 | ABF_FIELD(bf, unsigned short, n2, 17, 31) 20 | ABF_FIELD(bf, bool, f2, 31, 32) 21 | 22 | // If you want custom constructor: 23 | // ABF_CTOR(BASE_BITFIELD_CLASS, FIELD_NAME_b(VALUE)...) 24 | constexpr S(f1_b f1_, n1_b n1_, n2_b n2_, f2_b f2_) noexcept 25 | : ABF_CTOR(bf, f1_, n1_, n2_, f2_) 26 | { 27 | } 28 | }; 29 | 30 | // Using in constexpr 31 | constexpr S get_s() 32 | { 33 | return S(1, 0xffff, 0x1bcd, 1); 34 | } 35 | 36 | int main() 37 | { 38 | // To access fields(get/set) use OBJECT.FIELD_NAME() 39 | 40 | constexpr S s = get_s(); 41 | 42 | std::cout << std::hex << "s representation: " << static_cast(s) 43 | << "\n" 44 | << "s.f1() = " << s.f1() << "\n" 45 | << "s.n1() = " << s.n1() << "\n" 46 | << "s.n2() = " << s.n2() << "\n" 47 | << "s.f2() = " << s.f2() << "\n"; 48 | } 49 | -------------------------------------------------------------------------------- /examples/x86_segment_descriptor.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "../advanced_bitfield.hpp" 4 | 5 | namespace x86 6 | { 7 | namespace Segment 8 | { 9 | 10 | enum class Type : std::uint8_t 11 | { 12 | Data, 13 | Code 14 | }; 15 | 16 | enum class Readable : std::uint8_t 17 | { 18 | No, 19 | Yes 20 | }; 21 | 22 | enum class Writable : std::uint8_t 23 | { 24 | No, 25 | Yes 26 | }; 27 | 28 | enum class ConformingPrivilege : std::uint8_t 29 | { 30 | Equal_Ring, 31 | EqualLess_Ring 32 | }; 33 | 34 | enum class Privilege : std::uint8_t 35 | { 36 | Kernel = 0, 37 | User = 3 38 | }; 39 | 40 | enum class Direction : std::uint8_t 41 | { 42 | Up, 43 | Down 44 | }; 45 | 46 | template 47 | struct Access; 48 | 49 | template<> 50 | class Access 51 | : public abf::bitfield, std::uint8_t> 52 | { 53 | using bf = abf::bitfield, std::uint8_t>; 54 | using bf::bf; 55 | 56 | public: 57 | ABF_FIELD(bf, bool, accessed, 0, 1) 58 | ABF_FIELD(bf, Readable, readable, 1, 2) 59 | ABF_FIELD(bf, ConformingPrivilege, conforming_privilege, 2, 3) 60 | ABF_FIELD(bf, Type, type, 3, 4) 61 | ABF_FIELD(bf, bool, always1, 4, 5) 62 | ABF_FIELD(bf, Privilege, privilege, 5, 7) 63 | ABF_FIELD(bf, bool, present, 7, 8) 64 | 65 | constexpr Access( 66 | readable_b read, 67 | conforming_privilege_b conf, 68 | privilege_b privil) noexcept 69 | : ABF_CTOR( 70 | bf, 71 | accessed_b(0), 72 | read, 73 | conf, 74 | type_b(Type::Code), 75 | always1_b(1), 76 | privil, 77 | present_b(1)) 78 | { 79 | } 80 | }; 81 | 82 | template<> 83 | class Access 84 | : public abf::bitfield, std::uint8_t> 85 | { 86 | using bf = abf::bitfield, std::uint8_t>; 87 | using bf::bf; 88 | 89 | public: 90 | ABF_FIELD(bf, bool, accessed, 0, 1) 91 | ABF_FIELD(bf, Writable, writable, 1, 2) 92 | ABF_FIELD(bf, Direction, direction, 2, 3) 93 | ABF_FIELD(bf, Type, type, 3, 4) 94 | ABF_FIELD(bf, bool, always1, 4, 5) 95 | ABF_FIELD(bf, Privilege, privilege, 5, 7) 96 | ABF_FIELD(bf, bool, present, 7, 8) 97 | 98 | constexpr Access( 99 | writable_b write, 100 | direction_b dir, 101 | privilege_b privil) noexcept 102 | : ABF_CTOR( 103 | bf, 104 | accessed_b(0), 105 | write, 106 | dir, 107 | type_b(Type::Data), 108 | always1_b(1), 109 | privil, 110 | present_b(1)) 111 | { 112 | } 113 | }; 114 | 115 | enum class Size : std::uint8_t 116 | { 117 | bit16, 118 | bit32 119 | }; 120 | 121 | enum class LimitGranularity : std::uint8_t 122 | { 123 | byte1, 124 | kib4 125 | }; 126 | 127 | class Flags : public abf::bitfield 128 | { 129 | using bf = abf::bitfield; 130 | using bf::bf; 131 | 132 | public: 133 | ABF_FIELD(bf, bool, avl, 0, 1) 134 | ABF_FIELD(bf, bool, longmode, 1, 2) 135 | ABF_FIELD(bf, Size, size, 2, 3) 136 | ABF_FIELD(bf, LimitGranularity, granularity, 3, 4) 137 | 138 | constexpr Flags(size_b s, granularity_b lg) noexcept 139 | : ABF_CTOR(bf, avl_b(0), longmode_b(0), s, lg) 140 | { 141 | } 142 | }; 143 | 144 | template 145 | class Descriptor : public abf::bitfield, std::uint64_t> 146 | { 147 | using bf = abf::bitfield, std::uint64_t>; 148 | using bf::bf; 149 | 150 | public: 151 | ABF_FIELD(bf, 152 | std::uint32_t, base, 16, 32, 153 | 32, 40, 154 | 56, 64) 155 | 156 | ABF_FIELD(bf, 157 | std::uint32_t, lim, 0, 16, 158 | 48, 52) 159 | 160 | ABF_FIELD(bf, 161 | Access, access, 40, 48) 162 | 163 | ABF_FIELD(bf, 164 | Flags, flags, 52, 56) 165 | 166 | constexpr Descriptor(base_b b, lim_b l, access_b a, flags_b f) noexcept 167 | : ABF_CTOR(bf, b, l, a, f) 168 | { 169 | } 170 | }; 171 | 172 | } 173 | } // namespace x86::Segment 174 | 175 | constexpr std::uint64_t gdt[]{ 176 | 0, 177 | x86::Segment::Descriptor{ 178 | 0x0, 179 | 0xffffffff, 180 | x86::Segment::Access{ 181 | x86::Segment::Readable::Yes, 182 | x86::Segment::ConformingPrivilege::Equal_Ring, 183 | x86::Segment::Privilege::Kernel }, 184 | x86::Segment::Flags{ x86::Segment::Size::bit32, 185 | x86::Segment::LimitGranularity::kib4 } }, 186 | 187 | x86::Segment::Descriptor{ 188 | 0x0, 189 | 0xffffffff, 190 | x86::Segment::Access{ 191 | x86::Segment::Writable::Yes, 192 | x86::Segment::Direction::Up, 193 | x86::Segment::Privilege::Kernel }, 194 | x86::Segment::Flags{ x86::Segment::Size::bit32, 195 | x86::Segment::LimitGranularity::kib4 } } 196 | }; 197 | 198 | int main() 199 | { 200 | std::cout << "Code segment descriptor representation: 0x" << std::setfill('0') 201 | << std::setw(16) << std::right << std::hex 202 | << static_cast(gdt[1]) << "\n" 203 | 204 | << "Data segment descriptor representation: 0x" << std::setfill('0') 205 | << std::setw(16) << std::right << std::hex 206 | << static_cast(gdt[2]) << "\n"; 207 | } 208 | -------------------------------------------------------------------------------- /field_ref.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // reference to field 4 | 5 | #include "storage_ops.hpp" 6 | #include "get_int_storage.hpp" 7 | #include "bitcast.hpp" 8 | 9 | #if __cplusplus > 201103L 10 | #define CONSTEXPR_IF_NOT_CPP11 constexpr 11 | #else 12 | #define CONSTEXPR_IF_NOT_CPP11 13 | #endif 14 | 15 | namespace abf 16 | { 17 | namespace detail 18 | { 19 | 20 | template 21 | class field_ref 22 | { 23 | static_assert(sizeof...(Parts) != 0, "Parts list must be non empty"); 24 | 25 | static_assert( 26 | sizeof...(Parts) % 2 == 0, 27 | "Every part must contain begin and end"); 28 | 29 | using TInt = get_int_storage_t; 30 | 31 | int_type& storage; 32 | 33 | public: 34 | constexpr field_ref(int_type& storage_) noexcept 35 | : storage(storage_) 36 | { 37 | } 38 | 39 | constexpr operator T() const noexcept 40 | { 41 | return bit_cast__( 42 | static_cast(get_from_storage(storage))); 43 | } 44 | 45 | CONSTEXPR_IF_NOT_CPP11 field_ref& operator=(T t) noexcept 46 | { 47 | storage = set_storage( 48 | storage, static_cast(bit_cast__(t))); 49 | 50 | return *this; 51 | } 52 | }; 53 | 54 | } 55 | } // namespace abf::detail 56 | -------------------------------------------------------------------------------- /get_int_storage.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Get int int_type with size corresponding to "T" type 4 | 5 | #include 6 | #include 7 | 8 | namespace abf 9 | { 10 | namespace detail 11 | { 12 | 13 | template 14 | struct get_int_storage 15 | { 16 | using type = typename std::conditional< 17 | sizeof(T) <= sizeof(std::uint8_t), 18 | std::uint8_t, 19 | typename std::conditional< 20 | sizeof(T) <= sizeof(std::uint16_t), 21 | std::uint16_t, 22 | typename std::conditional< 23 | sizeof(T) <= sizeof(std::uint32_t), 24 | std::uint32_t, 25 | typename std:: 26 | conditional:: 27 | type>::type>::type>::type; 28 | 29 | static_assert( 30 | !std::is_same::value, 31 | "sizeof(T) can`t be greater than sizeof(std::uint64_t)"); 32 | }; 33 | 34 | template 35 | using get_int_storage_t = typename get_int_storage::type; 36 | 37 | } 38 | } // namespace abf::detail 39 | -------------------------------------------------------------------------------- /helper_macro.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "field_ref.hpp" 4 | #include "holder.hpp" 5 | 6 | #define ABF_CONST_FIELD(BF, T, NAME, ...) \ 7 | static_assert( \ 8 | std::is_trivially_copyable::value, "T must be trivially copyable"); \ 9 | \ 10 | constexpr T NAME() const noexcept \ 11 | { \ 12 | return static_cast(abf::detail::field_ref< \ 13 | typename std::add_const::type, \ 14 | T, \ 15 | __VA_ARGS__>(this->BF::storage)); \ 16 | } 17 | 18 | #define ABF_FIELD(BF, T, NAME, ...) \ 19 | ABF_CONST_FIELD(BF, T, NAME, __VA_ARGS__) \ 20 | \ 21 | CONSTEXPR_IF_NOT_CPP11 \ 22 | abf::detail::field_ref NAME() noexcept \ 23 | { \ 24 | return abf::detail::field_ref( \ 25 | this->BF::storage); \ 26 | } \ 27 | \ 28 | using NAME##_b = abf::detail::holder; 29 | 30 | #define ABF_CTOR(BF, ...) BF(typename BF::ctor_tag{}, __VA_ARGS__) 31 | -------------------------------------------------------------------------------- /holder.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Type holding parts info of field 4 | // used in user defined constructors 5 | 6 | #include "storage_ops.hpp" 7 | #include "get_int_storage.hpp" 8 | #include "bitcast.hpp" 9 | 10 | namespace abf 11 | { 12 | namespace detail 13 | { 14 | 15 | template 16 | class holder 17 | { 18 | using TInt = get_int_storage_t; 19 | 20 | T value; 21 | 22 | public: 23 | constexpr holder(T v) noexcept 24 | : value(v) 25 | { 26 | } 27 | 28 | template 29 | constexpr int_type set(int_type storage) const noexcept 30 | { 31 | return set_storage( 32 | storage, static_cast(bit_cast__(value))); 33 | } 34 | }; 35 | 36 | } 37 | } // namespace abf::detail 38 | -------------------------------------------------------------------------------- /storage_ops.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Part - pair of begin and end 4 | // Field consists of multiple non-overlapping parts 5 | 6 | #include 7 | #include "bit_ops.hpp" 8 | 9 | namespace abf 10 | { 11 | namespace detail 12 | { 13 | 14 | // Write to storage 15 | // ================ 16 | 17 | // Writing of field to storage: 18 | // Writing gets done by sequentially reading parts from "src"(from LSB to MSB), 19 | // positioning them at "BEGIN" part parameter, 20 | // merging them all together 21 | // and setting the resulting value to storage 22 | 23 | // Get part from number 24 | // Example: 25 | // END BEGIN 26 | // | | 27 | // Number - 1010100110011101 28 | // Part - 0000100110000000 29 | template 30 | constexpr int_type get_part(int_type i) noexcept 31 | { 32 | return i & mask::f(); 33 | } 34 | 35 | // Get part from "src" starting at "OFFSET" and position it at "BEGIN" 36 | template< 37 | std::size_t OFFSET, 38 | std::size_t BEGIN, 39 | std::size_t END, 40 | typename int_type> 41 | constexpr int_type get_part_from_src(int_type src) noexcept 42 | { 43 | return get_part(shift_to(src)); 44 | } 45 | 46 | // Recursively merge all "src" parts 47 | template< 48 | std::size_t OFFSET, 49 | std::size_t BEGIN, 50 | std::size_t END, 51 | std::size_t... Tail> 52 | struct merge_parts_from_src 53 | { 54 | template 55 | static constexpr int_type f(int_type src) noexcept 56 | { 57 | return get_part_from_src(src) | 58 | merge_parts_from_src::f(src); 59 | } 60 | }; 61 | 62 | // merge_parts_from_src recursion end 63 | template 64 | struct merge_parts_from_src 65 | { 66 | template 67 | static constexpr int_type f(int_type src) noexcept 68 | { 69 | return get_part_from_src(src); 70 | } 71 | }; 72 | 73 | // Apply zero mask to parts of storage 74 | template 75 | constexpr int_type zero_storage_parts(int_type storage) noexcept 76 | { 77 | return storage & ~mask::f(); 78 | } 79 | 80 | // Writing field to storage 81 | template 82 | constexpr int_type set_storage(int_type storage, int_type src) noexcept 83 | { 84 | return zero_storage_parts(storage) | 85 | merge_parts_from_src<0, Parts...>::f(src); 86 | } 87 | 88 | // Read from storage 89 | // ================= 90 | 91 | // Reading of field from storage: 92 | // Reading gets done by getting parts from storage 93 | // and sequntially writing them(from LSB to MSB) to result 94 | 95 | // Get part from "storage" starting at "BEGIN" and position it at "OFFSET" 96 | template< 97 | std::size_t OFFSET, 98 | std::size_t BEGIN, 99 | std::size_t END, 100 | typename int_type> 101 | constexpr int_type get_part_from_storage(int_type storage) noexcept 102 | { 103 | return get_part( 104 | shift_to(storage)); 105 | } 106 | 107 | // Recursively merge all "storage" parts 108 | template< 109 | typename int_type, 110 | std::size_t OFFSET, 111 | std::size_t BEGIN, 112 | std::size_t END, 113 | std::size_t... Tail> 114 | struct merge_parts_from_storage 115 | { 116 | static constexpr int_type f(int_type storage) noexcept 117 | { 118 | return get_part_from_storage(storage) | 119 | merge_parts_from_storage:: 120 | f(storage); 121 | } 122 | }; 123 | 124 | // merge_parts_from_storage recursion end 125 | template< 126 | typename int_type, 127 | std::size_t OFFSET, 128 | std::size_t BEGIN, 129 | std::size_t END> 130 | struct merge_parts_from_storage 131 | { 132 | static constexpr int_type f(int_type storage) noexcept 133 | { 134 | return get_part_from_storage(storage); 135 | } 136 | }; 137 | 138 | // Reading field from storage 139 | template 140 | constexpr int_type get_from_storage(int_type storage) noexcept 141 | { 142 | return merge_parts_from_storage::f(storage); 143 | } 144 | 145 | } 146 | } // namespace abf::detail 147 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | project(advanced-bitfield-tests) 3 | 4 | include(FetchDoctest.cmake) 5 | 6 | add_executable(${PROJECT_NAME} tests_main.cpp 7 | tests/simple.cpp 8 | tests/overlapping_parts.cpp 9 | tests/x86_segment_descriptor.cpp) 10 | target_link_libraries(${PROJECT_NAME} PRIVATE doctest::doctest) 11 | target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/..) 12 | 13 | target_compile_options(${PROJECT_NAME} PRIVATE 14 | -Wall; 15 | -Wextra; 16 | -Wconversion; 17 | -Wsign-conversion; 18 | -pedantic; 19 | -O0; 20 | -g3; 21 | -fsanitize=address; 22 | -fsanitize=undefined) 23 | 24 | target_link_options(${PROJECT_NAME} PRIVATE 25 | -fsanitize=address; 26 | -fsanitize=undefined) 27 | 28 | add_custom_target(run_tests ALL 29 | DEPENDS ${PROJECT_NAME} 30 | COMMAND ${PROJECT_NAME} 31 | WORKING_DIRECTORY $) 32 | 33 | if(ENABLE_COVERAGE) 34 | target_compile_options(${PROJECT_NAME} PRIVATE -coverage) 35 | target_link_options(${PROJECT_NAME} PRIVATE -coverage) 36 | 37 | # find required tools 38 | find_program(LCOV lcov REQUIRED) 39 | find_program(GENHTML genhtml REQUIRED) 40 | 41 | # add coverage target 42 | add_custom_target(coverage ALL 43 | DEPENDS run_tests 44 | 45 | # gather data 46 | COMMAND ${LCOV} --directory . --capture --output-file coverage.info 47 | 48 | # generate report 49 | COMMAND ${GENHTML} --demangle-cpp -o coverage coverage.info 50 | 51 | WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) 52 | endif() 53 | -------------------------------------------------------------------------------- /tests/FetchDoctest.cmake: -------------------------------------------------------------------------------- 1 | 2 | include(FetchContent) 3 | 4 | # Fetch doctest 5 | FetchContent_Declare( 6 | doctest 7 | GIT_REPOSITORY https://github.com/doctest/doctest.git 8 | GIT_TAG "v2.4.11") 9 | 10 | # make the dependencies available to the build system 11 | FetchContent_MakeAvailable(doctest) 12 | -------------------------------------------------------------------------------- /tests/README.md: -------------------------------------------------------------------------------- 1 | ## Build and run tests: 2 | ```console 3 | $ cmake -DENABLE_COVERAGE=ON/OFF -S . -B build && cmake --build build 4 | ``` 5 | Coverage info placed in build/coverage 6 | -------------------------------------------------------------------------------- /tests/tests/overlapping_parts.cpp: -------------------------------------------------------------------------------- 1 | #include "doctest/doctest.h" 2 | #include "advanced_bitfield.hpp" 3 | 4 | class S : public abf::bitfield 5 | { 6 | using bf = abf::bitfield; 7 | using bf::bf; 8 | 9 | public: 10 | ABF_FIELD(bf, std::uint16_t, base, 0, 16) 11 | ABF_FIELD(bf, std::uint8_t, offset1, 16, 24) 12 | ABF_FIELD(bf, std::uint8_t, offset2, 24, 32) 13 | 14 | ABF_CONST_FIELD(bf, 15 | std::uint32_t, addr1, 16, 24, //(base << 8) | offset1 16 | 0, 16) // 17 | 18 | ABF_CONST_FIELD(bf, 19 | std::uint32_t, addr2, 24, 32, //(base << 8) | offset2 20 | 0, 16) 21 | 22 | ABF_CONST_FIELD(bf, std::uint32_t, is_odd1, 16, 17); 23 | ABF_CONST_FIELD(bf, std::uint32_t, is_odd2, 24, 25); 24 | }; 25 | 26 | TEST_CASE("Bitfield with overlapping parts") 27 | { 28 | S s; 29 | 30 | SUBCASE("Check init") 31 | { 32 | CHECK(static_cast(s) == 0); 33 | CHECK(s.base() == 0); 34 | CHECK(s.offset1() == 0); 35 | CHECK(s.offset2() == 0); 36 | CHECK(s.addr1() == 0); 37 | CHECK(s.addr2() == 0); 38 | CHECK(s.is_odd1() == 0); 39 | CHECK(s.is_odd2() == 0); 40 | } 41 | 42 | s.base() = 0x8000; 43 | s.offset1() = 0xa0; 44 | s.offset2() = 0x41; 45 | 46 | SUBCASE("Check after setting some") 47 | { 48 | CHECK(static_cast(s) == 0x41a08000); 49 | CHECK(s.base() == 0x8000); 50 | CHECK(s.offset1() == 0xa0); 51 | CHECK(s.offset2() == 0x41); 52 | CHECK(s.addr1() == 0x8000a0); 53 | CHECK(s.addr2() == 0x800041); 54 | CHECK(s.is_odd1() == 0); 55 | CHECK(s.is_odd2() == 1); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /tests/tests/simple.cpp: -------------------------------------------------------------------------------- 1 | #include "doctest/doctest.h" 2 | #include "advanced_bitfield.hpp" 3 | 4 | class S : public abf::bitfield 5 | { 6 | using bf = abf::bitfield; 7 | using bf::bf; 8 | 9 | public: 10 | ABF_FIELD(bf, bool, f1, 0, 1 ) 11 | ABF_FIELD(bf, unsigned short, n1, 1, 17) 12 | ABF_FIELD(bf, unsigned short, n2, 17, 31) 13 | ABF_FIELD(bf, bool, f2, 31, 32) 14 | 15 | constexpr S(f1_b f1_, n1_b n1_, n2_b n2_, f2_b f2_) noexcept 16 | : ABF_CTOR(bf, f1_, n1_, n2_, f2_) 17 | { 18 | } 19 | }; 20 | 21 | // Using in constexpr 22 | constexpr S get_s() 23 | { 24 | return S(1, 0xffff, 0x1bcd, 1); 25 | } 26 | 27 | TEST_CASE("Simple bitfield") 28 | { 29 | constexpr S s = get_s(); 30 | 31 | CHECK(static_cast(s) == 0xb79bffff); 32 | CHECK(s.f1() == 1); 33 | CHECK(s.n1() == 0xffff); 34 | CHECK(s.n2() == 0x1bcd); 35 | CHECK(s.f2() == 1); 36 | } 37 | -------------------------------------------------------------------------------- /tests/tests/x86_segment_descriptor.cpp: -------------------------------------------------------------------------------- 1 | #include "doctest/doctest.h" 2 | #include "advanced_bitfield.hpp" 3 | 4 | namespace x86 5 | { 6 | namespace Segment 7 | { 8 | 9 | enum class Type : std::uint8_t 10 | { 11 | Data, 12 | Code 13 | }; 14 | 15 | enum class Readable : std::uint8_t 16 | { 17 | No, 18 | Yes 19 | }; 20 | 21 | enum class Writable : std::uint8_t 22 | { 23 | No, 24 | Yes 25 | }; 26 | 27 | enum class ConformingPrivilege : std::uint8_t 28 | { 29 | Equal_Ring, 30 | EqualLess_Ring 31 | }; 32 | 33 | enum class Privilege : std::uint8_t 34 | { 35 | Kernel = 0, 36 | User = 3 37 | }; 38 | 39 | enum class Direction : std::uint8_t 40 | { 41 | Up, 42 | Down 43 | }; 44 | 45 | template 46 | struct Access; 47 | 48 | template<> 49 | class Access 50 | : public abf::bitfield, std::uint8_t> 51 | { 52 | using bf = abf::bitfield, std::uint8_t>; 53 | using bf::bf; 54 | 55 | public: 56 | ABF_FIELD(bf, bool, accessed, 0, 1) 57 | ABF_FIELD(bf, Readable, readable, 1, 2) 58 | ABF_FIELD(bf, ConformingPrivilege, conforming_privilege, 2, 3) 59 | ABF_FIELD(bf, Type, type, 3, 4) 60 | ABF_FIELD(bf, bool, always1, 4, 5) 61 | ABF_FIELD(bf, Privilege, privilege, 5, 7) 62 | ABF_FIELD(bf, bool, present, 7, 8) 63 | 64 | constexpr Access( 65 | readable_b read, 66 | conforming_privilege_b conf, 67 | privilege_b privil) noexcept 68 | : ABF_CTOR( 69 | bf, 70 | accessed_b(0), 71 | read, 72 | conf, 73 | type_b(Type::Code), 74 | always1_b(1), 75 | privil, 76 | present_b(1)) 77 | { 78 | } 79 | }; 80 | 81 | template<> 82 | class Access 83 | : public abf::bitfield, std::uint8_t> 84 | { 85 | using bf = abf::bitfield, std::uint8_t>; 86 | using bf::bf; 87 | 88 | public: 89 | ABF_FIELD(bf, bool, accessed, 0, 1) 90 | ABF_FIELD(bf, Writable, writable, 1, 2) 91 | ABF_FIELD(bf, Direction, direction, 2, 3) 92 | ABF_FIELD(bf, Type, type, 3, 4) 93 | ABF_FIELD(bf, bool, always1, 4, 5) 94 | ABF_FIELD(bf, Privilege, privilege, 5, 7) 95 | ABF_FIELD(bf, bool, present, 7, 8) 96 | 97 | constexpr Access( 98 | writable_b write, 99 | direction_b dir, 100 | privilege_b privil) noexcept 101 | : ABF_CTOR( 102 | bf, 103 | accessed_b(0), 104 | write, 105 | dir, 106 | type_b(Type::Data), 107 | always1_b(1), 108 | privil, 109 | present_b(1)) 110 | { 111 | } 112 | }; 113 | 114 | enum class Size : std::uint8_t 115 | { 116 | bit16, 117 | bit32 118 | }; 119 | 120 | enum class LimitGranularity : std::uint8_t 121 | { 122 | byte1, 123 | kib4 124 | }; 125 | 126 | class Flags : public abf::bitfield 127 | { 128 | using bf = abf::bitfield; 129 | using bf::bf; 130 | 131 | public: 132 | ABF_FIELD(bf, bool, avl, 0, 1) 133 | ABF_FIELD(bf, bool, longmode, 1, 2) 134 | ABF_FIELD(bf, Size, size, 2, 3) 135 | ABF_FIELD(bf, LimitGranularity, granularity, 3, 4) 136 | 137 | constexpr Flags(size_b s, granularity_b lg) noexcept 138 | : ABF_CTOR(bf, avl_b(0), longmode_b(0), s, lg) 139 | { 140 | } 141 | }; 142 | 143 | template 144 | class Descriptor : public abf::bitfield, std::uint64_t> 145 | { 146 | using bf = abf::bitfield, std::uint64_t>; 147 | using bf::bf; 148 | 149 | public: 150 | ABF_FIELD(bf, 151 | std::uint32_t, base, 16, 32, 152 | 32, 40, 153 | 56, 64) 154 | 155 | ABF_FIELD(bf, 156 | std::uint32_t, lim, 0, 16, 157 | 48, 52) 158 | 159 | ABF_FIELD(bf, 160 | Access, access, 40, 48) 161 | 162 | ABF_FIELD(bf, 163 | Flags, flags, 52, 56) 164 | 165 | constexpr Descriptor(base_b b, lim_b l, access_b a, flags_b f) noexcept 166 | : ABF_CTOR(bf, b, l, a, f) 167 | { 168 | } 169 | }; 170 | 171 | } 172 | } // namespace x86::Segment 173 | 174 | constexpr std::uint64_t gdt[]{ 175 | 0, 176 | x86::Segment::Descriptor{ 177 | 0x0, 178 | 0xffffffff, 179 | x86::Segment::Access{ 180 | x86::Segment::Readable::Yes, 181 | x86::Segment::ConformingPrivilege::Equal_Ring, 182 | x86::Segment::Privilege::Kernel }, 183 | x86::Segment::Flags{ x86::Segment::Size::bit32, 184 | x86::Segment::LimitGranularity::kib4 } }, 185 | 186 | x86::Segment::Descriptor{ 187 | 0x0, 188 | 0xffffffff, 189 | x86::Segment::Access{ 190 | x86::Segment::Writable::Yes, 191 | x86::Segment::Direction::Up, 192 | x86::Segment::Privilege::Kernel }, 193 | x86::Segment::Flags{ x86::Segment::Size::bit32, 194 | x86::Segment::LimitGranularity::kib4 } } 195 | }; 196 | 197 | TEST_CASE("x86 Segment descriptor") 198 | { 199 | CHECK(gdt[1] == 0x00cf9a000000ffff); 200 | CHECK(gdt[2] == 0x00cf92000000ffff); 201 | } 202 | -------------------------------------------------------------------------------- /tests/tests_main.cpp: -------------------------------------------------------------------------------- 1 | #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN 2 | #include "doctest/doctest.h" 3 | --------------------------------------------------------------------------------