Last active
October 21, 2024 18:34
-
-
Save imaami/2c0c9e41eabea886460735d5fc9f5882 to your computer and use it in GitHub Desktop.
Generic popcount
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** @file popcnt.h | |
*/ | |
#ifndef POPCNT_H_ | |
#define POPCNT_H_ | |
#include <limits.h> | |
#include <stdint.h> | |
#undef define_popcnt | |
#undef popcnt16_impl | |
#undef popcnt32_impl | |
#undef popcnt64_impl | |
#undef popcnt_compat | |
#undef popcnt_inline | |
#ifdef _MSC_VER | |
# define popcnt_inline __forceinline | |
# ifndef typeof | |
# define typeof __typeof__ | |
# endif | |
# define popcnt16_impl return __popcnt16 | |
# pragma intrinsic(__popcnt16) | |
# define popcnt32_impl return __popcnt | |
# pragma intrinsic(__popcnt) | |
# ifdef _M_IX86 | |
# undef popcnt32x2 | |
# define popcnt64_impl return popcnt32x2 | |
# define popcnt32x2(x) \ | |
(__popcnt((uint32_t)(x >> 32U)) + \ | |
__popcnt((uint32_t)(x & ~(uint32_t)0))) | |
# else | |
# define popcnt64_impl return __popcnt64 | |
# pragma intrinsic(__popcnt64) | |
# endif | |
# define popcnt_pre_pragma _Pragma("warning(push)") \ | |
_Pragma("warning(disable: 4116)") | |
# define popcnt_post_pragma _Pragma("warning(pop)") | |
#else | |
# define popcnt_inline inline __attribute__((always_inline,const)) | |
# ifndef __has_builtin | |
# define __has_builtin(x) 0 | |
# endif | |
# if __has_builtin(__builtin_popcount) && (UINT_MAX == UINT32_MAX) | |
# define popcnt16_impl return (uint16_t)__builtin_popcount | |
# define popcnt32_impl return (uint32_t)__builtin_popcount | |
# endif | |
# if !defined(popcnt32_impl) && \ | |
__has_builtin(__builtin_popcountl) && (ULONG_MAX == UINT32_MAX) | |
# define popcnt16_impl return (uint16_t)__builtin_popcountl | |
# define popcnt32_impl return (uint32_t)__builtin_popcountl | |
# endif | |
# if __has_builtin(__builtin_popcountl) && (ULONG_MAX == UINT64_MAX) | |
# define popcnt64_impl return (uint64_t)__builtin_popcountl | |
# endif | |
# if !defined(popcnt64_impl) && \ | |
__has_builtin(__builtin_popcountll) && (ULLONG_MAX == UINT64_MAX) | |
# define popcnt64_impl return (uint64_t)__builtin_popcountll | |
# endif | |
# if __has_builtin(__builtin_popcountg) | |
# if !defined(popcnt16_impl) | |
# define popcnt16_impl return (uint16_t)__builtin_popcountg | |
# endif | |
# if !defined(popcnt32_impl) | |
# define popcnt32_impl return (uint32_t)__builtin_popcountg | |
# endif | |
# if !defined(popcnt64_impl) | |
# define popcnt64_impl return (uint64_t)__builtin_popcountg | |
# endif | |
# endif | |
#endif | |
#define define_popcnt(b, ...) popcnt_inline \ | |
static uint##b##_t popcnt##b(uint##b##_t val) \ | |
{ __VA_ARGS__(val); } _Static_assert(b>0, #b) | |
#define popcnt_compat(x) typeof(_Generic( \ | |
(char(*)[2 - (sizeof(x) > 4U)])0 \ | |
,char(*)[1]: (uint64_t)0 \ | |
,char(*)[2]: (uint32_t)0)) y = (x); \ | |
y -= (typeof(y))-1/3 & (y >> 1U); \ | |
y = ((typeof(y))-1/5 & (y >> 2U)) \ | |
+ ((typeof(y))-1/5 & y); \ | |
return ((typeof(y))-1/ 17 & ((y >> 4U) + y)) \ | |
* ((typeof(y))-1/255) >> (sizeof y - 1U) \ | |
* CHAR_BIT | |
#ifndef popcnt16_impl | |
# define popcnt16_impl popcnt_compat | |
#endif | |
#ifndef popcnt32_impl | |
# define popcnt32_impl popcnt_compat | |
#endif | |
#ifndef popcnt64_impl | |
# define popcnt64_impl popcnt_compat | |
#endif | |
#ifndef popcnt_pre_pragma | |
# define popcnt_pre_pragma | |
#endif | |
#ifndef popcnt_post_pragma | |
# define popcnt_post_pragma | |
#endif | |
define_popcnt(16, popcnt16_impl); | |
define_popcnt(32, popcnt32_impl); | |
define_popcnt(64, popcnt64_impl); | |
#undef define_popcnt | |
#undef popcnt16_impl | |
#undef popcnt32_impl | |
#undef popcnt64_impl | |
#undef popcnt_compat | |
#undef popcnt_inline | |
#if defined(_MSC_VER) && defined(_M_IX86) | |
# undef popcnt32x2 | |
#endif | |
#define popcnt(x) popcnt_pre_pragma _Generic((x), \ | |
typeof(_Generic((char)0, \ | |
signed char: (struct{int i;}){0}, \ | |
unsigned char: (struct{int i;}){0}, \ | |
default: (char)0)): popcnt16, \ | |
signed char: popcnt16, short: popcnt16, \ | |
unsigned char: popcnt16, int: popcnt32, \ | |
unsigned short: popcnt16, unsigned: popcnt32, \ | |
long long: popcnt64, unsigned long: _Generic( \ | |
&(int[sizeof 1UL]){0}, \ | |
int(*)[sizeof(uint32_t)]: popcnt32, \ | |
int(*)[sizeof(uint64_t)]: popcnt64), \ | |
unsigned long long: popcnt64, long: _Generic( \ | |
&(int[sizeof 1L]){0}, \ | |
int(*)[sizeof(int32_t)]: popcnt32, \ | |
int(*)[sizeof(int64_t)]: popcnt64)) \ | |
(_Generic((x), default: (x), \ | |
long long: (unsigned long long)(x), \ | |
signed char: (unsigned char)(x), \ | |
short: (unsigned short)(x), \ | |
typeof(_Generic((char)0, \ | |
signed char: \ | |
(struct{int i;}){0}, \ | |
unsigned char: \ | |
(struct{int i;}){0}, \ | |
default: (char)0)): \ | |
(unsigned char)(x), \ | |
long: (unsigned long)(x), \ | |
int: (unsigned)(x))) popcnt_post_pragma | |
#endif /* POPCNT_H_ */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <errno.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include "popcnt.h" | |
int | |
main (int c, | |
char **v) | |
{ | |
for (int i = 1; i < c; ++i) { | |
unsigned long long n = 0; | |
int e = v[i] && v[i][0] ? 0 : EINVAL; | |
if (!e) { | |
errno = 0; | |
char *ep = v[i]; | |
n = (typeof(n))strtoll(v[i], &ep, 0); | |
e = errno; | |
if (e == ERANGE) { | |
errno = 0; | |
ep = v[i]; | |
n = strtoull(v[i], &ep, 0); | |
e = errno; | |
} | |
if (!e && *ep) | |
e = EINVAL; | |
} | |
if (e) { | |
(void)fprintf(stderr, "%s: \"%s\"\n", | |
strerror(e), v[i]); | |
continue; | |
} | |
#define fmt(T) "%3zu " #T "\n" | |
#define run(T) , (size_t)popcnt((T)n) | |
#define test_types(T) T(char ) \ | |
T(signed char ) T(unsigned char ) \ | |
T(signed short ) T(unsigned short ) \ | |
T(signed int ) T(unsigned int ) \ | |
T(signed long ) T(unsigned long ) \ | |
T(signed long long) T(unsigned long long) | |
(void)printf(test_types(fmt) " \033[1m" | |
"\033[38;5;99m0x" | |
"\033[94m%08llx\033[96m%04llx" | |
"\033[92m%02llx\033[93m%02llx" | |
"\033[m\n" test_types(run) | |
, n >> 32U | |
, n >> 16U & 0xffffU | |
, n >> 8U & 0x00ffU | |
, n & 0x00ffU); | |
#undef test_types | |
#undef run | |
#undef fmt | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment