diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2024-05-31 12:01:43 +0200 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2024-05-31 16:03:23 +0200 |
commit | 2075553a1b0eb843d4ee73a039b3e30bed9274e0 (patch) | |
tree | 904f192fa9d7c920c9dde6a7bc26b208c3b03312 | |
parent | 5a68a246e750359819d63bcff5ef97dd9c7788fc (diff) | |
download | busybox-w32-2075553a1b0eb843d4ee73a039b3e30bed9274e0.tar.gz busybox-w32-2075553a1b0eb843d4ee73a039b3e30bed9274e0.tar.bz2 busybox-w32-2075553a1b0eb843d4ee73a039b3e30bed9274e0.zip |
libbb: add bit counting function, use where appropriate
Although "naive" counting function is not too slow and is smaller,
using it on e.g. each of 1024 words of CPU mask feels wrong.
function old new delta
bb_popcnt_32 - 52 +52
get_prefix 323 321 -2
nproc_main 206 199 -7
d4_run_script 739 731 -8
ipcalc_main 533 507 -26
------------------------------------------------------------------------------
(add/remove: 2/0 grow/shrink: 0/4 up/down: 52/-43) Total: 9 bytes
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r-- | coreutils/nproc.c | 9 | ||||
-rw-r--r-- | include/libbb.h | 7 | ||||
-rw-r--r-- | libbb/Kbuild.src | 1 | ||||
-rw-r--r-- | libbb/popcnt.c | 46 | ||||
-rw-r--r-- | networking/ifupdown.c | 8 | ||||
-rw-r--r-- | networking/ipcalc.c | 21 | ||||
-rw-r--r-- | networking/libiproute/utils.c | 11 | ||||
-rw-r--r-- | networking/udhcp/dhcpc.c | 15 |
8 files changed, 66 insertions, 52 deletions
diff --git a/coreutils/nproc.c b/coreutils/nproc.c index a0d818c59..df63bf57a 100644 --- a/coreutils/nproc.c +++ b/coreutils/nproc.c | |||
@@ -56,13 +56,10 @@ int nproc_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) | |||
56 | unsigned long *mask = get_malloc_cpu_affinity(0, &sz); | 56 | unsigned long *mask = get_malloc_cpu_affinity(0, &sz); |
57 | sz /= sizeof(long); | 57 | sz /= sizeof(long); |
58 | for (i = 0; i < sz; i++) { | 58 | for (i = 0; i < sz; i++) { |
59 | unsigned long m = mask[i]; | 59 | if (mask[i] != 0) /* most mask[i] are usually 0 */ |
60 | while (m) { | 60 | count += bb_popcnt_long(mask[i]); |
61 | if (m & 1) | ||
62 | count++; | ||
63 | m >>= 1; | ||
64 | } | ||
65 | } | 61 | } |
62 | IF_FEATURE_CLEAN_UP(free(mask);) | ||
66 | } | 63 | } |
67 | 64 | ||
68 | IF_LONG_OPTS(count -= ignore;) | 65 | IF_LONG_OPTS(count -= ignore;) |
diff --git a/include/libbb.h b/include/libbb.h index 67d29f843..6914b5882 100644 --- a/include/libbb.h +++ b/include/libbb.h | |||
@@ -397,6 +397,13 @@ extern int *BB_GLOBAL_CONST bb_errno; | |||
397 | uint64_t bb_bswap_64(uint64_t x) FAST_FUNC; | 397 | uint64_t bb_bswap_64(uint64_t x) FAST_FUNC; |
398 | #endif | 398 | #endif |
399 | 399 | ||
400 | unsigned FAST_FUNC bb_popcnt_32(uint32_t m); | ||
401 | #if ULONG_MAX > 0xffffffff | ||
402 | unsigned FAST_FUNC bb_popcnt_long(unsigned_long m); | ||
403 | #else | ||
404 | #define bb_popcnt_long(m) bb_popcnt_32(m) | ||
405 | #endif | ||
406 | |||
400 | unsigned long FAST_FUNC isqrt(unsigned long long N); | 407 | unsigned long FAST_FUNC isqrt(unsigned long long N); |
401 | 408 | ||
402 | unsigned long long monotonic_ns(void) FAST_FUNC; | 409 | unsigned long long monotonic_ns(void) FAST_FUNC; |
diff --git a/libbb/Kbuild.src b/libbb/Kbuild.src index a0e2a6da7..cb8d2c2ec 100644 --- a/libbb/Kbuild.src +++ b/libbb/Kbuild.src | |||
@@ -72,6 +72,7 @@ lib-y += perror_nomsg.o | |||
72 | lib-y += perror_nomsg_and_die.o | 72 | lib-y += perror_nomsg_and_die.o |
73 | lib-y += pidfile.o | 73 | lib-y += pidfile.o |
74 | lib-y += platform.o | 74 | lib-y += platform.o |
75 | lib-y += popcnt.o | ||
75 | lib-y += printable.o | 76 | lib-y += printable.o |
76 | lib-y += printable_string.o | 77 | lib-y += printable_string.o |
77 | lib-y += print_flags.o | 78 | lib-y += print_flags.o |
diff --git a/libbb/popcnt.c b/libbb/popcnt.c new file mode 100644 index 000000000..fe8cd240f --- /dev/null +++ b/libbb/popcnt.c | |||
@@ -0,0 +1,46 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Utility routines. | ||
4 | * | ||
5 | * Copyright (C) 2024 Denys Vlasenko | ||
6 | * | ||
7 | * Licensed under GPLv2, see file LICENSE in this source tree. | ||
8 | */ | ||
9 | #include "libbb.h" | ||
10 | |||
11 | unsigned FAST_FUNC bb_popcnt_32(uint32_t m) | ||
12 | { | ||
13 | /* replace each 2 bit group with the count of set bits in it */ | ||
14 | /* 00->00 01->01 10->01 11->10 */ | ||
15 | m = m - ((m >> 1) & 0x55555555); | ||
16 | /* in each 4 bit group, add two 2-bit counts */ | ||
17 | m = (m & 0x33333333) + ((m >> 2) & 0x33333333); | ||
18 | /* in each 8 bit group, add two 4-bit counts (in fact, 3-bit, 0nnn with n=0..4) */ | ||
19 | m = (m + (m >> 4)) & 0x0f0f0f0f; | ||
20 | #if 1 /* assume 32*32->32 multiply is fast */ | ||
21 | m = m * 0x01010101; /* top byte = m + (m<<8) + (m<<16) + (m<<24) */ | ||
22 | return m >> 24; | ||
23 | #else | ||
24 | /* 0000aaaa0000bbbb0000cccc0000dddd */ | ||
25 | /* + 0000aaaa0000bbbb0000cccc */ | ||
26 | /* = 0000xxxx000_a+b_000xxxxx000_c+d_ (we don't care about x bits) */ | ||
27 | m += m >> 8; /* in each 16-bit group, lowest 5 bits is the count */ | ||
28 | /* 0000xxxx000_a+b_000xxxxx000_c+d_ */ | ||
29 | /* + 0000xxxx000_a+b_ */ | ||
30 | /* = 0000xxxx000xxxxx00xxxxxx00a+b+cd */ | ||
31 | m += m >> 16; /* in each 32-bit group, lowest 6 bits is the count */ | ||
32 | return m & 0x3f; /* clear x bits */ | ||
33 | #endif | ||
34 | } | ||
35 | |||
36 | #if ULONG_MAX > 0xffffffff | ||
37 | unsigned FAST_FUNC bb_popcnt_long(unsigned long m) | ||
38 | { | ||
39 | BUILD_BUG_ON(sizeof(m) != 8); | ||
40 | /* 64-bit version of bb_popcnt_32 exists, but it uses 64-bit constants, | ||
41 | * which are awkward to generate on assembly level on most CPUs. | ||
42 | * For now, just add two 32-bit counts: | ||
43 | */ | ||
44 | return bb_popcnt_32((uint32_t)m) + bb_popcnt_32((uint32_t)(m >> 32)); | ||
45 | } | ||
46 | #endif | ||
diff --git a/networking/ifupdown.c b/networking/ifupdown.c index 6c4ae27f2..9c3640be7 100644 --- a/networking/ifupdown.c +++ b/networking/ifupdown.c | |||
@@ -306,7 +306,6 @@ static int count_netmask_bits(const char *dotted_quad) | |||
306 | // d = ~d; /* 11110000 -> 00001111 */ | 306 | // d = ~d; /* 11110000 -> 00001111 */ |
307 | 307 | ||
308 | /* Shorter version */ | 308 | /* Shorter version */ |
309 | int result; | ||
310 | struct in_addr ip; | 309 | struct in_addr ip; |
311 | unsigned d; | 310 | unsigned d; |
312 | 311 | ||
@@ -316,12 +315,7 @@ static int count_netmask_bits(const char *dotted_quad) | |||
316 | d = ~d; /* 11110000 -> 00001111 */ | 315 | d = ~d; /* 11110000 -> 00001111 */ |
317 | if (d & (d+1)) /* check that it is in 00001111 form */ | 316 | if (d & (d+1)) /* check that it is in 00001111 form */ |
318 | return -1; /* no it is not */ | 317 | return -1; /* no it is not */ |
319 | result = 32; | 318 | return bb_popcnt_32(~d); |
320 | while (d) { | ||
321 | d >>= 1; | ||
322 | result--; | ||
323 | } | ||
324 | return result; | ||
325 | } | 319 | } |
326 | # endif | 320 | # endif |
327 | 321 | ||
diff --git a/networking/ipcalc.c b/networking/ipcalc.c index 92e7b289d..3ec473c6b 100644 --- a/networking/ipcalc.c +++ b/networking/ipcalc.c | |||
@@ -71,25 +71,6 @@ static unsigned long get_netmask(unsigned long ipaddr) | |||
71 | return 0; | 71 | return 0; |
72 | } | 72 | } |
73 | 73 | ||
74 | #if ENABLE_FEATURE_IPCALC_FANCY | ||
75 | static int get_prefix(unsigned long netmask) | ||
76 | { | ||
77 | unsigned long msk = 0x80000000; | ||
78 | int ret = 0; | ||
79 | |||
80 | netmask = htonl(netmask); | ||
81 | while (msk) { | ||
82 | if (netmask & msk) | ||
83 | ret++; | ||
84 | msk >>= 1; | ||
85 | } | ||
86 | return ret; | ||
87 | } | ||
88 | #else | ||
89 | int get_prefix(unsigned long netmask); | ||
90 | #endif | ||
91 | |||
92 | |||
93 | #define NETMASK 0x01 | 74 | #define NETMASK 0x01 |
94 | #define BROADCAST 0x02 | 75 | #define BROADCAST 0x02 |
95 | #define NETWORK 0x04 | 76 | #define NETWORK 0x04 |
@@ -210,7 +191,7 @@ int ipcalc_main(int argc UNUSED_PARAM, char **argv) | |||
210 | 191 | ||
211 | if (ENABLE_FEATURE_IPCALC_FANCY) { | 192 | if (ENABLE_FEATURE_IPCALC_FANCY) { |
212 | if (opt & NETPREFIX) { | 193 | if (opt & NETPREFIX) { |
213 | printf("PREFIX=%i\n", get_prefix(netmask)); | 194 | printf("PREFIX=%i\n", bb_popcnt_32(netmask)); |
214 | } | 195 | } |
215 | 196 | ||
216 | if (opt & HOSTNAME) { | 197 | if (opt & HOSTNAME) { |
diff --git a/networking/libiproute/utils.c b/networking/libiproute/utils.c index 4ce230356..3cce4a06e 100644 --- a/networking/libiproute/utils.c +++ b/networking/libiproute/utils.c | |||
@@ -175,14 +175,13 @@ static void get_prefix_1(inet_prefix *dst, char *arg, int family) | |||
175 | if (netmask_pfx.family == AF_INET) { | 175 | if (netmask_pfx.family == AF_INET) { |
176 | /* fill in prefix length of dotted quad */ | 176 | /* fill in prefix length of dotted quad */ |
177 | uint32_t mask = ntohl(netmask_pfx.data[0]); | 177 | uint32_t mask = ntohl(netmask_pfx.data[0]); |
178 | uint32_t host = ~mask; | 178 | uint32_t inv = ~mask; |
179 | 179 | ||
180 | /* a valid netmask must be 2^n - 1 */ | 180 | /* a valid netmask must be 11..10..00 */ |
181 | if (host & (host + 1)) | 181 | if (inv & (inv + 1)) |
182 | goto bad; | 182 | goto bad; /* inv is not 00..01..11 */ |
183 | 183 | ||
184 | for (plen = 0; mask; mask <<= 1) | 184 | plen = bb_popcnt_32(mask); |
185 | ++plen; | ||
186 | if (plen > dst->bitlen) | 185 | if (plen > dst->bitlen) |
187 | goto bad; | 186 | goto bad; |
188 | /* dst->flags |= PREFIXLEN_SPECIFIED; */ | 187 | /* dst->flags |= PREFIXLEN_SPECIFIED; */ |
diff --git a/networking/udhcp/dhcpc.c b/networking/udhcp/dhcpc.c index 07e2eadfe..e44086c2e 100644 --- a/networking/udhcp/dhcpc.c +++ b/networking/udhcp/dhcpc.c | |||
@@ -150,18 +150,6 @@ static int sprint_nip(char *dest, const char *pre, const uint8_t *ip) | |||
150 | return sprintf(dest, "%s%u.%u.%u.%u", pre, ip[0], ip[1], ip[2], ip[3]); | 150 | return sprintf(dest, "%s%u.%u.%u.%u", pre, ip[0], ip[1], ip[2], ip[3]); |
151 | } | 151 | } |
152 | 152 | ||
153 | /* really simple implementation, just count the bits */ | ||
154 | static int mton(uint32_t mask) | ||
155 | { | ||
156 | int i = 0; | ||
157 | mask = ntohl(mask); /* 111110000-like bit pattern */ | ||
158 | while (mask) { | ||
159 | i++; | ||
160 | mask <<= 1; | ||
161 | } | ||
162 | return i; | ||
163 | } | ||
164 | |||
165 | #if ENABLE_FEATURE_UDHCPC_SANITIZEOPT | 153 | #if ENABLE_FEATURE_UDHCPC_SANITIZEOPT |
166 | /* Check if a given name represents a valid DNS name */ | 154 | /* Check if a given name represents a valid DNS name */ |
167 | /* See RFC1035, 2.3.1 */ | 155 | /* See RFC1035, 2.3.1 */ |
@@ -508,7 +496,8 @@ static void fill_envp(struct dhcp_packet *packet) | |||
508 | /* Generate extra envvar for DHCP_SUBNET, $mask */ | 496 | /* Generate extra envvar for DHCP_SUBNET, $mask */ |
509 | uint32_t subnet; | 497 | uint32_t subnet; |
510 | move_from_unaligned32(subnet, opt_item->data); | 498 | move_from_unaligned32(subnet, opt_item->data); |
511 | putenvp(xasprintf("mask=%u", mton(subnet))); | 499 | //FIXME: we do not check that subnet has bit pattern 11..10..0 |
500 | putenvp(xasprintf("mask=%u", bb_popcnt_32(subnet))); | ||
512 | } | 501 | } |
513 | } else { | 502 | } else { |
514 | unsigned ofs; | 503 | unsigned ofs; |