/src/net-snmp/snmplib/int64.c
Line | Count | Source (jump to first uncovered line) |
1 | | /** |
2 | | * @file int64.c |
3 | | * |
4 | | * @brief Functions for 64-bit integer computations. |
5 | | * |
6 | | * 21-jan-1998: David Perkins <dperkins@dsperkins.com> |
7 | | */ |
8 | | |
9 | | #include <net-snmp/net-snmp-config.h> |
10 | | #include <sys/types.h> |
11 | | #include <stdio.h> |
12 | | #include <stdlib.h> |
13 | | #include <ctype.h> |
14 | | #ifdef HAVE_INTTYPES_H |
15 | | #include <inttypes.h> |
16 | | #endif |
17 | | #ifdef HAVE_STRING_H |
18 | | #include <string.h> |
19 | | #else |
20 | | #include <strings.h> |
21 | | #endif |
22 | | |
23 | | #include <net-snmp/types.h> |
24 | | #include <net-snmp/library/int64.h> |
25 | | #include <net-snmp/library/snmp_assert.h> |
26 | | #include <net-snmp/library/snmp_debug.h> |
27 | | #include <net-snmp/library/snmp_logging.h> |
28 | | |
29 | | #include <net-snmp/net-snmp-features.h> |
30 | | |
31 | | /** |
32 | | * Divide an unsigned 64-bit integer by 10. |
33 | | * |
34 | | * @param[in] u64 Number to be divided. |
35 | | * @param[out] pu64Q Quotient. |
36 | | * @param[out] puR Remainder. |
37 | | */ |
38 | | void |
39 | | divBy10(struct counter64 u64, struct counter64 *pu64Q, unsigned int *puR) |
40 | 3.00k | { |
41 | 3.00k | unsigned long ulT; |
42 | 3.00k | unsigned long ulQ; |
43 | 3.00k | unsigned long ulR; |
44 | | |
45 | | /* |
46 | | * top 16 bits |
47 | | */ |
48 | 3.00k | ulT = (u64.high >> 16) & 0x0ffff; |
49 | 3.00k | ulQ = ulT / 10; |
50 | 3.00k | ulR = ulT % 10; |
51 | 3.00k | pu64Q->high = ulQ << 16; |
52 | | |
53 | | /* |
54 | | * next 16 |
55 | | */ |
56 | 3.00k | ulT = (u64.high & 0x0ffff); |
57 | 3.00k | ulT += (ulR << 16); |
58 | 3.00k | ulQ = ulT / 10; |
59 | 3.00k | ulR = ulT % 10; |
60 | 3.00k | pu64Q->high = pu64Q->high | ulQ; |
61 | | |
62 | | /* |
63 | | * next 16 |
64 | | */ |
65 | 3.00k | ulT = ((u64.low >> 16) & 0x0ffff) + (ulR << 16); |
66 | 3.00k | ulQ = ulT / 10; |
67 | 3.00k | ulR = ulT % 10; |
68 | 3.00k | pu64Q->low = ulQ << 16; |
69 | | |
70 | | /* |
71 | | * final 16 |
72 | | */ |
73 | 3.00k | ulT = (u64.low & 0x0ffff); |
74 | 3.00k | ulT += (ulR << 16); |
75 | 3.00k | ulQ = ulT / 10; |
76 | 3.00k | ulR = ulT % 10; |
77 | 3.00k | pu64Q->low = pu64Q->low | ulQ; |
78 | | |
79 | 3.00k | *puR = (unsigned int) (ulR); |
80 | 3.00k | } |
81 | | |
82 | | /** |
83 | | * Multiply an unsigned 64-bit integer by 10. |
84 | | * |
85 | | * @param[in] u64 Number to be multiplied. |
86 | | * @param[out] pu64P Product. |
87 | | */ |
88 | | void |
89 | | multBy10(struct counter64 u64, struct counter64 *pu64P) |
90 | 5.39k | { |
91 | 5.39k | unsigned long ulT; |
92 | 5.39k | unsigned long ulP; |
93 | 5.39k | unsigned long ulK; |
94 | | |
95 | | /* |
96 | | * lower 16 bits |
97 | | */ |
98 | 5.39k | ulT = u64.low & 0x0ffff; |
99 | 5.39k | ulP = ulT * 10; |
100 | 5.39k | ulK = ulP >> 16; |
101 | 5.39k | pu64P->low = ulP & 0x0ffff; |
102 | | |
103 | | /* |
104 | | * next 16 |
105 | | */ |
106 | 5.39k | ulT = (u64.low >> 16) & 0x0ffff; |
107 | 5.39k | ulP = (ulT * 10) + ulK; |
108 | 5.39k | ulK = ulP >> 16; |
109 | 5.39k | pu64P->low = (ulP & 0x0ffff) << 16 | pu64P->low; |
110 | | |
111 | | /* |
112 | | * next 16 bits |
113 | | */ |
114 | 5.39k | ulT = u64.high & 0x0ffff; |
115 | 5.39k | ulP = (ulT * 10) + ulK; |
116 | 5.39k | ulK = ulP >> 16; |
117 | 5.39k | pu64P->high = ulP & 0x0ffff; |
118 | | |
119 | | /* |
120 | | * final 16 |
121 | | */ |
122 | 5.39k | ulT = (u64.high >> 16) & 0x0ffff; |
123 | 5.39k | ulP = (ulT * 10) + ulK; |
124 | 5.39k | ulK = ulP >> 16; |
125 | 5.39k | pu64P->high = (ulP & 0x0ffff) << 16 | pu64P->high; |
126 | 5.39k | } |
127 | | |
128 | | /** |
129 | | * Add an unsigned 16-bit int to an unsigned 64-bit integer. |
130 | | * |
131 | | * @param[in,out] pu64 Number to be incremented. |
132 | | * @param[in] u16 Amount to add. |
133 | | * |
134 | | */ |
135 | | void |
136 | | incrByU16(struct counter64 *pu64, unsigned int u16) |
137 | 5.57k | { |
138 | 5.57k | incrByU32(pu64, u16); |
139 | 5.57k | } |
140 | | |
141 | | /** |
142 | | * Add an unsigned 32-bit int to an unsigned 64-bit integer. |
143 | | * |
144 | | * @param[in,out] pu64 Number to be incremented. |
145 | | * @param[in] u32 Amount to add. |
146 | | * |
147 | | */ |
148 | | void |
149 | | incrByU32(struct counter64 *pu64, unsigned int u32) |
150 | 5.62k | { |
151 | 5.62k | uint32_t tmp; |
152 | | |
153 | 5.62k | tmp = pu64->low; |
154 | 5.62k | pu64->low = (uint32_t)(tmp + u32); |
155 | 5.62k | if (pu64->low < tmp) |
156 | 221 | pu64->high = (uint32_t)(pu64->high + 1); |
157 | 5.62k | } |
158 | | |
159 | | /** |
160 | | * Subtract two 64-bit numbers. |
161 | | * |
162 | | * @param[in] pu64one Number to start from. |
163 | | * @param[in] pu64two Amount to subtract. |
164 | | * @param[out] pu64out pu64one - pu64two. |
165 | | */ |
166 | | void |
167 | | u64Subtract(const struct counter64 *pu64one, const struct counter64 *pu64two, |
168 | | struct counter64 *pu64out) |
169 | 0 | { |
170 | 0 | int carry; |
171 | |
|
172 | 0 | carry = pu64one->low < pu64two->low; |
173 | 0 | pu64out->low = (uint32_t)(pu64one->low - pu64two->low); |
174 | 0 | pu64out->high = (uint32_t)(pu64one->high - pu64two->high - carry); |
175 | 0 | } |
176 | | |
177 | | /** |
178 | | * Add two 64-bit numbers. |
179 | | * |
180 | | * @param[in] pu64one Amount to add. |
181 | | * @param[in,out] pu64out pu64out += pu64one. |
182 | | */ |
183 | | void |
184 | | u64Incr(struct counter64 *pu64out, const struct counter64 *pu64one) |
185 | 0 | { |
186 | 0 | pu64out->high = (uint32_t)(pu64out->high + pu64one->high); |
187 | 0 | incrByU32(pu64out, pu64one->low); |
188 | 0 | } |
189 | | |
190 | | /** |
191 | | * Add the difference of two 64-bit numbers to a 64-bit counter. |
192 | | * |
193 | | * @param[in] pu64one |
194 | | * @param[in] pu64two |
195 | | * @param[out] pu64out pu64out += (pu64one - pu64two) |
196 | | */ |
197 | | void |
198 | | u64UpdateCounter(struct counter64 *pu64out, const struct counter64 *pu64one, |
199 | | const struct counter64 *pu64two) |
200 | 0 | { |
201 | 0 | struct counter64 tmp; |
202 | |
|
203 | 0 | u64Subtract(pu64one, pu64two, &tmp); |
204 | 0 | u64Incr(pu64out, &tmp); |
205 | 0 | } |
206 | | |
207 | | netsnmp_feature_child_of(u64copy, netsnmp_unused); |
208 | | #ifndef NETSNMP_FEATURE_REMOVE_U64COPY |
209 | | /** |
210 | | * Copy a 64-bit number. |
211 | | * |
212 | | * @param[in] pu64two Number to be copied. |
213 | | * @param[out] pu64one Where to store the copy - *pu64one = *pu64two. |
214 | | */ |
215 | | void |
216 | | u64Copy(struct counter64 *pu64one, const struct counter64 *pu64two) |
217 | 0 | { |
218 | 0 | *pu64one = *pu64two; |
219 | 0 | } |
220 | | #endif /* NETSNMP_FEATURE_REMOVE_U64COPY */ |
221 | | |
222 | | /** |
223 | | * Set an unsigned 64-bit number to zero. |
224 | | * |
225 | | * @param[in] pu64 Number to be zeroed. |
226 | | */ |
227 | | void |
228 | | zeroU64(struct counter64 *pu64) |
229 | 496 | { |
230 | 496 | pu64->low = 0; |
231 | 496 | pu64->high = 0; |
232 | 496 | } |
233 | | |
234 | | /** |
235 | | * Check if an unsigned 64-bit number is zero. |
236 | | * |
237 | | * @param[in] pu64 Number to be checked. |
238 | | */ |
239 | | int |
240 | | isZeroU64(const struct counter64 *pu64) |
241 | 3.00k | { |
242 | 3.00k | return pu64->low == 0 && pu64->high == 0; |
243 | 3.00k | } |
244 | | |
245 | | /** |
246 | | * check the old and new values of a counter64 for 32bit wrapping |
247 | | * |
248 | | * @param adjust : set to 1 to auto-increment new_val->high |
249 | | * if a 32bit wrap is detected. |
250 | | * |
251 | | * @param old_val |
252 | | * @param new_val |
253 | | * |
254 | | * @note |
255 | | * The old and new values must be be from within a time period |
256 | | * which would only allow the 32bit portion of the counter to |
257 | | * wrap once. i.e. if the 32bit portion of the counter could |
258 | | * wrap every 60 seconds, the old and new values should be compared |
259 | | * at least every 59 seconds (though I'd recommend at least every |
260 | | * 50 seconds to allow for timer inaccuracies). |
261 | | * |
262 | | * @retval 64 : 64bit wrap |
263 | | * @retval 32 : 32bit wrap |
264 | | * @retval 0 : did not wrap |
265 | | * @retval -1 : bad parameter |
266 | | * @retval -2 : unexpected high value (changed by more than 1) |
267 | | */ |
268 | | int |
269 | | netsnmp_c64_check_for_32bit_wrap(const struct counter64 *old_val, |
270 | | struct counter64 *new_val, |
271 | | int adjust) |
272 | 0 | { |
273 | 0 | if( (NULL == old_val) || (NULL == new_val) ) |
274 | 0 | return -1; |
275 | | |
276 | 0 | DEBUGMSGTL(("9:c64:check_wrap", "check wrap 0x%0lx.0x%0lx 0x%0lx.0x%0lx\n", |
277 | 0 | old_val->high, old_val->low, new_val->high, new_val->low)); |
278 | | |
279 | | /* |
280 | | * check for wraps |
281 | | */ |
282 | 0 | if ((new_val->low >= old_val->low) && |
283 | 0 | (new_val->high == old_val->high)) { |
284 | 0 | DEBUGMSGTL(("9:c64:check_wrap", "no wrap\n")); |
285 | 0 | return 0; |
286 | 0 | } |
287 | | |
288 | | /* |
289 | | * low wrapped. did high change? |
290 | | */ |
291 | 0 | if (new_val->high == old_val->high) { |
292 | 0 | DEBUGMSGTL(("c64:check_wrap", "32 bit wrap\n")); |
293 | 0 | if (adjust) |
294 | 0 | new_val->high = (uint32_t)(new_val->high + 1); |
295 | 0 | return 32; |
296 | 0 | } |
297 | 0 | else if (new_val->high == (uint32_t)(old_val->high + 1)) { |
298 | 0 | DEBUGMSGTL(("c64:check_wrap", "64 bit wrap\n")); |
299 | 0 | return 64; |
300 | 0 | } |
301 | | |
302 | 0 | return -2; |
303 | 0 | } |
304 | | |
305 | | /** |
306 | | * update a 64 bit value with the difference between two (possibly) 32 bit vals |
307 | | * |
308 | | * @param prev_val : the 64 bit current counter |
309 | | * @param old_prev_val : the (possibly 32 bit) previous value |
310 | | * @param new_val : the (possible 32bit) new value |
311 | | * @param need_wrap_check: pointer to integer indicating if wrap check is needed |
312 | | * flag may be cleared if 64 bit counter is detected |
313 | | * |
314 | | * @note |
315 | | * The old_prev_val and new_val values must be be from within a time |
316 | | * period which would only allow the 32bit portion of the counter to |
317 | | * wrap once. i.e. if the 32bit portion of the counter could |
318 | | * wrap every 60 seconds, the old and new values should be compared |
319 | | * at least every 59 seconds (though I'd recommend at least every |
320 | | * 50 seconds to allow for timer inaccuracies). |
321 | | * |
322 | | * Suggested use: |
323 | | * |
324 | | * static needwrapcheck = 1; |
325 | | * static counter64 current, prev_val, new_val; |
326 | | * |
327 | | * your_functions_to_update_new_value(&new_val); |
328 | | * if (0 == needwrapcheck) |
329 | | * memcpy(current, new_val, sizeof(new_val)); |
330 | | * else { |
331 | | * netsnmp_c64_check32_and_update(¤t,&new,&prev,&needwrapcheck); |
332 | | * memcpy(prev_val, new_val, sizeof(new_val)); |
333 | | * } |
334 | | * |
335 | | * |
336 | | * @retval 0 : success |
337 | | * @retval -1 : error checking for 32 bit wrap |
338 | | * @retval -2 : look like we have 64 bit values, but sums aren't consistent |
339 | | */ |
340 | | int |
341 | | netsnmp_c64_check32_and_update(struct counter64 *prev_val, |
342 | | struct counter64 *new_val, |
343 | | const struct counter64 *old_prev_val, |
344 | | int *need_wrap_check) |
345 | 0 | { |
346 | 0 | int rc; |
347 | | |
348 | | /* |
349 | | * counters are 32bit or unknown (which we'll treat as 32bit). |
350 | | * update the prev values with the difference between the |
351 | | * new stats and the prev old_stats: |
352 | | * prev->stats += (new->stats - prev->old_stats) |
353 | | */ |
354 | 0 | if ((NULL == need_wrap_check) || (0 != *need_wrap_check)) { |
355 | 0 | rc = netsnmp_c64_check_for_32bit_wrap(old_prev_val, new_val, 1); |
356 | 0 | if (rc < 0) { |
357 | 0 | DEBUGMSGTL(("c64","32 bit check failed\n")); |
358 | 0 | return -1; |
359 | 0 | } |
360 | 0 | } |
361 | 0 | else |
362 | 0 | rc = 0; |
363 | | |
364 | | /* |
365 | | * update previous values |
366 | | */ |
367 | 0 | (void) u64UpdateCounter(prev_val, new_val, old_prev_val); |
368 | | |
369 | | /* |
370 | | * if wrap check was 32 bit, undo adjust, now that prev is updated |
371 | | */ |
372 | 0 | if (32 == rc) { |
373 | | /* |
374 | | * check wrap incremented high, so reset it. (Because having |
375 | | * high set for a 32 bit counter will confuse us in the next update). |
376 | | */ |
377 | 0 | if (1 != new_val->high) |
378 | 0 | DEBUGMSGTL(("c64", "error expanding to 64 bits: new_val->high != 1")); |
379 | 0 | new_val->high = 0; |
380 | 0 | } |
381 | 0 | else if (64 == rc) { |
382 | | /* |
383 | | * if we really have 64 bit counters, the summing we've been |
384 | | * doing for prev values should be equal to the new values. |
385 | | */ |
386 | 0 | if ((prev_val->low != new_val->low) || |
387 | 0 | (prev_val->high != new_val->high)) { |
388 | 0 | DEBUGMSGTL(("c64", "looks like a 64bit wrap, but prev!=new\n")); |
389 | 0 | return -2; |
390 | 0 | } |
391 | 0 | else if (NULL != need_wrap_check) |
392 | 0 | *need_wrap_check = 0; |
393 | 0 | } |
394 | | |
395 | 0 | return 0; |
396 | 0 | } |
397 | | |
398 | | /** Convert an unsigned 64-bit number to ASCII. */ |
399 | | void |
400 | | printU64(char *buf, /* char [I64CHARSZ+1]; */ |
401 | | const struct counter64 *pu64) |
402 | 205 | { |
403 | 205 | struct counter64 u64a; |
404 | 205 | struct counter64 u64b; |
405 | | |
406 | 205 | char aRes[I64CHARSZ + 1]; |
407 | 205 | unsigned int u; |
408 | 205 | int j; |
409 | | |
410 | 205 | u64a = *pu64; |
411 | 205 | aRes[I64CHARSZ] = 0; |
412 | 3.00k | for (j = 0; j < I64CHARSZ; j++) { |
413 | 3.00k | divBy10(u64a, &u64b, &u); |
414 | 3.00k | aRes[(I64CHARSZ - 1) - j] = (char) ('0' + u); |
415 | 3.00k | u64a = u64b; |
416 | 3.00k | if (isZeroU64(&u64a)) |
417 | 205 | break; |
418 | 3.00k | } |
419 | 205 | strcpy(buf, &aRes[(I64CHARSZ - 1) - j]); |
420 | 205 | } |
421 | | |
422 | | /** Convert a signed 64-bit number to ASCII. */ |
423 | | void |
424 | | printI64(char *buf, /* char [I64CHARSZ+1]; */ |
425 | | const struct counter64 *pu64) |
426 | 94 | { |
427 | 94 | struct counter64 u64a; |
428 | | |
429 | 94 | if (pu64->high & 0x80000000) { |
430 | 54 | u64a.high = (uint32_t) ~pu64->high; |
431 | 54 | u64a.low = (uint32_t) ~pu64->low; |
432 | 54 | incrByU32(&u64a, 1); /* bit invert and incr by 1 to print 2s complement */ |
433 | 54 | buf[0] = '-'; |
434 | 54 | printU64(buf + 1, &u64a); |
435 | 54 | } else { |
436 | 40 | printU64(buf, pu64); |
437 | 40 | } |
438 | 94 | } |
439 | | |
440 | | /** Convert a signed 64-bit integer from ASCII to struct counter64. */ |
441 | | int |
442 | | read64(struct counter64 *i64, const char *str) |
443 | 496 | { |
444 | 496 | struct counter64 i64p; |
445 | 496 | unsigned int u; |
446 | 496 | int sign = 0; |
447 | 496 | int ok = 0; |
448 | | |
449 | 496 | zeroU64(i64); |
450 | 496 | if (*str == '-') { |
451 | 174 | sign = 1; |
452 | 174 | str++; |
453 | 174 | } |
454 | | |
455 | 5.89k | while (*str && isdigit((unsigned char)(*str))) { |
456 | 5.39k | ok = 1; |
457 | 5.39k | u = *str - '0'; |
458 | 5.39k | multBy10(*i64, &i64p); |
459 | 5.39k | *i64 = i64p; |
460 | 5.39k | incrByU16(i64, u); |
461 | 5.39k | str++; |
462 | 5.39k | } |
463 | 496 | if (sign) { |
464 | 174 | i64->high = (uint32_t) ~i64->high; |
465 | 174 | i64->low = (uint32_t) ~i64->low; |
466 | 174 | incrByU16(i64, 1); |
467 | 174 | } |
468 | 496 | return ok; |
469 | 496 | } |