From 06767c0a1d48f3226fc6a94e997a163a0776ac1f Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 16 Jun 2023 16:16:44 -0700 Subject: [PATCH] libsysprof-analyze: add an XY series strucuture This is meant to contain information about XY coordinates that we can store in a normalized form. That allows them to be reused across different scales without regenerating them. It also includes a back-pointer to the model index so that we can pull out the real object when necessary for drawing. --- src/libsysprof-analyze/meson.build | 36 ++-- src/libsysprof-analyze/sysprof-analyze.h | 1 + src/libsysprof-analyze/sysprof-xy-series.c | 194 +++++++++++++++++++++ src/libsysprof-analyze/sysprof-xy-series.h | 68 ++++++++ 4 files changed, 282 insertions(+), 17 deletions(-) create mode 100644 src/libsysprof-analyze/sysprof-xy-series.c create mode 100644 src/libsysprof-analyze/sysprof-xy-series.h diff --git a/src/libsysprof-analyze/meson.build b/src/libsysprof-analyze/meson.build index 1409a669..78bb4a37 100644 --- a/src/libsysprof-analyze/meson.build +++ b/src/libsysprof-analyze/meson.build @@ -34,23 +34,7 @@ libsysprof_analyze_public_sources = [ 'sysprof-symbolizer.c', 'sysprof-time-series.c', 'sysprof-time-span.c', -] - -libsysprof_analyze_private_sources = [ - 'sysprof-address-layout.c', - 'sysprof-descendants-model.c', - 'sysprof-document-bitset-index.c', - 'sysprof-document-symbols.c', - 'sysprof-elf.c', - 'sysprof-elf-loader.c', - 'sysprof-mount.c', - 'sysprof-mount-device.c', - 'sysprof-mount-namespace.c', - 'sysprof-process-info.c', - 'sysprof-strings.c', - 'sysprof-symbol-cache.c', - 'sysprof-time-series.h', - 'sysprof-time-span.h', + 'sysprof-xy-series.c', ] libsysprof_analyze_public_headers = [ @@ -89,6 +73,24 @@ libsysprof_analyze_public_headers = [ 'sysprof-no-symbolizer.h', 'sysprof-symbol.h', 'sysprof-symbolizer.h', + 'sysprof-time-series.h', + 'sysprof-time-span.h', + 'sysprof-xy-series.h', +] + +libsysprof_analyze_private_sources = [ + 'sysprof-address-layout.c', + 'sysprof-descendants-model.c', + 'sysprof-document-bitset-index.c', + 'sysprof-document-symbols.c', + 'sysprof-elf.c', + 'sysprof-elf-loader.c', + 'sysprof-mount.c', + 'sysprof-mount-device.c', + 'sysprof-mount-namespace.c', + 'sysprof-process-info.c', + 'sysprof-strings.c', + 'sysprof-symbol-cache.c', ] libsysprof_analyze_deps = [ diff --git a/src/libsysprof-analyze/sysprof-analyze.h b/src/libsysprof-analyze/sysprof-analyze.h index 78fb828b..c89497ff 100644 --- a/src/libsysprof-analyze/sysprof-analyze.h +++ b/src/libsysprof-analyze/sysprof-analyze.h @@ -61,6 +61,7 @@ G_BEGIN_DECLS # include "sysprof-symbolizer.h" # include "sysprof-time-series.h" # include "sysprof-time-span.h" +# include "sysprof-xy-series.h" #undef SYSPROF_ANALYZE_INSIDE G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-xy-series.c b/src/libsysprof-analyze/sysprof-xy-series.c new file mode 100644 index 00000000..cf299c10 --- /dev/null +++ b/src/libsysprof-analyze/sysprof-xy-series.c @@ -0,0 +1,194 @@ +/* sysprof-xy-series.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +#include "config.h" + +#include "sysprof-xy-series.h" + +struct _SysprofXYSeries +{ + /* Model of SysprofDocumentFrame */ + GListModel *model; + + /* Array of SysprofXYSeriesValue */ + GArray *values; + + /* Our bounds for non-normalized values */ + float min_x; + float min_y; + float max_x; + float max_y; + + /* Pre-calculated distance between min/max */ + float x_distance; + float y_distance; + + /* Used for warning on items-changed */ + gulong items_changed_handler; +}; + +G_DEFINE_BOXED_TYPE (SysprofXYSeries, + sysprof_xy_series, + sysprof_xy_series_ref, + sysprof_xy_series_unref) + +static void +warn_on_items_changed_cb (GListModel *model, + guint position, + guint removed, + guint added, + gpointer user_data) +{ + g_critical ("%s @ %p emitted items changed while a xyseries is active! Expect errors.", + G_OBJECT_TYPE_NAME (model), model); +} + +/** + * sysprof_xy_series_new: + * @model: a #GListModel + * @xy_span: the span of the xy series + * + * Used for creating normalized xy spans (between 0..1) for xys + * within a xyspan. Useful for creating xy-based charts at any + * size or scale. + * + * It is required that @model does not change during the lifexy + * of the xy series. + */ +SysprofXYSeries * +sysprof_xy_series_new (GListModel *model, + float min_x, + float min_y, + float max_x, + float max_y) +{ + SysprofXYSeries *self; + + g_return_val_if_fail (G_IS_LIST_MODEL (model), NULL); + + self = g_atomic_rc_box_new0 (SysprofXYSeries); + self->model = g_object_ref (model); + self->values = g_array_new (FALSE, FALSE, sizeof (SysprofXYSeriesValue)); + self->min_x = min_x; + self->min_y = min_y; + self->max_x = max_x; + self->max_y = max_y; + self->items_changed_handler = + g_signal_connect (self->model, + "items-changed", + G_CALLBACK (warn_on_items_changed_cb), + NULL); + + return self; +} + +SysprofXYSeries * +sysprof_xy_series_ref (SysprofXYSeries *self) +{ + return g_atomic_rc_box_acquire (self); +} + +static void +_sysprof_xy_series_finalize (SysprofXYSeries *self) +{ + g_clear_signal_handler (&self->items_changed_handler, self->model); + g_clear_object (&self->model); + g_clear_pointer (&self->values, g_array_unref); +} + +void +sysprof_xy_series_unref (SysprofXYSeries *self) +{ + g_atomic_rc_box_release_full (self, + (GDestroyNotify)_sysprof_xy_series_finalize); +} + +void +sysprof_xy_series_add (SysprofXYSeries *self, + float x, + float y, + guint index) +{ + SysprofXYSeriesValue value; + + if (x < self->min_x || x > self->max_x) + return; + + if (y < self->min_y || y > self->max_y) + return; + + value.x = (x - self->min_x) / self->x_distance; + value.y = (y - self->min_y) / self->y_distance; + value.index = index; + + g_array_append_val (self->values, value); +} + +/** + * sysprof_xy_series_get_model: + * @self: a #SysprofXYSeries + * + * Gets the underlying model for the xy series. + * + * Returns: (transfer none): a #GListModel + */ +GListModel * +sysprof_xy_series_get_model (SysprofXYSeries *self) +{ + g_return_val_if_fail (self != NULL, NULL); + + return self->model; +} + +const SysprofXYSeriesValue * +sysprof_xy_series_get_values (const SysprofXYSeries *self, + guint *n_values) +{ + *n_values = self->values->len; + return &g_array_index (self->values, SysprofXYSeriesValue, 0); +} + +static int +compare_by_xy (gconstpointer aptr, + gconstpointer bptr) +{ + const SysprofXYSeriesValue *a = aptr; + const SysprofXYSeriesValue *b = bptr; + + if (a->x < b->x) + return -1; + else if (a->x > b->x) + return 1; + + if (a->y < b->y) + return -1; + else if (a->y > b->y) + return 1; + + return 0; +} + +void +sysprof_xy_series_sort (SysprofXYSeries *self) +{ + qsort (self->values->data, + self->values->len, + sizeof (SysprofXYSeriesValue), + compare_by_xy); +} diff --git a/src/libsysprof-analyze/sysprof-xy-series.h b/src/libsysprof-analyze/sysprof-xy-series.h new file mode 100644 index 00000000..ac6b1c46 --- /dev/null +++ b/src/libsysprof-analyze/sysprof-xy-series.h @@ -0,0 +1,68 @@ +/* sysprof-xy-series.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +#include + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_XY_SERIES (sysprof_xy_series_get_type()) + +typedef struct _SysprofXYSeries SysprofXYSeries; +typedef struct _SysprofXYSeriesValue SysprofXYSeriesValue; + +struct _SysprofXYSeriesValue +{ + float x; + float y; + guint index; +}; + +SYSPROF_AVAILABLE_IN_ALL +GType sysprof_xy_series_get_type (void) G_GNUC_CONST; +SYSPROF_AVAILABLE_IN_ALL +SysprofXYSeries *sysprof_xy_series_new (GListModel *model, + float min_x, + float min_y, + float max_x, + float max_y); +SYSPROF_AVAILABLE_IN_ALL +SysprofXYSeries *sysprof_xy_series_ref (SysprofXYSeries *self); +SYSPROF_AVAILABLE_IN_ALL +void sysprof_xy_series_unref (SysprofXYSeries *self); +SYSPROF_AVAILABLE_IN_ALL +void sysprof_xy_series_add (SysprofXYSeries *self, + float x, + float y, + guint index); +SYSPROF_AVAILABLE_IN_ALL +void sysprof_xy_series_sort (SysprofXYSeries *self); +SYSPROF_AVAILABLE_IN_ALL +GListModel *sysprof_xy_series_get_model (SysprofXYSeries *self); +SYSPROF_AVAILABLE_IN_ALL +const SysprofXYSeriesValue *sysprof_xy_series_get_values (const SysprofXYSeries *self, + guint *n_values); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofXYSeries, sysprof_xy_series_unref) + +G_END_DECLS