Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions doc/modules/ROOT/nav.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
** xref:examples.adoc#examples_charconv[Character Conversion]
** xref:examples.adoc#examples_fmt_format[Formatting]
** xref:examples.adoc#examples_iostream[Stream I/O]
** xref:examples.adoc#examples_bit[Bit Manipulation]
* xref:api_reference.adoc[]
** xref:api_reference.adoc#api_types[Types]
** xref:api_reference.adoc#api_functions[Functions]
Expand All @@ -23,4 +24,5 @@
* xref:format.adoc[]
* xref:iostream.adoc[]
* xref:charconv.adoc[]
* xref:bit.adoc[]
* xref:reference.adoc[]
46 changes: 46 additions & 0 deletions doc/modules/ROOT/pages/api_reference.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,49 @@ https://www.boost.org/LICENSE_1_0.txt
| Stream insertion and extraction for all safe integer types
|===

=== Bit Manipulation

[cols="1,2", options="header"]
|===
| Function | Description

| xref:bit.adoc[`has_single_bit`]
| Returns `true` if the value is a power of two

| xref:bit.adoc[`bit_ceil`]
| Returns the smallest power of two not less than the value

| xref:bit.adoc[`bit_floor`]
| Returns the largest power of two not greater than the value

| xref:bit.adoc[`bit_width`]
| Returns the number of bits needed to represent the value

| xref:bit.adoc[`rotl`]
| Bitwise left rotation

| xref:bit.adoc[`rotr`]
| Bitwise right rotation

| xref:bit.adoc[`countl_zero`]
| Counts consecutive zero bits from the most significant bit

| xref:bit.adoc[`countl_one`]
| Counts consecutive one bits from the most significant bit

| xref:bit.adoc[`countr_zero`]
| Counts consecutive zero bits from the least significant bit

| xref:bit.adoc[`countr_one`]
| Counts consecutive one bits from the least significant bit

| xref:bit.adoc[`popcount`]
| Returns the number of set bits

| xref:bit.adoc[`byteswap`]
| Reverses the bytes of the value
|===

=== Arithmetic

[cols="1,2", options="header"]
Expand Down Expand Up @@ -114,6 +157,9 @@ https://www.boost.org/LICENSE_1_0.txt
| `<boost/safe_numbers.hpp>`
| Convenience header including all library types

| `<boost/safe_numbers/bit.hpp>`
| Bit manipulation functions (`has_single_bit`, `bit_ceil`, `bit_floor`, `bit_width`, `rotl`, `rotr`, `countl_zero`, `countl_one`, `countr_zero`, `countr_one`, `popcount`, `byteswap`)

| `<boost/safe_numbers/charconv.hpp>`
| Character conversion functions (`to_chars`, `from_chars`)

Expand Down
194 changes: 194 additions & 0 deletions doc/modules/ROOT/pages/bit.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
////
Copyright 2026 Matt Borland
Distributed under the Boost Software License, Version 1.0.
https://www.boost.org/LICENSE_1_0.txt
////

[#bit]
= `<bit>` Support
:idprefix: bit_

== Description

The library provides thin wrappers around the C++20 https://en.cppreference.com/w/cpp/header/bit.html[`<bit>`] functions for safe integer types.
Each function extracts the underlying value, delegates to the corresponding `std::` function, and wraps the result back into the safe type where appropriate.
For `u128`, the functions delegate to the `boost::int128` implementations.

[source,c++]
----
#include <boost/safe_numbers/bit.hpp>
----

== Power-of-Two Functions

=== has_single_bit

[source,c++]
----
template <unsigned_library_type UnsignedInt>
constexpr auto has_single_bit(UnsignedInt x) noexcept -> bool;
----

Returns `true` if `x` is a power of two.
See https://en.cppreference.com/w/cpp/numeric/has_single_bit.html[`std::has_single_bit`].

=== bit_ceil

[source,c++]
----
template <unsigned_library_type UnsignedInt>
constexpr auto bit_ceil(UnsignedInt x) noexcept -> UnsignedInt;
----

Returns the smallest power of two not less than `x`.
Undefined behavior if the result is not representable in the underlying type.
See https://en.cppreference.com/w/cpp/numeric/bit_ceil.html[`std::bit_ceil`].

=== bit_floor

[source,c++]
----
template <unsigned_library_type UnsignedInt>
constexpr auto bit_floor(UnsignedInt x) noexcept -> UnsignedInt;
----

Returns the largest power of two not greater than `x`.
Returns zero if `x` is zero.
See https://en.cppreference.com/w/cpp/numeric/bit_floor.html[`std::bit_floor`].

=== bit_width

[source,c++]
----
template <unsigned_library_type UnsignedInt>
constexpr auto bit_width(UnsignedInt x) noexcept -> int;
----

Returns the number of bits needed to represent `x` (i.e., 1 + floor(log2(x)) for x > 0, or 0 for x == 0).
See https://en.cppreference.com/w/cpp/numeric/bit_width.html[`std::bit_width`].

== Rotation Functions

=== rotl

[source,c++]
----
template <unsigned_library_type UnsignedInt>
constexpr auto rotl(UnsignedInt x, int s) noexcept -> UnsignedInt;
----

Computes the result of bitwise left-rotating `x` by `s` positions.
See https://en.cppreference.com/w/cpp/numeric/rotl.html[`std::rotl`].

=== rotr

[source,c++]
----
template <unsigned_library_type UnsignedInt>
constexpr auto rotr(UnsignedInt x, int s) noexcept -> UnsignedInt;
----

Computes the result of bitwise right-rotating `x` by `s` positions.
See https://en.cppreference.com/w/cpp/numeric/rotr.html[`std::rotr`].

== Counting Functions

=== countl_zero

[source,c++]
----
template <unsigned_library_type UnsignedInt>
constexpr auto countl_zero(UnsignedInt x) noexcept -> int;
----

Returns the number of consecutive 0-bits starting from the most significant bit.
See https://en.cppreference.com/w/cpp/numeric/countl_zero.html[`std::countl_zero`].

=== countl_one

[source,c++]
----
template <unsigned_library_type UnsignedInt>
constexpr auto countl_one(UnsignedInt x) noexcept -> int;
----

Returns the number of consecutive 1-bits starting from the most significant bit.
See https://en.cppreference.com/w/cpp/numeric/countl_one.html[`std::countl_one`].

=== countr_zero

[source,c++]
----
template <unsigned_library_type UnsignedInt>
constexpr auto countr_zero(UnsignedInt x) noexcept -> int;
----

Returns the number of consecutive 0-bits starting from the least significant bit.
See https://en.cppreference.com/w/cpp/numeric/countr_zero.html[`std::countr_zero`].

=== countr_one

[source,c++]
----
template <unsigned_library_type UnsignedInt>
constexpr auto countr_one(UnsignedInt x) noexcept -> int;
----

Returns the number of consecutive 1-bits starting from the least significant bit.
See https://en.cppreference.com/w/cpp/numeric/countr_one.html[`std::countr_one`].

=== popcount

[source,c++]
----
template <unsigned_library_type UnsignedInt>
constexpr auto popcount(UnsignedInt x) noexcept -> int;
----

Returns the number of 1-bits in `x`.
See https://en.cppreference.com/w/cpp/numeric/popcount.html[`std::popcount`].

== Byte Manipulation

=== byteswap

[source,c++]
----
template <unsigned_library_type UnsignedInt>
constexpr auto byteswap(UnsignedInt x) noexcept -> UnsignedInt;
----

Reverses the bytes of `x`.
For standard unsigned types this delegates to `std::byteswap` (C++23).
For `u128` this delegates to the `boost::int128::byteswap` implementation.
See https://en.cppreference.com/w/cpp/numeric/byteswap.html[`std::byteswap`].

== Examples

.This https://github.com/boostorg/safe_numbers/blob/develop/examples/bit.cpp[example] demonstrates the bit manipulation functions.
====
[source, c++]
----
include::example$bit.cpp[]
----

Output:
----
has_single_bit(40) = 0
has_single_bit(32) = 1
bit_ceil(40) = 64
bit_floor(40) = 32
bit_width(40) = 6

rotl(0b10110001, 2) = 198
rotr(0b10110001, 2) = 108

countl_zero(0x0F00) = 4
countl_one(0x0F00) = 0
countr_zero(0x0F00) = 8
countr_one(0x0F00) = 0
popcount(0x0F00) = 4

byteswap(0x12345678) = 0x78563412
----
====
34 changes: 34 additions & 0 deletions doc/modules/ROOT/pages/examples.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,40 @@ Roundtrip: 18446744073709551615 -> "18446744073709551615" -> 1844674407370955161
----
====

[#examples_bit]
== Bit Manipulation

The library provides thin wrappers around all C++20 `<bit>` functions for safe integer types.
Each function extracts the underlying value, delegates to the standard library, and wraps the result back.

.This https://github.com/boostorg/safe_numbers/blob/develop/examples/bit.cpp[example] demonstrates the bit manipulation functions.
====
[source, c++]
----
include::example$bit.cpp[]
----

Output:
----
has_single_bit(40) = 0
has_single_bit(32) = 1
bit_ceil(40) = 64
bit_floor(40) = 32
bit_width(40) = 6

rotl(0b10110001, 2) = 198
rotr(0b10110001, 2) = 108

countl_zero(0x0F00) = 4
countl_one(0x0F00) = 0
countr_zero(0x0F00) = 8
countr_one(0x0F00) = 0
popcount(0x0F00) = 4

byteswap(0x12345678) = 0x78563412
----
====

[#examples_policy_comparison]
== Policy Comparison

Expand Down
47 changes: 47 additions & 0 deletions examples/bit.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Copyright 2026 Matt Borland
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt

#include <boost/safe_numbers/unsigned_integers.hpp>
#include <boost/safe_numbers/bit.hpp>
#include <iostream>

int main()
{
using namespace boost::safe_numbers;

const u32 x {0b0000'0000'0000'0000'0000'0000'0010'1000}; // 40

// Power-of-two queries
std::cout << "has_single_bit(40) = " << has_single_bit(x) << '\n';
std::cout << "has_single_bit(32) = " << has_single_bit(u32{32}) << '\n';
std::cout << "bit_ceil(40) = " << static_cast<std::uint32_t>(bit_ceil(x)) << '\n';
std::cout << "bit_floor(40) = " << static_cast<std::uint32_t>(bit_floor(x)) << '\n';
std::cout << "bit_width(40) = " << bit_width(x) << '\n';

std::cout << '\n';

// Rotation
const u8 y {0b1011'0001}; // 177
std::cout << "rotl(0b10110001, 2) = " << static_cast<unsigned>(rotl(y, 2)) << '\n';
std::cout << "rotr(0b10110001, 2) = " << static_cast<unsigned>(rotr(y, 2)) << '\n';

std::cout << '\n';

// Counting
const u16 z {0b0000'1111'0000'0000}; // 3840
std::cout << "countl_zero(0x0F00) = " << countl_zero(z) << '\n';
std::cout << "countl_one(0x0F00) = " << countl_one(z) << '\n';
std::cout << "countr_zero(0x0F00) = " << countr_zero(z) << '\n';
std::cout << "countr_one(0x0F00) = " << countr_one(z) << '\n';
std::cout << "popcount(0x0F00) = " << popcount(z) << '\n';

std::cout << '\n';

// Byteswap
const u32 w {0x12345678};
std::cout << std::hex;
std::cout << "byteswap(0x12345678) = 0x" << static_cast<std::uint32_t>(byteswap(w)) << '\n';

return 0;
}
1 change: 1 addition & 0 deletions include/boost/safe_numbers.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@
#include <boost/safe_numbers/literals.hpp>
#include <boost/safe_numbers/format.hpp>
#include <boost/safe_numbers/charconv.hpp>
#include <boost/safe_numbers/bit.hpp>

#endif //BOOST_SAFENUMBERS_HPP
Loading