1
0
mirror of https://github.com/php/php-src.git synced 2026-03-24 16:22:37 +01:00
Commit Graph

640 Commits

Author SHA1 Message Date
Niels Dossche
91279cfdc0 Use binary search for cp932ext*_ucs_table lookups (#12712)
* Use binary search for cp932ext*_ucs_table lookups

A large amount of time is spent doing a linear search through these
tables in the CP932 encoding. Instead of that, we can add sorted
versions of these tables that also store the index of the non-sorted
version and perform a binary search on those sorted versions.

This reduces the time spent from 1.54s to 0.91s for the reference
benchmark [1].

[1] https://github.com/php/php-src/issues/12684#issuecomment-1813799924

* Fix search bounds
2023-11-18 12:09:12 +01:00
Niels Dossche
3ad422ebd0 Avoid temporary string allocations in php_mb_parse_encoding_list() (#12714)
This brings execution time down from 0.91s to 0.86s on the reference
benchmark [1].

[1] https://github.com/php/php-src/issues/12684#issuecomment-1813799924
2023-11-18 12:08:59 +01:00
Niels Dossche
7658220599 Improve performance of mbfl_name2encoding() by using perfect hashing (#12707)
mbfl_name2encoding() uses a linear loop through the encodings, comparing
the name one by one, which is very slow. For the benchmark [1] just
looking up the name takes about 50% of run-time.

By using perfect hashing instead, we no longer have to loop over the
list, and the number of string comparisons is reduced to just a single
one. The perfect hashing table is generated using GNU gperf and amended
manually to fit in with mbstring and manually changed to  reduce the
cache size.

[1] https://github.com/php/php-src/issues/12684#issuecomment-1813799924
2023-11-17 19:38:43 +01:00
Alex Dowad
d04854b38c Add fast mb_strcut implementation for UTF-16
Similar to the fast, specialized mb_strcut implementation for UTF-8
in 1f0cf133db, this new implementation of mb_strcut for UTF-16 strings
just examines a few bytes before each cut point.

Even for short strings, the new implementation is around 2x faster.
For strings around 10,000 bytes in length, it comes out about 100-500x
faster in my microbenchmarks.

The new implementation behaves identically to the old one on valid
UTF-16 strings; a fuzzer was used to help verify this.
2023-10-28 19:09:08 +02:00
Alex Dowad
1f0cf133db Add fast mb_strcut implementation for UTF-8
The old implementation runs through the entire string to pick out the
part which should be returned by mb_strcut. This creates significant
performance overhead. The new specialized implementation of mb_strcut
for UTF-8 usually only examines a few bytes around the starting and
ending cut points, meaning it generally runs in constant time.

For UTF-8 strings just a few bytes long, the new implementation is
around 10% faster (according to microbenchmarks which I ran locally).
For strings around 10,000 bytes in length, it is 50-300x faster.
(Yes, that is 300x and not 300%.)

The new implementation behaves identically to the old one on VALID
UTF-8 strings; a fuzzer was used to help ensure this is the case.
On invalid UTF-8 strings, there is a difference: in some cases, the
old implementation will pass invalid byte sequences through unchanged,
while in others it will remove them. The new implementation has
behavior which is perhaps slightly more predictable: it simply backs
up the starting and ending cut points to the preceding "starter
byte" (one which is not a UTF-8 continuation byte).
2023-10-04 09:10:38 +02:00
Alex Dowad
a57fdea149 Add assertion to mb_utf7imap_to_wchar to catch buffer overrun
I don't believe such a buffer overrun will ever occur, but just in
case the code is changed in the future, it will be good to have an
assertion here to help catch bugs. (A similar assertion is already
used in the UTF-7 version of this function.)
2023-10-01 14:43:35 +02:00
Alex Dowad
50ca24251d PHP_HAVE_BUILTIN_USUB_OVERFLOW macro is defined even if __builtin_usub_overflow not available
...So conditionally including code which uses __builtin_usub_overflow
(for performance) if the macro is defined is not correct. We also need
to check if the macro is defined as a non-zero value.

Apparently this broke the build for a user whose C compiler is GCC
4.9.4. Sorry, user! That was my fault!

Thanks to Jakub Zelenka for reporting the issue.
2023-09-08 20:36:24 +02:00
Alex Dowad
6930ef5837 Merge branch 'PHP-8.2'
* PHP-8.2:
  Fix mb_strlen is wrong length for CP932 when 0x80.
2023-05-30 14:02:16 -07:00
Alex Dowad
c33589ea11 Merge branch 'PHP-8.1' into PHP-8.2
* PHP-8.1:
  Fix mb_strlen is wrong length for CP932 when 0x80.
2023-05-30 13:45:36 -07:00
Yuya Hamada
c50172e812 Fix mb_strlen is wrong length for CP932 when 0x80. 2023-05-30 13:44:30 -07:00
Alex Dowad
18ca489347 Convert mbfilter_conv{,_r}_map_tbl to return bool
Thanks to Girgias for pointing this out.
2023-05-20 21:27:48 -07:00
Alex Dowad
8e6be14372 Fix problem with CP949 conversion when 0xC9 precedes byte lower than 0xA1
This bug was introduced in e837a8800b. In that commit, I increased the
performance of CP949 text conversion, but accidentally broke the case
where 0xC9 (illegal byte to start a character) is followed by a valid
character with a first byte less than 0xA1. The 'broken' behavior is
that both the 0xC9 byte and the following valid character would be
converted to error markers.
2023-05-20 21:27:48 -07:00
Alex Dowad
245daedb41 Move kana translation tables to mbfilter_cjk.c
These (static) tables were defined in a header file, which was included
in two different .c files. That will result in two copies of the tables
being included in the PHP binary.

But the tables were only used in one of the two .c files. Move it where
it is used to avoid needlessly bloating the binary. (I checked in a
hex editor and confirmed that while the previous binary contained two
copies of these tables, it now only contains one.)
2023-05-20 21:27:48 -07:00
Alex Dowad
175154dbcc Optimize conversion of CP932 text to Unicode
Conversion of CP932 text to UTF-8 using `mb_convert_encoding` is
now about 20% faster than before.
2023-05-20 21:27:48 -07:00
Alex Dowad
73633bf1c3 Optimize conversion of SJIS-2004 text to Unicode
Conversion of SJIS-2004 text to UTF-8 using `mb_convert_encoding` is
now about 60% faster than before. (Many other mbstring functions will
also be faster now on SJIS-2004 text.)
2023-05-20 21:27:48 -07:00
Alex Dowad
c717c79a09 Combine CJK encoding conversion code in a single source file
This will make it easier to combine duplicated code between all the
CJK text encodings (a significant amount is already combined in this
commit, such as the repeated definitions of SJIS_DECODE and
SJIS_ENCODE), but I hope to remove even more redundancy in the future.

The table used to implement mb_strlen for CP932 has been changed to
the same table as "SJIS-win".
2023-05-20 21:27:48 -07:00
Ilija Tovilo
aa553af911 Fix segfault in mb_strrpos/mb_strripos with ASCII encoding and negative offset
We're setting the encoding from PHP_FUNCTION(mb_strpos), but mbfl_strpos would
discard it, setting it to mbfl_encoding_pass, making zend_memnrstr fail due to a
null-pointer exception.

Fixes GH-11217
Closes GH-11220
2023-05-15 10:36:37 +02:00
Niels Dossche
b915a1d8d7 Fix uninitialised variable warning in mbfilter_sjis.c
Compiling in release mode with UBSAN gives me the following compiler warning:
```
In function ‘mb_wchar_to_sjismac’:
mbfilter_sjis.c:1419:89: warning: ‘i’ may be used uninitialized [-Wmaybe-uninitialized]
 1419 | buf->state = (i << 24) | (index << 16) | (w & 0xFFFF);
      |                 ^~
mbfilter_sjis.c:1398:42: note: ‘i’ was declared here
 1398 | for (int i = 0; i < code_tbl_m_len; i++) {
      |          ^
```

Since the if condition will always be taken after the goto, we can get
rid of the warning by moving the label inside the if.

Signed-off-by: Alex Dowad <alexinbeijing@gmail.com>
2023-04-30 13:51:52 +02:00
Javier Eguiluz
732d92c0e5 [skip ci] Fix various typos and grammar issues (#11143) 2023-04-28 11:05:32 +02:00
Alex Dowad
6df7557e43 mb_parse_str, mb_http_input, and mb_convert_variables use fast text conversion code for automatic encoding detection
For mb_parse_str, when mbstring.http_input (INI parameter) is a list of
multiple possible text encodings (which is not the case by default),
this new implementation is about 25% faster.

When mbstring.http_input is a single value, then nothing is changed.
(No automatic encoding detection is done in that case.)
2023-04-12 19:57:52 +02:00
Alex Dowad
c4fb049bf6 For UTF-7, emit error marker if Base64 section ends abruptly after first half of surrogate pair
This (rare) situation was already handled correctly for the 1st and 2nd
of every 3 codepoints in a Base64-encoded section of a UTF-7 string.
However, it was not handled correctly if it happened on the 3rd,
6th, 9th, etc. codepoint of such a Base64-encoded section.
2023-03-27 11:34:11 +02:00
pakutoma
b721d0f71e Fix phpGH-10648: add check function pointer into mbfl_encoding
Previously, mbstring used the same logic for encoding validation as for
encoding conversion.

However, there are cases where we want to use different logic for validation
and conversion. For example, if a string ends up with missing input
required by the encoding, or if a character is input that is invalid
as an encoding but can be converted, the conversion should succeed and
the validation should fail.

To achieve this, a function pointer mb_check_fn has been added to
struct mbfl_encoding to implement the logic used for validation.
Also, added implementation of validation logic for UTF-7, UTF7-IMAP,
ISO-2022-JP and JIS.

(The same change has already been made to PHP 8.2 and 8.3; see
6fc8d014df. This commit is backporting the change to PHP 8.1.)
2023-03-25 09:52:10 +02:00
Alex Dowad
345abce590 Fix compile errors caused by missing initializers in 0779950768
When I built and tested 0779950768 locally, the build was successful
and all tests passed. However, in CI, some CI jobs are failing due to
compile errors. Fix those.
2023-03-24 22:18:18 +02:00
Alex Dowad
0779950768 Merge branch 'PHP-8.2'
* PHP-8.2:
  Fix phpGH-10648: add check function pointer into mbfl_encoding
2023-03-24 21:15:32 +02:00
pakutoma
6fc8d014df Fix phpGH-10648: add check function pointer into mbfl_encoding
Previously, mbstring used the same logic for encoding validation as for
encoding conversion.

However, there are cases where we want to use different logic for validation
and conversion. For example, if a string ends up with missing input
required by the encoding, or if a character is input that is invalid
as an encoding but can be converted, the conversion should succeed and
the validation should fail.

To achieve this, a function pointer mb_check_fn has been added to
struct mbfl_encoding to implement the logic used for validation.
Also, added implementation of validation logic for UTF-7, UTF7-IMAP,
ISO-2022-JP and JIS.
2023-03-24 20:34:22 +02:00
Alex Dowad
0ce755be26 Implement mb_encode_mimeheader using fast text conversion filters
The behavior of the new mb_encode_mimeheader implementation closely
follows the old implementation, except for three points:

• The old implementation was missing a call to the mbfl_convert_filter
  flush function. So it would sometimes truncate the input string just
  before its end.

• The old implementation would drop zero bytes when QPrint-encoding.
  So for example, if you tried to QPrint-encode the UTF-32BE string
  "\x00\x00\x12\x34", its QPrint-encoding would be "=12=34", which
  does not decode to a valid UTF-32BE string. This is now fixed.

• In some rare corner cases, the new implementation will choose to
  Base64-encode or QPrint-encode the input string, where the old
  implementation would have just added newlines to it. Specifically,
  this can happen when there is a non-space ASCII character, followed
  by a large number of ASCII spaces, followed by a non-ASCII character.

The new implementation is around 2.5-8x faster than the old one,
depending on the text encoding and transfer encoding used. Performance
gains are greater with Base64 transfer encoding than with QPrint
transfer encoding; this is not because QPrint-encoding bytes is slow,
but because QPrint-encoded output is much bigger than Base64-encoded
output and takes more lines, so we have to go through the process of
finding the right place to break a line many more times.
2023-03-15 15:53:08 +02:00
Alex Dowad
e447036dc6 Merge branch 'PHP-8.2'
* PHP-8.2:
  Propagate error checks for mbfl_filt_conv_illegal_output()
  Use CK() macro to check the output function in mbfilter_unicode2sjis_emoji_sb()
  Make error checks on encoding methods for docomo, kddi, sb consistent
2023-03-02 23:12:09 +02:00
Alex Dowad
3142829562 Merge branch 'PHP-8.1' into PHP-8.2
* PHP-8.1:
  Propagate error checks for mbfl_filt_conv_illegal_output()
  Use CK() macro to check the output function in mbfilter_unicode2sjis_emoji_sb()
  Make error checks on encoding methods for docomo, kddi, sb consistent
2023-03-02 22:50:37 +02:00
nielsdos
d66ca5dabb Propagate error checks for mbfl_filt_conv_illegal_output() 2023-03-02 22:36:00 +02:00
nielsdos
263655a520 Use CK() macro to check the output function in mbfilter_unicode2sjis_emoji_sb() 2023-03-02 22:36:00 +02:00
nielsdos
69543e6a10 Make error checks on encoding methods for docomo, kddi, sb consistent
Some places use an if check, which implicitly checks for a non-zero
value, and some places use > 0. The > 0 is the correct one because at
least some of those functions already use the CK() macro to return -1 on
error. Because -1 != 0 this is wrongly interpreted as a success instead
of a failure.
2023-03-02 22:36:00 +02:00
Alex Dowad
157ca654f2 Implement mb_decode_mimeheader using fast text conversion filters
The new implementation is 2.5x-3x faster.

If an invalid charset name was used, the old implementation would get
'stuck' trying to parse the charset name and would not interpret any
other MIME encoded words up to the end of the input string. The new
implementation fixes this bug.

If an (invalid) encoded word ends abruptly and a new (valid) encoded
word starts, the old implementation would not decode the valid encoded
word. The new implementation also fixes this.

Otherwise, the behavior of the new implementation has been designed to
closely match that of the old implementation.
2023-02-22 23:08:03 +02:00
Alex Dowad
117f2263ce Remove unneeded function mbfl_no2preferred_mime_name 2023-02-22 23:08:03 +02:00
Alex Dowad
a85adb170c Remove unneeded function mbfl_name2no_encoding 2023-02-22 23:08:03 +02:00
Alex Dowad
c8ec2ed730 Add AVX2-accelerated UTF-16 decoding/encoding routines
As with other SIMD-accelerated functions in php-src, the new UTF-16
encoding and decoding routines can be compiled either with AVX2
acceleration "always on", "always off", or else with runtime detection
of AVX2 support.

With the new UTF-16 decoder/encoder, conversion of extremely short
strings (as in several bytes) has the same performance as before,
and conversion of medium-length (~100 character) strings is about 65%
faster, but conversion of long (~10,000 character) strings is around
6 times faster.

Many other mbstring functions will also be faster now when handling
UTF-16; for example, mb_strlen is almost 3 times faster on medium
strings, and almost 9 times faster on long strings. (Why does mb_strlen
benefit more from AVX2 acceleration than mb_convert_encoding? It's
because mb_strlen only needs to decode, but not re-encode, the input
string, and the UTF-16 decoder benefits much more from SIMD
acceleration than the UTF-16 encoder.)
2023-02-05 20:06:42 +02:00
Alex Dowad
8a73a68190 Use fast encoding conversion filters in mb_send_mail 2023-01-21 23:12:58 +02:00
Christoph M. Becker
c8955c078a Revert GH-10220
Cf. <https://github.com/php/php-src/pull/10220#issuecomment-1383739816>.

This reverts commit ecc880f491.
This reverts commit 588a07f737.
This reverts commit f377e15751.
This reverts commit b4ba16fe18.
This reverts commit 694ec1deea.
This reverts commit 6b34de8eba.
This reverts commit aa1cd02a43.
This reverts commit 308fd311ea.
This reverts commit 16203b53e1.
This reverts commit 738fb5ca54.
This reverts commit 9fdbefacd3.
This reverts commit cd4a7c1d90.
This reverts commit 928685eba2.
This reverts commit 01e5ffc85c.
2023-01-16 12:27:33 +01:00
Alex Dowad
4427b2e1ab Mark UTF-8 strings emitted by mbstring functions as valid UTF-8
We now have a couple of mbstring functions which have fast paths for
strings marked as 'valid UTF-8'. Later, we may likely have more. So
that these fast paths can be used more frequently, mark UTF-8 strings
emitted by mbstring as 'valid UTF-8'. This is always a correct thing
to do, because mbstring never returns invalid UTF-8 as the result of
a conversion (or similar) operation.

Internally, we do have a conversion mode which deliberately emits
invalid UTF-8 in some cases. (This is done to prevent unwanted matches
when we are converting strings to UTF-8 before performing matching
operations on them.) For such strings, don't set the 'valid UTF-8' flag.
It probably wouldn't hurt anything to set it, because strings generated
using that special conversion mode should *never* be returned to
userland, and I don't think we do anything with them which cares about
the IS_STR_VALID_UTF8 flag... but still, it would likely cause
confusion for developers.
2023-01-11 17:08:27 +02:00
Max Kellermann
308fd311ea ext/{standard,json,random,...}: add missing includes 2023-01-10 14:19:03 +00:00
Alex Dowad
092ad3e462 Optimize branch structure of UTF-8 decoder routine
I like the asm which gcc -O3 generates on this modified code...
and guess what: my CPU likes it too!

(The asm is noticeably tighter, without any extra operations in the
path which dispatches to the code for decoding a 1-byte, 2-byte,
3-byte, or 4-byte character. It's just CMP, conditional jump, CMP,
conditional jump, CMP, conditional jump.

...Though I was admittedly impressed to see gcc could implement the
boolean expression `c >= 0xC2 && c <= 0xDF` with just 3 instructions:
add, CMP, then conditional jump. Pretty slick stuff there, guys.)

Benchmark results:

UTF-8, short - to UTF-16LE  faster by 7.36% (0.0001 vs 0.0002)
UTF-8, short - to UTF-16BE  faster by 6.24% (0.0001 vs 0.0002)
UTF-8, medium - to UTF-16BE faster by 4.56% (0.0003 vs 0.0003)
UTF-8, medium - to UTF-16LE faster by 4.00% (0.0003 vs 0.0003)
UTF-8, long - to UTF-16BE   faster by 1.02% (0.0215 vs 0.0217)
UTF-8, long - to UTF-16LE   faster by 1.01% (0.0209 vs 0.0211)
2023-01-08 17:27:19 +02:00
Alex Dowad
3ab72a4357 Merge branch 'PHP-8.2'
* PHP-8.2:
  Use different mblen_table for different SJIS variants
  Correct entry for 0x80,0xFD-FF in SJIS multi-byte character length table
2023-01-06 14:34:10 +02:00
Alex Dowad
1751f34cfa Merge branch 'PHP-8.1' into PHP-8.2
* PHP-8.1:
  Use different mblen_table for different SJIS variants
  Correct entry for 0x80,0xFD-FF in SJIS multi-byte character length table
2023-01-06 14:13:21 +02:00
Alex Dowad
3152b7b26f Use different mblen_table for different SJIS variants 2023-01-06 14:09:43 +02:00
Alex Dowad
d104481af8 Correct entry for 0x80,0xFD-FF in SJIS multi-byte character length table
As a performance optimization, mbstring implements some functions using
tables which give the (byte) length of a multi-byte character using a
lookup based on the value of the first byte. These tables are called
`mblen_table`.

For many years, the mblen_table for SJIS has had '2' in position 0x80.
That is wrong; it should have been '1'. Reasons:

For SJIS, SJIS-2004, and mobile variants of SJIS, 0x80 has never been
treated as the first byte of a 2-byte character. It has always been
treated as a single erroneous byte. On the other hand, 0x80 is a valid
character in MacJapanese... but a 1-byte character, not a 2-byte one.

The same applies to bytes 0xFD-FF; these are 1-byte characters in
MacJapanese, and in other SJIS variants, they are not valid (as the
first byte of a character).

Thanks to the GitHub user 'youkidearitai' for finding this problem.
2023-01-05 14:05:39 +02:00
Alex Dowad
204694cc71 Optimize out more checks from hot path for BIG5 decoding
This boosts the speed of BIG5 encoding conversion by just 1-2%.

I tried various other tweaks to the BIG5 decoding routine to see if
I could make it faster at the cost of using a larger conversion table,
but at least on the machine I am using for benchmarking, these other
changes just made things slower.
2023-01-05 08:05:05 +02:00
Alex Dowad
d75c78b0c8 Optimize out checks in hot path for SJIS decoding
This gives about a 20% speed boost when converting SJIS to some other
encoding.
2023-01-05 08:04:58 +02:00
Alex Dowad
9c283850fb Optimize out another bounds check in BIG5 decoder
This gives about a 9% speed boost for BIG5 encoding conversion.
(Not as much as I was hoping!)
2023-01-05 08:04:51 +02:00
Alex Dowad
e837a8800b Optimize another check out of hot path for UHC decoding
This gives about another 8-9% speed boost to UHC decoding.
2023-01-04 21:58:27 +02:00
Alex Dowad
a76658b329 Optimize out bounds check in UHC decoder
This gives a 25% speed boost for conversion operations on long strings
(~10,000 codepoints). For shorter strings, the speed boost is less
(as the input gets smaller, it is progressively swamped more and more
by the overhead of entering and exiting the conversion function).

When benchmarking string conversion speed, we are measuring not only
the speed of the decoder, but also the time which it takes to re-encode
the string in another encoding like UTF-8 or UTF-16. So the performance
increase for functions which only need to decode but not re-encode the
input string will be much more than 25%.
2023-01-04 21:58:27 +02:00
Alex Dowad
ffbddc4848 Optimize conversion of GB18030 to Unicode
As with CP936, iterating over the PUA table and looking for matches in
it was a significant bottleneck for GB18030 decoding (though not as
severe a bottleneck as for CP936, since more is involved in GB18030
decoding than CP936 decoding).

Here are some benchmark results after optimizing out that bottleneck:

    GB18030, medium - to UTF-16BE - faster by 60.71% (0.0007 vs 0.0017)
    GB18030, medium - to UTF-8    - faster by 59.88% (0.0007 vs 0.0017)
    GB18030, long - to UTF-8      - faster by 44.91% (0.0669 vs 0.1214)
    GB18030, long - to UTF-16BE   - faster by 43.05% (0.0672 vs 0.1181)
    GB18030, short - to UTF-8     - faster by 27.22% (0.0003 vs 0.0004)
    GB18030, short - to UTF-16BE  - faster by 26.98% (0.0003 vs 0.0004)

(The 'short' test strings had 0-5 codepoints each, 'medium' ~100
codepoints, and 'long' ~10,000 codepoints. For each benchmark, the
test harness cycled through all the test strings 40,000 times.)
2023-01-04 21:58:27 +02:00