Skip to content

Commit ab5f85a

Browse files
committed
Factor out modification nr caching
1 parent 3e791e3 commit ab5f85a

File tree

4 files changed

+34
-25
lines changed

4 files changed

+34
-25
lines changed

ext/dom/php_dom.h

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -226,14 +226,8 @@ xmlNodePtr dom_clone_node(php_dom_libxml_ns_mapper *ns_mapper, xmlNodePtr node,
226226

227227
static zend_always_inline bool php_dom_is_cache_tag_stale_from_doc_ptr(const php_libxml_cache_tag *cache_tag, const php_libxml_ref_obj *doc_ptr)
228228
{
229-
ZEND_ASSERT(cache_tag != NULL);
230229
ZEND_ASSERT(doc_ptr != NULL);
231-
/* See overflow comment in php_libxml_invalidate_node_list_cache(). */
232-
#if SIZEOF_SIZE_T == 8
233-
return cache_tag->modification_nr != doc_ptr->cache_tag.modification_nr;
234-
#else
235-
return cache_tag->modification_nr != doc_ptr->cache_tag.modification_nr || UNEXPECTED(doc_ptr->cache_tag.modification_nr == SIZE_MAX);
236-
#endif
230+
return php_libxml_is_cache_tag_stale(cache_tag, &doc_ptr->cache_tag);
237231
}
238232

239233
static zend_always_inline bool php_dom_is_cache_tag_stale_from_node(const php_libxml_cache_tag *cache_tag, const xmlNodePtr node)

ext/dom/token_list.c

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,7 @@ typedef struct _dom_token_list_it {
3232
zend_object_iterator it;
3333
/* Store the hash position here to allow multiple (e.g. nested) iterations of the same token list. */
3434
HashPosition pos;
35-
/* Modification tracking: when the token list changes, we increment its counter.
36-
* When this counter no longer matches the counter of the token list, we know that it has changed and we
37-
* have to update the iteration index. */
38-
size_t modification_nr;
35+
php_libxml_cache_tag cache_tag;
3936
} dom_token_list_it;
4037

4138
/* https://infra.spec.whatwg.org/#ascii-whitespace */
@@ -137,7 +134,7 @@ static void dom_token_list_update(dom_token_list_object *intern)
137134
const xmlAttr *attr = dom_token_list_get_attr(intern);
138135
HashTable *token_set = TOKEN_LIST_GET_SET(intern);
139136

140-
intern->modification_nr++;
137+
php_libxml_invalidate_cache_tag(&intern->cache_tag);
141138

142139
/* 1. If the associated element does not have an associated attribute and token set is empty, then return. */
143140
if (attr == NULL && zend_hash_num_elements(token_set) == 0) {
@@ -182,7 +179,7 @@ static void dom_token_list_ensure_set_up_to_date(dom_token_list_object *intern)
182179

183180
/* xmlStrEqual will automatically handle equality rules of NULL vs "" (etc) correctly. */
184181
if (!xmlStrEqual(value, (const xmlChar *) intern->cached_string)) {
185-
intern->modification_nr++;
182+
php_libxml_invalidate_cache_tag(&intern->cache_tag);
186183
efree(intern->cached_string);
187184
HashTable *token_set = TOKEN_LIST_GET_SET(intern);
188185
zend_hash_destroy(token_set);
@@ -199,7 +196,7 @@ void dom_token_list_ctor(dom_token_list_object *intern, dom_object *element_obj)
199196
element_obj->document->refcount++;
200197
intern->dom.document = element_obj->document;
201198

202-
intern->modification_nr = 0;
199+
intern->cache_tag.modification_nr = 0;
203200

204201
ALLOC_HASHTABLE(TOKEN_LIST_GET_SET(intern));
205202
HashTable *token_set = TOKEN_LIST_GET_SET(intern);
@@ -642,7 +639,7 @@ static void dom_token_list_it_get_current_key(zend_object_iterator *iter, zval *
642639

643640
dom_token_list_ensure_set_up_to_date(object);
644641

645-
if (UNEXPECTED(object->modification_nr != iterator->modification_nr)) {
642+
if (UNEXPECTED(php_libxml_is_cache_tag_stale(&object->cache_tag, &iterator->cache_tag))) {
646643
iter->index = 0;
647644
HashPosition pos;
648645
HashTable *token_set = TOKEN_LIST_GET_SET(object);
@@ -704,7 +701,7 @@ zend_object_iterator *dom_token_list_get_iterator(zend_class_entry *ce, zval *ob
704701
ZVAL_OBJ_COPY(&iterator->it.data, Z_OBJ_P(object));
705702

706703
iterator->it.funcs = &dom_token_list_it_funcs;
707-
iterator->modification_nr = intern->modification_nr;
704+
iterator->cache_tag = intern->cache_tag;
708705

709706
return &iterator->it;
710707
}

ext/dom/token_list.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,7 @@ typedef struct _dom_token_list_object {
2121
php_libxml_node_ptr *element_ptr;
2222
/* Used to check if the token set is up to date. */
2323
char *cached_string;
24-
/* See dom_token_list_it */
25-
size_t modification_nr;
24+
php_libxml_cache_tag cache_tag;
2625
dom_object dom;
2726
} dom_token_list_object;
2827

ext/libxml/php_libxml.h

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ typedef struct _libxml_doc_props {
5858
bool recover;
5959
} libxml_doc_props;
6060

61+
/* Modification tracking: when the object changes, we increment its counter.
62+
* When this counter no longer matches the counter at the time of caching,
63+
* we know that the object has changed and we have to update the cache. */
6164
typedef struct {
6265
size_t modification_nr;
6366
} php_libxml_cache_tag;
@@ -122,23 +125,39 @@ static inline php_libxml_node_object *php_libxml_node_fetch_object(zend_object *
122125
return (php_libxml_node_object *)((char*)(obj) - obj->handlers->offset);
123126
}
124127

125-
static zend_always_inline void php_libxml_invalidate_node_list_cache(php_libxml_ref_obj *doc_ptr)
128+
static zend_always_inline void php_libxml_invalidate_cache_tag(php_libxml_cache_tag *cache_tag)
126129
{
127-
if (!doc_ptr) {
128-
return;
129-
}
130130
#if SIZEOF_SIZE_T == 8
131131
/* If one operation happens every nanosecond, then it would still require 584 years to overflow
132132
* the counter. So we'll just assume this never happens. */
133-
doc_ptr->cache_tag.modification_nr++;
133+
cache_tag->modification_nr++;
134134
#else
135-
size_t new_modification_nr = doc_ptr->cache_tag.modification_nr + 1;
135+
size_t new_modification_nr = cache_tag->modification_nr + 1;
136136
if (EXPECTED(new_modification_nr > 0)) { /* unsigned overflow; checking after addition results in one less instruction */
137-
doc_ptr->cache_tag.modification_nr = new_modification_nr;
137+
cache_tag->modification_nr = new_modification_nr;
138138
}
139139
#endif
140140
}
141141

142+
static zend_always_inline bool php_libxml_is_cache_tag_stale(const php_libxml_cache_tag *object_tag, const php_libxml_cache_tag *cache_tag)
143+
{
144+
ZEND_ASSERT(object_tag != NULL);
145+
ZEND_ASSERT(cache_tag != NULL);
146+
/* See overflow comment in php_libxml_invalidate_node_list_cache(). */
147+
#if SIZEOF_SIZE_T == 8
148+
return cache_tag->modification_nr != object_tag->modification_nr;
149+
#else
150+
return cache_tag->modification_nr != object_tag->modification_nr || UNEXPECTED(object_tag->modification_nr == SIZE_MAX);
151+
#endif
152+
}
153+
154+
static zend_always_inline void php_libxml_invalidate_node_list_cache(php_libxml_ref_obj *doc_ptr)
155+
{
156+
if (doc_ptr) {
157+
php_libxml_invalidate_cache_tag(&doc_ptr->cache_tag);
158+
}
159+
}
160+
142161
static zend_always_inline void php_libxml_invalidate_node_list_cache_from_doc(xmlDocPtr docp)
143162
{
144163
if (docp && docp->_private) { /* docp is NULL for detached nodes */

0 commit comments

Comments
 (0)