The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
size.c
Go to the documentation of this file.
1/*
2 * This program is is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or (at
5 * your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17/** Size printing and parsing functions
18 *
19 * @file src/lib/util/size.c
20 *
21 * @copyright 2022 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
22 */
23RCSID("$Id: bf93de635b48c588c48a809721c2a68f1e3bb121 $")
24
25#include <freeradius-devel/util/math.h>
26#include <freeradius-devel/util/sbuff.h>
27
28#include "size.h"
29
30/** Parse a size string with optional unit
31 *
32 * Default scale with no suffix is bytes.
33 *
34 * @param[out] out Parsed and scaled size
35 * @param[in] in sbuff to parse.
36 * @return
37 * - >0 on success.
38 * - <0 on error.
39 */
41{
42 static uint64_t base2_units[]= {
43 ['k'] = (uint64_t)1024,
44 ['m'] = (uint64_t)1024 * 1024,
45 ['g'] = (uint64_t)1024 * 1024 * 1024,
46 ['t'] = (uint64_t)1024 * 1024 * 1024 * 1024,
47 ['p'] = (uint64_t)1024 * 1024 * 1024 * 1024 * 1024,
48 ['e'] = (uint64_t)1024 * 1024 * 1024 * 1024 * 1024 * 1024,
49 };
50 static size_t base2_units_len = NUM_ELEMENTS(base2_units);
51
52 static uint64_t base10_units[] = {
53 ['k'] = (uint64_t)1000,
54 ['m'] = (uint64_t)1000 * 1000,
55 ['g'] = (uint64_t)1000 * 1000 * 1000,
56 ['t'] = (uint64_t)1000 * 1000 * 1000 * 1000,
57 ['p'] = (uint64_t)1000 * 1000 * 1000 * 1000 * 1000,
58 ['e'] = (uint64_t)1000 * 1000 * 1000 * 1000 * 1000 * 1000,
59 };
60 static size_t base10_units_len = NUM_ELEMENTS(base10_units);
61
62 fr_sbuff_t our_in = FR_SBUFF(in);
63 char c = '\0';
64 uint64_t size;
65
66 *out = 0;
67
68 if (fr_sbuff_out(NULL, &size, &our_in) < 0) FR_SBUFF_ERROR_RETURN(&our_in);
69 if (!fr_sbuff_extend(&our_in)) goto done;
70
71 c = tolower(fr_sbuff_char(&our_in, '\0'));
72
73 /*
74 * Special cases first...
75 */
76 switch (c) {
77 case 'n': /* nibble */
78 fr_sbuff_next(&our_in);
79 if (size & 0x01) {
80 fr_strerror_const("Sizes specified in nibbles must be an even number");
81 fr_sbuff_set_to_start(&our_in);
82 FR_SBUFF_ERROR_RETURN(&our_in);
83 }
84 size /= 2;
85 break;
86
87 case '\0':
88 break;
89
90 case 'b': /* byte */
91 fr_sbuff_next(&our_in);
92 break;
93
94 default:
95 {
96 uint64_t *units;
97 size_t units_len;
98 bool is_base2;
99
100 fr_sbuff_next(&our_in);
101 is_base2 = fr_sbuff_next_if_char(&our_in, 'i') || fr_sbuff_next_if_char(&our_in, 'I');
102
103 if (!fr_sbuff_next_if_char(&our_in, 'b')) (void)fr_sbuff_next_if_char(&our_in, 'B'); /* Optional */
104
105 if (is_base2) {
106 units = base2_units;
107 units_len = base2_units_len;
108 } else {
109 units = base10_units;
110 units_len = base10_units_len;
111 }
112
113 if (((size_t)c >= units_len) || units[(uint8_t)c] == 0) {
114 fr_strerror_printf("Unknown unit '%c'", c);
115 FR_SBUFF_ERROR_RETURN(&our_in);
116 }
117
118 if (!fr_multiply(&size, size, units[(uint8_t)c])) {
119 overflow:
120 fr_strerror_printf("Value must be less than %zu", (size_t)SIZE_MAX);
121 fr_sbuff_set_to_start(&our_in);
122 FR_SBUFF_ERROR_RETURN(&our_in);
123 }
124 }
125 }
126
127 if (size > SIZE_MAX) {
128 fr_strerror_printf("Value %" PRIu64 " is greater than the maximum "
129 "file/memory size of this system (%zu)", size, (size_t)SIZE_MAX);
130
131 goto overflow;
132 }
133
134done:
135 *out = (size_t)size;
136
137 FR_SBUFF_SET_RETURN(in, &our_in);
138}
139
140typedef struct {
141 char const *suffix;
142 uint64_t mul;
144
145/** Print a size string with unit
146 *
147 * Suffix is the largest unit possible without losing precision.
148 *
149 * @param[out] out To write size to.
150 * @param[in] in size to print.
151 * @return
152 * - >0 on success.
153 * - <0 on error.
154 */
156{
157 fr_sbuff_t our_out = FR_SBUFF(out);
158
159 static fr_size_unit_t const base2_units[] = {
160 { "B", (uint64_t)1 },
161 { "KiB", (uint64_t)1024 },
162 { "MiB", (uint64_t)1024 * 1024 },
163 { "GiB", (uint64_t)1024 * 1024 * 1024},
164 { "TiB", (uint64_t)1024 * 1024 * 1024 * 1024},
165 { "PiB", (uint64_t)1024 * 1024 * 1024 * 1024 * 1024},
166 { "EiB", (uint64_t)1024 * 1024 * 1024 * 1024 * 1024 * 1024},
167 };
168 static fr_size_unit_t const base10_units[] = {
169 { "B", (uint64_t)1 },
170 { "KB", (uint64_t)1000 },
171 { "MB", (uint64_t)1000 * 1000 },
172 { "GB", (uint64_t)1000 * 1000 * 1000},
173 { "TB", (uint64_t)1000 * 1000 * 1000 * 1000},
174 { "PB", (uint64_t)1000 * 1000 * 1000 * 1000 * 1000},
175 { "EB", (uint64_t)1000 * 1000 * 1000 * 1000 * 1000 * 1000},
176 };
177 fr_slen_t slen;
178 fr_size_unit_t const *unit = &base10_units[0];
179 uint8_t b2_idx = 0, b10_idx = 0;
180
181 uint8_t pos2 = fr_low_bit_pos(in);
182 uint8_t pos10;
183 size_t tmp;
184
185 /*
186 * Fast path - Won't be divisible by a power of 1000 or a power of 1024
187 */
188 if (pos2 < 3) goto done;
189 pos2--;
190
191 /*
192 * Get a count of trailing decimal zeroes.
193 */
194 for (tmp = in, pos10 = 0; tmp && ((tmp % 1000) == 0); pos10++) tmp /= 1000;
195
196 if (pos10 > 0) b10_idx = (uint8_t)pos10;
197 if (pos2 >= 10) b2_idx = (uint8_t)(pos2 / 10);
198
199 /*
200 * Pick the most significant base2 or base10 unit, preferring base10.
201 */
202 if (b2_idx > b10_idx) {
203 unit = &base2_units[b2_idx];
204 } else {
205 unit = &base10_units[b10_idx];
206 }
207
208done:
209 slen = fr_sbuff_in_sprintf(&our_out, "%zu%s", in / unit->mul, unit->suffix);
210 if (slen < 0) return slen;
211 return fr_sbuff_set(out, &our_out);
212}
#define RCSID(id)
Definition build.h:483
#define NUM_ELEMENTS(_t)
Definition build.h:337
static fr_slen_t in
Definition dict.h:824
static uint8_t fr_low_bit_pos(uint64_t num)
Find the lowest order high bit in an unsigned 64 bit integer.
Definition math.h:55
#define fr_multiply(_out, _a, _b)
Multiplies two integers together.
Definition math.h:118
unsigned char uint8_t
ssize_t fr_slen_t
unsigned long int size_t
static bool done
Definition radclient.c:80
ssize_t fr_sbuff_in_sprintf(fr_sbuff_t *sbuff, char const *fmt,...)
Print using a fmt string to an sbuff.
Definition sbuff.c:1595
bool fr_sbuff_next_if_char(fr_sbuff_t *sbuff, char c)
Return true if the current char matches, and if it does, advance.
Definition sbuff.c:2088
#define fr_sbuff_set(_dst, _src)
#define fr_sbuff_char(_sbuff_or_marker, _eob)
#define fr_sbuff_extend(_sbuff_or_marker)
#define FR_SBUFF_ERROR_RETURN(_sbuff_or_marker)
#define FR_SBUFF_SET_RETURN(_dst, _src)
#define FR_SBUFF(_sbuff_or_marker)
#define fr_sbuff_out(_err, _out, _in)
fr_slen_t fr_size_from_str(size_t *out, fr_sbuff_t *in)
Parse a size string with optional unit.
Definition size.c:40
fr_slen_t fr_size_to_str(fr_sbuff_t *out, size_t in)
Print a size string with unit.
Definition size.c:155
uint64_t mul
Definition size.c:142
char const * suffix
Definition size.c:141
Boxed value structures and functions to manipulate them.
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition strerror.h:64
#define fr_strerror_const(_msg)
Definition strerror.h:223
static size_t char ** out
Definition value.h:997