The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
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  */
23 RCSID("$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 
134 done:
135  *out = (size_t)size;
136 
137  FR_SBUFF_SET_RETURN(in, &our_in);
138 }
139 
140 typedef 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 
208 done:
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:444
#define NUM_ELEMENTS(_t)
Definition: build.h:335
static fr_slen_t in
Definition: dict.h:645
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
Definition: merged_model.c:30
ssize_t fr_slen_t
Definition: merged_model.c:35
unsigned long int size_t
Definition: merged_model.c:25
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:1554
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:2047
#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(_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
FR_SBUFF_SET_RETURN(sbuff, &our_sbuff)
static size_t char ** out
Definition: value.h:984