summaryrefslogtreecommitdiff
path: root/snmplib/int64.c
diff options
context:
space:
mode:
Diffstat (limited to 'snmplib/int64.c')
-rw-r--r--snmplib/int64.c461
1 files changed, 461 insertions, 0 deletions
diff --git a/snmplib/int64.c b/snmplib/int64.c
new file mode 100644
index 0000000..51f1f32
--- /dev/null
+++ b/snmplib/int64.c
@@ -0,0 +1,461 @@
+/**
+ * @file int64.c
+ *
+ * @brief Functions for 64-bit integer computations.
+ *
+ * 21-jan-1998: David Perkins <dperkins@dsperkins.com>
+ */
+
+#include <net-snmp/net-snmp-config.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#if HAVE_STRING_H
+#include <string.h>
+#else
+#include <strings.h>
+#endif
+
+#include <net-snmp/types.h>
+#include <net-snmp/library/int64.h>
+#include <net-snmp/library/snmp_assert.h>
+#include <net-snmp/library/snmp_debug.h>
+#include <net-snmp/library/snmp_logging.h>
+
+#include <net-snmp/net-snmp-features.h>
+
+/**
+ * Divide an unsigned 64-bit integer by 10.
+ *
+ * @param[in] u64 Number to be divided.
+ * @param[out] pu64Q Quotient.
+ * @param[out] puR Remainder.
+ */
+void
+divBy10(U64 u64, U64 * pu64Q, unsigned int *puR)
+{
+ unsigned long ulT;
+ unsigned long ulQ;
+ unsigned long ulR;
+
+ /*
+ * top 16 bits
+ */
+ ulT = (u64.high >> 16) & 0x0ffff;
+ ulQ = ulT / 10;
+ ulR = ulT % 10;
+ pu64Q->high = ulQ << 16;
+
+ /*
+ * next 16
+ */
+ ulT = (u64.high & 0x0ffff);
+ ulT += (ulR << 16);
+ ulQ = ulT / 10;
+ ulR = ulT % 10;
+ pu64Q->high = pu64Q->high | ulQ;
+
+ /*
+ * next 16
+ */
+ ulT = ((u64.low >> 16) & 0x0ffff) + (ulR << 16);
+ ulQ = ulT / 10;
+ ulR = ulT % 10;
+ pu64Q->low = ulQ << 16;
+
+ /*
+ * final 16
+ */
+ ulT = (u64.low & 0x0ffff);
+ ulT += (ulR << 16);
+ ulQ = ulT / 10;
+ ulR = ulT % 10;
+ pu64Q->low = pu64Q->low | ulQ;
+
+ *puR = (unsigned int) (ulR);
+}
+
+/**
+ * Multiply an unsigned 64-bit integer by 10.
+ *
+ * @param[in] u64 Number to be multiplied.
+ * @param[out] pu64P Product.
+ */
+void
+multBy10(U64 u64, U64 * pu64P)
+{
+ unsigned long ulT;
+ unsigned long ulP;
+ unsigned long ulK;
+
+ /*
+ * lower 16 bits
+ */
+ ulT = u64.low & 0x0ffff;
+ ulP = ulT * 10;
+ ulK = ulP >> 16;
+ pu64P->low = ulP & 0x0ffff;
+
+ /*
+ * next 16
+ */
+ ulT = (u64.low >> 16) & 0x0ffff;
+ ulP = (ulT * 10) + ulK;
+ ulK = ulP >> 16;
+ pu64P->low = (ulP & 0x0ffff) << 16 | pu64P->low;
+
+ /*
+ * next 16 bits
+ */
+ ulT = u64.high & 0x0ffff;
+ ulP = (ulT * 10) + ulK;
+ ulK = ulP >> 16;
+ pu64P->high = ulP & 0x0ffff;
+
+ /*
+ * final 16
+ */
+ ulT = (u64.high >> 16) & 0x0ffff;
+ ulP = (ulT * 10) + ulK;
+ ulK = ulP >> 16;
+ pu64P->high = (ulP & 0x0ffff) << 16 | pu64P->high;
+}
+
+/**
+ * Add an unsigned 16-bit int to an unsigned 64-bit integer.
+ *
+ * @param[in,out] pu64 Number to be incremented.
+ * @param[in] u16 Amount to add.
+ *
+ */
+void
+incrByU16(U64 * pu64, unsigned int u16)
+{
+ incrByU32(pu64, u16);
+}
+
+/**
+ * Add an unsigned 32-bit int to an unsigned 64-bit integer.
+ *
+ * @param[in,out] pu64 Number to be incremented.
+ * @param[in] u32 Amount to add.
+ *
+ */
+void
+incrByU32(U64 * pu64, unsigned int u32)
+{
+ uint32_t tmp;
+
+ tmp = pu64->low;
+ pu64->low = (uint32_t)(tmp + u32);
+ if (pu64->low < tmp)
+ pu64->high = (uint32_t)(pu64->high + 1);
+}
+
+/**
+ * Subtract two 64-bit numbers.
+ *
+ * @param[in] pu64one Number to start from.
+ * @param[in] pu64two Amount to subtract.
+ * @param[out] pu64out pu64one - pu64two.
+ */
+void
+u64Subtract(const U64 * pu64one, const U64 * pu64two, U64 * pu64out)
+{
+ int carry;
+
+ carry = pu64one->low < pu64two->low;
+ pu64out->low = (uint32_t)(pu64one->low - pu64two->low);
+ pu64out->high = (uint32_t)(pu64one->high - pu64two->high - carry);
+}
+
+/**
+ * Add two 64-bit numbers.
+ *
+ * @param[in] pu64one Amount to add.
+ * @param[in,out] pu64out pu64out += pu64one.
+ */
+void
+u64Incr(U64 * pu64out, const U64 * pu64one)
+{
+ pu64out->high = (uint32_t)(pu64out->high + pu64one->high);
+ incrByU32(pu64out, pu64one->low);
+}
+
+/**
+ * Add the difference of two 64-bit numbers to a 64-bit counter.
+ *
+ * @param[in] pu64one
+ * @param[in] pu64two
+ * @param[out] pu64out pu64out += (pu64one - pu64two)
+ */
+void
+u64UpdateCounter(U64 * pu64out, const U64 * pu64one, const U64 * pu64two)
+{
+ U64 tmp;
+
+ u64Subtract(pu64one, pu64two, &tmp);
+ u64Incr(pu64out, &tmp);
+}
+
+netsnmp_feature_child_of(u64copy, netsnmp_unused)
+#ifndef NETSNMP_FEATURE_REMOVE_U64COPY
+/**
+ * Copy a 64-bit number.
+ *
+ * @param[in] pu64two Number to be copied.
+ * @param[out] pu64one Where to store the copy - *pu64one = *pu64two.
+ */
+void
+u64Copy(U64 * pu64one, const U64 * pu64two)
+{
+ *pu64one = *pu64two;
+}
+#endif /* NETSNMP_FEATURE_REMOVE_U64COPY */
+
+/**
+ * Set an unsigned 64-bit number to zero.
+ *
+ * @param[in] pu64 Number to be zeroed.
+ */
+void
+zeroU64(U64 * pu64)
+{
+ pu64->low = 0;
+ pu64->high = 0;
+}
+
+/**
+ * Check if an unsigned 64-bit number is zero.
+ *
+ * @param[in] pu64 Number to be checked.
+ */
+int
+isZeroU64(const U64 * pu64)
+{
+ return pu64->low == 0 && pu64->high == 0;
+}
+
+/**
+ * check the old and new values of a counter64 for 32bit wrapping
+ *
+ * @param adjust : set to 1 to auto-increment new_val->high
+ * if a 32bit wrap is detected.
+ *
+ * @param old_val
+ * @param new_val
+ *
+ * @note
+ * The old and new values must be be from within a time period
+ * which would only allow the 32bit portion of the counter to
+ * wrap once. i.e. if the 32bit portion of the counter could
+ * wrap every 60 seconds, the old and new values should be compared
+ * at least every 59 seconds (though I'd recommend at least every
+ * 50 seconds to allow for timer inaccuracies).
+ *
+ * @retval 64 : 64bit wrap
+ * @retval 32 : 32bit wrap
+ * @retval 0 : did not wrap
+ * @retval -1 : bad parameter
+ * @retval -2 : unexpected high value (changed by more than 1)
+ */
+int
+netsnmp_c64_check_for_32bit_wrap(struct counter64 *old_val,
+ struct counter64 *new_val,
+ int adjust)
+{
+ if( (NULL == old_val) || (NULL == new_val) )
+ return -1;
+
+ DEBUGMSGTL(("9:c64:check_wrap", "check wrap 0x%0lx.0x%0lx 0x%0lx.0x%0lx\n",
+ old_val->high, old_val->low, new_val->high, new_val->low));
+
+ /*
+ * check for wraps
+ */
+ if ((new_val->low >= old_val->low) &&
+ (new_val->high == old_val->high)) {
+ DEBUGMSGTL(("9:c64:check_wrap", "no wrap\n"));
+ return 0;
+ }
+
+ /*
+ * low wrapped. did high change?
+ */
+ if (new_val->high == old_val->high) {
+ DEBUGMSGTL(("c64:check_wrap", "32 bit wrap\n"));
+ if (adjust)
+ new_val->high = (uint32_t)(new_val->high + 1);
+ return 32;
+ }
+ else if (new_val->high == (uint32_t)(old_val->high + 1)) {
+ DEBUGMSGTL(("c64:check_wrap", "64 bit wrap\n"));
+ return 64;
+ }
+
+ return -2;
+}
+
+/**
+ * update a 64 bit value with the difference between two (possibly) 32 bit vals
+ *
+ * @param prev_val : the 64 bit current counter
+ * @param old_prev_val : the (possibly 32 bit) previous value
+ * @param new_val : the (possible 32bit) new value
+ * @param need_wrap_check: pointer to integer indicating if wrap check is needed
+ * flag may be cleared if 64 bit counter is detected
+ *
+ * @note
+ * The old_prev_val and new_val values must be be from within a time
+ * period which would only allow the 32bit portion of the counter to
+ * wrap once. i.e. if the 32bit portion of the counter could
+ * wrap every 60 seconds, the old and new values should be compared
+ * at least every 59 seconds (though I'd recommend at least every
+ * 50 seconds to allow for timer inaccuracies).
+ *
+ * Suggested use:
+ *
+ * static needwrapcheck = 1;
+ * static counter64 current, prev_val, new_val;
+ *
+ * your_functions_to_update_new_value(&new_val);
+ * if (0 == needwrapcheck)
+ * memcpy(current, new_val, sizeof(new_val));
+ * else {
+ * netsnmp_c64_check32_and_update(&current,&new,&prev,&needwrapcheck);
+ * memcpy(prev_val, new_val, sizeof(new_val));
+ * }
+ *
+ *
+ * @retval 0 : success
+ * @retval -1 : error checking for 32 bit wrap
+ * @retval -2 : look like we have 64 bit values, but sums aren't consistent
+ */
+int
+netsnmp_c64_check32_and_update(struct counter64 *prev_val, struct counter64 *new_val,
+ struct counter64 *old_prev_val, int *need_wrap_check)
+{
+ int rc;
+
+ /*
+ * counters are 32bit or unknown (which we'll treat as 32bit).
+ * update the prev values with the difference between the
+ * new stats and the prev old_stats:
+ * prev->stats += (new->stats - prev->old_stats)
+ */
+ if ((NULL == need_wrap_check) || (0 != *need_wrap_check)) {
+ rc = netsnmp_c64_check_for_32bit_wrap(old_prev_val,new_val, 1);
+ if (rc < 0) {
+ DEBUGMSGTL(("c64","32 bit check failed\n"));
+ return -1;
+ }
+ }
+ else
+ rc = 0;
+
+ /*
+ * update previous values
+ */
+ (void) u64UpdateCounter(prev_val, new_val, old_prev_val);
+
+ /*
+ * if wrap check was 32 bit, undo adjust, now that prev is updated
+ */
+ if (32 == rc) {
+ /*
+ * check wrap incremented high, so reset it. (Because having
+ * high set for a 32 bit counter will confuse us in the next update).
+ */
+ netsnmp_assert(1 == new_val->high);
+ new_val->high = 0;
+ }
+ else if (64 == rc) {
+ /*
+ * if we really have 64 bit counters, the summing we've been
+ * doing for prev values should be equal to the new values.
+ */
+ if ((prev_val->low != new_val->low) ||
+ (prev_val->high != new_val->high)) {
+ DEBUGMSGTL(("c64", "looks like a 64bit wrap, but prev!=new\n"));
+ return -2;
+ }
+ else if (NULL != need_wrap_check)
+ *need_wrap_check = 0;
+ }
+
+ return 0;
+}
+
+/** Convert an unsigned 64-bit number to ASCII. */
+void
+printU64(char *buf, /* char [I64CHARSZ+1]; */
+ const U64 * pu64)
+{
+ U64 u64a;
+ U64 u64b;
+
+ char aRes[I64CHARSZ + 1];
+ unsigned int u;
+ int j;
+
+ u64a = *pu64;
+ aRes[I64CHARSZ] = 0;
+ for (j = 0; j < I64CHARSZ; j++) {
+ divBy10(u64a, &u64b, &u);
+ aRes[(I64CHARSZ - 1) - j] = (char) ('0' + u);
+ u64a = u64b;
+ if (isZeroU64(&u64a))
+ break;
+ }
+ strcpy(buf, &aRes[(I64CHARSZ - 1) - j]);
+}
+
+/** Convert a signed 64-bit number to ASCII. */
+void
+printI64(char *buf, /* char [I64CHARSZ+1]; */
+ const U64 * pu64)
+{
+ U64 u64a;
+
+ if (pu64->high & 0x80000000) {
+ u64a.high = (uint32_t) ~pu64->high;
+ u64a.low = (uint32_t) ~pu64->low;
+ incrByU32(&u64a, 1); /* bit invert and incr by 1 to print 2s complement */
+ buf[0] = '-';
+ printU64(buf + 1, &u64a);
+ } else {
+ printU64(buf, pu64);
+ }
+}
+
+/** Convert a signed 64-bit integer from ASCII to U64. */
+int
+read64(U64 * i64, const char *str)
+{
+ U64 i64p;
+ unsigned int u;
+ int sign = 0;
+ int ok = 0;
+
+ zeroU64(i64);
+ if (*str == '-') {
+ sign = 1;
+ str++;
+ }
+
+ while (*str && isdigit((unsigned char)(*str))) {
+ ok = 1;
+ u = *str - '0';
+ multBy10(*i64, &i64p);
+ *i64 = i64p;
+ incrByU16(i64, u);
+ str++;
+ }
+ if (sign) {
+ i64->high = (uint32_t) ~i64->high;
+ i64->low = (uint32_t) ~i64->low;
+ incrByU16(i64, 1);
+ }
+ return ok;
+}