gmarkup: Use gvariant in the gmarkup binary format

This commit is contained in:
Alexander Larsson 2018-09-19 14:15:01 +02:00
parent a83c8705d1
commit 05f0c89ac4

View File

@ -2920,8 +2920,7 @@ struct RecordDataTree {
typedef struct { typedef struct {
char *string; char *string;
int count; int index;
int offset;
} RecordDataString; } RecordDataString;
@ -2972,13 +2971,11 @@ record_data_string_lookup (GHashTable *strings, const char *str, gssize len)
if (s) if (s)
{ {
g_free (copy); g_free (copy);
s->count++;
return s->string; return s->string;
} }
s = g_slice_new (RecordDataString); s = g_slice_new (RecordDataString);
s->string = copy ? copy : g_strdup (str); s->string = copy ? copy : g_strdup (str);
s->count = 1;
g_hash_table_insert (strings, s->string, s); g_hash_table_insert (strings, s->string, s);
return s->string; return s->string;
@ -3065,67 +3062,8 @@ static const GMarkupParser record_parser =
}; };
static gint static guint16
compare_string (gconstpointer _a, get_string_index (GHashTable *strings,
gconstpointer _b)
{
const RecordDataString *a = _a;
const RecordDataString *b = _b;
return b->count - a->count;
}
static void
marshal_uint32 (GString *str,
guint32 v)
{
/*
We encode in a variable length format similar to
utf8:
v size byte 1 byte 2 byte 3 byte 4 byte 5
7 bit: 0xxxxxxx
14 bit: 10xxxxxx xxxxxxxx
21 bit: 110xxxxx xxxxxxxx xxxxxxxx
28 bit: 1110xxxx xxxxxxxx xxxxxxxx xxxxxxxx
32 bit: 11110000 xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxx
*/
if (v < 128)
{
g_string_append_c (str, (guchar)v);
}
else if (v < (1<<14))
{
g_string_append_c (str, (guchar)(v >> 8) | 0x80);
g_string_append_c (str, (guchar)(v & 0xff));
}
else if (v < (1<<21))
{
g_string_append_c (str, (guchar)(v >> 16) | 0xc0);
g_string_append_c (str, (guchar)((v >> 8) & 0xff));
g_string_append_c (str, (guchar)(v & 0xff));
}
else if (v < (1<<28))
{
g_string_append_c (str, (guchar)(v >> 24) | 0xe0);
g_string_append_c (str, (guchar)((v >> 16) & 0xff));
g_string_append_c (str, (guchar)((v >> 8) & 0xff));
g_string_append_c (str, (guchar)(v & 0xff));
}
else
{
g_string_append_c (str, 0xf0);
g_string_append_c (str, (guchar)((v >> 24) & 0xff));
g_string_append_c (str, (guchar)((v >> 16) & 0xff));
g_string_append_c (str, (guchar)((v >> 8) & 0xff));
g_string_append_c (str, (guchar)(v & 0xff));
}
}
static void
marshal_string (GString *marshaled,
GHashTable *strings,
const char *string) const char *string)
{ {
RecordDataString *s; RecordDataString *s;
@ -3133,48 +3071,65 @@ marshal_string (GString *marshaled,
s = g_hash_table_lookup (strings, string); s = g_hash_table_lookup (strings, string);
g_assert (s != NULL); g_assert (s != NULL);
marshal_uint32 (marshaled, s->offset); return s->index;
} }
static void static void
marshal_tree (GString *marshaled, marshal_tree (GVariantBuilder *tree_builder,
GVariantBuilder *args_builder,
gsize *args_builder_count,
GHashTable *strings, GHashTable *strings,
RecordDataTree *tree) RecordDataTree *tree)
{ {
GList *l; GList *l;
int i; int i;
guint16 args_index;
/* Special case the root */ /* Special case the root */
if (tree->parent == NULL) if (tree->parent == NULL)
{ {
for (l = g_list_last (tree->children); l != NULL; l = l->prev) for (l = g_list_last (tree->children); l != NULL; l = l->prev)
marshal_tree (marshaled, strings, l->data); marshal_tree (tree_builder, args_builder, args_builder_count, strings, l->data);
return; return;
} }
switch (tree->type) switch (tree->type)
{ {
case RECORD_TYPE_ELEMENT: case RECORD_TYPE_ELEMENT:
marshal_uint32 (marshaled, RECORD_TYPE_ELEMENT); if (tree->attributes[0] != NULL)
marshal_string (marshaled, strings, tree->data); {
marshal_uint32 (marshaled, g_strv_length ((char **)tree->attributes)); args_index = *args_builder_count;
for (i = 0; tree->attributes[i] != NULL; i++) for (i = 0; tree->attributes[i] != NULL; i++)
{ {
marshal_string (marshaled, strings, tree->attributes[i]); g_variant_builder_add (args_builder, "(qq)",
marshal_string (marshaled, strings, tree->values[i]); GUINT16_TO_LE (get_string_index (strings, tree->attributes[i])),
GUINT16_TO_LE (get_string_index (strings, tree->values[i])));
(*args_builder_count)++;
} }
for (l = g_list_last (tree->children); l != NULL; l = l->prev) g_variant_builder_add (args_builder,"(qq)", 0, 0);
marshal_tree (marshaled, strings, l->data); (*args_builder_count)++;
}
else
args_index = 0;
marshal_uint32 (marshaled, RECORD_TYPE_END_ELEMENT); g_variant_builder_add (tree_builder, "(yqq)", RECORD_TYPE_ELEMENT,
GUINT16_TO_LE (get_string_index (strings, tree->data)),
GUINT16_TO_LE (args_index));
for (l = g_list_last (tree->children); l != NULL; l = l->prev)
marshal_tree (tree_builder, args_builder, args_builder_count, strings, l->data);
g_variant_builder_add (tree_builder, "(yqq)", RECORD_TYPE_END_ELEMENT, 0, 0);
break; break;
case RECORD_TYPE_TEXT: case RECORD_TYPE_TEXT:
marshal_uint32 (marshaled, RECORD_TYPE_TEXT); g_variant_builder_add (tree_builder, "(yqq)", RECORD_TYPE_TEXT,
marshal_string (marshaled, strings, tree->data); GUINT16_TO_LE (get_string_index (strings, tree->data)),
0);
break; break;
case RECORD_TYPE_PASSTHROUGH: case RECORD_TYPE_PASSTHROUGH:
marshal_uint32 (marshaled, RECORD_TYPE_PASSTHROUGH); g_variant_builder_add (tree_builder, "(yqq)", RECORD_TYPE_PASSTHROUGH,
marshal_string (marshaled, strings, tree->data); GUINT16_TO_LE (get_string_index (strings, tree->data)),
0);
break; break;
case RECORD_TYPE_END_ELEMENT: case RECORD_TYPE_END_ELEMENT:
default: default:
@ -3182,8 +3137,6 @@ marshal_tree (GString *marshaled,
} }
} }
static guint32 demarshal_uint32 (const char **tree);
GLIB_AVAILABLE_IN_ALL GLIB_AVAILABLE_IN_ALL
GBytes * GBytes *
g_markup_parse_context_record (GMarkupParseFlags flags, g_markup_parse_context_record (GMarkupParseFlags flags,
@ -3193,9 +3146,14 @@ g_markup_parse_context_record (GMarkupParseFlags flags,
{ {
GMarkupParseContext *ctx; GMarkupParseContext *ctx;
RecordData data = { 0 }; RecordData data = { 0 };
GBytes *result;
GVariant *v;
GList *string_table, *l; GList *string_table, *l;
GString *marshaled; GVariantBuilder stringtable_builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE_STRING_ARRAY);
int offset; GVariantBuilder tree_builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE("a(yqq)"));
GVariantBuilder args_builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE("a(qq)"));
gsize args_builder_count = 0;
int index;
data.strings = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify)record_data_string_free); data.strings = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify)record_data_string_free);
data.root = record_data_tree_new (NULL, RECORD_TYPE_ELEMENT, NULL); data.root = record_data_tree_new (NULL, RECORD_TYPE_ELEMENT, NULL);
@ -3216,100 +3174,72 @@ g_markup_parse_context_record (GMarkupParseFlags flags,
string_table = g_hash_table_get_values (data.strings); string_table = g_hash_table_get_values (data.strings);
string_table = g_list_sort (string_table, compare_string); /* reserve index 0 for end-of-array markers */
index = 1;
offset = 0; g_variant_builder_add (&stringtable_builder, "s", "");
for (l = string_table; l != NULL; l = l->next)
{
RecordDataString *s = l->data;
s->offset = offset;
offset += strlen (s->string) + 1;
}
marshaled = g_string_new ("");
/* Magic marker */
g_string_append_len (marshaled, "GMU\0", 4);
marshal_uint32 (marshaled, offset);
for (l = string_table; l != NULL; l = l->next) for (l = string_table; l != NULL; l = l->next)
{ {
RecordDataString *s = l->data; RecordDataString *s = l->data;
g_string_append_len (marshaled, s->string, strlen (s->string) + 1); s->index = index++;
g_variant_builder_add (&stringtable_builder, "s", s->string);
} }
g_list_free (string_table); /* Reserve 0 as no-args */
g_variant_builder_add (&args_builder, "(qq)", 0, 0);
args_builder_count++;
marshal_tree (marshaled, data.strings, data.root); marshal_tree (&tree_builder, &args_builder, &args_builder_count, data.strings, data.root);
v = g_variant_new ("(s@as@a(qq)@a(yqq))",
"GMU",
g_variant_builder_end (&stringtable_builder),
g_variant_builder_end (&args_builder),
g_variant_builder_end (&tree_builder));
result = g_variant_get_data_as_bytes (v);
g_variant_unref (g_variant_ref_sink (v));
record_data_tree_free (data.root); record_data_tree_free (data.root);
g_list_free (string_table);
g_hash_table_destroy (data.strings); g_hash_table_destroy (data.strings);
return g_string_free_to_bytes (marshaled); return result;
} }
static guint32 typedef struct {
demarshal_uint32 (const char **tree) guint8 type;
{ guint16 data;
const guchar *p = (const guchar *)*tree; guint16 attributes;
guchar c = *p; } RecordDataNode;
/* see marshal_uint32 for format */
if (c < 128) /* 7 bit */ typedef struct {
{ guint16 attr;
*tree += 1; guint16 val;
return c; } RecordDataAttr;
}
else if ((c & 0xc0) == 0x80) /* 14 bit */
{
*tree += 2;
return (c & 0x3f) << 8 | p[1];
}
else if ((c & 0xe0) == 0xc0) /* 21 bit */
{
*tree += 3;
return (c & 0x1f) << 16 | p[1] << 8 | p[2];
}
else if ((c & 0xf0) == 0xe0) /* 28 bit */
{
*tree += 4;
return (c & 0xf) << 24 | p[1] << 16 | p[2] << 8 | p[3];
}
else
{
*tree += 5;
return p[1] << 24 | p[2] << 16 | p[3] << 8 | p[4];
}
}
static const char *
demarshal_string (const char **tree, const char *strings)
{
guint32 offset = demarshal_uint32 (tree);
return strings + offset;
}
static gboolean static gboolean
replay_start_element (GMarkupParseContext *context, replay_start_element (GMarkupParseContext *context,
const char **tree, const char *element_name,
const char *strings, gint attr_index,
const char **strings,
const RecordDataAttr *attrs,
GError **error) GError **error)
{ {
const char *element_name; gsize end_index, n_attrs, i;
guint32 i, n_attrs;
const gchar **attr_names; const gchar **attr_names;
const gchar **attr_values; const gchar **attr_values;
GError *tmp_error = NULL; GError *tmp_error = NULL;
element_name = demarshal_string (tree, strings); /* Count nr of attrs */
n_attrs = demarshal_uint32 (tree); for (end_index = attr_index; attrs[end_index].attr != 0; end_index++)
;
n_attrs = end_index - attr_index,
attr_names = g_newa (const gchar *, n_attrs + 1); attr_names = g_newa (const gchar *, n_attrs + 1);
attr_values = g_newa (const gchar *, n_attrs + 1); attr_values = g_newa (const gchar *, n_attrs + 1);
for (i = 0; i < n_attrs; i++) for (i = 0; i < n_attrs; i++)
{ {
attr_names[i] = demarshal_string (tree, strings); attr_names[i] = strings[GUINT16_FROM_LE(attrs[attr_index + i].attr)];
attr_values[i] = demarshal_string (tree, strings); attr_values[i] = strings[GUINT16_FROM_LE(attrs[attr_index + i].val)];
} }
attr_names[i] = NULL; attr_names[i] = NULL;
attr_values[i] = NULL; attr_values[i] = NULL;
@ -3335,8 +3265,6 @@ replay_start_element (GMarkupParseContext *context,
static gboolean static gboolean
replay_end_element (GMarkupParseContext *context, replay_end_element (GMarkupParseContext *context,
const char **tree,
const char *strings,
GError **error) GError **error)
{ {
GError *tmp_error = NULL; GError *tmp_error = NULL;
@ -3369,15 +3297,11 @@ replay_end_element (GMarkupParseContext *context,
static gboolean static gboolean
replay_text (GMarkupParseContext *context, replay_text (GMarkupParseContext *context,
const char **tree, const char *text,
const char *strings,
GError **error) GError **error)
{ {
const char *text;
GError *tmp_error = NULL; GError *tmp_error = NULL;
text = demarshal_string (tree, strings);
if (context->parser->text) if (context->parser->text)
(*context->parser->text) (context, (*context->parser->text) (context,
text, text,
@ -3396,15 +3320,11 @@ replay_text (GMarkupParseContext *context,
static gboolean static gboolean
replay_passthrough (GMarkupParseContext *context, replay_passthrough (GMarkupParseContext *context,
const char **tree, const char *text,
const char *strings,
GError **error) GError **error)
{ {
const char *text;
GError *tmp_error = NULL; GError *tmp_error = NULL;
text = demarshal_string (tree, strings);
if (context->parser->passthrough) if (context->parser->passthrough)
(*context->parser->passthrough) (context, (*context->parser->passthrough) (context,
text, text,
@ -3427,14 +3347,14 @@ g_markup_parse_context_replay (GMarkupParseContext *context,
gsize data_size, gsize data_size,
GError **error) GError **error)
{ {
const char *data_end; GVariant *v, *stringsv, *nodesv, *attrsv;
guint32 len, type; const char **strings;
const char *strings; const RecordDataAttr *attrs;
const char *tree; const RecordDataNode *nodes;
gsize n_strings, n_attrs, n_nodes, i;
data_end = data + data_size; if (data_size < 4 ||
!(data[0] == 'G' &&
if (!(data[0] == 'G' &&
data[1] == 'M' && data[1] == 'M' &&
data[2] == 'U' && data[2] == 'U' &&
data[3] == 0)) data[3] == 0))
@ -3443,40 +3363,59 @@ g_markup_parse_context_replay (GMarkupParseContext *context,
_("Invalid gmarkup replay data")); _("Invalid gmarkup replay data"));
return FALSE; return FALSE;
} }
data = data + 4;
len = demarshal_uint32 (&data); v = g_variant_new_from_data (G_VARIANT_TYPE("(sasa(qq)a(yqq))"),
data, data_size, TRUE, NULL, NULL);
g_variant_ref_sink (v);
strings = data; stringsv = g_variant_get_child_value (v, 1);
data = data + len; strings = g_variant_get_strv (stringsv, &n_strings);
tree = data; g_variant_unref (stringsv);
while (tree < data_end) attrsv = g_variant_get_child_value (v, 2);
attrs = g_variant_get_fixed_array (attrsv, &n_attrs, sizeof (*attrs));
g_variant_unref (attrsv);
nodesv = g_variant_get_child_value (v, 3);
nodes = g_variant_get_fixed_array (nodesv, &n_nodes, sizeof (*nodes));
g_variant_unref (nodesv);
for (i = 0; i < n_nodes; i++)
{ {
const RecordDataNode *node = &nodes[i];
const char *node_data;
gboolean res; gboolean res;
type = demarshal_uint32 (&tree);
switch (type) node_data = strings[GUINT16_FROM_LE(node->data)];
switch (node->type)
{ {
case RECORD_TYPE_ELEMENT: case RECORD_TYPE_ELEMENT:
res = replay_start_element (context, &tree, strings, error); res = replay_start_element (context, node_data, GUINT16_FROM_LE(node->attributes),
strings, attrs, error);
break; break;
case RECORD_TYPE_END_ELEMENT: case RECORD_TYPE_END_ELEMENT:
res = replay_end_element (context, &tree, strings, error); res = replay_end_element (context, error);
break; break;
case RECORD_TYPE_TEXT: case RECORD_TYPE_TEXT:
res = replay_text (context, &tree, strings, error); res = replay_text (context, node_data, error);
break; break;
case RECORD_TYPE_PASSTHROUGH: case RECORD_TYPE_PASSTHROUGH:
res = replay_passthrough (context, &tree, strings, error); res = replay_passthrough (context, node_data, error);
break; break;
default: default:
g_assert_not_reached (); g_assert_not_reached ();
} }
if (!res) if (!res)
{
g_variant_unref (v);
return FALSE; return FALSE;
} }
}
g_variant_unref (v);
return TRUE; return TRUE;
} }