diff --git a/NEWS b/NEWS
index a0bce4ed6f43..4191061f0ccd 100644
--- a/NEWS
+++ b/NEWS
@@ -57,6 +57,10 @@ PHP NEWS
after a dot segment in a path, replacement file drive paths, the output
encoding used for percent-encoding, the URLSearchParams tail pointer and
fragment serialization without a query. (alexandre-daubois)
+ . Merge patch lexbor/lexbor@f4cbbcd, fixing lexbor_array_obj_t allocating 8
+ times the memory it uses. (Nicolas Grekas)
+ . Reduced the initial size of the tokenizer pools and of the memory pool
+ caches, saving about 900KB per HTML parse. (Nicolas Grekas)
- LibXML:
. Clear the libxml last error in libxml_use_internal_errors(false).
diff --git a/UPGRADING b/UPGRADING
index d9242ecb3c17..5e95bc667573 100644
--- a/UPGRADING
+++ b/UPGRADING
@@ -205,6 +205,10 @@ PHP 8.7 UPGRADE NOTES
target does not keep is now released at that assignment instead of after
the last one.
+- DOM:
+ . Parsing HTML with Dom\HTMLDocument or innerHTML uses much less memory for
+ small inputs.
+
- Standard:
. Improved performance of strcspn() and strpbrk() when searching longer strings
with a single-byte character mask.
diff --git a/ext/dom/tests/gh25118.phpt b/ext/dom/tests/gh25118.phpt
index 790ae71ef337..a8bc65bb22bb 100644
--- a/ext/dom/tests/gh25118.phpt
+++ b/ext/dom/tests/gh25118.phpt
@@ -5,13 +5,14 @@ dom
--SKIPIF--
--INI--
-memory_limit=32M
+memory_limit=26M
--FILE--
t
n' . str_repeat('', 100000) . '';
Dom\HTMLDocument::createFromString($html, LIBXML_NOERROR);
?>
--EXPECTF--
-Fatal error: Allowed memory size of 33554432 bytes exhausted %S(tried to allocate %d bytes) in %s on line %d
+Fatal error: Allowed memory size of 27262976 bytes exhausted %S(tried to allocate %d bytes) in %s on line %d
diff --git a/ext/lexbor/config.m4 b/ext/lexbor/config.m4
index 43123578a727..2c53e0c4f56a 100644
--- a/ext/lexbor/config.m4
+++ b/ext/lexbor/config.m4
@@ -1,4 +1,6 @@
PHP_LEXBOR_CFLAGS="-Wno-unknown-warning-option -Wno-unterminated-string-initialization -I@ext_srcdir@/"
+dnl Initial sizes of the tokenizer pools and of the mraw cache, see patch 0026.
+PHP_LEXBOR_CFLAGS="$PHP_LEXBOR_CFLAGS -DLXB_HTML_TKZ_TOKEN_POOL_SIZE=16 -DLXB_HTML_TKZ_ATTR_POOL_SIZE=256 -DLEXBOR_MRAW_CACHE_SIZE=64"
LEXBOR_DIR="lexbor"
AC_DEFINE([HAVE_LEXBOR], [1], [Define to 1 if the PHP extension 'lexbor' is available.])
diff --git a/ext/lexbor/config.w32 b/ext/lexbor/config.w32
index 403c3b98afb5..773b57d0516c 100644
--- a/ext/lexbor/config.w32
+++ b/ext/lexbor/config.w32
@@ -24,6 +24,8 @@ ADD_SOURCES("ext/lexbor/lexbor/tag", "tag.c","lexbor");
ADD_SOURCES("ext/lexbor/lexbor/unicode", "idna.c unicode.c","lexbor");
ADD_SOURCES("ext/lexbor/lexbor/url", "url.c","lexbor");
ADD_FLAG("CFLAGS_LEXBOR", "/D LEXBOR_BUILDING /utf-8");
+// Initial sizes of the tokenizer pools and of the mraw cache, see patch 0026.
+ADD_FLAG("CFLAGS_LEXBOR", "/D LXB_HTML_TKZ_TOKEN_POOL_SIZE=16 /D LXB_HTML_TKZ_ATTR_POOL_SIZE=256 /D LEXBOR_MRAW_CACHE_SIZE=64");
AC_DEFINE("HAVE_LEXBOR", 1, "Define to 1 if the PHP extension 'lexbor' is available.");
diff --git a/ext/lexbor/lexbor/core/array_obj.c b/ext/lexbor/lexbor/core/array_obj.c
index 79a4c3a65a81..8cceaf9fe018 100644
--- a/ext/lexbor/lexbor/core/array_obj.c
+++ b/ext/lexbor/lexbor/core/array_obj.c
@@ -29,8 +29,7 @@ lexbor_array_obj_init(lexbor_array_obj_t *array,
array->size = size;
array->struct_size = struct_size;
- array->list = lexbor_malloc(sizeof(uint8_t *)
- * (array->size * struct_size));
+ array->list = lexbor_malloc(array->size * struct_size);
if (array->list == NULL) {
return LXB_STATUS_ERROR_MEMORY_ALLOCATION;
}
@@ -77,8 +76,7 @@ lexbor_array_obj_expand(lexbor_array_obj_t *array, size_t up_to)
new_size = array->length + up_to;
- list = lexbor_realloc(array->list, sizeof(uint8_t *)
- * (new_size * array->struct_size));
+ list = lexbor_realloc(array->list, new_size * array->struct_size);
if (list == NULL) {
return NULL;
}
@@ -170,8 +168,7 @@ lexbor_array_obj_delete(lexbor_array_obj_t *array, size_t begin, size_t length)
memmove(&array->list[ begin * array->struct_size ],
&array->list[ end_len * array->struct_size ],
- sizeof(uint8_t *)
- * ((array->length - end_len) * array->struct_size));
+ (array->length - end_len) * array->struct_size);
array->length -= length;
}
diff --git a/ext/lexbor/lexbor/core/mraw.c b/ext/lexbor/lexbor/core/mraw.c
index c9904050dd84..30faca63135a 100644
--- a/ext/lexbor/lexbor/core/mraw.c
+++ b/ext/lexbor/lexbor/core/mraw.c
@@ -11,6 +11,10 @@
#include
#endif
+#ifndef LEXBOR_MRAW_CACHE_SIZE
+ #define LEXBOR_MRAW_CACHE_SIZE 64
+#endif
+
#define lexbor_mraw_meta_set(data, size) \
do { \
@@ -62,7 +66,7 @@ lexbor_mraw_init(lexbor_mraw_t *mraw, size_t chunk_size)
/* Cache */
mraw->cache = lexbor_bst_create();
- status = lexbor_bst_init(mraw->cache, 512);
+ status = lexbor_bst_init(mraw->cache, LEXBOR_MRAW_CACHE_SIZE);
if (status) {
return status;
}
diff --git a/ext/lexbor/lexbor/html/tokenizer.c b/ext/lexbor/lexbor/html/tokenizer.c
index 1d9f3781255d..219314691564 100644
--- a/ext/lexbor/lexbor/html/tokenizer.c
+++ b/ext/lexbor/lexbor/html/tokenizer.c
@@ -20,6 +20,14 @@
#define LXB_HTML_TKZ_TEMP_SIZE (4096 * 4)
+#ifndef LXB_HTML_TKZ_TOKEN_POOL_SIZE
+ #define LXB_HTML_TKZ_TOKEN_POOL_SIZE 16
+#endif
+
+#ifndef LXB_HTML_TKZ_ATTR_POOL_SIZE
+ #define LXB_HTML_TKZ_ATTR_POOL_SIZE 256
+#endif
+
enum {
LXB_HTML_TOKENIZER_OPT_TAGS_SELF = 1 << 0,
@@ -63,14 +71,16 @@ lxb_html_tokenizer_init(lxb_html_tokenizer_t *tkz)
tkz->dobj_token = lexbor_dobject_create();
status = lexbor_dobject_init(tkz->dobj_token,
- 4096, sizeof(lxb_html_token_t));
+ LXB_HTML_TKZ_TOKEN_POOL_SIZE,
+ sizeof(lxb_html_token_t));
if (status != LXB_STATUS_OK) {
return status;
}
/* Init Token Attributes */
tkz->dobj_token_attr = lexbor_dobject_create();
- status = lexbor_dobject_init(tkz->dobj_token_attr, 4096,
+ status = lexbor_dobject_init(tkz->dobj_token_attr,
+ LXB_HTML_TKZ_ATTR_POOL_SIZE,
sizeof(lxb_html_token_attr_t));
if (status != LXB_STATUS_OK) {
return status;
diff --git a/ext/lexbor/patches/0001-Expose-line-and-column-information-for-use-in-PHP.patch b/ext/lexbor/patches/0001-Expose-line-and-column-information-for-use-in-PHP.patch
index 2cd28f9eaf1d..8a5a13166a78 100644
--- a/ext/lexbor/patches/0001-Expose-line-and-column-information-for-use-in-PHP.patch
+++ b/ext/lexbor/patches/0001-Expose-line-and-column-information-for-use-in-PHP.patch
@@ -1,7 +1,7 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Niels Dossche <7771979+nielsdos@users.noreply.github.com>
Date: Sat, 26 Aug 2023 15:08:59 +0200
-Subject: [PATCH 01/24] Expose line and column information for use in PHP
+Subject: [PATCH 01/26] Expose line and column information for use in PHP
---
source/lexbor/dom/interfaces/node.h | 2 ++
diff --git a/ext/lexbor/patches/0002-Track-implied-added-nodes-for-options-use-in-PHP.patch b/ext/lexbor/patches/0002-Track-implied-added-nodes-for-options-use-in-PHP.patch
index 4cff1b906f8d..c6f567e9954d 100644
--- a/ext/lexbor/patches/0002-Track-implied-added-nodes-for-options-use-in-PHP.patch
+++ b/ext/lexbor/patches/0002-Track-implied-added-nodes-for-options-use-in-PHP.patch
@@ -1,7 +1,7 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Niels Dossche <7771979+nielsdos@users.noreply.github.com>
Date: Mon, 14 Aug 2023 20:18:51 +0200
-Subject: [PATCH 02/24] Track implied added nodes for options use in PHP
+Subject: [PATCH 02/26] Track implied added nodes for options use in PHP
---
source/lexbor/html/tree.h | 3 +++
diff --git a/ext/lexbor/patches/0003-Patch-utilities-and-data-structure-to-be-able-to-gen.patch b/ext/lexbor/patches/0003-Patch-utilities-and-data-structure-to-be-able-to-gen.patch
index 19b411b2ffcf..e2bddc62b70f 100644
--- a/ext/lexbor/patches/0003-Patch-utilities-and-data-structure-to-be-able-to-gen.patch
+++ b/ext/lexbor/patches/0003-Patch-utilities-and-data-structure-to-be-able-to-gen.patch
@@ -1,7 +1,7 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Niels Dossche <7771979+nielsdos@users.noreply.github.com>
Date: Thu, 24 Aug 2023 22:57:48 +0200
-Subject: [PATCH 03/24] Patch utilities and data structure to be able to
+Subject: [PATCH 03/26] Patch utilities and data structure to be able to
generate smaller lookup tables
Changed the generation script to check if everything fits in 32-bits.
diff --git a/ext/lexbor/patches/0004-Remove-unused-upper-case-tag-static-data.patch b/ext/lexbor/patches/0004-Remove-unused-upper-case-tag-static-data.patch
index 67f241a96cf9..2a4dcdc4e4d9 100644
--- a/ext/lexbor/patches/0004-Remove-unused-upper-case-tag-static-data.patch
+++ b/ext/lexbor/patches/0004-Remove-unused-upper-case-tag-static-data.patch
@@ -1,7 +1,7 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Niels Dossche <7771979+nielsdos@users.noreply.github.com>
Date: Wed, 29 Nov 2023 21:26:47 +0100
-Subject: [PATCH 04/24] Remove unused upper case tag static data
+Subject: [PATCH 04/26] Remove unused upper case tag static data
---
source/lexbor/tag/res.h | 2 ++
diff --git a/ext/lexbor/patches/0005-Shrink-size-of-static-binary-search-tree.patch b/ext/lexbor/patches/0005-Shrink-size-of-static-binary-search-tree.patch
index a36832333e12..5ed745307ad6 100644
--- a/ext/lexbor/patches/0005-Shrink-size-of-static-binary-search-tree.patch
+++ b/ext/lexbor/patches/0005-Shrink-size-of-static-binary-search-tree.patch
@@ -1,7 +1,7 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Niels Dossche <7771979+nielsdos@users.noreply.github.com>
Date: Wed, 29 Nov 2023 21:29:31 +0100
-Subject: [PATCH 05/24] Shrink size of static binary search tree
+Subject: [PATCH 05/26] Shrink size of static binary search tree
This also makes it more efficient on the data cache.
---
diff --git a/ext/lexbor/patches/0006-Patch-out-unused-CSS-style-code.patch b/ext/lexbor/patches/0006-Patch-out-unused-CSS-style-code.patch
index fa25a94ef3fa..58e64a0441cd 100644
--- a/ext/lexbor/patches/0006-Patch-out-unused-CSS-style-code.patch
+++ b/ext/lexbor/patches/0006-Patch-out-unused-CSS-style-code.patch
@@ -1,7 +1,7 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Niels Dossche <7771979+nielsdos@users.noreply.github.com>
Date: Sun, 7 Jan 2024 21:59:28 +0100
-Subject: [PATCH 06/24] Patch out unused CSS style code
+Subject: [PATCH 06/26] Patch out unused CSS style code
---
source/lexbor/css/rule.h | 2 ++
diff --git a/ext/lexbor/patches/0007-Add-lxb_url_is_special-to-the-public-API-362.patch b/ext/lexbor/patches/0007-Add-lxb_url_is_special-to-the-public-API-362.patch
index 2f939cb69f2c..f8c4b4d21bda 100644
--- a/ext/lexbor/patches/0007-Add-lxb_url_is_special-to-the-public-API-362.patch
+++ b/ext/lexbor/patches/0007-Add-lxb_url_is_special-to-the-public-API-362.patch
@@ -1,7 +1,7 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?=
Date: Sun, 17 May 2026 22:17:14 +0200
-Subject: [PATCH 07/24] Add lxb_url_is_special() to the public API (#362)
+Subject: [PATCH 07/26] Add lxb_url_is_special() to the public API (#362)
As https://wiki.php.net/rfc/uri_followup#uri_type_detection relies on this information.
---
diff --git a/ext/lexbor/patches/0008-URL-fixed-setters-for-empty-hosts.patch b/ext/lexbor/patches/0008-URL-fixed-setters-for-empty-hosts.patch
index d72c82fbdaea..869d4b645bbf 100644
--- a/ext/lexbor/patches/0008-URL-fixed-setters-for-empty-hosts.patch
+++ b/ext/lexbor/patches/0008-URL-fixed-setters-for-empty-hosts.patch
@@ -1,7 +1,7 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Alexander Borisov
Date: Fri, 26 Jun 2026 18:55:56 +0300
-Subject: [PATCH 08/24] URL: fixed setters for empty hosts.
+Subject: [PATCH 08/26] URL: fixed setters for empty hosts.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
diff --git a/ext/lexbor/patches/0009-URL-fixed-uninitialized-memory-in-the-path-buffer-gr.patch b/ext/lexbor/patches/0009-URL-fixed-uninitialized-memory-in-the-path-buffer-gr.patch
index b2ac318e2ec6..3dc3ba50e59e 100644
--- a/ext/lexbor/patches/0009-URL-fixed-uninitialized-memory-in-the-path-buffer-gr.patch
+++ b/ext/lexbor/patches/0009-URL-fixed-uninitialized-memory-in-the-path-buffer-gr.patch
@@ -1,7 +1,7 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Alexander Borisov
Date: Fri, 5 Jun 2026 22:13:32 +0300
-Subject: [PATCH 09/24] URL: fixed uninitialized memory in the path buffer
+Subject: [PATCH 09/26] URL: fixed uninitialized memory in the path buffer
growth.
When a path was long enough to outgrow the on-stack buffer, the first
diff --git a/ext/lexbor/patches/0010-Fix-parsing-for-URL-containing-empty-host-and-userin.patch b/ext/lexbor/patches/0010-Fix-parsing-for-URL-containing-empty-host-and-userin.patch
index 937722a246b5..23eb758d671d 100644
--- a/ext/lexbor/patches/0010-Fix-parsing-for-URL-containing-empty-host-and-userin.patch
+++ b/ext/lexbor/patches/0010-Fix-parsing-for-URL-containing-empty-host-and-userin.patch
@@ -1,7 +1,7 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?=
Date: Thu, 9 Jul 2026 21:51:05 +0200
-Subject: [PATCH 10/24] Fix parsing for URL containing empty host and userinfo
+Subject: [PATCH 10/26] Fix parsing for URL containing empty host and userinfo
The returned error code (LXB_URL_ERROR_TYPE_INVALID_CREDENTIALS) apparently contradicts the specification:
diff --git a/ext/lexbor/patches/0011-Percent-encode-the-caret-in-the-path.patch b/ext/lexbor/patches/0011-Percent-encode-the-caret-in-the-path.patch
index 9c1a0708a756..f977e68a12cc 100644
--- a/ext/lexbor/patches/0011-Percent-encode-the-caret-in-the-path.patch
+++ b/ext/lexbor/patches/0011-Percent-encode-the-caret-in-the-path.patch
@@ -1,7 +1,7 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?=
Date: Fri, 10 Jul 2026 22:31:16 +0200
-Subject: [PATCH 11/24] Percent-encode the caret in the path
+Subject: [PATCH 11/26] Percent-encode the caret in the path
The caret (^) is part of the path percent-encode set:
diff --git a/ext/lexbor/patches/0012-URL-added-public-IPv6-parser.patch b/ext/lexbor/patches/0012-URL-added-public-IPv6-parser.patch
index a4007d2a68f9..d633aa5c150a 100644
--- a/ext/lexbor/patches/0012-URL-added-public-IPv6-parser.patch
+++ b/ext/lexbor/patches/0012-URL-added-public-IPv6-parser.patch
@@ -1,7 +1,7 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Alexander Borisov
Date: Wed, 12 Aug 2026 23:29:20 +0300
-Subject: [PATCH 12/24] URL: added public IPv6 parser.
+Subject: [PATCH 12/26] URL: added public IPv6 parser.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
diff --git a/ext/lexbor/patches/0013-URL-added-public-percent-encoder-API.patch b/ext/lexbor/patches/0013-URL-added-public-percent-encoder-API.patch
index 729969019b40..5086da0e34e8 100644
--- a/ext/lexbor/patches/0013-URL-added-public-percent-encoder-API.patch
+++ b/ext/lexbor/patches/0013-URL-added-public-percent-encoder-API.patch
@@ -1,7 +1,7 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Alexander Borisov
Date: Thu, 13 Aug 2026 23:29:16 +0300
-Subject: [PATCH 13/24] URL: added public percent-encoder API.
+Subject: [PATCH 13/26] URL: added public percent-encoder API.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
diff --git a/ext/lexbor/patches/0014-CSS-fixed-heap-buffer-overflow-in-lexbor-contains-pa.patch b/ext/lexbor/patches/0014-CSS-fixed-heap-buffer-overflow-in-lexbor-contains-pa.patch
index 373a579e0842..8f494632948d 100644
--- a/ext/lexbor/patches/0014-CSS-fixed-heap-buffer-overflow-in-lexbor-contains-pa.patch
+++ b/ext/lexbor/patches/0014-CSS-fixed-heap-buffer-overflow-in-lexbor-contains-pa.patch
@@ -1,7 +1,7 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Alexander Borisov
Date: Fri, 5 Jun 2026 22:34:23 +0300
-Subject: [PATCH 14/24] CSS: fixed heap buffer overflow in :lexbor-contains()
+Subject: [PATCH 14/26] CSS: fixed heap buffer overflow in :lexbor-contains()
parsing.
The contains string buffer was allocated by the size of the string
diff --git a/ext/lexbor/patches/0015-Encoding-fixed-buffer-overflows-in-malformed-decode-.patch b/ext/lexbor/patches/0015-Encoding-fixed-buffer-overflows-in-malformed-decode-.patch
index 68756c46547e..59731a6d9258 100644
--- a/ext/lexbor/patches/0015-Encoding-fixed-buffer-overflows-in-malformed-decode-.patch
+++ b/ext/lexbor/patches/0015-Encoding-fixed-buffer-overflows-in-malformed-decode-.patch
@@ -1,7 +1,7 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Alexander Borisov
Date: Wed, 10 Jun 2026 19:50:10 +0300
-Subject: [PATCH 15/24] Encoding: fixed buffer overflows in malformed decode
+Subject: [PATCH 15/26] Encoding: fixed buffer overflows in malformed decode
replay.
Fixed out-of-bounds writes in buffering decoders when replacement output
diff --git a/ext/lexbor/patches/0016-URL-fixed-tail-pointer-in-URLSearchParams-for-delimi.patch b/ext/lexbor/patches/0016-URL-fixed-tail-pointer-in-URLSearchParams-for-delimi.patch
index bc44d2ec9b43..67582c73bbd4 100644
--- a/ext/lexbor/patches/0016-URL-fixed-tail-pointer-in-URLSearchParams-for-delimi.patch
+++ b/ext/lexbor/patches/0016-URL-fixed-tail-pointer-in-URLSearchParams-for-delimi.patch
@@ -1,7 +1,7 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Alexander Borisov
Date: Fri, 5 Jun 2026 21:46:26 +0300
-Subject: [PATCH 16/24] URL: fixed tail pointer in URLSearchParams for
+Subject: [PATCH 16/26] URL: fixed tail pointer in URLSearchParams for
delimiter-free query.
When a query had a single token without '=' or '&' (e.g. "?abc"), the
diff --git a/ext/lexbor/patches/0017-URL-Fix-parsing-of-usernames-containing.patch b/ext/lexbor/patches/0017-URL-Fix-parsing-of-usernames-containing.patch
index f6009b9709fa..9607196ab19b 100644
--- a/ext/lexbor/patches/0017-URL-Fix-parsing-of-usernames-containing.patch
+++ b/ext/lexbor/patches/0017-URL-Fix-parsing-of-usernames-containing.patch
@@ -1,7 +1,7 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Tim=20D=C3=BCsterhus?=
Date: Fri, 31 Jul 2026 20:56:18 +0200
-Subject: [PATCH 17/24] URL: Fix parsing of usernames containing `@`
+Subject: [PATCH 17/26] URL: Fix parsing of usernames containing `@`
Fixes lexbor/lexbor#399.
---
diff --git a/ext/lexbor/patches/0018-Unicode-fixed-uninitialized-memory-in-IDNA-buffer-gr.patch b/ext/lexbor/patches/0018-Unicode-fixed-uninitialized-memory-in-IDNA-buffer-gr.patch
index 23c4cf1acc03..791180bb156f 100644
--- a/ext/lexbor/patches/0018-Unicode-fixed-uninitialized-memory-in-IDNA-buffer-gr.patch
+++ b/ext/lexbor/patches/0018-Unicode-fixed-uninitialized-memory-in-IDNA-buffer-gr.patch
@@ -1,7 +1,7 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Alexander Borisov
Date: Mon, 7 Sep 2026 22:11:39 +0300
-Subject: [PATCH 18/24] Unicode: fixed uninitialized memory in IDNA buffer
+Subject: [PATCH 18/26] Unicode: fixed uninitialized memory in IDNA buffer
growth.
When an IDNA buffer outgrew the stack allocation, the move to the heap
diff --git a/ext/lexbor/patches/0019-URL-encode-opaque-path-spaces-before-query-and-fragm.patch b/ext/lexbor/patches/0019-URL-encode-opaque-path-spaces-before-query-and-fragm.patch
index 4d8338521997..99c116b1dfda 100644
--- a/ext/lexbor/patches/0019-URL-encode-opaque-path-spaces-before-query-and-fragm.patch
+++ b/ext/lexbor/patches/0019-URL-encode-opaque-path-spaces-before-query-and-fragm.patch
@@ -1,7 +1,7 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?=
Date: Fri, 11 Sep 2026 21:58:45 +0200
-Subject: [PATCH 19/24] URL: encode opaque path spaces before query and
+Subject: [PATCH 19/26] URL: encode opaque path spaces before query and
fragment. (#405)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
diff --git a/ext/lexbor/patches/0020-URL-Fix-lxb_url_serialize_fragment-without-a-query-4.patch b/ext/lexbor/patches/0020-URL-Fix-lxb_url_serialize_fragment-without-a-query-4.patch
index a0eecf7234be..d0224ac3e881 100644
--- a/ext/lexbor/patches/0020-URL-Fix-lxb_url_serialize_fragment-without-a-query-4.patch
+++ b/ext/lexbor/patches/0020-URL-Fix-lxb_url_serialize_fragment-without-a-query-4.patch
@@ -1,7 +1,7 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Tim=20D=C3=BCsterhus?=
Date: Fri, 11 Sep 2026 22:45:33 +0200
-Subject: [PATCH 20/24] URL: Fix `lxb_url_serialize_fragment()` without a query
+Subject: [PATCH 20/26] URL: Fix `lxb_url_serialize_fragment()` without a query
(#410)
The `lxb_url_serialize_fragment()` function checked if the query contained data
diff --git a/ext/lexbor/patches/0021-URL-expose-component-reset-functions.-415.patch b/ext/lexbor/patches/0021-URL-expose-component-reset-functions.-415.patch
index deaac125e82e..43fc3e582355 100644
--- a/ext/lexbor/patches/0021-URL-expose-component-reset-functions.-415.patch
+++ b/ext/lexbor/patches/0021-URL-expose-component-reset-functions.-415.patch
@@ -1,7 +1,7 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?=
Date: Tue, 22 Sep 2026 22:20:28 +0200
-Subject: [PATCH 21/24] URL: expose component reset functions. (#415)
+Subject: [PATCH 21/26] URL: expose component reset functions. (#415)
* URL: expose component reset functions.
diff --git a/ext/lexbor/patches/0022-URL-Fix-parsing-of-query-and-fragment-after-a-dot-co.patch b/ext/lexbor/patches/0022-URL-Fix-parsing-of-query-and-fragment-after-a-dot-co.patch
index 4f9607068b9d..27a0f1c0ae1b 100644
--- a/ext/lexbor/patches/0022-URL-Fix-parsing-of-query-and-fragment-after-a-dot-co.patch
+++ b/ext/lexbor/patches/0022-URL-Fix-parsing-of-query-and-fragment-after-a-dot-co.patch
@@ -1,7 +1,7 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Tim=20D=C3=BCsterhus?=
Date: Sat, 26 Sep 2026 18:41:05 +0200
-Subject: [PATCH 22/24] URL: Fix parsing of query and fragment after a
+Subject: [PATCH 22/26] URL: Fix parsing of query and fragment after a
dot-component in path (#411)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
diff --git a/ext/lexbor/patches/0023-URL-Keep-replacement-file-drive-paths-hierarchical-4.patch b/ext/lexbor/patches/0023-URL-Keep-replacement-file-drive-paths-hierarchical-4.patch
index 2caee494342d..39ff7528aeb3 100644
--- a/ext/lexbor/patches/0023-URL-Keep-replacement-file-drive-paths-hierarchical-4.patch
+++ b/ext/lexbor/patches/0023-URL-Keep-replacement-file-drive-paths-hierarchical-4.patch
@@ -1,7 +1,7 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?=
Date: Mon, 21 Sep 2026 20:01:43 +0200
-Subject: [PATCH 23/24] URL: Keep replacement file drive paths hierarchical
+Subject: [PATCH 23/26] URL: Keep replacement file drive paths hierarchical
(#423)
WHATWG file state (https://url.spec.whatwg.org/#file-state) step 4.4.3.2 resets the path to an empty list. Do not mark it opaque, as that makes subsequent pathname updates silently do nothing.
diff --git a/ext/lexbor/patches/0024-URL-normalize-output-encoding-for-percent-encoding.patch b/ext/lexbor/patches/0024-URL-normalize-output-encoding-for-percent-encoding.patch
index c0d192d24eaf..866daef041fc 100644
--- a/ext/lexbor/patches/0024-URL-normalize-output-encoding-for-percent-encoding.patch
+++ b/ext/lexbor/patches/0024-URL-normalize-output-encoding-for-percent-encoding.patch
@@ -1,7 +1,7 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Alexander Borisov
Date: Thu, 24 Sep 2026 15:05:51 +0300
-Subject: [PATCH 24/24] URL: normalize output encoding for percent-encoding.
+Subject: [PATCH 24/26] URL: normalize output encoding for percent-encoding.
---
source/lexbor/url/url.c | 34 +++++++++++++++++++++++++---------
diff --git a/ext/lexbor/patches/0025-Core-fixed-allocation-and-move-sizes-in-lexbor_array.patch b/ext/lexbor/patches/0025-Core-fixed-allocation-and-move-sizes-in-lexbor_array.patch
new file mode 100644
index 000000000000..2b30cc67b4f4
--- /dev/null
+++ b/ext/lexbor/patches/0025-Core-fixed-allocation-and-move-sizes-in-lexbor_array.patch
@@ -0,0 +1,52 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Nicolas Grekas
+Date: Mon, 28 Sep 2026 21:25:27 +0200
+Subject: [PATCH 25/26] Core: fixed allocation and move sizes in
+ lexbor_array_obj_t. (#428)
+
+lexbor_array_obj_init() and lexbor_array_obj_expand() multiplied the
+number of bytes they need by sizeof(uint8_t *), so they allocated 8
+times too much memory on 64-bit platforms. lexbor_array_obj_delete()
+moved 8 times the bytes of the remaining entries too, which only stayed
+within bounds thanks to the oversized allocation.
+
+Added tests for the allocated sizes and for the entries after the last
+one being left untouched by lexbor_array_obj_delete().
+---
+ source/lexbor/core/array_obj.c | 9 +++------
+ 1 file changed, 3 insertions(+), 6 deletions(-)
+
+diff --git a/source/lexbor/core/array_obj.c b/source/lexbor/core/array_obj.c
+index 79a4c3a..8cceaf9 100644
+--- a/source/lexbor/core/array_obj.c
++++ b/source/lexbor/core/array_obj.c
+@@ -29,8 +29,7 @@ lexbor_array_obj_init(lexbor_array_obj_t *array,
+ array->size = size;
+ array->struct_size = struct_size;
+
+- array->list = lexbor_malloc(sizeof(uint8_t *)
+- * (array->size * struct_size));
++ array->list = lexbor_malloc(array->size * struct_size);
+ if (array->list == NULL) {
+ return LXB_STATUS_ERROR_MEMORY_ALLOCATION;
+ }
+@@ -77,8 +76,7 @@ lexbor_array_obj_expand(lexbor_array_obj_t *array, size_t up_to)
+
+ new_size = array->length + up_to;
+
+- list = lexbor_realloc(array->list, sizeof(uint8_t *)
+- * (new_size * array->struct_size));
++ list = lexbor_realloc(array->list, new_size * array->struct_size);
+ if (list == NULL) {
+ return NULL;
+ }
+@@ -170,8 +168,7 @@ lexbor_array_obj_delete(lexbor_array_obj_t *array, size_t begin, size_t length)
+
+ memmove(&array->list[ begin * array->struct_size ],
+ &array->list[ end_len * array->struct_size ],
+- sizeof(uint8_t *)
+- * ((array->length - end_len) * array->struct_size));
++ (array->length - end_len) * array->struct_size);
+
+ array->length -= length;
+ }
diff --git a/ext/lexbor/patches/0026-Core-HTML-reduced-initial-sizes-of-tokenizer-pools-a.patch b/ext/lexbor/patches/0026-Core-HTML-reduced-initial-sizes-of-tokenizer-pools-a.patch
new file mode 100644
index 000000000000..1a50a90e9169
--- /dev/null
+++ b/ext/lexbor/patches/0026-Core-HTML-reduced-initial-sizes-of-tokenizer-pools-a.patch
@@ -0,0 +1,87 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Nicolas Grekas
+Date: Sun, 27 Sep 2026 19:49:58 +0200
+Subject: [PATCH 26/26] Core/HTML: reduced initial sizes of tokenizer pools and
+ mraw cache.
+
+The HTML tokenizer preallocated 4096 tokens and 4096 token attributes,
+and every lexbor_mraw_t preallocated 512 entries for its cache of freed
+blocks. A document parser has 7 mraws, so about 1 MB was allocated
+before parsing a single byte.
+
+The tree builder uses a single token, and all these pools grow on
+demand, so they now start with 16 tokens, 256 attributes and 64 cache
+entries.
+
+The initial sizes can be set at build time by defining
+LXB_HTML_TKZ_TOKEN_POOL_SIZE, LXB_HTML_TKZ_ATTR_POOL_SIZE and
+LEXBOR_MRAW_CACHE_SIZE, e.g. with -D compiler flags.
+
+This is lexbor/lexbor#427.
+---
+ source/lexbor/core/mraw.c | 6 +++++-
+ source/lexbor/html/tokenizer.c | 14 ++++++++++++--
+ 2 files changed, 17 insertions(+), 3 deletions(-)
+
+diff --git a/source/lexbor/core/mraw.c b/source/lexbor/core/mraw.c
+index c990405..30faca6 100644
+--- a/source/lexbor/core/mraw.c
++++ b/source/lexbor/core/mraw.c
+@@ -11,6 +11,10 @@
+ #include
+ #endif
+
++#ifndef LEXBOR_MRAW_CACHE_SIZE
++ #define LEXBOR_MRAW_CACHE_SIZE 64
++#endif
++
+
+ #define lexbor_mraw_meta_set(data, size) \
+ do { \
+@@ -62,7 +66,7 @@ lexbor_mraw_init(lexbor_mraw_t *mraw, size_t chunk_size)
+ /* Cache */
+ mraw->cache = lexbor_bst_create();
+
+- status = lexbor_bst_init(mraw->cache, 512);
++ status = lexbor_bst_init(mraw->cache, LEXBOR_MRAW_CACHE_SIZE);
+ if (status) {
+ return status;
+ }
+diff --git a/source/lexbor/html/tokenizer.c b/source/lexbor/html/tokenizer.c
+index 1d9f378..2193146 100644
+--- a/source/lexbor/html/tokenizer.c
++++ b/source/lexbor/html/tokenizer.c
+@@ -20,6 +20,14 @@
+
+ #define LXB_HTML_TKZ_TEMP_SIZE (4096 * 4)
+
++#ifndef LXB_HTML_TKZ_TOKEN_POOL_SIZE
++ #define LXB_HTML_TKZ_TOKEN_POOL_SIZE 16
++#endif
++
++#ifndef LXB_HTML_TKZ_ATTR_POOL_SIZE
++ #define LXB_HTML_TKZ_ATTR_POOL_SIZE 256
++#endif
++
+
+ enum {
+ LXB_HTML_TOKENIZER_OPT_TAGS_SELF = 1 << 0,
+@@ -63,14 +71,16 @@ lxb_html_tokenizer_init(lxb_html_tokenizer_t *tkz)
+
+ tkz->dobj_token = lexbor_dobject_create();
+ status = lexbor_dobject_init(tkz->dobj_token,
+- 4096, sizeof(lxb_html_token_t));
++ LXB_HTML_TKZ_TOKEN_POOL_SIZE,
++ sizeof(lxb_html_token_t));
+ if (status != LXB_STATUS_OK) {
+ return status;
+ }
+
+ /* Init Token Attributes */
+ tkz->dobj_token_attr = lexbor_dobject_create();
+- status = lexbor_dobject_init(tkz->dobj_token_attr, 4096,
++ status = lexbor_dobject_init(tkz->dobj_token_attr,
++ LXB_HTML_TKZ_ATTR_POOL_SIZE,
+ sizeof(lxb_html_token_attr_t));
+ if (status != LXB_STATUS_OK) {
+ return status;
diff --git a/ext/lexbor/patches/README.md b/ext/lexbor/patches/README.md
index 6c7b78fd59fa..0af4e2d978c2 100644
--- a/ext/lexbor/patches/README.md
+++ b/ext/lexbor/patches/README.md
@@ -29,6 +29,10 @@ This contains the following patch files in mailbox format.
**Note** for this patch the utilities to generate the tables are also patched.
Make sure to apply on a fresh Lexbor clone and run (in `lexbor/utils/encoding`): `python3 single-byte.py` to generate the tables.
Also run (in `lexbor/utils/lexbor/html`) `python3 tokenizer_entities_bst.py` to generate the static binary search tree for entities.
+* 0026-Core-HTML-reduced-initial-sizes-of-tokenizer-pools-a.patch
+ The patch of https://github.com/lexbor/lexbor/pull/427, which is not merged yet. It reduces the initial sizes
+ of the tokenizer pools and of the mraw cache, and allows setting them at build time.
+ PHP sets them in config.m4 and config.w32, so this patch can be replaced by the upstream commit once it is merged.
## How to apply