diff options
author | Benjamin Otte <otte@redhat.com> | 2021-11-25 06:30:52 +0100 |
---|---|---|
committer | Benjamin Otte <otte@redhat.com> | 2023-04-15 06:11:54 +0200 |
commit | 901a9edeb8d654d4db9000f2845bd6913da6dcde (patch) | |
tree | 7026225e2ef0e33ae2d142fe29a3906197647e1f | |
parent | 7ce2d70e0e9d6629c7800e845e3a8e687a20a5c3 (diff) | |
download | gtk+-901a9edeb8d654d4db9000f2845bd6913da6dcde.tar.gz |
Add GtkJsonPrinter
-rw-r--r-- | gtk/json/gtkjsonprinter.c | 390 | ||||
-rw-r--r-- | gtk/json/gtkjsonprinterprivate.h | 76 | ||||
-rw-r--r-- | gtk/json/meson.build | 1 |
3 files changed, 467 insertions, 0 deletions
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 = [ |