• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

PeterCDMcLean / BitLib / 16688112905

02 Aug 2025 12:57AM UTC coverage: 74.963% (-3.5%) from 78.485%
16688112905

Pull #18

github

web-flow
Merge 302018634 into 079daa142
Pull Request #18: From string

3447 of 5326 branches covered (64.72%)

Branch coverage included in aggregate %.

348 of 470 new or added lines in 12 files covered. (74.04%)

28 existing lines in 2 files now uncovered.

2556 of 2682 relevant lines covered (95.3%)

29850565.68 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

64.76
/include/bitlib/bit-algorithms/to_from_string.hpp
1
// ================================= array_REF =================================== //
2
// Project:     The Experimental Bit Algorithms Library
3
// \file        to_string.hpp
4
// Description: Implementation of array_ref
5
// Creator:     Vincent Reverdy
6
// Contributor: Peter McLean [2025]
7
// License:     BSD 3-Clause License
8
// ========================================================================== //
9

10
#ifndef _BIT_TO_STRING_HPP_INCLUDED
11
#define _BIT_TO_STRING_HPP_INCLUDED
12

13
#include <array>
14
#include <bit>
15
#include <iomanip>
16
#include <sstream>
17
#include <string>
18

19
#include "bitlib/bit-algorithms/accumulate.hpp"
20
#include "bitlib/bit-algorithms/count.hpp"
21
#include "bitlib/bit-algorithms/division.hpp"
22
#include "bitlib/bit-algorithms/multiplication.hpp"
23
#include "bitlib/bit-containers/bit_policy.hpp"
24
#include "bitlib/bit_concepts.hpp"
25

26
namespace bit {
27

28
namespace string {
29

30
template <std::size_t Base>
UNCOV
31
constexpr auto make_digit_map() {
UNCOV
32
  static_assert((Base >= 2) && ((Base & (Base - 1)) == 0), "Base must be power of 2 >= 2");
UNCOV
33
  static_assert(Base <= 64, "Base too large for simple char mapping");
UNCOV
34

NEW
35
  ::std::array<char, Base> map{};
UNCOV
36
  for (std::size_t i = 0; i < Base; ++i) {
UNCOV
37
    map[i] = (i < 10) ? ('0' + i) : ('A' + (i - 10));
UNCOV
38
  }
UNCOV
39
  return map;
UNCOV
40
}
41

42
constexpr std::span<const char> make_digit_map(std::size_t Base) {
8✔
43
  switch (Base) {
8✔
NEW
44
    case 2: {
×
NEW
45
      static constexpr auto map = make_digit_map<2>();
NEW
46
      return std::span<const char>(static_cast<const char*>(map.data()), map.size());
×
NEW
47
    }
NEW
48
    case 4: {
×
NEW
49
      static constexpr auto map = make_digit_map<4>();
NEW
50
      return std::span<const char>(static_cast<const char*>(map.data()), map.size());
×
NEW
51
    }
NEW
52
    case 8: {
×
NEW
53
      static constexpr auto map = make_digit_map<8>();
NEW
54
      return std::span<const char>(static_cast<const char*>(map.data()), map.size());
×
NEW
55
    }
56
    case 16: {
8!
57
      static constexpr auto map = make_digit_map<16>();
4✔
58
      return std::span<const char>(static_cast<const char*>(map.data()), map.size());
12✔
NEW
59
    }
NEW
60
    case 32: {
×
NEW
61
      static constexpr auto map = make_digit_map<32>();
NEW
62
      return std::span<const char>(static_cast<const char*>(map.data()), map.size());
×
NEW
63
    }
NEW
64
    case 64: {
×
NEW
65
      static constexpr auto map = make_digit_map<64>();
NEW
66
      return std::span<const char>(static_cast<const char*>(map.data()), map.size());
×
NEW
67
    }
NEW
68
    default:
×
NEW
69
      return {};  // or throw, or abort
×
70
  }
4✔
71
}
4✔
72

73
template <std::size_t Base>
UNCOV
74
constexpr auto make_from_digit_map() {
UNCOV
75
  static_assert((Base >= 2) && ((Base & (Base - 1)) == 0), "Base must be power of 2 >= 2");
UNCOV
76
  static_assert(Base <= 64, "Base too large for simple char mapping");
UNCOV
77

NEW
78
  ::std::array<char, 128> map{};
UNCOV
79
  for (std::size_t i = 0; i < 128; ++i) {
UNCOV
80
    map[i] = ~0;
UNCOV
81
    if (i >= '0' && i <= '9') {
UNCOV
82
      map[i] = i - '0';
UNCOV
83
    }
UNCOV
84
    if (i >= 'a' && i <= 'z') {
UNCOV
85
      map[i] = (i - 'a') + 10;
UNCOV
86
    }
UNCOV
87
    if (i >= 'A' && i <= 'Z') {
UNCOV
88
      map[i] = (i - 'A') + 10;
UNCOV
89
    }
UNCOV
90
  }
UNCOV
91
  return map;
UNCOV
92
}
93

94
constexpr auto make_from_digit_map(std::size_t Base) {
4✔
95
  switch (Base) {
4✔
NEW
96
    case 2: {
×
NEW
97
      static constexpr auto map2 = make_from_digit_map<2>();
NEW
98
      return map2;
×
NEW
99
    }
NEW
100
    case 4: {
×
NEW
101
      static constexpr auto map4 = make_from_digit_map<4>();
NEW
102
      return map4;
×
NEW
103
    }
NEW
104
    case 8: {
×
NEW
105
      static constexpr auto map8 = make_from_digit_map<8>();
NEW
106
      return map8;
×
NEW
107
    }
108
    case 16: {
4!
109
      static constexpr auto map16 = make_from_digit_map<16>();
2✔
110
      return map16;
4✔
NEW
111
    }
NEW
112
    case 32: {
×
NEW
113
      static constexpr auto map32 = make_from_digit_map<32>();
NEW
114
      return map32;
×
NEW
115
    }
NEW
116
    case 64: {
×
NEW
117
      static constexpr auto map64 = make_from_digit_map<64>();
NEW
118
      return map64;
×
NEW
119
    }
NEW
120
    default:
×
NEW
121
      throw std::runtime_error("Base not implemented");
×
122
  }
2✔
123
}
2✔
124

125
struct metadata_t {
126
  size_t base;
127
  bool is_signed;
128
  std::endian endian;
129
  bool str_sign_extend_zeros;
130
  char prefix[16];
131
  char fill;
132
};
133

134
constexpr metadata_t typical(size_t base = 10, bool str_sign_extend_zeros = false) {
2✔
135
  return {
1✔
136
      .base = base,
1✔
137
      .is_signed = false,
1✔
138
      .endian = std::endian::big,
1✔
139
      .str_sign_extend_zeros = str_sign_extend_zeros,
1✔
140
      .prefix = "",
1✔
141
      .fill = '\0'};
2✔
142
}
1✔
143

144
}  // namespace string
145

146
template <typename RandomAccessIt, typename CharIt>
147
constexpr CharIt to_string(
6✔
148
    const bit_iterator<RandomAccessIt>& bit_first,
149
    const bit_iterator<RandomAccessIt>& bit_last,
150
    const CharIt str_first,
151
    const CharIt str_last,
152
    string::metadata_t meta = string::typical()) {
6✔
153
  if (std::has_single_bit(meta.base)) {
12!
154
    const auto base_bits = std::bit_width(meta.base - 1);
8✔
155
    const auto base_digits = string::make_digit_map(meta.base);
8✔
156

157
    CharIt cursor = accumulate_while(
8✔
158
        policy::AccumulateNoInitialSubword{},
4✔
159
        bit_first, bit_last, str_last,
4✔
160
        [meta, base_bits, base_digits, str_first](CharIt cursor, auto word, const size_t bits = bitsof<decltype(word)>()) {
8✔
161
          const int characters = ((bits + base_bits - 1) / base_bits);
8✔
162
          for (int i = characters - 1; i >= 0; i--) {
72!
163
            if (cursor == str_first) {
66!
164
              return std::make_pair(false, cursor);
2✔
165
            }
1✔
166
            *(--cursor) = base_digits[word & (meta.base - 1)];
64✔
167
            word >>= base_bits;
64✔
168
          }
32✔
169
          return std::make_pair(cursor != str_first, cursor);
6✔
170
        });
4✔
171
    if (cursor != str_first) {
8!
NEW
172
      return std::copy(cursor, str_last, str_first);
×
173
    } else {
4✔
174
      return str_last;
8✔
175
    }
4✔
176
  } else {
4✔
177
    using word_type = typename bit_iterator<RandomAccessIt>::word_type;
2✔
178
    size_t store_bits = distance(bit_first, bit_last);
4✔
179
    std::vector<word_type> vec((store_bits + bitsof<word_type>() - 1) / bitsof<word_type>());
8✔
180
    vec.back() = 0;  // Ensure last word is zeroed
4✔
181
    bit_iterator<word_type*> bit_it(vec.data());
4✔
182

183
    const unsigned char base = static_cast<unsigned char>(meta.base);
4✔
184
    auto remainder = ::bit::division(bit_first, bit_last, bit_it, base);
4✔
185
    CharIt cursor = str_last;
4✔
186
    *(--cursor) = static_cast<char>(remainder + '0');
4✔
187

188
    while ((cursor != str_first) && (::bit::count(bit_it, bit_it + store_bits, bit1) > 0)) {
12!
189
      remainder = ::bit::division(bit_it, bit_it + store_bits, bit_it, base);
8✔
190
      *(--cursor) = static_cast<char>(remainder + '0');
8✔
191
    }
4✔
192
    if (cursor != str_first) {
4!
NEW
193
      return std::copy(cursor, str_last, str_first);
×
NEW
194
    }
195
    return str_last;
4✔
196
  }
4✔
197
}
6✔
198

199
template <typename RandomAccessIt>
200
constexpr size_t estimate_length(
6✔
201
    const bit_iterator<RandomAccessIt>& first,
202
    const bit_iterator<RandomAccessIt>& last,
203
    const size_t base,
204
    const bool str_sign_extend_zeros) {
6✔
205
  if (std::has_single_bit(base)) {
12!
206
    const auto base_bits = std::bit_width(base - 1);
8✔
207

208
    int skip_leading_bits = str_sign_extend_zeros ? 0 : count_msb(first, last, bit0);
8!
209

210
    int str_len = (distance(first, last) - skip_leading_bits);
8✔
211
    str_len = (str_len + base_bits - 1) / base_bits;  // Round up to nearest base digit
8✔
212
    return static_cast<size_t>(std::max(1, str_len));
8✔
213
  } else {
4✔
214
    const uint32_t LOG2BASE = std::ceil(1 / std::logbf(base) * (1 << 16));
4✔
215
    int skip_leading_bits = str_sign_extend_zeros ? 0 : count_msb(first, last, bit0);
4!
216
    const auto bits = distance(first, last) - skip_leading_bits;
4✔
217
    const auto fixed_point = (bits * LOG2BASE);
4✔
218
    const auto max_len = (fixed_point >> 16) + ((fixed_point & ((1 << 16) - 1)) != 0);
4✔
219
    return static_cast<size_t>(std::max(max_len, static_cast<decltype(max_len)>(1)));
4✔
220
  }
2✔
221
}
6✔
222

223
template <typename RandomAccessIt>
224
constexpr std::string to_string(
6✔
225
    const bit_iterator<RandomAccessIt>& first,
226
    const bit_iterator<RandomAccessIt>& last,
227
    string::metadata_t meta = string::typical()) {
6✔
228
  std::string buffer(estimate_length(first, last, meta.base, meta.str_sign_extend_zeros), meta.fill);
24✔
229
  if (meta.fill) {
12!
NEW
230
    std::fill(to_string(first, last, buffer.begin(), buffer.end(), meta), buffer.end(), meta.fill);
×
231
  } else {
6✔
232
    buffer.resize(to_string(first, last, buffer.begin(), buffer.end(), meta) - buffer.begin());
12✔
233
  }
6✔
234
  return buffer;
18✔
235
}
6✔
236

237
template <string::metadata_t meta = string::typical(), typename RandomAccessIt>
238
constexpr std::string to_string(
239
    const bit_iterator<RandomAccessIt>& first,
240
    const bit_iterator<RandomAccessIt>& last) {
241
  static_assert(meta.endian == std::endian::big, "Only bit big endian support (MSB on the left)");
242
  return to_string(first, last, meta);
243
}
244

245
constexpr std::string to_string(const bit_sized_range auto& bits, string::metadata_t meta = string::typical()) {
12✔
246
  return to_string(bits.begin(), bits.end(), meta);
18✔
247
}
6✔
248

249
template <string::metadata_t meta = string::typical()>
250
constexpr std::string to_string(const bit_sized_range auto& bits) {
10✔
251
  return to_string(bits, meta);
15✔
252
}
5✔
253

254
template <string::metadata_t meta = string::typical(), typename RandomAccessIt, typename CharIt>
255
constexpr CharIt to_string(
256
    const bit_iterator<RandomAccessIt>& first,
257
    const bit_iterator<RandomAccessIt>& last,
258
    const CharIt str_first,
259
    const CharIt str_last) {
260
  static_assert(meta.endian == std::endian::big, "Only bit big endian support (MSB on the left)");
261
  return to_string(first, last, str_first, str_last, meta);
262
}
263

264
template <typename CharIt>
265
constexpr CharIt to_string(
266
    const bit_sized_range auto& bits,
267
    const CharIt str_first,
268
    const CharIt str_last,
269
    string::metadata_t meta = string::typical()) {
270
  return to_string(str_first, str_last, bits.begin(), bits.end(), meta);
271
}
272

273
template <string::metadata_t meta = string::typical(), typename CharIt>
274
constexpr CharIt to_string(
275
    const bit_sized_range auto& bits,
276
    const CharIt str_first,
277
    const CharIt str_last) {
278
  return to_string(bits, str_first, str_last, meta);
279
}
280

281
template <typename CharIt, typename RandomAccessIt, typename Policy = policy::typical<typename RandomAccessIt::value_type>>
282
constexpr void from_string(
3✔
283
    const CharIt str_first, const CharIt str_last,
284
    const bit_iterator<RandomAccessIt>& bit_first, const bit_iterator<RandomAccessIt>& bit_last,
285
    string::metadata_t meta = string::typical()) {
3✔
286
  if (std::has_single_bit(meta.base)) {
6!
287
    const auto base_bits = std::bit_width(meta.base - 1);
4✔
288
    const auto base_from_digits = string::make_from_digit_map(meta.base);
4✔
289
    using word_type = uint64_t;
2✔
290
    std::vector<word_type> vec;
4✔
291
    size_t store_bits = distance(bit_first, bit_last);
4✔
292

293
    bit_iterator<RandomAccessIt> bit_it = bit_first;
4✔
294
    CharIt cursor = str_last;
4✔
295
    cursor--;
4✔
296
    while (cursor >= str_first && store_bits) {
6!
297
      word_type work = 0;
4✔
298
      size_t bits = 0;
4✔
299
      for (; (bits < bitsof<word_type>()) && (cursor >= str_first); cursor--) {
28!
300
        char c = *cursor;
24✔
301
        // TODO: This should be a policy
302
        if (c >= base_from_digits.size()) {
36!
NEW
303
          continue;
×
NEW
304
        }
305
        auto digit = base_from_digits[c];
24✔
306
        // TODO: This should be a policy
307
        if (~0 == digit) {
24!
NEW
308
          continue;
×
NEW
309
        }
310
        work |= (digit << bits);
24✔
311
        bits += base_bits;
24✔
312
      }
12✔
313
      if (store_bits < bits) {
4!
314
        Policy::truncation::template from_integral<word_type, std::dynamic_extent, RandomAccessIt>(
2✔
315
            bit_it, bit_last, work);
1✔
316
        return;
2✔
317
      } else if ((store_bits > bits) && (cursor < str_first)) {
2!
318
        const bit_iterator<word_type*> p_integral(&work);
2✔
319
        bit_it = ::bit::copy(p_integral, p_integral + bits, bit_it);
2✔
320
        Policy::extension::template from_integral<word_type, std::dynamic_extent, RandomAccessIt>(
2✔
321
            bit_it, bit_last, work);
1✔
322
      } else if (store_bits >= bits) {
1!
NEW
323
        const bit_iterator<word_type*> p_integral(&work);
×
NEW
324
        bit_it = ::bit::copy(p_integral, p_integral + bits, bit_it);
×
NEW
325
      }
326
    }
2✔
327
  } else {
4✔
328
    if (meta.base != 10) {
2!
NEW
329
      throw std::runtime_error("Base not implemented");
×
NEW
330
    }
331
    using word_type = typename bit_iterator<RandomAccessIt>::word_type;
1✔
332
    std::vector<word_type> vec;
2✔
333
    size_t store_bits = distance(bit_first, bit_last);
2✔
334

335
    // TODO: template with uninitialized_t
336
    ::bit::fill(bit_first, bit_last, bit0);  // Clear the bits first
2✔
337

338
    CharIt cursor = str_first;
2✔
339
    while (cursor != str_last) {
8!
340
      unsigned char c = (*cursor - '0');
6✔
341
      if (c <= 9) {
6!
342
        auto overflow_mult = ::bit::multiplication(bit_first, bit_last, word_type{10});
6✔
343
        auto overflow_add = ::bit::addition(bit_first, bit_last, c);
6✔
344
        if (overflow_mult || overflow_add) {
6!
345
          //Policy::truncation::template overflow(bit_first, bit_last);
NEW
346
          return;
×
NEW
347
        }
348
      }
3✔
349
      cursor++;
6✔
350
    }
3✔
351
    //Policy::extension::template extend(bit_first, bit_last);
352
  }
2✔
353
}
3✔
354

355
template <string::metadata_t meta = string::typical(),
356
          typename CharIt,
357
          typename RandomAccessIt,
358
          typename Policy = policy::typical<typename RandomAccessIt::value_type>>
359
constexpr void from_string(
3✔
360
    const CharIt str_first, const CharIt str_last,
361
    const bit_iterator<RandomAccessIt>& bit_first, const bit_iterator<RandomAccessIt>& bit_last) {
3✔
362
  static_assert(meta.endian == std::endian::big, "Only bit big endian support (MSB on the left)");
3✔
363
  from_string<CharIt, RandomAccessIt, Policy>(str_first, str_last, bit_first, bit_last, meta);
6✔
364
}
6✔
365

366
template <typename CharIt>
367
constexpr std::vector<uintptr_t> from_string(
368
    const CharIt first, const CharIt last, string::metadata_t meta = string::typical()) {
369
  if (std::has_single_bit(meta.base)) {
370
    const auto base_bits = std::bit_width(meta.base - 1);
371
    const auto base_from_digits = string::make_from_digit_map(meta.base);
372

373
    std::vector<uintptr_t> vec;
374

375
    last--;
376
    while (last >= first) {
377
      uintptr_t work = 0;
378
      size_t bits = 0;
379
      for (; (bits < bitsof<uintptr_t>()) && (last >= first); last--) {
380
        char c = *last;
381
        // TODO: This should be a policy
382
        if (c >= base_from_digits.size()) {
383
          continue;
384
        }
385
        auto digit = base_from_digits[c];
386
        // TODO: This should be a policy
387
        if (~0 == digit) {
388
          continue;
389
        }
390
        work |= (digit << bits);
391
        bits += base_bits;
392
      }
393
      if (bits) {
394
        vec.push_back(work);
395
      }
396
    }
397
    return vec;
398
  } else {
399
    //from_string base 10 not implemented yet;
400
    return {};
401
  }
402
}
403

404
template <string::metadata_t meta = string::typical(),
405
          typename CharIt>
406
constexpr std::vector<uintptr_t> from_string(
407
    const CharIt first, const CharIt last) {
408
  static_assert(meta.endian == std::endian::big, "Only bit big endian support (MSB on the left)");
409
  return from_string(first, last, meta);
410
}
411

412
template <string::metadata_t meta = string::typical(), typename RandomAccessIt, typename Policy = policy::typical<typename RandomAccessIt::value_type>>
413
constexpr void from_string(
414
    const std::string& str,
415
    const bit_iterator<RandomAccessIt>& bit_first, const bit_iterator<RandomAccessIt>& bit_last) {
416
  static_assert(meta.endian == std::endian::big, "Only bit big endian support (MSB on the left)");
417
  from_string<meta, RandomAccessIt, Policy>(str.c_str(), str.c_str() + str.length(), bit_first, bit_last);
418
}
419

420
template <string::metadata_t meta = string::typical(), bit_range RangeT, typename Policy = policy::typical<typename std::ranges::iterator_t<RangeT>::value_type>>
421
constexpr void from_string(
3✔
422
    const std::string& str,
423
    RangeT& bits) {
3✔
424
  using range_iterator_t = std::ranges::iterator_t<RangeT>;
3✔
425
  using RandomAccessIt = typename range_iterator_t::iterator_type;
3✔
426
  from_string<meta, std::string::const_iterator, RandomAccessIt, Policy>(str.begin(), str.end(), bits.begin(), bits.end());
6✔
427
}
6✔
428

429
template <typename RandomAccessIt, typename Policy = policy::typical<typename RandomAccessIt::value_type>>
430
constexpr void from_string(
431
    const std::string& str,
432
    const bit_iterator<RandomAccessIt>& bit_first, const bit_iterator<RandomAccessIt>& bit_last,
433
    string::metadata_t meta = string::typical()) {
434
  from_string<std::string::const_iterator, RandomAccessIt, Policy>(
435
      str.begin(), str.end(),
436
      bit_first, bit_last,
437
      meta);
438
}
439

440
template <bit_range RangeT, typename Policy = policy::typical<typename std::ranges::iterator_t<RangeT>::value_type>>
441
constexpr void from_string(
442
    const std::string& str,
443
    RangeT& bits,
444
    string::metadata_t meta = string::typical()) {
445
  using range_iterator_t = std::ranges::iterator_t<RangeT>;
446
  using RandomAccessIt = typename range_iterator_t::iterator_type;
447
  from_string<std::string::const_iterator, RandomAccessIt, Policy>(
448
      str.begin(), str.end(),
449
      bits.begin(), bits.end(),
450
      meta);
451
}
452

453
}  // namespace bit
454

455
#endif // _BIT_TO_STRING_HPP_INCLUDED
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc