source: mainline/uspace/lib/c/generic/cap.c@ e3272101

Last change on this file since e3272101 was e3272101, checked in by Matthieu Riolo <matthieu.riolo@…>, 7 years ago

make ilog10_u64() return an errno_t

The function ilog10_u64() used to return 0
for the value 0. Which is not correct. Either
NaN or -Infinity are correct, but not 0, since
it would be ambiguous with log(1). To ensure this
case the function ilog10_u64() has been changed
to return a errno_t indicating a failure.

  • Property mode set to 100644
File size: 6.3 KB
Line 
1/*
2 * Copyright (c) 2015 Jiri Svoboda
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup libc
30 * @{
31 */
32/**
33 * @file Storage capacity specification.
34 */
35
36#include <cap.h>
37#include <errno.h>
38#include <imath.h>
39#include <stdio.h>
40#include <str.h>
41
42/** Simplified capacity parameters */
43enum {
44 /** Simplified capacity maximum integer digits */
45 scap_max_idig = 3,
46 /** Simplified capacity maximum significant digits */
47 scap_max_sdig = 4
48};
49
50static const char *cu_str[] = {
51 [cu_byte] = "B",
52 [cu_kbyte] = "kB",
53 [cu_mbyte] = "MB",
54 [cu_gbyte] = "GB",
55 [cu_tbyte] = "TB",
56 [cu_pbyte] = "PB",
57 [cu_ebyte] = "EB",
58 [cu_zbyte] = "ZB",
59 [cu_ybyte] = "YB"
60};
61
62void cap_from_blocks(uint64_t nblocks, size_t block_size, cap_spec_t *cap)
63{
64 uint64_t tsize;
65
66 tsize = nblocks * block_size;
67 cap->m = tsize;
68 cap->dp = 0;
69 cap->cunit = cu_byte;
70}
71
72/** Convert capacity to blocks.
73 *
74 * If the value of bytes is not integer, it is properly rounded. If the number
75 * of bytes is not divisible by the number of blocks, it is rounded
76 * up to an integer number of blocks.
77 *
78 * A capacity value entails precision, i.e. it corresponds to a range
79 * of values. @a cvsel selects the value to return. @c cv_nom gives
80 * the nominal (middle) value, @c cv_min gives the minimum value
81 * and @c cv_max gives the maximum value.
82 */
83errno_t cap_to_blocks(cap_spec_t *cap, cap_vsel_t cvsel, size_t block_size,
84 uint64_t *rblocks)
85{
86 int exp;
87 uint64_t bytes;
88 uint64_t f;
89 uint64_t adj;
90 uint64_t blocks;
91 uint64_t rem;
92 errno_t rc;
93
94 exp = cap->cunit * 3 - cap->dp;
95 if (exp < 0) {
96 rc = ipow10_u64(-exp, &f);
97 if (rc != EOK)
98 return ERANGE;
99 bytes = (cap->m + (f / 2)) / f;
100 if (bytes * f - (f / 2) != cap->m)
101 return ERANGE;
102 } else {
103 rc = ipow10_u64(exp, &f);
104 if (rc != EOK)
105 return ERANGE;
106
107 adj = 0;
108 switch (cvsel) {
109 case cv_nom:
110 adj = 0;
111 break;
112 case cv_min:
113 adj = -(f / 2);
114 break;
115 case cv_max:
116 adj = f / 2 - 1;
117 break;
118 }
119
120 bytes = cap->m * f + adj;
121 if ((bytes - adj) / f != cap->m)
122 return ERANGE;
123 }
124
125 rem = bytes % block_size;
126 if ((bytes + rem) < bytes)
127 return ERANGE;
128
129 blocks = (bytes + rem) / block_size;
130
131 *rblocks = blocks;
132 return EOK;
133}
134
135/** Simplify and round capacity to a human-friendly form.
136 *
137 * Change unit and round the number so that we have at most three integer
138 * digits and at most two fractional digits, e.g abc.xy <unit>.
139 */
140void cap_simplify(cap_spec_t *cap)
141{
142 uint64_t div;
143 uint64_t maxv;
144 unsigned sdig;
145 unsigned rdig;
146 errno_t rc;
147
148 /* Change units so that we have at most @c scap_max_idig integer digits */
149 rc = ipow10_u64(scap_max_idig, &maxv);
150 assert(rc == EOK);
151
152 rc = ipow10_u64(cap->dp, &div);
153 assert(rc == EOK);
154
155 while (cap->m / div >= maxv) {
156 ++cap->cunit;
157 cap->dp += 3;
158 div = div * 1000;
159 }
160
161 /* Round the number so that we have at most @c scap_max_sdig significant digits */
162 rc = ilog10_u64(cap->m, &sdig); /* number of significant digits */
163 sdig += 1;
164 assert(rc == EOK);
165 if (sdig > scap_max_sdig) {
166 /* Number of digits to remove */
167 rdig = sdig - scap_max_sdig;
168 if (rdig > cap->dp)
169 rdig = cap->dp;
170
171 rc = ipow10_u64(rdig, &div);
172 assert(rc == EOK);
173
174 cap->m = (cap->m + (div / 2)) / div;
175 cap->dp -= rdig;
176 }
177}
178
179errno_t cap_format(cap_spec_t *cap, char **rstr)
180{
181 errno_t rc;
182 int ret;
183 const char *sunit;
184 uint64_t ipart;
185 uint64_t fpart;
186 uint64_t div;
187
188 sunit = NULL;
189
190 assert(cap->cunit < CU_LIMIT);
191
192 rc = ipow10_u64(cap->dp, &div);
193 if (rc != EOK)
194 return rc;
195
196 ipart = cap->m / div;
197 fpart = cap->m % div;
198
199 sunit = cu_str[cap->cunit];
200 if (cap->dp > 0) {
201 ret = asprintf(rstr, "%" PRIu64 ".%0*" PRIu64 " %s", ipart,
202 (int)cap->dp, fpart, sunit);
203 } else {
204 ret = asprintf(rstr, "%" PRIu64 " %s", ipart, sunit);
205 }
206 if (ret < 0)
207 return ENOMEM;
208
209 return EOK;
210}
211
212static errno_t cap_digit_val(char c, int *val)
213{
214 switch (c) {
215 case '0':
216 *val = 0;
217 break;
218 case '1':
219 *val = 1;
220 break;
221 case '2':
222 *val = 2;
223 break;
224 case '3':
225 *val = 3;
226 break;
227 case '4':
228 *val = 4;
229 break;
230 case '5':
231 *val = 5;
232 break;
233 case '6':
234 *val = 6;
235 break;
236 case '7':
237 *val = 7;
238 break;
239 case '8':
240 *val = 8;
241 break;
242 case '9':
243 *val = 9;
244 break;
245 default:
246 return EINVAL;
247 }
248
249 return EOK;
250}
251
252errno_t cap_parse(const char *str, cap_spec_t *cap)
253{
254 const char *eptr;
255 const char *p;
256 int d;
257 int dp;
258 unsigned long m;
259 int i;
260
261 m = 0;
262
263 eptr = str;
264 while (cap_digit_val(*eptr, &d) == EOK) {
265 m = m * 10 + d;
266 ++eptr;
267 }
268
269 if (*eptr == '.') {
270 ++eptr;
271 dp = 0;
272 while (cap_digit_val(*eptr, &d) == EOK) {
273 m = m * 10 + d;
274 ++dp;
275 ++eptr;
276 }
277 } else {
278 dp = 0;
279 }
280
281 while (*eptr == ' ')
282 ++eptr;
283
284 if (*eptr == '\0') {
285 cap->cunit = cu_byte;
286 } else {
287 for (i = 0; i < CU_LIMIT; i++) {
288 if (str_lcasecmp(eptr, cu_str[i],
289 str_length(cu_str[i])) == 0) {
290 p = eptr + str_size(cu_str[i]);
291 while (*p == ' ')
292 ++p;
293 if (*p == '\0')
294 goto found;
295 }
296 }
297
298 return EINVAL;
299 found:
300 cap->cunit = i;
301 }
302
303 cap->m = m;
304 cap->dp = dp;
305 return EOK;
306}
307
308/** @}
309 */
Note: See TracBrowser for help on using the repository browser.