diff --git a/docs/reference/glib/glib-sections.txt.in b/docs/reference/glib/glib-sections.txt.in index ab7453e60..0ec7d77ba 100644 --- a/docs/reference/glib/glib-sections.txt.in +++ b/docs/reference/glib/glib-sections.txt.in @@ -2736,6 +2736,8 @@ g_ptr_array_steal_index g_ptr_array_steal_index_fast g_ptr_array_sort g_ptr_array_sort_with_data +g_ptr_array_sort_values +g_ptr_array_sort_values_with_data g_ptr_array_set_size g_ptr_array_index g_ptr_array_free diff --git a/glib/garray.c b/glib/garray.c index ee4b859fe..42f36b2be 100644 --- a/glib/garray.c +++ b/glib/garray.c @@ -2124,7 +2124,10 @@ g_ptr_array_insert (GPtrArray *array, * * Note that the comparison function for g_ptr_array_sort() doesn't * take the pointers from the array as arguments, it takes pointers to - * the pointers in the array. Here is a full example of usage: + * the pointers in the array. + * + * Use g_ptr_array_sort_with_data() if you want to use normal + * #GCompareFuncs, otherwise here is a full example of use: * * |[ * typedef struct @@ -2181,7 +2184,10 @@ g_ptr_array_sort (GPtrArray *array, * * Note that the comparison function for g_ptr_array_sort_with_data() * doesn't take the pointers from the array as arguments, it takes - * pointers to the pointers in the array. Here is a full example of use: + * pointers to the pointers in the array. + * + * Use g_ptr_array_sort_with_data() if you want to use normal + * #GCompareDataFuncs, otherwise here is a full example of use: * * |[ * typedef enum { SORT_NAME, SORT_SIZE } SortMode; @@ -2245,6 +2251,80 @@ g_ptr_array_sort_with_data (GPtrArray *array, user_data); } +static inline gint +compare_ptr_array_values (gconstpointer a, gconstpointer b, gpointer user_data) +{ + gconstpointer aa = *((gconstpointer *) a); + gconstpointer bb = *((gconstpointer *) b); + GCompareFunc compare_func = user_data; + + return compare_func (aa, bb); +} + +/** + * g_ptr_array_sort_values: + * @array: a #GPtrArray + * @compare_func: a #GCompareFunc comparison function + * + * Sorts the array, using @compare_func which should be a qsort()-style + * comparison function (returns less than zero for first arg is less + * than second arg, zero for equal, greater than zero if first arg is + * greater than second arg). + * + * This is guaranteed to be a stable sort. + * + * Since: 2.76 + */ +void +g_ptr_array_sort_values (GPtrArray *array, + GCompareFunc compare_func) +{ + g_ptr_array_sort_with_data (array, compare_ptr_array_values, compare_func); +} + +typedef struct +{ + GCompareDataFunc compare_func; + gpointer user_data; +} GPtrArraySortValuesData; + +static inline gint +compare_ptr_array_values_with_data (gconstpointer a, + gconstpointer b, + gpointer user_data) +{ + gconstpointer aa = *((gconstpointer *) a); + gconstpointer bb = *((gconstpointer *) b); + GPtrArraySortValuesData *data = user_data; + + return data->compare_func (aa, bb, data->user_data); +} + +/** + * g_ptr_array_sort_values_with_data: + * @array: a #GPtrArray + * @compare_func: a #GCompareDataFunc comparison function + * @user_data: data to pass to @compare_func + * + * Like g_ptr_array_sort_values(), but the comparison function has an extra + * user data argument. + * + * This is guaranteed to be a stable sort. + * + * Since: 2.76 + */ +void +g_ptr_array_sort_values_with_data (GPtrArray *array, + GCompareDataFunc compare_func, + gpointer user_data) +{ + g_ptr_array_sort_with_data (array, compare_ptr_array_values_with_data, + &(GPtrArraySortValuesData){ + .compare_func = compare_func, + .user_data = user_data, + }); +} + /** * g_ptr_array_foreach: * @array: a #GPtrArray diff --git a/glib/garray.h b/glib/garray.h index 5b9c6752a..16f74015e 100644 --- a/glib/garray.h +++ b/glib/garray.h @@ -242,6 +242,13 @@ GLIB_AVAILABLE_IN_ALL void g_ptr_array_sort_with_data (GPtrArray *array, GCompareDataFunc compare_func, gpointer user_data); +GLIB_AVAILABLE_IN_2_76 +void g_ptr_array_sort_values (GPtrArray *array, + GCompareFunc compare_func); +GLIB_AVAILABLE_IN_2_76 +void g_ptr_array_sort_values_with_data (GPtrArray *array, + GCompareDataFunc compare_func, + gpointer user_data); GLIB_AVAILABLE_IN_ALL void g_ptr_array_foreach (GPtrArray *array, GFunc func, diff --git a/glib/tests/array-test.c b/glib/tests/array-test.c index 9826d3e33..257170567 100644 --- a/glib/tests/array-test.c +++ b/glib/tests/array-test.c @@ -1474,13 +1474,25 @@ pointer_array_extend_and_steal (void) g_free (array_test); } +static gint +ptr_compare_values (gconstpointer p1, gconstpointer p2) +{ + return GPOINTER_TO_INT (p1) - GPOINTER_TO_INT (p2); +} + static gint ptr_compare (gconstpointer p1, gconstpointer p2) { gpointer i1 = *(gpointer*)p1; gpointer i2 = *(gpointer*)p2; - return GPOINTER_TO_INT (i1) - GPOINTER_TO_INT (i2); + return ptr_compare_values (i1, i2); +} + +static gint +ptr_compare_values_data (gconstpointer p1, gconstpointer p2, gpointer data) +{ + return GPOINTER_TO_INT (p1) - GPOINTER_TO_INT (p2); } static gint @@ -1489,7 +1501,7 @@ ptr_compare_data (gconstpointer p1, gconstpointer p2, gpointer data) gpointer i1 = *(gpointer*)p1; gpointer i2 = *(gpointer*)p2; - return GPOINTER_TO_INT (i1) - GPOINTER_TO_INT (i2); + return ptr_compare_values_data (i1, i2, data); } static void @@ -1696,6 +1708,204 @@ pointer_array_sort_with_data (void) g_ptr_array_free (gparray, TRUE); } +static void +pointer_array_sort_values (void) +{ + GPtrArray *gparray; + gint i; + gint val; + gint prev, cur; + + gparray = g_ptr_array_new (); + + /* Sort empty array */ + g_ptr_array_sort_values (gparray, ptr_compare_values); + + for (i = 0; i < 10000; i++) + { + val = g_random_int_range (0, 10000); + g_ptr_array_add (gparray, GINT_TO_POINTER (val)); + } + + g_ptr_array_sort_values (gparray, ptr_compare_values); + + prev = -1; + for (i = 0; i < 10000; i++) + { + cur = GPOINTER_TO_INT (g_ptr_array_index (gparray, i)); + g_assert_cmpint (prev, <=, cur); + prev = cur; + } + + g_clear_pointer (&gparray, g_ptr_array_unref); + + gparray = g_ptr_array_new (); + + g_ptr_array_add (gparray, "dddd"); + g_ptr_array_add (gparray, "cccc"); + g_ptr_array_add (gparray, NULL); + g_ptr_array_add (gparray, "bbbb"); + g_ptr_array_add (gparray, "aaaa"); + + g_ptr_array_sort_values (gparray, (GCompareFunc) g_strcmp0); + + i = 0; + g_assert_cmpstr (g_ptr_array_index (gparray, i++), ==, NULL); + g_assert_cmpstr (g_ptr_array_index (gparray, i++), ==, "aaaa"); + g_assert_cmpstr (g_ptr_array_index (gparray, i++), ==, "bbbb"); + g_assert_cmpstr (g_ptr_array_index (gparray, i++), ==, "cccc"); + g_assert_cmpstr (g_ptr_array_index (gparray, i++), ==, "dddd"); + + g_clear_pointer (&gparray, g_ptr_array_unref); +} + +static gint +sort_filelist_values (gconstpointer a, gconstpointer b) +{ + const FileListEntry *entry1 = a; + const FileListEntry *entry2 = b; + + return g_ascii_strcasecmp (entry1->name, entry2->name); +} + +static void +pointer_array_sort_values_example (void) +{ + GPtrArray *file_list = NULL; + FileListEntry *entry; + + file_list = g_ptr_array_new_with_free_func (file_list_entry_free); + + entry = g_new0 (FileListEntry, 1); + entry->name = g_strdup ("README"); + entry->size = 42; + g_ptr_array_add (file_list, g_steal_pointer (&entry)); + + entry = g_new0 (FileListEntry, 1); + entry->name = g_strdup ("empty"); + entry->size = 0; + g_ptr_array_add (file_list, g_steal_pointer (&entry)); + + entry = g_new0 (FileListEntry, 1); + entry->name = g_strdup ("aardvark"); + entry->size = 23; + g_ptr_array_add (file_list, g_steal_pointer (&entry)); + + g_ptr_array_sort_values (file_list, sort_filelist_values); + + g_assert_cmpuint (file_list->len, ==, 3); + entry = g_ptr_array_index (file_list, 0); + g_assert_cmpstr (entry->name, ==, "aardvark"); + entry = g_ptr_array_index (file_list, 1); + g_assert_cmpstr (entry->name, ==, "empty"); + entry = g_ptr_array_index (file_list, 2); + g_assert_cmpstr (entry->name, ==, "README"); + + g_ptr_array_unref (file_list); +} + +static gint +sort_filelist_how_values (gconstpointer a, gconstpointer b, gpointer user_data) +{ + gint order; + const SortMode sort_mode = GPOINTER_TO_INT (user_data); + const FileListEntry *entry1 = a; + const FileListEntry *entry2 = b; + + switch (sort_mode) + { + case SORT_NAME: + order = g_ascii_strcasecmp (entry1->name, entry2->name); + break; + case SORT_SIZE: + order = entry1->size - entry2->size; + break; + default: + order = 0; + break; + } + return order; +} + +static void +pointer_array_sort_values_with_data_example (void) +{ + GPtrArray *file_list = NULL; + FileListEntry *entry; + SortMode sort_mode; + + file_list = g_ptr_array_new_with_free_func (file_list_entry_free); + + entry = g_new0 (FileListEntry, 1); + entry->name = g_strdup ("README"); + entry->size = 42; + g_ptr_array_add (file_list, g_steal_pointer (&entry)); + + entry = g_new0 (FileListEntry, 1); + entry->name = g_strdup ("empty"); + entry->size = 0; + g_ptr_array_add (file_list, g_steal_pointer (&entry)); + + entry = g_new0 (FileListEntry, 1); + entry->name = g_strdup ("aardvark"); + entry->size = 23; + g_ptr_array_add (file_list, g_steal_pointer (&entry)); + + sort_mode = SORT_NAME; + g_ptr_array_sort_values_with_data (file_list, sort_filelist_how_values, + GINT_TO_POINTER (sort_mode)); + + g_assert_cmpuint (file_list->len, ==, 3); + entry = g_ptr_array_index (file_list, 0); + g_assert_cmpstr (entry->name, ==, "aardvark"); + entry = g_ptr_array_index (file_list, 1); + g_assert_cmpstr (entry->name, ==, "empty"); + entry = g_ptr_array_index (file_list, 2); + g_assert_cmpstr (entry->name, ==, "README"); + + sort_mode = SORT_SIZE; + g_ptr_array_sort_values_with_data (file_list, sort_filelist_how_values, + GINT_TO_POINTER (sort_mode)); + + g_assert_cmpuint (file_list->len, ==, 3); + entry = g_ptr_array_index (file_list, 0); + g_assert_cmpstr (entry->name, ==, "empty"); + entry = g_ptr_array_index (file_list, 1); + g_assert_cmpstr (entry->name, ==, "aardvark"); + entry = g_ptr_array_index (file_list, 2); + g_assert_cmpstr (entry->name, ==, "README"); + + g_ptr_array_unref (file_list); +} + +static void +pointer_array_sort_values_with_data (void) +{ + GPtrArray *gparray; + gint i; + gint prev, cur; + + gparray = g_ptr_array_new (); + + /* Sort empty array */ + g_ptr_array_sort_values_with_data (gparray, ptr_compare_values_data, NULL); + + for (i = 0; i < 10000; i++) + g_ptr_array_add (gparray, GINT_TO_POINTER (g_random_int_range (0, 10000))); + + g_ptr_array_sort_values_with_data (gparray, ptr_compare_values_data, NULL); + + prev = -1; + for (i = 0; i < 10000; i++) + { + cur = GPOINTER_TO_INT (g_ptr_array_index (gparray, i)); + g_assert_cmpint (prev, <=, cur); + prev = cur; + } + + g_ptr_array_free (gparray, TRUE); +} + static void pointer_array_find_empty (void) { @@ -2240,6 +2450,10 @@ main (int argc, char *argv[]) g_test_add_func ("/pointerarray/sort/example", pointer_array_sort_example); g_test_add_func ("/pointerarray/sort-with-data", pointer_array_sort_with_data); g_test_add_func ("/pointerarray/sort-with-data/example", pointer_array_sort_with_data_example); + g_test_add_func ("/pointerarray/sort-values", pointer_array_sort_values); + g_test_add_func ("/pointerarray/sort-values/example", pointer_array_sort_values_example); + g_test_add_func ("/pointerarray/sort-values-with-data", pointer_array_sort_values_with_data); + g_test_add_func ("/pointerarray/sort-values-with-data/example", pointer_array_sort_values_with_data_example); g_test_add_func ("/pointerarray/find/empty", pointer_array_find_empty); g_test_add_func ("/pointerarray/find/non-empty", pointer_array_find_non_empty); g_test_add_func ("/pointerarray/remove-range", pointer_array_remove_range);