source: mainline/uspace/lib/fdisk/src/cap.c@ 03661d19

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 03661d19 was 03661d19, checked in by Jiri Svoboda <jiri@…>, 10 years ago

Handle simplified capacity entry.

  • Property mode set to 100644
File size: 6.6 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
29/** @addtogroup libfdisk
30 * @{
31 */
32/**
33 * @file Disk management library.
34 */
35
36#include <errno.h>
37#include <fdisk.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 fdisk_cap_from_blocks(uint64_t nblocks, size_t block_size,
63 fdisk_cap_t *cap)
64{
65 uint64_t tsize;
66
67 tsize = nblocks * block_size;
68 cap->m = tsize;
69 cap->dp = 0;
70 cap->cunit = cu_byte;
71}
72
73/** Convert capacity to blocks.
74 *
75 * If the value of bytes is not integer, it is properly rounded. If the number
76 * of bytes is not divisible by the number of blocks, it is rounded
77 * up to an integer number of blocks.
[03661d19]78 *
79 * A capacity value entails precision, i.e. it corresponds to a range
80 * of values. @a cvsel selects the value to return. @c fcv_nom gives
81 * the nominal (middle) value, @c fcv_min gives the minimum value
82 * and @c fcv_max gives the maximum value.
[9854a8f]83 */
[03661d19]84int fdisk_cap_to_blocks(fdisk_cap_t *cap, fdisk_cvsel_t cvsel,
85 size_t block_size, uint64_t *rblocks)
[9854a8f]86{
87 int exp;
88 uint64_t bytes;
89 uint64_t f;
[03661d19]90 uint64_t adj;
91 uint64_t blocks;
92 uint64_t rem;
[9854a8f]93 int rc;
94
[03661d19]95 printf("fdisk_cap_to_blocks: m=%" PRIu64 ", dp=%d, cunit=%d\n",
96 cap->m, cap->dp, cap->cunit);
[9854a8f]97
98 exp = cap->cunit * 3 - cap->dp;
99 if (exp < 0) {
100 rc = ipow10_u64(-exp, &f);
101 if (rc != EOK)
102 return ERANGE;
103 bytes = (cap->m + (f / 2)) / f;
[03661d19]104 if (bytes * f - (f / 2) != cap->m)
105 return ERANGE;
[9854a8f]106 } else {
107 rc = ipow10_u64(exp, &f);
108 if (rc != EOK)
109 return ERANGE;
[03661d19]110
111 adj = 0;
112 switch (cvsel) {
113 case fcv_nom:
114 adj = 0;
115 break;
116 case fcv_min:
117 adj = -(f / 2);
118 break;
119 case fcv_max:
120 adj = f / 2 - 1;
121 break;
122 }
123
124 printf("f=%" PRIu64 ", adj=%" PRId64 "\n", f, adj);
125 bytes = cap->m * f + adj;
126 printf("bytes=%" PRIu64 "\n", bytes);
127 if ((bytes - adj) / f != cap->m)
128 return ERANGE;
[9854a8f]129 }
130
[03661d19]131 rem = bytes % block_size;
132 if ((bytes + rem) < bytes)
133 return ERANGE;
134
135 blocks = (bytes + rem) / block_size;
136 printf("blocks=%" PRIu64 "\n", blocks);
137
138 *rblocks = blocks;
[9854a8f]139 return EOK;
140}
141
142/** Simplify and round capacity to a human-friendly form.
143 *
144 * Change unit and round the number so that we have at most three integer
145 * digits and at most two fractional digits, e.g abc.xy <unit>.
146 */
147void fdisk_cap_simplify(fdisk_cap_t *cap)
148{
149 uint64_t div;
150 uint64_t maxv;
151 unsigned sdig;
152 unsigned rdig;
153 int rc;
154
155 printf("before: m=%" PRIu64 " dp=%u cunit=%d\n",
156 cap->m, cap->dp, cap->cunit);
157
158 /* Change units so that we have at most @c scap_max_idig integer digits */
159 rc = ipow10_u64(scap_max_idig, &maxv);
160 assert(rc == EOK);
161
162 rc = ipow10_u64(cap->dp, &div);
163 assert(rc == EOK);
164
165 while (cap->m / div >= maxv) {
166 ++cap->cunit;
167 cap->dp += 3;
168 div = div * 1000;
169 }
170
171 /* Round the number so that we have at most @c scap_max_sdig significant digits */
172 sdig = 1 + ilog10_u64(cap->m); /* number of significant digits */
173 if (sdig > scap_max_sdig) {
174 /* Number of digits to remove */
175 rdig = sdig - scap_max_sdig;
176 if (rdig > cap->dp)
177 rdig = cap->dp;
178
179 rc = ipow10_u64(rdig, &div);
180 assert(rc == EOK);
181
182 cap->m = (cap->m + (div / 2)) / div;
183 cap->dp -= rdig;
184 }
185
186 printf("after: m=%" PRIu64 " dp=%u cunit=%d\n",
187 cap->m, cap->dp, cap->cunit);
188}
189
190int fdisk_cap_format(fdisk_cap_t *cap, char **rstr)
191{
192 int rc;
193 const char *sunit;
194 uint64_t ipart;
195 uint64_t fpart;
196 uint64_t div;
197
198 sunit = NULL;
199
200 if (cap->cunit < 0 || cap->cunit >= CU_LIMIT)
201 assert(false);
202
203 rc = ipow10_u64(cap->dp, &div);
204 if (rc != EOK)
205 return rc;
206
207 ipart = cap->m / div;
208 fpart = cap->m % div;
209
210 sunit = cu_str[cap->cunit];
211 if (cap->dp > 0) {
212 rc = asprintf(rstr, "%" PRIu64 ".%0*" PRIu64 " %s", ipart,
213 (int)cap->dp, fpart, sunit);
214 } else {
215 rc = asprintf(rstr, "%" PRIu64 " %s", ipart, sunit);
216 }
217 if (rc < 0)
218 return ENOMEM;
219
220 return EOK;
221}
222
[03661d19]223static int fdisk_digit_val(char c, int *val)
224{
225 switch (c) {
226 case '0': *val = 0; break;
227 case '1': *val = 1; break;
228 case '2': *val = 2; break;
229 case '3': *val = 3; break;
230 case '4': *val = 4; break;
231 case '5': *val = 5; break;
232 case '6': *val = 6; break;
233 case '7': *val = 7; break;
234 case '8': *val = 8; break;
235 case '9': *val = 9; break;
236 default:
237 return EINVAL;
238 }
239
240 return EOK;
241}
242
[9854a8f]243int fdisk_cap_parse(const char *str, fdisk_cap_t *cap)
244{
[03661d19]245 const char *eptr;
246 const char *p;
247 int d;
248 int dp;
249 unsigned long m;
[9854a8f]250 int i;
251
[03661d19]252 m = 0;
253
254 eptr = str;
255 while (fdisk_digit_val(*eptr, &d) == EOK) {
256 m = m * 10 + d;
257 ++eptr;
258 }
259
260 if (*eptr == '.') {
261 ++eptr;
262 dp = 0;
263 while (fdisk_digit_val(*eptr, &d) == EOK) {
264 m = m * 10 + d;
265 ++dp;
266 ++eptr;
267 }
268 } else {
269 m = 0; dp = 0;
270 }
[9854a8f]271
272 while (*eptr == ' ')
273 ++eptr;
274
275 if (*eptr == '\0') {
276 cap->cunit = cu_byte;
277 } else {
278 for (i = 0; i < CU_LIMIT; i++) {
279 if (str_lcasecmp(eptr, cu_str[i],
280 str_length(cu_str[i])) == 0) {
281 p = eptr + str_size(cu_str[i]);
282 while (*p == ' ')
283 ++p;
284 if (*p == '\0')
285 goto found;
286 }
287 }
288
289 return EINVAL;
290found:
291 cap->cunit = i;
292 }
293
[03661d19]294 cap->m = m;
295 cap->dp = dp;
[9854a8f]296 return EOK;
297}
298
299/** @}
300 */
Note: See TracBrowser for help on using the repository browser.