// -*- c-file-style: "bsd" -*-

#ifndef _LIB_LATENCY_H_
#define _LIB_LATENCY_H_

#include <stdbool.h>
#include <stdint.h>
#include <time.h>

#include "lib/iobuf.h"

// The number of the maximum distribution type.  Since we use
// characters as distribution types, this is 127.  We could probably
// shift things down by 32 to save some space, but that's probably not
// worth anything.
#define LATENCY_MAX_DIST 127

// The maximum number of unique distribution types in a single latency
// distribution.
#define LATENCY_DIST_POOL_SIZE 5

// The width of a printed histogram in characters.
#define LATENCY_HISTOGRAM_WIDTH 50

// The number of histogram buckets.
#define LATENCY_NUM_BUCKETS 65

typedef struct Latency_Frame_t
{
        struct timespec start;
        uint64_t accum;
        struct Latency_Frame_t *parent;
} Latency_Frame_t;

typedef struct Latency_Dist_t
{
        uint64_t min, max, total, count;
        uint32_t buckets[LATENCY_NUM_BUCKETS];
        char type;
} Latency_Dist_t;

typedef struct Latency_t
{
        const char *name;

        Latency_Dist_t *dists[LATENCY_MAX_DIST];
        Latency_Dist_t distPool[LATENCY_DIST_POOL_SIZE];
        int distPoolNext;

        Latency_Frame_t *bottom;
        Latency_Frame_t defaultFrame;

        struct Latency_t *next;
} Latency_t;

#define DEFINE_LATENCY(name)                                            \
        static Latency_t name;                                          \
        static __attribute__((constructor)) void _##name##_init(void)   \
        {                                                               \
                _Latency_Init(&name, #name);                            \
        }

void _Latency_Init(Latency_t *l, const char *name);

void Latency_StartRec(Latency_t *l, Latency_Frame_t *fr);
void Latency_EndRecType(Latency_t *l, Latency_Frame_t *fr, char type);
void Latency_Pause(Latency_t *l);
void Latency_Resume(Latency_t *l);

void Latency_Sum(Latency_t *dest, Latency_t *summand);

void Latency_Dump(Latency_t *l);
void Latency_DumpAll(void);
void Latency_Flush(void);

void Latency_Put(Latency_t *l, IOBuf_t *buf);
bool Latency_TryGet(Latency_t *l, IOBuf_t *buf);

static inline void
Latency_Start(Latency_t *l)
{
        Latency_StartRec(l, &l->defaultFrame);
}

static inline void
Latency_EndRec(Latency_t *l, Latency_Frame_t *fr)
{
        Latency_EndRecType(l, fr, '=');
}

static inline void
Latency_EndType(Latency_t *l, char type)
{
        Latency_EndRecType(l, &l->defaultFrame, type);
}

static inline void
Latency_End(Latency_t *l)
{
        Latency_EndRec(l, &l->defaultFrame);
}

#endif // _LIB_LATENCY_H_
