Skip to content

Commit e7cc5f4

Browse files
committed
Factor out modification nr caching
1 parent 24042b7 commit e7cc5f4

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
@@ -252,14 +252,8 @@ xmlNodePtr dom_clone_node(php_dom_libxml_ns_mapper *ns_mapper, xmlNodePtr node,
252252

253253
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)
254254
{
255-
ZEND_ASSERT(cache_tag != NULL);
256255
ZEND_ASSERT(doc_ptr != NULL);
257-
/* See overflow comment in php_libxml_invalidate_node_list_cache(). */
258-
#if SIZEOF_SIZE_T == 8
259-
return cache_tag->modification_nr != doc_ptr->cache_tag.modification_nr;
260-
#else
261-
return cache_tag->modification_nr != doc_ptr->cache_tag.modification_nr || UNEXPECTED(doc_ptr->cache_tag.modification_nr == SIZE_MAX);
262-
#endif
256+
return php_libxml_is_cache_tag_stale(cache_tag, &doc_ptr->cache_tag);
263257
}
264258

265259
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;
@@ -109,23 +112,39 @@ static inline php_libxml_node_object *php_libxml_node_fetch_object(zend_object *
109112
return (php_libxml_node_object *)((char*)(obj) - obj->handlers->offset);
110113
}
111114

112-
static zend_always_inline void php_libxml_invalidate_node_list_cache(php_libxml_ref_obj *doc_ptr)
115+
static zend_always_inline void php_libxml_invalidate_cache_tag(php_libxml_cache_tag *cache_tag)
113116
{
114-
if (!doc_ptr) {
115-
return;
116-
}
117117
#if SIZEOF_SIZE_T == 8
118118
/* If one operation happens every nanosecond, then it would still require 584 years to overflow
119119
* the counter. So we'll just assume this never happens. */
120-
doc_ptr->cache_tag.modification_nr++;
120+
cache_tag->modification_nr++;
121121
#else
122-
size_t new_modification_nr = doc_ptr->cache_tag.modification_nr + 1;
122+
size_t new_modification_nr = cache_tag->modification_nr + 1;
123123
if (EXPECTED(new_modification_nr > 0)) { /* unsigned overflow; checking after addition results in one less instruction */
124-
doc_ptr->cache_tag.modification_nr = new_modification_nr;
124+
cache_tag->modification_nr = new_modification_nr;
125125
}
126126
#endif
127127
}
128128

129+
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)
130+
{
131+
ZEND_ASSERT(object_tag != NULL);
132+
ZEND_ASSERT(cache_tag != NULL);
133+
/* See overflow comment in php_libxml_invalidate_node_list_cache(). */
134+
#if SIZEOF_SIZE_T == 8
135+
return cache_tag->modification_nr != object_tag->modification_nr;
136+
#else
137+
return cache_tag->modification_nr != object_tag->modification_nr || UNEXPECTED(object_tag->modification_nr == SIZE_MAX);
138+
#endif
139+
}
140+
141+
static zend_always_inline void php_libxml_invalidate_node_list_cache(php_libxml_ref_obj *doc_ptr)
142+
{
143+
if (doc_ptr) {
144+
php_libxml_invalidate_cache_tag(&doc_ptr->cache_tag);
145+
}
146+
}
147+
129148
static zend_always_inline void php_libxml_invalidate_node_list_cache_from_doc(xmlDocPtr docp)
130149
{
131150
if (docp && docp->_private) { /* docp is NULL for detached nodes */

0 commit comments

Comments
 (0)