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

PeterCDMcLean / BitLib / 16830501887

08 Aug 2025 12:32PM UTC coverage: 76.436% (-0.4%) from 76.797%
16830501887

Pull #29

github

web-flow
Merge d08a426b4 into 4adca69e9
Pull Request #29: Explicitly cast inside _mask

3353 of 5056 branches covered (66.32%)

Branch coverage included in aggregate %.

197 of 202 new or added lines in 16 files covered. (97.52%)

17 existing lines in 3 files now uncovered.

2570 of 2693 relevant lines covered (95.43%)

29289657.02 hits per line

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

63.8
/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>
31
constexpr auto make_digit_map() {
32
  static_assert((Base >= 2) && ((Base & (Base - 1)) == 0), "Base must be power of 2 >= 2");
33
  static_assert(Base <= 64, "Base too large for simple char mapping");
34

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

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

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

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

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

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

142
}  // namespace string
143

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

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

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

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

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

206
    int skip_leading_bits = str_sign_extend_zeros ? 0 : count_msb(first, last, bit0);
520!
207

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

221
template <typename RandomAccessIt>
222
constexpr std::string to_string(
262✔
223
    const bit_iterator<RandomAccessIt>& first,
224
    const bit_iterator<RandomAccessIt>& last,
225
    string::metadata_t meta = string::typical()) {
262✔
226
  std::string buffer(estimate_length(first, last, meta.base, meta.str_sign_extend_zeros), meta.fill);
1,048✔
227
  if (meta.fill) {
524!
UNCOV
228
    std::fill(to_string(first, last, buffer.begin(), buffer.end(), meta), buffer.end(), meta.fill);
×
229
  } else {
262✔
230
    buffer.resize(to_string(first, last, buffer.begin(), buffer.end(), meta) - buffer.begin());
524✔
231
  }
262✔
232
  return buffer;
786✔
233
}
262✔
234

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

243
constexpr std::string to_string(const bit_sized_range auto& bits, string::metadata_t meta = string::typical()) {
524✔
244
  return to_string(bits.begin(), bits.end(), meta);
786✔
245
}
262✔
246

247
template <string::metadata_t meta = string::typical()>
248
constexpr std::string to_string(const bit_sized_range auto& bits) {
522✔
249
  return to_string(bits, meta);
783✔
250
}
261✔
251

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

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

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

279
template <typename CharIt, typename RandomAccessIt, typename Policy = policy::typical<typename RandomAccessIt::value_type>>
280
constexpr void from_string(
263,057✔
281
    const CharIt str_first, const CharIt str_last,
282
    const bit_iterator<RandomAccessIt>& bit_first, const bit_iterator<RandomAccessIt>& bit_last,
283
    string::metadata_t meta = string::typical()) {
263,057✔
284
  // TODO: This should be a policy
285
  if (str_first == str_last) {
526,114!
286
    return;  // Nothing to do
×
287
  }
288
  if (std::has_single_bit(meta.base)) {
526,114!
289
    const auto base_bits = std::bit_width(meta.base - 1);
526,112✔
290
    const auto base_from_digits = string::make_from_digit_map(meta.base);
526,112✔
291
    using word_type = uint64_t;
263,056✔
292
    std::vector<word_type> vec;
526,112✔
293
    size_t store_bits = distance(bit_first, bit_last);
526,112✔
294

295
    bit_iterator<RandomAccessIt> bit_it = bit_first;
526,112✔
296
    auto cursor = std::distance(str_first, str_last) - 1;
526,112✔
297
    while ((cursor >= 0) && store_bits) {
1,543,712!
298
      word_type work = 0;
1,017,632✔
299
      size_t bits = 0;
1,017,632✔
300
      for (; (bits < bitsof<word_type>()) && (cursor >= 0); cursor--) {
13,349,372!
301
        char c = str_first[cursor];
12,331,740✔
302
        // TODO: This should be a policy
303
        if (c >= base_from_digits.size()) {
18,497,610!
304
          continue;
×
305
        }
306
        auto digit = base_from_digits[c];
12,331,740✔
307
        // TODO: This should be a policy
308
        if (~0 == digit) {
12,331,740!
309
          continue;
×
310
        }
311
        work |= (static_cast<word_type>(digit) << bits);
12,331,740✔
312
        bits += base_bits;
12,331,740✔
313
      }
6,165,870✔
314
      if (store_bits < bits) {
1,017,632!
315
        Policy::truncation::template from_integral<word_type, std::dynamic_extent, RandomAccessIt>(
30✔
316
            work, bit_it, bit_last);
15✔
317
        return;
30✔
318
      } else if ((store_bits > bits) && (cursor < 0)) {
1,017,602!
319
        const bit_iterator<word_type*> p_integral(&work);
2✔
320
        bit_it = ::bit::copy(p_integral, p_integral + bits, bit_it);
2✔
321
        // TODO: policy
322
        ::bit::fill(bit_it, bit_last, meta.is_signed ? bit_it[-1] : bit0);  // Clear the rest
2!
323
        return;
2✔
324
      } else if (store_bits >= bits) {
1,017,600!
325
        const bit_iterator<word_type*> p_integral(&work);
1,017,600✔
326
        bit_it = ::bit::copy(p_integral, p_integral + bits, bit_it);
1,017,600✔
327
        store_bits -= bits;
1,017,600✔
328
      }
508,800✔
329
    }
508,816✔
330
  } else {
526,112✔
331
    if (meta.base != 10) {
2!
332
      throw std::runtime_error("Base not implemented");
×
333
    }
334
    using word_type = typename bit_iterator<RandomAccessIt>::word_type;
1✔
335
    std::vector<word_type> vec;
2✔
336
    size_t store_bits = distance(bit_first, bit_last);
2✔
337

338
    // TODO: template with uninitialized_t
339
    ::bit::fill(bit_first, bit_last, bit0);  // Clear the bits first
2✔
340

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

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

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

376
    std::vector<uintptr_t> vec;
377

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

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

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

423
template <string::metadata_t meta = string::typical(), bit_range RangeT, typename Policy = policy::typical<typename std::ranges::iterator_t<RangeT>::value_type>>
424
constexpr void from_string(
263,057✔
425
    const std::string& str,
426
    RangeT&& bits) {
263,057✔
427
  using range_iterator_t = std::ranges::iterator_t<RangeT>;
263,057✔
428
  using RandomAccessIt = typename range_iterator_t::iterator_type;
263,057✔
429
  from_string<meta, std::string::const_iterator, RandomAccessIt, Policy>(str.begin(), str.end(), bits.begin(), bits.end());
526,114✔
430
}
526,114✔
431

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

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

456
}  // namespace bit
457

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

© 2025 Coveralls, Inc