From 2b12068ac15b670e577c52134c867809e11b17a5 Mon Sep 17 00:00:00 2001 From: voidbert <50591320+voidbert@users.noreply.github.com> Date: Sun, 21 Jan 2024 01:46:23 +0000 Subject: [PATCH 01/19] Query base system cleanup --- .../include/queries/query_dispatcher.h | 50 +++--- .../include/queries/query_file_parser.h | 21 ++- .../include/queries/query_instance.h | 98 ++++++------ .../include/queries/query_instance_list.h | 78 +++++----- .../include/queries/query_parser.h | 62 +++----- .../include/queries/query_tokenizer.h | 12 +- trabalho-pratico/include/queries/query_type.h | 143 ++++++++++-------- .../include/queries/query_type_list.h | 68 ++------- .../include/queries/query_writer.h | 32 ++-- .../src/queries/query_dispatcher.c | 93 ++++++------ .../src/queries/query_file_parser.c | 45 +++--- trabalho-pratico/src/queries/query_instance.c | 91 +++++------ .../src/queries/query_instance_list.c | 80 ++++------ trabalho-pratico/src/queries/query_parser.c | 97 +++++------- .../src/queries/query_tokenizer.c | 23 ++- trabalho-pratico/src/queries/query_type.c | 57 ++++--- .../src/queries/query_type_list.c | 99 ++++-------- trabalho-pratico/src/queries/query_writer.c | 27 ++-- 18 files changed, 505 insertions(+), 671 deletions(-) diff --git a/trabalho-pratico/include/queries/query_dispatcher.h b/trabalho-pratico/include/queries/query_dispatcher.h index 2c187f5..0ef48bb 100644 --- a/trabalho-pratico/include/queries/query_dispatcher.h +++ b/trabalho-pratico/include/queries/query_dispatcher.h @@ -22,50 +22,50 @@ * ### Examples * * batch_mode.c is a great example of everything you need to do to be able to start dispatching - * queries. Essentially, you need to [load a database from a dataset](@ref dataset_loader_examples), - * [create a list of query definitions](@ref query_type_list_examples) and - * [load a query file](@ref query_file_parser_examples). The hardest part is creating the set of - * output files for all queries. To make sure the output file order is correct, - * ::query_instance_list_iter is used. + * queries. Essentially, you need to [load a database from a dataset](@ref dataset_loader_examples) + * and [parse a query file](@ref query_file_parser_examples). The hardest part is creating the set + * of query writers for all queries. To make sure the output file order is correct, please use + * ::query_instance_list_iter. */ #ifndef QUERY_DISPATCHER_H #define QUERY_DISPATCHER_H -#include - #include "database/database.h" #include "queries/query_instance_list.h" #include "testing/performance_metrics.h" /** - * @brief Runs a single query. + * @brief Runs a single query. + * @details If you want to run multiple queries, do not call this method multiple times, as that + * will be very innefficient. Queries generate statistical data, shared by all queries of + * the same type to improve performance. That can only be taken advantage of by using + * ::query_dispatcher_dispatch_list. + * + * @param database Database, so that the query can get information. + * @param query_instance Query to be run. + * @param output Where the query's result should be written to. * - * @param database Database, so that the query can get information. - * @param query_instance Query to be run. - * @param query_type_list List of known queries. - * @param output Where the query's result should be written to. + * @retval 0 Query preparation success. Running the query itself might have silently failed. + * @retval 1 Allocation failure or invalid @p query_instance. */ -void query_dispatcher_dispatch_single(const database_t *database, - const query_instance_t *query_instance, - const query_type_list_t *query_type_list, - query_writer_t *output); +int query_dispatcher_dispatch_single(const database_t *database, + const query_instance_t *query_instance, + query_writer_t *output); /** * @brief Runs a list of queries. * * @param database Database, so that the queries can get information. * @param query_instance_list List of queries to be run. Cannot be `const`, as this list may get - * sorted. - * @param query_type_list List of known queries. + * sorted. However, no new items will be added to it. * @param outputs Where the queries' results will be written to. These should be in - * the same as the @p query_instance_list after being sorted. - * @param metrics Where to write profiling data to. Can be `NULL`. + * the same order as @p query_instance_list after being sorted. + * @param metrics Where to write profiling data to. Can be `NULL` for no profiling. */ -void query_dispatcher_dispatch_list(const database_t *database, - query_instance_list_t *query_instance_list, - const query_type_list_t *query_type_list, - query_writer_t *const *outputs, - performance_metrics_t *metrics); +void query_dispatcher_dispatch_list(const database_t *database, + query_instance_list_t *query_instance_list, + query_writer_t *const *outputs, + performance_metrics_t *metrics); #endif diff --git a/trabalho-pratico/include/queries/query_file_parser.h b/trabalho-pratico/include/queries/query_file_parser.h index d274a4b..fffee4f 100644 --- a/trabalho-pratico/include/queries/query_file_parser.h +++ b/trabalho-pratico/include/queries/query_file_parser.h @@ -21,11 +21,9 @@ * @anchor query_file_parser_examples * ### Examples * - * To parse a file with queries, first create a - * [list of query types](@ref query_type_list_examples). Then, open the file with `fopen`, and, - * along with the query type list, pass it to ::query_file_parser_parse. In the end, don't forget to - * free the resulting ::query_instance_list_t using ::query_instance_list_free. See batch_mode.c - * for a code example. + * To parse a file with queries, open the file with `fopen` and pass it to + * ::query_file_parser_parse. In the end, don't forget to close the file and to free the resulting + * ::query_instance_list_t using ::query_instance_list_free. See batch_mode.c for a code example. */ #ifndef QUERY_FILE_PARSER_H @@ -36,18 +34,17 @@ #include "queries/query_instance_list.h" /** - * @brief Parses a file containg a query in each line. - * - * @param input Input file stream to be read. - * @param query_type_list List of known queries. + * @brief Parses a file containg a query in each line. + * @details Queries whose parsing fails will neither appear on the returned list nor be reported be + * as errors. * + * @param input Input file stream to be read. * @return A pointer to a ::query_instance_list_t, that must later be `free`'d by - * ::query_instance_list_free, or `NULL` on failure. + * ::query_instance_list_free, or `NULL` on allocation failure. * * #### Examples * See [the header file's documentation](@ref query_file_parser_examples). */ -query_instance_list_t *query_file_parser_parse(FILE *input, - const query_type_list_t *query_type_list); +query_instance_list_t *query_file_parser_parse(FILE *input); #endif diff --git a/trabalho-pratico/include/queries/query_instance.h b/trabalho-pratico/include/queries/query_instance.h index 089c9dc..59d6c49 100644 --- a/trabalho-pratico/include/queries/query_instance.h +++ b/trabalho-pratico/include/queries/query_instance.h @@ -21,18 +21,17 @@ * @anchor query_instance_examples * ### Examples * - * A query instance usually originates from a [query parser](@ref queryparser.h). You can also + * A query instance usually originates from the [query parser](@ref query_parser.h). You can also * create it using ::query_instance_create, followed by calling each one of the following setters: * * - ::query_instance_set_type; * - ::query_instance_set_formatted; - * - ::query_instance_set_number_in_file (use 1 if not a query in a file); - * - ::query_instance_set_argument_data (see ::query_type_parse_arguments_callback_t for your query - * type). + * - ::query_instance_set_line_in_file (use 1 if not a query in a file); + * - ::query_instance_set_argument_data (using the ::query_type_parse_arguments_callback_t method + * for your query type). * - * To run the query you created, see ::query_dispatcher_dispatch_single. - * - * In the end, don't forget to call ::query_instance_free. + * To run the query you created, see ::query_dispatcher_dispatch_single. In the end, don't forget to + * call ::query_instance_free. */ #ifndef QUERY_INSTANCE_H @@ -41,7 +40,6 @@ #include #include -/* Weird structure to do something like C++'s `class Foo;` without adding a dependency */ /* clang-format off */ #ifndef query_instance_typedef /** @cond FALSE */ @@ -53,100 +51,90 @@ #endif /* clang-format on */ -#include "queries/query_type_list.h" +#include "queries/query_type.h" /** - * @brief Create a new query instance. - * @return A new ::query_instance_t, that should be `free`'d with ::query_instance_free, or `NULL` - * on failure. + * @brief Creates a new query instance. + * @return A pointer to a new ::query_instance_t, that should be `free`d with ::query_instance_free, + * or `NULL` on failure. */ query_instance_t *query_instance_create(void); /** - * @brief Creates a deep copy of a query instance. - * - * @param query Query to be copied. - * @param query_type_list List of supported queries (to know how to duplicate `argument_data` in @p - * query). - * - * @return A pointer to a copy of @p query, that must be deleted with ::query_instance_free, `NULL` - * on allocation failure or invalid query type. + * @brief Creates a deep copy of a query instance. + * @param query Query to be copied. + * @return A pointer to a copy of @p query, that must be `free`d with ::query_instance_free, `NULL` + * on allocation failure / or invalid query type. */ -query_instance_t *query_instance_clone(const query_instance_t *query, - const query_type_list_t *query_type_list); +query_instance_t *query_instance_clone(const query_instance_t *query); /** - * @brief Sets the type of a query. + * @brief Sets the type of a query instance. * @param query Query instance to have its type set. - * @param type Type of the query. + * @param type Type of the query instance. */ -void query_instance_set_type(query_instance_t *query, size_t type); +void query_instance_set_type(query_instance_t *query, const query_type_t *type); /** - * @brief Sets whether a query's output should be formatted or not. + * @brief Sets whether or not a query's output should be formatted. * @param query Query instance to have its formatting flag set. - * @param formatted If the query's output should be formatted or not. + * @param formatted Whether or not the query's output should be formatted. */ void query_instance_set_formatted(query_instance_t *query, int formatted); /** - * @brief Sets the number of the line a query was on. - * @param query Query instance to have its line number in the file set. - * @param number_in_file The number of the line @p query was on (in the query file). + * @brief Sets the number of the line a query instance was on. + * @param query Query instance to have its line number in the file set. + * @param line_in_file The number of the line the query instance was on (in the query file). Leave + * `1` for an interactive mode query. */ -void query_instance_set_number_in_file(query_instance_t *query, size_t number_in_file); +void query_instance_set_line_in_file(query_instance_t *query, size_t line_in_file); /** - * @brief Adds data relating to argument parsing results to a query. Its data type will depend on - * the query's type. + * @brief Adds parsed arguments to a query. Its data type will depend on the query's type. + * @details For any query instance, ::query_instance_set_type must be called before this setter. * - * @param query Query instance to have its formatting flag set. + * @param query Query instance to have its arguments set. * @param argument_data Data resulting from parsing the query's arguments. - * @param query_type_list List of supported queries (to know how to clone @p argument_data) * * @retval 0 Success. * @retval 1 Allocation failure or query type not set. */ -int query_instance_set_argument_data(query_instance_t *query, - const void *argument_data, - const query_type_list_t *query_type_list); +int query_instance_set_argument_data(query_instance_t *query, const void *argument_data); /** - * @brief Gets the type of a query. - * @param query Query to get the type from. + * @brief Gets the type of a query instance. + * @param query Query instance to get the type from. * @return The type of @p query. */ -size_t query_instance_get_type(const query_instance_t *query); +const query_type_t *query_instance_get_type(const query_instance_t *query); /** - * @brief Gets whether a query's output should or not be formatted. - * @param query Query to get the formatted flag from. - * @return Whether @p query 's output should or not be formatted. + * @brief Gets whether or not a query's output should be formatted. + * @param query Query instance to get the formatted flag from. + * @return Whether or not @p query 's output should be formatted. */ int query_instance_get_formatted(const query_instance_t *query); /** - * @brief Gets the number of the line a query was on. - * @param query Query to get the number in file from. + * @brief Gets the number of the line a query instance was on. + * @param query Query instance to get the line number from. * @return The number of the line @p query was on. */ -size_t query_instance_get_number_in_file(const query_instance_t *query); +size_t query_instance_get_line_in_file(const query_instance_t *query); /** - * @brief Gets data resulting from parsing the query's arguments. - * @param query Query to get argument data from. + * @brief Gets a query instance's parsed arguments. + * @param query Query instance to get parsed arguments from. * @return Data resulting from parsing the query's arguments. Its data type will depend on the * query's type. */ const void *query_instance_get_argument_data(const query_instance_t *query); /** - * @brief Frees memory used by a query instance, created by ::query_instance_create. - * - * @param query Query instance to be freed. - * @param query_type_list List of supported queries (to know how to free `argument_data` in @p - * query). + * @brief Frees memory used by a query instance. + * @param query Query instance to be `free`d. */ -void query_instance_free(query_instance_t *query, const query_type_list_t *query_type_list); +void query_instance_free(query_instance_t *query); #endif diff --git a/trabalho-pratico/include/queries/query_instance_list.h b/trabalho-pratico/include/queries/query_instance_list.h index 3a3a89a..cc3d103 100644 --- a/trabalho-pratico/include/queries/query_instance_list.h +++ b/trabalho-pratico/include/queries/query_instance_list.h @@ -37,15 +37,13 @@ * ``` * * - Add query instances to it, using ::query_instance_list_add. - * - * - When done using the list, free it with ::query_instance_list_free, in case you also own the - * queries you added to list, or using ::query_instance_list_free_no_internals if you don't. + * - When done using the list, free it with ::query_instance_list_free. * * There are two types of list iterations. Before any of these iterations, the list will be sorted * by query type order. * - * - Query by query iteration using ::query_instance_list_iter. This is trivial, though an example - * can be found in batch_mode.c, to open all files to write query outputs in. + * - Query by query iteration using ::query_instance_list_iter. An example of this can be found in + * batch_mode.c, to open all files to write query outputs in. * * - Iteration by query type using ::query_instance_list_iter_types, where a callback is called * for each set of queries of the same type. Here's an example. Suppose we have the following @@ -78,7 +76,7 @@ #include "queries/query_instance.h" -/** @brief A list of ::query_instance_t. */ +/** @brief A list of ::query_instance_t, sorted by query type. */ typedef struct query_instance_list query_instance_list_t; /** @@ -87,15 +85,15 @@ typedef struct query_instance_list query_instance_list_t; * * @param user_data Pointer, kept from call to call, so that this callback can modify the program's * state. - * @param instances All query instances of the same type. * @param n Number of instances in @p instances. It's guaranteed that there'll be no empty * sets. + * @param instances All query instances of the same type. * * @return `0` on success, another value for immediate termination of iteration. */ -typedef int (*query_instance_list_iter_types_callback)(void *user_data, - const query_instance_t *const *instances, - size_t n); +typedef int (*query_instance_list_iter_types_callback)(void *user_data, + size_t n, + const query_instance_t *const instances[n]); /** * @brief Method called for every query in a query list, used by ::query_instance_list_iter. @@ -109,9 +107,9 @@ typedef int (*query_instance_list_iter_types_callback)(void typedef int (*query_instance_list_iter_callback)(void *user_data, const query_instance_t *instance); /** - * @brief Creates an empty list of ::query_instance_t. - * @details This value must be `free`'d with ::query_instance_list_free. - * @return A new ::query_instance_list_t, or `NULL` on failure. + * @brief Creates an empty list of ::query_instance_t. + * @return A pointer to a new ::query_instance_list_t that must be `free`d with + * ::query_instance_list_free, or `NULL` on allocation failure. * * #### Examples * See [the header file's documentation](@ref query_instance_list_examples). @@ -119,35 +117,33 @@ typedef int (*query_instance_list_iter_callback)(void *user_data, const query_in query_instance_list_t *query_instance_list_create(void); /** - * @brief Creates a deep copy of a list of query instances. - * - * @param list List to be cloned. - * @param query_type_list List of supported queries (to know how to duplicate `argument_data` in - * queries in @p list). + * @brief Creates a deep copy of a list of query instances. + * @param list List to be cloned. + * @return A pointer to a new ::query_instance_list_t that must be `free`d with + * ::query_instance_list_free, or `NULL` on allocation failure. */ -query_instance_list_t *query_instance_list_clone(const query_instance_list_t *list, - const query_type_list_t *query_type_list); +query_instance_list_t *query_instance_list_clone(const query_instance_list_t *list); /** * @brief Copies a query instance into a list of query instances. * - * @param list List of query instances to add @p query to. - * @param query Query instance to be added to @p list. - * @param query_type_list List of supported queries (to know how to duplicate `argument_data` in @p - * query). + * @param list List of query instances to add @p query to. + * @param query Query instance to be added to @p list. + * + * @retval 0 Success. + * @retval 1 Allocation failure or invalid @p query. */ -void query_instance_list_add(query_instance_list_t *list, - const query_instance_t *query, - const query_type_list_t *query_type_list); +int query_instance_list_add(query_instance_list_t *list, const query_instance_t *query); /** - * @brief Iterates over every set of queries of each type. + * @brief Iterates over every set of queries of each type in a query instance list. * - * @param list List of query instances. Cannot be constant, as sorting may occur. + * @param list List of query instances. Cannot be constant, as internal sorting may occur. * @param callback Callback called for every set of queries of each type. * @param user_data Value passed to @p callback, so that it can modify the program's state. * - * @return The last value returned by @p callback (will always be `0` on success). + * @return The last value returned by @p callback (will always be `0` on success, meaning iteration + * reached the end). * * #### Examples * See [the header file's documentation](@ref query_instance_list_examples). @@ -159,11 +155,15 @@ int query_instance_list_iter_types(query_instance_list_t *list, /** * @brief Iterates over every query in a query instance list. * - * @param list List of query instances. Cannot be constant, as sorting may occur. - * @param callback Callback called for every set of queries of each type. + * @param list List of query instances. Cannot be constant, as internal sorting may occur. + * @param callback Callback called for every query in @p list. * @param user_data Value passed to @p callback, so that it can modify the program's state. * - * @return The last value returned by @p callback (will always be `0` on success). + * @return The last value returned by @p callback (will always be `0` on success, meaning iteration + * reached the end). + * + * #### Examples + * See [the header file's documentation](@ref query_instance_list_examples). */ int query_instance_list_iter(query_instance_list_t *list, query_instance_list_iter_callback callback, @@ -171,19 +171,15 @@ int query_instance_list_iter(query_instance_list_t *list, /** * @brief Gets the length of a ::query_instance_list_t. - * @param list List to get the length from. + * @param list List to get the length of. * @return The length of @p list. */ size_t query_instance_list_get_length(const query_instance_list_t *list); /** - * @brief Frees memory allocated by ::query_instance_list_create. - * - * @param list List allocated by ::query_instance_list_create. - * @param query_type_list List of supported queries (to know how to free `argument_data` in each - * query instance in the list). + * @brief Frees memory in a ::query_instance_list_t. + * @param list List to be deleted. */ -void query_instance_list_free(query_instance_list_t *list, - const query_type_list_t *query_type_list); +void query_instance_list_free(query_instance_list_t *list); #endif diff --git a/trabalho-pratico/include/queries/query_parser.h b/trabalho-pratico/include/queries/query_parser.h index 417b685..2b07814 100644 --- a/trabalho-pratico/include/queries/query_parser.h +++ b/trabalho-pratico/include/queries/query_parser.h @@ -23,7 +23,7 @@ * * The following example uses ::query_parser_parse_string_const. ::query_parser_parse_string has * the same behavior for non-constant strings. Also, suppose that query 1's - * ::query_type_parse_arguments_callback_t just prints the queries arguments to `stdout`, and there + * ::query_type_parse_arguments_callback_t just prints the queries' arguments to `stdout`, and there * are no other queries. * * ```c @@ -33,18 +33,12 @@ * "3 \"unknown query\" \"number\"", * "3F \"unknown formatted\" \"query\""}; * - * query_type_list_t *query_list = query_type_list_create(); - * if (!query_list) { - * fprintf(stderr, "Failed to allocate query definitions!\n"); - * return 1; - * } - * * GPtrArray *aux = g_ptr_array_new(); * * for (size_t i = 0; i < 4; ++i) { * query_instance_t *query = query_instance_create(); * - * int result = query_parser_parse_string_const(query, queries[i], query_list, aux); + * int result = query_parser_parse_string_const(query, queries[i], aux); * if (result) * fprintf(stderr, "Failed to parse query: %s\n", queries[i]); * else if (query_instance_get_formatted(query)) @@ -56,7 +50,7 @@ * } * * query_type_list_free(query_list); - * g_ptr_array_free(aux, TRUE); + * g_ptr_array_unref(aux); * return 0; * } * ``` @@ -74,11 +68,11 @@ * Failed to parse query: 3F "unknown formatted" "query" * ``` * - * Like in query_tokenizer.h, you can have arguments inside or outside quotes, and multiple + * Like in query_tokenizer.h, you can have arguments inside or outside of quotes, and multiple * consecutive spaces are allowed both in quotes (kept) or outside quotes (discarded). * * It should be noted that, because we're parsing multiple queries, we used an external `GPtrArray` - * that we provided to ::query_parser_parse_string_const. This way we avoid many allocations. For + * that we provided to ::query_parser_parse_string_const. This way, we avoid many allocations. For * parsing a single query, you can pass `NULL` to the mentioned method's `aux` paramater, and it'll * automatically allocate and de-allocate a new array. */ @@ -86,19 +80,15 @@ #ifndef QUERY_PARSER_H #define QUERY_PARSER_H -#include - #include "queries/query_instance.h" /** * @brief Parses a **MODIFIABLE** string containing a query. * - * @param output Where the parsed query is placed. This **will be modified on failure** - * too. - * @param input String to parse, that **will be modified** as a byproduct of parsing. - * @param query_type_list List of available queries. - * @param aux Auxiliary `GPtrArray`, that can be provided to be modified and avoid - * memory allocations. If `NULL`, a new array will be instantiated. + * @param output Where the parsed query is placed. This **will be modified on failure** too. + * @param input String to parse, that **will be modified** as a byproduct of parsing. + * @param aux Auxiliary `GPtrArray`, that can be provided to be modified and avoid memory + * allocations. If `NULL`, a new array will be instantiated. * * @retval 0 Parsing success. * @retval 1 Parsing failure. @@ -106,34 +96,28 @@ * ### Examples * See [the header file's documentation](@ref query_parser_examples). */ -int query_parser_parse_string(query_instance_t *output, - char *input, - const query_type_list_t *query_type_list, - GPtrArray *aux); +int query_parser_parse_string(query_instance_t *output, char *input, GPtrArray *aux); + +/** @brief Value returned by ::query_parser_parse_string_const when `malloc` fails. */ +#define QUERY_PARSER_PARSE_CONST_RET_FAILED_MALLOC -1 /** * @brief Parses a string containing a query. + * @details The current implementation copies the provided string to a temporary buffer. Keep that + * in mind for performance reasons. * - * @details The current implementation allocates a writeable buffer and copies over the string - * before calling ::query_parser_parse_string, so **it's very inefficient** and should not - * be used for large strings. - * - * @param output Where the parsed query is placed. This **will be modified on failure** - * too. - * @param input String to parse. - * @param query_type_list List of available queries. - * @param aux Auxiliary `GPtrArray`, that can be provided to be modified and avoid - * memory allocations. If `NULL`, a new array will be instantiated. + * @param output Where the parsed query is placed. This **will be modified on failure** too. + * @param input String to parse. + * @param aux Auxiliary `GPtrArray`, that can be provided to be modified and avoid memory + * memory allocations. If `NULL`, a new array will be instantiated. * - * @retval 0 Parsing success. - * @retval 1 Parsing or allocation failure. + * @retval 0 Parsing success. + * @retval 1 Parsing failure. + * @retval QUERY_PARSER_PARSE_CONST_RET_FAILED_MALLOC Allocation failure. * * ### Examples * See [the header file's documentation](@ref query_parser_examples). */ -int query_parser_parse_string_const(query_instance_t *output, - const char *input, - const query_type_list_t *query_type_list, - GPtrArray *aux); +int query_parser_parse_string_const(query_instance_t *output, const char *input, GPtrArray *aux); #endif diff --git a/trabalho-pratico/include/queries/query_tokenizer.h b/trabalho-pratico/include/queries/query_tokenizer.h index 494459a..f12d2bf 100644 --- a/trabalho-pratico/include/queries/query_tokenizer.h +++ b/trabalho-pratico/include/queries/query_tokenizer.h @@ -83,17 +83,13 @@ */ int query_tokenizer_tokenize(char *input, tokenize_iter_callback_t callback, void *user_data); -/** - * @brief Value returned by ::query_tokenizer_tokenize_const when `malloc` fails. - */ +/** @brief Value returned by ::query_tokenizer_tokenize_const when `malloc` fails. */ #define QUERY_TOKENIZER_TOKENIZE_CONST_RET_FAILED_MALLOC -1 /** - * @brief See ::query_tokenizer_tokenize_const, but this method applies to `const` strings. - * - * @details The current implementation allocates a writeable buffer and copies over the string - * before calling ::query_tokenizer_tokenize, so **it's very inefficient** and should not - * be used for large strings. + * @brief See ::query_tokenizer_tokenize_const, but this method applies to `const` strings. + * @details The current implementation copies the provided string to a temporary buffer. Keep that + * in mind for performance reasons. * * @param input String to tokenize. * @param callback Function called for every token read. diff --git a/trabalho-pratico/include/queries/query_type.h b/trabalho-pratico/include/queries/query_type.h index 00b3cbd..c4a62d7 100644 --- a/trabalho-pratico/include/queries/query_type.h +++ b/trabalho-pratico/include/queries/query_type.h @@ -24,9 +24,9 @@ * ### Examples * * The following summary is for people who are interested in implementing their own queries. If - * you just want to use existing queries, see ::query_type_list_t. + * you just want to use existing queries, see query_type_list.h. * - * Check out implementations of existing queries (q1.h to q10.h). In summary, here is what each + * Check out implementations of existing queries (q01.c to q10.c). In summary, here is what each * callback that needs to be defined does: * * - ::query_type_parse_arguments_callback_t parses arguments after the query type + formatting @@ -35,21 +35,24 @@ * later, as they may change when parsing a query file. The function must return the value that * will be stored in ::query_instance::argument_data (or `NULL` on failure). * - * - ::query_type_free_query_instance_argument_data_callback_t frees data generated by + * - ::query_type_clone_arguments_callback_t creates deep clones of the values generated by + * ::query_type_parse_arguments_callback_t, so that a ::query_instance_t can also be copied. + * + * - ::query_type_free_arguments_callback_t `free`s data generated by * ::query_type_parse_arguments_callback_t. * - * - ::query_type_generate_statistics_callback_t generates statistical data to be used by all - * queries of the same type. + * - ::query_type_generate_statistics_callback_t generates statistical data to be used for the + * execution of all queries of the same type. This method is optional. * * - ::query_type_free_statistics_callback_t frees data generated by - * ::query_type_generate_statistics_callback. + * ::query_type_generate_statistics_callback_t. This method is optional. * * - ::query_type_execute_callback_t executes a query. * * After defining these methods, create a constructor for your query using ::query_type_create. * Remember that any ::query_type_create call must have a ::query_type_free match. This is usually - * automatically handled by ::query_type_list_t. If you're creating a new query, you must modify - * the source code of ::query_type_list_create to take your new query into account. + * automatically handled by query_type_list.c. If you're creating a new query, you must modify + * the source code of query_type_list.c to take your new query into account. * * For accessing methods of existing queries, use the getters defined in this module. */ @@ -58,22 +61,19 @@ #define QUERY_TYPE_H #include -#include #include "database/database.h" #include "queries/query_writer.h" -/* Weird structure to do something like C++'s `class Foo;` without adding a dependency */ +/** @cond FALSE */ /* clang-format off */ +/* Weird structure to do something like C++'s `class Foo;` without adding a dependency. */ #ifndef query_instance_typedef - /** @cond FALSE */ #define query_instance_typedef - /** @endcond */ - - /** @brief An occurrence of a query (in a file, or inputted by the user). */ typedef struct query_instance query_instance_t; #endif /* clang-format on */ +/** @endcond */ /** @brief A definition of a query. */ typedef struct query_type query_type_t; @@ -81,64 +81,71 @@ typedef struct query_type query_type_t; /** * @brief Type of the method called for parsing query arguments. * - * @param argv Arguments of the query. Do not store these pointers without first making a copy of - * each string. They are non-constant and will be quickily discarded, so you can modify - * them during your parsing. * @param argc Number of query arguments. + * @param argv Arguments of the query. These won't include the query type or whether the query's + * output must be formatted. Also, do not store these pointers without first making a + * copy of each string. They are non-constant so that you can modify them during your + * parsing, but will be quickly discarded. * * @return `NULL` on failure, other value on success. This value will be stored in * ::query_instance::argument_data. */ -typedef void *(*query_type_parse_arguments_callback_t)(char *const *argv, size_t argc); +typedef void *(*query_type_parse_arguments_callback_t)(size_t argc, char *const argv[argc]); /** - * @brief Type of the method called for cloning query arguments. - * @param args_data Value returned by ::query_type_parse_arguments_callback_t. + * @brief Type of the method called for cloning query arguments. + * @details This exists for the sake of ::query_instance_t also being cloneable. + * + * @param args_data Value returned by ::query_type_parse_arguments_callback_t. + * * @return A deep clone of @p args, `NULL` on allocation failure. */ typedef void *(*query_type_clone_arguments_callback_t)(const void *args_data); /** - * @brief Type of the method called for freeing ::query_instance::argument_data. - * @param argument_data ::query_instance::argument_data. + * @brief Type of the method called for `free`ing what was returned by + * ::query_type_parse_arguments_callback_t. + * @param args_data Non-`NULL` value returned by ::query_type_parse_arguments_callback_t. */ -typedef void (*query_type_free_query_instance_argument_data_callback_t)(void *argument_data); +typedef void (*query_type_free_arguments_callback_t)(void *args_data); /** * @brief Type of the method called to generate statistical data before running all queries of the * same type. - * @details Can be `NULL`, not to generate any statistics. + * @details Can be `NULL`, for no statistics to be generated. * - * @param database Database to collect statistics - * @param instances List of query instances that will need to be processed. + * @param database Database to collect statistical information from. * @param n Number of query instances in @p instances. + * @param instances List of query instances that will need to be processed. * - * @return `NULL` on failure, another value on success. If you do not plan on collecting statistical - * data, this callback itself can be left as `NULL`. Statistical data will be global to - * all queries of the same type. + * @return `NULL` on failure, another value on success. Statistical data will be global to all + * queries of the same type. */ -typedef void *(*query_type_generate_statistics_callback_t)(const database_t *database, - const query_instance_t *const *instances, - size_t n); +typedef void *(*query_type_generate_statistics_callback_t)( + const database_t *database, + size_t n, + const query_instance_t *const instances[n]); /** - * @brief Frees data generated by ::query_type_generate_statistics_callback. - * @details Can be `NULL`, only if the query type's ::query_type_generate_statistics_callback is - * also `NULL`. - * @param statistics Data generated by ::query_type_generate_statistics_callback. + * @brief Type of method called to free data generated by + * ::query_type_generate_statistics_callback_t. + * @details Can be `NULL` if the query type's ::query_type_generate_statistics_callback_t is also + * `NULL`. + * + * @param statistics Non-`NULL` value returned by ::query_type_generate_statistics_callback_t. */ typedef void (*query_type_free_statistics_callback_t)(void *statistics); /** - * @brief Executes a query. + * @brief Type of method called to execute a query. * * @param database Database to perform data lookups. - * @param statistics Data generated by ::query_type_generate_statistics_callback, or `NULL` if that - * callback isn't defined. + * @param statistics Data generated by ::query_type_generate_statistics_callback_t, or `NULL` if + * that callback isn't defined for a given query type. * @param instance Query instance to execute. * @param output Where to write query results to. * - * @return `0` on success, other value on failure. + * @return `0` on success, another value on failure. */ typedef int (*query_type_execute_callback_t)(const database_t *database, const void *statistics, @@ -147,33 +154,42 @@ typedef int (*query_type_execute_callback_t)(const database_t *database, /** * @brief Creates a query type, defining its behavior. - * @details For parameter description, see the type name of each parameter. + * @details For parameter description, see the description for the type of each parameter. + * @p type_number is a number that identifies a query (e.g.: q01.h -> `1`). * - * @return A pointer to a new `malloc`-allocated ::query_type_t (or `NULL` on allocation failure). - * After being used, it must be `free`'d with ::query_type_free. + * @return A pointer to a new ::query_type_t, that must be `free`d with ::query_type_free. `NULL` + * can be returned on allocation failure. * * #### Examples * See [the header file's documentation](@ref query_type_examples). */ -query_type_t *query_type_create( - query_type_parse_arguments_callback_t parse_arguments, - query_type_clone_arguments_callback_t clone_arguments, - query_type_free_query_instance_argument_data_callback_t free_query_instance_argument_data, - query_type_generate_statistics_callback_t generate_statistics, - query_type_free_statistics_callback_t free_statistics, - query_type_execute_callback_t execute); +query_type_t *query_type_create(size_t type_number, + query_type_parse_arguments_callback_t parse_arguments, + query_type_clone_arguments_callback_t clone_arguments, + query_type_free_arguments_callback_t free_arguments, + query_type_generate_statistics_callback_t generate_statistics, + query_type_free_statistics_callback_t free_statistics, + query_type_execute_callback_t execute); /** * @brief Creates a deep copy of a query type. * @param type Query type to be copied. - * @return A pointer to a copy of @p type, or `NULL` on allocation failure. + * @return A pointer to a copy of @p type, that must be `free`d with ::query_type_free, or `NULL` on + * allocation failure. */ query_type_t *query_type_clone(const query_type_t *type); +/** + * @brief Gets the number that identifies a ::query_type_t. + * @param type ::query_type_t to get the number from. + * @return @p type 's identifier number. + */ +size_t query_type_get_type_number(const query_type_t *type); + /** * @brief Gets the method called for parsing query arguments from a ::query_type_t. * @param type ::query_type_t to get the method called for parsing query arguments from. - * @return The method called for parsing query arguments. + * @return @p type 's method called for parsing query arguments. */ query_type_parse_arguments_callback_t query_type_get_parse_arguments_callback(const query_type_t *type); @@ -181,32 +197,31 @@ query_type_parse_arguments_callback_t /** * @brief Gets the method called for cloning query arguments from a ::query_type_t. * @param type ::query_type_t to get the method called for cloning query arguments from. - * @return The method called for deep-cloning query arguments. + * @return @p type 's method called for deep-cloning query arguments. */ query_type_clone_arguments_callback_t query_type_get_clone_arguments_callback(const query_type_t *type); /** - * @brief Gets the method called for freeing ::query_instance::argument_data from a ::query_type_t. - * @param type ::query_type_t to get the method called for freeing ::query_instance::argument_data - * from. - * @return The method called for freeing ::query_instance::argument_data. + * @brief Gets the method called for `free`ing query arguments from a ::query_type_t. + * @param type ::query_type_t to get the method called for `free`ing query arguments from. + * @return @p type 's method called for `free`ing query arguments. */ -query_type_free_query_instance_argument_data_callback_t - query_type_get_free_query_instance_argument_data_callback(const query_type_t *type); +query_type_free_arguments_callback_t + query_type_get_free_arguments_callback(const query_type_t *type); /** * @brief Gets the method called for generating statistical data from a ::query_type_t. * @param type ::query_type_t to get the method called for generating statistical data from. - * @return The method called for generating statistical data. + * @return @p type 's method called for generating statistical data. */ query_type_generate_statistics_callback_t query_type_get_generate_statistics_callback(const query_type_t *type); /** - * @brief Gets the method called for freeing statistical data from a ::query_type_t. - * @param type ::query_type_t to get the method called for freeing statistical data from. - * @return The method called for freeing statistical data. + * @brief Gets the method called for `free`ing statistical data from a ::query_type_t. + * @param type ::query_type_t to get the method called for `free`ing statistical data from. + * @return @p type 's method called for `free`ing statistical data. */ query_type_free_statistics_callback_t query_type_get_free_statistics_callback(const query_type_t *type); @@ -214,7 +229,7 @@ query_type_free_statistics_callback_t /** * @brief Gets the method called for executing a query from a ::query_type_t. * @param type ::query_type_t to get the method called for query execution from. - * @return The method called for executing a query. + * @return @p type 's method called for executing a query. */ query_type_execute_callback_t query_type_get_execute_callback(const query_type_t *type); diff --git a/trabalho-pratico/include/queries/query_type_list.h b/trabalho-pratico/include/queries/query_type_list.h index bc73c6e..94e2503 100644 --- a/trabalho-pratico/include/queries/query_type_list.h +++ b/trabalho-pratico/include/queries/query_type_list.h @@ -15,89 +15,45 @@ */ /** - * @file query_type_list.h - * @brief A data structure for a list of all supported queries. + * @file query_type_list.h + * @brief The list of all supported queries. + * @details Essentially, a list of `vtable`s. * - * @anchor query_type_list_examples + * @anchor query_type_list_example * ### Examples * - * A `query_type_list_t` can be created the following way: - * - * ```c - * query_type_list_t *query_type_list = query_type_list_create(); - * if (!query_type_list) { - * fprintf(stderr, "Allocation error of query definitions!\n"); - * exit(1); - * } - * ``` - * - * You usually only need to create one of these, that will be passed around the whole program. - * * Suppose you want to perform actions related to the following query: `"1 EdSousa"`. From this * list, you can get the methods that define this query type, `1`: * * ```c - * const query_type_t *type = query_type_list_get_by_index(query_type_list, 1); + * const query_type_t *type = query_type_list_get_by_index(1); * ``` * * If your query were `"200 EdSousa"`, because there aren't 200 queries, - * `query_type_list_get_by_index(query_type_list, 200)` would return `NULL`. - * - * In the end, don't forget to call `query_type_list_free(query_type_list)`. + * `query_type_list_get_by_index(200)` would return `NULL`. */ #ifndef QUERY_TYPE_LIST_H #define QUERY_TYPE_LIST_H +#include + #include "queries/query_type.h" /** @brief Number of queries supported (1 to ::QUERY_TYPE_LIST_COUNT). */ #define QUERY_TYPE_LIST_COUNT 10 -/** @brief A list of all supported queries. */ -typedef struct query_type_list query_type_list_t; - -/** - * @brief Initializes a list of all supported queries. - * @details This must be called in the beginning of the program, as most functions that deal with - * queries require a list of query definitions. - * @return A pointer to a `query_type_list_t` (that must be freed with ::query_type_list_free), or - * `NULL` on failure. - * - * #### Examples - * See [the header file's documentation](@ref query_type_list_examples). - */ -query_type_list_t *query_type_list_create(void); - -/** - * @brief Creates a deep copy of a list of query types. - * @param query_type_list List of query types to be copied. - * @return A pointer to a copy of @p type, or `NULL` on allocation failure. - */ -query_type_list_t *query_type_list_clone(const query_type_list_t *query_type_list); - /** * @brief Gets a query definition by its numerical identifier (type). * @details Query indexing starts at `1` instead of `0`. * - * @param query_type_list List of query definitions. - * @param index ::query_instance::type. + * @param index Type of a query (::query_instance::type). * * @return A pointer to a ::query_type_t on success, or `NULL` if the index is out-of-bounds. * - * #### Examples - * See [the header file's documentation](@ref query_type_list_examples). - */ -const query_type_t *query_type_list_get_by_index(const query_type_list_t *query_type_list, - size_t index); - -/** - * @brief Frees memory allocated by ::query_type_list_create. - * @param query_type_list Value returned by ::query_type_list_create. - * - * #### Examples - * See [the header file's documentation](@ref query_type_list_examples). + * #### Example + * See [the header file's documentation](@ref query_type_list_example). */ -void query_type_list_free(query_type_list_t *query_type_list); +const query_type_t *query_type_list_get_by_index(size_t index); #endif diff --git a/trabalho-pratico/include/queries/query_writer.h b/trabalho-pratico/include/queries/query_writer.h index 52a63ae..0b9fbcc 100644 --- a/trabalho-pratico/include/queries/query_writer.h +++ b/trabalho-pratico/include/queries/query_writer.h @@ -21,8 +21,8 @@ * @anchor query_writer_examples * ### Example * - * The following example creates a query writer that will output unformatted objects to strings. - * Five objects with the fields `value` and `double` are outputted. + * The following example creates a query writer that will output unformatted objects to a list of + * strings. Five objects with the fields `value` and `double` are outputted. * * ```c * query_writer_t *writer = query_writer_create(NULL, 0); @@ -36,7 +36,7 @@ * } * ``` * - * The, we can print what was outputted and delete the strings: + * Then, we can print what was outputted and delete the writer: * * ```c * size_t nlines; @@ -82,27 +82,25 @@ * double: 8 * ``` * - * Alternatively, we can provide a output file directly to ::query_writer_create, but + * Alternatively, we can provide an output file directly to ::query_writer_create, but * ::query_writer_get_lines wouldn't work. */ #ifndef QUERY_WRITER_H #define QUERY_WRITER_H -#include - /** @brief Information about where to output query results to. */ typedef struct query_writer query_writer_t; /** - * @brief Creates a new place where to output query results to. + * @brief Creates a place where to output query results to. * * @param out_file_path Path to file to try to open for writing. Can be `NULL`, so that query - * results are outputted to internal strings. - * @param formatted If the output of the query should be formatted (pretty printed). + * results are outputted an internal list of strings. + * @param formatted Whether the output of the query should be formatted (pretty printed). * - * @return A ::query_writer_t that must be deleted with ::query_writer_free. `NULL` will be returned - * on both IO and allocation failures. + * @return A pointer to a ::query_writer_t that must be deleted with ::query_writer_free. `NULL` + * will be returned on both IO and allocation failures. * * #### Examples * See [the header file's documentation](@ref query_writer_examples). @@ -111,7 +109,7 @@ query_writer_t *query_writer_create(const char *out_file_path, int formatted); /** * @brief Marks that a new object will start to be written (a new flight, a new user, ...). - * @param writer Where to write the query output to. + * @param writer Where to write a query's output to. * * #### Examples * See [the header file's documentation](@ref query_writer_examples). @@ -119,9 +117,9 @@ query_writer_t *query_writer_create(const char *out_file_path, int formatted); void query_writer_write_new_object(query_writer_t *writer); /** - * @brief Writes a field of an object to the query output. + * @brief Writes a field of an object to a query writer. * - * @param writer Where to write the query output to. + * @param writer Where to write the query's output to. * @param key Name of the field being outputted. * @param format How to format the output (`printf` format string). * @param ... Objects to be formatted accoring to @p format. @@ -133,10 +131,10 @@ void query_writer_write_new_field(query_writer_t *writer, const char *key, const __attribute__((format(printf, 3, 4))); /** - * @brief Gets the lines outputted by @p writer. + * @brief Gets the lines outputted by a query writer. * @details Will only work if `NULL` was provided as a file path to ::query_writer_create. * - * @param writer Where the query output has been written to. Cannot be `const`, as some internal + * @param writer Where a query's output has been written to. Cannot be `const`, as some internal * buffers may need to be flushed before returning this value. * @param out_n Where to output the number of lines to. * @@ -149,7 +147,7 @@ const char *const *query_writer_get_lines(query_writer_t *writer, size_t *out_n) /** * @brief Frees memory allocated by ::query_writer_create. - * @param Non-`NULL` value returned by ::query_writer_create. + * @param writer Non-`NULL` value returned by ::query_writer_create. * * #### Examples * See [the header file's documentation](@ref query_writer_examples). diff --git a/trabalho-pratico/src/queries/query_dispatcher.c b/trabalho-pratico/src/queries/query_dispatcher.c index 9dd02ee..1d21ff0 100644 --- a/trabalho-pratico/src/queries/query_dispatcher.c +++ b/trabalho-pratico/src/queries/query_dispatcher.c @@ -22,30 +22,34 @@ * See [the header file's documentation](@ref query_dispatcher_examples). */ -#include +#include #include "queries/query_dispatcher.h" -void query_dispatcher_dispatch_single(const database_t *database, - const query_instance_t *query_instance, - const query_type_list_t *query_type_list, - query_writer_t *output) { +int query_dispatcher_dispatch_single(const database_t *database, + const query_instance_t *query_instance, + query_writer_t *output) { - query_instance_list_t *list = query_instance_list_create(); - query_instance_list_add(list, query_instance, query_type_list); + query_instance_list_t *const list = query_instance_list_create(); + if (!list) + return 1; - query_dispatcher_dispatch_list(database, list, query_type_list, &output, NULL); - query_instance_list_free(list, query_type_list); + if (query_instance_list_add(list, query_instance)) { + query_instance_list_free(list); + return 1; + } + + query_dispatcher_dispatch_list(database, list, &output, NULL); + query_instance_list_free(list); + return 0; } /** * @struct query_dispatcher_data_t - * @brief Data needed while dispatching a list of queries. + * @brief Data needed while dispatching a list of queries. * * @var query_dispatcher_data_t::database * @brief Database, so that queries can access data. - * @var query_dispatcher_data_t::query_type_list - * @brief List of known query types. * @var query_dispatcher_data_t::outputs * @brief Where to output query results to. * @var query_dispatcher_data_t::i @@ -54,60 +58,55 @@ void query_dispatcher_dispatch_single(const database_t *database, * @brief Performance metrics where to write profiling information to. */ typedef struct { - const database_t *database; - const query_type_list_t *query_type_list; - query_writer_t *const *outputs; - size_t i; - - performance_metrics_t *metrics; + const database_t *const database; + query_writer_t *const *const outputs; + size_t i; + performance_metrics_t *const metrics; } query_dispatcher_data_t; /** - * @brief Gets called for each set of queries of each type, to process them. + * @brief Gets called for each set of queries of the same type, to execute them. * * @param user_data A pointer to a ::query_dispatcher_data_t. - * @param instances Queries to be processed * @param n Number of queries to be processed. + * @param instances Queries of the same type to be processed. * - * @return Always `0`, even on failure. No invalid queries shall halt the program. + * @return Always `0`, even on failure. No query execution failures shall halt the program. */ -int __query_dispatcher_query_set_callback(void *user_data, - const query_instance_t *const *instances, - size_t n) { - query_dispatcher_data_t *dispatcher_data = (query_dispatcher_data_t *) user_data; - - size_t type_num = query_instance_get_type(instances[0]); - const query_type_t *type = - query_type_list_get_by_index(dispatcher_data->query_type_list, type_num); +int __query_dispatcher_query_set_callback(void *user_data, + size_t n, + const query_instance_t *const instances[n]) { + query_dispatcher_data_t *const dispatcher_data = user_data; - if (!type) - return 0; + const query_type_t *const type = query_instance_get_type(instances[0]); + const size_t type_num = query_type_get_type_number(type); - query_type_generate_statistics_callback_t generate_stats = + const query_type_generate_statistics_callback_t generate_stats = query_type_get_generate_statistics_callback(type); - query_type_free_statistics_callback_t free_stats = + const query_type_free_statistics_callback_t free_stats = query_type_get_free_statistics_callback(type); - query_type_execute_callback_t execute = query_type_get_execute_callback(type); + const query_type_execute_callback_t execute = query_type_get_execute_callback(type); void *statistics = NULL; if (generate_stats) { performance_metrics_start_measuring_query_statistics(dispatcher_data->metrics, type_num); - statistics = generate_stats(dispatcher_data->database, instances, n); + statistics = generate_stats(dispatcher_data->database, n, instances); performance_metrics_stop_measuring_query_statistics(dispatcher_data->metrics, type_num); + + if (!statistics) + return 0; /* Query statistical failure */ } for (size_t j = 0; j < n; ++j) { - size_t line = query_instance_get_number_in_file(instances[j]); + const size_t line = query_instance_get_line_in_file(instances[j]); performance_metrics_start_measuring_query_execution(dispatcher_data->metrics, type_num, line); - execute(dispatcher_data->database, statistics, instances[j], dispatcher_data->outputs[dispatcher_data->i + j]); /* Ignore returned result */ - performance_metrics_stop_measuring_query_execution(dispatcher_data->metrics, type_num, line); @@ -119,17 +118,15 @@ int __query_dispatcher_query_set_callback(void *user_da return 0; } -void query_dispatcher_dispatch_list(const database_t *database, - query_instance_list_t *query_instance_list, - const query_type_list_t *query_type_list, - query_writer_t *const *outputs, - performance_metrics_t *metrics) { - - query_dispatcher_data_t dispatcher_data = {.database = database, - .query_type_list = query_type_list, - .outputs = outputs, - .metrics = metrics}; +void query_dispatcher_dispatch_list(const database_t *database, + query_instance_list_t *query_instance_list, + query_writer_t *const *outputs, + performance_metrics_t *metrics) { + query_dispatcher_data_t dispatcher_data = {.database = database, + .outputs = outputs, + .i = 0, + .metrics = metrics}; query_instance_list_iter_types(query_instance_list, __query_dispatcher_query_set_callback, &dispatcher_data); diff --git a/trabalho-pratico/src/queries/query_file_parser.c b/trabalho-pratico/src/queries/query_file_parser.c index 3eaa4b1..99cb72e 100644 --- a/trabalho-pratico/src/queries/query_file_parser.c +++ b/trabalho-pratico/src/queries/query_file_parser.c @@ -35,20 +35,15 @@ * @var query_file_parser_data_t::aux_buffer * @brief Auxiliary array passed to ::query_parser_parse_string, to reduce the number of * allocations. - * @var query_file_parser_data_t::aux_query - * @brief Single query instance, overwritten multiple times to reduce the number of allocations. * @var query_file_parser_data_t::line_number * @brief Number of the current line of the file. * @var query_file_parser_data_t::query_instance_list * @brief List to add parsed queries to. - * @var query_file_parser_data_t::query_type_list - * @brief List of known query types (numerical identifiers). */ typedef struct { - GPtrArray *aux_buffer; - size_t line_number; - query_instance_list_t *query_instance_list; - const query_type_list_t *query_type_list; + GPtrArray *const aux_buffer; + size_t line_number; + query_instance_list_t *const query_instance_list; } query_file_parser_data_t; /** @@ -59,49 +54,45 @@ typedef struct { * @param line Query to be parsed. * * @retval 0 Success (parsing failures may occur). - * @retval 1 Allocation failure + * @retval 1 Allocation failure. */ int __query_file_parser_parse_query_callback(void *user_data, char *line) { - query_file_parser_data_t *parser_data = (query_file_parser_data_t *) user_data; + query_file_parser_data_t *const parser_data = user_data; - query_instance_t *aux_query = query_instance_create(); + query_instance_t *const aux_query = query_instance_create(); if (!aux_query) return 1; - int retval = query_parser_parse_string(aux_query, - line, - parser_data->query_type_list, - parser_data->aux_buffer); + const int retval = query_parser_parse_string(aux_query, line, parser_data->aux_buffer); if (retval) { - query_instance_free(aux_query, parser_data->query_type_list); + query_instance_free(aux_query); parser_data->line_number++; return 0; /* Ignore parsing failures */ } - query_instance_set_number_in_file(aux_query, parser_data->line_number); - query_instance_list_add(parser_data->query_instance_list, - aux_query, - parser_data->query_type_list); - query_instance_free(aux_query, parser_data->query_type_list); + query_instance_set_line_in_file(aux_query, parser_data->line_number); + if (query_instance_list_add(parser_data->query_instance_list, aux_query)) { + query_instance_free(aux_query); + return 1; /* Allocation failure */ + } + query_instance_free(aux_query); parser_data->line_number++; return 0; } -query_instance_list_t *query_file_parser_parse(FILE *input, - const query_type_list_t *query_type_list) { - query_instance_list_t *list = query_instance_list_create(); +query_instance_list_t *query_file_parser_parse(FILE *input) { + query_instance_list_t *const list = query_instance_list_create(); if (!list) return NULL; query_file_parser_data_t parser_data = {.aux_buffer = g_ptr_array_new(), .line_number = 1, - .query_instance_list = list, - .query_type_list = query_type_list}; + .query_instance_list = list}; if (stream_tokenize(input, '\n', __query_file_parser_parse_query_callback, &parser_data)) { g_ptr_array_unref(parser_data.aux_buffer); - query_instance_list_free(list, query_type_list); + query_instance_list_free(list); return NULL; } diff --git a/trabalho-pratico/src/queries/query_instance.c b/trabalho-pratico/src/queries/query_instance.c index b06d3ee..fbd739d 100644 --- a/trabalho-pratico/src/queries/query_instance.c +++ b/trabalho-pratico/src/queries/query_instance.c @@ -28,64 +28,55 @@ /** * @struct query_instance - * @brief Instance of a query in a query file. + * @brief Instance of a query (in a file, or inputted by the user). * * @var query_instance::type * @brief The type of a query, i.e. what defines what it does, i.e. the first number in the * line. * @var query_instance::formatted * @brief If the query's output should be formatted (pretty printed). - * @var query_instance::number_in_file - * @brief When this query comes from a file, this is the number of the line the query was on. - * @var query_instance::argument_data_free - * @brief Method that should be used to free ::query_instance::argument_data. + * @var query_instance::line_in_file + * @brief The number of the line this query is in the input file (`1` for interactive mode). * @var query_instance::argument_data * @brief The arguments of this query, after being parsed by the specific query type. */ struct query_instance { - size_t type; - int formatted; - - size_t number_in_file; - - void *argument_data; + const query_type_t *type; + int formatted; + size_t line_in_file; + void *argument_data; }; query_instance_t *query_instance_create(void) { - query_instance_t *ret = malloc(sizeof(struct query_instance)); + query_instance_t *const ret = malloc(sizeof(query_instance_t)); if (!ret) return NULL; - /* Invalid data so that deallocations don't deal with uninitialized data */ - ret->type = 0; + /* Invalid data so that deallocations and clones don't deal with uninitialized data. */ + ret->type = NULL; ret->argument_data = NULL; return ret; } -query_instance_t *query_instance_clone(const query_instance_t *query, - const query_type_list_t *query_type_list) { - - query_instance_t *clone = malloc(sizeof(query_instance_t)); +query_instance_t *query_instance_clone(const query_instance_t *query) { + query_instance_t *const clone = malloc(sizeof(query_instance_t)); if (!clone) return NULL; memcpy(clone, query, sizeof(query_instance_t)); - const query_type_t *type = query_type_list_get_by_index(query_type_list, query->type); - if (!type) { - return NULL; /* Invalid query type */ - } else if (query->argument_data != NULL) { - query_type_clone_arguments_callback_t cb = query_type_get_clone_arguments_callback(type); - clone->argument_data = cb(query->argument_data); + if (query->type && query->argument_data) { + const query_type_clone_arguments_callback_t clone_cb = + query_type_get_clone_arguments_callback(query->type); + clone->argument_data = clone_cb(query->argument_data); if (!clone->argument_data) { free(clone); return NULL; } } - return clone; } -void query_instance_set_type(query_instance_t *query, size_t type) { +void query_instance_set_type(query_instance_t *query, const query_type_t *type) { query->type = type; } @@ -93,36 +84,29 @@ void query_instance_set_formatted(query_instance_t *query, int formatted) { query->formatted = formatted; } -void query_instance_set_number_in_file(query_instance_t *query, size_t number_in_file) { - query->number_in_file = number_in_file; +void query_instance_set_line_in_file(query_instance_t *query, size_t line_in_file) { + query->line_in_file = line_in_file; } -int query_instance_set_argument_data(query_instance_t *query, - const void *argument_data, - const query_type_list_t *query_type_list) { - - const query_type_t *type = query_type_list_get_by_index(query_type_list, query->type); - if (!type) { - return 1; /* Invalid query type */ - } +int query_instance_set_argument_data(query_instance_t *query, const void *argument_data) { + if (!query->type) + return 1; if (query->argument_data) { - query_type_free_query_instance_argument_data_callback_t free_cb = - query_type_get_free_query_instance_argument_data_callback(type); + const query_type_free_arguments_callback_t free_cb = + query_type_get_free_arguments_callback(query->type); free_cb(query->argument_data); - return 1; } - query_type_clone_arguments_callback_t clone_cb = query_type_get_clone_arguments_callback(type); - query->argument_data = clone_cb(argument_data); - if (!query->argument_data) { + query_type_clone_arguments_callback_t clone_cb = + query_type_get_clone_arguments_callback(query->type); + query->argument_data = clone_cb(argument_data); + if (!query->argument_data) return 1; - } - return 0; } -size_t query_instance_get_type(const query_instance_t *query) { +const query_type_t *query_instance_get_type(const query_instance_t *query) { return query->type; } @@ -130,22 +114,19 @@ int query_instance_get_formatted(const query_instance_t *query) { return query->formatted; } -size_t query_instance_get_number_in_file(const query_instance_t *query) { - return query->number_in_file; +size_t query_instance_get_line_in_file(const query_instance_t *query) { + return query->line_in_file; } const void *query_instance_get_argument_data(const query_instance_t *query) { return query->argument_data; } -void query_instance_free(query_instance_t *query, const query_type_list_t *query_type_list) { - const query_type_t *type = query_type_list_get_by_index(query_type_list, query->type); - if (!type) { - return; /* Invalid query type */ - } else if (query->argument_data != NULL) { - query_type_free_query_instance_argument_data_callback_t cb = - query_type_get_free_query_instance_argument_data_callback(type); - cb(query->argument_data); +void query_instance_free(query_instance_t *query) { + if (query->type) { + const query_type_free_arguments_callback_t free_cb = + query_type_get_free_arguments_callback(query->type); + free_cb(query->argument_data); } free(query); } diff --git a/trabalho-pratico/src/queries/query_instance_list.c b/trabalho-pratico/src/queries/query_instance_list.c index fd8b2e3..824be29 100644 --- a/trabalho-pratico/src/queries/query_instance_list.c +++ b/trabalho-pratico/src/queries/query_instance_list.c @@ -43,73 +43,58 @@ struct query_instance_list { }; query_instance_list_t *query_instance_list_create(void) { - query_instance_list_t *list = malloc(sizeof(struct query_instance_list)); + query_instance_list_t *const list = malloc(sizeof(query_instance_list_t)); if (!list) return NULL; - list->list = g_ptr_array_new(); + list->list = g_ptr_array_new_with_free_func((GDestroyNotify) query_instance_free); list->sorted = 1; - return list; } -query_instance_list_t *query_instance_list_clone(const query_instance_list_t *list, - const query_type_list_t *query_type_list) { +/** @brief `GCopyFunc` to copy a ::query_instance_t. */ +gpointer __query_instance_list_copy_query_instance(gconstpointer instance, gpointer user_data) { + /* TODO - check if this works */ + (void) user_data; + return query_instance_clone(instance); +} - query_instance_list_t *clone = malloc(sizeof(query_instance_list_t)); +query_instance_list_t *query_instance_list_clone(const query_instance_list_t *list) { + query_instance_list_t *const clone = malloc(sizeof(query_instance_list_t)); if (!clone) return NULL; clone->sorted = list->sorted; - clone->list = g_ptr_array_sized_new(list->list->len); - - for (guint i = 0; i < list->list->len; ++i) { - query_instance_t *ins = - query_instance_clone(g_ptr_array_index(list->list, i), query_type_list); - if (!ins) { /* Allocation error */ - for (guint j = 0; j < i; ++j) { - query_instance_free(g_ptr_array_index(clone->list, j), query_type_list); - } - - g_ptr_array_unref(clone->list); - free(clone); - return NULL; - } else { - g_ptr_array_add(clone->list, ins); - } - } - + clone->list = g_ptr_array_copy(list->list, __query_instance_list_copy_query_instance, NULL); return clone; } -void query_instance_list_add(query_instance_list_t *list, - const query_instance_t *query, - const query_type_list_t *query_type_list) { +int query_instance_list_add(query_instance_list_t *list, const query_instance_t *query) { + query_instance_t *const clone = query_instance_clone(query); + if (!clone) + return 1; - query_instance_t *clone = query_instance_clone(query, query_type_list); g_ptr_array_add(list->list, clone); list->sorted = 0; + return 0; } /** @brief Compares two query instances to order them by type. */ gint __query_instance_list_compare(gconstpointer a, gconstpointer b) { - ssize_t crit1 = (ssize_t) query_instance_get_type(*(const query_instance_t *const *) a) - - (ssize_t) query_instance_get_type(*(const query_instance_t *const *) b); + const ssize_t crit1 = (ssize_t) query_instance_get_type(*(const query_instance_t *const *) a) - + (ssize_t) query_instance_get_type(*(const query_instance_t *const *) b); if (crit1) return crit1; - ssize_t crit2 = - (ssize_t) query_instance_get_number_in_file(*(const query_instance_t *const *) a) - - (ssize_t) query_instance_get_number_in_file(*(const query_instance_t *const *) b); + const ssize_t crit2 = + (ssize_t) query_instance_get_line_in_file(*(const query_instance_t *const *) a) - + (ssize_t) query_instance_get_line_in_file(*(const query_instance_t *const *) b); return crit2; } int query_instance_list_iter_types(query_instance_list_t *list, query_instance_list_iter_types_callback callback, void *user_data) { - - /* TODO - check if this works */ - if (!list->sorted) { g_ptr_array_sort(list->list, __query_instance_list_compare); list->sorted = 1; @@ -120,13 +105,13 @@ int query_instance_list_iter_types(query_instance_list_t *list, query_instance_t *instance = g_ptr_array_index(list->list, 0); - size_t current_set_start = 0; - size_t current_set_type = query_instance_get_type(instance); /* First instance */ - size_t current_set_count = 1; + size_t current_set_start = 0; + const query_type_t *current_set_type = query_instance_get_type(instance); /* First instance */ + size_t current_set_count = 1; for (size_t i = 1; i < list->list->len; ++i) { - instance = g_ptr_array_index(list->list, i); - size_t type = query_instance_get_type(instance); + instance = g_ptr_array_index(list->list, i); + const query_type_t *type = query_instance_get_type(instance); if (type == current_set_type) { current_set_count++; @@ -135,7 +120,8 @@ int query_instance_list_iter_types(query_instance_list_t *list, const query_instance_t *const *current_set = (const query_instance_t *const *) &g_ptr_array_index(list->list, current_set_start); - int cb_ret = callback(user_data, current_set, current_set_count); + + const int cb_ret = callback(user_data, current_set_count, current_set); if (cb_ret) return cb_ret; } @@ -148,7 +134,7 @@ int query_instance_list_iter_types(query_instance_list_t *list, const query_instance_t *const *current_set = (const query_instance_t *const *) &g_ptr_array_index(list->list, current_set_start); - int cb_ret = callback(user_data, current_set, current_set_count); + const int cb_ret = callback(user_data, current_set_count, current_set); if (cb_ret) return cb_ret; @@ -164,11 +150,10 @@ int query_instance_list_iter(query_instance_list_t *list, } for (size_t i = 0; i < list->list->len; ++i) { - int retval = callback(user_data, g_ptr_array_index(list->list, i)); + const int retval = callback(user_data, g_ptr_array_index(list->list, i)); if (retval) return retval; } - return 0; } @@ -176,10 +161,7 @@ size_t query_instance_list_get_length(const query_instance_list_t *list) { return list->list->len; } -void query_instance_list_free(query_instance_list_t *list, - const query_type_list_t *query_type_list) { - for (size_t i = 0; i < list->list->len; ++i) - query_instance_free(g_ptr_array_index(list->list, i), query_type_list); +void query_instance_list_free(query_instance_list_t *list) { g_ptr_array_unref(list->list); free(list); } diff --git a/trabalho-pratico/src/queries/query_parser.c b/trabalho-pratico/src/queries/query_parser.c index 014abd9..d21b098 100644 --- a/trabalho-pratico/src/queries/query_parser.c +++ b/trabalho-pratico/src/queries/query_parser.c @@ -22,34 +22,32 @@ * See [the header file's documentation](@ref query_parser_examples). */ -#include +#include #include "queries/query_parser.h" #include "queries/query_tokenizer.h" +#include "queries/query_type_list.h" #include "utils/int_utils.h" /** * @struct query_parser_data_t - * @brief State of a query parser. + * @brief State of a query parser. * - * @var query_parser_data_t::query_type_list - * @brief List of supported queries. * @var query_parser_data_t::output - * @brief Current query being parsed. + * @brief Query being currently parsed. * @var query_parser_data_t::args - * @brief Array to accumulate query arguments. + * @brief Array to store pointers to query arguments (`char *`) to. * @var query_parser_data_t::first_token_parsed - * @brief Whether the first token (contains query number) has been parsed. + * @brief Whether the first token (containg the query type) has already been parsed. * @var query_parser_data_t::last_terminator - * @brief Where a null terminator (``'\0'``) should be placed, so that the last token ends. + * @brief Where a null terminator (``'\0'``) should be placed, so that the previous token ends; * `NULL` if no token has yet been parsed. */ typedef struct { - const query_type_list_t *query_type_list; - query_instance_t *output; - GPtrArray *args; - int first_token_parsed; - char *last_terminator; + query_instance_t *const output; + GPtrArray *args; + int first_token_parsed; + char *last_terminator; } query_parser_data_t; /** @@ -59,16 +57,16 @@ typedef struct { * @param user_data A pointer to a ::query_parser_data_t. * @param token Current query token being parsed. * - * @retval 0 Parsing success - * @retval 1 Parsing failure + * @retval 0 Parsing success. + * @retval 1 Parsing failure. */ int __query_parser_tokenize_callback(void *user_data, char *token) { - query_parser_data_t *parser = user_data; + query_parser_data_t *const parser = user_data; if (!parser->first_token_parsed) { /* First argument: query number */ /* Check if query is formatted */ - size_t token_len = strlen(token); + const size_t token_len = strlen(token); if (token_len == 0) return 1; @@ -80,49 +78,42 @@ int __query_parser_tokenize_callback(void *user_data, char *token) { } /* Parse number of query */ - uint64_t query_type; - int retval = int_utils_parse_positive(&query_type, token); - if (retval || !query_type_list_get_by_index(parser->query_type_list, (size_t) query_type)) { - if (token[token_len - 1] == '\0') { /* Restore previous string form */ + uint64_t query_type; + const int retval = int_utils_parse_positive(&query_type, token); + const query_type_t *const type = query_type_list_get_by_index((size_t) query_type); + if (retval || !type) { + if (token[token_len - 1] == '\0') /* Restore previous string form */ token[token_len - 1] = 'F'; - } - return 1; } - query_instance_set_type(parser->output, (size_t) query_type); + query_instance_set_type(parser->output, type); - if (token[token_len - 1] == '\0') { /* Restore previous string form */ + if (token[token_len - 1] == '\0') /* Restore previous string form */ token[token_len - 1] = 'F'; - } parser->first_token_parsed = 1; } else { if (parser->last_terminator) *parser->last_terminator = '\0'; parser->last_terminator = token + strlen(token); - g_ptr_array_add(parser->args, token); } - return 0; } -int query_parser_parse_string(query_instance_t *output, - char *input, - const query_type_list_t *query_type_list, - GPtrArray *aux) { - - query_parser_data_t parser_data = {.query_type_list = query_type_list, - .output = output, +int query_parser_parse_string(query_instance_t *output, char *input, GPtrArray *aux) { + query_parser_data_t parser_data = {.output = output, .first_token_parsed = 0, .last_terminator = NULL}; - if (aux) + if (aux) { + g_ptr_array_set_size(aux, 0); parser_data.args = aux; - else + } else { parser_data.args = g_ptr_array_new(); - parser_data.args->len = 0; + } /* Query type parsing */ - int retval = query_tokenizer_tokenize(input, __query_parser_tokenize_callback, &parser_data); + const int retval = + query_tokenizer_tokenize(input, __query_parser_tokenize_callback, &parser_data); if (retval || !parser_data.first_token_parsed) { if (!aux) g_ptr_array_free(parser_data.args, TRUE); @@ -133,21 +124,21 @@ int query_parser_parse_string(query_instance_t *output, *parser_data.last_terminator = '\0'; /* Argument parsing */ - const query_type_t *query_type = - query_type_list_get_by_index(query_type_list, query_instance_get_type(output)); - void *argument_data = query_type_get_parse_arguments_callback( - query_type)((char *const *) parser_data.args->pdata, parser_data.args->len); + const query_type_t *const query_type = query_instance_get_type(output); + const query_type_parse_arguments_callback_t parse_cb = + query_type_get_parse_arguments_callback(query_type); + void *const argument_data = + parse_cb(parser_data.args->len, (char *const *) parser_data.args->pdata); if (!argument_data) { /* Argument parsing failure */ if (!aux) g_ptr_array_free(parser_data.args, TRUE); return 1; } + query_instance_set_argument_data(output, argument_data); - query_instance_set_argument_data(output, argument_data, query_type_list); - - query_type_free_query_instance_argument_data_callback_t free_cb = - query_type_get_free_query_instance_argument_data_callback(query_type); + const query_type_free_arguments_callback_t free_cb = + query_type_get_free_arguments_callback(query_type); free_cb(argument_data); if (!aux) @@ -155,16 +146,12 @@ int query_parser_parse_string(query_instance_t *output, return 0; } -int query_parser_parse_string_const(query_instance_t *output, - const char *input, - const query_type_list_t *query_type_list, - GPtrArray *aux) { - char *buffer = strdup(input); +int query_parser_parse_string_const(query_instance_t *output, const char *input, GPtrArray *aux) { + char *const buffer = strdup(input); if (!buffer) - return 1; - - int retval = query_parser_parse_string(output, buffer, query_type_list, aux); + return QUERY_PARSER_PARSE_CONST_RET_FAILED_MALLOC; + const int retval = query_parser_parse_string(output, buffer, aux); free(buffer); return retval != 0; } diff --git a/trabalho-pratico/src/queries/query_tokenizer.c b/trabalho-pratico/src/queries/query_tokenizer.c index ec65ae9..cb2339c 100644 --- a/trabalho-pratico/src/queries/query_tokenizer.c +++ b/trabalho-pratico/src/queries/query_tokenizer.c @@ -30,7 +30,7 @@ /** * @struct query_tokenizer_data_t - * @brief Contains the tokenizer's state. + * @brief Contains the tokenizer's state. * * @var query_tokenizer_data_t::user_data * @brief `user_data` parameter in ::query_tokenizer_tokenize. @@ -48,14 +48,14 @@ typedef struct { } query_tokenizer_data_t; /** - * @brief Handles space-separated tokens from a string. + * @brief Handles space-separated tokens from a string. * @details Auxiliary method for ::query_tokenizer_tokenize. * * @param tokenizer_data A pointer to a ::query_tokenizer_data_t. * @param token Space-separated token to process (take quotes into account). */ int __query_tokenizer_handle_space_split(void *tokenizer_data, char *token) { - query_tokenizer_data_t *tokenizer = (query_tokenizer_data_t *) tokenizer_data; + query_tokenizer_data_t *const tokenizer = tokenizer_data; if (*token == '\0') /* Skip empty tokens */ return 0; @@ -64,11 +64,11 @@ int __query_tokenizer_handle_space_split(void *tokenizer_data, char *token) { tokenizer->quote_token = token + 1; if (tokenizer->quote_token) { - size_t token_length = strlen(token); + const size_t token_length = strlen(token); if (token[token_length - 1] == '"') { token[token_length - 1] = '\0'; - int cb_result = tokenizer->callback(tokenizer->user_data, tokenizer->quote_token); + const int cb_result = tokenizer->callback(tokenizer->user_data, tokenizer->quote_token); token[token_length - 1] = '"'; /* Restore string */ tokenizer->quote_token = NULL; @@ -77,7 +77,7 @@ int __query_tokenizer_handle_space_split(void *tokenizer_data, char *token) { return cb_result; } } else { - int cb_result = tokenizer->callback(tokenizer->user_data, token); + const int cb_result = tokenizer->callback(tokenizer->user_data, token); if (cb_result) return cb_result; } @@ -89,25 +89,22 @@ int query_tokenizer_tokenize(char *input, tokenize_iter_callback_t callback, voi query_tokenizer_data_t tokenizer_data = {.user_data = user_data, .callback = callback, .quote_token = NULL}; - - int retval = string_tokenize(input, ' ', __query_tokenizer_handle_space_split, &tokenizer_data); + const int retval = + string_tokenize(input, ' ', __query_tokenizer_handle_space_split, &tokenizer_data); if (retval) return retval; - (void) callback; - (void) user_data; return 0; } int query_tokenizer_tokenize_const(const char *input, tokenize_iter_callback_t callback, void *user_data) { - char *buffer = strdup(input); + char *const buffer = strdup(input); if (!buffer) return QUERY_TOKENIZER_TOKENIZE_CONST_RET_FAILED_MALLOC; - int retval = query_tokenizer_tokenize(buffer, callback, user_data); - + const int retval = query_tokenizer_tokenize(buffer, callback, user_data); free(buffer); return retval; } diff --git a/trabalho-pratico/src/queries/query_type.c b/trabalho-pratico/src/queries/query_type.c index 6074709..980c565 100644 --- a/trabalho-pratico/src/queries/query_type.c +++ b/trabalho-pratico/src/queries/query_type.c @@ -31,12 +31,14 @@ * @struct query_type * @brief A query definition based on its behavior. * + * @var query_type::type_number + * @brief A number that identifies this query type. * @var query_type::parse_arguments * @brief Method that parses query arguments and generates ::query_instance::argument_data. * @var query_type::clone_arguments - * @brief Method that clones query arguments, generated by ::query_instance::parse_arguments. - * @var query_type::free_query_instance_argument_data - * @brief Method that frees data in ::query_instance::argument_data. + * @brief Method that clones query arguments, generated by ::query_type::parse_arguments. + * @var query_type::free_arguments + * @brief Method that frees data returned by ::query_type::parse_arguments. * @var query_type::generate_statistics * @brief Method that generates statistical data for all queries of the same type. * @var query_type::free_statistics @@ -45,9 +47,11 @@ * @brief Method that executes a single query. */ struct query_type { - query_type_parse_arguments_callback_t parse_arguments; - query_type_clone_arguments_callback_t clone_arguments; - query_type_free_query_instance_argument_data_callback_t free_query_instance_argument_data; + size_t type_number; + + query_type_parse_arguments_callback_t parse_arguments; + query_type_clone_arguments_callback_t clone_arguments; + query_type_free_arguments_callback_t free_arguments; query_type_generate_statistics_callback_t generate_statistics; query_type_free_statistics_callback_t free_statistics; @@ -55,30 +59,31 @@ struct query_type { query_type_execute_callback_t execute; }; -query_type_t *query_type_create( - query_type_parse_arguments_callback_t parse_arguments, - query_type_clone_arguments_callback_t clone_arguments, - query_type_free_query_instance_argument_data_callback_t free_query_instance_argument_data, - query_type_generate_statistics_callback_t generate_statistics, - query_type_free_statistics_callback_t free_statistics, - query_type_execute_callback_t execute) { +query_type_t *query_type_create(size_t type_number, + query_type_parse_arguments_callback_t parse_arguments, + query_type_clone_arguments_callback_t clone_arguments, + query_type_free_arguments_callback_t free_arguments, + query_type_generate_statistics_callback_t generate_statistics, + query_type_free_statistics_callback_t free_statistics, + query_type_execute_callback_t execute) { - query_type_t *query = malloc(sizeof(query_type_t)); + query_type_t *const query = malloc(sizeof(query_type_t)); if (!query) return NULL; - query->parse_arguments = parse_arguments; - query->clone_arguments = clone_arguments; - query->free_query_instance_argument_data = free_query_instance_argument_data; - query->generate_statistics = generate_statistics; - query->free_statistics = free_statistics; - query->execute = execute; + query->type_number = type_number; + query->parse_arguments = parse_arguments; + query->clone_arguments = clone_arguments; + query->free_arguments = free_arguments; + query->generate_statistics = generate_statistics; + query->free_statistics = free_statistics; + query->execute = execute; return query; } query_type_t *query_type_clone(const query_type_t *type) { - query_type_t *clone = malloc(sizeof(query_type_t)); + query_type_t *const clone = malloc(sizeof(query_type_t)); if (!clone) return NULL; @@ -86,6 +91,10 @@ query_type_t *query_type_clone(const query_type_t *type) { return clone; } +size_t query_type_get_type_number(const query_type_t *type) { + return type->type_number; +} + query_type_parse_arguments_callback_t query_type_get_parse_arguments_callback(const query_type_t *type) { @@ -98,10 +107,10 @@ query_type_clone_arguments_callback_t return type->clone_arguments; } -query_type_free_query_instance_argument_data_callback_t - query_type_get_free_query_instance_argument_data_callback(const query_type_t *type) { +query_type_free_arguments_callback_t + query_type_get_free_arguments_callback(const query_type_t *type) { - return type->free_query_instance_argument_data; + return type->free_arguments; } query_type_generate_statistics_callback_t diff --git a/trabalho-pratico/src/queries/query_type_list.c b/trabalho-pratico/src/queries/query_type_list.c index d7db90a..f2c1ab3 100644 --- a/trabalho-pratico/src/queries/query_type_list.c +++ b/trabalho-pratico/src/queries/query_type_list.c @@ -18,12 +18,11 @@ * @file query_type_list.c * @brief Implementation of methods in include/queries/query_type_list.h * - * #### Examples - * See [the header file's documentation](@ref query_type_list_examples). + * ### Examples + * See [the header file's documentation](@ref query_type_list_example). */ -#include -#include +#include "queries/query_type_list.h" #include "queries/q01.h" #include "queries/q02.h" @@ -35,77 +34,45 @@ #include "queries/q08.h" #include "queries/q09.h" #include "queries/q10.h" -#include "queries/query_type_list.h" /** - * @struct query_type_list - * @brief Container structure for the list of all supported queries. + * @brief List of all known queries. + * @details Shall not be modified apart from its creation. It's not constant because it requires + * run-time initialization. This global variable is justified for the following reasons: * - * @var query_type_list::list - * @brief List of all supported queries. + * -# It's not modified (no mutable global state); + * -# It's not directly exposed to other modules (very limited scope); + * -# It's a list of `vtable`s (dispatch tables). In other languages (such as C++), it'd be + * global; + * -# It'd be very inefficient for every polymorphic object to have its own copy of its + * `vtable`. */ -struct query_type_list { - query_type_t *list[QUERY_TYPE_LIST_COUNT]; -}; - -query_type_list_t *query_type_list_create(void) { - query_type_list_t *list = malloc(sizeof(struct query_type_list)); - if (!list) - return NULL; - - query_type_t *(*constructors[QUERY_TYPE_LIST_COUNT])(void) = {q01_create, - q02_create, - q03_create, - q04_create, - q05_create, - q06_create, - q07_create, - q08_create, - q09_create, - q10_create}; +query_type_t *__query_type_list[QUERY_TYPE_LIST_COUNT]; - for (size_t i = 0; i < QUERY_TYPE_LIST_COUNT; ++i) { - list->list[i] = constructors[i](); - - if (!list->list[i]) { /* Allocation failure */ - for (size_t j = 0; j < i; ++j) - query_type_free(list->list[j]); - free(list); - return NULL; - } - } - - return list; +/** @brief Automatically initializes ::__query_type_list when the program starts. */ +void __attribute__((constructor)) __query_type_list_create(void) { + query_type_t *(*const constructors[QUERY_TYPE_LIST_COUNT])(void) = {q01_create, + q02_create, + q03_create, + q04_create, + q05_create, + q06_create, + q07_create, + q08_create, + q09_create, + q10_create}; + for (size_t i = 0; i < QUERY_TYPE_LIST_COUNT; ++i) + __query_type_list[i] = constructors[i](); } -query_type_list_t *query_type_list_clone(const query_type_list_t *query_type_list) { - query_type_list_t *clone = malloc(sizeof(query_type_list_t)); - if (!clone) - return NULL; - - for (size_t i = 0; i < QUERY_TYPE_LIST_COUNT; ++i) { - clone->list[i] = query_type_clone(query_type_list->list[i]); - - if (!clone->list[i]) { /* Allocation failure */ - for (size_t j = 0; j < i; ++j) - query_type_free(clone->list[j]); - free(clone); - return NULL; - } - } - - return clone; +/** @brief Automatically `free`s ::__query_type_list when the program terminates. */ +void __attribute__((destructor)) __query_type_list_free(void) { + for (size_t i = 0; i < QUERY_TYPE_LIST_COUNT; ++i) + query_type_free(__query_type_list[i]); } -const query_type_t *query_type_list_get_by_index(const query_type_list_t *query_type_list, - size_t index) { +const query_type_t *query_type_list_get_by_index(size_t index) { if (1 <= index && index <= QUERY_TYPE_LIST_COUNT) - return query_type_list->list[index - 1]; + return __query_type_list[index - 1]; return NULL; } - -void query_type_list_free(query_type_list_t *query_type_list) { - for (size_t i = 0; i < QUERY_TYPE_LIST_COUNT; ++i) - query_type_free(query_type_list->list[i]); - free(query_type_list); -} diff --git a/trabalho-pratico/src/queries/query_writer.c b/trabalho-pratico/src/queries/query_writer.c index 0f2d95d..d2800dd 100644 --- a/trabalho-pratico/src/queries/query_writer.c +++ b/trabalho-pratico/src/queries/query_writer.c @@ -24,8 +24,8 @@ #include #include +#include #include -#include #include "queries/query_writer.h" #include "utils/string_pool.h" @@ -38,7 +38,7 @@ * @brief Where to write query outputs to. May be `NULL` (see ::query_writer::lines in that * case). * @var query_writer::formatted - * @brief If the output of the query should be formatted (pretty printed). + * @brief Whether the output of the query should be formatted (pretty printed). * @var query_writer::is_first_field * @brief Whether the next field to be printed is the first field of the current object. * @var query_writer::current_object @@ -51,7 +51,7 @@ * @var query_writer::current_line * @brief Current line being printed. Used for outputting non-formatted query results to strings. * @var query_writer::current_line_cursor - * @brief Position to start writing in ::query_write::current_line. + * @brief Position (in characters) where to start writing to ::query_writer::current_line. */ struct query_writer { FILE *stream; @@ -72,7 +72,7 @@ struct query_writer { #define QUERY_WRITER_STRING_POOL_BLOCK_SIZE (1 << 17) query_writer_t *query_writer_create(const char *out_file_path, int formatted) { - query_writer_t *ret = malloc(sizeof(query_writer_t)); + query_writer_t *const ret = malloc(sizeof(query_writer_t)); if (!ret) return NULL; @@ -116,8 +116,7 @@ void query_writer_write_new_object(query_writer_t *writer) { if (writer->current_object != 1) { if (writer->formatted) { /* Spacing after last item (don't add spacing to the beginning of the file) */ - char *empty = string_pool_put(writer->strings, ""); - g_ptr_array_add(writer->lines, empty); + g_ptr_array_add(writer->lines, string_pool_put(writer->strings, "")); } else { /* Flush last time (it's invalid for the first object) */ g_ptr_array_add(writer->lines, @@ -163,29 +162,24 @@ void query_writer_write_new_field(query_writer_t *writer, } else { if (writer->formatted) { /* Print line "key: value" */ - char line[LINE_MAX]; - size_t len = snprintf(line, LINE_MAX, "%s: ", key); + char line[LINE_MAX]; + const size_t len = snprintf(line, LINE_MAX, "%s: ", key); vsnprintf(line + len, LINE_MAX - len, format, printf_args); - - char *pool_line = string_pool_put(writer->strings, line); - g_ptr_array_add(writer->lines, pool_line); + g_ptr_array_add(writer->lines, string_pool_put(writer->strings, line)); } else { /* * Print only values, adding semicolons between them. This is done to * writer->current_line, which, when completed, is flushed to writer->lines. */ - if (!writer->is_first_field) { - writer->current_line[writer->current_line_cursor] = ';'; - writer->current_line_cursor++; - } + if (!writer->is_first_field) + writer->current_line[writer->current_line_cursor++] = ';'; writer->current_line_cursor += vsnprintf(writer->current_line + writer->current_line_cursor, LINE_MAX - writer->current_line_cursor, format, printf_args); - writer->is_first_field = 0; } } @@ -219,6 +213,5 @@ void query_writer_free(query_writer_t *writer) { string_pool_free(writer->strings); g_ptr_array_unref(writer->lines); } - free(writer); } From 353977f217af2bdd3866c884978d8a1b4dabe3ce Mon Sep 17 00:00:00 2001 From: voidbert <50591320+voidbert@users.noreply.github.com> Date: Sun, 21 Jan 2024 01:47:45 +0000 Subject: [PATCH 02/19] Adapt other modules to fit to new query subsystem --- trabalho-pratico/src/batch_mode.c | 22 +++---------- .../src/interactive_mode/interactive_mode.c | 32 ++++++------------- trabalho-pratico/src/queries/q01.c | 7 ++-- trabalho-pratico/src/queries/q02.c | 7 ++-- trabalho-pratico/src/queries/q03.c | 15 +++++---- trabalho-pratico/src/queries/q04.c | 13 ++++---- trabalho-pratico/src/queries/q05.c | 15 +++++---- trabalho-pratico/src/queries/q06.c | 13 ++++---- trabalho-pratico/src/queries/q07.c | 15 +++++---- trabalho-pratico/src/queries/q08.c | 15 +++++---- trabalho-pratico/src/queries/q09.c | 13 ++++---- trabalho-pratico/src/queries/q10.c | 16 ++++++---- 12 files changed, 83 insertions(+), 100 deletions(-) diff --git a/trabalho-pratico/src/batch_mode.c b/trabalho-pratico/src/batch_mode.c index 0eed343..0b4cca2 100644 --- a/trabalho-pratico/src/batch_mode.c +++ b/trabalho-pratico/src/batch_mode.c @@ -54,7 +54,7 @@ int __batch_mode_init_file_callback(void *user_data, const query_instance_t *ins /* Parent directory creation is assured by error file output while loading the dataset */ char path[PATH_MAX]; - sprintf(path, "Resultados/command%zu_output.txt", query_instance_get_number_in_file(instance)); + sprintf(path, "Resultados/command%zu_output.txt", query_instance_get_line_in_file(instance)); iter_data->outputs[iter_data->i] = query_writer_create(path, query_instance_get_formatted(instance)); @@ -76,13 +76,6 @@ int batch_mode_run(const char *dataset_dir, int retval = 0; - query_type_list_t *const query_type_list = query_type_list_create(); - if (!query_type_list) { - retval = 1; - fputs("Failed to allocate query definitions!\n", stderr); - goto DEFER_0; - } - FILE *const query_file = fopen(query_file_path, "r"); if (!query_file) { retval = 1; @@ -90,8 +83,7 @@ int batch_mode_run(const char *dataset_dir, goto DEFER_1; } - query_instance_list_t *const query_instance_list = - query_file_parser_parse(query_file, query_type_list); + query_instance_list_t *const query_instance_list = query_file_parser_parse(query_file); if (!query_instance_list) { retval = 1; fputs("Failed to allocate list of queries!\n", stderr); @@ -128,11 +120,7 @@ int batch_mode_run(const char *dataset_dir, goto DEFER_5; } - query_dispatcher_dispatch_list(database, - query_instance_list, - query_type_list, - query_outputs, - metrics); + query_dispatcher_dispatch_list(database, query_instance_list, query_outputs, metrics); for (size_t i = 0; i < query_instance_list_get_length(query_instance_list); ++i) query_writer_free(query_outputs[i]); @@ -142,11 +130,9 @@ int batch_mode_run(const char *dataset_dir, DEFER_4: database_free(database); DEFER_3: - query_instance_list_free(query_instance_list, query_type_list); + query_instance_list_free(query_instance_list); DEFER_2: fclose(query_file); DEFER_1: - query_type_list_free(query_type_list); -DEFER_0: return retval; } diff --git a/trabalho-pratico/src/interactive_mode/interactive_mode.c b/trabalho-pratico/src/interactive_mode/interactive_mode.c index 12535c0..6ee6cca 100644 --- a/trabalho-pratico/src/interactive_mode/interactive_mode.c +++ b/trabalho-pratico/src/interactive_mode/interactive_mode.c @@ -104,11 +104,9 @@ void __interactive_mode_load_dataset(database_t **database) { /** * @brief Method called when the user chooses to run a query in the main menu. - * - * @param query_type_list List of known query types (query definitions). - * @param database Database to be queried. + * @param database Database to be queried. */ -void __interactive_mode_run_query(query_type_list_t *query_type_list, const database_t *database) { +void __interactive_mode_run_query(const database_t *database) { if (!database) { activity_messagebox_run("Please load a dataset first!"); return; @@ -123,11 +121,11 @@ void __interactive_mode_run_query(query_type_list_t *query_type_list, const data } query_instance_t *query_parsed = query_instance_create(); - if (query_parser_parse_string_const(query_parsed, query_str, query_type_list, NULL)) { + if (query_parser_parse_string_const(query_parsed, query_str, NULL)) { g_free(query_old_str); query_old_str = query_str; - query_instance_free(query_parsed, query_type_list); + query_instance_free(query_parsed); activity_messagebox_run("Failed to parse query."); } else { query_writer_t *writer = @@ -135,11 +133,8 @@ void __interactive_mode_run_query(query_type_list_t *query_type_list, const data if (!writer) { activity_messagebox_run("Failed to create writer for query output."); } else { - /* TODO - fix cast when query system is fixed */ - query_dispatcher_dispatch_single((database_t *) database, - query_parsed, - query_type_list, - writer); + /* TODO - handle allocation errors */ + query_dispatcher_dispatch_single(database, query_parsed, writer); size_t nlines; const char *const *lines = query_writer_get_lines(writer, &nlines); activity_paging_run(lines, nlines, query_instance_get_formatted(query_parsed)); @@ -147,7 +142,7 @@ void __interactive_mode_run_query(query_type_list_t *query_type_list, const data query_writer_free(writer); } - query_instance_free(query_parsed, query_type_list); + query_instance_free(query_parsed); g_free(query_old_str); g_free(query_str); return; @@ -166,16 +161,8 @@ int __interactive_mode_terminate_ncurses(void) { } int interactive_mode_run(void) { - query_type_list_t *query_type_list = query_type_list_create(); - if (!query_type_list) { - fputs("Failed to allocate query definitions!\n", stderr); + if (__interactive_mode_init_ncurses()) return 1; - } - - if (__interactive_mode_init_ncurses()) { - query_type_list_free(query_type_list); - return 1; - } database_t *database = NULL; @@ -187,10 +174,9 @@ int interactive_mode_run(void) { __interactive_mode_load_dataset(&database); break; case ACTIVITY_MAIN_MENU_RUN_QUERY: - __interactive_mode_run_query(query_type_list, database); + __interactive_mode_run_query(database); break; case ACTIVITY_MAIN_MENU_LEAVE: - query_type_list_free(query_type_list); if (database) database_free(database); diff --git a/trabalho-pratico/src/queries/q01.c b/trabalho-pratico/src/queries/q01.c index b1c1957..de48778 100644 --- a/trabalho-pratico/src/queries/q01.c +++ b/trabalho-pratico/src/queries/q01.c @@ -52,12 +52,12 @@ typedef struct { * @brief Parses the arguments of a query of type 1. * @details Asserts there's only one string argument, that is stored. * - * @param argv Values of the arguments. * @param argc Number of arguments. + * @param argv Values of the arguments. * * @return `NULL` for invalid arguments, a copy of the only @p argv and its identifier on success. */ -void *__q01_parse_arguments(char *const *argv, size_t argc) { +void *__q01_parse_arguments(size_t argc, char *const argv[argc]) { if (argc != 1) return NULL; @@ -345,7 +345,8 @@ int __q01_execute(const database_t *database, } query_type_t *q01_create(void) { - return query_type_create(__q01_parse_arguments, + return query_type_create(1, + __q01_parse_arguments, __q01_clone_arguments, __q01_free_query_instance_argument_data, NULL, diff --git a/trabalho-pratico/src/queries/q02.c b/trabalho-pratico/src/queries/q02.c index 5759243..070bbd1 100644 --- a/trabalho-pratico/src/queries/q02.c +++ b/trabalho-pratico/src/queries/q02.c @@ -53,12 +53,12 @@ typedef struct { * @details The first argument, a user identifier, is mandatory. A optional second argument, taking * the value of either `"flights"` or `"reservations"`, is allowed. * - * @param argv Values of the arguments. * @param argc Number of arguments. + * @param argv Values of the arguments. * * @return `NULL` on failure, a pointer to a `q02_argument_data_t` otherwise. */ -void *__q02_parse_arguments(char *const *argv, size_t argc) { +void *__q02_parse_arguments(size_t argc, char *const argv[argc]) { if (argc == 1) { q02_argument_data_t *ret = malloc(sizeof(q02_argument_data_t)); ret->user_id = strdup(argv[0]); @@ -260,7 +260,8 @@ int __q02_execute(const database_t *database, } query_type_t *q02_create(void) { - return query_type_create(__q02_parse_arguments, + return query_type_create(2, + __q02_parse_arguments, __q02_clone_arguments, __q02_free_query_instance_argument_data, NULL, diff --git a/trabalho-pratico/src/queries/q03.c b/trabalho-pratico/src/queries/q03.c index efe6a0b..3ea793b 100644 --- a/trabalho-pratico/src/queries/q03.c +++ b/trabalho-pratico/src/queries/q03.c @@ -31,12 +31,12 @@ * @brief Parses arguments for the third query. * @details Asserts that there's only one argument, an hotel identifier. * - * @param argv Values of the arguments. * @param argc Number of arguments. + * @param argv Values of the arguments. * * @return `NULL` on failure, a pointer to a hotel ID otherwise. */ -void *__q03_parse_arguments(char *const *argv, size_t argc) { +void *__q03_parse_arguments(size_t argc, char *const argv[argc]) { if (argc != 1) return NULL; @@ -107,15 +107,15 @@ int __q03_generate_statistics_foreach_reservation(void *user_data * @brief Generates statistical data for queries of type 3. * * @param database Database, to iterate through reservations. - * @param instances Query instances that will need to be executed. * @param n Number of query instances that will need to be executed. + * @param instances Query instances that will need to be executed. * * @return A `GHashTable` that associates hotel identifiers with ::q03_average_t for those hotels, * or `NULL` on failure. */ -void *__q03_generate_statistics(const database_t *database, - const query_instance_t *const *instances, - size_t n) { +void *__q03_generate_statistics(const database_t *database, + size_t n, + const query_instance_t *const instances[n]) { GHashTable *ratings_averages = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) free); @@ -173,7 +173,8 @@ int __q03_execute(const database_t *database, } query_type_t *q03_create(void) { - return query_type_create(__q03_parse_arguments, + return query_type_create(3, + __q03_parse_arguments, __q03_clone_arguments, free, __q03_generate_statistics, diff --git a/trabalho-pratico/src/queries/q04.c b/trabalho-pratico/src/queries/q04.c index 18a9dd2..e581d1d 100644 --- a/trabalho-pratico/src/queries/q04.c +++ b/trabalho-pratico/src/queries/q04.c @@ -36,7 +36,7 @@ * * @return `NULL` on failure, a pointer to a hotel ID otherwise. */ -void *__q04_parse_arguments(char *const *argv, size_t argc) { +void *__q04_parse_arguments(size_t argc, char *const argv[argc]) { if (argc != 1) return NULL; @@ -112,14 +112,14 @@ gint __q04_sort_reservations_by_date(gconstpointer a, gconstpointer b) { * @brief Generates statistical data for queries of type 4. * * @param database Database, to iterate through reservations. - * @param instances Instances of the query. * @param n Number of instances. + * @param instances Instances of the query. * * @return A `GHashTable` associating hotel identifiers to `GPtrArray`s of `reservation_t`s. */ -void *__q04_generate_statistics(const database_t *database, - const query_instance_t *const *instances, - size_t n) { +void *__q04_generate_statistics(const database_t *database, + size_t n, + const query_instance_t *const instances[n]) { GHashTable *hotel_reservations = g_hash_table_new_full(g_direct_hash, g_direct_equal, @@ -201,7 +201,8 @@ int __q04_execute(const database_t *database, } query_type_t *q04_create(void) { - return query_type_create(__q04_parse_arguments, + return query_type_create(4, + __q04_parse_arguments, __q04_clone_arguments, free, __q04_generate_statistics, diff --git a/trabalho-pratico/src/queries/q05.c b/trabalho-pratico/src/queries/q05.c index 70366c3..ecacd62 100644 --- a/trabalho-pratico/src/queries/q05.c +++ b/trabalho-pratico/src/queries/q05.c @@ -47,12 +47,12 @@ typedef struct { * @brief Parses arguments for the fifth query. * @details Asserts that there's three arguments, an airport code, and two dates with time. * - * @param argv Values of the arguments. * @param argc Number of arguments. + * @param argv Values of the arguments. * * @return `NULL` on failure, a pointer to a `q05_airport_data_t` otherwise. */ -void *__q05_parse_arguments(char *const *argv, size_t argc) { +void *__q05_parse_arguments(size_t argc, char *const argv[argc]) { if (argc != 3) return NULL; @@ -137,15 +137,15 @@ int __q05_generate_statistics_foreach_flight(void *user_data, const flight_t *fl * @brief Generates statistical data for queries of type 5. * * @param database Database, to iterate through flights. - * @param instances Instances of the query 5. * @param n Number of query instances. + * @param instances Instances of the query 5. * * @return A `GHashTable` associating a ::q05_foreach_airport_data_t for each query to a * `GPtrArray` of ::flight_t `*`s. */ -void *__q05_generate_statistics(const database_t *database, - const query_instance_t *const *instances, - size_t n) { +void *__q05_generate_statistics(const database_t *database, + size_t n, + const query_instance_t *const instances[n]) { GPtrArray *filter_data = g_ptr_array_new(); GHashTable *origin_flights = g_hash_table_new_full(g_direct_hash, g_direct_equal, @@ -249,7 +249,8 @@ int __q05_execute(const database_t *database, } query_type_t *q05_create(void) { - return query_type_create(__q05_parse_arguments, + return query_type_create(5, + __q05_parse_arguments, __q05_clone_arguments, free, __q05_generate_statistics, diff --git a/trabalho-pratico/src/queries/q06.c b/trabalho-pratico/src/queries/q06.c index a949c3f..3792e9d 100644 --- a/trabalho-pratico/src/queries/q06.c +++ b/trabalho-pratico/src/queries/q06.c @@ -51,7 +51,7 @@ typedef struct { * * @return A pointer to a ::q06_parsed_arguments_t, or `NULL` on failure. */ -void *__q06_parse_arguments(char *const *argv, size_t argc) { +void *__q06_parse_arguments(size_t argc, char *const argv[argc]) { if (argc != 2) return NULL; @@ -227,15 +227,15 @@ void __q06_generate_statistics_foreach_year(gpointer key_year, * ::q06_array_item_t (airport + passenger count tuples). * * @param database Database to get data from. - * @param instances Array of query instances to be executed. * @param n Number of query instances to be executed. + * @param instances Array of query instances to be executed. * * @return A `GHashTable` that associates years with sorted `GArray`s of ::q06_array_item_t * (airport + passenger count tuples). */ -void *__q06_generate_statistics(const database_t *database, - const query_instance_t *const *instances, - size_t n) { +void *__q06_generate_statistics(const database_t *database, + size_t n, + const query_instance_t *const instances[n]) { GHashTable *years_airport_count = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, @@ -307,7 +307,8 @@ int __q06_execute(const database_t *database, } query_type_t *q06_create(void) { - return query_type_create(__q06_parse_arguments, + return query_type_create(6, + __q06_parse_arguments, __q06_clone_arguments, free, __q06_generate_statistics, diff --git a/trabalho-pratico/src/queries/q07.c b/trabalho-pratico/src/queries/q07.c index fbe7e21..3820229 100644 --- a/trabalho-pratico/src/queries/q07.c +++ b/trabalho-pratico/src/queries/q07.c @@ -32,12 +32,12 @@ * @brief Parses the arguments of a query of type 7. * @details Asserts there's only one integer argument, that is stored. * - * @param argv Values of the arguments. * @param argc Number of arguments. + * @param argv Values of the arguments. * * @return `NULL` for invalid arguments, a `malloc`-allocated `uint64_t` otherwise. */ -void *__q07_parse_arguments(char *const *argv, size_t argc) { +void *__q07_parse_arguments(size_t argc, char *const argv[argc]) { if (argc != 1) return NULL; @@ -172,14 +172,14 @@ gint __q07_generate_statistics_airport_median_compare_func(gconstpointer a, gcon * @brief Generates statistical data for queries of type 7. * * @param database Database, to iterate through flight. - * @param instances Query instances that will need to be executed. * @param n Number of query instances that will need to be executed. + * @param instances Query instances that will need to be executed. * * @return A sorted `GArray` of ::__q07_airport_median. */ -void *__q07_generate_statistics(const database_t *database, - const query_instance_t *const *instances, - size_t n) { +void *__q07_generate_statistics(const database_t *database, + size_t n, + const query_instance_t *const instances[n]) { (void) instances; (void) n; @@ -237,7 +237,8 @@ int __q07_execute(const database_t *database, } query_type_t *q07_create(void) { - return query_type_create(__q07_parse_arguments, + return query_type_create(7, + __q07_parse_arguments, __q07_clone_arguments, free, __q07_generate_statistics, diff --git a/trabalho-pratico/src/queries/q08.c b/trabalho-pratico/src/queries/q08.c index ce56903..19a6e3a 100644 --- a/trabalho-pratico/src/queries/q08.c +++ b/trabalho-pratico/src/queries/q08.c @@ -47,12 +47,12 @@ typedef struct { * @brief Parses arguments for query 8. * @details Asserts that there's three arguments, an hotel identifier, and two dates. * - * @param argv Values of the arguments. * @param argc Number of arguments. + * @param argv Values of the arguments. * * @return `NULL` on failure, a pointer to a `q08_airport_data_t` otherwise. */ -void *__q08_parse_arguments(char *const *argv, size_t argc) { +void *__q08_parse_arguments(size_t argc, char *const argv[argc]) { if (argc != 3) return NULL; @@ -150,14 +150,14 @@ int __q08_generate_statistics_foreach_reservation(void *user_data * @brief Generates statistical data for queries of type 8. * * @param database Database, to iterate through reservations. - * @param instances Instances of the query 8. * @param n Number of query instances. + * @param instances Instances of the query 8. * * @return A `GHashTable` associating a ::q08_parsed_arguments_t to an integer revenue as a pointer. */ -void *__q08_generate_statistics(const database_t *database, - const query_instance_t *const *instances, - size_t n) { +void *__q08_generate_statistics(const database_t *database, + size_t n, + const query_instance_t *const instances[n]) { GPtrArray *filter_data = g_ptr_array_new(); GHashTable *hotel_revenue = g_hash_table_new(g_direct_hash, g_direct_equal); @@ -215,7 +215,8 @@ int __q08_execute(const database_t *database, } query_type_t *q08_create(void) { - return query_type_create(__q08_parse_arguments, + return query_type_create(8, + __q08_parse_arguments, __q08_clone_arguments, free, __q08_generate_statistics, diff --git a/trabalho-pratico/src/queries/q09.c b/trabalho-pratico/src/queries/q09.c index 0c31387..5b8262e 100644 --- a/trabalho-pratico/src/queries/q09.c +++ b/trabalho-pratico/src/queries/q09.c @@ -38,7 +38,7 @@ * * @return `NULL` for invalid arguments, a copy of the only @p argv on success. */ -void *__q09_parse_arguments(char *const *argv, size_t argc) { +void *__q09_parse_arguments(size_t argc, char *const argv[argc]) { if (argc != 1) return NULL; else @@ -124,14 +124,14 @@ int __q09_sort_prefixes_callback(const void *a, const void *b) { * @brief Generates statistical data for queries of type 9. * * @param database Database, to iterate through users. - * @param instances Query instances that will need to be executed. * @param n Number of query instances that will need to be executed. + * @param instances Query instances that will need to be executed. * * @return A pointer to a ::q09_statistical_data_t. */ -void *__q09_generate_statistics(const database_t *database, - const query_instance_t *const *instances, - size_t n) { +void *__q09_generate_statistics(const database_t *database, + size_t n, + const query_instance_t *const instances[n]) { /* Set locale for sorting and restore older locale later */ char *old_locale = strdup(setlocale(LC_COLLATE, NULL)); @@ -254,7 +254,8 @@ int __q09_execute(const database_t *database, } query_type_t *q09_create(void) { - return query_type_create(__q09_parse_arguments, + return query_type_create(9, + __q09_parse_arguments, (query_type_clone_arguments_callback_t) strdup, free, __q09_generate_statistics, diff --git a/trabalho-pratico/src/queries/q10.c b/trabalho-pratico/src/queries/q10.c index 7923db5..bff7cd4 100644 --- a/trabalho-pratico/src/queries/q10.c +++ b/trabalho-pratico/src/queries/q10.c @@ -21,6 +21,7 @@ #include #include +#include #include #include "queries/q10.h" @@ -45,12 +46,12 @@ typedef struct { * @brief Parses arguments of a query of type 10. * @details Asserts there are 0 to 2 arguments, a year and a month, respectively. * - * @param argv Values of the arguments. * @param argc Number of arguments. + * @param argv Values of the arguments. * * @return `NULL` for invalid arguments, a copy of the only @p argv on success. */ -void *__q10_parse_arguments(char *const *argv, size_t argc) { +void *__q10_parse_arguments(size_t argc, char *const argv[argc]) { q10_parsed_arguments_t *ret = malloc(sizeof(q10_parsed_arguments_t)); if (!ret) return NULL; @@ -285,15 +286,15 @@ int __q10_generate_statistics_foreach_reservation(void *user_data * @brief Generates statistical data for queries of type 10. * * @param database Database, to iterate through users. - * @param instances Query instances that will need to be executed. * @param n Number of query instances that will need to be executed. + * @param instances Query instances that will need to be executed. * * @return A `GHashTable` that associates dates (also dayless and monthless dates) to pointers to * ::q10_instant_statistics_t (`NULL` on failure). */ -void *__q10_generate_statistics(const database_t *database, - const query_instance_t *const *instances, - size_t n) { +void *__q10_generate_statistics(const database_t *database, + size_t n, + const query_instance_t *const instances[n]) { GHashTable *stats = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) free); @@ -472,7 +473,8 @@ int __q10_execute(const database_t *database, } query_type_t *q10_create(void) { - return query_type_create(__q10_parse_arguments, + return query_type_create(10, + __q10_parse_arguments, __q10_clone_arguments, free, __q10_generate_statistics, From fddf80c6ba8c0b6a5ecc617d075d4d792944527f Mon Sep 17 00:00:00 2001 From: voidbert <50591320+voidbert@users.noreply.github.com> Date: Sun, 21 Jan 2024 02:07:03 +0000 Subject: [PATCH 03/19] Fix uninitialized data in query parser --- trabalho-pratico/src/queries/query_parser.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/trabalho-pratico/src/queries/query_parser.c b/trabalho-pratico/src/queries/query_parser.c index d21b098..712c3fd 100644 --- a/trabalho-pratico/src/queries/query_parser.c +++ b/trabalho-pratico/src/queries/query_parser.c @@ -80,7 +80,8 @@ int __query_parser_tokenize_callback(void *user_data, char *token) { /* Parse number of query */ uint64_t query_type; const int retval = int_utils_parse_positive(&query_type, token); - const query_type_t *const type = query_type_list_get_by_index((size_t) query_type); + const query_type_t *const type = + retval ? NULL : query_type_list_get_by_index((size_t) query_type); if (retval || !type) { if (token[token_len - 1] == '\0') /* Restore previous string form */ token[token_len - 1] = 'F'; From ddb16e16638482273bd3c60b353673c0aa503901 Mon Sep 17 00:00:00 2001 From: voidbert <50591320+voidbert@users.noreply.github.com> Date: Sun, 21 Jan 2024 14:19:46 +0000 Subject: [PATCH 04/19] Restore previous string in query_parser --- .../include/queries/query_parser.h | 4 ++- trabalho-pratico/src/queries/query_parser.c | 28 ++++++++++++------- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/trabalho-pratico/include/queries/query_parser.h b/trabalho-pratico/include/queries/query_parser.h index 2b07814..a0ef5a9 100644 --- a/trabalho-pratico/include/queries/query_parser.h +++ b/trabalho-pratico/include/queries/query_parser.h @@ -86,7 +86,9 @@ * @brief Parses a **MODIFIABLE** string containing a query. * * @param output Where the parsed query is placed. This **will be modified on failure** too. - * @param input String to parse, that **will be modified** as a byproduct of parsing. + * @param input String to parse, that will be modified during parsing, but then restored to its + * original form, assuming none of the ::query_type_parse_arguments_callback_t + * modifies its argument tokens. * @param aux Auxiliary `GPtrArray`, that can be provided to be modified and avoid memory * allocations. If `NULL`, a new array will be instantiated. * diff --git a/trabalho-pratico/src/queries/query_parser.c b/trabalho-pratico/src/queries/query_parser.c index 712c3fd..2563eb6 100644 --- a/trabalho-pratico/src/queries/query_parser.c +++ b/trabalho-pratico/src/queries/query_parser.c @@ -131,20 +131,28 @@ int query_parser_parse_string(query_instance_t *output, char *input, GPtrArray * void *const argument_data = parse_cb(parser_data.args->len, (char *const *) parser_data.args->pdata); - if (!argument_data) { /* Argument parsing failure */ - if (!aux) - g_ptr_array_free(parser_data.args, TRUE); - return 1; + if (argument_data) { + query_instance_set_argument_data(output, argument_data); + + const query_type_free_arguments_callback_t free_cb = + query_type_get_free_arguments_callback(query_type); + free_cb(argument_data); } - query_instance_set_argument_data(output, argument_data); - const query_type_free_arguments_callback_t free_cb = - query_type_get_free_arguments_callback(query_type); - free_cb(argument_data); + /* Restore string */ + for (ssize_t i = 0; i < parser_data.args->len; ++i) { + char *const str = g_ptr_array_index(parser_data.args, i); + char *const end = str + strlen(str); + + if (*(str - 1) == '"') + *end = '"'; + else if (i != parser_data.args->len - 1) + *end = ' '; + } if (!aux) - g_ptr_array_free(parser_data.args, TRUE); - return 0; + g_ptr_array_unref(parser_data.args); + return (argument_data == NULL); } int query_parser_parse_string_const(query_instance_t *output, const char *input, GPtrArray *aux) { From 77e1c71e16538be572460133f68dfa7522cedd22 Mon Sep 17 00:00:00 2001 From: voidbert <50591320+voidbert@users.noreply.github.com> Date: Sun, 21 Jan 2024 16:15:51 +0000 Subject: [PATCH 05/19] Cleanup of query 1 --- trabalho-pratico/include/queries/q01.h | 11 +- trabalho-pratico/src/queries/q01.c | 225 +++++++++++-------------- 2 files changed, 104 insertions(+), 132 deletions(-) diff --git a/trabalho-pratico/include/queries/q01.h b/trabalho-pratico/include/queries/q01.h index f95cca0..5ec5eb4 100644 --- a/trabalho-pratico/include/queries/q01.h +++ b/trabalho-pratico/include/queries/q01.h @@ -15,8 +15,8 @@ */ /** - * @file q01.h - * @brief Implementation of query 1. + * @file q01.h + * @brief A query to provide information about an entity in the dataset. * * ### Examples * @@ -66,9 +66,10 @@ #include "queries/query_type.h" /** - * @brief Initializes the definition of query 1. - * @details This is done automatically in ::query_type_list_create. - * @return A pointer to a `malloc`-allocated ::query_type_t on success, or `NULL` on failure. + * @brief Initializes the definition of queries of type 1. + * @details This is done automatically in [query_type_list](@ref query_type_list.c). + * @return On success, a pointer to a ::query_type_t that must be deleted with ::query_type_free, + * or `NULL` allocation on failure. */ query_type_t *q01_create(void); diff --git a/trabalho-pratico/src/queries/q01.c b/trabalho-pratico/src/queries/q01.c index de48778..b14c800 100644 --- a/trabalho-pratico/src/queries/q01.c +++ b/trabalho-pratico/src/queries/q01.c @@ -19,29 +19,27 @@ * @brief Implementation of methods in include/queries/q01.h */ -#include #include #include "queries/q01.h" #include "queries/query_instance.h" -#include "utils/int_utils.h" -#include "utils/single_pool_id_linked_list.h" -/** @brief Type of the enitity queried by a query 1 */ +/** @brief Type of the entity queried by a query 1. */ typedef enum { - ID_ENTITY_USER, /**< @brief The queried entity is a user */ - ID_ENTITY_FLIGHT, /**< @brief The queried entity is a flight */ - ID_ENTITY_RESERVATION /**< @brief The queried entity is a reservation */ + ID_ENTITY_USER, /**< @brief The queried entity is a user. */ + ID_ENTITY_FLIGHT, /**< @brief The queried entity is a flight. */ + ID_ENTITY_RESERVATION /**< @brief The queried entity is a reservation. */ } q01_id_entity_t; /** * @struct q01_parsed_arguments_t - * @brief Data needed for the execution of a query of type 1. + * @brief Parsed arguments of a query of type 1. * - * @var parse_arguments_helper::id_entity - * @brief Stores the type of the entity of an identifier. - * @var parse_arguments_helper::parsed_id - * @brief A pointer to an id, of type `char *` or `uint64_t *`. + * @var q01_parsed_arguments_t::id_entity + * @brief The type of the entity q01_parsed_arguments_t::parsed_id refers to. + * @var q01_parsed_arguments_t::parsed_id + * @brief A pointer to a string (user identifier), or a ::flight_id_t / ::reservation_id_t + * encoded as a pointer. */ typedef struct { q01_id_entity_t id_entity; @@ -50,146 +48,129 @@ typedef struct { /** * @brief Parses the arguments of a query of type 1. - * @details Asserts there's only one string argument, that is stored. + * @details Asserts there's only one argument, the identifier of a user, flight or reservation. * * @param argc Number of arguments. * @param argv Values of the arguments. * - * @return `NULL` for invalid arguments, a copy of the only @p argv and its identifier on success. + * @return `NULL` for invalid arguments (or allocation failure), a pointer to a valid + * ::q01_parsed_arguments_t on success. */ void *__q01_parse_arguments(size_t argc, char *const argv[argc]) { if (argc != 1) return NULL; - /* TODO - fix leaks */ - - flight_id_t parsed_flight_id_aux; - reservation_id_t parsed_reservation_id_aux; - q01_parsed_arguments_t *parsed_argument = malloc(sizeof(q01_parsed_arguments_t)); + flight_id_t parsed_flight_id; + reservation_id_t parsed_reservation_id; + q01_parsed_arguments_t *const parsed_argument = malloc(sizeof(q01_parsed_arguments_t)); if (!parsed_argument) return NULL; - if (!flight_id_from_string(&parsed_flight_id_aux, *argv)) { - flight_id_t *parsed_id = malloc(sizeof(flight_id_t)); - if (!parsed_id) - return NULL; - - *parsed_id = parsed_flight_id_aux; + if (!flight_id_from_string(&parsed_flight_id, *argv)) { parsed_argument->id_entity = ID_ENTITY_FLIGHT; - parsed_argument->parsed_id = parsed_id; - } else if (!reservation_id_from_string(&parsed_reservation_id_aux, *argv)) { - reservation_id_t *parsed_id = malloc(sizeof(reservation_id_t)); - if (!parsed_id) - return NULL; - - *parsed_id = parsed_reservation_id_aux; + parsed_argument->parsed_id = (void *) (size_t) parsed_flight_id; + } else if (!reservation_id_from_string(&parsed_reservation_id, *argv)) { parsed_argument->id_entity = ID_ENTITY_RESERVATION; - parsed_argument->parsed_id = parsed_id; + parsed_argument->parsed_id = (void *) (size_t) parsed_reservation_id; } else { - char *parsed_id = strdup(*argv); - if (!parsed_id) + parsed_argument->parsed_id = strdup(*argv); + if (!parsed_argument->parsed_id) { + free(parsed_argument); return NULL; - + } parsed_argument->id_entity = ID_ENTITY_USER; - parsed_argument->parsed_id = parsed_id; } return parsed_argument; } +/** + * @brief Creates a deep clone of the value returned by ::__q01_parse_arguments. + * @param args_data Non-`NULL` value returned by ::__q01_parse_arguments (a pointer to a + * ::q01_parsed_arguments_t). + * @return A deep copy of @p args_data. + */ void *__q01_clone_arguments(const void *args_data) { - const q01_parsed_arguments_t *args = args_data; - q01_parsed_arguments_t *clone = malloc(sizeof(q01_parsed_arguments_t)); + const q01_parsed_arguments_t *const args = args_data; + q01_parsed_arguments_t *const clone = malloc(sizeof(q01_parsed_arguments_t)); if (!clone) return NULL; clone->id_entity = args->id_entity; switch (args->id_entity) { case ID_ENTITY_FLIGHT: - clone->parsed_id = malloc(sizeof(sizeof(flight_id_t))); - memcpy(clone->parsed_id, args->parsed_id, sizeof(flight_id_t)); - break; case ID_ENTITY_RESERVATION: - clone->parsed_id = malloc(sizeof(sizeof(reservation_id_t))); - memcpy(clone->parsed_id, args->parsed_id, sizeof(reservation_id_t)); + clone->parsed_id = args->parsed_id; break; case ID_ENTITY_USER: clone->parsed_id = strdup(args->parsed_id); - break; - default: - clone->parsed_id = NULL; /* unreachable */ + if (!clone->parsed_id) { + free(clone); + return NULL; + } break; } - - if (!clone->parsed_id) { - free(clone); - return NULL; - } return clone; } /** * @brief Frees data generated by ::__q01_parse_arguments. - * @param argument_data Data generated by ::__q01_parse_arguments. + * @param args_data Data generated by ::__q01_parse_arguments. */ -void __q01_free_query_instance_argument_data(void *argument_data) { - free(((q01_parsed_arguments_t *) argument_data)->parsed_id); - free(argument_data); +void __q01_free_arguments(void *args_data) { + q01_parsed_arguments_t *const args = args_data; + if (args->id_entity == ID_ENTITY_USER) + free(args->parsed_id); + free(args); } /** - * @brief Calculates the total spent by a ::user_t. - * @details The total spent is the sum of the total price of all the reservations the user is - * related to. + * @brief Calculates the total money spent by a ::user_t. * * @param list List to get the reservation identifiers from. * @param manager Manager to get the reservations from. * - * @return The total spent by a ::user_t. + * @return The sum of the total price of all the reservations a user booked. */ double __q01_calculate_user_total_spent(const single_pool_id_linked_list_t *list, const reservation_manager_t *manager) { double total_spent = 0.0; - while (list) { - const reservation_t *reservation = + const reservation_t *const reservation = reservation_manager_get_by_id(manager, single_pool_id_linked_list_get_value(list)); total_spent += reservation_calculate_price(reservation); list = single_pool_id_linked_list_get_next(list); } - return total_spent; } /** * @brief Executes a query of type 1, when it refers to a ::user_t. * - * @param database Database do get the users and reservations from. - * @param id Identifier to find the user and its data. + * @param database Database do get users and reservations from. + * @param id Identifier of the user to be found. * @param output Where to write the query's output to. - * - * @retval 0 Never fails. */ -int __q01_execute_user_entity(const database_t *database, const char *id, query_writer_t *output) { - - const user_manager_t *user_manager = database_get_users(database); - const reservation_manager_t *reservation_manager = database_get_reservations(database); - const user_t *user = user_manager_get_by_id(user_manager, id); +void __q01_execute_user_entity(const database_t *database, const char *id, query_writer_t *output) { + const user_manager_t *const user_manager = database_get_users(database); + const reservation_manager_t *const reservation_manager = database_get_reservations(database); + const user_t *const user = user_manager_get_by_id(user_manager, id); if (!user || user_get_account_status(user) == ACCOUNT_STATUS_INACTIVE) - return 0; + return; - const single_pool_id_linked_list_t *reservation_list = - user_manager_get_reservations_by_id(user_manager, id); - size_t number_of_flights = + const size_t number_of_flights = single_pool_id_linked_list_length(user_manager_get_flights_by_id(user_manager, id)); - size_t number_of_reservations = single_pool_id_linked_list_length(reservation_list); - double total_spent = __q01_calculate_user_total_spent(reservation_list, reservation_manager); + const single_pool_id_linked_list_t *const reservation_list = + user_manager_get_reservations_by_id(user_manager, id); + const size_t number_of_reservations = single_pool_id_linked_list_length(reservation_list); + const double total_spent = + __q01_calculate_user_total_spent(reservation_list, reservation_manager); char sex[SEX_SPRINTF_MIN_BUFFER_SIZE]; sex_sprintf(sex, user_get_sex(user)); - int32_t age = user_calculate_age(user); + const int32_t age = user_calculate_age(user); char country_code[COUNTRY_CODE_SPRINTF_MIN_BUFFER_SIZE]; country_code_sprintf(country_code, user_get_country_code(user)); @@ -203,42 +184,37 @@ int __q01_execute_user_entity(const database_t *database, const char *id, query_ query_writer_write_new_field(output, "number_of_flights", "%zu", number_of_flights); query_writer_write_new_field(output, "number_of_reservations", "%zu", number_of_reservations); query_writer_write_new_field(output, "total_spent", "%.3lf", total_spent); - - return 0; } /** * @brief Executes a query of type 1, when it refers to a ::reservation_t. * - * @param database Database do get the users and reservations from. - * @param id Identifier to find the reservation and its data. + * @param database Database do get reservations from. + * @param id Identifier of the reservation to be found. * @param output Where to write the query's output to. - * - * @retval 0 Always successful. */ -int __q01_execute_reservation_entity(const database_t *database, - reservation_id_t id, - query_writer_t *output) { - - const reservation_manager_t *reservation_manager = database_get_reservations(database); - const reservation_t *reservation = reservation_manager_get_by_id(reservation_manager, id); +void __q01_execute_reservation_entity(const database_t *database, + reservation_id_t id, + query_writer_t *output) { + const reservation_t *const reservation = + reservation_manager_get_by_id(database_get_reservations(database), id); if (!reservation) - return 0; + return; - date_t begin_date = reservation_get_begin_date(reservation); - char begin_date_str[DATE_SPRINTF_MIN_BUFFER_SIZE]; + const date_t begin_date = reservation_get_begin_date(reservation); + char begin_date_str[DATE_SPRINTF_MIN_BUFFER_SIZE]; date_sprintf(begin_date_str, begin_date); - date_t end_date = reservation_get_end_date(reservation); - char end_date_str[DATE_SPRINTF_MIN_BUFFER_SIZE]; + const date_t end_date = reservation_get_end_date(reservation); + char end_date_str[DATE_SPRINTF_MIN_BUFFER_SIZE]; date_sprintf(end_date_str, end_date); - includes_breakfast_t includes_breakfast = reservation_get_includes_breakfast(reservation); - char includes_breakfast_str[INCLUDES_BREAKFAST_SPRINTF_MIN_BUFFER_SIZE]; + const includes_breakfast_t includes_breakfast = reservation_get_includes_breakfast(reservation); + char includes_breakfast_str[INCLUDES_BREAKFAST_SPRINTF_MIN_BUFFER_SIZE]; includes_breakfast_sprintf(includes_breakfast_str, includes_breakfast); - int64_t nights = date_diff(end_date, begin_date); - double total_price = reservation_calculate_price(reservation); + const int64_t nights = date_diff(end_date, begin_date); + const double total_price = reservation_calculate_price(reservation); char hotel_id_str[HOTEL_ID_SPRINTF_MIN_BUFFER_SIZE]; hotel_id_sprintf(hotel_id_str, reservation_get_hotel_id(reservation)); @@ -258,27 +234,23 @@ int __q01_execute_reservation_entity(const database_t *database, query_writer_write_new_field(output, "includes_breakfast", "%s", includes_breakfast_str); query_writer_write_new_field(output, "nights", "%" PRIi64, nights); query_writer_write_new_field(output, "total_price", "%.3lf", total_price); - - return 0; } /** * @brief Executes a query of type 1, when it refers to a ::flight_t. * * @param database Database do get the flights from. - * @param id Id to find the flight. + * @param id Identifier of the flight to be found. * @param output Where to write the query's output to. - * - * @retval 0 Always successful. */ -int __q01_execute_flight_entity(const database_t *database, - flight_id_t id, - query_writer_t *output) { +void __q01_execute_flight_entity(const database_t *database, + flight_id_t id, + query_writer_t *output) { - const flight_manager_t *flight_manager = database_get_flights(database); - const flight_t *flight = flight_manager_get_by_id(flight_manager, id); + const flight_manager_t *const flight_manager = database_get_flights(database); + const flight_t *const flight = flight_manager_get_by_id(flight_manager, id); if (!flight) - return 0; + return; char origin_airport[AIRPORT_CODE_SPRINTF_MIN_BUFFER_SIZE]; airport_code_sprintf(origin_airport, flight_get_origin(flight)); @@ -286,14 +258,14 @@ int __q01_execute_flight_entity(const database_t *database, char destination_airport[AIRPORT_CODE_SPRINTF_MIN_BUFFER_SIZE]; airport_code_sprintf(destination_airport, flight_get_destination(flight)); - date_and_time_t schedule_departure_date = flight_get_schedule_departure_date(flight); - char scheduled_departure_str[DATE_AND_TIME_SPRINTF_MIN_BUFFER_SIZE]; + const date_and_time_t schedule_departure_date = flight_get_schedule_departure_date(flight); + char scheduled_departure_str[DATE_AND_TIME_SPRINTF_MIN_BUFFER_SIZE]; date_and_time_sprintf(scheduled_departure_str, schedule_departure_date); char scheduled_arrival_str[DATE_AND_TIME_SPRINTF_MIN_BUFFER_SIZE]; date_and_time_sprintf(scheduled_arrival_str, flight_get_schedule_arrival_date(flight)); - int64_t delay = + const int64_t delay = date_and_time_diff(flight_get_real_departure_date(flight), schedule_departure_date); query_writer_write_new_object(output); @@ -308,20 +280,17 @@ int __q01_execute_flight_entity(const database_t *database, "%" PRIu16, flight_get_number_of_passengers(flight)); query_writer_write_new_field(output, "delay", "%" PRIi64, delay); - - return 0; } /** * @brief Executes a query of type 1. * - * @param database Database where to get users/reservations/flights from + * @param database Database where to get users / reservations / flights from * @param statistics `NULL`, as this query does not generate statistical data. * @param instance Query instance to be executed. * @param output Where to write the query's output to. * - * @retval 0 On success. - * @retval 1 On failure. + * @retval 0 Always successful. */ int __q01_execute(const database_t *database, const void *statistics, @@ -329,26 +298,28 @@ int __q01_execute(const database_t *database, query_writer_t *output) { (void) statistics; - const q01_parsed_arguments_t *arguments = query_instance_get_argument_data(instance); - const void *id = arguments->parsed_id; + const q01_parsed_arguments_t *const arguments = query_instance_get_argument_data(instance); + const void *const id = arguments->parsed_id; switch (arguments->id_entity) { case ID_ENTITY_USER: - return __q01_execute_user_entity(database, (const char *) id, output); + __q01_execute_user_entity(database, (const char *) id, output); + break; case ID_ENTITY_RESERVATION: - return __q01_execute_reservation_entity(database, *(reservation_id_t *) id, output); + __q01_execute_reservation_entity(database, (reservation_id_t) (size_t) id, output); + break; case ID_ENTITY_FLIGHT: - return __q01_execute_flight_entity(database, *(flight_id_t *) id, output); - default: - return 1; /* unreachable */ + __q01_execute_flight_entity(database, (flight_id_t) (size_t) id, output); + break; } + return 0; } query_type_t *q01_create(void) { return query_type_create(1, __q01_parse_arguments, __q01_clone_arguments, - __q01_free_query_instance_argument_data, + __q01_free_arguments, NULL, NULL, __q01_execute); From f36116f0faa5473aa91421e37a0a141750ae844c Mon Sep 17 00:00:00 2001 From: voidbert <50591320+voidbert@users.noreply.github.com> Date: Sun, 21 Jan 2024 17:18:49 +0000 Subject: [PATCH 06/19] Cleanup of query 2 --- trabalho-pratico/include/queries/q02.h | 10 +-- trabalho-pratico/src/queries/q02.c | 108 +++++++++++++------------ 2 files changed, 61 insertions(+), 57 deletions(-) diff --git a/trabalho-pratico/include/queries/q02.h b/trabalho-pratico/include/queries/q02.h index 50d7f04..0018029 100644 --- a/trabalho-pratico/include/queries/q02.h +++ b/trabalho-pratico/include/queries/q02.h @@ -15,9 +15,8 @@ */ /** - * @file q02.h - * @brief A query to list flights / reservations related to a user. - * @details + * @file q02.h + * @brief A query to list flights / reservations related to a user. * * ### Examples * @@ -44,8 +43,9 @@ /** * @brief Initializes the definition of the query of type 2. - * @details This is done automatically in ::query_type_list_create. - * @return A pointer to a `malloc`-allocated ::query_type_t on success, or `NULL` on failure. + * @details This is done automatically in [query_type_list](@ref query_type_list.c). + * @return On success, a pointer to a ::query_type_t that must be deleted with ::query_type_free, + * or `NULL` allocation on failure. */ query_type_t *q02_create(void); diff --git a/trabalho-pratico/src/queries/q02.c b/trabalho-pratico/src/queries/q02.c index 070bbd1..7fdaca9 100644 --- a/trabalho-pratico/src/queries/q02.c +++ b/trabalho-pratico/src/queries/q02.c @@ -20,14 +20,11 @@ */ #include -#include -#include -#include #include "queries/q02.h" #include "queries/query_instance.h" -/** @brief Argument values of the query of type 2. */ +/** @brief What entities related to a user must be outputted (flights, reservations, or both). */ typedef enum { Q02_ARGUMENTS_NO_ARGUMENT, /**< Display both flights and reservations. */ Q02_ARGUMENTS_FLIGHTS, /**< List only flights. */ @@ -49,20 +46,21 @@ typedef struct { } q02_argument_data_t; /** - * @brief Parses arguments for the second query type. + * @brief Parses the arguments of a query of type 2. * @details The first argument, a user identifier, is mandatory. A optional second argument, taking * the value of either `"flights"` or `"reservations"`, is allowed. * * @param argc Number of arguments. * @param argv Values of the arguments. * - * @return `NULL` on failure, a pointer to a `q02_argument_data_t` otherwise. + * @return `NULL` on failure (parsing or allocation), a pointer to a `q02_argument_data_t` + * otherwise. */ void *__q02_parse_arguments(size_t argc, char *const argv[argc]) { if (argc == 1) { - q02_argument_data_t *ret = malloc(sizeof(q02_argument_data_t)); - ret->user_id = strdup(argv[0]); - ret->filter = Q02_ARGUMENTS_NO_ARGUMENT; + q02_argument_data_t *const ret = malloc(sizeof(q02_argument_data_t)); + ret->user_id = strdup(argv[0]); + ret->filter = Q02_ARGUMENTS_NO_ARGUMENT; return ret; } else if (argc == 2) { q02_arguments_output_filter_t filter; @@ -74,18 +72,24 @@ void *__q02_parse_arguments(size_t argc, char *const argv[argc]) { else return NULL; - q02_argument_data_t *ret = malloc(sizeof(q02_argument_data_t)); - ret->user_id = strdup(argv[0]); - ret->filter = filter; + q02_argument_data_t *const ret = malloc(sizeof(q02_argument_data_t)); + ret->user_id = strdup(argv[0]); + ret->filter = filter; return ret; } return NULL; } +/** + * @brief Creates a deep clone of the value returned by ::__q02_parse_arguments. + * @param args_data Non-`NULL` value returned by ::__q02_parse_arguments (a pointer to a + * ::q02_argument_data_t). + * @return A deep copy of @p args_data. + */ void *__q02_clone_arguments(const void *args_data) { - const q02_argument_data_t *args = args_data; - q02_argument_data_t *clone = malloc(sizeof(q02_argument_data_t)); + const q02_argument_data_t *const args = args_data; + q02_argument_data_t *const clone = malloc(sizeof(q02_argument_data_t)); if (!clone) return NULL; @@ -99,12 +103,12 @@ void *__q02_clone_arguments(const void *args_data) { } /** - * @brief Frees argument data for query 2, allocated by ::__q02_parse_arguments. - * @param argument_data Data allocated by ::__q02_parse_arguments. + * @brief Frees data generated by ::__q02_parse_arguments. + * @param args_data Data generated by ::__q02_parse_arguments. */ -void __q02_free_query_instance_argument_data(void *argument_data) { - free(((q02_argument_data_t *) argument_data)->user_id); - free(argument_data); +void __q02_free_arguments(void *args_data) { + free(((q02_argument_data_t *) args_data)->user_id); + free(args_data); } /** @@ -119,10 +123,10 @@ void __q02_free_query_instance_argument_data(void *argument_data) { * @brief Type of the object in question (flight or reservation). */ typedef struct { - uint32_t id; - date_and_time_t date; + const uint32_t id; + const date_and_time_t date; - enum { + const enum { Q02_OUTPUT_ITEM_FLIGHT, /**< This output item is a flight. */ Q02_OUTPUT_ITEM_RESERVATION /**< This output item is a reservation. */ } type; @@ -133,19 +137,19 @@ typedef struct { * @details Auxiliary function for ::__q02_execute. */ gint __q02_execute_sort_compare(gconstpointer a_data, gconstpointer b_data) { - const q02_output_item_t *a = a_data; - const q02_output_item_t *b = b_data; + const q02_output_item_t *const a = a_data; + const q02_output_item_t *const b = b_data; - int64_t crit1 = date_and_time_diff(b->date, a->date); + const int64_t crit1 = date_and_time_diff(b->date, a->date); if (crit1) return crit1; - int64_t crit3 = (int32_t) a->id - (int32_t) b->id; - return crit3; + const int64_t crit2 = (int32_t) a->id - (int32_t) b->id; + return crit2; } /** - * @brief Prints the output of query 2 to an @p output file. + * @brief Prints the output of a query of type 2 to an @p output writer. * * @param output Where to output the query's results to. * @param items Array of ::q02_output_item_t, to be written as text. @@ -156,7 +160,7 @@ void __q02_print_output(query_writer_t *output, q02_arguments_output_filter_t filter) { for (size_t i = 0; i < items->len; ++i) { - q02_output_item_t *item = &g_array_index(items, q02_output_item_t, i); + const q02_output_item_t *const item = &g_array_index(items, q02_output_item_t, i); char date_string[DATE_SPRINTF_MIN_BUFFER_SIZE]; date_sprintf(date_string, date_and_time_get_date(item->date)); @@ -178,8 +182,10 @@ void __q02_print_output(query_writer_t *output, query_writer_write_new_field(output, "date", "%s", date_string); if (filter == Q02_ARGUMENTS_NO_ARGUMENT) { - const char *type = (item->type == Q02_OUTPUT_ITEM_FLIGHT) ? "flight" : "reservation"; - query_writer_write_new_field(output, "type", "%s", type); + query_writer_write_new_field(output, + "type", + item->type == Q02_OUTPUT_ITEM_FLIGHT ? "flight" + : "reservation"); } } } @@ -192,43 +198,41 @@ void __q02_print_output(query_writer_t *output, * @param instance Query instance to be executed. * @param output Where to output query results to. * - * @retval 0 Always. This query does not fail. + * @retval 0 Always successful. */ int __q02_execute(const database_t *database, const void *statistics, const query_instance_t *instance, query_writer_t *output) { - (void) statistics; - const q02_argument_data_t *args = query_instance_get_argument_data(instance); + const q02_argument_data_t *const args = query_instance_get_argument_data(instance); - const user_manager_t *users = database_get_users(database); - const reservation_manager_t *reservations = database_get_reservations(database); - const flight_manager_t *flights = database_get_flights(database); + const user_manager_t *const users = database_get_users(database); + const reservation_manager_t *const reservations = database_get_reservations(database); + const flight_manager_t *const flights = database_get_flights(database); - const user_t *user = user_manager_get_by_id(users, args->user_id); + const user_t *const user = user_manager_get_by_id(users, args->user_id); if (!user || user_get_account_status(user) == ACCOUNT_STATUS_INACTIVE) return 0; - GArray *output_items = g_array_new(FALSE, FALSE, sizeof(q02_output_item_t)); + GArray *const output_items = g_array_new(FALSE, FALSE, sizeof(q02_output_item_t)); if (args->filter != Q02_ARGUMENTS_FLIGHTS) { /* Add reservations */ const single_pool_id_linked_list_t *user_reservations = user_manager_get_reservations_by_id(users, args->user_id); while (user_reservations) { - uint32_t reservation_id = single_pool_id_linked_list_get_value(user_reservations); + const reservation_id_t id = single_pool_id_linked_list_get_value(user_reservations); date_and_time_t output_time; date_and_time_from_values( &output_time, - reservation_get_begin_date( - reservation_manager_get_by_id(reservations, reservation_id)), + reservation_get_begin_date(reservation_manager_get_by_id(reservations, id)), 0 /* 00:00:00 */); - q02_output_item_t output_item = {.id = reservation_id, - .date = output_time, - .type = Q02_OUTPUT_ITEM_RESERVATION}; + const q02_output_item_t output_item = {.id = id, + .date = output_time, + .type = Q02_OUTPUT_ITEM_RESERVATION}; g_array_append_val(output_items, output_item); user_reservations = single_pool_id_linked_list_get_next(user_reservations); @@ -240,12 +244,12 @@ int __q02_execute(const database_t *database, user_manager_get_flights_by_id(users, args->user_id); while (user_flights) { - uint32_t flight_id = single_pool_id_linked_list_get_value(user_flights); + const flight_id_t id = single_pool_id_linked_list_get_value(user_flights); - q02_output_item_t output_item = {.id = flight_id, - .date = flight_get_schedule_departure_date( - flight_manager_get_by_id(flights, flight_id)), - .type = Q02_OUTPUT_ITEM_FLIGHT}; + const q02_output_item_t output_item = { + .id = id, + .date = flight_get_schedule_departure_date(flight_manager_get_by_id(flights, id)), + .type = Q02_OUTPUT_ITEM_FLIGHT}; g_array_append_val(output_items, output_item); user_flights = single_pool_id_linked_list_get_next(user_flights); @@ -255,7 +259,7 @@ int __q02_execute(const database_t *database, g_array_sort(output_items, __q02_execute_sort_compare); __q02_print_output(output, output_items, args->filter); - g_array_free(output_items, TRUE); + g_array_unref(output_items); return 0; } @@ -263,7 +267,7 @@ query_type_t *q02_create(void) { return query_type_create(2, __q02_parse_arguments, __q02_clone_arguments, - __q02_free_query_instance_argument_data, + __q02_free_arguments, NULL, NULL, __q02_execute); From e54a634679fa4943aa0af89aefd821edec3a3564 Mon Sep 17 00:00:00 2001 From: voidbert <50591320+voidbert@users.noreply.github.com> Date: Sun, 21 Jan 2024 18:35:48 +0000 Subject: [PATCH 07/19] Cleanup of query 3 --- trabalho-pratico/include/queries/q03.h | 11 +- .../include/utils/glib/GConstKeyHashTable.h | 34 ++++++ trabalho-pratico/src/queries/q03.c | 100 +++++++++--------- .../src/utils/glib/GConstKeyHashTable.c | 15 ++- 4 files changed, 102 insertions(+), 58 deletions(-) diff --git a/trabalho-pratico/include/queries/q03.h b/trabalho-pratico/include/queries/q03.h index 15080a7..324a137 100644 --- a/trabalho-pratico/include/queries/q03.h +++ b/trabalho-pratico/include/queries/q03.h @@ -15,8 +15,8 @@ */ /** - * @file q03.h - * @brief A query to present the averate rating of an hotel, given its identifier. + * @file q03.h + * @brief A query that outputs the averate rating of an hotel, given its identifier. * * ### Examples * @@ -36,9 +36,10 @@ #include "queries/query_type.h" /** - * @brief Initializes the definition of the third query. - * @details This is done automatically in ::query_type_list_create. - * @return A pointer to a `malloc`-allocated ::query_type_t on success, or `NULL` on failure. + * @brief Initializes the definition of queries of type 3. + * @details This is done automatically in [query_type_list](@ref query_type_list.c). + * @return On success, a pointer to a ::query_type_t that must be deleted with ::query_type_free, + * or `NULL` allocation on failure. */ query_type_t *q03_create(void); diff --git a/trabalho-pratico/include/utils/glib/GConstKeyHashTable.h b/trabalho-pratico/include/utils/glib/GConstKeyHashTable.h index f8e5256..8e16a92 100644 --- a/trabalho-pratico/include/utils/glib/GConstKeyHashTable.h +++ b/trabalho-pratico/include/utils/glib/GConstKeyHashTable.h @@ -47,6 +47,24 @@ typedef struct GConstKeyHashTable GConstKeyHashTable; */ GConstKeyHashTable *g_const_key_hash_table_new(GHashFunc hash_func, GEqualFunc key_equal_func); +/** + * @brief Creates a new hash table where keys are pointers to constant values. + * @details A method must be provided to automatically destroy the table's values when they are + * ovewritten or the table is deleted. No method is provided for destroying keys, unlike in + * `glib`'s original method, because those are constant in this data structure. + * + * See https://libsoup.org/glib/glib-Hash-Tables.html#g-hash-table-new-full + * + * @param hash_func Function called for hashing a key. + * @param key_equal_func Function called for comparing keys. + * @param value_destroy_func Function called for destroying hash table values. + * + * @return A pointer to a ::GConstKeyHashTable. The program may be killed on allocation failure. + */ +GConstKeyHashTable *g_const_key_hash_table_new_full(GHashFunc hash_func, + GEqualFunc key_equal_func, + GDestroyNotify value_destroy_func); + /** * @brief Inserts a key-value pair into a hash table. * @details See https://libsoup.org/glib/glib-Hash-Tables.html#g-hash-table-insert @@ -72,6 +90,22 @@ gboolean g_const_key_hash_table_insert(GConstKeyHashTable *hash_table, */ gpointer g_const_key_hash_table_lookup(GConstKeyHashTable *hash_table, gconstpointer key); +/** + * @brief Gets the value a key is associated to in an hash table. + * @details Just like ::g_const_key_hash_table_lookup, but this method allows the hash table to be + * constant. Of course, this function cannot return a modifiable pointer to the value + * associated to the provided key. + * + * See https://libsoup.org/glib/glib-Hash-Tables.html#g-hash-table-lookup + * + * @param hash_table Table where to perform the lookup. + * @param key Key to be searched for. + * + * @return The value @p key is associated to, or `NULL` if that key is not in @p hash_table. + */ +gconstpointer g_const_key_hash_table_const_lookup(const GConstKeyHashTable *hash_table, + gconstpointer key); + /** * @brief Decrements the reference count of a hash table. * @details See https://libsoup.org/glib/glib-Hash-Tables.html#g-hash-table-unref diff --git a/trabalho-pratico/src/queries/q03.c b/trabalho-pratico/src/queries/q03.c index 3ea793b..f3eca9b 100644 --- a/trabalho-pratico/src/queries/q03.c +++ b/trabalho-pratico/src/queries/q03.c @@ -19,55 +19,51 @@ * @brief Implementation of methods in include/queries/q03.h */ -#include #include -#include #include "queries/q03.h" #include "queries/query_instance.h" -#include "utils/int_utils.h" +#include "utils/glib/GConstKeyHashTable.h" /** - * @brief Parses arguments for the third query. - * @details Asserts that there's only one argument, an hotel identifier. + * @brief Parses arguments for a query of type 3. + * @details Asserts that there's only one argument, a hotel identifier. * * @param argc Number of arguments. * @param argv Values of the arguments. * - * @return `NULL` on failure, a pointer to a hotel ID otherwise. + * @return `NULL` on parsing failure, an hotel ID encoded as a pointer otherwise otherwise. */ void *__q03_parse_arguments(size_t argc, char *const argv[argc]) { if (argc != 1) return NULL; hotel_id_t id; - int retval = hotel_id_from_string(&id, argv[0]); - if (retval == 0) { - hotel_id_t *id_ptr = malloc(sizeof(hotel_id_t)); - if (id_ptr) - *id_ptr = id; - - return id_ptr; - } else if (retval == 2) { - /* TODO - have a way of communicating this failure */ - /* fprintf(stderr, - "Hotel ID \"%s\" not if format HTLXXXXX. This isn't supported by our program!\n", - token);*/ - } - return NULL; + return hotel_id_from_string(&id, argv[0]) ? NULL : GUINT_TO_POINTER(id); } +/** + * @brief Creates a deep clone of the value returned by ::__q03_parse_arguments. + * @param args_data Non-`NULL` value returned by ::__q03_parse_arguments (a hotel ID encoded as a + * pointer). + * @return A deep copy of @p args_data. + */ void *__q03_clone_arguments(const void *args_data) { - hotel_id_t *clone = malloc(sizeof(hotel_id_t)); - if (!clone) - return NULL; - *clone = *(const hotel_id_t *) args_data; - return clone; + return (void *) args_data; /* Const cast acceptable - it's an just an integer */ +} + +/** + * @brief Frees data generated by ::__q03_parse_arguments. + * @details Does nothing, as @p args_data is just an integer encoded as a pointer. + * @param args_data Data generated by ::__q03_parse_arguments. + */ +void __q03_free_arguments(void *args_data) { + (void) args_data; } /** * @struct q03_average_t - * @brief Data structure containing the fields needed to calculate a flying average. + * @brief Data structure containing the for calculating a flying average. * * @var q03_average_t::sum * @brief Sum of all ratings checked until now. @@ -83,22 +79,23 @@ typedef struct { * @brief A method called for each reservation, to consider it in averages being calculated. * @details An auxiliary method for ::__q03_generate_statistics. * - * @param user_data `GHashTable` between hotel identifiers and pointers to ::q03_average_t. + * @param user_data ::GConstKeyHashTable between hotel identifiers (as pointers) and pointers to + * ::q03_average_t. * @param reservation Reservation being processed. * - * @retval 0 Always successful + * @retval 0 Always successful. */ int __q03_generate_statistics_foreach_reservation(void *user_data, const reservation_t *reservation) { - GHashTable *ratings_averages = (GHashTable *) user_data; + GConstKeyHashTable *const ratings_averages = user_data; - hotel_id_t hotel_id = reservation_get_hotel_id(reservation); - q03_average_t *avg = g_hash_table_lookup(ratings_averages, GUINT_TO_POINTER(hotel_id)); + const hotel_id_t hotel_id = reservation_get_hotel_id(reservation); + q03_average_t *const avg = + g_const_key_hash_table_lookup(ratings_averages, GUINT_TO_POINTER(hotel_id)); if (!avg) return 0; - uint8_t rating = reservation_get_rating(reservation); - avg->sum += rating; + avg->sum += reservation_get_rating(reservation); avg->count++; return 0; } @@ -107,31 +104,31 @@ int __q03_generate_statistics_foreach_reservation(void *user_data * @brief Generates statistical data for queries of type 3. * * @param database Database, to iterate through reservations. - * @param n Number of query instances that will need to be executed. - * @param instances Query instances that will need to be executed. + * @param n Number of query instances that need to be executed. + * @param instances Query instances that need to be executed. * - * @return A `GHashTable` that associates hotel identifiers with ::q03_average_t for those hotels, - * or `NULL` on failure. + * @return A ::GConstKeyHashTable that associates hotel identifiers with ::q03_average_t for those + * hotels, or `NULL` on allocation failure. */ void *__q03_generate_statistics(const database_t *database, size_t n, const query_instance_t *const instances[n]) { - GHashTable *ratings_averages = - g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) free); + GConstKeyHashTable *const ratings_averages = + g_const_key_hash_table_new_full(g_direct_hash, g_direct_equal, free); for (size_t i = 0; i < n; ++i) { - hotel_id_t hotel_id = *(const hotel_id_t *) query_instance_get_argument_data(instances[i]); - - q03_average_t *average = malloc(sizeof(q03_average_t)); + q03_average_t *const average = malloc(sizeof(q03_average_t)); if (!average) { - g_hash_table_unref(ratings_averages); + g_const_key_hash_table_unref(ratings_averages); return NULL; } average->sum = 0; average->count = 0; - g_hash_table_insert(ratings_averages, GUINT_TO_POINTER(hotel_id), average); + g_const_key_hash_table_insert(ratings_averages, + query_instance_get_argument_data(instances[i]), + average); } reservation_manager_iter(database_get_reservations(database), @@ -149,8 +146,8 @@ void *__q03_generate_statistics(const database_t *database, * @param instance Query instance to be executed. * @param output Where to write the query's result to. * - * @retval 0 Success - * @retval 1 Fatal failure (will only happen if a cosmic ray flips some bit in your memory). + * @retval 0 Success. + * @retval 1 Fatal failure (should, in principle, be unreachable). */ int __q03_execute(const database_t *database, const void *statistics, @@ -158,10 +155,11 @@ int __q03_execute(const database_t *database, query_writer_t *output) { (void) database; - GHashTable *ratings_averages = (GHashTable *) statistics; - hotel_id_t hotel_id = *(const hotel_id_t *) query_instance_get_argument_data(instance); + const GConstKeyHashTable *const ratings_averages = statistics; + const hotel_id_t hotel_id = GPOINTER_TO_UINT(query_instance_get_argument_data(instance)); - q03_average_t *avg = g_hash_table_lookup(ratings_averages, GUINT_TO_POINTER(hotel_id)); + const q03_average_t *const avg = + g_const_key_hash_table_const_lookup(ratings_averages, GUINT_TO_POINTER(hotel_id)); if (!avg) { fprintf(stderr, "Bad statistical data in query 3! This should not happen!\n"); return 1; @@ -176,8 +174,8 @@ query_type_t *q03_create(void) { return query_type_create(3, __q03_parse_arguments, __q03_clone_arguments, - free, + __q03_free_arguments, __q03_generate_statistics, - (query_type_free_statistics_callback_t) g_hash_table_unref, + (query_type_free_statistics_callback_t) g_const_key_hash_table_unref, __q03_execute); } diff --git a/trabalho-pratico/src/utils/glib/GConstKeyHashTable.c b/trabalho-pratico/src/utils/glib/GConstKeyHashTable.c index c885a1e..4f62249 100644 --- a/trabalho-pratico/src/utils/glib/GConstKeyHashTable.c +++ b/trabalho-pratico/src/utils/glib/GConstKeyHashTable.c @@ -19,14 +19,20 @@ * @brief Implementation of methods in include/utils/glib/GConstKeyHashTable.h */ -#include - #include "utils/glib/GConstKeyHashTable.h" GConstKeyHashTable *g_const_key_hash_table_new(GHashFunc hash_func, GEqualFunc key_equal_func) { return (GConstKeyHashTable *) g_hash_table_new(hash_func, key_equal_func); } +GConstKeyHashTable *g_const_key_hash_table_new_full(GHashFunc hash_func, + GEqualFunc key_equal_func, + GDestroyNotify value_destroy_func) { + + return (GConstKeyHashTable *) + g_hash_table_new_full(hash_func, key_equal_func, NULL, value_destroy_func); +} + gboolean g_const_key_hash_table_insert(GConstKeyHashTable *hash_table, gconstpointer key, gpointer value) { @@ -37,6 +43,11 @@ gpointer g_const_key_hash_table_lookup(GConstKeyHashTable *hash_table, gconstpoi return g_hash_table_lookup((GHashTable *) hash_table, key); } +gconstpointer g_const_key_hash_table_const_lookup(const GConstKeyHashTable *hash_table, + gconstpointer key) { + return g_hash_table_lookup((GHashTable *) hash_table, key); +} + void g_const_key_hash_table_unref(GConstKeyHashTable *hash_table) { g_hash_table_unref((GHashTable *) hash_table); } From 2fc2dfb9fcc0db002687b008c9845a7c1f445dd8 Mon Sep 17 00:00:00 2001 From: voidbert <50591320+voidbert@users.noreply.github.com> Date: Sun, 21 Jan 2024 23:41:18 +0000 Subject: [PATCH 08/19] GLib const wrappers additions --- .../include/utils/glib/GConstKeyHashTable.h | 51 +++++++++-- .../include/utils/glib/GConstPtrArray.h | 90 +++++++++++++++++++ .../src/utils/glib/GConstKeyHashTable.c | 6 ++ .../src/utils/glib/GConstPtrArray.c | 46 ++++++++++ 4 files changed, 184 insertions(+), 9 deletions(-) create mode 100644 trabalho-pratico/include/utils/glib/GConstPtrArray.h create mode 100644 trabalho-pratico/src/utils/glib/GConstPtrArray.c diff --git a/trabalho-pratico/include/utils/glib/GConstKeyHashTable.h b/trabalho-pratico/include/utils/glib/GConstKeyHashTable.h index 8e16a92..2ab0d50 100644 --- a/trabalho-pratico/include/utils/glib/GConstKeyHashTable.h +++ b/trabalho-pratico/include/utils/glib/GConstKeyHashTable.h @@ -32,10 +32,22 @@ * @details The underlying `struct` doesn't actually exist. A pointer to a ::GConstKeyHashTable will * merely be casted to a `GHashTable *` during operations, but there will be no implicit * casts this way. Also, the pointer cast (as opposed to a real structure) will avoid - * memory hops. + * memory indirection. */ typedef struct GConstKeyHashTable GConstKeyHashTable; +/** + * @brief Method for iterating over hash tables whose keys are constant. + * @details See https://libsoup.org/glib/glib-Hash-Tables.html#GHFunc and + * ::g_const_key_hash_table_foreach. + * + * @param key Pointer to the key in a key-value pair. + * @param value Pointer to the value in a key-value pair. + * @param user_data External data, passed to ::g_const_key_hash_table_foreach, so that this callback + * can modify the program's state. + */ +typedef void (*GHConstFunc)(gconstpointer key, gpointer value, gpointer user_data); + /** * @brief Creates a new hash table where keys are pointers to constant values. * @details See https://libsoup.org/glib/glib-Hash-Tables.html#g-hash-table-new @@ -43,15 +55,16 @@ typedef struct GConstKeyHashTable GConstKeyHashTable; * @param hash_func Function called for hashing a key. * @param key_equal_func Function called for comparing keys. * - * @return A pointer to a ::GConstKeyHashTable. The program may be killed on allocation failure. + * @return A pointer to a ::GConstKeyHashTable. */ GConstKeyHashTable *g_const_key_hash_table_new(GHashFunc hash_func, GEqualFunc key_equal_func); /** * @brief Creates a new hash table where keys are pointers to constant values. * @details A method must be provided to automatically destroy the table's values when they are - * ovewritten or the table is deleted. No method is provided for destroying keys, unlike in - * `glib`'s original method, because those are constant in this data structure. + * ovewritten or when the table is deleted. No method is provided for destroying keys, + * unlike in `glib`'s original method, because those are constant in a + * `GConstKeyHashTable`. * * See https://libsoup.org/glib/glib-Hash-Tables.html#g-hash-table-new-full * @@ -59,7 +72,7 @@ GConstKeyHashTable *g_const_key_hash_table_new(GHashFunc hash_func, GEqualFunc k * @param key_equal_func Function called for comparing keys. * @param value_destroy_func Function called for destroying hash table values. * - * @return A pointer to a ::GConstKeyHashTable. The program may be killed on allocation failure. + * @return A pointer to a ::GConstKeyHashTable. */ GConstKeyHashTable *g_const_key_hash_table_new_full(GHashFunc hash_func, GEqualFunc key_equal_func, @@ -73,20 +86,24 @@ GConstKeyHashTable *g_const_key_hash_table_new_full(GHashFunc hash_func, * @param key Key to be inserted into @p hash_table. * @param value Value to be related to @p key in @p hash_table. * - * @return `TRUE` if the key did not exist yet. The program may be killed on allocation failure. + * @return `TRUE` if the key did not exist yet. */ gboolean g_const_key_hash_table_insert(GConstKeyHashTable *hash_table, gconstpointer key, gpointer value); /** - * @brief Gets the value a key is associated to in an hash table. - * @details See https://libsoup.org/glib/glib-Hash-Tables.html#g-hash-table-lookup + * @brief Gets the value a key is associated to in a hash table. + * @details See https://libsoup.org/glib/glib-Hash-Tables.html#g-hash-table-lookup~ + * + * We're aware that this exposes memory (the value associated to @p key) that may be owned by + * `glib`. However, so does `glib`, and this module is a simple wrapper trying to stay as faithful + * to it as possible, while allowing for `const` hash table keys. * * @param hash_table Table where to perform the lookup. * @param key Key to be searched for. * - * @return The value @p key is associated to, or `NULL` if that key is not in @p hash_table. + * @return The value @p key is associated to, or `NULL` if @p key is not in @p hash_table. */ gpointer g_const_key_hash_table_lookup(GConstKeyHashTable *hash_table, gconstpointer key); @@ -106,6 +123,22 @@ gpointer g_const_key_hash_table_lookup(GConstKeyHashTable *hash_table, gconstpoi gconstpointer g_const_key_hash_table_const_lookup(const GConstKeyHashTable *hash_table, gconstpointer key); +/** + * @brief Iterates through a hash table with constant keys. + * @details See https://libsoup.org/glib/glib-Hash-Tables.html#g-hash-table-foreach + * + * We're aware that this exposes memory (the value associated to each key) that may be owned by + * `glib`. However, so does `glib`, and this module is a simple wrapper trying to stay as faithful + * to it as possible, while allowing for `const` hash table keys. + * + * @param hash_table Hash table to iterate thorugh. + * @param func Method called for each key-value pair. + * @param user_data Argument passed to @p func so that it can modify the program's state. + */ +void g_const_key_hash_table_foreach(GConstKeyHashTable *hash_table, + GHConstFunc func, + gpointer user_data); + /** * @brief Decrements the reference count of a hash table. * @details See https://libsoup.org/glib/glib-Hash-Tables.html#g-hash-table-unref diff --git a/trabalho-pratico/include/utils/glib/GConstPtrArray.h b/trabalho-pratico/include/utils/glib/GConstPtrArray.h new file mode 100644 index 0000000..dd1b418 --- /dev/null +++ b/trabalho-pratico/include/utils/glib/GConstPtrArray.h @@ -0,0 +1,90 @@ +/* + * Copyright 2023 Humberto Gomes, José Lopes, José Matos + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file GConstPtrArray.h + * @brief A wrapper over a `GPtrArray` but for `const` pointers. + * @details Available methods constitute a subset of the methods provided by `glib`, and they have + * the same behavior. There are many online documentation sources and the following is only + * an example: https://libsoup.org/glib/glib-Pointer-Arrays.html + */ + +#ifndef G_CONST_PTR_ARRAY_H +#define G_CONST_PTR_ARRAY_H + +#include + +/** + * @brief A dynamic array of constant pointers. + * @details The underlying `struct` doesn't actually exist. A pointer to a ::GConstPtrArray will + * merely be casted to a `GPtrArray *` during operations, but there will be no implicit + * casts this way. Also, the pointer cast (as opposed to a real structure) will avoid + * memory indirection. + */ +typedef struct GConstPtrArray GConstPtrArray; + +/** + * @brief Creates a new dynamic array of constant pointers. + * @details See https://libsoup.org/glib/glib-Pointer-Arrays.html#g-ptr-array-new + * @return A pointer to a ::GConstPtrArray. + */ +GConstPtrArray *g_const_ptr_array_new(void); + +/** + * @brief Adds a pointer to the end of a pointer array. + * @details See https://libsoup.org/glib/glib-Pointer-Arrays.html#g-ptr-array-add + * + * @param array The array to add @p data to. + * @param data Pointer to be added to the end of @p array. + */ +void g_const_ptr_array_add(GConstPtrArray *array, gconstpointer data); + +/** + * @brief Returns the pointer at a given index of a pointer array. + * @details See https://libsoup.org/glib/glib-Pointer-Arrays.html#g-ptr-array-index + * + * @param array The array to get a pointer from. + * @param index The index of the pointer to return. + * + * @return The pointer resulting from @p array indexed at @p index. + */ +gconstpointer g_const_ptr_array_index(const GConstPtrArray *array, guint index); + +/** + * @brief Returns the length of an array of constant pointers. + * @details There is no equivalent to this method in `glib`, as the field `len` is directly + * accessible from a `GPtrArray`. However, that isn't the case in a ::GConstPtrArray. + * @return The lenght of @p array. + */ +guint g_const_ptr_array_get_length(const GConstPtrArray *array); + +/** + * @brief Sorts an array of constant pointers. + * @details See https://libsoup.org/glib/glib-Pointer-Arrays.html#g-ptr-array-sort + * + * @param array Array to be sorted. + * @param compare_func Method used to compare between two given pointers. + */ +void g_const_ptr_array_sort(GConstPtrArray *array, GCompareFunc compare_func); + +/** + * @brief Decrements the reference count of a dynamic array of constant pointers. + * @details See https://libsoup.org/glib/glib-Pointer-Arrays.html#g-ptr-array-unref + * @param array Array to have its reference count decremented. + */ +void g_const_ptr_array_unref(GConstPtrArray *array); + +#endif diff --git a/trabalho-pratico/src/utils/glib/GConstKeyHashTable.c b/trabalho-pratico/src/utils/glib/GConstKeyHashTable.c index 4f62249..17a2910 100644 --- a/trabalho-pratico/src/utils/glib/GConstKeyHashTable.c +++ b/trabalho-pratico/src/utils/glib/GConstKeyHashTable.c @@ -48,6 +48,12 @@ gconstpointer g_const_key_hash_table_const_lookup(const GConstKeyHashTable *hash return g_hash_table_lookup((GHashTable *) hash_table, key); } +void g_const_key_hash_table_foreach(GConstKeyHashTable *hash_table, + GHConstFunc func, + gpointer user_data) { + g_hash_table_foreach((GHashTable *) hash_table, (GHFunc) func, user_data); +} + void g_const_key_hash_table_unref(GConstKeyHashTable *hash_table) { g_hash_table_unref((GHashTable *) hash_table); } diff --git a/trabalho-pratico/src/utils/glib/GConstPtrArray.c b/trabalho-pratico/src/utils/glib/GConstPtrArray.c new file mode 100644 index 0000000..809d6da --- /dev/null +++ b/trabalho-pratico/src/utils/glib/GConstPtrArray.c @@ -0,0 +1,46 @@ +/* + * Copyright 2023 Humberto Gomes, José Lopes, José Matos + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file GConstPtrArray.c + * @brief Implementation of methods in include/utils/glib/GConstPtrArray.h + */ + +#include "utils/glib/GConstPtrArray.h" + +GConstPtrArray *g_const_ptr_array_new(void) { + return (GConstPtrArray *) g_ptr_array_new(); +} + +void g_const_ptr_array_add(GConstPtrArray *array, gconstpointer data) { + g_ptr_array_add((GPtrArray *) array, (gpointer) data); +} + +gconstpointer g_const_ptr_array_index(const GConstPtrArray *array, guint index) { + return g_ptr_array_index((GPtrArray *) array, index); +} + +guint g_const_ptr_array_get_length(const GConstPtrArray *array) { + return ((const GPtrArray *) array)->len; +} + +void g_const_ptr_array_sort(GConstPtrArray *array, GCompareFunc compare_func) { + g_ptr_array_sort((GPtrArray *) array, compare_func); +} + +void g_const_ptr_array_unref(GConstPtrArray *array) { + g_ptr_array_unref((GPtrArray *) array); +} From 30fed596bcea9746f836d1e05980c9570fd1d985 Mon Sep 17 00:00:00 2001 From: voidbert <50591320+voidbert@users.noreply.github.com> Date: Mon, 22 Jan 2024 00:04:45 +0000 Subject: [PATCH 09/19] Cleanup of query 4 --- trabalho-pratico/include/queries/q04.h | 67 ++++--- trabalho-pratico/src/queries/q04.c | 238 +++++++++++++------------ 2 files changed, 154 insertions(+), 151 deletions(-) diff --git a/trabalho-pratico/include/queries/q04.h b/trabalho-pratico/include/queries/q04.h index 9836088..3329241 100644 --- a/trabalho-pratico/include/queries/q04.h +++ b/trabalho-pratico/include/queries/q04.h @@ -1,36 +1,34 @@ /* - * Copyright 2023 Humberto Gomes, José Lopes, José Matos - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ + * Copyright 2023 Humberto Gomes, José Lopes, José Matos + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ /** - * @file q04.h - * @brief List hotel reservations, ordered by start date (from most recent to oldest). - * @details If two reservations have the same date, the reservation identifier must - * be used as a tiebreaker (in ascending order). - * - * ### Examples - * - * ```text - * 4 HTL1001 - * 4 HTL1002 - * 4 HTL1003 - * 4F HTL1001 - * 4F HTL1002 - * 4F HTL1003 - * ``` - */ + * @file q04.h + * @brief List all reservations of a given hotel. + * + * ### Examples + * + * ```text + * 4 HTL1001 + * 4 HTL1002 + * 4 HTL1003 + * 4F HTL1001 + * 4F HTL1002 + * 4F HTL1003 + * ``` + */ #ifndef Q04_H #define Q04_H @@ -38,10 +36,11 @@ #include "queries/query_type.h" /** - * @brief Initializes the definition of the fourth query. - * @details This is done automatically in ::query_type_list_create. - * @return A pointer to a `malloc`-allocated ::query_type_t on success, or `NULL` on failure. - */ + * @brief Initializes the definition of queries of type 4. + * @details This is done automatically in [query_type_list](@ref query_type_list.c). + * @return On success, a pointer to a ::query_type_t that must be deleted with ::query_type_free, + * or `NULL` allocation on failure. + */ query_type_t *q04_create(void); #endif diff --git a/trabalho-pratico/src/queries/q04.c b/trabalho-pratico/src/queries/q04.c index e581d1d..37c4e67 100644 --- a/trabalho-pratico/src/queries/q04.c +++ b/trabalho-pratico/src/queries/q04.c @@ -1,146 +1,151 @@ /* - * Copyright 2023 Humberto Gomes, José Lopes, José Matos - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ + * Copyright 2023 Humberto Gomes, José Lopes, José Matos + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ /** - * @file q04.c - * @brief Implementation of methods in include/queries/q04.h - */ + * @file q04.c + * @brief Implementation of methods in include/queries/q04.h + */ -#include #include -#include #include "queries/q04.h" #include "queries/query_instance.h" -#include "utils/int_utils.h" +#include "utils/glib/GConstKeyHashTable.h" +#include "utils/glib/GConstPtrArray.h" /** - * @brief Parses arguments for the fourth query. - * @details Asserts that there's only one argument, the hotel identifier. - * - * @param argv Values of the arguments. - * @param argc Number of arguments. - * - * @return `NULL` on failure, a pointer to a hotel ID otherwise. - */ + * @brief Parses the arguments of a query of type 4. + * @details Asserts that there's only one argument, a hotel identifier. + * + * @param argc Number of arguments. + * @param argv Values of the arguments. + * + * @return `NULL` on parsing failure, a ::hotel_id_t encoded as a pointer otherwise. + */ void *__q04_parse_arguments(size_t argc, char *const argv[argc]) { if (argc != 1) return NULL; hotel_id_t id; - int retval = hotel_id_from_string(&id, argv[0]); - if (retval == 0) { - hotel_id_t *id_ptr = malloc(sizeof(hotel_id_t)); - if (id_ptr) - *id_ptr = id; - - return id_ptr; - } else if (retval == 2) { - /* TODO - have a way of communicating this failure */ - /* fprintf(stderr, - "Hotel ID \"%s\" not if format HTLXXXXX. This isn't supported by our program!\n", - token);*/ - } - return NULL; + return hotel_id_from_string(&id, argv[0]) ? NULL : GUINT_TO_POINTER(id); } +/** + * @brief Creates a deep clone of the value returned by ::__q04_parse_arguments. + * @param args_data Non-`NULL` value returned by ::__q04_parse_arguments (a hotel's identifier + * encoded as a pointer). + * @return A deep copy of @p args_data. + */ void *__q04_clone_arguments(const void *args_data) { - hotel_id_t *clone = malloc(sizeof(hotel_id_t)); - if (!clone) - return NULL; - *clone = *(const hotel_id_t *) args_data; - return clone; + return (void *) args_data; /* Const cast acceptable - it's an just an integer */ +} + +/** + * @brief Frees data generated by ::__q04_parse_arguments. + * @details Does nothing, as @p args_data is just an integer encoded as a pointer. + * @param args_data Data generated by ::__q04_parse_arguments. + */ +void __q04_free_arguments(void *args_data) { + (void) args_data; } /** - * @brief Callback for every reservation in the database, that adds it to its corresponing hotel - * in @p user_data. - * @details Auxiliary method for ::__q04_generate_statistics. - * - * @param user_data A `GHashTable` that associates hotel IDs to `GPtrArray`s of pointers to - * reservations. - * @param reservation A ::reservation_t in the database. - * - * @retval `0` Always successful. - */ + * @brief Callback for every reservation in the database, that adds it to its corresponding hotel + * in @p user_data. + * @details Auxiliary method for ::__q04_generate_statistics. + * + * @param user_data A ::GConstKeyHashTable that associates hotel identifiers (integers) to arrays + * (::GConstPtrArray) of pointers to reservations (::reservation_t). + * @param reservation A ::reservation_t in the database. + * + * @retval 0 Always successful. + */ int __q04_generate_statistics_foreach_reservation(void *user_data, const reservation_t *reservation) { - GHashTable *hotel_reservations = (GHashTable *) user_data; - hotel_id_t hotel_id = reservation_get_hotel_id(reservation); - GPtrArray *reservations = g_hash_table_lookup(hotel_reservations, GUINT_TO_POINTER(hotel_id)); + const hotel_id_t hotel_id = reservation_get_hotel_id(reservation); + GConstPtrArray *const reservations = g_hash_table_lookup(user_data, GUINT_TO_POINTER(hotel_id)); if (!reservations) return 0; - /* TODO - find a way of not keeping the const */ - g_ptr_array_add(reservations, (reservation_t *) reservation); + g_const_ptr_array_add(reservations, reservation); return 0; } /** - * @brief A comparison function for sorting a `GPtrArray` of reservations by date. - * @details Auxiliary method for ::__q04_generate_statistics_sort_each_array, itself an - * auxiliary method for ::__q04_generate_statistics. - */ + * @brief A comparison function for sorting a ::GConstPtrArray of reservations. + * @details Auxiliary method for ::__q04_generate_statistics_sort_each_array, itself an + * auxiliary method for ::__q04_generate_statistics. + */ gint __q04_sort_reservations_by_date(gconstpointer a, gconstpointer b) { - const reservation_t *reservation_a = *((const reservation_t *const *) a); - const reservation_t *reservation_b = *((const reservation_t *const *) b); + const reservation_t *const reservation_a = *((const reservation_t *const *) a); + const reservation_t *const reservation_b = *((const reservation_t *const *) b); - int64_t diff = date_diff(reservation_get_begin_date(reservation_b), - reservation_get_begin_date(reservation_a)); + const int64_t crit1 = date_diff(reservation_get_begin_date(reservation_b), + reservation_get_begin_date(reservation_a)); + if (crit1) + return crit1; - if (diff == 0) - return reservation_get_id(reservation_a) - reservation_get_id(reservation_b); - else - return diff; + const int64_t crit2 = reservation_get_id(reservation_a) - reservation_get_id(reservation_b); + return crit2; } /** - * @brief Generates statistical data for queries of type 4. - * - * @param database Database, to iterate through reservations. - * @param n Number of instances. - * @param instances Instances of the query. - * - * @return A `GHashTable` associating hotel identifiers to `GPtrArray`s of `reservation_t`s. - */ + * @brief Sorts each array of reservations in the statistical's data hash table. + * + * @param hotel Hotel identifier encoded as a pointer. + * @param list Array of reservations to be sorted (::GConstPtrArray). + * @param data External data (not used). + */ +void __q04_generate_statistics_sort_each_array(gconstpointer hotel, gpointer list, gpointer data) { + (void) hotel; + (void) data; + g_const_ptr_array_sort(list, __q04_sort_reservations_by_date); +} + +/** + * @brief Generates statistical data for queries of type 4. + * + * @param database Database, to iterate through reservations. + * @param n Number of query instances that need to be executed. + * @param instances Query instances that need to be executed. + * + * @return A ::GConstKeyHashTable associating hotel identifiers (integers) to arrays + * (::GConstPtrArray) of reservations (::reservation_t). + */ void *__q04_generate_statistics(const database_t *database, size_t n, const query_instance_t *const instances[n]) { - GHashTable *hotel_reservations = g_hash_table_new_full(g_direct_hash, - g_direct_equal, - NULL, - (GDestroyNotify) g_ptr_array_unref); - - for (size_t i = 0; i < n; ++i) { - hotel_id_t hotel_id = *(hotel_id_t *) query_instance_get_argument_data(instances[i]); - g_hash_table_insert(hotel_reservations, GUINT_TO_POINTER(hotel_id), g_ptr_array_new()); - } + GConstKeyHashTable *const hotel_reservations = + g_const_key_hash_table_new_full(g_direct_hash, + g_direct_equal, + (GDestroyNotify) g_const_ptr_array_unref); + for (size_t i = 0; i < n; ++i) + g_const_key_hash_table_insert(hotel_reservations, + query_instance_get_argument_data(instances[i]), /* hotel id */ + g_const_ptr_array_new()); reservation_manager_iter(database_get_reservations(database), __q04_generate_statistics_foreach_reservation, hotel_reservations); - GHashTableIter iter; - gpointer to_sort_array; - g_hash_table_iter_init(&iter, hotel_reservations); - while (g_hash_table_iter_next(&iter, NULL, &to_sort_array)) - g_ptr_array_sort((GPtrArray *) to_sort_array, __q04_sort_reservations_by_date); - + g_const_key_hash_table_foreach(hotel_reservations, + __q04_generate_statistics_sort_each_array, + NULL); return hotel_reservations; } @@ -149,13 +154,14 @@ void *__q04_generate_statistics(const database_t *database, * * @param database Database to get data from (not used, as all data is collected in * ::__q04_generate_statistics). - * @param statistics Statistical data generated by ::__q04_generate_statistics (a hashtable that - * associates identifiers of hotels to `GArray`s of reservations). + * @param statistics Statistical data generated by ::__q04_generate_statistics (a + * ::GConstKeyHashTable that associates identifiers of hotels (integers) to + * arrays (::GConstPtrArray) of reservations (::reservation_t)). * @param instance Query instance to be executed. * @param output Where to write the query's result to. * - * @retval 0 Success - * @retval 1 Fatal failure (shouldn't happen nunder normal circumstances). + * @retval 0 Success. + * @retval 1 Fatal failure (should, in principle, be unreachable). */ int __q04_execute(const database_t *database, const void *statistics, @@ -163,30 +169,28 @@ int __q04_execute(const database_t *database, query_writer_t *output) { (void) database; - hotel_id_t hotel_id = *((const hotel_id_t *) query_instance_get_argument_data(instance)); - GPtrArray *reservations = - g_hash_table_lookup((GHashTable *) statistics, GUINT_TO_POINTER(hotel_id)); + const hotel_id_t hotel_id = GPOINTER_TO_UINT(query_instance_get_argument_data(instance)); + const GConstPtrArray *const reservations = + g_const_key_hash_table_const_lookup(statistics, GUINT_TO_POINTER(hotel_id)); if (!reservations) { fprintf(stderr, "Bad statistical data in query 4! This should not happen!\n"); return 1; /* Only happens on bad statistics, which itself shouldn't happen. */ } - for (size_t i = 0; i < reservations->len; i++) { - const reservation_t *reservation = g_ptr_array_index(reservations, i); + const size_t reservations_len = g_const_ptr_array_get_length(reservations); + for (size_t i = 0; i < reservations_len; i++) { + const reservation_t *const reservation = g_const_ptr_array_index(reservations, i); - reservation_id_t reservation_id = reservation_get_id(reservation); - date_t begin_date = reservation_get_begin_date(reservation); - date_t end_date = reservation_get_end_date(reservation); - const char *user_id = reservation_get_const_user_id(reservation); - uint8_t rating = reservation_get_rating(reservation); - double total_price = reservation_calculate_price(reservation); + const char *const user_id = reservation_get_const_user_id(reservation); + const uint8_t rating = reservation_get_rating(reservation); + const double total_price = reservation_calculate_price(reservation); char begin_date_str[DATE_SPRINTF_MIN_BUFFER_SIZE]; char end_date_str[DATE_SPRINTF_MIN_BUFFER_SIZE]; char reservation_id_str[RESERVATION_ID_SPRINTF_MIN_BUFFER_SIZE]; - date_sprintf(begin_date_str, begin_date); - date_sprintf(end_date_str, end_date); - reservation_id_sprintf(reservation_id_str, reservation_id); + date_sprintf(begin_date_str, reservation_get_begin_date(reservation)); + date_sprintf(end_date_str, reservation_get_end_date(reservation)); + reservation_id_sprintf(reservation_id_str, reservation_get_id(reservation)); query_writer_write_new_object(output); query_writer_write_new_field(output, "id", "%s", reservation_id_str); @@ -194,7 +198,7 @@ int __q04_execute(const database_t *database, query_writer_write_new_field(output, "end_date", "%s", end_date_str); query_writer_write_new_field(output, "user_id", "%s", user_id); query_writer_write_new_field(output, "rating", "%" PRIu8, rating); - query_writer_write_new_field(output, "total_price", "%.3f", total_price); + query_writer_write_new_field(output, "total_price", "%.3lf", total_price); } return 0; @@ -204,7 +208,7 @@ query_type_t *q04_create(void) { return query_type_create(4, __q04_parse_arguments, __q04_clone_arguments, - free, + __q04_free_arguments, __q04_generate_statistics, (query_type_free_statistics_callback_t) g_hash_table_unref, __q04_execute); From f33120d86c7d396e8f676cbe49ecbbea70ddf1b6 Mon Sep 17 00:00:00 2001 From: voidbert <50591320+voidbert@users.noreply.github.com> Date: Mon, 22 Jan 2024 00:56:03 +0000 Subject: [PATCH 10/19] Cleanup of query 5 --- trabalho-pratico/include/queries/q05.h | 7 +- trabalho-pratico/src/queries/q05.c | 174 ++++++++++++++----------- 2 files changed, 103 insertions(+), 78 deletions(-) diff --git a/trabalho-pratico/include/queries/q05.h b/trabalho-pratico/include/queries/q05.h index 1433a5f..64a3080 100644 --- a/trabalho-pratico/include/queries/q05.h +++ b/trabalho-pratico/include/queries/q05.h @@ -37,9 +37,10 @@ #include "queries/query_type.h" /** - * @brief Initializes the definition of the fifth query. - * @details This is done automatically in ::query_type_list_init. - * @return A pointer to a `malloc`-allocated ::query_type_t on success, or `NULL` on failure. + * @brief Initializes the definition of queries of type 5. + * @details This is done automatically in [query_type_list](@ref query_type_list.c). + * @return On success, a pointer to a ::query_type_t that must be deleted with ::query_type_free, + * or `NULL` allocation on failure. */ query_type_t *q05_create(void); diff --git a/trabalho-pratico/src/queries/q05.c b/trabalho-pratico/src/queries/q05.c index ecacd62..d641b6f 100644 --- a/trabalho-pratico/src/queries/q05.c +++ b/trabalho-pratico/src/queries/q05.c @@ -19,19 +19,19 @@ * @brief Implementation of methods in include/queries/q05.h */ -#include #include -#include #include "queries/q05.h" #include "queries/query_instance.h" +#include "utils/glib/GConstKeyHashTable.h" +#include "utils/glib/GConstPtrArray.h" /** * @struct q05_parsed_arguments_t - * @brief Data needed for the execution of a query of type 5. + * @brief Parsed arguments of a query of type 5. * * @var q05_parsed_arguments_t::airport_code - * @brief Parsed origin airport code. + * @brief Code of the origin airport. * @var q05_parsed_arguments_t::begin_date * @brief Beginning date for date range filter. * @var q05_parsed_arguments_t::end_date @@ -44,25 +44,26 @@ typedef struct { } q05_parsed_arguments_t; /** - * @brief Parses arguments for the fifth query. - * @details Asserts that there's three arguments, an airport code, and two dates with time. + * @brief Parses the arguments of a query of type 5. + * @details Asserts that there's three arguments, an airport code, and two dates with times. * * @param argc Number of arguments. * @param argv Values of the arguments. * - * @return `NULL` on failure, a pointer to a `q05_airport_data_t` otherwise. + * @return `NULL` on parsing or allocation failure, a pointer to a ::q05_parsed_arguments_t + * otherwise. */ void *__q05_parse_arguments(size_t argc, char *const argv[argc]) { if (argc != 3) return NULL; - q05_parsed_arguments_t *parsed_arguments = malloc(sizeof(q05_parsed_arguments_t)); + q05_parsed_arguments_t *const parsed_arguments = malloc(sizeof(q05_parsed_arguments_t)); if (!parsed_arguments) return NULL; - int airport_retval = airport_code_from_string(&parsed_arguments->airport_code, argv[0]); - int begin_date_retval = date_and_time_from_string(&parsed_arguments->begin_date, argv[1]); - int end_date_retval = date_and_time_from_string(&parsed_arguments->end_date, argv[2]); + const int airport_retval = airport_code_from_string(&parsed_arguments->airport_code, argv[0]); + const int begin_date_retval = date_and_time_from_string(&parsed_arguments->begin_date, argv[1]); + const int end_date_retval = date_and_time_from_string(&parsed_arguments->end_date, argv[2]); if (airport_retval || begin_date_retval || end_date_retval) { free(parsed_arguments); @@ -72,9 +73,15 @@ void *__q05_parse_arguments(size_t argc, char *const argv[argc]) { return parsed_arguments; } +/** + * @brief Creates a deep clone of the value returned by ::__q05_parse_arguments. + * @param args_data Non-`NULL` value returned by ::__q05_parse_arguments (a pointer to a + * ::q05_parsed_arguments_t). + * @return A deep copy of @p args_data. + */ void *__q05_clone_arguments(const void *args_data) { - const q05_parsed_arguments_t *args = args_data; - q05_parsed_arguments_t *clone = malloc(sizeof(q05_parsed_arguments_t)); + const q05_parsed_arguments_t *const args = args_data; + q05_parsed_arguments_t *const clone = malloc(sizeof(q05_parsed_arguments_t)); if (!clone) return NULL; @@ -87,20 +94,20 @@ void *__q05_clone_arguments(const void *args_data) { * @brief Data needed while iterating over flights. * * @var q05_foreach_airport_data_t::filter_data - * @brief Array of pointers to ::q05_parsed_arguments_t. Information about which flights to - * keep. + * @brief Array of pointers to ::q05_parsed_arguments_t. Information about which flights and + * date ranges to keep. * @var q05_foreach_airport_data_t::origin_flights - * @brief Associations between ::q05_parsed_arguments_t and `GPtrArray`s of pointers to - * ::flight_t. + * @brief Associations between ::q05_parsed_arguments_t and arrays (::GConstPtrArray) of + * pointers to flights (::flight_t). */ typedef struct { - GPtrArray *filter_data; - GHashTable *origin_flights; + GConstPtrArray *const filter_data; + GConstKeyHashTable *const origin_flights; } q05_foreach_airport_data_t; /** * @brief A method called for each flight, to consider it in the list of flights with an origin - * on a given airport, depending on a time frame. + * on a given airport, depending on range of times. * @details An auxiliary method for ::__q05_generate_statistics. * * @param user_data A pointer to a ::q05_foreach_airport_data_t. @@ -109,87 +116,100 @@ typedef struct { * @retval 0 Always successful. */ int __q05_generate_statistics_foreach_flight(void *user_data, const flight_t *flight) { - q05_foreach_airport_data_t *foreach_data = (q05_foreach_airport_data_t *) user_data; + q05_foreach_airport_data_t *const foreach_data = user_data; airport_code_t airport = flight_get_origin(flight); date_and_time_t schedule_departure_date = flight_get_schedule_departure_date(flight); /* Add flight to all query answers whose filter matches */ - for (size_t i = 0; i < foreach_data->filter_data->len; i++) { - const q05_parsed_arguments_t *args = g_ptr_array_index(foreach_data->filter_data, i); + const size_t filter_data_len = g_const_ptr_array_get_length(foreach_data->filter_data); + for (size_t i = 0; i < filter_data_len; i++) { + const q05_parsed_arguments_t *const args = + g_const_ptr_array_index(foreach_data->filter_data, i); /* Check if the flight meets the filters in the arguments */ if (airport == args->airport_code && date_and_time_diff(args->begin_date, schedule_departure_date) <= 0 && date_and_time_diff(args->end_date, schedule_departure_date) >= 0) { - /* Add flight to answer (create answer array if needed) */ - GPtrArray *flights = g_hash_table_lookup(foreach_data->origin_flights, args); - /* TODO - find a way to keep const */ - g_ptr_array_add(flights, (flight_t *) flight); + /* Add flight to answer */ + GConstPtrArray *const flights = + g_const_key_hash_table_lookup(foreach_data->origin_flights, args); + g_const_ptr_array_add(flights, flight); } } return 0; } +/** + * @brief A comparison function for sorting a ::GConstPtrArray of flights. + * @details Auxiliary method for ::__q05_generate_statistics_sort_each_array, itself an + * auxiliary method for ::__q05_generate_statistics. + */ +gint __q05_flights_date_compare_func(gconstpointer a, gconstpointer b) { + const flight_t *const flight_a = *((const flight_t *const *) a); + const flight_t *const flight_b = *((const flight_t *const *) b); + + const int64_t crit1 = date_and_time_diff(flight_get_schedule_departure_date(flight_b), + flight_get_schedule_departure_date(flight_a)); + if (crit1) + return crit1; + + const int32_t crit2 = flight_get_id(flight_a) - flight_get_id(flight_b); + return crit2; +} + +/** + * @brief Sorts each array of flights in the statistical's data hash table. + * + * @param query Pointer to a ::q05_foreach_airport_data_t. + * @param list Array of reservations to be sorted (::GConstPtrArray). + * @param data External data (not used). + */ +void __q05_generate_statistics_sort_each_array(gconstpointer query, gpointer list, gpointer data) { + (void) query; + (void) data; + g_const_ptr_array_sort(list, __q05_flights_date_compare_func); +} + /** * @brief Generates statistical data for queries of type 5. * - * @param database Database, to iterate through flights. - * @param n Number of query instances. - * @param instances Instances of the query 5. + * @param database Database, to iterate through flights. + * @param n Number of query instances that need to be executed. + * @param instances Query instances that need to be executed. * - * @return A `GHashTable` associating a ::q05_foreach_airport_data_t for each query to a - * `GPtrArray` of ::flight_t `*`s. + * @return A ::GConstKeyHashTable associating a ::q05_foreach_airport_data_t for each query to a + * ::GConstPtrArray of pointers to flights (::flight_t). */ void *__q05_generate_statistics(const database_t *database, size_t n, const query_instance_t *const instances[n]) { - GPtrArray *filter_data = g_ptr_array_new(); - GHashTable *origin_flights = g_hash_table_new_full(g_direct_hash, - g_direct_equal, - NULL, - (GDestroyNotify) g_ptr_array_unref); + GConstPtrArray *const filter_data = g_const_ptr_array_new(); + GConstKeyHashTable *const origin_flights = + g_const_key_hash_table_new_full(g_direct_hash, + g_direct_equal, + (GDestroyNotify) g_ptr_array_unref); for (size_t i = 0; i < n; ++i) { - const q05_parsed_arguments_t *argument_data = + const q05_parsed_arguments_t *const argument_data = query_instance_get_argument_data(instances[i]); - /* TODO - find way of keeping const */ - g_hash_table_insert(origin_flights, - (q05_parsed_arguments_t *) argument_data, - g_ptr_array_new()); - g_ptr_array_add(filter_data, (q05_parsed_arguments_t *) argument_data); + g_const_key_hash_table_insert(origin_flights, argument_data, g_const_ptr_array_new()); + g_const_ptr_array_add(filter_data, argument_data); } q05_foreach_airport_data_t callback_data = {.filter_data = filter_data, .origin_flights = origin_flights}; - flight_manager_iter(database_get_flights(database), __q05_generate_statistics_foreach_flight, &callback_data); - g_ptr_array_unref(filter_data); - return origin_flights; -} - -/** - * @brief A comparison function for sorting a `GPtrArray` of flights by date. - * @details Auxiliary method for ::__q05_generate_statistics_sort_each_array, itself an - * auxiliary method for ::__q05_generate_statistics. - */ -gint __q05_flights_date_compare_func(gconstpointer a, gconstpointer b) { - const flight_t *flight_a = *((const flight_t *const *) a); - const flight_t *flight_b = *((const flight_t *const *) b); - - int64_t crit1 = date_and_time_diff(flight_get_schedule_departure_date(flight_b), - flight_get_schedule_departure_date(flight_a)); - if (crit1) - return crit1; + g_const_key_hash_table_foreach(origin_flights, __q05_generate_statistics_sort_each_array, NULL); - int32_t crit2 = flight_get_id(flight_a) - flight_get_id(flight_b); - return crit2; + g_const_ptr_array_unref(filter_data); + return origin_flights; } /** @@ -197,12 +217,14 @@ gint __q05_flights_date_compare_func(gconstpointer a, gconstpointer b) { * * @param database Database to get data from (not used, as all data is collected in * ::__q05_generate_statistics). - * @param statistics Statistical data generated by ::__q05_generate_statistics (a `GHashTable` that - * associates ::q05_parsed_arguments_t to `GPtrArray`s of flights). + * @param statistics Statistical data generated by ::__q05_generate_statistics (a + * ::GConstKeyHashTable that associates query arguments (::q05_parsed_arguments_t) + * to arrays (::GConstPtrArray) of flights (::flight_id_t)). * @param instance Query instance to be executed. * @param output Where to write the query's result to. * - * @retval 0 Always successful + * @retval 0 Success. + * @retval 1 Fatal failure (should, in principle, be unreachable). */ int __q05_execute(const database_t *database, const void *statistics, @@ -210,17 +232,19 @@ int __q05_execute(const database_t *database, query_writer_t *output) { (void) database; - GHashTable *origin_flights = (GHashTable *) statistics; - const q05_parsed_arguments_t *arguments = query_instance_get_argument_data(instance); + const GConstKeyHashTable *const origin_flights = statistics; + const q05_parsed_arguments_t *const arguments = query_instance_get_argument_data(instance); - GPtrArray *flights = g_hash_table_lookup(origin_flights, arguments); - if (!flights) - return 0; + const GConstPtrArray *const flights = + g_const_key_hash_table_const_lookup(origin_flights, arguments); + if (!flights) { + fprintf(stderr, "Bad statistical data in query 5! This should not happen!\n"); + return 1; /* Only happens on bad statistics, which itself shouldn't happen. */ + } - /* Sorting is only done once per query */ - g_ptr_array_sort(flights, __q05_flights_date_compare_func); - for (size_t i = 0; i < flights->len; i++) { - const flight_t *flight = g_ptr_array_index(flights, i); + const size_t flights_len = g_const_ptr_array_get_length(flights); + for (size_t i = 0; i < flights_len; i++) { + const flight_t *const flight = g_const_ptr_array_index(flights, i); char scheduled_departure_str[DATE_AND_TIME_SPRINTF_MIN_BUFFER_SIZE]; date_and_time_sprintf(scheduled_departure_str, flight_get_schedule_departure_date(flight)); From 005fc6d2ded08dd60b69dde3a859a4b87dd6fdde Mon Sep 17 00:00:00 2001 From: voidbert <50591320+voidbert@users.noreply.github.com> Date: Mon, 22 Jan 2024 01:25:26 +0000 Subject: [PATCH 11/19] Query 5 optimization --- trabalho-pratico/src/queries/q05.c | 31 +++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/trabalho-pratico/src/queries/q05.c b/trabalho-pratico/src/queries/q05.c index d641b6f..10134cb 100644 --- a/trabalho-pratico/src/queries/q05.c +++ b/trabalho-pratico/src/queries/q05.c @@ -93,7 +93,9 @@ void *__q05_clone_arguments(const void *args_data) { * @struct q05_foreach_airport_data_t * @brief Data needed while iterating over flights. * - * @var q05_foreach_airport_data_t::filter_data + * @var q05_foreach_airport_data_t::nfilters + * @brief Number of elements in ::q05_foreach_airport_data_t::filters. + * @var q05_foreach_airport_data_t::filters * @brief Array of pointers to ::q05_parsed_arguments_t. Information about which flights and * date ranges to keep. * @var q05_foreach_airport_data_t::origin_flights @@ -101,8 +103,9 @@ void *__q05_clone_arguments(const void *args_data) { * pointers to flights (::flight_t). */ typedef struct { - GConstPtrArray *const filter_data; - GConstKeyHashTable *const origin_flights; + size_t nfilters; + const q05_parsed_arguments_t *const *const filters; + GConstKeyHashTable *const origin_flights; } q05_foreach_airport_data_t; /** @@ -122,10 +125,8 @@ int __q05_generate_statistics_foreach_flight(void *user_data, const flight_t *fl date_and_time_t schedule_departure_date = flight_get_schedule_departure_date(flight); /* Add flight to all query answers whose filter matches */ - const size_t filter_data_len = g_const_ptr_array_get_length(foreach_data->filter_data); - for (size_t i = 0; i < filter_data_len; i++) { - const q05_parsed_arguments_t *const args = - g_const_ptr_array_index(foreach_data->filter_data, i); + for (size_t i = 0; i < foreach_data->nfilters; i++) { + const q05_parsed_arguments_t *const args = foreach_data->filters[i]; /* Check if the flight meets the filters in the arguments */ if (airport == args->airport_code && @@ -181,12 +182,16 @@ void __q05_generate_statistics_sort_each_array(gconstpointer query, gpointer lis * @param instances Query instances that need to be executed. * * @return A ::GConstKeyHashTable associating a ::q05_foreach_airport_data_t for each query to a - * ::GConstPtrArray of pointers to flights (::flight_t). + * ::GConstPtrArray of pointers to flights (::flight_t). `NULL` may be returned on + * allocation failure. */ void *__q05_generate_statistics(const database_t *database, size_t n, const query_instance_t *const instances[n]) { - GConstPtrArray *const filter_data = g_const_ptr_array_new(); + const q05_parsed_arguments_t **const filters = malloc(n * sizeof(q05_parsed_arguments_t *)); + if (!filters) + return NULL; + GConstKeyHashTable *const origin_flights = g_const_key_hash_table_new_full(g_direct_hash, g_direct_equal, @@ -197,18 +202,18 @@ void *__q05_generate_statistics(const database_t *database, query_instance_get_argument_data(instances[i]); g_const_key_hash_table_insert(origin_flights, argument_data, g_const_ptr_array_new()); - g_const_ptr_array_add(filter_data, argument_data); + filters[i] = argument_data; } - q05_foreach_airport_data_t callback_data = {.filter_data = filter_data, + q05_foreach_airport_data_t callback_data = {.nfilters = n, + .filters = filters, .origin_flights = origin_flights}; flight_manager_iter(database_get_flights(database), __q05_generate_statistics_foreach_flight, &callback_data); g_const_key_hash_table_foreach(origin_flights, __q05_generate_statistics_sort_each_array, NULL); - - g_const_ptr_array_unref(filter_data); + free(filters); return origin_flights; } From a11efcbec804c6d1ec9467a62a92cdad3300c60d Mon Sep 17 00:00:00 2001 From: voidbert <50591320+voidbert@users.noreply.github.com> Date: Mon, 22 Jan 2024 02:47:24 +0000 Subject: [PATCH 12/19] Cleanup query 6 --- trabalho-pratico/include/queries/q06.h | 68 +++++----- trabalho-pratico/src/queries/q06.c | 172 +++++++++++++------------ 2 files changed, 123 insertions(+), 117 deletions(-) diff --git a/trabalho-pratico/include/queries/q06.h b/trabalho-pratico/include/queries/q06.h index c204307..6ec13db 100644 --- a/trabalho-pratico/include/queries/q06.h +++ b/trabalho-pratico/include/queries/q06.h @@ -1,37 +1,34 @@ /* - * Copyright 2023 Humberto Gomes, José Lopes, José Matos - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ + * Copyright 2023 Humberto Gomes, José Lopes, José Matos + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ /** - * @file q06.h - * @brief List the top N airports with the most passengers, for a given year. Flights with an - * estimated departure date in that year must be counted. - * @details If two airports have the same value, the name of the airport must be used as a - * tiebreaker (in ascending order). - * - * ### Examples - * - * ```text - * 6 2023 10 - * 6 2022 10 - * 6 2021 10 - * 6F 2023 10 - * 6F 2022 10 - * 6F 2021 10 - * ``` - */ + * @file q06.h + * @brief List the top N airports with the most passengers, for a given year. + * + * ### Examples + * + * ```text + * 6 2023 10 + * 6 2022 10 + * 6 2021 10 + * 6F 2023 10 + * 6F 2022 10 + * 6F 2021 10 + * ``` + */ #ifndef Q06_H #define Q06_H @@ -39,10 +36,11 @@ #include "queries/query_type.h" /** - * @brief Initializes the definition of the sixth query. - * @details This is done automatically in ::query_type_list_create. - * @return A pointer to a `malloc`-allocated ::query_type_t on success, or `NULL` on failure. - */ + * @brief Initializes the definition of queries of type 6. + * @details This is done automatically in [query_type_list](@ref query_type_list.c). + * @return On success, a pointer to a ::query_type_t that must be deleted with ::query_type_free, + * or `NULL` allocation on failure. + */ query_type_t *q06_create(void); #endif diff --git a/trabalho-pratico/src/queries/q06.c b/trabalho-pratico/src/queries/q06.c index 3792e9d..e834d2f 100644 --- a/trabalho-pratico/src/queries/q06.c +++ b/trabalho-pratico/src/queries/q06.c @@ -19,51 +19,50 @@ * @brief Implementation of methods in include/queries/q06.h */ -#include #include -#include #include "queries/q06.h" #include "queries/query_instance.h" +#include "utils/glib/GConstKeyHashTable.h" #include "utils/int_utils.h" /** * @struct q06_parsed_arguments_t - * @brief A structure to hold the parsed arguments for a query of type 6. + * @brief Parsed arguments for a query of type 6. * - * @var q06_parsed_arguments_t::year - * @brief The year to consider. * @var q06_parsed_arguments_t::n * @brief The number of airports to display. + * @var q06_parsed_arguments_t::year + * @brief The year to consider. */ typedef struct { - uint16_t year; size_t n; + uint16_t year; } q06_parsed_arguments_t; /** - * @brief Parses the arguments for a query of type 6. + * @brief Parses the arguments for a query of type 6. * @details Asserts that there are exactly two arguments, a year and a number of airports to * display. * - * @param argv List of query arguments. * @param argc Number of query arguments. + * @param argv List of query arguments. * - * @return A pointer to a ::q06_parsed_arguments_t, or `NULL` on failure. + * @return A pointer to a ::q06_parsed_arguments_t, or `NULL` on parsing or allocation failure. */ void *__q06_parse_arguments(size_t argc, char *const argv[argc]) { if (argc != 2) return NULL; - q06_parsed_arguments_t *args = malloc(sizeof(q06_parsed_arguments_t)); + q06_parsed_arguments_t *const args = malloc(sizeof(q06_parsed_arguments_t)); if (!args) return NULL; /* Parse year */ - size_t length = strlen(argv[0]); + const size_t length = strlen(argv[0]); if (length == 4) { - uint64_t year; - int retval = int_utils_parse_positive(&year, argv[0]); + uint64_t year; + const int retval = int_utils_parse_positive(&year, argv[0]); if (retval) { free(args); @@ -76,9 +75,9 @@ void *__q06_parse_arguments(size_t argc, char *const argv[argc]) { return NULL; /* Invalid year format */ } - /* Parse number of flights */ - uint64_t n; - int retval = int_utils_parse_positive(&n, argv[1]); + /* Parse the number of flights */ + uint64_t n; + const int retval = int_utils_parse_positive(&n, argv[1]); if (retval) { free(args); return NULL; /* Invalid N format */ @@ -88,9 +87,15 @@ void *__q06_parse_arguments(size_t argc, char *const argv[argc]) { return args; } +/** + * @brief Creates a deep clone of the value returned by ::__q06_parse_arguments. + * @param args_data Non-`NULL` value returned by ::__q06_parse_arguments (a pointer to a + * ::q06_parsed_arguments_t). + * @return A deep copy of @p args_data. + */ void *__q06_clone_arguments(const void *args_data) { - const q06_parsed_arguments_t *args = args_data; - q06_parsed_arguments_t *clone = malloc(sizeof(q06_parsed_arguments_t)); + const q06_parsed_arguments_t *const args = args_data; + q06_parsed_arguments_t *const clone = malloc(sizeof(q06_parsed_arguments_t)); if (!clone) return NULL; @@ -103,14 +108,15 @@ void *__q06_clone_arguments(const void *args_data) { * @details Auxiliary function for ::__q06_generate_statistics_foreach_flight, itself an auxiliary * method for ::__q06_generate_statistics. * - * @param airport_count Hash table that associates `airport_code_t`s with numbers of passengers. + * @param airport_count `GHashTable` that associates airports (::airport_code_t) with numbers of + * passengers. * @param airport Airport to add passengers to. * @param num_passengers Number of passengers to be added to @p airport in @p airport_count. */ void __q06_generate_statistics_add_passengers(GHashTable *airport_count, airport_code_t airport, uint16_t num_passengers) { - uint64_t origin_count = + const uint64_t origin_count = GPOINTER_TO_UINT(g_hash_table_lookup(airport_count, GUINT_TO_POINTER(airport))); g_hash_table_insert(airport_count, GUINT_TO_POINTER(airport), @@ -118,63 +124,60 @@ void __q06_generate_statistics_add_passengers(GHashTable *airport_count, } /** - * @brief Method called for each flight, to generate statistical data. + * @brief Method called for each flight, to generate statistical data. * @details An auxiliary method for ::__q06_generate_statistics. * * @param user_data A `GHashTable` that associates a year with another `GHashTable`, that itself - * associates airport codes with integers numbers of passengers. + * associates airport codes with numbers of passengers (integers). * @param flight The flight to consider. * - * @retval 0 Always successful + * @retval 0 Always successful. */ int __q06_generate_statistics_foreach_flight(void *user_data, const flight_t *flight) { - GHashTable *years_airport_count = (GHashTable *) user_data; - - uint16_t year = + const uint16_t year = date_get_year(date_and_time_get_date(flight_get_schedule_departure_date(flight))); /* Get the airport code -> passenger count mapping */ - GHashTable *airport_count = g_hash_table_lookup(years_airport_count, GUINT_TO_POINTER(year)); + GHashTable *const airport_count = g_hash_table_lookup(user_data, GUINT_TO_POINTER(year)); if (!airport_count) return 0; /* Year not asked for in queries */ - airport_code_t origin = flight_get_origin(flight); - airport_code_t destination = flight_get_destination(flight); - uint16_t num_passengers = flight_get_number_of_passengers(flight); + const airport_code_t origin = flight_get_origin(flight); + const airport_code_t destination = flight_get_destination(flight); + const uint16_t num_passengers = flight_get_number_of_passengers(flight); __q06_generate_statistics_add_passengers(airport_count, origin, num_passengers); __q06_generate_statistics_add_passengers(airport_count, destination, num_passengers); - return 0; } /** * @struct q06_array_item_t - * @brief A structure to hold an item in the array of airport passanger counts. + * @brief A pair formed by an airport and its number of passengers. * * @var q06_array_item_t::airport * @brief An airport. * @var q06_array_item_t::count - * @brief The number of passangers. + * @brief The number of passangers of q06_array_item_t::airport. */ typedef struct { airport_code_t airport; - uint64_t count; + uint32_t count; } q06_array_item_t; /** - * @brief Method called for each airport-passenger count pair in a `GHashTable`, that adds it to a - * `GArray`. - * @details An auxiliary method for ::__q06_execute. + * @brief Method called for each airport-passenger count pair in a `GHashTable`, which is added to + * a `GArray`. + * @details An auxiliary method for ::__q06_generate_statistics_foreach_year. * - * @param key The key in the `GHashTable`, an `airport_code_t`. + * @param key The key in the `GHashTable`, an ::airport_code_t. * @param value The value in the `GHashTable`, an integer (as a pointer). * @param user_data The `GArray` of airport + passanger count tuples (::q06_array_item_t). */ void __q06_foreach_airport_count(gpointer key, gpointer value, gpointer user_data) { - q06_array_item_t item = {.airport = (airport_code_t) (size_t) key, - .count = GPOINTER_TO_UINT(value)}; - g_array_append_val((GArray *) user_data, item); + const q06_array_item_t item = {.airport = GPOINTER_TO_UINT(key), + .count = GPOINTER_TO_UINT(value)}; + g_array_append_val(user_data, item); } /** @@ -182,70 +185,70 @@ void __q06_foreach_airport_count(gpointer key, gpointer value, gpointer user_dat * @details Used for array sorting in ::__q06_generate_statistics_foreach_year. */ gint __q06_sort_airports_by_count(gconstpointer a, gconstpointer b) { - const q06_array_item_t *item_a = a; - const q06_array_item_t *item_b = b; + const q06_array_item_t *const item_a = a; + const q06_array_item_t *const item_b = b; - if (item_b->count == item_a->count) { - char a_airport_str[AIRPORT_CODE_SPRINTF_MIN_BUFFER_SIZE]; - char b_airport_str[AIRPORT_CODE_SPRINTF_MIN_BUFFER_SIZE]; - airport_code_sprintf(a_airport_str, item_a->airport); - airport_code_sprintf(b_airport_str, item_b->airport); + const int64_t crit1 = item_b->count - item_a->count; + if (crit1) + return crit1; - return strcmp(a_airport_str, b_airport_str); - } else { - return item_b->count - item_a->count; - } + char a_airport_str[AIRPORT_CODE_SPRINTF_MIN_BUFFER_SIZE]; + char b_airport_str[AIRPORT_CODE_SPRINTF_MIN_BUFFER_SIZE]; + airport_code_sprintf(a_airport_str, item_a->airport); + airport_code_sprintf(b_airport_str, item_b->airport); + + return strcmp(a_airport_str, b_airport_str); } /** - * @brief Converts a `GHashTable` to an ordered `GArray` of key-value tuples (::q06_array_item_t). - * @details Called for every year. + * @brief Converts a `GHashTable` (::airport_code_t -> passenger count) to an ordered `GArray` of + * key-value tuples (::q06_array_item_t). + * @details The `GArray` will be inserted into a ::GConstKeyHashTable whose keys are years. This is + * an auxiliary method for ::__q06_generate_statistics. * * @param key_year Year being processed (as a pointer). - * @param value_airport_count `GHashTable` (`airport -> passenger count`) associated to @p key_year. - * @param user_data `GHashTable` (`year -> tuple array`) to write the output array to. + * @param value_airport_count `GHashTable` (::airport_code_t -> passenger count) associated to + * @p key_year. + * @param user_data ::GConstKeyHashTable (year -> tuple array) to write the output array + * to. */ void __q06_generate_statistics_foreach_year(gpointer key_year, gpointer value_airport_count, gpointer user_data) { - int16_t year = GPOINTER_TO_UINT(key_year); - GHashTable *airport_count = (GHashTable *) value_airport_count; - - GArray *airport_count_array = + GHashTable *const airport_count = value_airport_count; + GArray *const airport_count_array = g_array_sized_new(FALSE, FALSE, sizeof(q06_array_item_t), g_hash_table_size(airport_count)); g_hash_table_foreach(airport_count, __q06_foreach_airport_count, airport_count_array); g_array_sort(airport_count_array, __q06_sort_airports_by_count); - g_hash_table_insert((GHashTable *) user_data, GUINT_TO_POINTER(year), airport_count_array); + g_const_key_hash_table_insert(user_data, key_year, airport_count_array); } /** * @brief Generates statistical data for queries of type 6. - * @details Generates a `GHashTable` that associates years with sorted `GArray`s of - * ::q06_array_item_t (airport + passenger count tuples). * - * @param database Database to get data from. + * @param database Database, to iterate through flights. * @param n Number of query instances to be executed. * @param instances Array of query instances to be executed. * - * @return A `GHashTable` that associates years with sorted `GArray`s of ::q06_array_item_t + * @return A ::GConstKeyHashTable that associates years with sorted `GArray`s of ::q06_array_item_t * (airport + passenger count tuples). */ void *__q06_generate_statistics(const database_t *database, size_t n, const query_instance_t *const instances[n]) { - GHashTable *years_airport_count = g_hash_table_new_full(g_direct_hash, + + GHashTable *const years_airport_count = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) g_hash_table_unref); - for (size_t i = 0; i < n; ++i) { - int16_t year = + const int16_t year = ((const q06_parsed_arguments_t *) query_instance_get_argument_data(instances[i]))->year; - GHashTable *airport_count = g_hash_table_new(g_direct_hash, g_direct_equal); + GHashTable *const airport_count = g_hash_table_new(g_direct_hash, g_direct_equal); g_hash_table_insert(years_airport_count, GUINT_TO_POINTER(year), airport_count); } @@ -254,9 +257,10 @@ void *__q06_generate_statistics(const database_t *database, years_airport_count); /* Create a sorted array for each year (instead of a hash table) */ - GHashTable *years_airport_count_array = - g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) g_array_unref); - + GConstKeyHashTable *const years_airport_count_array = + g_const_key_hash_table_new_full(g_direct_hash, + g_direct_equal, + (GDestroyNotify) g_array_unref); g_hash_table_foreach(years_airport_count, __q06_generate_statistics_foreach_year, years_airport_count_array); @@ -269,12 +273,13 @@ void *__q06_generate_statistics(const database_t *database, * @brief Executes a query of type 6. * @details Prints the top N airports with the most passangers in a given year. * - * @param database Database to get data from. + * @param database Database to get data from (not used, as all data is collected in + * ::__q06_generate_statistics). * @param statistics Statistics generated by ::__q06_generate_statistics. * @param instance Query instance to be executed. - * @param output File to write output to. + * @param output Where to write the query's result to. * - * @retval 0 Always successful + * @retval 0 Always successful. */ int __q06_execute(const database_t *database, const void *statistics, @@ -282,25 +287,28 @@ int __q06_execute(const database_t *database, query_writer_t *output) { (void) database; - const q06_parsed_arguments_t *args = query_instance_get_argument_data(instance); + const q06_parsed_arguments_t *const args = query_instance_get_argument_data(instance); + + const GConstKeyHashTable *const years_airport_count_array = statistics; + const GArray *const airport_count = + g_const_key_hash_table_const_lookup(years_airport_count_array, + GUINT_TO_POINTER(args->year)); - GHashTable *years_airport_count_array = (GHashTable *) statistics; - GArray *airport_count = - (GArray *) g_hash_table_lookup(years_airport_count_array, GUINT_TO_POINTER(args->year)); if (!airport_count) { fprintf(stderr, "Bad statistical data in query 6! This should not happen!\n"); return 1; /* Only if statistics are bad, which shouldn't happen */ } - for (size_t i = 0; i < min(args->n, airport_count->len); ++i) { - const q06_array_item_t *item = &g_array_index(airport_count, q06_array_item_t, i); + const size_t i_max = min(args->n, airport_count->len); + for (size_t i = 0; i < i_max; ++i) { + const q06_array_item_t *const item = &g_array_index(airport_count, q06_array_item_t, i); char airport_code_str[AIRPORT_CODE_SPRINTF_MIN_BUFFER_SIZE]; airport_code_sprintf(airport_code_str, item->airport); query_writer_write_new_object(output); query_writer_write_new_field(output, "name", "%s", airport_code_str); - query_writer_write_new_field(output, "passengers", "%" PRIu64, item->count); + query_writer_write_new_field(output, "passengers", "%" PRIu32, item->count); } return 0; From 3b2c56d29bc7cbf34db6763b0ebe5111d8d27832 Mon Sep 17 00:00:00 2001 From: voidbert <50591320+voidbert@users.noreply.github.com> Date: Mon, 22 Jan 2024 15:22:25 +0000 Subject: [PATCH 13/19] Cleanup query 7 --- trabalho-pratico/include/queries/q07.h | 62 +++++------ trabalho-pratico/src/queries/q07.c | 136 +++++++++++++------------ 2 files changed, 100 insertions(+), 98 deletions(-) diff --git a/trabalho-pratico/include/queries/q07.h b/trabalho-pratico/include/queries/q07.h index d48fb05..c330564 100644 --- a/trabalho-pratico/include/queries/q07.h +++ b/trabalho-pratico/include/queries/q07.h @@ -1,33 +1,32 @@ /* - * Copyright 2023 Humberto Gomes, José Lopes, José Matos - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ + * Copyright 2023 Humberto Gomes, José Lopes, José Matos + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ /** - * @file q07.h - * @brief A query to that lists the top N airports by median of departure delays. - * @details - * - * ### Examples - * - * ```text - * 7 10 - * 7 20 - * 7F 10 - * 7F 20 - * ``` - */ + * @file q07.h + * @brief A query to that lists the top N airports by median of departure delays. + * + * ### Examples + * + * ```text + * 7 10 + * 7 20 + * 7F 10 + * 7F 20 + * ``` + */ #ifndef Q07_H #define Q07_H @@ -35,10 +34,11 @@ #include "queries/query_type.h" /** - * @brief Initializes the definition of the seventh query. - * @details This is done automatically in ::query_type_list_create. - * @return A pointer to a `malloc`-allocated ::query_type_t on success, or `NULL` on failure. - */ + * @brief Initializes the definition of queries of type 7. + * @details This is done automatically in [query_type_list](@ref query_type_list.c). + * @return On success, a pointer to a ::query_type_t that must be deleted with ::query_type_free, + * or `NULL` allocation on failure. + */ query_type_t *q07_create(void); #endif diff --git a/trabalho-pratico/src/queries/q07.c b/trabalho-pratico/src/queries/q07.c index 3820229..ece9c0c 100644 --- a/trabalho-pratico/src/queries/q07.c +++ b/trabalho-pratico/src/queries/q07.c @@ -21,8 +21,6 @@ #include #include -#include -#include #include "queries/q07.h" #include "queries/query_instance.h" @@ -30,39 +28,43 @@ /** * @brief Parses the arguments of a query of type 7. - * @details Asserts there's only one integer argument, that is stored. + * @details Asserts there's only one integer argument.. * * @param argc Number of arguments. * @param argv Values of the arguments. * - * @return `NULL` for invalid arguments, a `malloc`-allocated `uint64_t` otherwise. + * @return `NULL` for invalid arguments, an integer encoded as a pointer otherwise. */ void *__q07_parse_arguments(size_t argc, char *const argv[argc]) { if (argc != 1) return NULL; /* Parse number of flights */ - uint64_t n; - int retval = int_utils_parse_positive(&n, argv[0]); - if (retval) { + uint64_t n; + const int retval = int_utils_parse_positive(&n, argv[0]); + if (retval) return NULL; /* Invalid N format */ - } - uint64_t *n_ptr = malloc(sizeof(uint64_t)); - if (n_ptr) { - *n_ptr = n; - return n_ptr; - } else { - return NULL; - } + return GUINT_TO_POINTER(n); } +/** + * @brief Creates a deep clone of the value returned by ::__q07_parse_arguments. + * @param args_data Non-`NULL` value returned by ::__q07_parse_arguments (an integer encoded as a + * pointer). + * @return A deep copy of @p args_data. + */ void *__q07_clone_arguments(const void *args_data) { - uint64_t *clone = malloc(sizeof(uint64_t)); - if (!clone) - return NULL; - *clone = *(const uint64_t *) args_data; - return clone; + return (void *) args_data; /* Const cast acceptable - it's an just an integer */ +} + +/** + * @brief Frees data generated by ::__q07_parse_arguments. + * @details Does nothing, as @p args_data is just an integer encoded as a pointer. + * @param args_data Data generated by ::__q07_parse_arguments. + */ +void __q07_free_arguments(void *args_data) { + (void) args_data; } /** @@ -77,23 +79,21 @@ gint __q07_generate_statistics_int64_compare_func(gconstpointer a, gconstpointer * @brief Function called for every flight, that adds it to an array of flights associated with an * airport. * - * @param user_data A `GHashTable` that associates ::airport_code_t's to `GArray`s of `int64_t`s, - * this is, delays in seconds. + * @param user_data A `GHashTable` that associates ::airport_code_t's to `GArray`s of delays + * (`int64_t`), * @param flight Flight to be processed. * - * @retval 0 Always `0`. Don't stop iteration. + * @retval 0 Always. Don't stop iteration. */ int __q07_generate_statistics_foreach_flight(void *user_data, const flight_t *flight) { - GHashTable *airport_delays = (GHashTable *) user_data; + GHashTable *const airport_delays = user_data; - airport_code_t airport_code = flight_get_origin(flight); - int64_t delay = date_and_time_diff(flight_get_real_departure_date(flight), - flight_get_schedule_departure_date(flight)); + const airport_code_t airport_code = flight_get_origin(flight); + const int64_t delay = date_and_time_diff(flight_get_real_departure_date(flight), + flight_get_schedule_departure_date(flight)); - GArray *delays; - if (g_hash_table_contains(airport_delays, GUINT_TO_POINTER(airport_code))) { - delays = g_hash_table_lookup(airport_delays, GUINT_TO_POINTER(airport_code)); - } else { + GArray *delays = g_hash_table_lookup(airport_delays, GUINT_TO_POINTER(airport_code)); + if (!delays) { delays = g_array_new(FALSE, FALSE, sizeof(int64_t)); g_hash_table_insert(airport_delays, GUINT_TO_POINTER(airport_code), delays); } @@ -103,60 +103,61 @@ int __q07_generate_statistics_foreach_flight(void *user_data, const flight_t *fl } /** - * @struct __q07_airport_median - * @brief Structure composed of an airport and its departure delay median. + * @struct q07_airport_median + * @brief Pair composed of an airport and its departure delay median. * - * @var __q07_airport_median::airport_code - * @brief Airport to whom @var __q07_airport_median::median applies. - * @var __q07_airport_median::median - * @brief Departure delay median of __q07_airport_median::airport_code. + * @var q07_airport_median::airport_code + * @brief Airport to whom ::q07_airport_median::median applies. + * @var q07_airport_median::median + * @brief Departure delay median of q07_airport_median::airport_code. */ typedef struct { airport_code_t airport_code; int64_t median; -} __q07_airport_median; +} q07_airport_median; /** - * @brief Function called for every airport, to generate and array of ::__q07_airport_median. + * @brief Function called for every airport, to generate an array of ::q07_airport_median. * * @param key An `airport_code_t` as a pointer. * @param value A pointer to a `GArray` of `int64_t`, the delays of all flights in seconds. - * @param user_data A `GArray` of ::__q07_airport_median to which a new value will be added. + * @param user_data A `GArray` of ::q07_airport_median to which a new value will be added. */ void __q07_generate_statistics_foreach_airport(gpointer key, gpointer value, gpointer user_data) { - airport_code_t airport = GPOINTER_TO_UINT(key); - GArray *delays = (GArray *) value; - GArray *to_add = (GArray *) user_data; + const airport_code_t airport = GPOINTER_TO_UINT(key); + GArray *const delays = value; + GArray *const to_add = user_data; g_array_sort(delays, __q07_generate_statistics_int64_compare_func); double median; if (delays->len % 2 == 0) { - size_t middle = delays->len / 2; - median = (g_array_index(delays, uint64_t, middle) + + const size_t middle = delays->len / 2; + median = (g_array_index(delays, uint64_t, middle) + g_array_index(delays, uint64_t, middle - 1)) * 0.5; } else { median = g_array_index(delays, uint64_t, delays->len / 2); } - __q07_airport_median airport_median = {.airport_code = airport, .median = round(median)}; + const q07_airport_median airport_median = {.airport_code = airport, .median = round(median)}; g_array_append_val(to_add, airport_median); } /** - * @brief Comparsion criteria for sorting arrays of ::__q07_airport_median. + * @brief Comparsion criteria for sorting arrays of ::q07_airport_median. + * @details Auxiliary method for ::__q07_generate_statistics. * - * @param a Pointer to a `const` ::__q07_airport_median. - * @param b Pointer to a `const` ::__q07_airport_median. + * @param a Pointer to a `const` ::q07_airport_median. + * @param b Pointer to a `const` ::q07_airport_median. * * @return Comparison value between @p a and @p b. */ gint __q07_generate_statistics_airport_median_compare_func(gconstpointer a, gconstpointer b) { - const __q07_airport_median *airport_median_a = a; - const __q07_airport_median *airport_median_b = b; + const q07_airport_median *const airport_median_a = a; + const q07_airport_median *const airport_median_b = b; - uint64_t crit1 = airport_median_b->median - airport_median_a->median; + const uint64_t crit1 = airport_median_b->median - airport_median_a->median; if (crit1) return crit1; @@ -171,11 +172,11 @@ gint __q07_generate_statistics_airport_median_compare_func(gconstpointer a, gcon /** * @brief Generates statistical data for queries of type 7. * - * @param database Database, to iterate through flight. + * @param database Database, to iterate through flights. * @param n Number of query instances that will need to be executed. * @param instances Query instances that will need to be executed. * - * @return A sorted `GArray` of ::__q07_airport_median. + * @return A sorted `GArray` of ::q07_airport_median. */ void *__q07_generate_statistics(const database_t *database, size_t n, @@ -184,14 +185,14 @@ void *__q07_generate_statistics(const database_t *database, (void) n; /* Associate each airport with list of delays */ - GHashTable *airport_delays = + GHashTable *const airport_delays = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) g_array_unref); flight_manager_iter(database_get_flights(database), __q07_generate_statistics_foreach_flight, airport_delays); - /* Calulate sorted array of airports with delays. */ - GArray *airport_medians = g_array_new(FALSE, FALSE, sizeof(__q07_airport_median)); + /* Calulate sorted array of airports with delays */ + GArray *const airport_medians = g_array_new(FALSE, FALSE, sizeof(q07_airport_median)); g_hash_table_foreach(airport_delays, __q07_generate_statistics_foreach_airport, airport_medians); @@ -204,13 +205,14 @@ void *__q07_generate_statistics(const database_t *database, /** * @brief Executes a query of type 7. * - * @param database Database (not used, as all used data comes from @p statistics). - * @param statistics Value returned by ::__q07_generate_statistics. + * @param database Database to get data from (not used, as all data is collected in + * ::__q07_generate_statistics). + * @param statistics Value returned by ::__q07_generate_statistics (a sorted `GArray` of + * ::q07_airport_median.) * @param instance Query instance to be executed. * @param output Where to write the query's output to. * - * @retval 0 On success. - * @retval 1 On failure. + * @retval 0 Always successful. */ int __q07_execute(const database_t *database, const void *statistics, @@ -218,13 +220,13 @@ int __q07_execute(const database_t *database, query_writer_t *output) { (void) database; - uint64_t n = *(const uint64_t *) query_instance_get_argument_data(instance); - GArray *airport_medians = (GArray *) statistics; + const uint64_t n = GPOINTER_TO_UINT(query_instance_get_argument_data(instance)); + const GArray *const airport_medians = statistics; - size_t i_max = min(n, airport_medians->len); + const size_t i_max = min(n, airport_medians->len); for (size_t i = 0; i < i_max; i++) { - __q07_airport_median *airport_median = - &g_array_index(airport_medians, __q07_airport_median, i); + const q07_airport_median *const airport_median = + &g_array_index(airport_medians, q07_airport_median, i); char airport_code_str[AIRPORT_CODE_SPRINTF_MIN_BUFFER_SIZE]; airport_code_sprintf(airport_code_str, airport_median->airport_code); @@ -240,7 +242,7 @@ query_type_t *q07_create(void) { return query_type_create(7, __q07_parse_arguments, __q07_clone_arguments, - free, + __q07_free_arguments, __q07_generate_statistics, (query_type_free_statistics_callback_t) g_array_unref, __q07_execute); From 4be5a0be897475e8d593748898233afbc780d919 Mon Sep 17 00:00:00 2001 From: voidbert <50591320+voidbert@users.noreply.github.com> Date: Mon, 22 Jan 2024 16:11:11 +0000 Subject: [PATCH 14/19] glib wrappers encapsulation fixes --- .../include/utils/glib/GConstPtrArray.h | 11 ++++++++++- trabalho-pratico/src/queries/q04.c | 2 +- trabalho-pratico/src/queries/q05.c | 2 +- trabalho-pratico/src/queries/q06.c | 14 ++++++++------ trabalho-pratico/src/utils/glib/GConstPtrArray.c | 4 ++-- 5 files changed, 22 insertions(+), 11 deletions(-) diff --git a/trabalho-pratico/include/utils/glib/GConstPtrArray.h b/trabalho-pratico/include/utils/glib/GConstPtrArray.h index dd1b418..0421b5f 100644 --- a/trabalho-pratico/include/utils/glib/GConstPtrArray.h +++ b/trabalho-pratico/include/utils/glib/GConstPtrArray.h @@ -36,6 +36,15 @@ */ typedef struct GConstPtrArray GConstPtrArray; +/** + * @brief A comparsion function for ::g_const_ptr_array_sort. + * @details See https://libsoup.org/glib/glib-Doubly-Linked-Lists.html#GCompareFunc. This method + * makes it clear that the arguments are pointers to pointers (unlike `GCompareFunc`) and + * informs the compiler that the values in the array are constant (`GCompareFunc` does not, + * only making the topmost pointer constant: `void *const *`). + */ +typedef gint (*GConstCompareFunc)(const void *const *, const void *const *); + /** * @brief Creates a new dynamic array of constant pointers. * @details See https://libsoup.org/glib/glib-Pointer-Arrays.html#g-ptr-array-new @@ -78,7 +87,7 @@ guint g_const_ptr_array_get_length(const GConstPtrArray *array); * @param array Array to be sorted. * @param compare_func Method used to compare between two given pointers. */ -void g_const_ptr_array_sort(GConstPtrArray *array, GCompareFunc compare_func); +void g_const_ptr_array_sort(GConstPtrArray *array, GConstCompareFunc compare_func); /** * @brief Decrements the reference count of a dynamic array of constant pointers. diff --git a/trabalho-pratico/src/queries/q04.c b/trabalho-pratico/src/queries/q04.c index 37c4e67..f1e26a9 100644 --- a/trabalho-pratico/src/queries/q04.c +++ b/trabalho-pratico/src/queries/q04.c @@ -90,7 +90,7 @@ int __q04_generate_statistics_foreach_reservation(void *user_data * @details Auxiliary method for ::__q04_generate_statistics_sort_each_array, itself an * auxiliary method for ::__q04_generate_statistics. */ -gint __q04_sort_reservations_by_date(gconstpointer a, gconstpointer b) { +gint __q04_sort_reservations_by_date(const void *const *a, const void *const *b) { const reservation_t *const reservation_a = *((const reservation_t *const *) a); const reservation_t *const reservation_b = *((const reservation_t *const *) b); diff --git a/trabalho-pratico/src/queries/q05.c b/trabalho-pratico/src/queries/q05.c index 10134cb..64c95b8 100644 --- a/trabalho-pratico/src/queries/q05.c +++ b/trabalho-pratico/src/queries/q05.c @@ -148,7 +148,7 @@ int __q05_generate_statistics_foreach_flight(void *user_data, const flight_t *fl * @details Auxiliary method for ::__q05_generate_statistics_sort_each_array, itself an * auxiliary method for ::__q05_generate_statistics. */ -gint __q05_flights_date_compare_func(gconstpointer a, gconstpointer b) { +gint __q05_flights_date_compare_func(const void *const *a, const void *const *b) { const flight_t *const flight_a = *((const flight_t *const *) a); const flight_t *const flight_b = *((const flight_t *const *) b); diff --git a/trabalho-pratico/src/queries/q06.c b/trabalho-pratico/src/queries/q06.c index e834d2f..230c079 100644 --- a/trabalho-pratico/src/queries/q06.c +++ b/trabalho-pratico/src/queries/q06.c @@ -217,7 +217,7 @@ void __q06_generate_statistics_foreach_year(gpointer key_year, gpointer user_data) { GHashTable *const airport_count = value_airport_count; - GArray *const airport_count_array = + GArray *const airport_count_array = g_array_sized_new(FALSE, FALSE, sizeof(q06_array_item_t), g_hash_table_size(airport_count)); g_hash_table_foreach(airport_count, __q06_foreach_airport_count, airport_count_array); @@ -240,10 +240,11 @@ void *__q06_generate_statistics(const database_t *database, size_t n, const query_instance_t *const instances[n]) { - GHashTable *const years_airport_count = g_hash_table_new_full(g_direct_hash, - g_direct_equal, - NULL, - (GDestroyNotify) g_hash_table_unref); + GHashTable *const years_airport_count = + g_hash_table_new_full(g_direct_hash, + g_direct_equal, + NULL, + (GDestroyNotify) g_hash_table_unref); for (size_t i = 0; i < n; ++i) { const int16_t year = ((const q06_parsed_arguments_t *) query_instance_get_argument_data(instances[i]))->year; @@ -279,7 +280,8 @@ void *__q06_generate_statistics(const database_t *database, * @param instance Query instance to be executed. * @param output Where to write the query's result to. * - * @retval 0 Always successful. + * @retval 0 Success. + * @retval 1 Fatal failure (should, in principle, be unreachable). */ int __q06_execute(const database_t *database, const void *statistics, diff --git a/trabalho-pratico/src/utils/glib/GConstPtrArray.c b/trabalho-pratico/src/utils/glib/GConstPtrArray.c index 809d6da..0b4a505 100644 --- a/trabalho-pratico/src/utils/glib/GConstPtrArray.c +++ b/trabalho-pratico/src/utils/glib/GConstPtrArray.c @@ -37,8 +37,8 @@ guint g_const_ptr_array_get_length(const GConstPtrArray *array) { return ((const GPtrArray *) array)->len; } -void g_const_ptr_array_sort(GConstPtrArray *array, GCompareFunc compare_func) { - g_ptr_array_sort((GPtrArray *) array, compare_func); +void g_const_ptr_array_sort(GConstPtrArray *array, GConstCompareFunc compare_func) { + g_ptr_array_sort((GPtrArray *) array, (GCompareFunc) compare_func); } void g_const_ptr_array_unref(GConstPtrArray *array) { From ca47a1dbda128496b718b7b23c4364c95fbe473b Mon Sep 17 00:00:00 2001 From: voidbert <50591320+voidbert@users.noreply.github.com> Date: Mon, 22 Jan 2024 17:18:25 +0000 Subject: [PATCH 15/19] Query 8 cleanup and slight optimization --- trabalho-pratico/include/queries/q08.h | 54 +++++--- trabalho-pratico/src/queries/q08.c | 167 ++++++++++++++----------- 2 files changed, 136 insertions(+), 85 deletions(-) diff --git a/trabalho-pratico/include/queries/q08.h b/trabalho-pratico/include/queries/q08.h index e290d97..71e005c 100644 --- a/trabalho-pratico/include/queries/q08.h +++ b/trabalho-pratico/include/queries/q08.h @@ -1,24 +1,50 @@ /* - * Copyright 2023 Humberto Gomes, José Lopes, José Matos - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ + * Copyright 2023 Humberto Gomes, José Lopes, José Matos + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file q08.h + * @brief A query to calculate the revenue of a hotel between two dates. + * + * ### Examples + * + * ```text + * 8 HTL1001 2023/05/02 2023/05/02 + * 8 HTL1001 2023/05/02 2023/05/03 + * 8 HTL1001 2023/05/01 2023/06/01 + * 8 HTL1001 2021/01/01 2022/01/01 + * 8 HTL1002 2021/01/01 2022/01/01 + * 8F HTL1001 2023/05/02 2023/05/02 + * 8F HTL1001 2023/05/02 2023/05/03 + * 8F HTL1001 2023/05/01 2023/06/01 + * 8F HTL1001 2021/01/01 2022/01/01 + * 8F HTL1002 2021/01/01 2022/01/01 + * ``` + */ #ifndef Q08_H #define Q08_H #include "queries/query_type.h" +/** + * @brief Initializes the definition of queries of type 8. + * @details This is done automatically in [query_type_list](@ref query_type_list.c). + * @return On success, a pointer to a ::query_type_t that must be deleted with ::query_type_free, + * or `NULL` allocation on failure. + */ query_type_t *q08_create(void); #endif diff --git a/trabalho-pratico/src/queries/q08.c b/trabalho-pratico/src/queries/q08.c index 19a6e3a..42ef7cd 100644 --- a/trabalho-pratico/src/queries/q08.c +++ b/trabalho-pratico/src/queries/q08.c @@ -19,7 +19,6 @@ * @brief Implementation of methods in include/queries/q08.h */ -#include #include #include @@ -28,7 +27,7 @@ /** * @struct q08_parsed_arguments_t - * @brief Data needed for the execution of a query of type 8. + * @brief Parsed arguments of a query of type 8. * * @var q08_parsed_arguments_t::hotel_id * @brief Hotel identifier to filter by. @@ -45,36 +44,42 @@ typedef struct { /** * @brief Parses arguments for query 8. - * @details Asserts that there's three arguments, an hotel identifier, and two dates. + * @details Asserts that there's three arguments, an hotel identifier and two dates. * * @param argc Number of arguments. * @param argv Values of the arguments. * - * @return `NULL` on failure, a pointer to a `q08_airport_data_t` otherwise. + * @return `NULL` on parsing or allocation failure, a pointer to a ::q08_parsed_arguments_t + * otherwise. */ void *__q08_parse_arguments(size_t argc, char *const argv[argc]) { if (argc != 3) return NULL; - q08_parsed_arguments_t *parsed_arguments = malloc(sizeof(q08_parsed_arguments_t)); + q08_parsed_arguments_t *const parsed_arguments = malloc(sizeof(q08_parsed_arguments_t)); if (!parsed_arguments) return NULL; - int hotel_retval = hotel_id_from_string(&parsed_arguments->hotel_id, argv[0]); - int begin_date_retval = date_from_string(&parsed_arguments->begin_date, argv[1]); - int end_date_retval = date_from_string(&parsed_arguments->end_date, argv[2]); + const int hotel_retval = hotel_id_from_string(&parsed_arguments->hotel_id, argv[0]); + const int begin_date_retval = date_from_string(&parsed_arguments->begin_date, argv[1]); + const int end_date_retval = date_from_string(&parsed_arguments->end_date, argv[2]); if (hotel_retval || begin_date_retval || end_date_retval) { free(parsed_arguments); return NULL; } - return parsed_arguments; } +/** + * @brief Creates a deep clone of the value returned by ::__q08_parse_arguments. + * @param args_data Non-`NULL` value returned by ::__q08_parse_arguments (a pointer to a + * ::q08_parsed_arguments_t). + * @return A deep copy of @p args_data. + */ void *__q08_clone_arguments(const void *args_data) { - const q08_parsed_arguments_t *args = args_data; - q08_parsed_arguments_t *clone = malloc(sizeof(q08_parsed_arguments_t)); + const q08_parsed_arguments_t *const args = args_data; + q08_parsed_arguments_t *const clone = malloc(sizeof(q08_parsed_arguments_t)); if (!clone) return NULL; @@ -83,63 +88,58 @@ void *__q08_clone_arguments(const void *args_data) { } /** - * @struct q08_foreach_reservation_data_t - * @brief Data needed while iterating over reservations. + * @struct q08_statistical_data_t + * @brief Association between query instances and hotel revenue prices. * - * @var q08_foreach_reservation_data_t::filter_data - * @brief Array of pointers to ::q08_parsed_arguments_t. - * @var q08_foreach_reservation_data_t::hotel_revenue - * @brief Associations between ::q08_parsed_arguments_t and it's hotel revenue. + * @var q08_statistical_data_t::n + * @brief Number of query instances (same as ::q08_statistical_data_t::filters and + * ::q08_statistical_data_t::revenues). + * @var q08_statistical_data_t::filters + * @brief Query instances that need to be answered. + * @var q08_statistical_data_t::revenues + * @brief Revenues of hotels (associated to ::q08_statistical_data_t::filters). */ typedef struct { - GPtrArray *filter_data; - GHashTable *hotel_revenue; -} q08_foreach_reservation_data_t; + size_t n; + q08_parsed_arguments_t *filters; + uint64_t *revenues; +} q08_statistical_data_t; /** * @brief A method called for each reservation, to calculate the revenue of a hotel. * @details An auxiliary method for ::__q08_generate_statistics. * - * @param user_data A pointer to a ::q08_foreach_reservation_data_t. + * @param user_data A pointer to a ::q08_statistical_data_t. * @param reservation Reservation being processed. * * @retval 0 Always successful. */ int __q08_generate_statistics_foreach_reservation(void *user_data, const reservation_t *reservation) { - q08_foreach_reservation_data_t *foreach_data = (q08_foreach_reservation_data_t *) user_data; + q08_statistical_data_t *const stats = user_data; + const hotel_id_t hotel_id = reservation_get_hotel_id(reservation); - hotel_id_t hotel_id = reservation_get_hotel_id(reservation); - - for (size_t i = 0; i < foreach_data->filter_data->len; i++) { - const q08_parsed_arguments_t *args = g_ptr_array_index(foreach_data->filter_data, i); + for (size_t i = 0; i < stats->n; i++) { + const q08_parsed_arguments_t *const args = &stats->filters[i]; if (hotel_id == args->hotel_id) { - uint16_t price_per_night = reservation_get_price_per_night(reservation); - - date_t reservation_begin = reservation_get_begin_date(reservation), - reservation_end = reservation_get_end_date(reservation); + const uint16_t price_per_night = reservation_get_price_per_night(reservation); + const date_t reservation_begin = reservation_get_begin_date(reservation); + date_t reservation_end = reservation_get_end_date(reservation); /* Reservations don't make money on their last day */ date_set_day(&reservation_end, date_get_day(reservation_end) - 1); - if (date_diff(args->begin_date, reservation_end) > 0 || date_diff(reservation_begin, args->end_date) > 0) continue; - date_t range_begin = date_diff(reservation_begin, args->begin_date) < 0 - ? args->begin_date - : reservation_begin; - date_t range_end = + const date_t range_begin = date_diff(reservation_begin, args->begin_date) < 0 + ? args->begin_date + : reservation_begin; + const date_t range_end = date_diff(reservation_end, args->end_date) < 0 ? reservation_end : args->end_date; - uint64_t reservation_revenue = - price_per_night * (date_diff(range_end, range_begin) + 1); - uint64_t revenue = - GPOINTER_TO_UINT(g_hash_table_lookup(foreach_data->hotel_revenue, args)); - g_hash_table_insert(foreach_data->hotel_revenue, - (q08_parsed_arguments_t *) args, - GUINT_TO_POINTER(revenue + reservation_revenue)); + stats->revenues[i] += price_per_night * (date_diff(range_end, range_begin) + 1); } } @@ -153,32 +153,53 @@ int __q08_generate_statistics_foreach_reservation(void *user_data * @param n Number of query instances. * @param instances Instances of the query 8. * - * @return A `GHashTable` associating a ::q08_parsed_arguments_t to an integer revenue as a pointer. + * @return A pointer to a ::q08_statistical_data_t on success, or `NULL` on allocation failure. */ void *__q08_generate_statistics(const database_t *database, size_t n, const query_instance_t *const instances[n]) { - GPtrArray *filter_data = g_ptr_array_new(); - GHashTable *hotel_revenue = g_hash_table_new(g_direct_hash, g_direct_equal); + + q08_statistical_data_t *const stats = malloc(sizeof(q08_statistical_data_t)); + if (!stats) + return NULL; + stats->n = n; + + stats->filters = malloc(n * sizeof(q08_parsed_arguments_t)); + if (!stats->filters) { + free(stats); + return NULL; + } + + stats->revenues = malloc(n * sizeof(uint64_t)); + if (!stats->revenues) { + free(stats->filters); + free(stats); + return NULL; + } for (size_t i = 0; i < n; ++i) { - const q08_parsed_arguments_t *argument_data = + const q08_parsed_arguments_t *const argument_data = query_instance_get_argument_data(instances[i]); - /* TODO - find way to keep const */ - g_hash_table_insert(hotel_revenue, (q08_parsed_arguments_t *) argument_data, 0); - g_ptr_array_add(filter_data, (q08_parsed_arguments_t *) argument_data); + stats->filters[i] = *argument_data; + stats->revenues[i] = 0; } - q08_foreach_reservation_data_t callback_data = {.filter_data = filter_data, - .hotel_revenue = hotel_revenue}; - reservation_manager_iter(database_get_reservations(database), __q08_generate_statistics_foreach_reservation, - &callback_data); + stats); + return stats; +} - g_ptr_array_unref(filter_data); - return hotel_revenue; +/** + * @brief Frees statistical data generated by ::__q08_generate_statistics. + * @param statistical_data Non-`NULL` value returned by ::__q08_generate_statistics. + */ +void __q08_free_statistics(void *statistical_data) { + q08_statistical_data_t *const stats = statistical_data; + free(stats->revenues); + free(stats->filters); + free(stats); } /** @@ -186,12 +207,13 @@ void *__q08_generate_statistics(const database_t *database, * * @param database Database to get data from (not used, as all data is collected in * ::__q08_generate_statistics). - * @param statistics Statistical data generated by ::__q08_generate_statistics. + * @param statistics Statistical data generated by ::__q08_generate_statistics (a pointer to a + * ::q08_statistical_data_t). * @param instance Query instance to be executed. * @param output Where to write the query's result to. * - * @retval 0 Always succesl - * @retval 1 Fatal failure (will only happen if a cosmic ray flips some bit in your memory). + * @retval 0 Success. + * @retval 1 Fatal failure (should, in principle, be unreachable). */ int __q08_execute(const database_t *database, const void *statistics, @@ -199,19 +221,22 @@ int __q08_execute(const database_t *database, query_writer_t *output) { (void) database; - GHashTable *hotel_revenue = (GHashTable *) statistics; - const q08_parsed_arguments_t *arguments = query_instance_get_argument_data(instance); - - gpointer revenue_ptr; - if (g_hash_table_lookup_extended(hotel_revenue, arguments, NULL, &revenue_ptr)) { - uint64_t revenue = GPOINTER_TO_UINT(revenue_ptr); - query_writer_write_new_object(output); - query_writer_write_new_field(output, "revenue", "%" PRIu64, revenue); - } else { - fprintf(stderr, "Bad statistical data in query 3! This should not happen!\n"); - return 1; + const q08_statistical_data_t *const stats = statistics; + const q08_parsed_arguments_t *const arguments = query_instance_get_argument_data(instance); + + for (size_t i = 0; i < stats->n; ++i) { + if (stats->filters[i].hotel_id == arguments->hotel_id && + stats->filters[i].begin_date == arguments->begin_date && + stats->filters[i].end_date == arguments->end_date) { + + query_writer_write_new_object(output); + query_writer_write_new_field(output, "revenue", "%" PRIu64, stats->revenues[i]); + return 0; + } } - return 0; + + fprintf(stderr, "Bad statistical data in query 8! This should not happen!\n"); + return 1; } query_type_t *q08_create(void) { @@ -220,6 +245,6 @@ query_type_t *q08_create(void) { __q08_clone_arguments, free, __q08_generate_statistics, - (query_type_free_statistics_callback_t) g_hash_table_unref, + __q08_free_statistics, __q08_execute); } From 9854b6066101770c39f313ce54c0930e20427a8b Mon Sep 17 00:00:00 2001 From: voidbert <50591320+voidbert@users.noreply.github.com> Date: Mon, 22 Jan 2024 23:41:01 +0000 Subject: [PATCH 16/19] Cleanup query 9 --- trabalho-pratico/include/queries/q09.h | 11 +-- trabalho-pratico/src/queries/q09.c | 105 ++++++++++++------------- 2 files changed, 56 insertions(+), 60 deletions(-) diff --git a/trabalho-pratico/include/queries/q09.h b/trabalho-pratico/include/queries/q09.h index 8004219..667b150 100644 --- a/trabalho-pratico/include/queries/q09.h +++ b/trabalho-pratico/include/queries/q09.h @@ -15,8 +15,8 @@ */ /** - * @file q09.h - * @brief A query to look up a user by a prefix of its identifier. + * @file q09.h + * @brief A query to look up a user by a prefix of its name. * * ### Examples * @@ -38,9 +38,10 @@ #include "queries/query_type.h" /** - * @brief Initializes the definition of query 9. - * @details This is done automatically in ::query_type_list_create. - * @return A pointer to a `malloc`-allocated ::query_type_t on success, or `NULL` on failure. + * @brief Initializes the definition of queries of type 9. + * @details This is done automatically in [query_type_list](@ref query_type_list.c). + * @return On success, a pointer to a ::query_type_t that must be deleted with ::query_type_free, + * or `NULL` allocation on failure. */ query_type_t *q09_create(void); diff --git a/trabalho-pratico/src/queries/q09.c b/trabalho-pratico/src/queries/q09.c index 5b8262e..89cbebb 100644 --- a/trabalho-pratico/src/queries/q09.c +++ b/trabalho-pratico/src/queries/q09.c @@ -19,22 +19,19 @@ * @brief Implementation of methods in include/queries/q09.h */ -#include -#include #include #include -#include -#include #include "queries/q09.h" #include "queries/query_instance.h" +#include "utils/glib/GConstPtrArray.h" /** * @brief Parses arguments of a query of type 9. - * @details Asserts there's only one string argument, that is stored. + * @details Asserts there's only one string argument. * - * @param argv Values of the arguments. * @param argc Number of arguments. + * @param argv Values of the arguments. * * @return `NULL` for invalid arguments, a copy of the only @p argv on success. */ @@ -47,7 +44,7 @@ void *__q09_parse_arguments(size_t argc, char *const argv[argc]) { /** * @struct q09_statistical_data_t - * @brief Query 9's statistical data. + * @brief Query 9's statistical data. * * @var q09_statistical_data_t::prefixes * @brief Sorted array of prefixes to compare user names against. @@ -60,10 +57,10 @@ void *__q09_parse_arguments(size_t argc, char *const argv[argc]) { * @brief Number of elements in every array of this struct. */ typedef struct { - const char **prefixes; - size_t *lengths; - GPtrArray **matches; - size_t n; + const char **prefixes; + size_t *lengths; + GConstPtrArray **matches; + size_t n; } q09_statistical_data_t; /** @@ -76,75 +73,73 @@ typedef struct { * @retval 0 Always successful. */ int __q09_generate_statistics_iter_callback(void *user_data, const user_t *user) { - q09_statistical_data_t *stats = (q09_statistical_data_t *) user_data; + q09_statistical_data_t *const stats = user_data; /* Local copies for slight performance increase */ - size_t n = stats->n; - const char **prefixes = stats->prefixes; - size_t *lengths = stats->lengths; + const size_t n = stats->n; + const char *const *const prefixes = stats->prefixes; + const size_t *const lengths = stats->lengths; for (size_t i = 0; i < n; ++i) { - int cmp = strncmp(prefixes[i], user_get_const_name(user), lengths[i]); + const int cmp = strncmp(prefixes[i], user_get_const_name(user), lengths[i]); - if (cmp == 0 && user_get_account_status(user) == ACCOUNT_STATUS_ACTIVE) { - /* TODO - find a way of not keeping the const */ - g_ptr_array_add(stats->matches[i], (user_t *) user); - } else if (cmp > 0) { + if (cmp == 0 && user_get_account_status(user) == ACCOUNT_STATUS_ACTIVE) + g_const_ptr_array_add(stats->matches[i], user); + else if (cmp > 0) break; - } } - return 0; } -/** @brief User comparison function for user ordering for final output. */ -gint __q09_sort_compare_callback(gconstpointer a, gconstpointer b) { - const user_t *user_a = *(const user_t *const *) a; - const user_t *user_b = *(const user_t *const *) b; +/** + * @brief User comparison function for user ordering for final output. + * @details Auxiliary method for ::__q09_generate_statistics. + */ +gint __q09_sort_compare_callback(const void *const *a, const void *const *b) { + const user_t *const user_a = *(const user_t *const *) a; + const user_t *const user_b = *(const user_t *const *) b; - gint crit1 = strcoll(user_get_const_name(user_a), user_get_const_name(user_b)); + const int crit1 = strcoll(user_get_const_name(user_a), user_get_const_name(user_b)); if (crit1) return crit1; - gint crit2 = strcoll(user_get_const_id(user_a), user_get_const_id(user_b)); + const int crit2 = strcoll(user_get_const_id(user_a), user_get_const_id(user_b)); return crit2; } /** * @brief Comparison function for sorting arrays of strings. - * @details Used in ::__q09_generate_statistics to sort ::q09_statistical_data_t::prefixes. + * @details Auxiliary method for ::__q09_generate_statistics. */ int __q09_sort_prefixes_callback(const void *a, const void *b) { - const char *ap = *(const char *const *) a; - const char *bp = *(const char *const *) b; - return strcmp(ap, bp); + return strcmp(*(const char *const *) a, *(const char *const *) b); } /** * @brief Generates statistical data for queries of type 9. * * @param database Database, to iterate through users. - * @param n Number of query instances that will need to be executed. - * @param instances Query instances that will need to be executed. + * @param n Number of query instances to be executed. + * @param instances Query instances to be executed. * - * @return A pointer to a ::q09_statistical_data_t. + * @return A pointer to a ::q09_statistical_data_t, or `NULL` on allocation failure. */ void *__q09_generate_statistics(const database_t *database, size_t n, const query_instance_t *const instances[n]) { /* Set locale for sorting and restore older locale later */ - char *old_locale = strdup(setlocale(LC_COLLATE, NULL)); + char *const old_locale = strdup(setlocale(LC_COLLATE, NULL)); setlocale(LC_COLLATE, "en_US.UTF-8"); /* Allocate and initialize statistics */ - q09_statistical_data_t *stats = malloc(sizeof(q09_statistical_data_t)); + q09_statistical_data_t *const stats = malloc(sizeof(q09_statistical_data_t)); if (!stats) return NULL; stats->prefixes = malloc(sizeof(const char *) * n); stats->lengths = malloc(sizeof(size_t) * n); - stats->matches = malloc(sizeof(GPtrArray **) * n); + stats->matches = malloc(sizeof(GConstPtrArray **) * n); stats->n = n; if (!stats->prefixes || !stats->lengths || !stats->matches) { @@ -161,7 +156,7 @@ void *__q09_generate_statistics(const database_t *database, size_t count = 0; for (size_t i = 0; i < n; ++i) { - const char *prefix = query_instance_get_argument_data(instances[i]); + const char *const prefix = query_instance_get_argument_data(instances[i]); int found = 0; for (size_t j = 0; j < count; ++j) { @@ -177,20 +172,18 @@ void *__q09_generate_statistics(const database_t *database, } } stats->n = count; - qsort(stats->prefixes, count, sizeof(const char *), __q09_sort_prefixes_callback); for (size_t i = 0; i < stats->n; ++i) { stats->lengths[i] = strlen(stats->prefixes[i]); - stats->matches[i] = g_ptr_array_new(); + stats->matches[i] = g_const_ptr_array_new(); } /* Fill matches in statistical data */ user_manager_iter(database_get_users(database), __q09_generate_statistics_iter_callback, stats); - for (size_t i = 0; i < stats->n; ++i) { - g_ptr_array_sort(stats->matches[i], __q09_sort_compare_callback); - } + for (size_t i = 0; i < stats->n; ++i) + g_const_ptr_array_sort(stats->matches[i], __q09_sort_compare_callback); if (old_locale) { setlocale(LC_COLLATE, old_locale); @@ -201,15 +194,15 @@ void *__q09_generate_statistics(const database_t *database, /** * @brief Frees statistical data generated by ::__q09_generate_statistics. - * @param statistical_data Value returned by ::__q09_generate_statistics. + * @param statistical_data Non-`NULL` value returned by ::__q09_generate_statistics. */ void __q09_free_statistics(void *statistical_data) { - q09_statistical_data_t *stats = (q09_statistical_data_t *) statistical_data; + q09_statistical_data_t *const stats = statistical_data; free(stats->prefixes); free(stats->lengths); for (size_t i = 0; i < stats->n; ++i) - g_ptr_array_unref(stats->matches[i]); + g_const_ptr_array_unref(stats->matches[i]); free(stats->matches); free(stats); @@ -218,13 +211,14 @@ void __q09_free_statistics(void *statistical_data) { /** * @brief Executes a query of type 9. * - * @param database Ignored database (all data is gathered in ::__q09_generate_statistics). + * @param database Database to get data from (not used, as all data is collected in + * ::__q09_generate_statistics). * @param statistics A pointer to a ::q09_statistical_data_t. * @param instance Query instance to be executed. * @param output Where to write the query's output to. * - * @retval 0 Always succcessful. - * @retval 1 Bad statistical data (should not happen, please raise an issue if it does). + * @retval 0 Success. + * @retval 1 Fatal failure (should, in principle, be unreachable). */ int __q09_execute(const database_t *database, const void *statistics, @@ -232,15 +226,16 @@ int __q09_execute(const database_t *database, query_writer_t *output) { (void) database; - const char *prefix = query_instance_get_argument_data(instance); - q09_statistical_data_t *stats = (q09_statistical_data_t *) statistics; + const char *const prefix = query_instance_get_argument_data(instance); + const q09_statistical_data_t *const stats = statistics; for (size_t i = 0; i < stats->n; ++i) { if (strcmp(stats->prefixes[i], prefix) == 0) { - GPtrArray *matches = stats->matches[i]; - for (size_t j = 0; j < matches->len; ++j) { - const user_t *user = g_ptr_array_index(matches, j); + const GConstPtrArray *const matches = stats->matches[i]; + const size_t matches_len = g_const_ptr_array_get_length(matches); + for (size_t j = 0; j < matches_len; ++j) { + const user_t *const user = g_const_ptr_array_index(matches, j); query_writer_write_new_object(output); query_writer_write_new_field(output, "id", "%s", user_get_const_id(user)); query_writer_write_new_field(output, "name", "%s", user_get_const_name(user)); From 670e467bd8798eda812739c878f396ca47e81b19 Mon Sep 17 00:00:00 2001 From: voidbert <50591320+voidbert@users.noreply.github.com> Date: Tue, 23 Jan 2024 01:20:25 +0000 Subject: [PATCH 17/19] Extra warnings and fixes (before merge) --- trabalho-pratico/Makefile | 5 +++-- trabalho-pratico/src/queries/q03.c | 2 +- trabalho-pratico/src/queries/q04.c | 2 +- trabalho-pratico/src/queries/q07.c | 2 +- trabalho-pratico/src/queries/q10.c | 4 ++-- trabalho-pratico/src/types/airport_code.c | 4 ++-- trabalho-pratico/src/types/country_code.c | 2 +- trabalho-pratico/src/types/flight.c | 8 ++++---- trabalho-pratico/src/types/reservation.c | 4 ++-- trabalho-pratico/src/utils/glib/GConstKeyHashTable.c | 4 ++-- trabalho-pratico/src/utils/glib/GConstPtrArray.c | 4 ++-- 11 files changed, 21 insertions(+), 20 deletions(-) diff --git a/trabalho-pratico/Makefile b/trabalho-pratico/Makefile index 2555a42..3274ba7 100644 --- a/trabalho-pratico/Makefile +++ b/trabalho-pratico/Makefile @@ -1,8 +1,9 @@ # CONFIGURATION VARIABLES CC := gcc -CFLAGS := -Wall -Wextra -Werror -pedantic -Iinclude $(shell pkg-config --cflags glib-2.0) \ - $(shell pkg-config --cflags ncursesw) +CFLAGS := -Wall -Wextra -Werror -pedantic -Wshadow -Wcast-qual -Wwrite-strings \ + -Wstrict-prototypes -Winit-self -Wfloat-equal -Iinclude $(shell pkg-config --cflags ncursesw) \ + $(shell pkg-config --cflags glib-2.0) STANDARDS := -std=c99 -D_POSIX_C_SOURCE=200809L LIBS := -lm $(shell pkg-config --libs glib-2.0) $(shell pkg-config --libs ncursesw) diff --git a/trabalho-pratico/src/queries/q03.c b/trabalho-pratico/src/queries/q03.c index f3eca9b..73398b8 100644 --- a/trabalho-pratico/src/queries/q03.c +++ b/trabalho-pratico/src/queries/q03.c @@ -49,7 +49,7 @@ void *__q03_parse_arguments(size_t argc, char *const argv[argc]) { * @return A deep copy of @p args_data. */ void *__q03_clone_arguments(const void *args_data) { - return (void *) args_data; /* Const cast acceptable - it's an just an integer */ + return (void *) (size_t) args_data; /* Const cast acceptable - it's an just an integer */ } /** diff --git a/trabalho-pratico/src/queries/q04.c b/trabalho-pratico/src/queries/q04.c index f1e26a9..ae96a89 100644 --- a/trabalho-pratico/src/queries/q04.c +++ b/trabalho-pratico/src/queries/q04.c @@ -50,7 +50,7 @@ void *__q04_parse_arguments(size_t argc, char *const argv[argc]) { * @return A deep copy of @p args_data. */ void *__q04_clone_arguments(const void *args_data) { - return (void *) args_data; /* Const cast acceptable - it's an just an integer */ + return (void *) (size_t) args_data; /* Const cast acceptable - it's an just an integer */ } /** diff --git a/trabalho-pratico/src/queries/q07.c b/trabalho-pratico/src/queries/q07.c index ece9c0c..5be6ce5 100644 --- a/trabalho-pratico/src/queries/q07.c +++ b/trabalho-pratico/src/queries/q07.c @@ -55,7 +55,7 @@ void *__q07_parse_arguments(size_t argc, char *const argv[argc]) { * @return A deep copy of @p args_data. */ void *__q07_clone_arguments(const void *args_data) { - return (void *) args_data; /* Const cast acceptable - it's an just an integer */ + return (void *) (size_t) args_data; /* Const cast acceptable - it's an just an integer */ } /** diff --git a/trabalho-pratico/src/queries/q10.c b/trabalho-pratico/src/queries/q10.c index bff7cd4..90f277e 100644 --- a/trabalho-pratico/src/queries/q10.c +++ b/trabalho-pratico/src/queries/q10.c @@ -192,7 +192,7 @@ int __q10_generate_statistics_foreach_user(void *u q10_foreach_user_data_t *iter_data = user_data; q10_instant_statistics_t *instants[3]; - date_t date = user_get_account_creation_date(user); + date_t date = date_and_time_get_date(user_get_account_creation_date(user)); if (__q10_fill_instants(iter_data->stats, instants, date)) return 1; @@ -427,7 +427,7 @@ int __q10_execute(const database_t *database, /* TODO - fix const */ const q10_parsed_arguments_t *args = query_instance_get_argument_data(instance); - GHashTable *stats = (GHashTable *) statistics; + GHashTable *stats = (GHashTable *) (size_t) statistics; if (args->year == -1) { date_t date; diff --git a/trabalho-pratico/src/types/airport_code.c b/trabalho-pratico/src/types/airport_code.c index 4693e62..bab379e 100644 --- a/trabalho-pratico/src/types/airport_code.c +++ b/trabalho-pratico/src/types/airport_code.c @@ -41,9 +41,9 @@ int airport_code_from_string(airport_code_t *output, const char *input) { * Why do this? Because I have a test about vectorization tomorrow and want to study. */ #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ - *output = (*(int32_t *) input) & 0x5f5f5f00; + *output = (*(const int32_t *) input) & 0x5f5f5f00; #else - *output = (*(int32_t *) input) & 0x005f5f5f; + *output = (*(const int32_t *) input) & 0x005f5f5f; #endif return 0; } diff --git a/trabalho-pratico/src/types/country_code.c b/trabalho-pratico/src/types/country_code.c index f9328e2..155f319 100644 --- a/trabalho-pratico/src/types/country_code.c +++ b/trabalho-pratico/src/types/country_code.c @@ -40,7 +40,7 @@ int country_code_from_string(country_code_t *output, const char *input) { * * Why do this? Because I have a test about vectorization tomorrow and want to study. */ - *output = *(country_code_t *) input & 0x5f5f; + *output = *(const country_code_t *) input & 0x5f5f; return 0; } } diff --git a/trabalho-pratico/src/types/flight.c b/trabalho-pratico/src/types/flight.c index 190c77b..9b1106f 100644 --- a/trabalho-pratico/src/types/flight.c +++ b/trabalho-pratico/src/types/flight.c @@ -128,7 +128,7 @@ int flight_set_airline(string_pool_no_duplicates_t *allocator, if (flight->owns_airline) /* Purposely remove const. We know it was allocated by this module */ - free((char *) flight->airline); + free((char *) (size_t) flight->airline); flight->owns_airline = allocator == NULL; flight->airline = new_airline; @@ -148,7 +148,7 @@ int flight_set_plane_model(string_pool_no_duplicates_t *allocator, if (flight->owns_plane_model) /* Purposely remove const. We know it was allocated by this module */ - free((char *) flight->plane_model); + free((char *) (size_t) flight->plane_model); flight->owns_plane_model = allocator == NULL; flight->plane_model = new_plane_model; @@ -268,11 +268,11 @@ void flight_invalidate(flight_t *flight) { void flight_free(flight_t *flight) { if (flight->owns_airline) /* Purposely remove const. We know it was allocated by this module */ - free((char *) flight->airline); + free((char *) (size_t) flight->airline); if (flight->owns_plane_model) /* Purposely remove const. We know it was allocated by this module */ - free((char *) flight->plane_model); + free((char *) (size_t) flight->plane_model); if (flight->owns_itself) free(flight); diff --git a/trabalho-pratico/src/types/reservation.c b/trabalho-pratico/src/types/reservation.c index 370361c..ad7caf7 100644 --- a/trabalho-pratico/src/types/reservation.c +++ b/trabalho-pratico/src/types/reservation.c @@ -150,7 +150,7 @@ int reservation_set_hotel_name(string_pool_no_duplicates_t *allocator, if (reservation->owns_hotel_name) /* Purposely remove const. We know it was allocated by this module */ - free((char *) reservation->hotel_name); + free((char *) (size_t) reservation->hotel_name); reservation->owns_hotel_name = allocator == NULL; reservation->hotel_name = new_hotel_name; @@ -289,7 +289,7 @@ void reservation_free(reservation_t *reservation) { if (reservation->owns_hotel_name) /* Purposely remove const. We know it was allocated by this module */ - free((char *) reservation->hotel_name); + free((char *) (size_t) reservation->hotel_name); if (reservation->owns_itself) free(reservation); diff --git a/trabalho-pratico/src/utils/glib/GConstKeyHashTable.c b/trabalho-pratico/src/utils/glib/GConstKeyHashTable.c index 17a2910..31b4dd2 100644 --- a/trabalho-pratico/src/utils/glib/GConstKeyHashTable.c +++ b/trabalho-pratico/src/utils/glib/GConstKeyHashTable.c @@ -36,7 +36,7 @@ GConstKeyHashTable *g_const_key_hash_table_new_full(GHashFunc hash_func, gboolean g_const_key_hash_table_insert(GConstKeyHashTable *hash_table, gconstpointer key, gpointer value) { - return g_hash_table_insert((GHashTable *) hash_table, (gpointer) key, value); + return g_hash_table_insert((GHashTable *) hash_table, (gpointer) (size_t) key, value); } gpointer g_const_key_hash_table_lookup(GConstKeyHashTable *hash_table, gconstpointer key) { @@ -45,7 +45,7 @@ gpointer g_const_key_hash_table_lookup(GConstKeyHashTable *hash_table, gconstpoi gconstpointer g_const_key_hash_table_const_lookup(const GConstKeyHashTable *hash_table, gconstpointer key) { - return g_hash_table_lookup((GHashTable *) hash_table, key); + return g_hash_table_lookup((GHashTable *) (size_t) hash_table, key); } void g_const_key_hash_table_foreach(GConstKeyHashTable *hash_table, diff --git a/trabalho-pratico/src/utils/glib/GConstPtrArray.c b/trabalho-pratico/src/utils/glib/GConstPtrArray.c index 0b4a505..dbde882 100644 --- a/trabalho-pratico/src/utils/glib/GConstPtrArray.c +++ b/trabalho-pratico/src/utils/glib/GConstPtrArray.c @@ -26,11 +26,11 @@ GConstPtrArray *g_const_ptr_array_new(void) { } void g_const_ptr_array_add(GConstPtrArray *array, gconstpointer data) { - g_ptr_array_add((GPtrArray *) array, (gpointer) data); + g_ptr_array_add((GPtrArray *) array, (gpointer) (size_t) data); } gconstpointer g_const_ptr_array_index(const GConstPtrArray *array, guint index) { - return g_ptr_array_index((GPtrArray *) array, index); + return g_ptr_array_index((GPtrArray *) (size_t) array, index); } guint g_const_ptr_array_get_length(const GConstPtrArray *array) { From 5aa0e0cebcf2c32ecf16e6bf7a74eb1827352df8 Mon Sep 17 00:00:00 2001 From: voidbert <50591320+voidbert@users.noreply.github.com> Date: Tue, 23 Jan 2024 01:56:11 +0000 Subject: [PATCH 18/19] Extra warnings and fixes (after merge) --- trabalho-pratico/src/interactive_mode/ncurses_utils.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/trabalho-pratico/src/interactive_mode/ncurses_utils.c b/trabalho-pratico/src/interactive_mode/ncurses_utils.c index eb54c30..1893507 100644 --- a/trabalho-pratico/src/interactive_mode/ncurses_utils.c +++ b/trabalho-pratico/src/interactive_mode/ncurses_utils.c @@ -88,7 +88,12 @@ size_t ncurses_measure_string(const char *str) { size_t width = 0; while (*str) { width += ncurses_measure_character(g_utf8_get_char(str)); + + /* Pragmas to fix bad const in glib's macro */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-qual" str = g_utf8_next_char(str); +#pragma GCC diagnostic pop } return width; } From 441ec28ccd8001a5d4d160796d1d1da9c3a3c1e3 Mon Sep 17 00:00:00 2001 From: voidbert <50591320+voidbert@users.noreply.github.com> Date: Tue, 23 Jan 2024 02:14:29 +0000 Subject: [PATCH 19/19] Fix remaining TODOs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Except for query 10. That's being handled by Zé --- .../src/interactive_mode/interactive_mode.c | 19 ++++++++++--------- .../interactive_mode/screen_loading_dataset.c | 1 - trabalho-pratico/src/main.c | 4 ---- .../src/queries/query_instance_list.c | 1 - 4 files changed, 10 insertions(+), 15 deletions(-) diff --git a/trabalho-pratico/src/interactive_mode/interactive_mode.c b/trabalho-pratico/src/interactive_mode/interactive_mode.c index 22fbf04..2334f36 100644 --- a/trabalho-pratico/src/interactive_mode/interactive_mode.c +++ b/trabalho-pratico/src/interactive_mode/interactive_mode.c @@ -142,15 +142,16 @@ void __interactive_mode_run_query(const database_t *database) { if (!writer) { activity_messagebox_run("Failed to create writer for query output."); } else { - /* TODO - handle allocation errors */ - query_dispatcher_dispatch_single(database, query_parsed, writer); - - size_t nlines; - const char *const *const lines = query_writer_get_lines(writer, &nlines); - activity_paging_run(nlines, - lines, - query_instance_get_formatted(query_parsed), - "QUERY OUTPUT"); + if (query_dispatcher_dispatch_single(database, query_parsed, writer)) { + activity_messagebox_run("Failed to run query: out of memory!"); + } else { + size_t nlines; + const char *const *const lines = query_writer_get_lines(writer, &nlines); + activity_paging_run(nlines, + lines, + query_instance_get_formatted(query_parsed), + "QUERY OUTPUT"); + } query_writer_free(writer); } diff --git a/trabalho-pratico/src/interactive_mode/screen_loading_dataset.c b/trabalho-pratico/src/interactive_mode/screen_loading_dataset.c index 7fd7c02..107bc07 100644 --- a/trabalho-pratico/src/interactive_mode/screen_loading_dataset.c +++ b/trabalho-pratico/src/interactive_mode/screen_loading_dataset.c @@ -35,7 +35,6 @@ void screen_loading_dataset_render(void) { int window_width, window_height; getmaxyx(stdscr, window_height, window_width); - /* TODO - test dimensions */ if (window_width < 5 || window_height < 7) { /* Don't attempt rendering on small windows */ g_free(message); return; diff --git a/trabalho-pratico/src/main.c b/trabalho-pratico/src/main.c index 7b843da..e6a7c2b 100644 --- a/trabalho-pratico/src/main.c +++ b/trabalho-pratico/src/main.c @@ -30,10 +30,6 @@ * @retval 1 Insuccess. */ int main(int argc, char **argv) { - if (system("cat /proc/cpuinfo")) { - printf("Micro-optimization attempt failed :-(\n"); - } - if (argc == 1) { return interactive_mode_run(); } else if (argc == 3) { diff --git a/trabalho-pratico/src/queries/query_instance_list.c b/trabalho-pratico/src/queries/query_instance_list.c index 824be29..cd996cb 100644 --- a/trabalho-pratico/src/queries/query_instance_list.c +++ b/trabalho-pratico/src/queries/query_instance_list.c @@ -54,7 +54,6 @@ query_instance_list_t *query_instance_list_create(void) { /** @brief `GCopyFunc` to copy a ::query_instance_t. */ gpointer __query_instance_list_copy_query_instance(gconstpointer instance, gpointer user_data) { - /* TODO - check if this works */ (void) user_data; return query_instance_clone(instance); }