162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci#include <stdio.h>
362306a36Sopenharmony_ci#include <stdlib.h>
462306a36Sopenharmony_ci#include <string.h>
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <helpers/bitmask.h>
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci/* How many bits in an unsigned long */
962306a36Sopenharmony_ci#define bitsperlong (8 * sizeof(unsigned long))
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci/* howmany(a,b) : how many elements of size b needed to hold all of a */
1262306a36Sopenharmony_ci#define howmany(x, y) (((x)+((y)-1))/(y))
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci/* How many longs in mask of n bits */
1562306a36Sopenharmony_ci#define longsperbits(n) howmany(n, bitsperlong)
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#define max(a, b) ((a) > (b) ? (a) : (b))
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci/*
2062306a36Sopenharmony_ci * Allocate and free `struct bitmask *`
2162306a36Sopenharmony_ci */
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci/* Allocate a new `struct bitmask` with a size of n bits */
2462306a36Sopenharmony_cistruct bitmask *bitmask_alloc(unsigned int n)
2562306a36Sopenharmony_ci{
2662306a36Sopenharmony_ci	struct bitmask *bmp;
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci	bmp = malloc(sizeof(*bmp));
2962306a36Sopenharmony_ci	if (!bmp)
3062306a36Sopenharmony_ci		return 0;
3162306a36Sopenharmony_ci	bmp->size = n;
3262306a36Sopenharmony_ci	bmp->maskp = calloc(longsperbits(n), sizeof(unsigned long));
3362306a36Sopenharmony_ci	if (!bmp->maskp) {
3462306a36Sopenharmony_ci		free(bmp);
3562306a36Sopenharmony_ci		return 0;
3662306a36Sopenharmony_ci	}
3762306a36Sopenharmony_ci	return bmp;
3862306a36Sopenharmony_ci}
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci/* Free `struct bitmask` */
4162306a36Sopenharmony_civoid bitmask_free(struct bitmask *bmp)
4262306a36Sopenharmony_ci{
4362306a36Sopenharmony_ci	if (!bmp)
4462306a36Sopenharmony_ci		return;
4562306a36Sopenharmony_ci	free(bmp->maskp);
4662306a36Sopenharmony_ci	bmp->maskp = (unsigned long *)0xdeadcdef;  /* double free tripwire */
4762306a36Sopenharmony_ci	free(bmp);
4862306a36Sopenharmony_ci}
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci/*
5162306a36Sopenharmony_ci * The routines _getbit() and _setbit() are the only
5262306a36Sopenharmony_ci * routines that actually understand the layout of bmp->maskp[].
5362306a36Sopenharmony_ci *
5462306a36Sopenharmony_ci * On little endian architectures, this could simply be an array of
5562306a36Sopenharmony_ci * bytes.  But the kernel layout of bitmasks _is_ visible to userspace
5662306a36Sopenharmony_ci * via the sched_(set/get)affinity calls in Linux 2.6, and on big
5762306a36Sopenharmony_ci * endian architectures, it is painfully obvious that this is an
5862306a36Sopenharmony_ci * array of unsigned longs.
5962306a36Sopenharmony_ci */
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci/* Return the value (0 or 1) of bit n in bitmask bmp */
6262306a36Sopenharmony_cistatic unsigned int _getbit(const struct bitmask *bmp, unsigned int n)
6362306a36Sopenharmony_ci{
6462306a36Sopenharmony_ci	if (n < bmp->size)
6562306a36Sopenharmony_ci		return (bmp->maskp[n/bitsperlong] >> (n % bitsperlong)) & 1;
6662306a36Sopenharmony_ci	else
6762306a36Sopenharmony_ci		return 0;
6862306a36Sopenharmony_ci}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci/* Set bit n in bitmask bmp to value v (0 or 1) */
7162306a36Sopenharmony_cistatic void _setbit(struct bitmask *bmp, unsigned int n, unsigned int v)
7262306a36Sopenharmony_ci{
7362306a36Sopenharmony_ci	if (n < bmp->size) {
7462306a36Sopenharmony_ci		if (v)
7562306a36Sopenharmony_ci			bmp->maskp[n/bitsperlong] |= 1UL << (n % bitsperlong);
7662306a36Sopenharmony_ci		else
7762306a36Sopenharmony_ci			bmp->maskp[n/bitsperlong] &=
7862306a36Sopenharmony_ci				~(1UL << (n % bitsperlong));
7962306a36Sopenharmony_ci	}
8062306a36Sopenharmony_ci}
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci/*
8362306a36Sopenharmony_ci * When parsing bitmask lists, only allow numbers, separated by one
8462306a36Sopenharmony_ci * of the allowed next characters.
8562306a36Sopenharmony_ci *
8662306a36Sopenharmony_ci * The parameter 'sret' is the return from a sscanf "%u%c".  It is
8762306a36Sopenharmony_ci * -1 if the sscanf input string was empty.  It is 0 if the first
8862306a36Sopenharmony_ci * character in the sscanf input string was not a decimal number.
8962306a36Sopenharmony_ci * It is 1 if the unsigned number matching the "%u" was the end of the
9062306a36Sopenharmony_ci * input string.  It is 2 if one or more additional characters followed
9162306a36Sopenharmony_ci * the matched unsigned number.  If it is 2, then 'nextc' is the first
9262306a36Sopenharmony_ci * character following the number.  The parameter 'ok_next_chars'
9362306a36Sopenharmony_ci * is the nul-terminated list of allowed next characters.
9462306a36Sopenharmony_ci *
9562306a36Sopenharmony_ci * The mask term just scanned was ok if and only if either the numbers
9662306a36Sopenharmony_ci * matching the %u were all of the input or if the next character in
9762306a36Sopenharmony_ci * the input past the numbers was one of the allowed next characters.
9862306a36Sopenharmony_ci */
9962306a36Sopenharmony_cistatic int scan_was_ok(int sret, char nextc, const char *ok_next_chars)
10062306a36Sopenharmony_ci{
10162306a36Sopenharmony_ci	return sret == 1 ||
10262306a36Sopenharmony_ci		(sret == 2 && strchr(ok_next_chars, nextc) != NULL);
10362306a36Sopenharmony_ci}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_cistatic const char *nexttoken(const char *q,  int sep)
10662306a36Sopenharmony_ci{
10762306a36Sopenharmony_ci	if (q)
10862306a36Sopenharmony_ci		q = strchr(q, sep);
10962306a36Sopenharmony_ci	if (q)
11062306a36Sopenharmony_ci		q++;
11162306a36Sopenharmony_ci	return q;
11262306a36Sopenharmony_ci}
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci/* Set a single bit i in bitmask */
11562306a36Sopenharmony_cistruct bitmask *bitmask_setbit(struct bitmask *bmp, unsigned int i)
11662306a36Sopenharmony_ci{
11762306a36Sopenharmony_ci	_setbit(bmp, i, 1);
11862306a36Sopenharmony_ci	return bmp;
11962306a36Sopenharmony_ci}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci/* Set all bits in bitmask: bmp = ~0 */
12262306a36Sopenharmony_cistruct bitmask *bitmask_setall(struct bitmask *bmp)
12362306a36Sopenharmony_ci{
12462306a36Sopenharmony_ci	unsigned int i;
12562306a36Sopenharmony_ci	for (i = 0; i < bmp->size; i++)
12662306a36Sopenharmony_ci		_setbit(bmp, i, 1);
12762306a36Sopenharmony_ci	return bmp;
12862306a36Sopenharmony_ci}
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci/* Clear all bits in bitmask: bmp = 0 */
13162306a36Sopenharmony_cistruct bitmask *bitmask_clearall(struct bitmask *bmp)
13262306a36Sopenharmony_ci{
13362306a36Sopenharmony_ci	unsigned int i;
13462306a36Sopenharmony_ci	for (i = 0; i < bmp->size; i++)
13562306a36Sopenharmony_ci		_setbit(bmp, i, 0);
13662306a36Sopenharmony_ci	return bmp;
13762306a36Sopenharmony_ci}
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci/* True if all bits are clear */
14062306a36Sopenharmony_ciint bitmask_isallclear(const struct bitmask *bmp)
14162306a36Sopenharmony_ci{
14262306a36Sopenharmony_ci	unsigned int i;
14362306a36Sopenharmony_ci	for (i = 0; i < bmp->size; i++)
14462306a36Sopenharmony_ci		if (_getbit(bmp, i))
14562306a36Sopenharmony_ci			return 0;
14662306a36Sopenharmony_ci	return 1;
14762306a36Sopenharmony_ci}
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci/* True if specified bit i is set */
15062306a36Sopenharmony_ciint bitmask_isbitset(const struct bitmask *bmp, unsigned int i)
15162306a36Sopenharmony_ci{
15262306a36Sopenharmony_ci	return _getbit(bmp, i);
15362306a36Sopenharmony_ci}
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci/* Number of lowest set bit (min) */
15662306a36Sopenharmony_ciunsigned int bitmask_first(const struct bitmask *bmp)
15762306a36Sopenharmony_ci{
15862306a36Sopenharmony_ci	return bitmask_next(bmp, 0);
15962306a36Sopenharmony_ci}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci/* Number of highest set bit (max) */
16262306a36Sopenharmony_ciunsigned int bitmask_last(const struct bitmask *bmp)
16362306a36Sopenharmony_ci{
16462306a36Sopenharmony_ci	unsigned int i;
16562306a36Sopenharmony_ci	unsigned int m = bmp->size;
16662306a36Sopenharmony_ci	for (i = 0; i < bmp->size; i++)
16762306a36Sopenharmony_ci		if (_getbit(bmp, i))
16862306a36Sopenharmony_ci			m = i;
16962306a36Sopenharmony_ci	return m;
17062306a36Sopenharmony_ci}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci/* Number of next set bit at or above given bit i */
17362306a36Sopenharmony_ciunsigned int bitmask_next(const struct bitmask *bmp, unsigned int i)
17462306a36Sopenharmony_ci{
17562306a36Sopenharmony_ci	unsigned int n;
17662306a36Sopenharmony_ci	for (n = i; n < bmp->size; n++)
17762306a36Sopenharmony_ci		if (_getbit(bmp, n))
17862306a36Sopenharmony_ci			break;
17962306a36Sopenharmony_ci	return n;
18062306a36Sopenharmony_ci}
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci/*
18362306a36Sopenharmony_ci * Parses a comma-separated list of numbers and ranges of numbers,
18462306a36Sopenharmony_ci * with optional ':%u' strides modifying ranges, into provided bitmask.
18562306a36Sopenharmony_ci * Some examples of input lists and their equivalent simple list:
18662306a36Sopenharmony_ci *	Input		Equivalent to
18762306a36Sopenharmony_ci *	0-3		0,1,2,3
18862306a36Sopenharmony_ci *	0-7:2		0,2,4,6
18962306a36Sopenharmony_ci *	1,3,5-7		1,3,5,6,7
19062306a36Sopenharmony_ci *	0-3:2,8-15:4	0,2,8,12
19162306a36Sopenharmony_ci */
19262306a36Sopenharmony_ciint bitmask_parselist(const char *buf, struct bitmask *bmp)
19362306a36Sopenharmony_ci{
19462306a36Sopenharmony_ci	const char *p, *q;
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	bitmask_clearall(bmp);
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	q = buf;
19962306a36Sopenharmony_ci	while (p = q, q = nexttoken(q, ','), p) {
20062306a36Sopenharmony_ci		unsigned int a;		/* begin of range */
20162306a36Sopenharmony_ci		unsigned int b;		/* end of range */
20262306a36Sopenharmony_ci		unsigned int s;		/* stride */
20362306a36Sopenharmony_ci		const char *c1, *c2;	/* next tokens after '-' or ',' */
20462306a36Sopenharmony_ci		char nextc;		/* char after sscanf %u match */
20562306a36Sopenharmony_ci		int sret;		/* sscanf return (number of matches) */
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci		sret = sscanf(p, "%u%c", &a, &nextc);
20862306a36Sopenharmony_ci		if (!scan_was_ok(sret, nextc, ",-"))
20962306a36Sopenharmony_ci			goto err;
21062306a36Sopenharmony_ci		b = a;
21162306a36Sopenharmony_ci		s = 1;
21262306a36Sopenharmony_ci		c1 = nexttoken(p, '-');
21362306a36Sopenharmony_ci		c2 = nexttoken(p, ',');
21462306a36Sopenharmony_ci		if (c1 != NULL && (c2 == NULL || c1 < c2)) {
21562306a36Sopenharmony_ci			sret = sscanf(c1, "%u%c", &b, &nextc);
21662306a36Sopenharmony_ci			if (!scan_was_ok(sret, nextc, ",:"))
21762306a36Sopenharmony_ci				goto err;
21862306a36Sopenharmony_ci			c1 = nexttoken(c1, ':');
21962306a36Sopenharmony_ci			if (c1 != NULL && (c2 == NULL || c1 < c2)) {
22062306a36Sopenharmony_ci				sret = sscanf(c1, "%u%c", &s, &nextc);
22162306a36Sopenharmony_ci				if (!scan_was_ok(sret, nextc, ","))
22262306a36Sopenharmony_ci					goto err;
22362306a36Sopenharmony_ci			}
22462306a36Sopenharmony_ci		}
22562306a36Sopenharmony_ci		if (!(a <= b))
22662306a36Sopenharmony_ci			goto err;
22762306a36Sopenharmony_ci		if (b >= bmp->size)
22862306a36Sopenharmony_ci			goto err;
22962306a36Sopenharmony_ci		while (a <= b) {
23062306a36Sopenharmony_ci			_setbit(bmp, a, 1);
23162306a36Sopenharmony_ci			a += s;
23262306a36Sopenharmony_ci		}
23362306a36Sopenharmony_ci	}
23462306a36Sopenharmony_ci	return 0;
23562306a36Sopenharmony_cierr:
23662306a36Sopenharmony_ci	bitmask_clearall(bmp);
23762306a36Sopenharmony_ci	return -1;
23862306a36Sopenharmony_ci}
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci/*
24162306a36Sopenharmony_ci * emit(buf, buflen, rbot, rtop, len)
24262306a36Sopenharmony_ci *
24362306a36Sopenharmony_ci * Helper routine for bitmask_displaylist().  Write decimal number
24462306a36Sopenharmony_ci * or range to buf+len, suppressing output past buf+buflen, with optional
24562306a36Sopenharmony_ci * comma-prefix.  Return len of what would be written to buf, if it
24662306a36Sopenharmony_ci * all fit.
24762306a36Sopenharmony_ci */
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_cistatic inline int emit(char *buf, int buflen, int rbot, int rtop, int len)
25062306a36Sopenharmony_ci{
25162306a36Sopenharmony_ci	if (len > 0)
25262306a36Sopenharmony_ci		len += snprintf(buf + len, max(buflen - len, 0), ",");
25362306a36Sopenharmony_ci	if (rbot == rtop)
25462306a36Sopenharmony_ci		len += snprintf(buf + len, max(buflen - len, 0), "%d", rbot);
25562306a36Sopenharmony_ci	else
25662306a36Sopenharmony_ci		len += snprintf(buf + len, max(buflen - len, 0), "%d-%d",
25762306a36Sopenharmony_ci				rbot, rtop);
25862306a36Sopenharmony_ci	return len;
25962306a36Sopenharmony_ci}
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci/*
26262306a36Sopenharmony_ci * Write decimal list representation of bmp to buf.
26362306a36Sopenharmony_ci *
26462306a36Sopenharmony_ci * Output format is a comma-separated list of decimal numbers and
26562306a36Sopenharmony_ci * ranges.  Consecutively set bits are shown as two hyphen-separated
26662306a36Sopenharmony_ci * decimal numbers, the smallest and largest bit numbers set in
26762306a36Sopenharmony_ci * the range.  Output format is compatible with the format
26862306a36Sopenharmony_ci * accepted as input by bitmap_parselist().
26962306a36Sopenharmony_ci *
27062306a36Sopenharmony_ci * The return value is the number of characters which would be
27162306a36Sopenharmony_ci * generated for the given input, excluding the trailing '\0', as
27262306a36Sopenharmony_ci * per ISO C99.
27362306a36Sopenharmony_ci */
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ciint bitmask_displaylist(char *buf, int buflen, const struct bitmask *bmp)
27662306a36Sopenharmony_ci{
27762306a36Sopenharmony_ci	int len = 0;
27862306a36Sopenharmony_ci	/* current bit is 'cur', most recently seen range is [rbot, rtop] */
27962306a36Sopenharmony_ci	unsigned int cur, rbot, rtop;
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	if (buflen > 0)
28262306a36Sopenharmony_ci		*buf = 0;
28362306a36Sopenharmony_ci	rbot = cur = bitmask_first(bmp);
28462306a36Sopenharmony_ci	while (cur < bmp->size) {
28562306a36Sopenharmony_ci		rtop = cur;
28662306a36Sopenharmony_ci		cur = bitmask_next(bmp, cur+1);
28762306a36Sopenharmony_ci		if (cur >= bmp->size || cur > rtop + 1) {
28862306a36Sopenharmony_ci			len = emit(buf, buflen, rbot, rtop, len);
28962306a36Sopenharmony_ci			rbot = cur;
29062306a36Sopenharmony_ci		}
29162306a36Sopenharmony_ci	}
29262306a36Sopenharmony_ci	return len;
29362306a36Sopenharmony_ci}
294