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

PeterCDMcLean / BitLib / 16609468613

29 Jul 2025 11:13PM UTC coverage: 75.101% (-3.4%) from 78.485%
16609468613

Pull #18

github

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

3442 of 5312 branches covered (64.8%)

Branch coverage included in aggregate %.

328 of 429 new or added lines in 12 files covered. (76.46%)

29 existing lines in 2 files now uncovered.

2530 of 2640 relevant lines covered (95.83%)

30325459.16 hits per line

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

63.82
/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
    case 4:
×
NEW
100
      static constexpr auto map4 = make_from_digit_map<4>();
NEW
101
      return map4;
×
NEW
102
    case 8:
×
NEW
103
      static constexpr auto map8 = make_from_digit_map<8>();
NEW
104
      return map8;
×
105
    case 16:
4!
106
      static constexpr auto map16 = make_from_digit_map<16>();
2✔
107
      return map16;
4✔
NEW
108
    case 32:
×
NEW
109
      static constexpr auto map32 = make_from_digit_map<32>();
NEW
110
      return map32;
×
NEW
111
    case 64:
×
NEW
112
      static constexpr auto map64 = make_from_digit_map<64>();
NEW
113
      return map64;
×
NEW
114
    default:
×
NEW
115
      throw std::runtime_error("Base not implemented");
×
116
  }
2✔
117
}
2✔
118

119
struct metadata_t {
120
  size_t base;
121
  bool is_signed;
122
  std::endian endian;
123
  bool str_sign_extend_zeros;
124
  char prefix[16];
125
  char fill;
126
};
127

128
constexpr metadata_t typical(size_t base = 10, bool str_sign_extend_zeros = false) {
129
  return {
130
      .base = base,
131
      .is_signed = false,
132
      .endian = std::endian::big,
NEW
133
      .str_sign_extend_zeros = str_sign_extend_zeros,
NEW
134
      .prefix = "",
NEW
135
      .fill = '\0'};
UNCOV
136
}
137

138
}  // namespace string
139

140
template <typename RandomAccessIt, typename CharIt>
141
constexpr CharIt to_string(
5✔
142
    const bit_iterator<RandomAccessIt>& bit_first,
143
    const bit_iterator<RandomAccessIt>& bit_last,
144
    const CharIt str_first,
145
    const CharIt str_last,
146
    string::metadata_t meta = string::typical()) {
5✔
147
  if (std::has_single_bit(meta.base)) {
10!
148
    const auto base_bits = std::bit_width(meta.base - 1);
8✔
149
    const auto base_digits = string::make_digit_map(meta.base);
8✔
150

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

177
    const unsigned char base = static_cast<unsigned char>(meta.base);
2✔
178
    auto remainder = ::bit::division(bit_first, bit_last, bit_it, base);
2✔
179
    CharIt cursor = str_last;
2✔
180
    *(--cursor) = static_cast<char>(remainder + '0');
2✔
181

182
    while ((cursor != str_first) && (::bit::count(bit_it, bit_it + store_bits, bit1) > 0)) {
6!
183
      remainder = ::bit::division(bit_it, bit_it + store_bits, bit_it, base);
4✔
184
      *(--cursor) = static_cast<char>(remainder + '0');
4✔
185
    }
2✔
186
    if (cursor != str_first) {
2!
NEW
187
      return std::copy(cursor, str_last, str_first);
×
NEW
188
    }
189
    return str_last;
2✔
190
  }
2✔
191
}
5✔
192

193
template <typename RandomAccessIt>
194
constexpr size_t estimate_length(
5✔
195
    const bit_iterator<RandomAccessIt>& first,
196
    const bit_iterator<RandomAccessIt>& last,
197
    const size_t base,
198
    const bool str_sign_extend_zeros) {
5✔
199
  if (std::has_single_bit(base)) {
10!
200
    const auto base_bits = std::bit_width(base - 1);
8✔
201

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

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

217
template <typename RandomAccessIt>
218
constexpr std::string to_string(
5✔
219
    const bit_iterator<RandomAccessIt>& first,
220
    const bit_iterator<RandomAccessIt>& last,
221
    string::metadata_t meta = string::typical()) {
5✔
222
  std::string buffer(estimate_length(first, last, meta.base, meta.str_sign_extend_zeros), meta.fill);
20✔
223
  if (meta.fill) {
10!
NEW
224
    std::fill(to_string(first, last, buffer.begin(), buffer.end(), meta), buffer.end(), meta.fill);
×
225
  } else {
5✔
226
    buffer.resize(to_string(first, last, buffer.begin(), buffer.end(), meta) - buffer.begin());
10✔
227
  }
5✔
228
  return buffer;
15✔
229
}
5✔
230

231
template <string::metadata_t meta = string::typical(), typename RandomAccessIt>
232
constexpr std::string to_string(
233
    const bit_iterator<RandomAccessIt>& first,
234
    const bit_iterator<RandomAccessIt>& last) {
235
  static_assert(meta.endian == std::endian::big, "Only bit big endian support (MSB on the left)");
236
  return to_string(first, last, meta);
237
}
238

239
constexpr std::string to_string(const bit_sized_range auto& bits, string::metadata_t meta = string::typical()) {
10✔
240
  return to_string(bits.begin(), bits.end(), meta);
15✔
241
}
5✔
242

243
template <string::metadata_t meta = string::typical()>
244
constexpr std::string to_string(const bit_sized_range auto& bits) {
10✔
245
  return to_string(bits, meta);
15✔
246
}
5✔
247

248
template <string::metadata_t meta = string::typical(), typename RandomAccessIt, typename CharIt>
249
constexpr CharIt to_string(
250
    const bit_iterator<RandomAccessIt>& first,
251
    const bit_iterator<RandomAccessIt>& last,
252
    const CharIt str_first,
253
    const CharIt str_last) {
254
  static_assert(meta.endian == std::endian::big, "Only bit big endian support (MSB on the left)");
255
  return to_string(first, last, str_first, str_last, meta);
256
}
257

258
template <typename CharIt>
259
constexpr CharIt to_string(
260
    const bit_sized_range auto& bits,
261
    const CharIt str_first,
262
    const CharIt str_last,
263
    string::metadata_t meta = string::typical()) {
264
  return to_string(str_first, str_last, bits.begin(), bits.end(), meta);
265
}
266

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

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

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

329
    // TODO: template with uninitialized_t
330
    ::bit::fill(bit_first, bit_last, bit0);  // Clear the bits first
2✔
331

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

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

360
template <typename CharIt>
361
constexpr std::vector<uintptr_t> from_string(
362
    const CharIt first, const CharIt last, string::metadata_t meta = string::typical()) {
363
  if (std::has_single_bit(meta.base)) {
364
    const auto base_bits = std::bit_width(meta.base - 1);
365
    const auto base_from_digits = string::make_from_digit_map(meta.base);
366

367
    std::vector<uintptr_t> vec;
368

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

398
template <string::metadata_t meta = string::typical(),
399
          typename CharIt>
400
constexpr std::vector<uintptr_t> from_string(
401
    const CharIt first, const CharIt last) {
402
  static_assert(meta.endian == std::endian::big, "Only bit big endian support (MSB on the left)");
403
  return from_string(first, last, meta);
404
}
405

406
template <string::metadata_t meta = string::typical(), typename RandomAccessIt, typename Policy = policy::typical<typename RandomAccessIt::value_type>>
407
constexpr void from_string(
408
    const std::string& str,
409
    const bit_iterator<RandomAccessIt>& bit_first, const bit_iterator<RandomAccessIt>& bit_last) {
410
  static_assert(meta.endian == std::endian::big, "Only bit big endian support (MSB on the left)");
411
  from_string<meta, RandomAccessIt, Policy>(str.c_str(), str.c_str() + str.length(), bit_first, bit_last);
412
}
413

414
template <string::metadata_t meta = string::typical(), bit_range RangeT, typename Policy = policy::typical<typename std::ranges::iterator_t<RangeT>::value_type>>
415
constexpr void from_string(
3✔
416
    const std::string& str,
417
    RangeT& bits) {
3✔
418
  using range_iterator_t = std::ranges::iterator_t<RangeT>;
3✔
419
  using RandomAccessIt = typename range_iterator_t::iterator_type;
3✔
420
  from_string<meta, std::string::const_iterator, RandomAccessIt, Policy>(str.begin(), str.end(), bits.begin(), bits.end());
6✔
421
}
6✔
422

423
template <typename RandomAccessIt, typename Policy = policy::typical<typename RandomAccessIt::value_type>>
424
constexpr void from_string(
425
    const std::string& str,
426
    const bit_iterator<RandomAccessIt>& bit_first, const bit_iterator<RandomAccessIt>& bit_last,
427
    string::metadata_t meta = string::typical()) {
428
  from_string<std::string::const_iterator, RandomAccessIt, Policy>(
429
      str.begin(), str.end(),
430
      bit_first, bit_last,
431
      meta);
432
}
433

434
template <bit_range RangeT, typename Policy = policy::typical<typename std::ranges::iterator_t<RangeT>::value_type>>
435
constexpr void from_string(
436
    const std::string& str,
437
    RangeT& bits,
438
    string::metadata_t meta = string::typical()) {
439
  using range_iterator_t = std::ranges::iterator_t<RangeT>;
440
  using RandomAccessIt = typename range_iterator_t::iterator_type;
441
  from_string<std::string::const_iterator, RandomAccessIt, Policy>(
442
      str.begin(), str.end(),
443
      bits.begin(), bits.end(),
444
      meta);
445
}
446

447
}  // namespace bit
448

449
#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