/** file: test.c - test of 64-bit integer stuff * * * 21-jan-1998: David Perkins * */ #include #include #include #include #include #if HAVE_STRING_H #include #else #include #endif #if HAVE_WINSOCK_H #include #endif #include #include #include #include #include #define TRUE 1 #define FALSE 0 /** divBy10 - divide an unsigned 64-bit integer by 10 * * call with: * u64 - number to be divided * pu64Q - location to store quotient * puR - location to store 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); } /* divBy10 */ /** multBy10 - multiply an unsigned 64-bit integer by 10 * * call with: * u64 - number to be multiplied * pu64P - location to store 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; } /* multBy10 */ /** incrByU16 - add an unsigned 16-bit int to an unsigned 64-bit integer * * call with: * pu64 - number to be incremented * u16 - amount to add * */ void incrByU16(U64 * pu64, unsigned int u16) { unsigned long ulT1; unsigned long ulT2; unsigned long ulR; unsigned long ulK; /* * lower 16 bits */ ulT1 = pu64->low; ulT2 = ulT1 & 0x0ffff; ulR = ulT2 + u16; ulK = ulR >> 16; if (ulK == 0) { pu64->low = ulT1 + u16; return; } /* * next 16 bits */ ulT2 = (ulT1 >> 16) & 0x0ffff; ulR = ulT2 + 1; ulK = ulR >> 16; if (ulK == 0) { pu64->low = ulT1 + u16; return; } /* * next 32 - ignore any overflow */ pu64->low = (ulT1 + u16) & 0x0FFFFFFFFL; pu64->high++; } /* incrByV16 */ void incrByU32(U64 * pu64, unsigned int u32) { unsigned int tmp; tmp = pu64->low; pu64->low += u32; if (pu64->low < tmp) pu64->high++; } /** * pu64out = pu64one - pu64two */ void u64Subtract(const U64 * pu64one, const U64 * pu64two, U64 * pu64out) { if (pu64one->low < pu64two->low) { pu64out->low = 0xffffffff - pu64two->low + pu64one->low + 1; pu64out->high = pu64one->high - pu64two->high - 1; } else { pu64out->low = pu64one->low - pu64two->low; pu64out->high = pu64one->high - pu64two->high; } } /** * pu64out += pu64one */ void u64Incr(U64 * pu64out, const U64 * pu64one) { pu64out->high += pu64one->high; incrByU32(pu64out, pu64one->low); } /** * pu64out += (pu64one - pu64two) */ void u64UpdateCounter(U64 * pu64out, const U64 * pu64one, const U64 * pu64two) { U64 tmp; u64Subtract(pu64one, pu64two, &tmp); u64Incr(pu64out, &tmp); } /** * pu64one = pu64two */ void u64Copy(U64 * pu64one, const U64 * pu64two) { pu64one->high = pu64two->high; pu64one->low = pu64two->low; } /** zeroU64 - set an unsigned 64-bit number to zero * * call with: * pu64 - number to be zero'ed * */ void zeroU64(U64 * pu64) { pu64->low = 0; pu64->high = 0; } /* zeroU64 */ /** isZeroU64 - check if an unsigned 64-bit number is * * call with: * pu64 - number to be zero'ed * */ int isZeroU64(const U64 * pu64) { if ((pu64->low == 0) && (pu64->high == 0)) return (TRUE); else return (FALSE); } /* isZeroU64 */ /** * 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. * *@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%0x.0x%0x 0x%0x.0x%0x\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; return 32; } else if ((new_val->high == (old_val->high + 1)) || ((0 == new_val->high) && (0xffffffff == old_val->high))) { 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(¤t,&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) { snmp_log(LOG_ERR,"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)) { snmp_log(LOG_ERR, "looks like a 64bit wrap, but prev!=new\n"); return -2; } else if (NULL != need_wrap_check) *need_wrap_check = 0; } return 0; } void printU64(char *buf, /* char [I64CHARSZ+1]; */ const U64 * pu64) { U64 u64a; U64 u64b; char aRes[I64CHARSZ + 1]; unsigned int u; int j; u64a.high = pu64->high; u64a.low = pu64->low; aRes[I64CHARSZ] = 0; for (j = 0; j < I64CHARSZ; j++) { divBy10(u64a, &u64b, &u); aRes[(I64CHARSZ - 1) - j] = (char) ('0' + u); u64a.high = u64b.high; u64a.low = u64b.low; if (isZeroU64(&u64a)) break; } strcpy(buf, &aRes[(I64CHARSZ - 1) - j]); } void printI64(char *buf, /* char [I64CHARSZ+1]; */ const U64 * pu64) { U64 u64a; U64 u64b; char aRes[I64CHARSZ + 1]; unsigned int u; int j, sign = 0; if (pu64->high & 0x80000000) { u64a.high = ~pu64->high; u64a.low = ~pu64->low; sign = 1; incrByU32(&u64a, 1); /* bit invert and incr by 1 to print 2s complement */ } else { u64a.high = pu64->high; u64a.low = pu64->low; } aRes[I64CHARSZ] = 0; for (j = 0; j < I64CHARSZ; j++) { divBy10(u64a, &u64b, &u); aRes[(I64CHARSZ - 1) - j] = (char) ('0' + u); u64a.high = u64b.high; u64a.low = u64b.low; if (isZeroU64(&u64a)) break; } if (sign == 1) { aRes[(I64CHARSZ - 1) - j - 1] = '-'; strcpy(buf, &aRes[(I64CHARSZ - 1) - j - 1]); return; } strcpy(buf, &aRes[(I64CHARSZ - 1) - j]); } int read64(U64 * i64, const char *string) { U64 i64p; unsigned int u; int sign = 0; int ok = 0; zeroU64(i64); if (*string == '-') { sign = 1; string++; } while (*string && isdigit(*string)) { ok = 1; u = *string - '0'; multBy10(*i64, &i64p); memcpy(i64, &i64p, sizeof(i64p)); incrByU16(i64, u); string++; } if (sign) { i64->high = ~i64->high; i64->low = ~i64->low; incrByU16(i64, 1); } return ok; } #ifdef TESTING void main(int argc, char *argv[]) { int i; int j; int l; unsigned int u; U64 u64a; U64 u64b; #define MXSZ 20 char aRes[MXSZ + 1]; if (argc < 2) { printf("This program takes numbers from the command line\n" "and prints them out.\n" "Usage: test ...\n"); exit(1); } aRes[MXSZ] = 0; for (i = 1; i < argc; i++) { l = strlen(argv[i]); zeroU64(&u64a); for (j = 0; j < l; j++) { if (!isdigit(argv[i][j])) { printf("Argument is not a number \"%s\"\n", argv[i]); exit(1); } u = argv[i][j] - '0'; multBy10(u64a, &u64b); u64a = u64b; incrByU16(&u64a, u); } printf("number \"%s\" in hex is '%08x%08x'h\n", argv[i], u64a.high, u64a.low); printf("number is \"%s\"\n", printU64(&u64a)); for (j = 0; j < MXSZ; j++) { divBy10(u64a, &u64b, &u); aRes[(MXSZ - 1) - j] = (char) ('0' + u); u64a = u64b; if (isZeroU64(&u64a)) break; } printf("number is \"%s\"\n", &aRes[(MXSZ - 1) - j]); } exit(0); } /* main */ #endif /* TESTING */ /* * file: test.c */