source: mainline/uspace/lib/c/generic/capa.c@ b83c5e4

ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since b83c5e4 was 5fc8244, checked in by Jiri Svoboda <jiri@…>, 4 years ago

Move device-related stuff out of libc to libdevice

Unfortunately, we need to keep clock_dev, which pulls in hw_res
and pio_window. clock_dev is used by time.c

  • Property mode set to 100644
File size: 6.8 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 <assert.h>
37#include <capa.h>
38#include <errno.h>
39#include <imath.h>
40#include <stddef.h>
41#include <stdio.h>
42#include <str.h>
43
44/** Simplified capacity parameters */
45enum {
46 /** Simplified capacity maximum integer digits */
47 scapa_max_idig = 3,
48 /** Simplified capacity maximum significant digits */
49 scapa_max_sdig = 4
50};
51
52static const char *cu_str[] = {
53 [cu_byte] = "B",
54 [cu_kbyte] = "kB",
55 [cu_mbyte] = "MB",
56 [cu_gbyte] = "GB",
57 [cu_tbyte] = "TB",
58 [cu_pbyte] = "PB",
59 [cu_ebyte] = "EB",
60 [cu_zbyte] = "ZB",
61 [cu_ybyte] = "YB"
62};
63
64void capa_from_blocks(uint64_t nblocks, size_t block_size, capa_spec_t *capa)
65{
66 uint64_t tsize;
67
68 tsize = nblocks * block_size;
69 capa->m = tsize;
70 capa->dp = 0;
71 capa->cunit = cu_byte;
72}
73
74/** Convert capacity to blocks.
75 *
76 * If the value of bytes is not integer, it is properly rounded. If the number
77 * of bytes is not divisible by the number of blocks, it is rounded
78 * up to an integer number of blocks.
79 *
80 * A capacity value entails precision, i.e. it corresponds to a range
81 * of values. @a cvsel selects the value to return. @c cv_nom gives
82 * the nominal (middle) value, @c cv_min gives the minimum value
83 * and @c cv_max gives the maximum value.
84 */
85errno_t capa_to_blocks(capa_spec_t *capa, capa_vsel_t cvsel, size_t block_size,
86 uint64_t *rblocks)
87{
88 int exp;
89 uint64_t bytes;
90 uint64_t f;
91 uint64_t adj;
92 uint64_t blocks;
93 uint64_t rem;
94 errno_t rc;
95
96 exp = capa->cunit * 3 - capa->dp;
97 if (exp < 0) {
98 rc = ipow10_u64(-exp, &f);
99 if (rc != EOK)
100 return ERANGE;
101 bytes = (capa->m + (f / 2)) / f;
102 if (bytes * f - (f / 2) != capa->m)
103 return ERANGE;
104 } else {
105 rc = ipow10_u64(exp, &f);
106 if (rc != EOK)
107 return ERANGE;
108
109 adj = 0;
110 switch (cvsel) {
111 case cv_nom:
112 adj = 0;
113 break;
114 case cv_min:
115 adj = -(f / 2);
116 break;
117 case cv_max:
118 adj = f / 2 - 1;
119 break;
120 }
121
122 bytes = capa->m * f + adj;
123 if ((bytes - adj) / f != capa->m)
124 return ERANGE;
125 }
126
127 rem = bytes % block_size;
128 if ((bytes + rem) < bytes)
129 return ERANGE;
130
131 blocks = (bytes + rem) / block_size;
132
133 *rblocks = blocks;
134 return EOK;
135}
136
137/** Simplify and round capacity to a human-friendly form.
138 *
139 * Change unit and round the number so that we have at most three integer
140 * digits and at most two fractional digits, e.g abc.xy <unit>.
141 */
142void capa_simplify(capa_spec_t *capa)
143{
144 uint64_t div;
145 uint64_t maxv;
146 unsigned sdig;
147 unsigned rdig;
148 errno_t rc;
149
150 /* Change units so that we have at most @c scapa_max_idig integer digits */
151 rc = ipow10_u64(scapa_max_idig, &maxv);
152 assert(rc == EOK);
153
154 rc = ipow10_u64(capa->dp, &div);
155 assert(rc == EOK);
156
157 /* Change units until we have no more than scapa_max_idig integer digits */
158 while (capa->m / div >= maxv) {
159 ++capa->cunit;
160 capa->dp += 3;
161 div = div * 1000;
162 }
163
164 /* Round the number so that we have at most @c scapa_max_sdig significant digits */
165 sdig = 1 + ilog10_u64(capa->m); /* number of significant digits */
166 if (sdig > scapa_max_sdig) {
167 /* Number of digits to remove */
168 rdig = sdig - scapa_max_sdig;
169 if (rdig > capa->dp)
170 rdig = capa->dp;
171
172 rc = ipow10_u64(rdig, &div);
173 assert(rc == EOK);
174
175 /* Division with rounding */
176 capa->m = (capa->m + (div / 2)) / div;
177 capa->dp -= rdig;
178 }
179
180 /*
181 * If we rounded up from something like 999.95 to 1000.0,, we still
182 * have more than scapa_max_idig integer digits and need to change
183 * units once more.
184 */
185 rc = ipow10_u64(capa->dp, &div);
186 assert(rc == EOK);
187
188 if (capa->m / div >= 1000) {
189 ++capa->cunit;
190 capa->dp += 3;
191
192 /*
193 * We now have one more significant digit than we want
194 * so round to one less digits
195 */
196 capa->m = (capa->m + 5) / 10;
197 --capa->dp;
198 }
199}
200
201errno_t capa_format(capa_spec_t *capa, char **rstr)
202{
203 errno_t rc;
204 int ret;
205 const char *sunit;
206 uint64_t ipart;
207 uint64_t fpart;
208 uint64_t div;
209
210 sunit = NULL;
211
212 assert(capa->cunit < CU_LIMIT);
213
214 rc = ipow10_u64(capa->dp, &div);
215 if (rc != EOK)
216 return rc;
217
218 ipart = capa->m / div;
219 fpart = capa->m % div;
220
221 sunit = cu_str[capa->cunit];
222 if (capa->dp > 0) {
223 ret = asprintf(rstr, "%" PRIu64 ".%0*" PRIu64 " %s", ipart,
224 (int)capa->dp, fpart, sunit);
225 } else {
226 ret = asprintf(rstr, "%" PRIu64 " %s", ipart, sunit);
227 }
228
229 if (ret < 0)
230 return ENOMEM;
231
232 return EOK;
233}
234
235static errno_t capa_digit_val(char c, int *val)
236{
237 switch (c) {
238 case '0':
239 *val = 0;
240 break;
241 case '1':
242 *val = 1;
243 break;
244 case '2':
245 *val = 2;
246 break;
247 case '3':
248 *val = 3;
249 break;
250 case '4':
251 *val = 4;
252 break;
253 case '5':
254 *val = 5;
255 break;
256 case '6':
257 *val = 6;
258 break;
259 case '7':
260 *val = 7;
261 break;
262 case '8':
263 *val = 8;
264 break;
265 case '9':
266 *val = 9;
267 break;
268 default:
269 return EINVAL;
270 }
271
272 return EOK;
273}
274
275errno_t capa_parse(const char *str, capa_spec_t *capa)
276{
277 const char *eptr;
278 const char *p;
279 int d;
280 int dp;
281 unsigned long m;
282 int i;
283
284 m = 0;
285
286 eptr = str;
287 while (capa_digit_val(*eptr, &d) == EOK) {
288 m = m * 10 + d;
289 ++eptr;
290 }
291
292 if (*eptr == '.') {
293 ++eptr;
294 dp = 0;
295 while (capa_digit_val(*eptr, &d) == EOK) {
296 m = m * 10 + d;
297 ++dp;
298 ++eptr;
299 }
300 } else {
301 dp = 0;
302 }
303
304 while (*eptr == ' ')
305 ++eptr;
306
307 if (*eptr == '\0') {
308 capa->cunit = cu_byte;
309 } else {
310 for (i = 0; i < CU_LIMIT; i++) {
311 if (str_lcasecmp(eptr, cu_str[i],
312 str_length(cu_str[i])) == 0) {
313 p = eptr + str_size(cu_str[i]);
314 while (*p == ' ')
315 ++p;
316 if (*p == '\0')
317 goto found;
318 }
319 }
320
321 return EINVAL;
322 found:
323 capa->cunit = i;
324 }
325
326 capa->m = m;
327 capa->dp = dp;
328 return EOK;
329}
330
331/** @}
332 */
Note: See TracBrowser for help on using the repository browser.