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