summaryrefslogtreecommitdiff
path: root/pango/json/gtkjsonprinter.c
diff options
context:
space:
mode:
Diffstat (limited to 'pango/json/gtkjsonprinter.c')
-rw-r--r--pango/json/gtkjsonprinter.c405
1 files changed, 405 insertions, 0 deletions
diff --git a/pango/json/gtkjsonprinter.c b/pango/json/gtkjsonprinter.c
new file mode 100644
index 00000000..f2f1e273
--- /dev/null
+++ b/pango/json/gtkjsonprinter.c
@@ -0,0 +1,405 @@
+/*
+ * 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_integer (GtkJsonPrinter *self,
+ const char *name,
+ int value)
+{
+ char buf[128];
+
+ 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_snprintf (buf, sizeof (buf), "%d", 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);
+}
+