aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenys Vlasenko <vda.linux@googlemail.com>2024-05-31 12:01:43 +0200
committerDenys Vlasenko <vda.linux@googlemail.com>2024-05-31 16:03:23 +0200
commit2075553a1b0eb843d4ee73a039b3e30bed9274e0 (patch)
tree904f192fa9d7c920c9dde6a7bc26b208c3b03312
parent5a68a246e750359819d63bcff5ef97dd9c7788fc (diff)
downloadbusybox-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.c9
-rw-r--r--include/libbb.h7
-rw-r--r--libbb/Kbuild.src1
-rw-r--r--libbb/popcnt.c46
-rw-r--r--networking/ifupdown.c8
-rw-r--r--networking/ipcalc.c21
-rw-r--r--networking/libiproute/utils.c11
-rw-r--r--networking/udhcp/dhcpc.c15
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;
397uint64_t bb_bswap_64(uint64_t x) FAST_FUNC; 397uint64_t bb_bswap_64(uint64_t x) FAST_FUNC;
398#endif 398#endif
399 399
400unsigned FAST_FUNC bb_popcnt_32(uint32_t m);
401#if ULONG_MAX > 0xffffffff
402unsigned FAST_FUNC bb_popcnt_long(unsigned_long m);
403#else
404#define bb_popcnt_long(m) bb_popcnt_32(m)
405#endif
406
400unsigned long FAST_FUNC isqrt(unsigned long long N); 407unsigned long FAST_FUNC isqrt(unsigned long long N);
401 408
402unsigned long long monotonic_ns(void) FAST_FUNC; 409unsigned 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
72lib-y += perror_nomsg_and_die.o 72lib-y += perror_nomsg_and_die.o
73lib-y += pidfile.o 73lib-y += pidfile.o
74lib-y += platform.o 74lib-y += platform.o
75lib-y += popcnt.o
75lib-y += printable.o 76lib-y += printable.o
76lib-y += printable_string.o 77lib-y += printable_string.o
77lib-y += print_flags.o 78lib-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
11unsigned 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
37unsigned 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
75static 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
89int 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 */
154static 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;