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

PeterCDMcLean / BitLib / 16551717780

27 Jul 2025 01:36PM UTC coverage: 75.189% (-3.3%) from 78.485%
16551717780

Pull #18

github

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

3433 of 5288 branches covered (64.92%)

Branch coverage included in aggregate %.

291 of 396 new or added lines in 12 files covered. (73.48%)

56 existing lines in 2 files now uncovered.

2519 of 2628 relevant lines covered (95.85%)

30463931.37 hits per line

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

63.73
/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 <string>
16

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

24
namespace bit {
25

26
namespace string {
27

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

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

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

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

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

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

117
struct metadata_t {
118
  size_t base;
119
  bool is_signed;
120
  std::endian endian;
121
  bool str_sign_extend_zeros;
122
};
123

124
constexpr metadata_t typical(size_t base = 10, bool str_sign_extend_zeros = false) {
125
  return {
126
      .base = base,
127
      .is_signed = false,
128
      .endian = std::endian::big,
129
      .str_sign_extend_zeros = str_sign_extend_zeros};
130
}
131

132
}  // namespace string
133

134
template <typename RandomAccessIt>
135
constexpr std::string to_string(
5✔
136
    const bit_iterator<RandomAccessIt>& first,
137
    const bit_iterator<RandomAccessIt>& last,
138
    string::metadata_t meta = string::typical(),
139
    std::string prefix = "") {
5✔
140
  if (std::has_single_bit(meta.base)) {
10!
141
    const auto base_bits = std::bit_width(meta.base - 1);
8✔
142

143
    int skip_leading_bits = meta.str_sign_extend_zeros ? 0 : count_msb(first, last, bit0);
8!
144

145
    int str_len = (distance(first, last) - skip_leading_bits);
8✔
146
    str_len = (str_len + base_bits - 1) / base_bits;  // Round up to nearest base digit
8✔
147
    if (0 == str_len) {
8!
UNCOV
148
      return prefix + "0";
×
UNCOV
149
    }
150
    std::string& str = prefix;
8✔
151
    str.resize(str.length() + str_len);
8✔
152

153
    const auto base_digits = string::make_digit_map(meta.base);
8✔
154

155
    return accumulate(
12✔
156
        policy::AccumulateNoInitialSubword{},
4✔
157
        first, last - skip_leading_bits, (str.data() + str_len),
12✔
158
        [meta, base_bits, base_digits](char* acc, auto word, const size_t bits = bitsof<decltype(word)>()) {
8✔
159
          const int characters = ((bits + base_bits - 1) / base_bits);
8✔
160
          acc -= characters;
8✔
161
          for (int i = characters - 1; i >= 0; i--) {
72!
162
            acc[i] = base_digits[word & (meta.base - 1)];
64✔
163
            word >>= base_bits;
64✔
164
          }
32✔
165
          return acc;
8✔
166
        });
20✔
167
  } else {
4✔
168
    if (meta.base > 10) {
2!
NEW
169
      throw std::runtime_error("Base not implemented");
×
NEW
170
    }
171
    using word_type = typename bit_iterator<RandomAccessIt>::word_type;
1✔
172
    size_t store_bits = distance(first, 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(first, last, bit_it, base);
2✔
179
    std::string str;
2✔
180
    str.push_back(static_cast<char>(remainder + '0'));
2✔
181

182
    while (::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
      str.push_back(static_cast<char>(remainder + '0'));
4✔
185
    }
2✔
186
    std::reverse(str.begin(), str.end());
2✔
187
    return prefix + str;
2✔
188
  }
2✔
189
}
5✔
190

191
template <string::metadata_t meta = string::typical(), typename RandomAccessIt>
192
constexpr std::string to_string(
5✔
193
    const bit_iterator<RandomAccessIt>& first,
194
    const bit_iterator<RandomAccessIt>& last,
195
    std::string prefix = "") {
5✔
196
  static_assert(meta.endian == std::endian::big, "Only bit big endian support (MSB on the left)");
5✔
197
  return to_string(first, last, meta, std::move(prefix));
15✔
198
}
5✔
199

200
template <string::metadata_t meta = string::typical()>
201
constexpr std::string to_string(const bit_sized_range auto& bits, std::string prefix = "") {
10✔
202
  return to_string<meta>(bits.begin(), bits.end(), prefix);
15✔
203
}
5✔
204

205
template <typename RandomAccessIt, typename Policy = policy::typical<typename RandomAccessIt::value_type>>
206
constexpr void from_string(
3✔
207
    const char* str_first, const char* str_last,
208
    const bit_iterator<RandomAccessIt>& bit_first, const bit_iterator<RandomAccessIt>& bit_last,
209
    string::metadata_t meta = string::typical()) {
3✔
210
  if (std::has_single_bit(meta.base)) {
6!
211
    const auto base_bits = std::bit_width(meta.base - 1);
4✔
212
    const auto base_from_digits = string::make_from_digit_map(meta.base);
4✔
213
    using word_type = uint64_t;
2✔
214
    std::vector<word_type> vec;
4✔
215
    size_t store_bits = distance(bit_first, bit_last);
4✔
216

217
    bit_iterator<RandomAccessIt> bit_it = bit_first;
4✔
218
    str_last--;
4✔
219
    while (str_last >= str_first && store_bits) {
6!
220
      word_type work = 0;
4✔
221
      size_t bits = 0;
4✔
222
      for (; (bits < bitsof<word_type>()) && (str_last >= str_first); str_last--) {
28!
223
        char c = *str_last;
24✔
224
        // TODO: This should be a policy
225
        if (c >= base_from_digits.size()) {
36!
NEW
226
          continue;
×
NEW
227
        }
228
        auto digit = base_from_digits[c];
24✔
229
        // TODO: This should be a policy
230
        if (~0 == digit) {
24!
NEW
231
          continue;
×
NEW
232
        }
233
        work |= (digit << bits);
24✔
234
        bits += base_bits;
24✔
235
      }
12✔
236
      if (store_bits < bits) {
4!
237
        Policy::truncation::template from_integral<word_type, std::dynamic_extent, RandomAccessIt>(
2✔
238
            bit_it, bit_last, work);
1✔
239
        return;
2✔
240
      } else if ((store_bits > bits) && (str_last < str_first)) {
2!
241
        const bit_iterator<word_type*> p_integral(&work);
2✔
242
        bit_it = ::bit::copy(p_integral, p_integral + bits, bit_it);
2✔
243
        Policy::extension::template from_integral<word_type, std::dynamic_extent, RandomAccessIt>(
2✔
244
            bit_it, bit_last, work);
1✔
245
      } else if (store_bits >= bits) {
1!
NEW
246
        const bit_iterator<word_type*> p_integral(&work);
×
NEW
247
        bit_it = ::bit::copy(p_integral, p_integral + bits, bit_it);
×
NEW
248
      }
249
    }
2✔
250
  } else {
4✔
251
    if (meta.base != 10) {
2!
NEW
252
      throw std::runtime_error("Base not implemented");
×
NEW
253
    }
254
    using word_type = typename bit_iterator<RandomAccessIt>::word_type;
1✔
255
    std::vector<word_type> vec;
2✔
256
    size_t store_bits = distance(bit_first, bit_last);
2✔
257

258
    // TODO: template with uninitialized_t
259
    ::bit::fill(bit_first, bit_last, bit0);  // Clear the bits first
2✔
260

261
    while (str_first != str_last) {
8!
262
      unsigned char c = (*str_first - '0');
6✔
263
      if (c <= 9) {
6!
264
        auto overflow_mult = ::bit::multiplication(bit_first, bit_last, word_type{10});
6✔
265
        auto overflow_add = ::bit::addition(bit_first, bit_last, c);
6✔
266
        if (overflow_mult || overflow_add) {
6!
267
          //Policy::truncation::template overflow(bit_first, bit_last);
NEW
268
          return;
×
NEW
269
        }
270
      }
3✔
271
      str_first++;
6✔
272
    }
3✔
273
    //Policy::extension::template extend(bit_first, bit_last);
274
  }
2✔
275
}
3✔
276

277
template <string::metadata_t meta = string::typical(),
278
          typename RandomAccessIt,
279
          typename Policy = policy::typical<typename RandomAccessIt::value_type>>
280
constexpr void from_string(
3✔
281
    const char* str_first, const char* str_last,
282
    const bit_iterator<RandomAccessIt>& bit_first, const bit_iterator<RandomAccessIt>& bit_last) {
3✔
283
  static_assert(meta.endian == std::endian::big, "Only bit big endian support (MSB on the left)");
3✔
284
  from_string<RandomAccessIt, Policy>(str_first, str_last, bit_first, bit_last, meta);
6✔
285
}
6✔
286

287
constexpr std::vector<uintptr_t> from_string(
NEW
288
    const char* first, const char* last, string::metadata_t meta = string::typical()) {
NEW
289
  if (std::has_single_bit(meta.base)) {
NEW
290
    const auto base_bits = std::bit_width(meta.base - 1);
NEW
291
    const auto base_from_digits = string::make_from_digit_map(meta.base);
UNCOV
292

NEW
293
    std::vector<uintptr_t> vec;
UNCOV
294

UNCOV
295
    last--;
UNCOV
296
    while (last >= first) {
NEW
297
      uintptr_t work = 0;
UNCOV
298
      size_t bits = 0;
NEW
299
      for (; (bits < bitsof<uintptr_t>()) && (last >= first); last--) {
UNCOV
300
        char c = *last;
UNCOV
301
        // TODO: This should be a policy
UNCOV
302
        if (c >= base_from_digits.size()) {
UNCOV
303
          continue;
UNCOV
304
        }
UNCOV
305
        auto digit = base_from_digits[c];
UNCOV
306
        // TODO: This should be a policy
UNCOV
307
        if (~0 == digit) {
UNCOV
308
          continue;
UNCOV
309
        }
UNCOV
310
        work |= (digit << bits);
UNCOV
311
        bits += base_bits;
UNCOV
312
      }
UNCOV
313
      if (bits) {
NEW
314
        vec.push_back(work);
UNCOV
315
      }
UNCOV
316
    }
UNCOV
317
    return vec;
UNCOV
318
  } else {
UNCOV
319
    //from_string base 10 not implemented yet;
NEW
320
    return {};
UNCOV
321
  }
UNCOV
322
}
323

324
template <string::metadata_t meta = string::typical()>
325
constexpr std::vector<uintptr_t> from_string(
326
    const char* first, const char* last) {
327
  static_assert(meta.endian == std::endian::big, "Only bit big endian support (MSB on the left)");
328
  return from_string(first, last, meta);
329
}
330

331
template <string::metadata_t meta = string::typical(), typename RandomAccessIt, typename Policy = policy::typical<typename RandomAccessIt::value_type>>
332
constexpr void from_string(
333
    const std::string& str,
334
    const bit_iterator<RandomAccessIt>& bit_first, const bit_iterator<RandomAccessIt>& bit_last) {
335
  static_assert(meta.endian == std::endian::big, "Only bit big endian support (MSB on the left)");
336
  from_string<meta, RandomAccessIt, Policy>(str.c_str(), str.c_str() + str.length(), bit_first, bit_last);
337
}
338

339
template <string::metadata_t meta = string::typical(), bit_range RangeT, typename Policy = policy::typical<typename std::ranges::iterator_t<RangeT>::value_type>>
340
constexpr void from_string(
3✔
341
    const std::string& str,
342
    RangeT& bits) {
3✔
343
  using range_iterator_t = std::ranges::iterator_t<RangeT>;
3✔
344
  using RandomAccessIt = typename range_iterator_t::iterator_type;
3✔
345
  from_string<meta, RandomAccessIt, Policy>(str.c_str(), str.c_str() + str.length(), bits.begin(), bits.end());
6✔
346
}
6✔
347

348
template <typename RandomAccessIt, typename Policy = policy::typical<typename RandomAccessIt::value_type>>
349
constexpr void from_string(
350
    const std::string& str,
351
    const bit_iterator<RandomAccessIt>& bit_first, const bit_iterator<RandomAccessIt>& bit_last,
352
    string::metadata_t meta = string::typical()) {
353
  from_string<RandomAccessIt, Policy>(
354
      str.c_str(), str.c_str() + str.length(),
355
      bit_first, bit_last,
356
      meta);
357
}
358

359
template <bit_range RangeT, typename Policy = policy::typical<typename std::ranges::iterator_t<RangeT>::value_type>>
360
constexpr void from_string(
361
    const std::string& str,
362
    RangeT& bits,
363
    string::metadata_t meta = string::typical()) {
364
  using range_iterator_t = std::ranges::iterator_t<RangeT>;
365
  using RandomAccessIt = typename range_iterator_t::iterator_type;
366
  from_string<RandomAccessIt, Policy>(
367
      str.c_str(), str.c_str() + str.length(),
368
      bits.begin(), bits.end(),
369
      meta);
370
}
371

372
}  // namespace bit
373

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