diff options
author | kcgen <kcgen@users.noreply.github.com> | 2022-01-24 05:52:44 +0300 |
---|---|---|
committer | kcgen <1557255+kcgen@users.noreply.github.com> | 2022-02-02 01:32:14 +0300 |
commit | 6f764a9872c5a38a86099e2b87cd4cad2641f522 (patch) | |
tree | b41950fa52c41df83772beb2c3e1e1c9e1309c80 /tests | |
parent | 1e50f8a058ba4aff6a57483f976fdbb8e0096379 (diff) |
Add bit-operations helper functions
Bit operations can often be done in different ways:
- one based bits? (not recommended, industy uses zero-based)
- zero based bits? (recommended, but many unaware of this)
- hex numbers? (common, but hard to read for multi-bits)
- binary-literals? (gets massive and unreadable for larger
bit depth: so then need to pick a different
style leading to inconsistencies)
- decimals? (confusion with counter and purpose)
- on-the-fly conversion (1 << 4) (OK, but visually messy)
this attempts to standardize these operations and also make
the code more self-documenting.
The functions are "const-correct", and all can be evaluated
at compile-time when literals are used.
Diffstat (limited to 'tests')
-rw-r--r-- | tests/bitops_tests.cpp | 282 | ||||
-rw-r--r-- | tests/meson.build | 1 | ||||
-rw-r--r-- | tests/vs/tests.vcxproj | 3 | ||||
-rw-r--r-- | tests/vs/tests.vcxproj.filters | 5 |
4 files changed, 289 insertions, 2 deletions
diff --git a/tests/bitops_tests.cpp b/tests/bitops_tests.cpp new file mode 100644 index 000000000..b5a44d73a --- /dev/null +++ b/tests/bitops_tests.cpp @@ -0,0 +1,282 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * + * Copyright (C) 2021-2021 The DOSBox Staging Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "bitops.h" + +#include <gtest/gtest.h> + +namespace { + +TEST(bitops, enum_vals) +{ + // check against bit-shifts + EXPECT_FALSE(bit_0 == 0 << 0); + EXPECT_TRUE(bit_0 == 1 << 0); // this is why + EXPECT_FALSE(bit_0 == 1 << 1); + + EXPECT_FALSE(bit_5 == 1 << 4); + EXPECT_TRUE(bit_5 == 1 << 5); // industry prefers + EXPECT_FALSE(bit_5 == 1 << 6); + + EXPECT_FALSE(bit_12 == 1 << 11); + EXPECT_TRUE(bit_12 == 1 << 12); // zero-based bit names + EXPECT_FALSE(bit_12 == 1 << 13); + + EXPECT_FALSE(bit_22 == 1 << 21); + EXPECT_TRUE(bit_22 == 1 << 22); // and not one-based + EXPECT_FALSE(bit_22 == 1 << 23); + + EXPECT_FALSE(bit_31 == 1 << 30); + EXPECT_TRUE(bit_31 == 1 << 31); + + // check against bit literals + EXPECT_TRUE(bit_0 == 0b1); + EXPECT_TRUE(bit_5 == 0b100000); + EXPECT_TRUE(bit_12 == 0b1000000000000); + EXPECT_TRUE(bit_22 == 0b10000000000000000000000); + EXPECT_TRUE(static_cast<uint32_t>(bit_14 | bit_22 | bit_31) == + 0b10000000010000000100000000000000); + + // check against magic numbers + EXPECT_TRUE(bit_0 == 1); + EXPECT_TRUE(bit_5 == 32); + EXPECT_TRUE(bit_12 == 4096); + EXPECT_TRUE(bit_22 == 4194304); + EXPECT_TRUE(static_cast<uint32_t>(bit_14 | bit_22 | bit_31) == 2151694336); + + // check some combos + EXPECT_FALSE((bit_4 | bit_5) == 0b1'1000); + EXPECT_TRUE((bit_4 | bit_5) == 0b11'0000); + EXPECT_FALSE((bit_4 | bit_5) == 0b110'0000); +} + +TEST(bitops, nominal_byte) +{ + const auto even_bits = bit_0 | bit_2 | bit_4 | bit_6; + const auto odd_bits = bit_1 | bit_3 | bit_5 | bit_7; + + uint8_t reg = 0; + set_bits(reg, odd_bits); + EXPECT_EQ(reg, 0b10101010); + + set_bits(reg, even_bits); + EXPECT_EQ(reg, 0b11111111); + + EXPECT_TRUE(are_bits_set(reg, bit_0)); + EXPECT_TRUE(are_bits_set(reg, bit_3)); + EXPECT_TRUE(are_bits_set(reg, bit_7)); + EXPECT_TRUE(are_bits_set(reg, even_bits)); + EXPECT_TRUE(are_bits_set(reg, odd_bits)); + + EXPECT_FALSE(are_bits_cleared(reg, bit_0)); + EXPECT_FALSE(are_bits_cleared(reg, bit_3)); + EXPECT_FALSE(are_bits_cleared(reg, bit_7)); + EXPECT_FALSE(are_bits_cleared(reg, even_bits)); + EXPECT_FALSE(are_bits_cleared(reg, odd_bits)); + + clear_bits(reg, odd_bits); // odd-off, even-on + EXPECT_EQ(reg, even_bits); + EXPECT_TRUE(are_bits_set(reg, even_bits)); + EXPECT_TRUE(are_bits_cleared(reg, odd_bits)); + + toggle_bits(reg, odd_bits); // both are on + EXPECT_TRUE(are_bits_set(reg, (odd_bits | even_bits))); + EXPECT_FALSE(are_bits_cleared(reg, (odd_bits | even_bits))); + + toggle_bits(reg, even_bits); // odd-on, even-off + EXPECT_TRUE(are_bits_set(reg, odd_bits)); + EXPECT_TRUE(are_bits_cleared(reg, even_bits)); + + toggle_bits(reg, even_bits | odd_bits); // odd-off, even-on + EXPECT_EQ(reg, even_bits); + + // set all bits + set_all_bits(reg); + EXPECT_EQ(reg, 0b1111'1111); + EXPECT_TRUE(are_bits_set(reg, even_bits | odd_bits)); + EXPECT_TRUE(are_any_bits_set(reg, even_bits | odd_bits)); + EXPECT_FALSE(are_bits_cleared(reg, (odd_bits | even_bits))); + + // toggle all bits + toggle_all_bits(reg); + EXPECT_EQ(reg, 0b0000'0000); + EXPECT_FALSE(are_bits_set(reg, even_bits | odd_bits)); + EXPECT_FALSE(are_any_bits_set(reg, even_bits | odd_bits)); + EXPECT_TRUE(are_bits_cleared(reg, (odd_bits | even_bits))); +} + +TEST(bitops, nominal_word) +{ + const auto even_bits = bit_8 | bit_10 | bit_12 | bit_14; + const auto odd_bits = bit_9 | bit_11 | bit_13 | bit_15; + + uint16_t reg = 0; + set_bits(reg, odd_bits); + EXPECT_EQ(reg, 0b10101010'00000000); + + set_bits(reg, even_bits); + EXPECT_EQ(reg, 0b11111111'00000000); + + EXPECT_FALSE(are_bits_cleared(reg, bit_8)); + EXPECT_FALSE(are_bits_cleared(reg, bit_12)); + EXPECT_FALSE(are_bits_cleared(reg, bit_15)); + EXPECT_FALSE(are_bits_cleared(reg, even_bits)); + EXPECT_FALSE(are_bits_cleared(reg, odd_bits)); + + clear_bits(reg, odd_bits); // odd-off, even-on + EXPECT_EQ(reg, even_bits); + EXPECT_TRUE(are_bits_set(reg, even_bits)); + EXPECT_TRUE(are_any_bits_set(reg, even_bits | odd_bits)); + EXPECT_TRUE(are_bits_cleared(reg, odd_bits)); + + toggle_bits(reg, odd_bits); // both are on + EXPECT_TRUE(are_bits_set(reg, (odd_bits | even_bits))); + EXPECT_FALSE(are_bits_cleared(reg, (odd_bits | even_bits))); + + toggle_bits(reg, even_bits); // odd-on, even-off + EXPECT_TRUE(are_bits_set(reg, odd_bits)); + EXPECT_TRUE(are_bits_cleared(reg, even_bits)); + + toggle_bits(reg, even_bits | odd_bits); // odd-off, even-on + EXPECT_EQ(reg, even_bits); + + // set all bits + set_all_bits(reg); + EXPECT_EQ(reg, 0b11111111'11111111); + EXPECT_TRUE(are_bits_set(reg, even_bits | odd_bits)); + EXPECT_TRUE(are_any_bits_set(reg, even_bits | odd_bits)); + EXPECT_FALSE(are_bits_cleared(reg, (odd_bits | even_bits))); + + // toggle all bits + toggle_all_bits(reg); + EXPECT_EQ(reg, 0b00000000'00000000); + EXPECT_FALSE(are_bits_set(reg, even_bits | odd_bits)); + EXPECT_FALSE(are_any_bits_set(reg, even_bits | odd_bits)); + EXPECT_TRUE(are_bits_cleared(reg, (odd_bits | even_bits))); +} + +TEST(bitops, nominal_dword) +{ + const auto even_bits = bit_16 | bit_18 | bit_20 | bit_22 | bit_24 | + bit_26 | bit_28 | bit_30; + const auto odd_bits = bit_17 | bit_19 | bit_21 | bit_23 | bit_25 | + bit_27 | bit_29 | bit_31; + + uint32_t reg = 0; + + set_bits(reg, even_bits); + EXPECT_EQ(reg, 0b01010101'01010101'00000000'00000000); + + set_bits(reg, odd_bits); + EXPECT_EQ(reg, 0b11111111'11111111'00000000'00000000); + + EXPECT_FALSE(are_bits_cleared(reg, bit_16)); + EXPECT_FALSE(are_bits_cleared(reg, bit_24)); + EXPECT_FALSE(are_bits_cleared(reg, bit_31)); + EXPECT_FALSE(are_bits_cleared(reg, even_bits)); + EXPECT_FALSE(are_bits_cleared(reg, odd_bits)); + + clear_bits(reg, odd_bits); // odd-off, even-on + EXPECT_EQ(reg, even_bits); + EXPECT_TRUE(are_bits_set(reg, even_bits)); + EXPECT_TRUE(are_bits_cleared(reg, odd_bits)); + + toggle_bits(reg, odd_bits); // both are on + EXPECT_TRUE(are_bits_set(reg, (odd_bits | even_bits))); + EXPECT_FALSE(are_bits_cleared(reg, (odd_bits | even_bits))); + + toggle_bits(reg, even_bits); // odd-on, even-off + EXPECT_TRUE(are_bits_set(reg, odd_bits)); + EXPECT_TRUE(are_bits_cleared(reg, even_bits)); + + toggle_bits(reg, even_bits | odd_bits); // odd-off, even-on + EXPECT_EQ(reg, even_bits); + + // set all bits + set_all_bits(reg); + EXPECT_EQ(reg, 0b11111111'11111111'11111111'11111111); + EXPECT_TRUE(are_bits_set(reg, even_bits | odd_bits)); + EXPECT_TRUE(are_any_bits_set(reg, even_bits | odd_bits)); + EXPECT_FALSE(are_bits_cleared(reg, (odd_bits | even_bits))); + + // toggle all bits + toggle_all_bits(reg); + EXPECT_EQ(reg, 0b00000000'00000000'00000000'00000000); + EXPECT_FALSE(are_bits_set(reg, even_bits | odd_bits)); + EXPECT_FALSE(are_any_bits_set(reg, even_bits | odd_bits)); + EXPECT_TRUE(are_bits_cleared(reg, (odd_bits | even_bits))); +} + +TEST(bitops, bits_too_wide_for_byte) +{ + uint8_t reg = 0; + set_bits(reg, bit_7); + EXPECT_TRUE(are_bits_set(reg, bit_7)); + EXPECT_DEBUG_DEATH({ set_bits(reg, bit_8); }, ""); + EXPECT_DEBUG_DEATH({ are_bits_set(reg, bit_8); }, ""); + + clear_bits(reg, bit_7); + EXPECT_TRUE(are_bits_cleared(reg, bit_7)); + EXPECT_DEBUG_DEATH({ clear_bits(reg, bit_8); }, ""); + EXPECT_DEBUG_DEATH({ are_bits_cleared(reg, bit_8); }, ""); + + toggle_bits(reg, bit_7); + EXPECT_TRUE(are_bits_set(reg, bit_7)); + EXPECT_DEBUG_DEATH({ toggle_bits(reg, bit_8); }, ""); +} + +TEST(bitops, bits_too_wide_for_word) +{ + uint16_t reg = 0; + set_bits(reg, bit_8); + EXPECT_TRUE(are_bits_set(reg, bit_8)); + EXPECT_DEBUG_DEATH({ set_bits(reg, bit_16); }, ""); + EXPECT_DEBUG_DEATH({ are_bits_set(reg, bit_16); }, ""); + + clear_bits(reg, bit_8); + EXPECT_TRUE(are_bits_cleared(reg, bit_8)); + EXPECT_DEBUG_DEATH({ clear_bits(reg, bit_16); }, ""); + EXPECT_DEBUG_DEATH({ are_bits_cleared(reg, bit_16); }, ""); + + toggle_bits(reg, bit_8); + EXPECT_TRUE(are_bits_set(reg, bit_8)); + EXPECT_DEBUG_DEATH({ toggle_bits(reg, bit_16); }, ""); +} + +TEST(bitops, bits_not_too_wide_for_dword) +{ + uint32_t reg = 0; + set_bits(reg, bit_8); + set_bits(reg, bit_24); + set_bits(reg, bit_31); + EXPECT_TRUE(are_bits_set(reg, (bit_8 | bit_24 | bit_31))); + + clear_bits(reg, bit_8); + clear_bits(reg, bit_24); + clear_bits(reg, bit_31); + EXPECT_TRUE(are_bits_cleared(reg, (bit_8 | bit_24 | bit_31))); + + toggle_bits(reg, bit_8); + toggle_bits(reg, bit_24); + toggle_bits(reg, bit_31); + EXPECT_TRUE(are_bits_set(reg, (bit_8 | bit_24 | bit_31))); +} + +} // namespace diff --git a/tests/meson.build b/tests/meson.build index 2dfea0407..287d04257 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -59,6 +59,7 @@ test('gtest fs_utils', fs_utils, # other unit tests unit_tests = [ + {'name' : 'bitops', 'deps' : []}, {'name' : 'iohandler_containers', 'deps' : [libmisc_dep]}, {'name' : 'rwqueue', 'deps' : [libmisc_dep]}, {'name' : 'soft_limiter', 'deps' : [atomic_dep, libmisc_dep]}, diff --git a/tests/vs/tests.vcxproj b/tests/vs/tests.vcxproj index 3fca85bb9..2f2f9866b 100644 --- a/tests/vs/tests.vcxproj +++ b/tests/vs/tests.vcxproj @@ -232,6 +232,7 @@ $(TargetPath) <ClCompile Include="..\..\src\libs\loguru\loguru.cpp" /> <ClCompile Include="..\..\src\libs\whereami\whereami.c" /> <ClCompile Include="..\ansi_code_markup_tests.cpp" /> + <ClCompile Include="..\bitops_tests.cpp" /> <ClCompile Include="..\fs_utils_tests.cpp" /> <ClCompile Include="..\iohandler_containers_tests.cpp" /> <ClCompile Include="..\rwqueue_tests.cpp" /> @@ -247,4 +248,4 @@ $(TargetPath) <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <ImportGroup Label="ExtensionTargets"> </ImportGroup> -</Project>
\ No newline at end of file +</Project> diff --git a/tests/vs/tests.vcxproj.filters b/tests/vs/tests.vcxproj.filters index 4dcfe1b46..d0dab98d1 100644 --- a/tests/vs/tests.vcxproj.filters +++ b/tests/vs/tests.vcxproj.filters @@ -18,6 +18,9 @@ </Filter> </ItemGroup> <ItemGroup> + <ClCompile Include="..\bitops_tests.cpp"> + <Filter>tests</Filter> + </ClCompile> <ClCompile Include="..\fs_utils_tests.cpp"> <Filter>tests</Filter> </ClCompile> @@ -82,4 +85,4 @@ <ItemGroup> <None Include="..\meson.build" /> </ItemGroup> -</Project>
\ No newline at end of file +</Project> |