// SPDX-License-Identifier: GPL-3.0-or-later #include "../libnetdata.h" #ifndef NETDATA_ADAPTIVE_RESORTABLE_LIST_H #define NETDATA_ADAPTIVE_RESORTABLE_LIST_H 1 #define ARL_ENTRY_FLAG_FOUND 0x01 // the entry has been found in the source data #define ARL_ENTRY_FLAG_EXPECTED 0x02 // the entry is expected by the program #define ARL_ENTRY_FLAG_DYNAMIC 0x04 // the entry was dynamically allocated, from source data typedef struct arl_entry { char *name; // the keywords uint32_t hash; // the hash of the keyword void *dst; // the dst to pass to the processor uint8_t flags; // ARL_ENTRY_FLAG_* // the processor to do the job void (*processor)(const char *name, uint32_t hash, const char *value, void *dst); // double linked list for fast re-linkings struct arl_entry *prev, *next; } ARL_ENTRY; typedef struct arl_base { char *name; size_t iteration; // incremented on each iteration (arl_begin()) size_t found; // the number of expected keywords found in this iteration size_t expected; // the number of expected keywords size_t wanted; // the number of wanted keywords // i.e. the number of keywords found and expected size_t relinkings; // the number of relinkings we have made so far size_t allocated; // the number of keywords allocated size_t fred; // the number of keywords cleaned up size_t rechecks; // the number of iterations between re-checks of the // wanted number of keywords // this is only needed in cases where the source // is having less lines over time. size_t added; // it is non-zero if new keywords have been added // this is only needed to detect new lines have // been added to the file, over time. #ifdef NETDATA_INTERNAL_CHECKS size_t fast; // the number of times we have taken the fast path size_t slow; // the number of times we have taken the slow path #endif // the processor to do the job void (*processor)(const char *name, uint32_t hash, const char *value, void *dst); // the linked list of the keywords ARL_ENTRY *head; // since we keep the list of keywords sorted (as found in the source data) // this is next keyword that we expect to find in the source data. ARL_ENTRY *next_keyword; } ARL_BASE; // create a new ARL ARL_BASE *arl_create(const char *name, void (*processor)(const char *, uint32_t, const char *, void *), size_t rechecks); // free an ARL void arl_free(ARL_BASE *arl_base); // register an expected keyword to the ARL // together with its destination ( i.e. the output of the processor() ) ARL_ENTRY *arl_expect_custom(ARL_BASE *base, const char *keyword, void (*processor)(const char *name, uint32_t hash, const char *value, void *dst), void *dst); #define arl_expect(base, keyword, dst) arl_expect_custom(base, keyword, NULL, dst) // an internal call to complete the check() call int arl_find_or_create_and_relink(ARL_BASE *base, const char *s, const char *value); // begin an ARL iteration void arl_begin(ARL_BASE *base); void arl_callback_str2ull(const char *name, uint32_t hash, const char *value, void *dst); void arl_callback_str2kernel_uint_t(const char *name, uint32_t hash, const char *value, void *dst); void arl_callback_ssize_t(const char *name, uint32_t hash, const char *value, void *dst); // check a keyword against the ARL // this is to be called for each keyword read from source data // s = the keyword, as collected // src = the src data to be passed to the processor // it is defined in the header file in order to be inlined static inline int arl_check(ARL_BASE *base, const char *keyword, const char *value) { ARL_ENTRY *e = base->next_keyword; #ifdef NETDATA_INTERNAL_CHECKS if(unlikely((base->fast + base->slow) % (base->expected + base->allocated) == 0 && (base->fast + base->slow) > (base->expected + base->allocated) * base->iteration)) netdata_log_info("ARL '%s': Did you forget to call arl_begin()?", base->name); #endif // it should be the first entry (pointed by base->next_keyword) if(likely(!strcmp(keyword, e->name))) { // it is #ifdef NETDATA_INTERNAL_CHECKS base->fast++; #endif e->flags |= ARL_ENTRY_FLAG_FOUND; // execute the processor if(unlikely(e->dst)) { e->processor(e->name, e->hash, value, e->dst); base->found++; } // be prepared for the next iteration base->next_keyword = e->next; if(unlikely(!base->next_keyword)) base->next_keyword = base->head; // stop if we collected all the values for this iteration if(unlikely(base->found == base->wanted)) { // fprintf(stderr, "FOUND ALL WANTED 2: found = %zu, wanted = %zu, expected %zu\n", base->found, base->wanted, base->expected); return 1; } return 0; } #ifdef NETDATA_INTERNAL_CHECKS base->slow++; #endif // we read from source, a not-expected keyword return arl_find_or_create_and_relink(base, keyword, value); } #endif //NETDATA_ADAPTIVE_RESORTABLE_LIST_H