diff options
author | Benjamin Otte <otte@redhat.com> | 2021-11-29 11:21:37 +0100 |
---|---|---|
committer | Benjamin Otte <otte@redhat.com> | 2023-04-15 06:11:54 +0200 |
commit | 798c35f5066aa061f1d85cb01d559aec33ce9a14 (patch) | |
tree | a98a3535e0387ced07db84d56817b28aadc733be | |
parent | 424e5eaf4eebaee14d14c7dd04acfda2655b58ac (diff) | |
download | gtk+-798c35f5066aa061f1d85cb01d559aec33ce9a14.tar.gz |
jsonparser: Make select_member() fast
Introduce a JsonStringIterator that allows iterating over a json encoded
string without memory allocation - use that code to do strcmp()-like
matching as well as strdup()-like copying.
Much fancy now.
-rw-r--r-- | gtk/json/gtkjsonparser.c | 156 |
1 files changed, 118 insertions, 38 deletions
diff --git a/gtk/json/gtkjsonparser.c b/gtk/json/gtkjsonparser.c index a2054c373c..38a90b4d67 100644 --- a/gtk/json/gtkjsonparser.c +++ b/gtk/json/gtkjsonparser.c @@ -375,6 +375,58 @@ gtk_json_unescape_char (const guchar *json_escape, } } +typedef struct _JsonStringIter JsonStringIter; +struct _JsonStringIter +{ + char buf[6]; + const guchar *s; + const guchar *next; +}; + +static gsize +json_string_iter_next (JsonStringIter *iter) +{ + gsize len; + + iter->s = iter->next; + iter->next = json_find_character (iter->s, STRING_MARKER); + if (iter->next != iter->s) + return iter->next - iter->s; + if (*iter->next == '"') + return 0; + iter->next += gtk_json_unescape_char (iter->next, iter->buf, &len); + iter->s = (const guchar *) iter->buf; + return len; +} + +/* The escaped string MUST be valid json, so it must begin + * with " and end with " and must not contain any invalid + * escape codes. + * This function is meant to be fast + */ +static gsize +json_string_iter_init (JsonStringIter *iter, + const guchar *string) +{ + g_assert (*string == '"'); + + iter->next = string + 1; + + return json_string_iter_next (iter); +} + +static gboolean +json_string_iter_has_next (JsonStringIter *iter) +{ + return *iter->next != '"'; +} + +static const char * +json_string_iter_get (JsonStringIter *iter) +{ + return (const char *) iter->s; +} + /* The escaped string MUST be valid json, so it must begin * with " and end with " and must not contain any invalid * escape codes. @@ -383,36 +435,25 @@ gtk_json_unescape_char (const guchar *json_escape, static char * gtk_json_unescape_string (const guchar *escaped) { - char buf[6]; - gsize buf_size; + JsonStringIter iter; GString *string; - const guchar *last, *s; + gsize len; + len = json_string_iter_init (&iter, escaped); string = NULL; - g_assert (*escaped == '"'); - last = escaped + 1; - for (s = json_find_character (last, STRING_MARKER); - *s != '"'; - s = json_find_character (last, STRING_MARKER)) - { - g_assert (*s == '\\'); - if (string == NULL) - string = g_string_new (NULL); - g_string_append_len (string, (const char *) last, s - last); - last = s + gtk_json_unescape_char (s, buf, &buf_size); - g_string_append_len (string, buf, buf_size); - } + if (!json_string_iter_has_next (&iter)) + return g_strndup (json_string_iter_get (&iter), len); - if (string) - { - g_string_append_len (string, (const char *) last, s - last); - return g_string_free (string, FALSE); - } - else + string = g_string_new (NULL); + + do { - return g_strndup ((const char *) last, s - last); + g_string_append_len (string, json_string_iter_get (&iter), len); } + while ((len = json_string_iter_next (&iter))); + + return g_string_free (string, FALSE); } static gboolean @@ -858,16 +899,25 @@ gtk_json_parser_get_error (GtkJsonParser *self) return self->error; } -char * -gtk_json_parser_get_member_name (GtkJsonParser *self) +static gboolean +gtk_json_parser_has_member (GtkJsonParser *self) { if (self->error) - return NULL; + return FALSE; if (self->block->type != GTK_JSON_BLOCK_OBJECT) - return NULL; + return FALSE; if (self->block->member_name == NULL) + return FALSE; + + return TRUE; +} + +char * +gtk_json_parser_get_member_name (GtkJsonParser *self) +{ + if (!gtk_json_parser_has_member (self)) return NULL; return gtk_json_unescape_string (self->block->member_name); @@ -877,24 +927,54 @@ gssize gtk_json_parser_select_member (GtkJsonParser *self, const char * const *options) { - char *member_name; - gssize i; + JsonStringIter iter; + gssize i, j; + gsize found, len; + + if (!gtk_json_parser_has_member (self)) + return -1; - member_name = gtk_json_parser_get_member_name (self); - if (member_name == NULL) + if (options[0] == NULL) return -1; - for (i = 0; options[i]; i++) + found = 0; + i = 0; + + for (len = json_string_iter_init (&iter, self->block->member_name); + len > 0; + len = json_string_iter_next (&iter)) { - if (strcmp (member_name, options[i]) == 0) - break; + const char *s = json_string_iter_get (&iter); + + if (strncmp (options[i] + found, s, len) != 0) + { + for (j = i + 1; options[j]; j++) + { + if (strncmp (options[j], options[i], found) == 0 && + strncmp (options[j] + found, s, len) == 0) + { + i = j; + break; + } + } + if (j != i) + return -1; + } + found += len; } - if (options[i] == NULL) - i = -1; - g_free (member_name); + if (options[i][found] == 0) + return i; + + for (j = i + 1; options[j]; i++) + { + if (strncmp (options[j], options[i], found) != 0) + continue; + if (options[j][found] == 0) + return j; + } - return i; + return -1; } gboolean |