From: Arran Cudbard-Bell Date: Thu, 10 Oct 2024 03:20:42 +0000 (-0400) Subject: Move table search functions into table.h, and add new macros to return pointers to... X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4a288f30ad2ace602a85afd48ab131b65f89ee6e;p=thirdparty%2Ffreeradius-server.git Move table search functions into table.h, and add new macros to return pointers to values This lets us create one line search function definitions for custom array types --- diff --git a/src/lib/util/table.c b/src/lib/util/table.c index c9bbf86f4f4..a8184ef5af3 100644 --- a/src/lib/util/table.c +++ b/src/lib/util/table.c @@ -26,12 +26,6 @@ RCSID("$Id$") #include #include -#include - -#define TABLE_IDX(_table, _idx, _element_size) (((uint8_t const *)(_table)) + ((_idx) * (_element_size))) -#define ELEM_STR(_offset) (*((fr_table_elem_t const *)(_offset))).str -#define ELEM_LEN(_offset) (*((fr_table_elem_t const *)(_offset))).len - /** Brute force search a sorted or ordered ptr table, assuming the pointers are strings * * @param[in] table to search in. @@ -50,417 +44,34 @@ char const *_fr_table_ptr_by_str_value(fr_table_ptr_sorted_t const *table, size_ return def; } -/** Create type specific string to value functions - * - * @param[in] _func used for searching. - * @param[in] _our_table_type that we'll be searching in. - * @param[in] _our_name The function that we'll be creating. - * @param[in] _our_def_type The type of the default value. - * @param[in] _our_return_type What we return. - */ -#define TABLE_TYPE_STR_FUNC(_func, _our_table_type, _our_name, _our_def_type, _our_return_type) \ -_our_return_type _our_name(_our_table_type table, size_t table_len, char const *name, _our_def_type def) \ -{ \ - _our_return_type ret; \ - _our_table_type found; \ - found = (_our_table_type)_func(table, table_len, sizeof(((_our_table_type)0)[0]), name); \ - if (!found) { \ - memcpy(&ret, &def, sizeof(ret)); \ - return ret; \ - } \ - memcpy(&ret, &found->value, sizeof(ret)); \ - return ret; \ -} - -/** Create type specific string to value functions with an input string length argument - * - * @param[in] _func used for searching. - * @param[in] _our_table_type that we'll be searching in. - * @param[in] _our_name The function that we'll be creating. - * @param[in] _our_def_type The type of the default value. - * @param[in] _our_return_type What we return. - */ -#define TABLE_TYPE_STR_LEN_FUNC(_func, _our_table_type, _our_name, _our_def_type, _our_return_type) \ -_our_return_type _our_name(_our_table_type table, size_t table_len, char const *name, ssize_t name_len, _our_def_type def) \ -{ \ - _our_return_type ret; \ - _our_table_type found; \ - found = (_our_table_type)_func(table, table_len, sizeof(((_our_table_type)0)[0]), name, name_len); \ - if (!found) { \ - memcpy(&ret, &def, sizeof(ret)); \ - return ret; \ - } \ - memcpy(&ret, &found->value, sizeof(ret)); \ - return ret; \ -} - -#define TABLE_TYPE_STR_MATCH_LEN_FUNC(_func, _our_table_type, _our_name, _our_def_type, _our_return_type) \ -_our_return_type _our_name(size_t *match_len, _our_table_type table, size_t table_len, char const *name, ssize_t name_len, _our_def_type def) \ -{ \ - _our_return_type ret; \ - _our_table_type found; \ - found = (_our_table_type)_func(match_len, table, table_len, sizeof(((_our_table_type)0)[0]), name, name_len); \ - if (!found) { \ - memcpy(&ret, &def, sizeof(ret)); \ - return ret; \ - } \ - memcpy(&ret, &found->value, sizeof(ret)); \ - return ret; \ -} - - -#define TABLE_TYPE_VALUE_FUNC(_our_table_type, _our_name, _our_value_type) \ -char const *_our_name(_our_table_type table, size_t table_len, _our_value_type value, char const *def) \ -{ \ - size_t i; \ - for (i = 0; i < table_len; i++) if (table[i].value == value) return table[i].name.str; \ - return def; \ -} - -#define TABLE_TYPE_VALUE_INDEX_BIT_FIELD_FUNC(_our_table_type, _our_name, _our_value_type) \ -char const *_our_name(_our_table_type table, size_t table_len, _our_value_type value, char const *def) \ -{ \ - uint8_t idx = fr_high_bit_pos(value); \ - if (idx >= table_len) return def; \ - return table[idx].name.str; \ -} - -#define TABLE_TYPE_VALUE_INDEX_FUNC(_our_table_type, _our_name, _our_value_type) \ -char const *_our_name(_our_table_type table, size_t table_len, _our_value_type value, char const *def) \ -{ \ - if (value >= table_len) return def; \ - return table[value].name.str; \ -} - -/** Convert a string to a value using a lexicographically sorted table - * - * @param[in] table to search in. - * @param[in] table_len The number of elements in the table. - * @param[in] element_size Size of elements in the table. - * @param[in] name to resolve to a value. - * @return - * - value of matching entry. - * - NULL if no matching entries. - */ -static void const *table_sorted_value_by_str(void const *table, size_t table_len, size_t element_size, - char const *name) -{ - ssize_t start = 0; - ssize_t end = table_len - 1; - ssize_t mid; - - int ret; - - if (!name) return NULL; - - while (start <= end) { - void const *offset; - - mid = start + ((end - start) / 2); /* Avoid overflow */ - - offset = TABLE_IDX(table, mid, element_size); - ret = strcasecmp(name, ELEM_STR(offset)); - if (ret == 0) return offset; - if (ret < 0) { - end = mid - 1; - } else { - start = mid + 1; - } - } - - return NULL; -} - -TABLE_TYPE_STR_FUNC(table_sorted_value_by_str, fr_table_num_sorted_t const *, +TABLE_TYPE_NAME_FUNC(table_sorted_value_by_str, fr_table_num_sorted_t const *, fr_table_sorted_num_by_str, int, int) -TABLE_TYPE_STR_FUNC(table_sorted_value_by_str, fr_table_ptr_sorted_t const *, +TABLE_TYPE_NAME_FUNC(table_sorted_value_by_str, fr_table_ptr_sorted_t const *, fr_table_sorted_ptr_by_str, void const *, void *) -/** Convert a string to a value using an arbitrarily ordered table - * - * @param[in] table to search in. - * @param[in] table_len The number of elements in the table. - * @param[in] element_size Size of elements in the table. - * @param[in] name to resolve to a number. - * @return - * - value of matching entry. - * - NULL if no matching entries. - */ -static void const *table_ordered_value_by_str(void const *table, size_t table_len, size_t element_size, - char const *name) -{ - size_t i; - - if (!name) return NULL; - - for (i = 0; i < table_len; i++) { - void const *offset = TABLE_IDX(table, i, element_size); - if (strcasecmp(name, ELEM_STR(offset)) == 0) return offset; - } - - return NULL; -} - -TABLE_TYPE_STR_FUNC(table_ordered_value_by_str, fr_table_num_ordered_t const *, +TABLE_TYPE_NAME_FUNC(table_ordered_value_by_str, fr_table_num_ordered_t const *, fr_table_ordered_num_by_str, int, int) -TABLE_TYPE_STR_FUNC(table_ordered_value_by_str, fr_table_ptr_ordered_t const *, +TABLE_TYPE_NAME_FUNC(table_ordered_value_by_str, fr_table_ptr_ordered_t const *, fr_table_ordered_ptr_by_str, void const *, void *) -/** Convert a string matching part of name to an integer using a lexicographically sorted table - * - * @param[in] table to search in. - * @param[in] table_len The number of elements in the table. - * @param[in] element_size Size of elements in the table. - * @param[in] name to locate. - * @param[in] name_len the maximum amount of name that should be matched. - * If < 0, the length of the name in the table offsetent - * will be used as the maximum match length. - * @return - * - value of matching entry. - * - NULL if no matching entries. - */ -static void const *table_sorted_value_by_substr(void const *table, size_t table_len, size_t element_size, - char const *name, ssize_t name_len) -{ - ssize_t start = 0; - ssize_t end = table_len - 1; - ssize_t mid; - - int ret; - - if (!name) return NULL; - - while (start <= end) { - void const *offset; - - mid = start + ((end - start) / 2); /* Avoid overflow */ - - offset = TABLE_IDX(table, mid, element_size); - - /* - * Match up to the length of the table entry if len is < 0. - */ - ret = strncasecmp(name, ELEM_STR(offset), - (name_len < 0) ? ELEM_LEN(offset) : (size_t)name_len); - if (ret == 0) return offset; - - if (ret < 0) { - end = mid - 1; - } else { - start = mid + 1; - } - } - - return NULL; -} - -TABLE_TYPE_STR_LEN_FUNC(table_sorted_value_by_substr, fr_table_num_sorted_t const *, +TABLE_TYPE_NAME_LEN_FUNC(table_sorted_value_by_substr, fr_table_num_sorted_t const *, fr_table_sorted_num_by_substr, int, int) -TABLE_TYPE_STR_LEN_FUNC(table_sorted_value_by_substr, fr_table_ptr_sorted_t const *, +TABLE_TYPE_NAME_LEN_FUNC(table_sorted_value_by_substr, fr_table_ptr_sorted_t const *, fr_table_sorted_ptr_by_substr, void const *, void *) -/** Convert a string matching part of name to an integer using an arbitrarily ordered table - * - * @param[in] table to search in. - * @param[in] table_len The number of elements in the table. - * @param[in] element_size Size of elements in the table. - * @param[in] name to locate. - * @param[in] name_len the maximum amount of name that should be matched. - * If < 0, the length of the name in the table offsetent - * will be used as the maximum match length. - * @return - * - value of matching entry. - * - NULL if no matching entries. - */ -static void const *table_ordered_value_by_substr(void const *table, size_t table_len, size_t element_size, - char const *name, ssize_t name_len) -{ - size_t i; - - if (!name) return NULL; - - for (i = 0; i < table_len; i++) { - void const *offset; - size_t tlen; - - offset = TABLE_IDX(table, i, element_size); - - tlen = ELEM_LEN(offset); - - /* - * Don't match "request" to user input "req". - */ - if ((name_len > 0) && (name_len < (int) tlen)) continue; - - /* - * Match up to the length of the table entry if len is < 0. - */ - if (strncasecmp(name, ELEM_STR(offset), - (name_len < 0) ? tlen : (size_t)name_len) == 0) return offset; - } - - return NULL; -} - -TABLE_TYPE_STR_LEN_FUNC(table_ordered_value_by_substr, fr_table_num_ordered_t const *, +TABLE_TYPE_NAME_LEN_FUNC(table_ordered_value_by_substr, fr_table_num_ordered_t const *, fr_table_ordered_num_by_substr, int, int) -TABLE_TYPE_STR_LEN_FUNC(table_ordered_value_by_substr, fr_table_ptr_ordered_t const *, - fr_table_ordered_ptr_by_substr, void const *, void *) - -/** Find the longest string match using a lexicographically sorted table - * - * Performs a binary search in the specified table, returning the longest - * offsetent which is a prefix of name. - * - * i.e. given name of "food", and table of f, foo, of - foo would be returned. - * - * @note The table *MUST* be sorted lexicographically, else the result may be incorrect. - * - * @param[out] match_len How much of the input string matched. - * @param[in] table to search in. - * @param[in] table_len The number of elements in the table. - * @param[in] element_size Size of elements in the table. - * @param[in] name to locate. - * @param[in] name_len the maximum amount of name that should be matched. - * @return - * - num value of matching entry. - * - NULL if no matching entries. - */ -static void const *table_sorted_value_by_longest_prefix(size_t *match_len, - void const *table, size_t table_len, size_t element_size, - char const *name, ssize_t name_len) -{ - ssize_t start = 0; - ssize_t end = table_len - 1; - ssize_t mid; - - int ret; - void const *found = NULL; - - if (!name) return NULL; - if (name_len < 0) name_len = strlen(name); +TABLE_TYPE_NAME_LEN_FUNC(table_ordered_value_by_substr, fr_table_ptr_ordered_t const *, + fr_table_ordered_ptr_by_substr, void const *, void *) - while (start <= end) { - void const *offset; - char const *elem; - size_t tlen; - - mid = start + ((end - start) / 2); /* Avoid overflow */ - - offset = TABLE_IDX(table, mid, element_size); - elem = ELEM_STR(offset); - tlen = ELEM_LEN(offset); - - ret = strncasecmp(name, elem, tlen < (size_t)name_len ? tlen : (size_t)name_len); - if (ret == 0) { - /* - * Exact match - */ - if (tlen == (size_t)name_len) { - if (match_len) *match_len = tlen; - return offset; - } - - /* - * Partial match. - * Name we're searching for is longer. - * This might be the longest prefix, - * so record it. - */ - if (tlen < (size_t)name_len) { - found = offset; - if (match_len) *match_len = tlen; - ret = 1; - } else { - ret = -1; - } - } - - if (ret < 0) { - end = mid - 1; - } else { - start = mid + 1; - } - } - - if (!found && match_len) *match_len = 0; - - return found; -} - -TABLE_TYPE_STR_MATCH_LEN_FUNC(table_sorted_value_by_longest_prefix, fr_table_num_sorted_t const *, +TABLE_TYPE_NAME_MATCH_LEN_FUNC(table_sorted_value_by_longest_prefix, fr_table_num_sorted_t const *, fr_table_sorted_num_by_longest_prefix, int, int) -TABLE_TYPE_STR_MATCH_LEN_FUNC(table_sorted_value_by_longest_prefix, fr_table_ptr_sorted_t const *, +TABLE_TYPE_NAME_MATCH_LEN_FUNC(table_sorted_value_by_longest_prefix, fr_table_ptr_sorted_t const *, fr_table_sorted_ptr_by_longest_prefix, void const *, void *) -/** Find the longest string match using an arbitrarily ordered table - * - * i.e. given name of "food", and table of f, foo, of - foo would be returned. - * - * @param[out] match_len How much of the input string matched. - * @param[in] table to search in. - * @param[in] table_len The number of elements in the table. - * @param[in] element_size Size of elements in the table. - * @param[in] name to locate. - * @param[in] name_len the maximum amount of name that should be matched. - * @return - * - num value of matching entry. - * - def if no matching entries. - */ -static void const *table_ordered_value_by_longest_prefix(size_t *match_len, - void const *table, size_t table_len, size_t element_size, - char const *name, ssize_t name_len) -{ - size_t i; - size_t found_len = 0; - void const *found = NULL; - - if (!name) return NULL; - if (name_len < 0) name_len = strlen(name); - - for (i = 0; i < table_len; i++) { - void const *offset; - size_t j; - - offset = TABLE_IDX(table, i, element_size); - - for (j = 0; (j < (size_t)name_len) && (j < ELEM_LEN(offset)) && - (tolower(name[j]) == tolower((ELEM_STR(offset))[j])); j++); - - /* - * If we didn't get to the end of the - * table string, then continue. - */ - if ((ELEM_STR(offset))[j] != '\0') continue; - - /* - * Exact match - */ - if (j == (size_t)name_len) { - if (match_len) *match_len = name_len; - return offset; - } - - /* - * Partial match. - * Name we're searching for is longer. - * This might be the longest prefix, - * so record it. - */ - if (j > found_len) { - found_len = j; - found = offset; - } - } - - if (match_len) *match_len = found_len; - - return found; -} - -TABLE_TYPE_STR_MATCH_LEN_FUNC(table_ordered_value_by_longest_prefix, fr_table_num_ordered_t const *, +TABLE_TYPE_NAME_MATCH_LEN_FUNC(table_ordered_value_by_longest_prefix, fr_table_num_ordered_t const *, fr_table_ordered_num_by_longest_prefix, int, int) -TABLE_TYPE_STR_MATCH_LEN_FUNC(table_ordered_value_by_longest_prefix, fr_table_ptr_ordered_t const *, +TABLE_TYPE_NAME_MATCH_LEN_FUNC(table_ordered_value_by_longest_prefix, fr_table_ptr_ordered_t const *, fr_table_ordered_ptr_by_longest_prefix, void const *, void *) /* diff --git a/src/lib/util/table.h b/src/lib/util/table.h index 1ccb12f6bd5..9c559bd5a31 100644 --- a/src/lib/util/table.h +++ b/src/lib/util/table.h @@ -30,20 +30,24 @@ RCSIDH(table_h, "$Id$") extern "C" { #endif +#include +#include #include #include #include +DIAG_OFF(unused-function) + typedef struct { char const *str; //!< Literal string. size_t len; //!< Literal string length. -} fr_table_elem_t; +} fr_table_elem_name_t; /** An element in a lexicographically sorted array of name to num mappings * */ typedef struct { - fr_table_elem_t name; + fr_table_elem_name_t name; int value; } fr_table_num_sorted_t; @@ -51,7 +55,7 @@ typedef struct { * */ typedef struct { - fr_table_elem_t name; + fr_table_elem_name_t name; int value; } fr_table_num_ordered_t; @@ -59,7 +63,7 @@ typedef struct { * */ typedef struct { - fr_table_elem_t name; + fr_table_elem_name_t name; void const *value; } fr_table_ptr_sorted_t; @@ -67,7 +71,7 @@ typedef struct { * */ typedef struct { - fr_table_elem_t name; + fr_table_elem_name_t name; void const *value; } fr_table_ptr_ordered_t; @@ -77,7 +81,7 @@ typedef struct { * will be returned. */ typedef struct { - fr_table_elem_t name; + fr_table_elem_name_t name; uint64_t value; } fr_table_num_indexed_bit_pos_t; @@ -86,7 +90,7 @@ typedef struct { * i.e. if the value is 0, we return the string mapped to the first element of the table. */ typedef struct { - fr_table_elem_t name; + fr_table_elem_name_t name; unsigned int value; } fr_table_num_indexed_t; @@ -97,6 +101,524 @@ typedef struct { char const *_fr_table_ptr_by_str_value(fr_table_ptr_sorted_t const *table, size_t table_len, char const *str_val, char const *def); +#define TABLE_IDX(_table, _idx, _element_size) (((uint8_t const *)(_table)) + ((_idx) * (_element_size))) +#define ELEM_STR(_offset) (*((fr_table_elem_name_t const *)(_offset))).str +#define ELEM_LEN(_offset) (*((fr_table_elem_name_t const *)(_offset))).len + +/** Create a type-specific name-to-value function + * + * @param[in] _func Used for resolving the name portion of an array element to a value. + * Should be one of the following: + * - table_sorted_value_by_str - for lexicographically sorted tables. + * - table_ordered_value_by_str - for arbitrarily ordered tables. + * @param[in] _our_table_type C type of the table elements. + * @param[in] _our_name name of the search function to define. + * @param[in] _our_def_type C type of the default value. + * @param[in] _our_return_type C type of the return value, i.e. the value part of the element. + */ +#define TABLE_TYPE_NAME_FUNC(_func, _our_table_type, _our_name, _our_def_type, _our_return_type) \ +_our_return_type _our_name(_our_table_type table, size_t table_len, char const *name, _our_def_type def) \ +{ \ + _our_return_type ret; \ + _our_table_type found; \ + found = (_our_table_type)_func(table, table_len, sizeof(((_our_table_type)0)[0]), name); \ + if (!found) { \ + memcpy(&ret, &def, sizeof(ret)); \ + return ret; \ + } \ + memcpy(&ret, &found->value, sizeof(ret)); \ + return ret; \ +} + +/** Create a type-specific name-to-value function + * + * @param[in] _func Used for resolving the name portion of an array element to a value. + * Should be one of the following: + * - table_sorted_value_by_str - for lexicographically sorted tables. + * - table_ordered_value_by_str - for arbitrarily ordered tables. + * @param[in] _our_table_type C type of the table elements. + * @param[in] _our_name name of the search function to define. + * @param[in] _our_def_type C type of the default value. + * @param[in] _our_out_type C type of the return/output value, i.e. the value part of the element. + */ +#define TABLE_TYPE_NAME_FUNC_RPTR(_func, _our_table_type, _our_name, _our_def_type, _our_return_type) \ +_our_return_type _our_name(__our_out_type **out, our_table_type table, size_t table_len, char const *name, _our_def_type *def) \ +{ \ + _our_return_type ret; \ + _our_table_type found; \ + found = (_our_table_type)_func(table, table_len, sizeof(((_our_table_type)0)[0]), name); \ + if (!found) { \ + *out = def; \ + return false; \ + } \ + *out = &found->value; \ + return true; \ +} + +/** Create a type-specific name-to-value function that can perform substring matching with a 'name_len' argument + * + * @param[in] _func Used for resolving the name portion of an array element to a value. + * Should be one of the following: + * - table_sorted_value_by_substr - for lexicographically sorted tables + * with partial matching. + * - table_ordered_value_by_substr - for arbitrarily ordered tables. + * with partial matching. + * @param[in] _our_table_type C type of the table elements. + * Must contain two fields, an #fr_table_elem_name_t called name + * and an arbitraryily typed field called value. + * A pointer to thi + * @param[in] _our_name name of the search function to define. + * @param[in] _our_def_type C type of the default value. + * @param[in] _our_return_type C type of the return value, i.e. the value part of the element. + */ +#define TABLE_TYPE_NAME_LEN_FUNC(_func, _our_table_type, _our_name, _our_def_type, _our_return_type) \ +_our_return_type _our_name(_our_table_type table, size_t table_len, char const *name, ssize_t name_len, _our_def_type def) \ +{ \ + _our_return_type ret; \ + _our_table_type found; \ + found = (_our_table_type)_func(table, table_len, sizeof(((_our_table_type)0)[0]), name, name_len); \ + if (!found) { \ + memcpy(&ret, &def, sizeof(ret)); \ + return ret; \ + } \ + memcpy(&ret, &found->value, sizeof(ret)); \ + return ret; \ +} + +/** Create a type-specific name-to-value function that can perform substring matching with a 'name_len' argument + * + * @note The functions created by this macro return true on a match, false on no match, and write a + * pointer to the value field of the table element to the 'out' argument. + * + * @param[in] _func Used for resolving the name portion of an array element to a value. + * Should be one of the following: + * - table_sorted_value_by_substr - for lexicographically sorted tables + * with partial matching. + * - table_ordered_value_by_substr - for arbitrarily ordered tables. + * with partial matching. + * @param[in] _our_table_type C type of the table elements. + * Must contain two fields, an #fr_table_elem_name_t called name + * and an arbitraryily typed field called value. + * A pointer to thi + * @param[in] _our_name name of the search function to define. + * @param[in] _our_def_type C type of the default value. + * @param[in] _our_out_type C type of the return/output value, i.e. the value part of the element. + */ +#define TABLE_TYPE_NAME_LEN_FUNC_RPTR(_func, _our_table_type, _our_name, _our_def_type, _our_return_type) \ +bool _our_name(_our_out_type **out, _our_table_type table, size_t table_len, char const *name, ssize_t name_len, _our_def_type *def) \ +{ \ + _our_return_type ret; \ + _our_table_type found; \ + found = (_our_table_type)_func(table, table_len, sizeof(((_our_table_type)0)[0]), name, name_len); \ + if (!found) { \ + *out = def; \ + return false; \ + } \ + *out = &found->value; \ + return true; \ +} + +/** Create a type-specific name-to-value function that can perform substring matching with a 'name_len' argument, and passes back the length of the matched string + * + * @note The functions created by this macro return the value field of the table element. + * + * @param[in] _func Used for resolving the name portion of an array element to a value. + * Should be one of the following: + * - table_sorted_value_by_longest_prefix - for lexicographically sorted tables + * with longest prefix match. + * - table_ordered_value_by_longest_prefix - for arbitrarily ordered tables + * with longest prefix match. + * @param[in] _our_table_type C type of the table elements. + * @param[in] _our_name name of the search function to define. + * @param[in] _our_def_type C type of the default value. + * @param[in] _our_return_type C type of the return value, i.e. the value part of the element. + */ +#define TABLE_TYPE_NAME_MATCH_LEN_FUNC(_func, _our_table_type, _our_name, _our_def_type, _our_return_type) \ +_our_return_type _our_name(size_t *match_len, _our_table_type table, size_t table_len, char const *name, ssize_t name_len, _our_def_type def) \ +{ \ + _our_return_type ret; \ + _our_table_type found; \ + found = (_our_table_type)_func(match_len, table, table_len, sizeof(((_our_table_type)0)[0]), name, name_len); \ + if (!found) { \ + memcpy(&ret, &def, sizeof(ret)); \ + return ret; \ + } \ + memcpy(&ret, &found->value, sizeof(ret)); \ + return ret; \ +} + +/** Create a type-specific name-to-value function that can perform substring matching with a 'name_len' argument, and passes back the length of the matched string + * + * @note The functions created by this macro return true on a match, false on no match, and write a + * pointer to the value field of the table element to the 'out' argument. + * + * @param[in] _func Used for resolving the name portion of an array element to a value. + * Should be one of the following: + * - table_sorted_value_by_longest_prefix - for lexicographically sorted tables + * with longest prefix match. + * - table_ordered_value_by_longest_prefix - for arbitrarily ordered tables + * with longest prefix match. + * @param[in] _our_table_type C type of the table elements. + * @param[in] _our_name name of the search function to define. + * @param[in] _our_def_type C type of the default value. + * @param[in] _our_out_type C type of the return/output value, i.e. the value part of the element. + */ +#define TABLE_TYPE_NAME_MATCH_LEN_FUNC_RPTR(_func, _our_table_type, _our_name, _our_def_type, _our_return_type) \ +bool _our_name(size_t *match_len, _our_out_type **out, _our_table_type table, size_t table_len, char const *name, ssize_t name_len, _our_def_type *def) \ +{ \ + _our_return_type ret; \ + _our_table_type found; \ + found = (_our_table_type)_func(match_len, table, table_len, sizeof(((_our_table_type)0)[0]), name, name_len); \ + if (!found) { \ + *out = def; \ + return false; \ + } \ + *out = &found->value; \ + return true; \ +} + +/** Create a type-specific value-to-name function + * + * @param[in] _our_table_type C type of the table elements. + * @param[in] _our_name name of the search function to define. + * @param[in] _our_return_type C type of the value field of the table element. + */ +#define TABLE_TYPE_VALUE_FUNC(_our_table_type, _our_name, _our_value_type) \ +char const *_our_name(_our_table_type table, size_t table_len, _our_value_type value, char const *def) \ +{ \ + size_t i; \ + for (i = 0; i < table_len; i++) if (table[i].value == value) return table[i].name.str; \ + return def; \ +} + +/** Create a type-specific value-to-name function, which uses the highest bit set in the value as an index into the table + * + * @param[in] _our_table_type C type of the table elements. + * @param[in] _our_name name of the search function to define. + * @param[in] _our_return_type C type of the value field of the table element. + */ +#define TABLE_TYPE_VALUE_INDEX_BIT_FIELD_FUNC(_our_table_type, _our_name, _our_value_type) \ +char const *_our_name(_our_table_type table, size_t table_len, _our_value_type value, char const *def) \ +{ \ + uint8_t idx = fr_high_bit_pos(value); \ + if (idx >= table_len) return def; \ + return table[idx].name.str; \ +} + +/** Create a type-specific value-to-name function, which uses the value as an index into the table + * + * @param[in] _our_table_type C type of the table elements. + * @param[in] _our_name name of the search function to define. + * @param[in] _our_return_type C type of the value field of the table element. + */ +#define TABLE_TYPE_VALUE_INDEX_FUNC(_our_table_type, _our_name, _our_value_type) \ +char const *_our_name(_our_table_type table, size_t table_len, _our_value_type value, char const *def) \ +{ \ + if (value >= table_len) return def; \ + return table[value].name.str; \ +} + +/** Convert a string to a value using a lexicographically sorted table + * + * @param[in] table to search in. + * @param[in] table_len The number of elements in the table. + * @param[in] element_size Size of elements in the table. + * @param[in] name to resolve to a value. + * @return + * - value of matching entry. + * - NULL if no matching entries. + */ +static void const *table_sorted_value_by_str(void const *table, size_t table_len, size_t element_size, + char const *name) +{ + ssize_t start = 0; + ssize_t end = table_len - 1; + ssize_t mid; + + int ret; + + if (!name) return NULL; + + while (start <= end) { + void const *offset; + + mid = start + ((end - start) / 2); /* Avoid overflow */ + + offset = TABLE_IDX(table, mid, element_size); + ret = strcasecmp(name, ELEM_STR(offset)); + if (ret == 0) return offset; + if (ret < 0) { + end = mid - 1; + } else { + start = mid + 1; + } + } + + return NULL; +} + +/** Convert a string to a value using an arbitrarily ordered table + * + * @param[in] table to search in. + * @param[in] table_len The number of elements in the table. + * @param[in] element_size Size of elements in the table. + * @param[in] name to resolve to a number. + * @return + * - value of matching entry. + * - NULL if no matching entries. + */ +static void const *table_ordered_value_by_str(void const *table, size_t table_len, size_t element_size, + char const *name) +{ + size_t i; + + if (!name) return NULL; + + for (i = 0; i < table_len; i++) { + void const *offset = TABLE_IDX(table, i, element_size); + if (strcasecmp(name, ELEM_STR(offset)) == 0) return offset; + } + + return NULL; +} + +/** Convert a string matching part of name to an integer using a lexicographically sorted table + * + * @param[in] table to search in. + * @param[in] table_len The number of elements in the table. + * @param[in] element_size Size of elements in the table. + * @param[in] name to locate. + * @param[in] name_len the maximum amount of name that should be matched. + * If < 0, the length of the name in the table offsetent + * will be used as the maximum match length. + * @return + * - value of matching entry. + * - NULL if no matching entries. + */ +static void const *table_sorted_value_by_substr(void const *table, size_t table_len, size_t element_size, + char const *name, ssize_t name_len) +{ + ssize_t start = 0; + ssize_t end = table_len - 1; + ssize_t mid; + + int ret; + + if (!name) return NULL; + + while (start <= end) { + void const *offset; + + mid = start + ((end - start) / 2); /* Avoid overflow */ + + offset = TABLE_IDX(table, mid, element_size); + + /* + * Match up to the length of the table entry if len is < 0. + */ + ret = strncasecmp(name, ELEM_STR(offset), + (name_len < 0) ? ELEM_LEN(offset) : (size_t)name_len); + if (ret == 0) return offset; + + if (ret < 0) { + end = mid - 1; + } else { + start = mid + 1; + } + } + + return NULL; +} + +/** Convert a string matching part of name to an integer using an arbitrarily ordered table + * + * @param[in] table to search in. + * @param[in] table_len The number of elements in the table. + * @param[in] element_size Size of elements in the table. + * @param[in] name to locate. + * @param[in] name_len the maximum amount of name that should be matched. + * If < 0, the length of the name in the table offsetent + * will be used as the maximum match length. + * @return + * - value of matching entry. + * - NULL if no matching entries. + */ +static void const *table_ordered_value_by_substr(void const *table, size_t table_len, size_t element_size, + char const *name, ssize_t name_len) +{ + size_t i; + + if (!name) return NULL; + + for (i = 0; i < table_len; i++) { + void const *offset; + size_t tlen; + + offset = TABLE_IDX(table, i, element_size); + + tlen = ELEM_LEN(offset); + + /* + * Don't match "request" to user input "req". + */ + if ((name_len > 0) && (name_len < (int) tlen)) continue; + + /* + * Match up to the length of the table entry if len is < 0. + */ + if (strncasecmp(name, ELEM_STR(offset), + (name_len < 0) ? tlen : (size_t)name_len) == 0) return offset; + } + + return NULL; +} + +/** Find the longest string match using a lexicographically sorted table + * + * Performs a binary search in the specified table, returning the longest + * offsetent which is a prefix of name. + * + * i.e. given name of "food", and table of f, foo, of - foo would be returned. + * + * @note The table *MUST* be sorted lexicographically, else the result may be incorrect. + * + * @param[out] match_len How much of the input string matched. + * @param[in] table to search in. + * @param[in] table_len The number of elements in the table. + * @param[in] element_size Size of elements in the table. + * @param[in] name to locate. + * @param[in] name_len the maximum amount of name that should be matched. + * @return + * - num value of matching entry. + * - NULL if no matching entries. + */ +static void const *table_sorted_value_by_longest_prefix(size_t *match_len, + void const *table, size_t table_len, size_t element_size, + char const *name, ssize_t name_len) +{ + ssize_t start = 0; + ssize_t end = table_len - 1; + ssize_t mid; + + int ret; + void const *found = NULL; + + if (!name) return NULL; + if (name_len < 0) name_len = strlen(name); + + while (start <= end) { + void const *offset; + char const *elem; + size_t tlen; + + mid = start + ((end - start) / 2); /* Avoid overflow */ + + offset = TABLE_IDX(table, mid, element_size); + elem = ELEM_STR(offset); + tlen = ELEM_LEN(offset); + + ret = strncasecmp(name, elem, tlen < (size_t)name_len ? tlen : (size_t)name_len); + if (ret == 0) { + /* + * Exact match + */ + if (tlen == (size_t)name_len) { + if (match_len) *match_len = tlen; + return offset; + } + + /* + * Partial match. + * Name we're searching for is longer. + * This might be the longest prefix, + * so record it. + */ + if (tlen < (size_t)name_len) { + found = offset; + if (match_len) *match_len = tlen; + ret = 1; + } else { + ret = -1; + } + } + + if (ret < 0) { + end = mid - 1; + } else { + start = mid + 1; + } + } + + if (!found && match_len) *match_len = 0; + + return found; +} + +/** Find the longest string match using an arbitrarily ordered table + * + * i.e. given name of "food", and table of f, foo, of - foo would be returned. + * + * @param[out] match_len How much of the input string matched. + * @param[in] table to search in. + * @param[in] table_len The number of elements in the table. + * @param[in] element_size Size of elements in the table. + * @param[in] name to locate. + * @param[in] name_len the maximum amount of name that should be matched. + * @return + * - num value of matching entry. + * - def if no matching entries. + */ +static void const *table_ordered_value_by_longest_prefix(size_t *match_len, + void const *table, size_t table_len, size_t element_size, + char const *name, ssize_t name_len) +{ + size_t i; + size_t found_len = 0; + void const *found = NULL; + + if (!name) return NULL; + if (name_len < 0) name_len = strlen(name); + + for (i = 0; i < table_len; i++) { + void const *offset; + size_t j; + + offset = TABLE_IDX(table, i, element_size); + + for (j = 0; (j < (size_t)name_len) && (j < ELEM_LEN(offset)) && + (tolower(name[j]) == tolower((ELEM_STR(offset))[j])); j++); + + /* + * If we didn't get to the end of the + * table string, then continue. + */ + if ((ELEM_STR(offset))[j] != '\0') continue; + + /* + * Exact match + */ + if (j == (size_t)name_len) { + if (match_len) *match_len = name_len; + return offset; + } + + /* + * Partial match. + * Name we're searching for is longer. + * This might be the longest prefix, + * so record it. + */ + if (j > found_len) { + found_len = j; + found = offset; + } + } + + if (match_len) *match_len = found_len; + + return found; +} + /** Brute force search a sorted or ordered ptr table, assuming the pointers are strings * * @param[in] _table to search in.