16BitVector::BitVector(std::span<const byte> bytes, std::optional<size_t> width,
18 : bitWidth(width ? *width : bytes.size() * 8), bitIndex(bitIndex) {
21 throw std::invalid_argument(
"bitIndex must be <= 7");
22 size_t totalBitsAvail = bytes.size() * 8 -
bitIndex;
24 throw std::invalid_argument(
25 std::format(
"Width of {} bits exceeds provided storage of {} bits",
30 : data(other.data), bitWidth(other.bitWidth), bitIndex(other.bitIndex) {}
43 throw std::out_of_range(
"Bit index out of range");
45 size_t byteIdx = absoluteBit / 8;
46 uint8_t bitOff = absoluteBit % 8;
47 return (
data[byteIdx] >> bitOff) & 0x1;
53 throw std::out_of_range(
"Right shift exceeds bit width");
65 size_t totalBitOffset =
bitIndex + n;
66 size_t byteOffset = totalBitOffset / 8;
67 result.
bitIndex =
static_cast<uint8_t
>(totalBitOffset % 8);
70 if (byteOffset > 0 && byteOffset <
data.size())
71 result.
data =
data.subspan(byteOffset);
72 else if (byteOffset >=
data.size())
85 throw std::invalid_argument(
"slice range exceeds current width");
90 size_t byteOffset = offset / 8;
91 result.
data =
data.subspan(byteOffset);
94 result.
data = result.
data.subspan(1);
106 std::optional<size_t> width)
107 : owner(std::move(bytes)) {
112 throw std::invalid_argument(
"Width exceeds provided storage");
123 :
BitVector(), owner(std::vector<
byte>((other.width() + 7) / 8, 0)) {
128 for (
size_t i = 0; i <
bitWidth; ++i)
134 :
BitVector(), owner(std::vector<
byte>((other.width() + 7) / 8, 0)) {
139 for (
size_t i = 0; i <
bitWidth; ++i)
145 :
BitVector(), owner(std::move(other.owner)) {
148 data = std::span<const byte>(owner.data(), owner.size());
168 owner = std::move(other.owner);
169 bitWidth = other.bitWidth;
171 data = std::span<const byte>(owner.data(), owner.size());
180 throw std::out_of_range(
"Bit index out of range");
181 size_t byteIdx = i / 8;
182 uint8_t bitOff = i % 8;
183 uint8_t mask =
static_cast<uint8_t
>(1u << bitOff);
184 byte &target =
owner[byteIdx];
188 target &=
static_cast<uint8_t
>(~mask);
194 throw std::out_of_range(
"Right shift exceeds bit width");
204 if (
data.size() == 0)
217 size_t newWidth = oldWidth + n;
219 std::vector<byte> newStorage((newWidth + 7) / 8, 0);
221 for (
size_t i = 0; i < oldWidth; ++i)
223 size_t newPos = i + n;
224 newStorage[newPos / 8] |=
static_cast<byte>(1u << (newPos % 8));
226 owner = std::move(newStorage);
236 size_t otherWidth = other.
width();
237 size_t newWidth = oldWidth + otherWidth;
238 std::vector<byte> newStorage((newWidth + 7) / 8, 0);
240 for (
size_t i = 0; i < oldWidth; ++i)
242 newStorage[i / 8] |=
static_cast<byte>(1u << (i % 8));
245 for (
size_t i = 0; i < otherWidth; ++i)
247 size_t newPos = i + oldWidth;
248 newStorage[newPos / 8] |=
static_cast<byte>(1u << (newPos % 8));
250 owner = std::move(newStorage);
259 throw std::invalid_argument(
"Bitwise & require equal widths");
261 for (
size_t i = 0; i < a.
width(); ++i)
263 result.setBit(i,
true);
269 throw std::invalid_argument(
"Bitwise ops require equal widths");
271 for (
size_t i = 0; i < a.
width(); ++i)
273 result.setBit(i,
true);
279 throw std::invalid_argument(
"Bitwise ops require equal widths");
281 for (
size_t i = 0; i < a.
width(); ++i)
283 result.setBit(i,
true);
290 unsigned bitsPerDigit,
bool uppercase) {
293 unsigned groups = (bv.
width() + bitsPerDigit - 1) / bitsPerDigit;
296 auto hexDigit = [&](
unsigned v) ->
char {
298 return static_cast<char>(
'0' + v);
299 return static_cast<char>((uppercase ?
'A' :
'a') + (v - 10));
301 for (
int g =
static_cast<int>(groups) - 1; g >= 0; --g) {
303 for (
unsigned j = 0; j < bitsPerDigit; ++j) {
304 unsigned bitIdx = g * bitsPerDigit + j;
308 if (out.empty() && value == 0 && g != 0)
310 if (bitsPerDigit == 4)
311 out.push_back(hexDigit(value));
313 out.push_back(
static_cast<char>(
'0' + value));
324 constexpr uint32_t BASE = 1000000000u;
325 std::vector<uint32_t> limbs(1, 0);
327 for (
size_t idx = 0; idx < bv.
width(); ++idx) {
328 size_t i = bv.
width() - 1 - idx;
331 for (
auto &d : limbs) {
332 uint64_t v =
static_cast<uint64_t
>(d) * 2 + carry;
333 d =
static_cast<uint32_t
>(v % BASE);
337 limbs.push_back(
static_cast<uint32_t
>(carry));
341 for (
auto &d : limbs) {
342 uint64_t v =
static_cast<uint64_t
>(d) + c;
343 d =
static_cast<uint32_t
>(v % BASE);
349 limbs.push_back(
static_cast<uint32_t
>(c));
353 std::string out = std::to_string(limbs.back());
354 for (
int i =
static_cast<int>(limbs.size()) - 2; i >= 0; --i) {
355 std::string chunk = std::to_string(limbs[i]);
356 out.append(9 - chunk.size(),
'0');
367 return std::string(
"0");
370 for (
size_t i = 0; i <
bitWidth; ++i)
381 throw std::invalid_argument(
382 std::format(
"Unsupported base '{}' for BitVector::toString", base));
388 ios_base::fmtflags basefmt = os.flags() & ios_base::basefield;
389 bool showBase = (os.flags() & ios_base::showbase) != 0;
390 bool upper = (os.flags() & ios_base::uppercase) != 0;
393 case ios_base::hex: {
396 os << (upper ?
"0X" :
"0x");
400 case ios_base::oct: {
407 case ios_base::dec: {
423 return std::ranges::equal(*
this, rhs);
438 const char *fmt,
unsigned target)
const {
439 const size_t absLo =
bitIndex + lowExclusive;
441 const size_t byteLo = absLo / 8;
442 const size_t byteHi = absHi / 8;
443 const unsigned loBit = absLo & 7u;
444 const unsigned hiBit = absHi & 7u;
446 if (byteLo == byteHi) {
449 static_cast<uint8_t
>(((1u << (hiBit - loBit + 1u)) - 1u) << loBit);
450 diff =
static_cast<uint8_t
>(
data[byteLo] ^ expectedByte) & mask;
453 const uint8_t firstMask =
static_cast<uint8_t
>(0xFFu << loBit);
454 diff =
static_cast<uint8_t
>(
data[byteLo] ^ expectedByte) & firstMask;
456 for (
size_t b = byteLo + 1; b < byteHi; ++b)
457 diff |=
static_cast<uint8_t
>(
data[b] ^ expectedByte);
459 const uint8_t lastMask =
static_cast<uint8_t
>((1u << (hiBit + 1u)) - 1u);
460 diff |=
static_cast<uint8_t
>(
data[byteHi] ^ expectedByte) & lastMask;
463 throw std::overflow_error(std::vformat(fmt, std::make_format_args(target)));
468 throw std::overflow_error(
469 std::format(
"Value {} does not fit in {} bits", v,
width));
470 for (
size_t i = 0; i <
width; ++i)
477 int64_t maxVal = (1LL << (
width - 1)) - 1;
478 int64_t minVal = -(1LL << (
width - 1));
479 if (v < minVal || v > maxVal)
480 throw std::overflow_error(
481 std::format(
"Value {} does not fit in {} bits", v,
width));
483 for (
size_t i = 0; i <
width; ++i)
490 throw std::invalid_argument(
"Bitwise &= requires equal widths");
491 for (
size_t i = 0; i <
bitWidth; ++i)
498 throw std::invalid_argument(
"Bitwise |= requires <= widths");
499 for (
size_t i = 0; i < other.
bitWidth; ++i)
506 throw std::invalid_argument(
"Bitwise ^= requires equal widths");
507 for (
size_t i = 0; i <
bitWidth; ++i)
513 for (
size_t i = 0; i <
bitWidth; ++i)
543 for (
size_t i = 0; i < limit; ++i)
549 for (
size_t i = this->
bitWidth; i < 64; ++i)
551 return static_cast<int64_t
>(u);
559 const bool destSignBit = (u >> 63) & 1ULL;
561 "Int does not fit in int{}_t", 64);
563 return static_cast<int64_t
>(u);
571 for (
size_t i = 0; i < limit; ++i)
static InstancePath empty
static std::string toPowerOfTwoString(const BitVector &bv, unsigned bitsPerDigit, bool uppercase)
static std::string toDecimalString(const BitVector &bv)
A lightweight, non-owning bit vector view backed by a byte array span.
friend MutableBitVector operator^(const BitVector &a, const BitVector &b)
Bitwise XOR: creates a new MutableBitVector with the result.
void checkHighBytesEqual(unsigned lowExclusive, uint8_t expectedByte, const char *fmt, unsigned target) const
friend MutableBitVector operator&(const BitVector &a, const BitVector &b)
Bitwise AND: creates a new MutableBitVector with the result.
BitVector slice(size_t offset, size_t sliceWidth) const
Create a new immutable view of a contiguous bit slice [offset, offset+sliceWidth).
BitVector & operator=(const BitVector &other)
bool getBit(size_t i) const
Return the i-th bit (0 = LSB) as boolean.
std::string toString(unsigned base=16) const
std::span< const byte > data
BitVector & operator>>=(size_t n)
BitVector operator>>(size_t n) const
Logical right shift that drops the least-significant n bits by advancing the byte/bit index and reduc...
bool operator==(const BitVector &rhs) const
friend MutableBitVector operator|(const BitVector &a, const BitVector &b)
Bitwise OR: creates a new MutableBitVector with the result.
int64_t toI64() const
Convert to an int64_t, sign-extending from the high bit and throwing if the value does not fit.
A mutable bit vector that owns its underlying storage.
MutableBitVector & operator|=(const MutableBitVector &other)
MutableBitVector & operator>>=(size_t n)
In-place logical right shift that drops the least-significant n bits.
MutableBitVector & operator<<=(size_t n)
In-place logical left shift shifts in n zero bits at LSB, shifting existing bits upward.
MutableBitVector operator~() const
MutableBitVector & operator&=(const MutableBitVector &other)
MutableBitVector()=default
std::vector< byte > owner
void setBit(size_t i, bool v)
Set the i-th bit.
MutableBitVector & operator=(const MutableBitVector &other)
MutableBitVector & operator^=(const MutableBitVector &other)
uint64_t toUI64() const
Convert to a uint64_t, throwing if the value does not fit.
std::ostream & operator<<(std::ostream &os, const BitVector &bv)