xref: /dragonfly/lib/libtcplay/humanize.c (revision 91f1d6e1)
1 /*
2  * Copyright (c) 2011 Alex Hornung <alex@alexhornung.com>.
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  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in
13  *    the documentation and/or other materials provided with the
14  *    distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
19  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
20  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
22  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
26  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29 
30 #include <string.h>
31 #include <stdlib.h>
32 #include <stdio.h>
33 #include <inttypes.h>
34 #include <ctype.h>
35 #include <errno.h>
36 
37 #include "humanize.h"
38 
39 static const char prefixes[] = " KMGTPE";
40 int
41 _humanize_number(char *buf, size_t bufsz, uint64_t num)
42 {
43 	const char *prefixp;
44 	int ret;
45 	uint64_t i, d;
46 
47 	prefixp = prefixes;
48 	i = num;
49 	d = 0;
50 
51 	while ((i > 1024) && (*prefixp != '\0')) {
52 		d = (i % 1024)/10;
53 		i /= 1024;
54 		++prefixp;
55 	}
56 
57 	if (d > 0)
58 		ret = snprintf(buf, bufsz, "%"PRIu64".%"PRIu64" %c",
59 		    i, d, *prefixp);
60 	else
61 		ret = snprintf(buf, bufsz, "%"PRIu64" %c", i, *prefixp);
62 
63 
64 	if ((ret < 0) || ((size_t)ret >= bufsz)) {
65 		errno = ENOMEM;
66 		return -1;
67 	} else {
68 		return 0;
69 	}
70 }
71 
72 int
73 _dehumanize_number(const char *buf, uint64_t *dest)
74 {
75 	char *endptr;
76 	uint64_t n, n_check, d;
77 	uint64_t multiplier;
78 	size_t len;
79 
80 	if (*buf == '\0') {
81 		errno = EINVAL;
82 		return -1;
83 	}
84 
85 	len = strlen(buf);
86 	if (tolower(buf[len-1]) == 'b')
87 		--len;
88 
89 	multiplier = 1;
90 
91 	switch (tolower(buf[len-1])) {
92 	case 'y':
93 		multiplier *= 1024;
94 		/* FALLTHROUGH */
95 	case 'z':
96 		multiplier *= 1024;
97 		/* FALLTHROUGH */
98 	case 'e':
99 		multiplier *= 1024;
100 		/* FALLTHROUGH */
101 	case 'p':
102 		multiplier *= 1024;
103 		/* FALLTHROUGH */
104 	case 't':
105 		multiplier *= 1024;
106 		/* FALLTHROUGH */
107 	case 'g':
108 		multiplier *= 1024;
109 		/* FALLTHROUGH */
110 	case 'm':
111 		multiplier *= 1024;
112 		/* FALLTHROUGH */
113 	case 'k':
114 		multiplier *= 1024;
115 		break;
116 	default:
117 		/*
118 		 * only set error if string ends in a character that
119 		 * is not a valid unit.
120 		 */
121 		if (isalpha(buf[len-1])) {
122 			errno = EINVAL;
123 			return -1;
124 		}
125 	}
126 
127 	d = 0;
128 	n = n_check = strtoull(buf, &endptr, 10);
129 	if (endptr) {
130 		if ((*endptr != '.') && (*endptr != '\0') &&
131 		    (*endptr != ' ') && (endptr != &buf[len-1])) {
132 			errno = EINVAL;
133 			return -1;
134 		}
135 
136 		if (*endptr == '.') {
137 			d = strtoull(endptr+1, &endptr, 10);
138 			if (endptr && (*endptr != '\0') &&
139 			    (*endptr != ' ') &&
140 			    (endptr != &buf[len-1])) {
141 				errno = EINVAL;
142 				return -1;
143 			}
144 		}
145 	}
146 
147 	if (d != 0) {
148 		while (d < 100)
149 			d *= 10;
150 
151 		while (d > 1000)
152 			d /= 10;
153 	}
154 
155 	d *= (multiplier/1024);
156 	n *= multiplier;
157 
158 	if ((uint64_t)(n/multiplier) != n_check) {
159 		errno = ERANGE;
160 		return -1;
161 	}
162 
163 	n += d;
164 	*dest = n;
165 
166 	return 0;
167 }
168 
169 #ifdef __TEST_HUMANIZE__
170 
171 #include <assert.h>
172 int main(int argc, char *argv[])
173 {
174 	char buf[1024];
175 	uint64_t n, out;
176 	int err;
177 
178 	if (argc < 3)
179 		return -1;
180 
181 	n = strtoull(argv[1], NULL, 10);
182 
183 	err = _humanize_number(buf, 1024, n);
184 	assert(err == 0);
185 
186 	err = _dehumanize_number(buf, &out);
187 	assert(err == 0);
188 
189 	printf("Converting: %"PRIu64" => %s => %"PRIu64"\n", n, buf, out);
190 
191 	err = _dehumanize_number(argv[2], &out);
192 	assert (err == 0);
193 
194 	printf("Converting: %s => %"PRIu64"\n", argv[2], out);
195 
196 	return 0;
197 }
198 
199 #endif
200