[gtk/wip/otte/json: 3/21] Add GtkJsonPrinter
- From: Benjamin Otte <otte src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk/wip/otte/json: 3/21] Add GtkJsonPrinter
- Date: Sun, 5 Dec 2021 21:10:00 +0000 (UTC)
commit 3520258e5d191b69549367374e940e49d89bf852
Author: Benjamin Otte <otte redhat com>
Date: Thu Nov 25 06:30:52 2021 +0100
Add GtkJsonPrinter
gtk/json/gtkjsonprinter.c | 390 +++++++++++++++++++++++++++++++++++++++
gtk/json/gtkjsonprinterprivate.h | 76 ++++++++
gtk/json/meson.build | 1 +
3 files changed, 467 insertions(+)
---
diff --git a/gtk/json/gtkjsonprinter.c b/gtk/json/gtkjsonprinter.c
new file mode 100644
index 0000000000..37b8d4a118
--- /dev/null
+++ b/gtk/json/gtkjsonprinter.c
@@ -0,0 +1,390 @@
+/*
+ * Copyright © 2021 Benjamin Otte
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Benjamin Otte <otte gnome org>
+ */
+
+
+#include "config.h"
+
+#include "gtkjsonprinterprivate.h"
+
+typedef struct _GtkJsonBlock GtkJsonBlock;
+
+typedef enum {
+ GTK_JSON_BLOCK_TOPLEVEL,
+ GTK_JSON_BLOCK_OBJECT,
+ GTK_JSON_BLOCK_ARRAY,
+} GtkJsonBlockType;
+
+struct _GtkJsonBlock
+{
+ GtkJsonBlockType type;
+ gsize n_elements; /* number of elements already written */
+};
+
+struct _GtkJsonPrinter
+{
+ GtkJsonPrinterFlags flags;
+ char *indentation;
+
+ GtkJsonPrinterWriteFunc write_func;
+ gpointer user_data;
+ GDestroyNotify user_destroy;
+
+ GtkJsonBlock *block; /* current block */
+ GtkJsonBlock *blocks; /* blocks array */
+ GtkJsonBlock *blocks_end; /* blocks array */
+ GtkJsonBlock blocks_preallocated[128]; /* preallocated */
+};
+
+static void
+gtk_json_printer_push_block (GtkJsonPrinter *self,
+ GtkJsonBlockType type)
+{
+ self->block++;
+ if (self->block == self->blocks_end)
+ {
+ gsize old_size = self->blocks_end - self->blocks;
+ gsize new_size = old_size + 128;
+
+ if (self->blocks == self->blocks_preallocated)
+ {
+ self->blocks = g_new (GtkJsonBlock, new_size);
+ memcpy (self->blocks, self->blocks_preallocated, sizeof (GtkJsonBlock) * G_N_ELEMENTS
(self->blocks_preallocated));
+ }
+ else
+ {
+ self->blocks = g_renew (GtkJsonBlock, self->blocks, new_size);
+ }
+ self->blocks_end = self->blocks + new_size;
+ self->block = self->blocks + old_size;
+ }
+
+ self->block->type = type;
+ self->block->n_elements = 0;
+}
+
+static void
+gtk_json_printer_pop_block (GtkJsonPrinter *self)
+{
+ g_assert (self->block > self->blocks);
+ self->block--;
+}
+
+GtkJsonPrinter *
+gtk_json_printer_new (GtkJsonPrinterWriteFunc write_func,
+ gpointer data,
+ GDestroyNotify destroy)
+{
+ GtkJsonPrinter *self;
+
+ g_return_val_if_fail (write_func, NULL);
+
+ self = g_slice_new0 (GtkJsonPrinter);
+
+ self->flags = 0;
+ self->indentation = g_strdup (" ");
+
+ self->write_func = write_func;
+ self->user_data = data;
+ self->user_destroy = destroy;
+
+ self->blocks = self->blocks_preallocated;
+ self->blocks_end = self->blocks + G_N_ELEMENTS (self->blocks_preallocated);
+ self->block = self->blocks;
+ self->block->type = GTK_JSON_BLOCK_TOPLEVEL;
+
+ return self;
+}
+
+void
+gtk_json_printer_free (GtkJsonPrinter *self)
+{
+ g_return_if_fail (self != NULL);
+
+ g_free (self->indentation);
+
+ if (self->user_destroy)
+ self->user_destroy (self->user_data);
+
+ if (self->blocks != self->blocks_preallocated)
+ g_free (self->blocks);
+
+ g_slice_free (GtkJsonPrinter, self);
+}
+
+static gboolean
+gtk_json_printer_has_flag (GtkJsonPrinter *self,
+ GtkJsonPrinterFlags flag)
+{
+ return (self->flags & flag) ? TRUE : FALSE;
+}
+
+gsize
+gtk_json_printer_get_depth (GtkJsonPrinter *self)
+{
+ return self->block - self->blocks;
+}
+
+gsize
+gtk_json_printer_get_n_elements (GtkJsonPrinter *self)
+{
+ return self->block->n_elements;
+}
+
+void
+gtk_json_printer_set_flags (GtkJsonPrinter *self,
+ GtkJsonPrinterFlags flags)
+{
+ g_return_if_fail (self != NULL);
+
+ self->flags = flags;
+}
+
+GtkJsonPrinterFlags
+gtk_json_printer_get_flags (GtkJsonPrinter *self)
+{
+ g_return_val_if_fail (self != NULL, 0);
+
+ return self->flags;
+}
+
+void
+gtk_json_printer_set_indentation (GtkJsonPrinter *self,
+ gsize amount)
+{
+ g_return_if_fail (self != NULL);
+
+ g_free (self->indentation);
+
+ self->indentation = g_malloc (amount + 1);
+ memset (self->indentation, ' ', amount);
+ self->indentation[amount] = 0;
+}
+
+gsize
+gtk_json_printer_get_indentation (GtkJsonPrinter *self)
+{
+ g_return_val_if_fail (self != NULL, 2);
+
+ return strlen (self->indentation);
+}
+
+static void
+gtk_json_printer_write (GtkJsonPrinter *self,
+ const char *s)
+{
+ self->write_func (self, s, self->user_data);
+}
+
+static char *
+gtk_json_printer_escape_string (GtkJsonPrinter *self,
+ const char *str)
+{
+ GString *string;
+
+ string = g_string_new (NULL);
+ string = g_string_append_c (string, '"');
+
+ for (; *str != '\0'; str = g_utf8_next_char (str))
+ {
+ switch (*str)
+ {
+ case '"':
+ g_string_append (string, "\\\"");
+ break;
+ case '\\':
+ g_string_append (string, "\\\\");
+ break;
+ case '\b':
+ g_string_append (string, "\\b");
+ break;
+ case '\f':
+ g_string_append (string, "\\f");
+ break;
+ case '\n':
+ g_string_append (string, "\\n");
+ break;
+ case '\r':
+ g_string_append (string, "\\r");
+ break;
+ case '\t':
+ g_string_append (string, "\\t");
+ break;
+ default:
+ if ((int) *str < 0x20)
+ {
+ if ((guint) *str < 0x20 || gtk_json_printer_has_flag (self, GTK_JSON_PRINTER_ASCII))
+ g_string_append_printf (string, "\\u%04x", g_utf8_get_char (str));
+ else
+ g_string_append_unichar (string, g_utf8_get_char (str));
+ }
+ else
+ g_string_append_c (string, *str);
+ }
+ }
+
+ string = g_string_append_c (string, '"');
+ return g_string_free (string, FALSE);
+}
+
+static void
+gtk_json_printer_newline (GtkJsonPrinter *self)
+{
+ gsize depth;
+
+ if (!gtk_json_printer_has_flag (self, GTK_JSON_PRINTER_PRETTY))
+ return;
+
+ gtk_json_printer_write (self, "\n");
+ for (depth = gtk_json_printer_get_depth (self); depth-->0;)
+ gtk_json_printer_write (self, self->indentation);
+}
+
+static void
+gtk_json_printer_begin_member (GtkJsonPrinter *self,
+ const char *name)
+{
+ if (gtk_json_printer_get_n_elements (self) > 0)
+ gtk_json_printer_write (self, ",");
+ if (self->block->type != GTK_JSON_BLOCK_TOPLEVEL || gtk_json_printer_get_n_elements (self) > 0)
+ gtk_json_printer_newline (self);
+
+ self->block->n_elements++;
+
+ if (name)
+ {
+ char *escaped = gtk_json_printer_escape_string (self, name);
+ gtk_json_printer_write (self, escaped);
+ g_free (escaped);
+ if (gtk_json_printer_has_flag (self, GTK_JSON_PRINTER_PRETTY))
+ gtk_json_printer_write (self, " : ");
+ else
+ gtk_json_printer_write (self, ":");
+ }
+}
+
+void
+gtk_json_printer_add_boolean (GtkJsonPrinter *self,
+ const char *name,
+ gboolean value)
+{
+ g_return_if_fail (self != NULL);
+ g_return_if_fail ((self->block->type == GTK_JSON_BLOCK_OBJECT) == (name != NULL));
+
+ gtk_json_printer_begin_member (self, name);
+ gtk_json_printer_write (self, value ? "true" : "false");
+}
+
+void
+gtk_json_printer_add_number (GtkJsonPrinter *self,
+ const char *name,
+ double value)
+{
+ char buf[G_ASCII_DTOSTR_BUF_SIZE];
+
+ g_return_if_fail (self != NULL);
+ g_return_if_fail ((self->block->type == GTK_JSON_BLOCK_OBJECT) == (name != NULL));
+
+ gtk_json_printer_begin_member (self, name);
+ g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE, value);
+ gtk_json_printer_write (self, buf);
+}
+
+void
+gtk_json_printer_add_string (GtkJsonPrinter *self,
+ const char *name,
+ const char *s)
+{
+ char *escaped;
+
+ g_return_if_fail (self != NULL);
+ g_return_if_fail ((self->block->type == GTK_JSON_BLOCK_OBJECT) == (name != NULL));
+ g_return_if_fail (s != NULL);
+
+ gtk_json_printer_begin_member (self, name);
+ escaped = gtk_json_printer_escape_string (self, s);
+ gtk_json_printer_write (self, escaped);
+ g_free (escaped);
+}
+
+void
+gtk_json_printer_add_null (GtkJsonPrinter *self,
+ const char *name)
+{
+ g_return_if_fail (self != NULL);
+ g_return_if_fail ((self->block->type == GTK_JSON_BLOCK_OBJECT) == (name != NULL));
+
+ gtk_json_printer_begin_member (self, name);
+ gtk_json_printer_write (self, "null");
+}
+
+void
+gtk_json_printer_start_object (GtkJsonPrinter *self,
+ const char *name)
+{
+ g_return_if_fail (self != NULL);
+ g_return_if_fail ((self->block->type == GTK_JSON_BLOCK_OBJECT) == (name != NULL));
+
+ gtk_json_printer_begin_member (self, name);
+ gtk_json_printer_write (self, "{");
+ gtk_json_printer_push_block (self, GTK_JSON_BLOCK_OBJECT);
+}
+
+void
+gtk_json_printer_start_array (GtkJsonPrinter *self,
+ const char *name)
+{
+ g_return_if_fail (self != NULL);
+ g_return_if_fail ((self->block->type == GTK_JSON_BLOCK_OBJECT) == (name != NULL));
+
+ gtk_json_printer_begin_member (self, name);
+ gtk_json_printer_write (self, "[");
+ gtk_json_printer_push_block (self, GTK_JSON_BLOCK_ARRAY);
+}
+
+void
+gtk_json_printer_end (GtkJsonPrinter *self)
+{
+ const char *bracket;
+ gboolean empty;
+
+ g_return_if_fail (self != NULL);
+
+ switch (self->block->type)
+ {
+ case GTK_JSON_BLOCK_OBJECT:
+ bracket = "}";
+ break;
+ case GTK_JSON_BLOCK_ARRAY:
+ bracket = "]";
+ break;
+ case GTK_JSON_BLOCK_TOPLEVEL:
+ default:
+ g_return_if_reached ();
+ }
+
+ empty = gtk_json_printer_get_n_elements (self) == 0;
+ gtk_json_printer_pop_block (self);
+
+ if (!empty)
+ {
+ gtk_json_printer_newline (self);
+ }
+ gtk_json_printer_write (self, bracket);
+}
+
diff --git a/gtk/json/gtkjsonprinterprivate.h b/gtk/json/gtkjsonprinterprivate.h
new file mode 100644
index 0000000000..628496d76c
--- /dev/null
+++ b/gtk/json/gtkjsonprinterprivate.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright © 2021 Benjamin Otte
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Benjamin Otte <otte gnome org>
+ */
+
+
+#ifndef __GTK_JSON_PRINTER_H__
+#define __GTK_JSON_PRINTER_H__
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+typedef struct _GtkJsonPrinter GtkJsonPrinter;
+
+typedef enum {
+ GTK_JSON_PRINTER_PRETTY = (1 << 0),
+ GTK_JSON_PRINTER_ASCII = (1 << 1),
+} GtkJsonPrinterFlags;
+
+typedef void (* GtkJsonPrinterWriteFunc) (GtkJsonPrinter *printer,
+ const char *s,
+ gpointer user_data);
+
+
+GtkJsonPrinter * gtk_json_printer_new (GtkJsonPrinterWriteFunc write_func,
+ gpointer data,
+ GDestroyNotify destroy);
+void gtk_json_printer_free (GtkJsonPrinter *self);
+
+void gtk_json_printer_set_flags (GtkJsonPrinter *self,
+ GtkJsonPrinterFlags flags);
+GtkJsonPrinterFlags gtk_json_printer_get_flags (GtkJsonPrinter *self);
+void gtk_json_printer_set_indentation (GtkJsonPrinter *self,
+ gsize amount);
+gsize gtk_json_printer_get_indentation (GtkJsonPrinter *self);
+
+gsize gtk_json_printer_get_depth (GtkJsonPrinter *self);
+gsize gtk_json_printer_get_n_elements (GtkJsonPrinter *self);
+
+void gtk_json_printer_add_boolean (GtkJsonPrinter *self,
+ const char *name,
+ gboolean value);
+void gtk_json_printer_add_number (GtkJsonPrinter *self,
+ const char *name,
+ double value);
+void gtk_json_printer_add_string (GtkJsonPrinter *self,
+ const char *name,
+ const char *s);
+void gtk_json_printer_add_null (GtkJsonPrinter *self,
+ const char *name);
+
+void gtk_json_printer_start_object (GtkJsonPrinter *self,
+ const char *name);
+void gtk_json_printer_start_array (GtkJsonPrinter *self,
+ const char *name);
+void gtk_json_printer_end (GtkJsonPrinter *self);
+
+
+G_END_DECLS
+
+#endif /* __GTK_JSON_PRINTER_H__ */
diff --git a/gtk/json/meson.build b/gtk/json/meson.build
index efdb4ea5fc..859c3787cb 100644
--- a/gtk/json/meson.build
+++ b/gtk/json/meson.build
@@ -1,5 +1,6 @@
gtk_json_sources = files([
'gtkjsonparser.c',
+ 'gtkjsonprinter.c',
])
gtk_json_deps = [
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]