source: mainline/uspace/lib/c/generic/cap.c@ 08e103d4

Last change on this file since 08e103d4 was 08e103d4, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 7 years ago

Use clearer naming for string length functions

This and the following commit change the names of functions, as well as
their documentation, to use unambiguous terms "bytes" and "code points"
instead of ambiguous terms "size", "length", and "characters".

  • Property mode set to 100644
File size: 6.2 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 sdig = 1 + ilog10_u64(cap->m); /* number of significant digits */
163 if (sdig > scap_max_sdig) {
164 /* Number of digits to remove */
165 rdig = sdig - scap_max_sdig;
166 if (rdig > cap->dp)
167 rdig = cap->dp;
168
169 rc = ipow10_u64(rdig, &div);
170 assert(rc == EOK);
171
172 cap->m = (cap->m + (div / 2)) / div;
173 cap->dp -= rdig;
174 }
175}
176
177errno_t cap_format(cap_spec_t *cap, char **rstr)
178{
179 errno_t rc;
180 int ret;
181 const char *sunit;
182 uint64_t ipart;
183 uint64_t fpart;
184 uint64_t div;
185
186 sunit = NULL;
187
188 assert(cap->cunit < CU_LIMIT);
189
190 rc = ipow10_u64(cap->dp, &div);
191 if (rc != EOK)
192 return rc;
193
194 ipart = cap->m / div;
195 fpart = cap->m % div;
196
197 sunit = cu_str[cap->cunit];
198 if (cap->dp > 0) {
199 ret = asprintf(rstr, "%" PRIu64 ".%0*" PRIu64 " %s", ipart,
200 (int)cap->dp, fpart, sunit);
201 } else {
202 ret = asprintf(rstr, "%" PRIu64 " %s", ipart, sunit);
203 }
204 if (ret < 0)
205 return ENOMEM;
206
207 return EOK;
208}
209
210static errno_t cap_digit_val(char c, int *val)
211{
212 switch (c) {
213 case '0':
214 *val = 0;
215 break;
216 case '1':
217 *val = 1;
218 break;
219 case '2':
220 *val = 2;
221 break;
222 case '3':
223 *val = 3;
224 break;
225 case '4':
226 *val = 4;
227 break;
228 case '5':
229 *val = 5;
230 break;
231 case '6':
232 *val = 6;
233 break;
234 case '7':
235 *val = 7;
236 break;
237 case '8':
238 *val = 8;
239 break;
240 case '9':
241 *val = 9;
242 break;
243 default:
244 return EINVAL;
245 }
246
247 return EOK;
248}
249
250errno_t cap_parse(const char *str, cap_spec_t *cap)
251{
252 const char *eptr;
253 const char *p;
254 int d;
255 int dp;
256 unsigned long m;
257 int i;
258
259 m = 0;
260
261 eptr = str;
262 while (cap_digit_val(*eptr, &d) == EOK) {
263 m = m * 10 + d;
264 ++eptr;
265 }
266
267 if (*eptr == '.') {
268 ++eptr;
269 dp = 0;
270 while (cap_digit_val(*eptr, &d) == EOK) {
271 m = m * 10 + d;
272 ++dp;
273 ++eptr;
274 }
275 } else {
276 dp = 0;
277 }
278
279 while (*eptr == ' ')
280 ++eptr;
281
282 if (*eptr == '\0') {
283 cap->cunit = cu_byte;
284 } else {
285 for (i = 0; i < CU_LIMIT; i++) {
286 if (str_lcasecmp(eptr, cu_str[i],
287 str_code_points(cu_str[i])) == 0) {
288 p = eptr + str_bytes(cu_str[i]);
289 while (*p == ' ')
290 ++p;
291 if (*p == '\0')
292 goto found;
293 }
294 }
295
296 return EINVAL;
297 found:
298 cap->cunit = i;
299 }
300
301 cap->m = m;
302 cap->dp = dp;
303 return EOK;
304}
305
306/** @}
307 */
Note: See TracBrowser for help on using the repository browser.