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