summaryrefslogtreecommitdiff
path: root/src/stylesheet.c
diff options
context:
space:
mode:
authorJohn Mark Bell <jmb@netsurf-browser.org>2008-11-27 12:14:07 +0000
committerJohn Mark Bell <jmb@netsurf-browser.org>2008-11-27 12:14:07 +0000
commit7adaf92154adc7f3455769ca197f906e3d4cddaa (patch)
treeae172720c8e44a35b87b5506602768db19ec538f /src/stylesheet.c
parent79c0f3bc5c1581a6f2f6568a50e3b4f64b452541 (diff)
downloadlibcss-7adaf92154adc7f3455769ca197f906e3d4cddaa.tar.gz
libcss-7adaf92154adc7f3455769ca197f906e3d4cddaa.tar.bz2
css_string is now the same as a parserutils_dict_entry. This allows us to use dict entries directly as strings.
iChange the way in which selectors are represented. This significantly reduces memory requirements -- reducing the approximate usage count (excludes the string dictionary, which is about 360k) of allzengarden.css from 4,535,400 bytes to 2,414,312 bytes on a 64bit platform. The string dictionary is now created and owned by the stylesheet object. The parser is just given access to this so that it can store strings in it. svn path=/trunk/libcss/; revision=5809
Diffstat (limited to 'src/stylesheet.c')
-rw-r--r--src/stylesheet.c253
1 files changed, 198 insertions, 55 deletions
diff --git a/src/stylesheet.c b/src/stylesheet.c
index 0b1e2cd..7e634a9 100644
--- a/src/stylesheet.c
+++ b/src/stylesheet.c
@@ -10,6 +10,7 @@
#include "stylesheet.h"
#include "bytecode/bytecode.h"
#include "parse/css21.h"
+#include "utils/parserutilserror.h"
#include "utils/utils.h"
/**
@@ -36,6 +37,7 @@ css_error css_stylesheet_create(css_language_level level,
css_import_handler import_callback, void *import_pw,
css_alloc alloc, void *alloc_pw, css_stylesheet **stylesheet)
{
+ parserutils_error perror;
css_error error;
css_stylesheet *sheet;
size_t len;
@@ -49,10 +51,18 @@ css_error css_stylesheet_create(css_language_level level,
memset(sheet, 0, sizeof(css_stylesheet));
+ perror = parserutils_dict_create((parserutils_alloc) alloc, alloc_pw,
+ &sheet->dictionary);
+ if (perror != PARSERUTILS_OK) {
+ alloc(sheet, 0, alloc_pw);
+ return css_error_from_parserutils_error(perror);
+ }
+
error = css_parser_create(charset,
charset ? CSS_CHARSET_DICTATED : CSS_CHARSET_DEFAULT,
- alloc, alloc_pw, &sheet->parser);
+ sheet->dictionary, alloc, alloc_pw, &sheet->parser);
if (error != CSS_OK) {
+ parserutils_dict_destroy(sheet->dictionary);
alloc(sheet, 0, alloc_pw);
return error;
}
@@ -60,6 +70,7 @@ css_error css_stylesheet_create(css_language_level level,
/* We only support CSS 2.1 */
if (level != CSS_LEVEL_21) {
css_parser_destroy(sheet->parser);
+ parserutils_dict_destroy(sheet->dictionary);
alloc(sheet, 0, alloc_pw);
return CSS_INVALID; /** \todo better error */
}
@@ -69,6 +80,7 @@ css_error css_stylesheet_create(css_language_level level,
&sheet->parser_frontend);
if (error != CSS_OK) {
css_parser_destroy(sheet->parser);
+ parserutils_dict_destroy(sheet->dictionary);
alloc(sheet, 0, alloc_pw);
return error;
}
@@ -80,6 +92,7 @@ css_error css_stylesheet_create(css_language_level level,
if (sheet->url == NULL) {
css_css21_destroy(sheet->parser_frontend);
css_parser_destroy(sheet->parser);
+ parserutils_dict_destroy(sheet->dictionary);
alloc(sheet, 0, alloc_pw);
return CSS_NOMEM;
}
@@ -89,9 +102,10 @@ css_error css_stylesheet_create(css_language_level level,
len = strlen(title) + 1;
sheet->title = alloc(NULL, len, alloc_pw);
if (sheet->title == NULL) {
+ alloc(sheet->url, 0, alloc_pw);
css_css21_destroy(sheet->parser_frontend);
css_parser_destroy(sheet->parser);
- alloc(sheet->url, 0, alloc_pw);
+ parserutils_dict_destroy(sheet->dictionary);
alloc(sheet, 0, alloc_pw);
return CSS_NOMEM;
}
@@ -123,6 +137,8 @@ css_error css_stylesheet_destroy(css_stylesheet *sheet)
if (sheet == NULL)
return CSS_BADPARM;
+ parserutils_dict_destroy(sheet->dictionary);
+
if (sheet->title != NULL)
sheet->alloc(sheet->title, 0, sheet->pw);
@@ -349,7 +365,9 @@ css_error css_stylesheet_selector_create(css_stylesheet *sheet,
css_selector_type type, const css_string *name,
const css_string *value, css_selector **selector)
{
+ const css_string *iname, *ivalue;
css_selector *sel;
+ parserutils_error perror;
if (sheet == NULL || name == NULL || selector == NULL)
return CSS_BADPARM;
@@ -360,14 +378,33 @@ css_error css_stylesheet_selector_create(css_stylesheet *sheet,
memset(sel, 0, sizeof(css_selector));
- sel->type = type;
- sel->data.name = *name;
+ sel->data.type = type;
+
+ /** \todo Given that this information is already guaranteed to be in
+ * the dictionary, it would be more efficient to pass a pointer to the
+ * dictionary entry around rather than re-discovering it here. The same
+ * applies for value, and in css_stylesheet_selector_detail_create() */
+ perror = parserutils_dict_insert(sheet->dictionary, name->data,
+ name->len, &iname);
+ if (perror != PARSERUTILS_OK) {
+ sheet->alloc(sel, 0, sheet->pw);
+ return css_error_from_parserutils_error(perror);
+ }
+ sel->data.name = iname;
+
if (value != NULL) {
- sel->data.value = *value;
+ perror = parserutils_dict_insert(sheet->dictionary,
+ value->data, value->len, &ivalue);
+ if (perror != PARSERUTILS_OK) {
+ sheet->alloc(sel, 0, sheet->pw);
+ return css_error_from_parserutils_error(perror);
+ }
+ sel->data.value = ivalue;
}
+
/** \todo specificity */
sel->specificity = 0;
- sel->combinator_type = CSS_COMBINATOR_NONE;
+ sel->data.comb = CSS_COMBINATOR_NONE;
*selector = sel;
@@ -395,32 +432,120 @@ css_error css_stylesheet_selector_destroy(css_stylesheet *sheet,
}
/**
+ * Create a selector detail
+ *
+ * \param sheet The stylesheet context
+ * \param type The type of selector to create
+ * \param name Name of selector
+ * \param value Value of selector, or NULL
+ * \param detail Pointer to location to receive detail object
+ * \return CSS_OK on success,
+ * CSS_BADPARM on bad parameters,
+ * CSS_NOMEM on memory exhaustion
+ */
+css_error css_stylesheet_selector_detail_create(css_stylesheet *sheet,
+ css_selector_type type, const css_string *name,
+ const css_string *value, css_selector_detail **detail)
+{
+ parserutils_error perror;
+ const css_string *iname, *ivalue;
+ css_selector_detail *det;
+
+ if (sheet == NULL || name == NULL || detail == NULL)
+ return CSS_BADPARM;
+
+ det = sheet->alloc(NULL, sizeof(css_selector_detail), sheet->pw);
+ if (det == NULL)
+ return CSS_NOMEM;
+
+ memset(det, 0, sizeof(css_selector_detail));
+
+ det->type = type;
+
+ perror = parserutils_dict_insert(sheet->dictionary, name->data,
+ name->len, &iname);
+ if (perror != PARSERUTILS_OK) {
+ sheet->alloc(det, 0, sheet->pw);
+ return css_error_from_parserutils_error(perror);
+ }
+ det->name = iname;
+
+ if (value != NULL) {
+ perror = parserutils_dict_insert(sheet->dictionary,
+ value->data, value->len, &ivalue);
+ if (perror != PARSERUTILS_OK) {
+ sheet->alloc(det, 0, sheet->pw);
+ return css_error_from_parserutils_error(perror);
+ }
+ det->value = ivalue;
+ }
+
+ *detail = det;
+
+ return CSS_OK;
+}
+
+/**
+ * Destroy a selector detail
+ *
+ * \param sheet The stylesheet context
+ * \param detail The detail to destroy
+ * \return CSS_OK on success, appropriate error otherwise
+ */
+css_error css_stylesheet_selector_detail_destroy(css_stylesheet *sheet,
+ css_selector_detail *detail)
+{
+ if (sheet == NULL || detail == NULL)
+ return CSS_BADPARM;
+
+ sheet->alloc(detail, 0, sheet->pw);
+
+ return CSS_OK;
+}
+
+
+/**
* Append a selector to the specifics chain of another selector
*
* \param sheet The stylesheet context
- * \param parent The parent selector
- * \param specific The selector to append
+ * \param parent Pointer to pointer to the parent selector (updated on exit)
+ * \param specific The selector to append (destroyed)
* \return CSS_OK on success, appropriate error otherwise.
*/
css_error css_stylesheet_selector_append_specific(css_stylesheet *sheet,
- css_selector *parent, css_selector *specific)
+ css_selector **parent, css_selector_detail *detail)
{
- css_selector *s;
+ css_selector *temp;
+ css_selector_detail *d;
+ size_t num_details = 0;
- if (sheet == NULL || parent == NULL || specific == NULL)
+ if (sheet == NULL || parent == NULL ||
+ *parent == NULL || detail == NULL)
return CSS_BADPARM;
- /** \todo this may want optimising */
- for (s = parent->specifics; s != NULL && s->next != NULL; s = s->next)
- /* do nothing */;
- if (s == NULL) {
- specific->prev = specific->next = NULL;
- parent->specifics = specific;
- } else {
- s->next = specific;
- specific->prev = s;
- specific->next = NULL;
- }
+ /** \todo this may want optimising -- counting blocks is O(n)
+ * In practice, however, n isn't likely to be large, so may be fine
+ */
+
+ /* Count current number of detail blocks */
+ for (d = &(*parent)->data; d->next != 0; d++)
+ num_details++;
+
+ /* Grow selector by one detail block */
+ temp = sheet->alloc((*parent), sizeof(css_selector) +
+ (num_details + 1) * sizeof(css_selector_detail),
+ sheet->pw);
+ if (temp == NULL)
+ return CSS_NOMEM;
+
+ /* Copy detail into empty block */
+ (&temp->data)[num_details + 1] = *detail;
+ /* Flag that there's another block */
+ (&temp->data)[num_details].next = 1;
+
+ css_stylesheet_selector_detail_destroy(sheet, detail);
+
+ (*parent) = temp;
return CSS_OK;
}
@@ -451,7 +576,7 @@ css_error css_stylesheet_selector_combine(css_stylesheet *sheet,
return CSS_INVALID;
b->combinator = a;
- b->combinator_type = type;
+ b->data.comb = type;
return CSS_OK;
}
@@ -666,7 +791,9 @@ static void css_stylesheet_dump_selector_list(css_selector *list, FILE *target,
size_t *size);
static void css_stylesheet_dump_selector(css_selector *selector, FILE *target,
size_t *size);
-static void css_stylesheet_dump_string(css_string *string, FILE *target);
+static void css_stylesheet_dump_selector_detail(css_selector_detail *detail,
+ FILE *target, size_t *size);
+static void css_stylesheet_dump_string(const css_string *string, FILE *target);
/**
* Dump a stylesheet
@@ -757,7 +884,7 @@ void css_stylesheet_dump_selector_list(css_selector *list, FILE *target,
size);
}
- switch (list->combinator_type) {
+ switch (list->data.comb) {
case CSS_COMBINATOR_NONE:
break;
case CSS_COMBINATOR_ANCESTOR:
@@ -784,71 +911,87 @@ void css_stylesheet_dump_selector_list(css_selector *list, FILE *target,
void css_stylesheet_dump_selector(css_selector *selector, FILE *target,
size_t *size)
{
- css_selector *s;
+ css_selector_detail *d = &selector->data;
+
+ *size += sizeof(css_selector) - sizeof(css_selector_detail);
+
+ while (true) {
+ css_stylesheet_dump_selector_detail(d, target, size);
- *size += sizeof(css_selector);
+ if (d->next == 0)
+ break;
- switch (selector->type) {
+ d++;
+ }
+}
+
+/**
+ * Dump a CSS selector detail
+ *
+ * \param detail The detail to dump
+ * \param target The file handle to output to
+ * \param size Pointer to current size of sheet, updated on exit
+ */
+void css_stylesheet_dump_selector_detail(css_selector_detail *detail,
+ FILE *target, size_t *size)
+{
+
+ *size += sizeof(css_selector_detail);
+
+ switch (detail->type) {
case CSS_SELECTOR_ELEMENT:
- if (selector->data.name.len == 1 &&
- selector->data.name.ptr[0] == '*' &&
- selector->specifics == NULL) {
- css_stylesheet_dump_string(&selector->data.name,
- target);
- } else if (selector->data.name.len != 1 ||
- selector->data.name.ptr[0] != '*') {
- css_stylesheet_dump_string(&selector->data.name,
- target);
+ if (detail->name->len == 1 && detail->name->data[0] == '*' &&
+ detail->next == 0) {
+ css_stylesheet_dump_string(detail->name, target);
+ } else if (detail->name->len != 1 ||
+ detail->name->data[0] != '*') {
+ css_stylesheet_dump_string(detail->name, target);
}
break;
case CSS_SELECTOR_CLASS:
fprintf(target, ".");
- css_stylesheet_dump_string(&selector->data.name, target);
+ css_stylesheet_dump_string(detail->name, target);
break;
case CSS_SELECTOR_ID:
fprintf(target, "#");
- css_stylesheet_dump_string(&selector->data.name, target);
+ css_stylesheet_dump_string(detail->name, target);
break;
case CSS_SELECTOR_PSEUDO:
fprintf(target, ":");
- css_stylesheet_dump_string(&selector->data.name, target);
- if (selector->data.value.len > 0) {
+ css_stylesheet_dump_string(detail->name, target);
+ if (detail->value != NULL) {
fprintf(target, "(");
- css_stylesheet_dump_string(&selector->data.value,
- target);
+ css_stylesheet_dump_string(detail->value, target);
fprintf(target, ")");
}
break;
case CSS_SELECTOR_ATTRIBUTE:
fprintf(target, "[");
- css_stylesheet_dump_string(&selector->data.name, target);
+ css_stylesheet_dump_string(detail->name, target);
fprintf(target, "]");
break;
case CSS_SELECTOR_ATTRIBUTE_EQUAL:
fprintf(target, "[");
- css_stylesheet_dump_string(&selector->data.name, target);
+ css_stylesheet_dump_string(detail->name, target);
fprintf(target, "=\"");
- css_stylesheet_dump_string(&selector->data.value, target);
+ css_stylesheet_dump_string(detail->value, target);
fprintf(target, "\"]");
break;
case CSS_SELECTOR_ATTRIBUTE_DASHMATCH:
fprintf(target, "[");
- css_stylesheet_dump_string(&selector->data.name, target);
+ css_stylesheet_dump_string(detail->name, target);
fprintf(target, "|=\"");
- css_stylesheet_dump_string(&selector->data.value, target);
+ css_stylesheet_dump_string(detail->value, target);
fprintf(target, "\"]");
break;
case CSS_SELECTOR_ATTRIBUTE_INCLUDES:
fprintf(target, "[");
- css_stylesheet_dump_string(&selector->data.name, target);
+ css_stylesheet_dump_string(detail->name, target);
fprintf(target, "~=\"");
- css_stylesheet_dump_string(&selector->data.value, target);
+ css_stylesheet_dump_string(detail->value, target);
fprintf(target, "\"]");
break;
}
-
- for (s = selector->specifics; s != NULL; s = s->next)
- css_stylesheet_dump_selector(s, target, size);
}
/**
@@ -857,8 +1000,8 @@ void css_stylesheet_dump_selector(css_selector *selector, FILE *target,
* \param string The string to dump
* \param target The file handle to output to
*/
-void css_stylesheet_dump_string(css_string *string, FILE *target)
+void css_stylesheet_dump_string(const css_string *string, FILE *target)
{
- fprintf(target, "%.*s", (int) string->len, string->ptr);
+ fprintf(target, "%.*s", (int) string->len, string->data);
}