// This file is part of the mcl project. // Copyright (c) 2022 merryhime // SPDX-License-Identifier: MIT #pragma once #include "mcl/assert.hpp" #include "mcl/bitsizeof.hpp" #include "mcl/concepts/bit_integral.hpp" #include "mcl/stdint.hpp" namespace mcl::bit { /// Create a mask with `count` number of one bits. template constexpr T ones() { static_assert(count <= bitsizeof, "count larger than bitsize of T"); if constexpr (count == 0) { return 0; } else { return static_cast(~static_cast(0)) >> (bitsizeof - count); } } /// Create a mask with `count` number of one bits. template constexpr T ones(size_t count) { ASSERT_MSG(count <= bitsizeof, "count larger than bitsize of T"); if (count == 0) { return 0; } return static_cast(~static_cast(0)) >> (bitsizeof - count); } /// Create a mask of type T for bits [begin_bit, end_bit] inclusive. template constexpr T mask() { static_assert(begin_bit <= end_bit, "invalid bit range (position of beginning bit cannot be greater than that of end bit)"); static_assert(begin_bit < bitsizeof, "begin_bit must be smaller than size of T"); static_assert(end_bit < bitsizeof, "end_bit must be smaller than size of T"); return ones() << begin_bit; } /// Create a mask of type T for bits [begin_bit, end_bit] inclusive. template constexpr T mask(size_t begin_bit, size_t end_bit) { ASSERT_MSG(begin_bit <= end_bit, "invalid bit range (position of beginning bit cannot be greater than that of end bit)"); ASSERT_MSG(begin_bit < bitsizeof, "begin_bit must be smaller than size of T"); ASSERT_MSG(end_bit < bitsizeof, "end_bit must be smaller than size of T"); return ones(end_bit - begin_bit + 1) << begin_bit; } /// Extract bits [begin_bit, end_bit] inclusive from value of type T. template constexpr T get_bits(T value) { constexpr T m = mask(); return (value & m) >> begin_bit; } /// Extract bits [begin_bit, end_bit] inclusive from value of type T. template constexpr T get_bits(size_t begin_bit, size_t end_bit, T value) { const T m = mask(begin_bit, end_bit); return (value & m) >> begin_bit; } /// Clears bits [begin_bit, end_bit] inclusive of value of type T. template constexpr T clear_bits(T value) { constexpr T m = mask(); return value & ~m; } /// Clears bits [begin_bit, end_bit] inclusive of value of type T. template constexpr T clear_bits(size_t begin_bit, size_t end_bit, T value) { const T m = mask(begin_bit, end_bit); return value & ~m; } /// Modifies bits [begin_bit, end_bit] inclusive of value of type T. template constexpr T set_bits(T value, T new_bits) { constexpr T m = mask(); return (value & ~m) | ((new_bits << begin_bit) & m); } /// Modifies bits [begin_bit, end_bit] inclusive of value of type T. template constexpr T set_bits(size_t begin_bit, size_t end_bit, T value, T new_bits) { const T m = mask(begin_bit, end_bit); return (value & ~m) | ((new_bits << begin_bit) & m); } /// Extract bit at bit_position from value of type T. template constexpr bool get_bit(T value) { constexpr T m = mask(); return (value & m) != 0; } /// Extract bit at bit_position from value of type T. template constexpr bool get_bit(size_t bit_position, T value) { const T m = mask(bit_position, bit_position); return (value & m) != 0; } /// Clears bit at bit_position of value of type T. template constexpr T clear_bit(T value) { constexpr T m = mask(); return value & ~m; } /// Clears bit at bit_position of value of type T. template constexpr T clear_bit(size_t bit_position, T value) { const T m = mask(bit_position, bit_position); return value & ~m; } /// Modifies bit at bit_position of value of type T. template constexpr T set_bit(T value, bool new_bit) { constexpr T m = mask(); return (value & ~m) | (new_bit ? m : static_cast(0)); } /// Modifies bit at bit_position of value of type T. template constexpr T set_bit(size_t bit_position, T value, bool new_bit) { const T m = mask(bit_position, bit_position); return (value & ~m) | (new_bit ? m : static_cast(0)); } /// Sign-extends a value that has bit_count bits to the full bitwidth of type T. template constexpr T sign_extend(T value) { static_assert(bit_count != 0, "cannot sign-extend zero-sized value"); using S = std::make_signed_t; constexpr size_t shift_amount = bitsizeof - bit_count; return static_cast(static_cast(value << shift_amount) >> shift_amount); } /// Sign-extends a value that has bit_count bits to the full bitwidth of type T. template constexpr T sign_extend(size_t bit_count, T value) { ASSERT_MSG(bit_count != 0, "cannot sign-extend zero-sized value"); using S = std::make_signed_t; const size_t shift_amount = bitsizeof - bit_count; return static_cast(static_cast(value << shift_amount) >> shift_amount); } /// Replicate an element across a value of type T. template constexpr T replicate_element(T value) { static_assert(element_size <= bitsizeof, "element_size is too large"); static_assert(bitsizeof % element_size == 0, "bitsize of T not divisible by element_size"); if constexpr (element_size == bitsizeof) { return value; } else { return replicate_element(static_cast(value | (value << element_size))); } } /// Replicate an element of type U across a value of type T. template constexpr T replicate_element(T value) { static_assert(bitsizeof <= bitsizeof, "element_size is too large"); return replicate_element, T>(value); } /// Replicate an element across a value of type T. template constexpr T replicate_element(size_t element_size, T value) { ASSERT_MSG(element_size <= bitsizeof, "element_size is too large"); ASSERT_MSG(bitsizeof % element_size == 0, "bitsize of T not divisible by element_size"); if (element_size == bitsizeof) { return value; } return replicate_element(element_size * 2, static_cast(value | (value << element_size))); } template constexpr bool most_significant_bit(T value) { return get_bit - 1, T>(value); } } // namespace mcl::bit