Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Blame | Last modification | View Log | Download | RSS feed

  1. /*
  2.  * This file is part of LibCSS
  3.  * Licensed under the MIT License,
  4.  *                http://www.opensource.org/licenses/mit-license.php
  5.  * Copyright 2009 John-Mark Bell <jmb@netsurf-browser.org>
  6.  */
  7.  
  8. #include <stdio.h>
  9. #include <string.h>
  10.  
  11. #include "stylesheet.h"
  12. #include "select/hash.h"
  13. #include "utils/utils.h"
  14.  
  15. typedef struct hash_entry {
  16.         const css_selector *sel;
  17.         struct hash_entry *next;
  18. } hash_entry;
  19.  
  20. typedef struct hash_t {
  21. #define DEFAULT_SLOTS (1<<6)
  22.         size_t n_slots;
  23.  
  24.         hash_entry *slots;
  25. } hash_t;
  26.  
  27. struct css_selector_hash {
  28.         hash_t elements;
  29.  
  30.         hash_t classes;
  31.  
  32.         hash_t ids;
  33.  
  34.         hash_entry universal;
  35.  
  36.         size_t hash_size;
  37.  
  38.         css_allocator_fn alloc;
  39.         void *pw;
  40. };
  41.  
  42. static hash_entry empty_slot;
  43.  
  44. static inline uint32_t _hash_name(lwc_string *name);
  45. static inline lwc_string *_class_name(const css_selector *selector);
  46. static inline lwc_string *_id_name(const css_selector *selector);
  47. static css_error _insert_into_chain(css_selector_hash *ctx, hash_entry *head,
  48.                 const css_selector *selector);
  49. static css_error _remove_from_chain(css_selector_hash *ctx, hash_entry *head,
  50.                 const css_selector *selector);
  51.  
  52. static css_error _iterate_elements(const css_selector **current,
  53.                 const css_selector ***next);
  54. static css_error _iterate_classes(const css_selector **current,
  55.                 const css_selector ***next);
  56. static css_error _iterate_ids(const css_selector **current,
  57.                 const css_selector ***next);
  58. static css_error _iterate_universal(const css_selector **current,
  59.                 const css_selector ***next);
  60.  
  61. /**
  62.  * Create a hash
  63.  *
  64.  * \param alloc  Memory (de)allocation function
  65.  * \param pw     Pointer to client-specific private data
  66.  * \param hash   Pointer to location to receive result
  67.  * \return CSS_OK on success, appropriate error otherwise
  68.  */
  69. css_error css__selector_hash_create(css_allocator_fn alloc, void *pw,
  70.                 css_selector_hash **hash)
  71. {
  72.         css_selector_hash *h;
  73.  
  74.         if (alloc == NULL || hash == NULL)
  75.                 return CSS_BADPARM;
  76.  
  77.         h = alloc(0, sizeof(css_selector_hash), pw);
  78.         if (h == NULL)
  79.                 return CSS_NOMEM;
  80.  
  81.         /* Element hash */
  82.         h->elements.slots = alloc(0, DEFAULT_SLOTS * sizeof(hash_entry), pw);
  83.         if (h->elements.slots == NULL) {
  84.                 alloc(h, 0, pw);
  85.                 return CSS_NOMEM;
  86.         }
  87.         memset(h->elements.slots, 0, DEFAULT_SLOTS * sizeof(hash_entry));
  88.         h->elements.n_slots = DEFAULT_SLOTS;
  89.  
  90.         /* Class hash */
  91.         h->classes.slots = alloc(0, DEFAULT_SLOTS * sizeof(hash_entry), pw);
  92.         if (h->classes.slots == NULL) {
  93.                 alloc(h->elements.slots, 0, pw);
  94.                 alloc(h, 0, pw);
  95.                 return CSS_NOMEM;
  96.         }
  97.         memset(h->classes.slots, 0, DEFAULT_SLOTS * sizeof(hash_entry));
  98.         h->classes.n_slots = DEFAULT_SLOTS;
  99.  
  100.         /* ID hash */
  101.         h->ids.slots = alloc(0, DEFAULT_SLOTS * sizeof(hash_entry), pw);
  102.         if (h->ids.slots == NULL) {
  103.                 alloc(h->classes.slots, 0, pw);
  104.                 alloc(h->elements.slots, 0, pw);
  105.                 alloc(h, 0, pw);
  106.                 return CSS_NOMEM;
  107.         }
  108.         memset(h->ids.slots, 0, DEFAULT_SLOTS * sizeof(hash_entry));
  109.         h->ids.n_slots = DEFAULT_SLOTS;
  110.  
  111.         /* Universal chain */
  112.         memset(&h->universal, 0, sizeof(hash_entry));
  113.  
  114.         h->hash_size = sizeof(css_selector_hash) +
  115.                         DEFAULT_SLOTS * sizeof(hash_entry) +
  116.                         DEFAULT_SLOTS * sizeof(hash_entry) +
  117.                         DEFAULT_SLOTS * sizeof(hash_entry);
  118.  
  119.         h->alloc = alloc;
  120.         h->pw = pw;
  121.  
  122.         *hash = h;
  123.  
  124.         return CSS_OK;
  125. }
  126.  
  127. /**
  128.  * Destroy a hash
  129.  *
  130.  * \param hash  The hash to destroy
  131.  * \return CSS_OK on success, appropriate error otherwise
  132.  */
  133. css_error css__selector_hash_destroy(css_selector_hash *hash)
  134. {
  135.         hash_entry *d, *e;
  136.         uint32_t i;
  137.  
  138.         if (hash == NULL)
  139.                 return CSS_BADPARM;
  140.  
  141.         /* Element hash */
  142.         for (i = 0; i < hash->elements.n_slots; i++) {
  143.                 for (d = hash->elements.slots[i].next; d != NULL; d = e) {
  144.                         e = d->next;
  145.  
  146.                         hash->alloc(d, 0, hash->pw);
  147.                 }
  148.         }
  149.         hash->alloc(hash->elements.slots, 0, hash->pw);
  150.  
  151.         /* Class hash */
  152.         for (i = 0; i < hash->classes.n_slots; i++) {
  153.                 for (d = hash->classes.slots[i].next; d != NULL; d = e) {
  154.                         e = d->next;
  155.  
  156.                         hash->alloc(d, 0, hash->pw);
  157.                 }
  158.         }
  159.         hash->alloc(hash->classes.slots, 0, hash->pw);
  160.  
  161.         /* ID hash */
  162.         for (i = 0; i < hash->ids.n_slots; i++) {
  163.                 for (d = hash->ids.slots[i].next; d != NULL; d = e) {
  164.                         e = d->next;
  165.  
  166.                         hash->alloc(d, 0, hash->pw);
  167.                 }
  168.         }
  169.         hash->alloc(hash->ids.slots, 0, hash->pw);
  170.  
  171.         /* Universal chain */
  172.         for (d = hash->universal.next; d != NULL; d = e) {
  173.                 e = d->next;
  174.  
  175.                 hash->alloc(d, 0, hash->pw);
  176.         }
  177.  
  178.         hash->alloc(hash, 0, hash->pw);
  179.  
  180.         return CSS_OK;
  181. }
  182.  
  183. /**
  184.  * Insert an item into a hash
  185.  *
  186.  * \param hash      The hash to insert into
  187.  * \param selector  Pointer to selector
  188.  * \return CSS_OK on success, appropriate error otherwise
  189.  */
  190. css_error css__selector_hash_insert(css_selector_hash *hash,
  191.                 const css_selector *selector)
  192. {
  193.         uint32_t index, mask;
  194.         lwc_string *name;
  195.         css_error error;
  196.  
  197.         if (hash == NULL || selector == NULL)
  198.                 return CSS_BADPARM;
  199.  
  200.         /* Work out which hash to insert into */
  201.         if ((name = _id_name(selector)) != NULL) {
  202.                 /* Named ID */
  203.                 mask = hash->ids.n_slots - 1;
  204.                 index = _hash_name(name) & mask;
  205.  
  206.                 error = _insert_into_chain(hash, &hash->ids.slots[index],
  207.                                 selector);
  208.         } else if ((name = _class_name(selector)) != NULL) {
  209.                 /* Named class */
  210.                 mask = hash->classes.n_slots - 1;
  211.                 index = _hash_name(name) & mask;
  212.  
  213.                 error = _insert_into_chain(hash, &hash->classes.slots[index],
  214.                                 selector);
  215.         } else if (lwc_string_length(selector->data.qname.name) != 1 ||
  216.                         lwc_string_data(selector->data.qname.name)[0] != '*') {
  217.                 /* Named element */
  218.                 mask = hash->elements.n_slots - 1;
  219.                 index = _hash_name(selector->data.qname.name) & mask;
  220.  
  221.                 error = _insert_into_chain(hash, &hash->elements.slots[index],
  222.                                 selector);
  223.         } else {
  224.                 /* Universal chain */
  225.                 error = _insert_into_chain(hash, &hash->universal, selector);
  226.         }
  227.  
  228.         return error;
  229. }
  230.  
  231. /**
  232.  * Remove an item from a hash
  233.  *
  234.  * \param hash      The hash to remove from
  235.  * \param selector  Pointer to selector
  236.  * \return CSS_OK on success, appropriate error otherwise
  237.  */
  238. css_error css__selector_hash_remove(css_selector_hash *hash,
  239.                 const css_selector *selector)
  240. {
  241.         uint32_t index, mask;
  242.         lwc_string *name;
  243.         css_error error;
  244.  
  245.         if (hash == NULL || selector == NULL)
  246.                 return CSS_BADPARM;
  247.  
  248.         /* Work out which hash to remove from */
  249.         if ((name = _id_name(selector)) != NULL) {
  250.                 /* Named ID */
  251.                 mask = hash->ids.n_slots - 1;
  252.                 index = _hash_name(name) & mask;
  253.  
  254.                 error = _remove_from_chain(hash, &hash->ids.slots[index],
  255.                                 selector);
  256.         } else if ((name = _class_name(selector)) != NULL) {
  257.                 /* Named class */
  258.                 mask = hash->classes.n_slots - 1;
  259.                 index = _hash_name(name) & mask;
  260.  
  261.                 error = _remove_from_chain(hash, &hash->classes.slots[index],
  262.                                 selector);
  263.         } else if (lwc_string_length(selector->data.qname.name) != 1 ||
  264.                         lwc_string_data(selector->data.qname.name)[0] != '*') {
  265.                 /* Named element */
  266.                 mask = hash->elements.n_slots - 1;
  267.                 index = _hash_name(selector->data.qname.name) & mask;
  268.  
  269.                 error = _remove_from_chain(hash, &hash->elements.slots[index],
  270.                                 selector);
  271.         } else {
  272.                 /* Universal chain */
  273.                 error = _remove_from_chain(hash, &hash->universal, selector);
  274.         }
  275.  
  276.         return error;
  277. }
  278.  
  279. /**
  280.  * Find the first selector that matches name
  281.  *
  282.  * \param hash      Hash to search
  283.  * \param qname     Qualified name to match
  284.  * \param iterator  Pointer to location to receive iterator function
  285.  * \param matched   Pointer to location to receive selector
  286.  * \return CSS_OK on success, appropriate error otherwise
  287.  *
  288.  * If nothing matches, CSS_OK will be returned and **matched == NULL
  289.  */
  290. css_error css__selector_hash_find(css_selector_hash *hash,
  291.                 css_qname *qname,
  292.                 css_selector_hash_iterator *iterator,
  293.                 const css_selector ***matched)
  294. {
  295.         uint32_t index, mask;
  296.         hash_entry *head;
  297.  
  298.         if (hash == NULL || qname == NULL || iterator == NULL || matched == NULL)
  299.                 return CSS_BADPARM;
  300.  
  301.         /* Find index */
  302.         mask = hash->elements.n_slots - 1;
  303.         index = _hash_name(qname->name) & mask;
  304.  
  305.         head = &hash->elements.slots[index];
  306.  
  307.         if (head->sel != NULL) {
  308.                 /* Search through chain for first match */
  309.                 while (head != NULL) {
  310.                         lwc_error lerror;
  311.                         bool match = false;
  312.  
  313.                         lerror = lwc_string_caseless_isequal(
  314.                                         qname->name, head->sel->data.qname.name,
  315.                                         &match);
  316.                         if (lerror != lwc_error_ok)
  317.                                 return css_error_from_lwc_error(lerror);
  318.  
  319.                         if (match)
  320.                                 break;
  321.  
  322.                         head = head->next;
  323.                 }
  324.  
  325.                 if (head == NULL)
  326.                         head = &empty_slot;
  327.         }
  328.  
  329.         (*iterator) = _iterate_elements;
  330.         (*matched) = (const css_selector **) head;
  331.  
  332.         return CSS_OK;
  333. }
  334.  
  335. /**
  336.  * Find the first selector that has a class that matches name
  337.  *
  338.  * \param hash      Hash to search
  339.  * \param name      Name to match
  340.  * \param iterator  Pointer to location to receive iterator function
  341.  * \param matched   Pointer to location to receive selector
  342.  * \return CSS_OK on success, appropriate error otherwise
  343.  *
  344.  * If nothing matches, CSS_OK will be returned and **matched == NULL
  345.  */
  346. css_error css__selector_hash_find_by_class(css_selector_hash *hash,
  347.                 lwc_string *name,
  348.                 css_selector_hash_iterator *iterator,
  349.                 const css_selector ***matched)
  350. {
  351.         uint32_t index, mask;
  352.         hash_entry *head;
  353.  
  354.         if (hash == NULL || name == NULL || iterator == NULL || matched == NULL)
  355.                 return CSS_BADPARM;
  356.  
  357.         /* Find index */
  358.         mask = hash->classes.n_slots - 1;
  359.         index = _hash_name(name) & mask;
  360.  
  361.         head = &hash->classes.slots[index];
  362.  
  363.         if (head->sel != NULL) {
  364.                 /* Search through chain for first match */
  365.                 while (head != NULL) {
  366.                         lwc_error lerror;
  367.                         lwc_string *n;
  368.                         bool match = false;
  369.  
  370.                         n = _class_name(head->sel);
  371.                         if (n != NULL) {
  372.                                 lerror = lwc_string_caseless_isequal(
  373.                                                 name, n, &match);
  374.                                 if (lerror != lwc_error_ok)
  375.                                         return css_error_from_lwc_error(lerror);
  376.  
  377.                                 if (match)
  378.                                         break;
  379.                         }
  380.  
  381.                         head = head->next;
  382.                 }
  383.  
  384.                 if (head == NULL)
  385.                         head = &empty_slot;
  386.         }
  387.  
  388.         (*iterator) = _iterate_classes;
  389.         (*matched) = (const css_selector **) head;
  390.  
  391.         return CSS_OK;
  392. }
  393.  
  394. /**
  395.  * Find the first selector that has an ID that matches name
  396.  *
  397.  * \param hash      Hash to search
  398.  * \param name      Name to match
  399.  * \param iterator  Pointer to location to receive iterator function
  400.  * \param matched   Pointer to location to receive selector
  401.  * \return CSS_OK on success, appropriate error otherwise
  402.  *
  403.  * If nothing matches, CSS_OK will be returned and **matched == NULL
  404.  */
  405. css_error css__selector_hash_find_by_id(css_selector_hash *hash,
  406.                 lwc_string *name,
  407.                 css_selector_hash_iterator *iterator,
  408.                 const css_selector ***matched)
  409. {
  410.         uint32_t index, mask;
  411.         hash_entry *head;
  412.  
  413.         if (hash == NULL || name == NULL || iterator == NULL || matched == NULL)
  414.                 return CSS_BADPARM;
  415.  
  416.         /* Find index */
  417.         mask = hash->ids.n_slots - 1;
  418.         index = _hash_name(name) & mask;
  419.  
  420.         head = &hash->ids.slots[index];
  421.  
  422.         if (head->sel != NULL) {
  423.                 /* Search through chain for first match */
  424.                 while (head != NULL) {
  425.                         lwc_error lerror;
  426.                         lwc_string *n;
  427.                         bool match = false;
  428.  
  429.                         n = _id_name(head->sel);
  430.                         if (n != NULL) {
  431.                                 lerror = lwc_string_caseless_isequal(
  432.                                                 name, n, &match);
  433.                                 if (lerror != lwc_error_ok)
  434.                                         return css_error_from_lwc_error(lerror);
  435.  
  436.                                 if (match)
  437.                                         break;
  438.                         }
  439.  
  440.                         head = head->next;
  441.                 }
  442.  
  443.                 if (head == NULL)
  444.                         head = &empty_slot;
  445.         }
  446.  
  447.         (*iterator) = _iterate_ids;
  448.         (*matched) = (const css_selector **) head;
  449.  
  450.         return CSS_OK;
  451. }
  452.  
  453. /**
  454.  * Find the first universal selector
  455.  *
  456.  * \param hash      Hash to search
  457.  * \param iterator  Pointer to location to receive iterator function
  458.  * \param matched   Pointer to location to receive selector
  459.  * \return CSS_OK on success, appropriate error otherwise
  460.  *
  461.  * If nothing matches, CSS_OK will be returned and **matched == NULL
  462.  */
  463. css_error css__selector_hash_find_universal(css_selector_hash *hash,
  464.                 css_selector_hash_iterator *iterator,
  465.                 const css_selector ***matched)
  466. {
  467.         if (hash == NULL || iterator == NULL || matched == NULL)
  468.                 return CSS_BADPARM;
  469.  
  470.         (*iterator) = _iterate_universal;
  471.         (*matched) = (const css_selector **) &hash->universal;
  472.  
  473.         return CSS_OK;
  474. }
  475.  
  476. /**
  477.  * Determine the memory-resident size of a hash
  478.  *
  479.  * \param hash  Hash to consider
  480.  * \param size  Pointer to location to receive byte count
  481.  * \return CSS_OK on success.
  482.  *
  483.  * \note The returned size will represent the size of the hash datastructures,
  484.  *       and will not include the size of the data stored in the hash.
  485.  */
  486. css_error css__selector_hash_size(css_selector_hash *hash, size_t *size)
  487. {
  488.         if (hash == NULL || size == NULL)
  489.                 return CSS_BADPARM;
  490.  
  491.         *size = hash->hash_size;
  492.  
  493.         return CSS_OK;
  494. }
  495.  
  496. /******************************************************************************
  497.  * Private functions                                                          *
  498.  ******************************************************************************/
  499.  
  500. /**
  501.  * Name hash function -- case-insensitive FNV.
  502.  *
  503.  * \param name  Name to hash
  504.  * \return hash value
  505.  */
  506. uint32_t _hash_name(lwc_string *name)
  507. {
  508.         uint32_t z = 0x811c9dc5;
  509.         const char *data = lwc_string_data(name);
  510.         const char *end = data + lwc_string_length(name);
  511.  
  512.         while (data != end) {
  513.                 const char c = *data++;
  514.  
  515.                 z *= 0x01000193;
  516.                 z ^= c & ~0x20;
  517.         }
  518.  
  519.         return z;
  520. }
  521.  
  522. /**
  523.  * Retrieve the first class name in a selector, or NULL if none
  524.  *
  525.  * \param selector  Selector to consider
  526.  * \return Pointer to class name, or NULL if none
  527.  */
  528. lwc_string *_class_name(const css_selector *selector)
  529. {
  530.         const css_selector_detail *detail = &selector->data;
  531.         lwc_string *name = NULL;
  532.  
  533.         do {
  534.                 /* Ignore :not(.class) */
  535.                 if (detail->type == CSS_SELECTOR_CLASS && detail->negate == 0) {
  536.                         name = detail->qname.name;
  537.                         break;
  538.                 }
  539.  
  540.                 if (detail->next)
  541.                         detail++;
  542.                 else
  543.                         detail = NULL;
  544.         } while (detail != NULL);
  545.  
  546.         return name;
  547. }
  548.  
  549. /**
  550.  * Retrieve the first ID name in a selector, or NULL if none
  551.  *
  552.  * \param selector  Selector to consider
  553.  * \return Pointer to ID name, or NULL if none
  554.  */
  555. lwc_string *_id_name(const css_selector *selector)
  556. {
  557.         const css_selector_detail *detail = &selector->data;
  558.         lwc_string *name = NULL;
  559.  
  560.         do {
  561.                 /* Ignore :not(#id) */
  562.                 if (detail->type == CSS_SELECTOR_ID && detail->negate == 0) {
  563.                         name = detail->qname.name;
  564.                         break;
  565.                 }
  566.  
  567.                 if (detail->next)
  568.                         detail++;
  569.                 else
  570.                         detail = NULL;
  571.         } while (detail != NULL);
  572.  
  573.         return name;
  574. }
  575.  
  576. /**
  577.  * Insert a selector into a hash chain
  578.  *
  579.  * \param ctx       Selector hash
  580.  * \param head      Head of chain to insert into
  581.  * \param selector  Selector to insert
  582.  * \return CSS_OK    on success,
  583.  *         CSS_NOMEM on memory exhaustion.
  584.  */
  585. css_error _insert_into_chain(css_selector_hash *ctx, hash_entry *head,
  586.                 const css_selector *selector)
  587. {
  588.         if (head->sel == NULL) {
  589.                 head->sel = selector;
  590.                 head->next = NULL;
  591.         } else {
  592.                 hash_entry *search = head;
  593.                 hash_entry *prev = NULL;
  594.                 hash_entry *entry =
  595.                                 ctx->alloc(NULL, sizeof(hash_entry), ctx->pw);
  596.                 if (entry == NULL)
  597.                         return CSS_NOMEM;
  598.  
  599.                 /* Find place to insert entry */
  600.                 do {
  601.                         /* Sort by ascending specificity */
  602.                         if (search->sel->specificity > selector->specificity)
  603.                                 break;
  604.  
  605.                         /* Sort by ascending rule index */
  606.                         if (search->sel->specificity == selector->specificity &&
  607.                                         search->sel->rule->index >
  608.                                         selector->rule->index)
  609.                                 break;
  610.  
  611.                         prev = search;
  612.                         search = search->next;
  613.                 } while (search != NULL);
  614.  
  615.                 if (prev == NULL) {
  616.                         entry->sel = head->sel;
  617.                         entry->next = head->next;
  618.                         head->sel = selector;
  619.                         head->next = entry;
  620.                 } else {
  621.                         entry->sel = selector;
  622.                         entry->next = prev->next;
  623.                         prev->next = entry;
  624.                 }
  625.  
  626.                 ctx->hash_size += sizeof(hash_entry);
  627.         }
  628.  
  629.         return CSS_OK;
  630. }
  631.  
  632. /**
  633.  * Remove a selector from a hash chain
  634.  *
  635.  * \param ctx       Selector hash
  636.  * \param head      Head of chain to remove from
  637.  * \param selector  Selector to remove
  638.  * \return CSS_OK       on success,
  639.  *         CSS_INVALID  if selector not found in chain.
  640.  */
  641. css_error _remove_from_chain(css_selector_hash *ctx, hash_entry *head,
  642.                 const css_selector *selector)
  643. {
  644.         hash_entry *search = head, *prev = NULL;
  645.  
  646.         if (head->sel == NULL)
  647.                 return CSS_INVALID;
  648.  
  649.         do {
  650.                 if (search->sel == selector)
  651.                         break;
  652.  
  653.                 prev = search;
  654.                 search = search->next;
  655.         } while (search != NULL);
  656.  
  657.         if (search == NULL)
  658.                 return CSS_INVALID;
  659.  
  660.         if (prev == NULL) {
  661.                 if (search->next != NULL) {
  662.                         head->sel = search->next->sel;
  663.                         head->next = search->next->next;
  664.                 } else {
  665.                         head->sel = NULL;
  666.                         head->next = NULL;
  667.                 }
  668.         } else {
  669.                 prev->next = search->next;
  670.  
  671.                 ctx->alloc(search, 0, ctx->pw);
  672.  
  673.                 ctx->hash_size -= sizeof(hash_entry);
  674.         }
  675.  
  676.         return CSS_OK;
  677. }
  678.  
  679. /**
  680.  * Find the next selector that matches
  681.  *
  682.  * \param current  Current item
  683.  * \param next     Pointer to location to receive next item
  684.  * \return CSS_OK on success, appropriate error otherwise
  685.  *
  686.  * If nothing further matches, CSS_OK will be returned and **next == NULL
  687.  */
  688. css_error _iterate_elements(const css_selector **current,
  689.                 const css_selector ***next)
  690. {
  691.         const hash_entry *head = (const hash_entry *) current;
  692.         bool match = false;
  693.         lwc_error lerror = lwc_error_ok;
  694.         lwc_string *name;
  695.  
  696.         name = head->sel->data.qname.name;
  697.  
  698.         /* Look for the next selector that matches the key */
  699.         while (match == false && (head = head->next) != NULL) {
  700.                 lerror = lwc_string_caseless_isequal(
  701.                                 name, head->sel->data.qname.name, &match);
  702.                 if (lerror != lwc_error_ok)
  703.                         return css_error_from_lwc_error(lerror);
  704.         }
  705.  
  706.         if (head == NULL)
  707.                 head = &empty_slot;
  708.  
  709.         (*next) = (const css_selector **) head;
  710.  
  711.         return CSS_OK;
  712. }
  713.  
  714. /**
  715.  * Find the next selector that matches
  716.  *
  717.  * \param current  Current item
  718.  * \param next     Pointer to location to receive next item
  719.  * \return CSS_OK on success, appropriate error otherwise
  720.  *
  721.  * If nothing further matches, CSS_OK will be returned and **next == NULL
  722.  */
  723. css_error _iterate_classes(const css_selector **current,
  724.                 const css_selector ***next)
  725. {
  726.         const hash_entry *head = (const hash_entry *) current;
  727.         bool match = false;
  728.         lwc_error lerror = lwc_error_ok;
  729.         lwc_string *name, *ref;
  730.  
  731.         ref = _class_name(head->sel);
  732.  
  733.         /* Look for the next selector that matches the key */
  734.         while (match == false && (head = head->next) != NULL) {
  735.                 name = _class_name(head->sel);
  736.                 if (name == NULL)
  737.                         continue;
  738.  
  739.                 lerror = lwc_string_caseless_isequal(
  740.                                 ref, name, &match);
  741.                 if (lerror != lwc_error_ok)
  742.                         return css_error_from_lwc_error(lerror);
  743.         }
  744.  
  745.         if (head == NULL)
  746.                 head = &empty_slot;
  747.  
  748.         (*next) = (const css_selector **) head;
  749.  
  750.         return CSS_OK;
  751. }
  752.  
  753. /**
  754.  * Find the next selector that matches
  755.  *
  756.  * \param current  Current item
  757.  * \param next     Pointer to location to receive next item
  758.  * \return CSS_OK on success, appropriate error otherwise
  759.  *
  760.  * If nothing further matches, CSS_OK will be returned and **next == NULL
  761.  */
  762. css_error _iterate_ids(const css_selector **current,
  763.                 const css_selector ***next)
  764. {
  765.         const hash_entry *head = (const hash_entry *) current;
  766.         bool match = false;
  767.         lwc_error lerror = lwc_error_ok;
  768.         lwc_string *name, *ref;
  769.  
  770.         ref = _id_name(head->sel);
  771.  
  772.         /* Look for the next selector that matches the key */
  773.         while (match == false && (head = head->next) != NULL) {
  774.                 name = _id_name(head->sel);
  775.                 if (name == NULL)
  776.                         continue;
  777.  
  778.                 lerror = lwc_string_caseless_isequal(
  779.                                 ref, name, &match);
  780.                 if (lerror != lwc_error_ok)
  781.                         return css_error_from_lwc_error(lerror);
  782.         }
  783.  
  784.         if (head == NULL)
  785.                 head = &empty_slot;
  786.  
  787.         (*next) = (const css_selector **) head;
  788.  
  789.         return CSS_OK;
  790. }
  791.  
  792. /**
  793.  * Find the next selector that matches
  794.  *
  795.  * \param current  Current item
  796.  * \param next     Pointer to location to receive next item
  797.  * \return CSS_OK on success, appropriate error otherwise
  798.  *
  799.  * If nothing further matches, CSS_OK will be returned and **next == NULL
  800.  */
  801. css_error _iterate_universal(const css_selector **current,
  802.                 const css_selector ***next)
  803. {
  804.         const hash_entry *head = (const hash_entry *) current;
  805.  
  806.         if (head->next == NULL)
  807.                 head = &empty_slot;
  808.         else
  809.                 head = head->next;
  810.  
  811.         (*next) = (const css_selector **) head;
  812.  
  813.         return CSS_OK;
  814. }
  815.  
  816.