Coverage Report

Created: 2024-07-27 06:09

/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(&current,&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
}