Blame snmplib/int64.c

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