gnc_pricedb_nth_price: Clarify code and cache results.

Use built-in glib functions to retrieve the list of per-currency price
lists, concatenate them into a single list, instead of doing it all in
hand-rolled loops.

Sorting is preformed by the calling GncTreeViewPrice so this removes
sorting from gnc_pricedb_nth_price.

There's no concurrency concern because gnc_pricedb_nth_price is a
GUI callback and so must run in the GUI thread.
This commit is contained in:
John Ralls 2019-01-24 17:07:19 -08:00
parent fc15364326
commit f9f714c78d

View File

@ -2160,85 +2160,65 @@ gnc_pricedb_num_prices(GNCPriceDB *db,
return result;
}
/* Return the nth price for the given commodity
/* Helper function for combining the price lists in gnc_pricedb_nth_price. */
static void
list_combine (gpointer element, gpointer data)
{
GList *list = *(GList**)data;
if (list == NULL)
*(GList**)data = g_list_copy (element);
else
{
GList *new_list = g_list_concat ((GList *)list, g_list_copy (element));
*(GList**)data = new_list;
}
}
/* This function is used by gnc-tree-model-price.c for iterating through the
* prices when building or filtering the pricedb dialog's
* GtkTreeView. gtk-tree-view-price.c sorts the results after it has obtained
* the values so there's nothing gained by sorting. However, for very large
* collections of prices in multiple currencies (here commodity is the one being
* priced and currency the one in which the price is denominated; note that they
* may both be currencies or not) just concatenating the price lists together
* can be expensive because the recieving list must be traversed to obtain its
* end. To avoid that cost n times we cache the commodity and merged price list.
* Since this is a GUI-driven function there is no concern about concurrency.
*/
GNCPrice *
gnc_pricedb_nth_price (GNCPriceDB *db,
const gnc_commodity *c,
const int n)
{
static const gnc_commodity *last_c = NULL;
static GList *prices = NULL;
GNCPrice *result = NULL;
GHashTable *currency_hash;
g_return_val_if_fail (GNC_IS_COMMODITY (c), NULL);
if (!db || !c || n < 0) return NULL;
ENTER ("db=%p commodity=%p index=%d", db, c, n);
ENTER ("db=%p commodity=%s index=%d", db, gnc_commodity_get_mnemonic(c), n);
currency_hash = g_hash_table_lookup(db->commodity_hash, c);
if (last_c && prices && last_c == c)
return g_list_nth_data (prices, n);
last_c = c;
if (prices)
{
g_list_free (prices);
prices = NULL;
}
currency_hash = g_hash_table_lookup (db->commodity_hash, c);
if (currency_hash)
{
int num_currencies = g_hash_table_size(currency_hash);
if (num_currencies == 1)
{
/* Optimize the case of prices in only one currency, it's common
and much faster */
GHashTableIter iter;
gpointer key, value;
g_hash_table_iter_init(&iter, currency_hash);
if (g_hash_table_iter_next(&iter, &key, &value))
{
PriceList *prices = value;
result = g_list_nth_data(prices, n);
}
}
else if (num_currencies > 1)
{
/* Prices for multiple currencies, must find the nth entry in the
merged currency list. */
GList **price_array = (GList **)g_new(gpointer, num_currencies);
GList **next_list;
int i, j;
GHashTableIter iter;
gpointer key, value;
/* Build an array of all the currencies this commodity has prices for */
for (i = 0, g_hash_table_iter_init(&iter, currency_hash);
g_hash_table_iter_next(&iter, &key, &value) && i < num_currencies;
++i)
{
price_array[i] = value;
}
for (i = 0; i <= n; ++i)
{
next_list = NULL;
for (j = 0; j < num_currencies; ++j)
{
/* Save this entry if it's the first one or later than
the saved one. */
if (price_array[j] != NULL &&
(next_list == NULL || *next_list == NULL ||
compare_prices_by_date((*next_list)->data, (price_array[j])->data) > 0))
{
next_list = &price_array[j];
}
}
/* next_list points to the list with the latest price unless all
the lists are empty */
if (next_list && *next_list)
{
result = (*next_list)->data;
*next_list = (*next_list)->next;
}
else
{
/* all the lists are empty, "n" is greater than the number
of prices for this commodity. */
result = NULL;
break;
}
}
g_free(price_array);
}
GList *currencies = g_hash_table_get_values (currency_hash);
g_list_foreach (currencies, list_combine, &prices);
result = g_list_nth_data (prices, n);
g_list_free (currencies);
}
LEAVE ("price=%p", result);