Coverage Report

Created: 2026-01-10 06:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/freeradius-server/src/lib/util/size.c
Line
Count
Source
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
 */
40
fr_slen_t fr_size_from_str(size_t *out, fr_sbuff_t *in)
41
48
{
42
48
  static uint64_t base2_units[]= {
43
48
    ['k'] = (uint64_t)1024,
44
48
    ['m'] = (uint64_t)1024 * 1024,
45
48
    ['g'] = (uint64_t)1024 * 1024 * 1024,
46
48
    ['t'] = (uint64_t)1024 * 1024 * 1024 * 1024,
47
48
    ['p'] = (uint64_t)1024 * 1024 * 1024 * 1024 * 1024,
48
48
    ['e'] = (uint64_t)1024 * 1024 * 1024 * 1024 * 1024 * 1024,
49
48
  };
50
48
  static size_t base2_units_len = NUM_ELEMENTS(base2_units);
51
52
48
  static uint64_t base10_units[] = {
53
48
    ['k'] = (uint64_t)1000,
54
48
    ['m'] = (uint64_t)1000 * 1000,
55
48
    ['g'] = (uint64_t)1000 * 1000 * 1000,
56
48
    ['t'] = (uint64_t)1000 * 1000 * 1000 * 1000,
57
48
    ['p'] = (uint64_t)1000 * 1000 * 1000 * 1000 * 1000,
58
48
    ['e'] = (uint64_t)1000 * 1000 * 1000 * 1000 * 1000 * 1000,
59
48
  };
60
48
  static size_t base10_units_len = NUM_ELEMENTS(base10_units);
61
62
48
  fr_sbuff_t  our_in = FR_SBUFF(in);
63
48
  char    c = '\0';
64
48
  uint64_t  size;
65
66
48
  *out = 0;
67
68
48
  if (fr_sbuff_out(NULL, &size, &our_in) < 0) FR_SBUFF_ERROR_RETURN(&our_in);
69
40
  if (!fr_sbuff_extend(&our_in)) goto done;
70
71
36
  c = tolower(fr_sbuff_char(&our_in, '\0'));
72
73
  /*
74
   *  Special cases first...
75
   */
76
36
  switch (c) {
77
2
  case 'n':   /* nibble */
78
2
    fr_sbuff_next(&our_in);
79
2
    if (size & 0x01) {
80
1
      fr_strerror_const("Sizes specified in nibbles must be an even number");
81
1
      fr_sbuff_set_to_start(&our_in);
82
1
      FR_SBUFF_ERROR_RETURN(&our_in);
83
1
    }
84
1
    size /= 2;
85
1
    break;
86
87
2
  case '\0':
88
2
    break;
89
90
2
  case 'b':   /* byte */
91
2
    fr_sbuff_next(&our_in);
92
2
    break;
93
94
30
  default:
95
30
  {
96
30
    uint64_t  *units;
97
30
    size_t    units_len;
98
30
    bool    is_base2;
99
100
30
    fr_sbuff_next(&our_in);
101
30
    is_base2 = fr_sbuff_next_if_char(&our_in, 'i') || fr_sbuff_next_if_char(&our_in, 'I');
102
103
30
    if (!fr_sbuff_next_if_char(&our_in, 'b')) (void)fr_sbuff_next_if_char(&our_in, 'B'); /* Optional */
104
105
30
    if (is_base2) {
106
6
      units = base2_units;
107
6
      units_len = base2_units_len;
108
24
    } else {
109
24
      units = base10_units;
110
24
      units_len = base10_units_len;
111
24
    }
112
113
30
    if (((size_t)c >= units_len) || units[(uint8_t)c] == 0) {
114
21
      fr_strerror_printf("Unknown unit '%c'", c);
115
21
      FR_SBUFF_ERROR_RETURN(&our_in);
116
21
    }
117
118
9
    if (!fr_multiply(&size, size, units[(uint8_t)c])) {
119
1
    overflow:
120
1
      fr_strerror_printf("Value must be less than %zu", (size_t)SIZE_MAX);
121
1
      fr_sbuff_set_to_start(&our_in);
122
1
      FR_SBUFF_ERROR_RETURN(&our_in);
123
1
    }
124
9
  }
125
36
  }
126
127
13
  if (size > SIZE_MAX) {
128
0
    fr_strerror_printf("Value %" PRIu64 " is greater than the maximum "
129
0
           "file/memory size of this system (%zu)", size, (size_t)SIZE_MAX);
130
131
0
    goto overflow;
132
0
  }
133
134
17
done:
135
17
  *out = (size_t)size;
136
137
17
  FR_SBUFF_SET_RETURN(in, &our_in);
138
13
}
139
140
typedef struct {
141
  char const *suffix;
142
  uint64_t mul;
143
} fr_size_unit_t;
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
 */
155
fr_slen_t fr_size_to_str(fr_sbuff_t *out, size_t in)
156
0
{
157
0
  fr_sbuff_t    our_out = FR_SBUFF(out);
158
159
0
  static fr_size_unit_t const base2_units[] = {
160
0
          { "B",    (uint64_t)1 },
161
0
          { "KiB",  (uint64_t)1024 },
162
0
          { "MiB",  (uint64_t)1024 * 1024 },
163
0
          { "GiB",  (uint64_t)1024 * 1024 * 1024},
164
0
          { "TiB",  (uint64_t)1024 * 1024 * 1024 * 1024},
165
0
          { "PiB",  (uint64_t)1024 * 1024 * 1024 * 1024 * 1024},
166
0
          { "EiB",  (uint64_t)1024 * 1024 * 1024 * 1024 * 1024 * 1024},
167
0
        };
168
0
  static fr_size_unit_t const base10_units[] = {
169
0
          { "B",    (uint64_t)1 },
170
0
          { "KB",   (uint64_t)1000 },
171
0
          { "MB",   (uint64_t)1000 * 1000 },
172
0
          { "GB",   (uint64_t)1000 * 1000 * 1000},
173
0
          { "TB",   (uint64_t)1000 * 1000 * 1000 * 1000},
174
0
          { "PB",   (uint64_t)1000 * 1000 * 1000 * 1000 * 1000},
175
0
          { "EB",   (uint64_t)1000 * 1000 * 1000 * 1000 * 1000 * 1000},
176
0
        };
177
0
  fr_slen_t slen;
178
0
  fr_size_unit_t const *unit = &base10_units[0];
179
0
  uint8_t b2_idx = 0, b10_idx = 0;
180
181
0
  uint8_t pos2 = fr_low_bit_pos(in);
182
0
  uint8_t pos10;
183
0
  size_t tmp;
184
185
  /*
186
   *  Fast path - Won't be divisible by a power of 1000 or a power of 1024
187
   */
188
0
  if (pos2 < 3) goto done;
189
0
  pos2--;
190
191
  /*
192
   *  Get a count of trailing decimal zeroes.
193
   */
194
0
  for (tmp = in, pos10 = 0; tmp && ((tmp % 1000) == 0); pos10++) tmp /= 1000;
195
196
0
  if (pos10 > 0) b10_idx = (uint8_t)pos10;
197
0
  if (pos2 >= 10) b2_idx = (uint8_t)(pos2 / 10);
198
199
  /*
200
   *  Pick the most significant base2 or base10 unit, preferring base10.
201
   */
202
0
  if (b2_idx > b10_idx) {
203
0
    unit = &base2_units[b2_idx];
204
0
  } else {
205
0
    unit = &base10_units[b10_idx];
206
0
  }
207
208
0
done:
209
0
  slen = fr_sbuff_in_sprintf(&our_out, "%zu%s", in / unit->mul, unit->suffix);
210
0
  if (slen < 0) return slen;
211
0
  return fr_sbuff_set(out, &our_out);
212
0
}