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

PeterCDMcLean / BitLib / 14814681695

03 May 2025 09:18PM UTC coverage: 98.083% (+0.4%) from 97.685%
14814681695

Pull #1

github

web-flow
Merge 91c2078e2 into ee3313bd8
Pull Request #1: Presets and fix sanitize

25 of 25 new or added lines in 2 files covered. (100.0%)

3 existing lines in 2 files now uncovered.

1279 of 1304 relevant lines covered (98.08%)

16917885.45 hits per line

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

98.5
/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(
8,951✔
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);
8,951✔
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;
8,951✔
60

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

67
    // Out of range cases
68
    if (n <= 0) return last;
8,951✔
69
    if (n >= d) {
8,937✔
70
      //bit::fill(first, last, bit::bit0);
71
      return first;
304✔
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()) {
17,266✔
77
        *first.base() = _bitblend<word_type>(
201✔
78
                *first.base(),
216✔
79
                ((
80
                    *first.base() & (
216✔
UNCOV
81
                        static_cast<word_type>(-1) >> (
×
82
                            digits - (is_last_aligned ? digits : last.position())
108✔
83
                        )
84
                    )
85
                )) >> n,
216✔
86
                first.position(),
56✔
87
                (is_last_aligned ? digits : last.position()) - first.position()
128✔
88
        );
89
        return first + d - n;
108✔
90
    }
91

92
    // Triggered if all remaining bits can fit in a word
93
    if (d - n <= digits)
8,525✔
94
    {
95
      word_type new_word = get_word<word_type>(middle, d - n);
1,618✔
96
      write_word<word_type>(new_word, first, d - n);
1,618✔
97
      return first + d - n;
1,618✔
98
    }
99
    // Multiple word case
100
    word_type first_value = *first.base();
6,907✔
101
    word_type last_value = !is_last_aligned ? *last.base() : 0;
6,907✔
102

103
    // Align first
104
    if (!is_first_aligned) {
6,907✔
105
      if (first.position() >= middle.position()) {
2,211✔
106
        *first.base() = _bitblend<word_type>(
1,121✔
107
            *first.base(),
968✔
108
            (*middle.base()) << (first.position() - middle.position()),
1,452✔
109
            first.position(),
331✔
110
            digits - first.position());
484✔
111
      } else {
112
        const int n1 = digits - middle.position();
1,727✔
113
        const int n2 = digits - first.position() - n1;
1,727✔
114
        *first.base() = _bitblend<word_type>(
4,465✔
115
            *first.base(),
3,454✔
116
            (*middle.base()) >> (middle.position() - first.position()),
5,181✔
117
            first.position(),
716✔
118
            n1);
119
        *first.base() = _bitblend<word_type>(
3,454✔
120
            *first.base(),
3,454✔
121
            (*std::next(middle.base())) << (digits - n2),
6,908✔
122
            first.position() + n1,
1,727✔
123
            n2);
124
      }
125
      const int shifted = std::min<difference_type>(d - n, (digits - first.position()));
2,211✔
126
      first += shifted;
2,211✔
127
      middle += shifted;
2,211✔
128
    }
129
    if (middle.base() == last.base())
6,907✔
130
    {
131
        const int bits_left = last.position() - middle.position();
545✔
132
        if (bits_left > 0)
545✔
133
        {
134
            *first.base() = _bitblend<word_type>(
1,635✔
135
                    *first.base(),
1,090✔
136
                    *middle.base() >> middle.position(),
2,180✔
137
                    0,
138
                    bits_left
139
            );
140
            first += bits_left;
545✔
141
        }
142
        // https://en.cppreference.com/w/cpp/algorithm/shift
143
        // "Elements that are in the original range but not the new range
144
        // are left in a valid but unspecified state."
145
        //
146
        //bit::fill(first, last, bit::bit0);
147
        return first;
545✔
148
    }
149

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

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

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

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

197
    while (std::next(middle_base) < last.base()) {
4,270,476✔
198
        *first_base = _shrd<word_type>(*middle_base, *std::next(middle_base), offset);
4,257,820✔
199
        first_base++;
2,128,910✔
200
        middle_base++;;
2,128,910✔
201
    }
202
    first = bit_iterator<RandomAccessIt>(first_base, 0);
6,328✔
203
    middle = bit_iterator<RandomAccessIt>(middle_base, middle.position());
6,328✔
204

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

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

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

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

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

246
    // Out of range cases
247
    if (n <= 0) return first;
29,683✔
248
    else if (n >= d) return last;
29,671✔
249

250
    // Single word case
251
    if (std::next(first.base(), is_last_aligned) == last.base()) {
40,204✔
252
        *first.base() = _bitblend<word_type>(
1,123✔
253
                *first.base(),
1,134✔
254
                (
255
                    *first.base() & (
1,134✔
UNCOV
256
                        static_cast<word_type>(-1) << first.position()
×
257
                    )
258
                ) << n,
1,134✔
259
                first.position(),
288✔
260
                (is_last_aligned ? digits : last.position()) - first.position()
694✔
261
        );
262
        return first + n;
567✔
263
    }
264

265
    // Align last
266
    if (last.position() != 0)
19,535✔
267
    {
268
      const size_type bits_to_align = std::min<size_type>(
17,337✔
269
          last.position(),
34,674✔
270
          bit::distance(first, middle));
34,674✔
271
      const word_type word_to_write = get_word<word_type>(
17,337✔
272
          middle - bits_to_align,
273
          bits_to_align);
274
      write_word<word_type>(
17,337✔
275
          word_to_write,
276
          last - bits_to_align,
277
          bits_to_align);
278
      middle -= bits_to_align;
17,337✔
279
      last -= bits_to_align;
17,337✔
280

281
      // Nothing left to do
282
      if (middle == first)
17,337✔
283
        return first + n;
155✔
284
    }
285

286
    // More initialization
287
    const size_type word_shifts = n / digits;
19,380✔
288
    const size_type offset = middle.position();
19,380✔
289

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

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

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

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

333
        auto last_base_prev   = std::prev(last.base());
17,585✔
334
        auto middle_base_prev = std::prev(middle.base());
17,585✔
335

336
        while (middle_base_prev > first.base()) {
3,545,227✔
337
            *last_base_prev = _shrd<word_type>(*middle_base_prev, *std::next(middle_base_prev), offset);
7,055,284✔
338
            last_base_prev--;
3,527,642✔
339
            middle_base_prev--;
3,527,642✔
340
        }
341

342
        if (first.position() <= middle.position())
17,585✔
343
        {
344
            *last_base_prev = _shrd<word_type>(*middle_base_prev, *std::next(middle_base_prev), offset);
34,676✔
345
            last_base_prev--;
17,338✔
346
            middle_base_prev--;
17,338✔
347
        }
348

349
        last = bit_iterator<RandomAccessIt>(std::next(last_base_prev), last.position());
35,170✔
350
        middle = bit_iterator<RandomAccessIt>(std::next(middle_base_prev), middle.position());
35,170✔
351
    }
352

353
    if (first.position() != middle.position())
19,321✔
354
    {
355
        const size_type bits_to_align = bit::distance(first, middle);
19,019✔
356
        const word_type word_to_write = get_word<word_type>(
19,019✔
357
            first,
358
            bits_to_align);
359
        write_word<word_type>(
19,019✔
360
            word_to_write,
361
            last - bits_to_align,
362
            bits_to_align);
363
    }
364

365
    return first + n;
19,321✔
366
}
367
// -------------------------------------------------------------------------- //
368

369

370

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