Finish markup parsing rewrite to avoid mallocs.

For that matter, use elements as a filter for elements we care about, and
let the hook implement whatever it needs without duplicating everything.

The resulting algorithm is still O(n²) where n is the number of filtered
elements (3 at most right now), which isn't bad if we don't need to get
too many elements, but at least it's not quadratic in the number of
attributes anymore.

Speedup improvements could be done using gperf btw.

Signed-off-by: Pierre Habouzit <madcoder@debian.org>
This commit is contained in:
Pierre Habouzit 2008-06-22 17:45:45 +02:00
parent 37d69b8dcc
commit b4b381e947
6 changed files with 137 additions and 108 deletions

View file

@ -759,6 +759,17 @@ client_updatesizehints(client_t *c)
return size;
}
/** Markup parsing hook.
* For now only substitute <title />
*/
static void
client_markup_on_elem(markup_parser_data_t *p, const char *elem,
const char **names, const char **values)
{
assert (!strcmp(elem, "title"));
buffer_add_xmlescaped(&p->text, p->priv);
}
/** Parse a markup string which contains special markup sequence relative to a
* client, i.e. its title, etc.
* \param c The client concerned by the markup string.
@ -768,17 +779,18 @@ client_updatesizehints(client_t *c)
char *
client_markup_parse(client_t *c, const char *str, ssize_t len)
{
const char *elements[] = { "title", NULL };
const char *elements_sub[] = { c->name , NULL };
markup_parser_data_t p;
static char const * const elements[] = { "title", NULL };
markup_parser_data_t p = {
.elements = elements,
.priv = c->name,
.on_element = client_markup_on_elem,
};
char *ret;
markup_parser_data_init(&p, elements, elements_sub, countof(elements));
markup_parser_data_init(&p);
if(markup_parse(&p, str, len))
{
ret = buffer_detach(&p.text);
}
else
ret = a_strdup(str);

View file

@ -216,15 +216,62 @@ typedef struct
} shadow;
} draw_parser_data_t;
static void
draw_markup_on_event(markup_parser_data_t *p, const char *elem,
const char **names, const char **values)
{
draw_parser_data_t *data = p->priv;
/* hack: markup.c validates tags so we can avoid strcmps here */
switch (*elem) {
case 'b': /* bg */
for (; *names; names++, values++)
{
if(!a_strcmp(*names, "color"))
data->has_bg_color = xcolor_new(data->connection, data->phys_screen,
*values, &data->bg_color);
else if(!a_strcmp(*names, "image"))
data->bg_image = draw_image_new(*values);
}
break;
case 't': /* text */
for (; *names; names++, values++)
{
if(!a_strcmp(*names, "align"))
data->align = draw_align_get_from_str(*values);
else if(!a_strcmp(*names, "shadow"))
xcolor_new(data->connection, data->phys_screen, *values,
&data->shadow.color);
else if(!a_strcmp(*names, "shadow_offset"))
data->shadow.offset = atoi(*values);
}
break;
case 'm': /* margin */
for (; *names; names++, values++)
{
if(!a_strcmp(*names, "left"))
data->margin.left = atoi(*values);
else if(!a_strcmp(*names, "right"))
data->margin.right = atoi(*values);
}
break;
}
}
static bool
draw_text_markup_expand(draw_parser_data_t *data,
const char *str, ssize_t slen)
{
const char *elements[] = { "bg", "text", "margin", NULL };
markup_parser_data_t p;
int i;
static char const * const elements[] = { "bg", "text", "margin", NULL };
markup_parser_data_t p = {
.elements = elements,
.priv = data,
.on_element = &draw_markup_on_event,
};
markup_parser_data_init(&p, elements, NULL, countof(elements));
markup_parser_data_init(&p);
if(!markup_parse(&p, str, slen))
{
@ -232,34 +279,6 @@ draw_text_markup_expand(draw_parser_data_t *data,
return false;
}
/* bg */
if(p.attribute_names[0])
for(i = 0; p.attribute_names[0][i]; i++)
if(!a_strcmp(p.attribute_names[0][i], "color"))
data->has_bg_color = xcolor_new(data->connection, data->phys_screen,
p.attribute_values[0][i], &data->bg_color);
else if(!a_strcmp(p.attribute_names[0][i], "image"))
data->bg_image = draw_image_new(p.attribute_values[0][i]);
/* text */
if(p.attribute_names[1])
for(i = 0; p.attribute_names[1][i]; i++)
if(!a_strcmp(p.attribute_names[1][i], "align"))
data->align = draw_align_get_from_str(p.attribute_values[1][i]);
else if(!a_strcmp(p.attribute_names[1][i], "shadow"))
xcolor_new(data->connection, data->phys_screen,
p.attribute_values[1][i], &data->shadow.color);
else if(!a_strcmp(p.attribute_names[1][i], "shadow_offset"))
data->shadow.offset = atoi(p.attribute_values[1][i]);
/* margin */
if(p.attribute_names[2])
for(i = 0; p.attribute_names[2][i]; i++)
if(!a_strcmp(p.attribute_names[2][i], "left"))
data->margin.left = atoi(p.attribute_values[2][i]);
else if(!a_strcmp(p.attribute_names[2][i], "right"))
data->margin.right = atoi(p.attribute_values[2][i]);
/* stole text */
data->text = p.text;
buffer_init(&p.text);

View file

@ -47,26 +47,12 @@ markup_parse_start_element(GMarkupParseContext *context __attribute__ ((unused))
GError **error __attribute__ ((unused)))
{
markup_parser_data_t *p = (markup_parser_data_t *) user_data;
int i, j;
int i;
for(i = 0; p->elements[i]; i++)
if(!a_strcmp(element_name, p->elements[i]))
{
for(j = 0; attribute_names[j]; j++);
p->attribute_names[i] = p_new(char *, ++j);
p->attribute_values[i] = p_new(char *, j);
for(j = 0; attribute_names[j]; j++)
{
p->attribute_names[i][j] = a_strdup(attribute_names[j]);
p->attribute_values[i][j] = a_strdup(attribute_values[j]);
}
if(p->elements_sub && p->elements_sub[i])
{
buffer_add_xmlescaped(&p->text, p->elements_sub[i]);
}
if(!a_strcmp(element_name, p->elements[i])) {
(*p->on_element)(p, element_name, attribute_names,
attribute_values);
return;
}
@ -134,20 +120,10 @@ markup_parse_text(GMarkupParseContext *context __attribute__ ((unused)),
/** Create a markup_parser_data_t structure with elements list.
* \param a pointer to an allocated markup_parser_data_t which must be wiped
* with markup_parser_data_wipe()
* \param elements an array of elements to look for, NULL terminated
* \param elements_sub an optional array of values to substitude to elements, NULL
* terminated, or NULL if not needed
* \param nelem number of elements in the array (without NULL)
*/
void
markup_parser_data_init(markup_parser_data_t *p, const char **elements,
const char **elements_sub, ssize_t nelem)
void markup_parser_data_init(markup_parser_data_t *p)
{
buffer_init(&p->text);
p->elements = elements;
p->elements_sub = elements_sub;
p->attribute_names = p_new(char **, nelem);
p->attribute_values = p_new(char **, nelem);
}
/** Wipe a markup_parser_data_t initialized with markup_parser_data_init.
@ -156,23 +132,6 @@ markup_parser_data_init(markup_parser_data_t *p, const char **elements,
void
markup_parser_data_wipe(markup_parser_data_t *p)
{
int i, j;
for(i = 0; p->elements[i]; i++)
if(p->attribute_names[i])
{
for(j = 0; p->attribute_names[i][j]; j++)
{
p_delete(&p->attribute_names[i][j]);
p_delete(&p->attribute_values[i][j]);
}
p_delete(&p->attribute_names[i]);
p_delete(&p->attribute_values[i]);
}
p_delete(&p->attribute_names);
p_delete(&p->attribute_values);
buffer_wipe(&p->text);
}

View file

@ -24,16 +24,20 @@
#include "common/buffer.h"
typedef struct
typedef struct markup_parser_data_t markup_parser_data_t;
typedef void (markup_on_elem_f)(markup_parser_data_t *, const char *,
const char **, const char **);
struct markup_parser_data_t
{
buffer_t text;
const char **elements;
const char **elements_sub;
char ***attribute_names;
char ***attribute_values;
} markup_parser_data_t;
const char * const *elements;
markup_on_elem_f *on_element;
void *priv;
};
void markup_parser_data_init(markup_parser_data_t *, const char **, const char **, ssize_t);
void markup_parser_data_init(markup_parser_data_t *);
void markup_parser_data_wipe(markup_parser_data_t *);
bool markup_parse(markup_parser_data_t *data, const char *, ssize_t);

View file

@ -54,15 +54,27 @@ typedef struct
} taglist_data_t;
static void
tag_markup_on_elem(markup_parser_data_t *p, const char *elem,
const char **names, const char **values)
{
assert (!strcmp(elem, "title"));
buffer_add_xmlescaped(&p->text, p->priv);
}
static char *
tag_markup_parse(tag_t *t, const char *str, ssize_t len)
{
const char *elements[] = { "title", NULL };
const char *elements_sub[] = { t->name , NULL };
markup_parser_data_t p;
static char const * const elements[] = { "title", NULL };
markup_parser_data_t p = {
.elements = elements,
.on_element = &tag_markup_on_elem,
.priv = t->name,
};
char *ret;
markup_parser_data_init(&p, elements, elements_sub, countof(elements));
markup_parser_data_init(&p);
if(markup_parse(&p, str, len))
ret = buffer_detach(&p.text);

View file

@ -63,6 +63,31 @@ tasklist_isvisible(client_t *c, int screen, showclient_t show)
return false;
}
struct tasklist_hook_data {
draw_context_t *ctx;
area_t *area;
};
static void
tasklist_markup_on_elem(markup_parser_data_t *p, const char *elem,
const char **names, const char **values)
{
struct tasklist_hook_data *data = p->priv;
draw_context_t *ctx = data->ctx;
assert (!strcmp(elem, "bg"));
for (; *names; names++, values++) {
if(!a_strcmp(*names, "color"))
{
xcolor_t bg_color;
xcolor_new(ctx->connection, ctx->phys_screen, *values, &bg_color);
draw_rectangle(ctx, *data->area, 1.0, true, bg_color);
break;
}
}
}
static int
tasklist_draw(draw_context_t *ctx, int screen,
widget_node_t *w,
@ -72,11 +97,8 @@ tasklist_draw(draw_context_t *ctx, int screen,
tasklist_data_t *d = w->widget->data;
area_t area;
char *text;
int n = 0, i = 0, box_width = 0, icon_width = 0, box_width_rest = 0, j = 0;
int n = 0, i = 0, box_width = 0, icon_width = 0, box_width_rest = 0;
netwm_icon_t *icon;
markup_parser_data_t p;
const char *elements[] = { "bg", NULL };
xcolor_t bg_color;
draw_image_t *image;
if(used >= ctx->width)
@ -114,6 +136,14 @@ tasklist_draw(draw_context_t *ctx, int screen,
if(d->show_icons)
{
static char const * const elements[] = { "bg", NULL };
struct tasklist_hook_data data = { .ctx = ctx, .area = &area };
markup_parser_data_t p = {
.elements = elements,
.on_element = &tasklist_markup_on_elem,
.priv = &data,
};
/* draw a background for icons */
area.x = w->area.x + box_width * i;
area.y = w->area.y;
@ -122,15 +152,8 @@ tasklist_draw(draw_context_t *ctx, int screen,
/* Actually look for the proper background color, since
* otherwise the background statusbar color is used instead */
markup_parser_data_init(&p, elements, NULL, countof(elements));
if(markup_parse(&p, text, a_strlen(text)) && p.attribute_names[0])
for(j = 0; p.attribute_names[0][j]; j++)
if(!a_strcmp(p.attribute_names[0][j], "color"))
{
xcolor_new(ctx->connection, ctx->phys_screen, p.attribute_values[0][j], &bg_color);
draw_rectangle(ctx, area, 1.0, true, bg_color);
break;
}
markup_parser_data_init(&p);
markup_parse(&p, text, a_strlen(text));
markup_parser_data_wipe(&p);
if((image = draw_image_new(c->icon_path)))