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

PeterCDMcLean / BitLib / 14917997084

08 May 2025 11:22PM UTC coverage: 97.032% (-0.004%) from 97.036%
14917997084

Pull #7

github

web-flow
Merge 0e6ecb85b into 701068a50
Pull Request #7: Werror option fix wall

1275 of 1314 relevant lines covered (97.03%)

16656556.43 hits per line

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

98.47
/include/bitlib/bit-algorithms/shift.hpp
1
// ================================ SHIFT ================================== //
2
// Project:         The Experimental Bit Algorithms Library
3
// Name:            shift.hpp
4
// Description:     Implementation of shift_left and shift_right
5
// Contributor(s):  Bryce Kille [2019]
6
// License:         BSD 3-Clause License
7
// ========================================================================== //
8
#ifndef _SHIFT_HPP_INCLUDED
9
#define _SHIFT_HPP_INCLUDED
10
// ========================================================================== //
11

12

13
// ================================ PREAMBLE ================================ //
14
// C++ standard library
15
#include <algorithm>
16
#include <iterator>
17
// Project sources
18
// Third-party libraries
19
#ifdef BITLIB_HWY
20
#include "hwy/highway.h"
21
HWY_BEFORE_NAMESPACE();
22
#endif
23
// Miscellaneous
24
#define is_aligned(POINTER, BYTE_COUNT) \
25
    (((uintptr_t)(const void *)(POINTER)) % (BYTE_COUNT) == 0)
26

27
#if __cplusplus >= 202002L
28
#define STD_SHIFT_RIGHT(FIRST, LAST, N) std::shift_right(FIRST, LAST, N)
29
#define STD_SHIFT_LEFT(FIRST, LAST, N) std::shift_left(FIRST, LAST, N)
30
#else
31
#define STD_SHIFT_RIGHT(FIRST, LAST, N) word_shift_right(FIRST, LAST, N)
32
#define STD_SHIFT_LEFT(FIRST, LAST, N) word_shift_left(FIRST, LAST, N)
33
#endif
34

35
namespace bit {
36

37
#ifdef BITLIB_HWY
38
namespace hn = hwy::HWY_NAMESPACE;
39
#endif
40
// ========================================================================== //
41

42

43

44
// --------------------------- Shift Algorithms ----------------------------- //
45

46
template <class RandomAccessIt>
47
bit_iterator<RandomAccessIt> shift_left(
11,105✔
48
        bit_iterator<RandomAccessIt> first,
49
        bit_iterator<RandomAccessIt> last,
50
        typename bit_iterator<RandomAccessIt>::difference_type n
51
) {
52
    // Assertions
53
     _assert_range_viability(first, last);
11,105✔
54

55
    // Types and constants
56
    using word_type = typename bit_iterator<RandomAccessIt>::word_type;
57
    using size_type = typename bit_iterator<RandomAccessIt>::size_type;
58
    using difference_type = typename bit_iterator<RandomAccessIt>::difference_type;
59
    constexpr size_type digits = binary_digits<word_type>::value;
11,105✔
60

61
    // Initialization
62
    auto d = bit::distance(first, last);
11,105✔
63
    const bool is_first_aligned = first.position() == 0;
11,105✔
64
    const bool is_last_aligned = last.position() == 0;
11,105✔
65
    auto middle = first + n;
11,105✔
66

67
    // Out of range cases
68
    if (n <= 0) return last;
11,105✔
69
    if (n >= d) {
11,102✔
70
      //bit::fill(first, last, bit::bit0);
71
      return first;
169✔
72
    }
73

74
    // Single word case
75
    // Triggered if all relevant bits are in first.base()
76
    if (std::next(first.base(), is_last_aligned) == last.base()) {
21,866✔
77
        *first.base() = _bitblend<word_type>(
389✔
78
                *first.base(),
366✔
79
                ((
80
                    *first.base() & (
366✔
81
                        static_cast<word_type>(-1) >> (
×
82
                            digits - (is_last_aligned ? digits : last.position())
183✔
83
                        )
84
                    )
85
                )) >> n,
366✔
86
                first.position(),
96✔
87
                (is_last_aligned ? digits : last.position()) - first.position()
242✔
88
        );
89
        return first + d - n;
183✔
90
    }
91

92
    // Triggered if all remaining bits can fit in a word
93
    if (d - n <= static_cast<typename bit_iterator<RandomAccessIt>::difference_type>(digits))
10,750✔
94
    {
95
      word_type new_word = get_word<word_type>(middle, d - n);
3,027✔
96
      write_word<word_type>(new_word, first, d - n);
3,027✔
97
      return first + d - n;
3,027✔
98
    }
99
    // Multiple word case
100
    // These values are needed for future use if changing direction implementation
101
    // word_type first_value = *first.base();
102
    // word_type last_value = !is_last_aligned ? *last.base() : 0;
103

104
    // Align first
105
    if (!is_first_aligned) {
7,723✔
106
      if (first.position() >= middle.position()) {
2,952✔
107
        *first.base() = _bitblend<word_type>(
968✔
108
            *first.base(),
952✔
109
            (*middle.base()) << (first.position() - middle.position()),
1,428✔
110
            first.position(),
460✔
111
            digits - first.position());
476✔
112
      } else {
113
        const int n1 = digits - middle.position();
2,476✔
114
        const int n2 = digits - first.position() - n1;
2,476✔
115
        *first.base() = _bitblend<word_type>(
6,404✔
116
            *first.base(),
4,952✔
117
            (*middle.base()) >> (middle.position() - first.position()),
7,428✔
118
            first.position(),
1,024✔
119
            n1);
120
        *first.base() = _bitblend<word_type>(
4,952✔
121
            *first.base(),
4,952✔
122
            (*std::next(middle.base())) << (digits - n2),
9,904✔
123
            first.position() + n1,
2,476✔
124
            n2);
125
      }
126
      const int shifted = std::min<difference_type>(d - n, (digits - first.position()));
2,952✔
127
      first += shifted;
2,952✔
128
      middle += shifted;
2,952✔
129
    }
130
    if (middle.base() == last.base())
7,723✔
131
    {
132
        const int bits_left = last.position() - middle.position();
1,609✔
133
        if (bits_left > 0)
1,609✔
134
        {
135
            *first.base() = _bitblend<word_type>(
4,827✔
136
                    *first.base(),
3,218✔
137
                    *middle.base() >> middle.position(),
6,436✔
138
                    0,
139
                    bits_left
140
            );
141
            first += bits_left;
1,609✔
142
        }
143
        // https://en.cppreference.com/w/cpp/algorithm/shift
144
        // "Elements that are in the original range but not the new range
145
        // are left in a valid but unspecified state."
146
        //
147
        //bit::fill(first, last, bit::bit0);
148
        return first;
1,609✔
149
    }
150

151
    // More initialization
152
    d = bit::distance(first, last);
6,114✔
153
    const size_type word_shifts = n / digits;
6,114✔
154
    const size_type offset = middle.position();
6,114✔
155

156
    // At this point, first is aligned
157
    if (offset == 0)
6,114✔
158
    {
159
      first = bit::bit_iterator<RandomAccessIt>(
50✔
160
          STD_SHIFT_LEFT(first.base(),
161
                         last.base(),
162
                         word_shifts),
163
          0);
164
      if (!is_last_aligned) {
50✔
165
        write_word<word_type>(*last.base(), first, last.position());
50✔
166
        first += last.position();
50✔
167
      }
168
        // https://en.cppreference.com/w/cpp/algorithm/shift
169
        // "Elements that are in the original range but not the new range
170
        // are left in a valid but unspecified state."
171
        //
172
        //bit::fill(first, last, bit::bit0);
173
        return first;
50✔
174
    }
175

176
    // Shift bit sequence to the lsb
177
#ifdef BITLIB_HWY
178
    // Align to 64 bit boundary
179
    while (std::next(middle.base()) < last.base() && !is_aligned(&*first.base(), 64)) {
180
        *first.base() = _shrd<word_type>(*middle.base(), *std::next(middle.base()), offset);
181
        first += digits;
182
        middle += digits;
183
    }
184

185
    const hn::ScalableTag<word_type> d_tag;
186
    while (std::distance(middle.base(), last.base()) >= hn::Lanes(d_tag) + 10 + !is_last_aligned)
187
    {
188
        const auto v = hn::ShiftRightSame(hn::LoadU(d_tag, &*middle.base()), offset);
189
        const auto v_plus1 = hn::ShiftLeftSame(hn::LoadU(d_tag, &*(middle.base()+1)), digits - offset);
190
        hn::Store(v | v_plus1, d_tag, &*first.base());
191
        first += hn::Lanes(d_tag)*digits;
192
        middle += hn::Lanes(d_tag)*digits;
193
    }
194
#endif
195
    auto first_base = first.base();
6,064✔
196
    auto middle_base = middle.base();
6,064✔
197

198
    while (std::next(middle_base) < last.base()) {
5,545,700✔
199
        *first_base = _shrd<word_type>(*middle_base, *std::next(middle_base), offset);
5,533,572✔
200
        first_base++;
2,766,786✔
201
        middle_base++;;
2,766,786✔
202
    }
203
    first = bit_iterator<RandomAccessIt>(first_base, 0);
6,064✔
204
    middle = bit_iterator<RandomAccessIt>(middle_base, middle.position());
6,064✔
205

206
    // If middle is now penultimate word
207
    if (std::next(middle.base()) == last.base())
12,128✔
208
    {
209
        *first.base() = _bitblend<word_type>(
12,128✔
210
                *first.base(),
12,128✔
211
                *middle.base() >> offset,
18,192✔
212
                0,
213
                digits - offset
1,461✔
214
        );
215
        first += digits - offset;
6,064✔
216
        middle += digits - offset;
6,064✔
217
    }
218

219
    if (!is_last_aligned)
6,064✔
220
    {
221
        const difference_type bits_left = last.position() - middle.position();
1,247✔
222
        const word_type new_word = get_word<word_type>(middle, bits_left);
1,247✔
223
        write_word<word_type>(new_word, first, bits_left);
1,247✔
224
        first += bits_left;
1,247✔
225
    }
226

227
    //bit::fill(first, last, bit::bit0);
228
    return first;
6,064✔
229
}
230

231
template <class RandomAccessIt>
232
bit_iterator<RandomAccessIt> shift_right(
27,302✔
233
        bit_iterator<RandomAccessIt> first,
234
        bit_iterator<RandomAccessIt> last,
235
        typename bit_iterator<RandomAccessIt>::difference_type n
236
) {
237
    // Types and constants
238
    using word_type = typename bit_iterator<RandomAccessIt>::word_type;
239
    using size_type = typename bit_iterator<RandomAccessIt>::size_type;
240

241
    // Initialization
242
    const bool is_last_aligned = last.position() == 0;
27,302✔
243
    constexpr auto digits = binary_digits<word_type>::value;
27,302✔
244
    auto d = bit::distance(first, last);
27,302✔
245
    bit_iterator<RandomAccessIt> middle = last - n;
27,302✔
246

247
    // Out of range cases
248
    if (n <= 0) return first;
27,302✔
249
    else if (n >= d) return last;
27,273✔
250

251
    // Single word case
252
    if (std::next(first.base(), is_last_aligned) == last.base()) {
35,554✔
253
        *first.base() = _bitblend<word_type>(
2,632✔
254
                *first.base(),
2,672✔
255
                (
256
                    *first.base() & (
2,672✔
257
                        static_cast<word_type>(-1) << first.position()
×
258
                    )
259
                ) << n,
2,672✔
260
                first.position(),
1,099✔
261
                (is_last_aligned ? digits : last.position()) - first.position()
1,841✔
262
        );
263
        return first + n;
1,336✔
264
    }
265

266
    // Align last
267
    if (last.position() != 0)
16,441✔
268
    {
269
      const size_type bits_to_align = std::min<size_type>(
14,929✔
270
          last.position(),
29,858✔
271
          bit::distance(first, middle));
29,858✔
272
      const word_type word_to_write = get_word<word_type>(
14,929✔
273
          middle - bits_to_align,
274
          bits_to_align);
275
      write_word<word_type>(
14,929✔
276
          word_to_write,
277
          last - bits_to_align,
278
          bits_to_align);
279
      middle -= bits_to_align;
14,929✔
280
      last -= bits_to_align;
14,929✔
281

282
      // Nothing left to do
283
      if (middle == first)
14,929✔
284
        return first + n;
124✔
285
    }
286

287
    // More initialization
288
    const size_type word_shifts = n / digits;
16,317✔
289
    const size_type offset = middle.position();
16,317✔
290

291
    // Shift bit sequence to the msb
292
    if (offset == 0) {
16,317✔
293
        STD_SHIFT_RIGHT(
82✔
294
            first.base(),
295
            last.base(),
296
            word_shifts);
297
        // https://en.cppreference.com/w/cpp/algorithm/shift
298
        // "Elements that are in the original range but not the new range
299
        // are left in a valid but unspecified state."
300
        //
301
        //bit::fill(first, new_first, bit::bit0);
302
        return first + n;
82✔
303
    }
304

305
    if (bit::distance(first, middle) >= static_cast<typename bit_iterator<RandomAccessIt>::difference_type>(digits))
16,235✔
306
    {
307
#ifdef BITLIB_HWY
308
        // Align to 64 bit boundary
309
        const hn::ScalableTag<word_type> d;
310
        while (std::prev(middle.base()) > first.base() && !is_aligned(&*(last.base() - hn::Lanes(d)), 64)) {
311
            *std::prev(last.base()) = _shrd<word_type>(*std::prev(middle.base()), *middle.base(), offset);
312
            last -= digits;
313
            middle -= digits;
314
        }
315

316
        while (std::distance(first.base(), middle.base()) > hn::Lanes(d) + 1)
317
        {
318
            const auto v = hn::ShiftRightSame(
319
                    hn::LoadU(d, &*(middle.base() - hn::Lanes(d))),
320
                    offset);
321
            const auto v_plus1 = hn::ShiftLeftSame(
322
                    hn::LoadU(d, &*(middle.base() - hn::Lanes(d) + 1)),
323
                    digits - offset);
324
            hn::Store(v | v_plus1, d, &*(last.base() - hn::Lanes(d)));
325

326
            last -= digits * hn::Lanes(d);
327
            middle -= digits * hn::Lanes(d);
328
        }
329
#endif
330

331
        auto last_base_prev   = std::prev(last.base());
13,754✔
332
        auto middle_base_prev = std::prev(middle.base());
13,754✔
333

334
        while (middle_base_prev > first.base()) {
2,483,646✔
335
            *last_base_prev = _shrd<word_type>(*middle_base_prev, *std::next(middle_base_prev), offset);
4,939,784✔
336
            last_base_prev--;
2,469,892✔
337
            middle_base_prev--;
2,469,892✔
338
        }
339

340
        if (first.position() <= middle.position())
13,754✔
341
        {
342
            *last_base_prev = _shrd<word_type>(*middle_base_prev, *std::next(middle_base_prev), offset);
27,288✔
343
            last_base_prev--;
13,644✔
344
            middle_base_prev--;
13,644✔
345
        }
346

347
        last = bit_iterator<RandomAccessIt>(std::next(last_base_prev), last.position());
27,508✔
348
        middle = bit_iterator<RandomAccessIt>(std::next(middle_base_prev), middle.position());
27,508✔
349
    }
350

351
    if (first.position() != middle.position())
16,235✔
352
    {
353
        const size_type bits_to_align = bit::distance(first, middle);
16,079✔
354
        const word_type word_to_write = get_word<word_type>(
16,079✔
355
            first,
356
            bits_to_align);
357
        write_word<word_type>(
16,079✔
358
            word_to_write,
359
            last - bits_to_align,
360
            bits_to_align);
361
    }
362

363
    return first + n;
16,235✔
364
}
365
// -------------------------------------------------------------------------- //
366

367

368

369
// ========================================================================== //
370
} // namespace bit
371
#ifdef BITLIB_HWY
372
HWY_AFTER_NAMESPACE();
373
#endif
374
#endif // _SHIFT_HPP_INCLUDED
375
// ========================================================================== //
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