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

PeterCDMcLean / BitLib / 16535147226

26 Jul 2025 02:33AM UTC coverage: 77.05% (-1.4%) from 78.485%
16535147226

Pull #18

github

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

3406 of 5080 branches covered (67.05%)

Branch coverage included in aggregate %.

248 of 295 new or added lines in 12 files covered. (84.07%)

30 existing lines in 2 files now uncovered.

2476 of 2554 relevant lines covered (96.95%)

31346596.33 hits per line

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

77.33
/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
template <std::size_t Base>
UNCOV
41
constexpr auto make_from_digit_map() {
UNCOV
42
  static_assert((Base >= 2) && ((Base & (Base - 1)) == 0), "Base must be power of 2 >= 2");
UNCOV
43
  static_assert(Base <= 64, "Base too large for simple char mapping");
UNCOV
44

NEW
45
  ::std::array<char, 128> map{};
UNCOV
46
  for (std::size_t i = 0; i < 128; ++i) {
UNCOV
47
    map[i] = ~0;
UNCOV
48
    if (i >= '0' && i <= '9') {
UNCOV
49
      map[i] = i - '0';
UNCOV
50
    }
UNCOV
51
    if (i >= 'a' && i <= 'z') {
UNCOV
52
      map[i] = (i - 'a') + 10;
UNCOV
53
    }
UNCOV
54
    if (i >= 'A' && i <= 'Z') {
UNCOV
55
      map[i] = (i - 'A') + 10;
UNCOV
56
    }
UNCOV
57
  }
UNCOV
58
  return map;
UNCOV
59
}
60

61
struct metadata_t {
62
  size_t base;
63
  bool is_signed;
64
  std::endian endian;
65
  bool str_sign_extend_zeros;
66
};
67

68
constexpr metadata_t typical(size_t base = 10, bool str_sign_extend_zeros = false) {
69
  return {
70
      .base = base,
71
      .is_signed = false,
72
      .endian = std::endian::big,
73
      .str_sign_extend_zeros = str_sign_extend_zeros};
74
}
75

76
}  // namespace string
77

78
template <string::metadata_t meta = string::typical(), typename RandomAccessIt>
79
constexpr std::string to_string(const bit_iterator<RandomAccessIt>& first, const bit_iterator<RandomAccessIt>& last, std::string prefix = "") {
8✔
80
  static_assert(meta.endian == std::endian::big, "Only bit big endian support (MSB on the left)");
4✔
81
  if constexpr (std::has_single_bit(meta.base)) {
4✔
82
    constexpr const auto base_bits = std::bit_width(meta.base - 1);
8✔
83

84
    int skip_leading_bits = meta.str_sign_extend_zeros ? 0 : count_msb(first, last, bit0);
8✔
85

86
    int str_len = (distance(first, last) - skip_leading_bits);
8✔
87
    str_len = (str_len + base_bits - 1) / base_bits;  // Round up to nearest base digit
8✔
88
    if (0 == str_len) {
8!
UNCOV
89
      return prefix + "0";
×
UNCOV
90
    }
91
    std::string& str = prefix;
8✔
92
    str.resize(str.length() + str_len);
8✔
93

94
    static constexpr auto base_digits = string::make_digit_map<meta.base>();
4✔
95

96
    return accumulate(
12✔
97
        policy::AccumulateNoInitialSubword{},
4✔
98
        first, last - skip_leading_bits, (str.data() + str_len),
12✔
99
        [](char* acc, auto word, const size_t bits = bitsof<decltype(word)>()) {
8✔
100
          const int characters = ((bits + base_bits - 1) / base_bits);
8✔
101
          acc -= characters;
8✔
102
          for (int i = characters - 1; i >= 0; i--) {
72✔
103
            acc[i] = base_digits[word & (meta.base - 1)];
64✔
104
            word >>= base_bits;
64✔
105
          }
32✔
106
          return acc;
8✔
107
        });
16✔
108
  } else {
109
    return "not_implented_yet";
110
  }
111
}
4✔
112

113
template <string::metadata_t meta = string::typical()>
114
constexpr std::string to_string(const bit_sized_range auto& bits, std::string prefix = "") {
8✔
115
  return to_string<meta>(bits.begin(), bits.end(), prefix);
12✔
116
}
4✔
117
#if 0
118
template <string::metadata_t meta = string::typical(), typename Policy = policy::typical<uintptr_t>, typename RandomAccessIt>
119
constexpr size_t pessimistic_bits_for_string(
120
    const char* str_first, const char* str_last) {
121
  const char* it = str_first;
122
  for (; (it != str_last) && (*it == '0'); ++it) {
123
  }
124
  const size_t non_zero_str_len = str_last - it;
125
  if (non_zero_str_len == 0) {
126
    return meta.is_signed ? 1 : 0;  // All zeros
127
  }
128
  if constexpr (std::has_single_bit(meta.base)) {
129
    constexpr const auto base_bits = std::bit_width(meta.base - 1);
130
    static constexpr auto base_from_digits = string::make_from_digit_map<meta.base>();
131
    return non_zero_str_len * base_bits + meta.is_signed;
132
  } else {
133
    constexpr double base_in_base2 = std::log2(meta.base);
134

135
    return static_cast<size_t>(std::floor(base_in_base2 * non_zero_str_len)) + 1 + meta.is_signed;
136
  }
137
}
138

139
template <string::metadata_t meta = string::typical(), typename Policy = policy::typical<uintptr_t>, typename RandomAccessIt>
140
constexpr void from_string(
141
    Policy,
142
    const char* str_first, const char* str_last,
143
    bit_iterator<RandomAccessIt> bit_first, bit_iterator<RandomAccessIt> bit_last) {
144
  const auto str_len = str_last - str_first;
145
  const auto store_bits = distance(bit_first, bit_last);
146
  if constexpr (std::has_single_bit(meta.base)) {
147
    constexpr const auto base_bits = std::bit_width(meta.base - 1);
148
    static constexpr auto base_from_digits = string::make_from_digit_map<meta.base>();
149
    constexpr const auto str_bits = str_len * base_bits;
150
    if (store_bits < str_bits) {
151
      Policy::truncation::template from_string<std::dynamic_extent>(
152
          str_first, str_last, str_cur, bit_first, bit_last);
153
    } else if (store_bits > str_bits) {
154
      Policy::extension::template from_string<std::dynamic_extent>(
155
          str_first, str_last, str_cur, bit_first, bit_last);
156
    } else {
157
      char str_cur = str_last - 1;
158
      bit_iterator<RandomAccessIt> bit_cur = bit_first;
159
      while (str_cur >= str_first) {
160
        str_cur--;
161
      }
162
    }
163
  }
164
}
165
#endif
166

167
template <string::metadata_t meta = string::typical(), typename RandomAccessIt, typename Policy = policy::typical<typename RandomAccessIt::value_type>>
168
constexpr void from_string(
3✔
169
    const char* str_first, const char* str_last,
170
    const bit_iterator<RandomAccessIt>& bit_first, const bit_iterator<RandomAccessIt>& bit_last) {
3✔
171
  static_assert(meta.endian == std::endian::big, "Only bit big endian support (MSB on the left)");
3✔
172
  if constexpr (std::has_single_bit(meta.base)) {
3✔
173
    constexpr const auto base_bits = std::bit_width(meta.base - 1);
4✔
174
    static constexpr auto base_from_digits = string::make_from_digit_map<meta.base>();
2✔
175
    using word_type = uint64_t;
2✔
176
    std::vector<word_type> vec;
4✔
177
    size_t store_bits = distance(bit_first, bit_last);
4✔
178

179
    bit_iterator<RandomAccessIt> bit_it = bit_first;
4✔
180
    str_last--;
4✔
181
    while (str_last >= str_first && store_bits) {
6!
182
      word_type work = 0;
4✔
183
      size_t bits = 0;
4✔
184
      for (; (bits < bitsof<word_type>()) && (str_last >= str_first); str_last--) {
28!
185
        char c = *str_last;
24✔
186
        // TODO: This should be a policy
187
        if (c >= base_from_digits.size()) {
36!
NEW
188
          continue;
×
NEW
189
        }
190
        auto digit = base_from_digits[c];
24✔
191
        // TODO: This should be a policy
192
        if (~0 == digit) {
24!
NEW
193
          continue;
×
NEW
194
        }
195
        work |= (digit << bits);
24✔
196
        bits += base_bits;
24✔
197
      }
12✔
198
      if (store_bits < bits) {
4!
199
        Policy::truncation::template from_integral<word_type, std::dynamic_extent, RandomAccessIt>(
2✔
200
            bit_it, bit_last, work);
1✔
201
        return;
2✔
202
      } else if ((store_bits > bits) && (str_last < str_first)) {
2!
203
        const bit_iterator<word_type*> p_integral(&work);
2✔
204
        bit_it = ::bit::copy(p_integral, p_integral + bits, bit_it);
2✔
205
        Policy::extension::template from_integral<word_type, std::dynamic_extent, RandomAccessIt>(
2✔
206
            bit_it, bit_last, work);
1✔
207
      } else if (store_bits >= bits) {
1!
NEW
208
        const bit_iterator<word_type*> p_integral(&work);
×
NEW
209
        bit_it = ::bit::copy(p_integral, p_integral + bits, bit_it);
×
NEW
210
      }
211
    }
2✔
212
  } else {
4✔
213
    if (meta.base != 10) {
2✔
NEW
214
      throw std::runtime_error("Base not implemented");
×
NEW
215
    }
216
    using word_type = typename bit_iterator<RandomAccessIt>::word_type;
1✔
217
    std::vector<word_type> vec;
2✔
218
    size_t store_bits = distance(bit_first, bit_last);
2✔
219

220
    // TODO: template with uninitialized_t
221
    ::bit::fill(bit_first, bit_last, bit0);  // Clear the bits first
2✔
222

223
    while (str_first != str_last) {
8✔
224
      unsigned char c = (*str_first - '0');
6✔
225
      if (c <= 9) {
6!
226
        auto overflow_mult = ::bit::multiplication(bit_first, bit_last, word_type{10});
6✔
227
        auto overflow_add = ::bit::addition(bit_first, bit_last, c);
6✔
228
        if (overflow_mult || overflow_add) {
6!
229
          //Policy::truncation::template overflow(bit_first, bit_last);
NEW
230
          return;
×
NEW
231
        }
232
      }
3✔
233
      str_first++;
6✔
234
    }
3✔
235
    //Policy::extension::template extend(bit_first, bit_last);
236
  }
2✔
237
}
3✔
238

239
template <string::metadata_t meta = string::typical()>
240
constexpr std::vector<uintptr_t> from_string(
241
    const char* first, const char* last) {
242
  static_assert(meta.endian == std::endian::big, "Only bit big endian support (MSB on the left)");
243
  if constexpr (std::has_single_bit(meta.base)) {
244
    constexpr const auto base_bits = std::bit_width(meta.base - 1);
245
    static constexpr auto base_from_digits = string::make_from_digit_map<meta.base>();
246

247
    std::vector<uintptr_t> vec;
248

249
    last--;
250
    while (last >= first) {
251
      uintptr_t work = 0;
252
      size_t bits = 0;
253
      for (; (bits < bitsof<uintptr_t>()) && (last >= first); last--) {
254
        char c = *last;
255
        // TODO: This should be a policy
256
        if (c >= base_from_digits.size()) {
257
          continue;
258
        }
259
        auto digit = base_from_digits[c];
260
        // TODO: This should be a policy
261
        if (~0 == digit) {
262
          continue;
263
        }
264
        work |= (digit << bits);
265
        bits += base_bits;
266
      }
267
      if (bits) {
268
        vec.push_back(work);
269
      }
270
    }
271
    return vec;
272
  } else {
273
    //from_string base 10 not implemented yet;
274
    return {};
275
  }
276
}
277

278
template <string::metadata_t meta = string::typical(), typename RandomAccessIt, typename Policy = policy::typical<typename RandomAccessIt::value_type>>
279
constexpr void from_string(
280
    const std::string& str,
281
    const bit_iterator<RandomAccessIt>& bit_first, const bit_iterator<RandomAccessIt>& bit_last) {
282
  from_string<meta, RandomAccessIt, Policy>(str.c_str(), str.c_str() + str.length(), bit_first, bit_last);
283
}
284

285
template <string::metadata_t meta = string::typical(), bit_range RangeT, typename Policy = policy::typical<typename std::ranges::iterator_t<RangeT>::value_type>>
286
constexpr void from_string(
3✔
287
    const std::string& str,
288
    RangeT& bits) {
3✔
289
  using range_iterator_t = std::ranges::iterator_t<RangeT>;
3✔
290
  using RandomAccessIt = typename range_iterator_t::iterator_type;
3✔
291
  from_string<meta, RandomAccessIt, Policy>(str.c_str(), str.c_str() + str.length(), bits.begin(), bits.end());
6✔
292
}
6✔
293

294
}  // namespace bit
295

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