Re: Add ASCII fast path to Unicode normalization functions

From: Andrew Dunstan <andrew(at)dunslane(dot)net>
To: Tristan Partin <tristan(at)partin(dot)io>, Rui Zhao <zhaorui126(at)gmail(dot)com>
Cc: Ayush Tiwari <ayushtiwari(dot)slg01(at)gmail(dot)com>, PostgreSQL Hackers <pgsql-hackers(at)lists(dot)postgresql(dot)org>
Subject: Re: Add ASCII fast path to Unicode normalization functions
Date: 2026-10-07 13:24:03
Message-ID: 5f3120be-66d0-44b6-b98d-cc35464fa8f3@dunslane.net
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-hackers


On 2026-10-06 Tu 5:56 PM, Tristan Partin wrote:
> On Tue Oct 6, 2026 at 9:22 PM UTC, Andrew Dunstan wrote:
>> On 2026-10-04 Su 12:42 PM, Rui Zhao wrote:
>>> Hi Andrew,
>>>
>>> Thanks for working on this. I tried two further optimizations on top
>>> of v4. They can be applied independently:
>>>
>>> 1. unicode_assigned() still calls pg_mbstrlen_with_len() on the
>>> remaining input, then walks it again to check each code point. 0001
>>> uses the byte length to walk to the end directly. This also avoids
>>> scanning the whole suffix before returning false for an unassigned
>>> code point near the beginning.
>>>
>>> 2. unicode_normalize_func() and unicode_is_normalized() allocate a C
>>> string for the normalization form on every call, even when the input
>>> is all ASCII. 0002 compares the form directly in the text value,
>>> preserving case-insensitive matching and rejecting extra bytes.
>>> It only builds a C string when reporting an invalid form.
>>>
>>> 0003 replaces the constant-folded VALUES test with stored short and
>>> compressed text values.
>>>
>>> Core regression and the Unicode normalization checks passed with these
>>> patches.
>>>
>> OK, that all looks good. The attached combines all this.
> Thanks for the updates Andrew. They looks pretty good to me. A few
> nitpicks:
>
>> +/*
>> + * Return the length of an initial portion of s[0..len) that is valid ASCII,
>> + * that is, contains no zero bytes and no bytes with the high bit set, or
>> + * len if all of it is.
>> + *
>> + * is_valid_ascii() requires a length that is a multiple of sizeof(Vector8),
>> + * so check one chunk at a time with it and then the remainder byte by byte.
>> + * A failure inside a chunk is reported at the start of that chunk, so the
>> + * result can be up to sizeof(Vector8) - 1 bytes short of the full valid
>> + * ASCII prefix.
>> + */
> I would relegate that second paragraph to a comment within the function
> itself. I am not sure it is valuable to callers, only to those reading
> the actual implementation. It also may be a bit verbose, but that isjust
> an opinion.
>
>> + /*
>> + * Check the remaining code points without first counting them, stopping
>> + * at a NUL as pg_mbstrlen_with_len() would.
>> + */
>> + p = (unsigned char *) VARDATA_ANY(input) + start;
>> + end = (unsigned char *) VARDATA_ANY(input) + len;
> I am not sure that it makes sense to mention pg_mbstrlen_with_len()
> here. I would be more explicit as to why we are stopping at a NUL.

Thanks. I'm intending to commit this during the November CF, will fix then.

cheers

andrew

--
Andrew Dunstan
EDB: https://www.enterprisedb.com

In response to

Browse pgsql-hackers by date

  From Date Subject
Next Message Nisha Moond 2026-10-07 13:26:14 Re: [PATCH] Preserve replication origin OIDs in pg_upgrade
Previous Message Zhijie Hou 2026-10-07 13:21:34 Re: Parallel Apply