1#![cfg_attr(
38 all(simd_popcnt_have_sve, any(target_feature = "sve", feature = "std")),
39 feature(stdarch_aarch64_sve)
40)]
41#![cfg_attr(
45 not(any(
46 test,
47 all(
48 feature = "std",
49 any(target_arch = "x86", target_arch = "x86_64"),
50 not(any(target_feature = "avx2", target_feature = "avx512vpopcntdq")),
51 ),
52 all(
53 feature = "std",
54 target_arch = "aarch64",
55 simd_popcnt_have_sve,
56 not(target_feature = "sve"),
57 ),
58 )),
59 no_std
60)]
61
62#[cfg(target_arch = "aarch64")]
63use core::arch::aarch64::*;
64#[cfg(target_arch = "x86")]
66#[allow(unused_imports)]
67use core::arch::x86::*;
68#[cfg(target_arch = "x86_64")]
69#[allow(unused_imports)]
70use core::arch::x86_64::*;
71#[cfg(all(
72 target_arch = "aarch64",
73 simd_popcnt_have_sve,
74 feature = "std",
75 not(target_feature = "sve")
76))]
77use std::arch::is_aarch64_feature_detected;
78
79#[must_use]
94#[inline]
95pub fn popcnt(bytes: &[u8]) -> u64 {
96 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
97 {
98 popcnt_x86(bytes)
99 }
100
101 #[cfg(target_arch = "aarch64")]
102 {
103 popcnt_aarch64(bytes)
104 }
105
106 #[cfg(not(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64")))]
107 {
108 popcnt_scalar(bytes)
109 }
110}
111
112pub trait PopcntExt {
129 #[must_use]
131 fn popcnt(&self) -> u64;
132}
133
134macro_rules! impl_popcnt_ext {
137 ($($t:ty),+ $(,)?) => {$(
138 impl PopcntExt for [$t] {
139 #[inline]
140 fn popcnt(&self) -> u64 {
141 let bytes = unsafe {
145 core::slice::from_raw_parts(
146 self.as_ptr().cast::<u8>(),
147 core::mem::size_of_val(self),
148 )
149 };
150 popcnt(bytes)
151 }
152 }
153 )+};
154}
155
156impl_popcnt_ext!(
157 u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize
158);
159
160#[inline]
168fn tail_u64(rem: &[u8]) -> u64 {
169 let mut v = 0u64;
170 for (j, &b) in rem.iter().enumerate() {
171 v |= (b as u64) << (j * 8);
172 }
173 v
174}
175
176macro_rules! popcnt_scalar_loop {
180 ($bytes:expr) => {{
181 let mut cnt = 0u64;
182 let (chunks, rem) = $bytes.as_chunks::<8>();
183 for chunk in chunks {
184 cnt += u64::from_ne_bytes(*chunk).count_ones() as u64;
185 }
186 if !rem.is_empty() {
187 cnt += tail_u64(rem).count_ones() as u64;
188 }
189 cnt
190 }};
191}
192
193#[allow(dead_code)]
195#[inline]
196fn popcnt_scalar(bytes: &[u8]) -> u64 {
197 popcnt_scalar_loop!(bytes)
198}
199
200#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
205#[inline]
206fn popcnt_x86(bytes: &[u8]) -> u64 {
207 #[cfg(target_feature = "avx512vpopcntdq")]
209 {
210 if bytes.len() >= 40 {
212 unsafe { popcnt_avx512(bytes) }
213 } else {
214 popcnt_scalar_static(bytes)
215 }
216 }
217
218 #[cfg(all(target_feature = "avx2", not(target_feature = "avx512vpopcntdq")))]
220 {
221 let mut cnt = 0u64;
222 let mut rest = bytes;
223 if bytes.len() >= 96 {
226 let n = bytes.len() / 32 * 32;
227 cnt += if bytes.len() >= 1024 {
228 unsafe { popcnt_avx2(&bytes[..n]) }
229 } else {
230 unsafe { popcnt_avx2_medium(&bytes[..n]) }
231 };
232 rest = &bytes[n..];
233 }
234 cnt + popcnt_scalar_static(rest)
235 }
236
237 #[cfg(all(
239 not(any(target_feature = "avx2", target_feature = "avx512vpopcntdq")),
240 feature = "std"
241 ))]
242 {
243 popcnt_x86_runtime(bytes)
244 }
245
246 #[cfg(all(
248 not(any(target_feature = "avx2", target_feature = "avx512vpopcntdq")),
249 not(feature = "std")
250 ))]
251 {
252 popcnt_scalar_static(bytes)
253 }
254}
255
256#[cfg(all(
259 any(target_arch = "x86", target_arch = "x86_64"),
260 any(
261 target_feature = "avx2",
262 target_feature = "avx512vpopcntdq",
263 not(feature = "std")
264 )
265))]
266#[inline]
267fn popcnt_scalar_static(bytes: &[u8]) -> u64 {
268 #[cfg(target_feature = "popcnt")]
269 {
270 unsafe { popcnt_scalar_hw(bytes) }
272 }
273 #[cfg(not(target_feature = "popcnt"))]
274 {
275 popcnt_scalar(bytes)
276 }
277}
278
279#[cfg(all(
282 any(target_arch = "x86", target_arch = "x86_64"),
283 not(any(target_feature = "avx2", target_feature = "avx512vpopcntdq")),
284 feature = "std"
285))]
286#[inline]
287fn has_avx512() -> bool {
288 use core::sync::atomic::{AtomicI32, Ordering};
289 static HAS_AVX512: AtomicI32 = AtomicI32::new(-1);
290 let cached = HAS_AVX512.load(Ordering::Relaxed);
291 if cached != -1 {
292 return cached != 0;
293 }
294 let v = (is_x86_feature_detected!("avx512f")
295 && is_x86_feature_detected!("avx512bw")
296 && is_x86_feature_detected!("avx512vpopcntdq")) as i32;
297 HAS_AVX512.store(v, Ordering::Relaxed);
298 v != 0
299}
300
301#[cfg(all(
304 any(target_arch = "x86", target_arch = "x86_64"),
305 not(any(target_feature = "avx2", target_feature = "avx512vpopcntdq")),
306 feature = "std"
307))]
308#[inline]
309fn popcnt_x86_runtime(bytes: &[u8]) -> u64 {
310 if bytes.len() >= 40 && has_avx512() {
312 return unsafe { popcnt_avx512(bytes) };
313 }
314
315 let mut cnt = 0u64;
316 let mut rest = bytes;
317
318 if bytes.len() >= 96 && is_x86_feature_detected!("avx2") {
324 let n = bytes.len() / 32 * 32;
325 cnt += if bytes.len() >= 1024 {
326 unsafe { popcnt_avx2(&bytes[..n]) }
327 } else {
328 unsafe { popcnt_avx2_medium(&bytes[..n]) }
329 };
330 rest = &bytes[n..];
331 }
332
333 cnt += if is_x86_feature_detected!("popcnt") {
337 #[cfg(target_arch = "x86_64")]
341 {
342 unsafe { popcnt_scalar_asm(rest) }
343 }
344 #[cfg(target_arch = "x86")]
345 {
346 unsafe { popcnt_scalar_hw(rest) }
347 }
348 } else {
349 popcnt_scalar(rest)
350 };
351
352 cnt
353}
354
355#[allow(dead_code)]
359#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
360#[target_feature(enable = "popcnt")]
361#[inline]
362fn popcnt_scalar_hw(bytes: &[u8]) -> u64 {
363 popcnt_scalar_loop!(bytes) }
365
366#[allow(dead_code)]
375#[cfg(target_arch = "x86_64")]
376#[inline(always)]
377unsafe fn popcnt64_asm(x: u64) -> u64 {
378 let out: u64;
379 unsafe {
381 core::arch::asm!(
382 "popcnt {out}, {inp}",
383 inp = in(reg) x,
384 out = out(reg) out,
385 options(pure, nomem, nostack),
386 );
387 }
388 out
389}
390
391#[allow(dead_code)]
394#[cfg(target_arch = "x86_64")]
395#[inline(always)]
396unsafe fn popcnt_scalar_asm(bytes: &[u8]) -> u64 {
397 let mut cnt = 0u64;
398 let (chunks, rem) = bytes.as_chunks::<8>();
399 for chunk in chunks {
400 cnt += unsafe { popcnt64_asm(u64::from_ne_bytes(*chunk)) };
402 }
403 if !rem.is_empty() {
404 cnt += unsafe { popcnt64_asm(tail_u64(rem)) };
405 }
406 cnt
407}
408
409#[cfg(all(
414 any(target_arch = "x86", target_arch = "x86_64"),
415 not(target_feature = "avx512vpopcntdq"),
416 any(target_feature = "avx2", feature = "std")
417))]
418#[target_feature(enable = "avx2")]
419#[inline]
420fn csa256(a: __m256i, b: __m256i, c: __m256i) -> (__m256i, __m256i) {
421 let u = _mm256_xor_si256(a, b);
422 let h = _mm256_or_si256(_mm256_and_si256(a, b), _mm256_and_si256(u, c));
423 let l = _mm256_xor_si256(u, c);
424 (h, l)
425}
426
427#[cfg(all(
430 any(target_arch = "x86", target_arch = "x86_64"),
431 not(target_feature = "avx512vpopcntdq"),
432 any(target_feature = "avx2", feature = "std")
433))]
434#[target_feature(enable = "avx2")]
435#[inline]
436fn popcnt256(v: __m256i) -> __m256i {
437 let lookup1 = _mm256_setr_epi8(
438 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7,
439 7, 8,
440 );
441 let lookup2 = _mm256_setr_epi8(
442 4, 3, 3, 2, 3, 2, 2, 1, 3, 2, 2, 1, 2, 1, 1, 0, 4, 3, 3, 2, 3, 2, 2, 1, 3, 2, 2, 1, 2, 1,
443 1, 0,
444 );
445 let low_mask = _mm256_set1_epi8(0x0f);
446 let lo = _mm256_and_si256(v, low_mask);
447 let hi = _mm256_and_si256(_mm256_srli_epi16(v, 4), low_mask);
448 let popcnt1 = _mm256_shuffle_epi8(lookup1, lo);
449 let popcnt2 = _mm256_shuffle_epi8(lookup2, hi);
450 _mm256_sad_epu8(popcnt1, popcnt2)
451}
452
453#[cfg(all(
459 any(target_arch = "x86", target_arch = "x86_64"),
460 not(target_feature = "avx512vpopcntdq"),
461 any(target_feature = "avx2", feature = "std")
462))]
463#[target_feature(enable = "avx2")]
464#[inline]
465#[rustfmt::skip]
467fn popcnt_avx2(bytes: &[u8]) -> u64 {
468 let zero = _mm256_setzero_si256();
469 let mut cnt = zero;
470 let mut ones = zero;
471 let mut twos = zero;
472 let mut fours = zero;
473 let mut eights = zero;
474 let mut twos_a;
475 let mut twos_b;
476 let mut fours_a;
477 let mut fours_b;
478 let mut eights_a;
479 let mut eights_b;
480 let mut sixteens;
481
482 let (blocks, tail) = bytes.as_chunks::<512>();
484 for chunk in blocks {
485 let p = chunk.as_ptr().cast::<__m256i>();
486 unsafe {
488 (twos_a, ones) = csa256(ones, _mm256_loadu_si256(p.add(0)), _mm256_loadu_si256(p.add(1)));
489 (twos_b, ones) = csa256(ones, _mm256_loadu_si256(p.add(2)), _mm256_loadu_si256(p.add(3)));
490 (fours_a, twos) = csa256(twos, twos_a, twos_b);
491 (twos_a, ones) = csa256(ones, _mm256_loadu_si256(p.add(4)), _mm256_loadu_si256(p.add(5)));
492 (twos_b, ones) = csa256(ones, _mm256_loadu_si256(p.add(6)), _mm256_loadu_si256(p.add(7)));
493 (fours_b, twos) = csa256(twos, twos_a, twos_b);
494 (eights_a, fours) = csa256(fours, fours_a, fours_b);
495 (twos_a, ones) = csa256(ones, _mm256_loadu_si256(p.add(8)), _mm256_loadu_si256(p.add(9)));
496 (twos_b, ones) = csa256(ones, _mm256_loadu_si256(p.add(10)), _mm256_loadu_si256(p.add(11)));
497 (fours_a, twos) = csa256(twos, twos_a, twos_b);
498 (twos_a, ones) = csa256(ones, _mm256_loadu_si256(p.add(12)), _mm256_loadu_si256(p.add(13)));
499 (twos_b, ones) = csa256(ones, _mm256_loadu_si256(p.add(14)), _mm256_loadu_si256(p.add(15)));
500 (fours_b, twos) = csa256(twos, twos_a, twos_b);
501 (eights_b, fours) = csa256(fours, fours_a, fours_b);
502 (sixteens, eights) = csa256(eights, eights_a, eights_b);
503 cnt = _mm256_add_epi64(cnt, popcnt256(sixteens));
504 }
505 }
506
507 cnt = _mm256_slli_epi64(cnt, 4);
508 cnt = _mm256_add_epi64(cnt, _mm256_slli_epi64(popcnt256(eights), 3));
509 cnt = _mm256_add_epi64(cnt, _mm256_slli_epi64(popcnt256(fours), 2));
510 cnt = _mm256_add_epi64(cnt, _mm256_slli_epi64(popcnt256(twos), 1));
511 cnt = _mm256_add_epi64(cnt, popcnt256(ones));
512
513 let (vecs, _) = tail.as_chunks::<32>();
515 for chunk in vecs {
516 let v = unsafe { _mm256_loadu_si256(chunk.as_ptr().cast::<__m256i>()) };
517 cnt = _mm256_add_epi64(cnt, popcnt256(v));
518 }
519
520 let lanes: [u64; 4] = unsafe { core::mem::transmute(cnt) };
523 lanes[0] + lanes[1] + lanes[2] + lanes[3]
524}
525
526#[cfg(all(
533 any(target_arch = "x86", target_arch = "x86_64"),
534 not(target_feature = "avx512vpopcntdq"),
535 any(target_feature = "avx2", feature = "std")
536))]
537#[target_feature(enable = "avx2")]
538#[inline]
539fn popcnt_avx2_medium(bytes: &[u8]) -> u64 {
540 let mut acc = _mm256_setzero_si256();
541 let (vecs, _) = bytes.as_chunks::<32>();
542 for chunk in vecs {
543 let v = unsafe { _mm256_loadu_si256(chunk.as_ptr().cast::<__m256i>()) };
544 acc = _mm256_add_epi64(acc, popcnt256(v));
545 }
546 let lanes: [u64; 4] = unsafe { core::mem::transmute(acc) };
548 lanes[0] + lanes[1] + lanes[2] + lanes[3]
549}
550
551#[cfg(all(
557 any(target_arch = "x86", target_arch = "x86_64"),
558 any(
559 all(not(target_feature = "avx2"), feature = "std"),
560 target_feature = "avx512vpopcntdq"
561 )
562))]
563#[target_feature(enable = "avx512f,avx512bw,avx512vpopcntdq")]
564#[inline]
565fn popcnt_avx512(bytes: &[u8]) -> u64 {
566 let mut cnt0 = _mm512_setzero_si512();
567
568 let (blocks, tail256) = bytes.as_chunks::<256>();
571 if !blocks.is_empty() {
572 let mut cnt1 = _mm512_setzero_si512();
573 let mut cnt2 = _mm512_setzero_si512();
574 let mut cnt3 = _mm512_setzero_si512();
575 for chunk in blocks {
576 let p = chunk.as_ptr();
577 unsafe {
579 let v0 = _mm512_loadu_si512(p.add(0).cast());
580 let v1 = _mm512_loadu_si512(p.add(64).cast());
581 let v2 = _mm512_loadu_si512(p.add(128).cast());
582 let v3 = _mm512_loadu_si512(p.add(192).cast());
583 cnt0 = _mm512_add_epi64(cnt0, _mm512_popcnt_epi64(v0));
584 cnt1 = _mm512_add_epi64(cnt1, _mm512_popcnt_epi64(v1));
585 cnt2 = _mm512_add_epi64(cnt2, _mm512_popcnt_epi64(v2));
586 cnt3 = _mm512_add_epi64(cnt3, _mm512_popcnt_epi64(v3));
587 }
588 }
589 cnt0 = _mm512_add_epi64(cnt0, cnt1);
590 cnt2 = _mm512_add_epi64(cnt2, cnt3);
591 cnt0 = _mm512_add_epi64(cnt0, cnt2);
592 }
593
594 let (vecs, tail64) = tail256.as_chunks::<64>();
596 for chunk in vecs {
597 let v = unsafe { _mm512_loadu_si512(chunk.as_ptr().cast()) };
598 cnt0 = _mm512_add_epi64(cnt0, _mm512_popcnt_epi64(v));
599 }
600
601 if !tail64.is_empty() {
603 let len = tail64.len();
604 let mask = (u64::MAX >> (64 - len)) as __mmask64;
605 unsafe {
608 let v = _mm512_maskz_loadu_epi8(mask, tail64.as_ptr().cast());
609 cnt0 = _mm512_add_epi64(cnt0, _mm512_popcnt_epi64(v));
610 }
611 }
612
613 _mm512_reduce_add_epi64(cnt0) as u64
614}
615
616#[cfg(target_arch = "aarch64")]
621#[inline]
622fn popcnt_aarch64(bytes: &[u8]) -> u64 {
623 #[cfg(all(target_feature = "sve", simd_popcnt_have_sve))]
625 {
626 unsafe { popcnt_arm_sve(bytes) }
627 }
628
629 #[cfg(not(all(target_feature = "sve", simd_popcnt_have_sve)))]
631 {
632 popcnt_neon(bytes)
633 }
634}
635
636#[cfg(all(
637 target_arch = "aarch64",
638 not(all(target_feature = "sve", simd_popcnt_have_sve))
639))]
640#[inline]
641fn vpadalq(sum: uint64x2_t, t: uint8x16_t) -> uint64x2_t {
642 unsafe { vpadalq_u32(sum, vpaddlq_u16(vpaddlq_u8(t))) }
643}
644
645#[cfg(all(
646 target_arch = "aarch64",
647 not(all(target_feature = "sve", simd_popcnt_have_sve))
648))]
649#[inline]
650fn popcnt_neon(bytes: &[u8]) -> u64 {
651 #[cfg(all(simd_popcnt_have_sve, feature = "std"))]
652 if is_aarch64_feature_detected!("sve") {
653 return unsafe { popcnt_arm_sve(bytes) };
654 }
655
656 const CHUNK: usize = 64;
657 let mut cnt = 0u64;
658 let iters = bytes.len() / CHUNK;
659 let ptr = bytes.as_ptr();
660
661 if iters > 0 {
662 unsafe {
665 let mut sum = vdupq_n_u64(0);
666 let zero = vdupq_n_u8(0);
667 let mut i = 0usize;
668
669 while i < iters {
670 let mut t0 = zero;
671 let mut t1 = zero;
672 let mut t2 = zero;
673 let mut t3 = zero;
674
675 let limit = (i + 31).min(iters);
678 while i < limit {
679 let input = vld1q_u8_x4(ptr.add(i * CHUNK));
683 t0 = vaddq_u8(t0, vcntq_u8(input.0));
684 t1 = vaddq_u8(t1, vcntq_u8(input.1));
685 t2 = vaddq_u8(t2, vcntq_u8(input.2));
686 t3 = vaddq_u8(t3, vcntq_u8(input.3));
687 i += 1;
688 }
689
690 sum = vpadalq(sum, t0);
691 sum = vpadalq(sum, t1);
692 sum = vpadalq(sum, t2);
693 sum = vpadalq(sum, t3);
694 }
695
696 let mut tmp = [0u64; 2];
697 vst1q_u64(tmp.as_mut_ptr(), sum);
698 cnt += tmp[0] + tmp[1];
699 }
700 }
701
702 let rest = &bytes[iters * CHUNK..];
705 cnt += popcnt_scalar_loop!(rest);
706 cnt
707}
708
709#[cfg(all(
714 target_arch = "aarch64",
715 simd_popcnt_have_sve,
716 any(target_feature = "sve", feature = "std")
717))]
718#[target_feature(enable = "sve")]
719#[inline]
720fn popcnt_arm_sve(bytes: &[u8]) -> u64 {
721 unsafe {
724 let mut i = 0usize;
725 let mut vcnt0 = svdup_n_u64(0);
726 let vl = svcntb() as usize; let ptr = bytes.as_ptr();
728 let len = bytes.len();
729
730 if i + vl * 4 <= len {
733 let mut vcnt1 = svdup_n_u64(0);
734 let mut vcnt2 = svdup_n_u64(0);
735 let mut vcnt3 = svdup_n_u64(0);
736 loop {
737 let v0 = svreinterpret_u64_u8(svld1_u8(svptrue_b8(), ptr.add(i)));
738 let v1 = svreinterpret_u64_u8(svld1_u8(svptrue_b8(), ptr.add(i + vl)));
739 let v2 = svreinterpret_u64_u8(svld1_u8(svptrue_b8(), ptr.add(i + vl * 2)));
740 let v3 = svreinterpret_u64_u8(svld1_u8(svptrue_b8(), ptr.add(i + vl * 3)));
741 vcnt0 = svadd_u64_x(svptrue_b64(), vcnt0, svcnt_u64_x(svptrue_b64(), v0));
742 vcnt1 = svadd_u64_x(svptrue_b64(), vcnt1, svcnt_u64_x(svptrue_b64(), v1));
743 vcnt2 = svadd_u64_x(svptrue_b64(), vcnt2, svcnt_u64_x(svptrue_b64(), v2));
744 vcnt3 = svadd_u64_x(svptrue_b64(), vcnt3, svcnt_u64_x(svptrue_b64(), v3));
745 i += vl * 4;
746 if i + vl * 4 > len {
747 break;
748 }
749 }
750 vcnt0 = svadd_u64_x(svptrue_b64(), vcnt0, vcnt1);
751 vcnt2 = svadd_u64_x(svptrue_b64(), vcnt2, vcnt3);
752 vcnt0 = svadd_u64_x(svptrue_b64(), vcnt0, vcnt2);
753 }
754
755 let mut pg = svwhilelt_b8_u64(i as u64, len as u64);
758 while svptest_any(svptrue_b8(), pg) {
759 let v = svreinterpret_u64_u8(svld1_u8(pg, ptr.add(i)));
760 vcnt0 = svadd_u64_x(svptrue_b64(), vcnt0, svcnt_u64_x(svptrue_b64(), v));
761 i += vl;
762 pg = svwhilelt_b8_u64(i as u64, len as u64);
763 }
764
765 svaddv_u64(svptrue_b64(), vcnt0)
766 }
767}
768
769#[cfg(test)]
774mod tests {
775 use super::*;
776
777 fn reference(bytes: &[u8]) -> u64 {
779 bytes.iter().map(|b| b.count_ones() as u64).sum()
780 }
781
782 fn popcnt64_bitwise(x: u64) -> u64 {
785 const M1: u64 = 0x5555555555555555;
786 const M2: u64 = 0x3333333333333333;
787 const M4: u64 = 0x0F0F0F0F0F0F0F0F;
788 const H01: u64 = 0x0101010101010101;
789 let x = x - ((x >> 1) & M1);
790 let x = (x & M2) + ((x >> 2) & M2);
791 let x = (x + (x >> 4)) & M4;
792 x.wrapping_mul(H01) >> 56
793 }
794
795 #[test]
796 fn empty() {
797 assert_eq!(popcnt(&[]), 0);
798 }
799
800 #[test]
801 fn all_ones() {
802 for &size in &[
803 0, 1, 7, 8, 31, 32, 39, 40, 63, 64, 255, 256, 511, 512, 4095, 4096, 65537,
804 ] {
805 let bytes = vec![0xFFu8; size];
806 assert_eq!(popcnt(&bytes), size as u64 * 8, "size={size}");
807 }
808 }
809
810 #[test]
811 fn all_zeros() {
812 let bytes = vec![0u8; 65536];
813 assert_eq!(popcnt(&bytes), 0);
814 }
815
816 #[test]
817 fn single_bits() {
818 for bit in 0u64..64 {
819 let val = 1u64 << bit;
820 assert_eq!(popcnt(&val.to_le_bytes()), 1, "bit={bit}");
821 }
822 }
823
824 #[test]
827 fn ext_trait_widths() {
828 let u8s: &[u8] = &[0xFF, 0x0F, 0x00, 0xAB, 0x01];
829 assert_eq!(
830 u8s.popcnt(),
831 u8s.iter().map(|x| x.count_ones() as u64).sum()
832 );
833
834 let u16s: &[u16] = &[0xFFFF, 0x0F0F, 0x1234, 0];
835 assert_eq!(
836 u16s.popcnt(),
837 u16s.iter().map(|x| x.count_ones() as u64).sum()
838 );
839
840 let u32s: &[u32] = &[u32::MAX, 0, 0x8000_0001];
841 assert_eq!(
842 u32s.popcnt(),
843 u32s.iter().map(|x| x.count_ones() as u64).sum()
844 );
845
846 let u64s: &[u64] = &[u64::MAX, 0x0F0F_0F0F_0F0F_0F0F, 0];
847 assert_eq!(
848 u64s.popcnt(),
849 u64s.iter().map(|x| x.count_ones() as u64).sum()
850 );
851
852 let i32s = [-1i32, 0, 1, i32::MIN];
855 assert_eq!(
856 i32s.popcnt(),
857 i32s.iter().map(|x| x.count_ones() as u64).sum()
858 );
859 assert_eq!([u128::MAX, 0].popcnt(), 128);
860 }
861
862 #[test]
866 fn pseudorandom_all_sizes() {
867 let mut state: u64 = 0x9E37_79B9_7F4A_7C15;
868 let mut next = || {
869 state ^= state << 13;
870 state ^= state >> 7;
871 state ^= state << 17;
872 state
873 };
874
875 const MAX_SIZE: usize = 4695;
878 const MAX_OFF: usize = 7;
879 let mut bytes = vec![0u8; MAX_SIZE + MAX_OFF + 1];
880 for b in bytes.iter_mut() {
881 *b = (next() & 0xFF) as u8;
882 }
883
884 let sizes =
887 (0usize..=600).chain([1023, 1024, 1025, 2048, 4095, 4096, 4097, 4608, MAX_SIZE]);
888 for size in sizes {
889 for &off in &[0usize, 1, 3, MAX_OFF] {
890 let slice = &bytes[off..off + size];
891 assert_eq!(popcnt(slice), reference(slice), "size={size} off={off}");
892 }
893 }
894 }
895
896 #[test]
904 fn suffix_sweep() {
905 let size = std::env::var("SIMD_POPCNT_TEST_SIZE")
906 .ok()
907 .and_then(|s| s.parse::<usize>().ok())
908 .unwrap_or(20_000);
909
910 let ones = vec![0xFFu8; size];
912 check_all_suffixes(&ones);
913
914 let mut state: u64 = 0x2545_F491_4F6C_DD1D;
916 let mut bytes = vec![0u8; size];
917 for b in bytes.iter_mut() {
918 state ^= state << 13;
919 state ^= state >> 7;
920 state ^= state << 17;
921 *b = state as u8;
922 }
923 check_all_suffixes(&bytes);
924 }
925
926 fn check_all_suffixes(bytes: &[u8]) {
929 let total: u64 = bytes.iter().map(|&b| popcnt64_bitwise(b as u64)).sum();
930 let mut prefix = 0u64; for (i, &byte) in bytes.iter().enumerate() {
932 assert_eq!(popcnt(&bytes[i..]), total - prefix, "suffix at offset {i}");
933 prefix += popcnt64_bitwise(byte as u64);
934 }
935 assert_eq!(popcnt(&bytes[bytes.len()..]), 0);
937 }
938}