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);
428 throw std::overflow_error(
429 std::format(
"Value {} does not fit in {} bits", v,
width));
430 for (
size_t i = 0; i <
width; ++i)
437 int64_t maxVal = (1LL << (
width - 1)) - 1;
438 int64_t minVal = -(1LL << (
width - 1));
439 if (v < minVal || v > maxVal)
440 throw std::overflow_error(
441 std::format(
"Value {} does not fit in {} bits", v,
width));
443 for (
size_t i = 0; i <
width; ++i)
450 throw std::invalid_argument(
"Bitwise &= requires equal widths");
451 for (
size_t i = 0; i <
bitWidth; ++i)
458 throw std::invalid_argument(
"Bitwise |= requires <= widths");
459 for (
size_t i = 0; i < other.
bitWidth; ++i)
466 throw std::invalid_argument(
"Bitwise ^= requires equal widths");
467 for (
size_t i = 0; i <
bitWidth; ++i)
473 for (
size_t i = 0; i <
bitWidth; ++i)
503 for (
size_t i = 0; i < limit; ++i)
509 for (
size_t i = this->
bitWidth; i < 64; ++i)
512 return static_cast<int64_t
>(u);
514 for (
size_t i = 64; i < this->
bitWidth; ++i) {
515 if (this->
getBit(i) != signBit)
516 throw std::overflow_error(
"Int does not fit in int64_t");
518 return static_cast<int64_t
>(u);
522 if (n == 0 || n > 64)
523 throw std::invalid_argument(std::format(
"Invalid bit width: {}", n));
524 int64_t min = -(1LL << (n - 1));
525 int64_t max = (1LL << (n - 1)) - 1;
526 if (v < min || v > max)
527 throw std::overflow_error(
528 std::format(
"Value {} does not fit in int{}_t", v, n));
533 for (
size_t i = 64; i < this->
bitWidth; ++i)
535 throw std::overflow_error(
"Int does not fit in uint64_t");
539 for (
size_t i = 0; i < limit; ++i)
546 if (n == 0 || n > 64)
547 throw std::invalid_argument(std::format(
"Invalid bit width: {}", n));
548 uint64_t max = (n == 64) ? UINT64_MAX : ((1ULL << n) - 1);
550 throw std::overflow_error(
551 std::format(
"Value {} does not fit in uint{}_t", v, n));
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.
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.
static void fits(int64_t v, unsigned n)
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)
static void fits(uint64_t v, unsigned n)
std::ostream & operator<<(std::ostream &os, const BitVector &bv)