Repository navigation
<regex>: Perform insertions into character class NFA node buffers when parsing of the character class completes - #6441
Conversation
โฆhen parsing of the character class completes
This comment was marked as resolved.
This comment was marked as resolved.
The maximum length is actually restricted by the return values of
I think we probably have existing coverage for this, but I will check later. |
โฆter class/bracket expression
|
It appears that we really didn't have any test coverage for combining two positive named character classes, except for the special case that combines |
|
Thanks! ๐ป I pushed a change to clear
Yep, I can get this in before the MSVC Build Tools 14.52 branches for release on 2026-09-23. |
|
I'm mirroring this to the MSVC-internal repo. Please notify me if any further changes are pushed, otherwise no action is required. |
65592cf
into
microsoft:main
๐ป ๐ ๐ |
I didn't clear these vectors because this can cause additional allocations when collating elements are used. But collating elements are so rare that this difference probably won't matter to anyone. |
|
Hmm, where are the additional allocations coming from? Clearing a vector doesn't deallocate its capacity unless we shrink to fit. The string elements have already been cleared, resetting them to small mode, so destroying them and recreating them (in small mode) won't allocate. |
Clearing does not reset them to small mode. See here: Lines 1909 to 1911 in 85f13f3 Which leads to here: Lines 3048 to 3052 in 85f13f3 So the size is changed to 0, but the capacity is still the same and thus the string remains in its current mode. So if the string engaged large mode while evaluating the current character class and it will have to grow big enough to require large mode again when evaluating a later character class, then clearing the whole vector incurs an additional allocation. |
|
Thanks, I stand corrected! I have clearly forgotten how this library works ๐น Since collating elements are rare, I'd like to leave the vector clearing to make it really clear we're not cross-contaminating but I do recognize that the way you had it working was faster. |
The builder currently eagerly mutates a character class NFA node whenever it handles the next syntactic element in the character class. In some cases there is no downside to this approach, e.g., when a single encountered character is inserted into a bitmap. But there are other cases where this has drawbacks:
\dor[:alpha:]), the builder updates the bitmap immediately. This means that when[\d[:alpha:]]is parsed, the builder evaluates the membership of a character in the bitmap range first for class\dand then for class[:alpha:]. If this evaluation did not happen eagerly, the builder could evaluate the combined class\d[:alpha:]by passing the or'ed character class masks to theisctype()function in the traits class, as is intended by [re.grammar/9].regexobject exists, and can lead to more allocations if there are several character classes that insert into these buffers.This PR moves all of these insertions to the point when the parser considers the character class complete. This means that a single pass is used to update the character bitmap for all positive named character classes (except for
\wdue to #5242), and character buffers in the node class are immediately filled with their full contents by copying from some temporary storage.This delayed insertion into the character buffers of the node class will actually briefly deteriorate performance: This PR does nothing about the fact that these character buffers are very simple implementations of dynamic arrays that reallocate as if all elements are inserted one by one, so the number of allocations can only increase. But after this PR, the builder sets the whole and final content of
_Bufbuffers in a single insert call everywhere. This means that we can solve this problem by just pulling out the dynamic array implementation in a follow-up PR.To store individual large characters, this PR reuses the existing
_Charsstring in the builder class. This string is also used for character sequences in a regex, but it wasn't used while parsing a character class previously.In the other cases, this PR adds more strings or vectors of strings as temporary storage. This changes the layout of the
_Builder3and_Parser3classes, but this is not an ABI break because there hasn't yet been a stable release with these classes.The same temporary storage is used for all character classes. For this reason, the new tests check that content stored in these buffers doesn't leak to the following character class because the temporary storage wasn't reset correctly.