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
RevLine 
[9854a8f]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
[6c4eedf]29/** @addtogroup libc
[9854a8f]30 * @{
31 */
32/**
[6c4eedf]33 * @file Storage capacity specification.
[9854a8f]34 */
35
[5fc8244]36#include <assert.h>
[c24b0dcb]37#include <capa.h>
[9854a8f]38#include <errno.h>
39#include <imath.h>
[5fc8244]40#include <stddef.h>
[9854a8f]41#include <stdio.h>
42#include <str.h>
43
44/** Simplified capacity parameters */
45enum {
46 /** Simplified capacity maximum integer digits */
[c24b0dcb]47 scapa_max_idig = 3,
[9854a8f]48 /** Simplified capacity maximum significant digits */
[c24b0dcb]49 scapa_max_sdig = 4
[9854a8f]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
[c24b0dcb]64void capa_from_blocks(uint64_t nblocks, size_t block_size, capa_spec_t *capa)
[9854a8f]65{
66 uint64_t tsize;
67
68 tsize = nblocks * block_size;
[c24b0dcb]69 capa->m = tsize;
70 capa->dp = 0;
71 capa->cunit = cu_byte;
[9854a8f]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.
[03661d19]79 *
80 * A capacity value entails precision, i.e. it corresponds to a range
[6c4eedf]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.
[9854a8f]84 */
[c24b0dcb]85errno_t capa_to_blocks(capa_spec_t *capa, capa_vsel_t cvsel, size_t block_size,
[6c4eedf]86 uint64_t *rblocks)
[9854a8f]87{
88 int exp;
89 uint64_t bytes;
90 uint64_t f;
[03661d19]91 uint64_t adj;
92 uint64_t blocks;
93 uint64_t rem;
[b7fd2a0]94 errno_t rc;
[9854a8f]95
[c24b0dcb]96 exp = capa->cunit * 3 - capa->dp;
[9854a8f]97 if (exp < 0) {
98 rc = ipow10_u64(-exp, &f);
99 if (rc != EOK)
100 return ERANGE;
[c24b0dcb]101 bytes = (capa->m + (f / 2)) / f;
102 if (bytes * f - (f / 2) != capa->m)
[03661d19]103 return ERANGE;
[9854a8f]104 } else {
105 rc = ipow10_u64(exp, &f);
106 if (rc != EOK)
107 return ERANGE;
[03661d19]108
109 adj = 0;
110 switch (cvsel) {
[6c4eedf]111 case cv_nom:
[03661d19]112 adj = 0;
113 break;
[6c4eedf]114 case cv_min:
[03661d19]115 adj = -(f / 2);
116 break;
[6c4eedf]117 case cv_max:
[03661d19]118 adj = f / 2 - 1;
119 break;
120 }
121
[c24b0dcb]122 bytes = capa->m * f + adj;
123 if ((bytes - adj) / f != capa->m)
[03661d19]124 return ERANGE;
[9854a8f]125 }
126
[03661d19]127 rem = bytes % block_size;
128 if ((bytes + rem) < bytes)
129 return ERANGE;
130
131 blocks = (bytes + rem) / block_size;
132
133 *rblocks = blocks;
[9854a8f]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 */
[c24b0dcb]142void capa_simplify(capa_spec_t *capa)
[9854a8f]143{
144 uint64_t div;
145 uint64_t maxv;
146 unsigned sdig;
147 unsigned rdig;
[b7fd2a0]148 errno_t rc;
[9854a8f]149
[c24b0dcb]150 /* Change units so that we have at most @c scapa_max_idig integer digits */
151 rc = ipow10_u64(scapa_max_idig, &maxv);
[9854a8f]152 assert(rc == EOK);
153
[c24b0dcb]154 rc = ipow10_u64(capa->dp, &div);
[9854a8f]155 assert(rc == EOK);
156
[de65624]157 /* Change units until we have no more than scapa_max_idig integer digits */
[c24b0dcb]158 while (capa->m / div >= maxv) {
159 ++capa->cunit;
160 capa->dp += 3;
[9854a8f]161 div = div * 1000;
162 }
163
[c24b0dcb]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) {
[9854a8f]167 /* Number of digits to remove */
[c24b0dcb]168 rdig = sdig - scapa_max_sdig;
169 if (rdig > capa->dp)
170 rdig = capa->dp;
[9854a8f]171
172 rc = ipow10_u64(rdig, &div);
173 assert(rc == EOK);
174
[de65624]175 /* Division with rounding */
[c24b0dcb]176 capa->m = (capa->m + (div / 2)) / div;
177 capa->dp -= rdig;
[9854a8f]178 }
[de65624]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 }
[9854a8f]199}
200
[c24b0dcb]201errno_t capa_format(capa_spec_t *capa, char **rstr)
[9854a8f]202{
[b7fd2a0]203 errno_t rc;
[d5c1051]204 int ret;
[9854a8f]205 const char *sunit;
206 uint64_t ipart;
207 uint64_t fpart;
208 uint64_t div;
209
210 sunit = NULL;
211
[c24b0dcb]212 assert(capa->cunit < CU_LIMIT);
[9854a8f]213
[c24b0dcb]214 rc = ipow10_u64(capa->dp, &div);
[9854a8f]215 if (rc != EOK)
216 return rc;
217
[c24b0dcb]218 ipart = capa->m / div;
219 fpart = capa->m % div;
[9854a8f]220
[c24b0dcb]221 sunit = cu_str[capa->cunit];
222 if (capa->dp > 0) {
[d5c1051]223 ret = asprintf(rstr, "%" PRIu64 ".%0*" PRIu64 " %s", ipart,
[c24b0dcb]224 (int)capa->dp, fpart, sunit);
[9854a8f]225 } else {
[d5c1051]226 ret = asprintf(rstr, "%" PRIu64 " %s", ipart, sunit);
[9854a8f]227 }
[de65624]228
[d5c1051]229 if (ret < 0)
[9854a8f]230 return ENOMEM;
231
232 return EOK;
233}
234
[c24b0dcb]235static errno_t capa_digit_val(char c, int *val)
[03661d19]236{
237 switch (c) {
[1433ecda]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;
[03661d19]268 default:
269 return EINVAL;
270 }
271
272 return EOK;
273}
274
[c24b0dcb]275errno_t capa_parse(const char *str, capa_spec_t *capa)
[9854a8f]276{
[03661d19]277 const char *eptr;
278 const char *p;
279 int d;
280 int dp;
281 unsigned long m;
[9854a8f]282 int i;
283
[03661d19]284 m = 0;
285
286 eptr = str;
[c24b0dcb]287 while (capa_digit_val(*eptr, &d) == EOK) {
[03661d19]288 m = m * 10 + d;
289 ++eptr;
290 }
291
292 if (*eptr == '.') {
293 ++eptr;
294 dp = 0;
[c24b0dcb]295 while (capa_digit_val(*eptr, &d) == EOK) {
[03661d19]296 m = m * 10 + d;
297 ++dp;
298 ++eptr;
299 }
300 } else {
[2dab624]301 dp = 0;
[03661d19]302 }
[9854a8f]303
304 while (*eptr == ' ')
305 ++eptr;
306
307 if (*eptr == '\0') {
[c24b0dcb]308 capa->cunit = cu_byte;
[9854a8f]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;
[1433ecda]322 found:
[c24b0dcb]323 capa->cunit = i;
[9854a8f]324 }
325
[c24b0dcb]326 capa->m = m;
327 capa->dp = dp;
[9854a8f]328 return EOK;
329}
330
331/** @}
332 */
Note: See TracBrowser for help on using the repository browser.