/** * mds — A micro-display server * Copyright © 2014, 2015, 2016, 2017 Mattias Andrée (maandree@kth.se) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef MDS_LIBMDSCLIENT_PROTO_UTIL_H #define MDS_LIBMDSCLIENT_PROTO_UTIL_H #include #include #include /** * Optimisations `libmds_headers_cherrypick` MAY use */ typedef enum libmds_cherrypick_optimisation { /** * No optimisation is allowed, in particular this * means that the array of headers may not be reordered. * The function may still create a copy of the header * array and sort the copy. * * Cannot be combined with `SORT` or `SORTED` * * This option is guaranteed to always have the value 0 */ DO_NOT_SORT = 0, /** * `libmds_headers_cherrypick` is allowed to * sort the header array. There is no guarantee * that the header array will be sorted. * * Cannot be combined with `DO_NOT_SORT` or `SORTED` */ SORT = 1, /** * Informs `libmds_headers_cherrypick` that the header * array already is sorted. `libmds_headers_cherrypick` * can use this information to optimise its procedure. * But this also means that the header array will not * be reordered. * * Cannot be combined with `DO_NOT_SORT` or `SORT` */ SORTED = 2, /** * The list of requested headers is not sorted * in ascending order * * Cannot be combined with `ARGS_SORTED` * * This option is guaranteed to always have the value 0 */ ARGS_UNSORTED = 0, /** * The list of requested headers is sorted in * ascending order * * Cannot be combined with `ARGS_UNSORTED` */ ARGS_SORTED = 4, } libmds_cherrypick_optimisation_t; /** * Cherrypick headers from a message * * @param headers The headers in the message * @param header_count The number of headers * @param found Output parameter for the number of found headers of those that * were requested. * @param optimisation Optimisations the function may use, only one value please * @param ... The first argument should be the name of a header, it should * have the type `const char*`. The second argument should be * a pointer to the location where the header named by the first * argument in the argument list names. It should have the type * `char**`. If the header is found, its value will be stored, * and it will be a NUL-terminated string. If the header is not * found, `NULL` will be stored. The next two arguments is * interpreted analogously to the first two arguments, and the * following two in the same way, and so on. When there are no * more headers in the list, it should be terminated with a `NULL`. * @return Zero on success, -1 on error, `errno` will have been set * accordingly on error. * * @throws ENOMEM Out of memory. Possibly, the process hit the RLIMIT_AS or * RLIMIT_DATA limit described in getrlimit(2). */ __attribute__((sentinel)) int libmds_headers_cherrypick(char **restrict headers, size_t header_count, size_t *restrict found, libmds_cherrypick_optimisation_t optimisation, ...); /** * Cherrypick headers from a message, * linear search will be used without optimisations * * @param headers The headers in the message * @param header_count The number of headers * @param ... The first argument should be the name of a header, it should * have the type `const char*`. The second argument should be * a pointer to the location where the header named by the first * argument in the argument list names. It should have the type * `char**`. If the header is found, its value will be stored, * and it will be a NUL-terminated string. If the header is not * found, `NULL` will be stored. The next two arguments is * interpreted analogously to the first two arguments, and the * following two in the same way, and so on. When there are no * more headers in the list, it should be terminated with a `NULL`. * @return The number of found headers of those that were requested */ __attribute__((sentinel)) size_t libmds_headers_cherrypick_linear_unsorted(char **restrict headers, size_t header_count, ...); #define libmds_headers_cherrypick_linear_unsorted libmds_headers_cherrypick_linear_unsorted /** * Cherrypick headers from a message, * linear search will be used with optimisation based * on that the array is sorted as well as the list of * requested headers (`...`) * * @param headers The headers in the message * @param header_count The number of headers * @param ... The first argument should be the name of a header, it should * have the type `const char*`. The second argument should be * a pointer to the location where the header named by the first * argument in the argument list names. It should have the type * `char**`. If the header is found, its value will be stored, * and it will be a NUL-terminated string. If the header is not * found, `NULL` will be stored. The next two arguments is * interpreted analogously to the first two arguments, and the * following two in the same way, and so on. When there are no * more headers in the list, it should be terminated with a `NULL`. * @return The number of found headers of those that were requested */ __attribute__((sentinel)) size_t libmds_headers_cherrypick_linear_sorted(char **restrict headers, size_t header_count, ...); #define libmds_headers_cherrypick_linear_sorted libmds_headers_cherrypick_linear_sorted /** * Cherrypick headers from a message, * binary search will be used * * @param headers The headers in the message * @param header_count The number of headers * @param ... The first argument should be the name of a header, it should * have the type `const char*`. The second argument should be * a pointer to the location where the header named by the first * argument in the argument list names. It should have the type * `char**`. If the header is found, its value will be stored, * and it will be a NUL-terminated string. If the header is not * found, `NULL` will be stored. The next two arguments is * interpreted analogously to the first two arguments, and the * following two in the same way, and so on. When there are no * more headers in the list, it should be terminated with a `NULL`. * @return The number of found headers of those that were requested */ __attribute__((sentinel)) size_t libmds_headers_cherrypick_binary_unsorted(char **restrict headers, size_t header_count, ...); #define libmds_headers_cherrypick_binary_unsorted libmds_headers_cherrypick_binary_unsorted /** * Cherrypick headers from a message, * binary search will be used with optimisation based on * that list of requested headers (`...`) is sorted * * @param headers The headers in the message * @param header_count The number of headers * @param ... The first argument should be the name of a header, it should * have the type `const char*`. The second argument should be * a pointer to the location where the header named by the first * argument in the argument list names. It should have the type * `char**`. If the header is found, its value will be stored, * and it will be a NUL-terminated string. If the header is not * found, `NULL` will be stored. The next two arguments is * interpreted analogously to the first two arguments, and the * following two in the same way, and so on. When there are no * more headers in the list, it should be terminated with a `NULL`. * @return The number of found headers of those that were requested */ __attribute__((sentinel)) size_t libmds_headers_cherrypick_binary_sorted(char **restrict headers, size_t header_count, ...); #define libmds_headers_cherrypick_binary_unsorted libmds_headers_cherrypick_binary_unsorted /** * Cherrypick headers from a message * * @param headers The headers in the message * @param header_count The number of headers * @param found Output parameter for the number of found headers of those that * were requested. `NULL` is permitted. * @param optimisation Optimisations the function may use * @param args The first argument should be the name of a header, it should * have the type `const char*`. The second argument should be * a pointer to the location where the header named by the first * argument in the argument list names. It should have the type * `char**`. If the header is found, its value will be stored, * and it will be a NUL-terminated string. If the header is not * found, `NULL` will be stored. The next two arguments is * interpreted analogously to the first two arguments, and the * following two in the same way, and so on. When there are no * more headers in the list, it should be terminated with a `NULL`. * @return Zero on success, -1 on error, `errno` will have been set * accordingly on error. * * @throws ENOMEM Out of memory. Possibly, the process hit the RLIMIT_AS or * RLIMIT_DATA limit described in getrlimit(2). */ int libmds_headers_cherrypick_v(char **restrict headers, size_t header_count, size_t *restrict found, libmds_cherrypick_optimisation_t optimisation, va_list args); /** * Cherrypick headers from a message, * linear search will be used without optimisation * * @param headers The headers in the message * @param header_count The number of headers * @param args The first argument should be the name of a header, it should * have the type `const char*`. The second argument should be * a pointer to the location where the header named by the first * argument in the argument list names. It should have the type * `char**`. If the header is found, its value will be stored, * and it will be a NUL-terminated string. If the header is not * found, `NULL` will be stored. The next two arguments is * interpreted analogously to the first two arguments, and the * following two in the same way, and so on. When there are no * more headers in the list, it should be terminated with a `NULL`. * @return The number of found headers of those that were requested */ size_t libmds_headers_cherrypick_linear_unsorted_v(char **restrict headers, size_t header_count, va_list args); #define libmds_headers_cherrypick_linear_unsorted_v libmds_headers_cherrypick_linear_unsorted_v /** * Cherrypick headers from a message, * linear search will be used with optimisation based * on that the array is sorted as well as the list of * requested headers (`args`) * * @param headers The headers in the message * @param header_count The number of headers * @param args The first argument should be the name of a header, it should * have the type `const char*`. The second argument should be * a pointer to the location where the header named by the first * argument in the argument list names. It should have the type * `char**`. If the header is found, its value will be stored, * and it will be a NUL-terminated string. If the header is not * found, `NULL` will be stored. The next two arguments is * interpreted analogously to the first two arguments, and the * following two in the same way, and so on. When there are no * more headers in the list, it should be terminated with a `NULL`. * @return The number of found headers of those that were requested */ size_t libmds_headers_cherrypick_linear_sorted_v(char **restrict headers, size_t header_count, va_list args); #define libmds_headers_cherrypick_linear_sorted_v libmds_headers_cherrypick_linear_sorted_v /** * Cherrypick headers from a message, * binary search will be used * * @param headers The headers in the message * @param header_count The number of headers * @param args The first argument should be the name of a header, it should * have the type `const char*`. The second argument should be * a pointer to the location where the header named by the first * argument in the argument list names. It should have the type * `char**`. If the header is found, its value will be stored, * and it will be a NUL-terminated string. If the header is not * found, `NULL` will be stored. The next two arguments is * interpreted analogously to the first two arguments, and the * following two in the same way, and so on. When there are no * more headers in the list, it should be terminated with a `NULL`. * @return The number of found headers of those that were requested */ size_t libmds_headers_cherrypick_binary_unsorted_v(char **restrict headers, size_t header_count, va_list args); #define libmds_headers_cherrypick_binary_unsorted_v libmds_headers_cherrypick_binary_unsorted_v /** * Cherrypick headers from a message, * binary search will be used with optimisation based on * that list of requested headers (`args`) is sorted * * @param headers The headers in the message * @param header_count The number of headers * @param args The first argument should be the name of a header, it should * have the type `const char*`. The second argument should be * a pointer to the location where the header named by the first * argument in the argument list names. It should have the type * `char**`. If the header is found, its value will be stored, * and it will be a NUL-terminated string. If the header is not * found, `NULL` will be stored. The next two arguments is * interpreted analogously to the first two arguments, and the * following two in the same way, and so on. When there are no * more headers in the list, it should be terminated with a `NULL`. * @return The number of found headers of those that were requested */ size_t libmds_headers_cherrypick_binary_sorted_v(char **restrict headers, size_t header_count, va_list args); #define libmds_headers_cherrypick_binary_sorted_v libmds_headers_cherrypick_binary_sorted_v /** * Sort the a header array, this is what `libmds_headers_cherrypick` * uses to optimise its procedure. * * @param headers The array of headers * @param headr_count The number of elements in `headers` */ void libmds_headers_sort(char **restrict headers, size_t header_count); /** * Compose a message * * @param buffer Pointer to the buffer where the message should be written, * may point to `NULL` if `buffer_size` points to zero, but the * pointer itself must not be `NULL`. The buffer may be reallocated. * Will be updated with the new buffer it is allocated. * The buffer the pointer points to will be filled with the * message, however there is no guarantee it will be NUL-terminated. * @param buffer_size The allocation size of, in `char` `*buffer`, if and only if `buffer` * points to `NULL`, this point should point to zero, and vice versa. * Must not be `NULL`. Will be update with the new allocation size. * It is guaranteed that there will be remove to NUL-terminate * the message even if it was already NUL-terminate (terminate it * doubly in that case.) * @param length Output parameter for the length of the constructed message. * Must not be `NULL`. * @param payload The payload that should be added to the message, it should end * with a LF. `NULL` if the message should not have a payload. * @param payload_length Pointer to the length of the payload. If `NULL`, `payload` * must be NUL-terminated, and if it is `NULL` this NUL-termination * will be used to find the length of the payload. This variable is * not used if `payload` is `NULL`. * @param ... The first argument should be a line describing the first header, * it should be a printf(3)-formatted line that fully describes the * header line, that is, the header name, colon, blank space and then * the header's value. No LF should be included. The following * arguments should be the argument to format the header line. * If a format for a line begins with a question mark, the remainder * of the line used, but only if the next argument is non-zero * (it should be of type `int`,) that argument will not be used * for the formatting. This may be be iterated any number of this. * The last argument should be `NULL` to specify that there are no * more headers. The `Length`-header should not be included, it is * added automatically. A header may not have a length larger than * 2¹⁵, otherwise the behaviour of this function is undefined. * @return Zero on success, -1 on error, `errno` will have been set * accordingly on error. * * @throws ENOMEM Out of memory. Possibly, the process hit the RLIMIT_AS or * RLIMIT_DATA limit described in getrlimit(2). */ __attribute__((nonnull(1, 2, 3), sentinel)) int libmds_compose(char **restrict buffer, size_t *restrict buffer_size, size_t *restrict length, const char *restrict payload, const size_t *restrict payload_length, ...); /** * Compose a message * * @param buffer Pointer to the buffer where the message should be written, * may point to `NULL` if `buffer_size` points to zero, but the * pointer itself must not be `NULL`. The buffer may be reallocated. * Will be updated with the new buffer it is allocated. * The buffer the pointer points to will be filled with the * message, however there is no guarantee it will be NUL-terminated. * @param buffer_size The allocation size, in `char` of `*buffer`, if and only if `buffer` * points to `NULL`, this point should point to zero, and vice versa. * Must not be `NULL`. Will be update with the new allocation size. * It is guaranteed that there will be remove to NUL-terminate * the message even if it was already NUL-terminate (terminate it * doubly in that case.) * @param length Output parameter for the length of the constructed message. * Must not be `NULL`. * @param payload The payload that should be added to the message, it should end * with a LF. `NULL` if the message should not have a payload. * @param payload_length Pointer to the length of the payload. If `NULL`, `payload` * must be NUL-terminated, and if it is `NULL` this NUL-termination * will be used to find the length of the payload. This variable is * not used if `payload` is `NULL`. * @param args The first argument should be a line describing the first header, * it should be a printf(3)-formatted line that fully describes the * header line, that is, the header name, colon, blank space and then * the header's value. No LF should be included. The following * arguments should be the argument to format the header line. * If a format for a line begins with a question mark, the remainder * of the line used, but only if the next argument is non-zero * (it should be of type `int`,) that argument will not be used * for the formatting. This may be be iterated any number of this. * The last argument should be `NULL` to specify that there are no * more headers. The `Length`-header should not be included, it is * added automatically. A header may not have a length larger than * 2¹⁵, otherwise the behaviour of this function is undefined. * @return Zero on success, -1 on error, `errno` will have been set * accordingly on error. * * @throws ENOMEM Out of memory. Possibly, the process hit the RLIMIT_AS or * RLIMIT_DATA limit described in getrlimit(2). */ __attribute__((nonnull(1, 2, 3))) int libmds_compose_v(char **restrict buffer, size_t *restrict buffer_size, size_t *restrict length, const char *restrict payload, const size_t *restrict payload_length, va_list args); /** * Increase the message ID counter * * @param message_id Pointer to the current message ID, will be update with * the next free message ID. Must not be `NULL`. * @param test Function that tests whether an message ID is free, * it takes the message ID to test as its first argument, * and `data` as its second argument, and returns zero if * it is in used, a positive integer if it is free, and a * negative integer if an error occurred. `NULL` if there * should be no testing. * @param data Argument to pass to `test` so that it can deal with * threading or any other issues. Unused if `test` is `NULL`. * @return Zero on success, -1 on error, `errno` will have been set * accordingly on error. * * @throws EAGAIN If there are no free message ID to be used. * It is advisable to make `test` throw this immediately if * there are no free message ID:s. * @throws Any error that `test` may throw. */ __attribute__((nonnull(1))) int libmds_next_message_id(uint32_t *restrict message_id, int (*test)(uint32_t message_id, void *data), void *data); #endif