Skip to content

Commit d8e97e1

Browse files
committed
libutil: add parse_size()
Problem: it is useful to not have to write a one off parser for options and config values that express a size with SI units. Add parse_size().
1 parent 7b6a459 commit d8e97e1

File tree

3 files changed

+174
-1
lines changed

3 files changed

+174
-1
lines changed

src/common/libutil/Makefile.am

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,9 @@ libutil_la_SOURCES = \
102102
basemoji.h \
103103
basemoji.c \
104104
sigutil.h \
105-
sigutil.c
105+
sigutil.c \
106+
parse_size.h \
107+
parse_size.c
106108

107109
EXTRA_DIST = veb_mach.c
108110

src/common/libutil/parse_size.c

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
/************************************************************\
2+
* Copyright 2023 Lawrence Livermore National Security, LLC
3+
* (c.f. AUTHORS, NOTICE.LLNS, COPYING)
4+
*
5+
* This file is part of the Flux resource manager framework.
6+
* For details, see https://github.com/flux-framework.
7+
*
8+
* SPDX-License-Identifier: LGPL-3.0
9+
\************************************************************/
10+
11+
#if HAVE_CONFIG_H
12+
#include "config.h"
13+
#endif
14+
15+
#include <stdint.h>
16+
#include <errno.h>
17+
#include <stdlib.h>
18+
#include <string.h>
19+
#include <stdbool.h>
20+
#include <math.h>
21+
22+
#include <ccan/array_size/array_size.h>
23+
#include <ccan/str/str.h>
24+
25+
#include "parse_size.h"
26+
27+
struct scale {
28+
const char *s;
29+
uint64_t scale;
30+
};
31+
32+
static struct scale mtab[] = {
33+
{ "", 1 },
34+
{ "k", 1024 },
35+
{ "K", 1024 }, // upper case K is not the SI prefix but is unambiguous
36+
{ "M", 1024*1024 },
37+
{ "G", 1024UL*1024*1024 },
38+
{ "T", 1024ULL*1024*1024*1024 },
39+
{ "P", 1024ULL*1024*1024*1024*1024 },
40+
{ "E", 1024ULL*1024*1024*1024*1024*1024 },
41+
};
42+
43+
static int lookup_scale (const char *s, uint64_t *vp)
44+
{
45+
for (int i = 0; i < ARRAY_SIZE (mtab); i++) {
46+
if (streq (mtab[i].s, s)) {
47+
*vp = mtab[i].scale;
48+
return 0;
49+
}
50+
}
51+
return -1;
52+
}
53+
54+
static bool invalid_fp_size (double val)
55+
{
56+
switch (fpclassify (val)) {
57+
case FP_NORMAL: // [[fallthrough]]
58+
case FP_SUBNORMAL: // [[fallthrough]]
59+
case FP_ZERO: // [[fallthrough]]
60+
break; // OK
61+
case FP_NAN: // [[fallthrough]]
62+
case FP_INFINITE: // [[fallthrough]]
63+
default: // something else, bad
64+
return true;
65+
}
66+
if (val < 0.)
67+
return true;
68+
return false;
69+
}
70+
71+
static int parse_as_integer (const char *s, uint64_t *up)
72+
{
73+
char *endptr;
74+
uint64_t u;
75+
uint64_t scale;
76+
77+
// strtoull() allows a leading minus sign but we do not
78+
if (strchr (s, '-')) {
79+
errno = EINVAL;
80+
return -1;
81+
}
82+
errno = 0;
83+
u = strtoull (s, &endptr, 0);
84+
if (errno != 0
85+
|| endptr == s
86+
|| lookup_scale (endptr, &scale) < 0) {
87+
errno = EINVAL;
88+
return -1;
89+
}
90+
uint64_t result = u * scale;
91+
if (result < u) {
92+
errno = EOVERFLOW;
93+
return -1;
94+
}
95+
*up = result;
96+
return 0;
97+
}
98+
99+
static int parse_as_double (const char *s, uint64_t *up)
100+
{
101+
char *endptr;
102+
double d;
103+
uint64_t scale;
104+
105+
errno = 0;
106+
d = strtold (s, &endptr);
107+
if (errno != 0
108+
|| endptr == s
109+
|| lookup_scale (endptr, &scale) < 0
110+
|| invalid_fp_size (d)) {
111+
errno = EINVAL;
112+
return -1;
113+
}
114+
double result = floor (d * scale);
115+
if (result > UINT64_MAX) {
116+
errno = EOVERFLOW;
117+
return -1;
118+
}
119+
*up = (uint64_t)result;
120+
return 0;
121+
}
122+
123+
int parse_size (const char *s, uint64_t *vp)
124+
{
125+
if (!s || !vp) {
126+
errno = EINVAL;
127+
return -1;
128+
}
129+
if (parse_as_integer (s, vp) < 0
130+
&& parse_as_double (s, vp) < 0)
131+
return -1;
132+
return 0;
133+
}
134+
135+
// vi:ts=4 sw=4 expandtab

src/common/libutil/parse_size.h

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/************************************************************\
2+
* Copyright 2023 Lawrence Livermore National Security, LLC
3+
* (c.f. AUTHORS, NOTICE.LLNS, COPYING)
4+
*
5+
* This file is part of the Flux resource manager framework.
6+
* For details, see https://github.com/flux-framework.
7+
*
8+
* SPDX-License-Identifier: LGPL-3.0
9+
\************************************************************/
10+
11+
#ifndef _UTIL_PARSE_SIZE_H
12+
#define _UTIL_PARSE_SIZE_H
13+
14+
#include <stdint.h>
15+
16+
/* Parse 's' as a floating point quantity scaled by optional suffix:
17+
*
18+
* k,K 2^10 (1024)
19+
* M 2^20
20+
* G 2^30
21+
* T 2^40
22+
* P 2^50
23+
* E 2^60
24+
*
25+
* The numeric part is parsed with first strtoull(3) then strtod(3), so
26+
* so all input supported by those functions should work including
27+
* decimal (255), hex (0xf), octal (0377 prefix), exponent (2.55E2), etc.
28+
*
29+
* Assign the result to 'vp' and return 0 on success,
30+
* or return -1 on failure with errno set (EINVAL, EOVERFLOW).
31+
*/
32+
int parse_size (const char *s, uint64_t *vp);
33+
34+
#endif /* !_UTIL_PARSE_SIZE_H */
35+
36+
// vi:ts=4 sw=4 expandtab

0 commit comments

Comments
 (0)