1 /*
2  * unit.c		Unit Definitions
3  *
4  * Copyright (c) 2001-2013 Thomas Graf <tgraf@suug.ch>
5  * Copyright (c) 2013 Red Hat, Inc.
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining a
8  * copy of this software and associated documentation files (the "Software"),
9  * to deal in the Software without restriction, including without limitation
10  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
11  * and/or sell copies of the Software, and to permit persons to whom the
12  * Software is furnished to do so, subject to the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be included
15  * in all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23  * DEALINGS IN THE SOFTWARE.
24  */
25 
26 #include <bmon/bmon.h>
27 #include <bmon/conf.h>
28 #include <bmon/utils.h>
29 #include <bmon/unit.h>
30 
31 static struct unit *byte_unit, *bit_unit;
32 
33 static LIST_HEAD(units);
34 
get_flist(struct unit * unit)35 static struct list_head *get_flist(struct unit *unit)
36 {
37 	int div = UNIT_DEFAULT;
38 
39 	if (cfg_getbool(cfg, "use_bit"))
40 		div = UNIT_BIT;
41 	else if (cfg_getbool(cfg, "use_si"))
42 		div = UNIT_SI;
43 
44 	if (!list_empty(&unit->u_div[div]))
45 		return &unit->u_div[div];
46 	else
47 		return &unit->u_div[UNIT_DEFAULT];
48 }
49 
unit_lookup(const char * name)50 struct unit *unit_lookup(const char *name)
51 {
52 	struct unit *unit;
53 
54 	list_for_each_entry(unit, &units, u_list)
55 		if (!strcmp(name, unit->u_name))
56 			return unit;
57 
58 	return NULL;
59 }
60 
61 /**
62  * Lookup best divisor to use for a certain situation
63  * @hint		Value in question (for DYNAMIC_EXP)
64  * @unit		Unit of value
65  * @name		Place to store name of divisor used
66  * @prec		Place to store suggested precision
67  *
68  * Searches for the best divisor to be used depending on the unit
69  * exponent configured by the user. If a dynamic exponent is
70  * configured, the divisor is selected based on the value of hint
71  * so that hint is dividied into a small float >= 1.0. The name
72  * of the divisor used is stored in *name.
73  *
74  * If prec points to a vaild integer, a number of precision digits
75  * is suggested to avoid n.00 to make pretty printing easier.
76  */
unit_divisor(uint64_t hint,struct unit * unit,char ** name,int * prec)77 double unit_divisor(uint64_t hint, struct unit *unit, char **name,
78 		    int *prec)
79 {
80 	struct list_head *flist = get_flist(unit);
81 	struct fraction *f;
82 
83 	if (prec)
84 		*prec = 2;
85 
86 	if (cfg_unit_exp == DYNAMIC_EXP) {
87 		list_for_each_entry_reverse(f, flist, f_list) {
88 			if (hint >= f->f_divisor)
89 				goto found_it;
90 		}
91 	} else {
92 		int n = cfg_unit_exp;
93 		list_for_each_entry(f, flist, f_list) {
94 			if (--n == 0)
95 				goto found_it;
96 		}
97 	}
98 
99 	*name = "";
100 	return 1;
101 
102 found_it:
103 	if (f->f_divisor == 1.0f && prec)
104 		*prec = 0;
105 
106 	*name = f->f_name;
107 	return f->f_divisor;
108 }
109 
unit_value2str(uint64_t value,struct unit * unit,char ** name,int * prec)110 double unit_value2str(uint64_t value, struct unit *unit,
111 		      char **name, int *prec)
112 {
113 	double div = unit_divisor(value, unit, name, prec);
114 	double v = (double) value;
115 
116 	if (fmod(v, div) == 0.0f && prec)
117 		*prec = 0;
118 
119 	return v / div;
120 }
121 
fraction_free(struct fraction * f)122 void fraction_free(struct fraction *f)
123 {
124 	if (!f)
125 		return;
126 
127 	list_del(&f->f_list);
128 	xfree(f->f_name);
129 	xfree(f);
130 }
131 
unit_add_div(struct unit * unit,int type,const char * txt,float div)132 void unit_add_div(struct unit *unit, int type, const char *txt, float div)
133 {
134 	struct fraction *f;
135 
136 	if (!unit)
137 		BUG();
138 
139 	f = xcalloc(1, sizeof(*f));
140 
141 	init_list_head(&f->f_list);
142 
143 	f->f_divisor = div;
144 	f->f_name = strdup(txt);
145 
146 	list_add_tail(&f->f_list, &unit->u_div[type]);
147 }
148 
unit_add(const char * name)149 struct unit *unit_add(const char *name)
150 {
151 	struct unit *unit;
152 	int i;
153 
154 	if (!(unit = unit_lookup(name))) {
155 		unit = xcalloc(1, sizeof(*unit));
156 		unit->u_name = strdup(name);
157 
158 		for (i = 0; i < __UNIT_MAX; i++)
159 			init_list_head(&unit->u_div[i]);
160 
161 		list_add_tail(&unit->u_list, &units);
162 	}
163 
164 	return unit;
165 }
166 
unit_free(struct unit * u)167 static void unit_free(struct unit *u)
168 {
169 	struct fraction *f, *n;
170 
171 	if (!u)
172 		return;
173 
174 	list_for_each_entry_safe(f, n, &u->u_div[UNIT_DEFAULT], f_list)
175 		fraction_free(f);
176 
177 	list_for_each_entry_safe(f, n, &u->u_div[UNIT_SI], f_list)
178 		fraction_free(f);
179 
180 	xfree(u->u_name);
181 	xfree(u);
182 }
183 
unit_bytes2str(uint64_t bytes,char * buf,size_t len)184 char *unit_bytes2str(uint64_t bytes, char *buf, size_t len)
185 {
186 	char *ustr;
187 	int prec;
188 	double v;
189 
190 	if (byte_unit) {
191 		v = unit_value2str(bytes, byte_unit, &ustr, &prec);
192 		snprintf(buf, len, "%'.*f%3s", prec, v, ustr);
193 	} else
194 		snprintf(buf, len, "%" PRIu64, bytes);
195 
196 	return buf;
197 }
198 
unit_bit2str(uint64_t bits,char * buf,size_t len)199 char *unit_bit2str(uint64_t bits, char *buf, size_t len)
200 {
201 	char *ustr;
202 	int prec;
203 	double v;
204 
205 	if (bit_unit) {
206 		v = unit_value2str(bits, bit_unit, &ustr, &prec);
207 		snprintf(buf, len, "%'.*f%3s", prec, v, ustr);
208 	} else
209 		snprintf(buf, len, "%" PRIu64, bits);
210 
211 	return buf;
212 }
213 
unit_exit(void)214 static void __exit unit_exit(void)
215 {
216 	struct unit *u, *n;
217 
218 	list_for_each_entry_safe(u, n, &units, u_list)
219 		unit_free(u);
220 }
221