/* Copyright (c) 2011-2018 Dovecot authors, see the included COPYING file */
#include "lib.h"
#include "str.h"
#include "time-util.h"
#include "stats-parser.h"
#define USECS_PER_SEC 1000000
static bool stats_diff_timeval(struct timeval *dest,
const struct timeval *src1,
const struct timeval *src2)
{
long long diff_usecs;
diff_usecs = timeval_diff_usecs(src2, src1);
if (diff_usecs < 0)
return FALSE;
dest->tv_sec = diff_usecs / USECS_PER_SEC;
dest->tv_usec = diff_usecs % USECS_PER_SEC;
return TRUE;
}
static bool
stats_diff_uint32(uint32_t *dest, const uint32_t *src1, const uint32_t *src2)
{
if (*src1 > *src2)
return FALSE;
*dest = *src2 - *src1;
return TRUE;
}
static bool
stats_diff_uint64(uint64_t *dest, const uint64_t *src1, const uint64_t *src2)
{
if (*src1 > *src2)
return FALSE;
*dest = *src2 - *src1;
return TRUE;
}
bool stats_parser_diff(const struct stats_parser_field *fields,
unsigned int fields_count,
const struct stats *stats1, const struct stats *stats2,
struct stats *diff_stats_r, const char **error_r)
{
unsigned int i;
for (i = 0; i < fields_count; i++) {
unsigned int offset = fields[i].offset;
void *dest = PTR_OFFSET(diff_stats_r, offset);
const void *src1 = CONST_PTR_OFFSET(stats1, offset);
const void *src2 = CONST_PTR_OFFSET(stats2, offset);
switch (fields[i].type) {
case STATS_PARSER_TYPE_UINT:
switch (fields[i].size) {
case sizeof(uint32_t):
if (!stats_diff_uint32(dest, src1, src2)) {
*error_r = t_strdup_printf("%s %u < %u",
fields[i].name,
*(const uint32_t *)src2,
*(const uint32_t *)src1);
return FALSE;
}
break;
case sizeof(uint64_t):
if (!stats_diff_uint64(dest, src1, src2)) {
const uint64_t *n1 = src1, *n2 = src2;
*error_r = t_strdup_printf("%s %"PRIu64" < %"PRIu64,
fields[i].name, *n2, *n1);
return FALSE;
}
break;
default:
i_unreached();
}
break;
case STATS_PARSER_TYPE_TIMEVAL:
if (!stats_diff_timeval(dest, src1, src2)) {
const struct timeval *tv1 = src1, *tv2 = src2;
*error_r = t_strdup_printf("%s %ld.%d < %ld.%d",
fields[i].name,
(long)tv2->tv_sec, (int)tv2->tv_usec,
(long)tv1->tv_sec, (int)tv1->tv_usec);
return FALSE;
}
break;
}
}
return TRUE;
}
static void stats_timeval_add(struct timeval *dest, const struct timeval *src)
{
dest->tv_sec += src->tv_sec;
dest->tv_usec += src->tv_usec;
if (dest->tv_usec > USECS_PER_SEC) {
dest->tv_usec -= USECS_PER_SEC;
dest->tv_sec++;
}
}
void stats_parser_add(const struct stats_parser_field *fields,
unsigned int fields_count,
struct stats *dest, const struct stats *src)
{
unsigned int i;
for (i = 0; i < fields_count; i++) {
unsigned int offset = fields[i].offset;
void *f_dest = PTR_OFFSET(dest, offset);
const void *f_src = CONST_PTR_OFFSET(src, offset);
switch (fields[i].type) {
case STATS_PARSER_TYPE_UINT:
switch (fields[i].size) {
case sizeof(uint32_t): {
uint32_t *n_dest = f_dest;
const uint32_t *n_src = f_src;
*n_dest += *n_src;
break;
}
case sizeof(uint64_t): {
uint64_t *n_dest = f_dest;
const uint64_t *n_src = f_src;
*n_dest += *n_src;
break;
}
default:
i_unreached();
}
break;
case STATS_PARSER_TYPE_TIMEVAL:
stats_timeval_add(f_dest, f_src);
break;
}
}
}
void stats_parser_value(string_t *str,
const struct stats_parser_field *field,
const void *data)
{
const void *ptr = CONST_PTR_OFFSET(data, field->offset);
switch (field->type) {
case STATS_PARSER_TYPE_UINT:
switch (field->size) {
case sizeof(uint32_t): {
const uint32_t *n = ptr;
str_printfa(str, "%u", *n);
break;
}
case sizeof(uint64_t): {
const uint64_t *n = ptr;
str_printfa(str, "%"PRIu64, *n);
break;
}
default:
i_unreached();
}
break;
case STATS_PARSER_TYPE_TIMEVAL: {
const struct timeval *tv = ptr;
str_printfa(str, "%"PRIdTIME_T".%u",
tv->tv_sec, (unsigned int)tv->tv_usec);
break;
}
}
}